]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-rr.c
resolved: add comments referencing various RFCs to various places
[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 "alloc-util.h"
25 #include "dns-domain.h"
26 #include "dns-type.h"
27 #include "hexdecoct.h"
28 #include "resolved-dns-packet.h"
29 #include "resolved-dns-rr.h"
30 #include "string-table.h"
31 #include "string-util.h"
32 #include "strv.h"
33
34 DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) {
35 DnsResourceKey *k;
36 size_t l;
37
38 assert(name);
39
40 l = strlen(name);
41 k = malloc0(sizeof(DnsResourceKey) + l + 1);
42 if (!k)
43 return NULL;
44
45 k->n_ref = 1;
46 k->class = class;
47 k->type = type;
48
49 strcpy((char*) k + sizeof(DnsResourceKey), name);
50
51 return k;
52 }
53
54 DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname) {
55 int r;
56
57 assert(key);
58 assert(cname);
59
60 assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME));
61
62 if (cname->key->type == DNS_TYPE_CNAME)
63 return dns_resource_key_new(key->class, key->type, cname->cname.name);
64 else {
65 DnsResourceKey *k;
66 char *destination = NULL;
67
68 r = dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname->key), cname->dname.name, &destination);
69 if (r < 0)
70 return NULL;
71 if (r == 0)
72 return dns_resource_key_ref((DnsResourceKey*) key);
73
74 k = dns_resource_key_new_consume(key->class, key->type, destination);
75 if (!k) {
76 free(destination);
77 return NULL;
78 }
79
80 return k;
81 }
82 }
83
84 int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key, char *name) {
85 DnsResourceKey *new_key;
86 char *joined;
87 int r;
88
89 assert(ret);
90 assert(key);
91 assert(name);
92
93 if (dns_name_is_root(name)) {
94 *ret = dns_resource_key_ref(key);
95 return 0;
96 }
97
98 r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), name, &joined);
99 if (r < 0)
100 return r;
101
102 new_key = dns_resource_key_new_consume(key->class, key->type, joined);
103 if (!new_key) {
104 free(joined);
105 return -ENOMEM;
106 }
107
108 *ret = new_key;
109 return 0;
110 }
111
112 DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name) {
113 DnsResourceKey *k;
114
115 assert(name);
116
117 k = new0(DnsResourceKey, 1);
118 if (!k)
119 return NULL;
120
121 k->n_ref = 1;
122 k->class = class;
123 k->type = type;
124 k->_name = name;
125
126 return k;
127 }
128
129 DnsResourceKey* dns_resource_key_ref(DnsResourceKey *k) {
130
131 if (!k)
132 return NULL;
133
134 /* Static/const keys created with DNS_RESOURCE_KEY_CONST will
135 * set this to -1, they should not be reffed/unreffed */
136 assert(k->n_ref != (unsigned) -1);
137
138 assert(k->n_ref > 0);
139 k->n_ref++;
140
141 return k;
142 }
143
144 DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) {
145 if (!k)
146 return NULL;
147
148 assert(k->n_ref != (unsigned) -1);
149 assert(k->n_ref > 0);
150
151 if (k->n_ref == 1) {
152 free(k->_name);
153 free(k);
154 } else
155 k->n_ref--;
156
157 return NULL;
158 }
159
160 bool dns_resource_key_is_address(const DnsResourceKey *key) {
161 assert(key);
162
163 /* Check if this is an A or AAAA resource key */
164
165 return key->class == DNS_CLASS_IN && IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_AAAA);
166 }
167
168 int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
169 int r;
170
171 if (a == b)
172 return 1;
173
174 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b));
175 if (r <= 0)
176 return r;
177
178 if (a->class != b->class)
179 return 0;
180
181 if (a->type != b->type)
182 return 0;
183
184 return 1;
185 }
186
187 int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr, const char *search_domain) {
188 int r;
189
190 assert(key);
191 assert(rr);
192
193 if (key == rr->key)
194 return 1;
195
196 /* Checks if an rr matches the specified key. If a search
197 * domain is specified, it will also be checked if the key
198 * with the search domain suffixed might match the RR. */
199
200 if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
201 return 0;
202
203 if (rr->key->type != key->type && key->type != DNS_TYPE_ANY)
204 return 0;
205
206 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
207 if (r != 0)
208 return r;
209
210 if (search_domain) {
211 _cleanup_free_ char *joined = NULL;
212
213 r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), search_domain, &joined);
214 if (r < 0)
215 return r;
216
217 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), joined);
218 }
219
220 return 0;
221 }
222
223 int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsResourceKey *cname, const char *search_domain) {
224 int r;
225
226 assert(key);
227 assert(cname);
228
229 if (cname->class != key->class && key->class != DNS_CLASS_ANY)
230 return 0;
231
232 if (cname->type == DNS_TYPE_CNAME)
233 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname));
234 else if (cname->type == DNS_TYPE_DNAME)
235 r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname));
236 else
237 return 0;
238
239 if (r != 0)
240 return r;
241
242 if (search_domain) {
243 _cleanup_free_ char *joined = NULL;
244
245 r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), search_domain, &joined);
246 if (r < 0)
247 return r;
248
249 if (cname->type == DNS_TYPE_CNAME)
250 return dns_name_equal(joined, DNS_RESOURCE_KEY_NAME(cname));
251 else if (cname->type == DNS_TYPE_DNAME)
252 return dns_name_endswith(joined, DNS_RESOURCE_KEY_NAME(cname));
253 }
254
255 return 0;
256 }
257
258 int dns_resource_key_match_soa(const DnsResourceKey *key, const DnsResourceKey *soa) {
259 assert(soa);
260 assert(key);
261
262 /* Checks whether 'soa' is a SOA record for the specified key. */
263
264 if (soa->class != key->class)
265 return 0;
266
267 if (soa->type != DNS_TYPE_SOA)
268 return 0;
269
270 return dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(soa));
271 }
272
273 static void dns_resource_key_hash_func(const void *i, struct siphash *state) {
274 const DnsResourceKey *k = i;
275
276 assert(k);
277
278 dns_name_hash_func(DNS_RESOURCE_KEY_NAME(k), state);
279 siphash24_compress(&k->class, sizeof(k->class), state);
280 siphash24_compress(&k->type, sizeof(k->type), state);
281 }
282
283 static int dns_resource_key_compare_func(const void *a, const void *b) {
284 const DnsResourceKey *x = a, *y = b;
285 int ret;
286
287 ret = dns_name_compare_func(DNS_RESOURCE_KEY_NAME(x), DNS_RESOURCE_KEY_NAME(y));
288 if (ret != 0)
289 return ret;
290
291 if (x->type < y->type)
292 return -1;
293 if (x->type > y->type)
294 return 1;
295
296 if (x->class < y->class)
297 return -1;
298 if (x->class > y->class)
299 return 1;
300
301 return 0;
302 }
303
304 const struct hash_ops dns_resource_key_hash_ops = {
305 .hash = dns_resource_key_hash_func,
306 .compare = dns_resource_key_compare_func
307 };
308
309 int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) {
310 char cbuf[strlen("CLASS") + DECIMAL_STR_MAX(uint16_t)], tbuf[strlen("TYPE") + DECIMAL_STR_MAX(uint16_t)];
311 const char *c, *t;
312 char *s;
313
314 /* If we cannot convert the CLASS/TYPE into a known string,
315 use the format recommended by RFC 3597, Section 5. */
316
317 c = dns_class_to_string(key->class);
318 if (!c) {
319 sprintf(cbuf, "CLASS%u", key->class);
320 c = cbuf;
321 }
322
323 t = dns_type_to_string(key->type);
324 if (!t){
325 sprintf(tbuf, "TYPE%u", key->type);
326 t = tbuf;
327 }
328
329 if (asprintf(&s, "%s. %s %-5s", DNS_RESOURCE_KEY_NAME(key), c, t) < 0)
330 return -ENOMEM;
331
332 *ret = s;
333 return 0;
334 }
335
336 DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
337 DnsResourceRecord *rr;
338
339 rr = new0(DnsResourceRecord, 1);
340 if (!rr)
341 return NULL;
342
343 rr->n_ref = 1;
344 rr->key = dns_resource_key_ref(key);
345 rr->expiry = USEC_INFINITY;
346
347 return rr;
348 }
349
350 DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name) {
351 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
352
353 key = dns_resource_key_new(class, type, name);
354 if (!key)
355 return NULL;
356
357 return dns_resource_record_new(key);
358 }
359
360 DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr) {
361 if (!rr)
362 return NULL;
363
364 assert(rr->n_ref > 0);
365 rr->n_ref++;
366
367 return rr;
368 }
369
370 DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
371 if (!rr)
372 return NULL;
373
374 assert(rr->n_ref > 0);
375
376 if (rr->n_ref > 1) {
377 rr->n_ref--;
378 return NULL;
379 }
380
381 if (rr->key) {
382 switch(rr->key->type) {
383
384 case DNS_TYPE_SRV:
385 free(rr->srv.name);
386 break;
387
388 case DNS_TYPE_PTR:
389 case DNS_TYPE_NS:
390 case DNS_TYPE_CNAME:
391 case DNS_TYPE_DNAME:
392 free(rr->ptr.name);
393 break;
394
395 case DNS_TYPE_HINFO:
396 free(rr->hinfo.cpu);
397 free(rr->hinfo.os);
398 break;
399
400 case DNS_TYPE_TXT:
401 case DNS_TYPE_SPF:
402 dns_txt_item_free_all(rr->txt.items);
403 break;
404
405 case DNS_TYPE_SOA:
406 free(rr->soa.mname);
407 free(rr->soa.rname);
408 break;
409
410 case DNS_TYPE_MX:
411 free(rr->mx.exchange);
412 break;
413
414 case DNS_TYPE_DS:
415 free(rr->ds.digest);
416 break;
417
418 case DNS_TYPE_SSHFP:
419 free(rr->sshfp.fingerprint);
420 break;
421
422 case DNS_TYPE_DNSKEY:
423 free(rr->dnskey.key);
424 break;
425
426 case DNS_TYPE_RRSIG:
427 free(rr->rrsig.signer);
428 free(rr->rrsig.signature);
429 break;
430
431 case DNS_TYPE_NSEC:
432 free(rr->nsec.next_domain_name);
433 bitmap_free(rr->nsec.types);
434 break;
435
436 case DNS_TYPE_NSEC3:
437 free(rr->nsec3.next_hashed_name);
438 free(rr->nsec3.salt);
439 bitmap_free(rr->nsec3.types);
440 break;
441
442 case DNS_TYPE_LOC:
443 case DNS_TYPE_A:
444 case DNS_TYPE_AAAA:
445 break;
446
447 default:
448 free(rr->generic.data);
449 }
450
451 free(rr->wire_format);
452 dns_resource_key_unref(rr->key);
453 }
454
455 free(rr->to_string);
456 free(rr);
457
458 return NULL;
459 }
460
461 int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) {
462 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
463 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
464 _cleanup_free_ char *ptr = NULL;
465 int r;
466
467 assert(ret);
468 assert(address);
469 assert(hostname);
470
471 r = dns_name_reverse(family, address, &ptr);
472 if (r < 0)
473 return r;
474
475 key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, ptr);
476 if (!key)
477 return -ENOMEM;
478
479 ptr = NULL;
480
481 rr = dns_resource_record_new(key);
482 if (!rr)
483 return -ENOMEM;
484
485 rr->ptr.name = strdup(hostname);
486 if (!rr->ptr.name)
487 return -ENOMEM;
488
489 *ret = rr;
490 rr = NULL;
491
492 return 0;
493 }
494
495 int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name) {
496 DnsResourceRecord *rr;
497
498 assert(ret);
499 assert(address);
500 assert(family);
501
502 if (family == AF_INET) {
503
504 rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, name);
505 if (!rr)
506 return -ENOMEM;
507
508 rr->a.in_addr = address->in;
509
510 } else if (family == AF_INET6) {
511
512 rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, name);
513 if (!rr)
514 return -ENOMEM;
515
516 rr->aaaa.in6_addr = address->in6;
517 } else
518 return -EAFNOSUPPORT;
519
520 *ret = rr;
521
522 return 0;
523 }
524
525 int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
526 int r;
527
528 assert(a);
529 assert(b);
530
531 if (a == b)
532 return 1;
533
534 r = dns_resource_key_equal(a->key, b->key);
535 if (r <= 0)
536 return r;
537
538 if (a->unparseable != b->unparseable)
539 return 0;
540
541 switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) {
542
543 case DNS_TYPE_SRV:
544 r = dns_name_equal(a->srv.name, b->srv.name);
545 if (r <= 0)
546 return r;
547
548 return a->srv.priority == b->srv.priority &&
549 a->srv.weight == b->srv.weight &&
550 a->srv.port == b->srv.port;
551
552 case DNS_TYPE_PTR:
553 case DNS_TYPE_NS:
554 case DNS_TYPE_CNAME:
555 case DNS_TYPE_DNAME:
556 return dns_name_equal(a->ptr.name, b->ptr.name);
557
558 case DNS_TYPE_HINFO:
559 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
560 strcaseeq(a->hinfo.os, b->hinfo.os);
561
562 case DNS_TYPE_SPF: /* exactly the same as TXT */
563 case DNS_TYPE_TXT:
564 return dns_txt_item_equal(a->txt.items, b->txt.items);
565
566 case DNS_TYPE_A:
567 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
568
569 case DNS_TYPE_AAAA:
570 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
571
572 case DNS_TYPE_SOA:
573 r = dns_name_equal(a->soa.mname, b->soa.mname);
574 if (r <= 0)
575 return r;
576 r = dns_name_equal(a->soa.rname, b->soa.rname);
577 if (r <= 0)
578 return r;
579
580 return a->soa.serial == b->soa.serial &&
581 a->soa.refresh == b->soa.refresh &&
582 a->soa.retry == b->soa.retry &&
583 a->soa.expire == b->soa.expire &&
584 a->soa.minimum == b->soa.minimum;
585
586 case DNS_TYPE_MX:
587 if (a->mx.priority != b->mx.priority)
588 return 0;
589
590 return dns_name_equal(a->mx.exchange, b->mx.exchange);
591
592 case DNS_TYPE_LOC:
593 assert(a->loc.version == b->loc.version);
594
595 return a->loc.size == b->loc.size &&
596 a->loc.horiz_pre == b->loc.horiz_pre &&
597 a->loc.vert_pre == b->loc.vert_pre &&
598 a->loc.latitude == b->loc.latitude &&
599 a->loc.longitude == b->loc.longitude &&
600 a->loc.altitude == b->loc.altitude;
601
602 case DNS_TYPE_DS:
603 return a->ds.key_tag == b->ds.key_tag &&
604 a->ds.algorithm == b->ds.algorithm &&
605 a->ds.digest_type == b->ds.digest_type &&
606 a->ds.digest_size == b->ds.digest_size &&
607 memcmp(a->ds.digest, b->ds.digest, a->ds.digest_size) == 0;
608
609 case DNS_TYPE_SSHFP:
610 return a->sshfp.algorithm == b->sshfp.algorithm &&
611 a->sshfp.fptype == b->sshfp.fptype &&
612 a->sshfp.fingerprint_size == b->sshfp.fingerprint_size &&
613 memcmp(a->sshfp.fingerprint, b->sshfp.fingerprint, a->sshfp.fingerprint_size) == 0;
614
615 case DNS_TYPE_DNSKEY:
616 return a->dnskey.flags == b->dnskey.flags &&
617 a->dnskey.protocol == b->dnskey.protocol &&
618 a->dnskey.algorithm == b->dnskey.algorithm &&
619 a->dnskey.key_size == b->dnskey.key_size &&
620 memcmp(a->dnskey.key, b->dnskey.key, a->dnskey.key_size) == 0;
621
622 case DNS_TYPE_RRSIG:
623 /* do the fast comparisons first */
624 if (a->rrsig.type_covered != b->rrsig.type_covered ||
625 a->rrsig.algorithm != b->rrsig.algorithm ||
626 a->rrsig.labels != b->rrsig.labels ||
627 a->rrsig.original_ttl != b->rrsig.original_ttl ||
628 a->rrsig.expiration != b->rrsig.expiration ||
629 a->rrsig.inception != b->rrsig.inception ||
630 a->rrsig.key_tag != b->rrsig.key_tag ||
631 a->rrsig.signature_size != b->rrsig.signature_size ||
632 memcmp(a->rrsig.signature, b->rrsig.signature, a->rrsig.signature_size) != 0)
633 return false;
634
635 return dns_name_equal(a->rrsig.signer, b->rrsig.signer);
636
637 case DNS_TYPE_NSEC:
638 return dns_name_equal(a->nsec.next_domain_name, b->nsec.next_domain_name) &&
639 bitmap_equal(a->nsec.types, b->nsec.types);
640
641 case DNS_TYPE_NSEC3:
642 return a->nsec3.algorithm == b->nsec3.algorithm &&
643 a->nsec3.flags == b->nsec3.flags &&
644 a->nsec3.iterations == b->nsec3.iterations &&
645 a->nsec3.salt_size == b->nsec3.salt_size &&
646 memcmp(a->nsec3.salt, b->nsec3.salt, a->nsec3.salt_size) == 0 &&
647 memcmp(a->nsec3.next_hashed_name, b->nsec3.next_hashed_name, a->nsec3.next_hashed_name_size) == 0 &&
648 bitmap_equal(a->nsec3.types, b->nsec3.types);
649
650 default:
651 return a->generic.size == b->generic.size &&
652 memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
653 }
654 }
655
656 static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t altitude,
657 uint8_t size, uint8_t horiz_pre, uint8_t vert_pre) {
658 char *s;
659 char NS = latitude >= 1U<<31 ? 'N' : 'S';
660 char EW = longitude >= 1U<<31 ? 'E' : 'W';
661
662 int lat = latitude >= 1U<<31 ? (int) (latitude - (1U<<31)) : (int) ((1U<<31) - latitude);
663 int lon = longitude >= 1U<<31 ? (int) (longitude - (1U<<31)) : (int) ((1U<<31) - longitude);
664 double alt = altitude >= 10000000u ? altitude - 10000000u : -(double)(10000000u - altitude);
665 double siz = (size >> 4) * exp10((double) (size & 0xF));
666 double hor = (horiz_pre >> 4) * exp10((double) (horiz_pre & 0xF));
667 double ver = (vert_pre >> 4) * exp10((double) (vert_pre & 0xF));
668
669 if (asprintf(&s, "%d %d %.3f %c %d %d %.3f %c %.2fm %.2fm %.2fm %.2fm",
670 (lat / 60000 / 60),
671 (lat / 60000) % 60,
672 (lat % 60000) / 1000.,
673 NS,
674 (lon / 60000 / 60),
675 (lon / 60000) % 60,
676 (lon % 60000) / 1000.,
677 EW,
678 alt / 100.,
679 siz / 100.,
680 hor / 100.,
681 ver / 100.) < 0)
682 return NULL;
683
684 return s;
685 }
686
687 static int format_timestamp_dns(char *buf, size_t l, time_t sec) {
688 struct tm tm;
689
690 assert(buf);
691 assert(l > strlen("YYYYMMDDHHmmSS"));
692
693 if (!gmtime_r(&sec, &tm))
694 return -EINVAL;
695
696 if (strftime(buf, l, "%Y%m%d%H%M%S", &tm) <= 0)
697 return -EINVAL;
698
699 return 0;
700 }
701
702 static char *format_types(Bitmap *types) {
703 _cleanup_strv_free_ char **strv = NULL;
704 _cleanup_free_ char *str = NULL;
705 Iterator i;
706 unsigned type;
707 int r;
708
709 BITMAP_FOREACH(type, types, i) {
710 if (dns_type_to_string(type)) {
711 r = strv_extend(&strv, dns_type_to_string(type));
712 if (r < 0)
713 return NULL;
714 } else {
715 char *t;
716
717 r = asprintf(&t, "TYPE%u", type);
718 if (r < 0)
719 return NULL;
720
721 r = strv_consume(&strv, t);
722 if (r < 0)
723 return NULL;
724 }
725 }
726
727 str = strv_join(strv, " ");
728 if (!str)
729 return NULL;
730
731 return strjoin("( ", str, " )", NULL);
732 }
733
734 static char *format_txt(DnsTxtItem *first) {
735 DnsTxtItem *i;
736 size_t c = 1;
737 char *p, *s;
738
739 LIST_FOREACH(items, i, first)
740 c += i->length * 4 + 3;
741
742 p = s = new(char, c);
743 if (!s)
744 return NULL;
745
746 LIST_FOREACH(items, i, first) {
747 size_t j;
748
749 if (i != first)
750 *(p++) = ' ';
751
752 *(p++) = '"';
753
754 for (j = 0; j < i->length; j++) {
755 if (i->data[j] < ' ' || i->data[j] == '"' || i->data[j] >= 127) {
756 *(p++) = '\\';
757 *(p++) = '0' + (i->data[j] / 100);
758 *(p++) = '0' + ((i->data[j] / 10) % 10);
759 *(p++) = '0' + (i->data[j] % 10);
760 } else
761 *(p++) = i->data[j];
762 }
763
764 *(p++) = '"';
765 }
766
767 *p = 0;
768 return s;
769 }
770
771 const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
772 _cleanup_free_ char *k = NULL, *t = NULL;
773 char *s;
774 int r;
775
776 assert(rr);
777
778 if (rr->to_string)
779 return rr->to_string;
780
781 r = dns_resource_key_to_string(rr->key, &k);
782 if (r < 0)
783 return NULL;
784
785 switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
786
787 case DNS_TYPE_SRV:
788 r = asprintf(&s, "%s %u %u %u %s",
789 k,
790 rr->srv.priority,
791 rr->srv.weight,
792 rr->srv.port,
793 strna(rr->srv.name));
794 if (r < 0)
795 return NULL;
796 break;
797
798 case DNS_TYPE_PTR:
799 case DNS_TYPE_NS:
800 case DNS_TYPE_CNAME:
801 case DNS_TYPE_DNAME:
802 s = strjoin(k, " ", rr->ptr.name, NULL);
803 if (!s)
804 return NULL;
805
806 break;
807
808 case DNS_TYPE_HINFO:
809 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
810 if (!s)
811 return NULL;
812 break;
813
814 case DNS_TYPE_SPF: /* exactly the same as TXT */
815 case DNS_TYPE_TXT:
816 t = format_txt(rr->txt.items);
817 if (!t)
818 return NULL;
819
820 s = strjoin(k, " ", t, NULL);
821 if (!s)
822 return NULL;
823 break;
824
825 case DNS_TYPE_A: {
826 _cleanup_free_ char *x = NULL;
827
828 r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
829 if (r < 0)
830 return NULL;
831
832 s = strjoin(k, " ", x, NULL);
833 if (!s)
834 return NULL;
835 break;
836 }
837
838 case DNS_TYPE_AAAA:
839 r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &t);
840 if (r < 0)
841 return NULL;
842
843 s = strjoin(k, " ", t, NULL);
844 if (!s)
845 return NULL;
846 break;
847
848 case DNS_TYPE_SOA:
849 r = asprintf(&s, "%s %s %s %u %u %u %u %u",
850 k,
851 strna(rr->soa.mname),
852 strna(rr->soa.rname),
853 rr->soa.serial,
854 rr->soa.refresh,
855 rr->soa.retry,
856 rr->soa.expire,
857 rr->soa.minimum);
858 if (r < 0)
859 return NULL;
860 break;
861
862 case DNS_TYPE_MX:
863 r = asprintf(&s, "%s %u %s",
864 k,
865 rr->mx.priority,
866 rr->mx.exchange);
867 if (r < 0)
868 return NULL;
869 break;
870
871 case DNS_TYPE_LOC:
872 assert(rr->loc.version == 0);
873
874 t = format_location(rr->loc.latitude,
875 rr->loc.longitude,
876 rr->loc.altitude,
877 rr->loc.size,
878 rr->loc.horiz_pre,
879 rr->loc.vert_pre);
880 if (!t)
881 return NULL;
882
883 s = strjoin(k, " ", t, NULL);
884 if (!s)
885 return NULL;
886 break;
887
888 case DNS_TYPE_DS:
889 t = hexmem(rr->ds.digest, rr->ds.digest_size);
890 if (!t)
891 return NULL;
892
893 r = asprintf(&s, "%s %u %u %u %s",
894 k,
895 rr->ds.key_tag,
896 rr->ds.algorithm,
897 rr->ds.digest_type,
898 t);
899 if (r < 0)
900 return NULL;
901 break;
902
903 case DNS_TYPE_SSHFP:
904 t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size);
905 if (!t)
906 return NULL;
907
908 r = asprintf(&s, "%s %u %u %s",
909 k,
910 rr->sshfp.algorithm,
911 rr->sshfp.fptype,
912 t);
913 if (r < 0)
914 return NULL;
915 break;
916
917 case DNS_TYPE_DNSKEY: {
918 const char *alg;
919
920 alg = dnssec_algorithm_to_string(rr->dnskey.algorithm);
921
922 t = base64mem(rr->dnskey.key, rr->dnskey.key_size);
923 if (!t)
924 return NULL;
925
926 r = asprintf(&s, "%s %u %u %.*s%.*u %s",
927 k,
928 rr->dnskey.flags,
929 rr->dnskey.protocol,
930 alg ? -1 : 0, alg,
931 alg ? 0 : 1, alg ? 0u : (unsigned) rr->dnskey.algorithm,
932 t);
933 if (r < 0)
934 return NULL;
935 break;
936 }
937
938 case DNS_TYPE_RRSIG: {
939 const char *type, *alg;
940 char expiration[strlen("YYYYMMDDHHmmSS") + 1], inception[strlen("YYYYMMDDHHmmSS") + 1];
941
942 type = dns_type_to_string(rr->rrsig.type_covered);
943 alg = dnssec_algorithm_to_string(rr->rrsig.algorithm);
944
945 t = base64mem(rr->rrsig.signature, rr->rrsig.signature_size);
946 if (!t)
947 return NULL;
948
949 r = format_timestamp_dns(expiration, sizeof(expiration), rr->rrsig.expiration);
950 if (r < 0)
951 return NULL;
952
953 r = format_timestamp_dns(inception, sizeof(inception), rr->rrsig.inception);
954 if (r < 0)
955 return NULL;
956
957 /* TYPE?? follows
958 * http://tools.ietf.org/html/rfc3597#section-5 */
959
960 r = asprintf(&s, "%s %s%.*u %.*s%.*u %u %u %s %s %u %s %s",
961 k,
962 type ?: "TYPE",
963 type ? 0 : 1, type ? 0u : (unsigned) rr->rrsig.type_covered,
964 alg ? -1 : 0, alg,
965 alg ? 0 : 1, alg ? 0u : (unsigned) rr->rrsig.algorithm,
966 rr->rrsig.labels,
967 rr->rrsig.original_ttl,
968 expiration,
969 inception,
970 rr->rrsig.key_tag,
971 rr->rrsig.signer,
972 t);
973 if (r < 0)
974 return NULL;
975 break;
976 }
977
978 case DNS_TYPE_NSEC:
979 t = format_types(rr->nsec.types);
980 if (!t)
981 return NULL;
982
983 r = asprintf(&s, "%s %s %s",
984 k,
985 rr->nsec.next_domain_name,
986 t);
987 if (r < 0)
988 return NULL;
989 break;
990
991 case DNS_TYPE_NSEC3: {
992 _cleanup_free_ char *salt = NULL, *hash = NULL;
993
994 if (rr->nsec3.salt_size > 0) {
995 salt = hexmem(rr->nsec3.salt, rr->nsec3.salt_size);
996 if (!salt)
997 return NULL;
998 }
999
1000 hash = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false);
1001 if (!hash)
1002 return NULL;
1003
1004 t = format_types(rr->nsec3.types);
1005 if (!t)
1006 return NULL;
1007
1008 r = asprintf(&s, "%s %"PRIu8" %"PRIu8" %"PRIu16" %s %s %s",
1009 k,
1010 rr->nsec3.algorithm,
1011 rr->nsec3.flags,
1012 rr->nsec3.iterations,
1013 rr->nsec3.salt_size > 0 ? salt : "-",
1014 hash,
1015 t);
1016 if (r < 0)
1017 return NULL;
1018
1019 break;
1020 }
1021
1022 default:
1023 t = hexmem(rr->generic.data, rr->generic.size);
1024 if (!t)
1025 return NULL;
1026
1027 /* Format as documented in RFC 3597, Section 5 */
1028 r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.size, t);
1029 if (r < 0)
1030 return NULL;
1031 break;
1032 }
1033
1034 rr->to_string = s;
1035 return s;
1036 }
1037
1038 int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) {
1039
1040 DnsPacket packet = {
1041 .n_ref = 1,
1042 .protocol = DNS_PROTOCOL_DNS,
1043 .on_stack = true,
1044 .refuse_compression = true,
1045 .canonical_form = canonical,
1046 };
1047
1048 size_t start, rds;
1049 int r;
1050
1051 assert(rr);
1052
1053 /* Generates the RR in wire-format, optionally in the
1054 * canonical form as discussed in the DNSSEC RFC 4034, Section
1055 * 6.2. We allocate a throw-away DnsPacket object on the stack
1056 * here, because we need some book-keeping for memory
1057 * management, and can reuse the DnsPacket serializer, that
1058 * can generate the canonical form, too, but also knows label
1059 * compression and suchlike. */
1060
1061 if (rr->wire_format && rr->wire_format_canonical == canonical)
1062 return 0;
1063
1064 r = dns_packet_append_rr(&packet, rr, &start, &rds);
1065 if (r < 0)
1066 return r;
1067
1068 assert(start == 0);
1069 assert(packet._data);
1070
1071 free(rr->wire_format);
1072 rr->wire_format = packet._data;
1073 rr->wire_format_size = packet.size;
1074 rr->wire_format_rdata_offset = rds;
1075 rr->wire_format_canonical = canonical;
1076
1077 packet._data = NULL;
1078 dns_packet_unref(&packet);
1079
1080 return 0;
1081 }
1082
1083 DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i) {
1084 DnsTxtItem *n;
1085
1086 if (!i)
1087 return NULL;
1088
1089 n = i->items_next;
1090
1091 free(i);
1092 return dns_txt_item_free_all(n);
1093 }
1094
1095 bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) {
1096
1097 if (a == b)
1098 return true;
1099
1100 if (!a != !b)
1101 return false;
1102
1103 if (!a)
1104 return true;
1105
1106 if (a->length != b->length)
1107 return false;
1108
1109 if (memcmp(a->data, b->data, a->length) != 0)
1110 return false;
1111
1112 return dns_txt_item_equal(a->items_next, b->items_next);
1113 }
1114
1115 static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = {
1116 /* Mnemonics as listed on https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
1117 [DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5",
1118 [DNSSEC_ALGORITHM_DH] = "DH",
1119 [DNSSEC_ALGORITHM_DSA] = "DSA",
1120 [DNSSEC_ALGORITHM_ECC] = "ECC",
1121 [DNSSEC_ALGORITHM_RSASHA1] = "RSASHA1",
1122 [DNSSEC_ALGORITHM_DSA_NSEC3_SHA1] = "DSA-NSEC3-SHA1",
1123 [DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1] = "RSASHA1-NSEC3-SHA1",
1124 [DNSSEC_ALGORITHM_RSASHA256] = "RSASHA256",
1125 [DNSSEC_ALGORITHM_RSASHA512] = "RSASHA512",
1126 [DNSSEC_ALGORITHM_ECC_GOST] = "ECC-GOST",
1127 [DNSSEC_ALGORITHM_ECDSAP256SHA256] = "ECDSAP256SHA256",
1128 [DNSSEC_ALGORITHM_ECDSAP384SHA384] = "ECDSAP384SHA384",
1129 [DNSSEC_ALGORITHM_INDIRECT] = "INDIRECT",
1130 [DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS",
1131 [DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID",
1132 };
1133 DEFINE_STRING_TABLE_LOOKUP(dnssec_algorithm, int);
1134
1135 static const char* const dnssec_digest_table[_DNSSEC_DIGEST_MAX_DEFINED] = {
1136 /* Names as listed on https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */
1137 [DNSSEC_DIGEST_SHA1] = "SHA-1",
1138 [DNSSEC_DIGEST_SHA256] = "SHA-256",
1139 [DNSSEC_DIGEST_GOST_R_34_11_94] = "GOST_R_34.11-94",
1140 [DNSSEC_DIGEST_SHA384] = "SHA-384",
1141 };
1142 DEFINE_STRING_TABLE_LOOKUP(dnssec_digest, int);