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