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