(no commit message)
[oweals/gnunet.git] / src / transport / gnunet-service-transport.c
index ce8338b1d78f14548d18672ef8be0babc048f087..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
@@ -79,7 +90,7 @@
  * How often must a peer violate bandwidth quotas before we start
  * to simply drop its messages?
  */
-#define QUOTA_VIOLATION_DROP_THRESHOLD 100
+#define QUOTA_VIOLATION_DROP_THRESHOLD 10
 
 /**
  * How long until a HELLO verification attempt should time out?
  * 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 will we allow sending of a ping to be delayed?
+ * 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 TRANSPORT_DEFAULT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
+#define PONG_SIGNATURE_LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
 
 /**
  * Priority to use for PONG messages.
  */
 #define HELLO_REVALIDATION_START_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
 
+/**
+ * Maximum frequency for re-evaluating latencies for all transport addresses.
+ */
+#define LATENCY_EVALUATION_MAX_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
+
+/**
+ * Maximum frequency for re-evaluating latencies for connected addresses.
+ */
+#define CONNECTED_LATENCY_EVALUATION_MAX_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 1)
+
 
 /**
  * List of addresses of other peers
@@ -145,9 +170,10 @@ struct ForeignAddressList
   struct GNUNET_TIME_Absolute expires;
 
   /**
-   * Length of addr.
+   * Task used to re-validate addresses, updates latencies and
+   * verifies liveness.
    */
-  size_t addrlen;
+  GNUNET_SCHEDULER_TaskIdentifier revalidate_task;
 
   /**
    * The address.
@@ -155,8 +181,13 @@ struct ForeignAddressList
   const void *addr;
 
   /**
-   * What was the last latency observed for this plugin
-   * and peer?  Invalid if connected is GNUNET_NO.
+   * Session (or NULL if no valid session currently exists or if the
+   * plugin does not use sessions).
+   */
+  struct Session *session;
+
+  /**
+   * What was the last latency observed for this address, plugin and peer?
    */
   struct GNUNET_TIME_Relative latency;
 
@@ -169,6 +200,31 @@ struct ForeignAddressList
    */
   struct GNUNET_TIME_Absolute timeout;
 
+  /**
+   * How often have we tried to connect using this plugin?  Used to
+   * discriminate against addresses that do not work well.
+   * FIXME: not yet used, but should be!
+   */
+  unsigned int connect_attempts;
+
+  /**
+   * DV distance to this peer (1 if no DV is used). 
+   * FIXME: need to set this from transport plugins!
+   */
+  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
+   * probe its latency.
+   */
+  int8_t estimated;
+
   /**
    * Are we currently connected via this address?  The first time we
    * successfully transmit or receive data to a peer via a particular
@@ -176,38 +232,26 @@ struct ForeignAddressList
    * (disconnect notification, transmission failure, timeout), we set
    * it back to GNUNET_NO.  
    */
-  int connected;
+  int8_t connected;
 
   /**
    * Is this plugin currently busy transmitting to the specific target?
    * GNUNET_NO if not (initial, default state is GNUNET_NO).   Internal
    * messages do not count as 'in transmit'.
    */
-  int in_transmit;
+  int8_t in_transmit;
 
   /**
    * Has this address been validated yet?
    */
-  int validated;
-
-  /**
-   * How often have we tried to connect using this plugin?  Used to
-   * discriminate against addresses that do not work well.
-   * FIXME: not yet used, but should be!
-   */
-  unsigned int connect_attempts;
-
-  /**
-   * DV distance to this peer (1 if no DV is used). 
-   * FIXME: need to set this from transport plugins!
-   */
-  uint32_t distance;
+  int8_t validated;
 
 };
 
 
 /**
- * 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
 {
@@ -217,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;
 
 };
 
@@ -280,14 +329,17 @@ struct TransportPlugin
   GNUNET_SCHEDULER_TaskIdentifier address_update_task;
 
   /**
-   * Set to GNUNET_YES if we need to scrap the existing
-   * list of "addresses" and start fresh when we receive
-   * the next address update from a transport.  Set to
-   * GNUNET_NO if we should just add the new address
-   * to the list and wait for the commit call.
+   * Set to GNUNET_YES if we need to scrap the existing list of
+   * "addresses" and start fresh when we receive the next address
+   * update from a transport.  Set to GNUNET_NO if we should just add
+   * the new address to the list and wait for the commit call.
    */
   int rebuild;
 
+  /**
+   * Hashmap of blacklisted peers for this particular transport.
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *blacklist;
 };
 
 struct NeighbourList;
@@ -388,6 +440,11 @@ struct ReadyList
    */
   struct ForeignAddressList *addresses;
 
+  /**
+   * To which neighbour does this ready list belong to?
+   */
+  struct NeighbourList *neighbour;
+
 };
 
 
@@ -420,6 +477,25 @@ struct NeighbourList
    */
   struct MessageQueue *messages_tail;
 
+  /**
+   * Buffer for at most one payload message used when we receive
+   * payload data before our PING-PONG has succeeded.  We then
+   * store such messages in this intermediary buffer until the
+   * connection is fully up.  
+   */
+  struct GNUNET_MessageHeader *pre_connect_message_buffer;
+
+  /**
+   * Context for peerinfo iteration.
+   * NULL after we are done processing peerinfo's information.
+   */
+  struct GNUNET_PEERINFO_IteratorContext *piter;
+
+  /**
+   * Public key for this peer.   Valid only if the respective flag is set below.
+   */
+  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey;
+
   /**
    * Identity of this neighbour.
    */
@@ -433,7 +509,9 @@ struct NeighbourList
 
   /**
    * ID of task scheduled to run when we should retry transmitting
-   * the head of the message queue.
+   * the head of the message queue.  Actually triggered when the
+   * transmission is timing out (we trigger instantly when we have
+   * a chance of success).
    */
   GNUNET_SCHEDULER_TaskIdentifier retry_task;
 
@@ -445,9 +523,9 @@ struct NeighbourList
   struct GNUNET_TIME_Absolute peer_timeout;
 
   /**
-   * At what time did we reset last_received last?
+   * Tracker for inbound bandwidth.
    */
-  struct GNUNET_TIME_Absolute last_quota_update;
+  struct GNUNET_BANDWIDTH_Tracker in_tracker;
 
   /**
    * The latency we have seen for this particular address for
@@ -462,22 +540,6 @@ struct NeighbourList
    */
   struct GNUNET_TIME_Relative latency;
 
-  /**
-   * DV distance to this peer (1 if no DV is used). 
-   */
-  uint32_t distance;
-
-  /**
-   * How many bytes have we received since the "last_quota_update"
-   * timestamp?
-   */
-  uint64_t last_received;
-
-  /**
-   * Global quota for inbound traffic for the neighbour in bytes/ms.
-   */
-  uint32_t quota_in;
-
   /**
    * How often has the other peer (recently) violated the
    * inbound traffic limit?  Incremented by 10 per violation,
@@ -486,19 +548,29 @@ struct NeighbourList
    */
   unsigned int quota_violation_count;
 
+  /**
+   * DV distance to this peer (1 if no DV is used). 
+   */
+  uint32_t distance;
+
   /**
    * Have we seen an PONG from this neighbour in the past (and
    * not had a disconnect since)?
    */
   int received_pong;
 
+  /**
+   * Do we have a valid public key for this neighbour?
+   */
+  int public_key_valid;
+
 };
 
 /**
  * Message used to ask a peer to validate receipt (to check an address
- * from a HELLO).  Followed by the address used.  Note that the
- * recipients response does not affirm that he has this address,
- * only that he got the challenge message.
+ * 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
 {
@@ -509,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;
 
@@ -524,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
 {
@@ -542,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.
@@ -552,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;
 
 };
 
@@ -641,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!
@@ -675,15 +765,20 @@ struct ValidationEntry
   struct GNUNET_TIME_Absolute send_time;
 
   /**
-   * Length of addr.
+   * Session being validated (or NULL for none).
    */
-  size_t addrlen;
+  struct Session *session;
 
   /**
    * Challenge number we used.
    */
   uint32_t challenge;
 
+  /**
+   * Length of addr.
+   */
+  uint16_t addrlen;
+
 };
 
 
@@ -720,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.
  */
@@ -770,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.
@@ -794,13 +889,16 @@ static struct CheckHelloValidatedContext *chvc_head;
  */
 static struct CheckHelloValidatedContext *chvc_tail;
 
-
 /**
  * Map of PeerIdentities to 'struct ValidationEntry*'s (addresses
  * of the given peer that we are currently validating).
  */
 static struct GNUNET_CONTAINER_MultiHashMap *validation_map;
 
+/**
+ * Handle for reporting statistics.
+ */
+static struct GNUNET_STATISTICS_Handle *stats;
 
 /**
  * The peer specified by the given neighbour has timed-out or a plugin
@@ -828,10 +926,7 @@ static void try_transmission_to_peer (struct NeighbourList *neighbour);
 
 /**
  * Find an entry in the neighbour list for a particular peer.
- * if sender_address is not specified (NULL) then return the
- * first matching entry.  If sender_address is specified, then
- * make sure that the address and address_len also matches.
- *
+ *  
  * @return NULL if not found.
  */
 static struct NeighbourList *
@@ -860,47 +955,215 @@ find_transport (const char *short_name)
   return head;
 }
 
