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