summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2015-12-02 23:59:19 +0100
committerLennart Poettering <lennart@poettering.net>2015-12-03 00:26:58 +0100
commit37da893166f39b5fb39b460baa41398847ff2c63 (patch)
tree3b8f202cdcae489cb5291953077ecf5d6260a7b1
parent8730bccfc59fe507bd3e0a3abcf411b497ac4f0e (diff)
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.
-rw-r--r--src/resolve/resolved-dns-cache.c45
1 files changed, 35 insertions, 10 deletions
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) {