(no commit message)
[oweals/gnunet.git] / src / transport / gnunet-service-transport.c
index 8af5c6d24b12825b67354a4b2f15fe27c78bf7af..12db541acf802efe8ba075a88deddc0962133af8 100644 (file)
@@ -4,7 +4,7 @@
 
      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!
- * - Already wrong with dv.
  */
 #include "platform.h"
 #include "gnunet_client_lib.h"
 
 #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.
  * Besides, if a single request to an address takes a long time,
  * then the peer is unlikely worthwhile anyway.
  */
-#define HELLO_VERIFICATION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
+#define HELLO_VERIFICATION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
+
+/**
+ * How long is a PONG signature valid?  We'll recycle a signature until
+ * 1/4 of this time is remaining.  PONGs should expire so that if our
+ * external addresses change an adversary cannot replay them indefinitely.
+ * OTOH, we don't want to spend too much time generating PONG signatures,
+ * so they must have some lifetime to reduce our CPU usage.
+ */
+#define PONG_SIGNATURE_LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
 
 /**
  * Priority to use for PONG messages.
@@ -165,11 +175,6 @@ struct ForeignAddressList
    */
   GNUNET_SCHEDULER_TaskIdentifier revalidate_task;
 
-  /**
-   * Length of addr.
-   */
-  size_t addrlen;
-
   /**
    * The address.
    */
@@ -208,6 +213,11 @@ struct ForeignAddressList
    */
   uint32_t distance;
 
+  /**
+   * Length of addr.
+   */
+  uint16_t addrlen;
+
   /**
    * Have we ever estimated the latency of this address?  Used to
    * ensure that the first time we add an address, we immediately
@@ -240,7 +250,8 @@ struct ForeignAddressList
 
 
 /**
- * Entry in linked list of network addresses for ourselves.
+ * Entry in linked list of network addresses for ourselves.  Also
+ * includes a cached signature for 'struct TransportPongMessage's.
  */
 struct OwnAddressList
 {
@@ -250,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;
 
 };
 
@@ -552,7 +568,9 @@ struct NeighbourList
 
 /**
  * Message used to ask a peer to validate receipt (to check an address
- * from a HELLO).  
+ * from a HELLO).  Followed by the address we are trying to validate,
+ * or an empty address if we are just sending a PING to confirm that a
+ * connection which the receiver (of the PING) initiated is still valid.
  */
 struct TransportPingMessage
 {
@@ -563,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;
 
@@ -578,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
 {
@@ -596,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.
@@ -606,24 +623,31 @@ struct TransportPongMessage
   struct GNUNET_CRYPTO_RsaSignature signature;
 
   /**
-   * What are we signing and why?
+   * What are we signing and why?  Two possible reason codes can be here:
+   * GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN to confirm that this is a
+   * plausible address for this peer (pid is set to identity of signer); or
+   * GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_USING to confirm that this is
+   * an address we used to connect to the peer with the given pid.
    */
   struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
 
   /**
-   * Random challenge number (in network byte order).
+   * When does this signature expire?
    */
-  uint32_t challenge GNUNET_PACKED;
+  struct GNUNET_TIME_AbsoluteNBO expiration;
 
   /**
-   * Who signed this message?
+   * Either the identity of the peer Who signed this message, or the
+   * identity of the peer that we're connected to using the given
+   * address (depending on purpose.type).
    */
-  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded signer;
+  struct GNUNET_PeerIdentity pid;
 
   /**
-   * Size of address appended to this message
+   * Size of address appended to this message (part of what is
+   * being signed, hence not redundant). 
    */
-  size_t addrlen;
+  uint32_t addrlen;
 
 };
 
@@ -695,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!
@@ -734,14 +770,14 @@ struct ValidationEntry
   struct Session *session;
 
   /**
-   * Length of addr.
+   * Challenge number we used.
    */
-  size_t addrlen;
+  uint32_t challenge;
 
   /**
-   * Challenge number we used.
+   * Length of addr.
    */
-  uint32_t challenge;
+  uint16_t addrlen;
 
 };
 
@@ -779,6 +815,11 @@ struct CheckHelloValidatedContext
    */
   int hello_known;
 
+  /**
+   * Number of validation entries currently referring to this
+   * CHVC.
+   */
+  unsigned int ve_count;
 };
 
 
@@ -788,12 +829,6 @@ struct CheckHelloValidatedContext
  */
 static struct GNUNET_HELLO_Message *our_hello;
 
-/**
- * "version" of "our_hello".  Used to see if a given neighbour has
- * already been sent the latest version of our HELLO message.
- */
-static unsigned int our_hello_version;
-
 /**
  * Our public key.
  */
@@ -829,11 +864,6 @@ static struct TransportClient *clients;
  */
 static struct TransportPlugin *plugins;
 
-/**
- * Our server.
- */
-static struct GNUNET_SERVER_Handle *server;
-
 /**
  * Handle to peerinfo service.
  */
@@ -870,7 +900,6 @@ static struct GNUNET_CONTAINER_MultiHashMap *validation_map;
  */
 static struct GNUNET_STATISTICS_Handle *stats;
 
