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