separate service for autoconfiguration from NAT traversal
[oweals/gnunet.git] / src / nat-auto / gnunet-nat-auto.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2015, 2016, 2017 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-auto.c
23  * @brief Command-line tool for testing and autoconfiguration of NAT traversal
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 #include "gnunet_nat_auto_service.h"
31
32 /**
33  * Value to return from #main().
34  */
35 static int global_ret;
36
37 /**
38  * Handle to ongoing autoconfiguration.
39  */
40 static struct GNUNET_NAT_AutoHandle *ah;
41
42 /**
43  * If we do auto-configuration, should we write the result
44  * to a file?
45  */
46 static int write_cfg;
47
48 /**
49  * Configuration filename.
50  */ 
51 static const char *cfg_file;
52
53 /**
54  * Original configuration.
55  */
56 static const struct GNUNET_CONFIGURATION_Handle *cfg;
57
58 /**
59  * Address we are bound to (in test), or should bind to
60  * (if #do_stun is set).
61  */
62 static char *bind_addr;
63
64 /**
65  * External IP address and port to use for the test.
66  * If not set, use #bind_addr.
67  */
68 static char *extern_addr;
69
70 /**
71  * Should we run autoconfiguration?
72  */
73 static unsigned int do_auto;
74
75 /**
76  * Handle to a NAT test operation.
77  */
78 static struct GNUNET_NAT_Test *nt;
79
80 /**
81  * Flag set to 1 if we use IPPROTO_UDP.
82  */
83 static int use_udp;
84
85 /**
86  * Flag set to 1 if we use IPPROTO_TCP.
87  */
88 static int use_tcp;
89
90 /**
91  * Protocol to use.
92  */
93 static uint8_t proto;
94
95 /**
96  * Test if all activities have finished, and if so,
97  * terminate.
98  */
99 static void
100 test_finished ()
101 {
102   if (NULL != ah)
103     return;
104   if (NULL != nt)
105     return;
106   GNUNET_SCHEDULER_shutdown ();
107 }
108
109
110 /**
111  * Function to iterate over sugested changes options
112  *
113  * @param cls closure
114  * @param section name of the section
115  * @param option name of the option
116  * @param value value of the option
117  */
118 static void
119 auto_conf_iter (void *cls,
120                 const char *section,
121                 const char *option,
122                 const char *value)
123 {
124   struct GNUNET_CONFIGURATION_Handle *new_cfg = cls;
125   
126   PRINTF ("%s: %s\n",
127           option,
128           value);
129   if (NULL != new_cfg)
130     GNUNET_CONFIGURATION_set_value_string (new_cfg,
131                                            section,
132                                            option,
133                                            value);
134 }
135
136
137 /**
138  * Function called with the result from the autoconfiguration.
139  *
140  * @param cls closure
141  * @param diff minimal suggested changes to the original configuration
142  *             to make it work (as best as we can)
143  * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
144  * @param type what the situation of the NAT
145  */
146 static void
147 auto_config_cb (void *cls,
148                 const struct GNUNET_CONFIGURATION_Handle *diff,
149                 enum GNUNET_NAT_StatusCode result,
150                 enum GNUNET_NAT_Type type)
151 {
152   const char *nat_type;
153   char unknown_type[64];
154   struct GNUNET_CONFIGURATION_Handle *new_cfg;
155
156   ah = NULL;
157   switch (type)
158   {
159   case GNUNET_NAT_TYPE_NO_NAT:
160     nat_type = "NO NAT";
161     break;
162   case GNUNET_NAT_TYPE_UNREACHABLE_NAT:
163     nat_type = "NAT but we can traverse";
164     break;
165   case GNUNET_NAT_TYPE_STUN_PUNCHED_NAT:
166     nat_type = "NAT but STUN is able to identify the correct information";
167     break;
168   case GNUNET_NAT_TYPE_UPNP_NAT:
169     nat_type = "NAT but UPNP opened the ports";
170     break;
171   default:
172     SPRINTF (unknown_type,
173              "NAT unknown, type %u",
174              type);
175     nat_type = unknown_type;
176     break;
177   }
178
179   GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
180               "NAT status: %s/%s\n",
181               GNUNET_NAT_status2string (result),
182               nat_type);
183
184   /* Shortcut: if there are no changes suggested, bail out early. */
185   if (GNUNET_NO ==
186       GNUNET_CONFIGURATION_is_dirty (diff))
187   {
188     test_finished ();
189     return;
190   }
191
192   /* Apply diff to original configuration and show changes
193      to the user */
194   new_cfg = write_cfg ? GNUNET_CONFIGURATION_dup (cfg) : NULL;
195   
196   if (NULL != diff)
197   {
198     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
199                 _("Suggested configuration changes:\n"));
200     GNUNET_CONFIGURATION_iterate_section_values (diff,
201                                                  "nat",
202                                                  &auto_conf_iter,
203                                                  new_cfg);
204   }
205
206   /* If desired, write configuration to file; we write only the
207      changes to the defaults to keep things compact. */
208   if ( (write_cfg) &&
209        (NULL != diff) )
210   {
211     struct GNUNET_CONFIGURATION_Handle *def_cfg;
212
213     GNUNET_CONFIGURATION_set_value_string (new_cfg,
214                                            "ARM",
215                                            "CONFIG",
216                                            NULL);
217     def_cfg = GNUNET_CONFIGURATION_create ();
218     GNUNET_break (GNUNET_OK ==
219                   GNUNET_CONFIGURATION_load (def_cfg,
220                                              NULL));
221     if (GNUNET_OK !=
222         GNUNET_CONFIGURATION_write_diffs (def_cfg,
223                                           new_cfg,
224                                           cfg_file))
225     {
226       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
227                   _("Failed to write configuration to `%s'\n"),
228                   cfg_file);
229       global_ret = 1;
230     }
231     else
232     {
233       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
234                   _("Wrote updated configuration to `%s'\n"),
235                   cfg_file);
236     }
237     GNUNET_CONFIGURATION_destroy (def_cfg);
238   }
239
240   if (NULL != new_cfg)
241     GNUNET_CONFIGURATION_destroy (new_cfg);
242   test_finished ();
243 }
244
245
246 /**
247  * Function called to report success or failure for
248  * NAT configuration test.
249  *
250  * @param cls closure
251  * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
252  */
253 static void
254 test_report_cb (void *cls,
255                 enum GNUNET_NAT_StatusCode result)
256 {
257   nt = NULL;
258   PRINTF ("NAT test result: %s\n",
259           GNUNET_NAT_status2string (result));
260   test_finished ();
261 }
262
263
264 /**
265  * Task run on shutdown.
266  *
267  * @param cls NULL
268  */
269 static void
270 do_shutdown (void *cls)
271 {
272   if (NULL != ah)
273   {
274     GNUNET_NAT_autoconfig_cancel (ah);
275     ah = NULL;
276   }
277   if (NULL != nt)
278   {
279     GNUNET_NAT_test_stop (nt);
280     nt = NULL;
281   }
282 }
283
284
285 /**
286  * Main function that will be run.
287  *
288  * @param cls closure
289  * @param args remaining command-line arguments
290  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
291  * @param c configuration
292  */
293 static void
294 run (void *cls,
295      char *const *args,
296      const char *cfgfile,
297      const struct GNUNET_CONFIGURATION_Handle *c)
298 {
299   struct sockaddr_in bind_sa;
300   struct sockaddr_in extern_sa;
301
302   cfg_file = cfgfile;
303   cfg = c;
304   
305   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
306                                  NULL);
307
308   if (do_auto)
309   {
310     ah = GNUNET_NAT_autoconfig_start (c,
311                                       &auto_config_cb,
312                                       NULL);
313   }
314
315   if (use_tcp && use_udp)
316   {
317     if (do_auto)
318       return;
319     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
320                 "Cannot use TCP and UDP\n");
321     global_ret = 1;
322     return;
323   }
324   proto = 0;
325   if (use_tcp)
326     proto = IPPROTO_TCP;
327   if (use_udp)
328     proto = IPPROTO_UDP;
329
330   if (NULL != bind_addr)
331   {
332     if (GNUNET_OK !=
333         GNUNET_STRINGS_to_address_ipv4 (bind_addr,
334                                         strlen (bind_addr),
335                                         &bind_sa))
336     {
337       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
338                   "Invalid socket address `%s'\n",
339                   bind_addr);
340       global_ret = 1;
341       return;
342     }
343   }
344   if (NULL != extern_addr)
345   {
346     if (GNUNET_OK !=
347         GNUNET_STRINGS_to_address_ipv4 (extern_addr,
348                                         strlen (extern_addr),
349                                         &extern_sa))
350     {
351       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
352                   "Invalid socket address `%s'\n",
353                   extern_addr);
354       global_ret = 1;
355       return;
356     }
357   }
358
359   if (NULL != bind_addr)
360   {
361     if (NULL == extern_addr)
362       extern_sa = bind_sa;
363     nt = GNUNET_NAT_test_start (c,
364                                 proto,
365                                 bind_sa.sin_addr,
366                                 ntohs (bind_sa.sin_port),
367                                 extern_sa.sin_addr,
368                                 ntohs (extern_sa.sin_port),
369                                 &test_report_cb,
370                                 NULL);
371   }
372   test_finished ();
373 }
374
375
376 /**
377  * Main function of gnunet-nat
378  *
379  * @param argc number of command-line arguments
380  * @param argv command line
381  * @return 0 on success, -1 on error
382  */
383 int
384 main (int argc,
385       char *const argv[])
386 {
387   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
388     {'a', "auto", NULL,
389      gettext_noop ("run autoconfiguration"),
390      GNUNET_NO, &GNUNET_GETOPT_set_one, &do_auto },
391     {'b', "bind", "ADDRESS",
392      gettext_noop ("which IP and port are we bound to"),
393      GNUNET_YES, &GNUNET_GETOPT_set_string, &bind_addr },
394     {'e', "external", "ADDRESS",
395      gettext_noop ("which external IP and port should be used to test"),
396      GNUNET_YES, &GNUNET_GETOPT_set_string, &extern_addr },
397     {'t', "tcp", NULL,
398      gettext_noop ("use TCP"),
399      GNUNET_NO, &GNUNET_GETOPT_set_one, &use_tcp },
400     {'u', "udp", NULL,
401      gettext_noop ("use UDP"),
402      GNUNET_NO, &GNUNET_GETOPT_set_one, &use_udp },
403     {'w', "write", NULL,
404      gettext_noop ("write configuration file (for autoconfiguration)"),
405      GNUNET_NO, &GNUNET_GETOPT_set_one, &write_cfg },
406     GNUNET_GETOPT_OPTION_END
407   };
408
409   if (GNUNET_OK !=
410       GNUNET_STRINGS_get_utf8_args (argc, argv,
411                                     &argc, &argv))
412     return 2;
413   if (GNUNET_OK !=
414       GNUNET_PROGRAM_run (argc, argv,
415                           "gnunet-nat-auto [options]",
416                           _("GNUnet NAT traversal autoconfiguration"),
417                           options,
418                           &run,
419                           NULL))
420   {
421     global_ret = 1;
422   }
423   GNUNET_free ((void*) argv);
424   return global_ret;
425 }
426
427
428 /* end of gnunet-nat-auto.c */