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