]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-packet.c
resolved: add API for resolving specific RRs
[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:
552 case DNS_TYPE_TXT:
553 case DNS_TYPE_SRV:
554 case DNS_TYPE_DNAME:
555 case DNS_TYPE_SSHFP:
556 default:
557 r = dns_packet_append_blob(p, rr->generic.data, rr->generic.size, NULL);
558 break;
559 }
560 if (r < 0)
561 goto fail;
562
563 /* Let's calculate the actual data size and update the field */
564 rdlength = p->size - rdlength_offset - sizeof(uint16_t);
565 if (rdlength > 0xFFFF) {
566 r = ENOSPC;
567 goto fail;
568 }
569
570 end = p->size;
571 p->size = rdlength_offset;
572 r = dns_packet_append_uint16(p, rdlength, NULL);
573 if (r < 0)
574 goto fail;
575 p->size = end;
576
577 return 0;
578
579fail:
580 dns_packet_truncate(p, saved_size);
581 return r;
582}
583
584
74b2466e
LP
585int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start) {
586 assert(p);
587
588 if (p->rindex + sz > p->size)
589 return -EMSGSIZE;
590
591 if (ret)
592 *ret = (uint8_t*) DNS_PACKET_DATA(p) + p->rindex;
593
594 if (start)
595 *start = p->rindex;
596
597 p->rindex += sz;
598 return 0;
599}
600
8ba9fd9c 601void dns_packet_rewind(DnsPacket *p, size_t idx) {
74b2466e
LP
602 assert(p);
603 assert(idx <= p->size);
604 assert(idx >= DNS_PACKET_HEADER_SIZE);
605
606 p->rindex = idx;
607}
608
623a4c97
LP
609int dns_packet_read_blob(DnsPacket *p, void *d, size_t sz, size_t *start) {
610 const void *q;
611 int r;
612
613 assert(p);
614 assert(d);
615
616 r = dns_packet_read(p, sz, &q, start);
617 if (r < 0)
618 return r;
619
620 memcpy(d, q, sz);
621 return 0;
622}
623
74b2466e
LP
624int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start) {
625 const void *d;
626 int r;
627
628 assert(p);
629
630 r = dns_packet_read(p, sizeof(uint8_t), &d, start);
631 if (r < 0)
632 return r;
633
634 *ret = ((uint8_t*) d)[0];
635 return 0;
636}
637
638int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start) {
639 const void *d;
640 int r;
641
642 assert(p);
643
644 r = dns_packet_read(p, sizeof(uint16_t), &d, start);
645 if (r < 0)
646 return r;
647
648 *ret = (((uint16_t) ((uint8_t*) d)[0]) << 8) |
649 ((uint16_t) ((uint8_t*) d)[1]);
650 return 0;
651}
652
653int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) {
654 const void *d;
655 int r;
656
657 assert(p);
658
659 r = dns_packet_read(p, sizeof(uint32_t), &d, start);
660 if (r < 0)
661 return r;
662
663 *ret = (((uint32_t) ((uint8_t*) d)[0]) << 24) |
664 (((uint32_t) ((uint8_t*) d)[1]) << 16) |
665 (((uint32_t) ((uint8_t*) d)[2]) << 8) |
666 ((uint32_t) ((uint8_t*) d)[3]);
667
668 return 0;
669}
670
671int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) {
672 size_t saved_rindex;
673 const void *d;
674 char *t;
675 uint8_t c;
676 int r;
677
678 assert(p);
679
680 saved_rindex = p->rindex;
681
682 r = dns_packet_read_uint8(p, &c, NULL);
683 if (r < 0)
684 goto fail;
685
686 r = dns_packet_read(p, c, &d, NULL);
687 if (r < 0)
688 goto fail;
689
690 if (memchr(d, 0, c)) {
691 r = -EBADMSG;
692 goto fail;
693 }
694
695 t = strndup(d, c);
696 if (!t) {
697 r = -ENOMEM;
698 goto fail;
699 }
700
701 if (!utf8_is_valid(t)) {
702 free(t);
703 r = -EBADMSG;
704 goto fail;
705 }
706
707 *ret = t;
708
709 if (start)
710 *start = saved_rindex;
711
712 return 0;
713
714fail:
715 dns_packet_rewind(p, saved_rindex);
716 return r;
717}
718
719int dns_packet_read_name(DnsPacket *p, char **_ret, size_t *start) {
720 size_t saved_rindex, after_rindex = 0;
721 _cleanup_free_ char *ret = NULL;
722 size_t n = 0, allocated = 0;
723 bool first = true;
724 int r;
725
726 assert(p);
727 assert(_ret);
728
729 saved_rindex = p->rindex;
730
731 for (;;) {
732 uint8_t c, d;
733
734 r = dns_packet_read_uint8(p, &c, NULL);
735 if (r < 0)
736 goto fail;
737
738 if (c == 0)
739 /* End of name */
740 break;
741 else if (c <= 63) {
742 _cleanup_free_ char *t = NULL;
743 const char *label;
744
745 /* Literal label */
746 r = dns_packet_read(p, c, (const void**) &label, NULL);
747 if (r < 0)
748 goto fail;
749
750 r = dns_label_escape(label, c, &t);
751 if (r < 0)
752 goto fail;
753
754 if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) {
755 r = -ENOMEM;
756 goto fail;
757 }
758
759 if (!first)
760 ret[n++] = '.';
761 else
762 first = false;
763
764 memcpy(ret + n, t, c);
765 n += r;
766 continue;
767 } else if ((c & 0xc0) == 0xc0) {
768 uint16_t ptr;
769
770 /* Pointer */
771 r = dns_packet_read_uint8(p, &d, NULL);
772 if (r < 0)
773 goto fail;
774
775 ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d;
776 if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= saved_rindex) {
777 r = -EBADMSG;
778 goto fail;
779 }
780
781 if (after_rindex == 0)
782 after_rindex = p->rindex;
783
784 p->rindex = ptr;
785 } else
786 goto fail;
787 }
788
789 if (!GREEDY_REALLOC(ret, allocated, n + 1)) {
790 r = -ENOMEM;
791 goto fail;
792 }
793
794 ret[n] = 0;
795
796 if (after_rindex != 0)
797 p->rindex= after_rindex;
798
799 *_ret = ret;
800 ret = NULL;
801
802 if (start)
803 *start = saved_rindex;
804
805 return 0;
806
807fail:
808 dns_packet_rewind(p, saved_rindex);
809 return r;
810}
811
faa133f3
LP
812int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
813 _cleanup_free_ char *name = NULL;
814 uint16_t class, type;
815 DnsResourceKey *key;
74b2466e
LP
816 size_t saved_rindex;
817 int r;
818
819 assert(p);
820 assert(ret);
821
822 saved_rindex = p->rindex;
823
faa133f3 824 r = dns_packet_read_name(p, &name, NULL);
74b2466e
LP
825 if (r < 0)
826 goto fail;
827
faa133f3 828 r = dns_packet_read_uint16(p, &type, NULL);
74b2466e
LP
829 if (r < 0)
830 goto fail;
831
faa133f3 832 r = dns_packet_read_uint16(p, &class, NULL);
74b2466e
LP
833 if (r < 0)
834 goto fail;
835
faa133f3
LP
836 key = dns_resource_key_new_consume(class, type, name);
837 if (!key) {
838 r = -ENOMEM;
839 goto fail;
840 }
841
842 name = NULL;
843 *ret = key;
74b2466e
LP
844
845 if (start)
846 *start = saved_rindex;
847
848 return 0;
849fail:
850 dns_packet_rewind(p, saved_rindex);
851 return r;
852}
853
854int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
faa133f3
LP
855 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
856 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
74b2466e
LP
857 size_t saved_rindex, offset;
858 uint16_t rdlength;
859 const void *d;
860 int r;
861
862 assert(p);
863 assert(ret);
864
4e0296a9 865 saved_rindex = p->rindex;
74b2466e 866
faa133f3 867 r = dns_packet_read_key(p, &key, NULL);
74b2466e
LP
868 if (r < 0)
869 goto fail;
870
0e2bcd6a
LP
871 if (key->class == DNS_CLASS_ANY ||
872 key->type == DNS_TYPE_ANY) {
873 r = -EBADMSG;
874 goto fail;
875 }
876
faa133f3
LP
877 rr = dns_resource_record_new(key);
878 if (!rr) {
879 r = -ENOMEM;
880 goto fail;
881 }
882
74b2466e
LP
883 r = dns_packet_read_uint32(p, &rr->ttl, NULL);
884 if (r < 0)
885 goto fail;
886
887 r = dns_packet_read_uint16(p, &rdlength, NULL);
888 if (r < 0)
889 goto fail;
890
891 if (p->rindex + rdlength > p->size) {
892 r = -EBADMSG;
893 goto fail;
894 }
895
896 offset = p->rindex;
897
faa133f3 898 switch (rr->key->type) {
74b2466e
LP
899
900 case DNS_TYPE_PTR:
901 case DNS_TYPE_NS:
902 case DNS_TYPE_CNAME:
903 r = dns_packet_read_name(p, &rr->ptr.name, NULL);
904 break;
905
906 case DNS_TYPE_HINFO:
907 r = dns_packet_read_string(p, &rr->hinfo.cpu, NULL);
908 if (r < 0)
909 goto fail;
910
911 r = dns_packet_read_string(p, &rr->hinfo.os, NULL);
912 break;
913
914 case DNS_TYPE_A:
623a4c97 915 r = dns_packet_read_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL);
74b2466e
LP
916 break;
917
918 case DNS_TYPE_AAAA:
623a4c97 919 r = dns_packet_read_blob(p, &rr->aaaa.in6_addr, sizeof(struct in6_addr), NULL);
74b2466e
LP
920 break;
921
7e8e0422
LP
922 case DNS_TYPE_SOA:
923 r = dns_packet_read_name(p, &rr->soa.mname, NULL);
924 if (r < 0)
925 goto fail;
926
927 r = dns_packet_read_name(p, &rr->soa.rname, NULL);
928 if (r < 0)
929 goto fail;
930
931 r = dns_packet_read_uint32(p, &rr->soa.serial, NULL);
932 if (r < 0)
933 goto fail;
934
935 r = dns_packet_read_uint32(p, &rr->soa.refresh, NULL);
936 if (r < 0)
937 goto fail;
938
939 r = dns_packet_read_uint32(p, &rr->soa.retry, NULL);
940 if (r < 0)
941 goto fail;
942
943 r = dns_packet_read_uint32(p, &rr->soa.expire, NULL);
944 if (r < 0)
945 goto fail;
946
947 r = dns_packet_read_uint32(p, &rr->soa.minimum, NULL);
948 break;
949
623a4c97
LP
950 case DNS_TYPE_MX:
951 case DNS_TYPE_TXT:
952 case DNS_TYPE_SRV:
953 case DNS_TYPE_DNAME:
954 case DNS_TYPE_SSHFP:
74b2466e
LP
955 default:
956 r = dns_packet_read(p, rdlength, &d, NULL);
957 if (r < 0)
958 goto fail;
959
960 rr->generic.data = memdup(d, rdlength);
961 if (!rr->generic.data) {
962 r = -ENOMEM;
963 goto fail;
964 }
965
966 rr->generic.size = rdlength;
967 break;
968 }
969 if (r < 0)
970 goto fail;
971 if (p->rindex != offset + rdlength) {
972 r = -EBADMSG;
973 goto fail;
974 }
975
976 *ret = rr;
977 rr = NULL;
978
979 if (start)
980 *start = saved_rindex;
981
982 return 0;
983fail:
984 dns_packet_rewind(p, saved_rindex);
985 return r;
986}
987
faa133f3
LP
988int dns_packet_extract(DnsPacket *p) {
989 _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
990 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
991 size_t saved_rindex;
992 unsigned n, i;
74b2466e
LP
993 int r;
994
faa133f3 995 saved_rindex = p->rindex;
322345fd
LP
996 dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE);
997
3cb10d3a 998 n = DNS_PACKET_QDCOUNT(p);
faa133f3
LP
999 if (n > 0) {
1000 question = dns_question_new(n);
1001 if (!question) {
1002 r = -ENOMEM;
1003 goto finish;
1004 }
74b2466e 1005
faa133f3
LP
1006 for (i = 0; i < n; i++) {
1007 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
74b2466e 1008
faa133f3
LP
1009 r = dns_packet_read_key(p, &key, NULL);
1010 if (r < 0)
1011 goto finish;
74b2466e 1012
faa133f3
LP
1013 r = dns_question_add(question, key);
1014 if (r < 0)
1015 goto finish;
1016 }
1017 }
322345fd 1018
faa133f3
LP
1019 n = DNS_PACKET_RRCOUNT(p);
1020 if (n > 0) {
1021 answer = dns_answer_new(n);
1022 if (!answer) {
1023 r = -ENOMEM;
1024 goto finish;
1025 }
322345fd 1026
faa133f3
LP
1027 for (i = 0; i < n; i++) {
1028 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
322345fd 1029
faa133f3
LP
1030 r = dns_packet_read_rr(p, &rr, NULL);
1031 if (r < 0)
1032 goto finish;
322345fd 1033
faa133f3
LP
1034 r = dns_answer_add(answer, rr);
1035 if (r < 0)
1036 goto finish;
1037 }
322345fd
LP
1038 }
1039
faa133f3
LP
1040 p->question = question;
1041 question = NULL;
322345fd 1042
faa133f3
LP
1043 p->answer = answer;
1044 answer = NULL;
322345fd 1045
faa133f3 1046 r = 0;
322345fd
LP
1047
1048finish:
1049 p->rindex = saved_rindex;
1050 return r;
1051}
1052
74b2466e
LP
1053static const char* const dns_rcode_table[_DNS_RCODE_MAX_DEFINED] = {
1054 [DNS_RCODE_SUCCESS] = "SUCCESS",
1055 [DNS_RCODE_FORMERR] = "FORMERR",
1056 [DNS_RCODE_SERVFAIL] = "SERVFAIL",
1057 [DNS_RCODE_NXDOMAIN] = "NXDOMAIN",
1058 [DNS_RCODE_NOTIMP] = "NOTIMP",
1059 [DNS_RCODE_REFUSED] = "REFUSED",
1060 [DNS_RCODE_YXDOMAIN] = "YXDOMAIN",
1061 [DNS_RCODE_YXRRSET] = "YRRSET",
1062 [DNS_RCODE_NXRRSET] = "NXRRSET",
1063 [DNS_RCODE_NOTAUTH] = "NOTAUTH",
1064 [DNS_RCODE_NOTZONE] = "NOTZONE",
1065 [DNS_RCODE_BADVERS] = "BADVERS",
1066 [DNS_RCODE_BADKEY] = "BADKEY",
1067 [DNS_RCODE_BADTIME] = "BADTIME",
1068 [DNS_RCODE_BADMODE] = "BADMODE",
1069 [DNS_RCODE_BADNAME] = "BADNAME",
1070 [DNS_RCODE_BADALG] = "BADALG",
1071 [DNS_RCODE_BADTRUNC] = "BADTRUNC",
1072};
1073DEFINE_STRING_TABLE_LOOKUP(dns_rcode, int);
1716f6dc
LP
1074
1075static const char* const dns_protocol_table[_DNS_PROTOCOL_MAX] = {
1076 [DNS_PROTOCOL_DNS] = "dns",
1077 [DNS_PROTOCOL_MDNS] = "mdns",
1078 [DNS_PROTOCOL_LLMNR] = "llmnr",
1079};
1080DEFINE_STRING_TABLE_LOOKUP(dns_protocol, DnsProtocol);