-
 /**
  * The peer specified by the given neighbour has timed-out or a plugin
  * has disconnected.  We may either need to do nothing (other plugins
@@ -943,7 +972,7 @@ is_blacklisted (const struct GNUNET_PeerIdentity *peer, struct TransportPlugin *
       if (GNUNET_CONTAINER_multihashmap_contains(plugin->blacklist, &peer->hashPubKey) == GNUNET_YES)
         {
 #if DEBUG_BLACKLIST
-          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                       _("Peer `%s:%s' is blacklisted!\n"),
                       plugin->short_name, GNUNET_i2s (peer));
 #endif
@@ -998,10 +1027,12 @@ read_blacklist_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
                                                "BLACKLIST_FILE",
                                                &fn))
     {
+#if DEBUG_TRANSPORT
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   _("Option `%s' in section `%s' not specified!\n"),
                   "BLACKLIST_FILE",
                   "TRANSPORT");
+#endif
       return;
     }
   if (GNUNET_OK != GNUNET_DISK_file_test (fn))
@@ -1016,14 +1047,17 @@ read_blacklist_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
     }
   if (frstat.st_size == 0)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+#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))
     {
@@ -1035,13 +1069,13 @@ read_blacklist_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
     }
   entries_found = 0;
   pos = 0;
-  while ((pos < frstat.st_size) && isspace (data[pos]))
+  while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
     pos++;
   while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
          (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
     {
       colon_pos = pos;
-      while ((colon_pos < frstat.st_size) && (data[colon_pos] != ':') && !isspace (data[colon_pos]))
+      while ((colon_pos < frstat.st_size) && (data[colon_pos] != ':') && !isspace ( (unsigned char) data[colon_pos]))
         colon_pos++;
 
       if (colon_pos >= frstat.st_size)
@@ -1054,18 +1088,18 @@ read_blacklist_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
           return;
         }
 
-      if (isspace(data[colon_pos]))
+      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 (data[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))
+      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"),
@@ -1075,23 +1109,25 @@ read_blacklist_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
           return;
         }
 
-      transport_name = GNUNET_malloc(tsize);
+      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 (enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
+      if (!isspace ( (unsigned char) enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
         {
           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                       _("Syntax error in blacklist file at offset %llu, skipping bytes.\n"),
                       (unsigned long long) pos);
           pos++;
-          while ((pos < frstat.st_size) && (!isspace (data[pos])))
+          while ((pos < frstat.st_size) && (!isspace ( (unsigned char) data[pos])))
             pos++;
           GNUNET_free_non_null(transport_name);
           continue;
@@ -1112,10 +1148,7 @@ read_blacklist_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
             {
               entries_found++;
               add_peer_to_blacklist (&pid,
-                              transport_name);
-              GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                          _("Found blacklisted peer `%s:%s' in configuration\n"),
-                          transport_name, GNUNET_i2s (&pid));
+                                     transport_name);
             }
           else
             {
@@ -1126,7 +1159,7 @@ read_blacklist_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
         }
       pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
       GNUNET_free_non_null(transport_name);
-      while ((pos < frstat.st_size) && isspace (data[pos]))
+      while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
         pos++;
     }
   GNUNET_free (data);
@@ -1209,6 +1242,32 @@ transmit_to_client_callback (void *cls, size_t size, void *buf)
 }
 
 
+/**
+ * Convert an address to a string.
+ *
+ * @param plugin name of the plugin responsible for the address
+ * @param addr binary address
+ * @param addr_len number of bytes in addr
+ * @return NULL on error, otherwise address string
+ */
+static const char*
+a2s (const char *plugin,
+     const void *addr,
+     uint16_t addr_len)
+{
+  struct TransportPlugin *p;
+
+  if (plugin == NULL)
+    return NULL;
+  p = find_transport (plugin);
+  if (p == NULL)
+    return NULL;
+  return p->api->address_to_string (p->api->cls,
+                                   addr,
+                                   addr_len);
+}   
+
+
 /**
  * Mark the given FAL entry as 'connected' (and hence preferred for
  * sending); also mark all others for the same peer as 'not connected'
@@ -1231,9 +1290,20 @@ mark_address_connected (struct ForeignAddressList *fal)
     {
       if (GNUNET_YES == pos->connected)
        {
+#if DEBUG_TRANSPORT
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                     "Marking address `%s' as no longer connected (due to connect on other address)\n",
+                     a2s (pos->ready_list->plugin->short_name,
+                          pos->addr,
+                          pos->addrlen));
+#endif
          GNUNET_break (cnt == GNUNET_YES);
          cnt = GNUNET_NO;
          pos->connected = GNUNET_NO;
+         GNUNET_STATISTICS_update (stats,
+                                   gettext_noop ("# connected addresses"),
+                                   -1,
+                                   GNUNET_NO);
        }
       pos = pos->next;
     }
@@ -1269,9 +1339,15 @@ transmit_to_client (struct TransportClient *client,
     {
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                   _
-                  ("Dropping message, have %u messages pending (%u is the soft limit)\n"),
-                  client->message_count, MAX_PENDING);
-      /* TODO: call to statistics... */
+                  ("Dropping message of type %u and size %u, have %u messages pending (%u is the soft limit)\n"),
+                 ntohs (msg->type),
+                 ntohs (msg->size),
+                  client->message_count, 
+                 MAX_PENDING);
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# messages dropped due to slow client"),
+                               1,
+                               GNUNET_NO);
       return;
     }
   msize = ntohs (msg->size);
