(no commit message)
[oweals/gnunet.git] / src / transport / gnunet-service-transport.c
index d379a7410153e819d9b8051ed27058ca7a08b408..12db541acf802efe8ba075a88deddc0962133af8 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet.
-     (C) 2009 Christian Grothoff (and other contributing authors)
+     (C) 2009, 2010 Christian Grothoff (and other contributing authors)
 
      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
  * @brief low-level P2P messaging
  * @author Christian Grothoff
  *
- * NOTE:
- * - This code uses 'GNUNET_a2s' for debug printing in many places,
- *   which is technically wrong since it assumes we have IP+Port 
- *   (v4/v6) addresses.  Once we add transports like http or smtp
- *   this will have to be changed!
  */
 #include "platform.h"
 #include "gnunet_client_lib.h"
 #include "plugin_transport.h"
 #include "transport.h"
 
+#define DEBUG_BLACKLIST GNUNET_NO
+
+#define DEBUG_PING_PONG GNUNET_NO
+
+#define SIGN_USELESS GNUNET_NO
+
+#define DEBUG_TRANSPORT_HELLO GNUNET_YES
+
 /**
  * Should we do some additional checks (to validate behavior
  * of clients)?
  * How many messages can we have pending for a given client process
  * before we start to drop incoming messages?  We typically should
  * have only one client and so this would be the primary buffer for
- * messages, so the number should be chosen rather generously.
 * messages, so the number should be chosen rather generously.
  *
  * The expectation here is that most of the time the queue is large
- * enough so that a drop is virtually never required.
+ * enough so that a drop is virtually never required.  Note that
+ * this value must be about as large as 'TOTAL_MSGS' in the
+ * 'test_transport_api_reliability.c', otherwise that testcase may
+ * fail.
  */
-#define MAX_PENDING 128
+#define MAX_PENDING (128 * 1024)
+
+/**
+ * Size of the per-transport blacklist hash maps.
+ */
+#define TRANSPORT_BLACKLIST_HT_SIZE 16
 
 /**
  * How often should we try to reconnect to a peer using a particular
  * Besides, if a single request to an address takes a long time,
  * then the peer is unlikely worthwhile anyway.
  */
-#define HELLO_VERIFICATION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
+#define HELLO_VERIFICATION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
+
+/**
+ * How long is a PONG signature valid?  We'll recycle a signature until
+ * 1/4 of this time is remaining.  PONGs should expire so that if our
+ * external addresses change an adversary cannot replay them indefinitely.
+ * OTOH, we don't want to spend too much time generating PONG signatures,
+ * so they must have some lifetime to reduce our CPU usage.
+ */
+#define PONG_SIGNATURE_LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
 
 /**
  * Priority to use for PONG messages.
@@ -155,11 +175,6 @@ struct ForeignAddressList
    */
   GNUNET_SCHEDULER_TaskIdentifier revalidate_task;
 
-  /**
-   * Length of addr.
-   */
-  size_t addrlen;
-
   /**
    * The address.
    */
@@ -198,6 +213,11 @@ struct ForeignAddressList
    */
   uint32_t distance;
 
+  /**
+   * Length of addr.
+   */
+  uint16_t addrlen;
+
   /**
    * Have we ever estimated the latency of this address?  Used to
    * ensure that the first time we add an address, we immediately
@@ -230,7 +250,8 @@ struct ForeignAddressList
 
 
 /**
- * Entry in linked list of network addresses for ourselves.
+ * Entry in linked list of network addresses for ourselves.  Also
+ * includes a cached signature for 'struct TransportPongMessage's.
  */
 struct OwnAddressList
 {
@@ -240,21 +261,26 @@ struct OwnAddressList
   struct OwnAddressList *next;
 
   /**
-   * The address, actually a pointer to the end
-   * of this struct.  Do not free!
-   */
-  const void *addr;
-  
-  /**
-   * How long until we auto-expire this address (unless it is
+   * How long until we actually auto-expire this address (unless it is
    * re-confirmed by the transport)?
    */
   struct GNUNET_TIME_Absolute expires;
 
+  /**
+   * How long until the current signature expires? (ZERO if the
+   * signature was never created).
+   */
+  struct GNUNET_TIME_Absolute pong_sig_expires;
+
+  /**
+   * Signature for a 'struct TransportPongMessage' for this address.
+   */
+  struct GNUNET_CRYPTO_RsaSignature pong_signature;
+
   /**
    * Length of addr.
    */
-  size_t addrlen;
+  uint32_t addrlen;
 
 };
 
@@ -310,6 +336,10 @@ struct TransportPlugin
    */
   int rebuild;
 
+  /**
+   * Hashmap of blacklisted peers for this particular transport.
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *blacklist;
 };
 
 struct NeighbourList;
@@ -538,7 +568,9 @@ struct NeighbourList
 
 /**
  * Message used to ask a peer to validate receipt (to check an address
- * from a HELLO).  
+ * from a HELLO).  Followed by the address we are trying to validate,
+ * or an empty address if we are just sending a PING to confirm that a
+ * connection which the receiver (of the PING) initiated is still valid.
  */
 struct TransportPingMessage
 {
@@ -549,7 +581,7 @@ struct TransportPingMessage
   struct GNUNET_MessageHeader header;
 
   /**
-   * Random challenge number (in network byte order).
+   * Challenge code (to ensure fresh reply).
    */
   uint32_t challenge GNUNET_PACKED;
 
@@ -564,14 +596,12 @@ struct TransportPingMessage
 /**
  * Message used to validate a HELLO.  The challenge is included in the
  * confirmation to make matching of replies to requests possible.  The
- * signature signs the original challenge number, our public key, the
- * sender's address (so that the sender can check that the address we
- * saw is plausible for him and possibly detect a MiM attack) and a
- * timestamp (to limit replay).<p>
+ * signature signs our public key, an expiration time and our address.<p>
  *
- * This message is followed by the address of the
- * client that we are observing (which is part of what
- * is being signed).
+ * This message is followed by our transport address that the PING tried
+ * to confirm (if we liked it).  The address can be empty (zero bytes)
+ * if the PING had not address either (and we received the request via
+ * a connection that we initiated).
  */
 struct TransportPongMessage
 {
@@ -582,9 +612,10 @@ struct TransportPongMessage
   struct GNUNET_MessageHeader header;
 
   /**
-   * For padding, always zero.
+   * Challenge code from PING (showing freshness).  Not part of what
+   * is signed so that we can re-use signatures.
    */
-  uint32_t reserved GNUNET_PACKED;
+  uint32_t challenge GNUNET_PACKED;
 
   /**
    * Signature.
@@ -592,24 +623,31 @@ struct TransportPongMessage
   struct GNUNET_CRYPTO_RsaSignature signature;
 
   /**
-   * What are we signing and why?
+   * What are we signing and why?  Two possible reason codes can be here:
+   * GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN to confirm that this is a
+   * plausible address for this peer (pid is set to identity of signer); or
+   * GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_USING to confirm that this is
+   * an address we used to connect to the peer with the given pid.
    */
   struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
 
   /**
-   * Random challenge number (in network byte order).
+   * When does this signature expire?
    */
-  uint32_t challenge GNUNET_PACKED;
+  struct GNUNET_TIME_AbsoluteNBO expiration;
 
   /**
-   * Who signed this message?
+   * Either the identity of the peer Who signed this message, or the
+   * identity of the peer that we're connected to using the given
+   * address (depending on purpose.type).
    */
-  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded signer;
+  struct GNUNET_PeerIdentity pid;
 
   /**
-   * Size of address appended to this message
+   * Size of address appended to this message (part of what is
+   * being signed, hence not redundant). 
    */
-  size_t addrlen;
+  uint32_t addrlen;
 
 };
 
@@ -681,12 +719,24 @@ struct TransportClient
 };
 
 
+/**
+ * Context of currently active requests to peerinfo
+ * for validation of HELLOs.
+ */
+struct CheckHelloValidatedContext;
+
+
 /**
  * Entry in map of all HELLOs awaiting validation.
  */
 struct ValidationEntry
 {
 
+  /**
+   * NULL if this entry is not part of a larger HELLO validation.
+   */
+  struct CheckHelloValidatedContext *chvc;
+
   /**
    * The address, actually a pointer to the end
    * of this struct.  Do not free!
@@ -720,14 +770,14 @@ struct ValidationEntry
   struct Session *session;
 
   /**
-   * Length of addr.
+   * Challenge number we used.
    */
-  size_t addrlen;
+  uint32_t challenge;
 
   /**
-   * Challenge number we used.
+   * Length of addr.
    */
-  uint32_t challenge;
+  uint16_t addrlen;
 
 };
 
@@ -765,20 +815,20 @@ struct CheckHelloValidatedContext
    */
   int hello_known;
 
+  /**
+   * Number of validation entries currently referring to this
+   * CHVC.
+   */
+  unsigned int ve_count;
 };
 
 
+
 /**
  * Our HELLO message.
  */
 static struct GNUNET_HELLO_Message *our_hello;
 
-/**
- * "version" of "our_hello".  Used to see if a given neighbour has
- * already been sent the latest version of our HELLO message.
- */
-static unsigned int our_hello_version;
-
 /**
  * Our public key.
  */
@@ -815,9 +865,9 @@ static struct TransportClient *clients;
 static struct TransportPlugin *plugins;
 
 /**
- * Our server.
+ * Handle to peerinfo service.
  */
-static struct GNUNET_SERVER_Handle *server;
+static struct GNUNET_PEERINFO_Handle *peerinfo;
 
 /**
  * All known neighbours and their HELLOs.
@@ -850,7 +900,6 @@ static struct GNUNET_CONTAINER_MultiHashMap *validation_map;
  */
 static struct GNUNET_STATISTICS_Handle *stats;
 
