]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: rework mDNS cache-flush bit handling
authorLennart Poettering <lennart@poettering.net>
Fri, 18 Dec 2015 18:32:46 +0000 (19:32 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 18 Dec 2015 18:40:47 +0000 (19:40 +0100)
This adds a new DnsAnswer item flag "DNS_ANSWER_SHARED_OWNER" which is
set for mDNS RRs that lack the cache-flush bit. The cache-flush bit is
removed from the DnsResourceRecord object in favour of this.

This also splits out the code that removes previous entries when adding
new positive ones into a new separate call dns_cache_remove_previous().

src/resolve-host/resolve-host.c
src/resolve/resolved-dns-answer.h
src/resolve/resolved-dns-cache.c
src/resolve/resolved-dns-packet.c
src/resolve/resolved-dns-packet.h
src/resolve/resolved-dns-rr.h

index bcacb2595ce0ecdf38291215e2752bcafcc4b32b..3e4b52a3a9974cd8cca7d0c1a155720971ac06b5 100644 (file)
@@ -398,7 +398,7 @@ static int resolve_record(sd_bus *bus, const char *name) {
                 if (r < 0)
                         return log_oom();
 
-                r = dns_packet_read_rr(p, &rr, NULL);
+                r = dns_packet_read_rr(p, &rr, NULL, NULL);
                 if (r < 0) {
                         log_error("Failed to parse RR.");
                         return r;
index c946f09f8a2e8ab710341109effd7ea1847f156b..29d6636e68ee90d817a679fb72afa098ebf0227d 100644 (file)
@@ -35,8 +35,9 @@ typedef struct DnsAnswerItem DnsAnswerItem;
  * Note that we usually encode the the empty DnsAnswer object as a simple NULL. */
 
 typedef enum DnsAnswerFlags {
-        DNS_ANSWER_AUTHENTICATED = 1,
-        DNS_ANSWER_CACHEABLE     = 2,
+        DNS_ANSWER_AUTHENTICATED = 1, /* Item has been authenticated */
+        DNS_ANSWER_CACHEABLE     = 2, /* Item is subject to caching */
+        DNS_ANSWER_SHARED_OWNER  = 4, /* For mDNS: RRset may be owner by multiple peers */
 } DnsAnswerFlags;
 
 struct DnsAnswerItem {
index 84ae8b59f9f5d8bbce2e27750ae7b94b4f6fffa8..a6b9fb8f2ded79c27f9aa620f4c6d5c4323747fc 100644 (file)
@@ -42,14 +42,18 @@ enum DnsCacheItemType {
 };
 
 struct DnsCacheItem {
+        DnsCacheItemType type;
         DnsResourceKey *key;
         DnsResourceRecord *rr;
+
         usec_t until;
-        DnsCacheItemType type;
-        unsigned prioq_idx;
-        bool authenticated;
+        bool authenticated:1;
+        bool shared_owner:1;
+
         int owner_family;
         union in_addr_union owner_address;
+
+        unsigned prioq_idx;
         LIST_FIELDS(DnsCacheItem, by_key);
 };
 
@@ -175,7 +179,6 @@ void dns_cache_prune(DnsCache *c) {
         /* Remove all entries that are past their TTL */
 
         for (;;) {
-                _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
                 DnsCacheItem *i;
 
                 i = prioq_peek(c->by_expiry);
@@ -188,10 +191,19 @@ void dns_cache_prune(DnsCache *c) {
                 if (i->until > t)
                         break;
 
-                /* Take an extra reference to the key so that it
-                 * doesn't go away in the middle of the remove call */
-                key = dns_resource_key_ref(i->key);
-                dns_cache_remove(c, key);
+                /* Depending whether this is an mDNS shared entry
+                 * either remove only this one RR or the whole
+                 * RRset */
+                if (i->shared_owner)
+                        dns_cache_item_remove_and_free(c, i);
+                else {
+                        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+
+                        /* Take an extra reference to the key so that it
+                         * doesn't go away in the middle of the remove call */
+                        key = dns_resource_key_ref(i->key);
+                        dns_cache_remove(c, key);
+                }
         }
 }
 
@@ -260,10 +272,20 @@ static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
         return NULL;
 }
 
-static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsResourceRecord *rr, bool authenticated, usec_t timestamp) {
+static void dns_cache_item_update_positive(
+                DnsCache *c,
+                DnsCacheItem *i,
+                DnsResourceRecord *rr,
+                bool authenticated,
+                bool shared_owner,
+                usec_t timestamp,
+                int owner_family,
+                const union in_addr_union *owner_address) {
+
         assert(c);
         assert(i);
         assert(rr);
+        assert(owner_address);
 
         i->type = DNS_CACHE_POSITIVE;
 
@@ -280,8 +302,12 @@ static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsReso
         dns_resource_key_unref(i->key);
         i->key = dns_resource_key_ref(rr->key);
 
-        i->authenticated = authenticated;
         i->until = timestamp + MIN(rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
+        i->authenticated = authenticated;
+        i->shared_owner = shared_owner;
+
+        i->owner_family = owner_family;
+        i->owner_address = *owner_address;
 
         prioq_reshuffle(c->by_expiry, i, &i->prioq_idx);
 }
@@ -290,6 +316,7 @@ static int dns_cache_put_positive(
                 DnsCache *c,
                 DnsResourceRecord *rr,
                 bool authenticated,
+                bool shared_owner,
                 usec_t timestamp,
                 int owner_family,
                 const union in_addr_union *owner_address) {
@@ -327,10 +354,18 @@ static int dns_cache_put_positive(
                 return 0;
         }
 
-        /* Entry exists already? Update TTL and timestamp */
+        /* Entry exists already? Update TTL, timestamp and owner*/
         existing = dns_cache_get(c, rr);
         if (existing) {
-                dns_cache_item_update_positive(c, existing, rr, authenticated, timestamp);
+                dns_cache_item_update_positive(
+                                c,
+                                existing,
+                                rr,
+                                authenticated,
+                                shared_owner,
+                                timestamp,
+                                owner_family,
+                                owner_address);
                 return 0;
         }
 
@@ -349,10 +384,11 @@ static int dns_cache_put_positive(
         i->key = dns_resource_key_ref(rr->key);
         i->rr = dns_resource_record_ref(rr);
         i->until = timestamp + MIN(i->rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
-        i->prioq_idx = PRIOQ_IDX_NULL;
+        i->authenticated = authenticated;
+        i->shared_owner = shared_owner;
         i->owner_family = owner_family;
         i->owner_address = *owner_address;
-        i->authenticated = authenticated;
+        i->prioq_idx = PRIOQ_IDX_NULL;
 
         r = dns_cache_link_item(c, i);
         if (r < 0)
@@ -363,7 +399,7 @@ static int dns_cache_put_positive(
                 if (r < 0)
                         return r;
 
-                log_debug("Added cache entry for %s", key_str);
+                log_debug("Added positive cache entry for %s", key_str);
         }
 
         i = NULL;
@@ -424,10 +460,10 @@ static int dns_cache_put_negative(
 
         i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN;
         i->until = timestamp + MIN(soa_ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
-        i->prioq_idx = PRIOQ_IDX_NULL;
+        i->authenticated = authenticated;
         i->owner_family = owner_family;
         i->owner_address = *owner_address;
-        i->authenticated = authenticated;
+        i->prioq_idx = PRIOQ_IDX_NULL;
 
         if (i->type == DNS_CACHE_NXDOMAIN) {
                 /* NXDOMAIN entries should apply equally to all types, so we use ANY as
@@ -454,6 +490,36 @@ static int dns_cache_put_negative(
         return 0;
 }
 
+static void dns_cache_remove_previous(
+                DnsCache *c,
+                DnsResourceKey *key,
+                DnsAnswer *answer) {
+
+        DnsResourceRecord *rr;
+        DnsAnswerFlags flags;
+
+        assert(c);
+
+        /* First, if we were passed a key (i.e. on LLMNR/DNS, but
+         * not on mDNS), delete all matching old RRs, so that we only
+         * keep complete by_key in place. */
+        if (key)
+                dns_cache_remove(c, key);
+
+        /* Second, flush all entries matching the answer, unless this
+         * is an RR that is explicitly marked to be "shared" between
+         * peers (i.e. mDNS RRs without the flush-cache bit set). */
+        DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
+                if ((flags & DNS_ANSWER_CACHEABLE) == 0)
+                        continue;
+
+                if (flags & DNS_ANSWER_SHARED_OWNER)
+                        continue;
+
+                dns_cache_remove(c, rr->key);
+        }
+}
+
 int dns_cache_put(
                 DnsCache *c,
                 DnsResourceKey *key,
@@ -470,12 +536,9 @@ int dns_cache_put(
         int r;
 
         assert(c);
+        assert(owner_address);
 
-        if (key) {
-                /* First, if we were passed a key, delete all matching old RRs,
-                 * so that we only keep complete by_key in place. */
-                dns_cache_remove(c, key);
-        }
+        dns_cache_remove_previous(c, key, answer);
 
         if (dns_answer_size(answer) <= 0) {
                 if (log_get_max_level() >= LOG_DEBUG) {
@@ -491,18 +554,9 @@ int dns_cache_put(
                 return 0;
         }
 
-        DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
-                if ((flags & DNS_ANSWER_CACHEABLE) == 0)
-                        continue;
-
-                if (rr->key->cache_flush)
-                        dns_cache_remove(c, rr->key);
-        }
-
         /* We only care for positive replies and NXDOMAINs, on all
          * other replies we will simply flush the respective entries,
          * and that's it */
-
         if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
                 return 0;
 
@@ -517,17 +571,22 @@ int dns_cache_put(
                 timestamp = now(clock_boottime_or_monotonic());
 
         /* Second, add in positive entries for all contained RRs */
-
         DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
                 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
                         continue;
 
-                r = dns_cache_put_positive(c, rr, flags & DNS_ANSWER_AUTHENTICATED, timestamp, owner_family, owner_address);
+                r = dns_cache_put_positive(
+                                c,
+                                rr,
+                                flags & DNS_ANSWER_AUTHENTICATED,
+                                flags & DNS_ANSWER_SHARED_OWNER,
+                                timestamp,
+                                owner_family, owner_address);
                 if (r < 0)
                         goto fail;
         }
 
-        if (!key)
+        if (!key) /* mDNS doesn't know negative caching, really */
                 return 0;
 
         /* Third, add in negative entries if the key has no RR */
@@ -561,7 +620,14 @@ int dns_cache_put(
         if (authenticated && (flags & DNS_ANSWER_AUTHENTICATED) == 0)
                 return 0;
 
-        r = dns_cache_put_negative(c, key, rcode, authenticated, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
+        r = dns_cache_put_negative(
+                        c,
+                        key,
+                        rcode,
+                        authenticated,
+                        timestamp,
+                        MIN(soa->soa.minimum, soa->ttl),
+                        owner_family, owner_address);
         if (r < 0)
                 goto fail;
 
@@ -822,7 +888,7 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
                         if (!j->rr)
                                 continue;
 
-                        if (!dns_key_is_shared(j->rr->key))
+                        if (!j->shared_owner)
                                 continue;
 
                         r = dns_packet_append_rr(p, j->rr, NULL, NULL);
index 2b7d634a519d372672581be15d509a37cf5dde64..14faf9e4abaa04d6829aa8865ad35415cc5a51f9 100644 (file)
@@ -1455,9 +1455,9 @@ fail:
         return r;
 }
 
-int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
+int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start) {
         _cleanup_free_ char *name = NULL;
-        bool cache_flush = true;
+        bool cache_flush = false;
         uint16_t class, type;
         DnsResourceKey *key;
         size_t saved_rindex;
@@ -1483,10 +1483,10 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
         if (p->protocol == DNS_PROTOCOL_MDNS) {
                 /* See RFC6762, Section 10.2 */
 
-                if (type != DNS_TYPE_OPT && (class & MDNS_RR_CACHE_FLUSH))
+                if (type != DNS_TYPE_OPT && (class & MDNS_RR_CACHE_FLUSH)) {
                         class &= ~MDNS_RR_CACHE_FLUSH;
-                else
-                        cache_flush = false;
+                        cache_flush = true;
+                }
         }
 
         key = dns_resource_key_new_consume(class, type, name);
@@ -1495,11 +1495,11 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
                 goto fail;
         }
 
-        key->cache_flush = cache_flush;
-
         name = NULL;
         *ret = key;
 
+        if (ret_cache_flush)
+                *ret_cache_flush = cache_flush;
         if (start)
                 *start = saved_rindex;
 
@@ -1515,11 +1515,12 @@ static bool loc_size_ok(uint8_t size) {
         return m <= 9 && e <= 9 && (m > 0 || e == 0);
 }
 
-int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
+int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start) {
         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
         size_t saved_rindex, offset;
         uint16_t rdlength;
+        bool cache_flush;
         int r;
 
         assert(p);
@@ -1527,7 +1528,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
 
         saved_rindex = p->rindex;
 
-        r = dns_packet_read_key(p, &key, NULL);
+        r = dns_packet_read_key(p, &key, &cache_flush, NULL);
         if (r < 0)
                 goto fail;
 
@@ -1939,6 +1940,8 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
         *ret = rr;
         rr = NULL;
 
+        if (ret_cache_flush)
+                *ret_cache_flush = cache_flush;
         if (start)
                 *start = saved_rindex;
 
@@ -1971,11 +1974,17 @@ int dns_packet_extract(DnsPacket *p) {
 
                 for (i = 0; i < n; i++) {
                         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+                        bool cache_flush;
 
-                        r = dns_packet_read_key(p, &key, NULL);
+                        r = dns_packet_read_key(p, &key, &cache_flush, NULL);
                         if (r < 0)
                                 goto finish;
 
+                        if (cache_flush) {
+                                r = -EBADMSG;
+                                goto finish;
+                        }
+
                         if (!dns_type_is_valid_query(key->type)) {
                                 r = -EBADMSG;
                                 goto finish;
@@ -2034,7 +2043,8 @@ int dns_packet_extract(DnsPacket *p) {
                                  * sections. */
 
                                 r = dns_answer_add(answer, rr, p->ifindex,
-                                                   i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0);
+                                                   (i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) |
+                                                   (p->protocol == DNS_PROTOCOL_MDNS && !cache_flush ? DNS_ANSWER_SHARED_OWNER : 0));
                                 if (r < 0)
                                         goto finish;
                         }
index 082e92833c3065821319dd6ad9dc9d1cb46c139d..36da86dee5590acabd521d53b09ccd99bbf09bb0 100644 (file)
@@ -185,8 +185,8 @@ int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start);
 int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start);
 int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start);
 int dns_packet_read_name(DnsPacket *p, char **ret, bool allow_compression, size_t *start);
-int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start);
-int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start);
+int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start);
+int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start);
 
 void dns_packet_rewind(DnsPacket *p, size_t idx);
 
index bb50cf6a340cfa2815bf90f2e912baa78e6c6c24..a35f01ce10ed28d190db455f9c263e4e9917978a 100644 (file)
@@ -71,7 +71,6 @@ struct DnsResourceKey {
         unsigned n_ref;
         uint16_t class, type;
         char *_name; /* don't access directy, use DNS_RESOURCE_KEY_NAME()! */
-        bool cache_flush:1;
 };
 
 /* Creates a temporary resource key. This is only useful to quickly