1 /**
2  * Parse the rdata component of a resource record.
3  *
4  * \param[out] rr resource record object whose ->rdata field will be populated
5  * \param[in] p pointer to start of message
6  * \param[in] eop end of message buffer
7  * \param[in] rdata pointer to rdata
8  * \param[in] rdlen
9  */
10 
11 wdns_res
_wdns_parse_rdata(wdns_rr_t * rr,const uint8_t * p,const uint8_t * eop,const uint8_t * rdata,uint16_t rdlen)12 _wdns_parse_rdata(wdns_rr_t *rr, const uint8_t *p, const uint8_t *eop,
13 		  const uint8_t *rdata, uint16_t rdlen)
14 {
15 
16 #define advance_bytes(x) do { \
17 	if (src_bytes < ((signed) (x))) { \
18 		res = wdns_res_parse_error; \
19 		goto parse_error; \
20 	} \
21 	src += (x); \
22 	src_bytes -= (x); \
23 } while (0)
24 
25 #define copy_bytes(x) do { \
26 	if (src_bytes < (x)) {\
27 		res = wdns_res_parse_error; \
28 		goto parse_error; \
29 	} \
30 	ubuf_append(u, src, x); \
31 	src += (x); \
32 	src_bytes -= (x); \
33 } while (0)
34 
35 	ubuf *u;
36 	const record_descr *descr = NULL;
37 	const uint8_t *src;
38 	const uint8_t *t;
39 	ssize_t src_bytes;
40 	size_t len;
41 	uint8_t domain_name[WDNS_MAXLEN_NAME];
42 	uint8_t oclen;
43 	wdns_res res;
44 
45 	u = ubuf_new();
46 	src = rdata;
47 	src_bytes = (ssize_t) rdlen;
48 
49 	if (rr->rrtype < record_descr_len)
50 		descr = &record_descr_array[rr->rrtype];
51 
52 	if (rr->rrtype >= record_descr_len ||
53 	    (descr != NULL && descr->types[0] == rdf_unknown))
54 	{
55 		/* unknown rrtype, treat generically */
56 		copy_bytes(src_bytes);
57 	} else if (descr != NULL &&
58 		   (descr->record_class == class_un ||
59 		    descr->record_class == rr->rrclass))
60 	{
61 		for (t = &descr->types[0]; *t != rdf_end; t++) {
62 			if (src_bytes == 0)
63 				break;
64 
65 			switch (*t) {
66 			case rdf_name:
67 			case rdf_uname:
68 				res = wdns_unpack_name(p, eop, src, domain_name, &len);
69 				if (res != wdns_res_success)
70 					goto parse_error;
71 				src_bytes -= wdns_skip_name(&src, eop);
72 				if (src_bytes < 0) {
73 					res = wdns_res_out_of_bounds;
74 					goto parse_error;
75 				}
76 				ubuf_append(u, domain_name, len);
77 				break;
78 
79 			case rdf_bytes:
80 			case rdf_bytes_b64:
81 			case rdf_bytes_str:
82 				copy_bytes(src_bytes);
83 				break;
84 
85 			case rdf_int8:
86 				copy_bytes(1);
87 				break;
88 
89 			case rdf_int16:
90 			case rdf_rrtype:
91 				copy_bytes(2);
92 				break;
93 
94 			case rdf_int32:
95 			case rdf_ipv4:
96 				copy_bytes(4);
97 				break;
98 
99 			case rdf_ipv6:
100 				copy_bytes(16);
101 				break;
102 
103 			case rdf_eui48:
104 				copy_bytes(6);
105 				break;
106 
107 			case rdf_eui64:
108 				copy_bytes(8);
109 				break;
110 
111 			case rdf_string:
112 			case rdf_salt:
113 			case rdf_hash:
114 				oclen = *src;
115 				copy_bytes(oclen + 1);
116 				break;
117 
118 			case rdf_repstring:
119 				while (src_bytes > 0) {
120 					oclen = *src;
121 					copy_bytes(oclen + 1);
122 				}
123 				break;
124 
125 			case rdf_ipv6prefix:
126 				oclen = *src;
127 				if (oclen > 16U) {
128 					res = wdns_res_out_of_bounds;
129 					goto parse_error;
130 				}
131 				copy_bytes(oclen + 1);
132 				break;
133 
134 			case rdf_type_bitmap: {
135 				uint8_t bitmap_len;
136 
137 				while (src_bytes >= 2) {
138 					bitmap_len = *(src + 1);
139 
140 					if (!(bitmap_len >= 1 && bitmap_len <= 32)) {
141 						res = wdns_res_out_of_bounds;
142 						goto parse_error;
143 					}
144 
145 					if (bitmap_len <= (src_bytes - 2)) {
146 						copy_bytes(2 + bitmap_len);
147 					} else {
148 						res = wdns_res_out_of_bounds;
149 						goto parse_error;
150 					}
151 				}
152 				break;
153 			}
154 
155 			default:
156 				fprintf(stderr, "%s: unhandled rdf type %u\n", __func__, *t);
157 				abort();
158 			}
159 
160 		}
161 		if (src_bytes != 0) {
162 			res = wdns_res_out_of_bounds;
163 			goto parse_error;
164 		}
165 	} else {
166 		/* unknown rrtype, treat generically */
167 		copy_bytes(src_bytes);
168 	}
169 
170 	/* load rr->rdata */
171 	len = ubuf_size(u);
172 	rr->rdata = my_malloc(sizeof(wdns_rdata_t) + len);
173 	rr->rdata->len = len;
174 	memcpy(rr->rdata->data, ubuf_cstr(u), len);
175 	ubuf_destroy(&u);
176 
177 	return (wdns_res_success);
178 
179 parse_error:
180 	ubuf_destroy(&u);
181 	return (res);
182 
183 #undef advance_bytes
184 #undef copy_bytes
185 }
186