Add filtering of arbitrary RR-types.

This commit is contained in:
Simon Kelley
2023-03-29 22:43:21 +01:00
parent 042c64273d
commit a3c8b75972
8 changed files with 102 additions and 90 deletions

View File

@@ -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)
{

View File

@@ -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

View File

@@ -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,

View File

@@ -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;

View File

@@ -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;

View File

@@ -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)

View File

@@ -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;
}