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 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
157ldns_status
158ldns_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*/
315ldns_status
316ldns_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
358status_error:
359	ldns_rr_free(rr);
360	return status;
361}
362
363static ldns_status
364ldns_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
391ldns_status
392ldns_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
400ldns_status
401ldns_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
484status_error:
485	ldns_pkt_free(packet);
486	return status;
487}
488