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