expand GNUNET_OS_ProjectData API to also enable de-duplcation of logic for --help
[oweals/gnunet.git] / src / util / client.c
index f964b7322177ced5a5e531c7bbe2913f10f23e3a..d87be74f6d943c1bfdc072906ca6fe8a523b3919 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet.
 /*
      This file is part of GNUnet.
-     (C) 2001, 2002, 2006, 2008, 2009 Christian Grothoff (and other contributing authors)
+     Copyright (C) 2001-2013 GNUnet e.V.
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
 
      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
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
@@ -14,8 +14,8 @@
 
      You should have received a copy of the GNU General Public License
      along with GNUnet; see the file COPYING.  If not, write to the
 
      You should have received a copy of the GNU General Public License
      along with GNUnet; see the file COPYING.  If not, write to the
-     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-     Boston, MA 02111-1307, USA.
+     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+     Boston, MA 02110-1301, USA.
 */
 
 /**
 */
 
 /**
  * Generic TCP code for reliable, record-oriented TCP
  * connections between clients and service providers.
  */
  * Generic TCP code for reliable, record-oriented TCP
  * connections between clients and service providers.
  */
-
 #include "platform.h"
 #include "platform.h"
-#include "gnunet_common.h"
-#include "gnunet_client_lib.h"
 #include "gnunet_protocols.h"
 #include "gnunet_protocols.h"
-#include "gnunet_server_lib.h"
-#include "gnunet_scheduler_lib.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_socks.h"
 
 
-#define DEBUG_CLIENT GNUNET_NO
 
 /**
  * How often do we re-try tranmsitting requests before giving up?
 
 /**
  * How often do we re-try tranmsitting requests before giving up?
@@ -43,6 +39,7 @@
  */
 #define MAX_ATTEMPTS 50
 
  */
 #define MAX_ATTEMPTS 50
 
+#define LOG(kind,...) GNUNET_log_from (kind, "util",__VA_ARGS__)
 
 /**
  * Handle for a transmission request.
 
 /**
  * Handle for a transmission request.
@@ -52,7 +49,7 @@ struct GNUNET_CLIENT_TransmitHandle
   /**
    * Connection state.
    */
   /**
    * Connection state.
    */
-  struct GNUNET_CLIENT_Connection *sock;
+  struct GNUNET_CLIENT_Connection *client;
 
   /**
    * Function to call to get the data for transmission.
 
   /**
    * Function to call to get the data for transmission.
@@ -60,7 +57,7 @@ struct GNUNET_CLIENT_TransmitHandle
   GNUNET_CONNECTION_TransmitReadyNotify notify;
 
   /**
   GNUNET_CONNECTION_TransmitReadyNotify notify;
 
   /**
-   * Closure for notify.
+   * Closure for @e notify.
    */
   void *notify_cls;
 
    */
   void *notify_cls;
 
@@ -74,7 +71,7 @@ struct GNUNET_CLIENT_TransmitHandle
    * If we are re-trying and are delaying to do so,
    * handle to the scheduled task managing the delay.
    */
    * If we are re-trying and are delaying to do so,
    * handle to the scheduled task managing the delay.
    */
-  GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
+  struct GNUNET_SCHEDULER_Task *reconnect_task;
 
   /**
    * Timeout for the operation overall.
 
   /**
    * Timeout for the operation overall.
@@ -104,7 +101,7 @@ struct GNUNET_CLIENT_TransmitHandle
 
 
 /**
 
 
 /**
- * Context for processing 
+ * Context for processing
  * "GNUNET_CLIENT_transmit_and_get_response" requests.
  */
 struct TransmitGetResponseContext
  * "GNUNET_CLIENT_transmit_and_get_response" requests.
  */
 struct TransmitGetResponseContext
@@ -112,7 +109,7 @@ struct TransmitGetResponseContext
   /**
    * Client handle.
    */
   /**
    * Client handle.
    */
-  struct GNUNET_CLIENT_Connection *sock;
+  struct GNUNET_CLIENT_Connection *client;
 
   /**
    * Message to transmit; do not free, allocated
 
   /**
    * Message to transmit; do not free, allocated
@@ -131,7 +128,7 @@ struct TransmitGetResponseContext
   GNUNET_CLIENT_MessageHandler rn;
 
   /**
   GNUNET_CLIENT_MessageHandler rn;
 
   /**
-   * Closure for "rn".
+   * Closure for @e rn.
    */
   void *rn_cls;
 };
    */
   void *rn_cls;
 };
@@ -146,20 +143,14 @@ struct GNUNET_CLIENT_Connection
 {
 
   /**
 {
 
   /**
-   * the socket handle, NULL if not live
-   */
-  struct GNUNET_CONNECTION_Handle *sock;
-
-  /**
-   * Our scheduler.
+   * The connection handle, NULL if not live
    */
    */
-  struct GNUNET_SCHEDULER_Handle *sched;
+  struct GNUNET_CONNECTION_Handle *connection;
 
   /**
    * Our configuration.
 
   /**
    * Our configuration.
-   * FIXME: why do we DUP the configuration? Avoid this!
    */
    */
-  struct GNUNET_CONFIGURATION_Handle *cfg;
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
 
   /**
    * Name of the service we interact with.
 
   /**
    * Name of the service we interact with.
@@ -178,7 +169,7 @@ struct GNUNET_CLIENT_Connection
   GNUNET_CLIENT_MessageHandler receiver_handler;
 
   /**
   GNUNET_CLIENT_MessageHandler receiver_handler;
 
   /**
-   * Closure for receiver_handler.
+   * Closure for @e receiver_handler.
    */
   void *receiver_handler_cls;
 
    */
   void *receiver_handler_cls;
 
@@ -188,26 +179,11 @@ struct GNUNET_CLIENT_Connection
    */
   struct GNUNET_CLIENT_TransmitHandle *th;
 
    */
   struct GNUNET_CLIENT_TransmitHandle *th;
 
-  /**
-   * Handler for service test completion (NULL unless in service_test)
-   */
-  GNUNET_SCHEDULER_Task test_cb;
-
-  /**
-   * Deadline for calling 'test_cb'.
-   */
-  struct GNUNET_TIME_Absolute test_deadline;
-
   /**
    * If we are re-trying and are delaying to do so,
    * handle to the scheduled task managing the delay.
    */
   /**
    * If we are re-trying and are delaying to do so,
    * handle to the scheduled task managing the delay.
    */
-  GNUNET_SCHEDULER_TaskIdentifier receive_task;
-
-  /**
-   * Closure for test_cb (NULL unless in service_test)
-   */
-  void *test_cb_cls;
+  struct GNUNET_SCHEDULER_Task * receive_task;
 
   /**
    * Buffer for received message.
 
   /**
    * Buffer for received message.
@@ -242,105 +218,218 @@ struct GNUNET_CLIENT_Connection
 
   /**
    * Are we currently busy doing receive-processing?
 
   /**
    * Are we currently busy doing receive-processing?
-   * GNUNET_YES if so, GNUNET_NO if not.
+   * #GNUNET_YES if so, #GNUNET_NO if not. #GNUNET_SYSERR
+   * if the connection has failed (but we may not have
+   * closed the handle itself yet).
    */
   int in_receive;
 
   /**
    */
   int in_receive;
 
   /**
-   * Are we ignoring shutdown signals?
+   * Is this the first message we are sending to the service?
+   */
+  int first_message;
+
+  /**
+   * How often have we tried to connect?
    */
    */
-  int ignore_shutdown;
+  unsigned int attempts;
 
 };
 
 
 
 };
 
 
