-makefile for new test_stream_local (commented)
[oweals/gnunet.git] / src / transport / plugin_transport_tcp.c
index 71e79e33c91ad6f37b8e05ecda080291f0c90086..9831070dd5e759adc0b67608f2e19f56d5210bb9 100644 (file)
 
 #define DEBUG_TCP_NAT GNUNET_EXTRA_LOGGING
 
+
+/**
+ * How long until we give up on establishing an NAT connection?
+ * Must be > 4 RTT
+ */
+#define NAT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
+
+
 GNUNET_NETWORK_STRUCT_BEGIN
 
 /**
@@ -239,6 +247,11 @@ struct Session
    */
   struct GNUNET_SERVER_Client *client;
 
+  /**
+   * Task cleaning up a NAT client connection establishment attempt;
+   */
+  GNUNET_SCHEDULER_TaskIdentifier nat_connection_timeout;
+
   /**
    * Messages currently pending for transmission
    * to this peer, if any.
@@ -270,19 +283,22 @@ struct Session
   /**
    * Address of the other peer (either based on our 'connect'
    * call or on our 'accept' call).
+   *
+   * struct IPv4TcpAddress or struct IPv6TcpAddress
+   *
    */
-  void *connect_addr;
+  void *addr;
 
   /**
-   * Last activity on this connection.  Used to select preferred
-   * connection.
+   * Length of connect_addr.
    */
-  struct GNUNET_TIME_Absolute last_activity;
+  size_t addrlen;
 
   /**
-   * Length of connect_addr.
+   * Last activity on this connection.  Used to select preferred
+   * connection.
    */
-  size_t connect_alen;
+  struct GNUNET_TIME_Absolute last_activity;
 
   /**
    * Are we still expecting the welcome message? (GNUNET_YES/GNUNET_NO)
@@ -326,11 +342,6 @@ struct Plugin
    */
   struct GNUNET_NAT_Handle *nat;
 
-  /**
-   * List of open TCP sessions.
-   */
-  struct Session *sessions;
-
   struct GNUNET_CONTAINER_MultiHashMap * sessionmap;
 
   /**
@@ -392,6 +403,44 @@ struct Plugin
 
 };
 
+/* DEBUG CODE */
+static const char *
+tcp_address_to_string (void *cls, const void *addr, size_t addrlen);
+
+static unsigned int sessions;
+
+static void inc_sessions (struct Plugin *plugin, struct Session *session, int line)
+{
+  sessions ++;
+  unsigned int size = GNUNET_CONTAINER_multihashmap_size(plugin->sessionmap);
+  if (sessions != size)
+    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp", "Inconsistent sessions %u <-> session map size: %u\n",
+        sessions, size);
+  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp", "%4i Session increased to %u (session map size: %u): `%s' `%s'\n",
+      line,
+      sessions,
+      size,
+      GNUNET_i2s (&session->target),
+      tcp_address_to_string (NULL, session->addr, session->addrlen));
+}
+
+static void dec_sessions (struct Plugin *plugin, struct Session *session, int line)
+{
+  GNUNET_assert (sessions > 0);
+  unsigned int size = GNUNET_CONTAINER_multihashmap_size(plugin->sessionmap);
+  sessions --;
+  if (sessions != size)
+    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp", "Inconsistent sessions %u <-> session map size: %u\n",
+      sessions, size);
+  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp", "%4i Session decreased to %u (session map size: %u): `%s' `%s'\n",
+      line,
+      sessions,
+      size,
+      GNUNET_i2s (&session->target),
+      tcp_address_to_string (NULL, session->addr, session->addrlen));
+}
+/* DEBUG CODE */
+
 
 /**
  * Function to check if an inbound connection is acceptable.
@@ -527,6 +576,74 @@ tcp_address_to_string (void *cls, const void *addr, size_t addrlen)
 }
 
 
+/**
+ * Function called to convert a string address to
+ * a binary address.
+ *
+ * @param cls closure ('struct Plugin*')
+ * @param addr string address
+ * @param addrlen length of the address
+ * @param buf location to store the buffer
+ * @param added location to store the number of bytes in the buffer.
+ *        If the function returns GNUNET_SYSERR, its contents are undefined.
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
+ */
+int
+tcp_string_to_address (void *cls, const char *addr, uint16_t addrlen,
+    void **buf, size_t *added)
+{
+  struct sockaddr_storage socket_address;
+  int ret = GNUNET_STRINGS_to_address_ip (addr, addrlen,
+    &socket_address);
+
+  if (ret != GNUNET_OK)
+    return GNUNET_SYSERR;
+
+  if (socket_address.ss_family == AF_INET)
+  {
+    struct IPv4TcpAddress *t4;
+    struct sockaddr_in *in4 = (struct sockaddr_in *) &socket_address;
+    t4 = GNUNET_malloc (sizeof (struct IPv4TcpAddress));
+    t4->ipv4_addr = in4->sin_addr.s_addr;
+    t4->t4_port = in4->sin_port;
+    *buf = t4;
+    *added = sizeof (struct IPv4TcpAddress);
+  }
+  else if (socket_address.ss_family == AF_INET6)
+  {
+    struct IPv6TcpAddress *t6;
+    struct sockaddr_in6 *in6 = (struct sockaddr_in6 *) &socket_address;
+    t6 = GNUNET_malloc (sizeof (struct IPv6TcpAddress));
+    t6->ipv6_addr = in6->sin6_addr;
+    t6->t6_port = in6->sin6_port;
+    *buf = t6;
+    *added = sizeof (struct IPv6TcpAddress);
+  }
+  return GNUNET_SYSERR;
+}
+
+
+struct SessionClientCtx
+{
+  const struct GNUNET_SERVER_Client *client;
+  struct Session *ret;
+};
+
+int session_lookup_by_client_it (void *cls,
+               const GNUNET_HashCode * key,
+               void *value)
+{
+  struct SessionClientCtx *sc_ctx = cls;
+  struct Session *s = value;
+
+  if (s->client == sc_ctx->client)
+  {
+    sc_ctx->ret = s;
+    return GNUNET_NO;
+  }
+  return GNUNET_YES;
+}
+
 /**
  * Find the session handle for the given client.
  *
@@ -535,15 +652,16 @@ tcp_address_to_string (void *cls, const void *addr, size_t addrlen)
  * @return NULL if no matching session exists
  */
 static struct Session *
