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