chiark / gitweb /
resolved: TXT records
[elogind.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 }