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:
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user