merge
[oweals/gnunet.git] / src / util / dnsparser.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2010-2014 GNUnet e.V.
4
5       GNUnet is free software: you can redistribute it and/or modify it
6       under the terms of the GNU Affero General Public License as published
7       by the Free Software Foundation, either version 3 of the License,
8       or (at your 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       Affero General Public License for more details.
14
15       You should have received a copy of the GNU Affero General Public License
16       along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 /**
20  * @file dns/dnsparser.c
21  * @brief helper library to parse DNS packets.
22  * @author Philipp Toelke
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include <idna.h>
27 #if WINDOWS
28 #include <idn-free.h>
29 #endif
30 #include "gnunet_util_lib.h"
31
32
33 /**
34  * Check if a label in UTF-8 format can be coded into valid IDNA.
35  * This can fail if the ASCII-conversion becomes longer than 63 characters.
36  *
37  * @param label label to check (UTF-8 string)
38  * @return #GNUNET_OK if the label can be converted to IDNA,
39  *         #GNUNET_SYSERR if the label is not valid for DNS names
40  */
41 int
42 GNUNET_DNSPARSER_check_label (const char *label)
43 {
44   char *output;
45   size_t slen;
46
47   if (NULL != strchr (label, '.'))
48     return GNUNET_SYSERR; /* not a label! Did you mean GNUNET_DNSPARSER_check_name? */
49   if (IDNA_SUCCESS !=
50       idna_to_ascii_8z (label, &output, IDNA_ALLOW_UNASSIGNED))
51     return GNUNET_SYSERR;
52   slen = strlen (output);
53 #if WINDOWS
54   idn_free (output);
55 #else
56   free (output);
57 #endif
58   return (slen > 63) ? GNUNET_SYSERR : GNUNET_OK;
59 }
60
61
62 /**
63  * Check if a label in UTF-8 format can be coded into valid IDNA.
64  * This can fail if the ASCII-conversion becomes longer than 253 characters.
65  *
66  * @param name name to check (UTF-8 string)
67  * @return #GNUNET_OK if the label can be converted to IDNA,
68  *         #GNUNET_SYSERR if the label is not valid for DNS names
69  */
70 int
71 GNUNET_DNSPARSER_check_name (const char *name)
72 {
73   char *ldup;
74   char *output;
75   size_t slen;
76   char *tok;
77
78   ldup = GNUNET_strdup (name);
79   for (tok = strtok (ldup, "."); NULL != tok; tok = strtok (NULL, "."))
80     if (GNUNET_OK !=
81         GNUNET_DNSPARSER_check_label (tok))
82     {
83       GNUNET_free (ldup);
84       return GNUNET_SYSERR;
85     }
86   GNUNET_free (ldup);
87   if (IDNA_SUCCESS !=
88       idna_to_ascii_8z (name, &output, IDNA_ALLOW_UNASSIGNED))
89     return GNUNET_SYSERR;
90   slen = strlen (output);
91 #if WINDOWS
92   idn_free (output);
93 #else
94   free (output);
95 #endif
96   return (slen > 253) ? GNUNET_SYSERR : GNUNET_OK;
97 }
98
99
100 /**
101  * Free SOA information record.
102  *
103  * @param soa record to free
104  */
105 void
106 GNUNET_DNSPARSER_free_soa (struct GNUNET_DNSPARSER_SoaRecord *soa)
107 {
108   if (NULL == soa)
109     return;
110   GNUNET_free_non_null (soa->mname);
111   GNUNET_free_non_null (soa->rname);
112   GNUNET_free (soa);
113 }
114
115
116 /**
117  * Free CERT information record.
118  *
119  * @param cert record to free
120  */
121 void
122 GNUNET_DNSPARSER_free_cert (struct GNUNET_DNSPARSER_CertRecord *cert)
123 {
124   if (NULL == cert)
125     return;
126   GNUNET_free_non_null (cert->certificate_data);
127   GNUNET_free (cert);
128 }
129
130
131 /**
132  * Free SRV information record.
133  *
134  * @param srv record to free
135  */
136 void
137 GNUNET_DNSPARSER_free_srv (struct GNUNET_DNSPARSER_SrvRecord *srv)
138 {
139   if (NULL == srv)
140     return;
141   GNUNET_free_non_null (srv->target);
142   GNUNET_free (srv);
143 }
144
145
146 /**
147  * Free MX information record.
148  *
149  * @param mx record to free
150  */
151 void
152 GNUNET_DNSPARSER_free_mx (struct GNUNET_DNSPARSER_MxRecord *mx)
153 {
154   if (NULL == mx)
155     return;
156   GNUNET_free_non_null (mx->mxhost);
157   GNUNET_free (mx);
158 }
159
160
161 /**
162  * Free the given DNS record.
163  *
164  * @param r record to free
165  */
166 void
167 GNUNET_DNSPARSER_free_record (struct GNUNET_DNSPARSER_Record *r)
168 {
169   GNUNET_free_non_null (r->name);
170   switch (r->type)
171   {
172   case GNUNET_DNSPARSER_TYPE_MX:
173     GNUNET_DNSPARSER_free_mx (r->data.mx);
174     break;
175   case GNUNET_DNSPARSER_TYPE_SOA:
176     GNUNET_DNSPARSER_free_soa (r->data.soa);
177     break;
178   case GNUNET_DNSPARSER_TYPE_SRV:
179     GNUNET_DNSPARSER_free_srv (r->data.srv);
180     break;
181   case GNUNET_DNSPARSER_TYPE_CERT:
182     GNUNET_DNSPARSER_free_cert (r->data.cert);
183     break;
184   case GNUNET_DNSPARSER_TYPE_NS:
185   case GNUNET_DNSPARSER_TYPE_CNAME:
186   case GNUNET_DNSPARSER_TYPE_PTR:
187     GNUNET_free_non_null (r->data.hostname);
188     break;
189   default:
190     GNUNET_free_non_null (r->data.raw.data);
191     break;
192   }
193 }
194
195
196 /**
197  * Parse name inside of a DNS query or record.
198  *
199  * @param udp_payload entire UDP payload
200  * @param udp_payload_length length of @a udp_payload
201  * @param off pointer to the offset of the name to parse in the udp_payload (to be
202  *                    incremented by the size of the name)
203  * @param depth current depth of our recursion (to prevent stack overflow)
204  * @return name as 0-terminated C string on success, NULL if the payload is malformed
205  */
206 static char *
207 parse_name (const char *udp_payload,
208             size_t udp_payload_length,
209             size_t *off,
210             unsigned int depth)
211 {
212   const uint8_t *input = (const uint8_t *) udp_payload;
213   char *ret;
214   char *tmp;
215   char *xstr;
216   uint8_t len;
217   size_t xoff;
218   char *utf8;
219   Idna_rc rc;
220
221   ret = GNUNET_strdup ("");
222   while (1)
223   {
224     if (*off >= udp_payload_length)
225     {
226       GNUNET_break_op (0);
227       goto error;
228     }
229     len = input[*off];
230     if (0 == len)
231     {
232       (*off)++;
233       break;
234     }
235     if (len < 64)
236     {
237       if (*off + 1 + len > udp_payload_length)
238       {
239         GNUNET_break_op (0);
240         goto error;
241       }
242       GNUNET_asprintf (&tmp,
243                        "%.*s",
244                        (int) len,
245                        &udp_payload[*off + 1]);
246       if (IDNA_SUCCESS !=
247           (rc = idna_to_unicode_8z8z (tmp, &utf8, IDNA_ALLOW_UNASSIGNED)))
248       {
249         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
250                     _("Failed to convert DNS IDNA name `%s' to UTF-8: %s\n"),
251                     tmp,
252                     idna_strerror (rc));
253         GNUNET_free (tmp);
254         GNUNET_asprintf (&tmp,
255                          "%s%.*s.",
256                          ret,
257                          (int) len,
258                          &udp_payload[*off + 1]);
259       }
260       else
261       {
262         GNUNET_free (tmp);
263         GNUNET_asprintf (&tmp,
264                          "%s%s.",
265                          ret,
266                          utf8);
267 #if WINDOWS
268         idn_free (utf8);
269 #else
270         free (utf8);
271 #endif
272       }
273       GNUNET_free (ret);
274       ret = tmp;
275       *off += 1 + len;
276     }
277     else if ((64 | 128) == (len & (64 | 128)) )
278     {
279       if (depth > 32)
280       {
281         GNUNET_break_op (0);
282         goto error; /* hard bound on stack to prevent "infinite" recursion, disallow! */
283       }
284       /* pointer to string */
285       if (*off + 1 > udp_payload_length)
286       {
287         GNUNET_break_op (0);
288         goto error;
289       }
290       xoff = ((len - (64 | 128)) << 8) + input[*off+1];
291       xstr = parse_name (udp_payload,
292                          udp_payload_length,
293                          &xoff,
294                          depth + 1);
295       if (NULL == xstr)
296       {
297         GNUNET_break_op (0);
298         goto error;
299       }
300       GNUNET_asprintf (&tmp,
301                        "%s%s.",
302                        ret,
303                        xstr);
304       GNUNET_free (ret);
305       GNUNET_free (xstr);
306       ret = tmp;
307       if (strlen (ret) > udp_payload_length)
308       {
309         GNUNET_break_op (0);
310         goto error; /* we are looping (building an infinite string) */
311       }
312       *off += 2;
313       /* pointers always terminate names */
314       break;
315     }
316     else
317     {
318       /* neither pointer nor inline string, not supported... */
319       GNUNET_break_op (0);
320       goto error;
321     }
322   }
323   if (0 < strlen(ret))
324     ret[strlen(ret)-1] = '\0'; /* eat tailing '.' */
325   return ret;
326  error:
327   GNUNET_break_op (0);
328   GNUNET_free (ret);
329   return NULL;
330 }
331
332
333 /**
334  * Parse name inside of a DNS query or record.
335  *
336  * @param udp_payload entire UDP payload
337  * @param udp_payload_length length of @a udp_payload
338  * @param off pointer to the offset of the name to parse in the udp_payload (to be
339  *                    incremented by the size of the name)
340  * @return name as 0-terminated C string on success, NULL if the payload is malformed
341  */
342 char *
343 GNUNET_DNSPARSER_parse_name (const char *udp_payload,
344                              size_t udp_payload_length,
345                              size_t *off)
346 {
347   return parse_name (udp_payload, udp_payload_length, off, 0);
348 }
349
350
351 /**
352  * Parse a DNS query entry.
353  *
354  * @param udp_payload entire UDP payload
355  * @param udp_payload_length length of @a udp_payload
356  * @param off pointer to the offset of the query to parse in the udp_payload (to be
357  *                    incremented by the size of the query)
358  * @param q where to write the query information
359  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the query is malformed
360  */
361 int
362 GNUNET_DNSPARSER_parse_query (const char *udp_payload,
363                               size_t udp_payload_length,
364                               size_t *off,
365                               struct GNUNET_DNSPARSER_Query *q)
366 {
367   char *name;
368   struct GNUNET_TUN_DnsQueryLine ql;
369
370   name = GNUNET_DNSPARSER_parse_name (udp_payload,
371                                       udp_payload_length,
372                                       off);
373   if (NULL == name)
374   {
375     GNUNET_break_op (0);
376     return GNUNET_SYSERR;
377   }
378   q->name = name;
379   if (*off + sizeof (struct GNUNET_TUN_DnsQueryLine) > udp_payload_length)
380   {
381     GNUNET_break_op (0);
382     return GNUNET_SYSERR;
383   }
384   GNUNET_memcpy (&ql, &udp_payload[*off], sizeof (ql));
385   *off += sizeof (ql);
386   q->type = ntohs (ql.type);
387   q->dns_traffic_class = ntohs (ql.dns_traffic_class);
388   return GNUNET_OK;
389 }
390
391
392 /**
393  * Parse a DNS SOA record.
394  *
395  * @param udp_payload reference to UDP packet
396  * @param udp_payload_length length of @a udp_payload
397  * @param off pointer to the offset of the query to parse in the SOA record (to be
398  *                    incremented by the size of the record), unchanged on error
399  * @return the parsed SOA record, NULL on error
400  */
401 struct GNUNET_DNSPARSER_SoaRecord *
402 GNUNET_DNSPARSER_parse_soa (const char *udp_payload,
403                             size_t udp_payload_length,
404                             size_t *off)
405 {
406   struct GNUNET_DNSPARSER_SoaRecord *soa;
407   struct GNUNET_TUN_DnsSoaRecord soa_bin;
408   size_t old_off;
409
410   old_off = *off;
411   soa = GNUNET_new (struct GNUNET_DNSPARSER_SoaRecord);
412   soa->mname = GNUNET_DNSPARSER_parse_name (udp_payload,
413                                             udp_payload_length,
414                                             off);
415   soa->rname = GNUNET_DNSPARSER_parse_name (udp_payload,
416                                             udp_payload_length,
417                                             off);
418   if ( (NULL == soa->mname) ||
419        (NULL == soa->rname) ||
420        (*off + sizeof (struct GNUNET_TUN_DnsSoaRecord) > udp_payload_length) )
421   {
422     GNUNET_break_op (0);
423     GNUNET_DNSPARSER_free_soa (soa);
424     *off = old_off;
425     return NULL;
426   }
427   GNUNET_memcpy (&soa_bin,
428           &udp_payload[*off],
429           sizeof (struct GNUNET_TUN_DnsSoaRecord));
430   soa->serial = ntohl (soa_bin.serial);
431   soa->refresh = ntohl (soa_bin.refresh);
432   soa->retry = ntohl (soa_bin.retry);
433   soa->expire = ntohl (soa_bin.expire);
434   soa->minimum_ttl = ntohl (soa_bin.minimum);
435   (*off) += sizeof (struct GNUNET_TUN_DnsSoaRecord);
436   return soa;
437 }
438
439
440 /**
441  * Parse a DNS MX record.
442  *
443  * @param udp_payload reference to UDP packet
444  * @param udp_payload_length length of @a udp_payload
445  * @param off pointer to the offset of the query to parse in the MX record (to be
446  *                    incremented by the size of the record), unchanged on error
447  * @return the parsed MX record, NULL on error
448  */
449 struct GNUNET_DNSPARSER_MxRecord *
450 GNUNET_DNSPARSER_parse_mx (const char *udp_payload,
451                            size_t udp_payload_length,
452                            size_t *off)
453 {
454   struct GNUNET_DNSPARSER_MxRecord *mx;
455   uint16_t mxpref;
456   size_t old_off;
457
458   old_off = *off;
459   if (*off + sizeof (uint16_t) > udp_payload_length)
460   {
461     GNUNET_break_op (0);
462     return NULL;
463   }
464   GNUNET_memcpy (&mxpref, &udp_payload[*off], sizeof (uint16_t));
465   (*off) += sizeof (uint16_t);
466   mx = GNUNET_new (struct GNUNET_DNSPARSER_MxRecord);
467   mx->preference = ntohs (mxpref);
468   mx->mxhost = GNUNET_DNSPARSER_parse_name (udp_payload,
469                                             udp_payload_length,
470                                             off);
471   if (NULL == mx->mxhost)
472   {
473     GNUNET_break_op (0);
474     GNUNET_DNSPARSER_free_mx (mx);
475     *off = old_off;
476     return NULL;
477   }
478   return mx;
479 }
480
481
482 /**
483  * Parse a DNS SRV record.
484  *
485  * @param udp_payload reference to UDP packet
486  * @param udp_payload_length length of @a udp_payload
487  * @param off pointer to the offset of the query to parse in the SRV record (to be
488  *                    incremented by the size of the record), unchanged on error
489  * @return the parsed SRV record, NULL on error
490  */
491 struct GNUNET_DNSPARSER_SrvRecord *
492 GNUNET_DNSPARSER_parse_srv (const char *udp_payload,
493                             size_t udp_payload_length,
494                             size_t *off)
495 {
496   struct GNUNET_DNSPARSER_SrvRecord *srv;
497   struct GNUNET_TUN_DnsSrvRecord srv_bin;
498   size_t old_off;
499
500   old_off = *off;
501   if (*off + sizeof (struct GNUNET_TUN_DnsSrvRecord) > udp_payload_length)
502     return NULL;
503   GNUNET_memcpy (&srv_bin,
504           &udp_payload[*off],
505           sizeof (struct GNUNET_TUN_DnsSrvRecord));
506   (*off) += sizeof (struct GNUNET_TUN_DnsSrvRecord);
507   srv = GNUNET_new (struct GNUNET_DNSPARSER_SrvRecord);
508   srv->priority = ntohs (srv_bin.prio);
509   srv->weight = ntohs (srv_bin.weight);
510   srv->port = ntohs (srv_bin.port);
511   srv->target = GNUNET_DNSPARSER_parse_name (udp_payload,
512                                              udp_payload_length,
513                                              off);
514   if (NULL == srv->target)
515   {
516     GNUNET_DNSPARSER_free_srv (srv);
517     *off = old_off;
518     return NULL;
519   }
520   return srv;
521 }
522
523
524 /**
525  * Parse a DNS CERT record.
526  *
527  * @param udp_payload reference to UDP packet
528  * @param udp_payload_length length of @a udp_payload
529  * @param off pointer to the offset of the query to parse in the CERT record (to be
530  *                    incremented by the size of the record), unchanged on error
531  * @return the parsed CERT record, NULL on error
532  */
533 struct GNUNET_DNSPARSER_CertRecord *
534 GNUNET_DNSPARSER_parse_cert (const char *udp_payload,
535                              size_t udp_payload_length,
536                              size_t *off)
537 {
538   struct GNUNET_DNSPARSER_CertRecord *cert;
539   struct GNUNET_TUN_DnsCertRecord dcert;
540
541   if (*off + sizeof (struct GNUNET_TUN_DnsCertRecord) >= udp_payload_length)
542   {
543     GNUNET_break_op (0);
544     return NULL;
545   }
546   GNUNET_memcpy (&dcert,
547                  &udp_payload[*off],
548                  sizeof (struct GNUNET_TUN_DnsCertRecord));
549   (*off) += sizeof (struct GNUNET_TUN_DnsCertRecord);
550   cert = GNUNET_new (struct GNUNET_DNSPARSER_CertRecord);
551   cert->cert_type = ntohs (dcert.cert_type);
552   cert->cert_tag = ntohs (dcert.cert_tag);
553   cert->algorithm = dcert.algorithm;
554   cert->certificate_size = udp_payload_length - (*off);
555   cert->certificate_data = GNUNET_malloc (cert->certificate_size);
556   GNUNET_memcpy (cert->certificate_data,
557                  &udp_payload[*off],
558                  cert->certificate_size);
559   (*off) += cert->certificate_size;
560   return cert;
561 }
562
563
564 /**
565  * Parse a DNS record entry.
566  *
567  * @param udp_payload entire UDP payload
568  * @param udp_payload_length length of @a udp_payload
569  * @param off pointer to the offset of the record to parse in the udp_payload (to be
570  *                    incremented by the size of the record)
571  * @param r where to write the record information
572  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the record is malformed
573  */
574 int
575 GNUNET_DNSPARSER_parse_record (const char *udp_payload,
576                                size_t udp_payload_length,
577                                size_t *off,
578                                struct GNUNET_DNSPARSER_Record *r)
579 {
580   char *name;
581   struct GNUNET_TUN_DnsRecordLine rl;
582   size_t old_off;
583   uint16_t data_len;
584
585   name = GNUNET_DNSPARSER_parse_name (udp_payload,
586                                       udp_payload_length,
587                                       off);
588   if (NULL == name)
589   {
590     GNUNET_break_op (0);
591     return GNUNET_SYSERR;
592   }
593   r->name = name;
594   if (*off + sizeof (struct GNUNET_TUN_DnsRecordLine) > udp_payload_length)
595   {
596     GNUNET_break_op (0);
597     return GNUNET_SYSERR;
598   }
599   GNUNET_memcpy (&rl, &udp_payload[*off], sizeof (rl));
600   (*off) += sizeof (rl);
601   r->type = ntohs (rl.type);
602   r->dns_traffic_class = ntohs (rl.dns_traffic_class);
603   r->expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
604                                                                                         ntohl (rl.ttl)));
605   data_len = ntohs (rl.data_len);
606   if (*off + data_len > udp_payload_length)
607   {
608     GNUNET_break_op (0);
609     return GNUNET_SYSERR;
610   }
611   old_off = *off;
612   switch (r->type)
613   {
614   case GNUNET_DNSPARSER_TYPE_NS:
615   case GNUNET_DNSPARSER_TYPE_CNAME:
616   case GNUNET_DNSPARSER_TYPE_DNAME:
617   case GNUNET_DNSPARSER_TYPE_PTR:
618     r->data.hostname = GNUNET_DNSPARSER_parse_name (udp_payload,
619                                                     udp_payload_length,
620                                                     off);
621     if ( (NULL == r->data.hostname) ||
622          (old_off + data_len != *off) )
623       return GNUNET_SYSERR;
624     return GNUNET_OK;
625   case GNUNET_DNSPARSER_TYPE_SOA:
626     r->data.soa = GNUNET_DNSPARSER_parse_soa (udp_payload,
627                                               udp_payload_length,
628                                               off);
629     if ( (NULL == r->data.soa) ||
630          (old_off + data_len != *off) )
631     {
632       GNUNET_break_op (0);
633       return GNUNET_SYSERR;
634     }
635     return GNUNET_OK;
636   case GNUNET_DNSPARSER_TYPE_MX:
637     r->data.mx = GNUNET_DNSPARSER_parse_mx (udp_payload,
638                                             udp_payload_length,
639                                             off);
640     if ( (NULL == r->data.mx) ||
641          (old_off + data_len != *off) )
642     {
643       GNUNET_break_op (0);
644       return GNUNET_SYSERR;
645     }
646     return GNUNET_OK;
647   case GNUNET_DNSPARSER_TYPE_SRV:
648     r->data.srv = GNUNET_DNSPARSER_parse_srv (udp_payload,
649                                               udp_payload_length,
650                                               off);
651     if ( (NULL == r->data.srv) ||
652          (old_off + data_len != *off) )
653     {
654       GNUNET_break_op (0);
655       return GNUNET_SYSERR;
656     }
657     return GNUNET_OK;
658   default:
659     r->data.raw.data = GNUNET_malloc (data_len);
660     r->data.raw.data_len = data_len;
661     GNUNET_memcpy (r->data.raw.data,
662                    &udp_payload[*off],
663                    data_len);
664     break;
665   }
666   (*off) += data_len;
667   return GNUNET_OK;
668 }
669
670
671 /**
672  * Parse a UDP payload of a DNS packet in to a nice struct for further
673  * processing and manipulation.
674  *
675  * @param udp_payload wire-format of the DNS packet
676  * @param udp_payload_length number of bytes in @a udp_payload
677  * @return NULL on error, otherwise the parsed packet
678  */
679 struct GNUNET_DNSPARSER_Packet *
680 GNUNET_DNSPARSER_parse (const char *udp_payload,
681                         size_t udp_payload_length)
682 {
683   struct GNUNET_DNSPARSER_Packet *p;
684   const struct GNUNET_TUN_DnsHeader *dns;
685   size_t off;
686   unsigned int n;
687
688   if (udp_payload_length < sizeof (struct GNUNET_TUN_DnsHeader))
689     return NULL;
690   dns = (const struct GNUNET_TUN_DnsHeader *) udp_payload;
691   off = sizeof (struct GNUNET_TUN_DnsHeader);
692   p = GNUNET_new (struct GNUNET_DNSPARSER_Packet);
693   p->flags = dns->flags;
694   p->id = dns->id;
695   n = ntohs (dns->query_count);
696   if (n > 0)
697   {
698     p->queries = GNUNET_new_array (n,
699                                    struct GNUNET_DNSPARSER_Query);
700     p->num_queries = n;
701     for (unsigned int i=0;i<n;i++)
702       if (GNUNET_OK !=
703           GNUNET_DNSPARSER_parse_query (udp_payload,
704                                         udp_payload_length,
705                                         &off,
706                                         &p->queries[i]))
707         goto error;
708   }
709   n = ntohs (dns->answer_rcount);
710   if (n > 0)
711   {
712     p->answers = GNUNET_new_array (n,
713                                    struct GNUNET_DNSPARSER_Record);
714     p->num_answers = n;
715     for (unsigned int i=0;i<n;i++)
716       if (GNUNET_OK !=
717           GNUNET_DNSPARSER_parse_record (udp_payload,
718                                          udp_payload_length,
719                                          &off,
720                                          &p->answers[i]))
721         goto error;
722   }
723   n = ntohs (dns->authority_rcount);
724   if (n > 0)
725   {
726     p->authority_records = GNUNET_new_array (n,
727                                              struct GNUNET_DNSPARSER_Record);
728     p->num_authority_records = n;
729     for (unsigned int i=0;i<n;i++)
730       if (GNUNET_OK !=
731           GNUNET_DNSPARSER_parse_record (udp_payload,
732                                          udp_payload_length,
733                                          &off,
734                                          &p->authority_records[i]))
735         goto error;
736   }
737   n = ntohs (dns->additional_rcount);
738   if (n > 0)
739   {
740     p->additional_records = GNUNET_new_array (n,
741                                               struct GNUNET_DNSPARSER_Record);
742     p->num_additional_records = n;
743     for (unsigned int i=0;i<n;i++)
744     {
745       if (GNUNET_OK !=
746           GNUNET_DNSPARSER_parse_record (udp_payload,
747                                          udp_payload_length,
748                                          &off,
749                                          &p->additional_records[i]))
750         goto error;
751     }
752   }
753   return p;
754  error:
755   GNUNET_break_op (0);
756   GNUNET_DNSPARSER_free_packet (p);
757   return NULL;
758 }
759
760
761 /**
762  * Duplicate (deep-copy) the given DNS record
763  *
764  * @param r the record
765  * @return the newly allocated record
766  */
767 struct GNUNET_DNSPARSER_Record *
768 GNUNET_DNSPARSER_duplicate_record (const struct GNUNET_DNSPARSER_Record *r)
769 {
770   struct GNUNET_DNSPARSER_Record *dup = GNUNET_memdup (r, sizeof (*r));
771
772   dup->name = GNUNET_strdup (r->name);
773   switch (r->type)
774   {
775     case GNUNET_DNSPARSER_TYPE_NS:
776     case GNUNET_DNSPARSER_TYPE_CNAME:
777     case GNUNET_DNSPARSER_TYPE_PTR:
778     {
779       dup->data.hostname = GNUNET_strdup (r->data.hostname);
780       break;
781     }
782     case GNUNET_DNSPARSER_TYPE_SOA:
783     {
784       dup->data.soa = GNUNET_DNSPARSER_duplicate_soa_record (r->data.soa);
785       break;
786     }
787     case GNUNET_DNSPARSER_TYPE_CERT:
788     {
789       dup->data.cert = GNUNET_DNSPARSER_duplicate_cert_record (r->data.cert);
790       break;
791     }
792     case GNUNET_DNSPARSER_TYPE_MX:
793     {
794       dup->data.mx = GNUNET_DNSPARSER_duplicate_mx_record (r->data.mx);
795       break;
796     }
797     case GNUNET_DNSPARSER_TYPE_SRV:
798     {
799       dup->data.srv = GNUNET_DNSPARSER_duplicate_srv_record (r->data.srv);
800       break;
801     }
802     default:
803     {
804       dup->data.raw.data = GNUNET_memdup (r->data.raw.data,
805                                           r->data.raw.data_len);
806     }
807   }
808   return dup;
809 }
810
811
812 /**
813  * Duplicate (deep-copy) the given DNS record
814  *
815  * @param r the record
816  * @return the newly allocated record
817  */
818 struct GNUNET_DNSPARSER_SoaRecord *
819 GNUNET_DNSPARSER_duplicate_soa_record (const struct GNUNET_DNSPARSER_SoaRecord *r)
820 {
821   struct GNUNET_DNSPARSER_SoaRecord *dup = GNUNET_memdup (r, sizeof (*r));
822
823   dup->mname = GNUNET_strdup (r->mname);
824   dup->rname = GNUNET_strdup (r->rname);
825   return dup;
826 }
827
828
829 /**
830  * Duplicate (deep-copy) the given DNS record
831  *
832  * @param r the record
833  * @return the newly allocated record
834  */
835 struct GNUNET_DNSPARSER_CertRecord *
836 GNUNET_DNSPARSER_duplicate_cert_record (const struct GNUNET_DNSPARSER_CertRecord *r)
837 {
838   struct GNUNET_DNSPARSER_CertRecord *dup = GNUNET_memdup (r, sizeof (*r));
839
840   dup->certificate_data = GNUNET_strdup (r->certificate_data);
841   return dup;
842 }
843
844
845 /**
846  * Duplicate (deep-copy) the given DNS record
847  *
848  * @param r the record
849  * @return the newly allocated record
850  */
851 struct GNUNET_DNSPARSER_MxRecord *
852 GNUNET_DNSPARSER_duplicate_mx_record (const struct GNUNET_DNSPARSER_MxRecord *r)
853 {
854   struct GNUNET_DNSPARSER_MxRecord *dup = GNUNET_memdup (r, sizeof (*r));
855
856   dup->mxhost = GNUNET_strdup (r->mxhost);
857   return dup;
858 }
859
860
861 /**
862  * Duplicate (deep-copy) the given DNS record
863  *
864  * @param r the record
865  * @return the newly allocated record
866  */
867 struct GNUNET_DNSPARSER_SrvRecord *
868 GNUNET_DNSPARSER_duplicate_srv_record (const struct GNUNET_DNSPARSER_SrvRecord *r)
869 {
870   struct GNUNET_DNSPARSER_SrvRecord *dup = GNUNET_memdup (r, sizeof (*r));
871
872   dup->target = GNUNET_strdup (r->target);
873   return dup;
874 }
875
876
877 /**
878  * Free memory taken by a packet.
879  *
880  * @param p packet to free
881  */
882 void
883 GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
884 {
885   for (unsigned int i=0;i<p->num_queries;i++)
886     GNUNET_free_non_null (p->queries[i].name);
887   GNUNET_free_non_null (p->queries);
888   for (unsigned int i=0;i<p->num_answers;i++)
889     GNUNET_DNSPARSER_free_record (&p->answers[i]);
890   GNUNET_free_non_null (p->answers);
891   for (unsigned int i=0;i<p->num_authority_records;i++)
892     GNUNET_DNSPARSER_free_record (&p->authority_records[i]);
893   GNUNET_free_non_null (p->authority_records);
894   for (unsigned int i=0;i<p->num_additional_records;i++)
895     GNUNET_DNSPARSER_free_record (&p->additional_records[i]);
896   GNUNET_free_non_null (p->additional_records);
897   GNUNET_free (p);
898 }
899
900
901 /* ********************** DNS packet assembly code **************** */
902
903
904 /**
905  * Add a DNS name to the UDP packet at the given location, converting
906  * the name to IDNA notation as necessary.
907  *
908  * @param dst where to write the name (UDP packet)
909  * @param dst_len number of bytes in @a dst
910  * @param off pointer to offset where to write the name (increment by bytes used)
911  *            must not be changed if there is an error
912  * @param name name to write
913  * @return #GNUNET_SYSERR if @a name is invalid
914  *         #GNUNET_NO if @a name did not fit
915  *         #GNUNET_OK if @a name was added to @a dst
916  */
917 int
918 GNUNET_DNSPARSER_builder_add_name (char *dst,
919                                    size_t dst_len,
920                                    size_t *off,
921                                    const char *name)
922 {
923   const char *dot;
924   const char *idna_name;
925   char *idna_start;
926   size_t start;
927   size_t pos;
928   size_t len;
929   Idna_rc rc;
930
931   if (NULL == name)
932     return GNUNET_SYSERR;
933
934   if (IDNA_SUCCESS !=
935       (rc = idna_to_ascii_8z (name,
936                               &idna_start,
937                               IDNA_ALLOW_UNASSIGNED)))
938   {
939     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
940                 _("Failed to convert UTF-8 name `%s' to DNS IDNA format: %s\n"),
941                 name,
942                 idna_strerror (rc));
943     return GNUNET_NO;
944   }
945   idna_name = idna_start;
946   start = *off;
947   if (start + strlen (idna_name) + 2 > dst_len)
948     goto fail;
949   pos = start;
950   do
951   {
952     dot = strchr (idna_name, '.');
953     if (NULL == dot)
954       len = strlen (idna_name);
955     else
956       len = dot - idna_name;
957     if ( (len >= 64) || (0 == len) )
958     {
959       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
960                   "Invalid DNS name `%s': label with %u characters encountered\n",
961                   name,
962                   (unsigned int) len);
963       goto fail; /* label too long or empty */
964     }
965     dst[pos++] = (char) (uint8_t) len;
966     GNUNET_memcpy (&dst[pos],
967                    idna_name,
968                    len);
969     pos += len;
970     idna_name += len + 1; /* also skip dot */
971   }
972   while (NULL != dot);
973   dst[pos++] = '\0'; /* terminator */
974   *off = pos;
975 #if WINDOWS
976   idn_free (idna_start);
977 #else
978   free (idna_start);
979 #endif
980   return GNUNET_OK;
981  fail:
982 #if WINDOWS
983   idn_free (idna_start);
984 #else
985   free (idna_start);
986 #endif
987   return GNUNET_NO;
988 }
989
990
991 /**
992  * Add a DNS query to the UDP packet at the given location.
993  *
994  * @param dst where to write the query
995  * @param dst_len number of bytes in @a dst
996  * @param off pointer to offset where to write the query (increment by bytes used)
997  *            must not be changed if there is an error
998  * @param query query to write
999  * @return #GNUNET_SYSERR if @a query is invalid
1000  *         #GNUNET_NO if @a query did not fit
1001  *         #GNUNET_OK if @a query was added to @a dst
1002  */
1003 int
1004 GNUNET_DNSPARSER_builder_add_query (char *dst,
1005                                     size_t dst_len,
1006                                     size_t *off,
1007                                     const struct GNUNET_DNSPARSER_Query *query)
1008 {
1009   int ret;
1010   struct GNUNET_TUN_DnsQueryLine ql;
1011
1012   ret = GNUNET_DNSPARSER_builder_add_name (dst, dst_len - sizeof (struct GNUNET_TUN_DnsQueryLine), off, query->name);
1013   if (ret != GNUNET_OK)
1014     return ret;
1015   ql.type = htons (query->type);
1016   ql.dns_traffic_class = htons (query->dns_traffic_class);
1017   GNUNET_memcpy (&dst[*off], &ql, sizeof (ql));
1018   (*off) += sizeof (ql);
1019   return GNUNET_OK;
1020 }
1021
1022
1023 /**
1024  * Add an MX record to the UDP packet at the given location.
1025  *
1026  * @param dst where to write the mx record
1027  * @param dst_len number of bytes in @a dst
1028  * @param off pointer to offset where to write the mx information (increment by bytes used);
1029  *            can also change if there was an error
1030  * @param mx mx information to write
1031  * @return #GNUNET_SYSERR if @a mx is invalid
1032  *         #GNUNET_NO if @a mx did not fit
1033  *         #GNUNET_OK if @a mx was added to @a dst
1034  */
1035 int
1036 GNUNET_DNSPARSER_builder_add_mx (char *dst,
1037                                  size_t dst_len,
1038                                  size_t *off,
1039                                  const struct GNUNET_DNSPARSER_MxRecord *mx)
1040 {
1041   uint16_t mxpref;
1042
1043   if (*off + sizeof (uint16_t) > dst_len)
1044     return GNUNET_NO;
1045   mxpref = htons (mx->preference);
1046   GNUNET_memcpy (&dst[*off],
1047                  &mxpref,
1048                  sizeof (mxpref));
1049   (*off) += sizeof (mxpref);
1050   return GNUNET_DNSPARSER_builder_add_name (dst,
1051                                             dst_len,
1052                                             off,
1053                                             mx->mxhost);
1054 }
1055
1056
1057 /**
1058  * Add a CERT record to the UDP packet at the given location.
1059  *
1060  * @param dst where to write the CERT record
1061  * @param dst_len number of bytes in @a dst
1062  * @param off pointer to offset where to write the CERT information (increment by bytes used);
1063  *            can also change if there was an error
1064  * @param cert CERT information to write
1065  * @return #GNUNET_SYSERR if @a cert is invalid
1066  *         #GNUNET_NO if @a cert did not fit
1067  *         #GNUNET_OK if @a cert was added to @a dst
1068  */
1069 int
1070 GNUNET_DNSPARSER_builder_add_cert (char *dst,
1071                                    size_t dst_len,
1072                                    size_t *off,
1073                                    const struct GNUNET_DNSPARSER_CertRecord *cert)
1074 {
1075   struct GNUNET_TUN_DnsCertRecord dcert;
1076
1077   if ( (cert->cert_type > UINT16_MAX) ||
1078        (cert->algorithm > UINT8_MAX) )
1079   {
1080     GNUNET_break (0);
1081     return GNUNET_SYSERR;
1082   }
1083   if (*off + sizeof (struct GNUNET_TUN_DnsCertRecord) + cert->certificate_size > dst_len)
1084     return GNUNET_NO;
1085   dcert.cert_type = htons ((uint16_t) cert->cert_type);
1086   dcert.cert_tag = htons ((uint16_t) cert->cert_tag);
1087   dcert.algorithm = (uint8_t) cert->algorithm;
1088   GNUNET_memcpy (&dst[*off], &dcert, sizeof (dcert));
1089   (*off) += sizeof (dcert);
1090   GNUNET_memcpy (&dst[*off], cert->certificate_data, cert->certificate_size);
1091   (*off) += cert->certificate_size;
1092   return GNUNET_OK;
1093 }
1094
1095
1096 /**
1097  * Add an SOA record to the UDP packet at the given location.
1098  *
1099  * @param dst where to write the SOA record
1100  * @param dst_len number of bytes in @a dst
1101  * @param off pointer to offset where to write the SOA information (increment by bytes used)
1102  *            can also change if there was an error
1103  * @param soa SOA information to write
1104  * @return #GNUNET_SYSERR if @a soa is invalid
1105  *         #GNUNET_NO if @a soa did not fit
1106  *         #GNUNET_OK if @a soa was added to @a dst
1107  */
1108 int
1109 GNUNET_DNSPARSER_builder_add_soa (char *dst,
1110                                   size_t dst_len,
1111                                   size_t *off,
1112                                   const struct GNUNET_DNSPARSER_SoaRecord *soa)
1113 {
1114   struct GNUNET_TUN_DnsSoaRecord sd;
1115   int ret;
1116
1117   if ( (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
1118                                                                dst_len,
1119                                                                off,
1120                                                                soa->mname))) ||
1121        (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
1122                                                                dst_len,
1123                                                                off,
1124                                                                soa->rname)) ) )
1125     return ret;
1126   if (*off + sizeof (struct GNUNET_TUN_DnsSoaRecord) > dst_len)
1127     return GNUNET_NO;
1128   sd.serial = htonl (soa->serial);
1129   sd.refresh = htonl (soa->refresh);
1130   sd.retry = htonl (soa->retry);
1131   sd.expire = htonl (soa->expire);
1132   sd.minimum = htonl (soa->minimum_ttl);
1133   GNUNET_memcpy (&dst[*off], &sd, sizeof (sd));
1134   (*off) += sizeof (sd);
1135   return GNUNET_OK;
1136 }
1137
1138
1139 /**
1140  * Add an SRV record to the UDP packet at the given location.
1141  *
1142  * @param dst where to write the SRV record
1143  * @param dst_len number of bytes in @a dst
1144  * @param off pointer to offset where to write the SRV information (increment by bytes used)
1145  *            can also change if there was an error
1146  * @param srv SRV information to write
1147  * @return #GNUNET_SYSERR if @a srv is invalid
1148  *         #GNUNET_NO if @a srv did not fit
1149  *         #GNUNET_OK if @a srv was added to @a dst
1150  */
1151 int
1152 GNUNET_DNSPARSER_builder_add_srv (char *dst,
1153                                   size_t dst_len,
1154                                   size_t *off,
1155                                   const struct GNUNET_DNSPARSER_SrvRecord *srv)
1156 {
1157   struct GNUNET_TUN_DnsSrvRecord sd;
1158   int ret;
1159
1160   if (*off + sizeof (struct GNUNET_TUN_DnsSrvRecord) > dst_len)
1161     return GNUNET_NO;
1162   sd.prio = htons (srv->priority);
1163   sd.weight = htons (srv->weight);
1164   sd.port = htons (srv->port);
1165   GNUNET_memcpy (&dst[*off],
1166                  &sd,
1167                  sizeof (sd));
1168   (*off) += sizeof (sd);
1169   if (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
1170                                                              dst_len,
1171                                                              off,
1172                                                              srv->target)))
1173     return ret;
1174   return GNUNET_OK;
1175 }
1176
1177
1178 /**
1179  * Add a DNS record to the UDP packet at the given location.
1180  *
1181  * @param dst where to write the query
1182  * @param dst_len number of bytes in @a dst
1183  * @param off pointer to offset where to write the query (increment by bytes used)
1184  *            must not be changed if there is an error
1185  * @param record record to write
1186  * @return #GNUNET_SYSERR if @a record is invalid
1187  *         #GNUNET_NO if @a record did not fit
1188  *         #GNUNET_OK if @a record was added to @a dst
1189  */
1190 static int
1191 add_record (char *dst,
1192             size_t dst_len,
1193             size_t *off,
1194             const struct GNUNET_DNSPARSER_Record *record)
1195 {
1196   int ret;
1197   size_t start;
1198   size_t pos;
1199   struct GNUNET_TUN_DnsRecordLine rl;
1200
1201   start = *off;
1202   ret = GNUNET_DNSPARSER_builder_add_name (dst,
1203                                            dst_len - sizeof (struct GNUNET_TUN_DnsRecordLine),
1204                                            off,
1205                                            record->name);
1206   if (GNUNET_OK != ret)
1207     return ret;
1208   /* '*off' is now the position where we will need to write the record line */
1209
1210   pos = *off + sizeof (struct GNUNET_TUN_DnsRecordLine);
1211   switch (record->type)
1212   {
1213   case GNUNET_DNSPARSER_TYPE_MX:
1214     ret = GNUNET_DNSPARSER_builder_add_mx (dst,
1215                                            dst_len,
1216                                            &pos,
1217                                            record->data.mx);
1218     break;
1219   case GNUNET_DNSPARSER_TYPE_CERT:
1220     ret = GNUNET_DNSPARSER_builder_add_cert (dst,
1221                                              dst_len,
1222                                              &pos,
1223                                              record->data.cert);
1224     break;
1225   case GNUNET_DNSPARSER_TYPE_SOA:
1226     ret = GNUNET_DNSPARSER_builder_add_soa (dst,
1227                                             dst_len,
1228                                             &pos,
1229                                             record->data.soa);
1230     break;
1231   case GNUNET_DNSPARSER_TYPE_NS:
1232   case GNUNET_DNSPARSER_TYPE_CNAME:
1233   case GNUNET_DNSPARSER_TYPE_PTR:
1234     ret = GNUNET_DNSPARSER_builder_add_name (dst,
1235                                              dst_len,
1236                                              &pos,
1237                                              record->data.hostname);
1238     break;
1239   case GNUNET_DNSPARSER_TYPE_SRV:
1240     ret = GNUNET_DNSPARSER_builder_add_srv (dst,
1241                                             dst_len,
1242                                             &pos,
1243                                             record->data.srv);
1244     break;
1245   default:
1246     if (pos + record->data.raw.data_len > dst_len)
1247     {
1248       ret = GNUNET_NO;
1249       break;
1250     }
1251     GNUNET_memcpy (&dst[pos],
1252                    record->data.raw.data,
1253                    record->data.raw.data_len);
1254     pos += record->data.raw.data_len;
1255     ret = GNUNET_OK;
1256     break;
1257   }
1258   if (GNUNET_OK != ret)
1259   {
1260     *off = start;
1261     return GNUNET_NO;
1262   }
1263
1264   if (pos - (*off + sizeof (struct GNUNET_TUN_DnsRecordLine)) > UINT16_MAX)
1265   {
1266     /* record data too long */
1267     *off = start;
1268     return GNUNET_NO;
1269   }
1270   rl.type = htons (record->type);
1271   rl.dns_traffic_class = htons (record->dns_traffic_class);
1272   rl.ttl = htonl (GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value_us / 1000LL / 1000LL); /* in seconds */
1273   rl.data_len = htons ((uint16_t) (pos - (*off + sizeof (struct GNUNET_TUN_DnsRecordLine))));
1274   GNUNET_memcpy (&dst[*off],
1275                  &rl,
1276                  sizeof (struct GNUNET_TUN_DnsRecordLine));
1277   *off = pos;
1278   return GNUNET_OK;
1279 }
1280
1281
1282 /**
1283  * Given a DNS packet @a p, generate the corresponding UDP payload.
1284  * Note that we do not attempt to pack the strings with pointers
1285  * as this would complicate the code and this is about being
1286  * simple and secure, not fast, fancy and broken like bind.
1287  *
1288  * @param p packet to pack
1289  * @param max maximum allowed size for the resulting UDP payload
1290  * @param buf set to a buffer with the packed message
1291  * @param buf_length set to the length of @a buf
1292  * @return #GNUNET_SYSERR if @a p is invalid
1293  *         #GNUNET_NO if @a p was truncated (but there is still a result in @a buf)
1294  *         #GNUNET_OK if @a p was packed completely into @a buf
1295  */
1296 int
1297 GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
1298                        uint16_t max,
1299                        char **buf,
1300                        size_t *buf_length)
1301 {
1302   struct GNUNET_TUN_DnsHeader dns;
1303   size_t off;
1304   char tmp[max];
1305   int ret;
1306   int trc;
1307
1308   if ( (p->num_queries > UINT16_MAX) ||
1309        (p->num_answers > UINT16_MAX) ||
1310        (p->num_authority_records > UINT16_MAX) ||
1311        (p->num_additional_records > UINT16_MAX) )
1312     return GNUNET_SYSERR;
1313   dns.id = p->id;
1314   dns.flags = p->flags;
1315   dns.query_count = htons (p->num_queries);
1316   dns.answer_rcount = htons (p->num_answers);
1317   dns.authority_rcount = htons (p->num_authority_records);
1318   dns.additional_rcount = htons (p->num_additional_records);
1319
1320   off = sizeof (struct GNUNET_TUN_DnsHeader);
1321   trc = GNUNET_NO;
1322   for (unsigned int i=0;i<p->num_queries;i++)
1323   {
1324     ret = GNUNET_DNSPARSER_builder_add_query (tmp,
1325                                               sizeof (tmp),
1326                                               &off,
1327                                               &p->queries[i]);
1328     if (GNUNET_SYSERR == ret)
1329       return GNUNET_SYSERR;
1330     if (GNUNET_NO == ret)
1331     {
1332       dns.query_count = htons ((uint16_t) (i-1));
1333       trc = GNUNET_YES;
1334       break;
1335     }
1336   }
1337   for (unsigned int i=0;i<p->num_answers;i++)
1338   {
1339     ret = add_record (tmp,
1340                       sizeof (tmp),
1341                       &off,
1342                       &p->answers[i]);
1343     if (GNUNET_SYSERR == ret)
1344       return GNUNET_SYSERR;
1345     if (GNUNET_NO == ret)
1346     {
1347       dns.answer_rcount = htons ((uint16_t) (i-1));
1348       trc = GNUNET_YES;
1349       break;
1350     }
1351   }
1352   for (unsigned int i=0;i<p->num_authority_records;i++)
1353   {
1354     ret = add_record (tmp,
1355                       sizeof (tmp),
1356                       &off,
1357                       &p->authority_records[i]);
1358     if (GNUNET_SYSERR == ret)
1359       return GNUNET_SYSERR;
1360     if (GNUNET_NO == ret)
1361     {
1362       dns.authority_rcount = htons ((uint16_t) (i-1));
1363       trc = GNUNET_YES;
1364       break;
1365     }
1366   }
1367   for (unsigned int i=0;i<p->num_additional_records;i++)
1368   {
1369     ret = add_record (tmp,
1370                       sizeof (tmp),
1371                       &off,
1372                       &p->additional_records[i]);
1373     if (GNUNET_SYSERR == ret)
1374       return GNUNET_SYSERR;
1375     if (GNUNET_NO == ret)
1376     {
1377       dns.additional_rcount = htons (i-1);
1378       trc = GNUNET_YES;
1379       break;
1380     }
1381   }
1382
1383   if (GNUNET_YES == trc)
1384     dns.flags.message_truncated = 1;
1385   GNUNET_memcpy (tmp,
1386                  &dns,
1387                  sizeof (struct GNUNET_TUN_DnsHeader));
1388
1389   *buf = GNUNET_malloc (off);
1390   *buf_length = off;
1391   GNUNET_memcpy (*buf,
1392                  tmp,
1393                  off);
1394   if (GNUNET_YES == trc)
1395     return GNUNET_NO;
1396   return GNUNET_OK;
1397 }
1398
1399
1400 /**
1401  * Convert a block of binary data to HEX.
1402  *
1403  * @param data binary data to convert
1404  * @param data_size number of bytes in @a data
1405  * @return HEX string (lower case)
1406  */
1407 char *
1408 GNUNET_DNSPARSER_bin_to_hex (const void *data,
1409                              size_t data_size)
1410 {
1411   char *ret;
1412   size_t off;
1413   const uint8_t *idata;
1414
1415   idata = data;
1416   ret = GNUNET_malloc (data_size * 2 + 1);
1417   for (off = 0; off < data_size; off++)
1418     sprintf (&ret[off * 2],
1419              "%02x",
1420              idata[off]);
1421   return ret;
1422 }
1423
1424
1425 /**
1426  * Convert a HEX string to block of binary data.
1427  *
1428  * @param hex HEX string to convert (may contain mixed case)
1429  * @param data where to write result, must be
1430  *             at least `strlen(hex)/2` bytes long
1431  * @return number of bytes written to data
1432  */
1433 size_t
1434 GNUNET_DNSPARSER_hex_to_bin (const char *hex,
1435                              void *data)
1436 {
1437   size_t data_size;
1438   size_t off;
1439   uint8_t *idata;
1440   unsigned int h;
1441   char in[3];
1442
1443   data_size = strlen (hex) / 2;
1444   idata = data;
1445   in[2] = '\0';
1446   for (off = 0; off < data_size; off++)
1447   {
1448     in[0] = tolower ((unsigned char) hex[off * 2]);
1449     in[1] = tolower ((unsigned char) hex[off * 2 + 1]);
1450     if (1 != sscanf (in, "%x", &h))
1451       return off;
1452     idata[off] = (uint8_t) h;
1453   }
1454   return off;
1455 }
1456
1457
1458 /* end of dnsparser.c */