(no commit message)
[oweals/gnunet.git] / src / transport / gnunet-service-transport.c
index 4c94e293965fa0fb30c006435766bdb4721cc1e0..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,32 +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;
+  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
 {
@@ -211,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;
 
 };
 
@@ -274,21 +329,24 @@ 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 NeighborList;
+struct NeighbourList;
 
 /**
- * For each neighbor we keep a list of messages
- * that we still want to transmit to the neighbor.
+ * For each neighbour we keep a list of messages
+ * that we still want to transmit to the neighbour.
  */
 struct MessageQueue
 {
@@ -327,9 +385,9 @@ struct MessageQueue
   struct ForeignAddressList *specific_address;
 
   /**
-   * Peer ID of the Neighbor this entry belongs to.
+   * Peer ID of the Neighbour this entry belongs to.
    */
-  struct GNUNET_PeerIdentity neighbor_id;
+  struct GNUNET_PeerIdentity neighbour_id;
 
   /**
    * Plugin that we used for the transmission.
@@ -337,6 +395,11 @@ struct MessageQueue
    */
   struct TransportPlugin *plugin;
 
+  /**
+   * At what time should we fail?
+   */
+  struct GNUNET_TIME_Absolute timeout;
+
   /**
    * Internal message of the transport system that should not be
    * included in the usual SEND-SEND_OK transmission confirmation
@@ -355,7 +418,7 @@ struct MessageQueue
 
 
 /**
- * For a given Neighbor, which plugins are available
+ * For a given Neighbour, which plugins are available
  * to talk to this peer and what are their costs?
  */
 struct ReadyList
@@ -377,19 +440,24 @@ struct ReadyList
    */
   struct ForeignAddressList *addresses;
 
+  /**
+   * To which neighbour does this ready list belong to?
+   */
+  struct NeighbourList *neighbour;
+
 };
 
 
 /**
- * Entry in linked list of all of our current neighbors.
+ * Entry in linked list of all of our current neighbours.
  */
-struct NeighborList
+struct NeighbourList
 {
 
   /**
    * This is a linked list.
    */
-  struct NeighborList *next;
+  struct NeighbourList *next;
 
   /**
    * Which of our transports is connected to this peer
@@ -410,7 +478,26 @@ struct NeighborList
   struct MessageQueue *messages_tail;
 
   /**
-   * Identity of this neighbor.
+   * 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.
    */
   struct GNUNET_PeerIdentity id;
 
@@ -420,6 +507,14 @@ struct NeighborList
    */
   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
 
+  /**
+   * ID of task scheduled to run when we should retry transmitting
+   * 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;
+
   /**
    * How long until we should consider this peer dead
    * (if we don't receive another message in the
@@ -428,16 +523,16 @@ struct NeighborList
   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
    * this particular peer.  This latency may have been calculated
    * over multiple transports.  This value reflects how long it took
    * us to receive a response when SENDING via this particular
-   * transport/neighbor/address combination!
+   * transport/neighbour/address combination!
    *
    * FIXME: we need to periodically send PINGs to update this
    * latency (at least more often than the current "huge" (11h?)
@@ -445,17 +540,6 @@ struct NeighborList
    */
   struct GNUNET_TIME_Relative latency;
 
-  /**
-   * How many bytes have we received since the "last_quota_update"
-   * timestamp?
-   */
-  uint64_t last_received;
-
-  /**
-   * Global quota for inbound traffic for the neighbor in bytes/ms.
-   */
-  uint32_t quota_in;
-
   /**
    * How often has the other peer (recently) violated the
    * inbound traffic limit?  Incremented by 10 per violation,
@@ -465,19 +549,28 @@ struct NeighborList
   unsigned int quota_violation_count;
 
   /**
-   * Have we seen an ACK from this neighbor in the past?
-   * (used to make up a fake ACK for clients connecting after
-   * the neighbor connected to us).
+   * 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
 {
@@ -488,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;
 
@@ -503,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
 {
@@ -521,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.
@@ -531,38 +623,50 @@ 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;
 
 };
 
+
 /**
- * Linked list of messages to be transmitted to
- * the client.  Each entry is followed by the
- * actual message.
+ * Linked list of messages to be transmitted to the client.  Each
+ * entry is followed by the actual message.
  */
 struct ClientMessageQueueEntry
 {
   /**
-   * This is a linked list.
+   * This is a doubly-linked list.
    */
   struct ClientMessageQueueEntry *next;
+
+  /**
+   * This is a doubly-linked list.
+   */
+  struct ClientMessageQueueEntry *prev;
 };
 
 
@@ -594,6 +698,11 @@ struct TransportClient
    */
   struct ClientMessageQueueEntry *message_queue_tail;
 
+  /**
+   * Current transmit request handle.
+   */ 
+  struct GNUNET_CONNECTION_TransmitHandle *th;
+
   /**
    * Is a call to "transmit_send_continuation" pending?  If so, we
    * must not free this struct (even if the corresponding client
@@ -610,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!
@@ -644,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;
+
 };
 
 
@@ -689,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 neighbor has
- * already been sent the latest version of our HELLO message.
- */
-static unsigned int our_hello_version;
-
 /**
  * Our public key.
  */
@@ -739,17 +865,17 @@ 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 neighbors and their HELLOs.
+ * All known neighbours and their HELLOs.
  */
-static struct NeighborList *neighbors;
+static struct NeighbourList *neighbours;
 
 /**
- * Number of neighbors we'd like to have.
+ * Number of neighbours we'd like to have.
  */
 static uint32_t max_connect_per_transport;
 
@@ -763,51 +889,50 @@ 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 neighbor has timed-out or a plugin
+ * The peer specified by the given neighbour has timed-out or a plugin
  * has disconnected.  We may either need to do nothing (other plugins
  * still up), or trigger a full disconnect and clean up.  This
  * function updates our state and do the necessary notifications.
- * Also notifies our clients that the neighbor is now officially
+ * Also notifies our clients that the neighbour is now officially
  * gone.
  *
- * @param n the neighbor list entry for the peer
+ * @param n the neighbour list entry for the peer
  * @param check should we just check if all plugins
  *        disconnected or must we ask all plugins to
  *        disconnect?
  */
-static void disconnect_neighbor (struct NeighborList *n, int check);
+static void disconnect_neighbour (struct NeighbourList *n, int check);
 
 /**
- * Check the ready list for the given neighbor and if a plugin is
+ * Check the ready list for the given neighbour and if a plugin is
  * ready for transmission (and if we have a message), do so!
  *
- * @param neighbor target peer for which to transmit
+ * @param neighbour target peer for which to transmit
  */
-static void try_transmission_to_peer (struct NeighborList *neighbor);
+static void try_transmission_to_peer (struct NeighbourList *neighbour);
 
 
 /**
- * Find an entry in the neighbor 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.
- *
+ * Find an entry in the neighbour list for a particular peer.
+ *  
  * @return NULL if not found.
  */
-static struct NeighborList *
-find_neighbor (const struct GNUNET_PeerIdentity *key)
+static struct NeighbourList *
+find_neighbour (const struct GNUNET_PeerIdentity *key)
 {
-  struct NeighborList *head = neighbors;
+  struct NeighbourList *head = neighbours;
 
   while ((head != NULL) &&
         (0 != memcmp (key, &head->id, sizeof (struct GNUNET_PeerIdentity))))
@@ -830,47 +955,215 @@ find_transport (const char *short_name)
   return head;
 }
 
-
 /**
- * Update the quota values for the given neighbor 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 NeighborList *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;
 
-  if (n->last_received < allowed)
+/**
+ * Read the blacklist file, containing transport:peer entries.
+ * Provided the transport is loaded, set up hashmap with these
+ * entries to blacklist peers by transport.
+ *
+ */
+static void
+read_blacklist_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  char *fn;
+  char *data;
+  size_t pos;
+  size_t colon_pos;
+  int tsize;
+  struct GNUNET_PeerIdentity pid;
+  struct stat frstat;
+  struct GNUNET_CRYPTO_HashAsciiEncoded enc;
+  unsigned int entries_found;
+  char *transport_name;
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                               "TRANSPORT",
+                                               "BLACKLIST_FILE",
+                                               &fn))
     {
-      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]))
         {
-          /* more than twice the allowed rate! */
-          n->quota_violation_count += 10;
+          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                      _("Syntax error in blacklist file at offset %llu, skipping bytes.\n"),
+                      (unsigned long long) pos);
+          pos++;
+          while ((pos < frstat.st_size) && (!isspace ( (unsigned char) data[pos])))
+            pos++;
+          GNUNET_free_non_null(transport_name);
+          continue;
         }
