]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-answer.c
Merge pull request #2287 from dandedrick/journal-gatewayd-timeout-fix
[thirdparty/systemd.git] / src / resolve / resolved-dns-answer.c
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
22 #include "alloc-util.h"
23 #include "dns-domain.h"
24 #include "resolved-dns-answer.h"
25 #include "resolved-dns-dnssec.h"
26 #include "string-util.h"
27
28 DnsAnswer *dns_answer_new(unsigned n) {
29 DnsAnswer *a;
30
31 a = malloc0(offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * n);
32 if (!a)
33 return NULL;
34
35 a->n_ref = 1;
36 a->n_allocated = n;
37
38 return a;
39 }
40
41 DnsAnswer *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
50 static 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
62 DnsAnswer *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) {
69 dns_answer_flush(a);
70 free(a);
71 } else
72 a->n_ref--;
73
74 return NULL;
75 }
76
77 static int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
78 assert(rr);
79
80 if (!a)
81 return -ENOSPC;
82
83 if (a->n_rrs >= a->n_allocated)
84 return -ENOSPC;
85
86 a->items[a->n_rrs++] = (DnsAnswerItem) {
87 .rr = dns_resource_record_ref(rr),
88 .ifindex = ifindex,
89 .flags = flags,
90 };
91
92 return 1;
93 }
94
95 static int dns_answer_add_raw_all(DnsAnswer *a, DnsAnswer *source) {
96 DnsResourceRecord *rr;
97 DnsAnswerFlags flags;
98 int ifindex, r;
99
100 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, source) {
101 r = dns_answer_add_raw(a, rr, ifindex, flags);
102 if (r < 0)
103 return r;
104 }
105
106 return 0;
107 }
108
109 int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
110 unsigned i;
111 int r;
112
113 assert(rr);
114
115 if (!a)
116 return -ENOSPC;
117 if (a->n_ref > 1)
118 return -EBUSY;
119
120 for (i = 0; i < a->n_rrs; i++) {
121 if (a->items[i].ifindex != ifindex)
122 continue;
123
124 r = dns_resource_record_equal(a->items[i].rr, rr);
125 if (r < 0)
126 return r;
127 if (r > 0) {
128 /* Don't mix contradicting TTLs (see below) */
129 if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
130 return -EINVAL;
131
132 /* Entry already exists, keep the entry with
133 * the higher RR. */
134 if (rr->ttl > a->items[i].rr->ttl) {
135 dns_resource_record_ref(rr);
136 dns_resource_record_unref(a->items[i].rr);
137 a->items[i].rr = rr;
138 }
139
140 a->items[i].flags |= flags;
141 return 0;
142 }
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 }
158 }
159
160 return dns_answer_add_raw(a, rr, ifindex, flags);
161 }
162
163 static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
164 DnsResourceRecord *rr;
165 DnsAnswerFlags flags;
166 int ifindex, r;
167
168 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, b) {
169 r = dns_answer_add(a, rr, ifindex, flags);
170 if (r < 0)
171 return r;
172 }
173
174 return 0;
175 }
176
177 int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
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
187 return dns_answer_add(*a, rr, ifindex, flags);
188 }
189
190 int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) {
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
197 soa->ttl = ttl;
198
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;
211 soa->soa.minimum = ttl;
212
213 return dns_answer_add(a, soa, 0, DNS_ANSWER_AUTHENTICATED);
214 }
215
216 int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
217 DnsAnswerFlags flags = 0, i_flags;
218 DnsResourceRecord *i;
219 bool found = false;
220 int r;
221
222 assert(key);
223
224 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
225 r = dns_resource_key_match_rr(key, i, NULL);
226 if (r < 0)
227 return r;
228 if (r == 0)
229 continue;
230
231 if (!ret_flags)
232 return 1;
233
234 if (found)
235 flags &= i_flags;
236 else {
237 flags = i_flags;
238 found = true;
239 }
240 }
241
242 if (ret_flags)
243 *ret_flags = flags;
244
245 return found;
246 }
247
248 int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *ret_flags) {
249 DnsAnswerFlags flags = 0, i_flags;
250 DnsResourceRecord *i;
251 bool found = false;
252 int r;
253
254 assert(rr);
255
256 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
257 r = dns_resource_record_equal(i, rr);
258 if (r < 0)
259 return r;
260 if (r == 0)
261 continue;
262
263 if (!ret_flags)
264 return 1;
265
266 if (found)
267 flags &= i_flags;
268 else {
269 flags = i_flags;
270 found = true;
271 }
272 }
273
274 if (ret_flags)
275 *ret_flags = flags;
276
277 return found;
278 }
279
280 int 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
312 int 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;
321 }
322
323 int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
324 DnsResourceRecord *rr, *soa = NULL;
325 DnsAnswerFlags rr_flags, soa_flags = 0;
326 int r;
327
328 assert(key);
329
330 /* For a SOA record we can never find a matching SOA record */
331 if (key->type == DNS_TYPE_SOA)
332 return 0;
333
334 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
335 r = dns_resource_key_match_soa(key, rr->key);
336 if (r < 0)
337 return r;
338 if (r > 0) {
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;
350 }
351 }
352
353 if (!soa)
354 return 0;
355
356 if (ret)
357 *ret = soa;
358 if (flags)
359 *flags = soa_flags;
360
361 return 1;
362 }
363
364 int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
365 DnsResourceRecord *rr;
366 DnsAnswerFlags rr_flags;
367 int r;
368
369 assert(key);
370
371 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
372 if (!dns_type_may_redirect(key->type))
373 return 0;
374
375 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
376 r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
377 if (r < 0)
378 return r;
379 if (r > 0) {
380 if (ret)
381 *ret = rr;
382 if (flags)
383 *flags = rr_flags;
384 return 1;
385 }
386 }
387
388 return 0;
389 }
390
391 int 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
425 int 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
441 int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
442 bool found = false, other = false;
443 DnsResourceRecord *rr;
444 unsigned i;
445 int r;
446
447 assert(a);
448 assert(key);
449
450 /* Remove all entries matching the specified key from *a */
451
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;
471 }
472
473 if ((*a)->n_ref > 1) {
474 _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
475 DnsAnswerFlags flags;
476 int ifindex;
477
478 copy = dns_answer_new((*a)->n_rrs);
479 if (!copy)
480 return -ENOMEM;
481
482 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
483 r = dns_resource_key_equal(rr->key, key);
484 if (r < 0)
485 return r;
486 if (r > 0)
487 continue;
488
489 r = dns_answer_add_raw(copy, rr, ifindex, flags);
490 if (r < 0)
491 return r;
492 }
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++;
522 }
523
524 return 1;
525 }
526
527 int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rm) {
528 bool found = false, other = false;
529 DnsResourceRecord *rr;
530 unsigned i;
531 int r;
532
533 assert(a);
534 assert(rm);
535
536 /* Remove all entries matching the specified RR from *a */
537
538 DNS_ANSWER_FOREACH(rr, *a) {
539 r = dns_resource_record_equal(rr, rm);
540 if (r < 0)
541 return r;
542 if (r > 0)
543 found = true;
544 else
545 other = true;
546
547 if (found && other)
548 break;
549 }
550
551 if (!found)
552 return 0;
553
554 if (!other) {
555 *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
556 return 1;
557 }
558
559 if ((*a)->n_ref > 1) {
560 _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
561 DnsAnswerFlags flags;
562 int ifindex;
563
564 copy = dns_answer_new((*a)->n_rrs);
565 if (!copy)
566 return -ENOMEM;
567
568 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
569 r = dns_resource_record_equal(rr, rm);
570 if (r < 0)
571 return r;
572 if (r > 0)
573 continue;
574
575 r = dns_answer_add_raw(copy, rr, ifindex, flags);
576 if (r < 0)
577 return r;
578 }
579
580 dns_answer_unref(*a);
581 *a = copy;
582 copy = NULL;
583
584 return 1;
585 }
586
587 /* Only a single reference, edit in-place */
588
589 i = 0;
590 for (;;) {
591 if (i >= (*a)->n_rrs)
592 break;
593
594 r = dns_resource_record_equal((*a)->items[i].rr, rm);
595 if (r < 0)
596 return r;
597 if (r > 0) {
598 /* Kill this entry */
599
600 dns_resource_record_unref((*a)->items[i].rr);
601 memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
602 (*a)->n_rrs --;
603 continue;
604
605 } else
606 /* Keep this entry */
607 i++;
608 }
609
610 return 1;
611 }
612
613 int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
614 DnsResourceRecord *rr_source;
615 int ifindex_source, r;
616 DnsAnswerFlags flags_source;
617
618 assert(a);
619 assert(key);
620
621 /* Copy all RRs matching the specified key from source into *a */
622
623 DNS_ANSWER_FOREACH_FULL(rr_source, ifindex_source, flags_source, source) {
624
625 r = dns_resource_key_equal(rr_source->key, key);
626 if (r < 0)
627 return r;
628 if (r == 0)
629 continue;
630
631 /* Make space for at least one entry */
632 r = dns_answer_reserve_or_clone(a, 1);
633 if (r < 0)
634 return r;
635
636 r = dns_answer_add(*a, rr_source, ifindex_source, flags_source|or_flags);
637 if (r < 0)
638 return r;
639 }
640
641 return 0;
642 }
643
644 int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
645 int r;
646
647 assert(to);
648 assert(from);
649 assert(key);
650
651 r = dns_answer_copy_by_key(to, *from, key, or_flags);
652 if (r < 0)
653 return r;
654
655 return dns_answer_remove_by_key(from, key);
656 }
657
658 void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
659 DnsAnswerItem *items;
660 unsigned i, start, end;
661
662 if (!a)
663 return;
664
665 if (a->n_rrs <= 1)
666 return;
667
668 start = 0;
669 end = a->n_rrs-1;
670
671 /* RFC 4795, Section 2.6 suggests we should order entries
672 * depending on whether the sender is a link-local address. */
673
674 items = newa(DnsAnswerItem, a->n_rrs);
675 for (i = 0; i < a->n_rrs; i++) {
676
677 if (a->items[i].rr->key->class == DNS_CLASS_IN &&
678 ((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) ||
679 (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)))
680 /* Order address records that are are not preferred to the end of the array */
681 items[end--] = a->items[i];
682 else
683 /* Order all other records to the beginning of the array */
684 items[start++] = a->items[i];
685 }
686
687 assert(start == end+1);
688 memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs);
689 }
690
691 int dns_answer_reserve(DnsAnswer **a, unsigned n_free) {
692 DnsAnswer *n;
693
694 assert(a);
695
696 if (n_free <= 0)
697 return 0;
698
699 if (*a) {
700 unsigned ns;
701
702 if ((*a)->n_ref > 1)
703 return -EBUSY;
704
705 ns = (*a)->n_rrs + n_free;
706
707 if ((*a)->n_allocated >= ns)
708 return 0;
709
710 /* Allocate more than we need */
711 ns *= 2;
712
713 n = realloc(*a, offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * ns);
714 if (!n)
715 return -ENOMEM;
716
717 n->n_allocated = ns;
718 } else {
719 n = dns_answer_new(n_free);
720 if (!n)
721 return -ENOMEM;
722 }
723
724 *a = n;
725 return 0;
726 }
727
728 int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free) {
729 _cleanup_(dns_answer_unrefp) DnsAnswer *n = NULL;
730 int r;
731
732 assert(a);
733
734 /* Tries to extend the DnsAnswer object. And if that's not
735 * possibly, since we are not the sole owner, then allocate a
736 * new, appropriately sized one. Either way, after this call
737 * the object will only have a single reference, and has room
738 * for at least the specified number of RRs. */
739
740 r = dns_answer_reserve(a, n_free);
741 if (r != -EBUSY)
742 return r;
743
744 assert(*a);
745
746 n = dns_answer_new(((*a)->n_rrs + n_free) * 2);
747 if (!n)
748 return -ENOMEM;
749
750 r = dns_answer_add_raw_all(n, *a);
751 if (r < 0)
752 return r;
753
754 dns_answer_unref(*a);
755 *a = n;
756 n = NULL;
757
758 return 0;
759 }
760
761 void dns_answer_dump(DnsAnswer *answer, FILE *f) {
762 DnsResourceRecord *rr;
763 DnsAnswerFlags flags;
764 int ifindex;
765
766 if (!f)
767 f = stdout;
768
769 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
770 const char *t;
771
772 fputc('\t', f);
773
774 t = dns_resource_record_to_string(rr);
775 if (!t) {
776 log_oom();
777 continue;
778 }
779
780 fputs(t, f);
781
782 if (ifindex != 0 || flags & (DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE|DNS_ANSWER_SHARED_OWNER))
783 fputs("\t;", f);
784
785 if (ifindex != 0)
786 printf(" ifindex=%i", ifindex);
787 if (flags & DNS_ANSWER_AUTHENTICATED)
788 fputs(" authenticated", f);
789 if (flags & DNS_ANSWER_CACHEABLE)
790 fputs(" cachable", f);
791 if (flags & DNS_ANSWER_SHARED_OWNER)
792 fputs(" shared-owner", f);
793
794 fputc('\n', f);
795 }
796 }