Compare commits

...
12 Commits
Author SHA1 Message Date
Simon Kelley 8c8e5385fd Close debian bug. 2024-02-12 23:11:03 +00:00
Simon Kelley 3de7289bd6 Make --filter-rr=ANY filter the answer to ANY queries.
Thanks to Dominik Derigs for an earlier patch which inspired this.
2024-02-12 20:45:20 +00:00
Simon Kelley febeea9d01 Tweak logging and special handling of T_ANY in rr-filter code. 2024-02-12 13:42:07 +00:00
Heikki Linnakangas 762a3f2430 Don't create a useless inotify file desrcriptor when --port=0
If there are no dynamic configuration directories configured with
dhcp-hostsdir, dhcp-optsdir and hostsdir then we need to use inotify
only to track changes to resolv-files, but we don't need to do
that when DNS is disabled (port=0) or no resolv-files are configured.

It turns out that inotify slots can be a scarce resource, so not
using one when it's not needed is a Goood Thing.

Patch by HL, description above from SRK.
2024-02-07 14:44:49 +00:00
Simon Kelley 6d35601da4 Refactor the accumulated crud of years in process_reply(). 2024-02-05 22:33:09 +00:00
Simon Kelley a827127c77 Handle caching SOA for negative PTR queries.
Also deal with the fact that a root SOA is a thing.
2024-02-03 20:46:23 +00:00
Simon Kelley d4a6f3a93e Fix logic error in signed RR handling.
In extract_addresses() the "secure" argument is only set if the
whole reply is validated (ie the AD bit can be set). Even without
that, some records may be validated, and should be marked
as such in the cache.

Related, the DNS doctor code has to update the flags for individual
RRs as it works, not the global "secure" flag.
2024-02-02 21:36:56 +00:00
Simon Kelley 86c15032ba Fix compiler warning. 2024-02-02 00:26:44 +00:00
Simon Kelley 12ddb2a4b9 Cache SOAs and return them with cached NXDOMAIN/NODATA replies.
Now we can cache arbirary RRs, give more correct answers when
replying negative answers from cache.

To implement this needed the DNS-doctor code to be untangled from
find_soa(), so it should be under suspicion for any regresssions
in that department.
2024-02-01 23:37:11 +00:00
Simon Kelley db07664f2a Hardcode Lua library version in debian/rules, rather than the Makefile. 2024-01-26 23:03:34 +00:00
Simon Kelley 1205fc3541 Let pkg-config select the newest installed Lua version, don't hardcode it.
The version can be overridden with the LUA envvar

Make LUA=lua5.4

Thanks to Petr Menšík for the patch which inspired this one.
2024-01-26 22:44:06 +00:00
Simon Kelley 3a8ebcac77 Debian changelog tweaking: LUA -> Lua and use upstream test version
for experimental release.
2024-01-26 22:23:12 +00:00
15 changed files with 350 additions and 212 deletions

No files matched your search

