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