resolved: SPF records
[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
2e276efc
ZJS
22#include "strv.h"
23
322345fd 24#include "resolved-dns-domain.h"
74b2466e
LP
25#include "resolved-dns-rr.h"
26
faa133f3
LP
27DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) {
28 DnsResourceKey *k;
29 size_t l;
74b2466e 30
faa133f3
LP
31 assert(name);
32
33 l = strlen(name);
34 k = malloc0(sizeof(DnsResourceKey) + l + 1);
35 if (!k)
36 return NULL;
37
38 k->n_ref = 1;
39 k->class = class;
40 k->type = type;
41
42 strcpy((char*) k + sizeof(DnsResourceKey), name);
43
44 return k;
45}
46
47DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name) {
48 DnsResourceKey *k;
49
50 assert(name);
51
52 k = new0(DnsResourceKey, 1);
53 if (!k)
54 return NULL;
55
56 k->n_ref = 1;
57 k->class = class;
58 k->type = type;
59 k->_name = name;
60
61 return k;
62}
63
64DnsResourceKey* dns_resource_key_ref(DnsResourceKey *k) {
65
66 if (!k)
67 return NULL;
68
69 assert(k->n_ref > 0);
70 k->n_ref++;
71
72 return k;
73}
74
75DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) {
76 if (!k)
77 return NULL;
78
79 assert(k->n_ref > 0);
80
81 if (k->n_ref == 1) {
82 free(k->_name);
83 free(k);
84 } else
85 k->n_ref--;
86
87 return NULL;
88}
89
90int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
91 int r;
92
93 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b));
94 if (r <= 0)
95 return r;
96
97 if (a->class != b->class)
98 return 0;
99
100 if (a->type != b->type)
101 return 0;
102
103 return 1;
104}
105
106int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr) {
107 assert(key);
108 assert(rr);
109
110 if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
111 return 0;
112
113 if (rr->key->type != key->type && key->type != DNS_TYPE_ANY)
114 return 0;
115
116 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
117}
118
119int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr) {
120 assert(key);
121 assert(rr);
122
123 if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
124 return 0;
125
126 if (rr->key->type != DNS_TYPE_CNAME)
127 return 0;
128
129 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
74b2466e
LP
130}
131
322345fd
LP
132unsigned long dns_resource_key_hash_func(const void *i, const uint8_t hash_key[HASH_KEY_SIZE]) {
133 const DnsResourceKey *k = i;
134 unsigned long ul;
135
faa133f3 136 ul = dns_name_hash_func(DNS_RESOURCE_KEY_NAME(k), hash_key);
322345fd
LP
137 ul = ul * hash_key[0] + ul + k->class;
138 ul = ul * hash_key[1] + ul + k->type;
139
140 return ul;
141}
142
143int dns_resource_key_compare_func(const void *a, const void *b) {
144 const DnsResourceKey *x = a, *y = b;
145 int ret;
146
faa133f3 147 ret = dns_name_compare_func(DNS_RESOURCE_KEY_NAME(x), DNS_RESOURCE_KEY_NAME(y));
322345fd
LP
148 if (ret != 0)
149 return ret;
150
151 if (x->type < y->type)
152 return -1;
153 if (x->type > y->type)
154 return 1;
155
156 if (x->class < y->class)
157 return -1;
158 if (x->class > y->class)
159 return 1;
160
161 return 0;
162}
163
2d4c5cbc
LP
164int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) {
165 char cbuf[DECIMAL_STR_MAX(uint16_t)], tbuf[DECIMAL_STR_MAX(uint16_t)];
166 const char *c, *t;
167 char *s;
168
169 c = dns_class_to_string(key->class);
170 if (!c) {
171 sprintf(cbuf, "%i", key->class);
172 c = cbuf;
173 }
174
175 t = dns_type_to_string(key->type);
176 if (!t){
177 sprintf(tbuf, "%i", key->type);
178 t = tbuf;
179 }
180
181 s = strjoin(DNS_RESOURCE_KEY_NAME(key), " ", c, " ", t, NULL);
182 if (!s)
183 return -ENOMEM;
184
185 *ret = s;
186 return 0;
187}
188
faa133f3 189DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
74b2466e
LP
190 DnsResourceRecord *rr;
191
192 rr = new0(DnsResourceRecord, 1);
193 if (!rr)
194 return NULL;
195
196 rr->n_ref = 1;
faa133f3
LP
197 rr->key = dns_resource_key_ref(key);
198
74b2466e
LP
199 return rr;
200}
201
8bf52d3d
LP
202DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name) {
203 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
204
205 key = dns_resource_key_new(class, type, name);
206 if (!key)
207 return NULL;
208
209 return dns_resource_record_new(key);
210}
211
74b2466e
LP
212DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr) {
213 if (!rr)
214 return NULL;
215
216 assert(rr->n_ref > 0);
217 rr->n_ref++;
218
219 return rr;
220}
221
222DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
223 if (!rr)
224 return NULL;
225
226 assert(rr->n_ref > 0);
227
228 if (rr->n_ref > 1) {
229 rr->n_ref--;
230 return NULL;
231 }
232
faa133f3 233 if (rr->key) {
9de3e329
ZJS
234 switch(rr->key->type) {
235 case DNS_TYPE_PTR:
236 case DNS_TYPE_NS:
237 case DNS_TYPE_CNAME:
faa133f3 238 free(rr->ptr.name);
9de3e329
ZJS
239 break;
240 case DNS_TYPE_HINFO:
faa133f3
LP
241 free(rr->hinfo.cpu);
242 free(rr->hinfo.os);
9de3e329
ZJS
243 break;
244 case DNS_TYPE_SPF:
245 case DNS_TYPE_TXT:
2e276efc 246 strv_free(rr->txt.strings);
9de3e329
ZJS
247 break;
248 case DNS_TYPE_SOA:
7e8e0422
LP
249 free(rr->soa.mname);
250 free(rr->soa.rname);
9de3e329
ZJS
251 break;
252 case DNS_TYPE_MX:
946c7094 253 free(rr->mx.exchange);
9de3e329
ZJS
254 break;
255 case DNS_TYPE_A:
256 case DNS_TYPE_AAAA:
257 break;
258 default:
faa133f3 259 free(rr->generic.data);
9de3e329 260 }
322345fd 261
faa133f3
LP
262 dns_resource_key_unref(rr->key);
263 }
322345fd 264
faa133f3 265 free(rr);
322345fd 266
322345fd
LP
267 return NULL;
268}
269
623a4c97
LP
270int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) {
271 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
272 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
273 _cleanup_free_ char *ptr = NULL;
274 int r;
275
276 assert(ret);
277 assert(address);
278 assert(hostname);
279
280 r = dns_name_reverse(family, address, &ptr);
281 if (r < 0)
282 return r;
283
284 key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, ptr);
285 if (!key)
286 return -ENOMEM;
287
288 ptr = NULL;
289
290 rr = dns_resource_record_new(key);
291 if (!rr)
292 return -ENOMEM;
293
294 rr->ptr.name = strdup(hostname);
295 if (!rr->ptr.name)
296 return -ENOMEM;
297
298 *ret = rr;
299 rr = NULL;
300
301 return 0;
302}
303
322345fd
LP
304int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
305 int r;
306
307 assert(a);
308 assert(b);
309
faa133f3 310 r = dns_resource_key_equal(a->key, b->key);
322345fd
LP
311 if (r <= 0)
312 return r;
313
2d4c5cbc
LP
314 switch (a->key->type) {
315
316 case DNS_TYPE_PTR:
317 case DNS_TYPE_NS:
318 case DNS_TYPE_CNAME:
322345fd 319 return dns_name_equal(a->ptr.name, b->ptr.name);
2d4c5cbc
LP
320
321 case DNS_TYPE_HINFO:
322 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
323 strcaseeq(a->hinfo.os, b->hinfo.os);
324
9de3e329 325 case DNS_TYPE_SPF: /* exactly the same as TXT */
2e276efc
ZJS
326 case DNS_TYPE_TXT: {
327 int i;
328
329 for (i = 0; a->txt.strings[i] || b->txt.strings[i]; i++)
330 if (!streq_ptr(a->txt.strings[i], b->txt.strings[i]))
331 return false;
332 return true;
333 }
334
2d4c5cbc 335 case DNS_TYPE_A:
322345fd 336 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
2d4c5cbc
LP
337
338 case DNS_TYPE_AAAA:
322345fd 339 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
2d4c5cbc
LP
340
341 case DNS_TYPE_SOA:
7e8e0422
LP
342 r = dns_name_equal(a->soa.mname, b->soa.mname);
343 if (r <= 0)
344 return r;
345 r = dns_name_equal(a->soa.rname, b->soa.rname);
346 if (r <= 0)
347 return r;
348
349 return a->soa.serial == b->soa.serial &&
350 a->soa.refresh == b->soa.refresh &&
351 a->soa.retry == b->soa.retry &&
352 a->soa.expire == b->soa.expire &&
353 a->soa.minimum == b->soa.minimum;
946c7094
ZJS
354 case DNS_TYPE_MX:
355 if (a->mx.priority != b->mx.priority)
356 return 0;
357
358 return dns_name_equal(a->mx.exchange, b->mx.exchange);
359
2d4c5cbc 360 default:
322345fd
LP
361 return a->generic.size == b->generic.size &&
362 memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
2d4c5cbc 363 }
322345fd
LP
364}
365
2d4c5cbc
LP
366int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
367 _cleanup_free_ char *k = NULL;
368 char *s;
369 int r;
322345fd 370
2d4c5cbc 371 assert(rr);
322345fd 372
2d4c5cbc
LP
373 r = dns_resource_key_to_string(rr->key, &k);
374 if (r < 0)
375 return r;
322345fd 376
2d4c5cbc 377 switch (rr->key->type) {
322345fd 378
2d4c5cbc
LP
379 case DNS_TYPE_PTR:
380 case DNS_TYPE_NS:
381 case DNS_TYPE_CNAME:
382 s = strjoin(k, " ", rr->ptr.name, NULL);
383 if (!s)
384 return -ENOMEM;
322345fd 385
2d4c5cbc 386 break;
322345fd 387
2d4c5cbc
LP
388 case DNS_TYPE_HINFO:
389 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
390 if (!s)
391 return -ENOMEM;
392 break;
322345fd 393
9de3e329 394 case DNS_TYPE_SPF: /* exactly the same as TXT */
2e276efc
ZJS
395 case DNS_TYPE_TXT: {
396 _cleanup_free_ char *t;
397
398 t = strv_join_quoted(rr->txt.strings);
399 if (!t)
400 return -ENOMEM;
401
402 s = strjoin(k, " ", t, NULL);
403 if (!s)
404 return -ENOMEM;
405
406 break;
407 }
408
2d4c5cbc
LP
409 case DNS_TYPE_A: {
410 _cleanup_free_ char *x = NULL;
322345fd 411
2d4c5cbc
LP
412 r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
413 if (r < 0)
414 return r;
322345fd 415
2d4c5cbc
LP
416 s = strjoin(k, " ", x, NULL);
417 if (!s)
418 return -ENOMEM;
419 break;
420 }
322345fd 421
2d4c5cbc
LP
422 case DNS_TYPE_AAAA: {
423 _cleanup_free_ char *x = NULL;
322345fd 424
2d4c5cbc
LP
425 r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &x);
426 if (r < 0)
427 return r;
322345fd 428
2d4c5cbc
LP
429 s = strjoin(k, " ", x, NULL);
430 if (!s)
431 return -ENOMEM;
432 break;
433 }
322345fd 434
2d4c5cbc
LP
435 case DNS_TYPE_SOA:
436 r = asprintf(&s, "%s %s %s %u %u %u %u %u",
437 k,
438 strna(rr->soa.mname),
439 strna(rr->soa.rname),
440 rr->soa.serial,
441 rr->soa.refresh,
442 rr->soa.retry,
443 rr->soa.expire,
444 rr->soa.minimum);
445 if (r < 0)
446 return -ENOMEM;
447 break;
448
946c7094
ZJS
449 case DNS_TYPE_MX:
450 r = asprintf(&s, "%s %u %s",
451 k,
452 rr->mx.priority,
453 rr->mx.exchange);
454 if (r < 0)
455 return -ENOMEM;
456 break;
457
2d4c5cbc
LP
458 default: {
459 _cleanup_free_ char *x = NULL;
460
461 x = hexmem(rr->generic.data, rr->generic.size);
462 if (!x)
463 return -ENOMEM;
464
465 s = strjoin(k, " ", x, NULL);
466 if (!s)
467 return -ENOMEM;
468 break;
469 }}
470
471 *ret = s;
472 return 0;
473}
322345fd 474
2d4c5cbc 475const char *dns_class_to_string(uint16_t class) {
322345fd 476
2d4c5cbc 477 switch (class) {
322345fd 478
2d4c5cbc
LP
479 case DNS_CLASS_IN:
480 return "IN";
322345fd 481
2d4c5cbc
LP
482 case DNS_CLASS_ANY:
483 return "ANY";
484 }
322345fd 485
2d4c5cbc
LP
486 return NULL;
487}
322345fd 488
2d4c5cbc
LP
489int dns_class_from_string(const char *s, uint16_t *class) {
490 assert(s);
491 assert(class);
492
493 if (strcaseeq(s, "IN"))
494 *class = DNS_CLASS_IN;
495 else if (strcaseeq(s, "ANY"))
496 *class = DNS_TYPE_ANY;
497 else
498 return -EINVAL;
322345fd 499
2d4c5cbc
LP
500 return 0;
501}
322345fd 502
2d4c5cbc
LP
503static const struct {
504 uint16_t type;
505 const char *name;
506} dns_types[] = {
507 { DNS_TYPE_A, "A" },
508 { DNS_TYPE_NS, "NS" },
509 { DNS_TYPE_CNAME, "CNAME" },
510 { DNS_TYPE_SOA, "SOA" },
511 { DNS_TYPE_PTR, "PTR" },
512 { DNS_TYPE_HINFO, "HINFO" },
513 { DNS_TYPE_MX, "MX" },
514 { DNS_TYPE_TXT, "TXT" },
515 { DNS_TYPE_AAAA, "AAAA" },
516 { DNS_TYPE_SRV, "SRV" },
517 { DNS_TYPE_SSHFP, "SSHFP" },
9de3e329 518 { DNS_TYPE_SPF, "SPF" },
2d4c5cbc
LP
519 { DNS_TYPE_DNAME, "DNAME" },
520 { DNS_TYPE_ANY, "ANY" },
521 { DNS_TYPE_OPT, "OPT" },
522 { DNS_TYPE_TKEY, "TKEY" },
523 { DNS_TYPE_TSIG, "TSIG" },
524 { DNS_TYPE_IXFR, "IXFR" },
525 { DNS_TYPE_AXFR, "AXFR" },
526};
322345fd 527
322345fd 528
2d4c5cbc
LP
529const char *dns_type_to_string(uint16_t type) {
530 unsigned i;
322345fd 531
2d4c5cbc
LP
532 for (i = 0; i < ELEMENTSOF(dns_types); i++)
533 if (dns_types[i].type == type)
534 return dns_types[i].name;
322345fd
LP
535
536 return NULL;
537}
2d4c5cbc
LP
538
539int dns_type_from_string(const char *s, uint16_t *type) {
540 unsigned i;
541
542 assert(s);
543 assert(type);
544
545 for (i = 0; i < ELEMENTSOF(dns_types); i++)
546 if (strcaseeq(dns_types[i].name, s)) {
547 *type = dns_types[i].type;
548 return 0;
549 }
550
551 return -EINVAL;
552}