-find_session_by_client (struct Plugin *plugin,
+lookup_session_by_client (struct Plugin *plugin,
                         const struct GNUNET_SERVER_Client *client)
 {
-  struct Session *ret;
+  struct SessionClientCtx sc_ctx;
+  sc_ctx.client = client;
+  sc_ctx.ret = NULL;
 
-  ret = plugin->sessions;
-  while ((ret != NULL) && (client != ret->client))
-    ret = ret->next;
-  return ret;
+  GNUNET_CONTAINER_multihashmap_iterate (plugin->sessionmap, &session_lookup_by_client_it, &sc_ctx);
+
+  return sc_ctx.ret;
 }
 
 
@@ -570,20 +688,15 @@ create_session (struct Plugin *plugin, const struct GNUNET_PeerIdentity *target,
     GNUNET_assert (client != NULL);
   else
     GNUNET_assert (client == NULL);
-#if DEBUG_TCP
+
   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
                    "Creating new session for peer `%4s'\n",
                    GNUNET_i2s (target));
-#endif
+
   ret = GNUNET_malloc (sizeof (struct Session));
   ret->last_activity = GNUNET_TIME_absolute_get ();
   ret->plugin = plugin;
   ret->is_nat = is_nat;
-  if (is_nat != GNUNET_YES)     /* If not a NAT WAIT conn, add it to global list */
-  {
-    ret->next = plugin->sessions;
-    plugin->sessions = ret;
-  }
   ret->client = client;
   ret->target = *target;
   ret->expecting_welcome = GNUNET_YES;
@@ -603,9 +716,11 @@ create_session (struct Plugin *plugin, const struct GNUNET_PeerIdentity *target,
   GNUNET_CONTAINER_DLL_insert (ret->pending_messages_head,
                                ret->pending_messages_tail, pm);
   if (is_nat != GNUNET_YES)
+  {
     GNUNET_STATISTICS_update (plugin->env->stats,
                               gettext_noop ("# TCP sessions active"), 1,
                               GNUNET_NO);
+  }
   return ret;
 }
 
@@ -783,32 +898,23 @@ process_pending_messages (struct Session *session)
 static void
 disconnect_session (struct Session *session)
 {
-  struct Session *prev;
-  struct Session *pos;
   struct PendingMessage *pm;
+  struct Plugin * plugin = session->plugin;
 
-#if DEBUG_TCP
   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
-                   "Disconnecting from `%4s' at %s.\n",
+                   "Disconnecting session %p for peer `%s' address `%s'\n",
+                   session,
                    GNUNET_i2s (&session->target),
-                   (session->connect_addr !=
-                    NULL) ? tcp_address_to_string (session->plugin,
-                                                   session->connect_addr,
-                                                   session->connect_alen) :
-                   "*");
-#endif
-  /* remove from session list */
-  prev = NULL;
-  pos = session->plugin->sessions;
-  while (pos != session)
-  {
-    prev = pos;
-    pos = pos->next;
-  }
-  if (prev == NULL)
-    session->plugin->sessions = session->next;
-  else
-    prev->next = session->next;
+                   tcp_address_to_string(NULL, session->addr, session->addrlen));
+
+   if (GNUNET_YES  == GNUNET_CONTAINER_multihashmap_remove(plugin->sessionmap, &session->target.hashPubKey, session))
+   {
+     GNUNET_STATISTICS_update (session->plugin->env->stats,
+                               gettext_noop ("# TCP sessions active"), -1,
+                               GNUNET_NO);
+     dec_sessions (plugin, session, __LINE__);
+   }
+   else GNUNET_assert (GNUNET_YES  == GNUNET_CONTAINER_multihashmap_remove(plugin->nat_wait_conns, &session->target.hashPubKey, session));
 
   /* clean up state */
   if (session->transmit_handle != NULL)
@@ -818,6 +924,13 @@ disconnect_session (struct Session *session)
   }
   session->plugin->env->session_end (session->plugin->env->cls,
                                      &session->target, session);
+
+  if (session->nat_connection_timeout != GNUNET_SCHEDULER_NO_TASK)
+  {
+    GNUNET_SCHEDULER_cancel (session->nat_connection_timeout);
+    session->nat_connection_timeout = GNUNET_SCHEDULER_NO_TASK;
+  }
+
   while (NULL != (pm = session->pending_messages_head))
   {
 #if DEBUG_TCP
@@ -841,7 +954,6 @@ disconnect_session (struct Session *session)
                          GNUNET_SYSERR);
     GNUNET_free (pm);
   }
-  GNUNET_break (session->client != NULL);
   if (session->receive_delay_task != GNUNET_SCHEDULER_NO_TASK)
   {
     GNUNET_SCHEDULER_cancel (session->receive_delay_task);
@@ -853,346 +965,14 @@ disconnect_session (struct Session *session)
     GNUNET_SERVER_client_drop (session->client);
     session->client = NULL;
   }
-  GNUNET_STATISTICS_update (session->plugin->env->stats,
-                            gettext_noop ("# TCP sessions active"), -1,
-                            GNUNET_NO);
-  GNUNET_free_non_null (session->connect_addr);
-  GNUNET_assert (NULL == session->transmit_handle);
-  GNUNET_free (session);
-}
 
 
-/**
- * Given two otherwise equivalent sessions, pick the better one.
- *
- * @param s1 one session (also default)
- * @param s2 other session
- * @return "better" session (more active)
- */
-static struct Session *
-select_better_session (struct Session *s1, struct Session *s2)
-{
-  if (s1 == NULL)
-    return s2;
-  if (s2 == NULL)
-    return s1;
-  if ((s1->expecting_welcome == GNUNET_NO) &&
-      (s2->expecting_welcome == GNUNET_YES))
-    return s1;
-  if ((s1->expecting_welcome == GNUNET_YES) &&
-      (s2->expecting_welcome == GNUNET_NO))
-    return s2;
-  if (s1->last_activity.abs_value < s2->last_activity.abs_value)
-    return s2;
-  if (s1->last_activity.abs_value > s2->last_activity.abs_value)
-    return s1;
-  if ((GNUNET_YES == s1->inbound) && (GNUNET_NO == s2->inbound))
-    return s1;
-  if ((GNUNET_NO == s1->inbound) && (GNUNET_YES == s2->inbound))
-    return s2;
-  return s1;
+  GNUNET_free_non_null (session->addr);
+  GNUNET_assert (NULL == session->transmit_handle);
+  GNUNET_free (session);
 }
 
 
