indent
[oweals/gnunet.git] / src / transport / plugin_transport_udp.c
index 52e59818901d4974d95a9ef0eb248f5d7849e763..582d083dcf1c3f1892af975af822f4688caebbb1 100644 (file)
@@ -4,7 +4,7 @@
 
      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 2, or (at your
+     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
@@ -40,6 +40,7 @@
 #include "platform.h"
 #include "gnunet_hello_lib.h"
 #include "gnunet_connection_lib.h"
+#include "gnunet_container_lib.h"
 #include "gnunet_os_lib.h"
 #include "gnunet_peerinfo_service.h"
 #include "gnunet_protocols.h"
@@ -98,12 +99,12 @@ struct IPv4UdpAddress
   /**
    * IPv4 address, in network byte order.
    */
-  uint32_t ipv4_addr;
+  uint32_t ipv4_addr GNUNET_PACKED;
 
   /**
    * Port number, in network byte order.
    */
-  uint16_t u_port;
+  uint16_t u_port GNUNET_PACKED;
 };
 
 
@@ -115,12 +116,12 @@ struct IPv6UdpAddress
   /**
    * IPv6 address.
    */
-  unsigned char ipv6_addr[16];
+  struct in6_addr ipv6_addr GNUNET_PACKED;
 
   /**
    * Port number, in network byte order.
    */
-  uint16_t u6_port;
+  uint16_t u6_port GNUNET_PACKED;
 };
 
 /* Forward definition */
@@ -210,6 +211,30 @@ struct UDP_NAT_ProbeMessageConfirmation
 };
 
 
+/**
+ * Local network addresses (actual IP address follows this struct).
+ * PORT is NOT included!
+ */
+struct LocalAddrList
+{
+  
+  /**
+   * This is a doubly linked list.
+   */
+  struct LocalAddrList *next;
+
+  /**
+   * This is a doubly linked list.
+   */
+  struct LocalAddrList *prev;
+
+  /**
+   * Number of bytes of the address that follow
+   */
+  size_t size;
+
+};
+
 
 /**
  * UDP NAT "Session"
@@ -354,17 +379,27 @@ struct Plugin
    */
   char *internal_address;
 
-  /*
+  /**
+   * List of our IP addresses.
+   */
+  struct LocalAddrList *lal_head;
+  
+  /**
+   * Tail of our IP address list.
+   */ 
+  struct LocalAddrList *lal_tail;
+
+  /**
    * FD Read set
    */
   struct GNUNET_NETWORK_FDSet *rs;
 
-  /*
+  /**
    * stdout pipe handle for the gnunet-nat-server process
    */
   struct GNUNET_DISK_PipeHandle *server_stdout;
 
-  /*
+  /**
    * stdout file handle (for reading) for the gnunet-nat-server process
    */
   const struct GNUNET_DISK_FileHandle *server_stdout_handle;
@@ -796,6 +831,49 @@ udp_plugin_send (void *cls,
 }
 
 
+static void
+add_to_address_list (struct Plugin *plugin,
+                    const void *arg,
+                    size_t arg_size)
+{
+  struct LocalAddrList *lal;
+
+  lal = plugin->lal_head;
+  while (NULL != lal)
+    {
+      if ( (lal->size == arg_size) &&
+          (0 == memcmp (&lal[1], arg, arg_size)) )
+       return;
+      lal = lal->next;
+    }
+  lal = GNUNET_malloc (sizeof (struct LocalAddrList) + arg_size);
+  lal->size = arg_size;
+  memcpy (&lal[1], arg, arg_size);
+  GNUNET_CONTAINER_DLL_insert (plugin->lal_head,
+                              plugin->lal_tail,
+                              lal);
+}
+
+
+static int
+check_local_addr (struct Plugin *plugin,
+                 const void *arg,
+                 size_t arg_size)
+{
+  struct LocalAddrList *lal;
+
+  lal = plugin->lal_head;
+  while (NULL != lal)
+    {
+      if ( (lal->size == arg_size) &&
+          (0 == memcmp (&lal[1], arg, arg_size)) )
+       return GNUNET_OK;
+      lal = lal->next;
+    }
+  return GNUNET_SYSERR;
+}
+
+
 /**
  * Add the IP of our network interface to the list of
  * our external IP addresses.
@@ -819,6 +897,7 @@ process_interfaces (void *cls,
   if (af == AF_INET)
     {
       t4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
+      add_to_address_list (plugin, &t4.ipv4_addr, sizeof (uint32_t));
       if ((plugin->behind_nat == GNUNET_YES) && (plugin->only_nat_addresses == GNUNET_YES))
         {
           t4.u_port = htons (DEFAULT_NAT_PORT);
@@ -839,7 +918,6 @@ process_interfaces (void *cls,
     }
   else if (af == AF_INET6)
     {
-
       if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr))
         {
           /* skip link local addresses */