+/**
+ * Try connecting to the server using UNIX domain sockets.
+ *
+ * @param service_name name of service to connect to
+ * @param cfg configuration to use
+ * @return NULL on error, connection to UNIX otherwise
+ */
+static struct GNUNET_CONNECTION_Handle *
+try_unixpath (const char *service_name,
+             const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+#if AF_UNIX
+  struct GNUNET_CONNECTION_Handle *connection;
+  char *unixpath;
+  struct sockaddr_un s_un;
+
+  unixpath = NULL;
+  if ((GNUNET_OK == GNUNET_CONFIGURATION_get_value_filename (cfg, service_name, "UNIXPATH", &unixpath)) &&
+      (0 < strlen (unixpath)))
+  {
+    /* We have a non-NULL unixpath, need to validate it */
+    if (strlen (unixpath) >= sizeof (s_un.sun_path))
+    {
+      LOG (GNUNET_ERROR_TYPE_WARNING,
+          _("UNIXPATH `%s' too long, maximum length is %llu\n"), unixpath,
+          (unsigned long long) sizeof (s_un.sun_path));
+      unixpath = GNUNET_NETWORK_shorten_unixpath (unixpath);
+      LOG (GNUNET_ERROR_TYPE_INFO,
+          _("Using `%s' instead\n"), unixpath);
+      if (NULL == unixpath)
+       return NULL;
+    }
+    connection = GNUNET_CONNECTION_create_from_connect_to_unixpath (cfg, unixpath);
+    if (NULL != connection)
+    {
+      LOG (GNUNET_ERROR_TYPE_DEBUG, "Connected to unixpath `%s'!\n",
+          unixpath);
+      GNUNET_free (unixpath);
+      return connection;
+    }
+  }
+  GNUNET_free_non_null (unixpath);
+#endif
+  return NULL;
+}
+
+
+/**
+ * Test whether the configuration has proper values for connection
+ * (UNIXPATH || (PORT && HOSTNAME)).
+ *
+ * @param service_name name of service to connect to
+ * @param cfg configuration to use
+ * @return #GNUNET_OK if the configuration is valid, #GNUNET_SYSERR if not
+ */
+static int
+test_service_configuration (const char *service_name,
+                           const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  int ret = GNUNET_SYSERR;
+  char *hostname = NULL;
+  unsigned long long port;
+#if AF_UNIX
+  char *unixpath = NULL;
+
+  if ((GNUNET_OK == GNUNET_CONFIGURATION_get_value_filename (cfg, service_name, "UNIXPATH", &unixpath)) &&
+      (0 < strlen (unixpath)))
+    ret = GNUNET_OK;
+  GNUNET_free_non_null (unixpath);
+#endif
+
+  if ( (GNUNET_YES ==
+       GNUNET_CONFIGURATION_have_value (cfg, service_name, "PORT")) &&
+       (GNUNET_OK ==
+       GNUNET_CONFIGURATION_get_value_number (cfg, service_name, "PORT", &port)) &&
+       (port <= 65535) && (0 != port) &&
+       (GNUNET_OK ==
+       GNUNET_CONFIGURATION_get_value_string (cfg, service_name, "HOSTNAME",
+                                              &hostname)) &&
+       (0 != strlen (hostname)) )
+    ret = GNUNET_OK;
+  GNUNET_free_non_null (hostname);
+  return ret;
+}
+
+
+/**
+ * Try to connect to the service.
+ *
+ * @param service_name name of service to connect to
+ * @param cfg configuration to use
+ * @param attempt counter used to alternate between IP and UNIX domain sockets
+ * @return NULL on error
+ */
 static struct GNUNET_CONNECTION_Handle *
 static struct GNUNET_CONNECTION_Handle *
-do_connect (struct GNUNET_SCHEDULER_Handle *sched,
-            const char *service_name,
-            const struct GNUNET_CONFIGURATION_Handle *cfg)
+do_connect (const char *service_name,
+            const struct GNUNET_CONFIGURATION_Handle *cfg,
+           unsigned int attempt)
 {
 {
-  struct GNUNET_CONNECTION_Handle *sock;
+  struct GNUNET_CONNECTION_Handle *connection;
   char *hostname;
   unsigned long long port;
 
   char *hostname;
   unsigned long long port;
 
-  if ((GNUNET_OK !=
-       GNUNET_CONFIGURATION_get_value_number (cfg,
-                                              service_name,
-                                              "PORT",
-                                              &port)) ||
-      (port > 65535) ||
-      (GNUNET_OK !=
-       GNUNET_CONFIGURATION_get_value_string (cfg,
-                                              service_name,
-                                              "HOSTNAME", &hostname)))
+  /* Never use a local source if a proxy is configured */
+  if (GNUNET_YES == GNUNET_SOCKS_check_service (service_name,cfg))
+    return GNUNET_SOCKS_do_connect (service_name,cfg);
+
+  connection = NULL;
+  if (0 == (attempt % 2))
+  {
+    /* on even rounds, try UNIX first */
+    connection = try_unixpath (service_name, cfg);
+    if (NULL != connection)
+      return connection;
+  }
+  if (GNUNET_YES ==
+      GNUNET_CONFIGURATION_have_value (cfg, service_name, "PORT"))
+  {
+    if ((GNUNET_OK !=
+        GNUNET_CONFIGURATION_get_value_number (cfg, service_name, "PORT", &port))
+       || (port > 65535) ||
+       (GNUNET_OK !=
+        GNUNET_CONFIGURATION_get_value_string (cfg, service_name, "HOSTNAME",
+                                               &hostname)))
     {
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  _
-                  ("Could not determine valid hostname and port for service `%s' from configuration.\n"),
-                  service_name);
+      LOG (GNUNET_ERROR_TYPE_WARNING,
+          _
+          ("Could not determine valid hostname and port for service `%s' from configuration.\n"),
+          service_name);
       return NULL;
     }
       return NULL;
     }
-  if (0 == strlen (hostname))
+    if (0 == strlen (hostname))
     {
       GNUNET_free (hostname);
     {
       GNUNET_free (hostname);
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  _("Need a non-empty hostname for service `%s'.\n"),
-                  service_name);
+      LOG (GNUNET_ERROR_TYPE_WARNING,
+          _("Need a non-empty hostname for service `%s'.\n"), service_name);
       return NULL;
     }
       return NULL;
     }
