From 80e1315b56f559db60499f5373e90c293c5ab065 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 6 Jan 2017 13:26:38 +0100 Subject: separate service for autoconfiguration from NAT traversal --- src/nat-auto/gnunet-nat-server.c | 384 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 384 insertions(+) create mode 100644 src/nat-auto/gnunet-nat-server.c (limited to 'src/nat-auto/gnunet-nat-server.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 */ -- cgit v1.2.3