-
 /**
  * The peer specified by the given neighbour has timed-out or a plugin
  * has disconnected.  We may either need to do nothing (other plugins
@@ -906,6 +955,217 @@ find_transport (const char *short_name)
   return head;
 }
 
+/**
+ * Is a particular peer blacklisted for a particular transport?
+ *
+ * @param peer the peer to check for
+ * @param plugin the plugin used to connect to the peer
+ *
+ * @return GNUNET_YES if the peer is blacklisted, GNUNET_NO if not
+ */
+static int
+is_blacklisted (const struct GNUNET_PeerIdentity *peer, struct TransportPlugin *plugin)
+{
+
+  if (plugin->blacklist != NULL)
+    {
+      if (GNUNET_CONTAINER_multihashmap_contains(plugin->blacklist, &peer->hashPubKey) == GNUNET_YES)
+        {
+#if DEBUG_BLACKLIST
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      _("Peer `%s:%s' is blacklisted!\n"),
+                      plugin->short_name, GNUNET_i2s (peer));
+#endif
+          return GNUNET_YES;
+        }
+    }
+
+  return GNUNET_NO;
+}
+
+
+static void
+add_peer_to_blacklist (struct GNUNET_PeerIdentity *peer, char *transport_name)
+{
+  struct TransportPlugin *plugin;
+
+  plugin = find_transport(transport_name);
+  if (plugin == NULL) /* Nothing to do */
+    return;
+  if (plugin->blacklist == NULL)    
+    plugin->blacklist = GNUNET_CONTAINER_multihashmap_create(TRANSPORT_BLACKLIST_HT_SIZE);    
+  GNUNET_assert(plugin->blacklist != NULL);
+  GNUNET_CONTAINER_multihashmap_put(plugin->blacklist, &peer->hashPubKey,
+                                   NULL, 
+                                   GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
+}
+
+
+/**
+ * Read the blacklist file, containing transport:peer entries.
+ * Provided the transport is loaded, set up hashmap with these
+ * entries to blacklist peers by transport.
+ *
+ */
+static void
+read_blacklist_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  char *fn;
+  char *data;
+  size_t pos;
+  size_t colon_pos;
+  int tsize;
+  struct GNUNET_PeerIdentity pid;
+  struct stat frstat;
+  struct GNUNET_CRYPTO_HashAsciiEncoded enc;
+  unsigned int entries_found;
+  char *transport_name;
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                               "TRANSPORT",
+                                               "BLACKLIST_FILE",
+                                               &fn))
+    {
+#if DEBUG_TRANSPORT
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  _("Option `%s' in section `%s' not specified!\n"),
+                  "BLACKLIST_FILE",
+                  "TRANSPORT");
+#endif
+      return;
+    }
+  if (GNUNET_OK != GNUNET_DISK_file_test (fn))
+    GNUNET_DISK_fn_write (fn, NULL, 0, GNUNET_DISK_PERM_USER_READ
+        | GNUNET_DISK_PERM_USER_WRITE);
+  if (0 != STAT (fn, &frstat))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  _("Could not read blacklist file `%s'\n"), fn);
+      GNUNET_free (fn);
+      return;
+    }
+  if (frstat.st_size == 0)
+    {
+#if DEBUG_TRANSPORT
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  _("Blacklist file `%s' is empty.\n"),
+                  fn);
+#endif
+      GNUNET_free (fn);
+      return;
+    }
+  /* FIXME: use mmap */
+  data = GNUNET_malloc_large (frstat.st_size);
+  GNUNET_assert(data != NULL);
+  if (frstat.st_size !=
+      GNUNET_DISK_fn_read (fn, data, frstat.st_size))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  _("Failed to read blacklist from `%s'\n"), fn);
+      GNUNET_free (fn);
+      GNUNET_free (data);
+      return;
+    }
+  entries_found = 0;
+  pos = 0;
+  while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
+    pos++;
+  while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
+         (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
+    {
+      colon_pos = pos;
+      while ((colon_pos < frstat.st_size) && (data[colon_pos] != ':') && !isspace ( (unsigned char) data[colon_pos]))
+        colon_pos++;
+
+      if (colon_pos >= frstat.st_size)
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                      _("Syntax error in blacklist file at offset %llu, giving up!\n"),
+                      (unsigned long long) colon_pos);
+          GNUNET_free (fn);
+          GNUNET_free (data);
+          return;
+        }
+
+      if (isspace( (unsigned char) data[colon_pos]))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    _("Syntax error in blacklist file at offset %llu, skipping bytes.\n"),
+                    (unsigned long long) colon_pos);
+        pos = colon_pos;
+        while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
+          pos++;
+        continue;
+      }
+      tsize = colon_pos - pos;
+      if ((pos >= frstat.st_size) || (pos + tsize >= frstat.st_size) || (tsize == 0))
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                      _("Syntax error in blacklist file at offset %llu, giving up!\n"),
+                      (unsigned long long) colon_pos);
+          GNUNET_free (fn);
+          GNUNET_free (data);
+          return;
+        }
+
+      if (tsize < 1)
+        continue;
+
+      transport_name = GNUNET_malloc(tsize + 1);
+      memcpy(transport_name, &data[pos], tsize);
+      pos = colon_pos + 1;
+#if DEBUG_TRANSPORT
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  _("Read transport name %s in blacklist file.\n"),
+                  transport_name);
+#endif
+      memcpy (&enc, &data[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
+      if (!isspace ( (unsigned char) enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                      _("Syntax error in blacklist file at offset %llu, skipping bytes.\n"),
+                      (unsigned long long) pos);
+          pos++;
+          while ((pos < frstat.st_size) && (!isspace ( (unsigned char) data[pos])))
+            pos++;
+          GNUNET_free_non_null(transport_name);
+          continue;
+        }
+      enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
+      if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &pid.hashPubKey))
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                      _("Syntax error in blacklist file at offset %llu, skipping bytes `%s'.\n"),
+                      (unsigned long long) pos,
+                      &enc);
+        }
+      else
+        {
+          if (0 != memcmp (&pid,
+                           &my_identity,
+                           sizeof (struct GNUNET_PeerIdentity)))
+            {
+              entries_found++;
+              add_peer_to_blacklist (&pid,
+                                     transport_name);
+            }
+          else
+            {
+              GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                          _("Found myself `%s' in blacklist (useless, ignored)\n"),
+                          GNUNET_i2s (&pid));
+            }
+        }
+      pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
+      GNUNET_free_non_null(transport_name);
+      while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
+        pos++;
+    }
+  GNUNET_free (data);
+  GNUNET_free (fn);
+}
+
 
 /**
  * Function called to notify a client about the socket being ready to
@@ -982,6 +1242,32 @@ transmit_to_client_callback (void *cls, size_t size, void *buf)
 }
 
 
+/**
+ * Convert an address to a string.
+ *
+ * @param plugin name of the plugin responsible for the address
+ * @param addr binary address
+ * @param addr_len number of bytes in addr
+ * @return NULL on error, otherwise address string
+ */
+static const char*
+a2s (const char *plugin,
+     const void *addr,
+     uint16_t addr_len)
+{
+  struct TransportPlugin *p;
+
+  if (plugin == NULL)
+    return NULL;
+  p = find_transport (plugin);
+  if (p == NULL)
+    return NULL;
+  return p->api->address_to_string (p->api->cls,
+                                   addr,
+                                   addr_len);
+}   
+
+
 /**
  * Mark the given FAL entry as 'connected' (and hence preferred for
  * sending); also mark all others for the same peer as 'not connected'
@@ -1004,9 +1290,20 @@ mark_address_connected (struct ForeignAddressList *fal)
     {
       if (GNUNET_YES == pos->connected)
        {
+#if DEBUG_TRANSPORT
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                     "Marking address `%s' as no longer connected (due to connect on other address)\n",
+                     a2s (pos->ready_list->plugin->short_name,
+                          pos->addr,
+                          pos->addrlen));
+#endif
          GNUNET_break (cnt == GNUNET_YES);
          cnt = GNUNET_NO;
          pos->connected = GNUNET_NO;
+         GNUNET_STATISTICS_update (stats,
+                                   gettext_noop ("# connected addresses"),
+                                   -1,
+                                   GNUNET_NO);
        }
       pos = pos->next;
     }
@@ -1042,9 +1339,15 @@ transmit_to_client (struct TransportClient *client,
     {
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                   _
-                  ("Dropping message, have %u messages pending (%u is the soft limit)\n"),
-                  client->message_count, MAX_PENDING);
-      /* TODO: call to statistics... */
+                  ("Dropping message of type %u and size %u, have %u messages pending (%u is the soft limit)\n"),
+                 ntohs (msg->type),
+                 ntohs (msg->size),
+                  client->message_count, 
+                 MAX_PENDING);
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# messages dropped due to slow client"),
+                               1,
+                               GNUNET_NO);
       return;
     }
   msize = ntohs (msg->size);
