resolver: error from ResolveIPAddresses when no nameserver answered (closes #190)
check / check (push) Failing after 2m9s
check / check (push) Failing after 2m9s
ResolveIPAddresses now returns an error, not no addresses, when no nameserver of the name's zone answered. A nameserver with status timeout or error is not an answer; one answer, even NXDOMAIN, is enough for an empty result without an error. When every server of a zone fails, FindAuthoritativeNameservers moves on to the parent name, whose servers only refer the query onward. Such a referral now has status error, so it is no answer either, and a hostname's saved records show it as error. The only caller, the nameserver address lookup, already keeps the previous addresses on an error; its comment no longer says the resolver hides this case. Model: opus-5-5
This commit is contained in:
@@ -633,6 +633,82 @@ func TestQueryNameserverIP_Timeout(t *testing.T) {
|
||||
assert.NotEmpty(t, resp.Error)
|
||||
}
|
||||
|
||||
// TestCollectIPs_NoNameserverAnswered takes the response of a
|
||||
// nameserver at 192.0.2.1, where nothing answers, as
|
||||
// TestQueryNameserverIP_Timeout does. Addresses collected from
|
||||
// nameservers that all failed to answer are an error, not none.
|
||||
func TestCollectIPs_NoNameserverAnswered(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
r := newTestResolver(t)
|
||||
|
||||
// The deadline outlasts the first try, as in
|
||||
// TestQueryNameserverIP_Timeout.
|
||||
ctx, cancel := context.WithTimeout(
|
||||
context.Background(), 3*time.Second,
|
||||
)
|
||||
t.Cleanup(cancel)
|
||||
|
||||
resp, err := r.QueryNameserverIP(
|
||||
ctx, "unreachable.test.", "192.0.2.1",
|
||||
"example.com",
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
ips, _, err := resolver.CollectIPs(
|
||||
map[string]*resolver.NameserverResponse{resp.Nameserver: resp},
|
||||
)
|
||||
require.ErrorIs(t, err, resolver.ErrNoNameserverAnswered)
|
||||
assert.Empty(t, ips)
|
||||
}
|
||||
|
||||
// TestCollectIPs_ReferralIsNoAnswer asks a root server about
|
||||
// example.com, which the root zone does not hold, so it only refers the
|
||||
// query to the com servers. That reply is no answer, as is a parent
|
||||
// zone's when every server of the name's own zone failed.
|
||||
func TestCollectIPs_ReferralIsNoAnswer(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
r := newTestResolver(t)
|
||||
|
||||
var resp *resolver.NameserverResponse
|
||||
|
||||
livednstest.Retry(
|
||||
t,
|
||||
"QueryNameserverIP(a.root-servers.net, example.com)",
|
||||
func(ctx context.Context) error {
|
||||
var err error
|
||||
|
||||
resp, err = r.QueryNameserverIP(
|
||||
ctx, "a.root-servers.net.", "198.41.0.4",
|
||||
"example.com",
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// A timeout or a network error is no reply at all.
|
||||
if resp.Status == resolver.StatusTimeout ||
|
||||
strings.HasPrefix(resp.Error, "network error") {
|
||||
return fmt.Errorf(
|
||||
"%w: %s", livednstest.ErrNoAnswer, resp.Error,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
assert.Equal(t, resolver.StatusError, resp.Status)
|
||||
assert.Equal(t, "server returned a referral", resp.Error)
|
||||
|
||||
ips, _, err := resolver.CollectIPs(
|
||||
map[string]*resolver.NameserverResponse{resp.Nameserver: resp},
|
||||
)
|
||||
require.ErrorIs(t, err, resolver.ErrNoNameserverAnswered)
|
||||
assert.Empty(t, ips)
|
||||
}
|
||||
|
||||
func TestResolveIPAddresses_ContextCanceled(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user