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