10a9d6d9a63b75279b3eacbb9a791b4baef73222
[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  * @param name name to write
470  * @return GNUNET_SYSERR if 'name' is invalid
471  *         GNUNET_NO if 'name' did not fit
472  *         GNUNET_OK if 'name' was added to 'dst'
473  */
474 static int
475 add_name (char *dst,
476           size_t dst_len,
477           size_t *off,
478           const char *name)
479 {
480   const char *dot;
481   size_t start;
482   size_t pos;
483   size_t len;
484
485   if (NULL == name)
486     return GNUNET_SYSERR;
487   start = *off;
488   if (start + strlen (name) + 2 > dst_len)
489     return GNUNET_NO;
490   pos = start;
491   do
492   {
493     dot = strchr (name, '.');
494     if (NULL == dot)
495       len = strlen (name);
496     else
497       len = dot - name;
498     if ( (len >= 64) || (len == 0) )
499       return GNUNET_NO; /* segment too long or empty */
500     dst[pos++] = (char) (uint8_t) len;
501     memcpy (&dst[pos], name, len);
502     pos += len;
503     name += len + 1; /* also skip dot */
504   }
505   while (NULL != dot);
506   dst[pos++] = '\0'; /* terminator */
507   *off = pos;
508   return GNUNET_OK;
509 }
510
511
512 /**
513  * Add a DNS query to the UDP packet at the given location.
514  *
515  * @param dst where to write the query
516  * @param dst_len number of bytes in dst
517  * @param off pointer to offset where to write the query (increment by bytes used)
518  * @param query query to write
519  * @return GNUNET_SYSERR if 'query' is invalid
520  *         GNUNET_NO if 'query' did not fit
521  *         GNUNET_OK if 'query' was added to 'dst'
522  */
523 static int
524 add_query (char *dst,
525            size_t dst_len,
526            size_t *off,
527            const struct GNUNET_DNSPARSER_Query *query)
528 {
529   int ret;
530   struct query_line ql;
531
532   ret = add_name (dst, dst_len - sizeof (struct query_line), off, query->name);
533   if (ret != GNUNET_OK)
534     return ret;
535   ql.type = htons (query->type);
536   ql.class = htons (query->class);
537   memcpy (&dst[*off], &ql, sizeof (ql));
538   (*off) += sizeof (ql);
539   return GNUNET_OK;
540 }
541
542
543 /**
544  * Add an MX record to the UDP packet at the given location.
545  *
546  * @param dst where to write the mx record
547  * @param dst_len number of bytes in dst
548  * @param off pointer to offset where to write the mx information (increment by bytes used)
549  * @param mx mx information to write
550  * @return GNUNET_SYSERR if 'mx' is invalid
551  *         GNUNET_NO if 'mx' did not fit
552  *         GNUNET_OK if 'mx' was added to 'dst'
553  */
554 static int
555 add_mx (char *dst,
556         size_t dst_len,
557         size_t *off,
558         const struct GNUNET_DNSPARSER_MxRecord *mx)
559 {
560   return GNUNET_SYSERR; // not implemented
561 }
562
563
564 /**
565  * Add an SOA record to the UDP packet at the given location.
566  *
567  * @param dst where to write the SOA record
568  * @param dst_len number of bytes in dst
569  * @param off pointer to offset where to write the SOA information (increment by bytes used)
570  * @param soa SOA information to write
571  * @return GNUNET_SYSERR if 'soa' is invalid
572  *         GNUNET_NO if 'soa' did not fit
573  *         GNUNET_OK if 'soa' was added to 'dst'
574  */
575 static int
576 add_soa (char *dst,
577          size_t dst_len,
578          size_t *off,
579          const struct GNUNET_DNSPARSER_SoaRecord *soa)
580 {
581   return GNUNET_SYSERR; // not implemented
582 }
583
584
585 /**
586  * Add a DNS record to the UDP packet at the given location.
587  *
588  * @param dst where to write the query
589  * @param dst_len number of bytes in dst
590  * @param off pointer to offset where to write the query (increment by bytes used)
591  * @param record record to write
592  * @return GNUNET_SYSERR if 'record' is invalid
593  *         GNUNET_NO if 'record' did not fit
594  *         GNUNET_OK if 'record' was added to 'dst'
595  */
596 static int
597 add_record (char *dst,
598             size_t dst_len,
599             size_t *off,
600             const struct GNUNET_DNSPARSER_Record *record)
601 {
602   int ret;
603   size_t start;
604   size_t pos;
605   struct record_line rl;
606
607   start = *off;
608   ret = add_name (dst, dst_len - sizeof (struct record_line), off, record->name);
609   if (ret != GNUNET_OK)
610     return ret;
611   /* '*off' is now the position where we will need to write the record line */
612
613   pos = *off + sizeof (struct record_line);
614   switch (record->type)
615   { 
616   case GNUNET_DNSPARSER_TYPE_MX:
617     ret = add_mx (dst, dst_len, &pos, record->data.mx);    
618     break;
619   case GNUNET_DNSPARSER_TYPE_SOA:
620     ret = add_soa (dst, dst_len, &pos, record->data.soa);
621     break;
622   case GNUNET_DNSPARSER_TYPE_NS:
623   case GNUNET_DNSPARSER_TYPE_CNAME:
624   case GNUNET_DNSPARSER_TYPE_PTR:
625     ret = add_name (dst, dst_len, &pos, record->data.hostname);
626     break;
627   default:
628     if (pos + record->data.raw.data_len > dst_len)
629     {
630       ret = GNUNET_NO;
631       break;
632     }
633     memcpy (&dst[pos], record->data.raw.data, record->data.raw.data_len);
634     pos += record->data.raw.data_len;
635     break;
636   }
637   if (pos - (*off + sizeof (struct record_line)) > UINT16_MAX)
638   {
639     /* record data too long */
640     *off = start;
641     return GNUNET_NO;
642   }
643   rl.type = htons (record->type);
644   rl.class = htons (record->class);
645   rl.ttl = htonl (GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value / 1000); /* in seconds */
646   rl.data_len = htons ((uint16_t) (pos - (*off + sizeof (struct record_line))));
647   memcpy (&dst[*off], &rl, sizeof (struct record_line));
648   *off = pos;
649   return GNUNET_OK;  
650 }
651
652
653
654 /**
655  * Given a DNS packet, generate the corresponding UDP payload.
656  * Note that we do not attempt to pack the strings with pointers
657  * as this would complicate the code and this is about being 
658  * simple and secure, not fast, fancy and broken like bind.
659  *
660  * @param p packet to pack
661  * @param max maximum allowed size for the resulting UDP payload
662  * @param buf set to a buffer with the packed message
663  * @param buf_length set to the length of buf
664  * @return GNUNET_SYSERR if 'p' is invalid
665  *         GNUNET_NO if 'p' was truncated (but there is still a result in 'buf')
666  *         GNUNET_OK if 'p' was packed completely into '*buf'
667  */
668 int
669 GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
670                        uint16_t max,
671                        char **buf,
672                        size_t *buf_length)
673 {  
674   struct dns_header dns;
675   size_t off;
676   char tmp[max];
677   unsigned int i;
678   int ret;
679   int trc;
680   
681   if ( (p->num_queries > UINT16_MAX) ||
682        (p->num_answers > UINT16_MAX) ||
683        (p->num_authority_records > UINT16_MAX) ||
684        (p->num_additional_records > UINT16_MAX) )
685     return GNUNET_SYSERR;
686   dns.id = p->id;
687   dns.flags = p->flags;
688   dns.query_count = htons (p->num_queries);
689   dns.answer_rcount = htons (p->num_answers);
690   dns.authority_rcount = htons (p->num_authority_records);
691   dns.additional_rcount = htons (p->num_additional_records);
692   off = sizeof (struct dns_header);
693   trc = GNUNET_NO;
694   for (i=0;i<p->num_queries;i++)
695   {
696     ret = add_query (tmp, sizeof (tmp), &off, &p->queries[i]);  
697     if (GNUNET_SYSERR == ret)
698       return GNUNET_SYSERR;
699     if (GNUNET_NO == ret)
700     {
701       dns.query_count = htons ((uint16_t) (i-1));
702       trc = GNUNET_YES;      
703       break;
704     }
705   }
706   for (i=0;i<p->num_answers;i++)
707   {
708     ret = add_record (tmp, sizeof (tmp), &off, &p->answers[i]);  
709     if (GNUNET_SYSERR == ret)
710       return GNUNET_SYSERR;
711     if (GNUNET_NO == ret)
712     {
713       dns.answer_rcount = htons ((uint16_t) (i-1));
714       trc = GNUNET_YES;      
715       break;
716     }
717   }
718   for (i=0;i<p->num_authority_records;i++)
719   {
720     ret = add_record (tmp, sizeof (tmp), &off, &p->authority_records[i]);  
721     if (GNUNET_SYSERR == ret)
722       return GNUNET_SYSERR;
723     if (GNUNET_NO == ret)
724     {
725       dns.authority_rcount = htons ((uint16_t) (i-1));
726       trc = GNUNET_YES;      
727       break;
728     }
729   }
730   for (i=0;i<p->num_additional_records;i++)
731   {
732     ret = add_record (tmp, sizeof (tmp), &off, &p->additional_records[i]);  
733     if (GNUNET_SYSERR == ret)
734       return GNUNET_SYSERR;
735     if (GNUNET_NO == ret)
736     {
737       dns.additional_rcount = htons (i-1);
738       trc = GNUNET_YES;      
739       break;
740     }
741   }
742   if (GNUNET_YES == trc)
743     dns.flags.message_truncated = 1;
744     
745
746   memcpy (tmp, &dns, sizeof (struct dns_header));
747   *buf = GNUNET_malloc (off);
748   *buf_length = off;
749   memcpy (*buf, tmp, off);
750   return trc;
751 }
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782 /* legacy code follows */
783
784 /**
785  * Parse a name from DNS to a normal .-delimited, 0-terminated string.
786  *
787  * @param d The destination of the name. Should have at least 255 bytes allocated.
788  * @param src The DNS-Packet
789  * @param idx The offset inside the Packet from which on the name should be read
790  * @returns The offset of the first unparsed byte (the byte right behind the name)
791  */
792 static unsigned int
793 parse_dns_name (char *d, const unsigned char *src, unsigned short idx)
794 {                               /*{{{ */
795   char *dest = d;
796
797   int len = src[idx++];
798
799   while (len != 0)
800   {
801     if (len & 0xC0)
802     {                           /* Compressed name, offset in this and the next octet */
803       unsigned short offset = ((len & 0x3F) << 8) | src[idx++];
804
805       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 */
806       return idx;
807     }
808     memcpy (dest, src + idx, len);
809     idx += len;
810     dest += len;
811     *dest = '.';
812     dest++;
813     len = src[idx++];
814   };
815   *dest = 0;
816
817   return idx;
818 }
819
820 /*}}}*/
821
822 /**
823  * Parse a complete DNS-Record from raw DNS-data to a struct dns_record
824  *
825  * @param data The DNS-data
826  * @param dst Pointer to count pointers; individual pointers will be allocated
827  * @param count Number of records to parse
828  * @param idx The offset inside the Packet from which on the name should be read
829  * @returns The offset of the first unparsed byte (the byte right behind the last record)
830  */
831 static unsigned short
832 parse_dns_record (unsigned char *data,  /*{{{ */
833                   struct dns_record **dst, unsigned short count,
834                   unsigned short idx)
835 {
836   int i;
837   unsigned short _idx;
838
839   for (i = 0; i < count; i++)
840   {
841     dst[i] = GNUNET_malloc (sizeof (struct dns_record));
842     dst[i]->name = alloca (255);        // see RFC1035, no name can be longer than this.
843     char *name = dst[i]->name;
844
845     _idx = parse_dns_name (name, data, idx);
846     dst[i]->namelen = _idx - idx;
847
848     dst[i]->name = GNUNET_malloc (dst[i]->namelen);
849     memcpy (dst[i]->name, name, dst[i]->namelen);
850
851     idx = _idx;
852
853     dst[i]->type = *((unsigned short *) (data + idx));
854     idx += 2;
855     dst[i]->class = *((unsigned short *) (data + idx));
856     idx += 2;
857     dst[i]->ttl = *((unsigned int *) (data + idx));
858     idx += 4;
859     dst[i]->data_len = *((unsigned short *) (data + idx));
860     idx += 2;
861     dst[i]->data = GNUNET_malloc (ntohs (dst[i]->data_len));
862     memcpy (dst[i]->data, data + idx, ntohs (dst[i]->data_len));
863     idx += ntohs (dst[i]->data_len);
864   }
865   return idx;
866 }                               /*}}} */
867
868 /**
869  * Parse a raw DNS-Packet into an usable struct
870  */
871 struct dns_pkt_parsed *
872 parse_dns_packet (struct dns_pkt *pkt)
873 {                               /*{{{ */
874   struct dns_pkt_parsed *ppkt = GNUNET_malloc (sizeof (struct dns_pkt_parsed));
875
876   memcpy (&ppkt->s, &pkt->s, sizeof pkt->s);
877
878   unsigned short qdcount = ntohs (ppkt->s.qdcount);
879   unsigned short ancount = ntohs (ppkt->s.ancount);
880   unsigned short nscount = ntohs (ppkt->s.nscount);
881   unsigned short arcount = ntohs (ppkt->s.arcount);
882
883   ppkt->queries = GNUNET_malloc (qdcount * sizeof (struct dns_query *));
884   ppkt->answers = GNUNET_malloc (ancount * sizeof (struct dns_record *));
885   ppkt->nameservers = GNUNET_malloc (nscount * sizeof (struct dns_record *));
886   ppkt->additional = GNUNET_malloc (arcount * sizeof (struct dns_record *));
887
888   unsigned short idx = 0, _idx; /* This keeps track how far we have parsed the data */
889
890   /* Parse the Query */
891   int i;
892
893   for (i = 0; i < qdcount; i++)
894   {                             /*{{{ */
895     ppkt->queries[i] = GNUNET_malloc (sizeof (struct dns_query));
896     char *name = alloca (255);  /* see RFC1035, it can't be more than this. */
897
898     _idx = parse_dns_name (name, pkt->data, idx);
899     ppkt->queries[i]->namelen = _idx - idx;
900     idx = _idx;
901
902     ppkt->queries[i]->name = GNUNET_malloc (ppkt->queries[i]->namelen);
903     memcpy (ppkt->queries[i]->name, name, ppkt->queries[i]->namelen);
904
905     ppkt->queries[i]->qtype = *((unsigned short *) (pkt->data + idx));
906     idx += 2;
907     ppkt->queries[i]->qclass = *((unsigned short *) (pkt->data + idx));
908     idx += 2;
909   }
910   /*}}} */
911   idx = parse_dns_record (pkt->data, ppkt->answers, ancount, idx);
912   idx = parse_dns_record (pkt->data, ppkt->nameservers, nscount, idx);
913   idx = parse_dns_record (pkt->data, ppkt->additional, arcount, idx);
914   return ppkt;
915 }                               /*}}} */
916
917 static void
918 unparse_dns_name (char *dest, char *src, size_t len)
919 {
920   char *b = dest;
921   char cnt = 0;
922
923   dest++;
924   while (*src != 0)
925   {
926     while (*src != '.' && *src != 0)
927     {
928       *dest = *src;
929       src++;
930       dest++;
931       cnt++;
932     }
933     *b = cnt;
934     cnt = 0;
935     b = dest;
936     dest++;
937     src++;
938   }
939   *b = 0;
940 }
941
942 struct dns_pkt *
943 unparse_dns_packet (struct dns_pkt_parsed *ppkt)
944 {
945   size_t size = sizeof (struct dns_pkt) - 1;
946   int i;
947
948   for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
949     size += ppkt->queries[i]->namelen + 1;
950
951   for (i = 0; i < ntohs (ppkt->s.ancount); i++)
952   {
953     size += ppkt->answers[i]->namelen + 1;
954     size += ppkt->answers[i]->data_len;
955   }
956   for (i = 0; i < ntohs (ppkt->s.nscount); i++)
957   {
958     size += ppkt->nameservers[i]->namelen + 1;
959     size += ppkt->nameservers[i]->data_len;
960   }
961   for (i = 0; i < ntohs (ppkt->s.arcount); i++)
962   {
963     size += ppkt->additional[i]->namelen + 1;
964     size += ppkt->additional[i]->data_len;
965   }
966
967   size +=
968       4 * ntohs (ppkt->s.qdcount) + 10 * (ntohs (ppkt->s.ancount) +
969                                           ntohs (ppkt->s.arcount) +
970                                           ntohs (ppkt->s.nscount));
971
972   struct dns_pkt *pkt = GNUNET_malloc (size);
973   char *pkt_c = (char *) pkt;
974
975   memcpy (&pkt->s, &ppkt->s, sizeof ppkt->s);
976   size_t idx = sizeof ppkt->s;
977
978   for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
979   {
980     unparse_dns_name (&pkt_c[idx], ppkt->queries[i]->name,
981                       ppkt->queries[i]->namelen);
982     idx += ppkt->queries[i]->namelen;
983     struct dns_query_line *d = (struct dns_query_line *) &pkt_c[idx];
984
985     d->class = ppkt->queries[i]->qclass;
986     d->type = ppkt->queries[i]->qtype;
987     idx += sizeof (struct dns_query_line);
988   }
989
990   for (i = 0; i < ntohs (ppkt->s.ancount); i++)
991   {
992     unparse_dns_name (&pkt_c[idx], ppkt->answers[i]->name,
993                       ppkt->answers[i]->namelen);
994     idx += ppkt->answers[i]->namelen;
995     struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
996
997     r->type = ppkt->answers[i]->type;
998     r->class = ppkt->answers[i]->class;
999     r->ttl = ppkt->answers[i]->ttl;
1000     r->data_len = ppkt->answers[i]->data_len;
1001     idx += sizeof (struct dns_record_line);
1002     memcpy (&r->data, ppkt->answers[i]->data, ppkt->answers[i]->data_len);
1003     idx += ppkt->answers[i]->data_len;
1004   }
1005
1006   for (i = 0; i < ntohs (ppkt->s.nscount); i++)
1007   {
1008     unparse_dns_name (&pkt_c[idx], ppkt->nameservers[i]->name,
1009                       ppkt->nameservers[i]->namelen);
1010     idx += ppkt->nameservers[i]->namelen;
1011     struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
1012
1013     r->type = ppkt->nameservers[i]->type;
1014     r->class = ppkt->nameservers[i]->class;
1015     r->ttl = ppkt->nameservers[i]->ttl;
1016     r->data_len = ppkt->nameservers[i]->data_len;
1017     idx += sizeof (struct dns_record_line);
1018     memcpy (&r->data, ppkt->nameservers[i]->data,
1019             ppkt->nameservers[i]->data_len);
1020     idx += ppkt->nameservers[i]->data_len;
1021   }
1022
1023   for (i = 0; i < ntohs (ppkt->s.arcount); i++)
1024   {
1025     unparse_dns_name (&pkt_c[idx], ppkt->additional[i]->name,
1026                       ppkt->additional[i]->namelen);
1027     idx += ppkt->additional[i]->namelen;
1028     struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
1029
1030     r->type = ppkt->additional[i]->type;
1031     r->class = ppkt->additional[i]->class;
1032     r->ttl = ppkt->additional[i]->ttl;
1033     r->data_len = ppkt->additional[i]->data_len;
1034     idx += sizeof (struct dns_record_line);
1035     memcpy (&r->data, ppkt->additional[i]->data, ppkt->additional[i]->data_len);
1036     idx += ppkt->additional[i]->data_len;
1037   }
1038
1039   return pkt;
1040 }
1041
1042 void
1043 free_parsed_dns_packet (struct dns_pkt_parsed *ppkt)
1044 {
1045   unsigned short qdcount = ntohs (ppkt->s.qdcount);
1046   unsigned short ancount = ntohs (ppkt->s.ancount);
1047   unsigned short nscount = ntohs (ppkt->s.nscount);
1048   unsigned short arcount = ntohs (ppkt->s.arcount);
1049
1050   int i;
1051
1052   for (i = 0; i < qdcount; i++)
1053   {
1054     GNUNET_free (ppkt->queries[i]->name);
1055     GNUNET_free (ppkt->queries[i]);
1056   }
1057   GNUNET_free (ppkt->queries);
1058   for (i = 0; i < ancount; i++)
1059   {
1060     GNUNET_free (ppkt->answers[i]->name);
1061     GNUNET_free (ppkt->answers[i]->data);
1062     GNUNET_free (ppkt->answers[i]);
1063   }
1064   GNUNET_free (ppkt->answers);
1065   for (i = 0; i < nscount; i++)
1066   {
1067     GNUNET_free (ppkt->nameservers[i]->name);
1068     GNUNET_free (ppkt->nameservers[i]->data);
1069     GNUNET_free (ppkt->nameservers[i]);
1070   }
1071   GNUNET_free (ppkt->nameservers);
1072   for (i = 0; i < arcount; i++)
1073   {
1074     GNUNET_free (ppkt->additional[i]->name);
1075     GNUNET_free (ppkt->additional[i]->data);
1076     GNUNET_free (ppkt->additional[i]);
1077   }
1078   GNUNET_free (ppkt->additional);
1079   GNUNET_free (ppkt);
1080 }