xref: /dragonfly/contrib/ldns/wire2host.c (revision 0ca59c34)
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 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 
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 = 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 */
284 ldns_status
285 ldns_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 
327 status_error:
328 	ldns_rr_free(rr);
329 	return status;
330 }
331 
332 static ldns_status
333 ldns_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 
360 ldns_status
361 ldns_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 
369 ldns_status
370 ldns_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 
453 status_error:
454 	ldns_pkt_free(packet);
455 	return status;
456 }
457