resolved: add identifiers for dnssec algorithms
[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
2e276efc
ZJS
24#include "strv.h"
25
322345fd 26#include "resolved-dns-domain.h"
74b2466e 27#include "resolved-dns-rr.h"
8db0d2f5 28#include "resolved-dns-packet.h"
7263f724 29#include "dns-type.h"
74b2466e 30
faa133f3
LP
31DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) {
32 DnsResourceKey *k;
33 size_t l;
74b2466e 34
faa133f3
LP
35 assert(name);
36
37 l = strlen(name);
38 k = malloc0(sizeof(DnsResourceKey) + l + 1);
39 if (!k)
40 return NULL;
41
42 k->n_ref = 1;
43 k->class = class;
44 k->type = type;
45
46 strcpy((char*) k + sizeof(DnsResourceKey), name);
47
48 return k;
49}
50
51DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name) {
52 DnsResourceKey *k;
53
54 assert(name);
55
56 k = new0(DnsResourceKey, 1);
57 if (!k)
58 return NULL;
59
60 k->n_ref = 1;
61 k->class = class;
62 k->type = type;
63 k->_name = name;
64
65 return k;
66}
67
68DnsResourceKey* dns_resource_key_ref(DnsResourceKey *k) {
69
70 if (!k)
71 return NULL;
72
73 assert(k->n_ref > 0);
74 k->n_ref++;
75
76 return k;
77}
78
79DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) {
80 if (!k)
81 return NULL;
82
83 assert(k->n_ref > 0);
84
85 if (k->n_ref == 1) {
86 free(k->_name);
87 free(k);
88 } else
89 k->n_ref--;
90
91 return NULL;
92}
93
94int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
95 int r;
96
97 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b));
98 if (r <= 0)
99 return r;
100
101 if (a->class != b->class)
102 return 0;
103
104 if (a->type != b->type)
105 return 0;
106
107 return 1;
108}
109
110int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr) {
111 assert(key);
112 assert(rr);
113
114 if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
115 return 0;
116
117 if (rr->key->type != key->type && key->type != DNS_TYPE_ANY)
118 return 0;
119
120 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
121}
122
123int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr) {
124 assert(key);
125 assert(rr);
126
127 if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
128 return 0;
129
130 if (rr->key->type != DNS_TYPE_CNAME)
131 return 0;
132
133 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
74b2466e
LP
134}
135
322345fd
LP
136unsigned long dns_resource_key_hash_func(const void *i, const uint8_t hash_key[HASH_KEY_SIZE]) {
137 const DnsResourceKey *k = i;
138 unsigned long ul;
139
faa133f3 140 ul = dns_name_hash_func(DNS_RESOURCE_KEY_NAME(k), hash_key);
322345fd
LP
141 ul = ul * hash_key[0] + ul + k->class;
142 ul = ul * hash_key[1] + ul + k->type;
143
144 return ul;
145}
146
147int dns_resource_key_compare_func(const void *a, const void *b) {
148 const DnsResourceKey *x = a, *y = b;
149 int ret;
150
faa133f3 151 ret = dns_name_compare_func(DNS_RESOURCE_KEY_NAME(x), DNS_RESOURCE_KEY_NAME(y));
322345fd
LP
152 if (ret != 0)
153 return ret;
154
155 if (x->type < y->type)
156 return -1;
157 if (x->type > y->type)
158 return 1;
159
160 if (x->class < y->class)
161 return -1;
162 if (x->class > y->class)
163 return 1;
164
165 return 0;
166}
167
2d4c5cbc
LP
168int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) {
169 char cbuf[DECIMAL_STR_MAX(uint16_t)], tbuf[DECIMAL_STR_MAX(uint16_t)];
170 const char *c, *t;
171 char *s;
172
173 c = dns_class_to_string(key->class);
174 if (!c) {
175 sprintf(cbuf, "%i", key->class);
176 c = cbuf;
177 }
178
179 t = dns_type_to_string(key->type);
180 if (!t){
181 sprintf(tbuf, "%i", key->type);
182 t = tbuf;
183 }
184
23432a1c 185 if (asprintf(&s, "%s %s %-5s", DNS_RESOURCE_KEY_NAME(key), c, t) < 0)
2d4c5cbc
LP
186 return -ENOMEM;
187
188 *ret = s;
189 return 0;
190}
191
faa133f3 192DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
74b2466e
LP
193 DnsResourceRecord *rr;
194
195 rr = new0(DnsResourceRecord, 1);
196 if (!rr)
197 return NULL;
198
199 rr->n_ref = 1;
faa133f3
LP
200 rr->key = dns_resource_key_ref(key);
201
74b2466e
LP
202 return rr;
203}
204
8bf52d3d
LP
205DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name) {
206 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
207
208 key = dns_resource_key_new(class, type, name);
209 if (!key)
210 return NULL;
211
212 return dns_resource_record_new(key);
213}
214
74b2466e
LP
215DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr) {
216 if (!rr)
217 return NULL;
218
219 assert(rr->n_ref > 0);
220 rr->n_ref++;
221
222 return rr;
223}
224
225DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
226 if (!rr)
227 return NULL;
228
229 assert(rr->n_ref > 0);
230
231 if (rr->n_ref > 1) {
232 rr->n_ref--;
233 return NULL;
234 }
235
faa133f3 236 if (rr->key) {
9de3e329 237 switch(rr->key->type) {
9c92ce6d
LP
238
239 case DNS_TYPE_SRV:
240 free(rr->srv.name);
241 break;
242
9de3e329
ZJS
243 case DNS_TYPE_PTR:
244 case DNS_TYPE_NS:
245 case DNS_TYPE_CNAME:
8ac4e9e1 246 case DNS_TYPE_DNAME:
faa133f3 247 free(rr->ptr.name);
9de3e329 248 break;
9c92ce6d 249
9de3e329 250 case DNS_TYPE_HINFO:
faa133f3
LP
251 free(rr->hinfo.cpu);
252 free(rr->hinfo.os);
9de3e329 253 break;
9c92ce6d 254
9de3e329 255 case DNS_TYPE_TXT:
9c92ce6d 256 case DNS_TYPE_SPF:
2e276efc 257 strv_free(rr->txt.strings);
9de3e329 258 break;
9c92ce6d 259
9de3e329 260 case DNS_TYPE_SOA:
7e8e0422
LP
261 free(rr->soa.mname);
262 free(rr->soa.rname);
9de3e329 263 break;
9c92ce6d 264
9de3e329 265 case DNS_TYPE_MX:
946c7094 266 free(rr->mx.exchange);
9de3e329 267 break;
9c92ce6d 268
42cc2eeb
LP
269 case DNS_TYPE_SSHFP:
270 free(rr->sshfp.key);
271 break;
272
8db0d2f5
ZJS
273 case DNS_TYPE_DNSKEY:
274 free(rr->dnskey.key);
275 break;
276
0dae31d4 277 case DNS_TYPE_LOC:
9de3e329
ZJS
278 case DNS_TYPE_A:
279 case DNS_TYPE_AAAA:
280 break;
9c92ce6d 281
9de3e329 282 default:
faa133f3 283 free(rr->generic.data);
9de3e329 284 }
322345fd 285
faa133f3
LP
286 dns_resource_key_unref(rr->key);
287 }
322345fd 288
faa133f3 289 free(rr);
322345fd 290
322345fd
LP
291 return NULL;
292}
293
623a4c97
LP
294int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) {
295 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
296 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
297 _cleanup_free_ char *ptr = NULL;
298 int r;
299
300 assert(ret);
301 assert(address);
302 assert(hostname);
303
304 r = dns_name_reverse(family, address, &ptr);
305 if (r < 0)
306 return r;
307
308 key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, ptr);
309 if (!key)
310 return -ENOMEM;
311
312 ptr = NULL;
313
314 rr = dns_resource_record_new(key);
315 if (!rr)
316 return -ENOMEM;
317
318 rr->ptr.name = strdup(hostname);
319 if (!rr->ptr.name)
320 return -ENOMEM;
321
322 *ret = rr;
323 rr = NULL;
324
325 return 0;
326}
327
322345fd
LP
328int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
329 int r;
330
331 assert(a);
332 assert(b);
333
faa133f3 334 r = dns_resource_key_equal(a->key, b->key);
322345fd
LP
335 if (r <= 0)
336 return r;
337
fd0b4602
LP
338 if (a->unparseable != b->unparseable)
339 return 0;
340
341 switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) {
2d4c5cbc 342
9c92ce6d
LP
343 case DNS_TYPE_SRV:
344 r = dns_name_equal(a->srv.name, b->srv.name);
345 if (r <= 0)
346 return r;
347
348 return a->srv.priority == b->srv.priority &&
349 a->srv.weight == b->srv.weight &&
350 a->srv.port == b->srv.port;
351
2d4c5cbc
LP
352 case DNS_TYPE_PTR:
353 case DNS_TYPE_NS:
354 case DNS_TYPE_CNAME:
8ac4e9e1 355 case DNS_TYPE_DNAME:
322345fd 356 return dns_name_equal(a->ptr.name, b->ptr.name);
2d4c5cbc
LP
357
358 case DNS_TYPE_HINFO:
359 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
360 strcaseeq(a->hinfo.os, b->hinfo.os);
361
9de3e329 362 case DNS_TYPE_SPF: /* exactly the same as TXT */
2e276efc
ZJS
363 case DNS_TYPE_TXT: {
364 int i;
365
366 for (i = 0; a->txt.strings[i] || b->txt.strings[i]; i++)
367 if (!streq_ptr(a->txt.strings[i], b->txt.strings[i]))
368 return false;
369 return true;
370 }
371
2d4c5cbc 372 case DNS_TYPE_A:
322345fd 373 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
2d4c5cbc
LP
374
375 case DNS_TYPE_AAAA:
322345fd 376 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
2d4c5cbc
LP
377
378 case DNS_TYPE_SOA:
7e8e0422
LP
379 r = dns_name_equal(a->soa.mname, b->soa.mname);
380 if (r <= 0)
381 return r;
382 r = dns_name_equal(a->soa.rname, b->soa.rname);
383 if (r <= 0)
384 return r;
385
386 return a->soa.serial == b->soa.serial &&
387 a->soa.refresh == b->soa.refresh &&
388 a->soa.retry == b->soa.retry &&
389 a->soa.expire == b->soa.expire &&
390 a->soa.minimum == b->soa.minimum;
9c92ce6d 391
946c7094
ZJS
392 case DNS_TYPE_MX:
393 if (a->mx.priority != b->mx.priority)
394 return 0;
395
396 return dns_name_equal(a->mx.exchange, b->mx.exchange);
397
0dae31d4
ZJS
398 case DNS_TYPE_LOC:
399 assert(a->loc.version == b->loc.version);
400
401 return a->loc.size == b->loc.size &&
402 a->loc.horiz_pre == b->loc.horiz_pre &&
403 a->loc.vert_pre == b->loc.vert_pre &&
404 a->loc.latitude == b->loc.latitude &&
405 a->loc.longitude == b->loc.longitude &&
406 a->loc.altitude == b->loc.altitude;
407
42cc2eeb
LP
408 case DNS_TYPE_SSHFP:
409 return a->sshfp.algorithm == b->sshfp.algorithm &&
410 a->sshfp.fptype == b->sshfp.fptype &&
411 a->sshfp.key_size == b->sshfp.key_size &&
412 memcmp(a->sshfp.key, b->sshfp.key, a->sshfp.key_size) == 0;
413
8db0d2f5
ZJS
414 case DNS_TYPE_DNSKEY:
415 return a->dnskey.zone_key_flag == b->dnskey.zone_key_flag &&
416 a->dnskey.sep_flag == b->dnskey.sep_flag &&
417 a->dnskey.algorithm == b->dnskey.algorithm &&
418 a->dnskey.key_size == b->dnskey.key_size &&
419 memcmp(a->dnskey.key, b->dnskey.key, a->dnskey.key_size) == 0;
420
2d4c5cbc 421 default:
322345fd
LP
422 return a->generic.size == b->generic.size &&
423 memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
2d4c5cbc 424 }
322345fd
LP
425}
426
0dae31d4
ZJS
427static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t altitude,
428 uint8_t size, uint8_t horiz_pre, uint8_t vert_pre) {
429 char *s;
430 char NS = latitude >= 1U<<31 ? 'N' : 'S';
431 char EW = longitude >= 1U<<31 ? 'E' : 'W';
432
433 int lat = latitude >= 1U<<31 ? (int) (latitude - (1U<<31)) : (int) ((1U<<31) - latitude);
434 int lon = longitude >= 1U<<31 ? (int) (longitude - (1U<<31)) : (int) ((1U<<31) - longitude);
435 double alt = altitude >= 10000000u ? altitude - 10000000u : -(double)(10000000u - altitude);
436 double siz = (size >> 4) * exp10((double) (size & 0xF));
437 double hor = (horiz_pre >> 4) * exp10((double) (horiz_pre & 0xF));
438 double ver = (vert_pre >> 4) * exp10((double) (vert_pre & 0xF));
439
440 if (asprintf(&s, "%d %d %.3f %c %d %d %.3f %c %.2fm %.2fm %.2fm %.2fm",
441 (lat / 60000 / 60),
442 (lat / 60000) % 60,
443 (lat % 60000) / 1000.,
444 NS,
445 (lon / 60000 / 60),
446 (lon / 60000) % 60,
447 (lon % 60000) / 1000.,
448 EW,
449 alt / 100.,
450 siz / 100.,
451 hor / 100.,
452 ver / 100.) < 0)
453 return NULL;
454
455 return s;
456}
457
2d4c5cbc 458int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
8db0d2f5 459 _cleanup_free_ char *k = NULL, *t = NULL;
2d4c5cbc
LP
460 char *s;
461 int r;
322345fd 462
2d4c5cbc 463 assert(rr);
322345fd 464
2d4c5cbc
LP
465 r = dns_resource_key_to_string(rr->key, &k);
466 if (r < 0)
467 return r;
322345fd 468
0dae31d4 469 switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
322345fd 470
9c92ce6d
LP
471 case DNS_TYPE_SRV:
472 r = asprintf(&s, "%s %u %u %u %s",
473 k,
474 rr->srv.priority,
475 rr->srv.weight,
476 rr->srv.port,
477 strna(rr->srv.name));
478 if (r < 0)
479 return -ENOMEM;
480 break;
481
2d4c5cbc
LP
482 case DNS_TYPE_PTR:
483 case DNS_TYPE_NS:
484 case DNS_TYPE_CNAME:
8ac4e9e1 485 case DNS_TYPE_DNAME:
2d4c5cbc
LP
486 s = strjoin(k, " ", rr->ptr.name, NULL);
487 if (!s)
488 return -ENOMEM;
322345fd 489
2d4c5cbc 490 break;
322345fd 491
2d4c5cbc
LP
492 case DNS_TYPE_HINFO:
493 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
494 if (!s)
495 return -ENOMEM;
496 break;
322345fd 497
9de3e329 498 case DNS_TYPE_SPF: /* exactly the same as TXT */
8db0d2f5 499 case DNS_TYPE_TXT:
2e276efc
ZJS
500 t = strv_join_quoted(rr->txt.strings);
501 if (!t)
502 return -ENOMEM;
503
504 s = strjoin(k, " ", t, NULL);
505 if (!s)
506 return -ENOMEM;
507
508 break;
2e276efc 509
2d4c5cbc
LP
510 case DNS_TYPE_A: {
511 _cleanup_free_ char *x = NULL;
322345fd 512
2d4c5cbc
LP
513 r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
514 if (r < 0)
515 return r;
322345fd 516
2d4c5cbc
LP
517 s = strjoin(k, " ", x, NULL);
518 if (!s)
519 return -ENOMEM;
520 break;
521 }
322345fd 522
8db0d2f5
ZJS
523 case DNS_TYPE_AAAA:
524 r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &t);
2d4c5cbc
LP
525 if (r < 0)
526 return r;
322345fd 527
8db0d2f5 528 s = strjoin(k, " ", t, NULL);
2d4c5cbc
LP
529 if (!s)
530 return -ENOMEM;
531 break;
322345fd 532
2d4c5cbc
LP
533 case DNS_TYPE_SOA:
534 r = asprintf(&s, "%s %s %s %u %u %u %u %u",
535 k,
536 strna(rr->soa.mname),
537 strna(rr->soa.rname),
538 rr->soa.serial,
539 rr->soa.refresh,
540 rr->soa.retry,
541 rr->soa.expire,
542 rr->soa.minimum);
543 if (r < 0)
544 return -ENOMEM;
545 break;
546
946c7094
ZJS
547 case DNS_TYPE_MX:
548 r = asprintf(&s, "%s %u %s",
549 k,
550 rr->mx.priority,
551 rr->mx.exchange);
552 if (r < 0)
553 return -ENOMEM;
554 break;
555
8db0d2f5 556 case DNS_TYPE_LOC:
0dae31d4
ZJS
557 assert(rr->loc.version == 0);
558
8db0d2f5
ZJS
559 t = format_location(rr->loc.latitude,
560 rr->loc.longitude,
561 rr->loc.altitude,
562 rr->loc.size,
563 rr->loc.horiz_pre,
564 rr->loc.vert_pre);
565 if (!t)
0dae31d4
ZJS
566 return -ENOMEM;
567
8db0d2f5 568 s = strjoin(k, " ", t, NULL);
0dae31d4
ZJS
569 if (!s)
570 return -ENOMEM;
0dae31d4 571 break;
0dae31d4 572
8db0d2f5
ZJS
573 case DNS_TYPE_SSHFP:
574 t = hexmem(rr->sshfp.key, rr->sshfp.key_size);
575 if (!t)
42cc2eeb
LP
576 return -ENOMEM;
577
578 r = asprintf(&s, "%s %u %u %s",
579 k,
580 rr->sshfp.algorithm,
581 rr->sshfp.fptype,
8db0d2f5 582 t);
42cc2eeb
LP
583 if (r < 0)
584 return -ENOMEM;
585 break;
42cc2eeb 586
ff3d6560
ZJS
587 case DNS_TYPE_DNSKEY: {
588 const char *alg;
589
590 alg = dnssec_algorithm_to_string(rr->dnskey.algorithm);
591
8db0d2f5
ZJS
592 t = hexmem(rr->dnskey.key, rr->dnskey.key_size);
593 if (!t)
594 return -ENOMEM;
2d4c5cbc 595
ff3d6560 596 r = asprintf(&s, "%s %u 3 %.*s%.*u %s",
8db0d2f5
ZJS
597 k,
598 dnskey_to_flags(rr),
ff3d6560
ZJS
599 alg ? -1 : 0, alg,
600 alg ? 0 : 1, alg ? 0u : (unsigned) rr->dnskey.algorithm,
8db0d2f5
ZJS
601 t);
602 if (r < 0)
2d4c5cbc 603 return -ENOMEM;
8db0d2f5 604 break;
ff3d6560 605 }
2d4c5cbc 606
8db0d2f5
ZJS
607 default:
608 t = hexmem(rr->generic.data, rr->generic.size);
609 if (!t)
610 return -ENOMEM;
611
612 s = strjoin(k, " ", t, NULL);
2d4c5cbc
LP
613 if (!s)
614 return -ENOMEM;
615 break;
8db0d2f5 616 }
2d4c5cbc
LP
617
618 *ret = s;
619 return 0;
620}
322345fd 621
2d4c5cbc 622const char *dns_class_to_string(uint16_t class) {
322345fd 623
2d4c5cbc 624 switch (class) {
322345fd 625
2d4c5cbc
LP
626 case DNS_CLASS_IN:
627 return "IN";
322345fd 628
2d4c5cbc
LP
629 case DNS_CLASS_ANY:
630 return "ANY";
631 }
322345fd 632
2d4c5cbc
LP
633 return NULL;
634}
322345fd 635
2d4c5cbc
LP
636int dns_class_from_string(const char *s, uint16_t *class) {
637 assert(s);
638 assert(class);
639
640 if (strcaseeq(s, "IN"))
641 *class = DNS_CLASS_IN;
642 else if (strcaseeq(s, "ANY"))
643 *class = DNS_TYPE_ANY;
644 else
645 return -EINVAL;
322345fd 646
2d4c5cbc
LP
647 return 0;
648}