introduce API functions for duplicating the DNSPARSER record types
[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_break (0);
960       goto fail; /* segment too long or empty */
961     }
962     dst[pos++] = (char) (uint8_t) len;
963     GNUNET_memcpy (&dst[pos],
964                    idna_name,
965                    len);
966     pos += len;
967     idna_name += len + 1; /* also skip dot */
968   }
969   while (NULL != dot);
970   dst[pos++] = '\0'; /* terminator */
971   *off = pos;
972 #if WINDOWS
973   idn_free (idna_start);
974 #else
975   free (idna_start);
976 #endif
977   return GNUNET_OK;
978  fail:
979 #if WINDOWS
980   idn_free (idna_start);
981 #else
982   free (idna_start);
983 #endif
984   return GNUNET_NO;
985 }
986
987
988 /**
989  * Add a DNS query to the UDP packet at the given location.
990  *
991  * @param dst where to write the query
992  * @param dst_len number of bytes in @a dst
993  * @param off pointer to offset where to write the query (increment by bytes used)
994  *            must not be changed if there is an error
995  * @param query query to write
996  * @return #GNUNET_SYSERR if @a query is invalid
997  *         #GNUNET_NO if @a query did not fit
998  *         #GNUNET_OK if @a query was added to @a dst
999  */
1000 int
1001 GNUNET_DNSPARSER_builder_add_query (char *dst,
1002                                     size_t dst_len,
1003                                     size_t *off,
1004                                     const struct GNUNET_DNSPARSER_Query *query)
1005 {
1006   int ret;
1007   struct GNUNET_TUN_DnsQueryLine ql;
1008
1009   ret = GNUNET_DNSPARSER_builder_add_name (dst, dst_len - sizeof (struct GNUNET_TUN_DnsQueryLine), off, query->name);
1010   if (ret != GNUNET_OK)
1011     return ret;
1012   ql.type = htons (query->type);
1013   ql.dns_traffic_class = htons (query->dns_traffic_class);
1014   GNUNET_memcpy (&dst[*off], &ql, sizeof (ql));
1015   (*off) += sizeof (ql);
1016   return GNUNET_OK;
1017 }
1018
1019
1020 /**
1021  * Add an MX record to the UDP packet at the given location.
1022  *
1023  * @param dst where to write the mx record
1024  * @param dst_len number of bytes in @a dst
1025  * @param off pointer to offset where to write the mx information (increment by bytes used);
1026  *            can also change if there was an error
1027  * @param mx mx information to write
1028  * @return #GNUNET_SYSERR if @a mx is invalid
1029  *         #GNUNET_NO if @a mx did not fit
1030  *         #GNUNET_OK if @a mx was added to @a dst
1031  */
1032 int
1033 GNUNET_DNSPARSER_builder_add_mx (char *dst,
1034                                  size_t dst_len,
1035                                  size_t *off,
1036                                  const struct GNUNET_DNSPARSER_MxRecord *mx)
1037 {
1038   uint16_t mxpref;
1039
1040   if (*off + sizeof (uint16_t) > dst_len)
1041     return GNUNET_NO;
1042   mxpref = htons (mx->preference);
1043   GNUNET_memcpy (&dst[*off],
1044                  &mxpref,
1045                  sizeof (mxpref));
1046   (*off) += sizeof (mxpref);
1047   return GNUNET_DNSPARSER_builder_add_name (dst,
1048                                             dst_len,
1049                                             off,
1050                                             mx->mxhost);
1051 }
1052
1053
1054 /**
1055  * Add a CERT record to the UDP packet at the given location.
1056  *
1057  * @param dst where to write the CERT record
1058  * @param dst_len number of bytes in @a dst
1059  * @param off pointer to offset where to write the CERT information (increment by bytes used);
1060  *            can also change if there was an error
1061  * @param cert CERT information to write
1062  * @return #GNUNET_SYSERR if @a cert is invalid
1063  *         #GNUNET_NO if @a cert did not fit
1064  *         #GNUNET_OK if @a cert was added to @a dst
1065  */
1066 int
1067 GNUNET_DNSPARSER_builder_add_cert (char *dst,
1068                                    size_t dst_len,
1069                                    size_t *off,
1070                                    const struct GNUNET_DNSPARSER_CertRecord *cert)
1071 {
1072   struct GNUNET_TUN_DnsCertRecord dcert;
1073
1074   if ( (cert->cert_type > UINT16_MAX) ||
1075        (cert->algorithm > UINT8_MAX) )
1076   {
1077     GNUNET_break (0);
1078     return GNUNET_SYSERR;
1079   }
1080   if (*off + sizeof (struct GNUNET_TUN_DnsCertRecord) + cert->certificate_size > dst_len)
1081     return GNUNET_NO;
1082   dcert.cert_type = htons ((uint16_t) cert->cert_type);
1083   dcert.cert_tag = htons ((uint16_t) cert->cert_tag);
1084   dcert.algorithm = (uint8_t) cert->algorithm;
1085   GNUNET_memcpy (&dst[*off], &dcert, sizeof (dcert));
1086   (*off) += sizeof (dcert);
1087   GNUNET_memcpy (&dst[*off], cert->certificate_data, cert->certificate_size);
1088   (*off) += cert->certificate_size;
1089   return GNUNET_OK;
1090 }
1091
1092
1093 /**
1094  * Add an SOA record to the UDP packet at the given location.
1095  *
1096  * @param dst where to write the SOA record
1097  * @param dst_len number of bytes in @a dst
1098  * @param off pointer to offset where to write the SOA information (increment by bytes used)
1099  *            can also change if there was an error
1100  * @param soa SOA information to write
1101  * @return #GNUNET_SYSERR if @a soa is invalid
1102  *         #GNUNET_NO if @a soa did not fit
1103  *         #GNUNET_OK if @a soa was added to @a dst
1104  */
1105 int
1106 GNUNET_DNSPARSER_builder_add_soa (char *dst,
1107                                   size_t dst_len,
1108                                   size_t *off,
1109                                   const struct GNUNET_DNSPARSER_SoaRecord *soa)
1110 {
1111   struct GNUNET_TUN_DnsSoaRecord sd;
1112   int ret;
1113
1114   if ( (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
1115                                                                dst_len,
1116                                                                off,
1117                                                                soa->mname))) ||
1118        (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
1119                                                                dst_len,
1120                                                                off,
1121                                                                soa->rname)) ) )
1122     return ret;
1123   if (*off + sizeof (struct GNUNET_TUN_DnsSoaRecord) > dst_len)
1124     return GNUNET_NO;
1125   sd.serial = htonl (soa->serial);
1126   sd.refresh = htonl (soa->refresh);
1127   sd.retry = htonl (soa->retry);
1128   sd.expire = htonl (soa->expire);
1129   sd.minimum = htonl (soa->minimum_ttl);
1130   GNUNET_memcpy (&dst[*off], &sd, sizeof (sd));
1131   (*off) += sizeof (sd);
1132   return GNUNET_OK;
1133 }
1134
1135
1136 /**
1137  * Add an SRV record to the UDP packet at the given location.
1138  *
1139  * @param dst where to write the SRV record
1140  * @param dst_len number of bytes in @a dst
1141  * @param off pointer to offset where to write the SRV information (increment by bytes used)
1142  *            can also change if there was an error
1143  * @param srv SRV information to write
1144  * @return #GNUNET_SYSERR if @a srv is invalid
1145  *         #GNUNET_NO if @a srv did not fit
1146  *         #GNUNET_OK if @a srv was added to @a dst
1147  */
1148 int
1149 GNUNET_DNSPARSER_builder_add_srv (char *dst,
1150                                   size_t dst_len,
1151                                   size_t *off,
1152                                   const struct GNUNET_DNSPARSER_SrvRecord *srv)
1153 {
1154   struct GNUNET_TUN_DnsSrvRecord sd;
1155   int ret;
1156
1157   if (*off + sizeof (struct GNUNET_TUN_DnsSrvRecord) > dst_len)
1158     return GNUNET_NO;
1159   sd.prio = htons (srv->priority);
1160   sd.weight = htons (srv->weight);
1161   sd.port = htons (srv->port);
1162   GNUNET_memcpy (&dst[*off],
1163                  &sd,
1164                  sizeof (sd));
1165   (*off) += sizeof (sd);
1166   if (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
1167                                                              dst_len,
1168                                                              off,
1169                                                              srv->target)))
1170     return ret;
1171   return GNUNET_OK;
1172 }
1173
1174
1175 /**
1176  * Add a DNS record to the UDP packet at the given location.
1177  *
1178  * @param dst where to write the query
1179  * @param dst_len number of bytes in @a dst
1180  * @param off pointer to offset where to write the query (increment by bytes used)
1181  *            must not be changed if there is an error
1182  * @param record record to write
1183  * @return #GNUNET_SYSERR if @a record is invalid
1184  *         #GNUNET_NO if @a record did not fit
1185  *         #GNUNET_OK if @a record was added to @a dst
1186  */
1187 static int
1188 add_record (char *dst,
1189             size_t dst_len,
1190             size_t *off,
1191             const struct GNUNET_DNSPARSER_Record *record)
1192 {
1193   int ret;
1194   size_t start;
1195   size_t pos;
1196   struct GNUNET_TUN_DnsRecordLine rl;
1197
1198   start = *off;
1199   ret = GNUNET_DNSPARSER_builder_add_name (dst,
1200                                            dst_len - sizeof (struct GNUNET_TUN_DnsRecordLine),
1201                                            off,
1202                                            record->name);
1203   if (GNUNET_OK != ret)
1204     return ret;
1205   /* '*off' is now the position where we will need to write the record line */
1206
1207   pos = *off + sizeof (struct GNUNET_TUN_DnsRecordLine);
1208   switch (record->type)
1209   {
1210   case GNUNET_DNSPARSER_TYPE_MX:
1211     ret = GNUNET_DNSPARSER_builder_add_mx (dst,
1212                                            dst_len,
1213                                            &pos,
1214                                            record->data.mx);
1215     break;
1216   case GNUNET_DNSPARSER_TYPE_CERT:
1217     ret = GNUNET_DNSPARSER_builder_add_cert (dst,
1218                                              dst_len,
1219                                              &pos,
1220                                              record->data.cert);
1221     break;
1222   case GNUNET_DNSPARSER_TYPE_SOA:
1223     ret = GNUNET_DNSPARSER_builder_add_soa (dst,
1224                                             dst_len,
1225                                             &pos,
1226                                             record->data.soa);
1227     break;
1228   case GNUNET_DNSPARSER_TYPE_NS:
1229   case GNUNET_DNSPARSER_TYPE_CNAME:
1230   case GNUNET_DNSPARSER_TYPE_PTR:
1231     ret = GNUNET_DNSPARSER_builder_add_name (dst,
1232                                              dst_len,
1233                                              &pos,
1234                                              record->data.hostname);
1235     break;
1236   case GNUNET_DNSPARSER_TYPE_SRV:
1237     ret = GNUNET_DNSPARSER_builder_add_srv (dst,
1238                                             dst_len,
1239                                             &pos,
1240                                             record->data.srv);
1241     break;
1242   default:
1243     if (pos + record->data.raw.data_len > dst_len)
1244     {
1245       ret = GNUNET_NO;
1246       break;
1247     }
1248     GNUNET_memcpy (&dst[pos],
1249                    record->data.raw.data,
1250                    record->data.raw.data_len);
1251     pos += record->data.raw.data_len;
1252     ret = GNUNET_OK;
1253     break;
1254   }
1255   if (GNUNET_OK != ret)
1256   {
1257     *off = start;
1258     return GNUNET_NO;
1259   }
1260
1261   if (pos - (*off + sizeof (struct GNUNET_TUN_DnsRecordLine)) > UINT16_MAX)
1262   {
1263     /* record data too long */
1264     *off = start;
1265     return GNUNET_NO;
1266   }
1267   rl.type = htons (record->type);
1268   rl.dns_traffic_class = htons (record->dns_traffic_class);
1269   rl.ttl = htonl (GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value_us / 1000LL / 1000LL); /* in seconds */
1270   rl.data_len = htons ((uint16_t) (pos - (*off + sizeof (struct GNUNET_TUN_DnsRecordLine))));
1271   GNUNET_memcpy (&dst[*off],
1272                  &rl,
1273                  sizeof (struct GNUNET_TUN_DnsRecordLine));
1274   *off = pos;
1275   return GNUNET_OK;
1276 }
1277
1278
1279 /**
1280  * Given a DNS packet @a p, generate the corresponding UDP payload.
1281  * Note that we do not attempt to pack the strings with pointers
1282  * as this would complicate the code and this is about being
1283  * simple and secure, not fast, fancy and broken like bind.
1284  *
1285  * @param p packet to pack
1286  * @param max maximum allowed size for the resulting UDP payload
1287  * @param buf set to a buffer with the packed message
1288  * @param buf_length set to the length of @a buf
1289  * @return #GNUNET_SYSERR if @a p is invalid
1290  *         #GNUNET_NO if @a p was truncated (but there is still a result in @a buf)
1291  *         #GNUNET_OK if @a p was packed completely into @a buf
1292  */
1293 int
1294 GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
1295                        uint16_t max,
1296                        char **buf,
1297                        size_t *buf_length)
1298 {
1299   struct GNUNET_TUN_DnsHeader dns;
1300   size_t off;
1301   char tmp[max];
1302   int ret;
1303   int trc;
1304
1305   if ( (p->num_queries > UINT16_MAX) ||
1306        (p->num_answers > UINT16_MAX) ||
1307        (p->num_authority_records > UINT16_MAX) ||
1308        (p->num_additional_records > UINT16_MAX) )
1309     return GNUNET_SYSERR;
1310   dns.id = p->id;
1311   dns.flags = p->flags;
1312   dns.query_count = htons (p->num_queries);
1313   dns.answer_rcount = htons (p->num_answers);
1314   dns.authority_rcount = htons (p->num_authority_records);
1315   dns.additional_rcount = htons (p->num_additional_records);
1316
1317   off = sizeof (struct GNUNET_TUN_DnsHeader);
1318   trc = GNUNET_NO;
1319   for (unsigned int i=0;i<p->num_queries;i++)
1320   {
1321     ret = GNUNET_DNSPARSER_builder_add_query (tmp,
1322                                               sizeof (tmp),
1323                                               &off,
1324                                               &p->queries[i]);
1325     if (GNUNET_SYSERR == ret)
1326       return GNUNET_SYSERR;
1327     if (GNUNET_NO == ret)
1328     {
1329       dns.query_count = htons ((uint16_t) (i-1));
1330       trc = GNUNET_YES;
1331       break;
1332     }
1333   }
1334   for (unsigned int i=0;i<p->num_answers;i++)
1335   {
1336     ret = add_record (tmp,
1337                       sizeof (tmp),
1338                       &off,
1339                       &p->answers[i]);
1340     if (GNUNET_SYSERR == ret)
1341       return GNUNET_SYSERR;
1342     if (GNUNET_NO == ret)
1343     {
1344       dns.answer_rcount = htons ((uint16_t) (i-1));
1345       trc = GNUNET_YES;
1346       break;
1347     }
1348   }
1349   for (unsigned int i=0;i<p->num_authority_records;i++)
1350   {
1351     ret = add_record (tmp,
1352                       sizeof (tmp),
1353                       &off,
1354                       &p->authority_records[i]);
1355     if (GNUNET_SYSERR == ret)
1356       return GNUNET_SYSERR;
1357     if (GNUNET_NO == ret)
1358     {
1359       dns.authority_rcount = htons ((uint16_t) (i-1));
1360       trc = GNUNET_YES;
1361       break;
1362     }
1363   }
1364   for (unsigned int i=0;i<p->num_additional_records;i++)
1365   {
1366     ret = add_record (tmp,
1367                       sizeof (tmp),
1368                       &off,
1369                       &p->additional_records[i]);
1370     if (GNUNET_SYSERR == ret)
1371       return GNUNET_SYSERR;
1372     if (GNUNET_NO == ret)
1373     {
1374       dns.additional_rcount = htons (i-1);
1375       trc = GNUNET_YES;
1376       break;
1377     }
1378   }
1379
1380   if (GNUNET_YES == trc)
1381     dns.flags.message_truncated = 1;
1382   GNUNET_memcpy (tmp,
1383                  &dns,
1384                  sizeof (struct GNUNET_TUN_DnsHeader));
1385
1386   *buf = GNUNET_malloc (off);
1387   *buf_length = off;
1388   GNUNET_memcpy (*buf,
1389                  tmp,
1390                  off);
1391   if (GNUNET_YES == trc)
1392     return GNUNET_NO;
1393   return GNUNET_OK;
1394 }
1395
1396
1397 /**
1398  * Convert a block of binary data to HEX.
1399  *
1400  * @param data binary data to convert
1401  * @param data_size number of bytes in @a data
1402  * @return HEX string (lower case)
1403  */
1404 char *
1405 GNUNET_DNSPARSER_bin_to_hex (const void *data,
1406                              size_t data_size)
1407 {
1408   char *ret;
1409   size_t off;
1410   const uint8_t *idata;
1411
1412   idata = data;
1413   ret = GNUNET_malloc (data_size * 2 + 1);
1414   for (off = 0; off < data_size; off++)
1415     sprintf (&ret[off * 2],
1416              "%02x",
1417              idata[off]);
1418   return ret;
1419 }
1420
1421
1422 /**
1423  * Convert a HEX string to block of binary data.
1424  *
1425  * @param hex HEX string to convert (may contain mixed case)
1426  * @param data where to write result, must be
1427  *             at least `strlen(hex)/2` bytes long
1428  * @return number of bytes written to data
1429  */
1430 size_t
1431 GNUNET_DNSPARSER_hex_to_bin (const char *hex,
1432                              void *data)
1433 {
1434   size_t data_size;
1435   size_t off;
1436   uint8_t *idata;
1437   unsigned int h;
1438   char in[3];
1439
1440   data_size = strlen (hex) / 2;
1441   idata = data;
1442   in[2] = '\0';
1443   for (off = 0; off < data_size; off++)
1444   {
1445     in[0] = tolower ((unsigned char) hex[off * 2]);
1446     in[1] = tolower ((unsigned char) hex[off * 2 + 1]);
1447     if (1 != sscanf (in, "%x", &h))
1448       return off;
1449     idata[off] = (uint8_t) h;
1450   }
1451   return off;
1452 }
1453
1454
1455 /* end of dnsparser.c */