From 46ac0cea02fe1949d76b86c7a0750f9e7854fef6 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 1 Jul 2011 10:25:59 +0000 Subject: finishing gnunet-nat-server --- doc/man/Makefile.am | 1 + doc/man/gnunet-arm.1 | 2 +- src/include/gnunet_protocols.h | 5 + src/nat/Makefile.am | 5 +- src/nat/gnunet-nat-server.c | 275 ++++++++++++++++++++++++++++++++++++++++- src/nat/nat.c | 8 +- src/nat/nat.h | 63 ++++++++++ src/util/network.c | 1 - 8 files changed, 347 insertions(+), 13 deletions(-) create mode 100644 src/nat/nat.h diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am index 4fda5b75c..20575204f 100644 --- a/doc/man/Makefile.am +++ b/doc/man/Makefile.am @@ -2,6 +2,7 @@ man_MANS = \ gnunet-arm.1 \ gnunet-directory.1 \ gnunet-download.1 \ + gnunet-nat-server.1 \ gnunet-peerinfo.1 \ gnunet-pseudonym.1 \ gnunet-publish.1 \ diff --git a/doc/man/gnunet-arm.1 b/doc/man/gnunet-arm.1 index e440479c3..30e0082ef 100644 --- a/doc/man/gnunet-arm.1 +++ b/doc/man/gnunet-arm.1 @@ -4,7 +4,7 @@ gnunet\-arm \- control GNUnet services .SH SYNOPSIS -.B gnunet-arm +.B gnunet\-arm .RI [ options ] .br diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h index 000b71f3b..957bcd0eb 100644 --- a/src/include/gnunet_protocols.h +++ b/src/include/gnunet_protocols.h @@ -308,6 +308,11 @@ extern "C" */ #define GNUNET_MESSAGE_TYPE_TRANSPORT_ATS 61 +/** + * Message to ask NAT server to perform traversal test + */ +#define GNUNET_MESSAGE_TYPE_NAT_TEST 63 + /** * Initial setup message from core client to core. */ diff --git a/src/nat/Makefile.am b/src/nat/Makefile.am index e8dadb240..75798a252 100644 --- a/src/nat/Makefile.am +++ b/src/nat/Makefile.am @@ -23,9 +23,10 @@ bin_PROGRAMS = \ $(NATBIN) gnunet_nat_server_SOURCES = \ - gnunet-nat-server.c + gnunet-nat-server.c nat.h gnunet_nat_server_LDADD = \ - $(top_builddir)/src/util/libgnunetutil.la + $(top_builddir)/src/nat/libgnunetnat.la \ + $(top_builddir)/src/util/libgnunetutil.la gnunet_helper_nat_server_SOURCES = \ $(NATSERVER) diff --git a/src/nat/gnunet-nat-server.c b/src/nat/gnunet-nat-server.c index c1f1be668..ae831db08 100644 --- a/src/nat/gnunet-nat-server.c +++ b/src/nat/gnunet-nat-server.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - (C) 2010 Christian Grothoff (and other contributing authors) + (C) 2011 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -26,11 +26,220 @@ */ #include "platform.h" #include "gnunet_util_lib.h" +#include "gnunet_nat_lib.h" +#include "gnunet_protocols.h" +#include "nat.h" /** - * Should we print some debug output? + * Our server. */ -#define VERBOSE 0 +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; + + h = GNUNET_NAT_register (cfg, + is_tcp, + dport, + 0, NULL, NULL, + NULL, NULL, NULL); + memset (&sa, 0, sizeof (sa)); +#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 ctx the 'struct TcpContext' + * @param tc scheduler context + */ +static void +tcp_send (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct TcpContext *ctx = cls; + + 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_WARNING, "send"); + GNUNET_NETWORK_socket_shutdown (ctx->s, SHUT_RDWR); + } + GNUNET_NETWORK_socket_close (ctx->s); + GNUNET_free (ctx); +} + + +/** + * Try to send 'data' to the + * IP 'dst_ipv4' at port 'dport' via TCP. + * + * @param dst_ivp4 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_UNIX, SOCK_STREAM, 0); + if (NULL == s) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "socket"); + return; + } + memset (&sa, 0, sizeof (sa)); +#if HAVE_SOCKADDR_IN_SIN_LEN + sa.sin_len = sizeof (sa); +#endif + sa.sin_addr.s_addr = dst_ipv4; + sa.sin_port = htons (dport); + 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_malloc (sizeof (struct TcpContext)); + ctx->s = s; + ctx->data = data; + GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_SECONDS, + s, + &tcp_send, ctx); +} + + +/** + * Try to send 'data' to the + * IP 'dst_ipv4' at port 'dport' via UDP. + * + * @param dst_ivp4 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_UNIX, SOCK_DGRAM, 0); + if (NULL == s) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "socket"); + return; + } + memset (&sa, 0, sizeof (sa)); +#if HAVE_SOCKADDR_IN_SIN_LEN + sa.sin_len = sizeof (sa); +#endif + sa.sin_addr.s_addr = dst_ipv4; + sa.sin_port = htons (dport); + 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; + + 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 ctx unused + * @param tc scheduler context + */ +static void +shutdown_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_SERVER_destroy (server); + server = NULL; +} + /** * Main function that will be run. @@ -44,11 +253,67 @@ static void run (void *cls, char *const *args, const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle * c) + 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 ( (args[0] == NULL) || + (1 != SSCANF (args[0], "%u", &port)) || + (0 == port) || + (65536 >= port) ) + { + fprintf (stderr, + _("Please pass valid port number as the first argument!\n")); + return; + } + memset (&in4, 0, sizeof (in4)); + memset (&in6, 0, sizeof (in6)); + in4.sin_port = htons ((uint16_t) port); + in6.sin6_port = htons ((uint16_t) port); +#if HAVE_SOCKADDR_IN_SIN_LEN + in4.sin_len = sizeof (in); + 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_delayed (GNUNET_TIME_UNIT_FOREVER_REL, + &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[]) { @@ -58,7 +323,7 @@ main (int argc, char *const argv[]) if (GNUNET_OK != GNUNET_PROGRAM_run (argc, argv, - "gnunet-nat-server", + "gnunet-nat-server [options] PORT", _("GNUnet NAT traversal test helper daemon"), options, &run, NULL)) diff --git a/src/nat/nat.c b/src/nat/nat.c index 418c619e3..e708cdabf 100644 --- a/src/nat/nat.c +++ b/src/nat/nat.c @@ -1323,10 +1323,10 @@ GNUNET_NAT_run_client (struct GNUNET_NAT_Handle *h, GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop"); return; } - GNUNET_snprintf(port_as_string, - sizeof (port_as_string), - "%d", - h->adv_port); + GNUNET_snprintf (port_as_string, + sizeof (port_as_string), + "%d", + h->adv_port); #if DEBUG_TCP_NAT GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "nat", diff --git a/src/nat/nat.h b/src/nat/nat.h new file mode 100644 index 000000000..bff444e6d --- /dev/null +++ b/src/nat/nat.h @@ -0,0 +1,63 @@ +/* + This file is part of GNUnet. + (C) 2011 Christian Grothoff (and other contributing authors) + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file src/nat/nat.h + * @brief Messages for interaction with gnunet-nat-server + * @author Christian Grothoff + * + */ +#ifndef NAT_H +#define NAT_H +#include "gnunet_util_lib.h" + +/** + * Request to test NAT traversal. + */ +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; + +}; + +#endif diff --git a/src/util/network.c b/src/util/network.c index b0669b5b1..e51d7fdab 100644 --- a/src/util/network.c +++ b/src/util/network.c @@ -392,7 +392,6 @@ GNUNET_NETWORK_socket_connect (const struct GNUNET_NETWORK_Handle *desc, #ifdef MINGW if (SOCKET_ERROR == ret) - { SetErrnoFromWinsockError (WSAGetLastError ()); if (errno == EWOULDBLOCK) -- cgit v1.2.3