chiark / gitweb /
resolve: set error code on failure
[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                 return strv_equal(a->txt.strings, b->txt.strings);
375
376         case DNS_TYPE_A:
377                 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
378
379         case DNS_TYPE_AAAA:
380                 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
381
382         case DNS_TYPE_SOA:
383                 r = dns_name_equal(a->soa.mname, b->soa.mname);
384                 if (r <= 0)
385                         return r;
386                 r = dns_name_equal(a->soa.rname, b->soa.rname);
387                 if (r <= 0)
388                         return r;
389
390                 return a->soa.serial  == b->soa.serial &&
391                        a->soa.refresh == b->soa.refresh &&
392                        a->soa.retry   == b->soa.retry &&
393                        a->soa.expire  == b->soa.expire &&
394                        a->soa.minimum == b->soa.minimum;
395
396         case DNS_TYPE_MX:
397                 if (a->mx.priority != b->mx.priority)
398                         return 0;
399
400                 return dns_name_equal(a->mx.exchange, b->mx.exchange);
401
402         case DNS_TYPE_LOC:
403                 assert(a->loc.version == b->loc.version);
404
405                 return a->loc.size == b->loc.size &&
406                        a->loc.horiz_pre == b->loc.horiz_pre &&
407                        a->loc.vert_pre == b->loc.vert_pre &&
408                        a->loc.latitude == b->loc.latitude &&
409                        a->loc.longitude == b->loc.longitude &&
410                        a->loc.altitude == b->loc.altitude;
411
412         case DNS_TYPE_SSHFP:
413                 return a->sshfp.algorithm == b->sshfp.algorithm &&
414                        a->sshfp.fptype == b->sshfp.fptype &&
415                        a->sshfp.key_size == b->sshfp.key_size &&
416                        memcmp(a->sshfp.key, b->sshfp.key, a->sshfp.key_size) == 0;
417
418         case DNS_TYPE_DNSKEY:
419                 return a->dnskey.zone_key_flag == b->dnskey.zone_key_flag &&
420                        a->dnskey.sep_flag == b->dnskey.sep_flag &&
421                        a->dnskey.algorithm == b->dnskey.algorithm &&
422                        a->dnskey.key_size == b->dnskey.key_size &&
423                        memcmp(a->dnskey.key, b->dnskey.key, a->dnskey.key_size) == 0;
424
425         case DNS_TYPE_RRSIG:
426                 /* do the fast comparisons first */
427                 if (a->rrsig.type_covered != b->rrsig.type_covered ||
428                     a->rrsig.algorithm != b->rrsig.algorithm ||
429                     a->rrsig.labels != b->rrsig.labels ||
430                     a->rrsig.original_ttl != b->rrsig.original_ttl ||
431                     a->rrsig.expiration != b->rrsig.expiration ||
432                     a->rrsig.inception != b->rrsig.inception ||
433                     a->rrsig.key_tag != b->rrsig.key_tag ||
434                     a->rrsig.signature_size != b->rrsig.signature_size ||
435                     memcmp(a->rrsig.signature, b->rrsig.signature, a->rrsig.signature_size) != 0)
436                         return false;
437
438                 return dns_name_equal(a->rrsig.signer, b->rrsig.signer);
439
440         default:
441                 return a->generic.size == b->generic.size &&
442                         memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
443         }
444 }
445
446 static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t altitude,
447                              uint8_t size, uint8_t horiz_pre, uint8_t vert_pre) {
448         char *s;
449         char NS = latitude >= 1U<<31 ? 'N' : 'S';
450         char EW = longitude >= 1U<<31 ? 'E' : 'W';
451
452         int lat = latitude >= 1U<<31 ? (int) (latitude - (1U<<31)) : (int) ((1U<<31) - latitude);
453         int lon = longitude >= 1U<<31 ? (int) (longitude - (1U<<31)) : (int) ((1U<<31) - longitude);
454         double alt = altitude >= 10000000u ? altitude - 10000000u : -(double)(10000000u - altitude);
455         double siz = (size >> 4) * exp10((double) (size & 0xF));
456         double hor = (horiz_pre >> 4) * exp10((double) (horiz_pre & 0xF));
457         double ver = (vert_pre >> 4) * exp10((double) (vert_pre & 0xF));
458
459         if (asprintf(&s, "%d %d %.3f %c %d %d %.3f %c %.2fm %.2fm %.2fm %.2fm",
460                      (lat / 60000 / 60),
461                      (lat / 60000) % 60,
462                      (lat % 60000) / 1000.,
463                      NS,
464                      (lon / 60000 / 60),
465                      (lon / 60000) % 60,
466                      (lon % 60000) / 1000.,
467                      EW,
468                      alt / 100.,
469                      siz / 100.,
470                      hor / 100.,
471                      ver / 100.) < 0)
472                 return NULL;
473
474         return s;
475 }
476
477 int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
478         _cleanup_free_ char *k = NULL, *t = NULL;
479         char *s;
480         int r;
481
482         assert(rr);
483
484         r = dns_resource_key_to_string(rr->key, &k);
485         if (r < 0)
486                 return r;
487
488         switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
489
490         case DNS_TYPE_SRV:
491                 r = asprintf(&s, "%s %u %u %u %s",
492                              k,
493                              rr->srv.priority,
494                              rr->srv.weight,
495                              rr->srv.port,
496                              strna(rr->srv.name));
497                 if (r < 0)
498                         return -ENOMEM;
499                 break;
500
501         case DNS_TYPE_PTR:
502         case DNS_TYPE_NS:
503         case DNS_TYPE_CNAME:
504         case DNS_TYPE_DNAME:
505                 s = strjoin(k, " ", rr->ptr.name, NULL);
506                 if (!s)
507                         return -ENOMEM;
508
509                 break;
510
511         case DNS_TYPE_HINFO:
512                 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
513                 if (!s)
514                         return -ENOMEM;
515                 break;
516
517         case DNS_TYPE_SPF: /* exactly the same as TXT */
518         case DNS_TYPE_TXT:
519                 t = strv_join_quoted(rr->txt.strings);
520                 if (!t)
521                         return -ENOMEM;
522
523                 s = strjoin(k, " ", t, NULL);
524                 if (!s)
525                         return -ENOMEM;
526
527                 break;
528
529         case DNS_TYPE_A: {
530                 _cleanup_free_ char *x = NULL;
531
532                 r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
533                 if (r < 0)
534                         return r;
535
536                 s = strjoin(k, " ", x, NULL);
537                 if (!s)
538                         return -ENOMEM;
539                 break;
540         }
541
542         case DNS_TYPE_AAAA:
543                 r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &t);
544                 if (r < 0)
545                         return r;
546
547                 s = strjoin(k, " ", t, NULL);
548                 if (!s)
549                         return -ENOMEM;
550                 break;
551
552         case DNS_TYPE_SOA:
553                 r = asprintf(&s, "%s %s %s %u %u %u %u %u",
554                              k,
555                              strna(rr->soa.mname),
556                              strna(rr->soa.rname),
557                              rr->soa.serial,
558                              rr->soa.refresh,
559                              rr->soa.retry,
560                              rr->soa.expire,
561                              rr->soa.minimum);
562                 if (r < 0)
563                         return -ENOMEM;
564                 break;
565
566         case DNS_TYPE_MX:
567                 r = asprintf(&s, "%s %u %s",
568                              k,
569                              rr->mx.priority,
570                              rr->mx.exchange);
571                 if (r < 0)
572                         return -ENOMEM;
573                 break;
574
575         case DNS_TYPE_LOC:
576                 assert(rr->loc.version == 0);
577
578                 t = format_location(rr->loc.latitude,
579                                     rr->loc.longitude,
580                                     rr->loc.altitude,
581                                     rr->loc.size,
582                                     rr->loc.horiz_pre,
583                                     rr->loc.vert_pre);
584                 if (!t)
585                         return -ENOMEM;
586
587                 s = strjoin(k, " ", t, NULL);
588                 if (!s)
589                         return -ENOMEM;
590                 break;
591
592         case DNS_TYPE_SSHFP:
593                 t = hexmem(rr->sshfp.key, rr->sshfp.key_size);
594                 if (!t)
595                         return -ENOMEM;
596
597                 r = asprintf(&s, "%s %u %u %s",
598                              k,
599                              rr->sshfp.algorithm,
600                              rr->sshfp.fptype,
601                              t);
602                 if (r < 0)
603                         return -ENOMEM;
604                 break;
605
606         case DNS_TYPE_DNSKEY: {
607                 const char *alg;
608
609                 alg = dnssec_algorithm_to_string(rr->dnskey.algorithm);
610
611                 t = hexmem(rr->dnskey.key, rr->dnskey.key_size);
612                 if (!t)
613                         return -ENOMEM;
614
615                 r = asprintf(&s, "%s %u 3 %.*s%.*u %s",
616                              k,
617                              dnskey_to_flags(rr),
618                              alg ? -1 : 0, alg,
619                              alg ? 0 : 1, alg ? 0u : (unsigned) rr->dnskey.algorithm,
620                              t);
621                 if (r < 0)
622                         return -ENOMEM;
623                 break;
624         }
625
626         case DNS_TYPE_RRSIG: {
627                 const char *type, *alg;
628
629                 type = dns_type_to_string(rr->rrsig.type_covered);
630                 alg = dnssec_algorithm_to_string(rr->rrsig.algorithm);
631
632                 t = hexmem(rr->rrsig.signature, rr->rrsig.signature_size);
633                 if (!t)
634                         return -ENOMEM;
635
636                 /* TYPE?? follows
637                  * http://tools.ietf.org/html/rfc3597#section-5 */
638
639                 r = asprintf(&s, "%s %s%.*u %.*s%.*u %u %u %u %u %u %s %s",
640                              k,
641                              type ?: "TYPE",
642                              type ? 0 : 1, type ? 0u : (unsigned) rr->rrsig.type_covered,
643                              alg ? -1 : 0, alg,
644                              alg ? 0 : 1, alg ? 0u : (unsigned) rr->rrsig.algorithm,
645                              rr->rrsig.labels,
646                              rr->rrsig.original_ttl,
647                              rr->rrsig.expiration,
648                              rr->rrsig.inception,
649                              rr->rrsig.key_tag,
650                              rr->rrsig.signer,
651                              t);
652                 if (r < 0)
653                         return -ENOMEM;
654                 break;
655         }
656
657         default:
658                 t = hexmem(rr->generic.data, rr->generic.size);
659                 if (!t)
660                         return -ENOMEM;
661
662                 s = strjoin(k, " ", t, NULL);
663                 if (!s)
664                         return -ENOMEM;
665                 break;
666         }
667
668         *ret = s;
669         return 0;
670 }
671
672 const char *dns_class_to_string(uint16_t class) {
673
674         switch (class) {
675
676         case DNS_CLASS_IN:
677                 return "IN";
678
679         case DNS_CLASS_ANY:
680                 return "ANY";
681         }
682
683         return NULL;
684 }
685
686 int dns_class_from_string(const char *s, uint16_t *class) {
687         assert(s);
688         assert(class);
689
690         if (strcaseeq(s, "IN"))
691                 *class = DNS_CLASS_IN;
692         else if (strcaseeq(s, "ANY"))
693                 *class = DNS_TYPE_ANY;
694         else
695                 return -EINVAL;
696
697         return 0;
698 }