+      enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
+      if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &pid.hashPubKey))
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                      _("Syntax error in blacklist file at offset %llu, skipping bytes `%s'.\n"),
+                      (unsigned long long) pos,
+                      &enc);
+        }
+      else
+        {
+          if (0 != memcmp (&pid,
+                           &my_identity,
+                           sizeof (struct GNUNET_PeerIdentity)))
+            {
+              entries_found++;
+              add_peer_to_blacklist (&pid,
+                                     transport_name);
+            }
+          else
+            {
+              GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                          _("Found myself `%s' in blacklist (useless, ignored)\n"),
+                          GNUNET_i2s (&pid));
+            }
+        }
+      pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
+      GNUNET_free_non_null(transport_name);
+      while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
+        pos++;
     }
+  GNUNET_free (data);
+  GNUNET_free (fn);
 }
 
 
@@ -892,9 +1185,9 @@ transmit_to_client_callback (void *cls, size_t size, void *buf)
   uint16_t msize;
   size_t tsize;
   const struct GNUNET_MessageHeader *msg;
-  struct GNUNET_CONNECTION_TransmitHandle *th;
   char *cbuf;
 
+  client->th = NULL;
   if (buf == NULL)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -902,10 +1195,15 @@ 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))
         {
-          client->message_queue_head = q->next;
+         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);
           GNUNET_free (q);
         }
-      client->message_queue_tail = NULL;
       client->message_count = 0;
       return 0;
     }
@@ -922,9 +1220,9 @@ transmit_to_client_callback (void *cls, size_t size, void *buf)
                   "Transmitting message of type %u to client.\n",
                   ntohs (msg->type));
 #endif
-      client->message_queue_head = q->next;
-      if (q->next == NULL)
-        client->message_queue_tail = NULL;
+      GNUNET_CONTAINER_DLL_remove (client->message_queue_head,
+                                  client->message_queue_tail,
+                                  q);
       memcpy (&cbuf[tsize], msg, msize);
       tsize += msize;
       GNUNET_free (q);
@@ -933,17 +1231,93 @@ transmit_to_client_callback (void *cls, size_t size, void *buf)
   if (NULL != q)
     {
       GNUNET_assert (msize >= sizeof (struct GNUNET_MessageHeader));
-      th = GNUNET_SERVER_notify_transmit_ready (client->client,
-                                                msize,
-                                                GNUNET_TIME_UNIT_FOREVER_REL,
-                                                &transmit_to_client_callback,
-                                                client);
-      GNUNET_assert (th != NULL);
+      client->th = GNUNET_SERVER_notify_transmit_ready (client->client,
+                                                       msize,
+                                                       GNUNET_TIME_UNIT_FOREVER_REL,
+                                                       &transmit_to_client_callback,
+                                                       client);
+      GNUNET_assert (client->th != NULL);
     }
   return tsize;
 }
 
 
+/**
+ * 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
@@ -960,45 +1334,67 @@ transmit_to_client (struct TransportClient *client,
 {
   struct ClientMessageQueueEntry *q;
   uint16_t msize;
-  struct GNUNET_CONNECTION_TransmitHandle *th;
 
   if ((client->message_count >= MAX_PENDING) && (GNUNET_YES == may_drop))
     {
       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;
     }
-  client->message_count++;
   msize = ntohs (msg->size);
   GNUNET_assert (msize >= sizeof (struct GNUNET_MessageHeader));
   q = GNUNET_malloc (sizeof (struct ClientMessageQueueEntry) + msize);
   memcpy (&q[1], msg, msize);
-  /* append to message queue */
-  if (client->message_queue_tail == NULL)
-    {
-      client->message_queue_tail = q;
-    }
-  else
-    {
-      client->message_queue_tail->next = q;
-      client->message_queue_tail = q;
-    }
-  if (client->message_queue_head == NULL)
+  GNUNET_CONTAINER_DLL_insert_after (client->message_queue_head,
+                                    client->message_queue_tail,
+                                    client->message_queue_tail,
+                                    q);                                     
+  client->message_count++;
+  if (client->th == NULL)
     {
-      client->message_queue_head = q;
-      th = GNUNET_SERVER_notify_transmit_ready (client->client,
-                                                msize,
-                                                GNUNET_TIME_UNIT_FOREVER_REL,
-                                                &transmit_to_client_callback,
-                                                client);
-      GNUNET_assert (th != NULL);
+      client->th = GNUNET_SERVER_notify_transmit_ready (client->client,
+                                                       msize,
+                                                       GNUNET_TIME_UNIT_FOREVER_REL,
+                                                       &transmit_to_client_callback,
+                                                       client);
+      GNUNET_assert (client->th != NULL);
     }
 }
 
 
