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