-
 /**
- * Update the quota values for the given neighbour now.
+ * 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
-update_quota (struct NeighbourList *n)
+add_peer_to_blacklist (struct GNUNET_PeerIdentity *peer, char *transport_name)
 {
-  struct GNUNET_TIME_Relative delta;
-  uint64_t allowed;
-  uint64_t remaining;
+  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);
+}
+
 
-  delta = GNUNET_TIME_absolute_get_duration (n->last_quota_update);
-  if (delta.value < MIN_QUOTA_REFRESH_TIME)
-    return;                     /* not enough time passed for doing quota update */
-  allowed = delta.value * n->quota_in;
+/**
+ * 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 (n->last_received < allowed)
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                               "TRANSPORT",
+                                               "BLACKLIST_FILE",
+                                               &fn))
     {
-      remaining = allowed - n->last_received;
-      if (n->quota_in > 0)
-        remaining /= n->quota_in;
-      else
-        remaining = 0;
-      if (remaining > MAX_BANDWIDTH_CARRY)
-        remaining = MAX_BANDWIDTH_CARRY;
-      n->last_received = 0;
-      n->last_quota_update = GNUNET_TIME_absolute_get ();
-      n->last_quota_update.value -= remaining;
-      if (n->quota_violation_count > 0)
-        n->quota_violation_count--;
+#if DEBUG_TRANSPORT
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  _("Option `%s' in section `%s' not specified!\n"),
+                  "BLACKLIST_FILE",
+                  "TRANSPORT");
+#endif
+      return;
     }
-  else
+  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)))
     {
-      n->last_received -= allowed;
-      n->last_quota_update = GNUNET_TIME_absolute_get ();
-      if (n->last_received > allowed)
+      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
         {
-          /* more than twice the allowed rate! */
-          n->quota_violation_count += 10;
+          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);
 }
 
 
@@ -932,6 +1195,10 @@ transmit_to_client_callback (void *cls, size_t size, void *buf)
       /* fatal error with client, free message queue! */
       while (NULL != (q = client->message_queue_head))
         {
+         GNUNET_STATISTICS_update (stats,
+                                   gettext_noop ("# bytes discarded (could not transmit to client)"),
+                                   ntohs (((const struct GNUNET_MessageHeader*)&q[1])->size),
+                                   GNUNET_NO);      
          GNUNET_CONTAINER_DLL_remove (client->message_queue_head,
                                       client->message_queue_tail,
                                       q);
@@ -975,6 +1242,82 @@ 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'
+ * (since only one can be preferred).
+ *
+ * @param fal address to set to 'connected'
+ */
+static void
+mark_address_connected (struct ForeignAddressList *fal)
+{
+  struct ForeignAddressList *pos;
+  int cnt;
+
+  GNUNET_assert (GNUNET_YES == fal->validated);
+  if (fal->connected == GNUNET_YES)
+    return; /* nothing to do */
+  cnt = GNUNET_YES;
+  pos = fal->ready_list->addresses;
+  while (pos != NULL)
+    {
+      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;
+    }
+  fal->connected = GNUNET_YES;
+  if (GNUNET_YES == cnt)
+    {
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# connected addresses"),
+                               1,
+                               GNUNET_NO);
+    }
+}
+
+
 /**
  * Send the specified message to the specified client.  Since multiple
  * messages may be pending for the same client at a time, this code
@@ -996,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);
@@ -1067,7 +1416,25 @@ transmit_send_continuation (void *cls,
 {
   struct MessageQueue *mq = cls;
   struct NeighbourList *n;
-
+  
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# bytes pending with plugins"),
+                           - (int64_t) mq->message_buf_size,
+                           GNUNET_NO);
+  if (result == GNUNET_OK)
+    {
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# bytes successfully transmitted by plugins"),
+                               mq->message_buf_size,
+                               GNUNET_NO);      
+    }
+  else
+    {
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# bytes with transmission failure by plugins"),
+                               mq->message_buf_size,
+                               GNUNET_NO);      
+    }  
   n = find_neighbour(&mq->neighbour_id);
   GNUNET_assert (n != NULL);
   if (mq->specific_address != NULL)
@@ -1077,11 +1444,26 @@ transmit_send_continuation (void *cls,
          mq->specific_address->timeout =
            GNUNET_TIME_relative_to_absolute
            (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
-         mq->specific_address->connected = GNUNET_YES;
+         if (mq->specific_address->validated == GNUNET_YES)
+           mark_address_connected (mq->specific_address);
        }    
       else
        {
-         mq->specific_address->connected = GNUNET_NO;
+         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,
+                                       GNUNET_NO);
+             mq->specific_address->connected = GNUNET_NO;
+           }
        }    
       if (! mq->internal_msg) 
        mq->specific_address->in_transmit = GNUNET_NO;
@@ -1090,8 +1472,6 @@ transmit_send_continuation (void *cls,
     transmit_send_ok (mq->client, n, result);
   GNUNET_free (mq);
   try_transmission_to_peer (n);
-  if (result != GNUNET_OK)
-    disconnect_neighbour (n, GNUNET_YES);    
 }
 
 
@@ -1126,6 +1506,10 @@ find_ready_address(struct NeighbourList *neighbour)
                           "Marking long-time inactive connection to `%4s' as down.\n",
                           GNUNET_i2s (&neighbour->id));
 #endif
+             GNUNET_STATISTICS_update (stats,
+                                       gettext_noop ("# connected addresses"),
+                                       -1,
+                                       GNUNET_NO);
               addresses->connected = GNUNET_NO;
             }
           addresses = addresses->next;
@@ -1134,6 +1518,21 @@ find_ready_address(struct NeighbourList *neighbour)
       addresses = head->addresses;
       while (addresses != NULL)
         {
+#if DEBUG_TRANSPORT > 1
+         if (addresses->addr != NULL)
+           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                       "Have address `%s' for peer `%4s' (status: %d, %d, %d, %u, %llums, %u)\n",
+                       a2s (head->plugin->short_name,
+                            addresses->addr,
+                            addresses->addrlen),
+                       GNUNET_i2s (&neighbour->id),
+                       addresses->connected,
+                       addresses->in_transmit,
+                       addresses->validated,
+                       addresses->connect_attempts,
+                       (unsigned long long) addresses->timeout.value,
+                       (unsigned int) addresses->distance);
+#endif
           if ( ( (best_address == NULL) || 
                 (addresses->connected == GNUNET_YES) ||
                 (best_address->connected == GNUNET_NO) ) &&
@@ -1147,14 +1546,26 @@ find_ready_address(struct NeighbourList *neighbour)
         }
       head = head->next;
     }
-#if DEBUG_TRANSPORT
   if (best_address != NULL)
     {
+#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
+    }
+  else
+    {
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# transmission attempts failed (no address)"),
+                               1,
+                               GNUNET_NO);
+    }
   return best_address;
 
 }
@@ -1187,16 +1598,36 @@ try_transmission_to_peer (struct NeighbourList *neighbour)
   struct ReadyList *rl;
   struct MessageQueue *mq;
   struct GNUNET_TIME_Relative timeout;
+  ssize_t ret;
+  int force_address;
 
   if (neighbour->messages_head == NULL)
-    return;                     /* nothing to do */
+    {
+#if DEBUG_TRANSPORT
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Transmission queue for `%4s' is empty\n",
+                 GNUNET_i2s (&neighbour->id));
+#endif
+      return;                     /* nothing to do */
+    }
   rl = NULL;
   mq = neighbour->messages_head;
-  /* FIXME: support bi-directional use of TCP */
+  force_address = GNUNET_YES;
   if (mq->specific_address == NULL)
-    mq->specific_address = find_ready_address(neighbour); 
+    {
+      mq->specific_address = find_ready_address(neighbour); 
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# transport selected peer address freely"),
+                               1,
+                               GNUNET_NO); 
+      force_address = GNUNET_NO;
+    }
   if (mq->specific_address == NULL)
     {
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# transport failed to selected peer address"),
+                               1,
+                               GNUNET_NO); 
       timeout = GNUNET_TIME_absolute_get_remaining (mq->timeout);
       if (timeout.value == 0)
        {
@@ -1206,6 +1637,14 @@ try_transmission_to_peer (struct NeighbourList *neighbour)
                      mq->message_buf_size,
                      GNUNET_i2s (&mq->neighbour_id));
 #endif
+         GNUNET_STATISTICS_update (stats,
+                                   gettext_noop ("# bytes in message queue for other peers"),
+                                   - (int64_t) mq->message_buf_size,
+                                   GNUNET_NO);
+         GNUNET_STATISTICS_update (stats,
+                                   gettext_noop ("# bytes discarded (no destination address available)"),
+                                   mq->message_buf_size,
+                                   GNUNET_NO);      
          if (mq->client != NULL)
            transmit_send_ok (mq->client, neighbour, GNUNET_NO);
          GNUNET_CONTAINER_DLL_remove (neighbour->messages_head,
@@ -1214,6 +1653,10 @@ try_transmission_to_peer (struct NeighbourList *neighbour)
          GNUNET_free (mq);
          return;               /* nobody ready */ 
        }
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# message delivery deferred (no address)"),
+                               1,
+                               GNUNET_NO);
       if (neighbour->retry_task != GNUNET_SCHEDULER_NO_TASK)
        GNUNET_SCHEDULER_cancel (sched,
                                 neighbour->retry_task);
@@ -1228,6 +1671,8 @@ try_transmission_to_peer (struct NeighbourList *neighbour)
                  GNUNET_i2s (&mq->neighbour_id),
                  timeout.value);
 #endif
+      /* FIXME: might want to trigger peerinfo lookup here
+        (unless that's already pending...) */
       return;    
     }
   GNUNET_CONTAINER_DLL_remove (neighbour->messages_head,
@@ -1244,20 +1689,40 @@ try_transmission_to_peer (struct NeighbourList *neighbour)
               "Sending message of size %u for `%4s' to `%s' via plugin `%s'\n",
               mq->message_buf_size,
               GNUNET_i2s (&neighbour->id), 
-             GNUNET_a2s (mq->specific_address->addr,
-                         mq->specific_address->addrlen),
+             (mq->specific_address->addr != NULL)
+             ? a2s (mq->plugin->short_name,
+                    mq->specific_address->addr,
+                    mq->specific_address->addrlen)
+             : "<inbound>",
              rl->plugin->short_name);
 #endif
-  rl->plugin->api->send (rl->plugin->api->cls,
-                        &mq->neighbour_id,
-                        mq->message_buf,
-                        mq->message_buf_size,
-                        mq->priority,
-                        GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
-                        mq->specific_address->addr,
-                        mq->specific_address->addrlen,
-                        GNUNET_YES /* FIXME: sometimes, we want to be more tolerant here! */,
-                        &transmit_send_continuation, mq);
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# bytes in message queue for other peers"),
+                           - (int64_t) mq->message_buf_size,
+                           GNUNET_NO);
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# bytes pending with plugins"),
+                           mq->message_buf_size,
+                           GNUNET_NO);
+  ret = rl->plugin->api->send (rl->plugin->api->cls,
+                              &mq->neighbour_id,
+                              mq->message_buf,
+                              mq->message_buf_size,
+                              mq->priority,
+                              GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
+                              mq->specific_address->session,
+                              mq->specific_address->addr,
+                              mq->specific_address->addrlen,
+                              force_address,
+                              &transmit_send_continuation, mq);
+  if (ret == -1)
+    {
+      /* failure, but 'send' would not call continuation in this case,
+        so we need to do it here! */
+      transmit_send_continuation (mq, 
+                                 &mq->neighbour_id,
+                                 GNUNET_SYSERR);
+    }
 }
 
 
@@ -1295,7 +1760,7 @@ transmit_to_peer (struct TransportClient *client,
           if (mq->client == client)
             {
               /* client transmitted to same peer twice
-                 before getting SendOk! */
+                 before getting SEND_OK! */
               GNUNET_break (0);
               return;
             }
@@ -1303,9 +1768,14 @@ transmit_to_peer (struct TransportClient *client,
         }
     }
 #endif
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# bytes in message queue for other peers"),
+                           message_buf_size,
+                           GNUNET_NO);
   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;
@@ -1358,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;
@@ -1385,6 +1855,10 @@ refresh_hello ()
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
               "Refreshed my `%s', new size is %d\n", "HELLO", GNUNET_HELLO_size(hello));
 #endif
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# refreshed my HELLO"),
+                           1,
+                           GNUNET_NO);
   cpos = clients;
   while (cpos != NULL)
     {
@@ -1396,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)
     {
@@ -1406,6 +1879,10 @@ refresh_hello ()
                   "Transmitting updated `%s' to neighbour `%4s'\n",
                   "HELLO", GNUNET_i2s (&npos->id));
 #endif
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# transmitted my HELLO to other peers"),
+                               1,
+                               GNUNET_NO);
       transmit_to_peer (NULL, NULL, 0,
                        HELLO_ADDRESS_EXPIRATION,
                         (const char *) our_hello, 
@@ -1439,6 +1916,7 @@ expire_address_task (void *cls,
 static void
 update_addresses (struct TransportPlugin *plugin, int fresh)
 {
+  static struct GNUNET_TIME_Absolute last_update;
   struct GNUNET_TIME_Relative min_remaining;
   struct GNUNET_TIME_Relative remaining;
   struct GNUNET_TIME_Absolute now;
@@ -1452,7 +1930,7 @@ update_addresses (struct TransportPlugin *plugin, int fresh)
   plugin->address_update_task = GNUNET_SCHEDULER_NO_TASK;
   now = GNUNET_TIME_absolute_get ();
   min_remaining = GNUNET_TIME_UNIT_FOREVER_REL;
-  expired = GNUNET_NO;
+  expired = (GNUNET_TIME_absolute_get_duration (last_update).value > (HELLO_ADDRESS_EXPIRATION.value / 4));
   prev = NULL;
   pos = plugin->addresses;
   while (pos != NULL)
@@ -1464,7 +1942,7 @@ update_addresses (struct TransportPlugin *plugin, int fresh)
           if (prev == NULL)
             plugin->addresses = pos->next;
           else
-            prev->next = pos->next;
+            prev->next = pos->next;  
           GNUNET_free (pos);
         }
       else
@@ -1478,13 +1956,17 @@ update_addresses (struct TransportPlugin *plugin, int fresh)
     }
 
   if (expired || fresh)
-    refresh_hello ();
-  if (min_remaining.value < GNUNET_TIME_UNIT_FOREVER_REL.value)
-    plugin->address_update_task
-      = GNUNET_SCHEDULER_add_delayed (plugin->env.sched,
-                                      min_remaining,
-                                      &expire_address_task, plugin);
-
+    {
+      last_update = now;
+      refresh_hello ();
+    }
+  min_remaining = GNUNET_TIME_relative_min (min_remaining,
+                                           GNUNET_TIME_relative_divide (HELLO_ADDRESS_EXPIRATION,
+                                                                        2));
+  plugin->address_update_task
+    = GNUNET_SCHEDULER_add_delayed (plugin->env.sched,
+                                   min_remaining,
+                                   &expire_address_task, plugin);
 }
 
 
