]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-cache.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / resolve / resolved-dns-cache.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
322345fd
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2014 Lennart Poettering
6
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.
11
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.
16
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/>.
19***/
20
202b76ae
ZJS
21#include <net/if.h>
22
23#include "af-list.h"
b5efdb8a 24#include "alloc-util.h"
58db254a 25#include "dns-domain.h"
02c2857b 26#include "resolved-dns-answer.h"
322345fd 27#include "resolved-dns-cache.h"
7e8e0422 28#include "resolved-dns-packet.h"
58db254a 29#include "string-util.h"
322345fd 30
6af47493
LP
31/* Never cache more than 4K entries. RFC 1536, Section 5 suggests to
32 * leave DNS caches unbounded, but that's crazy. */
d98e5504 33#define CACHE_MAX 4096
cbd4560e 34
d98e5504
LP
35/* We never keep any item longer than 2h in our cache */
36#define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR)
322345fd 37
201d9958
LP
38/* How long to cache strange rcodes, i.e. rcodes != SUCCESS and != NXDOMAIN (specifically: that's only SERVFAIL for
39 * now) */
40#define CACHE_TTL_STRANGE_RCODE_USEC (30 * USEC_PER_SEC)
41
623a4c97
LP
42typedef enum DnsCacheItemType DnsCacheItemType;
43typedef struct DnsCacheItem DnsCacheItem;
44
45enum DnsCacheItemType {
46 DNS_CACHE_POSITIVE,
47 DNS_CACHE_NODATA,
48 DNS_CACHE_NXDOMAIN,
201d9958 49 DNS_CACHE_RCODE, /* "strange" RCODE (effective only SERVFAIL for now) */
623a4c97
LP
50};
51
52struct DnsCacheItem {
d2579eec 53 DnsCacheItemType type;
623a4c97
LP
54 DnsResourceKey *key;
55 DnsResourceRecord *rr;
201d9958 56 int rcode;
d2579eec 57
623a4c97 58 usec_t until;
d2579eec
LP
59 bool authenticated:1;
60 bool shared_owner:1;
61
06d12754 62 int ifindex;
a4076574
LP
63 int owner_family;
64 union in_addr_union owner_address;
d2579eec
LP
65
66 unsigned prioq_idx;
623a4c97
LP
67 LIST_FIELDS(DnsCacheItem, by_key);
68};
69
201d9958
LP
70static const char *dns_cache_item_type_to_string(DnsCacheItem *item) {
71 assert(item);
72
73 switch (item->type) {
74
75 case DNS_CACHE_POSITIVE:
76 return "POSITIVE";
77
78 case DNS_CACHE_NODATA:
79 return "NODATA";
80
81 case DNS_CACHE_NXDOMAIN:
82 return "NXDOMAIN";
83
84 case DNS_CACHE_RCODE:
85 return dns_rcode_to_string(item->rcode);
86 }
87
88 return NULL;
89}
90
322345fd
LP
91static void dns_cache_item_free(DnsCacheItem *i) {
92 if (!i)
93 return;
94
95 dns_resource_record_unref(i->rr);
7e8e0422 96 dns_resource_key_unref(i->key);
322345fd
LP
97 free(i);
98}
99
100DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem*, dns_cache_item_free);
101
39963f11 102static void dns_cache_item_unlink_and_free(DnsCache *c, DnsCacheItem *i) {
322345fd
LP
103 DnsCacheItem *first;
104
105 assert(c);
106
107 if (!i)
108 return;
109
7e8e0422
LP
110 first = hashmap_get(c->by_key, i->key);
111 LIST_REMOVE(by_key, first, i);
322345fd
LP
112
113 if (first)
7e8e0422 114 assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
322345fd 115 else
7e8e0422 116 hashmap_remove(c->by_key, i->key);
322345fd 117
7e8e0422 118 prioq_remove(c->by_expiry, i, &i->prioq_idx);
322345fd
LP
119
120 dns_cache_item_free(i);
121}
122
f5bdeb01
LP
123static bool dns_cache_remove_by_rr(DnsCache *c, DnsResourceRecord *rr) {
124 DnsCacheItem *first, *i;
125 int r;
126
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);
130 if (r < 0)
131 return r;
132 if (r > 0) {
39963f11 133 dns_cache_item_unlink_and_free(c, i);
f5bdeb01
LP
134 return true;
135 }
136 }
137
138 return false;
139}
140
2dda578f 141static bool dns_cache_remove_by_key(DnsCache *c, DnsResourceKey *key) {
1f97052f 142 DnsCacheItem *first, *i, *n;
322345fd
LP
143
144 assert(c);
145 assert(key);
146
1f97052f
LP
147 first = hashmap_remove(c->by_key, key);
148 if (!first)
149 return false;
150
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);
6b34a6c9
TG
154 }
155
1f97052f 156 return true;
322345fd
LP
157}
158
ef9a3e3c
LP
159void dns_cache_flush(DnsCache *c) {
160 DnsResourceKey *key;
161
162 assert(c);
163
164 while ((key = hashmap_first_key(c->by_key)))
2dda578f 165 dns_cache_remove_by_key(c, key);
ef9a3e3c
LP
166
167 assert(hashmap_size(c->by_key) == 0);
168 assert(prioq_size(c->by_expiry) == 0);
169
170 c->by_key = hashmap_free(c->by_key);
171 c->by_expiry = prioq_free(c->by_expiry);
172}
173
322345fd
LP
174static void dns_cache_make_space(DnsCache *c, unsigned add) {
175 assert(c);
176
177 if (add <= 0)
178 return;
179
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. */
184
185 for (;;) {
faa133f3 186 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
322345fd
LP
187 DnsCacheItem *i;
188
7e8e0422 189 if (prioq_size(c->by_expiry) <= 0)
322345fd
LP
190 break;
191
7e8e0422 192 if (prioq_size(c->by_expiry) + add < CACHE_MAX)
322345fd
LP
193 break;
194
7e8e0422 195 i = prioq_peek(c->by_expiry);
cbd4560e
LP
196 assert(i);
197
faa133f3 198 /* Take an extra reference to the key so that it
cbd4560e 199 * doesn't go away in the middle of the remove call */
7e8e0422 200 key = dns_resource_key_ref(i->key);
2dda578f 201 dns_cache_remove_by_key(c, key);
322345fd
LP
202 }
203}
204
205void dns_cache_prune(DnsCache *c) {
206 usec_t t = 0;
207
208 assert(c);
209
210 /* Remove all entries that are past their TTL */
211
212 for (;;) {
213 DnsCacheItem *i;
202b76ae 214 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
322345fd 215
7e8e0422 216 i = prioq_peek(c->by_expiry);
322345fd
LP
217 if (!i)
218 break;
219
322345fd 220 if (t <= 0)
240b589b 221 t = now(clock_boottime_or_monotonic());
322345fd 222
7e8e0422 223 if (i->until > t)
322345fd
LP
224 break;
225
d2579eec 226 /* Depending whether this is an mDNS shared entry
202b76ae
ZJS
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);
232
d2579eec 233 if (i->shared_owner)
39963f11 234 dns_cache_item_unlink_and_free(c, i);
d2579eec
LP
235 else {
236 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
237
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);
2dda578f 241 dns_cache_remove_by_key(c, key);
d2579eec 242 }
322345fd
LP
243 }
244}
245
246static int dns_cache_item_prioq_compare_func(const void *a, const void *b) {
322345fd
LP
247 const DnsCacheItem *x = a, *y = b;
248
7e8e0422 249 if (x->until < y->until)
322345fd 250 return -1;
7e8e0422 251 if (x->until > y->until)
322345fd
LP
252 return 1;
253 return 0;
254}
255
623a4c97 256static int dns_cache_init(DnsCache *c) {
7e8e0422
LP
257 int r;
258
623a4c97
LP
259 assert(c);
260
7e8e0422
LP
261 r = prioq_ensure_allocated(&c->by_expiry, dns_cache_item_prioq_compare_func);
262 if (r < 0)
263 return r;
264
d5099efc 265 r = hashmap_ensure_allocated(&c->by_key, &dns_resource_key_hash_ops);
7e8e0422
LP
266 if (r < 0)
267 return r;
268
269 return r;
270}
271
272static int dns_cache_link_item(DnsCache *c, DnsCacheItem *i) {
273 DnsCacheItem *first;
274 int r;
275
322345fd
LP
276 assert(c);
277 assert(i);
322345fd 278
7e8e0422
LP
279 r = prioq_put(c->by_expiry, i, &i->prioq_idx);
280 if (r < 0)
281 return r;
322345fd 282
7e8e0422
LP
283 first = hashmap_get(c->by_key, i->key);
284 if (first) {
f57e3cd5
LP
285 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
286
287 /* Keep a reference to the original key, while we manipulate the list. */
288 k = dns_resource_key_ref(first->key);
289
290 /* Now, try to reduce the number of keys we keep */
291 dns_resource_key_reduce(&first->key, &i->key);
292
293 if (first->rr)
294 dns_resource_key_reduce(&first->rr->key, &i->key);
295 if (i->rr)
296 dns_resource_key_reduce(&i->rr->key, &i->key);
297
7e8e0422
LP
298 LIST_PREPEND(by_key, first, i);
299 assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
300 } else {
301 r = hashmap_put(c->by_key, i->key, i);
302 if (r < 0) {
303 prioq_remove(c->by_expiry, i, &i->prioq_idx);
304 return r;
305 }
322345fd
LP
306 }
307
7e8e0422 308 return 0;
322345fd
LP
309}
310
faa133f3
LP
311static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
312 DnsCacheItem *i;
313
314 assert(c);
315 assert(rr);
316
7e8e0422 317 LIST_FOREACH(by_key, i, hashmap_get(c->by_key, rr->key))
3ef77d04 318 if (i->rr && dns_resource_record_equal(i->rr, rr) > 0)
faa133f3
LP
319 return i;
320
321 return NULL;
322}
323
d3760be0 324static usec_t calculate_until(DnsResourceRecord *rr, uint32_t nsec_ttl, usec_t timestamp, bool use_soa_minimum) {
b211dc7e
LP
325 uint32_t ttl;
326 usec_t u;
ee3d6aff
LP
327
328 assert(rr);
329
d3760be0 330 ttl = MIN(rr->ttl, nsec_ttl);
b211dc7e
LP
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
336 * 5. */
ee3d6aff 337
b211dc7e
LP
338 if (ttl > rr->soa.minimum)
339 ttl = rr->soa.minimum;
340 }
341
342 u = ttl * USEC_PER_SEC;
343 if (u > CACHE_TTL_MAX_USEC)
344 u = CACHE_TTL_MAX_USEC;
ee3d6aff
LP
345
346 if (rr->expiry != USEC_INFINITY) {
347 usec_t left;
348
b211dc7e
LP
349 /* Make use of the DNSSEC RRSIG expiry info, if we
350 * have it */
ee3d6aff
LP
351
352 left = LESS_BY(rr->expiry, now(CLOCK_REALTIME));
b211dc7e
LP
353 if (u > left)
354 u = left;
ee3d6aff
LP
355 }
356
b211dc7e 357 return timestamp + u;
ee3d6aff
LP
358}
359
d2579eec
LP
360static void dns_cache_item_update_positive(
361 DnsCache *c,
362 DnsCacheItem *i,
363 DnsResourceRecord *rr,
364 bool authenticated,
365 bool shared_owner,
366 usec_t timestamp,
06d12754 367 int ifindex,
d2579eec
LP
368 int owner_family,
369 const union in_addr_union *owner_address) {
370
7e8e0422
LP
371 assert(c);
372 assert(i);
373 assert(rr);
d2579eec 374 assert(owner_address);
7e8e0422
LP
375
376 i->type = DNS_CACHE_POSITIVE;
377
ece174c5 378 if (!i->by_key_prev)
7e8e0422
LP
379 /* We are the first item in the list, we need to
380 * update the key used in the hashmap */
381
382 assert_se(hashmap_replace(c->by_key, rr->key, i) >= 0);
7e8e0422
LP
383
384 dns_resource_record_ref(rr);
385 dns_resource_record_unref(i->rr);
386 i->rr = rr;
387
388 dns_resource_key_unref(i->key);
389 i->key = dns_resource_key_ref(rr->key);
390
d3760be0 391 i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
d2579eec
LP
392 i->authenticated = authenticated;
393 i->shared_owner = shared_owner;
394
06d12754
LP
395 i->ifindex = ifindex;
396
d2579eec
LP
397 i->owner_family = owner_family;
398 i->owner_address = *owner_address;
7e8e0422
LP
399
400 prioq_reshuffle(c->by_expiry, i, &i->prioq_idx);
401}
402
a4076574
LP
403static int dns_cache_put_positive(
404 DnsCache *c,
405 DnsResourceRecord *rr,
931851e8 406 bool authenticated,
d2579eec 407 bool shared_owner,
a4076574 408 usec_t timestamp,
06d12754 409 int ifindex,
a4076574
LP
410 int owner_family,
411 const union in_addr_union *owner_address) {
412
322345fd 413 _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
7e8e0422 414 DnsCacheItem *existing;
202b76ae 415 char key_str[DNS_RESOURCE_KEY_STRING_MAX], ifname[IF_NAMESIZE];
a257f9d4 416 int r, k;
322345fd
LP
417
418 assert(c);
419 assert(rr);
a4076574 420 assert(owner_address);
322345fd 421
222148b6
LP
422 /* Never cache pseudo RRs */
423 if (dns_class_is_pseudo(rr->key->class))
424 return 0;
425 if (dns_type_is_pseudo(rr->key->type))
426 return 0;
427
f5bdeb01 428 /* New TTL is 0? Delete this specific entry... */
322345fd 429 if (rr->ttl <= 0) {
f5bdeb01 430 k = dns_cache_remove_by_rr(c, rr);
202b76ae
ZJS
431 log_debug("%s: %s",
432 k > 0 ? "Removed zero TTL entry from cache" : "Not caching zero TTL cache entry",
18665d1f 433 dns_resource_key_to_string(rr->key, key_str, sizeof key_str));
322345fd
LP
434 return 0;
435 }
436
13e785f7 437 /* Entry exists already? Update TTL, timestamp and owner */
322345fd
LP
438 existing = dns_cache_get(c, rr);
439 if (existing) {
d2579eec
LP
440 dns_cache_item_update_positive(
441 c,
442 existing,
443 rr,
444 authenticated,
445 shared_owner,
446 timestamp,
06d12754 447 ifindex,
d2579eec
LP
448 owner_family,
449 owner_address);
322345fd
LP
450 return 0;
451 }
452
453 /* Otherwise, add the new RR */
623a4c97 454 r = dns_cache_init(c);
322345fd
LP
455 if (r < 0)
456 return r;
457
7e8e0422
LP
458 dns_cache_make_space(c, 1);
459
460 i = new0(DnsCacheItem, 1);
461 if (!i)
462 return -ENOMEM;
463
464 i->type = DNS_CACHE_POSITIVE;
465 i->key = dns_resource_key_ref(rr->key);
466 i->rr = dns_resource_record_ref(rr);
d3760be0 467 i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
d2579eec
LP
468 i->authenticated = authenticated;
469 i->shared_owner = shared_owner;
06d12754 470 i->ifindex = ifindex;
a4076574
LP
471 i->owner_family = owner_family;
472 i->owner_address = *owner_address;
d2579eec 473 i->prioq_idx = PRIOQ_IDX_NULL;
7e8e0422
LP
474
475 r = dns_cache_link_item(c, i);
476 if (r < 0)
477 return r;
478
a257f9d4 479 if (log_get_max_level() >= LOG_DEBUG) {
202b76ae
ZJS
480 _cleanup_free_ char *t = NULL;
481
482 (void) in_addr_to_string(i->owner_family, &i->owner_address, &t);
483
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),
491 strna(t));
a257f9d4 492 }
6b34a6c9 493
7e8e0422
LP
494 i = NULL;
495 return 0;
496}
497
a4076574
LP
498static int dns_cache_put_negative(
499 DnsCache *c,
500 DnsResourceKey *key,
501 int rcode,
931851e8 502 bool authenticated,
d3760be0 503 uint32_t nsec_ttl,
a4076574 504 usec_t timestamp,
b211dc7e 505 DnsResourceRecord *soa,
a4076574
LP
506 int owner_family,
507 const union in_addr_union *owner_address) {
508
7e8e0422 509 _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
202b76ae 510 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
7e8e0422
LP
511 int r;
512
513 assert(c);
514 assert(key);
a4076574 515 assert(owner_address);
7e8e0422 516
98b6be77
LP
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 */
222148b6 520 if (dns_class_is_pseudo(key->class))
ddf16339 521 return 0;
c33be4a6 522 if (dns_type_is_pseudo(key->type))
ddf16339 523 return 0;
222148b6 524
201d9958
LP
525 if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
526 if (!soa)
527 return 0;
ddf16339 528
201d9958
LP
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));
533 return 0;
534 }
535 } else if (rcode != DNS_RCODE_SERVFAIL)
7e8e0422
LP
536 return 0;
537
623a4c97 538 r = dns_cache_init(c);
322345fd
LP
539 if (r < 0)
540 return r;
541
542 dns_cache_make_space(c, 1);
543
544 i = new0(DnsCacheItem, 1);
545 if (!i)
546 return -ENOMEM;
547
201d9958
LP
548 i->type =
549 rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA :
550 rcode == DNS_RCODE_NXDOMAIN ? DNS_CACHE_NXDOMAIN : DNS_CACHE_RCODE;
551 i->until =
552 i->type == DNS_CACHE_RCODE ? timestamp + CACHE_TTL_STRANGE_RCODE_USEC :
553 calculate_until(soa, nsec_ttl, timestamp, true);
d2579eec 554 i->authenticated = authenticated;
a4076574
LP
555 i->owner_family = owner_family;
556 i->owner_address = *owner_address;
d2579eec 557 i->prioq_idx = PRIOQ_IDX_NULL;
201d9958 558 i->rcode = rcode;
322345fd 559
71e13669
TG
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. */
1c02e7ba 563 i->key = dns_resource_key_new(key->class, DNS_TYPE_ANY, dns_resource_key_name(key));
71e13669
TG
564 if (!i->key)
565 return -ENOMEM;
a5444ca9
LP
566
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);
71e13669
TG
574 } else
575 i->key = dns_resource_key_ref(key);
576
7e8e0422 577 r = dns_cache_link_item(c, i);
322345fd
LP
578 if (r < 0)
579 return r;
580
202b76ae 581 log_debug("Added %s cache entry for %s "USEC_FMT"s",
201d9958 582 dns_cache_item_type_to_string(i),
202b76ae
ZJS
583 dns_resource_key_to_string(i->key, key_str, sizeof key_str),
584 (i->until - timestamp) / USEC_PER_SEC);
6b34a6c9 585
322345fd 586 i = NULL;
322345fd
LP
587 return 0;
588}
589
d2579eec
LP
590static void dns_cache_remove_previous(
591 DnsCache *c,
592 DnsResourceKey *key,
593 DnsAnswer *answer) {
594
595 DnsResourceRecord *rr;
596 DnsAnswerFlags flags;
597
598 assert(c);
599
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. */
603 if (key)
2dda578f 604 dns_cache_remove_by_key(c, key);
d2579eec
LP
605
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)
611 continue;
612
613 if (flags & DNS_ANSWER_SHARED_OWNER)
614 continue;
615
2dda578f 616 dns_cache_remove_by_key(c, rr->key);
d2579eec
LP
617 }
618}
619
f6618dcd
LP
620static bool rr_eligible(DnsResourceRecord *rr) {
621 assert(rr);
622
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. */
626
627 switch (rr->key->type) {
628
629 case DNS_TYPE_NSEC:
630 return !bitmap_isset(rr->nsec.types, DNS_TYPE_NS) ||
631 bitmap_isset(rr->nsec.types, DNS_TYPE_SOA);
632
633 case DNS_TYPE_NSEC3:
634 return !bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) ||
635 bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA);
636
637 default:
638 return true;
639 }
640}
641
a4076574
LP
642int dns_cache_put(
643 DnsCache *c,
8e427d9b 644 DnsResourceKey *key,
a4076574
LP
645 int rcode,
646 DnsAnswer *answer,
931851e8 647 bool authenticated,
d3760be0 648 uint32_t nsec_ttl,
a4076574
LP
649 usec_t timestamp,
650 int owner_family,
651 const union in_addr_union *owner_address) {
652
02c2857b 653 DnsResourceRecord *soa = NULL, *rr;
201d9958 654 bool weird_rcode = false;
105e1512
LP
655 DnsAnswerFlags flags;
656 unsigned cache_keys;
06d12754 657 int r, ifindex;
322345fd
LP
658
659 assert(c);
d2579eec 660 assert(owner_address);
322345fd 661
d2579eec 662 dns_cache_remove_previous(c, key, answer);
0ec7c46e 663
201d9958
LP
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
667 * short time.) */
6ff01a0d 668
201d9958 669 if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
c3cb6dc2 670
201d9958
LP
671 if (dns_answer_size(answer) <= 0) {
672 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
673
674 log_debug("Not caching negative entry without a SOA record: %s",
675 dns_resource_key_to_string(key, key_str, sizeof key_str));
676 return 0;
677 }
678
679 } else {
680 /* Only cache SERVFAIL as "weird" rcode for now. We can add more later, should that turn out to be
681 * beneficial. */
682 if (rcode != DNS_RCODE_SERVFAIL)
683 return 0;
684
685 weird_rcode = true;
c3cb6dc2 686 }
0ec7c46e 687
ea207b63 688 cache_keys = dns_answer_size(answer);
8e427d9b 689 if (key)
313cefa1 690 cache_keys++;
eff91ee0 691
7e8e0422 692 /* Make some space for our new entries */
eff91ee0 693 dns_cache_make_space(c, cache_keys);
322345fd 694
7e8e0422 695 if (timestamp <= 0)
240b589b 696 timestamp = now(clock_boottime_or_monotonic());
322345fd 697
7e8e0422 698 /* Second, add in positive entries for all contained RRs */
06d12754 699 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
105e1512
LP
700 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
701 continue;
702
f6618dcd
LP
703 r = rr_eligible(rr);
704 if (r < 0)
705 return r;
706 if (r == 0)
707 continue;
708
d2579eec
LP
709 r = dns_cache_put_positive(
710 c,
711 rr,
712 flags & DNS_ANSWER_AUTHENTICATED,
713 flags & DNS_ANSWER_SHARED_OWNER,
714 timestamp,
06d12754 715 ifindex,
d2579eec 716 owner_family, owner_address);
7e8e0422
LP
717 if (r < 0)
718 goto fail;
719 }
720
d2579eec 721 if (!key) /* mDNS doesn't know negative caching, really */
eff91ee0
DM
722 return 0;
723
8e427d9b 724 /* Third, add in negative entries if the key has no RR */
105e1512 725 r = dns_answer_match_key(answer, key, NULL);
8e427d9b
TG
726 if (r < 0)
727 goto fail;
728 if (r > 0)
729 return 0;
7e8e0422 730
5d27351f
TG
731 /* But not if it has a matching CNAME/DNAME (the negative
732 * caching will be done on the canonical name, not on the
733 * alias) */
105e1512 734 r = dns_answer_find_cname_or_dname(answer, key, NULL, NULL);
5d27351f
TG
735 if (r < 0)
736 goto fail;
737 if (r > 0)
738 return 0;
739
201d9958
LP
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. */
fd009cd8 743 r = dns_answer_find_soa(answer, key, &soa, &flags);
8e427d9b
TG
744 if (r < 0)
745 goto fail;
201d9958 746 if (r == 0 && !weird_rcode)
fd009cd8 747 return 0;
201d9958
LP
748 if (r > 0) {
749 /* Refuse using the SOA data if it is unsigned, but the key is
750 * signed */
751 if (authenticated && (flags & DNS_ANSWER_AUTHENTICATED) == 0)
752 return 0;
753 }
fd009cd8 754
d2579eec
LP
755 r = dns_cache_put_negative(
756 c,
757 key,
758 rcode,
759 authenticated,
d3760be0 760 nsec_ttl,
d2579eec 761 timestamp,
b211dc7e 762 soa,
d2579eec 763 owner_family, owner_address);
8e427d9b
TG
764 if (r < 0)
765 goto fail;
322345fd
LP
766
767 return 0;
768
769fail:
770 /* Adding all RRs failed. Let's clean up what we already
771 * added, just in case */
772
8e427d9b 773 if (key)
2dda578f 774 dns_cache_remove_by_key(c, key);
eff91ee0 775
105e1512
LP
776 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
777 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
778 continue;
779
2dda578f 780 dns_cache_remove_by_key(c, rr->key);
105e1512 781 }
322345fd
LP
782
783 return r;
784}
785
37da8931 786static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, DnsResourceKey *k) {
58db254a
LP
787 DnsCacheItem *i;
788 const char *n;
789 int r;
5643c00a
TG
790
791 assert(c);
792 assert(k);
793
58db254a
LP
794 /* If we hit some OOM error, or suchlike, we don't care too
795 * much, after all this is just a cache */
796
5643c00a 797 i = hashmap_get(c->by_key, k);
d7ce6c94 798 if (i)
37da8931
LP
799 return i;
800
1c02e7ba 801 n = dns_resource_key_name(k);
37da8931 802
71e13669
TG
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)
807 return i;
808
d3c7e913 809 if (dns_type_may_redirect(k->type)) {
d7ce6c94
TG
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));
812 if (i)
813 return i;
5643c00a 814
d7ce6c94
TG
815 /* OK, let's look for cached DNAME records. */
816 for (;;) {
d7ce6c94
TG
817 if (isempty(n))
818 return NULL;
819
820 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_DNAME, n));
821 if (i)
822 return i;
58db254a 823
d7ce6c94 824 /* Jump one label ahead */
950b692b 825 r = dns_name_parent(&n);
d7ce6c94
TG
826 if (r <= 0)
827 return NULL;
828 }
829 }
5643c00a 830
950b692b 831 if (k->type != DNS_TYPE_NSEC) {
d7ce6c94
TG
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));
58db254a
LP
834 if (i)
835 return i;
58db254a
LP
836 }
837
838 return NULL;
5643c00a
TG
839}
840
17c8de63 841int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcode, DnsAnswer **ret, bool *authenticated) {
faa133f3 842 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
202b76ae 843 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
f52e61da 844 unsigned n = 0;
322345fd 845 int r;
7e8e0422 846 bool nxdomain = false;
931851e8
LP
847 DnsCacheItem *j, *first, *nsec = NULL;
848 bool have_authenticated = false, have_non_authenticated = false;
17c8de63 849 usec_t current;
201d9958 850 int found_rcode = -1;
322345fd
LP
851
852 assert(c);
f52e61da 853 assert(key);
623a4c97 854 assert(rcode);
faa133f3 855 assert(ret);
931851e8 856 assert(authenticated);
322345fd 857
202b76ae 858 if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
931851e8
LP
859 /* If we have ANY lookups we don't use the cache, so
860 * that the caller refreshes via the network. */
322345fd 861
202b76ae
ZJS
862 log_debug("Ignoring cache for ANY lookup: %s",
863 dns_resource_key_to_string(key, key_str, sizeof key_str));
6b34a6c9 864
a150ff5e
LP
865 c->n_miss++;
866
f52e61da
LP
867 *ret = NULL;
868 *rcode = DNS_RCODE_SUCCESS;
2d4a4e14
LP
869 *authenticated = false;
870
f52e61da
LP
871 return 0;
872 }
6b34a6c9 873
37da8931 874 first = dns_cache_get_by_key_follow_cname_dname_nsec(c, key);
f52e61da
LP
875 if (!first) {
876 /* If one question cannot be answered we need to refresh */
ddf16339 877
202b76ae
ZJS
878 log_debug("Cache miss for %s",
879 dns_resource_key_to_string(key, key_str, sizeof key_str));
6b34a6c9 880
a150ff5e
LP
881 c->n_miss++;
882
f52e61da
LP
883 *ret = NULL;
884 *rcode = DNS_RCODE_SUCCESS;
2d4a4e14
LP
885 *authenticated = false;
886
f52e61da
LP
887 return 0;
888 }
6b34a6c9 889
f52e61da 890 LIST_FOREACH(by_key, j, first) {
37da8931
LP
891 if (j->rr) {
892 if (j->rr->key->type == DNS_TYPE_NSEC)
931851e8
LP
893 nsec = j;
894
f52e61da 895 n++;
37da8931 896 } else if (j->type == DNS_CACHE_NXDOMAIN)
f52e61da 897 nxdomain = true;
201d9958
LP
898 else if (j->type == DNS_CACHE_RCODE)
899 found_rcode = j->rcode;
931851e8
LP
900
901 if (j->authenticated)
902 have_authenticated = true;
903 else
904 have_non_authenticated = true;
f52e61da 905 }
6b34a6c9 906
201d9958
LP
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)));
911
912 *ret = NULL;
913 *rcode = found_rcode;
914 *authenticated = false;
915
916 c->n_hit++;
917 return 1;
918 }
919
f6618dcd
LP
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. */
923
202b76ae
ZJS
924 log_debug("NSEC NODATA cache hit for %s",
925 dns_resource_key_to_string(key, key_str, sizeof key_str));
37da8931
LP
926
927 /* We only found an NSEC record that matches our name.
a257f9d4 928 * If it says the type doesn't exist report
37da8931
LP
929 * NODATA. Otherwise report a cache miss. */
930
931 *ret = NULL;
932 *rcode = DNS_RCODE_SUCCESS;
931851e8 933 *authenticated = nsec->authenticated;
37da8931 934
a150ff5e
LP
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)) {
938 c->n_hit++;
939 return 1;
940 }
941
942 c->n_miss++;
943 return 0;
37da8931
LP
944 }
945
202b76ae
ZJS
946 log_debug("%s cache hit for %s",
947 n > 0 ? "Positive" :
948 nxdomain ? "NXDOMAIN" : "NODATA",
949 dns_resource_key_to_string(key, key_str, sizeof key_str));
faa133f3 950
7e8e0422 951 if (n <= 0) {
a150ff5e
LP
952 c->n_hit++;
953
7e8e0422
LP
954 *ret = NULL;
955 *rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
931851e8 956 *authenticated = have_authenticated && !have_non_authenticated;
7e8e0422
LP
957 return 1;
958 }
faa133f3
LP
959
960 answer = dns_answer_new(n);
961 if (!answer)
962 return -ENOMEM;
322345fd 963
17c8de63
LP
964 if (clamp_ttl)
965 current = now(clock_boottime_or_monotonic());
966
f52e61da 967 LIST_FOREACH(by_key, j, first) {
17c8de63
LP
968 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
969
f52e61da
LP
970 if (!j->rr)
971 continue;
972
17c8de63
LP
973 if (clamp_ttl) {
974 rr = dns_resource_record_ref(j->rr);
975
976 r = dns_resource_record_clamp_ttl(&rr, LESS_BY(j->until, current) / USEC_PER_SEC);
977 if (r < 0)
978 return r;
979 }
980
981 r = dns_answer_add(answer, rr ?: j->rr, j->ifindex, j->authenticated ? DNS_ANSWER_AUTHENTICATED : 0);
f52e61da
LP
982 if (r < 0)
983 return r;
322345fd
LP
984 }
985
a150ff5e
LP
986 c->n_hit++;
987
faa133f3 988 *ret = answer;
7e8e0422 989 *rcode = DNS_RCODE_SUCCESS;
931851e8 990 *authenticated = have_authenticated && !have_non_authenticated;
faa133f3
LP
991 answer = NULL;
992
993 return n;
322345fd 994}
a4076574
LP
995
996int 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;
999
1000 assert(cache);
1001 assert(rr);
1002
1003 dns_cache_prune(cache);
1004
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);
1008 if (!first)
1009 return 0;
1010
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)) {
1016 same_owner = false;
1017 break;
1018 }
1019 }
1020 if (same_owner)
1021 return 0;
1022
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))
1026 return 0;
1027
1028 /* There's a conflict */
1029 return 1;
1030}
4d506d6b 1031
7778dfff
DM
1032int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
1033 unsigned ancount = 0;
1034 Iterator iterator;
1035 DnsCacheItem *i;
1036 int r;
1037
1038 assert(cache);
261f3673 1039 assert(p);
7778dfff
DM
1040
1041 HASHMAP_FOREACH(i, cache->by_key, iterator) {
1042 DnsCacheItem *j;
1043
1044 LIST_FOREACH(by_key, j, i) {
7778dfff
DM
1045 if (!j->rr)
1046 continue;
1047
d2579eec 1048 if (!j->shared_owner)
7778dfff
DM
1049 continue;
1050
58ab31d5 1051 r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
261f3673
DM
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.
1055 */
1056
1057 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
1058 ancount = 0;
1059
1060 r = dns_packet_new_query(&p->more, p->protocol, 0, true);
1061 if (r < 0)
1062 return r;
1063
1064 /* continue with new packet */
1065 p = p->more;
58ab31d5 1066 r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
261f3673
DM
1067 }
1068
7778dfff
DM
1069 if (r < 0)
1070 return r;
1071
313cefa1 1072 ancount++;
7778dfff
DM
1073 }
1074 }
1075
1076 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
1077
1078 return 0;
1079}
1080
4d506d6b
LP
1081void dns_cache_dump(DnsCache *cache, FILE *f) {
1082 Iterator iterator;
1083 DnsCacheItem *i;
4d506d6b
LP
1084
1085 if (!cache)
1086 return;
1087
1088 if (!f)
1089 f = stdout;
1090
1091 HASHMAP_FOREACH(i, cache->by_key, iterator) {
1092 DnsCacheItem *j;
1093
1094 LIST_FOREACH(by_key, j, i) {
4d506d6b
LP
1095
1096 fputc('\t', f);
1097
1098 if (j->rr) {
7b50eb2e
LP
1099 const char *t;
1100 t = dns_resource_record_to_string(j->rr);
1101 if (!t) {
4d506d6b
LP
1102 log_oom();
1103 continue;
1104 }
1105
1106 fputs(t, f);
1107 fputc('\n', f);
1108 } else {
202b76ae 1109 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
4d506d6b 1110
202b76ae 1111 fputs(dns_resource_key_to_string(j->key, key_str, sizeof key_str), f);
4d506d6b 1112 fputs(" -- ", f);
201d9958 1113 fputs(dns_cache_item_type_to_string(j), f);
4d506d6b
LP
1114 fputc('\n', f);
1115 }
1116 }
1117 }
1118}
1119
1120bool dns_cache_is_empty(DnsCache *cache) {
1121 if (!cache)
1122 return true;
1123
1124 return hashmap_isempty(cache->by_key);
1125}
a150ff5e
LP
1126
1127unsigned dns_cache_size(DnsCache *cache) {
1128 if (!cache)
1129 return 0;
1130
1131 return hashmap_size(cache->by_key);
1132}