Merge branch 'master'
[oweals/gnunet.git] / src / util / client.c
index 55eb68d13ca2f58cdc2a584c1b7a810f56517b85..163ae6eb9f522c6e4fbbf35d5935cc13f7b03643 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-2016 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_resolver_service.h"
+#include "gnunet_socks.h"
 
 
-#define DEBUG_CLIENT GNUNET_NO
 
 
-/**
- * How often do we re-try tranmsitting requests before giving up?
- * Note that if we succeeded transmitting a request but failed to read
- * a response, we do NOT re-try.
- */
-#define MAX_ATTEMPTS 50
+#define LOG(kind,...) GNUNET_log_from (kind, "util-client",__VA_ARGS__)
 
 
 /**
 
 
 /**
- * Handle for a transmission request.
+ * Internal state for a client connected to a GNUnet service.
  */
  */
-struct GNUNET_CLIENT_TransmitHandle
-{
-  /**
-   * Connection state.
-   */
-  struct GNUNET_CLIENT_Connection *sock;
-
-  /**
-   * Function to call to get the data for transmission.
-   */
-  GNUNET_CONNECTION_TransmitReadyNotify notify;
-
-  /**
-   * Closure for notify.
-   */
-  void *notify_cls;
-
-  /**
-   * Handle to the transmission with the underlying
-   * connection.
-   */
-  struct GNUNET_CONNECTION_TransmitHandle *th;
-
-  /**
-   * If we are re-trying and are delaying to do so,
-   * handle to the scheduled task managing the delay.
-   */
-  GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
-
-  /**
-   * Timeout for the operation overall.
-   */
-  struct GNUNET_TIME_Absolute timeout;
-
-  /**
-   * Number of bytes requested.
-   */
-  size_t size;
-
-  /**
-   * Are we allowed to re-try to connect without telling
-   * the user (of this API) about the connection troubles?
-   */
-  int auto_retry;
-
-  /**
-   * Number of attempts left for transmitting the request.  We may
-   * fail the first time (say because the service is not yet up), in
-   * which case (if auto_retry is set) we wait a bit and re-try
-   * (timeout permitting).
-   */
-  unsigned int attempts_left;
-
-};
+struct ClientState;
 
 
 /**
 
 
 /**
- * Context for processing 
- * "GNUNET_CLIENT_transmit_and_get_response" requests.
+ * During connect, we try multiple possible IP addresses
+ * to find out which one might work.
  */
  */
-struct TransmitGetResponseContext
+struct AddressProbe
 {
 {
-  /**
-   * Client handle.
-   */
-  struct GNUNET_CLIENT_Connection *sock;
-
-  /**
-   * Message to transmit; do not free, allocated
-   * right after this struct.
-   */
-  const struct GNUNET_MessageHeader *hdr;
-
-  /**
-   * Timeout to use.
-   */
-  struct GNUNET_TIME_Absolute timeout;
 
   /**
 
   /**
-   * Function to call when done.
+   * This is a linked list.
    */
    */
-  GNUNET_CLIENT_MessageHandler rn;
+  struct AddressProbe *next;
 
   /**
 
   /**
-   * Closure for "rn".
+   * This is a doubly-linked list.
    */
    */
-  void *rn_cls;
-};
+  struct AddressProbe *prev;
 
 
-/**
- * Context for handling the shutdown of a service.
- */
-struct ShutdownContext
-{
-  /**
-   * Scheduler to be used to call continuation
-   */
-  struct GNUNET_SCHEDULER_Handle *sched;
   /**
   /**
-   * Connection to the service that is being shutdown.
+   * The address; do not free (allocated at the end of this struct).
    */
    */
-  struct GNUNET_CLIENT_Connection *sock;
+  const struct sockaddr *addr;
 
   /**
 
   /**
-   * Time allowed for shutdown to happen.
+   * Underlying OS's socket.
    */
    */
-  struct GNUNET_TIME_Absolute timeout;
+  struct GNUNET_NETWORK_Handle *sock;
 
   /**
 
   /**
-   * Task set up to cancel the shutdown request on timeout.
+   * Connection for which we are probing.
    */
    */
-  GNUNET_SCHEDULER_TaskIdentifier cancel_task;
+  struct ClientState *cstate;
 
   /**
 
   /**
-   * Task to call once shutdown complete
+   * Lenth of addr.
    */
    */
-  GNUNET_CLIENT_ShutdownTask cont;
+  socklen_t addrlen;
 
   /**
 
   /**
-   * Closure for shutdown continuation
+   * Task waiting for the connection to finish connecting.
    */
    */
-  void *cont_cls;
-
-  /**
-   * We received a confirmation that the service will shut down.
-   */
-  int confirmed;
-
+  struct GNUNET_SCHEDULER_Task *task;
 };
 
 };
 
+
 /**
 /**
- * Struct to refer to a GNUnet TCP connection.
- * This is more than just a socket because if the server
- * drops the connection, the client automatically tries
- * to reconnect (and for that needs connection information).
+ * Internal state for a client connected to a GNUnet service.
  */
  */
