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