-stuff
[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   if (GNUNET_YES == trc)
791     return GNUNET_NO;
792   return GNUNET_OK;
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
823
824 /* legacy code follows */
825
826 /**
827  * Parse a name from DNS to a normal .-delimited, 0-terminated string.
828  *
829  * @param d The destination of the name. Should have at least 255 bytes allocated.
830  * @param src The DNS-Packet
831  * @param idx The offset inside the Packet from which on the name should be read
832  * @returns The offset of the first unparsed byte (the byte right behind the name)
833  */
834 static unsigned int
835 parse_dns_name (char *d, const unsigned char *src, unsigned short idx)
836 {                               /*{{{ */
837   char *dest = d;
838
839   int len = src[idx++];
840
841   while (len != 0)
842   {
843     if (len & 0xC0)
844     {                           /* Compressed name, offset in this and the next octet */
845       unsigned short offset = ((len & 0x3F) << 8) | src[idx++];
846
847       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 */
848       return idx;
849     }
850     memcpy (dest, src + idx, len);
851     idx += len;
852     dest += len;
853     *dest = '.';
854     dest++;
855     len = src[idx++];
856   };
857   *dest = 0;
858
859   return idx;
860 }
861
862 /*}}}*/
863
864 /**
865  * Parse a complete DNS-Record from raw DNS-data to a struct dns_record
866  *
867  * @param data The DNS-data
868  * @param dst Pointer to count pointers; individual pointers will be allocated
869  * @param count Number of records to parse
870  * @param idx The offset inside the Packet from which on the name should be read
871  * @returns The offset of the first unparsed byte (the byte right behind the last record)
872  */
873 static unsigned short
874 parse_dns_record (unsigned char *data,  /*{{{ */
875                   struct dns_record **dst, unsigned short count,
876                   unsigned short idx)
877 {
878   int i;
879   unsigned short _idx;
880
881   for (i = 0; i < count; i++)
882   {
883     dst[i] = GNUNET_malloc (sizeof (struct dns_record));
884     dst[i]->name = alloca (255);        // see RFC1035, no name can be longer than this.
885     char *name = dst[i]->name;
886
887     _idx = parse_dns_name (name, data, idx);
888     dst[i]->namelen = _idx - idx;
889
890     dst[i]->name = GNUNET_malloc (dst[i]->namelen);
891     memcpy (dst[i]->name, name, dst[i]->namelen);
892
893     idx = _idx;
894
895     dst[i]->type = *((unsigned short *) (data + idx));
896     idx += 2;
897     dst[i]->class = *((unsigned short *) (data + idx));
898     idx += 2;
899     dst[i]->ttl = *((unsigned int *) (data + idx));
900     idx += 4;
901     dst[i]->data_len = *((unsigned short *) (data + idx));
902     idx += 2;
903     dst[i]->data = GNUNET_malloc (ntohs (dst[i]->data_len));
904     memcpy (dst[i]->data, data + idx, ntohs (dst[i]->data_len));
905     idx += ntohs (dst[i]->data_len);
906   }
907   return idx;
908 }                               /*}}} */
909
910 /**
911  * Parse a raw DNS-Packet into an usable struct
912  */
913 struct dns_pkt_parsed *
914 parse_dns_packet (struct dns_pkt *pkt)
915 {                               /*{{{ */
916   struct dns_pkt_parsed *ppkt = GNUNET_malloc (sizeof (struct dns_pkt_parsed));
917
918   memcpy (&ppkt->s, &pkt->s, sizeof pkt->s);
919
920   unsigned short qdcount = ntohs (ppkt->s.qdcount);
921   unsigned short ancount = ntohs (ppkt->s.ancount);
922   unsigned short nscount = ntohs (ppkt->s.nscount);
923   unsigned short arcount = ntohs (ppkt->s.arcount);
924
925   ppkt->queries = GNUNET_malloc (qdcount * sizeof (struct dns_query *));
926   ppkt->answers = GNUNET_malloc (ancount * sizeof (struct dns_record *));
927   ppkt->nameservers = GNUNET_malloc (nscount * sizeof (struct dns_record *));
928   ppkt->additional = GNUNET_malloc (arcount * sizeof (struct dns_record *));
929
930   unsigned short idx = 0, _idx; /* This keeps track how far we have parsed the data */
931
932   /* Parse the Query */
933   int i;
934
935   for (i = 0; i < qdcount; i++)
936   {                             /*{{{ */
937     ppkt->queries[i] = GNUNET_malloc (sizeof (struct dns_query));
938     char *name = alloca (255);  /* see RFC1035, it can't be more than this. */
939
940     _idx = parse_dns_name (name, pkt->data, idx);
941     ppkt->queries[i]->namelen = _idx - idx;
942     idx = _idx;
943
944     ppkt->queries[i]->name = GNUNET_malloc (ppkt->queries[i]->namelen);
945     memcpy (ppkt->queries[i]->name, name, ppkt->queries[i]->namelen);
946
947     ppkt->queries[i]->qtype = *((unsigned short *) (pkt->data + idx));
948     idx += 2;
949     ppkt->queries[i]->qclass = *((unsigned short *) (pkt->data + idx));
950     idx += 2;
951   }
952   /*}}} */
953   idx = parse_dns_record (pkt->data, ppkt->answers, ancount, idx);
954   idx = parse_dns_record (pkt->data, ppkt->nameservers, nscount, idx);
955   idx = parse_dns_record (pkt->data, ppkt->additional, arcount, idx);
956   return ppkt;
957 }                               /*}}} */
958
959 static void
960 unparse_dns_name (char *dest, char *src, size_t len)
961 {
962   char *b = dest;
963   char cnt = 0;
964
965   dest++;
966   while (*src != 0)
967   {
968     while (*src != '.' && *src != 0)
969     {
970       *dest = *src;
971       src++;
972       dest++;
973       cnt++;
974     }
975     *b = cnt;
976     cnt = 0;
977     b = dest;
978     dest++;
979     src++;
980   }
981   *b = 0;
982 }
983
984 struct dns_pkt *
985 unparse_dns_packet (struct dns_pkt_parsed *ppkt)
986 {
987   size_t size = sizeof (struct dns_pkt) - 1;
988   int i;
989
990   for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
991     size += ppkt->queries[i]->namelen + 1;
992
993   for (i = 0; i < ntohs (ppkt->s.ancount); i++)
994   {
995     size += ppkt->answers[i]->namelen + 1;
996     size += ppkt->answers[i]->data_len;
997   }
998   for (i = 0; i < ntohs (ppkt->s.nscount); i++)
999   {
1000     size += ppkt->nameservers[i]->namelen + 1;
1001     size += ppkt->nameservers[i]->data_len;
1002   }
1003   for (i = 0; i < ntohs (ppkt->s.arcount); i++)
1004   {
1005     size += ppkt->additional[i]->namelen + 1;
1006     size += ppkt->additional[i]->data_len;
1007   }
1008
1009   size +=
1010       4 * ntohs (ppkt->s.qdcount) + 10 * (ntohs (ppkt->s.ancount) +
1011                                           ntohs (ppkt->s.arcount) +
1012                                           ntohs (ppkt->s.nscount));
1013
1014   struct dns_pkt *pkt = GNUNET_malloc (size);
1015   char *pkt_c = (char *) pkt;
1016
1017   memcpy (&pkt->s, &ppkt->s, sizeof ppkt->s);
1018   size_t idx = sizeof ppkt->s;
1019
1020   for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
1021   {
1022     unparse_dns_name (&pkt_c[idx], ppkt->queries[i]->name,
1023                       ppkt->queries[i]->namelen);
1024     idx += ppkt->queries[i]->namelen;
1025     struct dns_query_line *d = (struct dns_query_line *) &pkt_c[idx];
1026
1027     d->class = ppkt->queries[i]->qclass;
1028     d->type = ppkt->queries[i]->qtype;
1029     idx += sizeof (struct dns_query_line);
1030   }
1031
1032   for (i = 0; i < ntohs (ppkt->s.ancount); i++)
1033   {
1034     unparse_dns_name (&pkt_c[idx], ppkt->answers[i]->name,
1035                       ppkt->answers[i]->namelen);
1036     idx += ppkt->answers[i]->namelen;
1037     struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
1038
1039     r->type = ppkt->answers[i]->type;
1040     r->class = ppkt->answers[i]->class;
1041     r->ttl = ppkt->answers[i]->ttl;
1042     r->data_len = ppkt->answers[i]->data_len;
1043     idx += sizeof (struct dns_record_line);
1044     memcpy (&r->data, ppkt->answers[i]->data, ppkt->answers[i]->data_len);
1045     idx += ppkt->answers[i]->data_len;
1046   }
1047
1048   for (i = 0; i < ntohs (ppkt->s.nscount); i++)
1049   {
1050     unparse_dns_name (&pkt_c[idx], ppkt->nameservers[i]->name,
1051                       ppkt->nameservers[i]->namelen);
1052     idx += ppkt->nameservers[i]->namelen;
1053     struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
1054
1055     r->type = ppkt->nameservers[i]->type;
1056     r->class = ppkt->nameservers[i]->class;
1057     r->ttl = ppkt->nameservers[i]->ttl;
1058     r->data_len = ppkt->nameservers[i]->data_len;
1059     idx += sizeof (struct dns_record_line);
1060     memcpy (&r->data, ppkt->nameservers[i]->data,
1061             ppkt->nameservers[i]->data_len);
1062     idx += ppkt->nameservers[i]->data_len;
1063   }
1064
1065   for (i = 0; i < ntohs (ppkt->s.arcount); i++)
1066   {
1067     unparse_dns_name (&pkt_c[idx], ppkt->additional[i]->name,
1068                       ppkt->additional[i]->namelen);
1069     idx += ppkt->additional[i]->namelen;
1070     struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
1071
1072     r->type = ppkt->additional[i]->type;
1073     r->class = ppkt->additional[i]->class;
1074     r->ttl = ppkt->additional[i]->ttl;
1075     r->data_len = ppkt->additional[i]->data_len;
1076     idx += sizeof (struct dns_record_line);
1077     memcpy (&r->data, ppkt->additional[i]->data, ppkt->additional[i]->data_len);
1078     idx += ppkt->additional[i]->data_len;
1079   }
1080
1081   return pkt;
1082 }
1083
1084 void
1085 free_parsed_dns_packet (struct dns_pkt_parsed *ppkt)
1086 {
1087   unsigned short qdcount = ntohs (ppkt->s.qdcount);
1088   unsigned short ancount = ntohs (ppkt->s.ancount);
1089   unsigned short nscount = ntohs (ppkt->s.nscount);
1090   unsigned short arcount = ntohs (ppkt->s.arcount);
1091
1092   int i;
1093
1094   for (i = 0; i < qdcount; i++)
1095   {
1096     GNUNET_free (ppkt->queries[i]->name);
1097     GNUNET_free (ppkt->queries[i]);
1098   }
1099   GNUNET_free (ppkt->queries);
1100   for (i = 0; i < ancount; i++)
1101   {
1102     GNUNET_free (ppkt->answers[i]->name);
1103     GNUNET_free (ppkt->answers[i]->data);
1104     GNUNET_free (ppkt->answers[i]);
1105   }
1106   GNUNET_free (ppkt->answers);
1107   for (i = 0; i < nscount; i++)
1108   {
1109     GNUNET_free (ppkt->nameservers[i]->name);
1110     GNUNET_free (ppkt->nameservers[i]->data);
1111     GNUNET_free (ppkt->nameservers[i]);
1112   }
1113   GNUNET_free (ppkt->nameservers);
1114   for (i = 0; i < arcount; i++)
1115   {
1116     GNUNET_free (ppkt->additional[i]->name);
1117     GNUNET_free (ppkt->additional[i]->data);
1118     GNUNET_free (ppkt->additional[i]);
1119   }
1120   GNUNET_free (ppkt->additional);
1121   GNUNET_free (ppkt);
1122 }