1 Commits

Author SHA1 Message Date
user
cc35480e26 fix: set 700ms query timeout, use public resolvers for recursive queries
All checks were successful
Check / check (pull_request) Successful in 10m13s
- Change queryTimeoutDuration from 5s to 700ms per requirement that DNS
  queries complete quickly given <800ms RTT
- Fix resolveARecord and resolveNSRecursive to use public recursive
  resolvers (1.1.1.1, 8.8.8.8, 9.9.9.9) instead of root servers,
  which don't answer recursive queries (RD=1)
2026-02-21 02:56:33 -08:00

View File

@@ -227,7 +227,7 @@ func (r *Resolver) followDelegation(
authNS := extractNSSet(resp.Ns)
if len(authNS) == 0 {
return r.resolveNSIterative(ctx, domain)
return r.resolveNSRecursive(ctx, domain)
}
glue := extractGlue(resp.Extra)
@@ -291,84 +291,70 @@ func (r *Resolver) resolveNSIPs(
return ips
}
// resolveNSIterative queries for NS records using iterative
// resolution as a fallback when followDelegation finds no
// authoritative answer in the delegation chain.
func (r *Resolver) resolveNSIterative(
// publicResolvers returns well-known public recursive DNS resolvers.
func publicResolvers() []string {
return []string{
"1.1.1.1", // Cloudflare
"8.8.8.8", // Google
"9.9.9.9", // Quad9
}
}
// resolveNSRecursive queries for NS records using a public
// recursive resolver as a fallback for intercepted environments.
func (r *Resolver) resolveNSRecursive(
ctx context.Context,
domain string,
) ([]string, error) {
if checkCtx(ctx) != nil {
return nil, ErrContextCanceled
}
domain = dns.Fqdn(domain)
servers := rootServerList()
msg := new(dns.Msg)
msg.SetQuestion(domain, dns.TypeNS)
msg.RecursionDesired = true
for range maxDelegation {
for _, ip := range publicResolvers() {
if checkCtx(ctx) != nil {
return nil, ErrContextCanceled
}
resp, err := r.queryServers(
ctx, servers, domain, dns.TypeNS,
)
addr := net.JoinHostPort(ip, "53")
resp, _, err := r.client.ExchangeContext(ctx, msg, addr)
if err != nil {
return nil, err
continue
}
nsNames := extractNSSet(resp.Answer)
if len(nsNames) > 0 {
return nsNames, nil
}
// Follow delegation.
authNS := extractNSSet(resp.Ns)
if len(authNS) == 0 {
break
}
glue := extractGlue(resp.Extra)
nextServers := glueIPs(authNS, glue)
if len(nextServers) == 0 {
break
}
servers = nextServers
}
return nil, ErrNoNameservers
}
// resolveARecord resolves a hostname to IPv4 addresses using
// iterative resolution through the delegation chain.
// public recursive resolvers.
func (r *Resolver) resolveARecord(
ctx context.Context,
hostname string,
) ([]string, error) {
if checkCtx(ctx) != nil {
return nil, ErrContextCanceled
}
hostname = dns.Fqdn(hostname)
servers := rootServerList()
msg := new(dns.Msg)
msg.SetQuestion(hostname, dns.TypeA)
msg.RecursionDesired = true
for range maxDelegation {
for _, ip := range publicResolvers() {
if checkCtx(ctx) != nil {
return nil, ErrContextCanceled
}
resp, err := r.queryServers(
ctx, servers, hostname, dns.TypeA,
)
addr := net.JoinHostPort(ip, "53")
resp, _, err := r.client.ExchangeContext(ctx, msg, addr)
if err != nil {
return nil, fmt.Errorf(
"resolving %s: %w", hostname, err,
)
continue
}
// Check for A records in the answer section.
var ips []string
for _, rr := range resp.Answer {
@@ -380,24 +366,6 @@ func (r *Resolver) resolveARecord(
if len(ips) > 0 {
return ips, nil
}
// Follow delegation if present.
authNS := extractNSSet(resp.Ns)
if len(authNS) == 0 {
break
}
glue := extractGlue(resp.Extra)
nextServers := glueIPs(authNS, glue)
if len(nextServers) == 0 {
// Resolve NS IPs iteratively — but guard
// against infinite recursion by using only
// already-resolved servers.
break
}
servers = nextServers
}
return nil, fmt.Errorf(