A high-performance, recursive DNS resolver server with DNSSEC support, focused on preserving privacy.
This project is maintained by semihalev
A high-performance, recursive DNS resolver server with DNSSEC support, focused on preserving privacy.
Install SDNS using the go install
command:
go install github.com/semihalev/sdns@latest
Download the latest release from the GitHub Releases page.
$ docker run -d --name sdns -p 53:53 -p 53:53/udp sdns
Install docker-compose
and run from the root directory:
$ sudo apt install docker-compose
$ docker-compose up -d
Install and run as a service:
$ brew install sdns
$ brew install semihalev/tap/sdns (updated every release)
$ brew services start sdns
$ snap install sdns
$ yay -S sdns-git
Note: Pre-built binaries, Docker packages, brew taps, and snaps are automatically created by GitHub workflows.
$ go build
$ make test
Flag | Description |
---|---|
-c, –config PATH | Location of the config file. If it doesn’t exist, a new one will be generated |
-v, –version | Show the SDNS version |
-h, –help | Show help information and exit |
To debug your environment, execute the following command:
$ export SDNS_DEBUGNS=true && export SDNS_PPROF=true && ./sdns
The SDNS_DEBUGNS
environment variable is beneficial for verifying the RTT (Round Trip Time) of authoritative servers. To use it, send an HINFO query for zones with chaos class.
Here’s an example of the output you might receive:
$ dig chaos hinfo example.com
; <<>> DiG 9.17.1 <<>> chaos hinfo example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29636
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: f27dbb995df5ac79e4fa37c07d131b5bd03aa1c5f802047a7c02fb228a886cb281ecc319323dea81 (good)
;; QUESTION SECTION:
;example.com. CH HINFO
;; AUTHORITY SECTION:
example.com. 0 CH HINFO "Host" "IPv4:199.43.135.53:53 rtt:142ms health:[GOOD]"
example.com. 0 CH HINFO "Host" "IPv4:199.43.133.53:53 rtt:145ms health:[GOOD]"
example.com. 0 CH HINFO "Host" "IPv6:[2001:500:8f::53]:53 rtt:147ms health:[GOOD]"
example.com. 0 CH HINFO "Host" "IPv6:[2001:500:8d::53]:53 rtt:148ms health:[GOOD]"
Key | Description |
---|---|
version | Configuration file version |
directory | Working directory for SDNS data storage. Must be writable by the SDNS process. Default: “db” |
bind | DNS server binding address and port. Default: “:53” |
bindtls | DNS-over-TLS (DoT) server binding address. Default: “:853” |
binddoh | DNS-over-HTTPS (DoH) server binding address. Default: “:8053” |
binddoq | DNS-over-QUIC (DoQ) server binding address. Default: “:853” |
tlscertificate | Path to the TLS certificate file for DoT/DoH/DoQ |
tlsprivatekey | Path to the TLS private key file for DoT/DoH/DoQ |
outboundips | Outbound IPv4 addresses for DNS queries. Multiple addresses enable random source IP selection per request |
outboundip6s | Outbound IPv6 addresses for DNS queries. Multiple addresses enable random source IP selection per request |
rootservers | Root DNS servers (IPv4). These are the authoritative name servers for the DNS root zone |
root6servers | Root DNS servers (IPv6). These are the authoritative name servers for the DNS root zone |
dnssec | Enable DNSSEC validation for secure DNS responses. Options: “on” or “off”. Default: “on” |
rootkeys | DNSSEC root zone trust anchors in DNSKEY format |
fallbackservers | Upstream DNS servers used when all others fail. Format: “IP:port” (e.g., “8.8.8.8:53”) |
forwarderservers | Forward all queries to these DNS servers. Format: “IP:port” (e.g., “8.8.8.8:53”) |
api | HTTP API server binding address for statistics and control. Leave empty to disable |
bearertoken | API bearer token for authorization. If set, Authorization header must be included in API requests |
blocklists | URLs of remote blocklists to download and use for filtering |
blocklistdir | [DEPRECATED] Blocklist directory. Now automatically created in the working directory |
loglevel | Logging verbosity level. Options: crit, error, warn, info, debug. Default: “info” |
accesslog | Path to the access log file in Common Log Format. Leave empty to disable |
nullroute | IPv4 address returned for blocked A queries. Default: “0.0.0.0” |
nullroutev6 | IPv6 address returned for blocked AAAA queries. Default: “::0” |
accesslist | IP addresses/subnets allowed to make queries. Default allows all: [“0.0.0.0/0”, “::0/0”] |
querytimeout | Maximum time to wait for any DNS query to complete. Default: “10s” |
timeout | Network timeout for upstream DNS queries. Default: “2s” |
hostsfile | Path to hosts file (RFC 952/1123 format) for local resolution. Leave empty to disable |
expire | Cache TTL for error responses in seconds. Default: 600 |
cachesize | Maximum number of cached DNS records. Default: 256000 |
prefetch | Prefetch threshold percentage (10-90). Refreshes popular cache entries before expiration. 0 disables |
maxdepth | Maximum recursion depth for queries. Prevents infinite loops. Default: 30 |
ratelimit | Global query rate limit per second. 0 disables. Default: 0 |
clientratelimit | Per-client rate limit per minute. 0 disables. Default: 0 |
domainmetrics | Enable per-domain query metrics collection. Default: false |
domainmetricslimit | Maximum number of domains to track in metrics. 0 = unlimited (use with caution). Default: 10000 |
blocklist | Manual domain blocklist. Domains listed here will be blocked |
whitelist | Manual domain whitelist. Overrides blocklist matches |
cookiesecret | DNS cookie secret (RFC 7873) for client verification. Auto-generated if not set |
nsid | DNS server identifier (RFC 5001) for identifying this instance. Leave empty to disable |
chaos | Enable responses to version.bind and hostname.bind chaos queries. Default: true |
qname_min_level | QNAME minimization level (RFC 7816). 0 disables. Higher values increase privacy but may impact performance |
emptyzones | Enable local authoritative responses for RFC 1918 zones. See http://as112.net/ for details |
tcpkeepalive | Enable TCP connection pooling for root and TLD servers. Improves performance by reusing connections. Default: false |
roottcptimeout | TCP idle timeout for root server connections. Default: “5s” |
tldtcptimeout | TCP idle timeout for TLD server connections (com, net, org, etc.). Default: “10s” |
tcpmaxconnections | Maximum number of pooled TCP connections. 0 uses default. Default: 100 |
dnstapsocket | Unix domain socket path for dnstap binary DNS logging. Leave empty to disable |
dnstapidentity | Server identity string for dnstap messages. Defaults to hostname |
dnstapversion | Server version string for dnstap messages. Default: “sdns” |
dnstaplogqueries | Log DNS queries via dnstap. Default: true |
dnstaplogresponses | Log DNS responses via dnstap. Default: true |
dnstapflushinterval | Dnstap message flush interval in seconds. Default: 5 |
SDNS supports custom plugins to extend its functionality. The execution order of plugins and middlewares affects their behavior. Configuration keys must be strings, while values can be any type. Plugins are loaded before the cache middleware in the order specified.
For implementation details, see the example plugin.
[plugins]
[plugins.example]
path = "/path/to/exampleplugin.so"
config = {key_1 = "value_1", intkey = 2, boolkey = true, keyN = "nnn"}
[plugins.another]
path = "/path/to/anotherplugin.so"
Tests were performed on the following DNS resolvers: SDNS 1.5.0, PowerDNS Recursor 5.0.2, BIND 9.19.12, and Unbound 1.17.1.
Resolver | Version | QPS | Avg Latency | Lost Queries | Runtime | Response Codes |
---|---|---|---|---|---|---|
SDNS | 1.5.0 | 709/s | 137ms | 2 (0.004%) | 70.5s | NOERROR: 79.10%, SERVFAIL: 1.50%, NXDOMAIN: 19.40% |
PowerDNS | 5.0.2 | 578/s | 156ms | 20 (0.04%) | 86.5s | NOERROR: 67.64%, SERVFAIL: 1.92%, NXDOMAIN: 30.43% |
BIND | 9.19.12 | 405/s | 200ms | 156 (0.31%) | 123.0s | NOERROR: 67.84%, SERVFAIL: 1.62%, NXDOMAIN: 30.54% |
Unbound | 1.17.1 | 338/s | 237ms | 263 (0.53%) | 147.0s | NOERROR: 68.20%, SERVFAIL: 1.20%, NXDOMAIN: 30.60% |
SDNS demonstrates superior performance across all key metrics:
We welcome pull requests. If you’re considering significant changes, please start a discussion by opening an issue first.
Before submitting patches, please review our CONTRIBUTING guidelines.