]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-cache.c
sd-netlink: add functions which manage sd_netlink_slot object
[thirdparty/systemd.git] / src / resolve / resolved-dns-cache.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
322345fd 2
202b76ae
ZJS
3#include <net/if.h>
4
5#include "af-list.h"
b5efdb8a 6#include "alloc-util.h"
58db254a 7#include "dns-domain.h"
02c2857b 8#include "resolved-dns-answer.h"
322345fd 9#include "resolved-dns-cache.h"
7e8e0422 10#include "resolved-dns-packet.h"
58db254a 11#include "string-util.h"
322345fd 12
6af47493
LP
13/* Never cache more than 4K entries. RFC 1536, Section 5 suggests to
14 * leave DNS caches unbounded, but that's crazy. */
d98e5504 15#define CACHE_MAX 4096
cbd4560e 16
d98e5504
LP
17/* We never keep any item longer than 2h in our cache */
18#define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR)
322345fd 19
201d9958
LP
20/* How long to cache strange rcodes, i.e. rcodes != SUCCESS and != NXDOMAIN (specifically: that's only SERVFAIL for
21 * now) */
22#define CACHE_TTL_STRANGE_RCODE_USEC (30 * USEC_PER_SEC)
23
623a4c97
LP
24typedef enum DnsCacheItemType DnsCacheItemType;
25typedef struct DnsCacheItem DnsCacheItem;
26
27enum DnsCacheItemType {
28 DNS_CACHE_POSITIVE,
29 DNS_CACHE_NODATA,
30 DNS_CACHE_NXDOMAIN,
201d9958 31 DNS_CACHE_RCODE, /* "strange" RCODE (effective only SERVFAIL for now) */
623a4c97
LP
32};
33
34struct DnsCacheItem {
d2579eec 35 DnsCacheItemType type;
623a4c97
LP
36 DnsResourceKey *key;
37 DnsResourceRecord *rr;
201d9958 38 int rcode;
d2579eec 39
623a4c97 40 usec_t until;
d2579eec
LP
41 bool authenticated:1;
42 bool shared_owner:1;
43
06d12754 44 int ifindex;
a4076574
LP
45 int owner_family;
46 union in_addr_union owner_address;
d2579eec
LP
47
48 unsigned prioq_idx;
623a4c97
LP
49 LIST_FIELDS(DnsCacheItem, by_key);
50};
51
201d9958
LP
52static const char *dns_cache_item_type_to_string(DnsCacheItem *item) {
53 assert(item);
54
55 switch (item->type) {
56
57 case DNS_CACHE_POSITIVE:
58 return "POSITIVE";
59
60 case DNS_CACHE_NODATA:
61 return "NODATA";
62
63 case DNS_CACHE_NXDOMAIN:
64 return "NXDOMAIN";
65
66 case DNS_CACHE_RCODE:
67 return dns_rcode_to_string(item->rcode);
68 }
69
70 return NULL;
71}
72
322345fd
LP
73static void dns_cache_item_free(DnsCacheItem *i) {
74 if (!i)
75 return;
76
77 dns_resource_record_unref(i->rr);
7e8e0422 78 dns_resource_key_unref(i->key);
322345fd
LP
79 free(i);
80}
81
82DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem*, dns_cache_item_free);
83
39963f11 84static void dns_cache_item_unlink_and_free(DnsCache *c, DnsCacheItem *i) {
322345fd
LP
85 DnsCacheItem *first;
86
87 assert(c);
88
89 if (!i)
90 return;
91
7e8e0422
LP
92 first = hashmap_get(c->by_key, i->key);
93 LIST_REMOVE(by_key, first, i);
322345fd
LP
94
95 if (first)
7e8e0422 96 assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
322345fd 97 else
7e8e0422 98 hashmap_remove(c->by_key, i->key);
322345fd 99
7e8e0422 100 prioq_remove(c->by_expiry, i, &i->prioq_idx);
322345fd
LP
101
102 dns_cache_item_free(i);
103}
104
f5bdeb01
LP
105static bool dns_cache_remove_by_rr(DnsCache *c, DnsResourceRecord *rr) {
106 DnsCacheItem *first, *i;
107 int r;
108
109 first = hashmap_get(c->by_key, rr->key);
110 LIST_FOREACH(by_key, i, first) {
111 r = dns_resource_record_equal(i->rr, rr);
112 if (r < 0)
113 return r;
114 if (r > 0) {
39963f11 115 dns_cache_item_unlink_and_free(c, i);
f5bdeb01
LP
116 return true;
117 }
118 }
119
120 return false;
121}
122
2dda578f 123static bool dns_cache_remove_by_key(DnsCache *c, DnsResourceKey *key) {
1f97052f 124 DnsCacheItem *first, *i, *n;
322345fd
LP
125
126 assert(c);
127 assert(key);
128
1f97052f
LP
129 first = hashmap_remove(c->by_key, key);
130 if (!first)
131 return false;
132
133 LIST_FOREACH_SAFE(by_key, i, n, first) {
134 prioq_remove(c->by_expiry, i, &i->prioq_idx);
135 dns_cache_item_free(i);
6b34a6c9
TG
136 }
137
1f97052f 138 return true;
322345fd
LP
139}
140
ef9a3e3c
LP
141void dns_cache_flush(DnsCache *c) {
142 DnsResourceKey *key;
143
144 assert(c);
145
146 while ((key = hashmap_first_key(c->by_key)))
2dda578f 147 dns_cache_remove_by_key(c, key);
ef9a3e3c
LP
148
149 assert(hashmap_size(c->by_key) == 0);
150 assert(prioq_size(c->by_expiry) == 0);
151
152 c->by_key = hashmap_free(c->by_key);
153 c->by_expiry = prioq_free(c->by_expiry);
154}
155
322345fd
LP
156static void dns_cache_make_space(DnsCache *c, unsigned add) {
157 assert(c);
158
159 if (add <= 0)
160 return;
161
162 /* Makes space for n new entries. Note that we actually allow
163 * the cache to grow beyond CACHE_MAX, but only when we shall
164 * add more RRs to the cache than CACHE_MAX at once. In that
165 * case the cache will be emptied completely otherwise. */
166
167 for (;;) {
faa133f3 168 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
322345fd
LP
169 DnsCacheItem *i;
170
7e8e0422 171 if (prioq_size(c->by_expiry) <= 0)
322345fd
LP
172 break;
173
7e8e0422 174 if (prioq_size(c->by_expiry) + add < CACHE_MAX)
322345fd
LP
175 break;
176
7e8e0422 177 i = prioq_peek(c->by_expiry);
cbd4560e
LP
178 assert(i);
179
faa133f3 180 /* Take an extra reference to the key so that it
cbd4560e 181 * doesn't go away in the middle of the remove call */
7e8e0422 182 key = dns_resource_key_ref(i->key);
2dda578f 183 dns_cache_remove_by_key(c, key);
322345fd
LP
184 }
185}
186
187void dns_cache_prune(DnsCache *c) {
188 usec_t t = 0;
189
190 assert(c);
191
192 /* Remove all entries that are past their TTL */
193
194 for (;;) {
195 DnsCacheItem *i;
202b76ae 196 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
322345fd 197
7e8e0422 198 i = prioq_peek(c->by_expiry);
322345fd
LP
199 if (!i)
200 break;
201
322345fd 202 if (t <= 0)
240b589b 203 t = now(clock_boottime_or_monotonic());
322345fd 204
7e8e0422 205 if (i->until > t)
322345fd
LP
206 break;
207
d2579eec 208 /* Depending whether this is an mDNS shared entry
202b76ae
ZJS
209 * either remove only this one RR or the whole RRset */
210 log_debug("Removing %scache entry for %s (expired "USEC_FMT"s ago)",
211 i->shared_owner ? "shared " : "",
212 dns_resource_key_to_string(i->key, key_str, sizeof key_str),
213 (t - i->until) / USEC_PER_SEC);
214
d2579eec 215 if (i->shared_owner)
39963f11 216 dns_cache_item_unlink_and_free(c, i);
d2579eec
LP
217 else {
218 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
219
220 /* Take an extra reference to the key so that it
221 * doesn't go away in the middle of the remove call */
222 key = dns_resource_key_ref(i->key);
2dda578f 223 dns_cache_remove_by_key(c, key);
d2579eec 224 }
322345fd
LP
225 }
226}
227
228static int dns_cache_item_prioq_compare_func(const void *a, const void *b) {
322345fd
LP
229 const DnsCacheItem *x = a, *y = b;
230
a0edd02e 231 return CMP(x->until, y->until);
322345fd
LP
232}
233
623a4c97 234static int dns_cache_init(DnsCache *c) {
7e8e0422
LP
235 int r;
236
623a4c97
LP
237 assert(c);
238
7e8e0422
LP
239 r = prioq_ensure_allocated(&c->by_expiry, dns_cache_item_prioq_compare_func);
240 if (r < 0)
241 return r;
242
d5099efc 243 r = hashmap_ensure_allocated(&c->by_key, &dns_resource_key_hash_ops);
7e8e0422
LP
244 if (r < 0)
245 return r;
246
247 return r;
248}
249
250static int dns_cache_link_item(DnsCache *c, DnsCacheItem *i) {
251 DnsCacheItem *first;
252 int r;
253
322345fd
LP
254 assert(c);
255 assert(i);
322345fd 256
7e8e0422
LP
257 r = prioq_put(c->by_expiry, i, &i->prioq_idx);
258 if (r < 0)
259 return r;
322345fd 260
7e8e0422
LP
261 first = hashmap_get(c->by_key, i->key);
262 if (first) {
f57e3cd5
LP
263 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
264
265 /* Keep a reference to the original key, while we manipulate the list. */
266 k = dns_resource_key_ref(first->key);
267
268 /* Now, try to reduce the number of keys we keep */
269 dns_resource_key_reduce(&first->key, &i->key);
270
271 if (first->rr)
272 dns_resource_key_reduce(&first->rr->key, &i->key);
273 if (i->rr)
274 dns_resource_key_reduce(&i->rr->key, &i->key);
275
7e8e0422
LP
276 LIST_PREPEND(by_key, first, i);
277 assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
278 } else {
279 r = hashmap_put(c->by_key, i->key, i);
280 if (r < 0) {
281 prioq_remove(c->by_expiry, i, &i->prioq_idx);
282 return r;
283 }
322345fd
LP
284 }
285
7e8e0422 286 return 0;
322345fd
LP
287}
288
faa133f3
LP
289static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
290 DnsCacheItem *i;
291
292 assert(c);
293 assert(rr);
294
7e8e0422 295 LIST_FOREACH(by_key, i, hashmap_get(c->by_key, rr->key))
3ef77d04 296 if (i->rr && dns_resource_record_equal(i->rr, rr) > 0)
faa133f3
LP
297 return i;
298
299 return NULL;
300}
301
d3760be0 302static usec_t calculate_until(DnsResourceRecord *rr, uint32_t nsec_ttl, usec_t timestamp, bool use_soa_minimum) {
b211dc7e
LP
303 uint32_t ttl;
304 usec_t u;
ee3d6aff
LP
305
306 assert(rr);
307
d3760be0 308 ttl = MIN(rr->ttl, nsec_ttl);
b211dc7e
LP
309 if (rr->key->type == DNS_TYPE_SOA && use_soa_minimum) {
310 /* If this is a SOA RR, and it is requested, clamp to
311 * the SOA's minimum field. This is used when we do
312 * negative caching, to determine the TTL for the
313 * negative caching entry. See RFC 2308, Section
314 * 5. */
ee3d6aff 315
b211dc7e
LP
316 if (ttl > rr->soa.minimum)
317 ttl = rr->soa.minimum;
318 }
319
320 u = ttl * USEC_PER_SEC;
321 if (u > CACHE_TTL_MAX_USEC)
322 u = CACHE_TTL_MAX_USEC;
ee3d6aff
LP
323
324 if (rr->expiry != USEC_INFINITY) {
325 usec_t left;
326
b211dc7e
LP
327 /* Make use of the DNSSEC RRSIG expiry info, if we
328 * have it */
ee3d6aff
LP
329
330 left = LESS_BY(rr->expiry, now(CLOCK_REALTIME));
b211dc7e
LP
331 if (u > left)
332 u = left;
ee3d6aff
LP
333 }
334
b211dc7e 335 return timestamp + u;
ee3d6aff
LP
336}
337
d2579eec
LP
338static void dns_cache_item_update_positive(
339 DnsCache *c,
340 DnsCacheItem *i,
341 DnsResourceRecord *rr,
342 bool authenticated,
343 bool shared_owner,
344 usec_t timestamp,
06d12754 345 int ifindex,
d2579eec
LP
346 int owner_family,
347 const union in_addr_union *owner_address) {
348
7e8e0422
LP
349 assert(c);
350 assert(i);
351 assert(rr);
d2579eec 352 assert(owner_address);
7e8e0422
LP
353
354 i->type = DNS_CACHE_POSITIVE;
355
ece174c5 356 if (!i->by_key_prev)
7e8e0422
LP
357 /* We are the first item in the list, we need to
358 * update the key used in the hashmap */
359
360 assert_se(hashmap_replace(c->by_key, rr->key, i) >= 0);
7e8e0422
LP
361
362 dns_resource_record_ref(rr);
363 dns_resource_record_unref(i->rr);
364 i->rr = rr;
365
366 dns_resource_key_unref(i->key);
367 i->key = dns_resource_key_ref(rr->key);
368
d3760be0 369 i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
d2579eec
LP
370 i->authenticated = authenticated;
371 i->shared_owner = shared_owner;
372
06d12754
LP
373 i->ifindex = ifindex;
374
d2579eec
LP
375 i->owner_family = owner_family;
376 i->owner_address = *owner_address;
7e8e0422
LP
377
378 prioq_reshuffle(c->by_expiry, i, &i->prioq_idx);
379}
380
a4076574
LP
381static int dns_cache_put_positive(
382 DnsCache *c,
383 DnsResourceRecord *rr,
931851e8 384 bool authenticated,
d2579eec 385 bool shared_owner,
a4076574 386 usec_t timestamp,
06d12754 387 int ifindex,
a4076574
LP
388 int owner_family,
389 const union in_addr_union *owner_address) {
390
322345fd 391 _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
7e8e0422 392 DnsCacheItem *existing;
202b76ae 393 char key_str[DNS_RESOURCE_KEY_STRING_MAX], ifname[IF_NAMESIZE];
a257f9d4 394 int r, k;
322345fd
LP
395
396 assert(c);
397 assert(rr);
a4076574 398 assert(owner_address);
322345fd 399
222148b6
LP
400 /* Never cache pseudo RRs */
401 if (dns_class_is_pseudo(rr->key->class))
402 return 0;
403 if (dns_type_is_pseudo(rr->key->type))
404 return 0;
405
f5bdeb01 406 /* New TTL is 0? Delete this specific entry... */
322345fd 407 if (rr->ttl <= 0) {
f5bdeb01 408 k = dns_cache_remove_by_rr(c, rr);
202b76ae
ZJS
409 log_debug("%s: %s",
410 k > 0 ? "Removed zero TTL entry from cache" : "Not caching zero TTL cache entry",
18665d1f 411 dns_resource_key_to_string(rr->key, key_str, sizeof key_str));
322345fd
LP
412 return 0;
413 }
414
13e785f7 415 /* Entry exists already? Update TTL, timestamp and owner */
322345fd
LP
416 existing = dns_cache_get(c, rr);
417 if (existing) {
d2579eec
LP
418 dns_cache_item_update_positive(
419 c,
420 existing,
421 rr,
422 authenticated,
423 shared_owner,
424 timestamp,
06d12754 425 ifindex,
d2579eec
LP
426 owner_family,
427 owner_address);
322345fd
LP
428 return 0;
429 }
430
431 /* Otherwise, add the new RR */
623a4c97 432 r = dns_cache_init(c);
322345fd
LP
433 if (r < 0)
434 return r;
435
7e8e0422
LP
436 dns_cache_make_space(c, 1);
437
438 i = new0(DnsCacheItem, 1);
439 if (!i)
440 return -ENOMEM;
441
442 i->type = DNS_CACHE_POSITIVE;
443 i->key = dns_resource_key_ref(rr->key);
444 i->rr = dns_resource_record_ref(rr);
d3760be0 445 i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
d2579eec
LP
446 i->authenticated = authenticated;
447 i->shared_owner = shared_owner;
06d12754 448 i->ifindex = ifindex;
a4076574
LP
449 i->owner_family = owner_family;
450 i->owner_address = *owner_address;
d2579eec 451 i->prioq_idx = PRIOQ_IDX_NULL;
7e8e0422
LP
452
453 r = dns_cache_link_item(c, i);
454 if (r < 0)
455 return r;
456
f1d34068 457 if (DEBUG_LOGGING) {
202b76ae
ZJS
458 _cleanup_free_ char *t = NULL;
459
460 (void) in_addr_to_string(i->owner_family, &i->owner_address, &t);
461
462 log_debug("Added positive %s%s cache entry for %s "USEC_FMT"s on %s/%s/%s",
463 i->authenticated ? "authenticated" : "unauthenticated",
464 i->shared_owner ? " shared" : "",
465 dns_resource_key_to_string(i->key, key_str, sizeof key_str),
466 (i->until - timestamp) / USEC_PER_SEC,
467 i->ifindex == 0 ? "*" : strna(if_indextoname(i->ifindex, ifname)),
468 af_to_name_short(i->owner_family),
469 strna(t));
a257f9d4 470 }
6b34a6c9 471
7e8e0422
LP
472 i = NULL;
473 return 0;
474}
475
a4076574
LP
476static int dns_cache_put_negative(
477 DnsCache *c,
478 DnsResourceKey *key,
479 int rcode,
931851e8 480 bool authenticated,
d3760be0 481 uint32_t nsec_ttl,
a4076574 482 usec_t timestamp,
b211dc7e 483 DnsResourceRecord *soa,
a4076574
LP
484 int owner_family,
485 const union in_addr_union *owner_address) {
486
7e8e0422 487 _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
202b76ae 488 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
7e8e0422
LP
489 int r;
490
491 assert(c);
492 assert(key);
a4076574 493 assert(owner_address);
7e8e0422 494
98b6be77
LP
495 /* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
496 * important to filter out as we use this as a pseudo-type for
497 * NXDOMAIN entries */
222148b6 498 if (dns_class_is_pseudo(key->class))
ddf16339 499 return 0;
c33be4a6 500 if (dns_type_is_pseudo(key->type))
ddf16339 501 return 0;
222148b6 502
201d9958
LP
503 if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
504 if (!soa)
505 return 0;
ddf16339 506
201d9958
LP
507 /* For negative replies, check if we have a TTL of a SOA */
508 if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) {
509 log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s",
510 dns_resource_key_to_string(key, key_str, sizeof key_str));
511 return 0;
512 }
513 } else if (rcode != DNS_RCODE_SERVFAIL)
7e8e0422
LP
514 return 0;
515
623a4c97 516 r = dns_cache_init(c);
322345fd
LP
517 if (r < 0)
518 return r;
519
520 dns_cache_make_space(c, 1);
521
522 i = new0(DnsCacheItem, 1);
523 if (!i)
524 return -ENOMEM;
525
201d9958
LP
526 i->type =
527 rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA :
528 rcode == DNS_RCODE_NXDOMAIN ? DNS_CACHE_NXDOMAIN : DNS_CACHE_RCODE;
529 i->until =
530 i->type == DNS_CACHE_RCODE ? timestamp + CACHE_TTL_STRANGE_RCODE_USEC :
531 calculate_until(soa, nsec_ttl, timestamp, true);
d2579eec 532 i->authenticated = authenticated;
a4076574
LP
533 i->owner_family = owner_family;
534 i->owner_address = *owner_address;
d2579eec 535 i->prioq_idx = PRIOQ_IDX_NULL;
201d9958 536 i->rcode = rcode;
322345fd 537
71e13669
TG
538 if (i->type == DNS_CACHE_NXDOMAIN) {
539 /* NXDOMAIN entries should apply equally to all types, so we use ANY as
540 * a pseudo type for this purpose here. */
1c02e7ba 541 i->key = dns_resource_key_new(key->class, DNS_TYPE_ANY, dns_resource_key_name(key));
71e13669
TG
542 if (!i->key)
543 return -ENOMEM;
a5444ca9
LP
544
545 /* Make sure to remove any previous entry for this
546 * specific ANY key. (For non-ANY keys the cache data
547 * is already cleared by the caller.) Note that we
548 * don't bother removing positive or NODATA cache
549 * items in this case, because it would either be slow
550 * or require explicit indexing by name */
551 dns_cache_remove_by_key(c, key);
71e13669
TG
552 } else
553 i->key = dns_resource_key_ref(key);
554
7e8e0422 555 r = dns_cache_link_item(c, i);
322345fd
LP
556 if (r < 0)
557 return r;
558
202b76ae 559 log_debug("Added %s cache entry for %s "USEC_FMT"s",
201d9958 560 dns_cache_item_type_to_string(i),
202b76ae
ZJS
561 dns_resource_key_to_string(i->key, key_str, sizeof key_str),
562 (i->until - timestamp) / USEC_PER_SEC);
6b34a6c9 563
322345fd 564 i = NULL;
322345fd
LP
565 return 0;
566}
567
d2579eec
LP
568static void dns_cache_remove_previous(
569 DnsCache *c,
570 DnsResourceKey *key,
571 DnsAnswer *answer) {
572
573 DnsResourceRecord *rr;
574 DnsAnswerFlags flags;
575
576 assert(c);
577
578 /* First, if we were passed a key (i.e. on LLMNR/DNS, but
579 * not on mDNS), delete all matching old RRs, so that we only
580 * keep complete by_key in place. */
581 if (key)
2dda578f 582 dns_cache_remove_by_key(c, key);
d2579eec
LP
583
584 /* Second, flush all entries matching the answer, unless this
585 * is an RR that is explicitly marked to be "shared" between
586 * peers (i.e. mDNS RRs without the flush-cache bit set). */
587 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
588 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
589 continue;
590
591 if (flags & DNS_ANSWER_SHARED_OWNER)
592 continue;
593
2dda578f 594 dns_cache_remove_by_key(c, rr->key);
d2579eec
LP
595 }
596}
597
f6618dcd
LP
598static bool rr_eligible(DnsResourceRecord *rr) {
599 assert(rr);
600
601 /* When we see an NSEC/NSEC3 RR, we'll only cache it if it is from the lower zone, not the upper zone, since
602 * that's where the interesting bits are (with exception of DS RRs). Of course, this way we cannot derive DS
603 * existence from any cached NSEC/NSEC3, but that should be fine. */
604
605 switch (rr->key->type) {
606
607 case DNS_TYPE_NSEC:
608 return !bitmap_isset(rr->nsec.types, DNS_TYPE_NS) ||
609 bitmap_isset(rr->nsec.types, DNS_TYPE_SOA);
610
611 case DNS_TYPE_NSEC3:
612 return !bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) ||
613 bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA);
614
615 default:
616 return true;
617 }
618}
619
a4076574
LP
620int dns_cache_put(
621 DnsCache *c,
8e427d9b 622 DnsResourceKey *key,
a4076574
LP
623 int rcode,
624 DnsAnswer *answer,
931851e8 625 bool authenticated,
d3760be0 626 uint32_t nsec_ttl,
a4076574
LP
627 usec_t timestamp,
628 int owner_family,
629 const union in_addr_union *owner_address) {
630
02c2857b 631 DnsResourceRecord *soa = NULL, *rr;
201d9958 632 bool weird_rcode = false;
105e1512
LP
633 DnsAnswerFlags flags;
634 unsigned cache_keys;
06d12754 635 int r, ifindex;
322345fd
LP
636
637 assert(c);
d2579eec 638 assert(owner_address);
322345fd 639
d2579eec 640 dns_cache_remove_previous(c, key, answer);
0ec7c46e 641
201d9958
LP
642 /* We only care for positive replies and NXDOMAINs, on all other replies we will simply flush the respective
643 * entries, and that's it. (Well, with one further exception: since some DNS zones (akamai!) return SERVFAIL
644 * consistently for some lookups, and forwarders tend to propagate that we'll cache that too, but only for a
645 * short time.) */
6ff01a0d 646
201d9958 647 if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
201d9958 648 if (dns_answer_size(answer) <= 0) {
e55fc5b0
YW
649 if (key) {
650 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
201d9958 651
e55fc5b0
YW
652 log_debug("Not caching negative entry without a SOA record: %s",
653 dns_resource_key_to_string(key, key_str, sizeof key_str));
654 }
201d9958
LP
655 return 0;
656 }
657
658 } else {
659 /* Only cache SERVFAIL as "weird" rcode for now. We can add more later, should that turn out to be
660 * beneficial. */
661 if (rcode != DNS_RCODE_SERVFAIL)
662 return 0;
663
664 weird_rcode = true;
c3cb6dc2 665 }
0ec7c46e 666
ea207b63 667 cache_keys = dns_answer_size(answer);
8e427d9b 668 if (key)
313cefa1 669 cache_keys++;
eff91ee0 670
7e8e0422 671 /* Make some space for our new entries */
eff91ee0 672 dns_cache_make_space(c, cache_keys);
322345fd 673
7e8e0422 674 if (timestamp <= 0)
240b589b 675 timestamp = now(clock_boottime_or_monotonic());
322345fd 676
7e8e0422 677 /* Second, add in positive entries for all contained RRs */
06d12754 678 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
fa6a69d7
YW
679 if ((flags & DNS_ANSWER_CACHEABLE) == 0 ||
680 !rr_eligible(rr))
f6618dcd
LP
681 continue;
682
d2579eec
LP
683 r = dns_cache_put_positive(
684 c,
685 rr,
686 flags & DNS_ANSWER_AUTHENTICATED,
687 flags & DNS_ANSWER_SHARED_OWNER,
688 timestamp,
06d12754 689 ifindex,
d2579eec 690 owner_family, owner_address);
7e8e0422
LP
691 if (r < 0)
692 goto fail;
693 }
694
d2579eec 695 if (!key) /* mDNS doesn't know negative caching, really */
eff91ee0
DM
696 return 0;
697
8e427d9b 698 /* Third, add in negative entries if the key has no RR */
105e1512 699 r = dns_answer_match_key(answer, key, NULL);
8e427d9b
TG
700 if (r < 0)
701 goto fail;
702 if (r > 0)
703 return 0;
7e8e0422 704
5d27351f
TG
705 /* But not if it has a matching CNAME/DNAME (the negative
706 * caching will be done on the canonical name, not on the
707 * alias) */
105e1512 708 r = dns_answer_find_cname_or_dname(answer, key, NULL, NULL);
5d27351f
TG
709 if (r < 0)
710 goto fail;
711 if (r > 0)
712 return 0;
713
201d9958
LP
714 /* See https://tools.ietf.org/html/rfc2308, which say that a matching SOA record in the packet is used to
715 * enable negative caching. We apply one exception though: if we are about to cache a weird rcode we do so
716 * regardless of a SOA. */
fd009cd8 717 r = dns_answer_find_soa(answer, key, &soa, &flags);
8e427d9b
TG
718 if (r < 0)
719 goto fail;
201d9958 720 if (r == 0 && !weird_rcode)
fd009cd8 721 return 0;
201d9958
LP
722 if (r > 0) {
723 /* Refuse using the SOA data if it is unsigned, but the key is
724 * signed */
725 if (authenticated && (flags & DNS_ANSWER_AUTHENTICATED) == 0)
726 return 0;
727 }
fd009cd8 728
d2579eec
LP
729 r = dns_cache_put_negative(
730 c,
731 key,
732 rcode,
733 authenticated,
d3760be0 734 nsec_ttl,
d2579eec 735 timestamp,
b211dc7e 736 soa,
d2579eec 737 owner_family, owner_address);
8e427d9b
TG
738 if (r < 0)
739 goto fail;
322345fd
LP
740
741 return 0;
742
743fail:
744 /* Adding all RRs failed. Let's clean up what we already
745 * added, just in case */
746
8e427d9b 747 if (key)
2dda578f 748 dns_cache_remove_by_key(c, key);
eff91ee0 749
105e1512
LP
750 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
751 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
752 continue;
753
2dda578f 754 dns_cache_remove_by_key(c, rr->key);
105e1512 755 }
322345fd
LP
756
757 return r;
758}
759
37da8931 760static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, DnsResourceKey *k) {
58db254a
LP
761 DnsCacheItem *i;
762 const char *n;
763 int r;
5643c00a
TG
764
765 assert(c);
766 assert(k);
767
58db254a
LP
768 /* If we hit some OOM error, or suchlike, we don't care too
769 * much, after all this is just a cache */
770
5643c00a 771 i = hashmap_get(c->by_key, k);
d7ce6c94 772 if (i)
37da8931
LP
773 return i;
774
1c02e7ba 775 n = dns_resource_key_name(k);
37da8931 776
71e13669
TG
777 /* Check if we have an NXDOMAIN cache item for the name, notice that we use
778 * the pseudo-type ANY for NXDOMAIN cache items. */
779 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_ANY, n));
780 if (i && i->type == DNS_CACHE_NXDOMAIN)
781 return i;
782
d3c7e913 783 if (dns_type_may_redirect(k->type)) {
d7ce6c94
TG
784 /* Check if we have a CNAME record instead */
785 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_CNAME, n));
3740146a 786 if (i && i->type != DNS_CACHE_NODATA)
d7ce6c94 787 return i;
5643c00a 788
d7ce6c94
TG
789 /* OK, let's look for cached DNAME records. */
790 for (;;) {
d7ce6c94
TG
791 if (isempty(n))
792 return NULL;
793
794 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_DNAME, n));
3740146a 795 if (i && i->type != DNS_CACHE_NODATA)
d7ce6c94 796 return i;
58db254a 797
d7ce6c94 798 /* Jump one label ahead */
950b692b 799 r = dns_name_parent(&n);
d7ce6c94
TG
800 if (r <= 0)
801 return NULL;
802 }
803 }
5643c00a 804
950b692b 805 if (k->type != DNS_TYPE_NSEC) {
d7ce6c94
TG
806 /* Check if we have an NSEC record instead for the name. */
807 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_NSEC, n));
58db254a
LP
808 if (i)
809 return i;
58db254a
LP
810 }
811
812 return NULL;
5643c00a
TG
813}
814
17c8de63 815int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcode, DnsAnswer **ret, bool *authenticated) {
faa133f3 816 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
202b76ae 817 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
f52e61da 818 unsigned n = 0;
322345fd 819 int r;
7e8e0422 820 bool nxdomain = false;
931851e8
LP
821 DnsCacheItem *j, *first, *nsec = NULL;
822 bool have_authenticated = false, have_non_authenticated = false;
17c8de63 823 usec_t current;
201d9958 824 int found_rcode = -1;
322345fd
LP
825
826 assert(c);
f52e61da 827 assert(key);
623a4c97 828 assert(rcode);
faa133f3 829 assert(ret);
931851e8 830 assert(authenticated);
322345fd 831
202b76ae 832 if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
931851e8
LP
833 /* If we have ANY lookups we don't use the cache, so
834 * that the caller refreshes via the network. */
322345fd 835
202b76ae
ZJS
836 log_debug("Ignoring cache for ANY lookup: %s",
837 dns_resource_key_to_string(key, key_str, sizeof key_str));
6b34a6c9 838
a150ff5e
LP
839 c->n_miss++;
840
f52e61da
LP
841 *ret = NULL;
842 *rcode = DNS_RCODE_SUCCESS;
2d4a4e14
LP
843 *authenticated = false;
844
f52e61da
LP
845 return 0;
846 }
6b34a6c9 847
37da8931 848 first = dns_cache_get_by_key_follow_cname_dname_nsec(c, key);
f52e61da
LP
849 if (!first) {
850 /* If one question cannot be answered we need to refresh */
ddf16339 851
202b76ae
ZJS
852 log_debug("Cache miss for %s",
853 dns_resource_key_to_string(key, key_str, sizeof key_str));
6b34a6c9 854
a150ff5e
LP
855 c->n_miss++;
856
f52e61da
LP
857 *ret = NULL;
858 *rcode = DNS_RCODE_SUCCESS;
2d4a4e14
LP
859 *authenticated = false;
860
f52e61da
LP
861 return 0;
862 }
6b34a6c9 863
f52e61da 864 LIST_FOREACH(by_key, j, first) {
37da8931
LP
865 if (j->rr) {
866 if (j->rr->key->type == DNS_TYPE_NSEC)
931851e8
LP
867 nsec = j;
868
f52e61da 869 n++;
37da8931 870 } else if (j->type == DNS_CACHE_NXDOMAIN)
f52e61da 871 nxdomain = true;
201d9958
LP
872 else if (j->type == DNS_CACHE_RCODE)
873 found_rcode = j->rcode;
931851e8
LP
874
875 if (j->authenticated)
876 have_authenticated = true;
877 else
878 have_non_authenticated = true;
f52e61da 879 }
6b34a6c9 880
201d9958
LP
881 if (found_rcode >= 0) {
882 log_debug("RCODE %s cache hit for %s",
883 dns_rcode_to_string(found_rcode),
884 dns_resource_key_to_string(key, key_str, sizeof(key_str)));
885
886 *ret = NULL;
887 *rcode = found_rcode;
888 *authenticated = false;
889
890 c->n_hit++;
891 return 1;
892 }
893
f6618dcd
LP
894 if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) {
895 /* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC RRs from
896 * the lower-zone of a zone cut, but the DS RRs are on the upper zone. */
897
202b76ae
ZJS
898 log_debug("NSEC NODATA cache hit for %s",
899 dns_resource_key_to_string(key, key_str, sizeof key_str));
37da8931
LP
900
901 /* We only found an NSEC record that matches our name.
a257f9d4 902 * If it says the type doesn't exist report
37da8931
LP
903 * NODATA. Otherwise report a cache miss. */
904
905 *ret = NULL;
906 *rcode = DNS_RCODE_SUCCESS;
931851e8 907 *authenticated = nsec->authenticated;
37da8931 908
a150ff5e
LP
909 if (!bitmap_isset(nsec->rr->nsec.types, key->type) &&
910 !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
911 !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME)) {
912 c->n_hit++;
913 return 1;
914 }
915
916 c->n_miss++;
917 return 0;
37da8931
LP
918 }
919
202b76ae
ZJS
920 log_debug("%s cache hit for %s",
921 n > 0 ? "Positive" :
922 nxdomain ? "NXDOMAIN" : "NODATA",
923 dns_resource_key_to_string(key, key_str, sizeof key_str));
faa133f3 924
7e8e0422 925 if (n <= 0) {
a150ff5e
LP
926 c->n_hit++;
927
7e8e0422
LP
928 *ret = NULL;
929 *rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
931851e8 930 *authenticated = have_authenticated && !have_non_authenticated;
7e8e0422
LP
931 return 1;
932 }
faa133f3
LP
933
934 answer = dns_answer_new(n);
935 if (!answer)
936 return -ENOMEM;
322345fd 937
17c8de63
LP
938 if (clamp_ttl)
939 current = now(clock_boottime_or_monotonic());
940
f52e61da 941 LIST_FOREACH(by_key, j, first) {
17c8de63
LP
942 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
943
f52e61da
LP
944 if (!j->rr)
945 continue;
946
17c8de63
LP
947 if (clamp_ttl) {
948 rr = dns_resource_record_ref(j->rr);
949
950 r = dns_resource_record_clamp_ttl(&rr, LESS_BY(j->until, current) / USEC_PER_SEC);
951 if (r < 0)
952 return r;
953 }
954
955 r = dns_answer_add(answer, rr ?: j->rr, j->ifindex, j->authenticated ? DNS_ANSWER_AUTHENTICATED : 0);
f52e61da
LP
956 if (r < 0)
957 return r;
322345fd
LP
958 }
959
a150ff5e
LP
960 c->n_hit++;
961
faa133f3 962 *ret = answer;
7e8e0422 963 *rcode = DNS_RCODE_SUCCESS;
931851e8 964 *authenticated = have_authenticated && !have_non_authenticated;
faa133f3
LP
965 answer = NULL;
966
967 return n;
322345fd 968}
a4076574
LP
969
970int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) {
971 DnsCacheItem *i, *first;
972 bool same_owner = true;
973
974 assert(cache);
975 assert(rr);
976
977 dns_cache_prune(cache);
978
979 /* See if there's a cache entry for the same key. If there
980 * isn't there's no conflict */
981 first = hashmap_get(cache->by_key, rr->key);
982 if (!first)
983 return 0;
984
985 /* See if the RR key is owned by the same owner, if so, there
986 * isn't a conflict either */
987 LIST_FOREACH(by_key, i, first) {
988 if (i->owner_family != owner_family ||
989 !in_addr_equal(owner_family, &i->owner_address, owner_address)) {
990 same_owner = false;
991 break;
992 }
993 }
994 if (same_owner)
995 return 0;
996
997 /* See if there's the exact same RR in the cache. If yes, then
998 * there's no conflict. */
999 if (dns_cache_get(cache, rr))
1000 return 0;
1001
1002 /* There's a conflict */
1003 return 1;
1004}
4d506d6b 1005
7778dfff
DM
1006int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
1007 unsigned ancount = 0;
1008 Iterator iterator;
1009 DnsCacheItem *i;
1010 int r;
1011
1012 assert(cache);
261f3673 1013 assert(p);
7778dfff
DM
1014
1015 HASHMAP_FOREACH(i, cache->by_key, iterator) {
1016 DnsCacheItem *j;
1017
1018 LIST_FOREACH(by_key, j, i) {
7778dfff
DM
1019 if (!j->rr)
1020 continue;
1021
d2579eec 1022 if (!j->shared_owner)
7778dfff
DM
1023 continue;
1024
58ab31d5 1025 r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
261f3673
DM
1026 if (r == -EMSGSIZE && p->protocol == DNS_PROTOCOL_MDNS) {
1027 /* For mDNS, if we're unable to stuff all known answers into the given packet,
1028 * allocate a new one, push the RR into that one and link it to the current one.
1029 */
1030
1031 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
1032 ancount = 0;
1033
1034 r = dns_packet_new_query(&p->more, p->protocol, 0, true);
1035 if (r < 0)
1036 return r;
1037
1038 /* continue with new packet */
1039 p = p->more;
58ab31d5 1040 r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
261f3673
DM
1041 }
1042
7778dfff
DM
1043 if (r < 0)
1044 return r;
1045
313cefa1 1046 ancount++;
7778dfff
DM
1047 }
1048 }
1049
1050 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
1051
1052 return 0;
1053}
1054
4d506d6b
LP
1055void dns_cache_dump(DnsCache *cache, FILE *f) {
1056 Iterator iterator;
1057 DnsCacheItem *i;
4d506d6b
LP
1058
1059 if (!cache)
1060 return;
1061
1062 if (!f)
1063 f = stdout;
1064
1065 HASHMAP_FOREACH(i, cache->by_key, iterator) {
1066 DnsCacheItem *j;
1067
1068 LIST_FOREACH(by_key, j, i) {
4d506d6b
LP
1069
1070 fputc('\t', f);
1071
1072 if (j->rr) {
7b50eb2e
LP
1073 const char *t;
1074 t = dns_resource_record_to_string(j->rr);
1075 if (!t) {
4d506d6b
LP
1076 log_oom();
1077 continue;
1078 }
1079
1080 fputs(t, f);
1081 fputc('\n', f);
1082 } else {
202b76ae 1083 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
4d506d6b 1084
202b76ae 1085 fputs(dns_resource_key_to_string(j->key, key_str, sizeof key_str), f);
4d506d6b 1086 fputs(" -- ", f);
201d9958 1087 fputs(dns_cache_item_type_to_string(j), f);
4d506d6b
LP
1088 fputc('\n', f);
1089 }
1090 }
1091 }
1092}
1093
1094bool dns_cache_is_empty(DnsCache *cache) {
1095 if (!cache)
1096 return true;
1097
1098 return hashmap_isempty(cache->by_key);
1099}
a150ff5e
LP
1100
1101unsigned dns_cache_size(DnsCache *cache) {
1102 if (!cache)
1103 return 0;
1104
1105 return hashmap_size(cache->by_key);
1106}