-struct GNUNET_CLIENT_Connection
+struct ClientState
 {
 
   /**
 {
 
   /**
-   * the socket handle, NULL if not live
+   * The connection handle, NULL if not live
    */
    */
-  struct GNUNET_CONNECTION_Handle *sock;
+  struct GNUNET_NETWORK_Handle *sock;
 
   /**
 
   /**
-   * Our scheduler.
+   * Handle to a pending DNS lookup request, NULL if DNS is finished.
    */
    */
-  struct GNUNET_SCHEDULER_Handle *sched;
+  struct GNUNET_RESOLVER_RequestHandle *dns_active;
 
   /**
    * Our configuration.
    */
 
   /**
    * Our configuration.
    */
-  struct GNUNET_CONFIGURATION_Handle *cfg;
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
 
   /**
 
   /**
-   * Name of the service we interact with.
+   * Linked list of sockets we are currently trying out
+   * (during connect).
    */
    */
-  char *service_name;
+  struct AddressProbe *ap_head;
 
   /**
 
   /**
-   * Context of a transmit_and_get_response operation, NULL
-   * if no such operation is pending.
+   * Linked list of sockets we are currently trying out
+   * (during connect).
    */
    */
-  struct TransmitGetResponseContext *tag;
+  struct AddressProbe *ap_tail;
 
   /**
 
   /**
-   * Handler for current receiver task.
+   * Name of the service we interact with.
    */
    */
-  GNUNET_CLIENT_MessageHandler receiver_handler;
+  char *service_name;
 
   /**
 
   /**
-   * Closure for receiver_handler.
+   * Hostname, if any.
    */
    */
-  void *receiver_handler_cls;
+  char *hostname;
 
   /**
 
   /**
-   * Handle for a pending transmission request, NULL if there is
-   * none pending.
+   * Next message to transmit to the service. NULL for none.
    */
    */
-  struct GNUNET_CLIENT_TransmitHandle *th;
+  const struct GNUNET_MessageHeader *msg;
 
   /**
 
   /**
-   * Handler for service test completion (NULL unless in service_test)
+   * Task for trying to connect to the service.
    */
    */
-  GNUNET_SCHEDULER_Task test_cb;
+  struct GNUNET_SCHEDULER_Task *retry_task;
 
   /**
 
   /**
-   * Deadline for calling 'test_cb'.
+   * Task for sending messages to the service.
    */
    */
-  struct GNUNET_TIME_Absolute test_deadline;
+  struct GNUNET_SCHEDULER_Task *send_task;
 
   /**
 
   /**
-   * If we are re-trying and are delaying to do so,
-   * handle to the scheduled task managing the delay.
+   * Task for sending messages to the service.
    */
    */
-  GNUNET_SCHEDULER_TaskIdentifier receive_task;
+  struct GNUNET_SCHEDULER_Task *recv_task;
 
   /**
 
   /**
-   * Closure for test_cb (NULL unless in service_test)
+   * Tokenizer for inbound messages.
    */
    */
-  void *test_cb_cls;
+  struct GNUNET_MessageStreamTokenizer *mst;
 
   /**
 
   /**
-   * Buffer for received message.
+   * Message queue under our control.
    */
    */
-  char *received_buf;
+  struct GNUNET_MQ_Handle *mq;
 
   /**
    * Timeout for receiving a response (absolute time).
 
   /**
    * Timeout for receiving a response (absolute time).
@@ -266,914 +171,717 @@ struct GNUNET_CLIENT_Connection
   struct GNUNET_TIME_Relative back_off;
 
   /**
   struct GNUNET_TIME_Relative back_off;
 
   /**
-   * Number of bytes in received_buf that are valid.
-   */
-  size_t received_pos;
-
-  /**
-   * Size of received_buf.
+   * TCP port (0 for disabled).
    */
    */
-  unsigned int received_size;
+  unsigned long long port;
 
   /**
 
   /**
-   * Do we have a complete response in received_buf?
+   * Offset in the message where we are for transmission.
    */
    */
-  int msg_complete;
+  size_t msg_off;
 
   /**
 
   /**
-   * Are we currently busy doing receive-processing?
-   * GNUNET_YES if so, GNUNET_NO if not.
+   * How often have we tried to connect?
    */
    */
-  int in_receive;
+  unsigned int attempts;
 
   /**
 
   /**
-   * Are we ignoring shutdown signals?
+   * Are we supposed to die?  #GNUNET_SYSERR if destruction must be
+   * deferred, #GNUNET_NO by default, #GNUNET_YES if destruction was
+   * deferred.
    */
    */
-  int ignore_shutdown;
+  int in_destroy;
 
 };
 
 
 
 };
 
 
-static struct GNUNET_CONNECTION_Handle *
-do_connect (struct GNUNET_SCHEDULER_Handle *sched,
-            const char *service_name,
-            const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
-  struct GNUNET_CONNECTION_Handle *sock;
-  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)))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  _
-                  ("Could not determine valid hostname and port for service `%s' from configuration.\n"),
-                  service_name);
-      return NULL;
-    }
-  if (0 == strlen (hostname))
-    {
-      GNUNET_free (hostname);
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  _("Need a non-empty hostname for service `%s'.\n"),
-                  service_name);
-      return NULL;
-    }
-  sock = GNUNET_CONNECTION_create_from_connect (sched,
-                                                cfg,
-                                                hostname,
-                                                port,
-                                                GNUNET_SERVER_MAX_MESSAGE_SIZE);
-  GNUNET_free (hostname);
-  return sock;
-}
-
-
 /**
 /**
- * Get a connection with a service.
+ * Try to connect to the 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)
+ * @param cls the `struct ClientState` to try to connect to the service
  */
  */
