From 38022819db1f89ce9bd65d0c416cfa6a39d0f502 Mon Sep 17 00:00:00 2001 From: Dominik Derigs Date: Tue, 25 Aug 2026 21:15:29 +0200 Subject: [PATCH] Answer NODATA for --address= names under a --local= domain A name that gets its address from --address= answers NXDOMAIN for the other address family whenever the --local= that makes dnsmasq authoritative for the zone sits in a shorter domain group, e.g. local=/lan/ address=/direct.lan/10.0.0.5 A returns 10.0.0.5 while AAAA returns NXDOMAIN, for a name dnsmasq itself resolves one query earlier. is_local_answer() decides this from the rollback within the matched domain group plus check_for_local_domain(), and neither of the two can see a literal address that lives in a longer group. process_reply() answers the same question for forwarded queries using check_for_local_domain() || lookup_domain(name, F_CONFIG, NULL, NULL), so use that same test here, restricted to literal addresses in a domain at least as specific as the one that matched. A less specific one is shadowed by the match, and is already shadowed when answering the type the name does have, so it must not make the name exist either: address=/example.com/10.0.0.8 local=/sub.example.com/ keeps returning NXDOMAIN for sub.example.com for every type. --host-record and /etc/hosts names are unaffected, they are cache entries that check_for_local_domain() already finds, and a name with no local record of any type keeps returning NXDOMAIN. --- src/domain-match.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/domain-match.c b/src/domain-match.c index d3ab6b3..97fe7ce 100644 --- a/src/domain-match.c +++ b/src/domain-match.c @@ -391,14 +391,22 @@ int is_local_answer(time_t now, int first, char *name) rc = F_IPV4 | F_IPV6; else { + int clow, chigh; + /* argument first is the first struct server which matches the query type; now roll back to the server which is just the same domain, to check if that provides an answer of a different type. */ for (;first > 0 && order_servers(daemon->serverarray[first-1], daemon->serverarray[first]) == 0; first--); + /* A literal address for the name in a domain at least as specific as the one + which matched here means the name exists, even though it has no answer of + this type. One in a less specific domain is shadowed by the match and does + not, exactly as it is shadowed when answering the type it does have. */ if ((daemon->serverarray[first]->flags & SERV_LOCAL_ADDRESS) || - check_for_local_domain(name, now)) + check_for_local_domain(name, now) || + (lookup_domain(name, F_CONFIG, &clow, &chigh) && + daemon->serverarray[clow]->domain_len >= daemon->serverarray[first]->domain_len)) rc = F_NOERR; else rc = F_NXDOMAIN; -- 2.43.0