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