]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-packet.c
dns-domain: introduce macros for accessing all DNS header fields
[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"
23
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
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
54int 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
72DnsPacket *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
82static 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
95DnsPacket *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
109int 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
118int dns_packet_validate_reply(DnsPacket *p) {
74b2466e
LP
119 int r;
120
121 assert(p);
122
123 r = dns_packet_validate(p);
124 if (r < 0)
125 return r;
126
3cb10d3a 127 if (DNS_PACKET_QR(p) == 0)
74b2466e
LP
128 return -EBADMSG;
129
3cb10d3a 130 if (DNS_PACKET_OPCODE(p) != 0)
74b2466e
LP
131 return -EBADMSG;
132
133 return 0;
134}
135
136static 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
152static 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
174int 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
189int 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
205int 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
227int 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
247int 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
303done:
304 if (start)
305 *start = saved_size;
306
307 return 0;
308
309fail:
310 dns_packet_truncate(p, saved_size);
311 return r;
312}
313
314int 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
340fail:
341 dns_packet_truncate(p, saved_size);
342 return r;
343}
344
345int 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
361static 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
369int 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
383int 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
398int 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
416int 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
459fail:
460 dns_packet_rewind(p, saved_rindex);
461 return r;
462}
463
464int 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
552fail:
553 dns_packet_rewind(p, saved_rindex);
554 return r;
555}
556
557int 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;
586fail:
587 dns_packet_rewind(p, saved_rindex);
588 return r;
589}
590
591int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
4e0296a9 592 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr;
74b2466e
LP
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
74b2466e
LP
601 rr = dns_resource_record_new();
602 if (!rr)
4e0296a9
ZJS
603 return -ENOMEM;
604
605 saved_rindex = p->rindex;
74b2466e
LP
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;
686fail:
687 dns_packet_rewind(p, saved_rindex);
688 return r;
689}
690
691int dns_packet_skip_question(DnsPacket *p) {
692 int r;
693
694 unsigned i, n;
695 assert(p);
696
3cb10d3a 697 n = DNS_PACKET_QDCOUNT(p);
74b2466e
LP
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
709static 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};
729DEFINE_STRING_TABLE_LOOKUP(dns_rcode, int);