- extractTTL: use 0 as initial value and take actual minimum TTL from answer records. Previously hardcoded 300 would cap higher TTLs. - TCP handler: add 90s read deadline to prevent idle connections from blocking goroutines indefinitely. - Server.Close: close upstream groups to release connection pool resources.
mini-dns
Lightweight DNS server with split routing, anti-pollution, and speed optimization. Single binary, single dependency (github.com/miekg/dns).
Built for networks where DNS is unreliable: split Chinese/foreign traffic to different upstreams, filter GFW-polluted responses, and pick the fastest IP from CDN results.
Features
- Split routing -- domain suffix trie, keyword, and exact match rules route queries to upstream groups
- Multi-protocol -- UDP, TCP, DNS-over-TLS (DoT), DNS-over-HTTPS (DoH)
- Proxy support -- upstreams can tunnel through HTTP CONNECT or SOCKS5 proxies (
detourfield) - Anti-pollution -- bogon IP filtering, IP blacklist, cross-validation across concurrent upstream responses
- Speed testing -- TCP connect latency test on A/AAAA results, reorder by fastest
- Cache -- TTL clamping, prefetch at 10% remaining TTL, serve-stale with background refresh
- Reverse cache -- IP-to-domain mapping for TUN routing integration
- Embeddable -- use as a library:
NewFromJSON(),Exchange(),Resolve()
Quick Start
go build ./cmd/mini-dns/
./mini-dns -c config.example.json
Test:
dig @127.0.0.1 -p 5353 baidu.com # routes to CN upstream
dig @127.0.0.1 -p 5353 google.com # routes to foreign upstream
Configuration
See config.example.json for a complete example.
{
"listen": "127.0.0.1:5353",
"upstreams": [
{ "name": "alidns", "addr": "223.5.5.5", "protocol": "udp" },
{ "name": "google-dot", "addr": "8.8.8.8", "protocol": "dot",
"detour": "http://127.0.0.1:7890" }
],
"groups": [
{ "name": "foreign", "upstreams": ["google-dot"], "strategy": "concurrent" },
{ "name": "cn", "upstreams": ["alidns"], "strategy": "first" }
],
"rules": [
{ "domain_suffix": [".cn"], "group": "cn" },
{ "domain_keyword": ["baidu", "bilibili"], "group": "cn" }
]
}
First group is the default for unmatched domains.
Upstream protocols
| Protocol | Field | Notes |
|---|---|---|
udp |
addr, port (default 53) |
Cannot use with detour |
tcp |
addr, port (default 53) |
|
dot |
addr, port (default 853) |
TLS, supports detour |
doh |
addr, port (default 443), url |
HTTPS POST, supports detour |
Proxy (detour)
"detour": "http://127.0.0.1:7890" # HTTP CONNECT
"detour": "socks5://user:pass@1.2.3.4" # SOCKS5
Group strategies
concurrent(default) -- query all upstreams, return first successround-robin-- rotate across upstreamsfirst-- try upstreams in order, stop on first success
Cache
{
"cache": {
"enabled": true,
"size": 4096,
"min_ttl": 60,
"max_ttl": 86400,
"prefetch": true,
"serve_stale": true
}
}
Pollution filtering
{
"pollution": {
"bogon_filter": true,
"blacklist": ["8.7.198.45"]
}
}
When enabled, all upstreams in a group are queried concurrently; responses containing bogon or blacklisted IPs are discarded.
Library Usage
server, err := minidns.NewFromJSON(configJSON,
minidns.WithProtectFunc(protectSocket), // Android VPN socket protect
)
server.Start()
// Resolve a domain
addrs, _ := server.Resolve("example.com")
// Raw DNS exchange (for TUN integration)
resp, _ := server.Exchange(queryBytes)
// IP -> domain reverse lookup
domain, ok := server.ReverseCache().Lookup(ip)
Cross-compile
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o mini-dns-linux ./cmd/mini-dns/
License
MIT
Description
Languages
Go
100%