]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-rr.c
resolve: add more record types and convert to gperf table
[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 #include "dns-type.h"
29
30 DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) {
31 DnsResourceKey *k;
32 size_t l;
33
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
50 DnsResourceKey* 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
67 DnsResourceKey* 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
78 DnsResourceKey* 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
93 int 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
109 int 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
122 int 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));
133 }
134
135 unsigned 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
139 ul = dns_name_hash_func(DNS_RESOURCE_KEY_NAME(k), hash_key);
140 ul = ul * hash_key[0] + ul + k->class;
141 ul = ul * hash_key[1] + ul + k->type;
142
143 return ul;
144 }
145
146 int dns_resource_key_compare_func(const void *a, const void *b) {
147 const DnsResourceKey *x = a, *y = b;
148 int ret;
149
150 ret = dns_name_compare_func(DNS_RESOURCE_KEY_NAME(x), DNS_RESOURCE_KEY_NAME(y));
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
167 int 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
184 if (asprintf(&s, "%s %s %-5s", DNS_RESOURCE_KEY_NAME(key), c, t) < 0)
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 }