]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-packet.c
shared: rename PROTO_ADDRESS_SIZE() to FAMILY_ADDRESS_SIZE()
[thirdparty/systemd.git] / src / resolve / resolved-dns-packet.c
CommitLineData
74b2466e
LP
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"
c73ce96b 23#include "util.h"
74b2466e
LP
24#include "resolved-dns-domain.h"
25#include "resolved-dns-packet.h"
26
27int 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
c73ce96b
LP
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
74b2466e
LP
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
61int 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
79DnsPacket *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
89static void dns_packet_free(DnsPacket *p) {
90 char *s;
91
92 assert(p);
93
322345fd
LP
94 if (p->rrs)
95 dns_resource_record_freev(p->rrs, DNS_PACKET_RRCOUNT(p));
96
74b2466e
LP
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
105DnsPacket *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
119int dns_packet_validate(DnsPacket *p) {
120 assert(p);
121
122 if (p->size < DNS_PACKET_HEADER_SIZE)
123 return -EBADMSG;
124
c73ce96b
LP
125 if (p->size > DNS_PACKET_SIZE_MAX)
126 return -EBADMSG;
127
74b2466e
LP
128 return 0;
129}
130
131int dns_packet_validate_reply(DnsPacket *p) {
74b2466e
LP
132 int r;
133
134 assert(p);
135
136 r = dns_packet_validate(p);
137 if (r < 0)
138 return r;
139
3cb10d3a 140 if (DNS_PACKET_QR(p) == 0)
74b2466e
LP
141 return -EBADMSG;
142
3cb10d3a 143 if (DNS_PACKET_OPCODE(p) != 0)
74b2466e
LP
144 return -EBADMSG;
145
146 return 0;
147}
148
149static int dns_packet_extend(DnsPacket *p, size_t add, void **ret, size_t *start) {
150 assert(p);
151
c73ce96b
LP
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 }
74b2466e
LP
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
192static 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
214int 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
229int 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
245int 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
267int 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
287int 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
343done:
344 if (start)
345 *start = saved_size;
346
347 return 0;
348
349fail:
350 dns_packet_truncate(p, saved_size);
351 return r;
352}
353
354int 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
380fail:
381 dns_packet_truncate(p, saved_size);
382 return r;
383}
384
385int 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
8ba9fd9c 401void dns_packet_rewind(DnsPacket *p, size_t idx) {
74b2466e
LP
402 assert(p);
403 assert(idx <= p->size);
404 assert(idx >= DNS_PACKET_HEADER_SIZE);
405
406 p->rindex = idx;
407}
408
409int 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
423int 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
438int 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
456int 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
499fail:
500 dns_packet_rewind(p, saved_rindex);
501 return r;
502}
503
504int 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
592fail:
593 dns_packet_rewind(p, saved_rindex);
594 return r;
595}
596
597int 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;
626fail:
627 dns_packet_rewind(p, saved_rindex);
628 return r;
629}
630
631int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
4e0296a9 632 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr;
74b2466e
LP
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
74b2466e
LP
641 rr = dns_resource_record_new();
642 if (!rr)
4e0296a9
ZJS
643 return -ENOMEM;
644
645 saved_rindex = p->rindex;
74b2466e
LP
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;
726fail:
727 dns_packet_rewind(p, saved_rindex);
728 return r;
729}
730
731int dns_packet_skip_question(DnsPacket *p) {
322345fd 732 unsigned i, n;
74b2466e
LP
733 int r;
734
74b2466e
LP
735 assert(p);
736
322345fd
LP
737 dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE);
738
3cb10d3a 739 n = DNS_PACKET_QDCOUNT(p);
74b2466e
LP
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
322345fd
LP
751int 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
789finish:
790 p->rindex = saved_rindex;
791 return r;
792}
793
74b2466e
LP
794static 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};
814DEFINE_STRING_TABLE_LOOKUP(dns_rcode, int);