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