-  sock = GNUNET_CONNECTION_create_from_connect (sched,
-                                                cfg,
-                                                hostname,
-                                                port,
-                                                GNUNET_SERVER_MAX_MESSAGE_SIZE);
+  }
+  else
+  {
+    /* unspecified means 0 (disabled) */
+    port = 0;
+    hostname = NULL;
+  }
+  if (0 == port)
+  {
+    /* if port is 0, try UNIX */
+    connection = try_unixpath (service_name, cfg);
+    if (NULL != connection)
+    {
+      GNUNET_free_non_null (hostname);
+      return connection;
+    }
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Port is 0 for service `%s', UNIXPATH did not work, returning NULL!\n",
+         service_name);
+    GNUNET_free_non_null (hostname);
+    return NULL;
+  }
+  connection = GNUNET_CONNECTION_create_from_connect (cfg, hostname, port);
   GNUNET_free (hostname);
   GNUNET_free (hostname);
-  return sock;
+  return connection;
 }
 
 
 /**
  * Get a connection with a service.
  *
 }
 
 
 /**
  * Get a connection with a service.
  *
- * @param sched scheduler to use
  * @param service_name name of the service
  * @param cfg configuration to use
  * @return NULL on error (service unknown to configuration)
  */
 struct GNUNET_CLIENT_Connection *
  * @param service_name name of the service
  * @param cfg configuration to use
  * @return NULL on error (service unknown to configuration)
  */
 struct GNUNET_CLIENT_Connection *
-GNUNET_CLIENT_connect (struct GNUNET_SCHEDULER_Handle *sched,
-                       const char *service_name,
+GNUNET_CLIENT_connect (const char *service_name,
                        const struct GNUNET_CONFIGURATION_Handle *cfg)
 {
                        const struct GNUNET_CONFIGURATION_Handle *cfg)
 {
-  struct GNUNET_CLIENT_Connection *ret;
-  struct GNUNET_CONNECTION_Handle *sock;
+  struct GNUNET_CLIENT_Connection *client;
+  struct GNUNET_CONNECTION_Handle *connection;
 
 
-  sock = do_connect (sched, service_name, cfg);
-  if (sock == NULL)
+  if (GNUNET_OK !=
+      test_service_configuration (service_name,
+                                 cfg))
     return NULL;
     return NULL;
-  ret = GNUNET_malloc (sizeof (struct GNUNET_CLIENT_Connection));
-  ret->sock = sock;
-  ret->sched = sched;
-  ret->service_name = GNUNET_strdup (service_name);
-  ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
-  ret->back_off = GNUNET_TIME_UNIT_MILLISECONDS;
-  return ret;
-}
-
-
-/**
- * Configure this connection to ignore shutdown signals.
- *
- * @param h client handle
- * @param do_ignore GNUNET_YES to ignore, GNUNET_NO to restore default
- */
-void
-GNUNET_CLIENT_ignore_shutdown (struct GNUNET_CLIENT_Connection *h,
-                              int do_ignore)
-{
-  h->ignore_shutdown = do_ignore;
-  if (h->sock != NULL)
-    GNUNET_CONNECTION_ignore_shutdown (h->sock,
-                                      do_ignore);
+  connection = do_connect (service_name, cfg, 0);
+  client = GNUNET_new (struct GNUNET_CLIENT_Connection);
+  client->first_message = GNUNET_YES;
+  client->attempts = 1;
+  client->connection = connection;
+  client->service_name = GNUNET_strdup (service_name);
+  client->cfg = cfg;
+  client->back_off = GNUNET_TIME_UNIT_MILLISECONDS;
+  return client;
 }
 
 
 }
 
 
@@ -355,116 +444,134 @@ GNUNET_CLIENT_ignore_shutdown (struct GNUNET_CLIENT_Connection *h,
  * destroyed (unless, of course, there is an error with the server in
  * which case the message may still be lost).
  *
  * destroyed (unless, of course, there is an error with the server in
  * which case the message may still be lost).
  *
- * @param finish_pending_write should a transmission already passed to the
- *          handle be completed?
- * @param sock handle to the service connection
+ * @param client handle to the service connection
  */
 void
  */
 void
-GNUNET_CLIENT_disconnect (struct GNUNET_CLIENT_Connection *sock,
-                         int finish_pending_write)
+GNUNET_CLIENT_disconnect (struct GNUNET_CLIENT_Connection *client)
 {
 {
-  GNUNET_assert (sock->sock != NULL);
-  if (sock->in_receive == GNUNET_YES)
-    {
-      GNUNET_CONNECTION_receive_cancel (sock->sock);
-      sock->in_receive = GNUNET_NO;
-    }
-  GNUNET_CONNECTION_destroy (sock->sock, finish_pending_write);
-  sock->sock = NULL;
-  if (sock->tag != NULL)
-    {
-      GNUNET_free (sock->tag);
-      sock->tag = NULL;
-    }
-  sock->receiver_handler = NULL;
-  if (sock->th != NULL)
-    GNUNET_CLIENT_notify_transmit_ready_cancel (sock->th);
-  if (sock->receive_task != GNUNET_SCHEDULER_NO_TASK)
-    {
-      GNUNET_SCHEDULER_cancel (sock->sched, sock->receive_task);
-      sock->receive_task = GNUNET_SCHEDULER_NO_TASK;
-    }
-  GNUNET_array_grow (sock->received_buf, sock->received_size, 0);
-  GNUNET_free (sock->service_name);
-  GNUNET_CONFIGURATION_destroy (sock->cfg);
-  GNUNET_free (sock);
+  if (GNUNET_YES == client->in_receive)
+  {
+    GNUNET_CONNECTION_receive_cancel (client->connection);
+    client->in_receive = GNUNET_NO;
+  }
+  if (NULL != client->th)
+  {
+    GNUNET_CLIENT_notify_transmit_ready_cancel (client->th);
+    client->th = NULL;
+  }
+  if (NULL != client->connection)
+  {
+    GNUNET_CONNECTION_destroy (client->connection);
+    client->connection = NULL;
+  }
+  if (NULL != client->receive_task)
+  {
+    GNUNET_SCHEDULER_cancel (client->receive_task);
+    client->receive_task = NULL;
+  }
+  if (NULL != client->tag)
+  {
+    GNUNET_free (client->tag);
+    client->tag = NULL;
+  }
+  client->receiver_handler = NULL;
+  GNUNET_array_grow (client->received_buf, client->received_size, 0);
+  GNUNET_free (client->service_name);
+  GNUNET_free (client);
 }
 
 
 /**
 }
 
 
 /**
- * Check if message is complete
+ * Check if message is complete.  Sets the "msg_complete" member
+ * in the client struct.
+ *
+ * @param client connection with the buffer to check
  */
 static void
  */
 static void
-check_complete (struct GNUNET_CLIENT_Connection *conn)
+check_complete (struct GNUNET_CLIENT_Connection *client)
 {
 {
-  if ((conn->received_pos >= sizeof (struct GNUNET_MessageHeader)) &&
-      (conn->received_pos >=
-       ntohs (((const struct GNUNET_MessageHeader *) conn->received_buf)->
+  if ((client->received_pos >= sizeof (struct GNUNET_MessageHeader)) &&
+      (client->received_pos >=
+       ntohs (((const struct GNUNET_MessageHeader *) client->received_buf)->
               size)))
               size)))
-    conn->msg_complete = GNUNET_YES;
+    client->msg_complete = GNUNET_YES;
 }
 
 
 /**
  * Callback function for data received from the network.  Note that
 }
 
 
 /**
  * Callback function for data received from the network.  Note that
- * both "available" and "errCode" would be 0 if the read simply timed out.
+ * both @a available and @a errCode would be 0 if the read simply timed out.
  *
  * @param cls closure
  * @param buf pointer to received data
  *
  * @param cls closure
  * @param buf pointer to received data
- * @param available number of bytes availabe in "buf",
+ * @param available number of bytes availabe in @a buf,
  *        possibly 0 (on errors)
  * @param addr address of the sender
  *        possibly 0 (on errors)
  * @param addr address of the sender
- * @param addrlen size of addr
+ * @param addrlen size of @a addr
  * @param errCode value of errno (on errors receiving)
  */
 static void
 receive_helper (void *cls,
                 const void *buf,
                 size_t available,
  * @param errCode value of errno (on errors receiving)
  */
 static void
 receive_helper (void *cls,
                 const void *buf,
                 size_t available,
-                const struct sockaddr *addr, socklen_t addrlen, int errCode)
+                const struct sockaddr *addr,
+                socklen_t addrlen,
+                int errCode)
 {
 {
-  struct GNUNET_CLIENT_Connection *conn = cls;
+  struct GNUNET_CLIENT_Connection *client = cls;
   struct GNUNET_TIME_Relative remaining;
   GNUNET_CLIENT_MessageHandler receive_handler;
   void *receive_handler_cls;
 
   struct GNUNET_TIME_Relative remaining;
   GNUNET_CLIENT_MessageHandler receive_handler;
   void *receive_handler_cls;
 
-  GNUNET_assert (conn->msg_complete == GNUNET_NO);
-  conn->in_receive = GNUNET_NO;
-  if ((available == 0) || (conn->sock == NULL) || (errCode != 0))
+  GNUNET_assert (GNUNET_NO == client->msg_complete);
+  GNUNET_assert (GNUNET_YES == client->in_receive);
+  client->in_receive = GNUNET_NO;
+  if ( (0 == available) ||
+       (NULL == client->connection) ||
+       (0 != errCode) )
+  {
+    /* signal timeout! */
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Timeout in receive_helper, available %u, client->connection %s, errCode `%s'\n",
+         (unsigned int) available,
+         NULL == client->connection ? "NULL" : "non-NULL",
+         STRERROR (errCode));
+    /* remember failure */
+    client->in_receive = GNUNET_SYSERR;
+    if (NULL != (receive_handler = client->receiver_handler))
     {
     {
-      /* signal timeout! */
-      if (NULL != (receive_handler = conn->receiver_handler))
-        {
-          receive_handler_cls = conn->receiver_handler_cls;
-          conn->receiver_handler = NULL;
-          receive_handler (receive_handler_cls, NULL);
-        }
-      return;
+      receive_handler_cls = client->receiver_handler_cls;
+      client->receiver_handler = NULL;
+      receive_handler (receive_handler_cls,
+                       NULL);
     }
     }
-
+    return;
+  }
   /* FIXME: optimize for common fast case where buf contains the
   /* FIXME: optimize for common fast case where buf contains the
-     entire message and we need no copying... */
-
+   * entire message and we need no copying... */
 
   /* slow path: append to array */
 
   /* slow path: append to array */
-  if (conn->received_size < conn->received_pos + available)
-    GNUNET_array_grow (conn->received_buf,
-                       conn->received_size, conn->received_pos + available);
-  memcpy (&conn->received_buf[conn->received_pos], buf, available);
-  conn->received_pos += available;
-  check_complete (conn);
+  if (client->received_size < client->received_pos + available)
+    GNUNET_array_grow (client->received_buf, client->received_size,
+                       client->received_pos + available);
+  memcpy (&client->received_buf[client->received_pos], buf, available);
+  client->received_pos += available;
+  check_complete (client);
   /* check for timeout */
   /* check for timeout */
-  remaining = GNUNET_TIME_absolute_get_remaining (conn->receive_timeout);
-  if (remaining.value == 0)
+  remaining = GNUNET_TIME_absolute_get_remaining (client->receive_timeout);
+  if (0 == remaining.rel_value_us)
+  {
+    /* signal timeout! */
+    if (NULL != (receive_handler = client->receiver_handler))
     {
     {
-      /* signal timeout! */
-      conn->receiver_handler (conn->receiver_handler_cls, NULL);
-      return;
+      client->receiver_handler = NULL;
+      receive_handler (client->receiver_handler_cls, NULL);
     }
     }
+    return;
+  }
   /* back to receive -- either for more data or to call callback! */
   /* back to receive -- either for more data or to call callback! */
-  GNUNET_CLIENT_receive (conn,
-                         conn->receiver_handler,
-                         conn->receiver_handler_cls, remaining);
+  GNUNET_CLIENT_receive (client, client->receiver_handler,
+                         client->receiver_handler_cls, remaining);
 }
 
 
 }
 
 
