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_find_soa(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsResourceRecord
**ret
, DnsAnswerFlags
*flags
) {
324 DnsResourceRecord
*rr
, *soa
= NULL
;
325 DnsAnswerFlags rr_flags
, soa_flags
= 0;
330 /* For a SOA record we can never find a matching SOA record */
331 if (key
->type
== DNS_TYPE_SOA
)
334 DNS_ANSWER_FOREACH_FLAGS(rr
, rr_flags
, a
) {
335 r
= dns_resource_key_match_soa(key
, rr
->key
);
341 r
= dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr
->key
), DNS_RESOURCE_KEY_NAME(soa
->key
));
349 soa_flags
= rr_flags
;
364 int dns_answer_find_cname_or_dname(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsResourceRecord
**ret
, DnsAnswerFlags
*flags
) {
365 DnsResourceRecord
*rr
;
366 DnsAnswerFlags rr_flags
;
371 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
372 if (!dns_type_may_redirect(key
->type
))
375 DNS_ANSWER_FOREACH_FLAGS(rr
, rr_flags
, a
) {
376 r
= dns_resource_key_match_cname_or_dname(key
, rr
->key
, NULL
);
391 int dns_answer_merge(DnsAnswer
*a
, DnsAnswer
*b
, DnsAnswer
**ret
) {
392 _cleanup_(dns_answer_unrefp
) DnsAnswer
*k
= NULL
;
397 if (dns_answer_size(a
) <= 0) {
398 *ret
= dns_answer_ref(b
);
402 if (dns_answer_size(b
) <= 0) {
403 *ret
= dns_answer_ref(a
);
407 k
= dns_answer_new(a
->n_rrs
+ b
->n_rrs
);
411 r
= dns_answer_add_raw_all(k
, a
);
415 r
= dns_answer_add_all(k
, b
);
425 int dns_answer_extend(DnsAnswer
**a
, DnsAnswer
*b
) {
431 r
= dns_answer_merge(*a
, b
, &merged
);
435 dns_answer_unref(*a
);
441 int dns_answer_remove_by_key(DnsAnswer
**a
, const DnsResourceKey
*key
) {
442 bool found
= false, other
= false;
443 DnsResourceRecord
*rr
;
450 /* Remove all entries matching the specified key from *a */
452 DNS_ANSWER_FOREACH(rr
, *a
) {
453 r
= dns_resource_key_equal(rr
->key
, key
);
469 *a
= dns_answer_unref(*a
); /* Return NULL for the empty answer */
473 if ((*a
)->n_ref
> 1) {
474 _cleanup_(dns_answer_unrefp
) DnsAnswer
*copy
= NULL
;
475 DnsAnswerFlags flags
;
478 copy
= dns_answer_new((*a
)->n_rrs
);
482 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, *a
) {
483 r
= dns_resource_key_equal(rr
->key
, key
);
489 r
= dns_answer_add_raw(copy
, rr
, ifindex
, flags
);
494 dns_answer_unref(*a
);
501 /* Only a single reference, edit in-place */
505 if (i
>= (*a
)->n_rrs
)
508 r
= dns_resource_key_equal((*a
)->items
[i
].rr
->key
, key
);
512 /* Kill this entry */
514 dns_resource_record_unref((*a
)->items
[i
].rr
);
515 memmove((*a
)->items
+ i
, (*a
)->items
+ i
+ 1, sizeof(DnsAnswerItem
) * ((*a
)->n_rrs
- i
- 1));
520 /* Keep this entry */
527 int dns_answer_remove_by_rr(DnsAnswer
**a
, DnsResourceRecord
*rm
) {
528 bool found
= false, other
= false;
529 DnsResourceRecord
*rr
;
536 /* Remove all entries matching the specified RR from *a */
538 DNS_ANSWER_FOREACH(rr
, *a
) {
539 r
= dns_resource_record_equal(rr
, rm
);
555 *a
= dns_answer_unref(*a
); /* Return NULL for the empty answer */
559 if ((*a
)->n_ref
> 1) {
560 _cleanup_(dns_answer_unrefp
) DnsAnswer
*copy
= NULL
;
561 DnsAnswerFlags flags
;
564 copy
= dns_answer_new((*a
)->n_rrs
);
568 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, *a
) {
569 r
= dns_resource_record_equal(rr
, rm
);
575 r
= dns_answer_add_raw(copy
, rr
, ifindex
, flags
);
580 dns_answer_unref(*a
);
587 /* Only a single reference, edit in-place */
591 if (i
>= (*a
)->n_rrs
)
594 r
= dns_resource_record_equal((*a
)->items
[i
].rr
, rm
);
598 /* Kill this entry */
600 dns_resource_record_unref((*a
)->items
[i
].rr
);
601 memmove((*a
)->items
+ i
, (*a
)->items
+ i
+ 1, sizeof(DnsAnswerItem
) * ((*a
)->n_rrs
- i
- 1));
606 /* Keep this entry */
613 int dns_answer_copy_by_key(DnsAnswer
**a
, DnsAnswer
*source
, const DnsResourceKey
*key
, DnsAnswerFlags or_flags
) {
614 DnsResourceRecord
*rr_source
;
615 int ifindex_source
, r
;
616 DnsAnswerFlags flags_source
;
621 /* Copy all RRs matching the specified key from source into *a */
623 DNS_ANSWER_FOREACH_FULL(rr_source
, ifindex_source
, flags_source
, source
) {
625 r
= dns_resource_key_equal(rr_source
->key
, key
);
631 /* Make space for at least one entry */
632 r
= dns_answer_reserve_or_clone(a
, 1);
636 r
= dns_answer_add(*a
, rr_source
, ifindex_source
, flags_source
|or_flags
);
644 int dns_answer_move_by_key(DnsAnswer
**to
, DnsAnswer
**from
, const DnsResourceKey
*key
, DnsAnswerFlags or_flags
) {
651 r
= dns_answer_copy_by_key(to
, *from
, key
, or_flags
);
655 return dns_answer_remove_by_key(from
, key
);
658 void dns_answer_order_by_scope(DnsAnswer
*a
, bool prefer_link_local
) {
659 DnsAnswerItem
*items
;
660 unsigned i
, start
, end
;
671 /* RFC 4795, Section 2.6 suggests we should order entries
672 * depending on whether the sender is a link-local address. */
674 items
= newa(DnsAnswerItem
, a
->n_rrs
);
675 for (i
= 0; i
< a
->n_rrs
; i
++) {
677 if (a
->items
[i
].rr
->key
->class == DNS_CLASS_IN
&&
678 ((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
) ||
679 (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
)))
680 /* Order address records that are are not preferred to the end of the array */
681 items
[end
--] = a
->items
[i
];
683 /* Order all other records to the beginning of the array */
684 items
[start
++] = a
->items
[i
];
687 assert(start
== end
+1);
688 memcpy(a
->items
, items
, sizeof(DnsAnswerItem
) * a
->n_rrs
);
691 int dns_answer_reserve(DnsAnswer
**a
, unsigned n_free
) {
705 ns
= (*a
)->n_rrs
+ n_free
;
707 if ((*a
)->n_allocated
>= ns
)
710 /* Allocate more than we need */
713 n
= realloc(*a
, offsetof(DnsAnswer
, items
) + sizeof(DnsAnswerItem
) * ns
);
719 n
= dns_answer_new(n_free
);
728 int dns_answer_reserve_or_clone(DnsAnswer
**a
, unsigned n_free
) {
729 _cleanup_(dns_answer_unrefp
) DnsAnswer
*n
= NULL
;
734 /* Tries to extend the DnsAnswer object. And if that's not
735 * possibly, since we are not the sole owner, then allocate a
736 * new, appropriately sized one. Either way, after this call
737 * the object will only have a single reference, and has room
738 * for at least the specified number of RRs. */
740 r
= dns_answer_reserve(a
, n_free
);
746 n
= dns_answer_new(((*a
)->n_rrs
+ n_free
) * 2);
750 r
= dns_answer_add_raw_all(n
, *a
);
754 dns_answer_unref(*a
);
761 void dns_answer_dump(DnsAnswer
*answer
, FILE *f
) {
762 DnsResourceRecord
*rr
;
763 DnsAnswerFlags flags
;
769 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, answer
) {
774 t
= dns_resource_record_to_string(rr
);
782 if (ifindex
!= 0 || flags
& (DNS_ANSWER_AUTHENTICATED
|DNS_ANSWER_CACHEABLE
|DNS_ANSWER_SHARED_OWNER
))
786 printf(" ifindex=%i", ifindex
);
787 if (flags
& DNS_ANSWER_AUTHENTICATED
)
788 fputs(" authenticated", f
);
789 if (flags
& DNS_ANSWER_CACHEABLE
)
790 fputs(" cachable", f
);
791 if (flags
& DNS_ANSWER_SHARED_OWNER
)
792 fputs(" shared-owner", f
);