@@ -1498,8 +1980,109 @@ static void
 expire_address_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct TransportPlugin *plugin = cls;
+
   plugin->address_update_task = GNUNET_SCHEDULER_NO_TASK;
-  update_addresses (plugin, GNUNET_NO);
+  if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+    update_addresses (plugin, GNUNET_NO);
+}
+
+
+/**
+ * Iterator over hash map entries that NULLs the session of validation
+ * entries that match the given session.
+ *
+ * @param cls closure (the 'struct Session*' to match against)
+ * @param key current key code (peer ID, not used)
+ * @param value value in the hash map ('struct ValidationEntry*')
+ * @return GNUNET_YES (we should continue to iterate)
+ */
+static int 
+remove_session_validations (void *cls,
+                           const GNUNET_HashCode * key,
+                           void *value)
+{
+  struct Session *session = cls;
+  struct ValidationEntry *ve = value;
+
+  if (session == ve->session)
+    ve->session = NULL;
+  return GNUNET_YES;
+}
+
+
+/**
+ * Function that will be called whenever the plugin internally
+ * cleans up a session pointer and hence the service needs to
+ * discard all of those sessions as well.  Plugins that do not
+ * use sessions can simply omit calling this function and always
+ * use NULL wherever a session pointer is needed.
+ * 
+ * @param cls closure
+ * @param peer which peer was the session for 
+ * @param session which session is being destoyed
+ */
+static void
+plugin_env_session_end  (void *cls,
+                        const struct GNUNET_PeerIdentity *peer,
+                        struct Session *session)
+{
+  struct TransportPlugin *p = cls;
+  struct NeighbourList *nl;
+  struct ReadyList *rl;
+  struct ForeignAddressList *pos;
+  struct ForeignAddressList *prev;
+
+  GNUNET_CONTAINER_multihashmap_iterate (validation_map,
+                                        &remove_session_validations,
+                                        session);
+  nl = find_neighbour (peer);
+  if (nl == NULL)
+    return;
+  rl = nl->plugins;
+  while (rl != NULL)
+    {
+      if (rl->plugin == p)
+       break;
+      rl = rl->next;
+    }
+  if (rl == NULL)
+    return;
+  prev = NULL;
+  pos = rl->addresses;
+  while ( (pos != NULL) &&
+         (pos->session != session) )
+    {
+      prev = pos;
+      pos = pos->next;
+    }
+  if (pos == NULL)
+    return;
+  pos->session = NULL;
+  if (pos->addrlen != 0)
+    return;
+  if (prev == NULL)
+    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 */
+  /* check if we have any validated addresses left */
+  pos = rl->addresses;
+  while (pos != NULL)
+    {
+      if (pos->validated)
+       return;
+      pos = pos->next;
+    }
+  /* no valid addresses left, signal disconnect! */
+  disconnect_neighbour (nl, GNUNET_NO);  
 }
 
 
@@ -1519,16 +2102,16 @@ 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;
   struct OwnAddressList *al;
   struct GNUNET_TIME_Absolute abex;
 
+  GNUNET_assert (addr != NULL);
   abex = GNUNET_TIME_relative_to_absolute (expires);
   GNUNET_assert (p == find_transport (name));
-
   al = p->addresses;
   while (al != NULL)
     {
@@ -1542,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;
@@ -1568,6 +2150,10 @@ notify_clients_connect (const struct GNUNET_PeerIdentity *peer,
              "Notifying clients about connection from `%s'\n",
              GNUNET_i2s (peer));
 #endif
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# peers connected"),
+                           1,
+                           GNUNET_NO);
   cim.header.size = htons (sizeof (struct ConnectInfoMessage));
   cim.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
   cim.distance = htonl (distance);
@@ -1596,6 +2182,10 @@ notify_clients_disconnect (const struct GNUNET_PeerIdentity *peer)
              "Notifying clients about lost connection to `%s'\n",
              GNUNET_i2s (peer));
 #endif
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# peers connected"),
+                           -1,
+                           GNUNET_NO);
   dim.header.size = htons (sizeof (struct DisconnectInfoMessage));
   dim.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT);
   dim.reserved = htonl (0);
@@ -1615,6 +2205,9 @@ notify_clients_disconnect (const struct GNUNET_PeerIdentity *peer)
  *
  * @param neighbour which peer we care about
  * @param tname name of the transport plugin
+ * @param session session to look for, NULL for 'any'; otherwise
+ *        can be used for the service to "learn" this session ID
+ *        if 'addr' matches
  * @param addr binary address
  * @param addrlen length of addr
  * @return NULL if no such entry exists
@@ -1622,11 +2215,12 @@ notify_clients_disconnect (const struct GNUNET_PeerIdentity *peer)
 static struct ForeignAddressList *
 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 *address_head;
+  struct ForeignAddressList *pos;
 
   head = neighbour->plugins;
   while (head != NULL)
@@ -1637,13 +2231,19 @@ find_peer_address(struct NeighbourList *neighbour,
     }
   if (head == NULL)
     return NULL;
-
-  address_head = head->addresses;
-  while ( (address_head != NULL) &&
-         ( (address_head->addrlen != addrlen) ||
-           (memcmp(address_head->addr, addr, addrlen) != 0) ) )
-    address_head = address_head->next;
-  return address_head;
+  pos = head->addresses;
+  while ( (pos != NULL) &&
+         ( (pos->addrlen != addrlen) ||
+           (memcmp(pos->addr, addr, addrlen) != 0) ) )
+    {
+      if ( (session != NULL) &&
+          (pos->session == session) )
+       return pos;
+      pos = pos->next;
+    }
+  if ( (session != NULL) && (pos != NULL) )
+    pos->session = session; /* learn it! */
+  return pos;
 }
 
 
