]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-answer.c
Merge pull request #2229 from cjmayo/m500
[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 /* Entry already exists, keep the entry with
129 * the higher RR, or the one with TTL 0 */
130
131 if (rr->ttl == 0 || (rr->ttl > a->items[i].rr->ttl && a->items[i].rr->ttl != 0)) {
132 dns_resource_record_ref(rr);
133 dns_resource_record_unref(a->items[i].rr);
134 a->items[i].rr = rr;
135 }
136
137 a->items[i].flags |= flags;
138 return 0;
139 }
140 }
141
142 return dns_answer_add_raw(a, rr, ifindex, flags);
143 }
144
145 static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
146 DnsResourceRecord *rr;
147 DnsAnswerFlags flags;
148 int ifindex, r;
149
150 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, b) {
151 r = dns_answer_add(a, rr, ifindex, flags);
152 if (r < 0)
153 return r;
154 }
155
156 return 0;
157 }
158
159 int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
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
169 return dns_answer_add(*a, rr, ifindex, flags);
170 }
171
172 int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) {
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
179 soa->ttl = ttl;
180
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;
193 soa->soa.minimum = ttl;
194
195 return dns_answer_add(a, soa, 0, DNS_ANSWER_AUTHENTICATED);
196 }
197
198 int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
199 DnsAnswerFlags flags = 0, i_flags;
200 DnsResourceRecord *i;
201 bool found = false;
202 int r;
203
204 assert(key);
205
206 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
207 r = dns_resource_key_match_rr(key, i, NULL);
208 if (r < 0)
209 return r;
210 if (r == 0)
211 continue;
212
213 if (!ret_flags)
214 return 1;
215
216 if (found)
217 flags &= i_flags;
218 else {
219 flags = i_flags;
220 found = true;
221 }
222 }
223
224 if (ret_flags)
225 *ret_flags = flags;
226
227 return found;
228 }
229
230 int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *ret_flags) {
231 DnsAnswerFlags flags = 0, i_flags;
232 DnsResourceRecord *i;
233 bool found = false;
234 int r;
235
236 assert(rr);
237
238 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
239 r = dns_resource_record_equal(i, rr);
240 if (r < 0)
241 return r;
242 if (r == 0)
243 continue;
244
245 if (!ret_flags)
246 return 1;
247
248 if (found)
249 flags &= i_flags;
250 else {
251 flags = i_flags;
252 found = true;
253 }
254 }
255
256 if (ret_flags)
257 *ret_flags = flags;
258
259 return found;
260 }
261
262 int 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
294 int 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;
303 }
304
305 int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
306 DnsResourceRecord *rr, *soa = NULL;
307 DnsAnswerFlags rr_flags, soa_flags = 0;
308 int r;
309
310 assert(key);
311
312 /* For a SOA record we can never find a matching SOA record */
313 if (key->type == DNS_TYPE_SOA)
314 return 0;
315
316 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
317 r = dns_resource_key_match_soa(key, rr->key);
318 if (r < 0)
319 return r;
320 if (r > 0) {
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;
332 }
333 }
334
335 if (!soa)
336 return 0;
337
338 if (ret)
339 *ret = soa;
340 if (flags)
341 *flags = soa_flags;
342
343 return 1;
344 }
345
346 int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
347 DnsResourceRecord *rr;
348 DnsAnswerFlags rr_flags;
349 int r;
350
351 assert(key);
352
353 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
354 if (!dns_type_may_redirect(key->type))
355 return 0;
356
357 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
358 r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
359 if (r < 0)
360 return r;
361 if (r > 0) {
362 if (ret)
363 *ret = rr;
364 if (flags)
365 *flags = rr_flags;
366 return 1;
367 }
368 }
369
370 return 0;
371 }
372
373 int 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
407 int 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
423 int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
424 bool found = false, other = false;
425 DnsResourceRecord *rr;
426 unsigned i;
427 int r;
428
429 assert(a);
430 assert(key);
431
432 /* Remove all entries matching the specified key from *a */
433
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;
453 }
454
455 if ((*a)->n_ref > 1) {
456 _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
457 DnsAnswerFlags flags;
458 int ifindex;
459
460 copy = dns_answer_new((*a)->n_rrs);
461 if (!copy)
462 return -ENOMEM;
463
464 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
465 r = dns_resource_key_equal(rr->key, key);
466 if (r < 0)
467 return r;
468 if (r > 0)
469 continue;
470
471 r = dns_answer_add_raw(copy, rr, ifindex, flags);
472 if (r < 0)
473 return r;
474 }
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++;
504 }
505
506 return 1;
507 }
508
509 int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
510 DnsResourceRecord *rr_source;
511 int ifindex_source, r;
512 DnsAnswerFlags flags_source;
513
514 assert(a);
515 assert(key);
516
517 /* Copy all RRs matching the specified key from source into *a */
518
519 DNS_ANSWER_FOREACH_FULL(rr_source, ifindex_source, flags_source, source) {
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;
531
532 r = dns_answer_add(*a, rr_source, ifindex_source, flags_source|or_flags);
533 if (r < 0)
534 return r;
535 }
536
537 return 0;
538 }
539
540 int 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
554 void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
555 DnsAnswerItem *items;
556 unsigned i, start, end;
557
558 if (!a)
559 return;
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
570 items = newa(DnsAnswerItem, a->n_rrs);
571 for (i = 0; i < a->n_rrs; i++) {
572
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)))
576 /* Order address records that are are not preferred to the end of the array */
577 items[end--] = a->items[i];
578 else
579 /* Order all other records to the beginning of the array */
580 items[start++] = a->items[i];
581 }
582
583 assert(start == end+1);
584 memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs);
585 }
586
587 int dns_answer_reserve(DnsAnswer **a, unsigned n_free) {
588 DnsAnswer *n;
589
590 assert(a);
591
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
606 /* Allocate more than we need */
607 ns *= 2;
608
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;
622 }
623
624 int 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 }
656
657 void dns_answer_dump(DnsAnswer *answer, FILE *f) {
658 DnsResourceRecord *rr;
659 DnsAnswerFlags flags;
660 int ifindex;
661
662 if (!f)
663 f = stdout;
664
665 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
666 const char *t;
667
668 fputc('\t', f);
669
670 t = dns_resource_record_to_string(rr);
671 if (!t) {
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 }