chiark / gitweb /
resolved: don't attempt to order empty answer array
[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 (!IN_SET(rr->key->type, DNS_TYPE_A, DNS_TYPE_AAAA))
241                         free(rr->generic.data);
242
243                 dns_resource_key_unref(rr->key);
244         }
245
246         free(rr);
247
248         return NULL;
249 }
250
251 int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) {
252         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
253         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
254         _cleanup_free_ char *ptr = NULL;
255         int r;
256
257         assert(ret);
258         assert(address);
259         assert(hostname);
260
261         r = dns_name_reverse(family, address, &ptr);
262         if (r < 0)
263                 return r;
264
265         key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, ptr);
266         if (!key)
267                 return -ENOMEM;
268
269         ptr = NULL;
270
271         rr = dns_resource_record_new(key);
272         if (!rr)
273                 return -ENOMEM;
274
275         rr->ptr.name = strdup(hostname);
276         if (!rr->ptr.name)
277                 return -ENOMEM;
278
279         *ret = rr;
280         rr = NULL;
281
282         return 0;
283 }
284
285 int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
286         int r;
287
288         assert(a);
289         assert(b);
290
291         r = dns_resource_key_equal(a->key, b->key);
292         if (r <= 0)
293                 return r;
294
295         switch (a->key->type) {
296
297         case DNS_TYPE_PTR:
298         case DNS_TYPE_NS:
299         case DNS_TYPE_CNAME:
300                 return dns_name_equal(a->ptr.name, b->ptr.name);
301
302         case DNS_TYPE_HINFO:
303                 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
304                        strcaseeq(a->hinfo.os, b->hinfo.os);
305
306         case DNS_TYPE_A:
307                 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
308
309         case DNS_TYPE_AAAA:
310                 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
311
312         case DNS_TYPE_SOA:
313                 r = dns_name_equal(a->soa.mname, b->soa.mname);
314                 if (r <= 0)
315                         return r;
316                 r = dns_name_equal(a->soa.rname, b->soa.rname);
317                 if (r <= 0)
318                         return r;
319
320                 return a->soa.serial  == b->soa.serial &&
321                        a->soa.refresh == b->soa.refresh &&
322                        a->soa.retry   == b->soa.retry &&
323                        a->soa.expire  == b->soa.expire &&
324                        a->soa.minimum == b->soa.minimum;
325         default:
326                 return a->generic.size == b->generic.size &&
327                         memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
328         }
329 }
330
331 int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
332         _cleanup_free_ char *k = NULL;
333         char *s;
334         int r;
335
336         assert(rr);
337
338         r = dns_resource_key_to_string(rr->key, &k);
339         if (r < 0)
340                 return r;
341
342         switch (rr->key->type) {
343
344         case DNS_TYPE_PTR:
345         case DNS_TYPE_NS:
346         case DNS_TYPE_CNAME:
347                 s = strjoin(k, " ", rr->ptr.name, NULL);
348                 if (!s)
349                         return -ENOMEM;
350
351                 break;
352
353         case DNS_TYPE_HINFO:
354                 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
355                 if (!s)
356                         return -ENOMEM;
357                 break;
358
359         case DNS_TYPE_A: {
360                 _cleanup_free_ char *x = NULL;
361
362                 r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
363                 if (r < 0)
364                         return r;
365
366                 s = strjoin(k, " ", x, NULL);
367                 if (!s)
368                         return -ENOMEM;
369                 break;
370         }
371
372         case DNS_TYPE_AAAA: {
373                 _cleanup_free_ char *x = NULL;
374
375                 r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &x);
376                 if (r < 0)
377                         return r;
378
379                 s = strjoin(k, " ", x, NULL);
380                 if (!s)
381                         return -ENOMEM;
382                 break;
383         }
384
385         case DNS_TYPE_SOA:
386                 r = asprintf(&s, "%s %s %s %u %u %u %u %u",
387                              k,
388                              strna(rr->soa.mname),
389                              strna(rr->soa.rname),
390                              rr->soa.serial,
391                              rr->soa.refresh,
392                              rr->soa.retry,
393                              rr->soa.expire,
394                              rr->soa.minimum);
395                 if (r < 0)
396                         return -ENOMEM;
397                 break;
398
399         default: {
400                 _cleanup_free_ char *x = NULL;
401
402                 x = hexmem(rr->generic.data, rr->generic.size);
403                 if (!x)
404                         return -ENOMEM;
405
406                 s = strjoin(k, " ", x, NULL);
407                 if (!s)
408                         return -ENOMEM;
409                 break;
410         }}
411
412         *ret = s;
413         return 0;
414 }
415
416 const char *dns_class_to_string(uint16_t class) {
417
418         switch (class) {
419
420         case DNS_CLASS_IN:
421                 return "IN";
422
423         case DNS_CLASS_ANY:
424                 return "ANY";
425         }
426
427         return NULL;
428 }
429
430 int dns_class_from_string(const char *s, uint16_t *class) {
431         assert(s);
432         assert(class);
433
434         if (strcaseeq(s, "IN"))
435                 *class = DNS_CLASS_IN;
436         else if (strcaseeq(s, "ANY"))
437                 *class = DNS_TYPE_ANY;
438         else
439                 return -EINVAL;
440
441         return 0;
442 }
443
444 static const struct {
445         uint16_t type;
446         const char *name;
447 } dns_types[] = {
448         { DNS_TYPE_A,     "A"     },
449         { DNS_TYPE_NS,    "NS"    },
450         { DNS_TYPE_CNAME, "CNAME" },
451         { DNS_TYPE_SOA,   "SOA"   },
452         { DNS_TYPE_PTR,   "PTR"   },
453         { DNS_TYPE_HINFO, "HINFO" },
454         { DNS_TYPE_MX,    "MX"    },
455         { DNS_TYPE_TXT,   "TXT"   },
456         { DNS_TYPE_AAAA,  "AAAA"  },
457         { DNS_TYPE_SRV,   "SRV"   },
458         { DNS_TYPE_SSHFP, "SSHFP" },
459         { DNS_TYPE_DNAME, "DNAME" },
460         { DNS_TYPE_ANY,   "ANY"   },
461         { DNS_TYPE_OPT,   "OPT"   },
462         { DNS_TYPE_TKEY,  "TKEY"  },
463         { DNS_TYPE_TSIG,  "TSIG"  },
464         { DNS_TYPE_IXFR,  "IXFR"  },
465         { DNS_TYPE_AXFR,  "AXFR"  },
466 };
467
468
469 const char *dns_type_to_string(uint16_t type) {
470         unsigned i;
471
472         for (i = 0; i < ELEMENTSOF(dns_types); i++)
473                 if (dns_types[i].type == type)
474                         return dns_types[i].name;
475
476         return NULL;
477 }
478
479 int dns_type_from_string(const char *s, uint16_t *type) {
480         unsigned i;
481
482         assert(s);
483         assert(type);
484
485         for (i = 0; i < ELEMENTSOF(dns_types); i++)
486                 if (strcaseeq(dns_types[i].name, s)) {
487                         *type = dns_types[i].type;
488                         return 0;
489                 }
490
491         return -EINVAL;
492 }