]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-rr.c
shared: simplify dns_name_hash_func() end of name detection
[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, *n;
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 n = DNS_RESOURCE_KEY_NAME(key);
330 if (asprintf(&s, "%s%s %s %-5s", n, endswith(n, ".") ? "" : ".", c, t) < 0)
331 return -ENOMEM;
332
333 *ret = s;
334 return 0;
335 }
336
337 DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
338 DnsResourceRecord *rr;
339
340 rr = new0(DnsResourceRecord, 1);
341 if (!rr)
342 return NULL;
343
344 rr->n_ref = 1;
345 rr->key = dns_resource_key_ref(key);
346 rr->expiry = USEC_INFINITY;
347
348 return rr;
349 }
350
351 DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name) {
352 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
353
354 key = dns_resource_key_new(class, type, name);
355 if (!key)
356 return NULL;
357
358 return dns_resource_record_new(key);
359 }
360
361 DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr) {
362 if (!rr)
363 return NULL;
364
365 assert(rr->n_ref > 0);
366 rr->n_ref++;
367
368 return rr;
369 }
370
371 DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
372 if (!rr)
373 return NULL;
374
375 assert(rr->n_ref > 0);
376
377 if (rr->n_ref > 1) {
378 rr->n_ref--;
379 return NULL;
380 }
381
382 if (rr->key) {
383 switch(rr->key->type) {
384
385 case DNS_TYPE_SRV:
386 free(rr->srv.name);
387 break;
388
389 case DNS_TYPE_PTR:
390 case DNS_TYPE_NS:
391 case DNS_TYPE_CNAME:
392 case DNS_TYPE_DNAME:
393 free(rr->ptr.name);
394 break;
395
396 case DNS_TYPE_HINFO:
397 free(rr->hinfo.cpu);
398 free(rr->hinfo.os);
399 break;
400
401 case DNS_TYPE_TXT:
402 case DNS_TYPE_SPF:
403 dns_txt_item_free_all(rr->txt.items);
404 break;
405
406 case DNS_TYPE_SOA:
407 free(rr->soa.mname);
408 free(rr->soa.rname);
409 break;
410
411 case DNS_TYPE_MX:
412 free(rr->mx.exchange);
413 break;
414
415 case DNS_TYPE_DS:
416 free(rr->ds.digest);
417 break;
418
419 case DNS_TYPE_SSHFP:
420 free(rr->sshfp.fingerprint);
421 break;
422
423 case DNS_TYPE_DNSKEY:
424 free(rr->dnskey.key);
425 break;
426
427 case DNS_TYPE_RRSIG:
428 free(rr->rrsig.signer);
429 free(rr->rrsig.signature);
430 break;
431
432 case DNS_TYPE_NSEC:
433 free(rr->nsec.next_domain_name);
434 bitmap_free(rr->nsec.types);
435 break;
436
437 case DNS_TYPE_NSEC3:
438 free(rr->nsec3.next_hashed_name);
439 free(rr->nsec3.salt);
440 bitmap_free(rr->nsec3.types);
441 break;
442
443 case DNS_TYPE_LOC:
444 case DNS_TYPE_A:
445 case DNS_TYPE_AAAA:
446 break;
447
448 default:
449 free(rr->generic.data);
450 }
451
452 free(rr->wire_format);
453 dns_resource_key_unref(rr->key);
454 }
455
456 free(rr->to_string);
457 free(rr);
458
459 return NULL;
460 }
461
462 int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) {
463 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
464 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
465 _cleanup_free_ char *ptr = NULL;
466 int r;
467
468 assert(ret);
469 assert(address);
470 assert(hostname);
471
472 r = dns_name_reverse(family, address, &ptr);
473 if (r < 0)
474 return r;
475
476 key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, ptr);
477 if (!key)
478 return -ENOMEM;
479
480 ptr = NULL;
481
482 rr = dns_resource_record_new(key);
483 if (!rr)
484 return -ENOMEM;
485
486 rr->ptr.name = strdup(hostname);
487 if (!rr->ptr.name)
488 return -ENOMEM;
489
490 *ret = rr;
491 rr = NULL;
492
493 return 0;
494 }
495
496 int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name) {
497 DnsResourceRecord *rr;
498
499 assert(ret);
500 assert(address);
501 assert(family);
502
503 if (family == AF_INET) {
504
505 rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, name);
506 if (!rr)
507 return -ENOMEM;
508
509 rr->a.in_addr = address->in;
510
511 } else if (family == AF_INET6) {
512
513 rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, name);
514 if (!rr)
515 return -ENOMEM;
516
517 rr->aaaa.in6_addr = address->in6;
518 } else
519 return -EAFNOSUPPORT;
520
521 *ret = rr;
522
523 return 0;
524 }
525
526 int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
527 int r;
528
529 assert(a);
530 assert(b);
531
532 if (a == b)
533 return 1;
534
535 r = dns_resource_key_equal(a->key, b->key);
536 if (r <= 0)
537 return r;
538
539 if (a->unparseable != b->unparseable)
540 return 0;
541
542 switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) {
543
544 case DNS_TYPE_SRV:
545 r = dns_name_equal(a->srv.name, b->srv.name);
546 if (r <= 0)
547 return r;
548
549 return a->srv.priority == b->srv.priority &&
550 a->srv.weight == b->srv.weight &&
551 a->srv.port == b->srv.port;
552
553 case DNS_TYPE_PTR:
554 case DNS_TYPE_NS:
555 case DNS_TYPE_CNAME:
556 case DNS_TYPE_DNAME:
557 return dns_name_equal(a->ptr.name, b->ptr.name);
558
559 case DNS_TYPE_HINFO:
560 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
561 strcaseeq(a->hinfo.os, b->hinfo.os);
562
563 case DNS_TYPE_SPF: /* exactly the same as TXT */
564 case DNS_TYPE_TXT:
565 return dns_txt_item_equal(a->txt.items, b->txt.items);
566
567 case DNS_TYPE_A:
568 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
569
570 case DNS_TYPE_AAAA:
571 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
572
573 case DNS_TYPE_SOA:
574 r = dns_name_equal(a->soa.mname, b->soa.mname);
575 if (r <= 0)
576 return r;
577 r = dns_name_equal(a->soa.rname, b->soa.rname);
578 if (r <= 0)
579 return r;
580
581 return a->soa.serial == b->soa.serial &&
582 a->soa.refresh == b->soa.refresh &&
583 a->soa.retry == b->soa.retry &&
584 a->soa.expire == b->soa.expire &&
585 a->soa.minimum == b->soa.minimum;
586
587 case DNS_TYPE_MX:
588 if (a->mx.priority != b->mx.priority)
589 return 0;
590
591 return dns_name_equal(a->mx.exchange, b->mx.exchange);
592
593 case DNS_TYPE_LOC:
594 assert(a->loc.version == b->loc.version);
595
596 return a->loc.size == b->loc.size &&
597 a->loc.horiz_pre == b->loc.horiz_pre &&
598 a->loc.vert_pre == b->loc.vert_pre &&
599 a->loc.latitude == b->loc.latitude &&
600 a->loc.longitude == b->loc.longitude &&
601 a->loc.altitude == b->loc.altitude;
602
603 case DNS_TYPE_DS:
604 return a->ds.key_tag == b->ds.key_tag &&
605 a->ds.algorithm == b->ds.algorithm &&
606 a->ds.digest_type == b->ds.digest_type &&
607 a->ds.digest_size == b->ds.digest_size &&
608 memcmp(a->ds.digest, b->ds.digest, a->ds.digest_size) == 0;
609
610 case DNS_TYPE_SSHFP:
611 return a->sshfp.algorithm == b->sshfp.algorithm &&
612 a->sshfp.fptype == b->sshfp.fptype &&
613 a->sshfp.fingerprint_size == b->sshfp.fingerprint_size &&
614 memcmp(a->sshfp.fingerprint, b->sshfp.fingerprint, a->sshfp.fingerprint_size) == 0;
615
616 case DNS_TYPE_DNSKEY:
617 return a->dnskey.flags == b->dnskey.flags &&
618 a->dnskey.protocol == b->dnskey.protocol &&
619 a->dnskey.algorithm == b->dnskey.algorithm &&
620 a->dnskey.key_size == b->dnskey.key_size &&
621 memcmp(a->dnskey.key, b->dnskey.key, a->dnskey.key_size) == 0;
622
623 case DNS_TYPE_RRSIG:
624 /* do the fast comparisons first */
625 if (a->rrsig.type_covered != b->rrsig.type_covered ||
626 a->rrsig.algorithm != b->rrsig.algorithm ||
627 a->rrsig.labels != b->rrsig.labels ||
628 a->rrsig.original_ttl != b->rrsig.original_ttl ||
629 a->rrsig.expiration != b->rrsig.expiration ||
630 a->rrsig.inception != b->rrsig.inception ||
631 a->rrsig.key_tag != b->rrsig.key_tag ||
632 a->rrsig.signature_size != b->rrsig.signature_size ||
633 memcmp(a->rrsig.signature, b->rrsig.signature, a->rrsig.signature_size) != 0)
634 return false;
635
636 return dns_name_equal(a->rrsig.signer, b->rrsig.signer);
637
638 case DNS_TYPE_NSEC:
639 return dns_name_equal(a->nsec.next_domain_name, b->nsec.next_domain_name) &&
640 bitmap_equal(a->nsec.types, b->nsec.types);
641
642 case DNS_TYPE_NSEC3:
643 return a->nsec3.algorithm == b->nsec3.algorithm &&
644 a->nsec3.flags == b->nsec3.flags &&
645 a->nsec3.iterations == b->nsec3.iterations &&
646 a->nsec3.salt_size == b->nsec3.salt_size &&
647 memcmp(a->nsec3.salt, b->nsec3.salt, a->nsec3.salt_size) == 0 &&
648 memcmp(a->nsec3.next_hashed_name, b->nsec3.next_hashed_name, a->nsec3.next_hashed_name_size) == 0 &&
649 bitmap_equal(a->nsec3.types, b->nsec3.types);
650
651 default:
652 return a->generic.size == b->generic.size &&
653 memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
654 }
655 }
656
657 static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t altitude,
658 uint8_t size, uint8_t horiz_pre, uint8_t vert_pre) {
659 char *s;
660 char NS = latitude >= 1U<<31 ? 'N' : 'S';
661 char EW = longitude >= 1U<<31 ? 'E' : 'W';
662
663 int lat = latitude >= 1U<<31 ? (int) (latitude - (1U<<31)) : (int) ((1U<<31) - latitude);
664 int lon = longitude >= 1U<<31 ? (int) (longitude - (1U<<31)) : (int) ((1U<<31) - longitude);
665 double alt = altitude >= 10000000u ? altitude - 10000000u : -(double)(10000000u - altitude);
666 double siz = (size >> 4) * exp10((double) (size & 0xF));
667 double hor = (horiz_pre >> 4) * exp10((double) (horiz_pre & 0xF));
668 double ver = (vert_pre >> 4) * exp10((double) (vert_pre & 0xF));
669
670 if (asprintf(&s, "%d %d %.3f %c %d %d %.3f %c %.2fm %.2fm %.2fm %.2fm",
671 (lat / 60000 / 60),
672 (lat / 60000) % 60,
673 (lat % 60000) / 1000.,
674 NS,
675 (lon / 60000 / 60),
676 (lon / 60000) % 60,
677 (lon % 60000) / 1000.,
678 EW,
679 alt / 100.,
680 siz / 100.,
681 hor / 100.,
682 ver / 100.) < 0)
683 return NULL;
684
685 return s;
686 }
687
688 static int format_timestamp_dns(char *buf, size_t l, time_t sec) {
689 struct tm tm;
690
691 assert(buf);
692 assert(l > strlen("YYYYMMDDHHmmSS"));
693
694 if (!gmtime_r(&sec, &tm))
695 return -EINVAL;
696
697 if (strftime(buf, l, "%Y%m%d%H%M%S", &tm) <= 0)
698 return -EINVAL;
699
700 return 0;
701 }
702
703 static char *format_types(Bitmap *types) {
704 _cleanup_strv_free_ char **strv = NULL;
705 _cleanup_free_ char *str = NULL;
706 Iterator i;
707 unsigned type;
708 int r;
709
710 BITMAP_FOREACH(type, types, i) {
711 if (dns_type_to_string(type)) {
712 r = strv_extend(&strv, dns_type_to_string(type));
713 if (r < 0)
714 return NULL;
715 } else {
716 char *t;
717
718 r = asprintf(&t, "TYPE%u", type);
719 if (r < 0)
720 return NULL;
721
722 r = strv_consume(&strv, t);
723 if (r < 0)
724 return NULL;
725 }
726 }
727
728 str = strv_join(strv, " ");
729 if (!str)
730 return NULL;
731
732 return strjoin("( ", str, " )", NULL);
733 }
734
735 static char *format_txt(DnsTxtItem *first) {
736 DnsTxtItem *i;
737 size_t c = 1;
738 char *p, *s;
739
740 LIST_FOREACH(items, i, first)
741 c += i->length * 4 + 3;
742
743 p = s = new(char, c);
744 if (!s)
745 return NULL;
746
747 LIST_FOREACH(items, i, first) {
748 size_t j;
749
750 if (i != first)
751 *(p++) = ' ';
752
753 *(p++) = '"';
754
755 for (j = 0; j < i->length; j++) {
756 if (i->data[j] < ' ' || i->data[j] == '"' || i->data[j] >= 127) {
757 *(p++) = '\\';
758 *(p++) = '0' + (i->data[j] / 100);
759 *(p++) = '0' + ((i->data[j] / 10) % 10);
760 *(p++) = '0' + (i->data[j] % 10);
761 } else
762 *(p++) = i->data[j];
763 }
764
765 *(p++) = '"';
766 }
767
768 *p = 0;
769 return s;
770 }
771
772 const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
773 _cleanup_free_ char *k = NULL, *t = NULL;
774 char *s;
775 int r;
776
777 assert(rr);
778
779 if (rr->to_string)
780 return rr->to_string;
781
782 r = dns_resource_key_to_string(rr->key, &k);
783 if (r < 0)
784 return NULL;
785
786 switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
787
788 case DNS_TYPE_SRV:
789 r = asprintf(&s, "%s %u %u %u %s",
790 k,
791 rr->srv.priority,
792 rr->srv.weight,
793 rr->srv.port,
794 strna(rr->srv.name));
795 if (r < 0)
796 return NULL;
797 break;
798
799 case DNS_TYPE_PTR:
800 case DNS_TYPE_NS:
801 case DNS_TYPE_CNAME:
802 case DNS_TYPE_DNAME:
803 s = strjoin(k, " ", rr->ptr.name, NULL);
804 if (!s)
805 return NULL;
806
807 break;
808
809 case DNS_TYPE_HINFO:
810 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
811 if (!s)
812 return NULL;
813 break;
814
815 case DNS_TYPE_SPF: /* exactly the same as TXT */
816 case DNS_TYPE_TXT:
817 t = format_txt(rr->txt.items);
818 if (!t)
819 return NULL;
820
821 s = strjoin(k, " ", t, NULL);
822 if (!s)
823 return NULL;
824 break;
825
826 case DNS_TYPE_A: {
827 _cleanup_free_ char *x = NULL;
828
829 r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
830 if (r < 0)
831 return NULL;
832
833 s = strjoin(k, " ", x, NULL);
834 if (!s)
835 return NULL;
836 break;
837 }
838
839 case DNS_TYPE_AAAA:
840 r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &t);
841 if (r < 0)
842 return NULL;
843
844 s = strjoin(k, " ", t, NULL);
845 if (!s)
846 return NULL;
847 break;
848
849 case DNS_TYPE_SOA:
850 r = asprintf(&s, "%s %s %s %u %u %u %u %u",
851 k,
852 strna(rr->soa.mname),
853 strna(rr->soa.rname),
854 rr->soa.serial,
855 rr->soa.refresh,
856 rr->soa.retry,
857 rr->soa.expire,
858 rr->soa.minimum);
859 if (r < 0)
860 return NULL;
861 break;
862
863 case DNS_TYPE_MX:
864 r = asprintf(&s, "%s %u %s",
865 k,
866 rr->mx.priority,
867 rr->mx.exchange);
868 if (r < 0)
869 return NULL;
870 break;
871
872 case DNS_TYPE_LOC:
873 assert(rr->loc.version == 0);
874
875 t = format_location(rr->loc.latitude,
876 rr->loc.longitude,
877 rr->loc.altitude,
878 rr->loc.size,
879 rr->loc.horiz_pre,
880 rr->loc.vert_pre);
881 if (!t)
882 return NULL;
883
884 s = strjoin(k, " ", t, NULL);
885 if (!s)
886 return NULL;
887 break;
888
889 case DNS_TYPE_DS:
890 t = hexmem(rr->ds.digest, rr->ds.digest_size);
891 if (!t)
892 return NULL;
893
894 r = asprintf(&s, "%s %u %u %u %s",
895 k,
896 rr->ds.key_tag,
897 rr->ds.algorithm,
898 rr->ds.digest_type,
899 t);
900 if (r < 0)
901 return NULL;
902 break;
903
904 case DNS_TYPE_SSHFP:
905 t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size);
906 if (!t)
907 return NULL;
908
909 r = asprintf(&s, "%s %u %u %s",
910 k,
911 rr->sshfp.algorithm,
912 rr->sshfp.fptype,
913 t);
914 if (r < 0)
915 return NULL;
916 break;
917
918 case DNS_TYPE_DNSKEY: {
919 _cleanup_free_ char *alg = NULL;
920
921 r = dnssec_algorithm_to_string_alloc(rr->dnskey.algorithm, &alg);
922 if (r < 0)
923 return NULL;
924
925 t = base64mem(rr->dnskey.key, rr->dnskey.key_size);
926 if (!t)
927 return NULL;
928
929 r = asprintf(&s, "%s %u %u %s %s",
930 k,
931 rr->dnskey.flags,
932 rr->dnskey.protocol,
933 alg,
934 t);
935 if (r < 0)
936 return NULL;
937 break;
938 }
939
940 case DNS_TYPE_RRSIG: {
941 _cleanup_free_ char *alg = NULL;
942 char expiration[strlen("YYYYMMDDHHmmSS") + 1], inception[strlen("YYYYMMDDHHmmSS") + 1];
943 const char *type;
944
945 type = dns_type_to_string(rr->rrsig.type_covered);
946
947 r = dnssec_algorithm_to_string_alloc(rr->rrsig.algorithm, &alg);
948 if (r < 0)
949 return NULL;
950
951 t = base64mem(rr->rrsig.signature, rr->rrsig.signature_size);
952 if (!t)
953 return NULL;
954
955 r = format_timestamp_dns(expiration, sizeof(expiration), rr->rrsig.expiration);
956 if (r < 0)
957 return NULL;
958
959 r = format_timestamp_dns(inception, sizeof(inception), rr->rrsig.inception);
960 if (r < 0)
961 return NULL;
962
963 /* TYPE?? follows
964 * http://tools.ietf.org/html/rfc3597#section-5 */
965
966 r = asprintf(&s, "%s %s%.*u %s %u %u %s %s %u %s %s",
967 k,
968 type ?: "TYPE",
969 type ? 0 : 1, type ? 0u : (unsigned) rr->rrsig.type_covered,
970 alg,
971 rr->rrsig.labels,
972 rr->rrsig.original_ttl,
973 expiration,
974 inception,
975 rr->rrsig.key_tag,
976 rr->rrsig.signer,
977 t);
978 if (r < 0)
979 return NULL;
980 break;
981 }
982
983 case DNS_TYPE_NSEC:
984 t = format_types(rr->nsec.types);
985 if (!t)
986 return NULL;
987
988 r = asprintf(&s, "%s %s %s",
989 k,
990 rr->nsec.next_domain_name,
991 t);
992 if (r < 0)
993 return NULL;
994 break;
995
996 case DNS_TYPE_NSEC3: {
997 _cleanup_free_ char *salt = NULL, *hash = NULL;
998
999 if (rr->nsec3.salt_size > 0) {
1000 salt = hexmem(rr->nsec3.salt, rr->nsec3.salt_size);
1001 if (!salt)
1002 return NULL;
1003 }
1004
1005 hash = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false);
1006 if (!hash)
1007 return NULL;
1008
1009 t = format_types(rr->nsec3.types);
1010 if (!t)
1011 return NULL;
1012
1013 r = asprintf(&s, "%s %"PRIu8" %"PRIu8" %"PRIu16" %s %s %s",
1014 k,
1015 rr->nsec3.algorithm,
1016 rr->nsec3.flags,
1017 rr->nsec3.iterations,
1018 rr->nsec3.salt_size > 0 ? salt : "-",
1019 hash,
1020 t);
1021 if (r < 0)
1022 return NULL;
1023
1024 break;
1025 }
1026
1027 default:
1028 t = hexmem(rr->generic.data, rr->generic.size);
1029 if (!t)
1030 return NULL;
1031
1032 /* Format as documented in RFC 3597, Section 5 */
1033 r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.size, t);
1034 if (r < 0)
1035 return NULL;
1036 break;
1037 }
1038
1039 rr->to_string = s;
1040 return s;
1041 }
1042
1043 int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) {
1044
1045 DnsPacket packet = {
1046 .n_ref = 1,
1047 .protocol = DNS_PROTOCOL_DNS,
1048 .on_stack = true,
1049 .refuse_compression = true,
1050 .canonical_form = canonical,
1051 };
1052
1053 size_t start, rds;
1054 int r;
1055
1056 assert(rr);
1057
1058 /* Generates the RR in wire-format, optionally in the
1059 * canonical form as discussed in the DNSSEC RFC 4034, Section
1060 * 6.2. We allocate a throw-away DnsPacket object on the stack
1061 * here, because we need some book-keeping for memory
1062 * management, and can reuse the DnsPacket serializer, that
1063 * can generate the canonical form, too, but also knows label
1064 * compression and suchlike. */
1065
1066 if (rr->wire_format && rr->wire_format_canonical == canonical)
1067 return 0;
1068
1069 r = dns_packet_append_rr(&packet, rr, &start, &rds);
1070 if (r < 0)
1071 return r;
1072
1073 assert(start == 0);
1074 assert(packet._data);
1075
1076 free(rr->wire_format);
1077 rr->wire_format = packet._data;
1078 rr->wire_format_size = packet.size;
1079 rr->wire_format_rdata_offset = rds;
1080 rr->wire_format_canonical = canonical;
1081
1082 packet._data = NULL;
1083 dns_packet_unref(&packet);
1084
1085 return 0;
1086 }
1087
1088 DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i) {
1089 DnsTxtItem *n;
1090
1091 if (!i)
1092 return NULL;
1093
1094 n = i->items_next;
1095
1096 free(i);
1097 return dns_txt_item_free_all(n);
1098 }
1099
1100 bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) {
1101
1102 if (a == b)
1103 return true;
1104
1105 if (!a != !b)
1106 return false;
1107
1108 if (!a)
1109 return true;
1110
1111 if (a->length != b->length)
1112 return false;
1113
1114 if (memcmp(a->data, b->data, a->length) != 0)
1115 return false;
1116
1117 return dns_txt_item_equal(a->items_next, b->items_next);
1118 }
1119
1120 static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = {
1121 /* Mnemonics as listed on https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
1122 [DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5",
1123 [DNSSEC_ALGORITHM_DH] = "DH",
1124 [DNSSEC_ALGORITHM_DSA] = "DSA",
1125 [DNSSEC_ALGORITHM_ECC] = "ECC",
1126 [DNSSEC_ALGORITHM_RSASHA1] = "RSASHA1",
1127 [DNSSEC_ALGORITHM_DSA_NSEC3_SHA1] = "DSA-NSEC3-SHA1",
1128 [DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1] = "RSASHA1-NSEC3-SHA1",
1129 [DNSSEC_ALGORITHM_RSASHA256] = "RSASHA256",
1130 [DNSSEC_ALGORITHM_RSASHA512] = "RSASHA512",
1131 [DNSSEC_ALGORITHM_ECC_GOST] = "ECC-GOST",
1132 [DNSSEC_ALGORITHM_ECDSAP256SHA256] = "ECDSAP256SHA256",
1133 [DNSSEC_ALGORITHM_ECDSAP384SHA384] = "ECDSAP384SHA384",
1134 [DNSSEC_ALGORITHM_INDIRECT] = "INDIRECT",
1135 [DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS",
1136 [DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID",
1137 };
1138 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(dnssec_algorithm, int, 255);
1139
1140 static const char* const dnssec_digest_table[_DNSSEC_DIGEST_MAX_DEFINED] = {
1141 /* Names as listed on https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */
1142 [DNSSEC_DIGEST_SHA1] = "SHA-1",
1143 [DNSSEC_DIGEST_SHA256] = "SHA-256",
1144 [DNSSEC_DIGEST_GOST_R_34_11_94] = "GOST_R_34.11-94",
1145 [DNSSEC_DIGEST_SHA384] = "SHA-384",
1146 };
1147 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(dnssec_digest, int, 255);