wip
[oweals/gnunet.git] / src / transport / plugin_transport_tcp.c
index 134bf50400e2bc1412be64e30252a29920df29ff..9dc67dcdf4eb098305c64658889e0fa9a2493da6 100644 (file)
@@ -453,6 +453,11 @@ struct Plugin
    */
   struct TCPProbeContext *probe_tail;
 
+  /**
+   * Handle for (DYN)DNS lookup of our external IP.
+   */
+  struct GNUNET_RESOLVER_RequestHandle *ext_dns;
+
   /**
    * ID of task used to update our addresses when one expires.
    */
@@ -474,15 +479,25 @@ struct Plugin
    */
   int behind_nat;
 
+  /**
+   * Has the NAT been punched?
+   */
+  int nat_punched;
+
   /**
    * Is this transport configured to allow connections to NAT'd peers?
    */
-  int allow_nat;
+  int enable_nat_client;
+
+  /**
+   * Should we run the gnunet-nat-server?
+   */
+  int enable_nat_server;
 
   /**
    * Are we allowed to try UPnP/PMP for NAT traversal?
    */
-  int allow_upnp;
+  int enable_upnp;
 
 };
 
@@ -609,7 +624,7 @@ add_to_address_list (struct Plugin *plugin,
       v6.sin6_port = htons (plugin->open_port);
       memcpy (&v6.sin6_addr, arg, arg_size);
 #if HAVE_SOCKADDR_IN_SIN_LEN
-      v6.sin_len = sizeof (struct sockaddr_in6);
+      v6.sin6_len = sizeof (struct sockaddr_in6);
 #endif
       sa = (const struct sockaddr*) &v6;
       salen = sizeof (v6);
@@ -619,7 +634,8 @@ add_to_address_list (struct Plugin *plugin,
       GNUNET_break (0);
       return;
     }
-  if (plugin->allow_upnp)
+  if ( (plugin->behind_nat == GNUNET_YES) &&
+       (plugin->enable_upnp == GNUNET_YES) )
     lal->nat = GNUNET_NAT_register (sa, salen,
                                    &nat_port_map_callback,
                                    lal);
@@ -1083,7 +1099,7 @@ disconnect_session (struct Session *session)
        GNUNET_SERVER_receive_done (session->client,
                                    GNUNET_SYSERR);     
     }
