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