]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-answer.c
resolved: don't allow RRs with TTL=0 and TTL!=0 in the same RRset
[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_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
528 DnsResourceRecord *rr_source;
529 int ifindex_source, r;
530 DnsAnswerFlags flags_source;
531
532 assert(a);
533 assert(key);
534
535 /* Copy all RRs matching the specified key from source into *a */
536
537 DNS_ANSWER_FOREACH_FULL(rr_source, ifindex_source, flags_source, source) {
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;
549
550 r = dns_answer_add(*a, rr_source, ifindex_source, flags_source|or_flags);
551 if (r < 0)
552 return r;
553 }
554
555 return 0;
556 }
557
558 int 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
572 void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
573 DnsAnswerItem *items;
574 unsigned i, start, end;
575
576 if (!a)
577 return;
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
588 items = newa(DnsAnswerItem, a->n_rrs);
589 for (i = 0; i < a->n_rrs; i++) {
590
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)))
594 /* Order address records that are are not preferred to the end of the array */
595 items[end--] = a->items[i];
596 else
597 /* Order all other records to the beginning of the array */
598 items[start++] = a->items[i];
599 }
600
601 assert(start == end+1);
602 memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs);
603 }
604
605 int dns_answer_reserve(DnsAnswer **a, unsigned n_free) {
606 DnsAnswer *n;
607
608 assert(a);
609
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
624 /* Allocate more than we need */
625 ns *= 2;
626
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;
640 }
641
642 int 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 }
674
675 void dns_answer_dump(DnsAnswer *answer, FILE *f) {
676 DnsResourceRecord *rr;
677 DnsAnswerFlags flags;
678 int ifindex;
679
680 if (!f)
681 f = stdout;
682
683 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
684 const char *t;
685
686 fputc('\t', f);
687
688 t = dns_resource_record_to_string(rr);
689 if (!t) {
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 }