Commit | Line | Data |
---|---|---|
b9b3af22 JL |
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; | |
435c04e0 | 91 | } else if (pointer_target >= max) { |
b9b3af22 JL |
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 | } | |
435c04e0 | 104 | if (*pos + 1 + label_size > max) { |
b9b3af22 JL |
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); | |
435c04e0 | 436 | have_edns += 1; |
b9b3af22 JL |
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) | |
435c04e0 JL |
447 | ldns_pkt_set_arcount(packet, ldns_pkt_arcount(packet) |
448 | - have_edns); | |
b9b3af22 JL |
449 | |
450 | *packet_p = packet; | |
451 | return status; | |
452 | ||
453 | status_error: | |
454 | ldns_pkt_free(packet); | |
455 | return status; | |
456 | } |