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