From 8a3016481ba6aeb36de3950a56e641dda53ca544 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 25 Jun 2011 06:24:01 +0000 Subject: [PATCH] the big NAT change --- contrib/defaults.conf | 14 +- src/include/gnunet_nat_lib.h | 76 +- src/include/gnunet_transport_plugin.h | 14 +- src/include/gnunet_transport_service.h | 4 +- src/nat/Makefile.am | 20 +- src/nat/bsdqueue.h | 157 -- src/nat/nat.c | 1307 ++++++++++++---- src/nat/nat.h | 72 - src/nat/natpmp.c | 325 ---- src/nat/natpmp.h | 46 - src/nat/test-nat.conf | 355 ----- src/nat/test_nat.c | 114 +- src/nat/test_nat_data.conf | 136 ++ src/nat/upnp-commands.c | 877 ----------- src/nat/upnp-commands.h | 271 ---- src/nat/upnp-discover.c | 1270 --------------- src/nat/upnp-discover.h | 82 - src/nat/upnp-igd-parse.c | 207 --- src/nat/upnp-igd-parse.h | 99 -- src/nat/upnp-minixml.c | 239 --- src/nat/upnp-minixml.h | 131 -- src/nat/upnp-reply-parse.c | 166 -- src/nat/upnp-reply-parse.h | 107 -- src/nat/upnp.c | 422 ----- src/nat/upnp.h | 52 - src/transport/Makefile.am | 2 + src/transport/gnunet-service-transport.c | 169 +- src/transport/plugin_transport_http.c | 24 +- src/transport/plugin_transport_tcp.c | 1259 ++------------- src/transport/plugin_transport_udp.c | 1358 ++++------------- src/transport/plugin_transport_unix.c | 79 +- src/transport/test_plugin_transport.c | 2 +- src/transport/test_transport_api.c | 3 +- .../test_transport_api_tcp_peer1.conf | 9 +- .../test_transport_api_tcp_peer2.conf | 9 +- src/transport/transport.h | 2 +- src/util/resolver_api.c | 11 +- 37 files changed, 1821 insertions(+), 7669 deletions(-) delete mode 100644 src/nat/bsdqueue.h delete mode 100644 src/nat/nat.h delete mode 100644 src/nat/natpmp.c delete mode 100644 src/nat/natpmp.h delete mode 100644 src/nat/test-nat.conf create mode 100644 src/nat/test_nat_data.conf delete mode 100644 src/nat/upnp-commands.c delete mode 100644 src/nat/upnp-commands.h delete mode 100644 src/nat/upnp-discover.c delete mode 100644 src/nat/upnp-discover.h delete mode 100644 src/nat/upnp-igd-parse.c delete mode 100644 src/nat/upnp-igd-parse.h delete mode 100644 src/nat/upnp-minixml.c delete mode 100644 src/nat/upnp-minixml.h delete mode 100644 src/nat/upnp-reply-parse.c delete mode 100644 src/nat/upnp-reply-parse.h delete mode 100644 src/nat/upnp.c delete mode 100644 src/nat/upnp.h diff --git a/contrib/defaults.conf b/contrib/defaults.conf index 2c2cd7a77..4c21158c8 100644 --- a/contrib/defaults.conf +++ b/contrib/defaults.conf @@ -31,6 +31,9 @@ PUNCHED_NAT = NO # Disable UPNP by default until it gets cleaner! ENABLE_UPNP = NO +# Use addresses from the local network interfaces (inluding loopback, but also others) +USE_LOCALADDR = YES + # External IP address of the NAT box (if known); IPv4 dotted-decimal ONLY at this time (should allow DynDNS!) # normal interface IP address for non-NATed peers; # possibly auto-detected (using UPnP) if possible if not specified @@ -58,12 +61,8 @@ ADVERTISED_PORT = 2086 # Maximum number of open TCP connections allowed MAX_CONNECTIONS = 128 -# After how long do we drop inactive connections? TIMEOUT = 5000 -# Allow use of loopback address -USE_LOCALADDR = NO - # ACCEPT_FROM = # ACCEPT_FROM6 = # REJECT_FROM = @@ -74,20 +73,13 @@ USE_LOCALADDR = NO [transport-udp] PORT = 2086 -# Allow use of loopback address -USE_LOCALADDR = NO - [transport-http] PORT = 1080 -# Allow use of loopback address -USE_LOCALADDR = NO [transport-https] PORT = 4433 -# Allow use of loopback address -USE_LOCALADDR = NO [transport-wlan] diff --git a/src/include/gnunet_nat_lib.h b/src/include/gnunet_nat_lib.h index 14da4e684..9550bb1e3 100644 --- a/src/include/gnunet_nat_lib.h +++ b/src/include/gnunet_nat_lib.h @@ -32,7 +32,8 @@ #include "gnunet_util_lib.h" /** - * Signature of the callback passed to GNUNET_NAT_register. + * Signature of the callback passed to GNUNET_NAT_register for + * a function to call whenever our set of 'valid' addresses changes. * * @param cls closure * @param add_remove GNUNET_YES to mean the new public IP address, GNUNET_NO to mean @@ -46,39 +47,96 @@ typedef void (*GNUNET_NAT_AddressCallback) (void *cls, socklen_t addrlen); +/** + * Signature of the callback passed to GNUNET_NAT_register + * for a function to call whenever someone asks us to do connection + * reversal. + * + * @param cls closure + * @param addr public IP address of the other peer + * @param addrlen actual lenght of the address + */ +typedef void (*GNUNET_NAT_ReversalCallback) (void *cls, + const struct sockaddr *addr, + socklen_t addrlen); + + /** * Handle for active NAT registrations. */ struct GNUNET_NAT_Handle; + /** * Attempt to enable port redirection and detect public IP address contacting * UPnP or NAT-PMP routers on the local network. Use addr to specify to which * of the local host's addresses should the external port be mapped. The port - * is taken from the corresponding sockaddr_in[6] field. + * is taken from the corresponding sockaddr_in[6] field. The NAT module + * should call the given callback for any 'plausible' external address. * * @param cfg configuration to use - * @param addr the local address packets should be redirected to - * @param addrlen actual lenght of the address - * @param callback function to call everytime the public IP address changes + * @param is_tcp GNUNET_YES for TCP, GNUNET_NO for UDP + * @param adv_port advertised port (port we are either bound to or that our OS + * locally performs redirection from to our bound port). + * @param num_addrs number of addresses in 'addrs' + * @param addrs list of local addresses packets should be redirected to + * @param addrlens actual lengths of the addresses + * @param address_callback function to call everytime the public IP address changes + * @param reversal_callback function to call if someone wants connection reversal from us, + * NULL if connection reversal is not supported * @param callback_cls closure for callback * @return NULL on error, otherwise handle that can be used to unregister */ struct GNUNET_NAT_Handle * GNUNET_NAT_register (const struct GNUNET_CONFIGURATION_Handle *cfg, - const struct sockaddr *addr, - socklen_t addrlen, - GNUNET_NAT_AddressCallback callback, + int is_tcp, + uint16_t adv_port, + unsigned int num_addrs, + const struct sockaddr **addrs, + const socklen_t *addrlens, + GNUNET_NAT_AddressCallback address_callback, + GNUNET_NAT_ReversalCallback reversal_callback, void *callback_cls); +/** + * Test if the given address is (currently) a plausible IP address for this peer. + * + * @param h the handle returned by register + * @param addr IP address to test (IPv4 or IPv6) + * @param addrlen number of bytes in addr + * @return GNUNET_YES if the address is plausible, + * GNUNET_NO if the address is not plausible, + * GNUNET_SYSERR if the address is malformed + */ +int +GNUNET_NAT_test_address (struct GNUNET_NAT_Handle *h, + const void *addr, + socklen_t addrlen); + + +/** + * We learned about a peer (possibly behind NAT) so run the + * gnunet-nat-client to send dummy ICMP responses to cause + * that peer to connect to us (connection reversal). + * + * @param h handle (used for configuration) + * @param sa the address of the peer (IPv4-only) + */ +void +GNUNET_NAT_run_client (struct GNUNET_NAT_Handle *h, + const struct sockaddr_in *sa); + + + /** * Stop port redirection and public IP address detection for the given handle. * This frees the handle, after having sent the needed commands to close open ports. * * @param h the handle to stop */ -void GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *h); +void +GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *h); #endif diff --git a/src/include/gnunet_transport_plugin.h b/src/include/gnunet_transport_plugin.h index 507d95abe..40f134db0 100644 --- a/src/include/gnunet_transport_plugin.h +++ b/src/include/gnunet_transport_plugin.h @@ -123,19 +123,17 @@ typedef struct GNUNET_TIME_Relative (*GNUNET_TRANSPORT_PluginReceiveCallback) (v * is aware that it might be reachable under. * * @param cls closure - * @param name name of the transport that generated the address - * @param addr one of the addresses of the host, NULL for the last address + * @param add_remove should the address added (YES) or removed (NO) from the + * set of valid addresses? + * @param addr one of the addresses of the host * the specific address format depends on the transport * @param addrlen length of the address - * @param expires when should this address automatically expire? */ typedef void (*GNUNET_TRANSPORT_AddressNotification) (void *cls, - const char *name, + int add_remove, const void *addr, - uint16_t addrlen, - struct - GNUNET_TIME_Relative - expires); + size_t addrlen); + /** * Function that will be called whenever the plugin receives data over diff --git a/src/include/gnunet_transport_service.h b/src/include/gnunet_transport_service.h index 98ef8c229..9a7093a10 100644 --- a/src/include/gnunet_transport_service.h +++ b/src/include/gnunet_transport_service.h @@ -558,12 +558,12 @@ struct GNUNET_TRANSPORT_TransmitHandle /** * Cancel the specified transmission-ready notification. * - * @param h handle of the transmission notification request to cancel + * @param th handle of the transmission notification request to cancel */ void GNUNET_TRANSPORT_notify_transmit_ready_cancel (struct GNUNET_TRANSPORT_TransmitHandle - *h); + *th); diff --git a/src/nat/Makefile.am b/src/nat/Makefile.am index 8542af8fa..2f30d3185 100644 --- a/src/nat/Makefile.am +++ b/src/nat/Makefile.am @@ -11,26 +11,12 @@ endif lib_LTLIBRARIES = libgnunetnat.la libgnunetnat_la_SOURCES = \ - upnp.c upnp.h \ - upnp-commands.c upnp-commands.h \ - upnp-discover.c upnp-discover.h \ - upnp-igd-parse.c upnp-igd-parse.h \ - upnp-minixml.c upnp-minixml.h \ - upnp-reply-parse.c upnp-reply-parse.h bsdqueue.h \ - nat.c nat.h \ - natpmp.h - -libgnunetnat_la_CFLAGS = \ - -DDEBUG_UPNP -g -O0 + nat.c libgnunetnat_la_LIBADD = \ $(top_builddir)/src/util/libgnunetutil.la \ - @LIBCURL@ \ $(GN_LIBINTL) @EXT_LIBS@ -libgnunetnat_la_CPPFLAGS = \ - @LIBCURL_CPPFLAGS@ - libgnunetnat_la_LDFLAGS = \ $(GN_LIB_LDFLAGS) $(WINFLAGS) \ -version-info 0:0:0 @@ -39,7 +25,7 @@ check_PROGRAMS = \ test-nat if ENABLE_TEST_RUN -TESTS = $(check_PROGRAMS) + TESTS = $(check_PROGRAMS) endif test_nat_SOURCES = \ @@ -51,4 +37,4 @@ test_nat_LDADD = \ @LIBCURL@ EXTRA_DIST = \ - test-nat.conf + test_nat_data.conf diff --git a/src/nat/bsdqueue.h b/src/nat/bsdqueue.h deleted file mode 100644 index 7b6cb60b3..000000000 --- a/src/nat/bsdqueue.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2009, 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 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., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. -*/ - -/** - * @file nat/bsdqueue.h - * @brief BSD implementation of simple lists - * - * @author Milan Bouchet-Valat - */ - -/* - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)queue.h 8.5 (Berkeley) 8/20/94 - */ - -#ifndef BSD_QUEUE_H -#define BSD_QUEUE_H - -/* - * A list is headed by a single forward pointer (or an array of forward - * pointers for a hash table header). The elements are doubly linked - * so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before - * or after an existing element or at the head of the list. A list - * may only be traversed in the forward direction. - */ - -#ifdef QUEUE_MACRO_DEBUG -#define _Q_INVALIDATE(a) (a) = ((void *)-1) -#else -#define _Q_INVALIDATE(a) -#endif - -/* - * List definitions. - */ -#define LIST_HEAD(name, type) \ -struct name { \ - struct type *lh_first; /* first element */ \ -} - -#define LIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define LIST_ENTRY(type) \ -struct { \ - struct type *le_next; /* next element */ \ - struct type **le_prev; /* address of previous next element */ \ -} - -/* - * List access methods - */ -#define LIST_FIRST(head) ((head)->lh_first) -#define LIST_END(head) NULL -#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) -#define LIST_NEXT(elm, field) ((elm)->field.le_next) - -#define LIST_FOREACH(var, head, field) \ - for((var) = LIST_FIRST(head); \ - (var)!= LIST_END(head); \ - (var) = LIST_NEXT(var, field)) - -/* - * List functions. - */ -#define LIST_INIT(head) do { \ - LIST_FIRST(head) = LIST_END(head); \ -} while (0) - -#define LIST_INSERT_AFTER(listelm, elm, field) do { \ - if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ - (listelm)->field.le_next->field.le_prev = \ - &(elm)->field.le_next; \ - (listelm)->field.le_next = (elm); \ - (elm)->field.le_prev = &(listelm)->field.le_next; \ -} while (0) - -#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.le_prev = (listelm)->field.le_prev; \ - (elm)->field.le_next = (listelm); \ - *(listelm)->field.le_prev = (elm); \ - (listelm)->field.le_prev = &(elm)->field.le_next; \ -} while (0) - -#define LIST_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.le_next = (head)->lh_first) != NULL) \ - (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ - (head)->lh_first = (elm); \ - (elm)->field.le_prev = &(head)->lh_first; \ -} while (0) - -#define LIST_REMOVE(elm, field) do { \ - if ((elm)->field.le_next != NULL) \ - (elm)->field.le_next->field.le_prev = \ - (elm)->field.le_prev; \ - *(elm)->field.le_prev = (elm)->field.le_next; \ - _Q_INVALIDATE((elm)->field.le_prev); \ - _Q_INVALIDATE((elm)->field.le_next); \ -} while (0) - -#define LIST_REPLACE(elm, elm2, field) do { \ - if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ - (elm2)->field.le_next->field.le_prev = \ - &(elm2)->field.le_next; \ - (elm2)->field.le_prev = (elm)->field.le_prev; \ - *(elm2)->field.le_prev = (elm2); \ - _Q_INVALIDATE((elm)->field.le_prev); \ - _Q_INVALIDATE((elm)->field.le_next); \ -} while (0) - -#endif - -/* end of bsd-queue.h */ diff --git a/src/nat/nat.c b/src/nat/nat.c index 7e83134bd..e92f57259 100644 --- a/src/nat/nat.c +++ b/src/nat/nat.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - (C) 2009, 2010 Christian Grothoff (and other contributing authors) + (C) 2009, 2010, 2011 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 @@ -18,345 +18,841 @@ Boston, MA 02111-1307, USA. */ -/* - * Parts of this file have been adapted from the Transmission project: - * Originally licensed by the GPL version 2. - * Copyright (C) 2007-2009 Charles Kerr - */ - /** * @file nat/nat.c * @brief Library handling UPnP and NAT-PMP port forwarding and * external IP address retrieval - * * @author Milan Bouchet-Valat + * @author Christian Grothoff + * + * TODO: + * - implement UPnP/PMP support + * - repeatedly perform certain checks again to notice changes */ #include "platform.h" #include "gnunet_util_lib.h" +#include "gnunet_resolver_service.h" #include "gnunet_nat_lib.h" -#include "nat.h" -#include "natpmp.h" -#include "upnp.h" + + +/** + * How long until we give up on transmitting the welcome message? + */ +#define HOSTNAME_RESOLVE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + + +/** + * Where did the given local address originate from? + * To be used for debugging as well as in the future + * to remove all addresses from a certain source when + * we reevaluate the source. + */ +enum LocalAddressSource + { + /** + * Address was obtained by DNS resolution of the external hostname + * given in the configuration (i.e. hole-punched DynDNS setup). + * FIXME: repeatedly do the lookup to notice changes! + */ + LAL_EXTERNAL_IP, + + /** + * Address was obtained by looking up our own hostname in DNS. + * FIXME: repeatedly do the lookup to notice changes! + */ + LAL_HOSTNAME_DNS, + + /** + * Address was obtained by scanning our hosts's network interfaces + * and taking their address (no DNS involved). + * FIXME: repeatedly do the lookup to notice changes! + */ + LAL_INTERFACE_ADDRESS, + + /* TODO: add UPnP, etc. */ + + /** + * End of the list. + */ + LAL_END + + }; + + +/** + * List of local addresses that we currently deem valid. Actual + * struct is followed by the 'struct sockaddr'. Note that the code + * intentionally makes no attempt to ensure that a particular address + * is only listed once (especially since it may come from different + * sources, and the source is an "internal" construct). + */ +struct LocalAddressList +{ + /** + * This is a linked list. + */ + struct LocalAddressList *next; + + /** + * Previous entry. + */ + struct LocalAddressList *prev; + + /** + * Number of bytes of address that follow. + */ + socklen_t addrlen; + + /** + * Origin of the local address. + */ + enum LocalAddressSource source; +}; + /** * Handle for active NAT registrations. */ struct GNUNET_NAT_Handle { + + /** + * Configuration to use. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Function to call when we learn about a new address. + */ + GNUNET_NAT_AddressCallback address_callback; + /** - * Handle for UPnP operations. + * Function to call when we notice another peer asking for + * connection reversal. */ - struct GNUNET_NAT_UPNP_Handle *upnp; + GNUNET_NAT_ReversalCallback reversal_callback; /** - * Handle for NAT PMP operations. + * Closure for 'callback'. */ - struct GNUNET_NAT_NATPMP_Handle *natpmp; + void *callback_cls; /** - * LAN address as passed by the caller + * Handle for (DYN)DNS lookup of our external IP. */ - struct sockaddr *local_addr; + struct GNUNET_RESOLVER_RequestHandle *ext_dns; /** - * External address as reported by found NAT box + * Handle for request of hostname resolution, non-NULL if pending. */ - struct sockaddr *ext_addr; + struct GNUNET_RESOLVER_RequestHandle *hostname_dns; /** - * External address as reported by each type of NAT box + * stdout pipe handle for the gnunet-nat-server process */ - struct sockaddr *ext_addr_upnp; - struct sockaddr *ext_addr_natpmp; + struct GNUNET_DISK_PipeHandle *server_stdout; /** - * External address and port where packets are redirected + * stdout file handle (for reading) for the gnunet-nat-server process */ - struct sockaddr *contact_addr; + const struct GNUNET_DISK_FileHandle *server_stdout_handle; - GNUNET_NAT_AddressCallback callback; + /** + * Linked list of currently valid addresses (head). + */ + struct LocalAddressList *lal_head; /** - * Closure for 'callback'. + * Linked list of currently valid addresses (tail). */ - void *callback_cls; + struct LocalAddressList *lal_tail; - GNUNET_SCHEDULER_TaskIdentifier pulse_timer; + /** + * How long do we wait for restarting a crashed gnunet-nat-server? + */ + struct GNUNET_TIME_Relative server_retry_delay; - enum GNUNET_NAT_PortState natpmp_status; + /** + * ID of select gnunet-nat-server stdout read task + */ + GNUNET_SCHEDULER_TaskIdentifier server_read_task; - enum GNUNET_NAT_PortState upnp_status; + /** + * The process id of the server process (if behind NAT) + */ + struct GNUNET_OS_Process *server_proc; - int is_enabled; + /** + * LAN address as passed by the caller (array). + */ + struct sockaddr **local_addrs; - int should_change; + /** + * Length of the 'local_addrs'. + */ + socklen_t *local_addrlens; + + /** + * Number of entries in 'local_addrs' array. + */ + unsigned int num_local_addrs; + + /** + * The our external address (according to config, UPnP may disagree...) + */ + char *external_address; + + /** + * Presumably our internal address (according to config) + */ + char *internal_address; + + /** + * Is this transport configured to be behind a NAT? + */ + int behind_nat; + + /** + * Has the NAT been punched? (according to config) + */ + int nat_punched; + + /** + * Is this transport configured to allow connections to NAT'd peers? + */ + int enable_nat_client; - int port_mapped; + /** + * Should we run the gnunet-nat-server? + */ + int enable_nat_server; - int old_status; + /** + * Are we allowed to try UPnP/PMP for NAT traversal? + */ + int enable_upnp; - int new_status; + /** + * Should we use local addresses (loopback)? (according to config) + */ + int use_localaddresses; - int did_warn; + /** + * Is using IPv6 disabled? + */ + int disable_ipv6; - int processing; + /** + * Is this TCP or UDP? + */ + int is_tcp; - uint16_t public_port; + /** + * Port we advertise to the outside. + */ + uint16_t adv_port; }; -#ifdef DEBUG -static const char * -get_nat_state_str (enum GNUNET_NAT_PortState state) -{ - switch (state) - { - case GNUNET_NAT_PORT_MAPPING: - return "Starting"; - case GNUNET_NAT_PORT_MAPPED: - return "Forwarded"; - case GNUNET_NAT_PORT_UNMAPPING: - return "Stopping"; - case GNUNET_NAT_PORT_UNMAPPED: - return "Not forwarded"; - case GNUNET_NAT_PORT_ERROR: - return "Redirection failed"; - default: - return "not found"; - } -} -#endif +/** + * Try to start the gnunet-nat-server (if it is not + * already running). + * + * @param h handle to NAT + */ +static void +start_gnunet_nat_server (struct GNUNET_NAT_Handle *h); -static int -get_traversal_status (const struct GNUNET_NAT_Handle *h) + +/** + * Add the given address to the list of 'local' addresses, thereby + * making it a 'legal' address for this peer to have. + * + * @param plugin the plugin + * @param src where did the local address originate from? + * @param arg the address, some 'struct sockaddr' + * @param arg_size number of bytes in arg + */ +static void +add_to_address_list_as_is (struct GNUNET_NAT_Handle *h, + enum LocalAddressSource src, + const struct sockaddr *arg, + socklen_t arg_size) { - return GNUNET_MAX (h->natpmp_status, h->upnp_status); + struct LocalAddressList *lal; + + lal = GNUNET_malloc (sizeof (struct LocalAddressList) + arg_size); + memcpy (&lal[1], arg, arg_size); + lal->addrlen = arg_size; + lal->source = src; + GNUNET_CONTAINER_DLL_insert (h->lal_head, + h->lal_tail, + lal); + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "nat", + "Adding address `%s' from source %d\n", + GNUNET_a2s (arg, arg_size), + src); + h->address_callback (h->callback_cls, + GNUNET_YES, + arg, + arg_size); } /** - * Compare the sin(6)_addr fields of AF_INET or AF_INET(6) sockaddr. - * @param a first sockaddr - * @param b second sockaddr - * @return 0 if addresses are equal, non-null value otherwise */ -int -GNUNET_NAT_cmp_addr (const struct sockaddr *a, const struct sockaddr *b) + * Add the given address to the list of 'local' addresses, thereby + * making it a 'legal' address for this peer to have. Set the + * port number in the process to the advertised port and possibly + * also to zero (if we have the gnunet-nat-server). + * + * @param plugin the plugin + * @param src where did the local address originate from? + * @param arg the address, some 'struct sockaddr' + * @param arg_size number of bytes in arg + */ +static void +add_to_address_list (struct GNUNET_NAT_Handle *h, + enum LocalAddressSource src, + const struct sockaddr *arg, + socklen_t arg_size) { - if (!(a && b)) - return -1; - if ((a->sa_family == AF_INET) && (b->sa_family == AF_INET)) - return memcmp (&(((struct sockaddr_in *) a)->sin_addr), - &(((struct sockaddr_in *) b)->sin_addr), - sizeof (struct in_addr)); - if ((a->sa_family == AF_INET6) && (b->sa_family == AF_INET6)) - return memcmp (&(((struct sockaddr_in6 *) a)->sin6_addr), - &(((struct sockaddr_in6 *) b)->sin6_addr), - sizeof (struct in6_addr)); - return -1; + struct sockaddr_in s4; + const struct sockaddr_in *in4; + struct sockaddr_in6 s6; + const struct sockaddr_in6 *in6; + + if (arg_size == sizeof (struct sockaddr_in)) + { + in4 = (const struct sockaddr_in *) arg; + s4 = *in4; + s4.sin_port = htons (h->adv_port); + add_to_address_list_as_is (h, + src, + (const struct sockaddr*) &s4, + sizeof (struct sockaddr_in)); + if (GNUNET_YES == h->enable_nat_server) + { + /* also add with PORT = 0 to indicate NAT server is enabled */ + s4.sin_port = htons(0); + add_to_address_list_as_is (h, + src, + (const struct sockaddr*) &s4, + sizeof (struct sockaddr_in)); + } + } + else if (arg_size == sizeof (struct sockaddr_in6)) + { + if (GNUNET_YES != h->disable_ipv6) + { + in6 = (const struct sockaddr_in6 *) arg; + s6 = *in6; + s6.sin6_port = htons(h->adv_port); + add_to_address_list_as_is (h, + src, + (const struct sockaddr*) &s6, + sizeof (struct sockaddr_in6)); + } + } + else + { + GNUNET_assert (0); + } } /** - * Deal with a new IP address or port redirection: - * Send signals with the appropriate sockaddr (IP and port), free and changes - * or nullify the previous sockaddr. Change the port if needed. + * Add the given IP address to the list of 'local' addresses, thereby + * making it a 'legal' address for this peer to have. + * + * @param plugin the plugin + * @param src where did the local address originate from? + * @param arg the address, some 'struct in_addr' or 'struct in6_addr' + * @param arg_size number of bytes in arg */ static void -notify_change (struct GNUNET_NAT_Handle *h, - struct sockaddr *addr, size_t addrlen, int new_port_mapped) +add_ip_to_address_list (struct GNUNET_NAT_Handle *h, + enum LocalAddressSource src, + const void *addr, + socklen_t addrlen) { - if (new_port_mapped == h->port_mapped) - return; - h->port_mapped = new_port_mapped; - - if ((NULL != h->contact_addr) && (NULL != h->callback)) - h->callback (h->callback_cls, - GNUNET_NO, h->contact_addr, sizeof (h->contact_addr)); - GNUNET_free_non_null (h->contact_addr); - h->contact_addr = NULL; - GNUNET_free_non_null (h->ext_addr); - h->ext_addr = NULL; - if (NULL == addr) - return; - h->ext_addr = GNUNET_malloc (addrlen); - memcpy (h->ext_addr, addr, addrlen); + struct sockaddr_in s4; + const struct in_addr *in4; + struct sockaddr_in6 s6; + const struct in6_addr *in6; - /* Recreate the ext_addr:public_port bogus address to pass to the callback */ - if (h->ext_addr->sa_family == AF_INET) + if (addrlen == sizeof (struct in_addr)) { - struct sockaddr_in *tmp_addr; - - tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in)); - tmp_addr->sin_family = AF_INET; -#ifdef HAVE_SOCKADDR_IN_SIN_LEN - tmp_addr->sin_len = sizeof (struct sockaddr_in); + in4 = (const struct in_addr *) addr; + memset (&s4, 0, sizeof (s4)); + s4.sin_family = AF_INET; + s4.sin_port = 0; +#if HAVE_SOCKADDR_IN_SIN_LEN + s4.sin_len = (u_char) sizeof (struct sockaddr_in); #endif - tmp_addr->sin_port = h->port_mapped ? htons (h->public_port) : 0; - tmp_addr->sin_addr = ((struct sockaddr_in *) h->ext_addr)->sin_addr; - h->contact_addr = (struct sockaddr *) tmp_addr; - - if (NULL != h->callback) - h->callback (h->callback_cls, - GNUNET_YES, - h->contact_addr, sizeof (struct sockaddr_in)); + s4.sin_addr = *in4; + add_to_address_list (h, + src, + (const struct sockaddr*) &s4, + sizeof (struct sockaddr_in)); + if (GNUNET_YES == h->enable_nat_server) + { + /* also add with PORT = 0 to indicate NAT server is enabled */ + s4.sin_port = htons(0); + add_to_address_list (h, + src, + (const struct sockaddr*) &s4, + sizeof (struct sockaddr_in)); + + } } - else if (h->ext_addr->sa_family == AF_INET6) + else if (addrlen == sizeof (struct in6_addr)) { - struct sockaddr_in6 *tmp_addr; - - tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in6)); - tmp_addr->sin6_family = AF_INET6; -#ifdef HAVE_SOCKADDR_IN_SIN_LEN - tmp_addr->sin6_len = sizeof (struct sockaddr_in6); + if (GNUNET_YES != h->disable_ipv6) + { + in6 = (const struct in6_addr *) addr; + memset (&s6, 0, sizeof (s6)); + s6.sin6_family = AF_INET6; + s6.sin6_port = htons(h->adv_port); +#if HAVE_SOCKADDR_IN_SIN_LEN + s6.sin6_len = (u_char) sizeof (struct sockaddr_in6); #endif - tmp_addr->sin6_port = h->port_mapped ? htons (h->public_port) : 0; - tmp_addr->sin6_addr = ((struct sockaddr_in6 *) h->ext_addr)->sin6_addr; - h->contact_addr = (struct sockaddr *) tmp_addr; - - if (NULL != h->callback) - h->callback (h->callback_cls, - GNUNET_YES, - h->contact_addr, sizeof (struct sockaddr_in6)); + s6.sin6_addr = *in6; + add_to_address_list (h, + src, + (const struct sockaddr*) &s6, + sizeof (struct sockaddr_in6)); + } } else { - GNUNET_break (0); + GNUNET_assert (0); } } -static void nat_pulse (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc); +/** + * Our (external) hostname was resolved and the configuration says that + * the NAT was hole-punched. + * + * @param cls the 'struct Plugin' + * @param addr NULL on error, otherwise result of DNS lookup + * @param addrlen number of bytes in addr + */ static void -pulse_cb (struct GNUNET_NAT_Handle *h) +process_external_ip (void *cls, + const struct sockaddr *addr, + socklen_t addrlen) { - socklen_t addrlen; - int port_mapped; + struct GNUNET_NAT_Handle *h = cls; - /* One of the protocols is still working, wait for it to complete */ - if (h->processing) - return; + if (addr == NULL) + { + h->ext_dns = NULL; + /* FIXME: schedule task to resolve IP again in the + future, and if the result changes, update the + local address list accordingly */ + return; + } + add_to_address_list (h, LAL_EXTERNAL_IP, addr, addrlen); +} - h->new_status = get_traversal_status (h); - if ((h->old_status != h->new_status) && - ((h->new_status == GNUNET_NAT_PORT_UNMAPPED) || - (h->new_status == GNUNET_NAT_PORT_ERROR))) - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, - "NAT", - _ - ("Port redirection failed: no UPnP or NAT-PMP routers supporting this feature found\n")); -#ifdef DEBUG - if (h->new_status != h->old_status) - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "NAT", - _("State changed from `%s' to `%s'\n"), - get_nat_state_str (h->old_status), - get_nat_state_str (h->new_status)); -#endif - port_mapped = (h->new_status == GNUNET_NAT_PORT_MAPPED); - if (!(h->ext_addr_upnp || h->ext_addr_natpmp)) +/** + * Function called by the resolver for each address obtained from DNS + * for our own hostname. Add the addresses to the list of our IP + * addresses. + * + * @param cls closure + * @param addr one of the addresses of the host, NULL for the last address + * @param addrlen length of the address + */ +static void +process_hostname_ip (void *cls, + const struct sockaddr *addr, socklen_t addrlen) +{ + struct GNUNET_NAT_Handle *h = cls; + + if (addr == NULL) { - /* Address has just changed and we could not get it, or it's the first try, - * and we're not waiting for a reply from UPnP or NAT-PMP */ - if (((NULL != h->ext_addr) || - (GNUNET_NO == h->did_warn)) && h->processing != 0) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, - "NAT", - _("Could not determine external IP address\n")); - h->did_warn = GNUNET_YES; - } - notify_change (h, NULL, 0, port_mapped); + h->hostname_dns = NULL; + /* FIXME: schedule task to resolve IP again in the + future, and if the result changes, update the + address list accordingly */ + return; } - else if (h->ext_addr_upnp - && GNUNET_NAT_cmp_addr (h->ext_addr, h->ext_addr_upnp) != 0) + add_to_address_list (h, LAL_HOSTNAME_DNS, addr, addrlen); +} + + +/** + * Add the IP of our network interface to the list of + * our IP addresses. + * + * @param cls the 'struct GNUNET_NAT_Handle' + * @param name name of the interface + * @param isDefault do we think this may be our default interface + * @param addr address of the interface + * @param addrlen number of bytes in addr + * @return GNUNET_OK to continue iterating + */ +static int +process_interfaces (void *cls, + const char *name, + int isDefault, + const struct sockaddr *addr, socklen_t addrlen) +{ + struct GNUNET_NAT_Handle *h = cls; + const struct sockaddr_in *s4; + const struct sockaddr_in6 *s6; + const void *ip; + char buf[INET6_ADDRSTRLEN]; + + switch (addr->sa_family) { - addrlen = h->ext_addr_upnp->sa_family == AF_INET ? - sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6); - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, - "NAT", - _("External IP address changed to %s\n"), - GNUNET_a2s (h->ext_addr_upnp, addrlen)); - notify_change (h, h->ext_addr_upnp, addrlen, port_mapped); + case AF_INET: + s4 = (struct sockaddr_in *) addr; + ip = &s4->sin_addr; + if (GNUNET_YES == h->use_localaddresses) + add_ip_to_address_list (h, + LAL_INTERFACE_ADDRESS, + &s4->sin_addr, + sizeof (struct in_addr)); + break; + case AF_INET6: + s6 = (struct sockaddr_in6 *) addr; + if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr)) + { + /* skip link local addresses */ + return GNUNET_OK; + } + ip = &s6->sin6_addr; + if (GNUNET_YES == h->use_localaddresses) + add_ip_to_address_list (h, + LAL_INTERFACE_ADDRESS, + &s6->sin6_addr, + sizeof (struct in6_addr)); + break; + default: + GNUNET_break (0); + break; } - else if (h->ext_addr_natpmp - && GNUNET_NAT_cmp_addr (h->ext_addr, h->ext_addr_natpmp) != 0) + if ( (h->internal_address == NULL) && + (h->server_proc == NULL) && + (h->server_read_task == GNUNET_SCHEDULER_NO_TASK) && + (GNUNET_YES == isDefault) && + ( (addr->sa_family == AF_INET) || (addr->sa_family == AF_INET6) ) ) { - addrlen = h->ext_addr_natpmp->sa_family == AF_INET ? - sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6); - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "NAT", - _("External IP address changed to `%s'\n"), - GNUNET_a2s (h->ext_addr_natpmp, addrlen)); - notify_change (h, h->ext_addr_natpmp, addrlen, port_mapped); + /* no internal address configured, but we found a "default" + interface, try using that as our 'internal' address */ + h->internal_address = GNUNET_strdup (inet_ntop (addr->sa_family, + ip, + buf, + sizeof (buf))); + start_gnunet_nat_server (h); } - - h->pulse_timer = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &nat_pulse, h); + return GNUNET_OK; } -static void -upnp_pulse_cb (int status, struct sockaddr *ext_addr, void *cls) + +/** + * Return the actual path to a file found in the current + * PATH environment variable. + * + * @param binary the name of the file to find + * @return path to binary, NULL if not found + */ +static char * +get_path_from_PATH (const char *binary) { - struct GNUNET_NAT_Handle *h = cls; + char *path; + char *pos; + char *end; + char *buf; + const char *p; + + p = getenv ("PATH"); + if (p == NULL) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, + "tcp", + _("PATH environment variable is unset.\n")); + return NULL; + } + path = GNUNET_strdup (p); /* because we write on it */ + buf = GNUNET_malloc (strlen (path) + 20); + pos = path; + + while (NULL != (end = strchr (pos, PATH_SEPARATOR))) + { + *end = '\0'; + sprintf (buf, "%s/%s", pos, binary); + if (GNUNET_DISK_file_test (buf) == GNUNET_YES) + { + GNUNET_free (path); + return buf; + } + pos = end + 1; + } + sprintf (buf, "%s/%s", pos, binary); + if (GNUNET_DISK_file_test (buf) == GNUNET_YES) + { + GNUNET_free (path); + return buf; + } + GNUNET_free (buf); + GNUNET_free (path); + return NULL; +} - h->upnp_status = status; - h->ext_addr_upnp = ext_addr; - h->processing--; - pulse_cb (h); +/** + * Check whether the suid bit is set on a file. + * Attempts to find the file using the current + * PATH environment variable as a search path. + * + * @param binary the name of the file to check + * @return GNUNET_YES if the file is SUID, + * GNUNET_NO if not, + * GNUNET_SYSERR on error + */ +static int +check_gnunet_nat_binary (const char *binary) +{ + struct stat statbuf; + char *p; +#ifdef MINGW + SOCKET rawsock; + char *binaryexe; + + GNUNET_asprintf (&binaryexe, "%s.exe", binary); + p = get_path_from_PATH (binaryexe); + free (binaryexe); +#else + p = get_path_from_PATH (binary); +#endif + if (p == NULL) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, + "tcp", + _("Could not find binary `%s' in PATH!\n"), + binary); + return GNUNET_NO; + } + if (0 != STAT (p, &statbuf)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("stat (%s) failed: %s\n"), + p, + STRERROR (errno)); + GNUNET_free (p); + return GNUNET_SYSERR; + } + GNUNET_free (p); +#ifndef MINGW + if ( (0 != (statbuf.st_mode & S_ISUID)) && + (statbuf.st_uid == 0) ) + return GNUNET_YES; + return GNUNET_NO; +#else + rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (INVALID_SOCKET == rawsock) + { + DWORD err = GetLastError (); + GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, + "tcp", + "socket (AF_INET, SOCK_RAW, IPPROTO_ICMP) failed! GLE = %d\n", err); + return GNUNET_NO; /* not running as administrator */ + } + closesocket (rawsock); + return GNUNET_YES; +#endif } -#if 0 + +/** + * Task that restarts the gnunet-nat-server process after a crash + * after a certain delay. + * + * @param cls the 'struct GNUNET_NAT_Handle' + * @param tc scheduler context + */ static void -natpmp_pulse_cb (int status, struct sockaddr *ext_addr, void *cls) +restart_nat_server (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) { struct GNUNET_NAT_Handle *h = cls; - h->natpmp_status = status; - h->ext_addr_natpmp = ext_addr; - - h->processing--; - pulse_cb (h); + h->server_read_task = GNUNET_SCHEDULER_NO_TASK; + if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) + return; + start_gnunet_nat_server (h); } -#endif + +/** + * We have been notified that gnunet-nat-server has written something to stdout. + * Handle the output, then reschedule this function to be called again once + * more is available. + * + * @param cls the NAT handle + * @param tc the scheduling context + */ static void -nat_pulse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +nat_server_read (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) { struct GNUNET_NAT_Handle *h = cls; - - /* Stop if we're already waiting for an action to complete */ - h->pulse_timer = GNUNET_SCHEDULER_NO_TASK; - if (h->processing) + char mybuf[40]; + ssize_t bytes; + size_t i; + int port; + const char *port_start; + struct sockaddr_in sin_addr; + + h->server_read_task = GNUNET_SCHEDULER_NO_TASK; + if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) return; - h->old_status = get_traversal_status (h); - - /* Only update the protocol that has been successful until now */ - if (h->upnp_status >= GNUNET_NAT_PORT_UNMAPPED) + memset (mybuf, 0, sizeof(mybuf)); + bytes = GNUNET_DISK_file_read(h->server_stdout_handle, + mybuf, + sizeof(mybuf)); + if (bytes < 1) { - h->processing = 1; - GNUNET_NAT_UPNP_pulse (h->upnp, h->is_enabled, GNUNET_YES); - - /* Wait for the callback to call pulse_cb() to handle changes */ +#if DEBUG_TCP_NAT + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "nat", + "Finished reading from server stdout with code: %d\n", + bytes); +#endif + if (0 != GNUNET_OS_process_kill (h->server_proc, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + GNUNET_OS_process_wait (h->server_proc); + GNUNET_OS_process_close (h->server_proc); + h->server_proc = NULL; + GNUNET_DISK_pipe_close (h->server_stdout); + h->server_stdout = NULL; + h->server_stdout_handle = NULL; + /* now try to restart it */ + h->server_retry_delay = GNUNET_TIME_relative_multiply (h->server_retry_delay, 2); + h->server_retry_delay = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_HOURS, + h->server_retry_delay); + h->server_read_task = GNUNET_SCHEDULER_add_delayed (h->server_retry_delay, + &restart_nat_server, + h); return; } - else if (h->natpmp_status >= GNUNET_NAT_PORT_UNMAPPED) + + port_start = NULL; + for (i = 0; i < sizeof(mybuf); i++) { - h->processing = 1; -#if 0 - GNUNET_NAT_NATPMP_pulse (h->natpmp, h->is_enabled); -#endif + if (mybuf[i] == '\n') + { + mybuf[i] = '\0'; + break; + } + if ( (mybuf[i] == ':') && (i + 1 < sizeof(mybuf)) ) + { + mybuf[i] = '\0'; + port_start = &mybuf[i + 1]; + } } - else /* try both */ - { - h->processing = 2; - GNUNET_NAT_UPNP_pulse (h->upnp, h->is_enabled, GNUNET_YES); -#if 0 - GNUNET_NAT_NATPMP_pulse (h->natpmp, h->is_enabled, &natpmp_pulse_cb, h); + /* construct socket address of sender */ + memset (&sin_addr, 0, sizeof (sin_addr)); + sin_addr.sin_family = AF_INET; +#if HAVE_SOCKADDR_IN_SIN_LEN + sin_addr.sin_len = sizeof (sin_addr); #endif + if ( (NULL == port_start) || + (1 != sscanf (port_start, "%d", &port)) || + (-1 == inet_pton(AF_INET, mybuf, &sin_addr.sin_addr)) ) + { + /* should we restart gnunet-nat-server? */ + GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, + "nat", + _("gnunet-nat-server generated malformed address `%s'\n"), + mybuf); + h->server_read_task + = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + h->server_stdout_handle, + &nat_server_read, + h); + return; } + sin_addr.sin_port = htons((uint16_t) port); +#if DEBUG_NAT + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "nat", + "gnunet-nat-server read: %s:%d\n", + mybuf, port); +#endif + h->reversal_callback (h->callback_cls, + (const struct sockaddr*) &sin_addr, + sizeof (sin_addr)); + h->server_read_task = + GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + h->server_stdout_handle, + &nat_server_read, + h); +} + + +/** + * Try to start the gnunet-nat-server (if it is not + * already running). + * + * @param h handle to NAT + */ +static void +start_gnunet_nat_server (struct GNUNET_NAT_Handle *h) +{ + if ( (h->behind_nat == GNUNET_YES) && + (h->enable_nat_server == GNUNET_YES) && + (h->internal_address != NULL) && + (NULL != (h->server_stdout = GNUNET_DISK_pipe (GNUNET_YES, + GNUNET_NO, + GNUNET_YES))) ) + { +#if DEBUG_NAT + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "nat" + "Starting %s at `%s'\n", + "gnunet-nat-server", + h->internal_address); +#endif + /* Start the server process */ + h->server_proc = GNUNET_OS_start_process (NULL, + h->server_stdout, + "gnunet-nat-server", + "gnunet-nat-server", + h->internal_address, + NULL); + if (h->server_proc == NULL) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, + "nat", + _("Failed to start %s\n"), + "gnunet-nat-server"); + GNUNET_DISK_pipe_close (h->server_stdout); + h->server_stdout = NULL; + } + else + { + /* Close the write end of the read pipe */ + GNUNET_DISK_pipe_close_end(h->server_stdout, + GNUNET_DISK_PIPE_END_WRITE); + h->server_stdout_handle + = GNUNET_DISK_pipe_handle (h->server_stdout, + GNUNET_DISK_PIPE_END_READ); + h->server_read_task + = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + h->server_stdout_handle, + &nat_server_read, + h); + } + } } @@ -367,55 +863,165 @@ nat_pulse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) * is taken from the corresponding sockaddr_in[6] field. * * @param cfg configuration to use + * @param is_tcp GNUNET_YES for TCP, GNUNET_NO for UDP + * @param adv_port advertised port (port we are either bound to or that our OS + * locally performs redirection from to our bound port). + * @param num_addrs number of addresses in 'addrs' * @param addr the local address packets should be redirected to * @param addrlen actual lenght of the address - * @param callback function to call everytime the public IP address changes - * @param callback_cls closure for callback + * @param address_callback function to call everytime the public IP address changes + * @param reversal_callback function to call if someone wants connection reversal from us + * @param callback_cls closure for callbacks * @return NULL on error, otherwise handle that can be used to unregister */ struct GNUNET_NAT_Handle * GNUNET_NAT_register (const struct GNUNET_CONFIGURATION_Handle *cfg, - const struct sockaddr *addr, - socklen_t addrlen, - GNUNET_NAT_AddressCallback callback, void *callback_cls) + int is_tcp, + uint16_t adv_port, + unsigned int num_addrs, + const struct sockaddr **addrs, + const socklen_t *addrlens, + GNUNET_NAT_AddressCallback address_callback, + GNUNET_NAT_ReversalCallback reversal_callback, + void *callback_cls) { struct GNUNET_NAT_Handle *h; + struct in_addr in_addr; + unsigned int i; h = GNUNET_malloc (sizeof (struct GNUNET_NAT_Handle)); + h->server_retry_delay = GNUNET_TIME_UNIT_SECONDS; + h->cfg = cfg; + h->is_tcp = is_tcp; + h->address_callback = address_callback; + h->reversal_callback = reversal_callback; + h->callback_cls = callback_cls; + h->num_local_addrs = num_addrs; + h->adv_port = adv_port; + if (num_addrs != 0) + { + h->local_addrs = GNUNET_malloc (num_addrs * sizeof (struct sockaddr*)); + h->local_addrlens = GNUNET_malloc (num_addrs * sizeof (socklen_t)); + for (i=0;ilocal_addrlens[i] = addrlens[i]; + h->local_addrs[i] = GNUNET_malloc (addrlens[i]); + memcpy (h->local_addrs[i], addrs[i], addrlens[i]); + } + } - if (addr) + if (GNUNET_OK == + GNUNET_CONFIGURATION_have_value (cfg, + "nat", + "INTERNAL_ADDRESS")) { - GNUNET_assert ((addr->sa_family == AF_INET) || - (addr->sa_family == AF_INET6)); - h->local_addr = GNUNET_malloc (addrlen); - memcpy (h->local_addr, addr, addrlen); - if (addr->sa_family == AF_INET) - { - h->public_port = ntohs (((struct sockaddr_in *) addr)->sin_port); - ((struct sockaddr_in *) h->local_addr)->sin_port = 0; - } - else if (addr->sa_family == AF_INET6) - { - h->public_port = ntohs (((struct sockaddr_in6 *) addr)->sin6_port); - ((struct sockaddr_in6 *) h->local_addr)->sin6_port = 0; - } + (void) GNUNET_CONFIGURATION_get_value_string (cfg, + "nat", + "INTERNAL_ADDRESS", + &h->internal_address); + } + if ( (h->internal_address != NULL) && + (inet_pton(AF_INET, h->internal_address, &in_addr) != 1) ) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, + "nat", + _("Malformed %s `%s' given in configuration!\n"), + "INTERNAL_ADDRESS", + h->internal_address); + GNUNET_free (h->internal_address); + h->internal_address = NULL; + } + + if (GNUNET_OK == + GNUNET_CONFIGURATION_have_value (cfg, + "nat", + "EXTERNAL_ADDRESS")) + { + (void) GNUNET_CONFIGURATION_get_value_string (cfg, + "nat", + "EXTERNAL_ADDRESS", + &h->external_address); + } + if ( (h->external_address != NULL) && + (inet_pton(AF_INET, h->external_address, &in_addr) != 1) ) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, + "nat", + _("Malformed %s `%s' given in configuration!\n"), + "EXTERNAL_ADDRESS", + h->external_address); + GNUNET_free (h->external_address); + h->external_address = NULL; + } + h->behind_nat = GNUNET_CONFIGURATION_get_value_yesno (cfg, + "nat", + "BEHIND_NAT"); + h->nat_punched = GNUNET_CONFIGURATION_get_value_yesno (cfg, + "nat", + "NAT_PUNCHED"); + h->enable_nat_client = GNUNET_CONFIGURATION_get_value_yesno (cfg, + "nat", + "ENABLE_NAT_CLIENT"); + h->enable_nat_server = GNUNET_CONFIGURATION_get_value_yesno (cfg, + "nat", + "ENABLE_NAT_SERVER"); + h->enable_upnp = GNUNET_CONFIGURATION_get_value_yesno (cfg, + "nat", + "ENABLE_UPNP"); + h->use_localaddresses = GNUNET_CONFIGURATION_get_value_yesno (cfg, + "nat", + "USE_LOCALADDR"); + h->disable_ipv6 = GNUNET_CONFIGURATION_get_value_yesno(cfg, + "nat", + "DISABLEV6"); + if (NULL == reversal_callback) + h->enable_nat_server = GNUNET_NO; + + /* Check if NAT was hole-punched */ + if ( (NULL != h->address_callback) && + (h->external_address != NULL) && + (h->nat_punched == GNUNET_YES) ) + { + h->ext_dns = GNUNET_RESOLVER_ip_get (h->external_address, + AF_INET, + GNUNET_TIME_UNIT_MINUTES, + &process_external_ip, + h); + h->enable_nat_server = GNUNET_NO; + h->enable_upnp = GNUNET_NO; + } + + /* Test for SUID binaries */ + if ( (h->behind_nat == GNUNET_YES) && + (GNUNET_YES == h->enable_nat_server) && + (GNUNET_YES != check_gnunet_nat_binary("gnunet-nat-server")) ) + { + h->enable_nat_server = GNUNET_NO; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Configuration requires `%s', but binary is not installed properly (SUID bit not set). Option disabled.\n"), + "gnunet-nat-server"); + } + if ( (GNUNET_YES == h->enable_nat_client) && + (GNUNET_YES != check_gnunet_nat_binary("gnunet-nat-client")) ) + { + h->enable_nat_client = GNUNET_NO; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Configuration requires `%s', but binary is not installed properly (SUID bit not set). Option disabled.\n"), + "gnunet-nat-client"); + } + + start_gnunet_nat_server (h); + + /* FIXME: add support for UPnP, etc */ + + if (NULL != h->address_callback) + { + GNUNET_OS_network_interfaces_list (&process_interfaces, h); + h->hostname_dns = GNUNET_RESOLVER_hostname_resolve (AF_UNSPEC, + HOSTNAME_RESOLVE_TIMEOUT, + &process_hostname_ip, + h); } - h->should_change = GNUNET_YES; - h->is_enabled = GNUNET_YES; - h->upnp_status = GNUNET_NAT_PORT_UNMAPPED; - h->natpmp_status = GNUNET_NAT_PORT_UNMAPPED; - h->callback = callback; - h->callback_cls = callback_cls; - h->upnp = - GNUNET_NAT_UPNP_init (h->local_addr, addrlen, h->public_port, - &upnp_pulse_cb, h); -#if 0 - h->natpmp = - GNUNET_NAT_NATPMP_init (h->local_addr, addrlen, h->public_port, - &natpmp_pulse_cb, h); -#endif - h->pulse_timer = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &nat_pulse, h); return h; } @@ -429,22 +1035,175 @@ GNUNET_NAT_register (const struct GNUNET_CONFIGURATION_Handle *cfg, void GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *h) { - GNUNET_NAT_UPNP_pulse (h->upnp, GNUNET_NO, GNUNET_NO); - GNUNET_NAT_UPNP_close (h->upnp); + unsigned int i; + struct LocalAddressList *lal; -#if 0 - GNUNET_NAT_NATPMP_pulse (h->natpmp, GNUNET_NO); - GNUNET_NAT_NATPMP_close (h->natpmp); + if (h->ext_dns != NULL) + { + GNUNET_RESOLVER_request_cancel (h->ext_dns); + h->ext_dns = NULL; + } + if (NULL != h->hostname_dns) + { + GNUNET_RESOLVER_request_cancel (h->hostname_dns); + h->hostname_dns = NULL; + } + if (GNUNET_SCHEDULER_NO_TASK != h->server_read_task) + { + GNUNET_SCHEDULER_cancel (h->server_read_task); + h->server_read_task = GNUNET_SCHEDULER_NO_TASK; + } + if (NULL != h->server_proc) + { + if (0 != GNUNET_OS_process_kill (h->server_proc, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + GNUNET_OS_process_wait (h->server_proc); + GNUNET_OS_process_close (h->server_proc); + h->server_proc = NULL; + GNUNET_DISK_pipe_close (h->server_stdout); + h->server_stdout = NULL; + h->server_stdout_handle = NULL; + } + if (NULL != h->server_stdout) + { + GNUNET_DISK_pipe_close (h->server_stdout); + h->server_stdout = NULL; + h->server_stdout_handle = NULL; + } + while (NULL != (lal = h->lal_head)) + { + GNUNET_CONTAINER_DLL_remove (h->lal_head, + h->lal_tail, + lal); + h->address_callback (h->callback_cls, + GNUNET_NO, + (const struct sockaddr*) &lal[1], + lal->addrlen); + GNUNET_free (lal); + } + for (i=0;inum_local_addrs;i++) + GNUNET_free (h->local_addrs[i]); + GNUNET_free_non_null (h->local_addrs); + GNUNET_free_non_null (h->local_addrlens); + GNUNET_free_non_null (h->external_address); + GNUNET_free_non_null (h->internal_address); + GNUNET_free (h); +} + + +/** + * We learned about a peer (possibly behind NAT) so run the + * gnunet-nat-client to send dummy ICMP responses to cause + * that peer to connect to us (connection reversal). + * + * @param h NAT handle for us (largely used for configuration) + * @param sa the address of the peer (IPv4-only) + */ +void +GNUNET_NAT_run_client (struct GNUNET_NAT_Handle *h, + const struct sockaddr_in *sa) +{ + char inet4[INET_ADDRSTRLEN]; + char port_as_string[6]; + struct GNUNET_OS_Process *proc; + + if (GNUNET_YES != h->enable_nat_client) + return; /* not permitted / possible */ + + if (h->internal_address == NULL) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, + "nat", + _("Internal IP address not known, cannot use ICMP NAT traversal method\n")); + return; + } + GNUNET_assert (sa->sin_family == AF_INET); + if (NULL == inet_ntop (AF_INET, + &sa->sin_addr, + inet4, INET_ADDRSTRLEN)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop"); + return; + } + GNUNET_snprintf(port_as_string, + sizeof (port_as_string), + "%d", + h->adv_port); +#if DEBUG_TCP_NAT + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "nat", + _("Running gnunet-nat-client %s %s %u\n"), + h->internal_address, + inet4, + (unsigned int) h->adv_port); #endif + proc = GNUNET_OS_start_process (NULL, + NULL, + "gnunet-nat-client", + "gnunet-nat-client", + h->internal_address, + inet4, + port_as_string, + NULL); + if (NULL == proc) + return; + /* we know that the gnunet-nat-client will terminate virtually + instantly */ + GNUNET_OS_process_wait (proc); + GNUNET_OS_process_close (proc); +} - if (GNUNET_SCHEDULER_NO_TASK != h->pulse_timer) - GNUNET_SCHEDULER_cancel (h->pulse_timer); - GNUNET_free_non_null (h->local_addr); - GNUNET_free_non_null (h->ext_addr); - GNUNET_free_non_null (h->ext_addr_upnp); - GNUNET_free_non_null (h->ext_addr_natpmp); - GNUNET_free (h); +/** + * Test if the given address is (currently) a plausible IP address for this peer. + * + * @param h the handle returned by register + * @param addr IP address to test (IPv4 or IPv6) + * @param addrlen number of bytes in addr + * @return GNUNET_YES if the address is plausible, + * GNUNET_NO if the address is not plausible, + * GNUNET_SYSERR if the address is malformed + */ +int +GNUNET_NAT_test_address (struct GNUNET_NAT_Handle *h, + const void *addr, + socklen_t addrlen) +{ + struct LocalAddressList *pos; + const struct sockaddr_in *in4; + const struct sockaddr_in6 *in6; + + if ( (addrlen != sizeof (struct in_addr)) && + (addrlen != sizeof (struct in6_addr)) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + pos = h->lal_head; + while (NULL != pos) + { + if (pos->addrlen == sizeof (struct sockaddr_in)) + { + in4 = (struct sockaddr_in* ) &pos[1]; + if ( (addrlen == sizeof (struct in_addr)) && + (0 == memcmp (&in4->sin_addr, addr, sizeof (struct in_addr))) ) + return GNUNET_YES; + } + else if (pos->addrlen == sizeof (struct sockaddr_in6)) + { + in6 = (struct sockaddr_in6* ) &pos[1]; + if ( (addrlen == sizeof (struct in6_addr)) && + (0 == memcmp (&in6->sin6_addr, addr, sizeof (struct in6_addr))) ) + return GNUNET_YES; + } + else + { + GNUNET_assert (0); + } + pos = pos->next; + } + return GNUNET_YES; } + /* end of nat.c */ diff --git a/src/nat/nat.h b/src/nat/nat.h deleted file mode 100644 index 3a0f21e7b..000000000 --- a/src/nat/nat.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2007, 2008, 2009 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 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., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. -*/ - -/** - * @file nat/nat.h - * @brief Library handling UPnP and NAT-PMP port forwarding and - * external IP address retrieval - * @author Milan Bouchet-Valat - */ - -#ifndef NAT_H -#define NAT_H - -#define DEBUG GNUNET_YES - -/** - * Used to communicate with the UPnP and NAT-PMP plugins - */ -enum GNUNET_NAT_PortState -{ - GNUNET_NAT_PORT_ERROR, - - /** - * the port isn't forwarded - */ - GNUNET_NAT_PORT_UNMAPPED, - - /** - * we're cancelling the port forwarding - */ - GNUNET_NAT_PORT_UNMAPPING, - - /** - * we're in the process of trying to set up port forwarding - */ - GNUNET_NAT_PORT_MAPPING, - - /** - * we've successfully forwarded the port - */ - GNUNET_NAT_PORT_MAPPED -}; - - -/** - * Compare the sin(6)_addr fields of AF_INET or AF_INET(6) sockaddr. - * - * @param a first sockaddr - * @param b second sockaddr - * @return 0 if addresses are equal, non-null value otherwise - */ -int GNUNET_NAT_cmp_addr (const struct sockaddr *a, const struct sockaddr *b); - - -#endif diff --git a/src/nat/natpmp.c b/src/nat/natpmp.c deleted file mode 100644 index c6a5604d8..000000000 --- a/src/nat/natpmp.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2009 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 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., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. -*/ - -/* - * This file has been adapted from the Transmission project: - * Originally licensed by the GPL version 2. - * Copyright (C) 2007-2009 Charles Kerr - */ - -/** - * @file nat/natpmp.c - * @brief NAT-PMP support for the NAT library - * - * @author Milan Bouchet-Valat - */ -#include -#include -#include -#include - -#define ENABLE_STRNATPMPERR -#include - -#include "platform.h" -#include "gnunet_common.h" -#include "gnunet_nat_lib.h" -#include "nat.h" -#include "natpmp.h" - -#define LIFETIME_SECS 3600 -#define COMMAND_WAIT_SECS 8 -/* Component name for logging */ -#define COMP_NAT_NATPMP _("NAT (NAT-PMP))") - -enum NATPMP_state -{ - NATPMP_IDLE, - NATPMP_ERR, - NATPMP_DISCOVER, - NATPMP_RECV_PUB, - NATPMP_SEND_MAP, - NATPMP_RECV_MAP, - NATPMP_SEND_UNMAP, - NATPMP_RECV_UNMAP -} - ; - -struct GNUNET_NAT_NATPMP_Handle -{ - const struct sockaddr *addr; - socklen_t addrlen; - struct sockaddr *ext_addr; - int is_mapped; - int has_discovered; - int port; - time_t renew_time; - time_t command_time; - enum NATPMP_state state; - struct natpmp_t natpmp; -}; - - -static void -log_val (const char *func, int ret) -{ -#ifdef DEBUG - if (ret == NATPMP_TRYAGAIN) - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - COMP_NAT_NATPMP, _("%s retry (%d)\n"), func, ret); - if (ret >= 0) - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, - COMP_NAT_NATPMP, _("%s succeeded (%d)\n"), func, ret); - else - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - COMP_NAT_NATPMP, - "%s failed. natpmp returned %d (%s); errno is %d (%s)\n", - func, ret, strnatpmperr (ret), errno, strerror (errno)); -#endif -} - -struct GNUNET_NAT_NATPMP_Handle * -GNUNET_NAT_NATPMP_init (const struct sockaddr *addr, socklen_t addrlen, - u_short port) -{ - struct GNUNET_NAT_NATPMP_Handle *nat; - - nat = GNUNET_malloc (sizeof (struct GNUNET_NAT_NATPMP_Handle)); - nat->state = NATPMP_DISCOVER; - nat->port = port; - nat->addr = addr; - nat->addrlen = addrlen; - return nat; -} - -void -GNUNET_NAT_NATPMP_close (struct GNUNET_NAT_NATPMP_Handle *nat) -{ - if (nat) - { - closenatpmp (&nat->natpmp); - GNUNET_free (nat); - } -} - -static int -can_send_command (const struct GNUNET_NAT_NATPMP_Handle *nat) -{ - return time (NULL) >= nat->command_time; -} - -static void -set_command_time (struct GNUNET_NAT_NATPMP_Handle *nat) -{ - nat->command_time = time (NULL) + COMMAND_WAIT_SECS; -} - -int -GNUNET_NAT_NATPMP_pulse (struct GNUNET_NAT_NATPMP_Handle *nat, int is_enabled, - struct sockaddr **ext_addr) -{ -#if DEBUG - char buf[INET6_ADDRSTRLEN]; -#endif - struct sockaddr_in *v4; - struct sockaddr_in6 *v6; - int ret; - - /* Keep to NULL if address could not be found */ - *ext_addr = NULL; - - if (is_enabled && (nat->state == NATPMP_DISCOVER)) - { - int val = initnatpmp (&nat->natpmp); - log_val ("initnatpmp", val); - val = sendpublicaddressrequest (&nat->natpmp); - log_val ("sendpublicaddressrequest", val); - nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_PUB; - nat->has_discovered = 1; - set_command_time (nat); - } - - if ((nat->state == NATPMP_RECV_PUB) && can_send_command (nat)) - { - struct natpmpresp_t response; - const int val = readnatpmpresponseorretry (&nat->natpmp, - &response); - log_val ("readnatpmpresponseorretry", val); - if (val >= 0) - { - if (NULL != nat->ext_addr) - { - GNUNET_free (nat->ext_addr); - nat->ext_addr = NULL; - } - - if (response.pnu.publicaddress.family == AF_INET) - { - v4 = GNUNET_malloc (sizeof (struct sockaddr_in)); - nat->ext_addr = (struct sockaddr *) v4; - v4->sin_family = AF_INET; - v4->sin_port = response.pnu.newportmapping.mappedpublicport; - memcpy (&v4->sin_addr, &response.pnu.publicaddress.addr, - sizeof (struct in_addr)); -#ifdef DEBUG - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP, - _("Found public IP address %s\n"), - inet_ntop (AF_INET, - &response.pnu.publicaddress.addr, - buf, sizeof (buf))); -#endif - } - else - { - v6 = GNUNET_malloc (sizeof (struct sockaddr_in6)); - nat->ext_addr = (struct sockaddr *) v6; - v6->sin6_family = AF_INET6; - v6->sin6_port = response.pnu.newportmapping.mappedpublicport; - memcpy (&v6->sin6_addr, - &response.pnu.publicaddress.addr6, - (sizeof (struct in6_addr))); -#ifdef DEBUG - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP, - _("Found public IP address %s\n"), - inet_ntop (AF_INET6, - &response.pnu.publicaddress.addr6, - buf, sizeof (buf))); -#endif - } - *ext_addr = nat->ext_addr; - nat->state = NATPMP_IDLE; - } - else if (val != NATPMP_TRYAGAIN) - { - nat->state = NATPMP_ERR; - } - } - - if ((nat->state == NATPMP_IDLE) || (nat->state == NATPMP_ERR)) - { - if (nat->is_mapped && !is_enabled) - nat->state = NATPMP_SEND_UNMAP; - } - - if ((nat->state == NATPMP_SEND_UNMAP) && can_send_command (nat)) - { - const int val = - sendnewportmappingrequest (&nat->natpmp, NATPMP_PROTOCOL_TCP, - nat->port, nat->port, - 0); - log_val ("sendnewportmappingrequest", val); - nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_UNMAP; - set_command_time (nat); - } - - if (nat->state == NATPMP_RECV_UNMAP) - { - struct natpmpresp_t resp; - const int val = readnatpmpresponseorretry (&nat->natpmp, &resp); - log_val ("readnatpmpresponseorretry", val); - if (val >= 0) - { - const int p = resp.pnu.newportmapping.privateport; - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP, - _("No longer forwarding port %d\n"), p); - if (nat->port == p) - { - nat->port = -1; - nat->state = NATPMP_IDLE; - nat->is_mapped = 0; - } - } - else if (val != NATPMP_TRYAGAIN) - { - nat->state = NATPMP_ERR; - } - } - - if (nat->state == NATPMP_IDLE) - { - if (is_enabled && !nat->is_mapped && nat->has_discovered) - nat->state = NATPMP_SEND_MAP; - - else if (nat->is_mapped && time (NULL) >= nat->renew_time) - nat->state = NATPMP_SEND_MAP; - } - - if ((nat->state == NATPMP_SEND_MAP) && can_send_command (nat)) - { - const int val = - sendnewportmappingrequest (&nat->natpmp, NATPMP_PROTOCOL_TCP, - nat->port, - nat->port, - LIFETIME_SECS); - log_val ("sendnewportmappingrequest", val); - nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_MAP; - set_command_time (nat); - } - - if (nat->state == NATPMP_RECV_MAP) - { - struct natpmpresp_t resp; - const int val = readnatpmpresponseorretry (&nat->natpmp, &resp); - log_val ("readnatpmpresponseorretry", val); - if (val >= 0) - { - nat->state = NATPMP_IDLE; - nat->is_mapped = 1; - nat->renew_time = time (NULL) + LIFETIME_SECS; - nat->port = resp.pnu.newportmapping.privateport; - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP, - _("Port %d forwarded successfully\n"), nat->port); - } - else if (val != NATPMP_TRYAGAIN) - { - nat->state = NATPMP_ERR; - } - } - - switch (nat->state) - { - case NATPMP_IDLE: - ret = - nat->is_mapped ? GNUNET_NAT_PORT_MAPPED : GNUNET_NAT_PORT_UNMAPPED; - break; - - case NATPMP_DISCOVER: - ret = GNUNET_NAT_PORT_UNMAPPED; - break; - - case NATPMP_RECV_PUB: - case NATPMP_SEND_MAP: - case NATPMP_RECV_MAP: - ret = GNUNET_NAT_PORT_MAPPING; - break; - - case NATPMP_SEND_UNMAP: - case NATPMP_RECV_UNMAP: - ret = GNUNET_NAT_PORT_UNMAPPING; - break; - - default: - ret = GNUNET_NAT_PORT_ERROR; - break; - } - return ret; -} - -/* end of natpmp.c */ diff --git a/src/nat/natpmp.h b/src/nat/natpmp.h deleted file mode 100644 index 56d5f0034..000000000 --- a/src/nat/natpmp.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2009 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 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., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. -*/ - -/** - * @file nat/natpmp.h - * @brief NAT-PMP support for the NAT library - * - * @author Milan Bouchet-Valat - */ - -#ifndef NATPMP_H -#define NATPMP_H 1 - -#include "platform.h" - -struct GNUNET_NAT_NATPMP_Handle; - -struct GNUNET_NAT_NATPMP_Handle *GNUNET_NAT_NATPMP_init (const struct sockaddr - *addr, - socklen_t addrlen, - unsigned short port); - -void GNUNET_NAT_NATPMP_close (struct GNUNET_NAT_NATPMP_Handle *nat); - -int GNUNET_NAT_NATPMP_pulse (struct GNUNET_NAT_NATPMP_Handle *nat, - int is_enabled, struct sockaddr **ext_addr); - -#endif -/* NATPMP_H */ diff --git a/src/nat/test-nat.conf b/src/nat/test-nat.conf deleted file mode 100644 index 311eb3c15..000000000 --- a/src/nat/test-nat.conf +++ /dev/null @@ -1,355 +0,0 @@ -[PATHS] -SERVICEHOME = /tmp/nat-test -# SERVICEHOME = /var/lib/gnunet/ -# DEFAULTCONFIG = /etc/gnunet.conf -# If 'DEFAULTCONFIG' is not defined, the current -# configuration file is assumed to be the default, -# which is what we want by default... - -[gnunetd] -HOSTKEY = $SERVICEHOME/.hostkey - -[TESTING] -WEAKRANDOM = NO - -[client] -HOME = $SERVICEHOME - -[transport-tcp] -# Use 0 to ONLY advertise as a peer behind NAT (no port binding) -PORT = 2086 -ADVERTISED_PORT = 2086 - -# Are we behind NAT? -BEHIND_NAT = YES - -# Is the NAT hole-punched? -PUNCHED_NAT = NO - -# Disable UPNP by default until it gets cleaner! -ENABLE_UPNP = NO - -# External IP address of the NAT box (if known); IPv4 dotted-decimal ONLY at this time (should allow DynDNS!) -# normal interface IP address for non-NATed peers; -# possibly auto-detected (using UPnP) if possible if not specified -# EXTERNAL_ADDRESS = - -# Should we use ICMP-based NAT traversal to try connect to NATed peers -# or, if we are behind NAT, to allow connections to us? -ENABLE_ICMP_CLIENT = YES -ENABLE_ICMP_SERVER = YES - -# IP address of the interface connected to the NAT box; IPv4 dotted-decimal ONLY; -# normal interface IP address for non-NATed peers; -# likely auto-detected (via interface list) if not specified (!) -# INTERNAL_ADDRESS = - -# Only bind to a specific IP address; if both are given, this should be idential to INTERNAL_ADDRESS -# (but use different: this one restricts our bind!) -# BINDTO = - -TIMEOUT = 300000 - -# Disable IPv6 support -# DISABLEV6 = -# ACCEPT_FROM = -# ACCEPT_FROM6 = -# REJECT_FROM = -# REJECT_FROM6 = - -[transport-udp] -PORT = 2086 -# BEHIND_NAT = -# BINDTO = -# ALLOW_NAT = -# ONLY_NAT_ADDRESSES = -# INTERNAL_ADDRESS = -# EXTERNAL_ADDRESS = - -[transport-http] -PORT = 1080 - -[transport-https] -PORT = 4433 - -[arm] -PORT = 2087 -HOSTNAME = localhost -HOME = $SERVICEHOME -CONFIG = $DEFAULTCONFIG -BINARY = gnunet-service-arm -ACCEPT_FROM = 127.0.0.1; -ACCEPT_FROM6 = ::1; -DEFAULTSERVICES = topology hostlist -UNIXPATH = /tmp/gnunet-service-arm.sock -UNIX_MATCH_UID = YES -UNIX_MATCH_GID = YES -# GLOBAL_POSTFIX = -l $SERVICEHOME/{}-logs -# GLOBAL_PREFIX = -# USERNAME = -# MAXBUF = -# TIMEOUT = -# DISABLEV6 = -# BINDTO = -# REJECT_FROM = -# REJECT_FROM6 = -# PREFIX = - -[statistics] -AUTOSTART = YES -PORT = 2088 -HOSTNAME = localhost -HOME = $SERVICEHOME -CONFIG = $DEFAULTCONFIG -BINARY = gnunet-service-statistics -ACCEPT_FROM = 127.0.0.1; -ACCEPT_FROM6 = ::1; -UNIXPATH = /tmp/gnunet-service-statistics.sock -UNIX_MATCH_UID = NO -UNIX_MATCH_GID = YES -# DISABLE_SOCKET_FORWARDING = NO -# USERNAME = -# MAXBUF = -# TIMEOUT = -# DISABLEV6 = -# BINDTO = -# REJECT_FROM = -# REJECT_FROM6 = -# PREFIX = - -[resolver] -AUTOSTART = YES -PORT = 2089 -HOSTNAME = localhost -HOME = $SERVICEHOME -CONFIG = $DEFAULTCONFIG -BINARY = gnunet-service-resolver -ACCEPT_FROM = 127.0.0.1; -ACCEPT_FROM6 = ::1; -UNIXPATH = /tmp/gnunet-service-resolver.sock -UNIX_MATCH_UID = NO -UNIX_MATCH_GID = NO -# DISABLE_SOCKET_FORWARDING = NO -# USERNAME = -# MAXBUF = -# TIMEOUT = -# DISABLEV6 = -# BINDTO = -# REJECT_FROM = -# REJECT_FROM6 = -# PREFIX = - -[peerinfo] -AUTOSTART = YES -PORT = 2090 -HOSTNAME = localhost -HOME = $SERVICEHOME -CONFIG = $DEFAULTCONFIG -BINARY = gnunet-service-peerinfo -ACCEPT_FROM = 127.0.0.1; -ACCEPT_FROM6 = ::1; -UNIXPATH = /tmp/gnunet-service-peerinfo.sock -UNIX_MATCH_UID = NO -UNIX_MATCH_GID = YES -# DISABLE_SOCKET_FORWARDING = NO -# USERNAME = -# MAXBUF = -# TIMEOUT = -# DISABLEV6 = -# BINDTO = -# REJECT_FROM = -# REJECT_FROM6 = -# PREFIX = -HOSTS = $SERVICEHOME/data/hosts/ - - -[transport] -AUTOSTART = YES -PORT = 2091 -HOSTNAME = localhost -HOME = $SERVICEHOME -CONFIG = $DEFAULTCONFIG -BINARY = gnunet-service-transport -NEIGHBOUR_LIMIT = 50 -ACCEPT_FROM = 127.0.0.1; -ACCEPT_FROM6 = ::1; -PLUGINS = tcp -UNIXPATH = /tmp/gnunet-service-transport.sock -BLACKLIST_FILE = $SERVICEHOME/blacklist -UNIX_MATCH_UID = YES -UNIX_MATCH_GID = YES -# DISABLE_SOCKET_FORWARDING = NO -# USERNAME = -# MAXBUF = -# TIMEOUT = -# DISABLEV6 = -# BINDTO = -# REJECT_FROM = -# REJECT_FROM6 = -# PREFIX = - -[core] -AUTOSTART = YES -PORT = 2092 -HOSTNAME = localhost -HOME = $SERVICEHOME -CONFIG = $DEFAULTCONFIG -BINARY = gnunet-service-core -ACCEPT_FROM = 127.0.0.1; -ACCEPT_FROM6 = ::1; -# quotas are in bytes per second now! -TOTAL_QUOTA_IN = 65536 -TOTAL_QUOTA_OUT = 65536 -UNIXPATH = /tmp/gnunet-service-core.sock -UNIX_MATCH_UID = YES -UNIX_MATCH_GID = YES -# DISABLE_SOCKET_FORWARDING = NO -# DEBUG = YES -# USERNAME = -# MAXBUF = -# TIMEOUT = -# DISABLEV6 = -# BINDTO = -# REJECT_FROM = -# REJECT_FROM6 = -# PREFIX = - - -[topology] -MINIMUM-FRIENDS = 0 -FRIENDS-ONLY = NO -AUTOCONNECT = YES -TARGET-CONNECTION-COUNT = 16 -FRIENDS = $SERVICEHOME/friends -CONFIG = $DEFAULTCONFIG -BINARY = gnunet-daemon-topology - -[hostlist] -# port for hostlist http server -HTTPPORT = 8080 -HOME = $SERVICEHOME -HOSTLISTFILE = $SERVICEHOME/hostlist/learned.data -CONFIG = $DEFAULTCONFIG -BINARY = gnunet-daemon-hostlist -# consider having "-e" as default as well once implemented -OPTIONS = -b -SERVERS = http://v9.gnunet.org:58080/ -# proxy for downloading hostlists -HTTP-PROXY = - - -[datastore] -AUTOSTART = YES -UNIXPATH = /tmp/gnunet-service-datastore.sock -UNIX_MATCH_UID = YES -UNIX_MATCH_GID = YES -PORT = 2093 -HOSTNAME = localhost -HOME = $SERVICEHOME -CONFIG = $DEFAULTCONFIG -BINARY = gnunet-service-datastore -ACCEPT_FROM = 127.0.0.1; -ACCEPT_FROM6 = ::1; -QUOTA = 100000000 -BLOOMFILTER = $SERVICEHOME/fs/bloomfilter -DATABASE = sqlite -# DISABLE_SOCKET_FORWARDING = NO - -[datastore-sqlite] -FILENAME = $SERVICEHOME/datastore/sqlite.db - -[datastore-postgres] -CONFIG = connect_timeout=10; dbname=gnunet - -[datastore-mysql] -DATABASE = gnunet -CONFIG = ~/.my.cnf -# USER = gnunet -# PASSWORD = -# HOST = localhost -# PORT = 3306 - -[datacache-mysql] -DATABASE = gnunetcheck -CONFIG = ~/.my.cnf -# USER = gnunet -# PASSWORD = -# HOST = localhost -# PORT = 3306 - - -[fs] -AUTOSTART = YES -INDEXDB = $SERVICEHOME/idxinfo.lst -TRUST = $SERVICEHOME/data/credit/ -IDENTITY_DIR = $SERVICEHOME/identities/ -STATE_DIR = $SERVICEHOME/persistence/ -UPDATE_DIR = $SERVICEHOME/updates/ -PORT = 2094 -HOSTNAME = localhost -HOME = $SERVICEHOME -CONFIG = $DEFAULTCONFIG -BINARY = gnunet-service-fs -ACCEPT_FROM = 127.0.0.1; -ACCEPT_FROM6 = ::1; - -CONTENT_CACHING = YES -CONTENT_PUSHING = YES - -UNIXPATH = /tmp/gnunet-service-fs.sock -UNIX_MATCH_UID = NO -UNIX_MATCH_GID = YES -# DISABLE_SOCKET_FORWARDING = NO -# DEBUG = YES -MAX_PENDING_REQUESTS = 65536 -# Maximum frequency we're allowed to poll the datastore -# for content for migration (can be used to reduce -# GNUnet's disk-IO rate) -MIN_MIGRATION_DELAY = 1000 -EXPECTED_NEIGHBOUR_COUNT = 128 - -[dht] -AUTOSTART = YES -PORT = 2095 -HOSTNAME = localhost -HOME = $SERVICEHOME -CONFIG = $DEFAULTCONFIG -BINARY = gnunet-service-dht -ACCEPT_FROM = 127.0.0.1; -ACCEPT_FROM6 = ::1; -BUCKET_SIZE = 4 -UNIXPATH = /tmp/gnunet-service-dht.sock -UNIX_MATCH_UID = YES -UNIX_MATCH_GID = YES -# DISABLE_SOCKET_FORWARDING = NO -# DEBUG = YES -# USERNAME = -# MAXBUF = -# TIMEOUT = -# DISABLEV6 = -# BINDTO = -# REJECT_FROM = -# REJECT_FROM6 = -# PREFIX = -# DO_FIND_PEER = -# STRICT_KADEMLIA = -# USE_MAX_HOPS = -# MAX_HOPS = -# REPUBLISH = YES -# REPLICATION_FREQUENCY = 60 -# STOP_ON_CLOSEST = -# STOP_FOUND = -# CONVERGE_MODIFIER = - - -[dhtcache] -DATABASE = sqlite -QUOTA = 1024000 - -[block] -PLUGINS = fs dht test - -[dns] -AUTOSTART = NO - - diff --git a/src/nat/test_nat.c b/src/nat/test_nat.c index d1268c93a..72cff0915 100644 --- a/src/nat/test_nat.c +++ b/src/nat/test_nat.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - (C) 2009 Christian Grothoff (and other contributing authors) + (C) 2009, 2011 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 @@ -18,21 +18,21 @@ Boston, MA 02111-1307, USA. */ -/** - * @file nat/test_nat.c - * @brief Testcase for NAT library - * @author Milan Bouchet-Valat - */ - /** * Testcase for port redirection and public IP address retrieval. * This test never fails, because there need to be a NAT box set up for that. - * So we only get IP address and open the 2086 port using any UPnP and NAT-PMP - * routers found, wait for 30s, close ports and return. + * So we only get IP address and open the 2086 port using any NAT traversal + * method available, wait for 30s, close ports and return. * Have a look at the logs and use NMAP to check that it works with your box. + * + * @file nat/test_nat.c + * @brief Testcase for NAT library + * @author Milan Bouchet-Valat + * @author Christian Grothoff + * + * TODO: actually use ARM to start resolver service to make DNS work! */ - #include "platform.h" #include "gnunet_common.h" #include "gnunet_util_lib.h" @@ -40,35 +40,62 @@ #include "gnunet_scheduler_lib.h" #include "gnunet_nat_lib.h" + +#define VERBOSE GNUNET_YES + + /* Time to wait before stopping NAT, in seconds */ -#define TIMEOUT 60 +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60) -struct addr_cls -{ - struct sockaddr *addr; - socklen_t addrlen; -}; +/** + * Function called on each address that the NAT service + * believes to be valid for the transport. + */ static void addr_callback (void *cls, int add_remove, const struct sockaddr *addr, socklen_t addrlen) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "External address changed: %s %s\n", + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Address changed: %s `%s' (%u bytes)\n", add_remove == GNUNET_YES ? "added" : "removed", - GNUNET_a2s (addr, addrlen)); + GNUNET_a2s (addr, addrlen), + (unsigned int) addrlen); } + +/** + * Function that terminates the test. + */ static void stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct GNUNET_NAT_Handle *nat = cls; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Stopping NAT and quitting...\n"); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Stopping NAT and quitting...\n"); GNUNET_NAT_unregister (nat); } -/* Return the address of the default interface, - * or any interface with a valid address if the default is not valid */ + +struct addr_cls +{ + struct sockaddr *addr; + socklen_t addrlen; +}; + + +/** + * Return the address of the default interface, + * or any interface with a valid address if the default is not valid + * + * @param cls the 'struct addr_cls' + * @param name name of the interface + * @param isDefault do we think this may be our default interface + * @param addr address of the interface + * @param addrlen number of bytes in addr + * @return GNUNET_OK to continue iterating + */ static int process_if (void *cls, const char *name, @@ -76,18 +103,21 @@ process_if (void *cls, { struct addr_cls *data = cls; - if (addr && addrlen > 0) - { - if (data->addr) - GNUNET_free (data->addr); - data->addr = memcpy (GNUNET_malloc (addrlen), addr, addrlen); - data->addrlen = addrlen; - if (isDefault) - return GNUNET_SYSERR; - } + if (addr == NULL) + return GNUNET_OK; + GNUNET_free_non_null (data->addr); + data->addr = GNUNET_malloc (addrlen); + memcpy (data->addr, addr, addrlen); + data->addrlen = addrlen; + if (isDefault) + return GNUNET_SYSERR; return GNUNET_OK; } + +/** + * Main function run with scheduler. + */ static void run (void *cls, char *const *args, @@ -98,19 +128,15 @@ run (void *cls, struct sockaddr *addr; GNUNET_log_setup ("test-nat", "DEBUG", NULL); - data.addr = NULL; GNUNET_OS_network_interfaces_list (process_if, &data); - if (!data.addr) + if (NULL == data.addr) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not find a valid interface address!\n"); exit (GNUNET_SYSERR); } - - addr = GNUNET_malloc (data.addrlen); - memcpy (addr, data.addr, data.addrlen); - + addr = data.addr; GNUNET_assert (addr->sa_family == AF_INET || addr->sa_family == AF_INET6); if (addr->sa_family == AF_INET) ((struct sockaddr_in *) addr)->sin_port = htons (2086); @@ -121,14 +147,18 @@ run (void *cls, "Requesting NAT redirection from address %s...\n", GNUNET_a2s (addr, data.addrlen)); - nat = GNUNET_NAT_register (cfg, addr, data.addrlen, addr_callback, NULL); + nat = GNUNET_NAT_register (cfg, + GNUNET_YES /* tcp */, + 2086, + 1, + (const struct sockaddr**) &addr, + &data.addrlen, + &addr_callback, NULL, NULL); GNUNET_free (addr); - - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, TIMEOUT), stop, - nat); + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &stop, nat); } + int main (int argc, char *const argv[]) { @@ -139,7 +169,7 @@ main (int argc, char *const argv[]) char *const argv_prog[] = { "test-nat", "-c", - "test-nat.conf", + "test_nat_data.conf", "-L", #if VERBOSE "DEBUG", diff --git a/src/nat/test_nat_data.conf b/src/nat/test_nat_data.conf new file mode 100644 index 000000000..c312c69fb --- /dev/null +++ b/src/nat/test_nat_data.conf @@ -0,0 +1,136 @@ +[PATHS] +SERVICEHOME = /tmp/nat-test +# SERVICEHOME = /var/lib/gnunet/ +# DEFAULTCONFIG = /etc/gnunet.conf +# If 'DEFAULTCONFIG' is not defined, the current +# configuration file is assumed to be the default, +# which is what we want by default... + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[TESTING] +WEAKRANDOM = NO + +[client] +HOME = $SERVICEHOME + + +[nat] +# Are we behind NAT? +BEHIND_NAT = YES + +# Is the NAT hole-punched? +PUNCHED_NAT = NO + +# Disable UPNP by default until it gets cleaner! +ENABLE_UPNP = YES + +# Use addresses from the local network interfaces (inluding loopback, but also others) +USE_LOCALADDR = YES + +# External IP address of the NAT box (if known); IPv4 dotted-decimal ONLY at this time (should allow DynDNS!) +# normal interface IP address for non-NATed peers; +# possibly auto-detected (using UPnP) if possible if not specified +# EXTERNAL_ADDRESS = + +# Should we use ICMP-based NAT traversal to try connect to NATed peers +# or, if we are behind NAT, to allow connections to us? +ENABLE_ICMP_CLIENT = YES +ENABLE_ICMP_SERVER = YES + +# IP address of the interface connected to the NAT box; IPv4 dotted-decimal ONLY; +# normal interface IP address for non-NATed peers; +# likely auto-detected (via interface list) if not specified (!) +# INTERNAL_ADDRESS = + +# Disable IPv6 support +DISABLEV6 = NO + +[arm] +PORT = 2087 +HOSTNAME = localhost +HOME = $SERVICEHOME +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-service-arm +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +DEFAULTSERVICES = topology hostlist +UNIXPATH = /tmp/gnunet-service-arm.sock +UNIX_MATCH_UID = YES +UNIX_MATCH_GID = YES +# GLOBAL_POSTFIX = -l $SERVICEHOME/{}-logs +# GLOBAL_PREFIX = +# USERNAME = +# MAXBUF = +# TIMEOUT = +# DISABLEV6 = +# BINDTO = +# REJECT_FROM = +# REJECT_FROM6 = +# PREFIX = + +[statistics] +AUTOSTART = YES +PORT = 2088 +HOSTNAME = localhost +HOME = $SERVICEHOME +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-service-statistics +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +UNIXPATH = /tmp/gnunet-service-statistics.sock +UNIX_MATCH_UID = NO +UNIX_MATCH_GID = YES +# DISABLE_SOCKET_FORWARDING = NO +# USERNAME = +# MAXBUF = +# TIMEOUT = +# DISABLEV6 = +# BINDTO = +# REJECT_FROM = +# REJECT_FROM6 = +# PREFIX = + +[resolver] +AUTOSTART = YES +PORT = 2089 +HOSTNAME = localhost +HOME = $SERVICEHOME +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-service-resolver +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +UNIXPATH = /tmp/gnunet-service-resolver.sock +UNIX_MATCH_UID = NO +UNIX_MATCH_GID = NO +# DISABLE_SOCKET_FORWARDING = NO +# USERNAME = +# MAXBUF = +# TIMEOUT = +# DISABLEV6 = +# BINDTO = +# REJECT_FROM = +# REJECT_FROM6 = +# PREFIX = + +[peerinfo] +AUTOSTART = NO + +[transport] +AUTOSTART = NO + +[core] +AUTOSTART = NO + +[datastore] +AUTOSTART = NO + +[fs] +AUTOSTART = NO + +[dht] +AUTOSTART = NO + +[mesh] +AUTOSTART = NO diff --git a/src/nat/upnp-commands.c b/src/nat/upnp-commands.c deleted file mode 100644 index 2a88f90f4..000000000 --- a/src/nat/upnp-commands.c +++ /dev/null @@ -1,877 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2009, 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 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., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. -*/ - -/* - * Code in this file is originally based on the miniupnp library. - * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved. - * - * Original licence: - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file nat/upnp-commands.c - * @brief Implementation of a basic set of UPnP commands - * - * @author Milan Bouchet-Valat - */ - -#include "platform.h" -#include "gnunet_util_lib.h" - -#include -#include -#include - -#include "upnp-reply-parse.h" -#include "upnp-igd-parse.h" -#include "upnp-discover.h" -#include "upnp-commands.h" - -#define SOAP_PREFIX "s" -#define SERVICE_PREFIX "u" -#define SERVICE_PREFIX2 'u' -#define MAX_HOSTNAME_LEN 64 - -#define PRINT_UPNP_ERROR(a, b) GNUNET_log_from(GNUNET_ERROR_TYPE_WARNING, "UPnP", _("%s failed at %s:%d: %s\n"), a, __FILE__, __LINE__, b); - - -/** - * Private closure used by UPNP_command() and its callbacks. - */ -struct UPNP_command_cls -{ - /** - * Connection handle used for sending and receiving. - */ - struct GNUNET_CONNECTION_Handle *s; - - /** - * Transmission handle used for sending command. - */ - struct GNUNET_CONNECTION_TransmitHandle *th; - - /** - * HTML content to send to run command. - */ - char *content; - - /** - * Buffer where to copy received data to pass to caller. - */ - char *buffer; - - /** - * Size of buffer. - */ - size_t buf_size; - - /** - * User callback to trigger when done. - */ - UPNP_command_cb_ caller_cb; - - /** - * User closure to pass to caller_cb. - */ - void *caller_cls; -}; - -/** - * Get the length of content included in an HTML line. - * - * @param p line to parse - * @param n size of p - * @return the length of the content - */ -static ssize_t -get_content_len_from_line (const char *p, int n) -{ - static const char cont_len_str[] = "content-length"; - const char *p2 = cont_len_str; - int a = 0; - - while (*p2) - { - if (n == 0) - return -1; - - if (*p2 != *p && *p2 != (*p + 32)) - return -1; - - p++; - p2++; - n--; - } - - if (n == 0) - return -1; - - if (*p != ':') - return -1; - - p++; - n--; - - while (*p == ' ') - { - if (n == 0) - return -1; - - p++; - n--; - } - - while (*p >= '0' && *p <= '9') - { - if (n == 0) - return -1; - - a = (a * 10) + (*p - '0'); - p++; - n--; - } - - return a; -} - -/** - * Get the respective lengths of content and header from an HTML reply. - * - * @param p HTML to parse - * @param n size of p - * @param content_len pointer to store content length to - * @param header_len pointer to store header length to - */ -static void -get_content_and_header_len (const char *p, int n, - int *content_len, int *header_len) -{ - const char *line; - int line_len; - int r; - - line = p; - - while (line < p + n) - { - line_len = 0; - - while (line[line_len] != '\r' && line[line_len] != '\r') - { - if (line + line_len >= p + n) - return; - - line_len++; - } - - r = get_content_len_from_line (line, line_len); - - if (r > 0) - *content_len = r; - - line = line + line_len + 2; - - if (line[0] == '\r' && line[1] == '\n') - { - *header_len = (line - p) + 2; - return; - } - } -} - -/** - * Receive reply of the device to our UPnP command. - * - * @param data closure from UPNP_command() - * @param buf struct UPNP_command_cls *cls - * @param available number of bytes in buf - * @param addr address of the sender - * @param addrlen size of addr - * @param errCode value of errno - */ -static void -UPNP_command_receiver (void *data, - const void *buf, - size_t available, - const struct sockaddr *addr, - socklen_t addrlen, int errCode) -{ - struct UPNP_command_cls *cls = data; - int content_len; - int header_len; - - if (available > 0) - { - content_len = -1; - header_len = -1; - get_content_and_header_len (buf, available, &content_len, &header_len); - - strncpy (cls->buffer, (char *) buf, cls->buf_size - 2); - cls->buffer[cls->buf_size - 2] = '\0'; - } - else - { - cls->buffer[0] = '\0'; - } - - GNUNET_CONNECTION_destroy (cls->s, GNUNET_NO); - - cls->caller_cb (cls->buffer, cls->buf_size, cls->caller_cls); - - GNUNET_free (cls->content); - GNUNET_free (cls); -} - -/** - * Send UPnP command to device. - */ -static size_t -UPNP_command_transmit (void *data, size_t size, void *buf) -{ - struct UPNP_command_cls *cls = data; - int n; - char *content = cls->content; - - n = strlen (content); - memcpy (buf, content, size); - - GNUNET_CONNECTION_receive (cls->s, cls->buf_size, GNUNET_TIME_UNIT_MINUTES, - UPNP_command_receiver, cls); - - return n; -} - -/** - * Parse a HTTP URL string to extract hostname, port and path it points to. - * - * @param url source string corresponding to URL - * @param hostname pointer where to store hostname (size of MAX_HOSTNAME_LEN+1) - * @param port pointer where to store port - * @param path pointer where to store path - * - * @return GNUNET_OK on success, GNUNET_SYSERR on failure - */ -int -parse_url (const char *url, char *hostname, unsigned short *port, char **path) -{ - char *p1, *p2, *p3; - - if (!url) - return GNUNET_SYSERR; - - p1 = strstr (url, "://"); - - if (!p1) - return GNUNET_SYSERR; - - p1 += 3; - - if ((url[0] != 'h') || (url[1] != 't') - || (url[2] != 't') || (url[3] != 'p')) - return GNUNET_SYSERR; - - p2 = strchr (p1, ':'); - p3 = strchr (p1, '/'); - - if (!p3) - return GNUNET_SYSERR; - - memset (hostname, 0, MAX_HOSTNAME_LEN + 1); - - if (!p2 || (p2 > p3)) - { - strncpy (hostname, p1, GNUNET_MIN (MAX_HOSTNAME_LEN, (int) (p3 - p1))); - *port = 80; - } - else - { - strncpy (hostname, p1, GNUNET_MIN (MAX_HOSTNAME_LEN, (int) (p2 - p1))); - *port = 0; - p2++; - - while ((*p2 >= '0') && (*p2 <= '9')) - { - *port *= 10; - *port += (unsigned short) (*p2 - '0'); - p2++; - } - } - - *path = p3; - return GNUNET_OK; -} - -/** - * Send UPnP command to the device identified by url and service. - * - * @param url control URL of the device - * @param service type of the service corresponding to the command - * @param action action to send - * @param args arguments for action - * @param buffer buffer - * @param buf_size buffer size - * @param caller_cb user callback to trigger when done - * @param caller_cls closure to pass to caller_cb - */ -void -UPNP_command_ (const char *url, const char *service, - const char *action, struct UPNP_Arg_ *args, - char *buffer, size_t buf_size, - UPNP_command_cb_ caller_cb, void *caller_cls) -{ - struct GNUNET_CONNECTION_Handle *s; - struct UPNP_command_cls *cls; - struct sockaddr_in dest; - struct sockaddr_in6 dest6; - char hostname[MAX_HOSTNAME_LEN + 1]; - unsigned short port = 0; - char *path; - char soap_act[128]; - char soap_body[2048]; - int body_size; - char *content_buf; - int headers_size; - char port_str[8]; - - snprintf (soap_act, sizeof (soap_act), "%s#%s", service, action); - - if (args == NULL) - { - snprintf (soap_body, sizeof (soap_body), - "\r\n" - "<" SOAP_PREFIX ":Envelope " - "xmlns:" SOAP_PREFIX - "=\"http://schemas.xmlsoap.org/soap/envelope/\" " - SOAP_PREFIX - ":encodingStyle=\"http://schema GNUNET_free (content_buf);s.xmlsoap.org/soap/encoding/\">" - "<" SOAP_PREFIX ":Body>" "<" SERVICE_PREFIX - ":%s xmlns:" SERVICE_PREFIX "=\"%s\">" "" "" "\r\n", - action, service, action); - } - else - { - char *p; - const char *pe, *pv; - int soap_body_len; - - soap_body_len = snprintf (soap_body, sizeof (soap_body), - "\r\n" - "<" SOAP_PREFIX ":Envelope " - "xmlns:" SOAP_PREFIX - "=\"http://schemas.xmlsoap.org/soap/envelope/\" " - SOAP_PREFIX - ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" - "<" SOAP_PREFIX ":Body>" "<" SERVICE_PREFIX - ":%s xmlns:" SERVICE_PREFIX "=\"%s\">", - action, service); - - p = soap_body + soap_body_len; - - while (args->elt) - { - /* check that we are never overflowing the string... */ - if (soap_body + sizeof (soap_body) <= p + 100) - { - GNUNET_assert (GNUNET_NO); - caller_cb (buffer, 0, caller_cls); - return; - } - *(p++) = '<'; - pe = args->elt; - while (*pe) - *(p++) = *(pe++); - *(p++) = '>'; - if ((pv = args->val)) - { - while (*pv) - *(p++) = *(pv++); - } - *(p++) = '<'; - *(p++) = '/'; - pe = args->elt; - while (*pe) - *(p++) = *(pe++); - *(p++) = '>'; - args++; - } - *(p++) = '<'; - *(p++) = '/'; - *(p++) = SERVICE_PREFIX2; - *(p++) = ':'; - pe = action; - - while (*pe) - *(p++) = *(pe++); - - strncpy (p, ">\r\n", - soap_body + sizeof (soap_body) - p); - } - - if (GNUNET_OK != parse_url (url, hostname, &port, &path)) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP", - "Invalid URL passed to UPNP_command(): %s\n", url); - caller_cb (buffer, 0, caller_cls); - return; - } - - - /* Test IPv4 address, else use IPv6 */ - memset (&dest, 0, sizeof (dest)); - memset (&dest6, 0, sizeof (dest6)); - - if (inet_pton (AF_INET, hostname, &dest.sin_addr) == 1) - { - dest.sin_family = AF_INET; - dest.sin_port = htons (port); -#ifdef HAVE_SOCKADDR_IN_SIN_LEN - dest.sin_len = sizeof (dest); -#endif - - s = GNUNET_CONNECTION_create_from_sockaddr (PF_INET, - (struct sockaddr *) &dest, - sizeof (dest)); - } - else if (inet_pton (AF_INET6, hostname, &dest6.sin6_addr) == 1) - { - dest6.sin6_family = AF_INET6; - dest6.sin6_port = htons (port); -#ifdef HAVE_SOCKADDR_IN_SIN_LEN - dest6.sin6_len = sizeof (dest6); -#endif - - s = GNUNET_CONNECTION_create_from_sockaddr (PF_INET6, - (struct sockaddr *) &dest6, - sizeof (dest6)); - } - else - { - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, _("%s failed at %s:%d\n"), - "UPnP", "inet_pton", __FILE__, __LINE__); - - caller_cb (buffer, 0, caller_cls); - return; - } - - body_size = (int) strlen (soap_body); - content_buf = GNUNET_malloc (512 + body_size); - - /* We are not using keep-alive HTTP connections. - * HTTP/1.1 needs the header Connection: close to do that. - * This is the default with HTTP/1.0 */ - /* Connection: Close is normally there only in HTTP/1.1 but who knows */ - port_str[0] = '\0'; - - if (port != 80) - snprintf (port_str, sizeof (port_str), ":%hu", port); - - headers_size = snprintf (content_buf, 512, "POST %s HTTP/1.1\r\n" "Host: %s%s\r\n" "User-Agent: GNU, UPnP/1.0, GNUnet/" PACKAGE_VERSION "\r\n" "Content-Length: %d\r\n" "Content-Type: text/xml\r\n" "SOAPAction: \"%s\"\r\n" "Connection: Close\r\n" "Cache-Control: no-cache\r\n" /* ??? */ - "Pragma: no-cache\r\n" - "\r\n", path, hostname, port_str, body_size, - soap_act); - memcpy (content_buf + headers_size, soap_body, body_size); - -#ifdef DEBUG_UPNP - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP", - "Sending command '%s' to '%s' (service '%s')\n", - action, url, service); -#endif - - cls = GNUNET_malloc (sizeof (struct UPNP_command_cls)); - cls->s = s; - cls->content = content_buf; - cls->buffer = buffer; - cls->buf_size = buf_size; - cls->caller_cb = caller_cb; - cls->caller_cls = caller_cls; - - cls->th = - GNUNET_CONNECTION_notify_transmit_ready (s, body_size + headers_size, - GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 15), - &UPNP_command_transmit, cls); - - - if (cls->th == NULL) - { -#ifdef DEBUG_UPNP - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP", - "Error sending SOAP request at %s:%d\n", __FILE__, - __LINE__); -#endif - - caller_cb (buffer, 0, caller_cls); - - GNUNET_free (content_buf); - GNUNET_free (cls); - GNUNET_CONNECTION_destroy (s, GNUNET_NO); - return; - } -} - -struct get_external_ip_address_cls -{ - UPNP_get_external_ip_address_cb_ caller_cb; - void *caller_cls; -}; - -static void -get_external_ip_address_receiver (char *response, size_t received, void *data) -{ - struct get_external_ip_address_cls *cls = data; - struct UPNP_REPLY_NameValueList_ pdata; - char extIpAdd[128]; - char *p; - int ret = UPNP_COMMAND_UNKNOWN_ERROR; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Response: %s", response); - - UPNP_REPLY_parse_ (response, received, &pdata); - p = UPNP_REPLY_get_value_ (&pdata, "NewExternalIPAddress"); - if (p) - { - strncpy (extIpAdd, p, 128); - extIpAdd[127] = '\0'; - ret = UPNP_COMMAND_SUCCESS; - } - else - extIpAdd[0] = '\0'; - - p = UPNP_REPLY_get_value_ (&pdata, "errorCode"); - if (p) - { - ret = UPNP_COMMAND_UNKNOWN_ERROR; - sscanf (p, "%d", &ret); - } - cls->caller_cb (ret, extIpAdd, cls->caller_cls); - - UPNP_REPLY_free_ (&pdata); - GNUNET_free (response); - GNUNET_free (cls); -} - -/* UPNP_get_external_ip_address_() call the corresponding UPNP method. - * - * Return values : - * 0 : SUCCESS - * NON ZERO : ERROR Either an UPnP error code or an unknown error. - * - * 402 Invalid Args - See UPnP Device Architecture section on Control. - * 501 Action Failed - See UPnP Device Architecture section on Control. - */ -void -UPNP_get_external_ip_address_ (const char *control_url, - const char *service_type, - UPNP_get_external_ip_address_cb_ caller_cb, - void *caller_cls) -{ - struct get_external_ip_address_cls *cls; - char *buffer; - - if (!control_url || !service_type) - caller_cb (UPNP_COMMAND_INVALID_ARGS, NULL, caller_cls); - - cls = GNUNET_malloc (sizeof (struct get_external_ip_address_cls)); - cls->caller_cb = caller_cb; - cls->caller_cls = caller_cls; - - buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE); - - UPNP_command_ (control_url, service_type, "GetExternalIPAddress", - NULL, buffer, UPNP_COMMAND_BUFSIZE, - (UPNP_command_cb_) get_external_ip_address_receiver, cls); -} - -struct PortMapping_cls -{ - const char *control_url; - const char *service_type; - const char *ext_port; - const char *in_port; - const char *proto; - const char *remoteHost; - UPNP_port_mapping_cb_ caller_cb; - void *caller_cls; -}; - -static void -add_delete_port_mapping_receiver (char *response, size_t received, void *data) -{ - struct PortMapping_cls *cls = data; - struct UPNP_REPLY_NameValueList_ pdata; - const char *resVal; - int ret; - - UPNP_REPLY_parse_ (response, received, &pdata); - resVal = UPNP_REPLY_get_value_ (&pdata, "errorCode"); - if (resVal) - { - ret = UPNP_COMMAND_UNKNOWN_ERROR; - sscanf (resVal, "%d", &ret); - } - else - { - ret = UPNP_COMMAND_SUCCESS; - } - - cls->caller_cb (ret, cls->control_url, cls->service_type, - cls->ext_port, cls->in_port, cls->proto, - cls->remoteHost, cls->caller_cls); - - UPNP_REPLY_free_ (&pdata); - GNUNET_free (response); - GNUNET_free (cls); -} - -void -UPNP_add_port_mapping_ (const char *control_url, const char *service_type, - const char *ext_port, - const char *in_port, - const char *inClient, - const char *desc, - const char *proto, const char *remoteHost, - UPNP_port_mapping_cb_ caller_cb, void *caller_cls) -{ - struct UPNP_Arg_ args[9]; - struct PortMapping_cls *cls; - char *buffer; - - if (!in_port || !inClient || !proto || !ext_port) - { - caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type, - ext_port, in_port, proto, remoteHost, caller_cls); - return; - } - - args[0].elt = "NewRemoteHost"; - args[0].val = remoteHost; - args[1].elt = "NewExternalPort"; - args[1].val = ext_port; - args[2].elt = "NewProtocol"; - args[2].val = proto; - args[3].elt = "NewInternalPort"; - args[3].val = in_port; - args[4].elt = "NewInternalClient"; - args[4].val = inClient; - args[5].elt = "NewEnabled"; - args[5].val = "1"; - args[6].elt = "NewPortMappingDescription"; - args[6].val = desc ? desc : "GNUnet"; - args[7].elt = "NewLeaseDuration"; - args[7].val = "0"; - args[8].elt = NULL; - args[8].val = NULL; - - cls = GNUNET_malloc (sizeof (struct PortMapping_cls)); - cls->control_url = control_url; - cls->service_type = service_type; - cls->ext_port = ext_port;; - cls->in_port = in_port; - cls->proto = proto; - cls->remoteHost = remoteHost; - cls->caller_cb = caller_cb; - cls->caller_cls = caller_cls; - - buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE); - - UPNP_command_ (control_url, service_type, "AddPortMapping", - args, buffer, UPNP_COMMAND_BUFSIZE, - add_delete_port_mapping_receiver, cls); -} - -void -UPNP_delete_port_mapping_ (const char *control_url, const char *service_type, - const char *ext_port, const char *proto, - const char *remoteHost, - UPNP_port_mapping_cb_ caller_cb, void *caller_cls) -{ - struct UPNP_Arg_ args[4]; - struct PortMapping_cls *cls; - char *buffer; - - if (!ext_port || !proto) - { - caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type, - ext_port, NULL, proto, remoteHost, caller_cls); - return; - } - - args[0].elt = "NewRemoteHost"; - args[0].val = remoteHost; - args[1].elt = "NewExternalPort"; - args[1].val = ext_port; - args[2].elt = "NewProtocol"; - args[2].val = proto; - args[3].elt = NULL; - args[3].val = NULL; - - cls = GNUNET_malloc (sizeof (struct PortMapping_cls)); - cls->control_url = control_url; - cls->service_type = service_type; - cls->ext_port = ext_port; - cls->in_port = "0"; - cls->proto = proto; - cls->remoteHost = remoteHost; - cls->caller_cb = caller_cb; - cls->caller_cls = caller_cls; - - buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE); - - UPNP_command_ (control_url, service_type, - "DeletePortMapping", - args, buffer, UPNP_COMMAND_BUFSIZE, - add_delete_port_mapping_receiver, cls); -} - - -struct get_specific_port_mapping_entry_cls -{ - const char *control_url; - const char *service_type; - const char *ext_port; - const char *proto; - UPNP_port_mapping_cb_ caller_cb; - void *caller_cls; -}; - -static void -get_specific_port_mapping_entry_receiver (char *response, size_t received, - void *data) -{ - struct PortMapping_cls *cls = data; - struct UPNP_REPLY_NameValueList_ pdata; - char *p; - char in_port[128]; - char in_client[128]; - int ret; - - UPNP_REPLY_parse_ (response, received, &pdata); - - p = UPNP_REPLY_get_value_ (&pdata, "NewInternalClient"); - if (p) - { - strncpy (in_client, p, 128); - in_client[127] = '\0'; - } - else - in_client[0] = '\0'; - - p = UPNP_REPLY_get_value_ (&pdata, "NewInternalPort"); - if (p) - { - strncpy (in_port, p, 6); - in_port[5] = '\0'; - } - else - in_port[0] = '\0'; - - p = UPNP_REPLY_get_value_ (&pdata, "errorCode"); - if (p) - { - if (p) - { - ret = UPNP_COMMAND_UNKNOWN_ERROR; - sscanf (p, "%d", &ret); - } -#if DEBUG_UPNP - PRINT_UPNP_ERROR ("GetSpecificPortMappingEntry", p); -#endif - } - - cls->caller_cb (ret, cls->control_url, cls->service_type, - cls->ext_port, cls->proto, in_port, in_client, - cls->caller_cls); - - UPNP_REPLY_free_ (&pdata); - GNUNET_free (response); - GNUNET_free (cls); -} - -/* UPNP_get_specific_port_mapping_entry _ retrieves an existing port mapping - * the result is returned in the in_client and in_port strings - * please provide 128 and 6 bytes of data */ -void -UPNP_get_specific_port_mapping_entry_ (const char *control_url, - const char *service_type, - const char *ext_port, - const char *proto, - UPNP_get_specific_port_mapping_entry_cb_ - caller_cb, void *caller_cls) -{ - struct UPNP_Arg_ args[4]; - struct get_specific_port_mapping_entry_cls *cls; - char *buffer; - - if (!ext_port || !proto) - { - caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type, - ext_port, proto, NULL, NULL, caller_cls); - return; - } - - args[0].elt = "NewRemoteHost"; - args[0].val = NULL; - args[1].elt = "NewExternalPort"; - args[1].val = ext_port; - args[2].elt = "NewProtocol"; - args[2].val = proto; - args[3].elt = NULL; - args[3].val = NULL; - - cls = GNUNET_malloc (sizeof (struct PortMapping_cls)); - cls->control_url = control_url; - cls->service_type = service_type; - cls->ext_port = ext_port; - cls->proto = proto; - cls->caller_cb = caller_cb; - cls->caller_cls = caller_cls; - - buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE); - - UPNP_command_ (control_url, service_type, - "GetSpecificPortMappingEntry", - args, buffer, UPNP_COMMAND_BUFSIZE, - get_specific_port_mapping_entry_receiver, cls); -} diff --git a/src/nat/upnp-commands.h b/src/nat/upnp-commands.h deleted file mode 100644 index ea7b48162..000000000 --- a/src/nat/upnp-commands.h +++ /dev/null @@ -1,271 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2009, 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 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., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. -*/ - -/* - * Code in this file is originally based on the miniupnp library. - * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved. - * - * Original licence: - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file nat/upnp-commands.h - * @brief Commands to control UPnP IGD devices - * - * @author Milan Bouchet-Valat - */ -#ifndef UPNP_COMMANDS_H -#define UPNP_COMMANDS_H - -#include "platform.h" -#include "gnunet_scheduler_lib.h" - -/** - * Generic UPnP error codes. - */ -#define UPNP_COMMAND_SUCCESS (0) -#define UPNP_COMMAND_UNKNOWN_ERROR (-1) -#define UPNP_COMMAND_INVALID_ARGS (-2) - -/** - * Size of the buffer used to store anwsers to UPnP commands. - */ -#define UPNP_COMMAND_BUFSIZE 4096 - -/** - * Name-value pair containing an argumeny to a UPnP command. - */ -struct UPNP_Arg_ -{ - const char *elt; - const char *val; -}; - -/** - * Callback for UPNP_command_(). - * - * @param response the buffer passed to UPNP_command_(), filled with - * NULL-terminated content (if any) - * @param received length of the content received and stored in response - * @param cls closure passed to UPNP_command_() - */ -typedef void (*UPNP_command_cb_) (char *response, size_t received, void *cls); - -/** - * Send UPnP command to the device identified by url and service. - * - * @param url control URL of the device - * @param service type of the service corresponding to the command - * @param action action to send - * @param args arguments for action - * @param buffer buffer - * @param buf_size buffer size - * @param caller_cb user callback to trigger when done - * @param caller_cls closure to pass to caller_cb - */ -void UPNP_command_ (const char *url, const char *service, - const char *action, struct UPNP_Arg_ *args, - char *buffer, size_t buf_size, - UPNP_command_cb_ caller_cb, void *caller_cls); - -/** - * Callback to UPNP_get_external_ip_address_(). - * - * Possible UPnP Errors : - * 402 Invalid Args - See UPnP Device Architecture section on Control. - * 501 Action Failed - See UPnP Device Architecture section on Control. - * - * @param error GNUNET_OK on success, another value on error (see above) - * @param ext_ip_addr the external IP address reported by the device (IPv4 or v6) - * @param cls the closure passed to UPNP_get_external_ip_address_() - */ -typedef void (*UPNP_get_external_ip_address_cb_) (int error, - char *ext_ip_addr, - void *cls); - -/** - * Get the IP address associated with the WAN connection of the device. - * See UPNP_get_external_ip_address_cb_. - * - * @param control_url the control URL corresponding to service_type on the device - * @param service_type service type to call the command on - * @param caller_cb function to call when done - * @param caller_cls closure passed to caller_cb - */ -void -UPNP_get_external_ip_address_ (const char *control_url, - const char *service_type, - UPNP_get_external_ip_address_cb_ caller_cb, - void *caller_cls); - -/** - * Callback to UPNP_add_port_mapping_() and UPNP_delete_port_mapping_(). - * - * Possible UPnP Errors with UPNP_add_port_mapping_(): - * 402 Invalid Args - See UPnP Device Architecture section on Control. - * 501 Action Failed - See UPnP Device Architecture section on Control. - * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be - * wild-carded - * 716 WildCardNotPermittedInext_port - The external port cannot be wild-carded - * 718 ConflictInMappingEntry - The port mapping entry specified conflicts - * with a mapping assigned previously to another client - * 724 SamePortValuesRequired - Internal and External port values - * must be the same - * 725 OnlyPermanentLeasesSupported - The NAT implementation only supports - * permanent lease times on port mappings - * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard - * and cannot be a specific IP address or DNS name - * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and - * cannot be a specific port value - * - * Possible UPnP Errors with UPNP_delete_port_mapping_(): - * 402 Invalid Args - See UPnP Device Architecture section on Control. - * 714 NoSuchEntryInArray - The specified value does not exist in the array - * - * @param error GNUNET_OK on success, another value on error (see above) - * @param control_url the control URL the command was called on - * @param service_type service the command was called on - * @param ext_port external port - * @param inPort port on the gateway on the LAN side which was requested - * @param proto protocol for which port mapping was requested - * @param remote_host remote host for which port mapping was requested - * @param cls the closure passed to the command function - */ -typedef void (*UPNP_port_mapping_cb_) (int error, - const char *control_url, - const char *service_type, - const char *ext_port, - const char *inPort, const char *proto, - const char *remote_host, void *cls); - - -/** - * Request opening a port on the IGD device. - * (remote_host is usually NULL because IGDs don't support it.) - * - * @param control_url the control URL corresponding to service_type on the device - * @param service_type service type to call the command on - * @param ext_port port that should be opened on the WAN side - * @param in_port port on the gateway on the LAN side which should map ext_port - * @param in_client address in the LAN to which packets should be redirected - * @param desc description - * @param proto protocol for which to request port mapping - * @param remote_host remote host for which to request port mapping - * @param caller_cb function to call when done - * @param caller_cls closure passed to caller_cb - */ -void -UPNP_add_port_mapping_ (const char *control_url, const char *service_type, - const char *ext_port, - const char *in_port, - const char *in_client, - const char *desc, - const char *proto, const char *remote_host, - UPNP_port_mapping_cb_ caller_cb, void *caller_cls); - -/** - * Request closing a a port on the IGD device that was previously opened - * using UPNP_add_port_mapping_(). Use the same argument values that were - * used when opening the port. - * (remote_host is usually NULL because IGDs don't support it.) - * - * @param control_url the control URL the command was called on - * @param service_type service the command was called on - * @param ext_port external port - * @param proto protocol for which port mapping was requested - * @param remote_host remote host for which port mapping was requested - * @param caller_cb function to call when done - * @param caller_cls closure passed to caller_cb - */ -void -UPNP_delete_port_mapping_ (const char *control_url, const char *service_type, - const char *ext_port, const char *proto, - const char *remote_host, - UPNP_port_mapping_cb_ caller_cb, void *caller_cls); - - -/** - * Callback to UPNP_get_specific_port_mapping_entry _(). - * - * @param error GNUNET_OK if port is currently mapped, another value on error - * @param control_url the control URL the command was called on - * @param service_type service the command was called on - * @param ext_port external port - * @param proto protocol for which port mapping was requested - * @param in_port port on the gateway on the LAN side which was requested - * @param in_client address in the LAN which was requested - * @param cls the closure passed to the command function - */ -typedef void (*UPNP_get_specific_port_mapping_entry_cb_) (int error, - const char - *control_url, - const char - *service_type, - const char - *ext_port, - const char *proto, - const char *in_port, - const char - *in_client, - void *cls); - -/** - * Check that a port mapping set up with UPNP_add_port_mapping_() - * is alive. - * - * @param control_url the control URL the command was called on - * @param service_type service the command was called on - * @param ext_port external port - * @param proto protocol for which port mapping was requested - * @param caller_cb function to call when done - * @param caller_cls closure passed to caller_cb - */ -void -UPNP_get_specific_port_mapping_entry_ (const char *control_url, - const char *service_type, - const char *ext_port, - const char *proto, - UPNP_get_specific_port_mapping_entry_cb_ - caller_cb, void *caller_cls); - -#endif diff --git a/src/nat/upnp-discover.c b/src/nat/upnp-discover.c deleted file mode 100644 index 2e609d790..000000000 --- a/src/nat/upnp-discover.c +++ /dev/null @@ -1,1270 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2009, 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 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., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. -*/ - -/* - * Code in this file is originally based on the miniupnp library. - * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved. - * - * Original license: - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file nat/upnp-discover.c - * @brief Look for UPnP IGD devices - * - * @author Milan Bouchet-Valat - */ -#include -#include -#include -#include - -#include "platform.h" -#include "gnunet_util_lib.h" -#include "upnp-discover.h" -#include "upnp-reply-parse.h" -#include "upnp-igd-parse.h" -#include "upnp-minixml.h" - -#define DISCOVER_BUFSIZE 512 -#define DESCRIPTION_BUFSIZE 2048 -#define CURL_EASY_SETOPT(c, a, b) do { ret = curl_easy_setopt(c, a, b); if (ret != CURLE_OK) GNUNET_log(GNUNET_ERROR_TYPE_WARNING, _("%s failed at %s:%d: `%s'\n"), "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret)); } while (0) -#define PRINT_SOCKET_ERROR(a) GNUNET_log_from(GNUNET_ERROR_TYPE_WARNING, "UPnP", _("%s failed at %s:%d: '%s'\n"), a, __FILE__, __LINE__, strerror (errno)); -#define PRINT_SOCKET_ERROR_STR(a, b) GNUNET_log_from(GNUNET_ERROR_TYPE_WARNING, "UPnP", _("%s failed at %s:%d: '%s' on `%s'\n"), a, __FILE__, __LINE__, strerror (errno), b); - -/** - * Callback function called when download is finished. - * - * @param data the contents of the downloaded file, or NULL - * @param cls closure passed via download_device_description() - */ -typedef void (*download_cb) (char *data, void *cls); - -/** - * Private closure used by download_device_description() and it's callbacks. - */ -struct download_cls -{ - /** - * curl_easy handle. - */ - CURL *curl; - - /** - * curl_multi handle. - */ - CURLM *multi; - - /** - * URL of the file to download. - */ - char *url; - - /** - * Time corresponding to timeout wanted by the caller. - */ - struct GNUNET_TIME_Absolute end_time; - - /** - * Buffer to store downloaded content. - */ - char download_buffer[DESCRIPTION_BUFSIZE]; - - /** - * Size of the already downloaded content. - */ - size_t download_pos; - - /** - * User callback to trigger when done. - */ - download_cb caller_cb; - - /** - * User closure to pass to caller_cb. - */ - void *caller_cls; -}; - -/** - * Clean up the state of CURL multi handle and that of - * the only easy handle it uses. - */ -static void -download_clean_up (struct download_cls *cls) -{ - CURLMcode mret; - - mret = curl_multi_cleanup (cls->multi); - if (mret != CURLM_OK) - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "UPnP", - _("%s failed at %s:%d: `%s'\n"), - "curl_multi_cleanup", __FILE__, __LINE__, - curl_multi_strerror (mret)); - - curl_easy_cleanup (cls->curl); - GNUNET_free (cls); -} - -/** - * Process downloaded bits by calling callback on each HELLO. - * - * @param ptr buffer with downloaded data - * @param size size of a record - * @param nmemb number of records downloaded - * @param ctx closure - * @return number of bytes that were processed (always size*nmemb) - */ -static size_t -callback_download (void *ptr, size_t size, size_t nmemb, void *ctx) -{ - struct download_cls *cls = ctx; - const char *cbuf = ptr; - size_t total; - size_t cpy; - - total = size * nmemb; - if (total == 0) - return total; /* ok, no data */ - - cpy = GNUNET_MIN (total, DESCRIPTION_BUFSIZE - cls->download_pos - 1); - memcpy (&cls->download_buffer[cls->download_pos], cbuf, cpy); - cbuf += cpy; - cls->download_pos += cpy; - -#if DEBUG_UPNP - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP", - "Downloaded %d records of size %d, download position: %d\n", - size, nmemb, cls->download_pos); -#endif - - return total; -} - -static void -task_download (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc); - -/** - * Ask CURL for the select set and then schedule the - * receiving task with the scheduler. - */ -static void -download_prepare (struct download_cls *cls) -{ - CURLMcode mret; - fd_set rs; - fd_set ws; - fd_set es; - int max; - struct GNUNET_NETWORK_FDSet *grs; - struct GNUNET_NETWORK_FDSet *gws; - long timeout; - struct GNUNET_TIME_Relative rtime; - - max = -1; - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - mret = curl_multi_fdset (cls->multi, &rs, &ws, &es, &max); - if (mret != CURLM_OK) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "UPnP", - _("%s failed at %s:%d: `%s'\n"), - "curl_multi_fdset", __FILE__, __LINE__, - curl_multi_strerror (mret)); - download_clean_up (cls); - cls->caller_cb (NULL, cls->caller_cls); - return; - } - mret = curl_multi_timeout (cls->multi, &timeout); - if (mret != CURLM_OK) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "UPnP", - _("%s failed at %s:%d: `%s'\n"), - "curl_multi_timeout", __FILE__, __LINE__, - curl_multi_strerror (mret)); - download_clean_up (cls); - cls->caller_cb (NULL, cls->caller_cls); - return; - } - rtime = - GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining - (cls->end_time), - GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_MILLISECONDS, timeout)); - grs = GNUNET_NETWORK_fdset_create (); - gws = GNUNET_NETWORK_fdset_create (); - GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1); - GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1); - - GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, - GNUNET_SCHEDULER_NO_TASK, - rtime, - grs, - gws, - & task_download, cls); - GNUNET_NETWORK_fdset_destroy (gws); - GNUNET_NETWORK_fdset_destroy (grs); -} - -/** - * Task that is run when we are ready to receive more data from the device. - * - * @param cls closure - * @param tc task context - */ -static void -task_download (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct download_cls *dc = cls; - int running; - struct CURLMsg *msg; - CURLMcode mret; - - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - { -#if DEBUG_UPNP - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP", - "Shutdown requested while trying to download device description from `%s'\n", - dc->url); -#endif - dc->caller_cb (NULL, dc->caller_cls); - download_clean_up (dc); - return; - } - if (GNUNET_TIME_absolute_get_remaining (dc->end_time).rel_value == 0) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP", - _ - ("Timeout trying to download UPnP device description from '%s'\n"), - dc->url); - dc->caller_cb (NULL, dc->caller_cls); - download_clean_up (dc); - return; - } - - do - { - running = 0; - mret = curl_multi_perform (dc->multi, &running); - - if (running == 0) - { - do - { - msg = curl_multi_info_read (dc->multi, &running); - GNUNET_break (msg != NULL); - if (msg == NULL) - break; - - if ((msg->data.result != CURLE_OK) && - (msg->data.result != CURLE_GOT_NOTHING)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("%s failed for `%s' at %s:%d: `%s'\n"), - "curl_multi_perform", - dc->url, - __FILE__, - __LINE__, - curl_easy_strerror (msg->data.result)); - dc->caller_cb (NULL, dc->caller_cls); - } - else - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP", - _ - ("Download of device description `%s' completed.\n"), - dc->url); - dc->caller_cb (GNUNET_strdup (dc->download_buffer), - dc->caller_cls); - } - - download_clean_up (dc); - return; - } - while ((running > 0)); - } - } - while (mret == CURLM_CALL_MULTI_PERFORM); - - if (mret != CURLM_OK) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "UPnP", - _("%s failed at %s:%d: `%s'\n"), - "curl_multi_perform", __FILE__, __LINE__, - curl_multi_strerror (mret)); - download_clean_up (dc); - dc->caller_cb (NULL, dc->caller_cls); - } - - download_prepare (dc); -} - - -/** - * Download description from devices. - * - * @param url URL of the file to download - * @param caller_cb user function to call when done - * @param caller_cls closure to pass to caller_cb - */ -void -download_device_description (char *url, download_cb caller_cb, - void *caller_cls) -{ - CURL *curl; - CURLM *multi; - CURLcode ret; - CURLMcode mret; - struct download_cls *cls; - - cls = GNUNET_malloc (sizeof (struct download_cls)); - - curl = curl_easy_init (); - if (curl == NULL) - goto error; - - CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, &callback_download); - if (ret != CURLE_OK) - goto error; - - CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, cls); - if (ret != CURLE_OK) - goto error; - - CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1); - CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4); - /* no need to abort if the above failed */ - CURL_EASY_SETOPT (curl, CURLOPT_URL, url); - if (ret != CURLE_OK) - goto error; - - CURL_EASY_SETOPT (curl, CURLOPT_FAILONERROR, 1); - CURL_EASY_SETOPT (curl, CURLOPT_BUFFERSIZE, DESCRIPTION_BUFSIZE); - CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet"); - CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 60L); - CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 60L); - - multi = curl_multi_init (); - if (multi == NULL) - { - GNUNET_break (0); - /* clean_up (); */ - return; - } - mret = curl_multi_add_handle (multi, curl); - if (mret != CURLM_OK) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "UPnP", - _("%s failed at %s:%d: `%s'\n"), - "curl_multi_add_handle", __FILE__, __LINE__, - curl_multi_strerror (mret)); - mret = curl_multi_cleanup (multi); - if (mret != CURLM_OK) - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "UPnP", - _("%s failed at %s:%d: `%s'\n"), - "curl_multi_cleanup", __FILE__, __LINE__, - curl_multi_strerror (mret)); - goto error; - return; - } - -#if DEBUG_UPNP - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP", - "Preparing to download device description from '%s'\n", - url); -#endif - - cls->curl = curl; - cls->multi = multi; - cls->url = url; - cls->end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES); - memset (cls->download_buffer, 0, DESCRIPTION_BUFSIZE); - cls->download_pos = 0; - cls->caller_cb = caller_cb; - cls->caller_cls = caller_cls; - download_prepare (cls); - return; - - -error: - GNUNET_break (0); - GNUNET_free (cls); - curl_easy_cleanup (curl); - caller_cb (NULL, caller_cls); -} - -/** - * Parse SSDP packet received in reply to a M-SEARCH message. - * - * @param reply contents of the packet - * @param size length of reply - * @param location address of a pointer that will be set to the start - * of the "location" field - * @param location_size pointer where to store the length of the "location" field - * @param st pointer address of a pointer that will be set to the start - * of the "st" (search target) field - * @param st_size pointer where to store the length of the "st" field - * The strings are NOT null terminated */ -static void -parse_msearch_reply (const char *reply, int size, - const char **location, int *location_size, - const char **st, int *st_size) -{ - int a, b, i; - - i = 0; - b = 0; - /* Start of the line */ - a = i; - - while (i < size) - { - switch (reply[i]) - { - case ':': - if (b == 0) - /* End of the "header" */ - b = i; - break; - case '\x0a': - case '\x0d': - if (b != 0) - { - do - { - b++; - } - while (reply[b] == ' '); - - if (0 == strncasecmp (reply + a, "location", 8)) - { - *location = reply + b; - *location_size = i - b; - } - else if (0 == strncasecmp (reply + a, "st", 2)) - { - *st = reply + b; - *st_size = i - b; - } - - b = 0; - } - - a = i + 1; - break; - default: - break; - } - - i++; - } -} - -/** - * Standard port for UPnP discovery (SSDP protocol). - */ -#define PORT 1900 - -/** - * Convert a constant integer into a string. - */ -#define XSTR(s) STR(s) -#define STR(s) #s - -/** - * Standard IPv4 multicast adress for UPnP discovery (SSDP protocol). - */ -#define UPNP_MCAST_ADDR "239.255.255.250" - -/** - * Standard IPv6 multicast adress for UPnP discovery (SSDP protocol). - */ -#define UPNP_MCAST_ADDR6 "FF02:0:0:0:0:0:0:F" - -/** - * Size of the buffer needed to store SSDP requests we send. - */ -#define UPNP_DISCOVER_BUFSIZE 1536 - -/** - * Description of a UPnP device containing everything - * we may need to control it. - * - * Meant to be member of a chained list. - */ -struct UPNP_Dev_ -{ - /** - * Next device in the list, if any. - */ - struct UPNP_Dev_ *pNext; - - /** - * Path to the file describing the device. - */ - char *desc_url; - - /** - * UPnP search target. - */ - char *st; - - /** - * Service type associated with the control_url for the device. - */ - char *service_type; - - /** - * URL to send commands to. - */ - char *control_url; - - /** - * Whether the device is currently connected to the WAN. - */ - int is_connected; - - /** - * IGD Data associated with the device. - */ - struct UPNP_IGD_Data_ *data; -}; - -/** - * Private closure used by UPNP_discover() and its callbacks. - */ -struct UPNP_discover_cls -{ - /** - * Remote address used for multicast emission and reception. - */ - struct sockaddr *multicast_addr; - - /** - * Network handle used to send and receive discovery messages. - */ - struct GNUNET_NETWORK_Handle *sudp; - - /** - * fdset used with sudp. - */ - struct GNUNET_NETWORK_FDSet *fdset; - - /** - * Connection handle used to download device description. - */ - struct GNUNET_CONNECTION_Handle *s; - - /** - * Transmission handle used with s. - */ - struct GNUNET_CONNECTION_TransmitHandle *th; - - /** - * Index of the UPnP device type we're currently sending discovery messages to. - */ - int type_index; - - /** - * List of discovered devices. - */ - struct UPNP_Dev_ *dev_list; - - /** - * Device we're currently fetching description from. - */ - struct UPNP_Dev_ *current_dev; - - /** - * User callback to trigger when done. - */ - UPNP_discover_cb_ caller_cb; - - /** - * Closure passed to caller_cb. - */ - void *caller_cls; -}; - -/** - * Check that raw_url is absolute, and if not, use ref_url to resolve it: - * if is_desc_file is GNUNET_YES, the path to the parent of the file is used; - * if it is GNUNET_NO, ref_url will be considered as the base URL for raw URL. - * - * @param ref_url base URL for the device - * @param is_desc_file whether ref_url is a path to the description file - * @param raw_url a possibly relative URL - * @returns a new string with an absolute URL - */ -static char * -get_absolute_url (const char *ref_url, int is_desc_file, const char *raw_url) -{ - char *final_url; - - if ((raw_url[0] == 'h') - && (raw_url[1] == 't') - && (raw_url[2] == 't') - && (raw_url[3] == 'p') - && (raw_url[4] == ':') && (raw_url[5] == '/') && (raw_url[6] == '/')) - { - final_url = GNUNET_strdup (raw_url); - } - else - { - int n = strlen (raw_url); - int l = strlen (ref_url); - int cpy_len = l; - char *slash; - - /* If base URL is a path to the description file, go one level higher */ - if (is_desc_file == GNUNET_YES) - { - slash = strrchr (ref_url, '/'); - cpy_len = slash - ref_url; - } - - final_url = GNUNET_malloc (l + n + 1); - - /* Add trailing slash to base URL if needed */ - if (raw_url[0] != '/' && ref_url[cpy_len] != '\0') - final_url[cpy_len++] = '/'; - - strncpy (final_url, ref_url, cpy_len); - strcpy (final_url + cpy_len, raw_url); - final_url[cpy_len + n] = '\0'; - } - - return final_url; -} - - -/** - * Construct control URL and service type for device from its description URL - * and UPNP_IGD_Data_ information. This involves resolving relative paths - * and choosing between Common Interface Config and interface-specific - * paths. - * - * @param desc_url URL to the description file of the device - * @param data IGD information obtained from the description file - * @param control_url place to store a URL to control the IGD device (will be - * the empty string in case of failure) - * @param service_type place to store the service type corresponding to control_url - * (will be the empty string in case of failure) - */ -static void -format_control_urls (const char *desc_url, struct UPNP_IGD_Data_ *data, char **control_url, char **service_type) -{ - const char *ref_url; - int is_desc_file; - - if (data->base_url[0] != '\0') - { - ref_url = data->base_url; - is_desc_file = GNUNET_NO; - } - else - { - ref_url = desc_url; - is_desc_file = GNUNET_YES; - } - - if (data->control_url[0] != '\0') - { - *control_url = get_absolute_url (ref_url, is_desc_file, data->control_url); - *service_type = GNUNET_strdup (data->service_type); - } - else if (data->control_url_CIF[0] != '\0') - { - *control_url = get_absolute_url (ref_url, is_desc_file, data->control_url_CIF); - *service_type = GNUNET_strdup (data->service_type_CIF); - } - else - { - /* If no suitable URL-service type pair was found, set both to empty - * to avoid pretending things will work */ - *control_url = GNUNET_strdup (""); - *service_type = GNUNET_strdup (""); - } -} - -static void get_valid_igd (struct UPNP_discover_cls *cls); - -/** - * Called when "GetStatusInfo" command finishes. Check whether IGD device reports - * to be currently connected or not. - * - * @param response content of the UPnP message answered by the device - * @param received number of received bytes stored in response - * @param data closure from UPNP_discover() - */ -static void -get_valid_igd_connected_cb (char *response, size_t received, void *data) -{ - struct UPNP_discover_cls *cls = data; - struct UPNP_REPLY_NameValueList_ pdata; - char *status; - char *error; - - UPNP_REPLY_parse_ (response, received, &pdata); - - status = UPNP_REPLY_get_value_ (&pdata, "NewConnectionStatus"); - error = UPNP_REPLY_get_value_ (&pdata, "errorCode"); - - if (status) - cls->current_dev->is_connected = (strcmp ("Connected", status) == 0); - else - cls->current_dev->is_connected = GNUNET_NO; - - if (error) - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP", - _("Could not get UPnP device status: error %s\n"), - error); - - GNUNET_free (response); - UPNP_REPLY_free_ (&pdata); - - /* Go on to next device, or finish discovery process */ - cls->current_dev = cls->current_dev->pNext; - get_valid_igd (cls); -} - -/** - * Receive contents of the downloaded UPnP IGD description file, - * and fill UPNP_Dev_ and UPNP_IGD_Data_ structs with this data. - * Then, schedule UPnP command to check whether device is connected. - * - * @param desc UPnP IGD description (in XML) - * @param data closure from UPNP_discover() - */ -static void -get_valid_igd_receive (char *desc, void *data) -{ - struct UPNP_discover_cls *cls = data; - struct UPNP_IGD_Data_ *igd_data; - char *buffer; - - if (!desc || strlen (desc) == 0) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP", - "Error getting IGD XML description at %s:%d\n", - __FILE__, __LINE__); - - /* Skip device */ - cls->current_dev->data = NULL; - cls->current_dev->is_connected = GNUNET_NO; - get_valid_igd (cls); - } - - igd_data = GNUNET_malloc (sizeof (struct UPNP_IGD_Data_)); - memset (igd_data, 0, sizeof (struct UPNP_IGD_Data_)); - UPNP_IGD_parse_desc_ (desc, strlen (desc), igd_data); - - format_control_urls (cls->current_dev->desc_url, igd_data, - &cls->current_dev->control_url, - &cls->current_dev->service_type); - - cls->current_dev->data = igd_data; - - /* Check whether device is connected */ - buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE); - UPNP_command_ (cls->current_dev->control_url, - cls->current_dev->data->service_type, - "GetStatusInfo", NULL, buffer, UPNP_COMMAND_BUFSIZE, - get_valid_igd_connected_cb, cls); - - GNUNET_free (desc); -} - -/** - * Free a chained list of UPnP devices. - */ -static void -free_dev_list (struct UPNP_Dev_ *devlist) -{ - struct UPNP_Dev_ *next; - - while (devlist) - { - next = devlist->pNext; - GNUNET_free (devlist->control_url); - GNUNET_free (devlist->service_type); - GNUNET_free (devlist->desc_url); - GNUNET_free (devlist->data); - GNUNET_free (devlist->st); - GNUNET_free (devlist); - devlist = next; - } -} - -/** - * Walk over the list of found devices looking for a connected IGD, - * if present, or at least a disconnected one. - */ -static void -get_valid_igd (struct UPNP_discover_cls *cls) -{ - struct UPNP_Dev_ *dev; - int step; - - /* No device was discovered */ - if (!cls->dev_list) - { - cls->caller_cb (NULL, NULL, cls->caller_cls); - - GNUNET_free (cls); - return; - } - /* We already walked over all devices, see what we got, - * and return the device with the best state we have. */ - else if (cls->current_dev == NULL) - { - for (step = 1; step <= 3; step++) - { - for (dev = cls->dev_list; dev; dev = dev->pNext) - { -#if DEBUG_UPNP - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP", - "Found device: control_url: %s, service_type: %s\n", - dev->control_url, dev->service_type); -#endif - /* Accept connected IGDs on step 1, non-connected IGDs - * on step 2, and other device types on step 3. */ - if ((step == 1 && dev->is_connected) - || (step < 3 && 0 != strcmp (dev->service_type, - "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"))) - continue; - - cls->caller_cb (dev->control_url, - dev->service_type, cls->caller_cls); - - free_dev_list (cls->dev_list); - GNUNET_free (cls); - return; - } - } - - /* We cannot reach this... */ - GNUNET_assert (GNUNET_NO); - } - - /* There are still devices to ask, go on */ - download_device_description (cls->current_dev->desc_url, - get_valid_igd_receive, cls); -} - -static const char *const discover_type_list[] = { - "urn:schemas-upnp-org:device:InternetGatewayDevice:1", - "urn:schemas-upnp-org:service:WANIPConnection:1", - "urn:schemas-upnp-org:service:WANPPPConnection:1", - NULL -}; - -static void -discover_send (void *data, const struct GNUNET_SCHEDULER_TaskContext *tc); - -/** - * Handle response from device. Stop when all device types have been tried, - * and get their descriptions. - * - * @param data closure from UPNP_discover() - * @param tc task context - */ -static void -discover_recv (void *data, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct UPNP_discover_cls *cls = data; - GNUNET_SCHEDULER_TaskIdentifier task_w; - struct UPNP_Dev_ *tmp; - socklen_t addrlen; - ssize_t received; - char buf[DISCOVER_BUFSIZE]; - const char *desc_url = NULL; - int urlsize = 0; - const char *st = NULL; - int stsize = 0; - - /* Free fdset that was used for this sned/receive operation */ - GNUNET_NETWORK_fdset_destroy (cls->fdset); - - if (cls->multicast_addr->sa_family == AF_INET) - addrlen = sizeof (struct sockaddr_in); - else - addrlen = sizeof (struct sockaddr_in6); - - errno = 0; - received = - GNUNET_NETWORK_socket_recvfrom (cls->sudp, &buf, DISCOVER_BUFSIZE - 1, - (struct sockaddr *) cls->multicast_addr, - &addrlen); - if (received == GNUNET_SYSERR) - { - if (errno != EAGAIN) - PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_recvfrom"); - } -#if DEBUG_UPNP - else - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP", - "Received %d bytes from %s\n", received, - GNUNET_a2s (cls->multicast_addr, addrlen)); - } -#endif - - parse_msearch_reply (buf, received, &desc_url, &urlsize, &st, &stsize); - - if (st && desc_url) - { - tmp = (struct UPNP_Dev_ *) GNUNET_malloc (sizeof (struct UPNP_Dev_)); - tmp->pNext = cls->dev_list; - - tmp->desc_url = GNUNET_malloc (urlsize + 1); - strncpy (tmp->desc_url, desc_url, urlsize); - tmp->desc_url[urlsize] = '\0'; - - tmp->st = GNUNET_malloc (stsize + 1); - strncpy (tmp->st, st, stsize); - tmp->st[stsize] = '\0'; - cls->dev_list = tmp; -#if DEBUG_UPNP - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP", - "Found device %s when looking for type %s\n", - tmp->desc_url, tmp->st); -#endif - } - - /* Continue discovery until all types of devices have been tried */ - if (discover_type_list[cls->type_index]) - { - /* Send queries for each device type and wait for a possible reply. - * receiver callback takes care of trying another device type, - * and eventually calls the caller's callback. */ - cls->fdset = GNUNET_NETWORK_fdset_create (); - GNUNET_NETWORK_fdset_zero (cls->fdset); - GNUNET_NETWORK_fdset_set (cls->fdset, cls->sudp); - - task_w = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, - GNUNET_SCHEDULER_NO_TASK, - GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 15), - NULL, cls->fdset, &discover_send, - cls); - - GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, - task_w, - GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 5), cls->fdset, - NULL, &discover_recv, cls); - } - else - { - GNUNET_NETWORK_socket_close (cls->sudp); - GNUNET_free (cls->multicast_addr); - cls->current_dev = cls->dev_list; - get_valid_igd (cls); - } -} - -/** - * Send the SSDP M-SEARCH packet. - * - * @param data closure from UPNP_discover() - * @param tc task context - */ -static void -discover_send (void *data, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct UPNP_discover_cls *cls = data; - socklen_t addrlen; - ssize_t n, sent; - char buf[DISCOVER_BUFSIZE]; - static const char msearch_msg[] = - "M-SEARCH * HTTP/1.1\r\n" - "HOST: " UPNP_MCAST_ADDR ":" XSTR (PORT) "\r\n" - "ST: %s\r\n" "MAN: \"ssdp:discover\"\r\n" "MX: 3\r\n" "\r\n"; - - if (cls->multicast_addr->sa_family == AF_INET) - addrlen = sizeof (struct sockaddr_in); - else - addrlen = sizeof (struct sockaddr_in6); - - n = - snprintf (buf, DISCOVER_BUFSIZE, msearch_msg, - discover_type_list[cls->type_index++]); - - errno = 0; - sent = GNUNET_NETWORK_socket_sendto (cls->sudp, buf, n, - (struct sockaddr *) - cls->multicast_addr, addrlen); - if (sent == GNUNET_SYSERR) - { - PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_sendto"); - } - else if (sent < n) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP", - "Could only send %d bytes to %s, needed %d bytes\n", - sent, GNUNET_a2s (cls->multicast_addr, addrlen), n); - } -#if DEBUG_UPNP - else - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP", - "Sent %d bytes to %s\n", sent, - GNUNET_a2s (cls->multicast_addr, addrlen)); - } -#endif -} - -/** - * Search for UPnP Internet Gateway Devices (IGD) on a given network interface. - * If several devices are found, a device that is connected to the WAN - * is returned first (if any). - * - * @param multicastif network interface to send discovery messages, or NULL - * @param addr address used to send messages on multicastif, or NULL - * @param caller_cb user function to call when done - * @param caller_cls closure to pass to caller_cb - */ -void -UPNP_discover_ (const char *multicastif, - const struct sockaddr *addr, - UPNP_discover_cb_ caller_cb, void *caller_cls) -{ - int opt = 1; - int domain = PF_INET; - int if_index; - struct in6_addr any_addr = IN6ADDR_ANY_INIT; - struct sockaddr_in sockudp_r, sockudp_w; - struct sockaddr_in6 sockudp6_r, sockudp6_w; - GNUNET_SCHEDULER_TaskIdentifier task_w; - struct GNUNET_NETWORK_Handle *sudp; - struct UPNP_discover_cls *cls; - - - if (addr && addr->sa_family == AF_INET) - { - domain = PF_INET; - } - else if (addr && addr->sa_family == AF_INET6) - { - domain = PF_INET6; - } - else if (addr) - { - GNUNET_break (0); - caller_cb (NULL, NULL, caller_cls); - return; - } - - errno = 0; - sudp = GNUNET_NETWORK_socket_create (domain, SOCK_DGRAM, 0); - - if (sudp == NULL) - { - PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_create"); - caller_cb (NULL, NULL, caller_cls); - return; - } - - - cls = GNUNET_malloc (sizeof (struct UPNP_discover_cls)); - cls->sudp = sudp; - cls->type_index = 0; - cls->dev_list = NULL; - cls->current_dev = NULL; - cls->caller_cb = caller_cb; - cls->caller_cls = caller_cls; - - - if (domain == PF_INET) - { - /* receive */ - memset (&sockudp_r, 0, sizeof (struct sockaddr_in)); - sockudp_r.sin_family = AF_INET; -#ifdef HAVE_SOCKADDR_IN_SIN_LEN - sockudp_r.sin_len = sizeof (struct sockaddr_in); -#endif - sockudp_r.sin_port = 0; - sockudp_r.sin_addr.s_addr = INADDR_ANY; - - /* send */ - memset (&sockudp_w, 0, sizeof (struct sockaddr_in)); - sockudp_w.sin_family = AF_INET; - sockudp_w.sin_port = htons (PORT); - sockudp_w.sin_addr.s_addr = inet_addr (UPNP_MCAST_ADDR); -#ifdef HAVE_SOCKADDR_IN_SIN_LEN - sockudp_w.sin_len = sizeof (struct sockaddr_in); -#endif - - cls->multicast_addr = GNUNET_malloc (sizeof (struct sockaddr_in)); - memcpy (cls->multicast_addr, &sockudp_w, sizeof (struct sockaddr_in)); - } - else - { - /* receive */ - memcpy (&sockudp6_r, addr, sizeof (struct sockaddr_in6)); - sockudp6_r.sin6_port = 0; - sockudp6_r.sin6_addr = any_addr; -#ifdef HAVE_SOCKADDR_IN_SIN_LEN - sockudp6_r.sin6_len = sizeof (struct sockaddr_in6); -#endif - - /* send */ - memset (&sockudp6_w, 0, sizeof (struct sockaddr_in6)); - sockudp6_w.sin6_family = AF_INET6; - sockudp6_w.sin6_port = htons (PORT); - if (inet_pton (AF_INET6, UPNP_MCAST_ADDR6, &sockudp6_w.sin6_addr) != 1) - { - PRINT_SOCKET_ERROR ("inet_pton"); - caller_cb (NULL, NULL, caller_cls); - return; - } -#ifdef HAVE_SOCKADDR_IN_SIN_LEN - sockudp6_w.sin6_len = sizeof (struct sockaddr_in6); -#endif - - cls->multicast_addr = GNUNET_malloc (sizeof (struct sockaddr_in6)); - memcpy (cls->multicast_addr, &sockudp6_w, sizeof (struct sockaddr_in6)); - } - - if (GNUNET_NETWORK_socket_setsockopt - (sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) == GNUNET_SYSERR) - { - PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_setsockopt"); - GNUNET_NETWORK_socket_close (sudp); - caller_cb (NULL, NULL, caller_cls); - return; - } - - if (addr) - { - if (domain == PF_INET) - { - sockudp_r.sin_addr.s_addr = - ((struct sockaddr_in *) addr)->sin_addr.s_addr; - if (GNUNET_NETWORK_socket_setsockopt - (sudp, IPPROTO_IP, IP_MULTICAST_IF, - (const char *) &sockudp_r.sin_addr, - sizeof (struct in_addr)) == GNUNET_SYSERR) - { - PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_setsockopt"); - } - } - else - { - if (multicastif) - { -#ifndef MINGW - if_index = if_nametoindex (multicastif); -#else - // FIXME - if_index = 0; -#endif - if (!if_index) - PRINT_SOCKET_ERROR_STR ("if_nametoindex", multicastif); - - if (GNUNET_NETWORK_socket_setsockopt - (sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index, - sizeof (if_index)) == GNUNET_SYSERR) - { - PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_setsockopt"); - } - } - - memcpy (&sockudp6_r.sin6_addr, - &((struct sockaddr_in6 *) addr)->sin6_addr, - sizeof (sockudp6_r.sin6_addr)); - } - } - - if (domain == PF_INET) - { - /* Bind to receive response before sending packet */ - if (GNUNET_NETWORK_socket_bind - (sudp, (struct sockaddr *) &sockudp_r, - sizeof (struct sockaddr_in)) != GNUNET_OK) - { - PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_bind"); - GNUNET_NETWORK_socket_close (sudp); - GNUNET_free (cls->multicast_addr); - caller_cb (NULL, NULL, caller_cls); - return; - } - } - else - { - /* Bind to receive response before sending packet */ - if (GNUNET_NETWORK_socket_bind - (sudp, (struct sockaddr *) &sockudp6_r, - sizeof (struct sockaddr_in6)) != GNUNET_OK) - { - PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_bind"); - GNUNET_free (cls->multicast_addr); - GNUNET_NETWORK_socket_close (sudp); - caller_cb (NULL, NULL, caller_cls); - return; - } - } - - /* Send queries for each device type and wait for a possible reply. - * receiver callback takes care of trying another device type, - * and eventually calls the caller's callback. */ - cls->fdset = GNUNET_NETWORK_fdset_create (); - GNUNET_NETWORK_fdset_zero (cls->fdset); - GNUNET_NETWORK_fdset_set (cls->fdset, sudp); - - task_w = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, - GNUNET_SCHEDULER_NO_TASK, - GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 15), NULL, - cls->fdset, &discover_send, cls); - - GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, - task_w, - GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 15), cls->fdset, - NULL, &discover_recv, cls); -} diff --git a/src/nat/upnp-discover.h b/src/nat/upnp-discover.h deleted file mode 100644 index 39b704bcf..000000000 --- a/src/nat/upnp-discover.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2009, 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 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., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. -*/ - -/* - * Code in this file is originally based on the miniupnp library. - * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved. - * - * Original licence: - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file nat/upnp-discover.h - * @brief Look for UPnP IGD devices - * - * @author Milan Bouchet-Valat - */ -#ifndef UPNPC_H -#define UPNPC_H - -#include "platform.h" -#include "gnunet_common.h" -#include "gnunet_util_lib.h" -#include "upnp-commands.h" - -typedef void (*UPNP_discover_cb_) (const char *control_urls, - const char *service_type, void *cls); - -/** - * Search for UPnP Internet Gateway Devices (IGD) on a given network interface. - * If several devices are found, a device that is connected to the WAN - * is returned first (if any). - * - * @param multicastif network interface to send discovery messages, or NULL - * @param addr address used to send messages on multicastif, or NULL - * @param caller_cb user function to call when done - * @param caller_cls closure to pass to caller_cb - */ -void UPNP_discover_ (const char *multicastif, - const struct sockaddr *addr, - UPNP_discover_cb_ caller_cb, void *caller_cls); - -#endif diff --git a/src/nat/upnp-igd-parse.c b/src/nat/upnp-igd-parse.c deleted file mode 100644 index 0812065ed..000000000 --- a/src/nat/upnp-igd-parse.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2009, 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 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., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. -*/ - -/* - * Code in this file is originally based on the miniupnp library. - * Copyright (c) 2005-2008, Thomas BERNARD. All rights reserved. - * - * Original licence: - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file nat/upnp-igd-parse.h - * @brief Parser for XML descriptions of UPnP Internet Gateway Devices - * - * @author Milan Bouchet-Valat - */ -#include -#include - -#include "platform.h" -#include "gnunet_util_lib.h" -#include "upnp-minixml.h" -#include "upnp-igd-parse.h" - -/** - * Start element handler: update nesting level counter - * and copy element name. - */ -static void -start_elt (void *d, const char *name, int l) -{ - struct UPNP_IGD_Data_ *datas = (struct UPNP_IGD_Data_ *) d; - - memcpy (datas->cur_elt_name, name, l); - datas->cur_elt_name[l] = '\0'; - datas->level++; - if ((l == 7) && !memcmp (name, "service", l)) - { - datas->control_url_tmp[0] = '\0'; - datas->event_sub_url_tmp[0] = '\0'; - datas->scpd_url_tmp[0] = '\0'; - datas->service_type_tmp[0] = '\0'; - } -} - -/** - * End element handler: update nesting level counter - * and update parser state if service element is parsed. - */ -static void -end_elt (void *d, const char *name, int l) -{ - struct UPNP_IGD_Data_ *datas = (struct UPNP_IGD_Data_ *) d; - - datas->level--; - - if ((l == 7) && !memcmp (name, "service", l)) - { - if (0 == strcmp (datas->service_type_tmp, - "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")) - { - memcpy (datas->control_url_CIF, datas->control_url_tmp, - MINIUPNPC_URL_MAXSIZE); - memcpy (datas->event_sub_url_CIF, datas->event_sub_url_tmp, - MINIUPNPC_URL_MAXSIZE); - memcpy (datas->scpd_url_CIF, datas->scpd_url_tmp, - MINIUPNPC_URL_MAXSIZE); - memcpy (datas->service_type_CIF, datas->service_type_tmp, - MINIUPNPC_URL_MAXSIZE); - } - else if (0 == strcmp (datas->service_type_tmp, - "urn:schemas-upnp-org:service:WANIPConnection:1") - || 0 == strcmp (datas->service_type_tmp, - "urn:schemas-upnp-org:service:WANPPPConnection:1")) - { - memcpy (datas->control_url, datas->control_url_tmp, - MINIUPNPC_URL_MAXSIZE); - memcpy (datas->event_sub_url, datas->event_sub_url_tmp, - MINIUPNPC_URL_MAXSIZE); - memcpy (datas->scpd_url, datas->scpd_url_tmp, - MINIUPNPC_URL_MAXSIZE); - memcpy (datas->service_type, datas->service_type_tmp, - MINIUPNPC_URL_MAXSIZE); - } - } -} - -/** - * Data handler: copy data depending on the current - * element name and state. - */ -static void -IGDdata (void *d, const char *data, int l) -{ - struct UPNP_IGD_Data_ *datas = (struct UPNP_IGD_Data_ *) d; - char *dstmember = NULL; - - if (!strcmp (datas->cur_elt_name, "URLBase")) - dstmember = datas->base_url; - else if (!strcmp (datas->cur_elt_name, "serviceType")) - dstmember = datas->service_type_tmp; - else if (!strcmp (datas->cur_elt_name, "controlURL")) - dstmember = datas->control_url_tmp; - else if (!strcmp (datas->cur_elt_name, "eventSubURL")) - dstmember = datas->event_sub_url_tmp; - else if (!strcmp (datas->cur_elt_name, "SCPDURL")) - dstmember = datas->scpd_url_tmp; - - /* Copy current element name into destination member */ - if (dstmember) - { - if (l >= MINIUPNPC_URL_MAXSIZE) - l = MINIUPNPC_URL_MAXSIZE - 1; - - memcpy (dstmember, data, l); - dstmember[l] = '\0'; - } -} - -#ifdef DEBUG_UPNP -static void -print_IGD (struct UPNP_IGD_Data_ *d) -{ - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP", - "base_url = %s\n", d->base_url); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP", - "WAN Device (Common interface config) :\n" - " sevice_type = %s\n" - " control_url = %s\n" - " event_sub_url = %s\n" - " scpd_url = %s\n", - d->service_type_CIF, - d->control_url_CIF, d->event_sub_url_CIF, d->scpd_url_CIF); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP", - "WAN Connection Device (IP or PPP Connection):\n" - " service_type = %s\n" - " control_url = %s\n" - " event_sub_url = %s\n" - " scpd_url = %s\n", - d->service_type, - d->control_url, d->event_sub_url, d->scpd_url); -} -#endif - -/** - * Parse XML description of an IGD device into a UPNP_IGD_Data_ struct. - */ -void -UPNP_IGD_parse_desc_ (const char *buffer, int buf_size, - struct UPNP_IGD_Data_ *data) -{ - struct UPNP_xml_parser_ parser; - - parser.xml_start = buffer; - parser.xml_size = buf_size; - parser.cls = data; - parser.start_elt_func = start_elt; - parser.end_elt_func = end_elt; - parser.data_func = IGDdata; - parser.att_func = 0; - - UPNP_parse_xml_ (&parser); - -#ifdef DEBUG_UPNP - print_IGD (data); -#endif -} diff --git a/src/nat/upnp-igd-parse.h b/src/nat/upnp-igd-parse.h deleted file mode 100644 index 8e0b8510c..000000000 --- a/src/nat/upnp-igd-parse.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2009, 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 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., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. -*/ - -/* - * Code in this file is originally based on the miniupnp library. - * Copyright (c) 2005-2008, Thomas BERNARD. All rights reserved. - * - * Original licence: - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file nat/upnp-igd-parse.h - * @brief Parser for XML descriptions of UPnP Internet Gateway Devices - * - * @author Milan Bouchet-Valat - */ -#ifndef UPNP_IGD_PARSE_H -#define UPNP_IGD_PARSE_H - -#define MINIUPNPC_URL_MAXSIZE (128) - -/** - * Structure to store the result of the parsing of UPnP - * descriptions of Internet Gateway Devices. - */ -struct UPNP_IGD_Data_ -{ - char cur_elt_name[MINIUPNPC_URL_MAXSIZE]; - char base_url[MINIUPNPC_URL_MAXSIZE]; - int level; - - /* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */ - char control_url_CIF[MINIUPNPC_URL_MAXSIZE]; - char event_sub_url_CIF[MINIUPNPC_URL_MAXSIZE]; - char scpd_url_CIF[MINIUPNPC_URL_MAXSIZE]; - char service_type_CIF[MINIUPNPC_URL_MAXSIZE]; - - /* "urn:schemas-upnp-org:service:WANIPConnection:1" - * "urn:schemas-upnp-org:service:WANPPPConnection:1" */ - char control_url[MINIUPNPC_URL_MAXSIZE]; - char event_sub_url[MINIUPNPC_URL_MAXSIZE]; - char scpd_url[MINIUPNPC_URL_MAXSIZE]; - char service_type[MINIUPNPC_URL_MAXSIZE]; - - /* Used temporarily by the parser */ - char control_url_tmp[MINIUPNPC_URL_MAXSIZE]; - char event_sub_url_tmp[MINIUPNPC_URL_MAXSIZE]; - char scpd_url_tmp[MINIUPNPC_URL_MAXSIZE]; - char service_type_tmp[MINIUPNPC_URL_MAXSIZE]; -}; - -/** - * Parse UPnP IGD XML description to a UPNP_IGD_Data_ structure. - */ -void -UPNP_IGD_parse_desc_ (const char *buffer, int buf_size, - struct UPNP_IGD_Data_ *data); - -#endif diff --git a/src/nat/upnp-minixml.c b/src/nat/upnp-minixml.c deleted file mode 100644 index cd2d48739..000000000 --- a/src/nat/upnp-minixml.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2009, 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 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., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. -*/ - -/* - * Code in this file is originally based on the miniupnp library. - * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved. - * - * Original licence: - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file nat/upnp-minixml.c - * @brief Simple XML parser used by UPnP - * - * @author Milan Bouchet-Valat - */ - -#include "platform.h" -#include "gnunet_util_lib.h" -#include "upnp-minixml.h" - -/** - * Used to parse the argument list. - * - * @returns GNUNET_OK on success, or GNUNET_SYSERR if the end - * of the xmlbuffer is reached - */ -static int -parse_att (struct UPNP_xml_parser_ *p) -{ - const char *att_name; - int att_name_len; - const char *att_value; - int att_value_len; - while (p->xml < p->xml_end) - { - if (*p->xml == '/' || *p->xml == '>') - return GNUNET_OK; - if (!IS_WHITE_SPACE (*p->xml)) - { - char sep; - att_name = p->xml; - att_name_len = 0; - while (*p->xml != '=' && !IS_WHITE_SPACE (*p->xml)) - { - att_name_len++; - p->xml++; - if (p->xml >= p->xml_end) - return GNUNET_SYSERR; - } - while (*(p->xml++) != '=') - { - if (p->xml >= p->xml_end) - return GNUNET_SYSERR; - } - while (IS_WHITE_SPACE (*p->xml)) - { - p->xml++; - if (p->xml >= p->xml_end) - return GNUNET_SYSERR; - } - sep = *p->xml; - if (sep == '\'' || sep == '\"') - { - p->xml++; - if (p->xml >= p->xml_end) - return GNUNET_SYSERR; - att_value = p->xml; - att_value_len = 0; - while (*p->xml != sep) - { - att_value_len++; - p->xml++; - if (p->xml >= p->xml_end) - return GNUNET_SYSERR; - } - } - else - { - att_value = p->xml; - att_value_len = 0; - while (!IS_WHITE_SPACE (*p->xml) - && *p->xml != '>' && *p->xml != '/') - { - att_value_len++; - p->xml++; - if (p->xml >= p->xml_end) - return GNUNET_SYSERR; - } - } - - if (p->att_func) - p->att_func (p->cls, att_name, att_name_len, att_value, - att_value_len); - } - p->xml++; - } - return GNUNET_SYSERR; -} - -/** - * Parse the xml stream and call the callback - * functions when needed... - */ -void -parse_elt (struct UPNP_xml_parser_ *p) -{ - int i; - const char *element_name; - while (p->xml < (p->xml_end - 1)) - { - /* Element name */ - if ((p->xml)[0] == '<' && (p->xml)[1] != '?') - { - i = 0; - element_name = ++p->xml; - while (!IS_WHITE_SPACE (*p->xml) - && (*p->xml != '>') && (*p->xml != '/')) - { - i++; - p->xml++; - if (p->xml >= p->xml_end) - return; - /* to ignore namespace : */ - if (*p->xml == ':') - { - i = 0; - element_name = ++p->xml; - } - } - - /* Start of element */ - if (i > 0) - { - if (p->start_elt_func) - p->start_elt_func (p->cls, element_name, i); - if (parse_att (p) != GNUNET_OK) - return; - if (*p->xml != '/') - { - const char *data; - i = 0; - data = ++p->xml; - if (p->xml >= p->xml_end) - return; - while (IS_WHITE_SPACE (*p->xml)) - { - p->xml++; - if (p->xml >= p->xml_end) - return; - } - while (*p->xml != '<') - { - i++; - p->xml++; - if (p->xml >= p->xml_end) - return; - } - if (i > 0 && p->data_func) - p->data_func (p->cls, data, i); - } - } - /* End of element */ - else if (*p->xml == '/') - { - i = 0; - element_name = ++p->xml; - if (p->xml >= p->xml_end) - return; - while ((*p->xml != '>')) - { - i++; - p->xml++; - if (p->xml >= p->xml_end) - return; - } - if (p->end_elt_func) - p->end_elt_func (p->cls, element_name, i); - p->xml++; - } - } - else - { - p->xml++; - } - } -} - -/** - * Parse XML content according to the values stored in the parser struct. - * The parser must be initialized before calling this function - */ -void -UPNP_parse_xml_ (struct UPNP_xml_parser_ *parser) -{ - parser->xml = parser->xml_start; - parser->xml_end = parser->xml_start + parser->xml_size; - parse_elt (parser); -} diff --git a/src/nat/upnp-minixml.h b/src/nat/upnp-minixml.h deleted file mode 100644 index 07cf70939..000000000 --- a/src/nat/upnp-minixml.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2009, 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 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., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. -*/ - -/* - * Code in this file is originally based on the miniupnp library. - * Copyright (c) 2005-2008, Thomas BERNARD. All rights reserved. - * - * Original licence: - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file nat/upnp-minixml.h - * @brief Simple XML parser used by UPnP - * - * @author Milan Bouchet-Valat - */ - -#ifndef MINIXML_H -#define MINIXML_H - -#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n')) - -/** - * Structure describing the contents and methods that should be - * used when running parse_xml(); - * - * If a callback function pointer is set to NULL, the function - * is not called */ -struct UPNP_xml_parser_ -{ - /** - * Pointer to the XML data to parse - */ - const char *xml_start; - - /** - * Pointer to the last character to parse (optional) - */ - const char *xml_end; - - /** - * Size of the data stored at xml_start - */ - int xml_size; - - /** - * Pointer to current character (private) - */ - const char *xml; - - /** - * Closure for user-provided callback functions - */ - void *cls; - - /** - * User function called when reaching the start of an XML element. - */ - void (*start_elt_func) (void *cls, const char *elt, int elt_len); - - /** - * User function called when reaching the end of an XML element. - */ - void (*end_elt_func) (void *cls, const char *elt, int elt_len); - - /** - * User function called when an XML element data is found. - */ - void (*data_func) (void *cls, const char *data, int data_len); - - /** - * User function called for every XML element attribute. - */ - void (*att_func) (void *cls, const char *att_name, int att_name_len, - const char *att_value, int att_value_len); -}; - -/** - * Parse data provided to the xml_parser structure, using - * user-provided functions. - * - * The xmlparser structure must be initialized before the call; - * the following structure members have to be set: - * xml_start, xml_size, cls, *func. - * The xml member is for internal usage, xml_end is computed - * automatically. - * - * @param parser the structure used for parsing */ -void UPNP_parse_xml_ (struct UPNP_xml_parser_ *parser); - -#endif diff --git a/src/nat/upnp-reply-parse.c b/src/nat/upnp-reply-parse.c deleted file mode 100644 index 398cde834..000000000 --- a/src/nat/upnp-reply-parse.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2009, 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 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., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. -*/ - -/* - * Code in this file is originally inspired by the miniupnp library. - * Copyright (c) 2006, Thomas BERNARD. All rights reserved. - * - * Original licence: - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file nat/upnp-reply-parse.c - * @brief Parser for XML replies to UPnP commands - * - * @author Milan Bouchet-Valat - */ - -#include -#include -#include - -#include "platform.h" -#include "gnunet_util_lib.h" -#include "upnp-minixml.h" -#include "upnp-reply-parse.h" - -static void -start_elt (void *d, const char *name, int l) -{ - struct UPNP_REPLY_NameValueList_ *data = - (struct UPNP_REPLY_NameValueList_ *) d; - - if (l > 63) - l = 63; - - memcpy (data->curelt, name, l); - data->curelt[l] = '\0'; -} - -static void -get_data (void *d, const char *datas, int l) -{ - struct UPNP_REPLY_NameValueList_ *data = - (struct UPNP_REPLY_NameValueList_ *) d; - struct UPNP_REPLY_NameValue_ *nv; - - nv = malloc (sizeof (struct UPNP_REPLY_NameValue_)); - - if (l > 63) - l = 63; - - strncpy (nv->name, data->curelt, 64); - nv->name[63] = '\0'; - memcpy (nv->value, datas, l); - nv->value[l] = '\0'; - - LIST_INSERT_HEAD (&(data->head), nv, entries); -} - -void -UPNP_REPLY_parse_ (const char *buffer, int buf_size, - struct UPNP_REPLY_NameValueList_ *data) -{ - struct UPNP_xml_parser_ parser; - - LIST_INIT (&(data->head)); - - /* Init xml_parser object */ - parser.xml_start = buffer; - parser.xml_size = buf_size; - parser.cls = data; - parser.start_elt_func = start_elt; - parser.end_elt_func = 0; - parser.data_func = get_data; - parser.att_func = 0; - - UPNP_parse_xml_ (&parser); -} - -void -UPNP_REPLY_free_ (struct UPNP_REPLY_NameValueList_ *pdata) -{ - struct UPNP_REPLY_NameValue_ *nv; - - while ((nv = pdata->head.lh_first) != NULL) - { - LIST_REMOVE (nv, entries); - GNUNET_free (nv); - } -} - -char * -UPNP_REPLY_get_value_ (struct UPNP_REPLY_NameValueList_ *pdata, - const char *Name) -{ - struct UPNP_REPLY_NameValue_ *nv; - char *p = NULL; - - for (nv = pdata->head.lh_first; - (nv != NULL) && (p == NULL); nv = nv->entries.le_next) - { - if (strcmp (nv->name, Name) == 0) - p = nv->value; - } - - return p; -} - -#if DEBUG_UPNP -void -UPNP_REPLY_print_ (char *buffer, int buf_size) -{ - struct UPNP_REPLY_NameValueList_ pdata; - struct UPNP_REPLY_NameValue_ *nv; - - UPNP_REPLY_parse_ (buffer, buf_size, &pdata); - - for (nv = pdata.head.lh_first; nv != NULL; nv = nv->entries.le_next) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP", - "%s = %s", nv->name, nv->value); - } - - UPNP_REPLY_free_ (&pdata); -} -#endif diff --git a/src/nat/upnp-reply-parse.h b/src/nat/upnp-reply-parse.h deleted file mode 100644 index e5bedbd8f..000000000 --- a/src/nat/upnp-reply-parse.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2009, 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 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., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. -*/ - -/* - * Code in this file is originally inspired by the miniupnp library. - * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved. - * - * Original licence: - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file nat/upnp-reply-parse.h - * @brief Parser for XML replies to UPnP commands - * - * @author Milan Bouchet-Valat - */ - -#ifndef UPNP_PARSE_REPLY_H -#define UPNP_PARSE_REPLY_H - -#include "bsdqueue.h" - - /** - * Name-value pair used by UPNP_REPLY_NameValueList. - */ -struct UPNP_REPLY_NameValue_ -{ - LIST_ENTRY (UPNP_REPLY_NameValue_) entries; - char name[64]; - char value[64]; -}; - - /** - * Name-value list to store data parsed from a UPnP reply. - */ -struct UPNP_REPLY_NameValueList_ -{ - LIST_HEAD (listhead, UPNP_REPLY_NameValue_) head; - char curelt[64]; -}; - - /** - * Parse UPnP XML reply to a name-value list. - */ -void -UPNP_REPLY_parse_ (const char *buffer, int buf_size, - struct UPNP_REPLY_NameValueList_ *data); - - /** - * Free name-value list obtained using UPNP_REPLY_parse(). - */ -void UPNP_REPLY_free_ (struct UPNP_REPLY_NameValueList_ *pdata); - - /** - * Get value corresponding to name from a name-value list. - */ -char *UPNP_REPLY_get_value_ (struct UPNP_REPLY_NameValueList_ *pdata, - const char *name); - -#if DEBUG_UPNP - /** - * Parse a UPnP XMl reply and print the result as names-value pairs. - */ -void UPNP_REPLY_print_ (char *buffer, int buf_size); -#endif - -#endif diff --git a/src/nat/upnp.c b/src/nat/upnp.c deleted file mode 100644 index 45c7df419..000000000 --- a/src/nat/upnp.c +++ /dev/null @@ -1,422 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2009 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 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., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. -*/ - -/* - * This file has been adapted from the Transmission project: - * Originally licensed by the GPL version 2. - * Copyright (C) 2007-2009 Charles Kerr - */ - -/** - * @file nat/upnp.c - * @brief UPnP support for the NAT library - * - * @author Milan Bouchet-Valat - */ -#include "platform.h" -#include "gnunet_common.h" - -#include -#include -#include -#include - -#include "gnunet_nat_lib.h" -#include "nat.h" -#include "upnp-discover.h" -#include "upnp-commands.h" -#include "upnp.h" - -/* Component name for logging */ -#define COMP_NAT_UPNP _("NAT (UPnP)") - -enum UPNP_State -{ - UPNP_IDLE, - UPNP_ERR, - UPNP_DISCOVER, - UPNP_MAP, - UPNP_UNMAP -}; - -struct GNUNET_NAT_UPNP_Handle -{ - int hasDiscovered; - char *control_url; - char *service_type; - int port; - const struct sockaddr *addr; - socklen_t addrlen; - unsigned int is_mapped; - enum UPNP_State state; - struct sockaddr *ext_addr; - char *iface; - int processing; - GNUNET_NAT_UPNP_pulse_cb pulse_cb; - void *pulse_cls; -}; - -static int -process_if (void *cls, - const char *name, - int isDefault, const struct sockaddr *addr, socklen_t addrlen) -{ - struct GNUNET_NAT_UPNP_Handle *upnp = cls; - GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "UPNP found if `%s'\n", name); - if (addr && GNUNET_NAT_cmp_addr (upnp->addr, addr) == 0) - { - upnp->iface = GNUNET_strdup(name); // BADNESS! - return GNUNET_SYSERR; - } - - return GNUNET_OK; -} - - -struct GNUNET_NAT_UPNP_Handle * -GNUNET_NAT_UPNP_init (const struct sockaddr *addr, - socklen_t addrlen, - u_short port, - GNUNET_NAT_UPNP_pulse_cb pulse_cb, void *pulse_cls) -{ - struct GNUNET_NAT_UPNP_Handle *handle; - - handle = GNUNET_malloc (sizeof (struct GNUNET_NAT_UPNP_Handle)); - handle->processing = GNUNET_NO; - handle->state = UPNP_DISCOVER; - handle->addr = addr; - handle->addrlen = addrlen; - handle->port = port; - handle->pulse_cb = pulse_cb; - handle->pulse_cls = pulse_cls; - handle->control_url = NULL; - handle->service_type = NULL; - - /* Find the interface corresponding to the address, - * on which we should broadcast call for routers */ - GNUNET_OS_network_interfaces_list (&process_if, handle); - if (!handle->iface) - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, - COMP_NAT_UPNP, - "Could not find an interface matching the wanted address.\n"); - return handle; -} - - -void -GNUNET_NAT_UPNP_close (struct GNUNET_NAT_UPNP_Handle *handle) -{ - GNUNET_assert (!handle->is_mapped); - GNUNET_assert ((handle->state == UPNP_IDLE) - || (handle->state == UPNP_ERR) - || (handle->state == UPNP_DISCOVER)); - - GNUNET_free_non_null (handle->control_url); - GNUNET_free_non_null (handle->service_type); - GNUNET_free (handle); -} - -static void -pulse_finish (struct GNUNET_NAT_UPNP_Handle *handle) -{ - enum GNUNET_NAT_PortState status; - handle->processing = GNUNET_NO; - - switch (handle->state) - { - case UPNP_DISCOVER: - status = GNUNET_NAT_PORT_UNMAPPED; - break; - - case UPNP_MAP: - status = GNUNET_NAT_PORT_MAPPING; - break; - - case UPNP_UNMAP: - status = GNUNET_NAT_PORT_UNMAPPING; - break; - - case UPNP_IDLE: - status = - handle->is_mapped ? GNUNET_NAT_PORT_MAPPED : GNUNET_NAT_PORT_UNMAPPED; - break; - - default: - status = GNUNET_NAT_PORT_ERROR; - break; - } - - handle->pulse_cb (status, handle->ext_addr, handle->pulse_cls); -} - -static void -discover_cb (const char *control_url, const char *service_type, void *cls) -{ - struct GNUNET_NAT_UPNP_Handle *handle = cls; - - if (control_url) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP, - _("Found Internet Gateway Device \"%s\"\n"), - control_url); - - GNUNET_free_non_null (handle->control_url); - GNUNET_free_non_null (handle->service_type); - - handle->control_url = GNUNET_strdup (control_url); - handle->service_type = GNUNET_strdup (service_type); - handle->state = UPNP_IDLE; - handle->hasDiscovered = 1; - } - else - { - handle->control_url = NULL; - handle->service_type = NULL; - handle->state = UPNP_ERR; -#ifdef DEBUG_UPNP - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP, - "UPNP device discovery failed\n"); -#endif - } - - pulse_finish (handle); -} - -static void -check_port_mapping_cb (int error, const char *control_url, - const char *service_type, const char *extPort, - const char *inPort, const char *proto, - const char *remoteHost, void *cls) -{ - struct GNUNET_NAT_UPNP_Handle *handle = cls; - - if (error) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP, - _("Port %d isn't forwarded\n"), handle->port); - handle->is_mapped = GNUNET_NO; - } - - pulse_finish (handle); -} - -static void -delete_port_mapping_cb (int error, const char *control_url, - const char *service_type, const char *extPort, - const char *inPort, const char *proto, - const char *remoteHost, void *cls) -{ - struct GNUNET_NAT_UPNP_Handle *handle = cls; - - if (error) - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP, - _ - ("Could not stop port forwarding through \"%s\", service \"%s\": error %d\n"), - handle->control_url, handle->service_type, error); - else - { - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP, - _ - ("Stopped port forwarding through \"%s\", service \"%s\"\n"), - handle->control_url, handle->service_type); - handle->is_mapped = !error; - handle->state = UPNP_IDLE; - handle->port = -1; - } - - pulse_finish (handle); -} - -static void -add_port_mapping_cb (int error, const char *control_url, - const char *service_type, const char *extPort, - const char *inPort, const char *proto, - const char *remoteHost, void *cls) -{ - struct GNUNET_NAT_UPNP_Handle *handle = cls; - - if (error) - { - handle->is_mapped = GNUNET_NO; - handle->state = UPNP_ERR; - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP, - _ - ("Port forwarding through \"%s\", service \"%s\" failed with error %d\n"), - handle->control_url, handle->service_type, error); - return; - } - else - { - handle->is_mapped = GNUNET_NO; - handle->state = UPNP_IDLE; - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP, - _("Port %d forwarded successfully\n"), handle->port); - } - - pulse_finish (handle); -} - -static void -get_ip_address_cb (int error, char *ext_addr, void *cls) -{ - struct GNUNET_NAT_UPNP_Handle *handle = cls; - - if (error) - { - if (handle->ext_addr) - { - GNUNET_free (handle->ext_addr); - handle->ext_addr = NULL; - } -#ifdef DEBUG_UPNP - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP, - "UPNP_get_external_ip_address_ failed (error %d)\n", - error); -#endif - } - else - { - struct in_addr addr; - struct in6_addr addr6; - - if (handle->ext_addr) - { - GNUNET_free (handle->ext_addr); - handle->ext_addr = NULL; - } - - /* Try IPv4 and IPv6 as we don't know what's the format */ -#ifndef MINGW - if (inet_aton (ext_addr, &addr) != 0) -#else - addr.S_un.S_addr = inet_addr(ext_addr); - if (addr.S_un.S_addr == INADDR_NONE) -#endif - { - handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in)); - handle->ext_addr->sa_family = AF_INET; - ((struct sockaddr_in *) handle->ext_addr)->sin_addr = addr; - } - else if (inet_pton (AF_INET6, ext_addr, &addr6) != 1) - { - handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in6)); - handle->ext_addr->sa_family = AF_INET6; - ((struct sockaddr_in6 *) handle->ext_addr)->sin6_addr = addr6; - } - else - GNUNET_assert (GNUNET_YES); - -#ifdef DEBUG_UPNP - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP, - _("Found public IP address %s\n"), ext_addr); -#endif - } - - pulse_finish (handle); -} - -/** - * Check state of UPnP NAT: port redirection, external IP address. - * - * - * @param handle the handle for UPnP object - * @param is_enabled whether enable port redirection - * @param doPortCheck do port check - */ -void -GNUNET_NAT_UPNP_pulse (struct GNUNET_NAT_UPNP_Handle *handle, - int is_enabled, int doPortCheck) -{ - /* Stop if we're already waiting for an action to complete */ - if (handle->processing == GNUNET_YES) - return; - - if (is_enabled && (handle->state == UPNP_DISCOVER)) - { - handle->processing = GNUNET_YES; - UPNP_discover_ (handle->iface, handle->addr, discover_cb, - handle); - } - - if (handle->state == UPNP_IDLE) - { - if (handle->is_mapped && !is_enabled) - handle->state = UPNP_UNMAP; - } - - if (is_enabled && handle->is_mapped && doPortCheck) - { - char portStr[8]; - - GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port); - - handle->processing = GNUNET_YES; - UPNP_get_specific_port_mapping_entry_ (handle->control_url, - handle->service_type, portStr, - "TCP", check_port_mapping_cb, - handle); - } - - if (handle->state == UPNP_UNMAP) - { - char portStr[16]; - GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port); - - handle->processing = GNUNET_YES; - UPNP_delete_port_mapping_ (handle->control_url, - handle->service_type, portStr, "TCP", NULL, - delete_port_mapping_cb, handle); - } - - if (handle->state == UPNP_IDLE) - { - if (is_enabled && !handle->is_mapped) - handle->state = UPNP_MAP; - } - - if (handle->state == UPNP_MAP) - { - if (!handle->control_url) - handle->is_mapped = 0; - else - { - char portStr[16]; - char desc[64]; - GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port); - GNUNET_snprintf (desc, sizeof (desc), "GNUnet at %d", handle->port); - - handle->processing = GNUNET_YES; - UPNP_add_port_mapping_ (handle->control_url, - handle->service_type, - portStr, portStr, GNUNET_a2s (handle->addr, - handle->addrlen), - desc, "TCP", NULL, add_port_mapping_cb, - handle); - } - } - - if (handle->state != UPNP_DISCOVER) - { - handle->processing = GNUNET_YES; - UPNP_get_external_ip_address_ (handle->control_url, - handle->service_type, - get_ip_address_cb, handle); - } -} diff --git a/src/nat/upnp.h b/src/nat/upnp.h deleted file mode 100644 index dc6ba0d45..000000000 --- a/src/nat/upnp.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2009, 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 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., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. -*/ - -/** - * @file nat/upnp.h - * @brief UPnP support for the NAT library - * - * @author Milan Bouchet-Valat - */ - -#ifndef UPNP_H -#define UPNP_H 1 - -#include "platform.h" - -struct GNUNET_NAT_UPNP_Handle; - -typedef void (*GNUNET_NAT_UPNP_pulse_cb) (int status, - struct sockaddr * ext_addr, - void *cls); - -struct GNUNET_NAT_UPNP_Handle *GNUNET_NAT_UPNP_init (const struct sockaddr - *addr, socklen_t addrlen, - unsigned short port, - GNUNET_NAT_UPNP_pulse_cb - pulse_cb, - void *pulse_cls); - -void GNUNET_NAT_UPNP_close (struct GNUNET_NAT_UPNP_Handle *h); - -void GNUNET_NAT_UPNP_pulse (struct GNUNET_NAT_UPNP_Handle *h, - int is_enabled, int do_port_check); - -#endif -/* UPNP_H */ diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am index 5919bf2ac..e8ed46aad 100644 --- a/src/transport/Makefile.am +++ b/src/transport/Makefile.am @@ -13,6 +13,7 @@ else endif if HAVE_MHD +if HAVE_EXPERIMENTAL GN_LIBMHD = -lmicrohttpd HTTP_PLUGIN_LA = libgnunet_plugin_transport_http.la HTTP_PLUGIN_TEST = test_plugin_transport_http @@ -27,6 +28,7 @@ if HAVE_MHD HTTPS_QUOTA_TEST = test_quota_compliance_https \ test_quota_compliance_https_asymmetric_recv_constant endif +endif if USE_COVERAGE AM_CFLAGS = --coverage -O0 diff --git a/src/transport/gnunet-service-transport.c b/src/transport/gnunet-service-transport.c index 0032b20c7..2010c8d51 100644 --- a/src/transport/gnunet-service-transport.c +++ b/src/transport/gnunet-service-transport.c @@ -44,9 +44,9 @@ #define DEBUG_BLACKLIST GNUNET_NO -#define DEBUG_PING_PONG GNUNET_NO +#define DEBUG_PING_PONG GNUNET_YES -#define DEBUG_TRANSPORT_HELLO GNUNET_NO +#define DEBUG_TRANSPORT_HELLO GNUNET_YES #define DEBUG_ATS GNUNET_NO @@ -276,12 +276,6 @@ struct OwnAddressList */ struct OwnAddressList *next; - /** - * How long until we actually auto-expire this address (unless it is - * re-confirmed by the transport)? - */ - struct GNUNET_TIME_Absolute expires; - /** * How long until the current signature expires? (ZERO if the * signature was never created). @@ -2330,100 +2324,6 @@ refresh_hello () } -/** - * Task used to clean up expired addresses for a plugin. - * - * @param cls closure - * @param tc context - */ -static void -expire_address_task (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc); - - -/** - * Update the list of addresses for this plugin, - * expiring those that are past their expiration date. - * - * @param plugin addresses of which plugin should be recomputed? - * @param fresh set to GNUNET_YES if a new address was added - * and we need to regenerate the HELLO even if nobody - * expired - */ -static void -update_addresses (struct TransportPlugin *plugin, - int fresh) -{ - static struct GNUNET_TIME_Absolute last_update; - struct GNUNET_TIME_Relative min_remaining; - struct GNUNET_TIME_Relative remaining; - struct GNUNET_TIME_Absolute now; - struct OwnAddressList *pos; - struct OwnAddressList *prev; - struct OwnAddressList *next; - int expired; - - if (plugin->address_update_task != GNUNET_SCHEDULER_NO_TASK) - GNUNET_SCHEDULER_cancel (plugin->address_update_task); - plugin->address_update_task = GNUNET_SCHEDULER_NO_TASK; - now = GNUNET_TIME_absolute_get (); - min_remaining = GNUNET_TIME_UNIT_FOREVER_REL; - expired = (GNUNET_TIME_absolute_get_duration (last_update).rel_value > (HELLO_ADDRESS_EXPIRATION.rel_value / 4)); - prev = NULL; - pos = plugin->addresses; - while (pos != NULL) - { - next = pos->next; - if (pos->expires.abs_value < now.abs_value) - { - expired = GNUNET_YES; - if (prev == NULL) - plugin->addresses = pos->next; - else - prev->next = pos->next; - GNUNET_free (pos); - } - else - { - remaining = GNUNET_TIME_absolute_get_remaining (pos->expires); - if (remaining.rel_value < min_remaining.rel_value) - min_remaining = remaining; - prev = pos; - } - pos = next; - } - - if (expired || fresh) - { - last_update = now; - refresh_hello (); - } - min_remaining = GNUNET_TIME_relative_min (min_remaining, - GNUNET_TIME_relative_divide (HELLO_ADDRESS_EXPIRATION, - 2)); - plugin->address_update_task - = GNUNET_SCHEDULER_add_delayed (min_remaining, - &expire_address_task, plugin); -} - - -/** - * Task used to clean up expired addresses for a plugin. - * - * @param cls closure - * @param tc context - */ -static void -expire_address_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct TransportPlugin *plugin = cls; - - plugin->address_update_task = GNUNET_SCHEDULER_NO_TASK; - if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - update_addresses (plugin, GNUNET_NO); -} - - /** * Iterator over hash map entries that NULLs the session of validation * entries that match the given session. @@ -2508,7 +2408,7 @@ try_fast_reconnect (struct TransportPlugin *p, gettext_noop ("# disconnects due to try_fast_reconnect"), 1, GNUNET_NO); -#if DISCONNECT +#if DISCONNECT || 1 disconnect_neighbour (nl, GNUNET_YES); #endif } @@ -2609,8 +2509,8 @@ plugin_env_session_end (void *cls, gettext_noop ("# try_fast_reconnect thanks to plugin_env_session_end"), 1, GNUNET_NO); - if (GNUNET_YES == pos->connected) - try_fast_reconnect (p, nl); + if (GNUNET_YES == pos->connected) + try_fast_reconnect (p, nl); } else { @@ -2696,45 +2596,51 @@ plugin_env_session_end (void *cls, * provided by the plugin can be reached. * * @param cls closure - * @param name name of the transport that generated the address + * @param add_remove YES to add, NO to remove the address * @param addr one of the addresses of the host, NULL for the last address * the specific address format depends on the transport * @param addrlen length of the address - * @param expires when should this address automatically expire? */ static void plugin_env_notify_address (void *cls, - const char *name, + int add_remove, const void *addr, - uint16_t addrlen, - struct GNUNET_TIME_Relative expires) + size_t addrlen) { struct TransportPlugin *p = cls; struct OwnAddressList *al; - struct GNUNET_TIME_Absolute abex; + struct OwnAddressList *prev; GNUNET_assert (addr != NULL); - abex = GNUNET_TIME_relative_to_absolute (expires); - GNUNET_assert (p == find_transport (name)); - al = p->addresses; - while (al != NULL) + if (GNUNET_NO == add_remove) { - if ( (addrlen == al->addrlen) && - (0 == memcmp (addr, &al[1], addrlen)) ) - { - al->expires = abex; - update_addresses (p, GNUNET_NO); - return; - } - al = al->next; + prev = NULL; + al = p->addresses; + while (al != NULL) + { + if ( (addrlen == al->addrlen) && + (0 == memcmp (addr, &al[1], addrlen)) ) + { + if (prev == NULL) + p->addresses = al->next; + else + prev->next = al->next; + GNUNET_free (al); + refresh_hello (); + return; + } + prev = al; + al = al->next; + } + GNUNET_break (0); + return; } al = GNUNET_malloc (sizeof (struct OwnAddressList) + addrlen); al->next = p->addresses; p->addresses = al; - al->expires = abex; al->addrlen = addrlen; memcpy (&al[1], addr, addrlen); - update_addresses (p, GNUNET_YES); + refresh_hello (); } @@ -4365,7 +4271,6 @@ check_pending_validation (void *cls, if (GNUNET_NO == n->received_pong) { n->received_pong = GNUNET_YES; - notify_clients_connect (&target, n->latency, n->distance); if (NULL != (prem = n->pre_connect_message_buffer)) { @@ -4508,6 +4413,8 @@ transmit_hello_and_ping (void *cls, abort_validation (NULL, NULL, va); return; } + if (NULL == our_hello) + refresh_hello (); hello_size = GNUNET_HELLO_size(our_hello); slen = strlen(va->transport_name) + 1; tsize = sizeof(struct TransportPingMessage) + hello_size + va->addrlen + slen; @@ -5004,8 +4911,11 @@ disconnect_neighbour (struct NeighbourList *n, int check) { #if DEBUG_TRANSPORT GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "NOT Disconnecting from `%4s', still have live addresses!\n", - GNUNET_i2s (&n->id)); + "NOT Disconnecting from `%4s', still have live address `%s'!\n", + GNUNET_i2s (&n->id), + a2s (peer_addresses->ready_list->plugin->short_name, + peer_addresses->addr, + peer_addresses->addrlen)); #endif return; /* still connected */ } @@ -5289,8 +5199,7 @@ handle_ping(void *cls, const struct GNUNET_MessageHeader *message, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating PONG signature to indicate ownership.\n"); #endif - oal->pong_sig_expires = GNUNET_TIME_absolute_min (oal->expires, - GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME)); + oal->pong_sig_expires = GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME); pong->expiration = GNUNET_TIME_absolute_hton (oal->pong_sig_expires); GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_rsa_sign (my_private_key, diff --git a/src/transport/plugin_transport_http.c b/src/transport/plugin_transport_http.c index ba58f9ea7..09b3b219f 100644 --- a/src/transport/plugin_transport_http.c +++ b/src/transport/plugin_transport_http.c @@ -821,19 +821,19 @@ process_interfaces (void *cls, t4->ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr; t4->u_port = htons (plugin->port_inbound); if (plugin->bind4_address != NULL) { - if (0 == memcmp(&plugin->bind4_address->sin_addr, &bnd_cmp, sizeof (struct in_addr))) - { - GNUNET_CONTAINER_DLL_insert(plugin->ipv4_addr_head, - plugin->ipv4_addr_tail,t4); - plugin->env->notify_address(plugin->env->cls, - PROTOCOL_PREFIX, - t4, sizeof (struct IPv4HttpAddress), - GNUNET_TIME_UNIT_FOREVER_REL); - return GNUNET_OK; - } - GNUNET_free (t4); - return GNUNET_OK; + if (0 == memcmp(&plugin->bind4_address->sin_addr, &bnd_cmp, sizeof (struct in_addr))) + { + GNUNET_CONTAINER_DLL_insert(plugin->ipv4_addr_head, + plugin->ipv4_addr_tail,t4); + plugin->env->notify_address(plugin->env->cls, + PROTOCOL_PREFIX, + t4, sizeof (struct IPv4HttpAddress), + GNUNET_TIME_UNIT_FOREVER_REL); + return GNUNET_OK; } + GNUNET_free (t4); + return GNUNET_OK; + } else { GNUNET_CONTAINER_DLL_insert (plugin->ipv4_addr_head, diff --git a/src/transport/plugin_transport_tcp.c b/src/transport/plugin_transport_tcp.c index 491c536ca..a8eee970c 100644 --- a/src/transport/plugin_transport_tcp.c +++ b/src/transport/plugin_transport_tcp.c @@ -38,15 +38,10 @@ #include "gnunet_transport_plugin.h" #include "transport.h" -#define DEBUG_TCP GNUNET_NO +#define DEBUG_TCP GNUNET_YES #define DEBUG_TCP_NAT GNUNET_YES -/** - * How long until we give up on transmitting the welcome message? - */ -#define HOSTNAME_RESOLVE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) - /** * Initial handshake message for a session. @@ -136,7 +131,7 @@ struct IPv4TcpAddress /** * Port number, in network byte order. */ - uint16_t t_port GNUNET_PACKED; + uint16_t t4_port GNUNET_PACKED; }; @@ -165,52 +160,6 @@ struct IPv6TcpAddress struct Plugin; -/** - * Local network addresses (actual IP address follows this struct). - * PORT is NOT included! - */ -struct LocalAddrList -{ - - /** - * This is a doubly linked list. - */ - struct LocalAddrList *next; - - /** - * This is a doubly linked list. - */ - struct LocalAddrList *prev; - - /** - * Link to plugin. - */ - struct Plugin *plugin; - - /** - * Handle to NAT holes we've tried to punch for this address. - */ - struct GNUNET_NAT_Handle *nat; - - /** - * Pointer to a 'struct IPv4/V6TcpAddress' describing our external IP and port - * as obtained from the NAT by automatic port mapping. - */ - void *external_nat_address; - - /** - * Number of bytes in 'external_nat_address' - */ - size_t ena_size; - - /** - * Number of bytes of the address that follow - */ - size_t size; - -}; - - /** * Information kept for each message that is yet to * be transmitted. @@ -365,24 +314,9 @@ struct Plugin struct GNUNET_CONNECTION_Handle *lsock; /** - * stdout pipe handle for the gnunet-nat-server process - */ - struct GNUNET_DISK_PipeHandle *server_stdout; - - /** - * stdout file handle (for reading) for the gnunet-nat-server process + * Our handle to the NAT module. */ - const struct GNUNET_DISK_FileHandle *server_stdout_handle; - - /** - * ID of select gnunet-nat-server stdout read task - */ - GNUNET_SCHEDULER_TaskIdentifier server_read_task; - - /** - * The process id of the server process (if behind NAT) - */ - struct GNUNET_OS_Process *server_proc; + struct GNUNET_NAT_Handle *nat; /** * List of open TCP sessions. @@ -405,49 +339,11 @@ struct Plugin */ struct GNUNET_SERVER_MessageHandler *handlers; - /** - * Handle for request of hostname resolution, non-NULL if pending. - */ - struct GNUNET_RESOLVER_RequestHandle *hostname_dns; - /** * Map of peers we have tried to contact behind a NAT */ struct GNUNET_CONTAINER_MultiHashMap *nat_wait_conns; - - /** - * The external address given to us by the user. Used for HELLOs - * and address validation. - */ - char *external_address; - - /** - * The internal address given to us by the user (or discovered). - * Used for NAT traversal (ICMP method), but not as a 'validateable' - * address in HELLOs. - */ - char *internal_address; - - /** - * Address given for us to bind to (ONLY). - */ - char *bind_address; - - /** - * use local addresses? - */ - int use_localaddresses; - - /** - * List of our IP addresses. - */ - struct LocalAddrList *lal_head; - - /** - * Tail of our IP address list. - */ - struct LocalAddrList *lal_tail; - + /** * List of active TCP probes. */ @@ -484,31 +380,6 @@ struct Plugin */ uint16_t adv_port; - /** - * Is this transport configured to be behind a NAT? - */ - int behind_nat; - - /** - * Has the NAT been punched? - */ - int nat_punched; - - /** - * Is this transport configured to allow connections to NAT'd peers? - */ - int enable_nat_client; - - /** - * Should we run the gnunet-nat-server? - */ - int enable_nat_server; - - /** - * Are we allowed to try UPnP/PMP for NAT traversal? - */ - int enable_upnp; - }; @@ -549,30 +420,34 @@ plugin_tcp_access_check (void *cls, * @param addrlen actual lenght of the address */ static void -nat_port_map_callback (void *cls, - int add_remove, - const struct sockaddr *addr, - socklen_t addrlen) +tcp_nat_port_map_callback (void *cls, + int add_remove, + const struct sockaddr *addr, + socklen_t addrlen) { - struct LocalAddrList *lal = cls; - struct Plugin *plugin = lal->plugin; - int af; + struct Plugin *plugin = cls; struct IPv4TcpAddress t4; struct IPv6TcpAddress t6; void *arg; - uint16_t args; + size_t args; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", + "NPMC called with %d for address `%s'\n", + add_remove, + GNUNET_a2s (addr, addrlen)); /* convert 'addr' to our internal format */ - af = addr->sa_family; - switch (af) + switch (addr->sa_family) { case AF_INET: + GNUNET_assert (addrlen == sizeof (struct sockaddr_in)); t4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr; - t4.t_port = ((struct sockaddr_in *) addr)->sin_port; + t4.t4_port = ((struct sockaddr_in *) addr)->sin_port; arg = &t4; args = sizeof (t4); break; case AF_INET6: + GNUNET_assert (addrlen == sizeof (struct sockaddr_in6)); memcpy (&t6.ipv6_addr, &((struct sockaddr_in6 *) addr)->sin6_addr, sizeof (struct in6_addr)); @@ -583,154 +458,11 @@ nat_port_map_callback (void *cls, default: GNUNET_break (0); return; - } - - /* modify our published address list */ - if (GNUNET_YES == add_remove) - { - plugin->env->notify_address (plugin->env->cls, - "tcp", - arg, args, GNUNET_TIME_UNIT_FOREVER_REL); - GNUNET_free_non_null (lal->external_nat_address); - lal->external_nat_address = GNUNET_memdup (arg, args); - lal->ena_size = args; - } - else - { - plugin->env->notify_address (plugin->env->cls, - "tcp", - arg, args, GNUNET_TIME_UNIT_ZERO); - GNUNET_free_non_null (lal->external_nat_address); - lal->ena_size = 0; - } -} - - -/** - * Add the given address to the list of 'local' addresses, thereby - * making it a 'legal' address for this peer to have. - * - * @param plugin the plugin - * @param arg the address, either an IPv4 or an IPv6 IP address - * @param arg_size number of bytes in arg - */ -static void -add_to_address_list (struct Plugin *plugin, - const void *arg, - size_t arg_size) -{ - struct LocalAddrList *lal; - struct sockaddr_in v4; - struct sockaddr_in6 v6; - const struct sockaddr *sa; - socklen_t salen; - - lal = plugin->lal_head; - while (NULL != lal) - { - if ( (lal->size == arg_size) && - (0 == memcmp (&lal[1], arg, arg_size)) ) - return; - lal = lal->next; - } - lal = GNUNET_malloc (sizeof (struct LocalAddrList) + arg_size); - lal->plugin = plugin; - lal->size = arg_size; - memcpy (&lal[1], arg, arg_size); - GNUNET_CONTAINER_DLL_insert (plugin->lal_head, - plugin->lal_tail, - lal); - if (plugin->open_port == 0) - return; /* we're not listening at all... */ - if (arg_size == sizeof (struct in_addr)) - { - memset (&v4, 0, sizeof (v4)); - v4.sin_family = AF_INET; - v4.sin_port = htons (plugin->open_port); - memcpy (&v4.sin_addr, arg, arg_size); -#if HAVE_SOCKADDR_IN_SIN_LEN - v4.sin_len = sizeof (struct sockaddr_in); -#endif - sa = (const struct sockaddr*) &v4; - salen = sizeof (v4); - } - else if (arg_size == sizeof (struct in6_addr)) - { - memset (&v6, 0, sizeof (v6)); - v6.sin6_family = AF_INET6; - v6.sin6_port = htons (plugin->open_port); - memcpy (&v6.sin6_addr, arg, arg_size); -#if HAVE_SOCKADDR_IN_SIN_LEN - v6.sin6_len = sizeof (struct sockaddr_in6); -#endif - sa = (const struct sockaddr*) &v6; - salen = sizeof (v6); - } - else - { - GNUNET_break (0); - return; } - if ( (plugin->behind_nat == GNUNET_YES) && - (plugin->enable_upnp == GNUNET_YES) ) - lal->nat = GNUNET_NAT_register (plugin->env->cfg, - sa, salen, - &nat_port_map_callback, - lal); -} - - -/** - * Check if the given address is in the list of 'local' addresses. - * - * @param plugin the plugin - * @param arg the address, either an IPv4 or an IPv6 IP address - * @param arg_size number of bytes in arg - * @return GNUNET_OK if this is one of our IPs, GNUNET_SYSERR if not - */ -static int -check_local_addr (struct Plugin *plugin, - const void *arg, - size_t arg_size) -{ - struct LocalAddrList *lal; - - lal = plugin->lal_head; - while (NULL != lal) - { - if ( (lal->size == arg_size) && - (0 == memcmp (&lal[1], arg, arg_size)) ) - return GNUNET_OK; - lal = lal->next; - } - return GNUNET_SYSERR; -} - - -/** - * Check if the given address is in the list of 'mapped' addresses. - * - * @param plugin the plugin - * @param arg the address, either a 'struct IPv4TcpAddress' or a 'struct IPv6TcpAddress' - * @param arg_size number of bytes in arg - * @return GNUNET_OK if this is one of our IPs, GNUNET_SYSERR if not - */ -static int -check_mapped_addr (struct Plugin *plugin, - const void *arg, - size_t arg_size) -{ - struct LocalAddrList *lal; - - lal = plugin->lal_head; - while (NULL != lal) - { - if ( (lal->ena_size == arg_size) && - (0 == memcmp (lal->external_nat_address, arg, arg_size)) ) - return GNUNET_OK; - lal = lal->next; - } - return GNUNET_SYSERR; + /* modify our published address list */ + plugin->env->notify_address (plugin->env->cls, + add_remove, + arg, args); } @@ -772,7 +504,7 @@ tcp_address_to_string (void *cls, { t4 = addr; af = AF_INET; - port = ntohs (t4->t_port); + port = ntohs (t4->t4_port); memcpy (&a4, &t4->ipv4_addr, sizeof (a4)); sb = &a4; } @@ -1186,64 +918,6 @@ select_better_session (struct Session *s1, } -/** - * We learned about a peer (possibly behind NAT) so run the - * gnunet-nat-client to send dummy ICMP responses. - * - * @param plugin the plugin for this transport - * @param sa the address of the peer (IPv4-only) - */ -static void -run_gnunet_nat_client (struct Plugin *plugin, - const struct sockaddr_in *sa) -{ - char inet4[INET_ADDRSTRLEN]; - char port_as_string[6]; - struct GNUNET_OS_Process *proc; - - if (plugin->internal_address == NULL) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, - "tcp", - _("Internal IP address not known, cannot use ICMP NAT traversal method\n")); - return; - } - GNUNET_assert (sa->sin_family == AF_INET); - if (NULL == inet_ntop (AF_INET, - &sa->sin_addr, - inet4, INET_ADDRSTRLEN)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop"); - return; - } - GNUNET_snprintf(port_as_string, - sizeof (port_as_string), - "%d", - plugin->adv_port); -#if DEBUG_TCP_NAT - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - "tcp", - _("Running gnunet-nat-client %s %s %u\n"), - plugin->internal_address, - inet4, - (unsigned int) plugin->adv_port); -#endif - proc = GNUNET_OS_start_process (NULL, - NULL, - "gnunet-nat-client", - "gnunet-nat-client", - plugin->internal_address, - inet4, - port_as_string, - NULL); - if (NULL == proc) - return; - /* we know that the gnunet-nat-client will terminate virtually - instantly */ - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_close (proc); -} - /** * Function that can be used by the transport service to transmit @@ -1400,8 +1074,8 @@ tcp_plugin_send (void *cls, a4.sin_len = sizeof (a4); #endif a4.sin_family = AF_INET; - a4.sin_port = t4->t_port; - if (t4->t_port == 0) + a4.sin_port = t4->t4_port; + if (t4->t4_port == 0) is_natd = GNUNET_YES; a4.sin_addr.s_addr = t4->ipv4_addr; sb = &a4; @@ -1422,8 +1096,8 @@ tcp_plugin_send (void *cls, if (0 == plugin->max_connections) return -1; /* saturated */ - if ( (plugin->enable_nat_client == GNUNET_YES) && - (is_natd == GNUNET_YES) && + if ( (is_natd == GNUNET_YES) && + (NULL != plugin->nat) && (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(plugin->nat_wait_conns, &target->hashPubKey)) ) { @@ -1466,11 +1140,10 @@ tcp_plugin_send (void *cls, GNUNET_i2s (target), GNUNET_a2s (sb, sbs)); #endif - run_gnunet_nat_client (plugin, &a4); + GNUNET_NAT_run_client (plugin->nat, &a4); return 0; } - if ( (plugin->enable_nat_client == GNUNET_YES) && - (is_natd == GNUNET_YES) && + if ( (is_natd == GNUNET_YES) && (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(plugin->nat_wait_conns, &target->hashPubKey)) ) { @@ -1697,9 +1370,9 @@ tcp_plugin_address_pretty_printer (void *cls, t4 = addr; memset (&a4, 0, sizeof (a4)); a4.sin_family = AF_INET; - a4.sin_port = t4->t_port; + a4.sin_port = t4->t4_port; a4.sin_addr.s_addr = t4->ipv4_addr; - port = ntohs (t4->t_port); + port = ntohs (t4->t4_port); sb = &a4; sbs = sizeof (a4); } @@ -1773,14 +1446,12 @@ tcp_plugin_check_address (void *cls, if (addrlen == sizeof (struct IPv4TcpAddress)) { v4 = (struct IPv4TcpAddress *) addr; - if (GNUNET_OK == - check_mapped_addr (plugin, v4, sizeof (struct IPv4TcpAddress))) - return GNUNET_OK; if (GNUNET_OK != - check_port (plugin, ntohs (v4->t_port))) + check_port (plugin, ntohs (v4->t4_port))) return GNUNET_SYSERR; if (GNUNET_OK != - check_local_addr (plugin, &v4->ipv4_addr, sizeof (struct in_addr))) + GNUNET_NAT_test_address (plugin->nat, + &v4->ipv4_addr, sizeof (struct in_addr))) return GNUNET_SYSERR; } else @@ -1791,14 +1462,12 @@ tcp_plugin_check_address (void *cls, GNUNET_break_op (0); return GNUNET_SYSERR; } - if (GNUNET_OK == - check_mapped_addr (plugin, v6, sizeof (struct IPv6TcpAddress))) - return GNUNET_OK; if (GNUNET_OK != check_port (plugin, ntohs (v6->t6_port))) return GNUNET_SYSERR; if (GNUNET_OK != - check_local_addr (plugin, &v6->ipv6_addr, sizeof (struct in6_addr))) + GNUNET_NAT_test_address (plugin->nat, + &v6->ipv6_addr, sizeof (struct in6_addr))) return GNUNET_SYSERR; } return GNUNET_OK; @@ -1903,7 +1572,7 @@ handle_tcp_nat_probe (void *cls, case AF_INET: s4 = vaddr; t4 = GNUNET_malloc (sizeof (struct IPv4TcpAddress)); - t4->t_port = s4->sin_port; + t4->t4_port = s4->sin_port; t4->ipv4_addr = s4->sin_addr.s_addr; session->connect_addr = t4; session->connect_alen = sizeof (struct IPv4TcpAddress); @@ -2016,7 +1685,7 @@ handle_tcp_welcome (void *cls, { s4 = vaddr; t4 = GNUNET_malloc (sizeof (struct IPv4TcpAddress)); - t4->t_port = s4->sin_port; + t4->t4_port = s4->sin_port; t4->ipv4_addr = s4->sin_addr.s_addr; session->connect_addr = t4; session->connect_alen = sizeof (struct IPv4TcpAddress); @@ -2215,228 +1884,6 @@ disconnect_notify (void *cls, } -static int check_localaddress (const struct sockaddr *addr, socklen_t addrlen) -{ - uint32_t res = 0; - int local = GNUNET_NO; - int af = addr->sa_family; - switch (af) - { - case AF_INET: - { - uint32_t netmask = 0x7F000000; - uint32_t address = ntohl (((struct sockaddr_in *) addr)->sin_addr.s_addr); - res = (address >> 24) ^ (netmask >> 24); - if (res != 0) - local = GNUNET_NO; - else - local = GNUNET_YES; -#if DEBUG_TCP - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Checking IPv4 address `%s': %s\n", GNUNET_a2s (addr, addrlen), (local==GNUNET_YES) ? "local" : "global"); -#endif - break; - } - case AF_INET6: - { - if (IN6_IS_ADDR_LOOPBACK (&((struct sockaddr_in6 *) addr)->sin6_addr) || - IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr)) - local = GNUNET_YES; - else - local = GNUNET_NO; -#if DEBUG_TCP - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Checking IPv6 address `%s' : %s\n", GNUNET_a2s (addr, addrlen), (local==GNUNET_YES) ? "local" : "global"); -#endif - break; - } - } - return local; -} - -/** - * Add the IP of our network interface to the list of - * our internal IP addresses. - * - * @param cls the 'struct Plugin*' - * @param name name of the interface - * @param isDefault do we think this may be our default interface - * @param addr address of the interface - * @param addrlen number of bytes in addr - * @return GNUNET_OK to continue iterating - */ -static int -process_interfaces (void *cls, - const char *name, - int isDefault, - const struct sockaddr *addr, socklen_t addrlen) -{ - struct Plugin *plugin = cls; - int af; - struct IPv4TcpAddress t4; - struct IPv6TcpAddress t6; - struct IPv4TcpAddress t4_nat; - struct IPv6TcpAddress t6_nat; - void *arg; - uint16_t args; - void *arg_nat; - char buf[INET6_ADDRSTRLEN]; - - af = addr->sa_family; - arg_nat = NULL; - - if (plugin->use_localaddresses == GNUNET_NO) - { - if (GNUNET_YES == check_localaddress (addr, addrlen)) - { -#if DEBUG_TCP - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, - "tcp", - "Not notifying transport of address `%s' (local address)\n", - GNUNET_a2s (addr, addrlen)); -#endif - return GNUNET_OK; - } - } - - switch (af) - { - case AF_INET: - t4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr; - GNUNET_assert (NULL != inet_ntop(AF_INET, - &t4.ipv4_addr, - buf, - sizeof (buf))); - if ( (plugin->bind_address != NULL) && - (0 != strcmp(buf, plugin->bind_address)) ) - { -#if DEBUG_TCP - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - "tcp", - "Not notifying transport of address `%s' (does not match bind address)\n", - GNUNET_a2s (addr, addrlen)); -#endif - return GNUNET_OK; - } - if ( (plugin->internal_address == NULL) && - (isDefault) ) - plugin->internal_address = GNUNET_strdup (buf); - add_to_address_list (plugin, &t4.ipv4_addr, sizeof (struct in_addr)); - if (plugin->behind_nat == GNUNET_YES) - { - /* Also advertise as NAT (with port 0) */ - t4_nat.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr; - t4_nat.t_port = htons(0); - arg_nat = &t4_nat; - } - t4.t_port = htons (plugin->adv_port); - arg = &t4; - args = sizeof (t4); - break; - case AF_INET6: - if ( (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr)) || - (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno(plugin->env->cfg, - "nat", - "DISABLEV6")) ) - { - /* skip link local addresses */ - return GNUNET_OK; - } - memcpy (&t6.ipv6_addr, - &((struct sockaddr_in6 *) addr)->sin6_addr, - sizeof (struct in6_addr)); - - /* check bind address */ - GNUNET_assert (NULL != inet_ntop(AF_INET6, - &t6.ipv6_addr, - buf, - sizeof (buf))); - - if ( (plugin->bind_address != NULL) && - (0 != strcmp(buf, plugin->bind_address)) ) - { -#if DEBUG_TCP - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - "tcp", - "Not notifying transport of address `%s' (does not match bind address)\n", - GNUNET_a2s (addr, addrlen)); -#endif - return GNUNET_OK; - } - - add_to_address_list (plugin, - &t6.ipv6_addr, - sizeof (struct in6_addr)); - if (plugin->behind_nat == GNUNET_YES) - { - /* Also advertise as NAT (with port 0) */ - memcpy (&t6_nat.ipv6_addr, - &((struct sockaddr_in6 *) addr)->sin6_addr, - sizeof (struct in6_addr)); - t6_nat.t6_port = htons(0); - arg_nat = &t6; - } - t6.t6_port = htons (plugin->adv_port); - arg = &t6; - args = sizeof (t6); - break; - default: - GNUNET_break (0); - return GNUNET_OK; - } - if (plugin->adv_port != 0) - { -#if DEBUG_TCP - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - "tcp", - "Found address `%s' (%s) len %d\n", - GNUNET_a2s (addr, addrlen), name, args); -#endif - plugin->env->notify_address (plugin->env->cls, - "tcp", - arg, args, GNUNET_TIME_UNIT_FOREVER_REL); - } - - if (arg_nat != NULL) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - "tcp", - _("Found address `%s' (%s) len %d\n"), - GNUNET_a2s (addr, addrlen), name, args); - plugin->env->notify_address (plugin->env->cls, - "tcp", - arg_nat, args, GNUNET_TIME_UNIT_FOREVER_REL); - } - - return GNUNET_OK; -} - - -/** - * Function called by the resolver for each address obtained from DNS - * for our own hostname. Add the addresses to the list of our - * external IP addresses. - * - * @param cls closure - * @param addr one of the addresses of the host, NULL for the last address - * @param addrlen length of the address - */ -static void -process_hostname_ips (void *cls, - const struct sockaddr *addr, socklen_t addrlen) -{ - struct Plugin *plugin = cls; - - if (addr == NULL) - { - plugin->hostname_dns = NULL; - return; - } - /* FIXME: Can we figure out our external address here so it doesn't need to be user specified? */ - process_interfaces (plugin, "", GNUNET_YES, addr, addrlen); -} - - /** * We can now send a probe message, copy into buffer to really send. * @@ -2475,112 +1922,36 @@ notify_send_probe (void *cls, /** - * We have been notified that gnunet-nat-server has written something to stdout. - * Handle the output, then reschedule this function to be called again once - * more is available. + * Function called by the NAT subsystem suggesting another peer wants + * to connect to us via connection reversal. Try to connect back to the + * given IP. * - * @param cls the plugin handle - * @param tc the scheduling context + * @param cls closure + * @param addr address to try + * @param addrlen number of bytes in addr */ static void -tcp_plugin_server_read (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +try_connection_reversal (void *cls, + const struct sockaddr *addr, + socklen_t addrlen) { struct Plugin *plugin = cls; - char mybuf[40]; - ssize_t bytes; - size_t i; - int port; - const char *port_start; - struct sockaddr_in sin_addr; - struct TCPProbeContext *tcp_probe_ctx; struct GNUNET_CONNECTION_Handle *sock; - - if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) - return; - memset (mybuf, 0, sizeof(mybuf)); - bytes = GNUNET_DISK_file_read(plugin->server_stdout_handle, - mybuf, - sizeof(mybuf)); - if (bytes < 1) - { -#if DEBUG_TCP_NAT - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - "tcp", - "Finished reading from server stdout with code: %d\n", - bytes); -#endif - /* FIXME: consider process_wait here? */ - return; - } - - port_start = NULL; - for (i = 0; i < sizeof(mybuf); i++) - { - if (mybuf[i] == '\n') - { - mybuf[i] = '\0'; - break; - } - if ( (mybuf[i] == ':') && (i + 1 < sizeof(mybuf)) ) - { - mybuf[i] = '\0'; - port_start = &mybuf[i + 1]; - } - } - - /* construct socket address of sender */ - memset (&sin_addr, 0, sizeof (sin_addr)); - sin_addr.sin_family = AF_INET; -#if HAVE_SOCKADDR_IN_SIN_LEN - sin_addr.sin_len = sizeof (sin_addr); -#endif - if ( (NULL == port_start) || - (1 != sscanf (port_start, "%d", &port)) || - (-1 == inet_pton(AF_INET, mybuf, &sin_addr.sin_addr)) ) - { - /* should we restart gnunet-nat-server? */ - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, - "tcp", - _("gnunet-nat-server generated malformed address `%s'\n"), - mybuf); - plugin->server_read_task - = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - plugin->server_stdout_handle, - &tcp_plugin_server_read, - plugin); - return; - } - sin_addr.sin_port = htons((uint16_t) port); -#if DEBUG_TCP_NAT - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - "tcp", - "gnunet-nat-server read: %s:%d\n", - mybuf, port); -#endif + struct TCPProbeContext *tcp_probe_ctx; /** * We have received an ICMP response, ostensibly from a peer * that wants to connect to us! Send a message to establish a connection. */ sock = GNUNET_CONNECTION_create_from_sockaddr (AF_INET, - (const struct sockaddr *)&sin_addr, - sizeof (sin_addr)); + addr, + addrlen); if (sock == NULL) { /* failed for some odd reason (out of sockets?); ignore attempt */ - plugin->server_read_task = - GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - plugin->server_stdout_handle, - &tcp_plugin_server_read, - plugin); return; } - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - "Sending TCP probe message to `%s:%u'!\n", - mybuf, - (unsigned int) port); /* FIXME: do we need to track these probe context objects so that we can clean them up on plugin unload? */ tcp_probe_ctx @@ -2603,251 +1974,6 @@ tcp_plugin_server_read (void *cls, GNUNET_TIME_UNIT_FOREVER_REL, ¬ify_send_probe, tcp_probe_ctx); - plugin->server_read_task = - GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - plugin->server_stdout_handle, - &tcp_plugin_server_read, - plugin); -} - - -/** - * Start the gnunet-nat-server process for users behind NAT. - * - * @param plugin the transport plugin - * @return GNUNET_YES if process was started, GNUNET_SYSERR on error - */ -static int -tcp_transport_start_nat_server (struct Plugin *plugin) -{ - if (plugin->internal_address == NULL) - return GNUNET_SYSERR; - plugin->server_stdout = GNUNET_DISK_pipe (GNUNET_YES, - GNUNET_NO, - GNUNET_YES); - if (plugin->server_stdout == NULL) - return GNUNET_SYSERR; -#if DEBUG_TCP_NAT - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - "tcp" - "Starting %s %s\n", "gnunet-nat-server", plugin->internal_address); -#endif - /* Start the server process */ - plugin->server_proc = GNUNET_OS_start_process (NULL, - plugin->server_stdout, - "gnunet-nat-server", - "gnunet-nat-server", - plugin->internal_address, - NULL); - if (plugin->server_proc == NULL) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, - "tcp", - _("Failed to start %s\n"), - "gnunet-nat-server"); - GNUNET_DISK_pipe_close (plugin->server_stdout); - plugin->server_stdout = NULL; - return GNUNET_SYSERR; - } - /* Close the write end of the read pipe */ - GNUNET_DISK_pipe_close_end(plugin->server_stdout, - GNUNET_DISK_PIPE_END_WRITE); - plugin->server_stdout_handle - = GNUNET_DISK_pipe_handle (plugin->server_stdout, - GNUNET_DISK_PIPE_END_READ); - plugin->server_read_task - = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - plugin->server_stdout_handle, - &tcp_plugin_server_read, - plugin); - return GNUNET_YES; -} - - -/** - * Return the actual path to a file found in the current - * PATH environment variable. - * - * @param binary the name of the file to find - * @return path to binary, NULL if not found - */ -static char * -get_path_from_PATH (const char *binary) -{ - char *path; - char *pos; - char *end; - char *buf; - const char *p; - - p = getenv ("PATH"); - if (p == NULL) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, - "tcp", - _("PATH environment variable is unset.\n")); - return NULL; - } - path = GNUNET_strdup (p); /* because we write on it */ - buf = GNUNET_malloc (strlen (path) + 20); - pos = path; - - while (NULL != (end = strchr (pos, PATH_SEPARATOR))) - { - *end = '\0'; - sprintf (buf, "%s/%s", pos, binary); - if (GNUNET_DISK_file_test (buf) == GNUNET_YES) - { - GNUNET_free (path); - return buf; - } - pos = end + 1; - } - sprintf (buf, "%s/%s", pos, binary); - if (GNUNET_DISK_file_test (buf) == GNUNET_YES) - { - GNUNET_free (path); - return buf; - } - GNUNET_free (buf); - GNUNET_free (path); - return NULL; -} - - -/** - * Check whether the suid bit is set on a file. - * Attempts to find the file using the current - * PATH environment variable as a search path. - * - * @param binary the name of the file to check - * @return GNUNET_YES if the file is SUID, - * GNUNET_NO if not, - * GNUNET_SYSERR on error - */ -static int -check_gnunet_nat_binary (const char *binary) -{ - struct stat statbuf; - char *p; -#ifdef MINGW - SOCKET rawsock; - char *binaryexe; - - GNUNET_asprintf (&binaryexe, "%s.exe", binary); - p = get_path_from_PATH (binaryexe); - free (binaryexe); -#else - p = get_path_from_PATH (binary); -#endif - if (p == NULL) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, - "tcp", - _("Could not find binary `%s' in PATH!\n"), - binary); - return GNUNET_NO; - } - if (0 != STAT (p, &statbuf)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("stat (%s) failed: %s\n"), - p, - STRERROR (errno)); - GNUNET_free (p); - return GNUNET_SYSERR; - } - GNUNET_free (p); -#ifndef MINGW - if ( (0 != (statbuf.st_mode & S_ISUID)) && - (statbuf.st_uid == 0) ) - return GNUNET_YES; - return GNUNET_NO; -#else - rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); - if (INVALID_SOCKET == rawsock) - { - DWORD err = GetLastError (); - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, - "tcp", - "socket (AF_INET, SOCK_RAW, IPPROTO_ICMP) failed! GLE = %d\n", err); - return GNUNET_NO; /* not running as administrator */ - } - closesocket (rawsock); - return GNUNET_YES; -#endif -} - - -/** - * Our (external) hostname was resolved. - * - * @param cls the 'struct Plugin' - * @param addr NULL on error, otherwise result of DNS lookup - * @param addrlen number of bytes in addr - */ -static void -process_external_ip (void *cls, - const struct sockaddr *addr, - socklen_t addrlen) -{ - struct Plugin *plugin = cls; - const struct sockaddr_in *s; - struct IPv4TcpAddress t4; - char buf[INET_ADDRSTRLEN]; - - plugin->ext_dns = NULL; - if (addr == NULL) - return; - GNUNET_assert (addrlen == sizeof (struct sockaddr_in)); - s = (const struct sockaddr_in *) addr; - t4.ipv4_addr = s->sin_addr.s_addr; - if ( (plugin->behind_nat == GNUNET_YES) && - (plugin->enable_nat_server == GNUNET_YES) ) - { - t4.t_port = htons(0); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - "tcp", - "Notifying transport of address %s:%d\n", - plugin->external_address, - 0); - } - else - { - t4.t_port = htons(plugin->adv_port); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - "tcp", - "Notifying transport of address %s:%d\n", - plugin->external_address, - (int) plugin->adv_port); - } - - if ((plugin->bind_address != NULL) && (plugin->behind_nat == GNUNET_NO)) - { - GNUNET_assert (NULL != inet_ntop(AF_INET, - &t4.ipv4_addr, - buf, - sizeof (buf))); - if (0 != strcmp (plugin->bind_address, buf)) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, - "tcp", - "NAT is not enabled and specific bind address `%s' differs from external address `%s'! Not notifying about external address `%s'\n", - plugin->bind_address, - plugin->external_address, - plugin->external_address); - return; - } - } - - add_to_address_list (plugin, - &t4.ipv4_addr, - sizeof (struct in_addr)); - - plugin->env->notify_address (plugin->env->cls, - "tcp", - &t4, sizeof(t4), - GNUNET_TIME_UNIT_FOREVER_REL); } @@ -2875,146 +2001,16 @@ libgnunet_plugin_transport_tcp_init (void *cls) unsigned long long bport; unsigned long long max_connections; unsigned int i; - int behind_nat; - int nat_punched; - int enable_nat_client; - int enable_nat_server; - int enable_upnp; - int use_localaddresses; - char *internal_address; - char *external_address; - char *bind_address; - struct sockaddr_in in_addr; struct GNUNET_TIME_Relative idle_timeout; + int ret; + struct sockaddr **addrs; + socklen_t *addrlens; - behind_nat = GNUNET_CONFIGURATION_get_value_yesno (env->cfg, - "nat", - "BEHIND_NAT"); - nat_punched = GNUNET_CONFIGURATION_get_value_yesno (env->cfg, - "nat", - "NAT_PUNCHED"); - enable_nat_client = GNUNET_CONFIGURATION_get_value_yesno (env->cfg, - "nat", - "ENABLE_NAT_CLIENT"); - enable_nat_server = GNUNET_CONFIGURATION_get_value_yesno (env->cfg, - "nat", - "ENABLE_NAT_SERVER"); - enable_upnp = GNUNET_CONFIGURATION_get_value_yesno (env->cfg, - "nat", - "ENABLE_UPNP"); - - if ( (GNUNET_YES == enable_nat_server) && - (GNUNET_YES != check_gnunet_nat_binary("gnunet-nat-server")) ) - { - enable_nat_server = GNUNET_NO; - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Configuration requires `%s', but binary is not installed properly (SUID bit not set). Option disabled.\n"), - "gnunet-nat-server"); - } - - if ( (GNUNET_YES == enable_nat_client) && - (GNUNET_YES != check_gnunet_nat_binary("gnunet-nat-client")) ) - { - enable_nat_client = GNUNET_NO; - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Configuration requires `%s', but binary is not installed properly (SUID bit not set). Option disabled.\n"), - "gnunet-nat-client"); - } - - external_address = NULL; - if (GNUNET_OK == - GNUNET_CONFIGURATION_have_value (env->cfg, - "nat", - "EXTERNAL_ADDRESS")) - { - (void) GNUNET_CONFIGURATION_get_value_string (env->cfg, - "nat", - "EXTERNAL_ADDRESS", - &external_address); - } - - if ( (external_address != NULL) && - (inet_pton(AF_INET, external_address, &in_addr.sin_addr) != 1) ) - { - - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, - "tcp", - _("Malformed %s `%s' given in configuration!\n"), - "EXTERNAL_ADDRESS", - external_address); - return NULL; - } - if ( (external_address == NULL) && - (nat_punched == GNUNET_YES) ) - { - nat_punched = GNUNET_NO; - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Configuration says NAT was punched, but `%s' is not given. Option ignored.\n"), - "EXTERNAL_ADDRESS"); - } - - if (GNUNET_YES == nat_punched) - { - enable_nat_server = GNUNET_NO; - enable_upnp = GNUNET_NO; - } - - bind_address = NULL; - if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (env->cfg, - "nat", - "BINDTO", - &bind_address)) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, - "tcp", - _("Binding TCP plugin to specific address: `%s'\n"), - bind_address); - } - - internal_address = NULL; - if (GNUNET_OK == - GNUNET_CONFIGURATION_have_value (env->cfg, - "nat", - "INTERNAL_ADDRESS")) - { - (void) GNUNET_CONFIGURATION_get_value_string (env->cfg, - "nat", - "INTERNAL_ADDRESS", - &internal_address); - } - - if ( (internal_address != NULL) && - (inet_pton(AF_INET, internal_address, &in_addr.sin_addr) != 1) ) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, - "tcp", - _("Malformed %s `%s' given in configuration!\n"), - "INTERNAL_ADDRESS", - internal_address); - GNUNET_free_non_null(internal_address); - GNUNET_free_non_null(external_address); - return NULL; - } - - if ((bind_address != NULL) && (internal_address != NULL)) - { - if (0 != strcmp(internal_address, bind_address )) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, - "tcp", - "Specific bind address `%s' and internal address `%s' must not differ, forcing internal address to bind address!\n", - bind_address, internal_address); - GNUNET_free (internal_address); - internal_address = bind_address; - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, - "tcp","New internal address `%s'\n", internal_address); - } - } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (env->cfg, - "transport-tcp", - "MAX_CONNECTIONS", - &max_connections)) + "transport-tcp", + "MAX_CONNECTIONS", + &max_connections)) max_connections = 128; aport = 0; @@ -3035,25 +2031,15 @@ libgnunet_plugin_transport_tcp_init (void *cls) "tcp", _("Require valid port number for service `%s' in configuration!\n"), "transport-tcp"); - GNUNET_free_non_null(external_address); - GNUNET_free_non_null(internal_address); return NULL; - } - - use_localaddresses = GNUNET_CONFIGURATION_get_value_yesno (env->cfg, - "transport-tcp", - "USE_LOCALADDR"); - if (use_localaddresses == GNUNET_SYSERR) - use_localaddresses = GNUNET_NO; - + } if (aport == 0) aport = bport; if (bport == 0) aport = 0; - if (bport != 0) { - service = GNUNET_SERVICE_start ("transport-tcp", env->cfg); + service = GNUNET_SERVICE_start ("transport-tcp", env->cfg); if (service == NULL) { GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, @@ -3065,21 +2051,45 @@ libgnunet_plugin_transport_tcp_init (void *cls) else service = NULL; + + plugin = GNUNET_malloc (sizeof (struct Plugin)); plugin->max_connections = max_connections; plugin->open_port = bport; plugin->adv_port = aport; - plugin->bind_address = bind_address; - plugin->external_address = external_address; - plugin->internal_address = internal_address; - plugin->behind_nat = behind_nat; - plugin->nat_punched = nat_punched; - plugin->enable_nat_client = enable_nat_client; - plugin->enable_nat_server = enable_nat_server; - plugin->enable_upnp = enable_upnp; - plugin->use_localaddresses = use_localaddresses; plugin->env = env; plugin->lsock = NULL; + if ( (service != NULL) && + (GNUNET_SYSERR != + (ret = GNUNET_SERVICE_get_server_addresses ("transport-tcp", + env->cfg, + &addrs, + &addrlens))) ) + { + plugin->nat = GNUNET_NAT_register (env->cfg, + GNUNET_YES, + aport, + (unsigned int) ret, + (const struct sockaddr **) addrs, + addrlens, + &tcp_nat_port_map_callback, + &try_connection_reversal, + plugin); + while (ret > 0) + GNUNET_free (addrs[--ret]); + GNUNET_free_non_null (addrs); + GNUNET_free_non_null (addrlens); + } + else + { + plugin->nat = GNUNET_NAT_register (env->cfg, + GNUNET_YES, + 0, + 0, NULL, NULL, + NULL, + &try_connection_reversal, + plugin); + } api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions)); api->cls = plugin; api->send = &tcp_plugin_send; @@ -3105,8 +2115,9 @@ libgnunet_plugin_transport_tcp_init (void *cls) _("Failed to find option %s in section %s!\n"), "TIMEOUT", "transport-tcp"); - GNUNET_free_non_null(external_address); - GNUNET_free_non_null(internal_address); + if (plugin->nat != NULL) + GNUNET_NAT_unregister (plugin->nat); + GNUNET_free (plugin); GNUNET_free (api); return NULL; } @@ -3123,33 +2134,7 @@ libgnunet_plugin_transport_tcp_init (void *cls) GNUNET_SERVER_disconnect_notify (plugin->server, &disconnect_notify, plugin); - GNUNET_OS_network_interfaces_list (&process_interfaces, plugin); - - if ( (plugin->behind_nat == GNUNET_YES) && - (plugin->enable_nat_server == GNUNET_YES) && - (GNUNET_YES != tcp_transport_start_nat_server(plugin)) ) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, - "tcp", - _("Failed to start %s required for NAT in %s!\n"), - "gnunet-nat-server" - "transport-tcp"); - GNUNET_free_non_null(external_address); - GNUNET_free_non_null(internal_address); - if (service != NULL) - GNUNET_SERVICE_stop (service); - else - GNUNET_SERVER_destroy (plugin->server); - GNUNET_free (api); - return NULL; - } - - if (enable_nat_client == GNUNET_YES) - { - plugin->nat_wait_conns = GNUNET_CONTAINER_multihashmap_create(16); - GNUNET_assert (plugin->nat_wait_conns != NULL); - } - + plugin->nat_wait_conns = GNUNET_CONTAINER_multihashmap_create(16); if (bport != 0) GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "tcp", @@ -3164,20 +2149,6 @@ libgnunet_plugin_transport_tcp_init (void *cls) "tcp", _("TCP transport advertises itself as being on port %llu\n"), aport); - - plugin->hostname_dns = GNUNET_RESOLVER_hostname_resolve (AF_UNSPEC, - HOSTNAME_RESOLVE_TIMEOUT, - &process_hostname_ips, - plugin); - - if (plugin->external_address != NULL) - { - plugin->ext_dns = GNUNET_RESOLVER_ip_get (plugin->external_address, - AF_INET, - GNUNET_TIME_UNIT_MINUTES, - &process_external_ip, - plugin); - } return api; } @@ -3191,36 +2162,17 @@ libgnunet_plugin_transport_tcp_done (void *cls) struct GNUNET_TRANSPORT_PluginFunctions *api = cls; struct Plugin *plugin = api->cls; struct Session *session; - struct LocalAddrList *lal; struct TCPProbeContext *tcp_probe; - if (plugin->ext_dns != NULL) - { - GNUNET_RESOLVER_request_cancel (plugin->ext_dns); - plugin->ext_dns = NULL; - } while (NULL != (session = plugin->sessions)) disconnect_session (session); - if (NULL != plugin->hostname_dns) - { - GNUNET_RESOLVER_request_cancel (plugin->hostname_dns); - plugin->hostname_dns = NULL; - } if (plugin->service != NULL) GNUNET_SERVICE_stop (plugin->service); else GNUNET_SERVER_destroy (plugin->server); GNUNET_free (plugin->handlers); - while (NULL != (lal = plugin->lal_head)) - { - GNUNET_CONTAINER_DLL_remove (plugin->lal_head, - plugin->lal_tail, - lal); - if (lal->nat != NULL) - GNUNET_NAT_unregister (lal->nat); - GNUNET_free_non_null (lal->external_nat_address); - GNUNET_free (lal); - } + if (plugin->nat != NULL) + GNUNET_NAT_unregister (plugin->nat); while (NULL != (tcp_probe = plugin->probe_head)) { GNUNET_CONTAINER_DLL_remove (plugin->probe_head, @@ -3229,19 +2181,6 @@ libgnunet_plugin_transport_tcp_done (void *cls) GNUNET_CONNECTION_destroy (tcp_probe->sock, GNUNET_NO); GNUNET_free (tcp_probe); } - - if ((plugin->behind_nat == GNUNET_YES) && - (plugin->enable_nat_server == GNUNET_YES)) - { - if (0 != GNUNET_OS_process_kill (plugin->server_proc, SIGTERM)) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); - GNUNET_OS_process_wait (plugin->server_proc); - GNUNET_OS_process_close (plugin->server_proc); - plugin->server_proc = NULL; - } - GNUNET_free_non_null(plugin->bind_address); - GNUNET_free_non_null(plugin->internal_address); - GNUNET_free_non_null(plugin->external_address); GNUNET_free (plugin); GNUNET_free (api); return NULL; diff --git a/src/transport/plugin_transport_udp.c b/src/transport/plugin_transport_udp.c index 2366a2e16..e5e741df3 100644 --- a/src/transport/plugin_transport_udp.c +++ b/src/transport/plugin_transport_udp.c @@ -41,6 +41,7 @@ #include "gnunet_hello_lib.h" #include "gnunet_connection_lib.h" #include "gnunet_container_lib.h" +#include "gnunet_nat_lib.h" #include "gnunet_os_lib.h" #include "gnunet_peerinfo_service.h" #include "gnunet_protocols.h" @@ -103,7 +104,7 @@ struct IPv4UdpAddress /** * Port number, in network byte order. */ - uint16_t u_port GNUNET_PACKED; + uint16_t u4_port GNUNET_PACKED; }; @@ -210,31 +211,6 @@ struct UDP_NAT_ProbeMessageConfirmation }; -/** - * Local network addresses (actual IP address follows this struct). - * PORT is NOT included! - */ -struct LocalAddrList -{ - - /** - * This is a doubly linked list. - */ - struct LocalAddrList *next; - - /** - * This is a doubly linked list. - */ - struct LocalAddrList *prev; - - /** - * Number of bytes of the address that follow - */ - size_t size; - -}; - - /** * UDP NAT "Session" */ @@ -294,11 +270,6 @@ struct UDP_NAT_Probes */ struct UDP_NAT_Probes *next; - /** - * Address string that the server process returned to us - */ - char *address_string; - /** * Timeout for this set of probes */ @@ -359,16 +330,6 @@ struct Plugin */ struct PeerSession *sessions; - /** - * Handle for request of hostname resolution, non-NULL if pending. - */ - struct GNUNET_RESOLVER_RequestHandle *hostname_dns; - - /** - * ID of task used to update our addresses when one expires. - */ - GNUNET_SCHEDULER_TaskIdentifier address_update_task; - /** * ID of select task */ @@ -379,17 +340,6 @@ struct Plugin */ uint16_t port; - /** - * The external address given to us by the user. Must be actual - * outside visible address for NAT punching to work. - */ - char *external_address; - - /** - * The internal address given to us by the user (or discovered). - */ - char *internal_address; - /** * Address we were told to bind to exclusively (IPv4). */ @@ -401,30 +351,15 @@ struct Plugin char *bind6_address; /** - * List of our IP addresses. - */ - struct LocalAddrList *lal_head; - - /** - * Tail of our IP address list. + * Handle to NAT traversal support. */ - struct LocalAddrList *lal_tail; + struct GNUNET_NAT_Handle *nat; /** * FD Read set */ struct GNUNET_NETWORK_FDSet *rs; - /** - * stdout pipe handle for the gnunet-nat-server process - */ - struct GNUNET_DISK_PipeHandle *server_stdout; - - /** - * stdout file handle (for reading) for the gnunet-nat-server process - */ - const struct GNUNET_DISK_FileHandle *server_stdout_handle; - /** * Probes in flight */ @@ -440,45 +375,13 @@ struct Plugin */ struct UDP_Sock_Info udp_sockv6; - /** - * ID of select gnunet-nat-server stdout read task - */ - GNUNET_SCHEDULER_TaskIdentifier server_read_task; - - /** - * Is this transport configured to be behind a NAT? - */ - int behind_nat; - - /** - * Is this transport configured to allow connections to NAT'd peers? - */ - int allow_nat; - - /** - * Should this transport advertise only NAT addresses (port set to 0)? - * If not, all addresses will be duplicated for NAT punching and regular - * ports. - */ - int only_nat_addresses; - - /** - * use local addresses? - */ - int use_localaddresses; - - /** - * The process id of the server process (if behind NAT) - */ - struct GNUNET_OS_Process *server_proc; - }; /** * Forward declaration. */ -void +static void udp_probe_continuation (void *cls, const struct GNUNET_PeerIdentity *target, int result); @@ -489,53 +392,13 @@ udp_probe_continuation (void *cls, const struct GNUNET_PeerIdentity *target, int * @param target the peeridentity of the peer to disconnect * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed */ -void +static void udp_disconnect (void *cls, const struct GNUNET_PeerIdentity *target) { /** TODO: Implement! */ return; } -/** - * Shutdown the server process (stop receiving inbound traffic). Maybe - * restarted later! - * - * @param cls Handle to the plugin for this transport - * - * @return returns the number of sockets successfully closed, - * should equal the number of sockets successfully opened - */ -static int -udp_transport_server_stop (void *cls) -{ - struct Plugin *plugin = cls; - - if (plugin->select_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (plugin->select_task); - plugin->select_task = GNUNET_SCHEDULER_NO_TASK; - } - if (plugin->udp_sockv4.desc != NULL) - { - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (plugin->udp_sockv4.desc)); - plugin->udp_sockv4.desc = NULL; - } - if (plugin->udp_sockv6.desc != NULL) - { - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (plugin->udp_sockv6.desc)); - plugin->udp_sockv6.desc = NULL; - } - if (plugin->behind_nat == GNUNET_YES) - { - if (0 != GNUNET_OS_process_kill (plugin->server_proc, SIGTERM)) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); - GNUNET_OS_process_wait (plugin->server_proc); - GNUNET_OS_process_close (plugin->server_proc); - plugin->server_proc = NULL; - } - return GNUNET_OK; -} - struct PeerSession * find_session (struct Plugin *plugin, @@ -650,7 +513,7 @@ udp_real_send (void *cls, a4.sin_len = sizeof (a4); #endif a4.sin_family = AF_INET; - a4.sin_port = t4->u_port; + a4.sin_port = t4->u4_port; a4.sin_addr.s_addr = t4->ipv4_addr; sb = &a4; sbs = sizeof (a4); @@ -689,51 +552,6 @@ udp_real_send (void *cls, return sent; } -/** - * We learned about a peer (possibly behind NAT) so run the - * gnunet-nat-client to send dummy ICMP responses - * - * @param plugin the plugin for this transport - * @param addr the address of the peer - * @param addrlen the length of the address - */ -void -run_gnunet_nat_client (struct Plugin *plugin, const char *addr, size_t addrlen) -{ - char addr_buf[INET_ADDRSTRLEN]; - char *address_as_string; - char *port_as_string; - struct GNUNET_OS_Process *proc; - const struct IPv4UdpAddress *t4; - - GNUNET_assert(addrlen == sizeof(struct IPv4UdpAddress)); - t4 = (struct IPv4UdpAddress *)addr; - - if (NULL == inet_ntop (AF_INET, - &t4->ipv4_addr, - addr_buf, INET_ADDRSTRLEN)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop"); - return; - } - address_as_string = GNUNET_strdup (addr_buf); - GNUNET_asprintf(&port_as_string, "%d", plugin->port); -#if DEBUG_UDP - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("Running gnunet-nat-client with arguments: %s %s %d\n"), plugin->external_address, address_as_string, plugin->port); -#endif - - /* Start the server process */ - proc = GNUNET_OS_start_process (NULL, NULL, "gnunet-nat-client", "gnunet-nat-client", plugin->external_address, address_as_string, port_as_string, NULL); - GNUNET_free(address_as_string); - GNUNET_free(port_as_string); - if (proc != NULL) - { - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_close (proc); - proc = NULL; - } -} /** * Function that can be used by the transport service to transmit @@ -778,6 +596,7 @@ udp_plugin_send (void *cls, struct PeerSession *peer_session; int other_peer_natd; const struct IPv4UdpAddress *t4; + struct sockaddr_in sin4; if (force_address == GNUNET_SYSERR) return GNUNET_SYSERR; @@ -787,7 +606,7 @@ udp_plugin_send (void *cls, if (addrlen == sizeof(struct IPv4UdpAddress)) { t4 = addr; - if (ntohs(t4->u_port) == 0) + if (ntohs(t4->u4_port) == 0) other_peer_natd = GNUNET_YES; } else if (addrlen != sizeof(struct IPv6UdpAddress)) @@ -797,7 +616,8 @@ udp_plugin_send (void *cls, } sent = 0; - if ((other_peer_natd == GNUNET_YES) && (plugin->allow_nat == GNUNET_YES)) + if ( (other_peer_natd == GNUNET_YES) && + (addrlen == sizeof(struct IPv4UdpAddress)) ) { peer_session = find_session(plugin, target); if (peer_session == NULL) /* We have a new peer to add */ @@ -830,7 +650,14 @@ udp_plugin_send (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Other peer is NAT'd, set up peer session for peer %s\n"), GNUNET_i2s(target)); #endif - run_gnunet_nat_client(plugin, addr, addrlen); + memset (&sin4, 0, sizeof (sin4)); + sin4.sin_family = AF_INET; +#if HAVE_SOCKADDR_IN_SIN_LEN + sin4.sin_len = sizeof (sin4); +#endif + sin4.sin_port = t4->u4_port; + sin4.sin_addr.s_addr = t4->ipv4_addr; + GNUNET_NAT_run_client (plugin->nat, &sin4); } else { @@ -881,244 +708,6 @@ udp_plugin_send (void *cls, } -static void -add_to_address_list (struct Plugin *plugin, - const void *arg, - size_t arg_size) -{ - struct LocalAddrList *lal; - - lal = plugin->lal_head; - while (NULL != lal) - { - if ( (lal->size == arg_size) && - (0 == memcmp (&lal[1], arg, arg_size)) ) - return; - lal = lal->next; - } - lal = GNUNET_malloc (sizeof (struct LocalAddrList) + arg_size); - lal->size = arg_size; - memcpy (&lal[1], arg, arg_size); - GNUNET_CONTAINER_DLL_insert (plugin->lal_head, - plugin->lal_tail, - lal); -} - - -static int -check_local_addr (struct Plugin *plugin, - const void *arg, - size_t arg_size) -{ - struct LocalAddrList *lal; - - lal = plugin->lal_head; - while (NULL != lal) - { - if ( (lal->size == arg_size) && - (0 == memcmp (&lal[1], arg, arg_size)) ) - return GNUNET_OK; - lal = lal->next; - } - return GNUNET_SYSERR; -} - -static int check_localaddress (const struct sockaddr *addr, socklen_t addrlen) -{ - uint32_t res = 0; - int local = GNUNET_NO; - int af = addr->sa_family; - switch (af) - { - case AF_INET: - { - uint32_t netmask = 0x7F000000; - uint32_t address = ntohl (((struct sockaddr_in *) addr)->sin_addr.s_addr); - res = (address >> 24) ^ (netmask >> 24); - if (res != 0) - local = GNUNET_NO; - else - local = GNUNET_YES; -#if DEBUG_UDP - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Checking IPv4 address `%s': %s\n", GNUNET_a2s (addr, addrlen), (local==GNUNET_YES) ? "local" : "global"); -#endif - break; - } - case AF_INET6: - { - if (IN6_IS_ADDR_LOOPBACK (&((struct sockaddr_in6 *) addr)->sin6_addr) || - IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr)) - local = GNUNET_YES; - else - local = GNUNET_NO; -#if DEBUG_UDP - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Checking IPv6 address `%s' : %s\n", GNUNET_a2s (addr, addrlen), (local==GNUNET_YES) ? "local" : "global"); -#endif - break; - } - } - return local; -} - - -/** - * Add the IP of our network interface to the list of - * our external IP addresses. - */ -static int -process_interfaces (void *cls, - const char *name, - int isDefault, - const struct sockaddr *addr, socklen_t addrlen) -{ - struct Plugin *plugin = cls; - int af; - struct IPv4UdpAddress t4; - struct IPv6UdpAddress t6; - void *arg; - uint16_t args; - void *addr_nat; - char buf[INET6_ADDRSTRLEN]; - - addr_nat = NULL; - af = addr->sa_family; - - if (plugin->use_localaddresses == GNUNET_NO) - { - if (GNUNET_YES == check_localaddress (addr, addrlen)) - { -#if DEBUG_UDP - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - "udp", - "Not notifying transport of address `%s' (local address)\n", - GNUNET_a2s (addr, addrlen)); -#endif - return GNUNET_OK; - } - } - - memset(buf, 0, INET6_ADDRSTRLEN); - if (af == AF_INET) - { - t4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr; - GNUNET_assert(NULL != inet_ntop(AF_INET, &t4.ipv4_addr, &buf[0], INET_ADDRSTRLEN)); - if ((plugin->bind6_address != NULL) || ((plugin->bind_address != NULL) && (0 != strcmp(buf, plugin->bind_address)))) - { - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "%s: Not notifying transport of address %s\n", "UDP", GNUNET_a2s (addr, addrlen)); - return GNUNET_OK; - } - add_to_address_list (plugin, &t4.ipv4_addr, sizeof (uint32_t)); - if ((plugin->behind_nat == GNUNET_YES) && (plugin->only_nat_addresses == GNUNET_YES)) - { - t4.u_port = htons (DEFAULT_NAT_PORT); - } - else if (plugin->behind_nat == GNUNET_YES) /* We are behind NAT, but will advertise NAT and normal addresses */ - { - addr_nat = GNUNET_malloc(sizeof(t4)); - t4.u_port = htons (DEFAULT_NAT_PORT); - memcpy(addr_nat, &t4, sizeof(t4)); - t4.u_port = plugin->port; - } - else - { - t4.u_port = htons(plugin->port); - } - arg = &t4; - args = sizeof (t4); - } - else if (af == AF_INET6) - { - if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr)) - { - /* skip link local addresses */ - return GNUNET_OK; - } - memcpy (&t6.ipv6_addr, - &((struct sockaddr_in6 *) addr)->sin6_addr, - sizeof (struct in6_addr)); - GNUNET_assert(NULL != inet_ntop(AF_INET6, &t6.ipv6_addr, &buf[0], INET6_ADDRSTRLEN)); - if (((plugin->bind_address != NULL) && (0 != strcmp(buf, plugin->bind_address))) - || ((plugin->bind6_address != NULL) && (0 != strcmp(buf, plugin->bind6_address)))) - { - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "%s: Not notifying transport of address %s\n", "UDP", GNUNET_a2s (addr, addrlen)); - return GNUNET_OK; - } - add_to_address_list (plugin, &t6.ipv6_addr, sizeof (struct in6_addr)); - if ((plugin->behind_nat == GNUNET_YES) && (plugin->only_nat_addresses == GNUNET_YES)) - { - t6.u6_port = htons (DEFAULT_NAT_PORT); - } - else if (plugin->behind_nat == GNUNET_YES) - { - addr_nat = GNUNET_malloc(sizeof(t6)); - t6.u6_port = htons (DEFAULT_NAT_PORT); - memcpy(addr_nat, &t6, sizeof(t6)); - t6.u6_port = plugin->port; - } - else - { - t6.u6_port = htons (plugin->port); - } - - arg = &t6; - args = sizeof (t6); - } - else - { - GNUNET_break (0); - return GNUNET_OK; - } - - GNUNET_log (GNUNET_ERROR_TYPE_INFO | - GNUNET_ERROR_TYPE_BULK, - _("Found address `%s' (%s)\n"), - GNUNET_a2s (addr, addrlen), name); - - if (addr_nat != NULL) - { - plugin->env->notify_address (plugin->env->cls, - "udp", - addr_nat, args, GNUNET_TIME_UNIT_FOREVER_REL); - GNUNET_log (GNUNET_ERROR_TYPE_INFO | - GNUNET_ERROR_TYPE_BULK, - _("Found NAT address `%s' (%s)\n"), - GNUNET_a2s (addr_nat, args), name); - GNUNET_free(addr_nat); - } - - plugin->env->notify_address (plugin->env->cls, - "udp", - arg, args, GNUNET_TIME_UNIT_FOREVER_REL); - return GNUNET_OK; -} - - -/** - * Function called by the resolver for each address obtained from DNS - * for our own hostname. Add the addresses to the list of our - * external IP addresses. - * - * @param cls closure - * @param addr one of the addresses of the host, NULL for the last address - * @param addrlen length of the address - */ -static void -process_hostname_ips (void *cls, - const struct sockaddr *addr, socklen_t addrlen) -{ - struct Plugin *plugin = cls; - - if (addr == NULL) - { - plugin->hostname_dns = NULL; - return; - } - process_interfaces (plugin, "", GNUNET_YES, addr, addrlen); -} - - /** * Send UDP probe messages or UDP keepalive messages, depending on the * state of the connection. @@ -1127,7 +716,8 @@ process_hostname_ips (void *cls, * @param tc task context for running this */ static void -send_udp_probe_message (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +send_udp_probe_message (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) { struct UDP_NAT_Probes *probe = cls; struct UDP_NAT_ProbeMessage message; @@ -1137,12 +727,12 @@ send_udp_probe_message (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc message.header.size = htons(sizeof(struct UDP_NAT_ProbeMessage)); message.header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_NAT_PROBE); /* If they gave us a port, use that. If not, try our port. */ - if (ntohs(probe->addr.u_port) == 0) - probe->addr.u_port = htons(plugin->port); + if (ntohs(probe->addr.u4_port) == 0) + probe->addr.u4_port = htons(plugin->port); #if DEBUG_UDP GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("Sending a probe to port %d\n"), ntohs(probe->addr.u_port)); + _("Sending a probe to port %d\n"), ntohs(probe->addr.u4_port)); #endif probe->count++; udp_real_send(plugin, @@ -1158,10 +748,12 @@ send_udp_probe_message (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc /** * Continuation for probe sends. If the last probe was sent * "successfully", schedule sending of another one. If not, - * + * FIXME... */ -void -udp_probe_continuation (void *cls, const struct GNUNET_PeerIdentity *target, int result) +static void +udp_probe_continuation (void *cls, + const struct GNUNET_PeerIdentity *target, + int result) { struct UDP_NAT_Probes *probe = cls; /*struct Plugin *plugin = probe->plugin;*/ @@ -1170,171 +762,79 @@ udp_probe_continuation (void *cls, const struct GNUNET_PeerIdentity *target, int { #if DEBUG_UDP GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("Scheduling next probe for 10000 milliseconds\n")); + _("Scheduling next probe for 10000 milliseconds\n")); #endif - probe->task = GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 10000), &send_udp_probe_message, probe); + probe->task = GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 10), + &send_udp_probe_message, probe); } else /* Destroy the probe context. */ { #if DEBUG_UDP GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("Sending probe didn't go well...\n")); + _("Sending probe didn't go well...\n")); #endif } } + /** - * Find probe message by address - * - * @param plugin the plugin for this transport - * @param address_string the ip address as a string + * FIXME. */ -struct UDP_NAT_Probes * -find_probe(struct Plugin *plugin, char * address_string) +static void +udp_plugin_reversal_callback (void *cls, + const struct sockaddr *addr, + socklen_t addrlen) { - struct UDP_NAT_Probes *pos; - - pos = plugin->probes; - while (pos != NULL) - if (strcmp(pos->address_string, address_string) == 0) - return pos; + struct Plugin *plugin = cls; + struct UDP_NAT_Probes *temp_probe; + const struct sockaddr_in *inaddr; - return pos; + if (sizeof (struct sockaddr_in) != addrlen) + { + GNUNET_break (0); + return; + } + inaddr = (const struct sockaddr_in *) addr; + temp_probe = GNUNET_malloc(sizeof(struct UDP_NAT_Probes)); + temp_probe->addr.ipv4_addr = inaddr->sin_addr.s_addr; + temp_probe->addr.u4_port = inaddr->sin_port; + temp_probe->next = plugin->probes; + temp_probe->plugin = plugin; + temp_probe->task = GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 500), + &send_udp_probe_message, + temp_probe); + plugin->probes = temp_probe; } -/* - * @param cls the plugin handle - * @param tc the scheduling context (for rescheduling this function again) - * - * We have been notified that gnunet-nat-server has written something to stdout. - * Handle the output, then reschedule this function to be called again once - * more is available. +/** + * Demultiplexer for UDP NAT messages * + * @param plugin the main plugin for this transport + * @param sender from which peer the message was received + * @param currhdr pointer to the header of the message + * @param sender_addr the address from which the message was received + * @param fromlen the length of the address + * @param sockinfo which socket did we receive the message on */ static void -udp_plugin_server_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +udp_demultiplexer(struct Plugin *plugin, + struct GNUNET_PeerIdentity *sender, + const struct GNUNET_MessageHeader *currhdr, + const void *sender_addr, + size_t fromlen, struct UDP_Sock_Info *sockinfo) { - struct Plugin *plugin = cls; - char mybuf[40]; - ssize_t bytes; - memset(&mybuf, 0, sizeof(mybuf)); - int i; - struct UDP_NAT_Probes *temp_probe; - int port; - char *port_start; - struct IPv4UdpAddress a4; - - if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) - return; - - bytes = GNUNET_DISK_file_read(plugin->server_stdout_handle, &mybuf, sizeof(mybuf)); - - if (bytes < 1) + struct UDP_NAT_ProbeMessageReply *outgoing_probe_reply; + struct PeerSession *peer_session; + struct MessageQueue *pending_message; + struct MessageQueue *pending_message_temp; + uint16_t incoming_port; + struct GNUNET_TRANSPORT_ATS_Information distance[2]; + if (memcmp(sender, plugin->env->my_identity, sizeof(struct GNUNET_PeerIdentity)) == 0) { #if DEBUG_UDP - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("Finished reading from server stdout with code: %d\n"), bytes); -#endif - return; - } - - port_start = NULL; - for (i = 0; i < sizeof(mybuf); i++) - { - if (mybuf[i] == '\n') - mybuf[i] = '\0'; - - if ((mybuf[i] == ':') && (i + 1 < sizeof(mybuf))) - { - mybuf[i] = '\0'; - port_start = &mybuf[i + 1]; - } - } - - if (port_start != NULL) - port = atoi(port_start); - else - { - plugin->server_read_task = - GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - plugin->server_stdout_handle, &udp_plugin_server_read, plugin); - return; - } - -#if DEBUG_UDP - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("nat-server-read read: %s port %d\n"), &mybuf, port); -#endif - - /** - * We have received an ICMP response, ostensibly from a non-NAT'd peer - * that wants to connect to us! Send a message to establish a connection. - */ - if (inet_pton(AF_INET, &mybuf[0], &a4.ipv4_addr) != 1) - { - - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("nat-server-read malformed address\n"), &mybuf, port); - - plugin->server_read_task = - GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - plugin->server_stdout_handle, &udp_plugin_server_read, plugin); - return; - } - - temp_probe = find_probe(plugin, &mybuf[0]); - - if (temp_probe == NULL) - { - temp_probe = GNUNET_malloc(sizeof(struct UDP_NAT_Probes)); - temp_probe->address_string = strdup(&mybuf[0]); - GNUNET_assert (1 == inet_pton(AF_INET, &mybuf[0], &temp_probe->addr.ipv4_addr)); - temp_probe->addr.u_port = htons(port); - temp_probe->next = plugin->probes; - temp_probe->plugin = plugin; - temp_probe->task = GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 500), &send_udp_probe_message, temp_probe); - plugin->probes = temp_probe; - } - - plugin->server_read_task = - GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - plugin->server_stdout_handle, &udp_plugin_server_read, plugin); - -} - - -/** - * Demultiplexer for UDP NAT messages - * - * @param plugin the main plugin for this transport - * @param sender from which peer the message was received - * @param currhdr pointer to the header of the message - * @param sender_addr the address from which the message was received - * @param fromlen the length of the address - * @param sockinfo which socket did we receive the message on - */ -static void -udp_demultiplexer(struct Plugin *plugin, - struct GNUNET_PeerIdentity *sender, - const struct GNUNET_MessageHeader *currhdr, - const void *sender_addr, - size_t fromlen, struct UDP_Sock_Info *sockinfo) -{ - struct UDP_NAT_ProbeMessageReply *outgoing_probe_reply; - struct UDP_NAT_ProbeMessageConfirmation *outgoing_probe_confirmation; - char addr_buf[INET_ADDRSTRLEN]; - struct UDP_NAT_Probes *outgoing_probe; - struct PeerSession *peer_session; - struct MessageQueue *pending_message; - struct MessageQueue *pending_message_temp; - uint16_t incoming_port; - struct GNUNET_TRANSPORT_ATS_Information distance[2]; - if (memcmp(sender, plugin->env->my_identity, sizeof(struct GNUNET_PeerIdentity)) == 0) - { -#if DEBUG_UDP - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp", - _("Received a message from myself, dropping!!!\n")); + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp", + _("Received a message from myself, dropping!!!\n")); #endif return; } @@ -1343,7 +843,7 @@ udp_demultiplexer(struct Plugin *plugin, GNUNET_assert(sender_addr != NULL); /* Can recvfrom have a NULL address? */ if (fromlen == sizeof(struct IPv4UdpAddress)) { - incoming_port = ntohs(((struct IPv4UdpAddress *)sender_addr)->u_port); + incoming_port = ntohs(((struct IPv4UdpAddress *)sender_addr)->u4_port); } else if (fromlen == sizeof(struct IPv6UdpAddress)) { @@ -1386,14 +886,10 @@ udp_demultiplexer(struct Plugin *plugin, #endif if (fromlen == sizeof(struct IPv4UdpAddress)) { - memset(&addr_buf, 0, sizeof(addr_buf)); - if (NULL == inet_ntop (AF_INET, - &((struct IPv4UdpAddress *) sender_addr)->ipv4_addr, addr_buf, - INET_ADDRSTRLEN)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop"); - return; - } + /* FIXME! */ +#if 0 + struct UDP_NAT_ProbeMessageConfirmation *outgoing_probe_confirmation; + struct UDP_NAT_Probes *outgoing_probe; outgoing_probe = find_probe(plugin, &addr_buf[0]); if (outgoing_probe != NULL) { @@ -1425,6 +921,7 @@ udp_demultiplexer(struct Plugin *plugin, _("Received a probe reply, but have no record of a sent probe!\n")); #endif } +#endif } else { @@ -1466,7 +963,7 @@ udp_demultiplexer(struct Plugin *plugin, peer_session->sock = sockinfo->desc; if (peer_session->connect_alen == sizeof(struct IPv4UdpAddress)) { - ((struct IPv4UdpAddress *)peer_session->connect_addr)->u_port = htons(incoming_port); + ((struct IPv4UdpAddress *)peer_session->connect_addr)->u4_port = htons(incoming_port); } else if (peer_session->connect_alen == sizeof(struct IPv4UdpAddress)) { @@ -1550,13 +1047,12 @@ udp_demultiplexer(struct Plugin *plugin, /* - * @param cls the plugin handle - * @param tc the scheduling context (for rescheduling this function again) - * * We have been notified that our writeset has something to read. We don't * know which socket needs to be read, so we have to check each one * Then reschedule this function to be called again once more is available. * + * @param cls the plugin handle + * @param tc the scheduling context (for rescheduling this function again) */ static void udp_plugin_select (void *cls, @@ -1606,7 +1102,7 @@ udp_plugin_select (void *cls, if (AF_INET == ((struct sockaddr *)addr)->sa_family) { s4 = (const struct sockaddr_in*) &addr; - t4.u_port = s4->sin_port; + t4.u4_port = s4->sin_port; t4.ipv4_addr = s4->sin_addr.s_addr; ca = &t4; calen = sizeof (t4); @@ -1676,187 +1172,6 @@ udp_plugin_select (void *cls, } -/** - * Create a slew of UDP sockets. If possible, use IPv6 and IPv4. - * - * @param cls closure for server start, should be a struct Plugin * - * @return number of sockets created or GNUNET_SYSERR on error -*/ -static int -udp_transport_server_start (void *cls) -{ - struct Plugin *plugin = cls; - struct sockaddr_in serverAddrv4; - struct sockaddr_in6 serverAddrv6; - struct sockaddr *serverAddr; - socklen_t addrlen; - int sockets_created; - int tries; - - - sockets_created = 0; - if (plugin->behind_nat == GNUNET_YES) - { - /* Pipe to read from started processes stdout (on read end) */ - plugin->server_stdout = GNUNET_DISK_pipe(GNUNET_YES, GNUNET_NO, GNUNET_YES); - if (plugin->server_stdout == NULL) - return sockets_created; -#if DEBUG_UDP - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Starting gnunet-nat-server process cmd: %s %s\n", - "gnunet-nat-server", - plugin->internal_address); -#endif - /* Start the server process */ - plugin->server_proc = GNUNET_OS_start_process(NULL, - plugin->server_stdout, - "gnunet-nat-server", - "gnunet-nat-server", - plugin->internal_address, NULL); - if (plugin->server_proc == NULL) - { -#if DEBUG_UDP - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Failed to start gnunet-nat-server process\n"); -#endif - return GNUNET_SYSERR; - } - /* Close the write end of the read pipe */ - GNUNET_DISK_pipe_close_end(plugin->server_stdout, GNUNET_DISK_PIPE_END_WRITE); - - plugin->server_stdout_handle = GNUNET_DISK_pipe_handle(plugin->server_stdout, GNUNET_DISK_PIPE_END_READ); - plugin->server_read_task = - GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - plugin->server_stdout_handle, &udp_plugin_server_read, plugin); - } - - if ( (GNUNET_YES != - GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, "nat", - "DISABLEV6"))) - { - plugin->udp_sockv6.desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_DGRAM, 0); - if (NULL == plugin->udp_sockv6.desc) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp", "socket"); - } - else - { - memset (&serverAddrv6, 0, sizeof (serverAddrv6)); -#if HAVE_SOCKADDR_IN_SIN_LEN - serverAddrv6.sin6_len = sizeof (serverAddrv6); -#endif - - serverAddrv6.sin6_family = AF_INET6; - serverAddrv6.sin6_addr = in6addr_any; - if (plugin->bind6_address != NULL) - { - if (1 != inet_pton(AF_INET6, plugin->bind6_address, &serverAddrv6.sin6_addr)) - return 0; - } - - serverAddrv6.sin6_port = htons (plugin->port); - addrlen = sizeof (serverAddrv6); - serverAddr = (struct sockaddr *) &serverAddrv6; -#if DEBUG_UDP - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Binding to IPv6 port %d\n", - ntohs(serverAddrv6.sin6_port)); -#endif - tries = 0; - while (GNUNET_NETWORK_socket_bind (plugin->udp_sockv6.desc, serverAddr, addrlen) != - GNUNET_OK) - { - serverAddrv6.sin6_port = htons (GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_STRONG, 33537) + 32000); /* Find a good, non-root port */ -#if DEBUG_UDP - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "IPv6 Binding failed, trying new port %d\n", - ntohs(serverAddrv6.sin6_port)); -#endif - tries++; - if (tries > 10) - { - GNUNET_NETWORK_socket_close (plugin->udp_sockv6.desc); - plugin->udp_sockv6.desc = NULL; - break; - } - } - if (plugin->udp_sockv6.desc != NULL) - { - plugin->udp_sockv6.port = ntohs(serverAddrv6.sin6_port); - sockets_created++; - } - } - } - - plugin->udp_sockv4.desc = GNUNET_NETWORK_socket_create (PF_INET, SOCK_DGRAM, 0); - if (NULL == plugin->udp_sockv4.desc) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "socket"); - } - else - { - memset (&serverAddrv4, 0, sizeof (serverAddrv4)); -#if HAVE_SOCKADDR_IN_SIN_LEN - serverAddrv4.sin_len = sizeof (serverAddrv4); -#endif - serverAddrv4.sin_family = AF_INET; - serverAddrv4.sin_addr.s_addr = INADDR_ANY; - if (plugin->bind_address != NULL) - { - if (1 != inet_pton(AF_INET, plugin->bind_address, &serverAddrv4.sin_addr)) - return 0; - } - serverAddrv4.sin_port = htons (plugin->port); - addrlen = sizeof (serverAddrv4); - serverAddr = (struct sockaddr *) &serverAddrv4; -#if DEBUG_UDP - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Binding to IPv4 port %d\n", - ntohs(serverAddrv4.sin_port)); -#endif - tries = 0; - while (GNUNET_NETWORK_socket_bind (plugin->udp_sockv4.desc, serverAddr, addrlen) != - GNUNET_OK) - { - serverAddrv4.sin_port = htons (GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_STRONG, 33537) + 32000); /* Find a good, non-root port */ -#if DEBUG_UDP - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "IPv4 Binding failed, trying new port %d\n", - ntohs(serverAddrv4.sin_port)); -#endif - tries++; - if (tries > 10) - { - GNUNET_NETWORK_socket_close (plugin->udp_sockv4.desc); - plugin->udp_sockv4.desc = NULL; - break; - } - } - if (plugin->udp_sockv4.desc != NULL) - { - plugin->udp_sockv4.port = ntohs(serverAddrv4.sin_port); - sockets_created++; - } - } - - plugin->rs = GNUNET_NETWORK_fdset_create (); - GNUNET_NETWORK_fdset_zero (plugin->rs); - if (NULL != plugin->udp_sockv4.desc) - GNUNET_NETWORK_fdset_set (plugin->rs, - plugin->udp_sockv4.desc); - if (NULL != plugin->udp_sockv6.desc) - GNUNET_NETWORK_fdset_set (plugin->rs, - plugin->udp_sockv6.desc); - - plugin->select_task = - GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, - GNUNET_SCHEDULER_NO_TASK, - GNUNET_TIME_UNIT_FOREVER_REL, plugin->rs, - NULL, &udp_plugin_select, plugin); - return sockets_created; -} - - /** * Check if the given port is plausible (must be either @@ -1870,11 +1185,8 @@ udp_transport_server_start (void *cls) static int check_port (struct Plugin *plugin, uint16_t in_port) { - if ( (plugin->behind_nat == GNUNET_YES) && (in_port == 0) ) + if (in_port == 0) return GNUNET_OK; - if ( (plugin->only_nat_addresses == GNUNET_YES) && - (plugin->behind_nat == GNUNET_YES) ) - return GNUNET_SYSERR; /* odd case... */ if (in_port == plugin->port) return GNUNET_OK; return GNUNET_SYSERR; @@ -1898,16 +1210,11 @@ check_port (struct Plugin *plugin, uint16_t in_port) * */ static int -udp_check_address (void *cls, - const void *addr, - size_t addrlen) +udp_plugin_check_address (void *cls, + const void *addr, + size_t addrlen) { struct Plugin *plugin = cls; - - const void *sb; - struct in_addr a4; - struct in6_addr a6; - int af; struct IPv4UdpAddress *v4; struct IPv6UdpAddress *v6; @@ -1917,20 +1224,16 @@ udp_check_address (void *cls, GNUNET_break_op (0); return GNUNET_SYSERR; } - if (addrlen == sizeof (struct IPv4UdpAddress)) { v4 = (struct IPv4UdpAddress *) addr; if (GNUNET_OK != - check_port (plugin, ntohs (v4->u_port))) + check_port (plugin, ntohs (v4->u4_port))) return GNUNET_SYSERR; if (GNUNET_OK != - check_local_addr (plugin, &v4->ipv4_addr, sizeof (uint32_t))) + GNUNET_NAT_test_address (plugin->nat, + &v4->ipv4_addr, sizeof (struct in_addr))) return GNUNET_SYSERR; - - af = AF_INET; - memcpy (&a4, &v4->ipv4_addr, sizeof (a4)); - sb = &a4; } else { @@ -1944,14 +1247,10 @@ udp_check_address (void *cls, check_port (plugin, ntohs (v6->u6_port))) return GNUNET_SYSERR; if (GNUNET_OK != - check_local_addr (plugin, &v6->ipv6_addr, sizeof (struct in6_addr))) + GNUNET_NAT_test_address (plugin->nat, + &v6->ipv6_addr, sizeof (struct in6_addr))) return GNUNET_SYSERR; - - af = AF_INET6; - memcpy (&a6, &v6->ipv6_addr, sizeof (a6)); - sb = &a6; } - return GNUNET_OK; } @@ -2028,9 +1327,9 @@ udp_plugin_address_pretty_printer (void *cls, u4 = addr; memset (&a4, 0, sizeof (a4)); a4.sin_family = AF_INET; - a4.sin_port = u4->u_port; + a4.sin_port = u4->u4_port; a4.sin_addr.s_addr = u4->ipv4_addr; - port = ntohs (u4->u_port); + port = ntohs (u4->u4_port); sb = &a4; sbs = sizeof (a4); } @@ -2050,99 +1349,6 @@ udp_plugin_address_pretty_printer (void *cls, !numeric, timeout, &append_port, ppc); } -/** - * Return the actual path to a file found in the current - * PATH environment variable. - * - * @param binary the name of the file to find - */ -static char * -get_path_from_PATH (char *binary) -{ - char *path; - char *pos; - char *end; - char *buf; - const char *p; - - p = getenv ("PATH"); - if (p == NULL) - return NULL; - path = GNUNET_strdup (p); /* because we write on it */ - buf = GNUNET_malloc (strlen (path) + 20); - pos = path; - - while (NULL != (end = strchr (pos, PATH_SEPARATOR))) - { - *end = '\0'; - sprintf (buf, "%s/%s", pos, binary); - if (GNUNET_DISK_file_test (buf) == GNUNET_YES) - { - GNUNET_free (path); - return buf; - } - pos = end + 1; - } - sprintf (buf, "%s/%s", pos, binary); - if (GNUNET_DISK_file_test (buf) == GNUNET_YES) - { - GNUNET_free (path); - return buf; - } - GNUNET_free (buf); - GNUNET_free (path); - return NULL; -} - -/** - * Check whether the suid bit is set on a file. - * Attempts to find the file using the current - * PATH environment variable as a search path. - * - * @param binary the name of the file to check - */ -static int -check_gnunet_nat_binary(char *binary) -{ - struct stat statbuf; - char *p; -#ifdef MINGW - SOCKET rawsock; -#endif - -#ifdef MINGW - char *binaryexe; - GNUNET_asprintf (&binaryexe, "%s.exe", binary); - p = get_path_from_PATH (binaryexe); - free (binaryexe); -#else - p = get_path_from_PATH (binary); -#endif - if (p == NULL) - return GNUNET_NO; - if (0 != STAT (p, &statbuf)) - { - GNUNET_free (p); - return GNUNET_SYSERR; - } - GNUNET_free (p); -#ifndef MINGW - if ( (0 != (statbuf.st_mode & S_ISUID)) && - (statbuf.st_uid == 0) ) - return GNUNET_YES; - return GNUNET_NO; -#else - rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); - if (INVALID_SOCKET == rawsock) - { - DWORD err = GetLastError (); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "socket (AF_INET, SOCK_RAW, IPPROTO_ICMP) have failed! GLE = %d\n", err); - return GNUNET_NO; /* not running as administrator */ - } - closesocket (rawsock); - return GNUNET_YES; -#endif -} /** * Function called for a quick conversion of the binary address to @@ -2182,7 +1388,7 @@ udp_address_to_string (void *cls, { t4 = addr; af = AF_INET; - port = ntohs (t4->u_port); + port = ntohs (t4->u4_port); memcpy (&a4, &t4->ipv4_addr, sizeof (a4)); sb = &a4; } @@ -2197,6 +1403,58 @@ udp_address_to_string (void *cls, return rbuf; } + +/** + * Our external IP address/port mapping has changed. + * + * @param cls closure, the 'struct LocalAddrList' + * @param add_remove GNUNET_YES to mean the new public IP address, GNUNET_NO to mean + * the previous (now invalid) one + * @param addr either the previous or the new public IP address + * @param addrlen actual lenght of the address + */ +static void +udp_nat_port_map_callback (void *cls, + int add_remove, + const struct sockaddr *addr, + socklen_t addrlen) +{ + struct Plugin *plugin = cls; + struct IPv4UdpAddress u4; + struct IPv6UdpAddress u6; + void *arg; + size_t args; + + /* convert 'addr' to our internal format */ + switch (addr->sa_family) + { + case AF_INET: + GNUNET_assert (addrlen == sizeof (struct sockaddr_in)); + u4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr; + u4.u4_port = ((struct sockaddr_in *) addr)->sin_port; + arg = &u4; + args = sizeof (u4); + break; + case AF_INET6: + GNUNET_assert (addrlen == sizeof (struct sockaddr_in6)); + memcpy (&u6.ipv6_addr, + &((struct sockaddr_in6 *) addr)->sin6_addr, + sizeof (struct in6_addr)); + u6.u6_port = ((struct sockaddr_in6 *) addr)->sin6_port; + arg = &u6; + args = sizeof (u6); + break; + default: + GNUNET_break (0); + return; + } + /* modify our published address list */ + plugin->env->notify_address (plugin->env->cls, + add_remove, + arg, args); +} + + /** * The exported method. Makes the core api available via a global and * returns the udp transport API. @@ -2210,89 +1468,13 @@ libgnunet_plugin_transport_udp_init (void *cls) struct GNUNET_TRANSPORT_PluginFunctions *api; struct Plugin *plugin; int sockets_created; - int behind_nat; - int allow_nat; - int only_nat_addresses; - int use_localaddresses; - char *internal_address; - char *external_address; - struct IPv4UdpAddress v4_address; - - if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (env->cfg, - "nat", - "BEHIND_NAT")) - { - /* We are behind nat (according to the user) */ - if (check_gnunet_nat_binary("gnunet-nat-server") == GNUNET_YES) - behind_nat = GNUNET_YES; - else - { - behind_nat = GNUNET_NO; - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Configuration specified you are behind a NAT, but gnunet-nat-server is not installed properly (suid bit not set)!\n"); - } - } - else - behind_nat = GNUNET_NO; /* We are not behind nat! */ - - if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (env->cfg, - "nat", - "ALLOW_NAT")) - { - if (check_gnunet_nat_binary("gnunet-nat-client") == GNUNET_YES) - allow_nat = GNUNET_YES; /* We will try to connect to NAT'd peers */ - else - { - allow_nat = GNUNET_NO; - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Configuration specified you want to connect to NAT'd peers, but gnunet-nat-client is not installed properly (suid bit not set)!\n"); - } - - } - else - allow_nat = GNUNET_NO; /* We don't want to try to help NAT'd peers */ - - if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (env->cfg, - "nat", - "ONLY_NAT_ADDRESSES")) - only_nat_addresses = GNUNET_YES; /* We will only report our addresses as NAT'd */ - else - only_nat_addresses = GNUNET_NO; /* We will report our addresses as NAT'd and non-NAT'd */ - - external_address = NULL; - if (((GNUNET_YES == behind_nat) || (GNUNET_YES == allow_nat)) && (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (env->cfg, - "nat", - "EXTERNAL_ADDRESS", - &external_address))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Require EXTERNAL_ADDRESS in section `%s' in configuration (either BEHIND_NAT or ALLOW_NAT set to YES)!\n"), - "nat"); - return NULL; - } - - if ((external_address != NULL) && (inet_pton(AF_INET, external_address, &v4_address.ipv4_addr) != 1)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Malformed EXTERNAL_ADDRESS %s given in configuration!\n", external_address); - } - - internal_address = NULL; - if ((GNUNET_YES == behind_nat) && (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (env->cfg, - "nat", - "INTERNAL_ADDRESS", - &internal_address))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Require INTERNAL_ADDRESS in section `%s' in configuration!\n"), - "nat"); - GNUNET_free_non_null(external_address); - return NULL; - } - - if ((internal_address != NULL) && (inet_pton(AF_INET, internal_address, &v4_address.ipv4_addr) != 1)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Malformed INTERNAL_ADDRESS %s given in configuration!\n", internal_address); - } + struct sockaddr_in serverAddrv4; + struct sockaddr_in6 serverAddrv6; + struct sockaddr *serverAddr; + struct sockaddr *addrs[2]; + socklen_t addrlens[2]; + socklen_t addrlen; + unsigned int tries; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (env->cfg, @@ -2300,15 +1482,13 @@ libgnunet_plugin_transport_udp_init (void *cls) "PORT", &port)) port = UDP_NAT_DEFAULT_PORT; - else if (port > 65535) + if (port > 65535) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Given `%s' option is out of range: %llu > %u\n"), "PORT", port, 65535); - GNUNET_free_non_null(external_address); - GNUNET_free_non_null(internal_address); return NULL; } @@ -2317,26 +1497,9 @@ libgnunet_plugin_transport_udp_init (void *cls) GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("MTU %llu for `%s' is probably too low!\n"), mtu, "UDP"); - - use_localaddresses = GNUNET_NO; - if (GNUNET_CONFIGURATION_have_value (env->cfg, - "transport-udp", "USE_LOCALADDR")) - { - use_localaddresses = GNUNET_CONFIGURATION_get_value_yesno (env->cfg, - "transport-udp", - "USE_LOCALADDR"); - } - plugin = GNUNET_malloc (sizeof (struct Plugin)); - plugin->external_address = external_address; - plugin->internal_address = internal_address; plugin->port = port; - plugin->behind_nat = behind_nat; - plugin->allow_nat = allow_nat; - plugin->only_nat_addresses = only_nat_addresses; plugin->env = env; - plugin->use_localaddresses = use_localaddresses; - api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions)); api->cls = plugin; @@ -2344,70 +1507,191 @@ libgnunet_plugin_transport_udp_init (void *cls) api->disconnect = &udp_disconnect; api->address_pretty_printer = &udp_plugin_address_pretty_printer; api->address_to_string = &udp_address_to_string; - api->check_address = &udp_check_address; + api->check_address = &udp_plugin_check_address; + + if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string(env->cfg, + "transport-udp", + "BINDTO", + &plugin->bind_address)) + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, + "Binding udp plugin to specific address: `%s'\n", + plugin->bind_address); + if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string(env->cfg, + "transport-udp", + "BINDTO6", + &plugin->bind6_address)) + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, + "Binding udp plugin to specific address: `%s'\n", + plugin->bind6_address); - if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string(env->cfg, "transport-udp", "BINDTO", &plugin->bind_address)) - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Binding udp plugin to specific address: `%s'\n", plugin->bind_address); - if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string(env->cfg, "transport-udp", "BINDTO6", &plugin->bind6_address)) - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Binding udp plugin to specific address: `%s'\n", plugin->bind6_address); - - if (plugin->behind_nat == GNUNET_NO) + sockets_created = 0; + if ( (GNUNET_YES != + GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, + "gnunetd", + "DISABLEV6"))) { - GNUNET_OS_network_interfaces_list (&process_interfaces, plugin); - } + plugin->udp_sockv6.desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_DGRAM, 0); + if (NULL == plugin->udp_sockv6.desc) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp", "socket"); + } + else + { + memset (&serverAddrv6, 0, sizeof (serverAddrv6)); +#if HAVE_SOCKADDR_IN_SIN_LEN + serverAddrv6.sin6_len = sizeof (serverAddrv6); +#endif - plugin->hostname_dns = GNUNET_RESOLVER_hostname_resolve (AF_UNSPEC, - HOSTNAME_RESOLVE_TIMEOUT, - &process_hostname_ips, - plugin); + serverAddrv6.sin6_family = AF_INET6; + serverAddrv6.sin6_addr = in6addr_any; + if (plugin->bind6_address != NULL) + { + if (1 != inet_pton(AF_INET6, plugin->bind6_address, &serverAddrv6.sin6_addr)) + return 0; + } + serverAddrv6.sin6_port = htons (plugin->port); + addrlen = sizeof (serverAddrv6); + serverAddr = (struct sockaddr *) &serverAddrv6; +#if DEBUG_UDP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Binding to IPv6 port %d\n", + ntohs(serverAddrv6.sin6_port)); +#endif + tries = 0; + while (GNUNET_NETWORK_socket_bind (plugin->udp_sockv6.desc, serverAddr, addrlen) != + GNUNET_OK) + { + serverAddrv6.sin6_port + = htons (GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_STRONG, 33537) + 32000); /* Find a good, non-root port */ +#if DEBUG_UDP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "IPv6 Binding failed, trying new port %d\n", + ntohs(serverAddrv6.sin6_port)); +#endif + tries++; + if (tries > 10) + { + GNUNET_NETWORK_socket_close (plugin->udp_sockv6.desc); + plugin->udp_sockv6.desc = NULL; + break; + } + } + if (plugin->udp_sockv6.desc != NULL) + { + plugin->udp_sockv6.port = ntohs(serverAddrv6.sin6_port); + addrs[sockets_created] = (struct sockaddr*) &serverAddrv6; + addrlens[sockets_created] = sizeof (serverAddrv6); + sockets_created++; + } + } + } - if ((plugin->behind_nat == GNUNET_YES) && (inet_pton(AF_INET, plugin->external_address, &v4_address.ipv4_addr) == 1)) + plugin->udp_sockv4.desc = GNUNET_NETWORK_socket_create (PF_INET, SOCK_DGRAM, 0); + if (NULL == plugin->udp_sockv4.desc) { - v4_address.u_port = htons(0); - plugin->env->notify_address (plugin->env->cls, - "udp", - &v4_address, sizeof(v4_address), GNUNET_TIME_UNIT_FOREVER_REL); - add_to_address_list (plugin, &v4_address.ipv4_addr, sizeof (uint32_t)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Notifying plugin of address %s:0\n", plugin->external_address); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "socket"); } - else if ((plugin->external_address != NULL) && (inet_pton(AF_INET, plugin->external_address, &v4_address.ipv4_addr) == 1)) + else { - v4_address.u_port = htons(plugin->port); - plugin->env->notify_address (plugin->env->cls, - "udp", - &v4_address, sizeof(v4_address), GNUNET_TIME_UNIT_FOREVER_REL); - add_to_address_list (plugin, &v4_address.ipv4_addr, sizeof (uint32_t)); + memset (&serverAddrv4, 0, sizeof (serverAddrv4)); +#if HAVE_SOCKADDR_IN_SIN_LEN + serverAddrv4.sin_len = sizeof (serverAddrv4); +#endif + serverAddrv4.sin_family = AF_INET; + serverAddrv4.sin_addr.s_addr = INADDR_ANY; + if (plugin->bind_address != NULL) + { + if (1 != inet_pton(AF_INET, plugin->bind_address, &serverAddrv4.sin_addr)) + return 0; + } + serverAddrv4.sin_port = htons (plugin->port); + addrlen = sizeof (serverAddrv4); + serverAddr = (struct sockaddr *) &serverAddrv4; +#if DEBUG_UDP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Binding to IPv4 port %d\n", + ntohs(serverAddrv4.sin_port)); +#endif + tries = 0; + while (GNUNET_NETWORK_socket_bind (plugin->udp_sockv4.desc, serverAddr, addrlen) != + GNUNET_OK) + { + serverAddrv4.sin_port = htons (GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_STRONG, 33537) + 32000); /* Find a good, non-root port */ +#if DEBUG_UDP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "IPv4 Binding failed, trying new port %d\n", + ntohs(serverAddrv4.sin_port)); +#endif + tries++; + if (tries > 10) + { + GNUNET_NETWORK_socket_close (plugin->udp_sockv4.desc); + plugin->udp_sockv4.desc = NULL; + break; + } + } + if (plugin->udp_sockv4.desc != NULL) + { + plugin->udp_sockv4.port = ntohs(serverAddrv4.sin_port); + addrs[sockets_created] = (struct sockaddr*) &serverAddrv4; + addrlens[sockets_created] = sizeof (serverAddrv4); + sockets_created++; + } } - sockets_created = udp_transport_server_start (plugin); + plugin->rs = GNUNET_NETWORK_fdset_create (); + GNUNET_NETWORK_fdset_zero (plugin->rs); + if (NULL != plugin->udp_sockv4.desc) + GNUNET_NETWORK_fdset_set (plugin->rs, + plugin->udp_sockv4.desc); + if (NULL != plugin->udp_sockv6.desc) + GNUNET_NETWORK_fdset_set (plugin->rs, + plugin->udp_sockv6.desc); + + plugin->select_task = + GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, + GNUNET_SCHEDULER_NO_TASK, + GNUNET_TIME_UNIT_FOREVER_REL, plugin->rs, + NULL, &udp_plugin_select, plugin); if (sockets_created == 0) GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Failed to open UDP sockets\n")); + plugin->nat = GNUNET_NAT_register (env->cfg, + GNUNET_NO, + port, + sockets_created, + (const struct sockaddr**) addrs, addrlens, + &udp_nat_port_map_callback, + &udp_plugin_reversal_callback, + plugin); return api; } + void * libgnunet_plugin_transport_udp_done (void *cls) { struct GNUNET_TRANSPORT_PluginFunctions *api = cls; struct Plugin *plugin = api->cls; - struct LocalAddrList *lal; - udp_transport_server_stop (plugin); - if (NULL != plugin->hostname_dns) + if (plugin->select_task != GNUNET_SCHEDULER_NO_TASK) { - GNUNET_RESOLVER_request_cancel (plugin->hostname_dns); - plugin->hostname_dns = NULL; + GNUNET_SCHEDULER_cancel (plugin->select_task); + plugin->select_task = GNUNET_SCHEDULER_NO_TASK; } - - GNUNET_NETWORK_fdset_destroy (plugin->rs); - while (NULL != (lal = plugin->lal_head)) + if (plugin->udp_sockv4.desc != NULL) + { + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (plugin->udp_sockv4.desc)); + plugin->udp_sockv4.desc = NULL; + } + if (plugin->udp_sockv6.desc != NULL) { - GNUNET_CONTAINER_DLL_remove (plugin->lal_head, - plugin->lal_tail, - lal); - GNUNET_free (lal); + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (plugin->udp_sockv6.desc)); + plugin->udp_sockv6.desc = NULL; } + GNUNET_NETWORK_fdset_destroy (plugin->rs); + GNUNET_NAT_unregister (plugin->nat); + plugin->nat = NULL; GNUNET_free (plugin); GNUNET_free (api); return NULL; diff --git a/src/transport/plugin_transport_unix.c b/src/transport/plugin_transport_unix.c index 835f01611..27187a9d6 100644 --- a/src/transport/plugin_transport_unix.c +++ b/src/transport/plugin_transport_unix.c @@ -217,29 +217,6 @@ struct RetrySendContext struct RetryList *retry_list_entry; }; -/** - * Local network addresses (actual unix path follows). - */ -struct LocalAddrList -{ - - /** - * This is a doubly linked list. - */ - struct LocalAddrList *next; - - /** - * This is a doubly linked list. - */ - struct LocalAddrList *prev; - - /** - * Number of bytes of the address that follow - */ - size_t size; - -}; - /** * UNIX NAT "Session" @@ -339,16 +316,6 @@ struct Plugin */ uint16_t port; - /** - * List of our IP addresses. - */ - struct LocalAddrList *lal_head; - - /** - * Tail of our IP address list. - */ - struct LocalAddrList *lal_tail; - /** * FD Read set */ @@ -763,30 +730,6 @@ unix_plugin_send (void *cls, } -static void -add_to_address_list (struct Plugin *plugin, - const void *arg, - size_t arg_size) -{ - struct LocalAddrList *lal; - - lal = plugin->lal_head; - while (NULL != lal) - { - if ( (lal->size == arg_size) && - (0 == memcmp (&lal[1], arg, arg_size)) ) - return; - lal = lal->next; - } - lal = GNUNET_malloc (sizeof (struct LocalAddrList) + arg_size); - lal->size = arg_size; - memcpy (&lal[1], arg, arg_size); - GNUNET_CONTAINER_DLL_insert (plugin->lal_head, - plugin->lal_tail, - lal); -} - - /** * Demultiplexer for UNIX messages * @@ -1197,8 +1140,10 @@ libgnunet_plugin_transport_unix_init (void *cls) plugin = GNUNET_malloc (sizeof (struct Plugin)); plugin->port = port; plugin->env = env; - GNUNET_asprintf(&plugin->unix_socket_path, "/tmp/unix-plugin-sock.%d", plugin->port); - + GNUNET_asprintf (&plugin->unix_socket_path, + "/tmp/unix-plugin-sock.%d", + plugin->port); + api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions)); api->cls = plugin; @@ -1207,19 +1152,15 @@ libgnunet_plugin_transport_unix_init (void *cls) api->address_pretty_printer = &unix_plugin_address_pretty_printer; api->address_to_string = &unix_address_to_string; api->check_address = &unix_check_address; - - add_to_address_list (plugin, plugin->unix_socket_path, strlen(plugin->unix_socket_path) + 1); - sockets_created = unix_transport_server_start (plugin); if (sockets_created == 0) GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Failed to open UNIX sockets\n")); plugin->env->notify_address(plugin->env->cls, - "unix", + GNUNET_YES, plugin->unix_socket_path, - strlen(plugin->unix_socket_path) + 1, - GNUNET_TIME_UNIT_FOREVER_REL); + strlen(plugin->unix_socket_path) + 1); return api; } @@ -1228,18 +1169,10 @@ libgnunet_plugin_transport_unix_done (void *cls) { struct GNUNET_TRANSPORT_PluginFunctions *api = cls; struct Plugin *plugin = api->cls; - struct LocalAddrList *lal; unix_transport_server_stop (plugin); GNUNET_NETWORK_fdset_destroy (plugin->rs); - while (NULL != (lal = plugin->lal_head)) - { - GNUNET_CONTAINER_DLL_remove (plugin->lal_head, - plugin->lal_tail, - lal); - GNUNET_free (lal); - } GNUNET_free (plugin); GNUNET_free (api); return NULL; diff --git a/src/transport/test_plugin_transport.c b/src/transport/test_plugin_transport.c index ca69b7601..0c335d6ba 100644 --- a/src/transport/test_plugin_transport.c +++ b/src/transport/test_plugin_transport.c @@ -37,7 +37,7 @@ #include "gnunet_transport_plugin.h" #include "transport.h" -#define VERBOSE GNUNET_NO +#define VERBOSE GNUNET_YES /** * How long until we give up on transmitting the message? diff --git a/src/transport/test_transport_api.c b/src/transport/test_transport_api.c index f51e96ff2..ac818d557 100644 --- a/src/transport/test_transport_api.c +++ b/src/transport/test_transport_api.c @@ -37,7 +37,7 @@ #include "transport.h" #include "transport-testing.h" -#define VERBOSE GNUNET_NO +#define VERBOSE GNUNET_YES #define VERBOSE_ARM GNUNET_NO @@ -385,6 +385,7 @@ try_connect (void *cls, { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Asking peers to connect...\n"); + /* FIXME: 'pX.id' may still be all-zeros here... */ GNUNET_TRANSPORT_try_connect (p2.th, &p1.id); GNUNET_TRANSPORT_try_connect (p1.th, diff --git a/src/transport/test_transport_api_tcp_peer1.conf b/src/transport/test_transport_api_tcp_peer1.conf index f677f9001..d6b0cc8ab 100644 --- a/src/transport/test_transport_api_tcp_peer1.conf +++ b/src/transport/test_transport_api_tcp_peer1.conf @@ -1,5 +1,4 @@ [transport-tcp] -USE_LOCALADDR = YES PORT = 12000 [nat] @@ -32,14 +31,14 @@ MINIMUM-FRIENDS = 0 [transport] PLUGINS = tcp -#DEBUG = YES +DEBUG = YES #PREFIX = xterm -T transport2 -e gdb --command=cmd --args -#PREFIX = valgrind --tool=memcheck --leak-check=full --log-file=transport%p +#PREFIX = valgrind --tool=memcheck --leak-check=full --log-file=transport%p.vg ACCEPT_FROM6 = ::1; ACCEPT_FROM = 127.0.0.1; NEIGHBOUR_LIMIT = 50 -BINARY = gnunet-service-transport -#BINARY = /home/mrwiggles/documents/research/gnunet/gnunet-ng/src/transport/.libs/gnunet-service-transport +#BINARY = gnunet-service-transport +#BINARY = /home/grothoff/bin/gnunet-service-transport CONFIG = $DEFAULTCONFIG HOME = $SERVICEHOME HOSTNAME = localhost diff --git a/src/transport/test_transport_api_tcp_peer2.conf b/src/transport/test_transport_api_tcp_peer2.conf index a0074f155..19b8b42d3 100644 --- a/src/transport/test_transport_api_tcp_peer2.conf +++ b/src/transport/test_transport_api_tcp_peer2.conf @@ -1,6 +1,5 @@ [transport-tcp] PORT = 0 -USE_LOCALADDR = YES [nat] DISABLEV6 = YES @@ -32,18 +31,18 @@ MINIMUM-FRIENDS = 0 [transport] PLUGINS = tcp -#DEBUG = YES +DEBUG = YES ACCEPT_FROM6 = ::1; ACCEPT_FROM = 127.0.0.1; NEIGHBOUR_LIMIT = 50 -#BINARY = /home/mrwiggles/documents/research/gnunet/gnunet-ng/src/transport/.libs/gnunet-service-transport -BINARY = gnunet-service-transport +#BINARY = /home/grothoff/bin/gnunet-service-transport +#BINARY = gnunet-service-transport CONFIG = $DEFAULTCONFIG HOME = $SERVICEHOME HOSTNAME = localhost PORT = 12010 UNIXPATH = /tmp/gnunet-p2-service-transport.sock -#PREFIX = valgrind --tool=callgrind +#PREFIX = valgrind --tool=memcheck --leak-check=full --log-file=transport%p.vg #PREFIX = xterm -T transport2 -e gdb --command=cmd --args #PREFIX = valgrind --leak-check=full diff --git a/src/transport/transport.h b/src/transport/transport.h index f05ef3d55..504cbe62b 100644 --- a/src/transport/transport.h +++ b/src/transport/transport.h @@ -36,7 +36,7 @@ #define ATS_MAX_EXEC_DURATION GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 3) #define ATS_MAX_ITERATIONS INT_MAX -#define DEBUG_TRANSPORT GNUNET_NO +#define DEBUG_TRANSPORT GNUNET_YES #define DEBUG_TRANSPORT_TIMEOUT GNUNET_NO diff --git a/src/util/resolver_api.c b/src/util/resolver_api.c index 6127badeb..4261f495c 100644 --- a/src/util/resolver_api.c +++ b/src/util/resolver_api.c @@ -348,9 +348,14 @@ handle_response (void *cls, #endif if (msg == NULL) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Timeout trying to resolve IP address `%s'.\n"), - GNUNET_a2s ((const void*) &rh[1], rh->data_len)); + if (NULL != rh->name_callback) + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Timeout trying to resolve IP address `%s'.\n"), + GNUNET_a2s ((const void*) &rh[1], rh->data_len)); + else + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Timeout trying to resolve hostname `%s'.\n"), + (const char *) &rh[1]); if (rh->was_transmitted != GNUNET_SYSERR) { if (NULL != rh->name_callback) -- 2.25.1