]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-rr.c
resolved: TXT records
[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 "strv.h"
23
24 #include "resolved-dns-domain.h"
25 #include "resolved-dns-rr.h"
26
27 DnsResourceKey* 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
47 DnsResourceKey* 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
64 DnsResourceKey* 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
75 DnsResourceKey* 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
90 int 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
106 int 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
119 int 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
132 unsigned 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
143 int 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
164 int 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
189 DnsResourceRecord* 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
202 DnsResourceRecord* 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
212 DnsResourceRecord* 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
222 DnsResourceRecord* 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 if (IN_SET(rr->key->type, DNS_TYPE_PTR, DNS_TYPE_NS, DNS_TYPE_CNAME))
235 free(rr->ptr.name);
236 else if (rr->key->type == DNS_TYPE_HINFO) {
237 free(rr->hinfo.cpu);
238 free(rr->hinfo.os);
239 } else if (rr->key->type == DNS_TYPE_TXT) {
240 strv_free(rr->txt.strings);
241 } else if (rr->key->type == DNS_TYPE_SOA) {
242 free(rr->soa.mname);
243 free(rr->soa.rname);
244 } else if (rr->key->type == DNS_TYPE_MX) {
245 free(rr->mx.exchange);
246 } else if (!IN_SET(rr->key->type, DNS_TYPE_A, DNS_TYPE_AAAA))
247 free(rr->generic.data);
248
249 dns_resource_key_unref(rr->key);
250 }
251
252 free(rr);
253
254 return NULL;
255 }
256
257 int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) {
258 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
259 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
260 _cleanup_free_ char *ptr = NULL;
261 int r;
262
263 assert(ret);
264 assert(address);
265 assert(hostname);
266
267 r = dns_name_reverse(family, address, &ptr);
268 if (r < 0)
269 return r;
270
271 key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, ptr);
272 if (!key)
273 return -ENOMEM;
274
275 ptr = NULL;
276
277 rr = dns_resource_record_new(key);
278 if (!rr)
279 return -ENOMEM;
280
281 rr->ptr.name = strdup(hostname);
282 if (!rr->ptr.name)
283 return -ENOMEM;
284
285 *ret = rr;
286 rr = NULL;
287
288 return 0;
289 }
290
291 int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
292 int r;
293
294 assert(a);
295 assert(b);
296
297 r = dns_resource_key_equal(a->key, b->key);
298 if (r <= 0)
299 return r;
300
301 switch (a->key->type) {
302
303 case DNS_TYPE_PTR:
304 case DNS_TYPE_NS:
305 case DNS_TYPE_CNAME:
306 return dns_name_equal(a->ptr.name, b->ptr.name);
307
308 case DNS_TYPE_HINFO:
309 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
310 strcaseeq(a->hinfo.os, b->hinfo.os);
311
312 case DNS_TYPE_TXT: {
313 int i;
314
315 for (i = 0; a->txt.strings[i] || b->txt.strings[i]; i++)
316 if (!streq_ptr(a->txt.strings[i], b->txt.strings[i]))
317 return false;
318 return true;
319 }
320
321 case DNS_TYPE_A:
322 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
323
324 case DNS_TYPE_AAAA:
325 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
326
327 case DNS_TYPE_SOA:
328 r = dns_name_equal(a->soa.mname, b->soa.mname);
329 if (r <= 0)
330 return r;
331 r = dns_name_equal(a->soa.rname, b->soa.rname);
332 if (r <= 0)
333 return r;
334
335 return a->soa.serial == b->soa.serial &&
336 a->soa.refresh == b->soa.refresh &&
337 a->soa.retry == b->soa.retry &&
338 a->soa.expire == b->soa.expire &&
339 a->soa.minimum == b->soa.minimum;
340 case DNS_TYPE_MX:
341 if (a->mx.priority != b->mx.priority)
342 return 0;
343
344 return dns_name_equal(a->mx.exchange, b->mx.exchange);
345
346 default:
347 return a->generic.size == b->generic.size &&
348 memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
349 }
350 }
351
352 int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
353 _cleanup_free_ char *k = NULL;
354 char *s;
355 int r;
356
357 assert(rr);
358
359 r = dns_resource_key_to_string(rr->key, &k);
360 if (r < 0)
361 return r;
362
363 switch (rr->key->type) {
364
365 case DNS_TYPE_PTR:
366 case DNS_TYPE_NS:
367 case DNS_TYPE_CNAME:
368 s = strjoin(k, " ", rr->ptr.name, NULL);
369 if (!s)
370 return -ENOMEM;
371
372 break;
373
374 case DNS_TYPE_HINFO:
375 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
376 if (!s)
377 return -ENOMEM;
378 break;
379
380 case DNS_TYPE_TXT: {
381 _cleanup_free_ char *t;
382
383 t = strv_join_quoted(rr->txt.strings);
384 if (!t)
385 return -ENOMEM;
386
387 s = strjoin(k, " ", t, NULL);
388 if (!s)
389 return -ENOMEM;
390
391 break;
392 }
393
394 case DNS_TYPE_A: {
395 _cleanup_free_ char *x = NULL;
396
397 r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
398 if (r < 0)
399 return r;
400
401 s = strjoin(k, " ", x, NULL);
402 if (!s)
403 return -ENOMEM;
404 break;
405 }
406
407 case DNS_TYPE_AAAA: {
408 _cleanup_free_ char *x = NULL;
409
410 r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &x);
411 if (r < 0)
412 return r;
413
414 s = strjoin(k, " ", x, NULL);
415 if (!s)
416 return -ENOMEM;
417 break;
418 }
419
420 case DNS_TYPE_SOA:
421 r = asprintf(&s, "%s %s %s %u %u %u %u %u",
422 k,
423 strna(rr->soa.mname),
424 strna(rr->soa.rname),
425 rr->soa.serial,
426 rr->soa.refresh,
427 rr->soa.retry,
428 rr->soa.expire,
429 rr->soa.minimum);
430 if (r < 0)
431 return -ENOMEM;
432 break;
433
434 case DNS_TYPE_MX:
435 r = asprintf(&s, "%s %u %s",
436 k,
437 rr->mx.priority,
438 rr->mx.exchange);
439 if (r < 0)
440 return -ENOMEM;
441 break;
442
443 default: {
444 _cleanup_free_ char *x = NULL;
445
446 x = hexmem(rr->generic.data, rr->generic.size);
447 if (!x)
448 return -ENOMEM;
449
450 s = strjoin(k, " ", x, NULL);
451 if (!s)
452 return -ENOMEM;
453 break;
454 }}
455
456 *ret = s;
457 return 0;
458 }
459
460 const char *dns_class_to_string(uint16_t class) {
461
462 switch (class) {
463
464 case DNS_CLASS_IN:
465 return "IN";
466
467 case DNS_CLASS_ANY:
468 return "ANY";
469 }
470
471 return NULL;
472 }
473
474 int dns_class_from_string(const char *s, uint16_t *class) {
475 assert(s);
476 assert(class);
477
478 if (strcaseeq(s, "IN"))
479 *class = DNS_CLASS_IN;
480 else if (strcaseeq(s, "ANY"))
481 *class = DNS_TYPE_ANY;
482 else
483 return -EINVAL;
484
485 return 0;
486 }
487
488 static const struct {
489 uint16_t type;
490 const char *name;
491 } dns_types[] = {
492 { DNS_TYPE_A, "A" },
493 { DNS_TYPE_NS, "NS" },
494 { DNS_TYPE_CNAME, "CNAME" },
495 { DNS_TYPE_SOA, "SOA" },
496 { DNS_TYPE_PTR, "PTR" },
497 { DNS_TYPE_HINFO, "HINFO" },
498 { DNS_TYPE_MX, "MX" },
499 { DNS_TYPE_TXT, "TXT" },
500 { DNS_TYPE_AAAA, "AAAA" },
501 { DNS_TYPE_SRV, "SRV" },
502 { DNS_TYPE_SSHFP, "SSHFP" },
503 { DNS_TYPE_DNAME, "DNAME" },
504 { DNS_TYPE_ANY, "ANY" },
505 { DNS_TYPE_OPT, "OPT" },
506 { DNS_TYPE_TKEY, "TKEY" },
507 { DNS_TYPE_TSIG, "TSIG" },
508 { DNS_TYPE_IXFR, "IXFR" },
509 { DNS_TYPE_AXFR, "AXFR" },
510 };
511
512
513 const char *dns_type_to_string(uint16_t type) {
514 unsigned i;
515
516 for (i = 0; i < ELEMENTSOF(dns_types); i++)
517 if (dns_types[i].type == type)
518 return dns_types[i].name;
519
520 return NULL;
521 }
522
523 int dns_type_from_string(const char *s, uint16_t *type) {
524 unsigned i;
525
526 assert(s);
527 assert(type);
528
529 for (i = 0; i < ELEMENTSOF(dns_types); i++)
530 if (strcaseeq(dns_types[i].name, s)) {
531 *type = dns_types[i].type;
532 return 0;
533 }
534
535 return -EINVAL;
536 }