-
-/**
- * Function that can be used by the transport service to transmit
- * a message using the plugin.   Note that in the case of a
- * peer disconnecting, the continuation MUST be called
- * prior to the disconnect notification itself.  This function
- * will be called with this peer's HELLO message to initiate
- * a fresh connection to another peer.
- *
- * @param cls closure
- * @param target who should receive this message
- * @param msg the message to transmit
- * @param msgbuf_size number of bytes in 'msg'
- * @param priority how important is the message (most plugins will
- *                 ignore message priority and just FIFO)
- * @param timeout how long to wait at most for the transmission (does not
- *                require plugins to discard the message after the timeout,
- *                just advisory for the desired delay; most plugins will ignore
- *                this as well)
- * @param session which session must be used (or NULL for "any")
- * @param addr the address to use (can be NULL if the plugin
- *                is "on its own" (i.e. re-use existing TCP connection))
- * @param addrlen length of the address in bytes
- * @param force_address GNUNET_YES if the plugin MUST use the given address,
- *                GNUNET_NO means the plugin may use any other address and
- *                GNUNET_SYSERR means that only reliable existing
- *                bi-directional connections should be used (regardless
- *                of address)
- * @param cont continuation to call once the message has
- *        been transmitted (or if the transport is ready
- *        for the next transmission call; or if the
- *        peer disconnected...); can be NULL
- * @param cont_cls closure for cont
- * @return number of bytes used (on the physical network, with overheads);
- *         -1 on hard errors (i.e. address invalid); 0 is a legal value
- *         and does NOT mean that the message was not transmitted (DV and NAT)
- */
-static ssize_t
-tcp_plugin_send_old (void *cls, const struct GNUNET_PeerIdentity *target,
-                 const char *msg, size_t msgbuf_size, uint32_t priority,
-                 struct GNUNET_TIME_Relative timeout, struct Session *session,
-                 const void *addr, size_t addrlen, int force_address,
-                 GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
-{
-  struct Plugin *plugin = cls;
-  struct Session *cand_session;
-  struct Session *next;
-  struct PendingMessage *pm;
-  struct GNUNET_CONNECTION_Handle *sa;
-  int af;
-  const void *sb;
-  size_t sbs;
-  struct sockaddr_in a4;
-  struct sockaddr_in6 a6;
-  const struct IPv4TcpAddress *t4;
-  const struct IPv6TcpAddress *t6;
-  unsigned int is_natd;
-
-  GNUNET_STATISTICS_update (plugin->env->stats,
-                            gettext_noop ("# bytes TCP was asked to transmit"),
-                            msgbuf_size, GNUNET_NO);
-  /* FIXME: we could do this cheaper with a hash table
-   * where we could restrict the iteration to entries that match
-   * the target peer... */
-  is_natd = GNUNET_NO;
-  if (session == NULL)
-  {
-    cand_session = NULL;
-    next = plugin->sessions;
-    while (NULL != (session = next))
-    {
-      next = session->next;
-      GNUNET_assert (session->client != NULL);
-      if (0 !=
-          memcmp (target, &session->target,
-                  sizeof (struct GNUNET_PeerIdentity)))
-        continue;
-      if (((GNUNET_SYSERR == force_address) &&
-           (session->expecting_welcome == GNUNET_NO)) ||
-          (GNUNET_NO == force_address))
-      {
-        cand_session = select_better_session (cand_session, session);
-        continue;
-      }
-      if (GNUNET_SYSERR == force_address)
-        continue;
-      GNUNET_break (GNUNET_YES == force_address);
-      if (addr == NULL)
-      {
-        GNUNET_break (0);
-        break;
-      }
-      if ((addrlen != session->connect_alen) && (session->is_nat == GNUNET_NO))
-        continue;
-      if ((0 != memcmp (session->connect_addr, addr, addrlen)) &&
-          (session->is_nat == GNUNET_NO))
-        continue;
-      cand_session = select_better_session (cand_session, session);
-    }
-    session = cand_session;
-  }
-  if ((session == NULL) && (addrlen == 0))
-  {
-#if DEBUG_TCP
-    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
-                     "Asked to transmit to `%4s' without address and I have no existing connection (failing).\n",
-                     GNUNET_i2s (target));
-#endif
-    GNUNET_STATISTICS_update (plugin->env->stats,
-                              gettext_noop
-                              ("# bytes discarded by TCP (no address and no connection)"),
-                              msgbuf_size, GNUNET_NO);
-    return -1;
-  }
-  if (session == NULL)
-  {
-    if (addrlen == sizeof (struct IPv6TcpAddress))
-    {
-      GNUNET_assert (NULL != addr);     /* make static analysis happy */
-      t6 = addr;
-      af = AF_INET6;
-      memset (&a6, 0, sizeof (a6));
-#if HAVE_SOCKADDR_IN_SIN_LEN
-      a6.sin6_len = sizeof (a6);
-#endif
-      a6.sin6_family = AF_INET6;
-      a6.sin6_port = t6->t6_port;
-      if (t6->t6_port == 0)
-        is_natd = GNUNET_YES;
-      memcpy (&a6.sin6_addr, &t6->ipv6_addr, sizeof (struct in6_addr));
-      sb = &a6;
-      sbs = sizeof (a6);
-    }
-    else if (addrlen == sizeof (struct IPv4TcpAddress))
-    {
-      GNUNET_assert (NULL != addr);     /* make static analysis happy */
-      t4 = addr;
-      af = AF_INET;
-      memset (&a4, 0, sizeof (a4));
-#if HAVE_SOCKADDR_IN_SIN_LEN
-      a4.sin_len = sizeof (a4);
-#endif
-      a4.sin_family = AF_INET;
-      a4.sin_port = t4->t4_port;
-      if (t4->t4_port == 0)
-        is_natd = GNUNET_YES;
-      a4.sin_addr.s_addr = t4->ipv4_addr;
-      sb = &a4;
-      sbs = sizeof (a4);
-    }
-    else
-    {
-      GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "tcp",
-                       _("Address of unexpected length: %u\n"), addrlen);
-      GNUNET_break (0);
-      return -1;
-    }
-
-    if ((is_natd == GNUNET_YES) && (addrlen == sizeof (struct IPv6TcpAddress)))
-      return -1;                /* NAT client only works with IPv4 addresses */
-    if (0 == plugin->max_connections)
-      return -1;                /* saturated */
-
-    if ((is_natd == GNUNET_YES) && (NULL != plugin->nat) &&
-        (GNUNET_NO ==
-         GNUNET_CONTAINER_multihashmap_contains (plugin->nat_wait_conns,
-                                                 &target->hashPubKey)))
-    {
-#if DEBUG_TCP_NAT
-      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
-                       _("Found valid IPv4 NAT address (creating session)!\n"));
-#endif
-      session = create_session (plugin, target, NULL, GNUNET_YES);
-      GNUNET_assert (session != NULL);
-
-      /* create new message entry */
-      pm = GNUNET_malloc (sizeof (struct PendingMessage) + msgbuf_size);
-      /* FIXME: the memset of this malloc can be up to 2% of our total runtime */
-      pm->msg = (const char *) &pm[1];
-      memcpy (&pm[1], msg, msgbuf_size);
-      /* FIXME: this memcpy can be up to 7% of our total run-time
-       * (for transport service) */
-      pm->message_size = msgbuf_size;
-      pm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
-      pm->transmit_cont = cont;
-      pm->transmit_cont_cls = cont_cls;
-
-      /* append pm to pending_messages list */
-      GNUNET_CONTAINER_DLL_insert_tail (session->pending_messages_head,
-                                        session->pending_messages_tail, pm);
-
-      GNUNET_assert (GNUNET_CONTAINER_multihashmap_put
-                     (plugin->nat_wait_conns, &target->hashPubKey, session,
-                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY) ==
-                     GNUNET_OK);
-#if DEBUG_TCP_NAT
-      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
-                       "Created NAT WAIT connection to `%4s' at `%s'\n",
-                       GNUNET_i2s (target), GNUNET_a2s (sb, sbs));
-#endif
-      GNUNET_NAT_run_client (plugin->nat, &a4);
-      return 0;
-    }
-    if ((is_natd == GNUNET_YES) &&
-        (GNUNET_YES ==
-         GNUNET_CONTAINER_multihashmap_contains (plugin->nat_wait_conns,
-                                                 &target->hashPubKey)))
-    {
-      /* Only do one NAT punch attempt per peer identity */
-      return -1;
-    }
-    sa = GNUNET_CONNECTION_create_from_sockaddr (af, sb, sbs);
-    if (sa == NULL)
-    {
-#if DEBUG_TCP
-      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
-                       "Failed to create connection to `%4s' at `%s'\n",
-                       GNUNET_i2s (target), GNUNET_a2s (sb, sbs));
-#endif
-      GNUNET_STATISTICS_update (plugin->env->stats,
-                                gettext_noop
-                                ("# bytes discarded by TCP (failed to connect)"),
-                                msgbuf_size, GNUNET_NO);
-      return -1;
-    }
-    GNUNET_assert (0 != plugin->max_connections);
-    plugin->max_connections--;
-#if DEBUG_TCP_NAT
-    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
-                     "Asked to transmit to `%4s', creating fresh session using address `%s'.\n",
-                     GNUNET_i2s (target), GNUNET_a2s (sb, sbs));
-#endif
-    session =
-        create_session (plugin, target,
-                        GNUNET_SERVER_connect_socket (plugin->server, sa),
-                        GNUNET_NO);
-    session->connect_addr = GNUNET_malloc (addrlen);
-    memcpy (session->connect_addr, addr, addrlen);
-    session->connect_alen = addrlen;
-    if (addrlen != 0)
-    {
-      struct GNUNET_ATS_Information ats;
-      ats = plugin->env->get_address_type (plugin->env->cls, sb ,sbs);
-      session->ats_address_network_type = ats.value;
-    }
-    else
-      GNUNET_break (0);
-  }
-  else                          /* session != NULL */
-  {
-    /* check if session is valid */
-    struct Session *ses = plugin->sessions;
-
-    if (0 !=
-        memcmp (target, &session->target, sizeof (struct GNUNET_PeerIdentity)))
-    {
-      GNUNET_break (0);
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Got session %p for `%s', but should be for peer `%s'!\n",
-                  session, GNUNET_i2s (&session->target),
-                  GNUNET_h2s (&target->hashPubKey));
-      return -1;
-    }
-
-    while ((ses != NULL) && (ses != session))
-      ses = ses->next;
-    if (ses == NULL)
-    {
-      return -1;
-    }
-  }
-  GNUNET_assert (session != NULL);
-  GNUNET_assert (session->client != NULL);
-  GNUNET_SERVER_client_set_timeout (session->client,
-                                    GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
-  GNUNET_STATISTICS_update (plugin->env->stats,
-                            gettext_noop ("# bytes currently in TCP buffers"),
-                            msgbuf_size, GNUNET_NO);
-  /* create new message entry */
-  pm = GNUNET_malloc (sizeof (struct PendingMessage) + msgbuf_size);
-  pm->msg = (const char *) &pm[1];
-  memcpy (&pm[1], msg, msgbuf_size);
-  pm->message_size = msgbuf_size;
-  pm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
-  pm->transmit_cont = cont;
-  pm->transmit_cont_cls = cont_cls;
-
-  /* append pm to pending_messages list */
-  GNUNET_CONTAINER_DLL_insert_tail (session->pending_messages_head,
-                                    session->pending_messages_tail, pm);
-#if DEBUG_TCP
-  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
-                   "Asked to transmit %u bytes to `%s', added message to list.\n",
-                   msgbuf_size, GNUNET_i2s (target));
-#endif
-  process_pending_messages (session);
-  return msgbuf_size;
-}
-
 /**
  * Function that can be used by the transport service to transmit
  * a message using the plugin.   Note that in the case of a
@@ -1233,13 +1013,7 @@ tcp_plugin_send (void *cls,
 
   GNUNET_assert (plugin != NULL);
   GNUNET_assert (session != NULL);
-  GNUNET_assert (session->client != NULL);
 
-  GNUNET_SERVER_client_set_timeout (session->client,
-                                    GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
-  GNUNET_STATISTICS_update (plugin->env->stats,
-                            gettext_noop ("# bytes currently in TCP buffers"),
-                            msgbuf_size, GNUNET_NO);
   /* create new message entry */
   pm = GNUNET_malloc (sizeof (struct PendingMessage) + msgbuf_size);
   pm->msg = (const char *) &pm[1];
