]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-answer.c
resolved: fix DNSSEC canonical ordering logic
[thirdparty/systemd.git] / src / resolve / resolved-dns-answer.c
CommitLineData
faa133f3
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
b5efdb8a 22#include "alloc-util.h"
4ad7f276 23#include "dns-domain.h"
07630cea 24#include "resolved-dns-answer.h"
72667f08 25#include "resolved-dns-dnssec.h"
07630cea 26#include "string-util.h"
faa133f3
LP
27
28DnsAnswer *dns_answer_new(unsigned n) {
29 DnsAnswer *a;
30
78c6a153 31 a = malloc0(offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * n);
faa133f3
LP
32 if (!a)
33 return NULL;
34
35 a->n_ref = 1;
36 a->n_allocated = n;
37
38 return a;
39}
40
41DnsAnswer *dns_answer_ref(DnsAnswer *a) {
42 if (!a)
43 return NULL;
44
45 assert(a->n_ref > 0);
46 a->n_ref++;
47 return a;
48}
49
d42800f1
LP
50static void dns_answer_flush(DnsAnswer *a) {
51 DnsResourceRecord *rr;
52
53 if (!a)
54 return;
55
56 DNS_ANSWER_FOREACH(rr, a)
57 dns_resource_record_unref(rr);
58
59 a->n_rrs = 0;
60}
61
faa133f3
LP
62DnsAnswer *dns_answer_unref(DnsAnswer *a) {
63 if (!a)
64 return NULL;
65
66 assert(a->n_ref > 0);
67
68 if (a->n_ref == 1) {
d42800f1 69 dns_answer_flush(a);
faa133f3
LP
70 free(a);
71 } else
72 a->n_ref--;
73
74 return NULL;
75}
76
105e1512 77static int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
547973de
LP
78 assert(rr);
79
80 if (!a)
81 return -ENOSPC;
82
83 if (a->n_rrs >= a->n_allocated)
84 return -ENOSPC;
85
105e1512
LP
86 a->items[a->n_rrs++] = (DnsAnswerItem) {
87 .rr = dns_resource_record_ref(rr),
88 .ifindex = ifindex,
89 .flags = flags,
90 };
547973de
LP
91
92 return 1;
93}
94
95static int dns_answer_add_raw_all(DnsAnswer *a, DnsAnswer *source) {
96 DnsResourceRecord *rr;
105e1512 97 DnsAnswerFlags flags;
547973de
LP
98 int ifindex, r;
99
105e1512
LP
100 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, source) {
101 r = dns_answer_add_raw(a, rr, ifindex, flags);
547973de
LP
102 if (r < 0)
103 return r;
104 }
105
106 return 0;
107}
108
105e1512 109int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
7e8e0422
LP
110 unsigned i;
111 int r;
112
faa133f3
LP
113 assert(rr);
114
78c6a153
LP
115 if (!a)
116 return -ENOSPC;
c296dd2e
LP
117 if (a->n_ref > 1)
118 return -EBUSY;
78c6a153 119
7e8e0422 120 for (i = 0; i < a->n_rrs; i++) {
78c6a153
LP
121 if (a->items[i].ifindex != ifindex)
122 continue;
123
124 r = dns_resource_record_equal(a->items[i].rr, rr);
7e8e0422
LP
125 if (r < 0)
126 return r;
127 if (r > 0) {
7feea00b
LP
128 /* Don't mix contradicting TTLs (see below) */
129 if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
130 return -EINVAL;
7e8e0422 131
7feea00b
LP
132 /* Entry already exists, keep the entry with
133 * the higher RR. */
134 if (rr->ttl > a->items[i].rr->ttl) {
7e8e0422 135 dns_resource_record_ref(rr);
78c6a153
LP
136 dns_resource_record_unref(a->items[i].rr);
137 a->items[i].rr = rr;
7e8e0422
LP
138 }
139
105e1512 140 a->items[i].flags |= flags;
7e8e0422
LP
141 return 0;
142 }
7feea00b
LP
143
144 r = dns_resource_key_equal(a->items[i].rr->key, rr->key);
145 if (r < 0)
146 return r;
147 if (r > 0) {
148 /* There's already an RR of the same RRset in
149 * place! Let's see if the TTLs more or less
150 * match. We don't really care if they match
151 * precisely, but we do care whether one is 0
152 * and the other is not. See RFC 2181, Section
153 * 5.2.*/
154
155 if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
156 return -EINVAL;
157 }
7e8e0422
LP
158 }
159
105e1512 160 return dns_answer_add_raw(a, rr, ifindex, flags);
547973de 161}
faa133f3 162
547973de
LP
163static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
164 DnsResourceRecord *rr;
105e1512 165 DnsAnswerFlags flags;
547973de 166 int ifindex, r;
78c6a153 167
105e1512
LP
168 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, b) {
169 r = dns_answer_add(a, rr, ifindex, flags);
547973de
LP
170 if (r < 0)
171 return r;
172 }
173
174 return 0;
175}
176
105e1512 177int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
547973de
LP
178 int r;
179
180 assert(a);
181 assert(rr);
182
183 r = dns_answer_reserve_or_clone(a, 1);
184 if (r < 0)
185 return r;
186
105e1512 187 return dns_answer_add(*a, rr, ifindex, flags);
7e8e0422
LP
188}
189
57f5ad31 190int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) {
8bf52d3d
LP
191 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *soa = NULL;
192
193 soa = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SOA, name);
194 if (!soa)
195 return -ENOMEM;
196
57f5ad31
LP
197 soa->ttl = ttl;
198
8bf52d3d
LP
199 soa->soa.mname = strdup(name);
200 if (!soa->soa.mname)
201 return -ENOMEM;
202
203 soa->soa.rname = strappend("root.", name);
204 if (!soa->soa.rname)
205 return -ENOMEM;
206
207 soa->soa.serial = 1;
208 soa->soa.refresh = 1;
209 soa->soa.retry = 1;
210 soa->soa.expire = 1;
57f5ad31 211 soa->soa.minimum = ttl;
8bf52d3d 212
105e1512 213 return dns_answer_add(a, soa, 0, DNS_ANSWER_AUTHENTICATED);
8bf52d3d
LP
214}
215
105e1512
LP
216int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
217 DnsAnswerFlags flags = 0, i_flags;
547973de 218 DnsResourceRecord *i;
105e1512 219 bool found = false;
7e8e0422
LP
220 int r;
221
7e8e0422
LP
222 assert(key);
223
105e1512 224 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
547973de 225 r = dns_resource_key_match_rr(key, i, NULL);
7e8e0422
LP
226 if (r < 0)
227 return r;
105e1512
LP
228 if (r == 0)
229 continue;
230
231 if (!ret_flags)
7e8e0422 232 return 1;
105e1512
LP
233
234 if (found)
235 flags &= i_flags;
236 else {
237 flags = i_flags;
238 found = true;
239 }
7e8e0422
LP
240 }
241
105e1512
LP
242 if (ret_flags)
243 *ret_flags = flags;
244
245 return found;
7e8e0422
LP
246}
247
105e1512
LP
248int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *ret_flags) {
249 DnsAnswerFlags flags = 0, i_flags;
547973de 250 DnsResourceRecord *i;
105e1512 251 bool found = false;
547973de 252 int r;
5eefe544 253
547973de 254 assert(rr);
5eefe544 255
105e1512 256 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
547973de
LP
257 r = dns_resource_record_equal(i, rr);
258 if (r < 0)
259 return r;
105e1512
LP
260 if (r == 0)
261 continue;
262
263 if (!ret_flags)
547973de 264 return 1;
105e1512
LP
265
266 if (found)
267 flags &= i_flags;
268 else {
269 flags = i_flags;
270 found = true;
271 }
547973de 272 }
5eefe544 273
105e1512
LP
274 if (ret_flags)
275 *ret_flags = flags;
276
277 return found;
278}
279
280int dns_answer_contains_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
281 DnsAnswerFlags flags = 0, i_flags;
282 DnsResourceRecord *i;
283 bool found = false;
284 int r;
285
286 assert(key);
287
288 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
289 r = dns_resource_key_equal(i->key, key);
290 if (r < 0)
291 return r;
292 if (r == 0)
293 continue;
294
295 if (!ret_flags)
296 return true;
297
298 if (found)
299 flags &= i_flags;
300 else {
301 flags = i_flags;
302 found = true;
303 }
304 }
305
306 if (ret_flags)
307 *ret_flags = flags;
308
309 return found;
310}
311
312int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
313 DnsResourceRecord *i;
314
315 DNS_ANSWER_FOREACH(i, a) {
316 if (IN_SET(i->key->type, DNS_TYPE_NSEC, DNS_TYPE_NSEC3))
317 return true;
318 }
319
320 return false;
5eefe544
TG
321}
322
fd009cd8 323int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
81f7fc5e
LP
324 DnsResourceRecord *rr, *soa = NULL;
325 DnsAnswerFlags rr_flags, soa_flags = 0;
29c1519e 326 int r;
7e8e0422 327
7e8e0422 328 assert(key);
7e8e0422 329
0f05c387
LP
330 /* For a SOA record we can never find a matching SOA record */
331 if (key->type == DNS_TYPE_SOA)
332 return 0;
333
fd009cd8 334 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
29c1519e
LP
335 r = dns_resource_key_match_soa(key, rr->key);
336 if (r < 0)
337 return r;
338 if (r > 0) {
81f7fc5e
LP
339
340 if (soa) {
341 r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(soa->key));
342 if (r < 0)
343 return r;
344 if (r > 0)
345 continue;
346 }
347
348 soa = rr;
349 soa_flags = rr_flags;
7e8e0422
LP
350 }
351 }
352
81f7fc5e
LP
353 if (!soa)
354 return 0;
355
356 if (ret)
357 *ret = soa;
358 if (flags)
359 *flags = soa_flags;
360
361 return 1;
faa133f3 362}
934e9b10 363
105e1512 364int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
5d27351f 365 DnsResourceRecord *rr;
105e1512 366 DnsAnswerFlags rr_flags;
29c1519e 367 int r;
5d27351f
TG
368
369 assert(key);
370
5d27351f 371 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
6b2f7093 372 if (!dns_type_may_redirect(key->type))
5d27351f
TG
373 return 0;
374
105e1512 375 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
29c1519e
LP
376 r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
377 if (r < 0)
378 return r;
379 if (r > 0) {
5d27351f
TG
380 if (ret)
381 *ret = rr;
105e1512
LP
382 if (flags)
383 *flags = rr_flags;
5d27351f
TG
384 return 1;
385 }
386 }
387
388 return 0;
389}
390
547973de
LP
391int dns_answer_merge(DnsAnswer *a, DnsAnswer *b, DnsAnswer **ret) {
392 _cleanup_(dns_answer_unrefp) DnsAnswer *k = NULL;
393 int r;
394
395 assert(ret);
396
397 if (dns_answer_size(a) <= 0) {
398 *ret = dns_answer_ref(b);
399 return 0;
400 }
401
402 if (dns_answer_size(b) <= 0) {
403 *ret = dns_answer_ref(a);
404 return 0;
405 }
406
407 k = dns_answer_new(a->n_rrs + b->n_rrs);
408 if (!k)
409 return -ENOMEM;
410
411 r = dns_answer_add_raw_all(k, a);
412 if (r < 0)
413 return r;
414
415 r = dns_answer_add_all(k, b);
416 if (r < 0)
417 return r;
418
419 *ret = k;
420 k = NULL;
421
422 return 0;
423}
424
425int dns_answer_extend(DnsAnswer **a, DnsAnswer *b) {
426 DnsAnswer *merged;
427 int r;
428
429 assert(a);
430
431 r = dns_answer_merge(*a, b, &merged);
432 if (r < 0)
433 return r;
434
435 dns_answer_unref(*a);
436 *a = merged;
437
438 return 0;
439}
440
441int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
442 bool found = false, other = false;
443 DnsResourceRecord *rr;
934e9b10
LP
444 unsigned i;
445 int r;
446
547973de
LP
447 assert(a);
448 assert(key);
934e9b10 449
547973de 450 /* Remove all entries matching the specified key from *a */
934e9b10 451
547973de
LP
452 DNS_ANSWER_FOREACH(rr, *a) {
453 r = dns_resource_key_equal(rr->key, key);
454 if (r < 0)
455 return r;
456 if (r > 0)
457 found = true;
458 else
459 other = true;
460
461 if (found && other)
462 break;
463 }
464
465 if (!found)
466 return 0;
467
468 if (!other) {
469 *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
470 return 1;
934e9b10
LP
471 }
472
547973de
LP
473 if ((*a)->n_ref > 1) {
474 _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
105e1512 475 DnsAnswerFlags flags;
547973de
LP
476 int ifindex;
477
478 copy = dns_answer_new((*a)->n_rrs);
479 if (!copy)
480 return -ENOMEM;
481
105e1512 482 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
547973de 483 r = dns_resource_key_equal(rr->key, key);
934e9b10 484 if (r < 0)
547973de
LP
485 return r;
486 if (r > 0)
487 continue;
488
105e1512 489 r = dns_answer_add_raw(copy, rr, ifindex, flags);
547973de
LP
490 if (r < 0)
491 return r;
934e9b10 492 }
547973de
LP
493
494 dns_answer_unref(*a);
495 *a = copy;
496 copy = NULL;
497
498 return 1;
499 }
500
501 /* Only a single reference, edit in-place */
502
503 i = 0;
504 for (;;) {
505 if (i >= (*a)->n_rrs)
506 break;
507
508 r = dns_resource_key_equal((*a)->items[i].rr->key, key);
509 if (r < 0)
510 return r;
511 if (r > 0) {
512 /* Kill this entry */
513
514 dns_resource_record_unref((*a)->items[i].rr);
515 memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
516 (*a)->n_rrs --;
517 continue;
518
519 } else
520 /* Keep this entry */
521 i++;
934e9b10
LP
522 }
523
547973de
LP
524 return 1;
525}
526
105e1512 527int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
547973de
LP
528 DnsResourceRecord *rr_source;
529 int ifindex_source, r;
105e1512 530 DnsAnswerFlags flags_source;
547973de
LP
531
532 assert(a);
533 assert(key);
534
535 /* Copy all RRs matching the specified key from source into *a */
536
105e1512 537 DNS_ANSWER_FOREACH_FULL(rr_source, ifindex_source, flags_source, source) {
547973de
LP
538
539 r = dns_resource_key_equal(rr_source->key, key);
540 if (r < 0)
541 return r;
542 if (r == 0)
543 continue;
544
545 /* Make space for at least one entry */
546 r = dns_answer_reserve_or_clone(a, 1);
547 if (r < 0)
548 return r;
934e9b10 549
105e1512 550 r = dns_answer_add(*a, rr_source, ifindex_source, flags_source|or_flags);
547973de
LP
551 if (r < 0)
552 return r;
553 }
554
555 return 0;
934e9b10 556}
af93291c 557
105e1512
LP
558int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
559 int r;
560
561 assert(to);
562 assert(from);
563 assert(key);
564
565 r = dns_answer_copy_by_key(to, *from, key, or_flags);
566 if (r < 0)
567 return r;
568
569 return dns_answer_remove_by_key(from, key);
570}
571
af93291c 572void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
78c6a153 573 DnsAnswerItem *items;
af93291c 574 unsigned i, start, end;
78c6a153
LP
575
576 if (!a)
577 return;
af93291c
LP
578
579 if (a->n_rrs <= 1)
580 return;
581
582 start = 0;
583 end = a->n_rrs-1;
584
585 /* RFC 4795, Section 2.6 suggests we should order entries
586 * depending on whether the sender is a link-local address. */
587
78c6a153 588 items = newa(DnsAnswerItem, a->n_rrs);
af93291c
LP
589 for (i = 0; i < a->n_rrs; i++) {
590
78c6a153
LP
591 if (a->items[i].rr->key->class == DNS_CLASS_IN &&
592 ((a->items[i].rr->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->items[i].rr->a.in_addr) != prefer_link_local) ||
593 (a->items[i].rr->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->items[i].rr->aaaa.in6_addr) != prefer_link_local)))
af93291c 594 /* Order address records that are are not preferred to the end of the array */
78c6a153 595 items[end--] = a->items[i];
af93291c
LP
596 else
597 /* Order all other records to the beginning of the array */
78c6a153 598 items[start++] = a->items[i];
af93291c
LP
599 }
600
601 assert(start == end+1);
78c6a153
LP
602 memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs);
603}
604
605int dns_answer_reserve(DnsAnswer **a, unsigned n_free) {
606 DnsAnswer *n;
607
2f763887
LP
608 assert(a);
609
78c6a153
LP
610 if (n_free <= 0)
611 return 0;
612
613 if (*a) {
614 unsigned ns;
615
616 if ((*a)->n_ref > 1)
617 return -EBUSY;
618
619 ns = (*a)->n_rrs + n_free;
620
621 if ((*a)->n_allocated >= ns)
622 return 0;
623
2f763887
LP
624 /* Allocate more than we need */
625 ns *= 2;
626
78c6a153
LP
627 n = realloc(*a, offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * ns);
628 if (!n)
629 return -ENOMEM;
630
631 n->n_allocated = ns;
632 } else {
633 n = dns_answer_new(n_free);
634 if (!n)
635 return -ENOMEM;
636 }
637
638 *a = n;
639 return 0;
af93291c 640}
547973de
LP
641
642int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free) {
643 _cleanup_(dns_answer_unrefp) DnsAnswer *n = NULL;
644 int r;
645
646 assert(a);
647
648 /* Tries to extend the DnsAnswer object. And if that's not
649 * possibly, since we are not the sole owner, then allocate a
650 * new, appropriately sized one. Either way, after this call
651 * the object will only have a single reference, and has room
652 * for at least the specified number of RRs. */
653
654 r = dns_answer_reserve(a, n_free);
655 if (r != -EBUSY)
656 return r;
657
658 assert(*a);
659
660 n = dns_answer_new(((*a)->n_rrs + n_free) * 2);
661 if (!n)
662 return -ENOMEM;
663
664 r = dns_answer_add_raw_all(n, *a);
665 if (r < 0)
666 return r;
667
668 dns_answer_unref(*a);
669 *a = n;
670 n = NULL;
671
672 return 0;
673}
26156910
LP
674
675void dns_answer_dump(DnsAnswer *answer, FILE *f) {
676 DnsResourceRecord *rr;
677 DnsAnswerFlags flags;
7b50eb2e 678 int ifindex;
26156910
LP
679
680 if (!f)
681 f = stdout;
682
683 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
7b50eb2e 684 const char *t;
26156910
LP
685
686 fputc('\t', f);
687
7b50eb2e
LP
688 t = dns_resource_record_to_string(rr);
689 if (!t) {
26156910
LP
690 log_oom();
691 continue;
692 }
693
694 fputs(t, f);
695
696 if (ifindex != 0 || flags & (DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE|DNS_ANSWER_SHARED_OWNER))
697 fputs("\t;", f);
698
699 if (ifindex != 0)
700 printf(" ifindex=%i", ifindex);
701 if (flags & DNS_ANSWER_AUTHENTICATED)
702 fputs(" authenticated", f);
703 if (flags & DNS_ANSWER_CACHEABLE)
704 fputs(" cachable", f);
705 if (flags & DNS_ANSWER_SHARED_OWNER)
706 fputs(" shared-owner", f);
707
708 fputc('\n', f);
709 }
710}