From 3de7289bd6333f91f045fd44bc5e13b92e3a9ed3 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 12 Feb 2024 16:14:06 +0000 Subject: [PATCH] Make --filter-rr=ANY filter the answer to ANY queries. Thanks to Dominik Derigs for an earlier patch which inspired this. --- CHANGELOG | 6 ++++++ man/dnsmasq.8 | 6 +++++- src/rfc1035.c | 25 ++++++++++++++----------- src/rrfilter.c | 8 ++++++++ 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 2ce53a8..2d46ae4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -25,6 +25,12 @@ version 2.90 end up in the query also. This bug only seems to cause problems when the usptream server is a DOH/DOT proxy. Thanks to Justin He for the bug report. + + Add configurable caching for arbitrary RR-types. + + Add --filter-rr option, to filter arbitrary RR-types. + --filter-rr=ANY has a special meaning: it filters the + answers to queries for the ANY RR-type. version 2.89 diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index bb8da54..80b3a38 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -386,7 +386,11 @@ Remove A records from answers. No IPv4 addresses will be returned. Remove AAAA records from answers. No IPv6 addresses will be returned. .TP .B --filter-rr=[,...] -Remove records of the specified type(s) from answers. +Remove records of the specified type(s) from answers. The otherwise-nonsensical --filter-rr=ANY has +a special meaning: it filters replies to queries for type ANY. Everything other than A, AAAA, MX and CNAME +records are removed. Since ANY queries with forged source addresses can be used in DNS amplification attacks +(replies to ANY queries can be large) this defangs such attacks, whilst still supporting the +one remaining possible use of ANY queries. See RFC 8482 para 4.3 for details. .TP .B --cache-rr=[,...] By default, dnsmasq caches A, AAAA, CNAME and SRV DNS record types. diff --git a/src/rfc1035.c b/src/rfc1035.c index a11c278..9ed7c17 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -1026,7 +1026,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t returned packet in process_reply() but gets cached here anyway and will be filtered again on the way out of the cache. Here, we just need to alter the logging. */ - if (rr_on_list(daemon->filter_rr, qtype)) + if (qtype != T_ANY && rr_on_list(daemon->filter_rr, qtype)) secflag = F_NEG | F_CONFIG; if (aqtype == T_TXT) @@ -1995,7 +1995,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!(crecp->flags & (F_HOSTS | F_DHCP))) auth = 0; - if (rr_on_list(daemon->filter_rr, qtype) && + if (qtype != T_ANY && rr_on_list(daemon->filter_rr, qtype) && !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG | F_NEG))) { /* We have a cached answer but we're filtering it. */ @@ -2009,13 +2009,16 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, } else if (crecp->flags & F_NEG) { - ans = 1; - auth = 0; - soa_lookup = crecp; - if (crecp->flags & F_NXDOMAIN) - nxdomain = 1; - - log_query(stale_flag | crecp->flags, name, NULL, NULL, 0); + if (qtype != T_ANY) + { + ans = 1; + auth = 0; + soa_lookup = crecp; + if (crecp->flags & F_NXDOMAIN) + nxdomain = 1; + + log_query(stale_flag | crecp->flags, name, NULL, NULL, 0); + } } else { @@ -2180,7 +2183,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (flags & F_NXDOMAIN) nxdomain = 1; - else if (rr_on_list(daemon->filter_rr, qtype)) + else if (qtype != T_ANY && rr_on_list(daemon->filter_rr, qtype)) flags |= F_NEG | F_CONFIG; auth = 0; @@ -2225,7 +2228,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, } - if (!ans && rr_on_list(daemon->filter_rr, qtype)) + if (qtype != T_ANY && !ans && rr_on_list(daemon->filter_rr, qtype)) { /* We don't have a cached answer and when we get an answer from upstream we're going to filter it anyway. If we have a cached answer for the domain for another RRtype then diff --git a/src/rrfilter.c b/src/rrfilter.c index 7c277fa..33d385c 100644 --- a/src/rrfilter.c +++ b/src/rrfilter.c @@ -213,6 +213,14 @@ size_t rrfilter(struct dns_header *header, size_t *plen, int mode) if (i < ntohs(header->ancount) && type == qtype && class == qclass) continue; } + else if (qtype == T_ANY && rr_on_list(daemon->filter_rr, T_ANY)) + { + /* Filter replies to ANY queries in the spirit of + RFC RFC 8482 para 4.3 */ + if (class != C_IN || + type == T_A || type == T_AAAA || type == T_MX || type == T_CNAME) + continue; + } else { /* Only looking at answer section now. */