reduce scope
[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   }
755   return p;
756  error:
757   GNUNET_break_op (0);
758   GNUNET_DNSPARSER_free_packet (p);
759   return NULL;
760 }
761
762
763 /**
764  * Free memory taken by a packet.
765  *
766  * @param p packet to free
767  */
768 void
769 GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
770 {
771   for (unsigned int i=0;i<p->num_queries;i++)
772     GNUNET_free_non_null (p->queries[i].name);
773   GNUNET_free_non_null (p->queries);
774   for (unsigned int i=0;i<p->num_answers;i++)
775     GNUNET_DNSPARSER_free_record (&p->answers[i]);
776   GNUNET_free_non_null (p->answers);
777   for (unsigned int i=0;i<p->num_authority_records;i++)
778     GNUNET_DNSPARSER_free_record (&p->authority_records[i]);
779   GNUNET_free_non_null (p->authority_records);
780   for (unsigned int i=0;i<p->num_additional_records;i++)
781     GNUNET_DNSPARSER_free_record (&p->additional_records[i]);
782   GNUNET_free_non_null (p->additional_records);
783   GNUNET_free (p);
784 }
785
786
787 /* ********************** DNS packet assembly code **************** */
788
789
790 /**
791  * Add a DNS name to the UDP packet at the given location, converting
792  * the name to IDNA notation as necessary.
793  *
794  * @param dst where to write the name (UDP packet)
795  * @param dst_len number of bytes in @a dst
796  * @param off pointer to offset where to write the name (increment by bytes used)
797  *            must not be changed if there is an error
798  * @param name name to write
799  * @return #GNUNET_SYSERR if @a name is invalid
800  *         #GNUNET_NO if @a name did not fit
801  *         #GNUNET_OK if @a name was added to @a dst
802  */
803 int
804 GNUNET_DNSPARSER_builder_add_name (char *dst,
805                                    size_t dst_len,
806                                    size_t *off,
807                                    const char *name)
808 {
809   const char *dot;
810   const char *idna_name;
811   char *idna_start;
812   size_t start;
813   size_t pos;
814   size_t len;
815   Idna_rc rc;
816
817   if (NULL == name)
818     return GNUNET_SYSERR;
819
820   if (IDNA_SUCCESS !=
821       (rc = idna_to_ascii_8z (name,
822                               &idna_start,
823                               IDNA_ALLOW_UNASSIGNED)))
824   {
825     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
826                 _("Failed to convert UTF-8 name `%s' to DNS IDNA format: %s\n"),
827                 name,
828                 idna_strerror (rc));
829     return GNUNET_NO;
830   }
831   idna_name = idna_start;
832   start = *off;
833   if (start + strlen (idna_name) + 2 > dst_len)
834     goto fail;
835   pos = start;
836   do
837   {
838     dot = strchr (idna_name, '.');
839     if (NULL == dot)
840       len = strlen (idna_name);
841     else
842       len = dot - idna_name;
843     if ( (len >= 64) || (0 == len) )
844     {
845       GNUNET_break (0);
846       goto fail; /* segment too long or empty */
847     }
848     dst[pos++] = (char) (uint8_t) len;
849     GNUNET_memcpy (&dst[pos],
850                    idna_name,
851                    len);
852     pos += len;
853     idna_name += len + 1; /* also skip dot */
854   }
855   while (NULL != dot);
856   dst[pos++] = '\0'; /* terminator */
857   *off = pos;
858 #if WINDOWS
859   idn_free (idna_start);
860 #else
861   free (idna_start);
862 #endif
863   return GNUNET_OK;
864  fail:
865 #if WINDOWS
866   idn_free (idna_start);
867 #else
868   free (idna_start);
869 #endif
870   return GNUNET_NO;
871 }
872
873
874 /**
875  * Add a DNS query to the UDP packet at the given location.
876  *
877  * @param dst where to write the query
878  * @param dst_len number of bytes in @a dst
879  * @param off pointer to offset where to write the query (increment by bytes used)
880  *            must not be changed if there is an error
881  * @param query query to write
882  * @return #GNUNET_SYSERR if @a query is invalid
883  *         #GNUNET_NO if @a query did not fit
884  *         #GNUNET_OK if @a query was added to @a dst
885  */
886 int
887 GNUNET_DNSPARSER_builder_add_query (char *dst,
888                                     size_t dst_len,
889                                     size_t *off,
890                                     const struct GNUNET_DNSPARSER_Query *query)
891 {
892   int ret;
893   struct GNUNET_TUN_DnsQueryLine ql;
894
895   ret = GNUNET_DNSPARSER_builder_add_name (dst, dst_len - sizeof (struct GNUNET_TUN_DnsQueryLine), off, query->name);
896   if (ret != GNUNET_OK)
897     return ret;
898   ql.type = htons (query->type);
899   ql.dns_traffic_class = htons (query->dns_traffic_class);
900   GNUNET_memcpy (&dst[*off], &ql, sizeof (ql));
901   (*off) += sizeof (ql);
902   return GNUNET_OK;
903 }
904
905
906 /**
907  * Add an MX record to the UDP packet at the given location.
908  *
909  * @param dst where to write the mx record
910  * @param dst_len number of bytes in @a dst
911  * @param off pointer to offset where to write the mx information (increment by bytes used);
912  *            can also change if there was an error
913  * @param mx mx information to write
914  * @return #GNUNET_SYSERR if @a mx is invalid
915  *         #GNUNET_NO if @a mx did not fit
916  *         #GNUNET_OK if @a mx was added to @a dst
917  */
918 int
919 GNUNET_DNSPARSER_builder_add_mx (char *dst,
920                                  size_t dst_len,
921                                  size_t *off,
922                                  const struct GNUNET_DNSPARSER_MxRecord *mx)
923 {
924   uint16_t mxpref;
925
926   if (*off + sizeof (uint16_t) > dst_len)
927     return GNUNET_NO;
928   mxpref = htons (mx->preference);
929   GNUNET_memcpy (&dst[*off],
930                  &mxpref,
931                  sizeof (mxpref));
932   (*off) += sizeof (mxpref);
933   return GNUNET_DNSPARSER_builder_add_name (dst,
934                                             dst_len,
935                                             off,
936                                             mx->mxhost);
937 }
938
939
940 /**
941  * Add a CERT record to the UDP packet at the given location.
942  *
943  * @param dst where to write the CERT record
944  * @param dst_len number of bytes in @a dst
945  * @param off pointer to offset where to write the CERT information (increment by bytes used);
946  *            can also change if there was an error
947  * @param cert CERT information to write
948  * @return #GNUNET_SYSERR if @a cert is invalid
949  *         #GNUNET_NO if @a cert did not fit
950  *         #GNUNET_OK if @a cert was added to @a dst
951  */
952 int
953 GNUNET_DNSPARSER_builder_add_cert (char *dst,
954                                    size_t dst_len,
955                                    size_t *off,
956                                    const struct GNUNET_DNSPARSER_CertRecord *cert)
957 {
958   struct GNUNET_TUN_DnsCertRecord dcert;
959
960   if ( (cert->cert_type > 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],
1049                  &sd,
1050                  sizeof (sd));
1051   (*off) += sizeof (sd);
1052   if (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
1053                                                              dst_len,
1054                                                              off,
1055                                                              srv->target)))
1056     return ret;
1057   return GNUNET_OK;
1058 }
1059
1060
1061 /**
1062  * Add a DNS record to the UDP packet at the given location.
1063  *
1064  * @param dst where to write the query
1065  * @param dst_len number of bytes in @a dst
1066  * @param off pointer to offset where to write the query (increment by bytes used)
1067  *            must not be changed if there is an error
1068  * @param record record to write
1069  * @return #GNUNET_SYSERR if @a record is invalid
1070  *         #GNUNET_NO if @a record did not fit
1071  *         #GNUNET_OK if @a record was added to @a dst
1072  */
1073 static int
1074 add_record (char *dst,
1075             size_t dst_len,
1076             size_t *off,
1077             const struct GNUNET_DNSPARSER_Record *record)
1078 {
1079   int ret;
1080   size_t start;
1081   size_t pos;
1082   struct GNUNET_TUN_DnsRecordLine rl;
1083
1084   start = *off;
1085   ret = GNUNET_DNSPARSER_builder_add_name (dst,
1086                                            dst_len - sizeof (struct GNUNET_TUN_DnsRecordLine),
1087                                            off,
1088                                            record->name);
1089   if (GNUNET_OK != ret)
1090     return ret;
1091   /* '*off' is now the position where we will need to write the record line */
1092
1093   pos = *off + sizeof (struct GNUNET_TUN_DnsRecordLine);
1094   switch (record->type)
1095   {
1096   case GNUNET_DNSPARSER_TYPE_MX:
1097     ret = GNUNET_DNSPARSER_builder_add_mx (dst,
1098                                            dst_len,
1099                                            &pos,
1100                                            record->data.mx);
1101     break;
1102   case GNUNET_DNSPARSER_TYPE_CERT:
1103     ret = GNUNET_DNSPARSER_builder_add_cert (dst,
1104                                              dst_len,
1105                                              &pos,
1106                                              record->data.cert);
1107     break;
1108   case GNUNET_DNSPARSER_TYPE_SOA:
1109     ret = GNUNET_DNSPARSER_builder_add_soa (dst,
1110                                             dst_len,
1111                                             &pos,
1112                                             record->data.soa);
1113     break;
1114   case GNUNET_DNSPARSER_TYPE_NS:
1115   case GNUNET_DNSPARSER_TYPE_CNAME:
1116   case GNUNET_DNSPARSER_TYPE_PTR:
1117     ret = GNUNET_DNSPARSER_builder_add_name (dst,
1118                                              dst_len,
1119                                              &pos,
1120                                              record->data.hostname);
1121     break;
1122   case GNUNET_DNSPARSER_TYPE_SRV:
1123     ret = GNUNET_DNSPARSER_builder_add_srv (dst,
1124                                             dst_len,
1125                                             &pos,
1126                                             record->data.srv);
1127     break;
1128   default:
1129     if (pos + record->data.raw.data_len > dst_len)
1130     {
1131       ret = GNUNET_NO;
1132       break;
1133     }
1134     GNUNET_memcpy (&dst[pos],
1135                    record->data.raw.data,
1136                    record->data.raw.data_len);
1137     pos += record->data.raw.data_len;
1138     ret = GNUNET_OK;
1139     break;
1140   }
1141   if (GNUNET_OK != ret)
1142   {
1143     *off = start;
1144     return GNUNET_NO;
1145   }
1146
1147   if (pos - (*off + sizeof (struct GNUNET_TUN_DnsRecordLine)) > UINT16_MAX)
1148   {
1149     /* record data too long */
1150     *off = start;
1151     return GNUNET_NO;
1152   }
1153   rl.type = htons (record->type);
1154   rl.dns_traffic_class = htons (record->dns_traffic_class);
1155   rl.ttl = htonl (GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value_us / 1000LL / 1000LL); /* in seconds */
1156   rl.data_len = htons ((uint16_t) (pos - (*off + sizeof (struct GNUNET_TUN_DnsRecordLine))));
1157   GNUNET_memcpy (&dst[*off],
1158                  &rl,
1159                  sizeof (struct GNUNET_TUN_DnsRecordLine));
1160   *off = pos;
1161   return GNUNET_OK;
1162 }
1163
1164
1165 /**
1166  * Given a DNS packet @a p, generate the corresponding UDP payload.
1167  * Note that we do not attempt to pack the strings with pointers
1168  * as this would complicate the code and this is about being
1169  * simple and secure, not fast, fancy and broken like bind.
1170  *
1171  * @param p packet to pack
1172  * @param max maximum allowed size for the resulting UDP payload
1173  * @param buf set to a buffer with the packed message
1174  * @param buf_length set to the length of @a buf
1175  * @return #GNUNET_SYSERR if @a p is invalid
1176  *         #GNUNET_NO if @a p was truncated (but there is still a result in @a buf)
1177  *         #GNUNET_OK if @a p was packed completely into @a buf
1178  */
1179 int
1180 GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
1181                        uint16_t max,
1182                        char **buf,
1183                        size_t *buf_length)
1184 {
1185   struct GNUNET_TUN_DnsHeader dns;
1186   size_t off;
1187   char tmp[max];
1188   int ret;
1189   int trc;
1190
1191   if ( (p->num_queries > UINT16_MAX) ||
1192        (p->num_answers > UINT16_MAX) ||
1193        (p->num_authority_records > UINT16_MAX) ||
1194        (p->num_additional_records > UINT16_MAX) )
1195     return GNUNET_SYSERR;
1196   dns.id = p->id;
1197   dns.flags = p->flags;
1198   dns.query_count = htons (p->num_queries);
1199   dns.answer_rcount = htons (p->num_answers);
1200   dns.authority_rcount = htons (p->num_authority_records);
1201   dns.additional_rcount = htons (p->num_additional_records);
1202
1203   off = sizeof (struct GNUNET_TUN_DnsHeader);
1204   trc = GNUNET_NO;
1205   for (unsigned int i=0;i<p->num_queries;i++)
1206   {
1207     ret = GNUNET_DNSPARSER_builder_add_query (tmp,
1208                                               sizeof (tmp),
1209                                               &off,
1210                                               &p->queries[i]);
1211     if (GNUNET_SYSERR == ret)
1212       return GNUNET_SYSERR;
1213     if (GNUNET_NO == ret)
1214     {
1215       dns.query_count = htons ((uint16_t) (i-1));
1216       trc = GNUNET_YES;
1217       break;
1218     }
1219   }
1220   for (unsigned int i=0;i<p->num_answers;i++)
1221   {
1222     ret = add_record (tmp,
1223                       sizeof (tmp),
1224                       &off,
1225                       &p->answers[i]);
1226     if (GNUNET_SYSERR == ret)
1227       return GNUNET_SYSERR;
1228     if (GNUNET_NO == ret)
1229     {
1230       dns.answer_rcount = htons ((uint16_t) (i-1));
1231       trc = GNUNET_YES;
1232       break;
1233     }
1234   }
1235   for (unsigned int i=0;i<p->num_authority_records;i++)
1236   {
1237     ret = add_record (tmp,
1238                       sizeof (tmp),
1239                       &off,
1240                       &p->authority_records[i]);
1241     if (GNUNET_SYSERR == ret)
1242       return GNUNET_SYSERR;
1243     if (GNUNET_NO == ret)
1244     {
1245       dns.authority_rcount = htons ((uint16_t) (i-1));
1246       trc = GNUNET_YES;
1247       break;
1248     }
1249   }
1250   for (unsigned int i=0;i<p->num_additional_records;i++)
1251   {
1252     ret = add_record (tmp,
1253                       sizeof (tmp),
1254                       &off,
1255                       &p->additional_records[i]);
1256     if (GNUNET_SYSERR == ret)
1257       return GNUNET_SYSERR;
1258     if (GNUNET_NO == ret)
1259     {
1260       dns.additional_rcount = htons (i-1);
1261       trc = GNUNET_YES;
1262       break;
1263     }
1264   }
1265
1266   if (GNUNET_YES == trc)
1267     dns.flags.message_truncated = 1;
1268   GNUNET_memcpy (tmp,
1269                  &dns,
1270                  sizeof (struct GNUNET_TUN_DnsHeader));
1271
1272   *buf = GNUNET_malloc (off);
1273   *buf_length = off;
1274   GNUNET_memcpy (*buf,
1275                  tmp,
1276                  off);
1277   if (GNUNET_YES == trc)
1278     return GNUNET_NO;
1279   return GNUNET_OK;
1280 }
1281
1282
1283 /**
1284  * Convert a block of binary data to HEX.
1285  *
1286  * @param data binary data to convert
1287  * @param data_size number of bytes in @a data
1288  * @return HEX string (lower case)
1289  */
1290 char *
1291 GNUNET_DNSPARSER_bin_to_hex (const void *data,
1292                              size_t data_size)
1293 {
1294   char *ret;
1295   size_t off;
1296   const uint8_t *idata;
1297
1298   idata = data;
1299   ret = GNUNET_malloc (data_size * 2 + 1);
1300   for (off = 0; off < data_size; off++)
1301     sprintf (&ret[off * 2],
1302              "%02x",
1303              idata[off]);
1304   return ret;
1305 }
1306
1307
1308 /**
1309  * Convert a HEX string to block of binary data.
1310  *
1311  * @param hex HEX string to convert (may contain mixed case)
1312  * @param data where to write result, must be
1313  *             at least `strlen(hex)/2` bytes long
1314  * @return number of bytes written to data
1315  */
1316 size_t
1317 GNUNET_DNSPARSER_hex_to_bin (const char *hex,
1318                              void *data)
1319 {
1320   size_t data_size;
1321   size_t off;
1322   uint8_t *idata;
1323   unsigned int h;
1324   char in[3];
1325
1326   data_size = strlen (hex) / 2;
1327   idata = data;
1328   in[2] = '\0';
1329   for (off = 0; off < data_size; off++)
1330   {
1331     in[0] = tolower ((unsigned char) hex[off * 2]);
1332     in[1] = tolower ((unsigned char) hex[off * 2 + 1]);
1333     if (1 != sscanf (in, "%x", &h))
1334       return off;
1335     idata[off] = (uint8_t) h;
1336   }
1337   return off;
1338 }
1339
1340
1341 /* end of dnsparser.c */