]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: cache - cache what we can of negative redirect chains 1279/head
authorTom Gundersen <teg@jklm.no>
Thu, 3 Sep 2015 23:58:20 +0000 (01:58 +0200)
committerTom Gundersen <teg@jklm.no>
Wed, 16 Sep 2015 15:03:18 +0000 (17:03 +0200)
When a NXDATA or a NODATA response is received for an alias it may
include CNAME records from the redirect chain. We should cache the
response for each of these names to avoid needless roundtrips in
the future.

It is not sufficient to do the negative caching only for the
canonical name, as the included redirection chain is not guaranteed
to be complete. In fact, only the final CNAME record from the chain
is guaranteed to be included.

We take care not to cache entries that redirects outside the current
zone, as the SOA will then not be valid.

src/resolve/resolved-dns-answer.c
src/resolve/resolved-dns-answer.h
src/resolve/resolved-dns-cache.c

index 13ad4ca6bd6c3ac858a1aff875ebc9303cd8a5b9..89b9b0e1ea034fcd90844b13f98197648d81ca57 100644 (file)
@@ -149,6 +149,19 @@ int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) {
         return 0;
 }
 
+int dns_answer_match_soa(DnsResourceKey *key, DnsResourceKey *soa) {
+        if (soa->class != DNS_CLASS_IN)
+                return 0;
+
+        if (soa->type != DNS_TYPE_SOA)
+                return 0;
+
+        if (!dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(soa)))
+                return 0;
+
+        return 1;
+}
+
 int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret) {
         unsigned i;
 
@@ -164,13 +177,7 @@ int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **r
 
         for (i = 0; i < a->n_rrs; i++) {
 
-                if (a->items[i].rr->key->class != DNS_CLASS_IN)
-                        continue;
-
-                if (a->items[i].rr->key->type != DNS_TYPE_SOA)
-                        continue;
-
-                if (dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(a->items[i].rr->key))) {
+                if (dns_answer_match_soa(key, a->items[i].rr->key)) {
                         *ret = a->items[i].rr;
                         return 1;
                 }
index 0757dd60d02075cc7e364134056f5beca182f5a9..044d73b19c3173fc9e718f712fe7a714f796d651 100644 (file)
@@ -49,6 +49,7 @@ DnsAnswer *dns_answer_unref(DnsAnswer *a);
 int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex);
 int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl);
 int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key);
+int dns_answer_match_soa(DnsResourceKey *key, DnsResourceKey *soa);
 int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret);
 
 DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b);
index 93b98f3727eb8173db5b80794bc0986110e662e1..ab13636bc182d3bb99ff2b3e038c7042932af08d 100644 (file)
@@ -474,6 +474,31 @@ int dns_cache_put(
         if (r == 0)
                 return 0;
 
+        /* Also, if the requested key is an alias, the negative response should
+           be cached for each name in the redirect chain. Any CNAME record in
+           the response is from the redirection chain, though only the final one
+           is guaranteed to be included. This means that we cannot verify the
+           chain and that we need to cache them all as it may be incomplete. */
+        for (i = 0; i < answer->n_rrs; i++) {
+                DnsResourceRecord *answer_rr = answer->items[i].rr;
+
+                if (answer_rr->key->type == DNS_TYPE_CNAME) {
+                        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *canonical_key = NULL;
+
+                        canonical_key = dns_resource_key_new_redirect(key, answer_rr);
+                        if (!canonical_key)
+                                goto fail;
+
+                        /* Let's not add negative cache entries for records outside the current zone. */
+                        if (!dns_answer_match_soa(canonical_key, soa->key))
+                                continue;
+
+                        r = dns_cache_put_negative(c, canonical_key, rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
+                        if (r < 0)
+                                goto fail;
+                }
+        }
+
         r = dns_cache_put_negative(c, key, rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
         if (r < 0)
                 goto fail;