fix leak
[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,
549                  &udp_payload[*off],
550                  sizeof (struct GNUNET_TUN_DnsCertRecord));
551   (*off) += sizeof (struct GNUNET_TUN_DnsCertRecord);
552   cert = GNUNET_new (struct GNUNET_DNSPARSER_CertRecord);
553   cert->cert_type = ntohs (dcert.cert_type);
554   cert->cert_tag = ntohs (dcert.cert_tag);
555   cert->algorithm = dcert.algorithm;
556   cert->certificate_size = udp_payload_length - (*off);
557   cert->certificate_data = GNUNET_malloc (cert->certificate_size);
558   GNUNET_memcpy (cert->certificate_data,
559                  &udp_payload[*off],
560                  cert->certificate_size);
561   (*off) += cert->certificate_size;
562   return cert;
563 }
564
565
566 /**
567  * Parse a DNS record entry.
568  *
569  * @param udp_payload entire UDP payload
570  * @param udp_payload_length length of @a udp_payload
571  * @param off pointer to the offset of the record to parse in the udp_payload (to be
572  *                    incremented by the size of the record)
573  * @param r where to write the record information
574  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the record is malformed
575  */
576 int
577 GNUNET_DNSPARSER_parse_record (const char *udp_payload,
578                                size_t udp_payload_length,
579                                size_t *off,
580                                struct GNUNET_DNSPARSER_Record *r)
581 {
582   char *name;
583   struct GNUNET_TUN_DnsRecordLine rl;
584   size_t old_off;
585   uint16_t data_len;
586
587   name = GNUNET_DNSPARSER_parse_name (udp_payload,
588                                       udp_payload_length,
589                                       off);
590   if (NULL == name)
591   {
592     GNUNET_break_op (0);
593     return GNUNET_SYSERR;
594   }
595   r->name = name;
596   if (*off + sizeof (struct GNUNET_TUN_DnsRecordLine) > udp_payload_length)
597   {
598     GNUNET_break_op (0);
599     return GNUNET_SYSERR;
600   }
601   GNUNET_memcpy (&rl, &udp_payload[*off], sizeof (rl));
602   (*off) += sizeof (rl);
603   r->type = ntohs (rl.type);
604   r->dns_traffic_class = ntohs (rl.dns_traffic_class);
605   r->expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
606                                                                                         ntohl (rl.ttl)));
607   data_len = ntohs (rl.data_len);
608   if (*off + data_len > udp_payload_length)
609   {
610     GNUNET_break_op (0);
611     return GNUNET_SYSERR;
612   }
613   old_off = *off;
614   switch (r->type)
615   {
616   case GNUNET_DNSPARSER_TYPE_NS:
617   case GNUNET_DNSPARSER_TYPE_CNAME:
618   case GNUNET_DNSPARSER_TYPE_DNAME:
619   case GNUNET_DNSPARSER_TYPE_PTR:
620     r->data.hostname = GNUNET_DNSPARSER_parse_name (udp_payload,
621                                                     udp_payload_length,
622                                                     off);
623     if ( (NULL == r->data.hostname) ||
624          (old_off + data_len != *off) )
625       return GNUNET_SYSERR;
626     return GNUNET_OK;
627   case GNUNET_DNSPARSER_TYPE_SOA:
628     r->data.soa = GNUNET_DNSPARSER_parse_soa (udp_payload,
629                                               udp_payload_length,
630                                               off);
631     if ( (NULL == r->data.soa) ||
632          (old_off + data_len != *off) )
633     {
634       GNUNET_break_op (0);
635       return GNUNET_SYSERR;
636     }
637     return GNUNET_OK;
638   case GNUNET_DNSPARSER_TYPE_MX:
639     r->data.mx = GNUNET_DNSPARSER_parse_mx (udp_payload,
640                                             udp_payload_length,
641                                             off);
642     if ( (NULL == r->data.mx) ||
643          (old_off + data_len != *off) )
644     {
645       GNUNET_break_op (0);
646       return GNUNET_SYSERR;
647     }
648     return GNUNET_OK;
649   case GNUNET_DNSPARSER_TYPE_SRV:
650     r->data.srv = GNUNET_DNSPARSER_parse_srv (udp_payload,
651                                               udp_payload_length,
652                                               off);
653     if ( (NULL == r->data.srv) ||
654          (old_off + data_len != *off) )
655     {
656       GNUNET_break_op (0);
657       return GNUNET_SYSERR;
658     }
659     return GNUNET_OK;
660   default:
661     r->data.raw.data = GNUNET_malloc (data_len);
662     r->data.raw.data_len = data_len;
663     GNUNET_memcpy (r->data.raw.data,
664                    &udp_payload[*off],
665                    data_len);
666     break;
667   }
668   (*off) += data_len;
669   return GNUNET_OK;
670 }
671
672
673 /**
674  * Parse a UDP payload of a DNS packet in to a nice struct for further
675  * processing and manipulation.
676  *
677  * @param udp_payload wire-format of the DNS packet
678  * @param udp_payload_length number of bytes in @a udp_payload
679  * @return NULL on error, otherwise the parsed packet
680  */
681 struct GNUNET_DNSPARSER_Packet *
682 GNUNET_DNSPARSER_parse (const char *udp_payload,
683                         size_t udp_payload_length)
684 {
685   struct GNUNET_DNSPARSER_Packet *p;
686   const struct GNUNET_TUN_DnsHeader *dns;
687   size_t off;
688   unsigned int n;
689
690   if (udp_payload_length < sizeof (struct GNUNET_TUN_DnsHeader))
691     return NULL;
692   dns = (const struct GNUNET_TUN_DnsHeader *) udp_payload;
693   off = sizeof (struct GNUNET_TUN_DnsHeader);
694   p = GNUNET_new (struct GNUNET_DNSPARSER_Packet);
695   p->flags = dns->flags;
696   p->id = dns->id;
697   n = ntohs (dns->query_count);
698   if (n > 0)
699   {
700     p->queries = GNUNET_new_array (n,
701                                    struct GNUNET_DNSPARSER_Query) ;
702     p->num_queries = n;
703     for (unsigned int i=0;i<n;i++)
704       if (GNUNET_OK !=
705           GNUNET_DNSPARSER_parse_query (udp_payload,
706                                         udp_payload_length,
707                                         &off,
708                                         &p->queries[i]))
709         goto error;
710   }
711   n = ntohs (dns->answer_rcount);
712   if (n > 0)
713   {
714     p->answers = GNUNET_new_array (n,
715                                    struct GNUNET_DNSPARSER_Record);
716     p->num_answers = n;
717     for (unsigned int i=0;i<n;i++)
718       if (GNUNET_OK !=
719           GNUNET_DNSPARSER_parse_record (udp_payload,
720                                          udp_payload_length,
721                                          &off,
722                                          &p->answers[i]))
723         goto error;
724   }
725   n = ntohs (dns->authority_rcount);
726   if (n > 0)
727   {
728     p->authority_records = GNUNET_new_array (n,
729                                              struct GNUNET_DNSPARSER_Record);
730     p->num_authority_records = n;
731     for (unsigned int i=0;i<n;i++)
732       if (GNUNET_OK !=
733           GNUNET_DNSPARSER_parse_record (udp_payload,
734                                          udp_payload_length,
735                                          &off,
736                                          &p->authority_records[i]))
737         goto error;
738   }
739   n = ntohs (dns->additional_rcount);
740   if (n > 0)
741   {
742     p->additional_records = GNUNET_new_array (n,
743                                               struct GNUNET_DNSPARSER_Record);
744     p->num_additional_records = n;
745     for (unsigned int i=0;i<n;i++)
746     {
747       if (GNUNET_OK !=
748           GNUNET_DNSPARSER_parse_record (udp_payload,
749                                          udp_payload_length,
750                                          &off,
751                                          &p->additional_records[i]))
752         goto error;
753   }
754   return p;
755  error:
756   GNUNET_break_op (0);
757   GNUNET_DNSPARSER_free_packet (p);
758   return NULL;
759 }
760
761
762 /**
763  * Free memory taken by a packet.
764  *
765  * @param p packet to free
766  */
767 void
768 GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
769 {
770   for (unsigned int i=0;i<p->num_queries;i++)
771     GNUNET_free_non_null (p->queries[i].name);
772   GNUNET_free_non_null (p->queries);
773   for (unsigned int i=0;i<p->num_answers;i++)
774     GNUNET_DNSPARSER_free_record (&p->answers[i]);
775   GNUNET_free_non_null (p->answers);
776   for (unsigned int i=0;i<p->num_authority_records;i++)
777     GNUNET_DNSPARSER_free_record (&p->authority_records[i]);
778   GNUNET_free_non_null (p->authority_records);
779   for (unsigned int i=0;i<p->num_additional_records;i++)
780     GNUNET_DNSPARSER_free_record (&p->additional_records[i]);
781   GNUNET_free_non_null (p->additional_records);
782   GNUNET_free (p);
783 }
784
785
786 /* ********************** DNS packet assembly code **************** */
787
788
789 /**
790  * Add a DNS name to the UDP packet at the given location, converting
791  * the name to IDNA notation as necessary.
792  *
793  * @param dst where to write the name (UDP packet)
794  * @param dst_len number of bytes in @a dst
795  * @param off pointer to offset where to write the name (increment by bytes used)
796  *            must not be changed if there is an error
797  * @param name name to write
798  * @return #GNUNET_SYSERR if @a name is invalid
799  *         #GNUNET_NO if @a name did not fit
800  *         #GNUNET_OK if @a name was added to @a dst
801  */
802 int
803 GNUNET_DNSPARSER_builder_add_name (char *dst,
804                                    size_t dst_len,
805                                    size_t *off,
806                                    const char *name)
807 {
808   const char *dot;
809   const char *idna_name;
810   char *idna_start;
811   size_t start;
812   size_t pos;
813   size_t len;
814   Idna_rc rc;
815
816   if (NULL == name)
817     return GNUNET_SYSERR;
818
819   if (IDNA_SUCCESS !=
820       (rc = idna_to_ascii_8z (name,
821                               &idna_start,
822                               IDNA_ALLOW_UNASSIGNED)))
823   {
824     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
825                 _("Failed to convert UTF-8 name `%s' to DNS IDNA format: %s\n"),
826                 name,
827                 idna_strerror (rc));
828     return GNUNET_NO;
829   }
830   idna_name = idna_start;
831   start = *off;
832   if (start + strlen (idna_name) + 2 > dst_len)
833     goto fail;
834   pos = start;
835   do
836   {
837     dot = strchr (idna_name, '.');
838     if (NULL == dot)
839       len = strlen (idna_name);
840     else
841       len = dot - idna_name;
842     if ( (len >= 64) || (0 == len) )
843     {
844       GNUNET_break (0);
845       goto fail; /* segment too long or empty */
846     }
847     dst[pos++] = (char) (uint8_t) len;
848     GNUNET_memcpy (&dst[pos],
849                    idna_name,
850                    len);
851     pos += len;
852     idna_name += len + 1; /* also skip dot */
853   }
854   while (NULL != dot);
855   dst[pos++] = '\0'; /* terminator */
856   *off = pos;
857 #if WINDOWS
858   idn_free (idna_start);
859 #else
860   free (idna_start);
861 #endif
862   return GNUNET_OK;
863  fail:
864 #if WINDOWS
865   idn_free (idna_start);
866 #else
867   free (idna_start);
868 #endif
869   return GNUNET_NO;
870 }
871
872
873 /**
874  * Add a DNS query to the UDP packet at the given location.
875  *
876  * @param dst where to write the query
877  * @param dst_len number of bytes in @a dst
878  * @param off pointer to offset where to write the query (increment by bytes used)
879  *            must not be changed if there is an error
880  * @param query query to write
881  * @return #GNUNET_SYSERR if @a query is invalid
882  *         #GNUNET_NO if @a query did not fit
883  *         #GNUNET_OK if @a query was added to @a dst
884  */
885 int
886 GNUNET_DNSPARSER_builder_add_query (char *dst,
887                                     size_t dst_len,
888                                     size_t *off,
889                                     const struct GNUNET_DNSPARSER_Query *query)
890 {
891   int ret;
892   struct GNUNET_TUN_DnsQueryLine ql;
893
894   ret = GNUNET_DNSPARSER_builder_add_name (dst, dst_len - sizeof (struct GNUNET_TUN_DnsQueryLine), off, query->name);
895   if (ret != GNUNET_OK)
896     return ret;
897   ql.type = htons (query->type);
898   ql.dns_traffic_class = htons (query->dns_traffic_class);
899   GNUNET_memcpy (&dst[*off], &ql, sizeof (ql));
900   (*off) += sizeof (ql);
901   return GNUNET_OK;
902 }
903
904
905 /**
906  * Add an MX record to the UDP packet at the given location.
907  *
908  * @param dst where to write the mx record
909  * @param dst_len number of bytes in @a dst
910  * @param off pointer to offset where to write the mx information (increment by bytes used);
911  *            can also change if there was an error
912  * @param mx mx information to write
913  * @return #GNUNET_SYSERR if @a mx is invalid
914  *         #GNUNET_NO if @a mx did not fit
915  *         #GNUNET_OK if @a mx was added to @a dst
916  */
917 int
918 GNUNET_DNSPARSER_builder_add_mx (char *dst,
919                                  size_t dst_len,
920                                  size_t *off,
921                                  const struct GNUNET_DNSPARSER_MxRecord *mx)
922 {
923   uint16_t mxpref;
924
925   if (*off + sizeof (uint16_t) > dst_len)
926     return GNUNET_NO;
927   mxpref = htons (mx->preference);
928   GNUNET_memcpy (&dst[*off],
929                  &mxpref,
930                  sizeof (mxpref));
931   (*off) += sizeof (mxpref);
932   return GNUNET_DNSPARSER_builder_add_name (dst,
933                                             dst_len,
934                                             off,
935                                             mx->mxhost);
936 }
937
938
939 /**
940  * Add a CERT record to the UDP packet at the given location.
941  *
942  * @param dst where to write the CERT record
943  * @param dst_len number of bytes in @a dst
944  * @param off pointer to offset where to write the CERT information (increment by bytes used);
945  *            can also change if there was an error
946  * @param cert CERT information to write
947  * @return #GNUNET_SYSERR if @a cert is invalid
948  *         #GNUNET_NO if @a cert did not fit
949  *         #GNUNET_OK if @a cert was added to @a dst
950  */
951 int
952 GNUNET_DNSPARSER_builder_add_cert (char *dst,
953                                    size_t dst_len,
954                                    size_t *off,
955                                    const struct GNUNET_DNSPARSER_CertRecord *cert)
956 {
957   struct GNUNET_TUN_DnsCertRecord dcert;
958
959   if ( (cert->cert_type > UINT16_MAX) ||
960        (cert->cert_tag > UINT16_MAX) ||
961        (cert->algorithm > UINT8_MAX) )
962   {
963     GNUNET_break (0);
964     return GNUNET_SYSERR;
965   }
966   if (*off + sizeof (struct GNUNET_TUN_DnsCertRecord) + cert->certificate_size > dst_len)
967     return GNUNET_NO;
968   dcert.cert_type = htons ((uint16_t) cert->cert_type);
969   dcert.cert_tag = htons ((uint16_t) cert->cert_tag);
970   dcert.algorithm = (uint8_t) cert->algorithm;
971   GNUNET_memcpy (&dst[*off], &dcert, sizeof (dcert));
972   (*off) += sizeof (dcert);
973   GNUNET_memcpy (&dst[*off], cert->certificate_data, cert->certificate_size);
974   (*off) += cert->certificate_size;
975   return GNUNET_OK;
976 }
977
978
979 /**
980  * Add an SOA record to the UDP packet at the given location.
981  *
982  * @param dst where to write the SOA record
983  * @param dst_len number of bytes in @a dst
984  * @param off pointer to offset where to write the SOA information (increment by bytes used)
985  *            can also change if there was an error
986  * @param soa SOA information to write
987  * @return #GNUNET_SYSERR if @a soa is invalid
988  *         #GNUNET_NO if @a soa did not fit
989  *         #GNUNET_OK if @a soa was added to @a dst
990  */
991 int
992 GNUNET_DNSPARSER_builder_add_soa (char *dst,
993                                   size_t dst_len,
994                                   size_t *off,
995                                   const struct GNUNET_DNSPARSER_SoaRecord *soa)
996 {
997   struct GNUNET_TUN_DnsSoaRecord sd;
998   int ret;
999
1000   if ( (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
1001                                                                dst_len,
1002                                                                off,
1003                                                                soa->mname))) ||
1004        (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
1005                                                                dst_len,
1006                                                                off,
1007                                                                soa->rname)) ) )
1008     return ret;
1009   if (*off + sizeof (struct GNUNET_TUN_DnsSoaRecord) > dst_len)
1010     return GNUNET_NO;
1011   sd.serial = htonl (soa->serial);
1012   sd.refresh = htonl (soa->refresh);
1013   sd.retry = htonl (soa->retry);
1014   sd.expire = htonl (soa->expire);
1015   sd.minimum = htonl (soa->minimum_ttl);
1016   GNUNET_memcpy (&dst[*off], &sd, sizeof (sd));
1017   (*off) += sizeof (sd);
1018   return GNUNET_OK;
1019 }
1020
1021
1022 /**
1023  * Add an SRV record to the UDP packet at the given location.
1024  *
1025  * @param dst where to write the SRV record
1026  * @param dst_len number of bytes in @a dst
1027  * @param off pointer to offset where to write the SRV information (increment by bytes used)
1028  *            can also change if there was an error
1029  * @param srv SRV information to write
1030  * @return #GNUNET_SYSERR if @a srv is invalid
1031  *         #GNUNET_NO if @a srv did not fit
1032  *         #GNUNET_OK if @a srv was added to @a dst
1033  */
1034 int
1035 GNUNET_DNSPARSER_builder_add_srv (char *dst,
1036                                   size_t dst_len,
1037                                   size_t *off,
1038                                   const struct GNUNET_DNSPARSER_SrvRecord *srv)
1039 {
1040   struct GNUNET_TUN_DnsSrvRecord sd;
1041   int ret;
1042
1043   if (*off + sizeof (struct GNUNET_TUN_DnsSrvRecord) > dst_len)
1044     return GNUNET_NO;
1045   sd.prio = htons (srv->priority);
1046   sd.weight = htons (srv->weight);
1047   sd.port = htons (srv->port);
1048   GNUNET_memcpy (&dst[*off], &sd, sizeof (sd));
1049   (*off) += sizeof (sd);
1050   if (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
1051                                     dst_len,
1052                                     off,
1053                                     srv->target)))
1054     return ret;
1055   return GNUNET_OK;
1056 }
1057
1058
1059 /**
1060  * Add a DNS record to the UDP packet at the given location.
1061  *
1062  * @param dst where to write the query
1063  * @param dst_len number of bytes in @a dst
1064  * @param off pointer to offset where to write the query (increment by bytes used)
1065  *            must not be changed if there is an error
1066  * @param record record to write
1067  * @return #GNUNET_SYSERR if @a record is invalid
1068  *         #GNUNET_NO if @a record did not fit
1069  *         #GNUNET_OK if @a record was added to @a dst
1070  */
1071 static int
1072 add_record (char *dst,
1073             size_t dst_len,
1074             size_t *off,
1075             const struct GNUNET_DNSPARSER_Record *record)
1076 {
1077   int ret;
1078   size_t start;
1079   size_t pos;
1080   struct GNUNET_TUN_DnsRecordLine rl;
1081
1082   start = *off;
1083   ret = GNUNET_DNSPARSER_builder_add_name (dst,
1084                                            dst_len - sizeof (struct GNUNET_TUN_DnsRecordLine),
1085                                            off,
1086                                            record->name);
1087   if (GNUNET_OK != ret)
1088     return ret;
1089   /* '*off' is now the position where we will need to write the record line */
1090
1091   pos = *off + sizeof (struct GNUNET_TUN_DnsRecordLine);
1092   switch (record->type)
1093   {
1094   case GNUNET_DNSPARSER_TYPE_MX:
1095     ret = GNUNET_DNSPARSER_builder_add_mx (dst,
1096                                            dst_len,
1097                                            &pos,
1098                                            record->data.mx);
1099     break;
1100   case GNUNET_DNSPARSER_TYPE_CERT:
1101     ret = GNUNET_DNSPARSER_builder_add_cert (dst,
1102                                              dst_len,
1103                                              &pos,
1104                                              record->data.cert);
1105     break;
1106   case GNUNET_DNSPARSER_TYPE_SOA:
1107     ret = GNUNET_DNSPARSER_builder_add_soa (dst,
1108                                             dst_len,
1109                                             &pos,
1110                                             record->data.soa);
1111     break;
1112   case GNUNET_DNSPARSER_TYPE_NS:
1113   case GNUNET_DNSPARSER_TYPE_CNAME:
1114   case GNUNET_DNSPARSER_TYPE_PTR:
1115     ret = GNUNET_DNSPARSER_builder_add_name (dst,
1116                                              dst_len,
1117                                              &pos,
1118                                              record->data.hostname);
1119     break;
1120   case GNUNET_DNSPARSER_TYPE_SRV:
1121     ret = GNUNET_DNSPARSER_builder_add_srv (dst,
1122                                             dst_len,
1123                                             &pos,
1124                                             record->data.srv);
1125     break;
1126   default:
1127     if (pos + record->data.raw.data_len > dst_len)
1128     {
1129       ret = GNUNET_NO;
1130       break;
1131     }
1132     GNUNET_memcpy (&dst[pos],
1133                    record->data.raw.data,
1134                    record->data.raw.data_len);
1135     pos += record->data.raw.data_len;
1136     ret = GNUNET_OK;
1137     break;
1138   }
1139   if (GNUNET_OK != ret)
1140   {
1141     *off = start;
1142     return GNUNET_NO;
1143   }
1144
1145   if (pos - (*off + sizeof (struct GNUNET_TUN_DnsRecordLine)) > UINT16_MAX)
1146   {
1147     /* record data too long */
1148     *off = start;
1149     return GNUNET_NO;
1150   }
1151   rl.type = htons (record->type);
1152   rl.dns_traffic_class = htons (record->dns_traffic_class);
1153   rl.ttl = htonl (GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value_us / 1000LL / 1000LL); /* in seconds */
1154   rl.data_len = htons ((uint16_t) (pos - (*off + sizeof (struct GNUNET_TUN_DnsRecordLine))));
1155   GNUNET_memcpy (&dst[*off],
1156                  &rl,
1157                  sizeof (struct GNUNET_TUN_DnsRecordLine));
1158   *off = pos;
1159   return GNUNET_OK;
1160 }
1161
1162
1163 /**
1164  * Given a DNS packet @a p, generate the corresponding UDP payload.
1165  * Note that we do not attempt to pack the strings with pointers
1166  * as this would complicate the code and this is about being
1167  * simple and secure, not fast, fancy and broken like bind.
1168  *
1169  * @param p packet to pack
1170  * @param max maximum allowed size for the resulting UDP payload
1171  * @param buf set to a buffer with the packed message
1172  * @param buf_length set to the length of @a buf
1173  * @return #GNUNET_SYSERR if @a p is invalid
1174  *         #GNUNET_NO if @a p was truncated (but there is still a result in @a buf)
1175  *         #GNUNET_OK if @a p was packed completely into @a buf
1176  */
1177 int
1178 GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
1179                        uint16_t max,
1180                        char **buf,
1181                        size_t *buf_length)
1182 {
1183   struct GNUNET_TUN_DnsHeader dns;
1184   size_t off;
1185   char tmp[max];
1186   unsigned int i;
1187   int ret;
1188   int trc;
1189
1190   if ( (p->num_queries > UINT16_MAX) ||
1191        (p->num_answers > UINT16_MAX) ||
1192        (p->num_authority_records > UINT16_MAX) ||
1193        (p->num_additional_records > UINT16_MAX) )
1194     return GNUNET_SYSERR;
1195   dns.id = p->id;
1196   dns.flags = p->flags;
1197   dns.query_count = htons (p->num_queries);
1198   dns.answer_rcount = htons (p->num_answers);
1199   dns.authority_rcount = htons (p->num_authority_records);
1200   dns.additional_rcount = htons (p->num_additional_records);
1201
1202   off = sizeof (struct GNUNET_TUN_DnsHeader);
1203   trc = GNUNET_NO;
1204   for (i=0;i<p->num_queries;i++)
1205   {
1206     ret = GNUNET_DNSPARSER_builder_add_query (tmp,
1207                                               sizeof (tmp),
1208                                               &off,
1209                                               &p->queries[i]);
1210     if (GNUNET_SYSERR == ret)
1211       return GNUNET_SYSERR;
1212     if (GNUNET_NO == ret)
1213     {
1214       dns.query_count = htons ((uint16_t) (i-1));
1215       trc = GNUNET_YES;
1216       break;
1217     }
1218   }
1219   for (i=0;i<p->num_answers;i++)
1220   {
1221     ret = add_record (tmp,
1222                       sizeof (tmp),
1223                       &off,
1224                       &p->answers[i]);
1225     if (GNUNET_SYSERR == ret)
1226       return GNUNET_SYSERR;
1227     if (GNUNET_NO == ret)
1228     {
1229       dns.answer_rcount = htons ((uint16_t) (i-1));
1230       trc = GNUNET_YES;
1231       break;
1232     }
1233   }
1234   for (i=0;i<p->num_authority_records;i++)
1235   {
1236     ret = add_record (tmp,
1237                       sizeof (tmp),
1238                       &off,
1239                       &p->authority_records[i]);
1240     if (GNUNET_SYSERR == ret)
1241       return GNUNET_SYSERR;
1242     if (GNUNET_NO == ret)
1243     {
1244       dns.authority_rcount = htons ((uint16_t) (i-1));
1245       trc = GNUNET_YES;
1246       break;
1247     }
1248   }
1249   for (i=0;i<p->num_additional_records;i++)
1250   {
1251     ret = add_record (tmp,
1252                       sizeof (tmp),
1253                       &off,
1254                       &p->additional_records[i]);
1255     if (GNUNET_SYSERR == ret)
1256       return GNUNET_SYSERR;
1257     if (GNUNET_NO == ret)
1258     {
1259       dns.additional_rcount = htons (i-1);
1260       trc = GNUNET_YES;
1261       break;
1262     }
1263   }
1264
1265   if (GNUNET_YES == trc)
1266     dns.flags.message_truncated = 1;
1267   GNUNET_memcpy (tmp,
1268                  &dns,
1269                  sizeof (struct GNUNET_TUN_DnsHeader));
1270
1271   *buf = GNUNET_malloc (off);
1272   *buf_length = off;
1273   GNUNET_memcpy (*buf,
1274                  tmp,
1275                  off);
1276   if (GNUNET_YES == trc)
1277     return GNUNET_NO;
1278   return GNUNET_OK;
1279 }
1280
1281
1282 /**
1283  * Convert a block of binary data to HEX.
1284  *
1285  * @param data binary data to convert
1286  * @param data_size number of bytes in @a data
1287  * @return HEX string (lower case)
1288  */
1289 char *
1290 GNUNET_DNSPARSER_bin_to_hex (const void *data,
1291                              size_t data_size)
1292 {
1293   char *ret;
1294   size_t off;
1295   const uint8_t *idata;
1296
1297   idata = data;
1298   ret = GNUNET_malloc (data_size * 2 + 1);
1299   for (off = 0; off < data_size; off++)
1300     sprintf (&ret[off * 2],
1301              "%02x",
1302              idata[off]);
1303   return ret;
1304 }
1305
1306
1307 /**
1308  * Convert a HEX string to block of binary data.
1309  *
1310  * @param hex HEX string to convert (may contain mixed case)
1311  * @param data where to write result, must be
1312  *             at least `strlen(hex)/2` bytes long
1313  * @return number of bytes written to data
1314  */
1315 size_t
1316 GNUNET_DNSPARSER_hex_to_bin (const char *hex,
1317                              void *data)
1318 {
1319   size_t data_size;
1320   size_t off;
1321   uint8_t *idata;
1322   unsigned int h;
1323   char in[3];
1324
1325   data_size = strlen (hex) / 2;
1326   idata = data;
1327   in[2] = '\0';
1328   for (off = 0; off < data_size; off++)
1329   {
1330     in[0] = tolower ((unsigned char) hex[off * 2]);
1331     in[1] = tolower ((unsigned char) hex[off * 2 + 1]);
1332     if (1 != sscanf (in, "%x", &h))
1333       return off;
1334     idata[off] = (uint8_t) h;
1335   }
1336   return off;
1337 }
1338
1339
1340 /* end of dnsparser.c */