resolved: provide properly named way to access SPF data in RRs
[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
ZJS
236 switch(rr->key->type) {
237 case DNS_TYPE_PTR:
238 case DNS_TYPE_NS:
239 case DNS_TYPE_CNAME:
8ac4e9e1 240 case DNS_TYPE_DNAME:
faa133f3 241 free(rr->ptr.name);
9de3e329
ZJS
242 break;
243 case DNS_TYPE_HINFO:
faa133f3
LP
244 free(rr->hinfo.cpu);
245 free(rr->hinfo.os);
9de3e329
ZJS
246 break;
247 case DNS_TYPE_SPF:
248 case DNS_TYPE_TXT:
2e276efc 249 strv_free(rr->txt.strings);
9de3e329
ZJS
250 break;
251 case DNS_TYPE_SOA:
7e8e0422
LP
252 free(rr->soa.mname);
253 free(rr->soa.rname);
9de3e329
ZJS
254 break;
255 case DNS_TYPE_MX:
946c7094 256 free(rr->mx.exchange);
9de3e329 257 break;
0dae31d4 258 case DNS_TYPE_LOC:
9de3e329
ZJS
259 case DNS_TYPE_A:
260 case DNS_TYPE_AAAA:
261 break;
262 default:
faa133f3 263 free(rr->generic.data);
9de3e329 264 }
322345fd 265
faa133f3
LP
266 dns_resource_key_unref(rr->key);
267 }
322345fd 268
faa133f3 269 free(rr);
322345fd 270
322345fd
LP
271 return NULL;
272}
273
623a4c97
LP
274int 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
322345fd
LP
308int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
309 int r;
310
311 assert(a);
312 assert(b);
313
faa133f3 314 r = dns_resource_key_equal(a->key, b->key);
322345fd
LP
315 if (r <= 0)
316 return r;
317
2d4c5cbc
LP
318 switch (a->key->type) {
319
320 case DNS_TYPE_PTR:
321 case DNS_TYPE_NS:
322 case DNS_TYPE_CNAME:
8ac4e9e1 323 case DNS_TYPE_DNAME:
322345fd 324 return dns_name_equal(a->ptr.name, b->ptr.name);
2d4c5cbc
LP
325
326 case DNS_TYPE_HINFO:
327 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
328 strcaseeq(a->hinfo.os, b->hinfo.os);
329
9de3e329 330 case DNS_TYPE_SPF: /* exactly the same as TXT */
2e276efc
ZJS
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
2d4c5cbc 340 case DNS_TYPE_A:
322345fd 341 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
2d4c5cbc
LP
342
343 case DNS_TYPE_AAAA:
322345fd 344 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
2d4c5cbc
LP
345
346 case DNS_TYPE_SOA:
7e8e0422
LP
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;
946c7094
ZJS
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
0dae31d4
ZJS
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
2d4c5cbc 375 default:
322345fd
LP
376 return a->generic.size == b->generic.size &&
377 memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
2d4c5cbc 378 }
322345fd
LP
379}
380
0dae31d4
ZJS
381static 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
2d4c5cbc
LP
413int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
414 _cleanup_free_ char *k = NULL;
415 char *s;
416 int r;
322345fd 417
2d4c5cbc 418 assert(rr);
322345fd 419
2d4c5cbc
LP
420 r = dns_resource_key_to_string(rr->key, &k);
421 if (r < 0)
422 return r;
322345fd 423
0dae31d4 424 switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
322345fd 425
2d4c5cbc
LP
426 case DNS_TYPE_PTR:
427 case DNS_TYPE_NS:
428 case DNS_TYPE_CNAME:
8ac4e9e1 429 case DNS_TYPE_DNAME:
2d4c5cbc
LP
430 s = strjoin(k, " ", rr->ptr.name, NULL);
431 if (!s)
432 return -ENOMEM;
322345fd 433
2d4c5cbc 434 break;
322345fd 435
2d4c5cbc
LP
436 case DNS_TYPE_HINFO:
437 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
438 if (!s)
439 return -ENOMEM;
440 break;
322345fd 441
9de3e329 442 case DNS_TYPE_SPF: /* exactly the same as TXT */
2e276efc
ZJS
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
2d4c5cbc
LP
457 case DNS_TYPE_A: {
458 _cleanup_free_ char *x = NULL;
322345fd 459
2d4c5cbc
LP
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;
322345fd 463
2d4c5cbc
LP
464 s = strjoin(k, " ", x, NULL);
465 if (!s)
466 return -ENOMEM;
467 break;
468 }
322345fd 469
2d4c5cbc
LP
470 case DNS_TYPE_AAAA: {
471 _cleanup_free_ char *x = NULL;
322345fd 472
2d4c5cbc
LP
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;
322345fd 476
2d4c5cbc
LP
477 s = strjoin(k, " ", x, NULL);
478 if (!s)
479 return -ENOMEM;
480 break;
481 }
322345fd 482
2d4c5cbc
LP
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
946c7094
ZJS
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
0dae31d4
ZJS
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
2d4c5cbc
LP
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}
322345fd 542
2d4c5cbc 543const char *dns_class_to_string(uint16_t class) {
322345fd 544
2d4c5cbc 545 switch (class) {
322345fd 546
2d4c5cbc
LP
547 case DNS_CLASS_IN:
548 return "IN";
322345fd 549
2d4c5cbc
LP
550 case DNS_CLASS_ANY:
551 return "ANY";
552 }
322345fd 553
2d4c5cbc
LP
554 return NULL;
555}
322345fd 556
2d4c5cbc
LP
557int 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;
322345fd 567
2d4c5cbc
LP
568 return 0;
569}
322345fd 570
2d4c5cbc
LP
571static 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" },
0dae31d4 584 { DNS_TYPE_LOC, "LOC" },
2d4c5cbc
LP
585 { DNS_TYPE_SRV, "SRV" },
586 { DNS_TYPE_SSHFP, "SSHFP" },
9de3e329 587 { DNS_TYPE_SPF, "SPF" },
2d4c5cbc
LP
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};
322345fd 596
2d4c5cbc
LP
597const char *dns_type_to_string(uint16_t type) {
598 unsigned i;
322345fd 599
2d4c5cbc
LP
600 for (i = 0; i < ELEMENTSOF(dns_types); i++)
601 if (dns_types[i].type == type)
602 return dns_types[i].name;
322345fd
LP
603
604 return NULL;
605}
2d4c5cbc
LP
606
607int 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}