mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 18:28:25 +00:00
Negative caching for DS records.
This commit is contained in:
34
src/cache.c
34
src/cache.c
@@ -564,7 +564,14 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
|
|||||||
*cache_get_name(new) = 0;
|
*cache_get_name(new) = 0;
|
||||||
|
|
||||||
if (addr)
|
if (addr)
|
||||||
new->addr.addr = *addr;
|
{
|
||||||
|
#ifdef HAVE_DNSSEC
|
||||||
|
if (flags & (F_DS | F_DNSKEY))
|
||||||
|
new->uid = addr->addr.dnssec.class;
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
new->addr.addr = *addr;
|
||||||
|
}
|
||||||
|
|
||||||
new->ttd = now + (time_t)ttl;
|
new->ttd = now + (time_t)ttl;
|
||||||
new->next = new_chain;
|
new->next = new_chain;
|
||||||
@@ -1304,25 +1311,16 @@ void dump_cache(time_t now)
|
|||||||
else if (cache->flags & F_DS)
|
else if (cache->flags & F_DS)
|
||||||
{
|
{
|
||||||
if (cache->flags & F_DNSKEY)
|
if (cache->flags & F_DNSKEY)
|
||||||
{
|
/* RRSIG */
|
||||||
/* RRSIG */
|
sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
|
||||||
a = daemon->addrbuff;
|
cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
|
||||||
sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
|
else if (!(cache->flags & F_NEG))
|
||||||
cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
|
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
|
||||||
}
|
cache->addr.ds.algo, cache->addr.ds.digest);
|
||||||
else
|
|
||||||
{
|
|
||||||
a = daemon->addrbuff;
|
|
||||||
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
|
|
||||||
cache->addr.ds.algo, cache->addr.ds.digest);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (cache->flags & F_DNSKEY)
|
else if (cache->flags & F_DNSKEY)
|
||||||
{
|
sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
|
||||||
a = daemon->addrbuff;
|
cache->addr.key.algo, cache->addr.key.flags);
|
||||||
sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
|
|
||||||
cache->addr.key.algo, cache->addr.key.flags);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
|
else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
|
||||||
{
|
{
|
||||||
|
|||||||
82
src/dnssec.c
82
src/dnssec.c
@@ -855,13 +855,17 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0)
|
if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0)
|
||||||
return STAT_BOGUS;
|
return STAT_BOGUS;
|
||||||
|
|
||||||
/* See if we have cached a DS record which validates this key */
|
/* See if we have cached a DS record which validates this key */
|
||||||
if (!(crecp = cache_find_by_name(NULL, name, now, F_DS)))
|
if (!(crecp = cache_find_by_name(NULL, name, now, F_DS)))
|
||||||
{
|
{
|
||||||
strcpy(keyname, name);
|
strcpy(keyname, name);
|
||||||
return STAT_NEED_DS;
|
return STAT_NEED_DS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If we've cached that DS provably doesn't exist, result must be INSECURE */
|
||||||
|
if (crecp->flags & F_NEG)
|
||||||
|
return STAT_INSECURE;
|
||||||
|
|
||||||
/* NOTE, we need to find ONE DNSKEY which matches the DS */
|
/* NOTE, we need to find ONE DNSKEY which matches the DS */
|
||||||
for (valid = 0, j = ntohs(header->ancount); j != 0 && !valid; j--)
|
for (valid = 0, j = ntohs(header->ancount); j != 0 && !valid; j--)
|
||||||
{
|
{
|
||||||
@@ -998,7 +1002,6 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
recp1->addr.key.algo = algo;
|
recp1->addr.key.algo = algo;
|
||||||
recp1->addr.key.keytag = keytag;
|
recp1->addr.key.keytag = keytag;
|
||||||
recp1->addr.key.flags = flags;
|
recp1->addr.key.flags = flags;
|
||||||
recp1->uid = class;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1024,7 +1027,6 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
blockdata_free(key);
|
blockdata_free(key);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
crecp->uid = class;
|
|
||||||
crecp->addr.sig.keydata = key;
|
crecp->addr.sig.keydata = key;
|
||||||
crecp->addr.sig.keylen = rdlen;
|
crecp->addr.sig.keylen = rdlen;
|
||||||
crecp->addr.sig.keytag = keytag;
|
crecp->addr.sig.keytag = keytag;
|
||||||
@@ -1054,7 +1056,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
/* The DNS packet is expected to contain the answer to a DS query
|
/* The DNS packet is expected to contain the answer to a DS query
|
||||||
Put all DSs in the answer which are valid into the cache.
|
Put all DSs in the answer which are valid into the cache.
|
||||||
return codes:
|
return codes:
|
||||||
STAT_INSECURE bad packet, no DS in reply.
|
STAT_INSECURE bad packet, no DS in reply, proven no DS in reply.
|
||||||
STAT_SECURE At least one valid DS found and in cache.
|
STAT_SECURE At least one valid DS found and in cache.
|
||||||
STAT_BOGUS At least one DS found, which fails validation.
|
STAT_BOGUS At least one DS found, which fails validation.
|
||||||
STAT_NEED_DNSKEY DNSKEY records to validate a DS not found, name in keyname
|
STAT_NEED_DNSKEY DNSKEY records to validate a DS not found, name in keyname
|
||||||
@@ -1063,10 +1065,10 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
|
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
|
||||||
{
|
{
|
||||||
unsigned char *p = (unsigned char *)(header+1);
|
unsigned char *p = (unsigned char *)(header+1);
|
||||||
int qtype, qclass, val;
|
int qtype, qclass, val, i;
|
||||||
|
|
||||||
if (ntohs(header->qdcount) != 1 ||
|
if (ntohs(header->qdcount) != 1 ||
|
||||||
!extract_name(header, plen, &p, name, 1, 4))
|
!(p = skip_name(p, header, plen, 4)))
|
||||||
return STAT_INSECURE;
|
return STAT_INSECURE;
|
||||||
|
|
||||||
GETSHORT(qtype, p);
|
GETSHORT(qtype, p);
|
||||||
@@ -1077,17 +1079,65 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
|||||||
else
|
else
|
||||||
val = dnssec_validate_reply(now, header, plen, name, keyname, NULL);
|
val = dnssec_validate_reply(now, header, plen, name, keyname, NULL);
|
||||||
|
|
||||||
|
p = (unsigned char *)(header+1);
|
||||||
|
extract_name(header, plen, &p, name, 1, 4);
|
||||||
|
p += 4; /* qtype, qclass */
|
||||||
|
|
||||||
if (val == STAT_BOGUS)
|
if (val == STAT_BOGUS)
|
||||||
{
|
log_query(F_UPSTREAM, name, NULL, "BOGUS DS");
|
||||||
p = (unsigned char *)(header+1);
|
|
||||||
extract_name(header, plen, &p, name, 1, 4);
|
|
||||||
log_query(F_UPSTREAM, name, NULL, "BOGUS DS");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* proved that no DS exists, can't validate */
|
/* proved that no DS exists, cache neg answer, can't validate */
|
||||||
if (val == STAT_SECURE && ntohs(header->ancount) == 0)
|
if (val == STAT_SECURE && ntohs(header->ancount) == 0)
|
||||||
return STAT_INSECURE;
|
{
|
||||||
|
int rdlen, rc;
|
||||||
|
unsigned long ttl, minttl = ULONG_MAX;
|
||||||
|
struct all_addr a;
|
||||||
|
|
||||||
|
for (i = ntohs(header->nscount); i != 0; i--)
|
||||||
|
{
|
||||||
|
if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
|
||||||
|
return STAT_INSECURE;
|
||||||
|
|
||||||
|
GETSHORT(qtype, p);
|
||||||
|
GETSHORT(qclass, p);
|
||||||
|
GETLONG(ttl, p);
|
||||||
|
GETSHORT(rdlen, p);
|
||||||
|
|
||||||
|
if (!CHECK_LEN(header, p, plen, rdlen) || rdlen < 4)
|
||||||
|
return STAT_INSECURE; /* bad packet */
|
||||||
|
|
||||||
|
if (qclass != class || qtype != T_SOA || rc ==2)
|
||||||
|
{
|
||||||
|
p += rdlen;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ttl < minttl)
|
||||||
|
minttl = ttl;
|
||||||
|
|
||||||
|
/* MNAME */
|
||||||
|
if (!(p = skip_name(p, header, plen, 0)))
|
||||||
|
return STAT_INSECURE;
|
||||||
|
/* RNAME */
|
||||||
|
if (!(p = skip_name(p, header, plen, 20)))
|
||||||
|
return STAT_INSECURE;
|
||||||
|
p += 16; /* SERIAL REFRESH RETRY EXPIRE */
|
||||||
|
|
||||||
|
GETLONG(ttl, p); /* minTTL */
|
||||||
|
if (ttl < minttl)
|
||||||
|
minttl = ttl;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache_start_insert();
|
||||||
|
|
||||||
|
a.addr.dnssec.class = class;
|
||||||
|
cache_insert(name, &a, now, ttl, F_FORWARD | F_DS | F_DNSSECOK | F_NEG | (RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0));
|
||||||
|
|
||||||
|
cache_end_insert();
|
||||||
|
|
||||||
|
return STAT_INSECURE;
|
||||||
|
}
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1707,7 +1757,6 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
crecp->addr.ds.keydata = key;
|
crecp->addr.ds.keydata = key;
|
||||||
crecp->addr.ds.algo = algo;
|
crecp->addr.ds.algo = algo;
|
||||||
crecp->addr.ds.keytag = keytag;
|
crecp->addr.ds.keytag = keytag;
|
||||||
crecp->uid = class2;
|
|
||||||
crecp->addr.ds.keylen = rdlen2 - 4;
|
crecp->addr.ds.keylen = rdlen2 - 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1737,7 +1786,6 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
blockdata_free(key);
|
blockdata_free(key);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
crecp->uid = class1;
|
|
||||||
crecp->addr.sig.keydata = key;
|
crecp->addr.sig.keydata = key;
|
||||||
crecp->addr.sig.keylen = rdlen2;
|
crecp->addr.sig.keylen = rdlen2;
|
||||||
crecp->addr.sig.keytag = keytag;
|
crecp->addr.sig.keytag = keytag;
|
||||||
|
|||||||
@@ -1580,19 +1580,29 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
while ((crecp = cache_find_by_name(crecp, name, now, F_DS)))
|
while ((crecp = cache_find_by_name(crecp, name, now, F_DS)))
|
||||||
if (crecp->uid == qclass)
|
if (crecp->uid == qclass)
|
||||||
{
|
{
|
||||||
gotone = 1;
|
gotone = 1;
|
||||||
if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL)))
|
if (!dryrun)
|
||||||
{
|
{
|
||||||
struct all_addr a;
|
if (crecp->flags & F_NEG)
|
||||||
a.addr.keytag = crecp->addr.ds.keytag;
|
{
|
||||||
log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DS keytag %u");
|
if (crecp->flags & F_NXDOMAIN)
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
nxdomain = 1;
|
||||||
crec_ttl(crecp, now), &nameoffset,
|
log_query(F_UPSTREAM, name, NULL, "secure no DS");
|
||||||
T_DS, qclass, "sbbt",
|
}
|
||||||
crecp->addr.ds.keytag, crecp->addr.ds.algo, crecp->addr.ds.digest, crecp->addr.ds.keylen, keydata))
|
else if ((keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL)))
|
||||||
anscount++;
|
{
|
||||||
|
struct all_addr a;
|
||||||
}
|
a.addr.keytag = crecp->addr.ds.keytag;
|
||||||
|
log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DS keytag %u");
|
||||||
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
|
crec_ttl(crecp, now), &nameoffset,
|
||||||
|
T_DS, qclass, "sbbt",
|
||||||
|
crecp->addr.ds.keytag, crecp->addr.ds.algo,
|
||||||
|
crecp->addr.ds.digest, crecp->addr.ds.keylen, keydata))
|
||||||
|
anscount++;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else /* DNSKEY */
|
else /* DNSKEY */
|
||||||
|
|||||||
Reference in New Issue
Block a user