/*
This file is part of GNUnet.
- Copyright (C) 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2015, 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 free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License,
+ 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.
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file src/nat/gnunet-nat.c
- * @brief Daemon to auto configure nat
+ * @brief Command-line tool to interact with the NAT service
* @author Christian Grothoff
* @author Bruno Cabral
*/
#include "platform.h"
#include "gnunet_util_lib.h"
-#include "gnunet_nat_lib.h"
-#include "gnunet_protocols.h"
-#include "nat.h"
+#include "gnunet_nat_service.h"
+/**
+ * Value to return from #main().
+ */
+static int global_ret;
-struct GNUNET_CONFIGURATION_Handle *cfg;
+/**
+ * Name of section in configuration file to use for
+ * additional options.
+ */
+static char *section_name;
+/**
+ * Flag set to 1 if we use IPPROTO_UDP.
+ */
+static int use_udp;
+/**
+ * Flag set to 1 if we are to listen for connection reversal requests.
+ */
+static int listen_reversal;
/**
- * 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
+ * Flag set to 1 if we use IPPROTO_TCP.
*/
-static void
-auto_conf_iter (void *cls,
- const char *section,
- const char *option,
- const char *value)
-{
+static int use_tcp;
- printf( "%s: %s \n", option, value);
-}
+/**
+ * Protocol to use.
+ */
+static uint8_t proto;
+/**
+ * Local address to use for connection reversal request.
+ */
+static char *local_addr;
+/**
+ * Remote address to use for connection reversal request.
+ */
+static char *remote_addr;
/**
- * 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
+ * Should we actually bind to #bind_addr and receive and process STUN requests?
*/
+static int do_stun;
-void
-auto_config_cb(void *cls,
- const struct GNUNET_CONFIGURATION_Handle *diff,
- enum GNUNET_NAT_StatusCode result, enum GNUNET_NAT_Type type)
-{
- char* nat_type;
+/**
+ * Handle to NAT operation.
+ */
+static struct GNUNET_NAT_Handle *nh;
+
+/**
+ * Listen socket for STUN processing.
+ */
+static struct GNUNET_NETWORK_Handle *ls;
+/**
+ * Task for reading STUN packets.
+ */
+static struct GNUNET_SCHEDULER_Task *rtask;
- 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;
- }
+/**
+ * Test if all activities have finished, and if so,
+ * terminate.
+ */
+static void
+test_finished ()
+{
+ if (NULL != nh)
+ return;
+ if (NULL != rtask)
+ return;
+ GNUNET_SCHEDULER_shutdown ();
+}
- printf("NAT status: %s \n", nat_type );
- printf("SUGGESTED CHANGES: \n" );
- GNUNET_CONFIGURATION_iterate_section_values (diff,
- "nat",
- &auto_conf_iter,
- NULL);
+/**
+ * Signature of the callback passed to #GNUNET_NAT_register() for
+ * a function to call whenever our set of 'valid' addresses changes.
+ *
+ * @param cls closure, NULL
+ * @param add_remove #GNUNET_YES to add a new public IP address,
+ * #GNUNET_NO to remove a previous (now invalid) one
+ * @param ac address class the address belongs to
+ * @param addr either the previous or the new public IP address
+ * @param addrlen actual length of the @a addr
+ */
+static void
+address_cb (void *cls,
+ int add_remove,
+ enum GNUNET_NAT_AddressClass ac,
+ const struct sockaddr *addr,
+ socklen_t addrlen)
+{
+ fprintf (stdout,
+ "%s %s (%d)\n",
+ add_remove ? "+" : "-",
+ GNUNET_a2s (addr,
+ addrlen),
+ (int) ac);
+}
+
+
+/**
+ * Signature of the callback passed to #GNUNET_NAT_register().
+ * for a function to call whenever someone asks us to do connection
+ * reversal.
+ *
+ * @param cls closure, NULL
+ * @param remote_addr public IP address of the other peer
+ * @param remote_addrlen actual length of the @a remote_addr
+ */
+static void
+reversal_cb (void *cls,
+ const struct sockaddr *remote_addr,
+ socklen_t remote_addrlen)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Connection reversal requested by %s\n",
+ GNUNET_a2s (remote_addr,
+ remote_addrlen));
+}
- //TODO: Save config
+/**
+ * Task run on shutdown.
+ *
+ * @param cls NULL
+ */
+static void
+do_shutdown (void *cls)
+{
+ if (NULL != nh)
+ {
+ GNUNET_NAT_unregister (nh);
+ nh = NULL;
+ }
+ if (NULL != ls)
+ {
+ GNUNET_NETWORK_socket_close (ls);
+ ls = NULL;
+ }
+ if (NULL != rtask)
+ {
+ GNUNET_SCHEDULER_cancel (rtask);
+ rtask = NULL;
+ }
}
+/**
+ * Task to receive incoming packets for STUN processing.
+ */
+static void
+stun_read_task (void *cls)
+{
+ ssize_t size;
+
+ rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+ ls,
+ &stun_read_task,
+ NULL);
+ size = GNUNET_NETWORK_socket_recvfrom_amount (ls);
+ if (size > 0)
+ {
+ GNUNET_break (0);
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = 1;
+ return;
+ }
+ {
+ char buf[size + 1];
+ struct sockaddr_storage sa;
+ socklen_t salen = sizeof (sa);
+ ssize_t ret;
+
+ ret = GNUNET_NETWORK_socket_recvfrom (ls,
+ buf,
+ size + 1,
+ (struct sockaddr *) &sa,
+ &salen);
+ if (ret != size)
+ {
+ GNUNET_break (0);
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = 1;
+ return;
+ }
+ (void) GNUNET_NAT_stun_handle_packet (nh,
+ (const struct sockaddr *) &sa,
+ salen,
+ buf,
+ ret);
+ }
+}
-
/**
* Main function that will be run.
* @param c configuration
*/
static void
-run (void *cls, char *const *args, const char *cfgfile,
+run (void *cls,
+ char *const *args,
+ const char *cfgfile,
const struct GNUNET_CONFIGURATION_Handle *c)
{
-
+ uint8_t af;
+ struct sockaddr *local_sa;
+ struct sockaddr *remote_sa;
+ socklen_t local_len;
+ size_t remote_len;
+
+ if (use_tcp && use_udp)
+ {
+ 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;
+
+ GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
+ NULL);
+
+ if (0 == proto)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Must specify either TCP or UDP\n");
+ global_ret = 1;
+ return;
+ }
+ local_len = 0;
+ local_sa = NULL;
+ remote_len = 0;
+ remote_sa = NULL;
+ if (NULL != local_addr)
+ {
+ local_len = (socklen_t) GNUNET_STRINGS_parse_socket_addr (local_addr,
+ &af,
+ &local_sa);
+ if (0 == local_len)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Invalid socket address `%s'\n",
+ local_addr);
+ goto fail_and_shutdown;
+ }
+ }
- GNUNET_NAT_autoconfig_start(c,auto_config_cb, NULL);
+ if (NULL != remote_addr)
+ {
+ remote_len = GNUNET_STRINGS_parse_socket_addr (remote_addr,
+ &af,
+ &remote_sa);
+ if (0 == remote_len)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Invalid socket address `%s'\n",
+ remote_addr);
+ goto fail_and_shutdown;
+ }
+ }
+
+ if (NULL != local_addr)
+ {
+ if (NULL == section_name)
+ section_name = GNUNET_strdup ("undefined");
+ nh = GNUNET_NAT_register (c,
+ section_name,
+ proto,
+ 1,
+ (const struct sockaddr **) &local_sa,
+ &local_len,
+ &address_cb,
+ (listen_reversal) ? &reversal_cb : NULL,
+ NULL);
+ }
+ else if (listen_reversal)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Use of `-W` only effective in combination with `-i`\n");
+ goto fail_and_shutdown;
+ }
+ if (NULL != remote_addr)
+ {
+ int ret;
+
+ if ( (NULL == nh) ||
+ (sizeof (struct sockaddr_in) != local_len) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Require IPv4 local address to initiate connection reversal\n");
+ goto fail_and_shutdown;
+ }
+ if (sizeof (struct sockaddr_in) != remote_len)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Require IPv4 reversal target address\n");
+ goto fail_and_shutdown;
+ }
+ GNUNET_assert (AF_INET == local_sa->sa_family);
+ GNUNET_assert (AF_INET == remote_sa->sa_family);
+ ret = GNUNET_NAT_request_reversal (nh,
+ (const struct sockaddr_in *) local_sa,
+ (const struct sockaddr_in *) remote_sa);
+ switch (ret)
+ {
+ case GNUNET_SYSERR:
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Connection reversal internal error\n");
+ break;
+ case GNUNET_NO:
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Connection reversal unavailable\n");
+ break;
+ case GNUNET_OK:
+ /* operation in progress */
+ break;
+ }
+ }
+
+ if (do_stun)
+ {
+ if (NULL == local_addr)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Require local address to support STUN requests\n");
+ goto fail_and_shutdown;
+ }
+ if (IPPROTO_UDP != proto)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "STUN only supported over UDP\n");
+ goto fail_and_shutdown;
+ }
+ ls = GNUNET_NETWORK_socket_create (af,
+ SOCK_DGRAM,
+ IPPROTO_UDP);
+ if (NULL == ls)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Failed to create socket\n");
+ goto fail_and_shutdown;
+ }
+ if (GNUNET_OK !=
+ GNUNET_NETWORK_socket_bind (ls,
+ local_sa,
+ local_len))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to bind to %s: %s\n",
+ GNUNET_a2s (local_sa,
+ local_len),
+ STRERROR (errno));
+ goto fail_and_shutdown;
+ }
+ rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+ ls,
+ &stun_read_task,
+ NULL);
+ }
+ GNUNET_free_non_null (remote_sa);
+ GNUNET_free_non_null (local_sa);
+ test_finished ();
+ return;
+ fail_and_shutdown:
+ global_ret = 1;
+ GNUNET_SCHEDULER_shutdown ();
+ GNUNET_free_non_null (remote_sa);
+ GNUNET_free_non_null (local_sa);
}
* @return 0 on success, -1 on error
*/
int
-main (int argc, char *const argv[])
+main (int argc,
+ char *const argv[])
{
- static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ struct GNUNET_GETOPT_CommandLineOption options[] = {
+
+ GNUNET_GETOPT_option_string ('i',
+ "in",
+ "ADDRESS",
+ gettext_noop ("which IP and port are we locally using to bind/listen to"),
+ &local_addr),
+
+ GNUNET_GETOPT_option_string ('r',
+ "remote",
+ "ADDRESS",
+ gettext_noop ("which remote IP and port should be asked for connection reversal"),
+ &remote_addr),
+
+ GNUNET_GETOPT_option_string ('S',
+ "section",
+ NULL,
+ gettext_noop ("name of configuration section to find additional options, such as manual host punching data"),
+ §ion_name),
+
+ GNUNET_GETOPT_option_flag ('s',
+ "stun",
+ gettext_noop ("enable STUN processing"),
+ &do_stun),
+
+ GNUNET_GETOPT_option_flag ('t',
+ "tcp",
+ gettext_noop ("use TCP"),
+ &use_tcp),
+
+ GNUNET_GETOPT_option_flag ('u',
+ "udp",
+ gettext_noop ("use UDP"),
+ &use_udp),
+
+ GNUNET_GETOPT_option_flag ('W',
+ "watch",
+ gettext_noop ("watch for connection reversal requests"),
+ &listen_reversal),
GNUNET_GETOPT_OPTION_END
};
- int ret = 0;
- if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_get_utf8_args (argc, argv,
+ &argc, &argv))
return 2;
-
- /* Lets start resolver */
- char *fn;
- struct GNUNET_OS_Process *proc;
-
- fn = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver");
- proc = GNUNET_OS_start_process (GNUNET_YES,
- GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
- NULL, NULL, NULL,
- fn,
- "gnunet-service-resolver");
- GNUNET_assert (NULL != proc);
-
if (GNUNET_OK !=
- GNUNET_PROGRAM_run (argc, argv, "gnunet-nat [options]",
- _("GNUnet NAT traversal autoconfigure daemon"), options,
- &run, NULL))
- {
- ret = 1;
- }
-
- /* Now kill the resolver */
- if (0 != GNUNET_OS_process_kill (proc, GNUNET_TERM_SIG))
+ GNUNET_PROGRAM_run (argc, argv,
+ "gnunet-nat [options]",
+ _("GNUnet NAT traversal autoconfigure daemon"),
+ options,
+ &run,
+ NULL))
{
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
+ global_ret = 1;
}
- GNUNET_OS_process_wait (proc);
- GNUNET_OS_process_destroy (proc);
- proc = NULL;
-
-
GNUNET_free ((void*) argv);
- return ret;
+ return global_ret;
}
-/* end of gnunet-nat-server.c */
+/* end of gnunet-nat.c */