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