From e43df97dfdb10903dd53571ab07863d95740592c Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 30 Nov 2016 08:17:33 +0100 Subject: [PATCH] moving basic logic for launching nat-server helper to new NAT service --- src/nat/Makefile.am | 3 +- src/nat/gnunet-service-nat.c | 1 + src/nat/gnunet-service-nat_helper.c | 345 ++++++++++++++++++++++++++++ src/nat/gnunet-service-nat_helper.h | 74 ++++++ 4 files changed, 422 insertions(+), 1 deletion(-) create mode 100644 src/nat/gnunet-service-nat_helper.c create mode 100644 src/nat/gnunet-service-nat_helper.h diff --git a/src/nat/Makefile.am b/src/nat/Makefile.am index 6e96c41ff..0ca6dc1d2 100644 --- a/src/nat/Makefile.am +++ b/src/nat/Makefile.am @@ -96,7 +96,8 @@ libgnunetnatnew_la_LDFLAGS = \ gnunet_service_nat_SOURCES = \ gnunet-service-nat.c \ - gnunet-service-nat_stun.c gnunet-service-nat_stun.h + gnunet-service-nat_stun.c gnunet-service-nat_stun.h \ + gnunet-service-nat_helper.c gnunet-service-nat_helper.h gnunet_service_nat_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ $(top_builddir)/src/statistics/libgnunetstatistics.la \ diff --git a/src/nat/gnunet-service-nat.c b/src/nat/gnunet-service-nat.c index 8499a9724..ffee6374e 100644 --- a/src/nat/gnunet-service-nat.c +++ b/src/nat/gnunet-service-nat.c @@ -35,6 +35,7 @@ #include "gnunet_statistics_service.h" #include "gnunet_nat_service.h" #include "gnunet-service-nat_stun.h" +#include "gnunet-service-nat_helper.h" #include "nat.h" #include diff --git a/src/nat/gnunet-service-nat_helper.c b/src/nat/gnunet-service-nat_helper.c new file mode 100644 index 000000000..4867f5675 --- /dev/null +++ b/src/nat/gnunet-service-nat_helper.c @@ -0,0 +1,345 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2010, 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/gnunet-service-nat_helper.c + * @brief runs the gnunet-helper-nat-server + * @author Milan Bouchet-Valat + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet-service-nat_helper.h" + +#define LOG(kind,...) GNUNET_log_from (kind, "nat", __VA_ARGS__) + +/** + * Information we keep per NAT helper process. + */ +struct HelperContext +{ + + /** + * IP address we pass to the NAT helper. + */ + const char *internal_address; + + /** + * Function to call if we receive a reversal request. + */ + GN_ReversalCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * 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; + + /** + * The process id of the server process (if behind NAT) + */ + struct GNUNET_OS_Process *server_proc; + + /** + * 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; +}; + + +/** + * Task that restarts the gnunet-helper-nat-server process after a crash + * after a certain delay. + * + * @param cls a `struct HelperContext` + */ +static void +restart_nat_server (void *cls); + + +/** + * Try again starting the helper later + * + * @param h context of the helper + */ +static void +try_again (struct HelperContext *h) +{ + GNUNET_assert (NULL == h->server_read_task); + 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); +} + + +/** + * 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 `struct HelperContext` + */ +static void +nat_server_read (void *cls) +{ + struct HelperContext *h = cls; + char mybuf[40]; + ssize_t bytes; + 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; + try_again (h); + return; + } + + port_start = NULL; + for (size_t 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->cb (h->cb_cls, + &sin_addr); + h->server_read_task + = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + h->server_stdout_handle, + &nat_server_read, + h); +} + + +/** + * Task that restarts the gnunet-helper-nat-server process after a crash + * after a certain delay. + * + * @param cls a `struct HelperContext` + */ +static void +restart_nat_server (void *cls) +{ + struct HelperContext *h = cls; + char *binary; + + h->server_read_task = NULL; + h->server_stdout + = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, + GNUNET_NO, GNUNET_YES); + if (NULL == h->server_stdout) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "pipe"); + try_again (h); + return; + } + 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 (NULL == h->server_proc) + { + 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; + try_again (h); + return; + } + /* 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); +} + + +/** + * Start the gnunet-helper-nat-server and process incoming + * requests. + * + * @param internal_address + * @param cb function to call if we receive a request + * @param cb_cls closure for @a cb + * @return NULL on error + */ +struct HelperContext * +GN_start_gnunet_nat_server_ (const char *internal_address, + GN_ReversalCallback cb, + void *cb_cls) +{ + struct HelperContext *h; + + h = GNUNET_new (struct HelperContext); + h->cb = cb; + h->cb_cls = cb_cls; + h->internal_address + = internal_address; + if (NULL == h->server_stdout) + { + GN_stop_gnunet_nat_server_ (h); + return NULL; + } + return h; +} + + +/** + * Start the gnunet-helper-nat-server and process incoming + * requests. + * + * @param h helper context to stop + */ +void +GN_stop_gnunet_nat_server_ (struct HelperContext *h) +{ + if (NULL != h->server_read_task) + { + GNUNET_SCHEDULER_cancel (h->server_read_task); + h->server_read_task = 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; + } + GNUNET_free (h); +} + +/* end of gnunet-service-nat_helper.c */ diff --git a/src/nat/gnunet-service-nat_helper.h b/src/nat/gnunet-service-nat_helper.h new file mode 100644 index 000000000..c3074d9ad --- /dev/null +++ b/src/nat/gnunet-service-nat_helper.h @@ -0,0 +1,74 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2010, 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/gnunet-service-nat_helper.h + * @brief runs the gnunet-helper-nat-server + * @author Milan Bouchet-Valat + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" + + +/** + * Information we keep per NAT helper process. + */ +struct HelperContext; + + +/** + * Function called whenever we get a connection reversal + * request from another peer. + * + * @param cls closure + * @param ra IP address of the peer who wants us to connect to it + */ +typedef void +(*GN_ReversalCallback) (void *cls, + const struct sockaddr_in *ra); + + +/** + * Start the gnunet-helper-nat-server and process incoming + * requests. + * + * @param internal_address + * @param cb function to call if we receive a request + * @param cb_cls closure for @a cb + * @return NULL on error + */ +struct HelperContext * +GN_start_gnunet_nat_server_ (const char *internal_address, + GN_ReversalCallback cb, + void *cb_cls); + + +/** + * Start the gnunet-helper-nat-server and process incoming + * requests. + * + * @param h helper context to stop + */ +void +GN_stop_gnunet_nat_server_ (struct HelperContext *h); + + +/* end of gnunet-service-nat_helper.h */ -- 2.25.1