A nameserver with several addresses is asked at only the first one, so one unreachable address reports it as failed #285

Closed
opened 2026-10-13 22:55:39 +02:00 by clawbot · 2 comments
Collaborator

What is wrong. To query a nameserver, the resolver looks up the A records of the nameserver's name and sends every query to the first address in the answer. The other addresses are never tried. A nameserver whose name has several addresses, one of them unreachable from the host, is then recorded as failed whenever that address comes first in the answer and as recovered when another does, although it answers at its other addresses throughout. Names with several addresses are common: each *.ns.cloudflare.com name has three IPv4 addresses, answered in rotating order.

Where (at v1.0.0-rc1): internal/resolver/iterative.go, queryNameserver lines 701-719: resolveARecord (lines 565-621) returns the addresses in answer order and line 718 passes nsIPs[0] to queryTypes; the retry in tryExchange (lines 78-101) resends to the same address; queryEachNS lines 1022-1054 saves the result as the nameserver's status.

Scenario. www.example.net is served by ns1.example.net, whose name has the addresses 203.0.113.1 and 203.0.113.2, and the host cannot reach 203.0.113.2 (a filtered prefix, a missing route, one anycast node down). Check 1: the answer lists .1 first, every query is answered, status ok. Check 2: the answer lists .2 first, all eight queries time out after two tries each (about 32 seconds), status error, NS Failure: www.example.net at error level. Check 3: .1 first again, NS Recovery. This goes on for as long as the address is unreachable, for every watched name in the zone, and on the failed checks the records the nameserver answers at .1 are not compared. Confirmed by reading; not reproduced, since it needs a nameserver with one unreachable address.

Correct. README, DNS Resolution Strategy: "A server that does not reply, refuses the query, or gives an error reply such as SERVFAIL or a referral that leads no closer to the name is passed over for the next one"; DNS Hostname Monitoring: an NS query failure is "A nameserver that previously responded becomes unreachable". A nameserver that answers at one of its addresses has not become unreachable. My reading: a nameserver's queries go to its addresses in turn until one gives a usable reply, as the walk already does for the servers of a zone, and the nameserver has failed only when none of its addresses answers; the README says so.

Done when: a nameserver's queries are tried at each of its addresses before it is recorded as failed, and a nameserver that answers at any of them is ok; a resolver test covers a nameserver list with an address that never answers (192.0.2.1, as the existing timeout tests use) before a live one, against live DNS and with no stand-in resolver; the README's DNS Resolution Strategy says which addresses are asked.

Severity: major

Model: fable-5-1

**What is wrong.** To query a nameserver, the resolver looks up the A records of the nameserver's name and sends every query to the first address in the answer. The other addresses are never tried. A nameserver whose name has several addresses, one of them unreachable from the host, is then recorded as failed whenever that address comes first in the answer and as recovered when another does, although it answers at its other addresses throughout. Names with several addresses are common: each `*.ns.cloudflare.com` name has three IPv4 addresses, answered in rotating order. **Where** (at `v1.0.0-rc1`): `internal/resolver/iterative.go`, `queryNameserver` lines 701-719: `resolveARecord` (lines 565-621) returns the addresses in answer order and line 718 passes `nsIPs[0]` to `queryTypes`; the retry in `tryExchange` (lines 78-101) resends to the same address; `queryEachNS` lines 1022-1054 saves the result as the nameserver's status. **Scenario.** `www.example.net` is served by `ns1.example.net`, whose name has the addresses `203.0.113.1` and `203.0.113.2`, and the host cannot reach `203.0.113.2` (a filtered prefix, a missing route, one anycast node down). Check 1: the answer lists `.1` first, every query is answered, status `ok`. Check 2: the answer lists `.2` first, all eight queries time out after two tries each (about 32 seconds), status `error`, `NS Failure: www.example.net` at error level. Check 3: `.1` first again, `NS Recovery`. This goes on for as long as the address is unreachable, for every watched name in the zone, and on the failed checks the records the nameserver answers at `.1` are not compared. Confirmed by reading; not reproduced, since it needs a nameserver with one unreachable address. **Correct.** README, DNS Resolution Strategy: "A server that does not reply, refuses the query, or gives an error reply such as SERVFAIL or a referral that leads no closer to the name is passed over for the next one"; DNS Hostname Monitoring: an NS query failure is "A nameserver that previously responded becomes unreachable". A nameserver that answers at one of its addresses has not become unreachable. My reading: a nameserver's queries go to its addresses in turn until one gives a usable reply, as the walk already does for the servers of a zone, and the nameserver has failed only when none of its addresses answers; the README says so. **Done when:** a nameserver's queries are tried at each of its addresses before it is recorded as failed, and a nameserver that answers at any of them is `ok`; a resolver test covers a nameserver list with an address that never answers (`192.0.2.1`, as the existing timeout tests use) before a live one, against live DNS and with no stand-in resolver; the README's DNS Resolution Strategy says which addresses are asked. Severity: major Model: fable-5-1
clawbot self-assigned this 2026-10-13 22:55:39 +02:00
Author
Collaborator

Plan. Package internal/resolver, whose doc covers iterative resolution; no new package. queryNameserver keeps every address resolveARecord returns (IPv4 only, as today) instead of the first. Each record-type query goes to those addresses in turn, starting with the one that replied to the previous query, until one replies; a type fails only when no address replies, and the nameserver is error or timeout only when it answered no type at any of them. A nameserver none of whose addresses replies costs one round of tries per address per type: accepted, recorded in the PR body. Test in internal/resolver: a nameserver address list with 192.0.2.1 before a live server's address, through internal/livednstest, against live DNS, no stand-in. README, DNS Resolution Strategy: a nameserver is asked at its IPv4 addresses in turn until one replies. TODO.md Completed Steps entry in the same commit.

Model: opus-5-5

Plan. Package `internal/resolver`, whose doc covers iterative resolution; no new package. `queryNameserver` keeps every address `resolveARecord` returns (IPv4 only, as today) instead of the first. Each record-type query goes to those addresses in turn, starting with the one that replied to the previous query, until one replies; a type fails only when no address replies, and the nameserver is `error` or `timeout` only when it answered no type at any of them. A nameserver none of whose addresses replies costs one round of tries per address per type: accepted, recorded in the PR body. Test in `internal/resolver`: a nameserver address list with `192.0.2.1` before a live server's address, through `internal/livednstest`, against live DNS, no stand-in. README, DNS Resolution Strategy: a nameserver is asked at its IPv4 addresses in turn until one replies. `TODO.md` Completed Steps entry in the same commit. Model: opus-5-5
Author
Collaborator

Built as the plan above says, in #291; its body lists the judgement calls.

Model: opus-5-5

Built as the plan above says, in https://git.eeqj.de/sneak/dnswatcher/pulls/291; its body lists the judgement calls. Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/dnswatcher#285