/* 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 Affero General Public License as published by the Free Software Foundation, either version 3 of the License, 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . SPDX-License-Identifier: AGPL3.0-or-later */ /** * @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 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); if (NULL == diff) return; /* 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; 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) { 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[]) { struct GNUNET_GETOPT_CommandLineOption options[] = { GNUNET_GETOPT_option_flag ('a', "auto", gettext_noop ("run autoconfiguration"), &do_auto), GNUNET_GETOPT_option_string ( 'S', "section", "NAME", gettext_noop ( "section name providing the configuration for the adapter"), §ion_name), GNUNET_GETOPT_option_flag ('t', "tcp", gettext_noop ("use TCP"), &use_tcp), GNUNET_GETOPT_option_flag ('u', "udp", gettext_noop ("use UDP"), &use_udp), GNUNET_GETOPT_option_flag ( 'w', "write", gettext_noop ("write configuration file (for autoconfiguration)"), &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 */