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