-struct GNUNET_CLIENT_Connection *
-GNUNET_CLIENT_connect (struct GNUNET_SCHEDULER_Handle *sched,
-                       const char *service_name,
-                       const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
-  struct GNUNET_CLIENT_Connection *ret;
-  struct GNUNET_CONNECTION_Handle *sock;
-
-  sock = do_connect (sched, service_name, cfg);
-  if (sock == 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;
-}
+static void
+start_connect (void *cls);
 
 
 /**
 
 
 /**
- * Configure this connection to ignore shutdown signals.
+ * We've failed for good to establish a connection (timeout or
+ * no more addresses to try).
  *
  *
- * @param h client handle
- * @param do_ignore GNUNET_YES to ignore, GNUNET_NO to restore default
+ * @param cstate the connection we tried to establish
  */
  */
-void
-GNUNET_CLIENT_ignore_shutdown (struct GNUNET_CLIENT_Connection *h,
-                              int do_ignore)
+static void
+connect_fail_continuation (struct ClientState *cstate)
 {
 {
-  h->ignore_shutdown = do_ignore;
-  if (h->sock != NULL)
-    GNUNET_CONNECTION_ignore_shutdown (h->sock,
-                                      do_ignore);
+  GNUNET_break (NULL == cstate->ap_head);
+  GNUNET_break (NULL == cstate->ap_tail);
+  GNUNET_break (NULL == cstate->dns_active);
+  GNUNET_break (NULL == cstate->sock);
+  GNUNET_assert (NULL == cstate->send_task);
+  GNUNET_assert (NULL == cstate->recv_task);
+  // GNUNET_assert (NULL == cstate->proxy_handshake);
+
+  cstate->back_off = GNUNET_TIME_STD_BACKOFF (cstate->back_off);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Failed to establish connection to `%s', no further addresses to try, will try again in %s.\n",
+       cstate->service_name,
+       GNUNET_STRINGS_relative_time_to_string (cstate->back_off,
+                                               GNUNET_YES));
+  cstate->retry_task
+    = GNUNET_SCHEDULER_add_delayed (cstate->back_off,
+                                    &start_connect,
+                                    cstate);
 }
 
 
 /**
 }
 
 
 /**
- * Destroy connection with the service.  This will automatically
- * cancel any pending "receive" request (however, the handler will
- * *NOT* be called, not even with a NULL message).  Any pending
- * transmission request will also be cancelled UNLESS the callback for
- * the transmission request has already been called, in which case the
- * transmission 'finish_pending_write' argument determines whether or
- * not the write is guaranteed to complete before the socket is fully
- * destroyed (unless, of course, there is an error with the server in
- * which case the message may still be lost).
+ * We are ready to send a message to the service.
  *
  *
- * @param finish_pending_write should a transmission already passed to the
- *          handle be completed?
- * @param sock handle to the service connection
- */
-void
-GNUNET_CLIENT_disconnect (struct GNUNET_CLIENT_Connection *sock,
-                         int finish_pending_write)
-{
-  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);
-}
-
-
-/**
- * Check if message is complete
+ * @param cls the `struct ClientState` with the `msg` to transmit
  */
 static void
  */
 static void
-check_complete (struct GNUNET_CLIENT_Connection *conn)
+transmit_ready (void *cls)
 {
 {
-  if ((conn->received_pos >= sizeof (struct GNUNET_MessageHeader)) &&
-      (conn->received_pos >=
-       ntohs (((const struct GNUNET_MessageHeader *) conn->received_buf)->
-              size)))
-    conn->msg_complete = GNUNET_YES;
+  struct ClientState *cstate = cls;
+  ssize_t ret;
+  size_t len;
+  const char *pos;
+  int notify_in_flight;
+
+  cstate->send_task = NULL;
+  pos = (const char *) cstate->msg;
+  len = ntohs (cstate->msg->size);
+  GNUNET_assert (cstate->msg_off < len);
+ RETRY:
+  ret = GNUNET_NETWORK_socket_send (cstate->sock,
+                                    &pos[cstate->msg_off],
+                                    len - cstate->msg_off);
+  if (-1 == ret)
+  {
+    if (EINTR == errno)
+      goto RETRY;
+    GNUNET_MQ_inject_error (cstate->mq,
+                            GNUNET_MQ_ERROR_WRITE);
+    return;
+  }
+  notify_in_flight = (0 == cstate->msg_off);
+  cstate->msg_off += ret;
+  if (cstate->msg_off < len)
+  {
+    cstate->send_task
+      = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                        cstate->sock,
+                                        &transmit_ready,
+                                        cstate);
+    if (notify_in_flight)
+      GNUNET_MQ_impl_send_in_flight (cstate->mq);
+    return;
+  }
+  cstate->msg = NULL;
+  GNUNET_MQ_impl_send_continue (cstate->mq);
 }
 
 
 /**
 }
 
 
 /**
- * Callback function for data received from the network.  Note that
- * both "available" and "errCode" would be 0 if the read simply timed out.
+ * We have received a full message, pass to the MQ dispatcher.
+ * Called by the tokenizer via #receive_ready().
  *
  *
- * @param cls closure
- * @param buf pointer to received data
- * @param available number of bytes availabe in "buf",
- *        possibly 0 (on errors)
- * @param addr address of the sender
- * @param addrlen size of addr
- * @param errCode value of errno (on errors receiving)
+ * @param cls the `struct ClientState`
+ * @param msg message we received.
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR to stop further processing
  */
  */
-static void
-receive_helper (void *cls,
-                const void *buf,
-                size_t available,
-                const struct sockaddr *addr, socklen_t addrlen, int errCode)
+static int
+recv_message (void *cls,
+              const struct GNUNET_MessageHeader *msg)
 {
 {
-  struct GNUNET_CLIENT_Connection *conn = 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))
-    {
-      /* 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;
-    }
-
-  /* FIXME: optimize for common fast case where buf contains the
-     entire message and we need no copying... */
-
+  struct ClientState *cstate = cls;
 
 
-  /* 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);
-  /* check for timeout */
-  remaining = GNUNET_TIME_absolute_get_remaining (conn->receive_timeout);
-  if (remaining.value == 0)
-    {
-      /* signal timeout! */
-      conn->receiver_handler (conn->receiver_handler_cls, NULL);
-      return;
-    }
-  /* back to receive -- either for more data or to call callback! */
-  GNUNET_CLIENT_receive (conn,
-                         conn->receiver_handler,
-                         conn->receiver_handler_cls, remaining);
+  if (GNUNET_YES == cstate->in_destroy)
+    return GNUNET_SYSERR;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Received message of type %u and size %u from %s\n",
+       ntohs (msg->type),
+       ntohs (msg->size),
+       cstate->service_name);
+  GNUNET_MQ_inject_message (cstate->mq,
+                            msg);
+  if (GNUNET_YES == cstate->in_destroy)
+    return GNUNET_SYSERR;
+  return GNUNET_OK;
 }
 
 
 /**
 }
 
 
 /**
- * Continuation to call the receive callback.
+ * Cancel all remaining connect attempts
  *
  *
- * @param cls  our handle to the client connection
- * @param tc scheduler context
+ * @param cstate handle of the client state to process
  */
 static void
  */
 static void
-receive_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+cancel_aps (struct ClientState *cstate)
 {
 {
-  struct GNUNET_CLIENT_Connection *sock = cls;
-  GNUNET_CLIENT_MessageHandler handler = sock->receiver_handler;
-  const struct GNUNET_MessageHeader *cmsg =
-    (const struct GNUNET_MessageHeader *) sock->received_buf;
-  void *handler_cls = sock->receiver_handler_cls;
-  uint16_t msize = ntohs (cmsg->size);
-  char mbuf[msize];
-  struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) mbuf;
-
-  sock->receive_task = GNUNET_SCHEDULER_NO_TASK;
-  GNUNET_assert (GNUNET_YES == sock->msg_complete);
-  GNUNET_assert (sock->received_pos >= 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)
-    handler (handler_cls, msg);
+  struct AddressProbe *pos;
+
+  while (NULL != (pos = cstate->ap_head))
+  {
+    GNUNET_break (GNUNET_OK ==
+                  GNUNET_NETWORK_socket_close (pos->sock));
+    GNUNET_SCHEDULER_cancel (pos->task);
+    GNUNET_CONTAINER_DLL_remove (cstate->ap_head,
+                                cstate->ap_tail,
+                                pos);
+    GNUNET_free (pos);
+  }
 }
 
 
 /**
 }
 
 
 /**
- * Read from the service.
+ * Implement the destruction of a message queue.  Implementations must
+ * not free @a mq, but should take care of @a impl_state.
  *
  *
- * @param sock the service
- * @param handler function to call with the message
- * @param handler_cls closure for handler
- * @param timeout how long to wait until timing out
- */
-void
-GNUNET_CLIENT_receive (struct GNUNET_CLIENT_Connection *sock,
-                       GNUNET_CLIENT_MessageHandler handler,
-                       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
-    {
-      sock->in_receive = GNUNET_YES;
-      GNUNET_CONNECTION_receive (sock->sock,
-                                 GNUNET_SERVER_MAX_MESSAGE_SIZE,
-                                 timeout, &receive_helper, sock);
-    }
-}
-
-
-/**
- * Handler receiving response to service shutdown requests.
- * First call with NULL: service misbehaving, or something.
- * First call with GNUNET_MESSAGE_TYPE_SHUTDOWN_ACK:
- *   - service will shutdown
- * First call with GNUNET_MESSAGE_TYPE_SHUTDOWN_REFUSE:
- *   - service will not be stopped!
- *
- * Second call with NULL:
- *   - service has now really shut down.
- *
- * @param cls closure
- * @param msg NULL, indicating socket closure.
+ * @param mq the message queue to destroy
+ * @param impl_state our `struct ClientState`
  */
 static void
  */
 static void
-service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg)
+connection_client_destroy_impl (struct GNUNET_MQ_Handle *mq,
+                                void *impl_state)
 {
 {
-  struct ShutdownContext *shutdown_ctx = cls;
-
-  if ((msg == NULL) && (shutdown_ctx->confirmed != GNUNET_YES))   
-    {
-      /* Means the other side closed the connection and never confirmed a shutdown */
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 
-                 "Service handle shutdown before ACK!\n");
-      if (shutdown_ctx->cont != NULL)
-        shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_SYSERR);      
-      GNUNET_SCHEDULER_cancel(shutdown_ctx->sched, shutdown_ctx->cancel_task);
-      GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
-      GNUNET_free(shutdown_ctx);
-    }
-  else if ((msg == NULL) && (shutdown_ctx->confirmed == GNUNET_YES))
-    {
-      GNUNET_log(GNUNET_ERROR_TYPE_WARNING, 
-                "Service shutdown complete.\n");
-      if (shutdown_ctx->cont != NULL)
-        shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_NO);
-
-      GNUNET_SCHEDULER_cancel(shutdown_ctx->sched, shutdown_ctx->cancel_task);
-      GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
-      GNUNET_free(shutdown_ctx);
-    }
-  else
-    {
-      GNUNET_assert(ntohs(msg->size) == sizeof(struct GNUNET_MessageHeader));
-
-      switch (ntohs(msg->type))
-      {
-      case GNUNET_MESSAGE_TYPE_SHUTDOWN_ACK:
-        GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
-                  "Received confirmation for service shutdown.\n");
-        shutdown_ctx->confirmed = GNUNET_YES;
-        GNUNET_CLIENT_receive (shutdown_ctx->sock, 
-                              &service_shutdown_handler, 
-                              shutdown_ctx, 
-                              GNUNET_TIME_UNIT_FOREVER_REL);
-        break;
-      case GNUNET_MESSAGE_TYPE_SHUTDOWN_REFUSE:
-      default: /* Fall through */
-        GNUNET_log(GNUNET_ERROR_TYPE_WARNING, 
-                  "Service shutdown refused!\n");
-        if (shutdown_ctx->cont != NULL)
-          shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_YES);
-
-        GNUNET_SCHEDULER_cancel(shutdown_ctx->sched, shutdown_ctx->cancel_task);
-        GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
-        GNUNET_free(shutdown_ctx);
-        break;
-      }
-    }
-}
-
-/**
- * Shutting down took too long, cancel receive and return error.
- *
- * @param cls closure
- * @param tc context information (why was this task triggered now)
- */
-void service_shutdown_cancel (void *cls,
-                              const struct GNUNET_SCHEDULER_TaskContext * tc)
-{
-  struct ShutdownContext *shutdown_ctx = cls;
-  GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "service_shutdown_cancel called!\n");
-  shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_SYSERR);
-  GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
-  GNUNET_free(shutdown_ctx);
+  struct ClientState *cstate = impl_state;
+
+  if (GNUNET_SYSERR == cstate->in_destroy)
+  {
+    /* defer destruction */
+    cstate->in_destroy = GNUNET_YES;
+    cstate->mq = NULL;
+    return;
+  }
+  if (NULL != cstate->dns_active)
+    GNUNET_RESOLVER_request_cancel (cstate->dns_active);
+  if (NULL != cstate->send_task)
+    GNUNET_SCHEDULER_cancel (cstate->send_task);
+  if (NULL != cstate->recv_task)
+    GNUNET_SCHEDULER_cancel (cstate->recv_task);
+  if (NULL != cstate->retry_task)
+    GNUNET_SCHEDULER_cancel (cstate->retry_task);
+  if (NULL != cstate->sock)
+    GNUNET_NETWORK_socket_close (cstate->sock);
+  cancel_aps (cstate);
+  GNUNET_free (cstate->service_name);
+  GNUNET_free_non_null (cstate->hostname);
+  GNUNET_MST_destroy (cstate->mst);
+  GNUNET_free (cstate);
 }
 
 
 /**
 }
 
 
 /**
- * If possible, write a shutdown message to the target
- * buffer and destroy the client connection.
+ * This function is called once we have data ready to read.
  *
  *
- * @param cls the "struct GNUNET_CLIENT_Connection" to destroy
- * @param size number of bytes available in buf
- * @param buf NULL on error, otherwise target buffer
- * @return number of bytes written to buf
+ * @param cls `struct ClientState` with connection to read from
  */
  */
-static size_t
-write_shutdown (void *cls, size_t size, void *buf)
+static void
+receive_ready (void *cls)
 {
 {
-  struct GNUNET_MessageHeader *msg;
-  struct ShutdownContext *shutdown_ctx = cls;
-
-  if (size < sizeof (struct GNUNET_MessageHeader))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  _("Failed to transmit shutdown request to client.\n"));
-      shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_SYSERR);
-      GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
-      GNUNET_free(shutdown_ctx);
-      return 0;                 /* client disconnected */
-    }
-
-  GNUNET_CLIENT_receive (shutdown_ctx->sock,
-                        &service_shutdown_handler, shutdown_ctx, 
-                        GNUNET_TIME_UNIT_FOREVER_REL);
-  shutdown_ctx->cancel_task = GNUNET_SCHEDULER_add_delayed (shutdown_ctx->sched, 
-                                                           GNUNET_TIME_absolute_get_remaining(shutdown_ctx->timeout), 
-                                                           &service_shutdown_cancel, 
-                                                           shutdown_ctx);
-  msg = (struct GNUNET_MessageHeader *) buf;
-  msg->type = htons (GNUNET_MESSAGE_TYPE_SHUTDOWN);
-  msg->size = htons (sizeof (struct GNUNET_MessageHeader));
-  return sizeof (struct GNUNET_MessageHeader);
+  struct ClientState *cstate = cls;
+  int ret;
+
+  cstate->recv_task = NULL;
+  cstate->in_destroy = GNUNET_SYSERR;
+  ret = GNUNET_MST_read (cstate->mst,
+                         cstate->sock,
+                         GNUNET_NO,
+                         GNUNET_NO);
+  if (GNUNET_SYSERR == ret)
+  {
+    if (NULL != cstate->mq)
+      GNUNET_MQ_inject_error (cstate->mq,
+                             GNUNET_MQ_ERROR_READ);
+    if (GNUNET_YES == cstate->in_destroy)
+      connection_client_destroy_impl (cstate->mq,
+                                     cstate);
+    return;
+  }
+  if (GNUNET_YES == cstate->in_destroy)
+  {
+    connection_client_destroy_impl (cstate->mq,
+                                    cstate);
+    return;
+  }
+  cstate->in_destroy = GNUNET_NO;
+  cstate->recv_task
+    = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                     cstate->sock,
+                                     &receive_ready,
+                                     cstate);
 }
 
 
 /**
 }
 
 
 /**
- * Request that the service should shutdown.
- * Afterwards, the connection will automatically be
- * disconnected.  Hence the "sock" should not
- * be used by the caller after this call
- * (calling this function frees "sock" after a while).
- *
- * @param sched the scheduler to use for calling shutdown continuation
- * @param sock the socket connected to the service
- * @param timeout how long to wait before giving up on transmission
- * @param cont continuation to call once the service is really down
- * @param cont_cls closure for continuation
+ * We've succeeded in establishing a connection.
  *
  *
- */
-void
-GNUNET_CLIENT_service_shutdown (struct GNUNET_SCHEDULER_Handle *sched,
-                                struct GNUNET_CLIENT_Connection *sock,
-                                struct GNUNET_TIME_Relative timeout,
-                                GNUNET_CLIENT_ShutdownTask cont,
-                                void *cont_cls)
-{
-  struct ShutdownContext *shutdown_ctx;
-  shutdown_ctx = GNUNET_malloc(sizeof(struct ShutdownContext));
-  shutdown_ctx->sched = sched;
-  shutdown_ctx->cont = cont;
-  shutdown_ctx->cont_cls = cont_cls;
-  shutdown_ctx->sock = sock;
-  shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute(timeout);
-  GNUNET_CONNECTION_notify_transmit_ready (sock->sock,
-                                           sizeof (struct
-                                                   GNUNET_MessageHeader),
-                                           timeout,
-                                           &write_shutdown, shutdown_ctx);
-}
-
-
-/**
- * Report service unavailable.
+ * @param cstate the connection we tried to establish
  */
 static void
  */
 static void
-service_test_error (struct GNUNET_SCHEDULER_Handle *s,
-                    GNUNET_SCHEDULER_Task task, void *task_cls)
+connect_success_continuation (struct ClientState *cstate)
 {
 {
-  GNUNET_SCHEDULER_add_continuation (s,
-                                     task,
-                                     task_cls,
-                                     GNUNET_SCHEDULER_REASON_TIMEOUT);
+  GNUNET_assert (NULL == cstate->recv_task);
+  cstate->recv_task
+    = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                     cstate->sock,
+                                     &receive_ready,
+                                     cstate);
+  if (NULL != cstate->msg)
+  {
+    GNUNET_assert (NULL == cstate->send_task);
+    cstate->send_task
+      = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                        cstate->sock,
+                                        &transmit_ready,
+                                        cstate);
+  }
 }
 
 
 /**
 }
 
 
 /**
- * Receive confirmation from test, service is up.
+ * Try connecting to the server using UNIX domain sockets.
  *
  *
- * @param cls closure
- * @param msg message received, NULL on timeout or fatal error
+ * @param service_name name of service to connect to
+ * @param cfg configuration to use
+ * @return NULL on error, socket connected to UNIX otherwise
  */
  */
-static void
-confirm_handler (void *cls, const struct GNUNET_MessageHeader *msg)
+static struct GNUNET_NETWORK_Handle *
+try_unixpath (const char *service_name,
+             const struct GNUNET_CONFIGURATION_Handle *cfg)
 {
 {
-  struct GNUNET_CLIENT_Connection *conn = cls;
-  /* 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 AF_UNIX
+  struct GNUNET_NETWORK_Handle *sock;
+  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))
     {
     {
-#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);
+      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;
     }
     }
-  else
+    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);
+#ifdef LINUX
     {
     {
-      service_test_error (conn->sched, conn->test_cb, conn->test_cb_cls);
-    }
-  GNUNET_CLIENT_disconnect (conn, GNUNET_NO);
-}
+      int abstract;
 
 
-
-static size_t
-write_test (void *cls, size_t size, void *buf)
-{
-  struct GNUNET_CLIENT_Connection *conn = cls;
-  struct GNUNET_MessageHeader *msg;
-
-  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 */
+      abstract = GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                                       "TESTING",
+                                                       "USE_ABSTRACT_SOCKETS");
+      if (GNUNET_YES == abstract)
+        s_un.sun_path[0] = '\0';
     }
     }
-#if DEBUG_CLIENT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Transmitting `%s' request.\n", "TEST");
 #endif
 #endif
-  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));
-  return sizeof (struct GNUNET_MessageHeader);
-}
-
-
-/**
- * Wait until the service is running.
- *
- * @param sched scheduler 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
- */
-void
-GNUNET_CLIENT_service_test (struct GNUNET_SCHEDULER_Handle *sched,
-                            const char *service,
-                            const struct GNUNET_CONFIGURATION_Handle *cfg,
-                            struct GNUNET_TIME_Relative timeout,
-                            GNUNET_SCHEDULER_Task task, void *task_cls)
-{
-  struct GNUNET_CLIENT_Connection *conn;
-
-#if DEBUG_CLIENT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Testing if service `%s' is running.\n", service);
+#if HAVE_SOCKADDR_UN_SUN_LEN
+    s_un.sun_len = (u_char) sizeof (struct sockaddr_un);
 #endif
 #endif
-  conn = GNUNET_CLIENT_connect (sched, service, cfg);
-  if (conn == NULL)
-    {
-      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;
-    }
-  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))  
+    sock = GNUNET_NETWORK_socket_create (AF_UNIX,
+                                         SOCK_STREAM,
+                                         0);
+    if ( (NULL != sock) &&
+         ( (GNUNET_OK ==
+            GNUNET_NETWORK_socket_connect (sock,
+                                           (struct sockaddr *) &s_un,
+                                           sizeof (s_un))) ||
+           (EINPROGRESS == errno) ) )
     {
     {
-      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;
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+           "Successfully connected to unixpath `%s'!\n",
+          unixpath);
+      GNUNET_free (unixpath);
+      return sock;
     }
     }
