]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-answer.c
144b8c3b6771056471136b993702609c35c3a2c5
[thirdparty/systemd.git] / src / resolve / resolved-dns-answer.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <stdio.h>
4
5 #include "alloc-util.h"
6 #include "dns-domain.h"
7 #include "log.h"
8 #include "random-util.h"
9 #include "resolved-dns-answer.h"
10 #include "resolved-dns-rr.h"
11 #include "siphash24.h"
12 #include "string-util.h"
13
14 static 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
24 DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(DnsAnswerItem, dns_answer_item, dns_answer_item_free);
25 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswerItem*, dns_answer_item_unref);
26
27 static void dns_answer_item_hash_func(const DnsAnswerItem *a, struct siphash *state) {
28 assert(a);
29 assert(state);
30
31 siphash24_compress_typesafe(a->ifindex, state);
32
33 dns_resource_record_hash_func(a->rr, state);
34 }
35
36 static 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
49 DEFINE_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);
55
56 static int dns_answer_reserve_internal(DnsAnswer *a, size_t n) {
57 size_t m;
58
59 assert(a);
60 assert(a->items);
61
62 m = ordered_set_size(a->items);
63 assert(m <= UINT16_MAX); /* We can only place 64K RRs in an answer at max */
64
65 n = saturate_add(m, n, UINT16_MAX);
66
67 /* Higher multipliers give slightly higher efficiency through hash collisions, but the gains
68 * quickly drop off after 2. */
69 return ordered_set_reserve(a->items, n * 2);
70 }
71
72 DnsAnswer *dns_answer_new(size_t n) {
73 _cleanup_ordered_set_free_ OrderedSet *s = NULL;
74 _cleanup_(dns_answer_unrefp) DnsAnswer *a = NULL;
75
76 if (n > UINT16_MAX)
77 n = UINT16_MAX;
78
79 s = ordered_set_new(&dns_answer_item_hash_ops);
80 if (!s)
81 return NULL;
82
83 a = new(DnsAnswer, 1);
84 if (!a)
85 return NULL;
86
87 *a = (DnsAnswer) {
88 .n_ref = 1,
89 .items = TAKE_PTR(s),
90 };
91
92 if (dns_answer_reserve_internal(a, n) < 0)
93 return NULL;
94
95 return TAKE_PTR(a);
96 }
97
98 static DnsAnswer *dns_answer_free(DnsAnswer *a) {
99 assert(a);
100
101 ordered_set_free(a->items);
102 return mfree(a);
103 }
104
105 DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsAnswer, dns_answer, dns_answer_free);
106
107 static int dns_answer_add_raw(
108 DnsAnswer *a,
109 DnsResourceRecord *rr,
110 int ifindex,
111 DnsAnswerFlags flags,
112 DnsResourceRecord *rrsig) {
113
114 _cleanup_(dns_answer_item_unrefp) DnsAnswerItem *item = NULL;
115 int r;
116
117 assert(rr);
118
119 if (!a)
120 return -ENOSPC;
121
122 if (dns_answer_size(a) >= UINT16_MAX)
123 return -ENOSPC;
124
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),
132 .ifindex = ifindex,
133 .flags = flags,
134 .rrsig = dns_resource_record_ref(rrsig),
135 };
136
137 r = ordered_set_put(a->items, item);
138 if (r < 0)
139 return r;
140
141 TAKE_PTR(item);
142 return 1;
143 }
144
145 static int dns_answer_add_raw_all(DnsAnswer *a, DnsAnswer *source) {
146 DnsAnswerItem *item;
147 int r;
148
149 DNS_ANSWER_FOREACH_ITEM(item, source) {
150 r = dns_answer_add_raw(
151 a,
152 item->rr,
153 item->ifindex,
154 item->flags,
155 item->rrsig);
156 if (r < 0)
157 return r;
158 }
159
160 return 0;
161 }
162
163 int dns_answer_add(
164 DnsAnswer *a,
165 DnsResourceRecord *rr,
166 int ifindex,
167 DnsAnswerFlags flags,
168 DnsResourceRecord *rrsig) {
169
170 DnsAnswerItem tmp, *exist;
171
172 assert(rr);
173
174 if (!a)
175 return -ENOSPC;
176 if (a->n_ref > 1)
177 return -EBUSY;
178
179 tmp = (DnsAnswerItem) {
180 .rr = rr,
181 .ifindex = ifindex,
182 };
183
184 exist = ordered_set_get(a->items, &tmp);
185 if (exist) {
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 }
203
204 /* Entry already exists, keep the entry with the higher TTL. */
205 if (rr->ttl > exist->rr->ttl) {
206 DNS_RR_REPLACE(exist->rr, dns_resource_record_ref(rr));
207
208 /* Update RRSIG and RR at the same time */
209 if (rrsig)
210 DNS_RR_REPLACE(exist->rrsig, dns_resource_record_ref(rrsig));
211 }
212
213 exist->flags |= flags;
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 }
220 return 0;
221 }
222
223 return dns_answer_add_raw(a, rr, ifindex, flags, rrsig);
224 }
225
226 static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
227 DnsAnswerItem *item;
228 int r;
229
230 DNS_ANSWER_FOREACH_ITEM(item, b) {
231 r = dns_answer_add(a, item->rr, item->ifindex, item->flags, item->rrsig);
232 if (r < 0)
233 return r;
234 }
235
236 return 0;
237 }
238
239 int dns_answer_add_extend(
240 DnsAnswer **a,
241 DnsResourceRecord *rr,
242 int ifindex,
243 DnsAnswerFlags flags,
244 DnsResourceRecord *rrsig) {
245
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
255 return dns_answer_add(*a, rr, ifindex, flags, rrsig);
256 }
257
258 int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl, int ifindex) {
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
265 soa->ttl = ttl;
266
267 soa->soa.mname = strdup(name);
268 if (!soa->soa.mname)
269 return -ENOMEM;
270
271 soa->soa.rname = strjoin("root.", name);
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;
279 soa->soa.minimum = ttl;
280
281 return dns_answer_add(a, soa, ifindex, DNS_ANSWER_AUTHENTICATED, NULL);
282 }
283
284 int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
285 DnsAnswerFlags flags = 0, i_flags;
286 DnsResourceRecord *i;
287 bool found = false;
288 int r;
289
290 assert(key);
291
292 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
293 r = dns_resource_key_match_rr(key, i, NULL);
294 if (r < 0)
295 return r;
296 if (r == 0)
297 continue;
298
299 if (!ret_flags)
300 return 1;
301
302 if (found)
303 flags &= i_flags;
304 else {
305 flags = i_flags;
306 found = true;
307 }
308 }
309
310 if (ret_flags)
311 *ret_flags = flags;
312
313 return found;
314 }
315
316 bool dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
317 DnsResourceRecord *i;
318
319 DNS_ANSWER_FOREACH(i, a)
320 if (IN_SET(i->key->type, DNS_TYPE_NSEC, DNS_TYPE_NSEC3))
321 return true;
322
323 return false;
324 }
325
326 int 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
338 p = dns_resource_key_name(rr->key);
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
353 bool dns_answer_contains(DnsAnswer *answer, DnsResourceRecord *rr) {
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
363 int dns_answer_find_soa(
364 DnsAnswer *a,
365 const DnsResourceKey *key,
366 DnsResourceRecord **ret,
367 DnsAnswerFlags *ret_flags) {
368
369 DnsResourceRecord *rr, *soa = NULL;
370 DnsAnswerFlags rr_flags, soa_flags = 0;
371 int r;
372
373 assert(key);
374
375 /* For a SOA record we can never find a matching SOA record */
376 if (key->type == DNS_TYPE_SOA)
377 goto not_found;
378
379 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
380 r = dns_resource_key_match_soa(key, rr->key);
381 if (r < 0)
382 return r;
383 if (r > 0) {
384
385 if (soa) {
386 r = dns_name_endswith(dns_resource_key_name(rr->key), dns_resource_key_name(soa->key));
387 if (r < 0)
388 return r;
389 if (r > 0)
390 continue;
391 }
392
393 soa = rr;
394 soa_flags = rr_flags;
395 }
396 }
397
398 if (!soa)
399 goto not_found;
400
401 if (ret)
402 *ret = soa;
403 if (ret_flags)
404 *ret_flags = soa_flags;
405
406 return 1;
407
408 not_found:
409 if (ret)
410 *ret = NULL;
411 if (ret_flags)
412 *ret_flags = 0;
413
414 return 0;
415 }
416
417 int dns_answer_find_cname_or_dname(
418 DnsAnswer *a,
419 const DnsResourceKey *key,
420 DnsResourceRecord **ret,
421 DnsAnswerFlags *ret_flags) {
422
423 DnsResourceRecord *rr;
424 DnsAnswerFlags rr_flags;
425 int r;
426
427 assert(key);
428
429 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
430 if (!dns_type_may_redirect(key->type))
431 return 0;
432
433 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
434 r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
435 if (r < 0)
436 return r;
437 if (r > 0) {
438 if (ret)
439 *ret = rr;
440 if (ret_flags)
441 *ret_flags = rr_flags;
442 return 1;
443 }
444 }
445
446 if (ret)
447 *ret = NULL;
448 if (ret_flags)
449 *ret_flags = 0;
450
451 return 0;
452 }
453
454 int 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
460 if (a == b) {
461 *ret = dns_answer_ref(a);
462 return 0;
463 }
464
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
475 k = dns_answer_new(dns_answer_size(a) + dns_answer_size(b));
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
487 *ret = TAKE_PTR(k);
488
489 return 0;
490 }
491
492 int 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
502 DNS_ANSWER_REPLACE(*a, merged);
503 return 0;
504 }
505
506 int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
507 DnsAnswerItem *item;
508 bool found = false;
509 int r;
510
511 assert(a);
512 assert(key);
513
514 /* Remove all entries matching the specified key from *a */
515
516 DNS_ANSWER_FOREACH_ITEM(item, *a) {
517 r = dns_resource_key_equal(item->rr->key, key);
518 if (r < 0)
519 return r;
520 if (r > 0) {
521 dns_answer_item_unref(ordered_set_remove((*a)->items, item));
522 found = true;
523 }
524 }
525
526 if (!found)
527 return 0;
528
529 if (dns_answer_isempty(*a))
530 *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
531
532 return 1;
533 }
534
535 int 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;
539 int r;
540
541 assert(a);
542 assert(rr);
543
544 /* Remove all entries matching the specified RR from *a */
545
546 DNS_ANSWER_FOREACH_ITEM(item, *a) {
547 r = dns_resource_record_equal(item->rr, rr);
548 if (r < 0)
549 return r;
550 if (r > 0) {
551 dns_answer_item_unref(ordered_set_remove((*a)->items, item));
552 found = true;
553 }
554 }
555
556 if (!found)
557 return 0;
558
559 if (dns_answer_isempty(*a))
560 *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
561
562 return 1;
563 }
564
565 int 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;
580 if (!*a)
581 return 0; /* a is already empty. */
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 */
585 DNS_RESOURCE_KEY_REPLACE(prev, dns_resource_key_ref(item->rr->key));
586 }
587
588 return 0;
589 }
590
591 int dns_answer_copy_by_key(
592 DnsAnswer **a,
593 DnsAnswer *source,
594 const DnsResourceKey *key,
595 DnsAnswerFlags or_flags,
596 DnsResourceRecord *rrsig) {
597
598 DnsAnswerItem *item;
599 int r;
600
601 assert(a);
602 assert(key);
603
604 /* Copy all RRs matching the specified key from source into *a */
605
606 DNS_ANSWER_FOREACH_ITEM(item, source) {
607
608 r = dns_resource_key_equal(item->rr->key, key);
609 if (r < 0)
610 return r;
611 if (r == 0)
612 continue;
613
614 r = dns_answer_add_extend(a, item->rr, item->ifindex, item->flags|or_flags, rrsig ?: item->rrsig);
615 if (r < 0)
616 return r;
617 }
618
619 return 0;
620 }
621
622 int dns_answer_move_by_key(
623 DnsAnswer **to,
624 DnsAnswer **from,
625 const DnsResourceKey *key,
626 DnsAnswerFlags or_flags,
627 DnsResourceRecord *rrsig) {
628
629 int r;
630
631 assert(to);
632 assert(from);
633 assert(key);
634
635 r = dns_answer_copy_by_key(to, *from, key, or_flags, rrsig);
636 if (r < 0)
637 return r;
638
639 return dns_answer_remove_by_key(from, key);
640 }
641
642 void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
643 _cleanup_free_ DnsAnswerItem **items = NULL;
644 DnsAnswerItem **p, *item;
645 size_t n;
646
647 n = dns_answer_size(a);
648 if (n <= 1)
649 return;
650
651 /* RFC 4795, Section 2.6 suggests we should order entries
652 * depending on whether the sender is a link-local address. */
653
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
668 assert((size_t) (p - items) == n);
669
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);
673 }
674
675 int dns_answer_reserve(DnsAnswer **a, size_t n_free) {
676 assert(a);
677
678 if (n_free <= 0)
679 return 0;
680
681 if (!*a) {
682 DnsAnswer *n;
683
684 n = dns_answer_new(n_free);
685 if (!n)
686 return -ENOMEM;
687
688 *a = n;
689 return 0;
690 }
691
692 if ((*a)->n_ref > 1)
693 return -EBUSY;
694
695 return dns_answer_reserve_internal(*a, n_free);
696 }
697
698 int dns_answer_reserve_or_clone(DnsAnswer **a, size_t n_free) {
699 _cleanup_(dns_answer_unrefp) DnsAnswer *n = NULL;
700 size_t ns;
701 int r;
702
703 assert(a);
704
705 r = dns_answer_reserve(a, n_free);
706 if (r != -EBUSY)
707 return r;
708
709 ns = dns_answer_size(*a);
710 assert(ns <= UINT16_MAX); /* Maximum number of RRs we can stick into a DNS packet section */
711
712 ns = saturate_add(ns, n_free, UINT16_MAX);
713
714 n = dns_answer_new(ns);
715 if (!n)
716 return -ENOMEM;
717
718 r = dns_answer_add_raw_all(n, *a);
719 if (r < 0)
720 return r;
721
722 DNS_ANSWER_REPLACE(*a, TAKE_PTR(n));
723 return 0;
724 }
725
726 /*
727 * This function is not used in the code base, but is useful when debugging. Do not delete.
728 */
729 void dns_answer_dump(DnsAnswer *answer, FILE *f) {
730 DnsAnswerItem *item;
731
732 if (!f)
733 f = stdout;
734
735 DNS_ANSWER_FOREACH_ITEM(item, answer) {
736 const char *t;
737
738 fputc('\t', f);
739
740 t = dns_resource_record_to_string(item->rr);
741 if (!t) {
742 log_oom();
743 continue;
744 }
745
746 fputs(t, f);
747 fputs("\t;", f);
748 fprintf(f, " ttl=%" PRIu32, item->rr->ttl);
749
750 if (item->ifindex != 0)
751 fprintf(f, " ifindex=%i", item->ifindex);
752 if (item->rrsig)
753 fputs(" rrsig", f);
754 if (item->flags & DNS_ANSWER_AUTHENTICATED)
755 fputs(" authenticated", f);
756 if (item->flags & DNS_ANSWER_CACHEABLE)
757 fputs(" cacheable", f);
758 if (item->flags & DNS_ANSWER_SHARED_OWNER)
759 fputs(" shared-owner", f);
760 if (item->flags & DNS_ANSWER_CACHE_FLUSH)
761 fputs(" cache-flush", f);
762 if (item->flags & DNS_ANSWER_GOODBYE)
763 fputs(" goodbye", f);
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);
770
771 fputc('\n', f);
772 }
773 }
774
775 int dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname) {
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
795 r = dns_name_change_suffix(cname->cname.name, rr->dname.name, dns_resource_key_name(rr->key), &n);
796 if (r < 0)
797 return r;
798 if (r == 0)
799 continue;
800
801 r = dns_name_equal(n, dns_resource_key_name(cname->key));
802 if (r < 0)
803 return r;
804 if (r > 0)
805 return 1;
806 }
807
808 return 0;
809 }
810
811 void dns_answer_randomize(DnsAnswer *a) {
812 _cleanup_free_ DnsAnswerItem **items = NULL;
813 DnsAnswerItem **p, *item;
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
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
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
838 SWAP_TWO(items[i], items[k]);
839 }
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);
844 }
845
846 uint32_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 }