]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-cache.c
resolved: cache stringified transaction key once per transaction
[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 /* New TTL is 0? Delete the entry... */
286 if (rr->ttl <= 0) {
287 k = dns_cache_remove(c, rr->key);
288
289 if (log_get_max_level() >= LOG_DEBUG) {
290 r = dns_resource_key_to_string(rr->key, &key_str);
291 if (r < 0)
292 return r;
293
294 if (k > 0)
295 log_debug("Removed zero TTL entry from cache: %s", key_str);
296 else
297 log_debug("Not caching zero TTL cache entry: %s", key_str);
298 }
299
300 return 0;
301 }
302
303 if (rr->key->class == DNS_CLASS_ANY)
304 return 0;
305 if (dns_type_is_pseudo(rr->key->type))
306 return 0;
307
308 /* Entry exists already? Update TTL and timestamp */
309 existing = dns_cache_get(c, rr);
310 if (existing) {
311 dns_cache_item_update_positive(c, existing, rr, authenticated, timestamp);
312 return 0;
313 }
314
315 /* Otherwise, add the new RR */
316 r = dns_cache_init(c);
317 if (r < 0)
318 return r;
319
320 dns_cache_make_space(c, 1);
321
322 i = new0(DnsCacheItem, 1);
323 if (!i)
324 return -ENOMEM;
325
326 i->type = DNS_CACHE_POSITIVE;
327 i->key = dns_resource_key_ref(rr->key);
328 i->rr = dns_resource_record_ref(rr);
329 i->until = timestamp + MIN(i->rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
330 i->prioq_idx = PRIOQ_IDX_NULL;
331 i->owner_family = owner_family;
332 i->owner_address = *owner_address;
333 i->authenticated = authenticated;
334
335 r = dns_cache_link_item(c, i);
336 if (r < 0)
337 return r;
338
339 if (log_get_max_level() >= LOG_DEBUG) {
340 r = dns_resource_key_to_string(i->key, &key_str);
341 if (r < 0)
342 return r;
343
344 log_debug("Added cache entry for %s", key_str);
345 }
346
347 i = NULL;
348 return 0;
349 }
350
351 static int dns_cache_put_negative(
352 DnsCache *c,
353 DnsResourceKey *key,
354 int rcode,
355 bool authenticated,
356 usec_t timestamp,
357 uint32_t soa_ttl,
358 int owner_family,
359 const union in_addr_union *owner_address) {
360
361 _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
362 _cleanup_free_ char *key_str = NULL;
363 int r;
364
365 assert(c);
366 assert(key);
367 assert(owner_address);
368
369 dns_cache_remove(c, key);
370
371 if (key->class == DNS_CLASS_ANY)
372 return 0;
373 if (dns_type_is_pseudo(key->type))
374 /* ANY is particularly important to filter out as we
375 * use this as a pseudo-type for NXDOMAIN entries */
376 return 0;
377 if (soa_ttl <= 0) {
378 if (log_get_max_level() >= LOG_DEBUG) {
379 r = dns_resource_key_to_string(key, &key_str);
380 if (r < 0)
381 return r;
382
383 log_debug("Not caching negative entry with zero SOA TTL: %s", key_str);
384 }
385
386 return 0;
387 }
388
389 if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
390 return 0;
391
392 r = dns_cache_init(c);
393 if (r < 0)
394 return r;
395
396 dns_cache_make_space(c, 1);
397
398 i = new0(DnsCacheItem, 1);
399 if (!i)
400 return -ENOMEM;
401
402 i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN;
403 i->until = timestamp + MIN(soa_ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
404 i->prioq_idx = PRIOQ_IDX_NULL;
405 i->owner_family = owner_family;
406 i->owner_address = *owner_address;
407 i->authenticated = authenticated;
408
409 if (i->type == DNS_CACHE_NXDOMAIN) {
410 /* NXDOMAIN entries should apply equally to all types, so we use ANY as
411 * a pseudo type for this purpose here. */
412 i->key = dns_resource_key_new(key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(key));
413 if (!i->key)
414 return -ENOMEM;
415 } else
416 i->key = dns_resource_key_ref(key);
417
418 r = dns_cache_link_item(c, i);
419 if (r < 0)
420 return r;
421
422 if (log_get_max_level() >= LOG_DEBUG) {
423 r = dns_resource_key_to_string(i->key, &key_str);
424 if (r < 0)
425 return r;
426
427 log_debug("Added %s cache entry for %s", i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", key_str);
428 }
429
430 i = NULL;
431 return 0;
432 }
433
434 int dns_cache_put(
435 DnsCache *c,
436 DnsResourceKey *key,
437 int rcode,
438 DnsAnswer *answer,
439 unsigned max_rrs,
440 bool authenticated,
441 usec_t timestamp,
442 int owner_family,
443 const union in_addr_union *owner_address) {
444
445 DnsResourceRecord *soa = NULL, *rr;
446 unsigned cache_keys, i;
447 int r;
448
449 assert(c);
450
451 if (key) {
452 /* First, if we were passed a key, delete all matching old RRs,
453 * so that we only keep complete by_key in place. */
454 dns_cache_remove(c, key);
455 }
456
457 if (!answer) {
458 if (log_get_max_level() >= LOG_DEBUG) {
459 _cleanup_free_ char *key_str = NULL;
460
461 r = dns_resource_key_to_string(key, &key_str);
462 if (r < 0)
463 return r;
464
465 log_debug("Not caching negative entry without a SOA record: %s", key_str);
466 }
467
468 return 0;
469 }
470
471 DNS_ANSWER_FOREACH(rr, answer)
472 if (rr->key->cache_flush)
473 dns_cache_remove(c, rr->key);
474
475 /* We only care for positive replies and NXDOMAINs, on all
476 * other replies we will simply flush the respective entries,
477 * and that's it */
478
479 if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
480 return 0;
481
482 cache_keys = answer->n_rrs;
483
484 if (key)
485 cache_keys ++;
486
487 /* Make some space for our new entries */
488 dns_cache_make_space(c, cache_keys);
489
490 if (timestamp <= 0)
491 timestamp = now(clock_boottime_or_monotonic());
492
493 /* Second, add in positive entries for all contained RRs */
494 for (i = 0; i < MIN(max_rrs, answer->n_rrs); i++) {
495 rr = answer->items[i].rr;
496
497 r = dns_cache_put_positive(c, rr, authenticated, timestamp, owner_family, owner_address);
498 if (r < 0)
499 goto fail;
500 }
501
502 if (!key)
503 return 0;
504
505 /* Third, add in negative entries if the key has no RR */
506 r = dns_answer_match_key(answer, key);
507 if (r < 0)
508 goto fail;
509 if (r > 0)
510 return 0;
511
512 /* But not if it has a matching CNAME/DNAME (the negative
513 * caching will be done on the canonical name, not on the
514 * alias) */
515 r = dns_answer_find_cname_or_dname(answer, key, NULL);
516 if (r < 0)
517 goto fail;
518 if (r > 0)
519 return 0;
520
521 /* See https://tools.ietf.org/html/rfc2308, which say that a
522 * matching SOA record in the packet is used to to enable
523 * negative caching. */
524
525 r = dns_answer_find_soa(answer, key, &soa);
526 if (r < 0)
527 goto fail;
528 if (r == 0)
529 return 0;
530
531 r = dns_cache_put_negative(c, key, rcode, authenticated, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
532 if (r < 0)
533 goto fail;
534
535 return 0;
536
537 fail:
538 /* Adding all RRs failed. Let's clean up what we already
539 * added, just in case */
540
541 if (key)
542 dns_cache_remove(c, key);
543
544 for (i = 0; i < answer->n_rrs; i++)
545 dns_cache_remove(c, answer->items[i].rr->key);
546
547 return r;
548 }
549
550 static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, DnsResourceKey *k) {
551 DnsCacheItem *i;
552 const char *n;
553 int r;
554
555 assert(c);
556 assert(k);
557
558 /* If we hit some OOM error, or suchlike, we don't care too
559 * much, after all this is just a cache */
560
561 i = hashmap_get(c->by_key, k);
562 if (i)
563 return i;
564
565 n = DNS_RESOURCE_KEY_NAME(k);
566
567 /* Check if we have an NXDOMAIN cache item for the name, notice that we use
568 * the pseudo-type ANY for NXDOMAIN cache items. */
569 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_ANY, n));
570 if (i && i->type == DNS_CACHE_NXDOMAIN)
571 return i;
572
573 /* The following record types should never be redirected. See
574 * <https://tools.ietf.org/html/rfc4035#section-2.5>. */
575 if (!IN_SET(k->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME,
576 DNS_TYPE_NSEC3, DNS_TYPE_NSEC, DNS_TYPE_RRSIG,
577 DNS_TYPE_NXT, DNS_TYPE_SIG, DNS_TYPE_KEY)) {
578 /* Check if we have a CNAME record instead */
579 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_CNAME, n));
580 if (i)
581 return i;
582
583 /* OK, let's look for cached DNAME records. */
584 for (;;) {
585 char label[DNS_LABEL_MAX];
586
587 if (isempty(n))
588 return NULL;
589
590 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_DNAME, n));
591 if (i)
592 return i;
593
594 /* Jump one label ahead */
595 r = dns_label_unescape(&n, label, sizeof(label));
596 if (r <= 0)
597 return NULL;
598 }
599 }
600
601 if (k-> type != DNS_TYPE_NSEC) {
602 /* Check if we have an NSEC record instead for the name. */
603 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_NSEC, n));
604 if (i)
605 return i;
606 }
607
608 return NULL;
609 }
610
611 int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret, bool *authenticated) {
612 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
613 unsigned n = 0;
614 int r;
615 bool nxdomain = false;
616 _cleanup_free_ char *key_str = NULL;
617 DnsCacheItem *j, *first, *nsec = NULL;
618 bool have_authenticated = false, have_non_authenticated = false;
619
620 assert(c);
621 assert(key);
622 assert(rcode);
623 assert(ret);
624 assert(authenticated);
625
626 if (key->type == DNS_TYPE_ANY ||
627 key->class == DNS_CLASS_ANY) {
628
629 /* If we have ANY lookups we don't use the cache, so
630 * that the caller refreshes via the network. */
631
632 if (log_get_max_level() >= LOG_DEBUG) {
633 r = dns_resource_key_to_string(key, &key_str);
634 if (r < 0)
635 return r;
636
637 log_debug("Ignoring cache for ANY lookup: %s", key_str);
638 }
639
640 *ret = NULL;
641 *rcode = DNS_RCODE_SUCCESS;
642 return 0;
643 }
644
645 first = dns_cache_get_by_key_follow_cname_dname_nsec(c, key);
646 if (!first) {
647 /* If one question cannot be answered we need to refresh */
648
649 if (log_get_max_level() >= LOG_DEBUG) {
650 r = dns_resource_key_to_string(key, &key_str);
651 if (r < 0)
652 return r;
653
654 log_debug("Cache miss for %s", key_str);
655 }
656
657 *ret = NULL;
658 *rcode = DNS_RCODE_SUCCESS;
659 return 0;
660 }
661
662 LIST_FOREACH(by_key, j, first) {
663 if (j->rr) {
664 if (j->rr->key->type == DNS_TYPE_NSEC)
665 nsec = j;
666
667 n++;
668 } else if (j->type == DNS_CACHE_NXDOMAIN)
669 nxdomain = true;
670
671 if (j->authenticated)
672 have_authenticated = true;
673 else
674 have_non_authenticated = true;
675 }
676
677 if (nsec && key->type != DNS_TYPE_NSEC) {
678 if (log_get_max_level() >= LOG_DEBUG) {
679 r = dns_resource_key_to_string(key, &key_str);
680 if (r < 0)
681 return r;
682
683 log_debug("NSEC NODATA cache hit for %s", key_str);
684 }
685
686 /* We only found an NSEC record that matches our name.
687 * If it says the type doesn't exist report
688 * NODATA. Otherwise report a cache miss. */
689
690 *ret = NULL;
691 *rcode = DNS_RCODE_SUCCESS;
692 *authenticated = nsec->authenticated;
693
694 return !bitmap_isset(nsec->rr->nsec.types, key->type) &&
695 !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
696 !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME);
697 }
698
699 if (log_get_max_level() >= LOG_DEBUG) {
700 r = dns_resource_key_to_string(key, &key_str);
701 if (r < 0)
702 return r;
703
704 log_debug("%s cache hit for %s",
705 n > 0 ? "Positive" :
706 nxdomain ? "NXDOMAIN" : "NODATA",
707 key_str);
708 }
709
710 if (n <= 0) {
711 *ret = NULL;
712 *rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
713 *authenticated = have_authenticated && !have_non_authenticated;
714 return 1;
715 }
716
717 answer = dns_answer_new(n);
718 if (!answer)
719 return -ENOMEM;
720
721 LIST_FOREACH(by_key, j, first) {
722 if (!j->rr)
723 continue;
724
725 r = dns_answer_add(answer, j->rr, 0);
726 if (r < 0)
727 return r;
728 }
729
730 *ret = answer;
731 *rcode = DNS_RCODE_SUCCESS;
732 *authenticated = have_authenticated && !have_non_authenticated;
733 answer = NULL;
734
735 return n;
736 }
737
738 int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) {
739 DnsCacheItem *i, *first;
740 bool same_owner = true;
741
742 assert(cache);
743 assert(rr);
744
745 dns_cache_prune(cache);
746
747 /* See if there's a cache entry for the same key. If there
748 * isn't there's no conflict */
749 first = hashmap_get(cache->by_key, rr->key);
750 if (!first)
751 return 0;
752
753 /* See if the RR key is owned by the same owner, if so, there
754 * isn't a conflict either */
755 LIST_FOREACH(by_key, i, first) {
756 if (i->owner_family != owner_family ||
757 !in_addr_equal(owner_family, &i->owner_address, owner_address)) {
758 same_owner = false;
759 break;
760 }
761 }
762 if (same_owner)
763 return 0;
764
765 /* See if there's the exact same RR in the cache. If yes, then
766 * there's no conflict. */
767 if (dns_cache_get(cache, rr))
768 return 0;
769
770 /* There's a conflict */
771 return 1;
772 }
773
774 int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
775 unsigned ancount = 0;
776 Iterator iterator;
777 DnsCacheItem *i;
778 int r;
779
780 assert(cache);
781 assert(p);
782
783 HASHMAP_FOREACH(i, cache->by_key, iterator) {
784 DnsCacheItem *j;
785
786 LIST_FOREACH(by_key, j, i) {
787 if (!j->rr)
788 continue;
789
790 if (!dns_key_is_shared(j->rr->key))
791 continue;
792
793 r = dns_packet_append_rr(p, j->rr, NULL, NULL);
794 if (r == -EMSGSIZE && p->protocol == DNS_PROTOCOL_MDNS) {
795 /* For mDNS, if we're unable to stuff all known answers into the given packet,
796 * allocate a new one, push the RR into that one and link it to the current one.
797 */
798
799 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
800 ancount = 0;
801
802 r = dns_packet_new_query(&p->more, p->protocol, 0, true);
803 if (r < 0)
804 return r;
805
806 /* continue with new packet */
807 p = p->more;
808 r = dns_packet_append_rr(p, j->rr, NULL, NULL);
809 }
810
811 if (r < 0)
812 return r;
813
814 ancount ++;
815 }
816 }
817
818 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
819
820 return 0;
821 }
822
823 void dns_cache_dump(DnsCache *cache, FILE *f) {
824 Iterator iterator;
825 DnsCacheItem *i;
826 int r;
827
828 if (!cache)
829 return;
830
831 if (!f)
832 f = stdout;
833
834 HASHMAP_FOREACH(i, cache->by_key, iterator) {
835 DnsCacheItem *j;
836
837 LIST_FOREACH(by_key, j, i) {
838 _cleanup_free_ char *t = NULL;
839
840 fputc('\t', f);
841
842 if (j->rr) {
843 r = dns_resource_record_to_string(j->rr, &t);
844 if (r < 0) {
845 log_oom();
846 continue;
847 }
848
849 fputs(t, f);
850 fputc('\n', f);
851 } else {
852 r = dns_resource_key_to_string(j->key, &t);
853 if (r < 0) {
854 log_oom();
855 continue;
856 }
857
858 fputs(t, f);
859 fputs(" -- ", f);
860 fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f);
861 fputc('\n', f);
862 }
863 }
864 }
865 }
866
867 bool dns_cache_is_empty(DnsCache *cache) {
868 if (!cache)
869 return true;
870
871 return hashmap_isempty(cache->by_key);
872 }