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