resolver: query a hostname at its own zone's servers (closes #189)
check / check (push) Failing after 2m4s
check / check (push) Failing after 2m4s
A hostname's nameservers came from its last two labels, so a name under co.uk was asked at the co.uk servers and a name in a delegated subdomain at the parent's servers; both only refer onward. The hostname now goes through FindAuthoritativeNameservers, which follows delegations for the name and walks up its labels until it finds the zone it is in. followDelegation now stops at an authoritative reply: that server holds the zone, so its reply is not a referral. Without this, a CNAME answer that also lists the zone's NS records in its authority section, as many servers send, was followed as a referral until the delegation limit. Model: opus-5-5
This commit was merged in pull request #191.
This commit is contained in:
@@ -17,7 +17,6 @@ const (
|
||||
maxRetries = 2
|
||||
maxDelegation = 20
|
||||
timeoutMultiplier = 2
|
||||
minDomainLabels = 2
|
||||
)
|
||||
|
||||
// ErrRefused is returned when a DNS server refuses a query.
|
||||
@@ -225,6 +224,15 @@ func (r *Resolver) followDelegation(
|
||||
return ansNS, nil
|
||||
}
|
||||
|
||||
// An authoritative reply comes from the servers of the zone
|
||||
// domain is in; it is not a referral, even when its authority
|
||||
// section lists that zone's NS records. Without NS records in
|
||||
// the answer, domain is not the zone's apex and has no
|
||||
// nameservers of its own.
|
||||
if resp.Authoritative {
|
||||
return nil, ErrNoNameservers
|
||||
}
|
||||
|
||||
authNS := extractNSSet(resp.Ns)
|
||||
if len(authNS) == 0 {
|
||||
return r.resolveNSIterative(ctx, domain)
|
||||
@@ -407,7 +415,9 @@ func (r *Resolver) resolveARecord(
|
||||
|
||||
// FindAuthoritativeNameservers traces the delegation chain from
|
||||
// root servers to discover all authoritative nameservers for the
|
||||
// given domain. Walks up the label hierarchy for subdomains.
|
||||
// given domain. For a name that is not a zone apex it tries each
|
||||
// parent name in turn, so it returns the nameservers of the zone the
|
||||
// name is in.
|
||||
func (r *Resolver) FindAuthoritativeNameservers(
|
||||
ctx context.Context,
|
||||
domain string,
|
||||
@@ -653,22 +663,8 @@ func extractRecordValue(rr dns.RR) string {
|
||||
}
|
||||
}
|
||||
|
||||
// parentDomain returns the registerable parent domain.
|
||||
func parentDomain(hostname string) string {
|
||||
hostname = dns.Fqdn(strings.ToLower(hostname))
|
||||
labels := dns.SplitDomainName(hostname)
|
||||
|
||||
if len(labels) <= minDomainLabels {
|
||||
return strings.Join(labels, ".") + "."
|
||||
}
|
||||
|
||||
return strings.Join(
|
||||
labels[len(labels)-minDomainLabels:], ".",
|
||||
) + "."
|
||||
}
|
||||
|
||||
// QueryAllNameservers discovers auth NSes for the hostname's
|
||||
// parent domain, then queries each one independently.
|
||||
// QueryAllNameservers discovers the auth NSes of the zone the
|
||||
// hostname is in, then queries each one independently.
|
||||
func (r *Resolver) QueryAllNameservers(
|
||||
ctx context.Context,
|
||||
hostname string,
|
||||
@@ -677,9 +673,7 @@ func (r *Resolver) QueryAllNameservers(
|
||||
return nil, ErrContextCanceled
|
||||
}
|
||||
|
||||
parent := parentDomain(hostname)
|
||||
|
||||
nameservers, err := r.FindAuthoritativeNameservers(ctx, parent)
|
||||
nameservers, err := r.FindAuthoritativeNameservers(ctx, hostname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user