]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-answer.c
update DNSSEC TODO
[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) {
128 /* Entry already exists, keep the entry with
129 * the higher RR, or the one with TTL 0 */
130
78c6a153 131 if (rr->ttl == 0 || (rr->ttl > a->items[i].rr->ttl && a->items[i].rr->ttl != 0)) {
7e8e0422 132 dns_resource_record_ref(rr);
78c6a153
LP
133 dns_resource_record_unref(a->items[i].rr);
134 a->items[i].rr = rr;
7e8e0422
LP
135 }
136
105e1512 137 a->items[i].flags |= flags;
7e8e0422
LP
138 return 0;
139 }
140 }
141
105e1512 142 return dns_answer_add_raw(a, rr, ifindex, flags);
547973de 143}
faa133f3 144
547973de
LP
145static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
146 DnsResourceRecord *rr;
105e1512 147 DnsAnswerFlags flags;
547973de 148 int ifindex, r;
78c6a153 149
105e1512
LP
150 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, b) {
151 r = dns_answer_add(a, rr, ifindex, flags);
547973de
LP
152 if (r < 0)
153 return r;
154 }
155
156 return 0;
157}
158
105e1512 159int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
547973de
LP
160 int r;
161
162 assert(a);
163 assert(rr);
164
165 r = dns_answer_reserve_or_clone(a, 1);
166 if (r < 0)
167 return r;
168
105e1512 169 return dns_answer_add(*a, rr, ifindex, flags);
7e8e0422
LP
170}
171
57f5ad31 172int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) {
8bf52d3d
LP
173 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *soa = NULL;
174
175 soa = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SOA, name);
176 if (!soa)
177 return -ENOMEM;
178
57f5ad31
LP
179 soa->ttl = ttl;
180
8bf52d3d
LP
181 soa->soa.mname = strdup(name);
182 if (!soa->soa.mname)
183 return -ENOMEM;
184
185 soa->soa.rname = strappend("root.", name);
186 if (!soa->soa.rname)
187 return -ENOMEM;
188
189 soa->soa.serial = 1;
190 soa->soa.refresh = 1;
191 soa->soa.retry = 1;
192 soa->soa.expire = 1;
57f5ad31 193 soa->soa.minimum = ttl;
8bf52d3d 194
105e1512 195 return dns_answer_add(a, soa, 0, DNS_ANSWER_AUTHENTICATED);
8bf52d3d
LP
196}
197
105e1512
LP
198int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
199 DnsAnswerFlags flags = 0, i_flags;
547973de 200 DnsResourceRecord *i;
105e1512 201 bool found = false;
7e8e0422
LP
202 int r;
203
7e8e0422
LP
204 assert(key);
205
105e1512 206 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
547973de 207 r = dns_resource_key_match_rr(key, i, NULL);
7e8e0422
LP
208 if (r < 0)
209 return r;
105e1512
LP
210 if (r == 0)
211 continue;
212
213 if (!ret_flags)
7e8e0422 214 return 1;
105e1512
LP
215
216 if (found)
217 flags &= i_flags;
218 else {
219 flags = i_flags;
220 found = true;
221 }
7e8e0422
LP
222 }
223
105e1512
LP
224 if (ret_flags)
225 *ret_flags = flags;
226
227 return found;
7e8e0422
LP
228}
229
105e1512
LP
230int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *ret_flags) {
231 DnsAnswerFlags flags = 0, i_flags;
547973de 232 DnsResourceRecord *i;
105e1512 233 bool found = false;
547973de 234 int r;
5eefe544 235
547973de 236 assert(rr);
5eefe544 237
105e1512 238 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
547973de
LP
239 r = dns_resource_record_equal(i, rr);
240 if (r < 0)
241 return r;
105e1512
LP
242 if (r == 0)
243 continue;
244
245 if (!ret_flags)
547973de 246 return 1;
105e1512
LP
247
248 if (found)
249 flags &= i_flags;
250 else {
251 flags = i_flags;
252 found = true;
253 }
547973de 254 }
5eefe544 255
105e1512
LP
256 if (ret_flags)
257 *ret_flags = flags;
258
259 return found;
260}
261
262int dns_answer_contains_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
263 DnsAnswerFlags flags = 0, i_flags;
264 DnsResourceRecord *i;
265 bool found = false;
266 int r;
267
268 assert(key);
269
270 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
271 r = dns_resource_key_equal(i->key, key);
272 if (r < 0)
273 return r;
274 if (r == 0)
275 continue;
276
277 if (!ret_flags)
278 return true;
279
280 if (found)
281 flags &= i_flags;
282 else {
283 flags = i_flags;
284 found = true;
285 }
286 }
287
288 if (ret_flags)
289 *ret_flags = flags;
290
291 return found;
292}
293
294int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
295 DnsResourceRecord *i;
296
297 DNS_ANSWER_FOREACH(i, a) {
298 if (IN_SET(i->key->type, DNS_TYPE_NSEC, DNS_TYPE_NSEC3))
299 return true;
300 }
301
302 return false;
5eefe544
TG
303}
304
fd009cd8 305int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
81f7fc5e
LP
306 DnsResourceRecord *rr, *soa = NULL;
307 DnsAnswerFlags rr_flags, soa_flags = 0;
29c1519e 308 int r;
7e8e0422 309
7e8e0422 310 assert(key);
7e8e0422 311
0f05c387
LP
312 /* For a SOA record we can never find a matching SOA record */
313 if (key->type == DNS_TYPE_SOA)
314 return 0;
315
fd009cd8 316 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
29c1519e
LP
317 r = dns_resource_key_match_soa(key, rr->key);
318 if (r < 0)
319 return r;
320 if (r > 0) {
81f7fc5e
LP
321
322 if (soa) {
323 r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(soa->key));
324 if (r < 0)
325 return r;
326 if (r > 0)
327 continue;
328 }
329
330 soa = rr;
331 soa_flags = rr_flags;
7e8e0422
LP
332 }
333 }
334
81f7fc5e
LP
335 if (!soa)
336 return 0;
337
338 if (ret)
339 *ret = soa;
340 if (flags)
341 *flags = soa_flags;
342
343 return 1;
faa133f3 344}
934e9b10 345
105e1512 346int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
5d27351f 347 DnsResourceRecord *rr;
105e1512 348 DnsAnswerFlags rr_flags;
29c1519e 349 int r;
5d27351f
TG
350
351 assert(key);
352
5d27351f
TG
353 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
354 if (key->type == DNS_TYPE_CNAME || key->type == DNS_TYPE_DNAME)
355 return 0;
356
105e1512 357 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
29c1519e
LP
358 r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
359 if (r < 0)
360 return r;
361 if (r > 0) {
5d27351f
TG
362 if (ret)
363 *ret = rr;
105e1512
LP
364 if (flags)
365 *flags = rr_flags;
5d27351f
TG
366 return 1;
367 }
368 }
369
370 return 0;
371}
372
547973de
LP
373int dns_answer_merge(DnsAnswer *a, DnsAnswer *b, DnsAnswer **ret) {
374 _cleanup_(dns_answer_unrefp) DnsAnswer *k = NULL;
375 int r;
376
377 assert(ret);
378
379 if (dns_answer_size(a) <= 0) {
380 *ret = dns_answer_ref(b);
381 return 0;
382 }
383
384 if (dns_answer_size(b) <= 0) {
385 *ret = dns_answer_ref(a);
386 return 0;
387 }
388
389 k = dns_answer_new(a->n_rrs + b->n_rrs);
390 if (!k)
391 return -ENOMEM;
392
393 r = dns_answer_add_raw_all(k, a);
394 if (r < 0)
395 return r;
396
397 r = dns_answer_add_all(k, b);
398 if (r < 0)
399 return r;
400
401 *ret = k;
402 k = NULL;
403
404 return 0;
405}
406
407int dns_answer_extend(DnsAnswer **a, DnsAnswer *b) {
408 DnsAnswer *merged;
409 int r;
410
411 assert(a);
412
413 r = dns_answer_merge(*a, b, &merged);
414 if (r < 0)
415 return r;
416
417 dns_answer_unref(*a);
418 *a = merged;
419
420 return 0;
421}
422
423int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
424 bool found = false, other = false;
425 DnsResourceRecord *rr;
934e9b10
LP
426 unsigned i;
427 int r;
428
547973de
LP
429 assert(a);
430 assert(key);
934e9b10 431
547973de 432 /* Remove all entries matching the specified key from *a */
934e9b10 433
547973de
LP
434 DNS_ANSWER_FOREACH(rr, *a) {
435 r = dns_resource_key_equal(rr->key, key);
436 if (r < 0)
437 return r;
438 if (r > 0)
439 found = true;
440 else
441 other = true;
442
443 if (found && other)
444 break;
445 }
446
447 if (!found)
448 return 0;
449
450 if (!other) {
451 *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
452 return 1;
934e9b10
LP
453 }
454
547973de
LP
455 if ((*a)->n_ref > 1) {
456 _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
105e1512 457 DnsAnswerFlags flags;
547973de
LP
458 int ifindex;
459
460 copy = dns_answer_new((*a)->n_rrs);
461 if (!copy)
462 return -ENOMEM;
463
105e1512 464 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
547973de 465 r = dns_resource_key_equal(rr->key, key);
934e9b10 466 if (r < 0)
547973de
LP
467 return r;
468 if (r > 0)
469 continue;
470
105e1512 471 r = dns_answer_add_raw(copy, rr, ifindex, flags);
547973de
LP
472 if (r < 0)
473 return r;
934e9b10 474 }
547973de
LP
475
476 dns_answer_unref(*a);
477 *a = copy;
478 copy = NULL;
479
480 return 1;
481 }
482
483 /* Only a single reference, edit in-place */
484
485 i = 0;
486 for (;;) {
487 if (i >= (*a)->n_rrs)
488 break;
489
490 r = dns_resource_key_equal((*a)->items[i].rr->key, key);
491 if (r < 0)
492 return r;
493 if (r > 0) {
494 /* Kill this entry */
495
496 dns_resource_record_unref((*a)->items[i].rr);
497 memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
498 (*a)->n_rrs --;
499 continue;
500
501 } else
502 /* Keep this entry */
503 i++;
934e9b10
LP
504 }
505
547973de
LP
506 return 1;
507}
508
105e1512 509int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
547973de
LP
510 DnsResourceRecord *rr_source;
511 int ifindex_source, r;
105e1512 512 DnsAnswerFlags flags_source;
547973de
LP
513
514 assert(a);
515 assert(key);
516
517 /* Copy all RRs matching the specified key from source into *a */
518
105e1512 519 DNS_ANSWER_FOREACH_FULL(rr_source, ifindex_source, flags_source, source) {
547973de
LP
520
521 r = dns_resource_key_equal(rr_source->key, key);
522 if (r < 0)
523 return r;
524 if (r == 0)
525 continue;
526
527 /* Make space for at least one entry */
528 r = dns_answer_reserve_or_clone(a, 1);
529 if (r < 0)
530 return r;
934e9b10 531
105e1512 532 r = dns_answer_add(*a, rr_source, ifindex_source, flags_source|or_flags);
547973de
LP
533 if (r < 0)
534 return r;
535 }
536
537 return 0;
934e9b10 538}
af93291c 539
105e1512
LP
540int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
541 int r;
542
543 assert(to);
544 assert(from);
545 assert(key);
546
547 r = dns_answer_copy_by_key(to, *from, key, or_flags);
548 if (r < 0)
549 return r;
550
551 return dns_answer_remove_by_key(from, key);
552}
553
af93291c 554void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
78c6a153 555 DnsAnswerItem *items;
af93291c 556 unsigned i, start, end;
78c6a153
LP
557
558 if (!a)
559 return;
af93291c
LP
560
561 if (a->n_rrs <= 1)
562 return;
563
564 start = 0;
565 end = a->n_rrs-1;
566
567 /* RFC 4795, Section 2.6 suggests we should order entries
568 * depending on whether the sender is a link-local address. */
569
78c6a153 570 items = newa(DnsAnswerItem, a->n_rrs);
af93291c
LP
571 for (i = 0; i < a->n_rrs; i++) {
572
78c6a153
LP
573 if (a->items[i].rr->key->class == DNS_CLASS_IN &&
574 ((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) ||
575 (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 576 /* Order address records that are are not preferred to the end of the array */
78c6a153 577 items[end--] = a->items[i];
af93291c
LP
578 else
579 /* Order all other records to the beginning of the array */
78c6a153 580 items[start++] = a->items[i];
af93291c
LP
581 }
582
583 assert(start == end+1);
78c6a153
LP
584 memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs);
585}
586
587int dns_answer_reserve(DnsAnswer **a, unsigned n_free) {
588 DnsAnswer *n;
589
2f763887
LP
590 assert(a);
591
78c6a153
LP
592 if (n_free <= 0)
593 return 0;
594
595 if (*a) {
596 unsigned ns;
597
598 if ((*a)->n_ref > 1)
599 return -EBUSY;
600
601 ns = (*a)->n_rrs + n_free;
602
603 if ((*a)->n_allocated >= ns)
604 return 0;
605
2f763887
LP
606 /* Allocate more than we need */
607 ns *= 2;
608
78c6a153
LP
609 n = realloc(*a, offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * ns);
610 if (!n)
611 return -ENOMEM;
612
613 n->n_allocated = ns;
614 } else {
615 n = dns_answer_new(n_free);
616 if (!n)
617 return -ENOMEM;
618 }
619
620 *a = n;
621 return 0;
af93291c 622}
547973de
LP
623
624int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free) {
625 _cleanup_(dns_answer_unrefp) DnsAnswer *n = NULL;
626 int r;
627
628 assert(a);
629
630 /* Tries to extend the DnsAnswer object. And if that's not
631 * possibly, since we are not the sole owner, then allocate a
632 * new, appropriately sized one. Either way, after this call
633 * the object will only have a single reference, and has room
634 * for at least the specified number of RRs. */
635
636 r = dns_answer_reserve(a, n_free);
637 if (r != -EBUSY)
638 return r;
639
640 assert(*a);
641
642 n = dns_answer_new(((*a)->n_rrs + n_free) * 2);
643 if (!n)
644 return -ENOMEM;
645
646 r = dns_answer_add_raw_all(n, *a);
647 if (r < 0)
648 return r;
649
650 dns_answer_unref(*a);
651 *a = n;
652 n = NULL;
653
654 return 0;
655}
26156910
LP
656
657void dns_answer_dump(DnsAnswer *answer, FILE *f) {
658 DnsResourceRecord *rr;
659 DnsAnswerFlags flags;
7b50eb2e 660 int ifindex;
26156910
LP
661
662 if (!f)
663 f = stdout;
664
665 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
7b50eb2e 666 const char *t;
26156910
LP
667
668 fputc('\t', f);
669
7b50eb2e
LP
670 t = dns_resource_record_to_string(rr);
671 if (!t) {
26156910
LP
672 log_oom();
673 continue;
674 }
675
676 fputs(t, f);
677
678 if (ifindex != 0 || flags & (DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE|DNS_ANSWER_SHARED_OWNER))
679 fputs("\t;", f);
680
681 if (ifindex != 0)
682 printf(" ifindex=%i", ifindex);
683 if (flags & DNS_ANSWER_AUTHENTICATED)
684 fputs(" authenticated", f);
685 if (flags & DNS_ANSWER_CACHEABLE)
686 fputs(" cachable", f);
687 if (flags & DNS_ANSWER_SHARED_OWNER)
688 fputs(" shared-owner", f);
689
690 fputc('\n', f);
691 }
692}