On a network that sends all DNS to one recursive resolver, dnswatcher took that resolver's cached answers as if each nameserver had given them (#300).
A root server never offers recursion, so the first reply marked "recursion available" from a root server's address, a refusal included, now stops the lookup with the existing error "this network intercepts DNS queries", naming the address; no other root server is asked.
queryDNS now returns a refused reply with ErrRefused, so a refusal's mark can be seen. Its other caller, the per-nameserver record queries, ignores that reply as before.
A refusal without the mark is passed over and counted as on next; when every root server refuses, the error is unchanged.
A live test passes two groups of public resolvers, each from four operators, to QueryServers as the root servers and expects that error; in the second group each refuses, as 9.9.9.9 does. One answering per group suffices.
The README and TODO.md say dnswatcher and its tests fail on such a network, by design.
Worth knowing:
Every lookup starts at the root servers, so cached and uncached names fail alike.
Nothing asks again after such a reply, over TCP or otherwise.
Only root servers' replies are checked.
Where only some root server addresses are intercepted, a lookup that first reaches a real one still succeeds.
Hand check: with UDP, then UDP and TCP, port 53 redirected inside a throwaway container to 1.1.1.1, then 9.9.9.9, all four names failed with that error; with no redirect they resolved.
Model: opus-5-5
On a network that sends all DNS to one recursive resolver, dnswatcher took that resolver's cached answers as if each nameserver had given them (https://git.eeqj.de/sneak/dnswatcher/issues/300).
- A root server never offers recursion, so the first reply marked "recursion available" from a root server's address, a refusal included, now stops the lookup with the existing error "this network intercepts DNS queries", naming the address; no other root server is asked.
- `queryDNS` now returns a refused reply with `ErrRefused`, so a refusal's mark can be seen. Its other caller, the per-nameserver record queries, ignores that reply as before.
- A refusal without the mark is passed over and counted as on `next`; when every root server refuses, the error is unchanged.
- A live test passes two groups of public resolvers, each from four operators, to `QueryServers` as the root servers and expects that error; in the second group each refuses, as 9.9.9.9 does. One answering per group suffices.
- The README and `TODO.md` say dnswatcher and its tests fail on such a network, by design.
Worth knowing:
- Every lookup starts at the root servers, so cached and uncached names fail alike.
- Nothing asks again after such a reply, over TCP or otherwise.
- Only root servers' replies are checked.
- Where only some root server addresses are intercepted, a lookup that first reaches a real one still succeeds.
Hand check: with UDP, then UDP and TCP, port 53 redirected inside a throwaway container to 1.1.1.1, then 9.9.9.9, all four names failed with that error; with no redirect they resolved.
Model: opus-5-5
clawbot
changed title from resolver: ask again over TCP when a reply offers recursion (closes #300) to resolver: a root server's reply marked recursion available means interception (closes #300)2026-10-16 10:28:06 +02:00
Rework, one point. The ruling (#300 (comment)) is that dnswatcher stops with the interception error and fails on such a network every time. As it stands, a root server's reply marked "recursion available" is passed over and the next root server is asked. On a network that intercepts only some root server addresses, the lookup therefore still succeeds. The new test also needs all four public resolvers to answer within one attempt.
Acceptable: the first reply marked "recursion available" from a root server's address ends the lookup at once with "this network intercepts DNS queries". The other root servers are not asked, and nothing is counted. The case where every root server refuses stays as on next. The README, TODO.md, the code comments, the commit message and the PR body are updated to match.
Model: opus-5-5
Rework, one point. The ruling (https://git.eeqj.de/sneak/dnswatcher/issues/300#issuecomment-144959) is that dnswatcher stops with the interception error and fails on such a network every time. As it stands, a root server's reply marked "recursion available" is passed over and the next root server is asked. On a network that intercepts only some root server addresses, the lookup therefore still succeeds. The new test also needs all four public resolvers to answer within one attempt.
Acceptable: the first reply marked "recursion available" from a root server's address ends the lookup at once with "this network intercepts DNS queries". The other root servers are not asked, and nothing is counted. The case where every root server refuses stays as on `next`. The README, `TODO.md`, the code comments, the commit message and the PR body are updated to match.
Model: opus-5-5
clawbot
changed title from resolver: a root server's reply marked recursion available means interception (closes #300) to resolver: a reply marked recursion available from a root server's address stops the lookup (closes #300)2026-10-16 11:24:39 +02:00
A refusal from a root server's address is not handled the way the ruling and the documents say. queryDNS (internal/resolver/iterative.go line 141) turns a REFUSED reply marked "recursion available" into a plain refusal before queryServers checks that flag (line 312). 9.9.9.9 answers every query that does not ask for recursion this way. The reply is therefore counted, and the next root server is asked. On a network that sends some root server addresses to such a resolver, dnswatcher passes over each of those replies and runs as normal. Where every address goes there, it asks all 13 and reports "every root server refused", naming no address. Several places say that the first reply marked "recursion available" ends the lookup at once and that no other root server is asked: the README (lines 564-574), the comments on ErrIntercepted (internal/resolver/errors.go lines 56-61) and on queryServers (iterative.go lines 290-294), TODO.md, the commit message and the PR body. The ruling at #301 (comment) says the same, and adds that nothing is counted.
Acceptable: a refusal marked "recursion available" from a root server's address ends the lookup at once with "this network intercepts DNS queries", naming the address, the same as any other reply with that mark. A refusal without the mark is counted as on next, and the case where every root server refuses keeps its message from next. The live test shows such a refusal ending the lookup; 9.9.9.9 gives one. The test's comment (internal/resolver/resolver_test.go line 860), which says a resolver that refuses is passed over, is changed to match.
Judgement call: this needs queryDNS to let queryServers see the mark on a refusal. It adds no retry and no second query. If a refusal should instead stay a refusal whatever its flags, every document listed above must say so. The test must then not count 9.9.9.9 among the resolvers of which one is enough, because it can never give the reply the test waits for.
Model: opus-5-5
Rework, one point.
A refusal from a root server's address is not handled the way the ruling and the documents say. `queryDNS` (`internal/resolver/iterative.go` line 141) turns a REFUSED reply marked "recursion available" into a plain refusal before `queryServers` checks that flag (line 312). 9.9.9.9 answers every query that does not ask for recursion this way. The reply is therefore counted, and the next root server is asked. On a network that sends some root server addresses to such a resolver, dnswatcher passes over each of those replies and runs as normal. Where every address goes there, it asks all 13 and reports "every root server refused", naming no address. Several places say that the first reply marked "recursion available" ends the lookup at once and that no other root server is asked: the README (lines 564-574), the comments on `ErrIntercepted` (`internal/resolver/errors.go` lines 56-61) and on `queryServers` (`iterative.go` lines 290-294), `TODO.md`, the commit message and the PR body. The ruling at https://git.eeqj.de/sneak/dnswatcher/pulls/301#issuecomment-144970 says the same, and adds that nothing is counted.
Acceptable: a refusal marked "recursion available" from a root server's address ends the lookup at once with "this network intercepts DNS queries", naming the address, the same as any other reply with that mark. A refusal without the mark is counted as on `next`, and the case where every root server refuses keeps its message from `next`. The live test shows such a refusal ending the lookup; 9.9.9.9 gives one. The test's comment (`internal/resolver/resolver_test.go` line 860), which says a resolver that refuses is passed over, is changed to match.
Judgement call: this needs `queryDNS` to let `queryServers` see the mark on a refusal. It adds no retry and no second query. If a refusal should instead stay a refusal whatever its flags, every document listed above must say so. The test must then not count 9.9.9.9 among the resolvers of which one is enough, because it can never give the reply the test waits for.
Model: opus-5-5
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 the
existing "this network intercepts DNS queries", naming the address,
and no other root server is asked, whether or not that resolver had
the name cached. queryDNS now returns a refused reply along with
ErrRefused, so the mark on a refusal can be seen. A refusal without
the mark is passed over and counted as before.
On such a network dnswatcher and its tests fail with that error, by
design. A live test passes public resolvers as the root servers and
expects it.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
On a network that sends all DNS to one recursive resolver, dnswatcher took that resolver's cached answers as if each nameserver had given them (#300).
queryDNSnow returns a refused reply withErrRefused, so a refusal's mark can be seen. Its other caller, the per-nameserver record queries, ignores that reply as before.next; when every root server refuses, the error is unchanged.QueryServersas the root servers and expects that error; in the second group each refuses, as 9.9.9.9 does. One answering per group suffices.TODO.mdsay dnswatcher and its tests fail on such a network, by design.Worth knowing:
Hand check: with UDP, then UDP and TCP, port 53 redirected inside a throwaway container to 1.1.1.1, then 9.9.9.9, all four names failed with that error; with no redirect they resolved.
Model: opus-5-5
no. on a network that is filtering/altering DNS packets, the tests should fail!
d1024b2b20toca26499c88resolver: ask again over TCP when a reply offers recursion (closes #300)to resolver: a root server's reply marked recursion available means interception (closes #300)Review: pass.
Model: opus-5-5
Rework, one point. The ruling (#300 (comment)) is that dnswatcher stops with the interception error and fails on such a network every time. As it stands, a root server's reply marked "recursion available" is passed over and the next root server is asked. On a network that intercepts only some root server addresses, the lookup therefore still succeeds. The new test also needs all four public resolvers to answer within one attempt.
Acceptable: the first reply marked "recursion available" from a root server's address ends the lookup at once with "this network intercepts DNS queries". The other root servers are not asked, and nothing is counted. The case where every root server refuses stays as on
next. The README,TODO.md, the code comments, the commit message and the PR body are updated to match.Model: opus-5-5
ca26499c88toc41349fd94resolver: a root server's reply marked recursion available means interception (closes #300)to resolver: a reply marked recursion available from a root server's address stops the lookup (closes #300)Rework, one point.
A refusal from a root server's address is not handled the way the ruling and the documents say.
queryDNS(internal/resolver/iterative.goline 141) turns a REFUSED reply marked "recursion available" into a plain refusal beforequeryServerschecks that flag (line 312). 9.9.9.9 answers every query that does not ask for recursion this way. The reply is therefore counted, and the next root server is asked. On a network that sends some root server addresses to such a resolver, dnswatcher passes over each of those replies and runs as normal. Where every address goes there, it asks all 13 and reports "every root server refused", naming no address. Several places say that the first reply marked "recursion available" ends the lookup at once and that no other root server is asked: the README (lines 564-574), the comments onErrIntercepted(internal/resolver/errors.golines 56-61) and onqueryServers(iterative.golines 290-294),TODO.md, the commit message and the PR body. The ruling at #301 (comment) says the same, and adds that nothing is counted.Acceptable: a refusal marked "recursion available" from a root server's address ends the lookup at once with "this network intercepts DNS queries", naming the address, the same as any other reply with that mark. A refusal without the mark is counted as on
next, and the case where every root server refuses keeps its message fromnext. The live test shows such a refusal ending the lookup; 9.9.9.9 gives one. The test's comment (internal/resolver/resolver_test.goline 860), which says a resolver that refuses is passed over, is changed to match.Judgement call: this needs
queryDNSto letqueryServerssee the mark on a refusal. It adds no retry and no second query. If a refusal should instead stay a refusal whatever its flags, every document listed above must say so. The test must then not count 9.9.9.9 among the resolvers of which one is enough, because it can never give the reply the test waits for.Model: opus-5-5
c41349fd94to4a4a966eb5Review: pass.
Model: opus-5-5