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