@@ -1249,16 +1023,51 @@ tcp_plugin_send (void *cls,
   pm->transmit_cont = cont;
   pm->transmit_cont_cls = cont_cls;
 
-  /* append pm to pending_messages list */
-  GNUNET_CONTAINER_DLL_insert_tail (session->pending_messages_head,
-                                    session->pending_messages_tail, pm);
-#if DEBUG_TCP
+
   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
                    "Asked to transmit %u bytes to `%s', added message to list.\n",
                    msgbuf_size, GNUNET_i2s (&session->target));
-#endif
-  process_pending_messages (session);
-  return msgbuf_size;
+
+  if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains_value(plugin->sessionmap, &session->target.hashPubKey, session))
+  {
+    GNUNET_assert (session->client != NULL);
+
+    GNUNET_SERVER_client_set_timeout (session->client,
+                                      GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
+    GNUNET_STATISTICS_update (plugin->env->stats,
+                              gettext_noop ("# bytes currently in TCP buffers"),
+                              msgbuf_size, GNUNET_NO);
+
+    /* append pm to pending_messages list */
+    GNUNET_CONTAINER_DLL_insert_tail (session->pending_messages_head,
+                                      session->pending_messages_tail, pm);
+
+    process_pending_messages (session);
+    return msgbuf_size;
+  }
+  else if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains_value(plugin->nat_wait_conns, &session->target.hashPubKey, session))
+  {
+    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
+                     "This NAT WAIT session for peer `%s' is not yet ready!\n",
+                     GNUNET_i2s (&session->target));
+
+    GNUNET_STATISTICS_update (plugin->env->stats,
+                              gettext_noop ("# bytes currently in TCP buffers"),
+                              msgbuf_size, GNUNET_NO);
+
+    /* append pm to pending_messages list */
+    GNUNET_CONTAINER_DLL_insert_tail (session->pending_messages_head,
+                                      session->pending_messages_tail, pm);
+    return msgbuf_size;
+  }
+  else
+  {
+    if (cont != NULL)
+      cont (cont_cls, &session->target, GNUNET_SYSERR);
+    GNUNET_break (0);
+    GNUNET_free (pm);
+    return GNUNET_SYSERR; /* session does not exist here */
+  }
 }
 
 struct SessionItCtx
@@ -1268,23 +1077,65 @@ struct SessionItCtx
   struct Session * result;
 };
 
