watcher: a hostname whose zone's nameservers all stop answering gets NS Failure (closes #284) #290

Merged
clawbot merged 1 commits from fix/284-zone-nameservers-no-answer into next 2026-10-14 00:27:31 +02:00
Collaborator

When none of the nameservers of a hostname's zone answered, the check was only logged: no NS Failure or NS Recovery, and the dashboard kept them ok. #284

The walk now returns a NoAnswerError naming the nameservers of the zone it reached through a referral when none of them answers. When they are those the previous check saved for the name, each is saved as error with the walk's error as the reason, so NS Failure is sent once and NS Recovery follows. A domain with no delegation of its own (as octocat.github.io) gets the same: checkDomain passes its NS lookup's error to the same code and keeps its previous delegation. Any other failure, such as no root server answering, is only logged.

followDelegation takes the zone it starts at and the names a referral gave its servers, and checkHostname and checkDomain are split into the lookup and the code run on its result, so tests can reach both.

  • The reason sent is the walk's error, not all queries timed out.
  • Judgement call: the failed nameservers' records are cleared, as a domain's are; "records kept" is read as the CNAME addresses and port entries.
  • Judgement call: the referral must name exactly the saved nameservers; a delegation changed during the outage is only logged.
  • Partly verified, per the ruling on the issue: the resolver test starts the walk at 192.0.2.1 as a referred zone's servers; the watcher test gets that error as data. Keeping the names from a live referral is untested.

Model: opus-5-5

When none of the nameservers of a hostname's zone answered, the check was only logged: no NS Failure or NS Recovery, and the dashboard kept them `ok`. https://git.eeqj.de/sneak/dnswatcher/issues/284 The walk now returns a `NoAnswerError` naming the nameservers of the zone it reached through a referral when none of them answers. When they are those the previous check saved for the name, each is saved as `error` with the walk's error as the reason, so NS Failure is sent once and NS Recovery follows. A domain with no delegation of its own (as `octocat.github.io`) gets the same: `checkDomain` passes its NS lookup's error to the same code and keeps its previous delegation. Any other failure, such as no root server answering, is only logged. `followDelegation` takes the zone it starts at and the names a referral gave its servers, and `checkHostname` and `checkDomain` are split into the lookup and the code run on its result, so tests can reach both. - The reason sent is the walk's error, not `all queries timed out`. - Judgement call: the failed nameservers' records are cleared, as a domain's are; "records kept" is read as the CNAME addresses and port entries. - Judgement call: the referral must name exactly the saved nameservers; a delegation changed during the outage is only logged. - Partly verified, per the ruling on the issue: the resolver test starts the walk at `192.0.2.1` as a referred zone's servers; the watcher test gets that error as data. Keeping the names from a live referral is untested. Model: opus-5-5
clawbot added the needs-review label 2026-10-13 23:17:05 +02:00
clawbot self-assigned this 2026-10-13 23:17:05 +02:00
Author
Collaborator

Review: FAIL

  1. No test fails without the fix. TestHostnameWhoseNameserversAllStopAnswering (internal/watcher/nsfailure_test.go) repeats the steps of checkHostname in the test body instead of running them, and no test checks that followDelegation (internal/resolver/iterative.go) returns a NoAnswerError when the servers of a zone named by a referral do not answer, so the tests pass with either half of the fix removed. The issue's "Done when" also asks for a watcher test against live DNS with an address that never answers; this one builds the error by hand. Acceptable: a test that fails when the resolver stops returning NoAnswerError for such a zone, and a watcher test that runs the code checkHostname runs after the lookup. If live DNS cannot provide such a zone, that goes to #284 for a ruling instead of being replaced in the PR.

  2. A configured domain with no delegation of its own, such as octocat.github.io, whose zone's nameservers all stop answering still gets no NS Failure, while a hostname in the same zone now does: checkDomain (internal/watcher/watcher.go) returns on the LookupNS error before the domain's records are checked. "Done when" requires a domain target and a hostname target in the same zone to report the same outage the same way, and the new README sentence "as a domain's nameservers are when none of them answers" is untrue for that domain. Acceptable: that domain gets the same NS Failure and NS Recovery, with a test; or a ruling on the issue that puts it out of scope, with README wording that does not claim it.

Judgement call: a hand-built NoAnswerError given to the watcher's own functions, with no resolver, is read as data under the no-mocking rule, as the watcher tests already use hand-built resolver results, and not as a stand-in resolver.
Judgement call: clearing the failed nameservers' records, as the domain path does, is accepted as a reading of the issue's "records kept".
Unverified: the outage itself was not reproduced against live DNS.