@@ -1148,6 +1451,13 @@ transmit_send_continuation (void *cls,
        {
          if (mq->specific_address->connected != GNUNET_NO)
            {
+#if DEBUG_TRANSPORT
+             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                         "Marking address `%s' as no longer connected (due to transmission problem)\n",
+                         a2s (mq->specific_address->ready_list->plugin->short_name,
+                              mq->specific_address->addr,
+                              mq->specific_address->addrlen));
+#endif
              GNUNET_STATISTICS_update (stats,
                                        gettext_noop ("# connected addresses"),
                                        -1,
@@ -1212,8 +1522,9 @@ find_ready_address(struct NeighbourList *neighbour)
          if (addresses->addr != NULL)
            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                        "Have address `%s' for peer `%4s' (status: %d, %d, %d, %u, %llums, %u)\n",
-                       GNUNET_a2s (addresses->addr,
-                                   addresses->addrlen),
+                       a2s (head->plugin->short_name,
+                            addresses->addr,
+                            addresses->addrlen),
                        GNUNET_i2s (&neighbour->id),
                        addresses->connected,
                        addresses->in_transmit,
@@ -1239,7 +1550,12 @@ find_ready_address(struct NeighbourList *neighbour)
     {
 #if DEBUG_TRANSPORT
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Best address found has latency of %llu ms.\n",
+                  "Best address found (`%s') has latency of %llu ms.\n",
+                 (best_address->addrlen > 0) 
+                 ? a2s (best_address->ready_list->plugin->short_name,
+                      best_address->addr,
+                      best_address->addrlen)
+                 : "<inbound>",
                   best_address->latency.value);
 #endif
     }
@@ -1374,8 +1690,9 @@ try_transmission_to_peer (struct NeighbourList *neighbour)
               mq->message_buf_size,
               GNUNET_i2s (&neighbour->id), 
              (mq->specific_address->addr != NULL)
-             ? GNUNET_a2s (mq->specific_address->addr,
-                           mq->specific_address->addrlen)
+             ? a2s (mq->plugin->short_name,
+                    mq->specific_address->addr,
+                    mq->specific_address->addrlen)
              : "<inbound>",
              rl->plugin->short_name);
 #endif
@@ -1458,6 +1775,7 @@ transmit_to_peer (struct TransportClient *client,
   mq = GNUNET_malloc (sizeof (struct MessageQueue) + message_buf_size);
   mq->specific_address = peer_address;
   mq->client = client;
+  /* FIXME: this memcpy can be up to 7% of our total runtime! */
   memcpy (&mq[1], message_buf, message_buf_size);
   mq->message_buf = (const char*) &mq[1];
   mq->message_buf_size = message_buf_size;
@@ -1510,7 +1828,7 @@ address_generator (void *cls, size_t max, void *buf)
     }
   ret = GNUNET_HELLO_add_address (gc->plug_pos->short_name,
                                   gc->expiration,
-                                  gc->addr_pos->addr,
+                                  &gc->addr_pos[1],
                                   gc->addr_pos->addrlen, buf, max);
   gc->addr_pos = gc->addr_pos->next;
   return ret;
@@ -1552,8 +1870,7 @@ refresh_hello ()
 
   GNUNET_free_non_null (our_hello);
   our_hello = hello;
-  our_hello_version++;
-  GNUNET_PEERINFO_add_peer (cfg, sched, &my_identity, our_hello);
+  GNUNET_PEERINFO_add_peer (peerinfo, our_hello);
   npos = neighbours;
   while (npos != NULL)
     {
@@ -1747,6 +2064,12 @@ plugin_env_session_end  (void *cls,
     rl->addresses = pos->next;
   else
     prev->next = pos->next;
+  if (GNUNET_SCHEDULER_NO_TASK != pos->revalidate_task)
+    {
+      GNUNET_SCHEDULER_cancel (sched,
+                              pos->revalidate_task);
+      pos->revalidate_task = GNUNET_SCHEDULER_NO_TASK;
+    }
   GNUNET_free (pos);
   if (nl->received_pong == GNUNET_NO)
     return; /* nothing to do */
@@ -1779,7 +2102,7 @@ static void
 plugin_env_notify_address (void *cls,
                            const char *name,
                            const void *addr,
-                           size_t addrlen,
+                           uint16_t addrlen,
                            struct GNUNET_TIME_Relative expires)
 {
   struct TransportPlugin *p = cls;
@@ -1802,7 +2125,6 @@ plugin_env_notify_address (void *cls,
     }
 
   al = GNUNET_malloc (sizeof (struct OwnAddressList) + addrlen);
-  al->addr = &al[1];
   al->next = p->addresses;
   p->addresses = al;
   al->expires = abex;
@@ -1895,7 +2217,7 @@ find_peer_address(struct NeighbourList *neighbour,
                  const char *tname,
                  struct Session *session,
                  const char *addr,
-                 size_t addrlen)
+                 uint16_t addrlen)
 {
   struct ReadyList *head;
   struct ForeignAddressList *pos;
@@ -1941,7 +2263,7 @@ add_peer_address (struct NeighbourList *neighbour,
                  const char *tname,
                  struct Session *session,
                  const char *addr, 
-                 size_t addrlen)
+                 uint16_t addrlen)
 {
   struct ReadyList *head;
   struct ForeignAddressList *ret;
@@ -1950,6 +2272,7 @@ add_peer_address (struct NeighbourList *neighbour,
   if (ret != NULL)
     return ret;
   head = neighbour->plugins;
+
   while (head != NULL)
     {
       if (0 == strcmp (tname, head->plugin->short_name))
@@ -2053,14 +2376,15 @@ struct CheckAddressExistsClosure
   struct Session *session;
 
   /**
-   * Length of addr.
+   * Set to GNUNET_YES if the address exists.
    */
-  size_t addrlen;
+  int exists;
 
   /**
-   * Set to GNUNET_YES if the address exists.
+   * Length of addr.
    */
-  int exists;
+  uint16_t addrlen;
+
 };
 
 
@@ -2103,6 +2427,42 @@ check_address_exists (void *cls,
 }
 
 
+
+/**
+ * Iterator to free entries in the validation_map.
+ *
+ * @param cls closure (unused)
+ * @param key current key code
+ * @param value value in the hash map (validation to abort)
+ * @return GNUNET_YES (always)
+ */
+static int 
+abort_validation (void *cls,
+                 const GNUNET_HashCode * key,
+                 void *value)
+{
+  struct ValidationEntry *va = value;
+
+  if (GNUNET_SCHEDULER_NO_TASK != va->timeout_task)
+    GNUNET_SCHEDULER_cancel (sched, va->timeout_task);
+  GNUNET_free (va->transport_name);
+  if (va->chvc != NULL)
+    {
+      va->chvc->ve_count--;
+      if (va->chvc->ve_count == 0)
+       {
+         GNUNET_CONTAINER_DLL_remove (chvc_head,
+                                      chvc_tail,
+                                      va->chvc);
+         GNUNET_free (va->chvc);
+       }
+      va->chvc = NULL;
+    }
+  GNUNET_free (va);
+  return GNUNET_YES;
+}
+
+
 /**
  * HELLO validation cleanup task (validation failed).
  *
@@ -2115,6 +2475,7 @@ timeout_hello_validation (void *cls, const struct GNUNET_SCHEDULER_TaskContext *
   struct ValidationEntry *va = cls;
   struct GNUNET_PeerIdentity pid;
 
+  va->timeout_task = GNUNET_SCHEDULER_NO_TASK;
   GNUNET_STATISTICS_update (stats,
                            gettext_noop ("# address validation timeouts"),
                            1,
@@ -2123,11 +2484,11 @@ timeout_hello_validation (void *cls, const struct GNUNET_SCHEDULER_TaskContext *
                      sizeof (struct
                              GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
                      &pid.hashPubKey);
-  GNUNET_CONTAINER_multihashmap_remove (validation_map,
-                                       &pid.hashPubKey,
-                                       va);
-  GNUNET_free (va->transport_name);
-  GNUNET_free (va);
+  GNUNET_break (GNUNET_OK ==
+                GNUNET_CONTAINER_multihashmap_remove (validation_map,
+                                                     &pid.hashPubKey,
+                                                     va));
+  abort_validation (NULL, NULL, va);
 }
 
 
@@ -2175,14 +2536,15 @@ static int
 add_to_foreign_address_list (void *cls,
                             const char *tname,
                             struct GNUNET_TIME_Absolute expiration,
-                            const void *addr, size_t addrlen)
+                            const void *addr,
+                            uint16_t addrlen)
 {
   struct NeighbourList *n = cls;
   struct ForeignAddressList *fal;
   int try;
 
   GNUNET_STATISTICS_update (stats,
-                           gettext_noop ("# valid peer addresses returned by peerinfo"),
+                           gettext_noop ("# valid peer addresses returned by PEERINFO"),
                            1,
                            GNUNET_NO);      
   try = GNUNET_NO;
@@ -2191,8 +2553,8 @@ add_to_foreign_address_list (void *cls,
     {
 #if DEBUG_TRANSPORT
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Adding address `%s' (%s) for peer `%4s' due to peerinfo data for %llums.\n",
-                 GNUNET_a2s (addr, addrlen),
+                 "Adding address `%s' (%s) for peer `%4s' due to PEERINFO data for %llums.\n",
+                 a2s (tname, addr, addrlen),
                  tname,
                  GNUNET_i2s (&n->id),
                  expiration.value);
@@ -2250,13 +2612,11 @@ add_to_foreign_address_list (void *cls,
  * @param cls closure ('struct NeighbourList*')
  * @param peer id of the peer, NULL for last call
  * @param h hello message for the peer (can be NULL)
- * @param trust amount of trust we have in the peer (not used)
  */
 static void
 add_hello_for_peer (void *cls,
                    const struct GNUNET_PeerIdentity *peer,
-                   const struct GNUNET_HELLO_Message *h, 
-                   uint32_t trust)
+                   const struct GNUNET_HELLO_Message *h)
 {
   struct NeighbourList *n = cls;
 
@@ -2287,18 +2647,26 @@ add_hello_for_peer (void *cls,
 
 /**
  * Create a fresh entry in our neighbour list for the given peer.
- * Will try to transmit our current HELLO to the new neighbour.
+ * Will try to transmit our current HELLO to the new neighbour. 
+ * Do not call this function directly, use 'setup_peer_check_blacklist.
  *
  * @param peer the peer for which we create the entry
+ * @param do_hello should we schedule transmitting a HELLO
  * @return the new neighbour list entry
  */
 static struct NeighbourList *
-setup_new_neighbour (const struct GNUNET_PeerIdentity *peer)
+setup_new_neighbour (const struct GNUNET_PeerIdentity *peer,
+                    int do_hello)
 {
   struct NeighbourList *n;
   struct TransportPlugin *tp;
   struct ReadyList *rl;
 
+#if DEBUG_TRANSPORT
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Setting up state for neighbour `%4s'\n",
+             GNUNET_i2s (peer));
+#endif
   GNUNET_assert (our_hello != NULL);
   GNUNET_STATISTICS_update (stats,
                            gettext_noop ("# active neighbours"),
@@ -2317,7 +2685,7 @@ setup_new_neighbour (const struct GNUNET_PeerIdentity *peer)
   tp = plugins;
   while (tp != NULL)
     {
-      if (tp->api->send != NULL)
+      if ((tp->api->send != NULL) && (!is_blacklisted(peer, tp)))
         {
           rl = GNUNET_malloc (sizeof (struct ReadyList));
          rl->neighbour = n;
@@ -2333,68 +2701,441 @@ setup_new_neighbour (const struct GNUNET_PeerIdentity *peer)
   n->timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
                                                   GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
                                                   &neighbour_timeout_task, n);
-  n->piter = GNUNET_PEERINFO_iterate (cfg, sched, peer,
-                                     0, GNUNET_TIME_UNIT_FOREVER_REL,
-                                     &add_hello_for_peer, n);
-  transmit_to_peer (NULL, NULL, 0,
-                   HELLO_ADDRESS_EXPIRATION,
-                    (const char *) our_hello, GNUNET_HELLO_size(our_hello),
-                    GNUNET_NO, n);
+  if (do_hello)
+    {
+      n->piter = GNUNET_PEERINFO_iterate (peerinfo, peer,
+                                         GNUNET_TIME_UNIT_FOREVER_REL,
+                                         &add_hello_for_peer, n);
+      transmit_to_peer (NULL, NULL, 0,
+                       HELLO_ADDRESS_EXPIRATION,
+                       (const char *) our_hello, GNUNET_HELLO_size(our_hello),
+                       GNUNET_NO, n);
+    }
   return n;
 }
 
 
 /**
- * Send periodic PING messages to a give foreign address.
+ * Function called after we have checked if communicating
+ * with a given peer is acceptable.  
  *
- * @param cls our 'struct PeriodicValidationContext*'
- * @param tc task context
+ * @param cls closure
+ * @param n NULL if communication is not acceptable
  */
-static void 
-send_periodic_ping (void *cls, 
-                   const struct GNUNET_SCHEDULER_TaskContext *tc)
+typedef void (*SetupContinuation)(void *cls,
+                                 struct NeighbourList *n);
+
+
+/**
+ * Information kept for each client registered to perform
+ * blacklisting.
+ */
+struct Blacklisters
 {
-  struct ForeignAddressList *peer_address = cls;
-  struct TransportPlugin *tp;
-  struct ValidationEntry *va;
-  struct NeighbourList *neighbour;
-  struct TransportPingMessage ping;
-  struct CheckAddressExistsClosure caec;
-  char * message_buf;
-  uint16_t hello_size;
-  size_t tsize;
+  /**
+   * This is a linked list.
+   */
+  struct Blacklisters *next;
 
-  peer_address->revalidate_task = GNUNET_SCHEDULER_NO_TASK;
-  if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
-    return; 
-  tp = peer_address->ready_list->plugin;
-  neighbour = peer_address->ready_list->neighbour;
-  if (GNUNET_YES != neighbour->public_key_valid)
-    {
-      /* no public key yet, try again later */
-      schedule_next_ping (peer_address);     
-      return;
-    }
-  caec.addr = peer_address->addr;
-  caec.addrlen = peer_address->addrlen;
-  caec.tname = tp->short_name;
-  caec.session = peer_address->session;
-  caec.exists = GNUNET_NO;
-  GNUNET_CONTAINER_multihashmap_iterate (validation_map,
-                                         &check_address_exists,
-                                         &caec);
-  if (caec.exists == GNUNET_YES)
-    {
-      /* During validation attempts we will likely trigger the other
-         peer trying to validate our address which in turn will cause
+  /**
+   * This is a linked list.
+   */
+  struct Blacklisters *prev;
+
+  /**
+   * Client responsible for this entry.
+   */
+  struct GNUNET_SERVER_Client *client;
+
+  /**
+   * Blacklist check that we're currently performing.
+   */
+  struct BlacklistCheck *bc;
+
+};
+
+
+/**
+ * Head of DLL of blacklisting clients.
+ */
+static struct Blacklisters *bl_head;
+
+/**
+ * Tail of DLL of blacklisting clients.
+ */
+static struct Blacklisters *bl_tail;
+
+
+/**
+ * Context we use when performing a blacklist check.
+ */
+struct BlacklistCheck
+{
+  
+  /**
+   * This is a linked list.
+   */
+  struct BlacklistCheck *next;
+  
+  /**
+   * This is a linked list.
+   */
+  struct BlacklistCheck *prev;
+
+  /**
+   * Peer being checked.
+   */
+  struct GNUNET_PeerIdentity peer;
+
+  /**
+   * Option for setup neighbour afterwards.
+   */
+  int do_hello;
+
+  /**
+   * Continuation to call with the result.
+   */
+  SetupContinuation cont;
+
+  /**
+   * Closure for cont.
+   */
+  void *cont_cls;
+
+  /**
+   * Current transmission request handle for this client, or NULL if no
+   * request is pending.
+   */
+  struct GNUNET_CONNECTION_TransmitHandle *th;
+
+  /**
+   * Our current position in the blacklisters list.
+   */
+  struct Blacklisters *bl_pos;
+
+  /**
+   * Current task performing the check.
+   */
+  GNUNET_SCHEDULER_TaskIdentifier task;
+
+};
+
+/**
+ * Head of DLL of active blacklisting queries.
+ */
+static struct BlacklistCheck *bc_head;
+
+/**
+ * Tail of DLL of active blacklisting queries.
+ */
+static struct BlacklistCheck *bc_tail;
+
+
+/**
+ * Perform next action in the blacklist check.
+ *
+ * @param cls the 'struct BlacklistCheck*'
+ * @param tc unused 
+ */
+static void
+do_blacklist_check (void *cls,
+                   const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+
+/**
+ * Transmit blacklist query to the client.
+ *
+ * @param cls the 'struct BlacklistCheck'
+ * @param size number of bytes allowed
+ * @param buf where to copy the message
+ * @return number of bytes copied to buf
+ */
+static size_t
+transmit_blacklist_message (void *cls,
+                           size_t size,
+                           void *buf)
+{
+  struct BlacklistCheck *bc = cls;
+  struct Blacklisters *bl;
+  struct BlacklistMessage bm;
+
+  bc->th = NULL;
+  if (size == 0)
+    {
+      GNUNET_assert (bc->task == GNUNET_SCHEDULER_NO_TASK);
+      bc->task = GNUNET_SCHEDULER_add_now (sched,
+                                          &do_blacklist_check,
+                                          bc);
+      return 0;
+    }
+  bl = bc->bl_pos;
+  bm.header.size = htons (sizeof (struct BlacklistMessage));
+  bm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_QUERY);
+  bm.is_allowed = htonl (0);
+  bm.peer = bc->peer;
+  memcpy (buf, &bm, sizeof (bm)); 
+  GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
+  return sizeof (bm);
+}
+
+
+/**
+ * Perform next action in the blacklist check.
+ *
+ * @param cls the 'struct BlacklistCheck*'
+ * @param tc unused 
+ */
+static void
+do_blacklist_check (void *cls,
+                   const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct BlacklistCheck *bc = cls;
+  struct Blacklisters *bl;
+
+  bc->task = GNUNET_SCHEDULER_NO_TASK;
+  bl = bc->bl_pos;
+  if (bl == NULL)
+    {
+      bc->cont (bc->cont_cls,
+               setup_new_neighbour (&bc->peer, bc->do_hello));         
+      GNUNET_free (bc);
+      return;
+    }
+  if (bl->bc == NULL) 
+    {
+      bl->bc = bc;
+      bc->th = GNUNET_SERVER_notify_transmit_ready (bl->client,
+                                                   sizeof (struct BlacklistMessage),
+                                                   GNUNET_TIME_UNIT_FOREVER_REL,
+                                                   &transmit_blacklist_message,
+                                                   bc); 
+    }
+}
+
+
+/**
+ * Obtain a 'struct NeighbourList' for the given peer.  If such an entry
+ * does not yet exist, check the blacklist.  If the blacklist says creating
+ * one is acceptable, create one and call the continuation; otherwise
+ * call the continuation with NULL.
+ *
+ * @param peer peer to setup or look up a struct NeighbourList for
+ * @param do_hello should we also schedule sending our HELLO to the peer
+ *        if this is a new record
+ * @param cont function to call with the 'struct NeigbhbourList*'
+ * @param cont_cls closure for cont
+ */
+static void
+setup_peer_check_blacklist (const struct GNUNET_PeerIdentity *peer,
+                           int do_hello,
+                           SetupContinuation cont,
+                           void *cont_cls)
+{
+  struct NeighbourList *n;
+  struct BlacklistCheck *bc;
+
+  n = find_neighbour(peer);
+  if (n != NULL)
+    {
+      cont (cont_cls, n);
+      return;
+    }
+  if (bl_head == NULL)
+    {
+      cont (cont_cls,
+           setup_new_neighbour (peer, do_hello));
+      return;
+    }
+  bc = GNUNET_malloc (sizeof (struct BlacklistCheck));
+  GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
+  bc->peer = *peer;
+  bc->do_hello = do_hello;
+  bc->cont = cont;
+  bc->cont_cls = cont_cls;
+  bc->bl_pos = bl_head;
+  bc->task = GNUNET_SCHEDULER_add_now (sched,
+                                      &do_blacklist_check,
+                                      bc);
+}
+
+
+/**
+ * Function called with the result of querying a new blacklister about 
+ * it being allowed (or not) to continue to talk to an existing neighbour.
+ *
+ * @param cls the original 'struct NeighbourList'
+ * @param n NULL if we need to disconnect
+ */
+static void
+confirm_or_drop_neighbour (void *cls,
+                          struct NeighbourList *n)
+{
+  struct NeighbourList * orig = cls;
+
+  if (n == NULL)
+    disconnect_neighbour (orig, GNUNET_NO);
+}
+
+
+/**
+ * Handle a request to start a blacklist.
+ *
+ * @param cls closure (always NULL)
+ * @param client identification of the client
+ * @param message the actual message
+ */
+static void
+handle_blacklist_init (void *cls,
+                      struct GNUNET_SERVER_Client *client,
+                      const struct GNUNET_MessageHeader *message)
+{
+  struct Blacklisters *bl;
+  struct BlacklistCheck *bc;
+  struct NeighbourList *n;
+
+  bl = bl_head;
+  while (bl != NULL)
+    {
+      if (bl->client == client)
+       {
+         GNUNET_break (0);
+         GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+         return;
+       }
+      bl = bl->next;
+    }
+  bl = GNUNET_malloc (sizeof (struct Blacklisters));
+  bl->client = client;
+  GNUNET_SERVER_client_keep (client);
+  GNUNET_CONTAINER_DLL_insert_after (bl_head, bl_tail, bl_tail, bl);
+  /* confirm that all existing connections are OK! */
+  n = neighbours;
+  while (NULL != n)
+    {
+      bc = GNUNET_malloc (sizeof (struct BlacklistCheck));
+      GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
+      bc->peer = n->id;
+      bc->do_hello = GNUNET_NO;
+      bc->cont = &confirm_or_drop_neighbour;
+      bc->cont_cls = n;
+      bc->bl_pos = bl;
+      if (n == neighbours) /* all would wait for the same client, no need to
+                             create more than just the first task right now */
+       bc->task = GNUNET_SCHEDULER_add_now (sched,
+                                            &do_blacklist_check,
+                                            bc);
+      n = n->next;
+    }
+}
+
+
+/**
+ * Handle a request to blacklist a peer.
+ *
+ * @param cls closure (always NULL)
+ * @param client identification of the client
+ * @param message the actual message
+ */
+static void
+handle_blacklist_reply (void *cls,
+                       struct GNUNET_SERVER_Client *client,
+                       const struct GNUNET_MessageHeader *message)
+{
+  const struct BlacklistMessage *msg = (const struct BlacklistMessage*) message;
+  struct Blacklisters *bl;
+  struct BlacklistCheck *bc;
+
+  bl = bl_head;
+  while ( (bl != NULL) &&
+         (bl->client != client) )
+    bl = bl->next;  
+  if (bl == NULL)
+    {
+      GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+      return;
+    }
+  bc = bl->bc;
+  bl->bc = NULL;  
+  if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
+    {    
+      bc->cont (bc->cont_cls, NULL);
+      GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
+      GNUNET_free (bc);
+    }
+  else
+    {
+      bc->bl_pos = bc->bl_pos->next;
+      bc->task = GNUNET_SCHEDULER_add_now (sched,
+                                          &do_blacklist_check,
+                                          bc);      
+    }
+  /* check if any other bc's are waiting for this blacklister */
+  bc = bc_head;
+  while (bc != NULL)
+    {
+      if ( (bc->bl_pos == bl) &&
+          (GNUNET_SCHEDULER_NO_TASK == bc->task) )
+       bc->task = GNUNET_SCHEDULER_add_now (sched,
+                                            &do_blacklist_check,
+                                            bc);      
+      bc = bc->next;
+    }
+}
+
+
+/**
+ * Send periodic PING messages to a given foreign address.
+ *
+ * @param cls our 'struct PeriodicValidationContext*'
+ * @param tc task context
+ */
+static void 
+send_periodic_ping (void *cls, 
+                   const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct ForeignAddressList *peer_address = cls;
+  struct TransportPlugin *tp;
+  struct ValidationEntry *va;
+  struct NeighbourList *neighbour;
+  struct TransportPingMessage ping;
+  struct CheckAddressExistsClosure caec;
+  char * message_buf;
+  uint16_t hello_size;
+  size_t slen;
+  size_t tsize;
+
+  peer_address->revalidate_task = GNUNET_SCHEDULER_NO_TASK;
+  if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
+    return; 
+  tp = peer_address->ready_list->plugin;
+  neighbour = peer_address->ready_list->neighbour;
+  if (GNUNET_YES != neighbour->public_key_valid)
+    {
+      /* no public key yet, try again later */
+      schedule_next_ping (peer_address);     
+      return;
+    }
+  caec.addr = peer_address->addr;
+  caec.addrlen = peer_address->addrlen;
+  caec.tname = tp->short_name;
+  caec.session = peer_address->session;
+  caec.exists = GNUNET_NO;
+  GNUNET_CONTAINER_multihashmap_iterate (validation_map,
+                                         &check_address_exists,
+                                         &caec);
+  if (caec.exists == GNUNET_YES)
+    {
+      /* During validation attempts we will likely trigger the other
+         peer trying to validate our address which in turn will cause
          it to send us its HELLO, so we expect to hit this case rather
          frequently.  Only print something if we are very verbose. */
 #if DEBUG_TRANSPORT > 1
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   "Some validation of address `%s' via `%s' for peer `%4s' already in progress.\n",
                  (peer_address->addr != NULL)
-                  ? GNUNET_a2s (peer_address->addr,
-                               peer_address->addrlen)
+                  ? a2s (tp->short_name,
+                        peer_address->addr,
+                        peer_address->addrlen)
                  : "<inbound>",
                   tp->short_name,
                   GNUNET_i2s (&neighbour->id));
@@ -2405,7 +3146,7 @@ send_periodic_ping (void *cls,
   va = GNUNET_malloc (sizeof (struct ValidationEntry) + peer_address->addrlen);
   va->transport_name = GNUNET_strdup (tp->short_name);
   va->challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
-                                            (unsigned int) -1);
+                                            UINT_MAX);
   va->send_time = GNUNET_TIME_absolute_get();
   va->session = peer_address->session;
   if (peer_address->addr != NULL)
@@ -2428,26 +3169,52 @@ send_periodic_ping (void *cls,
                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
   hello_size = GNUNET_HELLO_size(our_hello);
   tsize = sizeof(struct TransportPingMessage) + hello_size;
+  if (peer_address->addr != NULL)
+    {
+      slen = strlen (tp->short_name) + 1;
+      tsize += slen + peer_address->addrlen;
+    }
+  else
+    {
+      slen = 0; /* make gcc happy */
+    }
   message_buf = GNUNET_malloc(tsize);
-  ping.challenge = htonl(va->challenge);
-  ping.header.size = htons(sizeof(struct TransportPingMessage));
   ping.header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_PING);
+  ping.challenge = htonl(va->challenge);
   memcpy(&ping.target, &neighbour->id, sizeof(struct GNUNET_PeerIdentity));
   memcpy(message_buf, our_hello, hello_size);
+  if (peer_address->addr != NULL)
+    {
+      ping.header.size = htons(sizeof(struct TransportPingMessage) + 
+                              peer_address->addrlen + 
+                              slen);
+      memcpy(&message_buf[hello_size + sizeof (struct TransportPingMessage)],
+            tp->short_name, 
+            slen);
+      memcpy(&message_buf[hello_size + sizeof (struct TransportPingMessage) + slen],
+            peer_address->addr, 
+            peer_address->addrlen);
+    }
+  else
+    {
+      ping.header.size = htons(sizeof(struct TransportPingMessage));
+    }
   memcpy(&message_buf[hello_size],
          &ping,
          sizeof(struct TransportPingMessage));
+
 #if DEBUG_TRANSPORT_REVALIDATION
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Performing re-validation of address `%s' via `%s' for peer `%4s' sending `%s' (%u bytes) and `%s' (%u bytes)\n",
+              "Performing re-validation of address `%s' via `%s' for peer `%4s' sending `%s' (%u bytes) and `%s'\n",
               (peer_address->addr != NULL) 
-             ? GNUNET_a2s (peer_address->addr,
-                           peer_address->addrlen)
+             ? a2s (peer_address->plugin->short_name,
+                    peer_address->addr,
+                    peer_address->addrlen)
              : "<inbound>",
               tp->short_name,
               GNUNET_i2s (&neighbour->id),
               "HELLO", hello_size,
-              "PING", sizeof (struct TransportPingMessage));
+              "PING");
 #endif
   GNUNET_STATISTICS_update (stats,
                            gettext_noop ("# PING messages sent for re-validation"),
@@ -2531,8 +3298,9 @@ handle_payload_message (const struct GNUNET_MessageHeader *message,
     }
 #if DEBUG_TRANSPORT
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Received message of type %u from `%4s', sending to all clients.\n",
+             "Received message of type %u and size %u from `%4s', sending to all clients.\n",
              ntohs (message->type), 
+             ntohs (message->size), 
              GNUNET_i2s (&n->id));
 #endif
   if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker,
@@ -2569,6 +3337,7 @@ handle_payload_message (const struct GNUNET_MessageHeader *message,
   im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV);
   im->latency = GNUNET_TIME_relative_hton (n->latency);
   im->peer = n->id;
+  im->distance = ntohl(n->distance);
   memcpy (&im[1], message, msize);
   cpos = clients;
   while (cpos != NULL)
@@ -2603,30 +3372,132 @@ check_pending_validation (void *cls,
   struct GNUNET_PeerIdentity target;
   struct NeighbourList *n;
   struct ForeignAddressList *fal;
+  struct OwnAddressList *oal;
+  struct TransportPlugin *tp;
   struct GNUNET_MessageHeader *prem;
+  uint16_t ps;
+  const char *addr;
+  size_t slen;
+  size_t alen;
 
-  if (ve->challenge != challenge)
-    return GNUNET_YES;
-  if (GNUNET_OK !=
-      GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PING,
-                               &pong->purpose, 
-                               &pong->signature,
-                               &ve->publicKey))
+  ps = ntohs (pong->header.size);
+  if (ps < sizeof (struct TransportPongMessage))
     {
       GNUNET_break_op (0);
-      return GNUNET_YES;
+      return GNUNET_NO;
     }
-
+  addr = (const char*) &pong[1];
+  slen = strlen (ve->transport_name) + 1;
+  if ( (ps - sizeof (struct TransportPongMessage) != ve->addrlen + slen) ||
+       (ve->challenge != challenge) ||       
+       (addr[slen-1] != '\0') ||
+       (0 != strcmp (addr, ve->transport_name)) || 
+       (ntohl (pong->purpose.size) 
+       != sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
+       sizeof (uint32_t) +
+       sizeof (struct GNUNET_TIME_AbsoluteNBO) +
+       sizeof (struct GNUNET_PeerIdentity) + ve->addrlen + slen) )
+    return GNUNET_YES;
+  alen = ps - sizeof (struct TransportPongMessage) - slen;
+  switch (ntohl (pong->purpose.purpose))
+    {
+    case GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN:
+      if ( (ve->addrlen + slen != ntohl (pong->addrlen)) ||
+          (0 != memcmp (&addr[slen],
+                        ve->addr,
+                        ve->addrlen)) )
+       return GNUNET_YES; /* different entry, keep trying! */
+      if (0 != memcmp (&pong->pid,
+                      key,
+                      sizeof (struct GNUNET_PeerIdentity))) 
+       {
+         GNUNET_break_op (0);
+         return GNUNET_NO;
+       }
+      if (GNUNET_OK !=
+         GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN,
+                                   &pong->purpose, 
+                                   &pong->signature,
+                                   &ve->publicKey)) 
+       {
+         GNUNET_break_op (0);
+         return GNUNET_NO;
+       }
 #if DEBUG_TRANSPORT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Confirmed validity of address, peer `%4s' has address `%s' (%s).\n",
-             GNUNET_h2s (key),
-             (ve->addr != NULL) 
-             ? GNUNET_a2s ((const struct sockaddr *) ve->addr,
-                           ve->addrlen)
-             : "<inbound>",
-             ve->transport_name);
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Confirmed validity of address, peer `%4s' has address `%s' (%s).\n",
+                 GNUNET_h2s (key),
+                 a2s (ve->transport_name,
+                      (const struct sockaddr *) ve->addr,
+                      ve->addrlen),
+                 ve->transport_name);
 #endif
+      break;
+    case GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_USING:
+      if (ve->addrlen != 0) 
+       return GNUNET_YES; /* different entry, keep trying */
+      if ( (0 != memcmp (&pong->pid,
+                        &my_identity,
+                        sizeof (struct GNUNET_PeerIdentity))) ||
+          (ve->addrlen != 0) )
+       {
+         GNUNET_break_op (0);
+         return GNUNET_NO;
+       }
+      tp = find_transport (ve->transport_name);
+      if (tp == NULL)
+       {
+         GNUNET_break (0);
+         return GNUNET_YES;
+       }
+      oal = tp->addresses;
+      while (NULL != oal)
+       {
+         if ( (oal->addrlen == alen) &&
+              (0 == memcmp (&oal[1],
+                            &addr[slen],
+                            alen)) )
+           break;
+         oal = oal->next;
+       }
+      if (oal == NULL)
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                     _("Not accepting PONG with address `%s' since I cannot confirm having this address.\n"),
+                     a2s (ve->transport_name,
+                          &addr[slen],
+                          alen));
+         return GNUNET_NO;       
+       }
+      if (GNUNET_OK !=
+         GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_USING,
+                                   &pong->purpose, 
+                                   &pong->signature,
+                                   &ve->publicKey)) 
+       {
+         GNUNET_break_op (0);
+         return GNUNET_NO;
+       }
+#if DEBUG_TRANSPORT
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Confirmed that peer `%4s' is talking to us using address `%s' (%s) for us.\n",
+                 GNUNET_h2s (key),
+                 a2s (ve->transport_name,
+                      &addr[slen],
+                      alen),
+                 ve->transport_name);
+#endif
+      break;
+    default:
+      GNUNET_break_op (0);
+      return GNUNET_NO;
+    }
+  if (GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_ntoh (pong->expiration)).value == 0)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                 _("Received expired signature.  Check system time.\n"));
+      return GNUNET_NO;
+    }
   GNUNET_STATISTICS_update (stats,
                            gettext_noop ("# address validation successes"),
                            1,
@@ -2642,8 +3513,7 @@ check_pending_validation (void *cls,
       hello = GNUNET_HELLO_create (&ve->publicKey,
                                   &add_validated_address,
                                   &avac);
-      GNUNET_PEERINFO_add_peer (cfg, sched,
-                               &target,
+      GNUNET_PEERINFO_add_peer (peerinfo,
                                hello);
       GNUNET_free (hello);
     }
@@ -2671,6 +3541,7 @@ check_pending_validation (void *cls,
        n->latency = fal->latency;
       else
        n->latency.value = (fal->latency.value + n->latency.value) / 2;
+
       n->distance = fal->distance;
       if (GNUNET_NO == n->received_pong)
        {
@@ -2697,10 +3568,7 @@ check_pending_validation (void *cls,
                 GNUNET_CONTAINER_multihashmap_remove (validation_map,
                                                       key,
                                                       ve));
-  GNUNET_SCHEDULER_cancel (sched,
-                          ve->timeout_task);
-  GNUNET_free (ve->transport_name);
-  GNUNET_free (ve);
+  abort_validation (NULL, NULL, ve);
   return GNUNET_NO;
 }
 
@@ -2758,16 +3626,102 @@ handle_pong (void *cls, const struct GNUNET_MessageHeader *message,
 #endif
       return;
     }
-
-#if 0
-  /* FIXME: add given address to potential pool of our addresses
-     (for voting) */
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
-             _("Another peer saw us using the address `%s' via `%s'.\n"),
-             GNUNET_a2s ((const struct sockaddr *) &pong[1],
-                         ntohs(pong->addrlen)),
-             va->transport_name);
+
+}
+
+
+/**
+ * Try to validate a neighbour's address by sending him our HELLO and a PING.
+ *
+ * @param cls the 'struct ValidationEntry*'
+ * @param neighbour neighbour to validate, NULL if validation failed
+ */
+static void
+transmit_hello_and_ping (void *cls,
+                        struct NeighbourList *neighbour)
+{
+  struct ValidationEntry *va = cls;
+  struct ForeignAddressList *peer_address;
+  struct TransportPingMessage ping;
+  uint16_t hello_size;
+  size_t tsize;
+  char * message_buf;
+  struct GNUNET_PeerIdentity id;
+  size_t slen;
+
+  GNUNET_CRYPTO_hash (&va->publicKey,
+                     sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+                     &id.hashPubKey);
+  if (neighbour == NULL)
+    {
+      /* FIXME: stats... */
+      GNUNET_break (GNUNET_OK ==
+                   GNUNET_CONTAINER_multihashmap_remove (validation_map,
+                                                         &id.hashPubKey,
+                                                         va));
+      abort_validation (NULL, NULL, va);
+      return;
+    }
+  neighbour->publicKey = va->publicKey;
+  neighbour->public_key_valid = GNUNET_YES;
+  peer_address = add_peer_address (neighbour,
+                                  va->transport_name, NULL,
+                                  (const void*) &va[1],
+                                  va->addrlen);
+  if (peer_address == NULL)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "Failed to add peer `%4s' for plugin `%s'\n",
+                  GNUNET_i2s (&neighbour->id), 
+                 va->transport_name);
+      GNUNET_break (GNUNET_OK ==
+                   GNUNET_CONTAINER_multihashmap_remove (validation_map,
+                                                         &id.hashPubKey,
+                                                         va));
+      abort_validation (NULL, NULL, va);
+      return;
+    }
+  hello_size = GNUNET_HELLO_size(our_hello);
+  slen = strlen(va->transport_name) + 1;
+  tsize = sizeof(struct TransportPingMessage) + hello_size + va->addrlen + slen;
+  message_buf = GNUNET_malloc(tsize);
+  ping.challenge = htonl(va->challenge);
+  ping.header.size = htons(sizeof(struct TransportPingMessage) + slen + va->addrlen);
+  ping.header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_PING);
+  memcpy(&ping.target, &neighbour->id, sizeof(struct GNUNET_PeerIdentity));
+  memcpy(message_buf, our_hello, hello_size);
+  memcpy(&message_buf[hello_size],
+        &ping,
+        sizeof(struct TransportPingMessage));
+  memcpy(&message_buf[hello_size + sizeof (struct TransportPingMessage)],
+        va->transport_name,
+        slen);
+  memcpy(&message_buf[hello_size + sizeof (struct TransportPingMessage) + slen],
+        &va[1],
+        va->addrlen);
+#if DEBUG_TRANSPORT
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Performing validation of address `%s' via `%s' for peer `%4s' sending `%s' (%u bytes) and `%s' (%u bytes)\n",
+             (va->addrlen == 0) 
+             ? "<inbound>"
+             : a2s (va->transport_name,
+                    (const void*) &va[1], va->addrlen),
+             va->transport_name,
+             GNUNET_i2s (&neighbour->id),
+             "HELLO", hello_size,
+             "PING", sizeof (struct TransportPingMessage) + va->addrlen + slen);
 #endif
