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