[Dnsmasq-discuss] [PATCH v2] Fix local host records being overridden by upstream NXDOMAIN
Simon Kelley
simon at thekelleys.org.uk
Sun Aug 9 17:12:45 UTC 2026
Dominik,
Apologies for ghosting you: My hours are limited and backlog enormous!
I'm confused about the problem here.
The first part of the solution I'm wary of. The intention was always
that dnsmasq local names _overlay_ the public DNS, So it would be
possible to, for instance, provide a local override for the A record of
linux.org, but still see the public DNS for other RR-types. Hence if we
have no information for a particular RRtype, but we do for the domain at
another RRtype, we still send the query upstream.
I'm loath to change this: it's potentially useful for dnsmasq's niche,
and if it's not required, then local=/linux.org/ suppresses it. It's
certainly a big non-backwards-compatible change.
That brings us to the second part, which is the bogusanswer gate. "For a
local name under a signed public parent the upstream denial is exactly
the answer that fails validation" is just wrong: If the domain falls
within a signed zone, then the answer will be a signed NXDOMAIN.
Maybe the problem is that the NODATA answer sent back downstream is not
DNSSEC valid? There's nothing that can be done about that: You can't use
dnsmasq to mess with signed zones of the DNS _and_ have it give answers
that are DNSSEC valid - that's the whole point of DNSSEC.
I just tested this here. I query A non-existing TLD (uuu), which gets a
signed NXDOMAIN reply, because the root is signed.
So simon.uuu gets NXDOMAIN, with ad bit set.
Now add
host-record=simon.uuu,1,2,3,4
dig A simon.uuu gives 1.2.3.4
dig AAAA simon.uuu gives NODATA.
both without ad bit.
The bug report you quote has something about different answers to
different hosts, so maybe there's something else going on? It would be
useful to see dnsmasq logs.
The crucial thing I don't understand, is how bogusanswer is being set in
process_reply()
Cheers,
Simon.
On 09.08.2026 08:57, Dominik Derigs wrote:
> Hey Simon,
>
> this is iteration #2 of a patch I sent on 2026-05-16, archived as
>
> https://lists.thekelleys.org.uk/pipermail/dnsmasq-
> discuss/2026q2/018476.html
>
> It has had no reply, so here it is again, rebased onto current master
> and with
> one small change over v1 (see below).
>
> A name that only exists locally - /etc/hosts, a DHCP lease, or --host-
> record -
> loses to upstream when it is queried for a type it does not have.
> answer_request()
> finds no matching record, gives up, and the query is forwarded. The
> upstream is
> authoritative for the public parent zone, knows nothing about the name and
> answers NXDOMAIN, which is factually wrong: the name does exist, it just
> has no
> record of that type. This bites hard in practice because Linux resolvers
> send
> AAAA and HTTPS/SVCB alongside every A query, so a plain A-only host
> record is
> enough to trigger it several times per lookup.
>
> There is already a safety net for this in process_reply(), converting an
> upstream NXDOMAIN for a locally-known name into NODATA, but it sits
> behind the
> !bogusanswer gate. For a local name under a signed public parent the
> upstream
> denial is exactly the answer that fails validation, so the one code path
> meant
> to catch this case is skipped whenever DNSSEC is on.
>
> The patch closes both:
>
> 1. answer_request() answers NODATA itself when the name has local
> F_HOSTS/F_DHCP/F_CONFIG records but nothing for the queried type, so
> the
> query is never forwarded in the first place.
>
> 2. The NXDOMAIN-to-NODATA conversion in process_reply() runs before the
> bogus-answer gate. Local records are authoritative for a name's
> existence
> no matter what an upstream denial validates to.
>
> Changed in v2: the log_query() call in answer_request() now only sets
> F_IPV4/F_IPV6 for A/AAAA queries, so an MX or HTTPS query is logged as
> "NODATA" instead of "NODATA-IPv6".
>
> Reproducer, with --host-record=node-1.example.org,10.0.20.20 and any
> upstream
> that is authoritative for example.org:
>
> dig @localhost node-1.example.org A -> NOERROR, 10.0.20.20
> dig @localhost node-1.example.org AAAA -> forwarded, upstream
> NXDOMAIN
> dig @localhost node-1.example.org HTTPS -> forwarded, upstream
> NXDOMAIN
>
> With the patch the last two are answered locally as NODATA and never
> leave the
> machine. Downstream report with a user hitting this in the wild:
> https://github.com/pi-hole/FTL/issues/2841
>
> Best regards,
> Dominik
More information about the Dnsmasq-discuss
mailing list