惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
B
Blog
Stack Overflow Blog
Stack Overflow Blog
量子位
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Tailwind CSS Blog
MongoDB | Blog
MongoDB | Blog
小众软件
小众软件
博客园 - 三生石上(FineUI控件)
Recent Announcements
Recent Announcements
U
Unit 42
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
腾讯CDC
D
DataBreaches.Net
Microsoft Azure Blog
Microsoft Azure Blog
G
Google Developers Blog
M
MIT News - Artificial intelligence
P
Proofpoint News Feed
罗磊的独立博客
L
LangChain Blog
V
Visual Studio Blog
雷峰网
雷峰网
aimingoo的专栏
aimingoo的专栏
宝玉的分享
宝玉的分享

dnsmasq-discuss

[Dnsmasq-discuss] Announce: dnsmasq-2.92rc2 Re: [Dnsmasq-discuss] [PATCH] Fix arguments order for chaos subdomain check Re: [Dnsmasq-discuss] patch: block-file/allow-file - for review/feedback Re: [Dnsmasq-discuss] patch: block-file/allow-file - for review/feedback Re: [Dnsmasq-discuss] patch: block-file/allow-file - for review/feedback Re: [Dnsmasq-discuss] patch: block-file/allow-file - for review/feedback [Dnsmasq-discuss] patch: block-file/allow-file - for review/feedback Re: [Dnsmasq-discuss] server= with interface parameter changes behavior over time [Dnsmasq-discuss] NFTsets and hosts-files [Dnsmasq-discuss] [PATCH] Allow expired RRSIGs when stale caching is enabled [Dnsmasq-discuss] [PATCH] Fix arguments order for chaos subdomain check Re: [Dnsmasq-discuss] Malformed RRSIG Can Crash dnsmasq [Dnsmasq-discuss] Malformed NSEC/NSEC3 Can Hang dnsmasq [Dnsmasq-discuss] Malformed RRSIG Can Crash dnsmasq [Dnsmasq-discuss] Security - IMPORTANT Re: [Dnsmasq-discuss] Issue with circuit-id matching on dhcp requests Re: [Dnsmasq-discuss] Issue with circuit-id matching on dhcp requests Re: [Dnsmasq-discuss] Issue with circuit-id matching on dhcp requests [Dnsmasq-discuss] Issue with circuit-id matching on dhcp requests Re: [Dnsmasq-discuss] [PATCH] bpf.c: fix memory leak in arp_enumerate() on BSD Re: [Dnsmasq-discuss] [PATCH] bpf.c: fix memory leak in arp_enumerate() on BSD Re: [Dnsmasq-discuss] dnssec problem here and now Re: [Dnsmasq-discuss] dnssec problem here and now [Dnsmasq-discuss] dnssec problem here and now Re: [Dnsmasq-discuss] server= with interface parameter changes behavior over time Re: [Dnsmasq-discuss] [PATCH] bpf.c: fix memory leak in arp_enumerate() on BSD Re: [Dnsmasq-discuss] [PATCH] bpf.c: fix memory leak in arp_enumerate() on BSD Re: [Dnsmasq-discuss] [PATCH] Preserve existing log file permissions when adding group-write bit. [Dnsmasq-discuss] server= with interface parameter changes behavior over time [Dnsmasq-discuss] [PATCH] bpf.c: fix memory leak in arp_enumerate() on BSD
[Dnsmasq-discuss] [PATCH] Fix local host records being ov...
Dominik Derigs · 2026-05-16 · via dnsmasq-discuss
Hi Simon,

we've found a bug where locally-configured hostnames (from /etc/hosts, DHCP leases, or host-record) can return NXDOMAIN to clients instead of the expected local answer. The attached patch fixes this.

The problem has two parts:

1) When a client queries a local hostname for a record type that has no local answer (e.g. AAAA when only an A record exists in /etc/hosts), answer_request() returns 0 and the query is forwarded upstream. Since the domain only exists locally, upstream returns NXDOMAIN. The correct response would be NODATA -- the domain exists, it just doesn't have a record of the requested type.

This is particularly visible with modern Linux resolvers like systemd-resolved as they routinely send AAAA and A queries independently at the same time. Most often, modern versions also issue HTTPS queries. The NXDOMAIN response to the AAAA/HTTPS query can cause the client to treat the entire domain as non-existent, even though the A answer comes back correctly.

2) There is already a safety net in process_reply() that converts upstream NXDOMAIN to NODATA for locally-known domains. However, this conversion is inside the !bogusanswer gate (line 793 on current master), so it is skipped when DNSSEC validation fails. For local hostnames under publicly-signed domains, the upstream NXDOMAIN proof can fail validation, bypassing the safety net.

The fix:

