From 63c79c0bad02c7a47fa786f0038db15c479babaf Mon Sep 17 00:00:00 2001 From: clawbot Date: Sun, 22 Feb 2026 03:36:17 -0800 Subject: [PATCH] resolver: reduce query timeout to 1s and limit root fan-out to 3 (closes #29) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Timeout rationale: 3× max antipodal RTT (~300ms) + 10ms processing = ~910ms, rounded to 1s. Root fan-out rationale: if 3 of 13 roots are unreachable, the problem is local. --- internal/resolver/iterative.go | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/internal/resolver/iterative.go b/internal/resolver/iterative.go index 8f41b6d..3b7d16d 100644 --- a/internal/resolver/iterative.go +++ b/internal/resolver/iterative.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "math/rand/v2" "net" "sort" "strings" @@ -13,7 +14,13 @@ import ( ) const ( - queryTimeoutDuration = 5 * time.Second + // queryTimeoutDuration is the per-exchange DNS timeout. + // + // Rationale: maximum RTT to antipodal root/TLD servers is + // ~300ms. We use 3× max RTT + 10ms processing ≈ 910ms, + // rounded to 1s. Combined with maxRetries=2 (3 attempts + // total), worst case per server is 3s before failing over. + queryTimeoutDuration = 1 * time.Second maxRetries = 2 maxDelegation = 20 timeoutMultiplier = 2 @@ -23,7 +30,7 @@ const ( // ErrRefused is returned when a DNS server refuses a query. var ErrRefused = errors.New("dns query refused") -func rootServerList() []string { +func allRootServers() []string { return []string{ "198.41.0.4", // a.root-servers.net "170.247.170.2", // b @@ -41,6 +48,19 @@ func rootServerList() []string { } } +// rootServerList returns 3 randomly-selected root servers. +// The full set is 13; we limit fan-out because the root is +// operated reliably — if 3 are unreachable, the problem is +// local network, not the root. +func rootServerList() []string { + shuffled := allRootServers() + rand.Shuffle(len(shuffled), func(i, j int) { + shuffled[i], shuffled[j] = shuffled[j], shuffled[i] + }) + + return shuffled[:3] +} + func checkCtx(ctx context.Context) error { err := ctx.Err() if err != nil { @@ -302,7 +322,7 @@ func (r *Resolver) resolveNSRecursive( msg.SetQuestion(domain, dns.TypeNS) msg.RecursionDesired = true - for _, ip := range rootServerList()[:3] { + for _, ip := range rootServerList() { if checkCtx(ctx) != nil { return nil, ErrContextCanceled } @@ -333,7 +353,7 @@ func (r *Resolver) resolveARecord( msg.SetQuestion(hostname, dns.TypeA) msg.RecursionDesired = true - for _, ip := range rootServerList()[:3] { + for _, ip := range rootServerList() { if checkCtx(ctx) != nil { return nil, ErrContextCanceled }