@@ -1653,23 +2253,26 @@ find_peer_address(struct NeighbourList *neighbour,
  *
  * @param neighbour which peer we care about
  * @param tname name of the transport plugin
+ * @param session session of the plugin, or NULL for none
  * @param addr binary address
  * @param addrlen length of addr
  * @return NULL if we do not have a transport plugin for 'tname'
  */
 static struct ForeignAddressList *
-add_peer_address(struct NeighbourList *neighbour,
-                const char *tname,
-                const char *addr, 
-                size_t addrlen)
+add_peer_address (struct NeighbourList *neighbour,
+                 const char *tname,
+                 struct Session *session,
+                 const char *addr, 
+                 uint16_t addrlen)
 {
   struct ReadyList *head;
   struct ForeignAddressList *ret;
 
-  ret = find_peer_address (neighbour, tname, addr, addrlen);
+  ret = find_peer_address (neighbour, tname, session, addr, addrlen);
   if (ret != NULL)
     return ret;
   head = neighbour->plugins;
+
   while (head != NULL)
     {
       if (0 == strcmp (tname, head->plugin->short_name))
@@ -1679,8 +2282,16 @@ add_peer_address(struct NeighbourList *neighbour,
   if (head == NULL)
     return NULL;
   ret = GNUNET_malloc(sizeof(struct ForeignAddressList) + addrlen);
-  ret->addr = (const char*) &ret[1];
-  memcpy (&ret[1], addr, addrlen);
+  ret->session = session;
+  if (addrlen > 0)
+    {
+      ret->addr = (const char*) &ret[1];
+      memcpy (&ret[1], addr, addrlen);
+    }
+  else
+    {
+      ret->addr = NULL;
+    }
   ret->addrlen = addrlen;
   ret->expires = GNUNET_TIME_relative_to_absolute
     (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
@@ -1743,223 +2354,6 @@ add_validated_address (void *cls,
 }
 
 
-/**
- * Iterator over hash map entries.  Checks if the given
- * validation entry is for the same challenge as what
- * is given in the PONG.
- *
- * @param cls the 'struct TransportPongMessage*'
- * @param key peer identity 
- * @param value value in the hash map ('struct ValidationEntry')
- * @return GNUNET_YES if we should continue to
- *         iterate (mismatch), GNUNET_NO if not (entry matched)
- */
-static int
-check_pending_validation (void *cls,
-                         const GNUNET_HashCode * key,
-                         void *value)
-{
-  const struct TransportPongMessage *pong = cls;
-  struct ValidationEntry *ve = value;
-  struct AddValidatedAddressContext avac;
-  unsigned int challenge = ntohl(pong->challenge);
-  struct GNUNET_HELLO_Message *hello;
-  struct GNUNET_PeerIdentity target;
-  struct NeighbourList *n;
-  struct ForeignAddressList *fal;
-
-  if (ve->challenge != challenge)
-    return GNUNET_YES;
-  
-#if DEBUG_TRANSPORT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Confirmed validity of address, peer `%4s' has address `%s' (%s).\n",
-             GNUNET_h2s (key),
-             GNUNET_a2s ((const struct sockaddr *) ve->addr,
-                         ve->addrlen),
-             ve->transport_name);
-#endif
-  /* create the updated HELLO */
-  GNUNET_CRYPTO_hash (&ve->publicKey,
-                      sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
-                      &target.hashPubKey);
-  avac.done = GNUNET_NO;
-  avac.ve = ve;
-  hello = GNUNET_HELLO_create (&ve->publicKey,
-                              &add_validated_address,
-                              &avac);
-  GNUNET_PEERINFO_add_peer (cfg, sched,
-                           &target, 
-                           hello);
-  GNUNET_free (hello);
-  n = find_neighbour (&target);
-  if (n != NULL)
-    {
-      fal = add_peer_address (n, ve->transport_name, 
-                             ve->addr,
-                             ve->addrlen);
-      fal->expires = GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION);
-      fal->validated = GNUNET_YES;
-      fal->latency = GNUNET_TIME_absolute_get_duration (ve->send_time);
-      if (n->latency.value == GNUNET_TIME_UNIT_FOREVER_REL.value)
-       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)
-       {
-         notify_clients_connect (&target, n->latency, n->distance);
-         n->received_pong = GNUNET_YES;
-       }
-      if (n->retry_task != GNUNET_SCHEDULER_NO_TASK)
-       {
-         GNUNET_SCHEDULER_cancel (sched,
-                                  n->retry_task);
-         n->retry_task = GNUNET_SCHEDULER_NO_TASK;     
-         try_transmission_to_peer (n);
-       }
-    }
-
-  /* clean up validation entry */
-  GNUNET_assert (GNUNET_YES ==
-                GNUNET_CONTAINER_multihashmap_remove (validation_map,
-                                                      key,
-                                                      ve));
-  GNUNET_SCHEDULER_cancel (sched,
-                          ve->timeout_task);
-  GNUNET_free (ve->transport_name);
-  GNUNET_free (ve);
-  return GNUNET_NO;
-}
-
-
-/**
- * Function that will be called if we receive a validation
- * of an address challenge that we transmitted to another
- * peer.  Note that the validation should only be considered
- * acceptable if the challenge matches AND if the sender
- * address is at least a plausible address for this peer
- * (otherwise we may be seeing a MiM attack).
- *
- * @param cls closure
- * @param message the pong message
- * @param peer who responded to our challenge
- * @param sender_addr string describing our sender address (as observed
- *         by the other peer in binary format)
- * @param sender_address_len number of bytes in 'sender_address'
- */
-static void
-handle_pong (void *cls, const struct GNUNET_MessageHeader *message,
-             const struct GNUNET_PeerIdentity *peer,
-             const char *sender_address,
-             size_t sender_address_len)
-{
-#if DEBUG_TRANSPORT > 1
-  /* we get tons of these that just get discarded, only log
-     if we are quite verbose */
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Receiving `%s' message from `%4s'.\n", "PONG",
-             GNUNET_i2s (peer));
-#endif
-  if (GNUNET_SYSERR != 
-      GNUNET_CONTAINER_multihashmap_get_multiple (validation_map,
-                                                 &peer->hashPubKey,
-                                                 &check_pending_validation,
-                                                 (void*) message))
-    {
-      /* This is *expected* to happen a lot since we send
-        PONGs to *all* known addresses of the sender of
-        the PING, so most likely we get multiple PONGs
-        per PING, and all but the first PONG will end up
-        here. So really we should not print anything here
-        unless we want to be very, very verbose... */
-#if DEBUG_TRANSPORT > 2
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Received `%s' message from `%4s' but have no record of a matching `%s' message. Ignoring.\n",
-                  "PONG",
-                 GNUNET_i2s (peer),
-                 "PING");
-#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);  
-#endif
-}
-
-
-static void
-neighbour_timeout_task (void *cls,
-                      const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
-  struct NeighbourList *n = cls;
-
-#if DEBUG_TRANSPORT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
-              "Neighbour `%4s' has timed out!\n", GNUNET_i2s (&n->id));
-#endif
-  n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
-  disconnect_neighbour (n, GNUNET_NO);
-}
-
-
-/**
- * Create a fresh entry in our neighbour list for the given peer.
- * Will try to transmit our current HELLO to the new neighbour.  Also
- * notifies our clients about the new "connection".
- *
- * @param peer the peer for which we create the entry
- * @return the new neighbour list entry
- */
-static struct NeighbourList *
-setup_new_neighbour (const struct GNUNET_PeerIdentity *peer)
-{
-  struct NeighbourList *n;
-  struct TransportPlugin *tp;
-  struct ReadyList *rl;
-
-  GNUNET_assert (our_hello != NULL);
-  n = GNUNET_malloc (sizeof (struct NeighbourList));
-  n->next = neighbours;
-  neighbours = n;
-  n->id = *peer;
-  n->last_quota_update = GNUNET_TIME_absolute_get ();
-  n->peer_timeout =
-    GNUNET_TIME_relative_to_absolute
-    (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
-  n->quota_in = (GNUNET_CONSTANTS_DEFAULT_BPM_IN_OUT + 59999) / (60 * 1000);
-  tp = plugins;
-  while (tp != NULL)
-    {
-      if (tp->api->send != NULL)
-        {
-          rl = GNUNET_malloc (sizeof (struct ReadyList));
-          rl->next = n->plugins;
-          n->plugins = rl;
-          rl->plugin = tp;
-          rl->addresses = NULL;
-        }
-      tp = tp->next;
-    }
-  n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
-  n->distance = -1;
-  n->timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
-                                                  GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
-                                                  &neighbour_timeout_task, n);
-  transmit_to_peer (NULL, NULL, 0,
-                   HELLO_ADDRESS_EXPIRATION,
-                    (const char *) our_hello, GNUNET_HELLO_size(our_hello),
-                    GNUNET_NO, n);
-  return n;
-}
-
 
 /**
  * Closure for 'check_address_exists'.
@@ -1977,14 +2371,20 @@ struct CheckAddressExistsClosure
   const char *tname;
 
   /**
-   * Length of addr.
+   * Session, or NULL.
    */
-  size_t addrlen;
+  struct Session *session;
 
   /**
    * Set to GNUNET_YES if the address exists.
    */
   int exists;
+
+  /**
+   * Length of addr.
+   */
+  uint16_t addrlen;
+
 };
 
 
