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 1K entries */
30 #define CACHE_MAX 1024
32 /* We never keep any item longer than 10min in our cache */
33 #define CACHE_TTL_MAX_USEC (10 * USEC_PER_MINUTE)
35 typedef enum DnsCacheItemType DnsCacheItemType
;
36 typedef struct DnsCacheItem DnsCacheItem
;
38 enum DnsCacheItemType
{
46 DnsResourceRecord
*rr
;
48 DnsCacheItemType type
;
52 union in_addr_union owner_address
;
53 LIST_FIELDS(DnsCacheItem
, by_key
);
56 static void dns_cache_item_free(DnsCacheItem
*i
) {
60 dns_resource_record_unref(i
->rr
);
61 dns_resource_key_unref(i
->key
);
65 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem
*, dns_cache_item_free
);
67 static void dns_cache_item_remove_and_free(DnsCache
*c
, DnsCacheItem
*i
) {
75 first
= hashmap_get(c
->by_key
, i
->key
);
76 LIST_REMOVE(by_key
, first
, i
);
79 assert_se(hashmap_replace(c
->by_key
, first
->key
, first
) >= 0);
81 hashmap_remove(c
->by_key
, i
->key
);
83 prioq_remove(c
->by_expiry
, i
, &i
->prioq_idx
);
85 dns_cache_item_free(i
);
88 void dns_cache_flush(DnsCache
*c
) {
93 while ((i
= hashmap_first(c
->by_key
)))
94 dns_cache_item_remove_and_free(c
, i
);
96 assert(hashmap_size(c
->by_key
) == 0);
97 assert(prioq_size(c
->by_expiry
) == 0);
99 c
->by_key
= hashmap_free(c
->by_key
);
100 c
->by_expiry
= prioq_free(c
->by_expiry
);
103 static bool dns_cache_remove(DnsCache
*c
, DnsResourceKey
*key
) {
110 while ((i
= hashmap_get(c
->by_key
, key
))) {
111 dns_cache_item_remove_and_free(c
, i
);
118 static void dns_cache_make_space(DnsCache
*c
, unsigned add
) {
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. */
130 _cleanup_(dns_resource_key_unrefp
) DnsResourceKey
*key
= NULL
;
133 if (prioq_size(c
->by_expiry
) <= 0)
136 if (prioq_size(c
->by_expiry
) + add
< CACHE_MAX
)
139 i
= prioq_peek(c
->by_expiry
);
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
);
149 void dns_cache_prune(DnsCache
*c
) {
154 /* Remove all entries that are past their TTL */
157 _cleanup_(dns_resource_key_unrefp
) DnsResourceKey
*key
= NULL
;
160 i
= prioq_peek(c
->by_expiry
);
165 t
= now(clock_boottime_or_monotonic());
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
);
177 static int dns_cache_item_prioq_compare_func(const void *a
, const void *b
) {
178 const DnsCacheItem
*x
= a
, *y
= b
;
180 if (x
->until
< y
->until
)
182 if (x
->until
> y
->until
)
187 static int dns_cache_init(DnsCache
*c
) {
192 r
= prioq_ensure_allocated(&c
->by_expiry
, dns_cache_item_prioq_compare_func
);
196 r
= hashmap_ensure_allocated(&c
->by_key
, &dns_resource_key_hash_ops
);
203 static int dns_cache_link_item(DnsCache
*c
, DnsCacheItem
*i
) {
210 r
= prioq_put(c
->by_expiry
, i
, &i
->prioq_idx
);
214 first
= hashmap_get(c
->by_key
, i
->key
);
216 LIST_PREPEND(by_key
, first
, i
);
217 assert_se(hashmap_replace(c
->by_key
, first
->key
, first
) >= 0);
219 r
= hashmap_put(c
->by_key
, i
->key
, i
);
221 prioq_remove(c
->by_expiry
, i
, &i
->prioq_idx
);
229 static DnsCacheItem
* dns_cache_get(DnsCache
*c
, DnsResourceRecord
*rr
) {
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)
242 static void dns_cache_item_update_positive(DnsCache
*c
, DnsCacheItem
*i
, DnsResourceRecord
*rr
, bool authenticated
, usec_t timestamp
) {
247 i
->type
= DNS_CACHE_POSITIVE
;
250 /* We are the first item in the list, we need to
251 * update the key used in the hashmap */
253 assert_se(hashmap_replace(c
->by_key
, rr
->key
, i
) >= 0);
255 dns_resource_record_ref(rr
);
256 dns_resource_record_unref(i
->rr
);
259 dns_resource_key_unref(i
->key
);
260 i
->key
= dns_resource_key_ref(rr
->key
);
262 i
->authenticated
= authenticated
;
263 i
->until
= timestamp
+ MIN(rr
->ttl
* USEC_PER_SEC
, CACHE_TTL_MAX_USEC
);
265 prioq_reshuffle(c
->by_expiry
, i
, &i
->prioq_idx
);
268 static int dns_cache_put_positive(
270 DnsResourceRecord
*rr
,
274 const union in_addr_union
*owner_address
) {
276 _cleanup_(dns_cache_item_freep
) DnsCacheItem
*i
= NULL
;
277 _cleanup_free_
char *key_str
= NULL
;
278 DnsCacheItem
*existing
;
283 assert(owner_address
);
285 /* Never cache pseudo RRs */
286 if (dns_class_is_pseudo(rr
->key
->class))
288 if (dns_type_is_pseudo(rr
->key
->type
))
291 /* New TTL is 0? Delete the entry... */
293 k
= dns_cache_remove(c
, rr
->key
);
295 if (log_get_max_level() >= LOG_DEBUG
) {
296 r
= dns_resource_key_to_string(rr
->key
, &key_str
);
301 log_debug("Removed zero TTL entry from cache: %s", key_str
);
303 log_debug("Not caching zero TTL cache entry: %s", key_str
);
309 /* Entry exists already? Update TTL and timestamp */
310 existing
= dns_cache_get(c
, rr
);
312 dns_cache_item_update_positive(c
, existing
, rr
, authenticated
, timestamp
);
316 /* Otherwise, add the new RR */
317 r
= dns_cache_init(c
);
321 dns_cache_make_space(c
, 1);
323 i
= new0(DnsCacheItem
, 1);
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
;
336 r
= dns_cache_link_item(c
, i
);
340 if (log_get_max_level() >= LOG_DEBUG
) {
341 r
= dns_resource_key_to_string(i
->key
, &key_str
);
345 log_debug("Added cache entry for %s", key_str
);
352 static int dns_cache_put_negative(
360 const union in_addr_union
*owner_address
) {
362 _cleanup_(dns_cache_item_freep
) DnsCacheItem
*i
= NULL
;
363 _cleanup_free_
char *key_str
= NULL
;
368 assert(owner_address
);
370 dns_cache_remove(c
, key
);
372 /* Never cache pseudo RR keys */
373 if (dns_class_is_pseudo(key
->class))
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
382 if (log_get_max_level() >= LOG_DEBUG
) {
383 r
= dns_resource_key_to_string(key
, &key_str
);
387 log_debug("Not caching negative entry with zero SOA TTL: %s", key_str
);
393 if (!IN_SET(rcode
, DNS_RCODE_SUCCESS
, DNS_RCODE_NXDOMAIN
))
396 r
= dns_cache_init(c
);
400 dns_cache_make_space(c
, 1);
402 i
= new0(DnsCacheItem
, 1);
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
;
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
));
420 i
->key
= dns_resource_key_ref(key
);
422 r
= dns_cache_link_item(c
, i
);
426 if (log_get_max_level() >= LOG_DEBUG
) {
427 r
= dns_resource_key_to_string(i
->key
, &key_str
);
431 log_debug("Added %s cache entry for %s", i
->type
== DNS_CACHE_NODATA
? "NODATA" : "NXDOMAIN", key_str
);
446 const union in_addr_union
*owner_address
) {
448 DnsResourceRecord
*soa
= NULL
, *rr
;
449 DnsAnswerFlags flags
;
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
);
462 if (log_get_max_level() >= LOG_DEBUG
) {
463 _cleanup_free_
char *key_str
= NULL
;
465 r
= dns_resource_key_to_string(key
, &key_str
);
469 log_debug("Not caching negative entry without a SOA record: %s", key_str
);
475 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
476 if ((flags
& DNS_ANSWER_CACHEABLE
) == 0)
479 if (rr
->key
->cache_flush
)
480 dns_cache_remove(c
, rr
->key
);
483 /* We only care for positive replies and NXDOMAINs, on all
484 * other replies we will simply flush the respective entries,
487 if (!IN_SET(rcode
, DNS_RCODE_SUCCESS
, DNS_RCODE_NXDOMAIN
))
490 cache_keys
= answer
->n_rrs
;
494 /* Make some space for our new entries */
495 dns_cache_make_space(c
, cache_keys
);
498 timestamp
= now(clock_boottime_or_monotonic());
500 /* Second, add in positive entries for all contained RRs */
502 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
503 if ((flags
& DNS_ANSWER_CACHEABLE
) == 0)
506 r
= dns_cache_put_positive(c
, rr
, flags
& DNS_ANSWER_AUTHENTICATED
, timestamp
, owner_family
, owner_address
);
514 /* Third, add in negative entries if the key has no RR */
515 r
= dns_answer_match_key(answer
, key
, NULL
);
521 /* But not if it has a matching CNAME/DNAME (the negative
522 * caching will be done on the canonical name, not on the
524 r
= dns_answer_find_cname_or_dname(answer
, key
, NULL
, NULL
);
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. */
534 r
= dns_answer_find_soa(answer
, key
, &soa
);
540 r
= dns_cache_put_negative(c
, key
, rcode
, authenticated
, timestamp
, MIN(soa
->soa
.minimum
, soa
->ttl
), owner_family
, owner_address
);
547 /* Adding all RRs failed. Let's clean up what we already
548 * added, just in case */
551 dns_cache_remove(c
, key
);
553 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
554 if ((flags
& DNS_ANSWER_CACHEABLE
) == 0)
557 dns_cache_remove(c
, rr
->key
);
563 static DnsCacheItem
*dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache
*c
, DnsResourceKey
*k
) {
571 /* If we hit some OOM error, or suchlike, we don't care too
572 * much, after all this is just a cache */
574 i
= hashmap_get(c
->by_key
, k
);
578 n
= DNS_RESOURCE_KEY_NAME(k
);
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
)
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
));
596 /* OK, let's look for cached DNAME records. */
598 char label
[DNS_LABEL_MAX
];
603 i
= hashmap_get(c
->by_key
, &DNS_RESOURCE_KEY_CONST(k
->class, DNS_TYPE_DNAME
, n
));
607 /* Jump one label ahead */
608 r
= dns_label_unescape(&n
, label
, sizeof(label
));
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
));
624 int dns_cache_lookup(DnsCache
*c
, DnsResourceKey
*key
, int *rcode
, DnsAnswer
**ret
, bool *authenticated
) {
625 _cleanup_(dns_answer_unrefp
) DnsAnswer
*answer
= NULL
;
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;
637 assert(authenticated
);
639 if (key
->type
== DNS_TYPE_ANY
||
640 key
->class == DNS_CLASS_ANY
) {
642 /* If we have ANY lookups we don't use the cache, so
643 * that the caller refreshes via the network. */
645 if (log_get_max_level() >= LOG_DEBUG
) {
646 r
= dns_resource_key_to_string(key
, &key_str
);
650 log_debug("Ignoring cache for ANY lookup: %s", key_str
);
654 *rcode
= DNS_RCODE_SUCCESS
;
658 first
= dns_cache_get_by_key_follow_cname_dname_nsec(c
, key
);
660 /* If one question cannot be answered we need to refresh */
662 if (log_get_max_level() >= LOG_DEBUG
) {
663 r
= dns_resource_key_to_string(key
, &key_str
);
667 log_debug("Cache miss for %s", key_str
);
671 *rcode
= DNS_RCODE_SUCCESS
;
675 LIST_FOREACH(by_key
, j
, first
) {
677 if (j
->rr
->key
->type
== DNS_TYPE_NSEC
)
681 } else if (j
->type
== DNS_CACHE_NXDOMAIN
)
684 if (j
->authenticated
)
685 have_authenticated
= true;
687 have_non_authenticated
= true;
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
);
696 log_debug("NSEC NODATA cache hit for %s", key_str
);
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. */
704 *rcode
= DNS_RCODE_SUCCESS
;
705 *authenticated
= nsec
->authenticated
;
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
);
712 if (log_get_max_level() >= LOG_DEBUG
) {
713 r
= dns_resource_key_to_string(key
, &key_str
);
717 log_debug("%s cache hit for %s",
719 nxdomain
? "NXDOMAIN" : "NODATA",
725 *rcode
= nxdomain
? DNS_RCODE_NXDOMAIN
: DNS_RCODE_SUCCESS
;
726 *authenticated
= have_authenticated
&& !have_non_authenticated
;
730 answer
= dns_answer_new(n
);
734 LIST_FOREACH(by_key
, j
, first
) {
738 r
= dns_answer_add(answer
, j
->rr
, 0, have_authenticated
&& !have_non_authenticated
? DNS_ANSWER_AUTHENTICATED
: 0);
744 *rcode
= DNS_RCODE_SUCCESS
;
745 *authenticated
= have_authenticated
&& !have_non_authenticated
;
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;
758 dns_cache_prune(cache
);
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
);
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
)) {
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
))
783 /* There's a conflict */
787 int dns_cache_export_shared_to_packet(DnsCache
*cache
, DnsPacket
*p
) {
788 unsigned ancount
= 0;
796 HASHMAP_FOREACH(i
, cache
->by_key
, iterator
) {
799 LIST_FOREACH(by_key
, j
, i
) {
803 if (!dns_key_is_shared(j
->rr
->key
))
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.
812 DNS_PACKET_HEADER(p
)->ancount
= htobe16(ancount
);
815 r
= dns_packet_new_query(&p
->more
, p
->protocol
, 0, true);
819 /* continue with new packet */
821 r
= dns_packet_append_rr(p
, j
->rr
, NULL
, NULL
);
831 DNS_PACKET_HEADER(p
)->ancount
= htobe16(ancount
);
836 void dns_cache_dump(DnsCache
*cache
, FILE *f
) {
847 HASHMAP_FOREACH(i
, cache
->by_key
, iterator
) {
850 LIST_FOREACH(by_key
, j
, i
) {
851 _cleanup_free_
char *t
= NULL
;
856 r
= dns_resource_record_to_string(j
->rr
, &t
);
865 r
= dns_resource_key_to_string(j
->key
, &t
);
873 fputs(j
->type
== DNS_CACHE_NODATA
? "NODATA" : "NXDOMAIN", f
);
880 bool dns_cache_is_empty(DnsCache
*cache
) {
884 return hashmap_isempty(cache
->by_key
);