]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-answer.c
man/systemd-sysext: list ephemeral/ephemeral-import in the list of options
[thirdparty/systemd.git] / src / resolve / resolved-dns-answer.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
faa133f3 2
ca78ad1d
ZJS
3#include <stdio.h>
4
b5efdb8a 5#include "alloc-util.h"
4ad7f276 6#include "dns-domain.h"
93a1f792 7#include "log.h"
5b2d8ffb 8#include "random-util.h"
07630cea 9#include "resolved-dns-answer.h"
68527d30 10#include "resolved-dns-rr.h"
284d7641 11#include "siphash24.h"
07630cea 12#include "string-util.h"
faa133f3 13
71aee23d
YW
14static DnsAnswerItem *dns_answer_item_free(DnsAnswerItem *item) {
15 if (!item)
16 return NULL;
17
18 dns_resource_record_unref(item->rr);
19 dns_resource_record_unref(item->rrsig);
20
21 return mfree(item);
22}
23
24DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(DnsAnswerItem, dns_answer_item, dns_answer_item_free);
25DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswerItem*, dns_answer_item_unref);
26
ae45e1a3
YW
27static void dns_answer_item_hash_func(const DnsAnswerItem *a, struct siphash *state) {
28 assert(a);
29 assert(state);
30
c01a5c05 31 siphash24_compress_typesafe(a->ifindex, state);
ae45e1a3
YW
32
33 dns_resource_record_hash_func(a->rr, state);
34}
35
36static int dns_answer_item_compare_func(const DnsAnswerItem *a, const DnsAnswerItem *b) {
37 int r;
38
39 assert(a);
40 assert(b);
41
42 r = CMP(a->ifindex, b->ifindex);
43 if (r != 0)
44 return r;
45
46 return dns_resource_record_compare_func(a->rr, b->rr);
47}
48
71aee23d
YW
49DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
50 dns_answer_item_hash_ops,
51 DnsAnswerItem,
52 dns_answer_item_hash_func,
53 dns_answer_item_compare_func,
54 dns_answer_item_unref);
ae45e1a3 55
71aee23d
YW
56static int dns_answer_reserve_internal(DnsAnswer *a, size_t n) {
57 size_t m;
faa133f3 58
71aee23d
YW
59 assert(a);
60 assert(a->items);
398c6118 61
71aee23d
YW
62 m = ordered_set_size(a->items);
63 assert(m <= UINT16_MAX); /* We can only place 64K RRs in an answer at max */
64
14b71de4 65 n = saturate_add(m, n, UINT16_MAX);
ae45e1a3
YW
66
67 /* Higher multipliers give slightly higher efficiency through hash collisions, but the gains
68 * quickly drop off after 2. */
71aee23d
YW
69 return ordered_set_reserve(a->items, n * 2);
70}
ae45e1a3 71
71aee23d
YW
72DnsAnswer *dns_answer_new(size_t n) {
73 _cleanup_ordered_set_free_ OrderedSet *s = NULL;
6530ca0d 74 _cleanup_(dns_answer_unrefp) DnsAnswer *a = NULL;
faa133f3 75
71aee23d
YW
76 if (n > UINT16_MAX)
77 n = UINT16_MAX;
faa133f3 78
71aee23d
YW
79 s = ordered_set_new(&dns_answer_item_hash_ops);
80 if (!s)
81 return NULL;
d42800f1 82
71aee23d 83 a = new(DnsAnswer, 1);
d42800f1 84 if (!a)
71aee23d 85 return NULL;
d42800f1 86
71aee23d
YW
87 *a = (DnsAnswer) {
88 .n_ref = 1,
89 .items = TAKE_PTR(s),
90 };
ae45e1a3 91
71aee23d
YW
92 if (dns_answer_reserve_internal(a, n) < 0)
93 return NULL;
d42800f1 94
6530ca0d 95 return TAKE_PTR(a);
d42800f1
LP
96}
97
8301aa0b
YW
98static DnsAnswer *dns_answer_free(DnsAnswer *a) {
99 assert(a);
faa133f3 100
71aee23d 101 ordered_set_free(a->items);
8301aa0b 102 return mfree(a);
faa133f3
LP
103}
104
8301aa0b
YW
105DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsAnswer, dns_answer, dns_answer_free);
106
04617bf8
LP
107static int dns_answer_add_raw(
108 DnsAnswer *a,
109 DnsResourceRecord *rr,
110 int ifindex,
111 DnsAnswerFlags flags,
112 DnsResourceRecord *rrsig) {
113
71aee23d 114 _cleanup_(dns_answer_item_unrefp) DnsAnswerItem *item = NULL;
ae45e1a3
YW
115 int r;
116
547973de
LP
117 assert(rr);
118
119 if (!a)
120 return -ENOSPC;
121
71aee23d 122 if (dns_answer_size(a) >= UINT16_MAX)
547973de
LP
123 return -ENOSPC;
124
71aee23d
YW
125 item = new(DnsAnswerItem, 1);
126 if (!item)
127 return -ENOMEM;
128
129 *item = (DnsAnswerItem) {
130 .n_ref = 1,
131 .rr = dns_resource_record_ref(rr),
105e1512
LP
132 .ifindex = ifindex,
133 .flags = flags,
04617bf8 134 .rrsig = dns_resource_record_ref(rrsig),
105e1512 135 };
547973de 136
71aee23d 137 r = ordered_set_put(a->items, item);
ae45e1a3
YW
138 if (r < 0)
139 return r;
ae45e1a3 140
71aee23d 141 TAKE_PTR(item);
547973de
LP
142 return 1;
143}
144
145static int dns_answer_add_raw_all(DnsAnswer *a, DnsAnswer *source) {
9c5fcb8a
LP
146 DnsAnswerItem *item;
147 int r;
547973de 148
9c5fcb8a
LP
149 DNS_ANSWER_FOREACH_ITEM(item, source) {
150 r = dns_answer_add_raw(
151 a,
152 item->rr,
153 item->ifindex,
04617bf8
LP
154 item->flags,
155 item->rrsig);
547973de
LP
156 if (r < 0)
157 return r;
158 }
159
160 return 0;
161}
162
04617bf8
LP
163int dns_answer_add(
164 DnsAnswer *a,
165 DnsResourceRecord *rr,
166 int ifindex,
167 DnsAnswerFlags flags,
168 DnsResourceRecord *rrsig) {
169
ae45e1a3 170 DnsAnswerItem tmp, *exist;
7e8e0422 171
faa133f3
LP
172 assert(rr);
173
78c6a153
LP
174 if (!a)
175 return -ENOSPC;
c296dd2e
LP
176 if (a->n_ref > 1)
177 return -EBUSY;
78c6a153 178
ae45e1a3
YW
179 tmp = (DnsAnswerItem) {
180 .rr = rr,
181 .ifindex = ifindex,
182 };
7feea00b 183
71aee23d 184 exist = ordered_set_get(a->items, &tmp);
ae45e1a3 185 if (exist) {
8ec951e8
BP
186 /* There's already an RR of the same RRset in place! Let's see if the TTLs more or
187 * less match. RFC 2181, Section 5.2 suggests clients should reject RRsets
188 * containing RRs with differing TTLs. We are more tolerant of this situation except
189 * if one RR has a zero TTL and the other a nonzero TTL. In mDNS, zero TTLs are
190 * special, so we must error in that case. */
191 if ((rr->ttl == 0) != (exist->rr->ttl == 0)) {
192 if ((exist->flags | flags) & DNS_ANSWER_REFUSE_TTL_NO_MATCH)
193 return log_debug_errno(
194 SYNTHETIC_ERRNO(EINVAL),
195 "Refusing to merge RRs with zero TTL and non-zero TTL: %s vs. %s",
196 dns_resource_record_to_string(rr),
197 dns_resource_record_to_string(exist->rr));
198
199 log_debug("Merging RRs with zero TTL and non-zero TTL (not RFC 2181/5.2 compliant): %s vs. %s",
200 dns_resource_record_to_string(rr),
201 dns_resource_record_to_string(exist->rr));
202 }
dffb8277 203
ae45e1a3
YW
204 /* Entry already exists, keep the entry with the higher TTL. */
205 if (rr->ttl > exist->rr->ttl) {
7daeec3e 206 DNS_RR_REPLACE(exist->rr, dns_resource_record_ref(rr));
04617bf8
LP
207
208 /* Update RRSIG and RR at the same time */
7daeec3e
ZJS
209 if (rrsig)
210 DNS_RR_REPLACE(exist->rrsig, dns_resource_record_ref(rrsig));
7feea00b 211 }
dffb8277 212
ae45e1a3 213 exist->flags |= flags;
388c92ce
YW
214
215 if (rr->key->type == DNS_TYPE_RRSIG) {
216 /* If the rr is RRSIG, then move the rr to the end. */
217 assert_se(ordered_set_remove(a->items, exist) == exist);
218 assert_se(ordered_set_put(a->items, exist) == 1);
219 }
dffb8277 220 return 0;
7e8e0422
LP
221 }
222
04617bf8 223 return dns_answer_add_raw(a, rr, ifindex, flags, rrsig);
547973de 224}
faa133f3 225
547973de 226static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
9c5fcb8a
LP
227 DnsAnswerItem *item;
228 int r;
78c6a153 229
9c5fcb8a 230 DNS_ANSWER_FOREACH_ITEM(item, b) {
04617bf8 231 r = dns_answer_add(a, item->rr, item->ifindex, item->flags, item->rrsig);
547973de
LP
232 if (r < 0)
233 return r;
234 }
235
236 return 0;
237}
238
04617bf8
LP
239int dns_answer_add_extend(
240 DnsAnswer **a,
241 DnsResourceRecord *rr,
242 int ifindex,
243 DnsAnswerFlags flags,
244 DnsResourceRecord *rrsig) {
245
547973de
LP
246 int r;
247
248 assert(a);
249 assert(rr);
250
251 r = dns_answer_reserve_or_clone(a, 1);
252 if (r < 0)
253 return r;
254
04617bf8 255 return dns_answer_add(*a, rr, ifindex, flags, rrsig);
7e8e0422
LP
256}
257
97ebebbc 258int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl, int ifindex) {
8bf52d3d
LP
259 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *soa = NULL;
260
261 soa = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SOA, name);
262 if (!soa)
263 return -ENOMEM;
264
57f5ad31
LP
265 soa->ttl = ttl;
266
8bf52d3d
LP
267 soa->soa.mname = strdup(name);
268 if (!soa->soa.mname)
269 return -ENOMEM;
270
b910cc72 271 soa->soa.rname = strjoin("root.", name);
8bf52d3d
LP
272 if (!soa->soa.rname)
273 return -ENOMEM;
274
275 soa->soa.serial = 1;
276 soa->soa.refresh = 1;
277 soa->soa.retry = 1;
278 soa->soa.expire = 1;
57f5ad31 279 soa->soa.minimum = ttl;
8bf52d3d 280
04617bf8 281 return dns_answer_add(a, soa, ifindex, DNS_ANSWER_AUTHENTICATED, NULL);
8bf52d3d
LP
282}
283
105e1512
LP
284int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
285 DnsAnswerFlags flags = 0, i_flags;
547973de 286 DnsResourceRecord *i;
105e1512 287 bool found = false;
7e8e0422
LP
288 int r;
289
7e8e0422
LP
290 assert(key);
291
105e1512 292 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
547973de 293 r = dns_resource_key_match_rr(key, i, NULL);
7e8e0422
LP
294 if (r < 0)
295 return r;
105e1512
LP
296 if (r == 0)
297 continue;
298
299 if (!ret_flags)
7e8e0422 300 return 1;
105e1512
LP
301
302 if (found)
303 flags &= i_flags;
304 else {
305 flags = i_flags;
306 found = true;
307 }
7e8e0422
LP
308 }
309
105e1512
LP
310 if (ret_flags)
311 *ret_flags = flags;
312
313 return found;
7e8e0422
LP
314}
315
9fffe0a9 316bool dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
105e1512
LP
317 DnsResourceRecord *i;
318
b17b6a74 319 DNS_ANSWER_FOREACH(i, a)
105e1512
LP
320 if (IN_SET(i->key->type, DNS_TYPE_NSEC, DNS_TYPE_NSEC3))
321 return true;
105e1512
LP
322
323 return false;
5eefe544
TG
324}
325
e926785a
LP
326int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone) {
327 DnsResourceRecord *rr;
328 int r;
329
330 /* Checks whether the specified answer contains at least one NSEC3 RR in the specified zone */
331
332 DNS_ANSWER_FOREACH(rr, answer) {
333 const char *p;
334
335 if (rr->key->type != DNS_TYPE_NSEC3)
336 continue;
337
1c02e7ba 338 p = dns_resource_key_name(rr->key);
e926785a
LP
339 r = dns_name_parent(&p);
340 if (r < 0)
341 return r;
342 if (r == 0)
343 continue;
344
345 r = dns_name_equal(p, zone);
346 if (r != 0)
347 return r;
348 }
349
350 return false;
351}
352
9fffe0a9 353bool dns_answer_contains(DnsAnswer *answer, DnsResourceRecord *rr) {
7d44b198
LP
354 DnsResourceRecord *i;
355
356 DNS_ANSWER_FOREACH(i, answer)
357 if (dns_resource_record_equal(i, rr))
358 return true;
359
360 return false;
361}
362
b17b6a74
LP
363int dns_answer_find_soa(
364 DnsAnswer *a,
365 const DnsResourceKey *key,
366 DnsResourceRecord **ret,
367 DnsAnswerFlags *ret_flags) {
368
81f7fc5e
LP
369 DnsResourceRecord *rr, *soa = NULL;
370 DnsAnswerFlags rr_flags, soa_flags = 0;
29c1519e 371 int r;
7e8e0422 372
7e8e0422 373 assert(key);
7e8e0422 374
0f05c387
LP
375 /* For a SOA record we can never find a matching SOA record */
376 if (key->type == DNS_TYPE_SOA)
b17b6a74 377 goto not_found;
0f05c387 378
fd009cd8 379 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
29c1519e
LP
380 r = dns_resource_key_match_soa(key, rr->key);
381 if (r < 0)
382 return r;
383 if (r > 0) {
81f7fc5e
LP
384
385 if (soa) {
1c02e7ba 386 r = dns_name_endswith(dns_resource_key_name(rr->key), dns_resource_key_name(soa->key));
81f7fc5e
LP
387 if (r < 0)
388 return r;
389 if (r > 0)
390 continue;
391 }
392
393 soa = rr;
394 soa_flags = rr_flags;
7e8e0422
LP
395 }
396 }
397
81f7fc5e 398 if (!soa)
b17b6a74 399 goto not_found;
81f7fc5e
LP
400
401 if (ret)
402 *ret = soa;
b17b6a74
LP
403 if (ret_flags)
404 *ret_flags = soa_flags;
81f7fc5e
LP
405
406 return 1;
b17b6a74
LP
407
408not_found:
409 if (ret)
410 *ret = NULL;
411 if (ret_flags)
412 *ret_flags = 0;
413
414 return 0;
faa133f3 415}
934e9b10 416
b17b6a74
LP
417int dns_answer_find_cname_or_dname(
418 DnsAnswer *a,
419 const DnsResourceKey *key,
420 DnsResourceRecord **ret,
421 DnsAnswerFlags *ret_flags) {
422
5d27351f 423 DnsResourceRecord *rr;
105e1512 424 DnsAnswerFlags rr_flags;
29c1519e 425 int r;
5d27351f
TG
426
427 assert(key);
428
5d27351f 429 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
6b2f7093 430 if (!dns_type_may_redirect(key->type))
5d27351f
TG
431 return 0;
432
105e1512 433 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
29c1519e
LP
434 r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
435 if (r < 0)
436 return r;
437 if (r > 0) {
5d27351f
TG
438 if (ret)
439 *ret = rr;
b17b6a74
LP
440 if (ret_flags)
441 *ret_flags = rr_flags;
5d27351f
TG
442 return 1;
443 }
444 }
445
b17b6a74
LP
446 if (ret)
447 *ret = NULL;
448 if (ret_flags)
449 *ret_flags = 0;
450
5d27351f
TG
451 return 0;
452}
453
547973de
LP
454int dns_answer_merge(DnsAnswer *a, DnsAnswer *b, DnsAnswer **ret) {
455 _cleanup_(dns_answer_unrefp) DnsAnswer *k = NULL;
456 int r;
457
458 assert(ret);
459
032b3982
LP
460 if (a == b) {
461 *ret = dns_answer_ref(a);
462 return 0;
463 }
464
547973de
LP
465 if (dns_answer_size(a) <= 0) {
466 *ret = dns_answer_ref(b);
467 return 0;
468 }
469
470 if (dns_answer_size(b) <= 0) {
471 *ret = dns_answer_ref(a);
472 return 0;
473 }
474
71aee23d 475 k = dns_answer_new(dns_answer_size(a) + dns_answer_size(b));
547973de
LP
476 if (!k)
477 return -ENOMEM;
478
479 r = dns_answer_add_raw_all(k, a);
480 if (r < 0)
481 return r;
482
483 r = dns_answer_add_all(k, b);
484 if (r < 0)
485 return r;
486
1cc6c93a 487 *ret = TAKE_PTR(k);
547973de
LP
488
489 return 0;
490}
491
492int dns_answer_extend(DnsAnswer **a, DnsAnswer *b) {
493 DnsAnswer *merged;
494 int r;
495
496 assert(a);
497
498 r = dns_answer_merge(*a, b, &merged);
499 if (r < 0)
500 return r;
501
1117a960 502 DNS_ANSWER_REPLACE(*a, merged);
547973de
LP
503 return 0;
504}
505
506int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
71aee23d
YW
507 DnsAnswerItem *item;
508 bool found = false;
934e9b10
LP
509 int r;
510
547973de
LP
511 assert(a);
512 assert(key);
934e9b10 513
547973de 514 /* Remove all entries matching the specified key from *a */
934e9b10 515
71aee23d
YW
516 DNS_ANSWER_FOREACH_ITEM(item, *a) {
517 r = dns_resource_key_equal(item->rr->key, key);
547973de
LP
518 if (r < 0)
519 return r;
71aee23d
YW
520 if (r > 0) {
521 dns_answer_item_unref(ordered_set_remove((*a)->items, item));
547973de 522 found = true;
71aee23d 523 }
547973de
LP
524 }
525
526 if (!found)
527 return 0;
528
71aee23d 529 if (dns_answer_isempty(*a))
547973de 530 *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
934e9b10 531
547973de
LP
532 return 1;
533}
534
71aee23d
YW
535int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rr) {
536 _unused_ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr_ref = dns_resource_record_ref(rr);
537 DnsAnswerItem *item;
538 bool found = false;
0c857028
LP
539 int r;
540
541 assert(a);
71aee23d 542 assert(rr);
0c857028
LP
543
544 /* Remove all entries matching the specified RR from *a */
545
71aee23d
YW
546 DNS_ANSWER_FOREACH_ITEM(item, *a) {
547 r = dns_resource_record_equal(item->rr, rr);
0c857028
LP
548 if (r < 0)
549 return r;
71aee23d
YW
550 if (r > 0) {
551 dns_answer_item_unref(ordered_set_remove((*a)->items, item));
0c857028 552 found = true;
71aee23d 553 }
0c857028
LP
554 }
555
556 if (!found)
557 return 0;
558
71aee23d 559 if (dns_answer_isempty(*a))
0c857028 560 *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
0c857028
LP
561
562 return 1;
563}
564
5d7da51e
LP
565int dns_answer_remove_by_answer_keys(DnsAnswer **a, DnsAnswer *b) {
566 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *prev = NULL;
567 DnsAnswerItem *item;
568 int r;
569
570 /* Removes all items from '*a' that have a matching key in 'b' */
571
572 DNS_ANSWER_FOREACH_ITEM(item, b) {
573
574 if (prev && dns_resource_key_equal(item->rr->key, prev)) /* Skip this one, we already looked at it */
575 continue;
576
577 r = dns_answer_remove_by_key(a, item->rr->key);
578 if (r < 0)
579 return r;
71aee23d
YW
580 if (!*a)
581 return 0; /* a is already empty. */
5d7da51e
LP
582
583 /* Let's remember this entry's RR key, to optimize the loop a bit: if we have an RRset with
584 * more than one item then we don't need to remove the key multiple times */
57318441 585 DNS_RESOURCE_KEY_REPLACE(prev, dns_resource_key_ref(item->rr->key));
5d7da51e
LP
586 }
587
588 return 0;
589}
590
04617bf8
LP
591int dns_answer_copy_by_key(
592 DnsAnswer **a,
593 DnsAnswer *source,
594 const DnsResourceKey *key,
595 DnsAnswerFlags or_flags,
596 DnsResourceRecord *rrsig) {
597
9c5fcb8a
LP
598 DnsAnswerItem *item;
599 int r;
547973de
LP
600
601 assert(a);
602 assert(key);
603
604 /* Copy all RRs matching the specified key from source into *a */
605
9c5fcb8a 606 DNS_ANSWER_FOREACH_ITEM(item, source) {
547973de 607
9c5fcb8a 608 r = dns_resource_key_equal(item->rr->key, key);
547973de
LP
609 if (r < 0)
610 return r;
611 if (r == 0)
612 continue;
613
71aee23d 614 r = dns_answer_add_extend(a, item->rr, item->ifindex, item->flags|or_flags, rrsig ?: item->rrsig);
547973de
LP
615 if (r < 0)
616 return r;
617 }
618
619 return 0;
934e9b10 620}
af93291c 621
04617bf8
LP
622int dns_answer_move_by_key(
623 DnsAnswer **to,
624 DnsAnswer **from,
625 const DnsResourceKey *key,
626 DnsAnswerFlags or_flags,
627 DnsResourceRecord *rrsig) {
71aee23d 628
105e1512
LP
629 int r;
630
631 assert(to);
632 assert(from);
633 assert(key);
634
04617bf8 635 r = dns_answer_copy_by_key(to, *from, key, or_flags, rrsig);
105e1512
LP
636 if (r < 0)
637 return r;
638
639 return dns_answer_remove_by_key(from, key);
640}
641
af93291c 642void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
71aee23d
YW
643 _cleanup_free_ DnsAnswerItem **items = NULL;
644 DnsAnswerItem **p, *item;
645 size_t n;
af93291c 646
71aee23d
YW
647 n = dns_answer_size(a);
648 if (n <= 1)
af93291c
LP
649 return;
650
af93291c
LP
651 /* RFC 4795, Section 2.6 suggests we should order entries
652 * depending on whether the sender is a link-local address. */
653
71aee23d
YW
654 p = items = new(DnsAnswerItem*, n);
655 if (!items)
656 return (void) log_oom();
657
658 /* Order preferred address records and other records to the beginning of the array */
659 DNS_ANSWER_FOREACH_ITEM(item, a)
660 if (dns_resource_record_is_link_local_address(item->rr) == prefer_link_local)
661 *p++ = dns_answer_item_ref(item);
662
663 /* Order address records that are not preferred to the end of the array */
664 DNS_ANSWER_FOREACH_ITEM(item, a)
665 if (dns_resource_record_is_link_local_address(item->rr) != prefer_link_local)
666 *p++ = dns_answer_item_ref(item);
667
71aee23d 668 assert((size_t) (p - items) == n);
af93291c 669
71aee23d
YW
670 ordered_set_clear(a->items);
671 for (size_t i = 0; i < n; i++)
672 assert_se(ordered_set_put(a->items, items[i]) >= 0);
78c6a153
LP
673}
674
da6053d0 675int dns_answer_reserve(DnsAnswer **a, size_t n_free) {
2f763887
LP
676 assert(a);
677
78c6a153
LP
678 if (n_free <= 0)
679 return 0;
680
71aee23d
YW
681 if (!*a) {
682 DnsAnswer *n;
78c6a153 683
78c6a153
LP
684 n = dns_answer_new(n_free);
685 if (!n)
686 return -ENOMEM;
71aee23d
YW
687
688 *a = n;
689 return 0;
78c6a153
LP
690 }
691
71aee23d
YW
692 if ((*a)->n_ref > 1)
693 return -EBUSY;
694
695 return dns_answer_reserve_internal(*a, n_free);
af93291c 696}
547973de 697
da6053d0 698int dns_answer_reserve_or_clone(DnsAnswer **a, size_t n_free) {
71aee23d
YW
699 _cleanup_(dns_answer_unrefp) DnsAnswer *n = NULL;
700 size_t ns;
547973de
LP
701 int r;
702
703 assert(a);
704
71aee23d
YW
705 r = dns_answer_reserve(a, n_free);
706 if (r != -EBUSY)
707 return r;
547973de 708
71aee23d
YW
709 ns = dns_answer_size(*a);
710 assert(ns <= UINT16_MAX); /* Maximum number of RRs we can stick into a DNS packet section */
547973de 711
14b71de4 712 ns = saturate_add(ns, n_free, UINT16_MAX);
ae49ce87 713
71aee23d
YW
714 n = dns_answer_new(ns);
715 if (!n)
716 return -ENOMEM;
547973de 717
71aee23d
YW
718 r = dns_answer_add_raw_all(n, *a);
719 if (r < 0)
720 return r;
721
1117a960 722 DNS_ANSWER_REPLACE(*a, TAKE_PTR(n));
547973de
LP
723 return 0;
724}
26156910 725
a9420840
ZJS
726/*
727 * This function is not used in the code base, but is useful when debugging. Do not delete.
728 */
26156910 729void dns_answer_dump(DnsAnswer *answer, FILE *f) {
9c5fcb8a 730 DnsAnswerItem *item;
26156910
LP
731
732 if (!f)
733 f = stdout;
734
9c5fcb8a 735 DNS_ANSWER_FOREACH_ITEM(item, answer) {
7b50eb2e 736 const char *t;
26156910
LP
737
738 fputc('\t', f);
739
9c5fcb8a 740 t = dns_resource_record_to_string(item->rr);
7b50eb2e 741 if (!t) {
26156910
LP
742 log_oom();
743 continue;
744 }
745
746 fputs(t, f);
567aa5c8
LP
747 fputs("\t;", f);
748 fprintf(f, " ttl=%" PRIu32, item->rr->ttl);
26156910 749
9c5fcb8a
LP
750 if (item->ifindex != 0)
751 fprintf(f, " ifindex=%i", item->ifindex);
04617bf8
LP
752 if (item->rrsig)
753 fputs(" rrsig", f);
9c5fcb8a 754 if (item->flags & DNS_ANSWER_AUTHENTICATED)
26156910 755 fputs(" authenticated", f);
9c5fcb8a 756 if (item->flags & DNS_ANSWER_CACHEABLE)
a9420840 757 fputs(" cacheable", f);
9c5fcb8a 758 if (item->flags & DNS_ANSWER_SHARED_OWNER)
26156910 759 fputs(" shared-owner", f);
9c5fcb8a 760 if (item->flags & DNS_ANSWER_CACHE_FLUSH)
5cdcac6c 761 fputs(" cache-flush", f);
9c5fcb8a 762 if (item->flags & DNS_ANSWER_GOODBYE)
5cdcac6c 763 fputs(" goodbye", f);
fa4e74b8
LP
764 if (item->flags & DNS_ANSWER_SECTION_ANSWER)
765 fputs(" section-answer", f);
766 if (item->flags & DNS_ANSWER_SECTION_AUTHORITY)
767 fputs(" section-authority", f);
768 if (item->flags & DNS_ANSWER_SECTION_ADDITIONAL)
769 fputs(" section-additional", f);
26156910
LP
770
771 fputc('\n', f);
772 }
773}
43e6779a 774
a5042ec4 775int dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname) {
43e6779a
LP
776 DnsResourceRecord *rr;
777 int r;
778
779 assert(cname);
780
781 /* Checks whether the answer contains a DNAME record that indicates that the specified CNAME record is
782 * synthesized from it */
783
784 if (cname->key->type != DNS_TYPE_CNAME)
785 return 0;
786
787 DNS_ANSWER_FOREACH(rr, a) {
788 _cleanup_free_ char *n = NULL;
789
790 if (rr->key->type != DNS_TYPE_DNAME)
791 continue;
792 if (rr->key->class != cname->key->class)
793 continue;
794
1c02e7ba 795 r = dns_name_change_suffix(cname->cname.name, rr->dname.name, dns_resource_key_name(rr->key), &n);
43e6779a
LP
796 if (r < 0)
797 return r;
798 if (r == 0)
799 continue;
800
1c02e7ba 801 r = dns_name_equal(n, dns_resource_key_name(cname->key));
43e6779a
LP
802 if (r < 0)
803 return r;
804 if (r > 0)
805 return 1;
43e6779a
LP
806 }
807
808 return 0;
809}
5b2d8ffb
LP
810
811void dns_answer_randomize(DnsAnswer *a) {
71aee23d
YW
812 _cleanup_free_ DnsAnswerItem **items = NULL;
813 DnsAnswerItem **p, *item;
5b2d8ffb
LP
814 size_t n;
815
816 /* Permutes the answer list randomly (Knuth shuffle) */
817
818 n = dns_answer_size(a);
819 if (n <= 1)
820 return;
821
71aee23d
YW
822 p = items = new(DnsAnswerItem*, n);
823 if (!items)
824 return (void) log_oom();
825
826 DNS_ANSWER_FOREACH_ITEM(item, a)
827 *p++ = dns_answer_item_ref(item);
828
829 assert((size_t) (p - items) == n);
830
5b2d8ffb
LP
831 for (size_t i = 0; i < n; i++) {
832 size_t k;
833
834 k = random_u64_range(n);
835 if (k == i)
836 continue;
837
71aee23d 838 SWAP_TWO(items[i], items[k]);
5b2d8ffb 839 }
71aee23d
YW
840
841 ordered_set_clear(a->items);
842 for (size_t i = 0; i < n; i++)
843 assert_se(ordered_set_put(a->items, items[i]) >= 0);
5b2d8ffb 844}
1499a0a9
LP
845
846uint32_t dns_answer_min_ttl(DnsAnswer *a) {
847 uint32_t ttl = UINT32_MAX;
848 DnsResourceRecord *rr;
849
850 /* Return the smallest TTL of all RRs in this answer */
851
852 DNS_ANSWER_FOREACH(rr, a) {
853 /* Don't consider OPT (where the TTL field is used for other purposes than an actual TTL) */
854
855 if (dns_type_is_pseudo(rr->key->type) ||
856 dns_class_is_pseudo(rr->key->class))
857 continue;
858
859 ttl = MIN(ttl, rr->ttl);
860 }
861
862 return ttl;
863}