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