+/**
+ * Transmit a 'SEND_OK' notification to the given client for the
+ * given neighbour.
+ *
+ * @param client who to notify
+ * @param n neighbour to notify about
+ * @param result status code for the transmission request
+ */
+static void
+transmit_send_ok (struct TransportClient *client,
+                 struct NeighbourList *n,
+                 int result)
+{
+  struct SendOkMessage send_ok_msg;
+
+  send_ok_msg.header.size = htons (sizeof (send_ok_msg));
+  send_ok_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK);
+  send_ok_msg.success = htonl (result);
+  send_ok_msg.latency = GNUNET_TIME_relative_hton (n->latency);
+  send_ok_msg.peer = n->id;
+  transmit_to_client (client, &send_ok_msg.header, GNUNET_NO); 
+}
+
+
 /**
  * Function called by the GNUNET_TRANSPORT_TransmitFunction
  * upon "completion" of a send request.  This tells the API
@@ -1019,76 +1415,79 @@ transmit_send_continuation (void *cls,
                             int result)
 {
   struct MessageQueue *mq = cls;
-  /*struct ReadyList *rl;*/ /* We no longer use the ReadyList for anything here, safe to remove? */
-  struct SendOkMessage send_ok_msg;
-  struct NeighborList *n;
-
-  GNUNET_assert (mq != NULL);
-  n = find_neighbor(&mq->neighbor_id);
-  if (n == NULL) /* Neighbor must have been removed asynchronously! */
-    return;
-
-  /* Otherwise, let's make sure we've got the right peer */
-  GNUNET_assert (0 ==
-                 memcmp (&n->id, target,
-                         sizeof (struct GNUNET_PeerIdentity)));
-
+  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)
     {
-      if (mq->specific_address != NULL)
+      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)
+    {
+      if (result == GNUNET_OK)    
        {
          mq->specific_address->timeout =
            GNUNET_TIME_relative_to_absolute
            (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
-         mq->specific_address->connected = GNUNET_YES;
-       }
-    }
-  else
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Transmission to peer `%s' failed, marking connection as down.\n",
-                  GNUNET_i2s (target));
-      if (mq->specific_address != NULL)
-       mq->specific_address->connected = GNUNET_NO;
+         if (mq->specific_address->validated == GNUNET_YES)
+           mark_address_connected (mq->specific_address);
+       }    
+      else
+       {
+         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;
     }
-  if ( (! mq->internal_msg) &&
-       (mq->specific_address != NULL) )
-    mq->specific_address->in_transmit = GNUNET_NO;
-
   if (mq->client != NULL)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Notifying client %p about transmission to peer `%4s'.\n",
-                  mq->client, GNUNET_i2s (target));
-      send_ok_msg.header.size = htons (sizeof (send_ok_msg));
-      send_ok_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK);
-      send_ok_msg.success = htonl (result);
-      send_ok_msg.peer = n->id;
-      transmit_to_client (mq->client, &send_ok_msg.header, GNUNET_NO);
-    }
+    transmit_send_ok (mq->client, n, result);
   GNUNET_free (mq);
-  /* one plugin just became ready again, try transmitting
-     another message (if available) */
-  if (result == GNUNET_OK)
-    try_transmission_to_peer (n);
-  else
-    disconnect_neighbor (n, GNUNET_YES);
+  try_transmission_to_peer (n);
 }
 
 
 /**
  * Find an address in any of the available transports for
- * the given neighbor that would be good for message
+ * the given neighbour that would be good for message
  * transmission.  This is essentially the transport selection
  * routine.
  *
- * @param neighbor for whom to select an address
+ * @param neighbour for whom to select an address
  * @return selected address, NULL if we have none
  */
 struct ForeignAddressList *
-find_ready_address(struct NeighborList *neighbor)
+find_ready_address(struct NeighbourList *neighbour)
 {
-  struct ReadyList *head = neighbor->plugins;
+  struct ReadyList *head = neighbour->plugins;
   struct ForeignAddressList *addresses;
   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
   struct ForeignAddressList *best_address;
@@ -1105,8 +1504,12 @@ find_ready_address(struct NeighborList *neighbor)
 #if DEBUG_TRANSPORT
               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                           "Marking long-time inactive connection to `%4s' as down.\n",
-                          GNUNET_i2s (&neighbor->id));
+                          GNUNET_i2s (&neighbour->id));
 #endif
+             GNUNET_STATISTICS_update (stats,
+                                       gettext_noop ("# connected addresses"),
+                                       -1,
+                                       GNUNET_NO);
               addresses->connected = GNUNET_NO;
             }
           addresses = addresses->next;
@@ -1115,6 +1518,21 @@ find_ready_address(struct NeighborList *neighbor)
       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) ) &&
@@ -1128,55 +1546,141 @@ find_ready_address(struct NeighborList *neighbor)
         }
       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;
 
 }
 
 
 /**
- * Check the ready list for the given neighbor and if a plugin is
+ * We should re-try transmitting to the given peer,
+ * hopefully we've learned something in the meantime.
+ */
+static void
+retry_transmission_task (void *cls,
+                        const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct NeighbourList *n = cls;
+
+  n->retry_task = GNUNET_SCHEDULER_NO_TASK;
+  try_transmission_to_peer (n);
+}
+
+
+/**
+ * Check the ready list for the given neighbour and if a plugin is
  * ready for transmission (and if we have a message), do so!
  *
- * @param neighbor target peer for which to transmit
+ * @param neighbour target peer for which to transmit
  */
 static void
-try_transmission_to_peer (struct NeighborList *neighbor)
+try_transmission_to_peer (struct NeighbourList *neighbour)
 {
-  struct GNUNET_TIME_Relative min_latency;
   struct ReadyList *rl;
   struct MessageQueue *mq;
+  struct GNUNET_TIME_Relative timeout;
+  ssize_t ret;
+  int force_address;
 
-  if (neighbor->messages_head == NULL)
-    return;                     /* nothing to do */
-  min_latency = GNUNET_TIME_UNIT_FOREVER_REL;
+  if (neighbour->messages_head == NULL)
+    {
+#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 = neighbor->messages_head;
+  mq = neighbour->messages_head;
+  force_address = GNUNET_YES;
   if (mq->specific_address == NULL)
-    mq->specific_address = find_ready_address(neighbor); 
+    {
+      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)
+       {
+#if DEBUG_TRANSPORT
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                     "No destination address available to transmit message of size %u to peer `%4s'\n",
+                     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,
+                                      neighbour->messages_tail,
+                                      mq);
+         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);
+      neighbour->retry_task = GNUNET_SCHEDULER_add_delayed (sched,
+                                                           timeout,
+                                                           &retry_transmission_task,
+                                                           neighbour);
 #if DEBUG_TRANSPORT
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "No destination address available to transmit message of size %u to peer `%4s'\n",
+                 "No validated destination address available to transmit message of size %u to peer `%4s', will wait %llums to find an address.\n",
                  mq->message_buf_size,
-                 GNUNET_i2s (&mq->neighbor_id));
+                 GNUNET_i2s (&mq->neighbour_id),
+                 timeout.value);
 #endif
