3 * Copyright (c) 2018 Apple Computer, Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * DNS wire-format functions.
19 * These are really simple functions for constructing DNS messages wire format.
20 * The flow is that there is a transaction structure which contains pointers to both
21 * a message output buffer and a response input buffer. The structure is initialized,
22 * and then the various wire format functions are called repeatedly to store data.
23 * If an error occurs during this process, it's okay to just keep going, because the
24 * error is recorded in the transaction; once all of the copy-in functions have been
25 * called, the error status can be checked once at the end.
31 #include <sys/errno.h>
32 #include <sys/socket.h>
33 #include <arpa/inet.h>
40 dns_opt_parse(dns_edns0_t
*NONNULL
*NULLABLE ret
, dns_rr_t
*rr
)
45 dns_label_t
* NULLABLE
46 dns_label_parse(const uint8_t *buf
, unsigned mlen
, unsigned *NONNULL offp
)
48 uint8_t llen
= buf
[*offp
];
51 // Make sure that we got the data this label claims to encompass.
52 if (*offp
+ llen
+ 1 > mlen
) {
53 DEBUG("claimed length of label is too long: %u > %u.\n", *offp
+ llen
+ 1, mlen
);
57 rv
= calloc(llen
+ 1 - DNS_MAX_LABEL_SIZE
+ sizeof *rv
, 1);
59 DEBUG("memory allocation for %u byte label (%.*s) failed.\n",
60 *offp
+ llen
+ 1, *offp
+ llen
+ 1, &buf
[*offp
+ 1]);
65 memcpy(rv
->data
, &buf
[*offp
+ 1], llen
);
66 rv
->data
[llen
] = 0; // We NUL-terminate the label for convenience
72 dns_name_parse(dns_label_t
*NONNULL
*NULLABLE ret
, const uint8_t *buf
, unsigned len
,
73 unsigned *NONNULL offp
, unsigned base
)
82 if ((buf
[*offp
] & 0xC0) == 0xC0) {
84 if (*offp
+ 2 > len
) {
85 DEBUG("incomplete compression pointer: %u > %u", *offp
+ 2, len
);
88 pointer
= (((unsigned)buf
[*offp
] & 0x3f) << 8) | (unsigned)buf
[*offp
+ 1];
90 if (pointer
>= base
) {
91 // Don't allow a pointer forward, or to a pointer we've already visited.
92 DEBUG("compression pointer points forward: %u >= %u.\n", pointer
, base
);
95 if (pointer
< DNS_HEADER_SIZE
) {
96 // Don't allow pointers into the header.
97 DEBUG("compression pointer points into header: %u.\n", pointer
);
100 pointer
-= DNS_HEADER_SIZE
;
101 if (buf
[pointer
] & 0xC0) {
102 // If this is a pointer to a pointer, it's not valid.
103 DEBUG("compression pointer points into pointer: %u %02x%02x.\n", pointer
,
104 buf
[pointer
], pointer
+ 1 < len
? buf
[pointer
+ 1] : 0xFF);
107 if (buf
[pointer
] + pointer
>= base
|| buf
[pointer
] + pointer
>= *offp
) {
108 // Possibly this isn't worth checking.
109 DEBUG("compression pointer points to something that goes past current position: %u %u\n",
110 pointer
, buf
[pointer
]);
113 return dns_name_parse(ret
, buf
, len
, &pointer
, pointer
);
115 // We don't support binary labels, which are historical, and at this time there are no other valid
117 if (buf
[*offp
] & 0xC0) {
118 DEBUG("invalid label type: %x\n", buf
[*offp
]);
122 rv
= dns_label_parse(buf
, len
, offp
);
132 return dns_name_parse(&rv
->next
, buf
, len
, offp
, base
);
136 dns_u8_parse(const uint8_t *buf
, unsigned len
, unsigned *NONNULL offp
, uint8_t *NONNULL ret
)
139 if (*offp
+ 1 > len
) {
140 DEBUG("dns_u8_parse: not enough room: %u > %u.\n", *offp
+ 1, len
);
151 dns_u16_parse(const uint8_t *buf
, unsigned len
, unsigned *NONNULL offp
, uint16_t *NONNULL ret
)
154 if (*offp
+ 2 > len
) {
155 DEBUG("dns_u16_parse: not enough room: %u > %u.\n", *offp
+ 2, len
);
159 rv
= ((uint16_t)(buf
[*offp
]) << 8) | (uint16_t)(buf
[*offp
+ 1]);
166 dns_u32_parse(const uint8_t *buf
, unsigned len
, unsigned *NONNULL offp
, uint32_t *NONNULL ret
)
169 if (*offp
+ 4 > len
) {
170 DEBUG("dns_u32_parse: not enough room: %u > %u.\n", *offp
+ 4, len
);
174 rv
= (((uint32_t)(buf
[*offp
]) << 24) | ((uint32_t)(buf
[*offp
+ 1]) << 16) |
175 ((uint32_t)(buf
[*offp
+ 2]) << 8) | (uint32_t)(buf
[*offp
+ 3]));
182 dns_name_dump(FILE *outfile
, dns_label_t
*name
)
184 char buf
[DNS_MAX_NAME_SIZE_ESCAPED
+ 1];
186 dns_name_print(name
, buf
, sizeof buf
);
191 dns_rrdata_dump(FILE *outfile
, dns_rr_t
*rr
)
195 dns_txt_element_t
*txt
;
199 fprintf(outfile
, "KEY <AC %d> <Z %d> <XT %d> <ZZ %d> <NAMTYPE %d> <ZZZZ %d> <ORY %d> %d %d ",
200 ((rr
->data
.key
.flags
& 0xC000) >> 14 & 3), ((rr
->data
.key
.flags
& 0x2000) >> 13) & 1,
201 ((rr
->data
.key
.flags
& 0x1000) >> 12) & 1, ((rr
->data
.key
.flags
& 0xC00) >> 10) & 3,
202 ((rr
->data
.key
.flags
& 0x300) >> 8) & 3, ((rr
->data
.key
.flags
& 0xF0) >> 4) & 15, rr
->data
.key
.flags
& 15,
203 rr
->data
.key
.protocol
, rr
->data
.key
.algorithm
);
204 for (i
= 0; i
< rr
->data
.key
.len
; i
++) {
206 fprintf(outfile
, "%d [%02x", rr
->data
.key
.len
, rr
->data
.key
.key
[i
]);
208 fprintf(outfile
, " %02x", rr
->data
.key
.key
[i
]);
215 fprintf(outfile
, "SIG %d %d %d %lu %lu %lu %d ",
216 rr
->data
.sig
.type
, rr
->data
.sig
.algorithm
, rr
->data
.sig
.label
,
217 (unsigned long)rr
->data
.sig
.rrttl
, (unsigned long)rr
->data
.sig
.expiry
,
218 (unsigned long)rr
->data
.sig
.inception
, rr
->data
.sig
.key_tag
);
219 dns_name_dump(outfile
, rr
->data
.sig
.signer
);
220 for (i
= 0; i
< rr
->data
.sig
.len
; i
++) {
222 fprintf(outfile
, "%d [%02x", rr
->data
.sig
.len
, rr
->data
.sig
.signature
[i
]);
224 fprintf(outfile
, " %02x", rr
->data
.sig
.signature
[i
]);
231 fprintf(outfile
, "SRV %d %d %d ", rr
->data
.srv
.priority
, rr
->data
.srv
.weight
, rr
->data
.srv
.port
);
232 dns_name_dump(outfile
, rr
->data
.ptr
.name
);
236 fputs("PTR ", outfile
);
237 dns_name_dump(outfile
, rr
->data
.ptr
.name
);
240 case dns_rrtype_cname
:
241 fputs("CNAME ", outfile
);
242 dns_name_dump(outfile
, rr
->data
.ptr
.name
);
247 for (i
= 0; i
< rr
->data
.a
.num
; i
++) {
248 inet_ntop(AF_INET
, &rr
->data
.a
.addrs
[i
], nbuf
, sizeof nbuf
);
250 fputs(nbuf
, outfile
);
254 case dns_rrtype_aaaa
:
255 fputs("AAAA", outfile
);
256 for (i
= 0; i
< rr
->data
.aaaa
.num
; i
++) {
257 inet_ntop(AF_INET6
, &rr
->data
.aaaa
.addrs
[i
], nbuf
, sizeof nbuf
);
259 fputs(nbuf
, outfile
);
264 fputs("TXT", outfile
);
265 for (txt
= rr
->data
.txt
; txt
; txt
= txt
->next
) {
268 for (i
= 0; i
< txt
->len
; i
++) {
269 if (isascii(txt
->data
[i
]) && isprint(txt
->data
[i
])) {
270 putc(txt
->data
[i
], outfile
);
272 fprintf(outfile
, "<%x>", txt
->data
[i
]);
280 fprintf(outfile
, "<rrtype %d>:", rr
->type
);
281 if (rr
->data
.unparsed
.len
== 0) {
282 fputs(" <none>", outfile
);
284 for (i
= 0; i
< rr
->data
.unparsed
.len
; i
++) {
285 fprintf(outfile
, " %02x", rr
->data
.unparsed
.data
[i
]);
293 dns_rdata_parse_data(dns_rr_t
*NONNULL rr
, const uint8_t *buf
, unsigned *NONNULL offp
, unsigned target
, unsigned rdlen
, unsigned rrstart
)
296 dns_txt_element_t
*txt
, **ptxt
;
300 if (!dns_u16_parse(buf
, target
, offp
, &rr
->data
.key
.flags
) ||
301 !dns_u8_parse(buf
, target
, offp
, &rr
->data
.key
.protocol
) ||
302 !dns_u8_parse(buf
, target
, offp
, &rr
->data
.key
.algorithm
)) {
305 rr
->data
.key
.len
= target
- *offp
;
306 rr
->data
.key
.key
= malloc(rr
->data
.key
.len
);
307 if (!rr
->data
.key
.key
) {
310 memcpy(rr
->data
.key
.key
, &buf
[*offp
], rr
->data
.key
.len
);
311 *offp
+= rr
->data
.key
.len
;
315 rr
->data
.sig
.start
= rrstart
;
316 if (!dns_u16_parse(buf
, target
, offp
, &rr
->data
.sig
.type
) ||
317 !dns_u8_parse(buf
, target
, offp
, &rr
->data
.sig
.algorithm
) ||
318 !dns_u8_parse(buf
, target
, offp
, &rr
->data
.sig
.label
) ||
319 !dns_u32_parse(buf
, target
, offp
, &rr
->data
.sig
.rrttl
) ||
320 !dns_u32_parse(buf
, target
, offp
, &rr
->data
.sig
.expiry
) ||
321 !dns_u32_parse(buf
, target
, offp
, &rr
->data
.sig
.inception
) ||
322 !dns_u16_parse(buf
, target
, offp
, &rr
->data
.sig
.key_tag
) ||
323 !dns_name_parse(&rr
->data
.sig
.signer
, buf
, target
, offp
, *offp
)) {
326 // The signature is what's left of the RRDATA. It covers the message up to the signature, so we
327 // remember where it starts so as to know what memory to cover to validate it.
328 rr
->data
.sig
.len
= target
- *offp
;
329 rr
->data
.sig
.signature
= malloc(rr
->data
.sig
.len
);
330 if (!rr
->data
.sig
.signature
) {
333 memcpy(rr
->data
.sig
.signature
, &buf
[*offp
], rr
->data
.sig
.len
);
334 *offp
+= rr
->data
.sig
.len
;
338 if (!dns_u16_parse(buf
, target
, offp
, &rr
->data
.srv
.priority
) ||
339 !dns_u16_parse(buf
, target
, offp
, &rr
->data
.srv
.weight
) ||
340 !dns_u16_parse(buf
, target
, offp
, &rr
->data
.srv
.port
)) {
343 // This fallthrough assumes that the first element in the srv, ptr and cname structs is
344 // a pointer to a domain name.
347 case dns_rrtype_cname
:
348 if (!dns_name_parse(&rr
->data
.ptr
.name
, buf
, target
, offp
, *offp
)) {
353 // We assume below that the a and aaaa structures in the data union are exact aliases of
359 case dns_rrtype_aaaa
:
362 if (rdlen
& (addrlen
- 1)) {
363 DEBUG("dns_rdata_parse: %s rdlen not an even multiple of %u: %u",
364 addrlen
== 4 ? "A" : "AAAA", addrlen
, rdlen
);
367 rr
->data
.a
.addrs
= malloc(rdlen
);
368 if (rr
->data
.a
.addrs
== NULL
) {
371 rr
->data
.a
.num
= rdlen
/ addrlen
;
372 memcpy(rr
->data
.a
.addrs
, &buf
[*offp
], rdlen
);
377 ptxt
= &rr
->data
.txt
;
378 while (*offp
< target
) {
379 unsigned tlen
= buf
[*offp
];
380 if (*offp
+ tlen
+ 1 > target
) {
381 DEBUG("dns_rdata_parse: TXT RR length is larger than available space: %u %u",
382 *offp
+ tlen
+ 1, target
);
386 txt
= malloc(tlen
+ 1 + sizeof *txt
);
388 DEBUG("dns_rdata_parse: no memory for TXT RR");
393 memcpy(txt
->data
, &buf
[*offp
], tlen
);
403 rr
->data
.unparsed
.data
= malloc(rdlen
);
404 if (rr
->data
.unparsed
.data
== NULL
) {
407 memcpy(rr
->data
.unparsed
.data
, &buf
[*offp
], rdlen
);
409 rr
->data
.unparsed
.len
= rdlen
;
413 if (*offp
!= target
) {
414 DEBUG("dns_rdata_parse: parse for rrtype %d not fully contained: %u %u", rr
->type
, target
, *offp
);
421 dns_rdata_parse(dns_rr_t
*NONNULL rr
,
422 const uint8_t *buf
, unsigned len
, unsigned *NONNULL offp
, unsigned rrstart
)
427 if (!dns_u16_parse(buf
, len
, offp
, &rdlen
)) {
430 target
= *offp
+ rdlen
;
434 return dns_rdata_parse_data(rr
, buf
, offp
, target
, rdlen
, rrstart
);
438 dns_rr_parse(dns_rr_t
*NONNULL rr
,
439 const uint8_t *buf
, unsigned len
, unsigned *NONNULL offp
, bool rrdata_expected
)
441 int rrstart
= *offp
; // Needed to mark the start of the SIG RR for SIG(0).
442 memset(rr
, 0, sizeof *rr
);
443 if (!dns_name_parse(&rr
->name
, buf
, len
, offp
, *offp
)) {
447 if (!dns_u16_parse(buf
, len
, offp
, &rr
->type
)) {
451 if (!dns_u16_parse(buf
, len
, offp
, &rr
->qclass
)) {
455 if (rrdata_expected
) {
456 if (!dns_u32_parse(buf
, len
, offp
, &rr
->ttl
)) {
459 if (!dns_rdata_parse(rr
, buf
, len
, offp
, rrstart
)) {
464 printf("rrtype: %u qclass: %u name: ", rr
->type
, rr
->qclass
);
465 dns_name_dump(stdout
, rr
->name
);
466 if (rrdata_expected
) {
468 dns_rrdata_dump(stdout
, rr
);
474 void dns_name_free(dns_label_t
*name
)
482 return dns_name_free(next
);
486 dns_rrdata_free(dns_rr_t
*rr
)
490 free(rr
->data
.key
.key
);
494 dns_name_free(rr
->data
.sig
.signer
);
495 free(rr
->data
.sig
.signature
);
500 case dns_rrtype_cname
:
501 dns_name_free(rr
->data
.ptr
.name
);
502 rr
->data
.ptr
.name
= NULL
;
506 case dns_rrtype_aaaa
:
507 free(rr
->data
.a
.addrs
);
508 rr
->data
.a
.addrs
= NULL
;
513 free(rr
->data
.unparsed
.data
);
514 rr
->data
.unparsed
.data
= NULL
;
520 dns_message_free(dns_message_t
*message
)
524 #define FREE(count, sets) \
525 for (i = 0; i < message->count; i++) { \
526 dns_rr_t *set = &message->sets[i]; \
528 dns_name_free(set->name); \
531 dns_rrdata_free(set); \
533 if (message->sets) { \
534 free(message->sets); \
536 FREE(qdcount
, questions
);
537 FREE(ancount
, answers
);
538 FREE(nscount
, authority
);
539 FREE(arcount
, additional
);
544 dns_wire_parse(dns_message_t
*NONNULL
*NULLABLE ret
, dns_wire_t
*message
, unsigned len
)
547 dns_message_t
*rv
= calloc(sizeof *rv
, 1);
554 #define PARSE(count, sets, name, rrdata_expected) \
555 rv->count = ntohs(message->count); \
556 if (rv->count > 50) { \
557 dns_message_free(rv); \
561 if (rv->qdcount != 0) { \
562 rv->sets = calloc(sizeof *rv->sets, rv->count); \
563 if (rv->sets == NULL) { \
564 dns_message_free(rv); \
569 for (i = 0; i < rv->count; i++) { \
570 if (!dns_rr_parse(&rv->sets[i], message->data, len, &offset, rrdata_expected)) { \
571 dns_message_free(rv); \
572 fprintf(stderr, name " %d RR parse failed.\n", i); \
576 PARSE(qdcount
, questions
, "question", false);
577 PARSE(ancount
, answers
, "answers", true);
578 PARSE(nscount
, authority
, "authority", true);
579 PARSE(arcount
, additional
, "additional", true);
582 for (i
= 0; i
< rv
->ancount
; i
++) {
584 if (rv
->additional
[i
].type
== dns_rrtype_opt
) {
585 if (!dns_opt_parse(&rv
->edns0
, &rv
->additional
[i
])) {
586 dns_message_free(rv
);
596 dns_name_print(dns_name_t
*NONNULL name
, char *buf
, int bufmax
)
602 // Copy the labels in one at a time, putting a dot between each one; if there isn't room
603 // in the buffer (shouldn't be the case), copy as much as will fit, leaving room for a NUL
605 for (lp
= name
; lp
; lp
= lp
->next
) {
607 if (ix
+ 2 >= bufmax
) {
612 for (i
= 0; i
< lp
->len
; i
++) {
613 if (isascii(lp
->data
[i
]) && isprint(lp
->data
[i
])) {
614 if (ix
+ 2 >= bufmax
) {
617 buf
[ix
++] = lp
->data
[i
];
619 if (ix
+ 5 >= bufmax
) {
623 buf
[ix
++] = '0' + (lp
->data
[i
] >> 6);
624 buf
[ix
++] = '0' + (lp
->data
[i
] >> 3) & 3;
625 buf
[ix
++] = '0' + lp
->data
[i
] & 3;
637 labeleq(const char *label1
, const char *label2
, size_t len
)
640 for (i
= 0; i
< len
; i
++) {
641 if (isascii(label1
[i
]) && isascii(label2
[i
])) {
642 if (tolower(label1
[i
]) != tolower(label2
[i
])) {
647 if (label1
[i
] != label2
[i
]) {
656 dns_names_equal(dns_label_t
*NONNULL name1
, dns_label_t
*NONNULL name2
)
658 if (name1
->len
!= name2
->len
) {
661 if (name1
->len
!= 0 && !labeleq(name1
->data
, name2
->data
, name1
->len
) != 0) {
664 if (name1
->next
!= NULL
&& name2
->next
!= NULL
) {
665 return dns_names_equal(name1
->next
, name2
->next
);
667 if (name1
->next
== NULL
&& name2
->next
== NULL
) {
673 // Note that "foo.arpa" is not the same as "foo.arpa."
675 dns_names_equal_text(dns_label_t
*NONNULL name1
, const char *NONNULL name2
)
678 ndot
= strchr(name2
, '.');
680 ndot
= name2
+ strlen(name2
);
682 if (name1
->len
!= ndot
- name2
) {
685 if (name1
->len
!= 0 && !labeleq(name1
->data
, name2
, name1
->len
) != 0) {
688 if (name1
->next
!= NULL
&& *ndot
== '.') {
689 return dns_names_equal_text(name1
->next
, ndot
+ 1);
691 if (name1
->next
== NULL
&& *ndot
== 0) {
697 // Find the length of a name in uncompressed wire format.
698 // This is in fromwire because we use it for validating signatures, and don't need it for
701 dns_name_wire_length_in(dns_label_t
*NONNULL name
, size_t ret
)
706 return dns_name_wire_length_in(name
->next
, ret
+ name
->len
+ 1);
710 dns_name_wire_length(dns_label_t
*NONNULL name
)
712 return dns_name_wire_length_in(name
, 0);
715 // Copy a name we've parsed from a message out in canonical wire format so that we can
716 // use it to verify a signature. As above, not actually needed for copying to a message
717 // we're going to send, since in that case we want to try to compress.
719 dns_name_to_wire_canonical_in(uint8_t *NONNULL buf
, size_t max
, size_t ret
, dns_label_t
*NONNULL name
)
721 INFO("dns_name_to_wire_canonical_in: buf %p max %zd ret %zd name = %p '%.*s'",
722 buf
, max
, ret
, name
, name
? name
->len
: 0, name
? name
->data
: "");
726 if (max
< name
->len
+ 1) {
730 memcpy(buf
+ 1, name
->data
, name
->len
);
731 return dns_name_to_wire_canonical_in(buf
+ name
->len
+ 1,
732 max
- name
->len
- 1, ret
+ name
->len
+ 1, name
->next
);
736 dns_name_to_wire_canonical(uint8_t *NONNULL buf
, size_t max
, dns_label_t
*NONNULL name
)
738 return dns_name_to_wire_canonical_in(buf
, max
, 0, name
);
746 // c-file-style: "bsd"
749 // indent-tabs-mode: nil