@@ -1375,6 +1451,13 @@ transmit_send_continuation (void *cls,
        {
          if (mq->specific_address->connected != GNUNET_NO)
            {
+#if DEBUG_TRANSPORT
+             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                         "Marking address `%s' as no longer connected (due to transmission problem)\n",
+                         a2s (mq->specific_address->ready_list->plugin->short_name,
+                              mq->specific_address->addr,
+                              mq->specific_address->addrlen));
+#endif
              GNUNET_STATISTICS_update (stats,
                                        gettext_noop ("# connected addresses"),
                                        -1,
@@ -1439,8 +1522,9 @@ find_ready_address(struct NeighbourList *neighbour)
          if (addresses->addr != NULL)
            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                        "Have address `%s' for peer `%4s' (status: %d, %d, %d, %u, %llums, %u)\n",
-                       GNUNET_a2s (addresses->addr,
-                                   addresses->addrlen),
+                       a2s (head->plugin->short_name,
+                            addresses->addr,
+                            addresses->addrlen),
                        GNUNET_i2s (&neighbour->id),
                        addresses->connected,
                        addresses->in_transmit,
@@ -1466,7 +1550,12 @@ find_ready_address(struct NeighbourList *neighbour)
     {
 #if DEBUG_TRANSPORT
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Best address found has latency of %llu ms.\n",
+                  "Best address found (`%s') has latency of %llu ms.\n",
+                 (best_address->addrlen > 0) 
+                 ? a2s (best_address->ready_list->plugin->short_name,
+                      best_address->addr,
+                      best_address->addrlen)
+                 : "<inbound>",
                   best_address->latency.value);
 #endif
     }
@@ -1601,8 +1690,9 @@ try_transmission_to_peer (struct NeighbourList *neighbour)
               mq->message_buf_size,
               GNUNET_i2s (&neighbour->id), 
              (mq->specific_address->addr != NULL)
-             ? GNUNET_a2s (mq->specific_address->addr,
-                           mq->specific_address->addrlen)
+             ? a2s (mq->plugin->short_name,
+                    mq->specific_address->addr,
+                    mq->specific_address->addrlen)
              : "<inbound>",
              rl->plugin->short_name);
 #endif
@@ -1685,6 +1775,7 @@ transmit_to_peer (struct TransportClient *client,
   mq = GNUNET_malloc (sizeof (struct MessageQueue) + message_buf_size);
   mq->specific_address = peer_address;
   mq->client = client;
+  /* FIXME: this memcpy can be up to 7% of our total runtime! */
   memcpy (&mq[1], message_buf, message_buf_size);
   mq->message_buf = (const char*) &mq[1];
   mq->message_buf_size = message_buf_size;
@@ -1737,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;
@@ -1779,7 +1870,6 @@ refresh_hello ()
 
   GNUNET_free_non_null (our_hello);
   our_hello = hello;
-  our_hello_version++;
   GNUNET_PEERINFO_add_peer (peerinfo, our_hello);
   npos = neighbours;
   while (npos != NULL)
@@ -2012,7 +2102,7 @@ static void
 plugin_env_notify_address (void *cls,
                            const char *name,
                            const void *addr,
-                           size_t addrlen,
+                           uint16_t addrlen,
                            struct GNUNET_TIME_Relative expires)
 {
   struct TransportPlugin *p = cls;
@@ -2035,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;
@@ -2128,7 +2217,7 @@ find_peer_address(struct NeighbourList *neighbour,
                  const char *tname,
                  struct Session *session,
                  const char *addr,
-                 size_t addrlen)
+                 uint16_t addrlen)
 {
   struct ReadyList *head;
   struct ForeignAddressList *pos;
@@ -2174,7 +2263,7 @@ add_peer_address (struct NeighbourList *neighbour,
                  const char *tname,
                  struct Session *session,
                  const char *addr, 
-                 size_t addrlen)
+                 uint16_t addrlen)
 {
   struct ReadyList *head;
   struct ForeignAddressList *ret;
@@ -2287,14 +2376,15 @@ struct CheckAddressExistsClosure
   struct Session *session;
 
   /**
-   * Length of addr.
+   * Set to GNUNET_YES if the address exists.
    */
-  size_t addrlen;
+  int exists;
 
   /**
-   * Set to GNUNET_YES if the address exists.
+   * Length of addr.
    */
-  int exists;
+  uint16_t addrlen;
+
 };
 
 
@@ -2337,6 +2427,42 @@ check_address_exists (void *cls,
 }
 
 
+
+/**
+ * Iterator to free entries in the validation_map.
+ *
+ * @param cls closure (unused)
+ * @param key current key code
+ * @param value value in the hash map (validation to abort)
+ * @return GNUNET_YES (always)
+ */
+static int 
+abort_validation (void *cls,
+                 const GNUNET_HashCode * key,
+                 void *value)
+{
+  struct ValidationEntry *va = value;
+
+  if (GNUNET_SCHEDULER_NO_TASK != va->timeout_task)
+    GNUNET_SCHEDULER_cancel (sched, va->timeout_task);
+  GNUNET_free (va->transport_name);
+  if (va->chvc != NULL)
+    {
+      va->chvc->ve_count--;
+      if (va->chvc->ve_count == 0)
+       {
+         GNUNET_CONTAINER_DLL_remove (chvc_head,
+                                      chvc_tail,
+                                      va->chvc);
+         GNUNET_free (va->chvc);
+       }
+      va->chvc = NULL;
+    }
+  GNUNET_free (va);
+  return GNUNET_YES;
+}
+
+
 /**
  * HELLO validation cleanup task (validation failed).
  *
@@ -2349,6 +2475,7 @@ timeout_hello_validation (void *cls, const struct GNUNET_SCHEDULER_TaskContext *
   struct ValidationEntry *va = cls;
   struct GNUNET_PeerIdentity pid;
 
+  va->timeout_task = GNUNET_SCHEDULER_NO_TASK;
   GNUNET_STATISTICS_update (stats,
                            gettext_noop ("# address validation timeouts"),
                            1,
@@ -2357,11 +2484,11 @@ timeout_hello_validation (void *cls, const struct GNUNET_SCHEDULER_TaskContext *
                      sizeof (struct
                              GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
                      &pid.hashPubKey);
-  GNUNET_CONTAINER_multihashmap_remove (validation_map,
-                                       &pid.hashPubKey,
-                                       va);
-  GNUNET_free (va->transport_name);
-  GNUNET_free (va);
+  GNUNET_break (GNUNET_OK ==
+                GNUNET_CONTAINER_multihashmap_remove (validation_map,
+                                                     &pid.hashPubKey,
+                                                     va));
+  abort_validation (NULL, NULL, va);
 }
 
 
@@ -2409,14 +2536,15 @@ static int
 add_to_foreign_address_list (void *cls,
                             const char *tname,
                             struct GNUNET_TIME_Absolute expiration,
-                            const void *addr, size_t addrlen)
+                            const void *addr,
+                            uint16_t addrlen)
 {
   struct NeighbourList *n = cls;
   struct ForeignAddressList *fal;
   int try;
 
   GNUNET_STATISTICS_update (stats,
-                           gettext_noop ("# valid peer addresses returned by peerinfo"),
+                           gettext_noop ("# valid peer addresses returned by PEERINFO"),
                            1,
                            GNUNET_NO);      
   try = GNUNET_NO;
@@ -2425,8 +2553,8 @@ add_to_foreign_address_list (void *cls,
     {
 #if DEBUG_TRANSPORT
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Adding address `%s' (%s) for peer `%4s' due to peerinfo data for %llums.\n",
-                 GNUNET_a2s (addr, addrlen),
+                 "Adding address `%s' (%s) for peer `%4s' due to PEERINFO data for %llums.\n",
+                 a2s (tname, addr, addrlen),
                  tname,
                  GNUNET_i2s (&n->id),
                  expiration.value);
@@ -2484,13 +2612,11 @@ add_to_foreign_address_list (void *cls,
  * @param cls closure ('struct NeighbourList*')
  * @param peer id of the peer, NULL for last call
  * @param h hello message for the peer (can be NULL)
- * @param trust amount of trust we have in the peer (not used)
  */
 static void
 add_hello_for_peer (void *cls,
                    const struct GNUNET_PeerIdentity *peer,
-                   const struct GNUNET_HELLO_Message *h, 
-                   uint32_t trust)
+                   const struct GNUNET_HELLO_Message *h)
 {
   struct NeighbourList *n = cls;
 
@@ -2536,6 +2662,11 @@ setup_new_neighbour (const struct GNUNET_PeerIdentity *peer,
   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"),
@@ -2573,7 +2704,7 @@ setup_new_neighbour (const struct GNUNET_PeerIdentity *peer,
   if (do_hello)
     {
       n->piter = GNUNET_PEERINFO_iterate (peerinfo, peer,
-                                         0, GNUNET_TIME_UNIT_FOREVER_REL,
+                                         GNUNET_TIME_UNIT_FOREVER_REL,
                                          &add_hello_for_peer, n);
       transmit_to_peer (NULL, NULL, 0,
                        HELLO_ADDRESS_EXPIRATION,
@@ -2953,7 +3084,7 @@ handle_blacklist_reply (void *cls,
 
 
 /**
- * Send periodic PING messages to a give foreign address.
+ * Send periodic PING messages to a given foreign address.
  *
  * @param cls our 'struct PeriodicValidationContext*'
  * @param tc task context
@@ -2970,6 +3101,7 @@ send_periodic_ping (void *cls,
   struct CheckAddressExistsClosure caec;
   char * message_buf;
   uint16_t hello_size;
+  size_t slen;
   size_t tsize;
 
   peer_address->revalidate_task = GNUNET_SCHEDULER_NO_TASK;
@@ -3001,8 +3133,9 @@ send_periodic_ping (void *cls,
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   "Some validation of address `%s' via `%s' for peer `%4s' already in progress.\n",
                  (peer_address->addr != NULL)
-                  ? GNUNET_a2s (peer_address->addr,
-                               peer_address->addrlen)
+                  ? a2s (tp->short_name,
+                        peer_address->addr,
+                        peer_address->addrlen)
                  : "<inbound>",
                   tp->short_name,
                   GNUNET_i2s (&neighbour->id));
@@ -3013,7 +3146,7 @@ send_periodic_ping (void *cls,
   va = GNUNET_malloc (sizeof (struct ValidationEntry) + peer_address->addrlen);
   va->transport_name = GNUNET_strdup (tp->short_name);
   va->challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
-                                            (unsigned int) -1);
+                                            UINT_MAX);
   va->send_time = GNUNET_TIME_absolute_get();
   va->session = peer_address->session;
   if (peer_address->addr != NULL)
@@ -3036,26 +3169,52 @@ send_periodic_ping (void *cls,
                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
   hello_size = GNUNET_HELLO_size(our_hello);
   tsize = sizeof(struct TransportPingMessage) + hello_size;
+  if (peer_address->addr != NULL)
+    {
+      slen = strlen (tp->short_name) + 1;
+      tsize += slen + peer_address->addrlen;
+    }
+  else
+    {
+      slen = 0; /* make gcc happy */
+    }
   message_buf = GNUNET_malloc(tsize);
-  ping.challenge = htonl(va->challenge);
-  ping.header.size = htons(sizeof(struct TransportPingMessage));
   ping.header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_PING);
+  ping.challenge = htonl(va->challenge);
   memcpy(&ping.target, &neighbour->id, sizeof(struct GNUNET_PeerIdentity));
   memcpy(message_buf, our_hello, hello_size);
+  if (peer_address->addr != NULL)
+    {
+      ping.header.size = htons(sizeof(struct TransportPingMessage) + 
+                              peer_address->addrlen + 
+                              slen);
+      memcpy(&message_buf[hello_size + sizeof (struct TransportPingMessage)],
+            tp->short_name, 
+            slen);
+      memcpy(&message_buf[hello_size + sizeof (struct TransportPingMessage) + slen],
+            peer_address->addr, 
+            peer_address->addrlen);
+    }
+  else
+    {
+      ping.header.size = htons(sizeof(struct TransportPingMessage));
+    }
   memcpy(&message_buf[hello_size],
          &ping,
          sizeof(struct TransportPingMessage));
+
 #if DEBUG_TRANSPORT_REVALIDATION
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Performing re-validation of address `%s' via `%s' for peer `%4s' sending `%s' (%u bytes) and `%s' (%u bytes)\n",
+              "Performing re-validation of address `%s' via `%s' for peer `%4s' sending `%s' (%u bytes) and `%s'\n",
               (peer_address->addr != NULL) 
-             ? GNUNET_a2s (peer_address->addr,
-                           peer_address->addrlen)
+             ? a2s (peer_address->plugin->short_name,
+                    peer_address->addr,
+                    peer_address->addrlen)
              : "<inbound>",
               tp->short_name,
               GNUNET_i2s (&neighbour->id),
               "HELLO", hello_size,
-              "PING", sizeof (struct TransportPingMessage));
+              "PING");
 #endif
   GNUNET_STATISTICS_update (stats,
                            gettext_noop ("# PING messages sent for re-validation"),
@@ -3139,8 +3298,9 @@ handle_payload_message (const struct GNUNET_MessageHeader *message,
     }
 #if DEBUG_TRANSPORT
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Received message of type %u from `%4s', sending to all clients.\n",
+             "Received message of type %u and size %u from `%4s', sending to all clients.\n",
              ntohs (message->type), 
+             ntohs (message->size), 
              GNUNET_i2s (&n->id));
 #endif
   if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker,
@@ -3177,6 +3337,7 @@ handle_payload_message (const struct GNUNET_MessageHeader *message,
   im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV);
   im->latency = GNUNET_TIME_relative_hton (n->latency);
   im->peer = n->id;
+  im->distance = ntohl(n->distance);
   memcpy (&im[1], message, msize);
   cpos = clients;
   while (cpos != NULL)
@@ -3211,30 +3372,132 @@ check_pending_validation (void *cls,
   struct GNUNET_PeerIdentity target;
   struct NeighbourList *n;
   struct ForeignAddressList *fal;
+  struct OwnAddressList *oal;
+  struct TransportPlugin *tp;
   struct GNUNET_MessageHeader *prem;
+  uint16_t ps;
+  const char *addr;
+  size_t slen;
+  size_t alen;
 
-  if (ve->challenge != challenge)
-    return GNUNET_YES;
-  if (GNUNET_OK !=
-      GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PING,
-                               &pong->purpose, 
-                               &pong->signature,
-                               &ve->publicKey))
+  ps = ntohs (pong->header.size);
+  if (ps < sizeof (struct TransportPongMessage))
     {
       GNUNET_break_op (0);
-      return GNUNET_YES;
+      return GNUNET_NO;
     }
-
+  addr = (const char*) &pong[1];
+  slen = strlen (ve->transport_name) + 1;
+  if ( (ps - sizeof (struct TransportPongMessage) != ve->addrlen + slen) ||
+       (ve->challenge != challenge) ||       
+       (addr[slen-1] != '\0') ||
+       (0 != strcmp (addr, ve->transport_name)) || 
+       (ntohl (pong->purpose.size) 
+       != sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
+       sizeof (uint32_t) +
+       sizeof (struct GNUNET_TIME_AbsoluteNBO) +
+       sizeof (struct GNUNET_PeerIdentity) + ve->addrlen + slen) )
+    return GNUNET_YES;
+  alen = ps - sizeof (struct TransportPongMessage) - slen;
+  switch (ntohl (pong->purpose.purpose))
+    {
+    case GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN:
+      if ( (ve->addrlen + slen != ntohl (pong->addrlen)) ||
+          (0 != memcmp (&addr[slen],
+                        ve->addr,
+                        ve->addrlen)) )
+       return GNUNET_YES; /* different entry, keep trying! */
+      if (0 != memcmp (&pong->pid,
+                      key,
+                      sizeof (struct GNUNET_PeerIdentity))) 
+       {
+         GNUNET_break_op (0);
+         return GNUNET_NO;
+       }
+      if (GNUNET_OK !=
+         GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN,
+                                   &pong->purpose, 
+                                   &pong->signature,
+                                   &ve->publicKey)) 
+       {
+         GNUNET_break_op (0);
+         return GNUNET_NO;
+       }
 #if DEBUG_TRANSPORT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Confirmed validity of address, peer `%4s' has address `%s' (%s).\n",
-             GNUNET_h2s (key),
-             (ve->addr != NULL) 
-             ? GNUNET_a2s ((const struct sockaddr *) ve->addr,
-                           ve->addrlen)
-             : "<inbound>",
-             ve->transport_name);
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Confirmed validity of address, peer `%4s' has address `%s' (%s).\n",
+                 GNUNET_h2s (key),
+                 a2s (ve->transport_name,
+                      (const struct sockaddr *) ve->addr,
+                      ve->addrlen),
+                 ve->transport_name);
 #endif
+      break;
+    case GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_USING:
+      if (ve->addrlen != 0) 
+       return GNUNET_YES; /* different entry, keep trying */
+      if ( (0 != memcmp (&pong->pid,
+                        &my_identity,
+                        sizeof (struct GNUNET_PeerIdentity))) ||
+          (ve->addrlen != 0) )
+       {
+         GNUNET_break_op (0);
+         return GNUNET_NO;
+       }
+      tp = find_transport (ve->transport_name);
+      if (tp == NULL)
+       {
+         GNUNET_break (0);
+         return GNUNET_YES;
+       }
+      oal = tp->addresses;
+      while (NULL != oal)
+       {
+         if ( (oal->addrlen == alen) &&
+              (0 == memcmp (&oal[1],
+                            &addr[slen],
+                            alen)) )
+           break;
+         oal = oal->next;
+       }
+      if (oal == NULL)
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                     _("Not accepting PONG with address `%s' since I cannot confirm having this address.\n"),
+                     a2s (ve->transport_name,
+                          &addr[slen],
+                          alen));
+         return GNUNET_NO;       
+       }
+      if (GNUNET_OK !=
+         GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_USING,
+                                   &pong->purpose, 
+                                   &pong->signature,
+                                   &ve->publicKey)) 
+       {
+         GNUNET_break_op (0);
+         return GNUNET_NO;
+       }
+#if DEBUG_TRANSPORT
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Confirmed that peer `%4s' is talking to us using address `%s' (%s) for us.\n",
+                 GNUNET_h2s (key),
+                 a2s (ve->transport_name,
+                      &addr[slen],
+                      alen),
+                 ve->transport_name);
+#endif
+      break;
+    default:
+      GNUNET_break_op (0);
+      return GNUNET_NO;
+    }
+  if (GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_ntoh (pong->expiration)).value == 0)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                 _("Received expired signature.  Check system time.\n"));
+      return GNUNET_NO;
+    }
   GNUNET_STATISTICS_update (stats,
                            gettext_noop ("# address validation successes"),
                            1,
@@ -3278,6 +3541,7 @@ check_pending_validation (void *cls,
        n->latency = fal->latency;
       else
        n->latency.value = (fal->latency.value + n->latency.value) / 2;
+
       n->distance = fal->distance;
       if (GNUNET_NO == n->received_pong)
        {
@@ -3304,10 +3568,7 @@ check_pending_validation (void *cls,
                 GNUNET_CONTAINER_multihashmap_remove (validation_map,
                                                       key,
                                                       ve));
-  GNUNET_SCHEDULER_cancel (sched,
-                          ve->timeout_task);
-  GNUNET_free (ve->transport_name);
-  GNUNET_free (ve);
+  abort_validation (NULL, NULL, ve);
   return GNUNET_NO;
 }
 
@@ -3366,15 +3627,6 @@ handle_pong (void *cls, const struct GNUNET_MessageHeader *message,
       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
 }
 
 
@@ -3394,12 +3646,20 @@ transmit_hello_and_ping (void *cls,
   uint16_t hello_size;
   size_t tsize;
   char * message_buf;
+  struct GNUNET_PeerIdentity id;
+  size_t slen;
 
+  GNUNET_CRYPTO_hash (&va->publicKey,
+                     sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+                     &id.hashPubKey);
   if (neighbour == NULL)
     {
       /* FIXME: stats... */
-      GNUNET_free (va->transport_name);
-      GNUNET_free (va);
+      GNUNET_break (GNUNET_OK ==
+                   GNUNET_CONTAINER_multihashmap_remove (validation_map,
+                                                         &id.hashPubKey,
+                                                         va));
+      abort_validation (NULL, NULL, va);
       return;
     }
   neighbour->publicKey = va->publicKey;
@@ -3414,30 +3674,44 @@ transmit_hello_and_ping (void *cls,
                   "Failed to add peer `%4s' for plugin `%s'\n",
                   GNUNET_i2s (&neighbour->id), 
                  va->transport_name);
-      GNUNET_free (va->transport_name);
-      GNUNET_free (va);
+      GNUNET_break (GNUNET_OK ==
+                   GNUNET_CONTAINER_multihashmap_remove (validation_map,
+                                                         &id.hashPubKey,
+                                                         va));
+      abort_validation (NULL, NULL, va);
       return;
     }
   hello_size = GNUNET_HELLO_size(our_hello);
-  tsize = sizeof(struct TransportPingMessage) + hello_size;
+  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));
+  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",
-              GNUNET_a2s ((const void*) &va[1], va->addrlen),
+             (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));
+             "PING", sizeof (struct TransportPingMessage) + va->addrlen + slen);
 #endif
