NeoMody ec9cdb2784 Fix cache TTL extraction, add TCP idle timeout, close upstreams on shutdown
- 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.
2026-04-02 05:42:20 +08:00
2026-04-01 09:03:56 +08:00

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 (detour field)
  • 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 success
  • round-robin -- rotate across upstreams
  • first -- 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
No description provided
Readme 10 MiB
Languages
Go 100%