Compare commits
1
Commits
bb75020f1f
...
30f8f05fd5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30f8f05fd5 |
@@ -19,6 +19,8 @@ trial run of the finished image: https://git.eeqj.de/sneak/dnswatcher/issues/149
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-10-02: a nameserver's addresses are looked up asking only for A, AAAA and
|
||||
CNAME records, and a live test attempt may take 18s, not 8s (closes #214).
|
||||
- 2026-10-02: the resolver tries root servers, and every other server list it
|
||||
walks, in a random order each time, not always from the top (closes #138).
|
||||
- 2026-10-02: a name listed more than once in `DNSWATCHER_TARGETS`, in any
|
||||
|
||||
@@ -36,11 +36,14 @@ const (
|
||||
// before the test fails.
|
||||
attempts = 3
|
||||
|
||||
// AttemptTimeout bounds one attempt. Worst case for an operation
|
||||
// is attempts * AttemptTimeout plus the backoff — about 26
|
||||
// seconds, well inside the 90-second `go test -timeout` backstop
|
||||
// even when several operations exhaust their attempts.
|
||||
AttemptTimeout = 8 * time.Second
|
||||
// AttemptTimeout bounds one attempt. It must fit the longest
|
||||
// operation, a watcher check, which sends over a hundred queries one
|
||||
// after another and on a slow build host takes several times as long
|
||||
// as the few seconds it takes on a fast one. Worst case for an
|
||||
// operation is attempts * AttemptTimeout plus the backoff — about 56
|
||||
// seconds, under the suite's 60-second cap and inside the 90-second
|
||||
// `go test -timeout` backstop.
|
||||
AttemptTimeout = 18 * time.Second
|
||||
|
||||
// backoffBase is the delay after the first failed attempt; it is
|
||||
// multiplied by backoffFactor each time.
|
||||
|
||||
@@ -34,7 +34,7 @@ func (r *Resolver) QueryEachNS(
|
||||
nameservers []string,
|
||||
hostname string,
|
||||
) (map[string]*NameserverResponse, error) {
|
||||
return r.queryEachNS(ctx, nameservers, hostname)
|
||||
return r.queryEachNS(ctx, nameservers, hostname, recordTypes())
|
||||
}
|
||||
|
||||
// ResolveNSIPs exports resolveNSIPs for testing.
|
||||
|
||||
@@ -533,12 +533,39 @@ func (r *Resolver) FindAuthoritativeNameservers(
|
||||
return nil, ErrNoNameservers
|
||||
}
|
||||
|
||||
// recordTypes returns the record types a nameserver is asked for when a
|
||||
// name is checked.
|
||||
func recordTypes() []uint16 {
|
||||
return []uint16{
|
||||
dns.TypeA, dns.TypeAAAA, dns.TypeCNAME,
|
||||
dns.TypeMX, dns.TypeTXT, dns.TypeSRV,
|
||||
dns.TypeCAA, dns.TypeNS,
|
||||
}
|
||||
}
|
||||
|
||||
// addressTypes returns the record types ResolveIPAddresses asks for,
|
||||
// the only ones it reads.
|
||||
func addressTypes() []uint16 {
|
||||
return []uint16{dns.TypeA, dns.TypeAAAA, dns.TypeCNAME}
|
||||
}
|
||||
|
||||
// QueryNameserver queries a specific nameserver for all record
|
||||
// types and builds a NameserverResponse.
|
||||
func (r *Resolver) QueryNameserver(
|
||||
ctx context.Context,
|
||||
nsHostname string,
|
||||
hostname string,
|
||||
) (*NameserverResponse, error) {
|
||||
return r.queryNameserver(ctx, nsHostname, hostname, recordTypes())
|
||||
}
|
||||
|
||||
// queryNameserver queries a specific nameserver for the record types
|
||||
// in qtypes and builds a NameserverResponse.
|
||||
func (r *Resolver) queryNameserver(
|
||||
ctx context.Context,
|
||||
nsHostname string,
|
||||
hostname string,
|
||||
qtypes []uint16,
|
||||
) (*NameserverResponse, error) {
|
||||
if checkCtx(ctx) != nil {
|
||||
return nil, ErrContextCanceled
|
||||
@@ -551,7 +578,7 @@ func (r *Resolver) QueryNameserver(
|
||||
|
||||
hostname = dns.Fqdn(hostname)
|
||||
|
||||
return r.queryAllTypes(ctx, nsHostname, nsIPs[0], hostname)
|
||||
return r.queryTypes(ctx, nsHostname, nsIPs[0], hostname, qtypes)
|
||||
}
|
||||
|
||||
// QueryNameserverIP queries a nameserver by its IP address directly,
|
||||
@@ -568,14 +595,15 @@ func (r *Resolver) QueryNameserverIP(
|
||||
|
||||
hostname = dns.Fqdn(hostname)
|
||||
|
||||
return r.queryAllTypes(ctx, nsHostname, nsIP, hostname)
|
||||
return r.queryTypes(ctx, nsHostname, nsIP, hostname, recordTypes())
|
||||
}
|
||||
|
||||
func (r *Resolver) queryAllTypes(
|
||||
func (r *Resolver) queryTypes(
|
||||
ctx context.Context,
|
||||
nsHostname string,
|
||||
nsIP string,
|
||||
hostname string,
|
||||
qtypes []uint16,
|
||||
) (*NameserverResponse, error) {
|
||||
resp := &NameserverResponse{
|
||||
Nameserver: nsHostname,
|
||||
@@ -583,12 +611,6 @@ func (r *Resolver) queryAllTypes(
|
||||
Status: StatusOK,
|
||||
}
|
||||
|
||||
qtypes := []uint16{
|
||||
dns.TypeA, dns.TypeAAAA, dns.TypeCNAME,
|
||||
dns.TypeMX, dns.TypeTXT, dns.TypeSRV,
|
||||
dns.TypeCAA, dns.TypeNS,
|
||||
}
|
||||
|
||||
state := r.queryEachType(ctx, nsIP, hostname, qtypes, resp)
|
||||
classifyResponse(resp, state)
|
||||
|
||||
@@ -779,18 +801,19 @@ func (r *Resolver) QueryAllNameservers(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.queryEachNS(ctx, nameservers, hostname)
|
||||
return r.queryEachNS(ctx, nameservers, hostname, recordTypes())
|
||||
}
|
||||
|
||||
func (r *Resolver) queryEachNS(
|
||||
ctx context.Context,
|
||||
nameservers []string,
|
||||
hostname string,
|
||||
qtypes []uint16,
|
||||
) (map[string]*NameserverResponse, error) {
|
||||
results := make(map[string]*NameserverResponse)
|
||||
|
||||
for _, ns := range nameservers {
|
||||
resp, err := r.QueryNameserver(ctx, ns, hostname)
|
||||
resp, err := r.queryNameserver(ctx, ns, hostname, qtypes)
|
||||
|
||||
// A query the context cut short says nothing about the
|
||||
// nameserver, so it must not be returned as its failure.
|
||||
@@ -835,9 +858,10 @@ func (r *Resolver) LookupAllRecords(
|
||||
}
|
||||
|
||||
// ResolveIPAddresses resolves a hostname to all IPv4 and IPv6
|
||||
// addresses, following CNAME chains up to MaxCNAMEDepth. When no
|
||||
// nameserver of the name's zone answered, it returns an error rather
|
||||
// than no addresses.
|
||||
// addresses, following CNAME chains up to MaxCNAMEDepth. It asks each
|
||||
// nameserver of the name's zone for its A, AAAA and CNAME records only.
|
||||
// When no nameserver of the name's zone answered, it returns an error
|
||||
// rather than no addresses.
|
||||
func (r *Resolver) ResolveIPAddresses(
|
||||
ctx context.Context,
|
||||
hostname string,
|
||||
@@ -858,7 +882,12 @@ func (r *Resolver) resolveIPWithCNAME(
|
||||
return nil, ErrCNAMEDepthExceeded
|
||||
}
|
||||
|
||||
results, err := r.QueryAllNameservers(ctx, hostname)
|
||||
nameservers, err := r.FindAuthoritativeNameservers(ctx, hostname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
results, err := r.queryEachNS(ctx, nameservers, hostname, addressTypes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user