-      return;                   /* nobody ready */
+      /* FIXME: might want to trigger peerinfo lookup here
+        (unless that's already pending...) */
+      return;    
     }
+  GNUNET_CONTAINER_DLL_remove (neighbour->messages_head,
+                              neighbour->messages_tail,
+                              mq);
   if (mq->specific_address->connected == GNUNET_NO)
     mq->specific_address->connect_attempts++;
   rl = mq->specific_address->ready_list;
-  GNUNET_CONTAINER_DLL_remove (neighbor->messages_head,
-                              neighbor->messages_tail,
-                              mq);
   mq->plugin = rl->plugin;
   if (!mq->internal_msg)
     mq->specific_address->in_transmit = GNUNET_YES;
@@ -1184,21 +1688,41 @@ try_transmission_to_peer (struct NeighborList *neighbor)
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Sending message of size %u for `%4s' to `%s' via plugin `%s'\n",
               mq->message_buf_size,
-              GNUNET_i2s (&neighbor->id), 
-             GNUNET_a2s (mq->specific_address->addr,
-                         mq->specific_address->addrlen),
+              GNUNET_i2s (&neighbour->id), 
+             (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->neighbor_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);
+    }
 }
 
 
@@ -1208,19 +1732,21 @@ try_transmission_to_peer (struct NeighborList *neighbor)
  * @param client source of the transmission request (can be NULL)
  * @param peer_address ForeignAddressList where we should send this message
  * @param priority how important is the message
+ * @param timeout how long do we have to transmit?
  * @param message_buf message(s) to send GNUNET_MessageHeader(s)
  * @param message_buf_size total size of all messages in message_buf
  * @param is_internal is this an internal message; these are pre-pended and
  *                    also do not count for plugins being "ready" to transmit
