mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 18:28:25 +00:00
Handle caching SOA for negative PTR queries.
Also deal with the fact that a root SOA is a thing.
This commit is contained in:
98
src/cache.c
98
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 = "<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));
|
||||
|
||||
@@ -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++;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user