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 /* Entry already exists, keep the entry with
129 * the higher RR, or the one with TTL 0 */
131 if (rr
->ttl
== 0 || (rr
->ttl
> a
->items
[i
].rr
->ttl
&& a
->items
[i
].rr
->ttl
!= 0)) {
132 dns_resource_record_ref(rr
);
133 dns_resource_record_unref(a
->items
[i
].rr
);
137 a
->items
[i
].flags
|= flags
;
142 return dns_answer_add_raw(a
, rr
, ifindex
, flags
);
145 static int dns_answer_add_all(DnsAnswer
*a
, DnsAnswer
*b
) {
146 DnsResourceRecord
*rr
;
147 DnsAnswerFlags flags
;
150 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, b
) {
151 r
= dns_answer_add(a
, rr
, ifindex
, flags
);
159 int dns_answer_add_extend(DnsAnswer
**a
, DnsResourceRecord
*rr
, int ifindex
, DnsAnswerFlags flags
) {
165 r
= dns_answer_reserve_or_clone(a
, 1);
169 return dns_answer_add(*a
, rr
, ifindex
, flags
);
172 int dns_answer_add_soa(DnsAnswer
*a
, const char *name
, uint32_t ttl
) {
173 _cleanup_(dns_resource_record_unrefp
) DnsResourceRecord
*soa
= NULL
;
175 soa
= dns_resource_record_new_full(DNS_CLASS_IN
, DNS_TYPE_SOA
, name
);
181 soa
->soa
.mname
= strdup(name
);
185 soa
->soa
.rname
= strappend("root.", name
);
190 soa
->soa
.refresh
= 1;
193 soa
->soa
.minimum
= ttl
;
195 return dns_answer_add(a
, soa
, 0, DNS_ANSWER_AUTHENTICATED
);
198 int dns_answer_match_key(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsAnswerFlags
*ret_flags
) {
199 DnsAnswerFlags flags
= 0, i_flags
;
200 DnsResourceRecord
*i
;
206 DNS_ANSWER_FOREACH_FLAGS(i
, i_flags
, a
) {
207 r
= dns_resource_key_match_rr(key
, i
, NULL
);
230 int dns_answer_contains_rr(DnsAnswer
*a
, DnsResourceRecord
*rr
, DnsAnswerFlags
*ret_flags
) {
231 DnsAnswerFlags flags
= 0, i_flags
;
232 DnsResourceRecord
*i
;
238 DNS_ANSWER_FOREACH_FLAGS(i
, i_flags
, a
) {
239 r
= dns_resource_record_equal(i
, rr
);
262 int dns_answer_contains_key(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsAnswerFlags
*ret_flags
) {
263 DnsAnswerFlags flags
= 0, i_flags
;
264 DnsResourceRecord
*i
;
270 DNS_ANSWER_FOREACH_FLAGS(i
, i_flags
, a
) {
271 r
= dns_resource_key_equal(i
->key
, key
);
294 int dns_answer_contains_nsec_or_nsec3(DnsAnswer
*a
) {
295 DnsResourceRecord
*i
;
297 DNS_ANSWER_FOREACH(i
, a
) {
298 if (IN_SET(i
->key
->type
, DNS_TYPE_NSEC
, DNS_TYPE_NSEC3
))
305 int dns_answer_find_soa(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsResourceRecord
**ret
, DnsAnswerFlags
*flags
) {
306 DnsResourceRecord
*rr
, *soa
= NULL
;
307 DnsAnswerFlags rr_flags
, soa_flags
= 0;
312 /* For a SOA record we can never find a matching SOA record */
313 if (key
->type
== DNS_TYPE_SOA
)
316 DNS_ANSWER_FOREACH_FLAGS(rr
, rr_flags
, a
) {
317 r
= dns_resource_key_match_soa(key
, rr
->key
);
323 r
= dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr
->key
), DNS_RESOURCE_KEY_NAME(soa
->key
));
331 soa_flags
= rr_flags
;
346 int dns_answer_find_cname_or_dname(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsResourceRecord
**ret
, DnsAnswerFlags
*flags
) {
347 DnsResourceRecord
*rr
;
348 DnsAnswerFlags rr_flags
;
353 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
354 if (!dns_type_may_redirect(key
->type
))
357 DNS_ANSWER_FOREACH_FLAGS(rr
, rr_flags
, a
) {
358 r
= dns_resource_key_match_cname_or_dname(key
, rr
->key
, NULL
);
373 int dns_answer_merge(DnsAnswer
*a
, DnsAnswer
*b
, DnsAnswer
**ret
) {
374 _cleanup_(dns_answer_unrefp
) DnsAnswer
*k
= NULL
;
379 if (dns_answer_size(a
) <= 0) {
380 *ret
= dns_answer_ref(b
);
384 if (dns_answer_size(b
) <= 0) {
385 *ret
= dns_answer_ref(a
);
389 k
= dns_answer_new(a
->n_rrs
+ b
->n_rrs
);
393 r
= dns_answer_add_raw_all(k
, a
);
397 r
= dns_answer_add_all(k
, b
);
407 int dns_answer_extend(DnsAnswer
**a
, DnsAnswer
*b
) {
413 r
= dns_answer_merge(*a
, b
, &merged
);
417 dns_answer_unref(*a
);
423 int dns_answer_remove_by_key(DnsAnswer
**a
, const DnsResourceKey
*key
) {
424 bool found
= false, other
= false;
425 DnsResourceRecord
*rr
;
432 /* Remove all entries matching the specified key from *a */
434 DNS_ANSWER_FOREACH(rr
, *a
) {
435 r
= dns_resource_key_equal(rr
->key
, key
);
451 *a
= dns_answer_unref(*a
); /* Return NULL for the empty answer */
455 if ((*a
)->n_ref
> 1) {
456 _cleanup_(dns_answer_unrefp
) DnsAnswer
*copy
= NULL
;
457 DnsAnswerFlags flags
;
460 copy
= dns_answer_new((*a
)->n_rrs
);
464 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, *a
) {
465 r
= dns_resource_key_equal(rr
->key
, key
);
471 r
= dns_answer_add_raw(copy
, rr
, ifindex
, flags
);
476 dns_answer_unref(*a
);
483 /* Only a single reference, edit in-place */
487 if (i
>= (*a
)->n_rrs
)
490 r
= dns_resource_key_equal((*a
)->items
[i
].rr
->key
, key
);
494 /* Kill this entry */
496 dns_resource_record_unref((*a
)->items
[i
].rr
);
497 memmove((*a
)->items
+ i
, (*a
)->items
+ i
+ 1, sizeof(DnsAnswerItem
) * ((*a
)->n_rrs
- i
- 1));
502 /* Keep this entry */
509 int dns_answer_copy_by_key(DnsAnswer
**a
, DnsAnswer
*source
, const DnsResourceKey
*key
, DnsAnswerFlags or_flags
) {
510 DnsResourceRecord
*rr_source
;
511 int ifindex_source
, r
;
512 DnsAnswerFlags flags_source
;
517 /* Copy all RRs matching the specified key from source into *a */
519 DNS_ANSWER_FOREACH_FULL(rr_source
, ifindex_source
, flags_source
, source
) {
521 r
= dns_resource_key_equal(rr_source
->key
, key
);
527 /* Make space for at least one entry */
528 r
= dns_answer_reserve_or_clone(a
, 1);
532 r
= dns_answer_add(*a
, rr_source
, ifindex_source
, flags_source
|or_flags
);
540 int dns_answer_move_by_key(DnsAnswer
**to
, DnsAnswer
**from
, const DnsResourceKey
*key
, DnsAnswerFlags or_flags
) {
547 r
= dns_answer_copy_by_key(to
, *from
, key
, or_flags
);
551 return dns_answer_remove_by_key(from
, key
);
554 void dns_answer_order_by_scope(DnsAnswer
*a
, bool prefer_link_local
) {
555 DnsAnswerItem
*items
;
556 unsigned i
, start
, end
;
567 /* RFC 4795, Section 2.6 suggests we should order entries
568 * depending on whether the sender is a link-local address. */
570 items
= newa(DnsAnswerItem
, a
->n_rrs
);
571 for (i
= 0; i
< a
->n_rrs
; i
++) {
573 if (a
->items
[i
].rr
->key
->class == DNS_CLASS_IN
&&
574 ((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
) ||
575 (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
)))
576 /* Order address records that are are not preferred to the end of the array */
577 items
[end
--] = a
->items
[i
];
579 /* Order all other records to the beginning of the array */
580 items
[start
++] = a
->items
[i
];
583 assert(start
== end
+1);
584 memcpy(a
->items
, items
, sizeof(DnsAnswerItem
) * a
->n_rrs
);
587 int dns_answer_reserve(DnsAnswer
**a
, unsigned n_free
) {
601 ns
= (*a
)->n_rrs
+ n_free
;
603 if ((*a
)->n_allocated
>= ns
)
606 /* Allocate more than we need */
609 n
= realloc(*a
, offsetof(DnsAnswer
, items
) + sizeof(DnsAnswerItem
) * ns
);
615 n
= dns_answer_new(n_free
);
624 int dns_answer_reserve_or_clone(DnsAnswer
**a
, unsigned n_free
) {
625 _cleanup_(dns_answer_unrefp
) DnsAnswer
*n
= NULL
;
630 /* Tries to extend the DnsAnswer object. And if that's not
631 * possibly, since we are not the sole owner, then allocate a
632 * new, appropriately sized one. Either way, after this call
633 * the object will only have a single reference, and has room
634 * for at least the specified number of RRs. */
636 r
= dns_answer_reserve(a
, n_free
);
642 n
= dns_answer_new(((*a
)->n_rrs
+ n_free
) * 2);
646 r
= dns_answer_add_raw_all(n
, *a
);
650 dns_answer_unref(*a
);
657 void dns_answer_dump(DnsAnswer
*answer
, FILE *f
) {
658 DnsResourceRecord
*rr
;
659 DnsAnswerFlags flags
;
665 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, answer
) {
670 t
= dns_resource_record_to_string(rr
);
678 if (ifindex
!= 0 || flags
& (DNS_ANSWER_AUTHENTICATED
|DNS_ANSWER_CACHEABLE
|DNS_ANSWER_SHARED_OWNER
))
682 printf(" ifindex=%i", ifindex
);
683 if (flags
& DNS_ANSWER_AUTHENTICATED
)
684 fputs(" authenticated", f
);
685 if (flags
& DNS_ANSWER_CACHEABLE
)
686 fputs(" cachable", f
);
687 if (flags
& DNS_ANSWER_SHARED_OWNER
)
688 fputs(" shared-owner", f
);