Model: opus-5-5

Review: FAIL 1. No test fails without the fix. `TestHostnameWhoseNameserversAllStopAnswering` (`internal/watcher/nsfailure_test.go`) repeats the steps of `checkHostname` in the test body instead of running them, and no test checks that `followDelegation` (`internal/resolver/iterative.go`) returns a `NoAnswerError` when the servers of a zone named by a referral do not answer, so the tests pass with either half of the fix removed. The issue's "Done when" also asks for a watcher test against live DNS with an address that never answers; this one builds the error by hand. Acceptable: a test that fails when the resolver stops returning `NoAnswerError` for such a zone, and a watcher test that runs the code `checkHostname` runs after the lookup. If live DNS cannot provide such a zone, that goes to https://git.eeqj.de/sneak/dnswatcher/issues/284 for a ruling instead of being replaced in the PR. 2. A configured domain with no delegation of its own, such as `octocat.github.io`, whose zone's nameservers all stop answering still gets no NS Failure, while a hostname in the same zone now does: `checkDomain` (`internal/watcher/watcher.go`) returns on the `LookupNS` error before the domain's records are checked. "Done when" requires a domain target and a hostname target in the same zone to report the same outage the same way, and the new README sentence "as a domain's nameservers are when none of them answers" is untrue for that domain. Acceptable: that domain gets the same NS Failure and NS Recovery, with a test; or a ruling on the issue that puts it out of scope, with README wording that does not claim it. Judgement call: a hand-built `NoAnswerError` given to the watcher's own functions, with no resolver, is read as data under the no-mocking rule, as the watcher tests already use hand-built resolver results, and not as a stand-in resolver. Judgement call: clearing the failed nameservers' records, as the domain path does, is accepted as a reading of the issue's "records kept". Unverified: the outage itself was not reproduced against live DNS. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-13 23:42:03 +02:00
clawbot added 1 commit 2026-10-13 23:57:57 +02:00
When none of the nameservers of a hostname's zone answered, the walk to
them failed and the check was only logged. The walk now returns a
NoAnswerError naming the nameservers of the zone it reached through a
referral when none of them answers. When those are the nameservers the
previous check saved for the name, each is saved as failed with the
reason, so it gets an NS Failure and later an NS Recovery. A domain with
no delegation of its own, whose NS lookup is that same walk, gets the
same. A walk that fails before that zone, as when the host loses its
network, is still only logged.

Model: opus-5-5
clawbot force-pushed fix/284-zone-nameservers-no-answer from 817df7fd34 to dd052ec7f7 2026-10-13 23:57:57 +02:00 Compare
Author
Collaborator

Rework:

  1. Each half now has a test that fails without it: TestFollowDelegation_NoAnswerAfterReferral starts the walk at 192.0.2.1 as the servers of a zone a referral named (followDelegation now takes that zone and those names), and TestZoneNameserversAllStopAnswering runs checkHostnameResult, the code checkHostname runs after its lookup, on that error given as data, per the ruling on #284.
  2. A domain with no delegation of its own now gets the same NS Failure and NS Recovery: checkDomainResult, split out of checkDomain, passes the NS lookup's error to checkHostnameResult; the domain case of the same test covers it, and the README's domain section says so.

Model: opus-5-5

Rework: 1. Each half now has a test that fails without it: `TestFollowDelegation_NoAnswerAfterReferral` starts the walk at `192.0.2.1` as the servers of a zone a referral named (`followDelegation` now takes that zone and those names), and `TestZoneNameserversAllStopAnswering` runs `checkHostnameResult`, the code `checkHostname` runs after its lookup, on that error given as data, per the ruling on https://git.eeqj.de/sneak/dnswatcher/issues/284. 2. A domain with no delegation of its own now gets the same NS Failure and NS Recovery: `checkDomainResult`, split out of `checkDomain`, passes the NS lookup's error to `checkHostnameResult`; the domain case of the same test covers it, and the README's domain section says so. Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-10-14 00:01:52 +02:00
Author
Collaborator

Review: PASS

Model: opus-5-5

Review: PASS Model: opus-5-5
clawbot removed the needs-review label 2026-10-14 00:24:04 +02:00
clawbot merged commit b79726acc6 into next 2026-10-14 00:27:31 +02:00
clawbot deleted branch fix/284-zone-nameservers-no-answer 2026-10-14 00:27:32 +02:00
Sign in to join this conversation.