Handle caching SOA for negative PTR queries.

Also deal with the fact that a root SOA is a thing.
This commit is contained in:
Simon Kelley
2024-02-02 23:07:57 +00:00
parent d4a6f3a93e
commit a827127c77
2 changed files with 100 additions and 70 deletions

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

View File

@@ -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 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. 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; unsigned char *p, *psave;
int qtype, qclass, rdlen; int qtype, qclass, rdlen;
@@ -473,6 +473,9 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
if (substring) if (substring)
*substring = name_len; *substring = name_len;
if (ttlp)
*ttlp = daemon->neg_ttl;
for (i = 0; i < ntohs(header->nscount); i++) for (i = 0; i < ntohs(header->nscount); i++)
{ {
if (!extract_name(header, qlen, &p, daemon->workspacename, 1, 0)) if (!extract_name(header, qlen, &p, daemon->workspacename, 1, 0))
@@ -571,7 +574,10 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
if (substring) if (substring)
*substring = prefix; *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 0; /* bad packet */
} }
return daemon->neg_ttl; return 0;
} }
/* Print TXT reply to log */ /* 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)) if (!found && !option_bool(OPT_NO_NEG))
{ {
/* don't cache SOAs for negative PTR records */ /* For reverse records, we use the name field to store the SOA name. */
ttl = find_soa(header, qlen, name, NULL, 1, now); int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now);
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);
@@ -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))) if (!found && (qtype != T_ANY || (flags & F_NXDOMAIN)))
{ {
int substring; int substring, have_soa;
if (flags & F_NXDOMAIN) 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); log_query(F_UPSTREAM | F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0), name, NULL, NULL, 0);
if (insert && !option_bool(OPT_NO_NEG))
{
int have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now);
/* 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 = find_soa(header, qlen, name, &substring, no_cache_dnssec, now)) || cpp)) if (ttl || cpp)
{ {
if (!ttl)
ttl = cttl;
addr.rrdata.datalen = substring; addr.rrdata.datalen = substring;
addr.rrdata.rrtype = qtype; addr.rrdata.rrtype = qtype;
if (ttl == 0) if (!have_soa)
ttl = cttl; 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)
@@ -1627,6 +1643,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)
@@ -1831,6 +1850,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
{ {
@@ -2236,21 +2256,31 @@ 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 */
if (soa_lookup)
{
/* We found a negative record. See if we have an SOA record to /* We found a negative record. See if we have an SOA record to
return in the AUTH section. */ return in the AUTH section.
char *rrdata;
int substring = soa_lookup->addr.rrdata.datalen; 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; 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) if (crecp->addr.rrblock.rrtype == T_SOA)
{ {
char *rrdata;
if (!(crecp->flags & F_NEG) && if (!(crecp->flags & F_NEG) &&
(rrdata = blockdata_retrieve(crecp->addr.rrblock.rrdata, crecp->addr.rrblock.datalen, NULL)) && (rrdata = blockdata_retrieve(crecp->addr.rrblock.rrdata, crecp->addr.rrblock.datalen, NULL)) &&
add_resource_record(header, limit, &trunc, 0, &ansp, add_resource_record(header, limit, &trunc, 0, &ansp,
crec_ttl(crecp, now), NULL, T_SOA, C_IN, "t", 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++; nscount++;