Merge branch 'master'
[oweals/gnunet.git] / src / util / client.c
index 5131ce5247be5ea66433101967877a59dd670503..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 LOG(kind,...) GNUNET_log_from (kind, "util-client",__VA_ARGS__)
 
 
-#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.
+ * Internal state for a client connected to a GNUnet service.
  */
  */
-#define MAX_ATTEMPTS 50
+struct ClientState;
 
 
 /**
 
 
 /**
- * Handle for a transmission request.
+ * During connect, we try multiple possible IP addresses
+ * to find out which one might work.
  */
  */
-struct GNUNET_CLIENT_TransmitHandle
+struct AddressProbe
 {
 {
-  /**
-   * Connection state.
-   */
-  struct GNUNET_CLIENT_Connection *sock;
 
   /**
 
   /**
-   * Function to call to get the data for transmission.
+   * This is a linked list.
    */
    */
-  GNUNET_CONNECTION_TransmitReadyNotify notify;
+  struct AddressProbe *next;
 
   /**
 
   /**
-   * Closure for notify.
+   * This is a doubly-linked list.
    */
    */
-  void *notify_cls;
+  struct AddressProbe *prev;
 
   /**
 
   /**
-   * Handle to the transmission with the underlying
-   * connection.
+   * The address; do not free (allocated at the end of this struct).
    */
    */
-  struct GNUNET_CONNECTION_TransmitHandle *th;
+  const struct sockaddr *addr;
 
   /**
 
   /**
-   * If we are re-trying and are delaying to do so,
-   * handle to the scheduled task managing the delay.
+   * Underlying OS's socket.
    */
    */
-  GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
+  struct GNUNET_NETWORK_Handle *sock;
 
   /**
 
   /**
-   * Timeout for the operation overall.
+   * Connection for which we are probing.
    */
    */
-  struct GNUNET_TIME_Absolute timeout;
+  struct ClientState *cstate;
 
   /**
 
   /**
-   * Number of bytes requested.
+   * Lenth of addr.
    */
    */
-  size_t size;
+  socklen_t addrlen;
 
   /**
 
   /**
-   * Are we allowed to re-try to connect without telling
-   * the user (of this API) about the connection troubles?
+   * Task waiting for the connection to finish connecting.
    */
    */
-  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 GNUNET_SCHEDULER_Task *task;
 };
 
 
 /**
 };
 
 
 /**
- * Context for processing 
- * "GNUNET_CLIENT_transmit_and_get_response" requests.
+ * Internal state for a client connected to a GNUnet service.
  */
  */
-struct TransmitGetResponseContext
+struct ClientState
 {
 {
-  /**
-   * Client handle.
-   */
-  struct GNUNET_CLIENT_Connection *sock;
 
   /**
 
   /**
-   * Message to transmit; do not free, allocated
-   * right after this struct.
+   * The connection handle, NULL if not live
    */
    */
-  const struct GNUNET_MessageHeader *hdr;
+  struct GNUNET_NETWORK_Handle *sock;
 
   /**
 
   /**
-   * Timeout to use.
+   * Handle to a pending DNS lookup request, NULL if DNS is finished.
    */
    */
-  struct GNUNET_TIME_Absolute timeout;
+  struct GNUNET_RESOLVER_RequestHandle *dns_active;
 
   /**
 
   /**
-   * Function to call when done.
-   */
-  GNUNET_CLIENT_MessageHandler rn;
-
-  /**
-   * Closure for "rn".
-   */
-  void *rn_cls;
-};
-
-/**
- * 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).
- */
-struct GNUNET_CLIENT_Connection
-{
-
-  /**
-   * the socket handle, NULL if not live
+   * Our configuration.
    */
    */
-  struct GNUNET_CONNECTION_Handle *sock;
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
 
   /**
 
   /**
-   * Our scheduler.
+   * Linked list of sockets we are currently trying out
+   * (during connect).
    */
    */
-  struct GNUNET_SCHEDULER_Handle *sched;
+  struct AddressProbe *ap_head;
 
   /**
 
   /**
-   * Our configuration.
+   * Linked list of sockets we are currently trying out
+   * (during connect).
    */
    */
-  struct GNUNET_CONFIGURATION_Handle *cfg;
+  struct AddressProbe *ap_tail;
 
   /**
    * Name of the service we interact with.
 
   /**
    * Name of the service we interact with.
@@ -166,52 +125,39 @@ struct GNUNET_CLIENT_Connection
   char *service_name;
 
   /**
   char *service_name;
 
   /**
-   * Context of a transmit_and_get_response operation, NULL
-   * if no such operation is pending.
-   */
-  struct TransmitGetResponseContext *tag;
-
-  /**
-   * Handler for current receiver task.
-   */
-  GNUNET_CLIENT_MessageHandler receiver_handler;
-
-  /**
-   * 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).
@@ -225,752 +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.
+   * TCP port (0 for disabled).
    */
    */
-  size_t received_pos;
-
-  /**
-   * Size of received_buf.
-   */
-  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
+ * @param cls the `struct ClientState` with the `msg` to transmit
  */
  */
-void
-GNUNET_CLIENT_disconnect (struct GNUNET_CLIENT_Connection *sock,
-                         int finish_pending_write)
+static void
+transmit_ready (void *cls)
 {
 {
-  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);
+  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);
 }
 
 
 /**
 }
 
 
 /**
- * Check if message is complete
+ * We have received a full message, pass to the MQ dispatcher.
+ * Called by the tokenizer via #receive_ready().
+ *
+ * @param cls the `struct ClientState`
+ * @param msg message we received.
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR to stop further processing
  */
  */
-static void
-check_complete (struct GNUNET_CLIENT_Connection *conn)
+static int
+recv_message (void *cls,
+              const struct GNUNET_MessageHeader *msg)
 {
 {
-  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;
+
+  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;
 }
 
 
 /**
 }
 
 
 /**
- * Callback function for data received from the network.  Note that
- * both "available" and "errCode" would be 0 if the read simply timed out.
+ * Cancel all remaining connect attempts
  *
  *
- * @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 cstate handle of the client state to process
  */
 static void
  */
 static void
-receive_helper (void *cls,
-                const void *buf,
-                size_t available,
-                const struct sockaddr *addr, socklen_t addrlen, int errCode)
+cancel_aps (struct ClientState *cstate)
 {
 {
-  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... */
-
-
-  /* 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);
+  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);
+  }
 }
 
 
 /**
 }
 
 
 /**
- * Continuation to call the receive callback.
+ * Implement the destruction of a message queue.  Implementations must
+ * not free @a mq, but should take care of @a impl_state.
  *
  *
- * @param cls  our handle to the client connection
- * @param tc scheduler context
+ * @param mq the message queue to destroy
+ * @param impl_state our `struct ClientState`
  */
 static void
  */
 static void
-receive_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+connection_client_destroy_impl (struct GNUNET_MQ_Handle *mq,
+                                void *impl_state)
 {
 {
-  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 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);
 }
 
 
 /**
 }
 
 
 /**
- * Read from the service.
+ * This function is called once we have data ready to read.
  *
  *
- * @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
+ * @param cls `struct ClientState` with connection to read from
  */
  */
-void
-GNUNET_CLIENT_receive (struct GNUNET_CLIENT_Connection *sock,
-                       GNUNET_CLIENT_MessageHandler handler,
-                       void *handler_cls, struct GNUNET_TIME_Relative timeout)
+static void
+receive_ready (void *cls)
 {
 {
-  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);
-    }
+  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);
 }
 
 
 /**
 }
 
 
 /**
- * Report service unavailable.
+ * We've succeeded in establishing a connection.
+ *
+ * @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);
-}
-
-
-static size_t
-write_test (void *cls, size_t size, void *buf)
-{
-  struct GNUNET_CLIENT_Connection *conn = cls;
-  struct GNUNET_MessageHeader *msg;
+      int abstract;
 
 
-  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)
+    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_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))  
-    {
-      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 */