mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 18:28:25 +00:00
Add EDE "filtered" extended error when --filter-A or --filter-AAAA act.
If a NODATA answer is returned instead of actual data for A or AAAA queries because of the existence of --filter-A or --filter-AAAA config options, then mark the replies with an EDE "filtered" tag. Basic patch by Petr Menšík, tweaked by Simon Kelley to apply onto the preceding caching patches.
This commit is contained in:
@@ -1366,7 +1366,7 @@ void report_addresses(struct dns_header *header, size_t len, u32 mark);
|
|||||||
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||||
struct in_addr local_addr, struct in_addr local_netmask,
|
struct in_addr local_addr, struct in_addr local_netmask,
|
||||||
time_t now, int ad_reqd, int do_bit, int have_pseudoheader,
|
time_t now, int ad_reqd, int do_bit, int have_pseudoheader,
|
||||||
int *stale);
|
int *stale, int *filtered);
|
||||||
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
||||||
time_t now);
|
time_t now);
|
||||||
int check_for_ignored_address(struct dns_header *header, size_t qlen);
|
int check_for_ignored_address(struct dns_header *header, size_t qlen);
|
||||||
@@ -1811,7 +1811,7 @@ void poll_listen(int fd, short event);
|
|||||||
int do_poll(int timeout);
|
int do_poll(int timeout);
|
||||||
|
|
||||||
/* rrfilter.c */
|
/* rrfilter.c */
|
||||||
size_t rrfilter(struct dns_header *header, size_t plen, int mode);
|
size_t rrfilter(struct dns_header *header, size_t *plen, int mode);
|
||||||
u16 *rrfilter_desc(int type);
|
u16 *rrfilter_desc(int type);
|
||||||
int expand_workspace(unsigned char ***wkspc, int *szp, int new);
|
int expand_workspace(unsigned char ***wkspc, int *szp, int new);
|
||||||
/* modes. */
|
/* modes. */
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
|
|||||||
memcpy(buff, datap, rdlen);
|
memcpy(buff, datap, rdlen);
|
||||||
|
|
||||||
/* now, delete OPT RR */
|
/* now, delete OPT RR */
|
||||||
plen = rrfilter(header, plen, RRFILTER_EDNS0);
|
rrfilter(header, &plen, RRFILTER_EDNS0);
|
||||||
|
|
||||||
/* Now, force addition of a new one */
|
/* Now, force addition of a new one */
|
||||||
p = NULL;
|
p = NULL;
|
||||||
|
|||||||
@@ -721,7 +721,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||||||
if (added_pheader)
|
if (added_pheader)
|
||||||
{
|
{
|
||||||
/* client didn't send EDNS0, we added one, strip it off before returning answer. */
|
/* client didn't send EDNS0, we added one, strip it off before returning answer. */
|
||||||
n = rrfilter(header, n, RRFILTER_EDNS0);
|
rrfilter(header, &n, RRFILTER_EDNS0);
|
||||||
pheader = NULL;
|
pheader = NULL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -831,11 +831,16 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||||||
|
|
||||||
if (rcode == NOERROR)
|
if (rcode == NOERROR)
|
||||||
{
|
{
|
||||||
|
size_t modified = 0;
|
||||||
|
|
||||||
if (option_bool(OPT_FILTER_A))
|
if (option_bool(OPT_FILTER_A))
|
||||||
n = rrfilter(header, n, RRFILTER_A);
|
modified = rrfilter(header, &n, RRFILTER_A);
|
||||||
|
|
||||||
if (option_bool(OPT_FILTER_AAAA))
|
if (option_bool(OPT_FILTER_AAAA))
|
||||||
n = rrfilter(header, n, RRFILTER_AAAA);
|
modified += rrfilter(header, &n, RRFILTER_AAAA);
|
||||||
|
|
||||||
|
if (modified > 0)
|
||||||
|
ede = EDE_FILTERED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doctored)
|
if (doctored)
|
||||||
@@ -859,7 +864,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 the requestor didn't set the DO bit, don't return DNSSEC info. */
|
||||||
if (!do_bit)
|
if (!do_bit)
|
||||||
n = rrfilter(header, n, RRFILTER_DNSSEC);
|
rrfilter(header, &n, RRFILTER_DNSSEC);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1807,7 +1812,7 @@ void receive_query(struct listener *listen, time_t now)
|
|||||||
#endif
|
#endif
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int stale;
|
int stale, filtered;
|
||||||
int ad_reqd = do_bit;
|
int ad_reqd = do_bit;
|
||||||
u16 hb3 = header->hb3, hb4 = header->hb4;
|
u16 hb3 = header->hb3, hb4 = header->hb4;
|
||||||
int fd = listen->fd;
|
int fd = listen->fd;
|
||||||
@@ -1817,17 +1822,28 @@ void receive_query(struct listener *listen, time_t now)
|
|||||||
ad_reqd = 1;
|
ad_reqd = 1;
|
||||||
|
|
||||||
m = answer_request(header, ((char *) header) + udp_size, (size_t)n,
|
m = answer_request(header, ((char *) header) + udp_size, (size_t)n,
|
||||||
dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale);
|
dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale, &filtered);
|
||||||
|
|
||||||
if (m >= 1)
|
if (m >= 1)
|
||||||
{
|
{
|
||||||
if (stale && have_pseudoheader)
|
if (have_pseudoheader)
|
||||||
{
|
{
|
||||||
u16 swap = htons(EDE_STALE);
|
int ede = EDE_UNSET;
|
||||||
|
|
||||||
m = add_pseudoheader(header, m, ((unsigned char *) header) + udp_size, daemon->edns_pktsz,
|
if (filtered)
|
||||||
EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
ede = EDE_FILTERED;
|
||||||
|
else if (stale)
|
||||||
|
ede = EDE_STALE;
|
||||||
|
|
||||||
|
if (ede != EDE_UNSET)
|
||||||
|
{
|
||||||
|
u16 swap = htons(ede);
|
||||||
|
|
||||||
|
m = add_pseudoheader(header, m, ((unsigned char *) header) + udp_size, daemon->edns_pktsz,
|
||||||
|
EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_DUMPFILE
|
#ifdef HAVE_DUMPFILE
|
||||||
dump_packet_udp(DUMP_REPLY, daemon->packet, m, NULL, &source_addr, listen->fd);
|
dump_packet_udp(DUMP_REPLY, daemon->packet, m, NULL, &source_addr, listen->fd);
|
||||||
#endif
|
#endif
|
||||||
@@ -2097,7 +2113,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
unsigned char *pheader;
|
unsigned char *pheader;
|
||||||
unsigned int mark = 0;
|
unsigned int mark = 0;
|
||||||
int have_mark = 0;
|
int have_mark = 0;
|
||||||
int first, last, stale, do_stale = 0;
|
int first, last, filtered, stale, do_stale = 0;
|
||||||
unsigned int flags = 0;
|
unsigned int flags = 0;
|
||||||
u16 hb3, hb4;
|
u16 hb3, hb4;
|
||||||
|
|
||||||
@@ -2291,7 +2307,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
else
|
else
|
||||||
/* m > 0 if answered from cache */
|
/* m > 0 if answered from cache */
|
||||||
m = answer_request(header, ((char *) header) + 65536, (size_t)size,
|
m = answer_request(header, ((char *) header) + 65536, (size_t)size,
|
||||||
dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale);
|
dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale, &filtered);
|
||||||
|
|
||||||
/* Do this by steam now we're not in the select() loop */
|
/* Do this by steam now we're not in the select() loop */
|
||||||
check_log_writer(1);
|
check_log_writer(1);
|
||||||
@@ -2430,12 +2446,22 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
|
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (stale)
|
else
|
||||||
{
|
{
|
||||||
u16 swap = htons((u16)EDE_STALE);
|
ede = EDE_UNSET;
|
||||||
|
|
||||||
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
if (filtered)
|
||||||
}
|
ede = EDE_FILTERED;
|
||||||
|
else if (stale)
|
||||||
|
ede = EDE_STALE;
|
||||||
|
|
||||||
|
if (ede != EDE_UNSET)
|
||||||
|
{
|
||||||
|
u16 swap = htons((u16)ede);
|
||||||
|
|
||||||
|
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
check_log_writer(1);
|
check_log_writer(1);
|
||||||
|
|
||||||
|
|||||||
@@ -1419,7 +1419,7 @@ static int cache_validated(const struct crec *crecp)
|
|||||||
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||||
struct in_addr local_addr, struct in_addr local_netmask,
|
struct in_addr local_addr, struct in_addr local_netmask,
|
||||||
time_t now, int ad_reqd, int do_bit, int have_pseudoheader,
|
time_t now, int ad_reqd, int do_bit, int have_pseudoheader,
|
||||||
int *stale)
|
int *stale, int *filtered)
|
||||||
{
|
{
|
||||||
char *name = daemon->namebuff;
|
char *name = daemon->namebuff;
|
||||||
unsigned char *p, *ansp;
|
unsigned char *p, *ansp;
|
||||||
@@ -1438,6 +1438,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
if (stale)
|
if (stale)
|
||||||
*stale = 0;
|
*stale = 0;
|
||||||
|
|
||||||
|
if (filtered)
|
||||||
|
*filtered = 0;
|
||||||
|
|
||||||
/* never answer queries with RD unset, to avoid cache snooping. */
|
/* never answer queries with RD unset, to avoid cache snooping. */
|
||||||
if (ntohs(header->ancount) != 0 ||
|
if (ntohs(header->ancount) != 0 ||
|
||||||
ntohs(header->nscount) != 0 ||
|
ntohs(header->nscount) != 0 ||
|
||||||
@@ -1706,7 +1709,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
|
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
||||||
if (!(crecp->flags & F_DNSSECOK))
|
if (!(crecp->flags & F_DNSSECOK))
|
||||||
sec_data = 0;
|
sec_data = 0;
|
||||||
|
|
||||||
@@ -1887,6 +1889,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
|
|
||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0);
|
log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0);
|
||||||
|
|
||||||
|
if (filtered)
|
||||||
|
*filtered = 1;
|
||||||
}
|
}
|
||||||
else if (crecp->flags & F_NEG)
|
else if (crecp->flags & F_NEG)
|
||||||
{
|
{
|
||||||
@@ -1947,6 +1952,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
|
|
||||||
if (!dryrun)
|
if (!dryrun)
|
||||||
log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0);
|
log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0);
|
||||||
|
|
||||||
|
if (filtered)
|
||||||
|
*filtered = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,41 +156,43 @@ static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, i
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* mode may be remove EDNS0 or DNSSEC RRs or remove A or AAAA from answer section. */
|
/* 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)
|
* returns number of modified records. */
|
||||||
|
size_t rrfilter(struct dns_header *header, size_t *plen, int mode)
|
||||||
{
|
{
|
||||||
static unsigned char **rrs = NULL;
|
static unsigned char **rrs = NULL;
|
||||||
static int rr_sz = 0;
|
static int rr_sz = 0;
|
||||||
|
|
||||||
unsigned char *p = (unsigned char *)(header+1);
|
unsigned char *p = (unsigned char *)(header+1);
|
||||||
int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar;
|
size_t rr_found = 0;
|
||||||
|
int i, rdlen, qtype, qclass, chop_an, chop_ns, chop_ar;
|
||||||
|
|
||||||
if (ntohs(header->qdcount) != 1 ||
|
if (ntohs(header->qdcount) != 1 ||
|
||||||
!(p = skip_name(p, header, plen, 4)))
|
!(p = skip_name(p, header, *plen, 4)))
|
||||||
return plen;
|
return 0;
|
||||||
|
|
||||||
GETSHORT(qtype, p);
|
GETSHORT(qtype, p);
|
||||||
GETSHORT(qclass, p);
|
GETSHORT(qclass, p);
|
||||||
|
|
||||||
/* First pass, find pointers to start and end of all the records we wish to elide:
|
/* First pass, find pointers to start and end of all the records we wish to elide:
|
||||||
records added for DNSSEC, unless explicitly queried for */
|
records added for DNSSEC, unless explicitly queried for */
|
||||||
for (rr_found = 0, chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0;
|
for (chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0;
|
||||||
i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount);
|
i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount);
|
||||||
i++)
|
i++)
|
||||||
{
|
{
|
||||||
unsigned char *pstart = p;
|
unsigned char *pstart = p;
|
||||||
int type, class;
|
int type, class;
|
||||||
|
|
||||||
if (!(p = skip_name(p, header, plen, 10)))
|
if (!(p = skip_name(p, header, *plen, 10)))
|
||||||
return plen;
|
return rr_found;
|
||||||
|
|
||||||
GETSHORT(type, p);
|
GETSHORT(type, p);
|
||||||
GETSHORT(class, p);
|
GETSHORT(class, p);
|
||||||
p += 4; /* TTL */
|
p += 4; /* TTL */
|
||||||
GETSHORT(rdlen, p);
|
GETSHORT(rdlen, p);
|
||||||
|
|
||||||
if (!ADD_RDLEN(header, p, plen, rdlen))
|
if (!ADD_RDLEN(header, p, *plen, rdlen))
|
||||||
return plen;
|
return rr_found;
|
||||||
|
|
||||||
if (mode == RRFILTER_EDNS0) /* EDNS */
|
if (mode == RRFILTER_EDNS0) /* EDNS */
|
||||||
{
|
{
|
||||||
@@ -225,7 +227,7 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
|
if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
|
||||||
return plen;
|
return rr_found;
|
||||||
|
|
||||||
rrs[rr_found++] = pstart;
|
rrs[rr_found++] = pstart;
|
||||||
rrs[rr_found++] = p;
|
rrs[rr_found++] = p;
|
||||||
@@ -240,7 +242,7 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode)
|
|||||||
|
|
||||||
/* Nothing to do. */
|
/* Nothing to do. */
|
||||||
if (rr_found == 0)
|
if (rr_found == 0)
|
||||||
return plen;
|
return rr_found;
|
||||||
|
|
||||||
/* Second pass, look for pointers in names in the records we're keeping and make sure they don't
|
/* Second pass, look for pointers in names in the records we're keeping and make sure they don't
|
||||||
point to records we're going to elide. This is theoretically possible, but unlikely. If
|
point to records we're going to elide. This is theoretically possible, but unlikely. If
|
||||||
@@ -248,38 +250,38 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode)
|
|||||||
p = (unsigned char *)(header+1);
|
p = (unsigned char *)(header+1);
|
||||||
|
|
||||||
/* question first */
|
/* question first */
|
||||||
if (!check_name(&p, header, plen, 0, rrs, rr_found))
|
if (!check_name(&p, header, *plen, 0, rrs, rr_found))
|
||||||
return plen;
|
return rr_found;
|
||||||
p += 4; /* qclass, qtype */
|
p += 4; /* qclass, qtype */
|
||||||
|
|
||||||
/* Now answers and NS */
|
/* Now answers and NS */
|
||||||
if (!check_rrs(p, header, plen, 0, rrs, rr_found))
|
if (!check_rrs(p, header, *plen, 0, rrs, rr_found))
|
||||||
return plen;
|
return rr_found;
|
||||||
|
|
||||||
/* Third pass, actually fix up pointers in the records */
|
/* Third pass, actually fix up pointers in the records */
|
||||||
p = (unsigned char *)(header+1);
|
p = (unsigned char *)(header+1);
|
||||||
|
|
||||||
check_name(&p, header, plen, 1, rrs, rr_found);
|
check_name(&p, header, *plen, 1, rrs, rr_found);
|
||||||
p += 4; /* qclass, qtype */
|
p += 4; /* qclass, qtype */
|
||||||
|
|
||||||
check_rrs(p, header, plen, 1, rrs, rr_found);
|
check_rrs(p, header, *plen, 1, rrs, rr_found);
|
||||||
|
|
||||||
/* Fourth pass, elide records */
|
/* Fourth pass, elide records */
|
||||||
for (p = rrs[0], i = 1; i < rr_found; i += 2)
|
for (p = rrs[0], i = 1; (unsigned)i < rr_found; i += 2)
|
||||||
{
|
{
|
||||||
unsigned char *start = rrs[i];
|
unsigned char *start = rrs[i];
|
||||||
unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)header) + plen;
|
unsigned char *end = ((unsigned)i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)header) + *plen;
|
||||||
|
|
||||||
memmove(p, start, end-start);
|
memmove(p, start, end-start);
|
||||||
p += end-start;
|
p += end-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
plen = p - (unsigned char *)header;
|
*plen = p - (unsigned char *)header;
|
||||||
header->ancount = htons(ntohs(header->ancount) - chop_an);
|
header->ancount = htons(ntohs(header->ancount) - chop_an);
|
||||||
header->nscount = htons(ntohs(header->nscount) - chop_ns);
|
header->nscount = htons(ntohs(header->nscount) - chop_ns);
|
||||||
header->arcount = htons(ntohs(header->arcount) - chop_ar);
|
header->arcount = htons(ntohs(header->arcount) - chop_ar);
|
||||||
|
|
||||||
return plen;
|
return rr_found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is used in the DNSSEC code too, hence it's exported */
|
/* This is used in the DNSSEC code too, hence it's exported */
|
||||||
|
|||||||
Reference in New Issue
Block a user