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