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