diff options
Diffstat (limited to 'src/resolve/resolved-dns-cache.c')
-rw-r--r-- | src/resolve/resolved-dns-cache.c | 194 |
1 files changed, 125 insertions, 69 deletions
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 81ea1cafcb..ef6b69c768 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -411,22 +411,23 @@ int dns_cache_put( int owner_family, const union in_addr_union *owner_address) { - unsigned i; + unsigned cache_keys, i; int r; assert(c); - assert(q); - /* First, delete all matching old RRs, so that we only keep - * complete by_key in place. */ - for (i = 0; i < q->n_keys; i++) - dns_cache_remove(c, q->keys[i]); + if (q) { + /* First, if we were passed a question, delete all matching old RRs, + * so that we only keep complete by_key in place. */ + for (i = 0; i < q->n_keys; i++) + dns_cache_remove(c, q->keys[i]); + } if (!answer) return 0; for (i = 0; i < answer->n_rrs; i++) - dns_cache_remove(c, answer->rrs[i]->key); + dns_cache_remove(c, answer->items[i].rr->key); /* We only care for positive replies and NXDOMAINs, on all * other replies we will simply flush the respective entries, @@ -435,19 +436,27 @@ int dns_cache_put( if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) return 0; + cache_keys = answer->n_rrs; + + if (q) + cache_keys += q->n_keys; + /* Make some space for our new entries */ - dns_cache_make_space(c, answer->n_rrs + q->n_keys); + dns_cache_make_space(c, cache_keys); if (timestamp <= 0) timestamp = now(clock_boottime_or_monotonic()); /* Second, add in positive entries for all contained RRs */ for (i = 0; i < MIN(max_rrs, answer->n_rrs); i++) { - r = dns_cache_put_positive(c, answer->rrs[i], timestamp, owner_family, owner_address); + r = dns_cache_put_positive(c, answer->items[i].rr, timestamp, owner_family, owner_address); if (r < 0) goto fail; } + if (!q) + return 0; + /* Third, add in negative entries for all keys with no RR */ for (i = 0; i < q->n_keys; i++) { DnsResourceRecord *soa = NULL; @@ -458,6 +467,10 @@ int dns_cache_put( if (r > 0) continue; + /* See https://tools.ietf.org/html/rfc2308, which + * say that a matching SOA record in the packet + * is used to to enable negative caching. */ + r = dns_answer_find_soa(answer, q->keys[i], &soa); if (r < 0) goto fail; @@ -475,80 +488,76 @@ fail: /* Adding all RRs failed. Let's clean up what we already * added, just in case */ - for (i = 0; i < q->n_keys; i++) - dns_cache_remove(c, q->keys[i]); + if (q) { + for (i = 0; i < q->n_keys; i++) + dns_cache_remove(c, q->keys[i]); + } + for (i = 0; i < answer->n_rrs; i++) - dns_cache_remove(c, answer->rrs[i]->key); + dns_cache_remove(c, answer->items[i].rr->key); return r; } -int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **ret) { +int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret) { _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - unsigned i, n = 0; + unsigned n = 0; int r; bool nxdomain = false; + _cleanup_free_ char *key_str = NULL; + DnsCacheItem *j, *first; assert(c); - assert(q); + assert(key); assert(rcode); assert(ret); - if (q->n_keys <= 0) { - *ret = NULL; - *rcode = 0; - return 0; - } + if (key->type == DNS_TYPE_ANY || + key->class == DNS_CLASS_ANY) { - for (i = 0; i < q->n_keys; i++) { - _cleanup_free_ char *key_str = NULL; - DnsCacheItem *j; + /* If we have ANY lookups we simply refresh */ - if (q->keys[i]->type == DNS_TYPE_ANY || - q->keys[i]->class == DNS_CLASS_ANY) { - /* If we have ANY lookups we simply refresh */ + r = dns_resource_key_to_string(key, &key_str); + if (r < 0) + return r; - r = dns_resource_key_to_string(q->keys[i], &key_str); - if (r < 0) - return r; + log_debug("Ignoring cache for ANY lookup: %s", key_str); - log_debug("Ignoring cache for ANY lookup: %s", key_str); + *ret = NULL; + *rcode = DNS_RCODE_SUCCESS; + return 0; + } - *ret = NULL; - *rcode = 0; - return 0; - } + first = hashmap_get(c->by_key, key); + if (!first) { + /* If one question cannot be answered we need to refresh */ - j = hashmap_get(c->by_key, q->keys[i]); - if (!j) { - /* If one question cannot be answered we need to refresh */ + r = dns_resource_key_to_string(key, &key_str); + if (r < 0) + return r; - r = dns_resource_key_to_string(q->keys[i], &key_str); - if (r < 0) - return r; + log_debug("Cache miss for %s", key_str); - log_debug("Cache miss for %s", key_str); + *ret = NULL; + *rcode = DNS_RCODE_SUCCESS; + return 0; + } - *ret = NULL; - *rcode = 0; - return 0; - } else { - r = dns_resource_key_to_string(j->key, &key_str); - if (r < 0) - return r; + LIST_FOREACH(by_key, j, first) { + if (j->rr) + n++; + else if (j->type == DNS_CACHE_NXDOMAIN) + nxdomain = true; + } - log_debug("%s cache hit for %s", - j->type == DNS_CACHE_POSITIVE ? "Positive" : - (j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN"), key_str); - } + r = dns_resource_key_to_string(key, &key_str); + if (r < 0) + return r; - LIST_FOREACH(by_key, j, j) { - if (j->rr) - n++; - else if (j->type == DNS_CACHE_NXDOMAIN) - nxdomain = true; - } - } + log_debug("%s cache hit for %s", + nxdomain ? "NXDOMAIN" : + n > 0 ? "Positive" : "NODATA", + key_str); if (n <= 0) { *ret = NULL; @@ -560,17 +569,13 @@ int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **ret) { if (!answer) return -ENOMEM; - for (i = 0; i < q->n_keys; i++) { - DnsCacheItem *j; + LIST_FOREACH(by_key, j, first) { + if (!j->rr) + continue; - j = hashmap_get(c->by_key, q->keys[i]); - LIST_FOREACH(by_key, j, j) { - if (j->rr) { - r = dns_answer_add(answer, j->rr); - if (r < 0) - return r; - } - } + r = dns_answer_add(answer, j->rr, 0); + if (r < 0) + return r; } *ret = answer; @@ -615,3 +620,54 @@ int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_ /* There's a conflict */ return 1; } + +void dns_cache_dump(DnsCache *cache, FILE *f) { + Iterator iterator; + DnsCacheItem *i; + int r; + + if (!cache) + return; + + if (!f) + f = stdout; + + HASHMAP_FOREACH(i, cache->by_key, iterator) { + DnsCacheItem *j; + + LIST_FOREACH(by_key, j, i) { + _cleanup_free_ char *t = NULL; + + fputc('\t', f); + + if (j->rr) { + r = dns_resource_record_to_string(j->rr, &t); + if (r < 0) { + log_oom(); + continue; + } + + fputs(t, f); + fputc('\n', f); + } else { + r = dns_resource_key_to_string(j->key, &t); + if (r < 0) { + log_oom(); + continue; + } + + fputs(t, f); + fputs(" -- ", f); + fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f); + fputc('\n', f); + } + } + } +} + +bool dns_cache_is_empty(DnsCache *cache) { + if (!cache) + return true; + + return hashmap_isempty(cache->by_key); +} |