summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2023-03-29 22:43:21 +0100
committerSimon Kelley <simon@thekelleys.org.uk>2023-03-29 22:43:21 +0100
commita3c8b75972604443cf763fb06e259b38ceec3351 (patch)
treecc9fb57b8fb2b30266dc6ba8beb1a124a20d25ec
parent042c64273d553c8305d3747849b44a0b257b0622 (diff)
downloaddnsmasq-a3c8b75972604443cf763fb06e259b38ceec3351.tar.gz
Add filtering of arbitrary RR-types.
-rw-r--r--man/dnsmasq.83
-rw-r--r--src/dbus.c18
-rw-r--r--src/dnsmasq.c2
-rw-r--r--src/dnsmasq.h19
-rw-r--r--src/forward.c15
-rw-r--r--src/option.c41
-rw-r--r--src/rfc1035.c89
-rw-r--r--src/rrfilter.c5
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
diff --git a/src/dbus.c b/src/dbus.c
index 4366b7e..4512b8e 100644
--- a/src/dbus.c
+++ b/src/dbus.c
@@ -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;
}