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