From 1b4333f3a6a4f76aa76fc3abc21e8476ea0a92e3 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 30 Nov 2016 07:31:09 +0100 Subject: [PATCH] towards moving STUN logic into new NAT service --- src/include/gnunet_nat_service.h | 102 ++++++++---- src/nat/Makefile.am | 7 +- src/nat/gnunet-service-nat.c | 35 +++- src/nat/gnunet-service-nat_stun.c | 211 ++++++++++++++++++++++++ src/nat/gnunet-service-nat_stun.h | 61 +++++++ src/nat/nat_api.c | 10 +- src/nat/nat_api_stun.c | 261 ++++++++++++++++++++++++++++++ src/nat/nat_stun.c | 126 --------------- src/nat/nat_stun.h | 170 +++++++++++++++++-- 9 files changed, 802 insertions(+), 181 deletions(-) create mode 100644 src/nat/gnunet-service-nat_stun.c create mode 100644 src/nat/gnunet-service-nat_stun.h create mode 100644 src/nat/nat_api_stun.c diff --git a/src/include/gnunet_nat_service.h b/src/include/gnunet_nat_service.h index a59069545..378f5a8cb 100644 --- a/src/include/gnunet_nat_service.h +++ b/src/include/gnunet_nat_service.h @@ -197,37 +197,6 @@ GNUNET_NAT_register (const struct GNUNET_CONFIGURATION_Handle *cfg, void *callback_cls); -/** - * Handle an incoming STUN message. This function is useful as - * some GNUnet service may be listening on a UDP port and might - * thus receive STUN messages while trying to receive other data. - * In this case, this function can be used to act as a proper - * STUN server (if desired). - * - * The function does some basic sanity checks on packet size and - * content, try to extract a bit of information, and possibly replies - * if this is an actual STUN message. - * - * At the moment this only processes BIND requests, and returns the - * externally visible address of the request. - * - * @param nh handle to the NAT service - * @param sender_addr address from which we got @a data - * @param sender_addr_len number of bytes in @a sender_addr - * @param data the packet - * @param data_size number of bytes in @a data - * @return #GNUNET_OK on success - * #GNUNET_NO if the packet is not a STUN packet - * #GNUNET_SYSERR on internal error handling the packet - */ -int -GNUNET_NAT_stun_handle_packet (struct GNUNET_NAT_Handle *nh, - const struct sockaddr *sender_addr, - size_t sender_addr_len, - const void *data, - size_t data_size); - - /** * Test if the given address is (currently) a plausible IP address for * this peer. Mostly a convenience function so that clients do not @@ -397,6 +366,77 @@ typedef void enum GNUNET_NAT_StatusCode result); +/** + * Handle an incoming STUN message. This function is useful as + * some GNUnet service may be listening on a UDP port and might + * thus receive STUN messages while trying to receive other data. + * In this case, this function can be used to process replies + * to STUN requests. + * + * The function does some basic sanity checks on packet size and + * content, try to extract a bit of information. + * + * At the moment this only processes BIND requests, and returns the + * externally visible address of the request to the rest of the + * NAT logic. + * + * @param nh handle to the NAT service + * @param sender_addr address from which we got @a data + * @param sender_addr_len number of bytes in @a sender_addr + * @param data the packet + * @param data_size number of bytes in @a data + * @return #GNUNET_OK on success + * #GNUNET_NO if the packet is not a STUN packet + * #GNUNET_SYSERR on internal error handling the packet + */ +int +GNUNET_NAT_stun_handle_packet (struct GNUNET_NAT_Handle *nh, + const struct sockaddr *sender_addr, + size_t sender_addr_len, + const void *data, + size_t data_size); + + +/** + * Handle to a request given to the resolver. Can be used to cancel + * the request prior to the timeout or successful execution. Also + * used to track our internal state for the request. + */ +struct GNUNET_NAT_STUN_Handle; + + +/** + * Make Generic STUN request. Sends a generic stun request to the + * server specified using the specified socket. If we do this, + * we need to watch for possible responses and call + * #GNUNET_NAT_stun_handle_packet() on incoming packets. + * + * @param server the address of the stun server + * @param port port of the stun server, in host byte order + * @param sock the socket used to send the request, must be a + * UDP socket + * @param cb callback in case of error + * @param cb_cls closure for @a cb + * @return NULL on error + */ +struct GNUNET_NAT_STUN_Handle * +GNUNET_NAT_stun_make_request (const char *server, + uint16_t port, + struct GNUNET_NETWORK_Handle *sock, + GNUNET_NAT_TestCallback cb, + void *cb_cls); + + +/** + * Cancel active STUN request. Frees associated resources + * and ensures that the callback is no longer invoked. + * + * @param rh request to cancel + */ +void +GNUNET_NAT_stun_make_request_cancel (struct GNUNET_NAT_STUN_Handle *rh); + + /** * Start testing if NAT traversal works using the given configuration * (IPv4-only). The transport adapters should be down while using diff --git a/src/nat/Makefile.am b/src/nat/Makefile.am index c3477930d..6e96c41ff 100644 --- a/src/nat/Makefile.am +++ b/src/nat/Makefile.am @@ -84,7 +84,9 @@ libgnunetnat_la_LDFLAGS = \ -version-info 1:1:1 libgnunetnatnew_la_SOURCES = \ - nat_api.c nat.h + nat_api.c \ + nat_api_stun.c nat_stun.h \ + nat.h libgnunetnatnew_la_LIBADD = \ $(top_builddir)/src/util/libgnunetutil.la \ $(GN_LIBINTL) @EXT_LIBS@ @@ -93,7 +95,8 @@ libgnunetnatnew_la_LDFLAGS = \ -version-info 2:0:0 gnunet_service_nat_SOURCES = \ - gnunet-service-nat.c + gnunet-service-nat.c \ + gnunet-service-nat_stun.c gnunet-service-nat_stun.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 f7dbbce3a..8499a9724 100644 --- a/src/nat/gnunet-service-nat.c +++ b/src/nat/gnunet-service-nat.c @@ -34,6 +34,7 @@ #include "gnunet_signatures.h" #include "gnunet_statistics_service.h" #include "gnunet_nat_service.h" +#include "gnunet-service-nat_stun.h" #include "nat.h" #include @@ -360,6 +361,7 @@ handle_stun (void *cls, const void *payload; size_t sa_len; size_t payload_size; + struct sockaddr_in external_addr; sa_len = ntohs (message->sender_addr_size); payload_size = ntohs (message->payload_size); @@ -386,7 +388,28 @@ handle_stun (void *cls, } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received HANDLE_STUN message from client\n"); - // FIXME: actually handle STUN request! + if (GNUNET_OK == + GNUNET_NAT_stun_handle_packet_ (payload, + payload_size, + &external_addr)) + { + /* FIXME: do something with "external_addr"! We + now know that a server at "sa" claims that + we are visible at IP "external_addr". + + We should (for some fixed period of time) tell + all of our clients that listen to a NAT'ed address + that they might want to consider the given 'external_ip' + as their public IP address (this includes TCP and UDP + clients, even if only UDP sends STUN requests). + + If we do not get a renewal, the "external_addr" should be + removed again. The timeout frequency should be configurable + (with a sane default), so that the UDP plugin can tell how + often to re-request STUN. + */ + + } GNUNET_SERVICE_client_continue (ch->client); } @@ -786,12 +809,14 @@ ifc_proc (void *cls, * of addresses this peer has. * * @param delta the entry in the list that changed + * @param ch client to contact * @param add #GNUNET_YES to add, #GNUNET_NO to remove * @param addr the address that changed * @param addr_len number of bytes in @a addr */ static void notify_client (struct LocalAddressList *delta, + struct ClientHandle *ch, int add, const void *addr, size_t addr_len) @@ -800,13 +825,13 @@ notify_client (struct LocalAddressList *delta, struct GNUNET_NAT_AddressChangeNotificationMessage *msg; env = GNUNET_MQ_msg_extra (msg, - alen, + addr_len, GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE); msg->add_remove = htonl (add); msg->addr_class = htonl (delta->ac); GNUNET_memcpy (&msg[1], addr, - alen); + addr_len); GNUNET_MQ_send (ch->mq, env); } @@ -849,6 +874,7 @@ notify_clients (struct LocalAddressList *delta, c4 = (const struct sockaddr_in *) ch->addrs[i]; v4.sin_port = c4->sin_port; notify_client (delta, + ch, add, &v4, alen); @@ -866,8 +892,9 @@ notify_clients (struct LocalAddressList *delta, if (AF_INET6 != ch->addrs[i]->sa_family) continue; /* IPv4 not relevant */ c6 = (const struct sockaddr_in6 *) ch->addrs[i]; - v6.sin_port = c6->sin_port; + v6.sin6_port = c6->sin6_port; notify_client (delta, + ch, add, &v6, alen); diff --git a/src/nat/gnunet-service-nat_stun.c b/src/nat/gnunet-service-nat_stun.c new file mode 100644 index 000000000..6f3ea10eb --- /dev/null +++ b/src/nat/gnunet-service-nat_stun.c @@ -0,0 +1,211 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 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 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. +*/ +/** + * This code provides some support for doing STUN transactions. We + * receive the simplest possible packet as the STUN server and try + * to respond properly. + * + * All STUN packets start with a simple header made of a type, + * length (excluding the header) and a 16-byte random transaction id. + * Following the header we may have zero or more attributes, each + * structured as a type, length and a value (whose format depends + * on the type, but often contains addresses). + * Of course all fields are in network format. + * + * This code was based on ministun.c. + * + * @file nat/gnunet-service-nat_stun.c + * @brief Functions for STUN functionality + * @author Bruno Souza Cabral + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "nat_stun.h" + +#define LOG(kind,...) GNUNET_log_from (kind, "stun", __VA_ARGS__) + + +/** + * Context for #stun_get_mapped(). + * Used to store state across processing attributes. + */ +struct StunState +{ + uint16_t attr; +}; + + +/** + * Extract the STUN_MAPPED_ADDRESS from the stun response. + * This is used as a callback for stun_handle_response + * when called from stun_request. + * + * @param[out] st pointer where we will set the type + * @param attr received stun attribute + * @param magic Magic cookie + * @param[out] arg pointer to a sockaddr_in where we will set the reported IP and port + * @return #GNUNET_OK if @a arg was initialized + */ +static int +stun_get_mapped (struct StunState *st, + const struct stun_attr *attr, + uint32_t magic, + struct sockaddr_in *arg) +{ + const struct stun_addr *returned_addr; + struct sockaddr_in *sa = (struct sockaddr_in *) arg; + uint16_t type = ntohs (attr->attr); + + switch (type) + { + case STUN_MAPPED_ADDRESS: + if ( (st->attr == STUN_XOR_MAPPED_ADDRESS) || + (st->attr == STUN_MS_XOR_MAPPED_ADDRESS) ) + return GNUNET_NO; + magic = 0; + break; + case STUN_MS_XOR_MAPPED_ADDRESS: + if (st->attr == STUN_XOR_MAPPED_ADDRESS) + return GNUNET_NO; + break; + case STUN_XOR_MAPPED_ADDRESS: + break; + default: + return GNUNET_NO; + } + + if (ntohs (attr->len) < sizeof (struct stun_addr)) + return GNUNET_NO; + returned_addr = (const struct stun_addr *)(attr + 1); + if (AF_INET != returned_addr->family) + return GNUNET_NO; + st->attr = type; + sa->sin_family = AF_INET; + sa->sin_port = returned_addr->port ^ htons (ntohl(magic) >> 16); + sa->sin_addr.s_addr = returned_addr->addr ^ magic; + return GNUNET_OK; +} + + +/** + * Handle an incoming STUN response. Do some basic sanity checks on + * packet size and content, try to extract information. + * At the moment this only processes BIND requests, + * and returns the externally visible address of the original + * request. + * + * @param data the packet + * @param len the length of the packet in @a data + * @param[out] arg sockaddr_in where we will set our discovered address + * @return #GNUNET_OK on success, + * #GNUNET_NO if the packet is invalid (not a stun packet) + */ +int +GNUNET_NAT_stun_handle_packet_ (const void *data, + size_t len, + struct sockaddr_in *arg) +{ + const struct stun_header *hdr; + const struct stun_attr *attr; + struct StunState st; + uint32_t advertised_message_size; + uint32_t message_magic_cookie; + int ret = GNUNET_SYSERR; + + /* On entry, 'len' is the length of the UDP payload. After the + * initial checks it becomes the size of unprocessed options, + * while 'data' is advanced accordingly. + */ + if (len < sizeof(struct stun_header)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Packet too short to be a STUN packet\n"); + return GNUNET_NO; + } + hdr = data; + /* Skip header as it is already in hdr */ + len -= sizeof(struct stun_header); + data += sizeof(struct stun_header); + + /* len as advertised in the message */ + advertised_message_size = ntohs (hdr->msglen); + message_magic_cookie = ntohl (hdr->magic); + /* Compare if the cookie match */ + if (STUN_MAGIC_COOKIE != message_magic_cookie) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Invalid magic cookie for STUN packet\n"); + return GNUNET_NO; + } + + LOG (GNUNET_ERROR_TYPE_INFO, + "STUN Packet, msg %s (%04x), length: %d\n", + stun_msg2str (ntohs (hdr->msgtype)), + ntohs (hdr->msgtype), + advertised_message_size); + if (advertised_message_size > len) + { + LOG (GNUNET_ERROR_TYPE_INFO, + "Scrambled STUN packet length (got %d, expecting %d)\n", + advertised_message_size, + (int) len); + return GNUNET_NO; + } + len = advertised_message_size; + memset (&st, 0, sizeof(st)); + + while (len > 0) + { + if (len < sizeof (struct stun_attr)) + { + LOG (GNUNET_ERROR_TYPE_INFO, + "Attribute too short (got %d, expecting %d)\n", + (int) len, + (int) sizeof (struct stun_attr)); + break; + } + attr = (const struct stun_attr *) data; + + /* compute total attribute length */ + advertised_message_size = ntohs (attr->len) + sizeof (struct stun_attr); + + /* Check if we still have space in our buffer */ + if (advertised_message_size > len) + { + LOG (GNUNET_ERROR_TYPE_INFO, + "Inconsistent attribute (length %d exceeds remaining msg len %d)\n", + advertised_message_size, + (int) len); + break; + } + if (GNUNET_OK == + stun_get_mapped (&st, + attr, + hdr->magic, + arg)) + ret = GNUNET_OK; + data += advertised_message_size; + len -= advertised_message_size; + } + return ret; +} + +/* end of gnunet-service-nat_stun.c */ diff --git a/src/nat/gnunet-service-nat_stun.h b/src/nat/gnunet-service-nat_stun.h new file mode 100644 index 000000000..3d7e18f53 --- /dev/null +++ b/src/nat/gnunet-service-nat_stun.h @@ -0,0 +1,61 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 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 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. +*/ +/** + * This code provides some support for doing STUN transactions. We + * receive the simplest possible packet as the STUN server and try + * to respond properly. + * + * All STUN packets start with a simple header made of a type, + * length (excluding the header) and a 16-byte random transaction id. + * Following the header we may have zero or more attributes, each + * structured as a type, length and a value (whose format depends + * on the type, but often contains addresses). + * Of course all fields are in network format. + * + * This code was based on ministun.c. + * + * @file nat/gnunet-service-nat_stun.h + * @brief Functions for STUN functionality + * @author Bruno Souza Cabral + */ +#ifndef GNUNET_SERVICE_NAT_STUN_H +#define GNUNET_SERVICE_NAT_STUN_H + +#include "platform.h" + +/** + * Handle an incoming STUN response. Do some basic sanity checks on + * packet size and content, try to extract information. + * At the moment this only processes BIND requests, + * and returns the externally visible address of the original + * request. + * + * @param data the packet + * @param len the length of the packet in @a data + * @param[out] arg sockaddr_in where we will set our discovered address + * @return #GNUNET_OK on success, + * #GNUNET_NO if the packet is invalid (not a stun packet) + */ +int +GNUNET_NAT_stun_handle_packet_ (const void *data, + size_t len, + struct sockaddr_in *arg); + +#endif diff --git a/src/nat/nat_api.c b/src/nat/nat_api.c index 421befab3..4c324376b 100644 --- a/src/nat/nat_api.c +++ b/src/nat/nat_api.c @@ -544,15 +544,15 @@ test_stun_packet (const void *data, * Handle an incoming STUN message. This function is useful as * some GNUnet service may be listening on a UDP port and might * thus receive STUN messages while trying to receive other data. - * In this case, this function can be used to act as a proper - * STUN server (if desired). + * In this case, this function can be used to process replies + * to STUN requests. * * The function does some basic sanity checks on packet size and - * content, try to extract a bit of information, and possibly replies - * if this is an actual STUN message. + * content, try to extract a bit of information. * * At the moment this only processes BIND requests, and returns the - * externally visible address of the request. + * externally visible address of the request to the rest of the + * NAT logic. * * @param nh handle to the NAT service * @param sender_addr address from which we got @a data diff --git a/src/nat/nat_api_stun.c b/src/nat/nat_api_stun.c new file mode 100644 index 000000000..0d4ba86d1 --- /dev/null +++ b/src/nat/nat_api_stun.c @@ -0,0 +1,261 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 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 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. +*/ +/** + * This code provides some support for doing STUN transactions. + * We send simplest possible packet ia REQUEST with BIND to a STUN server. + * + * All STUN packets start with a simple header made of a type, + * length (excluding the header) and a 16-byte random transaction id. + * Following the header we may have zero or more attributes, each + * structured as a type, length and a value (whose format depends + * on the type, but often contains addresses). + * Of course all fields are in network format. + * + * This code was based on ministun.c. + * + * @file nat/nat_api_stun.c + * @brief Functions for STUN functionality + * @author Bruno Souza Cabral + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_resolver_service.h" +#include "gnunet_nat_lib.h" + + +#include "nat_stun.h" + +#define LOG(kind,...) GNUNET_log_from (kind, "stun", __VA_ARGS__) + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) + + +/** + * Handle to a request given to the resolver. Can be used to cancel + * the request prior to the timeout or successful execution. Also + * used to track our internal state for the request. + */ +struct GNUNET_NAT_STUN_Handle +{ + + /** + * Handle to a pending DNS lookup request. + */ + struct GNUNET_RESOLVER_RequestHandle *dns_active; + + /** + * Handle to the listen socket + */ + struct GNUNET_NETWORK_Handle *sock; + + /** + * Stun server address + */ + char *stun_server; + + /** + * Function to call when a error occours + */ + GNUNET_NAT_STUN_ErrorCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Do we got a DNS resolution successfully? + */ + int dns_success; + + /** + * STUN port + */ + uint16_t stun_port; + +}; + + +/** + * Encode a class and method to a compatible STUN format + * + * @param msg_class class to be converted + * @param method method to be converted + * @return message in a STUN compatible format + */ +static int +encode_message (enum StunClasses msg_class, + enum StunMethods method) +{ + return ((msg_class & 1) << 4) | ((msg_class & 2) << 7) | + (method & 0x000f) | ((method & 0x0070) << 1) | ((method & 0x0f800) << 2); +} + + +/** + * Fill the stun_header with a random request_id + * + * @param req, stun header to be filled + */ +static void +generate_request_id (struct stun_header *req) +{ + req->magic = htonl(STUN_MAGIC_COOKIE); + for (unsigned int x = 0; x < 3; x++) + req->id.id[x] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, + UINT32_MAX); +} + + +/** + * Try to establish a connection given the specified address. + * + * @param cls our `struct GNUNET_NAT_STUN_Handle *` + * @param addr address to try, NULL for "last call" + * @param addrlen length of @a addr + */ +static void +stun_dns_callback (void *cls, + const struct sockaddr *addr, + socklen_t addrlen) +{ + struct GNUNET_NAT_STUN_Handle *rh = cls; + struct stun_header req; + struct sockaddr_in server; + + if (NULL == addr) + { + rh->dns_active = NULL; + if (GNUNET_NO == rh->dns_success) + { + LOG (GNUNET_ERROR_TYPE_INFO, + "Error resolving host %s\n", + rh->stun_server); + rh->cb (rh->cb_cls, + GNUNET_NAT_ERROR_NOT_ONLINE); + } + else if (GNUNET_SYSERR == rh->dns_success) + { + rh->cb (rh->cb_cls, + GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR); + } + else + { + rh->cb (rh->cb_cls, + GNUNET_NAT_ERROR_SUCCESS); + } + GNUNET_NAT_stun_make_request_cancel (rh); + return; + } + + rh->dns_success = GNUNET_YES; + memset (&server, 0, sizeof(server)); + server.sin_family = AF_INET; + server.sin_addr = ((struct sockaddr_in *)addr)->sin_addr; + server.sin_port = htons (rh->stun_port); +#if HAVE_SOCKADDR_IN_SIN_LEN + server.sin_len = (u_char) sizeof (struct sockaddr_in); +#endif + + /* Craft the simplest possible STUN packet. A request binding */ + generate_request_id (&req); + req.msglen = htons (0); + req.msgtype = htons (encode_message (STUN_REQUEST, + STUN_BINDING)); + + /* Send the packet */ + if (-1 == + GNUNET_NETWORK_socket_sendto (rh->sock, + &req, + sizeof (req), + (const struct sockaddr *) &server, + sizeof (server))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "sendto"); + rh->dns_success = GNUNET_SYSERR; + return; + } +} + + +/** + * Make Generic STUN request. Sends a generic stun request to the + * server specified using the specified socket, possibly waiting for + * a reply and filling the 'reply' field with the externally visible + * address. + * + * @param server the address of the stun server + * @param port port of the stun server, in host byte order + * @param sock the socket used to send the request + * @param cb callback in case of error + * @param cb_cls closure for @a cb + * @return NULL on error + */ +struct GNUNET_NAT_STUN_Handle * +GNUNET_NAT_stun_make_request (const char *server, + uint16_t port, + struct GNUNET_NETWORK_Handle *sock, + GNUNET_NAT_STUN_ErrorCallback cb, + void *cb_cls) +{ + struct GNUNET_NAT_STUN_Handle *rh; + + rh = GNUNET_new (struct GNUNET_NAT_STUN_Handle); + rh->sock = sock; + rh->cb = cb; + rh->cb_cls = cb_cls; + rh->stun_server = GNUNET_strdup (server); + rh->stun_port = port; + rh->dns_success = GNUNET_NO; + rh->dns_active = GNUNET_RESOLVER_ip_get (rh->stun_server, + AF_INET, + TIMEOUT, + &stun_dns_callback, rh); + if (NULL == rh->dns_active) + { + GNUNET_NAT_stun_make_request_cancel (rh); + return NULL; + } + return rh; +} + + +/** + * Cancel active STUN request. Frees associated resources + * and ensures that the callback is no longer invoked. + * + * @param rh request to cancel + */ +void +GNUNET_NAT_stun_make_request_cancel (struct GNUNET_NAT_STUN_Handle *rh) +{ + if (NULL != rh->dns_active) + { + GNUNET_RESOLVER_request_cancel (rh->dns_active); + rh->dns_active = NULL; + } + GNUNET_free (rh->stun_server); + GNUNET_free (rh); +} + + +/* end of nat_stun.c */ diff --git a/src/nat/nat_stun.c b/src/nat/nat_stun.c index b914abb2e..62916ab84 100644 --- a/src/nat/nat_stun.c +++ b/src/nat/nat_stun.c @@ -103,32 +103,6 @@ struct StunState }; -/** - * Convert a message to a StunClass - * - * @param msg the received message - * @return the converted StunClass - */ -static int -decode_class(int msg) -{ - /* Sorry for the magic, but this maps the class according to rfc5245 */ - return ((msg & 0x0010) >> 4) | ((msg & 0x0100) >> 7); -} - -/** - * Convert a message to a StunMethod - * - * @param msg the received message - * @return the converted StunMethod - */ -static int -decode_method(int msg) -{ - return (msg & 0x000f) | ((msg & 0x00e0) >> 1) | ((msg & 0x3e00) >> 2); -} - - /** * Encode a class and method to a compatible STUN format * @@ -145,106 +119,6 @@ encode_message (enum StunClasses msg_class, } -/** - * Print a class and method from a STUN message - * - * @param msg - * @return string with the message class and method - */ -static const char * -stun_msg2str(int msg) -{ - static const struct { - enum StunClasses value; - const char *name; - } classes[] = { - { STUN_REQUEST, "Request" }, - { STUN_INDICATION, "Indication" }, - { STUN_RESPONSE, "Response" }, - { STUN_ERROR_RESPONSE, "Error Response" }, - { 0, NULL } - }; - static const struct { - enum StunMethods value; - const char *name; - } methods[] = { - { STUN_BINDING, "Binding" }, - { 0, NULL } - }; - static char result[32]; - const char *msg_class = NULL; - const char *method = NULL; - int i; - int value; - - value = decode_class(msg); - for (i = 0; classes[i].name; i++) - { - msg_class = classes[i].name; - if (classes[i].value == value) - break; - } - value = decode_method(msg); - for (i = 0; methods[i].name; i++) - { - method = methods[i].name; - if (methods[i].value == value) - break; - } - GNUNET_snprintf (result, - sizeof(result), - "%s %s", - method ? : "Unknown Method", - msg_class ? : "Unknown Class Message"); - return result; -} - - -/** - * Print attribute name - * - * @param msg with a attribute type - * @return string with the attribute name - */ -static const char * -stun_attr2str (int msg) -{ - static const struct { - enum StunAttributes value; - const char *name; - } attrs[] = { - { STUN_MAPPED_ADDRESS, "Mapped Address" }, - { STUN_RESPONSE_ADDRESS, "Response Address" }, - { STUN_CHANGE_ADDRESS, "Change Address" }, - { STUN_SOURCE_ADDRESS, "Source Address" }, - { STUN_CHANGED_ADDRESS, "Changed Address" }, - { STUN_USERNAME, "Username" }, - { STUN_PASSWORD, "Password" }, - { STUN_MESSAGE_INTEGRITY, "Message Integrity" }, - { STUN_ERROR_CODE, "Error Code" }, - { STUN_UNKNOWN_ATTRIBUTES, "Unknown Attributes" }, - { STUN_REFLECTED_FROM, "Reflected From" }, - { STUN_REALM, "Realm" }, - { STUN_NONCE, "Nonce" }, - { STUN_XOR_MAPPED_ADDRESS, "XOR Mapped Address" }, - { STUN_MS_VERSION, "MS Version" }, - { STUN_MS_XOR_MAPPED_ADDRESS, "MS XOR Mapped Address" }, - { STUN_SOFTWARE, "Software" }, - { STUN_ALTERNATE_SERVER, "Alternate Server" }, - { STUN_FINGERPRINT, "Fingerprint" }, - { 0, NULL } - }; - unsigned int i; - - for (i = 0; attrs[i].name; i++) - { - if (attrs[i].value == msg) - return attrs[i].name; - } - return "Unknown Attribute"; -} - - /** * Fill the stun_header with a random request_id * diff --git a/src/nat/nat_stun.h b/src/nat/nat_stun.h index f8d99b164..4c6c178fb 100644 --- a/src/nat/nat_stun.h +++ b/src/nat/nat_stun.h @@ -17,15 +17,14 @@ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - /** - * Testcase for STUN server resolution + * Message types for STUN server resolution * * @file nat/nat_stun.h * @brief Testcase for STUN library * @author Bruno Souza Cabral * @autor Mark Spencer (Original code borrowed from Asterisk) - * + * @author Christian Grothoff */ @@ -34,7 +33,10 @@ #define STUN_MAGIC_COOKIE 0x2112A442 -typedef struct { uint32_t id[3]; } GNUNET_PACKED stun_trans_id; +typedef struct { + uint32_t id[3]; +} GNUNET_PACKED stun_trans_id; + struct stun_header { @@ -44,32 +46,47 @@ struct stun_header stun_trans_id id; } GNUNET_PACKED; + struct stun_attr { uint16_t attr; uint16_t len; } GNUNET_PACKED; -/* + +/** * The format normally used for addresses carried by STUN messages. */ struct stun_addr { uint8_t unused; + + /** + * Address family, we expect AF_INET. + */ uint8_t family; + + /** + * Port number. + */ uint16_t port; + + /** + * IPv4 address. Should this be "struct in_addr"? + */ uint32_t addr; } GNUNET_PACKED; - -/* STUN message classes */ +/** + * STUN message classes + */ enum StunClasses { - INVALID_CLASS = 0, - STUN_REQUEST = 0x0000, - STUN_INDICATION = 0x0001, - STUN_RESPONSE = 0x0002, - STUN_ERROR_RESPONSE = 0x0003 + INVALID_CLASS = 0, + STUN_REQUEST = 0x0000, + STUN_INDICATION = 0x0001, + STUN_RESPONSE = 0x0002, + STUN_ERROR_RESPONSE = 0x0003 }; enum StunMethods { @@ -84,7 +101,9 @@ enum StunMethods { STUN_CHANNEL_BIND = 0x0009 }; -/* Basic attribute types in stun messages. + +/** + * Basic attribute types in stun messages. * Messages can also contain custom attributes (codes above 0x7fff) */ enum StunAttributes { @@ -108,3 +127,128 @@ enum StunAttributes { STUN_ALTERNATE_SERVER = 0x8023, STUN_FINGERPRINT = 0x8028 }; + + +/** + * Convert a message to a StunClass + * + * @param msg the received message + * @return the converted StunClass + */ +static int +decode_class(int msg) +{ + /* Sorry for the magic, but this maps the class according to rfc5245 */ + return ((msg & 0x0010) >> 4) | ((msg & 0x0100) >> 7); +} + +/** + * Convert a message to a StunMethod + * + * @param msg the received message + * @return the converted StunMethod + */ +static int +decode_method(int msg) +{ + return (msg & 0x000f) | ((msg & 0x00e0) >> 1) | ((msg & 0x3e00) >> 2); +} + + +/** + * Print a class and method from a STUN message + * + * @param msg + * @return string with the message class and method + */ +static const char * +stun_msg2str (int msg) +{ + static const struct { + enum StunClasses value; + const char *name; + } classes[] = { + { STUN_REQUEST, "Request" }, + { STUN_INDICATION, "Indication" }, + { STUN_RESPONSE, "Response" }, + { STUN_ERROR_RESPONSE, "Error Response" }, + { 0, NULL } + }; + static const struct { + enum StunMethods value; + const char *name; + } methods[] = { + { STUN_BINDING, "Binding" }, + { 0, NULL } + }; + static char result[64]; + const char *msg_class = NULL; + const char *method = NULL; + int value; + + value = decode_class (msg); + for (unsigned int i = 0; classes[i].name; i++) + if (classes[i].value == value) + { + msg_class = classes[i].name; + break; + } + value = decode_method (msg); + for (unsigned int i = 0; methods[i].name; i++) + if (methods[i].value == value) + { + method = methods[i].name; + break; + } + GNUNET_snprintf (result, + sizeof(result), + "%s %s", + method ? : "Unknown Method", + msg_class ? : "Unknown Class Message"); + return result; +} + + +/** + * Print attribute name + * + * @param msg with a attribute type + * @return string with the attribute name + */ +static const char * +stun_attr2str (enum StunAttributes msg) +{ + static const struct { + enum StunAttributes value; + const char *name; + } attrs[] = { + { STUN_MAPPED_ADDRESS, "Mapped Address" }, + { STUN_RESPONSE_ADDRESS, "Response Address" }, + { STUN_CHANGE_ADDRESS, "Change Address" }, + { STUN_SOURCE_ADDRESS, "Source Address" }, + { STUN_CHANGED_ADDRESS, "Changed Address" }, + { STUN_USERNAME, "Username" }, + { STUN_PASSWORD, "Password" }, + { STUN_MESSAGE_INTEGRITY, "Message Integrity" }, + { STUN_ERROR_CODE, "Error Code" }, + { STUN_UNKNOWN_ATTRIBUTES, "Unknown Attributes" }, + { STUN_REFLECTED_FROM, "Reflected From" }, + { STUN_REALM, "Realm" }, + { STUN_NONCE, "Nonce" }, + { STUN_XOR_MAPPED_ADDRESS, "XOR Mapped Address" }, + { STUN_MS_VERSION, "MS Version" }, + { STUN_MS_XOR_MAPPED_ADDRESS, "MS XOR Mapped Address" }, + { STUN_SOFTWARE, "Software" }, + { STUN_ALTERNATE_SERVER, "Alternate Server" }, + { STUN_FINGERPRINT, "Fingerprint" }, + { 0, NULL } + }; + + for (unsigned int i = 0; attrs[i].name; i++) + if (attrs[i].value == msg) + return attrs[i].name; + return "Unknown Attribute"; +} + + +/* end of nat_stun.h */ -- 2.25.1