]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-cache.c
resolved: make use of dns_{class|type}_is_{pseudo|valid_rr}() everywhere
[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 1K entries */
30 #define CACHE_MAX 1024
31
32 /* We never keep any item longer than 10min in our cache */
33 #define CACHE_TTL_MAX_USEC (10 * USEC_PER_MINUTE)
34
35 typedef enum DnsCacheItemType DnsCacheItemType;
36 typedef struct DnsCacheItem DnsCacheItem;
37
38 enum DnsCacheItemType {
39 DNS_CACHE_POSITIVE,
40 DNS_CACHE_NODATA,
41 DNS_CACHE_NXDOMAIN,
42 };
43
44 struct DnsCacheItem {
45 DnsResourceKey *key;
46 DnsResourceRecord *rr;
47 usec_t until;
48 DnsCacheItemType type;
49 unsigned prioq_idx;
50 bool authenticated;
51 int owner_family;
52 union in_addr_union owner_address;
53 LIST_FIELDS(DnsCacheItem, by_key);
54 };
55
56 static void dns_cache_item_free(DnsCacheItem *i) {
57 if (!i)
58 return;
59
60 dns_resource_record_unref(i->rr);
61 dns_resource_key_unref(i->key);
62 free(i);
63 }
64
65 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem*, dns_cache_item_free);
66
67 static void dns_cache_item_remove_and_free(DnsCache *c, DnsCacheItem *i) {
68 DnsCacheItem *first;
69
70 assert(c);
71
72 if (!i)
73 return;
74
75 first = hashmap_get(c->by_key, i->key);
76 LIST_REMOVE(by_key, first, i);
77
78 if (first)
79 assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
80 else
81 hashmap_remove(c->by_key, i->key);
82
83 prioq_remove(c->by_expiry, i, &i->prioq_idx);
84
85 dns_cache_item_free(i);
86 }
87
88 void dns_cache_flush(DnsCache *c) {
89 DnsCacheItem *i;
90
91 assert(c);
92
93 while ((i = hashmap_first(c->by_key)))
94 dns_cache_item_remove_and_free(c, i);
95
96 assert(hashmap_size(c->by_key) == 0);
97 assert(prioq_size(c->by_expiry) == 0);
98
99 c->by_key = hashmap_free(c->by_key);
100 c->by_expiry = prioq_free(c->by_expiry);
101 }
102
103 static bool dns_cache_remove(DnsCache *c, DnsResourceKey *key) {
104 DnsCacheItem *i;
105 bool exist = false;
106
107 assert(c);
108 assert(key);
109
110 while ((i = hashmap_get(c->by_key, key))) {
111 dns_cache_item_remove_and_free(c, i);
112 exist = true;
113 }
114
115 return exist;
116 }
117
118 static void dns_cache_make_space(DnsCache *c, unsigned add) {
119 assert(c);
120
121 if (add <= 0)
122 return;
123
124 /* Makes space for n new entries. Note that we actually allow
125 * the cache to grow beyond CACHE_MAX, but only when we shall
126 * add more RRs to the cache than CACHE_MAX at once. In that
127 * case the cache will be emptied completely otherwise. */
128
129 for (;;) {
130 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
131 DnsCacheItem *i;
132
133 if (prioq_size(c->by_expiry) <= 0)
134 break;
135
136 if (prioq_size(c->by_expiry) + add < CACHE_MAX)
137 break;
138
139 i = prioq_peek(c->by_expiry);
140 assert(i);
141
142 /* Take an extra reference to the key so that it
143 * doesn't go away in the middle of the remove call */
144 key = dns_resource_key_ref(i->key);
145 dns_cache_remove(c, key);
146 }
147 }
148
149 void dns_cache_prune(DnsCache *c) {
150 usec_t t = 0;
151
152 assert(c);
153
154 /* Remove all entries that are past their TTL */
155
156 for (;;) {
157 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
158 DnsCacheItem *i;
159
160 i = prioq_peek(c->by_expiry);
161 if (!i)
162 break;
163
164 if (t <= 0)
165 t = now(clock_boottime_or_monotonic());
166
167 if (i->until > t)
168 break;
169
170 /* Take an extra reference to the key so that it
171 * doesn't go away in the middle of the remove call */
172 key = dns_resource_key_ref(i->key);
173 dns_cache_remove(c, key);
174 }
175 }
176
177 static int dns_cache_item_prioq_compare_func(const void *a, const void *b) {
178 const DnsCacheItem *x = a, *y = b;
179
180 if (x->until < y->until)
181 return -1;
182 if (x->until > y->until)
183 return 1;
184 return 0;
185 }
186
187 static int dns_cache_init(DnsCache *c) {
188 int r;
189
190 assert(c);
191
192 r = prioq_ensure_allocated(&c->by_expiry, dns_cache_item_prioq_compare_func);
193 if (r < 0)
194 return r;
195
196 r = hashmap_ensure_allocated(&c->by_key, &dns_resource_key_hash_ops);
197 if (r < 0)
198 return r;
199
200 return r;
201 }
202
203 static int dns_cache_link_item(DnsCache *c, DnsCacheItem *i) {
204 DnsCacheItem *first;
205 int r;
206
207 assert(c);
208 assert(i);
209
210 r = prioq_put(c->by_expiry, i, &i->prioq_idx);
211 if (r < 0)
212 return r;
213
214 first = hashmap_get(c->by_key, i->key);
215 if (first) {
216 LIST_PREPEND(by_key, first, i);
217 assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
218 } else {
219 r = hashmap_put(c->by_key, i->key, i);
220 if (r < 0) {
221 prioq_remove(c->by_expiry, i, &i->prioq_idx);
222 return r;
223 }
224 }
225
226 return 0;
227 }
228
229 static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
230 DnsCacheItem *i;
231
232 assert(c);
233 assert(rr);
234
235 LIST_FOREACH(by_key, i, hashmap_get(c->by_key, rr->key))
236 if (i->rr && dns_resource_record_equal(i->rr, rr) > 0)
237 return i;
238
239 return NULL;
240 }
241
242 static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsResourceRecord *rr, bool authenticated, usec_t timestamp) {
243 assert(c);
244 assert(i);
245 assert(rr);
246
247 i->type = DNS_CACHE_POSITIVE;
248
249 if (!i->by_key_prev)
250 /* We are the first item in the list, we need to
251 * update the key used in the hashmap */
252
253 assert_se(hashmap_replace(c->by_key, rr->key, i) >= 0);
254
255 dns_resource_record_ref(rr);
256 dns_resource_record_unref(i->rr);
257 i->rr = rr;
258
259 dns_resource_key_unref(i->key);
260 i->key = dns_resource_key_ref(rr->key);
261
262 i->authenticated = authenticated;
263 i->until = timestamp + MIN(rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
264
265 prioq_reshuffle(c->by_expiry, i, &i->prioq_idx);
266 }
267
268 static int dns_cache_put_positive(
269 DnsCache *c,
270 DnsResourceRecord *rr,
271 bool authenticated,
272 usec_t timestamp,
273 int owner_family,
274 const union in_addr_union *owner_address) {
275
276 _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
277 _cleanup_free_ char *key_str = NULL;
278 DnsCacheItem *existing;
279 int r, k;
280
281 assert(c);
282 assert(rr);
283 assert(owner_address);
284
285 /* Never cache pseudo RRs */
286 if (dns_class_is_pseudo(rr->key->class))
287 return 0;
288 if (dns_type_is_pseudo(rr->key->type))
289 return 0;
290
291 /* New TTL is 0? Delete the entry... */
292 if (rr->ttl <= 0) {
293 k = dns_cache_remove(c, rr->key);
294
295 if (log_get_max_level() >= LOG_DEBUG) {
296 r = dns_resource_key_to_string(rr->key, &key_str);
297 if (r < 0)
298 return r;
299
300 if (k > 0)
301 log_debug("Removed zero TTL entry from cache: %s", key_str);
302 else
303 log_debug("Not caching zero TTL cache entry: %s", key_str);
304 }
305
306 return 0;
307 }
308
309 /* Entry exists already? Update TTL and timestamp */
310 existing = dns_cache_get(c, rr);
311 if (existing) {
312 dns_cache_item_update_positive(c, existing, rr, authenticated, timestamp);
313 return 0;
314 }
315
316 /* Otherwise, add the new RR */
317 r = dns_cache_init(c);
318 if (r < 0)
319 return r;
320
321 dns_cache_make_space(c, 1);
322
323 i = new0(DnsCacheItem, 1);
324 if (!i)
325 return -ENOMEM;
326
327 i->type = DNS_CACHE_POSITIVE;
328 i->key = dns_resource_key_ref(rr->key);
329 i->rr = dns_resource_record_ref(rr);
330 i->until = timestamp + MIN(i->rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
331 i->prioq_idx = PRIOQ_IDX_NULL;
332 i->owner_family = owner_family;
333 i->owner_address = *owner_address;
334 i->authenticated = authenticated;
335
336 r = dns_cache_link_item(c, i);
337 if (r < 0)
338 return r;
339
340 if (log_get_max_level() >= LOG_DEBUG) {
341 r = dns_resource_key_to_string(i->key, &key_str);
342 if (r < 0)
343 return r;
344
345 log_debug("Added cache entry for %s", key_str);
346 }
347
348 i = NULL;
349 return 0;
350 }
351
352 static int dns_cache_put_negative(
353 DnsCache *c,
354 DnsResourceKey *key,
355 int rcode,
356 bool authenticated,
357 usec_t timestamp,
358 uint32_t soa_ttl,
359 int owner_family,
360 const union in_addr_union *owner_address) {
361
362 _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
363 _cleanup_free_ char *key_str = NULL;
364 int r;
365
366 assert(c);
367 assert(key);
368 assert(owner_address);
369
370 dns_cache_remove(c, key);
371
372 /* Never cache pseudo RR keys */
373 if (dns_class_is_pseudo(key->class))
374 return 0;
375 if (dns_type_is_pseudo(key->type))
376 /* DNS_TYPE_ANY is particularly important to filter
377 * out as we use this as a pseudo-type for NXDOMAIN
378 * entries */
379 return 0;
380
381 if (soa_ttl <= 0) {
382 if (log_get_max_level() >= LOG_DEBUG) {
383 r = dns_resource_key_to_string(key, &key_str);
384 if (r < 0)
385 return r;
386
387 log_debug("Not caching negative entry with zero SOA TTL: %s", key_str);
388 }
389
390 return 0;
391 }
392
393 if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
394 return 0;
395
396 r = dns_cache_init(c);
397 if (r < 0)
398 return r;
399
400 dns_cache_make_space(c, 1);
401
402 i = new0(DnsCacheItem, 1);
403 if (!i)
404 return -ENOMEM;
405
406 i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN;
407 i->until = timestamp + MIN(soa_ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
408 i->prioq_idx = PRIOQ_IDX_NULL;
409 i->owner_family = owner_family;
410 i->owner_address = *owner_address;
411 i->authenticated = authenticated;
412
413 if (i->type == DNS_CACHE_NXDOMAIN) {
414 /* NXDOMAIN entries should apply equally to all types, so we use ANY as
415 * a pseudo type for this purpose here. */
416 i->key = dns_resource_key_new(key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(key));
417 if (!i->key)
418 return -ENOMEM;
419 } else
420 i->key = dns_resource_key_ref(key);
421
422 r = dns_cache_link_item(c, i);
423 if (r < 0)
424 return r;
425
426 if (log_get_max_level() >= LOG_DEBUG) {
427 r = dns_resource_key_to_string(i->key, &key_str);
428 if (r < 0)
429 return r;
430
431 log_debug("Added %s cache entry for %s", i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", key_str);
432 }
433
434 i = NULL;
435 return 0;
436 }
437
438 int dns_cache_put(
439 DnsCache *c,
440 DnsResourceKey *key,
441 int rcode,
442 DnsAnswer *answer,
443 bool authenticated,
444 usec_t timestamp,
445 int owner_family,
446 const union in_addr_union *owner_address) {
447
448 DnsResourceRecord *soa = NULL, *rr;
449 DnsAnswerFlags flags;
450 unsigned cache_keys;
451 int r;
452
453 assert(c);
454
455 if (key) {
456 /* First, if we were passed a key, delete all matching old RRs,
457 * so that we only keep complete by_key in place. */
458 dns_cache_remove(c, key);
459 }
460
461 if (!answer) {
462 if (log_get_max_level() >= LOG_DEBUG) {
463 _cleanup_free_ char *key_str = NULL;
464
465 r = dns_resource_key_to_string(key, &key_str);
466 if (r < 0)
467 return r;
468
469 log_debug("Not caching negative entry without a SOA record: %s", key_str);
470 }
471
472 return 0;
473 }
474
475 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
476 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
477 continue;
478
479 if (rr->key->cache_flush)
480 dns_cache_remove(c, rr->key);
481 }
482
483 /* We only care for positive replies and NXDOMAINs, on all
484 * other replies we will simply flush the respective entries,
485 * and that's it */
486
487 if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
488 return 0;
489
490 cache_keys = answer->n_rrs;
491 if (key)
492 cache_keys ++;
493
494 /* Make some space for our new entries */
495 dns_cache_make_space(c, cache_keys);
496
497 if (timestamp <= 0)
498 timestamp = now(clock_boottime_or_monotonic());
499
500 /* Second, add in positive entries for all contained RRs */
501
502 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
503 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
504 continue;
505
506 r = dns_cache_put_positive(c, rr, flags & DNS_ANSWER_AUTHENTICATED, timestamp, owner_family, owner_address);
507 if (r < 0)
508 goto fail;
509 }
510
511 if (!key)
512 return 0;
513
514 /* Third, add in negative entries if the key has no RR */
515 r = dns_answer_match_key(answer, key, NULL);
516 if (r < 0)
517 goto fail;
518 if (r > 0)
519 return 0;
520
521 /* But not if it has a matching CNAME/DNAME (the negative
522 * caching will be done on the canonical name, not on the
523 * alias) */
524 r = dns_answer_find_cname_or_dname(answer, key, NULL, NULL);
525 if (r < 0)
526 goto fail;
527 if (r > 0)
528 return 0;
529
530 /* See https://tools.ietf.org/html/rfc2308, which say that a
531 * matching SOA record in the packet is used to to enable
532 * negative caching. */
533
534 r = dns_answer_find_soa(answer, key, &soa);
535 if (r < 0)
536 goto fail;
537 if (r == 0)
538 return 0;
539
540 r = dns_cache_put_negative(c, key, rcode, authenticated, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
541 if (r < 0)
542 goto fail;
543
544 return 0;
545
546 fail:
547 /* Adding all RRs failed. Let's clean up what we already
548 * added, just in case */
549
550 if (key)
551 dns_cache_remove(c, key);
552
553 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
554 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
555 continue;
556
557 dns_cache_remove(c, rr->key);
558 }
559
560 return r;
561 }
562
563 static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, DnsResourceKey *k) {
564 DnsCacheItem *i;
565 const char *n;
566 int r;
567
568 assert(c);
569 assert(k);
570
571 /* If we hit some OOM error, or suchlike, we don't care too
572 * much, after all this is just a cache */
573
574 i = hashmap_get(c->by_key, k);
575 if (i)
576 return i;
577
578 n = DNS_RESOURCE_KEY_NAME(k);
579
580 /* Check if we have an NXDOMAIN cache item for the name, notice that we use
581 * the pseudo-type ANY for NXDOMAIN cache items. */
582 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_ANY, n));
583 if (i && i->type == DNS_CACHE_NXDOMAIN)
584 return i;
585
586 /* The following record types should never be redirected. See
587 * <https://tools.ietf.org/html/rfc4035#section-2.5>. */
588 if (!IN_SET(k->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME,
589 DNS_TYPE_NSEC3, DNS_TYPE_NSEC, DNS_TYPE_RRSIG,
590 DNS_TYPE_NXT, DNS_TYPE_SIG, DNS_TYPE_KEY)) {
591 /* Check if we have a CNAME record instead */
592 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_CNAME, n));
593 if (i)
594 return i;
595
596 /* OK, let's look for cached DNAME records. */
597 for (;;) {
598 char label[DNS_LABEL_MAX];
599
600 if (isempty(n))
601 return NULL;
602
603 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_DNAME, n));
604 if (i)
605 return i;
606
607 /* Jump one label ahead */
608 r = dns_label_unescape(&n, label, sizeof(label));
609 if (r <= 0)
610 return NULL;
611 }
612 }
613
614 if (k-> type != DNS_TYPE_NSEC) {
615 /* Check if we have an NSEC record instead for the name. */
616 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_NSEC, n));
617 if (i)
618 return i;
619 }
620
621 return NULL;
622 }
623
624 int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret, bool *authenticated) {
625 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
626 unsigned n = 0;
627 int r;
628 bool nxdomain = false;
629 _cleanup_free_ char *key_str = NULL;
630 DnsCacheItem *j, *first, *nsec = NULL;
631 bool have_authenticated = false, have_non_authenticated = false;
632
633 assert(c);
634 assert(key);
635 assert(rcode);
636 assert(ret);
637 assert(authenticated);
638
639 if (key->type == DNS_TYPE_ANY ||
640 key->class == DNS_CLASS_ANY) {
641
642 /* If we have ANY lookups we don't use the cache, so
643 * that the caller refreshes via the network. */
644
645 if (log_get_max_level() >= LOG_DEBUG) {
646 r = dns_resource_key_to_string(key, &key_str);
647 if (r < 0)
648 return r;
649
650 log_debug("Ignoring cache for ANY lookup: %s", key_str);
651 }
652
653 *ret = NULL;
654 *rcode = DNS_RCODE_SUCCESS;
655 return 0;
656 }
657
658 first = dns_cache_get_by_key_follow_cname_dname_nsec(c, key);
659 if (!first) {
660 /* If one question cannot be answered we need to refresh */
661
662 if (log_get_max_level() >= LOG_DEBUG) {
663 r = dns_resource_key_to_string(key, &key_str);
664 if (r < 0)
665 return r;
666
667 log_debug("Cache miss for %s", key_str);
668 }
669
670 *ret = NULL;
671 *rcode = DNS_RCODE_SUCCESS;
672 return 0;
673 }
674
675 LIST_FOREACH(by_key, j, first) {
676 if (j->rr) {
677 if (j->rr->key->type == DNS_TYPE_NSEC)
678 nsec = j;
679
680 n++;
681 } else if (j->type == DNS_CACHE_NXDOMAIN)
682 nxdomain = true;
683
684 if (j->authenticated)
685 have_authenticated = true;
686 else
687 have_non_authenticated = true;
688 }
689
690 if (nsec && key->type != DNS_TYPE_NSEC) {
691 if (log_get_max_level() >= LOG_DEBUG) {
692 r = dns_resource_key_to_string(key, &key_str);
693 if (r < 0)
694 return r;
695
696 log_debug("NSEC NODATA cache hit for %s", key_str);
697 }
698
699 /* We only found an NSEC record that matches our name.
700 * If it says the type doesn't exist report
701 * NODATA. Otherwise report a cache miss. */
702
703 *ret = NULL;
704 *rcode = DNS_RCODE_SUCCESS;
705 *authenticated = nsec->authenticated;
706
707 return !bitmap_isset(nsec->rr->nsec.types, key->type) &&
708 !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
709 !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME);
710 }
711
712 if (log_get_max_level() >= LOG_DEBUG) {
713 r = dns_resource_key_to_string(key, &key_str);
714 if (r < 0)
715 return r;
716
717 log_debug("%s cache hit for %s",
718 n > 0 ? "Positive" :
719 nxdomain ? "NXDOMAIN" : "NODATA",
720 key_str);
721 }
722
723 if (n <= 0) {
724 *ret = NULL;
725 *rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
726 *authenticated = have_authenticated && !have_non_authenticated;
727 return 1;
728 }
729
730 answer = dns_answer_new(n);
731 if (!answer)
732 return -ENOMEM;
733
734 LIST_FOREACH(by_key, j, first) {
735 if (!j->rr)
736 continue;
737
738 r = dns_answer_add(answer, j->rr, 0, have_authenticated && !have_non_authenticated ? DNS_ANSWER_AUTHENTICATED : 0);
739 if (r < 0)
740 return r;
741 }
742
743 *ret = answer;
744 *rcode = DNS_RCODE_SUCCESS;
745 *authenticated = have_authenticated && !have_non_authenticated;
746 answer = NULL;
747
748 return n;
749 }
750
751 int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) {
752 DnsCacheItem *i, *first;
753 bool same_owner = true;
754
755 assert(cache);
756 assert(rr);
757
758 dns_cache_prune(cache);
759
760 /* See if there's a cache entry for the same key. If there
761 * isn't there's no conflict */
762 first = hashmap_get(cache->by_key, rr->key);
763 if (!first)
764 return 0;
765
766 /* See if the RR key is owned by the same owner, if so, there
767 * isn't a conflict either */
768 LIST_FOREACH(by_key, i, first) {
769 if (i->owner_family != owner_family ||
770 !in_addr_equal(owner_family, &i->owner_address, owner_address)) {
771 same_owner = false;
772 break;
773 }
774 }
775 if (same_owner)
776 return 0;
777
778 /* See if there's the exact same RR in the cache. If yes, then
779 * there's no conflict. */
780 if (dns_cache_get(cache, rr))
781 return 0;
782
783 /* There's a conflict */
784 return 1;
785 }
786
787 int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
788 unsigned ancount = 0;
789 Iterator iterator;
790 DnsCacheItem *i;
791 int r;
792
793 assert(cache);
794 assert(p);
795
796 HASHMAP_FOREACH(i, cache->by_key, iterator) {
797 DnsCacheItem *j;
798
799 LIST_FOREACH(by_key, j, i) {
800 if (!j->rr)
801 continue;
802
803 if (!dns_key_is_shared(j->rr->key))
804 continue;
805
806 r = dns_packet_append_rr(p, j->rr, NULL, NULL);
807 if (r == -EMSGSIZE && p->protocol == DNS_PROTOCOL_MDNS) {
808 /* For mDNS, if we're unable to stuff all known answers into the given packet,
809 * allocate a new one, push the RR into that one and link it to the current one.
810 */
811
812 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
813 ancount = 0;
814
815 r = dns_packet_new_query(&p->more, p->protocol, 0, true);
816 if (r < 0)
817 return r;
818
819 /* continue with new packet */
820 p = p->more;
821 r = dns_packet_append_rr(p, j->rr, NULL, NULL);
822 }
823
824 if (r < 0)
825 return r;
826
827 ancount ++;
828 }
829 }
830
831 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
832
833 return 0;
834 }
835
836 void dns_cache_dump(DnsCache *cache, FILE *f) {
837 Iterator iterator;
838 DnsCacheItem *i;
839 int r;
840
841 if (!cache)
842 return;
843
844 if (!f)
845 f = stdout;
846
847 HASHMAP_FOREACH(i, cache->by_key, iterator) {
848 DnsCacheItem *j;
849
850 LIST_FOREACH(by_key, j, i) {
851 _cleanup_free_ char *t = NULL;
852
853 fputc('\t', f);
854
855 if (j->rr) {
856 r = dns_resource_record_to_string(j->rr, &t);
857 if (r < 0) {
858 log_oom();
859 continue;
860 }
861
862 fputs(t, f);
863 fputc('\n', f);
864 } else {
865 r = dns_resource_key_to_string(j->key, &t);
866 if (r < 0) {
867 log_oom();
868 continue;
869 }
870
871 fputs(t, f);
872 fputs(" -- ", f);
873 fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f);
874 fputc('\n', f);
875 }
876 }
877 }
878 }
879
880 bool dns_cache_is_empty(DnsCache *cache) {
881 if (!cache)
882 return true;
883
884 return hashmap_isempty(cache->by_key);
885 }