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