]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/resolve/resolved-dns-cache.c
ci: enable arm64 runner for build/unit jobs
[thirdparty/systemd.git] / src / resolve / resolved-dns-cache.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include "sd-json.h"
4
5#include "af-list.h"
6#include "alloc-util.h"
7#include "bitmap.h"
8#include "dns-domain.h"
9#include "format-ifname.h"
10#include "log.h"
11#include "prioq.h"
12#include "resolve-util.h"
13#include "resolved-dns-answer.h"
14#include "resolved-dns-cache.h"
15#include "resolved-dns-dnssec.h"
16#include "resolved-dns-packet.h"
17#include "resolved-dns-rr.h"
18#include "string-util.h"
19#include "time-util.h"
20
21/* Never cache more than 4K entries. RFC 1536, Section 5 suggests to
22 * leave DNS caches unbounded, but that's crazy. */
23#define CACHE_MAX 4096
24
25/* We never keep any item longer than 2h in our cache unless StaleRetentionSec is greater than zero. */
26#define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR)
27
28/* The max TTL for stale data is set to 30 seconds. See RFC 8767, Section 6. */
29#define CACHE_STALE_TTL_MAX_USEC (30 * USEC_PER_SEC)
30
31/* How long to cache strange rcodes, i.e. rcodes != SUCCESS and != NXDOMAIN (specifically: that's only SERVFAIL for
32 * now) */
33#define CACHE_TTL_STRANGE_RCODE_USEC (10 * USEC_PER_SEC)
34
35#define CACHEABLE_QUERY_FLAGS (SD_RESOLVED_AUTHENTICATED|SD_RESOLVED_CONFIDENTIAL)
36
37typedef enum DnsCacheItemType DnsCacheItemType;
38typedef struct DnsCacheItem DnsCacheItem;
39
40enum DnsCacheItemType {
41 DNS_CACHE_POSITIVE,
42 DNS_CACHE_NODATA,
43 DNS_CACHE_NXDOMAIN,
44 DNS_CACHE_RCODE, /* "strange" RCODE (effective only SERVFAIL for now) */
45};
46
47struct DnsCacheItem {
48 DnsCacheItemType type;
49 int rcode;
50 DnsResourceKey *key; /* The key for this item, i.e. the lookup key */
51 DnsResourceRecord *rr; /* The RR for this item, i.e. the lookup value for positive queries */
52 DnsAnswer *answer; /* The full validated answer, if this is an RRset acquired via a "primary" lookup */
53 DnsPacket *full_packet; /* The full packet this information was acquired with */
54
55 usec_t until; /* If StaleRetentionSec is greater than zero, until is set to a duration of StaleRetentionSec from the time of TTL expiry. If StaleRetentionSec is zero, both until and until_valid will be set to ttl. */
56 usec_t until_valid; /* The key is for storing the time when the TTL set to expire. */
57 uint64_t query_flags; /* SD_RESOLVED_AUTHENTICATED and/or SD_RESOLVED_CONFIDENTIAL */
58 DnssecResult dnssec_result;
59
60 int ifindex;
61 int owner_family;
62 union in_addr_union owner_address;
63
64 unsigned prioq_idx;
65 LIST_FIELDS(DnsCacheItem, by_key);
66
67 bool shared_owner;
68};
69
70/* Returns true if this is a cache item created as result of an explicit lookup, or created as "side-effect"
71 * of another request. "Primary" entries will carry the full answer data (with NSEC, …) that can aso prove
72 * wildcard expansion, non-existence and such, while entries that were created as "side-effect" just contain
73 * immediate RR data for the specified RR key, but nothing else. */
74#define DNS_CACHE_ITEM_IS_PRIMARY(item) (!!(item)->answer)
75
76static const char *dns_cache_item_type_to_string(DnsCacheItem *item) {
77 assert(item);
78
79 switch (item->type) {
80
81 case DNS_CACHE_POSITIVE:
82 return "POSITIVE";
83
84 case DNS_CACHE_NODATA:
85 return "NODATA";
86
87 case DNS_CACHE_NXDOMAIN:
88 return "NXDOMAIN";
89
90 case DNS_CACHE_RCODE:
91 return dns_rcode_to_string(item->rcode);
92 }
93
94 return NULL;
95}
96
97static DnsCacheItem* dns_cache_item_free(DnsCacheItem *i) {
98 if (!i)
99 return NULL;
100
101 dns_resource_record_unref(i->rr);
102 dns_resource_key_unref(i->key);
103 dns_answer_unref(i->answer);
104 dns_packet_unref(i->full_packet);
105 return mfree(i);
106}
107DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem*, dns_cache_item_free);
108
109static void dns_cache_item_unlink_and_free(DnsCache *c, DnsCacheItem *i) {
110 DnsCacheItem *first;
111
112 assert(c);
113
114 if (!i)
115 return;
116
117 first = hashmap_get(c->by_key, i->key);
118 LIST_REMOVE(by_key, first, i);
119
120 if (first)
121 assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
122 else
123 hashmap_remove(c->by_key, i->key);
124
125 prioq_remove(c->by_expiry, i, &i->prioq_idx);
126
127 dns_cache_item_free(i);
128}
129
130static bool dns_cache_remove_by_rr(DnsCache *c, DnsResourceRecord *rr) {
131 DnsCacheItem *first;
132 int r;
133
134 first = hashmap_get(c->by_key, rr->key);
135 LIST_FOREACH(by_key, i, first) {
136 r = dns_resource_record_equal(i->rr, rr);
137 if (r < 0)
138 return r;
139 if (r > 0) {
140 dns_cache_item_unlink_and_free(c, i);
141 return true;
142 }
143 }
144
145 return false;
146}
147
148static bool dns_cache_remove_by_key(DnsCache *c, DnsResourceKey *key) {
149 DnsCacheItem *first;
150
151 assert(c);
152 assert(key);
153
154 first = hashmap_remove(c->by_key, key);
155 if (!first)
156 return false;
157
158 LIST_FOREACH(by_key, i, first) {
159 prioq_remove(c->by_expiry, i, &i->prioq_idx);
160 dns_cache_item_free(i);
161 }
162
163 return true;
164}
165
166void dns_cache_flush(DnsCache *c) {
167 DnsResourceKey *key;
168
169 assert(c);
170
171 while ((key = hashmap_first_key(c->by_key)))
172 dns_cache_remove_by_key(c, key);
173
174 assert(hashmap_isempty(c->by_key));
175 assert(prioq_isempty(c->by_expiry));
176
177 c->by_key = hashmap_free(c->by_key);
178 c->by_expiry = prioq_free(c->by_expiry);
179}
180
181static void dns_cache_make_space(DnsCache *c, unsigned add) {
182 assert(c);
183
184 if (add <= 0)
185 return;
186
187 /* Makes space for n new entries. Note that we actually allow
188 * the cache to grow beyond CACHE_MAX, but only when we shall
189 * add more RRs to the cache than CACHE_MAX at once. In that
190 * case the cache will be emptied completely otherwise. */
191
192 for (;;) {
193 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
194 DnsCacheItem *i;
195
196 if (prioq_isempty(c->by_expiry))
197 break;
198
199 if (prioq_size(c->by_expiry) + add < CACHE_MAX)
200 break;
201
202 i = prioq_peek(c->by_expiry);
203 assert(i);
204
205 /* Take an extra reference to the key so that it
206 * doesn't go away in the middle of the remove call */
207 key = dns_resource_key_ref(i->key);
208 dns_cache_remove_by_key(c, key);
209 }
210}
211
212void dns_cache_prune(DnsCache *c) {
213 usec_t t = 0;
214
215 assert(c);
216
217 /* Remove all entries that are past their TTL */
218
219 for (;;) {
220 DnsCacheItem *i;
221 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
222
223 i = prioq_peek(c->by_expiry);
224 if (!i)
225 break;
226
227 if (t <= 0)
228 t = now(CLOCK_BOOTTIME);
229
230 if (i->until > t)
231 break;
232
233 /* Depending whether this is an mDNS shared entry
234 * either remove only this one RR or the whole RRset */
235 log_debug("Removing %scache entry for %s (expired "USEC_FMT"s ago)",
236 i->shared_owner ? "shared " : "",
237 dns_resource_key_to_string(i->key, key_str, sizeof key_str),
238 (t - i->until) / USEC_PER_SEC);
239
240 if (i->shared_owner)
241 dns_cache_item_unlink_and_free(c, i);
242 else {
243 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
244
245 /* Take an extra reference to the key so that it
246 * doesn't go away in the middle of the remove call */
247 key = dns_resource_key_ref(i->key);
248 dns_cache_remove_by_key(c, key);
249 }
250 }
251}
252
253bool dns_cache_expiry_in_one_second(DnsCache *c, usec_t t) {
254 DnsCacheItem *i;
255
256 assert(c);
257
258 /* Check if any items expire within the next second */
259 i = prioq_peek(c->by_expiry);
260 if (!i)
261 return false;
262
263 if (i->until <= usec_add(t, USEC_PER_SEC))
264 return true;
265
266 return false;
267}
268
269static int dns_cache_item_prioq_compare_func(const void *a, const void *b) {
270 const DnsCacheItem *x = a, *y = b;
271
272 return CMP(x->until, y->until);
273}
274
275static int dns_cache_init(DnsCache *c) {
276 int r;
277
278 assert(c);
279
280 r = prioq_ensure_allocated(&c->by_expiry, dns_cache_item_prioq_compare_func);
281 if (r < 0)
282 return r;
283
284 r = hashmap_ensure_allocated(&c->by_key, &dns_resource_key_hash_ops);
285 if (r < 0)
286 return r;
287
288 return r;
289}
290
291static int dns_cache_link_item(DnsCache *c, DnsCacheItem *i) {
292 DnsCacheItem *first;
293 int r;
294
295 assert(c);
296 assert(i);
297
298 r = prioq_put(c->by_expiry, i, &i->prioq_idx);
299 if (r < 0)
300 return r;
301
302 first = hashmap_get(c->by_key, i->key);
303 if (first) {
304 _unused_ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
305
306 /* Keep a reference to the original key, while we manipulate the list. */
307 k = dns_resource_key_ref(first->key);
308
309 /* Now, try to reduce the number of keys we keep */
310 dns_resource_key_reduce(&first->key, &i->key);
311
312 if (first->rr)
313 dns_resource_key_reduce(&first->rr->key, &i->key);
314 if (i->rr)
315 dns_resource_key_reduce(&i->rr->key, &i->key);
316
317 LIST_PREPEND(by_key, first, i);
318 assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
319 } else {
320 r = hashmap_put(c->by_key, i->key, i);
321 if (r < 0) {
322 prioq_remove(c->by_expiry, i, &i->prioq_idx);
323 return r;
324 }
325 }
326
327 return 0;
328}
329
330static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
331 assert(c);
332 assert(rr);
333
334 LIST_FOREACH(by_key, i, (DnsCacheItem*) hashmap_get(c->by_key, rr->key))
335 if (i->rr && dns_resource_record_equal(i->rr, rr) > 0)
336 return i;
337
338 return NULL;
339}
340
341static usec_t calculate_until_valid(
342 DnsResourceRecord *rr,
343 uint32_t min_ttl,
344 uint32_t nsec_ttl,
345 usec_t timestamp,
346 bool use_soa_minimum) {
347
348 uint32_t ttl;
349 usec_t u;
350
351 assert(rr);
352
353 ttl = MIN(min_ttl, nsec_ttl);
354 if (rr->key->type == DNS_TYPE_SOA && use_soa_minimum) {
355 /* If this is a SOA RR, and it is requested, clamp to the SOA's minimum field. This is used
356 * when we do negative caching, to determine the TTL for the negative caching entry. See RFC
357 * 2308, Section 5. */
358
359 if (ttl > rr->soa.minimum)
360 ttl = rr->soa.minimum;
361 }
362
363 u = ttl * USEC_PER_SEC;
364 if (u > CACHE_TTL_MAX_USEC)
365 u = CACHE_TTL_MAX_USEC;
366
367 if (rr->expiry != USEC_INFINITY) {
368 usec_t left;
369
370 /* Make use of the DNSSEC RRSIG expiry info, if we have it */
371
372 left = LESS_BY(rr->expiry, now(CLOCK_REALTIME));
373 if (u > left)
374 u = left;
375 }
376
377 return timestamp + u;
378}
379
380static usec_t calculate_until(
381 usec_t until_valid,
382 usec_t stale_retention_usec) {
383
384 return stale_retention_usec > 0 ? usec_add(until_valid, stale_retention_usec) : until_valid;
385}
386
387static void dns_cache_item_update_positive(
388 DnsCache *c,
389 DnsCacheItem *i,
390 DnsResourceRecord *rr,
391 DnsAnswer *answer,
392 DnsPacket *full_packet,
393 uint32_t min_ttl,
394 uint64_t query_flags,
395 bool shared_owner,
396 DnssecResult dnssec_result,
397 usec_t timestamp,
398 int ifindex,
399 int owner_family,
400 const union in_addr_union *owner_address,
401 usec_t stale_retention_usec) {
402
403 assert(c);
404 assert(i);
405 assert(rr);
406 assert(owner_address);
407
408 i->type = DNS_CACHE_POSITIVE;
409
410 if (!i->by_key_prev)
411 /* We are the first item in the list, we need to
412 * update the key used in the hashmap */
413
414 assert_se(hashmap_replace(c->by_key, rr->key, i) >= 0);
415
416 DNS_RR_REPLACE(i->rr, dns_resource_record_ref(rr));
417
418 DNS_RESOURCE_KEY_REPLACE(i->key, dns_resource_key_ref(rr->key));
419
420 DNS_ANSWER_REPLACE(i->answer, dns_answer_ref(answer));
421
422 DNS_PACKET_REPLACE(i->full_packet, dns_packet_ref(full_packet));
423
424 i->until_valid = calculate_until_valid(rr, min_ttl, UINT32_MAX, timestamp, false);
425 i->until = calculate_until(i->until_valid, stale_retention_usec);
426 i->query_flags = query_flags & CACHEABLE_QUERY_FLAGS;
427 i->shared_owner = shared_owner;
428 i->dnssec_result = dnssec_result;
429
430 i->ifindex = ifindex;
431
432 i->owner_family = owner_family;
433 i->owner_address = *owner_address;
434
435 prioq_reshuffle(c->by_expiry, i, &i->prioq_idx);
436}
437
438static int dns_cache_put_positive(
439 DnsCache *c,
440 DnsProtocol protocol,
441 DnsResourceRecord *rr,
442 DnsAnswer *answer,
443 DnsPacket *full_packet,
444 uint64_t query_flags,
445 bool shared_owner,
446 DnssecResult dnssec_result,
447 usec_t timestamp,
448 int ifindex,
449 int owner_family,
450 const union in_addr_union *owner_address,
451 usec_t stale_retention_usec) {
452
453 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
454 DnsCacheItem *existing;
455 uint32_t min_ttl;
456 int r;
457
458 assert(c);
459 assert(rr);
460 assert(owner_address);
461
462 /* Never cache pseudo RRs */
463 if (dns_class_is_pseudo(rr->key->class))
464 return 0;
465 if (dns_type_is_pseudo(rr->key->type))
466 return 0;
467
468 /* Determine the minimal TTL of all RRs in the answer plus the one by the main RR we are supposed to
469 * cache. Since we cache whole answers to questions we should never return answers where only some
470 * RRs are still valid, hence find the lowest here */
471 min_ttl = MIN(dns_answer_min_ttl(answer), rr->ttl);
472
473 /* New TTL is 0? Delete this specific entry... */
474 if (min_ttl <= 0) {
475 r = dns_cache_remove_by_rr(c, rr);
476 log_debug("%s: %s",
477 r > 0 ? "Removed zero TTL entry from cache" : "Not caching zero TTL cache entry",
478 dns_resource_key_to_string(rr->key, key_str, sizeof key_str));
479 return 0;
480 }
481
482 /* Entry exists already? Update TTL, timestamp and owner */
483 existing = dns_cache_get(c, rr);
484 if (existing) {
485 dns_cache_item_update_positive(
486 c,
487 existing,
488 rr,
489 answer,
490 full_packet,
491 min_ttl,
492 query_flags,
493 shared_owner,
494 dnssec_result,
495 timestamp,
496 ifindex,
497 owner_family,
498 owner_address,
499 stale_retention_usec);
500 return 0;
501 }
502
503 /* Do not cache mDNS goodbye packet. */
504 if (protocol == DNS_PROTOCOL_MDNS && rr->ttl <= 1)
505 return 0;
506
507 /* Otherwise, add the new RR */
508 r = dns_cache_init(c);
509 if (r < 0)
510 return r;
511
512 dns_cache_make_space(c, 1);
513
514 _cleanup_(dns_cache_item_freep) DnsCacheItem *i = new(DnsCacheItem, 1);
515 if (!i)
516 return -ENOMEM;
517
518 /* If StaleRetentionSec is greater than zero, the 'until' property is set to a duration
519 * of StaleRetentionSec from the time of TTL expiry.
520 * If StaleRetentionSec is zero, both the 'until' and 'until_valid' are set to the TTL duration,
521 * leading to the eviction of the record once the TTL expires. */
522 usec_t until_valid = calculate_until_valid(rr, min_ttl, UINT32_MAX, timestamp, false);
523 *i = (DnsCacheItem) {
524 .type = DNS_CACHE_POSITIVE,
525 .key = dns_resource_key_ref(rr->key),
526 .rr = dns_resource_record_ref(rr),
527 .answer = dns_answer_ref(answer),
528 .full_packet = dns_packet_ref(full_packet),
529 .until = calculate_until(until_valid, stale_retention_usec),
530 .until_valid = until_valid,
531 .query_flags = query_flags & CACHEABLE_QUERY_FLAGS,
532 .shared_owner = shared_owner,
533 .dnssec_result = dnssec_result,
534 .ifindex = ifindex,
535 .owner_family = owner_family,
536 .owner_address = *owner_address,
537 .prioq_idx = PRIOQ_IDX_NULL,
538 };
539
540 r = dns_cache_link_item(c, i);
541 if (r < 0)
542 return r;
543
544 log_debug("Added positive %s %s%s cache entry for %s "USEC_FMT"s on %s/%s/%s",
545 FLAGS_SET(i->query_flags, SD_RESOLVED_AUTHENTICATED) ? "authenticated" : "unauthenticated",
546 FLAGS_SET(i->query_flags, SD_RESOLVED_CONFIDENTIAL) ? "confidential" : "non-confidential",
547 i->shared_owner ? " shared" : "",
548 dns_resource_key_to_string(i->key, key_str, sizeof key_str),
549 (i->until - timestamp) / USEC_PER_SEC,
550 i->ifindex == 0 ? "*" : FORMAT_IFNAME(i->ifindex),
551 af_to_name_short(i->owner_family),
552 IN_ADDR_TO_STRING(i->owner_family, &i->owner_address));
553
554 TAKE_PTR(i);
555 return 0;
556}
557/* https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml */
558/* https://www.iana.org/assignments/locally-served-dns-zones/locally-served-dns-zones.xhtml#transport-independent */
559static bool dns_special_use_domain_invalid_answer(DnsResourceKey *key, int rcode) {
560 /* Sometimes we know a domain exists, even if broken nameservers say otherwise. Make sure not to
561 * cache any answers we know are wrong. */
562
563 /* RFC9462 § 6.4: resolvers SHOULD respond to queries of any type other than SVCB for
564 * _dns.resolver.arpa. with NODATA and queries of any type for any domain name under resolver.arpa
565 * with NODATA. */
566 if (dns_name_endswith(dns_resource_key_name(key), "resolver.arpa") > 0 && rcode == DNS_RCODE_NXDOMAIN)
567 return true;
568
569 return false;
570}
571
572static int dns_cache_put_negative(
573 DnsCache *c,
574 DnsResourceKey *key,
575 int rcode,
576 DnsAnswer *answer,
577 DnsPacket *full_packet,
578 uint64_t query_flags,
579 DnssecResult dnssec_result,
580 uint32_t nsec_ttl,
581 usec_t timestamp,
582 DnsResourceRecord *soa,
583 int owner_family,
584 const union in_addr_union *owner_address) {
585
586 _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
587 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
588 int r;
589
590 assert(c);
591 assert(key);
592 assert(owner_address);
593
594 /* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
595 * important to filter out as we use this as a pseudo-type for
596 * NXDOMAIN entries */
597 if (dns_class_is_pseudo(key->class))
598 return 0;
599 if (dns_type_is_pseudo(key->type))
600 return 0;
601 if (dns_special_use_domain_invalid_answer(key, rcode))
602 return 0;
603
604 if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
605 if (!soa)
606 return 0;
607
608 /* For negative replies, check if we have a TTL of a SOA */
609 if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) {
610 log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s",
611 dns_resource_key_to_string(key, key_str, sizeof key_str));
612 return 0;
613 }
614 } else if (rcode != DNS_RCODE_SERVFAIL)
615 return 0;
616
617 r = dns_cache_init(c);
618 if (r < 0)
619 return r;
620
621 dns_cache_make_space(c, 1);
622
623 i = new(DnsCacheItem, 1);
624 if (!i)
625 return -ENOMEM;
626
627 *i = (DnsCacheItem) {
628 .type =
629 rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA :
630 rcode == DNS_RCODE_NXDOMAIN ? DNS_CACHE_NXDOMAIN : DNS_CACHE_RCODE,
631 .query_flags = query_flags & CACHEABLE_QUERY_FLAGS,
632 .dnssec_result = dnssec_result,
633 .owner_family = owner_family,
634 .owner_address = *owner_address,
635 .prioq_idx = PRIOQ_IDX_NULL,
636 .rcode = rcode,
637 .answer = dns_answer_ref(answer),
638 .full_packet = dns_packet_ref(full_packet),
639 };
640
641 /* Determine how long to cache this entry. In case we have some RRs in the answer use the lowest TTL
642 * of any of them. Typically that's the SOA's TTL, which is OK, but could possibly be lower because
643 * of some other RR. Let's better take the lowest option here than a needlessly high one */
644 i->until = i->until_valid =
645 i->type == DNS_CACHE_RCODE ? timestamp + CACHE_TTL_STRANGE_RCODE_USEC :
646 calculate_until_valid(soa, dns_answer_min_ttl(answer), nsec_ttl, timestamp, true);
647
648 if (i->type == DNS_CACHE_NXDOMAIN) {
649 /* NXDOMAIN entries should apply equally to all types, so we use ANY as
650 * a pseudo type for this purpose here. */
651 i->key = dns_resource_key_new(key->class, DNS_TYPE_ANY, dns_resource_key_name(key));
652 if (!i->key)
653 return -ENOMEM;
654
655 /* Make sure to remove any previous entry for this
656 * specific ANY key. (For non-ANY keys the cache data
657 * is already cleared by the caller.) Note that we
658 * don't bother removing positive or NODATA cache
659 * items in this case, because it would either be slow
660 * or require explicit indexing by name */
661 dns_cache_remove_by_key(c, key);
662 } else
663 i->key = dns_resource_key_ref(key);
664
665 r = dns_cache_link_item(c, i);
666 if (r < 0)
667 return r;
668
669 log_debug("Added %s cache entry for %s "USEC_FMT"s",
670 dns_cache_item_type_to_string(i),
671 dns_resource_key_to_string(i->key, key_str, sizeof key_str),
672 (i->until - timestamp) / USEC_PER_SEC);
673
674 i = NULL;
675 return 0;
676}
677
678static void dns_cache_remove_previous(
679 DnsCache *c,
680 DnsResourceKey *key,
681 DnsAnswer *answer) {
682
683 DnsResourceRecord *rr;
684 DnsAnswerFlags flags;
685
686 assert(c);
687
688 /* First, if we were passed a key (i.e. on LLMNR/DNS, but
689 * not on mDNS), delete all matching old RRs, so that we only
690 * keep complete by_key in place. */
691 if (key)
692 dns_cache_remove_by_key(c, key);
693
694 /* Second, flush all entries matching the answer, unless this
695 * is an RR that is explicitly marked to be "shared" between
696 * peers (i.e. mDNS RRs without the flush-cache bit set). */
697 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
698 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
699 continue;
700
701 if (flags & DNS_ANSWER_SHARED_OWNER)
702 continue;
703
704 dns_cache_remove_by_key(c, rr->key);
705 }
706}
707
708static bool rr_eligible(DnsResourceRecord *rr) {
709 assert(rr);
710
711 /* When we see an NSEC/NSEC3 RR, we'll only cache it if it is from the lower zone, not the upper zone, since
712 * that's where the interesting bits are (with exception of DS RRs). Of course, this way we cannot derive DS
713 * existence from any cached NSEC/NSEC3, but that should be fine. */
714
715 switch (rr->key->type) {
716
717 case DNS_TYPE_NSEC:
718 return !bitmap_isset(rr->nsec.types, DNS_TYPE_NS) ||
719 bitmap_isset(rr->nsec.types, DNS_TYPE_SOA);
720
721 case DNS_TYPE_NSEC3:
722 return !bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) ||
723 bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA);
724
725 default:
726 return true;
727 }
728}
729
730int dns_cache_put(
731 DnsCache *c,
732 DnsCacheMode cache_mode,
733 DnsProtocol protocol,
734 DnsResourceKey *key,
735 int rcode,
736 DnsAnswer *answer,
737 DnsPacket *full_packet,
738 uint64_t query_flags,
739 DnssecResult dnssec_result,
740 uint32_t nsec_ttl,
741 int owner_family,
742 const union in_addr_union *owner_address,
743 usec_t stale_retention_usec) {
744
745 DnsResourceRecord *soa = NULL;
746 bool weird_rcode = false;
747 DnsAnswerItem *item;
748 DnsAnswerFlags flags;
749 unsigned cache_keys;
750 usec_t timestamp;
751 int r;
752
753 assert(c);
754 assert(owner_address);
755
756 dns_cache_remove_previous(c, key, answer);
757
758 /* We only care for positive replies and NXDOMAINs, on all other replies we will simply flush the respective
759 * entries, and that's it. (Well, with one further exception: since some DNS zones (akamai!) return SERVFAIL
760 * consistently for some lookups, and forwarders tend to propagate that we'll cache that too, but only for a
761 * short time.) */
762
763 if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
764 if (dns_answer_isempty(answer)) {
765 if (key) {
766 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
767
768 log_debug("Not caching negative entry without a SOA record: %s",
769 dns_resource_key_to_string(key, key_str, sizeof key_str));
770 }
771
772 return 0;
773 }
774
775 } else {
776 /* Only cache SERVFAIL as "weird" rcode for now. We can add more later, should that turn out to be
777 * beneficial. */
778 if (rcode != DNS_RCODE_SERVFAIL)
779 return 0;
780
781 weird_rcode = true;
782 }
783
784 cache_keys = dns_answer_size(answer);
785 if (key)
786 cache_keys++;
787
788 /* Make some space for our new entries */
789 dns_cache_make_space(c, cache_keys);
790
791 timestamp = now(CLOCK_BOOTTIME);
792
793 /* Second, add in positive entries for all contained RRs */
794 DNS_ANSWER_FOREACH_ITEM(item, answer) {
795 int primary = false;
796
797 if (!FLAGS_SET(item->flags, DNS_ANSWER_CACHEABLE) ||
798 !rr_eligible(item->rr))
799 continue;
800
801 if (key) {
802 /* We store the auxiliary RRs and packet data in the cache only if they were in
803 * direct response to the original query. If we cache an RR we also received, and
804 * that is just auxiliary information we can't use the data, hence don't. */
805
806 primary = dns_resource_key_match_rr(key, item->rr, NULL);
807 if (primary < 0)
808 return primary;
809 if (primary == 0) {
810 primary = dns_resource_key_match_cname_or_dname(key, item->rr->key, NULL);
811 if (primary < 0)
812 return primary;
813 }
814 }
815
816 if (!primary) {
817 DnsCacheItem *first;
818
819 /* Do not replace existing cache items for primary lookups with non-primary
820 * data. After all the primary lookup data is a lot more useful. */
821 first = hashmap_get(c->by_key, item->rr->key);
822 if (first && DNS_CACHE_ITEM_IS_PRIMARY(first))
823 return 0;
824 }
825
826 r = dns_cache_put_positive(
827 c,
828 protocol,
829 item->rr,
830 primary ? answer : NULL,
831 primary ? full_packet : NULL,
832 ((item->flags & DNS_ANSWER_AUTHENTICATED) ? SD_RESOLVED_AUTHENTICATED : 0) |
833 (query_flags & SD_RESOLVED_CONFIDENTIAL),
834 item->flags & DNS_ANSWER_SHARED_OWNER,
835 dnssec_result,
836 timestamp,
837 item->ifindex,
838 owner_family,
839 owner_address,
840 stale_retention_usec);
841 if (r < 0)
842 goto fail;
843 }
844
845 if (!key) /* mDNS doesn't know negative caching, really */
846 return 0;
847
848 /* Third, add in negative entries if the key has no RR */
849 r = dns_answer_match_key(answer, key, NULL);
850 if (r < 0)
851 goto fail;
852 if (r > 0)
853 return 0;
854
855 /* But not if it has a matching CNAME/DNAME (the negative caching will be done on the canonical name,
856 * not on the alias) */
857 r = dns_answer_find_cname_or_dname(answer, key, NULL, NULL);
858 if (r < 0)
859 goto fail;
860 if (r > 0)
861 return 0;
862
863 /* See https://tools.ietf.org/html/rfc2308, which say that a matching SOA record in the packet is used to
864 * enable negative caching. We apply one exception though: if we are about to cache a weird rcode we do so
865 * regardless of a SOA. */
866 r = dns_answer_find_soa(answer, key, &soa, &flags);
867 if (r < 0)
868 goto fail;
869 if (r == 0 && !weird_rcode)
870 return 0;
871 if (r > 0) {
872 /* Refuse using the SOA data if it is unsigned, but the key is signed */
873 if (FLAGS_SET(query_flags, SD_RESOLVED_AUTHENTICATED) &&
874 (flags & DNS_ANSWER_AUTHENTICATED) == 0)
875 return 0;
876 }
877
878 if (cache_mode == DNS_CACHE_MODE_NO_NEGATIVE) {
879 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
880 log_debug("Not caching negative entry for: %s, cache mode set to no-negative",
881 dns_resource_key_to_string(key, key_str, sizeof key_str));
882 return 0;
883 }
884
885 r = dns_cache_put_negative(
886 c,
887 key,
888 rcode,
889 answer,
890 full_packet,
891 query_flags,
892 dnssec_result,
893 nsec_ttl,
894 timestamp,
895 soa,
896 owner_family,
897 owner_address);
898 if (r < 0)
899 goto fail;
900
901 return 0;
902
903fail:
904 /* Adding all RRs failed. Let's clean up what we already
905 * added, just in case */
906
907 if (key)
908 dns_cache_remove_by_key(c, key);
909
910 DNS_ANSWER_FOREACH_ITEM(item, answer) {
911 if ((item->flags & DNS_ANSWER_CACHEABLE) == 0)
912 continue;
913
914 dns_cache_remove_by_key(c, item->rr->key);
915 }
916
917 return r;
918}
919
920static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(
921 DnsCache *c,
922 DnsResourceKey *k,
923 uint64_t query_flags) {
924
925 DnsCacheItem *i;
926 const char *n;
927 int r;
928
929 assert(c);
930 assert(k);
931
932 /* If we hit some OOM error, or suchlike, we don't care too
933 * much, after all this is just a cache */
934
935 i = hashmap_get(c->by_key, k);
936 if (i)
937 return i;
938
939 n = dns_resource_key_name(k);
940
941 /* Check if we have an NXDOMAIN cache item for the name, notice that we use
942 * the pseudo-type ANY for NXDOMAIN cache items. */
943 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_ANY, n));
944 if (i && i->type == DNS_CACHE_NXDOMAIN)
945 return i;
946
947 if (dns_type_may_redirect(k->type) && !FLAGS_SET(query_flags, SD_RESOLVED_NO_CNAME)) {
948 /* Check if we have a CNAME record instead */
949 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_CNAME, n));
950 if (i && i->type != DNS_CACHE_NODATA)
951 return i;
952
953 /* OK, let's look for cached DNAME records. */
954 for (;;) {
955 if (isempty(n))
956 return NULL;
957
958 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_DNAME, n));
959 if (i && i->type != DNS_CACHE_NODATA)
960 return i;
961
962 /* Jump one label ahead */
963 r = dns_name_parent(&n);
964 if (r <= 0)
965 return NULL;
966 }
967 }
968
969 if (k->type != DNS_TYPE_NSEC) {
970 /* Check if we have an NSEC record instead for the name. */
971 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_NSEC, n));
972 if (i)
973 return i;
974 }
975
976 return NULL;
977}
978
979static int answer_add_clamp_ttl(
980 DnsAnswer **answer,
981 DnsResourceRecord *rr,
982 int ifindex,
983 DnsAnswerFlags answer_flags,
984 DnsResourceRecord *rrsig,
985 uint64_t query_flags,
986 usec_t until,
987 usec_t current) {
988
989 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *patched = NULL, *patched_rrsig = NULL;
990 int r;
991
992 assert(answer);
993 assert(rr);
994
995 if (FLAGS_SET(query_flags, SD_RESOLVED_CLAMP_TTL)) {
996 uint32_t left_ttl;
997
998 assert(current > 0);
999
1000 /* Let's determine how much time is left for this cache entry. Note that we round down, but
1001 * clamp this to be 1s at minimum, since we usually want records to remain cached better too
1002 * short a time than too long a time, but otoh don't want to return 0 ever, since that has
1003 * special semantics in various contexts — in particular in mDNS */
1004
1005 left_ttl = MAX(1U, LESS_BY(until, current) / USEC_PER_SEC);
1006
1007 patched = dns_resource_record_ref(rr);
1008
1009 r = dns_resource_record_clamp_ttl(&patched, left_ttl);
1010 if (r < 0)
1011 return r;
1012
1013 rr = patched;
1014
1015 if (rrsig) {
1016 patched_rrsig = dns_resource_record_ref(rrsig);
1017 r = dns_resource_record_clamp_ttl(&patched_rrsig, left_ttl);
1018 if (r < 0)
1019 return r;
1020
1021 rrsig = patched_rrsig;
1022 }
1023 }
1024
1025 r = dns_answer_add_extend(answer, rr, ifindex, answer_flags, rrsig);
1026 if (r < 0)
1027 return r;
1028
1029 return 0;
1030}
1031
1032int dns_cache_lookup(
1033 DnsCache *c,
1034 DnsResourceKey *key,
1035 uint64_t query_flags,
1036 int *ret_rcode,
1037 DnsAnswer **ret_answer,
1038 DnsPacket **ret_full_packet,
1039 uint64_t *ret_query_flags,
1040 DnssecResult *ret_dnssec_result) {
1041
1042 _cleanup_(dns_packet_unrefp) DnsPacket *full_packet = NULL;
1043 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
1044 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
1045 unsigned n = 0;
1046 int r;
1047 bool nxdomain = false;
1048 DnsCacheItem *first, *nsec = NULL;
1049 bool have_authenticated = false, have_non_authenticated = false, have_confidential = false, have_non_confidential = false;
1050 usec_t current = 0;
1051 int found_rcode = -1;
1052 DnssecResult dnssec_result = -1;
1053 int have_dnssec_result = -1;
1054
1055 assert(c);
1056 assert(key);
1057
1058 if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
1059 /* If we have ANY lookups we don't use the cache, so that the caller refreshes via the
1060 * network. */
1061
1062 log_debug("Ignoring cache for ANY lookup: %s",
1063 dns_resource_key_to_string(key, key_str, sizeof key_str));
1064 goto miss;
1065 }
1066
1067 first = dns_cache_get_by_key_follow_cname_dname_nsec(c, key, query_flags);
1068 if (!first) {
1069 /* If one question cannot be answered we need to refresh */
1070
1071 log_debug("Cache miss for %s",
1072 dns_resource_key_to_string(key, key_str, sizeof key_str));
1073 goto miss;
1074 }
1075
1076 if ((query_flags & (SD_RESOLVED_CLAMP_TTL | SD_RESOLVED_NO_STALE)) != 0) {
1077 /* 'current' is always passed to answer_add_clamp_ttl(), but is only used conditionally.
1078 * We'll do the same assert there to make sure that it was initialized properly.
1079 * 'current' is also used below when SD_RESOLVED_NO_STALE is set. */
1080 current = now(CLOCK_BOOTTIME);
1081 assert(current > 0);
1082 }
1083
1084 LIST_FOREACH(by_key, j, first) {
1085 /* If the caller doesn't allow us to answer questions from cache data learned from
1086 * "side-effect", skip this entry. */
1087 if (FLAGS_SET(query_flags, SD_RESOLVED_REQUIRE_PRIMARY) &&
1088 !DNS_CACHE_ITEM_IS_PRIMARY(j)) {
1089 log_debug("Primary answer was requested for cache lookup for %s, which we don't have.",
1090 dns_resource_key_to_string(key, key_str, sizeof key_str));
1091
1092 goto miss;
1093 }
1094
1095 /* Skip the next part if ttl is expired and requested with no stale flag. */
1096 if (FLAGS_SET(query_flags, SD_RESOLVED_NO_STALE) && j->until_valid < current) {
1097 log_debug("Requested with no stale and TTL expired for %s",
1098 dns_resource_key_to_string(key, key_str, sizeof key_str));
1099
1100 goto miss;
1101 }
1102
1103 if (j->type == DNS_CACHE_NXDOMAIN)
1104 nxdomain = true;
1105 else if (j->type == DNS_CACHE_RCODE)
1106 found_rcode = j->rcode;
1107 else if (j->rr) {
1108 if (j->rr->key->type == DNS_TYPE_NSEC)
1109 nsec = j;
1110
1111 n++;
1112 }
1113
1114 if (FLAGS_SET(j->query_flags, SD_RESOLVED_AUTHENTICATED))
1115 have_authenticated = true;
1116 else
1117 have_non_authenticated = true;
1118
1119 if (FLAGS_SET(j->query_flags, SD_RESOLVED_CONFIDENTIAL))
1120 have_confidential = true;
1121 else
1122 have_non_confidential = true;
1123
1124 if (j->dnssec_result < 0) {
1125 have_dnssec_result = false; /* an entry without dnssec result? then invalidate things for good */
1126 dnssec_result = _DNSSEC_RESULT_INVALID;
1127 } else if (have_dnssec_result < 0) {
1128 have_dnssec_result = true; /* So far no result seen, let's pick this one up */
1129 dnssec_result = j->dnssec_result;
1130 } else if (have_dnssec_result > 0 && j->dnssec_result != dnssec_result) {
1131 have_dnssec_result = false; /* conflicting result seen? then invalidate for good */
1132 dnssec_result = _DNSSEC_RESULT_INVALID;
1133 }
1134
1135 /* If the question is being resolved using stale data, the clamp TTL will be set to CACHE_STALE_TTL_MAX_USEC. */
1136 usec_t until = FLAGS_SET(query_flags, SD_RESOLVED_NO_STALE) ? j->until_valid
1137 : usec_add(current, CACHE_STALE_TTL_MAX_USEC);
1138
1139 /* Append the answer RRs to our answer. Ideally we have the answer object, which we
1140 * preferably use. But if the cached entry was generated as "side-effect" of a reply,
1141 * i.e. from validated auxiliary records rather than from the main reply, then we use the
1142 * individual RRs only instead. */
1143 if (j->answer) {
1144
1145 /* Minor optimization, if the full answer object of this and the previous RR is the
1146 * same, don't bother adding it again. Typically we store a full RRset here, hence
1147 * that should be the case. */
1148 if (!j->by_key_prev || j->answer != j->by_key_prev->answer) {
1149 DnsAnswerItem *item;
1150
1151 DNS_ANSWER_FOREACH_ITEM(item, j->answer) {
1152 r = answer_add_clamp_ttl(
1153 &answer,
1154 item->rr,
1155 item->ifindex,
1156 item->flags,
1157 item->rrsig,
1158 query_flags,
1159 until,
1160 current);
1161 if (r < 0)
1162 return r;
1163 }
1164 }
1165
1166 } else if (j->rr) {
1167 r = answer_add_clamp_ttl(
1168 &answer,
1169 j->rr,
1170 j->ifindex,
1171 FLAGS_SET(j->query_flags, SD_RESOLVED_AUTHENTICATED) ? DNS_ANSWER_AUTHENTICATED : 0,
1172 NULL,
1173 query_flags,
1174 until,
1175 current);
1176 if (r < 0)
1177 return r;
1178 }
1179
1180 /* We'll return any packet we have for this. Typically all cache entries for the same key
1181 * should come from the same packet anyway, hence it doesn't really matter which packet we
1182 * return here, they should all resolve to the same anyway. */
1183 if (!full_packet && j->full_packet)
1184 full_packet = dns_packet_ref(j->full_packet);
1185 }
1186
1187 if (found_rcode >= 0) {
1188 log_debug("RCODE %s cache hit for %s",
1189 FORMAT_DNS_RCODE(found_rcode),
1190 dns_resource_key_to_string(key, key_str, sizeof(key_str)));
1191
1192 if (ret_rcode)
1193 *ret_rcode = found_rcode;
1194 if (ret_answer)
1195 *ret_answer = TAKE_PTR(answer);
1196 if (ret_full_packet)
1197 *ret_full_packet = TAKE_PTR(full_packet);
1198 if (ret_query_flags)
1199 *ret_query_flags = 0;
1200 if (ret_dnssec_result)
1201 *ret_dnssec_result = dnssec_result;
1202
1203 c->n_hit++;
1204 return 1;
1205 }
1206
1207 if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) {
1208 /* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC
1209 * RRs from the lower-zone of a zone cut, but the DS RRs are on the upper zone. */
1210
1211 log_debug("NSEC NODATA cache hit for %s",
1212 dns_resource_key_to_string(key, key_str, sizeof key_str));
1213
1214 /* We only found an NSEC record that matches our name. If it says the type doesn't exist
1215 * report NODATA. Otherwise report a cache miss. */
1216
1217 if (ret_rcode)
1218 *ret_rcode = DNS_RCODE_SUCCESS;
1219 if (ret_answer)
1220 *ret_answer = TAKE_PTR(answer);
1221 if (ret_full_packet)
1222 *ret_full_packet = TAKE_PTR(full_packet);
1223 if (ret_query_flags)
1224 *ret_query_flags = nsec->query_flags;
1225 if (ret_dnssec_result)
1226 *ret_dnssec_result = nsec->dnssec_result;
1227
1228 if (!bitmap_isset(nsec->rr->nsec.types, key->type) &&
1229 !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
1230 !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME)) {
1231 c->n_hit++;
1232 return 1;
1233 }
1234
1235 c->n_miss++;
1236 return 0;
1237 }
1238
1239 log_debug("%s cache hit for %s",
1240 n > 0 ? "Positive" :
1241 nxdomain ? "NXDOMAIN" : "NODATA",
1242 dns_resource_key_to_string(key, key_str, sizeof key_str));
1243
1244 if (n <= 0) {
1245 c->n_hit++;
1246
1247 if (ret_rcode)
1248 *ret_rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
1249 if (ret_answer)
1250 *ret_answer = TAKE_PTR(answer);
1251 if (ret_full_packet)
1252 *ret_full_packet = TAKE_PTR(full_packet);
1253 if (ret_query_flags)
1254 *ret_query_flags =
1255 ((have_authenticated && !have_non_authenticated) ? SD_RESOLVED_AUTHENTICATED : 0) |
1256 ((have_confidential && !have_non_confidential) ? SD_RESOLVED_CONFIDENTIAL : 0);
1257 if (ret_dnssec_result)
1258 *ret_dnssec_result = dnssec_result;
1259
1260 return 1;
1261 }
1262
1263 c->n_hit++;
1264
1265 if (ret_rcode)
1266 *ret_rcode = DNS_RCODE_SUCCESS;
1267 if (ret_answer)
1268 *ret_answer = TAKE_PTR(answer);
1269 if (ret_full_packet)
1270 *ret_full_packet = TAKE_PTR(full_packet);
1271 if (ret_query_flags)
1272 *ret_query_flags =
1273 ((have_authenticated && !have_non_authenticated) ? SD_RESOLVED_AUTHENTICATED : 0) |
1274 ((have_confidential && !have_non_confidential) ? SD_RESOLVED_CONFIDENTIAL : 0);
1275 if (ret_dnssec_result)
1276 *ret_dnssec_result = dnssec_result;
1277
1278 return n;
1279
1280miss:
1281 if (ret_rcode)
1282 *ret_rcode = DNS_RCODE_SUCCESS;
1283 if (ret_answer)
1284 *ret_answer = NULL;
1285 if (ret_full_packet)
1286 *ret_full_packet = NULL;
1287 if (ret_query_flags)
1288 *ret_query_flags = 0;
1289 if (ret_dnssec_result)
1290 *ret_dnssec_result = _DNSSEC_RESULT_INVALID;
1291
1292 c->n_miss++;
1293 return 0;
1294}
1295
1296int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) {
1297 DnsCacheItem *first;
1298 bool same_owner = true;
1299
1300 assert(cache);
1301 assert(rr);
1302
1303 dns_cache_prune(cache);
1304
1305 /* See if there's a cache entry for the same key. If there
1306 * isn't there's no conflict */
1307 first = hashmap_get(cache->by_key, rr->key);
1308 if (!first)
1309 return 0;
1310
1311 /* See if the RR key is owned by the same owner, if so, there
1312 * isn't a conflict either */
1313 LIST_FOREACH(by_key, i, first) {
1314 if (i->owner_family != owner_family ||
1315 !in_addr_equal(owner_family, &i->owner_address, owner_address)) {
1316 same_owner = false;
1317 break;
1318 }
1319 }
1320 if (same_owner)
1321 return 0;
1322
1323 /* See if there's the exact same RR in the cache. If yes, then
1324 * there's no conflict. */
1325 if (dns_cache_get(cache, rr))
1326 return 0;
1327
1328 /* There's a conflict */
1329 return 1;
1330}
1331
1332int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p, usec_t ts, unsigned max_rr) {
1333 unsigned ancount = 0;
1334 DnsCacheItem *i;
1335 int r;
1336
1337 assert(cache);
1338 assert(p);
1339 assert(p->protocol == DNS_PROTOCOL_MDNS);
1340
1341 HASHMAP_FOREACH(i, cache->by_key)
1342 LIST_FOREACH(by_key, j, i) {
1343 if (!j->rr)
1344 continue;
1345
1346 if (!j->shared_owner)
1347 continue;
1348
1349 /* Ignore cached goodby packet. See on_mdns_packet() and RFC 6762 section 10.1. */
1350 if (j->rr->ttl <= 1)
1351 continue;
1352
1353 /* RFC6762 7.1: Don't append records with less than half the TTL remaining
1354 * as known answers. */
1355 if (usec_sub_unsigned(j->until, ts) < j->rr->ttl * USEC_PER_SEC / 2)
1356 continue;
1357
1358 if (max_rr > 0 && ancount >= max_rr) {
1359 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
1360 ancount = 0;
1361
1362 r = dns_packet_new_query(&p->more, p->protocol, 0, true);
1363 if (r < 0)
1364 return r;
1365
1366 p = p->more;
1367
1368 max_rr = UINT_MAX;
1369 }
1370
1371 r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
1372 if (r == -EMSGSIZE) {
1373 if (max_rr == 0)
1374 /* If max_rr == 0, do not allocate more packets. */
1375 goto finalize;
1376
1377 /* If we're unable to stuff all known answers into the given packet, allocate
1378 * a new one, push the RR into that one and link it to the current one. */
1379
1380 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
1381 ancount = 0;
1382
1383 r = dns_packet_new_query(&p->more, p->protocol, 0, true);
1384 if (r < 0)
1385 return r;
1386
1387 /* continue with new packet */
1388 p = p->more;
1389 r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
1390 }
1391
1392 if (r < 0)
1393 return r;
1394
1395 ancount++;
1396 }
1397
1398finalize:
1399 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
1400
1401 return 0;
1402}
1403
1404void dns_cache_dump(DnsCache *cache, FILE *f) {
1405 DnsCacheItem *i;
1406
1407 if (!cache)
1408 return;
1409
1410 if (!f)
1411 f = stdout;
1412
1413 HASHMAP_FOREACH(i, cache->by_key)
1414 LIST_FOREACH(by_key, j, i) {
1415
1416 fputc('\t', f);
1417
1418 if (j->rr) {
1419 const char *t;
1420 t = dns_resource_record_to_string(j->rr);
1421 if (!t) {
1422 log_oom();
1423 continue;
1424 }
1425
1426 fputs(t, f);
1427 fputc('\n', f);
1428 } else {
1429 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
1430
1431 fputs(dns_resource_key_to_string(j->key, key_str, sizeof key_str), f);
1432 fputs(" -- ", f);
1433 fputs(dns_cache_item_type_to_string(j), f);
1434 fputc('\n', f);
1435 }
1436 }
1437}
1438
1439int dns_cache_dump_to_json(DnsCache *cache, sd_json_variant **ret) {
1440 _cleanup_(sd_json_variant_unrefp) sd_json_variant *c = NULL;
1441 DnsCacheItem *i;
1442 int r;
1443
1444 assert(cache);
1445 assert(ret);
1446
1447 HASHMAP_FOREACH(i, cache->by_key) {
1448 _cleanup_(sd_json_variant_unrefp) sd_json_variant *d = NULL, *k = NULL;
1449
1450 r = dns_resource_key_to_json(i->key, &k);
1451 if (r < 0)
1452 return r;
1453
1454 if (i->rr) {
1455 _cleanup_(sd_json_variant_unrefp) sd_json_variant *l = NULL;
1456
1457 LIST_FOREACH(by_key, j, i) {
1458 _cleanup_(sd_json_variant_unrefp) sd_json_variant *rj = NULL;
1459
1460 assert(j->rr);
1461
1462 r = dns_resource_record_to_json(j->rr, &rj);
1463 if (r < 0)
1464 return r;
1465
1466 r = dns_resource_record_to_wire_format(j->rr, /* canonical= */ false); /* don't use DNSSEC canonical format, since it removes casing, but we want that for DNS_SD compat */
1467 if (r < 0)
1468 return r;
1469
1470 r = sd_json_variant_append_arraybo(
1471 &l,
1472 SD_JSON_BUILD_PAIR_VARIANT("rr", rj),
1473 SD_JSON_BUILD_PAIR_BASE64("raw", j->rr->wire_format, j->rr->wire_format_size));
1474 if (r < 0)
1475 return r;
1476 }
1477
1478 if (!l) {
1479 r = sd_json_variant_new_array(&l, NULL, 0);
1480 if (r < 0)
1481 return r;
1482 }
1483
1484 r = sd_json_buildo(
1485 &d,
1486 SD_JSON_BUILD_PAIR_VARIANT("key", k),
1487 SD_JSON_BUILD_PAIR_VARIANT("rrs", l),
1488 SD_JSON_BUILD_PAIR_UNSIGNED("until", i->until));
1489 } else if (i->type == DNS_CACHE_NODATA) {
1490 r = sd_json_buildo(
1491 &d,
1492 SD_JSON_BUILD_PAIR_VARIANT("key", k),
1493 SD_JSON_BUILD_PAIR_EMPTY_ARRAY("rrs"),
1494 SD_JSON_BUILD_PAIR_UNSIGNED("until", i->until));
1495 } else
1496 r = sd_json_buildo(
1497 &d,
1498 SD_JSON_BUILD_PAIR_VARIANT("key", k),
1499 SD_JSON_BUILD_PAIR_STRING("type", dns_cache_item_type_to_string(i)),
1500 SD_JSON_BUILD_PAIR_UNSIGNED("until", i->until));
1501 if (r < 0)
1502 return r;
1503
1504 r = sd_json_variant_append_array(&c, d);
1505 if (r < 0)
1506 return r;
1507 }
1508
1509 if (!c)
1510 return sd_json_variant_new_array(ret, NULL, 0);
1511
1512 *ret = TAKE_PTR(c);
1513 return 0;
1514}
1515
1516bool dns_cache_is_empty(DnsCache *cache) {
1517 if (!cache)
1518 return true;
1519
1520 return hashmap_isempty(cache->by_key);
1521}
1522
1523unsigned dns_cache_size(DnsCache *cache) {
1524 if (!cache)
1525 return 0;
1526
1527 return hashmap_size(cache->by_key);
1528}