- * @param neighbor handle to the neighbor for transmission
+ * @param neighbour handle to the neighbour for transmission
  */
 static void
 transmit_to_peer (struct TransportClient *client,
                   struct ForeignAddressList *peer_address,
                   unsigned int priority,
+                 struct GNUNET_TIME_Relative timeout,
                   const char *message_buf,
                   size_t message_buf_size,
-                  int is_internal, struct NeighborList *neighbor)
+                  int is_internal, struct NeighbourList *neighbour)
 {
   struct MessageQueue *mq;
 
@@ -1228,13 +1754,13 @@ transmit_to_peer (struct TransportClient *client,
   if (client != NULL)
     {
       /* check for duplicate submission */
-      mq = neighbor->messages_head;
+      mq = neighbour->messages_head;
       while (NULL != mq)
         {
           if (mq->client == client)
             {
               /* client transmitted to same peer twice
-                 before getting SendOk! */
+                 before getting SEND_OK! */
               GNUNET_break (0);
               return;
             }
@@ -1242,26 +1768,31 @@ 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;
-  memcpy(&mq->neighbor_id, &neighbor->id, sizeof(struct GNUNET_PeerIdentity));
+  memcpy(&mq->neighbour_id, &neighbour->id, sizeof(struct GNUNET_PeerIdentity));
   mq->internal_msg = is_internal;
   mq->priority = priority;
-
+  mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
   if (is_internal)    
-    GNUNET_CONTAINER_DLL_insert (neighbor->messages_head,
-                                neighbor->messages_tail,
+    GNUNET_CONTAINER_DLL_insert (neighbour->messages_head,
+                                neighbour->messages_tail,
                                 mq);
   else
-    GNUNET_CONTAINER_DLL_insert_after (neighbor->messages_head,
-                                      neighbor->messages_tail,
-                                      neighbor->messages_tail,
+    GNUNET_CONTAINER_DLL_insert_after (neighbour->messages_head,
+                                      neighbour->messages_tail,
+                                      neighbour->messages_tail,
                                       mq);
-  try_transmission_to_peer (neighbor);
+  try_transmission_to_peer (neighbour);
 }
 
 
@@ -1297,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;
@@ -1313,7 +1844,7 @@ refresh_hello ()
 {
   struct GNUNET_HELLO_Message *hello;
   struct TransportClient *cpos;
-  struct NeighborList *npos;
+  struct NeighbourList *npos;
   struct GeneratorContext gc;
 
   gc.plug_pos = plugins;
@@ -1324,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)
     {
@@ -1335,17 +1870,21 @@ refresh_hello ()
 
   GNUNET_free_non_null (our_hello);
   our_hello = hello;
-  our_hello_version++;
-  GNUNET_PEERINFO_add_peer (cfg, sched, &my_identity, our_hello);
-  npos = neighbors;
+  GNUNET_PEERINFO_add_peer (peerinfo, our_hello);
+  npos = neighbours;
   while (npos != NULL)
     {
 #if DEBUG_TRANSPORT
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
-                  "Transmitting updated `%s' to neighbor `%4s'\n",
+                  "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, 
                        GNUNET_HELLO_size(our_hello),
                         GNUNET_NO, npos);
@@ -1377,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;
@@ -1390,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)
@@ -1402,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
@@ -1416,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);
 }
 
 
@@ -1436,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);  
 }
 
 
@@ -1457,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)
     {
@@ -1480,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;
@@ -1495,14 +2139,24 @@ plugin_env_notify_address (void *cls,
  */
 static void
 notify_clients_connect (const struct GNUNET_PeerIdentity *peer,
-                        struct GNUNET_TIME_Relative latency)
+                        struct GNUNET_TIME_Relative latency,
+                       uint32_t distance)
 {
   struct ConnectInfoMessage cim;
   struct TransportClient *cpos;
 
+#if DEBUG_TRANSPORT
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "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.quota_out = htonl (GNUNET_CONSTANTS_DEFAULT_BPM_IN_OUT / (60 * 1000));
+  cim.distance = htonl (distance);
   cim.latency = GNUNET_TIME_relative_hton (latency);
   memcpy (&cim.id, peer, sizeof (struct GNUNET_PeerIdentity));
   cpos = clients;
@@ -1523,6 +2177,15 @@ notify_clients_disconnect (const struct GNUNET_PeerIdentity *peer)
   struct DisconnectInfoMessage dim;
   struct TransportClient *cpos;
 
+#if DEBUG_TRANSPORT
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "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);
@@ -1540,22 +2203,26 @@ notify_clients_disconnect (const struct GNUNET_PeerIdentity *peer)
  * Find a ForeignAddressList entry for the given neighbour
  * that matches the given address and transport.
  *
- * @param neighbor which peer we care about
+ * @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
  */
 static struct ForeignAddressList *
-find_peer_address(struct NeighborList *neighbor,
+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 = neighbor->plugins;
+  head = neighbour->plugins;
   while (head != NULL)
     {
       if (0 == strcmp (tname, head->plugin->short_name))
@@ -1564,39 +2231,48 @@ find_peer_address(struct NeighborList *neighbor,
     }
   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;
 }
 
 
 /**
- * Get the peer address struct for the given neighbor and
+ * Get the peer address struct for the given neighbour and
  * address.  If it doesn't yet exist, create it.
  *
- * @param neighbor which peer we care about
+ * @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 NeighborList *neighbor,
-                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 (neighbor, tname, addr, addrlen);
+  ret = find_peer_address (neighbour, tname, session, addr, addrlen);
   if (ret != NULL)
     return ret;
-  head = neighbor->plugins;
+  head = neighbour->plugins;
+
   while (head != NULL)
     {
       if (0 == strcmp (tname, head->plugin->short_name))
@@ -1606,12 +2282,21 @@ add_peer_address(struct NeighborList *neighbor,
   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);
   ret->latency = GNUNET_TIME_relative_get_forever();
+  ret->distance = -1;
   ret->timeout = GNUNET_TIME_relative_to_absolute
     (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); 
   ret->ready_list = head;
@@ -1669,119 +2354,1265 @@ add_validated_address (void *cls,
 }
 
 
+
+/**
+ * Closure for 'check_address_exists'.
+ */
+struct CheckAddressExistsClosure
+{
+  /**
+   * Address to check for.
+   */
+  const void *addr;
+
+  /**
+   * Name of the transport.
+   */
+  const char *tname;
+
+  /**
+   * Session, or NULL.
+   */
+  struct Session *session;
+
+  /**
+   * Set to GNUNET_YES if the address exists.
+   */
+  int exists;
+
+  /**
+   * Length of addr.
+   */
+  uint16_t addrlen;
+
+};
+
+
 /**
  * Iterator over hash map entries.  Checks if the given
- * validation entry is for the same challenge as what
- * is given in the PONG.
+ * validation entry is for the same address as what is given
+ * in the closure.
  *
- * @param cls the 'struct TransportPongMessage*'
- * @param key peer identity 
+ * @param cls the 'struct CheckAddressExistsClosure*'
+ * @param key current key code (ignored)
  * @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)
+check_address_exists (void *cls,
+                     const GNUNET_HashCode * key,
+                     void *value)
 {
-  const struct TransportPongMessage *pong = cls;
+  struct CheckAddressExistsClosure *caec = cls;
   struct ValidationEntry *ve = value;
-  struct AddValidatedAddressContext avac;
-  unsigned int challenge = ntohl(pong->challenge);
-  struct GNUNET_HELLO_Message *hello;
-  struct GNUNET_PeerIdentity target;
-  struct NeighborList *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_neighbor (&target);
-  if (n != NULL)
+  if ( (0 == strcmp (caec->tname,
+                    ve->transport_name)) &&
+       (caec->addrlen == ve->addrlen) &&
+       (0 == memcmp (caec->addr,
+                    ve->addr,
+                    caec->addrlen)) )
     {
-      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;
+      caec->exists = GNUNET_YES;
+      return GNUNET_NO;
     }
-
-  /* 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;
+  if ( (ve->session != NULL) &&
+       (caec->session == ve->session) )
+    {
+      caec->exists = GNUNET_YES;
+      return GNUNET_NO;
+    }
+  return GNUNET_YES;
 }
 
 
+
 /**
- * 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).
+ * Iterator to free entries in the validation_map.
  *
- * @param cls closure
- * @param name name of the transport that generated the address
- * @param peer who responded to our challenge
- * @param challenge the challenge number we presumably used
- * @param sender_addr string describing our sender address (as observed
- *         by the other peer in human-readable format)
+ * @param cls closure (unused)
+ * @param key current key code
+ * @param value value in the hash map (validation to abort)
+ * @return GNUNET_YES (always)
  */
-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)
+static int 
+abort_validation (void *cls,
+                 const GNUNET_HashCode * key,
+                 void *value)
 {
-#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))
+  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)
     {
-      /* This is *expected* to happen a lot since we send
-        PONGs to *all* known addresses of the sender of
+      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
+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
@@ -1795,163 +3626,102 @@ handle_pong (void *cls, const struct GNUNET_MessageHeader *message,
 #endif
       return;
     }
-#if 0
-  /* FIXME: add given address to potential pool of our addresses
-     (for voting) */
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
-             _("Another peer saw us using the address `%s' via `%s'.\n"),
-             GNUNET_a2s ((const struct sockaddr *) &pong[1],
-                         ntohs(pong->addrlen)), 
-             va->transport_name);  
-#endif
-}
-
-
-static void
-neighbor_timeout_task (void *cls,
-                        const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
-  struct NeighborList *n = cls;
 
-#if DEBUG_TRANSPORT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
-              "Neighbor `%4s' has timed out!\n", GNUNET_i2s (&n->id));
-#endif
-  n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
-  disconnect_neighbor (n, GNUNET_NO);
 }
 
 
 /**
- * Create a fresh entry in our neighbor list for the given peer.
- * Will try to transmit our current HELLO to the new neighbor.  Also
- * notifies our clients about the new "connection".
+ * Try to validate a neighbour's address by sending him our HELLO and a PING.
  *
- * @param peer the peer for which we create the entry
- * @return the new neighbor list entry
+ * @param cls the 'struct ValidationEntry*'
+ * @param neighbour neighbour to validate, NULL if validation failed
  */
-static struct NeighborList *
-setup_new_neighbor (const struct GNUNET_PeerIdentity *peer)
+static void
+transmit_hello_and_ping (void *cls,
+                        struct NeighbourList *neighbour)
 {
-  struct NeighborList *n;
-  struct TransportPlugin *tp;
-  struct ReadyList *rl;
+  struct ValidationEntry *va = cls;
+  struct ForeignAddressList *peer_address;
+  struct TransportPingMessage ping;
+  uint16_t hello_size;
+  size_t tsize;
+  char * message_buf;
+  struct GNUNET_PeerIdentity id;
+  size_t slen;
 
-  GNUNET_assert (our_hello != NULL);
-  n = GNUNET_malloc (sizeof (struct NeighborList));
-  n->next = neighbors;
-  neighbors = 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)
+  GNUNET_CRYPTO_hash (&va->publicKey,
+                     sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+                     &id.hashPubKey);
+  if (neighbour == 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;
+      /* FIXME: stats... */
+      GNUNET_break (GNUNET_OK ==
+                   GNUNET_CONTAINER_multihashmap_remove (validation_map,
+                                                         &id.hashPubKey,
+                                                         va));
+      abort_validation (NULL, NULL, va);
+      return;
     }