-int session_it (void *cls,
+int session_lookup_it (void *cls,
                const GNUNET_HashCode * key,
                void *value)
 {
   struct SessionItCtx * si_ctx = cls;
   struct Session * session = value;
-
-  if (session->connect_alen != si_ctx->addrlen)
+#if 0
+  char * a1 = strdup (tcp_address_to_string(NULL, session->addr, session->addrlen));
+  char * a2 = strdup (tcp_address_to_string(NULL, si_ctx->addr, si_ctx->addrlen));
+  GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "tcp",
+                   "Comparing: %s %u <-> %s %u\n",
+                   a1,
+                   session->addrlen,
+                   a2,
+                   si_ctx->addrlen);
+  GNUNET_free (a1);
+  GNUNET_free (a2);
+#endif
+  if (session->addrlen != si_ctx->addrlen)
+  {
     return GNUNET_YES;
-  if (0 != memcmp (&session->connect_addr, si_ctx->addr, si_ctx->addrlen))
+  }
+  if (0 != memcmp (session->addr, si_ctx->addr, si_ctx->addrlen))
+  {
     return GNUNET_YES;
-
+  }
+#if 0
+  a1 = strdup (tcp_address_to_string(NULL, session->addr, session->addrlen));
+  a2 = strdup (tcp_address_to_string(NULL, si_ctx->addr, si_ctx->addrlen));
+  GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "tcp",
+                   "Comparing: %s %u <-> %s %u , OK!\n",
+                   a1,
+                   session->addrlen,
+                   a2,
+                   si_ctx->addrlen);
+  GNUNET_free (a1);
+  GNUNET_free (a2);
+#endif
   /* Found existing session */
   si_ctx->result = session;
   return GNUNET_NO;
 }
 
+/**
+ * Task cleaning up a NAT connection attempt after timeout
+ */
+
+static void
+nat_connect_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct Session *session = cls;
+
+  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
+                   "NAT WAIT connection to `%4s' at `%s' could not be established, removing session\n",
+                   GNUNET_i2s (&session->target), tcp_address_to_string(NULL, session->addr, session->addrlen));
+
+  disconnect_session (session);
+
+}
 
 /**
  * Create a new session to transmit data to the target
@@ -1292,9 +1143,7 @@ int session_it (void *cls,
  * notify us by calling the env->session_end function
  *
  * @param cls closure
- * @param target the neighbour id
- * @param addr pointer to the address
- * @param addrlen length of addr
+ * @param address pointer to the GNUNET_HELLO_Address
  * @return the session if the address is valid, NULL otherwise
  */
 static struct Session *
@@ -1312,12 +1161,43 @@ tcp_plugin_get_session (void *cls,
   struct sockaddr_in6 a6;
   const struct IPv4TcpAddress *t4;
   const struct IPv6TcpAddress *t6;
+  struct GNUNET_ATS_Information ats;
   unsigned int is_natd = GNUNET_NO;
-  size_t addrlen = address->address_length;
+  size_t addrlen = 0;
 
   GNUNET_assert (plugin != NULL);
   GNUNET_assert (address != NULL);
 
+  addrlen = address->address_length;
+
+  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
+                   "Trying to get session for `%s' address length %i\n",
+                   tcp_address_to_string(NULL, address->address, address->address_length),
+                   addrlen);
+
+  /* look for existing session */
+  if (GNUNET_CONTAINER_multihashmap_contains(plugin->sessionmap, &address->peer.hashPubKey))
+  {
+    struct SessionItCtx si_ctx;
+
+    si_ctx.addr = (void *) address->address;
+    si_ctx.addrlen = address->address_length;
+
+    si_ctx.result = NULL;
+
+    GNUNET_CONTAINER_multihashmap_get_multiple(plugin->sessionmap, &address->peer.hashPubKey, &session_lookup_it, &si_ctx);
+    if (si_ctx.result != NULL)
+    {
+      session = si_ctx.result;
+      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
+                       "Found exisiting session for `%s' address `%s' session %p\n",
+                       GNUNET_i2s (&address->peer),
+                       tcp_address_to_string(NULL, address->address, address->address_length),
+                       session);
+      return session;
+    }
+  }
+
   if (addrlen == sizeof (struct IPv6TcpAddress))
   {
     GNUNET_assert (NULL != address->address);     /* make static analysis happy */
@@ -1360,29 +1240,28 @@ tcp_plugin_get_session (void *cls,
     return NULL;
   }
 
-  /* look for existing session */
-  if (GNUNET_CONTAINER_multihashmap_contains(plugin->sessionmap, &address->peer.hashPubKey))
-  {
-    struct SessionItCtx si_ctx;
-    si_ctx.addr = &sbs;
-    si_ctx.addrlen = sbs;
-    GNUNET_CONTAINER_multihashmap_get_multiple(plugin->sessionmap, &address->peer.hashPubKey, &session_it, &si_ctx);
-    if (si_ctx.result != NULL)
-      session = si_ctx.result;
-    return session;
-  }
+  ats = plugin->env->get_address_type (plugin->env->cls, sb ,sbs);
 
   if ((is_natd == GNUNET_YES) && (addrlen == sizeof (struct IPv6TcpAddress)))
-    return NULL;              /* NAT client only works with IPv4 addresses */
+  {
+    /* NAT client only works with IPv4 addresses */
+    return NULL;
+  }
 
   if (0 == plugin->max_connections)
-    return NULL;              /* saturated */
+  {
+    /* saturated */
+    return NULL;
+  }
 
   if ((is_natd == GNUNET_YES) &&
       (GNUNET_YES ==
        GNUNET_CONTAINER_multihashmap_contains (plugin->nat_wait_conns,
                                                &address->peer.hashPubKey)))
-     return NULL;             /* Only do one NAT punch attempt per peer identity */
+  {
+    /* Only do one NAT punch attempt per peer identity */
+     return NULL;
+  }
 
   if ((is_natd == GNUNET_YES) && (NULL != plugin->nat) &&
       (GNUNET_NO ==
@@ -1394,18 +1273,33 @@ tcp_plugin_get_session (void *cls,
                      _("Found valid IPv4 NAT address (creating session)!\n"));
 #endif
     session = create_session (plugin, &address->peer, NULL, GNUNET_YES);
+    session->addrlen = 0;
+    session->addr = NULL;
+    session->ats_address_network_type = ats.value;
+    session->nat_connection_timeout = GNUNET_SCHEDULER_add_delayed(NAT_TIMEOUT,
+        &nat_connect_timeout,
+        session);
     GNUNET_assert (session != NULL);
 
     GNUNET_assert (GNUNET_CONTAINER_multihashmap_put
                    (plugin->nat_wait_conns, &address->peer.hashPubKey, session,
                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY) == GNUNET_OK);
-#if DEBUG_TCP_NAT
+
     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
                      "Created NAT WAIT connection to `%4s' at `%s'\n",
-                     GNUNET_i2s (&address->peer), GNUNET_a2s (sb, sbs));
-#endif
-    GNUNET_NAT_run_client (plugin->nat, &a4);
-    return session;
+                     GNUNET_i2s (&session->target), GNUNET_a2s (sb, sbs));
+
+    if (GNUNET_OK == GNUNET_NAT_run_client (plugin->nat, &a4))
+      return session;
+    else
+    {
+      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
+                       "Running NAT client for `%4s' at `%s' failed\n",
+                       GNUNET_i2s (&session->target), GNUNET_a2s (sb, sbs));
+
+      disconnect_session (session);
+      return NULL;
+    }
   }
 
   /* create new outbound session */
