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