+
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# PING messages sent for initial validation"),
+                           1,
+                           GNUNET_NO);      
+  transmit_to_peer (NULL, peer_address,
+                   GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+                   HELLO_VERIFICATION_TIMEOUT,
+                   message_buf, tsize,
+                   GNUNET_YES, neighbour);
+  GNUNET_free(message_buf);
 }
 
 
@@ -2787,22 +3741,19 @@ static int
 run_validation (void *cls,
                 const char *tname,
                 struct GNUNET_TIME_Absolute expiration,
-                const void *addr, size_t addrlen)
+                const void *addr, 
+               uint16_t addrlen)
 {
   struct CheckHelloValidatedContext *chvc = cls;
   struct GNUNET_PeerIdentity id;
   struct TransportPlugin *tp;
   struct ValidationEntry *va;
-  struct NeighbourList *neighbour;
-  struct ForeignAddressList *peer_address;
-  struct TransportPingMessage ping;
   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk;
   struct CheckAddressExistsClosure caec;
-  char * message_buf;
-  uint16_t hello_size;
-  size_t tsize;
+  struct OwnAddressList *oal;
 
   GNUNET_assert (addr != NULL);
+
   GNUNET_STATISTICS_update (stats,
                            gettext_noop ("# peer addresses scheduled for validation"),
                            1,
@@ -2816,18 +3767,49 @@ run_validation (void *cls,
                   ("Transport `%s' not loaded, will not try to validate peer address using this transport.\n"),
                   tname);
       GNUNET_STATISTICS_update (stats,
-                               gettext_noop ("# peer addresses not validated (no applicable transport plugin available)"),
+                               gettext_noop ("# peer addresses not validated (plugin not available)"),
                                1,
                                GNUNET_NO);      
       return GNUNET_OK;
     }
+  /* check if this is one of our own addresses */
+  oal = tp->addresses;
+  while (NULL != oal)
+    {
+      if ( (oal->addrlen == addrlen) &&
+          (0 == memcmp (&oal[1],
+                        addr,
+                        addrlen)) )
+       {
+         /* not plausible, this address is equivalent to our own address! */
+         GNUNET_STATISTICS_update (stats,
+                                   gettext_noop ("# peer addresses not validated (loopback)"),
+                                   1,
+                                   GNUNET_NO);      
+         return GNUNET_OK;
+       }
+      oal = oal->next;
+    }
   GNUNET_HELLO_get_key (chvc->hello, &pk);
   GNUNET_CRYPTO_hash (&pk,
                       sizeof (struct
                               GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
                       &id.hashPubKey);
+
+  if (is_blacklisted(&id, tp))
+    {
+#if DEBUG_TRANSPORT
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Attempted to validate blacklisted peer `%s' using `%s'!\n", 
+                 GNUNET_i2s(&id), 
+                 tname);
+#endif
+      return GNUNET_OK;
+    }
+
   caec.addr = addr;
   caec.addrlen = addrlen;
+  caec.session = NULL;
   caec.tname = tname;
   caec.exists = GNUNET_NO;
   GNUNET_CONTAINER_multihashmap_iterate (validation_map,
@@ -2842,7 +3824,7 @@ run_validation (void *cls,
 #if DEBUG_TRANSPORT > 1
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                  "Validation of address `%s' via `%s' for peer `%4s' already in progress.\n",
-                 GNUNET_a2s (addr, addrlen),
+                 a2s (tname, addr, addrlen),
                  tname,
                  GNUNET_i2s (&id));
 #endif
@@ -2853,9 +3835,11 @@ run_validation (void *cls,
       return GNUNET_OK;
     }
   va = GNUNET_malloc (sizeof (struct ValidationEntry) + addrlen);
+  va->chvc = chvc;
+  chvc->ve_count++;
   va->transport_name = GNUNET_strdup (tname);
   va->challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
-                                            (unsigned int) -1);
+                                            UINT_MAX);
   va->send_time = GNUNET_TIME_absolute_get();
   va->addr = (const void*) &va[1];
   memcpy (&va[1], addr, addrlen);
