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