chiark / gitweb /
bus-proxyd: explicitly address messages to unique and well-known name
[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 static 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 static 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 const struct hash_ops dns_resource_key_hash_ops = {
169         .hash = dns_resource_key_hash_func,
170         .compare = dns_resource_key_compare_func
171 };
172
173 int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) {
174         char cbuf[DECIMAL_STR_MAX(uint16_t)], tbuf[DECIMAL_STR_MAX(uint16_t)];
175         const char *c, *t;
176         char *s;
177
178         c = dns_class_to_string(key->class);
179         if (!c) {
180                 sprintf(cbuf, "%i", key->class);
181                 c = cbuf;
182         }
183
184         t = dns_type_to_string(key->type);
185         if (!t){
186                 sprintf(tbuf, "%i", key->type);
187                 t = tbuf;
188         }
189
190         if (asprintf(&s, "%s %s %-5s", DNS_RESOURCE_KEY_NAME(key), c, t) < 0)
191                 return -ENOMEM;
192
193         *ret = s;
194         return 0;
195 }
196
197 DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
198         DnsResourceRecord *rr;
199
200         rr = new0(DnsResourceRecord, 1);
201         if (!rr)
202                 return NULL;
203
204         rr->n_ref = 1;
205         rr->key = dns_resource_key_ref(key);
206
207         return rr;
208 }
209
210 DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name) {
211         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
212
213         key = dns_resource_key_new(class, type, name);
214         if (!key)
215                 return NULL;
216
217         return dns_resource_record_new(key);
218 }
219
220 DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr) {
221         if (!rr)
222                 return NULL;
223
224         assert(rr->n_ref > 0);
225         rr->n_ref++;
226
227         return rr;
228 }
229
230 DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
231         if (!rr)
232                 return NULL;
233
234         assert(rr->n_ref > 0);
235
236         if (rr->n_ref > 1) {
237                 rr->n_ref--;
238                 return NULL;
239         }
240
241         if (rr->key) {
242                 switch(rr->key->type) {
243
244                 case DNS_TYPE_SRV:
245                         free(rr->srv.name);
246                         break;
247
248                 case DNS_TYPE_PTR:
249                 case DNS_TYPE_NS:
250                 case DNS_TYPE_CNAME:
251                 case DNS_TYPE_DNAME:
252                         free(rr->ptr.name);
253                         break;
254
255                 case DNS_TYPE_HINFO:
256                         free(rr->hinfo.cpu);
257                         free(rr->hinfo.os);
258                         break;
259
260                 case DNS_TYPE_TXT:
261                 case DNS_TYPE_SPF:
262                         strv_free(rr->txt.strings);
263                         break;
264
265                 case DNS_TYPE_SOA:
266                         free(rr->soa.mname);
267                         free(rr->soa.rname);
268                         break;
269
270                 case DNS_TYPE_MX:
271                         free(rr->mx.exchange);
272                         break;
273
274                 case DNS_TYPE_SSHFP:
275                         free(rr->sshfp.key);
276                         break;
277
278                 case DNS_TYPE_DNSKEY:
279                         free(rr->dnskey.key);
280                         break;
281
282                 case DNS_TYPE_RRSIG:
283                         free(rr->rrsig.signer);
284                         free(rr->rrsig.signature);
285                         break;
286
287                 case DNS_TYPE_LOC:
288                 case DNS_TYPE_A:
289                 case DNS_TYPE_AAAA:
290                         break;
291
292                 default:
293                         free(rr->generic.data);
294                 }
295
296                 dns_resource_key_unref(rr->key);
297         }
298
299         free(rr);
300
301         return NULL;
302 }
303
304 int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) {
305         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
306         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
307         _cleanup_free_ char *ptr = NULL;
308         int r;
309
310         assert(ret);
311         assert(address);
312         assert(hostname);
313
314         r = dns_name_reverse(family, address, &ptr);
315         if (r < 0)
316                 return r;
317
318         key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, ptr);
319         if (!key)
320                 return -ENOMEM;
321
322         ptr = NULL;
323
324         rr = dns_resource_record_new(key);
325         if (!rr)
326                 return -ENOMEM;
327
328         rr->ptr.name = strdup(hostname);
329         if (!rr->ptr.name)
330                 return -ENOMEM;
331
332         *ret = rr;
333         rr = NULL;
334
335         return 0;
336 }
337
338 int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
339         int r;
340
341         assert(a);
342         assert(b);
343
344         r = dns_resource_key_equal(a->key, b->key);
345         if (r <= 0)
346                 return r;
347
348         if (a->unparseable != b->unparseable)
349                 return 0;
350
351         switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) {
352
353         case DNS_TYPE_SRV:
354                 r = dns_name_equal(a->srv.name, b->srv.name);
355                 if (r <= 0)
356                         return r;
357
358                 return a->srv.priority == b->srv.priority &&
359                        a->srv.weight == b->srv.weight &&
360                        a->srv.port == b->srv.port;
361
362         case DNS_TYPE_PTR:
363         case DNS_TYPE_NS:
364         case DNS_TYPE_CNAME:
365         case DNS_TYPE_DNAME:
366                 return dns_name_equal(a->ptr.name, b->ptr.name);
367
368         case DNS_TYPE_HINFO:
369                 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
370                        strcaseeq(a->hinfo.os, b->hinfo.os);
371
372         case DNS_TYPE_SPF: /* exactly the same as TXT */
373         case DNS_TYPE_TXT: {
374                 int i;
375
376                 for (i = 0; a->txt.strings[i] || b->txt.strings[i]; i++)
377                         if (!streq_ptr(a->txt.strings[i], b->txt.strings[i]))
378                                 return false;
379                 return true;
380         }
381
382         case DNS_TYPE_A:
383                 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
384
385         case DNS_TYPE_AAAA:
386                 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
387
388         case DNS_TYPE_SOA:
389                 r = dns_name_equal(a->soa.mname, b->soa.mname);
390                 if (r <= 0)
391                         return r;
392                 r = dns_name_equal(a->soa.rname, b->soa.rname);
393                 if (r <= 0)
394                         return r;
395
396                 return a->soa.serial  == b->soa.serial &&
397                        a->soa.refresh == b->soa.refresh &&
398                        a->soa.retry   == b->soa.retry &&
399                        a->soa.expire  == b->soa.expire &&
400                        a->soa.minimum == b->soa.minimum;
401
402         case DNS_TYPE_MX:
403                 if (a->mx.priority != b->mx.priority)
404                         return 0;
405
406                 return dns_name_equal(a->mx.exchange, b->mx.exchange);
407
408         case DNS_TYPE_LOC:
409                 assert(a->loc.version == b->loc.version);
410
411                 return a->loc.size == b->loc.size &&
412                        a->loc.horiz_pre == b->loc.horiz_pre &&
413                        a->loc.vert_pre == b->loc.vert_pre &&
414                        a->loc.latitude == b->loc.latitude &&
415                        a->loc.longitude == b->loc.longitude &&
416                        a->loc.altitude == b->loc.altitude;
417
418         case DNS_TYPE_SSHFP:
419                 return a->sshfp.algorithm == b->sshfp.algorithm &&
420                        a->sshfp.fptype == b->sshfp.fptype &&
421                        a->sshfp.key_size == b->sshfp.key_size &&
422                        memcmp(a->sshfp.key, b->sshfp.key, a->sshfp.key_size) == 0;
423
424         case DNS_TYPE_DNSKEY:
425                 return a->dnskey.zone_key_flag == b->dnskey.zone_key_flag &&
426                        a->dnskey.sep_flag == b->dnskey.sep_flag &&
427                        a->dnskey.algorithm == b->dnskey.algorithm &&
428                        a->dnskey.key_size == b->dnskey.key_size &&
429                        memcmp(a->dnskey.key, b->dnskey.key, a->dnskey.key_size) == 0;
430
431         case DNS_TYPE_RRSIG:
432                 /* do the fast comparisons first */
433                 if (a->rrsig.type_covered != b->rrsig.type_covered ||
434                     a->rrsig.algorithm != b->rrsig.algorithm ||
435                     a->rrsig.labels != b->rrsig.labels ||
436                     a->rrsig.original_ttl != b->rrsig.original_ttl ||
437                     a->rrsig.expiration != b->rrsig.expiration ||
438                     a->rrsig.inception != b->rrsig.inception ||
439                     a->rrsig.key_tag != b->rrsig.key_tag ||
440                     a->rrsig.signature_size != b->rrsig.signature_size ||
441                     memcmp(a->rrsig.signature, b->rrsig.signature, a->rrsig.signature_size) != 0)
442                         return false;
443
444                 return dns_name_equal(a->rrsig.signer, b->rrsig.signer);
445
446         default:
447                 return a->generic.size == b->generic.size &&
448                         memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
449         }
450 }
451
452 static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t altitude,
453                              uint8_t size, uint8_t horiz_pre, uint8_t vert_pre) {
454         char *s;
455         char NS = latitude >= 1U<<31 ? 'N' : 'S';
456         char EW = longitude >= 1U<<31 ? 'E' : 'W';
457
458         int lat = latitude >= 1U<<31 ? (int) (latitude - (1U<<31)) : (int) ((1U<<31) - latitude);
459         int lon = longitude >= 1U<<31 ? (int) (longitude - (1U<<31)) : (int) ((1U<<31) - longitude);
460         double alt = altitude >= 10000000u ? altitude - 10000000u : -(double)(10000000u - altitude);
461         double siz = (size >> 4) * exp10((double) (size & 0xF));
462         double hor = (horiz_pre >> 4) * exp10((double) (horiz_pre & 0xF));
463         double ver = (vert_pre >> 4) * exp10((double) (vert_pre & 0xF));
464
465         if (asprintf(&s, "%d %d %.3f %c %d %d %.3f %c %.2fm %.2fm %.2fm %.2fm",
466                      (lat / 60000 / 60),
467                      (lat / 60000) % 60,
468                      (lat % 60000) / 1000.,
469                      NS,
470                      (lon / 60000 / 60),
471                      (lon / 60000) % 60,
472                      (lon % 60000) / 1000.,
473                      EW,
474                      alt / 100.,
475                      siz / 100.,
476                      hor / 100.,
477                      ver / 100.) < 0)
478                 return NULL;
479
480         return s;
481 }
482
483 int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
484         _cleanup_free_ char *k = NULL, *t = NULL;
485         char *s;
486         int r;
487
488         assert(rr);
489
490         r = dns_resource_key_to_string(rr->key, &k);
491         if (r < 0)
492                 return r;
493
494         switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
495
496         case DNS_TYPE_SRV:
497                 r = asprintf(&s, "%s %u %u %u %s",
498                              k,
499                              rr->srv.priority,
500                              rr->srv.weight,
501                              rr->srv.port,
502                              strna(rr->srv.name));
503                 if (r < 0)
504                         return -ENOMEM;
505                 break;
506
507         case DNS_TYPE_PTR:
508         case DNS_TYPE_NS:
509         case DNS_TYPE_CNAME:
510         case DNS_TYPE_DNAME:
511                 s = strjoin(k, " ", rr->ptr.name, NULL);
512                 if (!s)
513                         return -ENOMEM;
514
515                 break;
516
517         case DNS_TYPE_HINFO:
518                 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
519                 if (!s)
520                         return -ENOMEM;
521                 break;
522
523         case DNS_TYPE_SPF: /* exactly the same as TXT */
524         case DNS_TYPE_TXT:
525                 t = strv_join_quoted(rr->txt.strings);
526                 if (!t)
527                         return -ENOMEM;
528
529                 s = strjoin(k, " ", t, NULL);
530                 if (!s)
531                         return -ENOMEM;
532
533                 break;
534
535         case DNS_TYPE_A: {
536                 _cleanup_free_ char *x = NULL;
537
538                 r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
539                 if (r < 0)
540                         return r;
541
542                 s = strjoin(k, " ", x, NULL);
543                 if (!s)
544                         return -ENOMEM;
545                 break;
546         }
547
548         case DNS_TYPE_AAAA:
549                 r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &t);
550                 if (r < 0)
551                         return r;
552
553                 s = strjoin(k, " ", t, NULL);
554                 if (!s)
555                         return -ENOMEM;
556                 break;
557
558         case DNS_TYPE_SOA:
559                 r = asprintf(&s, "%s %s %s %u %u %u %u %u",
560                              k,
561                              strna(rr->soa.mname),
562                              strna(rr->soa.rname),
563                              rr->soa.serial,
564                              rr->soa.refresh,
565                              rr->soa.retry,
566                              rr->soa.expire,
567                              rr->soa.minimum);
568                 if (r < 0)
569                         return -ENOMEM;
570                 break;
571
572         case DNS_TYPE_MX:
573                 r = asprintf(&s, "%s %u %s",
574                              k,
575                              rr->mx.priority,
576                              rr->mx.exchange);
577                 if (r < 0)
578                         return -ENOMEM;
579                 break;
580
581         case DNS_TYPE_LOC:
582                 assert(rr->loc.version == 0);
583
584                 t = format_location(rr->loc.latitude,
585                                     rr->loc.longitude,
586                                     rr->loc.altitude,
587                                     rr->loc.size,
588                                     rr->loc.horiz_pre,
589                                     rr->loc.vert_pre);
590                 if (!t)
591                         return -ENOMEM;
592
593                 s = strjoin(k, " ", t, NULL);
594                 if (!s)
595                         return -ENOMEM;
596                 break;
597
598         case DNS_TYPE_SSHFP:
599                 t = hexmem(rr->sshfp.key, rr->sshfp.key_size);
600                 if (!t)
601                         return -ENOMEM;
602
603                 r = asprintf(&s, "%s %u %u %s",
604                              k,
605                              rr->sshfp.algorithm,
606                              rr->sshfp.fptype,
607                              t);
608                 if (r < 0)
609                         return -ENOMEM;
610                 break;
611
612         case DNS_TYPE_DNSKEY: {
613                 const char *alg;
614
615                 alg = dnssec_algorithm_to_string(rr->dnskey.algorithm);
616
617                 t = hexmem(rr->dnskey.key, rr->dnskey.key_size);
618                 if (!t)
619                         return -ENOMEM;
620
621                 r = asprintf(&s, "%s %u 3 %.*s%.*u %s",
622                              k,
623                              dnskey_to_flags(rr),
624                              alg ? -1 : 0, alg,
625                              alg ? 0 : 1, alg ? 0u : (unsigned) rr->dnskey.algorithm,
626                              t);
627                 if (r < 0)
628                         return -ENOMEM;
629                 break;
630         }
631
632         case DNS_TYPE_RRSIG: {
633                 const char *type, *alg;
634
635                 type = dns_type_to_string(rr->rrsig.type_covered);
636                 alg = dnssec_algorithm_to_string(rr->rrsig.algorithm);
637
638                 t = hexmem(rr->rrsig.signature, rr->rrsig.signature_size);
639                 if (!t)
640                         return -ENOMEM;
641
642                 /* TYPE?? follows
643                  * http://tools.ietf.org/html/rfc3597#section-5 */
644
645                 r = asprintf(&s, "%s %s%.*u %.*s%.*u %u %u %u %u %u %s %s",
646                              k,
647                              type ?: "TYPE",
648                              type ? 0 : 1, type ? 0u : (unsigned) rr->rrsig.type_covered,
649                              alg ? -1 : 0, alg,
650                              alg ? 0 : 1, alg ? 0u : (unsigned) rr->rrsig.algorithm,
651                              rr->rrsig.labels,
652                              rr->rrsig.original_ttl,
653                              rr->rrsig.expiration,
654                              rr->rrsig.inception,
655                              rr->rrsig.key_tag,
656                              rr->rrsig.signer,
657                              t);
658                 if (r < 0)
659                         return -ENOMEM;
660                 break;
661         }
662
663         default:
664                 t = hexmem(rr->generic.data, rr->generic.size);
665                 if (!t)
666                         return -ENOMEM;
667
668                 s = strjoin(k, " ", t, NULL);
669                 if (!s)
670                         return -ENOMEM;
671                 break;
672         }
673
674         *ret = s;
675         return 0;
676 }
677
678 const char *dns_class_to_string(uint16_t class) {
679
680         switch (class) {
681
682         case DNS_CLASS_IN:
683                 return "IN";
684
685         case DNS_CLASS_ANY:
686                 return "ANY";
687         }
688
689         return NULL;
690 }
691
692 int dns_class_from_string(const char *s, uint16_t *class) {
693         assert(s);
694         assert(class);
695
696         if (strcaseeq(s, "IN"))
697                 *class = DNS_CLASS_IN;
698         else if (strcaseeq(s, "ANY"))
699                 *class = DNS_TYPE_ANY;
700         else
701                 return -EINVAL;
702
703         return 0;
704 }