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