/* This file is part of GNUnet. Copyright (C) 2015, 2016, 2017 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** * @file src/nat/gnunet-nat-auto.c * @brief Command-line tool for testing and autoconfiguration of NAT traversal * @author Christian Grothoff * @author Bruno Cabral */ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_nat_service.h" #include "gnunet_nat_auto_service.h" /** * Value to return from #main(). */ static int global_ret; /** * Handle to ongoing autoconfiguration. */ static struct GNUNET_NAT_AUTO_AutoHandle *ah; /** * If we do auto-configuration, should we write the result * to a file? */ static int write_cfg; /** * Configuration filename. */ static const char *cfg_file; /** * Original configuration. */ static const struct GNUNET_CONFIGURATION_Handle *cfg; /** * Adapter we are supposed to test. */ static char *section_name; /** * Should we run autoconfiguration? */ static unsigned int do_auto; /** * Handle to a NAT test operation. */ static struct GNUNET_NAT_AUTO_Test *nt; /** * Flag set to 1 if we use IPPROTO_UDP. */ static int use_udp; /** * Flag set to 1 if we use IPPROTO_TCP. */ static int use_tcp; /** * Protocol to use. */ static uint8_t proto; /** * Test if all activities have finished, and if so, * terminate. */ static void test_finished () { if (NULL != ah) return; if (NULL != nt) return; GNUNET_SCHEDULER_shutdown (); } /** * Function to iterate over sugested changes options * * @param cls closure * @param section name of the section * @param option name of the option * @param value value of the option */ static void auto_conf_iter (void *cls, const char *section, const char *option, const char *value) { struct GNUNET_CONFIGURATION_Handle *new_cfg = cls; PRINTF ("%s: %s\n", option, value); if (NULL != new_cfg) GNUNET_CONFIGURATION_set_value_string (new_cfg, section, option, value); } /** * Function called with the result from the autoconfiguration. * * @param cls closure * @param diff minimal suggested changes to the original configuration * to make it work (as best as we can) * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code * @param type what the situation of the NAT */ static void auto_config_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *diff, enum GNUNET_NAT_StatusCode result, enum GNUNET_NAT_Type type) { const char *nat_type; char unknown_type[64]; struct GNUNET_CONFIGURATION_Handle *new_cfg; ah = NULL; switch (type) { case GNUNET_NAT_TYPE_NO_NAT: nat_type = "NO NAT"; break; case GNUNET_NAT_TYPE_UNREACHABLE_NAT: nat_type = "NAT but we can traverse"; break; case GNUNET_NAT_TYPE_STUN_PUNCHED_NAT: nat_type = "NAT but STUN is able to identify the correct information"; break; case GNUNET_NAT_TYPE_UPNP_NAT: nat_type = "NAT but UPNP opened the ports"; break; default: SPRINTF (unknown_type, "NAT unknown, type %u", type); nat_type = unknown_type; break; } GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "NAT status: %s/%s\n", GNUNET_NAT_AUTO_status2string (result), nat_type); /* Shortcut: if there are no changes suggested, bail out early. */ if (GNUNET_NO == GNUNET_CONFIGURATION_is_dirty (diff)) { test_finished (); return; } /* Apply diff to original configuration and show changes to the user */ new_cfg = write_cfg ? GNUNET_CONFIGURATION_dup (cfg) : NULL; if (NULL != diff) { GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, _("Suggested configuration changes:\n")); GNUNET_CONFIGURATION_iterate_section_values (diff, "nat", &auto_conf_iter, new_cfg); } /* If desired, write configuration to file; we write only the changes to the defaults to keep things compact. */ if ( (write_cfg) && (NULL != diff) ) { struct GNUNET_CONFIGURATION_Handle *def_cfg; GNUNET_CONFIGURATION_set_value_string (new_cfg, "ARM", "CONFIG", NULL); def_cfg = GNUNET_CONFIGURATION_create (); GNUNET_break (GNUNET_OK == GNUNET_CONFIGURATION_load (def_cfg, NULL)); if (GNUNET_OK != GNUNET_CONFIGURATION_write_diffs (def_cfg, new_cfg, cfg_file)) { GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, _("Failed to write configuration to `%s'\n"), cfg_file); global_ret = 1; } else { GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, _("Wrote updated configuration to `%s'\n"), cfg_file); } GNUNET_CONFIGURATION_destroy (def_cfg); } if (NULL != new_cfg) GNUNET_CONFIGURATION_destroy (new_cfg); test_finished (); } /** * Function called to report success or failure for * NAT configuration test. * * @param cls closure * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code */ static void test_report_cb (void *cls, enum GNUNET_NAT_StatusCode result) { nt = NULL; PRINTF ("NAT test result: %s\n", GNUNET_NAT_AUTO_status2string (result)); test_finished (); } /** * Task run on shutdown. * * @param cls NULL */ static void do_shutdown (void *cls) { if (NULL != ah) { GNUNET_NAT_AUTO_autoconfig_cancel (ah); ah = NULL; } if (NULL != nt) { GNUNET_NAT_AUTO_test_stop (nt); nt = NULL; } } /** * Main function that will be run. * * @param cls closure * @param args remaining command-line arguments * @param cfgfile name of the configuration file used (for saving, can be NULL!) * @param c configuration */ static void run (void *cls, char *const *args, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *c) { cfg_file = cfgfile; cfg = c; GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); if (do_auto) { ah = GNUNET_NAT_AUTO_autoconfig_start (c, &auto_config_cb, NULL); } if (use_tcp && use_udp) { if (do_auto) return; GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Cannot use TCP and UDP\n"); global_ret = 1; return; } proto = 0; if (use_tcp) proto = IPPROTO_TCP; if (use_udp) proto = IPPROTO_UDP; if (NULL != section_name) { nt = GNUNET_NAT_AUTO_test_start (c, proto, section_name, &test_report_cb, NULL); } test_finished (); } /** * Main function of gnunet-nat-auto * * @param argc number of command-line arguments * @param argv command line * @return 0 on success, -1 on error */ int main (int argc, char *const argv[]) { static const struct GNUNET_GETOPT_CommandLineOption options[] = { {'a', "auto", NULL, gettext_noop ("run autoconfiguration"), GNUNET_NO, &GNUNET_GETOPT_set_one, &do_auto }, {'S', "section", "NAME", gettext_noop ("section name providing the configuration for the adapter"), GNUNET_YES, &GNUNET_GETOPT_set_string, §ion_name }, {'t', "tcp", NULL, gettext_noop ("use TCP"), GNUNET_NO, &GNUNET_GETOPT_set_one, &use_tcp }, {'u', "udp", NULL, gettext_noop ("use UDP"), GNUNET_NO, &GNUNET_GETOPT_set_one, &use_udp }, {'w', "write", NULL, gettext_noop ("write configuration file (for autoconfiguration)"), GNUNET_NO, &GNUNET_GETOPT_set_one, &write_cfg }, GNUNET_GETOPT_OPTION_END }; if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) return 2; if (GNUNET_OK != GNUNET_PROGRAM_run (argc, argv, "gnunet-nat-auto [options]", _("GNUnet NAT traversal autoconfiguration"), options, &run, NULL)) { global_ret = 1; } GNUNET_free ((void*) argv); return global_ret; } /* end of gnunet-nat-auto.c */