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