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