]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-answer.c
resolve: first increment the reference counter
[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) {
4ce30e4d 172 dns_resource_record_ref(rr);
ae45e1a3 173 dns_resource_record_unref(exist->rr);
4ce30e4d 174 exist->rr = rr;
04617bf8
LP
175
176 /* Update RRSIG and RR at the same time */
177 if (rrsig) {
178 dns_resource_record_ref(rrsig);
179 dns_resource_record_unref(exist->rrsig);
180 exist->rrsig = rrsig;
181 }
7feea00b 182 }
dffb8277 183
ae45e1a3 184 exist->flags |= flags;
dffb8277 185 return 0;
7e8e0422
LP
186 }
187
04617bf8 188 return dns_answer_add_raw(a, rr, ifindex, flags, rrsig);
547973de 189}
faa133f3 190
547973de 191static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
9c5fcb8a
LP
192 DnsAnswerItem *item;
193 int r;
78c6a153 194
9c5fcb8a 195 DNS_ANSWER_FOREACH_ITEM(item, b) {
04617bf8 196 r = dns_answer_add(a, item->rr, item->ifindex, item->flags, item->rrsig);
547973de
LP
197 if (r < 0)
198 return r;
199 }
200
201 return 0;
202}
203
04617bf8
LP
204int dns_answer_add_extend(
205 DnsAnswer **a,
206 DnsResourceRecord *rr,
207 int ifindex,
208 DnsAnswerFlags flags,
209 DnsResourceRecord *rrsig) {
210
547973de
LP
211 int r;
212
213 assert(a);
214 assert(rr);
215
216 r = dns_answer_reserve_or_clone(a, 1);
217 if (r < 0)
218 return r;
219
04617bf8 220 return dns_answer_add(*a, rr, ifindex, flags, rrsig);
7e8e0422
LP
221}
222
97ebebbc 223int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl, int ifindex) {
8bf52d3d
LP
224 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *soa = NULL;
225
226 soa = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SOA, name);
227 if (!soa)
228 return -ENOMEM;
229
57f5ad31
LP
230 soa->ttl = ttl;
231
8bf52d3d
LP
232 soa->soa.mname = strdup(name);
233 if (!soa->soa.mname)
234 return -ENOMEM;
235
b910cc72 236 soa->soa.rname = strjoin("root.", name);
8bf52d3d
LP
237 if (!soa->soa.rname)
238 return -ENOMEM;
239
240 soa->soa.serial = 1;
241 soa->soa.refresh = 1;
242 soa->soa.retry = 1;
243 soa->soa.expire = 1;
57f5ad31 244 soa->soa.minimum = ttl;
8bf52d3d 245
04617bf8 246 return dns_answer_add(a, soa, ifindex, DNS_ANSWER_AUTHENTICATED, NULL);
8bf52d3d
LP
247}
248
105e1512
LP
249int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
250 DnsAnswerFlags flags = 0, i_flags;
547973de 251 DnsResourceRecord *i;
105e1512 252 bool found = false;
7e8e0422
LP
253 int r;
254
7e8e0422
LP
255 assert(key);
256
105e1512 257 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
547973de 258 r = dns_resource_key_match_rr(key, i, NULL);
7e8e0422
LP
259 if (r < 0)
260 return r;
105e1512
LP
261 if (r == 0)
262 continue;
263
264 if (!ret_flags)
7e8e0422 265 return 1;
105e1512
LP
266
267 if (found)
268 flags &= i_flags;
269 else {
270 flags = i_flags;
271 found = true;
272 }
7e8e0422
LP
273 }
274
105e1512
LP
275 if (ret_flags)
276 *ret_flags = flags;
277
278 return found;
7e8e0422
LP
279}
280
9fffe0a9 281bool dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
105e1512
LP
282 DnsResourceRecord *i;
283
b17b6a74 284 DNS_ANSWER_FOREACH(i, a)
105e1512
LP
285 if (IN_SET(i->key->type, DNS_TYPE_NSEC, DNS_TYPE_NSEC3))
286 return true;
105e1512
LP
287
288 return false;
5eefe544
TG
289}
290
e926785a
LP
291int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone) {
292 DnsResourceRecord *rr;
293 int r;
294
295 /* Checks whether the specified answer contains at least one NSEC3 RR in the specified zone */
296
297 DNS_ANSWER_FOREACH(rr, answer) {
298 const char *p;
299
300 if (rr->key->type != DNS_TYPE_NSEC3)
301 continue;
302
1c02e7ba 303 p = dns_resource_key_name(rr->key);
e926785a
LP
304 r = dns_name_parent(&p);
305 if (r < 0)
306 return r;
307 if (r == 0)
308 continue;
309
310 r = dns_name_equal(p, zone);
311 if (r != 0)
312 return r;
313 }
314
315 return false;
316}
317
9fffe0a9 318bool dns_answer_contains(DnsAnswer *answer, DnsResourceRecord *rr) {
7d44b198
LP
319 DnsResourceRecord *i;
320
321 DNS_ANSWER_FOREACH(i, answer)
322 if (dns_resource_record_equal(i, rr))
323 return true;
324
325 return false;
326}
327
b17b6a74
LP
328int dns_answer_find_soa(
329 DnsAnswer *a,
330 const DnsResourceKey *key,
331 DnsResourceRecord **ret,
332 DnsAnswerFlags *ret_flags) {
333
81f7fc5e
LP
334 DnsResourceRecord *rr, *soa = NULL;
335 DnsAnswerFlags rr_flags, soa_flags = 0;
29c1519e 336 int r;
7e8e0422 337
7e8e0422 338 assert(key);
7e8e0422 339
0f05c387
LP
340 /* For a SOA record we can never find a matching SOA record */
341 if (key->type == DNS_TYPE_SOA)
b17b6a74 342 goto not_found;
0f05c387 343
fd009cd8 344 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
29c1519e
LP
345 r = dns_resource_key_match_soa(key, rr->key);
346 if (r < 0)
347 return r;
348 if (r > 0) {
81f7fc5e
LP
349
350 if (soa) {
1c02e7ba 351 r = dns_name_endswith(dns_resource_key_name(rr->key), dns_resource_key_name(soa->key));
81f7fc5e
LP
352 if (r < 0)
353 return r;
354 if (r > 0)
355 continue;
356 }
357
358 soa = rr;
359 soa_flags = rr_flags;
7e8e0422
LP
360 }
361 }
362
81f7fc5e 363 if (!soa)
b17b6a74 364 goto not_found;
81f7fc5e
LP
365
366 if (ret)
367 *ret = soa;
b17b6a74
LP
368 if (ret_flags)
369 *ret_flags = soa_flags;
81f7fc5e
LP
370
371 return 1;
b17b6a74
LP
372
373not_found:
374 if (ret)
375 *ret = NULL;
376 if (ret_flags)
377 *ret_flags = 0;
378
379 return 0;
faa133f3 380}
934e9b10 381
b17b6a74
LP
382int dns_answer_find_cname_or_dname(
383 DnsAnswer *a,
384 const DnsResourceKey *key,
385 DnsResourceRecord **ret,
386 DnsAnswerFlags *ret_flags) {
387
5d27351f 388 DnsResourceRecord *rr;
105e1512 389 DnsAnswerFlags rr_flags;
29c1519e 390 int r;
5d27351f
TG
391
392 assert(key);
393
5d27351f 394 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
6b2f7093 395 if (!dns_type_may_redirect(key->type))
5d27351f
TG
396 return 0;
397
105e1512 398 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
29c1519e
LP
399 r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
400 if (r < 0)
401 return r;
402 if (r > 0) {
5d27351f
TG
403 if (ret)
404 *ret = rr;
b17b6a74
LP
405 if (ret_flags)
406 *ret_flags = rr_flags;
5d27351f
TG
407 return 1;
408 }
409 }
410
b17b6a74
LP
411 if (ret)
412 *ret = NULL;
413 if (ret_flags)
414 *ret_flags = 0;
415
5d27351f
TG
416 return 0;
417}
418
547973de
LP
419int dns_answer_merge(DnsAnswer *a, DnsAnswer *b, DnsAnswer **ret) {
420 _cleanup_(dns_answer_unrefp) DnsAnswer *k = NULL;
421 int r;
422
423 assert(ret);
424
032b3982
LP
425 if (a == b) {
426 *ret = dns_answer_ref(a);
427 return 0;
428 }
429
547973de
LP
430 if (dns_answer_size(a) <= 0) {
431 *ret = dns_answer_ref(b);
432 return 0;
433 }
434
435 if (dns_answer_size(b) <= 0) {
436 *ret = dns_answer_ref(a);
437 return 0;
438 }
439
440 k = dns_answer_new(a->n_rrs + b->n_rrs);
441 if (!k)
442 return -ENOMEM;
443
444 r = dns_answer_add_raw_all(k, a);
445 if (r < 0)
446 return r;
447
448 r = dns_answer_add_all(k, b);
449 if (r < 0)
450 return r;
451
1cc6c93a 452 *ret = TAKE_PTR(k);
547973de
LP
453
454 return 0;
455}
456
457int dns_answer_extend(DnsAnswer **a, DnsAnswer *b) {
458 DnsAnswer *merged;
459 int r;
460
461 assert(a);
462
463 r = dns_answer_merge(*a, b, &merged);
464 if (r < 0)
465 return r;
466
467 dns_answer_unref(*a);
468 *a = merged;
469
470 return 0;
471}
472
473int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
474 bool found = false, other = false;
475 DnsResourceRecord *rr;
da6053d0 476 size_t i;
934e9b10
LP
477 int r;
478
547973de
LP
479 assert(a);
480 assert(key);
934e9b10 481
547973de 482 /* Remove all entries matching the specified key from *a */
934e9b10 483
547973de
LP
484 DNS_ANSWER_FOREACH(rr, *a) {
485 r = dns_resource_key_equal(rr->key, key);
486 if (r < 0)
487 return r;
488 if (r > 0)
489 found = true;
490 else
491 other = true;
492
493 if (found && other)
494 break;
495 }
496
497 if (!found)
498 return 0;
499
500 if (!other) {
501 *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
502 return 1;
934e9b10
LP
503 }
504
547973de
LP
505 if ((*a)->n_ref > 1) {
506 _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
9c5fcb8a 507 DnsAnswerItem *item;
547973de
LP
508
509 copy = dns_answer_new((*a)->n_rrs);
510 if (!copy)
511 return -ENOMEM;
512
9c5fcb8a
LP
513 DNS_ANSWER_FOREACH_ITEM(item, *a) {
514 r = dns_resource_key_equal(item->rr->key, key);
934e9b10 515 if (r < 0)
547973de
LP
516 return r;
517 if (r > 0)
518 continue;
519
04617bf8 520 r = dns_answer_add_raw(copy, item->rr, item->ifindex, item->flags, item->rrsig);
547973de
LP
521 if (r < 0)
522 return r;
934e9b10 523 }
547973de
LP
524
525 dns_answer_unref(*a);
1cc6c93a 526 *a = TAKE_PTR(copy);
547973de
LP
527
528 return 1;
529 }
530
531 /* Only a single reference, edit in-place */
532
533 i = 0;
534 for (;;) {
535 if (i >= (*a)->n_rrs)
536 break;
537
538 r = dns_resource_key_equal((*a)->items[i].rr->key, key);
539 if (r < 0)
540 return r;
541 if (r > 0) {
542 /* Kill this entry */
543
544 dns_resource_record_unref((*a)->items[i].rr);
04617bf8
LP
545 dns_resource_record_unref((*a)->items[i].rrsig);
546
547973de 547 memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
313cefa1 548 (*a)->n_rrs--;
547973de
LP
549 continue;
550
551 } else
552 /* Keep this entry */
553 i++;
934e9b10
LP
554 }
555
547973de
LP
556 return 1;
557}
558
0c857028
LP
559int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rm) {
560 bool found = false, other = false;
561 DnsResourceRecord *rr;
da6053d0 562 size_t i;
0c857028
LP
563 int r;
564
565 assert(a);
566 assert(rm);
567
568 /* Remove all entries matching the specified RR from *a */
569
570 DNS_ANSWER_FOREACH(rr, *a) {
571 r = dns_resource_record_equal(rr, rm);
572 if (r < 0)
573 return r;
574 if (r > 0)
575 found = true;
576 else
577 other = true;
578
579 if (found && other)
580 break;
581 }
582
583 if (!found)
584 return 0;
585
586 if (!other) {
587 *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
588 return 1;
589 }
590
591 if ((*a)->n_ref > 1) {
592 _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
9c5fcb8a 593 DnsAnswerItem *item;
0c857028
LP
594
595 copy = dns_answer_new((*a)->n_rrs);
596 if (!copy)
597 return -ENOMEM;
598
9c5fcb8a
LP
599 DNS_ANSWER_FOREACH_ITEM(item, *a) {
600 r = dns_resource_record_equal(item->rr, rm);
0c857028
LP
601 if (r < 0)
602 return r;
603 if (r > 0)
604 continue;
605
04617bf8 606 r = dns_answer_add_raw(copy, item->rr, item->ifindex, item->flags, item->rrsig);
0c857028
LP
607 if (r < 0)
608 return r;
609 }
610
611 dns_answer_unref(*a);
1cc6c93a 612 *a = TAKE_PTR(copy);
0c857028
LP
613
614 return 1;
615 }
616
617 /* Only a single reference, edit in-place */
618
619 i = 0;
620 for (;;) {
621 if (i >= (*a)->n_rrs)
622 break;
623
624 r = dns_resource_record_equal((*a)->items[i].rr, rm);
625 if (r < 0)
626 return r;
627 if (r > 0) {
628 /* Kill this entry */
629
630 dns_resource_record_unref((*a)->items[i].rr);
04617bf8 631 dns_resource_record_unref((*a)->items[i].rrsig);
0c857028 632 memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
313cefa1 633 (*a)->n_rrs--;
0c857028
LP
634 continue;
635
636 } else
637 /* Keep this entry */
638 i++;
639 }
640
641 return 1;
642}
643
5d7da51e
LP
644int dns_answer_remove_by_answer_keys(DnsAnswer **a, DnsAnswer *b) {
645 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *prev = NULL;
646 DnsAnswerItem *item;
647 int r;
648
649 /* Removes all items from '*a' that have a matching key in 'b' */
650
651 DNS_ANSWER_FOREACH_ITEM(item, b) {
652
653 if (prev && dns_resource_key_equal(item->rr->key, prev)) /* Skip this one, we already looked at it */
654 continue;
655
656 r = dns_answer_remove_by_key(a, item->rr->key);
657 if (r < 0)
658 return r;
659
660 /* Let's remember this entry's RR key, to optimize the loop a bit: if we have an RRset with
661 * more than one item then we don't need to remove the key multiple times */
662 dns_resource_key_unref(prev);
663 prev = dns_resource_key_ref(item->rr->key);
664 }
665
666 return 0;
667}
668
04617bf8
LP
669int dns_answer_copy_by_key(
670 DnsAnswer **a,
671 DnsAnswer *source,
672 const DnsResourceKey *key,
673 DnsAnswerFlags or_flags,
674 DnsResourceRecord *rrsig) {
675
9c5fcb8a
LP
676 DnsAnswerItem *item;
677 int r;
547973de
LP
678
679 assert(a);
680 assert(key);
681
682 /* Copy all RRs matching the specified key from source into *a */
683
9c5fcb8a 684 DNS_ANSWER_FOREACH_ITEM(item, source) {
547973de 685
9c5fcb8a 686 r = dns_resource_key_equal(item->rr->key, key);
547973de
LP
687 if (r < 0)
688 return r;
689 if (r == 0)
690 continue;
691
692 /* Make space for at least one entry */
693 r = dns_answer_reserve_or_clone(a, 1);
694 if (r < 0)
695 return r;
934e9b10 696
048e0433 697 r = dns_answer_add(*a, item->rr, item->ifindex, item->flags|or_flags, rrsig ?: item->rrsig);
547973de
LP
698 if (r < 0)
699 return r;
700 }
701
702 return 0;
934e9b10 703}
af93291c 704
04617bf8
LP
705int dns_answer_move_by_key(
706 DnsAnswer **to,
707 DnsAnswer **from,
708 const DnsResourceKey *key,
709 DnsAnswerFlags or_flags,
710 DnsResourceRecord *rrsig) {
105e1512
LP
711 int r;
712
713 assert(to);
714 assert(from);
715 assert(key);
716
04617bf8 717 r = dns_answer_copy_by_key(to, *from, key, or_flags, rrsig);
105e1512
LP
718 if (r < 0)
719 return r;
720
721 return dns_answer_remove_by_key(from, key);
722}
723
af93291c 724void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
78c6a153 725 DnsAnswerItem *items;
da6053d0 726 size_t i, start, end;
78c6a153
LP
727
728 if (!a)
729 return;
af93291c
LP
730
731 if (a->n_rrs <= 1)
732 return;
733
734 start = 0;
735 end = a->n_rrs-1;
736
737 /* RFC 4795, Section 2.6 suggests we should order entries
738 * depending on whether the sender is a link-local address. */
739
78c6a153 740 items = newa(DnsAnswerItem, a->n_rrs);
af93291c 741 for (i = 0; i < a->n_rrs; i++) {
48662847 742 if (dns_resource_record_is_link_local_address(a->items[i].rr) != prefer_link_local)
61233823 743 /* Order address records that are not preferred to the end of the array */
78c6a153 744 items[end--] = a->items[i];
af93291c
LP
745 else
746 /* Order all other records to the beginning of the array */
78c6a153 747 items[start++] = a->items[i];
af93291c
LP
748 }
749
750 assert(start == end+1);
78c6a153
LP
751 memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs);
752}
753
da6053d0 754int dns_answer_reserve(DnsAnswer **a, size_t n_free) {
78c6a153
LP
755 DnsAnswer *n;
756
2f763887
LP
757 assert(a);
758
78c6a153
LP
759 if (n_free <= 0)
760 return 0;
761
762 if (*a) {
da6053d0 763 size_t ns;
0c2c0fd2 764 int r;
78c6a153
LP
765
766 if ((*a)->n_ref > 1)
767 return -EBUSY;
768
ae49ce87
LP
769 ns = (*a)->n_rrs;
770 assert(ns <= UINT16_MAX); /* Maximum number of RRs we can stick into a DNS packet section */
771
772 if (n_free > UINT16_MAX - ns) /* overflow check */
398c6118 773 ns = UINT16_MAX;
ae49ce87
LP
774 else
775 ns += n_free;
78c6a153
LP
776
777 if ((*a)->n_allocated >= ns)
778 return 0;
779
ae49ce87
LP
780 /* Allocate more than we need, but not more than UINT16_MAX */
781 if (ns <= UINT16_MAX/2)
782 ns *= 2;
783 else
398c6118 784 ns = UINT16_MAX;
2f763887 785
0c2c0fd2
YW
786 /* This must be done before realloc() below. Otherwise, the original DnsAnswer object
787 * may be broken. */
788 r = set_reserve((*a)->set_items, ns);
789 if (r < 0)
790 return r;
791
78c6a153
LP
792 n = realloc(*a, offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * ns);
793 if (!n)
794 return -ENOMEM;
795
796 n->n_allocated = ns;
0c2c0fd2
YW
797
798 /* Previously all items are stored in the set, and the enough memory area is allocated
799 * in the above. So set_put() in the below cannot fail. */
800 set_clear(n->set_items);
801 for (size_t i = 0; i < n->n_rrs; i++)
802 assert_se(set_put(n->set_items, &n->items[i]) > 0);
78c6a153
LP
803 } else {
804 n = dns_answer_new(n_free);
805 if (!n)
806 return -ENOMEM;
807 }
808
809 *a = n;
810 return 0;
af93291c 811}
547973de 812
da6053d0 813int dns_answer_reserve_or_clone(DnsAnswer **a, size_t n_free) {
547973de
LP
814 int r;
815
816 assert(a);
817
ae49ce87
LP
818 /* Tries to extend the DnsAnswer object. And if that's not possible, since we are not the sole owner,
819 * then allocate a new, appropriately sized one. Either way, after this call the object will only
820 * have a single reference, and has room for at least the specified number of RRs. */
547973de 821
ae49ce87
LP
822 if (*a && (*a)->n_ref > 1) {
823 _cleanup_(dns_answer_unrefp) DnsAnswer *n = NULL;
824 size_t ns;
547973de 825
ae49ce87
LP
826 ns = (*a)->n_rrs;
827 assert(ns <= UINT16_MAX); /* Maximum number of RRs we can stick into a DNS packet section */
547973de 828
ae49ce87
LP
829 if (n_free > UINT16_MAX - ns) /* overflow check */
830 ns = UINT16_MAX;
831 else if (n_free > 0) { /* Increase size and double the result, just in case — except if the
832 * increase is specified as 0, in which case we just allocate the
833 * exact amount as before, under the assumption this is just a request
834 * to copy the answer. */
835 ns += n_free;
836
837 if (ns <= UINT16_MAX/2) /* overflow check */
838 ns *= 2;
839 else
840 ns = UINT16_MAX;
841 }
547973de 842
ae49ce87
LP
843 n = dns_answer_new(ns);
844 if (!n)
845 return -ENOMEM;
547973de 846
ae49ce87
LP
847 r = dns_answer_add_raw_all(n, *a);
848 if (r < 0)
849 return r;
850
851 dns_answer_unref(*a);
852 assert_se(*a = TAKE_PTR(n));
853 } else if (n_free > 0) {
854 r = dns_answer_reserve(a, n_free);
855 if (r < 0)
856 return r;
857 }
547973de
LP
858
859 return 0;
860}
26156910 861
a9420840
ZJS
862/*
863 * This function is not used in the code base, but is useful when debugging. Do not delete.
864 */
26156910 865void dns_answer_dump(DnsAnswer *answer, FILE *f) {
9c5fcb8a 866 DnsAnswerItem *item;
26156910
LP
867
868 if (!f)
869 f = stdout;
870
9c5fcb8a 871 DNS_ANSWER_FOREACH_ITEM(item, answer) {
7b50eb2e 872 const char *t;
26156910
LP
873
874 fputc('\t', f);
875
9c5fcb8a 876 t = dns_resource_record_to_string(item->rr);
7b50eb2e 877 if (!t) {
26156910
LP
878 log_oom();
879 continue;
880 }
881
882 fputs(t, f);
567aa5c8
LP
883 fputs("\t;", f);
884 fprintf(f, " ttl=%" PRIu32, item->rr->ttl);
26156910 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}