]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-answer.c
hwdb: remove references to udevadm info /dev/input/event*
[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;
307 DnsAnswerFlags rr_flags;
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 if (ret)
322 *ret = rr;
323 if (flags)
324 *flags = rr_flags;
325 return 1;
326 }
327 }
328
329 return 0;
330 }
331
332 int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
333 DnsResourceRecord *rr;
334 DnsAnswerFlags rr_flags;
335 int r;
336
337 assert(key);
338
339 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
340 if (key->type == DNS_TYPE_CNAME || key->type == DNS_TYPE_DNAME)
341 return 0;
342
343 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
344 r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
345 if (r < 0)
346 return r;
347 if (r > 0) {
348 if (ret)
349 *ret = rr;
350 if (flags)
351 *flags = rr_flags;
352 return 1;
353 }
354 }
355
356 return 0;
357 }
358
359 int dns_answer_merge(DnsAnswer *a, DnsAnswer *b, DnsAnswer **ret) {
360 _cleanup_(dns_answer_unrefp) DnsAnswer *k = NULL;
361 int r;
362
363 assert(ret);
364
365 if (dns_answer_size(a) <= 0) {
366 *ret = dns_answer_ref(b);
367 return 0;
368 }
369
370 if (dns_answer_size(b) <= 0) {
371 *ret = dns_answer_ref(a);
372 return 0;
373 }
374
375 k = dns_answer_new(a->n_rrs + b->n_rrs);
376 if (!k)
377 return -ENOMEM;
378
379 r = dns_answer_add_raw_all(k, a);
380 if (r < 0)
381 return r;
382
383 r = dns_answer_add_all(k, b);
384 if (r < 0)
385 return r;
386
387 *ret = k;
388 k = NULL;
389
390 return 0;
391 }
392
393 int dns_answer_extend(DnsAnswer **a, DnsAnswer *b) {
394 DnsAnswer *merged;
395 int r;
396
397 assert(a);
398
399 r = dns_answer_merge(*a, b, &merged);
400 if (r < 0)
401 return r;
402
403 dns_answer_unref(*a);
404 *a = merged;
405
406 return 0;
407 }
408
409 int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
410 bool found = false, other = false;
411 DnsResourceRecord *rr;
412 unsigned i;
413 int r;
414
415 assert(a);
416 assert(key);
417
418 /* Remove all entries matching the specified key from *a */
419
420 DNS_ANSWER_FOREACH(rr, *a) {
421 r = dns_resource_key_equal(rr->key, key);
422 if (r < 0)
423 return r;
424 if (r > 0)
425 found = true;
426 else
427 other = true;
428
429 if (found && other)
430 break;
431 }
432
433 if (!found)
434 return 0;
435
436 if (!other) {
437 *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
438 return 1;
439 }
440
441 if ((*a)->n_ref > 1) {
442 _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
443 DnsAnswerFlags flags;
444 int ifindex;
445
446 copy = dns_answer_new((*a)->n_rrs);
447 if (!copy)
448 return -ENOMEM;
449
450 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
451 r = dns_resource_key_equal(rr->key, key);
452 if (r < 0)
453 return r;
454 if (r > 0)
455 continue;
456
457 r = dns_answer_add_raw(copy, rr, ifindex, flags);
458 if (r < 0)
459 return r;
460 }
461
462 dns_answer_unref(*a);
463 *a = copy;
464 copy = NULL;
465
466 return 1;
467 }
468
469 /* Only a single reference, edit in-place */
470
471 i = 0;
472 for (;;) {
473 if (i >= (*a)->n_rrs)
474 break;
475
476 r = dns_resource_key_equal((*a)->items[i].rr->key, key);
477 if (r < 0)
478 return r;
479 if (r > 0) {
480 /* Kill this entry */
481
482 dns_resource_record_unref((*a)->items[i].rr);
483 memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
484 (*a)->n_rrs --;
485 continue;
486
487 } else
488 /* Keep this entry */
489 i++;
490 }
491
492 return 1;
493 }
494
495 int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
496 DnsResourceRecord *rr_source;
497 int ifindex_source, r;
498 DnsAnswerFlags flags_source;
499
500 assert(a);
501 assert(key);
502
503 /* Copy all RRs matching the specified key from source into *a */
504
505 DNS_ANSWER_FOREACH_FULL(rr_source, ifindex_source, flags_source, source) {
506
507 r = dns_resource_key_equal(rr_source->key, key);
508 if (r < 0)
509 return r;
510 if (r == 0)
511 continue;
512
513 /* Make space for at least one entry */
514 r = dns_answer_reserve_or_clone(a, 1);
515 if (r < 0)
516 return r;
517
518 r = dns_answer_add(*a, rr_source, ifindex_source, flags_source|or_flags);
519 if (r < 0)
520 return r;
521 }
522
523 return 0;
524 }
525
526 int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
527 int r;
528
529 assert(to);
530 assert(from);
531 assert(key);
532
533 r = dns_answer_copy_by_key(to, *from, key, or_flags);
534 if (r < 0)
535 return r;
536
537 return dns_answer_remove_by_key(from, key);
538 }
539
540 void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
541 DnsAnswerItem *items;
542 unsigned i, start, end;
543
544 if (!a)
545 return;
546
547 if (a->n_rrs <= 1)
548 return;
549
550 start = 0;
551 end = a->n_rrs-1;
552
553 /* RFC 4795, Section 2.6 suggests we should order entries
554 * depending on whether the sender is a link-local address. */
555
556 items = newa(DnsAnswerItem, a->n_rrs);
557 for (i = 0; i < a->n_rrs; i++) {
558
559 if (a->items[i].rr->key->class == DNS_CLASS_IN &&
560 ((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) ||
561 (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)))
562 /* Order address records that are are not preferred to the end of the array */
563 items[end--] = a->items[i];
564 else
565 /* Order all other records to the beginning of the array */
566 items[start++] = a->items[i];
567 }
568
569 assert(start == end+1);
570 memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs);
571 }
572
573 int dns_answer_reserve(DnsAnswer **a, unsigned n_free) {
574 DnsAnswer *n;
575
576 assert(a);
577
578 if (n_free <= 0)
579 return 0;
580
581 if (*a) {
582 unsigned ns;
583
584 if ((*a)->n_ref > 1)
585 return -EBUSY;
586
587 ns = (*a)->n_rrs + n_free;
588
589 if ((*a)->n_allocated >= ns)
590 return 0;
591
592 /* Allocate more than we need */
593 ns *= 2;
594
595 n = realloc(*a, offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * ns);
596 if (!n)
597 return -ENOMEM;
598
599 n->n_allocated = ns;
600 } else {
601 n = dns_answer_new(n_free);
602 if (!n)
603 return -ENOMEM;
604 }
605
606 *a = n;
607 return 0;
608 }
609
610 int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free) {
611 _cleanup_(dns_answer_unrefp) DnsAnswer *n = NULL;
612 int r;
613
614 assert(a);
615
616 /* Tries to extend the DnsAnswer object. And if that's not
617 * possibly, since we are not the sole owner, then allocate a
618 * new, appropriately sized one. Either way, after this call
619 * the object will only have a single reference, and has room
620 * for at least the specified number of RRs. */
621
622 r = dns_answer_reserve(a, n_free);
623 if (r != -EBUSY)
624 return r;
625
626 assert(*a);
627
628 n = dns_answer_new(((*a)->n_rrs + n_free) * 2);
629 if (!n)
630 return -ENOMEM;
631
632 r = dns_answer_add_raw_all(n, *a);
633 if (r < 0)
634 return r;
635
636 dns_answer_unref(*a);
637 *a = n;
638 n = NULL;
639
640 return 0;
641 }
642
643 void dns_answer_dump(DnsAnswer *answer, FILE *f) {
644 DnsResourceRecord *rr;
645 DnsAnswerFlags flags;
646 int ifindex, r;
647
648 if (!f)
649 f = stdout;
650
651 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
652 _cleanup_free_ char *t = NULL;
653
654 fputc('\t', f);
655
656 r = dns_resource_record_to_string(rr, &t);
657 if (r < 0) {
658 log_oom();
659 continue;
660 }
661
662 fputs(t, f);
663
664 if (ifindex != 0 || flags & (DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE|DNS_ANSWER_SHARED_OWNER))
665 fputs("\t;", f);
666
667 if (ifindex != 0)
668 printf(" ifindex=%i", ifindex);
669 if (flags & DNS_ANSWER_AUTHENTICATED)
670 fputs(" authenticated", f);
671 if (flags & DNS_ANSWER_CACHEABLE)
672 fputs(" cachable", f);
673 if (flags & DNS_ANSWER_SHARED_OWNER)
674 fputs(" shared-owner", f);
675
676 fputc('\n', f);
677 }
678 }