glitch in the license text detected by hyazinthe, thank you!
[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 it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14 */
15
16 /**
17  * @file src/nat/gnunet-nat-auto.c
18  * @brief Command-line tool for testing and autoconfiguration of NAT traversal
19  * @author Christian Grothoff
20  * @author Bruno Cabral
21  */
22 #include "platform.h"
23 #include "gnunet_util_lib.h"
24 #include "gnunet_nat_service.h"
25 #include "gnunet_nat_auto_service.h"
26
27 /**
28  * Value to return from #main().
29  */
30 static int global_ret;
31
32 /**
33  * Handle to ongoing autoconfiguration.
34  */
35 static struct GNUNET_NAT_AUTO_AutoHandle *ah;
36
37 /**
38  * If we do auto-configuration, should we write the result
39  * to a file?
40  */
41 static int write_cfg;
42
43 /**
44  * Configuration filename.
45  */
46 static const char *cfg_file;
47
48 /**
49  * Original configuration.
50  */
51 static const struct GNUNET_CONFIGURATION_Handle *cfg;
52
53 /**
54  * Adapter we are supposed to test.
55  */
56 static char *section_name;
57
58 /**
59  * Should we run autoconfiguration?
60  */
61 static int do_auto;
62
63 /**
64  * Handle to a NAT test operation.
65  */
66 static struct GNUNET_NAT_AUTO_Test *nt;
67
68 /**
69  * Flag set to 1 if we use IPPROTO_UDP.
70  */
71 static int use_udp;
72
73 /**
74  * Flag set to 1 if we use IPPROTO_TCP.
75  */
76 static int use_tcp;
77
78 /**
79  * Protocol to use.
80  */
81 static uint8_t proto;
82
83 /**
84  * Test if all activities have finished, and if so,
85  * terminate.
86  */
87 static void
88 test_finished ()
89 {
90   if (NULL != ah)
91     return;
92   if (NULL != nt)
93     return;
94   GNUNET_SCHEDULER_shutdown ();
95 }
96
97
98 /**
99  * Function to iterate over sugested changes options
100  *
101  * @param cls closure
102  * @param section name of the section
103  * @param option name of the option
104  * @param value value of the option
105  */
106 static void
107 auto_conf_iter (void *cls,
108                 const char *section,
109                 const char *option,
110                 const char *value)
111 {
112   struct GNUNET_CONFIGURATION_Handle *new_cfg = cls;
113
114   PRINTF ("%s: %s\n",
115           option,
116           value);
117   if (NULL != new_cfg)
118     GNUNET_CONFIGURATION_set_value_string (new_cfg,
119                                            section,
120                                            option,
121                                            value);
122 }
123
124
125 /**
126  * Function called with the result from the autoconfiguration.
127  *
128  * @param cls closure
129  * @param diff minimal suggested changes to the original configuration
130  *             to make it work (as best as we can)
131  * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
132  * @param type what the situation of the NAT
133  */
134 static void
135 auto_config_cb (void *cls,
136                 const struct GNUNET_CONFIGURATION_Handle *diff,
137                 enum GNUNET_NAT_StatusCode result,
138                 enum GNUNET_NAT_Type type)
139 {
140   const char *nat_type;
141   char unknown_type[64];
142   struct GNUNET_CONFIGURATION_Handle *new_cfg;
143
144   ah = NULL;
145   switch (type)
146   {
147   case GNUNET_NAT_TYPE_NO_NAT:
148     nat_type = "NO NAT";
149     break;
150   case GNUNET_NAT_TYPE_UNREACHABLE_NAT:
151     nat_type = "NAT but we can traverse";
152     break;
153   case GNUNET_NAT_TYPE_STUN_PUNCHED_NAT:
154     nat_type = "NAT but STUN is able to identify the correct information";
155     break;
156   case GNUNET_NAT_TYPE_UPNP_NAT:
157     nat_type = "NAT but UPNP opened the ports";
158     break;
159   default:
160     SPRINTF (unknown_type,
161              "NAT unknown, type %u",
162              type);
163     nat_type = unknown_type;
164     break;
165   }
166
167   GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
168               "NAT status: %s/%s\n",
169               GNUNET_NAT_AUTO_status2string (result),
170               nat_type);
171
172   if (NULL == diff)
173     return;
174
175   /* Shortcut: if there are no changes suggested, bail out early. */
176   if (GNUNET_NO ==
177       GNUNET_CONFIGURATION_is_dirty (diff))
178   {
179     test_finished ();
180     return;
181   }
182
183   /* Apply diff to original configuration and show changes
184      to the user */
185   new_cfg = write_cfg ? GNUNET_CONFIGURATION_dup (cfg) : NULL;
186
187   GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
188               _("Suggested configuration changes:\n"));
189   GNUNET_CONFIGURATION_iterate_section_values (diff,
190                                                "nat",
191                                                &auto_conf_iter,
192                                                new_cfg);
193
194   /* If desired, write configuration to file; we write only the
195      changes to the defaults to keep things compact. */
196   if (write_cfg)
197   {
198     struct GNUNET_CONFIGURATION_Handle *def_cfg;
199
200     GNUNET_CONFIGURATION_set_value_string (new_cfg,
201                                            "ARM",
202                                            "CONFIG",
203                                            NULL);
204     def_cfg = GNUNET_CONFIGURATION_create ();
205     GNUNET_break (GNUNET_OK ==
206                   GNUNET_CONFIGURATION_load (def_cfg,
207                                              NULL));
208     if (GNUNET_OK !=
209         GNUNET_CONFIGURATION_write_diffs (def_cfg,
210                                           new_cfg,
211                                           cfg_file))
212     {
213       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
214                   _("Failed to write configuration to `%s'\n"),
215                   cfg_file);
216       global_ret = 1;
217     }
218     else
219     {
220       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
221                   _("Wrote updated configuration to `%s'\n"),
222                   cfg_file);
223     }
224     GNUNET_CONFIGURATION_destroy (def_cfg);
225   }
226
227   if (NULL != new_cfg)
228     GNUNET_CONFIGURATION_destroy (new_cfg);
229   test_finished ();
230 }
231
232
233 /**
234  * Function called to report success or failure for
235  * NAT configuration test.
236  *
237  * @param cls closure
238  * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
239  */
240 static void
241 test_report_cb (void *cls,
242                 enum GNUNET_NAT_StatusCode result)
243 {
244   nt = NULL;
245   PRINTF ("NAT test result: %s\n",
246           GNUNET_NAT_AUTO_status2string (result));
247   test_finished ();
248 }
249
250
251 /**
252  * Task run on shutdown.
253  *
254  * @param cls NULL
255  */
256 static void
257 do_shutdown (void *cls)
258 {
259   if (NULL != ah)
260   {
261     GNUNET_NAT_AUTO_autoconfig_cancel (ah);
262     ah = NULL;
263   }
264   if (NULL != nt)
265   {
266     GNUNET_NAT_AUTO_test_stop (nt);
267     nt = NULL;
268   }
269 }
270
271
272 /**
273  * Main function that will be run.
274  *
275  * @param cls closure
276  * @param args remaining command-line arguments
277  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
278  * @param c configuration
279  */
280 static void
281 run (void *cls,
282      char *const *args,
283      const char *cfgfile,
284      const struct GNUNET_CONFIGURATION_Handle *c)
285 {
286   cfg_file = cfgfile;
287   cfg = c;
288
289   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
290                                  NULL);
291
292   if (do_auto)
293   {
294     ah = GNUNET_NAT_AUTO_autoconfig_start (c,
295                                            &auto_config_cb,
296                                            NULL);
297   }
298
299   if (use_tcp && use_udp)
300   {
301     if (do_auto)
302       return;
303     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
304                 "Cannot use TCP and UDP\n");
305     global_ret = 1;
306     return;
307   }
308   proto = 0;
309   if (use_tcp)
310     proto = IPPROTO_TCP;
311   if (use_udp)
312     proto = IPPROTO_UDP;
313
314   if (NULL != section_name)
315   {
316     nt = GNUNET_NAT_AUTO_test_start (c,
317                                      proto,
318                                      section_name,
319                                      &test_report_cb,
320                                      NULL);
321   }
322   test_finished ();
323 }
324
325
326 /**
327  * Main function of gnunet-nat-auto
328  *
329  * @param argc number of command-line arguments
330  * @param argv command line
331  * @return 0 on success, -1 on error
332  */
333 int
334 main (int argc,
335       char *const argv[])
336 {
337   struct GNUNET_GETOPT_CommandLineOption options[] = {
338     GNUNET_GETOPT_option_flag ('a',
339                                   "auto",
340                                   gettext_noop ("run autoconfiguration"),
341                                   &do_auto),
342
343     GNUNET_GETOPT_option_string ('S',
344                                  "section",
345                                  "NAME",
346                                  gettext_noop ("section name providing the configuration for the adapter"),
347                                  &section_name),
348
349     GNUNET_GETOPT_option_flag ('t',
350                                    "tcp",
351                                    gettext_noop ("use TCP"),
352                                    &use_tcp),
353
354     GNUNET_GETOPT_option_flag ('u',
355                                    "udp",
356                                    gettext_noop ("use UDP"),
357                                    &use_udp),
358
359     GNUNET_GETOPT_option_flag ('w',
360                                    "write",
361                                    gettext_noop ("write configuration file (for autoconfiguration)"),
362                                    &write_cfg),
363     GNUNET_GETOPT_OPTION_END
364   };
365
366   if (GNUNET_OK !=
367       GNUNET_STRINGS_get_utf8_args (argc, argv,
368                                     &argc, &argv))
369     return 2;
370   if (GNUNET_OK !=
371       GNUNET_PROGRAM_run (argc, argv,
372                           "gnunet-nat-auto [options]",
373                           _("GNUnet NAT traversal autoconfiguration"),
374                           options,
375                           &run,
376                           NULL))
377   {
378     global_ret = 1;
379   }
380   GNUNET_free ((void*) argv);
381   return global_ret;
382 }
383
384
385 /* end of gnunet-nat-auto.c */