resolved: never store NSEC/NSEC3 RRs from the upper zone of a zone cut in cache
authorLennart Poettering <lennart@poettering.net>
Thu, 21 Jan 2016 00:24:30 +0000 (01:24 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 25 Jan 2016 16:19:19 +0000 (17:19 +0100)
When using NSEC/NSEC3 RRs from the cache to derive existance of arbitrary RRs, we should not get confused by the fact
that NSEC/NSEC3 RRs exist twice at zone cuts: once in the parent zone, and once in the child zone. For most RR types we
should only consult the latter since that's where the beef is. However, for DS lookups we have to check the former.

This change makes sure we never cache NSEC/NSEC3 RRs from any parent zone of a zone-cut. It also makes sure that when
we look for a DS RR in the cache we never consider any cached NSEC RR, as those are now always from the child zone.

src/resolve/resolved-dns-cache.c

index fdb34d1..9267b67 100644 (file)
@@ -579,6 +579,28 @@ static void dns_cache_remove_previous(
         }
 }
 
+static bool rr_eligible(DnsResourceRecord *rr) {
+        assert(rr);
+
+        /* When we see an NSEC/NSEC3 RR, we'll only cache it if it is from the lower zone, not the upper zone, since
+         * that's where the interesting bits are (with exception of DS RRs). Of course, this way we cannot derive DS
+         * existence from any cached NSEC/NSEC3, but that should be fine. */
+
+        switch (rr->key->type) {
+
+        case DNS_TYPE_NSEC:
+                return !bitmap_isset(rr->nsec.types, DNS_TYPE_NS) ||
+                        bitmap_isset(rr->nsec.types, DNS_TYPE_SOA);
+
+        case DNS_TYPE_NSEC3:
+                return !bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) ||
+                        bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA);
+
+        default:
+                return true;
+        }
+}
+
 int dns_cache_put(
                 DnsCache *c,
                 DnsResourceKey *key,
@@ -635,6 +657,12 @@ int dns_cache_put(
                 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
                         continue;
 
+                r = rr_eligible(rr);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
+
                 r = dns_cache_put_positive(
                                 c,
                                 rr,
@@ -835,7 +863,10 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
                         have_non_authenticated = true;
         }
 
-        if (nsec && key->type != DNS_TYPE_NSEC) {
+        if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) {
+                /* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC RRs from
+                 * the lower-zone of a zone cut, but the DS RRs are on the upper zone. */
+
                 if (log_get_max_level() >= LOG_DEBUG) {
                         r = dns_resource_key_to_string(key, &key_str);
                         if (r < 0)