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