From 37da893166f39b5fb39b460baa41398847ff2c63 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 2 Dec 2015 23:59:19 +0100 Subject: resolved: synthesize NODATA cache results when we find matching NSEC RRs If we have a precisely matching NSEC RR for a name, we can use its type bit field to synthesize NODATA cache lookup results for all types not mentioned in there. This is useful for mDNS where NSEC RRs are used to indicate missing RRs for a specific type, but is beneficial in other cases too. To test this, consider these two lines: systemd-resolve-host -t NSEC nasa.gov systemd-resolve-host -t SRV nasa.gov The second line will not result in traffic as the first line already cached the NSEC field. --- src/resolve/resolved-dns-cache.c | 45 +++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 10 deletions(-) (limited to 'src/resolve/resolved-dns-cache.c') diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index d963ce6e00..3f34017789 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -521,8 +521,8 @@ fail: return r; } -static DnsCacheItem *dns_cache_get_by_key_follow_cname(DnsCache *c, DnsResourceKey *k) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *cname_key = NULL; +static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, DnsResourceKey *k) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *nsec_key = NULL, *cname_key = NULL; DnsCacheItem *i; const char *n; int r; @@ -534,20 +534,29 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname(DnsCache *c, DnsResourceK * much, after all this is just a cache */ i = hashmap_get(c->by_key, k); - if (i || k->type == DNS_TYPE_CNAME || k->type == DNS_TYPE_DNAME) + if (i || IN_SET(k->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME, DNS_TYPE_NSEC)) + return i; + + n = DNS_RESOURCE_KEY_NAME(k); + + /* Check if we have an NSEC record instead for the name. */ + nsec_key = dns_resource_key_new(k->class, DNS_TYPE_NSEC, n); + if (!nsec_key) + return NULL; + + i = hashmap_get(c->by_key, nsec_key); + if (i) return i; /* Check if we have a CNAME record instead */ cname_key = dns_resource_key_new_cname(k); if (!cname_key) return NULL; - i = hashmap_get(c->by_key, cname_key); if (i) return i; /* OK, let's look for cached DNAME records. */ - n = DNS_RESOURCE_KEY_NAME(k); for (;;) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *dname_key = NULL; char label[DNS_LABEL_MAX]; @@ -578,6 +587,7 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r int r; bool nxdomain = false; _cleanup_free_ char *key_str = NULL; + DnsResourceRecord *nsec = NULL; DnsCacheItem *j, *first; assert(c); @@ -601,7 +611,7 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r return 0; } - first = dns_cache_get_by_key_follow_cname(c, key); + first = dns_cache_get_by_key_follow_cname_dname_nsec(c, key); if (!first) { /* If one question cannot be answered we need to refresh */ @@ -617,9 +627,11 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r } LIST_FOREACH(by_key, j, first) { - if (j->rr) + if (j->rr) { + if (j->rr->key->type == DNS_TYPE_NSEC) + nsec = j->rr; n++; - else if (j->type == DNS_CACHE_NXDOMAIN) + } else if (j->type == DNS_CACHE_NXDOMAIN) nxdomain = true; } @@ -627,9 +639,22 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r if (r < 0) return r; + if (nsec && key->type != DNS_TYPE_NSEC) { + log_debug("NSEC NODATA cache hit for %s", key_str); + + /* We only found an NSEC record that matches our name. + * If it says the type doesn't exit report + * NODATA. Otherwise report a cache miss. */ + + *ret = NULL; + *rcode = DNS_RCODE_SUCCESS; + + return !bitmap_isset(nsec->nsec.types, key->type); + } + log_debug("%s cache hit for %s", - nxdomain ? "NXDOMAIN" : - n > 0 ? "Positive" : "NODATA", + n > 0 ? "Positive" : + nxdomain ? "NXDOMAIN" : "NODATA", key_str); if (n <= 0) { -- cgit v1.2.3