chiark / gitweb /
resolved: properly process SRV records
[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_LOC:
269                 case DNS_TYPE_A:
270                 case DNS_TYPE_AAAA:
271                         break;
272
273                 default:
274                         free(rr->generic.data);
275                 }
276
277                 dns_resource_key_unref(rr->key);
278         }
279
280         free(rr);
281
282         return NULL;
283 }
284
285 int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) {
286         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
287         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
288         _cleanup_free_ char *ptr = NULL;
289         int r;
290
291         assert(ret);
292         assert(address);
293         assert(hostname);
294
295         r = dns_name_reverse(family, address, &ptr);
296         if (r < 0)
297                 return r;
298
299         key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, ptr);
300         if (!key)
301                 return -ENOMEM;
302
303         ptr = NULL;
304
305         rr = dns_resource_record_new(key);
306         if (!rr)
307                 return -ENOMEM;
308
309         rr->ptr.name = strdup(hostname);
310         if (!rr->ptr.name)
311                 return -ENOMEM;
312
313         *ret = rr;
314         rr = NULL;
315
316         return 0;
317 }
318
319 int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
320         int r;
321
322         assert(a);
323         assert(b);
324
325         r = dns_resource_key_equal(a->key, b->key);
326         if (r <= 0)
327                 return r;
328
329         switch (a->key->type) {
330
331         case DNS_TYPE_SRV:
332                 r = dns_name_equal(a->srv.name, b->srv.name);
333                 if (r <= 0)
334                         return r;
335
336                 return a->srv.priority == b->srv.priority &&
337                        a->srv.weight == b->srv.weight &&
338                        a->srv.port == b->srv.port;
339
340         case DNS_TYPE_PTR:
341         case DNS_TYPE_NS:
342         case DNS_TYPE_CNAME:
343         case DNS_TYPE_DNAME:
344                 return dns_name_equal(a->ptr.name, b->ptr.name);
345
346         case DNS_TYPE_HINFO:
347                 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
348                        strcaseeq(a->hinfo.os, b->hinfo.os);
349
350         case DNS_TYPE_SPF: /* exactly the same as TXT */
351         case DNS_TYPE_TXT: {
352                 int i;
353
354                 for (i = 0; a->txt.strings[i] || b->txt.strings[i]; i++)
355                         if (!streq_ptr(a->txt.strings[i], b->txt.strings[i]))
356                                 return false;
357                 return true;
358         }
359
360         case DNS_TYPE_A:
361                 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
362
363         case DNS_TYPE_AAAA:
364                 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
365
366         case DNS_TYPE_SOA:
367                 r = dns_name_equal(a->soa.mname, b->soa.mname);
368                 if (r <= 0)
369                         return r;
370                 r = dns_name_equal(a->soa.rname, b->soa.rname);
371                 if (r <= 0)
372                         return r;
373
374                 return a->soa.serial  == b->soa.serial &&
375                        a->soa.refresh == b->soa.refresh &&
376                        a->soa.retry   == b->soa.retry &&
377                        a->soa.expire  == b->soa.expire &&
378                        a->soa.minimum == b->soa.minimum;
379
380         case DNS_TYPE_MX:
381                 if (a->mx.priority != b->mx.priority)
382                         return 0;
383
384                 return dns_name_equal(a->mx.exchange, b->mx.exchange);
385
386         case DNS_TYPE_LOC:
387                 assert(a->loc.version == b->loc.version);
388
389                 return a->loc.size == b->loc.size &&
390                        a->loc.horiz_pre == b->loc.horiz_pre &&
391                        a->loc.vert_pre == b->loc.vert_pre &&
392                        a->loc.latitude == b->loc.latitude &&
393                        a->loc.longitude == b->loc.longitude &&
394                        a->loc.altitude == b->loc.altitude;
395
396         default:
397                 return a->generic.size == b->generic.size &&
398                         memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
399         }
400 }
401
402 static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t altitude,
403                              uint8_t size, uint8_t horiz_pre, uint8_t vert_pre) {
404         char *s;
405         char NS = latitude >= 1U<<31 ? 'N' : 'S';
406         char EW = longitude >= 1U<<31 ? 'E' : 'W';
407
408         int lat = latitude >= 1U<<31 ? (int) (latitude - (1U<<31)) : (int) ((1U<<31) - latitude);
409         int lon = longitude >= 1U<<31 ? (int) (longitude - (1U<<31)) : (int) ((1U<<31) - longitude);
410         double alt = altitude >= 10000000u ? altitude - 10000000u : -(double)(10000000u - altitude);
411         double siz = (size >> 4) * exp10((double) (size & 0xF));
412         double hor = (horiz_pre >> 4) * exp10((double) (horiz_pre & 0xF));
413         double ver = (vert_pre >> 4) * exp10((double) (vert_pre & 0xF));
414
415         if (asprintf(&s, "%d %d %.3f %c %d %d %.3f %c %.2fm %.2fm %.2fm %.2fm",
416                      (lat / 60000 / 60),
417                      (lat / 60000) % 60,
418                      (lat % 60000) / 1000.,
419                      NS,
420                      (lon / 60000 / 60),
421                      (lon / 60000) % 60,
422                      (lon % 60000) / 1000.,
423                      EW,
424                      alt / 100.,
425                      siz / 100.,
426                      hor / 100.,
427                      ver / 100.) < 0)
428                 return NULL;
429
430         return s;
431 }
432
433
434 int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
435         _cleanup_free_ char *k = NULL;
436         char *s;
437         int r;
438
439         assert(rr);
440
441         r = dns_resource_key_to_string(rr->key, &k);
442         if (r < 0)
443                 return r;
444
445         switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
446
447         case DNS_TYPE_SRV:
448                 r = asprintf(&s, "%s %u %u %u %s",
449                              k,
450                              rr->srv.priority,
451                              rr->srv.weight,
452                              rr->srv.port,
453                              strna(rr->srv.name));
454                 if (r < 0)
455                         return -ENOMEM;
456                 break;
457
458         case DNS_TYPE_PTR:
459         case DNS_TYPE_NS:
460         case DNS_TYPE_CNAME:
461         case DNS_TYPE_DNAME:
462                 s = strjoin(k, " ", rr->ptr.name, NULL);
463                 if (!s)
464                         return -ENOMEM;
465
466                 break;
467
468         case DNS_TYPE_HINFO:
469                 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
470                 if (!s)
471                         return -ENOMEM;
472                 break;
473
474         case DNS_TYPE_SPF: /* exactly the same as TXT */
475         case DNS_TYPE_TXT: {
476                 _cleanup_free_ char *t;
477
478                 t = strv_join_quoted(rr->txt.strings);
479                 if (!t)
480                         return -ENOMEM;
481
482                 s = strjoin(k, " ", t, NULL);
483                 if (!s)
484                         return -ENOMEM;
485
486                 break;
487         }
488
489         case DNS_TYPE_A: {
490                 _cleanup_free_ char *x = NULL;
491
492                 r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
493                 if (r < 0)
494                         return r;
495
496                 s = strjoin(k, " ", x, NULL);
497                 if (!s)
498                         return -ENOMEM;
499                 break;
500         }
501
502         case DNS_TYPE_AAAA: {
503                 _cleanup_free_ char *x = NULL;
504
505                 r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &x);
506                 if (r < 0)
507                         return r;
508
509                 s = strjoin(k, " ", x, NULL);
510                 if (!s)
511                         return -ENOMEM;
512                 break;
513         }
514
515         case DNS_TYPE_SOA:
516                 r = asprintf(&s, "%s %s %s %u %u %u %u %u",
517                              k,
518                              strna(rr->soa.mname),
519                              strna(rr->soa.rname),
520                              rr->soa.serial,
521                              rr->soa.refresh,
522                              rr->soa.retry,
523                              rr->soa.expire,
524                              rr->soa.minimum);
525                 if (r < 0)
526                         return -ENOMEM;
527                 break;
528
529         case DNS_TYPE_MX:
530                 r = asprintf(&s, "%s %u %s",
531                              k,
532                              rr->mx.priority,
533                              rr->mx.exchange);
534                 if (r < 0)
535                         return -ENOMEM;
536                 break;
537
538         case DNS_TYPE_LOC: {
539                 _cleanup_free_ char *loc;
540                 assert(rr->loc.version == 0);
541
542                 loc = format_location(rr->loc.latitude,
543                                       rr->loc.longitude,
544                                       rr->loc.altitude,
545                                       rr->loc.size,
546                                       rr->loc.horiz_pre,
547                                       rr->loc.vert_pre);
548                 if (!loc)
549                         return -ENOMEM;
550
551                 s = strjoin(k, " ", loc, NULL);
552                 if (!s)
553                         return -ENOMEM;
554
555                 break;
556         }
557
558         default: {
559                 _cleanup_free_ char *x = NULL;
560
561                 x = hexmem(rr->generic.data, rr->generic.size);
562                 if (!x)
563                         return -ENOMEM;
564
565                 s = strjoin(k, " ", x, NULL);
566                 if (!s)
567                         return -ENOMEM;
568                 break;
569         }}
570
571         *ret = s;
572         return 0;
573 }
574
575 const char *dns_class_to_string(uint16_t class) {
576
577         switch (class) {
578
579         case DNS_CLASS_IN:
580                 return "IN";
581
582         case DNS_CLASS_ANY:
583                 return "ANY";
584         }
585
586         return NULL;
587 }
588
589 int dns_class_from_string(const char *s, uint16_t *class) {
590         assert(s);
591         assert(class);
592
593         if (strcaseeq(s, "IN"))
594                 *class = DNS_CLASS_IN;
595         else if (strcaseeq(s, "ANY"))
596                 *class = DNS_TYPE_ANY;
597         else
598                 return -EINVAL;
599
600         return 0;
601 }
602
603 static const struct {
604         uint16_t type;
605         const char *name;
606 } dns_types[] = {
607         { DNS_TYPE_A,     "A"     },
608         { DNS_TYPE_NS,    "NS"    },
609         { DNS_TYPE_CNAME, "CNAME" },
610         { DNS_TYPE_SOA,   "SOA"   },
611         { DNS_TYPE_PTR,   "PTR"   },
612         { DNS_TYPE_HINFO, "HINFO" },
613         { DNS_TYPE_MX,    "MX"    },
614         { DNS_TYPE_TXT,   "TXT"   },
615         { DNS_TYPE_AAAA,  "AAAA"  },
616         { DNS_TYPE_LOC,   "LOC"   },
617         { DNS_TYPE_SRV,   "SRV"   },
618         { DNS_TYPE_SSHFP, "SSHFP" },
619         { DNS_TYPE_SPF,   "SPF"   },
620         { DNS_TYPE_DNAME, "DNAME" },
621         { DNS_TYPE_ANY,   "ANY"   },
622         { DNS_TYPE_OPT,   "OPT"   },
623         { DNS_TYPE_TKEY,  "TKEY"  },
624         { DNS_TYPE_TSIG,  "TSIG"  },
625         { DNS_TYPE_IXFR,  "IXFR"  },
626         { DNS_TYPE_AXFR,  "AXFR"  },
627 };
628
629 const char *dns_type_to_string(uint16_t type) {
630         unsigned i;
631
632         for (i = 0; i < ELEMENTSOF(dns_types); i++)
633                 if (dns_types[i].type == type)
634                         return dns_types[i].name;
635
636         return NULL;
637 }
638
639 int dns_type_from_string(const char *s, uint16_t *type) {
640         unsigned i;
641
642         assert(s);
643         assert(type);
644
645         for (i = 0; i < ELEMENTSOF(dns_types); i++)
646                 if (strcaseeq(dns_types[i].name, s)) {
647                         *type = dns_types[i].type;
648                         return 0;
649                 }
650
651         return -EINVAL;
652 }