From b4102039739ffbf9654b25ac974edca24e98c27c Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Thu, 7 Oct 2021 23:12:59 +0100 Subject: [PATCH] Add --filter and --filter-AAAA options. (cherry picked from commit 37a70d39e0cd49f086b757937fa8735e6263cd7a) --- man/dnsmasq.8 | 6 ++++++ src/dnsmasq.h | 10 ++++++++-- src/edns0.c | 2 +- src/forward.c | 16 +++++++++++++--- src/option.c | 8 +++++++- src/rrfilter.c | 37 +++++++++++++++++++++++++++---------- 6 files changed, 62 insertions(+), 17 deletions(-) diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index 8bb81b6..e47ee5f 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -348,6 +348,12 @@ the public DNS and can cause problems by triggering dial-on-demand links. This f to filter such requests. The requests blocked are for records of types SOA and SRV, and type ANY where the requested name has underscores, to catch LDAP requests. .TP +.B --filter-A +Remove A records from answers. No IPv4 addresses will be returned. +.TP +.B --filter-AAAA +Remove AAAA records from answers. No IPv6 addresses will be returned. +.TP .B \-r, --resolv-file= Read the IP addresses of the upstream nameservers from , instead of /etc/resolv.conf. For the format of this file see diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 6fa2593..5f6d63b 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -270,7 +270,9 @@ struct event_desc { #define OPT_SINGLE_PORT 60 #define OPT_LEASE_RENEW 61 #define OPT_LOG_DEBUG 62 -#define OPT_LAST 63 +#define OPT_FILTER_A 67 +#define OPT_FILTER_AAAA 68 +#define OPT_LAST 69 #define OPTION_BITS (sizeof(unsigned int)*8) #define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) ) @@ -1706,7 +1708,11 @@ int do_poll(int timeout); size_t rrfilter(struct dns_header *header, size_t plen, int mode); short *rrfilter_desc(int type); int expand_workspace(unsigned char ***wkspc, int *szp, int new); - +/* modes. */ +#define RRFILTER_EDNS0 0 +#define RRFILTER_DNSSEC 1 +#define RRFILTER_A 2 +#define RRFILTER_AAAA 3 /* edns0.c */ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign, int *is_last); diff --git a/src/edns0.c b/src/edns0.c index 33b5283..b1615ae 100644 --- a/src/edns0.c +++ b/src/edns0.c @@ -178,7 +178,7 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l memcpy(buff, datap, rdlen); /* now, delete OPT RR */ - plen = rrfilter(header, plen, 0); + plen = rrfilter(header, plen, RRFILTER_EDNS0); /* Now, force addition of a new one */ p = NULL; diff --git a/src/forward.c b/src/forward.c index 388b0c3..36c769a 100644 --- a/src/forward.c +++ b/src/forward.c @@ -710,7 +710,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server if (added_pheader) { /* client didn't send EDNS0, we added one, strip it off before returning answer. */ - n = rrfilter(header, n, 0); + n = rrfilter(header, n, RRFILTER_EDNS0); pheader = NULL; } else @@ -793,7 +793,17 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server SET_RCODE(header, NOERROR); cache_secure = 0; } - + + /* Before extract_addresses() */ + if (rcode == NOERROR) + { + if (option_bool(OPT_FILTER_A)) + n = rrfilter(header, n, RRFILTER_A); + + if (option_bool(OPT_FILTER_AAAA)) + n = rrfilter(header, n, RRFILTER_AAAA); + } + if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, no_cache, cache_secure, &doctored)) { my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff); @@ -822,7 +832,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server /* If the requestor didn't set the DO bit, don't return DNSSEC info. */ if (!do_bit) - n = rrfilter(header, n, 1); + n = rrfilter(header, n, RRFILTER_DNSSEC); } #endif diff --git a/src/option.c b/src/option.c index 8b40332..44bc46f 100644 --- a/src/option.c +++ b/src/option.c @@ -171,7 +171,9 @@ struct myoption { #define LOPT_DYNHOST 362 #define LOPT_LOG_DEBUG 363 #define LOPT_DNSSEC_LIMITS 364 - +#define LOPT_FILTER_A 369 +#define LOPT_FILTER_AAAA 370 + #ifdef HAVE_GETOPT_LONG static const struct option opts[] = #else @@ -208,6 +210,8 @@ static const struct myoption opts[] = { "ignore-address", 1, 0, LOPT_IGNORE_ADDR }, { "selfmx", 0, 0, 'e' }, { "filterwin2k", 0, 0, 'f' }, + { "filter-A", 0, 0, LOPT_FILTER_A }, + { "filter-AAAA", 0, 0, LOPT_FILTER_AAAA }, { "pid-file", 2, 0, 'x' }, { "strict-order", 0, 0, 'o' }, { "server", 1, 0, 'S' }, @@ -374,6 +378,8 @@ static struct { { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL }, { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL }, { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL }, + { LOPT_FILTER_A, OPT_FILTER_A, NULL, gettext_noop("Don't include IPv4 addresses in DNS answers."), NULL }, + { LOPT_FILTER_AAAA, OPT_FILTER_AAAA, NULL, gettext_noop("Don't include IPv6 addresses in DNS answers."), NULL }, { 'F', ARG_DUP, ",...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL }, { 'g', ARG_ONE, "", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP }, { 'G', ARG_DUP, "", gettext_noop("Set address or hostname for a specified machine."), NULL }, diff --git a/src/rrfilter.c b/src/rrfilter.c index 32ef34a..262c114 100644 --- a/src/rrfilter.c +++ b/src/rrfilter.c @@ -156,7 +156,7 @@ static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, i } -/* mode is 0 to remove EDNS0, 1 to filter DNSSEC RRs */ +/* mode may be remove EDNS0 or DNSSEC RRs or remove A or AAAA from answer section. */ size_t rrfilter(struct dns_header *header, size_t plen, int mode) { static unsigned char **rrs; @@ -192,20 +192,37 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode) if (!ADD_RDLEN(header, p, plen, rdlen)) return plen; - /* Don't remove the answer. */ - if (i < ntohs(header->ancount) && type == qtype && class == qclass) - continue; - - if (mode == 0) /* EDNS */ + if (mode == RRFILTER_EDNS0) /* EDNS */ { /* EDNS mode, remove T_OPT from additional section only */ if (i < (ntohs(header->nscount) + ntohs(header->ancount)) || type != T_OPT) continue; } - else if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG) - /* DNSSEC mode, remove SIGs and NSECs from all three sections. */ - continue; - + else if (mode == RRFILTER_DNSSEC) + { + if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG) + /* DNSSEC mode, remove SIGs and NSECs from all three sections. */ + continue; + + /* Don't remove the answer. */ + if (i < ntohs(header->ancount) && type == qtype && class == qclass) + continue; + } + else + { + /* Only looking at answer section now. */ + if (i >= ntohs(header->ancount)) + break; + + if (class != C_IN) + continue; + + if (mode == RRFILTER_A && type != T_A) + continue; + + if (mode == RRFILTER_AAAA && type != T_AAAA) + continue; + } if (!expand_workspace(&rrs, &rr_sz, rr_found + 1)) return plen; -- 2.50.1