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_copy_by_key(DnsAnswer
**a
, DnsAnswer
*source
, const DnsResourceKey
*key
, DnsAnswerFlags or_flags
) {
528 DnsResourceRecord
*rr_source
;
529 int ifindex_source
, r
;
530 DnsAnswerFlags flags_source
;
535 /* Copy all RRs matching the specified key from source into *a */
537 DNS_ANSWER_FOREACH_FULL(rr_source
, ifindex_source
, flags_source
, source
) {
539 r
= dns_resource_key_equal(rr_source
->key
, key
);
545 /* Make space for at least one entry */
546 r
= dns_answer_reserve_or_clone(a
, 1);
550 r
= dns_answer_add(*a
, rr_source
, ifindex_source
, flags_source
|or_flags
);
558 int dns_answer_move_by_key(DnsAnswer
**to
, DnsAnswer
**from
, const DnsResourceKey
*key
, DnsAnswerFlags or_flags
) {
565 r
= dns_answer_copy_by_key(to
, *from
, key
, or_flags
);
569 return dns_answer_remove_by_key(from
, key
);
572 void dns_answer_order_by_scope(DnsAnswer
*a
, bool prefer_link_local
) {
573 DnsAnswerItem
*items
;
574 unsigned i
, start
, end
;
585 /* RFC 4795, Section 2.6 suggests we should order entries
586 * depending on whether the sender is a link-local address. */
588 items
= newa(DnsAnswerItem
, a
->n_rrs
);
589 for (i
= 0; i
< a
->n_rrs
; i
++) {
591 if (a
->items
[i
].rr
->key
->class == DNS_CLASS_IN
&&
592 ((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
) ||
593 (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
)))
594 /* Order address records that are are not preferred to the end of the array */
595 items
[end
--] = a
->items
[i
];
597 /* Order all other records to the beginning of the array */
598 items
[start
++] = a
->items
[i
];
601 assert(start
== end
+1);
602 memcpy(a
->items
, items
, sizeof(DnsAnswerItem
) * a
->n_rrs
);
605 int dns_answer_reserve(DnsAnswer
**a
, unsigned n_free
) {
619 ns
= (*a
)->n_rrs
+ n_free
;
621 if ((*a
)->n_allocated
>= ns
)
624 /* Allocate more than we need */
627 n
= realloc(*a
, offsetof(DnsAnswer
, items
) + sizeof(DnsAnswerItem
) * ns
);
633 n
= dns_answer_new(n_free
);
642 int dns_answer_reserve_or_clone(DnsAnswer
**a
, unsigned n_free
) {
643 _cleanup_(dns_answer_unrefp
) DnsAnswer
*n
= NULL
;
648 /* Tries to extend the DnsAnswer object. And if that's not
649 * possibly, since we are not the sole owner, then allocate a
650 * new, appropriately sized one. Either way, after this call
651 * the object will only have a single reference, and has room
652 * for at least the specified number of RRs. */
654 r
= dns_answer_reserve(a
, n_free
);
660 n
= dns_answer_new(((*a
)->n_rrs
+ n_free
) * 2);
664 r
= dns_answer_add_raw_all(n
, *a
);
668 dns_answer_unref(*a
);
675 void dns_answer_dump(DnsAnswer
*answer
, FILE *f
) {
676 DnsResourceRecord
*rr
;
677 DnsAnswerFlags flags
;
683 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, answer
) {
688 t
= dns_resource_record_to_string(rr
);
696 if (ifindex
!= 0 || flags
& (DNS_ANSWER_AUTHENTICATED
|DNS_ANSWER_CACHEABLE
|DNS_ANSWER_SHARED_OWNER
))
700 printf(" ifindex=%i", ifindex
);
701 if (flags
& DNS_ANSWER_AUTHENTICATED
)
702 fputs(" authenticated", f
);
703 if (flags
& DNS_ANSWER_CACHEABLE
)
704 fputs(" cachable", f
);
705 if (flags
& DNS_ANSWER_SHARED_OWNER
)
706 fputs(" shared-owner", f
);