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