1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
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.
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.
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/>.
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"
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
33 /* We never keep any item longer than 2h in our cache */
34 #define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR)
36 typedef enum DnsCacheItemType DnsCacheItemType
;
37 typedef struct DnsCacheItem DnsCacheItem
;
39 enum DnsCacheItemType
{
46 DnsCacheItemType type
;
48 DnsResourceRecord
*rr
;
56 union in_addr_union owner_address
;
59 LIST_FIELDS(DnsCacheItem
, by_key
);
62 static void dns_cache_item_free(DnsCacheItem
*i
) {
66 dns_resource_record_unref(i
->rr
);
67 dns_resource_key_unref(i
->key
);
71 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem
*, dns_cache_item_free
);
73 static void dns_cache_item_unlink_and_free(DnsCache
*c
, DnsCacheItem
*i
) {
81 first
= hashmap_get(c
->by_key
, i
->key
);
82 LIST_REMOVE(by_key
, first
, i
);
85 assert_se(hashmap_replace(c
->by_key
, first
->key
, first
) >= 0);
87 hashmap_remove(c
->by_key
, i
->key
);
89 prioq_remove(c
->by_expiry
, i
, &i
->prioq_idx
);
91 dns_cache_item_free(i
);
94 static bool dns_cache_remove_by_rr(DnsCache
*c
, DnsResourceRecord
*rr
) {
95 DnsCacheItem
*first
, *i
;
98 first
= hashmap_get(c
->by_key
, rr
->key
);
99 LIST_FOREACH(by_key
, i
, first
) {
100 r
= dns_resource_record_equal(i
->rr
, rr
);
104 dns_cache_item_unlink_and_free(c
, i
);
112 static bool dns_cache_remove_by_key(DnsCache
*c
, DnsResourceKey
*key
) {
113 DnsCacheItem
*first
, *i
, *n
;
118 first
= hashmap_remove(c
->by_key
, key
);
122 LIST_FOREACH_SAFE(by_key
, i
, n
, first
) {
123 prioq_remove(c
->by_expiry
, i
, &i
->prioq_idx
);
124 dns_cache_item_free(i
);
130 void dns_cache_flush(DnsCache
*c
) {
135 while ((key
= hashmap_first_key(c
->by_key
)))
136 dns_cache_remove_by_key(c
, key
);
138 assert(hashmap_size(c
->by_key
) == 0);
139 assert(prioq_size(c
->by_expiry
) == 0);
141 c
->by_key
= hashmap_free(c
->by_key
);
142 c
->by_expiry
= prioq_free(c
->by_expiry
);
145 static void dns_cache_make_space(DnsCache
*c
, unsigned add
) {
151 /* Makes space for n new entries. Note that we actually allow
152 * the cache to grow beyond CACHE_MAX, but only when we shall
153 * add more RRs to the cache than CACHE_MAX at once. In that
154 * case the cache will be emptied completely otherwise. */
157 _cleanup_(dns_resource_key_unrefp
) DnsResourceKey
*key
= NULL
;
160 if (prioq_size(c
->by_expiry
) <= 0)
163 if (prioq_size(c
->by_expiry
) + add
< CACHE_MAX
)
166 i
= prioq_peek(c
->by_expiry
);
169 /* Take an extra reference to the key so that it
170 * doesn't go away in the middle of the remove call */
171 key
= dns_resource_key_ref(i
->key
);
172 dns_cache_remove_by_key(c
, key
);
176 void dns_cache_prune(DnsCache
*c
) {
181 /* Remove all entries that are past their TTL */
186 i
= prioq_peek(c
->by_expiry
);
191 t
= now(clock_boottime_or_monotonic());
196 /* Depending whether this is an mDNS shared entry
197 * either remove only this one RR or the whole
200 dns_cache_item_unlink_and_free(c
, i
);
202 _cleanup_(dns_resource_key_unrefp
) DnsResourceKey
*key
= NULL
;
204 /* Take an extra reference to the key so that it
205 * doesn't go away in the middle of the remove call */
206 key
= dns_resource_key_ref(i
->key
);
207 dns_cache_remove_by_key(c
, key
);
212 static int dns_cache_item_prioq_compare_func(const void *a
, const void *b
) {
213 const DnsCacheItem
*x
= a
, *y
= b
;
215 if (x
->until
< y
->until
)
217 if (x
->until
> y
->until
)
222 static int dns_cache_init(DnsCache
*c
) {
227 r
= prioq_ensure_allocated(&c
->by_expiry
, dns_cache_item_prioq_compare_func
);
231 r
= hashmap_ensure_allocated(&c
->by_key
, &dns_resource_key_hash_ops
);
238 static int dns_cache_link_item(DnsCache
*c
, DnsCacheItem
*i
) {
245 r
= prioq_put(c
->by_expiry
, i
, &i
->prioq_idx
);
249 first
= hashmap_get(c
->by_key
, i
->key
);
251 _cleanup_(dns_resource_key_unrefp
) DnsResourceKey
*k
= NULL
;
253 /* Keep a reference to the original key, while we manipulate the list. */
254 k
= dns_resource_key_ref(first
->key
);
256 /* Now, try to reduce the number of keys we keep */
257 dns_resource_key_reduce(&first
->key
, &i
->key
);
260 dns_resource_key_reduce(&first
->rr
->key
, &i
->key
);
262 dns_resource_key_reduce(&i
->rr
->key
, &i
->key
);
264 LIST_PREPEND(by_key
, first
, i
);
265 assert_se(hashmap_replace(c
->by_key
, first
->key
, first
) >= 0);
267 r
= hashmap_put(c
->by_key
, i
->key
, i
);
269 prioq_remove(c
->by_expiry
, i
, &i
->prioq_idx
);
277 static DnsCacheItem
* dns_cache_get(DnsCache
*c
, DnsResourceRecord
*rr
) {
283 LIST_FOREACH(by_key
, i
, hashmap_get(c
->by_key
, rr
->key
))
284 if (i
->rr
&& dns_resource_record_equal(i
->rr
, rr
) > 0)
290 static usec_t
calculate_until(DnsResourceRecord
*rr
, uint32_t nsec_ttl
, usec_t timestamp
, bool use_soa_minimum
) {
296 ttl
= MIN(rr
->ttl
, nsec_ttl
);
297 if (rr
->key
->type
== DNS_TYPE_SOA
&& use_soa_minimum
) {
298 /* If this is a SOA RR, and it is requested, clamp to
299 * the SOA's minimum field. This is used when we do
300 * negative caching, to determine the TTL for the
301 * negative caching entry. See RFC 2308, Section
304 if (ttl
> rr
->soa
.minimum
)
305 ttl
= rr
->soa
.minimum
;
308 u
= ttl
* USEC_PER_SEC
;
309 if (u
> CACHE_TTL_MAX_USEC
)
310 u
= CACHE_TTL_MAX_USEC
;
312 if (rr
->expiry
!= USEC_INFINITY
) {
315 /* Make use of the DNSSEC RRSIG expiry info, if we
318 left
= LESS_BY(rr
->expiry
, now(CLOCK_REALTIME
));
323 return timestamp
+ u
;
326 static void dns_cache_item_update_positive(
329 DnsResourceRecord
*rr
,
335 const union in_addr_union
*owner_address
) {
340 assert(owner_address
);
342 i
->type
= DNS_CACHE_POSITIVE
;
345 /* We are the first item in the list, we need to
346 * update the key used in the hashmap */
348 assert_se(hashmap_replace(c
->by_key
, rr
->key
, i
) >= 0);
350 dns_resource_record_ref(rr
);
351 dns_resource_record_unref(i
->rr
);
354 dns_resource_key_unref(i
->key
);
355 i
->key
= dns_resource_key_ref(rr
->key
);
357 i
->until
= calculate_until(rr
, (uint32_t) -1, timestamp
, false);
358 i
->authenticated
= authenticated
;
359 i
->shared_owner
= shared_owner
;
361 i
->ifindex
= ifindex
;
363 i
->owner_family
= owner_family
;
364 i
->owner_address
= *owner_address
;
366 prioq_reshuffle(c
->by_expiry
, i
, &i
->prioq_idx
);
369 static int dns_cache_put_positive(
371 DnsResourceRecord
*rr
,
377 const union in_addr_union
*owner_address
) {
379 _cleanup_(dns_cache_item_freep
) DnsCacheItem
*i
= NULL
;
380 _cleanup_free_
char *key_str
= NULL
;
381 DnsCacheItem
*existing
;
386 assert(owner_address
);
388 /* Never cache pseudo RRs */
389 if (dns_class_is_pseudo(rr
->key
->class))
391 if (dns_type_is_pseudo(rr
->key
->type
))
394 /* New TTL is 0? Delete this specific entry... */
396 k
= dns_cache_remove_by_rr(c
, rr
);
398 if (log_get_max_level() >= LOG_DEBUG
) {
399 r
= dns_resource_key_to_string(rr
->key
, &key_str
);
404 log_debug("Removed zero TTL entry from cache: %s", key_str
);
406 log_debug("Not caching zero TTL cache entry: %s", key_str
);
412 /* Entry exists already? Update TTL, timestamp and owner*/
413 existing
= dns_cache_get(c
, rr
);
415 dns_cache_item_update_positive(
428 /* Otherwise, add the new RR */
429 r
= dns_cache_init(c
);
433 dns_cache_make_space(c
, 1);
435 i
= new0(DnsCacheItem
, 1);
439 i
->type
= DNS_CACHE_POSITIVE
;
440 i
->key
= dns_resource_key_ref(rr
->key
);
441 i
->rr
= dns_resource_record_ref(rr
);
442 i
->until
= calculate_until(rr
, (uint32_t) -1, timestamp
, false);
443 i
->authenticated
= authenticated
;
444 i
->shared_owner
= shared_owner
;
445 i
->ifindex
= ifindex
;
446 i
->owner_family
= owner_family
;
447 i
->owner_address
= *owner_address
;
448 i
->prioq_idx
= PRIOQ_IDX_NULL
;
450 r
= dns_cache_link_item(c
, i
);
454 if (log_get_max_level() >= LOG_DEBUG
) {
455 r
= dns_resource_key_to_string(i
->key
, &key_str
);
459 log_debug("Added positive cache entry for %s", key_str
);
466 static int dns_cache_put_negative(
473 DnsResourceRecord
*soa
,
475 const union in_addr_union
*owner_address
) {
477 _cleanup_(dns_cache_item_freep
) DnsCacheItem
*i
= NULL
;
478 _cleanup_free_
char *key_str
= NULL
;
484 assert(owner_address
);
486 /* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
487 * important to filter out as we use this as a pseudo-type for
488 * NXDOMAIN entries */
489 if (dns_class_is_pseudo(key
->class))
491 if (dns_type_is_pseudo(key
->type
))
494 if (nsec_ttl
<= 0 || soa
->soa
.minimum
<= 0 || soa
->ttl
<= 0) {
495 if (log_get_max_level() >= LOG_DEBUG
) {
496 r
= dns_resource_key_to_string(key
, &key_str
);
500 log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s", key_str
);
506 if (!IN_SET(rcode
, DNS_RCODE_SUCCESS
, DNS_RCODE_NXDOMAIN
))
509 r
= dns_cache_init(c
);
513 dns_cache_make_space(c
, 1);
515 i
= new0(DnsCacheItem
, 1);
519 i
->type
= rcode
== DNS_RCODE_SUCCESS
? DNS_CACHE_NODATA
: DNS_CACHE_NXDOMAIN
;
520 i
->until
= calculate_until(soa
, nsec_ttl
, timestamp
, true);
521 i
->authenticated
= authenticated
;
522 i
->owner_family
= owner_family
;
523 i
->owner_address
= *owner_address
;
524 i
->prioq_idx
= PRIOQ_IDX_NULL
;
526 if (i
->type
== DNS_CACHE_NXDOMAIN
) {
527 /* NXDOMAIN entries should apply equally to all types, so we use ANY as
528 * a pseudo type for this purpose here. */
529 i
->key
= dns_resource_key_new(key
->class, DNS_TYPE_ANY
, DNS_RESOURCE_KEY_NAME(key
));
533 /* Make sure to remove any previous entry for this
534 * specific ANY key. (For non-ANY keys the cache data
535 * is already cleared by the caller.) Note that we
536 * don't bother removing positive or NODATA cache
537 * items in this case, because it would either be slow
538 * or require explicit indexing by name */
539 dns_cache_remove_by_key(c
, key
);
541 i
->key
= dns_resource_key_ref(key
);
543 r
= dns_cache_link_item(c
, i
);
547 if (log_get_max_level() >= LOG_DEBUG
) {
548 r
= dns_resource_key_to_string(i
->key
, &key_str
);
552 log_debug("Added %s cache entry for %s", i
->type
== DNS_CACHE_NODATA
? "NODATA" : "NXDOMAIN", key_str
);
559 static void dns_cache_remove_previous(
564 DnsResourceRecord
*rr
;
565 DnsAnswerFlags flags
;
569 /* First, if we were passed a key (i.e. on LLMNR/DNS, but
570 * not on mDNS), delete all matching old RRs, so that we only
571 * keep complete by_key in place. */
573 dns_cache_remove_by_key(c
, key
);
575 /* Second, flush all entries matching the answer, unless this
576 * is an RR that is explicitly marked to be "shared" between
577 * peers (i.e. mDNS RRs without the flush-cache bit set). */
578 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
579 if ((flags
& DNS_ANSWER_CACHEABLE
) == 0)
582 if (flags
& DNS_ANSWER_SHARED_OWNER
)
585 dns_cache_remove_by_key(c
, rr
->key
);
589 static bool rr_eligible(DnsResourceRecord
*rr
) {
592 /* When we see an NSEC/NSEC3 RR, we'll only cache it if it is from the lower zone, not the upper zone, since
593 * that's where the interesting bits are (with exception of DS RRs). Of course, this way we cannot derive DS
594 * existence from any cached NSEC/NSEC3, but that should be fine. */
596 switch (rr
->key
->type
) {
599 return !bitmap_isset(rr
->nsec
.types
, DNS_TYPE_NS
) ||
600 bitmap_isset(rr
->nsec
.types
, DNS_TYPE_SOA
);
603 return !bitmap_isset(rr
->nsec3
.types
, DNS_TYPE_NS
) ||
604 bitmap_isset(rr
->nsec3
.types
, DNS_TYPE_SOA
);
620 const union in_addr_union
*owner_address
) {
622 DnsResourceRecord
*soa
= NULL
, *rr
;
623 DnsAnswerFlags flags
;
628 assert(owner_address
);
630 dns_cache_remove_previous(c
, key
, answer
);
632 if (dns_answer_size(answer
) <= 0) {
633 if (log_get_max_level() >= LOG_DEBUG
) {
634 _cleanup_free_
char *key_str
= NULL
;
636 r
= dns_resource_key_to_string(key
, &key_str
);
640 log_debug("Not caching negative entry without a SOA record: %s", key_str
);
646 /* We only care for positive replies and NXDOMAINs, on all
647 * other replies we will simply flush the respective entries,
649 if (!IN_SET(rcode
, DNS_RCODE_SUCCESS
, DNS_RCODE_NXDOMAIN
))
652 cache_keys
= dns_answer_size(answer
);
656 /* Make some space for our new entries */
657 dns_cache_make_space(c
, cache_keys
);
660 timestamp
= now(clock_boottime_or_monotonic());
662 /* Second, add in positive entries for all contained RRs */
663 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, answer
) {
664 if ((flags
& DNS_ANSWER_CACHEABLE
) == 0)
673 r
= dns_cache_put_positive(
676 flags
& DNS_ANSWER_AUTHENTICATED
,
677 flags
& DNS_ANSWER_SHARED_OWNER
,
680 owner_family
, owner_address
);
685 if (!key
) /* mDNS doesn't know negative caching, really */
688 /* Third, add in negative entries if the key has no RR */
689 r
= dns_answer_match_key(answer
, key
, NULL
);
695 /* But not if it has a matching CNAME/DNAME (the negative
696 * caching will be done on the canonical name, not on the
698 r
= dns_answer_find_cname_or_dname(answer
, key
, NULL
, NULL
);
704 /* See https://tools.ietf.org/html/rfc2308, which say that a
705 * matching SOA record in the packet is used to to enable
706 * negative caching. */
707 r
= dns_answer_find_soa(answer
, key
, &soa
, &flags
);
713 /* Refuse using the SOA data if it is unsigned, but the key is
715 if (authenticated
&& (flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
718 r
= dns_cache_put_negative(
726 owner_family
, owner_address
);
733 /* Adding all RRs failed. Let's clean up what we already
734 * added, just in case */
737 dns_cache_remove_by_key(c
, key
);
739 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
740 if ((flags
& DNS_ANSWER_CACHEABLE
) == 0)
743 dns_cache_remove_by_key(c
, rr
->key
);
749 static DnsCacheItem
*dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache
*c
, DnsResourceKey
*k
) {
757 /* If we hit some OOM error, or suchlike, we don't care too
758 * much, after all this is just a cache */
760 i
= hashmap_get(c
->by_key
, k
);
764 n
= DNS_RESOURCE_KEY_NAME(k
);
766 /* Check if we have an NXDOMAIN cache item for the name, notice that we use
767 * the pseudo-type ANY for NXDOMAIN cache items. */
768 i
= hashmap_get(c
->by_key
, &DNS_RESOURCE_KEY_CONST(k
->class, DNS_TYPE_ANY
, n
));
769 if (i
&& i
->type
== DNS_CACHE_NXDOMAIN
)
772 if (dns_type_may_redirect(k
->type
)) {
773 /* Check if we have a CNAME record instead */
774 i
= hashmap_get(c
->by_key
, &DNS_RESOURCE_KEY_CONST(k
->class, DNS_TYPE_CNAME
, n
));
778 /* OK, let's look for cached DNAME records. */
783 i
= hashmap_get(c
->by_key
, &DNS_RESOURCE_KEY_CONST(k
->class, DNS_TYPE_DNAME
, n
));
787 /* Jump one label ahead */
788 r
= dns_name_parent(&n
);
794 if (k
->type
!= DNS_TYPE_NSEC
) {
795 /* Check if we have an NSEC record instead for the name. */
796 i
= hashmap_get(c
->by_key
, &DNS_RESOURCE_KEY_CONST(k
->class, DNS_TYPE_NSEC
, n
));
804 int dns_cache_lookup(DnsCache
*c
, DnsResourceKey
*key
, int *rcode
, DnsAnswer
**ret
, bool *authenticated
) {
805 _cleanup_(dns_answer_unrefp
) DnsAnswer
*answer
= NULL
;
808 bool nxdomain
= false;
809 _cleanup_free_
char *key_str
= NULL
;
810 DnsCacheItem
*j
, *first
, *nsec
= NULL
;
811 bool have_authenticated
= false, have_non_authenticated
= false;
817 assert(authenticated
);
819 if (key
->type
== DNS_TYPE_ANY
||
820 key
->class == DNS_CLASS_ANY
) {
822 /* If we have ANY lookups we don't use the cache, so
823 * that the caller refreshes via the network. */
825 if (log_get_max_level() >= LOG_DEBUG
) {
826 r
= dns_resource_key_to_string(key
, &key_str
);
830 log_debug("Ignoring cache for ANY lookup: %s", key_str
);
836 *rcode
= DNS_RCODE_SUCCESS
;
840 first
= dns_cache_get_by_key_follow_cname_dname_nsec(c
, key
);
842 /* If one question cannot be answered we need to refresh */
844 if (log_get_max_level() >= LOG_DEBUG
) {
845 r
= dns_resource_key_to_string(key
, &key_str
);
849 log_debug("Cache miss for %s", key_str
);
855 *rcode
= DNS_RCODE_SUCCESS
;
859 LIST_FOREACH(by_key
, j
, first
) {
861 if (j
->rr
->key
->type
== DNS_TYPE_NSEC
)
865 } else if (j
->type
== DNS_CACHE_NXDOMAIN
)
868 if (j
->authenticated
)
869 have_authenticated
= true;
871 have_non_authenticated
= true;
874 if (nsec
&& !IN_SET(key
->type
, DNS_TYPE_NSEC
, DNS_TYPE_DS
)) {
875 /* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC RRs from
876 * the lower-zone of a zone cut, but the DS RRs are on the upper zone. */
878 if (log_get_max_level() >= LOG_DEBUG
) {
879 r
= dns_resource_key_to_string(key
, &key_str
);
883 log_debug("NSEC NODATA cache hit for %s", key_str
);
886 /* We only found an NSEC record that matches our name.
887 * If it says the type doesn't exist report
888 * NODATA. Otherwise report a cache miss. */
891 *rcode
= DNS_RCODE_SUCCESS
;
892 *authenticated
= nsec
->authenticated
;
894 if (!bitmap_isset(nsec
->rr
->nsec
.types
, key
->type
) &&
895 !bitmap_isset(nsec
->rr
->nsec
.types
, DNS_TYPE_CNAME
) &&
896 !bitmap_isset(nsec
->rr
->nsec
.types
, DNS_TYPE_DNAME
)) {
905 if (log_get_max_level() >= LOG_DEBUG
) {
906 r
= dns_resource_key_to_string(key
, &key_str
);
910 log_debug("%s cache hit for %s",
912 nxdomain
? "NXDOMAIN" : "NODATA",
920 *rcode
= nxdomain
? DNS_RCODE_NXDOMAIN
: DNS_RCODE_SUCCESS
;
921 *authenticated
= have_authenticated
&& !have_non_authenticated
;
925 answer
= dns_answer_new(n
);
929 LIST_FOREACH(by_key
, j
, first
) {
933 r
= dns_answer_add(answer
, j
->rr
, j
->ifindex
, j
->authenticated
? DNS_ANSWER_AUTHENTICATED
: 0);
941 *rcode
= DNS_RCODE_SUCCESS
;
942 *authenticated
= have_authenticated
&& !have_non_authenticated
;
948 int dns_cache_check_conflicts(DnsCache
*cache
, DnsResourceRecord
*rr
, int owner_family
, const union in_addr_union
*owner_address
) {
949 DnsCacheItem
*i
, *first
;
950 bool same_owner
= true;
955 dns_cache_prune(cache
);
957 /* See if there's a cache entry for the same key. If there
958 * isn't there's no conflict */
959 first
= hashmap_get(cache
->by_key
, rr
->key
);
963 /* See if the RR key is owned by the same owner, if so, there
964 * isn't a conflict either */
965 LIST_FOREACH(by_key
, i
, first
) {
966 if (i
->owner_family
!= owner_family
||
967 !in_addr_equal(owner_family
, &i
->owner_address
, owner_address
)) {
975 /* See if there's the exact same RR in the cache. If yes, then
976 * there's no conflict. */
977 if (dns_cache_get(cache
, rr
))
980 /* There's a conflict */
984 int dns_cache_export_shared_to_packet(DnsCache
*cache
, DnsPacket
*p
) {
985 unsigned ancount
= 0;
993 HASHMAP_FOREACH(i
, cache
->by_key
, iterator
) {
996 LIST_FOREACH(by_key
, j
, i
) {
1000 if (!j
->shared_owner
)
1003 r
= dns_packet_append_rr(p
, j
->rr
, NULL
, NULL
);
1004 if (r
== -EMSGSIZE
&& p
->protocol
== DNS_PROTOCOL_MDNS
) {
1005 /* For mDNS, if we're unable to stuff all known answers into the given packet,
1006 * allocate a new one, push the RR into that one and link it to the current one.
1009 DNS_PACKET_HEADER(p
)->ancount
= htobe16(ancount
);
1012 r
= dns_packet_new_query(&p
->more
, p
->protocol
, 0, true);
1016 /* continue with new packet */
1018 r
= dns_packet_append_rr(p
, j
->rr
, NULL
, NULL
);
1028 DNS_PACKET_HEADER(p
)->ancount
= htobe16(ancount
);
1033 void dns_cache_dump(DnsCache
*cache
, FILE *f
) {
1044 HASHMAP_FOREACH(i
, cache
->by_key
, iterator
) {
1047 LIST_FOREACH(by_key
, j
, i
) {
1053 t
= dns_resource_record_to_string(j
->rr
);
1062 _cleanup_free_
char *z
= NULL
;
1063 r
= dns_resource_key_to_string(j
->key
, &z
);
1071 fputs(j
->type
== DNS_CACHE_NODATA
? "NODATA" : "NXDOMAIN", f
);
1078 bool dns_cache_is_empty(DnsCache
*cache
) {
1082 return hashmap_isempty(cache
->by_key
);
1085 unsigned dns_cache_size(DnsCache
*cache
) {
1089 return hashmap_size(cache
->by_key
);