1238104Sdes/*
2238104Sdes * wire2host.c
3238104Sdes *
4238104Sdes * conversion routines from the wire to the host
5238104Sdes * format.
6238104Sdes * This will usually just a re-ordering of the
7238104Sdes * data (as we store it in network format)
8238104Sdes *
9238104Sdes * a Net::DNS like library for C
10238104Sdes *
11238104Sdes * (c) NLnet Labs, 2004-2006
12238104Sdes *
13238104Sdes * See the file LICENSE for the license
14238104Sdes */
15238104Sdes
16238104Sdes
17238104Sdes#include <ldns/config.h>
18238104Sdes
19238104Sdes#include <ldns/ldns.h>
20238104Sdes/*#include <ldns/wire2host.h>*/
21238104Sdes
22238104Sdes#include <strings.h>
23238104Sdes#include <limits.h>
24238104Sdes
25238104Sdes
26238104Sdes
27238104Sdes/*
28238104Sdes * Set of macro's to deal with the dns message header as specified
29238104Sdes * in RFC1035 in portable way.
30238104Sdes *
31238104Sdes */
32238104Sdes
33238104Sdes/*
34238104Sdes *
35238104Sdes *                                    1  1  1  1  1  1
36238104Sdes *      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
37238104Sdes *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
38238104Sdes *    |                      ID                       |
39238104Sdes *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
40238104Sdes *    |QR|   Opcode  |AA|TC|RD|RA| Z|AD|CD|   RCODE   |
41238104Sdes *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
42238104Sdes *    |                    QDCOUNT                    |
43238104Sdes *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
44238104Sdes *    |                    ANCOUNT                    |
45238104Sdes *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
46238104Sdes *    |                    NSCOUNT                    |
47238104Sdes *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
48238104Sdes *    |                    ARCOUNT                    |
49238104Sdes *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
50238104Sdes *
51238104Sdes */
52238104Sdes
53238104Sdes
54238104Sdes/* allocates memory to *dname! */
55238104Sdesldns_status
56238104Sdesldns_wire2dname(ldns_rdf **dname, const uint8_t *wire, size_t max, size_t *pos)
57238104Sdes{
58238104Sdes	uint8_t label_size;
59238104Sdes	uint16_t pointer_target;
60238104Sdes	uint8_t pointer_target_buf[2];
61238104Sdes	size_t dname_pos = 0;
62238104Sdes	size_t uncompressed_length = 0;
63238104Sdes	size_t compression_pos = 0;
64238104Sdes	uint8_t tmp_dname[LDNS_MAX_DOMAINLEN];
65238104Sdes	unsigned int pointer_count = 0;
66238104Sdes
67269257Sdes	if (pos == NULL) {
68269257Sdes		return LDNS_STATUS_WIRE_RDATA_ERR;
69269257Sdes	}
70238104Sdes	if (*pos >= max) {
71238104Sdes		return LDNS_STATUS_PACKET_OVERFLOW;
72238104Sdes	}
73238104Sdes	label_size = wire[*pos];
74238104Sdes	while (label_size > 0) {
75238104Sdes		/* compression */
76238104Sdes		while (label_size >= 192) {
77238104Sdes			if (compression_pos == 0) {
78238104Sdes				compression_pos = *pos + 2;
79238104Sdes			}
80238104Sdes
81238104Sdes			pointer_count++;
82238104Sdes
83238104Sdes			/* remove first two bits */
84238104Sdes			if (*pos + 2 > max) {
85238104Sdes				return LDNS_STATUS_PACKET_OVERFLOW;
86238104Sdes			}
87238104Sdes			pointer_target_buf[0] = wire[*pos] & 63;
88238104Sdes			pointer_target_buf[1] = wire[*pos + 1];
89238104Sdes			pointer_target = ldns_read_uint16(pointer_target_buf);
90238104Sdes
91238104Sdes			if (pointer_target == 0) {
92238104Sdes				return LDNS_STATUS_INVALID_POINTER;
93238104Sdes			} else if (pointer_target >= max) {
94238104Sdes				return LDNS_STATUS_INVALID_POINTER;
95238104Sdes			} else if (pointer_count > LDNS_MAX_POINTERS) {
96238104Sdes				return LDNS_STATUS_INVALID_POINTER;
97238104Sdes			}
98238104Sdes			*pos = pointer_target;
99238104Sdes			label_size = wire[*pos];
100238104Sdes		}
101238104Sdes		if(label_size == 0)
102238104Sdes			break; /* break from pointer to 0 byte */
103238104Sdes		if (label_size > LDNS_MAX_LABELLEN) {
104238104Sdes			return LDNS_STATUS_LABEL_OVERFLOW;
105238104Sdes		}
106238104Sdes		if (*pos + 1 + label_size > max) {
107238104Sdes			return LDNS_STATUS_LABEL_OVERFLOW;
108238104Sdes		}
109238104Sdes
110238104Sdes		/* check space for labelcount itself */
111238104Sdes		if (dname_pos + 1 > LDNS_MAX_DOMAINLEN) {
112238104Sdes			return LDNS_STATUS_DOMAINNAME_OVERFLOW;
113238104Sdes		}
114238104Sdes		tmp_dname[dname_pos] = label_size;
115238104Sdes		if (label_size > 0) {
116238104Sdes			dname_pos++;
117238104Sdes		}
118238104Sdes		*pos = *pos + 1;
119238104Sdes		if (dname_pos + label_size > LDNS_MAX_DOMAINLEN) {
120238104Sdes			return LDNS_STATUS_DOMAINNAME_OVERFLOW;
121238104Sdes		}
122238104Sdes		memcpy(&tmp_dname[dname_pos], &wire[*pos], label_size);
123238104Sdes		uncompressed_length += label_size + 1;
124238104Sdes		dname_pos += label_size;
125238104Sdes		*pos = *pos + label_size;
126238104Sdes
127238104Sdes		if (*pos < max) {
128238104Sdes			label_size = wire[*pos];
129238104Sdes		}
130238104Sdes	}
131238104Sdes
132238104Sdes	if (compression_pos > 0) {
133238104Sdes		*pos = compression_pos;
134238104Sdes	} else {
135238104Sdes		*pos = *pos + 1;
136238104Sdes	}
137238104Sdes
138238104Sdes	if (dname_pos >= LDNS_MAX_DOMAINLEN) {
139238104Sdes		return LDNS_STATUS_DOMAINNAME_OVERFLOW;
140238104Sdes	}
141238104Sdes
142238104Sdes	tmp_dname[dname_pos] = 0;
143238104Sdes	dname_pos++;
144238104Sdes
145238104Sdes	*dname = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_DNAME,
146238104Sdes			(uint16_t) dname_pos, tmp_dname);
147238104Sdes	if (!*dname) {
148238104Sdes		return LDNS_STATUS_MEM_ERR;
149238104Sdes	}
150238104Sdes	return LDNS_STATUS_OK;
151238104Sdes}
152238104Sdes
153238104Sdes/* maybe make this a goto error so data can be freed or something/ */
154238104Sdes#define LDNS_STATUS_CHECK_RETURN(st) {if (st != LDNS_STATUS_OK) { return st; }}
155238104Sdes#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; }}
156238104Sdes
157238104Sdesldns_status
158238104Sdesldns_wire2rdf(ldns_rr *rr, const uint8_t *wire, size_t max, size_t *pos)
159238104Sdes{
160238104Sdes	size_t end;
161238104Sdes	size_t cur_rdf_length;
162238104Sdes	uint8_t rdf_index;
163238104Sdes	uint8_t *data;
164238104Sdes	uint16_t rd_length;
165238104Sdes	ldns_rdf *cur_rdf = NULL;
166238104Sdes	ldns_rdf_type cur_rdf_type;
167269257Sdes	const ldns_rr_descriptor *descriptor;
168238104Sdes	ldns_status status;
169238104Sdes
170269257Sdes	assert(rr != NULL);
171269257Sdes
172269257Sdes	descriptor = ldns_rr_descript(ldns_rr_get_type(rr));
173269257Sdes
174238104Sdes	if (*pos + 2 > max) {
175238104Sdes		return LDNS_STATUS_PACKET_OVERFLOW;
176238104Sdes	}
177238104Sdes
178238104Sdes	rd_length = ldns_read_uint16(&wire[*pos]);
179238104Sdes	*pos = *pos + 2;
180238104Sdes
181238104Sdes	if (*pos + rd_length > max) {
182238104Sdes		return LDNS_STATUS_PACKET_OVERFLOW;
183238104Sdes	}
184238104Sdes
185238104Sdes	end = *pos + (size_t) rd_length;
186238104Sdes
187269257Sdes	rdf_index = 0;
188269257Sdes	while (*pos < end &&
189269257Sdes			rdf_index < ldns_rr_descriptor_maximum(descriptor)) {
190269257Sdes
191238104Sdes		cur_rdf_length = 0;
192238104Sdes
193269257Sdes		cur_rdf_type = ldns_rr_descriptor_field_type(
194269257Sdes				descriptor, rdf_index);
195269257Sdes
196238104Sdes		/* handle special cases immediately, set length
197238104Sdes		   for fixed length rdata and do them below */
198238104Sdes		switch (cur_rdf_type) {
199238104Sdes		case LDNS_RDF_TYPE_DNAME:
200238104Sdes			status = ldns_wire2dname(&cur_rdf, wire, max, pos);
201238104Sdes			LDNS_STATUS_CHECK_RETURN(status);
202238104Sdes			break;
203238104Sdes		case LDNS_RDF_TYPE_CLASS:
204238104Sdes		case LDNS_RDF_TYPE_ALG:
205238104Sdes		case LDNS_RDF_TYPE_INT8:
206238104Sdes			cur_rdf_length = LDNS_RDF_SIZE_BYTE;
207238104Sdes			break;
208238104Sdes		case LDNS_RDF_TYPE_TYPE:
209238104Sdes		case LDNS_RDF_TYPE_INT16:
210238104Sdes		case LDNS_RDF_TYPE_CERT_ALG:
211238104Sdes			cur_rdf_length = LDNS_RDF_SIZE_WORD;
212238104Sdes			break;
213238104Sdes		case LDNS_RDF_TYPE_TIME:
214238104Sdes		case LDNS_RDF_TYPE_INT32:
215238104Sdes		case LDNS_RDF_TYPE_A:
216238104Sdes		case LDNS_RDF_TYPE_PERIOD:
217238104Sdes			cur_rdf_length = LDNS_RDF_SIZE_DOUBLEWORD;
218238104Sdes			break;
219238104Sdes		case LDNS_RDF_TYPE_TSIGTIME:
220269257Sdes		case LDNS_RDF_TYPE_EUI48:
221238104Sdes			cur_rdf_length = LDNS_RDF_SIZE_6BYTES;
222238104Sdes			break;
223269257Sdes		case LDNS_RDF_TYPE_ILNP64:
224269257Sdes		case LDNS_RDF_TYPE_EUI64:
225269257Sdes			cur_rdf_length = LDNS_RDF_SIZE_8BYTES;
226269257Sdes			break;
227238104Sdes		case LDNS_RDF_TYPE_AAAA:
228238104Sdes			cur_rdf_length = LDNS_RDF_SIZE_16BYTES;
229238104Sdes			break;
230238104Sdes		case LDNS_RDF_TYPE_STR:
231238104Sdes		case LDNS_RDF_TYPE_NSEC3_SALT:
232269257Sdes		case LDNS_RDF_TYPE_TAG:
233238104Sdes			/* len is stored in first byte
234238104Sdes			 * it should be in the rdf too, so just
235238104Sdes			 * copy len+1 from this position
236238104Sdes			 */
237238104Sdes			cur_rdf_length = ((size_t) wire[*pos]) + 1;
238238104Sdes			break;
239269257Sdes
240238104Sdes		case LDNS_RDF_TYPE_INT16_DATA:
241269257Sdes			if (*pos + 2 > end) {
242269257Sdes				return LDNS_STATUS_PACKET_OVERFLOW;
243269257Sdes			}
244269257Sdes			cur_rdf_length =
245269257Sdes				(size_t) ldns_read_uint16(&wire[*pos]) + 2;
246238104Sdes			break;
247269257Sdes		case LDNS_RDF_TYPE_HIP:
248269257Sdes			if (*pos + 4 > end) {
249269257Sdes				return LDNS_STATUS_PACKET_OVERFLOW;
250269257Sdes			}
251269257Sdes			cur_rdf_length =
252269257Sdes				(size_t) wire[*pos] +
253269257Sdes				(size_t) ldns_read_uint16(&wire[*pos + 2]) + 4;
254269257Sdes			break;
255238104Sdes		case LDNS_RDF_TYPE_B32_EXT:
256238104Sdes		case LDNS_RDF_TYPE_NSEC3_NEXT_OWNER:
257238104Sdes			/* length is stored in first byte */
258238104Sdes			cur_rdf_length = ((size_t) wire[*pos]) + 1;
259238104Sdes			break;
260238104Sdes		case LDNS_RDF_TYPE_APL:
261238104Sdes		case LDNS_RDF_TYPE_B64:
262238104Sdes		case LDNS_RDF_TYPE_HEX:
263238104Sdes		case LDNS_RDF_TYPE_NSEC:
264238104Sdes		case LDNS_RDF_TYPE_UNKNOWN:
265238104Sdes		case LDNS_RDF_TYPE_SERVICE:
266238104Sdes		case LDNS_RDF_TYPE_LOC:
267238104Sdes		case LDNS_RDF_TYPE_WKS:
268238104Sdes		case LDNS_RDF_TYPE_NSAP:
269238104Sdes		case LDNS_RDF_TYPE_ATMA:
270238104Sdes		case LDNS_RDF_TYPE_IPSECKEY:
271269257Sdes		case LDNS_RDF_TYPE_LONG_STR:
272238104Sdes		case LDNS_RDF_TYPE_NONE:
273238104Sdes			/*
274238104Sdes			 * Read to end of rr rdata
275238104Sdes			 */
276238104Sdes			cur_rdf_length = end - *pos;
277238104Sdes			break;
278238104Sdes		}
279238104Sdes
280238104Sdes		/* fixed length rdata */
281238104Sdes		if (cur_rdf_length > 0) {
282238104Sdes			if (cur_rdf_length + *pos > end) {
283238104Sdes				return LDNS_STATUS_PACKET_OVERFLOW;
284238104Sdes			}
285238104Sdes			data = LDNS_XMALLOC(uint8_t, rd_length);
286238104Sdes			if (!data) {
287238104Sdes				return LDNS_STATUS_MEM_ERR;
288238104Sdes			}
289238104Sdes			memcpy(data, &wire[*pos], cur_rdf_length);
290238104Sdes
291269257Sdes			cur_rdf = ldns_rdf_new(cur_rdf_type,
292269257Sdes					cur_rdf_length, data);
293238104Sdes			*pos = *pos + cur_rdf_length;
294238104Sdes		}
295238104Sdes
296238104Sdes		if (cur_rdf) {
297238104Sdes			ldns_rr_push_rdf(rr, cur_rdf);
298238104Sdes			cur_rdf = NULL;
299238104Sdes		}
300238104Sdes
301269257Sdes		rdf_index++;
302269257Sdes
303269257Sdes	} /* while (rdf_index < ldns_rr_descriptor_maximum(descriptor)) */
304269257Sdes
305269257Sdes
306238104Sdes	return LDNS_STATUS_OK;
307238104Sdes}
308238104Sdes
309238104Sdes
310238104Sdes/* TODO:
311238104Sdes         can *pos be incremented at READ_INT? or maybe use something like
312238104Sdes         RR_CLASS(wire)?
313238104Sdes	 uhhm Jelte??
314238104Sdes*/
315238104Sdesldns_status
316238104Sdesldns_wire2rr(ldns_rr **rr_p, const uint8_t *wire, size_t max,
317238104Sdes             size_t *pos, ldns_pkt_section section)
318238104Sdes{
319238104Sdes	ldns_rdf *owner = NULL;
320238104Sdes	ldns_rr *rr = ldns_rr_new();
321238104Sdes	ldns_status status;
322238104Sdes
323238104Sdes	status = ldns_wire2dname(&owner, wire, max, pos);
324238104Sdes	LDNS_STATUS_CHECK_GOTO(status, status_error);
325238104Sdes
326238104Sdes	ldns_rr_set_owner(rr, owner);
327238104Sdes
328238104Sdes	if (*pos + 4 > max) {
329238104Sdes		status = LDNS_STATUS_PACKET_OVERFLOW;
330238104Sdes		goto status_error;
331238104Sdes	}
332238104Sdes
333238104Sdes	ldns_rr_set_type(rr, ldns_read_uint16(&wire[*pos]));
334238104Sdes	*pos = *pos + 2;
335238104Sdes
336238104Sdes	ldns_rr_set_class(rr, ldns_read_uint16(&wire[*pos]));
337238104Sdes	*pos = *pos + 2;
338238104Sdes
339238104Sdes	if (section != LDNS_SECTION_QUESTION) {
340238104Sdes		if (*pos + 4 > max) {
341238104Sdes			status = LDNS_STATUS_PACKET_OVERFLOW;
342238104Sdes			goto status_error;
343238104Sdes		}
344238104Sdes		ldns_rr_set_ttl(rr, ldns_read_uint32(&wire[*pos]));
345238104Sdes
346238104Sdes		*pos = *pos + 4;
347238104Sdes		status = ldns_wire2rdf(rr, wire, max, pos);
348238104Sdes
349238104Sdes		LDNS_STATUS_CHECK_GOTO(status, status_error);
350238104Sdes        ldns_rr_set_question(rr, false);
351238104Sdes	} else {
352238104Sdes        ldns_rr_set_question(rr, true);
353238104Sdes    }
354238104Sdes
355238104Sdes	*rr_p = rr;
356238104Sdes	return LDNS_STATUS_OK;
357238104Sdes
358238104Sdesstatus_error:
359238104Sdes	ldns_rr_free(rr);
360238104Sdes	return status;
361238104Sdes}
362238104Sdes
363238104Sdesstatic ldns_status
364238104Sdesldns_wire2pkt_hdr(ldns_pkt *packet, const uint8_t *wire, size_t max, size_t *pos)
365238104Sdes{
366238104Sdes	if (*pos + LDNS_HEADER_SIZE > max) {
367238104Sdes		return LDNS_STATUS_WIRE_INCOMPLETE_HEADER;
368238104Sdes	} else {
369238104Sdes		ldns_pkt_set_id(packet, LDNS_ID_WIRE(wire));
370238104Sdes		ldns_pkt_set_qr(packet, LDNS_QR_WIRE(wire));
371238104Sdes		ldns_pkt_set_opcode(packet, LDNS_OPCODE_WIRE(wire));
372238104Sdes		ldns_pkt_set_aa(packet, LDNS_AA_WIRE(wire));
373238104Sdes		ldns_pkt_set_tc(packet, LDNS_TC_WIRE(wire));
374238104Sdes		ldns_pkt_set_rd(packet, LDNS_RD_WIRE(wire));
375238104Sdes		ldns_pkt_set_ra(packet, LDNS_RA_WIRE(wire));
376238104Sdes		ldns_pkt_set_ad(packet, LDNS_AD_WIRE(wire));
377238104Sdes		ldns_pkt_set_cd(packet, LDNS_CD_WIRE(wire));
378238104Sdes		ldns_pkt_set_rcode(packet, LDNS_RCODE_WIRE(wire));
379238104Sdes
380238104Sdes		ldns_pkt_set_qdcount(packet, LDNS_QDCOUNT(wire));
381238104Sdes		ldns_pkt_set_ancount(packet, LDNS_ANCOUNT(wire));
382238104Sdes		ldns_pkt_set_nscount(packet, LDNS_NSCOUNT(wire));
383238104Sdes		ldns_pkt_set_arcount(packet, LDNS_ARCOUNT(wire));
384238104Sdes
385238104Sdes		*pos += LDNS_HEADER_SIZE;
386238104Sdes
387238104Sdes		return LDNS_STATUS_OK;
388238104Sdes	}
389238104Sdes}
390238104Sdes
391238104Sdesldns_status
392238104Sdesldns_buffer2pkt_wire(ldns_pkt **packet, ldns_buffer *buffer)
393238104Sdes{
394238104Sdes	/* lazy */
395238104Sdes	return ldns_wire2pkt(packet, ldns_buffer_begin(buffer),
396238104Sdes				ldns_buffer_limit(buffer));
397238104Sdes
398238104Sdes}
399238104Sdes
400238104Sdesldns_status
401238104Sdesldns_wire2pkt(ldns_pkt **packet_p, const uint8_t *wire, size_t max)
402238104Sdes{
403238104Sdes	size_t pos = 0;
404238104Sdes	uint16_t i;
405238104Sdes	ldns_rr *rr;
406238104Sdes	ldns_pkt *packet = ldns_pkt_new();
407238104Sdes	ldns_status status = LDNS_STATUS_OK;
408238104Sdes	int have_edns = 0;
409238104Sdes
410238104Sdes	uint8_t data[4];
411238104Sdes
412238104Sdes	status = ldns_wire2pkt_hdr(packet, wire, max, &pos);
413238104Sdes	LDNS_STATUS_CHECK_GOTO(status, status_error);
414238104Sdes
415238104Sdes	for (i = 0; i < ldns_pkt_qdcount(packet); i++) {
416238104Sdes
417238104Sdes		status = ldns_wire2rr(&rr, wire, max, &pos, LDNS_SECTION_QUESTION);
418238104Sdes		if (status == LDNS_STATUS_PACKET_OVERFLOW) {
419238104Sdes			status = LDNS_STATUS_WIRE_INCOMPLETE_QUESTION;
420238104Sdes		}
421238104Sdes		LDNS_STATUS_CHECK_GOTO(status, status_error);
422238104Sdes		if (!ldns_rr_list_push_rr(ldns_pkt_question(packet), rr)) {
423238104Sdes			ldns_pkt_free(packet);
424238104Sdes			return LDNS_STATUS_INTERNAL_ERR;
425238104Sdes		}
426238104Sdes	}
427238104Sdes	for (i = 0; i < ldns_pkt_ancount(packet); i++) {
428238104Sdes		status = ldns_wire2rr(&rr, wire, max, &pos, LDNS_SECTION_ANSWER);
429238104Sdes		if (status == LDNS_STATUS_PACKET_OVERFLOW) {
430238104Sdes			status = LDNS_STATUS_WIRE_INCOMPLETE_ANSWER;
431238104Sdes		}
432238104Sdes		LDNS_STATUS_CHECK_GOTO(status, status_error);
433238104Sdes		if (!ldns_rr_list_push_rr(ldns_pkt_answer(packet), rr)) {
434238104Sdes			ldns_pkt_free(packet);
435238104Sdes			return LDNS_STATUS_INTERNAL_ERR;
436238104Sdes		}
437238104Sdes	}
438238104Sdes	for (i = 0; i < ldns_pkt_nscount(packet); i++) {
439238104Sdes		status = ldns_wire2rr(&rr, wire, max, &pos, LDNS_SECTION_AUTHORITY);
440238104Sdes		if (status == LDNS_STATUS_PACKET_OVERFLOW) {
441238104Sdes			status = LDNS_STATUS_WIRE_INCOMPLETE_AUTHORITY;
442238104Sdes		}
443238104Sdes		LDNS_STATUS_CHECK_GOTO(status, status_error);
444238104Sdes		if (!ldns_rr_list_push_rr(ldns_pkt_authority(packet), rr)) {
445238104Sdes			ldns_pkt_free(packet);
446238104Sdes			return LDNS_STATUS_INTERNAL_ERR;
447238104Sdes		}
448238104Sdes	}
449238104Sdes	for (i = 0; i < ldns_pkt_arcount(packet); i++) {
450238104Sdes		status = ldns_wire2rr(&rr, wire, max, &pos, LDNS_SECTION_ADDITIONAL);
451238104Sdes		if (status == LDNS_STATUS_PACKET_OVERFLOW) {
452238104Sdes			status = LDNS_STATUS_WIRE_INCOMPLETE_ADDITIONAL;
453238104Sdes		}
454238104Sdes		LDNS_STATUS_CHECK_GOTO(status, status_error);
455238104Sdes
456238104Sdes		if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_OPT) {
457238104Sdes			ldns_pkt_set_edns_udp_size(packet, ldns_rr_get_class(rr));
458238104Sdes			ldns_write_uint32(data, ldns_rr_ttl(rr));
459238104Sdes			ldns_pkt_set_edns_extended_rcode(packet, data[0]);
460238104Sdes			ldns_pkt_set_edns_version(packet, data[1]);
461238104Sdes			ldns_pkt_set_edns_z(packet, ldns_read_uint16(&data[2]));
462238104Sdes			/* edns might not have rdfs */
463238104Sdes			if (ldns_rr_rdf(rr, 0)) {
464238104Sdes				ldns_pkt_set_edns_data(packet, ldns_rdf_clone(ldns_rr_rdf(rr, 0)));
465238104Sdes			}
466238104Sdes			ldns_rr_free(rr);
467238104Sdes			have_edns += 1;
468238104Sdes		} else if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_TSIG) {
469238104Sdes			ldns_pkt_set_tsig(packet, rr);
470238104Sdes			ldns_pkt_set_arcount(packet, ldns_pkt_arcount(packet) - 1);
471238104Sdes		} else if (!ldns_rr_list_push_rr(ldns_pkt_additional(packet), rr)) {
472238104Sdes			ldns_pkt_free(packet);
473238104Sdes			return LDNS_STATUS_INTERNAL_ERR;
474238104Sdes		}
475238104Sdes	}
476238104Sdes	ldns_pkt_set_size(packet, max);
477238104Sdes	if(have_edns)
478238104Sdes		ldns_pkt_set_arcount(packet, ldns_pkt_arcount(packet)
479238104Sdes                        - have_edns);
480238104Sdes
481238104Sdes	*packet_p = packet;
482238104Sdes	return status;
483238104Sdes
484238104Sdesstatus_error:
485238104Sdes	ldns_pkt_free(packet);
486238104Sdes	return status;
487238104Sdes}
488