+
   GNUNET_STATISTICS_update (stats,
                            gettext_noop ("# PING messages sent for initial validation"),
                            1,
@@ -3467,7 +3741,8 @@ 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;
@@ -3478,6 +3753,7 @@ run_validation (void *cls,
   struct OwnAddressList *oal;
 
   GNUNET_assert (addr != NULL);
+
   GNUNET_STATISTICS_update (stats,
                            gettext_noop ("# peer addresses scheduled for validation"),
                            1,
@@ -3501,7 +3777,7 @@ run_validation (void *cls,
   while (NULL != oal)
     {
       if ( (oal->addrlen == addrlen) &&
-          (0 == memcmp (oal->addr,
+          (0 == memcmp (&oal[1],
                         addr,
                         addrlen)) )
        {
@@ -3524,7 +3800,9 @@ run_validation (void *cls,
     {
 #if DEBUG_TRANSPORT
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  _("Attempted to validate blacklisted peer `%s' using `%s'!\n"), GNUNET_i2s(&id), tname);
+                  "Attempted to validate blacklisted peer `%s' using `%s'!\n", 
+                 GNUNET_i2s(&id), 
+                 tname);
 #endif
       return GNUNET_OK;
     }
@@ -3546,7 +3824,7 @@ run_validation (void *cls,
 #if DEBUG_TRANSPORT > 1
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                  "Validation of address `%s' via `%s' for peer `%4s' already in progress.\n",
-                 GNUNET_a2s (addr, addrlen),
+                 a2s (tname, addr, addrlen),
                  tname,
                  GNUNET_i2s (&id));
 #endif
@@ -3557,9 +3835,11 @@ run_validation (void *cls,
       return GNUNET_OK;
     }
   va = GNUNET_malloc (sizeof (struct ValidationEntry) + addrlen);
+  va->chvc = chvc;
+  chvc->ve_count++;
   va->transport_name = GNUNET_strdup (tname);
   va->challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
-                                            (unsigned int) -1);
+                                            UINT_MAX);
   va->send_time = GNUNET_TIME_absolute_get();
   va->addr = (const void*) &va[1];
   memcpy (&va[1], addr, addrlen);
@@ -3588,13 +3868,11 @@ run_validation (void *cls,
  * @param cls closure
  * @param peer id of the peer, NULL for last call
  * @param h hello message for the peer (can be NULL)
- * @param trust amount of trust we have in the peer (not used)
  */
 static void
 check_hello_validated (void *cls,
                        const struct GNUNET_PeerIdentity *peer,
-                       const struct GNUNET_HELLO_Message *h, 
-                      uint32_t trust)
+                       const struct GNUNET_HELLO_Message *h)
 {
   struct CheckHelloValidatedContext *chvc = cls;
   struct GNUNET_HELLO_Message *plain_hello;
@@ -3605,9 +3883,6 @@ check_hello_validated (void *cls,
   if (peer == NULL)
     {
       chvc->piter = NULL;
-      GNUNET_CONTAINER_DLL_remove (chvc_head,
-                                  chvc_tail,
-                                  chvc);
       if (GNUNET_NO == chvc->hello_known)
        {
          /* notify PEERINFO about the peer now, so that we at least
@@ -3623,7 +3898,7 @@ check_hello_validated (void *cls,
          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
@@ -3643,14 +3918,21 @@ check_hello_validated (void *cls,
                                    1,
                                    GNUNET_NO);      
        }
-      GNUNET_free (chvc);
+      chvc->ve_count--;
+      if (chvc->ve_count == 0)
+       {
+         GNUNET_CONTAINER_DLL_remove (chvc_head,
+                                      chvc_tail,
+                                      chvc);
+         GNUNET_free (chvc);     
+       }
       return;
     } 
   if (h == NULL)
     return;
 #if DEBUG_TRANSPORT
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Peerinfo had `%s' message for peer `%4s', validating only new addresses.\n",
+             "PEERINFO had `%s' message for peer `%4s', validating only new addresses.\n",
              "HELLO",
              GNUNET_i2s (peer));
 #endif
@@ -3699,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)))
@@ -3711,6 +3995,7 @@ process_hello (struct TransportPlugin *plugin,
                            gettext_noop ("# HELLOs received for validation"),
                            1,
                            GNUNET_NO);      
+
   /* first, check if load is too high */
   if (GNUNET_SCHEDULER_get_load (sched,
                                 GNUNET_SCHEDULER_PRIORITY_BACKGROUND) > MAX_HELLO_LOAD)
@@ -3719,17 +4004,31 @@ process_hello (struct TransportPlugin *plugin,
                                gettext_noop ("# HELLOs ignored due to high load"),
                                1,
                                GNUNET_NO);      
+#if DEBUG_TRANSPORT_HELLO
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Ignoring `%s' for `%4s', load too high.\n",
+                  "HELLO",
+                  GNUNET_i2s (&target));
+#endif
       return GNUNET_OK;
     }
   hello = (const struct GNUNET_HELLO_Message *) message;
   if (GNUNET_OK != GNUNET_HELLO_get_key (hello, &publicKey))
     {
+#if DEBUG_TRANSPORT_HELLO
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Unable to get public key from `%s' for `%4s'!\n",
+                  "HELLO",
+                  GNUNET_i2s (&target));
+#endif
       GNUNET_break_op (0);
       return GNUNET_SYSERR;
     }
+
   GNUNET_CRYPTO_hash (&publicKey,
                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
                       &target.hashPubKey);
+
   if (0 == memcmp (&my_identity,
                   &target,
                   sizeof (struct GNUNET_PeerIdentity)))
@@ -3740,14 +4039,42 @@ process_hello (struct TransportPlugin *plugin,
                                GNUNET_NO);      
       return GNUNET_OK;      
     }
-#if DEBUG_TRANSPORT > 1
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Processing `%s' message for `%4s' of size %u\n",
-              "HELLO", 
-             GNUNET_i2s (&target), 
-             GNUNET_HELLO_size(hello));
+  chvc = chvc_head;
+  while (NULL != chvc)
+    {
+      if (GNUNET_HELLO_equals (hello,
+                              chvc->hello,
+                              GNUNET_TIME_absolute_get ()).value > 0)
+       {
+#if DEBUG_TRANSPORT_HELLO
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                     "Received duplicate `%s' message for `%4s'; ignored\n",
+                     "HELLO", 
+                     GNUNET_i2s (&target));
+#endif
+         return GNUNET_OK; /* validation already pending */
+       }
+      if (GNUNET_HELLO_size(hello) == GNUNET_HELLO_size (chvc->hello))
+       GNUNET_break (0 != memcmp (hello, chvc->hello,
+                                  GNUNET_HELLO_size(hello)));
+      chvc = chvc->next;
+    }
+#if DEBUG_TRANSPORT_HELLO
+  if (plugin != NULL)
+    {
+      my_id = GNUNET_strdup(GNUNET_i2s(plugin->env.my_identity));
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "%s: Starting validation of `%s' message for `%4s' via '%s' of size %u\n",
+                  my_id,
+                  "HELLO",
+                  GNUNET_i2s (&target),
+                  plugin->short_name,
+                  GNUNET_HELLO_size(hello));
+      GNUNET_free(my_id);
+    }
 #endif
   chvc = GNUNET_malloc (sizeof (struct CheckHelloValidatedContext) + hsize);
+  chvc->ve_count = 1;
   chvc->hello = (const struct GNUNET_HELLO_Message *) &chvc[1];
   memcpy (&chvc[1], hello, hsize);
   GNUNET_CONTAINER_DLL_insert (chvc_head,
@@ -3757,7 +4084,6 @@ process_hello (struct TransportPlugin *plugin,
      (continuation will then schedule actual validation) */
   chvc->piter = GNUNET_PEERINFO_iterate (peerinfo,
                                          &target,
-                                         0,
                                          HELLO_VERIFICATION_TIMEOUT,
                                          &check_hello_validated, chvc);
   return GNUNET_OK;
@@ -3906,17 +4232,23 @@ disconnect_neighbour (struct NeighbourList *n, int check)
 static int 
 handle_ping(void *cls, const struct GNUNET_MessageHeader *message,
            const struct GNUNET_PeerIdentity *peer,
+           struct Session *session,
            const char *sender_address,
-           size_t sender_address_len)
+           uint16_t sender_address_len)
 {
   struct TransportPlugin *plugin = cls;
+  struct SessionHeader *session_header = (struct SessionHeader*) session;
   struct TransportPingMessage *ping;
   struct TransportPongMessage *pong;
   struct NeighbourList *n;
   struct ReadyList *rl;
   struct ForeignAddressList *fal;
+  struct OwnAddressList *oal;
+  const char *addr;
+  size_t alen;
+  size_t slen;
 
-  if (ntohs (message->size) != sizeof (struct TransportPingMessage))
+  if (ntohs (message->size) < sizeof (struct TransportPingMessage))
     {
       GNUNET_break_op (0);
       return GNUNET_SYSERR;
@@ -3937,32 +4269,152 @@ handle_ping(void *cls, const struct GNUNET_MessageHeader *message,
              "Processing `%s' from `%s'\n",
              "PING", 
              (sender_address != NULL) 
-             ? GNUNET_a2s ((const struct sockaddr *)sender_address, 
-                           sender_address_len)
+             ? a2s (plugin->short_name,
+                    (const struct sockaddr *)sender_address, 
+                    sender_address_len)
              : "<inbound>");
 #endif
   GNUNET_STATISTICS_update (stats,
                            gettext_noop ("# PING messages received"),
                            1,
                            GNUNET_NO);
-  pong = GNUNET_malloc (sizeof (struct TransportPongMessage) + sender_address_len);
-  pong->header.size = htons (sizeof (struct TransportPongMessage) + sender_address_len);
-  pong->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG);
-  pong->purpose.size =
-    htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
-           sizeof (uint32_t) +
-           sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) + sender_address_len);
-  pong->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PING);
-  pong->challenge = ping->challenge;
-  pong->addrlen = htons(sender_address_len);
-  memcpy(&pong->signer, 
-        &my_public_key, 
-        sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
-  if (sender_address != NULL)
-    memcpy (&pong[1], sender_address, sender_address_len);
-  GNUNET_assert (GNUNET_OK ==
-                 GNUNET_CRYPTO_rsa_sign (my_private_key,
-                                         &pong->purpose, &pong->signature));
+  addr = (const char*) &ping[1];
+  alen = ntohs (message->size) - sizeof (struct TransportPingMessage);
+  slen = strlen (plugin->short_name) + 1;
+  if (alen == 0)
+    {      
+      /* peer wants to confirm that we have an outbound connection to him */
+      if (session == NULL)
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                     _("Refusing to create PONG since I do not have a session with `%s'.\n"),
+                     GNUNET_i2s (peer));
+         return GNUNET_SYSERR;
+       }
+      pong = GNUNET_malloc (sizeof (struct TransportPongMessage) + sender_address_len + slen);
+      pong->header.size = htons (sizeof (struct TransportPongMessage) + sender_address_len + slen);
+      pong->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG);
+      pong->purpose.size =
+       htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
+              sizeof (uint32_t) +
+              sizeof (struct GNUNET_TIME_AbsoluteNBO) +
+              sizeof (struct GNUNET_PeerIdentity) + sender_address_len + slen);
+      pong->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_USING);
+      pong->challenge = ping->challenge;
+      pong->addrlen = htonl(sender_address_len + slen);
+      memcpy(&pong->pid, 
+            peer,
+            sizeof(struct GNUNET_PeerIdentity));
+      memcpy (&pong[1], 
+             plugin->short_name, 
+             slen);
+      memcpy (&((char*)&pong[1])[slen], 
+             sender_address, 
+             sender_address_len);
+      if (GNUNET_TIME_absolute_get_remaining (session_header->pong_sig_expires).value < PONG_SIGNATURE_LIFETIME.value / 4)
+       {
+         /* create / update cached sig */
+#if DEBUG_TRANSPORT
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                     "Creating PONG signature to indicate active connection.\n");
+#endif
+         session_header->pong_sig_expires = GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME);
+         pong->expiration = GNUNET_TIME_absolute_hton (session_header->pong_sig_expires);
+         GNUNET_assert (GNUNET_OK ==
+                        GNUNET_CRYPTO_rsa_sign (my_private_key,
+                                                &pong->purpose,
+                                                &session_header->pong_signature));
+       }
+      else
+       {
+         pong->expiration = GNUNET_TIME_absolute_hton (session_header->pong_sig_expires);
+       }
+      memcpy (&pong->signature,
+             &session_header->pong_signature,
+             sizeof (struct GNUNET_CRYPTO_RsaSignature));    
+
+
+    }
+  else
+    {
+      /* peer wants to confirm that this is one of our addresses */
+      addr += slen;
+      alen -= slen;
+      if (GNUNET_OK !=
+         plugin->api->check_address (plugin->api->cls,
+                                     addr,
+                                     alen))
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                     _("Not confirming PING with address `%s' since I cannot confirm having this address.\n"),
+                     a2s (plugin->short_name,
+                          addr,
+                          alen));
+         return GNUNET_NO;
+       }
+      oal = plugin->addresses;
+      while (NULL != oal)
+       {
+         if ( (oal->addrlen == alen) &&
+              (0 == memcmp (addr,
+                            &oal[1],
+                            alen)) )
+           break;
+         oal = oal->next;
+       }
+      pong = GNUNET_malloc (sizeof (struct TransportPongMessage) + alen + slen);
+      pong->header.size = htons (sizeof (struct TransportPongMessage) + alen + slen);
+      pong->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG);
+      pong->purpose.size =
+       htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
+              sizeof (uint32_t) +
+              sizeof (struct GNUNET_TIME_AbsoluteNBO) +
+              sizeof (struct GNUNET_PeerIdentity) + alen + slen);
+      pong->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN);
+      pong->challenge = ping->challenge;
+      pong->addrlen = htonl(alen + slen);
+      memcpy(&pong->pid, 
+            &my_identity, 
+            sizeof(struct GNUNET_PeerIdentity));
+      memcpy (&pong[1], plugin->short_name, slen);
+      memcpy (&((char*)&pong[1])[slen], addr, alen);
+      if ( (oal != NULL) &&
+          (GNUNET_TIME_absolute_get_remaining (oal->pong_sig_expires).value < PONG_SIGNATURE_LIFETIME.value / 4) )
+       {
+         /* create / update cached sig */
+#if DEBUG_TRANSPORT
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                     "Creating PONG signature to indicate ownership.\n");
+#endif
+         oal->pong_sig_expires = GNUNET_TIME_absolute_min (oal->expires,
+                                                           GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME));
+         pong->expiration = GNUNET_TIME_absolute_hton (oal->pong_sig_expires);
+         GNUNET_assert (GNUNET_OK ==
+                        GNUNET_CRYPTO_rsa_sign (my_private_key,
+                                                &pong->purpose,
+                                                &oal->pong_signature));            
+         memcpy (&pong->signature,
+                 &oal->pong_signature,
+                 sizeof (struct GNUNET_CRYPTO_RsaSignature));    
+       }
+      else if (oal == NULL)
+       {
+         /* not using cache (typically DV-only) */
+         pong->expiration = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME));
+         GNUNET_assert (GNUNET_OK ==
+                        GNUNET_CRYPTO_rsa_sign (my_private_key,
+                                                &pong->purpose,
+                                                &pong->signature));        
+       }
+      else
+       {
+         /* can used cached version */
+         pong->expiration = GNUNET_TIME_absolute_hton (oal->pong_sig_expires);
+         memcpy (&pong->signature,
+                 &oal->pong_signature,
+                 sizeof (struct GNUNET_CRYPTO_RsaSignature));    
+       }
+    }
   n = find_neighbour(peer);
   GNUNET_assert (n != NULL);
   /* first try reliable response transmission */
