2 This file is part of systemd.
4 Copyright 2014 Lennart Poettering
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.
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.
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/>.
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"
26 DnsAnswer
*dns_answer_new(unsigned n
) {
29 a
= malloc0(offsetof(DnsAnswer
, items
) + sizeof(DnsAnswerItem
) * n
);
39 DnsAnswer
*dns_answer_ref(DnsAnswer
*a
) {
48 static void dns_answer_flush(DnsAnswer
*a
) {
49 DnsResourceRecord
*rr
;
54 DNS_ANSWER_FOREACH(rr
, a
)
55 dns_resource_record_unref(rr
);
60 DnsAnswer
*dns_answer_unref(DnsAnswer
*a
) {
75 static int dns_answer_add_raw(DnsAnswer
*a
, DnsResourceRecord
*rr
, int ifindex
, DnsAnswerFlags flags
) {
81 if (a
->n_rrs
>= a
->n_allocated
)
84 a
->items
[a
->n_rrs
++] = (DnsAnswerItem
) {
85 .rr
= dns_resource_record_ref(rr
),
93 static int dns_answer_add_raw_all(DnsAnswer
*a
, DnsAnswer
*source
) {
94 DnsResourceRecord
*rr
;
98 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, source
) {
99 r
= dns_answer_add_raw(a
, rr
, ifindex
, flags
);
107 int dns_answer_add(DnsAnswer
*a
, DnsResourceRecord
*rr
, int ifindex
, DnsAnswerFlags flags
) {
118 for (i
= 0; i
< a
->n_rrs
; i
++) {
119 if (a
->items
[i
].ifindex
!= ifindex
)
122 r
= dns_resource_record_equal(a
->items
[i
].rr
, rr
);
126 /* Don't mix contradicting TTLs (see below) */
127 if ((rr
->ttl
== 0) != (a
->items
[i
].rr
->ttl
== 0))
130 /* Entry already exists, keep the entry with
132 if (rr
->ttl
> a
->items
[i
].rr
->ttl
) {
133 dns_resource_record_ref(rr
);
134 dns_resource_record_unref(a
->items
[i
].rr
);
138 a
->items
[i
].flags
|= flags
;
142 r
= dns_resource_key_equal(a
->items
[i
].rr
->key
, rr
->key
);
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
153 if ((rr
->ttl
== 0) != (a
->items
[i
].rr
->ttl
== 0))
158 return dns_answer_add_raw(a
, rr
, ifindex
, flags
);
161 static int dns_answer_add_all(DnsAnswer
*a
, DnsAnswer
*b
) {
162 DnsResourceRecord
*rr
;
163 DnsAnswerFlags flags
;
166 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, b
) {
167 r
= dns_answer_add(a
, rr
, ifindex
, flags
);
175 int dns_answer_add_extend(DnsAnswer
**a
, DnsResourceRecord
*rr
, int ifindex
, DnsAnswerFlags flags
) {
181 r
= dns_answer_reserve_or_clone(a
, 1);
185 return dns_answer_add(*a
, rr
, ifindex
, flags
);
188 int dns_answer_add_soa(DnsAnswer
*a
, const char *name
, uint32_t ttl
) {
189 _cleanup_(dns_resource_record_unrefp
) DnsResourceRecord
*soa
= NULL
;
191 soa
= dns_resource_record_new_full(DNS_CLASS_IN
, DNS_TYPE_SOA
, name
);
197 soa
->soa
.mname
= strdup(name
);
201 soa
->soa
.rname
= strappend("root.", name
);
206 soa
->soa
.refresh
= 1;
209 soa
->soa
.minimum
= ttl
;
211 return dns_answer_add(a
, soa
, 0, DNS_ANSWER_AUTHENTICATED
);
214 int dns_answer_match_key(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsAnswerFlags
*ret_flags
) {
215 DnsAnswerFlags flags
= 0, i_flags
;
216 DnsResourceRecord
*i
;
222 DNS_ANSWER_FOREACH_FLAGS(i
, i_flags
, a
) {
223 r
= dns_resource_key_match_rr(key
, i
, NULL
);
246 int dns_answer_contains_rr(DnsAnswer
*a
, DnsResourceRecord
*rr
, DnsAnswerFlags
*ret_flags
) {
247 DnsAnswerFlags flags
= 0, i_flags
;
248 DnsResourceRecord
*i
;
254 DNS_ANSWER_FOREACH_FLAGS(i
, i_flags
, a
) {
255 r
= dns_resource_record_equal(i
, rr
);
278 int dns_answer_contains_key(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsAnswerFlags
*ret_flags
) {
279 DnsAnswerFlags flags
= 0, i_flags
;
280 DnsResourceRecord
*i
;
286 DNS_ANSWER_FOREACH_FLAGS(i
, i_flags
, a
) {
287 r
= dns_resource_key_equal(i
->key
, key
);
310 int dns_answer_contains_nsec_or_nsec3(DnsAnswer
*a
) {
311 DnsResourceRecord
*i
;
313 DNS_ANSWER_FOREACH(i
, a
) {
314 if (IN_SET(i
->key
->type
, DNS_TYPE_NSEC
, DNS_TYPE_NSEC3
))
321 int dns_answer_contains_zone_nsec3(DnsAnswer
*answer
, const char *zone
) {
322 DnsResourceRecord
*rr
;
325 /* Checks whether the specified answer contains at least one NSEC3 RR in the specified zone */
327 DNS_ANSWER_FOREACH(rr
, answer
) {
330 if (rr
->key
->type
!= DNS_TYPE_NSEC3
)
333 p
= dns_resource_key_name(rr
->key
);
334 r
= dns_name_parent(&p
);
340 r
= dns_name_equal(p
, zone
);
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;
355 /* For a SOA record we can never find a matching SOA record */
356 if (key
->type
== DNS_TYPE_SOA
)
359 DNS_ANSWER_FOREACH_FLAGS(rr
, rr_flags
, a
) {
360 r
= dns_resource_key_match_soa(key
, rr
->key
);
366 r
= dns_name_endswith(dns_resource_key_name(rr
->key
), dns_resource_key_name(soa
->key
));
374 soa_flags
= rr_flags
;
389 int dns_answer_find_cname_or_dname(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsResourceRecord
**ret
, DnsAnswerFlags
*flags
) {
390 DnsResourceRecord
*rr
;
391 DnsAnswerFlags rr_flags
;
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
))
400 DNS_ANSWER_FOREACH_FLAGS(rr
, rr_flags
, a
) {
401 r
= dns_resource_key_match_cname_or_dname(key
, rr
->key
, NULL
);
416 int dns_answer_merge(DnsAnswer
*a
, DnsAnswer
*b
, DnsAnswer
**ret
) {
417 _cleanup_(dns_answer_unrefp
) DnsAnswer
*k
= NULL
;
422 if (dns_answer_size(a
) <= 0) {
423 *ret
= dns_answer_ref(b
);
427 if (dns_answer_size(b
) <= 0) {
428 *ret
= dns_answer_ref(a
);
432 k
= dns_answer_new(a
->n_rrs
+ b
->n_rrs
);
436 r
= dns_answer_add_raw_all(k
, a
);
440 r
= dns_answer_add_all(k
, b
);
450 int dns_answer_extend(DnsAnswer
**a
, DnsAnswer
*b
) {
456 r
= dns_answer_merge(*a
, b
, &merged
);
460 dns_answer_unref(*a
);
466 int dns_answer_remove_by_key(DnsAnswer
**a
, const DnsResourceKey
*key
) {
467 bool found
= false, other
= false;
468 DnsResourceRecord
*rr
;
475 /* Remove all entries matching the specified key from *a */
477 DNS_ANSWER_FOREACH(rr
, *a
) {
478 r
= dns_resource_key_equal(rr
->key
, key
);
494 *a
= dns_answer_unref(*a
); /* Return NULL for the empty answer */
498 if ((*a
)->n_ref
> 1) {
499 _cleanup_(dns_answer_unrefp
) DnsAnswer
*copy
= NULL
;
500 DnsAnswerFlags flags
;
503 copy
= dns_answer_new((*a
)->n_rrs
);
507 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, *a
) {
508 r
= dns_resource_key_equal(rr
->key
, key
);
514 r
= dns_answer_add_raw(copy
, rr
, ifindex
, flags
);
519 dns_answer_unref(*a
);
526 /* Only a single reference, edit in-place */
530 if (i
>= (*a
)->n_rrs
)
533 r
= dns_resource_key_equal((*a
)->items
[i
].rr
->key
, key
);
537 /* Kill this entry */
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));
545 /* Keep this entry */
552 int dns_answer_remove_by_rr(DnsAnswer
**a
, DnsResourceRecord
*rm
) {
553 bool found
= false, other
= false;
554 DnsResourceRecord
*rr
;
561 /* Remove all entries matching the specified RR from *a */
563 DNS_ANSWER_FOREACH(rr
, *a
) {
564 r
= dns_resource_record_equal(rr
, rm
);
580 *a
= dns_answer_unref(*a
); /* Return NULL for the empty answer */
584 if ((*a
)->n_ref
> 1) {
585 _cleanup_(dns_answer_unrefp
) DnsAnswer
*copy
= NULL
;
586 DnsAnswerFlags flags
;
589 copy
= dns_answer_new((*a
)->n_rrs
);
593 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, *a
) {
594 r
= dns_resource_record_equal(rr
, rm
);
600 r
= dns_answer_add_raw(copy
, rr
, ifindex
, flags
);
605 dns_answer_unref(*a
);
612 /* Only a single reference, edit in-place */
616 if (i
>= (*a
)->n_rrs
)
619 r
= dns_resource_record_equal((*a
)->items
[i
].rr
, rm
);
623 /* Kill this entry */
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));
631 /* Keep this entry */
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
;
646 /* Copy all RRs matching the specified key from source into *a */
648 DNS_ANSWER_FOREACH_FULL(rr_source
, ifindex_source
, flags_source
, source
) {
650 r
= dns_resource_key_equal(rr_source
->key
, key
);
656 /* Make space for at least one entry */
657 r
= dns_answer_reserve_or_clone(a
, 1);
661 r
= dns_answer_add(*a
, rr_source
, ifindex_source
, flags_source
|or_flags
);
669 int dns_answer_move_by_key(DnsAnswer
**to
, DnsAnswer
**from
, const DnsResourceKey
*key
, DnsAnswerFlags or_flags
) {
676 r
= dns_answer_copy_by_key(to
, *from
, key
, or_flags
);
680 return dns_answer_remove_by_key(from
, key
);
683 void dns_answer_order_by_scope(DnsAnswer
*a
, bool prefer_link_local
) {
684 DnsAnswerItem
*items
;
685 unsigned i
, start
, end
;
696 /* RFC 4795, Section 2.6 suggests we should order entries
697 * depending on whether the sender is a link-local address. */
699 items
= newa(DnsAnswerItem
, a
->n_rrs
);
700 for (i
= 0; i
< a
->n_rrs
; i
++) {
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
];
708 /* Order all other records to the beginning of the array */
709 items
[start
++] = a
->items
[i
];
712 assert(start
== end
+1);
713 memcpy(a
->items
, items
, sizeof(DnsAnswerItem
) * a
->n_rrs
);
716 int dns_answer_reserve(DnsAnswer
**a
, unsigned n_free
) {
730 ns
= (*a
)->n_rrs
+ n_free
;
732 if ((*a
)->n_allocated
>= ns
)
735 /* Allocate more than we need */
738 n
= realloc(*a
, offsetof(DnsAnswer
, items
) + sizeof(DnsAnswerItem
) * ns
);
744 n
= dns_answer_new(n_free
);
753 int dns_answer_reserve_or_clone(DnsAnswer
**a
, unsigned n_free
) {
754 _cleanup_(dns_answer_unrefp
) DnsAnswer
*n
= NULL
;
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. */
765 r
= dns_answer_reserve(a
, n_free
);
771 n
= dns_answer_new(((*a
)->n_rrs
+ n_free
) * 2);
775 r
= dns_answer_add_raw_all(n
, *a
);
779 dns_answer_unref(*a
);
786 void dns_answer_dump(DnsAnswer
*answer
, FILE *f
) {
787 DnsResourceRecord
*rr
;
788 DnsAnswerFlags flags
;
794 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, answer
) {
799 t
= dns_resource_record_to_string(rr
);
807 if (ifindex
!= 0 || flags
& (DNS_ANSWER_AUTHENTICATED
|DNS_ANSWER_CACHEABLE
|DNS_ANSWER_SHARED_OWNER
))
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
);
823 bool dns_answer_has_dname_for_cname(DnsAnswer
*a
, DnsResourceRecord
*cname
) {
824 DnsResourceRecord
*rr
;
829 /* Checks whether the answer contains a DNAME record that indicates that the specified CNAME record is
830 * synthesized from it */
832 if (cname
->key
->type
!= DNS_TYPE_CNAME
)
835 DNS_ANSWER_FOREACH(rr
, a
) {
836 _cleanup_free_
char *n
= NULL
;
838 if (rr
->key
->type
!= DNS_TYPE_DNAME
)
840 if (rr
->key
->class != cname
->key
->class)
843 r
= dns_name_change_suffix(cname
->cname
.name
, rr
->dname
.name
, dns_resource_key_name(rr
->key
), &n
);
849 r
= dns_name_equal(n
, dns_resource_key_name(cname
->key
));