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