]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-answer.c
add ipv6 range element creation test cases
[thirdparty/systemd.git] / src / resolve / resolved-dns-answer.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <stdio.h>
4
5 #include "alloc-util.h"
6 #include "dns-domain.h"
7 #include "resolved-dns-answer.h"
8 #include "resolved-dns-dnssec.h"
9 #include "string-util.h"
10
11 static void dns_answer_item_hash_func(const DnsAnswerItem *a, struct siphash *state) {
12 assert(a);
13 assert(state);
14
15 siphash24_compress(&a->ifindex, sizeof(a->ifindex), state);
16
17 dns_resource_record_hash_func(a->rr, state);
18 }
19
20 static int dns_answer_item_compare_func(const DnsAnswerItem *a, const DnsAnswerItem *b) {
21 int r;
22
23 assert(a);
24 assert(b);
25
26 r = CMP(a->ifindex, b->ifindex);
27 if (r != 0)
28 return r;
29
30 return dns_resource_record_compare_func(a->rr, b->rr);
31 }
32
33 DEFINE_PRIVATE_HASH_OPS(dns_answer_item_hash_ops, DnsAnswerItem, dns_answer_item_hash_func, dns_answer_item_compare_func);
34
35 DnsAnswer *dns_answer_new(size_t n) {
36 _cleanup_set_free_ Set *s = NULL;
37 DnsAnswer *a;
38
39 if (n > UINT16_MAX) /* We can only place 64K RRs in an answer at max */
40 n = UINT16_MAX;
41
42 s = set_new(&dns_answer_item_hash_ops);
43 if (!s)
44 return NULL;
45
46 /* Higher multipliers give slightly higher efficiency through hash collisions, but the gains
47 * quickly drop off after 2. */
48 if (set_reserve(s, n * 2) < 0)
49 return NULL;
50
51 a = malloc0(offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * n);
52 if (!a)
53 return NULL;
54
55 a->n_ref = 1;
56 a->n_allocated = n;
57 a->set_items = TAKE_PTR(s);
58
59 return a;
60 }
61
62 static void dns_answer_flush(DnsAnswer *a) {
63 DnsResourceRecord *rr;
64
65 if (!a)
66 return;
67
68 a->set_items = set_free(a->set_items);
69
70 DNS_ANSWER_FOREACH(rr, a)
71 dns_resource_record_unref(rr);
72
73 a->n_rrs = 0;
74 }
75
76 static DnsAnswer *dns_answer_free(DnsAnswer *a) {
77 assert(a);
78
79 dns_answer_flush(a);
80 return mfree(a);
81 }
82
83 DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsAnswer, dns_answer, dns_answer_free);
84
85 static int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
86 int r;
87
88 assert(rr);
89
90 if (!a)
91 return -ENOSPC;
92
93 if (a->n_rrs >= a->n_allocated)
94 return -ENOSPC;
95
96 a->items[a->n_rrs] = (DnsAnswerItem) {
97 .rr = rr,
98 .ifindex = ifindex,
99 .flags = flags,
100 };
101
102 r = set_put(a->set_items, &a->items[a->n_rrs]);
103 if (r < 0)
104 return r;
105 if (r == 0)
106 return -EEXIST;
107
108 dns_resource_record_ref(rr);
109 a->n_rrs++;
110
111 return 1;
112 }
113
114 static int dns_answer_add_raw_all(DnsAnswer *a, DnsAnswer *source) {
115 DnsResourceRecord *rr;
116 DnsAnswerFlags flags;
117 int ifindex, r;
118
119 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, source) {
120 r = dns_answer_add_raw(a, rr, ifindex, flags);
121 if (r < 0)
122 return r;
123 }
124
125 return 0;
126 }
127
128 int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
129 DnsAnswerItem tmp, *exist;
130
131 assert(rr);
132
133 if (!a)
134 return -ENOSPC;
135 if (a->n_ref > 1)
136 return -EBUSY;
137
138 tmp = (DnsAnswerItem) {
139 .rr = rr,
140 .ifindex = ifindex,
141 };
142
143 exist = set_get(a->set_items, &tmp);
144 if (exist) {
145 /* There's already an RR of the same RRset in place! Let's see if the TTLs more or less
146 * match. We don't really care if they match precisely, but we do care whether one is 0 and
147 * the other is not. See RFC 2181, Section 5.2. */
148 if ((rr->ttl == 0) != (exist->rr->ttl == 0))
149 return -EINVAL;
150
151 /* Entry already exists, keep the entry with the higher TTL. */
152 if (rr->ttl > exist->rr->ttl) {
153 dns_resource_record_unref(exist->rr);
154 exist->rr = dns_resource_record_ref(rr);
155 }
156
157 exist->flags |= flags;
158 return 0;
159 }
160
161 return dns_answer_add_raw(a, rr, ifindex, flags);
162 }
163
164 static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
165 DnsResourceRecord *rr;
166 DnsAnswerFlags flags;
167 int ifindex, r;
168
169 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, b) {
170 r = dns_answer_add(a, rr, ifindex, flags);
171 if (r < 0)
172 return r;
173 }
174
175 return 0;
176 }
177
178 int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
179 int r;
180
181 assert(a);
182 assert(rr);
183
184 r = dns_answer_reserve_or_clone(a, 1);
185 if (r < 0)
186 return r;
187
188 return dns_answer_add(*a, rr, ifindex, flags);
189 }
190
191 int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl, int ifindex) {
192 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *soa = NULL;
193
194 soa = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SOA, name);
195 if (!soa)
196 return -ENOMEM;
197
198 soa->ttl = ttl;
199
200 soa->soa.mname = strdup(name);
201 if (!soa->soa.mname)
202 return -ENOMEM;
203
204 soa->soa.rname = strjoin("root.", name);
205 if (!soa->soa.rname)
206 return -ENOMEM;
207
208 soa->soa.serial = 1;
209 soa->soa.refresh = 1;
210 soa->soa.retry = 1;
211 soa->soa.expire = 1;
212 soa->soa.minimum = ttl;
213
214 return dns_answer_add(a, soa, ifindex, DNS_ANSWER_AUTHENTICATED);
215 }
216
217 int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
218 DnsAnswerFlags flags = 0, i_flags;
219 DnsResourceRecord *i;
220 bool found = false;
221 int r;
222
223 assert(key);
224
225 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
226 r = dns_resource_key_match_rr(key, i, NULL);
227 if (r < 0)
228 return r;
229 if (r == 0)
230 continue;
231
232 if (!ret_flags)
233 return 1;
234
235 if (found)
236 flags &= i_flags;
237 else {
238 flags = i_flags;
239 found = true;
240 }
241 }
242
243 if (ret_flags)
244 *ret_flags = flags;
245
246 return found;
247 }
248
249 int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
250 DnsResourceRecord *i;
251
252 DNS_ANSWER_FOREACH(i, a) {
253 if (IN_SET(i->key->type, DNS_TYPE_NSEC, DNS_TYPE_NSEC3))
254 return true;
255 }
256
257 return false;
258 }
259
260 int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone) {
261 DnsResourceRecord *rr;
262 int r;
263
264 /* Checks whether the specified answer contains at least one NSEC3 RR in the specified zone */
265
266 DNS_ANSWER_FOREACH(rr, answer) {
267 const char *p;
268
269 if (rr->key->type != DNS_TYPE_NSEC3)
270 continue;
271
272 p = dns_resource_key_name(rr->key);
273 r = dns_name_parent(&p);
274 if (r < 0)
275 return r;
276 if (r == 0)
277 continue;
278
279 r = dns_name_equal(p, zone);
280 if (r != 0)
281 return r;
282 }
283
284 return false;
285 }
286
287 int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
288 DnsResourceRecord *rr, *soa = NULL;
289 DnsAnswerFlags rr_flags, soa_flags = 0;
290 int r;
291
292 assert(key);
293
294 /* For a SOA record we can never find a matching SOA record */
295 if (key->type == DNS_TYPE_SOA)
296 return 0;
297
298 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
299 r = dns_resource_key_match_soa(key, rr->key);
300 if (r < 0)
301 return r;
302 if (r > 0) {
303
304 if (soa) {
305 r = dns_name_endswith(dns_resource_key_name(rr->key), dns_resource_key_name(soa->key));
306 if (r < 0)
307 return r;
308 if (r > 0)
309 continue;
310 }
311
312 soa = rr;
313 soa_flags = rr_flags;
314 }
315 }
316
317 if (!soa)
318 return 0;
319
320 if (ret)
321 *ret = soa;
322 if (flags)
323 *flags = soa_flags;
324
325 return 1;
326 }
327
328 int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
329 DnsResourceRecord *rr;
330 DnsAnswerFlags rr_flags;
331 int r;
332
333 assert(key);
334
335 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
336 if (!dns_type_may_redirect(key->type))
337 return 0;
338
339 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
340 r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
341 if (r < 0)
342 return r;
343 if (r > 0) {
344 if (ret)
345 *ret = rr;
346 if (flags)
347 *flags = rr_flags;
348 return 1;
349 }
350 }
351
352 return 0;
353 }
354
355 int dns_answer_merge(DnsAnswer *a, DnsAnswer *b, DnsAnswer **ret) {
356 _cleanup_(dns_answer_unrefp) DnsAnswer *k = NULL;
357 int r;
358
359 assert(ret);
360
361 if (a == b) {
362 *ret = dns_answer_ref(a);
363 return 0;
364 }
365
366 if (dns_answer_size(a) <= 0) {
367 *ret = dns_answer_ref(b);
368 return 0;
369 }
370
371 if (dns_answer_size(b) <= 0) {
372 *ret = dns_answer_ref(a);
373 return 0;
374 }
375
376 k = dns_answer_new(a->n_rrs + b->n_rrs);
377 if (!k)
378 return -ENOMEM;
379
380 r = dns_answer_add_raw_all(k, a);
381 if (r < 0)
382 return r;
383
384 r = dns_answer_add_all(k, b);
385 if (r < 0)
386 return r;
387
388 *ret = TAKE_PTR(k);
389
390 return 0;
391 }
392
393 int dns_answer_extend(DnsAnswer **a, DnsAnswer *b) {
394 DnsAnswer *merged;
395 int r;
396
397 assert(a);
398
399 r = dns_answer_merge(*a, b, &merged);
400 if (r < 0)
401 return r;
402
403 dns_answer_unref(*a);
404 *a = merged;
405
406 return 0;
407 }
408
409 int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
410 bool found = false, other = false;
411 DnsResourceRecord *rr;
412 size_t i;
413 int r;
414
415 assert(a);
416 assert(key);
417
418 /* Remove all entries matching the specified key from *a */
419
420 DNS_ANSWER_FOREACH(rr, *a) {
421 r = dns_resource_key_equal(rr->key, key);
422 if (r < 0)
423 return r;
424 if (r > 0)
425 found = true;
426 else
427 other = true;
428
429 if (found && other)
430 break;
431 }
432
433 if (!found)
434 return 0;
435
436 if (!other) {
437 *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
438 return 1;
439 }
440
441 if ((*a)->n_ref > 1) {
442 _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
443 DnsAnswerFlags flags;
444 int ifindex;
445
446 copy = dns_answer_new((*a)->n_rrs);
447 if (!copy)
448 return -ENOMEM;
449
450 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
451 r = dns_resource_key_equal(rr->key, key);
452 if (r < 0)
453 return r;
454 if (r > 0)
455 continue;
456
457 r = dns_answer_add_raw(copy, rr, ifindex, flags);
458 if (r < 0)
459 return r;
460 }
461
462 dns_answer_unref(*a);
463 *a = TAKE_PTR(copy);
464
465 return 1;
466 }
467
468 /* Only a single reference, edit in-place */
469
470 i = 0;
471 for (;;) {
472 if (i >= (*a)->n_rrs)
473 break;
474
475 r = dns_resource_key_equal((*a)->items[i].rr->key, key);
476 if (r < 0)
477 return r;
478 if (r > 0) {
479 /* Kill this entry */
480
481 dns_resource_record_unref((*a)->items[i].rr);
482 memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
483 (*a)->n_rrs--;
484 continue;
485
486 } else
487 /* Keep this entry */
488 i++;
489 }
490
491 return 1;
492 }
493
494 int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rm) {
495 bool found = false, other = false;
496 DnsResourceRecord *rr;
497 size_t i;
498 int r;
499
500 assert(a);
501 assert(rm);
502
503 /* Remove all entries matching the specified RR from *a */
504
505 DNS_ANSWER_FOREACH(rr, *a) {
506 r = dns_resource_record_equal(rr, rm);
507 if (r < 0)
508 return r;
509 if (r > 0)
510 found = true;
511 else
512 other = true;
513
514 if (found && other)
515 break;
516 }
517
518 if (!found)
519 return 0;
520
521 if (!other) {
522 *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
523 return 1;
524 }
525
526 if ((*a)->n_ref > 1) {
527 _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
528 DnsAnswerFlags flags;
529 int ifindex;
530
531 copy = dns_answer_new((*a)->n_rrs);
532 if (!copy)
533 return -ENOMEM;
534
535 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
536 r = dns_resource_record_equal(rr, rm);
537 if (r < 0)
538 return r;
539 if (r > 0)
540 continue;
541
542 r = dns_answer_add_raw(copy, rr, ifindex, flags);
543 if (r < 0)
544 return r;
545 }
546
547 dns_answer_unref(*a);
548 *a = TAKE_PTR(copy);
549
550 return 1;
551 }
552
553 /* Only a single reference, edit in-place */
554
555 i = 0;
556 for (;;) {
557 if (i >= (*a)->n_rrs)
558 break;
559
560 r = dns_resource_record_equal((*a)->items[i].rr, rm);
561 if (r < 0)
562 return r;
563 if (r > 0) {
564 /* Kill this entry */
565
566 dns_resource_record_unref((*a)->items[i].rr);
567 memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
568 (*a)->n_rrs--;
569 continue;
570
571 } else
572 /* Keep this entry */
573 i++;
574 }
575
576 return 1;
577 }
578
579 int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
580 DnsResourceRecord *rr_source;
581 int ifindex_source, r;
582 DnsAnswerFlags flags_source;
583
584 assert(a);
585 assert(key);
586
587 /* Copy all RRs matching the specified key from source into *a */
588
589 DNS_ANSWER_FOREACH_FULL(rr_source, ifindex_source, flags_source, source) {
590
591 r = dns_resource_key_equal(rr_source->key, key);
592 if (r < 0)
593 return r;
594 if (r == 0)
595 continue;
596
597 /* Make space for at least one entry */
598 r = dns_answer_reserve_or_clone(a, 1);
599 if (r < 0)
600 return r;
601
602 r = dns_answer_add(*a, rr_source, ifindex_source, flags_source|or_flags);
603 if (r < 0)
604 return r;
605 }
606
607 return 0;
608 }
609
610 int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
611 int r;
612
613 assert(to);
614 assert(from);
615 assert(key);
616
617 r = dns_answer_copy_by_key(to, *from, key, or_flags);
618 if (r < 0)
619 return r;
620
621 return dns_answer_remove_by_key(from, key);
622 }
623
624 void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
625 DnsAnswerItem *items;
626 size_t i, start, end;
627
628 if (!a)
629 return;
630
631 if (a->n_rrs <= 1)
632 return;
633
634 start = 0;
635 end = a->n_rrs-1;
636
637 /* RFC 4795, Section 2.6 suggests we should order entries
638 * depending on whether the sender is a link-local address. */
639
640 items = newa(DnsAnswerItem, a->n_rrs);
641 for (i = 0; i < a->n_rrs; i++) {
642
643 if (a->items[i].rr->key->class == DNS_CLASS_IN &&
644 ((a->items[i].rr->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->items[i].rr->a.in_addr) != prefer_link_local) ||
645 (a->items[i].rr->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->items[i].rr->aaaa.in6_addr) != prefer_link_local)))
646 /* Order address records that are not preferred to the end of the array */
647 items[end--] = a->items[i];
648 else
649 /* Order all other records to the beginning of the array */
650 items[start++] = a->items[i];
651 }
652
653 assert(start == end+1);
654 memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs);
655 }
656
657 int dns_answer_reserve(DnsAnswer **a, size_t n_free) {
658 DnsAnswer *n;
659
660 assert(a);
661
662 if (n_free <= 0)
663 return 0;
664
665 if (*a) {
666 size_t ns;
667 int r;
668
669 if ((*a)->n_ref > 1)
670 return -EBUSY;
671
672 ns = (*a)->n_rrs + n_free;
673 if (ns > UINT16_MAX) /* Maximum number of RRs we can stick into a DNS packet section */
674 ns = UINT16_MAX;
675
676 if ((*a)->n_allocated >= ns)
677 return 0;
678
679 /* Allocate more than we need */
680 ns *= 2;
681 if (ns > UINT16_MAX)
682 ns = UINT16_MAX;
683
684 /* This must be done before realloc() below. Otherwise, the original DnsAnswer object
685 * may be broken. */
686 r = set_reserve((*a)->set_items, ns);
687 if (r < 0)
688 return r;
689
690 n = realloc(*a, offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * ns);
691 if (!n)
692 return -ENOMEM;
693
694 n->n_allocated = ns;
695
696 /* Previously all items are stored in the set, and the enough memory area is allocated
697 * in the above. So set_put() in the below cannot fail. */
698 set_clear(n->set_items);
699 for (size_t i = 0; i < n->n_rrs; i++)
700 assert_se(set_put(n->set_items, &n->items[i]) > 0);
701 } else {
702 n = dns_answer_new(n_free);
703 if (!n)
704 return -ENOMEM;
705 }
706
707 *a = n;
708 return 0;
709 }
710
711 int dns_answer_reserve_or_clone(DnsAnswer **a, size_t n_free) {
712 _cleanup_(dns_answer_unrefp) DnsAnswer *n = NULL;
713 int r;
714
715 assert(a);
716
717 /* Tries to extend the DnsAnswer object. And if that's not
718 * possible, since we are not the sole owner, then allocate a
719 * new, appropriately sized one. Either way, after this call
720 * the object will only have a single reference, and has room
721 * for at least the specified number of RRs. */
722
723 r = dns_answer_reserve(a, n_free);
724 if (r != -EBUSY)
725 return r;
726
727 assert(*a);
728
729 n = dns_answer_new(((*a)->n_rrs + n_free) * 2);
730 if (!n)
731 return -ENOMEM;
732
733 r = dns_answer_add_raw_all(n, *a);
734 if (r < 0)
735 return r;
736
737 dns_answer_unref(*a);
738 *a = TAKE_PTR(n);
739
740 return 0;
741 }
742
743 /*
744 * This function is not used in the code base, but is useful when debugging. Do not delete.
745 */
746 void dns_answer_dump(DnsAnswer *answer, FILE *f) {
747 DnsResourceRecord *rr;
748 DnsAnswerFlags flags;
749 int ifindex;
750
751 if (!f)
752 f = stdout;
753
754 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
755 const char *t;
756
757 fputc('\t', f);
758
759 t = dns_resource_record_to_string(rr);
760 if (!t) {
761 log_oom();
762 continue;
763 }
764
765 fputs(t, f);
766
767 if (ifindex != 0 || flags != 0)
768 fputs("\t;", f);
769
770 if (ifindex != 0)
771 fprintf(f, " ifindex=%i", ifindex);
772 if (flags & DNS_ANSWER_AUTHENTICATED)
773 fputs(" authenticated", f);
774 if (flags & DNS_ANSWER_CACHEABLE)
775 fputs(" cacheable", f);
776 if (flags & DNS_ANSWER_SHARED_OWNER)
777 fputs(" shared-owner", f);
778 if (flags & DNS_ANSWER_CACHE_FLUSH)
779 fputs(" cache-flush", f);
780 if (flags & DNS_ANSWER_GOODBYE)
781 fputs(" goodbye", f);
782
783 fputc('\n', f);
784 }
785 }
786
787 int dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname) {
788 DnsResourceRecord *rr;
789 int r;
790
791 assert(cname);
792
793 /* Checks whether the answer contains a DNAME record that indicates that the specified CNAME record is
794 * synthesized from it */
795
796 if (cname->key->type != DNS_TYPE_CNAME)
797 return 0;
798
799 DNS_ANSWER_FOREACH(rr, a) {
800 _cleanup_free_ char *n = NULL;
801
802 if (rr->key->type != DNS_TYPE_DNAME)
803 continue;
804 if (rr->key->class != cname->key->class)
805 continue;
806
807 r = dns_name_change_suffix(cname->cname.name, rr->dname.name, dns_resource_key_name(rr->key), &n);
808 if (r < 0)
809 return r;
810 if (r == 0)
811 continue;
812
813 r = dns_name_equal(n, dns_resource_key_name(cname->key));
814 if (r < 0)
815 return r;
816 if (r > 0)
817 return 1;
818 }
819
820 return 0;
821 }