chiark / gitweb /
ada7333a69209988e183e90cfe8b28f1b954b5c9
[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 <math.h>
23
24 #include "strv.h"
25
26 #include "resolved-dns-domain.h"
27 #include "resolved-dns-rr.h"
28 #include "resolved-dns-packet.h"
29 #include "dns-type.h"
30
31 DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) {
32         DnsResourceKey *k;
33         size_t l;
34
35         assert(name);
36
37         l = strlen(name);
38         k = malloc0(sizeof(DnsResourceKey) + l + 1);
39         if (!k)
40                 return NULL;
41
42         k->n_ref = 1;
43         k->class = class;
44         k->type = type;
45
46         strcpy((char*) k + sizeof(DnsResourceKey), name);
47
48         return k;
49 }
50
51 DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name) {
52         DnsResourceKey *k;
53
54         assert(name);
55
56         k = new0(DnsResourceKey, 1);
57         if (!k)
58                 return NULL;
59
60         k->n_ref = 1;
61         k->class = class;
62         k->type = type;
63         k->_name = name;
64
65         return k;
66 }
67
68 DnsResourceKey* dns_resource_key_ref(DnsResourceKey *k) {
69
70         if (!k)
71                 return NULL;
72
73         assert(k->n_ref > 0);
74         k->n_ref++;
75
76         return k;
77 }
78
79 DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) {
80         if (!k)
81                 return NULL;
82
83         assert(k->n_ref > 0);
84
85         if (k->n_ref == 1) {
86                 free(k->_name);
87                 free(k);
88         } else
89                 k->n_ref--;
90
91         return NULL;
92 }
93
94 int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
95         int r;
96
97         r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b));
98         if (r <= 0)
99                 return r;
100
101         if (a->class != b->class)
102                 return 0;
103
104         if (a->type != b->type)
105                 return 0;
106
107         return 1;
108 }
109
110 int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr) {
111         assert(key);
112         assert(rr);
113
114         if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
115                 return 0;
116
117         if (rr->key->type != key->type && key->type != DNS_TYPE_ANY)
118                 return 0;
119
120         return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
121 }
122
123 int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr) {
124         assert(key);
125         assert(rr);
126
127         if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
128                 return 0;
129
130         if (rr->key->type != DNS_TYPE_CNAME)
131                 return 0;
132
133         return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
134 }
135
136 unsigned long dns_resource_key_hash_func(const void *i, const uint8_t hash_key[HASH_KEY_SIZE]) {
137         const DnsResourceKey *k = i;
138         unsigned long ul;
139
140         ul = dns_name_hash_func(DNS_RESOURCE_KEY_NAME(k), hash_key);
141         ul = ul * hash_key[0] + ul + k->class;
142         ul = ul * hash_key[1] + ul + k->type;
143
144         return ul;
145 }
146
147 int dns_resource_key_compare_func(const void *a, const void *b) {
148         const DnsResourceKey *x = a, *y = b;
149         int ret;
150
151         ret = dns_name_compare_func(DNS_RESOURCE_KEY_NAME(x), DNS_RESOURCE_KEY_NAME(y));
152         if (ret != 0)
153                 return ret;
154
155         if (x->type < y->type)
156                 return -1;
157         if (x->type > y->type)
158                 return 1;
159
160         if (x->class < y->class)
161                 return -1;
162         if (x->class > y->class)
163                 return 1;
164
165         return 0;
166 }
167
168 int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) {
169         char cbuf[DECIMAL_STR_MAX(uint16_t)], tbuf[DECIMAL_STR_MAX(uint16_t)];
170         const char *c, *t;
171         char *s;
172
173         c = dns_class_to_string(key->class);
174         if (!c) {
175                 sprintf(cbuf, "%i", key->class);
176                 c = cbuf;
177         }
178
179         t = dns_type_to_string(key->type);
180         if (!t){
181                 sprintf(tbuf, "%i", key->type);
182                 t = tbuf;
183         }
184
185         if (asprintf(&s, "%s %s %-5s", DNS_RESOURCE_KEY_NAME(key), c, t) < 0)
186                 return -ENOMEM;
187
188         *ret = s;
189         return 0;
190 }
191
192 DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
193         DnsResourceRecord *rr;
194
195         rr = new0(DnsResourceRecord, 1);
196         if (!rr)
197                 return NULL;
198
199         rr->n_ref = 1;
200         rr->key = dns_resource_key_ref(key);
201
202         return rr;
203 }
204
205 DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name) {
206         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
207
208         key = dns_resource_key_new(class, type, name);
209         if (!key)
210                 return NULL;
211
212         return dns_resource_record_new(key);
213 }
214
215 DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr) {
216         if (!rr)
217                 return NULL;
218
219         assert(rr->n_ref > 0);
220         rr->n_ref++;
221
222         return rr;
223 }
224
225 DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
226         if (!rr)
227                 return NULL;
228
229         assert(rr->n_ref > 0);
230
231         if (rr->n_ref > 1) {
232                 rr->n_ref--;
233                 return NULL;
234         }
235
236         if (rr->key) {
237                 switch(rr->key->type) {
238
239                 case DNS_TYPE_SRV:
240                         free(rr->srv.name);
241                         break;
242
243                 case DNS_TYPE_PTR:
244                 case DNS_TYPE_NS:
245                 case DNS_TYPE_CNAME:
246                 case DNS_TYPE_DNAME:
247                         free(rr->ptr.name);
248                         break;
249
250                 case DNS_TYPE_HINFO:
251                         free(rr->hinfo.cpu);
252                         free(rr->hinfo.os);
253                         break;
254
255                 case DNS_TYPE_TXT:
256                 case DNS_TYPE_SPF:
257                         strv_free(rr->txt.strings);
258                         break;
259
260                 case DNS_TYPE_SOA:
261                         free(rr->soa.mname);
262                         free(rr->soa.rname);
263                         break;
264
265                 case DNS_TYPE_MX:
266                         free(rr->mx.exchange);
267                         break;
268
269                 case DNS_TYPE_SSHFP:
270                         free(rr->sshfp.key);
271                         break;
272
273                 case DNS_TYPE_DNSKEY:
274                         free(rr->dnskey.key);
275                         break;
276
277                 case DNS_TYPE_LOC:
278                 case DNS_TYPE_A:
279                 case DNS_TYPE_AAAA:
280                         break;
281
282                 default:
283                         free(rr->generic.data);
284                 }
285
286                 dns_resource_key_unref(rr->key);
287         }
288
289         free(rr);
290
291         return NULL;
292 }
293
294 int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) {
295         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
296         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
297         _cleanup_free_ char *ptr = NULL;
298         int r;
299
300         assert(ret);
301         assert(address);
302         assert(hostname);
303
304         r = dns_name_reverse(family, address, &ptr);
305         if (r < 0)
306                 return r;
307
308         key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, ptr);
309         if (!key)
310                 return -ENOMEM;
311
312         ptr = NULL;
313
314         rr = dns_resource_record_new(key);
315         if (!rr)
316                 return -ENOMEM;
317
318         rr->ptr.name = strdup(hostname);
319         if (!rr->ptr.name)
320                 return -ENOMEM;
321
322         *ret = rr;
323         rr = NULL;
324
325         return 0;
326 }
327
328 int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
329         int r;
330
331         assert(a);
332         assert(b);
333
334         r = dns_resource_key_equal(a->key, b->key);
335         if (r <= 0)
336                 return r;
337
338         if (a->unparseable != b->unparseable)
339                 return 0;
340
341         switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) {
342
343         case DNS_TYPE_SRV:
344                 r = dns_name_equal(a->srv.name, b->srv.name);
345                 if (r <= 0)
346                         return r;
347
348                 return a->srv.priority == b->srv.priority &&
349                        a->srv.weight == b->srv.weight &&
350                        a->srv.port == b->srv.port;
351
352         case DNS_TYPE_PTR:
353         case DNS_TYPE_NS:
354         case DNS_TYPE_CNAME:
355         case DNS_TYPE_DNAME:
356                 return dns_name_equal(a->ptr.name, b->ptr.name);
357
358         case DNS_TYPE_HINFO:
359                 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
360                        strcaseeq(a->hinfo.os, b->hinfo.os);
361
362         case DNS_TYPE_SPF: /* exactly the same as TXT */
363         case DNS_TYPE_TXT: {
364                 int i;
365
366                 for (i = 0; a->txt.strings[i] || b->txt.strings[i]; i++)
367                         if (!streq_ptr(a->txt.strings[i], b->txt.strings[i]))
368                                 return false;
369                 return true;
370         }
371
372         case DNS_TYPE_A:
373                 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
374
375         case DNS_TYPE_AAAA:
376                 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
377
378         case DNS_TYPE_SOA:
379                 r = dns_name_equal(a->soa.mname, b->soa.mname);
380                 if (r <= 0)
381                         return r;
382                 r = dns_name_equal(a->soa.rname, b->soa.rname);
383                 if (r <= 0)
384                         return r;
385
386                 return a->soa.serial  == b->soa.serial &&
387                        a->soa.refresh == b->soa.refresh &&
388                        a->soa.retry   == b->soa.retry &&
389                        a->soa.expire  == b->soa.expire &&
390                        a->soa.minimum == b->soa.minimum;
391
392         case DNS_TYPE_MX:
393                 if (a->mx.priority != b->mx.priority)
394                         return 0;
395
396                 return dns_name_equal(a->mx.exchange, b->mx.exchange);
397
398         case DNS_TYPE_LOC:
399                 assert(a->loc.version == b->loc.version);
400
401                 return a->loc.size == b->loc.size &&
402                        a->loc.horiz_pre == b->loc.horiz_pre &&
403                        a->loc.vert_pre == b->loc.vert_pre &&
404                        a->loc.latitude == b->loc.latitude &&
405                        a->loc.longitude == b->loc.longitude &&
406                        a->loc.altitude == b->loc.altitude;
407
408         case DNS_TYPE_SSHFP:
409                 return a->sshfp.algorithm == b->sshfp.algorithm &&
410                        a->sshfp.fptype == b->sshfp.fptype &&
411                        a->sshfp.key_size == b->sshfp.key_size &&
412                        memcmp(a->sshfp.key, b->sshfp.key, a->sshfp.key_size) == 0;
413
414         case DNS_TYPE_DNSKEY:
415                 return a->dnskey.zone_key_flag == b->dnskey.zone_key_flag &&
416                        a->dnskey.sep_flag == b->dnskey.sep_flag &&
417                        a->dnskey.algorithm == b->dnskey.algorithm &&
418                        a->dnskey.key_size == b->dnskey.key_size &&
419                        memcmp(a->dnskey.key, b->dnskey.key, a->dnskey.key_size) == 0;
420
421         default:
422                 return a->generic.size == b->generic.size &&
423                         memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
424         }
425 }
426
427 static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t altitude,
428                              uint8_t size, uint8_t horiz_pre, uint8_t vert_pre) {
429         char *s;
430         char NS = latitude >= 1U<<31 ? 'N' : 'S';
431         char EW = longitude >= 1U<<31 ? 'E' : 'W';
432
433         int lat = latitude >= 1U<<31 ? (int) (latitude - (1U<<31)) : (int) ((1U<<31) - latitude);
434         int lon = longitude >= 1U<<31 ? (int) (longitude - (1U<<31)) : (int) ((1U<<31) - longitude);
435         double alt = altitude >= 10000000u ? altitude - 10000000u : -(double)(10000000u - altitude);
436         double siz = (size >> 4) * exp10((double) (size & 0xF));
437         double hor = (horiz_pre >> 4) * exp10((double) (horiz_pre & 0xF));
438         double ver = (vert_pre >> 4) * exp10((double) (vert_pre & 0xF));
439
440         if (asprintf(&s, "%d %d %.3f %c %d %d %.3f %c %.2fm %.2fm %.2fm %.2fm",
441                      (lat / 60000 / 60),
442                      (lat / 60000) % 60,
443                      (lat % 60000) / 1000.,
444                      NS,
445                      (lon / 60000 / 60),
446                      (lon / 60000) % 60,
447                      (lon % 60000) / 1000.,
448                      EW,
449                      alt / 100.,
450                      siz / 100.,
451                      hor / 100.,
452                      ver / 100.) < 0)
453                 return NULL;
454
455         return s;
456 }
457
458 int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
459         _cleanup_free_ char *k = NULL, *t = NULL;
460         char *s;
461         int r;
462
463         assert(rr);
464
465         r = dns_resource_key_to_string(rr->key, &k);
466         if (r < 0)
467                 return r;
468
469         switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
470
471         case DNS_TYPE_SRV:
472                 r = asprintf(&s, "%s %u %u %u %s",
473                              k,
474                              rr->srv.priority,
475                              rr->srv.weight,
476                              rr->srv.port,
477                              strna(rr->srv.name));
478                 if (r < 0)
479                         return -ENOMEM;
480                 break;
481
482         case DNS_TYPE_PTR:
483         case DNS_TYPE_NS:
484         case DNS_TYPE_CNAME:
485         case DNS_TYPE_DNAME:
486                 s = strjoin(k, " ", rr->ptr.name, NULL);
487                 if (!s)
488                         return -ENOMEM;
489
490                 break;
491
492         case DNS_TYPE_HINFO:
493                 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
494                 if (!s)
495                         return -ENOMEM;
496                 break;
497
498         case DNS_TYPE_SPF: /* exactly the same as TXT */
499         case DNS_TYPE_TXT:
500                 t = strv_join_quoted(rr->txt.strings);
501                 if (!t)
502                         return -ENOMEM;
503
504                 s = strjoin(k, " ", t, NULL);
505                 if (!s)
506                         return -ENOMEM;
507
508                 break;
509
510         case DNS_TYPE_A: {
511                 _cleanup_free_ char *x = NULL;
512
513                 r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
514                 if (r < 0)
515                         return r;
516
517                 s = strjoin(k, " ", x, NULL);
518                 if (!s)
519                         return -ENOMEM;
520                 break;
521         }
522
523         case DNS_TYPE_AAAA:
524                 r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &t);
525                 if (r < 0)
526                         return r;
527
528                 s = strjoin(k, " ", t, NULL);
529                 if (!s)
530                         return -ENOMEM;
531                 break;
532
533         case DNS_TYPE_SOA:
534                 r = asprintf(&s, "%s %s %s %u %u %u %u %u",
535                              k,
536                              strna(rr->soa.mname),
537                              strna(rr->soa.rname),
538                              rr->soa.serial,
539                              rr->soa.refresh,
540                              rr->soa.retry,
541                              rr->soa.expire,
542                              rr->soa.minimum);
543                 if (r < 0)
544                         return -ENOMEM;
545                 break;
546
547         case DNS_TYPE_MX:
548                 r = asprintf(&s, "%s %u %s",
549                              k,
550                              rr->mx.priority,
551                              rr->mx.exchange);
552                 if (r < 0)
553                         return -ENOMEM;
554                 break;
555
556         case DNS_TYPE_LOC:
557                 assert(rr->loc.version == 0);
558
559                 t = format_location(rr->loc.latitude,
560                                     rr->loc.longitude,
561                                     rr->loc.altitude,
562                                     rr->loc.size,
563                                     rr->loc.horiz_pre,
564                                     rr->loc.vert_pre);
565                 if (!t)
566                         return -ENOMEM;
567
568                 s = strjoin(k, " ", t, NULL);
569                 if (!s)
570                         return -ENOMEM;
571                 break;
572
573         case DNS_TYPE_SSHFP:
574                 t = hexmem(rr->sshfp.key, rr->sshfp.key_size);
575                 if (!t)
576                         return -ENOMEM;
577
578                 r = asprintf(&s, "%s %u %u %s",
579                              k,
580                              rr->sshfp.algorithm,
581                              rr->sshfp.fptype,
582                              t);
583                 if (r < 0)
584                         return -ENOMEM;
585                 break;
586
587         case DNS_TYPE_DNSKEY:
588                 t = hexmem(rr->dnskey.key, rr->dnskey.key_size);
589                 if (!t)
590                         return -ENOMEM;
591
592                 r = asprintf(&s, "%s %u 3 %u %s",
593                              k,
594                              dnskey_to_flags(rr),
595                              rr->dnskey.algorithm,
596                              t);
597                 if (r < 0)
598                         return -ENOMEM;
599                 break;
600
601         default:
602                 t = hexmem(rr->generic.data, rr->generic.size);
603                 if (!t)
604                         return -ENOMEM;
605
606                 s = strjoin(k, " ", t, NULL);
607                 if (!s)
608                         return -ENOMEM;
609                 break;
610         }
611
612         *ret = s;
613         return 0;
614 }
615
616 const char *dns_class_to_string(uint16_t class) {
617
618         switch (class) {
619
620         case DNS_CLASS_IN:
621                 return "IN";
622
623         case DNS_CLASS_ANY:
624                 return "ANY";
625         }
626
627         return NULL;
628 }
629
630 int dns_class_from_string(const char *s, uint16_t *class) {
631         assert(s);
632         assert(class);
633
634         if (strcaseeq(s, "IN"))
635                 *class = DNS_CLASS_IN;
636         else if (strcaseeq(s, "ANY"))
637                 *class = DNS_TYPE_ANY;
638         else
639                 return -EINVAL;
640
641         return 0;
642 }