resolver: a reply marked recursion available from a root server's address stops the lookup (closes #300)
check / check (push) Waiting to run
check / check (push) Waiting to run
dnswatcher never asks for recursion and a root server never offers it, so a reply marked recursion available from a root server's address came from a resolver on the network answering in its place. The first such reply, a refusal included, now stops the lookup at once with "this network intercepts DNS queries", naming the address, whether or not that resolver had the name cached. queryDNS returns a refused reply along with ErrRefused so the mark can be seen; a refusal without the mark is handled as before. On such a network dnswatcher and its tests fail with that error, by design, as the owner ruled on the issue. Model: opus-5-5
This commit was merged in pull request #301.
This commit is contained in:
@@ -850,6 +850,74 @@ func TestQueryServers_NotEveryRootServerRefused(t *testing.T) {
|
||||
require.NotContains(t, err.Error(), "refused a query for")
|
||||
}
|
||||
|
||||
// TestQueryServers_RecursiveResolversAsRootServers passes public
|
||||
// recursive resolvers to QueryServers as the servers of the root zone,
|
||||
// as a network that sends every DNS query to a resolver would. Their
|
||||
// replies are marked recursion available, which no root server's is:
|
||||
// SERVFAIL, REFUSED, or an answer from a resolver's cache, as 1.1.1.1
|
||||
// gives for com., which would be used if a root server gave it. The
|
||||
// first such reply ends the lookup with ErrIntercepted, naming the
|
||||
// resolver that gave it. In the first group the replies differ; every
|
||||
// resolver in the second refuses a query that does not ask for
|
||||
// recursion, as 9.9.9.9 does, so there a refusal ends the lookup
|
||||
// instead of being passed over. Each group has resolvers of four
|
||||
// operators, and a resolver that gives no reply is passed over for the
|
||||
// next, so one of them answering is enough.
|
||||
func TestQueryServers_RecursiveResolversAsRootServers(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
r := newTestResolver(t)
|
||||
groups := [][]string{
|
||||
{"1.1.1.1", "8.8.8.8", "9.9.9.9", "208.67.222.222"},
|
||||
{"64.6.64.6", "185.222.222.222", "4.2.2.1", "94.140.14.14"},
|
||||
}
|
||||
|
||||
for _, resolvers := range groups {
|
||||
for _, name := range []string{"com.", "desec.io."} {
|
||||
var err error
|
||||
|
||||
livednstest.Retry(
|
||||
t,
|
||||
"QueryServers("+strings.Join(resolvers, ", ")+
|
||||
" as root servers, "+name+")",
|
||||
func(ctx context.Context) error {
|
||||
_, err = r.QueryServers(
|
||||
ctx, resolvers, ".", name, dns.TypeNS,
|
||||
)
|
||||
|
||||
// When no resolver gave a reply marked recursion
|
||||
// available, none may have replied at all, so the
|
||||
// attempt is tried again.
|
||||
if err != nil && !strings.Contains(
|
||||
err.Error(), "marked recursion available",
|
||||
) {
|
||||
return fmt.Errorf(
|
||||
"%w: %w", livednstest.ErrNoAnswer, err,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
require.ErrorIs(t, err, resolver.ErrIntercepted)
|
||||
|
||||
// The error names the resolver whose reply ended the
|
||||
// lookup.
|
||||
messages := make([]string, 0, len(resolvers))
|
||||
for _, ip := range resolvers {
|
||||
messages = append(messages,
|
||||
"query "+name+" @"+ip+": reply marked recursion "+
|
||||
"available, so not from a root server: "+
|
||||
"this network intercepts DNS queries",
|
||||
)
|
||||
}
|
||||
|
||||
require.Contains(t, messages, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryNameserver_RecordsSorted(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user