@@ -4043,10 +4495,10 @@ handle_ping(void *cls, const struct GNUNET_MessageHeader *message,
 static struct GNUNET_TIME_Relative
 plugin_env_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
                     const struct GNUNET_MessageHeader *message,
-                    unsigned int distance,
+                    uint32_t distance,
                    struct Session *session,
                    const char *sender_address,
-                    size_t sender_address_len)
+                    uint16_t sender_address_len)
 {
   struct TransportPlugin *plugin = cls;
   struct ReadyList *service_context;
@@ -4119,8 +4571,10 @@ plugin_env_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
        }
 #if DEBUG_PING_PONG
           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                      "Received message of type %u from `%4s', sending to all clients.\n",
-                      ntohs (message->type), GNUNET_i2s (peer));
+                      "Received message of type %u and size %u from `%4s', sending to all clients.\n",
+                      ntohs (message->type), 
+                      ntohs (message->size), 
+                     GNUNET_i2s (peer));
 #endif
       switch (ntohs (message->type))
        {
@@ -4132,7 +4586,7 @@ plugin_env_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
          process_hello (plugin, message);
          break;
        case GNUNET_MESSAGE_TYPE_TRANSPORT_PING:
-         handle_ping (plugin, message, peer, sender_address, sender_address_len);
+         handle_ping (plugin, message, peer, session, sender_address, sender_address_len);
          break;
        case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG:
          handle_pong (plugin, message, peer, sender_address, sender_address_len);
@@ -4337,7 +4791,7 @@ handle_send (void *cls,
                            GNUNET_NO);      
   obm = (const struct OutboundMessage *) message;
   obmm = (const struct GNUNET_MessageHeader *) &obm[1];
-  msize = ntohs (obmm->size);
+  msize = size - sizeof (struct OutboundMessage);
 #if DEBUG_TRANSPORT
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Received `%s' request from client with target `%4s' and message of type %u and size %u\n",
@@ -4345,17 +4799,12 @@ handle_send (void *cls,
               ntohs (obmm->type),
               msize);
 #endif
-  if (size != msize + sizeof (struct OutboundMessage))
-    {
-      GNUNET_break (0);
-      GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
-      return;
-    }
   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,
@@ -4411,7 +4860,7 @@ handle_set_quota (void *cls,
 
 
 /**
- * Take the given address and append it to the set of results send back to
+ * Take the given address and append it to the set of results sent back to
  * the client.
  * 
  * @param cls the transmission context used ('struct GNUNET_SERVER_TransmitContext*')
@@ -4427,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)
@@ -4501,29 +4951,6 @@ handle_address_lookup (void *cls,
                                          &transmit_address_to_client, tc);
 }
 
-/**
- * List of handlers for the messages understood by this
- * service.
- */
-static struct GNUNET_SERVER_MessageHandler handlers[] = {
-  {&handle_start, NULL,
-   GNUNET_MESSAGE_TYPE_TRANSPORT_START, 0},
-  {&handle_hello, NULL,
-   GNUNET_MESSAGE_TYPE_HELLO, 0},
-  {&handle_send, NULL,
-   GNUNET_MESSAGE_TYPE_TRANSPORT_SEND, 0},
-  {&handle_set_quota, NULL,
-   GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA, sizeof (struct QuotaSetMessage)},
-  {&handle_address_lookup, NULL,
-   GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_LOOKUP,
-   0},
-  {&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}
-};
-
 
 /**
  * Setup the environment for this plugin.
@@ -4669,28 +5096,6 @@ client_disconnect_notification (void *cls,
 }
 
 
-/**
- * Iterator to free entries in the validation_map.
- *
- * @param cls closure (unused)
- * @param key current key code
- * @param value value in the hash map (validation to abort)
- * @return GNUNET_YES (always)
- */
-static int 
-abort_validation (void *cls,
-                 const GNUNET_HashCode * key,
-                 void *value)
-{
-  struct ValidationEntry *va = value;
-
-  GNUNET_SCHEDULER_cancel (sched, va->timeout_task);
-  GNUNET_free (va->transport_name);
-  GNUNET_free (va);
-  return GNUNET_YES;
-}
-
-
 /**
  * Function called when the service shuts down.  Unloads our plugins
  * and cancels pending validations.
@@ -4734,26 +5139,35 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
     GNUNET_CRYPTO_rsa_key_free (my_private_key);
   GNUNET_free_non_null (our_hello);
 
+  GNUNET_CONTAINER_multihashmap_iterate (validation_map,
+                                        &abort_validation,
+                                        NULL);
+  GNUNET_CONTAINER_multihashmap_destroy (validation_map);
+  validation_map = NULL;
+
   /* free 'chvc' data structure */
   while (NULL != (chvc = chvc_head))
     {
       chvc_head = chvc->next;
-      GNUNET_PEERINFO_iterate_cancel (chvc->piter);
+      if (chvc->piter != NULL)
+       GNUNET_PEERINFO_iterate_cancel (chvc->piter);      
+      else
+       GNUNET_break (0);
+      GNUNET_assert (chvc->ve_count == 0);
       GNUNET_free (chvc);
     }
   chvc_tail = NULL;
 
-  GNUNET_CONTAINER_multihashmap_iterate (validation_map,
-                                        &abort_validation,
-                                        NULL);
-  GNUNET_CONTAINER_multihashmap_destroy (validation_map);
-  validation_map = NULL;
   if (stats != NULL)
     {
       GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
       stats = NULL;
     }
-
+  if (peerinfo != NULL)
+    {
+      GNUNET_PEERINFO_disconnect (peerinfo);
+      peerinfo = NULL;
+    }
   /* Can we assume those are gone by now, or do we need to clean up
      explicitly!? */
   GNUNET_break (bl_head == NULL);
@@ -4766,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;
@@ -4847,7 +5279,6 @@ run (void *cls,
   GNUNET_CRYPTO_hash (&my_public_key,
                       sizeof (my_public_key), &my_identity.hashPubKey);
   /* setup notification */
-  server = serv;
   GNUNET_SERVER_disconnect_notify (server,
                                    &client_disconnect_notification, NULL);
   /* load plugins... */
@@ -4893,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,