From 1ce1c6beae9f683bec54cba4c0d375f85b209b95 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Sun, 5 Sep 2021 18:47:45 +0100 Subject: [PATCH 1/2] Caching cleanup. Use cached NXDOMAIN to answer queries of any type. --- src/rfc1035.c | 139 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 85 insertions(+), 54 deletions(-) diff --git a/src/rfc1035.c b/src/rfc1035.c index 6fc4f26..6e195cc 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -787,7 +787,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t if (!extract_name(header, qlen, &p1, name, 1, 0)) return 0; - goto cname_loop1; + if (qtype != T_CNAME) + goto cname_loop1; + + found = 1; } else if (aqtype != qtype) { @@ -877,8 +880,19 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t return 0; /* bad packet */ } - if (!found && !option_bool(OPT_NO_NEG)) + if (!found && (qtype != T_ANY || (flags & F_NXDOMAIN))) { + if (flags & F_NXDOMAIN) + { + flags &= ~(F_IPV4 | F_IPV6 | F_SRV); + + /* Can store NXDOMAIN reply to CNAME or ANY query. */ + if (qtype == T_CNAME || qtype == T_ANY) + insert = 1; + } + + log_query(F_UPSTREAM | F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0), name, NULL, NULL); + if (!searched_soa) { searched_soa = 1; @@ -887,22 +901,17 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t /* If there's no SOA to get the TTL from, but there is a CNAME pointing at this, inherit its TTL */ - if (ttl || cpp) + if (insert && !option_bool(OPT_NO_NEG) && (ttl || cpp)) { if (ttl == 0) ttl = cttl; - log_query(F_UPSTREAM | F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0), name, NULL, NULL); - - if (insert) + newc = cache_insert(name, NULL, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0)); + if (newc && cpp) { - newc = cache_insert(name, NULL, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0)); - if (newc && cpp) - { - next_uid(newc); - cpp->addr.cname.target.cache = newc; - cpp->addr.cname.uid = newc->uid; - } + next_uid(newc); + cpp->addr.cname.target.cache = newc; + cpp->addr.cname.uid = newc->uid; } } } @@ -1440,36 +1449,55 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ans = 0; /* have we answered this question */ - while (--count != 0 && (crecp = cache_find_by_name(NULL, name, now, F_CNAME))) - { - char *cname_target = cache_get_cname_target(crecp); + if (qclass == C_IN) + while (--count != 0 && (crecp = cache_find_by_name(NULL, name, now, F_CNAME | F_NXDOMAIN))) + { + char *cname_target; - /* If the client asked for DNSSEC don't use cached data. */ - if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || - (rd_bit && (!do_bit || cache_validated(crecp)))) - { - if (crecp->flags & F_CONFIG || qtype == T_CNAME) - ans = 1; + if (crecp->flags & F_NXDOMAIN) + { + if (qtype == T_CNAME) + { + if (!dryrun) + log_query(crecp->flags, name, NULL, record_source(crecp->uid)); + auth = 0; + nxdomain = 1; + ans = 1; + } + break; + } - if (!(crecp->flags & F_DNSSECOK)) - sec_data = 0; - - if (!dryrun) - { - log_query(crecp->flags, name, NULL, record_source(crecp->uid)); - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, - crec_ttl(crecp, now), &nameoffset, - T_CNAME, C_IN, "d", cname_target)) - anscount++; - } - - } - else - return 0; /* give up if any cached CNAME in chain can't be used for DNSSEC reasons. */ - - strcpy(name, cname_target); - } - + cname_target = cache_get_cname_target(crecp); + + /* If the client asked for DNSSEC don't use cached data. */ + if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || + (rd_bit && (!do_bit || cache_validated(crecp)))) + { + if (crecp->flags & F_CONFIG || qtype == T_CNAME) + ans = 1; + + if (!(crecp->flags & F_DNSSECOK)) + sec_data = 0; + + if (!dryrun) + { + log_query(crecp->flags, name, NULL, record_source(crecp->uid)); + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, + crec_ttl(crecp, now), &nameoffset, + T_CNAME, C_IN, "d", cname_target)) + anscount++; + } + + } + else + return 0; /* give up if any cached CNAME in chain can't be used for DNSSEC reasons. */ + + if (qtype == T_CNAME) + break; + + strcpy(name, cname_target); + } + if (qtype == T_TXT || qtype == T_ANY) { struct txt_record *t; @@ -1612,7 +1640,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, } } - else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa))) + else if (is_arpa && (crecp = cache_find_by_addr(NULL, &addr, now, is_arpa))) { /* Don't use cache when DNSSEC data required, unless we know that the zone is unsigned, which implies that we're doing @@ -1751,7 +1779,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, continue; } - if ((crecp = cache_find_by_name(NULL, name, now, flag | (dryrun ? F_NO_RR : 0)))) + if ((crecp = cache_find_by_name(NULL, name, now, flag | F_NXDOMAIN | (dryrun ? F_NO_RR : 0)))) { int localise = 0; @@ -1915,16 +1943,20 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!found) { - if ((crecp = cache_find_by_name(NULL, name, now, F_SRV | (dryrun ? F_NO_RR : 0))) && + if ((crecp = cache_find_by_name(NULL, name, now, F_SRV | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))) && rd_bit && (!do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))) - { - if (!(crecp->flags & F_DNSSECOK)) - sec_data = 0; - - auth = 0; - found = ans = 1; - - do { + do + { + /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases, except for NXDOMAIN */ + if (qtype == T_ANY && !(crecp->flags & (F_NXDOMAIN))) + break; + + if (!(crecp->flags & F_DNSSECOK)) + sec_data = 0; + + auth = 0; + found = ans = 1; + if (crecp->flags & F_NEG) { if (crecp->flags & F_NXDOMAIN) @@ -1945,8 +1977,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, } } while ((crecp = cache_find_by_name(crecp, name, now, F_SRV))); } - } - + if (!found && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_')))) { ans = 1; From c4523639d54973d92978db4fd9b156530470fe96 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Wed, 8 Sep 2021 21:19:15 +0100 Subject: [PATCH 2/2] Treat ANY queries the same as CNAME queries WRT to DNSSEC on CNAME targets. --- src/dnssec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index 94ebb6f..153cac4 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -1893,7 +1893,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch /* Find all the targets we're looking for answers to. The zeroth array element is for the query, subsequent ones - for CNAME targets, unless the query is for a CNAME. */ + for CNAME targets, unless the query is for a CNAME or ANY. */ if (!expand_workspace(&targets, &target_sz, 0)) return STAT_BOGUS; @@ -1912,7 +1912,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch if (qtype == T_RRSIG) return STAT_INSECURE; - if (qtype != T_CNAME) + if (qtype != T_CNAME && qtype != T_ANY) for (j = ntohs(header->ancount); j != 0; j--) { if (!(p1 = skip_name(p1, header, plen, 10)))