chiark / gitweb /
resolve-host: make arg_type an int
[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 "dns-type.h"
29
30 DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) {
31         DnsResourceKey *k;
32         size_t l;
33
34         assert(name);
35
36         l = strlen(name);
37         k = malloc0(sizeof(DnsResourceKey) + l + 1);
38         if (!k)
39                 return NULL;
40
41         k->n_ref = 1;
42         k->class = class;
43         k->type = type;
44
45         strcpy((char*) k + sizeof(DnsResourceKey), name);
46
47         return k;
48 }
49
50 DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name) {
51         DnsResourceKey *k;
52
53         assert(name);
54
55         k = new0(DnsResourceKey, 1);
56         if (!k)
57                 return NULL;
58
59         k->n_ref = 1;
60         k->class = class;
61         k->type = type;
62         k->_name = name;
63
64         return k;
65 }
66
67 DnsResourceKey* dns_resource_key_ref(DnsResourceKey *k) {
68
69         if (!k)
70                 return NULL;
71
72         assert(k->n_ref > 0);
73         k->n_ref++;
74
75         return k;
76 }
77
78 DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) {
79         if (!k)
80                 return NULL;
81
82         assert(k->n_ref > 0);
83
84         if (k->n_ref == 1) {
85                 free(k->_name);
86                 free(k);
87         } else
88                 k->n_ref--;
89
90         return NULL;
91 }
92
93 int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
94         int r;
95
96         r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b));
97         if (r <= 0)
98                 return r;
99
100         if (a->class != b->class)
101                 return 0;
102
103         if (a->type != b->type)
104                 return 0;
105
106         return 1;
107 }
108
109 int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr) {
110         assert(key);
111         assert(rr);
112
113         if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
114                 return 0;
115
116         if (rr->key->type != key->type && key->type != DNS_TYPE_ANY)
117                 return 0;
118
119         return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
120 }
121
122 int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr) {
123         assert(key);
124         assert(rr);
125
126         if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
127                 return 0;
128
129         if (rr->key->type != DNS_TYPE_CNAME)
130                 return 0;
131
132         return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
133 }
134
135 unsigned long dns_resource_key_hash_func(const void *i, const uint8_t hash_key[HASH_KEY_SIZE]) {
136         const DnsResourceKey *k = i;
137         unsigned long ul;
138
139         ul = dns_name_hash_func(DNS_RESOURCE_KEY_NAME(k), hash_key);
140         ul = ul * hash_key[0] + ul + k->class;
141         ul = ul * hash_key[1] + ul + k->type;
142
143         return ul;
144 }
145
146 int dns_resource_key_compare_func(const void *a, const void *b) {
147         const DnsResourceKey *x = a, *y = b;
148         int ret;
149
150         ret = dns_name_compare_func(DNS_RESOURCE_KEY_NAME(x), DNS_RESOURCE_KEY_NAME(y));
151         if (ret != 0)
152                 return ret;
153
154         if (x->type < y->type)
155                 return -1;
156         if (x->type > y->type)
157                 return 1;
158
159         if (x->class < y->class)
160                 return -1;
161         if (x->class > y->class)
162                 return 1;
163
164         return 0;
165 }
166
167 int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) {
168         char cbuf[DECIMAL_STR_MAX(uint16_t)], tbuf[DECIMAL_STR_MAX(uint16_t)];
169         const char *c, *t;
170         char *s;
171
172         c = dns_class_to_string(key->class);
173         if (!c) {
174                 sprintf(cbuf, "%i", key->class);
175                 c = cbuf;
176         }
177
178         t = dns_type_to_string(key->type);
179         if (!t){
180                 sprintf(tbuf, "%i", key->type);
181                 t = tbuf;
182         }
183
184         if (asprintf(&s, "%s %s %-5s", DNS_RESOURCE_KEY_NAME(key), c, t) < 0)
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         if (a->unparseable != b->unparseable)
334                 return 0;
335
336         switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) {
337
338         case DNS_TYPE_SRV:
339                 r = dns_name_equal(a->srv.name, b->srv.name);
340                 if (r <= 0)
341                         return r;
342
343                 return a->srv.priority == b->srv.priority &&
344                        a->srv.weight == b->srv.weight &&
345                        a->srv.port == b->srv.port;
346
347         case DNS_TYPE_PTR:
348         case DNS_TYPE_NS:
349         case DNS_TYPE_CNAME:
350         case DNS_TYPE_DNAME:
351                 return dns_name_equal(a->ptr.name, b->ptr.name);
352
353         case DNS_TYPE_HINFO:
354                 return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
355                        strcaseeq(a->hinfo.os, b->hinfo.os);
356
357         case DNS_TYPE_SPF: /* exactly the same as TXT */
358         case DNS_TYPE_TXT: {
359                 int i;
360
361                 for (i = 0; a->txt.strings[i] || b->txt.strings[i]; i++)
362                         if (!streq_ptr(a->txt.strings[i], b->txt.strings[i]))
363                                 return false;
364                 return true;
365         }
366
367         case DNS_TYPE_A:
368                 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
369
370         case DNS_TYPE_AAAA:
371                 return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
372
373         case DNS_TYPE_SOA:
374                 r = dns_name_equal(a->soa.mname, b->soa.mname);
375                 if (r <= 0)
376                         return r;
377                 r = dns_name_equal(a->soa.rname, b->soa.rname);
378                 if (r <= 0)
379                         return r;
380
381                 return a->soa.serial  == b->soa.serial &&
382                        a->soa.refresh == b->soa.refresh &&
383                        a->soa.retry   == b->soa.retry &&
384                        a->soa.expire  == b->soa.expire &&
385                        a->soa.minimum == b->soa.minimum;
386
387         case DNS_TYPE_MX:
388                 if (a->mx.priority != b->mx.priority)
389                         return 0;
390
391                 return dns_name_equal(a->mx.exchange, b->mx.exchange);
392
393         case DNS_TYPE_LOC:
394                 assert(a->loc.version == b->loc.version);
395
396                 return a->loc.size == b->loc.size &&
397                        a->loc.horiz_pre == b->loc.horiz_pre &&
398                        a->loc.vert_pre == b->loc.vert_pre &&
399                        a->loc.latitude == b->loc.latitude &&
400                        a->loc.longitude == b->loc.longitude &&
401                        a->loc.altitude == b->loc.altitude;
402
403         case DNS_TYPE_SSHFP:
404                 return a->sshfp.algorithm == b->sshfp.algorithm &&
405                        a->sshfp.fptype == b->sshfp.fptype &&
406                        a->sshfp.key_size == b->sshfp.key_size &&
407                        memcmp(a->sshfp.key, b->sshfp.key, a->sshfp.key_size) == 0;
408
409         default:
410                 return a->generic.size == b->generic.size &&
411                         memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
412         }
413 }
414
415 static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t altitude,
416                              uint8_t size, uint8_t horiz_pre, uint8_t vert_pre) {
417         char *s;
418         char NS = latitude >= 1U<<31 ? 'N' : 'S';
419         char EW = longitude >= 1U<<31 ? 'E' : 'W';
420
421         int lat = latitude >= 1U<<31 ? (int) (latitude - (1U<<31)) : (int) ((1U<<31) - latitude);
422         int lon = longitude >= 1U<<31 ? (int) (longitude - (1U<<31)) : (int) ((1U<<31) - longitude);
423         double alt = altitude >= 10000000u ? altitude - 10000000u : -(double)(10000000u - altitude);
424         double siz = (size >> 4) * exp10((double) (size & 0xF));
425         double hor = (horiz_pre >> 4) * exp10((double) (horiz_pre & 0xF));
426         double ver = (vert_pre >> 4) * exp10((double) (vert_pre & 0xF));
427
428         if (asprintf(&s, "%d %d %.3f %c %d %d %.3f %c %.2fm %.2fm %.2fm %.2fm",
429                      (lat / 60000 / 60),
430                      (lat / 60000) % 60,
431                      (lat % 60000) / 1000.,
432                      NS,
433                      (lon / 60000 / 60),
434                      (lon / 60000) % 60,
435                      (lon % 60000) / 1000.,
436                      EW,
437                      alt / 100.,
438                      siz / 100.,
439                      hor / 100.,
440                      ver / 100.) < 0)
441                 return NULL;
442
443         return s;
444 }
445
446 int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
447         _cleanup_free_ char *k = NULL;
448         char *s;
449         int r;
450
451         assert(rr);
452
453         r = dns_resource_key_to_string(rr->key, &k);
454         if (r < 0)
455                 return r;
456
457         switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
458
459         case DNS_TYPE_SRV:
460                 r = asprintf(&s, "%s %u %u %u %s",
461                              k,
462                              rr->srv.priority,
463                              rr->srv.weight,
464                              rr->srv.port,
465                              strna(rr->srv.name));
466                 if (r < 0)
467                         return -ENOMEM;
468                 break;
469
470         case DNS_TYPE_PTR:
471         case DNS_TYPE_NS:
472         case DNS_TYPE_CNAME:
473         case DNS_TYPE_DNAME:
474                 s = strjoin(k, " ", rr->ptr.name, NULL);
475                 if (!s)
476                         return -ENOMEM;
477
478                 break;
479
480         case DNS_TYPE_HINFO:
481                 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
482                 if (!s)
483                         return -ENOMEM;
484                 break;
485
486         case DNS_TYPE_SPF: /* exactly the same as TXT */
487         case DNS_TYPE_TXT: {
488                 _cleanup_free_ char *t;
489
490                 t = strv_join_quoted(rr->txt.strings);
491                 if (!t)
492                         return -ENOMEM;
493
494                 s = strjoin(k, " ", t, NULL);
495                 if (!s)
496                         return -ENOMEM;
497
498                 break;
499         }
500
501         case DNS_TYPE_A: {
502                 _cleanup_free_ char *x = NULL;
503
504                 r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
505                 if (r < 0)
506                         return r;
507
508                 s = strjoin(k, " ", x, NULL);
509                 if (!s)
510                         return -ENOMEM;
511                 break;
512         }
513
514         case DNS_TYPE_AAAA: {
515                 _cleanup_free_ char *x = NULL;
516
517                 r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &x);
518                 if (r < 0)
519                         return r;
520
521                 s = strjoin(k, " ", x, NULL);
522                 if (!s)
523                         return -ENOMEM;
524                 break;
525         }
526
527         case DNS_TYPE_SOA:
528                 r = asprintf(&s, "%s %s %s %u %u %u %u %u",
529                              k,
530                              strna(rr->soa.mname),
531                              strna(rr->soa.rname),
532                              rr->soa.serial,
533                              rr->soa.refresh,
534                              rr->soa.retry,
535                              rr->soa.expire,
536                              rr->soa.minimum);
537                 if (r < 0)
538                         return -ENOMEM;
539                 break;
540
541         case DNS_TYPE_MX:
542                 r = asprintf(&s, "%s %u %s",
543                              k,
544                              rr->mx.priority,
545                              rr->mx.exchange);
546                 if (r < 0)
547                         return -ENOMEM;
548                 break;
549
550         case DNS_TYPE_LOC: {
551                 _cleanup_free_ char *loc;
552                 assert(rr->loc.version == 0);
553
554                 loc = format_location(rr->loc.latitude,
555                                       rr->loc.longitude,
556                                       rr->loc.altitude,
557                                       rr->loc.size,
558                                       rr->loc.horiz_pre,
559                                       rr->loc.vert_pre);
560                 if (!loc)
561                         return -ENOMEM;
562
563                 s = strjoin(k, " ", loc, NULL);
564                 if (!s)
565                         return -ENOMEM;
566
567                 break;
568         }
569
570         case DNS_TYPE_SSHFP: {
571                 _cleanup_free_ char *x = NULL;
572
573                 x = hexmem(rr->sshfp.key, rr->sshfp.key_size);
574                 if (!x)
575                         return -ENOMEM;
576
577                 r = asprintf(&s, "%s %u %u %s",
578                              k,
579                              rr->sshfp.algorithm,
580                              rr->sshfp.fptype,
581                              x);
582                 if (r < 0)
583                         return -ENOMEM;
584                 break;
585         }
586
587         default: {
588                 _cleanup_free_ char *x = NULL;
589
590                 x = hexmem(rr->generic.data, rr->generic.size);
591                 if (!x)
592                         return -ENOMEM;
593
594                 s = strjoin(k, " ", x, NULL);
595                 if (!s)
596                         return -ENOMEM;
597                 break;
598         }}
599
600         *ret = s;
601         return 0;
602 }
603
604 const char *dns_class_to_string(uint16_t class) {
605
606         switch (class) {
607
608         case DNS_CLASS_IN:
609                 return "IN";
610
611         case DNS_CLASS_ANY:
612                 return "ANY";
613         }
614
615         return NULL;
616 }
617
618 int dns_class_from_string(const char *s, uint16_t *class) {
619         assert(s);
620         assert(class);
621
622         if (strcaseeq(s, "IN"))
623                 *class = DNS_CLASS_IN;
624         else if (strcaseeq(s, "ANY"))
625                 *class = DNS_TYPE_ANY;
626         else
627                 return -EINVAL;
628
629         return 0;
630 }