c2afbdf3aae27e62c87216e1f896d71f4ac37238
[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 ('_' == *tok);
270     r->data.srv->service = GNUNET_strdup (&tok[1]);
271     tok = strtok (NULL, ".");
272     if ('_' != *tok)
273     {
274       GNUNET_free (r->data.srv);
275       GNUNET_free (ndup);
276       return GNUNET_SYSERR;
277     }
278     r->data.srv->proto = GNUNET_strdup (&tok[1]);
279     tok = strtok (NULL, ".");
280     if (NULL == tok)
281     {
282       GNUNET_free (r->data.srv);
283       GNUNET_free (ndup);
284       return GNUNET_SYSERR;
285     }
286     r->data.srv->domain_name = GNUNET_strdup (tok);
287     GNUNET_free (ndup);
288     r->data.srv->target = parse_name (udp_payload,
289                                       udp_payload_length,
290                                       off, 0);
291     if (old_off + data_len != *off) 
292       return GNUNET_SYSERR;
293     return GNUNET_OK;
294   default:
295     r->data.raw.data = GNUNET_malloc (data_len);
296     r->data.raw.data_len = data_len;
297     memcpy (r->data.raw.data, &udp_payload[*off], data_len);
298     break;
299   }
300   (*off) += data_len;
301   return GNUNET_OK;  
302 }
303
304
305 /**
306  * Parse a UDP payload of a DNS packet in to a nice struct for further
307  * processing and manipulation.
308  *
309  * @param udp_payload wire-format of the DNS packet
310  * @param udp_payload_length number of bytes in udp_payload 
311  * @return NULL on error, otherwise the parsed packet
312  */
313 struct GNUNET_DNSPARSER_Packet *
314 GNUNET_DNSPARSER_parse (const char *udp_payload,
315                         size_t udp_payload_length)
316 {
317   struct GNUNET_DNSPARSER_Packet *p;
318   const struct GNUNET_TUN_DnsHeader *dns;
319   size_t off;
320   unsigned int n;  
321   unsigned int i;
322
323   if (udp_payload_length < sizeof (struct GNUNET_TUN_DnsHeader))
324     return NULL;
325   dns = (const struct GNUNET_TUN_DnsHeader *) udp_payload;
326   off = sizeof (struct GNUNET_TUN_DnsHeader);
327   p = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_Packet));
328   p->flags = dns->flags;
329   p->id = dns->id;
330   n = ntohs (dns->query_count);
331   if (n > 0)
332   {
333     p->queries = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Query));
334     p->num_queries = n;
335     for (i=0;i<n;i++)
336       if (GNUNET_OK !=
337           parse_query (udp_payload,
338                        udp_payload_length,
339                        &off,
340                        &p->queries[i]))
341         goto error;
342   }
343   n = ntohs (dns->answer_rcount);
344   if (n > 0)
345   {
346     p->answers = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
347     p->num_answers = n;
348     for (i=0;i<n;i++)
349       if (GNUNET_OK !=
350           parse_record (udp_payload,
351                         udp_payload_length,
352                         &off,
353                         &p->answers[i]))
354         goto error;
355   }
356   n = ntohs (dns->authority_rcount);
357   if (n > 0)
358   {
359     p->authority_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
360     p->num_authority_records = n;
361     for (i=0;i<n;i++)
362       if (GNUNET_OK !=
363           parse_record (udp_payload,
364                         udp_payload_length,
365                         &off,
366                         &p->authority_records[i]))
367         goto error;  
368   }
369   n = ntohs (dns->additional_rcount);
370   if (n > 0)
371   {
372     p->additional_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
373     p->num_additional_records = n;
374     for (i=0;i<n;i++)
375       if (GNUNET_OK !=
376           parse_record (udp_payload,
377                         udp_payload_length,
378                         &off,
379                         &p->additional_records[i]))
380         goto error;   
381   }
382   return p;
383  error:
384   GNUNET_DNSPARSER_free_packet (p);
385   return NULL;
386 }
387
388
389 /**
390  * Free SOA information record.
391  *
392  * @param soa record to free
393  */
394 static void
395 free_soa (struct GNUNET_DNSPARSER_SoaRecord *soa)
396 {
397   if (NULL == soa)
398     return;
399   GNUNET_free_non_null (soa->mname);
400   GNUNET_free_non_null (soa->rname);
401   GNUNET_free (soa);      
402 }
403
404
405 /**
406  * Free SRV information record.
407  *
408  * @param srv record to free
409  */
410 static void
411 free_srv (struct GNUNET_DNSPARSER_SrvRecord *srv)
412 {
413   if (NULL == srv)
414     return;
415   GNUNET_free_non_null (srv->target);
416   GNUNET_free_non_null (srv->domain_name);
417   GNUNET_free_non_null (srv->proto);
418   GNUNET_free_non_null (srv->service);
419   GNUNET_free (srv);      
420 }
421
422
423 /**
424  * Free MX information record.
425  *
426  * @param mx record to free
427  */
428 static void
429 free_mx (struct GNUNET_DNSPARSER_MxRecord *mx)
430 {
431   if (NULL == mx)
432     return;
433   GNUNET_free_non_null (mx->mxhost);
434   GNUNET_free (mx);      
435 }
436
437
438 static void
439 free_record (struct GNUNET_DNSPARSER_Record *r)
440 {
441   GNUNET_free_non_null (r->name);
442   switch (r->type)
443   {
444   case GNUNET_DNSPARSER_TYPE_MX:
445     free_mx (r->data.mx);
446     break;
447   case GNUNET_DNSPARSER_TYPE_SOA:
448     free_soa (r->data.soa);
449     break;
450   case GNUNET_DNSPARSER_TYPE_SRV:
451     free_srv (r->data.srv);
452     break;
453   case GNUNET_DNSPARSER_TYPE_NS:
454   case GNUNET_DNSPARSER_TYPE_CNAME:
455   case GNUNET_DNSPARSER_TYPE_PTR:
456     GNUNET_free_non_null (r->data.hostname);
457     break;
458   default:
459     GNUNET_free_non_null (r->data.raw.data);
460     break;
461   }
462 }
463
464
465 /**
466  * Free memory taken by a packet.
467  *
468  * @param p packet to free
469  */
470 void
471 GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
472 {
473   unsigned int i;
474
475   for (i=0;i<p->num_queries;i++)
476     GNUNET_free_non_null (p->queries[i].name);
477   GNUNET_free_non_null (p->queries);
478   for (i=0;i<p->num_answers;i++)
479     free_record (&p->answers[i]);
480   GNUNET_free_non_null (p->answers);
481   for (i=0;i<p->num_authority_records;i++)
482     free_record (&p->authority_records[i]);
483   GNUNET_free_non_null (p->authority_records);
484   for (i=0;i<p->num_additional_records;i++)
485     free_record (&p->additional_records[i]);
486   GNUNET_free_non_null (p->additional_records);
487   GNUNET_free (p);
488 }
489
490
491 /* ********************** DNS packet assembly code **************** */
492
493
494 /**
495  * Add a DNS name to the UDP packet at the given location.
496  *
497  * @param dst where to write the name
498  * @param dst_len number of bytes in dst
499  * @param off pointer to offset where to write the name (increment by bytes used)
500  *            must not be changed if there is an error
501  * @param name name to write
502  * @return GNUNET_SYSERR if 'name' is invalid
503  *         GNUNET_NO if 'name' did not fit
504  *         GNUNET_OK if 'name' was added to 'dst'
505  */
506 static int
507 add_name (char *dst,
508           size_t dst_len,
509           size_t *off,
510           const char *name)
511 {
512   const char *dot;
513   size_t start;
514   size_t pos;
515   size_t len;
516
517   if (NULL == name)
518     return GNUNET_SYSERR;
519   start = *off;
520   if (start + strlen (name) + 2 > dst_len)
521     return GNUNET_NO;
522   pos = start;
523   do
524   {
525     dot = strchr (name, '.');
526     if (NULL == dot)
527       len = strlen (name);
528     else
529       len = dot - name;
530     if ( (len >= 64) || (len == 0) )
531       return GNUNET_NO; /* segment too long or empty */
532     dst[pos++] = (char) (uint8_t) len;
533     memcpy (&dst[pos], name, len);
534     pos += len;
535     name += len + 1; /* also skip dot */
536   }
537   while (NULL != dot);
538   dst[pos++] = '\0'; /* terminator */
539   *off = pos;
540   return GNUNET_OK;
541 }
542
543
544 /**
545  * Add a DNS query to the UDP packet at the given location.
546  *
547  * @param dst where to write the query
548  * @param dst_len number of bytes in dst
549  * @param off pointer to offset where to write the query (increment by bytes used)
550  *            must not be changed if there is an error
551  * @param query query to write
552  * @return GNUNET_SYSERR if 'query' is invalid
553  *         GNUNET_NO if 'query' did not fit
554  *         GNUNET_OK if 'query' was added to 'dst'
555  */
556 static int
557 add_query (char *dst,
558            size_t dst_len,
559            size_t *off,
560            const struct GNUNET_DNSPARSER_Query *query)
561 {
562   int ret;
563   struct query_line ql;
564
565   ret = add_name (dst, dst_len - sizeof (struct query_line), off, query->name);
566   if (ret != GNUNET_OK)
567     return ret;
568   ql.type = htons (query->type);
569   ql.class = htons (query->class);
570   memcpy (&dst[*off], &ql, sizeof (ql));
571   (*off) += sizeof (ql);
572   return GNUNET_OK;
573 }
574
575
576 /**
577  * Add an MX record to the UDP packet at the given location.
578  *
579  * @param dst where to write the mx record
580  * @param dst_len number of bytes in dst
581  * @param off pointer to offset where to write the mx information (increment by bytes used);
582  *            can also change if there was an error
583  * @param mx mx information to write
584  * @return GNUNET_SYSERR if 'mx' is invalid
585  *         GNUNET_NO if 'mx' did not fit
586  *         GNUNET_OK if 'mx' was added to 'dst'
587  */
588 static int
589 add_mx (char *dst,
590         size_t dst_len,
591         size_t *off,
592         const struct GNUNET_DNSPARSER_MxRecord *mx)
593 {
594   uint16_t mxpref;
595
596   if (*off + sizeof (uint16_t) > dst_len)
597     return GNUNET_NO;
598   mxpref = htons (mx->preference);
599   memcpy (&dst[*off], &mxpref, sizeof (mxpref));
600   (*off) += sizeof (mxpref);
601   return add_name (dst, dst_len, off, mx->mxhost);
602 }
603
604
605 /**
606  * Add an SOA record to the UDP packet at the given location.
607  *
608  * @param dst where to write the SOA record
609  * @param dst_len number of bytes in dst
610  * @param off pointer to offset where to write the SOA information (increment by bytes used)
611  *            can also change if there was an error
612  * @param soa SOA information to write
613  * @return GNUNET_SYSERR if 'soa' is invalid
614  *         GNUNET_NO if 'soa' did not fit
615  *         GNUNET_OK if 'soa' was added to 'dst'
616  */
617 static int
618 add_soa (char *dst,
619          size_t dst_len,
620          size_t *off,
621          const struct GNUNET_DNSPARSER_SoaRecord *soa)
622 {
623   struct soa_data sd;
624   int ret;
625
626   if ( (GNUNET_OK != (ret = add_name (dst,
627                                       dst_len,
628                                       off,
629                                       soa->mname))) ||
630        (GNUNET_OK != (ret = add_name (dst,
631                                       dst_len,
632                                       off,
633                                       soa->rname)) ) )
634     return ret;
635   if (*off + sizeof (struct soa_data) > dst_len)
636     return GNUNET_NO;
637   sd.serial = htonl (soa->serial);
638   sd.refresh = htonl (soa->refresh);
639   sd.retry = htonl (soa->retry);
640   sd.expire = htonl (soa->expire);
641   sd.minimum = htonl (soa->minimum_ttl);
642   memcpy (&dst[*off], &sd, sizeof (sd));
643   (*off) += sizeof (sd);
644   return GNUNET_OK;
645 }
646
647
648 /**
649  * Add an SRV record to the UDP packet at the given location.
650  *
651  * @param dst where to write the SRV record
652  * @param dst_len number of bytes in dst
653  * @param off pointer to offset where to write the SRV information (increment by bytes used)
654  *            can also change if there was an error
655  * @param srv SRV information to write
656  * @return GNUNET_SYSERR if 'srv' is invalid
657  *         GNUNET_NO if 'srv' did not fit
658  *         GNUNET_OK if 'srv' was added to 'dst'
659  */
660 static int
661 add_srv (char *dst,
662          size_t dst_len,
663          size_t *off,
664          const struct GNUNET_DNSPARSER_SrvRecord *srv)
665 {
666   struct srv_data sd;
667   int ret;
668
669   if (*off + sizeof (struct srv_data) > dst_len)
670     return GNUNET_NO;
671   sd.prio = htons (srv->priority);
672   sd.weight = htons (srv->weight);
673   sd.port = htons (srv->port);
674   memcpy (&dst[*off], &sd, sizeof (sd));
675   (*off) += sizeof (sd);
676   if (GNUNET_OK != (ret = add_name (dst,
677                                     dst_len,
678                                     off,
679                                     srv->target)))
680     return ret;
681   return GNUNET_OK;
682 }
683
684
685 /**
686  * Add a DNS record to the UDP packet at the given location.
687  *
688  * @param dst where to write the query
689  * @param dst_len number of bytes in dst
690  * @param off pointer to offset where to write the query (increment by bytes used)
691  *            must not be changed if there is an error
692  * @param record record to write
693  * @return GNUNET_SYSERR if 'record' is invalid
694  *         GNUNET_NO if 'record' did not fit
695  *         GNUNET_OK if 'record' was added to 'dst'
696  */
697 static int
698 add_record (char *dst,
699             size_t dst_len,
700             size_t *off,
701             const struct GNUNET_DNSPARSER_Record *record)
702 {
703   int ret;
704   size_t start;
705   size_t pos;
706   struct record_line rl;
707   char *name;
708   
709   start = *off;
710   /* for SRV records, we can create the name from the details
711      of the record if needed */
712   name = record->name;
713   if  ( (GNUNET_DNSPARSER_TYPE_SRV == record->type) &&
714         (NULL == name) )
715     GNUNET_asprintf (&name,
716                      "_%s._%s.%s",
717                      record->data.srv->service,
718                      record->data.srv->proto,
719                      record->data.srv->domain_name);
720   ret = add_name (dst, dst_len - sizeof (struct record_line), off, name);
721   if (name != record->name)
722     GNUNET_free (name);
723   if (GNUNET_OK != ret)
724     return ret;
725   /* '*off' is now the position where we will need to write the record line */
726
727   pos = *off + sizeof (struct record_line);
728   switch (record->type)
729   { 
730   case GNUNET_DNSPARSER_TYPE_MX:
731     ret = add_mx (dst, dst_len, &pos, record->data.mx);    
732     break;
733   case GNUNET_DNSPARSER_TYPE_SOA:
734     ret = add_soa (dst, dst_len, &pos, record->data.soa);
735     break;
736   case GNUNET_DNSPARSER_TYPE_NS:
737   case GNUNET_DNSPARSER_TYPE_CNAME:
738   case GNUNET_DNSPARSER_TYPE_PTR:
739     ret = add_name (dst, dst_len, &pos, record->data.hostname);
740     break;
741   case GNUNET_DNSPARSER_TYPE_SRV:
742     ret = add_srv (dst, dst_len, &pos, record->data.srv);
743     break;
744   default:
745     if (pos + record->data.raw.data_len > dst_len)
746     {
747       ret = GNUNET_NO;
748       break;
749     }
750     memcpy (&dst[pos], record->data.raw.data, record->data.raw.data_len);
751     pos += record->data.raw.data_len;
752     ret = GNUNET_OK;
753     break;
754   }
755   if (GNUNET_OK != ret)
756   {
757     *off = start;
758     return GNUNET_NO;
759   }
760
761   if (pos - (*off + sizeof (struct record_line)) > UINT16_MAX)
762   {
763     /* record data too long */
764     *off = start;
765     return GNUNET_NO;
766   }
767   rl.type = htons (record->type);
768   rl.class = htons (record->class);
769   rl.ttl = htonl (GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value / 1000); /* in seconds */
770   rl.data_len = htons ((uint16_t) (pos - (*off + sizeof (struct record_line))));
771   memcpy (&dst[*off], &rl, sizeof (struct record_line));
772   *off = pos;
773   return GNUNET_OK;  
774 }
775
776
777 /**
778  * Given a DNS packet, generate the corresponding UDP payload.
779  * Note that we do not attempt to pack the strings with pointers
780  * as this would complicate the code and this is about being 
781  * simple and secure, not fast, fancy and broken like bind.
782  *
783  * @param p packet to pack
784  * @param max maximum allowed size for the resulting UDP payload
785  * @param buf set to a buffer with the packed message
786  * @param buf_length set to the length of buf
787  * @return GNUNET_SYSERR if 'p' is invalid
788  *         GNUNET_NO if 'p' was truncated (but there is still a result in 'buf')
789  *         GNUNET_OK if 'p' was packed completely into '*buf'
790  */
791 int
792 GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
793                        uint16_t max,
794                        char **buf,
795                        size_t *buf_length)
796 {  
797   struct GNUNET_TUN_DnsHeader dns;
798   size_t off;
799   char tmp[max];
800   unsigned int i;
801   int ret;
802   int trc;
803   
804   if ( (p->num_queries > UINT16_MAX) ||
805        (p->num_answers > UINT16_MAX) ||
806        (p->num_authority_records > UINT16_MAX) ||
807        (p->num_additional_records > UINT16_MAX) )
808     return GNUNET_SYSERR;
809   dns.id = p->id;
810   dns.flags = p->flags;
811   dns.query_count = htons (p->num_queries);
812   dns.answer_rcount = htons (p->num_answers);
813   dns.authority_rcount = htons (p->num_authority_records);
814   dns.additional_rcount = htons (p->num_additional_records);
815
816   off = sizeof (struct GNUNET_TUN_DnsHeader);
817   trc = GNUNET_NO;
818   for (i=0;i<p->num_queries;i++)
819   {
820     ret = add_query (tmp, sizeof (tmp), &off, &p->queries[i]);  
821     if (GNUNET_SYSERR == ret)
822       return GNUNET_SYSERR;
823     if (GNUNET_NO == ret)
824     {
825       dns.query_count = htons ((uint16_t) (i-1));
826       trc = GNUNET_YES;      
827       break;
828     }
829   }
830   for (i=0;i<p->num_answers;i++)
831   {
832     ret = add_record (tmp, sizeof (tmp), &off, &p->answers[i]);  
833     if (GNUNET_SYSERR == ret)
834       return GNUNET_SYSERR;
835     if (GNUNET_NO == ret)
836     {
837       dns.answer_rcount = htons ((uint16_t) (i-1));
838       trc = GNUNET_YES;      
839       break;
840     }
841   }
842   for (i=0;i<p->num_authority_records;i++)
843   {
844     ret = add_record (tmp, sizeof (tmp), &off, &p->authority_records[i]);  
845     if (GNUNET_SYSERR == ret)
846       return GNUNET_SYSERR;
847     if (GNUNET_NO == ret)
848     {
849       dns.authority_rcount = htons ((uint16_t) (i-1));
850       trc = GNUNET_YES;      
851       break;
852     }
853   }
854   for (i=0;i<p->num_additional_records;i++)
855   {
856     ret = add_record (tmp, sizeof (tmp), &off, &p->additional_records[i]);  
857     if (GNUNET_SYSERR == ret)
858       return GNUNET_SYSERR;
859     if (GNUNET_NO == ret)
860     {
861       dns.additional_rcount = htons (i-1);
862       trc = GNUNET_YES;      
863       break;
864     }
865   }
866
867   if (GNUNET_YES == trc)
868     dns.flags.message_truncated = 1;    
869   memcpy (tmp, &dns, sizeof (struct GNUNET_TUN_DnsHeader));
870
871   *buf = GNUNET_malloc (off);
872   *buf_length = off;
873   memcpy (*buf, tmp, off);
874   if (GNUNET_YES == trc)
875     return GNUNET_NO;
876   return GNUNET_OK;
877 }
878
879 /* end of dnsparser.c */