+    if (NULL != sock)
+      GNUNET_NETWORK_socket_close (sock);
+  }
+  GNUNET_free_non_null (unixpath);
+#endif
+  return NULL;
 }
 
 
 /**
 }
 
 
 /**
- * Connection notifies us about failure or success of
- * a transmission request.  Either pass it on to our
- * user or, if possible, retry.
+ * Scheduler let us know that we're either ready to write on the
+ * socket OR connect timed out.  Do the right thing.
  *
  *
- * @param cls our "struct GNUNET_CLIENT_TransmissionHandle"
- * @param size number of bytes available for transmission
- * @param buf where to write them
- * @return number of bytes written to buf
+ * @param cls the `struct AddressProbe *` with the address that we are probing
  */
  */
-static size_t client_notify (void *cls, size_t size, void *buf);
+static void
+connect_probe_continuation (void *cls)
+{
+  struct AddressProbe *ap = cls;
+  struct ClientState *cstate = ap->cstate;
+  const struct GNUNET_SCHEDULER_TaskContext *tc;
+  int error;
+  socklen_t len;
+
+  ap->task = NULL;
+  GNUNET_assert (NULL != ap->sock);
+  GNUNET_CONTAINER_DLL_remove (cstate->ap_head,
+                              cstate->ap_tail,
+                              ap);
+  len = sizeof (error);
+  error = 0;
+  tc = GNUNET_SCHEDULER_get_task_context ();
+  if ( (0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) ||
+       (GNUNET_OK !=
+       GNUNET_NETWORK_socket_getsockopt (ap->sock,
+                                         SOL_SOCKET,
+                                         SO_ERROR,
+                                         &error,
+                                         &len)) ||
+       (0 != error) )
+  {
+    GNUNET_break (GNUNET_OK ==
+                 GNUNET_NETWORK_socket_close (ap->sock));
+    GNUNET_free (ap);
+    if ( (NULL == cstate->ap_head) &&
+         //     (NULL == cstate->proxy_handshake) &&
+        (NULL == cstate->dns_active) )
+      connect_fail_continuation (cstate);
+    return;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Connection to `%s' succeeded!\n",
+       cstate->service_name);
+  /* trigger jobs that waited for the connection */
+  GNUNET_assert (NULL == cstate->sock);
+  cstate->sock = ap->sock;
+  GNUNET_free (ap);
+  cancel_aps (cstate);
+  connect_success_continuation (cstate);
+}
 
 
 /**
 
 
 /**
- * This task is run if we should re-try connection to the
- * service after a while.
+ * Try to establish a connection given the specified address.
+ * This function is called by the resolver once we have a DNS reply.
  *
  *
- * @param cls our "struct GNUNET_CLIENT_TransmitHandle" of the request
- * @param tc unused
+ * @param cls our `struct ClientState *`
+ * @param addr address to try, NULL for "last call"
+ * @param addrlen length of @a addr
  */
 static void
  */
 static void
-client_delayed_retry (void *cls,
-                      const struct GNUNET_SCHEDULER_TaskContext *tc)
+try_connect_using_address (void *cls,
+                           const struct sockaddr *addr,
+                           socklen_t addrlen)
 {
 {
-  struct GNUNET_CLIENT_TransmitHandle *th = cls;
-
-  th->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
-  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;
-    }
+  struct ClientState *cstate = cls;
+  struct AddressProbe *ap;
+
+  if (NULL == addr)
+  {
+    cstate->dns_active = NULL;
+    if ( (NULL == cstate->ap_head) &&
+         //  (NULL == cstate->proxy_handshake) &&
+         (NULL == cstate->sock) )
+      connect_fail_continuation (cstate);
+    return;
+  }
+  if (NULL != cstate->sock)
+    return;                     /* already connected */
+  /* try to connect */
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Trying to connect using address `%s:%u'\n",
+       GNUNET_a2s (addr,
+                   addrlen),
+       cstate->port);
+  ap = GNUNET_malloc (sizeof (struct AddressProbe) + addrlen);
+  ap->addr = (const struct sockaddr *) &ap[1];
+  GNUNET_memcpy (&ap[1],
+                 addr,
+                 addrlen);
+  ap->addrlen = addrlen;
+  ap->cstate = cstate;
+
+  switch (ap->addr->sa_family)
+  {
+  case AF_INET:
+    ((struct sockaddr_in *) ap->addr)->sin_port = htons (cstate->port);
+    break;
+  case AF_INET6:
+    ((struct sockaddr_in6 *) ap->addr)->sin6_port = htons (cstate->port);
+    break;
+  default:
+    GNUNET_break (0);
+    GNUNET_free (ap);
+    return;                     /* not supported by us */
+  }
+  ap->sock = GNUNET_NETWORK_socket_create (ap->addr->sa_family,
+                                          SOCK_STREAM,
+                                           0);
+  if (NULL == ap->sock)
+  {
+    GNUNET_free (ap);
+    return;                     /* not supported by OS */
+  }
+  if ( (GNUNET_OK !=
+        GNUNET_NETWORK_socket_connect (ap->sock,
+                                       ap->addr,
+                                       ap->addrlen)) &&
+       (EINPROGRESS != errno) )
+  {
+    /* maybe refused / unsupported address, try next */
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO,
+                         "connect");
+    GNUNET_break (GNUNET_OK ==
+                  GNUNET_NETWORK_socket_close (ap->sock));
+    GNUNET_free (ap);
+    return;
+  }
+  GNUNET_CONTAINER_DLL_insert (cstate->ap_head,
+                               cstate->ap_tail,
+                               ap);
+  ap->task = GNUNET_SCHEDULER_add_write_net (GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT,
+                                            ap->sock,
+                                            &connect_probe_continuation,
+                                            ap);
 }
 
 
 /**
 }
 
 
 /**
- * Connection notifies us about failure or success of a transmission
- * request.  Either pass it on to our user or, if possible, retry.
+ * Test whether the configuration has proper values for connection
+ * (UNIXPATH || (PORT && HOSTNAME)).
  *
  *
- * @param cls our "struct GNUNET_CLIENT_TransmissionHandle"
- * @param size number of bytes available for transmission
- * @param buf where to write them
- * @return number of bytes written to buf
+ * @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 size_t
-client_notify (void *cls, size_t size, void *buf)
+static int
+test_service_configuration (const char *service_name,
+                           const struct GNUNET_CONFIGURATION_Handle *cfg)
 {
 {
-  struct GNUNET_CLIENT_TransmitHandle *th = cls;
-  size_t ret;
-  struct GNUNET_TIME_Relative delay;
-
-  th->th = NULL;
-  th->sock->th = NULL;
-  if (buf == NULL)
-    {
-      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);
+  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
 #endif
-      th->reconnect_task = GNUNET_SCHEDULER_add_delayed (th->sock->sched,
-                                                         delay,
-                                                         &client_delayed_retry,
-                                                         th);
-      th->sock->th = th;
-      return 0;
-    }
-  GNUNET_assert (size >= th->size);
-  ret = th->notify (th->notify_cls, size, buf);
-  GNUNET_free (th);
+
+  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;
 }
 
 
 /**
   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.
+ * Try to connect to the service.
  *
  *
- * @param sock 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 auto_retry if the connection to the service dies, should we
- *        automatically re-connect and retry (within the timeout period)
- *        or should we immediately fail in this case?  Pass GNUNET_YES
- *        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
- * @return NULL if our buffer will never hold size bytes,
- *         a handle if the notify callback was queued (can be used to cancel)
+ * @param cls the `struct ClientState` to try to connect to the service
  */
  */
