]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-answer.c
tests: pass halt_on_error=1 to UBSan
[thirdparty/systemd.git] / src / resolve / resolved-dns-answer.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
faa133f3 2
b5efdb8a 3#include "alloc-util.h"
4ad7f276 4#include "dns-domain.h"
07630cea 5#include "resolved-dns-answer.h"
72667f08 6#include "resolved-dns-dnssec.h"
07630cea 7#include "string-util.h"
faa133f3 8
da6053d0 9DnsAnswer *dns_answer_new(size_t n) {
faa133f3
LP
10 DnsAnswer *a;
11
78c6a153 12 a = malloc0(offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * n);
faa133f3
LP
13 if (!a)
14 return NULL;
15
16 a->n_ref = 1;
17 a->n_allocated = n;
18
19 return a;
20}
21
d42800f1
LP
22static 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
8301aa0b
YW
34static DnsAnswer *dns_answer_free(DnsAnswer *a) {
35 assert(a);
faa133f3 36
8301aa0b
YW
37 dns_answer_flush(a);
38 return mfree(a);
faa133f3
LP
39}
40
8301aa0b
YW
41DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsAnswer, dns_answer, dns_answer_free);
42
105e1512 43static int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
547973de
LP
44 assert(rr);
45
46 if (!a)
47 return -ENOSPC;
48
49 if (a->n_rrs >= a->n_allocated)
50 return -ENOSPC;
51
105e1512
LP
52 a->items[a->n_rrs++] = (DnsAnswerItem) {
53 .rr = dns_resource_record_ref(rr),
54 .ifindex = ifindex,
55 .flags = flags,
56 };
547973de
LP
57
58 return 1;
59}
60
61static int dns_answer_add_raw_all(DnsAnswer *a, DnsAnswer *source) {
62 DnsResourceRecord *rr;
105e1512 63 DnsAnswerFlags flags;
547973de
LP
64 int ifindex, r;
65
105e1512
LP
66 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, source) {
67 r = dns_answer_add_raw(a, rr, ifindex, flags);
547973de
LP
68 if (r < 0)
69 return r;
70 }
71
72 return 0;
73}
74
105e1512 75int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
da6053d0 76 size_t i;
7e8e0422
LP
77 int r;
78
faa133f3
LP
79 assert(rr);
80
78c6a153
LP
81 if (!a)
82 return -ENOSPC;
c296dd2e
LP
83 if (a->n_ref > 1)
84 return -EBUSY;
78c6a153 85
7e8e0422 86 for (i = 0; i < a->n_rrs; i++) {
78c6a153
LP
87 if (a->items[i].ifindex != ifindex)
88 continue;
89
90 r = dns_resource_record_equal(a->items[i].rr, rr);
7e8e0422
LP
91 if (r < 0)
92 return r;
93 if (r > 0) {
7feea00b
LP
94 /* Don't mix contradicting TTLs (see below) */
95 if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
96 return -EINVAL;
7e8e0422 97
7feea00b
LP
98 /* Entry already exists, keep the entry with
99 * the higher RR. */
100 if (rr->ttl > a->items[i].rr->ttl) {
7e8e0422 101 dns_resource_record_ref(rr);
78c6a153
LP
102 dns_resource_record_unref(a->items[i].rr);
103 a->items[i].rr = rr;
7e8e0422
LP
104 }
105
105e1512 106 a->items[i].flags |= flags;
7e8e0422
LP
107 return 0;
108 }
7feea00b
LP
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
13e785f7 119 * 5.2. */
7feea00b
LP
120
121 if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
122 return -EINVAL;
123 }
7e8e0422
LP
124 }
125
105e1512 126 return dns_answer_add_raw(a, rr, ifindex, flags);
547973de 127}
faa133f3 128
547973de
LP
129static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
130 DnsResourceRecord *rr;
105e1512 131 DnsAnswerFlags flags;
547973de 132 int ifindex, r;
78c6a153 133
105e1512
LP
134 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, b) {
135 r = dns_answer_add(a, rr, ifindex, flags);
547973de
LP
136 if (r < 0)
137 return r;
138 }
139
140 return 0;
141}
142
105e1512 143int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
547973de
LP
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
105e1512 153 return dns_answer_add(*a, rr, ifindex, flags);
7e8e0422
LP
154}
155
97ebebbc 156int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl, int ifindex) {
8bf52d3d
LP
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
57f5ad31
LP
163 soa->ttl = ttl;
164
8bf52d3d
LP
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;
57f5ad31 177 soa->soa.minimum = ttl;
8bf52d3d 178
97ebebbc 179 return dns_answer_add(a, soa, ifindex, DNS_ANSWER_AUTHENTICATED);
8bf52d3d
LP
180}
181
105e1512
LP
182int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
183 DnsAnswerFlags flags = 0, i_flags;
547973de 184 DnsResourceRecord *i;
105e1512 185 bool found = false;
7e8e0422
LP
186 int r;
187
7e8e0422
LP
188 assert(key);
189
105e1512 190 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
547973de 191 r = dns_resource_key_match_rr(key, i, NULL);
7e8e0422
LP
192 if (r < 0)
193 return r;
105e1512
LP
194 if (r == 0)
195 continue;
196
197 if (!ret_flags)
7e8e0422 198 return 1;
105e1512
LP
199
200 if (found)
201 flags &= i_flags;
202 else {
203 flags = i_flags;
204 found = true;
205 }
7e8e0422
LP
206 }
207
105e1512
LP
208 if (ret_flags)
209 *ret_flags = flags;
210
211 return found;
7e8e0422
LP
212}
213
105e1512
LP
214int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *ret_flags) {
215 DnsAnswerFlags flags = 0, i_flags;
547973de 216 DnsResourceRecord *i;
105e1512 217 bool found = false;
547973de 218 int r;
5eefe544 219
547973de 220 assert(rr);
5eefe544 221
105e1512 222 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
547973de
LP
223 r = dns_resource_record_equal(i, rr);
224 if (r < 0)
225 return r;
105e1512
LP
226 if (r == 0)
227 continue;
228
229 if (!ret_flags)
547973de 230 return 1;
105e1512
LP
231
232 if (found)
233 flags &= i_flags;
234 else {
235 flags = i_flags;
236 found = true;
237 }
547973de 238 }
5eefe544 239
105e1512
LP
240 if (ret_flags)
241 *ret_flags = flags;
242
243 return found;
244}
245
246int 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
278int 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;
5eefe544
TG
287}
288
e926785a
LP
289int 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
1c02e7ba 301 p = dns_resource_key_name(rr->key);
e926785a
LP
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
fd009cd8 316int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
81f7fc5e
LP
317 DnsResourceRecord *rr, *soa = NULL;
318 DnsAnswerFlags rr_flags, soa_flags = 0;
29c1519e 319 int r;
7e8e0422 320
7e8e0422 321 assert(key);
7e8e0422 322
0f05c387
LP
323 /* For a SOA record we can never find a matching SOA record */
324 if (key->type == DNS_TYPE_SOA)
325 return 0;
326
fd009cd8 327 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
29c1519e
LP
328 r = dns_resource_key_match_soa(key, rr->key);
329 if (r < 0)
330 return r;
331 if (r > 0) {
81f7fc5e
LP
332
333 if (soa) {
1c02e7ba 334 r = dns_name_endswith(dns_resource_key_name(rr->key), dns_resource_key_name(soa->key));
81f7fc5e
LP
335 if (r < 0)
336 return r;
337 if (r > 0)
338 continue;
339 }
340
341 soa = rr;
342 soa_flags = rr_flags;
7e8e0422
LP
343 }
344 }
345
81f7fc5e
LP
346 if (!soa)
347 return 0;
348
349 if (ret)
350 *ret = soa;
351 if (flags)
352 *flags = soa_flags;
353
354 return 1;
faa133f3 355}
934e9b10 356
105e1512 357int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
5d27351f 358 DnsResourceRecord *rr;
105e1512 359 DnsAnswerFlags rr_flags;
29c1519e 360 int r;
5d27351f
TG
361
362 assert(key);
363
5d27351f 364 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
6b2f7093 365 if (!dns_type_may_redirect(key->type))
5d27351f
TG
366 return 0;
367
105e1512 368 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
29c1519e
LP
369 r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
370 if (r < 0)
371 return r;
372 if (r > 0) {
5d27351f
TG
373 if (ret)
374 *ret = rr;
105e1512
LP
375 if (flags)
376 *flags = rr_flags;
5d27351f
TG
377 return 1;
378 }
379 }
380
381 return 0;
382}
383
547973de
LP
384int 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
1cc6c93a 412 *ret = TAKE_PTR(k);
547973de
LP
413
414 return 0;
415}
416
417int 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
433int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
434 bool found = false, other = false;
435 DnsResourceRecord *rr;
da6053d0 436 size_t i;
934e9b10
LP
437 int r;
438
547973de
LP
439 assert(a);
440 assert(key);
934e9b10 441
547973de 442 /* Remove all entries matching the specified key from *a */
934e9b10 443
547973de
LP
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;
934e9b10
LP
463 }
464
547973de
LP
465 if ((*a)->n_ref > 1) {
466 _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
105e1512 467 DnsAnswerFlags flags;
547973de
LP
468 int ifindex;
469
470 copy = dns_answer_new((*a)->n_rrs);
471 if (!copy)
472 return -ENOMEM;
473
105e1512 474 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
547973de 475 r = dns_resource_key_equal(rr->key, key);
934e9b10 476 if (r < 0)
547973de
LP
477 return r;
478 if (r > 0)
479 continue;
480
105e1512 481 r = dns_answer_add_raw(copy, rr, ifindex, flags);
547973de
LP
482 if (r < 0)
483 return r;
934e9b10 484 }
547973de
LP
485
486 dns_answer_unref(*a);
1cc6c93a 487 *a = TAKE_PTR(copy);
547973de
LP
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));
313cefa1 507 (*a)->n_rrs--;
547973de
LP
508 continue;
509
510 } else
511 /* Keep this entry */
512 i++;
934e9b10
LP
513 }
514
547973de
LP
515 return 1;
516}
517
0c857028
LP
518int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rm) {
519 bool found = false, other = false;
520 DnsResourceRecord *rr;
da6053d0 521 size_t i;
0c857028
LP
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);
1cc6c93a 572 *a = TAKE_PTR(copy);
0c857028
LP
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));
313cefa1 592 (*a)->n_rrs--;
0c857028
LP
593 continue;
594
595 } else
596 /* Keep this entry */
597 i++;
598 }
599
600 return 1;
601}
602
105e1512 603int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
547973de
LP
604 DnsResourceRecord *rr_source;
605 int ifindex_source, r;
105e1512 606 DnsAnswerFlags flags_source;
547973de
LP
607
608 assert(a);
609 assert(key);
610
611 /* Copy all RRs matching the specified key from source into *a */
612
105e1512 613 DNS_ANSWER_FOREACH_FULL(rr_source, ifindex_source, flags_source, source) {
547973de
LP
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;
934e9b10 625
105e1512 626 r = dns_answer_add(*a, rr_source, ifindex_source, flags_source|or_flags);
547973de
LP
627 if (r < 0)
628 return r;
629 }
630
631 return 0;
934e9b10 632}
af93291c 633
105e1512
LP
634int 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
af93291c 648void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
78c6a153 649 DnsAnswerItem *items;
da6053d0 650 size_t i, start, end;
78c6a153
LP
651
652 if (!a)
653 return;
af93291c
LP
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
78c6a153 664 items = newa(DnsAnswerItem, a->n_rrs);
af93291c
LP
665 for (i = 0; i < a->n_rrs; i++) {
666
78c6a153
LP
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)))
61233823 670 /* Order address records that are not preferred to the end of the array */
78c6a153 671 items[end--] = a->items[i];
af93291c
LP
672 else
673 /* Order all other records to the beginning of the array */
78c6a153 674 items[start++] = a->items[i];
af93291c
LP
675 }
676
677 assert(start == end+1);
78c6a153
LP
678 memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs);
679}
680
da6053d0 681int dns_answer_reserve(DnsAnswer **a, size_t n_free) {
78c6a153
LP
682 DnsAnswer *n;
683
2f763887
LP
684 assert(a);
685
78c6a153
LP
686 if (n_free <= 0)
687 return 0;
688
689 if (*a) {
da6053d0 690 size_t ns;
78c6a153
LP
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
2f763887
LP
700 /* Allocate more than we need */
701 ns *= 2;
702
78c6a153
LP
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;
af93291c 716}
547973de 717
da6053d0 718int dns_answer_reserve_or_clone(DnsAnswer **a, size_t n_free) {
547973de
LP
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
96d49011 725 * possible, since we are not the sole owner, then allocate a
547973de
LP
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);
1cc6c93a 745 *a = TAKE_PTR(n);
547973de
LP
746
747 return 0;
748}
26156910
LP
749
750void dns_answer_dump(DnsAnswer *answer, FILE *f) {
751 DnsResourceRecord *rr;
752 DnsAnswerFlags flags;
7b50eb2e 753 int ifindex;
26156910
LP
754
755 if (!f)
756 f = stdout;
757
758 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
7b50eb2e 759 const char *t;
26156910
LP
760
761 fputc('\t', f);
762
7b50eb2e
LP
763 t = dns_resource_record_to_string(rr);
764 if (!t) {
26156910
LP
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}
43e6779a 786
a5042ec4 787int dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname) {
43e6779a
LP
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
1c02e7ba 807 r = dns_name_change_suffix(cname->cname.name, rr->dname.name, dns_resource_key_name(rr->key), &n);
43e6779a
LP
808 if (r < 0)
809 return r;
810 if (r == 0)
811 continue;
812
1c02e7ba 813 r = dns_name_equal(n, dns_resource_key_name(cname->key));
43e6779a
LP
814 if (r < 0)
815 return r;
816 if (r > 0)
817 return 1;
43e6779a
LP
818 }
819
820 return 0;
821}