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