/* This file is part of GNUnet. (C) 2010 Christian Grothoff (and other contributing authors) 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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** * @file src/gnunet-setup-transport.c * @brief support for transport (NAT) configuration * @author Christian Grothoff */ #include "gnunet-setup.h" #include #include #include /** * How long do we wait for the NAT test to report success? */ #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1) /** * Handle to the active NAT test. */ static struct GNUNET_NAT_Test *tst; /** * Task identifier for the timeout. */ static GNUNET_SCHEDULER_TaskIdentifier tsk; /** * Function called by NAT on success. * Clean up and update GUI (with success). * * @param cls closure (unused) * @param success currently always GNUNET_OK */ static void result_callback (void *cls, int success) { int *ok = cls; *ok = success; GNUNET_SCHEDULER_cancel (tsk); tsk = GNUNET_SCHEDULER_NO_TASK; GNUNET_NAT_test_stop (tst); tst = NULL; } /** * Function called if NAT failed to confirm success. * Clean up and update GUI (with failure). * * @param cls closure (unused) * @param tc scheduler callback */ static void fail_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { int *ok = cls; GNUNET_assert (NULL != tst); *ok = GNUNET_NO; tsk = GNUNET_SCHEDULER_NO_TASK; GNUNET_NAT_test_stop (tst); tst = NULL; } /** * Main function for the connection reversal test. * * @param cls the 'int*' for the result * @param tc scheduler context */ static void reversal_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { int *ok = cls; GNUNET_assert (NULL != cfg); GNUNET_RESOLVER_connect (cfg); tst = GNUNET_NAT_test_start (cfg, GNUNET_YES, 0, 0, &result_callback, ok); if (NULL == tst) { *ok = GNUNET_SYSERR; return; } tsk = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &fail_timeout, ok); } /** * Test if connection reversal (ICMP method) works. * * @return GNUNET_YES if it works, GNUNET_NO if not. */ static int test_connection_reversal () { int ok; struct GNUNET_OS_Process *resolver; resolver = GNUNET_OS_start_process (NULL, NULL, "gnunet-service-resolver", "gnunet-service-resolver", NULL); ok = GNUNET_NO; GNUNET_SCHEDULER_run (&reversal_test, &ok); if (NULL != resolver) { GNUNET_break (0 == GNUNET_OS_process_kill (resolver, SIGTERM)); GNUNET_OS_process_close (resolver); } return ok; } /** * Process list of local IP addresses. Find and set the * one of the default interface. * * @param cls closure (not used) * @param name name of the interface (can be NULL for unknown) * @param isDefault is this presumably the default interface * @param addr address of this interface (can be NULL for unknown or unassigned) * @param addrlen length of the address * @return GNUNET_OK to continue iteration, GNUNET_SYSERR to abort */ static int nipo (void *cls, const char *name, int isDefault, const struct sockaddr * addr, socklen_t addrlen) { const struct sockaddr_in *in; char buf[INET_ADDRSTRLEN]; GtkEntry *entry; if (! isDefault) return GNUNET_OK; if (addrlen != sizeof (struct sockaddr_in)) return GNUNET_OK; in = (const struct sockaddr_in*) addr; /* set internal IP address */ inet_ntop (AF_INET, &in->sin_addr, buf, sizeof(buf)); GNUNET_CONFIGURATION_set_value_string (cfg, "nat", "INTERNAL_ADDRESS", buf); entry = GTK_ENTRY (gtk_builder_get_object (builder, "GNUNET_setup_transport_internal_ip_entry")); if (entry == NULL) { GNUNET_break (0); return GNUNET_SYSERR; } gtk_entry_set_text (entry, buf); /* no need to continue iteration */ return GNUNET_SYSERR; } /** * User asked for autoconfiguration. Try the full program. */ void GNUNET_setup_transport_autoconfig_button_clicked_cb () { struct in_addr ia; char buf[INET_ADDRSTRLEN]; GtkEntry *entry; GtkToggleButton *button; int hns; int hnc; char *tmp; /* try to detect external IP */ if (GNUNET_OK == GNUNET_NAT_mini_get_external_ipv4 (&ia)) { /* enable 'behind nat' */ GNUNET_CONFIGURATION_set_value_string (cfg, "nat", "BEHIND_NAT", "YES"); button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "GNUNET_setup_transport_nat_checkbutton")); if (button == NULL) { GNUNET_break (0); return; } gtk_toggle_button_set_active (button, TRUE); /* set external IP address */ inet_ntop (AF_INET, &ia, buf, sizeof(buf)); GNUNET_CONFIGURATION_set_value_string (cfg, "nat", "EXTERNAL_ADDRESS", buf); entry = GTK_ENTRY (gtk_builder_get_object (builder, "GNUNET_setup_transport_external_ip_address_entry")); if (entry == NULL) { GNUNET_break (0); return; } gtk_entry_set_text (entry, buf); } /* Try to detect internal IP */ GNUNET_OS_network_interfaces_list (&nipo, NULL); /* FIXME: do more: test if UPnP works */ /* test gnunet-helper-nat-server */ tmp = NULL; hns = ( (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, "nat", "EXTERNAL_ADDRESS", &tmp)) && (0 < strlen (tmp)) && (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (cfg, "nat", "BEHIND_NAT")) && (GNUNET_YES == GNUNET_OS_check_helper_binary ("gnunet-helper-nat-server")) ); GNUNET_free_non_null (tmp); if (hns) hns = test_connection_reversal (); button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "GNUNET_setup_transport_icmp_server_enable_checkbutton")); if (button == NULL) { GNUNET_break (0); return; } gtk_toggle_button_set_active (button, hns ? TRUE : FALSE); /* test gnunet-helper-nat-client */ tmp = NULL; hnc = ( (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, "nat", "INTERNAL_ADDRESS", &tmp)) && (0 < strlen (tmp)) && (GNUNET_YES != GNUNET_CONFIGURATION_get_value_yesno (cfg, "nat", "BEHIND_NAT")) && (GNUNET_YES == GNUNET_OS_check_helper_binary ("gnunet-helper-nat-client")) ); GNUNET_free_non_null (tmp); button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "GNUNET_setup_transport_icmp_client_enable_checkbutton")); if (button == NULL) { GNUNET_break (0); return; } gtk_toggle_button_set_active (button, hnc ? TRUE : FALSE); } /* end of gnunet-setup-transport.c */