]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-rr.c
resolved: DNSKEY records
[thirdparty/systemd.git] / src / resolve / resolved-dns-rr.c
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 "resolved-dns-domain.h"
27 #include "resolved-dns-rr.h"
28 #include "resolved-dns-packet.h"
29 #include "dns-type.h"
30
31 DnsResourceKey* 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
51 DnsResourceKey* 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
68 DnsResourceKey* 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
79 DnsResourceKey* 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
94 int 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
110 int 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
123 int 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
136 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
147 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
168 int 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
185 if (asprintf(&s, "%s %s %-5s", DNS_RESOURCE_KEY_NAME(key), c, t) < 0)
186 return -ENOMEM;
187
188 *ret = s;
189 return 0;
190 }
191
192 DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
193 DnsResourceRecord *rr;
194
195 rr = new0(DnsResourceRecord, 1);
196 if (!rr)
197 return NULL;
198
199 rr->n_ref = 1;
200 rr->key = dns_resource_key_ref(key);
201
202 return rr;
203 }
204
205 DnsResourceRecord* 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
215 DnsResourceRecord* 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
225 DnsResourceRecord* 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
236 if (rr->key) {
237 switch(rr->key->type) {
238
239 case DNS_TYPE_SRV:
240 free(rr->srv.name);
241 break;
242
243 case DNS_TYPE_PTR:
244 case DNS_TYPE_NS:
245 case DNS_TYPE_CNAME:
246 case DNS_TYPE_DNAME:
247 free(rr->ptr.name);
248 break;
249
250 case DNS_TYPE_HINFO:
251 free(rr->hinfo.cpu);
252 free(rr->hinfo.os);
253 break;
254
255 case DNS_TYPE_TXT:
256 case DNS_TYPE_SPF:
257 strv_free(rr->txt.strings);
258 break;
259
260 case DNS_TYPE_SOA:
261 free(rr->soa.mname);
262 free(rr->soa.rname);
263 break;
264
265 case DNS_TYPE_MX:
266 free(rr->mx.exchange);
267 break;
268
269 case DNS_TYPE_SSHFP:
270 free(rr->sshfp.key);
271 break;
272
273 case DNS_TYPE_DNSKEY:
274 free(rr->dnskey.key);
275 break;
276
277 case DNS_TYPE_LOC:
278 case DNS_TYPE_A:
279 case DNS_TYPE_AAAA:
280 break;
281
282 default:
283 free(rr->generic.data);
284 }
285
286 dns_resource_key_unref(rr->key);
287 }
288
289 free(rr);
290
291 return NULL;
292 }
293
294 int 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
328 int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
329 int r;
330
331 assert(a);
332 assert(b);
333
334 r = dns_resource_key_equal(a->key, b->key);
335 if (r <= 0)
336 return r;
337
338 if (a->unparseable != b->unparseable)
339 return 0;
340
341 switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) {
342
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
352 case DNS_TYPE_PTR:
353 case DNS_TYPE_NS:
354 case DNS_TYPE_CNAME:
355 case DNS_TYPE_DNAME:
356 return dns_name_equal(a->ptr.name, b->ptr.name);
357
358 case DNS_TYPE_HINFO:
359 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
360 strcaseeq(a->hinfo.os, b->hinfo.os);
361
362 case DNS_TYPE_SPF: /* exactly the same as TXT */
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
372 case DNS_TYPE_A:
373 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
374
375 case DNS_TYPE_AAAA:
376 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
377
378 case DNS_TYPE_SOA:
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;
391
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
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
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
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
421 default:
422 return a->generic.size == b->generic.size &&
423 memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
424 }
425 }
426
427 static 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
458 int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
459 _cleanup_free_ char *k = NULL, *t = NULL;
460 char *s;
461 int r;
462
463 assert(rr);
464
465 r = dns_resource_key_to_string(rr->key, &k);
466 if (r < 0)
467 return r;
468
469 switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
470
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
482 case DNS_TYPE_PTR:
483 case DNS_TYPE_NS:
484 case DNS_TYPE_CNAME:
485 case DNS_TYPE_DNAME:
486 s = strjoin(k, " ", rr->ptr.name, NULL);
487 if (!s)
488 return -ENOMEM;
489
490 break;
491
492 case DNS_TYPE_HINFO:
493 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
494 if (!s)
495 return -ENOMEM;
496 break;
497
498 case DNS_TYPE_SPF: /* exactly the same as TXT */
499 case DNS_TYPE_TXT:
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;
509
510 case DNS_TYPE_A: {
511 _cleanup_free_ char *x = NULL;
512
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;
516
517 s = strjoin(k, " ", x, NULL);
518 if (!s)
519 return -ENOMEM;
520 break;
521 }
522
523 case DNS_TYPE_AAAA:
524 r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &t);
525 if (r < 0)
526 return r;
527
528 s = strjoin(k, " ", t, NULL);
529 if (!s)
530 return -ENOMEM;
531 break;
532
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
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
556 case DNS_TYPE_LOC:
557 assert(rr->loc.version == 0);
558
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)
566 return -ENOMEM;
567
568 s = strjoin(k, " ", t, NULL);
569 if (!s)
570 return -ENOMEM;
571 break;
572
573 case DNS_TYPE_SSHFP:
574 t = hexmem(rr->sshfp.key, rr->sshfp.key_size);
575 if (!t)
576 return -ENOMEM;
577
578 r = asprintf(&s, "%s %u %u %s",
579 k,
580 rr->sshfp.algorithm,
581 rr->sshfp.fptype,
582 t);
583 if (r < 0)
584 return -ENOMEM;
585 break;
586
587 case DNS_TYPE_DNSKEY:
588 t = hexmem(rr->dnskey.key, rr->dnskey.key_size);
589 if (!t)
590 return -ENOMEM;
591
592 r = asprintf(&s, "%s %u 3 %u %s",
593 k,
594 dnskey_to_flags(rr),
595 rr->dnskey.algorithm,
596 t);
597 if (r < 0)
598 return -ENOMEM;
599 break;
600
601 default:
602 t = hexmem(rr->generic.data, rr->generic.size);
603 if (!t)
604 return -ENOMEM;
605
606 s = strjoin(k, " ", t, NULL);
607 if (!s)
608 return -ENOMEM;
609 break;
610 }
611
612 *ret = s;
613 return 0;
614 }
615
616 const char *dns_class_to_string(uint16_t class) {
617
618 switch (class) {
619
620 case DNS_CLASS_IN:
621 return "IN";
622
623 case DNS_CLASS_ANY:
624 return "ANY";
625 }
626
627 return NULL;
628 }
629
630 int dns_class_from_string(const char *s, uint16_t *class) {
631 assert(s);
632 assert(class);
633
634 if (strcaseeq(s, "IN"))
635 *class = DNS_CLASS_IN;
636 else if (strcaseeq(s, "ANY"))
637 *class = DNS_TYPE_ANY;
638 else
639 return -EINVAL;
640
641 return 0;
642 }