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 */
30 #define CACHE_MAX 4096
32 /* We never keep any item longer than 2h in our cache */
33 #define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR)
35 typedef enum DnsCacheItemType DnsCacheItemType
;
36 typedef struct DnsCacheItem DnsCacheItem
;
38 enum DnsCacheItemType
{
45 DnsCacheItemType type
;
47 DnsResourceRecord
*rr
;
54 union in_addr_union owner_address
;
57 LIST_FIELDS(DnsCacheItem
, by_key
);
60 static void dns_cache_item_free(DnsCacheItem
*i
) {
64 dns_resource_record_unref(i
->rr
);
65 dns_resource_key_unref(i
->key
);
69 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem
*, dns_cache_item_free
);
71 static void dns_cache_item_unlink_and_free(DnsCache
*c
, DnsCacheItem
*i
) {
79 first
= hashmap_get(c
->by_key
, i
->key
);
80 LIST_REMOVE(by_key
, first
, i
);
83 assert_se(hashmap_replace(c
->by_key
, first
->key
, first
) >= 0);
85 hashmap_remove(c
->by_key
, i
->key
);
87 prioq_remove(c
->by_expiry
, i
, &i
->prioq_idx
);
89 dns_cache_item_free(i
);
92 static bool dns_cache_remove_by_rr(DnsCache
*c
, DnsResourceRecord
*rr
) {
93 DnsCacheItem
*first
, *i
;
96 first
= hashmap_get(c
->by_key
, rr
->key
);
97 LIST_FOREACH(by_key
, i
, first
) {
98 r
= dns_resource_record_equal(i
->rr
, rr
);
102 dns_cache_item_unlink_and_free(c
, i
);
110 static bool dns_cache_remove_by_key(DnsCache
*c
, DnsResourceKey
*key
) {
111 DnsCacheItem
*first
, *i
, *n
;
116 first
= hashmap_remove(c
->by_key
, key
);
120 LIST_FOREACH_SAFE(by_key
, i
, n
, first
) {
121 prioq_remove(c
->by_expiry
, i
, &i
->prioq_idx
);
122 dns_cache_item_free(i
);
128 void dns_cache_flush(DnsCache
*c
) {
133 while ((key
= hashmap_first_key(c
->by_key
)))
134 dns_cache_remove_by_key(c
, key
);
136 assert(hashmap_size(c
->by_key
) == 0);
137 assert(prioq_size(c
->by_expiry
) == 0);
139 c
->by_key
= hashmap_free(c
->by_key
);
140 c
->by_expiry
= prioq_free(c
->by_expiry
);
143 static void dns_cache_make_space(DnsCache
*c
, unsigned add
) {
149 /* Makes space for n new entries. Note that we actually allow
150 * the cache to grow beyond CACHE_MAX, but only when we shall
151 * add more RRs to the cache than CACHE_MAX at once. In that
152 * case the cache will be emptied completely otherwise. */
155 _cleanup_(dns_resource_key_unrefp
) DnsResourceKey
*key
= NULL
;
158 if (prioq_size(c
->by_expiry
) <= 0)
161 if (prioq_size(c
->by_expiry
) + add
< CACHE_MAX
)
164 i
= prioq_peek(c
->by_expiry
);
167 /* Take an extra reference to the key so that it
168 * doesn't go away in the middle of the remove call */
169 key
= dns_resource_key_ref(i
->key
);
170 dns_cache_remove_by_key(c
, key
);
174 void dns_cache_prune(DnsCache
*c
) {
179 /* Remove all entries that are past their TTL */
184 i
= prioq_peek(c
->by_expiry
);
189 t
= now(clock_boottime_or_monotonic());
194 /* Depending whether this is an mDNS shared entry
195 * either remove only this one RR or the whole
198 dns_cache_item_unlink_and_free(c
, i
);
200 _cleanup_(dns_resource_key_unrefp
) DnsResourceKey
*key
= NULL
;
202 /* Take an extra reference to the key so that it
203 * doesn't go away in the middle of the remove call */
204 key
= dns_resource_key_ref(i
->key
);
205 dns_cache_remove_by_key(c
, key
);
210 static int dns_cache_item_prioq_compare_func(const void *a
, const void *b
) {
211 const DnsCacheItem
*x
= a
, *y
= b
;
213 if (x
->until
< y
->until
)
215 if (x
->until
> y
->until
)
220 static int dns_cache_init(DnsCache
*c
) {
225 r
= prioq_ensure_allocated(&c
->by_expiry
, dns_cache_item_prioq_compare_func
);
229 r
= hashmap_ensure_allocated(&c
->by_key
, &dns_resource_key_hash_ops
);
236 static int dns_cache_link_item(DnsCache
*c
, DnsCacheItem
*i
) {
243 r
= prioq_put(c
->by_expiry
, i
, &i
->prioq_idx
);
247 first
= hashmap_get(c
->by_key
, i
->key
);
249 LIST_PREPEND(by_key
, first
, i
);
250 assert_se(hashmap_replace(c
->by_key
, first
->key
, first
) >= 0);
252 r
= hashmap_put(c
->by_key
, i
->key
, i
);
254 prioq_remove(c
->by_expiry
, i
, &i
->prioq_idx
);
262 static DnsCacheItem
* dns_cache_get(DnsCache
*c
, DnsResourceRecord
*rr
) {
268 LIST_FOREACH(by_key
, i
, hashmap_get(c
->by_key
, rr
->key
))
269 if (i
->rr
&& dns_resource_record_equal(i
->rr
, rr
) > 0)
275 static usec_t
calculate_until(DnsResourceRecord
*rr
, usec_t timestamp
, bool use_soa_minimum
) {
282 if (rr
->key
->type
== DNS_TYPE_SOA
&& use_soa_minimum
) {
283 /* If this is a SOA RR, and it is requested, clamp to
284 * the SOA's minimum field. This is used when we do
285 * negative caching, to determine the TTL for the
286 * negative caching entry. See RFC 2308, Section
289 if (ttl
> rr
->soa
.minimum
)
290 ttl
= rr
->soa
.minimum
;
293 u
= ttl
* USEC_PER_SEC
;
294 if (u
> CACHE_TTL_MAX_USEC
)
295 u
= CACHE_TTL_MAX_USEC
;
297 if (rr
->expiry
!= USEC_INFINITY
) {
300 /* Make use of the DNSSEC RRSIG expiry info, if we
303 left
= LESS_BY(rr
->expiry
, now(CLOCK_REALTIME
));
308 return timestamp
+ u
;
311 static void dns_cache_item_update_positive(
314 DnsResourceRecord
*rr
,
319 const union in_addr_union
*owner_address
) {
324 assert(owner_address
);
326 i
->type
= DNS_CACHE_POSITIVE
;
329 /* We are the first item in the list, we need to
330 * update the key used in the hashmap */
332 assert_se(hashmap_replace(c
->by_key
, rr
->key
, i
) >= 0);
334 dns_resource_record_ref(rr
);
335 dns_resource_record_unref(i
->rr
);
338 dns_resource_key_unref(i
->key
);
339 i
->key
= dns_resource_key_ref(rr
->key
);
341 i
->until
= calculate_until(rr
, timestamp
, false);
342 i
->authenticated
= authenticated
;
343 i
->shared_owner
= shared_owner
;
345 i
->owner_family
= owner_family
;
346 i
->owner_address
= *owner_address
;
348 prioq_reshuffle(c
->by_expiry
, i
, &i
->prioq_idx
);
351 static int dns_cache_put_positive(
353 DnsResourceRecord
*rr
,
358 const union in_addr_union
*owner_address
) {
360 _cleanup_(dns_cache_item_freep
) DnsCacheItem
*i
= NULL
;
361 _cleanup_free_
char *key_str
= NULL
;
362 DnsCacheItem
*existing
;
367 assert(owner_address
);
369 /* Never cache pseudo RRs */
370 if (dns_class_is_pseudo(rr
->key
->class))
372 if (dns_type_is_pseudo(rr
->key
->type
))
375 /* New TTL is 0? Delete this specific entry... */
377 k
= dns_cache_remove_by_rr(c
, rr
);
379 if (log_get_max_level() >= LOG_DEBUG
) {
380 r
= dns_resource_key_to_string(rr
->key
, &key_str
);
385 log_debug("Removed zero TTL entry from cache: %s", key_str
);
387 log_debug("Not caching zero TTL cache entry: %s", key_str
);
393 /* Entry exists already? Update TTL, timestamp and owner*/
394 existing
= dns_cache_get(c
, rr
);
396 dns_cache_item_update_positive(
408 /* Otherwise, add the new RR */
409 r
= dns_cache_init(c
);
413 dns_cache_make_space(c
, 1);
415 i
= new0(DnsCacheItem
, 1);
419 i
->type
= DNS_CACHE_POSITIVE
;
420 i
->key
= dns_resource_key_ref(rr
->key
);
421 i
->rr
= dns_resource_record_ref(rr
);
422 i
->until
= calculate_until(rr
, timestamp
, false);
423 i
->authenticated
= authenticated
;
424 i
->shared_owner
= shared_owner
;
425 i
->owner_family
= owner_family
;
426 i
->owner_address
= *owner_address
;
427 i
->prioq_idx
= PRIOQ_IDX_NULL
;
429 r
= dns_cache_link_item(c
, i
);
433 if (log_get_max_level() >= LOG_DEBUG
) {
434 r
= dns_resource_key_to_string(i
->key
, &key_str
);
438 log_debug("Added positive cache entry for %s", key_str
);
445 static int dns_cache_put_negative(
451 DnsResourceRecord
*soa
,
453 const union in_addr_union
*owner_address
) {
455 _cleanup_(dns_cache_item_freep
) DnsCacheItem
*i
= NULL
;
456 _cleanup_free_
char *key_str
= NULL
;
462 assert(owner_address
);
464 /* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
465 * important to filter out as we use this as a pseudo-type for
466 * NXDOMAIN entries */
467 if (dns_class_is_pseudo(key
->class))
469 if (dns_type_is_pseudo(key
->type
))
472 if (soa
->soa
.minimum
<= 0 || soa
->ttl
<= 0) {
473 if (log_get_max_level() >= LOG_DEBUG
) {
474 r
= dns_resource_key_to_string(key
, &key_str
);
478 log_debug("Not caching negative entry with zero SOA TTL: %s", key_str
);
484 if (!IN_SET(rcode
, DNS_RCODE_SUCCESS
, DNS_RCODE_NXDOMAIN
))
487 r
= dns_cache_init(c
);
491 dns_cache_make_space(c
, 1);
493 i
= new0(DnsCacheItem
, 1);
497 i
->type
= rcode
== DNS_RCODE_SUCCESS
? DNS_CACHE_NODATA
: DNS_CACHE_NXDOMAIN
;
498 i
->until
= calculate_until(soa
, timestamp
, true);
499 i
->authenticated
= authenticated
;
500 i
->owner_family
= owner_family
;
501 i
->owner_address
= *owner_address
;
502 i
->prioq_idx
= PRIOQ_IDX_NULL
;
504 if (i
->type
== DNS_CACHE_NXDOMAIN
) {
505 /* NXDOMAIN entries should apply equally to all types, so we use ANY as
506 * a pseudo type for this purpose here. */
507 i
->key
= dns_resource_key_new(key
->class, DNS_TYPE_ANY
, DNS_RESOURCE_KEY_NAME(key
));
511 /* Make sure to remove any previous entry for this
512 * specific ANY key. (For non-ANY keys the cache data
513 * is already cleared by the caller.) Note that we
514 * don't bother removing positive or NODATA cache
515 * items in this case, because it would either be slow
516 * or require explicit indexing by name */
517 dns_cache_remove_by_key(c
, key
);
519 i
->key
= dns_resource_key_ref(key
);
521 r
= dns_cache_link_item(c
, i
);
525 if (log_get_max_level() >= LOG_DEBUG
) {
526 r
= dns_resource_key_to_string(i
->key
, &key_str
);
530 log_debug("Added %s cache entry for %s", i
->type
== DNS_CACHE_NODATA
? "NODATA" : "NXDOMAIN", key_str
);
537 static void dns_cache_remove_previous(
542 DnsResourceRecord
*rr
;
543 DnsAnswerFlags flags
;
547 /* First, if we were passed a key (i.e. on LLMNR/DNS, but
548 * not on mDNS), delete all matching old RRs, so that we only
549 * keep complete by_key in place. */
551 dns_cache_remove_by_key(c
, key
);
553 /* Second, flush all entries matching the answer, unless this
554 * is an RR that is explicitly marked to be "shared" between
555 * peers (i.e. mDNS RRs without the flush-cache bit set). */
556 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
557 if ((flags
& DNS_ANSWER_CACHEABLE
) == 0)
560 if (flags
& DNS_ANSWER_SHARED_OWNER
)
563 dns_cache_remove_by_key(c
, rr
->key
);
575 const union in_addr_union
*owner_address
) {
577 DnsResourceRecord
*soa
= NULL
, *rr
;
578 DnsAnswerFlags flags
;
583 assert(owner_address
);
585 dns_cache_remove_previous(c
, key
, answer
);
587 if (dns_answer_size(answer
) <= 0) {
588 if (log_get_max_level() >= LOG_DEBUG
) {
589 _cleanup_free_
char *key_str
= NULL
;
591 r
= dns_resource_key_to_string(key
, &key_str
);
595 log_debug("Not caching negative entry without a SOA record: %s", key_str
);
601 /* We only care for positive replies and NXDOMAINs, on all
602 * other replies we will simply flush the respective entries,
604 if (!IN_SET(rcode
, DNS_RCODE_SUCCESS
, DNS_RCODE_NXDOMAIN
))
607 cache_keys
= dns_answer_size(answer
);
611 /* Make some space for our new entries */
612 dns_cache_make_space(c
, cache_keys
);
615 timestamp
= now(clock_boottime_or_monotonic());
617 /* Second, add in positive entries for all contained RRs */
618 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
619 if ((flags
& DNS_ANSWER_CACHEABLE
) == 0)
622 r
= dns_cache_put_positive(
625 flags
& DNS_ANSWER_AUTHENTICATED
,
626 flags
& DNS_ANSWER_SHARED_OWNER
,
628 owner_family
, owner_address
);
633 if (!key
) /* mDNS doesn't know negative caching, really */
636 /* Third, add in negative entries if the key has no RR */
637 r
= dns_answer_match_key(answer
, key
, NULL
);
643 /* But not if it has a matching CNAME/DNAME (the negative
644 * caching will be done on the canonical name, not on the
646 r
= dns_answer_find_cname_or_dname(answer
, key
, NULL
, NULL
);
652 /* See https://tools.ietf.org/html/rfc2308, which say that a
653 * matching SOA record in the packet is used to to enable
654 * negative caching. */
655 r
= dns_answer_find_soa(answer
, key
, &soa
, &flags
);
661 /* Refuse using the SOA data if it is unsigned, but the key is
663 if (authenticated
&& (flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
666 r
= dns_cache_put_negative(
673 owner_family
, owner_address
);
680 /* Adding all RRs failed. Let's clean up what we already
681 * added, just in case */
684 dns_cache_remove_by_key(c
, key
);
686 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
687 if ((flags
& DNS_ANSWER_CACHEABLE
) == 0)
690 dns_cache_remove_by_key(c
, rr
->key
);
696 static DnsCacheItem
*dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache
*c
, DnsResourceKey
*k
) {
704 /* If we hit some OOM error, or suchlike, we don't care too
705 * much, after all this is just a cache */
707 i
= hashmap_get(c
->by_key
, k
);
711 n
= DNS_RESOURCE_KEY_NAME(k
);
713 /* Check if we have an NXDOMAIN cache item for the name, notice that we use
714 * the pseudo-type ANY for NXDOMAIN cache items. */
715 i
= hashmap_get(c
->by_key
, &DNS_RESOURCE_KEY_CONST(k
->class, DNS_TYPE_ANY
, n
));
716 if (i
&& i
->type
== DNS_CACHE_NXDOMAIN
)
719 if (dns_type_may_redirect(k
->type
)) {
720 /* Check if we have a CNAME record instead */
721 i
= hashmap_get(c
->by_key
, &DNS_RESOURCE_KEY_CONST(k
->class, DNS_TYPE_CNAME
, n
));
725 /* OK, let's look for cached DNAME records. */
730 i
= hashmap_get(c
->by_key
, &DNS_RESOURCE_KEY_CONST(k
->class, DNS_TYPE_DNAME
, n
));
734 /* Jump one label ahead */
735 r
= dns_name_parent(&n
);
741 if (k
->type
!= DNS_TYPE_NSEC
) {
742 /* Check if we have an NSEC record instead for the name. */
743 i
= hashmap_get(c
->by_key
, &DNS_RESOURCE_KEY_CONST(k
->class, DNS_TYPE_NSEC
, n
));
751 int dns_cache_lookup(DnsCache
*c
, DnsResourceKey
*key
, int *rcode
, DnsAnswer
**ret
, bool *authenticated
) {
752 _cleanup_(dns_answer_unrefp
) DnsAnswer
*answer
= NULL
;
755 bool nxdomain
= false;
756 _cleanup_free_
char *key_str
= NULL
;
757 DnsCacheItem
*j
, *first
, *nsec
= NULL
;
758 bool have_authenticated
= false, have_non_authenticated
= false;
764 assert(authenticated
);
766 if (key
->type
== DNS_TYPE_ANY
||
767 key
->class == DNS_CLASS_ANY
) {
769 /* If we have ANY lookups we don't use the cache, so
770 * that the caller refreshes via the network. */
772 if (log_get_max_level() >= LOG_DEBUG
) {
773 r
= dns_resource_key_to_string(key
, &key_str
);
777 log_debug("Ignoring cache for ANY lookup: %s", key_str
);
783 *rcode
= DNS_RCODE_SUCCESS
;
787 first
= dns_cache_get_by_key_follow_cname_dname_nsec(c
, key
);
789 /* If one question cannot be answered we need to refresh */
791 if (log_get_max_level() >= LOG_DEBUG
) {
792 r
= dns_resource_key_to_string(key
, &key_str
);
796 log_debug("Cache miss for %s", key_str
);
802 *rcode
= DNS_RCODE_SUCCESS
;
806 LIST_FOREACH(by_key
, j
, first
) {
808 if (j
->rr
->key
->type
== DNS_TYPE_NSEC
)
812 } else if (j
->type
== DNS_CACHE_NXDOMAIN
)
815 if (j
->authenticated
)
816 have_authenticated
= true;
818 have_non_authenticated
= true;
821 if (nsec
&& key
->type
!= DNS_TYPE_NSEC
) {
822 if (log_get_max_level() >= LOG_DEBUG
) {
823 r
= dns_resource_key_to_string(key
, &key_str
);
827 log_debug("NSEC NODATA cache hit for %s", key_str
);
830 /* We only found an NSEC record that matches our name.
831 * If it says the type doesn't exist report
832 * NODATA. Otherwise report a cache miss. */
835 *rcode
= DNS_RCODE_SUCCESS
;
836 *authenticated
= nsec
->authenticated
;
838 if (!bitmap_isset(nsec
->rr
->nsec
.types
, key
->type
) &&
839 !bitmap_isset(nsec
->rr
->nsec
.types
, DNS_TYPE_CNAME
) &&
840 !bitmap_isset(nsec
->rr
->nsec
.types
, DNS_TYPE_DNAME
)) {
849 if (log_get_max_level() >= LOG_DEBUG
) {
850 r
= dns_resource_key_to_string(key
, &key_str
);
854 log_debug("%s cache hit for %s",
856 nxdomain
? "NXDOMAIN" : "NODATA",
864 *rcode
= nxdomain
? DNS_RCODE_NXDOMAIN
: DNS_RCODE_SUCCESS
;
865 *authenticated
= have_authenticated
&& !have_non_authenticated
;
869 answer
= dns_answer_new(n
);
873 LIST_FOREACH(by_key
, j
, first
) {
877 r
= dns_answer_add(answer
, j
->rr
, 0, j
->authenticated
? DNS_ANSWER_AUTHENTICATED
: 0);
885 *rcode
= DNS_RCODE_SUCCESS
;
886 *authenticated
= have_authenticated
&& !have_non_authenticated
;
892 int dns_cache_check_conflicts(DnsCache
*cache
, DnsResourceRecord
*rr
, int owner_family
, const union in_addr_union
*owner_address
) {
893 DnsCacheItem
*i
, *first
;
894 bool same_owner
= true;
899 dns_cache_prune(cache
);
901 /* See if there's a cache entry for the same key. If there
902 * isn't there's no conflict */
903 first
= hashmap_get(cache
->by_key
, rr
->key
);
907 /* See if the RR key is owned by the same owner, if so, there
908 * isn't a conflict either */
909 LIST_FOREACH(by_key
, i
, first
) {
910 if (i
->owner_family
!= owner_family
||
911 !in_addr_equal(owner_family
, &i
->owner_address
, owner_address
)) {
919 /* See if there's the exact same RR in the cache. If yes, then
920 * there's no conflict. */
921 if (dns_cache_get(cache
, rr
))
924 /* There's a conflict */
928 int dns_cache_export_shared_to_packet(DnsCache
*cache
, DnsPacket
*p
) {
929 unsigned ancount
= 0;
937 HASHMAP_FOREACH(i
, cache
->by_key
, iterator
) {
940 LIST_FOREACH(by_key
, j
, i
) {
944 if (!j
->shared_owner
)
947 r
= dns_packet_append_rr(p
, j
->rr
, NULL
, NULL
);
948 if (r
== -EMSGSIZE
&& p
->protocol
== DNS_PROTOCOL_MDNS
) {
949 /* For mDNS, if we're unable to stuff all known answers into the given packet,
950 * allocate a new one, push the RR into that one and link it to the current one.
953 DNS_PACKET_HEADER(p
)->ancount
= htobe16(ancount
);
956 r
= dns_packet_new_query(&p
->more
, p
->protocol
, 0, true);
960 /* continue with new packet */
962 r
= dns_packet_append_rr(p
, j
->rr
, NULL
, NULL
);
972 DNS_PACKET_HEADER(p
)->ancount
= htobe16(ancount
);
977 void dns_cache_dump(DnsCache
*cache
, FILE *f
) {
988 HASHMAP_FOREACH(i
, cache
->by_key
, iterator
) {
991 LIST_FOREACH(by_key
, j
, i
) {
997 t
= dns_resource_record_to_string(j
->rr
);
1006 _cleanup_free_
char *z
= NULL
;
1007 r
= dns_resource_key_to_string(j
->key
, &z
);
1015 fputs(j
->type
== DNS_CACHE_NODATA
? "NODATA" : "NXDOMAIN", f
);
1022 bool dns_cache_is_empty(DnsCache
*cache
) {
1026 return hashmap_isempty(cache
->by_key
);
1029 unsigned dns_cache_size(DnsCache
*cache
) {
1033 return hashmap_size(cache
->by_key
);