From a827127c77f2f005ef893abf633be956f34fcafb Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Fri, 2 Feb 2024 23:07:57 +0000 Subject: [PATCH] Handle caching SOA for negative PTR queries. Also deal with the fact that a root SOA is a thing. --- src/cache.c | 98 +++++++++++++++++++++++++-------------------------- src/rfc1035.c | 72 ++++++++++++++++++++++++++----------- 2 files changed, 100 insertions(+), 70 deletions(-) diff --git a/src/cache.c b/src/cache.c index e00c922..69afeca 100644 --- a/src/cache.c +++ b/src/cache.c @@ -802,32 +802,28 @@ void cache_end_insert(void) 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 *)&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); + + if (flags & F_RR) { - read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), 0); - - if (flags & F_RR) - { - /* A negative RR entry is possible and has no data, obviously. */ - if (!(flags & F_NEG) && (flags & F_KEYTAG)) - blockdata_write(new_chain->addr.rrblock.rrdata, new_chain->addr.rrblock.datalen, daemon->pipe_to_parent); - } -#ifdef HAVE_DNSSEC - if (flags & F_DNSKEY) - { - read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), 0); - blockdata_write(new_chain->addr.key.keydata, new_chain->addr.key.keylen, daemon->pipe_to_parent); - } - else if (flags & F_DS) - { - read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), 0); - /* A negative DS entry is possible and has no data, obviously. */ - if (!(flags & F_NEG)) - blockdata_write(new_chain->addr.ds.keydata, new_chain->addr.ds.keylen, daemon->pipe_to_parent); - } -#endif + /* A negative RR entry is possible and has no data, obviously. */ + if (!(flags & F_NEG) && (flags & F_KEYTAG)) + blockdata_write(new_chain->addr.rrblock.rrdata, new_chain->addr.rrblock.datalen, daemon->pipe_to_parent); } +#ifdef HAVE_DNSSEC + if (flags & F_DNSKEY) + { + read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), 0); + blockdata_write(new_chain->addr.key.keydata, new_chain->addr.key.keylen, daemon->pipe_to_parent); + } + else if (flags & F_DS) + { + read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), 0); + /* A negative DS entry is possible and has no data, obviously. */ + if (!(flags & F_NEG)) + blockdata_write(new_chain->addr.ds.keydata, new_chain->addr.ds.keylen, daemon->pipe_to_parent); + } +#endif } } @@ -871,7 +867,8 @@ int cache_recv_insert(time_t now, int fd) if (!read_write(fd, (unsigned char *)daemon->namebuff, m, 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; daemon->namebuff[m] = 0; @@ -902,30 +899,23 @@ int cache_recv_insert(time_t now, int fd) { 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) - && !(addr.rrblock.rrdata = blockdata_read(fd, addr.rrblock.datalen))) - return 0; + if ((flags & F_RR) && !(flags & F_NEG) && (flags & F_KEYTAG) + && !(addr.rrblock.rrdata = blockdata_read(fd, addr.rrblock.datalen))) + return 0; #ifdef HAVE_DNSSEC - if (flags & F_DNSKEY) - { - if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1) || - !(addr.key.keydata = blockdata_read(fd, addr.key.keylen))) - return 0; - } - else if (flags & F_DS) - { - if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1) || - (!(flags & F_NEG) && !(addr.key.keydata = blockdata_read(fd, addr.key.keylen)))) - return 0; - } -#endif + if (flags & F_DNSKEY) + { + if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1) || + !(addr.key.keydata = blockdata_read(fd, addr.key.keylen))) + return 0; } - + else if (flags & F_DS) + { + if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1) || + (!(flags & F_NEG) && !(addr.key.keydata = blockdata_read(fd, addr.key.keylen)))) + return 0; + } +#endif 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; *a = 0; - if (strlen(n) == 0 && !(cache->flags & F_REVERSE)) - n = ""; + + if (cache->flags & F_REVERSE) + { + if ((cache->flags & F_NEG)) + n = ""; + } + else + { + if (strlen(n) == 0) + n = ""; + } + p += sprintf(p, "%-30.30s ", sanitise(n)); if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache)) a = sanitise(cache_get_cname_target(cache)); diff --git a/src/rfc1035.c b/src/rfc1035.c index 0d0cdb8..a83d9c1 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -454,7 +454,7 @@ int do_doctor(struct dns_header *header, size_t qlen) 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, int no_cache, time_t now) +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, *psave; int qtype, qclass, rdlen; @@ -472,6 +472,9 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub if (substring) *substring = name_len; + + if (ttlp) + *ttlp = daemon->neg_ttl; for (i = 0; i < ntohs(header->nscount); i++) { @@ -571,7 +574,10 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub if (substring) *substring = prefix; - return minttl; + if (ttlp) + *ttlp = minttl; + + return 1; } } @@ -581,7 +587,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub return 0; /* bad packet */ } - return daemon->neg_ttl; + return 0; } /* Print TXT reply to log */ @@ -747,14 +753,16 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t if (!found && !option_bool(OPT_NO_NEG)) { - /* don't cache SOAs for negative PTR records */ - ttl = find_soa(header, qlen, name, NULL, 1, now); + /* 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); flags |= F_NEG | (secure ? F_DNSSECOK : 0); if (name_encoding && ttl) { 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); @@ -1038,7 +1046,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t if (!found && (qtype != T_ANY || (flags & F_NXDOMAIN))) { - int substring; + int substring, have_soa; if (flags & F_NXDOMAIN) { @@ -1050,15 +1058,23 @@ 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); - /* If there's no SOA to get the TTL from, but there is a CNAME - pointing at this, inherit its TTL */ - if (insert && !option_bool(OPT_NO_NEG) && ((ttl = find_soa(header, qlen, name, &substring, no_cache_dnssec, now)) || cpp)) + if (insert && !option_bool(OPT_NO_NEG)) { - addr.rrdata.datalen = substring; - addr.rrdata.rrtype = qtype; + int have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now); - if (ttl == 0) - ttl = cttl; + /* 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 (!ttl) + ttl = cttl; + + addr.rrdata.datalen = substring; + 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)); if (newc && cpp) @@ -1627,6 +1643,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, stale_flag = F_STALE; } + if (crecp->flags & F_NEG) + soa_lookup = crecp; + if (crecp->flags & F_NXDOMAIN) { if (qtype == T_CNAME) @@ -1831,6 +1850,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (crecp->flags & F_NXDOMAIN) nxdomain = 1; log_query(stale_flag | (crecp->flags & ~F_FORWARD), name, &addr, NULL, 0); + soa_lookup = crecp; } else { @@ -2236,21 +2256,31 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!ans) return 0; /* failed to answer a question */ - if (soa_lookup) + /* 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)) { - /* We found a negative record. See if we have an SOA record to - return in the AUTH section. */ - char *rrdata; - int substring = soa_lookup->addr.rrdata.datalen; + 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, name + substring, now, F_RR))) + 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", - name + substring, crecp->addr.rrblock.datalen, rrdata)) + soa_name, crecp->addr.rrblock.datalen, rrdata)) { nscount++;