Make --filter-rr=ANY filter the answer to ANY queries.

Thanks to Dominik Derigs for an earlier patch which inspired this.
This commit is contained in:
Simon Kelley
2024-02-12 16:14:06 +00:00
parent febeea9d01
commit 3de7289bd6
4 changed files with 33 additions and 12 deletions

View File

@@ -25,6 +25,12 @@ version 2.90
end up in the query also. This bug only seems to cause problems 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 when the usptream server is a DOH/DOT proxy. Thanks to Justin He
for the bug report. 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 version 2.89

View File

@@ -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. Remove AAAA records from answers. No IPv6 addresses will be returned.
.TP .TP
.B --filter-rr=<rrtype>[,<rrtype>...] .B --filter-rr=<rrtype>[,<rrtype>...]
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 .TP
.B --cache-rr=<rrtype>[,<rrtype>...] .B --cache-rr=<rrtype>[,<rrtype>...]
By default, dnsmasq caches A, AAAA, CNAME and SRV DNS record types. By default, dnsmasq caches A, AAAA, CNAME and SRV DNS record types.

View File

@@ -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 returned packet in process_reply() but gets cached here anyway
and will be filtered again on the way out of the cache. Here, and will be filtered again on the way out of the cache. Here,
we just need to alter the logging. */ 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; secflag = F_NEG | F_CONFIG;
if (aqtype == T_TXT) 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))) if (!(crecp->flags & (F_HOSTS | F_DHCP)))
auth = 0; 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))) !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG | F_NEG)))
{ {
/* We have a cached answer but we're filtering it. */ /* 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) else if (crecp->flags & F_NEG)
{ {
ans = 1; if (qtype != T_ANY)
auth = 0; {
soa_lookup = crecp; ans = 1;
if (crecp->flags & F_NXDOMAIN) auth = 0;
nxdomain = 1; soa_lookup = crecp;
if (crecp->flags & F_NXDOMAIN)
log_query(stale_flag | crecp->flags, name, NULL, NULL, 0); nxdomain = 1;
log_query(stale_flag | crecp->flags, name, NULL, NULL, 0);
}
} }
else else
{ {
@@ -2180,7 +2183,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (flags & F_NXDOMAIN) if (flags & F_NXDOMAIN)
nxdomain = 1; 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; flags |= F_NEG | F_CONFIG;
auth = 0; 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 /* 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 filter it anyway. If we have a cached answer for the domain for another RRtype then

View File

@@ -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) if (i < ntohs(header->ancount) && type == qtype && class == qclass)
continue; 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 else
{ {
/* Only looking at answer section now. */ /* Only looking at answer section now. */