chiark / gitweb /
resolved: properly process DNAME RRs
[elogind.git] / src / resolve / resolved-dns-cache.c
index 6ea5d49fc49e290f115aa2957c7bf921d2257e40..7359dfa271973272637ca15f050b6e2538df090c 100644 (file)
 /* We never keep any item longer than 10min in our cache */
 #define CACHE_TTL_MAX_USEC (10 * USEC_PER_MINUTE)
 
+typedef enum DnsCacheItemType DnsCacheItemType;
+typedef struct DnsCacheItem DnsCacheItem;
+
+enum DnsCacheItemType {
+        DNS_CACHE_POSITIVE,
+        DNS_CACHE_NODATA,
+        DNS_CACHE_NXDOMAIN,
+};
+
+struct DnsCacheItem {
+        DnsResourceKey *key;
+        DnsResourceRecord *rr;
+        usec_t until;
+        DnsCacheItemType type;
+        unsigned prioq_idx;
+        LIST_FIELDS(DnsCacheItem, by_key);
+};
+
 static void dns_cache_item_free(DnsCacheItem *i) {
         if (!i)
                 return;
@@ -157,9 +175,11 @@ static int dns_cache_item_prioq_compare_func(const void *a, const void *b) {
         return 0;
 }
 
-static int init_cache(DnsCache *c) {
+static int dns_cache_init(DnsCache *c) {
         int r;
 
+        assert(c);
+
         r = prioq_ensure_allocated(&c->by_expiry, dns_cache_item_prioq_compare_func);
         if (r < 0)
                 return r;
@@ -250,6 +270,11 @@ static int dns_cache_put_positive(DnsCache *c, DnsResourceRecord *rr, usec_t tim
                 return 0;
         }
 
+        if (rr->key->class == DNS_CLASS_ANY)
+                return 0;
+        if (rr->key->type == DNS_TYPE_ANY)
+                return 0;
+
         /* Entry exists already? Update TTL and timestamp */
         existing = dns_cache_get(c, rr);
         if (existing) {
@@ -258,7 +283,7 @@ static int dns_cache_put_positive(DnsCache *c, DnsResourceRecord *rr, usec_t tim
         }
 
         /* Otherwise, add the new RR */
-        r = init_cache(c);
+        r = dns_cache_init(c);
         if (r < 0)
                 return r;
 
@@ -291,10 +316,15 @@ static int dns_cache_put_negative(DnsCache *c, DnsResourceKey *key, int rcode, u
 
         dns_cache_remove(c, key);
 
+        if (key->class == DNS_CLASS_ANY)
+                return 0;
+        if (key->type == DNS_TYPE_ANY)
+                return 0;
+
         if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
                 return 0;
 
-        r = init_cache(c);
+        r = dns_cache_init(c);
         if (r < 0)
                 return r;
 
@@ -317,17 +347,21 @@ static int dns_cache_put_negative(DnsCache *c, DnsResourceKey *key, int rcode, u
         return 0;
 }
 
-int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, usec_t timestamp) {
+int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, unsigned max_rrs, usec_t timestamp) {
         unsigned i;
         int r;
 
         assert(c);
-        assert(answer);
+        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 (!answer)
+                return 0;
+
         for (i = 0; i < answer->n_rrs; i++)
                 dns_cache_remove(c, answer->rrs[i]->key);
 
@@ -345,7 +379,7 @@ int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, use
                 timestamp = now(CLOCK_MONOTONIC);
 
         /* Second, add in positive entries for all contained RRs */
-        for (i = 0; i < answer->n_rrs; i++) {
+        for (i = 0; i < MIN(max_rrs, answer->n_rrs); i++) {
                 r = dns_cache_put_positive(c, answer->rrs[i], timestamp);
                 if (r < 0)
                         goto fail;
@@ -394,6 +428,7 @@ int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **ret) {
 
         assert(c);
         assert(q);
+        assert(rcode);
         assert(ret);
 
         if (q->n_keys <= 0) {
@@ -405,6 +440,14 @@ int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **ret) {
         for (i = 0; i < q->n_keys; i++) {
                 DnsCacheItem *j;
 
+                if (q->keys[i]->type == DNS_TYPE_ANY ||
+                    q->keys[i]->class == DNS_CLASS_ANY) {
+                        /* If we have ANY lookups we simply refresh */
+                        *ret = NULL;
+                        *rcode = 0;
+                        return 0;
+                }
+
                 j = hashmap_get(c->by_key, q->keys[i]);
                 if (!j) {
                         /* If one question cannot be answered we need to refresh */