@@ -2006,6 +2406,7 @@ check_address_exists (void *cls,
 {
   struct CheckAddressExistsClosure *caec = cls;
   struct ValidationEntry *ve = value;
+
   if ( (0 == strcmp (caec->tname,
                     ve->transport_name)) &&
        (caec->addrlen == ve->addrlen) &&
@@ -2016,31 +2417,1311 @@ check_address_exists (void *cls,
       caec->exists = GNUNET_YES;
       return GNUNET_NO;
     }
+  if ( (ve->session != NULL) &&
+       (caec->session == ve->session) )
+    {
+      caec->exists = GNUNET_YES;
+      return GNUNET_NO;
+    }
   return GNUNET_YES;
 }
 
 
+
 /**
- * HELLO validation cleanup task (validation failed).
+ * Iterator to free entries in the validation_map.
  *
- * @param cls the 'struct ValidationEntry' that failed
- * @param tc scheduler context (unused)
+ * @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).
+ *
+ * @param cls the 'struct ValidationEntry' that failed
+ * @param tc scheduler context (unused)
+ */
+static void
+timeout_hello_validation (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  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,
+                           GNUNET_NO);
+  GNUNET_CRYPTO_hash (&va->publicKey,
+                     sizeof (struct
+                             GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+                     &pid.hashPubKey);
+  GNUNET_break (GNUNET_OK ==
+                GNUNET_CONTAINER_multihashmap_remove (validation_map,
+                                                     &pid.hashPubKey,
+                                                     va));
+  abort_validation (NULL, NULL, va);
+}
+
+
+static void
+neighbour_timeout_task (void *cls,
+                      const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct NeighbourList *n = cls;
+
+#if DEBUG_TRANSPORT
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
+              "Neighbour `%4s' has timed out!\n", GNUNET_i2s (&n->id));
+#endif
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# disconnects due to timeout"),
+                           1,
+                           GNUNET_NO);
+  n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+  disconnect_neighbour (n, GNUNET_NO);
+}
+
+
+/**
+ * Schedule the job that will cause us to send a PING to the
+ * foreign address to evaluate its validity and latency.
+ *
+ * @param fal address to PING
+ */
+static void
+schedule_next_ping (struct ForeignAddressList *fal);
+
+
+/**
+ * Add the given address to the list of foreign addresses
+ * available for the given peer (check for duplicates).
+ *
+ * @param cls the respective 'struct NeighbourList' to update
+ * @param tname name of the transport
+ * @param expiration expiration time
+ * @param addr the address
+ * @param addrlen length of the address
+ * @return GNUNET_OK (always)
+ */
+static int
+add_to_foreign_address_list (void *cls,
+                            const char *tname,
+                            struct GNUNET_TIME_Absolute expiration,
+                            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"),
+                           1,
+                           GNUNET_NO);      
+  try = GNUNET_NO;
+  fal = find_peer_address (n, tname, NULL, addr, addrlen);
+  if (fal == NULL)
+    {
+#if DEBUG_TRANSPORT
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "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);
+#endif
+      fal = add_peer_address (n, tname, NULL, addr, addrlen);
+      if (fal == NULL)
+       {
+         GNUNET_STATISTICS_update (stats,
+                                   gettext_noop ("# previously validated addresses lacking transport"),
+                                   1,
+                                   GNUNET_NO); 
+       }
+      else
+       {
+         fal->expires = GNUNET_TIME_absolute_max (expiration,
+                                                  fal->expires);
+         schedule_next_ping (fal);
+       }
+      try = GNUNET_YES;
+    }
+  else
+    {
+      fal->expires = GNUNET_TIME_absolute_max (expiration,
+                                              fal->expires);
+    }
+  if (fal == NULL)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Failed to add new address for `%4s'\n",
+                 GNUNET_i2s (&n->id));
+      return GNUNET_OK;
+    }
+  if (fal->validated == GNUNET_NO)
+    {
+      fal->validated = GNUNET_YES;  
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# peer addresses considered valid"),
+                               1,
+                               GNUNET_NO);      
+    }
+  if (try == GNUNET_YES)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Have new addresses, will try to trigger transmissions.\n");
+      try_transmission_to_peer (n);
+    }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Add addresses in validated HELLO "h" to the set of addresses
+ * we have for this peer.
+ *
+ * @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)
+ */
+static void
+add_hello_for_peer (void *cls,
+                   const struct GNUNET_PeerIdentity *peer,
+                   const struct GNUNET_HELLO_Message *h)
+{
+  struct NeighbourList *n = cls;
+
+  if (peer == NULL)
+    {
+      n->piter = NULL;
+      return;
+    } 
+  if (h == NULL)
+    return; /* no HELLO available */
+#if DEBUG_TRANSPORT
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Peerinfo had `%s' message for peer `%4s', adding existing addresses.\n",
+             "HELLO",
+             GNUNET_i2s (peer));
+#endif
+  if (GNUNET_YES != n->public_key_valid)
+    {
+      GNUNET_HELLO_get_key (h, &n->publicKey);
+      n->public_key_valid = GNUNET_YES;
+    }
+  GNUNET_HELLO_iterate_addresses (h,
+                                 GNUNET_NO,
+                                 &add_to_foreign_address_list,
+                                 n);
+}
+
+
+/**
+ * Create a fresh entry in our neighbour list for the given peer.
+ * 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,
+                    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"),
+                           1,
+                           GNUNET_NO);
+  n = GNUNET_malloc (sizeof (struct NeighbourList));
+  n->next = neighbours;
+  neighbours = n;
+  n->id = *peer;
+  n->peer_timeout =
+    GNUNET_TIME_relative_to_absolute
+    (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
+  GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
+                                GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
+                                MAX_BANDWIDTH_CARRY_S);
+  tp = plugins;
+  while (tp != NULL)
+    {
+      if ((tp->api->send != NULL) && (!is_blacklisted(peer, tp)))
+        {
+          rl = GNUNET_malloc (sizeof (struct ReadyList));
+         rl->neighbour = n;
+          rl->next = n->plugins;
+          n->plugins = rl;
+          rl->plugin = tp;
+          rl->addresses = NULL;
+        }
+      tp = tp->next;
+    }
+  n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
+  n->distance = -1;
+  n->timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
+                                                  GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
+                                                  &neighbour_timeout_task, 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;
+}
+
+
+/**
+ * Function called after we have checked if communicating
+ * with a given peer is acceptable.  
+ *
+ * @param cls closure
+ * @param n NULL if communication is not acceptable
+ */
+typedef void (*SetupContinuation)(void *cls,
+                                 struct NeighbourList *n);
+
+
+/**
+ * Information kept for each client registered to perform
+ * blacklisting.
+ */
+struct Blacklisters
+{
+  /**
+   * This is a linked list.
+   */
+  struct Blacklisters *next;
+
+  /**
+   * 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)
+                  ? a2s (tp->short_name,
+                        peer_address->addr,
+                        peer_address->addrlen)
+                 : "<inbound>",
+                  tp->short_name,
+                  GNUNET_i2s (&neighbour->id));
+#endif
+      schedule_next_ping (peer_address);     
+      return;
+    }
+  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,
+                                            UINT_MAX);
+  va->send_time = GNUNET_TIME_absolute_get();
+  va->session = peer_address->session;
+  if (peer_address->addr != NULL)
+    {
+      va->addr = (const void*) &va[1];
+      memcpy (&va[1], peer_address->addr, peer_address->addrlen);
+      va->addrlen = peer_address->addrlen;
+    }
+  memcpy(&va->publicKey,
+        &neighbour->publicKey, 
+        sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
+
+  va->timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
+                                                   HELLO_VERIFICATION_TIMEOUT,
+                                                   &timeout_hello_validation,
+                                                   va);
+  GNUNET_CONTAINER_multihashmap_put (validation_map,
+                                     &neighbour->id.hashPubKey,
+                                     va,
+                                     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.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'\n",
+              (peer_address->addr != NULL) 
+             ? a2s (peer_address->plugin->short_name,
+                    peer_address->addr,
+                    peer_address->addrlen)
+             : "<inbound>",
+              tp->short_name,
+              GNUNET_i2s (&neighbour->id),
+              "HELLO", hello_size,
+              "PING");
+#endif
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# PING messages sent for re-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);
+  schedule_next_ping (peer_address);
+}
+
+
+/**
+ * Schedule the job that will cause us to send a PING to the
+ * foreign address to evaluate its validity and latency.
+ *
+ * @param fal address to PING
+ */
+static void
+schedule_next_ping (struct ForeignAddressList *fal)
+{
+  struct GNUNET_TIME_Relative delay;
+
+  if (fal->revalidate_task != GNUNET_SCHEDULER_NO_TASK)
+    return;
+  delay = GNUNET_TIME_absolute_get_remaining (fal->expires);
+  delay.value /= 2; /* do before expiration */
+  delay = GNUNET_TIME_relative_min (delay,
+                                   LATENCY_EVALUATION_MAX_DELAY);
+  if (GNUNET_YES != fal->estimated)
+    {
+      delay = GNUNET_TIME_UNIT_ZERO;
+      fal->estimated = GNUNET_YES;
+    }                              
+  if (GNUNET_YES == fal->connected)
+    {
+      delay = GNUNET_TIME_relative_min (delay,
+                                       CONNECTED_LATENCY_EVALUATION_MAX_DELAY);
+    }  
+  /* FIXME: also adjust delay based on how close the last
+     observed latency is to the latency of the best alternative */
+  /* bound how fast we can go */
+  delay = GNUNET_TIME_relative_max (delay,
+                                   GNUNET_TIME_UNIT_SECONDS);
+  /* randomize a bit (to avoid doing all at the same time) */
+  delay.value += GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1000);
+  fal->revalidate_task = GNUNET_SCHEDULER_add_delayed(sched, 
+                                                     delay,
+                                                     &send_periodic_ping, 
+                                                     fal);
+}
+
+
+
+
+/**
+ * Function that will be called if we receive some payload
+ * from another peer.
+ *
+ * @param message the payload
+ * @param n peer who claimed to be the sender
+ */
+static void
+handle_payload_message (const struct GNUNET_MessageHeader *message,
+                       struct NeighbourList *n)
+{
+  struct InboundMessage *im;
+  struct TransportClient *cpos;
+  uint16_t msize;
+
+  msize = ntohs (message->size);
+  if (n->received_pong == GNUNET_NO)
+    {
+      GNUNET_free_non_null (n->pre_connect_message_buffer);
+      n->pre_connect_message_buffer = GNUNET_malloc (msize);
+      memcpy (n->pre_connect_message_buffer, message, msize);
+      return;
+    }
+#if DEBUG_TRANSPORT
+  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 (&n->id));
+#endif
+  if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker,
+                                                     (ssize_t) msize))
+    {
+      n->quota_violation_count++;
+#if DEBUG_TRANSPORT
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,                       
+                 "Bandwidth quota (%u b/s) violation detected (total of %u).\n", 
+                 n->in_tracker.available_bytes_per_s__,
+                 n->quota_violation_count);
+#endif
+      /* Discount 32k per violation */
+      GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker,
+                                       - 32 * 1024);           
+    }
+  else 
+    {
+      if (n->quota_violation_count > 0)
+       {
+         /* try to add 32k back */
+         GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker,
+                                           32 * 1024);
+         n->quota_violation_count--;
+       }
+    }
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# payload received from other peers"),
+                           msize,
+                           GNUNET_NO);
+  /* transmit message to all clients */
+  im = GNUNET_malloc (sizeof (struct InboundMessage) + msize);
+  im->header.size = htons (sizeof (struct InboundMessage) + msize);
+  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)
+    {
+      transmit_to_client (cpos, &im->header, GNUNET_YES);
+      cpos = cpos->next;
+    }
+  GNUNET_free (im);
+}
+
+
+/**
+ * Iterator over hash map entries.  Checks if the given validation
+ * entry is for the same challenge as what is given in the PONG.
+ *
+ * @param cls the 'struct TransportPongMessage*'
+ * @param key peer identity
+ * @param value value in the hash map ('struct ValidationEntry')
+ * @return GNUNET_YES if we should continue to
+ *         iterate (mismatch), GNUNET_NO if not (entry matched)
+ */
+static int
+check_pending_validation (void *cls,
+                         const GNUNET_HashCode * key,
+                         void *value)
+{
+  const struct TransportPongMessage *pong = cls;
+  struct ValidationEntry *ve = value;
+  struct AddValidatedAddressContext avac;
+  unsigned int challenge = ntohl(pong->challenge);
+  struct GNUNET_HELLO_Message *hello;
+  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;
+
+  ps = ntohs (pong->header.size);
+  if (ps < sizeof (struct TransportPongMessage))
+    {
+      GNUNET_break_op (0);
+      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),
+                 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,
+                           GNUNET_NO);
+  /* create the updated HELLO */
+  GNUNET_CRYPTO_hash (&ve->publicKey,
+                      sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+                      &target.hashPubKey);
+  if (ve->addr != NULL)
+    {
+      avac.done = GNUNET_NO;
+      avac.ve = ve;
+      hello = GNUNET_HELLO_create (&ve->publicKey,
+                                  &add_validated_address,
+                                  &avac);
+      GNUNET_PEERINFO_add_peer (peerinfo,
+                               hello);
+      GNUNET_free (hello);
+    }
+  n = find_neighbour (&target);
+  if (n != NULL)
+    {
+      n->publicKey = ve->publicKey;
+      n->public_key_valid = GNUNET_YES;
+      fal = add_peer_address (n,
+                             ve->transport_name,
+                             ve->session,
+                             ve->addr,
+                             ve->addrlen);
+      GNUNET_assert (fal != NULL);
+      fal->expires = GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION);
+      fal->validated = GNUNET_YES;
+      mark_address_connected (fal);
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# peer addresses considered valid"),
+                               1,
+                               GNUNET_NO);      
+      fal->latency = GNUNET_TIME_absolute_get_duration (ve->send_time);
+      schedule_next_ping (fal);
+      if (n->latency.value == GNUNET_TIME_UNIT_FOREVER_REL.value)
+       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)
+       {
+         n->received_pong = GNUNET_YES;
+         notify_clients_connect (&target, n->latency, n->distance);
+         if (NULL != (prem = n->pre_connect_message_buffer))
+           {
+             n->pre_connect_message_buffer = NULL;
+             handle_payload_message (prem, n);
+             GNUNET_free (prem);
+           }
+       }
+      if (n->retry_task != GNUNET_SCHEDULER_NO_TASK)
+       {
+         GNUNET_SCHEDULER_cancel (sched,
+                                  n->retry_task);
+         n->retry_task = GNUNET_SCHEDULER_NO_TASK;
+         try_transmission_to_peer (n);
+       }
+    }
+
+  /* clean up validation entry */
+  GNUNET_assert (GNUNET_YES ==
+                GNUNET_CONTAINER_multihashmap_remove (validation_map,
+                                                      key,
+                                                      ve));
+  abort_validation (NULL, NULL, ve);
+  return GNUNET_NO;
+}
+
+
+/**
+ * Function that will be called if we receive a validation
+ * of an address challenge that we transmitted to another
+ * peer.  Note that the validation should only be considered
+ * acceptable if the challenge matches AND if the sender
+ * address is at least a plausible address for this peer
+ * (otherwise we may be seeing a MiM attack).
+ *
+ * @param cls closure
+ * @param message the pong message
+ * @param peer who responded to our challenge
+ * @param sender_address string describing our sender address (as observed
+ *         by the other peer in binary format)
+ * @param sender_address_len number of bytes in 'sender_address'
  */
 static void