@@ -848,6 +926,7 @@ process_interfaces (void *cls,
       memcpy (&t6.ipv6_addr,
               &((struct sockaddr_in6 *) addr)->sin6_addr,
               sizeof (struct in6_addr));
+      add_to_address_list (plugin, &t6.ipv6_addr, sizeof (struct in6_addr));
       if ((plugin->behind_nat == GNUNET_YES) && (plugin->only_nat_addresses == GNUNET_YES))
         {
           t6.u6_port = htons (0);
@@ -872,28 +951,27 @@ process_interfaces (void *cls,
       GNUNET_break (0);
       return GNUNET_OK;
     }
-
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO |
-                     GNUNET_ERROR_TYPE_BULK,
-                       _("Found address `%s' (%s)\n"),
-                      GNUNET_a2s (addr, addrlen), name);
-
-    if (addr_nat != NULL)
-      {
-        plugin->env->notify_address (plugin->env->cls,
-                                    "udp",
-                                    addr_nat, args, GNUNET_TIME_UNIT_FOREVER_REL);
-        GNUNET_log (GNUNET_ERROR_TYPE_INFO |
-                         GNUNET_ERROR_TYPE_BULK,
-                          _("Found NAT address `%s' (%s)\n"),
-                         GNUNET_a2s (addr_nat, args), name);
-        GNUNET_free(addr_nat);
-      }
-
-    plugin->env->notify_address (plugin->env->cls,
-                                "udp",
-                                arg, args, GNUNET_TIME_UNIT_FOREVER_REL);
-
+  
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO |
+             GNUNET_ERROR_TYPE_BULK,
+             _("Found address `%s' (%s)\n"),
+             GNUNET_a2s (addr, addrlen), name);
+  
+  if (addr_nat != NULL)
+    {
+      plugin->env->notify_address (plugin->env->cls,
+                                  "udp",
+                                  addr_nat, args, GNUNET_TIME_UNIT_FOREVER_REL);
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO |
+                 GNUNET_ERROR_TYPE_BULK,
+                 _("Found NAT address `%s' (%s)\n"),
+                 GNUNET_a2s (addr_nat, args), name);
+      GNUNET_free(addr_nat);
+    }
+  
+  plugin->env->notify_address (plugin->env->cls,
+                              "udp",
+                              arg, args, GNUNET_TIME_UNIT_FOREVER_REL);
   return GNUNET_OK;
 }
 
@@ -1328,7 +1406,7 @@ udp_demultiplexer(struct Plugin *plugin, struct GNUNET_PeerIdentity *sender,
     default:
 #if DEBUG_UDP
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                       _("Sending message type %d to transport\n!"), ntohs(currhdr->type));
+                       _("Sending message type %d to transport!\n"), ntohs(currhdr->type));
 #endif
       plugin->env->receive (plugin->env->cls, sender, currhdr, UDP_DIRECT_DISTANCE, 
                            NULL, sender_addr, fromlen);
@@ -1538,9 +1616,10 @@ udp_transport_server_start (void *cls)
           {
             serverAddrv4.sin_port = htons (GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_STRONG, 33537) + 32000); /* Find a good, non-root port */
 #if DEBUG_UDP
-        GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
-                        "udp",
-                        "Binding failed, trying new port %d\n", ntohs(serverAddrv4.sin_port));
+           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
+                            "udp",
+                            "Binding failed, trying new port %d\n", 
+                            ntohs(serverAddrv4.sin_port));
 #endif
           }
         udp_sock.port = ntohs(serverAddrv4.sin_port);
@@ -1586,52 +1665,112 @@ udp_transport_server_start (void *cls)
 }
 
 
+
 /**
- * Another peer has suggested an address for this peer and transport
- * plugin.  Check that this could be a valid address.  This function
- * is not expected to 'validate' the address in the sense of trying to
- * connect to it but simply to see if the binary format is technically
- * legal for establishing a connection.
+ * Check if the given port is plausible (must be either
+ * our listen port or our advertised port).  If it is
+ * neither, we return GNUNET_SYSERR.
+ *
+ * @param plugin global variables
+ * @param in_port port number to check
+ * @return GNUNET_OK if port is either open_port or adv_port
+ */
+static int
+check_port (struct Plugin *plugin, uint16_t in_port)
+{
+  if ( (plugin->behind_nat == GNUNET_YES) && (in_port == 0) )
+    return GNUNET_OK;
+  if ( (plugin->only_nat_addresses == GNUNET_YES) &&
+       (plugin->behind_nat == GNUNET_YES) )
+    return GNUNET_SYSERR; /* odd case... */
+  if (in_port == plugin->port) 
+    return GNUNET_OK;
+  return GNUNET_SYSERR;
+}
+
+
+/**
+ * Function that will be called to check if a binary address for this
+ * plugin is well-formed and corresponds to an address for THIS peer
+ * (as per our configuration).  Naturally, if absolutely necessary,
+ * plugins can be a bit conservative in their answer, but in general
+ * plugins should make sure that the address does not redirect
+ * traffic to a 3rd party that might try to man-in-the-middle our
+ * traffic.
  *
  * @param cls closure, should be our handle to the Plugin
- * @param addr pointer to the address, may be modified (slightly)
+ * @param addr pointer to the address
  * @param addrlen length of addr
  * @return GNUNET_OK if this is a plausible address for this peer
  *         and transport, GNUNET_SYSERR if not
  *
  */
 static int