@@ -475,33 +582,44 @@ receive_helper (void *cls,
  * @param tc scheduler context
  */
 static void
  * @param tc scheduler context
  */
 static void
-receive_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+receive_task (void *cls,
+              const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
 {
-  struct GNUNET_CLIENT_Connection *sock = cls;
-  GNUNET_CLIENT_MessageHandler handler = sock->receiver_handler;
+  struct GNUNET_CLIENT_Connection *client = cls;
+  GNUNET_CLIENT_MessageHandler handler = client->receiver_handler;
   const struct GNUNET_MessageHeader *cmsg =
   const struct GNUNET_MessageHeader *cmsg =
-    (const struct GNUNET_MessageHeader *) sock->received_buf;
-  void *handler_cls = sock->receiver_handler_cls;
+      (const struct GNUNET_MessageHeader *) client->received_buf;
+  void *handler_cls = client->receiver_handler_cls;
   uint16_t msize = ntohs (cmsg->size);
   uint16_t msize = ntohs (cmsg->size);
-  char mbuf[msize];
+  char mbuf[msize] GNUNET_ALIGN;
   struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) mbuf;
 
   struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) mbuf;
 
-#if DEBUG_CLIENT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Received message of size %u\n",
-             msize);
-#endif
-  sock->receive_task = GNUNET_SCHEDULER_NO_TASK;
-  GNUNET_assert (GNUNET_YES == sock->msg_complete);
-  GNUNET_assert (sock->received_pos >= msize);
+  client->receive_task = NULL;
+  if ( (GNUNET_SYSERR == client->in_receive) &&
+       (GNUNET_YES != client->msg_complete) )
+  {
+    /* Connection failure, signal to caller! */
+    client->receiver_handler = NULL;
+    if (NULL != handler)
+      handler (handler_cls,
+               NULL);
+    return;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Received message of type %u and size %u from %s service.\n",
+       ntohs (cmsg->type),
+       msize,
+       client->service_name);
+  GNUNET_assert (GNUNET_YES == client->msg_complete);
+  GNUNET_assert (client->received_pos >= msize);
   memcpy (msg, cmsg, msize);
   memcpy (msg, cmsg, msize);
-  memmove (sock->received_buf,
-           &sock->received_buf[msize], sock->received_pos - msize);
-  sock->received_pos -= msize;
-  sock->msg_complete = GNUNET_NO;
-  sock->receiver_handler = NULL;
-  check_complete (sock);
-  if (handler != NULL)
+  memmove (client->received_buf, &client->received_buf[msize],
+           client->received_pos - msize);
+  client->received_pos -= msize;
+  client->msg_complete = GNUNET_NO;
+  client->receiver_handler = NULL;
+  check_complete (client);
+  if (NULL != handler)
     handler (handler_cls, msg);
 }
 
     handler (handler_cls, msg);
 }
 
@@ -509,172 +627,437 @@ receive_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 /**
  * Read from the service.
  *
 /**
  * Read from the service.
  *
- * @param sock the service
+ * @param client the service
  * @param handler function to call with the message
  * @param handler function to call with the message
- * @param handler_cls closure for handler
+ * @param handler_cls closure for @a handler
  * @param timeout how long to wait until timing out
  */
 void
  * @param timeout how long to wait until timing out
  */
 void
-GNUNET_CLIENT_receive (struct GNUNET_CLIENT_Connection *sock,
+GNUNET_CLIENT_receive (struct GNUNET_CLIENT_Connection *client,
                        GNUNET_CLIENT_MessageHandler handler,
                        GNUNET_CLIENT_MessageHandler handler,
-                       void *handler_cls, struct GNUNET_TIME_Relative timeout)
+                      void *handler_cls,
+                       struct GNUNET_TIME_Relative timeout)
 {
 {
-  if (sock->sock == NULL)
-    {
-      /* already disconnected, fail instantly! */
-      GNUNET_break (0);         /* this should not happen in well-written code! */
-      handler (handler_cls, NULL);
-      return;
-    }
-  sock->receiver_handler = handler;
-  sock->receiver_handler_cls = handler_cls;
-  sock->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout);
-  if (GNUNET_YES == sock->msg_complete)
-    {
-      sock->receive_task = GNUNET_SCHEDULER_add_after (sock->sched,
-                                                       GNUNET_SCHEDULER_NO_TASK,
-                                                       &receive_task, sock);
-    }
-  else
-    {
-      GNUNET_assert (sock->in_receive == GNUNET_NO);
-      sock->in_receive = GNUNET_YES;
-      GNUNET_CONNECTION_receive (sock->sock,
-                                 GNUNET_SERVER_MAX_MESSAGE_SIZE,
-                                 timeout, &receive_helper, sock);
-    }
+  if (NULL == client->connection)
+  {
+    /* already disconnected, fail instantly! */
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               "Client API violation for service `%s'\n",
+               client->service_name);
+    GNUNET_break (0);           /* this should not happen in well-written code! */
+    if (NULL != handler)
+      handler (handler_cls,
+               NULL);
+    return;
+  }
+  client->receiver_handler = handler;
+  client->receiver_handler_cls = handler_cls;
+  client->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout);
+  if ( (GNUNET_YES == client->msg_complete) ||
+       (GNUNET_SYSERR == client->in_receive) )
+  {
+    GNUNET_assert (NULL == client->receive_task);
+    client->receive_task = GNUNET_SCHEDULER_add_now (&receive_task, client);
+    return;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "calling GNUNET_CONNECTION_receive\n");
+  GNUNET_assert (GNUNET_NO == client->in_receive);
+  client->in_receive = GNUNET_YES;
+  GNUNET_CONNECTION_receive (client->connection,
+                             GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
+                             timeout,
+                             &receive_helper,
+                             client);
 }
 
 
 /**
 }
 
 
 /**
- * Report service unavailable.
+ * Handle for a test to check if a service is running.
+ */
+struct GNUNET_CLIENT_TestHandle
+{
+  /**
+   * Function to call with the result of the test.
+   */
+  GNUNET_CLIENT_TestResultCallback cb;
+
+  /**
+   * Closure for @e cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Client connection we are using for the test, if any.
+   */
+  struct GNUNET_CLIENT_Connection *client;
+
+  /**
+   * Handle for the transmission request, if any.
+   */
+  struct GNUNET_CLIENT_TransmitHandle *th;
+
+  /**
+   * Deadline for calling @e cb.
+   */
+  struct GNUNET_TIME_Absolute test_deadline;
+
+  /**
+   * ID of task used for asynchronous operations.
+   */
+  struct GNUNET_SCHEDULER_Task * task;
+
+  /**
+   * Final result to report back (once known).
+   */
+  int result;
+};
+
+
+/**
+ * Abort testing for service.
+ *
+ * @param th test handle
+ */
+void
+GNUNET_CLIENT_service_test_cancel (struct GNUNET_CLIENT_TestHandle *th)
+{
+  if (NULL != th->th)
+  {
+    GNUNET_CLIENT_notify_transmit_ready_cancel (th->th);
+    th->th = NULL;
+  }
+  if (NULL != th->client)
+  {
+    GNUNET_CLIENT_disconnect (th->client);
+    th->client = NULL;
+  }
+  if (NULL != th->task)
+  {
+    GNUNET_SCHEDULER_cancel (th->task);
+    th->task = NULL;
+  }
+  GNUNET_free (th);
+}
+
+
+/**
+ * Task that reports back the result by calling the callback
+ * and then cleans up.
+ *
+ * @param cls the `struct GNUNET_CLIENT_TestHandle`
+ * @param tc scheduler context
  */
 static void
  */
 static void
