]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-packet.c
resolved: MX records
[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
1716f6dc 27int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
74b2466e
LP
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;
1716f6dc 54 p->protocol = protocol;
74b2466e
LP
55 p->n_ref = 1;
56
57 *ret = p;
58
59 return 0;
60}
61
1716f6dc 62int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
74b2466e
LP
63 DnsPacket *p;
64 DnsPacketHeader *h;
65 int r;
66
67 assert(ret);
68
1716f6dc 69 r = dns_packet_new(&p, protocol, mtu);
74b2466e
LP
70 if (r < 0)
71 return r;
72
73 h = DNS_PACKET_HEADER(p);
1716f6dc 74
ea917db9
LP
75 if (protocol == DNS_PROTOCOL_LLMNR)
76 h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */,
77 0 /* opcode */,
78 0 /* c */,
79 0 /* tc */,
80 0 /* t */,
81 0 /* ra */,
82 0 /* ad */,
83 0 /* cd */,
84 0 /* rcode */));
1716f6dc 85 else
ea917db9
LP
86 h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */,
87 0 /* opcode */,
88 0 /* aa */,
89 0 /* tc */,
90 1 /* rd (ask for recursion) */,
91 0 /* ra */,
92 0 /* ad */,
93 0 /* cd */,
94 0 /* rcode */));
74b2466e
LP
95
96 *ret = p;
97 return 0;
98}
99
100DnsPacket *dns_packet_ref(DnsPacket *p) {
101
102 if (!p)
103 return NULL;
104
105 assert(p->n_ref > 0);
106 p->n_ref++;
107 return p;
108}
109
110static void dns_packet_free(DnsPacket *p) {
111 char *s;
112
113 assert(p);
114
faa133f3
LP
115 dns_question_unref(p->question);
116 dns_answer_unref(p->answer);
322345fd 117
74b2466e
LP
118 while ((s = hashmap_steal_first_key(p->names)))
119 free(s);
120 hashmap_free(p->names);
121
faa133f3 122 free(p->_data);
74b2466e
LP
123 free(p);
124}
125
126DnsPacket *dns_packet_unref(DnsPacket *p) {
127 if (!p)
128 return NULL;
129
130 assert(p->n_ref > 0);
131
132 if (p->n_ref == 1)
133 dns_packet_free(p);
134 else
135 p->n_ref--;
136
137 return NULL;
138}
139
140int dns_packet_validate(DnsPacket *p) {
141 assert(p);
142
143 if (p->size < DNS_PACKET_HEADER_SIZE)
144 return -EBADMSG;
145
c73ce96b
LP
146 if (p->size > DNS_PACKET_SIZE_MAX)
147 return -EBADMSG;
148
623a4c97 149 return 1;
74b2466e
LP
150}
151
152int dns_packet_validate_reply(DnsPacket *p) {
74b2466e
LP
153 int r;
154
155 assert(p);
156
157 r = dns_packet_validate(p);
158 if (r < 0)
159 return r;
160
623a4c97
LP
161 if (DNS_PACKET_QR(p) != 1)
162 return 0;
163
164 if (DNS_PACKET_OPCODE(p) != 0)
74b2466e
LP
165 return -EBADMSG;
166
ea917db9
LP
167 /* RFC 4795, Section 2.1.1. says to discard all replies with QDCOUNT != 1 */
168 if (p->protocol == DNS_PROTOCOL_LLMNR &&
169 DNS_PACKET_QDCOUNT(p) != 1)
170 return -EBADMSG;
171
623a4c97
LP
172 return 1;
173}
174
175int dns_packet_validate_query(DnsPacket *p) {
176 int r;
177
178 assert(p);
179
180 r = dns_packet_validate(p);
181 if (r < 0)
182 return r;
183
184 if (DNS_PACKET_QR(p) != 0)
185 return 0;
186
3cb10d3a 187 if (DNS_PACKET_OPCODE(p) != 0)
74b2466e
LP
188 return -EBADMSG;
189
623a4c97
LP
190 if (DNS_PACKET_TC(p))
191 return -EBADMSG;
192
ea917db9 193 /* RFC 4795, Section 2.1.1. says to discard all queries with QDCOUNT != 1 */
623a4c97
LP
194 if (p->protocol == DNS_PROTOCOL_LLMNR &&
195 DNS_PACKET_QDCOUNT(p) != 1)
196 return -EBADMSG;
197
ea917db9 198 /* RFC 4795, Section 2.1.1. says to discard all queries with ANCOUNT != 0 */
623a4c97
LP
199 if (DNS_PACKET_ANCOUNT(p) > 0)
200 return -EBADMSG;
201
ea917db9 202 /* RFC 4795, Section 2.1.1. says to discard all queries with NSCOUNT != 0 */
623a4c97
LP
203 if (DNS_PACKET_NSCOUNT(p) > 0)
204 return -EBADMSG;
205
206 return 1;
74b2466e
LP
207}
208
209static int dns_packet_extend(DnsPacket *p, size_t add, void **ret, size_t *start) {
210 assert(p);
211
c73ce96b
LP
212 if (p->size + add > p->allocated) {
213 size_t a;
214
215 a = PAGE_ALIGN((p->size + add) * 2);
216 if (a > DNS_PACKET_SIZE_MAX)
217 a = DNS_PACKET_SIZE_MAX;
218
219 if (p->size + add > a)
220 return -EMSGSIZE;
221
faa133f3 222 if (p->_data) {
c73ce96b
LP
223 void *d;
224
faa133f3 225 d = realloc(p->_data, a);
c73ce96b
LP
226 if (!d)
227 return -ENOMEM;
228
faa133f3 229 p->_data = d;
c73ce96b 230 } else {
faa133f3
LP
231 p->_data = malloc(a);
232 if (!p->_data)
c73ce96b
LP
233 return -ENOMEM;
234
faa133f3
LP
235 memcpy(p->_data, (uint8_t*) p + ALIGN(sizeof(DnsPacket)), p->size);
236 memzero((uint8_t*) p->_data + p->size, a - p->size);
c73ce96b
LP
237 }
238
239 p->allocated = a;
240 }
74b2466e
LP
241
242 if (start)
243 *start = p->size;
244
245 if (ret)
246 *ret = (uint8_t*) DNS_PACKET_DATA(p) + p->size;
247
248 p->size += add;
249 return 0;
250}
251
252static void dns_packet_truncate(DnsPacket *p, size_t sz) {
253 Iterator i;
254 char *s;
255 void *n;
256
257 assert(p);
258
259 if (p->size <= sz)
260 return;
261
262 HASHMAP_FOREACH_KEY(s, n, p->names, i) {
263
264 if (PTR_TO_SIZE(n) < sz)
265 continue;
266
267 hashmap_remove(p->names, s);
268 free(s);
269 }
270
271 p->size = sz;
272}
273
623a4c97
LP
274int dns_packet_append_blob(DnsPacket *p, const void *d, size_t l, size_t *start) {
275 void *q;
276 int r;
277
278 assert(p);
279
280 r = dns_packet_extend(p, l, &q, start);
281 if (r < 0)
282 return r;
283
284 memcpy(q, d, l);
285 return 0;
286}
287
74b2466e
LP
288int dns_packet_append_uint8(DnsPacket *p, uint8_t v, size_t *start) {
289 void *d;
290 int r;
291
292 assert(p);
293
294 r = dns_packet_extend(p, sizeof(uint8_t), &d, start);
295 if (r < 0)
296 return r;
297
298 ((uint8_t*) d)[0] = v;
299
300 return 0;
301}
302
303int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start) {
304 void *d;
305 int r;
306
307 assert(p);
308
309 r = dns_packet_extend(p, sizeof(uint16_t), &d, start);
310 if (r < 0)
311 return r;
312
313 ((uint8_t*) d)[0] = (uint8_t) (v >> 8);
623a4c97
LP
314 ((uint8_t*) d)[1] = (uint8_t) v;
315
316 return 0;
317}
318
319int dns_packet_append_uint32(DnsPacket *p, uint32_t v, size_t *start) {
320 void *d;
321 int r;
322
323 assert(p);
324
325 r = dns_packet_extend(p, sizeof(uint32_t), &d, start);
326 if (r < 0)
327 return r;
328
329 ((uint8_t*) d)[0] = (uint8_t) (v >> 24);
330 ((uint8_t*) d)[1] = (uint8_t) (v >> 16);
331 ((uint8_t*) d)[2] = (uint8_t) (v >> 8);
332 ((uint8_t*) d)[3] = (uint8_t) v;
74b2466e
LP
333
334 return 0;
335}
336
337int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start) {
338 void *d;
339 size_t l;
340 int r;
341
342 assert(p);
343 assert(s);
344
345 l = strlen(s);
346 if (l > 255)
347 return -E2BIG;
348
349 r = dns_packet_extend(p, 1 + l, &d, start);
350 if (r < 0)
351 return r;
352
353 ((uint8_t*) d)[0] = (uint8_t) l;
354 memcpy(((uint8_t*) d) + 1, s, l);
355
356 return 0;
357}
358
359int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start) {
360 void *w;
361 int r;
362
363 assert(p);
364 assert(d);
365
366 if (l > DNS_LABEL_MAX)
367 return -E2BIG;
368
369 r = dns_packet_extend(p, 1 + l, &w, start);
370 if (r < 0)
371 return r;
372
373 ((uint8_t*) w)[0] = (uint8_t) l;
374 memcpy(((uint8_t*) w) + 1, d, l);
375
376 return 0;
377}
378
379int dns_packet_append_name(DnsPacket *p, const char *name, size_t *start) {
380 size_t saved_size;
381 int r;
382
383 assert(p);
384 assert(name);
385
386 saved_size = p->size;
387
388 while (*name) {
389 _cleanup_free_ char *s = NULL;
390 char label[DNS_LABEL_MAX];
391 size_t n;
392
393 n = PTR_TO_SIZE(hashmap_get(p->names, name));
394 if (n > 0) {
395 assert(n < p->size);
396
397 if (n < 0x4000) {
398 r = dns_packet_append_uint16(p, 0xC000 | n, NULL);
399 if (r < 0)
400 goto fail;
401
402 goto done;
403 }
404 }
405
406 s = strdup(name);
407 if (!s) {
408 r = -ENOMEM;
409 goto fail;
410 }
411
412 r = dns_label_unescape(&name, label, sizeof(label));
413 if (r < 0)
414 goto fail;
415
416 r = dns_packet_append_label(p, label, r, &n);
417 if (r < 0)
418 goto fail;
419
420 r = hashmap_ensure_allocated(&p->names, dns_name_hash_func, dns_name_compare_func);
421 if (r < 0)
422 goto fail;
423
424 r = hashmap_put(p->names, s, SIZE_TO_PTR(n));
425 if (r < 0)
426 goto fail;
427
428 s = NULL;
429 }
430
431 r = dns_packet_append_uint8(p, 0, NULL);
432 if (r < 0)
433 return r;
434
435done:
436 if (start)
437 *start = saved_size;
438
439 return 0;
440
441fail:
442 dns_packet_truncate(p, saved_size);
443 return r;
444}
445
446int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start) {
447 size_t saved_size;
448 int r;
449
450 assert(p);
451 assert(k);
452
453 saved_size = p->size;
454
faa133f3 455 r = dns_packet_append_name(p, DNS_RESOURCE_KEY_NAME(k), NULL);
74b2466e
LP
456 if (r < 0)
457 goto fail;
458
459 r = dns_packet_append_uint16(p, k->type, NULL);
460 if (r < 0)
461 goto fail;
462
463 r = dns_packet_append_uint16(p, k->class, NULL);
464 if (r < 0)
465 goto fail;
466
467 if (start)
468 *start = saved_size;
469
470 return 0;
471
472fail:
473 dns_packet_truncate(p, saved_size);
474 return r;
475}
476
623a4c97
LP
477int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start) {
478 size_t saved_size, rdlength_offset, end, rdlength;
479 int r;
480
481 assert(p);
482 assert(rr);
483
484 saved_size = p->size;
485
486 r = dns_packet_append_key(p, rr->key, NULL);
487 if (r < 0)
488 goto fail;
489
490 r = dns_packet_append_uint32(p, rr->ttl, NULL);
491 if (r < 0)
492 goto fail;
493
494 /* Initially we write 0 here */
495 r = dns_packet_append_uint16(p, 0, &rdlength_offset);
496 if (r < 0)
497 goto fail;
498
499 switch (rr->key->type) {
500
501 case DNS_TYPE_PTR:
502 case DNS_TYPE_NS:
503 case DNS_TYPE_CNAME:
504 r = dns_packet_append_name(p, rr->ptr.name, NULL);
505 break;
506
507 case DNS_TYPE_HINFO:
508 r = dns_packet_append_string(p, rr->hinfo.cpu, NULL);
509 if (r < 0)
510 goto fail;
511
512 r = dns_packet_append_string(p, rr->hinfo.os, NULL);
513 break;
514
515 case DNS_TYPE_A:
516 r = dns_packet_append_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL);
517 break;
518
519 case DNS_TYPE_AAAA:
520 r = dns_packet_append_blob(p, &rr->aaaa.in6_addr, sizeof(struct in6_addr), NULL);
521 break;
522
523 case DNS_TYPE_SOA:
524 r = dns_packet_append_name(p, rr->soa.mname, NULL);
525 if (r < 0)
526 goto fail;
527
528 r = dns_packet_append_name(p, rr->soa.rname, NULL);
529 if (r < 0)
530 goto fail;
531
532 r = dns_packet_append_uint32(p, rr->soa.serial, NULL);
533 if (r < 0)
534 goto fail;
535
536 r = dns_packet_append_uint32(p, rr->soa.refresh, NULL);
537 if (r < 0)
538 goto fail;
539
540 r = dns_packet_append_uint32(p, rr->soa.retry, NULL);
541 if (r < 0)
542 goto fail;
543
544 r = dns_packet_append_uint32(p, rr->soa.expire, NULL);
545 if (r < 0)
546 goto fail;
547
548 r = dns_packet_append_uint32(p, rr->soa.minimum, NULL);
549 break;
550
551 case DNS_TYPE_MX:
946c7094
ZJS
552 r = dns_packet_append_uint16(p, rr->mx.priority, NULL);
553 if (r < 0)
554 goto fail;
555
556 r = dns_packet_append_name(p, rr->mx.exchange, NULL);
557 break;
558
623a4c97
LP
559 case DNS_TYPE_TXT:
560 case DNS_TYPE_SRV:
561 case DNS_TYPE_DNAME:
562 case DNS_TYPE_SSHFP:
563 default:
564 r = dns_packet_append_blob(p, rr->generic.data, rr->generic.size, NULL);
565 break;
566 }
567 if (r < 0)
568 goto fail;
569
570 /* Let's calculate the actual data size and update the field */
571 rdlength = p->size - rdlength_offset - sizeof(uint16_t);
572 if (rdlength > 0xFFFF) {
573 r = ENOSPC;
574 goto fail;
575 }
576
577 end = p->size;
578 p->size = rdlength_offset;
579 r = dns_packet_append_uint16(p, rdlength, NULL);
580 if (r < 0)
581 goto fail;
582 p->size = end;
583
351e6342
LP
584 if (start)
585 *start = saved_size;
586
623a4c97
LP
587 return 0;
588
589fail:
590 dns_packet_truncate(p, saved_size);
591 return r;
592}
593
594
74b2466e
LP
595int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start) {
596 assert(p);
597
598 if (p->rindex + sz > p->size)
599 return -EMSGSIZE;
600
601 if (ret)
602 *ret = (uint8_t*) DNS_PACKET_DATA(p) + p->rindex;
603
604 if (start)
605 *start = p->rindex;
606
607 p->rindex += sz;
608 return 0;
609}
610
8ba9fd9c 611void dns_packet_rewind(DnsPacket *p, size_t idx) {
74b2466e
LP
612 assert(p);
613 assert(idx <= p->size);
614 assert(idx >= DNS_PACKET_HEADER_SIZE);
615
616 p->rindex = idx;
617}
618
623a4c97
LP
619int dns_packet_read_blob(DnsPacket *p, void *d, size_t sz, size_t *start) {
620 const void *q;
621 int r;
622
623 assert(p);
624 assert(d);
625
626 r = dns_packet_read(p, sz, &q, start);
627 if (r < 0)
628 return r;
629
630 memcpy(d, q, sz);
631 return 0;
632}
633
74b2466e
LP
634int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start) {
635 const void *d;
636 int r;
637
638 assert(p);
639
640 r = dns_packet_read(p, sizeof(uint8_t), &d, start);
641 if (r < 0)
642 return r;
643
644 *ret = ((uint8_t*) d)[0];
645 return 0;
646}
647
648int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start) {
649 const void *d;
650 int r;
651
652 assert(p);
653
654 r = dns_packet_read(p, sizeof(uint16_t), &d, start);
655 if (r < 0)
656 return r;
657
658 *ret = (((uint16_t) ((uint8_t*) d)[0]) << 8) |
659 ((uint16_t) ((uint8_t*) d)[1]);
660 return 0;
661}
662
663int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) {
664 const void *d;
665 int r;
666
667 assert(p);
668
669 r = dns_packet_read(p, sizeof(uint32_t), &d, start);
670 if (r < 0)
671 return r;
672
673 *ret = (((uint32_t) ((uint8_t*) d)[0]) << 24) |
674 (((uint32_t) ((uint8_t*) d)[1]) << 16) |
675 (((uint32_t) ((uint8_t*) d)[2]) << 8) |
676 ((uint32_t) ((uint8_t*) d)[3]);
677
678 return 0;
679}
680
681int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) {
682 size_t saved_rindex;
683 const void *d;
684 char *t;
685 uint8_t c;
686 int r;
687
688 assert(p);
689
690 saved_rindex = p->rindex;
691
692 r = dns_packet_read_uint8(p, &c, NULL);
693 if (r < 0)
694 goto fail;
695
696 r = dns_packet_read(p, c, &d, NULL);
697 if (r < 0)
698 goto fail;
699
700 if (memchr(d, 0, c)) {
701 r = -EBADMSG;
702 goto fail;
703 }
704
705 t = strndup(d, c);
706 if (!t) {
707 r = -ENOMEM;
708 goto fail;
709 }
710
711 if (!utf8_is_valid(t)) {
712 free(t);
713 r = -EBADMSG;
714 goto fail;
715 }
716
717 *ret = t;
718
719 if (start)
720 *start = saved_rindex;
721
722 return 0;
723
724fail:
725 dns_packet_rewind(p, saved_rindex);
726 return r;
727}
728
729int dns_packet_read_name(DnsPacket *p, char **_ret, size_t *start) {
730 size_t saved_rindex, after_rindex = 0;
731 _cleanup_free_ char *ret = NULL;
732 size_t n = 0, allocated = 0;
733 bool first = true;
734 int r;
735
736 assert(p);
737 assert(_ret);
738
739 saved_rindex = p->rindex;
740
741 for (;;) {
742 uint8_t c, d;
743
744 r = dns_packet_read_uint8(p, &c, NULL);
745 if (r < 0)
746 goto fail;
747
748 if (c == 0)
749 /* End of name */
750 break;
751 else if (c <= 63) {
752 _cleanup_free_ char *t = NULL;
753 const char *label;
754
755 /* Literal label */
756 r = dns_packet_read(p, c, (const void**) &label, NULL);
757 if (r < 0)
758 goto fail;
759
760 r = dns_label_escape(label, c, &t);
761 if (r < 0)
762 goto fail;
763
764 if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) {
765 r = -ENOMEM;
766 goto fail;
767 }
768
769 if (!first)
770 ret[n++] = '.';
771 else
772 first = false;
773
774 memcpy(ret + n, t, c);
775 n += r;
776 continue;
777 } else if ((c & 0xc0) == 0xc0) {
778 uint16_t ptr;
779
780 /* Pointer */
781 r = dns_packet_read_uint8(p, &d, NULL);
782 if (r < 0)
783 goto fail;
784
785 ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d;
786 if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= saved_rindex) {
787 r = -EBADMSG;
788 goto fail;
789 }
790
791 if (after_rindex == 0)
792 after_rindex = p->rindex;
793
794 p->rindex = ptr;
795 } else
796 goto fail;
797 }
798
799 if (!GREEDY_REALLOC(ret, allocated, n + 1)) {
800 r = -ENOMEM;
801 goto fail;
802 }
803
804 ret[n] = 0;
805
806 if (after_rindex != 0)
807 p->rindex= after_rindex;
808
809 *_ret = ret;
810 ret = NULL;
811
812 if (start)
813 *start = saved_rindex;
814
815 return 0;
816
817fail:
818 dns_packet_rewind(p, saved_rindex);
819 return r;
820}
821
faa133f3
LP
822int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
823 _cleanup_free_ char *name = NULL;
824 uint16_t class, type;
825 DnsResourceKey *key;
74b2466e
LP
826 size_t saved_rindex;
827 int r;
828
829 assert(p);
830 assert(ret);
831
832 saved_rindex = p->rindex;
833
faa133f3 834 r = dns_packet_read_name(p, &name, NULL);
74b2466e
LP
835 if (r < 0)
836 goto fail;
837
faa133f3 838 r = dns_packet_read_uint16(p, &type, NULL);
74b2466e
LP
839 if (r < 0)
840 goto fail;
841
faa133f3 842 r = dns_packet_read_uint16(p, &class, NULL);
74b2466e
LP
843 if (r < 0)
844 goto fail;
845
faa133f3
LP
846 key = dns_resource_key_new_consume(class, type, name);
847 if (!key) {
848 r = -ENOMEM;
849 goto fail;
850 }
851
852 name = NULL;
853 *ret = key;
74b2466e
LP
854
855 if (start)
856 *start = saved_rindex;
857
858 return 0;
859fail:
860 dns_packet_rewind(p, saved_rindex);
861 return r;
862}
863
864int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
faa133f3
LP
865 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
866 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
74b2466e
LP
867 size_t saved_rindex, offset;
868 uint16_t rdlength;
869 const void *d;
870 int r;
871
872 assert(p);
873 assert(ret);
874
4e0296a9 875 saved_rindex = p->rindex;
74b2466e 876
faa133f3 877 r = dns_packet_read_key(p, &key, NULL);
74b2466e
LP
878 if (r < 0)
879 goto fail;
880
0e2bcd6a
LP
881 if (key->class == DNS_CLASS_ANY ||
882 key->type == DNS_TYPE_ANY) {
883 r = -EBADMSG;
884 goto fail;
885 }
886
faa133f3
LP
887 rr = dns_resource_record_new(key);
888 if (!rr) {
889 r = -ENOMEM;
890 goto fail;
891 }
892
74b2466e
LP
893 r = dns_packet_read_uint32(p, &rr->ttl, NULL);
894 if (r < 0)
895 goto fail;
896
897 r = dns_packet_read_uint16(p, &rdlength, NULL);
898 if (r < 0)
899 goto fail;
900
901 if (p->rindex + rdlength > p->size) {
902 r = -EBADMSG;
903 goto fail;
904 }
905
906 offset = p->rindex;
907
faa133f3 908 switch (rr->key->type) {
74b2466e
LP
909
910 case DNS_TYPE_PTR:
911 case DNS_TYPE_NS:
912 case DNS_TYPE_CNAME:
913 r = dns_packet_read_name(p, &rr->ptr.name, NULL);
914 break;
915
916 case DNS_TYPE_HINFO:
917 r = dns_packet_read_string(p, &rr->hinfo.cpu, NULL);
918 if (r < 0)
919 goto fail;
920
921 r = dns_packet_read_string(p, &rr->hinfo.os, NULL);
922 break;
923
924 case DNS_TYPE_A:
623a4c97 925 r = dns_packet_read_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL);
74b2466e
LP
926 break;
927
928 case DNS_TYPE_AAAA:
623a4c97 929 r = dns_packet_read_blob(p, &rr->aaaa.in6_addr, sizeof(struct in6_addr), NULL);
74b2466e
LP
930 break;
931
7e8e0422
LP
932 case DNS_TYPE_SOA:
933 r = dns_packet_read_name(p, &rr->soa.mname, NULL);
934 if (r < 0)
935 goto fail;
936
937 r = dns_packet_read_name(p, &rr->soa.rname, NULL);
938 if (r < 0)
939 goto fail;
940
941 r = dns_packet_read_uint32(p, &rr->soa.serial, NULL);
942 if (r < 0)
943 goto fail;
944
945 r = dns_packet_read_uint32(p, &rr->soa.refresh, NULL);
946 if (r < 0)
947 goto fail;
948
949 r = dns_packet_read_uint32(p, &rr->soa.retry, NULL);
950 if (r < 0)
951 goto fail;
952
953 r = dns_packet_read_uint32(p, &rr->soa.expire, NULL);
954 if (r < 0)
955 goto fail;
956
957 r = dns_packet_read_uint32(p, &rr->soa.minimum, NULL);
958 break;
959
623a4c97 960 case DNS_TYPE_MX:
946c7094
ZJS
961 r = dns_packet_read_uint16(p, &rr->mx.priority, NULL);
962 if (r < 0)
963 goto fail;
964
965 r = dns_packet_read_name(p, &rr->mx.exchange, NULL);
966 break;
967
623a4c97
LP
968 case DNS_TYPE_TXT:
969 case DNS_TYPE_SRV:
970 case DNS_TYPE_DNAME:
971 case DNS_TYPE_SSHFP:
74b2466e
LP
972 default:
973 r = dns_packet_read(p, rdlength, &d, NULL);
974 if (r < 0)
975 goto fail;
976
977 rr->generic.data = memdup(d, rdlength);
978 if (!rr->generic.data) {
979 r = -ENOMEM;
980 goto fail;
981 }
982
983 rr->generic.size = rdlength;
984 break;
985 }
986 if (r < 0)
987 goto fail;
988 if (p->rindex != offset + rdlength) {
989 r = -EBADMSG;
990 goto fail;
991 }
992
993 *ret = rr;
994 rr = NULL;
995
996 if (start)
997 *start = saved_rindex;
998
999 return 0;
1000fail:
1001 dns_packet_rewind(p, saved_rindex);
1002 return r;
1003}
1004
faa133f3
LP
1005int dns_packet_extract(DnsPacket *p) {
1006 _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
1007 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
1008 size_t saved_rindex;
1009 unsigned n, i;
74b2466e
LP
1010 int r;
1011
faa133f3 1012 saved_rindex = p->rindex;
322345fd
LP
1013 dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE);
1014
3cb10d3a 1015 n = DNS_PACKET_QDCOUNT(p);
faa133f3
LP
1016 if (n > 0) {
1017 question = dns_question_new(n);
1018 if (!question) {
1019 r = -ENOMEM;
1020 goto finish;
1021 }
74b2466e 1022
faa133f3
LP
1023 for (i = 0; i < n; i++) {
1024 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
74b2466e 1025
faa133f3
LP
1026 r = dns_packet_read_key(p, &key, NULL);
1027 if (r < 0)
1028 goto finish;
74b2466e 1029
faa133f3
LP
1030 r = dns_question_add(question, key);
1031 if (r < 0)
1032 goto finish;
1033 }
1034 }
322345fd 1035
faa133f3
LP
1036 n = DNS_PACKET_RRCOUNT(p);
1037 if (n > 0) {
1038 answer = dns_answer_new(n);
1039 if (!answer) {
1040 r = -ENOMEM;
1041 goto finish;
1042 }
322345fd 1043
faa133f3
LP
1044 for (i = 0; i < n; i++) {
1045 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
322345fd 1046
faa133f3
LP
1047 r = dns_packet_read_rr(p, &rr, NULL);
1048 if (r < 0)
1049 goto finish;
322345fd 1050
faa133f3
LP
1051 r = dns_answer_add(answer, rr);
1052 if (r < 0)
1053 goto finish;
1054 }
322345fd
LP
1055 }
1056
faa133f3
LP
1057 p->question = question;
1058 question = NULL;
322345fd 1059
faa133f3
LP
1060 p->answer = answer;
1061 answer = NULL;
322345fd 1062
faa133f3 1063 r = 0;
322345fd
LP
1064
1065finish:
1066 p->rindex = saved_rindex;
1067 return r;
1068}
1069
74b2466e
LP
1070static const char* const dns_rcode_table[_DNS_RCODE_MAX_DEFINED] = {
1071 [DNS_RCODE_SUCCESS] = "SUCCESS",
1072 [DNS_RCODE_FORMERR] = "FORMERR",
1073 [DNS_RCODE_SERVFAIL] = "SERVFAIL",
1074 [DNS_RCODE_NXDOMAIN] = "NXDOMAIN",
1075 [DNS_RCODE_NOTIMP] = "NOTIMP",
1076 [DNS_RCODE_REFUSED] = "REFUSED",
1077 [DNS_RCODE_YXDOMAIN] = "YXDOMAIN",
1078 [DNS_RCODE_YXRRSET] = "YRRSET",
1079 [DNS_RCODE_NXRRSET] = "NXRRSET",
1080 [DNS_RCODE_NOTAUTH] = "NOTAUTH",
1081 [DNS_RCODE_NOTZONE] = "NOTZONE",
1082 [DNS_RCODE_BADVERS] = "BADVERS",
1083 [DNS_RCODE_BADKEY] = "BADKEY",
1084 [DNS_RCODE_BADTIME] = "BADTIME",
1085 [DNS_RCODE_BADMODE] = "BADMODE",
1086 [DNS_RCODE_BADNAME] = "BADNAME",
1087 [DNS_RCODE_BADALG] = "BADALG",
1088 [DNS_RCODE_BADTRUNC] = "BADTRUNC",
1089};
1090DEFINE_STRING_TABLE_LOOKUP(dns_rcode, int);
1716f6dc
LP
1091
1092static const char* const dns_protocol_table[_DNS_PROTOCOL_MAX] = {
1093 [DNS_PROTOCOL_DNS] = "dns",
1094 [DNS_PROTOCOL_MDNS] = "mdns",
1095 [DNS_PROTOCOL_LLMNR] = "llmnr",
1096};
1097DEFINE_STRING_TABLE_LOOKUP(dns_protocol, DnsProtocol);