]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-rr.c
resolved: properly compare RRs we cannot parse
[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 if (a->unparseable != b->unparseable)
334 return 0;
335
336 switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) {
337
338 case DNS_TYPE_SRV:
339 r = dns_name_equal(a->srv.name, b->srv.name);
340 if (r <= 0)
341 return r;
342
343 return a->srv.priority == b->srv.priority &&
344 a->srv.weight == b->srv.weight &&
345 a->srv.port == b->srv.port;
346
347 case DNS_TYPE_PTR:
348 case DNS_TYPE_NS:
349 case DNS_TYPE_CNAME:
350 case DNS_TYPE_DNAME:
351 return dns_name_equal(a->ptr.name, b->ptr.name);
352
353 case DNS_TYPE_HINFO:
354 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
355 strcaseeq(a->hinfo.os, b->hinfo.os);
356
357 case DNS_TYPE_SPF: /* exactly the same as TXT */
358 case DNS_TYPE_TXT: {
359 int i;
360
361 for (i = 0; a->txt.strings[i] || b->txt.strings[i]; i++)
362 if (!streq_ptr(a->txt.strings[i], b->txt.strings[i]))
363 return false;
364 return true;
365 }
366
367 case DNS_TYPE_A:
368 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
369
370 case DNS_TYPE_AAAA:
371 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
372
373 case DNS_TYPE_SOA:
374 r = dns_name_equal(a->soa.mname, b->soa.mname);
375 if (r <= 0)
376 return r;
377 r = dns_name_equal(a->soa.rname, b->soa.rname);
378 if (r <= 0)
379 return r;
380
381 return a->soa.serial == b->soa.serial &&
382 a->soa.refresh == b->soa.refresh &&
383 a->soa.retry == b->soa.retry &&
384 a->soa.expire == b->soa.expire &&
385 a->soa.minimum == b->soa.minimum;
386
387 case DNS_TYPE_MX:
388 if (a->mx.priority != b->mx.priority)
389 return 0;
390
391 return dns_name_equal(a->mx.exchange, b->mx.exchange);
392
393 case DNS_TYPE_LOC:
394 assert(a->loc.version == b->loc.version);
395
396 return a->loc.size == b->loc.size &&
397 a->loc.horiz_pre == b->loc.horiz_pre &&
398 a->loc.vert_pre == b->loc.vert_pre &&
399 a->loc.latitude == b->loc.latitude &&
400 a->loc.longitude == b->loc.longitude &&
401 a->loc.altitude == b->loc.altitude;
402
403 case DNS_TYPE_SSHFP:
404 return a->sshfp.algorithm == b->sshfp.algorithm &&
405 a->sshfp.fptype == b->sshfp.fptype &&
406 a->sshfp.key_size == b->sshfp.key_size &&
407 memcmp(a->sshfp.key, b->sshfp.key, a->sshfp.key_size) == 0;
408
409 default:
410 return a->generic.size == b->generic.size &&
411 memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
412 }
413 }
414
415 static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t altitude,
416 uint8_t size, uint8_t horiz_pre, uint8_t vert_pre) {
417 char *s;
418 char NS = latitude >= 1U<<31 ? 'N' : 'S';
419 char EW = longitude >= 1U<<31 ? 'E' : 'W';
420
421 int lat = latitude >= 1U<<31 ? (int) (latitude - (1U<<31)) : (int) ((1U<<31) - latitude);
422 int lon = longitude >= 1U<<31 ? (int) (longitude - (1U<<31)) : (int) ((1U<<31) - longitude);
423 double alt = altitude >= 10000000u ? altitude - 10000000u : -(double)(10000000u - altitude);
424 double siz = (size >> 4) * exp10((double) (size & 0xF));
425 double hor = (horiz_pre >> 4) * exp10((double) (horiz_pre & 0xF));
426 double ver = (vert_pre >> 4) * exp10((double) (vert_pre & 0xF));
427
428 if (asprintf(&s, "%d %d %.3f %c %d %d %.3f %c %.2fm %.2fm %.2fm %.2fm",
429 (lat / 60000 / 60),
430 (lat / 60000) % 60,
431 (lat % 60000) / 1000.,
432 NS,
433 (lon / 60000 / 60),
434 (lon / 60000) % 60,
435 (lon % 60000) / 1000.,
436 EW,
437 alt / 100.,
438 siz / 100.,
439 hor / 100.,
440 ver / 100.) < 0)
441 return NULL;
442
443 return s;
444 }
445
446 int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
447 _cleanup_free_ char *k = NULL;
448 char *s;
449 int r;
450
451 assert(rr);
452
453 r = dns_resource_key_to_string(rr->key, &k);
454 if (r < 0)
455 return r;
456
457 switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
458
459 case DNS_TYPE_SRV:
460 r = asprintf(&s, "%s %u %u %u %s",
461 k,
462 rr->srv.priority,
463 rr->srv.weight,
464 rr->srv.port,
465 strna(rr->srv.name));
466 if (r < 0)
467 return -ENOMEM;
468 break;
469
470 case DNS_TYPE_PTR:
471 case DNS_TYPE_NS:
472 case DNS_TYPE_CNAME:
473 case DNS_TYPE_DNAME:
474 s = strjoin(k, " ", rr->ptr.name, NULL);
475 if (!s)
476 return -ENOMEM;
477
478 break;
479
480 case DNS_TYPE_HINFO:
481 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
482 if (!s)
483 return -ENOMEM;
484 break;
485
486 case DNS_TYPE_SPF: /* exactly the same as TXT */
487 case DNS_TYPE_TXT: {
488 _cleanup_free_ char *t;
489
490 t = strv_join_quoted(rr->txt.strings);
491 if (!t)
492 return -ENOMEM;
493
494 s = strjoin(k, " ", t, NULL);
495 if (!s)
496 return -ENOMEM;
497
498 break;
499 }
500
501 case DNS_TYPE_A: {
502 _cleanup_free_ char *x = NULL;
503
504 r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
505 if (r < 0)
506 return r;
507
508 s = strjoin(k, " ", x, NULL);
509 if (!s)
510 return -ENOMEM;
511 break;
512 }
513
514 case DNS_TYPE_AAAA: {
515 _cleanup_free_ char *x = NULL;
516
517 r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &x);
518 if (r < 0)
519 return r;
520
521 s = strjoin(k, " ", x, NULL);
522 if (!s)
523 return -ENOMEM;
524 break;
525 }
526
527 case DNS_TYPE_SOA:
528 r = asprintf(&s, "%s %s %s %u %u %u %u %u",
529 k,
530 strna(rr->soa.mname),
531 strna(rr->soa.rname),
532 rr->soa.serial,
533 rr->soa.refresh,
534 rr->soa.retry,
535 rr->soa.expire,
536 rr->soa.minimum);
537 if (r < 0)
538 return -ENOMEM;
539 break;
540
541 case DNS_TYPE_MX:
542 r = asprintf(&s, "%s %u %s",
543 k,
544 rr->mx.priority,
545 rr->mx.exchange);
546 if (r < 0)
547 return -ENOMEM;
548 break;
549
550 case DNS_TYPE_LOC: {
551 _cleanup_free_ char *loc;
552 assert(rr->loc.version == 0);
553
554 loc = format_location(rr->loc.latitude,
555 rr->loc.longitude,
556 rr->loc.altitude,
557 rr->loc.size,
558 rr->loc.horiz_pre,
559 rr->loc.vert_pre);
560 if (!loc)
561 return -ENOMEM;
562
563 s = strjoin(k, " ", loc, NULL);
564 if (!s)
565 return -ENOMEM;
566
567 break;
568 }
569
570 case DNS_TYPE_SSHFP: {
571 _cleanup_free_ char *x = NULL;
572
573 x = hexmem(rr->sshfp.key, rr->sshfp.key_size);
574 if (!x)
575 return -ENOMEM;
576
577 r = asprintf(&s, "%s %u %u %s",
578 k,
579 rr->sshfp.algorithm,
580 rr->sshfp.fptype,
581 x);
582 if (r < 0)
583 return -ENOMEM;
584 break;
585 }
586
587 default: {
588 _cleanup_free_ char *x = NULL;
589
590 x = hexmem(rr->generic.data, rr->generic.size);
591 if (!x)
592 return -ENOMEM;
593
594 s = strjoin(k, " ", x, NULL);
595 if (!s)
596 return -ENOMEM;
597 break;
598 }}
599
600 *ret = s;
601 return 0;
602 }
603
604 const char *dns_class_to_string(uint16_t class) {
605
606 switch (class) {
607
608 case DNS_CLASS_IN:
609 return "IN";
610
611 case DNS_CLASS_ANY:
612 return "ANY";
613 }
614
615 return NULL;
616 }
617
618 int dns_class_from_string(const char *s, uint16_t *class) {
619 assert(s);
620 assert(class);
621
622 if (strcaseeq(s, "IN"))
623 *class = DNS_CLASS_IN;
624 else if (strcaseeq(s, "ANY"))
625 *class = DNS_TYPE_ANY;
626 else
627 return -EINVAL;
628
629 return 0;
630 }
631
632 static const struct {
633 uint16_t type;
634 const char *name;
635 } dns_types[] = {
636 { DNS_TYPE_A, "A" },
637 { DNS_TYPE_NS, "NS" },
638 { DNS_TYPE_CNAME, "CNAME" },
639 { DNS_TYPE_SOA, "SOA" },
640 { DNS_TYPE_PTR, "PTR" },
641 { DNS_TYPE_HINFO, "HINFO" },
642 { DNS_TYPE_MX, "MX" },
643 { DNS_TYPE_TXT, "TXT" },
644 { DNS_TYPE_AAAA, "AAAA" },
645 { DNS_TYPE_LOC, "LOC" },
646 { DNS_TYPE_SRV, "SRV" },
647 { DNS_TYPE_SSHFP, "SSHFP" },
648 { DNS_TYPE_SPF, "SPF" },
649 { DNS_TYPE_DNAME, "DNAME" },
650 { DNS_TYPE_ANY, "ANY" },
651 { DNS_TYPE_OPT, "OPT" },
652 { DNS_TYPE_TKEY, "TKEY" },
653 { DNS_TYPE_TSIG, "TSIG" },
654 { DNS_TYPE_IXFR, "IXFR" },
655 { DNS_TYPE_AXFR, "AXFR" },
656 };
657
658 const char *dns_type_to_string(uint16_t type) {
659 unsigned i;
660
661 for (i = 0; i < ELEMENTSOF(dns_types); i++)
662 if (dns_types[i].type == type)
663 return dns_types[i].name;
664
665 return NULL;
666 }
667
668 int dns_type_from_string(const char *s, uint16_t *type) {
669 unsigned i;
670
671 assert(s);
672 assert(type);
673
674 for (i = 0; i < ELEMENTSOF(dns_types); i++)
675 if (strcaseeq(dns_types[i].name, s)) {
676 *type = dns_types[i].type;
677 return 0;
678 }
679
680 return -EINVAL;
681 }