@@ -1416,33 +1310,32 @@ tcp_plugin_get_session (void *cls,
 #if DEBUG_TCP
     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
                      "Failed to create connection to `%4s' at `%s'\n",
-                     GNUNET_i2s (&address->peer), GNUNET_a2s (sb, sbs));
+                     GNUNET_i2s (&session->target), GNUNET_a2s (sb, sbs));
 #endif
     return NULL;
   }
   plugin->max_connections--;
-#if DEBUG_TCP_NAT
+
   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
                    "Asked to transmit to `%4s', creating fresh session using address `%s'.\n",
                    GNUNET_i2s (&address->peer), GNUNET_a2s (sb, sbs));
-#endif
+
   session = create_session (plugin,
                             &address->peer,
                             GNUNET_SERVER_connect_socket (plugin->server, sa),
                             GNUNET_NO);
-  session->connect_addr = GNUNET_malloc (addrlen);
-  memcpy (session->connect_addr, address->address, addrlen);
-  session->connect_alen = addrlen;
-  if (addrlen != 0)
-  {
-    struct GNUNET_ATS_Information ats;
-    ats = plugin->env->get_address_type (plugin->env->cls, sb ,sbs);
-    session->ats_address_network_type = ats.value;
-  }
-  else
-    GNUNET_break (0);
+  session->addr = GNUNET_malloc (addrlen);
+  memcpy (session->addr, address->address, addrlen);
+  session->addrlen = addrlen;
+  session->ats_address_network_type = ats.value;
 
   GNUNET_CONTAINER_multihashmap_put(plugin->sessionmap, &address->peer.hashPubKey, session, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+  inc_sessions (plugin, session, __LINE__);
+  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
+                   "Creating new session for `%s' address `%s' session %p\n",
+                   GNUNET_i2s (&address->peer),
+                   tcp_address_to_string(NULL, address->address, address->address_length),
+                   session);
 
   /* Send TCP Welcome */
   process_pending_messages (session);
@@ -1451,6 +1344,20 @@ tcp_plugin_get_session (void *cls,
 }
 
 
+int session_disconnect_it (void *cls,
+               const GNUNET_HashCode * key,
+               void *value)
+{
+  struct Session *session = value;
+
+  GNUNET_STATISTICS_update (session->plugin->env->stats,
+                            gettext_noop
+                            ("# transport-service disconnect requests for TCP"),
+                            1, GNUNET_NO);
+  disconnect_session (session);
+  return GNUNET_YES;
+}
+
 /**
  * Function that can be called to force a disconnect from the
  * specified neighbour.  This should also cancel all previously
@@ -1471,33 +1378,19 @@ static void
 tcp_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
 {
   struct Plugin *plugin = cls;
-  struct Session *session;
-  struct Session *next;
-  struct PendingMessage *pm;
+  struct Session *nat_session = NULL;
 
-#if DEBUG_TCP
   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
-                   "Asked to cancel session with `%4s'\n", GNUNET_i2s (target));
-#endif
-  next = plugin->sessions;
-  while (NULL != (session = next))
-  {
-    next = session->next;
-    if (0 !=
-        memcmp (target, &session->target, sizeof (struct GNUNET_PeerIdentity)))
-      continue;
-    pm = session->pending_messages_head;
-    while (pm != NULL)
-    {
-      pm->transmit_cont = NULL;
-      pm->transmit_cont_cls = NULL;
-      pm = pm->next;
-    }
-    GNUNET_STATISTICS_update (session->plugin->env->stats,
-                              gettext_noop
-                              ("# transport-service disconnect requests for TCP"),
-                              1, GNUNET_NO);
-    disconnect_session (session);
+                   "Disconnecting peer `%4s'\n", GNUNET_i2s (target));
+
+  GNUNET_CONTAINER_multihashmap_get_multiple (plugin->sessionmap, &target->hashPubKey, session_disconnect_it, plugin);
+
+  nat_session = GNUNET_CONTAINER_multihashmap_get(plugin->nat_wait_conns, &target->hashPubKey);
+  if (nat_session != NULL)
+  {
+    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
+                     "Cleaning up pending NAT session for peer `%4s'\n", GNUNET_i2s (target));
+    disconnect_session (nat_session);
   }
 }
 
@@ -1521,6 +1414,8 @@ struct PrettyPrinterContext
    * Port to add after the IP address.
    */
   uint16_t port;
+
+  int ipv6;
 };
 
 
@@ -1542,7 +1437,10 @@ append_port (void *cls, const char *hostname)
     GNUNET_free (ppc);
     return;
   }
-  GNUNET_asprintf (&ret, "%s:%d", hostname, ppc->port);
+  if (GNUNET_YES == ppc->ipv6)
+    GNUNET_asprintf (&ret, "[%s]:%d", hostname, ppc->port);
+  else
+    GNUNET_asprintf (&ret, "%s:%d", hostname, ppc->port);
   ppc->asc (ppc->asc_cls, ret);
   GNUNET_free (ret);
 }
@@ -1604,11 +1502,17 @@ tcp_plugin_address_pretty_printer (void *cls, const char *type,
   else
   {
     /* invalid address */
+    GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "tcp",
+        "Invalid address to string request: plugin `%s', address length: %u bytes\n");
     GNUNET_break_op (0);
     asc (asc_cls, NULL);
     return;
   }
   ppc = GNUNET_malloc (sizeof (struct PrettyPrinterContext));
+  if (addrlen == sizeof (struct IPv6TcpAddress))
+    ppc->ipv6 = GNUNET_YES;
+  else
+    ppc->ipv6 = GNUNET_NO;
   ppc->asc = asc;
   ppc->asc_cls = asc_cls;
   ppc->port = port;