- In answer_request(): before the final `return 0`, check whether the domain has any local record with F_HOSTS/F_DHCP/F_CONFIG. If so, return NODATA instead of forwarding. This prevents the upstream query entirely.

- In process_reply(): move the NXDOMAIN-to-NODATA conversion for locally-known domains before the bogusanswer check, so it applies regardless of DNSSEC validation status. Local host records are authoritative for domain existence.

Originally reported and investigated by me in https://github.com/pi-hole/FTL/issues/2841


Cheers,
Dominik
From bb1f182beeb5ca5c8ecc4638d302cb6ed183acc8 Mon Sep 17 00:00:00 2001
From: Dominik <[email protected]>
Date: Sun, 12 Apr 2026 13:17:53 +0200
Subject: [PATCH] Fix local host records being overridden by upstream NXDOMAIN

When a locally-configured hostname (from /etc/hosts, DHCP, or
host-record) is queried for a record type with no local answer
(e.g. AAAA when only A exists), the query is forwarded upstream.
If the domain only exists locally, upstream returns NXDOMAIN,
which incorrectly overrides the local host record's existence.

Fix in two places:

1. answer_request(): When no answer is found for the requested
   type but the domain has local host/DHCP/config records, return
   NODATA instead of forwarding. The domain exists locally, just
   not for the requested type.

2. process_reply(): Move the NXDOMAIN-to-NODATA conversion for
   locally-known domains before the DNSSEC bogus-answer gate.
   Local host records are authoritative for domain existence
   regardless of upstream DNSSEC validation status.
---
 src/forward.c | 24 +++++++++++++-----------
 src/rfc1035.c | 21 +++++++++++++++++++++
 2 files changed, 34 insertions(+), 11 deletions(-)

diff --git a/src/forward.c b/src/forward.c
index 1a7c586..501d8e4 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -788,21 +788,23 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
       return resize_packet(header, n, pheader, plen);
     }
   
+  /* Convert NXDOMAIN to NODATA for locally-known domains. This must happen
+     unconditionally, even for DNSSEC BOGUS answers, since local host records
+     are authoritative for domain existence. */
+  if (!(header->hb3 & HB3_TC) && rcode == NXDOMAIN &&
+      extract_name(header, n, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0) &&
+      (check_for_local_domain(daemon->namebuff, now) || lookup_domain(daemon->namebuff, F_CONFIG, NULL, NULL)))
+    {
+      header->hb3 |= HB3_AA;
+      SET_RCODE(header, NOERROR);
+      rcode = NOERROR;
+      cache_secure = 0;
+    }
+
   if (header->hb3 & HB3_TC)
     log_query(F_UPSTREAM, NULL, NULL, "truncated", 0);
   else if (!bogusanswer || (header->hb4 & HB4_CD))
     {
-      if (rcode == NXDOMAIN && extract_name(header, n, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0) &&
-	  (check_for_local_domain(daemon->namebuff, now) || lookup_domain(daemon->namebuff, F_CONFIG, NULL, NULL)))
-	{
-	  /* if we forwarded a query for a locally known name (because it was for 
-	     an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
-	     since we know that the domain exists, even if upstream doesn't */
-	  header->hb3 |= HB3_AA;
-	  SET_RCODE(header, NOERROR);
-	  cache_secure = 0;
-	}
-      
       if (daemon->doctors && do_doctor(header, n, daemon->namebuff))
 	cache_secure = 0;
       
diff --git a/src/rfc1035.c b/src/rfc1035.c
index 0e31b83..4714d3f 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -2302,6 +2302,27 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
 	}
     }
   
+  /* If we couldn't answer (e.g. AAAA query with only A host record, or an
+     unsupported RR type) but the domain has local records from /etc/hosts
+     or DHCP, return NODATA instead of forwarding upstream. This prevents
+     upstream NXDOMAIN responses from overriding local domain existence. */
+  if (!ans)
+    {
+      struct crec *local;
+
+      for (local = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME);
+	   local;
+	   local = cache_find_by_name(local, name, now, F_IPV4 | F_IPV6 | F_CNAME))
+	if (local->flags & (F_HOSTS | F_DHCP | F_CONFIG))
+	  {
+	    ans = 1;
+	    sec_data = 0;
+	    auth = 0;
+	    log_query(F_NEG | F_CONFIG | (qtype == T_A ? F_IPV4 : F_IPV6), name, NULL, NULL, 0);
+	    break;
+	  }
+    }
+
   if (!ans)
     return 0; /* failed to answer a question */
 
-- 
2.43.0

_______________________________________________
Dnsmasq-discuss mailing list
[email protected]
https://lists.thekelleys.org.uk/cgi-bin/mailman/listinfo/dnsmasq-discuss