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