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