-  n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
-  n->timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
-                                                  GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
-                                                  &neighbor_timeout_task, n);
-  transmit_to_peer (NULL, NULL, 0,
-                    (const char *) our_hello, GNUNET_HELLO_size(our_hello),
-                    GNUNET_NO, n);
-  notify_clients_connect (peer, GNUNET_TIME_UNIT_FOREVER_REL);
-  return n;
-}
-
-
-/**
- * Closure for 'check_address_exists'.
- */
-struct CheckAddressExistsClosure
-{
-  /**
-   * Address to check for.
-   */
-  const void *addr;
-
-  /**
-   * Name of the transport.
-   */
-  const char *tname;
-
-  /**
-   * Length of addr.
-   */
-  size_t addrlen;
-
-  /**
-   * Set to GNUNET_YES if the address exists.
-   */
-  int exists;
-};
-
-
-/**
- * Iterator over hash map entries.  Checks if the given
- * validation entry is for the same address as what is given
- * in the closure.
- *
- * @param cls the 'struct CheckAddressExistsClosure*'
- * @param key current key code (ignored)
- * @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_address_exists (void *cls,
-                     const GNUNET_HashCode * key,
-                     void *value)
-{
-  struct CheckAddressExistsClosure *caec = cls;
-  struct ValidationEntry *ve = value;
-  if ( (0 == strcmp (caec->tname,
-                    ve->transport_name)) &&
-       (caec->addrlen == ve->addrlen) &&
-       (0 == memcmp (caec->addr,
-                    ve->addr,
-                    caec->addrlen)) )
+  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)
     {
-      caec->exists = GNUNET_YES;
-      return GNUNET_NO;
+      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;
     }
-  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;
+  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_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);
+  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);
 }
 
 
@@ -1971,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 NeighborList *neighbor;
-  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)
     {
@@ -1994,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,
@@ -2017,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);
@@ -2036,82 +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);
-  neighbor = find_neighbor(&id);  
-  if (neighbor == NULL)
-    neighbor = setup_new_neighbor(&id);
-  peer_address = add_peer_address(neighbor, 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,
-                   message_buf, tsize, 
-                   GNUNET_YES, neighbor);
-  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 NeighborList' 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 NeighborList *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.\n",
-                 GNUNET_a2s (addr, addrlen),
-                 tname,
-                 GNUNET_i2s (&n->id));
-#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;
 }
 
@@ -2122,27 +3867,22 @@ add_to_foreign_address_list (void *cls,
  *
  * @param cls closure
  * @param peer id of the peer, NULL for last call
- * @param hello hello message for the peer (can be NULL)
- * @param trust amount of trust we have in the peer (not used)
+ * @param h hello message for the peer (can be NULL)
  */
 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;
   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk;
   struct GNUNET_PeerIdentity target;
-  struct NeighborList *n;
+  struct NeighbourList *n;
 
   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
@@ -2154,31 +3894,69 @@ 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_neighbor (peer);
+  n = find_neighbour (peer);
   if (n != NULL)
-    GNUNET_HELLO_iterate_addresses (h,
-                                   GNUNET_NO,
-                                   &add_to_foreign_address_list,
-                                   n);
+    {
+      GNUNET_HELLO_iterate_addresses (h,
+                                     GNUNET_NO,
+                                     &add_to_foreign_address_list,
+                                     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),
@@ -2186,6 +3964,7 @@ check_hello_validated (void *cls,
                                      chvc);
 }
 
+
 /**
  * Process HELLO-message.
  *
@@ -2202,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)))
@@ -2210,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))
     {
-      GNUNET_break_op (0);
-      return GNUNET_SYSERR;
+#if DEBUG_TRANSPORT_HELLO
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Unable to get public key from `%s' for `%4s'!\n",
+                  "HELLO",
+                  GNUNET_i2s (&target));
+#endif
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+
+  GNUNET_CRYPTO_hash (&publicKey,
+                      sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+                      &target.hashPubKey);
+
+  if (0 == memcmp (&my_identity,
+                  &target,
+                  sizeof (struct GNUNET_PeerIdentity)))
+    {
+      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);
     }
-  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
-
   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,
@@ -2242,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;
@@ -2253,41 +4091,28 @@ process_hello (struct TransportPlugin *plugin,
 
 
 /**
- * The peer specified by the given neighbor has timed-out or a plugin
+ * The peer specified by the given neighbour has timed-out or a plugin
  * has disconnected.  We may either need to do nothing (other plugins
  * still up), or trigger a full disconnect and clean up.  This
  * function updates our state and does the necessary notifications.
- * Also notifies our clients that the neighbor is now officially
+ * Also notifies our clients that the neighbour is now officially
  * gone.
  *
- * @param n the neighbor list entry for the peer
+ * @param n the neighbour list entry for the peer
  * @param check should we just check if all plugins
  *        disconnected or must we ask all plugins to
  *        disconnect?
  */
 static void
-disconnect_neighbor (struct NeighborList *current_handle, int check)
+disconnect_neighbour (struct NeighbourList *n, int check)
 {
   struct ReadyList *rpos;
-  struct NeighborList *npos;
-  struct NeighborList *nprev;
-  struct NeighborList *n;
+  struct NeighbourList *npos;
+  struct NeighbourList *nprev;
   struct MessageQueue *mq;
   struct ForeignAddressList *peer_addresses;
   struct ForeignAddressList *peer_pos;
 
-  if (neighbors == NULL)
-    return; /* We don't have any neighbors, so client has an already removed handle! */
-
-  npos = neighbors;
-  while ((npos != NULL) && (current_handle != npos))
-    npos = npos->next;
-
-  if (npos == NULL)
-    return; /* Couldn't find neighbor in existing list, must have been already removed! */
-  else
-    n = npos;
-
   if (GNUNET_YES == check)
     {
       rpos = n->plugins;
@@ -2303,14 +4128,14 @@ disconnect_neighbor (struct NeighborList *current_handle, int check)
           rpos = rpos->next;
         }
     }
-
 #if DEBUG_TRANSPORT
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
-              "Disconnecting from `%4s'\n", GNUNET_i2s (&n->id));
+              "Disconnecting from `%4s'\n",
+             GNUNET_i2s (&n->id));
 #endif