-struct GNUNET_CLIENT_TransmitHandle *
-GNUNET_CLIENT_notify_transmit_ready (struct GNUNET_CLIENT_Connection *sock,
-                                     size_t size,
-                                     struct GNUNET_TIME_Relative timeout,
-                                     int auto_retry,
-                                     GNUNET_CONNECTION_TransmitReadyNotify
-                                     notify, void *notify_cls)
+static void
+start_connect (void *cls)
 {
 {
-  struct GNUNET_CLIENT_TransmitHandle *th;
+  struct ClientState *cstate = cls;
+
+  cstate->retry_task = NULL;
+#if 0
+  /* Never use a local source if a proxy is configured */
+  if (GNUNET_YES ==
+      GNUNET_SOCKS_check_service (cstate->service_name,
+                                  cstate->cfg))
+  {
+    socks_connect (cstate);
+    return;
+  }
+#endif
 
 
-  if (NULL != sock->th)
-    return NULL;
-  th = GNUNET_malloc (sizeof (struct GNUNET_CLIENT_TransmitHandle));
-  th->sock = sock;
-  th->size = size;
-  th->timeout = GNUNET_TIME_relative_to_absolute (timeout);
-  th->auto_retry = auto_retry;
-  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)
+  if ( (0 == (cstate->attempts++ % 2)) ||
+       (0 == cstate->port) ||
+       (NULL == cstate->hostname) )
+  {
+    /* on even rounds, try UNIX first, or always
+       if we do not have a DNS name and TCP port. */
+    cstate->sock = try_unixpath (cstate->service_name,
+                                 cstate->cfg);
+    if (NULL != cstate->sock)
     {
     {
-      GNUNET_break (0);
-      GNUNET_free (th);
-      return NULL;
+      connect_success_continuation (cstate);
+      return;
     }
     }
-  sock->th = th;
-  return th;
+  }
+  if ( (NULL == cstate->hostname) ||
+       (0 == cstate->port) )
+  {
+    /* All options failed. Boo! */
+    connect_fail_continuation (cstate);
+    return;
+  }
+  cstate->dns_active
+    = GNUNET_RESOLVER_ip_get (cstate->hostname,
+                             AF_UNSPEC,
+                              GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT,
+                              &try_connect_using_address,
+                             cstate);
 }
 
 
 /**
 }
 
 
 /**
- * Cancel a request for notification.
- * 
- * @param th handle from the original request.
+ * Implements the transmission functionality of a message queue.
+ *
+ * @param mq the message queue
+ * @param msg the message to send
+ * @param impl_state our `struct ClientState`
  */
  */
