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