From 9db0fa74594c163571f1b14619ee45c4f8a8ac20 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 5 Aug 2026 19:18:07 +0200 Subject: [PATCH] Don't loop when a DS reply carries no proof of non-existence To: Simon Kelley Cc: dnsmasq-discuss@lists.thekelleys.org.uk A DS query answered with an unsigned NXDOMAIN which contains neither NSEC nor NSEC3 records leaves `dnssec_validate_reply()` without a proof of non-existence, so it falls back to `zone_status()` to find out whether the zone is unsigned. For a DS query, that returns `STAT_NEED_DS` for the very name whose DS we are resolving. The frec dependency graph then contains a cycle, the loop detection in `dnssec_validate()` catches it, and the query ends up `ABANDONED` without any log message explaining why. Treat this self-referential answer as insecure instead. It carries no information either way, and the existing handling of insecure DS replies already makes the right decision about it: unsigned is assumed for RFC-1918 reverse names when `--bogus-priv` is set, and for domains served by a `--server=/domain/...` directive, everything else stays BOGUS. This is what breaks reverse lookups when a private range is delegated with `--rev-server` and DNSSEC is enabled. Validating the answer walks the chain of trust down to `10.in-addr.arpa`, which is above the delegated zone and therefore goes to the public upstream, and the public resolvers serve the RFC 6303 empty reverse zones locally, without any DNSSEC records to prove that they are unsigned. The existing `--bogus-priv` workaround for exactly this behavior was never reached because validation was abandoned first. Signed-off-by: DL6ER --- src/dnssec.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/dnssec.c b/src/dnssec.c index 71c9bac..54de836 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -1022,6 +1022,13 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char if (!servfail) { + /* If the answer carries no proof of non-existence, we fall back to asking if the + zone is unsigned, but that question is answered by the very DS record we're + chasing here, so we'd loop getting nowhere. Treat the zone as unsigned and let + the checks below decide if that's acceptable. */ + if (STAT_ISEQUAL(rc, STAT_NEED_DS) && hostname_isequal(name, keyname)) + rc = STAT_INSECURE; + if (STAT_ISEQUAL(rc, STAT_INSECURE)) { /* A INSECURE DS answer is OK if it's negative and there's a CNAME answer to the DS answer which is -- 2.43.0