]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-rr.c
resolved: properly process SRV records
[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
LP
27#include "resolved-dns-rr.h"
28
faa133f3
LP
29DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) {
30 DnsResourceKey *k;
31 size_t l;
74b2466e 32
faa133f3
LP
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
49DnsResourceKey* 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
66DnsResourceKey* 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
77DnsResourceKey* 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
92int 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
108int 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
121int 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));
74b2466e
LP
132}
133
322345fd
LP
134unsigned 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
faa133f3 138 ul = dns_name_hash_func(DNS_RESOURCE_KEY_NAME(k), hash_key);
322345fd
LP
139 ul = ul * hash_key[0] + ul + k->class;
140 ul = ul * hash_key[1] + ul + k->type;
141
142 return ul;
143}
144
145int dns_resource_key_compare_func(const void *a, const void *b) {
146 const DnsResourceKey *x = a, *y = b;
147 int ret;
148
faa133f3 149 ret = dns_name_compare_func(DNS_RESOURCE_KEY_NAME(x), DNS_RESOURCE_KEY_NAME(y));
322345fd
LP
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
2d4c5cbc
LP
166int 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
faa133f3 191DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
74b2466e
LP
192 DnsResourceRecord *rr;
193
194 rr = new0(DnsResourceRecord, 1);
195 if (!rr)
196 return NULL;
197
198 rr->n_ref = 1;
faa133f3
LP
199 rr->key = dns_resource_key_ref(key);
200
74b2466e
LP
201 return rr;
202}
203
8bf52d3d
LP
204DnsResourceRecord* 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
74b2466e
LP
214DnsResourceRecord* 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
224DnsResourceRecord* 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
faa133f3 235 if (rr->key) {
9de3e329 236 switch(rr->key->type) {
9c92ce6d
LP
237
238 case DNS_TYPE_SRV:
239 free(rr->srv.name);
240 break;
241
9de3e329
ZJS
242 case DNS_TYPE_PTR:
243 case DNS_TYPE_NS:
244 case DNS_TYPE_CNAME:
8ac4e9e1 245 case DNS_TYPE_DNAME:
faa133f3 246 free(rr->ptr.name);
9de3e329 247 break;
9c92ce6d 248
9de3e329 249 case DNS_TYPE_HINFO:
faa133f3
LP
250 free(rr->hinfo.cpu);
251 free(rr->hinfo.os);
9de3e329 252 break;
9c92ce6d 253
9de3e329 254 case DNS_TYPE_TXT:
9c92ce6d 255 case DNS_TYPE_SPF:
2e276efc 256 strv_free(rr->txt.strings);
9de3e329 257 break;
9c92ce6d 258
9de3e329 259 case DNS_TYPE_SOA:
7e8e0422
LP
260 free(rr->soa.mname);
261 free(rr->soa.rname);
9de3e329 262 break;
9c92ce6d 263
9de3e329 264 case DNS_TYPE_MX:
946c7094 265 free(rr->mx.exchange);
9de3e329 266 break;
9c92ce6d 267
0dae31d4 268 case DNS_TYPE_LOC:
9de3e329
ZJS
269 case DNS_TYPE_A:
270 case DNS_TYPE_AAAA:
271 break;
9c92ce6d 272
9de3e329 273 default:
faa133f3 274 free(rr->generic.data);
9de3e329 275 }
322345fd 276
faa133f3
LP
277 dns_resource_key_unref(rr->key);
278 }
322345fd 279
faa133f3 280 free(rr);
322345fd 281
322345fd
LP
282 return NULL;
283}
284
623a4c97
LP
285int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) {
286 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
287 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
288 _cleanup_free_ char *ptr = NULL;
289 int r;
290
291 assert(ret);
292 assert(address);
293 assert(hostname);
294
295 r = dns_name_reverse(family, address, &ptr);
296 if (r < 0)
297 return r;
298
299 key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, ptr);
300 if (!key)
301 return -ENOMEM;
302
303 ptr = NULL;
304
305 rr = dns_resource_record_new(key);
306 if (!rr)
307 return -ENOMEM;
308
309 rr->ptr.name = strdup(hostname);
310 if (!rr->ptr.name)
311 return -ENOMEM;
312
313 *ret = rr;
314 rr = NULL;
315
316 return 0;
317}
318
322345fd
LP
319int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
320 int r;
321
322 assert(a);
323 assert(b);
324
faa133f3 325 r = dns_resource_key_equal(a->key, b->key);
322345fd
LP
326 if (r <= 0)
327 return r;
328
2d4c5cbc
LP
329 switch (a->key->type) {
330
9c92ce6d
LP
331 case DNS_TYPE_SRV:
332 r = dns_name_equal(a->srv.name, b->srv.name);
333 if (r <= 0)
334 return r;
335
336 return a->srv.priority == b->srv.priority &&
337 a->srv.weight == b->srv.weight &&
338 a->srv.port == b->srv.port;
339
2d4c5cbc
LP
340 case DNS_TYPE_PTR:
341 case DNS_TYPE_NS:
342 case DNS_TYPE_CNAME:
8ac4e9e1 343 case DNS_TYPE_DNAME:
322345fd 344 return dns_name_equal(a->ptr.name, b->ptr.name);
2d4c5cbc
LP
345
346 case DNS_TYPE_HINFO:
347 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
348 strcaseeq(a->hinfo.os, b->hinfo.os);
349
9de3e329 350 case DNS_TYPE_SPF: /* exactly the same as TXT */
2e276efc
ZJS
351 case DNS_TYPE_TXT: {
352 int i;
353
354 for (i = 0; a->txt.strings[i] || b->txt.strings[i]; i++)
355 if (!streq_ptr(a->txt.strings[i], b->txt.strings[i]))
356 return false;
357 return true;
358 }
359
2d4c5cbc 360 case DNS_TYPE_A:
322345fd 361 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
2d4c5cbc
LP
362
363 case DNS_TYPE_AAAA:
322345fd 364 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
2d4c5cbc
LP
365
366 case DNS_TYPE_SOA:
7e8e0422
LP
367 r = dns_name_equal(a->soa.mname, b->soa.mname);
368 if (r <= 0)
369 return r;
370 r = dns_name_equal(a->soa.rname, b->soa.rname);
371 if (r <= 0)
372 return r;
373
374 return a->soa.serial == b->soa.serial &&
375 a->soa.refresh == b->soa.refresh &&
376 a->soa.retry == b->soa.retry &&
377 a->soa.expire == b->soa.expire &&
378 a->soa.minimum == b->soa.minimum;
9c92ce6d 379
946c7094
ZJS
380 case DNS_TYPE_MX:
381 if (a->mx.priority != b->mx.priority)
382 return 0;
383
384 return dns_name_equal(a->mx.exchange, b->mx.exchange);
385
0dae31d4
ZJS
386 case DNS_TYPE_LOC:
387 assert(a->loc.version == b->loc.version);
388
389 return a->loc.size == b->loc.size &&
390 a->loc.horiz_pre == b->loc.horiz_pre &&
391 a->loc.vert_pre == b->loc.vert_pre &&
392 a->loc.latitude == b->loc.latitude &&
393 a->loc.longitude == b->loc.longitude &&
394 a->loc.altitude == b->loc.altitude;
395
2d4c5cbc 396 default:
322345fd
LP
397 return a->generic.size == b->generic.size &&
398 memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
2d4c5cbc 399 }
322345fd
LP
400}
401
0dae31d4
ZJS
402static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t altitude,
403 uint8_t size, uint8_t horiz_pre, uint8_t vert_pre) {
404 char *s;
405 char NS = latitude >= 1U<<31 ? 'N' : 'S';
406 char EW = longitude >= 1U<<31 ? 'E' : 'W';
407
408 int lat = latitude >= 1U<<31 ? (int) (latitude - (1U<<31)) : (int) ((1U<<31) - latitude);
409 int lon = longitude >= 1U<<31 ? (int) (longitude - (1U<<31)) : (int) ((1U<<31) - longitude);
410 double alt = altitude >= 10000000u ? altitude - 10000000u : -(double)(10000000u - altitude);
411 double siz = (size >> 4) * exp10((double) (size & 0xF));
412 double hor = (horiz_pre >> 4) * exp10((double) (horiz_pre & 0xF));
413 double ver = (vert_pre >> 4) * exp10((double) (vert_pre & 0xF));
414
415 if (asprintf(&s, "%d %d %.3f %c %d %d %.3f %c %.2fm %.2fm %.2fm %.2fm",
416 (lat / 60000 / 60),
417 (lat / 60000) % 60,
418 (lat % 60000) / 1000.,
419 NS,
420 (lon / 60000 / 60),
421 (lon / 60000) % 60,
422 (lon % 60000) / 1000.,
423 EW,
424 alt / 100.,
425 siz / 100.,
426 hor / 100.,
427 ver / 100.) < 0)
428 return NULL;
429
430 return s;
431}
432
433
2d4c5cbc
LP
434int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
435 _cleanup_free_ char *k = NULL;
436 char *s;
437 int r;
322345fd 438
2d4c5cbc 439 assert(rr);
322345fd 440
2d4c5cbc
LP
441 r = dns_resource_key_to_string(rr->key, &k);
442 if (r < 0)
443 return r;
322345fd 444
0dae31d4 445 switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
322345fd 446
9c92ce6d
LP
447 case DNS_TYPE_SRV:
448 r = asprintf(&s, "%s %u %u %u %s",
449 k,
450 rr->srv.priority,
451 rr->srv.weight,
452 rr->srv.port,
453 strna(rr->srv.name));
454 if (r < 0)
455 return -ENOMEM;
456 break;
457
2d4c5cbc
LP
458 case DNS_TYPE_PTR:
459 case DNS_TYPE_NS:
460 case DNS_TYPE_CNAME:
8ac4e9e1 461 case DNS_TYPE_DNAME:
2d4c5cbc
LP
462 s = strjoin(k, " ", rr->ptr.name, NULL);
463 if (!s)
464 return -ENOMEM;
322345fd 465
2d4c5cbc 466 break;
322345fd 467
2d4c5cbc
LP
468 case DNS_TYPE_HINFO:
469 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
470 if (!s)
471 return -ENOMEM;
472 break;
322345fd 473
9de3e329 474 case DNS_TYPE_SPF: /* exactly the same as TXT */
2e276efc
ZJS
475 case DNS_TYPE_TXT: {
476 _cleanup_free_ char *t;
477
478 t = strv_join_quoted(rr->txt.strings);
479 if (!t)
480 return -ENOMEM;
481
482 s = strjoin(k, " ", t, NULL);
483 if (!s)
484 return -ENOMEM;
485
486 break;
487 }
488
2d4c5cbc
LP
489 case DNS_TYPE_A: {
490 _cleanup_free_ char *x = NULL;
322345fd 491
2d4c5cbc
LP
492 r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
493 if (r < 0)
494 return r;
322345fd 495
2d4c5cbc
LP
496 s = strjoin(k, " ", x, NULL);
497 if (!s)
498 return -ENOMEM;
499 break;
500 }
322345fd 501
2d4c5cbc
LP
502 case DNS_TYPE_AAAA: {
503 _cleanup_free_ char *x = NULL;
322345fd 504
2d4c5cbc
LP
505 r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &x);
506 if (r < 0)
507 return r;
322345fd 508
2d4c5cbc
LP
509 s = strjoin(k, " ", x, NULL);
510 if (!s)
511 return -ENOMEM;
512 break;
513 }
322345fd 514
2d4c5cbc
LP
515 case DNS_TYPE_SOA:
516 r = asprintf(&s, "%s %s %s %u %u %u %u %u",
517 k,
518 strna(rr->soa.mname),
519 strna(rr->soa.rname),
520 rr->soa.serial,
521 rr->soa.refresh,
522 rr->soa.retry,
523 rr->soa.expire,
524 rr->soa.minimum);
525 if (r < 0)
526 return -ENOMEM;
527 break;
528
946c7094
ZJS
529 case DNS_TYPE_MX:
530 r = asprintf(&s, "%s %u %s",
531 k,
532 rr->mx.priority,
533 rr->mx.exchange);
534 if (r < 0)
535 return -ENOMEM;
536 break;
537
0dae31d4
ZJS
538 case DNS_TYPE_LOC: {
539 _cleanup_free_ char *loc;
540 assert(rr->loc.version == 0);
541
542 loc = format_location(rr->loc.latitude,
543 rr->loc.longitude,
544 rr->loc.altitude,
545 rr->loc.size,
546 rr->loc.horiz_pre,
547 rr->loc.vert_pre);
548 if (!loc)
549 return -ENOMEM;
550
551 s = strjoin(k, " ", loc, NULL);
552 if (!s)
553 return -ENOMEM;
554
555 break;
556 }
557
2d4c5cbc
LP
558 default: {
559 _cleanup_free_ char *x = NULL;
560
561 x = hexmem(rr->generic.data, rr->generic.size);
562 if (!x)
563 return -ENOMEM;
564
565 s = strjoin(k, " ", x, NULL);
566 if (!s)
567 return -ENOMEM;
568 break;
569 }}
570
571 *ret = s;
572 return 0;
573}
322345fd 574
2d4c5cbc 575const char *dns_class_to_string(uint16_t class) {
322345fd 576
2d4c5cbc 577 switch (class) {
322345fd 578
2d4c5cbc
LP
579 case DNS_CLASS_IN:
580 return "IN";
322345fd 581
2d4c5cbc
LP
582 case DNS_CLASS_ANY:
583 return "ANY";
584 }
322345fd 585
2d4c5cbc
LP
586 return NULL;
587}
322345fd 588
2d4c5cbc
LP
589int dns_class_from_string(const char *s, uint16_t *class) {
590 assert(s);
591 assert(class);
592
593 if (strcaseeq(s, "IN"))
594 *class = DNS_CLASS_IN;
595 else if (strcaseeq(s, "ANY"))
596 *class = DNS_TYPE_ANY;
597 else
598 return -EINVAL;
322345fd 599
2d4c5cbc
LP
600 return 0;
601}
322345fd 602
2d4c5cbc
LP
603static const struct {
604 uint16_t type;
605 const char *name;
606} dns_types[] = {
607 { DNS_TYPE_A, "A" },
608 { DNS_TYPE_NS, "NS" },
609 { DNS_TYPE_CNAME, "CNAME" },
610 { DNS_TYPE_SOA, "SOA" },
611 { DNS_TYPE_PTR, "PTR" },
612 { DNS_TYPE_HINFO, "HINFO" },
613 { DNS_TYPE_MX, "MX" },
614 { DNS_TYPE_TXT, "TXT" },
615 { DNS_TYPE_AAAA, "AAAA" },
0dae31d4 616 { DNS_TYPE_LOC, "LOC" },
2d4c5cbc
LP
617 { DNS_TYPE_SRV, "SRV" },
618 { DNS_TYPE_SSHFP, "SSHFP" },
9de3e329 619 { DNS_TYPE_SPF, "SPF" },
2d4c5cbc
LP
620 { DNS_TYPE_DNAME, "DNAME" },
621 { DNS_TYPE_ANY, "ANY" },
622 { DNS_TYPE_OPT, "OPT" },
623 { DNS_TYPE_TKEY, "TKEY" },
624 { DNS_TYPE_TSIG, "TSIG" },
625 { DNS_TYPE_IXFR, "IXFR" },
626 { DNS_TYPE_AXFR, "AXFR" },
627};
322345fd 628
2d4c5cbc
LP
629const char *dns_type_to_string(uint16_t type) {
630 unsigned i;
322345fd 631
2d4c5cbc
LP
632 for (i = 0; i < ELEMENTSOF(dns_types); i++)
633 if (dns_types[i].type == type)
634 return dns_types[i].name;
322345fd
LP
635
636 return NULL;
637}
2d4c5cbc
LP
638
639int dns_type_from_string(const char *s, uint16_t *type) {
640 unsigned i;
641
642 assert(s);
643 assert(type);
644
645 for (i = 0; i < ELEMENTSOF(dns_types); i++)
646 if (strcaseeq(dns_types[i].name, s)) {
647 *type = dns_types[i].type;
648 return 0;
649 }
650
651 return -EINVAL;
652}