1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2014 Lennart Poettering
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.
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.
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/>.
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"
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
35 /* We never keep any item longer than 2h in our cache */
36 #define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR)
38 /* How long to cache strange rcodes, i.e. rcodes != SUCCESS and != NXDOMAIN (specifically: that's only SERVFAIL for
40 #define CACHE_TTL_STRANGE_RCODE_USEC (30 * USEC_PER_SEC)
42 typedef enum DnsCacheItemType DnsCacheItemType
;
43 typedef struct DnsCacheItem DnsCacheItem
;
45 enum DnsCacheItemType
{
49 DNS_CACHE_RCODE
, /* "strange" RCODE (effective only SERVFAIL for now) */
53 DnsCacheItemType type
;
55 DnsResourceRecord
*rr
;
64 union in_addr_union owner_address
;
67 LIST_FIELDS(DnsCacheItem
, by_key
);
70 static const char *dns_cache_item_type_to_string(DnsCacheItem
*item
) {
75 case DNS_CACHE_POSITIVE
:
78 case DNS_CACHE_NODATA
:
81 case DNS_CACHE_NXDOMAIN
:
85 return dns_rcode_to_string(item
->rcode
);
91 static void dns_cache_item_free(DnsCacheItem
*i
) {
95 dns_resource_record_unref(i
->rr
);
96 dns_resource_key_unref(i
->key
);
100 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem
*, dns_cache_item_free
);
102 static void dns_cache_item_unlink_and_free(DnsCache
*c
, DnsCacheItem
*i
) {
110 first
= hashmap_get(c
->by_key
, i
->key
);
111 LIST_REMOVE(by_key
, first
, i
);
114 assert_se(hashmap_replace(c
->by_key
, first
->key
, first
) >= 0);
116 hashmap_remove(c
->by_key
, i
->key
);
118 prioq_remove(c
->by_expiry
, i
, &i
->prioq_idx
);
120 dns_cache_item_free(i
);
123 static bool dns_cache_remove_by_rr(DnsCache
*c
, DnsResourceRecord
*rr
) {
124 DnsCacheItem
*first
, *i
;
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
);
133 dns_cache_item_unlink_and_free(c
, i
);
141 static bool dns_cache_remove_by_key(DnsCache
*c
, DnsResourceKey
*key
) {
142 DnsCacheItem
*first
, *i
, *n
;
147 first
= hashmap_remove(c
->by_key
, key
);
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
);
159 void dns_cache_flush(DnsCache
*c
) {
164 while ((key
= hashmap_first_key(c
->by_key
)))
165 dns_cache_remove_by_key(c
, key
);
167 assert(hashmap_size(c
->by_key
) == 0);
168 assert(prioq_size(c
->by_expiry
) == 0);
170 c
->by_key
= hashmap_free(c
->by_key
);
171 c
->by_expiry
= prioq_free(c
->by_expiry
);
174 static void dns_cache_make_space(DnsCache
*c
, unsigned add
) {
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. */
186 _cleanup_(dns_resource_key_unrefp
) DnsResourceKey
*key
= NULL
;
189 if (prioq_size(c
->by_expiry
) <= 0)
192 if (prioq_size(c
->by_expiry
) + add
< CACHE_MAX
)
195 i
= prioq_peek(c
->by_expiry
);
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
);
205 void dns_cache_prune(DnsCache
*c
) {
210 /* Remove all entries that are past their TTL */
214 char key_str
[DNS_RESOURCE_KEY_STRING_MAX
];
216 i
= prioq_peek(c
->by_expiry
);
221 t
= now(clock_boottime_or_monotonic());
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
);
234 dns_cache_item_unlink_and_free(c
, i
);
236 _cleanup_(dns_resource_key_unrefp
) DnsResourceKey
*key
= NULL
;
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
);
246 static int dns_cache_item_prioq_compare_func(const void *a
, const void *b
) {
247 const DnsCacheItem
*x
= a
, *y
= b
;
249 if (x
->until
< y
->until
)
251 if (x
->until
> y
->until
)
256 static int dns_cache_init(DnsCache
*c
) {
261 r
= prioq_ensure_allocated(&c
->by_expiry
, dns_cache_item_prioq_compare_func
);
265 r
= hashmap_ensure_allocated(&c
->by_key
, &dns_resource_key_hash_ops
);
272 static int dns_cache_link_item(DnsCache
*c
, DnsCacheItem
*i
) {
279 r
= prioq_put(c
->by_expiry
, i
, &i
->prioq_idx
);
283 first
= hashmap_get(c
->by_key
, i
->key
);
285 _cleanup_(dns_resource_key_unrefp
) DnsResourceKey
*k
= NULL
;
287 /* Keep a reference to the original key, while we manipulate the list. */
288 k
= dns_resource_key_ref(first
->key
);
290 /* Now, try to reduce the number of keys we keep */
291 dns_resource_key_reduce(&first
->key
, &i
->key
);
294 dns_resource_key_reduce(&first
->rr
->key
, &i
->key
);
296 dns_resource_key_reduce(&i
->rr
->key
, &i
->key
);
298 LIST_PREPEND(by_key
, first
, i
);
299 assert_se(hashmap_replace(c
->by_key
, first
->key
, first
) >= 0);
301 r
= hashmap_put(c
->by_key
, i
->key
, i
);
303 prioq_remove(c
->by_expiry
, i
, &i
->prioq_idx
);
311 static DnsCacheItem
* dns_cache_get(DnsCache
*c
, DnsResourceRecord
*rr
) {
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)
324 static usec_t
calculate_until(DnsResourceRecord
*rr
, uint32_t nsec_ttl
, usec_t timestamp
, bool use_soa_minimum
) {
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
338 if (ttl
> rr
->soa
.minimum
)
339 ttl
= rr
->soa
.minimum
;
342 u
= ttl
* USEC_PER_SEC
;
343 if (u
> CACHE_TTL_MAX_USEC
)
344 u
= CACHE_TTL_MAX_USEC
;
346 if (rr
->expiry
!= USEC_INFINITY
) {
349 /* Make use of the DNSSEC RRSIG expiry info, if we
352 left
= LESS_BY(rr
->expiry
, now(CLOCK_REALTIME
));
357 return timestamp
+ u
;
360 static void dns_cache_item_update_positive(
363 DnsResourceRecord
*rr
,
369 const union in_addr_union
*owner_address
) {
374 assert(owner_address
);
376 i
->type
= DNS_CACHE_POSITIVE
;
379 /* We are the first item in the list, we need to
380 * update the key used in the hashmap */
382 assert_se(hashmap_replace(c
->by_key
, rr
->key
, i
) >= 0);
384 dns_resource_record_ref(rr
);
385 dns_resource_record_unref(i
->rr
);
388 dns_resource_key_unref(i
->key
);
389 i
->key
= dns_resource_key_ref(rr
->key
);
391 i
->until
= calculate_until(rr
, (uint32_t) -1, timestamp
, false);
392 i
->authenticated
= authenticated
;
393 i
->shared_owner
= shared_owner
;
395 i
->ifindex
= ifindex
;
397 i
->owner_family
= owner_family
;
398 i
->owner_address
= *owner_address
;
400 prioq_reshuffle(c
->by_expiry
, i
, &i
->prioq_idx
);
403 static int dns_cache_put_positive(
405 DnsResourceRecord
*rr
,
411 const union in_addr_union
*owner_address
) {
413 _cleanup_(dns_cache_item_freep
) DnsCacheItem
*i
= NULL
;
414 DnsCacheItem
*existing
;
415 char key_str
[DNS_RESOURCE_KEY_STRING_MAX
], ifname
[IF_NAMESIZE
];
420 assert(owner_address
);
422 /* Never cache pseudo RRs */
423 if (dns_class_is_pseudo(rr
->key
->class))
425 if (dns_type_is_pseudo(rr
->key
->type
))
428 /* New TTL is 0? Delete this specific entry... */
430 k
= dns_cache_remove_by_rr(c
, rr
);
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
));
437 /* Entry exists already? Update TTL, timestamp and owner */
438 existing
= dns_cache_get(c
, rr
);
440 dns_cache_item_update_positive(
453 /* Otherwise, add the new RR */
454 r
= dns_cache_init(c
);
458 dns_cache_make_space(c
, 1);
460 i
= new0(DnsCacheItem
, 1);
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
;
475 r
= dns_cache_link_item(c
, i
);
480 _cleanup_free_
char *t
= NULL
;
482 (void) in_addr_to_string(i
->owner_family
, &i
->owner_address
, &t
);
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
),
498 static int dns_cache_put_negative(
505 DnsResourceRecord
*soa
,
507 const union in_addr_union
*owner_address
) {
509 _cleanup_(dns_cache_item_freep
) DnsCacheItem
*i
= NULL
;
510 char key_str
[DNS_RESOURCE_KEY_STRING_MAX
];
515 assert(owner_address
);
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))
522 if (dns_type_is_pseudo(key
->type
))
525 if (IN_SET(rcode
, DNS_RCODE_SUCCESS
, DNS_RCODE_NXDOMAIN
)) {
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
));
535 } else if (rcode
!= DNS_RCODE_SERVFAIL
)
538 r
= dns_cache_init(c
);
542 dns_cache_make_space(c
, 1);
544 i
= new0(DnsCacheItem
, 1);
549 rcode
== DNS_RCODE_SUCCESS
? DNS_CACHE_NODATA
:
550 rcode
== DNS_RCODE_NXDOMAIN
? DNS_CACHE_NXDOMAIN
: DNS_CACHE_RCODE
;
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
;
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
));
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
);
575 i
->key
= dns_resource_key_ref(key
);
577 r
= dns_cache_link_item(c
, i
);
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
);
590 static void dns_cache_remove_previous(
595 DnsResourceRecord
*rr
;
596 DnsAnswerFlags flags
;
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. */
604 dns_cache_remove_by_key(c
, key
);
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)
613 if (flags
& DNS_ANSWER_SHARED_OWNER
)
616 dns_cache_remove_by_key(c
, rr
->key
);
620 static bool rr_eligible(DnsResourceRecord
*rr
) {
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. */
627 switch (rr
->key
->type
) {
630 return !bitmap_isset(rr
->nsec
.types
, DNS_TYPE_NS
) ||
631 bitmap_isset(rr
->nsec
.types
, DNS_TYPE_SOA
);
634 return !bitmap_isset(rr
->nsec3
.types
, DNS_TYPE_NS
) ||
635 bitmap_isset(rr
->nsec3
.types
, DNS_TYPE_SOA
);
651 const union in_addr_union
*owner_address
) {
653 DnsResourceRecord
*soa
= NULL
, *rr
;
654 bool weird_rcode
= false;
655 DnsAnswerFlags flags
;
660 assert(owner_address
);
662 dns_cache_remove_previous(c
, key
, answer
);
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
669 if (IN_SET(rcode
, DNS_RCODE_SUCCESS
, DNS_RCODE_NXDOMAIN
)) {
671 if (dns_answer_size(answer
) <= 0) {
672 char key_str
[DNS_RESOURCE_KEY_STRING_MAX
];
674 log_debug("Not caching negative entry without a SOA record: %s",
675 dns_resource_key_to_string(key
, key_str
, sizeof key_str
));
680 /* Only cache SERVFAIL as "weird" rcode for now. We can add more later, should that turn out to be
682 if (rcode
!= DNS_RCODE_SERVFAIL
)
688 cache_keys
= dns_answer_size(answer
);
692 /* Make some space for our new entries */
693 dns_cache_make_space(c
, cache_keys
);
696 timestamp
= now(clock_boottime_or_monotonic());
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)
709 r
= dns_cache_put_positive(
712 flags
& DNS_ANSWER_AUTHENTICATED
,
713 flags
& DNS_ANSWER_SHARED_OWNER
,
716 owner_family
, owner_address
);
721 if (!key
) /* mDNS doesn't know negative caching, really */
724 /* Third, add in negative entries if the key has no RR */
725 r
= dns_answer_match_key(answer
, key
, NULL
);
731 /* But not if it has a matching CNAME/DNAME (the negative
732 * caching will be done on the canonical name, not on the
734 r
= dns_answer_find_cname_or_dname(answer
, key
, NULL
, NULL
);
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
);
746 if (r
== 0 && !weird_rcode
)
749 /* Refuse using the SOA data if it is unsigned, but the key is
751 if (authenticated
&& (flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
755 r
= dns_cache_put_negative(
763 owner_family
, owner_address
);
770 /* Adding all RRs failed. Let's clean up what we already
771 * added, just in case */
774 dns_cache_remove_by_key(c
, key
);
776 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
777 if ((flags
& DNS_ANSWER_CACHEABLE
) == 0)
780 dns_cache_remove_by_key(c
, rr
->key
);
786 static DnsCacheItem
*dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache
*c
, DnsResourceKey
*k
) {
794 /* If we hit some OOM error, or suchlike, we don't care too
795 * much, after all this is just a cache */
797 i
= hashmap_get(c
->by_key
, k
);
801 n
= dns_resource_key_name(k
);
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
)
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
));
815 /* OK, let's look for cached DNAME records. */
820 i
= hashmap_get(c
->by_key
, &DNS_RESOURCE_KEY_CONST(k
->class, DNS_TYPE_DNAME
, n
));
824 /* Jump one label ahead */
825 r
= dns_name_parent(&n
);
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
));
841 int 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
];
846 bool nxdomain
= false;
847 DnsCacheItem
*j
, *first
, *nsec
= NULL
;
848 bool have_authenticated
= false, have_non_authenticated
= false;
850 int found_rcode
= -1;
856 assert(authenticated
);
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. */
862 log_debug("Ignoring cache for ANY lookup: %s",
863 dns_resource_key_to_string(key
, key_str
, sizeof key_str
));
868 *rcode
= DNS_RCODE_SUCCESS
;
869 *authenticated
= false;
874 first
= dns_cache_get_by_key_follow_cname_dname_nsec(c
, key
);
876 /* If one question cannot be answered we need to refresh */
878 log_debug("Cache miss for %s",
879 dns_resource_key_to_string(key
, key_str
, sizeof key_str
));
884 *rcode
= DNS_RCODE_SUCCESS
;
885 *authenticated
= false;
890 LIST_FOREACH(by_key
, j
, first
) {
892 if (j
->rr
->key
->type
== DNS_TYPE_NSEC
)
896 } else if (j
->type
== DNS_CACHE_NXDOMAIN
)
898 else if (j
->type
== DNS_CACHE_RCODE
)
899 found_rcode
= j
->rcode
;
901 if (j
->authenticated
)
902 have_authenticated
= true;
904 have_non_authenticated
= true;
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
)));
913 *rcode
= found_rcode
;
914 *authenticated
= false;
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. */
924 log_debug("NSEC NODATA cache hit for %s",
925 dns_resource_key_to_string(key
, key_str
, sizeof key_str
));
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. */
932 *rcode
= DNS_RCODE_SUCCESS
;
933 *authenticated
= nsec
->authenticated
;
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
)) {
946 log_debug("%s cache hit for %s",
948 nxdomain
? "NXDOMAIN" : "NODATA",
949 dns_resource_key_to_string(key
, key_str
, sizeof key_str
));
955 *rcode
= nxdomain
? DNS_RCODE_NXDOMAIN
: DNS_RCODE_SUCCESS
;
956 *authenticated
= have_authenticated
&& !have_non_authenticated
;
960 answer
= dns_answer_new(n
);
965 current
= now(clock_boottime_or_monotonic());
967 LIST_FOREACH(by_key
, j
, first
) {
968 _cleanup_(dns_resource_record_unrefp
) DnsResourceRecord
*rr
= NULL
;
974 rr
= dns_resource_record_ref(j
->rr
);
976 r
= dns_resource_record_clamp_ttl(&rr
, LESS_BY(j
->until
, current
) / USEC_PER_SEC
);
981 r
= dns_answer_add(answer
, rr
?: j
->rr
, j
->ifindex
, j
->authenticated
? DNS_ANSWER_AUTHENTICATED
: 0);
989 *rcode
= DNS_RCODE_SUCCESS
;
990 *authenticated
= have_authenticated
&& !have_non_authenticated
;
996 int 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;
1003 dns_cache_prune(cache
);
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
);
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
)) {
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
))
1028 /* There's a conflict */
1032 int dns_cache_export_shared_to_packet(DnsCache
*cache
, DnsPacket
*p
) {
1033 unsigned ancount
= 0;
1041 HASHMAP_FOREACH(i
, cache
->by_key
, iterator
) {
1044 LIST_FOREACH(by_key
, j
, i
) {
1048 if (!j
->shared_owner
)
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.
1057 DNS_PACKET_HEADER(p
)->ancount
= htobe16(ancount
);
1060 r
= dns_packet_new_query(&p
->more
, p
->protocol
, 0, true);
1064 /* continue with new packet */
1066 r
= dns_packet_append_rr(p
, j
->rr
, 0, NULL
, NULL
);
1076 DNS_PACKET_HEADER(p
)->ancount
= htobe16(ancount
);
1081 void dns_cache_dump(DnsCache
*cache
, FILE *f
) {
1091 HASHMAP_FOREACH(i
, cache
->by_key
, iterator
) {
1094 LIST_FOREACH(by_key
, j
, i
) {
1100 t
= dns_resource_record_to_string(j
->rr
);
1109 char key_str
[DNS_RESOURCE_KEY_STRING_MAX
];
1111 fputs(dns_resource_key_to_string(j
->key
, key_str
, sizeof key_str
), f
);
1113 fputs(dns_cache_item_type_to_string(j
), f
);
1120 bool dns_cache_is_empty(DnsCache
*cache
) {
1124 return hashmap_isempty(cache
->by_key
);
1127 unsigned dns_cache_size(DnsCache
*cache
) {
1131 return hashmap_size(cache
->by_key
);