-  if (session->client != NULL) 
+  else if (session->client != NULL)
     GNUNET_SERVER_client_drop (session->client);
   GNUNET_STATISTICS_update (session->plugin->env->stats,
                            gettext_noop ("# TCP sessions active"),
@@ -1134,7 +1150,7 @@ select_better_session (struct Session *s1,
  * gnunet-nat-client to send dummy ICMP responses.
  *
  * @param plugin the plugin for this transport
- * @param addr the address of the peer (IPv4-only)
+ * @param sa the address of the peer (IPv4-only)
  */
 static void
 run_gnunet_nat_client (struct Plugin *plugin, 
@@ -1179,6 +1195,8 @@ run_gnunet_nat_client (struct Plugin *plugin,
                                  inet4,
                                  port_as_string, 
                                  NULL);
+  if (NULL == proc)
+    return;
   /* we know that the gnunet-nat-client will terminate virtually
      instantly */
   GNUNET_OS_process_wait (proc);
@@ -1362,8 +1380,10 @@ tcp_plugin_send (void *cls,
         return -1; /* NAT client only works with IPv4 addresses */
 
 
-      if ((plugin->allow_nat == GNUNET_YES) && (is_natd == GNUNET_YES) &&
-           (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(plugin->nat_wait_conns, &target->hashPubKey)))
+      if ( (plugin->enable_nat_client == GNUNET_YES) && 
+          (is_natd == GNUNET_YES) &&
+           (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(plugin->nat_wait_conns,
+                                                               &target->hashPubKey)) )
         {
 #if DEBUG_TCP_NAT
           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
@@ -1407,9 +1427,10 @@ tcp_plugin_send (void *cls,
           run_gnunet_nat_client (plugin, &a4);
           return 0;
         }
-      if ( (plugin->allow_nat == GNUNET_YES) && 
+      if ( (plugin->enable_nat_client == GNUNET_YES) && 
           (is_natd == GNUNET_YES) && 
-          (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(plugin->nat_wait_conns, &target->hashPubKey)) )
+          (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(plugin->nat_wait_conns, 
+                                                                &target->hashPubKey)) )
         {
           /* Only do one NAT punch attempt per peer identity */
           return -1;
@@ -2001,6 +2022,7 @@ delayed_done (void *cls,
   session->receive_delay_task = GNUNET_SCHEDULER_NO_TASK;
   delay = session->plugin->env->receive (session->plugin->env->cls,
                                         &session->target,
+                                        NULL,
                                         NULL, 0,
                                         session,
                                         NULL, 0);
@@ -2057,7 +2079,14 @@ handle_tcp_data (void *cls,
                            gettext_noop ("# bytes received via TCP"),
                            ntohs (message->size),
                            GNUNET_NO);
-  delay = plugin->env->receive (plugin->env->cls, &session->target, message, 1,
+  struct GNUNET_TRANSPORT_ATS_Information distance[2];
+  distance[0].type = htonl (GNUNET_TRANSPORT_ATS_QUALITY_NET_DISTANCE);
+  distance[0].value = htonl (1);
+  distance[1].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
+  distance[1].value = htonl (0);
+  delay = plugin->env->receive (plugin->env->cls, &session->target, message,
+                               (const struct GNUNET_TRANSPORT_ATS_Information *) &distance,
+                               2,
                                session,
                                (GNUNET_YES == session->inbound) ? NULL : session->connect_addr,
                                (GNUNET_YES == session->inbound) ? 0 : session->connect_alen);
@@ -2307,7 +2336,7 @@ tcp_plugin_server_read (void *cls,
   struct TCPProbeContext *tcp_probe_ctx;
   struct GNUNET_CONNECTION_Handle *sock;
 
-  if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
+  if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
     return;
   memset (mybuf, 0, sizeof(mybuf));
   bytes = GNUNET_DISK_file_read(plugin->server_stdout_handle, 
@@ -2321,6 +2350,7 @@ tcp_plugin_server_read (void *cls,
                       "Finished reading from server stdout with code: %d\n", 
                       bytes);
 #endif
+      /* FIXME: consider process_wait here? */
       return;
     }
 
@@ -2342,7 +2372,6 @@ tcp_plugin_server_read (void *cls,
   /* construct socket address of sender */
   memset (&sin_addr, 0, sizeof (sin_addr));
   sin_addr.sin_family = AF_INET;
-  sin_addr.sin_port = htons((uint16_t) port);
 #if HAVE_SOCKADDR_IN_SIN_LEN
   sin_addr.sin_len = sizeof (sin_addr);
 #endif
@@ -2362,7 +2391,7 @@ tcp_plugin_server_read (void *cls,
                                          plugin);
       return;
     }
-
+  sin_addr.sin_port = htons((uint16_t) port);
 #if DEBUG_TCP_NAT
   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
                   "tcp",
@@ -2592,6 +2621,58 @@ check_gnunet_nat_binary (const char *binary)
 }
 
 
+/**
+ * Our (external) hostname was resolved.
+ *
+ * @param cls the 'struct Plugin'
+ * @param addr NULL on error, otherwise result of DNS lookup
+ * @param addrlen number of bytes in addr
+ */
+static void
+process_external_ip (void *cls,
+                    const struct sockaddr *addr,
+                    socklen_t addrlen)
+{
+  struct Plugin *plugin = cls;
+  const struct sockaddr_in *s;
+  struct IPv4TcpAddress t4;
+
+
+  plugin->ext_dns = NULL;
+  if (addr == NULL)
+    return;
+  GNUNET_assert (addrlen == sizeof (struct sockaddr_in));
+  s = (const struct sockaddr_in *) addr;
+  t4.ipv4_addr = s->sin_addr.s_addr;
+  if ( (plugin->behind_nat == GNUNET_YES) &&
+       (plugin->enable_nat_server == GNUNET_YES) )
+    {
+      t4.t_port = htons(0);
+      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, 
+                      "tcp",
+                      "Notifying transport of address %s:%d\n", 
+                      plugin->external_address,
+                      0);
+    }
+  else
+    {
+      t4.t_port = htons(plugin->adv_port);
+      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, 
+                      "tcp",
+                      "Notifying transport of address %s:%d\n",
+                      plugin->external_address, 
+                      (int) plugin->adv_port);
+    }
+  add_to_address_list (plugin, 
+                      &t4.ipv4_addr, 
+                      sizeof (struct in_addr));
+  plugin->env->notify_address (plugin->env->cls,
+                              "tcp",
+                              &t4, sizeof(t4),
+                              GNUNET_TIME_UNIT_FOREVER_REL);
+}
+
+
 /**
  * Entry point for the plugin.
  *
@@ -2616,32 +2697,44 @@ libgnunet_plugin_transport_tcp_init (void *cls)
   unsigned long long bport;
   unsigned int i;
   int behind_nat;
-  int allow_nat;
+  int nat_punched;
+  int enable_nat_client;
+  int enable_nat_server;
+  int enable_upnp;
   char *internal_address;
   char *external_address;
   struct sockaddr_in in_addr;
-  struct IPv4TcpAddress t4;
   struct GNUNET_TIME_Relative idle_timeout;
 
   behind_nat = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
                                                     "transport-tcp",
                                                     "BEHIND_NAT");
-  if ( (GNUNET_YES == behind_nat) &&
+  nat_punched = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
+                                                     "transport-tcp",
+                                                     "NAT_PUNCHED");
+  enable_nat_client = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
+                                                           "transport-tcp",
+                                                           "ENABLE_NAT_CLIENT");
+  enable_nat_server = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
+                                                           "transport-tcp",
+                                                           "ENABLE_NAT_SERVER");
+  enable_upnp = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
+                                                     "transport-tcp",
+                                                     "ENABLE_UPNP");
+  
+  if ( (GNUNET_YES == enable_nat_server) &&
        (GNUNET_YES != check_gnunet_nat_binary("gnunet-nat-server")) )
     {
-      behind_nat = GNUNET_NO;
+      enable_nat_server = GNUNET_NO;
       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                  _("Configuration requires `%s', but binary is not installed properly (SUID bit not set).  Option disabled.\n"),
                  "gnunet-nat-server");        
     }
 
-  allow_nat = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
-                                                   "transport-tcp",
-                                                   "ALLOW_NAT");
-  if ( (GNUNET_YES == allow_nat) &&
+  if ( (GNUNET_YES == enable_nat_client) &&
        (GNUNET_YES != check_gnunet_nat_binary("gnunet-nat-client")) )
     {
-      allow_nat = GNUNET_NO;
+      enable_nat_client = GNUNET_NO;
       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                  _("Configuration requires `%s', but binary is not installed properly (SUID bit not set).  Option disabled.\n"),
                  "gnunet-nat-client"); 
@@ -2662,6 +2755,7 @@ libgnunet_plugin_transport_tcp_init (void *cls)
   if ( (external_address != NULL) && 
        (inet_pton(AF_INET, external_address, &in_addr.sin_addr) != 1) ) 
     {
+      
       GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
                       "tcp",
                       _("Malformed %s `%s' given in configuration!\n"), 
@@ -2669,6 +2763,20 @@ libgnunet_plugin_transport_tcp_init (void *cls)
                       external_address);
       return NULL;   
     }
+  if ( (external_address == NULL) &&
+       (nat_punched == GNUNET_YES) )
+    {
+      nat_punched = GNUNET_NO;
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                 _("Configuration says NAT was punched, but `%s' is not given.  Option ignored.\n"),
+                 "EXTERNAL_ADDRESS");  
+    }
+
+  if (GNUNET_YES == nat_punched)
+    {
+      enable_nat_server = GNUNET_NO;
+      enable_upnp = GNUNET_NO;
+    }
 
   internal_address = NULL;
   if (GNUNET_OK ==
@@ -2743,10 +2851,10 @@ libgnunet_plugin_transport_tcp_init (void *cls)
   plugin->external_address = external_address;
   plugin->internal_address = internal_address;
   plugin->behind_nat = behind_nat;
-  plugin->allow_nat = allow_nat;
-  plugin->allow_upnp = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
-                                                            "transport-tcp",
-                                                            "ENABLE_UPNP");
+  plugin->nat_punched = nat_punched;
+  plugin->enable_nat_client = enable_nat_client;
+  plugin->enable_nat_server = enable_nat_server;
+  plugin->enable_upnp = enable_upnp;
   plugin->env = env;
   plugin->lsock = NULL;
   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
@@ -2795,6 +2903,7 @@ libgnunet_plugin_transport_tcp_init (void *cls)
   GNUNET_OS_network_interfaces_list (&process_interfaces, plugin);
 
   if ( (plugin->behind_nat == GNUNET_YES) &&
+       (plugin->enable_nat_server == GNUNET_YES) &&
        (GNUNET_YES != tcp_transport_start_nat_server(plugin)) )
     {
       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
@@ -2812,7 +2921,7 @@ libgnunet_plugin_transport_tcp_init (void *cls)
       return NULL;
     }
 
-  if (allow_nat == GNUNET_YES)
+  if (enable_nat_client == GNUNET_YES)
     {
       plugin->nat_wait_conns = GNUNET_CONTAINER_multihashmap_create(16);
       GNUNET_assert (plugin->nat_wait_conns != NULL);
@@ -2850,32 +2959,14 @@ libgnunet_plugin_transport_tcp_init (void *cls)
                                                            &process_hostname_ips,
                                                            plugin);
 
-  if ( (plugin->external_address != NULL) && 
-       (inet_pton(AF_INET,
-                 plugin->external_address, 
-                 &t4.ipv4_addr) == 1) )
+  if (plugin->external_address != NULL) 
     {
-      if (plugin->behind_nat == GNUNET_YES) 
-       {
-         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, 
-                          "tcp",
-                          "Notifying transport of address %s:0\n", 
-                          plugin->external_address);
-         t4.t_port = htons(0);
-       }
-      else
-       {
-         t4.t_port = htons(plugin->adv_port);
-         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, 
-                          "tcp",
-                          "Notifying transport of address %s:%d\n",
-                          plugin->external_address, 
-                          plugin->adv_port);
-       }
-      add_to_address_list (plugin, &t4.ipv4_addr, sizeof (struct in_addr));
-      plugin->env->notify_address (plugin->env->cls,
-                                  "tcp",
-                                  &t4, sizeof(t4), GNUNET_TIME_UNIT_FOREVER_REL);
+      plugin->ext_dns = GNUNET_RESOLVER_ip_get (env->cfg,
+                                               plugin->external_address,
+                                               AF_INET,
+                                               GNUNET_TIME_UNIT_MINUTES,
+                                               &process_external_ip,
+                                               plugin);
     }
   return api;
 }
@@ -2893,6 +2984,11 @@ libgnunet_plugin_transport_tcp_done (void *cls)
   struct LocalAddrList *lal;
   struct TCPProbeContext *tcp_probe;
 
+  if (plugin->ext_dns != NULL)
+    {
+      GNUNET_RESOLVER_request_cancel (plugin->ext_dns);
+      plugin->ext_dns = NULL;
+    }
   while (NULL != (session = plugin->sessions))
     disconnect_session (session);
   if (NULL != plugin->hostname_dns)
@@ -2924,7 +3020,8 @@ libgnunet_plugin_transport_tcp_done (void *cls)
       GNUNET_free (tcp_probe);
     }
 
-  if (plugin->behind_nat == GNUNET_YES)
+  if ((plugin->behind_nat == GNUNET_YES) &&
+      (plugin->enable_nat_server == GNUNET_YES))
     {
       if (0 != GNUNET_OS_process_kill (plugin->server_proc, SIGTERM))
         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");