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,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);
read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), 0);
if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_RR))
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 = "<Root>";
if (cache->flags & F_REVERSE)
{
if ((cache->flags & F_NEG))
n = "";
}
else
{
if (strlen(n) == 0)
n = "<Root>";
}
p += sprintf(p, "%-30.30s ", sanitise(n));
if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(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
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;
@@ -473,6 +473,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++)
{
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)
*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++;