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