/* 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/setup/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; static struct GNUNET_OS_Process *resolver; /** * Update the ICMP server button based on the result. * * @param on GNUNET_YES to enable, GNUNET_NO to disable */ static void update_icmp_server_enable_button (int on) { GtkToggleButton *button; button = GTK_TOGGLE_BUTTON (GNUNET_SETUP_get_object ("GNUNET_setup_transport_icmp_server_enable_checkbutton")); if (button == NULL) { GNUNET_break (0); return; } gtk_toggle_button_set_active (button, on ? TRUE : FALSE); } /** * 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) { GNUNET_SCHEDULER_cancel (tsk); tsk = GNUNET_SCHEDULER_NO_TASK; GNUNET_NAT_test_stop (tst); tst = NULL; if (NULL != resolver) { GNUNET_break (0 == GNUNET_OS_process_kill (resolver, SIGTERM)); GNUNET_OS_process_destroy (resolver); } update_icmp_server_enable_button (success); } /** * 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) { GNUNET_assert (NULL != tst); tsk = GNUNET_SCHEDULER_NO_TASK; GNUNET_NAT_test_stop (tst); tst = NULL; update_icmp_server_enable_button (GNUNET_NO); } /** * 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. */ static void test_connection_reversal () { if (NULL != resolver) return; /* test already active!? */ resolver = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-service-resolver", "gnunet-service-resolver", NULL); GNUNET_SCHEDULER_add_now (&reversal_test, NULL); } /** * 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 broadcast_addr the broadcast address (can be NULL for unknown or unassigned) * @param netmask the network mask (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, const struct sockaddr *broadcast_addr, const struct sockaddr *netmask, 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 */ if (NULL == inet_ntop (AF_INET, &in->sin_addr, buf, sizeof (buf))) { GNUNET_break (0); return GNUNET_OK; } GNUNET_CONFIGURATION_set_value_string (cfg, "nat", "INTERNAL_ADDRESS", buf); entry = GTK_ENTRY (GNUNET_SETUP_get_object ("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; } /** * Set our external IPv4 address. * * @param cls closure * @param addr the address, NULL on errors */ static void set_external_ipv4 (void *cls, const struct in_addr *addr) { char buf[INET_ADDRSTRLEN]; GObject *o; GtkEntry *entry; GtkToggleButton *button; if (NULL != addr) { /* enable 'behind nat' */ if (NULL != cfg) GNUNET_CONFIGURATION_set_value_string (cfg, "nat", "BEHIND_NAT", "YES"); o = GNUNET_SETUP_get_object ("GNUNET_setup_transport_nat_checkbutton"); if (NULL != o) { button = GTK_TOGGLE_BUTTON (o); if (button == NULL) { GNUNET_break (0); return; } gtk_toggle_button_set_active (button, TRUE); } /* set external IP address */ if (NULL == inet_ntop (AF_INET, addr, buf, sizeof (buf))) { GNUNET_break (0); return; } if (NULL != cfg) GNUNET_CONFIGURATION_set_value_string (cfg, "nat", "EXTERNAL_ADDRESS", buf); o = GNUNET_SETUP_get_object ("GNUNET_setup_transport_external_ip_address_entry"); if (NULL != o) { entry = GTK_ENTRY (o); if (entry == NULL) { GNUNET_break (0); return; } gtk_entry_set_text (entry, buf); } } } /** * User asked for autoconfiguration. Try the full program. */ void GNUNET_setup_transport_autoconfig_button_clicked_cb () { GtkToggleButton *button; int hns; int hnc; char *tmp; /* try to detect external IP */ (void) GNUNET_NAT_mini_get_external_ipv4 (TIMEOUT, &set_external_ipv4, NULL); /* 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) test_connection_reversal (); /* 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 (GNUNET_SETUP_get_object ("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 */