-  /* remove n from neighbors list */
+  /* remove n from neighbours list */
   nprev = NULL;
-  npos = neighbors;
+  npos = neighbours;
   while ((npos != NULL) && (npos != n))
     {
       nprev = npos;
@@ -2318,23 +4143,39 @@ disconnect_neighbor (struct NeighborList *current_handle, int check)
     }
   GNUNET_assert (npos != NULL);
   if (nprev == NULL)
-    neighbors = n->next;
+    neighbours = n->next;
   else
     nprev->next = n->next;
 
   /* notify all clients about disconnect */
-  notify_clients_disconnect (&n->id);
+  if (GNUNET_YES == n->received_pong)
+    notify_clients_disconnect (&n->id);
 
   /* clean up all plugins, cancel connections and pending transmissions */
   while (NULL != (rpos = n->plugins))
     {
       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);
@@ -2343,17 +4184,43 @@ disconnect_neighbor (struct NeighborList *current_handle, 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);
-      GNUNET_assert (0 == memcmp(&mq->neighbor_id, 
+      GNUNET_assert (0 == memcmp(&mq->neighbour_id, 
                                 &n->id,
                                 sizeof(struct GNUNET_PeerIdentity)));
       GNUNET_free (mq);
     }
   if (n->timeout_task != GNUNET_SCHEDULER_NO_TASK)
-    GNUNET_SCHEDULER_cancel (sched, n->timeout_task);
+    {
+      GNUNET_SCHEDULER_cancel (sched, n->timeout_task);
+      n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+    }
+  if (n->retry_task != GNUNET_SCHEDULER_NO_TASK)
+    {
+      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);
 }
 
@@ -2361,31 +4228,32 @@ disconnect_neighbor (struct NeighborList *current_handle, 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 NeighborList *n;
+  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,
@@ -2396,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));    
 
