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