-fixes
[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 #include "dnsparser.h"
31
32
33
34 /**
35  * Parse name inside of a DNS query or record.
36  *
37  * @param udp_payload entire UDP payload
38  * @param udp_payload_length length of udp_payload
39  * @param off pointer to the offset of the name to parse in the udp_payload (to be
40  *                    incremented by the size of the name)
41  * @param depth current depth of our recursion (to prevent stack overflow)
42  * @return name as 0-terminated C string on success, NULL if the payload is malformed
43  */
44 static char *
45 parse_name (const char *udp_payload,
46             size_t udp_payload_length,
47             size_t *off,
48             unsigned int depth)
49 {
50   const uint8_t *input = (const uint8_t *) udp_payload;
51   char *ret;
52   char *tmp;
53   char *xstr;
54   uint8_t len;
55   size_t xoff;
56   
57   ret = GNUNET_strdup ("");
58   while (1)
59   {
60     if (*off >= udp_payload_length)
61       goto error;
62     len = input[*off];
63     if (0 == len)
64     {
65       (*off)++;
66       break;
67     }
68     if (len < 64)
69     {
70       if (*off + 1 + len > udp_payload_length)
71         goto error;
72       GNUNET_asprintf (&tmp,
73                        "%s%.*s.",
74                        ret,
75                        (int) len,
76                        &udp_payload[*off + 1]);
77       GNUNET_free (ret);
78       ret = tmp;
79       *off += 1 + len;
80     }
81     else if ((64 | 128) == (len & (64 | 128)) )
82     {
83       if (depth > 32)
84         goto error; /* hard bound on stack to prevent "infinite" recursion, disallow! */
85       /* pointer to string */
86       if (*off + 1 > udp_payload_length)
87         goto error;
88       xoff = ((len - (64 | 128)) << 8) + input[*off+1];
89       xstr = parse_name (udp_payload,
90                          udp_payload_length,
91                          &xoff,
92                          depth + 1);
93       if (NULL == xstr)
94         goto error;
95       GNUNET_asprintf (&tmp,
96                        "%s%s.",
97                        ret,
98                        xstr);
99       GNUNET_free (ret);
100       GNUNET_free (xstr);
101       ret = tmp;
102       if (strlen (ret) > udp_payload_length)
103         goto error; /* we are looping (building an infinite string) */
104       *off += 2;
105       /* pointers always terminate names */
106       break;
107     } 
108     else
109     {
110       /* neither pointer nor inline string, not supported... */
111       goto error;
112     }
113   }
114   if (0 < strlen(ret))
115     ret[strlen(ret)-1] = '\0'; /* eat tailing '.' */
116   return ret;
117  error:  
118   GNUNET_free (ret);
119   return NULL;
120 }
121
122
123 /**
124  * Parse a DNS query entry.
125  *
126  * @param udp_payload entire UDP payload
127  * @param udp_payload_length length of udp_payload
128  * @param off pointer to the offset of the query to parse in the udp_payload (to be
129  *                    incremented by the size of the query)
130  * @param q where to write the query information
131  * @return GNUNET_OK on success, GNUNET_SYSERR if the query is malformed
132  */
133 static int
134 parse_query (const char *udp_payload,
135              size_t udp_payload_length,
136              size_t *off,
137              struct GNUNET_DNSPARSER_Query *q)
138 {
139   char *name;
140   struct query_line ql;
141
142   name = parse_name (udp_payload, 
143                      udp_payload_length,
144                      off, 0);
145   if (NULL == name)
146     return GNUNET_SYSERR;
147   q->name = name;
148   if (*off + sizeof (struct query_line) > udp_payload_length)
149     return GNUNET_SYSERR;
150   memcpy (&ql, &udp_payload[*off], sizeof (ql));
151   *off += sizeof (ql);
152   q->type = ntohs (ql.type);
153   q->class = ntohs (ql.class);
154   return GNUNET_OK;
155 }
156
157
158 /**
159  * Parse a DNS record entry.
160  *
161  * @param udp_payload entire UDP payload
162  * @param udp_payload_length length of udp_payload
163  * @param off pointer to the offset of the record to parse in the udp_payload (to be
164  *                    incremented by the size of the record)
165  * @param r where to write the record information
166  * @return GNUNET_OK on success, GNUNET_SYSERR if the record is malformed
167  */
168 static int
169 parse_record (const char *udp_payload,
170               size_t udp_payload_length,
171               size_t *off,
172               struct GNUNET_DNSPARSER_Record *r)
173 {
174   char *name;
175   struct record_line rl;
176   size_t old_off;
177   struct soa_data soa;
178   uint16_t mxpref;
179   uint16_t data_len;
180   struct srv_data srv;
181   char *ndup;
182   char *tok;
183
184   name = parse_name (udp_payload, 
185                      udp_payload_length,
186                      off, 0);
187   if (NULL == name)
188     return GNUNET_SYSERR;
189   r->name = name;
190   if (*off + sizeof (struct record_line) > udp_payload_length)
191     return GNUNET_SYSERR;
192   memcpy (&rl, &udp_payload[*off], sizeof (rl));
193   (*off) += sizeof (rl);
194   r->type = ntohs (rl.type);
195   r->class = ntohs (rl.class);
196   r->expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
197                                                                                         ntohl (rl.ttl)));
198   data_len = ntohs (rl.data_len);
199   if (*off + data_len > udp_payload_length)
200     return GNUNET_SYSERR;
201   switch (r->type)
202   {
203   case GNUNET_DNSPARSER_TYPE_NS:
204   case GNUNET_DNSPARSER_TYPE_CNAME:
205   case GNUNET_DNSPARSER_TYPE_PTR:
206     old_off = *off;
207     r->data.hostname = parse_name (udp_payload,
208                                    udp_payload_length,
209                                    off, 0);    
210     if ( (NULL == r->data.hostname) ||
211          (old_off + data_len != *off) )
212       return GNUNET_SYSERR;
213     return GNUNET_OK;
214   case GNUNET_DNSPARSER_TYPE_SOA:
215     old_off = *off;
216     r->data.soa = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_SoaRecord));
217     r->data.soa->mname = parse_name (udp_payload,
218                                      udp_payload_length,
219                                      off, 0);
220     r->data.soa->rname = parse_name (udp_payload,
221                                      udp_payload_length,
222                                      off, 0);
223     if ( (NULL == r->data.soa->mname) ||
224          (NULL == r->data.soa->rname) ||
225          (*off + sizeof (struct soa_data) > udp_payload_length) )
226       return GNUNET_SYSERR;
227     memcpy (&soa, &udp_payload[*off], sizeof (struct soa_data));
228     r->data.soa->serial = ntohl (soa.serial);
229     r->data.soa->refresh = ntohl (soa.refresh);
230     r->data.soa->retry = ntohl (soa.retry);
231     r->data.soa->expire = ntohl (soa.expire);
232     r->data.soa->minimum_ttl = ntohl (soa.minimum);
233     (*off) += sizeof (struct soa_data);
234     if (old_off + data_len != *off) 
235       return GNUNET_SYSERR;
236     return GNUNET_OK;
237   case GNUNET_DNSPARSER_TYPE_MX:
238     old_off = *off;
239     if (*off + sizeof (uint16_t) > udp_payload_length)
240       return GNUNET_SYSERR;
241     memcpy (&mxpref, &udp_payload[*off], sizeof (uint16_t));    
242     (*off) += sizeof (uint16_t);
243     r->data.mx = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_MxRecord));
244     r->data.mx->preference = ntohs (mxpref);
245     r->data.mx->mxhost = parse_name (udp_payload,
246                                      udp_payload_length,
247                                      off, 0);
248     if (old_off + data_len != *off) 
249       return GNUNET_SYSERR;
250     return GNUNET_OK;
251   case GNUNET_DNSPARSER_TYPE_SRV:
252     if ('_' != *r->name)
253       return GNUNET_SYSERR; /* all valid srv names must start with "_" */
254     if (NULL == strstr (r->name, "._"))
255       return GNUNET_SYSERR; /* necessary string from "._$PROTO" not present */
256     old_off = *off;
257     if (*off + sizeof (struct srv_data) > udp_payload_length)
258       return GNUNET_SYSERR;
259     memcpy (&srv, &udp_payload[*off], sizeof (struct srv_data));    
260     (*off) += sizeof (struct srv_data);
261     r->data.srv = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_SrvRecord));
262     r->data.srv->priority = ntohs (srv.prio);
263     r->data.srv->weight = ntohs (srv.weight);
264     r->data.srv->port = ntohs (srv.port);
265     /* parse 'data.hostname' into components, which are
266        "_$SERVICE._$PROTO.$DOMAIN_NAME" */
267     ndup = GNUNET_strdup (r->name);
268     tok = strtok (ndup, ".");
269     GNUNET_assert (NULL != tok);
270     GNUNET_assert ('_' == *tok);
271     r->data.srv->service = GNUNET_strdup (&tok[1]);
272     tok = strtok (NULL, ".");
273     if ( (NULL == tok) || ('_' != *tok) )
274     {
275       GNUNET_free (r->data.srv);
276       GNUNET_free (ndup);
277       return GNUNET_SYSERR;
278     }
279     r->data.srv->proto = GNUNET_strdup (&tok[1]);
280     tok = strtok (NULL, ".");
281     if (NULL == tok)
282     {
283       GNUNET_free (r->data.srv);
284       GNUNET_free (ndup);
285       return GNUNET_SYSERR;
286     }
287     r->data.srv->domain_name = GNUNET_strdup (tok);
288     GNUNET_free (ndup);
289     r->data.srv->target = parse_name (udp_payload,
290                                       udp_payload_length,
291                                       off, 0);
292     if (old_off + data_len != *off) 
293       return GNUNET_SYSERR;
294     return GNUNET_OK;
295   default:
296     r->data.raw.data = GNUNET_malloc (data_len);
297     r->data.raw.data_len = data_len;
298     memcpy (r->data.raw.data, &udp_payload[*off], data_len);
299     break;
300   }
301   (*off) += data_len;
302   return GNUNET_OK;  
303 }
304
305
306 /**
307  * Parse a UDP payload of a DNS packet in to a nice struct for further
308  * processing and manipulation.
309  *
310  * @param udp_payload wire-format of the DNS packet
311  * @param udp_payload_length number of bytes in udp_payload 
312  * @return NULL on error, otherwise the parsed packet
313  */
314 struct GNUNET_DNSPARSER_Packet *
315 GNUNET_DNSPARSER_parse (const char *udp_payload,
316                         size_t udp_payload_length)
317 {
318   struct GNUNET_DNSPARSER_Packet *p;
319   const struct GNUNET_TUN_DnsHeader *dns;
320   size_t off;
321   unsigned int n;  
322   unsigned int i;
323
324   if (udp_payload_length < sizeof (struct GNUNET_TUN_DnsHeader))
325     return NULL;
326   dns = (const struct GNUNET_TUN_DnsHeader *) udp_payload;
327   off = sizeof (struct GNUNET_TUN_DnsHeader);
328   p = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_Packet));
329   p->flags = dns->flags;
330   p->id = dns->id;
331   n = ntohs (dns->query_count);
332   if (n > 0)
333   {
334     p->queries = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Query));
335     p->num_queries = n;
336     for (i=0;i<n;i++)
337       if (GNUNET_OK !=
338           parse_query (udp_payload,
339                        udp_payload_length,
340                        &off,
341                        &p->queries[i]))
342         goto error;
343   }
344   n = ntohs (dns->answer_rcount);
345   if (n > 0)
346   {
347     p->answers = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
348     p->num_answers = n;
349     for (i=0;i<n;i++)
350       if (GNUNET_OK !=
351           parse_record (udp_payload,
352                         udp_payload_length,
353                         &off,
354                         &p->answers[i]))
355         goto error;
356   }
357   n = ntohs (dns->authority_rcount);
358   if (n > 0)
359   {
360     p->authority_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
361     p->num_authority_records = n;
362     for (i=0;i<n;i++)
363       if (GNUNET_OK !=
364           parse_record (udp_payload,
365                         udp_payload_length,
366                         &off,
367                         &p->authority_records[i]))
368         goto error;  
369   }
370   n = ntohs (dns->additional_rcount);
371   if (n > 0)
372   {
373     p->additional_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
374     p->num_additional_records = n;
375     for (i=0;i<n;i++)
376       if (GNUNET_OK !=
377           parse_record (udp_payload,
378                         udp_payload_length,
379                         &off,
380                         &p->additional_records[i]))
381         goto error;   
382   }
383   return p;
384  error:
385   GNUNET_DNSPARSER_free_packet (p);
386   return NULL;
387 }
388
389
390 /**
391  * Free SOA information record.
392  *
393  * @param soa record to free
394  */
395 static void
396 free_soa (struct GNUNET_DNSPARSER_SoaRecord *soa)
397 {
398   if (NULL == soa)
399     return;
400   GNUNET_free_non_null (soa->mname);
401   GNUNET_free_non_null (soa->rname);
402   GNUNET_free (soa);      
403 }
404
405
406 /**
407  * Free SRV information record.
408  *
409  * @param srv record to free
410  */
411 static void
412 free_srv (struct GNUNET_DNSPARSER_SrvRecord *srv)
413 {
414   if (NULL == srv)
415     return;
416   GNUNET_free_non_null (srv->target);
417   GNUNET_free_non_null (srv->domain_name);
418   GNUNET_free_non_null (srv->proto);
419   GNUNET_free_non_null (srv->service);
420   GNUNET_free (srv);      
421 }
422
423
424 /**
425  * Free MX information record.
426  *
427  * @param mx record to free
428  */
429 static void
430 free_mx (struct GNUNET_DNSPARSER_MxRecord *mx)
431 {
432   if (NULL == mx)
433     return;
434   GNUNET_free_non_null (mx->mxhost);
435   GNUNET_free (mx);      
436 }
437
438
439 static void
440 free_record (struct GNUNET_DNSPARSER_Record *r)
441 {
442   GNUNET_free_non_null (r->name);
443   switch (r->type)
444   {
445   case GNUNET_DNSPARSER_TYPE_MX:
446     free_mx (r->data.mx);
447     break;
448   case GNUNET_DNSPARSER_TYPE_SOA:
449     free_soa (r->data.soa);
450     break;
451   case GNUNET_DNSPARSER_TYPE_SRV:
452     free_srv (r->data.srv);
453     break;
454   case GNUNET_DNSPARSER_TYPE_NS:
455   case GNUNET_DNSPARSER_TYPE_CNAME:
456   case GNUNET_DNSPARSER_TYPE_PTR:
457     GNUNET_free_non_null (r->data.hostname);
458     break;
459   default:
460     GNUNET_free_non_null (r->data.raw.data);
461     break;
462   }
463 }
464
465
466 /**
467  * Free memory taken by a packet.
468  *
469  * @param p packet to free
470  */
471 void
472 GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
473 {
474   unsigned int i;
475
476   for (i=0;i<p->num_queries;i++)
477     GNUNET_free_non_null (p->queries[i].name);
478   GNUNET_free_non_null (p->queries);
479   for (i=0;i<p->num_answers;i++)
480     free_record (&p->answers[i]);
481   GNUNET_free_non_null (p->answers);
482   for (i=0;i<p->num_authority_records;i++)
483     free_record (&p->authority_records[i]);
484   GNUNET_free_non_null (p->authority_records);
485   for (i=0;i<p->num_additional_records;i++)
486     free_record (&p->additional_records[i]);
487   GNUNET_free_non_null (p->additional_records);
488   GNUNET_free (p);
489 }
490
491
492 /* ********************** DNS packet assembly code **************** */
493
494
495 /**
496  * Add a DNS name to the UDP packet at the given location.
497  *
498  * @param dst where to write the name
499  * @param dst_len number of bytes in dst
500  * @param off pointer to offset where to write the name (increment by bytes used)
501  *            must not be changed if there is an error
502  * @param name name to write
503  * @return GNUNET_SYSERR if 'name' is invalid
504  *         GNUNET_NO if 'name' did not fit
505  *         GNUNET_OK if 'name' was added to 'dst'
506  */
507 static int
508 add_name (char *dst,
509           size_t dst_len,
510           size_t *off,
511           const char *name)
512 {
513   const char *dot;
514   size_t start;
515   size_t pos;
516   size_t len;
517
518   if (NULL == name)
519     return GNUNET_SYSERR;
520   start = *off;
521   if (start + strlen (name) + 2 > dst_len)
522     return GNUNET_NO;
523   pos = start;
524   do
525   {
526     dot = strchr (name, '.');
527     if (NULL == dot)
528       len = strlen (name);
529     else
530       len = dot - name;
531     if ( (len >= 64) || (len == 0) )
532       return GNUNET_NO; /* segment too long or empty */
533     dst[pos++] = (char) (uint8_t) len;
534     memcpy (&dst[pos], name, len);
535     pos += len;
536     name += len + 1; /* also skip dot */
537   }
538   while (NULL != dot);
539   dst[pos++] = '\0'; /* terminator */
540   *off = pos;
541   return GNUNET_OK;
542 }
543
544
545 /**
546  * Add a DNS query to the UDP packet at the given location.
547  *
548  * @param dst where to write the query
549  * @param dst_len number of bytes in dst
550  * @param off pointer to offset where to write the query (increment by bytes used)
551  *            must not be changed if there is an error
552  * @param query query to write
553  * @return GNUNET_SYSERR if 'query' is invalid
554  *         GNUNET_NO if 'query' did not fit
555  *         GNUNET_OK if 'query' was added to 'dst'
556  */
557 static int
558 add_query (char *dst,
559            size_t dst_len,
560            size_t *off,
561            const struct GNUNET_DNSPARSER_Query *query)
562 {
563   int ret;
564   struct query_line ql;
565
566   ret = add_name (dst, dst_len - sizeof (struct query_line), off, query->name);
567   if (ret != GNUNET_OK)
568     return ret;
569   ql.type = htons (query->type);
570   ql.class = htons (query->class);
571   memcpy (&dst[*off], &ql, sizeof (ql));
572   (*off) += sizeof (ql);
573   return GNUNET_OK;
574 }
575
576
577 /**
578  * Add an MX record to the UDP packet at the given location.
579  *
580  * @param dst where to write the mx record
581  * @param dst_len number of bytes in dst
582  * @param off pointer to offset where to write the mx information (increment by bytes used);
583  *            can also change if there was an error
584  * @param mx mx information to write
585  * @return GNUNET_SYSERR if 'mx' is invalid
586  *         GNUNET_NO if 'mx' did not fit
587  *         GNUNET_OK if 'mx' was added to 'dst'
588  */
589 static int
590 add_mx (char *dst,
591         size_t dst_len,
592         size_t *off,
593         const struct GNUNET_DNSPARSER_MxRecord *mx)
594 {
595   uint16_t mxpref;
596
597   if (*off + sizeof (uint16_t) > dst_len)
598     return GNUNET_NO;
599   mxpref = htons (mx->preference);
600   memcpy (&dst[*off], &mxpref, sizeof (mxpref));
601   (*off) += sizeof (mxpref);
602   return add_name (dst, dst_len, off, mx->mxhost);
603 }
604
605
606 /**
607  * Add an SOA record to the UDP packet at the given location.
608  *
609  * @param dst where to write the SOA record
610  * @param dst_len number of bytes in dst
611  * @param off pointer to offset where to write the SOA information (increment by bytes used)
612  *            can also change if there was an error
613  * @param soa SOA information to write
614  * @return GNUNET_SYSERR if 'soa' is invalid
615  *         GNUNET_NO if 'soa' did not fit
616  *         GNUNET_OK if 'soa' was added to 'dst'
617  */
618 static int
619 add_soa (char *dst,
620          size_t dst_len,
621          size_t *off,
622          const struct GNUNET_DNSPARSER_SoaRecord *soa)
623 {
624   struct soa_data sd;
625   int ret;
626
627   if ( (GNUNET_OK != (ret = add_name (dst,
628                                       dst_len,
629                                       off,
630                                       soa->mname))) ||
631        (GNUNET_OK != (ret = add_name (dst,
632                                       dst_len,
633                                       off,
634                                       soa->rname)) ) )
635     return ret;
636   if (*off + sizeof (struct soa_data) > dst_len)
637     return GNUNET_NO;
638   sd.serial = htonl (soa->serial);
639   sd.refresh = htonl (soa->refresh);
640   sd.retry = htonl (soa->retry);
641   sd.expire = htonl (soa->expire);
642   sd.minimum = htonl (soa->minimum_ttl);
643   memcpy (&dst[*off], &sd, sizeof (sd));
644   (*off) += sizeof (sd);
645   return GNUNET_OK;
646 }
647
648
649 /**
650  * Add an SRV record to the UDP packet at the given location.
651  *
652  * @param dst where to write the SRV record
653  * @param dst_len number of bytes in dst
654  * @param off pointer to offset where to write the SRV information (increment by bytes used)
655  *            can also change if there was an error
656  * @param srv SRV information to write
657  * @return GNUNET_SYSERR if 'srv' is invalid
658  *         GNUNET_NO if 'srv' did not fit
659  *         GNUNET_OK if 'srv' was added to 'dst'
660  */
661 static int
662 add_srv (char *dst,
663          size_t dst_len,
664          size_t *off,
665          const struct GNUNET_DNSPARSER_SrvRecord *srv)
666 {
667   struct srv_data sd;
668   int ret;
669
670   if (*off + sizeof (struct srv_data) > dst_len)
671     return GNUNET_NO;
672   sd.prio = htons (srv->priority);
673   sd.weight = htons (srv->weight);
674   sd.port = htons (srv->port);
675   memcpy (&dst[*off], &sd, sizeof (sd));
676   (*off) += sizeof (sd);
677   if (GNUNET_OK != (ret = add_name (dst,
678                                     dst_len,
679                                     off,
680                                     srv->target)))
681     return ret;
682   return GNUNET_OK;
683 }
684
685
686 /**
687  * Add a DNS record to the UDP packet at the given location.
688  *
689  * @param dst where to write the query
690  * @param dst_len number of bytes in dst
691  * @param off pointer to offset where to write the query (increment by bytes used)
692  *            must not be changed if there is an error
693  * @param record record to write
694  * @return GNUNET_SYSERR if 'record' is invalid
695  *         GNUNET_NO if 'record' did not fit
696  *         GNUNET_OK if 'record' was added to 'dst'
697  */
698 static int
699 add_record (char *dst,
700             size_t dst_len,
701             size_t *off,
702             const struct GNUNET_DNSPARSER_Record *record)
703 {
704   int ret;
705   size_t start;
706   size_t pos;
707   struct record_line rl;
708   char *name;
709   
710   start = *off;
711   /* for SRV records, we can create the name from the details
712      of the record if needed */
713   name = record->name;
714   if  ( (GNUNET_DNSPARSER_TYPE_SRV == record->type) &&
715         (NULL == name) )
716     GNUNET_asprintf (&name,
717                      "_%s._%s.%s",
718                      record->data.srv->service,
719                      record->data.srv->proto,
720                      record->data.srv->domain_name);
721   ret = add_name (dst, dst_len - sizeof (struct record_line), off, name);
722   if (name != record->name)
723     GNUNET_free (name);
724   if (GNUNET_OK != ret)
725     return ret;
726   /* '*off' is now the position where we will need to write the record line */
727
728   pos = *off + sizeof (struct record_line);
729   switch (record->type)
730   { 
731   case GNUNET_DNSPARSER_TYPE_MX:
732     ret = add_mx (dst, dst_len, &pos, record->data.mx);    
733     break;
734   case GNUNET_DNSPARSER_TYPE_SOA:
735     ret = add_soa (dst, dst_len, &pos, record->data.soa);
736     break;
737   case GNUNET_DNSPARSER_TYPE_NS:
738   case GNUNET_DNSPARSER_TYPE_CNAME:
739   case GNUNET_DNSPARSER_TYPE_PTR:
740     ret = add_name (dst, dst_len, &pos, record->data.hostname);
741     break;
742   case GNUNET_DNSPARSER_TYPE_SRV:
743     ret = add_srv (dst, dst_len, &pos, record->data.srv);
744     break;
745   default:
746     if (pos + record->data.raw.data_len > dst_len)
747     {
748       ret = GNUNET_NO;
749       break;
750     }
751     memcpy (&dst[pos], record->data.raw.data, record->data.raw.data_len);
752     pos += record->data.raw.data_len;
753     ret = GNUNET_OK;
754     break;
755   }
756   if (GNUNET_OK != ret)
757   {
758     *off = start;
759     return GNUNET_NO;
760   }
761
762   if (pos - (*off + sizeof (struct record_line)) > UINT16_MAX)
763   {
764     /* record data too long */
765     *off = start;
766     return GNUNET_NO;
767   }
768   rl.type = htons (record->type);
769   rl.class = htons (record->class);
770   rl.ttl = htonl (GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value / 1000); /* in seconds */
771   rl.data_len = htons ((uint16_t) (pos - (*off + sizeof (struct record_line))));
772   memcpy (&dst[*off], &rl, sizeof (struct record_line));
773   *off = pos;
774   return GNUNET_OK;  
775 }
776
777
778 /**
779  * Given a DNS packet, generate the corresponding UDP payload.
780  * Note that we do not attempt to pack the strings with pointers
781  * as this would complicate the code and this is about being 
782  * simple and secure, not fast, fancy and broken like bind.
783  *
784  * @param p packet to pack
785  * @param max maximum allowed size for the resulting UDP payload
786  * @param buf set to a buffer with the packed message
787  * @param buf_length set to the length of buf
788  * @return GNUNET_SYSERR if 'p' is invalid
789  *         GNUNET_NO if 'p' was truncated (but there is still a result in 'buf')
790  *         GNUNET_OK if 'p' was packed completely into '*buf'
791  */
792 int
793 GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
794                        uint16_t max,
795                        char **buf,
796                        size_t *buf_length)
797 {  
798   struct GNUNET_TUN_DnsHeader dns;
799   size_t off;
800   char tmp[max];
801   unsigned int i;
802   int ret;
803   int trc;
804   
805   if ( (p->num_queries > UINT16_MAX) ||
806        (p->num_answers > UINT16_MAX) ||
807        (p->num_authority_records > UINT16_MAX) ||
808        (p->num_additional_records > UINT16_MAX) )
809     return GNUNET_SYSERR;
810   dns.id = p->id;
811   dns.flags = p->flags;
812   dns.query_count = htons (p->num_queries);
813   dns.answer_rcount = htons (p->num_answers);
814   dns.authority_rcount = htons (p->num_authority_records);
815   dns.additional_rcount = htons (p->num_additional_records);
816
817   off = sizeof (struct GNUNET_TUN_DnsHeader);
818   trc = GNUNET_NO;
819   for (i=0;i<p->num_queries;i++)
820   {
821     ret = add_query (tmp, sizeof (tmp), &off, &p->queries[i]);  
822     if (GNUNET_SYSERR == ret)
823       return GNUNET_SYSERR;
824     if (GNUNET_NO == ret)
825     {
826       dns.query_count = htons ((uint16_t) (i-1));
827       trc = GNUNET_YES;      
828       break;
829     }
830   }
831   for (i=0;i<p->num_answers;i++)
832   {
833     ret = add_record (tmp, sizeof (tmp), &off, &p->answers[i]);  
834     if (GNUNET_SYSERR == ret)
835       return GNUNET_SYSERR;
836     if (GNUNET_NO == ret)
837     {
838       dns.answer_rcount = htons ((uint16_t) (i-1));
839       trc = GNUNET_YES;      
840       break;
841     }
842   }
843   for (i=0;i<p->num_authority_records;i++)
844   {
845     ret = add_record (tmp, sizeof (tmp), &off, &p->authority_records[i]);  
846     if (GNUNET_SYSERR == ret)
847       return GNUNET_SYSERR;
848     if (GNUNET_NO == ret)
849     {
850       dns.authority_rcount = htons ((uint16_t) (i-1));
851       trc = GNUNET_YES;      
852       break;
853     }
854   }
855   for (i=0;i<p->num_additional_records;i++)
856   {
857     ret = add_record (tmp, sizeof (tmp), &off, &p->additional_records[i]);  
858     if (GNUNET_SYSERR == ret)
859       return GNUNET_SYSERR;
860     if (GNUNET_NO == ret)
861     {
862       dns.additional_rcount = htons (i-1);
863       trc = GNUNET_YES;      
864       break;
865     }
866   }
867
868   if (GNUNET_YES == trc)
869     dns.flags.message_truncated = 1;    
870   memcpy (tmp, &dns, sizeof (struct GNUNET_TUN_DnsHeader));
871
872   *buf = GNUNET_malloc (off);
873   *buf_length = off;
874   memcpy (*buf, tmp, off);
875   if (GNUNET_YES == trc)
876     return GNUNET_NO;
877   return GNUNET_OK;
878 }
879
880 /* end of dnsparser.c */