summaryrefslogtreecommitdiff
path: root/src/nat-auto
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2017-01-06 13:26:38 +0100
committerChristian Grothoff <christian@grothoff.org>2017-01-06 13:26:38 +0100
commit80e1315b56f559db60499f5373e90c293c5ab065 (patch)
tree3b279e6ac8e6298d7d9d96d12868f628a413939b /src/nat-auto
parent1b7f6eea0a06abab9b75b30f021aa6313fccfcd4 (diff)
separate service for autoconfiguration from NAT traversal
Diffstat (limited to 'src/nat-auto')
-rw-r--r--src/nat-auto/.gitignore3
-rw-r--r--src/nat-auto/Makefile.am58
-rw-r--r--src/nat-auto/gnunet-nat-auto.c428
-rw-r--r--src/nat-auto/gnunet-nat-server.c384
-rw-r--r--src/nat-auto/gnunet-service-nat-auto.c481
-rw-r--r--src/nat-auto/nat-auto.conf.in15
-rw-r--r--src/nat-auto/nat-auto.h110
-rw-r--r--src/nat-auto/nat_auto_api.c274
-rw-r--r--src/nat-auto/nat_auto_api_test.c644
9 files changed, 2397 insertions, 0 deletions
diff --git a/src/nat-auto/.gitignore b/src/nat-auto/.gitignore
new file mode 100644
index 000000000..6ba53d72f
--- /dev/null
+++ b/src/nat-auto/.gitignore
@@ -0,0 +1,3 @@
+gnunet-service-nat-auto
+gnunet-nat-auto
+gnunet-nat-server
diff --git a/src/nat-auto/Makefile.am b/src/nat-auto/Makefile.am
new file mode 100644
index 000000000..dbe910306
--- /dev/null
+++ b/src/nat-auto/Makefile.am
@@ -0,0 +1,58 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+libexecdir= $(pkglibdir)/libexec/
+
+pkgcfgdir= $(pkgdatadir)/config.d/
+
+pkgcfg_DATA = \
+ nat-auto.conf
+
+bin_PROGRAMS = \
+ gnunet-nat-auto \
+ gnunet-nat-server
+
+libexec_PROGRAMS = \
+ gnunet-service-nat-auto
+
+gnunet_nat_server_SOURCES = \
+ gnunet-nat-server.c nat-auto.h
+gnunet_nat_server_LDADD = \
+ $(top_builddir)/src/nat/libgnunetnat.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+gnunet_nat_auto_SOURCES = \
+ gnunet-nat-auto.c nat-auto.h
+gnunet_nat_auto_LDADD = \
+ libgnunetnatauto.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+
+if USE_COVERAGE
+ AM_CFLAGS = -fprofile-arcs -ftest-coverage
+endif
+
+lib_LTLIBRARIES = \
+ libgnunetnatauto.la
+
+libgnunetnatauto_la_SOURCES = \
+ nat_auto_api.c \
+ nat_auto_api_test.c
+libgnunetnatauto_la_LIBADD = \
+ $(top_builddir)/src/nat/libgnunetnatnew.la \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(GN_LIBINTL) @EXT_LIBS@
+libgnunetnatauto_la_LDFLAGS = \
+ $(GN_LIB_LDFLAGS) $(WINFLAGS) \
+ -version-info 0:0:0
+
+gnunet_service_nat_auto_SOURCES = \
+ gnunet-service-nat-auto.c
+gnunet_service_nat_auto_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(top_builddir)/src/statistics/libgnunetstatistics.la \
+ $(top_builddir)/src/nat/libgnunetnatnew.la \
+ $(LIBGCRYPT_LIBS) \
+ -lgcrypt \
+ $(GN_LIBINTL)
+
diff --git a/src/nat-auto/gnunet-nat-auto.c b/src/nat-auto/gnunet-nat-auto.c
new file mode 100644
index 000000000..3b9a5fa94
--- /dev/null
+++ b/src/nat-auto/gnunet-nat-auto.c
@@ -0,0 +1,428 @@
+/*
+ This file is part of GNUnet.
+ Copyright (C) 2015, 2016, 2017 GNUnet e.V.
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU 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 src/nat/gnunet-nat-auto.c
+ * @brief Command-line tool for testing and autoconfiguration of NAT traversal
+ * @author Christian Grothoff
+ * @author Bruno Cabral
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_nat_service.h"
+#include "gnunet_nat_auto_service.h"
+
+/**
+ * Value to return from #main().
+ */
+static int global_ret;
+
+/**
+ * Handle to ongoing autoconfiguration.
+ */
+static struct GNUNET_NAT_AutoHandle *ah;
+
+/**
+ * If we do auto-configuration, should we write the result
+ * to a file?
+ */
+static int write_cfg;
+
+/**
+ * Configuration filename.
+ */
+static const char *cfg_file;
+
+/**
+ * Original configuration.
+ */
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Address we are bound to (in test), or should bind to
+ * (if #do_stun is set).
+ */
+static char *bind_addr;
+
+/**
+ * External IP address and port to use for the test.
+ * If not set, use #bind_addr.
+ */
+static char *extern_addr;
+
+/**
+ * Should we run autoconfiguration?
+ */
+static unsigned int do_auto;
+
+/**
+ * Handle to a NAT test operation.
+ */
+static struct GNUNET_NAT_Test *nt;
+
+/**
+ * Flag set to 1 if we use IPPROTO_UDP.
+ */
+static int use_udp;
+
+/**
+ * Flag set to 1 if we use IPPROTO_TCP.
+ */
+static int use_tcp;
+
+/**
+ * Protocol to use.
+ */
+static uint8_t proto;
+
+/**
+ * Test if all activities have finished, and if so,
+ * terminate.
+ */
+static void
+test_finished ()
+{
+ if (NULL != ah)
+ return;
+ if (NULL != nt)
+ return;
+ GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Function to iterate over sugested changes options
+ *
+ * @param cls closure
+ * @param section name of the section
+ * @param option name of the option
+ * @param value value of the option
+ */
+static void
+auto_conf_iter (void *cls,
+ const char *section,
+ const char *option,
+ const char *value)
+{
+ struct GNUNET_CONFIGURATION_Handle *new_cfg = cls;
+
+ PRINTF ("%s: %s\n",
+ option,
+ value);
+ if (NULL != new_cfg)
+ GNUNET_CONFIGURATION_set_value_string (new_cfg,
+ section,
+ option,
+ value);
+}
+
+
+/**
+ * Function called with the result from the autoconfiguration.
+ *
+ * @param cls closure
+ * @param diff minimal suggested changes to the original configuration
+ * to make it work (as best as we can)
+ * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
+ * @param type what the situation of the NAT
+ */
+static void
+auto_config_cb (void *cls,
+ const struct GNUNET_CONFIGURATION_Handle *diff,
+ enum GNUNET_NAT_StatusCode result,
+ enum GNUNET_NAT_Type type)
+{
+ const char *nat_type;
+ char unknown_type[64];
+ struct GNUNET_CONFIGURATION_Handle *new_cfg;
+
+ ah = NULL;
+ switch (type)
+ {
+ case GNUNET_NAT_TYPE_NO_NAT:
+ nat_type = "NO NAT";
+ break;
+ case GNUNET_NAT_TYPE_UNREACHABLE_NAT:
+ nat_type = "NAT but we can traverse";
+ break;
+ case GNUNET_NAT_TYPE_STUN_PUNCHED_NAT:
+ nat_type = "NAT but STUN is able to identify the correct information";
+ break;
+ case GNUNET_NAT_TYPE_UPNP_NAT:
+ nat_type = "NAT but UPNP opened the ports";
+ break;
+ default:
+ SPRINTF (unknown_type,
+ "NAT unknown, type %u",
+ type);
+ nat_type = unknown_type;
+ break;
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "NAT status: %s/%s\n",
+ GNUNET_NAT_status2string (result),
+ nat_type);
+
+ /* Shortcut: if there are no changes suggested, bail out early. */
+ if (GNUNET_NO ==
+ GNUNET_CONFIGURATION_is_dirty (diff))
+ {
+ test_finished ();
+ return;
+ }
+
+ /* Apply diff to original configuration and show changes
+ to the user */
+ new_cfg = write_cfg ? GNUNET_CONFIGURATION_dup (cfg) : NULL;
+
+ if (NULL != diff)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ _("Suggested configuration changes:\n"));
+ GNUNET_CONFIGURATION_iterate_section_values (diff,
+ "nat",
+ &auto_conf_iter,
+ new_cfg);
+ }
+
+ /* If desired, write configuration to file; we write only the
+ changes to the defaults to keep things compact. */
+ if ( (write_cfg) &&
+ (NULL != diff) )
+ {
+ struct GNUNET_CONFIGURATION_Handle *def_cfg;
+
+ GNUNET_CONFIGURATION_set_value_string (new_cfg,
+ "ARM",
+ "CONFIG",
+ NULL);
+ def_cfg = GNUNET_CONFIGURATION_create ();
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_CONFIGURATION_load (def_cfg,
+ NULL));
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_write_diffs (def_cfg,
+ new_cfg,
+ cfg_file))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ _("Failed to write configuration to `%s'\n"),
+ cfg_file);
+ global_ret = 1;
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ _("Wrote updated configuration to `%s'\n"),
+ cfg_file);
+ }
+ GNUNET_CONFIGURATION_destroy (def_cfg);
+ }
+
+ if (NULL != new_cfg)
+ GNUNET_CONFIGURATION_destroy (new_cfg);
+ test_finished ();
+}
+
+
+/**
+ * Function called to report success or failure for
+ * NAT configuration test.
+ *
+ * @param cls closure
+ * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
+ */
+static void
+test_report_cb (void *cls,
+ enum GNUNET_NAT_StatusCode result)
+{
+ nt = NULL;
+ PRINTF ("NAT test result: %s\n",
+ GNUNET_NAT_status2string (result));
+ test_finished ();
+}
+
+
+/**
+ * Task run on shutdown.
+ *
+ * @param cls NULL
+ */
+static void
+do_shutdown (void *cls)
+{
+ if (NULL != ah)
+ {
+ GNUNET_NAT_autoconfig_cancel (ah);
+ ah = NULL;
+ }
+ if (NULL != nt)
+ {
+ GNUNET_NAT_test_stop (nt);
+ nt = NULL;
+ }
+}
+
+
+/**
+ * Main function that will be run.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param c configuration
+ */
+static void
+run (void *cls,
+ char *const *args,
+ const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *c)
+{
+ struct sockaddr_in bind_sa;
+ struct sockaddr_in extern_sa;
+
+ cfg_file = cfgfile;
+ cfg = c;
+
+ GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
+ NULL);
+
+ if (do_auto)
+ {
+ ah = GNUNET_NAT_autoconfig_start (c,
+ &auto_config_cb,
+ NULL);
+ }
+
+ if (use_tcp && use_udp)
+ {
+ if (do_auto)
+ return;
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Cannot use TCP and UDP\n");
+ global_ret = 1;
+ return;
+ }
+ proto = 0;
+ if (use_tcp)
+ proto = IPPROTO_TCP;
+ if (use_udp)
+ proto = IPPROTO_UDP;
+
+ if (NULL != bind_addr)
+ {
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_to_address_ipv4 (bind_addr,
+ strlen (bind_addr),
+ &bind_sa))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Invalid socket address `%s'\n",
+ bind_addr);
+ global_ret = 1;
+ return;
+ }
+ }
+ if (NULL != extern_addr)
+ {
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_to_address_ipv4 (extern_addr,
+ strlen (extern_addr),
+ &extern_sa))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Invalid socket address `%s'\n",
+ extern_addr);
+ global_ret = 1;
+ return;
+ }
+ }
+
+ if (NULL != bind_addr)
+ {
+ if (NULL == extern_addr)
+ extern_sa = bind_sa;
+ nt = GNUNET_NAT_test_start (c,
+ proto,
+ bind_sa.sin_addr,
+ ntohs (bind_sa.sin_port),
+ extern_sa.sin_addr,
+ ntohs (extern_sa.sin_port),
+ &test_report_cb,
+ NULL);
+ }
+ test_finished ();
+}
+
+
+/**
+ * Main function of gnunet-nat
+ *
+ * @param argc number of command-line arguments
+ * @param argv command line
+ * @return 0 on success, -1 on error
+ */
+int
+main (int argc,
+ char *const argv[])
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ {'a', "auto", NULL,
+ gettext_noop ("run autoconfiguration"),
+ GNUNET_NO, &GNUNET_GETOPT_set_one, &do_auto },
+ {'b', "bind", "ADDRESS",
+ gettext_noop ("which IP and port are we bound to"),
+ GNUNET_YES, &GNUNET_GETOPT_set_string, &bind_addr },
+ {'e', "external", "ADDRESS",
+ gettext_noop ("which external IP and port should be used to test"),
+ GNUNET_YES, &GNUNET_GETOPT_set_string, &extern_addr },
+ {'t', "tcp", NULL,
+ gettext_noop ("use TCP"),
+ GNUNET_NO, &GNUNET_GETOPT_set_one, &use_tcp },
+ {'u', "udp", NULL,
+ gettext_noop ("use UDP"),
+ GNUNET_NO, &GNUNET_GETOPT_set_one, &use_udp },
+ {'w', "write", NULL,
+ gettext_noop ("write configuration file (for autoconfiguration)"),
+ GNUNET_NO, &GNUNET_GETOPT_set_one, &write_cfg },
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_get_utf8_args (argc, argv,
+ &argc, &argv))
+ return 2;
+ if (GNUNET_OK !=
+ GNUNET_PROGRAM_run (argc, argv,
+ "gnunet-nat-auto [options]",
+ _("GNUnet NAT traversal autoconfiguration"),
+ options,
+ &run,
+ NULL))
+ {
+ global_ret = 1;
+ }
+ GNUNET_free ((void*) argv);
+ return global_ret;
+}
+
+
+/* end of gnunet-nat-auto.c */
diff --git a/src/nat-auto/gnunet-nat-server.c b/src/nat-auto/gnunet-nat-server.c
new file mode 100644
index 000000000..93352f5f0
--- /dev/null
+++ b/src/nat-auto/gnunet-nat-server.c
@@ -0,0 +1,384 @@
+/*
+ This file is part of GNUnet.
+ Copyright (C) 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 src/nat/gnunet-nat-server.c
+ * @brief Daemon to run on 'gnunet.org' to help test NAT traversal code
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_nat_lib.h"
+#include "gnunet_protocols.h"
+#include "nat-auto.h"
+
+
+/**
+ * Our server.
+ */
+static struct GNUNET_SERVER_Handle *server;
+
+/**
+ * Our configuration.
+ */
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+
+/**
+ * Try contacting the peer using autonomous NAT traveral method.
+ *
+ * @param dst_ipv4 IPv4 address to send the fake ICMP message
+ * @param dport destination port to include in ICMP message
+ * @param is_tcp mark for TCP (#GNUNET_YES) or UDP (#GNUNET_NO)
+ */
+static void
+try_anat (uint32_t dst_ipv4,
+ uint16_t dport,
+ int is_tcp)
+{
+ struct GNUNET_NAT_Handle *h;
+ struct sockaddr_in sa;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Asking for connection reversal with %x and code %u\n",
+ (unsigned int) dst_ipv4,
+ (unsigned int) dport);
+ h = GNUNET_NAT_register (cfg,
+ is_tcp,
+ dport,
+ 0,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ memset (&sa, 0, sizeof (sa));
+ sa.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa.sin_len = sizeof (sa);
+#endif
+ sa.sin_addr.s_addr = dst_ipv4;
+ GNUNET_NAT_run_client (h, &sa);
+ GNUNET_NAT_unregister (h);
+}
+
+
+/**
+ * Closure for #tcp_send.
+ */
+struct TcpContext
+{
+ /**
+ * TCP socket.
+ */
+ struct GNUNET_NETWORK_Handle *s;
+
+ /**
+ * Data to transmit.
+ */
+ uint16_t data;
+};
+
+
+/**
+ * Task called by the scheduler once we can do the TCP send
+ * (or once we failed to connect...).
+ *
+ * @param cls the `struct TcpContext`
+ */
+static void
+tcp_send (void *cls)
+{
+ struct TcpContext *ctx = cls;
+ const struct GNUNET_SCHEDULER_TaskContext *tc;
+
+ tc = GNUNET_SCHEDULER_get_task_context ();
+ if ((NULL != tc->write_ready) &&
+ (GNUNET_NETWORK_fdset_isset (tc->write_ready, ctx->s)))
+ {
+ if (-1 ==
+ GNUNET_NETWORK_socket_send (ctx->s, &ctx->data, sizeof (ctx->data)))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "send");
+ }
+ GNUNET_NETWORK_socket_shutdown (ctx->s, SHUT_RDWR);
+ }
+ GNUNET_NETWORK_socket_close (ctx->s);
+ GNUNET_free (ctx);
+}
+
+
+/**
+ * Try to send @a data to the
+ * IP @a dst_ipv4' at port @a dport via TCP.
+ *
+ * @param dst_ipv4 target IP
+ * @param dport target port
+ * @param data data to send
+ */
+static void
+try_send_tcp (uint32_t dst_ipv4,
+ uint16_t dport,
+ uint16_t data)
+{
+ struct GNUNET_NETWORK_Handle *s;
+ struct sockaddr_in sa;
+ struct TcpContext *ctx;
+
+ s = GNUNET_NETWORK_socket_create (AF_INET,
+ SOCK_STREAM,
+ 0);
+ if (NULL == s)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "socket");
+ return;
+ }
+ memset (&sa, 0, sizeof (sa));
+ sa.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa.sin_len = sizeof (sa);
+#endif
+ sa.sin_addr.s_addr = dst_ipv4;
+ sa.sin_port = htons (dport);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending TCP message to `%s'\n",
+ GNUNET_a2s ((struct sockaddr *) &sa,
+ sizeof (sa)));
+ if ( (GNUNET_OK !=
+ GNUNET_NETWORK_socket_connect (s,
+ (const struct sockaddr *) &sa,
+ sizeof (sa))) &&
+ (errno != EINPROGRESS) )
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "connect");
+ GNUNET_NETWORK_socket_close (s);
+ return;
+ }
+ ctx = GNUNET_new (struct TcpContext);
+ ctx->s = s;
+ ctx->data = data;
+ GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_SECONDS,
+ s,
+ &tcp_send,
+ ctx);
+}
+
+
+/**
+ * Try to send @a data to the
+ * IP @a dst_ipv4 at port @a dport via UDP.
+ *
+ * @param dst_ipv4 target IP
+ * @param dport target port
+ * @param data data to send
+ */
+static void
+try_send_udp (uint32_t dst_ipv4,
+ uint16_t dport,
+ uint16_t data)
+{
+ struct GNUNET_NETWORK_Handle *s;
+ struct sockaddr_in sa;
+
+ s = GNUNET_NETWORK_socket_create (AF_INET,
+ SOCK_DGRAM,
+ 0);
+ if (NULL == s)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "socket");
+ return;
+ }
+ memset (&sa, 0, sizeof (sa));
+ sa.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa.sin_len = sizeof (sa);
+#endif
+ sa.sin_addr.s_addr = dst_ipv4;
+ sa.sin_port = htons (dport);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending UDP packet to `%s'\n",
+ GNUNET_a2s ((struct sockaddr *) &sa,
+ sizeof (sa)));
+ if (-1 ==
+ GNUNET_NETWORK_socket_sendto (s,
+ &data,
+ sizeof (data),
+ (const struct sockaddr *) &sa,
+ sizeof (sa)))
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "sendto");
+ GNUNET_NETWORK_socket_close (s);
+}
+
+
+/**
+ * We've received a request to probe a NAT
+ * traversal. Do it.
+ *
+ * @param cls unused
+ * @param client handle to client (we always close)
+ * @param msg message with details about what to test
+ */
+static void
+test (void *cls,
+ struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *msg)
+{
+ const struct GNUNET_NAT_TestMessage *tm;
+ uint16_t dport;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received test request\n");
+ tm = (const struct GNUNET_NAT_TestMessage *) msg;
+ dport = ntohs (tm->dport);
+ if (0 == dport)
+ try_anat (tm->dst_ipv4,
+ ntohs (tm->data),
+ (int) ntohl (tm->is_tcp));
+ else if (GNUNET_YES == ntohl (tm->is_tcp))
+ try_send_tcp (tm->dst_ipv4,
+ dport,
+ tm->data);
+ else
+ try_send_udp (tm->dst_ipv4,
+ dport,
+ tm->data);
+ GNUNET_SERVER_receive_done (client,
+ GNUNET_NO);
+}
+
+
+/**
+ * Task run during shutdown.
+ *
+ * @param cls unused
+ */
+static void
+shutdown_task (void *cls)
+{
+ GNUNET_SERVER_destroy (server);
+ server = NULL;
+}
+
+
+/**
+ * Main function that will be run.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param c configuration
+ */
+static void
+run (void *cls,
+ char *const *args,
+ const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *c)
+{
+ static const struct GNUNET_SERVER_MessageHandler handlers[] = {
+ {&test, NULL, GNUNET_MESSAGE_TYPE_NAT_TEST,
+ sizeof (struct GNUNET_NAT_TestMessage)},
+ {NULL, NULL, 0, 0}
+ };
+ unsigned int port;
+ struct sockaddr_in in4;
+ struct sockaddr_in6 in6;
+
+ socklen_t slen[] = {
+ sizeof (in4),
+ sizeof (in6),
+ 0
+ };
+ struct sockaddr *sa[] = {
+ (struct sockaddr *) &in4,
+ (struct sockaddr *) &in6,
+ NULL
+ };
+
+ cfg = c;
+ if ( (NULL == args[0]) ||
+ (1 != SSCANF (args[0], "%u", &port)) ||
+ (0 == port) ||
+ (65536 <= port) )
+ {
+ FPRINTF (stderr,
+ _("Please pass valid port number as the first argument! (got `%s')\n"),
+ args[0]);
+ return;
+ }
+ memset (&in4, 0, sizeof (in4));
+ memset (&in6, 0, sizeof (in6));
+ in4.sin_family = AF_INET;
+ in4.sin_port = htons ((uint16_t) port);
+ in6.sin6_family = AF_INET6;
+ in6.sin6_port = htons ((uint16_t) port);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ in4.sin_len = sizeof (in4);
+ in6.sin6_len = sizeof (in6);
+#endif
+ server = GNUNET_SERVER_create (NULL,
+ NULL,
+ (struct sockaddr * const *) sa,
+ slen,
+ GNUNET_TIME_UNIT_SECONDS,
+ GNUNET_YES);
+ GNUNET_SERVER_add_handlers (server,
+ handlers);
+ GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
+ NULL);
+}
+
+
+/**
+ * Main function of gnunet-nat-server.
+ *
+ * @param argc number of command-line arguments
+ * @param argv command line
+ * @return 0 on success, -1 on error
+ */
+int
+main (int argc, char *const argv[])
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_get_utf8_args (argc, argv,
+ &argc, &argv))
+ return 2;
+
+ if (GNUNET_OK !=
+ GNUNET_PROGRAM_run (argc,
+ argv,
+ "gnunet-nat-server [options] PORT",
+ _("GNUnet NAT traversal test helper daemon"),
+ options,
+ &run,
+ NULL))
+ {
+ GNUNET_free ((void*) argv);
+ return 1;
+ }
+ GNUNET_free ((void*) argv);
+ return 0;
+}
+
+
+/* end of gnunet-nat-server.c */
diff --git a/src/nat-auto/gnunet-service-nat-auto.c b/src/nat-auto/gnunet-service-nat-auto.c
new file mode 100644
index 000000000..897d6feb2
--- /dev/null
+++ b/src/nat-auto/gnunet-service-nat-auto.c
@@ -0,0 +1,481 @@
+/*
+ This file is part of GNUnet.
+ Copyright (C) 2016, 2017 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-auto/gnunet-service-nat-auto.c
+ * @brief NAT autoconfiguration service
+ * @author Christian Grothoff
+ *
+ * TODO:
+ * - merge client handle and autoconfig context
+ * - implement "more" autoconfig:
+ * + re-work gnunet-nat-server & integrate!
+ * + test manually punched NAT (how?)
+ */
+#include "platform.h"
+#include <math.h>
+#include "gnunet_util_lib.h"
+#include "gnunet_protocols.h"
+#include "gnunet_signatures.h"
+#include "gnunet_nat_service.h"
+#include "gnunet_statistics_service.h"
+#include "gnunet_resolver_service.h"
+#include "nat-auto.h"
+#include <gcrypt.h>
+
+
+/**
+ * How long do we wait until we forcefully terminate autoconfiguration?
+ */
+#define AUTOCONFIG_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
+
+
+/**
+ * Internal data structure we track for each of our clients.
+ */
+struct ClientHandle
+{
+
+ /**
+ * Kept in a DLL.
+ */
+ struct ClientHandle *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct ClientHandle *prev;
+
+ /**
+ * Underlying handle for this client with the service.
+ */
+ struct GNUNET_SERVICE_Client *client;
+
+ /**
+ * Message queue for communicating with the client.
+ */
+ struct GNUNET_MQ_Handle *mq;
+};
+
+
+/**
+ * Context for autoconfiguration operations.
+ */
+struct AutoconfigContext
+{
+ /**
+ * Kept in a DLL.
+ */
+ struct AutoconfigContext *prev;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct AutoconfigContext *next;
+
+ /**
+ * Which client asked the question.
+ */
+ struct ClientHandle *ch;
+
+ /**
+ * Configuration we are creating.
+ */
+ struct GNUNET_CONFIGURATION_Handle *c;
+
+ /**
+ * Original configuration (for diffing).
+ */
+ struct GNUNET_CONFIGURATION_Handle *orig;
+
+ /**
+ * Timeout task to force termination.
+ */
+ struct GNUNET_SCHEDULER_Task *timeout_task;
+
+ /**
+ * #GNUNET_YES if upnpc should be used,
+ * #GNUNET_NO if upnpc should not be used,
+ * #GNUNET_SYSERR if we should simply not change the option.
+ */
+ int enable_upnpc;
+
+ /**
+ * Status code to return to the client.
+ */
+ enum GNUNET_NAT_StatusCode status_code;
+
+ /**
+ * NAT type to return to the client.
+ */
+ enum GNUNET_NAT_Type type;
+};
+
+
+/**
+ * Head of client DLL.
+ */
+static struct ClientHandle *ch_head;
+
+/**
+ * Tail of client DLL.
+ */
+static struct ClientHandle *ch_tail;
+
+/**
+ * DLL of our autoconfiguration operations.
+ */
+static struct AutoconfigContext *ac_head;
+
+/**
+ * DLL of our autoconfiguration operations.
+ */
+static struct AutoconfigContext *ac_tail;
+
+/**
+ * Handle to our current configuration.
+ */
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Handle to the statistics service.
+ */
+static struct GNUNET_STATISTICS_Handle *stats;
+
+
+/**
+ * Check validity of #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message
+ * from client.
+ *
+ * @param cls client who sent the message
+ * @param message the message received
+ * @return #GNUNET_OK if message is well-formed
+ */
+static int
+check_autoconfig_request (void *cls,
+ const struct GNUNET_NAT_AutoconfigRequestMessage *message)
+{
+ return GNUNET_OK; /* checked later */
+}
+
+
+/**
+ * Stop all pending activities with respect to the @a ac
+ *
+ * @param ac autoconfiguration to terminate activities for
+ */
+static void
+terminate_ac_activities (struct AutoconfigContext *ac)
+{
+ if (NULL != ac->timeout_task)
+ {
+ GNUNET_SCHEDULER_cancel (ac->timeout_task);
+ ac->timeout_task = NULL;
+ }
+}
+
+
+/**
+ * Finish handling the autoconfiguration request and send
+ * the response to the client.
+ *
+ * @param cls the `struct AutoconfigContext` to conclude
+ */
+static void
+conclude_autoconfig_request (void *cls)
+{
+ struct AutoconfigContext *ac = cls;
+ struct ClientHandle *ch = ac->ch;
+ struct GNUNET_NAT_AutoconfigResultMessage *arm;
+ struct GNUNET_MQ_Envelope *env;
+ size_t c_size;
+ char *buf;
+ struct GNUNET_CONFIGURATION_Handle *diff;
+
+ ac->timeout_task = NULL;
+ terminate_ac_activities (ac);
+
+ /* Send back response */
+ diff = GNUNET_CONFIGURATION_get_diff (ac->orig,
+ ac->c);
+ buf = GNUNET_CONFIGURATION_serialize (diff,
+ &c_size);
+ GNUNET_CONFIGURATION_destroy (diff);
+ env = GNUNET_MQ_msg_extra (arm,
+ c_size,
+ GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT);
+ arm->status_code = htonl ((uint32_t) ac->status_code);
+ arm->type = htonl ((uint32_t) ac->type);
+ GNUNET_memcpy (&arm[1],
+ buf,
+ c_size);
+ GNUNET_free (buf);
+ GNUNET_MQ_send (ch->mq,
+ env);
+
+ /* clean up */
+ GNUNET_CONFIGURATION_destroy (ac->orig);
+ GNUNET_CONFIGURATION_destroy (ac->c);
+ GNUNET_CONTAINER_DLL_remove (ac_head,
+ ac_tail,
+ ac);
+ GNUNET_free (ac);
+ GNUNET_SERVICE_client_continue (ch->client);
+}
+
+
+/**
+ * Check if all autoconfiguration operations have concluded,
+ * and if they have, send the result back to the client.
+ *
+ * @param ac autoconfiguation context to check
+ */
+static void
+check_autoconfig_finished (struct AutoconfigContext *ac)
+{
+ GNUNET_SCHEDULER_cancel (ac->timeout_task);
+ ac->timeout_task
+ = GNUNET_SCHEDULER_add_now (&conclude_autoconfig_request,
+ ac);
+}
+
+
+/**
+ * Update ENABLE_UPNPC configuration option.
+ *
+ * @param ac autoconfiguration to update
+ */
+static void
+update_enable_upnpc_option (struct AutoconfigContext *ac)
+{
+ switch (ac->enable_upnpc)
+ {
+ case GNUNET_YES:
+ GNUNET_CONFIGURATION_set_value_string (ac->c,
+ "NAT",
+ "ENABLE_UPNP",
+ "YES");
+ break;
+ case GNUNET_NO:
+ GNUNET_CONFIGURATION_set_value_string (ac->c,
+ "NAT",
+ "ENABLE_UPNP",
+ "NO");
+ break;
+ case GNUNET_SYSERR:
+ /* We are unsure, do not change option */
+ break;
+ }
+}
+
+
+/**
+ * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message from
+ * client.
+ *
+ * @param cls client who sent the message
+ * @param message the message received
+ */
+static void
+handle_autoconfig_request (void *cls,
+ const struct GNUNET_NAT_AutoconfigRequestMessage *message)
+{
+ struct ClientHandle *ch = cls;
+ size_t left = ntohs (message->header.size) - sizeof (*message);
+ struct AutoconfigContext *ac;
+
+ ac = GNUNET_new (struct AutoconfigContext);
+ ac->status_code = GNUNET_NAT_ERROR_SUCCESS;
+ ac->ch = ch;
+ ac->c = GNUNET_CONFIGURATION_create ();
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_deserialize (ac->c,
+ (const char *) &message[1],
+ left,
+ GNUNET_NO))
+ {
+ GNUNET_break (0);
+ GNUNET_SERVICE_client_drop (ch->client);
+ GNUNET_CONFIGURATION_destroy (ac->c);
+ GNUNET_free (ac);
+ return;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received REQUEST_AUTO_CONFIG message from client\n");
+
+ GNUNET_CONTAINER_DLL_insert (ac_head,
+ ac_tail,
+ ac);
+ ac->orig
+ = GNUNET_CONFIGURATION_dup (ac->c);
+ ac->timeout_task
+ = GNUNET_SCHEDULER_add_delayed (AUTOCONFIG_TIMEOUT,
+ &conclude_autoconfig_request,
+ ac);
+ ac->enable_upnpc = GNUNET_SYSERR; /* undecided */
+
+ /* Probe for upnpc */
+ if (GNUNET_SYSERR ==
+ GNUNET_OS_check_helper_binary ("upnpc",
+ GNUNET_NO,
+ NULL))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("UPnP client `upnpc` command not found, disabling UPnP\n"));
+ ac->enable_upnpc = GNUNET_NO;
+ }
+ else
+ {
+ /* We might at some point be behind NAT, try upnpc */
+ ac->enable_upnpc = GNUNET_YES;
+ }
+ update_enable_upnpc_option (ac);
+
+ /* Finally, check if we are already done */
+ check_autoconfig_finished (ac);
+}
+
+
+/**
+ * Task run during shutdown.
+ *
+ * @param cls unused
+ */
+static void
+shutdown_task (void *cls)
+{
+ struct AutoconfigContext *ac;
+
+ while (NULL != (ac = ac_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (ac_head,
+ ac_tail,
+ ac);
+ terminate_ac_activities (ac);
+ GNUNET_free (ac);
+ }
+ if (NULL != stats)
+ {
+ GNUNET_STATISTICS_destroy (stats,
+ GNUNET_NO);
+ stats = NULL;
+ }
+}
+
+
+/**
+ * Setup NAT service.
+ *
+ * @param cls closure
+ * @param c configuration to use
+ * @param service the initialized service
+ */
+static void
+run (void *cls,
+ const struct GNUNET_CONFIGURATION_Handle *c,
+ struct GNUNET_SERVICE_Handle *service)
+{
+ cfg = c;
+ GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
+ NULL);
+ stats = GNUNET_STATISTICS_create ("nat-auto",
+ cfg);
+}
+
+
+/**
+ * Callback called when a client connects to the service.
+ *
+ * @param cls closure for the service
+ * @param c the new client that connected to the service
+ * @param mq the message queue used to send messages to the client
+ * @return a `struct ClientHandle`
+ */
+static void *
+client_connect_cb (void *cls,
+ struct GNUNET_SERVICE_Client *c,
+ struct GNUNET_MQ_Handle *mq)
+{
+ struct ClientHandle *ch;
+
+ ch = GNUNET_new (struct ClientHandle);
+ ch->mq = mq;
+ ch->client = c;
+ GNUNET_CONTAINER_DLL_insert (ch_head,
+ ch_tail,
+ ch);
+ return ch;
+}
+
+
+/**
+ * Callback called when a client disconnected from the service
+ *
+ * @param cls closure for the service
+ * @param c the client that disconnected
+ * @param internal_cls a `struct ClientHandle *`
+ */
+static void
+client_disconnect_cb (void *cls,
+ struct GNUNET_SERVICE_Client *c,
+ void *internal_cls)
+{
+ struct ClientHandle *ch = internal_cls;
+
+ GNUNET_CONTAINER_DLL_remove (ch_head,
+ ch_tail,
+ ch);
+ GNUNET_free (ch);
+}
+
+
+/**
+ * Define "main" method using service macro.
+ */
+GNUNET_SERVICE_MAIN
+("nat",
+ GNUNET_SERVICE_OPTION_NONE,
+ &run,
+ &client_connect_cb,
+ &client_disconnect_cb,
+ NULL,
+ GNUNET_MQ_hd_var_size (autoconfig_request,
+ GNUNET_MESSAGE_TYPE_NAT_AUTO_REQUEST_CFG,
+ struct GNUNET_NAT_AutoconfigRequestMessage,
+ NULL),
+ GNUNET_MQ_handler_end ());
+
+
+#if defined(LINUX) && defined(__GLIBC__)
+#include <malloc.h>
+
+/**
+ * MINIMIZE heap size (way below 128k) since this process doesn't need much.
+ */
+void __attribute__ ((constructor))
+GNUNET_ARM_memory_init ()
+{
+ mallopt (M_TRIM_THRESHOLD, 4 * 1024);
+ mallopt (M_TOP_PAD, 1 * 1024);
+ malloc_trim (0);
+}
+#endif
+
+/* end of gnunet-service-nat.c */
diff --git a/src/nat-auto/nat-auto.conf.in b/src/nat-auto/nat-auto.conf.in
new file mode 100644
index 000000000..daa3e389d
--- /dev/null
+++ b/src/nat-auto/nat-auto.conf.in
@@ -0,0 +1,15 @@
+[nat]
+AUTOSTART = @AUTOSTART@
+@UNIXONLY@ PORT = 2124
+HOSTNAME = localhost
+BINARY = gnunet-service-nat-auto
+ACCEPT_FROM = 127.0.0.1;
+ACCEPT_FROM6 = ::1;
+UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-nat-auto.sock
+UNIX_MATCH_UID = YES
+UNIX_MATCH_GID = YES
+
+[gnunet-nat-server]
+HOSTNAME = gnunet.org
+PORT = 5724
+NOARMBIND = YES
diff --git a/src/nat-auto/nat-auto.h b/src/nat-auto/nat-auto.h
new file mode 100644
index 000000000..150dc32c2
--- /dev/null
+++ b/src/nat-auto/nat-auto.h
@@ -0,0 +1,110 @@
+/*
+ This file is part of GNUnet.
+ Copyright (C) 2011, 2016, 2017 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 src/nat-auto/nat-auto.h
+ * @brief Messages for interaction with gnunet-nat-auto-service
+ * @author Christian Grothoff
+ *
+ */
+#ifndef NAT_AUTO_H
+#define NAT_AUTO_H
+#include "gnunet_util_lib.h"
+
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * Request to test NAT traversal, sent to the gnunet-nat-server
+ * (not the service!).
+ */
+struct GNUNET_NAT_TestMessage
+{
+ /**
+ * Header with type #GNUNET_MESSAGE_TYPE_NAT_TEST
+ */
+ struct GNUNET_MessageHeader header;
+
+ /**
+ * IPv4 target IP address
+ */
+ uint32_t dst_ipv4;
+
+ /**
+ * Port to use, 0 to send dummy ICMP response.
+ */
+ uint16_t dport;
+
+ /**
+ * Data to send OR advertised-port (in NBO) to use for dummy ICMP.
+ */
+ uint16_t data;
+
+ /**
+ * #GNUNET_YES for TCP, #GNUNET_NO for UDP.
+ */
+ int32_t is_tcp;
+
+};
+
+
+/**
+ * Client requesting automatic configuration.
+ */
+struct GNUNET_NAT_AutoconfigRequestMessage
+{
+ /**
+ * Header with type #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG
+ */
+ struct GNUNET_MessageHeader header;
+
+ /* Followed by configuration (diff, serialized, compressed) */
+
+};
+
+
+/**
+ * Service responding with proposed configuration.
+ */
+struct GNUNET_NAT_AutoconfigResultMessage
+{
+ /**
+ * Header with type #GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT
+ */
+ struct GNUNET_MessageHeader header;
+
+ /**
+ * An `enum GNUNET_NAT_StatusCode` in NBO.
+ */
+ int32_t status_code GNUNET_PACKED;
+
+ /**
+ * An `enum GNUNET_NAT_Type` in NBO.
+ */
+ int32_t type GNUNET_PACKED;
+
+ /* Followed by configuration (diff, serialized, compressed) */
+};
+
+
+GNUNET_NETWORK_STRUCT_END
+
+#endif
diff --git a/src/nat-auto/nat_auto_api.c b/src/nat-auto/nat_auto_api.c
new file mode 100644
index 000000000..e6b0512c6
--- /dev/null
+++ b/src/nat-auto/nat_auto_api.c
@@ -0,0 +1,274 @@
+
+/*
+ This file is part of GNUnet.
+ Copyright (C) 2007-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.
+*/
+
+/**
+ * @author Christian Grothoff
+ * @author Milan Bouchet-Valat
+ *
+ * @file nat/nat_auto_api.c
+ * Routines for NAT auto configuration.
+ */
+#include "platform.h"
+#include "gnunet_nat_service.h"
+#include "gnunet_nat_auto_service.h"
+#include "nat-auto.h"
+
+
+
+/**
+ * Handle to auto-configuration in progress.
+ */
+struct GNUNET_NAT_AutoHandle
+{
+
+ /**
+ * Configuration we use.
+ */
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ /**
+ * Message queue for communicating with the NAT service.
+ */
+ struct GNUNET_MQ_Handle *mq;
+
+ /**
+ * Function called with the result from the autoconfiguration.
+ */
+ GNUNET_NAT_AutoResultCallback arc;
+
+ /**
+ * Closure for @e arc.
+ */
+ void *arc_cls;
+
+};
+
+
+/**
+ * Converts `enum GNUNET_NAT_StatusCode` to string
+ *
+ * @param err error code to resolve to a string
+ * @return point 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 _ ("IPC failure");
+ 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");
+ default:
+ return "unknown status code";
+ }
+}
+
+
+/**
+ * Check result from autoconfiguration attempt.
+ *
+ * @param cls the `struct GNUNET_NAT_AutoHandle`
+ * @param res the result
+ * @return #GNUNET_OK if @a res is well-formed (always for now)
+ */
+static int
+check_auto_result (void *cls,
+ const struct GNUNET_NAT_AutoconfigResultMessage *res)
+{
+ return GNUNET_OK;
+}
+
+
+/**
+ * Handle result from autoconfiguration attempt.
+ *
+ * @param cls the `struct GNUNET_NAT_AutoHandle`
+ * @param res the result
+ */
+static void
+handle_auto_result (void *cls,
+ const struct GNUNET_NAT_AutoconfigResultMessage *res)
+{
+ struct GNUNET_NAT_AutoHandle *ah = cls;
+ size_t left;
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+ enum GNUNET_NAT_Type type
+ = (enum GNUNET_NAT_Type) ntohl (res->type);
+ enum GNUNET_NAT_StatusCode status
+ = (enum GNUNET_NAT_StatusCode) ntohl (res->status_code);
+
+ left = ntohs (res->header.size) - sizeof (*res);
+ cfg = GNUNET_CONFIGURATION_create ();
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_deserialize (cfg,
+ (const char *) &res[1],
+ left,
+ GNUNET_NO))
+ {
+ GNUNET_break (0);
+ ah->arc (ah->arc_cls,
+ NULL,
+ GNUNET_NAT_ERROR_IPC_FAILURE,
+ type);
+ }
+ else
+ {
+ ah->arc (ah->arc_cls,
+ cfg,
+ status,
+ type);
+ }
+ GNUNET_CONFIGURATION_destroy (cfg);
+ GNUNET_NAT_autoconfig_cancel (ah);
+}
+
+
+/**
+ * Handle queue errors by reporting autoconfiguration failure.
+ *
+ * @param cls the `struct GNUNET_NAT_AutoHandle *`
+ * @param error details about the error
+ */
+static void
+ah_error_handler (void *cls,
+ enum GNUNET_MQ_Error error)
+{
+ struct GNUNET_NAT_AutoHandle *ah = cls;
+
+ ah->arc (ah->arc_cls,
+ NULL,
+ GNUNET_NAT_ERROR_IPC_FAILURE,
+ GNUNET_NAT_TYPE_UNKNOWN);
+ GNUNET_NAT_autoconfig_cancel (ah);
+}
+
+
+/**
+ * Start auto-configuration routine. The transport adapters should
+ * be stopped while 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 = GNUNET_new (struct GNUNET_NAT_AutoHandle);
+ struct GNUNET_MQ_MessageHandler handlers[] = {
+ GNUNET_MQ_hd_var_size (auto_result,
+ GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT,
+ struct GNUNET_NAT_AutoconfigResultMessage,
+ ah),
+ GNUNET_MQ_handler_end ()
+ };
+ struct GNUNET_MQ_Envelope *env;
+ struct GNUNET_NAT_AutoconfigRequestMessage *req;
+ char *buf;
+ size_t size;
+
+ buf = GNUNET_CONFIGURATION_serialize (cfg,
+ &size);
+ if (size > GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (*req))
+ {
+ GNUNET_break (0);
+ GNUNET_free (buf);
+ GNUNET_free (ah);
+ return NULL;
+ }
+ ah->arc = cb;
+ ah->arc_cls = cb_cls;
+ ah->mq = GNUNET_CLIENT_connecT (cfg,
+ "nat",
+ handlers,
+ &ah_error_handler,
+ ah);
+ if (NULL == ah->mq)
+ {
+ GNUNET_break (0);
+ GNUNET_free (buf);
+ GNUNET_free (ah);
+ return NULL;
+ }
+ env = GNUNET_MQ_msg_extra (req,
+ size,
+ GNUNET_MESSAGE_TYPE_NAT_AUTO_REQUEST_CFG);
+ GNUNET_memcpy (&req[1],
+ buf,
+ size);
+ GNUNET_free (buf);
+ GNUNET_MQ_send (ah->mq,
+ env);
+ return ah;
+}
+
+
+/**
+ * Abort autoconfiguration.
+ *
+ * @param ah handle for operation to abort
+ */
+void
+GNUNET_NAT_autoconfig_cancel (struct GNUNET_NAT_AutoHandle *ah)
+{
+ GNUNET_MQ_destroy (ah->mq);
+ GNUNET_free (ah);
+}
+
+/* end of nat_api_auto.c */
diff --git a/src/nat-auto/nat_auto_api_test.c b/src/nat-auto/nat_auto_api_test.c
new file mode 100644
index 000000000..056d2a2bf
--- /dev/null
+++ b/src/nat-auto/nat_auto_api_test.c
@@ -0,0 +1,644 @@
+/*
+ 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_auto_api_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-auto.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 */