-timeout_hello_validation (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+handle_pong (void *cls, const struct GNUNET_MessageHeader *message,
+             const struct GNUNET_PeerIdentity *peer,
+             const char *sender_address,
+             size_t sender_address_len)
+{
+#if DEBUG_TRANSPORT > 1
+  /* we get tons of these that just get discarded, only log
+     if we are quite verbose */
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Receiving `%s' message from `%4s'.\n", "PONG",
+             GNUNET_i2s (peer));
+#endif
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# PONG messages received"),
+                           1,
+                           GNUNET_NO);
+  if (GNUNET_SYSERR !=
+      GNUNET_CONTAINER_multihashmap_get_multiple (validation_map,
+                                                 &peer->hashPubKey,
+                                                 &check_pending_validation,
+                                                 (void*) message))
+    {
+      /* This is *expected* to happen a lot since we send
+        PONGs to *all* known addresses of the sender of
+        the PING, so most likely we get multiple PONGs
+        per PING, and all but the first PONG will end up
+        here. So really we should not print anything here
+        unless we want to be very, very verbose... */
+#if DEBUG_TRANSPORT > 2
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Received `%s' message from `%4s' but have no record of a matching `%s' message. Ignoring.\n",
+                  "PONG",
+                 GNUNET_i2s (peer),
+                 "PING");
+#endif
+      return;
+    }
+
+}
+
+
+/**
+ * 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 GNUNET_PeerIdentity pid;
+  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),
-                     &pid.hashPubKey);
-  GNUNET_CONTAINER_multihashmap_remove (validation_map,
-                                       &pid.hashPubKey,
-                                       va);
-  GNUNET_free (va->transport_name);
-  GNUNET_free (va);
+                     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);
 }
 
 
@@ -2060,21 +3741,23 @@ 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,
+                           GNUNET_NO);      
   tp = find_transport (tname);
   if (tp == NULL)
     {
@@ -2083,15 +3766,50 @@ 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 (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,
@@ -2106,16 +3824,22 @@ 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), 
-                 tname, 
+                 a2s (tname, addr, addrlen),
+                 tname,
                  GNUNET_i2s (&id));
 #endif
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# peer addresses not validated (in progress)"),
+                               1,
+                               GNUNET_NO);      
       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);
@@ -2125,84 +3849,14 @@ run_validation (void *cls,
   va->timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
                                                   HELLO_VERIFICATION_TIMEOUT,
                                                   &timeout_hello_validation,
-                                                  va);  
+                                                  va);
   GNUNET_CONTAINER_multihashmap_put (validation_map,
                                     &id.hashPubKey,
                                     va,
                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
-  neighbour = find_neighbour(&id);  
-  if (neighbour == NULL)
-    neighbour = setup_new_neighbour(&id);
-  peer_address = add_peer_address(neighbour, tname, 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
-  transmit_to_peer (NULL, peer_address, 
-                   GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-                   HELLO_VERIFICATION_TIMEOUT,
-                   message_buf, tsize, 
-                   GNUNET_YES, neighbour);
-  GNUNET_free(message_buf);
-  return GNUNET_OK;
-}
-
-
-/**
- * Add the given address to the list of foreign addresses
- * available for the given peer (check for duplicates).
- *
- * @param cls the respective 'struct NeighbourList' to update
- * @param tname name of the transport
- * @param expiration expiration time
- * @param addr the address
- * @param addrlen length of the address
- * @return GNUNET_OK (always)
- */
-static int
-add_to_foreign_address_list (void *cls,
-                            const char *tname,
-                            struct GNUNET_TIME_Absolute expiration,
-                            const void *addr, size_t addrlen)
-{
-  struct NeighbourList *n = cls;
-  struct ForeignAddressList *fal;
-
-  fal = find_peer_address (n, tname, addr, addrlen);
-  if (fal == NULL)
-    {
-#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),
-                 tname,
-                 GNUNET_i2s (&n->id),
-                 expiration.value);
-#endif
-      fal = add_peer_address (n, tname, addr, addrlen);
-    }
-  if (fal == NULL)
-    return GNUNET_OK;
-  fal->expires = GNUNET_TIME_absolute_max (expiration,
-                                          fal->expires);
-  fal->validated = GNUNET_YES;
+  setup_peer_check_blacklist (&id, GNUNET_NO,
+                             &transmit_hello_and_ping,
+                             va);
   return GNUNET_OK;
 }
 
@@ -2214,13 +3868,11 @@ add_to_foreign_address_list (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;
@@ -2231,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
@@ -2245,24 +3894,48 @@ 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
+         GNUNET_STATISTICS_update (stats,
+                                   gettext_noop ("# new HELLOs requiring full validation"),
+                                   1,
+                                   GNUNET_NO);      
          GNUNET_HELLO_iterate_addresses (chvc->hello,
                                          GNUNET_NO, 
                                          &run_validation, 
                                          chvc);
        }
-      GNUNET_free (chvc);
+      else
+       {
+         GNUNET_STATISTICS_update (stats,
+                                   gettext_noop ("# duplicate HELLO (peer known)"),
+                                   1,
+                                   GNUNET_NO);      
+       }
+      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",
+             "HELLO",
+             GNUNET_i2s (peer));
+#endif
   chvc->hello_known = GNUNET_YES;
   n = find_neighbour (peer);
   if (n != NULL)
