diff options
author | Simon Kelley <simon@thekelleys.org.uk> | 2023-03-29 22:43:21 +0100 |
---|---|---|
committer | Simon Kelley <simon@thekelleys.org.uk> | 2023-03-29 22:43:21 +0100 |
commit | a3c8b75972604443cf763fb06e259b38ceec3351 (patch) | |
tree | cc9fb57b8fb2b30266dc6ba8beb1a124a20d25ec | |
parent | 042c64273d553c8305d3747849b44a0b257b0622 (diff) | |
download | dnsmasq-a3c8b75972604443cf763fb06e259b38ceec3351.tar.gz |
Add filtering of arbitrary RR-types.
-rw-r--r-- | man/dnsmasq.8 | 3 | ||||
-rw-r--r-- | src/dbus.c | 18 | ||||
-rw-r--r-- | src/dnsmasq.c | 2 | ||||
-rw-r--r-- | src/dnsmasq.h | 19 | ||||
-rw-r--r-- | src/forward.c | 15 | ||||
-rw-r--r-- | src/option.c | 41 | ||||
-rw-r--r-- | src/rfc1035.c | 89 | ||||
-rw-r--r-- | src/rrfilter.c | 5 |
8 files changed, 102 insertions, 90 deletions
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index 6d844bf..acb78df 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -376,6 +376,9 @@ Remove A records from answers. No IPv4 addresses will be returned. .B --filter-AAAA Remove AAAA records from answers. No IPv6 addresses will be returned. .TP +.B --filter-rr=<rrtype>[,<rrtype>...] +Remove records of the specified type(s) from answers. +.TP .B --cache-rr=<rrtype>[,<rrtype>...] By default, dnsmasq caches A, AAAA, CNAME and SRV DNS record types. This option adds other record types to the cache. The RR-type can be given @@ -825,11 +825,25 @@ DBusHandlerResult message_handler(DBusConnection *connection, } else if (strcmp(method, "SetFilterA") == 0) { - reply = dbus_set_bool(message, OPT_FILTER_A, "filter-A"); + static int done = 0; + static struct rrlist list = { T_A, NULL }; + + if (!done) + { + list.next = daemon->filter_rr; + daemon->filter_rr = &list; + } } else if (strcmp(method, "SetFilterAAAA") == 0) { - reply = dbus_set_bool(message, OPT_FILTER_AAAA, "filter-AAAA"); + static int done = 0; + static struct rrlist list = { T_AAAA, NULL }; + + if (!done) + { + list.next = daemon->filter_rr; + daemon->filter_rr = &list; + } } else if (strcmp(method, "SetLocaliseQueriesOption") == 0) { diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 6fb9571..c77d9c3 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -140,7 +140,7 @@ int main (int argc, char **argv) /* CONNTRACK UBUS code uses this buffer, so if not allocated above, we need to allocate it here. */ if (option_bool(OPT_CMARK_ALST_EN) && !daemon->workspacename) - daemon->workspacename = safe_malloc(MAXDNAME); + daemon->workspacename = safe_malloc((MAXDNAME * 2) + 1); #endif #ifdef HAVE_DHCP diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 7a00ece..9ee5e39 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -276,14 +276,12 @@ struct event_desc { #define OPT_UMBRELLA_DEVID 64 #define OPT_CMARK_ALST_EN 65 #define OPT_QUIET_TFTP 66 -#define OPT_FILTER_A 67 -#define OPT_FILTER_AAAA 68 -#define OPT_STRIP_ECS 69 -#define OPT_STRIP_MAC 70 -#define OPT_NORR 71 -#define OPT_NO_IDENT 72 -#define OPT_CACHE_RR 73 -#define OPT_LAST 74 +#define OPT_STRIP_ECS 67 +#define OPT_STRIP_MAC 68 +#define OPT_NORR 69 +#define OPT_NO_IDENT 70 +#define OPT_CACHE_RR 71 +#define OPT_LAST 72 #define OPTION_BITS (sizeof(unsigned int)*8) #define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) ) @@ -1130,7 +1128,7 @@ extern struct daemon { struct naptr *naptr; struct txt_record *txt, *rr; struct ptr_record *ptr; - struct rrlist *cache_rr, filter_rr; + struct rrlist *cache_rr, *filter_rr; struct host_record *host_records, *host_records_tail; struct cname *cnames; struct auth_zone *auth_zones; @@ -1831,8 +1829,7 @@ void from_wire(char *name); /* modes. */ #define RRFILTER_EDNS0 0 #define RRFILTER_DNSSEC 1 -#define RRFILTER_A 2 -#define RRFILTER_AAAA 3 +#define RRFILTER_CONF 2 /* edns0.c */ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, diff --git a/src/forward.c b/src/forward.c index d79cc56..5c1bad3 100644 --- a/src/forward.c +++ b/src/forward.c @@ -829,19 +829,8 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server break; } - if (rcode == NOERROR) - { - size_t modified = 0; - - if (option_bool(OPT_FILTER_A)) - modified = rrfilter(header, &n, RRFILTER_A); - - if (option_bool(OPT_FILTER_AAAA)) - modified += rrfilter(header, &n, RRFILTER_AAAA); - - if (modified > 0) - ede = EDE_FILTERED; - } + if (rcode == NOERROR && rrfilter(header, &n, RRFILTER_CONF) > 0) + ede = EDE_FILTERED; if (doctored) cache_secure = 0; diff --git a/src/option.c b/src/option.c index 0f70932..ac3b24d 100644 --- a/src/option.c +++ b/src/option.c @@ -187,6 +187,7 @@ struct myoption { #define LOPT_NORR 378 #define LOPT_NO_IDENT 379 #define LOPT_CACHE_RR 380 +#define LOPT_FILTER_RR 381 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = @@ -226,6 +227,7 @@ static const struct myoption opts[] = { "filterwin2k", 0, 0, 'f' }, { "filter-A", 0, 0, LOPT_FILTER_A }, { "filter-AAAA", 0, 0, LOPT_FILTER_AAAA }, + { "filter-rr", 1, 0, LOPT_FILTER_RR }, { "pid-file", 2, 0, 'x' }, { "strict-order", 0, 0, 'o' }, { "server", 1, 0, 'S' }, @@ -405,8 +407,9 @@ 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 }, + { LOPT_FILTER_A, ARG_DUP, NULL, gettext_noop("Don't include IPv4 addresses in DNS answers."), NULL }, + { LOPT_FILTER_AAAA, ARG_DUP, NULL, gettext_noop("Don't include IPv6 addresses in DNS answers."), NULL }, + { LOPT_FILTER_RR, ARG_DUP, "<RR-type>", gettext_noop("Don't include resource records of the given type in DNS answers."), NULL }, { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL }, { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP }, { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL }, @@ -575,7 +578,7 @@ static struct { { LOPT_QUIET_TFTP, OPT_QUIET_TFTP, NULL, gettext_noop("Do not log routine TFTP."), NULL }, { LOPT_NORR, OPT_NORR, NULL, gettext_noop("Suppress round-robin ordering of DNS records."), NULL }, { LOPT_NO_IDENT, OPT_NO_IDENT, NULL, gettext_noop("Do not add CHAOS TXT records."), NULL }, - { LOPT_CACHE_RR, ARG_DUP, "RRtype", gettext_noop("Cache this DNS resource record type."), NULL }, + { LOPT_CACHE_RR, ARG_DUP, "<RR-type>", gettext_noop("Cache this DNS resource record type."), NULL }, { 0, 0, NULL, NULL, NULL } }; @@ -3470,19 +3473,39 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma break; case LOPT_CACHE_RR: /* --cache-rr */ + case LOPT_FILTER_RR: /* --filter-rr */ + case LOPT_FILTER_A: /* --filter-A */ + case LOPT_FILTER_AAAA: /* --filter-AAAA */ while (1) { int type; struct rrlist *new; - - comma = split(arg); - if (!atoi_check(arg, &type) && (type = rrtype(arg)) == 0) - ret_err(_("bad RR type")); + comma = NULL; + + if (option == LOPT_FILTER_A) + type = T_A; + else if (option == LOPT_FILTER_AAAA) + type = T_AAAA; + else + { + comma = split(arg); + if (!atoi_check(arg, &type) && (type = rrtype(arg)) == 0) + ret_err(_("bad RR type")); + } + new = opt_malloc(sizeof(struct rrlist)); new->rr = type; - new->next = daemon->cache_rr; - daemon->cache_rr = new; + if (option == LOPT_CACHE_RR) + { + new->next = daemon->cache_rr; + daemon->cache_rr = new; + } + else + { + new->next = daemon->filter_rr; + daemon->filter_rr = new; + } if (!comma) break; arg = comma; diff --git a/src/rfc1035.c b/src/rfc1035.c index 63aff8a..8c8b73a 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -916,7 +916,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 (((flags & F_IPV4) && option_bool(OPT_FILTER_A)) || ((flags & F_IPV6) && option_bool(OPT_FILTER_AAAA))) + if (rr_on_list(daemon->filter_rr, qtype)) negflag = F_NEG | F_CONFIG; log_query(negflag | flags | F_FORWARD | secflag, name, &addr, NULL, aqtype); @@ -1912,7 +1912,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!(crecp->flags & (F_HOSTS | F_DHCP))) auth = 0; - if ((((flag & F_IPV4) && option_bool(OPT_FILTER_A)) || ((flag & F_IPV6) && option_bool(OPT_FILTER_AAAA))) && + if (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. */ @@ -1921,7 +1921,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!dryrun) log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0); - + if (filtered) *filtered = 1; } @@ -1968,27 +1968,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, anscount++; } } - else if (((flag & F_IPV4) && option_bool(OPT_FILTER_A)) || ((flag & F_IPV6) && option_bool(OPT_FILTER_AAAA))) - { - /* 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 - that may be enough to tell us if the answer should be NODATA and save the round trip. - Cached NXDOMAIN has already been handled, so here we look for any record for the domain, - since its existence allows us to return a NODATA answer. Note that we never set the AD flag, - since we didn't authentucate the record. */ - - if (cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_RR)) - { - ans = 1; - sec_data = auth = 0; - - if (!dryrun) - log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0); - - if (filtered) - *filtered = 1; - } - } } if (qtype == T_MX || qtype == T_ANY) @@ -2103,30 +2082,32 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, log_query(F_CONFIG | F_NEG, name, &addr, NULL, 0); } - if (!ans && qtype != T_ANY) + if (!ans) { if ((crecp = cache_find_by_name(NULL, name, now, F_RR | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))) && rd_bit && (!do_bit || cache_validated(crecp))) do { - int stale_flag = 0; + int flags = crecp->flags; - if (crecp->addr.rr.rrtype == qtype) + if ((flags & F_NXDOMAIN) || crecp->addr.rr.rrtype == qtype) { if (crec_isstale(crecp, now)) { if (stale) *stale = 1; - stale_flag = F_STALE; + flags |= F_STALE; } - if (!(crecp->flags & F_DNSSECOK)) + if (!(flags & F_DNSSECOK)) sec_data = 0; - if (crecp->flags & F_NXDOMAIN) + if (flags & F_NXDOMAIN) nxdomain = 1; - + else if (rr_on_list(daemon->filter_rr, qtype)) + flags |= F_NEG | F_CONFIG; + auth = 0; ans = 1; @@ -2134,7 +2115,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, { char *rrdata = NULL; - if (!(crecp->flags & F_NEG)) + if (!(flags & F_NEG)) { rrdata = blockdata_retrieve(crecp->addr.rr.rrdata, crecp->addr.rr.datalen, NULL); @@ -2148,38 +2129,46 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (qtype == T_TXT && !(crecp->flags & F_NEG)) log_txt(name, (unsigned char *)rrdata, crecp->addr.rr.datalen, crecp->flags & F_DNSSECOK); else - log_query(stale_flag | crecp->flags, name, &crecp->addr, NULL, 0); + log_query(flags, name, &crecp->addr, NULL, 0); } } } while ((crecp = cache_find_by_name(crecp, name, now, F_RR))); } - } - - - if (!ans) - { - if (option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_')))) + + if (!ans && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_')))) { ans = 1; sec_data = 0; if (!dryrun) log_query(F_CONFIG | F_NEG, name, NULL, NULL, 0); } - else if ((crecp = cache_find_by_name(NULL, name, now, F_NXDOMAIN))) + + + if (!ans && rr_on_list(daemon->filter_rr, qtype)) { - /* We may know that the domain doesn't exist for any RRtype. */ - ans = nxdomain = 1; - auth = 0; - - if (!(crecp->flags & F_DNSSECOK)) - sec_data = 0; + /* 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 + that may be enough to tell us if the answer should be NODATA and save the round trip. + Cached NXDOMAIN has already been handled, so here we look for any record for the domain, + since its existence allows us to return a NODATA answer. Note that we never set the AD flag, + since we didn't authenticate the record. */ - if (!dryrun) - log_query(F_NXDOMAIN | F_NEG, name, NULL, NULL, 0); + if (cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_RR | F_CNAME)) + { + ans = 1; + sec_data = auth = 0; + + if (!dryrun) + log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0); + + if (filtered) + *filtered = 1; + } } - else - return 0; /* failed to answer a question */ } + + if (!ans) + return 0; /* failed to answer a question */ } if (dryrun) diff --git a/src/rrfilter.c b/src/rrfilter.c index e4c56cb..d98236e 100644 --- a/src/rrfilter.c +++ b/src/rrfilter.c @@ -219,10 +219,7 @@ size_t rrfilter(struct dns_header *header, size_t *plen, int mode) if (class != C_IN) continue; - if (mode == RRFILTER_A && type != T_A) - continue; - - if (mode == RRFILTER_AAAA && type != T_AAAA) + if (!rr_on_list(daemon->filter_rr, type)) continue; } |