1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include "alloc-util.h"
23 #include "dns-domain.h"
24 #include "resolved-dns-answer.h"
25 #include "resolved-dns-dnssec.h"
26 #include "string-util.h"
28 DnsAnswer
*dns_answer_new(unsigned n
) {
31 a
= malloc0(offsetof(DnsAnswer
, items
) + sizeof(DnsAnswerItem
) * n
);
41 DnsAnswer
*dns_answer_ref(DnsAnswer
*a
) {
50 static void dns_answer_flush(DnsAnswer
*a
) {
51 DnsResourceRecord
*rr
;
56 DNS_ANSWER_FOREACH(rr
, a
)
57 dns_resource_record_unref(rr
);
62 DnsAnswer
*dns_answer_unref(DnsAnswer
*a
) {
77 static int dns_answer_add_raw(DnsAnswer
*a
, DnsResourceRecord
*rr
, int ifindex
, DnsAnswerFlags flags
) {
83 if (a
->n_rrs
>= a
->n_allocated
)
86 a
->items
[a
->n_rrs
++] = (DnsAnswerItem
) {
87 .rr
= dns_resource_record_ref(rr
),
95 static int dns_answer_add_raw_all(DnsAnswer
*a
, DnsAnswer
*source
) {
96 DnsResourceRecord
*rr
;
100 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, source
) {
101 r
= dns_answer_add_raw(a
, rr
, ifindex
, flags
);
109 int dns_answer_add(DnsAnswer
*a
, DnsResourceRecord
*rr
, int ifindex
, DnsAnswerFlags flags
) {
120 for (i
= 0; i
< a
->n_rrs
; i
++) {
121 if (a
->items
[i
].ifindex
!= ifindex
)
124 r
= dns_resource_record_equal(a
->items
[i
].rr
, rr
);
128 /* Don't mix contradicting TTLs (see below) */
129 if ((rr
->ttl
== 0) != (a
->items
[i
].rr
->ttl
== 0))
132 /* Entry already exists, keep the entry with
134 if (rr
->ttl
> a
->items
[i
].rr
->ttl
) {
135 dns_resource_record_ref(rr
);
136 dns_resource_record_unref(a
->items
[i
].rr
);
140 a
->items
[i
].flags
|= flags
;
144 r
= dns_resource_key_equal(a
->items
[i
].rr
->key
, rr
->key
);
148 /* There's already an RR of the same RRset in
149 * place! Let's see if the TTLs more or less
150 * match. We don't really care if they match
151 * precisely, but we do care whether one is 0
152 * and the other is not. See RFC 2181, Section
155 if ((rr
->ttl
== 0) != (a
->items
[i
].rr
->ttl
== 0))
160 return dns_answer_add_raw(a
, rr
, ifindex
, flags
);
163 static int dns_answer_add_all(DnsAnswer
*a
, DnsAnswer
*b
) {
164 DnsResourceRecord
*rr
;
165 DnsAnswerFlags flags
;
168 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, b
) {
169 r
= dns_answer_add(a
, rr
, ifindex
, flags
);
177 int dns_answer_add_extend(DnsAnswer
**a
, DnsResourceRecord
*rr
, int ifindex
, DnsAnswerFlags flags
) {
183 r
= dns_answer_reserve_or_clone(a
, 1);
187 return dns_answer_add(*a
, rr
, ifindex
, flags
);
190 int dns_answer_add_soa(DnsAnswer
*a
, const char *name
, uint32_t ttl
) {
191 _cleanup_(dns_resource_record_unrefp
) DnsResourceRecord
*soa
= NULL
;
193 soa
= dns_resource_record_new_full(DNS_CLASS_IN
, DNS_TYPE_SOA
, name
);
199 soa
->soa
.mname
= strdup(name
);
203 soa
->soa
.rname
= strappend("root.", name
);
208 soa
->soa
.refresh
= 1;
211 soa
->soa
.minimum
= ttl
;
213 return dns_answer_add(a
, soa
, 0, DNS_ANSWER_AUTHENTICATED
);
216 int dns_answer_match_key(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsAnswerFlags
*ret_flags
) {
217 DnsAnswerFlags flags
= 0, i_flags
;
218 DnsResourceRecord
*i
;
224 DNS_ANSWER_FOREACH_FLAGS(i
, i_flags
, a
) {
225 r
= dns_resource_key_match_rr(key
, i
, NULL
);
248 int dns_answer_contains_rr(DnsAnswer
*a
, DnsResourceRecord
*rr
, DnsAnswerFlags
*ret_flags
) {
249 DnsAnswerFlags flags
= 0, i_flags
;
250 DnsResourceRecord
*i
;
256 DNS_ANSWER_FOREACH_FLAGS(i
, i_flags
, a
) {
257 r
= dns_resource_record_equal(i
, rr
);
280 int dns_answer_contains_key(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsAnswerFlags
*ret_flags
) {
281 DnsAnswerFlags flags
= 0, i_flags
;
282 DnsResourceRecord
*i
;
288 DNS_ANSWER_FOREACH_FLAGS(i
, i_flags
, a
) {
289 r
= dns_resource_key_equal(i
->key
, key
);
312 int dns_answer_contains_nsec_or_nsec3(DnsAnswer
*a
) {
313 DnsResourceRecord
*i
;
315 DNS_ANSWER_FOREACH(i
, a
) {
316 if (IN_SET(i
->key
->type
, DNS_TYPE_NSEC
, DNS_TYPE_NSEC3
))
323 int dns_answer_contains_zone_nsec3(DnsAnswer
*answer
, const char *zone
) {
324 DnsResourceRecord
*rr
;
327 /* Checks whether the specified answer contains at least one NSEC3 RR in the specified zone */
329 DNS_ANSWER_FOREACH(rr
, answer
) {
332 if (rr
->key
->type
!= DNS_TYPE_NSEC3
)
335 p
= DNS_RESOURCE_KEY_NAME(rr
->key
);
336 r
= dns_name_parent(&p
);
342 r
= dns_name_equal(p
, zone
);
350 int dns_answer_find_soa(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsResourceRecord
**ret
, DnsAnswerFlags
*flags
) {
351 DnsResourceRecord
*rr
, *soa
= NULL
;
352 DnsAnswerFlags rr_flags
, soa_flags
= 0;
357 /* For a SOA record we can never find a matching SOA record */
358 if (key
->type
== DNS_TYPE_SOA
)
361 DNS_ANSWER_FOREACH_FLAGS(rr
, rr_flags
, a
) {
362 r
= dns_resource_key_match_soa(key
, rr
->key
);
368 r
= dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr
->key
), DNS_RESOURCE_KEY_NAME(soa
->key
));
376 soa_flags
= rr_flags
;
391 int dns_answer_find_cname_or_dname(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsResourceRecord
**ret
, DnsAnswerFlags
*flags
) {
392 DnsResourceRecord
*rr
;
393 DnsAnswerFlags rr_flags
;
398 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
399 if (!dns_type_may_redirect(key
->type
))
402 DNS_ANSWER_FOREACH_FLAGS(rr
, rr_flags
, a
) {
403 r
= dns_resource_key_match_cname_or_dname(key
, rr
->key
, NULL
);
418 int dns_answer_merge(DnsAnswer
*a
, DnsAnswer
*b
, DnsAnswer
**ret
) {
419 _cleanup_(dns_answer_unrefp
) DnsAnswer
*k
= NULL
;
424 if (dns_answer_size(a
) <= 0) {
425 *ret
= dns_answer_ref(b
);
429 if (dns_answer_size(b
) <= 0) {
430 *ret
= dns_answer_ref(a
);
434 k
= dns_answer_new(a
->n_rrs
+ b
->n_rrs
);
438 r
= dns_answer_add_raw_all(k
, a
);
442 r
= dns_answer_add_all(k
, b
);
452 int dns_answer_extend(DnsAnswer
**a
, DnsAnswer
*b
) {
458 r
= dns_answer_merge(*a
, b
, &merged
);
462 dns_answer_unref(*a
);
468 int dns_answer_remove_by_key(DnsAnswer
**a
, const DnsResourceKey
*key
) {
469 bool found
= false, other
= false;
470 DnsResourceRecord
*rr
;
477 /* Remove all entries matching the specified key from *a */
479 DNS_ANSWER_FOREACH(rr
, *a
) {
480 r
= dns_resource_key_equal(rr
->key
, key
);
496 *a
= dns_answer_unref(*a
); /* Return NULL for the empty answer */
500 if ((*a
)->n_ref
> 1) {
501 _cleanup_(dns_answer_unrefp
) DnsAnswer
*copy
= NULL
;
502 DnsAnswerFlags flags
;
505 copy
= dns_answer_new((*a
)->n_rrs
);
509 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, *a
) {
510 r
= dns_resource_key_equal(rr
->key
, key
);
516 r
= dns_answer_add_raw(copy
, rr
, ifindex
, flags
);
521 dns_answer_unref(*a
);
528 /* Only a single reference, edit in-place */
532 if (i
>= (*a
)->n_rrs
)
535 r
= dns_resource_key_equal((*a
)->items
[i
].rr
->key
, key
);
539 /* Kill this entry */
541 dns_resource_record_unref((*a
)->items
[i
].rr
);
542 memmove((*a
)->items
+ i
, (*a
)->items
+ i
+ 1, sizeof(DnsAnswerItem
) * ((*a
)->n_rrs
- i
- 1));
547 /* Keep this entry */
554 int dns_answer_remove_by_rr(DnsAnswer
**a
, DnsResourceRecord
*rm
) {
555 bool found
= false, other
= false;
556 DnsResourceRecord
*rr
;
563 /* Remove all entries matching the specified RR from *a */
565 DNS_ANSWER_FOREACH(rr
, *a
) {
566 r
= dns_resource_record_equal(rr
, rm
);
582 *a
= dns_answer_unref(*a
); /* Return NULL for the empty answer */
586 if ((*a
)->n_ref
> 1) {
587 _cleanup_(dns_answer_unrefp
) DnsAnswer
*copy
= NULL
;
588 DnsAnswerFlags flags
;
591 copy
= dns_answer_new((*a
)->n_rrs
);
595 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, *a
) {
596 r
= dns_resource_record_equal(rr
, rm
);
602 r
= dns_answer_add_raw(copy
, rr
, ifindex
, flags
);
607 dns_answer_unref(*a
);
614 /* Only a single reference, edit in-place */
618 if (i
>= (*a
)->n_rrs
)
621 r
= dns_resource_record_equal((*a
)->items
[i
].rr
, rm
);
625 /* Kill this entry */
627 dns_resource_record_unref((*a
)->items
[i
].rr
);
628 memmove((*a
)->items
+ i
, (*a
)->items
+ i
+ 1, sizeof(DnsAnswerItem
) * ((*a
)->n_rrs
- i
- 1));
633 /* Keep this entry */
640 int dns_answer_copy_by_key(DnsAnswer
**a
, DnsAnswer
*source
, const DnsResourceKey
*key
, DnsAnswerFlags or_flags
) {
641 DnsResourceRecord
*rr_source
;
642 int ifindex_source
, r
;
643 DnsAnswerFlags flags_source
;
648 /* Copy all RRs matching the specified key from source into *a */
650 DNS_ANSWER_FOREACH_FULL(rr_source
, ifindex_source
, flags_source
, source
) {
652 r
= dns_resource_key_equal(rr_source
->key
, key
);
658 /* Make space for at least one entry */
659 r
= dns_answer_reserve_or_clone(a
, 1);
663 r
= dns_answer_add(*a
, rr_source
, ifindex_source
, flags_source
|or_flags
);
671 int dns_answer_move_by_key(DnsAnswer
**to
, DnsAnswer
**from
, const DnsResourceKey
*key
, DnsAnswerFlags or_flags
) {
678 r
= dns_answer_copy_by_key(to
, *from
, key
, or_flags
);
682 return dns_answer_remove_by_key(from
, key
);
685 void dns_answer_order_by_scope(DnsAnswer
*a
, bool prefer_link_local
) {
686 DnsAnswerItem
*items
;
687 unsigned i
, start
, end
;
698 /* RFC 4795, Section 2.6 suggests we should order entries
699 * depending on whether the sender is a link-local address. */
701 items
= newa(DnsAnswerItem
, a
->n_rrs
);
702 for (i
= 0; i
< a
->n_rrs
; i
++) {
704 if (a
->items
[i
].rr
->key
->class == DNS_CLASS_IN
&&
705 ((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
) ||
706 (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
)))
707 /* Order address records that are are not preferred to the end of the array */
708 items
[end
--] = a
->items
[i
];
710 /* Order all other records to the beginning of the array */
711 items
[start
++] = a
->items
[i
];
714 assert(start
== end
+1);
715 memcpy(a
->items
, items
, sizeof(DnsAnswerItem
) * a
->n_rrs
);
718 int dns_answer_reserve(DnsAnswer
**a
, unsigned n_free
) {
732 ns
= (*a
)->n_rrs
+ n_free
;
734 if ((*a
)->n_allocated
>= ns
)
737 /* Allocate more than we need */
740 n
= realloc(*a
, offsetof(DnsAnswer
, items
) + sizeof(DnsAnswerItem
) * ns
);
746 n
= dns_answer_new(n_free
);
755 int dns_answer_reserve_or_clone(DnsAnswer
**a
, unsigned n_free
) {
756 _cleanup_(dns_answer_unrefp
) DnsAnswer
*n
= NULL
;
761 /* Tries to extend the DnsAnswer object. And if that's not
762 * possibly, since we are not the sole owner, then allocate a
763 * new, appropriately sized one. Either way, after this call
764 * the object will only have a single reference, and has room
765 * for at least the specified number of RRs. */
767 r
= dns_answer_reserve(a
, n_free
);
773 n
= dns_answer_new(((*a
)->n_rrs
+ n_free
) * 2);
777 r
= dns_answer_add_raw_all(n
, *a
);
781 dns_answer_unref(*a
);
788 void dns_answer_dump(DnsAnswer
*answer
, FILE *f
) {
789 DnsResourceRecord
*rr
;
790 DnsAnswerFlags flags
;
796 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, answer
) {
801 t
= dns_resource_record_to_string(rr
);
809 if (ifindex
!= 0 || flags
& (DNS_ANSWER_AUTHENTICATED
|DNS_ANSWER_CACHEABLE
|DNS_ANSWER_SHARED_OWNER
))
813 printf(" ifindex=%i", ifindex
);
814 if (flags
& DNS_ANSWER_AUTHENTICATED
)
815 fputs(" authenticated", f
);
816 if (flags
& DNS_ANSWER_CACHEABLE
)
817 fputs(" cachable", f
);
818 if (flags
& DNS_ANSWER_SHARED_OWNER
)
819 fputs(" shared-owner", f
);
825 bool dns_answer_has_dname_for_cname(DnsAnswer
*a
, DnsResourceRecord
*cname
) {
826 DnsResourceRecord
*rr
;
831 /* Checks whether the answer contains a DNAME record that indicates that the specified CNAME record is
832 * synthesized from it */
834 if (cname
->key
->type
!= DNS_TYPE_CNAME
)
837 DNS_ANSWER_FOREACH(rr
, a
) {
838 _cleanup_free_
char *n
= NULL
;
840 if (rr
->key
->type
!= DNS_TYPE_DNAME
)
842 if (rr
->key
->class != cname
->key
->class)
845 r
= dns_name_change_suffix(cname
->cname
.name
, rr
->dname
.name
, DNS_RESOURCE_KEY_NAME(rr
->key
), &n
);
851 r
= dns_name_equal(n
, DNS_RESOURCE_KEY_NAME(cname
->key
));