#include "resolved-dns-dnssec.h"
#include "string-util.h"
+static void dns_answer_item_hash_func(const DnsAnswerItem *a, struct siphash *state) {
+ assert(a);
+ assert(state);
+
+ siphash24_compress(&a->ifindex, sizeof(a->ifindex), state);
+
+ dns_resource_record_hash_func(a->rr, state);
+}
+
+static int dns_answer_item_compare_func(const DnsAnswerItem *a, const DnsAnswerItem *b) {
+ int r;
+
+ assert(a);
+ assert(b);
+
+ r = CMP(a->ifindex, b->ifindex);
+ if (r != 0)
+ return r;
+
+ return dns_resource_record_compare_func(a->rr, b->rr);
+}
+
+DEFINE_PRIVATE_HASH_OPS(dns_answer_item_hash_ops, DnsAnswerItem, dns_answer_item_hash_func, dns_answer_item_compare_func);
+
DnsAnswer *dns_answer_new(size_t n) {
+ _cleanup_set_free_ Set *s = NULL;
DnsAnswer *a;
if (n > UINT16_MAX) /* We can only place 64K RRs in an answer at max */
n = UINT16_MAX;
+ s = set_new(&dns_answer_item_hash_ops);
+ if (!s)
+ return NULL;
+
+ /* Higher multipliers give slightly higher efficiency through hash collisions, but the gains
+ * quickly drop off after 2. */
+ if (set_reserve(s, n * 2) < 0)
+ return NULL;
+
a = malloc0(offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * n);
if (!a)
return NULL;
a->n_ref = 1;
a->n_allocated = n;
+ a->set_items = TAKE_PTR(s);
return a;
}
if (!a)
return;
+ a->set_items = set_free(a->set_items);
+
DNS_ANSWER_FOREACH(rr, a)
dns_resource_record_unref(rr);
DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsAnswer, dns_answer, dns_answer_free);
static int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
+ int r;
+
assert(rr);
if (!a)
if (a->n_rrs >= a->n_allocated)
return -ENOSPC;
- a->items[a->n_rrs++] = (DnsAnswerItem) {
- .rr = dns_resource_record_ref(rr),
+ a->items[a->n_rrs] = (DnsAnswerItem) {
+ .rr = rr,
.ifindex = ifindex,
.flags = flags,
};
+ r = set_put(a->set_items, &a->items[a->n_rrs]);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EEXIST;
+
+ dns_resource_record_ref(rr);
+ a->n_rrs++;
+
return 1;
}
}
int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
- size_t i;
- int r;
+ DnsAnswerItem tmp, *exist;
assert(rr);
if (a->n_ref > 1)
return -EBUSY;
- for (i = 0; i < a->n_rrs; i++) {
- if (a->items[i].ifindex != ifindex)
- continue;
-
- r = dns_resource_key_equal(a->items[i].rr->key, rr->key);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
+ tmp = (DnsAnswerItem) {
+ .rr = rr,
+ .ifindex = ifindex,
+ };
+ exist = set_get(a->set_items, &tmp);
+ if (exist) {
/* There's already an RR of the same RRset in place! Let's see if the TTLs more or less
* match. We don't really care if they match precisely, but we do care whether one is 0 and
* the other is not. See RFC 2181, Section 5.2. */
- if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
+ if ((rr->ttl == 0) != (exist->rr->ttl == 0))
return -EINVAL;
- r = dns_resource_record_payload_equal(a->items[i].rr, rr);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- /* Entry already exists, keep the entry with the higher RR. */
- if (rr->ttl > a->items[i].rr->ttl) {
- dns_resource_record_ref(rr);
- dns_resource_record_unref(a->items[i].rr);
- a->items[i].rr = rr;
+ /* Entry already exists, keep the entry with the higher TTL. */
+ if (rr->ttl > exist->rr->ttl) {
+ dns_resource_record_unref(exist->rr);
+ exist->rr = dns_resource_record_ref(rr);
}
- a->items[i].flags |= flags;
+ exist->flags |= flags;
return 0;
}