@@ -1715,9 +1619,8 @@ handle_tcp_nat_probe (void *cls, struct GNUNET_SERVER_Client *client,
   const struct sockaddr_in *s4;
   const struct sockaddr_in6 *s6;
 
-#if DEBUG_TCP_NAT
   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp", "received NAT probe\n");
-#endif
+
   /* We have received a TCP NAT probe, meaning we (hopefully) initiated
    * a connection to this peer by running gnunet-nat-client.  This peer
    * received the punch message and now wants us to use the new connection
@@ -1747,17 +1650,20 @@ handle_tcp_nat_probe (void *cls, struct GNUNET_SERVER_Client *client,
                                          clientIdentity.hashPubKey);
   if (session == NULL)
   {
-#if DEBUG_TCP_NAT
     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
                      "Did NOT find session for NAT probe!\n");
-#endif
     GNUNET_SERVER_receive_done (client, GNUNET_OK);
     return;
   }
-#if DEBUG_TCP_NAT
   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
                    "Found session for NAT probe!\n");
-#endif
+
+  if (session->nat_connection_timeout != GNUNET_SCHEDULER_NO_TASK)
+  {
+    GNUNET_SCHEDULER_cancel (session->nat_connection_timeout);
+    session->nat_connection_timeout = GNUNET_SCHEDULER_NO_TASK;
+  }
+
   GNUNET_assert (GNUNET_CONTAINER_multihashmap_remove
                  (plugin->nat_wait_conns,
                   &tcp_nat_probe->clientIdentity.hashPubKey,
@@ -1787,24 +1693,24 @@ handle_tcp_nat_probe (void *cls, struct GNUNET_SERVER_Client *client,
     t4 = GNUNET_malloc (sizeof (struct IPv4TcpAddress));
     t4->t4_port = s4->sin_port;
     t4->ipv4_addr = s4->sin_addr.s_addr;
-    session->connect_addr = t4;
-    session->connect_alen = sizeof (struct IPv4TcpAddress);
+    session->addr = t4;
+    session->addrlen = sizeof (struct IPv4TcpAddress);
     break;
   case AF_INET6:
     s6 = vaddr;
     t6 = GNUNET_malloc (sizeof (struct IPv6TcpAddress));
     t6->t6_port = s6->sin6_port;
     memcpy (&t6->ipv6_addr, &s6->sin6_addr, sizeof (struct in6_addr));
-    session->connect_addr = t6;
-    session->connect_alen = sizeof (struct IPv6TcpAddress);
+    session->addr = t6;
+    session->addrlen = sizeof (struct IPv6TcpAddress);
     break;
   default:
     GNUNET_break_op (0);
-#if DEBUG_TCP_NAT
+
     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
                      "Bad address for incoming connection!\n");
-#endif
     GNUNET_free (vaddr);
+
     GNUNET_SERVER_client_drop (client);
     GNUNET_free (session);
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
@@ -1812,8 +1718,8 @@ handle_tcp_nat_probe (void *cls, struct GNUNET_SERVER_Client *client,
   }
   GNUNET_free (vaddr);
 
-  session->next = plugin->sessions;
-  plugin->sessions = session;
+  GNUNET_CONTAINER_multihashmap_put(plugin->sessionmap, &session->target.hashPubKey, session, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+  inc_sessions (plugin, session, __LINE__);
   GNUNET_STATISTICS_update (plugin->env->stats,
                             gettext_noop ("# TCP sessions active"), 1,
                             GNUNET_NO);
@@ -1852,42 +1758,42 @@ handle_tcp_welcome (void *cls, struct GNUNET_SERVER_Client *client,
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
   }
-#if DEBUG_TCP
+
   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
-                   "Received %s message from `%4s'.\n", "WELCOME",
+                   "Received %s message from `%4s'\n", "WELCOME",
                    GNUNET_i2s (&wm->clientIdentity));
-#endif
   GNUNET_STATISTICS_update (plugin->env->stats,
                             gettext_noop ("# TCP WELCOME messages received"), 1,
                             GNUNET_NO);
-  session = find_session_by_client (plugin, client);
 
-  if (session == NULL)
+  session = lookup_session_by_client (plugin, client);
+  if (session != NULL)
   {
-#if DEBUG_TCP_NAT
-    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
-                     "Received %s message from a `%4s', creating new session\n",
-                     "WELCOME", GNUNET_i2s (&wm->clientIdentity));
-#endif
-    GNUNET_SERVER_client_keep (client);
-    session = create_session (plugin, &wm->clientIdentity, client, GNUNET_NO);
-    session->inbound = GNUNET_YES;
     if (GNUNET_OK == GNUNET_SERVER_client_get_address (client, &vaddr, &alen))
     {
-#if DEBUG_TCP_NAT
       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
-                       "Found address `%s' for incoming connection\n",
+                       "Found existing session %p for peer `%s'\n",
+                       session,
                        GNUNET_a2s (vaddr, alen));
-#endif
+      GNUNET_free (vaddr);
+    }
+  }
+  else
+  {
+    GNUNET_SERVER_client_keep (client);
+    session = create_session (plugin, &wm->clientIdentity, client, GNUNET_NO);
+    session->inbound = GNUNET_YES;
 
+    if (GNUNET_OK == GNUNET_SERVER_client_get_address (client, &vaddr, &alen))
+    {
       if (alen == sizeof (struct sockaddr_in))
       {
         s4 = vaddr;
         t4 = GNUNET_malloc (sizeof (struct IPv4TcpAddress));
         t4->t4_port = s4->sin_port;
         t4->ipv4_addr = s4->sin_addr.s_addr;
-        session->connect_addr = t4;
-        session->connect_alen = sizeof (struct IPv4TcpAddress);
+        session->addr = t4;
+        session->addrlen = sizeof (struct IPv4TcpAddress);
       }
       else if (alen == sizeof (struct sockaddr_in6))
       {
@@ -1895,8 +1801,8 @@ handle_tcp_welcome (void *cls, struct GNUNET_SERVER_Client *client,
         t6 = GNUNET_malloc (sizeof (struct IPv6TcpAddress));
         t6->t6_port = s6->sin6_port;
         memcpy (&t6->ipv6_addr, &s6->sin6_addr, sizeof (struct in6_addr));
-        session->connect_addr = t6;
-        session->connect_alen = sizeof (struct IPv6TcpAddress);
+        session->addr = t6;
+        session->addrlen = sizeof (struct IPv6TcpAddress);
       }
 
       struct GNUNET_ATS_Information ats;
@@ -1912,19 +1818,8 @@ handle_tcp_welcome (void *cls, struct GNUNET_SERVER_Client *client,
                        "Did not obtain TCP socket address for incoming connection\n");
 #endif
     }
-    process_pending_messages (session);
-  }
-  else
-  {
-#if DEBUG_TCP_NAT
-    if (GNUNET_OK == GNUNET_SERVER_client_get_address (client, &vaddr, &alen))
-    {
-      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
-                       "Found address `%s' (already have session)\n",
-                       GNUNET_a2s (vaddr, alen));
-      GNUNET_free (vaddr);
-    }
-#endif
+    GNUNET_CONTAINER_multihashmap_put(plugin->sessionmap, &wm->clientIdentity.hashPubKey, session, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+    inc_sessions (plugin, session, __LINE__);
   }
 
   if (session->expecting_welcome != GNUNET_YES)
@@ -1935,6 +1830,10 @@ handle_tcp_welcome (void *cls, struct GNUNET_SERVER_Client *client,
   }
   session->last_activity = GNUNET_TIME_absolute_get ();
   session->expecting_welcome = GNUNET_NO;
+
+
+  process_pending_messages (session);
+
   GNUNET_SERVER_client_set_timeout (client,
                                     GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
@@ -1993,20 +1892,46 @@ handle_tcp_data (void *cls, struct GNUNET_SERVER_Client *client,
     GNUNET_SERVER_receive_done (client, GNUNET_OK);
     return;
   }
-  session = find_session_by_client (plugin, client);
-  if ((NULL == session) || (GNUNET_YES == session->expecting_welcome))
+  session = lookup_session_by_client (plugin, client);
+  if (NULL == session)
   {
+    /* No inbound session found */
+    void *vaddr;
+    size_t alen;
+    GNUNET_SERVER_client_get_address (client, &vaddr, &alen);
+    GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "tcp",
+                     "Received unexpected %u bytes of type %u from `%s'\n",
+                     (unsigned int) ntohs (message->size),
+                     (unsigned int) ntohs (message->type),
+                     GNUNET_a2s(vaddr, alen));
+    GNUNET_break_op (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  }
+  else if (GNUNET_YES == session->expecting_welcome)
+  {
+    /* Session is expecting WELCOME message */
+    void *vaddr;
+    size_t alen;
+    GNUNET_SERVER_client_get_address (client, &vaddr, &alen);
+    GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "tcp",
+                     "Received unexpected %u bytes of type %u from `%s'\n",
+                     (unsigned int) ntohs (message->size),
+                     (unsigned int) ntohs (message->type),
+                     GNUNET_a2s(vaddr, alen));
+    GNUNET_break_op (0);
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
   }
