4d0ed572371dca0499bc09f4cb7c0a80f0bb2eba
[oweals/gnunet.git] / src / nat / gnunet-nat.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2015, 2016 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 /**
22  * @file src/nat/gnunet-nat.c
23  * @brief Command-line tool to interact with the NAT service
24  * @author Christian Grothoff
25  * @author Bruno Cabral
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_nat_service.h"
30
31 /**
32  * Value to return from #main().
33  */
34 static int global_ret;
35
36 /**
37  * Handle to ongoing autoconfiguration.
38  */
39 static struct GNUNET_NAT_AutoHandle *ah;
40
41 /**
42  * Port we advertise.
43  */ 
44 static unsigned int adv_port;
45
46 /**
47  * Flag set to 1 if we use IPPROTO_UDP.
48  */
49 static int use_udp;
50
51 /**
52  * Flag set to 1 if we are to listen for connection reversal requests.
53  */
54 static int listen_reversal;
55
56 /**
57  * Flag set to 1 if we use IPPROTO_TCP.
58  */
59 static int use_tcp;
60
61 /**
62  * If we do auto-configuration, should we write the result
63  * to a file?
64  */
65 static int write_cfg;
66
67 /**
68  * Configuration filename.
69  */ 
70 static const char *cfg_file;
71
72 /**
73  * Original configuration.
74  */
75 static const struct GNUNET_CONFIGURATION_Handle *cfg;
76
77 /**
78  * Protocol to use.
79  */
80 static uint8_t proto;
81
82 /**
83  * Address we are bound to (in test), or should bind to
84  * (if #do_stun is set).
85  */
86 static char *bind_addr;
87
88 /**
89  * External IP address and port to use for the test.
90  * If not set, use #bind_addr.
91  */
92 static char *extern_addr;
93
94 /**
95  * Local address to use for connection reversal request.
96  */
97 static char *local_addr;
98
99 /**
100  * Remote address to use for connection reversal request.
101  */
102 static char *remote_addr;
103
104 /**
105  * Should we actually bind to #bind_addr and receive and process STUN requests?
106  */
107 static unsigned int do_stun;
108
109 /**
110  * Should we run autoconfiguration?
111  */
112 static unsigned int do_auto;
113
114 /**
115  * Handle to a NAT test operation.
116  */
117 static struct GNUNET_NAT_Test *nt;
118
119 /**
120  * Handle to NAT operation.
121  */
122 static struct GNUNET_NAT_Handle *nh;
123
124 /**
125  * Listen socket for STUN processing.
126  */ 
127 static struct GNUNET_NETWORK_Handle *ls;
128
129 /**
130  * Task for reading STUN packets.
131  */
132 static struct GNUNET_SCHEDULER_Task *rtask;
133
134
135 /**
136  * Test if all activities have finished, and if so,
137  * terminate.
138  */
139 static void
140 test_finished ()
141 {
142   if (NULL != ah)
143     return;
144   if (NULL != nt)
145     return;
146   if (NULL != nh)
147     return;
148   if (NULL != rtask)
149     return;
150   GNUNET_SCHEDULER_shutdown ();
151 }
152
153
154 /**
155  * Function to iterate over sugested changes options
156  *
157  * @param cls closure
158  * @param section name of the section
159  * @param option name of the option
160  * @param value value of the option
161  */
162 static void
163 auto_conf_iter (void *cls,
164                 const char *section,
165                 const char *option,
166                 const char *value)
167 {
168   struct GNUNET_CONFIGURATION_Handle *new_cfg = cls;
169   
170   PRINTF ("%s: %s\n",
171           option,
172           value);
173   if (NULL != new_cfg)
174     GNUNET_CONFIGURATION_set_value_string (new_cfg,
175                                            section,
176                                            option,
177                                            value);
178 }
179
180
181 /**
182  * Function called with the result from the autoconfiguration.
183  *
184  * @param cls closure
185  * @param diff minimal suggested changes to the original configuration
186  *             to make it work (as best as we can)
187  * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
188  * @param type what the situation of the NAT
189  */
190 static void
191 auto_config_cb (void *cls,
192                 const struct GNUNET_CONFIGURATION_Handle *diff,
193                 enum GNUNET_NAT_StatusCode result,
194                 enum GNUNET_NAT_Type type)
195 {
196   const char *nat_type;
197   char unknown_type[64];
198   struct GNUNET_CONFIGURATION_Handle *new_cfg;
199
200   ah = NULL;
201   switch (type)
202   {
203   case GNUNET_NAT_TYPE_NO_NAT:
204     nat_type = "NO NAT";
205     break;
206   case GNUNET_NAT_TYPE_UNREACHABLE_NAT:
207     nat_type = "NAT but we can traverse";
208     break;
209   case GNUNET_NAT_TYPE_STUN_PUNCHED_NAT:
210     nat_type = "NAT but STUN is able to identify the correct information";
211     break;
212   case GNUNET_NAT_TYPE_UPNP_NAT:
213     nat_type = "NAT but UPNP opened the ports";
214     break;
215   default:
216     SPRINTF (unknown_type,
217              "NAT unknown, type %u",
218              type);
219     nat_type = unknown_type;
220     break;
221   }
222
223   GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
224               "NAT status: %s/%s\n",
225               GNUNET_NAT_status2string (result),
226               nat_type);
227
228   /* Shortcut: if there are no changes suggested, bail out early. */
229   if (GNUNET_NO ==
230       GNUNET_CONFIGURATION_is_dirty (diff))
231   {
232     test_finished ();
233     return;
234   }
235
236   /* Apply diff to original configuration and show changes
237      to the user */
238   new_cfg = write_cfg ? GNUNET_CONFIGURATION_dup (cfg) : NULL;
239   
240   if (NULL != diff)
241   {
242     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
243                 _("Suggested configuration changes:\n"));
244     GNUNET_CONFIGURATION_iterate_section_values (diff,
245                                                  "nat",
246                                                  &auto_conf_iter,
247                                                  new_cfg);
248   }
249
250   /* If desired, write configuration to file; we write only the
251      changes to the defaults to keep things compact. */
252   if ( (write_cfg) &&
253        (NULL != diff) )
254   {
255     struct GNUNET_CONFIGURATION_Handle *def_cfg;
256
257     GNUNET_CONFIGURATION_set_value_string (new_cfg,
258                                            "ARM",
259                                            "CONFIG",
260                                            NULL);
261     def_cfg = GNUNET_CONFIGURATION_create ();
262     GNUNET_break (GNUNET_OK ==
263                   GNUNET_CONFIGURATION_load (def_cfg,
264                                              NULL));
265     if (GNUNET_OK !=
266         GNUNET_CONFIGURATION_write_diffs (def_cfg,
267                                           new_cfg,
268                                           cfg_file))
269     {
270       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
271                   _("Failed to write configuration to `%s'\n"),
272                   cfg_file);
273       global_ret = 1;
274     }
275     else
276     {
277       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
278                   _("Wrote updated configuration to `%s'\n"),
279                   cfg_file);
280     }
281     GNUNET_CONFIGURATION_destroy (def_cfg);
282   }
283
284   if (NULL != new_cfg)
285     GNUNET_CONFIGURATION_destroy (new_cfg);
286   test_finished ();
287 }
288
289
290 /**
291  * Function called to report success or failure for
292  * NAT configuration test.
293  *
294  * @param cls closure
295  * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
296  */
297 static void
298 test_report_cb (void *cls,
299                 enum GNUNET_NAT_StatusCode result)
300 {
301   nt = NULL;
302   PRINTF ("NAT test result: %s\n",
303           GNUNET_NAT_status2string (result));
304   test_finished ();
305 }
306
307
308 /**
309  * Signature of the callback passed to #GNUNET_NAT_register() for
310  * a function to call whenever our set of 'valid' addresses changes.
311  *
312  * @param cls closure, NULL
313  * @param add_remove #GNUNET_YES to add a new public IP address, 
314  *                   #GNUNET_NO to remove a previous (now invalid) one
315  * @param ac address class the address belongs to
316  * @param addr either the previous or the new public IP address
317  * @param addrlen actual length of the @a addr
318  */
319 static void
320 address_cb (void *cls,
321             int add_remove,
322             enum GNUNET_NAT_AddressClass ac,
323             const struct sockaddr *addr,
324             socklen_t addrlen)
325 {
326   GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
327               "%s %s (%d)\n",
328               add_remove ? "+" : "-",
329               GNUNET_a2s (addr,
330                           addrlen),
331               (int) ac);
332 }
333
334
335 /**
336  * Signature of the callback passed to #GNUNET_NAT_register().
337  * for a function to call whenever someone asks us to do connection
338  * reversal.
339  *
340  * @param cls closure, NULL
341  * @param local_addr address where we received the request
342  * @param local_addrlen actual length of the @a local_addr
343  * @param remote_addr public IP address of the other peer
344  * @param remote_addrlen actual length of the @a remote_addr
345  */
346 static void
347 reversal_cb (void *cls,
348              const struct sockaddr *local_addr,
349              socklen_t local_addrlen,
350              const struct sockaddr *remote_addr,
351              socklen_t remote_addrlen)
352 {
353   GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
354               "Connection reversal requested by %s\n",
355               GNUNET_a2s (remote_addr,
356                           remote_addrlen));
357 }
358
359
360 /**
361  * Task run on shutdown.
362  *
363  * @param cls NULL
364  */
365 static void
366 do_shutdown (void *cls)
367 {
368   if (NULL != ah)
369   {
370     GNUNET_NAT_autoconfig_cancel (ah);
371     ah = NULL;
372   }
373   if (NULL != nt)
374   {
375     GNUNET_NAT_test_stop (nt);
376     nt = NULL;
377   }
378   if (NULL != nh)
379   {
380     GNUNET_NAT_unregister (nh);
381     nh = NULL;
382   }
383   if (NULL != ls)
384   {
385     GNUNET_NETWORK_socket_close (ls);
386     ls = NULL;
387   }
388   if (NULL != rtask)
389   {
390     GNUNET_SCHEDULER_cancel (rtask);
391     rtask = NULL;
392   }
393 }
394
395
396 /**
397  * Task to receive incoming packets for STUN processing.
398  */
399 static void
400 stun_read_task (void *cls)
401 {
402   ssize_t size;
403   
404   rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
405                                          ls,
406                                          &stun_read_task,
407                                          NULL);
408   size = GNUNET_NETWORK_socket_recvfrom_amount (ls);
409   if (size > 0)
410   {
411     GNUNET_break (0);
412     GNUNET_SCHEDULER_shutdown ();
413     global_ret = 1;
414     return;
415   }
416   {
417     char buf[size + 1];
418     struct sockaddr_storage sa;
419     socklen_t salen = sizeof (sa);
420     ssize_t ret;
421     
422     ret = GNUNET_NETWORK_socket_recvfrom (ls,
423                                           buf,
424                                           size + 1,
425                                           (struct sockaddr *) &sa,
426                                           &salen);
427     if (ret != size)
428     {
429       GNUNET_break (0);
430       GNUNET_SCHEDULER_shutdown ();
431       global_ret = 1;
432       return;
433     }
434     (void) GNUNET_NAT_stun_handle_packet (nh,
435                                           (const struct sockaddr *) &sa,
436                                           salen,
437                                           buf,
438                                           ret);
439   }
440 }
441
442
443 /**
444  * Main function that will be run.
445  *
446  * @param cls closure
447  * @param args remaining command-line arguments
448  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
449  * @param c configuration
450  */
451 static void
452 run (void *cls,
453      char *const *args,
454      const char *cfgfile,
455      const struct GNUNET_CONFIGURATION_Handle *c)
456 {
457   uint8_t af;
458   struct sockaddr_in bind_sa;
459   struct sockaddr_in extern_sa;
460   struct sockaddr *local_sa;
461   struct sockaddr *remote_sa;
462   socklen_t local_len;
463   size_t remote_len;
464
465   cfg_file = cfgfile;
466   cfg = c;
467   
468   if (use_tcp && use_udp)
469   {
470     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
471                 "Cannot use TCP and UDP\n");
472     global_ret = 1;
473     return;
474   }
475   proto = 0;
476   if (use_tcp)
477     proto = IPPROTO_TCP;
478   if (use_udp)
479     proto = IPPROTO_UDP;
480
481   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
482                                  NULL);
483
484   if (do_auto)
485   {
486     ah = GNUNET_NAT_autoconfig_start (c,
487                                       &auto_config_cb,
488                                       NULL);
489   }
490
491   if (0 == proto)
492   {
493     if (do_auto)
494       return; /* all good, we just run auto config */
495     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
496                 "Must specify either TCP or UDP\n");
497     global_ret = 1;
498     return;
499   }
500   if (NULL != bind_addr)
501   {
502     if (GNUNET_OK !=
503         GNUNET_STRINGS_to_address_ipv4 (bind_addr,
504                                         strlen (bind_addr),
505                                         &bind_sa))
506     {
507       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
508                   "Invalid socket address `%s'\n",
509                   bind_addr);
510       global_ret = 1;
511       return;
512     }
513   }
514   if (NULL != extern_addr)
515   {
516     if (GNUNET_OK !=
517         GNUNET_STRINGS_to_address_ipv4 (extern_addr,
518                                         strlen (extern_addr),
519                                         &extern_sa))
520     {
521       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
522                   "Invalid socket address `%s'\n",
523                   extern_addr);
524       global_ret = 1;
525       return;
526     }
527   }
528   if (NULL != local_addr)
529   {
530     local_len = (socklen_t) GNUNET_STRINGS_parse_socket_addr (local_addr,
531                                                               &af,
532                                                               &local_sa);
533     if (0 == local_len)
534     {
535       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
536                   "Invalid socket address `%s'\n",
537                   local_addr);
538       global_ret = 1;
539       return;
540     }
541   }
542   if (NULL != remote_addr)
543   {
544     remote_len = GNUNET_STRINGS_parse_socket_addr (remote_addr,
545                                                    &af,
546                                                    &remote_sa);
547     if (0 == remote_len)
548     {
549       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
550                   "Invalid socket address `%s'\n",
551                   remote_addr);
552       global_ret = 1;
553       return;
554     }
555   }
556
557   if (NULL != bind_addr)
558   {
559     if (NULL == extern_addr)
560       extern_sa = bind_sa;
561     nt = GNUNET_NAT_test_start (c,
562                                 proto,
563                                 bind_sa.sin_addr,
564                                 ntohs (bind_sa.sin_port),
565                                 extern_sa.sin_addr,
566                                 ntohs (extern_sa.sin_port),
567                                 &test_report_cb,
568                                 NULL);
569   }
570
571   if (NULL != local_addr)
572   {
573     nh = GNUNET_NAT_register (c,
574                               proto,
575                               (uint16_t) adv_port,
576                               1,
577                               (const struct sockaddr **) &local_sa,
578                               &local_len,
579                               &address_cb,
580                               (listen_reversal) ? &reversal_cb : NULL,
581                               NULL);
582   }
583
584   if (NULL != remote_addr)
585   {
586     int ret;
587     
588     if ( (NULL == nh) ||
589          (sizeof (struct sockaddr_in) != local_len) )
590     {
591       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
592                   "Require IPv4 local address to initiate connection reversal\n");
593       global_ret = 1;
594       GNUNET_SCHEDULER_shutdown ();
595       return;
596     }
597     if (sizeof (struct sockaddr_in) != remote_len)
598     {
599       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
600                   "Require IPv4 reversal target address\n");
601       global_ret = 1;
602       GNUNET_SCHEDULER_shutdown ();
603       return;
604     }
605     ret = GNUNET_NAT_request_reversal (nh,
606                                        (const struct sockaddr_in *) &local_sa,
607                                        (const struct sockaddr_in *) &remote_sa);
608     switch (ret)
609     {
610     case GNUNET_SYSERR:
611       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
612                   "Connection reversal internal error\n");
613       break;
614     case GNUNET_NO:
615       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
616                   "Connection reversal unavailable\n");
617       break;
618     case GNUNET_OK:
619       /* operation in progress */
620       break;
621     }
622   }
623   
624   if (do_stun)
625   {
626     if (NULL == local_addr)
627     {
628       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
629                   "Require local address to support STUN requests\n");
630       global_ret = 1;
631       GNUNET_SCHEDULER_shutdown ();
632       return;
633     }
634     if (IPPROTO_UDP != proto)
635     {
636       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
637                   "STUN only supported over UDP\n");
638       global_ret = 1;
639       GNUNET_SCHEDULER_shutdown ();
640       return;
641     }
642     ls = GNUNET_NETWORK_socket_create (af,
643                                        SOCK_DGRAM,
644                                        IPPROTO_UDP);
645     if (GNUNET_OK !=
646         GNUNET_NETWORK_socket_bind (ls,
647                                     local_sa,
648                                     local_len))
649     {
650       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
651                   "Failed to bind to %s: %s\n",
652                   GNUNET_a2s (local_sa,
653                               local_len),
654                   STRERROR (errno));
655       global_ret = 1;
656       GNUNET_SCHEDULER_shutdown ();
657       return;
658     }
659     rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
660                                            ls,
661                                            &stun_read_task,
662                                            NULL);
663   }
664
665   test_finished ();
666 }
667
668
669 /**
670  * Main function of gnunet-nat
671  *
672  * @param argc number of command-line arguments
673  * @param argv command line
674  * @return 0 on success, -1 on error
675  */
676 int
677 main (int argc,
678       char *const argv[])
679 {
680   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
681     {'a', "auto", NULL,
682      gettext_noop ("run autoconfiguration"),
683      GNUNET_NO, &GNUNET_GETOPT_set_one, &do_auto },
684     {'b', "bind", "ADDRESS",
685      gettext_noop ("which IP and port are we bound to"),
686      GNUNET_YES, &GNUNET_GETOPT_set_string, &bind_addr },
687     {'e', "external", "ADDRESS",
688      gettext_noop ("which external IP and port should be used to test"),
689      GNUNET_YES, &GNUNET_GETOPT_set_string, &extern_addr },
690     {'i', "in", "ADDRESS",
691      gettext_noop ("which IP and port are we locally using to bind/listen to"),
692      GNUNET_YES, &GNUNET_GETOPT_set_string, &local_addr },
693     {'r', "remote", "ADDRESS",
694      gettext_noop ("which remote IP and port should be asked for connection reversal"),
695      GNUNET_YES, &GNUNET_GETOPT_set_string, &remote_addr },
696     {'p', "port", NULL,
697      gettext_noop ("port to use to advertise"),
698      GNUNET_YES, &GNUNET_GETOPT_set_uint, &adv_port },
699     {'s', "stun", NULL,
700      gettext_noop ("enable STUN processing"),
701      GNUNET_NO, &GNUNET_GETOPT_set_one, &do_stun },
702     {'t', "tcp", NULL,
703      gettext_noop ("use TCP"),
704      GNUNET_NO, &GNUNET_GETOPT_set_one, &use_tcp },
705     {'u', "udp", NULL,
706      gettext_noop ("use UDP"),
707      GNUNET_NO, &GNUNET_GETOPT_set_one, &use_udp },
708     {'w', "write", NULL,
709      gettext_noop ("write configuration file (for autoconfiguration)"),
710      GNUNET_NO, &GNUNET_GETOPT_set_one, &write_cfg },
711     {'W', "watch", NULL,
712      gettext_noop ("watch for connection reversal requests"),
713      GNUNET_NO, &GNUNET_GETOPT_set_one, &listen_reversal },
714    GNUNET_GETOPT_OPTION_END
715   };
716
717   if (GNUNET_OK !=
718       GNUNET_STRINGS_get_utf8_args (argc, argv,
719                                     &argc, &argv))
720     return 2;
721   if (GNUNET_OK !=
722       GNUNET_PROGRAM_run (argc, argv,
723                           "gnunet-nat [options]",
724                           _("GNUnet NAT traversal autoconfigure daemon"),
725                           options,
726                           &run,
727                           NULL))
728   {
729     global_ret = 1;
730   }
731   GNUNET_free ((void*) argv);
732   return global_ret;
733 }
734
735
736 /* end of gnunet-nat.c */