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