-void
-GNUNET_CLIENT_notify_transmit_ready_cancel (struct
-                                            GNUNET_CLIENT_TransmitHandle *th)
+static void
+connection_client_send_impl (struct GNUNET_MQ_Handle *mq,
+                             const struct GNUNET_MessageHeader *msg,
+                             void *impl_state)
 {
 {
-  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;
-    }
-  else
-    {
-      GNUNET_break (NULL != th->th);
-      GNUNET_CONNECTION_notify_transmit_ready_cancel (th->th);
-    }
-  th->sock->th = NULL;
-  GNUNET_free (th);
+  struct ClientState *cstate = impl_state;
+
+  /* only one message at a time allowed */
+  GNUNET_assert (NULL == cstate->msg);
+  GNUNET_assert (NULL == cstate->send_task);
+  cstate->msg = msg;
+  cstate->msg_off = 0;
+  if (NULL == cstate->sock)
+    return; /* still waiting for connection */
+  cstate->send_task
+    = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                      cstate->sock,
+                                      &transmit_ready,
+                                      cstate);
 }
 
 
 /**
 }
 
 
 /**
- * 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
- * writing in the meantime.
+ * Cancel the currently sent message.
  *
  *
- * @param cls closure of type "struct TransmitGetResponseContext*"
- * @param size number of bytes available in buf
- * @param buf where the callee should write the message
- * @return number of bytes written to buf
+ * @param mq message queue
+ * @param impl_state our `struct ClientState`
  */
  */
