summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2015-12-18 19:32:46 +0100
committerLennart Poettering <lennart@poettering.net>2015-12-18 19:40:47 +0100
commitd2579eec5e1b845b2cf29caddc951dc22f2abb91 (patch)
treea79b2eedd458f8f93b0bec40a40490b4098e951d
parentea207b639a379b2a0bb8f2cafb0893e406c6152e (diff)
resolved: rework mDNS cache-flush bit handling
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().
-rw-r--r--src/resolve-host/resolve-host.c2
-rw-r--r--src/resolve/resolved-dns-answer.h5
-rw-r--r--src/resolve/resolved-dns-cache.c138
-rw-r--r--src/resolve/resolved-dns-packet.c32
-rw-r--r--src/resolve/resolved-dns-packet.h4
-rw-r--r--src/resolve/resolved-dns-rr.h1
6 files changed, 129 insertions, 53 deletions
diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c
index bcacb2595c..3e4b52a3a9 100644
--- a/src/resolve-host/resolve-host.c
+++ b/src/resolve-host/resolve-host.c
@@ -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;
diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h
index c946f09f8a..29d6636e68 100644
--- a/src/resolve/resolved-dns-answer.h
+++ b/src/resolve/resolved-dns-answer.h
@@ -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 {
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index 84ae8b59f9..a6b9fb8f2d 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -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);
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index 2b7d634a51..14faf9e4ab 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -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;
}
diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h
index 082e92833c..36da86dee5 100644
--- a/src/resolve/resolved-dns-packet.h
+++ b/src/resolve/resolved-dns-packet.h
@@ -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);
diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h
index bb50cf6a34..a35f01ce10 100644
--- a/src/resolve/resolved-dns-rr.h
+++ b/src/resolve/resolved-dns-rr.h
@@ -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