]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/resolve/resolved-dns-rr.c
resolved: rr - print formated timestamps in RRSIG
[thirdparty/systemd.git] / src / resolve / resolved-dns-rr.c
... / ...
CommitLineData
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
22#include <math.h>
23
24#include "strv.h"
25
26#include "dns-domain.h"
27#include "resolved-dns-rr.h"
28#include "resolved-dns-packet.h"
29#include "dns-type.h"
30
31DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) {
32 DnsResourceKey *k;
33 size_t l;
34
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));
134}
135
136static unsigned 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
140 ul = dns_name_hash_func(DNS_RESOURCE_KEY_NAME(k), hash_key);
141 ul = ul * hash_key[0] + ul + k->class;
142 ul = ul * hash_key[1] + ul + k->type;
143
144 return ul;
145}
146
147static int dns_resource_key_compare_func(const void *a, const void *b) {
148 const DnsResourceKey *x = a, *y = b;
149 int ret;
150
151 ret = dns_name_compare_func(DNS_RESOURCE_KEY_NAME(x), DNS_RESOURCE_KEY_NAME(y));
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
168const struct hash_ops dns_resource_key_hash_ops = {
169 .hash = dns_resource_key_hash_func,
170 .compare = dns_resource_key_compare_func
171};
172
173int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) {
174 char cbuf[DECIMAL_STR_MAX(uint16_t)], tbuf[DECIMAL_STR_MAX(uint16_t)];
175 const char *c, *t;
176 char *s;
177
178 c = dns_class_to_string(key->class);
179 if (!c) {
180 sprintf(cbuf, "%i", key->class);
181 c = cbuf;
182 }
183
184 t = dns_type_to_string(key->type);
185 if (!t){
186 sprintf(tbuf, "%i", key->type);
187 t = tbuf;
188 }
189
190 if (asprintf(&s, "%s %s %-5s", DNS_RESOURCE_KEY_NAME(key), c, t) < 0)
191 return -ENOMEM;
192
193 *ret = s;
194 return 0;
195}
196
197DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
198 DnsResourceRecord *rr;
199
200 rr = new0(DnsResourceRecord, 1);
201 if (!rr)
202 return NULL;
203
204 rr->n_ref = 1;
205 rr->key = dns_resource_key_ref(key);
206
207 return rr;
208}
209
210DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name) {
211 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
212
213 key = dns_resource_key_new(class, type, name);
214 if (!key)
215 return NULL;
216
217 return dns_resource_record_new(key);
218}
219
220DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr) {
221 if (!rr)
222 return NULL;
223
224 assert(rr->n_ref > 0);
225 rr->n_ref++;
226
227 return rr;
228}
229
230DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
231 if (!rr)
232 return NULL;
233
234 assert(rr->n_ref > 0);
235
236 if (rr->n_ref > 1) {
237 rr->n_ref--;
238 return NULL;
239 }
240
241 if (rr->key) {
242 switch(rr->key->type) {
243
244 case DNS_TYPE_SRV:
245 free(rr->srv.name);
246 break;
247
248 case DNS_TYPE_PTR:
249 case DNS_TYPE_NS:
250 case DNS_TYPE_CNAME:
251 case DNS_TYPE_DNAME:
252 free(rr->ptr.name);
253 break;
254
255 case DNS_TYPE_HINFO:
256 free(rr->hinfo.cpu);
257 free(rr->hinfo.os);
258 break;
259
260 case DNS_TYPE_TXT:
261 case DNS_TYPE_SPF:
262 strv_free(rr->txt.strings);
263 break;
264
265 case DNS_TYPE_SOA:
266 free(rr->soa.mname);
267 free(rr->soa.rname);
268 break;
269
270 case DNS_TYPE_MX:
271 free(rr->mx.exchange);
272 break;
273
274 case DNS_TYPE_SSHFP:
275 free(rr->sshfp.key);
276 break;
277
278 case DNS_TYPE_DNSKEY:
279 free(rr->dnskey.key);
280 break;
281
282 case DNS_TYPE_RRSIG:
283 free(rr->rrsig.signer);
284 free(rr->rrsig.signature);
285 break;
286
287 case DNS_TYPE_LOC:
288 case DNS_TYPE_A:
289 case DNS_TYPE_AAAA:
290 break;
291
292 default:
293 free(rr->generic.data);
294 }
295
296 dns_resource_key_unref(rr->key);
297 }
298
299 free(rr);
300
301 return NULL;
302}
303
304int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) {
305 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
306 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
307 _cleanup_free_ char *ptr = NULL;
308 int r;
309
310 assert(ret);
311 assert(address);
312 assert(hostname);
313
314 r = dns_name_reverse(family, address, &ptr);
315 if (r < 0)
316 return r;
317
318 key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, ptr);
319 if (!key)
320 return -ENOMEM;
321
322 ptr = NULL;
323
324 rr = dns_resource_record_new(key);
325 if (!rr)
326 return -ENOMEM;
327
328 rr->ptr.name = strdup(hostname);
329 if (!rr->ptr.name)
330 return -ENOMEM;
331
332 *ret = rr;
333 rr = NULL;
334
335 return 0;
336}
337
338int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
339 int r;
340
341 assert(a);
342 assert(b);
343
344 r = dns_resource_key_equal(a->key, b->key);
345 if (r <= 0)
346 return r;
347
348 if (a->unparseable != b->unparseable)
349 return 0;
350
351 switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) {
352
353 case DNS_TYPE_SRV:
354 r = dns_name_equal(a->srv.name, b->srv.name);
355 if (r <= 0)
356 return r;
357
358 return a->srv.priority == b->srv.priority &&
359 a->srv.weight == b->srv.weight &&
360 a->srv.port == b->srv.port;
361
362 case DNS_TYPE_PTR:
363 case DNS_TYPE_NS:
364 case DNS_TYPE_CNAME:
365 case DNS_TYPE_DNAME:
366 return dns_name_equal(a->ptr.name, b->ptr.name);
367
368 case DNS_TYPE_HINFO:
369 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
370 strcaseeq(a->hinfo.os, b->hinfo.os);
371
372 case DNS_TYPE_SPF: /* exactly the same as TXT */
373 case DNS_TYPE_TXT:
374 return strv_equal(a->txt.strings, b->txt.strings);
375
376 case DNS_TYPE_A:
377 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
378
379 case DNS_TYPE_AAAA:
380 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
381
382 case DNS_TYPE_SOA:
383 r = dns_name_equal(a->soa.mname, b->soa.mname);
384 if (r <= 0)
385 return r;
386 r = dns_name_equal(a->soa.rname, b->soa.rname);
387 if (r <= 0)
388 return r;
389
390 return a->soa.serial == b->soa.serial &&
391 a->soa.refresh == b->soa.refresh &&
392 a->soa.retry == b->soa.retry &&
393 a->soa.expire == b->soa.expire &&
394 a->soa.minimum == b->soa.minimum;
395
396 case DNS_TYPE_MX:
397 if (a->mx.priority != b->mx.priority)
398 return 0;
399
400 return dns_name_equal(a->mx.exchange, b->mx.exchange);
401
402 case DNS_TYPE_LOC:
403 assert(a->loc.version == b->loc.version);
404
405 return a->loc.size == b->loc.size &&
406 a->loc.horiz_pre == b->loc.horiz_pre &&
407 a->loc.vert_pre == b->loc.vert_pre &&
408 a->loc.latitude == b->loc.latitude &&
409 a->loc.longitude == b->loc.longitude &&
410 a->loc.altitude == b->loc.altitude;
411
412 case DNS_TYPE_SSHFP:
413 return a->sshfp.algorithm == b->sshfp.algorithm &&
414 a->sshfp.fptype == b->sshfp.fptype &&
415 a->sshfp.key_size == b->sshfp.key_size &&
416 memcmp(a->sshfp.key, b->sshfp.key, a->sshfp.key_size) == 0;
417
418 case DNS_TYPE_DNSKEY:
419 return a->dnskey.zone_key_flag == b->dnskey.zone_key_flag &&
420 a->dnskey.sep_flag == b->dnskey.sep_flag &&
421 a->dnskey.algorithm == b->dnskey.algorithm &&
422 a->dnskey.key_size == b->dnskey.key_size &&
423 memcmp(a->dnskey.key, b->dnskey.key, a->dnskey.key_size) == 0;
424
425 case DNS_TYPE_RRSIG:
426 /* do the fast comparisons first */
427 if (a->rrsig.type_covered != b->rrsig.type_covered ||
428 a->rrsig.algorithm != b->rrsig.algorithm ||
429 a->rrsig.labels != b->rrsig.labels ||
430 a->rrsig.original_ttl != b->rrsig.original_ttl ||
431 a->rrsig.expiration != b->rrsig.expiration ||
432 a->rrsig.inception != b->rrsig.inception ||
433 a->rrsig.key_tag != b->rrsig.key_tag ||
434 a->rrsig.signature_size != b->rrsig.signature_size ||
435 memcmp(a->rrsig.signature, b->rrsig.signature, a->rrsig.signature_size) != 0)
436 return false;
437
438 return dns_name_equal(a->rrsig.signer, b->rrsig.signer);
439
440 default:
441 return a->generic.size == b->generic.size &&
442 memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
443 }
444}
445
446static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t altitude,
447 uint8_t size, uint8_t horiz_pre, uint8_t vert_pre) {
448 char *s;
449 char NS = latitude >= 1U<<31 ? 'N' : 'S';
450 char EW = longitude >= 1U<<31 ? 'E' : 'W';
451
452 int lat = latitude >= 1U<<31 ? (int) (latitude - (1U<<31)) : (int) ((1U<<31) - latitude);
453 int lon = longitude >= 1U<<31 ? (int) (longitude - (1U<<31)) : (int) ((1U<<31) - longitude);
454 double alt = altitude >= 10000000u ? altitude - 10000000u : -(double)(10000000u - altitude);
455 double siz = (size >> 4) * exp10((double) (size & 0xF));
456 double hor = (horiz_pre >> 4) * exp10((double) (horiz_pre & 0xF));
457 double ver = (vert_pre >> 4) * exp10((double) (vert_pre & 0xF));
458
459 if (asprintf(&s, "%d %d %.3f %c %d %d %.3f %c %.2fm %.2fm %.2fm %.2fm",
460 (lat / 60000 / 60),
461 (lat / 60000) % 60,
462 (lat % 60000) / 1000.,
463 NS,
464 (lon / 60000 / 60),
465 (lon / 60000) % 60,
466 (lon % 60000) / 1000.,
467 EW,
468 alt / 100.,
469 siz / 100.,
470 hor / 100.,
471 ver / 100.) < 0)
472 return NULL;
473
474 return s;
475}
476
477static int format_timestamp_dns(char *buf, size_t l, time_t sec) {
478 struct tm tm;
479
480 assert(buf);
481 assert(l > strlen("YYYYMMDDHHmmSS"));
482
483 if (!gmtime_r(&sec, &tm))
484 return -EINVAL;
485
486 if (strftime(buf, l, "%Y%m%d%H%M%S", &tm) <= 0)
487 return -EINVAL;
488
489 return 0;
490}
491
492int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
493 _cleanup_free_ char *k = NULL, *t = NULL;
494 char *s;
495 int r;
496
497 assert(rr);
498
499 r = dns_resource_key_to_string(rr->key, &k);
500 if (r < 0)
501 return r;
502
503 switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
504
505 case DNS_TYPE_SRV:
506 r = asprintf(&s, "%s %u %u %u %s",
507 k,
508 rr->srv.priority,
509 rr->srv.weight,
510 rr->srv.port,
511 strna(rr->srv.name));
512 if (r < 0)
513 return -ENOMEM;
514 break;
515
516 case DNS_TYPE_PTR:
517 case DNS_TYPE_NS:
518 case DNS_TYPE_CNAME:
519 case DNS_TYPE_DNAME:
520 s = strjoin(k, " ", rr->ptr.name, NULL);
521 if (!s)
522 return -ENOMEM;
523
524 break;
525
526 case DNS_TYPE_HINFO:
527 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
528 if (!s)
529 return -ENOMEM;
530 break;
531
532 case DNS_TYPE_SPF: /* exactly the same as TXT */
533 case DNS_TYPE_TXT:
534 t = strv_join_quoted(rr->txt.strings);
535 if (!t)
536 return -ENOMEM;
537
538 s = strjoin(k, " ", t, NULL);
539 if (!s)
540 return -ENOMEM;
541
542 break;
543
544 case DNS_TYPE_A: {
545 _cleanup_free_ char *x = NULL;
546
547 r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
548 if (r < 0)
549 return r;
550
551 s = strjoin(k, " ", x, NULL);
552 if (!s)
553 return -ENOMEM;
554 break;
555 }
556
557 case DNS_TYPE_AAAA:
558 r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &t);
559 if (r < 0)
560 return r;
561
562 s = strjoin(k, " ", t, NULL);
563 if (!s)
564 return -ENOMEM;
565 break;
566
567 case DNS_TYPE_SOA:
568 r = asprintf(&s, "%s %s %s %u %u %u %u %u",
569 k,
570 strna(rr->soa.mname),
571 strna(rr->soa.rname),
572 rr->soa.serial,
573 rr->soa.refresh,
574 rr->soa.retry,
575 rr->soa.expire,
576 rr->soa.minimum);
577 if (r < 0)
578 return -ENOMEM;
579 break;
580
581 case DNS_TYPE_MX:
582 r = asprintf(&s, "%s %u %s",
583 k,
584 rr->mx.priority,
585 rr->mx.exchange);
586 if (r < 0)
587 return -ENOMEM;
588 break;
589
590 case DNS_TYPE_LOC:
591 assert(rr->loc.version == 0);
592
593 t = format_location(rr->loc.latitude,
594 rr->loc.longitude,
595 rr->loc.altitude,
596 rr->loc.size,
597 rr->loc.horiz_pre,
598 rr->loc.vert_pre);
599 if (!t)
600 return -ENOMEM;
601
602 s = strjoin(k, " ", t, NULL);
603 if (!s)
604 return -ENOMEM;
605 break;
606
607 case DNS_TYPE_SSHFP:
608 t = hexmem(rr->sshfp.key, rr->sshfp.key_size);
609 if (!t)
610 return -ENOMEM;
611
612 r = asprintf(&s, "%s %u %u %s",
613 k,
614 rr->sshfp.algorithm,
615 rr->sshfp.fptype,
616 t);
617 if (r < 0)
618 return -ENOMEM;
619 break;
620
621 case DNS_TYPE_DNSKEY: {
622 const char *alg;
623
624 alg = dnssec_algorithm_to_string(rr->dnskey.algorithm);
625
626 t = base64mem(rr->dnskey.key, rr->dnskey.key_size);
627 if (!t)
628 return -ENOMEM;
629
630 r = asprintf(&s, "%s %u 3 %.*s%.*u %s",
631 k,
632 dnskey_to_flags(rr),
633 alg ? -1 : 0, alg,
634 alg ? 0 : 1, alg ? 0u : (unsigned) rr->dnskey.algorithm,
635 t);
636 if (r < 0)
637 return -ENOMEM;
638 break;
639 }
640
641 case DNS_TYPE_RRSIG: {
642 const char *type, *alg;
643 char expiration[strlen("YYYYMMDDHHmmSS") + 1], inception[strlen("YYYYMMDDHHmmSS") + 1];
644
645 type = dns_type_to_string(rr->rrsig.type_covered);
646 alg = dnssec_algorithm_to_string(rr->rrsig.algorithm);
647
648 t = base64mem(rr->rrsig.signature, rr->rrsig.signature_size);
649 if (!t)
650 return -ENOMEM;
651
652 r = format_timestamp_dns(expiration, sizeof(expiration), rr->rrsig.expiration);
653 if (r < 0)
654 return r;
655
656 r = format_timestamp_dns(inception, sizeof(inception), rr->rrsig.inception);
657 if (r < 0)
658 return r;
659
660 /* TYPE?? follows
661 * http://tools.ietf.org/html/rfc3597#section-5 */
662
663 r = asprintf(&s, "%s %s%.*u %.*s%.*u %u %u %s %s %u %s %s",
664 k,
665 type ?: "TYPE",
666 type ? 0 : 1, type ? 0u : (unsigned) rr->rrsig.type_covered,
667 alg ? -1 : 0, alg,
668 alg ? 0 : 1, alg ? 0u : (unsigned) rr->rrsig.algorithm,
669 rr->rrsig.labels,
670 rr->rrsig.original_ttl,
671 expiration,
672 inception,
673 rr->rrsig.key_tag,
674 rr->rrsig.signer,
675 t);
676 if (r < 0)
677 return -ENOMEM;
678 break;
679 }
680
681 default:
682 t = hexmem(rr->generic.data, rr->generic.size);
683 if (!t)
684 return -ENOMEM;
685
686 s = strjoin(k, " ", t, NULL);
687 if (!s)
688 return -ENOMEM;
689 break;
690 }
691
692 *ret = s;
693 return 0;
694}
695
696const char *dns_class_to_string(uint16_t class) {
697
698 switch (class) {
699
700 case DNS_CLASS_IN:
701 return "IN";
702
703 case DNS_CLASS_ANY:
704 return "ANY";
705 }
706
707 return NULL;
708}
709
710int dns_class_from_string(const char *s, uint16_t *class) {
711 assert(s);
712 assert(class);
713
714 if (strcaseeq(s, "IN"))
715 *class = DNS_CLASS_IN;
716 else if (strcaseeq(s, "ANY"))
717 *class = DNS_TYPE_ANY;
718 else
719 return -EINVAL;
720
721 return 0;
722}