-service_test_error (struct GNUNET_SCHEDULER_Handle *s,
-                    GNUNET_SCHEDULER_Task task, void *task_cls)
+report_result (void *cls,
+              const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
 {
-  GNUNET_SCHEDULER_add_continuation (s,
-                                     task,
-                                     task_cls,
-                                     GNUNET_SCHEDULER_REASON_TIMEOUT);
+  struct GNUNET_CLIENT_TestHandle *th = cls;
+
+  th->task = NULL;
+  th->cb (th->cb_cls, th->result);
+  GNUNET_CLIENT_service_test_cancel (th);
+}
+
+
+/**
+ * Report service test result asynchronously back to callback.
+ *
+ * @param th test handle with the result and the callback
+ * @param result result to report
+ */
+static void
+service_test_report (struct GNUNET_CLIENT_TestHandle *th,
+                    int result)
+{
+  th->result = result;
+  th->task = GNUNET_SCHEDULER_add_now (&report_result,
+                                      th);
 }
 
 
 /**
  * Receive confirmation from test, service is up.
  *
 }
 
 
 /**
  * Receive confirmation from test, service is up.
  *
- * @param cls closure
+ * @param cls closure with the `struct GNUNET_CLIENT_TestHandle`
  * @param msg message received, NULL on timeout or fatal error
  */
 static void
  * @param msg message received, NULL on timeout or fatal error
  */
 static void
-confirm_handler (void *cls, const struct GNUNET_MessageHeader *msg)
+confirm_handler (void *cls,
+                 const struct GNUNET_MessageHeader *msg)
 {
 {
-  struct GNUNET_CLIENT_Connection *conn = cls;
+  struct GNUNET_CLIENT_TestHandle *th = cls;
+
   /* We may want to consider looking at the reply in more
   /* We may want to consider looking at the reply in more
-     detail in the future, for example, is this the
-     correct service? FIXME! */
-  if (msg != NULL)
-    {
-#if DEBUG_CLIENT
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Received confirmation that service is running.\n");
-#endif
-      GNUNET_SCHEDULER_add_continuation (conn->sched,
-                                         conn->test_cb,
-                                         conn->test_cb_cls,
-                                         GNUNET_SCHEDULER_REASON_PREREQ_DONE);
-    }
+   * detail in the future, for example, is this the
+   * correct service? FIXME! */
+  if (NULL != msg)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Received confirmation that service is running.\n");
+    service_test_report (th, GNUNET_YES);
+  }
   else
   else
-    {
-      service_test_error (conn->sched, conn->test_cb, conn->test_cb_cls);
-    }
-  GNUNET_CLIENT_disconnect (conn, GNUNET_NO);
+  {
+    service_test_report (th, GNUNET_NO);
+  }
 }
 
 
 }
 
 
+/**
+ * Send the 'TEST' message to the service.  If successful, prepare to
+ * receive the reply.
+ *
+ * @param cls the `struct GNUNET_CLIENT_TestHandle` of the test
+ * @param size number of bytes available in @a buf
+ * @param buf where to write the message
+ * @return number of bytes written to @a buf
+ */
 static size_t
 write_test (void *cls, size_t size, void *buf)
 {
 static size_t
 write_test (void *cls, size_t size, void *buf)
 {
-  struct GNUNET_CLIENT_Connection *conn = cls;
+  struct GNUNET_CLIENT_TestHandle *th = cls;
   struct GNUNET_MessageHeader *msg;
 
   struct GNUNET_MessageHeader *msg;
 
+  th->th = NULL;
   if (size < sizeof (struct GNUNET_MessageHeader))
   if (size < sizeof (struct GNUNET_MessageHeader))
-    {
-#if DEBUG_CLIENT
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  _("Failure to transmit TEST request.\n"));
-#endif
-      service_test_error (conn->sched, conn->test_cb, conn->test_cb_cls);
-      GNUNET_CLIENT_disconnect (conn, GNUNET_NO);
-      return 0;                 /* client disconnected */
-    }
-#if DEBUG_CLIENT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Transmitting `%s' request.\n", "TEST");
-#endif
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+        "Failed to transmit TEST request.\n");
+    service_test_report (th, GNUNET_NO);
+    return 0;                   /* client disconnected */
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Transmitting `%s' request.\n",
+       "TEST");
   msg = (struct GNUNET_MessageHeader *) buf;
   msg->type = htons (GNUNET_MESSAGE_TYPE_TEST);
   msg->size = htons (sizeof (struct GNUNET_MessageHeader));
   msg = (struct GNUNET_MessageHeader *) buf;
   msg->type = htons (GNUNET_MESSAGE_TYPE_TEST);
   msg->size = htons (sizeof (struct GNUNET_MessageHeader));
-  GNUNET_CLIENT_receive (conn, 
-                        &confirm_handler, 
-                        conn, 
-                        GNUNET_TIME_absolute_get_remaining (conn->test_deadline));
+  GNUNET_CLIENT_receive (th->client,
+                        &confirm_handler, th,
+                         GNUNET_TIME_absolute_get_remaining
+                         (th->test_deadline));
   return sizeof (struct GNUNET_MessageHeader);
 }
 
 
 /**
   return sizeof (struct GNUNET_MessageHeader);
 }
 
 
 /**
- * Wait until the service is running.
+ * Test if the service is running.  If we are given a UNIXPATH or a
+ * local address, we do this NOT by trying to connect to the service,
+ * but by trying to BIND to the same port.  If the BIND fails, we know
+ * the service is running.
  *
  *
- * @param sched scheduler to use
  * @param service name of the service to wait for
  * @param cfg configuration to use
  * @param service name of the service to wait for
  * @param cfg configuration to use
- * @param timeout how long to wait at most in ms
- * @param task task to run if service is running
- *        (reason will be "PREREQ_DONE" (service running)
- *         or "TIMEOUT" (service not known to be running))
- * @param task_cls closure for task
+ * @param timeout how long to wait at most
+ * @param cb function to call with the result
+ * @param cb_cls closure for @a cb
+ * @return handle to cancel the test
  */
  */
-void
-GNUNET_CLIENT_service_test (struct GNUNET_SCHEDULER_Handle *sched,
-                            const char *service,
+struct GNUNET_CLIENT_TestHandle *
+GNUNET_CLIENT_service_test (const char *service,
                             const struct GNUNET_CONFIGURATION_Handle *cfg,
                             struct GNUNET_TIME_Relative timeout,
                             const struct GNUNET_CONFIGURATION_Handle *cfg,
                             struct GNUNET_TIME_Relative timeout,
-                            GNUNET_SCHEDULER_Task task, void *task_cls)
+                            GNUNET_CLIENT_TestResultCallback cb,
+                           void *cb_cls)
 {
 {
-  struct GNUNET_CLIENT_Connection *conn;
+  struct GNUNET_CLIENT_TestHandle *th;
+  char *hostname;
+  unsigned long long port;
+  struct GNUNET_NETWORK_Handle *sock;
+
+  th = GNUNET_new (struct GNUNET_CLIENT_TestHandle);
+  th->cb = cb;
+  th->cb_cls = cb_cls;
+  th->test_deadline = GNUNET_TIME_relative_to_absolute (timeout);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Testing if service `%s' is running.\n",
+       service);
+#ifdef AF_UNIX
+  {
+    /* probe UNIX support */
+    struct sockaddr_un s_un;
+    char *unixpath;
+    int abstract;
+
+    unixpath = NULL;
+    if ((GNUNET_OK ==
+        GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                                 service,
+                                                 "UNIXPATH",
+                                                 &unixpath)) &&
+       (0 < strlen (unixpath)))  /* We have a non-NULL unixpath, does that mean it's valid? */
+    {
+      if (strlen (unixpath) >= sizeof (s_un.sun_path))
+      {
+        LOG (GNUNET_ERROR_TYPE_WARNING,
+             _("UNIXPATH `%s' too long, maximum length is %llu\n"),
+            unixpath,
+             (unsigned long long) sizeof (s_un.sun_path));
+       unixpath = GNUNET_NETWORK_shorten_unixpath (unixpath);
+        LOG (GNUNET_ERROR_TYPE_INFO,
+             _("Using `%s' instead\n"), unixpath);
+      }
+    }
+#ifdef LINUX
+    abstract = GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                                     "TESTING",
+                                                     "USE_ABSTRACT_SOCKETS");
+#else
+    abstract = GNUNET_NO;
+#endif
+    if ((NULL != unixpath) && (GNUNET_YES != abstract))
+    {
+      if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (unixpath))
+        GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+                                  "mkdir", unixpath);
+    }
+    if (NULL != unixpath)
+    {
+      sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
+      if (NULL != sock)
+      {
+       memset (&s_un, 0, sizeof (s_un));
+       s_un.sun_family = AF_UNIX;
+        strncpy (s_un.sun_path, unixpath, sizeof (s_un.sun_path) - 1);
+        if (GNUNET_YES == abstract)
+          s_un.sun_path[0] = '\0';
+#if HAVE_SOCKADDR_IN_SIN_LEN
+        s_un.sun_len = (u_char) sizeof (struct sockaddr_un);
+#endif
+       if (GNUNET_OK !=
+           GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) &s_un,
+                                       sizeof (struct sockaddr_un)))
+        {
+         /* failed to bind => service must be running */
+         GNUNET_free (unixpath);
+         (void) GNUNET_NETWORK_socket_close (sock);
+         service_test_report (th, GNUNET_YES);
+         return th;
+       }
+       (void) GNUNET_NETWORK_socket_close (sock);
+        /* let's try IP */
+      }
+    }
+    GNUNET_free_non_null (unixpath);
+  }
+#endif
 
 
-#if DEBUG_CLIENT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Testing if service `%s' is running.\n", service);
+  hostname = NULL;
+  if ((GNUNET_OK !=
+       GNUNET_CONFIGURATION_get_value_number (cfg, service, "PORT", &port)) ||
+      (port > 65535) ||
+      (GNUNET_OK !=
+       GNUNET_CONFIGURATION_get_value_string (cfg, service, "HOSTNAME",
+                                              &hostname)))
+  {
+    /* UNIXPATH failed (if possible) AND IP failed => error */
+    service_test_report (th, GNUNET_SYSERR);
+    return th;
+  }
+
+  if (0 == strcmp ("localhost", hostname)
+#if !LINUX
+      && 0
 #endif
 #endif
-  conn = GNUNET_CLIENT_connect (sched, service, cfg);
-  if (conn == NULL)
+      )
+  {
+    /* can test using 'bind' */
+    struct sockaddr_in s_in;
+
+    memset (&s_in, 0, sizeof (s_in));
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    s_in.sin_len = sizeof (struct sockaddr_in);
+#endif
+    s_in.sin_family = AF_INET;
+    s_in.sin_port = htons (port);
+
+    sock = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0);
+    if (NULL != sock)
     {
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  _
-                  ("Could not connect to service `%s', must not be running.\n"),
-                  service);
-      service_test_error (sched, task, task_cls);
-      return;
+      if (GNUNET_OK !=
+          GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) &s_in,
+                                      sizeof (s_in)))
+      {
+        /* failed to bind => service must be running */
+        GNUNET_free (hostname);
+        (void) GNUNET_NETWORK_socket_close (sock);
+        service_test_report (th, GNUNET_YES);
+        return th;
+      }
+      (void) GNUNET_NETWORK_socket_close (sock);
     }
     }
