-getting rid of duplication of record type values / enum GNUNET_GNS_RecordType
[oweals/gnunet.git] / src / gns / gnunet-dns2gns.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 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  * @file gnunet-dns2gns.c
22  * @brief DNS server that translates DNS requests to GNS
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include <gnunet_util_lib.h>
27 #include <gnunet_dnsparser_lib.h>
28 #include <gnunet_gns_service.h>
29 #include <gnunet_dnsstub_lib.h>
30 #include "gns.h"
31
32 /**
33  * Timeout for DNS requests.
34  */
35 #define TIMEOUT GNUNET_TIME_UNIT_MINUTES
36
37 /**
38  * Default suffix
39  */
40 #define DNS_SUFFIX ".zkey.eu"
41
42 /**
43  * FCFS suffix
44  */
45 #define FCFS_SUFFIX "fcfs.zkey.eu"
46
47 /**
48  * Data kept per request.
49  */
50 struct Request
51 {
52   /**
53    * Socket to use for sending the reply.
54    */
55   struct GNUNET_NETWORK_Handle *lsock;
56
57   /**
58    * Destination address to use.
59    */
60   const void *addr;
61
62   /**
63    * Initially, this is the DNS request, it will then be
64    * converted to the DNS response.
65    */
66   struct GNUNET_DNSPARSER_Packet *packet;
67   
68   /**
69    * Our GNS request handle.
70    */
71   struct GNUNET_GNS_LookupRequest *lookup;
72
73   /**
74    * Our DNS request handle
75    */
76   struct GNUNET_DNSSTUB_RequestSocket *dns_lookup;
77
78   /**
79    * Task run on timeout or shutdown to clean up without
80    * response.
81    */
82   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
83
84   /**
85    * Number of bytes in 'addr'.
86    */ 
87   size_t addr_len;
88
89 };
90
91
92 /**
93  * Handle to GNS resolver.
94  */
95 struct GNUNET_GNS_Handle *gns;
96
97 /**
98  * Stub resolver
99  */
100 struct GNUNET_DNSSTUB_Context *dns_stub;
101
102 /**
103  * Listen socket for IPv4.
104  */
105 static struct GNUNET_NETWORK_Handle *listen_socket4;
106
107 /**
108  * Listen socket for IPv6.
109  */
110 static struct GNUNET_NETWORK_Handle *listen_socket6;
111
112 /**
113  * Task for IPv4 socket.
114  */
115 static GNUNET_SCHEDULER_TaskIdentifier t4;
116
117 /**
118  * Task for IPv6 socket.
119  */
120 static GNUNET_SCHEDULER_TaskIdentifier t6;
121
122 /**
123  * DNS suffix, suffix of this gateway in DNS; defaults to '.zkey.eu'
124  */
125 static char *dns_suffix;
126
127 /**
128  * FCFS suffix, suffix of FCFS-authority in DNS; defaults to 'fcfs.zkey.eu'.
129  */
130 static char *fcfs_suffix;
131
132 /**
133  * IP of DNS server
134  */
135 static char *dns_ip;
136
137 /**
138  * UDP Port we listen on for inbound DNS requests.
139  */
140 static unsigned int listen_port = 53;
141
142
143 /**
144  * Task run on shutdown.  Cleans up everything.
145  *
146  * @param cls unused
147  * @param tc scheduler context
148  */
149 static void
150 do_shutdown (void *cls,
151              const struct GNUNET_SCHEDULER_TaskContext *tc)
152 {
153   if (GNUNET_SCHEDULER_NO_TASK != t4)
154     GNUNET_SCHEDULER_cancel (t4);
155   if (GNUNET_SCHEDULER_NO_TASK != t6)
156     GNUNET_SCHEDULER_cancel (t6);
157   if (NULL != listen_socket4)
158   {
159     GNUNET_NETWORK_socket_close (listen_socket4);
160     listen_socket4 = NULL;
161   }
162   if (NULL != listen_socket6)
163   {
164     GNUNET_NETWORK_socket_close (listen_socket6);
165     listen_socket6 = NULL;
166   }
167   GNUNET_GNS_disconnect (gns);
168   gns = NULL;
169   GNUNET_DNSSTUB_stop (dns_stub);
170   dns_stub = NULL;
171 }
172
173
174 /**
175  * Send the response for the given request and clean up.
176  *
177  * @param request context for the request.
178  */
179 static void
180 send_response (struct Request *request)
181 {
182   char *buf;
183   size_t size;
184   
185   if (GNUNET_SYSERR ==
186       GNUNET_DNSPARSER_pack (request->packet,
187                              UINT16_MAX /* is this not too much? */,
188                              &buf,
189                              &size))
190     {
191       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
192                   _("Failed to pack DNS response into UDP packet!\n"));
193     }
194   else
195     {
196       if (size !=
197           GNUNET_NETWORK_socket_sendto (request->lsock,
198                                         buf, size,
199                                         request->addr,
200                                         request->addr_len))
201         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "sendto");
202       GNUNET_free (buf);
203     }
204   GNUNET_SCHEDULER_cancel (request->timeout_task);
205   GNUNET_DNSPARSER_free_packet (request->packet);
206   GNUNET_free (request);
207 }
208
209
210 /**
211  * Task run on timeout.  Cleans up request.
212  *
213  * @param cls 'struct Request' of the request to clean up
214  * @param tc scheduler context
215  */
216 static void
217 do_timeout (void *cls,
218             const struct GNUNET_SCHEDULER_TaskContext *tc)
219 {
220   struct Request *request = cls;
221
222   if (NULL != request->packet)
223     GNUNET_DNSPARSER_free_packet (request->packet);
224   if (NULL != request->lookup)
225     GNUNET_GNS_cancel_lookup_request (request->lookup);
226   if (NULL != request->dns_lookup)
227     GNUNET_DNSSTUB_resolve_cancel (request->dns_lookup);
228   GNUNET_free (request);
229 }
230
231
232 /**
233  * Iterator called on obtained result for a DNS
234  * lookup
235  *
236  * @param cls closure
237  * @param rs the request socket
238  * @param dns the DNS udp payload
239  * @param r size of the DNS payload
240  */
241 static void
242 dns_result_processor (void *cls,
243                   struct GNUNET_DNSSTUB_RequestSocket *rs,
244                   const struct GNUNET_TUN_DnsHeader *dns,
245                   size_t r)
246 {
247   struct Request *request = cls;
248
249   request->packet = GNUNET_DNSPARSER_parse ((char*)dns, r);
250   send_response (request);
251 }
252
253
254 /**
255  * Iterator called on obtained result for a GNS
256  * lookup
257  *
258  * @param cls closure
259  * @param rd_count number of records
260  * @param rd the records in reply
261  */
262 static void
263 result_processor (void *cls,
264                   uint32_t rd_count,
265                   const struct GNUNET_NAMESTORE_RecordData *rd)
266 {
267   struct Request *request = cls;
268   struct GNUNET_DNSPARSER_Packet *packet;
269   uint32_t i;
270   struct GNUNET_DNSPARSER_Record rec;
271
272   request->lookup = NULL;
273   packet = request->packet;
274   packet->flags.query_or_response = 1;
275   packet->flags.return_code = GNUNET_DNSPARSER_RETURN_CODE_NO_ERROR;
276   packet->flags.checking_disabled = 0;
277   packet->flags.authenticated_data = 1;
278   packet->flags.zero = 0;
279   packet->flags.recursion_available = 1;
280   packet->flags.message_truncated = 0;
281   packet->flags.authoritative_answer = 0;
282   //packet->flags.opcode = GNUNET_DNSPARSER_OPCODE_STATUS; // ???
283   for (i=0;i<rd_count;i++)
284     {
285       rec.expiration_time.abs_value_us = rd[i].expiration_time;
286       switch (rd[i].record_type)
287         {
288         case GNUNET_DNSPARSER_TYPE_A:
289           GNUNET_assert (sizeof (struct in_addr) == rd[i].data_size);
290           rec.name = GNUNET_strdup (packet->queries[0].name);
291           rec.class = GNUNET_DNSPARSER_CLASS_INTERNET;
292           rec.type = GNUNET_DNSPARSER_TYPE_A;
293           rec.data.raw.data = GNUNET_malloc (sizeof (struct in_addr));
294           memcpy (rec.data.raw.data,
295                   rd[i].data,
296                   rd[i].data_size);
297           rec.data.raw.data_len = sizeof (struct in_addr);
298           GNUNET_array_append (packet->answers,
299                                packet->num_answers,
300                                rec);
301           break;
302         case GNUNET_DNSPARSER_TYPE_AAAA:
303           GNUNET_assert (sizeof (struct in6_addr) == rd[i].data_size);
304           rec.name = GNUNET_strdup (packet->queries[0].name);
305           rec.data.raw.data = GNUNET_malloc (sizeof (struct in6_addr));
306           rec.class = GNUNET_DNSPARSER_CLASS_INTERNET;
307           rec.type = GNUNET_DNSPARSER_TYPE_AAAA;
308           memcpy (rec.data.raw.data,
309                   rd[i].data,
310                   rd[i].data_size);
311           rec.data.raw.data_len = sizeof (struct in6_addr);
312           GNUNET_array_append (packet->answers,
313                                packet->num_answers,
314                                rec);
315           break;
316         case GNUNET_DNSPARSER_TYPE_CNAME:
317           rec.name = GNUNET_strdup (packet->queries[0].name);
318           rec.data.hostname = strdup (rd[i].data);
319           rec.class = GNUNET_DNSPARSER_CLASS_INTERNET;
320           rec.type = GNUNET_DNSPARSER_TYPE_CNAME;
321           memcpy (rec.data.hostname,
322                   rd[i].data,
323                   rd[i].data_size);
324           GNUNET_array_append (packet->answers,
325                                packet->num_answers,
326                                rec);
327           break;
328         default:
329           /* skip */
330           break;
331         }
332     }
333   send_response (request);
334 }
335
336
337 /**
338  * Handle DNS request.
339  *
340  * @param lsock socket to use for sending the reply
341  * @param addr address to use for sending the reply
342  * @param addr_len number of bytes in addr
343  * @param udp_msg DNS request payload
344  * @param udp_msg_size number of bytes in udp_msg 
345  */
346 static void
347 handle_request (struct GNUNET_NETWORK_Handle *lsock,
348                 const void *addr,
349                 size_t addr_len,
350                 const char *udp_msg,
351                 size_t udp_msg_size)
352 {
353   struct Request *request;
354   struct GNUNET_DNSPARSER_Packet *packet;
355   char *name;
356   char *dot;
357   char *nname;
358   size_t name_len;
359   int type;
360   int use_gns;
361   struct GNUNET_CRYPTO_ShortHashCode zone;
362
363   packet = GNUNET_DNSPARSER_parse (udp_msg, udp_msg_size);
364   if (NULL == packet)
365     {
366       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
367                   _("Cannot parse DNS request from %s\n"),
368                   GNUNET_a2s (addr, addr_len));
369       return;
370     }
371   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
372               "Received request for `%s' with flags %u, #answers %d, #auth %d, #additional %d\n",
373               packet->queries[0].name,
374               (unsigned int) packet->flags.query_or_response,
375               (int) packet->num_answers,
376               (int) packet->num_authority_records,
377               (int) packet->num_additional_records);
378   if ( (0 != packet->flags.query_or_response) || 
379        (0 != packet->num_answers) ||
380        (0 != packet->num_authority_records))
381     {
382       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
383                   _("Received malformed DNS request from %s\n"),
384                   GNUNET_a2s (addr, addr_len));
385       GNUNET_DNSPARSER_free_packet (packet);
386       return;
387     }
388   if ( (1 != packet->num_queries) )
389     {
390       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
391                   _("Received unsupported DNS request from %s\n"),
392                   GNUNET_a2s (addr, addr_len));
393       GNUNET_DNSPARSER_free_packet (packet);
394       return;
395     }
396   request = GNUNET_malloc (sizeof (struct Request) + addr_len);
397   request->lsock = lsock;
398   request->packet = packet;
399   request->addr = &request[1];
400   request->addr_len = addr_len;
401   memcpy (&request[1], addr, addr_len);
402   request->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
403                                                         &do_timeout,
404                                                         request);
405   name = GNUNET_strdup (packet->queries[0].name);
406   name_len = strlen (name);
407   use_gns = GNUNET_NO;
408   if ( (name_len > strlen (dns_suffix)) &&
409        (0 == strcasecmp (dns_suffix,
410                          &name[name_len - strlen (dns_suffix)])) )
411     {
412       /* Test if '.zkey' was requested */
413       name[name_len - strlen (dns_suffix)] = '\0';
414       dot = strrchr (name, (int) '.');
415       if ( (NULL != dot) &&
416            (GNUNET_OK ==
417             GNUNET_CRYPTO_short_hash_from_string (dot + 1, &zone)) )
418       {
419         /* valid '.zkey' name */
420         GNUNET_asprintf (&nname, 
421                          "%s.%s", 
422                          name, 
423                          GNUNET_GNS_TLD_ZKEY);
424         GNUNET_free (name);
425         name = nname;
426       }
427       else
428       { 
429         /* try '.gads' name */
430         GNUNET_asprintf (&nname, 
431                          "%s.%s", 
432                          name, 
433                          GNUNET_GNS_TLD);
434         GNUNET_free (name);
435         name = nname;
436       }
437       name_len = strlen (name);
438     }
439   if ( (name_len >= strlen ((GNUNET_GNS_TLD))) &&
440        (0 == strcasecmp (GNUNET_GNS_TLD,
441                          &name[name_len - strlen (GNUNET_GNS_TLD)])) )
442     use_gns = GNUNET_YES;
443
444   if ( (name_len > strlen (GNUNET_GNS_TLD_ZKEY)) &&
445        (0 == strcasecmp (GNUNET_GNS_TLD_ZKEY,
446                          &name[name_len - strlen (GNUNET_GNS_TLD_ZKEY)])) )
447     use_gns = GNUNET_YES;
448
449   if (GNUNET_YES == use_gns)
450   {
451       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
452                    "Calling GNS\n");
453       type = packet->queries[0].type;
454       request->lookup = GNUNET_GNS_lookup (gns,
455                                            name,
456                                            type,
457                                            GNUNET_NO,
458                                            NULL,
459                                            &result_processor,
460                                            request);
461     }
462   else
463     {
464       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
465                    "Calling DNS at %s\n", dns_ip);
466       GNUNET_DNSPARSER_free_packet (request->packet);
467       request->packet = NULL;
468       request->dns_lookup = GNUNET_DNSSTUB_resolve2 (dns_stub,
469                                                      udp_msg,
470                                                      udp_msg_size,
471                                                      &dns_result_processor,
472                                                      request);
473     }
474   GNUNET_free (name);
475 }
476
477
478 /**
479  * Task to read IPv4 DNS packets.
480  *
481  * @param cls the 'listen_socket4'
482  * @param tc scheduler context
483  */ 
484 static void
485 read_dns4 (void *cls,
486            const struct GNUNET_SCHEDULER_TaskContext *tc)
487 {
488   struct sockaddr_in v4;
489   socklen_t addrlen;
490   ssize_t size;
491
492   GNUNET_assert (listen_socket4 == cls);
493   t4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
494                                       listen_socket4,
495                                       &read_dns4,
496                                       listen_socket4);
497   if (0 == (GNUNET_SCHEDULER_REASON_READ_READY & tc->reason))
498     return; /* shutdown? */
499   size = GNUNET_NETWORK_socket_recvfrom_amount (listen_socket4);
500   if (0 > size)
501     {
502       GNUNET_break (0);
503       return; /* read error!? */
504     }
505   {
506     char buf[size];
507     
508     addrlen = sizeof (v4);
509     GNUNET_break (size == 
510                   GNUNET_NETWORK_socket_recvfrom (listen_socket4,
511                                                   buf,
512                                                   size,
513                                                   (struct sockaddr *) &v4,
514                                                   &addrlen));
515     handle_request (listen_socket4, &v4, addrlen,
516                     buf, size);
517   }
518 }
519
520
521 /**
522  * Task to read IPv6 DNS packets.
523  *
524  * @param cls the 'listen_socket6'
525  * @param tc scheduler context
526  */ 
527 static void
528 read_dns6 (void *cls,
529            const struct GNUNET_SCHEDULER_TaskContext *tc)
530 {
531   struct sockaddr_in6 v6;
532   socklen_t addrlen;
533   ssize_t size;
534
535   GNUNET_assert (listen_socket6 == cls);
536   t6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
537                                       listen_socket6,
538                                       &read_dns6,
539                                       listen_socket6);
540   if (0 == (GNUNET_SCHEDULER_REASON_READ_READY & tc->reason))
541     return; /* shutdown? */
542   size = GNUNET_NETWORK_socket_recvfrom_amount (listen_socket6);
543   if (0 > size)
544     {
545       GNUNET_break (0);
546       return; /* read error!? */
547     }
548   {
549     char buf[size];
550     
551     addrlen = sizeof (v6);
552     GNUNET_break (size == 
553                   GNUNET_NETWORK_socket_recvfrom (listen_socket6,
554                                                   buf,
555                                                   size,
556                                                   (struct sockaddr *) &v6,
557                                                   &addrlen));
558     handle_request (listen_socket6, &v6, addrlen,
559                     buf, size);
560   }
561 }
562
563
564 /**
565  * Main function that will be run.
566  *
567  * @param cls closure
568  * @param args remaining command-line arguments
569  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
570  * @param cfg configuration
571  */
572 static void
573 run (void *cls, char *const *args, const char *cfgfile,
574      const struct GNUNET_CONFIGURATION_Handle *cfg)
575 {
576   if (NULL == dns_ip)
577   {
578     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
579                 "No DNS server specified!\n");
580     return;
581   }
582
583   if (NULL == dns_suffix)
584     dns_suffix = DNS_SUFFIX;
585   if (NULL == fcfs_suffix)
586     fcfs_suffix = FCFS_SUFFIX;
587   if (NULL == (gns = GNUNET_GNS_connect (cfg)))
588     return;
589   if (NULL == (dns_stub = GNUNET_DNSSTUB_start (dns_ip)))
590     {
591       GNUNET_GNS_disconnect (gns);
592       gns = NULL;
593       return;
594     }
595   listen_socket4 = GNUNET_NETWORK_socket_create (PF_INET,
596                                                  SOCK_DGRAM, 
597                                                  IPPROTO_UDP);
598   if (NULL != listen_socket4)
599     {
600       struct sockaddr_in v4;
601
602       memset (&v4, 0, sizeof (v4));
603       v4.sin_family = AF_INET;
604 #if HAVE_SOCKADDR_IN_SIN_LEN
605       v4.sin_len = sizeof (v4);
606 #endif
607       v4.sin_port = htons (listen_port);
608       if (GNUNET_OK !=
609           GNUNET_NETWORK_socket_bind (listen_socket4,
610                                       (struct sockaddr *) &v4,
611                                       sizeof (v4),
612                                       0))
613         {
614           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
615           GNUNET_NETWORK_socket_close (listen_socket4);
616           listen_socket4 = NULL;
617         }
618     }
619   listen_socket6 = GNUNET_NETWORK_socket_create (PF_INET6,
620                                                 SOCK_DGRAM, 
621                                                 IPPROTO_UDP);
622   if (NULL != listen_socket6)
623     {
624       struct sockaddr_in6 v6;
625
626       memset (&v6, 0, sizeof (v6));
627       v6.sin6_family = AF_INET6;
628 #if HAVE_SOCKADDR_IN_SIN_LEN
629       v6.sin6_len = sizeof (v6);
630 #endif
631       v6.sin6_port = htons (listen_port);
632       if (GNUNET_OK !=
633           GNUNET_NETWORK_socket_bind (listen_socket6,
634                                       (struct sockaddr *) &v6,
635                                       sizeof (v6),
636                                       0))
637         {
638           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
639           GNUNET_NETWORK_socket_close (listen_socket6);
640           listen_socket6 = NULL;
641         }
642     }
643   if ( (NULL == listen_socket4) &&
644        (NULL == listen_socket6) )
645     {
646       GNUNET_GNS_disconnect (gns);
647       gns = NULL;
648       GNUNET_DNSSTUB_stop (dns_stub);
649       dns_stub = NULL;
650       return;
651     }
652   if (NULL != listen_socket4)
653     t4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
654                                         listen_socket4,
655                                         &read_dns4,
656                                         listen_socket4);
657   if (NULL != listen_socket6)
658     t6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
659                                         listen_socket6,
660                                         &read_dns6,
661                                         listen_socket6);
662
663   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
664                                 &do_shutdown, NULL);
665 }
666
667
668 /**
669  * The main function for the fcfs daemon.
670  *
671  * @param argc number of arguments from the command line
672  * @param argv command line arguments
673  * @return 0 ok, 1 on error
674  */
675 int
676 main (int argc, 
677       char *const *argv)
678 {
679   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
680     {'d', "dns", "IP",
681       gettext_noop ("IP of recursive DNS resolver to use (required)"), 1,
682       &GNUNET_GETOPT_set_string, &dns_ip},
683     {'s', "suffix", "SUFFIX",
684       gettext_noop ("Authoritative DNS suffix to use (optional); default: zkey.eu"), 1,
685       &GNUNET_GETOPT_set_string, &dns_suffix},
686     {'f', "fcfs", "NAME",
687       gettext_noop ("Authoritative FCFS suffix to use (optional); default: fcfs.zkey.eu"), 1,
688       &GNUNET_GETOPT_set_string, &fcfs_suffix},
689     {'p', "port", "UDPPORT",
690       gettext_noop ("UDP port to listen on for inbound DNS requests; default: 53"), 1,
691       &GNUNET_GETOPT_set_uint, &listen_port},
692     GNUNET_GETOPT_OPTION_END
693   };
694   int ret;
695
696   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv,
697                                                  &argc, &argv))
698     return 2;
699   GNUNET_log_setup ("gnunet-dns2gns", "WARNING", NULL);
700   ret =
701       (GNUNET_OK ==
702        GNUNET_PROGRAM_run (argc, argv, "gnunet-dns2gns",
703                            _("GNUnet DNS-to-GNS proxy (a DNS server)"), 
704                            options,
705                            &run, NULL)) ? 0 : 1;
706   GNUNET_free ((void*) argv);
707   return ret;
708 }
709
710 /* end of gnunet-dns2gns.c */