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