]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-packet.c
resolved: add a DNS client stub resolver
[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
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);