@@ -2273,6 +3946,17 @@ check_hello_validated (void *cls,
                                      n);
       try_transmission_to_peer (n);
     }
+  else
+    {
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# no existing neighbour record (validating HELLO)"),
+                               1,
+                               GNUNET_NO);      
+    }
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# HELLO validations (update case)"),
+                           1,
+                           GNUNET_NO);      
   GNUNET_HELLO_iterate_new_addresses (chvc->hello,
                                      h,
                                      GNUNET_TIME_relative_to_absolute (HELLO_REVALIDATION_START_TIME),
@@ -2280,6 +3964,7 @@ check_hello_validated (void *cls,
                                      chvc);
 }
 
+
 /**
  * Process HELLO-message.
  *
@@ -2296,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)))
@@ -2304,31 +3991,90 @@ process_hello (struct TransportPlugin *plugin,
       GNUNET_break (0);
       return GNUNET_SYSERR;
     }
+  GNUNET_STATISTICS_update (stats,
+                           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)
     {
-      /* TODO: call to stats? */
+      GNUNET_STATISTICS_update (stats,
+                               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 DEBUG_TRANSPORT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Processing `%s' message for `%4s' of size %u\n",
-              "HELLO", 
-             GNUNET_i2s (&target), 
-             GNUNET_HELLO_size(hello));
-#endif
 
+  if (0 == memcmp (&my_identity,
+                  &target,
+                  sizeof (struct GNUNET_PeerIdentity)))
+    {
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# HELLOs ignored for validation (is my own HELLO)"),
+                               1,
+                               GNUNET_NO);      
+      return GNUNET_OK;      
+    }
+  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,
@@ -2336,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;
@@ -2412,11 +4156,26 @@ disconnect_neighbour (struct NeighbourList *n, int check)
     {
       n->plugins = rpos->next;
       rpos->plugin->api->disconnect (rpos->plugin->api->cls, &n->id);
-
       while (rpos->addresses != NULL)
         {
           peer_pos = rpos->addresses;
           rpos->addresses = peer_pos->next;
+         if (peer_pos->connected == GNUNET_YES)
+           GNUNET_STATISTICS_update (stats,
+                                     gettext_noop ("# connected addresses"),
+                                     -1,
+                                     GNUNET_NO); 
+         if (GNUNET_YES == peer_pos->validated)
+           GNUNET_STATISTICS_update (stats,
+                                     gettext_noop ("# peer addresses considered valid"),
+                                     -1,
+                                     GNUNET_NO);      
+         if (GNUNET_SCHEDULER_NO_TASK != peer_pos->revalidate_task)
+           {
+             GNUNET_SCHEDULER_cancel (sched,
+                                      peer_pos->revalidate_task);
+             peer_pos->revalidate_task = GNUNET_SCHEDULER_NO_TASK;
+           }
           GNUNET_free(peer_pos);
         }
       GNUNET_free (rpos);
@@ -2425,6 +4184,14 @@ disconnect_neighbour (struct NeighbourList *n, int check)
   /* free all messages on the queue */
   while (NULL != (mq = n->messages_head))
     {
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# bytes in message queue for other peers"),
+                               - (int64_t) mq->message_buf_size,
+                               GNUNET_NO);
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# bytes discarded due to disconnect"),
+                               mq->message_buf_size,
+                               GNUNET_NO);
       GNUNET_CONTAINER_DLL_remove (n->messages_head,
                                   n->messages_tail,
                                   mq);
@@ -2443,7 +4210,17 @@ disconnect_neighbour (struct NeighbourList *n, int check)
       GNUNET_SCHEDULER_cancel (sched, n->retry_task);
       n->retry_task = GNUNET_SCHEDULER_NO_TASK;
     }
+  if (n->piter != NULL)
+    {
+      GNUNET_PEERINFO_iterate_cancel (n->piter);
+      n->piter = NULL;
+    }
   /* finally, free n itself */
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# active neighbours"),
+                           -1,
+                           GNUNET_NO);
+  GNUNET_free_non_null (n->pre_connect_message_buffer);
   GNUNET_free (n);
 }
 
@@ -2451,31 +4228,32 @@ disconnect_neighbour (struct NeighbourList *n, int check)
 /**
  * We have received a PING message from someone.  Need to send a PONG message
  * in response to the peer by any means necessary. 
- *
- * FIXME: With something like TCP where a connection exists, we may
- * want to send it that way.  But the current API does not seem to
- * allow us to do so (can't tell this to the transport!)
  */
 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;
-  uint16_t msize;
   struct NeighbourList *n;
   struct ReadyList *rl;
   struct ForeignAddressList *fal;
+  struct OwnAddressList *oal;
+  const char *addr;
+  size_t alen;
+  size_t slen;
 
-  msize = ntohs (message->size);
-  if (msize < 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,
@@ -2486,36 +4264,195 @@ 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
-  msize -= sizeof (struct TransportPingMessage);
-  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_TCP_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));
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# PING messages received"),
+                           1,
+                           GNUNET_NO);
+  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);
-  if (n == NULL)
-    n = setup_new_neighbour(peer);
-  /* broadcast 'PONG' to all available addresses */
+  GNUNET_assert (n != NULL);
+  /* first try reliable response transmission */
+  rl = n->plugins;
+  while (rl != NULL)
+    {
+      fal = rl->addresses;
+      while (fal != NULL)
+       {
+         if (-1 != rl->plugin->api->send (rl->plugin->api->cls,
+                                          peer,
+                                          (const char*) pong,
+                                          ntohs (pong->header.size),
+                                          TRANSPORT_PONG_PRIORITY, 
+                                          HELLO_VERIFICATION_TIMEOUT,
+                                          fal->session,
+                                          fal->addr,
+                                          fal->addrlen,
+                                          GNUNET_SYSERR,
+                                          NULL, NULL))
+           {
+             /* done! */
+             GNUNET_STATISTICS_update (stats,
+                                       gettext_noop ("# PONGs unicast via reliable transport"),
+                                       1,
+                                       GNUNET_NO);      
+             GNUNET_free (pong);
+             return GNUNET_OK;
+           }
+         fal = fal->next;
+       }
+      rl = rl->next;
+    }
+  /* no reliable method found, do multicast */
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# PONGs multicast to all available addresses"),
+                           1,
+                           GNUNET_NO);      
   rl = n->plugins;
   while (rl != NULL)
     {
@@ -2545,129 +4482,136 @@ handle_ping(void *cls, const struct GNUNET_MessageHeader *message,
  * and generally forward to our receive callback.
  *
  * @param cls the "struct TransportPlugin *" we gave to the plugin
- * @param message the message, NULL if peer was disconnected
- * @param distance the transport cost to this peer (not latency!)
- * @param sender_address the address that the sender reported
- *        (opaque to transport service)
- * @param sender_address_len the length of the sender address
  * @param peer (claimed) identity of the other peer
- * @return the new service_context that the plugin should use
- *         for future receive calls for messages from this
- *         particular peer
- *
+ * @param message the message, NULL if we only care about
+ *                learning about the delay until we should receive again
+ * @param distance in overlay hops; use 1 unless DV (or 0 if message == NULL)
+ * @param session identifier used for this session (can be NULL)
+ * @param sender_address binary address of the sender (if observed)
+ * @param sender_address_len number of bytes in sender_address
+ * @return how long the plugin should wait until receiving more data
+ *         (plugins that do not support this, can ignore the return value)
  */
-static void
+static struct GNUNET_TIME_Relative
 plugin_env_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
                     const struct GNUNET_MessageHeader *message,
-                    unsigned int distance, const char *sender_address,
-                    size_t sender_address_len)
+                    uint32_t distance,
+                   struct Session *session,
+                   const char *sender_address,
+                    uint16_t sender_address_len)
 {
-  struct ReadyList *service_context;
   struct TransportPlugin *plugin = cls;
-  struct TransportClient *cpos;
-  struct InboundMessage *im;
+  struct ReadyList *service_context;
   struct ForeignAddressList *peer_address;
   uint16_t msize;
   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)
