towards moving STUN logic into new NAT service
authorChristian Grothoff <christian@grothoff.org>
Wed, 30 Nov 2016 06:31:09 +0000 (07:31 +0100)
committerChristian Grothoff <christian@grothoff.org>
Wed, 30 Nov 2016 06:31:09 +0000 (07:31 +0100)
src/include/gnunet_nat_service.h
src/nat/Makefile.am
src/nat/gnunet-service-nat.c
src/nat/gnunet-service-nat_stun.c [new file with mode: 0644]
src/nat/gnunet-service-nat_stun.h [new file with mode: 0644]
src/nat/nat_api.c
src/nat/nat_api_stun.c [new file with mode: 0644]
src/nat/nat_stun.c
src/nat/nat_stun.h

index a590695452fa017a9b74cfdec8dd75895eee2770..378f5a8cbd6b8978b21313862943cb9f17617972 100644 (file)
@@ -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
index c3477930d9d71ca072c330d868329f0e480b6593..6e96c41ff8f693c6dcef4ef5b6c9f728c8397371 100644 (file)
@@ -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 \
index f7dbbce3acf16e6489554959d97bf4327797c169..8499a97243f7fd97c1e9d25aa22498df9188ebc7 100644 (file)
@@ -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 <gcrypt.h>
 
@@ -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 (file)
index 0000000..6f3ea10
--- /dev/null
@@ -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 (file)
index 0000000..3d7e18f
--- /dev/null
@@ -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
index 421befab39cf349c5969bde2611281ce1183e15e..4c324376b17e35a2e640e4d01b539839da2dab10 100644 (file)
@@ -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 (file)
index 0000000..0d4ba86
--- /dev/null
@@ -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 */
index b914abb2e615e2759e0c8eb9b3374d36622cca7c..62916ab84df2591a3fffcc0045b1bb81e0dc6583 100644 (file)
@@ -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
  *
index f8d99b1642e2622d5973149349067e64f81d94ab..4c6c178fba1d36fd57ce22c1ad79fbcda827961e 100644 (file)
      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
  */
 
 
 
 #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 */