4149d102f2ac9ac6930c941c479e4ef0ccc47b40
[oweals/gnunet.git] / src / dns / dnsparser.c
1 /*
2       This file is part of GNUnet
3       (C) 2010, 2011, 2012 Christian Grothoff (and other contributing authors)
4
5       GNUnet is free software; you can redistribute it and/or modify
6       it under the terms of the GNU General Public License as published
7       by the Free Software Foundation; either version 2, or (at your
8       option) any later version.
9
10       GNUnet is distributed in the hope that it will be useful, but
11       WITHOUT ANY WARRANTY; without even the implied warranty of
12       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13       General Public License for more details.
14
15       You should have received a copy of the GNU General Public License
16       along with GNUnet; see the file COPYING.  If not, write to the
17       Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18       Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * @file dns/dnsparser.c
23  * @brief helper library to parse DNS packets. 
24  * @author Philipp Toelke
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_dnsparser_lib.h"
30
31
32 // DNS-Stuff
33 GNUNET_NETWORK_STRUCT_BEGIN
34 struct dns_header
35 {
36   uint16_t id GNUNET_PACKED;
37   struct GNUNET_DNSPARSER_Flags flags; 
38   uint16_t query_count GNUNET_PACKED;       // number of questions
39   uint16_t answer_rcount GNUNET_PACKED;       // number of answers
40   uint16_t authority_rcount GNUNET_PACKED;       // number of authority-records
41   uint16_t additional_rcount GNUNET_PACKED;       // number of additional records
42 };
43
44 struct query_line
45 {
46   uint16_t type GNUNET_PACKED;
47   uint16_t class GNUNET_PACKED;
48 };
49
50 struct record_line
51 {
52   uint16_t type GNUNET_PACKED;
53   uint16_t class GNUNET_PACKED;
54   uint32_t ttl GNUNET_PACKED;
55   uint16_t data_len GNUNET_PACKED;
56 };
57
58 struct soa_data
59 {
60   uint32_t serial GNUNET_PACKED;
61   uint32_t refresh GNUNET_PACKED;
62   uint32_t retry GNUNET_PACKED;
63   uint32_t expire GNUNET_PACKED;
64   uint32_t minimum GNUNET_PACKED;
65 };
66
67 GNUNET_NETWORK_STRUCT_END
68
69
70 /**
71  * Parse name inside of a DNS query or record.
72  *
73  * @param udp_payload entire UDP payload
74  * @param udp_payload_length length of udp_payload
75  * @param off pointer to the offset of the name to parse in the udp_payload (to be
76  *                    incremented by the size of the name)
77  * @param depth current depth of our recursion (to prevent stack overflow)
78  * @return name as 0-terminated C string on success, NULL if the payload is malformed
79  */
80 static char *
81 parse_name (const char *udp_payload,
82             size_t udp_payload_length,
83             size_t *off,
84             unsigned int depth)
85 {
86   const uint8_t *input = (const uint8_t *) udp_payload;
87   char *ret;
88   char *tmp;
89   char *xstr;
90   uint8_t len;
91   size_t xoff;
92   
93   ret = GNUNET_strdup ("");
94   while (1)
95   {
96     if (*off >= udp_payload_length)
97       goto error;
98     len = input[*off];
99     if (0 == len)
100     {
101       (*off)++;
102       break;
103     }
104     if (len < 64)
105     {
106       if (*off + 1 + len > udp_payload_length)
107         goto error;
108       GNUNET_asprintf (&tmp,
109                        "%s%.*s.",
110                        ret,
111                        (int) len,
112                        &udp_payload[*off + 1]);
113       GNUNET_free (ret);
114       ret = tmp;
115       *off += 1 + len;
116     }
117     else if ((64 | 128) == (len & (64 | 128)) )
118     {
119       if (depth > 32)
120         goto error; /* hard bound on stack to prevent "infinite" recursion, disallow! */
121       /* pointer to string */
122       if (*off + 1 > udp_payload_length)
123         goto error;
124       xoff = ((len - (64 | 128)) << 8) + input[*off+1];
125       xstr = parse_name (udp_payload,
126                          udp_payload_length,
127                          &xoff,
128                          depth + 1);
129       if (NULL == xstr)
130         goto error;
131       GNUNET_asprintf (&tmp,
132                        "%s%s.",
133                        ret,
134                        xstr);
135       GNUNET_free (ret);
136       GNUNET_free (xstr);
137       ret = tmp;
138       if (strlen (ret) > udp_payload_length)
139         goto error; /* we are looping (building an infinite string) */
140       *off += 2;
141       /* pointers always terminate names */
142       break;
143     } 
144     else
145     {
146       /* neither pointer nor inline string, not supported... */
147       goto error;
148     }
149   }
150   if (0 < strlen(ret))
151     ret[strlen(ret)-1] = '\0'; /* eat tailing '.' */
152   return ret;
153  error:  
154   GNUNET_free (ret);
155   return NULL;
156 }
157
158
159 /**
160  * Parse a DNS query entry.
161  *
162  * @param udp_payload entire UDP payload
163  * @param udp_payload_length length of udp_payload
164  * @param off pointer to the offset of the query to parse in the udp_payload (to be
165  *                    incremented by the size of the query)
166  * @param q where to write the query information
167  * @return GNUNET_OK on success, GNUNET_SYSERR if the query is malformed
168  */
169 static int
170 parse_query (const char *udp_payload,
171              size_t udp_payload_length,
172              size_t *off,
173              struct GNUNET_DNSPARSER_Query *q)
174 {
175   char *name;
176   struct query_line ql;
177
178   name = parse_name (udp_payload, 
179                      udp_payload_length,
180                      off, 0);
181   if (NULL == name)
182     return GNUNET_SYSERR;
183   q->name = name;
184   if (*off + sizeof (struct query_line) > udp_payload_length)
185     return GNUNET_SYSERR;
186   memcpy (&ql, &udp_payload[*off], sizeof (ql));
187   *off += sizeof (ql);
188   q->type = ntohs (ql.type);
189   q->class = ntohs (ql.class);
190   return GNUNET_OK;
191 }
192
193
194 /**
195  * Parse a DNS record entry.
196  *
197  * @param udp_payload entire UDP payload
198  * @param udp_payload_length length of udp_payload
199  * @param off pointer to the offset of the record to parse in the udp_payload (to be
200  *                    incremented by the size of the record)
201  * @param r where to write the record information
202  * @return GNUNET_OK on success, GNUNET_SYSERR if the record is malformed
203  */
204 static int
205 parse_record (const char *udp_payload,
206               size_t udp_payload_length,
207               size_t *off,
208               struct GNUNET_DNSPARSER_Record *r)
209 {
210   char *name;
211   struct record_line rl;
212   size_t old_off;
213   struct soa_data soa;
214   uint16_t mxpref;
215   uint16_t data_len;
216
217   name = parse_name (udp_payload, 
218                      udp_payload_length,
219                      off, 0);
220   if (NULL == name)
221     return GNUNET_SYSERR;
222   r->name = name;
223   if (*off + sizeof (struct record_line) > udp_payload_length)
224     return GNUNET_SYSERR;
225   memcpy (&rl, &udp_payload[*off], sizeof (rl));
226   (*off) += sizeof (rl);
227   r->type = ntohs (rl.type);
228   r->class = ntohs (rl.class);
229   r->expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
230                                                                                         ntohl (rl.ttl)));
231   data_len = ntohs (rl.data_len);
232   if (*off + data_len > udp_payload_length)
233     return GNUNET_SYSERR;
234   switch (r->type)
235   {
236   case GNUNET_DNSPARSER_TYPE_NS:
237   case GNUNET_DNSPARSER_TYPE_CNAME:
238   case GNUNET_DNSPARSER_TYPE_PTR:
239     old_off = *off;
240     r->data.hostname = parse_name (udp_payload,
241                                    udp_payload_length,
242                                    off, 0);    
243     if ( (NULL == r->data.hostname) ||
244          (old_off + data_len != *off) )
245       return GNUNET_SYSERR;
246     return GNUNET_OK;
247   case GNUNET_DNSPARSER_TYPE_SOA:
248     old_off = *off;
249     r->data.soa = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_SoaRecord));
250     r->data.soa->mname = parse_name (udp_payload,
251                                      udp_payload_length,
252                                      off, 0);
253     r->data.soa->rname = parse_name (udp_payload,
254                                      udp_payload_length,
255                                      off, 0);
256     if ( (NULL == r->data.soa->mname) ||
257          (NULL == r->data.soa->rname) ||
258          (*off + sizeof (soa) > udp_payload_length) )
259       return GNUNET_SYSERR;
260     memcpy (&soa, &udp_payload[*off], sizeof (soa));
261     r->data.soa->serial = ntohl (soa.serial);
262     r->data.soa->refresh = ntohl (soa.refresh);
263     r->data.soa->retry = ntohl (soa.retry);
264     r->data.soa->expire = ntohl (soa.expire);
265     r->data.soa->minimum_ttl = ntohl (soa.minimum);
266     (*off) += sizeof (soa);
267     if (old_off + data_len != *off) 
268       return GNUNET_SYSERR;
269     return GNUNET_OK;
270   case GNUNET_DNSPARSER_TYPE_MX:
271     old_off = *off;
272     if (*off + sizeof (uint16_t) > udp_payload_length)
273       return GNUNET_SYSERR;
274     memcpy (&mxpref, &udp_payload[*off], sizeof (uint16_t));    
275     (*off) += sizeof (uint16_t);
276     r->data.mx = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_MxRecord));
277     r->data.mx->preference = ntohs (mxpref);
278     r->data.mx->mxhost = parse_name (udp_payload,
279                                      udp_payload_length,
280                                      off, 0);
281     if (old_off + data_len != *off) 
282       return GNUNET_SYSERR;
283     return GNUNET_OK;
284   default:
285     r->data.raw.data = GNUNET_malloc (data_len);
286     r->data.raw.data_len = data_len;
287     memcpy (r->data.raw.data, &udp_payload[*off], data_len);
288     break;
289   }
290   (*off) += data_len;
291   return GNUNET_OK;  
292 }
293
294
295 /**
296  * Parse a UDP payload of a DNS packet in to a nice struct for further
297  * processing and manipulation.
298  *
299  * @param udp_payload wire-format of the DNS packet
300  * @param udp_payload_length number of bytes in udp_payload 
301  * @return NULL on error, otherwise the parsed packet
302  */
303 struct GNUNET_DNSPARSER_Packet *
304 GNUNET_DNSPARSER_parse (const char *udp_payload,
305                         size_t udp_payload_length)
306 {
307   struct GNUNET_DNSPARSER_Packet *p;
308   const struct dns_header *dns;
309   size_t off;
310   unsigned int n;  
311   unsigned int i;
312
313   if (udp_payload_length < sizeof (struct dns_header))
314     return NULL;
315   dns = (const struct dns_header *) udp_payload;
316   off = sizeof (struct dns_header);
317   p = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_Packet));
318   p->flags = dns->flags;
319   p->id = dns->id;
320   n = ntohs (dns->query_count);
321   if (n > 0)
322   {
323     p->queries = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Query));
324     p->num_queries = n;
325     for (i=0;i<n;i++)
326       if (GNUNET_OK !=
327           parse_query (udp_payload,
328                        udp_payload_length,
329                        &off,
330                        &p->queries[i]))
331         goto error;
332   }
333   n = ntohs (dns->answer_rcount);
334   if (n > 0)
335   {
336     p->answers = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
337     p->num_answers = n;
338     for (i=0;i<n;i++)
339       if (GNUNET_OK !=
340           parse_record (udp_payload,
341                         udp_payload_length,
342                         &off,
343                         &p->answers[i]))
344         goto error;
345   }
346   n = ntohs (dns->authority_rcount);
347   if (n > 0)
348   {
349     p->authority_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
350     p->num_authority_records = n;
351     for (i=0;i<n;i++)
352       if (GNUNET_OK !=
353           parse_record (udp_payload,
354                         udp_payload_length,
355                         &off,
356                         &p->authority_records[i]))
357         goto error;  
358   }
359   n = ntohs (dns->additional_rcount);
360   if (n > 0)
361   {
362     p->additional_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
363     p->num_additional_records = n;
364     for (i=0;i<n;i++)
365       if (GNUNET_OK !=
366           parse_record (udp_payload,
367                         udp_payload_length,
368                         &off,
369                         &p->additional_records[i]))
370         goto error;   
371   }
372   return p;
373  error:
374   GNUNET_DNSPARSER_free_packet (p);
375   return NULL;
376 }
377
378
379 /**
380  * Free SOA information record.
381  *
382  * @param soa record to free
383  */
384 static void
385 free_soa (struct GNUNET_DNSPARSER_SoaRecord *soa)
386 {
387   if (NULL == soa)
388     return;
389   GNUNET_free_non_null (soa->mname);
390   GNUNET_free_non_null (soa->rname);
391   GNUNET_free (soa);      
392 }
393
394
395 /**
396  * Free MX information record.
397  *
398  * @param mx record to free
399  */
400 static void
401 free_mx (struct GNUNET_DNSPARSER_MxRecord *mx)
402 {
403   if (NULL == mx)
404     return;
405   GNUNET_free_non_null (mx->mxhost);
406   GNUNET_free (mx);      
407 }
408
409
410 static void
411 free_record (struct GNUNET_DNSPARSER_Record *r)
412 {
413   GNUNET_free_non_null (r->name);
414   switch (r->type)
415   {
416   case GNUNET_DNSPARSER_TYPE_MX:
417     free_mx (r->data.mx);
418     break;
419   case GNUNET_DNSPARSER_TYPE_SOA:
420     free_soa (r->data.soa);
421     break;
422   case GNUNET_DNSPARSER_TYPE_NS:
423   case GNUNET_DNSPARSER_TYPE_CNAME:
424   case GNUNET_DNSPARSER_TYPE_PTR:
425     GNUNET_free_non_null (r->data.hostname);
426     break;
427   default:
428     GNUNET_free_non_null (r->data.raw.data);
429     break;
430   }
431 }
432
433
434 /**
435  * Free memory taken by a packet.
436  *
437  * @param p packet to free
438  */
439 void
440 GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
441 {
442   unsigned int i;
443
444   for (i=0;i<p->num_queries;i++)
445     GNUNET_free_non_null (p->queries[i].name);
446   GNUNET_free_non_null (p->queries);
447   for (i=0;i<p->num_answers;i++)
448     free_record (&p->answers[i]);
449   GNUNET_free_non_null (p->answers);
450   for (i=0;i<p->num_authority_records;i++)
451     free_record (&p->authority_records[i]);
452   GNUNET_free_non_null (p->authority_records);
453   for (i=0;i<p->num_additional_records;i++)
454     free_record (&p->additional_records[i]);
455   GNUNET_free_non_null (p->additional_records);
456   GNUNET_free (p);
457 }
458
459
460 /* ********************** DNS packet assembly code **************** */
461
462
463 /**
464  * Add a DNS name to the UDP packet at the given location.
465  *
466  * @param dst where to write the name
467  * @param dst_len number of bytes in dst
468  * @param off pointer to offset where to write the name (increment by bytes used)
469  *            must not be changed if there is an error
470  * @param name name to write
471  * @return GNUNET_SYSERR if 'name' is invalid
472  *         GNUNET_NO if 'name' did not fit
473  *         GNUNET_OK if 'name' was added to 'dst'
474  */
475 static int
476 add_name (char *dst,
477           size_t dst_len,
478           size_t *off,
479           const char *name)
480 {
481   const char *dot;
482   size_t start;
483   size_t pos;
484   size_t len;
485
486   if (NULL == name)
487     return GNUNET_SYSERR;
488   start = *off;
489   if (start + strlen (name) + 2 > dst_len)
490     return GNUNET_NO;
491   pos = start;
492   do
493   {
494     dot = strchr (name, '.');
495     if (NULL == dot)
496       len = strlen (name);
497     else
498       len = dot - name;
499     if ( (len >= 64) || (len == 0) )
500       return GNUNET_NO; /* segment too long or empty */
501     dst[pos++] = (char) (uint8_t) len;
502     memcpy (&dst[pos], name, len);
503     pos += len;
504     name += len + 1; /* also skip dot */
505   }
506   while (NULL != dot);
507   dst[pos++] = '\0'; /* terminator */
508   *off = pos;
509   return GNUNET_OK;
510 }
511
512
513 /**
514  * Add a DNS query to the UDP packet at the given location.
515  *
516  * @param dst where to write the query
517  * @param dst_len number of bytes in dst
518  * @param off pointer to offset where to write the query (increment by bytes used)
519  *            must not be changed if there is an error
520  * @param query query to write
521  * @return GNUNET_SYSERR if 'query' is invalid
522  *         GNUNET_NO if 'query' did not fit
523  *         GNUNET_OK if 'query' was added to 'dst'
524  */
525 static int
526 add_query (char *dst,
527            size_t dst_len,
528            size_t *off,
529            const struct GNUNET_DNSPARSER_Query *query)
530 {
531   int ret;
532   struct query_line ql;
533
534   ret = add_name (dst, dst_len - sizeof (struct query_line), off, query->name);
535   if (ret != GNUNET_OK)
536     return ret;
537   ql.type = htons (query->type);
538   ql.class = htons (query->class);
539   memcpy (&dst[*off], &ql, sizeof (ql));
540   (*off) += sizeof (ql);
541   return GNUNET_OK;
542 }
543
544
545 /**
546  * Add an MX record to the UDP packet at the given location.
547  *
548  * @param dst where to write the mx record
549  * @param dst_len number of bytes in dst
550  * @param off pointer to offset where to write the mx information (increment by bytes used);
551  *            can also change if there was an error
552  * @param mx mx information to write
553  * @return GNUNET_SYSERR if 'mx' is invalid
554  *         GNUNET_NO if 'mx' did not fit
555  *         GNUNET_OK if 'mx' was added to 'dst'
556  */
557 static int
558 add_mx (char *dst,
559         size_t dst_len,
560         size_t *off,
561         const struct GNUNET_DNSPARSER_MxRecord *mx)
562 {
563   uint16_t mxpref;
564
565   if (*off + sizeof (uint16_t) > dst_len)
566     return GNUNET_NO;
567   mxpref = htons (mx->preference);
568   memcpy (&dst[*off], &mxpref, sizeof (mxpref));
569   (*off) += sizeof (mxpref);
570   return add_name (dst, dst_len, off, mx->mxhost);
571 }
572
573
574 /**
575  * Add an SOA record to the UDP packet at the given location.
576  *
577  * @param dst where to write the SOA record
578  * @param dst_len number of bytes in dst
579  * @param off pointer to offset where to write the SOA information (increment by bytes used)
580  *            can also change if there was an error
581  * @param soa SOA information to write
582  * @return GNUNET_SYSERR if 'soa' is invalid
583  *         GNUNET_NO if 'soa' did not fit
584  *         GNUNET_OK if 'soa' was added to 'dst'
585  */
586 static int
587 add_soa (char *dst,
588          size_t dst_len,
589          size_t *off,
590          const struct GNUNET_DNSPARSER_SoaRecord *soa)
591 {
592   struct soa_data sd;
593   int ret;
594
595   if ( (GNUNET_OK != (ret = add_name (dst,
596                                       dst_len,
597                                       off,
598                                       soa->mname))) ||
599        (GNUNET_OK != (ret = add_name (dst,
600                                       dst_len,
601                                       off,
602                                       soa->rname)) ) )
603     return ret;
604   if (*off + sizeof (soa) > dst_len)
605     return GNUNET_NO;
606   sd.serial = htonl (soa->serial);
607   sd.refresh = htonl (soa->refresh);
608   sd.retry = htonl (soa->retry);
609   sd.expire = htonl (soa->expire);
610   sd.minimum = htonl (soa->minimum_ttl);
611   memcpy (&dst[*off], &sd, sizeof (sd));
612   (*off) += sizeof (sd);
613   return GNUNET_OK;
614 }
615
616
617 /**
618  * Add a DNS record to the UDP packet at the given location.
619  *
620  * @param dst where to write the query
621  * @param dst_len number of bytes in dst
622  * @param off pointer to offset where to write the query (increment by bytes used)
623  *            must not be changed if there is an error
624  * @param record record to write
625  * @return GNUNET_SYSERR if 'record' is invalid
626  *         GNUNET_NO if 'record' did not fit
627  *         GNUNET_OK if 'record' was added to 'dst'
628  */
629 static int
630 add_record (char *dst,
631             size_t dst_len,
632             size_t *off,
633             const struct GNUNET_DNSPARSER_Record *record)
634 {
635   int ret;
636   size_t start;
637   size_t pos;
638   struct record_line rl;
639
640   start = *off;
641   ret = add_name (dst, dst_len - sizeof (struct record_line), off, record->name);
642   if (ret != GNUNET_OK)
643     return ret;
644   /* '*off' is now the position where we will need to write the record line */
645
646   pos = *off + sizeof (struct record_line);
647   switch (record->type)
648   { 
649   case GNUNET_DNSPARSER_TYPE_MX:
650     ret = add_mx (dst, dst_len, &pos, record->data.mx);    
651     break;
652   case GNUNET_DNSPARSER_TYPE_SOA:
653     ret = add_soa (dst, dst_len, &pos, record->data.soa);
654     break;
655   case GNUNET_DNSPARSER_TYPE_NS:
656   case GNUNET_DNSPARSER_TYPE_CNAME:
657   case GNUNET_DNSPARSER_TYPE_PTR:
658     ret = add_name (dst, dst_len, &pos, record->data.hostname);
659     break;
660   default:
661     if (pos + record->data.raw.data_len > dst_len)
662     {
663       ret = GNUNET_NO;
664       break;
665     }
666     memcpy (&dst[pos], record->data.raw.data, record->data.raw.data_len);
667     pos += record->data.raw.data_len;
668     ret = GNUNET_OK;
669     break;
670   }
671   if (ret != GNUNET_OK)
672   {
673     *off = start;
674     return GNUNET_NO;
675   }
676
677   if (pos - (*off + sizeof (struct record_line)) > UINT16_MAX)
678   {
679     /* record data too long */
680     *off = start;
681     return GNUNET_NO;
682   }
683   rl.type = htons (record->type);
684   rl.class = htons (record->class);
685   rl.ttl = htonl (GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value / 1000); /* in seconds */
686   rl.data_len = htons ((uint16_t) (pos - (*off + sizeof (struct record_line))));
687   memcpy (&dst[*off], &rl, sizeof (struct record_line));
688   *off = pos;
689   return GNUNET_OK;  
690 }
691
692
693 /**
694  * Given a DNS packet, generate the corresponding UDP payload.
695  * Note that we do not attempt to pack the strings with pointers
696  * as this would complicate the code and this is about being 
697  * simple and secure, not fast, fancy and broken like bind.
698  *
699  * @param p packet to pack
700  * @param max maximum allowed size for the resulting UDP payload
701  * @param buf set to a buffer with the packed message
702  * @param buf_length set to the length of buf
703  * @return GNUNET_SYSERR if 'p' is invalid
704  *         GNUNET_NO if 'p' was truncated (but there is still a result in 'buf')
705  *         GNUNET_OK if 'p' was packed completely into '*buf'
706  */
707 int
708 GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
709                        uint16_t max,
710                        char **buf,
711                        size_t *buf_length)
712 {  
713   struct dns_header dns;
714   size_t off;
715   char tmp[max];
716   unsigned int i;
717   int ret;
718   int trc;
719   
720   if ( (p->num_queries > UINT16_MAX) ||
721        (p->num_answers > UINT16_MAX) ||
722        (p->num_authority_records > UINT16_MAX) ||
723        (p->num_additional_records > UINT16_MAX) )
724     return GNUNET_SYSERR;
725   dns.id = p->id;
726   dns.flags = p->flags;
727   dns.query_count = htons (p->num_queries);
728   dns.answer_rcount = htons (p->num_answers);
729   dns.authority_rcount = htons (p->num_authority_records);
730   dns.additional_rcount = htons (p->num_additional_records);
731
732   off = sizeof (struct dns_header);
733   trc = GNUNET_NO;
734   for (i=0;i<p->num_queries;i++)
735   {
736     ret = add_query (tmp, sizeof (tmp), &off, &p->queries[i]);  
737     if (GNUNET_SYSERR == ret)
738       return GNUNET_SYSERR;
739     if (GNUNET_NO == ret)
740     {
741       dns.query_count = htons ((uint16_t) (i-1));
742       trc = GNUNET_YES;      
743       break;
744     }
745   }
746   for (i=0;i<p->num_answers;i++)
747   {
748     ret = add_record (tmp, sizeof (tmp), &off, &p->answers[i]);  
749     if (GNUNET_SYSERR == ret)
750       return GNUNET_SYSERR;
751     if (GNUNET_NO == ret)
752     {
753       dns.answer_rcount = htons ((uint16_t) (i-1));
754       trc = GNUNET_YES;      
755       break;
756     }
757   }
758   for (i=0;i<p->num_authority_records;i++)
759   {
760     ret = add_record (tmp, sizeof (tmp), &off, &p->authority_records[i]);  
761     if (GNUNET_SYSERR == ret)
762       return GNUNET_SYSERR;
763     if (GNUNET_NO == ret)
764     {
765       dns.authority_rcount = htons ((uint16_t) (i-1));
766       trc = GNUNET_YES;      
767       break;
768     }
769   }
770   for (i=0;i<p->num_additional_records;i++)
771   {
772     ret = add_record (tmp, sizeof (tmp), &off, &p->additional_records[i]);  
773     if (GNUNET_SYSERR == ret)
774       return GNUNET_SYSERR;
775     if (GNUNET_NO == ret)
776     {
777       dns.additional_rcount = htons (i-1);
778       trc = GNUNET_YES;      
779       break;
780     }
781   }
782
783   if (GNUNET_YES == trc)
784     dns.flags.message_truncated = 1;    
785   memcpy (tmp, &dns, sizeof (struct dns_header));
786
787   *buf = GNUNET_malloc (off);
788   *buf_length = off;
789   memcpy (*buf, tmp, off);
790   return trc;
791 }
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822 /* legacy code follows */
823
824 /**
825  * Parse a name from DNS to a normal .-delimited, 0-terminated string.
826  *
827  * @param d The destination of the name. Should have at least 255 bytes allocated.
828  * @param src The DNS-Packet
829  * @param idx The offset inside the Packet from which on the name should be read
830  * @returns The offset of the first unparsed byte (the byte right behind the name)
831  */
832 static unsigned int
833 parse_dns_name (char *d, const unsigned char *src, unsigned short idx)
834 {                               /*{{{ */
835   char *dest = d;
836
837   int len = src[idx++];
838
839   while (len != 0)
840   {
841     if (len & 0xC0)
842     {                           /* Compressed name, offset in this and the next octet */
843       unsigned short offset = ((len & 0x3F) << 8) | src[idx++];
844
845       parse_dns_name (dest, src, offset - 12);  /* 12 for the Header of the DNS-Packet, idx starts at 0 which is 12 bytes from the start of the packet */
846       return idx;
847     }
848     memcpy (dest, src + idx, len);
849     idx += len;
850     dest += len;
851     *dest = '.';
852     dest++;
853     len = src[idx++];
854   };
855   *dest = 0;
856
857   return idx;
858 }
859
860 /*}}}*/
861
862 /**
863  * Parse a complete DNS-Record from raw DNS-data to a struct dns_record
864  *
865  * @param data The DNS-data
866  * @param dst Pointer to count pointers; individual pointers will be allocated
867  * @param count Number of records to parse
868  * @param idx The offset inside the Packet from which on the name should be read
869  * @returns The offset of the first unparsed byte (the byte right behind the last record)
870  */
871 static unsigned short
872 parse_dns_record (unsigned char *data,  /*{{{ */
873                   struct dns_record **dst, unsigned short count,
874                   unsigned short idx)
875 {
876   int i;
877   unsigned short _idx;
878
879   for (i = 0; i < count; i++)
880   {
881     dst[i] = GNUNET_malloc (sizeof (struct dns_record));
882     dst[i]->name = alloca (255);        // see RFC1035, no name can be longer than this.
883     char *name = dst[i]->name;
884
885     _idx = parse_dns_name (name, data, idx);
886     dst[i]->namelen = _idx - idx;
887
888     dst[i]->name = GNUNET_malloc (dst[i]->namelen);
889     memcpy (dst[i]->name, name, dst[i]->namelen);
890
891     idx = _idx;
892
893     dst[i]->type = *((unsigned short *) (data + idx));
894     idx += 2;
895     dst[i]->class = *((unsigned short *) (data + idx));
896     idx += 2;
897     dst[i]->ttl = *((unsigned int *) (data + idx));
898     idx += 4;
899     dst[i]->data_len = *((unsigned short *) (data + idx));
900     idx += 2;
901     dst[i]->data = GNUNET_malloc (ntohs (dst[i]->data_len));
902     memcpy (dst[i]->data, data + idx, ntohs (dst[i]->data_len));
903     idx += ntohs (dst[i]->data_len);
904   }
905   return idx;
906 }                               /*}}} */
907
908 /**
909  * Parse a raw DNS-Packet into an usable struct
910  */
911 struct dns_pkt_parsed *
912 parse_dns_packet (struct dns_pkt *pkt)
913 {                               /*{{{ */
914   struct dns_pkt_parsed *ppkt = GNUNET_malloc (sizeof (struct dns_pkt_parsed));
915
916   memcpy (&ppkt->s, &pkt->s, sizeof pkt->s);
917
918   unsigned short qdcount = ntohs (ppkt->s.qdcount);
919   unsigned short ancount = ntohs (ppkt->s.ancount);
920   unsigned short nscount = ntohs (ppkt->s.nscount);
921   unsigned short arcount = ntohs (ppkt->s.arcount);
922
923   ppkt->queries = GNUNET_malloc (qdcount * sizeof (struct dns_query *));
924   ppkt->answers = GNUNET_malloc (ancount * sizeof (struct dns_record *));
925   ppkt->nameservers = GNUNET_malloc (nscount * sizeof (struct dns_record *));
926   ppkt->additional = GNUNET_malloc (arcount * sizeof (struct dns_record *));
927
928   unsigned short idx = 0, _idx; /* This keeps track how far we have parsed the data */
929
930   /* Parse the Query */
931   int i;
932
933   for (i = 0; i < qdcount; i++)
934   {                             /*{{{ */
935     ppkt->queries[i] = GNUNET_malloc (sizeof (struct dns_query));
936     char *name = alloca (255);  /* see RFC1035, it can't be more than this. */
937
938     _idx = parse_dns_name (name, pkt->data, idx);
939     ppkt->queries[i]->namelen = _idx - idx;
940     idx = _idx;
941
942     ppkt->queries[i]->name = GNUNET_malloc (ppkt->queries[i]->namelen);
943     memcpy (ppkt->queries[i]->name, name, ppkt->queries[i]->namelen);
944
945     ppkt->queries[i]->qtype = *((unsigned short *) (pkt->data + idx));
946     idx += 2;
947     ppkt->queries[i]->qclass = *((unsigned short *) (pkt->data + idx));
948     idx += 2;
949   }
950   /*}}} */
951   idx = parse_dns_record (pkt->data, ppkt->answers, ancount, idx);
952   idx = parse_dns_record (pkt->data, ppkt->nameservers, nscount, idx);
953   idx = parse_dns_record (pkt->data, ppkt->additional, arcount, idx);
954   return ppkt;
955 }                               /*}}} */
956
957 static void
958 unparse_dns_name (char *dest, char *src, size_t len)
959 {
960   char *b = dest;
961   char cnt = 0;
962
963   dest++;
964   while (*src != 0)
965   {
966     while (*src != '.' && *src != 0)
967     {
968       *dest = *src;
969       src++;
970       dest++;
971       cnt++;
972     }
973     *b = cnt;
974     cnt = 0;
975     b = dest;
976     dest++;
977     src++;
978   }
979   *b = 0;
980 }
981
982 struct dns_pkt *
983 unparse_dns_packet (struct dns_pkt_parsed *ppkt)
984 {
985   size_t size = sizeof (struct dns_pkt) - 1;
986   int i;
987
988   for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
989     size += ppkt->queries[i]->namelen + 1;
990
991   for (i = 0; i < ntohs (ppkt->s.ancount); i++)
992   {
993     size += ppkt->answers[i]->namelen + 1;
994     size += ppkt->answers[i]->data_len;
995   }
996   for (i = 0; i < ntohs (ppkt->s.nscount); i++)
997   {
998     size += ppkt->nameservers[i]->namelen + 1;
999     size += ppkt->nameservers[i]->data_len;
1000   }
1001   for (i = 0; i < ntohs (ppkt->s.arcount); i++)
1002   {
1003     size += ppkt->additional[i]->namelen + 1;
1004     size += ppkt->additional[i]->data_len;
1005   }
1006
1007   size +=
1008       4 * ntohs (ppkt->s.qdcount) + 10 * (ntohs (ppkt->s.ancount) +
1009                                           ntohs (ppkt->s.arcount) +
1010                                           ntohs (ppkt->s.nscount));
1011
1012   struct dns_pkt *pkt = GNUNET_malloc (size);
1013   char *pkt_c = (char *) pkt;
1014
1015   memcpy (&pkt->s, &ppkt->s, sizeof ppkt->s);
1016   size_t idx = sizeof ppkt->s;
1017
1018   for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
1019   {
1020     unparse_dns_name (&pkt_c[idx], ppkt->queries[i]->name,
1021                       ppkt->queries[i]->namelen);
1022     idx += ppkt->queries[i]->namelen;
1023     struct dns_query_line *d = (struct dns_query_line *) &pkt_c[idx];
1024
1025     d->class = ppkt->queries[i]->qclass;
1026     d->type = ppkt->queries[i]->qtype;
1027     idx += sizeof (struct dns_query_line);
1028   }
1029
1030   for (i = 0; i < ntohs (ppkt->s.ancount); i++)
1031   {
1032     unparse_dns_name (&pkt_c[idx], ppkt->answers[i]->name,
1033                       ppkt->answers[i]->namelen);
1034     idx += ppkt->answers[i]->namelen;
1035     struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
1036
1037     r->type = ppkt->answers[i]->type;
1038     r->class = ppkt->answers[i]->class;
1039     r->ttl = ppkt->answers[i]->ttl;
1040     r->data_len = ppkt->answers[i]->data_len;
1041     idx += sizeof (struct dns_record_line);
1042     memcpy (&r->data, ppkt->answers[i]->data, ppkt->answers[i]->data_len);
1043     idx += ppkt->answers[i]->data_len;
1044   }
1045
1046   for (i = 0; i < ntohs (ppkt->s.nscount); i++)
1047   {
1048     unparse_dns_name (&pkt_c[idx], ppkt->nameservers[i]->name,
1049                       ppkt->nameservers[i]->namelen);
1050     idx += ppkt->nameservers[i]->namelen;
1051     struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
1052
1053     r->type = ppkt->nameservers[i]->type;
1054     r->class = ppkt->nameservers[i]->class;
1055     r->ttl = ppkt->nameservers[i]->ttl;
1056     r->data_len = ppkt->nameservers[i]->data_len;
1057     idx += sizeof (struct dns_record_line);
1058     memcpy (&r->data, ppkt->nameservers[i]->data,
1059             ppkt->nameservers[i]->data_len);
1060     idx += ppkt->nameservers[i]->data_len;
1061   }
1062
1063   for (i = 0; i < ntohs (ppkt->s.arcount); i++)
1064   {
1065     unparse_dns_name (&pkt_c[idx], ppkt->additional[i]->name,
1066                       ppkt->additional[i]->namelen);
1067     idx += ppkt->additional[i]->namelen;
1068     struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
1069
1070     r->type = ppkt->additional[i]->type;
1071     r->class = ppkt->additional[i]->class;
1072     r->ttl = ppkt->additional[i]->ttl;
1073     r->data_len = ppkt->additional[i]->data_len;
1074     idx += sizeof (struct dns_record_line);
1075     memcpy (&r->data, ppkt->additional[i]->data, ppkt->additional[i]->data_len);
1076     idx += ppkt->additional[i]->data_len;
1077   }
1078
1079   return pkt;
1080 }
1081
1082 void
1083 free_parsed_dns_packet (struct dns_pkt_parsed *ppkt)
1084 {
1085   unsigned short qdcount = ntohs (ppkt->s.qdcount);
1086   unsigned short ancount = ntohs (ppkt->s.ancount);
1087   unsigned short nscount = ntohs (ppkt->s.nscount);
1088   unsigned short arcount = ntohs (ppkt->s.arcount);
1089
1090   int i;
1091
1092   for (i = 0; i < qdcount; i++)
1093   {
1094     GNUNET_free (ppkt->queries[i]->name);
1095     GNUNET_free (ppkt->queries[i]);
1096   }
1097   GNUNET_free (ppkt->queries);
1098   for (i = 0; i < ancount; i++)
1099   {
1100     GNUNET_free (ppkt->answers[i]->name);
1101     GNUNET_free (ppkt->answers[i]->data);
1102     GNUNET_free (ppkt->answers[i]);
1103   }
1104   GNUNET_free (ppkt->answers);
1105   for (i = 0; i < nscount; i++)
1106   {
1107     GNUNET_free (ppkt->nameservers[i]->name);
1108     GNUNET_free (ppkt->nameservers[i]->data);
1109     GNUNET_free (ppkt->nameservers[i]);
1110   }
1111   GNUNET_free (ppkt->nameservers);
1112   for (i = 0; i < arcount; i++)
1113   {
1114     GNUNET_free (ppkt->additional[i]->name);
1115     GNUNET_free (ppkt->additional[i]->data);
1116     GNUNET_free (ppkt->additional[i]);
1117   }
1118   GNUNET_free (ppkt->additional);
1119   GNUNET_free (ppkt);
1120 }