-    {
-      if (message == NULL)
-        return;                 /* disconnect of peer already marked down */
-      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;
   GNUNET_assert ((plugin->api->send == NULL) || (service_context != NULL));
-  if (message == NULL)
+  peer_address = NULL;
+  if (message != NULL)
     {
-#if DEBUG_TRANSPORT
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
-                  "Receive failed from `%4s', triggering disconnect\n",
-                  GNUNET_i2s (&n->id));
+      if ( (session != NULL) ||
+          (sender_address != NULL) )
+       peer_address = add_peer_address (n, 
+                                        plugin->short_name,
+                                        session,
+                                        sender_address, 
+                                        sender_address_len);  
+      if (peer_address != NULL)
+       {
+         peer_address->distance = distance;
+         if (GNUNET_YES == peer_address->validated)
+           mark_address_connected (peer_address);
+         peer_address->timeout
+           =
+           GNUNET_TIME_relative_to_absolute
+           (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
+         schedule_next_ping (peer_address);
+       }
+      /* update traffic received amount ... */
+      msize = ntohs (message->size);      
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# bytes received from other peers"),
+                               msize,
+                               GNUNET_NO);
+      n->distance = distance;
+      n->peer_timeout =
+       GNUNET_TIME_relative_to_absolute
+       (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
+      GNUNET_SCHEDULER_cancel (sched,
+                              n->timeout_task);
+      n->timeout_task =
+       GNUNET_SCHEDULER_add_delayed (sched,
+                                     GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
+                                     &neighbour_timeout_task, n);
+      if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
+       {
+         /* dropping message due to frequent inbound volume violations! */
+         GNUNET_log (GNUNET_ERROR_TYPE_WARNING |
+                     GNUNET_ERROR_TYPE_BULK,
+                     _
+                     ("Dropping incoming message due to repeated bandwidth quota (%u b/s) violations (total of %u).\n"), 
+                     n->in_tracker.available_bytes_per_s__,
+                     n->quota_violation_count);
+         GNUNET_STATISTICS_update (stats,
+                                   gettext_noop ("# bandwidth quota violations by other peers"),
+                                   1,
+                                   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
-      /* TODO: call stats */
-      disconnect_neighbour (n, GNUNET_YES);
-      return;
-    }
-  peer_address = add_peer_address(n, 
-                                 plugin->short_name,
-                                 sender_address, 
-                                 sender_address_len);  
-  if (peer_address != NULL)
-    {
-      peer_address->distance = distance;
-      if (peer_address->connected == GNUNET_NO)
-        {
-         peer_address->connected = GNUNET_YES;
-          peer_address->connect_attempts++;
-        }
-      peer_address->timeout
-        =
-        GNUNET_TIME_relative_to_absolute
-        (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
-    }
-  /* update traffic received amount ... */
-  msize = ntohs (message->size);
-  n->last_received += msize;
-  n->distance = distance;
-  n->peer_timeout =
-    GNUNET_TIME_relative_to_absolute
-    (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
-  GNUNET_SCHEDULER_cancel (sched,
-                          n->timeout_task);
-  n->timeout_task =
-    GNUNET_SCHEDULER_add_delayed (sched,
-                                  GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
-                                  &neighbour_timeout_task, n);
-  update_quota (n);
-  if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
-    {
-      /* dropping message due to frequent inbound volume violations! */
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING |
-                  GNUNET_ERROR_TYPE_BULK,
-                  _
-                  ("Dropping incoming message due to repeated bandwidth quota violations (total of %u).\n"), n->quota_violation_count);
-      /* TODO: call stats */
-      return;
-    }
-  switch (ntohs (message->type))
+      switch (ntohs (message->type))
+       {
+       case GNUNET_MESSAGE_TYPE_HELLO:
+         GNUNET_STATISTICS_update (stats,
+                                   gettext_noop ("# HELLO messages received from other peers"),
+                                   1,
+                                   GNUNET_NO);
+         process_hello (plugin, message);
+         break;
+       case GNUNET_MESSAGE_TYPE_TRANSPORT_PING:
+         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);
+         break;
+       default:
+         handle_payload_message (message, n);
+         break;
+       }
+    }  
+  ret = GNUNET_BANDWIDTH_tracker_get_delay (&n->in_tracker, 0);
+  if (ret.value > 0)
     {
-    case GNUNET_MESSAGE_TYPE_HELLO:
-      process_hello (plugin, message);
-      break;
-    case GNUNET_MESSAGE_TYPE_TRANSPORT_PING:
-      handle_ping(plugin, message, peer, sender_address, sender_address_len);
-      break;
-    case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG:
-      handle_pong(plugin, message, peer, sender_address, sender_address_len);
-      break;
-    default:
-#if DEBUG_TRANSPORT
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Received message of type %u from `%4s', sending to all clients.\n",
-                  ntohs (message->type), GNUNET_i2s (peer));
-#endif
-      /* transmit message to all clients */
-      im = GNUNET_malloc (sizeof (struct InboundMessage) + msize);
-      im->header.size = htons (sizeof (struct InboundMessage) + msize);
-      im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV);
-      im->latency = GNUNET_TIME_relative_hton (n->latency);
-      im->peer = *peer;
-      memcpy (&im[1], message, msize);
-      cpos = clients;
-      while (cpos != NULL)
-        {
-          transmit_to_client (cpos, &im->header, GNUNET_YES);
-          cpos = cpos->next;
-        }
-      GNUNET_free (im);
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                 "Throttling read (%llu bytes excess at %u b/s), waiting %llums before reading more.\n",
+                 (unsigned long long) n->in_tracker.consumption_since_last_update__,
+                 (unsigned int) n->in_tracker.available_bytes_per_s__,
+                 (unsigned long long) ret.value);
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# ms throttling suggested"),
+                               (int64_t) ret.value,
+                               GNUNET_NO);      
     }
+  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.
@@ -2748,11 +4692,73 @@ handle_hello (void *cls,
 {
   int ret;
 
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# HELLOs received from clients"),
+                           1,
+                           GNUNET_NO);      
   ret = process_hello (NULL, message);
   GNUNET_SERVER_receive_done (client, ret);
 }
 
 
+/**
+ * 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.
  *
@@ -2765,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;
 
@@ -2780,38 +4785,31 @@ handle_send (void *cls,
       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
       return;
     }
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# payload received for other peers"),
+                           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);
 }
 
 
@@ -2830,38 +4828,44 @@ handle_set_quota (void *cls,
   const struct QuotaSetMessage *qsm =
     (const struct QuotaSetMessage *) message;
   struct NeighbourList *n;
-  struct TransportPlugin *p;
-  struct ReadyList *rl;
-
+  
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# SET QUOTA messages received"),
+                           1,
+                           GNUNET_NO);      
   n = find_neighbour (&qsm->peer);
   if (n == NULL)
     {
       GNUNET_SERVER_receive_done (client, GNUNET_OK);
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# SET QUOTA messages ignored (no such peer)"),
+                               1,
+                               GNUNET_NO);      
       return;
     }
-
 #if DEBUG_TRANSPORT
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Received `%s' request (new quota %u, old quota %u) from client for peer `%4s'\n",
-              "SET_QUOTA", ntohl(qsm->quota_in), n->quota_in, GNUNET_i2s (&qsm->peer));
+              "SET_QUOTA", 
+             (unsigned int) ntohl (qsm->quota.value__),
+             (unsigned int) n->in_tracker.available_bytes_per_s__,
+             GNUNET_i2s (&qsm->peer));
 #endif
-
-  update_quota (n);
-  if (n->quota_in < ntohl (qsm->quota_in))
-    n->last_quota_update = GNUNET_TIME_absolute_get ();
-  n->quota_in = ntohl (qsm->quota_in);
-  rl = n->plugins;
-  while (rl != NULL)
-    {
-      p = rl->plugin;
-      p->api->set_receive_quota (p->api->cls,
-                                 &qsm->peer, ntohl (qsm->quota_in));
-      rl = rl->next;
-    }
+  GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker,
+                                        qsm->quota);
+  if (0 == ntohl (qsm->quota.value__)) 
+    disconnect_neighbour (n, GNUNET_NO);    
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
 
+/**
+ * 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*')
+ * @param address the resolved name, NULL to indicate the last response
+ */
 static void
 transmit_address_to_client (void *cls, const char *address)
 {
@@ -2872,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)
@@ -2897,6 +4902,9 @@ handle_address_lookup (void *cls,
   const char *address;
   uint16_t size;
   struct GNUNET_SERVER_TransmitContext *tc;
+  struct GNUNET_TIME_Absolute timeout;
+  struct GNUNET_TIME_Relative rtimeout;
+  int32_t numeric;
 
   size = ntohs (message->size);
   if (size < sizeof (struct AddressLookupMessage))
@@ -2922,10 +4930,9 @@ handle_address_lookup (void *cls,
       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
       return;
     }
-  struct GNUNET_TIME_Absolute timeout =
-    GNUNET_TIME_absolute_ntoh (alum->timeout);
-  struct GNUNET_TIME_Relative rtimeout =
-    GNUNET_TIME_absolute_get_remaining (timeout);
+  timeout = GNUNET_TIME_absolute_ntoh (alum->timeout);
+  rtimeout = GNUNET_TIME_absolute_get_remaining (timeout);
+  numeric = ntohl (alum->numeric_only);
   lsPlugin = find_transport (nameTransport);
   if (NULL == lsPlugin)
     {
@@ -2936,31 +4943,14 @@ handle_address_lookup (void *cls,
       return;
     }
   tc = GNUNET_SERVER_transmit_context_create (client);
-  lsPlugin->api->address_pretty_printer (cls, nameTransport,
-                                         address, addressLen, GNUNET_YES,
+  lsPlugin->api->address_pretty_printer (lsPlugin->api->cls,
+                                        nameTransport,
+                                         address, addressLen, 
+                                        numeric,
                                          rtimeout,
                                          &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.
@@ -2974,9 +4964,9 @@ create_environment (struct TransportPlugin *plug)
   plug->env.cls = plug;
   plug->env.receive = &plugin_env_receive;
   plug->env.notify_address = &plugin_env_notify_address;
-  plug->env.default_quota_in =
-    (GNUNET_CONSTANTS_DEFAULT_BPM_IN_OUT + 59999) / (60 * 1000);
+  plug->env.session_end = &plugin_env_session_end;
   plug->env.max_connections = max_connect_per_transport;
+  plug->env.stats = stats;
 }
 
 
@@ -2984,7 +4974,8 @@ create_environment (struct TransportPlugin *plug)
  * Start the specified transport (load the plugin).
  */
 static void
-start_transport (struct GNUNET_SERVER_Handle *server, const char *name)
+start_transport (struct GNUNET_SERVER_Handle *server, 
+                const char *name)
 {
   struct TransportPlugin *plug;
   char *libname;
@@ -3025,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;
@@ -3032,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))
@@ -3068,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.
@@ -3113,6 +5119,12 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   while (NULL != (plug = plugins))
     {
       plugins = plug->next;
+      if (plug->address_update_task != GNUNET_SCHEDULER_NO_TASK)
+       {
+         GNUNET_SCHEDULER_cancel (plug->env.sched, 
+                                  plug->address_update_task);
+         plug->address_update_task = GNUNET_SCHEDULER_NO_TASK;
+       }
       GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api));
       GNUNET_free (plug->lib_name);
       GNUNET_free (plug->short_name);
@@ -3127,19 +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);
+  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);
 }
 
 
@@ -3148,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;
@@ -3165,6 +5215,7 @@ run (void *cls,
 
   sched = s;
   cfg = c;
+  stats = GNUNET_STATISTICS_create (sched, "transport", cfg);
   validation_map = GNUNET_CONTAINER_multihashmap_create (64);
   /* parse configuration */
   if ((GNUNET_OK !=
@@ -3181,9 +5232,32 @@ run (void *cls,
                   _
                   ("Transport service is lacking key configuration settings.  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;
       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)
@@ -3192,13 +5266,19 @@ run (void *cls,
                   _
                   ("Transport service could not access hostkey.  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;
       return;
     }
   GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key);
   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... */
@@ -3227,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);
 }
@@ -3242,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,