-static size_t
-transmit_for_response (void *cls, size_t size, void *buf)
+static void
+connection_client_cancel_impl (struct GNUNET_MQ_Handle *mq,
+                               void *impl_state)
 {
 {
-  struct TransmitGetResponseContext *tc = cls;
-  uint16_t msize;
-
-  tc->sock->tag = NULL;
-  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
-      tc->rn (tc->rn_cls, NULL);
-      GNUNET_free (tc);
-      return 0;
-    }
-  GNUNET_assert (size >= msize);
-  memcpy (buf, tc->hdr, msize);
-  GNUNET_CLIENT_receive (tc->sock,
-                         tc->rn,
-                         tc->rn_cls,
-                         GNUNET_TIME_absolute_get_remaining (tc->timeout));
-  GNUNET_free (tc);
-  return msize;
+  struct ClientState *cstate = impl_state;
+
+  GNUNET_assert (NULL != cstate->msg);
+  GNUNET_assert (0 == cstate->msg_off);
+  cstate->msg = NULL;
+  if (NULL != cstate->send_task)
+  {
+    GNUNET_SCHEDULER_cancel (cstate->send_task);
+    cstate->send_task = NULL;
+  }
 }
 
 
 /**
 }
 
 
 /**
- * Convenience API that combines sending a request
- * to the service and waiting for a response.
- * If either operation times out, the callback
- * will be called with a "NULL" response (in which
- * case the connection should probably be destroyed).
+ * Create a message queue to connect to a GNUnet service.
+ * If handlers are specfied, receive messages from the connection.
  *
  *
- * @param sock connection to use
- * @param hdr message to transmit
- * @param timeout when to give up (for both transmission
- *         and for waiting for a response)
- * @param auto_retry if the connection to the service dies, should we
- *        automatically re-connect and retry (within the timeout period)
- *        or should we immediately fail in this case?  Pass GNUNET_YES
- *        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
- *         is already pending
+ * @param cfg our configuration
+ * @param service_name name of the service to connect to
+ * @param handlers handlers for receiving messages, can be NULL
+ * @param error_handler error handler
+ * @param error_handler_cls closure for the @a error_handler
+ * @return the message queue, NULL on error
  */
  */
