watcher: notify NS query failure and recovery (closes #104)
check / check (push) Successful in 1m37s
check / check (push) Successful in 1m37s
LookupAllRecords now returns each nameserver's response, so the watcher saves its status: ok when it answered, NXDOMAIN and no records included, and error with the reason when it timed out, answered SERVFAIL or REFUSED, or could not be reached. A nameserver that starts failing sends NS Failure and one that answers again sends NS Recovery. A failing nameserver is left out of the record change and inconsistency comparisons. The resolver used to report REFUSED and network errors as an answer with no records; they are now errors. A lookup cut short by its context now returns an error instead of a failure of the nameserver it was querying. Model: opus-5-5
This commit is contained in:
@@ -504,7 +504,9 @@ func (r *Resolver) queryAllTypes(
|
||||
type queryState struct {
|
||||
gotNXDomain bool
|
||||
gotSERVFAIL bool
|
||||
gotRefused bool
|
||||
gotTimeout bool
|
||||
netErr error
|
||||
hasRecords bool
|
||||
}
|
||||
|
||||
@@ -542,8 +544,13 @@ func (r *Resolver) querySingleType(
|
||||
) {
|
||||
msg, err := r.queryDNS(ctx, nsIP, hostname, qtype)
|
||||
if err != nil {
|
||||
if isTimeout(err) {
|
||||
switch {
|
||||
case isTimeout(err):
|
||||
state.gotTimeout = true
|
||||
case errors.Is(err, ErrRefused):
|
||||
state.gotRefused = true
|
||||
default:
|
||||
state.netErr = err
|
||||
}
|
||||
|
||||
return
|
||||
@@ -603,6 +610,12 @@ func classifyResponse(resp *NameserverResponse, state queryState) {
|
||||
case state.gotSERVFAIL && !state.hasRecords:
|
||||
resp.Status = StatusError
|
||||
resp.Error = "server returned SERVFAIL"
|
||||
case state.gotRefused && !state.hasRecords:
|
||||
resp.Status = StatusError
|
||||
resp.Error = "server returned REFUSED"
|
||||
case state.netErr != nil && !state.hasRecords:
|
||||
resp.Status = StatusError
|
||||
resp.Error = "network error: " + state.netErr.Error()
|
||||
case !state.hasRecords && !state.gotNXDomain:
|
||||
resp.Status = StatusNoData
|
||||
}
|
||||
@@ -682,11 +695,14 @@ func (r *Resolver) queryEachNS(
|
||||
results := make(map[string]*NameserverResponse)
|
||||
|
||||
for _, ns := range nameservers {
|
||||
resp, err := r.QueryNameserver(ctx, ns, hostname)
|
||||
|
||||
// A query the context cut short says nothing about the
|
||||
// nameserver, so it must not be returned as its failure.
|
||||
if checkCtx(ctx) != nil {
|
||||
return nil, ErrContextCanceled
|
||||
}
|
||||
|
||||
resp, err := r.QueryNameserver(ctx, ns, hostname)
|
||||
if err != nil {
|
||||
results[ns] = &NameserverResponse{
|
||||
Nameserver: ns,
|
||||
@@ -714,21 +730,13 @@ func (r *Resolver) LookupNS(
|
||||
|
||||
// LookupAllRecords performs iterative resolution to find all DNS
|
||||
// records for the given hostname, keyed by authoritative nameserver.
|
||||
// Each nameserver's response carries its status and error with its
|
||||
// records.
|
||||
func (r *Resolver) LookupAllRecords(
|
||||
ctx context.Context,
|
||||
hostname string,
|
||||
) (map[string]map[string][]string, error) {
|
||||
results, err := r.QueryAllNameservers(ctx, hostname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := make(map[string]map[string][]string, len(results))
|
||||
for ns, resp := range results {
|
||||
out[ns] = resp.Records
|
||||
}
|
||||
|
||||
return out, nil
|
||||
) (map[string]*NameserverResponse, error) {
|
||||
return r.QueryAllNameservers(ctx, hostname)
|
||||
}
|
||||
|
||||
// ResolveIPAddresses resolves a hostname to all IPv4 and IPv6
|
||||
|
||||
Reference in New Issue
Block a user