-  conn->test_cb = task;
-  conn->test_cb_cls = task_cls;
-  conn->test_deadline = GNUNET_TIME_relative_to_absolute (timeout);
-
-  if (NULL == GNUNET_CLIENT_notify_transmit_ready (conn,
-                                                  sizeof (struct GNUNET_MessageHeader),
-                                                  timeout,
-                                                  GNUNET_YES,
-                                                  &write_test, conn))  
+  }
+
+  if (0 == strcmp ("ip6-localhost", hostname)
+#if !LINUX
+      && 0
+#endif
+      )
+  {
+    /* can test using 'bind' */
+    struct sockaddr_in6 s_in6;
+
+    memset (&s_in6, 0, sizeof (s_in6));
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    s_in6.sin6_len = sizeof (struct sockaddr_in6);
+#endif
+    s_in6.sin6_family = AF_INET6;
+    s_in6.sin6_port = htons (port);
+
+    sock = GNUNET_NETWORK_socket_create (AF_INET6, SOCK_STREAM, 0);
+    if (NULL != sock)
     {
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  _("Failure to transmit request to service `%s'\n"),
-                  service);
-      service_test_error (sched, task, task_cls);
-      GNUNET_CLIENT_disconnect (conn, GNUNET_NO);
-      return;
+      if (GNUNET_OK !=
+          GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) &s_in6,
+                                      sizeof (s_in6)))
+      {
+        /* failed to bind => service must be running */
+        GNUNET_free (hostname);
+        (void) GNUNET_NETWORK_socket_close (sock);
+        service_test_report (th, GNUNET_YES);
+        return th;
+      }
+      (void) GNUNET_NETWORK_socket_close (sock);
     }
     }
+  }
+
+  if (((0 == strcmp ("localhost", hostname)) ||
+       (0 == strcmp ("ip6-localhost", hostname)))
+#if !LINUX
+      && 0
+#endif
+      )
+  {
+    /* all binds succeeded => claim service not running right now */
+    GNUNET_free_non_null (hostname);
+    service_test_report (th, GNUNET_NO);
+    return th;
+  }
+  GNUNET_free_non_null (hostname);
+
+  /* non-localhost, try 'connect' method */
+  th->client = GNUNET_CLIENT_connect (service, cfg);
+  if (NULL == th->client)
+  {
+    LOG (GNUNET_ERROR_TYPE_INFO,
+         _("Could not connect to service `%s', configuration broken.\n"),
+         service);
+    service_test_report (th, GNUNET_SYSERR);
+    return th;
+  }
+  th->th = GNUNET_CLIENT_notify_transmit_ready (th->client,
+                                               sizeof (struct GNUNET_MessageHeader),
+                                               timeout, GNUNET_YES,
+                                               &write_test, th);
+  if (NULL == th->th)
+  {
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         _("Failure to transmit request to service `%s'\n"), service);
+    service_test_report (th, GNUNET_SYSERR);
+    return th;
+  }
+  return th;
 }
 
 
 }
 
 
@@ -683,51 +1066,72 @@ GNUNET_CLIENT_service_test (struct GNUNET_SCHEDULER_Handle *sched,
  * a transmission request.  Either pass it on to our
  * user or, if possible, retry.
  *
  * a transmission request.  Either pass it on to our
  * user or, if possible, retry.
  *
- * @param cls our "struct GNUNET_CLIENT_TransmissionHandle"
+ * @param cls our `struct GNUNET_CLIENT_TransmissionHandle`
  * @param size number of bytes available for transmission
  * @param buf where to write them
  * @param size number of bytes available for transmission
  * @param buf where to write them
- * @return number of bytes written to buf
+ * @return number of bytes written to @a buf
  */
  */
-static size_t client_notify (void *cls, size_t size, void *buf);
+static size_t
+client_notify (void *cls, size_t size, void *buf);
 
 
 /**
  * This task is run if we should re-try connection to the
  * service after a while.
  *
 
 
 /**
  * This task is run if we should re-try connection to the
  * service after a while.
  *
- * @param cls our "struct GNUNET_CLIENT_TransmitHandle" of the request
+ * @param cls our `struct GNUNET_CLIENT_TransmitHandle` of the request
  * @param tc unused
  */
 static void
 client_delayed_retry (void *cls,
  * @param tc unused
  */
 static void
 client_delayed_retry (void *cls,
-                      const struct GNUNET_SCHEDULER_TaskContext *tc)
+                     const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct GNUNET_CLIENT_TransmitHandle *th = cls;
 {
   struct GNUNET_CLIENT_TransmitHandle *th = cls;
+  struct GNUNET_TIME_Relative delay;
 
 
-  th->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
+  th->reconnect_task = NULL;
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
-    {
-#if DEBUG_CLIENT
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Transmission failed due to shutdown.\n");
-#endif
-      th->sock->th = NULL;
-      th->notify (th->notify_cls, 0, NULL);
-      GNUNET_free (th);
-      return;
-    }
-  th->th = GNUNET_CONNECTION_notify_transmit_ready (th->sock->sock,
-                                                    th->size,
-                                                    GNUNET_TIME_absolute_get_remaining
-                                                    (th->timeout),
-                                                    &client_notify, th);
-  if (th->th == NULL)
-    {
-      GNUNET_break (0);
-      th->notify (th->notify_cls, 0, NULL);
-      GNUNET_free (th);
-      return;
-    }
+  {
+    /* give up, was shutdown */
+    th->client->th = NULL;
+    th->notify (th->notify_cls, 0, NULL);
+    GNUNET_free (th);
+    return;
+  }
+  th->client->connection =
+    do_connect (th->client->service_name,
+               th->client->cfg,
+               th->client->attempts++);
+  th->client->first_message = GNUNET_YES;
+  if (NULL == th->client->connection)
+  {
+    /* could happen if we're out of sockets */
+    delay = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (th->timeout),
+                                      th->client->back_off);
+    th->client->back_off = GNUNET_TIME_STD_BACKOFF (th->client->back_off);
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Transmission failed %u times, trying again in %s.\n",
+         MAX_ATTEMPTS - th->attempts_left,
+         GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES));
+    GNUNET_assert (NULL == th->th);
+    GNUNET_assert (NULL == th->reconnect_task);
+    th->reconnect_task =
+        GNUNET_SCHEDULER_add_delayed (delay, &client_delayed_retry, th);
+    return;
+  }
+  th->th =
+      GNUNET_CONNECTION_notify_transmit_ready (th->client->connection, th->size,
+                                               GNUNET_TIME_absolute_get_remaining
+                                               (th->timeout), &client_notify,
+                                               th);
+  if (NULL == th->th)
+  {
+    GNUNET_break (0);
+    th->client->th = NULL;
+    th->notify (th->notify_cls, 0, NULL);
+    GNUNET_free (th);
+    return;
+  }
 }
 
 
 }
 
 
