check / check (push) Canceled after 0s
When a domain's parent zone's servers answer NXDOMAIN, LookupNS returns ErrNXDomain. The watcher then saves the domain with no nameservers and nxdomain set, shown on the dashboard and in /api/v1/status, asks for none of its records and removes those saved, so its old nameservers go in one NS Change. A domain with no delegation of its own gets an empty set and its records are still asked at the zone it is in. FindAuthoritativeNameservers moves to a parent name only on one of those two answers; when the servers do not answer, it returns the error. After an upgrade, a domain without its own delegation that was saved with its parent zone's nameservers gets one NS Change; the README says so. Model: opus-5-5
147 lines
3.6 KiB
Go
147 lines
3.6 KiB
Go
package resolver
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"time"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
// NewWithFailingTCP returns a Resolver whose TCP client gives up before
|
|
// it can connect, so the retry over TCP of every truncated reply fails.
|
|
func NewWithFailingTCP(log *slog.Logger) *Resolver {
|
|
r := NewFromLogger(log)
|
|
r.tcp = &tcpClient{timeout: time.Nanosecond}
|
|
|
|
return r
|
|
}
|
|
|
|
// NewWithQueryTimeout returns a Resolver whose queries over UDP give up
|
|
// after timeout, so a test that asks an address where nothing answers
|
|
// does not wait out the usual timeout.
|
|
func NewWithQueryTimeout(log *slog.Logger, timeout time.Duration) *Resolver {
|
|
r := NewFromLogger(log)
|
|
r.client = &udpClient{timeout: timeout}
|
|
|
|
return r
|
|
}
|
|
|
|
// FollowDelegation exports followDelegation for testing.
|
|
func (r *Resolver) FollowDelegation(
|
|
ctx context.Context,
|
|
domain string,
|
|
servers []string,
|
|
) ([]string, error) {
|
|
return r.followDelegation(ctx, domain, servers)
|
|
}
|
|
|
|
// FindAuthoritativeNameserversFrom exports findAuthoritativeNameservers
|
|
// for testing.
|
|
func (r *Resolver) FindAuthoritativeNameserversFrom(
|
|
ctx context.Context,
|
|
domain string,
|
|
servers []string,
|
|
) ([]string, error) {
|
|
return r.findAuthoritativeNameservers(ctx, domain, servers)
|
|
}
|
|
|
|
// ResolveNSIterative exports resolveNSIterative for testing.
|
|
func (r *Resolver) ResolveNSIterative(
|
|
ctx context.Context,
|
|
domain string,
|
|
) ([]string, error) {
|
|
return r.resolveNSIterative(ctx, domain)
|
|
}
|
|
|
|
// ExtractRecordValue exports extractRecordValue for testing.
|
|
func ExtractRecordValue(rr dns.RR) string {
|
|
return extractRecordValue(rr)
|
|
}
|
|
|
|
// CollectAnswerRecords exports collectAnswerRecords for testing.
|
|
func CollectAnswerRecords(msg *dns.Msg, resp *NameserverResponse) {
|
|
var state queryState
|
|
|
|
collectAnswerRecords(msg, resp, &state)
|
|
}
|
|
|
|
// UsableReply exports usableReply for testing.
|
|
func UsableReply(resp *dns.Msg, zone string, name string) bool {
|
|
return usableReply(resp, zone, name)
|
|
}
|
|
|
|
// NSSetFrom exports nsSetFrom for testing.
|
|
func NSSetFrom(resp *dns.Msg, domain string) []string {
|
|
return nsSetFrom(resp, domain)
|
|
}
|
|
|
|
// CollectIPs exports collectIPs for testing.
|
|
func CollectIPs(
|
|
results map[string]*NameserverResponse,
|
|
) ([]string, string, error) {
|
|
return collectIPs(results)
|
|
}
|
|
|
|
// QueryServers exports queryServers for testing.
|
|
func (r *Resolver) QueryServers(
|
|
ctx context.Context,
|
|
servers []string,
|
|
zone string,
|
|
name string,
|
|
qtype uint16,
|
|
) (*dns.Msg, error) {
|
|
return r.queryServers(ctx, servers, zone, name, qtype)
|
|
}
|
|
|
|
// QueryEachNS exports queryEachNS for testing.
|
|
func (r *Resolver) QueryEachNS(
|
|
ctx context.Context,
|
|
nameservers []string,
|
|
hostname string,
|
|
) (map[string]*NameserverResponse, error) {
|
|
return r.queryEachNS(ctx, nameservers, hostname, recordTypes())
|
|
}
|
|
|
|
// ResolveNSIPs exports resolveNSIPs for testing, looking each name up
|
|
// as a lookup that no other lookup started.
|
|
func (r *Resolver) ResolveNSIPs(
|
|
ctx context.Context,
|
|
nsNames []string,
|
|
) []string {
|
|
ips, _ := r.resolveNSIPs(ctx, nsNames, 1)
|
|
|
|
return ips
|
|
}
|
|
|
|
// MaxLookupDepth exports maxLookupDepth for testing.
|
|
const MaxLookupDepth = maxLookupDepth
|
|
|
|
// QueryZone exports queryZone for testing.
|
|
func (r *Resolver) QueryZone(
|
|
ctx context.Context,
|
|
given []string,
|
|
withoutAddresses []string,
|
|
zone string,
|
|
name string,
|
|
qtype uint16,
|
|
depth int,
|
|
) (*dns.Msg, error) {
|
|
return r.queryZone(
|
|
ctx, given, withoutAddresses, zone, name, qtype, depth,
|
|
)
|
|
}
|
|
|
|
// RootServerList exports rootServerList for testing.
|
|
func RootServerList() []string {
|
|
return rootServerList()
|
|
}
|
|
|
|
// Shuffled exports shuffled for testing.
|
|
func Shuffled(
|
|
servers []string,
|
|
shuffle func(n int, swap func(i, j int)),
|
|
) []string {
|
|
return shuffled(servers, shuffle)
|
|
}
|