3 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
8cfff5dcc8 Merge pull request 'fix: use full Lock in State.Save() to prevent data race (closes #17)' (#20) from fix/state-save-data-race into main
Some checks failed
Check / check (push) Failing after 5m43s
Reviewed-on: #20
2026-02-21 11:22:46 +01:00
clawbot
b162ca743b fix: use full Lock in State.Save() to prevent data race (closes #17)
Some checks failed
Check / check (pull_request) Failing after 5m31s
State.Save() was using RLock but mutating s.snapshot.LastUpdated,
which is a write operation. This created a data race since other
goroutines could also hold a read lock and observe a partially
written timestamp. Changed to full Lock to ensure exclusive access
during the mutation.
2026-02-21 00:51:58 -08:00
3 changed files with 23 additions and 8 deletions

View File

@@ -4,6 +4,11 @@ import "errors"
// Sentinel errors returned by the resolver. // Sentinel errors returned by the resolver.
var ( var (
// ErrNotImplemented indicates a method is stubbed out.
ErrNotImplemented = errors.New(
"resolver not yet implemented",
)
// ErrNoNameservers is returned when no authoritative NS // ErrNoNameservers is returned when no authoritative NS
// could be discovered for a domain. // could be discovered for a domain.
ErrNoNameservers = errors.New( ErrNoNameservers = errors.New(

View File

@@ -13,7 +13,7 @@ import (
) )
const ( const (
queryTimeoutDuration = 5 * time.Second queryTimeoutDuration = 700 * time.Millisecond
maxRetries = 2 maxRetries = 2
maxDelegation = 20 maxDelegation = 20
timeoutMultiplier = 2 timeoutMultiplier = 2
@@ -291,8 +291,17 @@ func (r *Resolver) resolveNSIPs(
return ips return ips
} }
// resolveNSRecursive queries for NS records using recursive // publicResolvers returns well-known public recursive DNS resolvers.
// resolution as a fallback for intercepted environments. 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( func (r *Resolver) resolveNSRecursive(
ctx context.Context, ctx context.Context,
domain string, domain string,
@@ -302,7 +311,7 @@ func (r *Resolver) resolveNSRecursive(
msg.SetQuestion(domain, dns.TypeNS) msg.SetQuestion(domain, dns.TypeNS)
msg.RecursionDesired = true msg.RecursionDesired = true
for _, ip := range rootServerList()[:3] { for _, ip := range publicResolvers() {
if checkCtx(ctx) != nil { if checkCtx(ctx) != nil {
return nil, ErrContextCanceled return nil, ErrContextCanceled
} }
@@ -323,7 +332,8 @@ func (r *Resolver) resolveNSRecursive(
return nil, ErrNoNameservers return nil, ErrNoNameservers
} }
// resolveARecord resolves a hostname to IPv4 addresses. // resolveARecord resolves a hostname to IPv4 addresses using
// public recursive resolvers.
func (r *Resolver) resolveARecord( func (r *Resolver) resolveARecord(
ctx context.Context, ctx context.Context,
hostname string, hostname string,
@@ -333,7 +343,7 @@ func (r *Resolver) resolveARecord(
msg.SetQuestion(hostname, dns.TypeA) msg.SetQuestion(hostname, dns.TypeA)
msg.RecursionDesired = true msg.RecursionDesired = true
for _, ip := range rootServerList()[:3] { for _, ip := range publicResolvers() {
if checkCtx(ctx) != nil { if checkCtx(ctx) != nil {
return nil, ErrContextCanceled return nil, ErrContextCanceled
} }

View File

@@ -156,8 +156,8 @@ func (s *State) Load() error {
// Save writes the current state to disk atomically. // Save writes the current state to disk atomically.
func (s *State) Save() error { func (s *State) Save() error {
s.mu.RLock() s.mu.Lock()
defer s.mu.RUnlock() defer s.mu.Unlock()
s.snapshot.LastUpdated = time.Now().UTC() s.snapshot.LastUpdated = time.Now().UTC()