resolve-host: make arg_type an int
[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 27#include "resolved-dns-rr.h"
7263f724 28#include "dns-type.h"
74b2466e 29
faa133f3
LP
30DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) {
31 DnsResourceKey *k;
32 size_t l;
74b2466e 33
faa133f3
LP
34 assert(name);
35
36 l = strlen(name);
37 k = malloc0(sizeof(DnsResourceKey) + l + 1);
38 if (!k)
39 return NULL;
40
41 k->n_ref = 1;
42 k->class = class;
43 k->type = type;
44
45 strcpy((char*) k + sizeof(DnsResourceKey), name);
46
47 return k;
48}
49
50DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name) {
51 DnsResourceKey *k;
52
53 assert(name);
54
55 k = new0(DnsResourceKey, 1);
56 if (!k)
57 return NULL;
58
59 k->n_ref = 1;
60 k->class = class;
61 k->type = type;
62 k->_name = name;
63
64 return k;
65}
66
67DnsResourceKey* dns_resource_key_ref(DnsResourceKey *k) {
68
69 if (!k)
70 return NULL;
71
72 assert(k->n_ref > 0);
73 k->n_ref++;
74
75 return k;
76}
77
78DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) {
79 if (!k)
80 return NULL;
81
82 assert(k->n_ref > 0);
83
84 if (k->n_ref == 1) {
85 free(k->_name);
86 free(k);
87 } else
88 k->n_ref--;
89
90 return NULL;
91}
92
93int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
94 int r;
95
96 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b));
97 if (r <= 0)
98 return r;
99
100 if (a->class != b->class)
101 return 0;
102
103 if (a->type != b->type)
104 return 0;
105
106 return 1;
107}
108
109int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr) {
110 assert(key);
111 assert(rr);
112
113 if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
114 return 0;
115
116 if (rr->key->type != key->type && key->type != DNS_TYPE_ANY)
117 return 0;
118
119 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
120}
121
122int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr) {
123 assert(key);
124 assert(rr);
125
126 if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
127 return 0;
128
129 if (rr->key->type != DNS_TYPE_CNAME)
130 return 0;
131
132 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
74b2466e
LP
133}
134
322345fd
LP
135unsigned long dns_resource_key_hash_func(const void *i, const uint8_t hash_key[HASH_KEY_SIZE]) {
136 const DnsResourceKey *k = i;
137 unsigned long ul;
138
faa133f3 139 ul = dns_name_hash_func(DNS_RESOURCE_KEY_NAME(k), hash_key);
322345fd
LP
140 ul = ul * hash_key[0] + ul + k->class;
141 ul = ul * hash_key[1] + ul + k->type;
142
143 return ul;
144}
145
146int dns_resource_key_compare_func(const void *a, const void *b) {
147 const DnsResourceKey *x = a, *y = b;
148 int ret;
149
faa133f3 150 ret = dns_name_compare_func(DNS_RESOURCE_KEY_NAME(x), DNS_RESOURCE_KEY_NAME(y));
322345fd
LP
151 if (ret != 0)
152 return ret;
153
154 if (x->type < y->type)
155 return -1;
156 if (x->type > y->type)
157 return 1;
158
159 if (x->class < y->class)
160 return -1;
161 if (x->class > y->class)
162 return 1;
163
164 return 0;
165}
166
2d4c5cbc
LP
167int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) {
168 char cbuf[DECIMAL_STR_MAX(uint16_t)], tbuf[DECIMAL_STR_MAX(uint16_t)];
169 const char *c, *t;
170 char *s;
171
172 c = dns_class_to_string(key->class);
173 if (!c) {
174 sprintf(cbuf, "%i", key->class);
175 c = cbuf;
176 }
177
178 t = dns_type_to_string(key->type);
179 if (!t){
180 sprintf(tbuf, "%i", key->type);
181 t = tbuf;
182 }
183
23432a1c 184 if (asprintf(&s, "%s %s %-5s", DNS_RESOURCE_KEY_NAME(key), c, t) < 0)
2d4c5cbc
LP
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
42cc2eeb
LP
268 case DNS_TYPE_SSHFP:
269 free(rr->sshfp.key);
270 break;
271
0dae31d4 272 case DNS_TYPE_LOC:
9de3e329
ZJS
273 case DNS_TYPE_A:
274 case DNS_TYPE_AAAA:
275 break;
9c92ce6d 276
9de3e329 277 default:
faa133f3 278 free(rr->generic.data);
9de3e329 279 }
322345fd 280
faa133f3
LP
281 dns_resource_key_unref(rr->key);
282 }
322345fd 283
faa133f3 284 free(rr);
322345fd 285
322345fd
LP
286 return NULL;
287}
288
623a4c97
LP
289int 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
322345fd
LP
323int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
324 int r;
325
326 assert(a);
327 assert(b);
328
faa133f3 329 r = dns_resource_key_equal(a->key, b->key);
322345fd
LP
330 if (r <= 0)
331 return r;
332
fd0b4602
LP
333 if (a->unparseable != b->unparseable)
334 return 0;
335
336 switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) {
2d4c5cbc 337
9c92ce6d
LP
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
2d4c5cbc
LP
347 case DNS_TYPE_PTR:
348 case DNS_TYPE_NS:
349 case DNS_TYPE_CNAME:
8ac4e9e1 350 case DNS_TYPE_DNAME:
322345fd 351 return dns_name_equal(a->ptr.name, b->ptr.name);
2d4c5cbc
LP
352
353 case DNS_TYPE_HINFO:
354 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
355 strcaseeq(a->hinfo.os, b->hinfo.os);
356
9de3e329 357 case DNS_TYPE_SPF: /* exactly the same as TXT */
2e276efc
ZJS
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
2d4c5cbc 367 case DNS_TYPE_A:
322345fd 368 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
2d4c5cbc
LP
369
370 case DNS_TYPE_AAAA:
322345fd 371 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
2d4c5cbc
LP
372
373 case DNS_TYPE_SOA:
7e8e0422
LP
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;
9c92ce6d 386
946c7094
ZJS
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
0dae31d4
ZJS
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
42cc2eeb
LP
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
2d4c5cbc 409 default:
322345fd
LP
410 return a->generic.size == b->generic.size &&
411 memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
2d4c5cbc 412 }
322345fd
LP
413}
414
0dae31d4
ZJS
415static 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
2d4c5cbc
LP
446int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
447 _cleanup_free_ char *k = NULL;
448 char *s;
449 int r;
322345fd 450
2d4c5cbc 451 assert(rr);
322345fd 452
2d4c5cbc
LP
453 r = dns_resource_key_to_string(rr->key, &k);
454 if (r < 0)
455 return r;
322345fd 456
0dae31d4 457 switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
322345fd 458
9c92ce6d
LP
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
2d4c5cbc
LP
470 case DNS_TYPE_PTR:
471 case DNS_TYPE_NS:
472 case DNS_TYPE_CNAME:
8ac4e9e1 473 case DNS_TYPE_DNAME:
2d4c5cbc
LP
474 s = strjoin(k, " ", rr->ptr.name, NULL);
475 if (!s)
476 return -ENOMEM;
322345fd 477
2d4c5cbc 478 break;
322345fd 479
2d4c5cbc
LP
480 case DNS_TYPE_HINFO:
481 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
482 if (!s)
483 return -ENOMEM;
484 break;
322345fd 485
9de3e329 486 case DNS_TYPE_SPF: /* exactly the same as TXT */
2e276efc
ZJS
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
2d4c5cbc
LP
501 case DNS_TYPE_A: {
502 _cleanup_free_ char *x = NULL;
322345fd 503
2d4c5cbc
LP
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;
322345fd 507
2d4c5cbc
LP
508 s = strjoin(k, " ", x, NULL);
509 if (!s)
510 return -ENOMEM;
511 break;
512 }
322345fd 513
2d4c5cbc
LP
514 case DNS_TYPE_AAAA: {
515 _cleanup_free_ char *x = NULL;
322345fd 516
2d4c5cbc
LP
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;
322345fd 520
2d4c5cbc
LP
521 s = strjoin(k, " ", x, NULL);
522 if (!s)
523 return -ENOMEM;
524 break;
525 }
322345fd 526
2d4c5cbc
LP
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
946c7094
ZJS
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
0dae31d4
ZJS
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
42cc2eeb
LP
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
2d4c5cbc
LP
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}
322345fd 603
2d4c5cbc 604const char *dns_class_to_string(uint16_t class) {
322345fd 605
2d4c5cbc 606 switch (class) {
322345fd 607
2d4c5cbc
LP
608 case DNS_CLASS_IN:
609 return "IN";
322345fd 610
2d4c5cbc
LP
611 case DNS_CLASS_ANY:
612 return "ANY";
613 }
322345fd 614
2d4c5cbc
LP
615 return NULL;
616}
322345fd 617
2d4c5cbc
LP
618int 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;
322345fd 628
2d4c5cbc
LP
629 return 0;
630}