feat(dnstt): integrate hmrd_multi_resolver_dns smart pool inside dnstt#21
Conversation
18b2332 to
445e384
Compare
| // the dnstt tunnel can speak to as a single virtual resolver. Library | ||
| // defaults (5s deadline, 2s per-attempt, 5s probe, 8 failures = down, | ||
| // round-robin LB) cover the common case; tuning can land later. | ||
| func startSmartPool(ctx context.Context) (*multidns.Manager, string, error) { |
There was a problem hiding this comment.
اینو ببر توی hmrd
چون مشترکه بین همه
There was a problem hiding this comment.
Done in hiddify/hmrd_multi_resolver_dns#5 (merged) — moved into the library as multidns.StartLocal(opts) → (*Manager, addr, error). The dnstt outbound now just calls that. Bonus: it uses pre-bound net.PacketConn / net.Listener so the pick-port-then-rebind TOCTOU window is gone.
| c.tunnels = append(c.tunnels, nil) | ||
| // } | ||
| } | ||
| c.mutlitunnel = nil |
There was a problem hiding this comment.
اینم حذف بشه چون تونل دیگه تغییر نمیکنه ریزالور هندل مبکنه
There was a problem hiding this comment.
البته اگه اسمارت پول فعال بود نیاز نیست
There was a problem hiding this comment.
Done — c.mutlitunnel = nil is now only reset in the legacy path. In smart-pool mode the dnstt tunnel always points at the same virtual resolver, so the existing tunnel keeps running while the multidns pool rotates real upstreams behind it.
| // Smart pool mode: register the resolver as a multidns upstream; | ||
| // c.resolvers stays at the single virtual resolver set up in | ||
| // NewOutbound, so dnstt's tunnel rides on top of the pool. | ||
| _, _ = c.mdMgr.AddResolver(multidns.ResolverConfig{Protocol: mdProto(resolver.ResolverType), Address: resolver.ResolverAddr}) |
There was a problem hiding this comment.
اینجا به نظرم ریزالور به صورت
dot://ip:port
tcp://ip:port
udp://ip: port or ip:port
دریافت کنه بهتر باشه
تغییر درر
Hmrd
نیازه
There was a problem hiding this comment.
Done in hiddify/hmrd_multi_resolver_dns#5. Library now exposes multidns.AddResolverURL(s) accepting udp://, tcp://, dot:// (or tls://), https:// (or doh://), or bare host[:port] (defaults to UDP/53). dnstt calls it that way and the local mdProto switch is gone.
When `smart_pool: true` is set on a dnstt outbound, every working
resolver added via the existing addResolver flow is registered as an
upstream of an hmrd_multi_resolver_dns pool, and dnstt's tunnel speaks
to a single virtual resolver pointing at the local DNS listener the
pool fronts. The pool transparently handles deadline-aware failover,
AIMD rate-limit throttling (REFUSED / SERVFAIL / HTTP 429), and
recovery probing across the upstreams — replacing dnstt's own
per-resolver tunnel distribution.
Why: dnstt's tunnel sends DNS queries to one resolver at a time, so
when a resolver gets rate-limited or blocked the tunnel stalls until
the operator rotates manually. The integration sits at exactly the line
the manager pointed at — addResolver in dnstt_func.go — so the
user-visible config stays a regular dnstt outbound with one extra bool.
Plumbing:
- option/dnstt.go — DnsttOptions.SmartPool bool
- protocol/hiddify/dnstt/outbound.go — `mdMgr *multidns.Manager`
field; multidns.StartLocal
on NewOutbound; Close
tears the manager (and
bundled listener) down
- protocol/hiddify/dnstt/dnstt_func.go — addResolver registers each
working resolver via
multidns.AddResolverURL
(udp://, dot://, https://,
or bare host:port for
UDP/53). c.mutlitunnel
isn't reset in this branch
because the tunnel keeps
pointing at the same
virtual resolver — only
the legacy path needs the
tunnel rebuild.
go.mod adds github.com/hiddify/hmrd_multi_resolver_dns
v0.0.0-20260429114007-8d809dc33d0e. The library require pulls go 1.25,
so go mod tidy bumped the toolchain line accordingly.
Default behavior is unchanged: leaving smart_pool unset preserves the
existing per-resolver tunnel distribution.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
445e384 to
bccd6e7
Compare
Summary
Replaces the prior shape — a separate `smart_dns_pool` sing-box service (#20) and the earlier top-level `hmrd` DNS transport — with an in-dnstt integration of `hiddify/hmrd_multi_resolver_dns`.
When `smart_pool.enabled` is set on a `dnstt` outbound, every working resolver added via `addResolver` is registered as an upstream of an internal multidns pool. The dnstt tunnel then speaks to a single virtual resolver pointing at a local `127.0.0.1:` listener; the pool transparently handles deadline-aware failover, AIMD rate-limit throttling (REFUSED / SERVFAIL / HTTP 429), and recovery probing across the upstreams — replacing dnstt's own per-resolver tunnel distribution.
Why
dnstt's tunnel sends DNS queries to one resolver at a time and has no smart routing across them — when a resolver gets rate-limited or blocked, the tunnel stalls until the operator rotates manually. The manager wanted this fixed inline at the resolver-pool level, not behind a separate sing-box service the operator has to wire up. The integration sits at exactly the line he pointed at: `addResolver` in `protocol/hiddify/dnstt/dnstt_func.go`.
Sample config
{ "outbounds": [ { "type": "dnstt", "tag": "dnstt-out", "domain": "t.example.com", "pubkey": "...", "resolvers": [ "1.1.1.1:53", "8.8.8.8:53", "https://cloudflare-dns.com/dns-query", "dot://1.1.1.1:853" ], "smart_pool": { "enabled": true, "load_balance": "weighted", "deadline": "5s", "per_attempt": "2s", "probe_interval": "5s", "down_after": 8 } } ] }Leaving `smart_pool` unset preserves the existing per-resolver tunnel distribution — default behavior is unchanged.
Plumbing
The library require pulls go 1.25, so `go mod tidy` bumped the toolchain line from 1.24.7 to 1.25.6.
What's deliberately not here
Test plan
🤖 Generated with Claude Code