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