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