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