xref: /freebsd/contrib/ldns/wire2host.c (revision 5b9c547c)
1 /*
2  * wire2host.c
3  *
4  * conversion routines from the wire to the host
5  * format.
6  * This will usually just a re-ordering of the
7  * data (as we store it in network format)
8  *
9  * a Net::DNS like library for C
10  *
11  * (c) NLnet Labs, 2004-2006
12  *
13  * See the file LICENSE for the license
14  */
15 
16 
17 #include <ldns/config.h>
18 
19 #include <ldns/ldns.h>
20 /*#include <ldns/wire2host.h>*/
21 
22 #include <strings.h>
23 #include <limits.h>
24 
25 
26 
27 /*
28  * Set of macro's to deal with the dns message header as specified
29  * in RFC1035 in portable way.
30  *
31  */
32 
33 /*
34  *
35  *                                    1  1  1  1  1  1
36  *      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
37  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
38  *    |                      ID                       |
39  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
40  *    |QR|   Opcode  |AA|TC|RD|RA| Z|AD|CD|   RCODE   |
41  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
42  *    |                    QDCOUNT                    |
43  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
44  *    |                    ANCOUNT                    |
45  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
46  *    |                    NSCOUNT                    |
47  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
48  *    |                    ARCOUNT                    |
49  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
50  *
51  */
52 
53 
54 /* allocates memory to *dname! */
55 ldns_status
56 ldns_wire2dname(ldns_rdf **dname, const uint8_t *wire, size_t max, size_t *pos)
57 {
58 	uint8_t label_size;
59 	uint16_t pointer_target;
60 	uint8_t pointer_target_buf[2];
61 	size_t dname_pos = 0;
62 	size_t uncompressed_length = 0;
63 	size_t compression_pos = 0;
64 	uint8_t tmp_dname[LDNS_MAX_DOMAINLEN];
65 	unsigned int pointer_count = 0;
66 
67 	if (pos == NULL) {
68 		return LDNS_STATUS_WIRE_RDATA_ERR;
69 	}
70 	if (*pos >= max) {
71 		return LDNS_STATUS_PACKET_OVERFLOW;
72 	}
73 	label_size = wire[*pos];
74 	while (label_size > 0) {
75 		/* compression */
76 		while (label_size >= 192) {
77 			if (compression_pos == 0) {
78 				compression_pos = *pos + 2;
79 			}
80 
81 			pointer_count++;
82 
83 			/* remove first two bits */
84 			if (*pos + 2 > max) {
85 				return LDNS_STATUS_PACKET_OVERFLOW;
86 			}
87 			pointer_target_buf[0] = wire[*pos] & 63;
88 			pointer_target_buf[1] = wire[*pos + 1];
89 			pointer_target = ldns_read_uint16(pointer_target_buf);
90 
91 			if (pointer_target == 0) {
92 				return LDNS_STATUS_INVALID_POINTER;
93 			} else if (pointer_target >= max) {
94 				return LDNS_STATUS_INVALID_POINTER;
95 			} else if (pointer_count > LDNS_MAX_POINTERS) {
96 				return LDNS_STATUS_INVALID_POINTER;
97 			}
98 			*pos = pointer_target;
99 			label_size = wire[*pos];
100 		}
101 		if(label_size == 0)
102 			break; /* break from pointer to 0 byte */
103 		if (label_size > LDNS_MAX_LABELLEN) {
104 			return LDNS_STATUS_LABEL_OVERFLOW;
105 		}
106 		if (*pos + 1 + label_size > max) {
107 			return LDNS_STATUS_LABEL_OVERFLOW;
108 		}
109 
110 		/* check space for labelcount itself */
111 		if (dname_pos + 1 > LDNS_MAX_DOMAINLEN) {
112 			return LDNS_STATUS_DOMAINNAME_OVERFLOW;
113 		}
114 		tmp_dname[dname_pos] = label_size;
115 		if (label_size > 0) {
116 			dname_pos++;
117 		}
118 		*pos = *pos + 1;
119 		if (dname_pos + label_size > LDNS_MAX_DOMAINLEN) {
120 			return LDNS_STATUS_DOMAINNAME_OVERFLOW;
121 		}
122 		memcpy(&tmp_dname[dname_pos], &wire[*pos], label_size);
123 		uncompressed_length += label_size + 1;
124 		dname_pos += label_size;
125 		*pos = *pos + label_size;
126 
127 		if (*pos < max) {
128 			label_size = wire[*pos];
129 		}
130 	}
131 
132 	if (compression_pos > 0) {
133 		*pos = compression_pos;
134 	} else {
135 		*pos = *pos + 1;
136 	}
137 
138 	if (dname_pos >= LDNS_MAX_DOMAINLEN) {
139 		return LDNS_STATUS_DOMAINNAME_OVERFLOW;
140 	}
141 
142 	tmp_dname[dname_pos] = 0;
143 	dname_pos++;
144 
145 	*dname = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_DNAME,
146 			(uint16_t) dname_pos, tmp_dname);
147 	if (!*dname) {
148 		return LDNS_STATUS_MEM_ERR;
149 	}
150 	return LDNS_STATUS_OK;
151 }
152 
153 /* maybe make this a goto error so data can be freed or something/ */
154 #define LDNS_STATUS_CHECK_RETURN(st) {if (st != LDNS_STATUS_OK) { return st; }}
155 #define LDNS_STATUS_CHECK_GOTO(st, label) {if (st != LDNS_STATUS_OK) { /*printf("STG %s:%d: status code %d\n", __FILE__, __LINE__, st);*/  goto label; }}
156 
157 ldns_status
158 ldns_wire2rdf(ldns_rr *rr, const uint8_t *wire, size_t max, size_t *pos)
159 {
160 	size_t end;
161 	size_t cur_rdf_length;
162 	uint8_t rdf_index;
163 	uint8_t *data;
164 	uint16_t rd_length;
165 	ldns_rdf *cur_rdf = NULL;
166 	ldns_rdf_type cur_rdf_type;
167 	const ldns_rr_descriptor *descriptor;
168 	ldns_status status;
169 
170 	assert(rr != NULL);
171 
172 	descriptor = ldns_rr_descript(ldns_rr_get_type(rr));
173 
174 	if (*pos + 2 > max) {
175 		return LDNS_STATUS_PACKET_OVERFLOW;
176 	}
177 
178 	rd_length = ldns_read_uint16(&wire[*pos]);
179 	*pos = *pos + 2;
180 
181 	if (*pos + rd_length > max) {
182 		return LDNS_STATUS_PACKET_OVERFLOW;
183 	}
184 
185 	end = *pos + (size_t) rd_length;
186 
187 	rdf_index = 0;
188 	while (*pos < end &&
189 			rdf_index < ldns_rr_descriptor_maximum(descriptor)) {
190 
191 		cur_rdf_length = 0;
192 
193 		cur_rdf_type = ldns_rr_descriptor_field_type(
194 				descriptor, rdf_index);
195 
196 		/* handle special cases immediately, set length
197 		   for fixed length rdata and do them below */
198 		switch (cur_rdf_type) {
199 		case LDNS_RDF_TYPE_DNAME:
200 			status = ldns_wire2dname(&cur_rdf, wire, max, pos);
201 			LDNS_STATUS_CHECK_RETURN(status);
202 			break;
203 		case LDNS_RDF_TYPE_CLASS:
204 		case LDNS_RDF_TYPE_ALG:
205 		case LDNS_RDF_TYPE_INT8:
206 			cur_rdf_length = LDNS_RDF_SIZE_BYTE;
207 			break;
208 		case LDNS_RDF_TYPE_TYPE:
209 		case LDNS_RDF_TYPE_INT16:
210 		case LDNS_RDF_TYPE_CERT_ALG:
211 			cur_rdf_length = LDNS_RDF_SIZE_WORD;
212 			break;
213 		case LDNS_RDF_TYPE_TIME:
214 		case LDNS_RDF_TYPE_INT32:
215 		case LDNS_RDF_TYPE_A:
216 		case LDNS_RDF_TYPE_PERIOD:
217 			cur_rdf_length = LDNS_RDF_SIZE_DOUBLEWORD;
218 			break;
219 		case LDNS_RDF_TYPE_TSIGTIME:
220 		case LDNS_RDF_TYPE_EUI48:
221 			cur_rdf_length = LDNS_RDF_SIZE_6BYTES;
222 			break;
223 		case LDNS_RDF_TYPE_ILNP64:
224 		case LDNS_RDF_TYPE_EUI64:
225 			cur_rdf_length = LDNS_RDF_SIZE_8BYTES;
226 			break;
227 		case LDNS_RDF_TYPE_AAAA:
228 			cur_rdf_length = LDNS_RDF_SIZE_16BYTES;
229 			break;
230 		case LDNS_RDF_TYPE_STR:
231 		case LDNS_RDF_TYPE_NSEC3_SALT:
232 		case LDNS_RDF_TYPE_TAG:
233 			/* len is stored in first byte
234 			 * it should be in the rdf too, so just
235 			 * copy len+1 from this position
236 			 */
237 			cur_rdf_length = ((size_t) wire[*pos]) + 1;
238 			break;
239 
240 		case LDNS_RDF_TYPE_INT16_DATA:
241 			if (*pos + 2 > end) {
242 				return LDNS_STATUS_PACKET_OVERFLOW;
243 			}
244 			cur_rdf_length =
245 				(size_t) ldns_read_uint16(&wire[*pos]) + 2;
246 			break;
247 		case LDNS_RDF_TYPE_HIP:
248 			if (*pos + 4 > end) {
249 				return LDNS_STATUS_PACKET_OVERFLOW;
250 			}
251 			cur_rdf_length =
252 				(size_t) wire[*pos] +
253 				(size_t) ldns_read_uint16(&wire[*pos + 2]) + 4;
254 			break;
255 		case LDNS_RDF_TYPE_B32_EXT:
256 		case LDNS_RDF_TYPE_NSEC3_NEXT_OWNER:
257 			/* length is stored in first byte */
258 			cur_rdf_length = ((size_t) wire[*pos]) + 1;
259 			break;
260 		case LDNS_RDF_TYPE_APL:
261 		case LDNS_RDF_TYPE_B64:
262 		case LDNS_RDF_TYPE_HEX:
263 		case LDNS_RDF_TYPE_NSEC:
264 		case LDNS_RDF_TYPE_UNKNOWN:
265 		case LDNS_RDF_TYPE_SERVICE:
266 		case LDNS_RDF_TYPE_LOC:
267 		case LDNS_RDF_TYPE_WKS:
268 		case LDNS_RDF_TYPE_NSAP:
269 		case LDNS_RDF_TYPE_ATMA:
270 		case LDNS_RDF_TYPE_IPSECKEY:
271 		case LDNS_RDF_TYPE_LONG_STR:
272 		case LDNS_RDF_TYPE_NONE:
273 			/*
274 			 * Read to end of rr rdata
275 			 */
276 			cur_rdf_length = end - *pos;
277 			break;
278 		}
279 
280 		/* fixed length rdata */
281 		if (cur_rdf_length > 0) {
282 			if (cur_rdf_length + *pos > end) {
283 				return LDNS_STATUS_PACKET_OVERFLOW;
284 			}
285 			data = LDNS_XMALLOC(uint8_t, rd_length);
286 			if (!data) {
287 				return LDNS_STATUS_MEM_ERR;
288 			}
289 			memcpy(data, &wire[*pos], cur_rdf_length);
290 
291 			cur_rdf = ldns_rdf_new(cur_rdf_type,
292 					cur_rdf_length, data);
293 			*pos = *pos + cur_rdf_length;
294 		}
295 
296 		if (cur_rdf) {
297 			ldns_rr_push_rdf(rr, cur_rdf);
298 			cur_rdf = NULL;
299 		}
300 
301 		rdf_index++;
302 
303 	} /* while (rdf_index < ldns_rr_descriptor_maximum(descriptor)) */
304 
305 
306 	return LDNS_STATUS_OK;
307 }
308 
309 
310 /* TODO:
311          can *pos be incremented at READ_INT? or maybe use something like
312          RR_CLASS(wire)?
313 	 uhhm Jelte??
314 */
315 ldns_status
316 ldns_wire2rr(ldns_rr **rr_p, const uint8_t *wire, size_t max,
317              size_t *pos, ldns_pkt_section section)
318 {
319 	ldns_rdf *owner = NULL;
320 	ldns_rr *rr = ldns_rr_new();
321 	ldns_status status;
322 
323 	status = ldns_wire2dname(&owner, wire, max, pos);
324 	LDNS_STATUS_CHECK_GOTO(status, status_error);
325 
326 	ldns_rr_set_owner(rr, owner);
327 
328 	if (*pos + 4 > max) {
329 		status = LDNS_STATUS_PACKET_OVERFLOW;
330 		goto status_error;
331 	}
332 
333 	ldns_rr_set_type(rr, ldns_read_uint16(&wire[*pos]));
334 	*pos = *pos + 2;
335 
336 	ldns_rr_set_class(rr, ldns_read_uint16(&wire[*pos]));
337 	*pos = *pos + 2;
338 
339 	if (section != LDNS_SECTION_QUESTION) {
340 		if (*pos + 4 > max) {
341 			status = LDNS_STATUS_PACKET_OVERFLOW;
342 			goto status_error;
343 		}
344 		ldns_rr_set_ttl(rr, ldns_read_uint32(&wire[*pos]));
345 
346 		*pos = *pos + 4;
347 		status = ldns_wire2rdf(rr, wire, max, pos);
348 
349 		LDNS_STATUS_CHECK_GOTO(status, status_error);
350         ldns_rr_set_question(rr, false);
351 	} else {
352         ldns_rr_set_question(rr, true);
353     }
354 
355 	*rr_p = rr;
356 	return LDNS_STATUS_OK;
357 
358 status_error:
359 	ldns_rr_free(rr);
360 	return status;
361 }
362 
363 static ldns_status
364 ldns_wire2pkt_hdr(ldns_pkt *packet, const uint8_t *wire, size_t max, size_t *pos)
365 {
366 	if (*pos + LDNS_HEADER_SIZE > max) {
367 		return LDNS_STATUS_WIRE_INCOMPLETE_HEADER;
368 	} else {
369 		ldns_pkt_set_id(packet, LDNS_ID_WIRE(wire));
370 		ldns_pkt_set_qr(packet, LDNS_QR_WIRE(wire));
371 		ldns_pkt_set_opcode(packet, LDNS_OPCODE_WIRE(wire));
372 		ldns_pkt_set_aa(packet, LDNS_AA_WIRE(wire));
373 		ldns_pkt_set_tc(packet, LDNS_TC_WIRE(wire));
374 		ldns_pkt_set_rd(packet, LDNS_RD_WIRE(wire));
375 		ldns_pkt_set_ra(packet, LDNS_RA_WIRE(wire));
376 		ldns_pkt_set_ad(packet, LDNS_AD_WIRE(wire));
377 		ldns_pkt_set_cd(packet, LDNS_CD_WIRE(wire));
378 		ldns_pkt_set_rcode(packet, LDNS_RCODE_WIRE(wire));
379 
380 		ldns_pkt_set_qdcount(packet, LDNS_QDCOUNT(wire));
381 		ldns_pkt_set_ancount(packet, LDNS_ANCOUNT(wire));
382 		ldns_pkt_set_nscount(packet, LDNS_NSCOUNT(wire));
383 		ldns_pkt_set_arcount(packet, LDNS_ARCOUNT(wire));
384 
385 		*pos += LDNS_HEADER_SIZE;
386 
387 		return LDNS_STATUS_OK;
388 	}
389 }
390 
391 ldns_status
392 ldns_buffer2pkt_wire(ldns_pkt **packet, ldns_buffer *buffer)
393 {
394 	/* lazy */
395 	return ldns_wire2pkt(packet, ldns_buffer_begin(buffer),
396 				ldns_buffer_limit(buffer));
397 
398 }
399 
400 ldns_status
401 ldns_wire2pkt(ldns_pkt **packet_p, const uint8_t *wire, size_t max)
402 {
403 	size_t pos = 0;
404 	uint16_t i;
405 	ldns_rr *rr;
406 	ldns_pkt *packet = ldns_pkt_new();
407 	ldns_status status = LDNS_STATUS_OK;
408 	int have_edns = 0;
409 
410 	uint8_t data[4];
411 
412 	status = ldns_wire2pkt_hdr(packet, wire, max, &pos);
413 	LDNS_STATUS_CHECK_GOTO(status, status_error);
414 
415 	for (i = 0; i < ldns_pkt_qdcount(packet); i++) {
416 
417 		status = ldns_wire2rr(&rr, wire, max, &pos, LDNS_SECTION_QUESTION);
418 		if (status == LDNS_STATUS_PACKET_OVERFLOW) {
419 			status = LDNS_STATUS_WIRE_INCOMPLETE_QUESTION;
420 		}
421 		LDNS_STATUS_CHECK_GOTO(status, status_error);
422 		if (!ldns_rr_list_push_rr(ldns_pkt_question(packet), rr)) {
423 			ldns_pkt_free(packet);
424 			return LDNS_STATUS_INTERNAL_ERR;
425 		}
426 	}
427 	for (i = 0; i < ldns_pkt_ancount(packet); i++) {
428 		status = ldns_wire2rr(&rr, wire, max, &pos, LDNS_SECTION_ANSWER);
429 		if (status == LDNS_STATUS_PACKET_OVERFLOW) {
430 			status = LDNS_STATUS_WIRE_INCOMPLETE_ANSWER;
431 		}
432 		LDNS_STATUS_CHECK_GOTO(status, status_error);
433 		if (!ldns_rr_list_push_rr(ldns_pkt_answer(packet), rr)) {
434 			ldns_pkt_free(packet);
435 			return LDNS_STATUS_INTERNAL_ERR;
436 		}
437 	}
438 	for (i = 0; i < ldns_pkt_nscount(packet); i++) {
439 		status = ldns_wire2rr(&rr, wire, max, &pos, LDNS_SECTION_AUTHORITY);
440 		if (status == LDNS_STATUS_PACKET_OVERFLOW) {
441 			status = LDNS_STATUS_WIRE_INCOMPLETE_AUTHORITY;
442 		}
443 		LDNS_STATUS_CHECK_GOTO(status, status_error);
444 		if (!ldns_rr_list_push_rr(ldns_pkt_authority(packet), rr)) {
445 			ldns_pkt_free(packet);
446 			return LDNS_STATUS_INTERNAL_ERR;
447 		}
448 	}
449 	for (i = 0; i < ldns_pkt_arcount(packet); i++) {
450 		status = ldns_wire2rr(&rr, wire, max, &pos, LDNS_SECTION_ADDITIONAL);
451 		if (status == LDNS_STATUS_PACKET_OVERFLOW) {
452 			status = LDNS_STATUS_WIRE_INCOMPLETE_ADDITIONAL;
453 		}
454 		LDNS_STATUS_CHECK_GOTO(status, status_error);
455 
456 		if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_OPT) {
457 			ldns_pkt_set_edns_udp_size(packet, ldns_rr_get_class(rr));
458 			ldns_write_uint32(data, ldns_rr_ttl(rr));
459 			ldns_pkt_set_edns_extended_rcode(packet, data[0]);
460 			ldns_pkt_set_edns_version(packet, data[1]);
461 			ldns_pkt_set_edns_z(packet, ldns_read_uint16(&data[2]));
462 			/* edns might not have rdfs */
463 			if (ldns_rr_rdf(rr, 0)) {
464 				ldns_pkt_set_edns_data(packet, ldns_rdf_clone(ldns_rr_rdf(rr, 0)));
465 			}
466 			ldns_rr_free(rr);
467 			have_edns += 1;
468 		} else if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_TSIG) {
469 			ldns_pkt_set_tsig(packet, rr);
470 			ldns_pkt_set_arcount(packet, ldns_pkt_arcount(packet) - 1);
471 		} else if (!ldns_rr_list_push_rr(ldns_pkt_additional(packet), rr)) {
472 			ldns_pkt_free(packet);
473 			return LDNS_STATUS_INTERNAL_ERR;
474 		}
475 	}
476 	ldns_pkt_set_size(packet, max);
477 	if(have_edns)
478 		ldns_pkt_set_arcount(packet, ldns_pkt_arcount(packet)
479                         - have_edns);
480 
481 	*packet_p = packet;
482 	return status;
483 
484 status_error:
485 	ldns_pkt_free(packet);
486 	return status;
487 }
488