1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
6 #include "dns-domain.h"
7 #include "resolved-dns-answer.h"
8 #include "resolved-dns-dnssec.h"
9 #include "string-util.h"
11 static void dns_answer_item_hash_func(const DnsAnswerItem
*a
, struct siphash
*state
) {
15 siphash24_compress(&a
->ifindex
, sizeof(a
->ifindex
), state
);
17 dns_resource_record_hash_func(a
->rr
, state
);
20 static int dns_answer_item_compare_func(const DnsAnswerItem
*a
, const DnsAnswerItem
*b
) {
26 r
= CMP(a
->ifindex
, b
->ifindex
);
30 return dns_resource_record_compare_func(a
->rr
, b
->rr
);
33 DEFINE_PRIVATE_HASH_OPS(dns_answer_item_hash_ops
, DnsAnswerItem
, dns_answer_item_hash_func
, dns_answer_item_compare_func
);
35 DnsAnswer
*dns_answer_new(size_t n
) {
36 _cleanup_set_free_ Set
*s
= NULL
;
39 if (n
> UINT16_MAX
) /* We can only place 64K RRs in an answer at max */
42 s
= set_new(&dns_answer_item_hash_ops
);
46 /* Higher multipliers give slightly higher efficiency through hash collisions, but the gains
47 * quickly drop off after 2. */
48 if (set_reserve(s
, n
* 2) < 0)
51 a
= malloc0(offsetof(DnsAnswer
, items
) + sizeof(DnsAnswerItem
) * n
);
57 a
->set_items
= TAKE_PTR(s
);
62 static void dns_answer_flush(DnsAnswer
*a
) {
63 DnsResourceRecord
*rr
;
68 a
->set_items
= set_free(a
->set_items
);
70 DNS_ANSWER_FOREACH(rr
, a
)
71 dns_resource_record_unref(rr
);
76 static DnsAnswer
*dns_answer_free(DnsAnswer
*a
) {
83 DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsAnswer
, dns_answer
, dns_answer_free
);
85 static int dns_answer_add_raw(DnsAnswer
*a
, DnsResourceRecord
*rr
, int ifindex
, DnsAnswerFlags flags
) {
93 if (a
->n_rrs
>= a
->n_allocated
)
96 a
->items
[a
->n_rrs
] = (DnsAnswerItem
) {
102 r
= set_put(a
->set_items
, &a
->items
[a
->n_rrs
]);
108 dns_resource_record_ref(rr
);
114 static int dns_answer_add_raw_all(DnsAnswer
*a
, DnsAnswer
*source
) {
115 DnsResourceRecord
*rr
;
116 DnsAnswerFlags flags
;
119 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, source
) {
120 r
= dns_answer_add_raw(a
, rr
, ifindex
, flags
);
128 int dns_answer_add(DnsAnswer
*a
, DnsResourceRecord
*rr
, int ifindex
, DnsAnswerFlags flags
) {
129 DnsAnswerItem tmp
, *exist
;
138 tmp
= (DnsAnswerItem
) {
143 exist
= set_get(a
->set_items
, &tmp
);
145 /* There's already an RR of the same RRset in place! Let's see if the TTLs more or less
146 * match. We don't really care if they match precisely, but we do care whether one is 0 and
147 * the other is not. See RFC 2181, Section 5.2. */
148 if ((rr
->ttl
== 0) != (exist
->rr
->ttl
== 0))
151 /* Entry already exists, keep the entry with the higher TTL. */
152 if (rr
->ttl
> exist
->rr
->ttl
) {
153 dns_resource_record_unref(exist
->rr
);
154 exist
->rr
= dns_resource_record_ref(rr
);
157 exist
->flags
|= flags
;
161 return dns_answer_add_raw(a
, rr
, ifindex
, flags
);
164 static int dns_answer_add_all(DnsAnswer
*a
, DnsAnswer
*b
) {
165 DnsResourceRecord
*rr
;
166 DnsAnswerFlags flags
;
169 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, b
) {
170 r
= dns_answer_add(a
, rr
, ifindex
, flags
);
178 int dns_answer_add_extend(DnsAnswer
**a
, DnsResourceRecord
*rr
, int ifindex
, DnsAnswerFlags flags
) {
184 r
= dns_answer_reserve_or_clone(a
, 1);
188 return dns_answer_add(*a
, rr
, ifindex
, flags
);
191 int dns_answer_add_soa(DnsAnswer
*a
, const char *name
, uint32_t ttl
, int ifindex
) {
192 _cleanup_(dns_resource_record_unrefp
) DnsResourceRecord
*soa
= NULL
;
194 soa
= dns_resource_record_new_full(DNS_CLASS_IN
, DNS_TYPE_SOA
, name
);
200 soa
->soa
.mname
= strdup(name
);
204 soa
->soa
.rname
= strjoin("root.", name
);
209 soa
->soa
.refresh
= 1;
212 soa
->soa
.minimum
= ttl
;
214 return dns_answer_add(a
, soa
, ifindex
, DNS_ANSWER_AUTHENTICATED
);
217 int dns_answer_match_key(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsAnswerFlags
*ret_flags
) {
218 DnsAnswerFlags flags
= 0, i_flags
;
219 DnsResourceRecord
*i
;
225 DNS_ANSWER_FOREACH_FLAGS(i
, i_flags
, a
) {
226 r
= dns_resource_key_match_rr(key
, i
, NULL
);
249 int dns_answer_contains_nsec_or_nsec3(DnsAnswer
*a
) {
250 DnsResourceRecord
*i
;
252 DNS_ANSWER_FOREACH(i
, a
) {
253 if (IN_SET(i
->key
->type
, DNS_TYPE_NSEC
, DNS_TYPE_NSEC3
))
260 int dns_answer_contains_zone_nsec3(DnsAnswer
*answer
, const char *zone
) {
261 DnsResourceRecord
*rr
;
264 /* Checks whether the specified answer contains at least one NSEC3 RR in the specified zone */
266 DNS_ANSWER_FOREACH(rr
, answer
) {
269 if (rr
->key
->type
!= DNS_TYPE_NSEC3
)
272 p
= dns_resource_key_name(rr
->key
);
273 r
= dns_name_parent(&p
);
279 r
= dns_name_equal(p
, zone
);
287 int dns_answer_find_soa(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsResourceRecord
**ret
, DnsAnswerFlags
*flags
) {
288 DnsResourceRecord
*rr
, *soa
= NULL
;
289 DnsAnswerFlags rr_flags
, soa_flags
= 0;
294 /* For a SOA record we can never find a matching SOA record */
295 if (key
->type
== DNS_TYPE_SOA
)
298 DNS_ANSWER_FOREACH_FLAGS(rr
, rr_flags
, a
) {
299 r
= dns_resource_key_match_soa(key
, rr
->key
);
305 r
= dns_name_endswith(dns_resource_key_name(rr
->key
), dns_resource_key_name(soa
->key
));
313 soa_flags
= rr_flags
;
328 int dns_answer_find_cname_or_dname(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsResourceRecord
**ret
, DnsAnswerFlags
*flags
) {
329 DnsResourceRecord
*rr
;
330 DnsAnswerFlags rr_flags
;
335 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
336 if (!dns_type_may_redirect(key
->type
))
339 DNS_ANSWER_FOREACH_FLAGS(rr
, rr_flags
, a
) {
340 r
= dns_resource_key_match_cname_or_dname(key
, rr
->key
, NULL
);
355 int dns_answer_merge(DnsAnswer
*a
, DnsAnswer
*b
, DnsAnswer
**ret
) {
356 _cleanup_(dns_answer_unrefp
) DnsAnswer
*k
= NULL
;
362 *ret
= dns_answer_ref(a
);
366 if (dns_answer_size(a
) <= 0) {
367 *ret
= dns_answer_ref(b
);
371 if (dns_answer_size(b
) <= 0) {
372 *ret
= dns_answer_ref(a
);
376 k
= dns_answer_new(a
->n_rrs
+ b
->n_rrs
);
380 r
= dns_answer_add_raw_all(k
, a
);
384 r
= dns_answer_add_all(k
, b
);
393 int dns_answer_extend(DnsAnswer
**a
, DnsAnswer
*b
) {
399 r
= dns_answer_merge(*a
, b
, &merged
);
403 dns_answer_unref(*a
);
409 int dns_answer_remove_by_key(DnsAnswer
**a
, const DnsResourceKey
*key
) {
410 bool found
= false, other
= false;
411 DnsResourceRecord
*rr
;
418 /* Remove all entries matching the specified key from *a */
420 DNS_ANSWER_FOREACH(rr
, *a
) {
421 r
= dns_resource_key_equal(rr
->key
, key
);
437 *a
= dns_answer_unref(*a
); /* Return NULL for the empty answer */
441 if ((*a
)->n_ref
> 1) {
442 _cleanup_(dns_answer_unrefp
) DnsAnswer
*copy
= NULL
;
443 DnsAnswerFlags flags
;
446 copy
= dns_answer_new((*a
)->n_rrs
);
450 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, *a
) {
451 r
= dns_resource_key_equal(rr
->key
, key
);
457 r
= dns_answer_add_raw(copy
, rr
, ifindex
, flags
);
462 dns_answer_unref(*a
);
468 /* Only a single reference, edit in-place */
472 if (i
>= (*a
)->n_rrs
)
475 r
= dns_resource_key_equal((*a
)->items
[i
].rr
->key
, key
);
479 /* Kill this entry */
481 dns_resource_record_unref((*a
)->items
[i
].rr
);
482 memmove((*a
)->items
+ i
, (*a
)->items
+ i
+ 1, sizeof(DnsAnswerItem
) * ((*a
)->n_rrs
- i
- 1));
487 /* Keep this entry */
494 int dns_answer_remove_by_rr(DnsAnswer
**a
, DnsResourceRecord
*rm
) {
495 bool found
= false, other
= false;
496 DnsResourceRecord
*rr
;
503 /* Remove all entries matching the specified RR from *a */
505 DNS_ANSWER_FOREACH(rr
, *a
) {
506 r
= dns_resource_record_equal(rr
, rm
);
522 *a
= dns_answer_unref(*a
); /* Return NULL for the empty answer */
526 if ((*a
)->n_ref
> 1) {
527 _cleanup_(dns_answer_unrefp
) DnsAnswer
*copy
= NULL
;
528 DnsAnswerFlags flags
;
531 copy
= dns_answer_new((*a
)->n_rrs
);
535 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, *a
) {
536 r
= dns_resource_record_equal(rr
, rm
);
542 r
= dns_answer_add_raw(copy
, rr
, ifindex
, flags
);
547 dns_answer_unref(*a
);
553 /* Only a single reference, edit in-place */
557 if (i
>= (*a
)->n_rrs
)
560 r
= dns_resource_record_equal((*a
)->items
[i
].rr
, rm
);
564 /* Kill this entry */
566 dns_resource_record_unref((*a
)->items
[i
].rr
);
567 memmove((*a
)->items
+ i
, (*a
)->items
+ i
+ 1, sizeof(DnsAnswerItem
) * ((*a
)->n_rrs
- i
- 1));
572 /* Keep this entry */
579 int dns_answer_copy_by_key(DnsAnswer
**a
, DnsAnswer
*source
, const DnsResourceKey
*key
, DnsAnswerFlags or_flags
) {
580 DnsResourceRecord
*rr_source
;
581 int ifindex_source
, r
;
582 DnsAnswerFlags flags_source
;
587 /* Copy all RRs matching the specified key from source into *a */
589 DNS_ANSWER_FOREACH_FULL(rr_source
, ifindex_source
, flags_source
, source
) {
591 r
= dns_resource_key_equal(rr_source
->key
, key
);
597 /* Make space for at least one entry */
598 r
= dns_answer_reserve_or_clone(a
, 1);
602 r
= dns_answer_add(*a
, rr_source
, ifindex_source
, flags_source
|or_flags
);
610 int dns_answer_move_by_key(DnsAnswer
**to
, DnsAnswer
**from
, const DnsResourceKey
*key
, DnsAnswerFlags or_flags
) {
617 r
= dns_answer_copy_by_key(to
, *from
, key
, or_flags
);
621 return dns_answer_remove_by_key(from
, key
);
624 void dns_answer_order_by_scope(DnsAnswer
*a
, bool prefer_link_local
) {
625 DnsAnswerItem
*items
;
626 size_t i
, start
, end
;
637 /* RFC 4795, Section 2.6 suggests we should order entries
638 * depending on whether the sender is a link-local address. */
640 items
= newa(DnsAnswerItem
, a
->n_rrs
);
641 for (i
= 0; i
< a
->n_rrs
; i
++) {
643 if (a
->items
[i
].rr
->key
->class == DNS_CLASS_IN
&&
644 ((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
) ||
645 (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
)))
646 /* Order address records that are not preferred to the end of the array */
647 items
[end
--] = a
->items
[i
];
649 /* Order all other records to the beginning of the array */
650 items
[start
++] = a
->items
[i
];
653 assert(start
== end
+1);
654 memcpy(a
->items
, items
, sizeof(DnsAnswerItem
) * a
->n_rrs
);
657 int dns_answer_reserve(DnsAnswer
**a
, size_t n_free
) {
672 ns
= (*a
)->n_rrs
+ n_free
;
673 if (ns
> UINT16_MAX
) /* Maximum number of RRs we can stick into a DNS packet section */
676 if ((*a
)->n_allocated
>= ns
)
679 /* Allocate more than we need */
684 /* This must be done before realloc() below. Otherwise, the original DnsAnswer object
686 r
= set_reserve((*a
)->set_items
, ns
);
690 n
= realloc(*a
, offsetof(DnsAnswer
, items
) + sizeof(DnsAnswerItem
) * ns
);
696 /* Previously all items are stored in the set, and the enough memory area is allocated
697 * in the above. So set_put() in the below cannot fail. */
698 set_clear(n
->set_items
);
699 for (size_t i
= 0; i
< n
->n_rrs
; i
++)
700 assert_se(set_put(n
->set_items
, &n
->items
[i
]) > 0);
702 n
= dns_answer_new(n_free
);
711 int dns_answer_reserve_or_clone(DnsAnswer
**a
, size_t n_free
) {
712 _cleanup_(dns_answer_unrefp
) DnsAnswer
*n
= NULL
;
717 /* Tries to extend the DnsAnswer object. And if that's not
718 * possible, since we are not the sole owner, then allocate a
719 * new, appropriately sized one. Either way, after this call
720 * the object will only have a single reference, and has room
721 * for at least the specified number of RRs. */
723 r
= dns_answer_reserve(a
, n_free
);
729 n
= dns_answer_new(((*a
)->n_rrs
+ n_free
) * 2);
733 r
= dns_answer_add_raw_all(n
, *a
);
737 dns_answer_unref(*a
);
744 * This function is not used in the code base, but is useful when debugging. Do not delete.
746 void dns_answer_dump(DnsAnswer
*answer
, FILE *f
) {
747 DnsResourceRecord
*rr
;
748 DnsAnswerFlags flags
;
754 DNS_ANSWER_FOREACH_FULL(rr
, ifindex
, flags
, answer
) {
759 t
= dns_resource_record_to_string(rr
);
767 if (ifindex
!= 0 || flags
!= 0)
771 fprintf(f
, " ifindex=%i", ifindex
);
772 if (flags
& DNS_ANSWER_AUTHENTICATED
)
773 fputs(" authenticated", f
);
774 if (flags
& DNS_ANSWER_CACHEABLE
)
775 fputs(" cacheable", f
);
776 if (flags
& DNS_ANSWER_SHARED_OWNER
)
777 fputs(" shared-owner", f
);
778 if (flags
& DNS_ANSWER_CACHE_FLUSH
)
779 fputs(" cache-flush", f
);
780 if (flags
& DNS_ANSWER_GOODBYE
)
781 fputs(" goodbye", f
);
787 int dns_answer_has_dname_for_cname(DnsAnswer
*a
, DnsResourceRecord
*cname
) {
788 DnsResourceRecord
*rr
;
793 /* Checks whether the answer contains a DNAME record that indicates that the specified CNAME record is
794 * synthesized from it */
796 if (cname
->key
->type
!= DNS_TYPE_CNAME
)
799 DNS_ANSWER_FOREACH(rr
, a
) {
800 _cleanup_free_
char *n
= NULL
;
802 if (rr
->key
->type
!= DNS_TYPE_DNAME
)
804 if (rr
->key
->class != cname
->key
->class)
807 r
= dns_name_change_suffix(cname
->cname
.name
, rr
->dname
.name
, dns_resource_key_name(rr
->key
), &n
);
813 r
= dns_name_equal(n
, dns_resource_key_name(cname
->key
));