1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2014 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include "alloc-util.h"
22 #include "dns-domain.h"
23 #include "resolved-dns-answer.h"
24 #include "resolved-dns-dnssec.h"
25 #include "string-util.h"
27 DnsAnswer
*dns_answer_new(unsigned n
) {
30 a
= malloc0(offsetof(DnsAnswer
, items
) + sizeof(DnsAnswerItem
) * n
);
40 DnsAnswer
*dns_answer_ref(DnsAnswer
*a
) {
49 static void dns_answer_flush(DnsAnswer
*a
) {
50 DnsResourceRecord
*rr
;
55 DNS_ANSWER_FOREACH(rr
, a
)
56 dns_resource_record_unref(rr
);
61 DnsAnswer
*dns_answer_unref(DnsAnswer
*a
) {
76 static int dns_answer_add_raw(DnsAnswer
*a
, DnsResourceRecord
*rr
, int ifindex
, DnsAnswerFlags flags
) {
82 if (a
->n_rrs
>= a
->n_allocated
)
85 a
->items
[a
->n_rrs
++] = (DnsAnswerItem
) {
86 .rr
= dns_resource_record_ref(rr
),
94 static int dns_answer_add_raw_all(DnsAnswer
*a
, DnsAnswer
*source
) {
95 DnsResourceRecord
*rr
;
99 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, source
) {
100 r
= dns_answer_add_raw(a
, rr
, ifindex
, flags
);
108 int dns_answer_add(DnsAnswer
*a
, DnsResourceRecord
*rr
, int ifindex
, DnsAnswerFlags flags
) {
119 for (i
= 0; i
< a
->n_rrs
; i
++) {
120 if (a
->items
[i
].ifindex
!= ifindex
)
123 r
= dns_resource_record_equal(a
->items
[i
].rr
, rr
);
127 /* Don't mix contradicting TTLs (see below) */
128 if ((rr
->ttl
== 0) != (a
->items
[i
].rr
->ttl
== 0))
131 /* Entry already exists, keep the entry with
133 if (rr
->ttl
> a
->items
[i
].rr
->ttl
) {
134 dns_resource_record_ref(rr
);
135 dns_resource_record_unref(a
->items
[i
].rr
);
139 a
->items
[i
].flags
|= flags
;
143 r
= dns_resource_key_equal(a
->items
[i
].rr
->key
, rr
->key
);
147 /* There's already an RR of the same RRset in
148 * place! Let's see if the TTLs more or less
149 * match. We don't really care if they match
150 * precisely, but we do care whether one is 0
151 * and the other is not. See RFC 2181, Section
154 if ((rr
->ttl
== 0) != (a
->items
[i
].rr
->ttl
== 0))
159 return dns_answer_add_raw(a
, rr
, ifindex
, flags
);
162 static int dns_answer_add_all(DnsAnswer
*a
, DnsAnswer
*b
) {
163 DnsResourceRecord
*rr
;
164 DnsAnswerFlags flags
;
167 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, b
) {
168 r
= dns_answer_add(a
, rr
, ifindex
, flags
);
176 int dns_answer_add_extend(DnsAnswer
**a
, DnsResourceRecord
*rr
, int ifindex
, DnsAnswerFlags flags
) {
182 r
= dns_answer_reserve_or_clone(a
, 1);
186 return dns_answer_add(*a
, rr
, ifindex
, flags
);
189 int dns_answer_add_soa(DnsAnswer
*a
, const char *name
, uint32_t ttl
, int ifindex
) {
190 _cleanup_(dns_resource_record_unrefp
) DnsResourceRecord
*soa
= NULL
;
192 soa
= dns_resource_record_new_full(DNS_CLASS_IN
, DNS_TYPE_SOA
, name
);
198 soa
->soa
.mname
= strdup(name
);
202 soa
->soa
.rname
= strappend("root.", name
);
207 soa
->soa
.refresh
= 1;
210 soa
->soa
.minimum
= ttl
;
212 return dns_answer_add(a
, soa
, ifindex
, DNS_ANSWER_AUTHENTICATED
);
215 int dns_answer_match_key(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsAnswerFlags
*ret_flags
) {
216 DnsAnswerFlags flags
= 0, i_flags
;
217 DnsResourceRecord
*i
;
223 DNS_ANSWER_FOREACH_FLAGS(i
, i_flags
, a
) {
224 r
= dns_resource_key_match_rr(key
, i
, NULL
);
247 int dns_answer_contains_rr(DnsAnswer
*a
, DnsResourceRecord
*rr
, DnsAnswerFlags
*ret_flags
) {
248 DnsAnswerFlags flags
= 0, i_flags
;
249 DnsResourceRecord
*i
;
255 DNS_ANSWER_FOREACH_FLAGS(i
, i_flags
, a
) {
256 r
= dns_resource_record_equal(i
, rr
);
279 int dns_answer_contains_key(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsAnswerFlags
*ret_flags
) {
280 DnsAnswerFlags flags
= 0, i_flags
;
281 DnsResourceRecord
*i
;
287 DNS_ANSWER_FOREACH_FLAGS(i
, i_flags
, a
) {
288 r
= dns_resource_key_equal(i
->key
, key
);
311 int dns_answer_contains_nsec_or_nsec3(DnsAnswer
*a
) {
312 DnsResourceRecord
*i
;
314 DNS_ANSWER_FOREACH(i
, a
) {
315 if (IN_SET(i
->key
->type
, DNS_TYPE_NSEC
, DNS_TYPE_NSEC3
))
322 int dns_answer_contains_zone_nsec3(DnsAnswer
*answer
, const char *zone
) {
323 DnsResourceRecord
*rr
;
326 /* Checks whether the specified answer contains at least one NSEC3 RR in the specified zone */
328 DNS_ANSWER_FOREACH(rr
, answer
) {
331 if (rr
->key
->type
!= DNS_TYPE_NSEC3
)
334 p
= dns_resource_key_name(rr
->key
);
335 r
= dns_name_parent(&p
);
341 r
= dns_name_equal(p
, zone
);
349 int dns_answer_find_soa(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsResourceRecord
**ret
, DnsAnswerFlags
*flags
) {
350 DnsResourceRecord
*rr
, *soa
= NULL
;
351 DnsAnswerFlags rr_flags
, soa_flags
= 0;
356 /* For a SOA record we can never find a matching SOA record */
357 if (key
->type
== DNS_TYPE_SOA
)
360 DNS_ANSWER_FOREACH_FLAGS(rr
, rr_flags
, a
) {
361 r
= dns_resource_key_match_soa(key
, rr
->key
);
367 r
= dns_name_endswith(dns_resource_key_name(rr
->key
), dns_resource_key_name(soa
->key
));
375 soa_flags
= rr_flags
;
390 int dns_answer_find_cname_or_dname(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsResourceRecord
**ret
, DnsAnswerFlags
*flags
) {
391 DnsResourceRecord
*rr
;
392 DnsAnswerFlags rr_flags
;
397 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
398 if (!dns_type_may_redirect(key
->type
))
401 DNS_ANSWER_FOREACH_FLAGS(rr
, rr_flags
, a
) {
402 r
= dns_resource_key_match_cname_or_dname(key
, rr
->key
, NULL
);
417 int dns_answer_merge(DnsAnswer
*a
, DnsAnswer
*b
, DnsAnswer
**ret
) {
418 _cleanup_(dns_answer_unrefp
) DnsAnswer
*k
= NULL
;
423 if (dns_answer_size(a
) <= 0) {
424 *ret
= dns_answer_ref(b
);
428 if (dns_answer_size(b
) <= 0) {
429 *ret
= dns_answer_ref(a
);
433 k
= dns_answer_new(a
->n_rrs
+ b
->n_rrs
);
437 r
= dns_answer_add_raw_all(k
, a
);
441 r
= dns_answer_add_all(k
, b
);
451 int dns_answer_extend(DnsAnswer
**a
, DnsAnswer
*b
) {
457 r
= dns_answer_merge(*a
, b
, &merged
);
461 dns_answer_unref(*a
);
467 int dns_answer_remove_by_key(DnsAnswer
**a
, const DnsResourceKey
*key
) {
468 bool found
= false, other
= false;
469 DnsResourceRecord
*rr
;
476 /* Remove all entries matching the specified key from *a */
478 DNS_ANSWER_FOREACH(rr
, *a
) {
479 r
= dns_resource_key_equal(rr
->key
, key
);
495 *a
= dns_answer_unref(*a
); /* Return NULL for the empty answer */
499 if ((*a
)->n_ref
> 1) {
500 _cleanup_(dns_answer_unrefp
) DnsAnswer
*copy
= NULL
;
501 DnsAnswerFlags flags
;
504 copy
= dns_answer_new((*a
)->n_rrs
);
508 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, *a
) {
509 r
= dns_resource_key_equal(rr
->key
, key
);
515 r
= dns_answer_add_raw(copy
, rr
, ifindex
, flags
);
520 dns_answer_unref(*a
);
527 /* Only a single reference, edit in-place */
531 if (i
>= (*a
)->n_rrs
)
534 r
= dns_resource_key_equal((*a
)->items
[i
].rr
->key
, key
);
538 /* Kill this entry */
540 dns_resource_record_unref((*a
)->items
[i
].rr
);
541 memmove((*a
)->items
+ i
, (*a
)->items
+ i
+ 1, sizeof(DnsAnswerItem
) * ((*a
)->n_rrs
- i
- 1));
546 /* Keep this entry */
553 int dns_answer_remove_by_rr(DnsAnswer
**a
, DnsResourceRecord
*rm
) {
554 bool found
= false, other
= false;
555 DnsResourceRecord
*rr
;
562 /* Remove all entries matching the specified RR from *a */
564 DNS_ANSWER_FOREACH(rr
, *a
) {
565 r
= dns_resource_record_equal(rr
, rm
);
581 *a
= dns_answer_unref(*a
); /* Return NULL for the empty answer */
585 if ((*a
)->n_ref
> 1) {
586 _cleanup_(dns_answer_unrefp
) DnsAnswer
*copy
= NULL
;
587 DnsAnswerFlags flags
;
590 copy
= dns_answer_new((*a
)->n_rrs
);
594 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, *a
) {
595 r
= dns_resource_record_equal(rr
, rm
);
601 r
= dns_answer_add_raw(copy
, rr
, ifindex
, flags
);
606 dns_answer_unref(*a
);
613 /* Only a single reference, edit in-place */
617 if (i
>= (*a
)->n_rrs
)
620 r
= dns_resource_record_equal((*a
)->items
[i
].rr
, rm
);
624 /* Kill this entry */
626 dns_resource_record_unref((*a
)->items
[i
].rr
);
627 memmove((*a
)->items
+ i
, (*a
)->items
+ i
+ 1, sizeof(DnsAnswerItem
) * ((*a
)->n_rrs
- i
- 1));
632 /* Keep this entry */
639 int dns_answer_copy_by_key(DnsAnswer
**a
, DnsAnswer
*source
, const DnsResourceKey
*key
, DnsAnswerFlags or_flags
) {
640 DnsResourceRecord
*rr_source
;
641 int ifindex_source
, r
;
642 DnsAnswerFlags flags_source
;
647 /* Copy all RRs matching the specified key from source into *a */
649 DNS_ANSWER_FOREACH_FULL(rr_source
, ifindex_source
, flags_source
, source
) {
651 r
= dns_resource_key_equal(rr_source
->key
, key
);
657 /* Make space for at least one entry */
658 r
= dns_answer_reserve_or_clone(a
, 1);
662 r
= dns_answer_add(*a
, rr_source
, ifindex_source
, flags_source
|or_flags
);
670 int dns_answer_move_by_key(DnsAnswer
**to
, DnsAnswer
**from
, const DnsResourceKey
*key
, DnsAnswerFlags or_flags
) {
677 r
= dns_answer_copy_by_key(to
, *from
, key
, or_flags
);
681 return dns_answer_remove_by_key(from
, key
);
684 void dns_answer_order_by_scope(DnsAnswer
*a
, bool prefer_link_local
) {
685 DnsAnswerItem
*items
;
686 unsigned i
, start
, end
;
697 /* RFC 4795, Section 2.6 suggests we should order entries
698 * depending on whether the sender is a link-local address. */
700 items
= newa(DnsAnswerItem
, a
->n_rrs
);
701 for (i
= 0; i
< a
->n_rrs
; i
++) {
703 if (a
->items
[i
].rr
->key
->class == DNS_CLASS_IN
&&
704 ((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
) ||
705 (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
)))
706 /* Order address records that are not preferred to the end of the array */
707 items
[end
--] = a
->items
[i
];
709 /* Order all other records to the beginning of the array */
710 items
[start
++] = a
->items
[i
];
713 assert(start
== end
+1);
714 memcpy(a
->items
, items
, sizeof(DnsAnswerItem
) * a
->n_rrs
);
717 int dns_answer_reserve(DnsAnswer
**a
, unsigned n_free
) {
731 ns
= (*a
)->n_rrs
+ n_free
;
733 if ((*a
)->n_allocated
>= ns
)
736 /* Allocate more than we need */
739 n
= realloc(*a
, offsetof(DnsAnswer
, items
) + sizeof(DnsAnswerItem
) * ns
);
745 n
= dns_answer_new(n_free
);
754 int dns_answer_reserve_or_clone(DnsAnswer
**a
, unsigned n_free
) {
755 _cleanup_(dns_answer_unrefp
) DnsAnswer
*n
= NULL
;
760 /* Tries to extend the DnsAnswer object. And if that's not
761 * possible, since we are not the sole owner, then allocate a
762 * new, appropriately sized one. Either way, after this call
763 * the object will only have a single reference, and has room
764 * for at least the specified number of RRs. */
766 r
= dns_answer_reserve(a
, n_free
);
772 n
= dns_answer_new(((*a
)->n_rrs
+ n_free
) * 2);
776 r
= dns_answer_add_raw_all(n
, *a
);
780 dns_answer_unref(*a
);
787 void dns_answer_dump(DnsAnswer
*answer
, FILE *f
) {
788 DnsResourceRecord
*rr
;
789 DnsAnswerFlags flags
;
795 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, answer
) {
800 t
= dns_resource_record_to_string(rr
);
808 if (ifindex
!= 0 || flags
& (DNS_ANSWER_AUTHENTICATED
|DNS_ANSWER_CACHEABLE
|DNS_ANSWER_SHARED_OWNER
))
812 printf(" ifindex=%i", ifindex
);
813 if (flags
& DNS_ANSWER_AUTHENTICATED
)
814 fputs(" authenticated", f
);
815 if (flags
& DNS_ANSWER_CACHEABLE
)
816 fputs(" cachable", f
);
817 if (flags
& DNS_ANSWER_SHARED_OWNER
)
818 fputs(" shared-owner", f
);
824 bool dns_answer_has_dname_for_cname(DnsAnswer
*a
, DnsResourceRecord
*cname
) {
825 DnsResourceRecord
*rr
;
830 /* Checks whether the answer contains a DNAME record that indicates that the specified CNAME record is
831 * synthesized from it */
833 if (cname
->key
->type
!= DNS_TYPE_CNAME
)
836 DNS_ANSWER_FOREACH(rr
, a
) {
837 _cleanup_free_
char *n
= NULL
;
839 if (rr
->key
->type
!= DNS_TYPE_DNAME
)
841 if (rr
->key
->class != cname
->key
->class)
844 r
= dns_name_change_suffix(cname
->cname
.name
, rr
->dname
.name
, dns_resource_key_name(rr
->key
), &n
);
850 r
= dns_name_equal(n
, dns_resource_key_name(cname
->key
));