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! */
55ldns_status
56ldns_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
155ldns_status
156ldns_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*/
317ldns_status
318ldns_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
360status_error:
361	ldns_rr_free(rr);
362	return status;
363}
364
365static ldns_status
366ldns_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
393ldns_status
394ldns_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
402ldns_status
403ldns_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
492status_error:
493	ldns_pkt_free(packet);
494	return status;
495}
496