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