+Sat Jan 7 16:41:34 CET 2017
+ Converting NAT library to new NAT service (and
+ splitting of nat-auto service for auto-configuration). -CG
--- /dev/null
+/*
+ This file is part of GNUnet.
+ Copyright (C) 2011, 2016 GNUnet e.V.
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/**
+ * @file nat/nat_test.c
+ * @brief functions to test if the NAT configuration is successful at achieving NAT traversal (with the help of a gnunet-nat-server)
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_nat_lib.h"
+#include "nat.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "nat", __VA_ARGS__)
+
+#define NAT_SERVER_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
+
+/**
+ * Entry we keep for each incoming connection.
+ */
+struct NatActivity
+{
+ /**
+ * This is a doubly-linked list.
+ */
+ struct NatActivity *next;
+
+ /**
+ * This is a doubly-linked list.
+ */
+ struct NatActivity *prev;
+
+ /**
+ * Socket of the incoming connection.
+ */
+ struct GNUNET_NETWORK_Handle *sock;
+
+ /**
+ * Handle of the master context.
+ */
+ struct GNUNET_NAT_Test *h;
+
+ /**
+ * Task reading from the incoming connection.
+ */
+ struct GNUNET_SCHEDULER_Task *rtask;
+};
+
+
+/**
+ * Entry we keep for each connection to the gnunet-nat-service.
+ */
+struct ClientActivity
+{
+ /**
+ * This is a doubly-linked list.
+ */
+ struct ClientActivity *next;
+
+ /**
+ * This is a doubly-linked list.
+ */
+ struct ClientActivity *prev;
+
+ /**
+ * Socket of the incoming connection.
+ */
+ struct GNUNET_MQ_Handle *mq;
+
+ /**
+ * Handle to overall NAT test.
+ */
+ struct GNUNET_NAT_Test *h;
+
+};
+
+
+/**
+ * Handle to a NAT test.
+ */
+struct GNUNET_NAT_Test
+{
+
+ /**
+ * Configuration used
+ */
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ /**
+ * Function to call with success report
+ */
+ GNUNET_NAT_TestCallback report;
+
+ /**
+ * Closure for @e report.
+ */
+ void *report_cls;
+
+ /**
+ * Handle to NAT traversal in use
+ */
+ struct GNUNET_NAT_Handle *nat;
+
+ /**
+ * Handle to listen socket, or NULL
+ */
+ struct GNUNET_NETWORK_Handle *lsock;
+
+ /**
+ * Head of list of nat activities.
+ */
+ struct NatActivity *na_head;
+
+ /**
+ * Tail of list of nat activities.
+ */
+ struct NatActivity *na_tail;
+
+ /**
+ * Head of list of client activities.
+ */
+ struct ClientActivity *ca_head;
+
+ /**
+ * Tail of list of client activities.
+ */
+ struct ClientActivity *ca_tail;
+
+ /**
+ * Identity of task for the listen socket (if any)
+ */
+ struct GNUNET_SCHEDULER_Task *ltask;
+
+ /**
+ * Task identifier for the timeout (if any)
+ */
+ struct GNUNET_SCHEDULER_Task *ttask;
+
+ /**
+ * #GNUNET_YES if we're testing TCP
+ */
+ int is_tcp;
+
+ /**
+ * Data that should be transmitted or source-port.
+ */
+ uint16_t data;
+
+ /**
+ * Advertised port to the other peer.
+ */
+ uint16_t adv_port;
+
+ /**
+ * Status code to be reported to the timeout/status call
+ */
+ enum GNUNET_NAT_StatusCode status;
+};
+
+
+/**
+ * Function called from #GNUNET_NAT_register whenever someone asks us
+ * to do connection reversal.
+ *
+ * @param cls closure, our `struct GNUNET_NAT_Handle`
+ * @param addr public IP address of the other peer
+ * @param addrlen actual lenght of the @a addr
+ */
+static void
+reversal_cb (void *cls,
+ const struct sockaddr *addr,
+ socklen_t addrlen)
+{
+ struct GNUNET_NAT_Test *h = cls;
+ const struct sockaddr_in *sa;
+
+ if (sizeof (struct sockaddr_in) != addrlen)
+ return;
+ sa = (const struct sockaddr_in *) addr;
+ if (h->data != sa->sin_port)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received connection reversal request for wrong port\n");
+ return; /* wrong port */
+ }
+ /* report success */
+ h->report (h->report_cls,
+ GNUNET_NAT_ERROR_SUCCESS);
+}
+
+
+/**
+ * Activity on our incoming socket. Read data from the
+ * incoming connection.
+ *
+ * @param cls the `struct GNUNET_NAT_Test`
+ */
+static void
+do_udp_read (void *cls)
+{
+ struct GNUNET_NAT_Test *tst = cls;
+ uint16_t data;
+ const struct GNUNET_SCHEDULER_TaskContext *tc;
+
+ tc = GNUNET_SCHEDULER_get_task_context ();
+ tst->ltask =
+ GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+ tst->lsock,
+ &do_udp_read,
+ tst);
+ if ((NULL != tc->write_ready) &&
+ (GNUNET_NETWORK_fdset_isset (tc->read_ready,
+ tst->lsock)) &&
+ (sizeof (data) ==
+ GNUNET_NETWORK_socket_recv (tst->lsock,
+ &data,
+ sizeof (data))))
+ {
+ if (data == tst->data)
+ tst->report (tst->report_cls,
+ GNUNET_NAT_ERROR_SUCCESS);
+ else
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received data mismatches expected value\n");
+ }
+ else
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Failed to receive data from inbound connection\n");
+}
+
+
+/**
+ * Activity on our incoming socket. Read data from the
+ * incoming connection.
+ *
+ * @param cls the `struct NatActivity`
+ */
+static void
+do_read (void *cls)
+{
+ struct NatActivity *na = cls;
+ struct GNUNET_NAT_Test *tst;
+ uint16_t data;
+ const struct GNUNET_SCHEDULER_TaskContext *tc;
+
+ tc = GNUNET_SCHEDULER_get_task_context ();
+ na->rtask = NULL;
+ tst = na->h;
+ GNUNET_CONTAINER_DLL_remove (tst->na_head,
+ tst->na_tail,
+ na);
+ if ((NULL != tc->write_ready) &&
+ (GNUNET_NETWORK_fdset_isset (tc->read_ready,
+ na->sock)) &&
+ (sizeof (data) ==
+ GNUNET_NETWORK_socket_recv (na->sock,
+ &data,
+ sizeof (data))))
+ {
+ if (data == tst->data)
+ tst->report (tst->report_cls,
+ GNUNET_NAT_ERROR_SUCCESS);
+ else
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received data does not match expected value\n");
+ }
+ else
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Failed to receive data from inbound connection\n");
+ GNUNET_NETWORK_socket_close (na->sock);
+ GNUNET_free (na);
+}
+
+
+/**
+ * Activity on our listen socket. Accept the
+ * incoming connection.
+ *
+ * @param cls the `struct GNUNET_NAT_Test`
+ */
+static void
+do_accept (void *cls)
+{
+ struct GNUNET_NAT_Test *tst = cls;
+ struct GNUNET_NETWORK_Handle *s;
+ struct NatActivity *wl;
+
+ tst->ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+ tst->lsock,
+ &do_accept,
+ tst);
+ s = GNUNET_NETWORK_socket_accept (tst->lsock,
+ NULL,
+ NULL);
+ if (NULL == s)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO,
+ "accept");
+ return; /* odd error */
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Got an inbound connection, waiting for data\n");
+ wl = GNUNET_new (struct NatActivity);
+ wl->sock = s;
+ wl->h = tst;
+ wl->rtask =
+ GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+ wl->sock,
+ &do_read,
+ wl);
+ GNUNET_CONTAINER_DLL_insert (tst->na_head,
+ tst->na_tail,
+ wl);
+}
+
+
+/**
+ * We got disconnected from the NAT server. Stop
+ * waiting for a reply.
+ *
+ * @param cls the `struct ClientActivity`
+ * @param error error code
+ */
+static void
+mq_error_handler (void *cls,
+ enum GNUNET_MQ_Error error)
+{
+ struct ClientActivity *ca = cls;
+ struct GNUNET_NAT_Test *tst = ca->h;
+
+ GNUNET_CONTAINER_DLL_remove (tst->ca_head,
+ tst->ca_tail,
+ ca);
+ GNUNET_MQ_destroy (ca->mq);
+ GNUNET_free (ca);
+}
+
+
+/**
+ * Address-callback, used to send message to gnunet-nat-server.
+ *
+ * @param cls closure
+ * @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 length of the @a addr
+ */
+static void
+addr_cb (void *cls,
+ int add_remove,
+ const struct sockaddr *addr,
+ socklen_t addrlen)
+{
+ struct GNUNET_NAT_Test *h = cls;
+ struct ClientActivity *ca;
+ struct GNUNET_MQ_Envelope *env;
+ struct GNUNET_NAT_TestMessage *msg;
+ const struct sockaddr_in *sa;
+
+ if (GNUNET_YES != add_remove)
+ return;
+ if (addrlen != sizeof (struct sockaddr_in))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "NAT test ignores IPv6 address `%s' returned from NAT library\n",
+ GNUNET_a2s (addr,
+ addrlen));
+ return; /* ignore IPv6 here */
+ }
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ "Asking gnunet-nat-server to connect to `%s'\n",
+ GNUNET_a2s (addr,
+ addrlen));
+
+ ca = GNUNET_new (struct ClientActivity);
+ ca->h = h;
+ ca->mq = GNUNET_CLIENT_connecT (h->cfg,
+ "gnunet-nat-server",
+ NULL,
+ &mq_error_handler,
+ ca);
+ if (NULL == ca->mq)
+ {
+ GNUNET_free (ca);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Failed to connect to `gnunet-nat-server'\n"));
+ return;
+ }
+ GNUNET_CONTAINER_DLL_insert (h->ca_head,
+ h->ca_tail,
+ ca);
+ sa = (const struct sockaddr_in *) addr;
+ env = GNUNET_MQ_msg (msg,
+ GNUNET_MESSAGE_TYPE_NAT_TEST);
+ msg->dst_ipv4 = sa->sin_addr.s_addr;
+ msg->dport = sa->sin_port;
+ msg->data = h->data;
+ msg->is_tcp = htonl ((uint32_t) h->is_tcp);
+ GNUNET_MQ_send (ca->mq,
+ env);
+}
+
+
+/**
+ * Timeout task for a nat test.
+ * Calls the report-callback with a timeout return value
+ *
+ * Destroys the nat handle after the callback has been processed.
+ *
+ * @param cls handle to the timed out NAT test
+ */
+static void
+do_timeout (void *cls)
+{
+ struct GNUNET_NAT_Test *nh = cls;
+
+ nh->ttask = NULL;
+ nh->report (nh->report_cls,
+ (GNUNET_NAT_ERROR_SUCCESS == nh->status)
+ ? GNUNET_NAT_ERROR_TIMEOUT
+ : nh->status);
+}
+
+
+/**
+ * Start testing if NAT traversal works using the
+ * given configuration (IPv4-only).
+ *
+ * ALL failures are reported directly to the report callback
+ *
+ * @param cfg configuration for the NAT traversal
+ * @param is_tcp #GNUNET_YES to test TCP, #GNUNET_NO to test UDP
+ * @param bnd_port port to bind to, 0 for connection reversal
+ * @param adv_port externally advertised port to use
+ * @param timeout delay after which the test should be aborted
+ * @param report function to call with the result of the test
+ * @param report_cls closure for @a report
+ * @return handle to cancel NAT test or NULL. The error is always indicated via the report callback
+ */
+struct GNUNET_NAT_Test *
+GNUNET_NAT_test_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ int is_tcp,
+ uint16_t bnd_port,
+ uint16_t adv_port,
+ struct GNUNET_TIME_Relative timeout,
+ GNUNET_NAT_TestCallback report,
+ void *report_cls)
+{
+ struct GNUNET_NAT_Test *nh;
+ struct sockaddr_in sa;
+ const struct sockaddr *addrs[] = {
+ (const struct sockaddr *) &sa
+ };
+ const socklen_t addrlens[] = {
+ sizeof (sa)
+ };
+
+ memset (&sa, 0, sizeof (sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons (bnd_port);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa.sin_len = sizeof (sa);
+#endif
+
+ nh = GNUNET_new (struct GNUNET_NAT_Test);
+ nh->cfg = cfg;
+ nh->is_tcp = is_tcp;
+ nh->data = bnd_port;
+ nh->adv_port = adv_port;
+ nh->report = report;
+ nh->report_cls = report_cls;
+ nh->status = GNUNET_NAT_ERROR_SUCCESS;
+ if (0 == bnd_port)
+ {
+ nh->nat
+ = GNUNET_NAT_register (cfg,
+ is_tcp,
+ 0,
+ 0,
+ NULL,
+ NULL,
+ &addr_cb,
+ &reversal_cb,
+ nh,
+ NULL);
+ }
+ else
+ {
+ nh->lsock =
+ GNUNET_NETWORK_socket_create (AF_INET,
+ (is_tcp ==
+ GNUNET_YES) ? SOCK_STREAM : SOCK_DGRAM,
+ 0);
+ if ((nh->lsock == NULL) ||
+ (GNUNET_OK !=
+ GNUNET_NETWORK_socket_bind (nh->lsock,
+ (const struct sockaddr *) &sa,
+ sizeof (sa))))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Failed to create listen socket bound to `%s' for NAT test: %s\n"),
+ GNUNET_a2s ((const struct sockaddr *) &sa,
+ sizeof (sa)),
+ STRERROR (errno));
+ if (NULL != nh->lsock)
+ {
+ GNUNET_NETWORK_socket_close (nh->lsock);
+ nh->lsock = NULL;
+ }
+ nh->status = GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR;
+ nh->ttask = GNUNET_SCHEDULER_add_now (&do_timeout,
+ nh);
+ return nh;
+ }
+ if (GNUNET_YES == is_tcp)
+ {
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_NETWORK_socket_listen (nh->lsock,
+ 5));
+ nh->ltask =
+ GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+ nh->lsock,
+ &do_accept,
+ nh);
+ }
+ else
+ {
+ nh->ltask =
+ GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+ nh->lsock,
+ &do_udp_read,
+ nh);
+ }
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ "NAT test listens on port %u (%s)\n",
+ bnd_port,
+ (GNUNET_YES == is_tcp) ? "tcp" : "udp");
+ nh->nat = GNUNET_NAT_register (cfg,
+ is_tcp,
+ adv_port,
+ 1,
+ addrs,
+ addrlens,
+ &addr_cb,
+ NULL,
+ nh,
+ NULL);
+ if (NULL == nh->nat)
+ {
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _("NAT test failed to start NAT library\n"));
+ if (NULL != nh->ltask)
+ {
+ GNUNET_SCHEDULER_cancel (nh->ltask);
+ nh->ltask = NULL;
+ }
+ if (NULL != nh->lsock)
+ {
+ GNUNET_NETWORK_socket_close (nh->lsock);
+ nh->lsock = NULL;
+ }
+ nh->status = GNUNET_NAT_ERROR_NAT_REGISTER_FAILED;
+ nh->ttask = GNUNET_SCHEDULER_add_now (&do_timeout,
+ nh);
+ return nh;
+ }
+ }
+ nh->ttask = GNUNET_SCHEDULER_add_delayed (timeout,
+ &do_timeout,
+ nh);
+ return nh;
+}
+
+
+/**
+ * Stop an active NAT test.
+ *
+ * @param tst test to stop.
+ */
+void
+GNUNET_NAT_test_stop (struct GNUNET_NAT_Test *tst)
+{
+ struct NatActivity *pos;
+ struct ClientActivity *cpos;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Stopping NAT test\n");
+ while (NULL != (cpos = tst->ca_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (tst->ca_head,
+ tst->ca_tail,
+ cpos);
+ GNUNET_MQ_destroy (cpos->mq);
+ GNUNET_free (cpos);
+ }
+ while (NULL != (pos = tst->na_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (tst->na_head,
+ tst->na_tail,
+ pos);
+ GNUNET_SCHEDULER_cancel (pos->rtask);
+ GNUNET_NETWORK_socket_close (pos->sock);
+ GNUNET_free (pos);
+ }
+ if (NULL != tst->ttask)
+ {
+ GNUNET_SCHEDULER_cancel (tst->ttask);
+ tst->ttask = NULL;
+ }
+ if (NULL != tst->ltask)
+ {
+ GNUNET_SCHEDULER_cancel (tst->ltask);
+ tst->ltask = NULL;
+ }
+ if (NULL != tst->lsock)
+ {
+ GNUNET_NETWORK_socket_close (tst->lsock);
+ tst->lsock = NULL;
+ }
+ if (NULL != tst->nat)
+ {
+ GNUNET_NAT_unregister (tst->nat);
+ tst->nat = NULL;
+ }
+ GNUNET_free (tst);
+}
+
+/* end of nat_test.c */
* - merge client handle and autoconfig context
* - implement "more" autoconfig:
* + re-work gnunet-nat-server & integrate!
+ * + integrate "legacy" code
* + test manually punched NAT (how?)
*/
#include "platform.h"
* Define "main" method using service macro.
*/
GNUNET_SERVICE_MAIN
-("nat",
+("nat-auto",
GNUNET_SERVICE_OPTION_NONE,
&run,
&client_connect_cb,
--- /dev/null
+/*
+ This file is part of GNUnet.
+ Copyright (C) 2015 GNUnet e.V.
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/**
+ * @file nat/nat_auto.c
+ * @brief functions for auto-configuration of the network
+ * @author Christian Grothoff
+ * @author Bruno Cabral
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_resolver_service.h"
+#include "gnunet_nat_lib.h"
+#include "nat.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "nat", __VA_ARGS__)
+
+
+/**
+ * How long do we wait for the NAT test to report success?
+ */
+#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
+
+#define NAT_SERVER_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
+
+/**
+ * Phases of the auto configuration.
+ */
+enum AutoPhase
+{
+ /**
+ * Initial start value.
+ */
+ AUTO_INIT = 0,
+
+ /**
+ * Test our external IP.
+ */
+ AUTO_EXTERNAL_IP,
+
+ /**
+ * Test our external IP.
+ */
+ AUTO_STUN,
+
+ /**
+ * Test our internal IP.
+ */
+ AUTO_LOCAL_IP,
+
+ /**
+ * Test if NAT was punched.
+ */
+ AUTO_NAT_PUNCHED,
+
+ /**
+ * Test if UPnP is working.
+ */
+ AUTO_UPNPC,
+
+ /**
+ * Test if ICMP server works.
+ */
+ AUTO_ICMP_SERVER,
+
+ /**
+ * Test if ICMP client works.
+ */
+ AUTO_ICMP_CLIENT,
+
+ /**
+ * Last phase, we're done.
+ */
+ AUTO_DONE
+
+};
+
+
+/**
+ * Handle to auto-configuration in progress.
+ */
+struct GNUNET_NAT_AutoHandle
+{
+
+ /**
+ * Handle to the active NAT test.
+ */
+ struct GNUNET_NAT_Test *tst;
+
+ /**
+ * Function to call when done.
+ */
+ GNUNET_NAT_AutoResultCallback fin_cb;
+
+ /**
+ * Closure for @e fin_cb.
+ */
+ void *fin_cb_cls;
+
+ /**
+ * Handle for active 'GNUNET_NAT_mini_get_external_ipv4'-operation.
+ */
+ struct GNUNET_NAT_ExternalHandle *eh;
+
+ /**
+ * Current configuration (with updates from previous phases)
+ */
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ /**
+ * Original configuration (used to calculate differences)
+ */
+ struct GNUNET_CONFIGURATION_Handle *initial_cfg;
+
+ /**
+ * Task identifier for the timeout.
+ */
+ struct GNUNET_SCHEDULER_Task *task;
+
+ /**
+ * Message queue to the gnunet-nat-server.
+ */
+ struct GNUNET_MQ_Handle *mq;
+
+ /**
+ * Where are we in the test?
+ */
+ enum AutoPhase phase;
+
+ /**
+ * Situation of the NAT
+ */
+ enum GNUNET_NAT_Type type;
+
+ /**
+ * Do we have IPv6?
+ */
+ int have_v6;
+
+ /**
+ * UPnP already set the external ip address ?
+ */
+ int upnp_set_external_address;
+
+ /**
+ * Did the external server connected back ?
+ */
+ int connected_back;
+
+ /**
+ * Address detected by STUN
+ */
+ char *stun_ip;
+
+ unsigned int stun_port;
+
+ /**
+ * Internal IP is the same as the public one ?
+ */
+ int internal_ip_is_public;
+
+ /**
+ * Error code for better debugging and user feedback
+ */
+ enum GNUNET_NAT_StatusCode ret;
+};
+
+
+/**
+ * The listen socket of the service for IPv4
+ */
+static struct GNUNET_NETWORK_Handle *lsock4;
+
+/**
+ * The listen task ID for IPv4
+ */
+static struct GNUNET_SCHEDULER_Task *ltask4;
+
+/**
+ * The port the test service is running on (default 7895)
+ */
+static unsigned long long port = 7895;
+
+static char *stun_server = "stun.ekiga.net";
+
+static unsigned int stun_port = 3478;
+
+
+/**
+ * Run the next phase of the auto test.
+ *
+ * @param ah auto test handle
+ */
+static void
+next_phase (struct GNUNET_NAT_AutoHandle *ah);
+
+
+static void
+process_stun_reply(struct sockaddr_in *answer,
+ struct GNUNET_NAT_AutoHandle *ah)
+{
+ ah->stun_ip = inet_ntoa(answer->sin_addr);
+ ah->stun_port = ntohs (answer->sin_port);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "External IP is: %s , with port %u\n",
+ ah->stun_ip,
+ ah->stun_port);
+ next_phase (ah);
+}
+
+
+/**
+ * Function that terminates the test.
+ */
+static void
+stop_stun ()
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Stopping STUN and quitting...\n");
+ /* Clean task */
+ if (NULL != ltask4)
+ {
+ GNUNET_SCHEDULER_cancel (ltask4);
+ ltask4 = NULL;
+ }
+ /* Clean socket */
+ if (NULL != lsock4)
+ {
+ GNUNET_NETWORK_socket_close (lsock4);
+ lsock4 = NULL;
+ }
+}
+
+
+/**
+ * Activity on our incoming socket. Read data from the
+ * incoming connection.
+ *
+ * @param cls
+ */
+static void
+do_udp_read (void *cls)
+{
+ struct GNUNET_NAT_AutoHandle *ah = cls;
+ unsigned char reply_buf[1024];
+ ssize_t rlen;
+ struct sockaddr_in answer;
+ const struct GNUNET_SCHEDULER_TaskContext *tc;
+
+ tc = GNUNET_SCHEDULER_get_task_context ();
+ if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
+ (GNUNET_NETWORK_fdset_isset (tc->read_ready,
+ lsock4)))
+ {
+ rlen = GNUNET_NETWORK_socket_recv (lsock4,
+ reply_buf,
+ sizeof (reply_buf));
+
+ //Lets handle the packet
+ memset (&answer, 0, sizeof(struct sockaddr_in));
+ if (ah->phase == AUTO_NAT_PUNCHED)
+ {
+ //Destroy the connection
+ GNUNET_NETWORK_socket_close (lsock4);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "The external server was able to connect back");
+ ah->connected_back = GNUNET_YES;
+ next_phase (ah);
+ }
+ else
+ {
+ if (GNUNET_OK ==
+ GNUNET_NAT_stun_handle_packet (reply_buf, rlen, &answer))
+ {
+ //Process the answer
+ process_stun_reply (&answer, ah);
+ }
+ else
+ {
+ next_phase (ah);
+ }
+ }
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "TIMEOUT while waiting for an answer\n");
+ if (ah->phase == AUTO_NAT_PUNCHED)
+ {
+ stop_stun();
+ }
+
+ next_phase (ah);
+ }
+}
+
+
+/**
+ * Create an IPv4 listen socket bound to our port.
+ *
+ * @return NULL on error
+ */
+static struct GNUNET_NETWORK_Handle *
+bind_v4 ()
+{
+ struct GNUNET_NETWORK_Handle *ls;
+ struct sockaddr_in sa4;
+ int eno;
+
+ memset (&sa4, 0, sizeof (sa4));
+ sa4.sin_family = AF_INET;
+ sa4.sin_port = htons (port);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa4.sin_len = sizeof (sa4);
+#endif
+ ls = GNUNET_NETWORK_socket_create (AF_INET,
+ SOCK_DGRAM,
+ 0);
+ if (NULL == ls)
+ return NULL;
+ if (GNUNET_OK !=
+ GNUNET_NETWORK_socket_bind (ls, (const struct sockaddr *) &sa4,
+ sizeof (sa4)))
+ {
+ eno = errno;
+ GNUNET_NETWORK_socket_close (ls);
+ errno = eno;
+ return NULL;
+ }
+ return ls;
+}
+
+
+static void
+request_callback (void *cls,
+ enum GNUNET_NAT_StatusCode result)
+{
+ // struct GNUNET_NAT_AutoHandle *ah = cls;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Request callback: stop and quit\n");
+ stop_stun ();
+
+ // next_phase (ah); FIXME this always will be NULL, as called in test_stun()
+}
+
+
+/**
+ * Function called by NAT to report the outcome of the nat-test.
+ * Clean up and update GUI.
+ *
+ * @param cls the auto handle
+ * @param success currently always #GNUNET_OK
+ * @param emsg NULL on success, otherwise an error message
+ */
+static void
+result_callback (void *cls,
+ enum GNUNET_NAT_StatusCode ret)
+{
+ struct GNUNET_NAT_AutoHandle *ah = cls;
+
+ if (GNUNET_NAT_ERROR_SUCCESS == ret)
+ GNUNET_NAT_test_stop (ah->tst);
+ ah->tst = NULL;
+ ah->ret = ret;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ GNUNET_NAT_ERROR_SUCCESS == ret
+ ? _("NAT traversal with ICMP Server succeeded.\n")
+ : _("NAT traversal with ICMP Server failed.\n"));
+ GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "ENABLE_ICMP_SERVER",
+ GNUNET_NAT_ERROR_SUCCESS == ret ? "NO" : "YES");
+ next_phase (ah);
+}
+
+
+/**
+ * Main function for the connection reversal test.
+ *
+ * @param cls the `struct GNUNET_NAT_AutoHandle`
+ */
+static void
+reversal_test (void *cls)
+{
+ struct GNUNET_NAT_AutoHandle *ah = cls;
+
+ ah->task = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Testing connection reversal with ICMP server.\n"));
+ GNUNET_RESOLVER_connect (ah->cfg);
+ ah->tst = GNUNET_NAT_test_start (ah->cfg, GNUNET_YES, 0, 0, TIMEOUT,
+ &result_callback, ah);
+}
+
+
+/**
+ * Set our external IPv4 address based on the UPnP.
+ *
+ *
+ * @param cls closure with our setup context
+ * @param addr the address, NULL on errors
+ * @param emsg NULL on success, otherwise an error message
+ */
+static void
+set_external_ipv4 (void *cls,
+ const struct in_addr *addr,
+ enum GNUNET_NAT_StatusCode ret)
+{
+ struct GNUNET_NAT_AutoHandle *ah = cls;
+ char buf[INET_ADDRSTRLEN];
+
+ ah->eh = NULL;
+ ah->ret = ret;
+ if (GNUNET_NAT_ERROR_SUCCESS != ret)
+ {
+ next_phase (ah);
+ return;
+ }
+ /* enable 'behind nat' */
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Detected external IP `%s'\n"),
+ inet_ntop (AF_INET,
+ addr,
+ buf,
+ sizeof (buf)));
+ GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "BEHIND_NAT", "YES");
+
+ /* set external IP address */
+ if (NULL == inet_ntop (AF_INET, addr, buf, sizeof (buf)))
+ {
+ GNUNET_break (0);
+ /* actually, this should never happen, as the caller already executed just
+ * this check, but for consistency (eg: future changes in the caller)
+ * we still need to report this error...
+ */
+ ah->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID;
+ next_phase (ah);
+ return;
+ }
+ GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "EXTERNAL_ADDRESS",
+ buf);
+ ah->upnp_set_external_address = GNUNET_YES;
+ next_phase (ah);
+}
+
+
+/**
+ * Determine our external IPv4 address.
+ *
+ * @param ah auto setup context
+ */
+static void
+test_external_ip (struct GNUNET_NAT_AutoHandle *ah)
+{
+ if (GNUNET_NAT_ERROR_SUCCESS != ah->ret)
+ next_phase (ah);
+
+ // FIXME: CPS?
+ /* try to detect external IP */
+ ah->eh = GNUNET_NAT_mini_get_external_ipv4 (TIMEOUT,
+ &set_external_ipv4, ah);
+}
+
+
+/**
+ * Determine our external IPv4 address and port using an external STUN server
+ *
+ * @param ah auto setup context
+ */
+static void
+test_stun (struct GNUNET_NAT_AutoHandle *ah)
+{
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running STUN test\n");
+
+ /* Get port from the configuration */
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (ah->cfg,
+ "transport-udp",
+ "PORT",
+ &port))
+ {
+ port = 2086;
+ }
+
+ //Lets create the socket
+ lsock4 = bind_v4 ();
+ if (NULL == lsock4)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
+ next_phase(ah);
+ return;
+ }
+ else
+ {
+ //Lets call our function now when it accepts
+ ltask4 = GNUNET_SCHEDULER_add_read_net (NAT_SERVER_TIMEOUT,
+ lsock4,
+ &do_udp_read,
+ ah);
+ }
+
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "STUN service listens on port %u\n",
+ (unsigned int) port);
+ if (GNUNET_NO ==
+ GNUNET_NAT_stun_make_request (stun_server,
+ stun_port,
+ lsock4,
+ &request_callback,
+ NULL))
+ {
+ /*An error happened*/
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "STUN error, stopping\n");
+ stop_stun ();
+ next_phase (ah);
+ }
+}
+
+
+/**
+ * Process list of local IP addresses. Find and set the
+ * one of the default interface.
+ *
+ * @param cls our `struct GNUNET_NAT_AutoHandle`
+ * @param name name of the interface (can be NULL for unknown)
+ * @param isDefault is this presumably the default interface
+ * @param addr address of this interface (can be NULL for unknown or unassigned)
+ * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
+ * @param netmask the network mask (can be NULL for unknown or unassigned))
+ * @param addrlen length of the @a addr and @a broadcast_addr
+ * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort
+ */
+static int
+process_if (void *cls,
+ const char *name,
+ int isDefault,
+ const struct sockaddr *addr,
+ const struct sockaddr *broadcast_addr,
+ const struct sockaddr *netmask,
+ socklen_t addrlen)
+{
+ struct GNUNET_NAT_AutoHandle *ah = cls;
+ const struct sockaddr_in *in;
+ char buf[INET_ADDRSTRLEN];
+
+
+ if ( (sizeof (struct sockaddr_in6) == addrlen) &&
+ (0 != memcmp (&in6addr_loopback, &((const struct sockaddr_in6 *) addr)->sin6_addr,
+ sizeof (struct in6_addr))) &&
+ (! IN6_IS_ADDR_LINKLOCAL(&((const struct sockaddr_in6 *) addr)->sin6_addr)) )
+ {
+ ah->have_v6 = GNUNET_YES;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("This system has a global IPv6 address, setting IPv6 to supported.\n"));
+
+ return GNUNET_OK;
+ }
+ if (addrlen != sizeof (struct sockaddr_in))
+ return GNUNET_OK;
+ in = (const struct sockaddr_in *) addr;
+
+
+ /* set internal IP address */
+ if (NULL == inet_ntop (AF_INET, &in->sin_addr, buf, sizeof (buf)))
+ {
+ GNUNET_break (0);
+ return GNUNET_OK;
+ }
+ GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "INTERNAL_ADDRESS",
+ buf);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Detected internal network address `%s'.\n"),
+ buf);
+
+
+ ah->ret = GNUNET_NAT_ERROR_SUCCESS;
+
+ /* Check if our internal IP is the same as the External detect by STUN*/
+ if(ah->stun_ip && (strcmp(buf, ah->stun_ip) == 0) )
+ {
+ ah->internal_ip_is_public = GNUNET_YES;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,"A internal IP is the sameas the external");
+ /* No need to continue*/
+ return GNUNET_SYSERR;
+ }
+
+ /* no need to continue iteration if we found the default */
+ if (!isDefault)
+ return GNUNET_OK;
+ else
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Determine our local IP addresses; detect internal IP & IPv6-support
+ *
+ * @param ah auto setup context
+ */
+static void
+test_local_ip (struct GNUNET_NAT_AutoHandle *ah)
+{
+ ah->have_v6 = GNUNET_NO;
+ ah->ret = GNUNET_NAT_ERROR_NO_VALID_IF_IP_COMBO; // reset to success if any of the IFs in below iterator has a valid IP
+ GNUNET_OS_network_interfaces_list (&process_if, ah);
+
+ GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "DISABLEV6",
+ (GNUNET_YES == ah->have_v6) ? "NO" : "YES");
+ next_phase (ah);
+}
+
+
+/**
+ * We got disconnected from the NAT server. Stop
+ * waiting for a reply.
+ *
+ * @param cls the `struct GNUNET_NAT_AutoHandle`
+ * @param error error code
+ */
+static void
+mq_error_handler (void *cls,
+ enum GNUNET_MQ_Error error)
+{
+ struct GNUNET_NAT_AutoHandle *ah = cls;
+
+ GNUNET_MQ_destroy (ah->mq);
+ ah->mq = NULL;
+ /* wait a bit first? */
+ next_phase (ah);
+}
+
+
+/**
+ * Test if NAT has been punched
+ *
+ * @param ah auto setup context
+ */
+static void
+test_nat_punched (struct GNUNET_NAT_AutoHandle *ah)
+{
+ struct GNUNET_NAT_TestMessage *msg;
+ struct GNUNET_MQ_Envelope *env;
+
+ if (! ah->stun_ip)
+ {
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ "We don't have a STUN IP");
+ next_phase (ah);
+ return;
+ }
+
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ "Asking gnunet-nat-server to connect to `%s'\n",
+ ah->stun_ip);
+ ah->mq = GNUNET_CLIENT_connecT (ah->cfg,
+ "gnunet-nat-server",
+ NULL,
+ &mq_error_handler,
+ ah);
+ if (NULL == ah->mq)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Failed to connect to `gnunet-nat-server'\n"));
+ next_phase (ah);
+ return;
+ }
+ env = GNUNET_MQ_msg (msg,
+ GNUNET_MESSAGE_TYPE_NAT_TEST);
+ msg->dst_ipv4 = inet_addr (ah->stun_ip);
+ msg->dport = htons (ah->stun_port);
+ msg->data = port;
+ msg->is_tcp = htonl ((uint32_t) GNUNET_NO);
+ GNUNET_MQ_send (ah->mq,
+ env);
+ if (NULL != ltask4)
+ {
+ GNUNET_SCHEDULER_cancel (ltask4);
+ ltask4 = GNUNET_SCHEDULER_add_read_net (NAT_SERVER_TIMEOUT,
+ lsock4,
+ &do_udp_read,
+ ah);
+ }
+}
+
+
+/**
+ * Test if UPnPC works.
+ *
+ * @param ah auto setup context
+ */
+static void
+test_upnpc (struct GNUNET_NAT_AutoHandle *ah)
+{
+
+ int have_upnpc;
+
+ if (GNUNET_NAT_ERROR_SUCCESS != ah->ret)
+ next_phase (ah);
+
+ // test if upnpc is available
+ have_upnpc = (GNUNET_SYSERR !=
+ GNUNET_OS_check_helper_binary ("upnpc", GNUNET_NO, NULL));
+ //FIXME: test if upnpc is actually working, that is, if transports start to work once we use UPnP
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ (have_upnpc)
+ ? _("upnpc found, enabling its use\n")
+ : _("upnpc not found\n"));
+ GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "ENABLE_UPNP",
+ (GNUNET_YES == have_upnpc) ? "YES" : "NO");
+ next_phase (ah);
+
+}
+
+
+/**
+ * Test if ICMP server is working
+ *
+ * @param ah auto setup context
+ */
+static void
+test_icmp_server (struct GNUNET_NAT_AutoHandle *ah)
+{
+
+ int ext_ip;
+ int nated;
+ int binary;
+ char *tmp;
+ char *helper;
+ ext_ip = GNUNET_NO;
+ nated = GNUNET_NO;
+ binary = GNUNET_NO;
+
+ tmp = NULL;
+ helper = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-server");
+ if ( (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (ah->cfg,
+ "nat",
+ "EXTERNAL_ADDRESS",
+ &tmp)) &&
+ (0 < strlen (tmp)) )
+ {
+ ext_ip = GNUNET_OK;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("test_icmp_server not possible, as we have no public IPv4 address\n"));
+ }
+ else
+ goto err;
+
+ if (GNUNET_YES ==
+ GNUNET_CONFIGURATION_get_value_yesno (ah->cfg,
+ "nat",
+ "BEHIND_NAT"))
+ {
+ nated = GNUNET_YES;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("test_icmp_server not possible, as we are not behind NAT\n"));
+ }
+ else
+ goto err;
+
+ if (GNUNET_YES ==
+ GNUNET_OS_check_helper_binary (helper,
+ GNUNET_YES,
+ "-d 127.0.0.1" ))
+ {
+ binary = GNUNET_OK; // use localhost as source for that one udp-port, ok for testing
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("No working gnunet-helper-nat-server found\n"));
+ }
+err:
+ GNUNET_free_non_null (tmp);
+ GNUNET_free (helper);
+
+ if ( (GNUNET_OK == ext_ip) &&
+ (GNUNET_YES == nated) &&
+ (GNUNET_OK == binary) )
+ ah->task = GNUNET_SCHEDULER_add_now (&reversal_test,
+ ah);
+ else
+ next_phase (ah);
+}
+
+
+/**
+ * Test if ICMP client is working
+ *
+ * @param ah auto setup context
+ */
+static void
+test_icmp_client (struct GNUNET_NAT_AutoHandle *ah)
+{
+ char *tmp;
+ char *helper;
+
+ tmp = NULL;
+ helper = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-client");
+ if ( (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (ah->cfg,
+ "nat",
+ "INTERNAL_ADDRESS",
+ &tmp)) &&
+ (0 < strlen (tmp)) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("test_icmp_client not possible, as we have no internal IPv4 address\n"));
+ }
+ else
+ goto err;
+
+ if (GNUNET_YES !=
+ GNUNET_CONFIGURATION_get_value_yesno (ah->cfg,
+ "nat",
+ "BEHIND_NAT"))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("test_icmp_server not possible, as we are not behind NAT\n"));
+ }
+ else
+ goto err;
+
+ if (GNUNET_YES ==
+ GNUNET_OS_check_helper_binary (helper,
+ GNUNET_YES,
+ "-d 127.0.0.1 127.0.0.2 42"))
+ {
+ // none of these parameters are actually used in privilege testing mode
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("No working gnunet-helper-nat-server found\n"));
+ }
+err:
+ GNUNET_free_non_null (tmp);
+ GNUNET_free (helper);
+
+ next_phase (ah);
+}
+
+
+/**
+ * Run the next phase of the auto test.
+ */
+static void
+next_phase (struct GNUNET_NAT_AutoHandle *ah)
+{
+ struct GNUNET_CONFIGURATION_Handle *diff;
+
+ ah->phase++;
+ switch (ah->phase)
+ {
+ case AUTO_INIT:
+ GNUNET_assert (0);
+ break;
+ case AUTO_EXTERNAL_IP:
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Will run AUTO_EXTERNAL_IP\n");
+ test_external_ip (ah);
+ break;
+ case AUTO_STUN:
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Will run AUTO_STUN\n");
+ test_stun (ah);
+ break;
+ case AUTO_LOCAL_IP:
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Will run AUTO_LOCAL_IP\n");
+ test_local_ip (ah);
+ break;
+ case AUTO_NAT_PUNCHED:
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Will run AUTO_NAT_PUNCHED\n");
+ test_nat_punched (ah);
+ break;
+ case AUTO_UPNPC:
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Will run AUTO_UPNPC\n");
+ test_upnpc (ah);
+ break;
+ case AUTO_ICMP_SERVER:
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Will run AUTO_ICMP_SERVER\n");
+ test_icmp_server (ah);
+ break;
+ case AUTO_ICMP_CLIENT:
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Will run AUTO_ICMP_CLIENT\n");
+ test_icmp_client (ah);
+ break;
+ case AUTO_DONE:
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Done with tests\n");
+ if (!ah->internal_ip_is_public)
+ {
+ GNUNET_CONFIGURATION_set_value_string (ah->cfg,
+ "nat",
+ "BEHIND_NAT",
+ "YES");
+
+ if (ah->connected_back)
+ {
+ GNUNET_CONFIGURATION_set_value_string (ah->cfg,
+ "nat",
+ "PUNCHED_NAT",
+ "YES");
+ }
+ else
+ {
+ GNUNET_CONFIGURATION_set_value_string (ah->cfg,
+ "nat",
+ "PUNCHED_NAT",
+ "NO");
+ }
+
+ if (ah->stun_ip)
+ {
+ GNUNET_CONFIGURATION_set_value_string (ah->cfg,
+ "nat",
+ "EXTERNAL_ADDRESS",
+ ah->stun_ip);
+ if (ah->connected_back)
+ {
+ ah->type = GNUNET_NAT_TYPE_STUN_PUNCHED_NAT;
+ GNUNET_CONFIGURATION_set_value_string (ah->cfg,
+ "nat",
+ "USE_STUN",
+ "YES");
+ }
+ else
+ {
+ ah->type = GNUNET_NAT_TYPE_UNREACHABLE_NAT;
+ GNUNET_CONFIGURATION_set_value_string (ah->cfg,
+ "nat",
+ "USE_STUN",
+ "NO");
+ }
+
+ }
+ if (0 != ah->stun_port)
+ {
+ GNUNET_CONFIGURATION_set_value_number (ah->cfg,
+ "transport-udp",
+ "ADVERTISED_PORT",
+ ah->stun_port);
+ }
+
+ }
+ else
+ {
+ //The internal IP is the same as public, but we didn't got a incoming connection
+ if (ah->connected_back)
+ {
+ ah->type = GNUNET_NAT_TYPE_NO_NAT;
+ GNUNET_CONFIGURATION_set_value_string (ah->cfg,
+ "nat",
+ "BEHIND_NAT",
+ "NO");
+ }
+ else
+ {
+ GNUNET_CONFIGURATION_set_value_string (ah->cfg,
+ "nat",
+ "BEHIND_NAT",
+ "YES");
+ ah->type = GNUNET_NAT_TYPE_UNREACHABLE_NAT;
+ if (ah->stun_ip)
+ {
+ GNUNET_CONFIGURATION_set_value_string (ah->cfg,
+ "nat",
+ "EXTERNAL_ADDRESS",
+ ah->stun_ip);
+ }
+ if (0 != ah->stun_port)
+ {
+ GNUNET_CONFIGURATION_set_value_number (ah->cfg,
+ "transport-udp",
+ "ADVERTISED_PORT",
+ ah->stun_port);
+
+ }
+ }
+ }
+
+ diff = GNUNET_CONFIGURATION_get_diff (ah->initial_cfg,
+ ah->cfg);
+
+
+ ah->fin_cb (ah->fin_cb_cls,
+ diff,
+ ah->ret,
+ ah->type);
+ GNUNET_CONFIGURATION_destroy (diff);
+ GNUNET_NAT_autoconfig_cancel (ah);
+ }
+}
+
+
+/**
+ * Start auto-configuration routine. The resolver service should
+ * be available when this function is called.
+ *
+ * @param cfg initial configuration
+ * @param cb function to call with autoconfiguration result
+ * @param cb_cls closure for @a cb
+ * @return handle to cancel operation
+ */
+struct GNUNET_NAT_AutoHandle *
+GNUNET_NAT_autoconfig_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ GNUNET_NAT_AutoResultCallback cb,
+ void *cb_cls)
+{
+ struct GNUNET_NAT_AutoHandle *ah;
+
+ ah = GNUNET_new (struct GNUNET_NAT_AutoHandle);
+ ah->fin_cb = cb;
+ ah->fin_cb_cls = cb_cls;
+ ah->ret = GNUNET_NAT_ERROR_SUCCESS;
+ ah->cfg = GNUNET_CONFIGURATION_dup (cfg);
+ ah->initial_cfg = GNUNET_CONFIGURATION_dup (cfg);
+
+ /* never use loopback addresses if user wanted autoconfiguration */
+ GNUNET_CONFIGURATION_set_value_string (ah->cfg,
+ "nat",
+ "USE_LOCALADDR",
+ "NO");
+
+ next_phase (ah);
+ return ah;
+}
+
+
+/**
+ * Abort autoconfiguration.
+ *
+ * @param ah handle for operation to abort
+ */
+void
+GNUNET_NAT_autoconfig_cancel (struct GNUNET_NAT_AutoHandle *ah)
+{
+ if (NULL != ah->tst)
+ {
+ GNUNET_NAT_test_stop (ah->tst);
+ ah->tst = NULL;
+ }
+ if (NULL != ah->eh)
+ {
+ GNUNET_NAT_mini_get_external_ipv4_cancel (ah->eh);
+ ah->eh = NULL;
+ }
+ if (NULL != ah->mq)
+ {
+ GNUNET_MQ_destroy (ah->mq);
+ ah->mq = NULL;
+ }
+ if (NULL != ah->task)
+ {
+ GNUNET_SCHEDULER_cancel (ah->task);
+ ah->task = NULL;
+ }
+ GNUNET_CONFIGURATION_destroy (ah->cfg);
+ GNUNET_CONFIGURATION_destroy (ah->initial_cfg);
+ GNUNET_free (ah);
+}
+
+
+/* end of nat_auto.c */
endif
lib_LTLIBRARIES = \
- libgnunetnat.la \
libgnunetnatnew.la
-libgnunetnat_la_SOURCES = \
- nat.c nat.h \
- nat_auto.c \
- nat_test.c \
- nat_mini.c \
- nat_stun.c
-libgnunetnat_la_LIBADD = \
- $(top_builddir)/src/util/libgnunetutil.la \
- $(GN_LIBINTL) @EXT_LIBS@
-libgnunetnat_la_LDFLAGS = \
- $(GN_LIB_LDFLAGS) $(WINFLAGS) \
- -version-info 1:1:1
-
libgnunetnatnew_la_SOURCES = \
nat_api.c \
nat_api_stun.c nat_stun.h \
-lgcrypt \
$(GN_LIBINTL)
-check_PROGRAMS = \
- test_nat \
- test_nat_mini \
- test_nat_test \
- test_stun
+#check_PROGRAMS = \
+# test_nat \
+# test_nat_mini \
+# test_nat_test \
+# test_stun
if ENABLE_TEST_RUN
AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;
TESTS = $(check_PROGRAMS)
endif
-test_nat_SOURCES = \
- test_nat.c
-test_nat_LDADD = \
- libgnunetnat.la \
- $(top_builddir)/src/util/libgnunetutil.la
-
-test_nat_mini_SOURCES = \
- test_nat_mini.c
-test_nat_mini_LDADD = \
- libgnunetnat.la \
- $(top_builddir)/src/util/libgnunetutil.la
-
-test_nat_test_SOURCES = \
- test_nat_test.c
-test_nat_test_LDADD = \
- libgnunetnat.la \
- $(top_builddir)/src/util/libgnunetutil.la
-
-test_stun_SOURCES = \
- test_stun.c
-test_stun_LDADD = \
- libgnunetnat.la \
- $(top_builddir)/src/util/libgnunetutil.la
+#test_nat_SOURCES = \
+# test_nat.c
+#test_nat_LDADD = \
+# libgnunetnat.la \
+# $(top_builddir)/src/util/libgnunetutil.la
+
+#test_nat_mini_SOURCES = \
+# test_nat_mini.c
+#test_nat_mini_LDADD = \
+# libgnunetnat.la \
+# $(top_builddir)/src/util/libgnunetutil.la
+
+#test_nat_test_SOURCES = \
+# test_nat_test.c
+#test_nat_test_LDADD = \
+# libgnunetnat.la \
+# $(top_builddir)/src/util/libgnunetutil.la
+
+#test_stun_SOURCES = \
+# test_stun.c
+#test_stun_LDADD = \
+# libgnunetnat.la \
+# $(top_builddir)/src/util/libgnunetutil.la
EXTRA_DIST = \
test_nat_data.conf \
* knowledge about the local network topology.
*
* TODO:
- * - adapt existing transports to use new NAT logic
- * - abandon legacy NAT code
- *
- * - implement "more" autoconfig:
- * + consider moving autoconfig-logic into separate service!
- * + re-work gnunet-nat-server & integrate!
- * + test manually punched NAT (how?)
+ * - migrate test cases to new NAT service
+ * - add new traceroute-based logic for external IP detection
*
* - implement & test STUN processing to classify NAT;
* basically, open port & try different methods.
+++ /dev/null
-/*
- This file is part of GNUnet.
- Copyright (C) 2009, 2010, 2011 GNUnet e.V.
-
- GNUnet is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published
- by the Free Software Foundation; either version 3, or (at your
- option) any later version.
-
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with GNUnet; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA.
-*/
-
-/**
- * @file nat/nat.c
- * @brief Library handling UPnP and NAT-PMP port forwarding and
- * external IP address retrieval
- * @author Milan Bouchet-Valat
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include "gnunet_util_lib.h"
-#include "gnunet_resolver_service.h"
-#include "gnunet_nat_lib.h"
-#include "nat.h"
-
-#define LOG(kind,...) GNUNET_log_from (kind, "nat", __VA_ARGS__)
-
-/**
- * How often do we scan for changes in our IP address from our local
- * interfaces?
- */
-#define IFC_SCAN_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
-
-/**
- * How often do we scan for changes in how our hostname resolves?
- */
-#define HOSTNAME_DNS_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 20)
-
-
-/**
- * How often do we scan for changes in how our external (dyndns) hostname resolves?
- */
-#define DYNDNS_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 7)
-
-/**
- * How long until we give up trying to resolve our own hostname?
- */
-#define HOSTNAME_RESOLVE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
-
-
-/**
- * How often do we check a STUN server ?
- */
-#define STUN_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2)
-
-
-/**
- * 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).
- */
- LAL_EXTERNAL_IP,
-
- /**
- * Address was obtained by an external STUN server
- */
- LAL_EXTERNAL_STUN_IP,
-
- /**
- * Address was obtained by DNS resolution of the external hostname
- * given in the configuration (i.e. hole-punched DynDNS setup)
- * during the previous iteration (see #3213).
- */
- LAL_EXTERNAL_IP_OLD,
-
- /**
- * Address was obtained by looking up our own hostname in DNS.
- */
- LAL_HOSTNAME_DNS,
-
- /**
- * Address was obtained by scanning our hosts's network interfaces
- * and taking their address (no DNS involved).
- */
- LAL_INTERFACE_ADDRESS,
-
- /**
- * Addresses we were explicitly bound to.
- */
- LAL_BINDTO_ADDRESS,
-
- /**
- * Addresses from UPnP or PMP
- */
- LAL_UPNP,
-
- /**
- * 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 miniupnp-based NAT traversal actions.
- */
-struct MiniList
-{
-
- /**
- * Doubly-linked list.
- */
- struct MiniList *next;
-
- /**
- * Doubly-linked list.
- */
- struct MiniList *prev;
-
- /**
- * Handle to mini-action.
- */
- struct GNUNET_NAT_MiniHandle *mini;
-
- /**
- * Local port number that was mapped.
- */
- uint16_t port;
-
-};
-
-
-/**
- * List of STUN servers
- */
-struct StunServerList
-{
-
- /**
- * Doubly-linked list.
- */
- struct StunServerList *next;
-
- /**
- * Doubly-linked list.
- */
- struct StunServerList *prev;
-
- /**
- * Address
- */
- char * address;
-
- /**
- * Server Port
- */
- uint16_t port;
-
-};
-
-
-/**
- * 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;
-
- /**
- * Function to call when we notice another peer asking for
- * connection reversal.
- */
- GNUNET_NAT_ReversalCallback reversal_callback;
-
- /**
- * Closure for callbacks (@e address_callback and @e reversal_callback)
- */
- void *callback_cls;
-
- /**
- * Handle for (DYN)DNS lookup of our external IP.
- */
- struct GNUNET_RESOLVER_RequestHandle *ext_dns;
-
- /**
- * Handle for request of hostname resolution, non-NULL if pending.
- */
- struct GNUNET_RESOLVER_RequestHandle *hostname_dns;
-
- /**
- * stdout pipe handle for the gnunet-helper-nat-server process
- */
- struct GNUNET_DISK_PipeHandle *server_stdout;
-
- /**
- * stdout file handle (for reading) for the gnunet-helper-nat-server process
- */
- const struct GNUNET_DISK_FileHandle *server_stdout_handle;
-
- /**
- * Linked list of currently valid addresses (head).
- */
- struct LocalAddressList *lal_head;
-
- /**
- * Linked list of currently valid addresses (tail).
- */
- struct LocalAddressList *lal_tail;
-
- /**
- * How long do we wait for restarting a crashed gnunet-helper-nat-server?
- */
- struct GNUNET_TIME_Relative server_retry_delay;
-
- /**
- * ID of select gnunet-helper-nat-server stdout read task
- */
- struct GNUNET_SCHEDULER_Task *server_read_task;
-
- /**
- * ID of interface IP-scan task
- */
- struct GNUNET_SCHEDULER_Task *ifc_task;
-
- /**
- * ID of hostname DNS lookup task
- */
- struct GNUNET_SCHEDULER_Task *hostname_task;
-
- /**
- * ID of DynDNS lookup task
- */
- struct GNUNET_SCHEDULER_Task *dns_task;
-
- /**
- * Active STUN request, if any.
- */
- struct GNUNET_NAT_STUN_Handle *stun_request;
-
- /**
- * How often do we scan for changes in our IP address from our local
- * interfaces?
- */
- struct GNUNET_TIME_Relative ifc_scan_frequency;
-
- /**
- * How often do we scan for changes in how our hostname resolves?
- */
- struct GNUNET_TIME_Relative hostname_dns_frequency;
-
- /**
- * How often do we scan for changes in how our external (dyndns) hostname resolves?
- */
- struct GNUNET_TIME_Relative dyndns_frequency;
-
- /**
- * The process id of the server process (if behind NAT)
- */
- struct GNUNET_OS_Process *server_proc;
-
- /**
- * LAN address as passed by the caller (array).
- */
- struct sockaddr **local_addrs;
-
- /**
- * Length of the @e local_addrs.
- */
- socklen_t *local_addrlens;
-
- /**
- * List of handles for UPnP-traversal, one per local port (if
- * not IPv6-only).
- */
- struct MiniList *mini_head;
-
- /**
- * List of handles for UPnP-traversal, one per local port (if
- * not IPv6-only).
- */
- struct MiniList *mini_tail;
-
- /**
- * Number of entries in 'local_addrs' array.
- */
- unsigned int num_local_addrs;
-
- /**
- * Our external address (according to config, UPnP may disagree...),
- * in dotted decimal notation, IPv4-only. Or NULL if not known.
- */
- 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;
-
- /**
- * Should we run the gnunet-helper-nat-server?
- */
- int enable_nat_server;
-
- /**
- * Are we allowed to try UPnP/PMP for NAT traversal?
- */
- int enable_upnp;
-
- /**
- * Should we use local addresses (loopback)? (according to config)
- */
- int use_localaddresses;
-
- /**
- * Should we return local addresses to clients
- */
- int return_localaddress;
-
- /**
- * Should we do a DNS lookup of our hostname to find out our own IP?
- */
- int use_hostname;
-
- /**
- * Is using IPv6 disabled?
- */
- int disable_ipv6;
-
- /**
- * Is this TCP or UDP?
- */
- int is_tcp;
-
- /**
- * Port we advertise to the outside.
- */
- uint16_t adv_port;
-
- /**
- * Should we use STUN ?
- */
- int use_stun;
-
- /**
- * How often should we check STUN ?
- */
- struct GNUNET_TIME_Relative stun_frequency;
-
- /**
- * STUN socket
- */
- struct GNUNET_NETWORK_Handle* socket;
-
- /*
- * Am I waiting for a STUN response ?
- */
- int waiting_stun;
-
- /**
- * STUN request task
- */
- struct GNUNET_SCHEDULER_Task *stun_task;
-
- /**
- * Head of List of STUN servers
- */
- struct StunServerList *stun_servers_head;
-
- /**
- * Tail of List of STUN servers
- */
- struct StunServerList *stun_servers_tail;
-
- /**
- * Actual STUN Server
- */
- struct StunServerList *actual_stun_server;
-
-};
-
-
-/**
- * Try to start the gnunet-helper-nat-server (if it is not
- * already running).
- *
- * @param h handle to NAT
- */
-static void
-start_gnunet_nat_server (struct GNUNET_NAT_Handle *h);
-
-
-/**
- * Remove all addresses from the list of 'local' addresses
- * that originated from the given source.
- *
- * @param h handle to NAT
- * @param src source that identifies addresses to remove
- */
-static void
-remove_from_address_list_by_source (struct GNUNET_NAT_Handle *h,
- enum LocalAddressSource src)
-{
- struct LocalAddressList *pos;
- struct LocalAddressList *next;
-
- next = h->lal_head;
- while (NULL != (pos = next))
- {
- next = pos->next;
- if (pos->source != src)
- continue;
- GNUNET_CONTAINER_DLL_remove (h->lal_head,
- h->lal_tail,
- pos);
- if (NULL != h->address_callback)
- h->address_callback (h->callback_cls,
- GNUNET_NO,
- (const struct sockaddr *) &pos[1],
- pos->addrlen);
- GNUNET_free (pos);
- }
-}
-
-
-/**
- * Add the given address to the list of 'local' addresses, thereby
- * making it a 'legal' address for this peer to have.
- *
- * @param h handle to NAT
- * @param src where did the local address originate from?
- * @param arg the address, some `struct sockaddr`
- * @param arg_size number of bytes in @a 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)
-{
- struct LocalAddressList *lal;
-
- lal = GNUNET_malloc (sizeof (struct LocalAddressList) + arg_size);
- GNUNET_memcpy (&lal[1], arg, arg_size);
- lal->addrlen = arg_size;
- lal->source = src;
- GNUNET_CONTAINER_DLL_insert (h->lal_head,
- h->lal_tail,
- lal);
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Adding address `%s' from source %d\n",
- GNUNET_a2s (arg, arg_size),
- src);
- if (NULL != h->address_callback)
- h->address_callback (h->callback_cls,
- GNUNET_YES,
- arg,
- arg_size);
-}
-
-
-/**
- * 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-helper-nat-server).
- *
- * @param h handle to NAT
- * @param src where did the local address originate from?
- * @param arg the address, some `struct sockaddr`
- * @param arg_size number of bytes in @a arg
- */
-static void
-add_to_address_list (struct GNUNET_NAT_Handle *h,
- enum LocalAddressSource src,
- const struct sockaddr *arg,
- socklen_t arg_size)
-{
- 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);
- }
-}
-
-
-/**
- * Add the given IP address to the list of 'local' addresses, thereby
- * making it a 'legal' address for this peer to have.
- *
- * @param h handle to NAT
- * @param src where did the local address originate from?
- * @param addr the address, some `struct in_addr` or `struct in6_addr`
- * @param addrlen number of bytes in addr
- */
-static void
-add_ip_to_address_list (struct GNUNET_NAT_Handle *h,
- enum LocalAddressSource src,
- const void *addr,
- socklen_t addrlen)
-{
- struct sockaddr_in s4;
- const struct in_addr *in4;
- struct sockaddr_in6 s6;
- const struct in6_addr *in6;
-
- if (addrlen == sizeof (struct in_addr))
- {
- 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
- 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 (addrlen == sizeof (struct in6_addr))
- {
- 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
- s6.sin6_addr = *in6;
- add_to_address_list (h, src, (const struct sockaddr *) &s6,
- sizeof (struct sockaddr_in6));
- }
- }
- else
- {
- GNUNET_assert (0);
- }
-}
-
-
-/**
- * Task to do DNS lookup on our external hostname to
- * get DynDNS-IP addresses.
- *
- * @param cls the NAT handle
- */
-static void
-resolve_dns (void *cls);
-
-
-/**
- * Our (external) hostname was resolved and the configuration says that
- * the NAT was hole-punched.
- *
- * @param cls the `struct GNUNET_NAT_Handle`
- * @param addr NULL on error, otherwise result of DNS lookup
- * @param addrlen number of bytes in @a addr
- */
-static void
-process_external_ip (void *cls,
- const struct sockaddr *addr,
- socklen_t addrlen)
-{
- struct GNUNET_NAT_Handle *h = cls;
- struct in_addr dummy;
-
- if (NULL == addr)
- {
- h->ext_dns = NULL;
- /* Current iteration is over, remove 'old' IPs now */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Purging old IPs for external address\n");
- remove_from_address_list_by_source (h,
- LAL_EXTERNAL_IP_OLD);
- if (1 == inet_pton (AF_INET,
- h->external_address,
- &dummy))
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Got numeric IP for external address, not repeating lookup\n");
- return; /* repated lookup pointless: was numeric! */
- }
- h->dns_task =
- GNUNET_SCHEDULER_add_delayed (h->dyndns_frequency,
- &resolve_dns, h);
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Got IP `%s' for external address `%s'\n",
- GNUNET_a2s (addr,
- addrlen),
- h->external_address);
- add_to_address_list (h,
- LAL_EXTERNAL_IP,
- addr,
- addrlen);
-}
-
-
-/**
- * Task to do a lookup on our hostname for IP addresses.
- *
- * @param cls the NAT handle
- */
-static void
-resolve_hostname (void *cls);
-
-
-/**
- * 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 @a addr
- */
-static void
-process_hostname_ip (void *cls,
- const struct sockaddr *addr,
- socklen_t addrlen)
-{
- struct GNUNET_NAT_Handle *h = cls;
-
- if (NULL == addr)
- {
- h->hostname_dns = NULL;
- h->hostname_task =
- GNUNET_SCHEDULER_add_delayed (h->hostname_dns_frequency,
- &resolve_hostname,
- h);
- return;
- }
- add_to_address_list (h,
- LAL_HOSTNAME_DNS,
- addr,
- addrlen);
-}
-
-
-/**
- * Length of the interface names returned from os_network.c.
- * (in that file, hardcoded at 11).
- */
-#define IF_NAME_LEN 11
-
-
-/**
- * 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 broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
- * @param netmask the network mask (can be NULL for unknown or unassigned))
- * @param addrlen number of bytes in @a addr and @a broadcast_addr
- * @return #GNUNET_OK to continue iterating
- */
-static int
-process_interfaces (void *cls,
- const char *name,
- int isDefault,
- const struct sockaddr *addr,
- const struct sockaddr *broadcast_addr,
- const struct sockaddr *netmask,
- socklen_t addrlen)
-{
- const static struct in6_addr any6 = IN6ADDR_ANY_INIT;
- struct GNUNET_NAT_Handle *h = cls;
- const struct sockaddr_in *s4;
- const struct sockaddr_in6 *s6;
- const void *ip;
- char buf[INET6_ADDRSTRLEN];
- unsigned int i;
- int have_any;
- char *tun_if;
-
- /* skip virtual interfaces created by GNUnet-vpn */
- if (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_string (h->cfg,
- "vpn",
- "IFNAME",
- &tun_if))
- {
- if (0 == strncasecmp (name,
- tun_if,
- IF_NAME_LEN))
- {
- GNUNET_free (tun_if);
- return GNUNET_OK;
- }
- GNUNET_free (tun_if);
- }
- /* skip virtual interfaces created by GNUnet-dns */
- if (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_string (h->cfg,
- "dns",
- "IFNAME",
- &tun_if))
- {
- if (0 == strncasecmp (name,
- tun_if,
- IF_NAME_LEN))
- {
- GNUNET_free (tun_if);
- return GNUNET_OK;
- }
- GNUNET_free (tun_if);
- }
- /* skip virtual interfaces created by GNUnet-exit */
- if (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_string (h->cfg,
- "exit",
- "TUN_IFNAME",
- &tun_if))
- {
- if (0 == strncasecmp (name,
- tun_if,
- IF_NAME_LEN))
- {
- GNUNET_free (tun_if);
- return GNUNET_OK;
- }
- GNUNET_free (tun_if);
- }
-
- switch (addr->sa_family)
- {
- case AF_INET:
- /* check if we're bound to the "ANY" IP address */
- have_any = GNUNET_NO;
- for (i=0;i<h->num_local_addrs;i++)
- {
- if (h->local_addrs[i]->sa_family != AF_INET)
- continue;
-#ifndef INADDR_ANY
-#define INADDR_ANY 0
-#endif
- if (INADDR_ANY == ((struct sockaddr_in*) h->local_addrs[i])->sin_addr.s_addr)
- {
- have_any = GNUNET_YES;
- break;
- }
- }
- if (GNUNET_NO == have_any)
- return GNUNET_OK; /* not bound to IP 0.0.0.0 but to specific IP addresses,
- do not use those from interfaces */
- s4 = (struct sockaddr_in *) addr;
- ip = &s4->sin_addr;
-
- /* Check if address is in 127.0.0.0/8 */
- uint32_t address = ntohl ((uint32_t) (s4->sin_addr.s_addr));
- uint32_t value = (address & 0xFF000000) ^ 0x7F000000;
-
- if ((h->return_localaddress == GNUNET_NO) && (value == 0))
- {
- return GNUNET_OK;
- }
- if ((GNUNET_YES == h->use_localaddresses) || (value != 0))
- {
- add_ip_to_address_list (h, LAL_INTERFACE_ADDRESS, &s4->sin_addr,
- sizeof (struct in_addr));
- }
- break;
- case AF_INET6:
- /* check if we're bound to the "ANY" IP address */
- have_any = GNUNET_NO;
- for (i=0;i<h->num_local_addrs;i++)
- {
- if (h->local_addrs[i]->sa_family != AF_INET6)
- continue;
- if (0 == memcmp (&any6,
- &((struct sockaddr_in6*) h->local_addrs[i])->sin6_addr,
- sizeof (struct in6_addr)))
- {
- have_any = GNUNET_YES;
- break;
- }
- }
- if (GNUNET_NO == have_any)
- return GNUNET_OK; /* not bound to "ANY" IP (::0) but to specific IP addresses,
- do not use those from interfaces */
-
- s6 = (struct sockaddr_in6 *) addr;
- if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr))
- {
- /* skip link local addresses */
- return GNUNET_OK;
- }
- if ((h->return_localaddress == GNUNET_NO) &&
- (IN6_IS_ADDR_LOOPBACK (&((struct sockaddr_in6 *) addr)->sin6_addr)))
- {
- 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);
- return GNUNET_OK;
- }
- if ( (h->internal_address == NULL) &&
- (h->server_proc == NULL) &&
- (h->server_read_task == NULL) &&
- (GNUNET_YES == isDefault) &&
- ( (addr->sa_family == AF_INET) ||
- (addr->sa_family == AF_INET6) ) )
- {
- /* 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);
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Task that restarts the gnunet-helper-nat-server process after a crash
- * after a certain delay.
- *
- * @param cls the `struct GNUNET_NAT_Handle`
- */
-static void
-restart_nat_server (void *cls)
-{
- struct GNUNET_NAT_Handle *h = cls;
-
- h->server_read_task = NULL;
- start_gnunet_nat_server (h);
-}
-
-
-/**
- * We have been notified that gnunet-helper-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
- */
-static void
-nat_server_read (void *cls)
-{
- struct GNUNET_NAT_Handle *h = cls;
- char mybuf[40];
- ssize_t bytes;
- size_t i;
- int port;
- const char *port_start;
- struct sockaddr_in sin_addr;
-
- h->server_read_task = NULL;
- memset (mybuf, 0, sizeof (mybuf));
- bytes =
- GNUNET_DISK_file_read (h->server_stdout_handle, mybuf, sizeof (mybuf));
- if (bytes < 1)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Finished reading from server stdout with code: %d\n",
- bytes);
- if (0 != GNUNET_OS_process_kill (h->server_proc, GNUNET_TERM_SIG))
- GNUNET_log_from_strerror (GNUNET_ERROR_TYPE_WARNING, "nat", "kill");
- GNUNET_OS_process_wait (h->server_proc);
- GNUNET_OS_process_destroy (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_STD_BACKOFF (h->server_retry_delay);
- h->server_read_task =
- GNUNET_SCHEDULER_add_delayed (h->server_retry_delay,
- &restart_nat_server, h);
- 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-helper-nat-server? */
- LOG (GNUNET_ERROR_TYPE_WARNING, "nat",
- _("gnunet-helper-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);
- LOG (GNUNET_ERROR_TYPE_DEBUG, "gnunet-helper-nat-server read: %s:%d\n", mybuf,
- port);
- 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-helper-nat-server (if it is not
- * already running).
- *
- * @param h handle to NAT
- */
-static void
-start_gnunet_nat_server (struct GNUNET_NAT_Handle *h)
-{
- char *binary;
-
- 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_YES, GNUNET_NO, GNUNET_YES))))
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Starting `%s' at `%s'\n",
- "gnunet-helper-nat-server", h->internal_address);
- /* Start the server process */
- binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-server");
- h->server_proc =
- GNUNET_OS_start_process (GNUNET_NO, 0, NULL, h->server_stdout, NULL,
- binary,
- "gnunet-helper-nat-server",
- h->internal_address, NULL);
- GNUNET_free (binary);
- if (h->server_proc == NULL)
- {
- LOG (GNUNET_ERROR_TYPE_WARNING, "nat", _("Failed to start %s\n"),
- "gnunet-helper-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);
- }
- }
-}
-
-
-/**
- * Task to scan the local network interfaces for IP addresses.
- *
- * @param cls the NAT handle
- */
-static void
-list_interfaces (void *cls)
-{
- struct GNUNET_NAT_Handle *h = cls;
-
- h->ifc_task = NULL;
- remove_from_address_list_by_source (h, LAL_INTERFACE_ADDRESS);
- GNUNET_OS_network_interfaces_list (&process_interfaces, h);
- h->ifc_task =
- GNUNET_SCHEDULER_add_delayed (h->ifc_scan_frequency,
- &list_interfaces, h);
-}
-
-
-/**
- * Callback with the result from the STUN request.
- *
- * @param cls the NAT handle
- * @param result the status
- */
-static void
-stun_request_callback (void *cls,
- enum GNUNET_NAT_StatusCode result)
-{
- struct GNUNET_NAT_Handle *h = cls;
-
- h->stun_request = NULL;
- switch (result)
- {
- case GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR:
- LOG (GNUNET_ERROR_TYPE_WARNING,
- "Failed to transmit STUN request\n");
- break;
- case GNUNET_NAT_ERROR_NOT_ONLINE:
- LOG (GNUNET_ERROR_TYPE_WARNING,
- "Failed to resolve STUN server (are we online?)\n");
- break;
- case GNUNET_NAT_ERROR_SUCCESS:
- /* all good, STUN request active */
- h->waiting_stun = GNUNET_YES;
- break;
- default:
- /* unexpected error code for STUN */
- GNUNET_break (0);
- }
-}
-
-
-/**
- * CHECK if is a valid STUN packet sending to GNUNET_NAT_stun_handle_packet().
- * It also check if it can handle the packet based on the NAT handler.
- * You don't need to call anything else to check if the packet is valid,
- *
- * @param cls the NAT handle
- * @param data packet
- * @param len packet length
- * @return #GNUNET_NO if it can't decode, #GNUNET_YES if is a packet
- */
-int
-GNUNET_NAT_is_valid_stun_packet (void *cls,
- const void *data,
- size_t len)
-{
- struct GNUNET_NAT_Handle *h = cls;
- struct sockaddr_in answer;
-
- /* We are not expecting a STUN message */
- if (GNUNET_YES != h->waiting_stun)
- return GNUNET_NO;
-
- /* We dont have STUN installed */
- if (! h->use_stun)
- return GNUNET_NO;
-
- /* Empty the answer structure */
- memset (&answer,
- 0,
- sizeof(struct sockaddr_in));
-
- /* Lets handle the packet*/
- if (GNUNET_NO ==
- GNUNET_NAT_stun_handle_packet (data,
- len,
- &answer))
- return GNUNET_NO;
-
- LOG (GNUNET_ERROR_TYPE_INFO,
- "STUN server returned %s:%d\n",
- inet_ntoa (answer.sin_addr),
- ntohs (answer.sin_port));
- /* Remove old IPs from previous STUN calls */
- remove_from_address_list_by_source (h,
- LAL_EXTERNAL_STUN_IP);
- /* Add new IP from STUN packet */
- add_to_address_list (h,
- LAL_EXTERNAL_STUN_IP,
- (const struct sockaddr *) &answer,
- sizeof (struct sockaddr_in));
- h->waiting_stun = GNUNET_NO;
- return GNUNET_YES;
-}
-
-
-/**
- * Task to do a STUN request
- *
- * @param cls the NAT handle
- */
-static void
-process_stun (void *cls)
-{
- struct GNUNET_NAT_Handle *h = cls;
- struct StunServerList *elem = h->actual_stun_server;
-
- h->stun_task = NULL;
- /* Make the request */
- LOG (GNUNET_ERROR_TYPE_INFO,
- "I will request the stun server %s:%i\n",
- elem->address,
- elem->port);
- if (NULL != h->stun_request)
- {
- GNUNET_NAT_stun_make_request_cancel (h->stun_request);
- h->stun_request = NULL;
- }
- h->waiting_stun = GNUNET_NO;
- h->stun_request
- = GNUNET_NAT_stun_make_request (elem->address,
- elem->port,
- h->socket,
- &stun_request_callback,
- h);
- if (NULL == h->stun_request)
- {
- LOG (GNUNET_ERROR_TYPE_ERROR,
- "STUN request to %s:%i failed\n",
- elem->address,
- elem->port);
- }
- h->stun_task =
- GNUNET_SCHEDULER_add_delayed (h->stun_frequency,
- &process_stun,
- h);
-
- /* Set actual Server*/
- if (NULL != elem->next)
- {
- h->actual_stun_server = elem->next;
- }
- else
- {
- h->actual_stun_server = h->stun_servers_head;
- }
-}
-
-
-/**
- * Task to do a lookup on our hostname for IP addresses.
- *
- * @param cls the NAT handle
- */
-static void
-resolve_hostname (void *cls)
-{
- struct GNUNET_NAT_Handle *h = cls;
-
- h->hostname_task = NULL;
- remove_from_address_list_by_source (h, LAL_HOSTNAME_DNS);
- GNUNET_assert (NULL == h->hostname_dns);
- h->hostname_dns =
- GNUNET_RESOLVER_hostname_resolve (AF_UNSPEC,
- HOSTNAME_RESOLVE_TIMEOUT,
- &process_hostname_ip,
- h);
-}
-
-
-/**
- * Task to do DNS lookup on our external hostname to
- * get DynDNS-IP addresses.
- *
- * @param cls the NAT handle
- */
-static void
-resolve_dns (void *cls)
-{
- struct GNUNET_NAT_Handle *h = cls;
- struct LocalAddressList *pos;
-
- h->dns_task = NULL;
- for (pos = h->lal_head; NULL != pos; pos = pos->next)
- if (pos->source == LAL_EXTERNAL_IP)
- pos->source = LAL_EXTERNAL_IP_OLD;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Resolving external address `%s'\n",
- h->external_address);
- GNUNET_assert (NULL == h->ext_dns);
- h->ext_dns =
- GNUNET_RESOLVER_ip_get (h->external_address,
- AF_INET,
- GNUNET_TIME_UNIT_MINUTES,
- &process_external_ip,
- h);
-}
-
-
-/**
- * Add or remove UPnP-mapped addresses.
- *
- * @param cls the `struct GNUNET_NAT_Handle`
- * @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 @a addr
- * @param ret GNUNET_NAT_ERROR_SUCCESS on success, otherwise an error code
- */
-static void
-upnp_add (void *cls,
- int add_remove,
- const struct sockaddr *addr,
- socklen_t addrlen,
- enum GNUNET_NAT_StatusCode ret)
-{
- struct GNUNET_NAT_Handle *h = cls;
- struct LocalAddressList *pos;
- struct LocalAddressList *next;
-
-
- if (GNUNET_NAT_ERROR_SUCCESS != ret)
- {
- /* Error while running upnp client */
- LOG (GNUNET_ERROR_TYPE_ERROR,
- _("Error while running upnp client:\n"));
- //FIXME: convert error code to string
- return;
- }
-
- if (GNUNET_YES == add_remove)
- {
- add_to_address_list (h,
- LAL_UPNP,
- addr,
- addrlen);
- return;
- }
- else if (GNUNET_NO == add_remove)
- {
- /* remove address */
- next = h->lal_head;
- while (NULL != (pos = next))
- {
- next = pos->next;
- if ((pos->source != LAL_UPNP) || (pos->addrlen != addrlen) ||
- (0 != memcmp (&pos[1], addr, addrlen)))
- continue;
- GNUNET_CONTAINER_DLL_remove (h->lal_head,
- h->lal_tail,
- pos);
- if (NULL != h->address_callback)
- h->address_callback (h->callback_cls,
- GNUNET_NO,
- (const struct sockaddr *) &pos[1],
- pos->addrlen);
- GNUNET_free (pos);
- return; /* only remove once */
- }
- /* asked to remove address that does not exist */
- LOG (GNUNET_ERROR_TYPE_ERROR,
- "Asked to remove unkown address `%s'\n",
- GNUNET_a2s(addr, addrlen));
- GNUNET_break (0);
- }
- else
- {
-
- GNUNET_break (0);
- }
-}
-
-
-/**
- * Try to add a port mapping using UPnP.
- *
- * @param h overall NAT handle
- * @param port port to map with UPnP
- */
-static void
-add_minis (struct GNUNET_NAT_Handle *h,
- uint16_t port)
-{
- struct MiniList *ml;
-
- ml = h->mini_head;
- while (NULL != ml)
- {
- if (port == ml->port)
- return; /* already got this port */
- ml = ml->next;
- }
-
- ml = GNUNET_new (struct MiniList);
- ml->port = port;
- ml->mini = GNUNET_NAT_mini_map_start (port, h->is_tcp, &upnp_add, h);
-
- if (NULL == ml->mini)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Failed to run upnp client for port %u\n"), ml->port);
- GNUNET_free (ml);
- return;
- }
-
- GNUNET_CONTAINER_DLL_insert (h->mini_head,
- h->mini_tail,
- ml);
-}
-
-
-/**
- * Task to add addresses from original bind to set of valid addrs.
- *
- * @param h the NAT handle
- */
-static void
-add_from_bind (struct GNUNET_NAT_Handle *h)
-{
- static struct in6_addr any = IN6ADDR_ANY_INIT;
-
- unsigned int i;
- struct sockaddr *sa;
- const struct sockaddr_in *v4;
-
- for (i = 0; i < h->num_local_addrs; i++)
- {
- sa = h->local_addrs[i];
- switch (sa->sa_family)
- {
- case AF_INET:
- if (sizeof (struct sockaddr_in) != h->local_addrlens[i])
- {
- GNUNET_break (0);
- break;
- }
- v4 = (const struct sockaddr_in *) sa;
- if (0 != v4->sin_addr.s_addr)
- add_to_address_list (h,
- LAL_BINDTO_ADDRESS, sa,
- sizeof (struct sockaddr_in));
- if (h->enable_upnp)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Running upnp client for address `%s'\n",
- GNUNET_a2s (sa,sizeof (struct sockaddr_in)));
- add_minis (h, ntohs (v4->sin_port));
- }
- break;
- case AF_INET6:
- if (sizeof (struct sockaddr_in6) != h->local_addrlens[i])
- {
- GNUNET_break (0);
- break;
- }
- if (0 !=
- memcmp (&((const struct sockaddr_in6 *) sa)->sin6_addr,
- &any,
- sizeof (struct in6_addr)))
- add_to_address_list (h,
- LAL_BINDTO_ADDRESS,
- sa,
- sizeof (struct sockaddr_in6));
- break;
- default:
- break;
- }
- }
-}
-
-
-/**
- * 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.
- *
- * @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 @a addrs
- * @param addrs the 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
- * @param callback_cls closure for callbacks
- * @param sock used socket
- * @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,
- 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_NETWORK_Handle *sock)
-{
- struct GNUNET_NAT_Handle *h;
- struct in_addr in_addr;
- unsigned int i;
- char *binary;
-
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Registered with NAT service at port %u with %u IP bound local addresses\n",
- (unsigned int) adv_port, num_addrs);
- h = GNUNET_new (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 (0 != num_addrs)
- {
- h->local_addrs = GNUNET_malloc (num_addrs * sizeof (struct sockaddr *));
- h->local_addrlens = GNUNET_malloc (num_addrs * sizeof (socklen_t));
- for (i = 0; i < num_addrs; i++)
- {
- GNUNET_assert (addrlens[i] > 0);
- GNUNET_assert (addrs[i] != NULL);
- h->local_addrlens[i] = addrlens[i];
- h->local_addrs[i] = GNUNET_malloc (addrlens[i]);
- GNUNET_memcpy (h->local_addrs[i], addrs[i], addrlens[i]);
- }
- }
- if (GNUNET_OK ==
- GNUNET_CONFIGURATION_have_value (cfg, "nat", "INTERNAL_ADDRESS"))
- {
- (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_config_invalid (GNUNET_ERROR_TYPE_WARNING,
- "nat", "INTERNAL_ADDRESS",
- _("malformed"));
- 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);
- }
- h->behind_nat =
- GNUNET_CONFIGURATION_get_value_yesno (cfg, "nat", "BEHIND_NAT");
- h->nat_punched =
- GNUNET_CONFIGURATION_get_value_yesno (cfg, "nat", "PUNCHED_NAT");
- h->enable_nat_client =
- GNUNET_CONFIGURATION_get_value_yesno (cfg, "nat", "ENABLE_ICMP_CLIENT");
- h->enable_nat_server =
- GNUNET_CONFIGURATION_get_value_yesno (cfg, "nat", "ENABLE_ICMP_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->return_localaddress =
- GNUNET_CONFIGURATION_get_value_yesno (cfg, "nat",
- "RETURN_LOCAL_ADDRESSES");
-
- h->use_hostname =
- GNUNET_CONFIGURATION_get_value_yesno (cfg, "nat", "USE_HOSTNAME");
- h->disable_ipv6 =
- GNUNET_CONFIGURATION_get_value_yesno (cfg, "nat", "DISABLEV6");
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (cfg, "nat", "DYNDNS_FREQUENCY",
- &h->dyndns_frequency))
- h->dyndns_frequency = DYNDNS_FREQUENCY;
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (cfg, "nat", "IFC_SCAN_FREQUENCY",
- &h->ifc_scan_frequency))
- h->ifc_scan_frequency = IFC_SCAN_FREQUENCY;
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (cfg, "nat", "HOSTNAME_DNS_FREQUENCY",
- &h->hostname_dns_frequency))
- h->hostname_dns_frequency = HOSTNAME_DNS_FREQUENCY;
-
- if (NULL == reversal_callback)
- h->enable_nat_server = GNUNET_NO;
-
- /* Check for UPnP client, disable immediately if not available */
- if ( (GNUNET_YES == h->enable_upnp) &&
- (GNUNET_SYSERR ==
- GNUNET_OS_check_helper_binary ("upnpc", GNUNET_NO, NULL)) )
- {
- LOG (GNUNET_ERROR_TYPE_ERROR,
- _("UPnP enabled in configuration, but UPnP client `upnpc` command not found, disabling UPnP \n"));
- h->enable_upnp = GNUNET_NO;
- }
-
- /* STUN */
- h->use_stun =
- GNUNET_CONFIGURATION_get_value_yesno (cfg,
- "nat",
- "USE_STUN");
-
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (cfg,
- "nat",
- "STUN_FREQUENCY",
- &h->stun_frequency))
- h->stun_frequency = STUN_FREQUENCY;
-
-
- /* Check if NAT was hole-punched */
- if ((NULL != h->address_callback) &&
- (NULL != h->external_address) &&
- (GNUNET_YES == h->nat_punched))
- {
- h->dns_task = GNUNET_SCHEDULER_add_now (&resolve_dns, h);
- h->enable_nat_server = GNUNET_NO;
- h->enable_upnp = GNUNET_NO;
- h->use_stun = GNUNET_NO;
- }
- else
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "No external IP address given to add to our list of addresses\n");
- }
-
- /* ENABLE STUN ONLY ON UDP */
- if( (! is_tcp) &&
- (NULL != sock) &&
- h->use_stun)
- {
- char *stun_servers;
- size_t urls;
- ssize_t pos;
- size_t pos_port;
-
- h->socket = sock;
- stun_servers = NULL;
- /* Lets process the servers*/
- (void) GNUNET_CONFIGURATION_get_value_string (cfg,
- "nat",
- "STUN_SERVERS",
- &stun_servers);
- urls = 0;
- if ( (NULL != stun_servers) &&
- (strlen (stun_servers) > 0) )
- {
- pos_port = 0;
- for (pos = strlen (stun_servers) - 1;
- pos >= 0;
- pos--)
- {
- if (stun_servers[pos] == ':')
- {
- pos_port = pos + 1;
- stun_servers[pos] = '\0';
- continue;
- }
- if ((stun_servers[pos] == ' ') || (0 == pos))
- {
- struct StunServerList *ml;
-
- /* Check if we do have a port */
- if ((0 == pos_port) || (pos_port <= pos))
- {
- LOG (GNUNET_ERROR_TYPE_WARNING,
- "STUN server format mistake\n");
- break;
- }
- urls++;
- ml = GNUNET_new (struct StunServerList);
- ml->port = atoi (&stun_servers[pos_port]);
-
- /* Remove trailing space */
- if (stun_servers[pos] == ' ')
- ml->address = GNUNET_strdup (&stun_servers[pos + 1]);
- else
- ml->address = GNUNET_strdup (&stun_servers[pos]);
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Found STUN server %s:%i\n",
- ml->address,
- ml->port);
- GNUNET_CONTAINER_DLL_insert (h->stun_servers_head,
- h->stun_servers_tail,
- ml);
- stun_servers[pos] = '\0';
- }
- }
- }
- if (0 == urls)
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
- "nat",
- "STUN_SERVERS");
- }
- else
- {
- /* Set the actual STUN server*/
- h->actual_stun_server = h->stun_servers_head;
- }
- h->stun_task = GNUNET_SCHEDULER_add_now (&process_stun,
- h);
- GNUNET_free_non_null (stun_servers);
- }
-
-
- /* Test for SUID binaries */
- binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-server");
- if ( (GNUNET_YES == h->behind_nat) &&
- (GNUNET_YES == h->enable_nat_server) &&
- (GNUNET_YES !=
- GNUNET_OS_check_helper_binary (binary,
- GNUNET_YES,
- "-d 127.0.0.1" )))
- {
- // use localhost as source for that one udp-port, ok for testing
- h->enable_nat_server = GNUNET_NO;
- LOG (GNUNET_ERROR_TYPE_WARNING,
- _("Configuration requires `%s', but binary is not installed properly (SUID bit not set). Option disabled.\n"),
- "gnunet-helper-nat-server");
- }
- GNUNET_free (binary);
- binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-client");
- if ((GNUNET_YES == h->enable_nat_client) &&
- (GNUNET_YES !=
- GNUNET_OS_check_helper_binary (binary,
- GNUNET_YES,
- "-d 127.0.0.1 127.0.0.2 42"))) /* none of these parameters are actually used in privilege testing mode */
- {
- h->enable_nat_client = GNUNET_NO;
- LOG (GNUNET_ERROR_TYPE_WARNING,
- _("Configuration requires `%s', but binary is not installed properly (SUID bit not set). Option disabled.\n"),
- "gnunet-helper-nat-client");
- }
- GNUNET_free (binary);
- start_gnunet_nat_server (h);
-
- /* FIXME: add support for UPnP, etc */
-
- if (NULL != h->address_callback)
- {
- h->ifc_task = GNUNET_SCHEDULER_add_now (&list_interfaces,
- h);
- if (GNUNET_YES == h->use_hostname)
- h->hostname_task = GNUNET_SCHEDULER_add_now (&resolve_hostname,
- h);
- }
- add_from_bind (h);
-
- return h;
-}
-
-
-/**
- * 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)
-{
- unsigned int i;
- struct LocalAddressList *lal;
- struct MiniList *ml;
- struct StunServerList *ssl;
-
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "NAT unregister called\n");
- while (NULL != (ssl = h->stun_servers_head))
- {
- GNUNET_CONTAINER_DLL_remove (h->stun_servers_head,
- h->stun_servers_tail,
- ssl);
- GNUNET_free (ssl->address);
- GNUNET_free (ssl);
- }
- while (NULL != (lal = h->lal_head))
- {
- GNUNET_CONTAINER_DLL_remove (h->lal_head,
- h->lal_tail,
- lal);
- if (NULL != h->address_callback)
- h->address_callback (h->callback_cls,
- GNUNET_NO,
- (const struct sockaddr *) &lal[1],
- lal->addrlen);
- GNUNET_free (lal);
- }
- while (NULL != (ml = h->mini_head))
- {
- GNUNET_CONTAINER_DLL_remove (h->mini_head,
- h->mini_tail,
- ml);
- if (NULL != ml->mini)
- GNUNET_NAT_mini_map_stop (ml->mini);
- GNUNET_free (ml);
- }
- if (NULL != h->ext_dns)
- {
- 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 (NULL != h->server_read_task)
- {
- GNUNET_SCHEDULER_cancel (h->server_read_task);
- h->server_read_task = NULL;
- }
- if (NULL != h->ifc_task)
- {
- GNUNET_SCHEDULER_cancel (h->ifc_task);
- h->ifc_task = NULL;
- }
- if (NULL != h->hostname_task)
- {
- GNUNET_SCHEDULER_cancel (h->hostname_task);
- h->hostname_task = NULL;
- }
- if (NULL != h->dns_task)
- {
- GNUNET_SCHEDULER_cancel (h->dns_task);
- h->dns_task = NULL;
- }
- if (NULL != h->stun_task)
- {
- GNUNET_SCHEDULER_cancel (h->stun_task);
- h->stun_task = NULL;
- }
- if (NULL != h->stun_request)
- {
- GNUNET_NAT_stun_make_request_cancel (h->stun_request);
- h->stun_request = NULL;
- }
- if (NULL != h->server_proc)
- {
- if (0 != GNUNET_OS_process_kill (h->server_proc,
- GNUNET_TERM_SIG))
- GNUNET_log_from_strerror (GNUNET_ERROR_TYPE_WARNING,
- "nat",
- "kill");
- GNUNET_OS_process_wait (h->server_proc);
- GNUNET_OS_process_destroy (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;
- }
- for (i = 0; i < h->num_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-helper-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)
- * @return #GNUNET_SYSERR on error, #GNUNET_NO if nat client is disabled,
- * #GNUNET_OK otherwise
- */
-int
-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;
- char *binary;
-
- if (GNUNET_YES != h->enable_nat_client)
- return GNUNET_NO; /* not permitted / possible */
-
- if (h->internal_address == NULL)
- {
- LOG (GNUNET_ERROR_TYPE_WARNING, "nat",
- _("Internal IP address not known, cannot use ICMP NAT traversal method\n"));
- return GNUNET_SYSERR;
- }
- GNUNET_assert (sa->sin_family == AF_INET);
- if (NULL == inet_ntop (AF_INET, &sa->sin_addr, inet4, INET_ADDRSTRLEN))
- {
- GNUNET_log_from_strerror (GNUNET_ERROR_TYPE_WARNING,
- "nat",
- "inet_ntop");
- return GNUNET_SYSERR;
- }
- GNUNET_snprintf (port_as_string,
- sizeof (port_as_string),
- "%d",
- h->adv_port);
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- _("Running gnunet-helper-nat-client %s %s %u\n"),
- h->internal_address,
- inet4,
- (unsigned int) h->adv_port);
- binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-client");
- proc =
- GNUNET_OS_start_process (GNUNET_NO, 0, NULL, NULL, NULL,
- binary,
- "gnunet-helper-nat-client",
- h->internal_address,
- inet4, port_as_string, NULL);
- GNUNET_free (binary);
- if (NULL == proc)
- return GNUNET_SYSERR;
- /* we know that the gnunet-helper-nat-client will terminate virtually
- * instantly */
- GNUNET_OS_process_wait (proc);
- GNUNET_OS_process_destroy (proc);
- return GNUNET_OK;
-}
-
-
-/**
- * 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 @a 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;
- char pbuf[INET6_ADDRSTRLEN+1];
-
- if ((addrlen != sizeof (struct in_addr)) &&
- (addrlen != sizeof (struct in6_addr)))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- for (pos = h->lal_head; NULL != pos; pos = pos->next)
- {
- 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);
- }
- }
- LOG (GNUNET_ERROR_TYPE_WARNING,
- "Asked to validate one of my addresses (%s) and validation failed!\n",
- inet_ntop ((addrlen == sizeof(struct in_addr))
- ? AF_INET
- : AF_INET6,
- addr,
- pbuf, sizeof (pbuf)));
- return GNUNET_NO;
-}
-
-/**
- * Converts enum GNUNET_NAT_StatusCode to a string
- *
- * @param err error code to resolve to a string
- * @return pointer to a static string containing the error code
- */
-const char *
-GNUNET_NAT_status2string (enum GNUNET_NAT_StatusCode err)
-{
- switch (err)
- {
- case GNUNET_NAT_ERROR_SUCCESS:
- return _ ("Operation Successful");
- case GNUNET_NAT_ERROR_IPC_FAILURE:
- return _ ("Internal Failure (IPC, ...)");
- case GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR:
- return _ ("Failure in network subsystem, check permissions.");
- case GNUNET_NAT_ERROR_TIMEOUT:
- return _ ("Encountered timeout while performing operation");
- case GNUNET_NAT_ERROR_NOT_ONLINE:
- return _ ("detected that we are offline");
- case GNUNET_NAT_ERROR_UPNPC_NOT_FOUND:
- return _ ("`upnpc` command not found");
- case GNUNET_NAT_ERROR_UPNPC_FAILED:
- return _ ("Failed to run `upnpc` command");
- case GNUNET_NAT_ERROR_UPNPC_TIMEOUT:
- return _ ("`upnpc' command took too long, process killed");
- case GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED:
- return _ ("`upnpc' command failed to establish port mapping");
- case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND:
- return _ ("`external-ip' command not found");
- case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED:
- return _ ("Failed to run `external-ip` command");
- case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID:
- return _ ("`external-ip' command output invalid");
- case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID:
- return _ ("no valid address was returned by `external-ip'");
- case GNUNET_NAT_ERROR_NO_VALID_IF_IP_COMBO:
- return _ ("Could not determine interface with internal/local network address");
- case GNUNET_NAT_ERROR_HELPER_NAT_SERVER_NOT_FOUND:
- return _ ("No functioning gnunet-helper-nat-server installation found");
- case GNUNET_NAT_ERROR_NAT_TEST_START_FAILED:
- return _ ("NAT test could not be initialized");
- case GNUNET_NAT_ERROR_NAT_TEST_TIMEOUT:
- return _ ("NAT test timeout reached");
- case GNUNET_NAT_ERROR_NAT_REGISTER_FAILED:
- return _ ("could not register NAT");
- case GNUNET_NAT_ERROR_HELPER_NAT_CLIENT_NOT_FOUND:
- return _ ("No working gnunet-helper-nat-client installation found");
-/* case:
- return _ ("");*/
- default:
- return "unknown status code";
- }
-}
-
-/* end of nat.c */
+++ /dev/null
-/*
- This file is part of GNUnet.
- Copyright (C) 2015 GNUnet e.V.
-
- GNUnet is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published
- by the Free Software Foundation; either version 3, or (at your
- option) any later version.
-
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with GNUnet; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA.
-*/
-
-/**
- * @file nat/nat_auto.c
- * @brief functions for auto-configuration of the network
- * @author Christian Grothoff
- * @author Bruno Cabral
- */
-#include "platform.h"
-#include "gnunet_util_lib.h"
-#include "gnunet_resolver_service.h"
-#include "gnunet_nat_lib.h"
-#include "nat.h"
-
-#define LOG(kind,...) GNUNET_log_from (kind, "nat", __VA_ARGS__)
-
-
-/**
- * How long do we wait for the NAT test to report success?
- */
-#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
-
-#define NAT_SERVER_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
-
-/**
- * Phases of the auto configuration.
- */
-enum AutoPhase
-{
- /**
- * Initial start value.
- */
- AUTO_INIT = 0,
-
- /**
- * Test our external IP.
- */
- AUTO_EXTERNAL_IP,
-
- /**
- * Test our external IP.
- */
- AUTO_STUN,
-
- /**
- * Test our internal IP.
- */
- AUTO_LOCAL_IP,
-
- /**
- * Test if NAT was punched.
- */
- AUTO_NAT_PUNCHED,
-
- /**
- * Test if UPnP is working.
- */
- AUTO_UPNPC,
-
- /**
- * Test if ICMP server works.
- */
- AUTO_ICMP_SERVER,
-
- /**
- * Test if ICMP client works.
- */
- AUTO_ICMP_CLIENT,
-
- /**
- * Last phase, we're done.
- */
- AUTO_DONE
-
-};
-
-
-/**
- * Handle to auto-configuration in progress.
- */
-struct GNUNET_NAT_AutoHandle
-{
-
- /**
- * Handle to the active NAT test.
- */
- struct GNUNET_NAT_Test *tst;
-
- /**
- * Function to call when done.
- */
- GNUNET_NAT_AutoResultCallback fin_cb;
-
- /**
- * Closure for @e fin_cb.
- */
- void *fin_cb_cls;
-
- /**
- * Handle for active 'GNUNET_NAT_mini_get_external_ipv4'-operation.
- */
- struct GNUNET_NAT_ExternalHandle *eh;
-
- /**
- * Current configuration (with updates from previous phases)
- */
- struct GNUNET_CONFIGURATION_Handle *cfg;
-
- /**
- * Original configuration (used to calculate differences)
- */
- struct GNUNET_CONFIGURATION_Handle *initial_cfg;
-
- /**
- * Task identifier for the timeout.
- */
- struct GNUNET_SCHEDULER_Task *task;
-
- /**
- * Message queue to the gnunet-nat-server.
- */
- struct GNUNET_MQ_Handle *mq;
-
- /**
- * Where are we in the test?
- */
- enum AutoPhase phase;
-
- /**
- * Situation of the NAT
- */
- enum GNUNET_NAT_Type type;
-
- /**
- * Do we have IPv6?
- */
- int have_v6;
-
- /**
- * UPnP already set the external ip address ?
- */
- int upnp_set_external_address;
-
- /**
- * Did the external server connected back ?
- */
- int connected_back;
-
- /**
- * Address detected by STUN
- */
- char *stun_ip;
-
- unsigned int stun_port;
-
- /**
- * Internal IP is the same as the public one ?
- */
- int internal_ip_is_public;
-
- /**
- * Error code for better debugging and user feedback
- */
- enum GNUNET_NAT_StatusCode ret;
-};
-
-
-/**
- * The listen socket of the service for IPv4
- */
-static struct GNUNET_NETWORK_Handle *lsock4;
-
-/**
- * The listen task ID for IPv4
- */
-static struct GNUNET_SCHEDULER_Task *ltask4;
-
-/**
- * The port the test service is running on (default 7895)
- */
-static unsigned long long port = 7895;
-
-static char *stun_server = "stun.ekiga.net";
-
-static unsigned int stun_port = 3478;
-
-
-/**
- * Run the next phase of the auto test.
- *
- * @param ah auto test handle
- */
-static void
-next_phase (struct GNUNET_NAT_AutoHandle *ah);
-
-
-static void
-process_stun_reply(struct sockaddr_in *answer,
- struct GNUNET_NAT_AutoHandle *ah)
-{
- ah->stun_ip = inet_ntoa(answer->sin_addr);
- ah->stun_port = ntohs (answer->sin_port);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "External IP is: %s , with port %u\n",
- ah->stun_ip,
- ah->stun_port);
- next_phase (ah);
-}
-
-
-/**
- * Function that terminates the test.
- */
-static void
-stop_stun ()
-{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Stopping STUN and quitting...\n");
- /* Clean task */
- if (NULL != ltask4)
- {
- GNUNET_SCHEDULER_cancel (ltask4);
- ltask4 = NULL;
- }
- /* Clean socket */
- if (NULL != lsock4)
- {
- GNUNET_NETWORK_socket_close (lsock4);
- lsock4 = NULL;
- }
-}
-
-
-/**
- * Activity on our incoming socket. Read data from the
- * incoming connection.
- *
- * @param cls
- */
-static void
-do_udp_read (void *cls)
-{
- struct GNUNET_NAT_AutoHandle *ah = cls;
- unsigned char reply_buf[1024];
- ssize_t rlen;
- struct sockaddr_in answer;
- const struct GNUNET_SCHEDULER_TaskContext *tc;
-
- tc = GNUNET_SCHEDULER_get_task_context ();
- if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
- (GNUNET_NETWORK_fdset_isset (tc->read_ready,
- lsock4)))
- {
- rlen = GNUNET_NETWORK_socket_recv (lsock4,
- reply_buf,
- sizeof (reply_buf));
-
- //Lets handle the packet
- memset (&answer, 0, sizeof(struct sockaddr_in));
- if (ah->phase == AUTO_NAT_PUNCHED)
- {
- //Destroy the connection
- GNUNET_NETWORK_socket_close (lsock4);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "The external server was able to connect back");
- ah->connected_back = GNUNET_YES;
- next_phase (ah);
- }
- else
- {
- if (GNUNET_OK ==
- GNUNET_NAT_stun_handle_packet (reply_buf, rlen, &answer))
- {
- //Process the answer
- process_stun_reply (&answer, ah);
- }
- else
- {
- next_phase (ah);
- }
- }
- }
- else
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "TIMEOUT while waiting for an answer\n");
- if (ah->phase == AUTO_NAT_PUNCHED)
- {
- stop_stun();
- }
-
- next_phase (ah);
- }
-}
-
-
-/**
- * Create an IPv4 listen socket bound to our port.
- *
- * @return NULL on error
- */
-static struct GNUNET_NETWORK_Handle *
-bind_v4 ()
-{
- struct GNUNET_NETWORK_Handle *ls;
- struct sockaddr_in sa4;
- int eno;
-
- memset (&sa4, 0, sizeof (sa4));
- sa4.sin_family = AF_INET;
- sa4.sin_port = htons (port);
-#if HAVE_SOCKADDR_IN_SIN_LEN
- sa4.sin_len = sizeof (sa4);
-#endif
- ls = GNUNET_NETWORK_socket_create (AF_INET,
- SOCK_DGRAM,
- 0);
- if (NULL == ls)
- return NULL;
- if (GNUNET_OK !=
- GNUNET_NETWORK_socket_bind (ls, (const struct sockaddr *) &sa4,
- sizeof (sa4)))
- {
- eno = errno;
- GNUNET_NETWORK_socket_close (ls);
- errno = eno;
- return NULL;
- }
- return ls;
-}
-
-
-static void
-request_callback (void *cls,
- enum GNUNET_NAT_StatusCode result)
-{
- // struct GNUNET_NAT_AutoHandle *ah = cls;
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Request callback: stop and quit\n");
- stop_stun ();
-
- // next_phase (ah); FIXME this always will be NULL, as called in test_stun()
-}
-
-
-/**
- * Function called by NAT to report the outcome of the nat-test.
- * Clean up and update GUI.
- *
- * @param cls the auto handle
- * @param success currently always #GNUNET_OK
- * @param emsg NULL on success, otherwise an error message
- */
-static void
-result_callback (void *cls,
- enum GNUNET_NAT_StatusCode ret)
-{
- struct GNUNET_NAT_AutoHandle *ah = cls;
-
- if (GNUNET_NAT_ERROR_SUCCESS == ret)
- GNUNET_NAT_test_stop (ah->tst);
- ah->tst = NULL;
- ah->ret = ret;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- GNUNET_NAT_ERROR_SUCCESS == ret
- ? _("NAT traversal with ICMP Server succeeded.\n")
- : _("NAT traversal with ICMP Server failed.\n"));
- GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "ENABLE_ICMP_SERVER",
- GNUNET_NAT_ERROR_SUCCESS == ret ? "NO" : "YES");
- next_phase (ah);
-}
-
-
-/**
- * Main function for the connection reversal test.
- *
- * @param cls the `struct GNUNET_NAT_AutoHandle`
- */
-static void
-reversal_test (void *cls)
-{
- struct GNUNET_NAT_AutoHandle *ah = cls;
-
- ah->task = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("Testing connection reversal with ICMP server.\n"));
- GNUNET_RESOLVER_connect (ah->cfg);
- ah->tst = GNUNET_NAT_test_start (ah->cfg, GNUNET_YES, 0, 0, TIMEOUT,
- &result_callback, ah);
-}
-
-
-/**
- * Set our external IPv4 address based on the UPnP.
- *
- *
- * @param cls closure with our setup context
- * @param addr the address, NULL on errors
- * @param emsg NULL on success, otherwise an error message
- */
-static void
-set_external_ipv4 (void *cls,
- const struct in_addr *addr,
- enum GNUNET_NAT_StatusCode ret)
-{
- struct GNUNET_NAT_AutoHandle *ah = cls;
- char buf[INET_ADDRSTRLEN];
-
- ah->eh = NULL;
- ah->ret = ret;
- if (GNUNET_NAT_ERROR_SUCCESS != ret)
- {
- next_phase (ah);
- return;
- }
- /* enable 'behind nat' */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("Detected external IP `%s'\n"),
- inet_ntop (AF_INET,
- addr,
- buf,
- sizeof (buf)));
- GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "BEHIND_NAT", "YES");
-
- /* set external IP address */
- if (NULL == inet_ntop (AF_INET, addr, buf, sizeof (buf)))
- {
- GNUNET_break (0);
- /* actually, this should never happen, as the caller already executed just
- * this check, but for consistency (eg: future changes in the caller)
- * we still need to report this error...
- */
- ah->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID;
- next_phase (ah);
- return;
- }
- GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "EXTERNAL_ADDRESS",
- buf);
- ah->upnp_set_external_address = GNUNET_YES;
- next_phase (ah);
-}
-
-
-/**
- * Determine our external IPv4 address.
- *
- * @param ah auto setup context
- */
-static void
-test_external_ip (struct GNUNET_NAT_AutoHandle *ah)
-{
- if (GNUNET_NAT_ERROR_SUCCESS != ah->ret)
- next_phase (ah);
-
- // FIXME: CPS?
- /* try to detect external IP */
- ah->eh = GNUNET_NAT_mini_get_external_ipv4 (TIMEOUT,
- &set_external_ipv4, ah);
-}
-
-
-/**
- * Determine our external IPv4 address and port using an external STUN server
- *
- * @param ah auto setup context
- */
-static void
-test_stun (struct GNUNET_NAT_AutoHandle *ah)
-{
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running STUN test\n");
-
- /* Get port from the configuration */
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_number (ah->cfg,
- "transport-udp",
- "PORT",
- &port))
- {
- port = 2086;
- }
-
- //Lets create the socket
- lsock4 = bind_v4 ();
- if (NULL == lsock4)
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
- next_phase(ah);
- return;
- }
- else
- {
- //Lets call our function now when it accepts
- ltask4 = GNUNET_SCHEDULER_add_read_net (NAT_SERVER_TIMEOUT,
- lsock4,
- &do_udp_read,
- ah);
- }
-
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "STUN service listens on port %u\n",
- (unsigned int) port);
- if (GNUNET_NO ==
- GNUNET_NAT_stun_make_request (stun_server,
- stun_port,
- lsock4,
- &request_callback,
- NULL))
- {
- /*An error happened*/
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "STUN error, stopping\n");
- stop_stun ();
- next_phase (ah);
- }
-}
-
-
-/**
- * Process list of local IP addresses. Find and set the
- * one of the default interface.
- *
- * @param cls our `struct GNUNET_NAT_AutoHandle`
- * @param name name of the interface (can be NULL for unknown)
- * @param isDefault is this presumably the default interface
- * @param addr address of this interface (can be NULL for unknown or unassigned)
- * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
- * @param netmask the network mask (can be NULL for unknown or unassigned))
- * @param addrlen length of the @a addr and @a broadcast_addr
- * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort
- */
-static int
-process_if (void *cls,
- const char *name,
- int isDefault,
- const struct sockaddr *addr,
- const struct sockaddr *broadcast_addr,
- const struct sockaddr *netmask,
- socklen_t addrlen)
-{
- struct GNUNET_NAT_AutoHandle *ah = cls;
- const struct sockaddr_in *in;
- char buf[INET_ADDRSTRLEN];
-
-
- if ( (sizeof (struct sockaddr_in6) == addrlen) &&
- (0 != memcmp (&in6addr_loopback, &((const struct sockaddr_in6 *) addr)->sin6_addr,
- sizeof (struct in6_addr))) &&
- (! IN6_IS_ADDR_LINKLOCAL(&((const struct sockaddr_in6 *) addr)->sin6_addr)) )
- {
- ah->have_v6 = GNUNET_YES;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("This system has a global IPv6 address, setting IPv6 to supported.\n"));
-
- return GNUNET_OK;
- }
- if (addrlen != sizeof (struct sockaddr_in))
- return GNUNET_OK;
- in = (const struct sockaddr_in *) addr;
-
-
- /* set internal IP address */
- if (NULL == inet_ntop (AF_INET, &in->sin_addr, buf, sizeof (buf)))
- {
- GNUNET_break (0);
- return GNUNET_OK;
- }
- GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "INTERNAL_ADDRESS",
- buf);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("Detected internal network address `%s'.\n"),
- buf);
-
-
- ah->ret = GNUNET_NAT_ERROR_SUCCESS;
-
- /* Check if our internal IP is the same as the External detect by STUN*/
- if(ah->stun_ip && (strcmp(buf, ah->stun_ip) == 0) )
- {
- ah->internal_ip_is_public = GNUNET_YES;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,"A internal IP is the sameas the external");
- /* No need to continue*/
- return GNUNET_SYSERR;
- }
-
- /* no need to continue iteration if we found the default */
- if (!isDefault)
- return GNUNET_OK;
- else
- return GNUNET_SYSERR;
-}
-
-
-/**
- * Determine our local IP addresses; detect internal IP & IPv6-support
- *
- * @param ah auto setup context
- */
-static void
-test_local_ip (struct GNUNET_NAT_AutoHandle *ah)
-{
- ah->have_v6 = GNUNET_NO;
- ah->ret = GNUNET_NAT_ERROR_NO_VALID_IF_IP_COMBO; // reset to success if any of the IFs in below iterator has a valid IP
- GNUNET_OS_network_interfaces_list (&process_if, ah);
-
- GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "DISABLEV6",
- (GNUNET_YES == ah->have_v6) ? "NO" : "YES");
- next_phase (ah);
-}
-
-
-/**
- * We got disconnected from the NAT server. Stop
- * waiting for a reply.
- *
- * @param cls the `struct GNUNET_NAT_AutoHandle`
- * @param error error code
- */
-static void
-mq_error_handler (void *cls,
- enum GNUNET_MQ_Error error)
-{
- struct GNUNET_NAT_AutoHandle *ah = cls;
-
- GNUNET_MQ_destroy (ah->mq);
- ah->mq = NULL;
- /* wait a bit first? */
- next_phase (ah);
-}
-
-
-/**
- * Test if NAT has been punched
- *
- * @param ah auto setup context
- */
-static void
-test_nat_punched (struct GNUNET_NAT_AutoHandle *ah)
-{
- struct GNUNET_NAT_TestMessage *msg;
- struct GNUNET_MQ_Envelope *env;
-
- if (! ah->stun_ip)
- {
- LOG (GNUNET_ERROR_TYPE_INFO,
- "We don't have a STUN IP");
- next_phase (ah);
- return;
- }
-
- LOG (GNUNET_ERROR_TYPE_INFO,
- "Asking gnunet-nat-server to connect to `%s'\n",
- ah->stun_ip);
- ah->mq = GNUNET_CLIENT_connecT (ah->cfg,
- "gnunet-nat-server",
- NULL,
- &mq_error_handler,
- ah);
- if (NULL == ah->mq)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Failed to connect to `gnunet-nat-server'\n"));
- next_phase (ah);
- return;
- }
- env = GNUNET_MQ_msg (msg,
- GNUNET_MESSAGE_TYPE_NAT_TEST);
- msg->dst_ipv4 = inet_addr (ah->stun_ip);
- msg->dport = htons (ah->stun_port);
- msg->data = port;
- msg->is_tcp = htonl ((uint32_t) GNUNET_NO);
- GNUNET_MQ_send (ah->mq,
- env);
- if (NULL != ltask4)
- {
- GNUNET_SCHEDULER_cancel (ltask4);
- ltask4 = GNUNET_SCHEDULER_add_read_net (NAT_SERVER_TIMEOUT,
- lsock4,
- &do_udp_read,
- ah);
- }
-}
-
-
-/**
- * Test if UPnPC works.
- *
- * @param ah auto setup context
- */
-static void
-test_upnpc (struct GNUNET_NAT_AutoHandle *ah)
-{
-
- int have_upnpc;
-
- if (GNUNET_NAT_ERROR_SUCCESS != ah->ret)
- next_phase (ah);
-
- // test if upnpc is available
- have_upnpc = (GNUNET_SYSERR !=
- GNUNET_OS_check_helper_binary ("upnpc", GNUNET_NO, NULL));
- //FIXME: test if upnpc is actually working, that is, if transports start to work once we use UPnP
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- (have_upnpc)
- ? _("upnpc found, enabling its use\n")
- : _("upnpc not found\n"));
- GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "ENABLE_UPNP",
- (GNUNET_YES == have_upnpc) ? "YES" : "NO");
- next_phase (ah);
-
-}
-
-
-/**
- * Test if ICMP server is working
- *
- * @param ah auto setup context
- */
-static void
-test_icmp_server (struct GNUNET_NAT_AutoHandle *ah)
-{
-
- int ext_ip;
- int nated;
- int binary;
- char *tmp;
- char *helper;
- ext_ip = GNUNET_NO;
- nated = GNUNET_NO;
- binary = GNUNET_NO;
-
- tmp = NULL;
- helper = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-server");
- if ( (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_string (ah->cfg,
- "nat",
- "EXTERNAL_ADDRESS",
- &tmp)) &&
- (0 < strlen (tmp)) )
- {
- ext_ip = GNUNET_OK;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("test_icmp_server not possible, as we have no public IPv4 address\n"));
- }
- else
- goto err;
-
- if (GNUNET_YES ==
- GNUNET_CONFIGURATION_get_value_yesno (ah->cfg,
- "nat",
- "BEHIND_NAT"))
- {
- nated = GNUNET_YES;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("test_icmp_server not possible, as we are not behind NAT\n"));
- }
- else
- goto err;
-
- if (GNUNET_YES ==
- GNUNET_OS_check_helper_binary (helper,
- GNUNET_YES,
- "-d 127.0.0.1" ))
- {
- binary = GNUNET_OK; // use localhost as source for that one udp-port, ok for testing
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("No working gnunet-helper-nat-server found\n"));
- }
-err:
- GNUNET_free_non_null (tmp);
- GNUNET_free (helper);
-
- if ( (GNUNET_OK == ext_ip) &&
- (GNUNET_YES == nated) &&
- (GNUNET_OK == binary) )
- ah->task = GNUNET_SCHEDULER_add_now (&reversal_test,
- ah);
- else
- next_phase (ah);
-}
-
-
-/**
- * Test if ICMP client is working
- *
- * @param ah auto setup context
- */
-static void
-test_icmp_client (struct GNUNET_NAT_AutoHandle *ah)
-{
- char *tmp;
- char *helper;
-
- tmp = NULL;
- helper = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-client");
- if ( (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_string (ah->cfg,
- "nat",
- "INTERNAL_ADDRESS",
- &tmp)) &&
- (0 < strlen (tmp)) )
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("test_icmp_client not possible, as we have no internal IPv4 address\n"));
- }
- else
- goto err;
-
- if (GNUNET_YES !=
- GNUNET_CONFIGURATION_get_value_yesno (ah->cfg,
- "nat",
- "BEHIND_NAT"))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("test_icmp_server not possible, as we are not behind NAT\n"));
- }
- else
- goto err;
-
- if (GNUNET_YES ==
- GNUNET_OS_check_helper_binary (helper,
- GNUNET_YES,
- "-d 127.0.0.1 127.0.0.2 42"))
- {
- // none of these parameters are actually used in privilege testing mode
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("No working gnunet-helper-nat-server found\n"));
- }
-err:
- GNUNET_free_non_null (tmp);
- GNUNET_free (helper);
-
- next_phase (ah);
-}
-
-
-/**
- * Run the next phase of the auto test.
- */
-static void
-next_phase (struct GNUNET_NAT_AutoHandle *ah)
-{
- struct GNUNET_CONFIGURATION_Handle *diff;
-
- ah->phase++;
- switch (ah->phase)
- {
- case AUTO_INIT:
- GNUNET_assert (0);
- break;
- case AUTO_EXTERNAL_IP:
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Will run AUTO_EXTERNAL_IP\n");
- test_external_ip (ah);
- break;
- case AUTO_STUN:
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Will run AUTO_STUN\n");
- test_stun (ah);
- break;
- case AUTO_LOCAL_IP:
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Will run AUTO_LOCAL_IP\n");
- test_local_ip (ah);
- break;
- case AUTO_NAT_PUNCHED:
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Will run AUTO_NAT_PUNCHED\n");
- test_nat_punched (ah);
- break;
- case AUTO_UPNPC:
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Will run AUTO_UPNPC\n");
- test_upnpc (ah);
- break;
- case AUTO_ICMP_SERVER:
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Will run AUTO_ICMP_SERVER\n");
- test_icmp_server (ah);
- break;
- case AUTO_ICMP_CLIENT:
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Will run AUTO_ICMP_CLIENT\n");
- test_icmp_client (ah);
- break;
- case AUTO_DONE:
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Done with tests\n");
- if (!ah->internal_ip_is_public)
- {
- GNUNET_CONFIGURATION_set_value_string (ah->cfg,
- "nat",
- "BEHIND_NAT",
- "YES");
-
- if (ah->connected_back)
- {
- GNUNET_CONFIGURATION_set_value_string (ah->cfg,
- "nat",
- "PUNCHED_NAT",
- "YES");
- }
- else
- {
- GNUNET_CONFIGURATION_set_value_string (ah->cfg,
- "nat",
- "PUNCHED_NAT",
- "NO");
- }
-
- if (ah->stun_ip)
- {
- GNUNET_CONFIGURATION_set_value_string (ah->cfg,
- "nat",
- "EXTERNAL_ADDRESS",
- ah->stun_ip);
- if (ah->connected_back)
- {
- ah->type = GNUNET_NAT_TYPE_STUN_PUNCHED_NAT;
- GNUNET_CONFIGURATION_set_value_string (ah->cfg,
- "nat",
- "USE_STUN",
- "YES");
- }
- else
- {
- ah->type = GNUNET_NAT_TYPE_UNREACHABLE_NAT;
- GNUNET_CONFIGURATION_set_value_string (ah->cfg,
- "nat",
- "USE_STUN",
- "NO");
- }
-
- }
- if (0 != ah->stun_port)
- {
- GNUNET_CONFIGURATION_set_value_number (ah->cfg,
- "transport-udp",
- "ADVERTISED_PORT",
- ah->stun_port);
- }
-
- }
- else
- {
- //The internal IP is the same as public, but we didn't got a incoming connection
- if (ah->connected_back)
- {
- ah->type = GNUNET_NAT_TYPE_NO_NAT;
- GNUNET_CONFIGURATION_set_value_string (ah->cfg,
- "nat",
- "BEHIND_NAT",
- "NO");
- }
- else
- {
- GNUNET_CONFIGURATION_set_value_string (ah->cfg,
- "nat",
- "BEHIND_NAT",
- "YES");
- ah->type = GNUNET_NAT_TYPE_UNREACHABLE_NAT;
- if (ah->stun_ip)
- {
- GNUNET_CONFIGURATION_set_value_string (ah->cfg,
- "nat",
- "EXTERNAL_ADDRESS",
- ah->stun_ip);
- }
- if (0 != ah->stun_port)
- {
- GNUNET_CONFIGURATION_set_value_number (ah->cfg,
- "transport-udp",
- "ADVERTISED_PORT",
- ah->stun_port);
-
- }
- }
- }
-
- diff = GNUNET_CONFIGURATION_get_diff (ah->initial_cfg,
- ah->cfg);
-
-
- ah->fin_cb (ah->fin_cb_cls,
- diff,
- ah->ret,
- ah->type);
- GNUNET_CONFIGURATION_destroy (diff);
- GNUNET_NAT_autoconfig_cancel (ah);
- }
-}
-
-
-/**
- * Start auto-configuration routine. The resolver service should
- * be available when this function is called.
- *
- * @param cfg initial configuration
- * @param cb function to call with autoconfiguration result
- * @param cb_cls closure for @a cb
- * @return handle to cancel operation
- */
-struct GNUNET_NAT_AutoHandle *
-GNUNET_NAT_autoconfig_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
- GNUNET_NAT_AutoResultCallback cb,
- void *cb_cls)
-{
- struct GNUNET_NAT_AutoHandle *ah;
-
- ah = GNUNET_new (struct GNUNET_NAT_AutoHandle);
- ah->fin_cb = cb;
- ah->fin_cb_cls = cb_cls;
- ah->ret = GNUNET_NAT_ERROR_SUCCESS;
- ah->cfg = GNUNET_CONFIGURATION_dup (cfg);
- ah->initial_cfg = GNUNET_CONFIGURATION_dup (cfg);
-
- /* never use loopback addresses if user wanted autoconfiguration */
- GNUNET_CONFIGURATION_set_value_string (ah->cfg,
- "nat",
- "USE_LOCALADDR",
- "NO");
-
- next_phase (ah);
- return ah;
-}
-
-
-/**
- * Abort autoconfiguration.
- *
- * @param ah handle for operation to abort
- */
-void
-GNUNET_NAT_autoconfig_cancel (struct GNUNET_NAT_AutoHandle *ah)
-{
- if (NULL != ah->tst)
- {
- GNUNET_NAT_test_stop (ah->tst);
- ah->tst = NULL;
- }
- if (NULL != ah->eh)
- {
- GNUNET_NAT_mini_get_external_ipv4_cancel (ah->eh);
- ah->eh = NULL;
- }
- if (NULL != ah->mq)
- {
- GNUNET_MQ_destroy (ah->mq);
- ah->mq = NULL;
- }
- if (NULL != ah->task)
- {
- GNUNET_SCHEDULER_cancel (ah->task);
- ah->task = NULL;
- }
- GNUNET_CONFIGURATION_destroy (ah->cfg);
- GNUNET_CONFIGURATION_destroy (ah->initial_cfg);
- GNUNET_free (ah);
-}
-
-
-/* end of nat_auto.c */
+++ /dev/null
-/*
- This file is part of GNUnet.
- Copyright (C) 2011-2014 GNUnet e.V.
-
- GNUnet is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published
- by the Free Software Foundation; either version 3, or (at your
- option) any later version.
-
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with GNUnet; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA.
-*/
-
-/**
- * @file nat/nat_mini.c
- * @brief functions for interaction with miniupnp; tested with miniupnpc 1.5
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include "gnunet_util_lib.h"
-#include "gnunet_nat_lib.h"
-#include "nat.h"
-
-#define LOG(kind,...) GNUNET_log_from (kind, "nat", __VA_ARGS__)
-
-/**
- * How long do we give upnpc to create a mapping?
- */
-#define MAP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
-
-/**
- * How long do we give upnpc to remove a mapping?
- */
-#define UNMAP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
-
-/**
- * How often do we check for changes in the mapping?
- */
-#define MAP_REFRESH_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
-
-
-
-/**
- * Opaque handle to cancel "GNUNET_NAT_mini_get_external_ipv4" operation.
- */
-struct GNUNET_NAT_ExternalHandle
-{
-
- /**
- * Function to call with the result.
- */
- GNUNET_NAT_IPCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
- /**
- * Read task.
- */
- struct GNUNET_SCHEDULER_Task * task;
-
- /**
- * Handle to 'external-ip' process.
- */
- struct GNUNET_OS_Process *eip;
-
- /**
- * Handle to stdout pipe of 'external-ip'.
- */
- struct GNUNET_DISK_PipeHandle *opipe;
-
- /**
- * Read handle of @e opipe.
- */
- const struct GNUNET_DISK_FileHandle *r;
-
- /**
- * When should this operation time out?
- */
- struct GNUNET_TIME_Absolute timeout;
-
- /**
- * Number of bytes in 'buf' that are valid.
- */
- size_t off;
-
- /**
- * Destination of our read operation (output of 'external-ip').
- */
- char buf[17];
-
- /**
- * Error code for better debugging and user feedback
- */
- enum GNUNET_NAT_StatusCode ret;
-};
-
-
-/**
- * Read the output of 'external-ip' into buf. When complete, parse the
- * address and call our callback.
- *
- * @param cls the `struct GNUNET_NAT_ExternalHandle`
- */
-static void
-read_external_ipv4 (void *cls)
-{
- struct GNUNET_NAT_ExternalHandle *eh = cls;
- ssize_t ret;
- struct in_addr addr;
- const struct GNUNET_SCHEDULER_TaskContext *tc;
-
- eh->task = NULL;
- tc = GNUNET_SCHEDULER_get_task_context ();
- if (GNUNET_YES ==
- GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, eh->r))
- {
- ret =
- GNUNET_DISK_file_read (eh->r, &eh->buf[eh->off],
- sizeof (eh->buf) - eh->off);
- }
- else
- {
- eh->ret = GNUNET_NAT_ERROR_IPC_FAILURE;
- ret = -1; /* error reading, timeout, etc. */
- }
- if (ret > 0)
- {
- /* try to read more */
- eh->off += ret;
- eh->task =
- GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
- (eh->timeout), eh->r,
- &read_external_ipv4, eh);
- return;
- }
- eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID;
- if ((eh->off > 7) && (eh->buf[eh->off - 1] == '\n'))
- {
- eh->buf[eh->off - 1] = '\0';
- if (1 == inet_pton (AF_INET, eh->buf, &addr))
- {
- if (0 != addr.s_addr)
- eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID; /* got 0.0.0.0 */
- else
- eh->ret = GNUNET_NAT_ERROR_SUCCESS;
- }
- }
- eh->cb (eh->cb_cls,
- (GNUNET_NAT_ERROR_SUCCESS == eh->ret) ? &addr : NULL,
- eh->ret);
- GNUNET_NAT_mini_get_external_ipv4_cancel (eh);
-}
-
-
-/**
- * (Asynchronously) signal error invoking "external-ip" to client.
- *
- * @param cls the `struct GNUNET_NAT_ExternalHandle` (freed)
- */
-static void
-signal_external_ip_error (void *cls)
-{
- struct GNUNET_NAT_ExternalHandle *eh = cls;
-
- eh->task = NULL;
- eh->cb (eh->cb_cls,
- NULL,
- eh->ret);
- GNUNET_free (eh);
-}
-
-
-/**
- * Try to get the external IPv4 address of this peer.
- *
- * @param timeout when to fail
- * @param cb function to call with result
- * @param cb_cls closure for @a cb
- * @return handle for cancellation (can only be used until @a cb is called), never NULL
- */
-struct GNUNET_NAT_ExternalHandle *
-GNUNET_NAT_mini_get_external_ipv4 (struct GNUNET_TIME_Relative timeout,
- GNUNET_NAT_IPCallback cb, void *cb_cls)
-{
- struct GNUNET_NAT_ExternalHandle *eh;
-
- eh = GNUNET_new (struct GNUNET_NAT_ExternalHandle);
- eh->cb = cb;
- eh->cb_cls = cb_cls;
- eh->ret = GNUNET_NAT_ERROR_SUCCESS;
- if (GNUNET_SYSERR ==
- GNUNET_OS_check_helper_binary ("external-ip", GNUNET_NO, NULL))
- {
- LOG (GNUNET_ERROR_TYPE_INFO,
- _("`external-ip' command not found\n"));
- eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND;
- eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error,
- eh);
- return eh;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Running `external-ip' to determine our external IP\n");
- eh->opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
- if (NULL == eh->opipe)
- {
- eh->ret = GNUNET_NAT_ERROR_IPC_FAILURE;
- eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error,
- eh);
- return eh;
- }
- eh->eip =
- GNUNET_OS_start_process (GNUNET_NO, 0, NULL, eh->opipe, NULL,
- "external-ip", "external-ip",
- NULL);
- if (NULL == eh->eip)
- {
- GNUNET_DISK_pipe_close (eh->opipe);
- eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED;
- eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error,
- eh);
- return eh;
- }
- GNUNET_DISK_pipe_close_end (eh->opipe, GNUNET_DISK_PIPE_END_WRITE);
- eh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
- eh->r = GNUNET_DISK_pipe_handle (eh->opipe, GNUNET_DISK_PIPE_END_READ);
- eh->task =
- GNUNET_SCHEDULER_add_read_file (timeout,
- eh->r,
- &read_external_ipv4, eh);
- return eh;
-}
-
-
-/**
- * Cancel operation.
- *
- * @param eh operation to cancel
- */
-void
-GNUNET_NAT_mini_get_external_ipv4_cancel (struct GNUNET_NAT_ExternalHandle *eh)
-{
- if (NULL != eh->eip)
- {
- (void) GNUNET_OS_process_kill (eh->eip, SIGKILL);
- GNUNET_OS_process_destroy (eh->eip);
- }
- if (NULL != eh->opipe)
- GNUNET_DISK_pipe_close (eh->opipe);
- if (NULL != eh->task)
- GNUNET_SCHEDULER_cancel (eh->task);
- GNUNET_free (eh);
-}
-
-
-/**
- * Handle to a mapping created with upnpc.
- */
-struct GNUNET_NAT_MiniHandle
-{
-
- /**
- * Function to call on mapping changes.
- */
- GNUNET_NAT_MiniAddressCallback ac;
-
- /**
- * Closure for @e ac.
- */
- void *ac_cls;
-
- /**
- * Command used to install the map.
- */
- struct GNUNET_OS_CommandHandle *map_cmd;
-
- /**
- * Command used to refresh our map information.
- */
- struct GNUNET_OS_CommandHandle *refresh_cmd;
-
- /**
- * Command used to remove the mapping.
- */
- struct GNUNET_OS_CommandHandle *unmap_cmd;
-
- /**
- * Our current external mapping (if we have one).
- */
- struct sockaddr_in current_addr;
-
- /**
- * We check the mapping periodically to see if it
- * still works. This task triggers the check.
- */
- struct GNUNET_SCHEDULER_Task * refresh_task;
-
- /**
- * Are we mapping TCP or UDP?
- */
- int is_tcp;
-
- /**
- * Did we succeed with creating a mapping?
- */
- int did_map;
-
- /**
- * Did we find our mapping during refresh scan?
- */
- int found;
-
- /**
- * Which port are we mapping?
- */
- uint16_t port;
-
-};
-
-
-/**
- * Run "upnpc -l" to find out if our mapping changed.
- *
- * @param cls the `struct GNUNET_NAT_MiniHandle`
- */
-static void
-do_refresh (void *cls);
-
-
-/**
- * Process the output from the "upnpc -r" command.
- *
- * @param cls the `struct GNUNET_NAT_MiniHandle`
- * @param line line of output, NULL at the end
- */
-static void
-process_map_output (void *cls, const char *line);
-
-
-/**
- * Run "upnpc -r" to map our internal port.
- *
- * @param mini our handle
- */
-static void
-run_upnpc_r (struct GNUNET_NAT_MiniHandle *mini)
-{
- char pstr[6];
-
- GNUNET_snprintf (pstr,
- sizeof (pstr),
- "%u",
- (unsigned int) mini->port);
- mini->map_cmd =
- GNUNET_OS_command_run (&process_map_output, mini, MAP_TIMEOUT,
- "upnpc", "upnpc", "-r", pstr,
- mini->is_tcp ? "tcp" : "udp", NULL);
- if (NULL == mini->map_cmd)
- {
- mini->ac (mini->ac_cls,
- GNUNET_SYSERR,
- NULL, 0,
- GNUNET_NAT_ERROR_UPNPC_FAILED);
- return;
- }
-}
-
-
-/**
- * Process the output from "upnpc -l" to see if our
- * external mapping changed. If so, do the notifications.
- *
- * @param cls the `struct GNUNET_NAT_MiniHandle`
- * @param line line of output, NULL at the end
- */
-static void
-process_refresh_output (void *cls, const char *line)
-{
- struct GNUNET_NAT_MiniHandle *mini = cls;
- char pstr[9];
- const char *s;
- unsigned int nport;
- struct in_addr exip;
-
- if (NULL == line)
- {
- GNUNET_OS_command_stop (mini->refresh_cmd);
- mini->refresh_cmd = NULL;
- if (GNUNET_NO == mini->found)
- {
- /* mapping disappeared, try to re-create */
- if (GNUNET_YES == mini->did_map)
- {
- mini->ac (mini->ac_cls,
- GNUNET_NO,
- (const struct sockaddr *) &mini->current_addr,
- sizeof (mini->current_addr),
- GNUNET_NAT_ERROR_SUCCESS);
- mini->did_map = GNUNET_NO;
- }
- run_upnpc_r (mini);
- }
- return;
- }
- if (!mini->did_map)
- return; /* never mapped, won't find our mapping anyway */
-
- /* we're looking for output of the form:
- * "ExternalIPAddress = 12.134.41.124" */
-
- s = strstr (line, "ExternalIPAddress = ");
- if (NULL != s)
- {
- s += strlen ("ExternalIPAddress = ");
- if (1 != inet_pton (AF_INET, s, &exip))
- return; /* skip */
- if (exip.s_addr == mini->current_addr.sin_addr.s_addr)
- return; /* no change */
- /* update mapping */
- mini->ac (mini->ac_cls, GNUNET_NO,
- (const struct sockaddr *) &mini->current_addr,
- sizeof (mini->current_addr),
- GNUNET_NAT_ERROR_SUCCESS);
- mini->current_addr.sin_addr = exip;
- mini->ac (mini->ac_cls, GNUNET_YES,
- (const struct sockaddr *) &mini->current_addr,
- sizeof (mini->current_addr),
- GNUNET_NAT_ERROR_SUCCESS);
- return;
- }
- /*
- * we're looking for output of the form:
- *
- * "0 TCP 3000->192.168.2.150:3000 'libminiupnpc' ''"
- * "1 UDP 3001->192.168.2.150:3001 'libminiupnpc' ''"
- *
- * the pattern we look for is:
- *
- * "%s TCP PORT->STRING:OURPORT *" or
- * "%s UDP PORT->STRING:OURPORT *"
- */
- GNUNET_snprintf (pstr, sizeof (pstr), ":%u ", mini->port);
- if (NULL == (s = strstr (line, "->")))
- return; /* skip */
- if (NULL == strstr (s, pstr))
- return; /* skip */
- if (1 !=
- SSCANF (line,
- (mini->is_tcp) ? "%*u TCP %u->%*s:%*u %*s" :
- "%*u UDP %u->%*s:%*u %*s", &nport))
- return; /* skip */
- mini->found = GNUNET_YES;
- if (nport == ntohs (mini->current_addr.sin_port))
- return; /* no change */
-
- /* external port changed, update mapping */
- mini->ac (mini->ac_cls, GNUNET_NO,
- (const struct sockaddr *) &mini->current_addr,
- sizeof (mini->current_addr),
- GNUNET_NAT_ERROR_SUCCESS);
- mini->current_addr.sin_port = htons ((uint16_t) nport);
- mini->ac (mini->ac_cls, GNUNET_YES,
- (const struct sockaddr *) &mini->current_addr,
- sizeof (mini->current_addr),
- GNUNET_NAT_ERROR_SUCCESS);
-}
-
-
-/**
- * Run "upnpc -l" to find out if our mapping changed.
- *
- * @param cls the 'struct GNUNET_NAT_MiniHandle'
- */
-static void
-do_refresh (void *cls)
-{
- struct GNUNET_NAT_MiniHandle *mini = cls;
- int ac;
-
- mini->refresh_task =
- GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ,
- &do_refresh, mini);
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Running `upnpc' to check if our mapping still exists\n");
- mini->found = GNUNET_NO;
- ac = GNUNET_NO;
- if (NULL != mini->map_cmd)
- {
- /* took way too long, abort it! */
- GNUNET_OS_command_stop (mini->map_cmd);
- mini->map_cmd = NULL;
- ac = GNUNET_YES;
- }
- if (NULL != mini->refresh_cmd)
- {
- /* took way too long, abort it! */
- GNUNET_OS_command_stop (mini->refresh_cmd);
- mini->refresh_cmd = NULL;
- ac = GNUNET_YES;
- }
- mini->refresh_cmd =
- GNUNET_OS_command_run (&process_refresh_output, mini, MAP_TIMEOUT,
- "upnpc", "upnpc", "-l", NULL);
- if (GNUNET_YES == ac)
- mini->ac (mini->ac_cls,
- GNUNET_SYSERR,
- NULL, 0,
- GNUNET_NAT_ERROR_UPNPC_TIMEOUT);
-}
-
-
-/**
- * Process the output from the 'upnpc -r' command.
- *
- * @param cls the `struct GNUNET_NAT_MiniHandle`
- * @param line line of output, NULL at the end
- */
-static void
-process_map_output (void *cls,
- const char *line)
-{
- struct GNUNET_NAT_MiniHandle *mini = cls;
- const char *ipaddr;
- char *ipa;
- const char *pstr;
- unsigned int port;
-
- if (NULL == line)
- {
- GNUNET_OS_command_stop (mini->map_cmd);
- mini->map_cmd = NULL;
- if (GNUNET_YES != mini->did_map)
- mini->ac (mini->ac_cls,
- GNUNET_SYSERR,
- NULL, 0,
- GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED);
- if (NULL == mini->refresh_task)
- mini->refresh_task =
- GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ,
- &do_refresh,
- mini);
- return;
- }
- /*
- * The upnpc output we're after looks like this:
- *
- * "external 87.123.42.204:3000 TCP is redirected to internal 192.168.2.150:3000"
- */
- if ((NULL == (ipaddr = strstr (line, " "))) ||
- (NULL == (pstr = strstr (ipaddr, ":"))) ||
- (1 != SSCANF (pstr + 1, "%u", &port)))
- {
- return; /* skip line */
- }
- ipa = GNUNET_strdup (ipaddr + 1);
- strstr (ipa, ":")[0] = '\0';
- if (1 != inet_pton (AF_INET, ipa, &mini->current_addr.sin_addr))
- {
- GNUNET_free (ipa);
- return; /* skip line */
- }
- GNUNET_free (ipa);
-
- mini->current_addr.sin_port = htons (port);
- mini->current_addr.sin_family = AF_INET;
-#if HAVE_SOCKADDR_IN_SIN_LEN
- mini->current_addr.sin_len = sizeof (struct sockaddr_in);
-#endif
- mini->did_map = GNUNET_YES;
- mini->ac (mini->ac_cls, GNUNET_YES,
- (const struct sockaddr *) &mini->current_addr,
- sizeof (mini->current_addr),
- GNUNET_NAT_ERROR_SUCCESS);
-}
-
-
-/**
- * Start mapping the given port using (mini)upnpc. This function
- * should typically not be used directly (it is used within the
- * general-purpose #GNUNET_NAT_register() code). However, it can be
- * used if specifically UPnP-based NAT traversal is to be used or
- * tested.
- *
- * @param port port to map
- * @param is_tcp #GNUNET_YES to map TCP, #GNUNET_NO for UDP
- * @param ac function to call with mapping result
- * @param ac_cls closure for @a ac
- * @return NULL on error (no 'upnpc' installed)
- */
-struct GNUNET_NAT_MiniHandle *
-GNUNET_NAT_mini_map_start (uint16_t port,
- int is_tcp,
- GNUNET_NAT_MiniAddressCallback ac,
- void *ac_cls)
-{
- struct GNUNET_NAT_MiniHandle *ret;
-
- if (GNUNET_SYSERR ==
- GNUNET_OS_check_helper_binary ("upnpc", GNUNET_NO, NULL))
- {
- LOG (GNUNET_ERROR_TYPE_INFO,
- _("`upnpc' command not found\n"));
- ac (ac_cls,
- GNUNET_SYSERR,
- NULL, 0,
- GNUNET_NAT_ERROR_UPNPC_NOT_FOUND);
- return NULL;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Running `upnpc' to install mapping\n");
- ret = GNUNET_new (struct GNUNET_NAT_MiniHandle);
- ret->ac = ac;
- ret->ac_cls = ac_cls;
- ret->is_tcp = is_tcp;
- ret->port = port;
- ret->refresh_task =
- GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ,
- &do_refresh,
- ret);
- run_upnpc_r (ret);
- return ret;
-}
-
-
-/**
- * Process output from our 'unmap' command.
- *
- * @param cls the `struct GNUNET_NAT_MiniHandle`
- * @param line line of output, NULL at the end
- */
-static void
-process_unmap_output (void *cls, const char *line)
-{
- struct GNUNET_NAT_MiniHandle *mini = cls;
-
- if (NULL == line)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "UPnP unmap done\n");
- GNUNET_OS_command_stop (mini->unmap_cmd);
- mini->unmap_cmd = NULL;
- GNUNET_free (mini);
- return;
- }
- /* we don't really care about the output... */
-}
-
-
-/**
- * Remove a mapping created with (mini)upnpc. Calling
- * this function will give 'upnpc' 1s to remove tha mapping,
- * so while this function is non-blocking, a task will be
- * left with the scheduler for up to 1s past this call.
- *
- * @param mini the handle
- */
-void
-GNUNET_NAT_mini_map_stop (struct GNUNET_NAT_MiniHandle *mini)
-{
- char pstr[6];
-
- if (NULL != mini->refresh_task)
- {
- GNUNET_SCHEDULER_cancel (mini->refresh_task);
- mini->refresh_task = NULL;
- }
- if (NULL != mini->refresh_cmd)
- {
- GNUNET_OS_command_stop (mini->refresh_cmd);
- mini->refresh_cmd = NULL;
- }
- if (NULL != mini->map_cmd)
- {
- GNUNET_OS_command_stop (mini->map_cmd);
- mini->map_cmd = NULL;
- }
- if (GNUNET_NO == mini->did_map)
- {
- GNUNET_free (mini);
- return;
- }
- mini->ac (mini->ac_cls, GNUNET_NO,
- (const struct sockaddr *) &mini->current_addr,
- sizeof (mini->current_addr),
- GNUNET_NAT_ERROR_SUCCESS);
- /* Note: oddly enough, deletion uses the external port whereas
- * addition uses the internal port; this rarely matters since they
- * often are the same, but it might... */
- GNUNET_snprintf (pstr,
- sizeof (pstr),
- "%u",
- (unsigned int) ntohs (mini->current_addr.sin_port));
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Unmapping port %u with UPnP\n",
- ntohs (mini->current_addr.sin_port));
- mini->unmap_cmd =
- GNUNET_OS_command_run (&process_unmap_output, mini, UNMAP_TIMEOUT,
- "upnpc", "upnpc", "-d", pstr,
- mini->is_tcp ? "tcp" : "udp", NULL);
-}
-
-
-/* end of nat_mini.c */
+++ /dev/null
-/*
- This file is part of GNUnet.
- Copyright (C) 2009, 2015 GNUnet e.V.
-
- GNUnet is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published
- by the Free Software Foundation; either version 3, or (at your
- option) any later version.
-
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with GNUnet; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA.
-*/
-/**
- * This code provides some support for doing STUN transactions.
- * We send simplest possible packet ia REQUEST with BIND to a STUN server.
- *
- * All STUN packets start with a simple header made of a type,
- * length (excluding the header) and a 16-byte random transaction id.
- * Following the header we may have zero or more attributes, each
- * structured as a type, length and a value (whose format depends
- * on the type, but often contains addresses).
- * Of course all fields are in network format.
- *
- * This code was based on ministun.c.
- *
- * @file nat/nat_stun.c
- * @brief Functions for STUN functionality
- * @author Bruno Souza Cabral
- */
-
-#include "platform.h"
-#include "gnunet_util_lib.h"
-#include "gnunet_resolver_service.h"
-#include "gnunet_nat_lib.h"
-
-
-#include "nat_stun.h"
-
-#define LOG(kind,...) GNUNET_log_from (kind, "stun", __VA_ARGS__)
-
-#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
-
-
-/**
- * Handle to a request given to the resolver. Can be used to cancel
- * the request prior to the timeout or successful execution. Also
- * used to track our internal state for the request.
- */
-struct GNUNET_NAT_STUN_Handle
-{
-
- /**
- * Handle to a pending DNS lookup request.
- */
- struct GNUNET_RESOLVER_RequestHandle *dns_active;
-
- /**
- * Handle to the listen socket
- */
- struct GNUNET_NETWORK_Handle *sock;
-
- /**
- * Stun server address
- */
- char *stun_server;
-
- /**
- * Function to call when a error occours
- */
- GNUNET_NAT_STUN_ErrorCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
- /**
- * Do we got a DNS resolution successfully?
- */
- int dns_success;
-
- /**
- * STUN port
- */
- uint16_t stun_port;
-
-};
-
-
-/**
- * here we store credentials extracted from a message
-*/
-struct StunState
-{
- uint16_t attr;
-};
-
-
-/**
- * Encode a class and method to a compatible STUN format
- *
- * @param msg_class class to be converted
- * @param method method to be converted
- * @return message in a STUN compatible format
- */
-static int
-encode_message (enum StunClasses msg_class,
- enum StunMethods method)
-{
- return ((msg_class & 1) << 4) | ((msg_class & 2) << 7) |
- (method & 0x000f) | ((method & 0x0070) << 1) | ((method & 0x0f800) << 2);
-}
-
-
-/**
- * Fill the stun_header with a random request_id
- *
- * @param req, stun header to be filled
- */
-static void
-generate_request_id (struct stun_header *req)
-{
- unsigned int x;
-
- req->magic = htonl(STUN_MAGIC_COOKIE);
- for (x = 0; x < 3; x++)
- req->id.id[x] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
- UINT32_MAX);
-}
-
-
-/**
- * Extract the STUN_MAPPED_ADDRESS from the stun response.
- * This is used as a callback for stun_handle_response
- * when called from stun_request.
- *
- * @param st, pointer where we will set the type
- * @param attr , received stun attribute
- * @param arg , pointer to a sockaddr_in where we will set the reported IP and port
- * @param magic , Magic cookie
- *
- * @return 0 on success, other value otherwise
- */
-static int
-stun_get_mapped (struct StunState *st,
- struct stun_attr *attr,
- struct sockaddr_in *arg,
- unsigned int magic)
-{
- struct stun_addr *returned_addr = (struct stun_addr *)(attr + 1);
- struct sockaddr_in *sa = (struct sockaddr_in *)arg;
- unsigned short type = ntohs(attr->attr);
-
- switch (type)
- {
- case STUN_MAPPED_ADDRESS:
- if (st->attr == STUN_XOR_MAPPED_ADDRESS ||
- st->attr == STUN_MS_XOR_MAPPED_ADDRESS)
- return 1;
- magic = 0;
- break;
- case STUN_MS_XOR_MAPPED_ADDRESS:
- if (st->attr == STUN_XOR_MAPPED_ADDRESS)
- return 1;
- break;
- case STUN_XOR_MAPPED_ADDRESS:
- break;
- default:
- return 1;
- }
- if ( (ntohs(attr->len) < 8) &&
- (returned_addr->family != 1) )
- {
- return 1;
- }
- st->attr = type;
- sa->sin_family = AF_INET;
- sa->sin_port = returned_addr->port ^ htons(ntohl(magic) >> 16);
- sa->sin_addr.s_addr = returned_addr->addr ^ magic;
- return 0;
-}
-
-
-/**
- * Handle an incoming STUN message, Do some basic sanity checks on packet size and content,
- * try to extract a bit of information, and possibly reply.
- * At the moment this only processes BIND requests, and returns
- * the externally visible address of the request.
- * If a callback is specified, invoke it with the attribute.
- *
- * @param data the packet
- * @param len the length of the packet in @a data
- * @param[out] arg sockaddr_in where we will set our discovered address
- *
- * @return, #GNUNET_OK on OK, #GNUNET_NO if the packet is invalid (not a stun packet)
- */
-int
-GNUNET_NAT_stun_handle_packet (const void *data,
- size_t len,
- struct sockaddr_in *arg)
-{
- const struct stun_header *hdr = (const struct stun_header *)data;
- struct stun_attr *attr;
- struct StunState st;
- int ret = GNUNET_OK;
- uint32_t advertised_message_size;
- uint32_t message_magic_cookie;
-
- /* On entry, 'len' is the length of the udp payload. After the
- * initial checks it becomes the size of unprocessed options,
- * while 'data' is advanced accordingly.
- */
- if (len < sizeof(struct stun_header))
- {
- LOG (GNUNET_ERROR_TYPE_INFO,
- "STUN packet too short (only %d, wanting at least %d)\n",
- (int) len,
- (int) sizeof(struct stun_header));
- GNUNET_break_op (0);
- return GNUNET_NO;
- }
- /* Skip header as it is already in hdr */
- len -= sizeof(struct stun_header);
- data += sizeof(struct stun_header);
-
- /* len as advertised in the message */
- advertised_message_size = ntohs(hdr->msglen);
-
- message_magic_cookie = ntohl(hdr->magic);
- /* Compare if the cookie match */
- if (STUN_MAGIC_COOKIE != message_magic_cookie)
- {
- LOG (GNUNET_ERROR_TYPE_INFO,
- "Invalid magic cookie \n");
- return GNUNET_NO;
- }
-
- LOG (GNUNET_ERROR_TYPE_INFO,
- "STUN Packet, msg %s (%04x), length: %d\n",
- stun_msg2str(ntohs(hdr->msgtype)),
- ntohs(hdr->msgtype),
- advertised_message_size);
- if (advertised_message_size > len)
- {
- LOG (GNUNET_ERROR_TYPE_INFO,
- "Scrambled STUN packet length (got %d, expecting %d)\n",
- advertised_message_size,
- (int)len);
- return GNUNET_NO;
- }
- len = advertised_message_size;
- memset (&st, 0, sizeof(st));
-
- while (len > 0)
- {
- if (len < sizeof(struct stun_attr))
- {
- LOG (GNUNET_ERROR_TYPE_INFO,
- "Attribute too short (got %d, expecting %d)\n",
- (int)len,
- (int) sizeof(struct stun_attr));
- break;
- }
- attr = (struct stun_attr *)data;
-
- /* compute total attribute length */
- advertised_message_size = ntohs(attr->len) + sizeof(struct stun_attr);
-
- /* Check if we still have space in our buffer */
- if (advertised_message_size > len )
- {
- LOG (GNUNET_ERROR_TYPE_INFO,
- "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n",
- advertised_message_size,
- (int)len);
- break;
- }
- stun_get_mapped (&st,
- attr,
- arg,
- hdr->magic);
- /* Clear attribute id: in case previous entry was a string,
- * this will act as the terminator for the string.
- */
- attr->attr = 0;
- data += advertised_message_size;
- len -= advertised_message_size;
- ret = GNUNET_OK;
- }
- return ret;
-}
-
-
-/**
- * Cancel active STUN request. Frees associated resources
- * and ensures that the callback is no longer invoked.
- *
- * @param rh request to cancel
- */
-void
-GNUNET_NAT_stun_make_request_cancel (struct GNUNET_NAT_STUN_Handle *rh)
-{
- if (NULL != rh->dns_active)
- {
- GNUNET_RESOLVER_request_cancel (rh->dns_active);
- rh->dns_active = NULL;
- }
- GNUNET_free (rh->stun_server);
- GNUNET_free (rh);
-}
-
-
-/**
- * Try to establish a connection given the specified address.
- *
- * @param cls our `struct GNUNET_NAT_STUN_Handle *`
- * @param addr address to try, NULL for "last call"
- * @param addrlen length of @a addr
- */
-static void
-stun_dns_callback (void *cls,
- const struct sockaddr *addr,
- socklen_t addrlen)
-{
- struct GNUNET_NAT_STUN_Handle *rh = cls;
- struct stun_header *req;
- uint8_t reqdata[1024];
- int reqlen;
- struct sockaddr_in server;
-
- if (NULL == addr)
- {
- rh->dns_active = NULL;
- if (GNUNET_NO == rh->dns_success)
- {
- LOG (GNUNET_ERROR_TYPE_INFO,
- "Error resolving host %s\n",
- rh->stun_server);
- rh->cb (rh->cb_cls,
- GNUNET_NAT_ERROR_NOT_ONLINE);
- }
- else if (GNUNET_SYSERR == rh->dns_success)
- {
- rh->cb (rh->cb_cls,
- GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR);
- }
- else
- {
- rh->cb (rh->cb_cls,
- GNUNET_NAT_ERROR_SUCCESS);
- }
- GNUNET_NAT_stun_make_request_cancel (rh);
- return;
- }
-
- rh->dns_success = GNUNET_YES;
- memset (&server,0, sizeof(server));
- server.sin_family = AF_INET;
- server.sin_addr = ((struct sockaddr_in *)addr)->sin_addr;
- server.sin_port = htons(rh->stun_port);
-#if HAVE_SOCKADDR_IN_SIN_LEN
- server.sin_len = (u_char) sizeof (struct sockaddr_in);
-#endif
-
- /*Craft the simplest possible STUN packet. A request binding*/
- req = (struct stun_header *)reqdata;
- generate_request_id (req);
- reqlen = 0;
- req->msgtype = 0;
- req->msglen = 0;
- req->msglen = htons (reqlen);
- req->msgtype = htons (encode_message (STUN_REQUEST,
- STUN_BINDING));
-
- /* Send the packet */
- if (-1 ==
- GNUNET_NETWORK_socket_sendto (rh->sock,
- req,
- ntohs(req->msglen) + sizeof(*req),
- (const struct sockaddr *) &server,
- sizeof (server)))
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
- "sendto");
- rh->dns_success = GNUNET_SYSERR;
- return;
- }
-}
-
-
-/**
- * Make Generic STUN request. Sends a generic stun request to the
- * server specified using the specified socket, possibly waiting for
- * a reply and filling the 'reply' field with the externally visible
- * address.
- *
- * @param server the address of the stun server
- * @param port port of the stun server
- * @param sock the socket used to send the request
- * @param cb callback in case of error
- * @param cb_cls closure for @a cb
- * @return NULL on error
- */
-struct GNUNET_NAT_STUN_Handle *
-GNUNET_NAT_stun_make_request (const char *server,
- uint16_t port,
- struct GNUNET_NETWORK_Handle *sock,
- GNUNET_NAT_STUN_ErrorCallback cb,
- void *cb_cls)
-{
- struct GNUNET_NAT_STUN_Handle *rh;
-
- rh = GNUNET_new (struct GNUNET_NAT_STUN_Handle);
- rh->sock = sock;
- rh->cb = cb;
- rh->cb_cls = cb_cls;
- rh->stun_server = GNUNET_strdup (server);
- rh->stun_port = port;
- rh->dns_success = GNUNET_NO;
- rh->dns_active = GNUNET_RESOLVER_ip_get (rh->stun_server,
- AF_INET,
- TIMEOUT,
- &stun_dns_callback, rh);
- if (NULL == rh->dns_active)
- {
- GNUNET_NAT_stun_make_request_cancel (rh);
- return NULL;
- }
- return rh;
-}
-
-/* end of nat_stun.c */
+++ /dev/null
-/*
- This file is part of GNUnet.
- Copyright (C) 2011, 2016 GNUnet e.V.
-
- GNUnet is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published
- by the Free Software Foundation; either version 3, or (at your
- option) any later version.
-
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with GNUnet; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA.
-*/
-
-/**
- * @file nat/nat_test.c
- * @brief functions to test if the NAT configuration is successful at achieving NAT traversal (with the help of a gnunet-nat-server)
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include "gnunet_util_lib.h"
-#include "gnunet_nat_lib.h"
-#include "nat.h"
-
-#define LOG(kind,...) GNUNET_log_from (kind, "nat", __VA_ARGS__)
-
-#define NAT_SERVER_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
-
-/**
- * Entry we keep for each incoming connection.
- */
-struct NatActivity
-{
- /**
- * This is a doubly-linked list.
- */
- struct NatActivity *next;
-
- /**
- * This is a doubly-linked list.
- */
- struct NatActivity *prev;
-
- /**
- * Socket of the incoming connection.
- */
- struct GNUNET_NETWORK_Handle *sock;
-
- /**
- * Handle of the master context.
- */
- struct GNUNET_NAT_Test *h;
-
- /**
- * Task reading from the incoming connection.
- */
- struct GNUNET_SCHEDULER_Task *rtask;
-};
-
-
-/**
- * Entry we keep for each connection to the gnunet-nat-service.
- */
-struct ClientActivity
-{
- /**
- * This is a doubly-linked list.
- */
- struct ClientActivity *next;
-
- /**
- * This is a doubly-linked list.
- */
- struct ClientActivity *prev;
-
- /**
- * Socket of the incoming connection.
- */
- struct GNUNET_MQ_Handle *mq;
-
- /**
- * Handle to overall NAT test.
- */
- struct GNUNET_NAT_Test *h;
-
-};
-
-
-/**
- * Handle to a NAT test.
- */
-struct GNUNET_NAT_Test
-{
-
- /**
- * Configuration used
- */
- const struct GNUNET_CONFIGURATION_Handle *cfg;
-
- /**
- * Function to call with success report
- */
- GNUNET_NAT_TestCallback report;
-
- /**
- * Closure for @e report.
- */
- void *report_cls;
-
- /**
- * Handle to NAT traversal in use
- */
- struct GNUNET_NAT_Handle *nat;
-
- /**
- * Handle to listen socket, or NULL
- */
- struct GNUNET_NETWORK_Handle *lsock;
-
- /**
- * Head of list of nat activities.
- */
- struct NatActivity *na_head;
-
- /**
- * Tail of list of nat activities.
- */
- struct NatActivity *na_tail;
-
- /**
- * Head of list of client activities.
- */
- struct ClientActivity *ca_head;
-
- /**
- * Tail of list of client activities.
- */
- struct ClientActivity *ca_tail;
-
- /**
- * Identity of task for the listen socket (if any)
- */
- struct GNUNET_SCHEDULER_Task *ltask;
-
- /**
- * Task identifier for the timeout (if any)
- */
- struct GNUNET_SCHEDULER_Task *ttask;
-
- /**
- * #GNUNET_YES if we're testing TCP
- */
- int is_tcp;
-
- /**
- * Data that should be transmitted or source-port.
- */
- uint16_t data;
-
- /**
- * Advertised port to the other peer.
- */
- uint16_t adv_port;
-
- /**
- * Status code to be reported to the timeout/status call
- */
- enum GNUNET_NAT_StatusCode status;
-};
-
-
-/**
- * Function called from #GNUNET_NAT_register whenever someone asks us
- * to do connection reversal.
- *
- * @param cls closure, our `struct GNUNET_NAT_Handle`
- * @param addr public IP address of the other peer
- * @param addrlen actual lenght of the @a addr
- */
-static void
-reversal_cb (void *cls,
- const struct sockaddr *addr,
- socklen_t addrlen)
-{
- struct GNUNET_NAT_Test *h = cls;
- const struct sockaddr_in *sa;
-
- if (sizeof (struct sockaddr_in) != addrlen)
- return;
- sa = (const struct sockaddr_in *) addr;
- if (h->data != sa->sin_port)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received connection reversal request for wrong port\n");
- return; /* wrong port */
- }
- /* report success */
- h->report (h->report_cls,
- GNUNET_NAT_ERROR_SUCCESS);
-}
-
-
-/**
- * Activity on our incoming socket. Read data from the
- * incoming connection.
- *
- * @param cls the `struct GNUNET_NAT_Test`
- */
-static void
-do_udp_read (void *cls)
-{
- struct GNUNET_NAT_Test *tst = cls;
- uint16_t data;
- const struct GNUNET_SCHEDULER_TaskContext *tc;
-
- tc = GNUNET_SCHEDULER_get_task_context ();
- tst->ltask =
- GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- tst->lsock,
- &do_udp_read,
- tst);
- if ((NULL != tc->write_ready) &&
- (GNUNET_NETWORK_fdset_isset (tc->read_ready,
- tst->lsock)) &&
- (sizeof (data) ==
- GNUNET_NETWORK_socket_recv (tst->lsock,
- &data,
- sizeof (data))))
- {
- if (data == tst->data)
- tst->report (tst->report_cls,
- GNUNET_NAT_ERROR_SUCCESS);
- else
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received data mismatches expected value\n");
- }
- else
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Failed to receive data from inbound connection\n");
-}
-
-
-/**
- * Activity on our incoming socket. Read data from the
- * incoming connection.
- *
- * @param cls the `struct NatActivity`
- */
-static void
-do_read (void *cls)
-{
- struct NatActivity *na = cls;
- struct GNUNET_NAT_Test *tst;
- uint16_t data;
- const struct GNUNET_SCHEDULER_TaskContext *tc;
-
- tc = GNUNET_SCHEDULER_get_task_context ();
- na->rtask = NULL;
- tst = na->h;
- GNUNET_CONTAINER_DLL_remove (tst->na_head,
- tst->na_tail,
- na);
- if ((NULL != tc->write_ready) &&
- (GNUNET_NETWORK_fdset_isset (tc->read_ready,
- na->sock)) &&
- (sizeof (data) ==
- GNUNET_NETWORK_socket_recv (na->sock,
- &data,
- sizeof (data))))
- {
- if (data == tst->data)
- tst->report (tst->report_cls,
- GNUNET_NAT_ERROR_SUCCESS);
- else
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received data does not match expected value\n");
- }
- else
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Failed to receive data from inbound connection\n");
- GNUNET_NETWORK_socket_close (na->sock);
- GNUNET_free (na);
-}
-
-
-/**
- * Activity on our listen socket. Accept the
- * incoming connection.
- *
- * @param cls the `struct GNUNET_NAT_Test`
- */
-static void
-do_accept (void *cls)
-{
- struct GNUNET_NAT_Test *tst = cls;
- struct GNUNET_NETWORK_Handle *s;
- struct NatActivity *wl;
-
- tst->ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- tst->lsock,
- &do_accept,
- tst);
- s = GNUNET_NETWORK_socket_accept (tst->lsock,
- NULL,
- NULL);
- if (NULL == s)
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO,
- "accept");
- return; /* odd error */
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Got an inbound connection, waiting for data\n");
- wl = GNUNET_new (struct NatActivity);
- wl->sock = s;
- wl->h = tst;
- wl->rtask =
- GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- wl->sock,
- &do_read,
- wl);
- GNUNET_CONTAINER_DLL_insert (tst->na_head,
- tst->na_tail,
- wl);
-}
-
-
-/**
- * We got disconnected from the NAT server. Stop
- * waiting for a reply.
- *
- * @param cls the `struct ClientActivity`
- * @param error error code
- */
-static void
-mq_error_handler (void *cls,
- enum GNUNET_MQ_Error error)
-{
- struct ClientActivity *ca = cls;
- struct GNUNET_NAT_Test *tst = ca->h;
-
- GNUNET_CONTAINER_DLL_remove (tst->ca_head,
- tst->ca_tail,
- ca);
- GNUNET_MQ_destroy (ca->mq);
- GNUNET_free (ca);
-}
-
-
-/**
- * Address-callback, used to send message to gnunet-nat-server.
- *
- * @param cls closure
- * @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 length of the @a addr
- */
-static void
-addr_cb (void *cls,
- int add_remove,
- const struct sockaddr *addr,
- socklen_t addrlen)
-{
- struct GNUNET_NAT_Test *h = cls;
- struct ClientActivity *ca;
- struct GNUNET_MQ_Envelope *env;
- struct GNUNET_NAT_TestMessage *msg;
- const struct sockaddr_in *sa;
-
- if (GNUNET_YES != add_remove)
- return;
- if (addrlen != sizeof (struct sockaddr_in))
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "NAT test ignores IPv6 address `%s' returned from NAT library\n",
- GNUNET_a2s (addr,
- addrlen));
- return; /* ignore IPv6 here */
- }
- LOG (GNUNET_ERROR_TYPE_INFO,
- "Asking gnunet-nat-server to connect to `%s'\n",
- GNUNET_a2s (addr,
- addrlen));
-
- ca = GNUNET_new (struct ClientActivity);
- ca->h = h;
- ca->mq = GNUNET_CLIENT_connecT (h->cfg,
- "gnunet-nat-server",
- NULL,
- &mq_error_handler,
- ca);
- if (NULL == ca->mq)
- {
- GNUNET_free (ca);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Failed to connect to `gnunet-nat-server'\n"));
- return;
- }
- GNUNET_CONTAINER_DLL_insert (h->ca_head,
- h->ca_tail,
- ca);
- sa = (const struct sockaddr_in *) addr;
- env = GNUNET_MQ_msg (msg,
- GNUNET_MESSAGE_TYPE_NAT_TEST);
- msg->dst_ipv4 = sa->sin_addr.s_addr;
- msg->dport = sa->sin_port;
- msg->data = h->data;
- msg->is_tcp = htonl ((uint32_t) h->is_tcp);
- GNUNET_MQ_send (ca->mq,
- env);
-}
-
-
-/**
- * Timeout task for a nat test.
- * Calls the report-callback with a timeout return value
- *
- * Destroys the nat handle after the callback has been processed.
- *
- * @param cls handle to the timed out NAT test
- */
-static void
-do_timeout (void *cls)
-{
- struct GNUNET_NAT_Test *nh = cls;
-
- nh->ttask = NULL;
- nh->report (nh->report_cls,
- (GNUNET_NAT_ERROR_SUCCESS == nh->status)
- ? GNUNET_NAT_ERROR_TIMEOUT
- : nh->status);
-}
-
-
-/**
- * Start testing if NAT traversal works using the
- * given configuration (IPv4-only).
- *
- * ALL failures are reported directly to the report callback
- *
- * @param cfg configuration for the NAT traversal
- * @param is_tcp #GNUNET_YES to test TCP, #GNUNET_NO to test UDP
- * @param bnd_port port to bind to, 0 for connection reversal
- * @param adv_port externally advertised port to use
- * @param timeout delay after which the test should be aborted
- * @param report function to call with the result of the test
- * @param report_cls closure for @a report
- * @return handle to cancel NAT test or NULL. The error is always indicated via the report callback
- */
-struct GNUNET_NAT_Test *
-GNUNET_NAT_test_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
- int is_tcp,
- uint16_t bnd_port,
- uint16_t adv_port,
- struct GNUNET_TIME_Relative timeout,
- GNUNET_NAT_TestCallback report,
- void *report_cls)
-{
- struct GNUNET_NAT_Test *nh;
- struct sockaddr_in sa;
- const struct sockaddr *addrs[] = {
- (const struct sockaddr *) &sa
- };
- const socklen_t addrlens[] = {
- sizeof (sa)
- };
-
- memset (&sa, 0, sizeof (sa));
- sa.sin_family = AF_INET;
- sa.sin_port = htons (bnd_port);
-#if HAVE_SOCKADDR_IN_SIN_LEN
- sa.sin_len = sizeof (sa);
-#endif
-
- nh = GNUNET_new (struct GNUNET_NAT_Test);
- nh->cfg = cfg;
- nh->is_tcp = is_tcp;
- nh->data = bnd_port;
- nh->adv_port = adv_port;
- nh->report = report;
- nh->report_cls = report_cls;
- nh->status = GNUNET_NAT_ERROR_SUCCESS;
- if (0 == bnd_port)
- {
- nh->nat
- = GNUNET_NAT_register (cfg,
- is_tcp,
- 0,
- 0,
- NULL,
- NULL,
- &addr_cb,
- &reversal_cb,
- nh,
- NULL);
- }
- else
- {
- nh->lsock =
- GNUNET_NETWORK_socket_create (AF_INET,
- (is_tcp ==
- GNUNET_YES) ? SOCK_STREAM : SOCK_DGRAM,
- 0);
- if ((nh->lsock == NULL) ||
- (GNUNET_OK !=
- GNUNET_NETWORK_socket_bind (nh->lsock,
- (const struct sockaddr *) &sa,
- sizeof (sa))))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Failed to create listen socket bound to `%s' for NAT test: %s\n"),
- GNUNET_a2s ((const struct sockaddr *) &sa,
- sizeof (sa)),
- STRERROR (errno));
- if (NULL != nh->lsock)
- {
- GNUNET_NETWORK_socket_close (nh->lsock);
- nh->lsock = NULL;
- }
- nh->status = GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR;
- nh->ttask = GNUNET_SCHEDULER_add_now (&do_timeout,
- nh);
- return nh;
- }
- if (GNUNET_YES == is_tcp)
- {
- GNUNET_break (GNUNET_OK ==
- GNUNET_NETWORK_socket_listen (nh->lsock,
- 5));
- nh->ltask =
- GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- nh->lsock,
- &do_accept,
- nh);
- }
- else
- {
- nh->ltask =
- GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- nh->lsock,
- &do_udp_read,
- nh);
- }
- LOG (GNUNET_ERROR_TYPE_INFO,
- "NAT test listens on port %u (%s)\n",
- bnd_port,
- (GNUNET_YES == is_tcp) ? "tcp" : "udp");
- nh->nat = GNUNET_NAT_register (cfg,
- is_tcp,
- adv_port,
- 1,
- addrs,
- addrlens,
- &addr_cb,
- NULL,
- nh,
- NULL);
- if (NULL == nh->nat)
- {
- LOG (GNUNET_ERROR_TYPE_INFO,
- _("NAT test failed to start NAT library\n"));
- if (NULL != nh->ltask)
- {
- GNUNET_SCHEDULER_cancel (nh->ltask);
- nh->ltask = NULL;
- }
- if (NULL != nh->lsock)
- {
- GNUNET_NETWORK_socket_close (nh->lsock);
- nh->lsock = NULL;
- }
- nh->status = GNUNET_NAT_ERROR_NAT_REGISTER_FAILED;
- nh->ttask = GNUNET_SCHEDULER_add_now (&do_timeout,
- nh);
- return nh;
- }
- }
- nh->ttask = GNUNET_SCHEDULER_add_delayed (timeout,
- &do_timeout,
- nh);
- return nh;
-}
-
-
-/**
- * Stop an active NAT test.
- *
- * @param tst test to stop.
- */
-void
-GNUNET_NAT_test_stop (struct GNUNET_NAT_Test *tst)
-{
- struct NatActivity *pos;
- struct ClientActivity *cpos;
-
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Stopping NAT test\n");
- while (NULL != (cpos = tst->ca_head))
- {
- GNUNET_CONTAINER_DLL_remove (tst->ca_head,
- tst->ca_tail,
- cpos);
- GNUNET_MQ_destroy (cpos->mq);
- GNUNET_free (cpos);
- }
- while (NULL != (pos = tst->na_head))
- {
- GNUNET_CONTAINER_DLL_remove (tst->na_head,
- tst->na_tail,
- pos);
- GNUNET_SCHEDULER_cancel (pos->rtask);
- GNUNET_NETWORK_socket_close (pos->sock);
- GNUNET_free (pos);
- }
- if (NULL != tst->ttask)
- {
- GNUNET_SCHEDULER_cancel (tst->ttask);
- tst->ttask = NULL;
- }
- if (NULL != tst->ltask)
- {
- GNUNET_SCHEDULER_cancel (tst->ltask);
- tst->ltask = NULL;
- }
- if (NULL != tst->lsock)
- {
- GNUNET_NETWORK_socket_close (tst->lsock);
- tst->lsock = NULL;
- }
- if (NULL != tst->nat)
- {
- GNUNET_NAT_unregister (tst->nat);
- tst->nat = NULL;
- }
- GNUNET_free (tst);
-}
-
-/* end of nat_test.c */
gnunet_peerinfo_LDADD = \
$(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \
- $(top_builddir)/src/nat/libgnunetnat.la \
$(top_builddir)/src/transport/libgnunettransport.la \
$(top_builddir)/src/hello/libgnunethello.la \
$(top_builddir)/src/statistics/libgnunetstatistics.la \
gnunet-transport-profiler.c
gnunet_transport_profiler_LDADD = \
libgnunettransport.la \
- $(top_builddir)/src/nat/libgnunetnat.la \
$(top_builddir)/src/hello/libgnunethello.la \
$(top_builddir)/src/ats/libgnunetats.la \
$(top_builddir)/src/util/libgnunetutil.la \
$(top_builddir)/src/ats/libgnunetats.la \
$(top_builddir)/src/hello/libgnunethello.la \
$(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \
- $(top_builddir)/src/nat/libgnunetnat.la \
$(top_builddir)/src/statistics/libgnunetstatistics.la \
$(top_builddir)/src/util/libgnunetutil.la \
$(GN_GLPK) \
$(top_builddir)/src/statistics/libgnunetstatistics.la \
$(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \
$(LIB_GNURL) \
- $(top_builddir)/src/nat/libgnunetnat.la \
$(top_builddir)/src/util/libgnunetutil.la
libgnunet_plugin_transport_http_client_la_LDFLAGS = \
$(GN_PLUGIN_LDFLAGS)
$(top_builddir)/src/statistics/libgnunetstatistics.la \
$(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \
$(LIB_GNURL) \
- $(top_builddir)/src/nat/libgnunetnat.la \
$(top_builddir)/src/util/libgnunetutil.la
libgnunet_plugin_transport_https_client_la_LDFLAGS = \
$(GN_PLUGIN_LDFLAGS)
$(top_builddir)/src/hello/libgnunethello.la \
$(top_builddir)/src/statistics/libgnunetstatistics.la \
$(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \
- $(top_builddir)/src/nat/libgnunetnat.la \
+ $(top_builddir)/src/nat/libgnunetnatnew.la \
$(top_builddir)/src/util/libgnunetutil.la
libgnunet_plugin_transport_https_server_la_LDFLAGS = \
$(GN_LIBMHD) \
test_quota_compliance_bluetooth_SOURCES = \
test_quota_compliance.c
test_quota_compliance_bluetooth_LDADD = \
- $(top_builddir)/src/nat/libgnunetnat.la \
libgnunettransport.la \
$(top_builddir)/src/hello/libgnunethello.la \
$(top_builddir)/src/ats/libgnunetats.la \