+
   session->last_activity = GNUNET_TIME_absolute_get ();
-#if DEBUG_TCP
+
   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
                    "Passing %u bytes of type %u from `%4s' to transport service.\n",
                    (unsigned int) ntohs (message->size),
                    (unsigned int) ntohs (message->type),
                    GNUNET_i2s (&session->target));
-#endif
+
   GNUNET_STATISTICS_update (plugin->env->stats,
                             gettext_noop ("# bytes received via TCP"),
                             ntohs (message->size), GNUNET_NO);
@@ -2018,14 +1943,13 @@ handle_tcp_data (void *cls, struct GNUNET_SERVER_Client *client,
   distance[1].value = session->ats_address_network_type;
   GNUNET_break (ntohl(session->ats_address_network_type) != GNUNET_ATS_NET_UNSPECIFIED);
 
-  delay =
-      plugin->env->receive (plugin->env->cls, &session->target, message,
-                            (const struct GNUNET_ATS_Information *) &distance,
-                            1, session,
-                            (GNUNET_YES ==
-                             session->inbound) ? NULL : session->connect_addr,
-                            (GNUNET_YES ==
-                             session->inbound) ? 0 : session->connect_alen);
+  delay = plugin->env->receive (plugin->env->cls,
+                                &session->target,
+                                message,
+                                (const struct GNUNET_ATS_Information *) &distance,
+                                1, session,
+                                (GNUNET_YES == session->inbound) ? NULL : session->addr,
+                                (GNUNET_YES == session->inbound) ? 0 : session->addrlen);
   if (delay.rel_value == 0)
   {
     GNUNET_SERVER_receive_done (client, GNUNET_OK);
@@ -2061,17 +1985,17 @@ disconnect_notify (void *cls, struct GNUNET_SERVER_Client *client)
   if (client == NULL)
     return;
   plugin->max_connections++;
-  session = find_session_by_client (plugin, client);
+  session = lookup_session_by_client (plugin, client);
   if (session == NULL)
     return;                     /* unknown, nothing to do */
 #if DEBUG_TCP
   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp",
                    "Destroying session of `%4s' with %s due to network-level disconnect.\n",
                    GNUNET_i2s (&session->target),
-                   (session->connect_addr !=
+                   (session->addr !=
                     NULL) ? tcp_address_to_string (session->plugin,
-                                                   session->connect_addr,
-                                                   session->connect_alen) :
+                                                   session->addr,
+                                                   session->addrlen) :
                    "*");
 #endif
   GNUNET_STATISTICS_update (session->plugin->env->stats,
@@ -2198,6 +2122,18 @@ libgnunet_plugin_transport_tcp_init (void *cls)
   struct sockaddr **addrs;
   socklen_t *addrlens;
 
+  if (NULL == env->receive)
+  {
+    /* run in 'stub' mode (i.e. as part of gnunet-peerinfo), don't fully
+       initialze the plugin or the API */
+    api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
+    api->cls = NULL;
+    api->address_pretty_printer = &tcp_plugin_address_pretty_printer;
+    api->address_to_string = &tcp_address_to_string;
+    api->string_to_address = &tcp_string_to_address;
+    return api;
+  }
+
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_number (env->cfg, "transport-tcp",
                                              "MAX_CONNECTIONS",
@@ -2236,8 +2172,6 @@ libgnunet_plugin_transport_tcp_init (void *cls)
   else
     service = NULL;
 
-
-
   plugin = GNUNET_malloc (sizeof (struct Plugin));
   plugin->sessionmap = GNUNET_CONTAINER_multihashmap_create(max_connections);
   plugin->max_connections = max_connections;
@@ -2273,15 +2207,14 @@ libgnunet_plugin_transport_tcp_init (void *cls)
   }
   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
   api->cls = plugin;
-  api->send = &tcp_plugin_send_old;
-
-  api->send_with_session = &tcp_plugin_send;
+  api->send = &tcp_plugin_send;
   api->get_session = &tcp_plugin_get_session;
 
   api->disconnect = &tcp_plugin_disconnect;
   api->address_pretty_printer = &tcp_plugin_address_pretty_printer;
   api->check_address = &tcp_plugin_check_address;
   api->address_to_string = &tcp_address_to_string;
+  api->string_to_address = &tcp_string_to_address;
   plugin->service = service;
   if (service != NULL)
   {
@@ -2327,6 +2260,10 @@ libgnunet_plugin_transport_tcp_init (void *cls)
                      _
                      ("TCP transport advertises itself as being on port %llu\n"),
                      aport);
+  /* Initially set connections to 0 */
+  GNUNET_STATISTICS_set(plugin->env->stats,
+                        gettext_noop ("# TCP sessions active"), 0,
+                        GNUNET_NO);
   return api;
 }
 
@@ -2339,11 +2276,20 @@ libgnunet_plugin_transport_tcp_done (void *cls)
 {
   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
   struct Plugin *plugin = api->cls;
-  struct Session *session;
   struct TCPProbeContext *tcp_probe;
 
-  while (NULL != (session = plugin->sessions))
-    disconnect_session (session);
+  if (NULL == plugin)
+  {
+    GNUNET_free (api);
+    return NULL;
+  }
+  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "tcp", "Shutting down TCP plugin\n");
+
+  /* Removing leftover sessions */
+  GNUNET_CONTAINER_multihashmap_iterate(plugin->sessionmap, &session_disconnect_it, NULL);
+  /* Removing leftover NAT sessions */
+  GNUNET_CONTAINER_multihashmap_iterate(plugin->nat_wait_conns, &session_disconnect_it, NULL);
+
   if (plugin->service != NULL)
     GNUNET_SERVICE_stop (plugin->service);
   else