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