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