-  n = find_neighbor(peer);
-  if (n == NULL)
-    n = setup_new_neighbor(peer);
-  /* broadcast 'PONG' to all available addresses */
+
+    }
+  else
+    {
+      /* peer wants to confirm that this is one of our addresses */
+      addr += slen;
+      alen -= slen;
+      if (GNUNET_OK !=
+         plugin->api->check_address (plugin->api->cls,
+                                     addr,
+                                     alen))
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                     _("Not confirming PING with address `%s' since I cannot confirm having this address.\n"),
+                     a2s (plugin->short_name,
+                          addr,
+                          alen));
+         return GNUNET_NO;
+       }
+      oal = plugin->addresses;
+      while (NULL != oal)
+       {
+         if ( (oal->addrlen == alen) &&
+              (0 == memcmp (addr,
+                            &oal[1],
+                            alen)) )
+           break;
+         oal = oal->next;
+       }
+      pong = GNUNET_malloc (sizeof (struct TransportPongMessage) + alen + slen);
+      pong->header.size = htons (sizeof (struct TransportPongMessage) + alen + slen);
+      pong->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG);
+      pong->purpose.size =
+       htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
+              sizeof (uint32_t) +
+              sizeof (struct GNUNET_TIME_AbsoluteNBO) +
+              sizeof (struct GNUNET_PeerIdentity) + alen + slen);
+      pong->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN);
+      pong->challenge = ping->challenge;
+      pong->addrlen = htonl(alen + slen);
+      memcpy(&pong->pid, 
+            &my_identity, 
+            sizeof(struct GNUNET_PeerIdentity));
+      memcpy (&pong[1], plugin->short_name, slen);
+      memcpy (&((char*)&pong[1])[slen], addr, alen);
+      if ( (oal != NULL) &&
+          (GNUNET_TIME_absolute_get_remaining (oal->pong_sig_expires).value < PONG_SIGNATURE_LIFETIME.value / 4) )
+       {
+         /* create / update cached sig */
+#if DEBUG_TRANSPORT
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                     "Creating PONG signature to indicate ownership.\n");
+#endif
+         oal->pong_sig_expires = GNUNET_TIME_absolute_min (oal->expires,
+                                                           GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME));
+         pong->expiration = GNUNET_TIME_absolute_hton (oal->pong_sig_expires);
+         GNUNET_assert (GNUNET_OK ==
+                        GNUNET_CRYPTO_rsa_sign (my_private_key,
+                                                &pong->purpose,
+                                                &oal->pong_signature));            
+         memcpy (&pong->signature,
+                 &oal->pong_signature,
+                 sizeof (struct GNUNET_CRYPTO_RsaSignature));    
+       }
+      else if (oal == NULL)
+       {
+         /* not using cache (typically DV-only) */
+         pong->expiration = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME));
+         GNUNET_assert (GNUNET_OK ==
+                        GNUNET_CRYPTO_rsa_sign (my_private_key,
+                                                &pong->purpose,
+                                                &pong->signature));        
+       }
+      else
+       {
+         /* can used cached version */
+         pong->expiration = GNUNET_TIME_absolute_hton (oal->pong_sig_expires);
+         memcpy (&pong->signature,
+                 &oal->pong_signature,
+                 sizeof (struct GNUNET_CRYPTO_RsaSignature));    
+       }
+    }
+  n = find_neighbour(peer);
+  GNUNET_assert (n != NULL);
+  /* first try reliable response transmission */
+  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)
     {
@@ -2434,6 +4461,7 @@ handle_ping(void *cls, const struct GNUNET_MessageHeader *message,
        {
          transmit_to_peer(NULL, fal,
                           TRANSPORT_PONG_PRIORITY, 
+                          HELLO_VERIFICATION_TIMEOUT,
                           (const char *)pong, 
                           ntohs(pong->header.size), 
                           GNUNET_YES, 
@@ -2454,127 +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 NeighborList *n;
+  struct NeighbourList *n;
+  struct GNUNET_TIME_Relative ret;
+
+  if (is_blacklisted (peer, plugin))
+    return GNUNET_TIME_UNIT_FOREVER_REL;
 
-  n = find_neighbor (peer);
+  n = find_neighbour (peer);
   if (n == NULL)
-    {
-      if (message == NULL)
-        return;                 /* disconnect of peer already marked down */
-      n = setup_new_neighbor (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_neighbor (n, GNUNET_YES);
-      return;
-    }
-  peer_address = add_peer_address(n, 
-                                 plugin->short_name,
-                                 sender_address, 
-                                 sender_address_len);
-  if (peer_address != NULL)
-    {
-      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;
-  GNUNET_SCHEDULER_cancel (sched, n->timeout_task);
-  n->peer_timeout =
-    GNUNET_TIME_relative_to_absolute
-    (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
-  n->timeout_task =
-    GNUNET_SCHEDULER_add_delayed (sched,
-                                  GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
-                                  &neighbor_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.
@@ -2590,9 +4627,7 @@ handle_start (void *cls,
 {
   struct TransportClient *c;
   struct ConnectInfoMessage cim;
-  struct NeighborList *n;
-  struct InboundMessage *im;
-  struct GNUNET_MessageHeader *ack;
+  struct NeighbourList *n;
 
 #if DEBUG_TRANSPORT
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -2626,33 +4661,18 @@ handle_start (void *cls,
       /* tell new client about all existing connections */
       cim.header.size = htons (sizeof (struct ConnectInfoMessage));
       cim.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
-      cim.quota_out =
-        htonl (GNUNET_CONSTANTS_DEFAULT_BPM_IN_OUT / (60 * 1000));
-      cim.latency = GNUNET_TIME_relative_hton (GNUNET_TIME_UNIT_ZERO);  /* FIXME? */
-      im = GNUNET_malloc (sizeof (struct InboundMessage) +
-                          sizeof (struct GNUNET_MessageHeader));
-      im->header.size = htons (sizeof (struct InboundMessage) +
-                               sizeof (struct GNUNET_MessageHeader));
-      im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV);
-      im->latency = GNUNET_TIME_relative_hton (GNUNET_TIME_UNIT_ZERO);  /* FIXME? */
-      ack = (struct GNUNET_MessageHeader *) &im[1];
-      ack->size = htons (sizeof (struct GNUNET_MessageHeader));
-      ack->type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_ACK);
-      for (n = neighbors; n != NULL; n = n->next)
-        {
-          cim.id = n->id;
-          transmit_to_client (c, &cim.header, GNUNET_NO);
-          if (n->received_pong)
-            {
-              im->peer = n->id;
-              transmit_to_client (c, &im->header, GNUNET_NO);
+      n = neighbours; 
+      while (n != NULL)
+       {
+         if (GNUNET_YES == n->received_pong)
+           {
+             cim.id = n->id;
+             cim.latency = GNUNET_TIME_relative_hton (n->latency);
+             cim.distance = htonl (n->distance);
+             transmit_to_client (c, &cim.header, GNUNET_NO);
             }
+           n = n->next;
         }
-      GNUNET_free (im);
-    }
-  else
-    {
-      fprintf(stderr, "Our hello is NULL!\n");
     }
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
@@ -2672,15 +4692,73 @@ handle_hello (void *cls,
 {
   int ret;
 
-#if DEBUG_TRANSPORT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Received `%s' request from client\n", "HELLO");
-#endif
+  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.
  *
@@ -2693,10 +4771,9 @@ handle_send (void *cls,
              struct GNUNET_SERVER_Client *client,
              const struct GNUNET_MessageHeader *message)
 {
-  struct TransportClient *tc;
-  struct NeighborList *n;
   const struct OutboundMessage *obm;
   const struct GNUNET_MessageHeader *obmm;
+  struct TransmitClientMessageContext *tcmc;
   uint16_t size;
   uint16_t msize;
 
@@ -2708,36 +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_neighbor (&obm->peer);
-  if (n == NULL)
-    n = setup_new_neighbor (&obm->peer); /* But won't ever add address, we have none! */
-  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), (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);
 }
 
 
@@ -2755,66 +4827,45 @@ handle_set_quota (void *cls,
 {
   const struct QuotaSetMessage *qsm =
     (const struct QuotaSetMessage *) message;
-  struct NeighborList *n;
-  struct TransportPlugin *p;
-  struct ReadyList *rl;
-
-  n = find_neighbor (&qsm->peer);
+  struct NeighbourList *n;
+  
+  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);
 }
 
 
 /**
- * Handle TRY_CONNECT-message.
- *
- * @param cls closure (always NULL)
- * @param client identification of the client
- * @param message the actual message
+ * 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
-handle_try_connect (void *cls,
-                    struct GNUNET_SERVER_Client *client,
-                    const struct GNUNET_MessageHeader *message)
-{
-  const struct TryConnectMessage *tcm;
-  struct NeighborList *neighbor;
-  tcm = (const struct TryConnectMessage *) message;
-#if DEBUG_TRANSPORT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Received `%s' request from client %p asking to connect to `%4s'\n",
-              "TRY_CONNECT", client, GNUNET_i2s (&tcm->peer));
-#endif
-  neighbor = find_neighbor(&tcm->peer);
-  if (neighbor == NULL)
-    setup_new_neighbor (&tcm->peer);
-  GNUNET_SERVER_receive_done (client, GNUNET_OK);
-}
-
-
 static void
 transmit_address_to_client (void *cls, const char *address)
 {
@@ -2825,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)
@@ -2850,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))
@@ -2875,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)
     {
@@ -2889,34 +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_try_connect, NULL,
-   GNUNET_MESSAGE_TYPE_TRANSPORT_TRY_CONNECT,
-   sizeof (struct TryConnectMessage)},
-  {&handle_address_lookup, NULL,
-   GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_LOOKUP,
-   0},
-  {NULL, NULL, 0, 0}
-};
-
 
 /**
  * Setup the environment for this plugin.
@@ -2930,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;
 }
 
 
@@ -2940,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;
@@ -2981,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;
@@ -2988,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))
@@ -2999,10 +5071,12 @@ client_disconnect_notification (void *cls,
     return;
   while (NULL != (mqe = pos->message_queue_head))
     {
-      pos->message_queue_head = mqe->next;
+      GNUNET_CONTAINER_DLL_remove (pos->message_queue_head,
+                                  pos->message_queue_tail,
+                                  mqe);
+      pos->message_count--;
       GNUNET_free (mqe);
     }
-  pos->message_queue_head = NULL;
   if (prev == NULL)
     clients = pos->next;
   else
@@ -3012,32 +5086,16 @@ client_disconnect_notification (void *cls,
       pos->client = NULL;
       return;
     }
+  if (pos->th != NULL)
+    {
+      GNUNET_CONNECTION_notify_transmit_ready_cancel (pos->th);
+      pos->th = NULL;
+    }
+  GNUNET_break (0 == pos->message_count);
   GNUNET_free (pos);
 }
 
 
-/**
- * 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.
@@ -3052,6 +5110,8 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   struct OwnAddressList *al;
   struct CheckHelloValidatedContext *chvc;
 
+  while (neighbours != NULL)
+    disconnect_neighbour (neighbours, GNUNET_NO);
 #if DEBUG_TRANSPORT
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Transport service is unloading plugins...\n");
@@ -3059,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);
@@ -3073,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);
 }
 
 
@@ -3094,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;
@@ -3111,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 !=
@@ -3127,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)
@@ -3138,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... */
@@ -3173,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);
 }
@@ -3188,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,