]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-cache.c
tree-wide: use UINT64_MAX or friends
[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
LP
322 if (rr->key->type == DNS_TYPE_SOA && use_soa_minimum) {
323 /* If this is a SOA RR, and it is requested, clamp to
324 * the SOA's minimum field. This is used when we do
325 * negative caching, to determine the TTL for the
326 * negative caching entry. See RFC 2308, Section
327 * 5. */
ee3d6aff 328
b211dc7e
LP
329 if (ttl > rr->soa.minimum)
330 ttl = rr->soa.minimum;
331 }
332
333 u = ttl * USEC_PER_SEC;
334 if (u > CACHE_TTL_MAX_USEC)
335 u = CACHE_TTL_MAX_USEC;
ee3d6aff
LP
336
337 if (rr->expiry != USEC_INFINITY) {
338 usec_t left;
339
b211dc7e
LP
340 /* Make use of the DNSSEC RRSIG expiry info, if we
341 * have it */
ee3d6aff
LP
342
343 left = LESS_BY(rr->expiry, now(CLOCK_REALTIME));
b211dc7e
LP
344 if (u > left)
345 u = left;
ee3d6aff
LP
346 }
347
b211dc7e 348 return timestamp + u;
ee3d6aff
LP
349}
350
d2579eec
LP
351static void dns_cache_item_update_positive(
352 DnsCache *c,
353 DnsCacheItem *i,
354 DnsResourceRecord *rr,
775ae354
LP
355 DnsAnswer *answer,
356 DnsPacket *full_packet,
6f055e43 357 uint64_t query_flags,
d2579eec 358 bool shared_owner,
775ae354 359 DnssecResult dnssec_result,
d2579eec 360 usec_t timestamp,
06d12754 361 int ifindex,
d2579eec
LP
362 int owner_family,
363 const union in_addr_union *owner_address) {
364
7e8e0422
LP
365 assert(c);
366 assert(i);
367 assert(rr);
d2579eec 368 assert(owner_address);
7e8e0422
LP
369
370 i->type = DNS_CACHE_POSITIVE;
371
ece174c5 372 if (!i->by_key_prev)
7e8e0422
LP
373 /* We are the first item in the list, we need to
374 * update the key used in the hashmap */
375
376 assert_se(hashmap_replace(c->by_key, rr->key, i) >= 0);
7e8e0422
LP
377
378 dns_resource_record_ref(rr);
379 dns_resource_record_unref(i->rr);
380 i->rr = rr;
381
382 dns_resource_key_unref(i->key);
383 i->key = dns_resource_key_ref(rr->key);
384
775ae354
LP
385 dns_answer_ref(answer);
386 dns_answer_unref(i->answer);
387 i->answer = answer;
388
389 dns_packet_ref(full_packet);
390 dns_packet_unref(i->full_packet);
391 i->full_packet = full_packet;
392
393 i->until = calculate_until(rr, UINT32_MAX, timestamp, false);
6f055e43 394 i->query_flags = query_flags & CACHEABLE_QUERY_FLAGS;
d2579eec 395 i->shared_owner = shared_owner;
775ae354 396 i->dnssec_result = dnssec_result;
d2579eec 397
06d12754
LP
398 i->ifindex = ifindex;
399
d2579eec
LP
400 i->owner_family = owner_family;
401 i->owner_address = *owner_address;
7e8e0422
LP
402
403 prioq_reshuffle(c->by_expiry, i, &i->prioq_idx);
404}
405
a4076574
LP
406static int dns_cache_put_positive(
407 DnsCache *c,
408 DnsResourceRecord *rr,
775ae354
LP
409 DnsAnswer *answer,
410 DnsPacket *full_packet,
6f055e43 411 uint64_t query_flags,
d2579eec 412 bool shared_owner,
775ae354 413 DnssecResult dnssec_result,
a4076574 414 usec_t timestamp,
06d12754 415 int ifindex,
a4076574
LP
416 int owner_family,
417 const union in_addr_union *owner_address) {
418
322345fd 419 _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
7e8e0422 420 DnsCacheItem *existing;
518a66ec 421 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
a257f9d4 422 int r, k;
322345fd
LP
423
424 assert(c);
425 assert(rr);
a4076574 426 assert(owner_address);
322345fd 427
222148b6
LP
428 /* Never cache pseudo RRs */
429 if (dns_class_is_pseudo(rr->key->class))
430 return 0;
431 if (dns_type_is_pseudo(rr->key->type))
432 return 0;
433
f5bdeb01 434 /* New TTL is 0? Delete this specific entry... */
322345fd 435 if (rr->ttl <= 0) {
f5bdeb01 436 k = dns_cache_remove_by_rr(c, rr);
202b76ae
ZJS
437 log_debug("%s: %s",
438 k > 0 ? "Removed zero TTL entry from cache" : "Not caching zero TTL cache entry",
18665d1f 439 dns_resource_key_to_string(rr->key, key_str, sizeof key_str));
322345fd
LP
440 return 0;
441 }
442
13e785f7 443 /* Entry exists already? Update TTL, timestamp and owner */
322345fd
LP
444 existing = dns_cache_get(c, rr);
445 if (existing) {
d2579eec
LP
446 dns_cache_item_update_positive(
447 c,
448 existing,
449 rr,
775ae354
LP
450 answer,
451 full_packet,
6f055e43 452 query_flags,
d2579eec 453 shared_owner,
775ae354 454 dnssec_result,
d2579eec 455 timestamp,
06d12754 456 ifindex,
d2579eec
LP
457 owner_family,
458 owner_address);
322345fd
LP
459 return 0;
460 }
461
462 /* Otherwise, add the new RR */
623a4c97 463 r = dns_cache_init(c);
322345fd
LP
464 if (r < 0)
465 return r;
466
7e8e0422
LP
467 dns_cache_make_space(c, 1);
468
1ed31408 469 i = new(DnsCacheItem, 1);
7e8e0422
LP
470 if (!i)
471 return -ENOMEM;
472
1ed31408
LP
473 *i = (DnsCacheItem) {
474 .type = DNS_CACHE_POSITIVE,
475 .key = dns_resource_key_ref(rr->key),
476 .rr = dns_resource_record_ref(rr),
775ae354
LP
477 .answer = dns_answer_ref(answer),
478 .full_packet = dns_packet_ref(full_packet),
f5fbe71d 479 .until = calculate_until(rr, UINT32_MAX, timestamp, false),
6f055e43 480 .query_flags = query_flags & CACHEABLE_QUERY_FLAGS,
1ed31408 481 .shared_owner = shared_owner,
775ae354 482 .dnssec_result = dnssec_result,
1ed31408
LP
483 .ifindex = ifindex,
484 .owner_family = owner_family,
485 .owner_address = *owner_address,
486 .prioq_idx = PRIOQ_IDX_NULL,
487 };
7e8e0422
LP
488
489 r = dns_cache_link_item(c, i);
490 if (r < 0)
491 return r;
492
f1d34068 493 if (DEBUG_LOGGING) {
202b76ae 494 _cleanup_free_ char *t = NULL;
518a66ec 495 char ifname[IF_NAMESIZE + 1];
202b76ae
ZJS
496
497 (void) in_addr_to_string(i->owner_family, &i->owner_address, &t);
498
43fc4baa 499 log_debug("Added positive %s %s%s cache entry for %s "USEC_FMT"s on %s/%s/%s",
6f055e43 500 FLAGS_SET(i->query_flags, SD_RESOLVED_AUTHENTICATED) ? "authenticated" : "unauthenticated",
43fc4baa 501 FLAGS_SET(i->query_flags, SD_RESOLVED_CONFIDENTIAL) ? "confidential" : "non-confidential",
202b76ae
ZJS
502 i->shared_owner ? " shared" : "",
503 dns_resource_key_to_string(i->key, key_str, sizeof key_str),
504 (i->until - timestamp) / USEC_PER_SEC,
518a66ec 505 i->ifindex == 0 ? "*" : strna(format_ifname(i->ifindex, ifname)),
202b76ae
ZJS
506 af_to_name_short(i->owner_family),
507 strna(t));
a257f9d4 508 }
6b34a6c9 509
7e8e0422
LP
510 i = NULL;
511 return 0;
512}
513
a4076574
LP
514static int dns_cache_put_negative(
515 DnsCache *c,
516 DnsResourceKey *key,
517 int rcode,
775ae354
LP
518 DnsAnswer *answer,
519 DnsPacket *full_packet,
6f055e43 520 uint64_t query_flags,
775ae354 521 DnssecResult dnssec_result,
d3760be0 522 uint32_t nsec_ttl,
a4076574 523 usec_t timestamp,
b211dc7e 524 DnsResourceRecord *soa,
a4076574
LP
525 int owner_family,
526 const union in_addr_union *owner_address) {
527
7e8e0422 528 _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
202b76ae 529 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
7e8e0422
LP
530 int r;
531
532 assert(c);
533 assert(key);
a4076574 534 assert(owner_address);
7e8e0422 535
98b6be77
LP
536 /* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
537 * important to filter out as we use this as a pseudo-type for
538 * NXDOMAIN entries */
222148b6 539 if (dns_class_is_pseudo(key->class))
ddf16339 540 return 0;
c33be4a6 541 if (dns_type_is_pseudo(key->type))
ddf16339 542 return 0;
222148b6 543
201d9958
LP
544 if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
545 if (!soa)
546 return 0;
ddf16339 547
201d9958
LP
548 /* For negative replies, check if we have a TTL of a SOA */
549 if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) {
550 log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s",
551 dns_resource_key_to_string(key, key_str, sizeof key_str));
552 return 0;
553 }
554 } else if (rcode != DNS_RCODE_SERVFAIL)
7e8e0422
LP
555 return 0;
556
623a4c97 557 r = dns_cache_init(c);
322345fd
LP
558 if (r < 0)
559 return r;
560
561 dns_cache_make_space(c, 1);
562
1ed31408 563 i = new(DnsCacheItem, 1);
322345fd
LP
564 if (!i)
565 return -ENOMEM;
566
1ed31408
LP
567 *i = (DnsCacheItem) {
568 .type =
569 rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA :
570 rcode == DNS_RCODE_NXDOMAIN ? DNS_CACHE_NXDOMAIN : DNS_CACHE_RCODE,
6f055e43 571 .query_flags = query_flags & CACHEABLE_QUERY_FLAGS,
775ae354 572 .dnssec_result = dnssec_result,
1ed31408
LP
573 .owner_family = owner_family,
574 .owner_address = *owner_address,
575 .prioq_idx = PRIOQ_IDX_NULL,
576 .rcode = rcode,
775ae354
LP
577 .answer = dns_answer_ref(answer),
578 .full_packet = dns_packet_ref(full_packet),
1ed31408 579 };
322345fd 580
eaa26948
LP
581 i->until =
582 i->type == DNS_CACHE_RCODE ? timestamp + CACHE_TTL_STRANGE_RCODE_USEC :
583 calculate_until(soa, nsec_ttl, timestamp, true);
584
71e13669
TG
585 if (i->type == DNS_CACHE_NXDOMAIN) {
586 /* NXDOMAIN entries should apply equally to all types, so we use ANY as
587 * a pseudo type for this purpose here. */
1c02e7ba 588 i->key = dns_resource_key_new(key->class, DNS_TYPE_ANY, dns_resource_key_name(key));
71e13669
TG
589 if (!i->key)
590 return -ENOMEM;
a5444ca9
LP
591
592 /* Make sure to remove any previous entry for this
593 * specific ANY key. (For non-ANY keys the cache data
594 * is already cleared by the caller.) Note that we
595 * don't bother removing positive or NODATA cache
596 * items in this case, because it would either be slow
597 * or require explicit indexing by name */
598 dns_cache_remove_by_key(c, key);
71e13669
TG
599 } else
600 i->key = dns_resource_key_ref(key);
601
7e8e0422 602 r = dns_cache_link_item(c, i);
322345fd
LP
603 if (r < 0)
604 return r;
605
202b76ae 606 log_debug("Added %s cache entry for %s "USEC_FMT"s",
201d9958 607 dns_cache_item_type_to_string(i),
202b76ae
ZJS
608 dns_resource_key_to_string(i->key, key_str, sizeof key_str),
609 (i->until - timestamp) / USEC_PER_SEC);
6b34a6c9 610
322345fd 611 i = NULL;
322345fd
LP
612 return 0;
613}
614
d2579eec
LP
615static void dns_cache_remove_previous(
616 DnsCache *c,
617 DnsResourceKey *key,
618 DnsAnswer *answer) {
619
620 DnsResourceRecord *rr;
621 DnsAnswerFlags flags;
622
623 assert(c);
624
625 /* First, if we were passed a key (i.e. on LLMNR/DNS, but
626 * not on mDNS), delete all matching old RRs, so that we only
627 * keep complete by_key in place. */
628 if (key)
2dda578f 629 dns_cache_remove_by_key(c, key);
d2579eec
LP
630
631 /* Second, flush all entries matching the answer, unless this
632 * is an RR that is explicitly marked to be "shared" between
633 * peers (i.e. mDNS RRs without the flush-cache bit set). */
634 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
635 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
636 continue;
637
638 if (flags & DNS_ANSWER_SHARED_OWNER)
639 continue;
640
2dda578f 641 dns_cache_remove_by_key(c, rr->key);
d2579eec
LP
642 }
643}
644
f6618dcd
LP
645static bool rr_eligible(DnsResourceRecord *rr) {
646 assert(rr);
647
648 /* When we see an NSEC/NSEC3 RR, we'll only cache it if it is from the lower zone, not the upper zone, since
649 * that's where the interesting bits are (with exception of DS RRs). Of course, this way we cannot derive DS
650 * existence from any cached NSEC/NSEC3, but that should be fine. */
651
652 switch (rr->key->type) {
653
654 case DNS_TYPE_NSEC:
655 return !bitmap_isset(rr->nsec.types, DNS_TYPE_NS) ||
656 bitmap_isset(rr->nsec.types, DNS_TYPE_SOA);
657
658 case DNS_TYPE_NSEC3:
659 return !bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) ||
660 bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA);
661
662 default:
663 return true;
664 }
665}
666
a4076574
LP
667int dns_cache_put(
668 DnsCache *c,
37d7a7d9 669 DnsCacheMode cache_mode,
8e427d9b 670 DnsResourceKey *key,
a4076574
LP
671 int rcode,
672 DnsAnswer *answer,
775ae354 673 DnsPacket *full_packet,
6f055e43 674 uint64_t query_flags,
775ae354 675 DnssecResult dnssec_result,
d3760be0 676 uint32_t nsec_ttl,
a4076574
LP
677 int owner_family,
678 const union in_addr_union *owner_address) {
679
9c5fcb8a 680 DnsResourceRecord *soa = NULL;
201d9958 681 bool weird_rcode = false;
9c5fcb8a 682 DnsAnswerItem *item;
105e1512
LP
683 DnsAnswerFlags flags;
684 unsigned cache_keys;
43475909 685 usec_t timestamp;
9c5fcb8a 686 int r;
322345fd
LP
687
688 assert(c);
d2579eec 689 assert(owner_address);
322345fd 690
d2579eec 691 dns_cache_remove_previous(c, key, answer);
0ec7c46e 692
201d9958
LP
693 /* We only care for positive replies and NXDOMAINs, on all other replies we will simply flush the respective
694 * entries, and that's it. (Well, with one further exception: since some DNS zones (akamai!) return SERVFAIL
695 * consistently for some lookups, and forwarders tend to propagate that we'll cache that too, but only for a
696 * short time.) */
6ff01a0d 697
201d9958 698 if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
201d9958 699 if (dns_answer_size(answer) <= 0) {
e55fc5b0
YW
700 if (key) {
701 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
201d9958 702
e55fc5b0
YW
703 log_debug("Not caching negative entry without a SOA record: %s",
704 dns_resource_key_to_string(key, key_str, sizeof key_str));
705 }
775ae354 706
201d9958
LP
707 return 0;
708 }
709
710 } else {
711 /* Only cache SERVFAIL as "weird" rcode for now. We can add more later, should that turn out to be
712 * beneficial. */
713 if (rcode != DNS_RCODE_SERVFAIL)
714 return 0;
715
716 weird_rcode = true;
c3cb6dc2 717 }
0ec7c46e 718
ea207b63 719 cache_keys = dns_answer_size(answer);
8e427d9b 720 if (key)
313cefa1 721 cache_keys++;
eff91ee0 722
7e8e0422 723 /* Make some space for our new entries */
eff91ee0 724 dns_cache_make_space(c, cache_keys);
322345fd 725
43475909 726 timestamp = now(clock_boottime_or_monotonic());
322345fd 727
7e8e0422 728 /* Second, add in positive entries for all contained RRs */
9c5fcb8a 729 DNS_ANSWER_FOREACH_ITEM(item, answer) {
775ae354
LP
730 int primary = false;
731
732 if (!FLAGS_SET(item->flags, DNS_ANSWER_CACHEABLE) ||
9c5fcb8a 733 !rr_eligible(item->rr))
f6618dcd
LP
734 continue;
735
775ae354
LP
736 if (key) {
737 /* We store the auxiliary RRs and packet data in the cache only if they were in
738 * direct response to the original query. If we cache an RR we also received, and
739 * that is just auxiliary information we can't use the data, hence don't. */
740
741 primary = dns_resource_key_match_rr(key, item->rr, NULL);
742 if (primary < 0)
743 return primary;
744 if (primary == 0) {
745 primary = dns_resource_key_match_cname_or_dname(key, item->rr->key, NULL);
746 if (primary < 0)
747 return primary;
748 }
749 }
750
751 if (!primary) {
752 DnsCacheItem *first;
753
754 /* Do not replace existing cache items for primary lookups with non-primary
755 * data. After all the primary lookup data is a lot more useful. */
756 first = hashmap_get(c->by_key, item->rr->key);
757 if (first && DNS_CACHE_ITEM_IS_PRIMARY(first))
758 return 0;
759 }
760
d2579eec
LP
761 r = dns_cache_put_positive(
762 c,
9c5fcb8a 763 item->rr,
775ae354
LP
764 primary ? answer : NULL,
765 primary ? full_packet : NULL,
43fc4baa
LP
766 ((item->flags & DNS_ANSWER_AUTHENTICATED) ? SD_RESOLVED_AUTHENTICATED : 0) |
767 (query_flags & SD_RESOLVED_CONFIDENTIAL),
9c5fcb8a 768 item->flags & DNS_ANSWER_SHARED_OWNER,
775ae354 769 dnssec_result,
d2579eec 770 timestamp,
9c5fcb8a 771 item->ifindex,
775ae354
LP
772 owner_family,
773 owner_address);
7e8e0422
LP
774 if (r < 0)
775 goto fail;
776 }
777
d2579eec 778 if (!key) /* mDNS doesn't know negative caching, really */
eff91ee0
DM
779 return 0;
780
8e427d9b 781 /* Third, add in negative entries if the key has no RR */
105e1512 782 r = dns_answer_match_key(answer, key, NULL);
8e427d9b
TG
783 if (r < 0)
784 goto fail;
785 if (r > 0)
786 return 0;
7e8e0422 787
5d27351f
TG
788 /* But not if it has a matching CNAME/DNAME (the negative
789 * caching will be done on the canonical name, not on the
790 * alias) */
105e1512 791 r = dns_answer_find_cname_or_dname(answer, key, NULL, NULL);
5d27351f
TG
792 if (r < 0)
793 goto fail;
794 if (r > 0)
795 return 0;
796
201d9958
LP
797 /* See https://tools.ietf.org/html/rfc2308, which say that a matching SOA record in the packet is used to
798 * enable negative caching. We apply one exception though: if we are about to cache a weird rcode we do so
799 * regardless of a SOA. */
fd009cd8 800 r = dns_answer_find_soa(answer, key, &soa, &flags);
8e427d9b
TG
801 if (r < 0)
802 goto fail;
201d9958 803 if (r == 0 && !weird_rcode)
fd009cd8 804 return 0;
201d9958
LP
805 if (r > 0) {
806 /* Refuse using the SOA data if it is unsigned, but the key is
807 * signed */
6f055e43
LP
808 if (FLAGS_SET(query_flags, SD_RESOLVED_AUTHENTICATED) &&
809 (flags & DNS_ANSWER_AUTHENTICATED) == 0)
201d9958
LP
810 return 0;
811 }
fd009cd8 812
37d7a7d9
JN
813 if (cache_mode == DNS_CACHE_MODE_NO_NEGATIVE) {
814 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
815 log_debug("Not caching negative entry for: %s, cache mode set to no-negative",
816 dns_resource_key_to_string(key, key_str, sizeof key_str));
817 return 0;
818 }
819
d2579eec
LP
820 r = dns_cache_put_negative(
821 c,
822 key,
823 rcode,
775ae354
LP
824 answer,
825 full_packet,
6f055e43 826 query_flags,
775ae354 827 dnssec_result,
d3760be0 828 nsec_ttl,
d2579eec 829 timestamp,
b211dc7e 830 soa,
d2579eec 831 owner_family, owner_address);
8e427d9b
TG
832 if (r < 0)
833 goto fail;
322345fd
LP
834
835 return 0;
836
837fail:
838 /* Adding all RRs failed. Let's clean up what we already
839 * added, just in case */
840
8e427d9b 841 if (key)
2dda578f 842 dns_cache_remove_by_key(c, key);
eff91ee0 843
9c5fcb8a
LP
844 DNS_ANSWER_FOREACH_ITEM(item, answer) {
845 if ((item->flags & DNS_ANSWER_CACHEABLE) == 0)
105e1512
LP
846 continue;
847
9c5fcb8a 848 dns_cache_remove_by_key(c, item->rr->key);
105e1512 849 }
322345fd
LP
850
851 return r;
852}
853
37da8931 854static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, DnsResourceKey *k) {
58db254a
LP
855 DnsCacheItem *i;
856 const char *n;
857 int r;
5643c00a
TG
858
859 assert(c);
860 assert(k);
861
58db254a
LP
862 /* If we hit some OOM error, or suchlike, we don't care too
863 * much, after all this is just a cache */
864
5643c00a 865 i = hashmap_get(c->by_key, k);
d7ce6c94 866 if (i)
37da8931
LP
867 return i;
868
1c02e7ba 869 n = dns_resource_key_name(k);
37da8931 870
71e13669
TG
871 /* Check if we have an NXDOMAIN cache item for the name, notice that we use
872 * the pseudo-type ANY for NXDOMAIN cache items. */
873 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_ANY, n));
874 if (i && i->type == DNS_CACHE_NXDOMAIN)
875 return i;
876
d3c7e913 877 if (dns_type_may_redirect(k->type)) {
d7ce6c94
TG
878 /* Check if we have a CNAME record instead */
879 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_CNAME, n));
3740146a 880 if (i && i->type != DNS_CACHE_NODATA)
d7ce6c94 881 return i;
5643c00a 882
d7ce6c94
TG
883 /* OK, let's look for cached DNAME records. */
884 for (;;) {
d7ce6c94
TG
885 if (isempty(n))
886 return NULL;
887
888 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_DNAME, n));
3740146a 889 if (i && i->type != DNS_CACHE_NODATA)
d7ce6c94 890 return i;
58db254a 891
d7ce6c94 892 /* Jump one label ahead */
950b692b 893 r = dns_name_parent(&n);
d7ce6c94
TG
894 if (r <= 0)
895 return NULL;
896 }
897 }
5643c00a 898
950b692b 899 if (k->type != DNS_TYPE_NSEC) {
d7ce6c94
TG
900 /* Check if we have an NSEC record instead for the name. */
901 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_NSEC, n));
58db254a
LP
902 if (i)
903 return i;
58db254a
LP
904 }
905
906 return NULL;
5643c00a
TG
907}
908
775ae354
LP
909static int answer_add_clamp_ttl(
910 DnsAnswer **answer,
911 DnsResourceRecord *rr,
912 int ifindex,
913 DnsAnswerFlags answer_flags,
914 DnsResourceRecord *rrsig,
915 uint64_t query_flags,
916 usec_t until,
917 usec_t current) {
918
919 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *patched = NULL, *patched_rrsig = NULL;
920 int r;
921
922 assert(answer);
923 assert(rr);
924
925 if (FLAGS_SET(query_flags, SD_RESOLVED_CLAMP_TTL)) {
926 patched = dns_resource_record_ref(rr);
927
928 r = dns_resource_record_clamp_ttl(&patched, LESS_BY(until, current) / USEC_PER_SEC);
929 if (r < 0)
930 return r;
931
932 rr = patched;
933
934 if (rrsig) {
935 patched_rrsig = dns_resource_record_ref(rrsig);
936 r = dns_resource_record_clamp_ttl(&patched_rrsig, LESS_BY(until, current) / USEC_PER_SEC);
937 if (r < 0)
938 return r;
939
940 rrsig = patched_rrsig;
941 }
942 }
943
944 r = dns_answer_add_extend(answer, rr, ifindex, answer_flags, rrsig);
945 if (r < 0)
946 return r;
947
948 return 0;
949}
950
951int dns_cache_lookup(
952 DnsCache *c,
953 DnsResourceKey *key,
954 uint64_t query_flags,
955 int *ret_rcode,
956 DnsAnswer **ret_answer,
957 DnsPacket **ret_full_packet,
6f055e43 958 uint64_t *ret_query_flags,
775ae354
LP
959 DnssecResult *ret_dnssec_result) {
960
961 _cleanup_(dns_packet_unrefp) DnsPacket *full_packet = NULL;
faa133f3 962 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
202b76ae 963 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
f52e61da 964 unsigned n = 0;
322345fd 965 int r;
7e8e0422 966 bool nxdomain = false;
931851e8 967 DnsCacheItem *j, *first, *nsec = NULL;
43fc4baa 968 bool have_authenticated = false, have_non_authenticated = false, have_confidential = false, have_non_confidential = false;
17c8de63 969 usec_t current;
201d9958 970 int found_rcode = -1;
775ae354
LP
971 DnssecResult dnssec_result = -1;
972 int have_dnssec_result = -1;
322345fd
LP
973
974 assert(c);
f52e61da 975 assert(key);
322345fd 976
202b76ae 977 if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
775ae354
LP
978 /* If we have ANY lookups we don't use the cache, so that the caller refreshes via the
979 * network. */
322345fd 980
202b76ae
ZJS
981 log_debug("Ignoring cache for ANY lookup: %s",
982 dns_resource_key_to_string(key, key_str, sizeof key_str));
775ae354 983 goto miss;
f52e61da 984 }
6b34a6c9 985
37da8931 986 first = dns_cache_get_by_key_follow_cname_dname_nsec(c, key);
f52e61da
LP
987 if (!first) {
988 /* If one question cannot be answered we need to refresh */
ddf16339 989
202b76ae
ZJS
990 log_debug("Cache miss for %s",
991 dns_resource_key_to_string(key, key_str, sizeof key_str));
775ae354
LP
992 goto miss;
993 }
6b34a6c9 994
775ae354
LP
995 if (FLAGS_SET(query_flags, SD_RESOLVED_CLAMP_TTL))
996 current = now(clock_boottime_or_monotonic());
a150ff5e 997
775ae354
LP
998 LIST_FOREACH(by_key, j, first) {
999 /* If the caller doesn't allow us to answer questions from cache data learned from
1000 * "side-effect", skip this entry. */
1001 if (FLAGS_SET(query_flags, SD_RESOLVED_REQUIRE_PRIMARY) &&
1002 !DNS_CACHE_ITEM_IS_PRIMARY(j)) {
1003 log_debug("Primary answer was requested for cache lookup for %s, which we don't have.",
1004 dns_resource_key_to_string(key, key_str, sizeof key_str));
2d4a4e14 1005
775ae354
LP
1006 goto miss;
1007 }
6b34a6c9 1008
775ae354
LP
1009 if (j->type == DNS_CACHE_NXDOMAIN)
1010 nxdomain = true;
1011 else if (j->type == DNS_CACHE_RCODE)
1012 found_rcode = j->rcode;
1013 else if (j->rr) {
37da8931 1014 if (j->rr->key->type == DNS_TYPE_NSEC)
931851e8
LP
1015 nsec = j;
1016
f52e61da 1017 n++;
775ae354 1018 }
931851e8 1019
6f055e43 1020 if (FLAGS_SET(j->query_flags, SD_RESOLVED_AUTHENTICATED))
931851e8
LP
1021 have_authenticated = true;
1022 else
1023 have_non_authenticated = true;
775ae354 1024
43fc4baa
LP
1025 if (FLAGS_SET(j->query_flags, SD_RESOLVED_CONFIDENTIAL))
1026 have_confidential = true;
1027 else
1028 have_non_confidential = true;
1029
775ae354
LP
1030 if (j->dnssec_result < 0) {
1031 have_dnssec_result = false; /* an entry without dnssec result? then invalidate things for good */
1032 dnssec_result = _DNSSEC_RESULT_INVALID;
1033 } else if (have_dnssec_result < 0) {
1034 have_dnssec_result = true; /* So far no result seen, let's pick this one up */
1035 dnssec_result = j->dnssec_result;
1036 } else if (have_dnssec_result > 0 && j->dnssec_result != dnssec_result) {
1037 have_dnssec_result = false; /* conflicting result seen? then invalidate for good */
1038 dnssec_result = _DNSSEC_RESULT_INVALID;
1039 }
1040
1041 /* Append the answer RRs to our answer. Ideally we have the answer object, which we
1042 * preferably use. But if the cached entry was generated as "side-effect" of a reply,
1043 * i.e. from validated auxiliary records rather than from the main reply, then we use the
1044 * individual RRs only instead. */
1045 if (j->answer) {
1046
1047 /* Minor optimization, if the full answer object of this and the previous RR is the
1048 * same, don't bother adding it again. Typically we store a full RRset here, hence
1049 * that should be the case. */
1050 if (!j->by_key_prev || j->answer != j->by_key_prev->answer) {
1051 DnsAnswerItem *item;
1052
1053 DNS_ANSWER_FOREACH_ITEM(item, j->answer) {
1054 r = answer_add_clamp_ttl(&answer, item->rr, item->ifindex, item->flags, item->rrsig, query_flags, j->until, current);
1055 if (r < 0)
1056 return r;
1057 }
1058 }
1059
1060 } else if (j->rr) {
6f055e43
LP
1061 r = answer_add_clamp_ttl(&answer,
1062 j->rr,
1063 j->ifindex,
1064 FLAGS_SET(j->query_flags, SD_RESOLVED_AUTHENTICATED) ? DNS_ANSWER_AUTHENTICATED : 0,
1065 NULL,
1066 query_flags,
1067 j->until,
1068 current);
775ae354
LP
1069 if (r < 0)
1070 return r;
1071 }
1072
1073 /* We'll return any packet we have for this. Typically all cache entries for the same key
1074 * should come from the same packet anyway, hence it doesn't really matter which packet we
1075 * return here, they should all resolve to the same anyway. */
1076 if (!full_packet && j->full_packet)
1077 full_packet = dns_packet_ref(j->full_packet);
f52e61da 1078 }
6b34a6c9 1079
201d9958
LP
1080 if (found_rcode >= 0) {
1081 log_debug("RCODE %s cache hit for %s",
1082 dns_rcode_to_string(found_rcode),
1083 dns_resource_key_to_string(key, key_str, sizeof(key_str)));
1084
775ae354
LP
1085 if (ret_rcode)
1086 *ret_rcode = found_rcode;
1087 if (ret_answer)
1088 *ret_answer = TAKE_PTR(answer);
1089 if (ret_full_packet)
1090 *ret_full_packet = TAKE_PTR(full_packet);
6f055e43
LP
1091 if (ret_query_flags)
1092 *ret_query_flags = 0;
775ae354
LP
1093 if (ret_dnssec_result)
1094 *ret_dnssec_result = dnssec_result;
201d9958
LP
1095
1096 c->n_hit++;
1097 return 1;
1098 }
1099
f6618dcd 1100 if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) {
775ae354
LP
1101 /* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC
1102 * RRs from the lower-zone of a zone cut, but the DS RRs are on the upper zone. */
f6618dcd 1103
202b76ae
ZJS
1104 log_debug("NSEC NODATA cache hit for %s",
1105 dns_resource_key_to_string(key, key_str, sizeof key_str));
37da8931 1106
775ae354
LP
1107 /* We only found an NSEC record that matches our name. If it says the type doesn't exist
1108 * report NODATA. Otherwise report a cache miss. */
37da8931 1109
775ae354
LP
1110 if (ret_rcode)
1111 *ret_rcode = DNS_RCODE_SUCCESS;
1112 if (ret_answer)
1113 *ret_answer = TAKE_PTR(answer);
1114 if (ret_full_packet)
1115 *ret_full_packet = TAKE_PTR(full_packet);
6f055e43
LP
1116 if (ret_query_flags)
1117 *ret_query_flags = nsec->query_flags;
775ae354
LP
1118 if (ret_dnssec_result)
1119 *ret_dnssec_result = nsec->dnssec_result;
37da8931 1120
a150ff5e
LP
1121 if (!bitmap_isset(nsec->rr->nsec.types, key->type) &&
1122 !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
1123 !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME)) {
1124 c->n_hit++;
1125 return 1;
1126 }
1127
1128 c->n_miss++;
1129 return 0;
37da8931
LP
1130 }
1131
202b76ae
ZJS
1132 log_debug("%s cache hit for %s",
1133 n > 0 ? "Positive" :
1134 nxdomain ? "NXDOMAIN" : "NODATA",
1135 dns_resource_key_to_string(key, key_str, sizeof key_str));
faa133f3 1136
7e8e0422 1137 if (n <= 0) {
a150ff5e
LP
1138 c->n_hit++;
1139
775ae354
LP
1140 if (ret_rcode)
1141 *ret_rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
1142 if (ret_answer)
1143 *ret_answer = TAKE_PTR(answer);
1144 if (ret_full_packet)
1145 *ret_full_packet = TAKE_PTR(full_packet);
6f055e43 1146 if (ret_query_flags)
43fc4baa
LP
1147 *ret_query_flags =
1148 ((have_authenticated && !have_non_authenticated) ? SD_RESOLVED_AUTHENTICATED : 0) |
1149 ((have_confidential && !have_non_confidential) ? SD_RESOLVED_CONFIDENTIAL : 0);
775ae354
LP
1150 if (ret_dnssec_result)
1151 *ret_dnssec_result = dnssec_result;
17c8de63 1152
775ae354 1153 return 1;
322345fd
LP
1154 }
1155
a150ff5e
LP
1156 c->n_hit++;
1157
775ae354
LP
1158 if (ret_rcode)
1159 *ret_rcode = DNS_RCODE_SUCCESS;
1160 if (ret_answer)
1161 *ret_answer = TAKE_PTR(answer);
1162 if (ret_full_packet)
1163 *ret_full_packet = TAKE_PTR(full_packet);
6f055e43 1164 if (ret_query_flags)
43fc4baa
LP
1165 *ret_query_flags =
1166 ((have_authenticated && !have_non_authenticated) ? SD_RESOLVED_AUTHENTICATED : 0) |
1167 ((have_confidential && !have_non_confidential) ? SD_RESOLVED_CONFIDENTIAL : 0);
775ae354
LP
1168 if (ret_dnssec_result)
1169 *ret_dnssec_result = dnssec_result;
faa133f3
LP
1170
1171 return n;
775ae354
LP
1172
1173miss:
1174 if (ret_rcode)
1175 *ret_rcode = DNS_RCODE_SUCCESS;
1176 if (ret_answer)
1177 *ret_answer = NULL;
1178 if (ret_full_packet)
1179 *ret_full_packet = NULL;
6f055e43
LP
1180 if (ret_query_flags)
1181 *ret_query_flags = 0;
775ae354
LP
1182 if (ret_dnssec_result)
1183 *ret_dnssec_result = _DNSSEC_RESULT_INVALID;
1184
1185 c->n_miss++;
1186 return 0;
322345fd 1187}
a4076574
LP
1188
1189int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) {
1190 DnsCacheItem *i, *first;
1191 bool same_owner = true;
1192
1193 assert(cache);
1194 assert(rr);
1195
1196 dns_cache_prune(cache);
1197
1198 /* See if there's a cache entry for the same key. If there
1199 * isn't there's no conflict */
1200 first = hashmap_get(cache->by_key, rr->key);
1201 if (!first)
1202 return 0;
1203
1204 /* See if the RR key is owned by the same owner, if so, there
1205 * isn't a conflict either */
1206 LIST_FOREACH(by_key, i, first) {
1207 if (i->owner_family != owner_family ||
1208 !in_addr_equal(owner_family, &i->owner_address, owner_address)) {
1209 same_owner = false;
1210 break;
1211 }
1212 }
1213 if (same_owner)
1214 return 0;
1215
1216 /* See if there's the exact same RR in the cache. If yes, then
1217 * there's no conflict. */
1218 if (dns_cache_get(cache, rr))
1219 return 0;
1220
1221 /* There's a conflict */
1222 return 1;
1223}
4d506d6b 1224
7778dfff
DM
1225int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
1226 unsigned ancount = 0;
7778dfff
DM
1227 DnsCacheItem *i;
1228 int r;
1229
1230 assert(cache);
261f3673 1231 assert(p);
7778dfff 1232
90e74a66 1233 HASHMAP_FOREACH(i, cache->by_key) {
7778dfff
DM
1234 DnsCacheItem *j;
1235
1236 LIST_FOREACH(by_key, j, i) {
7778dfff
DM
1237 if (!j->rr)
1238 continue;
1239
d2579eec 1240 if (!j->shared_owner)
7778dfff
DM
1241 continue;
1242
58ab31d5 1243 r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
261f3673
DM
1244 if (r == -EMSGSIZE && p->protocol == DNS_PROTOCOL_MDNS) {
1245 /* For mDNS, if we're unable to stuff all known answers into the given packet,
1246 * allocate a new one, push the RR into that one and link it to the current one.
1247 */
1248
1249 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
1250 ancount = 0;
1251
1252 r = dns_packet_new_query(&p->more, p->protocol, 0, true);
1253 if (r < 0)
1254 return r;
1255
1256 /* continue with new packet */
1257 p = p->more;
58ab31d5 1258 r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
261f3673
DM
1259 }
1260
7778dfff
DM
1261 if (r < 0)
1262 return r;
1263
313cefa1 1264 ancount++;
7778dfff
DM
1265 }
1266 }
1267
1268 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
1269
1270 return 0;
1271}
1272
ca9fab88 1273void dns_cache_dump(DnsCache *cache, FILE *f) {
4d506d6b 1274 DnsCacheItem *i;
4d506d6b
LP
1275
1276 if (!cache)
1277 return;
1278
1279 if (!f)
1280 f = stdout;
1281
90e74a66 1282 HASHMAP_FOREACH(i, cache->by_key) {
4d506d6b
LP
1283 DnsCacheItem *j;
1284
1285 LIST_FOREACH(by_key, j, i) {
4d506d6b
LP
1286
1287 fputc('\t', f);
1288
1289 if (j->rr) {
7b50eb2e
LP
1290 const char *t;
1291 t = dns_resource_record_to_string(j->rr);
1292 if (!t) {
4d506d6b
LP
1293 log_oom();
1294 continue;
1295 }
1296
1297 fputs(t, f);
1298 fputc('\n', f);
1299 } else {
202b76ae 1300 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
4d506d6b 1301
202b76ae 1302 fputs(dns_resource_key_to_string(j->key, key_str, sizeof key_str), f);
4d506d6b 1303 fputs(" -- ", f);
201d9958 1304 fputs(dns_cache_item_type_to_string(j), f);
4d506d6b
LP
1305 fputc('\n', f);
1306 }
1307 }
1308 }
1309}
1310
1311bool dns_cache_is_empty(DnsCache *cache) {
1312 if (!cache)
1313 return true;
1314
1315 return hashmap_isempty(cache->by_key);
1316}
a150ff5e
LP
1317
1318unsigned dns_cache_size(DnsCache *cache) {
1319 if (!cache)
1320 return 0;
1321
1322 return hashmap_size(cache->by_key);
1323}