]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-rr.c
resolved: fix serialization of the root domain
[thirdparty/systemd.git] / src / resolve / resolved-dns-rr.c
CommitLineData
74b2466e
LP
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
0dae31d4
ZJS
22#include <math.h>
23
b5efdb8a 24#include "alloc-util.h"
4ad7f276 25#include "dns-domain.h"
7263f724 26#include "dns-type.h"
e4e73a63 27#include "hexdecoct.h"
07630cea 28#include "resolved-dns-packet.h"
e4e73a63 29#include "resolved-dns-rr.h"
8730bccf 30#include "string-table.h"
07630cea
LP
31#include "string-util.h"
32#include "strv.h"
74b2466e 33
faa133f3
LP
34DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) {
35 DnsResourceKey *k;
36 size_t l;
74b2466e 37
faa133f3
LP
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
36d9205d 54DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname) {
58db254a
LP
55 int r;
56
36d9205d
TG
57 assert(key);
58 assert(cname);
59
58db254a
LP
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 }
36d9205d
TG
82}
83
801ad6a6
LP
84int 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
dc477e73 93 if (dns_name_is_root(name)) {
801ad6a6
LP
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
faa133f3
LP
112DnsResourceKey* 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
129DnsResourceKey* dns_resource_key_ref(DnsResourceKey *k) {
130
131 if (!k)
132 return NULL;
133
1b4f6e79
LP
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
faa133f3
LP
138 assert(k->n_ref > 0);
139 k->n_ref++;
140
141 return k;
142}
143
144DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) {
145 if (!k)
146 return NULL;
147
1b4f6e79 148 assert(k->n_ref != (unsigned) -1);
faa133f3
LP
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
28b9b764
LP
160bool 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
faa133f3
LP
168int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
169 int r;
170
4d247a6c
LP
171 if (a == b)
172 return 1;
173
faa133f3
LP
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
105e1512 187int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr, const char *search_domain) {
801ad6a6
LP
188 int r;
189
faa133f3
LP
190 assert(key);
191 assert(rr);
192
4d247a6c
LP
193 if (key == rr->key)
194 return 1;
195
801ad6a6
LP
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
faa133f3
LP
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
801ad6a6
LP
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;
faa133f3
LP
221}
222
5d27351f 223int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsResourceKey *cname, const char *search_domain) {
801ad6a6
LP
224 int r;
225
faa133f3 226 assert(key);
5d27351f 227 assert(cname);
faa133f3 228
5d27351f 229 if (cname->class != key->class && key->class != DNS_CLASS_ANY)
faa133f3
LP
230 return 0;
231
5d27351f
TG
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));
58db254a 236 else
faa133f3 237 return 0;
801ad6a6
LP
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
5d27351f
TG
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));
801ad6a6
LP
253 }
254
255 return 0;
547973de
LP
256}
257
258int 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
65b200e7 264 if (soa->class != key->class)
547973de 265 return 0;
801ad6a6 266
547973de
LP
267 if (soa->type != DNS_TYPE_SOA)
268 return 0;
269
0936416a 270 return dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(soa));
74b2466e
LP
271}
272
b826ab58 273static void dns_resource_key_hash_func(const void *i, struct siphash *state) {
322345fd 274 const DnsResourceKey *k = i;
322345fd 275
b826ab58 276 assert(k);
322345fd 277
b826ab58
TG
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);
322345fd
LP
281}
282
d5099efc 283static int dns_resource_key_compare_func(const void *a, const void *b) {
322345fd
LP
284 const DnsResourceKey *x = a, *y = b;
285 int ret;
286
faa133f3 287 ret = dns_name_compare_func(DNS_RESOURCE_KEY_NAME(x), DNS_RESOURCE_KEY_NAME(y));
322345fd
LP
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
d5099efc
MS
304const struct hash_ops dns_resource_key_hash_ops = {
305 .hash = dns_resource_key_hash_func,
306 .compare = dns_resource_key_compare_func
307};
308
2d4c5cbc 309int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) {
d23a27a9 310 char cbuf[strlen("CLASS") + DECIMAL_STR_MAX(uint16_t)], tbuf[strlen("TYPE") + DECIMAL_STR_MAX(uint16_t)];
f2af5ea3 311 const char *c, *t, *n;
2d4c5cbc
LP
312 char *s;
313
6af47493
LP
314 /* If we cannot convert the CLASS/TYPE into a known string,
315 use the format recommended by RFC 3597, Section 5. */
316
2d4c5cbc
LP
317 c = dns_class_to_string(key->class);
318 if (!c) {
d23a27a9 319 sprintf(cbuf, "CLASS%u", key->class);
2d4c5cbc
LP
320 c = cbuf;
321 }
322
323 t = dns_type_to_string(key->type);
324 if (!t){
d23a27a9 325 sprintf(tbuf, "TYPE%u", key->type);
2d4c5cbc
LP
326 t = tbuf;
327 }
328
f2af5ea3
LP
329 n = DNS_RESOURCE_KEY_NAME(key);
330 if (asprintf(&s, "%s%s %s %-5s", n, endswith(n, ".") ? "" : ".", c, t) < 0)
2d4c5cbc
LP
331 return -ENOMEM;
332
333 *ret = s;
334 return 0;
335}
336
faa133f3 337DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
74b2466e
LP
338 DnsResourceRecord *rr;
339
340 rr = new0(DnsResourceRecord, 1);
341 if (!rr)
342 return NULL;
343
344 rr->n_ref = 1;
faa133f3 345 rr->key = dns_resource_key_ref(key);
ee3d6aff 346 rr->expiry = USEC_INFINITY;
faa133f3 347
74b2466e
LP
348 return rr;
349}
350
8bf52d3d
LP
351DnsResourceRecord* 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
74b2466e
LP
361DnsResourceRecord* 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
371DnsResourceRecord* 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
faa133f3 382 if (rr->key) {
9de3e329 383 switch(rr->key->type) {
9c92ce6d
LP
384
385 case DNS_TYPE_SRV:
386 free(rr->srv.name);
387 break;
388
9de3e329
ZJS
389 case DNS_TYPE_PTR:
390 case DNS_TYPE_NS:
391 case DNS_TYPE_CNAME:
8ac4e9e1 392 case DNS_TYPE_DNAME:
faa133f3 393 free(rr->ptr.name);
9de3e329 394 break;
9c92ce6d 395
9de3e329 396 case DNS_TYPE_HINFO:
faa133f3
LP
397 free(rr->hinfo.cpu);
398 free(rr->hinfo.os);
9de3e329 399 break;
9c92ce6d 400
9de3e329 401 case DNS_TYPE_TXT:
9c92ce6d 402 case DNS_TYPE_SPF:
2001c805 403 dns_txt_item_free_all(rr->txt.items);
9de3e329 404 break;
9c92ce6d 405
9de3e329 406 case DNS_TYPE_SOA:
7e8e0422
LP
407 free(rr->soa.mname);
408 free(rr->soa.rname);
9de3e329 409 break;
9c92ce6d 410
9de3e329 411 case DNS_TYPE_MX:
946c7094 412 free(rr->mx.exchange);
9de3e329 413 break;
9c92ce6d 414
abf126a3
TG
415 case DNS_TYPE_DS:
416 free(rr->ds.digest);
417 break;
418
42cc2eeb 419 case DNS_TYPE_SSHFP:
549c1a25 420 free(rr->sshfp.fingerprint);
42cc2eeb
LP
421 break;
422
8db0d2f5
ZJS
423 case DNS_TYPE_DNSKEY:
424 free(rr->dnskey.key);
425 break;
426
151226ab
ZJS
427 case DNS_TYPE_RRSIG:
428 free(rr->rrsig.signer);
429 free(rr->rrsig.signature);
430 break;
431
50f1e641
TG
432 case DNS_TYPE_NSEC:
433 free(rr->nsec.next_domain_name);
434 bitmap_free(rr->nsec.types);
435 break;
436
5d45a880
TG
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
0dae31d4 443 case DNS_TYPE_LOC:
9de3e329
ZJS
444 case DNS_TYPE_A:
445 case DNS_TYPE_AAAA:
446 break;
9c92ce6d 447
9de3e329 448 default:
faa133f3 449 free(rr->generic.data);
9de3e329 450 }
322345fd 451
a8812dd7 452 free(rr->wire_format);
faa133f3
LP
453 dns_resource_key_unref(rr->key);
454 }
322345fd 455
7b50eb2e 456 free(rr->to_string);
faa133f3 457 free(rr);
322345fd 458
322345fd
LP
459 return NULL;
460}
461
623a4c97
LP
462int 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
78c6a153
LP
496int 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
322345fd
LP
526int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
527 int r;
528
529 assert(a);
530 assert(b);
531
4d247a6c
LP
532 if (a == b)
533 return 1;
534
faa133f3 535 r = dns_resource_key_equal(a->key, b->key);
322345fd
LP
536 if (r <= 0)
537 return r;
538
fd0b4602
LP
539 if (a->unparseable != b->unparseable)
540 return 0;
541
542 switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) {
2d4c5cbc 543
9c92ce6d
LP
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
2d4c5cbc
LP
553 case DNS_TYPE_PTR:
554 case DNS_TYPE_NS:
555 case DNS_TYPE_CNAME:
8ac4e9e1 556 case DNS_TYPE_DNAME:
322345fd 557 return dns_name_equal(a->ptr.name, b->ptr.name);
2d4c5cbc
LP
558
559 case DNS_TYPE_HINFO:
560 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
561 strcaseeq(a->hinfo.os, b->hinfo.os);
562
9de3e329 563 case DNS_TYPE_SPF: /* exactly the same as TXT */
0f84a72e 564 case DNS_TYPE_TXT:
2001c805 565 return dns_txt_item_equal(a->txt.items, b->txt.items);
2e276efc 566
2d4c5cbc 567 case DNS_TYPE_A:
322345fd 568 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
2d4c5cbc
LP
569
570 case DNS_TYPE_AAAA:
322345fd 571 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
2d4c5cbc
LP
572
573 case DNS_TYPE_SOA:
7e8e0422
LP
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;
9c92ce6d 586
946c7094
ZJS
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
0dae31d4
ZJS
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
abf126a3
TG
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
42cc2eeb
LP
610 case DNS_TYPE_SSHFP:
611 return a->sshfp.algorithm == b->sshfp.algorithm &&
612 a->sshfp.fptype == b->sshfp.fptype &&
549c1a25
TG
613 a->sshfp.fingerprint_size == b->sshfp.fingerprint_size &&
614 memcmp(a->sshfp.fingerprint, b->sshfp.fingerprint, a->sshfp.fingerprint_size) == 0;
42cc2eeb 615
8db0d2f5 616 case DNS_TYPE_DNSKEY:
f91dc240
LP
617 return a->dnskey.flags == b->dnskey.flags &&
618 a->dnskey.protocol == b->dnskey.protocol &&
8db0d2f5
ZJS
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
151226ab
ZJS
623 case DNS_TYPE_RRSIG:
624 /* do the fast comparisons first */
03664a62
LN
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 ||
151226ab
ZJS
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
50f1e641
TG
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
5d45a880
TG
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
2d4c5cbc 651 default:
322345fd
LP
652 return a->generic.size == b->generic.size &&
653 memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
2d4c5cbc 654 }
322345fd
LP
655}
656
0dae31d4
ZJS
657static 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
7c6423e1
TG
688static 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
50f1e641
TG
703static char *format_types(Bitmap *types) {
704 _cleanup_strv_free_ char **strv = NULL;
705 _cleanup_free_ char *str = NULL;
cb57dd41 706 Iterator i;
50f1e641
TG
707 unsigned type;
708 int r;
709
cb57dd41 710 BITMAP_FOREACH(type, types, i) {
50f1e641 711 if (dns_type_to_string(type)) {
2c1fb4f7 712 r = strv_extend(&strv, dns_type_to_string(type));
50f1e641
TG
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
2c1fb4f7 722 r = strv_consume(&strv, t);
50f1e641
TG
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
2001c805
LP
735static 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
7b50eb2e 772const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
8db0d2f5 773 _cleanup_free_ char *k = NULL, *t = NULL;
2d4c5cbc
LP
774 char *s;
775 int r;
322345fd 776
2d4c5cbc 777 assert(rr);
322345fd 778
7b50eb2e
LP
779 if (rr->to_string)
780 return rr->to_string;
781
2d4c5cbc
LP
782 r = dns_resource_key_to_string(rr->key, &k);
783 if (r < 0)
7b50eb2e 784 return NULL;
322345fd 785
0dae31d4 786 switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
322345fd 787
9c92ce6d
LP
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)
7b50eb2e 796 return NULL;
9c92ce6d
LP
797 break;
798
2d4c5cbc
LP
799 case DNS_TYPE_PTR:
800 case DNS_TYPE_NS:
801 case DNS_TYPE_CNAME:
8ac4e9e1 802 case DNS_TYPE_DNAME:
2d4c5cbc
LP
803 s = strjoin(k, " ", rr->ptr.name, NULL);
804 if (!s)
7b50eb2e 805 return NULL;
322345fd 806
2d4c5cbc 807 break;
322345fd 808
2d4c5cbc
LP
809 case DNS_TYPE_HINFO:
810 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
811 if (!s)
7b50eb2e 812 return NULL;
2d4c5cbc 813 break;
322345fd 814
9de3e329 815 case DNS_TYPE_SPF: /* exactly the same as TXT */
8db0d2f5 816 case DNS_TYPE_TXT:
2001c805 817 t = format_txt(rr->txt.items);
2e276efc 818 if (!t)
7b50eb2e 819 return NULL;
2e276efc
ZJS
820
821 s = strjoin(k, " ", t, NULL);
822 if (!s)
7b50eb2e 823 return NULL;
2e276efc 824 break;
2e276efc 825
2d4c5cbc
LP
826 case DNS_TYPE_A: {
827 _cleanup_free_ char *x = NULL;
322345fd 828
2d4c5cbc
LP
829 r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
830 if (r < 0)
7b50eb2e 831 return NULL;
322345fd 832
2d4c5cbc
LP
833 s = strjoin(k, " ", x, NULL);
834 if (!s)
7b50eb2e 835 return NULL;
2d4c5cbc
LP
836 break;
837 }
322345fd 838
8db0d2f5
ZJS
839 case DNS_TYPE_AAAA:
840 r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &t);
2d4c5cbc 841 if (r < 0)
7b50eb2e 842 return NULL;
322345fd 843
8db0d2f5 844 s = strjoin(k, " ", t, NULL);
2d4c5cbc 845 if (!s)
7b50eb2e 846 return NULL;
2d4c5cbc 847 break;
322345fd 848
2d4c5cbc
LP
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)
7b50eb2e 860 return NULL;
2d4c5cbc
LP
861 break;
862
946c7094
ZJS
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)
7b50eb2e 869 return NULL;
946c7094
ZJS
870 break;
871
8db0d2f5 872 case DNS_TYPE_LOC:
0dae31d4
ZJS
873 assert(rr->loc.version == 0);
874
8db0d2f5
ZJS
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)
7b50eb2e 882 return NULL;
0dae31d4 883
8db0d2f5 884 s = strjoin(k, " ", t, NULL);
0dae31d4 885 if (!s)
7b50eb2e 886 return NULL;
0dae31d4 887 break;
0dae31d4 888
abf126a3
TG
889 case DNS_TYPE_DS:
890 t = hexmem(rr->ds.digest, rr->ds.digest_size);
891 if (!t)
7b50eb2e 892 return NULL;
abf126a3
TG
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)
7b50eb2e 901 return NULL;
abf126a3
TG
902 break;
903
8db0d2f5 904 case DNS_TYPE_SSHFP:
549c1a25 905 t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size);
8db0d2f5 906 if (!t)
7b50eb2e 907 return NULL;
42cc2eeb
LP
908
909 r = asprintf(&s, "%s %u %u %s",
910 k,
911 rr->sshfp.algorithm,
912 rr->sshfp.fptype,
8db0d2f5 913 t);
42cc2eeb 914 if (r < 0)
7b50eb2e 915 return NULL;
42cc2eeb 916 break;
42cc2eeb 917
ff3d6560
ZJS
918 case DNS_TYPE_DNSKEY: {
919 const char *alg;
920
921 alg = dnssec_algorithm_to_string(rr->dnskey.algorithm);
922
1bf968f3 923 t = base64mem(rr->dnskey.key, rr->dnskey.key_size);
8db0d2f5 924 if (!t)
7b50eb2e 925 return NULL;
2d4c5cbc 926
f91dc240 927 r = asprintf(&s, "%s %u %u %.*s%.*u %s",
8db0d2f5 928 k,
f91dc240
LP
929 rr->dnskey.flags,
930 rr->dnskey.protocol,
ff3d6560
ZJS
931 alg ? -1 : 0, alg,
932 alg ? 0 : 1, alg ? 0u : (unsigned) rr->dnskey.algorithm,
8db0d2f5
ZJS
933 t);
934 if (r < 0)
7b50eb2e 935 return NULL;
8db0d2f5 936 break;
ff3d6560 937 }
2d4c5cbc 938
151226ab
ZJS
939 case DNS_TYPE_RRSIG: {
940 const char *type, *alg;
7c6423e1 941 char expiration[strlen("YYYYMMDDHHmmSS") + 1], inception[strlen("YYYYMMDDHHmmSS") + 1];
151226ab
ZJS
942
943 type = dns_type_to_string(rr->rrsig.type_covered);
944 alg = dnssec_algorithm_to_string(rr->rrsig.algorithm);
945
1bf968f3 946 t = base64mem(rr->rrsig.signature, rr->rrsig.signature_size);
151226ab 947 if (!t)
7b50eb2e 948 return NULL;
151226ab 949
7c6423e1
TG
950 r = format_timestamp_dns(expiration, sizeof(expiration), rr->rrsig.expiration);
951 if (r < 0)
7b50eb2e 952 return NULL;
7c6423e1
TG
953
954 r = format_timestamp_dns(inception, sizeof(inception), rr->rrsig.inception);
955 if (r < 0)
7b50eb2e 956 return NULL;
7c6423e1 957
151226ab
ZJS
958 /* TYPE?? follows
959 * http://tools.ietf.org/html/rfc3597#section-5 */
960
7c6423e1 961 r = asprintf(&s, "%s %s%.*u %.*s%.*u %u %u %s %s %u %s %s",
151226ab
ZJS
962 k,
963 type ?: "TYPE",
964 type ? 0 : 1, type ? 0u : (unsigned) rr->rrsig.type_covered,
965 alg ? -1 : 0, alg,
966 alg ? 0 : 1, alg ? 0u : (unsigned) rr->rrsig.algorithm,
967 rr->rrsig.labels,
968 rr->rrsig.original_ttl,
7c6423e1
TG
969 expiration,
970 inception,
151226ab
ZJS
971 rr->rrsig.key_tag,
972 rr->rrsig.signer,
973 t);
974 if (r < 0)
7b50eb2e 975 return NULL;
151226ab
ZJS
976 break;
977 }
978
50f1e641
TG
979 case DNS_TYPE_NSEC:
980 t = format_types(rr->nsec.types);
981 if (!t)
7b50eb2e 982 return NULL;
50f1e641
TG
983
984 r = asprintf(&s, "%s %s %s",
985 k,
986 rr->nsec.next_domain_name,
987 t);
988 if (r < 0)
7b50eb2e 989 return NULL;
50f1e641
TG
990 break;
991
5d45a880
TG
992 case DNS_TYPE_NSEC3: {
993 _cleanup_free_ char *salt = NULL, *hash = NULL;
994
f5430a3e 995 if (rr->nsec3.salt_size > 0) {
5d45a880
TG
996 salt = hexmem(rr->nsec3.salt, rr->nsec3.salt_size);
997 if (!salt)
7b50eb2e 998 return NULL;
5d45a880
TG
999 }
1000
1001 hash = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false);
1002 if (!hash)
7b50eb2e 1003 return NULL;
5d45a880
TG
1004
1005 t = format_types(rr->nsec3.types);
1006 if (!t)
7b50eb2e 1007 return NULL;
5d45a880
TG
1008
1009 r = asprintf(&s, "%s %"PRIu8" %"PRIu8" %"PRIu16" %s %s %s",
1010 k,
1011 rr->nsec3.algorithm,
1012 rr->nsec3.flags,
1013 rr->nsec3.iterations,
f5430a3e 1014 rr->nsec3.salt_size > 0 ? salt : "-",
5d45a880
TG
1015 hash,
1016 t);
1017 if (r < 0)
7b50eb2e 1018 return NULL;
5d45a880
TG
1019
1020 break;
1021 }
1022
8db0d2f5
ZJS
1023 default:
1024 t = hexmem(rr->generic.data, rr->generic.size);
1025 if (!t)
7b50eb2e 1026 return NULL;
8db0d2f5 1027
6af47493 1028 /* Format as documented in RFC 3597, Section 5 */
f5430a3e 1029 r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.size, t);
d23a27a9 1030 if (r < 0)
7b50eb2e 1031 return NULL;
2d4c5cbc 1032 break;
8db0d2f5 1033 }
2d4c5cbc 1034
7b50eb2e
LP
1035 rr->to_string = s;
1036 return s;
2d4c5cbc 1037}
322345fd 1038
a8812dd7
LP
1039int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) {
1040
1041 DnsPacket packet = {
1042 .n_ref = 1,
1043 .protocol = DNS_PROTOCOL_DNS,
1044 .on_stack = true,
1045 .refuse_compression = true,
1046 .canonical_form = canonical,
1047 };
1048
1049 size_t start, rds;
1050 int r;
1051
1052 assert(rr);
1053
1054 /* Generates the RR in wire-format, optionally in the
1055 * canonical form as discussed in the DNSSEC RFC 4034, Section
1056 * 6.2. We allocate a throw-away DnsPacket object on the stack
1057 * here, because we need some book-keeping for memory
1058 * management, and can reuse the DnsPacket serializer, that
1059 * can generate the canonical form, too, but also knows label
1060 * compression and suchlike. */
1061
1062 if (rr->wire_format && rr->wire_format_canonical == canonical)
1063 return 0;
1064
1065 r = dns_packet_append_rr(&packet, rr, &start, &rds);
1066 if (r < 0)
1067 return r;
1068
1069 assert(start == 0);
1070 assert(packet._data);
1071
1072 free(rr->wire_format);
1073 rr->wire_format = packet._data;
1074 rr->wire_format_size = packet.size;
1075 rr->wire_format_rdata_offset = rds;
1076 rr->wire_format_canonical = canonical;
1077
1078 packet._data = NULL;
1079 dns_packet_unref(&packet);
1080
1081 return 0;
1082}
1083
2001c805
LP
1084DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i) {
1085 DnsTxtItem *n;
1086
1087 if (!i)
1088 return NULL;
1089
1090 n = i->items_next;
1091
1092 free(i);
1093 return dns_txt_item_free_all(n);
1094}
1095
1096bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) {
1097
4d247a6c
LP
1098 if (a == b)
1099 return true;
1100
2001c805
LP
1101 if (!a != !b)
1102 return false;
1103
1104 if (!a)
1105 return true;
1106
1107 if (a->length != b->length)
1108 return false;
1109
1110 if (memcmp(a->data, b->data, a->length) != 0)
1111 return false;
1112
1113 return dns_txt_item_equal(a->items_next, b->items_next);
1114}
8730bccf
LP
1115
1116static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = {
6f717d08 1117 /* Mnemonics as listed on https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
8730bccf
LP
1118 [DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5",
1119 [DNSSEC_ALGORITHM_DH] = "DH",
1120 [DNSSEC_ALGORITHM_DSA] = "DSA",
1121 [DNSSEC_ALGORITHM_ECC] = "ECC",
1122 [DNSSEC_ALGORITHM_RSASHA1] = "RSASHA1",
1123 [DNSSEC_ALGORITHM_DSA_NSEC3_SHA1] = "DSA-NSEC3-SHA1",
1124 [DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1] = "RSASHA1-NSEC3-SHA1",
1125 [DNSSEC_ALGORITHM_RSASHA256] = "RSASHA256",
1126 [DNSSEC_ALGORITHM_RSASHA512] = "RSASHA512",
6f717d08
LP
1127 [DNSSEC_ALGORITHM_ECC_GOST] = "ECC-GOST",
1128 [DNSSEC_ALGORITHM_ECDSAP256SHA256] = "ECDSAP256SHA256",
1129 [DNSSEC_ALGORITHM_ECDSAP384SHA384] = "ECDSAP384SHA384",
8730bccf
LP
1130 [DNSSEC_ALGORITHM_INDIRECT] = "INDIRECT",
1131 [DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS",
1132 [DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID",
1133};
1134DEFINE_STRING_TABLE_LOOKUP(dnssec_algorithm, int);
1135
1136static const char* const dnssec_digest_table[_DNSSEC_DIGEST_MAX_DEFINED] = {
6f717d08
LP
1137 /* Names as listed on https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */
1138 [DNSSEC_DIGEST_SHA1] = "SHA-1",
1139 [DNSSEC_DIGEST_SHA256] = "SHA-256",
1140 [DNSSEC_DIGEST_GOST_R_34_11_94] = "GOST_R_34.11-94",
1141 [DNSSEC_DIGEST_SHA384] = "SHA-384",
8730bccf
LP
1142};
1143DEFINE_STRING_TABLE_LOOKUP(dnssec_digest, int);