[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