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