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