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().
This commit is contained in:
Lennart Poettering
2015-12-18 19:40:47 +01:00
parent ea207b639a
commit d2579eec5e
6 changed files with 129 additions and 53 deletions
+1 -1
View 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;
+3 -2
View 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 {
+102 -36
View 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);
+21 -11
View 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;
}
+2 -2
View 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);
-1
View 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