-int
-GNUNET_CLIENT_transmit_and_get_response (struct GNUNET_CLIENT_Connection
-                                         *sock,
-                                         const struct GNUNET_MessageHeader
-                                         *hdr,
-                                         struct GNUNET_TIME_Relative timeout,
-                                         int auto_retry,
-                                         GNUNET_CLIENT_MessageHandler rn,
-                                         void *rn_cls)
+struct GNUNET_MQ_Handle *
+GNUNET_CLIENT_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                      const char *service_name,
+                      const struct GNUNET_MQ_MessageHandler *handlers,
+                      GNUNET_MQ_ErrorHandler error_handler,
+                      void *error_handler_cls)
 {
 {
-  struct TransmitGetResponseContext *tc;
-  uint16_t msize;
+  struct ClientState *cstate;
 
 
-  if (NULL != sock->th)
-    return GNUNET_SYSERR;
-  GNUNET_assert (sock->tag == NULL);
-  msize = ntohs (hdr->size);
-  tc = GNUNET_malloc (sizeof (struct TransmitGetResponseContext) + msize);
-  tc->sock = sock;
-  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))
+  if (GNUNET_OK !=
+      test_service_configuration (service_name,
+                                 cfg))
+    return NULL;
+  cstate = GNUNET_new (struct ClientState);
+  cstate->service_name = GNUNET_strdup (service_name);
+  cstate->cfg = cfg;
+  cstate->retry_task = GNUNET_SCHEDULER_add_now (&start_connect,
+                                                 cstate);
+  cstate->mst = GNUNET_MST_create (&recv_message,
+                                   cstate);
+  if (GNUNET_YES ==
+      GNUNET_CONFIGURATION_have_value (cfg,
+                                       service_name,
+                                       "PORT"))
+  {
+    if (! ( (GNUNET_OK !=
+            GNUNET_CONFIGURATION_get_value_number (cfg,
+                                                   service_name,
+                                                   "PORT",
+                                                   &cstate->port)) ||
+           (cstate->port > 65535) ||
+           (GNUNET_OK !=
+            GNUNET_CONFIGURATION_get_value_string (cfg,
+                                                   service_name,
+                                                   "HOSTNAME",
+                                                   &cstate->hostname)) ) &&
+       (0 == strlen (cstate->hostname)) )
     {
     {
-      GNUNET_break (0);
-      GNUNET_free (tc);
-      return GNUNET_SYSERR;
+      GNUNET_free (cstate->hostname);
+      cstate->hostname = NULL;
+      LOG (GNUNET_ERROR_TYPE_WARNING,
+          _("Need a non-empty hostname for service `%s'.\n"),
+          service_name);
     }
     }
-  sock->tag = tc;
-  return GNUNET_OK;
+  }
+  cstate->mq = GNUNET_MQ_queue_for_callbacks (&connection_client_send_impl,
+                                             &connection_client_destroy_impl,
+                                             &connection_client_cancel_impl,
+                                             cstate,
+                                             handlers,
+                                             error_handler,
+                                             error_handler_cls);
+  return cstate->mq;
 }
 
 }
 
-
-
-/*  end of client.c */
+/* end of client.c */