]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-cache.c
resolved: fix the rcode to SUCCESS if we find at least one matching RR in a DNS response
[thirdparty/systemd.git] / src / resolve / resolved-dns-cache.c
CommitLineData
322345fd
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
b5efdb8a 22#include "alloc-util.h"
58db254a 23#include "dns-domain.h"
02c2857b 24#include "resolved-dns-answer.h"
322345fd 25#include "resolved-dns-cache.h"
7e8e0422 26#include "resolved-dns-packet.h"
58db254a 27#include "string-util.h"
322345fd 28
6af47493
LP
29/* Never cache more than 4K entries. RFC 1536, Section 5 suggests to
30 * leave DNS caches unbounded, but that's crazy. */
d98e5504 31#define CACHE_MAX 4096
cbd4560e 32
d98e5504
LP
33/* We never keep any item longer than 2h in our cache */
34#define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR)
322345fd 35
623a4c97
LP
36typedef enum DnsCacheItemType DnsCacheItemType;
37typedef struct DnsCacheItem DnsCacheItem;
38
39enum DnsCacheItemType {
40 DNS_CACHE_POSITIVE,
41 DNS_CACHE_NODATA,
42 DNS_CACHE_NXDOMAIN,
43};
44
45struct DnsCacheItem {
d2579eec 46 DnsCacheItemType type;
623a4c97
LP
47 DnsResourceKey *key;
48 DnsResourceRecord *rr;
d2579eec 49
623a4c97 50 usec_t until;
d2579eec
LP
51 bool authenticated:1;
52 bool shared_owner:1;
53
a4076574
LP
54 int owner_family;
55 union in_addr_union owner_address;
d2579eec
LP
56
57 unsigned prioq_idx;
623a4c97
LP
58 LIST_FIELDS(DnsCacheItem, by_key);
59};
60
322345fd
LP
61static void dns_cache_item_free(DnsCacheItem *i) {
62 if (!i)
63 return;
64
65 dns_resource_record_unref(i->rr);
7e8e0422 66 dns_resource_key_unref(i->key);
322345fd
LP
67 free(i);
68}
69
70DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem*, dns_cache_item_free);
71
39963f11 72static void dns_cache_item_unlink_and_free(DnsCache *c, DnsCacheItem *i) {
322345fd
LP
73 DnsCacheItem *first;
74
75 assert(c);
76
77 if (!i)
78 return;
79
7e8e0422
LP
80 first = hashmap_get(c->by_key, i->key);
81 LIST_REMOVE(by_key, first, i);
322345fd
LP
82
83 if (first)
7e8e0422 84 assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
322345fd 85 else
7e8e0422 86 hashmap_remove(c->by_key, i->key);
322345fd 87
7e8e0422 88 prioq_remove(c->by_expiry, i, &i->prioq_idx);
322345fd
LP
89
90 dns_cache_item_free(i);
91}
92
f5bdeb01
LP
93static bool dns_cache_remove_by_rr(DnsCache *c, DnsResourceRecord *rr) {
94 DnsCacheItem *first, *i;
95 int r;
96
97 first = hashmap_get(c->by_key, rr->key);
98 LIST_FOREACH(by_key, i, first) {
99 r = dns_resource_record_equal(i->rr, rr);
100 if (r < 0)
101 return r;
102 if (r > 0) {
39963f11 103 dns_cache_item_unlink_and_free(c, i);
f5bdeb01
LP
104 return true;
105 }
106 }
107
108 return false;
109}
110
2dda578f 111static bool dns_cache_remove_by_key(DnsCache *c, DnsResourceKey *key) {
1f97052f 112 DnsCacheItem *first, *i, *n;
322345fd
LP
113
114 assert(c);
115 assert(key);
116
1f97052f
LP
117 first = hashmap_remove(c->by_key, key);
118 if (!first)
119 return false;
120
121 LIST_FOREACH_SAFE(by_key, i, n, first) {
122 prioq_remove(c->by_expiry, i, &i->prioq_idx);
123 dns_cache_item_free(i);
6b34a6c9
TG
124 }
125
1f97052f 126 return true;
322345fd
LP
127}
128
ef9a3e3c
LP
129void dns_cache_flush(DnsCache *c) {
130 DnsResourceKey *key;
131
132 assert(c);
133
134 while ((key = hashmap_first_key(c->by_key)))
2dda578f 135 dns_cache_remove_by_key(c, key);
ef9a3e3c
LP
136
137 assert(hashmap_size(c->by_key) == 0);
138 assert(prioq_size(c->by_expiry) == 0);
139
140 c->by_key = hashmap_free(c->by_key);
141 c->by_expiry = prioq_free(c->by_expiry);
142}
143
322345fd
LP
144static void dns_cache_make_space(DnsCache *c, unsigned add) {
145 assert(c);
146
147 if (add <= 0)
148 return;
149
150 /* Makes space for n new entries. Note that we actually allow
151 * the cache to grow beyond CACHE_MAX, but only when we shall
152 * add more RRs to the cache than CACHE_MAX at once. In that
153 * case the cache will be emptied completely otherwise. */
154
155 for (;;) {
faa133f3 156 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
322345fd
LP
157 DnsCacheItem *i;
158
7e8e0422 159 if (prioq_size(c->by_expiry) <= 0)
322345fd
LP
160 break;
161
7e8e0422 162 if (prioq_size(c->by_expiry) + add < CACHE_MAX)
322345fd
LP
163 break;
164
7e8e0422 165 i = prioq_peek(c->by_expiry);
cbd4560e
LP
166 assert(i);
167
faa133f3 168 /* Take an extra reference to the key so that it
cbd4560e 169 * doesn't go away in the middle of the remove call */
7e8e0422 170 key = dns_resource_key_ref(i->key);
2dda578f 171 dns_cache_remove_by_key(c, key);
322345fd
LP
172 }
173}
174
175void dns_cache_prune(DnsCache *c) {
176 usec_t t = 0;
177
178 assert(c);
179
180 /* Remove all entries that are past their TTL */
181
182 for (;;) {
183 DnsCacheItem *i;
322345fd 184
7e8e0422 185 i = prioq_peek(c->by_expiry);
322345fd
LP
186 if (!i)
187 break;
188
322345fd 189 if (t <= 0)
240b589b 190 t = now(clock_boottime_or_monotonic());
322345fd 191
7e8e0422 192 if (i->until > t)
322345fd
LP
193 break;
194
d2579eec
LP
195 /* Depending whether this is an mDNS shared entry
196 * either remove only this one RR or the whole
197 * RRset */
198 if (i->shared_owner)
39963f11 199 dns_cache_item_unlink_and_free(c, i);
d2579eec
LP
200 else {
201 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
202
203 /* Take an extra reference to the key so that it
204 * doesn't go away in the middle of the remove call */
205 key = dns_resource_key_ref(i->key);
2dda578f 206 dns_cache_remove_by_key(c, key);
d2579eec 207 }
322345fd
LP
208 }
209}
210
211static int dns_cache_item_prioq_compare_func(const void *a, const void *b) {
322345fd
LP
212 const DnsCacheItem *x = a, *y = b;
213
7e8e0422 214 if (x->until < y->until)
322345fd 215 return -1;
7e8e0422 216 if (x->until > y->until)
322345fd
LP
217 return 1;
218 return 0;
219}
220
623a4c97 221static int dns_cache_init(DnsCache *c) {
7e8e0422
LP
222 int r;
223
623a4c97
LP
224 assert(c);
225
7e8e0422
LP
226 r = prioq_ensure_allocated(&c->by_expiry, dns_cache_item_prioq_compare_func);
227 if (r < 0)
228 return r;
229
d5099efc 230 r = hashmap_ensure_allocated(&c->by_key, &dns_resource_key_hash_ops);
7e8e0422
LP
231 if (r < 0)
232 return r;
233
234 return r;
235}
236
237static int dns_cache_link_item(DnsCache *c, DnsCacheItem *i) {
238 DnsCacheItem *first;
239 int r;
240
322345fd
LP
241 assert(c);
242 assert(i);
322345fd 243
7e8e0422
LP
244 r = prioq_put(c->by_expiry, i, &i->prioq_idx);
245 if (r < 0)
246 return r;
322345fd 247
7e8e0422
LP
248 first = hashmap_get(c->by_key, i->key);
249 if (first) {
f57e3cd5
LP
250 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
251
252 /* Keep a reference to the original key, while we manipulate the list. */
253 k = dns_resource_key_ref(first->key);
254
255 /* Now, try to reduce the number of keys we keep */
256 dns_resource_key_reduce(&first->key, &i->key);
257
258 if (first->rr)
259 dns_resource_key_reduce(&first->rr->key, &i->key);
260 if (i->rr)
261 dns_resource_key_reduce(&i->rr->key, &i->key);
262
7e8e0422
LP
263 LIST_PREPEND(by_key, first, i);
264 assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
265 } else {
266 r = hashmap_put(c->by_key, i->key, i);
267 if (r < 0) {
268 prioq_remove(c->by_expiry, i, &i->prioq_idx);
269 return r;
270 }
322345fd
LP
271 }
272
7e8e0422 273 return 0;
322345fd
LP
274}
275
faa133f3
LP
276static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
277 DnsCacheItem *i;
278
279 assert(c);
280 assert(rr);
281
7e8e0422 282 LIST_FOREACH(by_key, i, hashmap_get(c->by_key, rr->key))
3ef77d04 283 if (i->rr && dns_resource_record_equal(i->rr, rr) > 0)
faa133f3
LP
284 return i;
285
286 return NULL;
287}
288
d3760be0 289static usec_t calculate_until(DnsResourceRecord *rr, uint32_t nsec_ttl, usec_t timestamp, bool use_soa_minimum) {
b211dc7e
LP
290 uint32_t ttl;
291 usec_t u;
ee3d6aff
LP
292
293 assert(rr);
294
d3760be0 295 ttl = MIN(rr->ttl, nsec_ttl);
b211dc7e
LP
296 if (rr->key->type == DNS_TYPE_SOA && use_soa_minimum) {
297 /* If this is a SOA RR, and it is requested, clamp to
298 * the SOA's minimum field. This is used when we do
299 * negative caching, to determine the TTL for the
300 * negative caching entry. See RFC 2308, Section
301 * 5. */
ee3d6aff 302
b211dc7e
LP
303 if (ttl > rr->soa.minimum)
304 ttl = rr->soa.minimum;
305 }
306
307 u = ttl * USEC_PER_SEC;
308 if (u > CACHE_TTL_MAX_USEC)
309 u = CACHE_TTL_MAX_USEC;
ee3d6aff
LP
310
311 if (rr->expiry != USEC_INFINITY) {
312 usec_t left;
313
b211dc7e
LP
314 /* Make use of the DNSSEC RRSIG expiry info, if we
315 * have it */
ee3d6aff
LP
316
317 left = LESS_BY(rr->expiry, now(CLOCK_REALTIME));
b211dc7e
LP
318 if (u > left)
319 u = left;
ee3d6aff
LP
320 }
321
b211dc7e 322 return timestamp + u;
ee3d6aff
LP
323}
324
d2579eec
LP
325static void dns_cache_item_update_positive(
326 DnsCache *c,
327 DnsCacheItem *i,
328 DnsResourceRecord *rr,
329 bool authenticated,
330 bool shared_owner,
331 usec_t timestamp,
332 int owner_family,
333 const union in_addr_union *owner_address) {
334
7e8e0422
LP
335 assert(c);
336 assert(i);
337 assert(rr);
d2579eec 338 assert(owner_address);
7e8e0422
LP
339
340 i->type = DNS_CACHE_POSITIVE;
341
ece174c5 342 if (!i->by_key_prev)
7e8e0422
LP
343 /* We are the first item in the list, we need to
344 * update the key used in the hashmap */
345
346 assert_se(hashmap_replace(c->by_key, rr->key, i) >= 0);
7e8e0422
LP
347
348 dns_resource_record_ref(rr);
349 dns_resource_record_unref(i->rr);
350 i->rr = rr;
351
352 dns_resource_key_unref(i->key);
353 i->key = dns_resource_key_ref(rr->key);
354
d3760be0 355 i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
d2579eec
LP
356 i->authenticated = authenticated;
357 i->shared_owner = shared_owner;
358
359 i->owner_family = owner_family;
360 i->owner_address = *owner_address;
7e8e0422
LP
361
362 prioq_reshuffle(c->by_expiry, i, &i->prioq_idx);
363}
364
a4076574
LP
365static int dns_cache_put_positive(
366 DnsCache *c,
367 DnsResourceRecord *rr,
931851e8 368 bool authenticated,
d2579eec 369 bool shared_owner,
a4076574
LP
370 usec_t timestamp,
371 int owner_family,
372 const union in_addr_union *owner_address) {
373
322345fd 374 _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
6b34a6c9 375 _cleanup_free_ char *key_str = NULL;
7e8e0422 376 DnsCacheItem *existing;
a257f9d4 377 int r, k;
322345fd
LP
378
379 assert(c);
380 assert(rr);
a4076574 381 assert(owner_address);
322345fd 382
222148b6
LP
383 /* Never cache pseudo RRs */
384 if (dns_class_is_pseudo(rr->key->class))
385 return 0;
386 if (dns_type_is_pseudo(rr->key->type))
387 return 0;
388
f5bdeb01 389 /* New TTL is 0? Delete this specific entry... */
322345fd 390 if (rr->ttl <= 0) {
f5bdeb01 391 k = dns_cache_remove_by_rr(c, rr);
6b34a6c9 392
a257f9d4
TG
393 if (log_get_max_level() >= LOG_DEBUG) {
394 r = dns_resource_key_to_string(rr->key, &key_str);
395 if (r < 0)
396 return r;
397
398 if (k > 0)
399 log_debug("Removed zero TTL entry from cache: %s", key_str);
400 else
401 log_debug("Not caching zero TTL cache entry: %s", key_str);
402 }
6b34a6c9 403
322345fd
LP
404 return 0;
405 }
406
d2579eec 407 /* Entry exists already? Update TTL, timestamp and owner*/
322345fd
LP
408 existing = dns_cache_get(c, rr);
409 if (existing) {
d2579eec
LP
410 dns_cache_item_update_positive(
411 c,
412 existing,
413 rr,
414 authenticated,
415 shared_owner,
416 timestamp,
417 owner_family,
418 owner_address);
322345fd
LP
419 return 0;
420 }
421
422 /* Otherwise, add the new RR */
623a4c97 423 r = dns_cache_init(c);
322345fd
LP
424 if (r < 0)
425 return r;
426
7e8e0422
LP
427 dns_cache_make_space(c, 1);
428
429 i = new0(DnsCacheItem, 1);
430 if (!i)
431 return -ENOMEM;
432
433 i->type = DNS_CACHE_POSITIVE;
434 i->key = dns_resource_key_ref(rr->key);
435 i->rr = dns_resource_record_ref(rr);
d3760be0 436 i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
d2579eec
LP
437 i->authenticated = authenticated;
438 i->shared_owner = shared_owner;
a4076574
LP
439 i->owner_family = owner_family;
440 i->owner_address = *owner_address;
d2579eec 441 i->prioq_idx = PRIOQ_IDX_NULL;
7e8e0422
LP
442
443 r = dns_cache_link_item(c, i);
444 if (r < 0)
445 return r;
446
a257f9d4
TG
447 if (log_get_max_level() >= LOG_DEBUG) {
448 r = dns_resource_key_to_string(i->key, &key_str);
449 if (r < 0)
450 return r;
6b34a6c9 451
d2579eec 452 log_debug("Added positive cache entry for %s", key_str);
a257f9d4 453 }
6b34a6c9 454
7e8e0422
LP
455 i = NULL;
456 return 0;
457}
458
a4076574
LP
459static int dns_cache_put_negative(
460 DnsCache *c,
461 DnsResourceKey *key,
462 int rcode,
931851e8 463 bool authenticated,
d3760be0 464 uint32_t nsec_ttl,
a4076574 465 usec_t timestamp,
b211dc7e 466 DnsResourceRecord *soa,
a4076574
LP
467 int owner_family,
468 const union in_addr_union *owner_address) {
469
7e8e0422 470 _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
6b34a6c9 471 _cleanup_free_ char *key_str = NULL;
7e8e0422
LP
472 int r;
473
474 assert(c);
475 assert(key);
b211dc7e 476 assert(soa);
a4076574 477 assert(owner_address);
7e8e0422 478
98b6be77
LP
479 /* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
480 * important to filter out as we use this as a pseudo-type for
481 * NXDOMAIN entries */
222148b6 482 if (dns_class_is_pseudo(key->class))
ddf16339 483 return 0;
c33be4a6 484 if (dns_type_is_pseudo(key->type))
ddf16339 485 return 0;
222148b6 486
d3760be0 487 if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) {
a257f9d4
TG
488 if (log_get_max_level() >= LOG_DEBUG) {
489 r = dns_resource_key_to_string(key, &key_str);
490 if (r < 0)
491 return r;
6b34a6c9 492
d3760be0 493 log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s", key_str);
a257f9d4 494 }
6b34a6c9 495
95dd6257 496 return 0;
6b34a6c9 497 }
ddf16339 498
7e8e0422
LP
499 if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
500 return 0;
501
623a4c97 502 r = dns_cache_init(c);
322345fd
LP
503 if (r < 0)
504 return r;
505
506 dns_cache_make_space(c, 1);
507
508 i = new0(DnsCacheItem, 1);
509 if (!i)
510 return -ENOMEM;
511
7e8e0422 512 i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN;
d3760be0 513 i->until = calculate_until(soa, nsec_ttl, timestamp, true);
d2579eec 514 i->authenticated = authenticated;
a4076574
LP
515 i->owner_family = owner_family;
516 i->owner_address = *owner_address;
d2579eec 517 i->prioq_idx = PRIOQ_IDX_NULL;
322345fd 518
71e13669
TG
519 if (i->type == DNS_CACHE_NXDOMAIN) {
520 /* NXDOMAIN entries should apply equally to all types, so we use ANY as
521 * a pseudo type for this purpose here. */
522 i->key = dns_resource_key_new(key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(key));
523 if (!i->key)
524 return -ENOMEM;
a5444ca9
LP
525
526 /* Make sure to remove any previous entry for this
527 * specific ANY key. (For non-ANY keys the cache data
528 * is already cleared by the caller.) Note that we
529 * don't bother removing positive or NODATA cache
530 * items in this case, because it would either be slow
531 * or require explicit indexing by name */
532 dns_cache_remove_by_key(c, key);
71e13669
TG
533 } else
534 i->key = dns_resource_key_ref(key);
535
7e8e0422 536 r = dns_cache_link_item(c, i);
322345fd
LP
537 if (r < 0)
538 return r;
539
a257f9d4
TG
540 if (log_get_max_level() >= LOG_DEBUG) {
541 r = dns_resource_key_to_string(i->key, &key_str);
542 if (r < 0)
543 return r;
6b34a6c9 544
a257f9d4
TG
545 log_debug("Added %s cache entry for %s", i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", key_str);
546 }
6b34a6c9 547
322345fd 548 i = NULL;
322345fd
LP
549 return 0;
550}
551
d2579eec
LP
552static void dns_cache_remove_previous(
553 DnsCache *c,
554 DnsResourceKey *key,
555 DnsAnswer *answer) {
556
557 DnsResourceRecord *rr;
558 DnsAnswerFlags flags;
559
560 assert(c);
561
562 /* First, if we were passed a key (i.e. on LLMNR/DNS, but
563 * not on mDNS), delete all matching old RRs, so that we only
564 * keep complete by_key in place. */
565 if (key)
2dda578f 566 dns_cache_remove_by_key(c, key);
d2579eec
LP
567
568 /* Second, flush all entries matching the answer, unless this
569 * is an RR that is explicitly marked to be "shared" between
570 * peers (i.e. mDNS RRs without the flush-cache bit set). */
571 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
572 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
573 continue;
574
575 if (flags & DNS_ANSWER_SHARED_OWNER)
576 continue;
577
2dda578f 578 dns_cache_remove_by_key(c, rr->key);
d2579eec
LP
579 }
580}
581
f6618dcd
LP
582static bool rr_eligible(DnsResourceRecord *rr) {
583 assert(rr);
584
585 /* When we see an NSEC/NSEC3 RR, we'll only cache it if it is from the lower zone, not the upper zone, since
586 * that's where the interesting bits are (with exception of DS RRs). Of course, this way we cannot derive DS
587 * existence from any cached NSEC/NSEC3, but that should be fine. */
588
589 switch (rr->key->type) {
590
591 case DNS_TYPE_NSEC:
592 return !bitmap_isset(rr->nsec.types, DNS_TYPE_NS) ||
593 bitmap_isset(rr->nsec.types, DNS_TYPE_SOA);
594
595 case DNS_TYPE_NSEC3:
596 return !bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) ||
597 bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA);
598
599 default:
600 return true;
601 }
602}
603
a4076574
LP
604int dns_cache_put(
605 DnsCache *c,
8e427d9b 606 DnsResourceKey *key,
a4076574
LP
607 int rcode,
608 DnsAnswer *answer,
931851e8 609 bool authenticated,
d3760be0 610 uint32_t nsec_ttl,
a4076574
LP
611 usec_t timestamp,
612 int owner_family,
613 const union in_addr_union *owner_address) {
614
02c2857b 615 DnsResourceRecord *soa = NULL, *rr;
105e1512
LP
616 DnsAnswerFlags flags;
617 unsigned cache_keys;
322345fd
LP
618 int r;
619
620 assert(c);
d2579eec 621 assert(owner_address);
322345fd 622
d2579eec 623 dns_cache_remove_previous(c, key, answer);
0ec7c46e 624
ea207b63 625 if (dns_answer_size(answer) <= 0) {
a257f9d4
TG
626 if (log_get_max_level() >= LOG_DEBUG) {
627 _cleanup_free_ char *key_str = NULL;
c3cb6dc2 628
a257f9d4
TG
629 r = dns_resource_key_to_string(key, &key_str);
630 if (r < 0)
631 return r;
c3cb6dc2 632
a257f9d4
TG
633 log_debug("Not caching negative entry without a SOA record: %s", key_str);
634 }
c3cb6dc2 635
0ec7c46e 636 return 0;
c3cb6dc2 637 }
0ec7c46e 638
7e8e0422
LP
639 /* We only care for positive replies and NXDOMAINs, on all
640 * other replies we will simply flush the respective entries,
641 * and that's it */
7e8e0422
LP
642 if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
643 return 0;
644
ea207b63 645 cache_keys = dns_answer_size(answer);
8e427d9b
TG
646 if (key)
647 cache_keys ++;
eff91ee0 648
7e8e0422 649 /* Make some space for our new entries */
eff91ee0 650 dns_cache_make_space(c, cache_keys);
322345fd 651
7e8e0422 652 if (timestamp <= 0)
240b589b 653 timestamp = now(clock_boottime_or_monotonic());
322345fd 654
7e8e0422 655 /* Second, add in positive entries for all contained RRs */
105e1512
LP
656 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
657 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
658 continue;
659
f6618dcd
LP
660 r = rr_eligible(rr);
661 if (r < 0)
662 return r;
663 if (r == 0)
664 continue;
665
d2579eec
LP
666 r = dns_cache_put_positive(
667 c,
668 rr,
669 flags & DNS_ANSWER_AUTHENTICATED,
670 flags & DNS_ANSWER_SHARED_OWNER,
671 timestamp,
672 owner_family, owner_address);
7e8e0422
LP
673 if (r < 0)
674 goto fail;
675 }
676
d2579eec 677 if (!key) /* mDNS doesn't know negative caching, really */
eff91ee0
DM
678 return 0;
679
8e427d9b 680 /* Third, add in negative entries if the key has no RR */
105e1512 681 r = dns_answer_match_key(answer, key, NULL);
8e427d9b
TG
682 if (r < 0)
683 goto fail;
684 if (r > 0)
685 return 0;
7e8e0422 686
5d27351f
TG
687 /* But not if it has a matching CNAME/DNAME (the negative
688 * caching will be done on the canonical name, not on the
689 * alias) */
105e1512 690 r = dns_answer_find_cname_or_dname(answer, key, NULL, NULL);
5d27351f
TG
691 if (r < 0)
692 goto fail;
693 if (r > 0)
694 return 0;
695
547973de
LP
696 /* See https://tools.ietf.org/html/rfc2308, which say that a
697 * matching SOA record in the packet is used to to enable
698 * negative caching. */
fd009cd8 699 r = dns_answer_find_soa(answer, key, &soa, &flags);
8e427d9b
TG
700 if (r < 0)
701 goto fail;
702 if (r == 0)
703 return 0;
7e8e0422 704
fd009cd8
LP
705 /* Refuse using the SOA data if it is unsigned, but the key is
706 * signed */
707 if (authenticated && (flags & DNS_ANSWER_AUTHENTICATED) == 0)
708 return 0;
709
d2579eec
LP
710 r = dns_cache_put_negative(
711 c,
712 key,
713 rcode,
714 authenticated,
d3760be0 715 nsec_ttl,
d2579eec 716 timestamp,
b211dc7e 717 soa,
d2579eec 718 owner_family, owner_address);
8e427d9b
TG
719 if (r < 0)
720 goto fail;
322345fd
LP
721
722 return 0;
723
724fail:
725 /* Adding all RRs failed. Let's clean up what we already
726 * added, just in case */
727
8e427d9b 728 if (key)
2dda578f 729 dns_cache_remove_by_key(c, key);
eff91ee0 730
105e1512
LP
731 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
732 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
733 continue;
734
2dda578f 735 dns_cache_remove_by_key(c, rr->key);
105e1512 736 }
322345fd
LP
737
738 return r;
739}
740
37da8931 741static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, DnsResourceKey *k) {
58db254a
LP
742 DnsCacheItem *i;
743 const char *n;
744 int r;
5643c00a
TG
745
746 assert(c);
747 assert(k);
748
58db254a
LP
749 /* If we hit some OOM error, or suchlike, we don't care too
750 * much, after all this is just a cache */
751
5643c00a 752 i = hashmap_get(c->by_key, k);
d7ce6c94 753 if (i)
37da8931
LP
754 return i;
755
756 n = DNS_RESOURCE_KEY_NAME(k);
757
71e13669
TG
758 /* Check if we have an NXDOMAIN cache item for the name, notice that we use
759 * the pseudo-type ANY for NXDOMAIN cache items. */
760 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_ANY, n));
761 if (i && i->type == DNS_CACHE_NXDOMAIN)
762 return i;
763
d3c7e913 764 if (dns_type_may_redirect(k->type)) {
d7ce6c94
TG
765 /* Check if we have a CNAME record instead */
766 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_CNAME, n));
767 if (i)
768 return i;
5643c00a 769
d7ce6c94
TG
770 /* OK, let's look for cached DNAME records. */
771 for (;;) {
d7ce6c94
TG
772 if (isempty(n))
773 return NULL;
774
775 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_DNAME, n));
776 if (i)
777 return i;
58db254a 778
d7ce6c94 779 /* Jump one label ahead */
950b692b 780 r = dns_name_parent(&n);
d7ce6c94
TG
781 if (r <= 0)
782 return NULL;
783 }
784 }
5643c00a 785
950b692b 786 if (k->type != DNS_TYPE_NSEC) {
d7ce6c94
TG
787 /* Check if we have an NSEC record instead for the name. */
788 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_NSEC, n));
58db254a
LP
789 if (i)
790 return i;
58db254a
LP
791 }
792
793 return NULL;
5643c00a
TG
794}
795
931851e8 796int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret, bool *authenticated) {
faa133f3 797 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
f52e61da 798 unsigned n = 0;
322345fd 799 int r;
7e8e0422 800 bool nxdomain = false;
f52e61da 801 _cleanup_free_ char *key_str = NULL;
931851e8
LP
802 DnsCacheItem *j, *first, *nsec = NULL;
803 bool have_authenticated = false, have_non_authenticated = false;
322345fd
LP
804
805 assert(c);
f52e61da 806 assert(key);
623a4c97 807 assert(rcode);
faa133f3 808 assert(ret);
931851e8 809 assert(authenticated);
322345fd 810
f52e61da
LP
811 if (key->type == DNS_TYPE_ANY ||
812 key->class == DNS_CLASS_ANY) {
322345fd 813
931851e8
LP
814 /* If we have ANY lookups we don't use the cache, so
815 * that the caller refreshes via the network. */
322345fd 816
a257f9d4
TG
817 if (log_get_max_level() >= LOG_DEBUG) {
818 r = dns_resource_key_to_string(key, &key_str);
819 if (r < 0)
820 return r;
6b34a6c9 821
a257f9d4
TG
822 log_debug("Ignoring cache for ANY lookup: %s", key_str);
823 }
6b34a6c9 824
a150ff5e
LP
825 c->n_miss++;
826
f52e61da
LP
827 *ret = NULL;
828 *rcode = DNS_RCODE_SUCCESS;
829 return 0;
830 }
6b34a6c9 831
37da8931 832 first = dns_cache_get_by_key_follow_cname_dname_nsec(c, key);
f52e61da
LP
833 if (!first) {
834 /* If one question cannot be answered we need to refresh */
ddf16339 835
a257f9d4
TG
836 if (log_get_max_level() >= LOG_DEBUG) {
837 r = dns_resource_key_to_string(key, &key_str);
838 if (r < 0)
839 return r;
6b34a6c9 840
a257f9d4
TG
841 log_debug("Cache miss for %s", key_str);
842 }
6b34a6c9 843
a150ff5e
LP
844 c->n_miss++;
845
f52e61da
LP
846 *ret = NULL;
847 *rcode = DNS_RCODE_SUCCESS;
848 return 0;
849 }
6b34a6c9 850
f52e61da 851 LIST_FOREACH(by_key, j, first) {
37da8931
LP
852 if (j->rr) {
853 if (j->rr->key->type == DNS_TYPE_NSEC)
931851e8
LP
854 nsec = j;
855
f52e61da 856 n++;
37da8931 857 } else if (j->type == DNS_CACHE_NXDOMAIN)
f52e61da 858 nxdomain = true;
931851e8
LP
859
860 if (j->authenticated)
861 have_authenticated = true;
862 else
863 have_non_authenticated = true;
f52e61da 864 }
6b34a6c9 865
f6618dcd
LP
866 if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) {
867 /* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC RRs from
868 * the lower-zone of a zone cut, but the DS RRs are on the upper zone. */
869
a257f9d4
TG
870 if (log_get_max_level() >= LOG_DEBUG) {
871 r = dns_resource_key_to_string(key, &key_str);
872 if (r < 0)
873 return r;
874
875 log_debug("NSEC NODATA cache hit for %s", key_str);
876 }
37da8931
LP
877
878 /* We only found an NSEC record that matches our name.
a257f9d4 879 * If it says the type doesn't exist report
37da8931
LP
880 * NODATA. Otherwise report a cache miss. */
881
882 *ret = NULL;
883 *rcode = DNS_RCODE_SUCCESS;
931851e8 884 *authenticated = nsec->authenticated;
37da8931 885
a150ff5e
LP
886 if (!bitmap_isset(nsec->rr->nsec.types, key->type) &&
887 !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
888 !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME)) {
889 c->n_hit++;
890 return 1;
891 }
892
893 c->n_miss++;
894 return 0;
37da8931
LP
895 }
896
a257f9d4
TG
897 if (log_get_max_level() >= LOG_DEBUG) {
898 r = dns_resource_key_to_string(key, &key_str);
899 if (r < 0)
900 return r;
901
902 log_debug("%s cache hit for %s",
903 n > 0 ? "Positive" :
904 nxdomain ? "NXDOMAIN" : "NODATA",
905 key_str);
906 }
faa133f3 907
7e8e0422 908 if (n <= 0) {
a150ff5e
LP
909 c->n_hit++;
910
7e8e0422
LP
911 *ret = NULL;
912 *rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
931851e8 913 *authenticated = have_authenticated && !have_non_authenticated;
7e8e0422
LP
914 return 1;
915 }
faa133f3
LP
916
917 answer = dns_answer_new(n);
918 if (!answer)
919 return -ENOMEM;
322345fd 920
f52e61da
LP
921 LIST_FOREACH(by_key, j, first) {
922 if (!j->rr)
923 continue;
924
eed749cc 925 r = dns_answer_add(answer, j->rr, 0, j->authenticated ? DNS_ANSWER_AUTHENTICATED : 0);
f52e61da
LP
926 if (r < 0)
927 return r;
322345fd
LP
928 }
929
a150ff5e
LP
930 c->n_hit++;
931
faa133f3 932 *ret = answer;
7e8e0422 933 *rcode = DNS_RCODE_SUCCESS;
931851e8 934 *authenticated = have_authenticated && !have_non_authenticated;
faa133f3
LP
935 answer = NULL;
936
937 return n;
322345fd 938}
a4076574
LP
939
940int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) {
941 DnsCacheItem *i, *first;
942 bool same_owner = true;
943
944 assert(cache);
945 assert(rr);
946
947 dns_cache_prune(cache);
948
949 /* See if there's a cache entry for the same key. If there
950 * isn't there's no conflict */
951 first = hashmap_get(cache->by_key, rr->key);
952 if (!first)
953 return 0;
954
955 /* See if the RR key is owned by the same owner, if so, there
956 * isn't a conflict either */
957 LIST_FOREACH(by_key, i, first) {
958 if (i->owner_family != owner_family ||
959 !in_addr_equal(owner_family, &i->owner_address, owner_address)) {
960 same_owner = false;
961 break;
962 }
963 }
964 if (same_owner)
965 return 0;
966
967 /* See if there's the exact same RR in the cache. If yes, then
968 * there's no conflict. */
969 if (dns_cache_get(cache, rr))
970 return 0;
971
972 /* There's a conflict */
973 return 1;
974}
4d506d6b 975
7778dfff
DM
976int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
977 unsigned ancount = 0;
978 Iterator iterator;
979 DnsCacheItem *i;
980 int r;
981
982 assert(cache);
261f3673 983 assert(p);
7778dfff
DM
984
985 HASHMAP_FOREACH(i, cache->by_key, iterator) {
986 DnsCacheItem *j;
987
988 LIST_FOREACH(by_key, j, i) {
7778dfff
DM
989 if (!j->rr)
990 continue;
991
d2579eec 992 if (!j->shared_owner)
7778dfff
DM
993 continue;
994
995 r = dns_packet_append_rr(p, j->rr, NULL, NULL);
261f3673
DM
996 if (r == -EMSGSIZE && p->protocol == DNS_PROTOCOL_MDNS) {
997 /* For mDNS, if we're unable to stuff all known answers into the given packet,
998 * allocate a new one, push the RR into that one and link it to the current one.
999 */
1000
1001 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
1002 ancount = 0;
1003
1004 r = dns_packet_new_query(&p->more, p->protocol, 0, true);
1005 if (r < 0)
1006 return r;
1007
1008 /* continue with new packet */
1009 p = p->more;
1010 r = dns_packet_append_rr(p, j->rr, NULL, NULL);
1011 }
1012
7778dfff
DM
1013 if (r < 0)
1014 return r;
1015
1016 ancount ++;
1017 }
1018 }
1019
1020 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
1021
1022 return 0;
1023}
1024
4d506d6b
LP
1025void dns_cache_dump(DnsCache *cache, FILE *f) {
1026 Iterator iterator;
1027 DnsCacheItem *i;
1028 int r;
1029
1030 if (!cache)
1031 return;
1032
1033 if (!f)
1034 f = stdout;
1035
1036 HASHMAP_FOREACH(i, cache->by_key, iterator) {
1037 DnsCacheItem *j;
1038
1039 LIST_FOREACH(by_key, j, i) {
4d506d6b
LP
1040
1041 fputc('\t', f);
1042
1043 if (j->rr) {
7b50eb2e
LP
1044 const char *t;
1045 t = dns_resource_record_to_string(j->rr);
1046 if (!t) {
4d506d6b
LP
1047 log_oom();
1048 continue;
1049 }
1050
1051 fputs(t, f);
1052 fputc('\n', f);
1053 } else {
7b50eb2e
LP
1054 _cleanup_free_ char *z = NULL;
1055 r = dns_resource_key_to_string(j->key, &z);
4d506d6b
LP
1056 if (r < 0) {
1057 log_oom();
1058 continue;
1059 }
1060
7b50eb2e 1061 fputs(z, f);
4d506d6b
LP
1062 fputs(" -- ", f);
1063 fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f);
1064 fputc('\n', f);
1065 }
1066 }
1067 }
1068}
1069
1070bool dns_cache_is_empty(DnsCache *cache) {
1071 if (!cache)
1072 return true;
1073
1074 return hashmap_isempty(cache->by_key);
1075}
a150ff5e
LP
1076
1077unsigned dns_cache_size(DnsCache *cache) {
1078 if (!cache)
1079 return 0;
1080
1081 return hashmap_size(cache->by_key);
1082}