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