-udp_check_address (void *cls, void *addr, size_t addrlen)
+udp_check_address (void *cls, 
+                  const void *addr, 
+                  size_t addrlen)
 {
   struct Plugin *plugin = cls;
-  char buf[sizeof (struct sockaddr_in6)];
-
-  struct sockaddr_in *v4;
-  struct sockaddr_in6 *v6;
+  char buf[INET6_ADDRSTRLEN];
+  const void *sb;
+  struct in_addr a4;
+  struct in6_addr a6;
+  int af;
+  uint16_t port;
+  struct IPv4UdpAddress *v4;
+  struct IPv6UdpAddress *v6;
 
-  if ((addrlen != sizeof (struct sockaddr_in)) &&
-      (addrlen != sizeof (struct sockaddr_in6)))
+  if ((addrlen != sizeof (struct IPv4UdpAddress)) &&
+      (addrlen != sizeof (struct IPv6UdpAddress)))
     {
       GNUNET_break_op (0);
       return GNUNET_SYSERR;
     }
-  memcpy (buf, addr, sizeof (struct sockaddr_in6));
-  if (addrlen == sizeof (struct sockaddr_in))
+
+  if (addrlen == sizeof (struct IPv4UdpAddress))
     {
-      v4 = (struct sockaddr_in *) buf;
-      v4->sin_port = htons (plugin->port);
+      v4 = (struct IPv4UdpAddress *) addr;
+      if (GNUNET_OK !=
+         check_port (plugin, ntohs (v4->u_port)))
+       return GNUNET_SYSERR;
+      if (GNUNET_OK !=
+         check_local_addr (plugin, &v4->ipv4_addr, sizeof (uint32_t)))
+       return GNUNET_SYSERR;
+
+      af = AF_INET;
+      port = ntohs (v4->u_port);
+      memcpy (&a4, &v4->ipv4_addr, sizeof (a4));
+      sb = &a4;
     }
   else
     {
-      v6 = (struct sockaddr_in6 *) buf;
-      v6->sin6_port = htons (plugin->port);
+      v6 = (struct IPv6UdpAddress *) addr;
+      if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr))
+       {
+         GNUNET_break_op (0);
+         return GNUNET_SYSERR;
+       }
+      if (GNUNET_OK != 
+         check_port (plugin, ntohs (v6->u6_port)))
+       return GNUNET_SYSERR;
+      if (GNUNET_OK !=
+         check_local_addr (plugin, &v6->ipv6_addr, sizeof (struct in6_addr)))
+       return GNUNET_SYSERR;
+
+      af = AF_INET6;
+      port = ntohs (v6->u6_port);
+      memcpy (&a6, &v6->ipv6_addr, sizeof (a6));
+      sb = &a6;
     }
 
+  inet_ntop (af, sb, buf, INET6_ADDRSTRLEN);
+
 #if DEBUG_UDP
   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
                    "udp",
-                   "Informing transport service about my address `%s'.\n",
-                   GNUNET_a2s (addr, addrlen));
+                   "Informing transport service about my address `%s:%u'\n",
+                   buf,
+                   port);
 #endif
   return GNUNET_OK;
 }
@@ -2039,6 +2178,7 @@ libgnunet_plugin_transport_udp_done (void *cls)
 {
   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
   struct Plugin *plugin = api->cls;
+  struct LocalAddrList *lal;
 
   udp_transport_server_stop (plugin);
   if (NULL != plugin->hostname_dns)
@@ -2050,6 +2190,13 @@ libgnunet_plugin_transport_udp_done (void *cls)
   GNUNET_SERVICE_stop (plugin->service);
 
   GNUNET_NETWORK_fdset_destroy (plugin->rs);
+  while (NULL != (lal = plugin->lal_head))
+    {
+      GNUNET_CONTAINER_DLL_remove (plugin->lal_head,
+                                  plugin->lal_tail,
+                                  lal);
+      GNUNET_free (lal);
+    }
   GNUNET_free (plugin);
   GNUNET_free (api);
   return NULL;