chiark / gitweb /
1ee93fe8c5df64b9a8d1eca6188e73232cabe61e
[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                 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
270 int 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
304 int 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
366 int 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
475 const 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
489 int 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
503 static 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
529 const 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
539 int 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 }