]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-rr.c
basic: add a Bitmap implementation
[thirdparty/systemd.git] / src / resolve / resolved-dns-rr.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 <math.h>
23
24 #include "strv.h"
25
26 #include "dns-domain.h"
27 #include "resolved-dns-rr.h"
28 #include "resolved-dns-packet.h"
29 #include "dns-type.h"
30
31 DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) {
32 DnsResourceKey *k;
33 size_t l;
34
35 assert(name);
36
37 l = strlen(name);
38 k = malloc0(sizeof(DnsResourceKey) + l + 1);
39 if (!k)
40 return NULL;
41
42 k->n_ref = 1;
43 k->class = class;
44 k->type = type;
45
46 strcpy((char*) k + sizeof(DnsResourceKey), name);
47
48 return k;
49 }
50
51 DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name) {
52 DnsResourceKey *k;
53
54 assert(name);
55
56 k = new0(DnsResourceKey, 1);
57 if (!k)
58 return NULL;
59
60 k->n_ref = 1;
61 k->class = class;
62 k->type = type;
63 k->_name = name;
64
65 return k;
66 }
67
68 DnsResourceKey* dns_resource_key_ref(DnsResourceKey *k) {
69
70 if (!k)
71 return NULL;
72
73 assert(k->n_ref > 0);
74 k->n_ref++;
75
76 return k;
77 }
78
79 DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) {
80 if (!k)
81 return NULL;
82
83 assert(k->n_ref > 0);
84
85 if (k->n_ref == 1) {
86 free(k->_name);
87 free(k);
88 } else
89 k->n_ref--;
90
91 return NULL;
92 }
93
94 int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
95 int r;
96
97 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b));
98 if (r <= 0)
99 return r;
100
101 if (a->class != b->class)
102 return 0;
103
104 if (a->type != b->type)
105 return 0;
106
107 return 1;
108 }
109
110 int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr) {
111 assert(key);
112 assert(rr);
113
114 if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
115 return 0;
116
117 if (rr->key->type != key->type && key->type != DNS_TYPE_ANY)
118 return 0;
119
120 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
121 }
122
123 int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr) {
124 assert(key);
125 assert(rr);
126
127 if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
128 return 0;
129
130 if (rr->key->type != DNS_TYPE_CNAME)
131 return 0;
132
133 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
134 }
135
136 static unsigned long dns_resource_key_hash_func(const void *i, const uint8_t hash_key[HASH_KEY_SIZE]) {
137 const DnsResourceKey *k = i;
138 unsigned long ul;
139
140 ul = dns_name_hash_func(DNS_RESOURCE_KEY_NAME(k), hash_key);
141 ul = ul * hash_key[0] + ul + k->class;
142 ul = ul * hash_key[1] + ul + k->type;
143
144 return ul;
145 }
146
147 static int dns_resource_key_compare_func(const void *a, const void *b) {
148 const DnsResourceKey *x = a, *y = b;
149 int ret;
150
151 ret = dns_name_compare_func(DNS_RESOURCE_KEY_NAME(x), DNS_RESOURCE_KEY_NAME(y));
152 if (ret != 0)
153 return ret;
154
155 if (x->type < y->type)
156 return -1;
157 if (x->type > y->type)
158 return 1;
159
160 if (x->class < y->class)
161 return -1;
162 if (x->class > y->class)
163 return 1;
164
165 return 0;
166 }
167
168 const struct hash_ops dns_resource_key_hash_ops = {
169 .hash = dns_resource_key_hash_func,
170 .compare = dns_resource_key_compare_func
171 };
172
173 int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) {
174 char cbuf[strlen("CLASS") + DECIMAL_STR_MAX(uint16_t)], tbuf[strlen("TYPE") + DECIMAL_STR_MAX(uint16_t)];
175 const char *c, *t;
176 char *s;
177
178 c = dns_class_to_string(key->class);
179 if (!c) {
180 sprintf(cbuf, "CLASS%u", key->class);
181 c = cbuf;
182 }
183
184 t = dns_type_to_string(key->type);
185 if (!t){
186 sprintf(tbuf, "TYPE%u", key->type);
187 t = tbuf;
188 }
189
190 if (asprintf(&s, "%s %s %-5s", DNS_RESOURCE_KEY_NAME(key), c, t) < 0)
191 return -ENOMEM;
192
193 *ret = s;
194 return 0;
195 }
196
197 DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
198 DnsResourceRecord *rr;
199
200 rr = new0(DnsResourceRecord, 1);
201 if (!rr)
202 return NULL;
203
204 rr->n_ref = 1;
205 rr->key = dns_resource_key_ref(key);
206
207 return rr;
208 }
209
210 DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name) {
211 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
212
213 key = dns_resource_key_new(class, type, name);
214 if (!key)
215 return NULL;
216
217 return dns_resource_record_new(key);
218 }
219
220 DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr) {
221 if (!rr)
222 return NULL;
223
224 assert(rr->n_ref > 0);
225 rr->n_ref++;
226
227 return rr;
228 }
229
230 DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
231 if (!rr)
232 return NULL;
233
234 assert(rr->n_ref > 0);
235
236 if (rr->n_ref > 1) {
237 rr->n_ref--;
238 return NULL;
239 }
240
241 if (rr->key) {
242 switch(rr->key->type) {
243
244 case DNS_TYPE_SRV:
245 free(rr->srv.name);
246 break;
247
248 case DNS_TYPE_PTR:
249 case DNS_TYPE_NS:
250 case DNS_TYPE_CNAME:
251 case DNS_TYPE_DNAME:
252 free(rr->ptr.name);
253 break;
254
255 case DNS_TYPE_HINFO:
256 free(rr->hinfo.cpu);
257 free(rr->hinfo.os);
258 break;
259
260 case DNS_TYPE_TXT:
261 case DNS_TYPE_SPF:
262 strv_free(rr->txt.strings);
263 break;
264
265 case DNS_TYPE_SOA:
266 free(rr->soa.mname);
267 free(rr->soa.rname);
268 break;
269
270 case DNS_TYPE_MX:
271 free(rr->mx.exchange);
272 break;
273
274 case DNS_TYPE_DS:
275 free(rr->ds.digest);
276 break;
277
278 case DNS_TYPE_SSHFP:
279 free(rr->sshfp.key);
280 break;
281
282 case DNS_TYPE_DNSKEY:
283 free(rr->dnskey.key);
284 break;
285
286 case DNS_TYPE_RRSIG:
287 free(rr->rrsig.signer);
288 free(rr->rrsig.signature);
289 break;
290
291 case DNS_TYPE_LOC:
292 case DNS_TYPE_A:
293 case DNS_TYPE_AAAA:
294 break;
295
296 default:
297 free(rr->generic.data);
298 }
299
300 dns_resource_key_unref(rr->key);
301 }
302
303 free(rr);
304
305 return NULL;
306 }
307
308 int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) {
309 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
310 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
311 _cleanup_free_ char *ptr = NULL;
312 int r;
313
314 assert(ret);
315 assert(address);
316 assert(hostname);
317
318 r = dns_name_reverse(family, address, &ptr);
319 if (r < 0)
320 return r;
321
322 key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, ptr);
323 if (!key)
324 return -ENOMEM;
325
326 ptr = NULL;
327
328 rr = dns_resource_record_new(key);
329 if (!rr)
330 return -ENOMEM;
331
332 rr->ptr.name = strdup(hostname);
333 if (!rr->ptr.name)
334 return -ENOMEM;
335
336 *ret = rr;
337 rr = NULL;
338
339 return 0;
340 }
341
342 int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
343 int r;
344
345 assert(a);
346 assert(b);
347
348 r = dns_resource_key_equal(a->key, b->key);
349 if (r <= 0)
350 return r;
351
352 if (a->unparseable != b->unparseable)
353 return 0;
354
355 switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) {
356
357 case DNS_TYPE_SRV:
358 r = dns_name_equal(a->srv.name, b->srv.name);
359 if (r <= 0)
360 return r;
361
362 return a->srv.priority == b->srv.priority &&
363 a->srv.weight == b->srv.weight &&
364 a->srv.port == b->srv.port;
365
366 case DNS_TYPE_PTR:
367 case DNS_TYPE_NS:
368 case DNS_TYPE_CNAME:
369 case DNS_TYPE_DNAME:
370 return dns_name_equal(a->ptr.name, b->ptr.name);
371
372 case DNS_TYPE_HINFO:
373 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
374 strcaseeq(a->hinfo.os, b->hinfo.os);
375
376 case DNS_TYPE_SPF: /* exactly the same as TXT */
377 case DNS_TYPE_TXT:
378 return strv_equal(a->txt.strings, b->txt.strings);
379
380 case DNS_TYPE_A:
381 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
382
383 case DNS_TYPE_AAAA:
384 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
385
386 case DNS_TYPE_SOA:
387 r = dns_name_equal(a->soa.mname, b->soa.mname);
388 if (r <= 0)
389 return r;
390 r = dns_name_equal(a->soa.rname, b->soa.rname);
391 if (r <= 0)
392 return r;
393
394 return a->soa.serial == b->soa.serial &&
395 a->soa.refresh == b->soa.refresh &&
396 a->soa.retry == b->soa.retry &&
397 a->soa.expire == b->soa.expire &&
398 a->soa.minimum == b->soa.minimum;
399
400 case DNS_TYPE_MX:
401 if (a->mx.priority != b->mx.priority)
402 return 0;
403
404 return dns_name_equal(a->mx.exchange, b->mx.exchange);
405
406 case DNS_TYPE_LOC:
407 assert(a->loc.version == b->loc.version);
408
409 return a->loc.size == b->loc.size &&
410 a->loc.horiz_pre == b->loc.horiz_pre &&
411 a->loc.vert_pre == b->loc.vert_pre &&
412 a->loc.latitude == b->loc.latitude &&
413 a->loc.longitude == b->loc.longitude &&
414 a->loc.altitude == b->loc.altitude;
415
416 case DNS_TYPE_DS:
417 return a->ds.key_tag == b->ds.key_tag &&
418 a->ds.algorithm == b->ds.algorithm &&
419 a->ds.digest_type == b->ds.digest_type &&
420 a->ds.digest_size == b->ds.digest_size &&
421 memcmp(a->ds.digest, b->ds.digest, a->ds.digest_size) == 0;
422
423 case DNS_TYPE_SSHFP:
424 return a->sshfp.algorithm == b->sshfp.algorithm &&
425 a->sshfp.fptype == b->sshfp.fptype &&
426 a->sshfp.key_size == b->sshfp.key_size &&
427 memcmp(a->sshfp.key, b->sshfp.key, a->sshfp.key_size) == 0;
428
429 case DNS_TYPE_DNSKEY:
430 return a->dnskey.zone_key_flag == b->dnskey.zone_key_flag &&
431 a->dnskey.sep_flag == b->dnskey.sep_flag &&
432 a->dnskey.algorithm == b->dnskey.algorithm &&
433 a->dnskey.key_size == b->dnskey.key_size &&
434 memcmp(a->dnskey.key, b->dnskey.key, a->dnskey.key_size) == 0;
435
436 case DNS_TYPE_RRSIG:
437 /* do the fast comparisons first */
438 if (a->rrsig.type_covered != b->rrsig.type_covered ||
439 a->rrsig.algorithm != b->rrsig.algorithm ||
440 a->rrsig.labels != b->rrsig.labels ||
441 a->rrsig.original_ttl != b->rrsig.original_ttl ||
442 a->rrsig.expiration != b->rrsig.expiration ||
443 a->rrsig.inception != b->rrsig.inception ||
444 a->rrsig.key_tag != b->rrsig.key_tag ||
445 a->rrsig.signature_size != b->rrsig.signature_size ||
446 memcmp(a->rrsig.signature, b->rrsig.signature, a->rrsig.signature_size) != 0)
447 return false;
448
449 return dns_name_equal(a->rrsig.signer, b->rrsig.signer);
450
451 default:
452 return a->generic.size == b->generic.size &&
453 memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
454 }
455 }
456
457 static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t altitude,
458 uint8_t size, uint8_t horiz_pre, uint8_t vert_pre) {
459 char *s;
460 char NS = latitude >= 1U<<31 ? 'N' : 'S';
461 char EW = longitude >= 1U<<31 ? 'E' : 'W';
462
463 int lat = latitude >= 1U<<31 ? (int) (latitude - (1U<<31)) : (int) ((1U<<31) - latitude);
464 int lon = longitude >= 1U<<31 ? (int) (longitude - (1U<<31)) : (int) ((1U<<31) - longitude);
465 double alt = altitude >= 10000000u ? altitude - 10000000u : -(double)(10000000u - altitude);
466 double siz = (size >> 4) * exp10((double) (size & 0xF));
467 double hor = (horiz_pre >> 4) * exp10((double) (horiz_pre & 0xF));
468 double ver = (vert_pre >> 4) * exp10((double) (vert_pre & 0xF));
469
470 if (asprintf(&s, "%d %d %.3f %c %d %d %.3f %c %.2fm %.2fm %.2fm %.2fm",
471 (lat / 60000 / 60),
472 (lat / 60000) % 60,
473 (lat % 60000) / 1000.,
474 NS,
475 (lon / 60000 / 60),
476 (lon / 60000) % 60,
477 (lon % 60000) / 1000.,
478 EW,
479 alt / 100.,
480 siz / 100.,
481 hor / 100.,
482 ver / 100.) < 0)
483 return NULL;
484
485 return s;
486 }
487
488 static int format_timestamp_dns(char *buf, size_t l, time_t sec) {
489 struct tm tm;
490
491 assert(buf);
492 assert(l > strlen("YYYYMMDDHHmmSS"));
493
494 if (!gmtime_r(&sec, &tm))
495 return -EINVAL;
496
497 if (strftime(buf, l, "%Y%m%d%H%M%S", &tm) <= 0)
498 return -EINVAL;
499
500 return 0;
501 }
502
503 int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
504 _cleanup_free_ char *k = NULL, *t = NULL;
505 char *s;
506 int r;
507
508 assert(rr);
509
510 r = dns_resource_key_to_string(rr->key, &k);
511 if (r < 0)
512 return r;
513
514 switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
515
516 case DNS_TYPE_SRV:
517 r = asprintf(&s, "%s %u %u %u %s",
518 k,
519 rr->srv.priority,
520 rr->srv.weight,
521 rr->srv.port,
522 strna(rr->srv.name));
523 if (r < 0)
524 return -ENOMEM;
525 break;
526
527 case DNS_TYPE_PTR:
528 case DNS_TYPE_NS:
529 case DNS_TYPE_CNAME:
530 case DNS_TYPE_DNAME:
531 s = strjoin(k, " ", rr->ptr.name, NULL);
532 if (!s)
533 return -ENOMEM;
534
535 break;
536
537 case DNS_TYPE_HINFO:
538 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
539 if (!s)
540 return -ENOMEM;
541 break;
542
543 case DNS_TYPE_SPF: /* exactly the same as TXT */
544 case DNS_TYPE_TXT:
545 t = strv_join_quoted(rr->txt.strings);
546 if (!t)
547 return -ENOMEM;
548
549 s = strjoin(k, " ", t, NULL);
550 if (!s)
551 return -ENOMEM;
552
553 break;
554
555 case DNS_TYPE_A: {
556 _cleanup_free_ char *x = NULL;
557
558 r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
559 if (r < 0)
560 return r;
561
562 s = strjoin(k, " ", x, NULL);
563 if (!s)
564 return -ENOMEM;
565 break;
566 }
567
568 case DNS_TYPE_AAAA:
569 r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &t);
570 if (r < 0)
571 return r;
572
573 s = strjoin(k, " ", t, NULL);
574 if (!s)
575 return -ENOMEM;
576 break;
577
578 case DNS_TYPE_SOA:
579 r = asprintf(&s, "%s %s %s %u %u %u %u %u",
580 k,
581 strna(rr->soa.mname),
582 strna(rr->soa.rname),
583 rr->soa.serial,
584 rr->soa.refresh,
585 rr->soa.retry,
586 rr->soa.expire,
587 rr->soa.minimum);
588 if (r < 0)
589 return -ENOMEM;
590 break;
591
592 case DNS_TYPE_MX:
593 r = asprintf(&s, "%s %u %s",
594 k,
595 rr->mx.priority,
596 rr->mx.exchange);
597 if (r < 0)
598 return -ENOMEM;
599 break;
600
601 case DNS_TYPE_LOC:
602 assert(rr->loc.version == 0);
603
604 t = format_location(rr->loc.latitude,
605 rr->loc.longitude,
606 rr->loc.altitude,
607 rr->loc.size,
608 rr->loc.horiz_pre,
609 rr->loc.vert_pre);
610 if (!t)
611 return -ENOMEM;
612
613 s = strjoin(k, " ", t, NULL);
614 if (!s)
615 return -ENOMEM;
616 break;
617
618 case DNS_TYPE_DS:
619 t = hexmem(rr->ds.digest, rr->ds.digest_size);
620 if (!t)
621 return -ENOMEM;
622
623 r = asprintf(&s, "%s %u %u %u %s",
624 k,
625 rr->ds.key_tag,
626 rr->ds.algorithm,
627 rr->ds.digest_type,
628 t);
629 if (r < 0)
630 return -ENOMEM;
631 break;
632
633 case DNS_TYPE_SSHFP:
634 t = hexmem(rr->sshfp.key, rr->sshfp.key_size);
635 if (!t)
636 return -ENOMEM;
637
638 r = asprintf(&s, "%s %u %u %s",
639 k,
640 rr->sshfp.algorithm,
641 rr->sshfp.fptype,
642 t);
643 if (r < 0)
644 return -ENOMEM;
645 break;
646
647 case DNS_TYPE_DNSKEY: {
648 const char *alg;
649
650 alg = dnssec_algorithm_to_string(rr->dnskey.algorithm);
651
652 t = base64mem(rr->dnskey.key, rr->dnskey.key_size);
653 if (!t)
654 return -ENOMEM;
655
656 r = asprintf(&s, "%s %u 3 %.*s%.*u %s",
657 k,
658 dnskey_to_flags(rr),
659 alg ? -1 : 0, alg,
660 alg ? 0 : 1, alg ? 0u : (unsigned) rr->dnskey.algorithm,
661 t);
662 if (r < 0)
663 return -ENOMEM;
664 break;
665 }
666
667 case DNS_TYPE_RRSIG: {
668 const char *type, *alg;
669 char expiration[strlen("YYYYMMDDHHmmSS") + 1], inception[strlen("YYYYMMDDHHmmSS") + 1];
670
671 type = dns_type_to_string(rr->rrsig.type_covered);
672 alg = dnssec_algorithm_to_string(rr->rrsig.algorithm);
673
674 t = base64mem(rr->rrsig.signature, rr->rrsig.signature_size);
675 if (!t)
676 return -ENOMEM;
677
678 r = format_timestamp_dns(expiration, sizeof(expiration), rr->rrsig.expiration);
679 if (r < 0)
680 return r;
681
682 r = format_timestamp_dns(inception, sizeof(inception), rr->rrsig.inception);
683 if (r < 0)
684 return r;
685
686 /* TYPE?? follows
687 * http://tools.ietf.org/html/rfc3597#section-5 */
688
689 r = asprintf(&s, "%s %s%.*u %.*s%.*u %u %u %s %s %u %s %s",
690 k,
691 type ?: "TYPE",
692 type ? 0 : 1, type ? 0u : (unsigned) rr->rrsig.type_covered,
693 alg ? -1 : 0, alg,
694 alg ? 0 : 1, alg ? 0u : (unsigned) rr->rrsig.algorithm,
695 rr->rrsig.labels,
696 rr->rrsig.original_ttl,
697 expiration,
698 inception,
699 rr->rrsig.key_tag,
700 rr->rrsig.signer,
701 t);
702 if (r < 0)
703 return -ENOMEM;
704 break;
705 }
706
707 default:
708 t = hexmem(rr->generic.data, rr->generic.size);
709 if (!t)
710 return -ENOMEM;
711
712 r = asprintf(&s, "%s \\# %"PRIu8" %s", k, rr->generic.size, t);
713 if (r < 0)
714 return -ENOMEM;
715 break;
716 }
717
718 *ret = s;
719 return 0;
720 }
721
722 const char *dns_class_to_string(uint16_t class) {
723
724 switch (class) {
725
726 case DNS_CLASS_IN:
727 return "IN";
728
729 case DNS_CLASS_ANY:
730 return "ANY";
731 }
732
733 return NULL;
734 }
735
736 int dns_class_from_string(const char *s, uint16_t *class) {
737 assert(s);
738 assert(class);
739
740 if (strcaseeq(s, "IN"))
741 *class = DNS_CLASS_IN;
742 else if (strcaseeq(s, "ANY"))
743 *class = DNS_TYPE_ANY;
744 else
745 return -EINVAL;
746
747 return 0;
748 }