@@ -735,75 +1139,90 @@ client_delayed_retry (void *cls,
  * Connection notifies us about failure or success of a transmission
  * request.  Either pass it on to our user or, if possible, retry.
  *
  * Connection notifies us about failure or success of a transmission
  * request.  Either pass it on to our user or, if possible, retry.
  *
- * @param cls our "struct GNUNET_CLIENT_TransmissionHandle"
+ * @param cls our `struct GNUNET_CLIENT_TransmissionHandle`
  * @param size number of bytes available for transmission
  * @param buf where to write them
  * @param size number of bytes available for transmission
  * @param buf where to write them
- * @return number of bytes written to buf
+ * @return number of bytes written to @a buf
  */
 static size_t
  */
 static size_t
-client_notify (void *cls, size_t size, void *buf)
+client_notify (void *cls,
+               size_t size,
+               void *buf)
 {
   struct GNUNET_CLIENT_TransmitHandle *th = cls;
 {
   struct GNUNET_CLIENT_TransmitHandle *th = cls;
+  struct GNUNET_CLIENT_Connection *client = th->client;
   size_t ret;
   struct GNUNET_TIME_Relative delay;
 
   size_t ret;
   struct GNUNET_TIME_Relative delay;
 
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "client_notify is running\n");
   th->th = NULL;
   th->th = NULL;
-  th->sock->th = NULL;
-  if (buf == NULL)
+  client->th = NULL;
+  if (NULL == buf)
+  {
+    delay = GNUNET_TIME_absolute_get_remaining (th->timeout);
+    delay.rel_value_us /= 2;
+    if ( (GNUNET_YES != th->auto_retry) ||
+         (0 == --th->attempts_left) ||
+         (delay.rel_value_us < 1)||
+         (0 != (GNUNET_SCHEDULER_get_reason() & GNUNET_SCHEDULER_REASON_SHUTDOWN)))
     {
     {
-      delay = GNUNET_TIME_absolute_get_remaining (th->timeout);
-      delay.value /= 2;
-      if ( (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & GNUNET_SCHEDULER_get_reason (th->sock->sched))) ||
-          (GNUNET_YES != th->auto_retry) ||
-          (0 == --th->attempts_left) || 
-          (delay.value < 1) )
-        {
-#if DEBUG_CLIENT
-          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                      "Transmission failed %u times, giving up.\n",
-                      MAX_ATTEMPTS - th->attempts_left);
-#endif
-          GNUNET_break (0 == th->notify (th->notify_cls, 0, NULL));
-          GNUNET_free (th);
-          return 0;
-        }
-      /* auto-retry */
-      GNUNET_CONNECTION_destroy (th->sock->sock, GNUNET_NO);
-      th->sock->sock = do_connect (th->sock->sched,
-                                   th->sock->service_name, th->sock->cfg);
-      GNUNET_assert (NULL != th->sock->sock);
-      GNUNET_CONNECTION_ignore_shutdown (th->sock->sock,
-                                        th->sock->ignore_shutdown);
-      delay = GNUNET_TIME_relative_min (delay, th->sock->back_off);
-      th->sock->back_off 
-         = GNUNET_TIME_relative_min (GNUNET_TIME_relative_multiply (th->sock->back_off, 2),
-                                   GNUNET_TIME_UNIT_SECONDS);
-#if DEBUG_CLIENT
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Transmission failed %u times, trying again in %llums.\n",
-                  MAX_ATTEMPTS - th->attempts_left,
-                  (unsigned long long) delay.value);
-#endif
-      th->reconnect_task = GNUNET_SCHEDULER_add_delayed (th->sock->sched,
-                                                         delay,
-                                                         &client_delayed_retry,
-                                                         th);
-      th->sock->th = th;
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+           "Transmission failed %u times, giving up.\n",
+           MAX_ATTEMPTS - th->attempts_left);
+      GNUNET_break (0 ==
+                    th->notify (th->notify_cls, 0, NULL));
+      GNUNET_free (th);
       return 0;
     }
       return 0;
     }
