xref: /dragonfly/contrib/ldns/wire2host.c (revision 7733acb50455a11cc2ee36edd926ff0fa3361e9a)
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
ldns_wire2dname(ldns_rdf ** dname,const uint8_t * wire,size_t max,size_t * pos)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
ldns_wire2rdf(ldns_rr * rr,const uint8_t * wire,size_t max,size_t * pos)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_AMTRELAY:
276                     case LDNS_RDF_TYPE_SVCPARAMS:
277                     case LDNS_RDF_TYPE_NONE:
278                               /*
279                                * Read to end of rr rdata
280                                */
281                               cur_rdf_length = end - *pos;
282                               break;
283                     }
284 
285                     /* fixed length rdata */
286                     if (cur_rdf_length > 0) {
287                               if (cur_rdf_length + *pos > end) {
288                                         return LDNS_STATUS_PACKET_OVERFLOW;
289                               }
290                               data = LDNS_XMALLOC(uint8_t, rd_length);
291                               if (!data) {
292                                         return LDNS_STATUS_MEM_ERR;
293                               }
294                               memcpy(data, &wire[*pos], cur_rdf_length);
295 
296                               cur_rdf = ldns_rdf_new(cur_rdf_type,
297                                                   cur_rdf_length, data);
298                               *pos = *pos + cur_rdf_length;
299                     }
300 
301                     if (cur_rdf) {
302                               ldns_rr_push_rdf(rr, cur_rdf);
303                               cur_rdf = NULL;
304                     }
305 
306                     rdf_index++;
307 
308           } /* while (rdf_index < ldns_rr_descriptor_maximum(descriptor)) */
309 
310 
311           return LDNS_STATUS_OK;
312 }
313 
314 /* TODO:
315          can *pos be incremented at READ_INT? or maybe use something like
316          RR_CLASS(wire)?
317            uhhm Jelte??
318 */
319 ldns_status
ldns_wire2rr(ldns_rr ** rr_p,const uint8_t * wire,size_t max,size_t * pos,ldns_pkt_section section)320 ldns_wire2rr(ldns_rr **rr_p, const uint8_t *wire, size_t max,
321              size_t *pos, ldns_pkt_section section)
322 {
323           ldns_rdf *owner = NULL;
324           ldns_rr *rr = ldns_rr_new();
325           ldns_status status;
326 
327           status = ldns_wire2dname(&owner, wire, max, pos);
328           LDNS_STATUS_CHECK_GOTO(status, status_error);
329 
330           ldns_rr_set_owner(rr, owner);
331 
332           if (*pos + 4 > max) {
333                     status = LDNS_STATUS_PACKET_OVERFLOW;
334                     goto status_error;
335           }
336 
337           ldns_rr_set_type(rr, ldns_read_uint16(&wire[*pos]));
338           *pos = *pos + 2;
339 
340           ldns_rr_set_class(rr, ldns_read_uint16(&wire[*pos]));
341           *pos = *pos + 2;
342 
343           if (section != LDNS_SECTION_QUESTION) {
344                     if (*pos + 4 > max) {
345                               status = LDNS_STATUS_PACKET_OVERFLOW;
346                               goto status_error;
347                     }
348                     ldns_rr_set_ttl(rr, ldns_read_uint32(&wire[*pos]));
349 
350                     *pos = *pos + 4;
351                     status = ldns_wire2rdf(rr, wire, max, pos);
352 
353                     LDNS_STATUS_CHECK_GOTO(status, status_error);
354         ldns_rr_set_question(rr, false);
355           } else {
356         ldns_rr_set_question(rr, true);
357     }
358 
359           *rr_p = rr;
360           return LDNS_STATUS_OK;
361 
362 status_error:
363           ldns_rr_free(rr);
364           return status;
365 }
366 
367 static ldns_status
ldns_wire2pkt_hdr(ldns_pkt * packet,const uint8_t * wire,size_t max,size_t * pos)368 ldns_wire2pkt_hdr(ldns_pkt *packet, const uint8_t *wire, size_t max, size_t *pos)
369 {
370           if (*pos + LDNS_HEADER_SIZE > max) {
371                     return LDNS_STATUS_WIRE_INCOMPLETE_HEADER;
372           } else {
373                     ldns_pkt_set_id(packet, LDNS_ID_WIRE(wire));
374                     ldns_pkt_set_qr(packet, LDNS_QR_WIRE(wire));
375                     ldns_pkt_set_opcode(packet, LDNS_OPCODE_WIRE(wire));
376                     ldns_pkt_set_aa(packet, LDNS_AA_WIRE(wire));
377                     ldns_pkt_set_tc(packet, LDNS_TC_WIRE(wire));
378                     ldns_pkt_set_rd(packet, LDNS_RD_WIRE(wire));
379                     ldns_pkt_set_ra(packet, LDNS_RA_WIRE(wire));
380                     ldns_pkt_set_ad(packet, LDNS_AD_WIRE(wire));
381                     ldns_pkt_set_cd(packet, LDNS_CD_WIRE(wire));
382                     ldns_pkt_set_rcode(packet, LDNS_RCODE_WIRE(wire));
383 
384                     ldns_pkt_set_qdcount(packet, LDNS_QDCOUNT(wire));
385                     ldns_pkt_set_ancount(packet, LDNS_ANCOUNT(wire));
386                     ldns_pkt_set_nscount(packet, LDNS_NSCOUNT(wire));
387                     ldns_pkt_set_arcount(packet, LDNS_ARCOUNT(wire));
388 
389                     *pos += LDNS_HEADER_SIZE;
390 
391                     return LDNS_STATUS_OK;
392           }
393 }
394 
395 ldns_status
ldns_buffer2pkt_wire(ldns_pkt ** packet,const ldns_buffer * buffer)396 ldns_buffer2pkt_wire(ldns_pkt **packet, const ldns_buffer *buffer)
397 {
398           /* lazy */
399           return ldns_wire2pkt(packet, ldns_buffer_begin(buffer),
400                                         ldns_buffer_limit(buffer));
401 
402 }
403 
404 ldns_status
ldns_wire2pkt(ldns_pkt ** packet_p,const uint8_t * wire,size_t max)405 ldns_wire2pkt(ldns_pkt **packet_p, const uint8_t *wire, size_t max)
406 {
407           size_t pos = 0;
408           uint16_t i;
409           ldns_rr *rr;
410           ldns_pkt *packet = ldns_pkt_new();
411           ldns_status status = LDNS_STATUS_OK;
412           uint8_t have_edns = 0;
413 
414           uint8_t data[4];
415 
416           if (!packet) {
417                     return LDNS_STATUS_MEM_ERR;
418           }
419 
420           status = ldns_wire2pkt_hdr(packet, wire, max, &pos);
421           LDNS_STATUS_CHECK_GOTO(status, status_error);
422 
423           for (i = 0; i < ldns_pkt_qdcount(packet); i++) {
424 
425                     status = ldns_wire2rr(&rr, wire, max, &pos, LDNS_SECTION_QUESTION);
426                     if (status == LDNS_STATUS_PACKET_OVERFLOW) {
427                               status = LDNS_STATUS_WIRE_INCOMPLETE_QUESTION;
428                     }
429                     LDNS_STATUS_CHECK_GOTO(status, status_error);
430                     if (!ldns_rr_list_push_rr(ldns_pkt_question(packet), rr)) {
431                               ldns_pkt_free(packet);
432                               return LDNS_STATUS_INTERNAL_ERR;
433                     }
434           }
435           for (i = 0; i < ldns_pkt_ancount(packet); i++) {
436                     status = ldns_wire2rr(&rr, wire, max, &pos, LDNS_SECTION_ANSWER);
437                     if (status == LDNS_STATUS_PACKET_OVERFLOW) {
438                               status = LDNS_STATUS_WIRE_INCOMPLETE_ANSWER;
439                     }
440                     LDNS_STATUS_CHECK_GOTO(status, status_error);
441                     if (!ldns_rr_list_push_rr(ldns_pkt_answer(packet), rr)) {
442                               ldns_pkt_free(packet);
443                               return LDNS_STATUS_INTERNAL_ERR;
444                     }
445           }
446           for (i = 0; i < ldns_pkt_nscount(packet); i++) {
447                     status = ldns_wire2rr(&rr, wire, max, &pos, LDNS_SECTION_AUTHORITY);
448                     if (status == LDNS_STATUS_PACKET_OVERFLOW) {
449                               status = LDNS_STATUS_WIRE_INCOMPLETE_AUTHORITY;
450                     }
451                     LDNS_STATUS_CHECK_GOTO(status, status_error);
452                     if (!ldns_rr_list_push_rr(ldns_pkt_authority(packet), rr)) {
453                               ldns_pkt_free(packet);
454                               return LDNS_STATUS_INTERNAL_ERR;
455                     }
456           }
457           for (i = 0; i < ldns_pkt_arcount(packet); i++) {
458                     status = ldns_wire2rr(&rr, wire, max, &pos, LDNS_SECTION_ADDITIONAL);
459                     if (status == LDNS_STATUS_PACKET_OVERFLOW) {
460                               status = LDNS_STATUS_WIRE_INCOMPLETE_ADDITIONAL;
461                     }
462                     LDNS_STATUS_CHECK_GOTO(status, status_error);
463 
464                     if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_OPT) {
465                               ldns_pkt_set_edns_udp_size(packet, ldns_rr_get_class(rr));
466                               ldns_write_uint32(data, ldns_rr_ttl(rr));
467                               ldns_pkt_set_edns_extended_rcode(packet, data[0]);
468                               ldns_pkt_set_edns_version(packet, data[1]);
469                               ldns_pkt_set_edns_z(packet, ldns_read_uint16(&data[2]));
470                               /* edns might not have rdfs */
471                               if (ldns_rr_rdf(rr, 0)) {
472                                         ldns_rdf_deep_free(ldns_pkt_edns_data(packet));
473                                         ldns_pkt_set_edns_data(packet, ldns_rdf_clone(ldns_rr_rdf(rr, 0)));
474                               }
475                               ldns_rr_free(rr);
476                               have_edns += 1;
477                     } else if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_TSIG) {
478                               ldns_pkt_set_tsig(packet, rr);
479                               ldns_pkt_set_arcount(packet, ldns_pkt_arcount(packet) - 1);
480                     } else if (!ldns_rr_list_push_rr(ldns_pkt_additional(packet), rr)) {
481                               ldns_pkt_free(packet);
482                               return LDNS_STATUS_INTERNAL_ERR;
483                     }
484           }
485           ldns_pkt_set_size(packet, max);
486           if(have_edns)
487                     ldns_pkt_set_arcount(packet, ldns_pkt_arcount(packet)
488                         - have_edns);
489         packet->_edns_present = have_edns;
490 
491           *packet_p = packet;
492           return status;
493 
494 status_error:
495           ldns_pkt_free(packet);
496           return status;
497 }
498