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