kernel - Fix races created by a comedy of circumstansces (3)
[dragonfly.git] / contrib / ldns / wire2host.c
blobe87fcdf5df64725d891079752778411bbd6ddb82
1 /*
2 * wire2host.c
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)
9 * a Net::DNS like library for C
11 * (c) NLnet Labs, 2004-2006
13 * See the file LICENSE for the license
17 #include <ldns/config.h>
19 #include <ldns/ldns.h>
20 /*#include <ldns/wire2host.h>*/
22 #include <strings.h>
23 #include <limits.h>
28 * Set of macro's to deal with the dns message header as specified
29 * in RFC1035 in portable way.
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 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
54 /* allocates memory to *dname! */
55 ldns_status
56 ldns_wire2dname(ldns_rdf **dname, const uint8_t *wire, size_t max, size_t *pos)
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;
67 if (*pos >= max) {
68 return LDNS_STATUS_PACKET_OVERFLOW;
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;
79 pointer_count++;
81 /* remove first two bits */
82 if (*pos + 2 > max) {
83 return LDNS_STATUS_PACKET_OVERFLOW;
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);
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;
96 *pos = pointer_target;
97 label_size = wire[*pos];
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;
104 if (*pos + 1 + label_size > max) {
105 return LDNS_STATUS_LABEL_OVERFLOW;
108 /* check space for labelcount itself */
109 if (dname_pos + 1 > LDNS_MAX_DOMAINLEN) {
110 return LDNS_STATUS_DOMAINNAME_OVERFLOW;
112 tmp_dname[dname_pos] = label_size;
113 if (label_size > 0) {
114 dname_pos++;
116 *pos = *pos + 1;
117 if (dname_pos + label_size > LDNS_MAX_DOMAINLEN) {
118 return LDNS_STATUS_DOMAINNAME_OVERFLOW;
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;
125 if (*pos < max) {
126 label_size = wire[*pos];
130 if (compression_pos > 0) {
131 *pos = compression_pos;
132 } else {
133 *pos = *pos + 1;
136 if (dname_pos >= LDNS_MAX_DOMAINLEN) {
137 return LDNS_STATUS_DOMAINNAME_OVERFLOW;
140 tmp_dname[dname_pos] = 0;
141 dname_pos++;
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;
148 return LDNS_STATUS_OK;
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; }}
155 ldns_status
156 ldns_wire2rdf(ldns_rr *rr, const uint8_t *wire, size_t max, size_t *pos)
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;
168 if (*pos + 2 > max) {
169 return LDNS_STATUS_PACKET_OVERFLOW;
172 rd_length = ldns_read_uint16(&wire[*pos]);
173 *pos = *pos + 2;
175 if (*pos + rd_length > max) {
176 return LDNS_STATUS_PACKET_OVERFLOW;
179 end = *pos + (size_t) rd_length;
181 for (rdf_index = 0;
182 rdf_index < ldns_rr_descriptor_maximum(descriptor); rdf_index++) {
183 if (*pos >= end) {
184 break;
186 cur_rdf_length = 0;
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
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:
248 * Read to end of rr rdata
250 cur_rdf_length = end - *pos;
251 break;
254 /* fixed length rdata */
255 if (cur_rdf_length > 0) {
256 if (cur_rdf_length + *pos > end) {
257 return LDNS_STATUS_PACKET_OVERFLOW;
259 data = LDNS_XMALLOC(uint8_t, rd_length);
260 if (!data) {
261 return LDNS_STATUS_MEM_ERR;
263 memcpy(data, &wire[*pos], cur_rdf_length);
265 cur_rdf = ldns_rdf_new(cur_rdf_type, cur_rdf_length, data);
266 *pos = *pos + cur_rdf_length;
269 if (cur_rdf) {
270 ldns_rr_push_rdf(rr, cur_rdf);
271 cur_rdf = NULL;
275 return LDNS_STATUS_OK;
279 /* TODO:
280 can *pos be incremented at READ_INT? or maybe use something like
281 RR_CLASS(wire)?
282 uhhm Jelte??
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)
288 ldns_rdf *owner = NULL;
289 ldns_rr *rr = ldns_rr_new();
290 ldns_status status;
292 status = ldns_wire2dname(&owner, wire, max, pos);
293 LDNS_STATUS_CHECK_GOTO(status, status_error);
295 ldns_rr_set_owner(rr, owner);
297 if (*pos + 4 > max) {
298 status = LDNS_STATUS_PACKET_OVERFLOW;
299 goto status_error;
302 ldns_rr_set_type(rr, ldns_read_uint16(&wire[*pos]));
303 *pos = *pos + 2;
305 ldns_rr_set_class(rr, ldns_read_uint16(&wire[*pos]));
306 *pos = *pos + 2;
308 if (section != LDNS_SECTION_QUESTION) {
309 if (*pos + 4 > max) {
310 status = LDNS_STATUS_PACKET_OVERFLOW;
311 goto status_error;
313 ldns_rr_set_ttl(rr, ldns_read_uint32(&wire[*pos]));
315 *pos = *pos + 4;
316 status = ldns_wire2rdf(rr, wire, max, pos);
318 LDNS_STATUS_CHECK_GOTO(status, status_error);
319 ldns_rr_set_question(rr, false);
320 } else {
321 ldns_rr_set_question(rr, true);
324 *rr_p = rr;
325 return LDNS_STATUS_OK;
327 status_error:
328 ldns_rr_free(rr);
329 return status;
332 static ldns_status
333 ldns_wire2pkt_hdr(ldns_pkt *packet, const uint8_t *wire, size_t max, size_t *pos)
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));
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));
354 *pos += LDNS_HEADER_SIZE;
356 return LDNS_STATUS_OK;
360 ldns_status
361 ldns_buffer2pkt_wire(ldns_pkt **packet, ldns_buffer *buffer)
363 /* lazy */
364 return ldns_wire2pkt(packet, ldns_buffer_begin(buffer),
365 ldns_buffer_limit(buffer));
369 ldns_status
370 ldns_wire2pkt(ldns_pkt **packet_p, const uint8_t *wire, size_t max)
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;
379 uint8_t data[4];
381 status = ldns_wire2pkt_hdr(packet, wire, max, &pos);
382 LDNS_STATUS_CHECK_GOTO(status, status_error);
384 for (i = 0; i < ldns_pkt_qdcount(packet); i++) {
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;
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;
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;
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;
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;
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;
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;
423 LDNS_STATUS_CHECK_GOTO(status, status_error);
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)));
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;
445 ldns_pkt_set_size(packet, max);
446 if(have_edns)
447 ldns_pkt_set_arcount(packet, ldns_pkt_arcount(packet)
448 - have_edns);
450 *packet_p = packet;
451 return status;
453 status_error:
454 ldns_pkt_free(packet);
455 return status;