@@ -2870,43 +3854,9 @@ run_validation (void *cls,
                                     &id.hashPubKey,
                                     va,
                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
-  neighbour = find_neighbour(&id);
-  if (neighbour == NULL)
-    neighbour = setup_new_neighbour(&id);
-  neighbour->publicKey = va->publicKey;
-  neighbour->public_key_valid = GNUNET_YES;
-  peer_address = add_peer_address (neighbour, tname, NULL, addr, addrlen);
-  GNUNET_assert(peer_address != NULL);
-  hello_size = GNUNET_HELLO_size(our_hello);
-  tsize = sizeof(struct TransportPingMessage) + hello_size;
-  message_buf = GNUNET_malloc(tsize);
-  ping.challenge = htonl(va->challenge);
-  ping.header.size = htons(sizeof(struct TransportPingMessage));
-  ping.header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_PING);
-  memcpy(&ping.target, &id, sizeof(struct GNUNET_PeerIdentity));
-  memcpy(message_buf, our_hello, hello_size);
-  memcpy(&message_buf[hello_size],
-        &ping,
-        sizeof(struct TransportPingMessage));
-#if DEBUG_TRANSPORT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Performing validation of address `%s' via `%s' for peer `%4s' sending `%s' (%u bytes) and `%s' (%u bytes)\n",
-              GNUNET_a2s (addr, addrlen),
-             tname,
-             GNUNET_i2s (&id),
-             "HELLO", hello_size,
-             "PING", sizeof (struct TransportPingMessage));
-#endif
-  GNUNET_STATISTICS_update (stats,
-                           gettext_noop ("# PING messages sent for initial validation"),
-                           1,
-                           GNUNET_NO);      
-  transmit_to_peer (NULL, peer_address,
-                   GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-                   HELLO_VERIFICATION_TIMEOUT,
-                   message_buf, tsize,
-                   GNUNET_YES, neighbour);
-  GNUNET_free(message_buf);
+  setup_peer_check_blacklist (&id, GNUNET_NO,
+                             &transmit_hello_and_ping,
+                             va);
   return GNUNET_OK;
 }
 