+    /* auto-retry */
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Failed to connect to `%s', automatically trying again.\n",
+         client->service_name);
+    if (GNUNET_YES == client->in_receive)
+    {
+      GNUNET_CONNECTION_receive_cancel (client->connection);
+      client->in_receive = GNUNET_NO;
+    }
+    GNUNET_CONNECTION_destroy (client->connection);
+    client->connection = NULL;
+    delay = GNUNET_TIME_relative_min (delay, client->back_off);
+    client->back_off =
+        GNUNET_TIME_relative_min (GNUNET_TIME_relative_multiply
+                                  (client->back_off, 2),
+                                  GNUNET_TIME_UNIT_SECONDS);
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Transmission failed %u times, trying again in %s.\n",
+         MAX_ATTEMPTS - th->attempts_left,
+         GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES));
+    client->th = th;
+    GNUNET_assert (NULL == th->reconnect_task);
+    GNUNET_assert (NULL == th->th);
+    th->reconnect_task =
+        GNUNET_SCHEDULER_add_delayed (delay, &client_delayed_retry, th);
+    return 0;
+  }
   GNUNET_assert (size >= th->size);
   ret = th->notify (th->notify_cls, size, buf);
   GNUNET_free (th);
   GNUNET_assert (size >= th->size);
   ret = th->notify (th->notify_cls, size, buf);
   GNUNET_free (th);
+  if (sizeof (struct GNUNET_MessageHeader) <= ret)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Transmitting message of type %u and size %u to %s service.\n",
+         ntohs (((struct GNUNET_MessageHeader *) buf)->type),
+         ntohs (((struct GNUNET_MessageHeader *) buf)->size),
+         client->service_name);
+  }
   return ret;
 }
 
 
 /**
  * Ask the client to call us once the specified number of bytes
   return ret;
 }
 
 
 /**
  * Ask the client to call us once the specified number of bytes
- * are free in the transmission buffer.  May call the notify
- * method immediately if enough space is available.
+ * are free in the transmission buffer.  Will never call the @a notify
+ * callback in this task, but always first go into the scheduler.
  *
  *
- * @param sock connection to the service
+ * @param client connection to the service
  * @param size number of bytes to send
  * @param timeout after how long should we give up (and call
  *        notify with buf NULL and size 0)?
  * @param size number of bytes to send
  * @param timeout after how long should we give up (and call
  *        notify with buf NULL and size 0)?
@@ -813,102 +1232,123 @@ client_notify (void *cls, size_t size, void *buf)
  *        if the caller does not care about temporary connection errors,
  *        for example because the protocol is stateless
  * @param notify function to call
  *        if the caller does not care about temporary connection errors,
  *        for example because the protocol is stateless
  * @param notify function to call
- * @param notify_cls closure for notify
+ * @param notify_cls closure for @a notify
  * @return NULL if our buffer will never hold size bytes,
  *         a handle if the notify callback was queued (can be used to cancel)
  */
 struct GNUNET_CLIENT_TransmitHandle *
  * @return NULL if our buffer will never hold size bytes,
  *         a handle if the notify callback was queued (can be used to cancel)
  */
 struct GNUNET_CLIENT_TransmitHandle *
-GNUNET_CLIENT_notify_transmit_ready (struct GNUNET_CLIENT_Connection *sock,
+GNUNET_CLIENT_notify_transmit_ready (struct GNUNET_CLIENT_Connection *client,
                                      size_t size,
                                      struct GNUNET_TIME_Relative timeout,
                                      int auto_retry,
                                      size_t size,
                                      struct GNUNET_TIME_Relative timeout,
                                      int auto_retry,
-                                     GNUNET_CONNECTION_TransmitReadyNotify
-                                     notify, void *notify_cls)
+                                     GNUNET_CONNECTION_TransmitReadyNotify notify,
+                                    void *notify_cls)
 {
   struct GNUNET_CLIENT_TransmitHandle *th;
 
 {
   struct GNUNET_CLIENT_TransmitHandle *th;
 
-  if (NULL != sock->th)
+  if (NULL != client->th)
+  {
+    /* If this breaks, you most likley called this function twice without waiting
+     * for completion or canceling the request */
+    GNUNET_assert (0);
     return NULL;
     return NULL;
-  th = GNUNET_malloc (sizeof (struct GNUNET_CLIENT_TransmitHandle));
-  th->sock = sock;
+  }
+  th = GNUNET_new (struct GNUNET_CLIENT_TransmitHandle);
+  th->client = client;
   th->size = size;
   th->timeout = GNUNET_TIME_relative_to_absolute (timeout);
   th->size = size;
   th->timeout = GNUNET_TIME_relative_to_absolute (timeout);
-  th->auto_retry = auto_retry;
+  /* always auto-retry on first message to service */
+  th->auto_retry = (GNUNET_YES == client->first_message) ? GNUNET_YES : auto_retry;
+  client->first_message = GNUNET_NO;
   th->notify = notify;
   th->notify_cls = notify_cls;
   th->attempts_left = MAX_ATTEMPTS;
   th->notify = notify;
   th->notify_cls = notify_cls;
   th->attempts_left = MAX_ATTEMPTS;
-  th->th = GNUNET_CONNECTION_notify_transmit_ready (sock->sock,
-                                                    size,
-                                                    timeout,
-                                                    &client_notify, th);
-  if (NULL == th->th)
+  client->th = th;
+  if (NULL == client->connection)
+  {
+    GNUNET_assert (NULL == th->th);
+    GNUNET_assert (NULL == th->reconnect_task);
+    th->reconnect_task =
+        GNUNET_SCHEDULER_add_delayed (client->back_off,
+                                      &client_delayed_retry,
+                                      th);
+  }
+  else
+  {
+    th->th = GNUNET_CONNECTION_notify_transmit_ready (client->connection,
+                                                      size,
+                                                      timeout,
+                                                      &client_notify,
+                                                      th);
+    if (NULL == th->th)
     {
       GNUNET_break (0);
       GNUNET_free (th);
     {
       GNUNET_break (0);
       GNUNET_free (th);
+      client->th = NULL;
       return NULL;
     }
       return NULL;
     }
-  sock->th = th;
+  }
   return th;
 }
 
 
 /**
  * Cancel a request for notification.
   return th;
 }
 
 
 /**
  * Cancel a request for notification.
- * 
+ *
  * @param th handle from the original request.
  */
 void
  * @param th handle from the original request.
  */
 void
-GNUNET_CLIENT_notify_transmit_ready_cancel (struct
-                                            GNUNET_CLIENT_TransmitHandle *th)
+GNUNET_CLIENT_notify_transmit_ready_cancel (struct GNUNET_CLIENT_TransmitHandle *th)
 {
 {
-  if (th->reconnect_task != GNUNET_SCHEDULER_NO_TASK)
-    {
-      GNUNET_break (NULL == th->th);
-      GNUNET_SCHEDULER_cancel (th->sock->sched, th->reconnect_task);
-      th->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
-    }
+  if (NULL != th->reconnect_task)
+  {
+    GNUNET_assert (NULL == th->th);
+    GNUNET_SCHEDULER_cancel (th->reconnect_task);
+    th->reconnect_task = NULL;
+  }
   else
   else
-    {
-      GNUNET_break (NULL != th->th);
-      GNUNET_CONNECTION_notify_transmit_ready_cancel (th->th);
-    }
-  th->sock->th = NULL;
+  {
+    GNUNET_assert (NULL != th->th);
+    GNUNET_CONNECTION_notify_transmit_ready_cancel (th->th);
+  }
+  th->client->th = NULL;
   GNUNET_free (th);
 }
 
 
 /**
  * Function called to notify a client about the socket
   GNUNET_free (th);
 }
 
 
 /**
  * Function called to notify a client about the socket
- * begin ready to queue the message.  "buf" will be
- * NULL and "size" zero if the socket was closed for
+ * begin ready to queue the message.  @a buf will be
+ * NULL and @a size zero if the socket was closed for
  * writing in the meantime.
  *
  * writing in the meantime.
  *
- * @param cls closure of type "struct TransmitGetResponseContext*"
- * @param size number of bytes available in buf
+ * @param cls closure of type `struct TransmitGetResponseContext *`
+ * @param size number of bytes available in @a buf
  * @param buf where the callee should write the message
  * @param buf where the callee should write the message
- * @return number of bytes written to buf
+ * @return number of bytes written to @a buf
  */
 static size_t
  */
 static size_t
-transmit_for_response (void *cls, size_t size, void *buf)
+transmit_for_response (void *cls,
+                      size_t size,
+                      void *buf)
 {
   struct TransmitGetResponseContext *tc = cls;
   uint16_t msize;
 
 {
   struct TransmitGetResponseContext *tc = cls;
   uint16_t msize;
 
-  tc->sock->tag = NULL;
+  tc->client->tag = NULL;
   msize = ntohs (tc->hdr->size);
   if (NULL == buf)
   msize = ntohs (tc->hdr->size);
   if (NULL == buf)
-    {
-#if DEBUG_CLIENT
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 _("Could not submit request, not expecting to receive a response.\n"));
-#endif
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Could not submit request, not expecting to receive a response.\n");
+    if (NULL != tc->rn)
       tc->rn (tc->rn_cls, NULL);
       tc->rn (tc->rn_cls, NULL);
-      GNUNET_free (tc);
-      return 0;
-    }
+    GNUNET_free (tc);
+    return 0;
+  }
   GNUNET_assert (size >= msize);
   memcpy (buf, tc->hdr, msize);
   GNUNET_assert (size >= msize);
   memcpy (buf, tc->hdr, msize);
-  GNUNET_CLIENT_receive (tc->sock,
+  GNUNET_CLIENT_receive (tc->client,
                          tc->rn,
                          tc->rn_cls,
                          GNUNET_TIME_absolute_get_remaining (tc->timeout));
                          tc->rn,
                          tc->rn_cls,
                          GNUNET_TIME_absolute_get_remaining (tc->timeout));
@@ -924,7 +1364,7 @@ transmit_for_response (void *cls, size_t size, void *buf)
  * will be called with a "NULL" response (in which
  * case the connection should probably be destroyed).
  *
  * will be called with a "NULL" response (in which
  * case the connection should probably be destroyed).
  *
- * @param sock connection to use
+ * @param client connection to use
  * @param hdr message to transmit
  * @param timeout when to give up (for both transmission
  *         and for waiting for a response)
  * @param hdr message to transmit
  * @param timeout when to give up (for both transmission
  *         and for waiting for a response)
@@ -934,15 +1374,13 @@ transmit_for_response (void *cls, size_t size, void *buf)
  *        if the caller does not care about temporary connection errors,
  *        for example because the protocol is stateless
  * @param rn function to call with the response
  *        if the caller does not care about temporary connection errors,
  *        for example because the protocol is stateless
  * @param rn function to call with the response
- * @param rn_cls closure for rn 
- * @return GNUNET_OK on success, GNUNET_SYSERR if a request
+ * @param rn_cls closure for @a rn
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR if a request
  *         is already pending
  */
 int
  *         is already pending
  */
 int
-GNUNET_CLIENT_transmit_and_get_response (struct GNUNET_CLIENT_Connection
-                                         *sock,
-                                         const struct GNUNET_MessageHeader
-                                         *hdr,
+GNUNET_CLIENT_transmit_and_get_response (struct GNUNET_CLIENT_Connection *client,
+                                         const struct GNUNET_MessageHeader *hdr,
                                          struct GNUNET_TIME_Relative timeout,
                                          int auto_retry,
                                          GNUNET_CLIENT_MessageHandler rn,
                                          struct GNUNET_TIME_Relative timeout,
                                          int auto_retry,
                                          GNUNET_CLIENT_MessageHandler rn,
@@ -951,32 +1389,28 @@ GNUNET_CLIENT_transmit_and_get_response (struct GNUNET_CLIENT_Connection
   struct TransmitGetResponseContext *tc;
   uint16_t msize;
 
   struct TransmitGetResponseContext *tc;
   uint16_t msize;
 
-  if (NULL != sock->th)
+  if (NULL != client->th)
     return GNUNET_SYSERR;
     return GNUNET_SYSERR;
-  GNUNET_assert (sock->tag == NULL);
+  GNUNET_assert (NULL == client->tag);
   msize = ntohs (hdr->size);
   tc = GNUNET_malloc (sizeof (struct TransmitGetResponseContext) + msize);
   msize = ntohs (hdr->size);
   tc = GNUNET_malloc (sizeof (struct TransmitGetResponseContext) + msize);
-  tc->sock = sock;
+  tc->client = client;
   tc->hdr = (const struct GNUNET_MessageHeader *) &tc[1];
   memcpy (&tc[1], hdr, msize);
   tc->timeout = GNUNET_TIME_relative_to_absolute (timeout);
   tc->rn = rn;
   tc->rn_cls = rn_cls;
   tc->hdr = (const struct GNUNET_MessageHeader *) &tc[1];
   memcpy (&tc[1], hdr, msize);
   tc->timeout = GNUNET_TIME_relative_to_absolute (timeout);
   tc->rn = rn;
   tc->rn_cls = rn_cls;
-  if (NULL == GNUNET_CLIENT_notify_transmit_ready (sock,
-                                                   msize,
-                                                   timeout,
-                                                   auto_retry,
-                                                   &transmit_for_response,
-                                                   tc))
-    {
-      GNUNET_break (0);
-      GNUNET_free (tc);
-      return GNUNET_SYSERR;
-    }
-  sock->tag = tc;
+  if (NULL ==
+      GNUNET_CLIENT_notify_transmit_ready (client, msize, timeout, auto_retry,
+                                           &transmit_for_response, tc))
+  {
+    GNUNET_break (0);
+    GNUNET_free (tc);
+    return GNUNET_SYSERR;
+  }
+  client->tag = tc;
   return GNUNET_OK;
 }
 
 
   return GNUNET_OK;
 }
 
 
-
 /*  end of client.c */
 /*  end of client.c */