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