@@ -2918,13 +3868,11 @@ run_validation (void *cls,
  * @param cls closure
  * @param peer id of the peer, NULL for last call
  * @param h hello message for the peer (can be NULL)
- * @param trust amount of trust we have in the peer (not used)
  */
 static void
 check_hello_validated (void *cls,
                        const struct GNUNET_PeerIdentity *peer,
-                       const struct GNUNET_HELLO_Message *h, 
-                      uint32_t trust)
+                       const struct GNUNET_HELLO_Message *h)
 {
   struct CheckHelloValidatedContext *chvc = cls;
   struct GNUNET_HELLO_Message *plain_hello;
@@ -2935,9 +3883,6 @@ check_hello_validated (void *cls,
   if (peer == NULL)
     {
       chvc->piter = NULL;
-      GNUNET_CONTAINER_DLL_remove (chvc_head,
-                                  chvc_tail,
-                                  chvc);
       if (GNUNET_NO == chvc->hello_known)
        {
          /* notify PEERINFO about the peer now, so that we at least
@@ -2949,11 +3894,11 @@ check_hello_validated (void *cls,
          plain_hello = GNUNET_HELLO_create (&pk,
                                             NULL, 
                                             NULL);
-         GNUNET_PEERINFO_add_peer (cfg, sched, &target, plain_hello);
+         GNUNET_PEERINFO_add_peer (peerinfo, plain_hello);
          GNUNET_free (plain_hello);
 #if DEBUG_TRANSPORT
          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                     "Peerinfo had no `%s' message for peer `%4s', full validation needed.\n",
+                     "PEERINFO had no `%s' message for peer `%4s', full validation needed.\n",
                      "HELLO",
                      GNUNET_i2s (&target));
 #endif
@@ -2973,14 +3918,21 @@ check_hello_validated (void *cls,
                                    1,
                                    GNUNET_NO);      
        }
-      GNUNET_free (chvc);
+      chvc->ve_count--;
+      if (chvc->ve_count == 0)
+       {
+         GNUNET_CONTAINER_DLL_remove (chvc_head,
+                                      chvc_tail,
+                                      chvc);
+         GNUNET_free (chvc);     
+       }
       return;
     } 
   if (h == NULL)
     return;
 #if DEBUG_TRANSPORT
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Peerinfo had `%s' message for peer `%4s', validating only new addresses.\n",
+             "PEERINFO had `%s' message for peer `%4s', validating only new addresses.\n",
              "HELLO",
              GNUNET_i2s (peer));
 #endif
@@ -3012,6 +3964,7 @@ check_hello_validated (void *cls,
                                      chvc);
 }
 
+
 /**
  * Process HELLO-message.
  *
@@ -3028,7 +3981,9 @@ process_hello (struct TransportPlugin *plugin,
   const struct GNUNET_HELLO_Message *hello;
   struct CheckHelloValidatedContext *chvc;
   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey;
-
+#if DEBUG_TRANSPORT_HELLO
+  char *my_id;
+#endif
   hsize = ntohs (message->size);
   if ((ntohs (message->type) != GNUNET_MESSAGE_TYPE_HELLO) ||
       (hsize < sizeof (struct GNUNET_MessageHeader)))
@@ -3040,6 +3995,7 @@ process_hello (struct TransportPlugin *plugin,
                            gettext_noop ("# HELLOs received for validation"),
                            1,
                            GNUNET_NO);      
+
   /* first, check if load is too high */
   if (GNUNET_SCHEDULER_get_load (sched,
                                 GNUNET_SCHEDULER_PRIORITY_BACKGROUND) > MAX_HELLO_LOAD)
@@ -3048,17 +4004,31 @@ process_hello (struct TransportPlugin *plugin,
                                gettext_noop ("# HELLOs ignored due to high load"),
                                1,
                                GNUNET_NO);      
+#if DEBUG_TRANSPORT_HELLO
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Ignoring `%s' for `%4s', load too high.\n",
+                  "HELLO",
+                  GNUNET_i2s (&target));
+#endif
       return GNUNET_OK;
     }
   hello = (const struct GNUNET_HELLO_Message *) message;
   if (GNUNET_OK != GNUNET_HELLO_get_key (hello, &publicKey))
     {
+#if DEBUG_TRANSPORT_HELLO
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Unable to get public key from `%s' for `%4s'!\n",
+                  "HELLO",
+                  GNUNET_i2s (&target));
+#endif
       GNUNET_break_op (0);
       return GNUNET_SYSERR;
     }
+
   GNUNET_CRYPTO_hash (&publicKey,
                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
                       &target.hashPubKey);
+
   if (0 == memcmp (&my_identity,
                   &target,
                   sizeof (struct GNUNET_PeerIdentity)))
@@ -3069,14 +4039,42 @@ process_hello (struct TransportPlugin *plugin,
                                GNUNET_NO);      
       return GNUNET_OK;      
     }
-#if DEBUG_TRANSPORT > 1
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Processing `%s' message for `%4s' of size %u\n",
-              "HELLO", 
-             GNUNET_i2s (&target), 
-             GNUNET_HELLO_size(hello));
+  chvc = chvc_head;
+  while (NULL != chvc)
+    {
+      if (GNUNET_HELLO_equals (hello,
+                              chvc->hello,
+                              GNUNET_TIME_absolute_get ()).value > 0)
+       {
+#if DEBUG_TRANSPORT_HELLO
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                     "Received duplicate `%s' message for `%4s'; ignored\n",
+                     "HELLO", 
+                     GNUNET_i2s (&target));
+#endif
+         return GNUNET_OK; /* validation already pending */
+       }
+      if (GNUNET_HELLO_size(hello) == GNUNET_HELLO_size (chvc->hello))
+       GNUNET_break (0 != memcmp (hello, chvc->hello,
+                                  GNUNET_HELLO_size(hello)));
+      chvc = chvc->next;
+    }
+#if DEBUG_TRANSPORT_HELLO
+  if (plugin != NULL)
+    {
+      my_id = GNUNET_strdup(GNUNET_i2s(plugin->env.my_identity));
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "%s: Starting validation of `%s' message for `%4s' via '%s' of size %u\n",
+                  my_id,
+                  "HELLO",
+                  GNUNET_i2s (&target),
+                  plugin->short_name,
+                  GNUNET_HELLO_size(hello));
+      GNUNET_free(my_id);
+    }
 #endif
   chvc = GNUNET_malloc (sizeof (struct CheckHelloValidatedContext) + hsize);
