summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2014-02-27 14:30:03 +0000
committerSimon Kelley <simon@thekelleys.org.uk>2014-02-27 14:30:03 +0000
commitb8eac1917737c51cb640fb370d3ed3b2177f1cc7 (patch)
treecd74c620a79cdda62145b1c162114b54fe46d676
parentb47b04c8460321d18bd76e641f7cf2ba626e32ad (diff)
downloaddnsmasq-b8eac1917737c51cb640fb370d3ed3b2177f1cc7.tar.gz
Negative caching for DS records.
-rw-r--r--src/cache.c34
-rw-r--r--src/dnssec.c82
-rw-r--r--src/rfc1035.c36
3 files changed, 104 insertions, 48 deletions
diff --git a/src/cache.c b/src/cache.c
index dd393c4..3ebb49d 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -564,7 +564,14 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
*cache_get_name(new) = 0;
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->next = new_chain;
@@ -1304,25 +1311,16 @@ void dump_cache(time_t now)
else if (cache->flags & F_DS)
{
if (cache->flags & F_DNSKEY)
- {
- /* RRSIG */
- a = daemon->addrbuff;
- sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
- cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
- }
- else
- {
- a = daemon->addrbuff;
- sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
- cache->addr.ds.algo, cache->addr.ds.digest);
- }
+ /* RRSIG */
+ sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
+ cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
+ else if (!(cache->flags & F_NEG))
+ sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
+ cache->addr.ds.algo, cache->addr.ds.digest);
}
else if (cache->flags & F_DNSKEY)
- {
- a = daemon->addrbuff;
- sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
- 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
else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
{
diff --git a/src/dnssec.c b/src/dnssec.c
index 8a99a26..1a1c0e4 100644
--- a/src/dnssec.c
+++ b/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)
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)))
{
strcpy(keyname, name);
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 */
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.keytag = keytag;
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);
else
{
- crecp->uid = class;
crecp->addr.sig.keydata = key;
crecp->addr.sig.keylen = rdlen;
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
Put all DSs in the answer which are valid into the cache.
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_BOGUS At least one DS found, which fails validation.
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)
{
unsigned char *p = (unsigned char *)(header+1);
- int qtype, qclass, val;
+ int qtype, qclass, val, i;
if (ntohs(header->qdcount) != 1 ||
- !extract_name(header, plen, &p, name, 1, 4))
+ !(p = skip_name(p, header, plen, 4)))
return STAT_INSECURE;
GETSHORT(qtype, p);
@@ -1077,17 +1079,65 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
else
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)
- {
- p = (unsigned char *)(header+1);
- extract_name(header, plen, &p, name, 1, 4);
- log_query(F_UPSTREAM, name, NULL, "BOGUS DS");
- }
+ 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)
- 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;
}
@@ -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.algo = algo;
crecp->addr.ds.keytag = keytag;
- crecp->uid = class2;
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);
else
{
- crecp->uid = class1;
crecp->addr.sig.keydata = key;
crecp->addr.sig.keylen = rdlen2;
crecp->addr.sig.keytag = keytag;
diff --git a/src/rfc1035.c b/src/rfc1035.c
index 5693ef9..77156e4 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -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)))
if (crecp->uid == qclass)
{
- gotone = 1;
- if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL)))
- {
- 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++;
-
- }
+ gotone = 1;
+ if (!dryrun)
+ {
+ if (crecp->flags & F_NEG)
+ {
+ if (crecp->flags & F_NXDOMAIN)
+ nxdomain = 1;
+ log_query(F_UPSTREAM, name, NULL, "secure no DS");
+ }
+ else if ((keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL)))
+ {
+ 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 */