1 /**
2  * Uncompress a domain name from a message.
3  *
4  * The caller must allocate at least #WDNS_MAXLEN_NAME bytes for
5  * the destination buffer.
6  *
7  * \param[in] p pointer to message
8  * \param[in] eop pointer to end of message
9  * \param[in] src pointer to domain name
10  * \param[out] dst caller-allocated buffer for uncompressed domain name
11  * \param[out] sz total length of uncompressed domain name (may be NULL)
12  *
13  * \return
14  */
15 
16 wdns_res
wdns_unpack_name(const uint8_t * p,const uint8_t * eop,const uint8_t * src,uint8_t * dst,size_t * sz)17 wdns_unpack_name(const uint8_t *p, const uint8_t *eop, const uint8_t *src,
18 		 uint8_t *dst, size_t *sz)
19 {
20 	const uint8_t *cptr;
21 	uint8_t c;
22 
23 	size_t total_len = 0;
24 
25 	if (p >= eop || src >= eop || src < p)
26 		return (wdns_res_out_of_bounds);
27 
28 	while ((c = *src++) != 0) {
29 		if (c >= 192) {
30 			uint16_t offset;
31 
32 			if (src > eop)
33 				return (wdns_res_out_of_bounds);
34 
35 			/* offset is the lower 14 bits of the 2 octet sequence */
36 			offset = ((c & 63) << 8) + *src;
37 
38 			cptr = p + offset;
39 
40 			if (cptr > eop)
41 				return (wdns_res_invalid_compression_pointer);
42 
43 			if (cptr == src - 1 && (*(src - 1) == 0)) {
44 				/* if a compression pointer points to exactly one octet
45 				 * before itself, then the only valid domain name pointee
46 				 * is the zero-octet root label. */
47 				src = cptr;
48 			} else if (cptr > src - 2) {
49 				return (wdns_res_invalid_compression_pointer);
50 			} else {
51 				src = cptr;
52 			}
53 		} else if (c <= 63) {
54 			total_len++;
55 			if (total_len >= WDNS_MAXLEN_NAME)
56 				return (wdns_res_name_overflow);
57 			*dst++ = c;
58 
59 			total_len += c;
60 			if (total_len >= WDNS_MAXLEN_NAME)
61 				return (wdns_res_name_overflow);
62 			if (src + c > eop)
63 				return (wdns_res_out_of_bounds);
64 			memcpy(dst, src, c);
65 
66 			dst += c;
67 			src += c;
68 		} else {
69 			return (wdns_res_invalid_length_octet);
70 		}
71 	}
72 	*dst = '\0';
73 	total_len++;
74 
75 	if (sz)
76 		*sz = total_len;
77 	return (wdns_res_success);
78 }
79