+  chvc->ve_count = 1;
   chvc->hello = (const struct GNUNET_HELLO_Message *) &chvc[1];
   memcpy (&chvc[1], hello, hsize);
   GNUNET_CONTAINER_DLL_insert (chvc_head,
@@ -3084,10 +4082,8 @@ process_hello (struct TransportPlugin *plugin,
                               chvc);
   /* finally, check if HELLO was previously validated
      (continuation will then schedule actual validation) */
-  chvc->piter = GNUNET_PEERINFO_iterate (cfg,
-                                         sched,
+  chvc->piter = GNUNET_PEERINFO_iterate (peerinfo,
                                          &target,
-                                         0,
                                          HELLO_VERIFICATION_TIMEOUT,
                                          &check_hello_validated, chvc);
   return GNUNET_OK;
@@ -3236,21 +4232,28 @@ disconnect_neighbour (struct NeighbourList *n, int check)
 static int 
 handle_ping(void *cls, const struct GNUNET_MessageHeader *message,
            const struct GNUNET_PeerIdentity *peer,
+           struct Session *session,
            const char *sender_address,
-           size_t sender_address_len)
+           uint16_t sender_address_len)
 {
   struct TransportPlugin *plugin = cls;
+  struct SessionHeader *session_header = (struct SessionHeader*) session;
   struct TransportPingMessage *ping;
   struct TransportPongMessage *pong;
   struct NeighbourList *n;
   struct ReadyList *rl;
   struct ForeignAddressList *fal;
+  struct OwnAddressList *oal;
+  const char *addr;
+  size_t alen;
+  size_t slen;
 
-  if (ntohs (message->size) != sizeof (struct TransportPingMessage))
+  if (ntohs (message->size) < sizeof (struct TransportPingMessage))
     {
       GNUNET_break_op (0);
       return GNUNET_SYSERR;
     }
+
   ping = (struct TransportPingMessage *) message;
   if (0 != memcmp (&ping->target,
                    plugin->env.my_identity,
@@ -3261,34 +4264,157 @@ handle_ping(void *cls, const struct GNUNET_MessageHeader *message,
                  "PING");
       return GNUNET_SYSERR;
     }
-#if DEBUG_TRANSPORT
+#if DEBUG_PING_PONG
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
              "Processing `%s' from `%s'\n",
              "PING", 
-             GNUNET_a2s ((const struct sockaddr *)sender_address, 
-                         sender_address_len));
+             (sender_address != NULL) 
+             ? a2s (plugin->short_name,
+                    (const struct sockaddr *)sender_address, 
+                    sender_address_len)
+             : "<inbound>");
 #endif
   GNUNET_STATISTICS_update (stats,
                            gettext_noop ("# PING messages received"),
                            1,
                            GNUNET_NO);
-  pong = GNUNET_malloc (sizeof (struct TransportPongMessage) + sender_address_len);
-  pong->header.size = htons (sizeof (struct TransportPongMessage) + sender_address_len);
-  pong->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG);
-  pong->purpose.size =
-    htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
-           sizeof (uint32_t) +
-           sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) + sender_address_len);
-  pong->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PING);
-  pong->challenge = ping->challenge;
-  pong->addrlen = htons(sender_address_len);
-  memcpy(&pong->signer, 
-        &my_public_key, 
-        sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
-  memcpy (&pong[1], sender_address, sender_address_len);
-  GNUNET_assert (GNUNET_OK ==
-                 GNUNET_CRYPTO_rsa_sign (my_private_key,
-                                         &pong->purpose, &pong->signature));
+  addr = (const char*) &ping[1];
+  alen = ntohs (message->size) - sizeof (struct TransportPingMessage);
+  slen = strlen (plugin->short_name) + 1;
+  if (alen == 0)
+    {      
+      /* peer wants to confirm that we have an outbound connection to him */
+      if (session == NULL)
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                     _("Refusing to create PONG since I do not have a session with `%s'.\n"),
+                     GNUNET_i2s (peer));
+         return GNUNET_SYSERR;
+       }
+      pong = GNUNET_malloc (sizeof (struct TransportPongMessage) + sender_address_len + slen);
+      pong->header.size = htons (sizeof (struct TransportPongMessage) + sender_address_len + slen);
+      pong->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG);
+      pong->purpose.size =
+       htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
+              sizeof (uint32_t) +
+              sizeof (struct GNUNET_TIME_AbsoluteNBO) +
+              sizeof (struct GNUNET_PeerIdentity) + sender_address_len + slen);
+      pong->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_USING);
+      pong->challenge = ping->challenge;
+      pong->addrlen = htonl(sender_address_len + slen);
+      memcpy(&pong->pid, 
+            peer,
+            sizeof(struct GNUNET_PeerIdentity));
+      memcpy (&pong[1], 
+             plugin->short_name, 
+             slen);
+      memcpy (&((char*)&pong[1])[slen], 
+             sender_address, 
+             sender_address_len);
+      if (GNUNET_TIME_absolute_get_remaining (session_header->pong_sig_expires).value < PONG_SIGNATURE_LIFETIME.value / 4)
+       {
+         /* create / update cached sig */
+#if DEBUG_TRANSPORT
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                     "Creating PONG signature to indicate active connection.\n");
+#endif
+         session_header->pong_sig_expires = GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME);
+         pong->expiration = GNUNET_TIME_absolute_hton (session_header->pong_sig_expires);
+         GNUNET_assert (GNUNET_OK ==
+                        GNUNET_CRYPTO_rsa_sign (my_private_key,
+                                                &pong->purpose,
+                                                &session_header->pong_signature));
+       }
+      else
+       {
+         pong->expiration = GNUNET_TIME_absolute_hton (session_header->pong_sig_expires);
+       }
+      memcpy (&pong->signature,
+             &session_header->pong_signature,
+             sizeof (struct GNUNET_CRYPTO_RsaSignature));    
+
+
+    }
+  else
+    {
+      /* peer wants to confirm that this is one of our addresses */
+      addr += slen;
+      alen -= slen;
+      if (GNUNET_OK !=
+         plugin->api->check_address (plugin->api->cls,
+                                     addr,
+                                     alen))
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                     _("Not confirming PING with address `%s' since I cannot confirm having this address.\n"),
+                     a2s (plugin->short_name,
+                          addr,
+                          alen));
+         return GNUNET_NO;
+       }
+      oal = plugin->addresses;
+      while (NULL != oal)
+       {
+         if ( (oal->addrlen == alen) &&
+              (0 == memcmp (addr,
+                            &oal[1],
+                            alen)) )
+           break;
+         oal = oal->next;
+       }
+      pong = GNUNET_malloc (sizeof (struct TransportPongMessage) + alen + slen);
+      pong->header.size = htons (sizeof (struct TransportPongMessage) + alen + slen);
+      pong->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG);
+      pong->purpose.size =
+       htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
+              sizeof (uint32_t) +
+              sizeof (struct GNUNET_TIME_AbsoluteNBO) +
+              sizeof (struct GNUNET_PeerIdentity) + alen + slen);
+      pong->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN);
+      pong->challenge = ping->challenge;
+      pong->addrlen = htonl(alen + slen);
+      memcpy(&pong->pid, 
+            &my_identity, 
+            sizeof(struct GNUNET_PeerIdentity));
+      memcpy (&pong[1], plugin->short_name, slen);
+      memcpy (&((char*)&pong[1])[slen], addr, alen);
+      if ( (oal != NULL) &&
+          (GNUNET_TIME_absolute_get_remaining (oal->pong_sig_expires).value < PONG_SIGNATURE_LIFETIME.value / 4) )
+       {
+         /* create / update cached sig */
+#if DEBUG_TRANSPORT
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                     "Creating PONG signature to indicate ownership.\n");
+#endif
+         oal->pong_sig_expires = GNUNET_TIME_absolute_min (oal->expires,
+                                                           GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME));
+         pong->expiration = GNUNET_TIME_absolute_hton (oal->pong_sig_expires);
+         GNUNET_assert (GNUNET_OK ==
+                        GNUNET_CRYPTO_rsa_sign (my_private_key,
+                                                &pong->purpose,
+                                                &oal->pong_signature));            
+         memcpy (&pong->signature,
+                 &oal->pong_signature,
+                 sizeof (struct GNUNET_CRYPTO_RsaSignature));    
+       }
+      else if (oal == NULL)
+       {
+         /* not using cache (typically DV-only) */
+         pong->expiration = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME));
+         GNUNET_assert (GNUNET_OK ==
+                        GNUNET_CRYPTO_rsa_sign (my_private_key,
+                                                &pong->purpose,
+                                                &pong->signature));        
+       }
+      else
+       {
+         /* can used cached version */
+         pong->expiration = GNUNET_TIME_absolute_hton (oal->pong_sig_expires);
+         memcpy (&pong->signature,
+                 &oal->pong_signature,
+                 sizeof (struct GNUNET_CRYPTO_RsaSignature));    
+       }
+    }
   n = find_neighbour(peer);
   GNUNET_assert (n != NULL);
   /* first try reliable response transmission */
@@ -3369,10 +4495,10 @@ handle_ping(void *cls, const struct GNUNET_MessageHeader *message,
 static struct GNUNET_TIME_Relative
 plugin_env_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
                     const struct GNUNET_MessageHeader *message,
-                    unsigned int distance,
+                    uint32_t distance,
                    struct Session *session,
                    const char *sender_address,
-                    size_t sender_address_len)
+                    uint16_t sender_address_len)
 {
   struct TransportPlugin *plugin = cls;
   struct ReadyList *service_context;
@@ -3381,9 +4507,12 @@ plugin_env_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
   struct NeighbourList *n;
   struct GNUNET_TIME_Relative ret;
 
+  if (is_blacklisted (peer, plugin))
+    return GNUNET_TIME_UNIT_FOREVER_REL;
+
   n = find_neighbour (peer);
   if (n == NULL)
-    n = setup_new_neighbour (peer);
+    n = setup_new_neighbour (peer, GNUNET_YES);
   service_context = n->plugins;
   while ((service_context != NULL) && (plugin != service_context->plugin))
     service_context = service_context->next;
@@ -3440,6 +4569,13 @@ plugin_env_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
                                    GNUNET_NO);
          return GNUNET_CONSTANTS_QUOTA_VIOLATION_TIMEOUT;
        }
+#if DEBUG_PING_PONG
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "Received message of type %u and size %u from `%4s', sending to all clients.\n",
+                      ntohs (message->type), 
+                      ntohs (message->size), 
+                     GNUNET_i2s (peer));
+#endif
       switch (ntohs (message->type))
        {
        case GNUNET_MESSAGE_TYPE_HELLO:
@@ -3450,7 +4586,7 @@ plugin_env_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
          process_hello (plugin, message);
          break;
        case GNUNET_MESSAGE_TYPE_TRANSPORT_PING:
-         handle_ping (plugin, message, peer, sender_address, sender_address_len);
+         handle_ping (plugin, message, peer, session, sender_address, sender_address_len);
          break;
        case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG:
          handle_pong (plugin, message, peer, sender_address, sender_address_len);
@@ -3476,7 +4612,6 @@ plugin_env_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
   return ret;
 }
 
