]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/resolve/resolved-dns-rr.c
resolved: SPF records
[thirdparty/systemd.git] / src / resolve / resolved-dns-rr.c
... / ...
CommitLineData
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 "strv.h"
23
24#include "resolved-dns-domain.h"
25#include "resolved-dns-rr.h"
26
27DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) {
28 DnsResourceKey *k;
29 size_t l;
30
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));
130}
131
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
136 ul = dns_name_hash_func(DNS_RESOURCE_KEY_NAME(k), hash_key);
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
147 ret = dns_name_compare_func(DNS_RESOURCE_KEY_NAME(x), DNS_RESOURCE_KEY_NAME(y));
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
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
189DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
190 DnsResourceRecord *rr;
191
192 rr = new0(DnsResourceRecord, 1);
193 if (!rr)
194 return NULL;
195
196 rr->n_ref = 1;
197 rr->key = dns_resource_key_ref(key);
198
199 return rr;
200}
201
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
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
233 if (rr->key) {
234 switch(rr->key->type) {
235 case DNS_TYPE_PTR:
236 case DNS_TYPE_NS:
237 case DNS_TYPE_CNAME:
238 free(rr->ptr.name);
239 break;
240 case DNS_TYPE_HINFO:
241 free(rr->hinfo.cpu);
242 free(rr->hinfo.os);
243 break;
244 case DNS_TYPE_SPF:
245 case DNS_TYPE_TXT:
246 strv_free(rr->txt.strings);
247 break;
248 case DNS_TYPE_SOA:
249 free(rr->soa.mname);
250 free(rr->soa.rname);
251 break;
252 case DNS_TYPE_MX:
253 free(rr->mx.exchange);
254 break;
255 case DNS_TYPE_A:
256 case DNS_TYPE_AAAA:
257 break;
258 default:
259 free(rr->generic.data);
260 }
261
262 dns_resource_key_unref(rr->key);
263 }
264
265 free(rr);
266
267 return NULL;
268}
269
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
304int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
305 int r;
306
307 assert(a);
308 assert(b);
309
310 r = dns_resource_key_equal(a->key, b->key);
311 if (r <= 0)
312 return r;
313
314 switch (a->key->type) {
315
316 case DNS_TYPE_PTR:
317 case DNS_TYPE_NS:
318 case DNS_TYPE_CNAME:
319 return dns_name_equal(a->ptr.name, b->ptr.name);
320
321 case DNS_TYPE_HINFO:
322 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
323 strcaseeq(a->hinfo.os, b->hinfo.os);
324
325 case DNS_TYPE_SPF: /* exactly the same as TXT */
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
335 case DNS_TYPE_A:
336 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
337
338 case DNS_TYPE_AAAA:
339 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
340
341 case DNS_TYPE_SOA:
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;
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
360 default:
361 return a->generic.size == b->generic.size &&
362 memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
363 }
364}
365
366int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
367 _cleanup_free_ char *k = NULL;
368 char *s;
369 int r;
370
371 assert(rr);
372
373 r = dns_resource_key_to_string(rr->key, &k);
374 if (r < 0)
375 return r;
376
377 switch (rr->key->type) {
378
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;
385
386 break;
387
388 case DNS_TYPE_HINFO:
389 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
390 if (!s)
391 return -ENOMEM;
392 break;
393
394 case DNS_TYPE_SPF: /* exactly the same as TXT */
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
409 case DNS_TYPE_A: {
410 _cleanup_free_ char *x = NULL;
411
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;
415
416 s = strjoin(k, " ", x, NULL);
417 if (!s)
418 return -ENOMEM;
419 break;
420 }
421
422 case DNS_TYPE_AAAA: {
423 _cleanup_free_ char *x = NULL;
424
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;
428
429 s = strjoin(k, " ", x, NULL);
430 if (!s)
431 return -ENOMEM;
432 break;
433 }
434
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
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
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}
474
475const char *dns_class_to_string(uint16_t class) {
476
477 switch (class) {
478
479 case DNS_CLASS_IN:
480 return "IN";
481
482 case DNS_CLASS_ANY:
483 return "ANY";
484 }
485
486 return NULL;
487}
488
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;
499
500 return 0;
501}
502
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" },
518 { DNS_TYPE_SPF, "SPF" },
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};
527
528
529const char *dns_type_to_string(uint16_t type) {
530 unsigned i;
531
532 for (i = 0; i < ELEMENTSOF(dns_types); i++)
533 if (dns_types[i].type == type)
534 return dns_types[i].name;
535
536 return NULL;
537}
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}