+6
View File
@@ -26,6 +26,12 @@ version 2.90
when the usptream server is a DOH/DOT proxy. Thanks to Justin He when the usptream server is a DOH/DOT proxy. Thanks to Justin He
for the bug report. for the bug report.
Add configurable caching for arbitrary RR-types.
Add --filter-rr option, to filter arbitrary RR-types.
--filter-rr=ANY has a special meaning: it filters the
answers to queries for the ANY RR-type.
version 2.89 version 2.89
Fix bug introduced in 2.88 (commit fe91134b) which can result Fix bug introduced in 2.88 (commit fe91134b) which can result
+3 -2
View File
@@ -29,6 +29,7 @@ LDFLAGS =
COPTS = COPTS =
RPM_OPT_FLAGS = RPM_OPT_FLAGS =
LIBS = LIBS =
LUA = lua
################################################################# #################################################################
@@ -60,8 +61,8 @@ idn2_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LIBIDN2 $(PKG_CONFI
idn2_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LIBIDN2 $(PKG_CONFIG) --libs libidn2` idn2_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LIBIDN2 $(PKG_CONFIG) --libs libidn2`
ct_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --cflags libnetfilter_conntrack` ct_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --cflags libnetfilter_conntrack`
ct_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --libs libnetfilter_conntrack` ct_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --libs libnetfilter_conntrack`
lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua5.4` lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags $(LUA)`
lua_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs lua5.4` lua_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs $(LUA)`
nettle_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags 'nettle hogweed' \ nettle_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags 'nettle hogweed' \
HAVE_CRYPTOHASH $(PKG_CONFIG) --cflags nettle \ HAVE_CRYPTOHASH $(PKG_CONFIG) --cflags nettle \
HAVE_NETTLEHASH $(PKG_CONFIG) --cflags nettle` HAVE_NETTLEHASH $(PKG_CONFIG) --cflags nettle`
+5 -3
View File
@@ -1,7 +1,9 @@
dnsmasq (2.90-1) experimental; urgency=medium dnsmasq (2.90~test4-1) experimental; urgency=medium
[ Simon Kelley ] [ Simon Kelley ]
* New upstream. (closes: #1033165) * New upstream. (closes: #1033165)
* Move hard-coding of Lua version from the upstream Makefile
to d/rules.
[ Sven Geuer ] [ Sven Geuer ]
* Introduce autokpgtests per d/tests/* (closes: #1034135). * Introduce autokpgtests per d/tests/* (closes: #1034135).
* Switch to dpkg-source 3.0 (quilt) format (closes: #1007041). * Switch to dpkg-source 3.0 (quilt) format (closes: #1007041).
@@ -11,7 +13,7 @@ dnsmasq (2.90-1) experimental; urgency=medium
* Rename d/systemd.service to d/dnsmasq.service. * Rename d/systemd.service to d/dnsmasq.service.
* Rename d/systemd@.service to d/dnsmasq@.service. * Rename d/systemd@.service to d/dnsmasq@.service.
* Refactor d/rules to use the DH sequencer and fix major lintian issues * Refactor d/rules to use the DH sequencer and fix major lintian issues
(closes: #844989, #1040923). (closes: #844989, #1040923, #1063551).
Modified files: Modified files:
- d/rules - d/rules
Complete rewrite making use of debhelper and its tools, fixes lintian Complete rewrite making use of debhelper and its tools, fixes lintian
@@ -78,7 +80,7 @@ dnsmasq (2.90-1) experimental; urgency=medium
* Introduce d/u/metadata. * Introduce d/u/metadata.
* Fix lintian issue duplicate-short-description. * Fix lintian issue duplicate-short-description.
* Fix lintian issue capitalization-error-in-description. * Fix lintian issue capitalization-error-in-description.
* Bump LUA version to 5.4 (closes: #1050750). * Bump Lua version to 5.4 (closes: #1050750).
Modified files: Modified files:
- d/control - d/control
- d/t/functions.d/log.patterns - d/t/functions.d/log.patterns
+1 -1
View File
@@ -87,7 +87,7 @@ override_dh_auto_install:
BUILDDIR=debian/auto-build/dnsmasq-base-lua \ BUILDDIR=debian/auto-build/dnsmasq-base-lua \
DESTDIR=$(CURDIR)/debian/dnsmasq-base-lua \ DESTDIR=$(CURDIR)/debian/dnsmasq-base-lua \
PREFIX=$(PREFIX) CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \ PREFIX=$(PREFIX) CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \
COPTS="$(COPTS) -DHAVE_LUASCRIPT" LUA=lua5.4 COPTS="$(COPTS) -DHAVE_LUASCRIPT"
dh_auto_build -p dnsmasq-utils -D contrib/lease-tools dh_auto_build -p dnsmasq-utils -D contrib/lease-tools
override_dh_auto_clean: override_dh_auto_clean:
+5 -1
View File
@@ -386,7 +386,11 @@ Remove A records from answers. No IPv4 addresses will be returned.
Remove AAAA records from answers. No IPv6 addresses will be returned. Remove AAAA records from answers. No IPv6 addresses will be returned.
.TP .TP
.B --filter-rr=<rrtype>[,<rrtype>...] .B --filter-rr=<rrtype>[,<rrtype>...]
Remove records of the specified type(s) from answers. Remove records of the specified type(s) from answers. The otherwise-nonsensical --filter-rr=ANY has
a special meaning: it filters replies to queries for type ANY. Everything other than A, AAAA, MX and CNAME
records are removed. Since ANY queries with forged source addresses can be used in DNS amplification attacks
(replies to ANY queries can be large) this defangs such attacks, whilst still supporting the
one remaining possible use of ANY queries. See RFC 8482 para 4.3 for details.
.TP .TP
.B --cache-rr=<rrtype>[,<rrtype>...] .B --cache-rr=<rrtype>[,<rrtype>...]
By default, dnsmasq caches A, AAAA, CNAME and SRV DNS record types. By default, dnsmasq caches A, AAAA, CNAME and SRV DNS record types.
+13 -13
View File
@@ -802,9 +802,6 @@ void cache_end_insert(void)
read_write(daemon->pipe_to_parent, (unsigned char *)name, m, 0); read_write(daemon->pipe_to_parent, (unsigned char *)name, m, 0);
read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->ttd, sizeof(new_chain->ttd), 0); read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->ttd, sizeof(new_chain->ttd), 0);
read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), 0); read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), 0);
if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_RR))
{
read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), 0); read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), 0);
if (flags & F_RR) if (flags & F_RR)
@@ -829,7 +826,6 @@ void cache_end_insert(void)
#endif #endif
} }
} }
}
new_chain = tmp; new_chain = tmp;
} }
@@ -871,7 +867,8 @@ int cache_recv_insert(time_t now, int fd)
if (!read_write(fd, (unsigned char *)daemon->namebuff, m, 1) || if (!read_write(fd, (unsigned char *)daemon->namebuff, m, 1) ||
!read_write(fd, (unsigned char *)&ttd, sizeof(ttd), 1) || !read_write(fd, (unsigned char *)&ttd, sizeof(ttd), 1) ||
!read_write(fd, (unsigned char *)&flags, sizeof(flags), 1)) !read_write(fd, (unsigned char *)&flags, sizeof(flags), 1) ||
!read_write(fd, (unsigned char *)&addr, sizeof(addr), 1))
return 0; return 0;
daemon->namebuff[m] = 0; daemon->namebuff[m] = 0;
@@ -902,11 +899,6 @@ int cache_recv_insert(time_t now, int fd)
{ {
unsigned short class = C_IN; unsigned short class = C_IN;
if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_RR))
{
if (!read_write(fd, (unsigned char *)&addr, sizeof(addr), 1))
return 0;
if ((flags & F_RR) && !(flags & F_NEG) && (flags & F_KEYTAG) if ((flags & F_RR) && !(flags & F_NEG) && (flags & F_KEYTAG)
&& !(addr.rrblock.rrdata = blockdata_read(fd, addr.rrblock.datalen))) && !(addr.rrblock.rrdata = blockdata_read(fd, addr.rrblock.datalen)))
return 0; return 0;
@@ -924,8 +916,6 @@ int cache_recv_insert(time_t now, int fd)
return 0; return 0;
} }
#endif #endif
}
crecp = really_insert(daemon->namebuff, &addr, class, now, ttl, flags); crecp = really_insert(daemon->namebuff, &addr, class, now, ttl, flags);
} }
} }
@@ -1809,8 +1799,18 @@ static void dump_cache_entry(struct crec *cache, time_t now)
p = buff; p = buff;
*a = 0; *a = 0;
if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
if (cache->flags & F_REVERSE)
{
if ((cache->flags & F_NEG))
n = "";
}
else
{
if (strlen(n) == 0)
n = "<Root>"; n = "<Root>";
}
p += sprintf(p, "%-30.30s ", sanitise(n)); p += sprintf(p, "%-30.30s ", sanitise(n));
if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache)) if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
a = sanitise(cache_get_cname_target(cache)); a = sanitise(cache_get_cname_target(cache));
+2 -10
View File
@@ -131,20 +131,12 @@ int main (int argc, char **argv)
'.' or NAME_ESCAPE then all would have to be escaped, so the '.' or NAME_ESCAPE then all would have to be escaped, so the
presentation format would be twice as long as the spec. */ presentation format would be twice as long as the spec. */
daemon->keyname = safe_malloc((MAXDNAME * 2) + 1); daemon->keyname = safe_malloc((MAXDNAME * 2) + 1);
daemon->workspacename = safe_malloc((MAXDNAME * 2) + 1);
/* one char flag per possible RR in answer section (may get extended). */ /* one char flag per possible RR in answer section (may get extended). */
daemon->rr_status_sz = 64; daemon->rr_status_sz = 64;
daemon->rr_status = safe_malloc(sizeof(*daemon->rr_status) * daemon->rr_status_sz); daemon->rr_status = safe_malloc(sizeof(*daemon->rr_status) * daemon->rr_status_sz);
} }
#endif #endif
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
/* 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 * 2) + 1);
#endif
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
if (!daemon->lease_file) if (!daemon->lease_file)
{ {
@@ -429,8 +421,8 @@ int main (int argc, char **argv)
} }
#ifdef HAVE_INOTIFY #ifdef HAVE_INOTIFY
if ((daemon->port != 0 || daemon->dhcp || daemon->doing_dhcp6) if ((daemon->port != 0 && !option_bool(OPT_NO_RESOLV)) ||
&& (!option_bool(OPT_NO_RESOLV) || daemon->dynamic_dirs)) daemon->dynamic_dirs)
inotify_dnsmasq_init(); inotify_dnsmasq_init();
else else
daemon->inotifyfd = -1; daemon->inotifyfd = -1;
+3 -5
View File
@@ -341,7 +341,7 @@ union all_addr {
in the cache flags. */ in the cache flags. */
struct datablock { struct datablock {
unsigned short rrtype; unsigned short rrtype;
unsigned char datalen; unsigned char datalen; /* also length of SOA in negative records. */
char data[]; char data[];
} rrdata; } rrdata;
}; };
@@ -1233,10 +1233,7 @@ extern struct daemon {
char *packet; /* packet buffer */ char *packet; /* packet buffer */
int packet_buff_sz; /* size of above */ int packet_buff_sz; /* size of above */
char *namebuff; /* MAXDNAME size buffer */ char *namebuff; /* MAXDNAME size buffer */
#if (defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)) || defined(HAVE_DNSSEC)
/* CONNTRACK UBUS code uses this buffer, as well as DNSSEC code. */
char *workspacename; char *workspacename;
#endif
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
char *keyname; /* MAXDNAME size buffer */ char *keyname; /* MAXDNAME size buffer */
unsigned long *rr_status; /* ceiling in TTL from DNSSEC or zero for insecure */ unsigned long *rr_status; /* ceiling in TTL from DNSSEC or zero for insecure */
@@ -1376,6 +1373,7 @@ int is_name_synthetic(int flags, char *name, union all_addr *addr);
int is_rev_synth(int flag, union all_addr *addr, char *name); int is_rev_synth(int flag, union all_addr *addr, char *name);
/* rfc1035.c */ /* rfc1035.c */
int do_doctor(struct dns_header *header, size_t qlen, char *namebuff);
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
char *name, int isExtract, int extrabytes); char *name, int isExtract, int extrabytes);
unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes); unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes);
@@ -1386,7 +1384,7 @@ unsigned int extract_request(struct dns_header *header, size_t qlen,
void setup_reply(struct dns_header *header, unsigned int flags, int ede); void setup_reply(struct dns_header *header, unsigned int flags, int ede);
int extract_addresses(struct dns_header *header, size_t qlen, char *name, int extract_addresses(struct dns_header *header, size_t qlen, char *name,
time_t now, struct ipsets *ipsets, struct ipsets *nftsets, int is_sign, time_t now, struct ipsets *ipsets, struct ipsets *nftsets, int is_sign,
int check_rebind, int no_cache_dnssec, int secure, int *doctored); int check_rebind, int no_cache_dnssec, int secure);
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS) #if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
void report_addresses(struct dns_header *header, size_t len, u32 mark); void report_addresses(struct dns_header *header, size_t len, u32 mark);
#endif #endif
+1 -1
View File
@@ -1804,7 +1804,7 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
When validating replies to DS records, we're only interested in the NSEC{3} RRs in the auth section. When validating replies to DS records, we're only interested in the NSEC{3} RRs in the auth section.
Other RRs in that section missing sigs will not cause am INSECURE reply. We determine this mode Other RRs in that section missing sigs will not cause am INSECURE reply. We determine this mode
is the nons argument is non-NULL. if the nons argument is non-NULL.
*/ */
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname,
int *class, int check_unsigned, int *neganswer, int *nons, int *nsec_ttl) int *class, int check_unsigned, int *neganswer, int *nons, int *nsec_ttl)
+58 -54
View File
@@ -687,13 +687,12 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
{ {
unsigned char *pheader, *sizep; unsigned char *pheader, *sizep;
struct ipsets *ipsets = NULL, *nftsets = NULL; struct ipsets *ipsets = NULL, *nftsets = NULL;
int munged = 0, is_sign; int is_sign;
unsigned int rcode = RCODE(header); unsigned int rcode = RCODE(header);
size_t plen; size_t plen;
(void)ad_reqd; (void)ad_reqd;
(void)do_bit; (void)do_bit;
(void)bogusanswer;
#ifdef HAVE_IPSET #ifdef HAVE_IPSET
if (daemon->ipsets && extract_request(header, n, daemon->namebuff, NULL)) if (daemon->ipsets && extract_request(header, n, daemon->namebuff, NULL))
@@ -782,10 +781,38 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
server->flags |= SERV_WARNED_RECURSIVE; server->flags |= SERV_WARNED_RECURSIVE;
} }
if (header->hb3 & HB3_TC)
{
log_query(F_UPSTREAM, NULL, NULL, "truncated", 0);
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
}
if (!(header->hb3 & HB3_TC) && (!bogusanswer || (header->hb4 & HB4_CD)))
{
if (rcode == NXDOMAIN && extract_request(header, n, daemon->namebuff, NULL) &&
(check_for_local_domain(daemon->namebuff, now) || lookup_domain(daemon->namebuff, F_CONFIG, NULL, NULL)))
{
/* if we forwarded a query for a locally known name (because it was for
an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
since we know that the domain exists, even if upstream doesn't */
header->hb3 |= HB3_AA;
SET_RCODE(header, NOERROR);
cache_secure = 0;
}
if (daemon->doctors && do_doctor(header, n, daemon->namebuff))
cache_secure = 0;
/* check_for_bogus_wildcard() does it's own caching, so
don't call extract_addresses() if it triggers. */
if (daemon->bogus_addr && rcode != NXDOMAIN && if (daemon->bogus_addr && rcode != NXDOMAIN &&
check_for_bogus_wildcard(header, n, daemon->namebuff, now)) check_for_bogus_wildcard(header, n, daemon->namebuff, now))
{ {
munged = 1; header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
SET_RCODE(header, NXDOMAIN); SET_RCODE(header, NXDOMAIN);
header->hb3 &= ~HB3_AA; header->hb3 &= ~HB3_AA;
cache_secure = 0; cache_secure = 0;
@@ -793,62 +820,50 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
} }
else else
{ {
int doctored = 0; int rc = extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure);
if (rcode == NXDOMAIN && if (rc != 0)
extract_request(header, n, daemon->namebuff, NULL))
{ {
if (check_for_local_domain(daemon->namebuff, now) || header->ancount = htons(0);
lookup_domain(daemon->namebuff, F_CONFIG, NULL, NULL)) header->nscount = htons(0);
{ header->arcount = htons(0);
/* if we forwarded a query for a locally known name (because it was for
an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
since we know that the domain exists, even if upstream doesn't */
munged = 1;
header->hb3 |= HB3_AA;
SET_RCODE(header, NOERROR);
cache_secure = 0; cache_secure = 0;
} }
}
switch (extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure, &doctored)) if (rc == 1)
{ {
case 1:
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff); my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
munged = 1;
cache_secure = 0;
ede = EDE_BLOCKED; ede = EDE_BLOCKED;
break;
/* extract_addresses() found a malformed answer. */
case 2:
munged = 1;
SET_RCODE(header, SERVFAIL);
cache_secure = 0;
ede = EDE_OTHER;
break;
} }
if (rcode == NOERROR && rrfilter(header, &n, RRFILTER_CONF) > 0) if (rc == 2)
ede = EDE_FILTERED; {
/* extract_addresses() found a malformed answer. */
SET_RCODE(header, SERVFAIL);
ede = EDE_OTHER;
}
}
if (doctored) if (RCODE(header) == NOERROR && rrfilter(header, &n, RRFILTER_CONF) > 0)
cache_secure = 0; ede = EDE_FILTERED;
} }
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (bogusanswer && !(header->hb4 & HB4_CD) && !option_bool(OPT_DNSSEC_DEBUG)) if (option_bool(OPT_DNSSEC_VALID))
{
if (bogusanswer)
{
if (!(header->hb4 & HB4_CD) && !option_bool(OPT_DNSSEC_DEBUG))
{ {
/* Bogus reply, turn into SERVFAIL */ /* Bogus reply, turn into SERVFAIL */
SET_RCODE(header, SERVFAIL); SET_RCODE(header, SERVFAIL);
munged = 1; header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
ede = EDE_DNSSEC_BOGUS;
} }
}
if (option_bool(OPT_DNSSEC_VALID)) else if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
{
header->hb4 &= ~HB4_AD;
if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
header->hb4 |= HB4_AD; header->hb4 |= HB4_AD;
/* 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. */
@@ -857,19 +872,8 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
} }
#endif #endif
/* do this after extract_addresses. Ensure NODATA reply and remove /* the code above can elide sections of the packet. Find the new length here
nameserver info. */ and put back pseudoheader if it was removed. */
if (munged)
{
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
header->hb3 &= ~HB3_TC;
}
/* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
sections of the packet. Find the new length here and put back pseudoheader
if it was removed. */
n = resize_packet(header, n, pheader, plen); n = resize_packet(header, n, pheader, plen);
if (pheader && ede != EDE_UNSET) if (pheader && ede != EDE_UNSET)
+1 -1
View File
@@ -94,7 +94,7 @@ void inotify_dnsmasq_init()
if (daemon->inotifyfd == -1) if (daemon->inotifyfd == -1)
die(_("failed to create inotify: %s"), NULL, EC_MISC); die(_("failed to create inotify: %s"), NULL, EC_MISC);
if (option_bool(OPT_NO_RESOLV)) if (daemon->port == 0 || option_bool(OPT_NO_RESOLV))
return; return;
for (res = daemon->resolv_files; res; res = res->next) for (res = daemon->resolv_files; res; res = res->next)
+1
View File
@@ -5844,6 +5844,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
daemon = opt_malloc(sizeof(struct daemon)); daemon = opt_malloc(sizeof(struct daemon));
memset(daemon, 0, sizeof(struct daemon)); memset(daemon, 0, sizeof(struct daemon));
daemon->namebuff = buff; daemon->namebuff = buff;
daemon->workspacename = safe_malloc((MAXDNAME * 2) + 1);
daemon->addrbuff = safe_malloc(ADDRSTRLEN); daemon->addrbuff = safe_malloc(ADDRSTRLEN);
/* Set defaults - everything else is zero or NULL */ /* Set defaults - everything else is zero or NULL */
+242 -120
View File
@@ -384,14 +384,23 @@ static int private_net6(struct in6_addr *a, int ban_localhost)
((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */ ((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */
} }
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, int *doctored) int do_doctor(struct dns_header *header, size_t qlen, char *namebuff)
{ {
unsigned char *p;
int i, qtype, qclass, rdlen; int i, qtype, qclass, rdlen;
int done = 0;
for (i = count; i != 0; i--) if (!(p = skip_questions(header, qlen)))
return done;
for (i = 0; i < ntohs(header->ancount) + ntohs(header->arcount); i++)
{ {
if (!(p = skip_name(p, header, qlen, 10))) /* Skip over auth section */
return 0; /* bad packet */ if (i == ntohs(header->ancount) && !(p = skip_section(p, ntohs(header->nscount), header, qlen)))
return done;
if (!extract_name(header, qlen, &p, namebuff, 1, 10))
return done; /* bad packet */
GETSHORT(qtype, p); GETSHORT(qtype, p);
GETSHORT(qclass, p); GETSHORT(qclass, p);
@@ -401,57 +410,77 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
if (qclass == C_IN && qtype == T_A) if (qclass == C_IN && qtype == T_A)
{ {
struct doctor *doctor; struct doctor *doctor;
struct in_addr addr; union all_addr addr;
if (!CHECK_LEN(header, p, qlen, INADDRSZ)) if (!CHECK_LEN(header, p, qlen, INADDRSZ))
return 0; return done;
/* alignment */ /* alignment */
memcpy(&addr, p, INADDRSZ); memcpy(&addr.addr4, p, INADDRSZ);
for (doctor = daemon->doctors; doctor; doctor = doctor->next) for (doctor = daemon->doctors; doctor; doctor = doctor->next)
{ {
if (doctor->end.s_addr == 0) if (doctor->end.s_addr == 0)
{ {
if (!is_same_net(doctor->in, addr, doctor->mask)) if (!is_same_net(doctor->in, addr.addr4, doctor->mask))
continue; continue;
} }
else if (ntohl(doctor->in.s_addr) > ntohl(addr.s_addr) || else if (ntohl(doctor->in.s_addr) > ntohl(addr.addr4.s_addr) ||
ntohl(doctor->end.s_addr) < ntohl(addr.s_addr)) ntohl(doctor->end.s_addr) < ntohl(addr.addr4.s_addr))
continue; continue;
addr.s_addr &= ~doctor->mask.s_addr; addr.addr4.s_addr &= ~doctor->mask.s_addr;
addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr); addr.addr4.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
/* Since we munged the data, the server it came from is no longer authoritative */ /* Since we munged the data, the server it came from is no longer authoritative */
header->hb3 &= ~HB3_AA; header->hb3 &= ~HB3_AA;
*doctored = 1; #ifdef HAVE_DNSSEC
memcpy(p, &addr, INADDRSZ); /* remove validated flag from this RR, since we changed it! */
if (option_bool(OPT_DNSSEC_VALID) && i < ntohs(header->ancount))
daemon->rr_status[i] = 0;
#endif
done = 1;
memcpy(p, &addr.addr4, INADDRSZ);
log_query(F_FORWARD | F_CONFIG | F_IPV4, namebuff, &addr, NULL, 0);
break; break;
} }
} }
if (!ADD_RDLEN(header, p, qlen, rdlen)) if (!ADD_RDLEN(header, p, qlen, rdlen))
return 0; /* bad packet */ return done; /* bad packet */
} }
return p; return done;
} }
static int find_soa(struct dns_header *header, size_t qlen, int *doctored) /* Find SOA RR in auth section to get TTL for negative caching of name.
Cache said SOA and return the difference in length between name and the name of the
SOA RR so we can look it up again.
*/
static int find_soa(struct dns_header *header, size_t qlen, char *name, int *substring, unsigned long *ttlp, int no_cache, time_t now)
{ {
unsigned char *p; unsigned char *p, *psave;
int qtype, qclass, rdlen; int qtype, qclass, rdlen;
unsigned long ttl, minttl = ULONG_MAX; unsigned long ttl, minttl;
int i, found_soa = 0; int i, j;
size_t name_len, soa_len, len;
union all_addr addr;
/* first move to NS section and find TTL from any SOA section */ /* first move to NS section and find TTL from SOA RR */
if (!(p = skip_questions(header, qlen)) || if (!(p = skip_questions(header, qlen)) ||
!(p = do_doctor(p, ntohs(header->ancount), header, qlen, doctored))) !(p = skip_section(p, ntohs(header->ancount), header, qlen)))
return 0; /* bad packet */ return 0; /* bad packet */
for (i = ntohs(header->nscount); i != 0; i--) name_len = strlen(name);
if (substring)
*substring = name_len;
if (ttlp)
*ttlp = daemon->neg_ttl;
for (i = 0; i < ntohs(header->nscount); i++)
{ {
if (!(p = skip_name(p, header, qlen, 10))) if (!extract_name(header, qlen, &p, daemon->workspacename, 1, 0))
return 0; /* bad packet */ return 0; /* bad packet */
GETSHORT(qtype, p); GETSHORT(qtype, p);
@@ -459,40 +488,112 @@ static int find_soa(struct dns_header *header, size_t qlen, int *doctored)
GETLONG(ttl, p); GETLONG(ttl, p);
GETSHORT(rdlen, p); GETSHORT(rdlen, p);
psave = p;
if ((qclass == C_IN) && (qtype == T_SOA)) if ((qclass == C_IN) && (qtype == T_SOA))
{ {
found_soa = 1; soa_len = strlen(daemon->workspacename);
if (ttl < minttl)
minttl = ttl;
/* MNAME */ /* SOA must be for the name we're interested in. */
if (!(p = skip_name(p, header, qlen, 0))) if (soa_len <= name_len && memcmp(daemon->workspacename, name + name_len - soa_len, soa_len) == 0)
{
int prefix = name_len - soa_len;
if (!no_cache)
{
if (!(addr.rrblock.rrdata = blockdata_alloc(NULL, 0)))
return 0; return 0;
/* RNAME */ addr.rrblock.rrtype = T_SOA;
if (!(p = skip_name(p, header, qlen, 20))) addr.rrblock.datalen = 0;
}
for (j = 0; j < 2; j++) /* MNAME, RNAME */
{
if (!extract_name(header, qlen, &p, daemon->workspacename, 1, 0))
{
if (!no_cache)
blockdata_free(addr.rrblock.rrdata);
return 0; return 0;
}
if (!no_cache)
{
len = to_wire(daemon->workspacename);
if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, daemon->workspacename, len))
{
blockdata_free(addr.rrblock.rrdata);
return 0;
}
addr.rrblock.datalen += len;
}
}
if (!CHECK_LEN(header, p, qlen, 20))
{
if (!no_cache)
blockdata_free(addr.rrblock.rrdata);
return 0;
}
/* rest of RR */
if (!no_cache && !blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p, 20))
{
blockdata_free(addr.rrblock.rrdata);
return 0;
}
addr.rrblock.datalen += 20;
if (!no_cache)
{
int secflag = 0;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[i + ntohs(header->ancount)] != 0)
{
secflag = F_DNSSECOK;
/* limit TTL based on signature. */
if (daemon->rr_status[i + ntohs(header->ancount)] < ttl)
ttl = daemon->rr_status[i + ntohs(header->ancount)];
}
#endif
if (!cache_insert(name + prefix, &addr, C_IN, now, ttl, F_FORWARD | F_RR | F_KEYTAG | secflag))
{
blockdata_free(addr.rrblock.rrdata);
return 0;
}
}
p += 16; /* SERIAL REFRESH RETRY EXPIRE */ p += 16; /* SERIAL REFRESH RETRY EXPIRE */
GETLONG(ttl, p); /* minTTL */ GETLONG(minttl, p); /* minTTL */
if (ttl < minttl) if (ttl < minttl)
minttl = ttl; minttl = ttl;
if (substring)
*substring = prefix;
if (ttlp)
*ttlp = minttl;
return 1;
} }
else if (!ADD_RDLEN(header, p, qlen, rdlen)) }
p = psave;
if (!ADD_RDLEN(header, p, qlen, rdlen))
return 0; /* bad packet */ return 0; /* bad packet */
} }
/* rewrite addresses in additional section too */
if (!do_doctor(p, ntohs(header->arcount), header, qlen, doctored))
return 0; return 0;
if (!found_soa)
minttl = daemon->neg_ttl;
return minttl;
} }
/* Print TXT reply to log */ /* Print TXT reply to log */
static int log_txt(char *name, unsigned char *p, const int ardlen, int secflag) static int log_txt(char *name, unsigned char *p, const int ardlen, int flag)
{ {
unsigned char *p1 = p; unsigned char *p1 = p;
@@ -514,7 +615,7 @@ static int log_txt(char *name, unsigned char *p, const int ardlen, int secflag)
} }
*p3 = 0; *p3 = 0;
log_query(secflag | F_FORWARD, name, NULL, (char*)p1, 0); log_query(flag, name, NULL, (char*)p1, 0);
/* restore */ /* restore */
memmove(p1 + 1, p1, i); memmove(p1 + 1, p1, i);
*p1 = len; *p1 = len;
@@ -531,10 +632,10 @@ static int log_txt(char *name, unsigned char *p, const int ardlen, int secflag)
*/ */
int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now, int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
struct ipsets *ipsets, struct ipsets *nftsets, int is_sign, int check_rebind, struct ipsets *ipsets, struct ipsets *nftsets, int is_sign, int check_rebind,
int no_cache_dnssec, int secure, int *doctored) int no_cache_dnssec, int secure)
{ {
unsigned char *p, *p1, *endrr, *namep; unsigned char *p, *p1, *endrr, *namep;
int j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0; int j, qtype, qclass, aqtype, aqclass, ardlen, res;
unsigned long ttl = 0; unsigned long ttl = 0;
union all_addr addr; union all_addr addr;
#ifdef HAVE_IPSET #ifdef HAVE_IPSET
@@ -557,25 +658,6 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
cache_start_insert(); cache_start_insert();
/* find_soa is needed for dns_doctor side effects, so don't call it lazily if there are any. */
if (daemon->doctors || option_bool(OPT_DNSSEC_VALID))
{
searched_soa = 1;
ttl = find_soa(header, qlen, doctored);
if (*doctored)
{
if (secure)
return 0;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
for (j = 0; j < ntohs(header->ancount); j++)
if (daemon->rr_status[j] != 0)
return 0;
#endif
}
}
namep = p = (unsigned char *)(header+1); namep = p = (unsigned char *)(header+1);
if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, 1, 4)) if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, 1, 4))
@@ -624,7 +706,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR)) if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
{ {
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && !no_cache_dnssec && daemon->rr_status[j] != 0) if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j] != 0)
{ {
/* validated RR anywhere in CNAME chain, don't cache. */ /* validated RR anywhere in CNAME chain, don't cache. */
if (cname_short || aqtype == T_CNAME) if (cname_short || aqtype == T_CNAME)
@@ -673,17 +755,16 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (!found && !option_bool(OPT_NO_NEG)) if (!found && !option_bool(OPT_NO_NEG))
{ {
if (!searched_soa) /* For reverse records, we use the name field to store the SOA name. */
{ int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now);
searched_soa = 1;
ttl = find_soa(header, qlen, doctored);
}
flags |= F_NEG | (secure ? F_DNSSECOK : 0); flags |= F_NEG | (secure ? F_DNSSECOK : 0);
if (name_encoding && ttl) if (name_encoding && ttl)
{ {
flags |= F_REVERSE | name_encoding; flags |= F_REVERSE | name_encoding;
cache_insert(NULL, &addr, C_IN, now, ttl, flags); if (!have_soa)
flags |= F_NO_RR; /* Marks no SOA found. */
cache_insert(name + substring, &addr, C_IN, now, ttl, flags);
} }
log_query(flags | F_UPSTREAM, name, &addr, NULL, 0); log_query(flags | F_UPSTREAM, name, &addr, NULL, 0);
@@ -705,7 +786,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
addrlen = IN6ADDRSZ; addrlen = IN6ADDRSZ;
flags |= F_IPV6; flags |= F_IPV6;
} }
else if (qtype != T_CNAME && (qtype == T_SRV || rr_on_list(daemon->cache_rr, qtype))) else if (qtype != T_CNAME &&
(qtype == T_SRV || rr_on_list(daemon->cache_rr, qtype) || rr_on_list(daemon->cache_rr, T_ANY)))
flags |= F_RR; flags |= F_RR;
else else
insert = 0; /* NOTE: do not cache data from CNAME queries. */ insert = 0; /* NOTE: do not cache data from CNAME queries. */
@@ -732,17 +814,18 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
GETSHORT(ardlen, p1); GETSHORT(ardlen, p1);
endrr = p1+ardlen; endrr = p1+ardlen;
if (!CHECK_LEN(header, endrr, qlen, 0))
return 2; /* bad packet */
/* Not what we're looking for? */ /* Not what we're looking for? */
if (aqclass != C_IN || res == 2) if (aqclass != C_IN || res == 2)
{ {
p1 = endrr; p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0))
return 2; /* bad packet */
continue; continue;
} }
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && !no_cache_dnssec && daemon->rr_status[j] != 0) if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j] != 0)
{ {
secflag = F_DNSSECOK; secflag = F_DNSSECOK;
@@ -787,11 +870,12 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
found = 1; found = 1;
} }
else if (aqtype != qtype) else if (qtype == T_ANY || aqtype != qtype)
{ {
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (!option_bool(OPT_DNSSEC_VALID) || aqtype != T_RRSIG) if (!option_bool(OPT_DNSSEC_VALID) || aqtype != T_RRSIG)
#endif #endif
if (qtype != T_ANY)
log_query(secflag | F_FORWARD | F_UPSTREAM | F_RRNAME, name, NULL, NULL, aqtype); log_query(secflag | F_FORWARD | F_UPSTREAM | F_RRNAME, name, NULL, NULL, aqtype);
} }
else if (!(flags & F_NXDOMAIN)) else if (!(flags & F_NXDOMAIN))
@@ -838,7 +922,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
{ {
/* Copy the rest of the RR and end. */ /* Copy the rest of the RR and end. */
if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p1, endrr - p1)) if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p1, endrr - p1))
{
blockdata_free(addr.rrblock.rrdata);
return 0; return 0;
}
addr.rrblock.datalen += endrr - p1; addr.rrblock.datalen += endrr - p1;
} }
else if (desc == 0) else if (desc == 0)
@@ -935,26 +1022,17 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
blockdata_free(addr.rrblock.rrdata); blockdata_free(addr.rrblock.rrdata);
} }
if (aqtype == T_TXT)
{
if (!CHECK_LEN(header, p1, qlen, ardlen))
return 2;
log_txt(name, p1, ardlen, secflag | F_UPSTREAM);
}
else
{
int negflag = F_UPSTREAM;
/* We're filtering this RRtype. It will be removed from the /* We're filtering this RRtype. It will be removed from the
returned packet in process_reply() but gets cached here anyway returned packet in process_reply() but gets cached here anyway
and will be filtered again on the way out of the cache. Here, and will be filtered again on the way out of the cache. Here,
we just need to alter the logging. */ we just need to alter the logging. */
if (rr_on_list(daemon->filter_rr, qtype)) if (qtype != T_ANY && rr_on_list(daemon->filter_rr, qtype))
negflag = F_NEG | F_CONFIG; secflag = F_NEG | F_CONFIG;
log_query(negflag | flags | F_FORWARD | secflag, name, &addr, NULL, aqtype); if (aqtype == T_TXT)
} log_txt(name, p1, ardlen, flags | F_FORWARD | F_UPSTREAM | secflag);
else
log_query(flags | F_FORWARD | F_UPSTREAM | secflag, name, &addr, NULL, aqtype);
} }
p1 = endrr; p1 = endrr;
@@ -974,22 +1052,24 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
log_query(F_UPSTREAM | F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0), name, NULL, NULL, 0); log_query(F_UPSTREAM | F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0), name, NULL, NULL, 0);
if (!searched_soa) if (insert && !option_bool(OPT_NO_NEG))
{ {
searched_soa = 1; int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now);
ttl = find_soa(header, qlen, doctored);
}
/* If there's no SOA to get the TTL from, but there is a CNAME /* If there's no SOA to get the TTL from, but there is a CNAME
pointing at this, inherit its TTL */ pointing at this, inherit its TTL */
if (insert && !option_bool(OPT_NO_NEG) && (ttl || cpp)) if (ttl || cpp)
{ {
if (ttl == 0) if (!ttl)
ttl = cttl; ttl = cttl;
if (flags & F_RR) addr.rrdata.datalen = substring;
addr.rrdata.rrtype = qtype; addr.rrdata.rrtype = qtype;
if (!have_soa)
flags |= F_NO_RR; /* Marks no SOA found. */
}
newc = cache_insert(name, &addr, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0)); newc = cache_insert(name, &addr, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
if (newc && cpp) if (newc && cpp)
{ {
@@ -1001,15 +1081,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
} }
} }
if (header->hb3 & HB3_TC) /* Don't cache replies from non-recursive nameservers, since we may get a
log_query(F_UPSTREAM, NULL, NULL, "truncated", 0);
/* Don't put stuff from a truncated packet into the cache.
Don't cache replies from non-recursive nameservers, since we may get a
reply containing a CNAME but not its target, even though the target reply containing a CNAME but not its target, even though the target
does exist. */ does exist. */
if (!(header->hb3 & HB3_TC) && if (!(header->hb4 & HB4_CD) &&
!(header->hb4 & HB4_CD) &&
(header->hb4 & HB4_RA) && (header->hb4 & HB4_RA) &&
!no_cache_dnssec) !no_cache_dnssec)
cache_end_insert(); cache_end_insert();
@@ -1242,8 +1317,7 @@ static int check_bad_address(struct dns_header *header, size_t qlen, struct bogu
GETSHORT(qtype, p); GETSHORT(qtype, p);
GETSHORT(qclass, p); GETSHORT(qclass, p);
GETLONG(ttl, p); GETLONG(ttl, p);
GETSHORT(rdlen, p); GETSHORT(rdlen, p)
if (ttlp) if (ttlp)
*ttlp = ttl; *ttlp = ttl;
@@ -1296,8 +1370,9 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
/* Found a bogus address. Insert that info here, since there no SOA record /* Found a bogus address. Insert that info here, since there no SOA record
to get the ttl from in the normal processing */ to get the ttl from in the normal processing */
cache_start_insert(); cache_start_insert();
cache_insert(name, NULL, C_IN, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN); cache_insert(name, NULL, C_IN, now, ttl, F_FORWARD | F_NEG | F_NXDOMAIN);
cache_end_insert(); cache_end_insert();
log_query(F_CONFIG | F_FORWARD | F_NEG | F_NXDOMAIN, name, NULL, NULL, 0);
return 1; return 1;
} }
@@ -1500,12 +1575,13 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
union all_addr addr; union all_addr addr;
int nameoffset; int nameoffset;
unsigned short flag; unsigned short flag;
int q, ans, anscount = 0, addncount = 0; int ans, anscount = 0, nscount = 0, addncount = 0;
struct crec *crecp; struct crec *crecp, *soa_lookup = NULL;
int nxdomain = 0, notimp = 0, auth = 1, trunc = 0, sec_data = 1; int nxdomain = 0, notimp = 0, auth = 1, trunc = 0, sec_data = 1;
struct mx_srv_record *rec; struct mx_srv_record *rec;
size_t len; size_t len;
int rd_bit = (header->hb3 & HB3_RD); int rd_bit = (header->hb3 & HB3_RD);
int count = 255; /* catch loops */
if (stale) if (stale)
*stale = 0; *stale = 0;
@@ -1514,7 +1590,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
*filtered = 0; *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->qdcount) != 1 ||
ntohs(header->ancount) != 0 ||
ntohs(header->nscount) != 0 || ntohs(header->nscount) != 0 ||
ntohs(header->qdcount) == 0 || ntohs(header->qdcount) == 0 ||
OPCODE(header) != QUERY ) OPCODE(header) != QUERY )
@@ -1534,10 +1611,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
/* now process each question, answers go in RRs after the question */ /* now process each question, answers go in RRs after the question */
p = (unsigned char *)(header+1); p = (unsigned char *)(header+1);
for (q = ntohs(header->qdcount); q != 0; q--)
{
int count = 255; /* catch loops */
/* save pointer to name for copying into answers */ /* save pointer to name for copying into answers */
nameoffset = p - (unsigned char *)header; nameoffset = p - (unsigned char *)header;
@@ -1564,6 +1637,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
stale_flag = F_STALE; stale_flag = F_STALE;
} }
if (crecp->flags & F_NEG)
soa_lookup = crecp;
if (crecp->flags & F_NXDOMAIN) if (crecp->flags & F_NXDOMAIN)
{ {
if (qtype == T_CNAME) if (qtype == T_CNAME)
@@ -1768,6 +1844,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (crecp->flags & F_NXDOMAIN) if (crecp->flags & F_NXDOMAIN)
nxdomain = 1; nxdomain = 1;
log_query(stale_flag | (crecp->flags & ~F_FORWARD), name, &addr, NULL, 0); log_query(stale_flag | (crecp->flags & ~F_FORWARD), name, &addr, NULL, 0);
soa_lookup = crecp;
} }
else else
{ {
@@ -1872,13 +1949,13 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
continue; continue;
} }
if ((crecp = cache_find_by_name(NULL, name, now, flag | F_NXDOMAIN))) if ((crecp = cache_find_by_name(NULL, name, now, flag)))
{ {
int localise = 0; int localise = 0;
/* See if a putative address is on the network from which we received /* See if a putative address is on the network from which we received
the query, is so we'll filter other answers. */ the query, is so we'll filter other answers. */
if (local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && flag == F_IPV4) if (!(crecp->flags & F_NEG) && local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && flag == F_IPV4)
{ {
struct crec *save = crecp; struct crec *save = crecp;
do { do {
@@ -1918,7 +1995,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (!(crecp->flags & (F_HOSTS | F_DHCP))) if (!(crecp->flags & (F_HOSTS | F_DHCP)))
auth = 0; auth = 0;
if (rr_on_list(daemon->filter_rr, qtype) && if (qtype != T_ANY && rr_on_list(daemon->filter_rr, qtype) &&
!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG | F_NEG))) !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG | F_NEG)))
{ {
/* We have a cached answer but we're filtering it. */ /* We have a cached answer but we're filtering it. */
@@ -1931,14 +2008,18 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
*filtered = 1; *filtered = 1;
} }
else if (crecp->flags & F_NEG) else if (crecp->flags & F_NEG)
{
if (qtype != T_ANY)
{ {
ans = 1; ans = 1;
auth = 0; auth = 0;
soa_lookup = crecp;
if (crecp->flags & F_NXDOMAIN) if (crecp->flags & F_NXDOMAIN)
nxdomain = 1; nxdomain = 1;
log_query(stale_flag | crecp->flags, name, NULL, NULL, 0); log_query(stale_flag | crecp->flags, name, NULL, NULL, 0);
} }
}
else else
{ {
/* If we are returning local answers depending on network, /* If we are returning local answers depending on network,
@@ -2102,12 +2183,15 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (flags & F_NXDOMAIN) if (flags & F_NXDOMAIN)
nxdomain = 1; nxdomain = 1;
else if (rr_on_list(daemon->filter_rr, qtype)) else if (qtype != T_ANY && rr_on_list(daemon->filter_rr, qtype))
flags |= F_NEG | F_CONFIG; flags |= F_NEG | F_CONFIG;
auth = 0; auth = 0;
ans = 1; ans = 1;
if (flags & F_NEG)
soa_lookup = crecp;
if (!(flags & F_NEG)) if (!(flags & F_NEG))
{ {
if (flags & F_KEYTAG) if (flags & F_KEYTAG)
@@ -2144,7 +2228,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
} }
if (!ans && rr_on_list(daemon->filter_rr, qtype)) if (qtype != T_ANY && !ans && rr_on_list(daemon->filter_rr, qtype))
{ {
/* We don't have a cached answer and when we get an answer from upstream we're going to /* 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 filter it anyway. If we have a cached answer for the domain for another RRtype then
@@ -2168,6 +2252,40 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (!ans) if (!ans)
return 0; /* failed to answer a question */ return 0; /* failed to answer a question */
/* We found a negative record. See if we have an SOA record to
return in the AUTH section.
For FORWARD NEG records, the addr.rrdata.datalen field of the othewise
empty addr is used to held an offset in to the name which yields the SOA
name. For REVERSE NEG records, the otherwise empty name field holds the
SOA name. If soa_name has zero length, then no SOA is known. soa_lookup
MUST be a neg record here.
If the F_NO_RR flag is set, there was no SOA record supplied with the RR. */
if (soa_lookup && !(soa_lookup->flags & F_NO_RR))
{
char *soa_name = soa_lookup->flags & F_REVERSE ? cache_get_name(soa_lookup) : name + soa_lookup->addr.rrdata.datalen;
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, soa_name, now, F_RR)))
if (crecp->addr.rrblock.rrtype == T_SOA)
{
char *rrdata;
if (!(crecp->flags & F_NEG) &&
(rrdata = blockdata_retrieve(crecp->addr.rrblock.rrdata, crecp->addr.rrblock.datalen, NULL)) &&
add_resource_record(header, limit, &trunc, 0, &ansp,
crec_ttl(crecp, now), NULL, T_SOA, C_IN, "t",
soa_name, crecp->addr.rrblock.datalen, rrdata))
{
nscount++;
if (!(crecp->flags & F_DNSSECOK))
sec_data = 0;
}
break;
}
} }
/* create an additional data section, for stuff in SRV and MX record replies. */ /* create an additional data section, for stuff in SRV and MX record replies. */
@@ -2191,7 +2309,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (add_resource_record(header, limit, NULL, rec->offset, &ansp, if (add_resource_record(header, limit, NULL, rec->offset, &ansp,
crec_ttl(crecp, now), NULL, type, C_IN, crec_ttl(crecp, now), NULL, type, C_IN,
crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr)) crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr))
{
addncount++; addncount++;
if (!(crecp->flags & F_DNSSECOK))
sec_data = 0;
}
} }
} }
@@ -2216,7 +2338,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
else else
SET_RCODE(header, NOERROR); /* no error */ SET_RCODE(header, NOERROR); /* no error */
header->ancount = htons(anscount); header->ancount = htons(anscount);
header->nscount = htons(0); header->nscount = htons(nscount);
header->arcount = htons(addncount); header->arcount = htons(addncount);
len = ansp - (unsigned char *)header; len = ansp - (unsigned char *)header;
+8
View File
@@ -213,6 +213,14 @@ size_t rrfilter(struct dns_header *header, size_t *plen, int mode)
if (i < ntohs(header->ancount) && type == qtype && class == qclass) if (i < ntohs(header->ancount) && type == qtype && class == qclass)
continue; continue;
} }
else if (qtype == T_ANY && rr_on_list(daemon->filter_rr, T_ANY))
{
/* Filter replies to ANY queries in the spirit of
RFC RFC 8482 para 4.3 */
if (class != C_IN ||
type == T_A || type == T_AAAA || type == T_MX || type == T_CNAME)
continue;
}
else else
{ {
/* Only looking at answer section now. */ /* Only looking at answer section now. */
+1 -1
View File
@@ -119,7 +119,7 @@ int rr_on_list(struct rrlist *list, unsigned short rr)
{ {
while (list) while (list)
{ {
if (list->rr == rr || list->rr == T_ANY) if (list->rr == rr)
return 1; return 1;
list = list->next; list = list->next;