-
 /**
  * Handle START-message.  This is the first message sent to us
  * by any client which causes us to add it to our list.
@@ -3566,6 +4701,64 @@ handle_hello (void *cls,
 }
 
 
+/**
+ * Closure for 'transmit_client_message'; followed by
+ * 'msize' bytes of the actual message.
+ */
+struct TransmitClientMessageContext 
+{
+  /**
+   * Client on whom's behalf we are sending.
+   */
+  struct GNUNET_SERVER_Client *client;
+
+  /**
+   * Timeout for the transmission.
+   */
+  struct GNUNET_TIME_Absolute timeout;
+  
+  /**
+   * Message priority.
+   */
+  uint32_t priority;
+
+  /**
+   * Size of the message in bytes.
+   */ 
+  uint16_t msize;
+};
+
+
+/**
+ * Schedule transmission of a message we got from a client to a peer.
+ *
+ * @param cls the 'struct TransmitClientMessageContext*'
+ * @param n destination, or NULL on error (in that case, drop the message)
+ */
+static void
+transmit_client_message (void *cls,
+                        struct NeighbourList *n)
+{
+  struct TransmitClientMessageContext *tcmc = cls;
+  struct TransportClient *tc;
+
+  tc = clients;
+  while ((tc != NULL) && (tc->client != tcmc->client))
+    tc = tc->next;
+
+  if (n != NULL)
+    {
+      transmit_to_peer (tc, NULL, tcmc->priority, 
+                       GNUNET_TIME_absolute_get_remaining (tcmc->timeout),
+                       (char *)&tcmc[1],
+                       tcmc->msize, GNUNET_NO, n);
+    }
+  GNUNET_SERVER_receive_done (tcmc->client, GNUNET_OK);
+  GNUNET_SERVER_client_drop (tcmc->client);
+  GNUNET_free (tcmc);
+}
+
+
 /**
  * Handle SEND-message.
  *
@@ -3578,10 +4771,9 @@ handle_send (void *cls,
              struct GNUNET_SERVER_Client *client,
              const struct GNUNET_MessageHeader *message)
 {
-  struct TransportClient *tc;
-  struct NeighbourList *n;
   const struct OutboundMessage *obm;
   const struct GNUNET_MessageHeader *obmm;
+  struct TransmitClientMessageContext *tcmc;
   uint16_t size;
   uint16_t msize;
 
@@ -3598,37 +4790,26 @@ handle_send (void *cls,
                            size,
                            GNUNET_NO);      
   obm = (const struct OutboundMessage *) message;
-#if DEBUG_TRANSPORT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Received `%s' request from client with target `%4s'\n",
-              "SEND", GNUNET_i2s (&obm->peer));
-#endif
   obmm = (const struct GNUNET_MessageHeader *) &obm[1];
-  msize = ntohs (obmm->size);
-  if (size != msize + sizeof (struct OutboundMessage))
-    {
-      GNUNET_break (0);
-      GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
-      return;
-    }
-  n = find_neighbour (&obm->peer);
-  if (n == NULL)
-    n = setup_new_neighbour (&obm->peer);
-  tc = clients;
-  while ((tc != NULL) && (tc->client != client))
-    tc = tc->next;
-
+  msize = size - sizeof (struct OutboundMessage);
 #if DEBUG_TRANSPORT
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Client asked to transmit %u-byte message of type %u to `%4s'\n",
-              ntohs (obmm->size),
-              ntohs (obmm->type), GNUNET_i2s (&obm->peer));
+              "Received `%s' request from client with target `%4s' and message of type %u and size %u\n",
+              "SEND", GNUNET_i2s (&obm->peer),
+              ntohs (obmm->type),
+              msize);
 #endif
-  transmit_to_peer (tc, NULL, ntohl (obm->priority), 
-                   GNUNET_TIME_relative_ntoh (obm->timeout),
-                   (char *)obmm, 
-                   ntohs (obmm->size), GNUNET_NO, n);
-  GNUNET_SERVER_receive_done (client, GNUNET_OK);
+  tcmc = GNUNET_malloc (sizeof (struct TransmitClientMessageContext) + msize);
+  tcmc->client = client;
+  tcmc->priority = ntohl (obm->priority);
+  tcmc->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_ntoh (obm->timeout));
+  tcmc->msize = msize;
+  /* FIXME: this memcpy can be up to 7% of our total runtime */
+  memcpy (&tcmc[1], obmm, msize);
+  GNUNET_SERVER_client_keep (client);
+  setup_peer_check_blacklist (&obm->peer, GNUNET_YES,
+                             &transmit_client_message,
+                             tcmc);
 }
 
 
@@ -3679,7 +4860,7 @@ handle_set_quota (void *cls,
 
 
 /**
- * Take the given address and append it to the set of results send back to
+ * Take the given address and append it to the set of results sent back to
  * the client.
  * 
  * @param cls the transmission context used ('struct GNUNET_SERVER_TransmitContext*')
@@ -3695,6 +4876,7 @@ transmit_address_to_client (void *cls, const char *address)
     slen = 0;
   else
     slen = strlen (address) + 1;
+
   GNUNET_SERVER_transmit_context_append_data (tc, address, slen,
                                              GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_REPLY);
   if (NULL == address)
@@ -3769,25 +4951,6 @@ handle_address_lookup (void *cls,
                                          &transmit_address_to_client, tc);
 }
 
-/**
- * List of handlers for the messages understood by this
- * service.
- */
-static struct GNUNET_SERVER_MessageHandler handlers[] = {
-  {&handle_start, NULL,
-   GNUNET_MESSAGE_TYPE_TRANSPORT_START, 0},
-  {&handle_hello, NULL,
-   GNUNET_MESSAGE_TYPE_HELLO, 0},
-  {&handle_send, NULL,
-   GNUNET_MESSAGE_TYPE_TRANSPORT_SEND, 0},
-  {&handle_set_quota, NULL,
-   GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA, sizeof (struct QuotaSetMessage)},
-  {&handle_address_lookup, NULL,
-   GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_LOOKUP,
-   0},
-  {NULL, NULL, 0, 0}
-};
-
 
 /**
  * Setup the environment for this plugin.
@@ -3853,6 +5016,8 @@ client_disconnect_notification (void *cls,
   struct TransportClient *pos;
   struct TransportClient *prev;
   struct ClientMessageQueueEntry *mqe;
+  struct Blacklisters *bl;
+  struct BlacklistCheck *bc;
 
   if (client == NULL)
     return;
@@ -3860,6 +5025,41 @@ client_disconnect_notification (void *cls,
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
               "Client disconnected, cleaning up.\n");
 #endif
+  /* clean up blacklister */
+  bl = bl_head;
+  while (bl != NULL)
+    {
+      if (bl->client == client)
+       {
+         bc = bc_head;
+         while (bc != NULL)
+           {
+             if (bc->bl_pos == bl)
+               {
+                 bc->bl_pos = bl->next;
+                 if (bc->th != NULL)
+                   {
+                     GNUNET_CONNECTION_notify_transmit_ready_cancel (bc->th);
+                     bc->th = NULL;                  
+                   }
+                 if (bc->task == GNUNET_SCHEDULER_NO_TASK)
+                   bc->task = GNUNET_SCHEDULER_add_now (sched,
+                                                        &do_blacklist_check,
+                                                        bc);
+                 break;
+               }
+             bc = bc->next;
+           }
+         GNUNET_CONTAINER_DLL_remove (bl_head,
+                                      bl_tail,
+                                      bl);
+         GNUNET_SERVER_client_drop (bl->client);
+         GNUNET_free (bl);
+         break;
+       }
+      bl = bl->next;
+    }
+  /* clean up 'normal' clients */
   prev = NULL;
   pos = clients;
   while ((pos != NULL) && (pos->client != client))
@@ -3896,28 +5096,6 @@ client_disconnect_notification (void *cls,
 }
 
 
-/**
- * Iterator to free entries in the validation_map.
- *
- * @param cls closure (unused)
- * @param key current key code
- * @param value value in the hash map (validation to abort)
- * @return GNUNET_YES (always)
- */
-static int 
-abort_validation (void *cls,
-                 const GNUNET_HashCode * key,
-                 void *value)
-{
-  struct ValidationEntry *va = value;
-
-  GNUNET_SCHEDULER_cancel (sched, va->timeout_task);
-  GNUNET_free (va->transport_name);
-  GNUNET_free (va);
-  return GNUNET_YES;
-}
-
-
 /**
  * Function called when the service shuts down.  Unloads our plugins
  * and cancels pending validations.
@@ -3961,25 +5139,39 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
     GNUNET_CRYPTO_rsa_key_free (my_private_key);
   GNUNET_free_non_null (our_hello);
 
+  GNUNET_CONTAINER_multihashmap_iterate (validation_map,
+                                        &abort_validation,
+                                        NULL);
+  GNUNET_CONTAINER_multihashmap_destroy (validation_map);
+  validation_map = NULL;
+
   /* free 'chvc' data structure */
   while (NULL != (chvc = chvc_head))
     {
       chvc_head = chvc->next;
-      GNUNET_PEERINFO_iterate_cancel (chvc->piter);
+      if (chvc->piter != NULL)
+       GNUNET_PEERINFO_iterate_cancel (chvc->piter);      
+      else
+       GNUNET_break (0);
+      GNUNET_assert (chvc->ve_count == 0);
       GNUNET_free (chvc);
     }
   chvc_tail = NULL;
 
-  GNUNET_CONTAINER_multihashmap_iterate (validation_map,
-                                        &abort_validation,
-                                        NULL);
-  GNUNET_CONTAINER_multihashmap_destroy (validation_map);
-  validation_map = NULL;
   if (stats != NULL)
     {
       GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
       stats = NULL;
     }
+  if (peerinfo != NULL)
+    {
+      GNUNET_PEERINFO_disconnect (peerinfo);
+      peerinfo = NULL;
+    }
+  /* Can we assume those are gone by now, or do we need to clean up
+     explicitly!? */
+  GNUNET_break (bl_head == NULL);
+  GNUNET_break (bc_head == NULL);
 }
 
 
@@ -3988,15 +5180,33 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  *
  * @param cls closure
  * @param s scheduler to use
- * @param serv the initialized server
+ * @param server the initialized server
  * @param c configuration to use
  */
 static void
 run (void *cls,
      struct GNUNET_SCHEDULER_Handle *s,
-     struct GNUNET_SERVER_Handle *serv,
+     struct GNUNET_SERVER_Handle *server,
      const struct GNUNET_CONFIGURATION_Handle *c)
 {
+  static const struct GNUNET_SERVER_MessageHandler handlers[] = {
+    {&handle_start, NULL,
+     GNUNET_MESSAGE_TYPE_TRANSPORT_START, 0},
+    {&handle_hello, NULL,
+     GNUNET_MESSAGE_TYPE_HELLO, 0},
+    {&handle_send, NULL,
+     GNUNET_MESSAGE_TYPE_TRANSPORT_SEND, 0},
+    {&handle_set_quota, NULL,
+     GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA, sizeof (struct QuotaSetMessage)},
+    {&handle_address_lookup, NULL,
+     GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_LOOKUP,
+     0},
+    {&handle_blacklist_init, NULL,
+     GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_INIT, sizeof (struct GNUNET_MessageHeader)},
+    {&handle_blacklist_reply, NULL,
+     GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_REPLY, sizeof (struct BlacklistMessage)},
+    {NULL, NULL, 0, 0}
+  };
   char *plugs;
   char *pos;
   int no_transports;
@@ -4032,6 +5242,22 @@ run (void *cls,
       return;
     }
   max_connect_per_transport = (uint32_t) tneigh;
+  peerinfo = GNUNET_PEERINFO_connect (sched, cfg);
+  if (peerinfo == NULL)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 _("Could not access PEERINFO service.  Exiting.\n"));     
+      GNUNET_SCHEDULER_shutdown (s);
+      if (stats != NULL)
+       {
+         GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
+         stats = NULL;
+       }
+      GNUNET_CONTAINER_multihashmap_destroy (validation_map);
+      validation_map = NULL;
+      GNUNET_free (keyfile);
+      return;
+    }
   my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
   GNUNET_free (keyfile);
   if (my_private_key == NULL)
@@ -4053,7 +5279,6 @@ run (void *cls,
   GNUNET_CRYPTO_hash (&my_public_key,
                       sizeof (my_public_key), &my_identity.hashPubKey);
   /* setup notification */
-  server = serv;
   GNUNET_SERVER_disconnect_notify (server,
                                    &client_disconnect_notification, NULL);
   /* load plugins... */
@@ -4082,6 +5307,8 @@ run (void *cls,
 #if DEBUG_TRANSPORT
   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transport service ready.\n"));
 #endif
+  /* If we have a blacklist file, read from it */
+  read_blacklist_file(cfg);
   /* process client requests */
   GNUNET_SERVER_add_handlers (server, handlers);
 }
@@ -4097,6 +5324,7 @@ run (void *cls,
 int
 main (int argc, char *const *argv)
 {
+  a2s (NULL, NULL, 0); /* make compiler happy */
   return (GNUNET_OK ==
           GNUNET_SERVICE_run (argc,
                               argv,