wire2host.c
Go to the documentation of this file.
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! */
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) {
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) {
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) {
91  } else if (pointer_target >= max) {
93  } else if (pointer_count > LDNS_MAX_POINTERS) {
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) {
103  }
104  if (*pos + 1 + label_size > max) {
106  }
107 
108  /* check space for labelcount itself */
109  if (dname_pos + 1 > LDNS_MAX_DOMAINLEN) {
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) {
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) {
138  }
139 
140  tmp_dname[dname_pos] = 0;
141  dname_pos++;
142 
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 
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) {
170  }
171 
172  rd_length = ldns_read_uint16(&wire[*pos]);
173  *pos = *pos + 2;
174 
175  if (*pos + rd_length > max) {
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:
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:
210  cur_rdf_length = LDNS_RDF_SIZE_DOUBLEWORD;
211  break;
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:
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;
227  cur_rdf_length = (size_t) ldns_read_uint16(&wire[*pos]) + 2;
228  break;
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:
240  case LDNS_RDF_TYPE_LOC:
241  case LDNS_RDF_TYPE_WKS:
242  case LDNS_RDF_TYPE_NSAP:
243  case LDNS_RDF_TYPE_ATMA:
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) {
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 */
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) {
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) {
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) {
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 
362 {
363  /* lazy */
364  return ldns_wire2pkt(packet, ldns_buffer_begin(buffer),
365  ldns_buffer_limit(buffer));
366 
367 }
368 
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) {
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);
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) {
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);
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) {
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);
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) {
422  }
423  LDNS_STATUS_CHECK_GOTO(status, status_error);
424 
425  if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_OPT) {
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)) {
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);
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 }