]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-cache.c
resolved: when switching between DNSSEC modes, possibly flush caches
[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) {
250 LIST_PREPEND(by_key, first, i);
251 assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
252 } else {
253 r = hashmap_put(c->by_key, i->key, i);
254 if (r < 0) {
255 prioq_remove(c->by_expiry, i, &i->prioq_idx);
256 return r;
257 }
322345fd
LP
258 }
259
7e8e0422 260 return 0;
322345fd
LP
261}
262
faa133f3
LP
263static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
264 DnsCacheItem *i;
265
266 assert(c);
267 assert(rr);
268
7e8e0422 269 LIST_FOREACH(by_key, i, hashmap_get(c->by_key, rr->key))
3ef77d04 270 if (i->rr && dns_resource_record_equal(i->rr, rr) > 0)
faa133f3
LP
271 return i;
272
273 return NULL;
274}
275
d3760be0 276static usec_t calculate_until(DnsResourceRecord *rr, uint32_t nsec_ttl, usec_t timestamp, bool use_soa_minimum) {
b211dc7e
LP
277 uint32_t ttl;
278 usec_t u;
ee3d6aff
LP
279
280 assert(rr);
281
d3760be0 282 ttl = MIN(rr->ttl, nsec_ttl);
b211dc7e
LP
283 if (rr->key->type == DNS_TYPE_SOA && use_soa_minimum) {
284 /* If this is a SOA RR, and it is requested, clamp to
285 * the SOA's minimum field. This is used when we do
286 * negative caching, to determine the TTL for the
287 * negative caching entry. See RFC 2308, Section
288 * 5. */
ee3d6aff 289
b211dc7e
LP
290 if (ttl > rr->soa.minimum)
291 ttl = rr->soa.minimum;
292 }
293
294 u = ttl * USEC_PER_SEC;
295 if (u > CACHE_TTL_MAX_USEC)
296 u = CACHE_TTL_MAX_USEC;
ee3d6aff
LP
297
298 if (rr->expiry != USEC_INFINITY) {
299 usec_t left;
300
b211dc7e
LP
301 /* Make use of the DNSSEC RRSIG expiry info, if we
302 * have it */
ee3d6aff
LP
303
304 left = LESS_BY(rr->expiry, now(CLOCK_REALTIME));
b211dc7e
LP
305 if (u > left)
306 u = left;
ee3d6aff
LP
307 }
308
b211dc7e 309 return timestamp + u;
ee3d6aff
LP
310}
311
d2579eec
LP
312static void dns_cache_item_update_positive(
313 DnsCache *c,
314 DnsCacheItem *i,
315 DnsResourceRecord *rr,
316 bool authenticated,
317 bool shared_owner,
318 usec_t timestamp,
319 int owner_family,
320 const union in_addr_union *owner_address) {
321
7e8e0422
LP
322 assert(c);
323 assert(i);
324 assert(rr);
d2579eec 325 assert(owner_address);
7e8e0422
LP
326
327 i->type = DNS_CACHE_POSITIVE;
328
ece174c5 329 if (!i->by_key_prev)
7e8e0422
LP
330 /* We are the first item in the list, we need to
331 * update the key used in the hashmap */
332
333 assert_se(hashmap_replace(c->by_key, rr->key, i) >= 0);
7e8e0422
LP
334
335 dns_resource_record_ref(rr);
336 dns_resource_record_unref(i->rr);
337 i->rr = rr;
338
339 dns_resource_key_unref(i->key);
340 i->key = dns_resource_key_ref(rr->key);
341
d3760be0 342 i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
d2579eec
LP
343 i->authenticated = authenticated;
344 i->shared_owner = shared_owner;
345
346 i->owner_family = owner_family;
347 i->owner_address = *owner_address;
7e8e0422
LP
348
349 prioq_reshuffle(c->by_expiry, i, &i->prioq_idx);
350}
351
a4076574
LP
352static int dns_cache_put_positive(
353 DnsCache *c,
354 DnsResourceRecord *rr,
931851e8 355 bool authenticated,
d2579eec 356 bool shared_owner,
a4076574
LP
357 usec_t timestamp,
358 int owner_family,
359 const union in_addr_union *owner_address) {
360
322345fd 361 _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
6b34a6c9 362 _cleanup_free_ char *key_str = NULL;
7e8e0422 363 DnsCacheItem *existing;
a257f9d4 364 int r, k;
322345fd
LP
365
366 assert(c);
367 assert(rr);
a4076574 368 assert(owner_address);
322345fd 369
222148b6
LP
370 /* Never cache pseudo RRs */
371 if (dns_class_is_pseudo(rr->key->class))
372 return 0;
373 if (dns_type_is_pseudo(rr->key->type))
374 return 0;
375
f5bdeb01 376 /* New TTL is 0? Delete this specific entry... */
322345fd 377 if (rr->ttl <= 0) {
f5bdeb01 378 k = dns_cache_remove_by_rr(c, rr);
6b34a6c9 379
a257f9d4
TG
380 if (log_get_max_level() >= LOG_DEBUG) {
381 r = dns_resource_key_to_string(rr->key, &key_str);
382 if (r < 0)
383 return r;
384
385 if (k > 0)
386 log_debug("Removed zero TTL entry from cache: %s", key_str);
387 else
388 log_debug("Not caching zero TTL cache entry: %s", key_str);
389 }
6b34a6c9 390
322345fd
LP
391 return 0;
392 }
393
d2579eec 394 /* Entry exists already? Update TTL, timestamp and owner*/
322345fd
LP
395 existing = dns_cache_get(c, rr);
396 if (existing) {
d2579eec
LP
397 dns_cache_item_update_positive(
398 c,
399 existing,
400 rr,
401 authenticated,
402 shared_owner,
403 timestamp,
404 owner_family,
405 owner_address);
322345fd
LP
406 return 0;
407 }
408
409 /* Otherwise, add the new RR */
623a4c97 410 r = dns_cache_init(c);
322345fd
LP
411 if (r < 0)
412 return r;
413
7e8e0422
LP
414 dns_cache_make_space(c, 1);
415
416 i = new0(DnsCacheItem, 1);
417 if (!i)
418 return -ENOMEM;
419
420 i->type = DNS_CACHE_POSITIVE;
421 i->key = dns_resource_key_ref(rr->key);
422 i->rr = dns_resource_record_ref(rr);
d3760be0 423 i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
d2579eec
LP
424 i->authenticated = authenticated;
425 i->shared_owner = shared_owner;
a4076574
LP
426 i->owner_family = owner_family;
427 i->owner_address = *owner_address;
d2579eec 428 i->prioq_idx = PRIOQ_IDX_NULL;
7e8e0422
LP
429
430 r = dns_cache_link_item(c, i);
431 if (r < 0)
432 return r;
433
a257f9d4
TG
434 if (log_get_max_level() >= LOG_DEBUG) {
435 r = dns_resource_key_to_string(i->key, &key_str);
436 if (r < 0)
437 return r;
6b34a6c9 438
d2579eec 439 log_debug("Added positive cache entry for %s", key_str);
a257f9d4 440 }
6b34a6c9 441
7e8e0422
LP
442 i = NULL;
443 return 0;
444}
445
a4076574
LP
446static int dns_cache_put_negative(
447 DnsCache *c,
448 DnsResourceKey *key,
449 int rcode,
931851e8 450 bool authenticated,
d3760be0 451 uint32_t nsec_ttl,
a4076574 452 usec_t timestamp,
b211dc7e 453 DnsResourceRecord *soa,
a4076574
LP
454 int owner_family,
455 const union in_addr_union *owner_address) {
456
7e8e0422 457 _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
6b34a6c9 458 _cleanup_free_ char *key_str = NULL;
7e8e0422
LP
459 int r;
460
461 assert(c);
462 assert(key);
b211dc7e 463 assert(soa);
a4076574 464 assert(owner_address);
7e8e0422 465
98b6be77
LP
466 /* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
467 * important to filter out as we use this as a pseudo-type for
468 * NXDOMAIN entries */
222148b6 469 if (dns_class_is_pseudo(key->class))
ddf16339 470 return 0;
c33be4a6 471 if (dns_type_is_pseudo(key->type))
ddf16339 472 return 0;
222148b6 473
d3760be0 474 if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) {
a257f9d4
TG
475 if (log_get_max_level() >= LOG_DEBUG) {
476 r = dns_resource_key_to_string(key, &key_str);
477 if (r < 0)
478 return r;
6b34a6c9 479
d3760be0 480 log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s", key_str);
a257f9d4 481 }
6b34a6c9 482
95dd6257 483 return 0;
6b34a6c9 484 }
ddf16339 485
7e8e0422
LP
486 if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
487 return 0;
488
623a4c97 489 r = dns_cache_init(c);
322345fd
LP
490 if (r < 0)
491 return r;
492
493 dns_cache_make_space(c, 1);
494
495 i = new0(DnsCacheItem, 1);
496 if (!i)
497 return -ENOMEM;
498
7e8e0422 499 i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN;
d3760be0 500 i->until = calculate_until(soa, nsec_ttl, timestamp, true);
d2579eec 501 i->authenticated = authenticated;
a4076574
LP
502 i->owner_family = owner_family;
503 i->owner_address = *owner_address;
d2579eec 504 i->prioq_idx = PRIOQ_IDX_NULL;
322345fd 505
71e13669
TG
506 if (i->type == DNS_CACHE_NXDOMAIN) {
507 /* NXDOMAIN entries should apply equally to all types, so we use ANY as
508 * a pseudo type for this purpose here. */
509 i->key = dns_resource_key_new(key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(key));
510 if (!i->key)
511 return -ENOMEM;
a5444ca9
LP
512
513 /* Make sure to remove any previous entry for this
514 * specific ANY key. (For non-ANY keys the cache data
515 * is already cleared by the caller.) Note that we
516 * don't bother removing positive or NODATA cache
517 * items in this case, because it would either be slow
518 * or require explicit indexing by name */
519 dns_cache_remove_by_key(c, key);
71e13669
TG
520 } else
521 i->key = dns_resource_key_ref(key);
522
7e8e0422 523 r = dns_cache_link_item(c, i);
322345fd
LP
524 if (r < 0)
525 return r;
526
a257f9d4
TG
527 if (log_get_max_level() >= LOG_DEBUG) {
528 r = dns_resource_key_to_string(i->key, &key_str);
529 if (r < 0)
530 return r;
6b34a6c9 531
a257f9d4
TG
532 log_debug("Added %s cache entry for %s", i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", key_str);
533 }
6b34a6c9 534
322345fd 535 i = NULL;
322345fd
LP
536 return 0;
537}
538
d2579eec
LP
539static void dns_cache_remove_previous(
540 DnsCache *c,
541 DnsResourceKey *key,
542 DnsAnswer *answer) {
543
544 DnsResourceRecord *rr;
545 DnsAnswerFlags flags;
546
547 assert(c);
548
549 /* First, if we were passed a key (i.e. on LLMNR/DNS, but
550 * not on mDNS), delete all matching old RRs, so that we only
551 * keep complete by_key in place. */
552 if (key)
2dda578f 553 dns_cache_remove_by_key(c, key);
d2579eec
LP
554
555 /* Second, flush all entries matching the answer, unless this
556 * is an RR that is explicitly marked to be "shared" between
557 * peers (i.e. mDNS RRs without the flush-cache bit set). */
558 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
559 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
560 continue;
561
562 if (flags & DNS_ANSWER_SHARED_OWNER)
563 continue;
564
2dda578f 565 dns_cache_remove_by_key(c, rr->key);
d2579eec
LP
566 }
567}
568
a4076574
LP
569int dns_cache_put(
570 DnsCache *c,
8e427d9b 571 DnsResourceKey *key,
a4076574
LP
572 int rcode,
573 DnsAnswer *answer,
931851e8 574 bool authenticated,
d3760be0 575 uint32_t nsec_ttl,
a4076574
LP
576 usec_t timestamp,
577 int owner_family,
578 const union in_addr_union *owner_address) {
579
02c2857b 580 DnsResourceRecord *soa = NULL, *rr;
105e1512
LP
581 DnsAnswerFlags flags;
582 unsigned cache_keys;
322345fd
LP
583 int r;
584
585 assert(c);
d2579eec 586 assert(owner_address);
322345fd 587
d2579eec 588 dns_cache_remove_previous(c, key, answer);
0ec7c46e 589
ea207b63 590 if (dns_answer_size(answer) <= 0) {
a257f9d4
TG
591 if (log_get_max_level() >= LOG_DEBUG) {
592 _cleanup_free_ char *key_str = NULL;
c3cb6dc2 593
a257f9d4
TG
594 r = dns_resource_key_to_string(key, &key_str);
595 if (r < 0)
596 return r;
c3cb6dc2 597
a257f9d4
TG
598 log_debug("Not caching negative entry without a SOA record: %s", key_str);
599 }
c3cb6dc2 600
0ec7c46e 601 return 0;
c3cb6dc2 602 }
0ec7c46e 603
7e8e0422
LP
604 /* We only care for positive replies and NXDOMAINs, on all
605 * other replies we will simply flush the respective entries,
606 * and that's it */
7e8e0422
LP
607 if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
608 return 0;
609
ea207b63 610 cache_keys = dns_answer_size(answer);
8e427d9b
TG
611 if (key)
612 cache_keys ++;
eff91ee0 613
7e8e0422 614 /* Make some space for our new entries */
eff91ee0 615 dns_cache_make_space(c, cache_keys);
322345fd 616
7e8e0422 617 if (timestamp <= 0)
240b589b 618 timestamp = now(clock_boottime_or_monotonic());
322345fd 619
7e8e0422 620 /* Second, add in positive entries for all contained RRs */
105e1512
LP
621 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
622 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
623 continue;
624
d2579eec
LP
625 r = dns_cache_put_positive(
626 c,
627 rr,
628 flags & DNS_ANSWER_AUTHENTICATED,
629 flags & DNS_ANSWER_SHARED_OWNER,
630 timestamp,
631 owner_family, owner_address);
7e8e0422
LP
632 if (r < 0)
633 goto fail;
634 }
635
d2579eec 636 if (!key) /* mDNS doesn't know negative caching, really */
eff91ee0
DM
637 return 0;
638
8e427d9b 639 /* Third, add in negative entries if the key has no RR */
105e1512 640 r = dns_answer_match_key(answer, key, NULL);
8e427d9b
TG
641 if (r < 0)
642 goto fail;
643 if (r > 0)
644 return 0;
7e8e0422 645
5d27351f
TG
646 /* But not if it has a matching CNAME/DNAME (the negative
647 * caching will be done on the canonical name, not on the
648 * alias) */
105e1512 649 r = dns_answer_find_cname_or_dname(answer, key, NULL, NULL);
5d27351f
TG
650 if (r < 0)
651 goto fail;
652 if (r > 0)
653 return 0;
654
547973de
LP
655 /* See https://tools.ietf.org/html/rfc2308, which say that a
656 * matching SOA record in the packet is used to to enable
657 * negative caching. */
fd009cd8 658 r = dns_answer_find_soa(answer, key, &soa, &flags);
8e427d9b
TG
659 if (r < 0)
660 goto fail;
661 if (r == 0)
662 return 0;
7e8e0422 663
fd009cd8
LP
664 /* Refuse using the SOA data if it is unsigned, but the key is
665 * signed */
666 if (authenticated && (flags & DNS_ANSWER_AUTHENTICATED) == 0)
667 return 0;
668
d2579eec
LP
669 r = dns_cache_put_negative(
670 c,
671 key,
672 rcode,
673 authenticated,
d3760be0 674 nsec_ttl,
d2579eec 675 timestamp,
b211dc7e 676 soa,
d2579eec 677 owner_family, owner_address);
8e427d9b
TG
678 if (r < 0)
679 goto fail;
322345fd
LP
680
681 return 0;
682
683fail:
684 /* Adding all RRs failed. Let's clean up what we already
685 * added, just in case */
686
8e427d9b 687 if (key)
2dda578f 688 dns_cache_remove_by_key(c, key);
eff91ee0 689
105e1512
LP
690 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
691 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
692 continue;
693
2dda578f 694 dns_cache_remove_by_key(c, rr->key);
105e1512 695 }
322345fd
LP
696
697 return r;
698}
699
37da8931 700static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, DnsResourceKey *k) {
58db254a
LP
701 DnsCacheItem *i;
702 const char *n;
703 int r;
5643c00a
TG
704
705 assert(c);
706 assert(k);
707
58db254a
LP
708 /* If we hit some OOM error, or suchlike, we don't care too
709 * much, after all this is just a cache */
710
5643c00a 711 i = hashmap_get(c->by_key, k);
d7ce6c94 712 if (i)
37da8931
LP
713 return i;
714
715 n = DNS_RESOURCE_KEY_NAME(k);
716
71e13669
TG
717 /* Check if we have an NXDOMAIN cache item for the name, notice that we use
718 * the pseudo-type ANY for NXDOMAIN cache items. */
719 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_ANY, n));
720 if (i && i->type == DNS_CACHE_NXDOMAIN)
721 return i;
722
d3c7e913 723 if (dns_type_may_redirect(k->type)) {
d7ce6c94
TG
724 /* Check if we have a CNAME record instead */
725 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_CNAME, n));
726 if (i)
727 return i;
5643c00a 728
d7ce6c94
TG
729 /* OK, let's look for cached DNAME records. */
730 for (;;) {
d7ce6c94
TG
731 if (isempty(n))
732 return NULL;
733
734 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_DNAME, n));
735 if (i)
736 return i;
58db254a 737
d7ce6c94 738 /* Jump one label ahead */
950b692b 739 r = dns_name_parent(&n);
d7ce6c94
TG
740 if (r <= 0)
741 return NULL;
742 }
743 }
5643c00a 744
950b692b 745 if (k->type != DNS_TYPE_NSEC) {
d7ce6c94
TG
746 /* Check if we have an NSEC record instead for the name. */
747 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_NSEC, n));
58db254a
LP
748 if (i)
749 return i;
58db254a
LP
750 }
751
752 return NULL;
5643c00a
TG
753}
754
931851e8 755int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret, bool *authenticated) {
faa133f3 756 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
f52e61da 757 unsigned n = 0;
322345fd 758 int r;
7e8e0422 759 bool nxdomain = false;
f52e61da 760 _cleanup_free_ char *key_str = NULL;
931851e8
LP
761 DnsCacheItem *j, *first, *nsec = NULL;
762 bool have_authenticated = false, have_non_authenticated = false;
322345fd
LP
763
764 assert(c);
f52e61da 765 assert(key);
623a4c97 766 assert(rcode);
faa133f3 767 assert(ret);
931851e8 768 assert(authenticated);
322345fd 769
f52e61da
LP
770 if (key->type == DNS_TYPE_ANY ||
771 key->class == DNS_CLASS_ANY) {
322345fd 772
931851e8
LP
773 /* If we have ANY lookups we don't use the cache, so
774 * that the caller refreshes via the network. */
322345fd 775
a257f9d4
TG
776 if (log_get_max_level() >= LOG_DEBUG) {
777 r = dns_resource_key_to_string(key, &key_str);
778 if (r < 0)
779 return r;
6b34a6c9 780
a257f9d4
TG
781 log_debug("Ignoring cache for ANY lookup: %s", key_str);
782 }
6b34a6c9 783
a150ff5e
LP
784 c->n_miss++;
785
f52e61da
LP
786 *ret = NULL;
787 *rcode = DNS_RCODE_SUCCESS;
788 return 0;
789 }
6b34a6c9 790
37da8931 791 first = dns_cache_get_by_key_follow_cname_dname_nsec(c, key);
f52e61da
LP
792 if (!first) {
793 /* If one question cannot be answered we need to refresh */
ddf16339 794
a257f9d4
TG
795 if (log_get_max_level() >= LOG_DEBUG) {
796 r = dns_resource_key_to_string(key, &key_str);
797 if (r < 0)
798 return r;
6b34a6c9 799
a257f9d4
TG
800 log_debug("Cache miss for %s", key_str);
801 }
6b34a6c9 802
a150ff5e
LP
803 c->n_miss++;
804
f52e61da
LP
805 *ret = NULL;
806 *rcode = DNS_RCODE_SUCCESS;
807 return 0;
808 }
6b34a6c9 809
f52e61da 810 LIST_FOREACH(by_key, j, first) {
37da8931
LP
811 if (j->rr) {
812 if (j->rr->key->type == DNS_TYPE_NSEC)
931851e8
LP
813 nsec = j;
814
f52e61da 815 n++;
37da8931 816 } else if (j->type == DNS_CACHE_NXDOMAIN)
f52e61da 817 nxdomain = true;
931851e8
LP
818
819 if (j->authenticated)
820 have_authenticated = true;
821 else
822 have_non_authenticated = true;
f52e61da 823 }
6b34a6c9 824
37da8931 825 if (nsec && key->type != DNS_TYPE_NSEC) {
a257f9d4
TG
826 if (log_get_max_level() >= LOG_DEBUG) {
827 r = dns_resource_key_to_string(key, &key_str);
828 if (r < 0)
829 return r;
830
831 log_debug("NSEC NODATA cache hit for %s", key_str);
832 }
37da8931
LP
833
834 /* We only found an NSEC record that matches our name.
a257f9d4 835 * If it says the type doesn't exist report
37da8931
LP
836 * NODATA. Otherwise report a cache miss. */
837
838 *ret = NULL;
839 *rcode = DNS_RCODE_SUCCESS;
931851e8 840 *authenticated = nsec->authenticated;
37da8931 841
a150ff5e
LP
842 if (!bitmap_isset(nsec->rr->nsec.types, key->type) &&
843 !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
844 !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME)) {
845 c->n_hit++;
846 return 1;
847 }
848
849 c->n_miss++;
850 return 0;
37da8931
LP
851 }
852
a257f9d4
TG
853 if (log_get_max_level() >= LOG_DEBUG) {
854 r = dns_resource_key_to_string(key, &key_str);
855 if (r < 0)
856 return r;
857
858 log_debug("%s cache hit for %s",
859 n > 0 ? "Positive" :
860 nxdomain ? "NXDOMAIN" : "NODATA",
861 key_str);
862 }
faa133f3 863
7e8e0422 864 if (n <= 0) {
a150ff5e
LP
865 c->n_hit++;
866
7e8e0422
LP
867 *ret = NULL;
868 *rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
931851e8 869 *authenticated = have_authenticated && !have_non_authenticated;
7e8e0422
LP
870 return 1;
871 }
faa133f3
LP
872
873 answer = dns_answer_new(n);
874 if (!answer)
875 return -ENOMEM;
322345fd 876
f52e61da
LP
877 LIST_FOREACH(by_key, j, first) {
878 if (!j->rr)
879 continue;
880
eed749cc 881 r = dns_answer_add(answer, j->rr, 0, j->authenticated ? DNS_ANSWER_AUTHENTICATED : 0);
f52e61da
LP
882 if (r < 0)
883 return r;
322345fd
LP
884 }
885
a150ff5e
LP
886 c->n_hit++;
887
faa133f3 888 *ret = answer;
7e8e0422 889 *rcode = DNS_RCODE_SUCCESS;
931851e8 890 *authenticated = have_authenticated && !have_non_authenticated;
faa133f3
LP
891 answer = NULL;
892
893 return n;
322345fd 894}
a4076574
LP
895
896int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) {
897 DnsCacheItem *i, *first;
898 bool same_owner = true;
899
900 assert(cache);
901 assert(rr);
902
903 dns_cache_prune(cache);
904
905 /* See if there's a cache entry for the same key. If there
906 * isn't there's no conflict */
907 first = hashmap_get(cache->by_key, rr->key);
908 if (!first)
909 return 0;
910
911 /* See if the RR key is owned by the same owner, if so, there
912 * isn't a conflict either */
913 LIST_FOREACH(by_key, i, first) {
914 if (i->owner_family != owner_family ||
915 !in_addr_equal(owner_family, &i->owner_address, owner_address)) {
916 same_owner = false;
917 break;
918 }
919 }
920 if (same_owner)
921 return 0;
922
923 /* See if there's the exact same RR in the cache. If yes, then
924 * there's no conflict. */
925 if (dns_cache_get(cache, rr))
926 return 0;
927
928 /* There's a conflict */
929 return 1;
930}
4d506d6b 931
7778dfff
DM
932int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
933 unsigned ancount = 0;
934 Iterator iterator;
935 DnsCacheItem *i;
936 int r;
937
938 assert(cache);
261f3673 939 assert(p);
7778dfff
DM
940
941 HASHMAP_FOREACH(i, cache->by_key, iterator) {
942 DnsCacheItem *j;
943
944 LIST_FOREACH(by_key, j, i) {
7778dfff
DM
945 if (!j->rr)
946 continue;
947
d2579eec 948 if (!j->shared_owner)
7778dfff
DM
949 continue;
950
951 r = dns_packet_append_rr(p, j->rr, NULL, NULL);
261f3673
DM
952 if (r == -EMSGSIZE && p->protocol == DNS_PROTOCOL_MDNS) {
953 /* For mDNS, if we're unable to stuff all known answers into the given packet,
954 * allocate a new one, push the RR into that one and link it to the current one.
955 */
956
957 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
958 ancount = 0;
959
960 r = dns_packet_new_query(&p->more, p->protocol, 0, true);
961 if (r < 0)
962 return r;
963
964 /* continue with new packet */
965 p = p->more;
966 r = dns_packet_append_rr(p, j->rr, NULL, NULL);
967 }
968
7778dfff
DM
969 if (r < 0)
970 return r;
971
972 ancount ++;
973 }
974 }
975
976 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
977
978 return 0;
979}
980
4d506d6b
LP
981void dns_cache_dump(DnsCache *cache, FILE *f) {
982 Iterator iterator;
983 DnsCacheItem *i;
984 int r;
985
986 if (!cache)
987 return;
988
989 if (!f)
990 f = stdout;
991
992 HASHMAP_FOREACH(i, cache->by_key, iterator) {
993 DnsCacheItem *j;
994
995 LIST_FOREACH(by_key, j, i) {
4d506d6b
LP
996
997 fputc('\t', f);
998
999 if (j->rr) {
7b50eb2e
LP
1000 const char *t;
1001 t = dns_resource_record_to_string(j->rr);
1002 if (!t) {
4d506d6b
LP
1003 log_oom();
1004 continue;
1005 }
1006
1007 fputs(t, f);
1008 fputc('\n', f);
1009 } else {
7b50eb2e
LP
1010 _cleanup_free_ char *z = NULL;
1011 r = dns_resource_key_to_string(j->key, &z);
4d506d6b
LP
1012 if (r < 0) {
1013 log_oom();
1014 continue;
1015 }
1016
7b50eb2e 1017 fputs(z, f);
4d506d6b
LP
1018 fputs(" -- ", f);
1019 fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f);
1020 fputc('\n', f);
1021 }
1022 }
1023 }
1024}
1025
1026bool dns_cache_is_empty(DnsCache *cache) {
1027 if (!cache)
1028 return true;
1029
1030 return hashmap_isempty(cache->by_key);
1031}
a150ff5e
LP
1032
1033unsigned dns_cache_size(DnsCache *cache) {
1034 if (!cache)
1035 return 0;
1036
1037 return hashmap_size(cache->by_key);
1038}