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