chiark / gitweb /
resolved: add a DNS client stub resolver
[elogind.git] / src / resolve / resolved-dns-packet.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 "utf8.h"
23
24 #include "resolved-dns-domain.h"
25 #include "resolved-dns-packet.h"
26
27 int dns_packet_new(DnsPacket **ret, size_t mtu) {
28         DnsPacket *p;
29         size_t a;
30
31         assert(ret);
32
33         if (mtu <= 0)
34                 a = DNS_PACKET_SIZE_START;
35         else
36                 a = mtu;
37
38         if (a < DNS_PACKET_HEADER_SIZE)
39                 a = DNS_PACKET_HEADER_SIZE;
40
41         p = malloc0(ALIGN(sizeof(DnsPacket)) + a);
42         if (!p)
43                 return -ENOMEM;
44
45         p->size = p->rindex = DNS_PACKET_HEADER_SIZE;
46         p->allocated = a;
47         p->n_ref = 1;
48
49         *ret = p;
50
51         return 0;
52 }
53
54 int dns_packet_new_query(DnsPacket **ret, size_t mtu) {
55         DnsPacket *p;
56         DnsPacketHeader *h;
57         int r;
58
59         assert(ret);
60
61         r = dns_packet_new(&p, mtu);
62         if (r < 0)
63                 return r;
64
65         h = DNS_PACKET_HEADER(p);
66         h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0, 0, 0, 0, 1, 0, 0, 0, 0));
67
68         *ret = p;
69         return 0;
70 }
71
72 DnsPacket *dns_packet_ref(DnsPacket *p) {
73
74         if (!p)
75                 return NULL;
76
77         assert(p->n_ref > 0);
78         p->n_ref++;
79         return p;
80 }
81
82 static void dns_packet_free(DnsPacket *p) {
83         char *s;
84
85         assert(p);
86
87         while ((s = hashmap_steal_first_key(p->names)))
88                 free(s);
89         hashmap_free(p->names);
90
91         free(p->data);
92         free(p);
93 }
94
95 DnsPacket *dns_packet_unref(DnsPacket *p) {
96         if (!p)
97                 return NULL;
98
99         assert(p->n_ref > 0);
100
101         if (p->n_ref == 1)
102                 dns_packet_free(p);
103         else
104                 p->n_ref--;
105
106         return NULL;
107 }
108
109 int dns_packet_validate(DnsPacket *p) {
110         assert(p);
111
112         if (p->size < DNS_PACKET_HEADER_SIZE)
113                 return -EBADMSG;
114
115         return 0;
116 }
117
118 int dns_packet_validate_reply(DnsPacket *p) {
119         DnsPacketHeader *h;
120         int r;
121
122         assert(p);
123
124         r = dns_packet_validate(p);
125         if (r < 0)
126                 return r;
127
128         h = DNS_PACKET_HEADER(p);
129
130         /* Check QR field */
131         if ((be16toh(h->flags) & 1) == 0)
132                 return -EBADMSG;
133
134         /* Check opcode field */
135         if (((be16toh(h->flags) >> 1) & 15) != 0)
136                 return -EBADMSG;
137
138         return 0;
139 }
140
141 static int dns_packet_extend(DnsPacket *p, size_t add, void **ret, size_t *start) {
142         assert(p);
143
144         if (p->size + add > p->allocated)
145                 return -ENOMEM;
146
147         if (start)
148                 *start = p->size;
149
150         if (ret)
151                 *ret = (uint8_t*) DNS_PACKET_DATA(p) + p->size;
152
153         p->size += add;
154         return 0;
155 }
156
157 static void dns_packet_truncate(DnsPacket *p, size_t sz) {
158         Iterator i;
159         char *s;
160         void *n;
161
162         assert(p);
163
164         if (p->size <= sz)
165                 return;
166
167         HASHMAP_FOREACH_KEY(s, n, p->names, i) {
168
169                 if (PTR_TO_SIZE(n) < sz)
170                         continue;
171
172                 hashmap_remove(p->names, s);
173                 free(s);
174         }
175
176         p->size = sz;
177 }
178
179 int dns_packet_append_uint8(DnsPacket *p, uint8_t v, size_t *start) {
180         void *d;
181         int r;
182
183         assert(p);
184
185         r = dns_packet_extend(p, sizeof(uint8_t), &d, start);
186         if (r < 0)
187                 return r;
188
189         ((uint8_t*) d)[0] = v;
190
191         return 0;
192 }
193
194 int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start) {
195         void *d;
196         int r;
197
198         assert(p);
199
200         r = dns_packet_extend(p, sizeof(uint16_t), &d, start);
201         if (r < 0)
202                 return r;
203
204         ((uint8_t*) d)[0] = (uint8_t) (v >> 8);
205         ((uint8_t*) d)[1] = (uint8_t) (v & 255);
206
207         return 0;
208 }
209
210 int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start) {
211         void *d;
212         size_t l;
213         int r;
214
215         assert(p);
216         assert(s);
217
218         l = strlen(s);
219         if (l > 255)
220                 return -E2BIG;
221
222         r = dns_packet_extend(p, 1 + l, &d, start);
223         if (r < 0)
224                 return r;
225
226         ((uint8_t*) d)[0] = (uint8_t) l;
227         memcpy(((uint8_t*) d) + 1, s, l);
228
229         return 0;
230 }
231
232 int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start) {
233         void *w;
234         int r;
235
236         assert(p);
237         assert(d);
238
239         if (l > DNS_LABEL_MAX)
240                 return -E2BIG;
241
242         r = dns_packet_extend(p, 1 + l, &w, start);
243         if (r < 0)
244                 return r;
245
246         ((uint8_t*) w)[0] = (uint8_t) l;
247         memcpy(((uint8_t*) w) + 1, d, l);
248
249         return 0;
250 }
251
252 int dns_packet_append_name(DnsPacket *p, const char *name, size_t *start) {
253         size_t saved_size;
254         int r;
255
256         assert(p);
257         assert(name);
258
259         saved_size = p->size;
260
261         while (*name) {
262                 _cleanup_free_ char *s = NULL;
263                 char label[DNS_LABEL_MAX];
264                 size_t n;
265
266                 n = PTR_TO_SIZE(hashmap_get(p->names, name));
267                 if (n > 0) {
268                         assert(n < p->size);
269
270                         if (n < 0x4000) {
271                                 r = dns_packet_append_uint16(p, 0xC000 | n, NULL);
272                                 if (r < 0)
273                                         goto fail;
274
275                                 goto done;
276                         }
277                 }
278
279                 s = strdup(name);
280                 if (!s) {
281                         r = -ENOMEM;
282                         goto fail;
283                 }
284
285                 r = dns_label_unescape(&name, label, sizeof(label));
286                 if (r < 0)
287                         goto fail;
288
289                 r = dns_packet_append_label(p, label, r, &n);
290                 if (r < 0)
291                         goto fail;
292
293                 r = hashmap_ensure_allocated(&p->names, dns_name_hash_func, dns_name_compare_func);
294                 if (r < 0)
295                         goto fail;
296
297                 r = hashmap_put(p->names, s, SIZE_TO_PTR(n));
298                 if (r < 0)
299                         goto fail;
300
301                 s = NULL;
302         }
303
304         r = dns_packet_append_uint8(p, 0, NULL);
305         if (r < 0)
306                 return r;
307
308 done:
309         if (start)
310                 *start = saved_size;
311
312         return 0;
313
314 fail:
315         dns_packet_truncate(p, saved_size);
316         return r;
317 }
318
319 int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start) {
320         size_t saved_size;
321         int r;
322
323         assert(p);
324         assert(k);
325
326         saved_size = p->size;
327
328         r = dns_packet_append_name(p, k->name, NULL);
329         if (r < 0)
330                 goto fail;
331
332         r = dns_packet_append_uint16(p, k->type, NULL);
333         if (r < 0)
334                 goto fail;
335
336         r = dns_packet_append_uint16(p, k->class, NULL);
337         if (r < 0)
338                 goto fail;
339
340         if (start)
341                 *start = saved_size;
342
343         return 0;
344
345 fail:
346         dns_packet_truncate(p, saved_size);
347         return r;
348 }
349
350 int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start) {
351         assert(p);
352
353         if (p->rindex + sz > p->size)
354                 return -EMSGSIZE;
355
356         if (ret)
357                 *ret = (uint8_t*) DNS_PACKET_DATA(p) + p->rindex;
358
359         if (start)
360                 *start = p->rindex;
361
362         p->rindex += sz;
363         return 0;
364 }
365
366 static void dns_packet_rewind(DnsPacket *p, size_t idx) {
367         assert(p);
368         assert(idx <= p->size);
369         assert(idx >= DNS_PACKET_HEADER_SIZE);
370
371         p->rindex = idx;
372 }
373
374 int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start) {
375         const void *d;
376         int r;
377
378         assert(p);
379
380         r = dns_packet_read(p, sizeof(uint8_t), &d, start);
381         if (r < 0)
382                 return r;
383
384         *ret = ((uint8_t*) d)[0];
385         return 0;
386 }
387
388 int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start) {
389         const void *d;
390         int r;
391
392         assert(p);
393
394         r = dns_packet_read(p, sizeof(uint16_t), &d, start);
395         if (r < 0)
396                 return r;
397
398         *ret = (((uint16_t) ((uint8_t*) d)[0]) << 8) |
399                 ((uint16_t) ((uint8_t*) d)[1]);
400         return 0;
401 }
402
403 int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) {
404         const void *d;
405         int r;
406
407         assert(p);
408
409         r = dns_packet_read(p, sizeof(uint32_t), &d, start);
410         if (r < 0)
411                 return r;
412
413         *ret = (((uint32_t) ((uint8_t*) d)[0]) << 24) |
414                (((uint32_t) ((uint8_t*) d)[1]) << 16) |
415                (((uint32_t) ((uint8_t*) d)[2]) << 8) |
416                 ((uint32_t) ((uint8_t*) d)[3]);
417
418         return 0;
419 }
420
421 int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) {
422         size_t saved_rindex;
423         const void *d;
424         char *t;
425         uint8_t c;
426         int r;
427
428         assert(p);
429
430         saved_rindex = p->rindex;
431
432         r = dns_packet_read_uint8(p, &c, NULL);
433         if (r < 0)
434                 goto fail;
435
436         r = dns_packet_read(p, c, &d, NULL);
437         if (r < 0)
438                 goto fail;
439
440         if (memchr(d, 0, c)) {
441                 r = -EBADMSG;
442                 goto fail;
443         }
444
445         t = strndup(d, c);
446         if (!t) {
447                 r = -ENOMEM;
448                 goto fail;
449         }
450
451         if (!utf8_is_valid(t)) {
452                 free(t);
453                 r = -EBADMSG;
454                 goto fail;
455         }
456
457         *ret = t;
458
459         if (start)
460                 *start = saved_rindex;
461
462         return 0;
463
464 fail:
465         dns_packet_rewind(p, saved_rindex);
466         return r;
467 }
468
469 int dns_packet_read_name(DnsPacket *p, char **_ret, size_t *start) {
470         size_t saved_rindex, after_rindex = 0;
471         _cleanup_free_ char *ret = NULL;
472         size_t n = 0, allocated = 0;
473         bool first = true;
474         int r;
475
476         assert(p);
477         assert(_ret);
478
479         saved_rindex = p->rindex;
480
481         for (;;) {
482                 uint8_t c, d;
483
484                 r = dns_packet_read_uint8(p, &c, NULL);
485                 if (r < 0)
486                         goto fail;
487
488                 if (c == 0)
489                         /* End of name */
490                         break;
491                 else if (c <= 63) {
492                         _cleanup_free_ char *t = NULL;
493                         const char *label;
494
495                         /* Literal label */
496                         r = dns_packet_read(p, c, (const void**) &label, NULL);
497                         if (r < 0)
498                                 goto fail;
499
500                         r = dns_label_escape(label, c, &t);
501                         if (r < 0)
502                                 goto fail;
503
504                         if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) {
505                                 r = -ENOMEM;
506                                 goto fail;
507                         }
508
509                         if (!first)
510                                 ret[n++] = '.';
511                         else
512                                 first = false;
513
514                         memcpy(ret + n, t, c);
515                         n += r;
516                         continue;
517                 } else if ((c & 0xc0) == 0xc0) {
518                         uint16_t ptr;
519
520                         /* Pointer */
521                         r = dns_packet_read_uint8(p, &d, NULL);
522                         if (r < 0)
523                                 goto fail;
524
525                         ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d;
526                         if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= saved_rindex) {
527                                 r = -EBADMSG;
528                                 goto fail;
529                         }
530
531                         if (after_rindex == 0)
532                                 after_rindex = p->rindex;
533
534                         p->rindex = ptr;
535                 } else
536                         goto fail;
537         }
538
539         if (!GREEDY_REALLOC(ret, allocated, n + 1)) {
540                 r = -ENOMEM;
541                 goto fail;
542         }
543
544         ret[n] = 0;
545
546         if (after_rindex != 0)
547                 p->rindex= after_rindex;
548
549         *_ret = ret;
550         ret = NULL;
551
552         if (start)
553                 *start = saved_rindex;
554
555         return 0;
556
557 fail:
558         dns_packet_rewind(p, saved_rindex);
559         return r;
560 }
561
562 int dns_packet_read_key(DnsPacket *p, DnsResourceKey *ret, size_t *start) {
563         _cleanup_(dns_resource_key_free) DnsResourceKey k = {};
564         size_t saved_rindex;
565         int r;
566
567         assert(p);
568         assert(ret);
569
570         saved_rindex = p->rindex;
571
572         r = dns_packet_read_name(p, &k.name, NULL);
573         if (r < 0)
574                 goto fail;
575
576         r = dns_packet_read_uint16(p, &k.type, NULL);
577         if (r < 0)
578                 goto fail;
579
580         r = dns_packet_read_uint16(p, &k.class, NULL);
581         if (r < 0)
582                 goto fail;
583
584         *ret = k;
585         zero(k);
586
587         if (start)
588                 *start = saved_rindex;
589
590         return 0;
591 fail:
592         dns_packet_rewind(p, saved_rindex);
593         return r;
594 }
595
596 int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
597         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
598         size_t saved_rindex, offset;
599         uint16_t rdlength;
600         const void *d;
601         int r;
602
603         assert(p);
604         assert(ret);
605
606         saved_rindex = p->rindex;
607
608         rr = dns_resource_record_new();
609         if (!rr)
610                 goto fail;
611
612         r = dns_packet_read_key(p, &rr->key, NULL);
613         if (r < 0)
614                 goto fail;
615
616         r = dns_packet_read_uint32(p, &rr->ttl, NULL);
617         if (r < 0)
618                 goto fail;
619
620         r = dns_packet_read_uint16(p, &rdlength, NULL);
621         if (r < 0)
622                 goto fail;
623
624         if (p->rindex + rdlength > p->size) {
625                 r = -EBADMSG;
626                 goto fail;
627         }
628
629         offset = p->rindex;
630
631         switch (rr->key.type) {
632
633         case DNS_TYPE_PTR:
634         case DNS_TYPE_NS:
635         case DNS_TYPE_CNAME:
636                 r = dns_packet_read_name(p, &rr->ptr.name, NULL);
637                 break;
638
639         case DNS_TYPE_HINFO:
640                 r = dns_packet_read_string(p, &rr->hinfo.cpu, NULL);
641                 if (r < 0)
642                         goto fail;
643
644                 r = dns_packet_read_string(p, &rr->hinfo.os, NULL);
645                 break;
646
647         case DNS_TYPE_A:
648                 r = dns_packet_read(p, sizeof(struct in_addr), &d, NULL);
649                 if (r < 0)
650                         goto fail;
651
652                 memcpy(&rr->a.in_addr, d, sizeof(struct in_addr));
653                 break;
654
655         case DNS_TYPE_AAAA:
656                 r = dns_packet_read(p, sizeof(struct in6_addr), &d, NULL);
657                 if (r < 0)
658                         goto fail;
659
660                 memcpy(&rr->aaaa.in6_addr, d, sizeof(struct in6_addr));
661                 break;
662
663         default:
664                 r = dns_packet_read(p, rdlength, &d, NULL);
665                 if (r < 0)
666                         goto fail;
667
668                 rr->generic.data = memdup(d, rdlength);
669                 if (!rr->generic.data) {
670                         r = -ENOMEM;
671                         goto fail;
672                 }
673
674                 rr->generic.size = rdlength;
675                 break;
676         }
677         if (r < 0)
678                 goto fail;
679         if (p->rindex != offset + rdlength) {
680                 r = -EBADMSG;
681                 goto fail;
682         }
683
684         *ret = rr;
685         rr = NULL;
686
687         if (start)
688                 *start = saved_rindex;
689
690         return 0;
691 fail:
692         dns_packet_rewind(p, saved_rindex);
693         return r;
694 }
695
696 int dns_packet_skip_question(DnsPacket *p) {
697         int r;
698
699         unsigned i, n;
700         assert(p);
701
702         n = be16toh(DNS_PACKET_HEADER(p)->qdcount);
703         for (i = 0; i < n; i++) {
704                 _cleanup_(dns_resource_key_free) DnsResourceKey key = {};
705
706                 r = dns_packet_read_key(p, &key, NULL);
707                 if (r < 0)
708                         return r;
709         }
710
711         return 0;
712 }
713
714 static const char* const dns_rcode_table[_DNS_RCODE_MAX_DEFINED] = {
715         [DNS_RCODE_SUCCESS] = "SUCCESS",
716         [DNS_RCODE_FORMERR] = "FORMERR",
717         [DNS_RCODE_SERVFAIL] = "SERVFAIL",
718         [DNS_RCODE_NXDOMAIN] = "NXDOMAIN",
719         [DNS_RCODE_NOTIMP] = "NOTIMP",
720         [DNS_RCODE_REFUSED] = "REFUSED",
721         [DNS_RCODE_YXDOMAIN] = "YXDOMAIN",
722         [DNS_RCODE_YXRRSET] = "YRRSET",
723         [DNS_RCODE_NXRRSET] = "NXRRSET",
724         [DNS_RCODE_NOTAUTH] = "NOTAUTH",
725         [DNS_RCODE_NOTZONE] = "NOTZONE",
726         [DNS_RCODE_BADVERS] = "BADVERS",
727         [DNS_RCODE_BADKEY] = "BADKEY",
728         [DNS_RCODE_BADTIME] = "BADTIME",
729         [DNS_RCODE_BADMODE] = "BADMODE",
730         [DNS_RCODE_BADNAME] = "BADNAME",
731         [DNS_RCODE_BADALG] = "BADALG",
732         [DNS_RCODE_BADTRUNC] = "BADTRUNC",
733 };
734 DEFINE_STRING_TABLE_LOOKUP(dns_rcode, int);