Don't shadow the system() function
[oweals/gnunet.git] / src / util / server.c
index 4b3ebb7d970a44834a60d6c77072b886b0ad3290..1a4b73126397f1c9d8c6076824fc014b1dce857c 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet.
 /*
      This file is part of GNUnet.
-     (C) 2009 Christian Grothoff (and other contributing authors)
+     Copyright (C) 2009-2013 Christian Grothoff (and other contributing authors)
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
 
      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
  */
 
 #include "platform.h"
  */
 
 #include "platform.h"
-#include "gnunet_common.h"
-#include "gnunet_connection_lib.h"
-#include "gnunet_scheduler_lib.h"
-#include "gnunet_server_lib.h"
-#include "gnunet_time_lib.h"
-#include "gnunet_disk_lib.h"
+#include "gnunet_util_lib.h"
 #include "gnunet_protocols.h"
 
 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
 #include "gnunet_protocols.h"
 
 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
@@ -63,10 +58,15 @@ struct HandlerList
 struct NotifyList
 {
   /**
 struct NotifyList
 {
   /**
-   * This is a linked list.
+   * This is a doubly linked list.
    */
   struct NotifyList *next;
 
    */
   struct NotifyList *next;
 
+  /**
+   * This is a doubly linked list.
+   */
+  struct NotifyList *prev;
+
   /**
    * Function to call.
    */
   /**
    * Function to call.
    */
@@ -90,24 +90,44 @@ struct GNUNET_SERVER_Handle
   struct HandlerList *handlers;
 
   /**
   struct HandlerList *handlers;
 
   /**
-   * List of our current clients.
+   * Head of list of our current clients.
+   */
+  struct GNUNET_SERVER_Client *clients_head;
+
+  /**
+   * Head of list of our current clients.
+   */
+  struct GNUNET_SERVER_Client *clients_tail;
+
+  /**
+   * Head of linked list of functions to call on disconnects by clients.
    */
    */
-  struct GNUNET_SERVER_Client *clients;
+  struct NotifyList *disconnect_notify_list_head;
 
   /**
 
   /**
-   * Linked list of functions to call on disconnects by clients.
+   * Tail of linked list of functions to call on disconnects by clients.
    */
    */
-  struct NotifyList *disconnect_notify_list;
+  struct NotifyList *disconnect_notify_list_tail;
+
+  /**
+   * Head of linked list of functions to call on connects by clients.
+   */
+  struct NotifyList *connect_notify_list_head;
+
+  /**
+   * Tail of linked list of functions to call on connects by clients.
+   */
+  struct NotifyList *connect_notify_list_tail;
 
   /**
    * Function to call for access control.
    */
 
   /**
    * Function to call for access control.
    */
-  GNUNET_CONNECTION_AccessCheck access;
+  GNUNET_CONNECTION_AccessCheck access_cb;
 
   /**
 
   /**
-   * Closure for access.
+   * Closure for @e access_cb.
    */
    */
-  void *access_cls;
+  void *access_cb_cls;
 
   /**
    * NULL-terminated array of sockets used to listen for new
 
   /**
    * NULL-terminated array of sockets used to listen for new
@@ -124,7 +144,27 @@ struct GNUNET_SERVER_Handle
   /**
    * Task scheduled to do the listening.
    */
   /**
    * Task scheduled to do the listening.
    */
-  GNUNET_SCHEDULER_TaskIdentifier listen_task;
+  struct GNUNET_SCHEDULER_Task * listen_task;
+
+  /**
+   * Alternative function to create a MST instance.
+   */
+  GNUNET_SERVER_MstCreateCallback mst_create;
+
+  /**
+   * Alternative function to destroy a MST instance.
+   */
+  GNUNET_SERVER_MstDestroyCallback mst_destroy;
+
+  /**
+   * Alternative function to give data to a MST instance.
+   */
+  GNUNET_SERVER_MstReceiveCallback mst_receive;
+
+  /**
+   * Closure for 'mst_'-callbacks.
+   */
+  void *mst_cls;
 
   /**
    * Do we ignore messages of types that we do not understand or do we
 
   /**
    * Do we ignore messages of types that we do not understand or do we
@@ -133,16 +173,36 @@ struct GNUNET_SERVER_Handle
   int require_found;
 
   /**
   int require_found;
 
   /**
-   * Should all of the clients of this server continue to process
-   * connections as usual even if we get a shutdown request? (the
-   * listen socket always ignores shutdown).
+   * Set to #GNUNET_YES once we are in 'soft' shutdown where we wait for
+   * all non-monitor clients to disconnect before we call
+   * #GNUNET_SERVER_destroy.  See test_monitor_clients().  Set to
+   * #GNUNET_SYSERR once the final destroy task has been scheduled
+   * (we cannot run it in the same task).
    */
    */
-  int clients_ignore_shutdown;
+  int in_soft_shutdown;
+};
+
+
+/**
+ * Handle server returns for aborting transmission to a client.
+ */
+struct GNUNET_SERVER_TransmitHandle
+{
+  /**
+   * Function to call to get the message.
+   */
+  GNUNET_CONNECTION_TransmitReadyNotify callback;
+
+  /**
+   * Closure for @e callback
+   */
+  void *callback_cls;
+
+  /**
+   * Active connection transmission handle.
+   */
+  struct GNUNET_CONNECTION_TransmitHandle *cth;
 
 
-  GNUNET_SERVER_MstCreateCallback mst_create;
-  GNUNET_SERVER_MstDestroyCallback mst_destroy;
-  GNUNET_SERVER_MstReceiveCallback mst_receive;
-  void *mst_cls;
 };
 
 
 };
 
 
@@ -153,10 +213,15 @@ struct GNUNET_SERVER_Client
 {
 
   /**
 {
 
   /**
-   * This is a linked list.
+   * This is a doubly linked list.
    */
   struct GNUNET_SERVER_Client *next;
 
    */
   struct GNUNET_SERVER_Client *next;
 
+  /**
+   * This is a doubly linked list.
+   */
+  struct GNUNET_SERVER_Client *prev;
+
   /**
    * Processing of incoming data.
    */
   /**
    * Processing of incoming data.
    */
@@ -172,15 +237,21 @@ struct GNUNET_SERVER_Client
    */
   struct GNUNET_CONNECTION_Handle *connection;
 
    */
   struct GNUNET_CONNECTION_Handle *connection;
 
+  /**
+   * User context value, manipulated using
+   * 'GNUNET_SERVER_client_{get/set}_user_context' functions.
+   */
+  void *user_context;
+
   /**
    * ID of task used to restart processing.
    */
   /**
    * ID of task used to restart processing.
    */
-  GNUNET_SCHEDULER_TaskIdentifier restart_task;
+  struct GNUNET_SCHEDULER_Task * restart_task;
 
   /**
 
   /**
-   * Task that warns about missing calls to 'GNUNET_SERVER_receive_done'.
+   * Task that warns about missing calls to #GNUNET_SERVER_receive_done.
    */
    */
-  GNUNET_SCHEDULER_TaskIdentifier warn_task;
+  struct GNUNET_SCHEDULER_Task * warn_task;
 
   /**
    * Time when the warn task was started.
 
   /**
    * Time when the warn task was started.
@@ -194,14 +265,10 @@ struct GNUNET_SERVER_Client
   struct GNUNET_TIME_Absolute last_activity;
 
   /**
   struct GNUNET_TIME_Absolute last_activity;
 
   /**
-   *
-   */
-  GNUNET_CONNECTION_TransmitReadyNotify callback;
-
-  /**
-   * callback
+   * Transmission handle we return for this client from
+   * #GNUNET_SERVER_notify_transmit_ready.
    */
    */
-  void *callback_cls;
+  struct GNUNET_SERVER_TransmitHandle th;
 
   /**
    * After how long should an idle connection time
 
   /**
    * After how long should an idle connection time
@@ -223,6 +290,12 @@ struct GNUNET_SERVER_Client
    */
   unsigned int suspended;
 
    */
   unsigned int suspended;
 
+  /**
+   * Last size given when user context was initialized; used for
+   * sanity check.
+   */
+  size_t user_context_size;
+
   /**
    * Are we currently in the "process_client_buffer" function (and
    * will hence restart the receive job on exit if suspended == 0 once
   /**
    * Are we currently in the "process_client_buffer" function (and
    * will hence restart the receive job on exit if suspended == 0 once
@@ -234,22 +307,17 @@ struct GNUNET_SERVER_Client
   int in_process_client_buffer;
 
   /**
   int in_process_client_buffer;
 
   /**
-   * We're about to close down this client due to some serious
-   * error.
+   * We're about to close down this client.
    */
   int shutdown_now;
 
   /**
    */
   int shutdown_now;
 
   /**
-   * Are we currently trying to receive? (YES if we are, NO if we are not,
-   * SYSERR if data is already available in MST).
+   * Are we currently trying to receive? (#GNUNET_YES if we are,
+   * #GNUNET_NO if we are not, #GNUNET_SYSERR if data is already
+   * available in MST).
    */
   int receive_pending;
 
    */
   int receive_pending;
 
-  /**
-   * Finish pending write when disconnecting?
-   */
-  int finish_pending_write;
-
   /**
    * Persist the file handle for this client no matter what happens,
    * force the OS to close once the process actually dies.  Should only
   /**
    * Persist the file handle for this client no matter what happens,
    * force the OS to close once the process actually dies.  Should only
@@ -257,6 +325,13 @@ struct GNUNET_SERVER_Client
    */
   int persist;
 
    */
   int persist;
 
+  /**
+   * Is this client a 'monitor' client that should not be counted
+   * when deciding on destroying the server during soft shutdown?
+   * (see also #GNUNET_SERVICE_start)
+   */
+  int is_monitor;
+
   /**
    * Type of last message processed (for warn_no_receive_done).
    */
   /**
    * Type of last message processed (for warn_no_receive_done).
    */
@@ -264,6 +339,51 @@ struct GNUNET_SERVER_Client
 };
 
 
 };
 
 
+
+/**
+ * Return user context associated with the given client.
+ * Note: you should probably use the macro (call without the underscore).
+ *
+ * @param client client to query
+ * @param size number of bytes in user context struct (for verification only)
+ * @return pointer to user context
+ */
+void *
+GNUNET_SERVER_client_get_user_context_ (struct GNUNET_SERVER_Client *client,
+                                       size_t size)
+{
+  if ((0 == client->user_context_size) &&
+      (NULL == client->user_context))
+    return NULL; /* never set */
+  GNUNET_assert (size == client->user_context_size);
+  return client->user_context;
+}
+
+
+/**
+ * Set user context to be associated with the given client.
+ * Note: you should probably use the macro (call without the underscore).
+ *
+ * @param client client to query
+ * @param ptr pointer to user context
+ * @param size number of bytes in user context struct (for verification only)
+ */
+void
+GNUNET_SERVER_client_set_user_context_ (struct GNUNET_SERVER_Client *client,
+                                       void *ptr,
+                                       size_t size)
+{
+  if (NULL == ptr)
+  {
+    client->user_context_size = 0;
+    client->user_context = ptr;
+    return;
+  }
+  client->user_context_size = size;
+  client->user_context = ptr;
+}
+
+
 /**
  * Scheduler says our listen socket is ready.  Process it!
  *
 /**
  * Scheduler says our listen socket is ready.  Process it!
  *
@@ -272,83 +392,64 @@ struct GNUNET_SERVER_Client
  * @param tc reason why we are running right now
  */
 static void
  * @param tc reason why we are running right now
  */
 static void
-process_listen_socket (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+process_listen_socket (void *cls,
+                       const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct GNUNET_SERVER_Handle *server = cls;
   struct GNUNET_CONNECTION_Handle *sock;
 {
   struct GNUNET_SERVER_Handle *server = cls;
   struct GNUNET_CONNECTION_Handle *sock;
-  struct GNUNET_SERVER_Client *client;
-  struct GNUNET_NETWORK_FDSet *r;
   unsigned int i;
 
   unsigned int i;
 
-  server->listen_task = GNUNET_SCHEDULER_NO_TASK;
-  r = GNUNET_NETWORK_fdset_create ();
-  i = 0;
-  while (NULL != server->listen_sockets[i])
-    GNUNET_NETWORK_fdset_set (r, server->listen_sockets[i++]);
+  server->listen_task = NULL;
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
   {
     /* ignore shutdown, someone else will take care of it! */
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
   {
     /* ignore shutdown, someone else will take care of it! */
-    server->listen_task =
-        GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
-                                     GNUNET_SCHEDULER_NO_TASK,
-                                     GNUNET_TIME_UNIT_FOREVER_REL, r, NULL,
-                                     &process_listen_socket, server);
-    GNUNET_NETWORK_fdset_destroy (r);
+    GNUNET_SERVER_resume (server);
     return;
   }
     return;
   }
-  i = 0;
-  while (NULL != server->listen_sockets[i])
+  for (i = 0; NULL != server->listen_sockets[i]; i++)
   {
   {
-    if (GNUNET_NETWORK_fdset_isset (tc->read_ready, server->listen_sockets[i]))
+    if (GNUNET_NETWORK_fdset_isset (tc->read_ready,
+                                    server->listen_sockets[i]))
     {
       sock =
     {
       sock =
-          GNUNET_CONNECTION_create_from_accept (server->access,
-                                                server->access_cls,
+          GNUNET_CONNECTION_create_from_accept (server->access_cb,
+                                                server->access_cb_cls,
                                                 server->listen_sockets[i]);
                                                 server->listen_sockets[i]);
-      if (sock != NULL)
+      if (NULL != sock)
       {
       {
-        LOG (GNUNET_ERROR_TYPE_DEBUG, "Server accepted incoming connection.\n");
-        client = GNUNET_SERVER_connect_socket (server, sock);
-        GNUNET_CONNECTION_ignore_shutdown (sock,
-                                           server->clients_ignore_shutdown);
-        /* decrement reference count, we don't keep "client" alive */
-        GNUNET_SERVER_client_drop (client);
+        LOG (GNUNET_ERROR_TYPE_DEBUG,
+             "Server accepted incoming connection.\n");
+        (void) GNUNET_SERVER_connect_socket (server,
+                                             sock);
       }
     }
       }
     }
-    i++;
   }
   /* listen for more! */
   }
   /* listen for more! */
-  server->listen_task =
-      GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
-                                   GNUNET_SCHEDULER_NO_TASK,
-                                   GNUNET_TIME_UNIT_FOREVER_REL, r, NULL,
-                                   &process_listen_socket, server);
-  GNUNET_NETWORK_fdset_destroy (r);
+  GNUNET_SERVER_resume (server);
 }
 
 
 /**
  * Create and initialize a listen socket for the server.
  *
 }
 
 
 /**
  * Create and initialize a listen socket for the server.
  *
- * @param serverAddr address to listen on
- * @param socklen length of address
+ * @param server_addr address to listen on
+ * @param socklen length of @a server_addr
  * @return NULL on error, otherwise the listen socket
  */
 static struct GNUNET_NETWORK_Handle *
  * @return NULL on error, otherwise the listen socket
  */
 static struct GNUNET_NETWORK_Handle *
-open_listen_socket (const struct sockaddr *serverAddr, socklen_t socklen)
+open_listen_socket (const struct sockaddr *server_addr, socklen_t socklen)
 {
 {
-  const static int on = 1;
   struct GNUNET_NETWORK_Handle *sock;
   uint16_t port;
   int eno;
 
   struct GNUNET_NETWORK_Handle *sock;
   uint16_t port;
   int eno;
 
-  switch (serverAddr->sa_family)
+  switch (server_addr->sa_family)
   {
   case AF_INET:
   {
   case AF_INET:
-    port = ntohs (((const struct sockaddr_in *) serverAddr)->sin_port);
+    port = ntohs (((const struct sockaddr_in *) server_addr)->sin_port);
     break;
   case AF_INET6:
     break;
   case AF_INET6:
-    port = ntohs (((const struct sockaddr_in6 *) serverAddr)->sin6_port);
+    port = ntohs (((const struct sockaddr_in6 *) server_addr)->sin6_port);
     break;
   case AF_UNIX:
     port = 0;
     break;
   case AF_UNIX:
     port = 0;
@@ -358,56 +459,47 @@ open_listen_socket (const struct sockaddr *serverAddr, socklen_t socklen)
     port = 0;
     break;
   }
     port = 0;
     break;
   }
-  sock = GNUNET_NETWORK_socket_create (serverAddr->sa_family, SOCK_STREAM, 0);
+  sock = GNUNET_NETWORK_socket_create (server_addr->sa_family, SOCK_STREAM, 0);
   if (NULL == sock)
   {
     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket");
     errno = 0;
     return NULL;
   }
   if (NULL == sock)
   {
     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket");
     errno = 0;
     return NULL;
   }
-  if (port != 0)
-  {
-    if (GNUNET_NETWORK_socket_setsockopt
-        (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
-      LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
-                    "setsockopt");
-#ifdef IPV6_V6ONLY
-    if ((serverAddr->sa_family == AF_INET6) &&
-        (GNUNET_NETWORK_socket_setsockopt
-         (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK))
-      LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
-                    "setsockopt");
-#endif
-  }
   /* bind the socket */
   /* bind the socket */
-  if (GNUNET_NETWORK_socket_bind (sock, serverAddr, socklen) != GNUNET_OK)
+  if (GNUNET_OK != GNUNET_NETWORK_socket_bind (sock, server_addr, socklen))
   {
     eno = errno;
   {
     eno = errno;
-    if (errno != EADDRINUSE)
+    if (EADDRINUSE != errno)
     {
       /* we don't log 'EADDRINUSE' here since an IPv4 bind may
        * fail if we already took the port on IPv6; if both IPv4 and
        * IPv6 binds fail, then our caller will log using the
        * errno preserved in 'eno' */
     {
       /* we don't log 'EADDRINUSE' here since an IPv4 bind may
        * fail if we already took the port on IPv6; if both IPv4 and
        * IPv6 binds fail, then our caller will log using the
        * errno preserved in 'eno' */
-      LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "bind");
-      if (port != 0)
-        LOG (GNUNET_ERROR_TYPE_ERROR, _("`%s' failed for port %d (%s).\n"),
-             "bind", port,
-             (serverAddr->sa_family == AF_INET) ? "IPv4" : "IPv6");
+      LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
+                    "bind");
+      if (0 != port)
+        LOG (GNUNET_ERROR_TYPE_ERROR,
+             _("`%s' failed for port %d (%s).\n"),
+             "bind",
+             port,
+             (AF_INET == server_addr->sa_family) ? "IPv4" : "IPv6");
       eno = 0;
     }
     else
     {
       eno = 0;
     }
     else
     {
-      if (port != 0)
+      if (0 != port)
         LOG (GNUNET_ERROR_TYPE_WARNING,
              _("`%s' failed for port %d (%s): address already in use\n"),
              "bind", port,
         LOG (GNUNET_ERROR_TYPE_WARNING,
              _("`%s' failed for port %d (%s): address already in use\n"),
              "bind", port,
-             (serverAddr->sa_family == AF_INET) ? "IPv4" : "IPv6");
-      else if (serverAddr->sa_family == AF_UNIX)
+             (AF_INET == server_addr->sa_family) ? "IPv4" : "IPv6");
+      else if (AF_UNIX == server_addr->sa_family)
+      {
         LOG (GNUNET_ERROR_TYPE_WARNING,
         LOG (GNUNET_ERROR_TYPE_WARNING,
-             _("`%s' failed for `%s': address already in use\n"), "bind",
-             ((const struct sockaddr_un *) serverAddr)->sun_path);
-
+             _("`%s' failed for `%s': address already in use\n"),
+             "bind",
+             GNUNET_a2s (server_addr, socklen));
+      }
     }
     GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
     errno = eno;
     }
     GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
     errno = eno;
@@ -415,13 +507,15 @@ open_listen_socket (const struct sockaddr *serverAddr, socklen_t socklen)
   }
   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (sock, 5))
   {
   }
   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (sock, 5))
   {
-    LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "listen");
+    LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
+                  "listen");
     GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
     errno = 0;
     return NULL;
   }
     GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
     errno = 0;
     return NULL;
   }
-  if (port != 0)
-    LOG (GNUNET_ERROR_TYPE_DEBUG, "Server starts to listen on port %u.\n",
+  if (0 != port)
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Server starts to listen on port %u.\n",
          port);
   return sock;
 }
          port);
   return sock;
 }
@@ -430,56 +524,43 @@ open_listen_socket (const struct sockaddr *serverAddr, socklen_t socklen)
 /**
  * Create a new server.
  *
 /**
  * Create a new server.
  *
- * @param access function for access control
- * @param access_cls closure for access
+ * @param access_cb function for access control
+ * @param access_cb_cls closure for @a access_cb
  * @param lsocks NULL-terminated array of listen sockets
  * @param idle_timeout after how long should we timeout idle connections?
  * @param lsocks NULL-terminated array of listen sockets
  * @param idle_timeout after how long should we timeout idle connections?
- * @param require_found if YES, connections sending messages of unknown type
+ * @param require_found if #GNUNET_YES, connections sending messages of unknown type
  *        will be closed
  * @return handle for the new server, NULL on error
  *         (typically, "port" already in use)
  */
 struct GNUNET_SERVER_Handle *
  *        will be closed
  * @return handle for the new server, NULL on error
  *         (typically, "port" already in use)
  */
 struct GNUNET_SERVER_Handle *
-GNUNET_SERVER_create_with_sockets (GNUNET_CONNECTION_AccessCheck access,
-                                   void *access_cls,
+GNUNET_SERVER_create_with_sockets (GNUNET_CONNECTION_AccessCheck access_cb,
+                                   void *access_cb_cls,
                                    struct GNUNET_NETWORK_Handle **lsocks,
                                    struct GNUNET_TIME_Relative idle_timeout,
                                    int require_found)
 {
                                    struct GNUNET_NETWORK_Handle **lsocks,
                                    struct GNUNET_TIME_Relative idle_timeout,
                                    int require_found)
 {
-  struct GNUNET_SERVER_Handle *ret;
-  struct GNUNET_NETWORK_FDSet *r;
-  int i;
-
-  ret = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Handle));
-  ret->idle_timeout = idle_timeout;
-  ret->listen_sockets = lsocks;
-  ret->access = access;
-  ret->access_cls = access_cls;
-  ret->require_found = require_found;
-  if (lsocks != NULL)
-  {
-    r = GNUNET_NETWORK_fdset_create ();
-    i = 0;
-    while (NULL != ret->listen_sockets[i])
-      GNUNET_NETWORK_fdset_set (r, ret->listen_sockets[i++]);
-    ret->listen_task =
-        GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
-                                     GNUNET_SCHEDULER_NO_TASK,
-                                     GNUNET_TIME_UNIT_FOREVER_REL, r, NULL,
-                                     &process_listen_socket, ret);
-    GNUNET_NETWORK_fdset_destroy (r);
-  }
-  return ret;
+  struct GNUNET_SERVER_Handle *server;
+
+  server = GNUNET_new (struct GNUNET_SERVER_Handle);
+  server->idle_timeout = idle_timeout;
+  server->listen_sockets = lsocks;
+  server->access_cb = access_cb;
+  server->access_cb_cls = access_cb_cls;
+  server->require_found = require_found;
+  if (NULL != lsocks)
+    GNUNET_SERVER_resume (server);
+  return server;
 }
 
 
 /**
  * Create a new server.
  *
 }
 
 
 /**
  * Create a new server.
  *
- * @param access function for access control
- * @param access_cls closure for access
- * @param serverAddr address to listen on (including port), NULL terminated array
- * @param socklen length of serverAddr
+ * @param access_cb function for access control
+ * @param access_cb_cls closure for @a access_cb
+ * @param server_addr address to listen on (including port), NULL terminated array
+ * @param socklen length of server_addr
  * @param idle_timeout after how long should we timeout idle connections?
  * @param require_found if YES, connections sending messages of unknown type
  *        will be closed
  * @param idle_timeout after how long should we timeout idle connections?
  * @param require_found if YES, connections sending messages of unknown type
  *        will be closed
@@ -487,8 +568,9 @@ GNUNET_SERVER_create_with_sockets (GNUNET_CONNECTION_AccessCheck access,
  *         (typically, "port" already in use)
  */
 struct GNUNET_SERVER_Handle *
  *         (typically, "port" already in use)
  */
 struct GNUNET_SERVER_Handle *
-GNUNET_SERVER_create (GNUNET_CONNECTION_AccessCheck access, void *access_cls,
-                      struct sockaddr *const *serverAddr,
+GNUNET_SERVER_create (GNUNET_CONNECTION_AccessCheck access_cb,
+                     void *access_cb_cls,
+                      struct sockaddr *const *server_addr,
                       const socklen_t * socklen,
                       struct GNUNET_TIME_Relative idle_timeout,
                       int require_found)
                       const socklen_t * socklen,
                       struct GNUNET_TIME_Relative idle_timeout,
                       int require_found)
@@ -496,25 +578,41 @@ GNUNET_SERVER_create (GNUNET_CONNECTION_AccessCheck access, void *access_cls,
   struct GNUNET_NETWORK_Handle **lsocks;
   unsigned int i;
   unsigned int j;
   struct GNUNET_NETWORK_Handle **lsocks;
   unsigned int i;
   unsigned int j;
+  unsigned int k;
+  int seen;
 
   i = 0;
 
   i = 0;
-  while (serverAddr[i] != NULL)
+  while (NULL != server_addr[i])
     i++;
   if (i > 0)
   {
     lsocks = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle *) * (i + 1));
     i = 0;
     j = 0;
     i++;
   if (i > 0)
   {
     lsocks = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle *) * (i + 1));
     i = 0;
     j = 0;
-    while (serverAddr[i] != NULL)
+    while (NULL != server_addr[i])
     {
     {
-      lsocks[j] = open_listen_socket (serverAddr[i], socklen[i]);
-      if (lsocks[j] != NULL)
+      seen = 0;
+      for (k=0;k<i;k++)
+       if ( (socklen[k] == socklen[i]) &&
+            (0 == memcmp (server_addr[k], server_addr[i], socklen[i])) )
+       {
+         seen = 1;
+         break;
+       }
+      if (0 != seen)
+      {
+       /* duplicate address, skip */
+       i++;
+       continue;
+      }
+      lsocks[j] = open_listen_socket (server_addr[i], socklen[i]);
+      if (NULL != lsocks[j])
         j++;
       i++;
     }
         j++;
       i++;
     }
-    if (j == 0)
+    if (0 == j)
     {
     {
-      if (errno != 0)
+      if (0 != errno)
         LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "bind");
       GNUNET_free (lsocks);
       lsocks = NULL;
         LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "bind");
       GNUNET_free (lsocks);
       lsocks = NULL;
@@ -524,52 +622,214 @@ GNUNET_SERVER_create (GNUNET_CONNECTION_AccessCheck access, void *access_cls,
   {
     lsocks = NULL;
   }
   {
     lsocks = NULL;
   }
-  return GNUNET_SERVER_create_with_sockets (access, access_cls, lsocks,
-                                            idle_timeout, require_found);
+  return GNUNET_SERVER_create_with_sockets (access_cb,
+                                           access_cb_cls,
+                                           lsocks,
+                                            idle_timeout,
+                                           require_found);
+}
+
+
+/**
+ * Set the 'monitor' flag on this client.  Clients which have been
+ * marked as 'monitors' won't prevent the server from shutting down
+ * once '#GNUNET_SERVER_stop_listening()' has been invoked.  The idea is
+ * that for "normal" clients we likely want to allow them to process
+ * their requests; however, monitor-clients are likely to 'never'
+ * disconnect during shutdown and thus will not be considered when
+ * determining if the server should continue to exist after
+ * #GNUNET_SERVER_destroy() has been called.
+ *
+ * @param client the client to set the 'monitor' flag on
+ */
+void
+GNUNET_SERVER_client_mark_monitor (struct GNUNET_SERVER_Client *client)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Marking client as monitor!\n");
+  client->is_monitor = GNUNET_YES;
+}
+
+
+/**
+ * Helper function for #test_monitor_clients() to trigger
+ * #GNUNET_SERVER_destroy() after the stack has unwound.
+ *
+ * @param cls the 'struct GNUNET_SERVER_Handle' to destroy
+ * @param tc unused
+ */
+static void
+do_destroy (void *cls,
+           const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct GNUNET_SERVER_Handle *server = cls;
+
+  GNUNET_SERVER_destroy (server);
+}
+
+
+/**
+ * Check if only 'monitor' clients are left.  If so, destroy the
+ * server completely.
+ *
+ * @param server server to test for full shutdown
+ */
+static void
+test_monitor_clients (struct GNUNET_SERVER_Handle *server)
+{
+  struct GNUNET_SERVER_Client *client;
+
+  if (GNUNET_YES != server->in_soft_shutdown)
+    return;
+  for (client = server->clients_head; NULL != client; client = client->next)
+    if (GNUNET_NO == client->is_monitor)
+      return; /* not done yet */
+  server->in_soft_shutdown = GNUNET_SYSERR;
+  GNUNET_SCHEDULER_add_now (&do_destroy, server);
+}
+
+
+/**
+ * Suspend accepting connections from the listen socket temporarily.
+ *
+ * @param server server to stop accepting connections.
+ */
+void
+GNUNET_SERVER_suspend (struct GNUNET_SERVER_Handle *server)
+{
+  if (NULL != server->listen_task)
+  {
+    GNUNET_SCHEDULER_cancel (server->listen_task);
+    server->listen_task = NULL;
+  }
+}
+
+
+/**
+ * Resume accepting connections from the listen socket.
+ *
+ * @param server server to stop accepting connections.
+ */
+void
+GNUNET_SERVER_resume (struct GNUNET_SERVER_Handle *server)
+{
+  struct GNUNET_NETWORK_FDSet *r;
+  unsigned int i;
+
+  if (NULL == server->listen_sockets)
+    return;
+  if (NULL == server->listen_sockets[0])
+    return; /* nothing to do, no listen sockets! */
+  if (NULL == server->listen_sockets[1])
+  {
+    /* simplified method: no fd set needed; this is then much simpler and
+       much more efficient */
+    server->listen_task =
+      GNUNET_SCHEDULER_add_read_net_with_priority (GNUNET_TIME_UNIT_FOREVER_REL,
+                                                  GNUNET_SCHEDULER_PRIORITY_HIGH,
+                                                  server->listen_sockets[0],
+                                                  &process_listen_socket, server);
+    return;
+  }
+  r = GNUNET_NETWORK_fdset_create ();
+  i = 0;
+  while (NULL != server->listen_sockets[i])
+    GNUNET_NETWORK_fdset_set (r, server->listen_sockets[i++]);
+  server->listen_task =
+    GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
+                                GNUNET_TIME_UNIT_FOREVER_REL, r, NULL,
+                                &process_listen_socket, server);
+  GNUNET_NETWORK_fdset_destroy (r);
+}
+
+
+/**
+ * Stop the listen socket and get ready to shutdown the server
+ * once only 'monitor' clients are left.
+ *
+ * @param server server to stop listening on
+ */
+void
+GNUNET_SERVER_stop_listening (struct GNUNET_SERVER_Handle *server)
+{
+  unsigned int i;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Server in soft shutdown\n");
+  if (NULL != server->listen_task)
+  {
+    GNUNET_SCHEDULER_cancel (server->listen_task);
+    server->listen_task = NULL;
+  }
+  if (NULL != server->listen_sockets)
+  {
+    i = 0;
+    while (NULL != server->listen_sockets[i])
+      GNUNET_break (GNUNET_OK ==
+                    GNUNET_NETWORK_socket_close (server->listen_sockets[i++]));
+    GNUNET_free (server->listen_sockets);
+    server->listen_sockets = NULL;
+  }
+  if (GNUNET_NO == server->in_soft_shutdown)
+    server->in_soft_shutdown = GNUNET_YES;
+  test_monitor_clients (server);
 }
 
 
 /**
  * Free resources held by this server.
  *
 }
 
 
 /**
  * Free resources held by this server.
  *
- * @param s server to destroy
+ * @param server server to destroy
  */
 void
  */
 void
-GNUNET_SERVER_destroy (struct GNUNET_SERVER_Handle *s)
+GNUNET_SERVER_destroy (struct GNUNET_SERVER_Handle *server)
 {
   struct HandlerList *hpos;
   struct NotifyList *npos;
   unsigned int i;
 
 {
   struct HandlerList *hpos;
   struct NotifyList *npos;
   unsigned int i;
 
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "Server shutting down.\n");
-  if (GNUNET_SCHEDULER_NO_TASK != s->listen_task)
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Server shutting down.\n");
+  if (NULL != server->listen_task)
   {
   {
-    GNUNET_SCHEDULER_cancel (s->listen_task);
-    s->listen_task = GNUNET_SCHEDULER_NO_TASK;
+    GNUNET_SCHEDULER_cancel (server->listen_task);
+    server->listen_task = NULL;
   }
   }
-  if (s->listen_sockets != NULL)
+  if (NULL != server->listen_sockets)
   {
     i = 0;
   {
     i = 0;
-    while (s->listen_sockets[i] != NULL)
+    while (NULL != server->listen_sockets[i])
       GNUNET_break (GNUNET_OK ==
       GNUNET_break (GNUNET_OK ==
-                    GNUNET_NETWORK_socket_close (s->listen_sockets[i++]));
-    GNUNET_free (s->listen_sockets);
-    s->listen_sockets = NULL;
+                    GNUNET_NETWORK_socket_close (server->listen_sockets[i++]));
+    GNUNET_free (server->listen_sockets);
+    server->listen_sockets = NULL;
   }
   }
-  while (s->clients != NULL)
-    GNUNET_SERVER_client_disconnect (s->clients);
-  while (NULL != (hpos = s->handlers))
+  while (NULL != server->clients_head)
+    GNUNET_SERVER_client_disconnect (server->clients_head);
+  while (NULL != (hpos = server->handlers))
   {
   {
-    s->handlers = hpos->next;
+    server->handlers = hpos->next;
     GNUNET_free (hpos);
   }
     GNUNET_free (hpos);
   }
-  while (NULL != (npos = s->disconnect_notify_list))
+  while (NULL != (npos = server->disconnect_notify_list_head))
   {
   {
-    npos->callback (npos->callback_cls, NULL);
-    s->disconnect_notify_list = npos->next;
+    npos->callback (npos->callback_cls,
+                    NULL);
+    GNUNET_CONTAINER_DLL_remove (server->disconnect_notify_list_head,
+                                server->disconnect_notify_list_tail,
+                                npos);
     GNUNET_free (npos);
   }
     GNUNET_free (npos);
   }
-  GNUNET_free (s);
+  while (NULL != (npos = server->connect_notify_list_head))
+  {
+    npos->callback (npos->callback_cls,
+                    NULL);
+    GNUNET_CONTAINER_DLL_remove (server->connect_notify_list_head,
+                                server->connect_notify_list_tail,
+                                npos);
+    GNUNET_free (npos);
+  }
+  GNUNET_free (server);
 }
 
 
 }
 
 
@@ -592,13 +852,23 @@ GNUNET_SERVER_add_handlers (struct GNUNET_SERVER_Handle *server,
 {
   struct HandlerList *p;
 
 {
   struct HandlerList *p;
 
-  p = GNUNET_malloc (sizeof (struct HandlerList));
+  p = GNUNET_new (struct HandlerList);
   p->handlers = handlers;
   p->next = server->handlers;
   server->handlers = p;
 }
 
 
   p->handlers = handlers;
   p->next = server->handlers;
   server->handlers = p;
 }
 
 
+/**
+ * Change functions used by the server to tokenize the message stream.
+ * (very rarely used).
+ *
+ * @param server server to modify
+ * @param create new tokenizer initialization function
+ * @param destroy new tokenizer destruction function
+ * @param receive new tokenizer receive function
+ * @param cls closure for @a create, @a receive, @a destroy
+ */
 void
 GNUNET_SERVER_set_callbacks (struct GNUNET_SERVER_Handle *server,
                              GNUNET_SERVER_MstCreateCallback create,
 void
 GNUNET_SERVER_set_callbacks (struct GNUNET_SERVER_Handle *server,
                              GNUNET_SERVER_MstCreateCallback create,
@@ -614,26 +884,26 @@ GNUNET_SERVER_set_callbacks (struct GNUNET_SERVER_Handle *server,
 
 
 /**
 
 
 /**
- * Task run to warn about missing calls to 'GNUNET_SERVER_receive_done'.
+ * Task run to warn about missing calls to #GNUNET_SERVER_receive_done.
  *
  *
- * @param cls our 'struct GNUNET_SERVER_Client*' to process more requests from
+ * @param cls our `struct GNUNET_SERVER_Client *` to process more requests from
  * @param tc scheduler context (unused)
  */
 static void
  * @param tc scheduler context (unused)
  */
 static void
-warn_no_receive_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+warn_no_receive_done (void *cls,
+                     const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct GNUNET_SERVER_Client *client = cls;
 
 {
   struct GNUNET_SERVER_Client *client = cls;
 
+  GNUNET_break (0 != client->warn_type); /* type should never be 0 here, as we don't use 0 */
   client->warn_task =
       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
                                     &warn_no_receive_done, client);
   if (0 == (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
     LOG (GNUNET_ERROR_TYPE_WARNING,
   client->warn_task =
       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
                                     &warn_no_receive_done, client);
   if (0 == (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
     LOG (GNUNET_ERROR_TYPE_WARNING,
-         _
-         ("Processing code for message of type %u did not call GNUNET_SERVER_receive_done after %llums\n"),
+         _("Processing code for message of type %u did not call `GNUNET_SERVER_receive_done' after %s\n"),
          (unsigned int) client->warn_type,
          (unsigned int) client->warn_type,
-         (unsigned long long)
-         GNUNET_TIME_absolute_get_duration (client->warn_start).rel_value);
+         GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (client->warn_start), GNUNET_YES));
 }
 
 
 }
 
 
@@ -647,10 +917,10 @@ warn_no_receive_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 void
 GNUNET_SERVER_disable_receive_done_warning (struct GNUNET_SERVER_Client *client)
 {
 void
 GNUNET_SERVER_disable_receive_done_warning (struct GNUNET_SERVER_Client *client)
 {
-  if (GNUNET_SCHEDULER_NO_TASK != client->warn_task)
+  if (NULL != client->warn_task)
   {
     GNUNET_SCHEDULER_cancel (client->warn_task);
   {
     GNUNET_SCHEDULER_cancel (client->warn_task);
-    client->warn_task = GNUNET_SCHEDULER_NO_TASK;
+    client->warn_task = NULL;
   }
 }
 
   }
 }
 
@@ -665,9 +935,9 @@ GNUNET_SERVER_disable_receive_done_warning (struct GNUNET_SERVER_Client *client)
  * @param sender the "pretended" sender of the message
  *        can be NULL!
  * @param message message to transmit
  * @param sender the "pretended" sender of the message
  *        can be NULL!
  * @param message message to transmit
- * @return GNUNET_OK if the message was OK and the
+ * @return #GNUNET_OK if the message was OK and the
  *                   connection can stay open
  *                   connection can stay open
- *         GNUNET_SYSERR if the connection to the
+ *         #GNUNET_SYSERR if the connection to the
  *         client should be shut down
  */
 int
  *         client should be shut down
  */
 int
@@ -687,9 +957,8 @@ GNUNET_SERVER_inject (struct GNUNET_SERVER_Handle *server,
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Server schedules transmission of %u-byte message of type %u to client.\n",
        size, type);
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Server schedules transmission of %u-byte message of type %u to client.\n",
        size, type);
-  pos = server->handlers;
   found = GNUNET_NO;
   found = GNUNET_NO;
-  while (pos != NULL)
+  for (pos = server->handlers; NULL != pos; pos = pos->next)
   {
     i = 0;
     while (pos->handlers[i].callback != NULL)
   {
     i = 0;
     while (pos->handlers[i].callback != NULL)
@@ -697,20 +966,26 @@ GNUNET_SERVER_inject (struct GNUNET_SERVER_Handle *server,
       mh = &pos->handlers[i];
       if ((mh->type == type) || (mh->type == GNUNET_MESSAGE_TYPE_ALL))
       {
       mh = &pos->handlers[i];
       if ((mh->type == type) || (mh->type == GNUNET_MESSAGE_TYPE_ALL))
       {
-        if ((mh->expected_size != 0) && (mh->expected_size != size))
+        if ((0 != mh->expected_size) && (mh->expected_size != size))
         {
 #if GNUNET8_NETWORK_IS_DEAD
           LOG (GNUNET_ERROR_TYPE_WARNING,
                "Expected %u bytes for message of type %u, got %u\n",
                mh->expected_size, mh->type, size);
           GNUNET_break_op (0);
         {
 #if GNUNET8_NETWORK_IS_DEAD
           LOG (GNUNET_ERROR_TYPE_WARNING,
                "Expected %u bytes for message of type %u, got %u\n",
                mh->expected_size, mh->type, size);
           GNUNET_break_op (0);
+#else
+          LOG (GNUNET_ERROR_TYPE_DEBUG,
+               "Expected %u bytes for message of type %u, got %u\n",
+               mh->expected_size, mh->type, size);
 #endif
           return GNUNET_SYSERR;
         }
 #endif
           return GNUNET_SYSERR;
         }
-        if (sender != NULL)
+        if (NULL != sender)
         {
         {
-          if (0 == sender->suspended)
+          if ( (0 == sender->suspended) &&
+              (NULL == sender->warn_task) )
           {
           {
+           GNUNET_break (0 != type); /* type should never be 0 here, as we don't use 0 */
             sender->warn_start = GNUNET_TIME_absolute_get ();
             sender->warn_task =
                 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
             sender->warn_start = GNUNET_TIME_absolute_get ();
             sender->warn_task =
                 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
@@ -724,13 +999,12 @@ GNUNET_SERVER_inject (struct GNUNET_SERVER_Handle *server,
       }
       i++;
     }
       }
       i++;
     }
-    pos = pos->next;
   }
   }
-  if (found == GNUNET_NO)
+  if (GNUNET_NO == found)
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
          "Received message of unknown type %d\n", type);
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
          "Received message of unknown type %d\n", type);
-    if (server->require_found == GNUNET_YES)
+    if (GNUNET_YES == server->require_found)
       return GNUNET_SYSERR;
   }
   return GNUNET_OK;
       return GNUNET_SYSERR;
   }
   return GNUNET_OK;
@@ -744,12 +1018,16 @@ GNUNET_SERVER_inject (struct GNUNET_SERVER_Handle *server,
  * @param buf buffer with data received from network
  * @param available number of bytes available in buf
  * @param addr address of the sender
  * @param buf buffer with data received from network
  * @param available number of bytes available in buf
  * @param addr address of the sender
- * @param addrlen length of addr
+ * @param addrlen length of @a addr
  * @param errCode code indicating errors receiving, 0 for success
  */
 static void
  * @param errCode code indicating errors receiving, 0 for success
  */
 static void
-process_incoming (void *cls, const void *buf, size_t available,
-                  const struct sockaddr *addr, socklen_t addrlen, int errCode);
+process_incoming (void *cls,
+                  const void *buf,
+                  size_t available,
+                  const struct sockaddr *addr,
+                  socklen_t addrlen,
+                  int errCode);
 
 
 /**
 
 
 /**
@@ -759,33 +1037,35 @@ process_incoming (void *cls, const void *buf, size_t available,
  * or shutdown.
  *
  * @param client the client to process, RC must have already been increased
  * or shutdown.
  *
  * @param client the client to process, RC must have already been increased
- *        using GNUNET_SERVER_client_keep and will be decreased by one in this
+ *        using #GNUNET_SERVER_client_keep and will be decreased by one in this
  *        function
  *        function
- * @param ret GNUNET_NO to start processing from the buffer,
- *            GNUNET_OK if the mst buffer is drained and we should instantly go back to receiving
- *            GNUNET_SYSERR if we should instantly abort due to error in a previous step
+ * @param ret #GNUNET_NO to start processing from the buffer,
+ *            #GNUNET_OK if the mst buffer is drained and we should instantly go back to receiving
+ *            #GNUNET_SYSERR if we should instantly abort due to error in a previous step
  */
 static void
  */
 static void
-process_mst (struct GNUNET_SERVER_Client *client, int ret)
+process_mst (struct GNUNET_SERVER_Client *client,
+             int ret)
 {
 {
-  while ((ret != GNUNET_SYSERR) && (client->server != NULL) &&
+  while ((GNUNET_SYSERR != ret) && (NULL != client->server) &&
          (GNUNET_YES != client->shutdown_now) && (0 == client->suspended))
   {
          (GNUNET_YES != client->shutdown_now) && (0 == client->suspended))
   {
-    if (ret == GNUNET_OK)
+    if (GNUNET_OK == ret)
     {
     {
-      client->receive_pending = GNUNET_YES;
       LOG (GNUNET_ERROR_TYPE_DEBUG,
       LOG (GNUNET_ERROR_TYPE_DEBUG,
-           "Server re-enters receive loop, timeout: %llu.\n",
-           client->idle_timeout.rel_value);
+           "Server re-enters receive loop, timeout: %s.\n",
+           GNUNET_STRINGS_relative_time_to_string (client->idle_timeout, GNUNET_YES));
+      client->receive_pending = GNUNET_YES;
       GNUNET_CONNECTION_receive (client->connection,
                                  GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
       GNUNET_CONNECTION_receive (client->connection,
                                  GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
-                                 client->idle_timeout, &process_incoming,
+                                 client->idle_timeout,
+                                 &process_incoming,
                                  client);
       break;
     }
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Server processes additional messages instantly.\n");
                                  client);
       break;
     }
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Server processes additional messages instantly.\n");
-    if (client->server->mst_receive != NULL)
+    if (NULL != client->server->mst_receive)
       ret =
           client->server->mst_receive (client->server->mst_cls, client->mst,
                                        client, NULL, 0, GNUNET_NO, GNUNET_YES);
       ret =
           client->server->mst_receive (client->server->mst_cls, client->mst,
                                        client, NULL, 0, GNUNET_NO, GNUNET_YES);
@@ -796,16 +1076,18 @@ process_mst (struct GNUNET_SERVER_Client *client, int ret)
   }
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Server leaves instant processing loop: ret = %d, server = %p, shutdown = %d, suspended = %u\n",
   }
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Server leaves instant processing loop: ret = %d, server = %p, shutdown = %d, suspended = %u\n",
-       ret, client->server, client->shutdown_now, client->suspended);
-  if (ret == GNUNET_NO)
+       ret, client->server,
+       client->shutdown_now,
+       client->suspended);
+  if (GNUNET_NO == ret)
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Server has more data pending but is suspended.\n");
     client->receive_pending = GNUNET_SYSERR;    /* data pending */
   }
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Server has more data pending but is suspended.\n");
     client->receive_pending = GNUNET_SYSERR;    /* data pending */
   }
-  if ((ret == GNUNET_SYSERR) || (GNUNET_YES == client->shutdown_now))
+  if ( (GNUNET_SYSERR == ret) ||
+       (GNUNET_YES == client->shutdown_now) )
     GNUNET_SERVER_client_disconnect (client);
     GNUNET_SERVER_client_disconnect (client);
-  GNUNET_SERVER_client_drop (client);
 }
 
 
 }
 
 
@@ -816,12 +1098,16 @@ process_mst (struct GNUNET_SERVER_Client *client, int ret)
  * @param buf buffer with data received from network
  * @param available number of bytes available in buf
  * @param addr address of the sender
  * @param buf buffer with data received from network
  * @param available number of bytes available in buf
  * @param addr address of the sender
- * @param addrlen length of addr
+ * @param addrlen length of @a addr
  * @param errCode code indicating errors receiving, 0 for success
  */
 static void
  * @param errCode code indicating errors receiving, 0 for success
  */
 static void
-process_incoming (void *cls, const void *buf, size_t available,
-                  const struct sockaddr *addr, socklen_t addrlen, int errCode)
+process_incoming (void *cls,
+                  const void *buf,
+                  size_t available,
+                  const struct sockaddr *addr,
+                  socklen_t addrlen,
+                  int errCode)
 {
   struct GNUNET_SERVER_Client *client = cls;
   struct GNUNET_SERVER_Handle *server = client->server;
 {
   struct GNUNET_SERVER_Client *client = cls;
   struct GNUNET_SERVER_Handle *server = client->server;
@@ -829,50 +1115,82 @@ process_incoming (void *cls, const void *buf, size_t available,
   struct GNUNET_TIME_Absolute now;
   int ret;
 
   struct GNUNET_TIME_Absolute now;
   int ret;
 
-  GNUNET_assert (client->receive_pending == GNUNET_YES);
+  GNUNET_assert (GNUNET_YES == client->receive_pending);
   client->receive_pending = GNUNET_NO;
   now = GNUNET_TIME_absolute_get ();
   client->receive_pending = GNUNET_NO;
   now = GNUNET_TIME_absolute_get ();
-  end = GNUNET_TIME_absolute_add (client->last_activity, client->idle_timeout);
-
-  if ((buf == NULL) && (available == 0) && (addr == NULL) && (errCode == 0) &&
-      (client->shutdown_now != GNUNET_YES) && (server != NULL) &&
-      (GNUNET_YES == GNUNET_CONNECTION_check (client->connection)) &&
-      (end.abs_value > now.abs_value))
+  end = GNUNET_TIME_absolute_add (client->last_activity,
+                                  client->idle_timeout);
+
+  if ( (NULL == buf) &&
+       (0 == available) &&
+       (NULL == addr) &&
+       (0 == errCode) &&
+       (GNUNET_YES != client->shutdown_now) &&
+       (NULL != server) &&
+       (GNUNET_YES == GNUNET_CONNECTION_check (client->connection)) &&
+       (end.abs_value_us > now.abs_value_us) )
   {
     /* wait longer, timeout changed (i.e. due to us sending) */
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Receive time out, but no disconnect due to sending (%p)\n",
   {
     /* wait longer, timeout changed (i.e. due to us sending) */
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Receive time out, but no disconnect due to sending (%p)\n",
-         GNUNET_a2s (addr, addrlen));
+         client);
     client->receive_pending = GNUNET_YES;
     GNUNET_CONNECTION_receive (client->connection,
                                GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
                                GNUNET_TIME_absolute_get_remaining (end),
     client->receive_pending = GNUNET_YES;
     GNUNET_CONNECTION_receive (client->connection,
                                GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
                                GNUNET_TIME_absolute_get_remaining (end),
-                               &process_incoming, client);
+                               &process_incoming,
+                               client);
     return;
   }
     return;
   }
-  if ((buf == NULL) || (available == 0) || (errCode != 0) || (server == NULL) ||
-      (client->shutdown_now == GNUNET_YES) ||
-      (GNUNET_YES != GNUNET_CONNECTION_check (client->connection)))
+  if ( (NULL == buf) ||
+       (0 == available) ||
+       (0 != errCode) ||
+       (NULL == server) ||
+       (GNUNET_YES == client->shutdown_now) ||
+       (GNUNET_YES != GNUNET_CONNECTION_check (client->connection)) )
   {
     /* other side closed connection, error connecting, etc. */
   {
     /* other side closed connection, error connecting, etc. */
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Failed to connect or other side closed connection (%p)\n",
+         client);
     GNUNET_SERVER_client_disconnect (client);
     return;
   }
     GNUNET_SERVER_client_disconnect (client);
     return;
   }
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "Server receives %u bytes from `%s'.\n",
-       (unsigned int) available, GNUNET_a2s (addr, addrlen));
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Server receives %u bytes from `%s'.\n",
+       (unsigned int) available,
+       GNUNET_a2s (addr, addrlen));
   GNUNET_SERVER_client_keep (client);
   client->last_activity = now;
 
   GNUNET_SERVER_client_keep (client);
   client->last_activity = now;
 
-  if (server->mst_receive != NULL)
-    ret =
-        client->server->mst_receive (client->server->mst_cls, client->mst,
-                                     client, buf, available, GNUNET_NO, GNUNET_YES);
-  else
+  if (NULL != server->mst_receive)
+  {
+    ret = client->server->mst_receive (client->server->mst_cls,
+                                       client->mst,
+                                       client,
+                                       buf,
+                                       available,
+                                       GNUNET_NO,
+                                       GNUNET_YES);
+  }
+  else if (NULL != client->mst)
+  {
     ret =
     ret =
-        GNUNET_SERVER_mst_receive (client->mst, client, buf, available, GNUNET_NO,
+        GNUNET_SERVER_mst_receive (client->mst,
+                                   client,
+                                   buf,
+                                   available,
+                                   GNUNET_NO,
                                    GNUNET_YES);
                                    GNUNET_YES);
-
-  process_mst (client, ret);
+  }
+  else
+  {
+    GNUNET_break (0);
+    return;
+  }
+  process_mst (client,
+               ret);
+  GNUNET_SERVER_client_drop (client);
 }
 
 
 }
 
 
@@ -880,36 +1198,35 @@ process_incoming (void *cls, const void *buf, size_t available,
  * Task run to start again receiving from the network
  * and process requests.
  *
  * Task run to start again receiving from the network
  * and process requests.
  *
- * @param cls our 'struct GNUNET_SERVER_Client*' to process more requests from
+ * @param cls our `struct GNUNET_SERVER_Client *` to process more requests from
  * @param tc scheduler context (unused)
  */
 static void
  * @param tc scheduler context (unused)
  */
 static void
-restart_processing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+restart_processing (void *cls,
+                    const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct GNUNET_SERVER_Client *client = cls;
 {
   struct GNUNET_SERVER_Client *client = cls;
-  struct GNUNET_SERVER_Handle *server = client->server;
 
 
-  client->restart_task = GNUNET_SCHEDULER_NO_TASK;
-  if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) &&
-      (GNUNET_NO == server->clients_ignore_shutdown))
-  {
-    GNUNET_SERVER_client_disconnect (client);
-    return;
-  }
-  if (client->receive_pending == GNUNET_NO)
+  GNUNET_assert (GNUNET_YES != client->shutdown_now);
+  client->restart_task = NULL;
+  if (GNUNET_NO == client->receive_pending)
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG, "Server begins to read again from client.\n");
     client->receive_pending = GNUNET_YES;
     GNUNET_CONNECTION_receive (client->connection,
                                GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG, "Server begins to read again from client.\n");
     client->receive_pending = GNUNET_YES;
     GNUNET_CONNECTION_receive (client->connection,
                                GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
-                               client->idle_timeout, &process_incoming, client);
+                               client->idle_timeout,
+                               &process_incoming,
+                               client);
     return;
   }
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Server continues processing messages still in the buffer.\n");
   GNUNET_SERVER_client_keep (client);
   client->receive_pending = GNUNET_NO;
     return;
   }
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Server continues processing messages still in the buffer.\n");
   GNUNET_SERVER_client_keep (client);
   client->receive_pending = GNUNET_NO;
-  process_mst (client, GNUNET_NO);
+  process_mst (client,
+               GNUNET_NO);
+  GNUNET_SERVER_client_drop (client);
 }
 
 
 }
 
 
@@ -918,11 +1235,14 @@ restart_processing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  * received a complete message.
  *
  * @param cls closure (struct GNUNET_SERVER_Handle)
  * received a complete message.
  *
  * @param cls closure (struct GNUNET_SERVER_Handle)
- * @param client identification of the client (struct GNUNET_SERVER_Client*)
+ * @param client identification of the client (`struct GNUNET_SERVER_Client *`)
  * @param message the actual message
  * @param message the actual message
+ *
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR to stop further processing
  */
  */
-static void
-client_message_tokenizer_callback (void *cls, void *client,
+static int
+client_message_tokenizer_callback (void *cls,
+                                   void *client,
                                    const struct GNUNET_MessageHeader *message)
 {
   struct GNUNET_SERVER_Handle *server = cls;
                                    const struct GNUNET_MessageHeader *message)
 {
   struct GNUNET_SERVER_Handle *server = cls;
@@ -935,8 +1255,12 @@ client_message_tokenizer_callback (void *cls, void *client,
   sender->in_process_client_buffer = GNUNET_YES;
   ret = GNUNET_SERVER_inject (server, sender, message);
   sender->in_process_client_buffer = GNUNET_NO;
   sender->in_process_client_buffer = GNUNET_YES;
   ret = GNUNET_SERVER_inject (server, sender, message);
   sender->in_process_client_buffer = GNUNET_NO;
-  if (GNUNET_OK != ret)
+  if ( (GNUNET_OK != ret) || (GNUNET_YES == sender->shutdown_now) )
+  {
     GNUNET_SERVER_client_disconnect (sender);
     GNUNET_SERVER_client_disconnect (sender);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
 }
 
 
 }
 
 
@@ -949,37 +1273,39 @@ client_message_tokenizer_callback (void *cls, void *client,
  * @param server the server to use
  * @param connection the connection to manage (client must
  *        stop using this connection from now on)
  * @param server the server to use
  * @param connection the connection to manage (client must
  *        stop using this connection from now on)
- * @return the client handle (client should call
- *         "client_drop" on the return value eventually)
+ * @return the client handle
  */
 struct GNUNET_SERVER_Client *
 GNUNET_SERVER_connect_socket (struct GNUNET_SERVER_Handle *server,
                               struct GNUNET_CONNECTION_Handle *connection)
 {
   struct GNUNET_SERVER_Client *client;
  */
 struct GNUNET_SERVER_Client *
 GNUNET_SERVER_connect_socket (struct GNUNET_SERVER_Handle *server,
                               struct GNUNET_CONNECTION_Handle *connection)
 {
   struct GNUNET_SERVER_Client *client;
+  struct NotifyList *n;
 
 
-  client = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Client));
+  client = GNUNET_new (struct GNUNET_SERVER_Client);
   client->connection = connection;
   client->connection = connection;
-  client->reference_count = 1;
   client->server = server;
   client->last_activity = GNUNET_TIME_absolute_get ();
   client->server = server;
   client->last_activity = GNUNET_TIME_absolute_get ();
-  client->next = server->clients;
   client->idle_timeout = server->idle_timeout;
   client->idle_timeout = server->idle_timeout;
-  server->clients = client;
-  client->receive_pending = GNUNET_YES;
-  client->callback = NULL;
-  client->callback_cls = NULL;
-
-  if (server->mst_create != NULL)
+  GNUNET_CONTAINER_DLL_insert (server->clients_head,
+                              server->clients_tail,
+                              client);
+  if (NULL != server->mst_create)
     client->mst =
         server->mst_create (server->mst_cls, client);
   else
     client->mst =
     client->mst =
         server->mst_create (server->mst_cls, client);
   else
     client->mst =
-        GNUNET_SERVER_mst_create (&client_message_tokenizer_callback, server);
-
+        GNUNET_SERVER_mst_create (&client_message_tokenizer_callback,
+                                  server);
+  GNUNET_assert (NULL != client->mst);
+  for (n = server->connect_notify_list_head; NULL != n; n = n->next)
+    n->callback (n->callback_cls, client);
+  client->receive_pending = GNUNET_YES;
   GNUNET_CONNECTION_receive (client->connection,
                              GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
   GNUNET_CONNECTION_receive (client->connection,
                              GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
-                             client->idle_timeout, &process_incoming, client);
+                             client->idle_timeout,
+                             &process_incoming,
+                             client);
   return client;
 }
 
   return client;
 }
 
@@ -1027,7 +1353,7 @@ GNUNET_SERVER_client_drop (struct GNUNET_SERVER_Client *client)
 {
   GNUNET_assert (client->reference_count > 0);
   client->reference_count--;
 {
   GNUNET_assert (client->reference_count > 0);
   client->reference_count--;
-  if ((client->shutdown_now == GNUNET_YES) && (client->reference_count == 0))
+  if ((GNUNET_YES == client->shutdown_now) && (0 == client->reference_count))
     GNUNET_SERVER_client_disconnect (client);
 }
 
     GNUNET_SERVER_client_disconnect (client);
 }
 
@@ -1037,8 +1363,8 @@ GNUNET_SERVER_client_drop (struct GNUNET_SERVER_Client *client)
  *
  * @param client the client to get the address for
  * @param addr where to store the address
  *
  * @param client the client to get the address for
  * @param addr where to store the address
- * @param addrlen where to store the length of the address
- * @return GNUNET_OK on success
+ * @param addrlen where to store the length of the @a addr
+ * @return #GNUNET_OK on success
  */
 int
 GNUNET_SERVER_client_get_address (struct GNUNET_SERVER_Client *client,
  */
 int
 GNUNET_SERVER_client_get_address (struct GNUNET_SERVER_Client *client,
@@ -1056,7 +1382,7 @@ GNUNET_SERVER_client_get_address (struct GNUNET_SERVER_Client *client,
  *
  * @param server the server manageing the clients
  * @param callback function to call on disconnect
  *
  * @param server the server manageing the clients
  * @param callback function to call on disconnect
- * @param callback_cls closure for callback
+ * @param callback_cls closure for @a callback
  */
 void
 GNUNET_SERVER_disconnect_notify (struct GNUNET_SERVER_Handle *server,
  */
 void
 GNUNET_SERVER_disconnect_notify (struct GNUNET_SERVER_Handle *server,
@@ -1065,54 +1391,127 @@ GNUNET_SERVER_disconnect_notify (struct GNUNET_SERVER_Handle *server,
 {
   struct NotifyList *n;
 
 {
   struct NotifyList *n;
 
-  n = GNUNET_malloc (sizeof (struct NotifyList));
+  n = GNUNET_new (struct NotifyList);
   n->callback = callback;
   n->callback_cls = callback_cls;
   n->callback = callback;
   n->callback_cls = callback_cls;
-  n->next = server->disconnect_notify_list;
-  server->disconnect_notify_list = n;
+  GNUNET_CONTAINER_DLL_insert (server->disconnect_notify_list_head,
+                              server->disconnect_notify_list_tail,
+                              n);
 }
 
 
 /**
 }
 
 
 /**
- * Ask the server to stop notifying us whenever a client disconnects.
+ * Ask the server to notify us whenever a client connects.
+ * This function is called whenever the actual network connection
+ * is opened. If the server is destroyed before this
+ * notification is explicitly cancelled, the 'callback' will
+ * once be called with a 'client' argument of NULL to indicate
+ * that the server itself is now gone (and that the callback
+ * won't be called anymore and also can no longer be cancelled).
  *
  * @param server the server manageing the clients
  *
  * @param server the server manageing the clients
- * @param callback function to call on disconnect
- * @param callback_cls closure for callback
+ * @param callback function to call on sconnect
+ * @param callback_cls closure for @a callback
+ */
+void
+GNUNET_SERVER_connect_notify (struct GNUNET_SERVER_Handle *server,
+                             GNUNET_SERVER_ConnectCallback callback,
+                             void *callback_cls)
+{
+  struct NotifyList *n;
+  struct GNUNET_SERVER_Client *client;
+
+  n = GNUNET_new (struct NotifyList);
+  n->callback = callback;
+  n->callback_cls = callback_cls;
+  GNUNET_CONTAINER_DLL_insert (server->connect_notify_list_head,
+                              server->connect_notify_list_tail,
+                              n);
+  for (client = server->clients_head; NULL != client; client = client->next)
+    callback (callback_cls, client);
+}
+
+
+/**
+ * Ask the server to stop notifying us whenever a client connects.
+ *
+ * @param server the server manageing the clients
+ * @param callback function to call on connect
+ * @param callback_cls closure for @a callback
  */
 void
 GNUNET_SERVER_disconnect_notify_cancel (struct GNUNET_SERVER_Handle *server,
  */
 void
 GNUNET_SERVER_disconnect_notify_cancel (struct GNUNET_SERVER_Handle *server,
-                                        GNUNET_SERVER_DisconnectCallback
-                                        callback, void *callback_cls)
+                                        GNUNET_SERVER_DisconnectCallback callback,
+                                        void *callback_cls)
 {
   struct NotifyList *pos;
 {
   struct NotifyList *pos;
-  struct NotifyList *prev;
 
 
-  prev = NULL;
-  pos = server->disconnect_notify_list;
-  while (pos != NULL)
-  {
+  for (pos = server->disconnect_notify_list_head; NULL != pos; pos = pos->next)
     if ((pos->callback == callback) && (pos->callback_cls == callback_cls))
       break;
     if ((pos->callback == callback) && (pos->callback_cls == callback_cls))
       break;
-    prev = pos;
-    pos = pos->next;
+  if (NULL == pos)
+  {
+    GNUNET_break (0);
+    return;
   }
   }
-  if (pos == NULL)
+  GNUNET_CONTAINER_DLL_remove (server->disconnect_notify_list_head,
+                              server->disconnect_notify_list_tail,
+                              pos);
+  GNUNET_free (pos);
+}
+
+
+/**
+ * Ask the server to stop notifying us whenever a client disconnects.
+ *
+ * @param server the server manageing the clients
+ * @param callback function to call on disconnect
+ * @param callback_cls closure for @a callback
+ */
+void
+GNUNET_SERVER_connect_notify_cancel (struct GNUNET_SERVER_Handle *server,
+                                    GNUNET_SERVER_ConnectCallback callback,
+                                    void *callback_cls)
+{
+  struct NotifyList *pos;
+
+  for (pos = server->connect_notify_list_head; NULL != pos; pos = pos->next)
+    if ((pos->callback == callback) && (pos->callback_cls == callback_cls))
+      break;
+  if (NULL == pos)
   {
     GNUNET_break (0);
     return;
   }
   {
     GNUNET_break (0);
     return;
   }
-  if (prev == NULL)
-    server->disconnect_notify_list = pos->next;
-  else
-    prev->next = pos->next;
+  GNUNET_CONTAINER_DLL_remove (server->connect_notify_list_head,
+                              server->connect_notify_list_tail,
+                              pos);
   GNUNET_free (pos);
 }
 
 
   GNUNET_free (pos);
 }
 
 
+/**
+ * Destroy the connection that is passed in via @a cls.  Used
+ * as calling #GNUNET_CONNECTION_destroy from within a function
+ * that was itself called from within process_notify() of
+ * 'connection.c' is not allowed (see #2329).
+ *
+ * @param cls connection to destroy
+ * @param tc scheduler context (unused)
+ */
+static void
+destroy_connection (void *cls,
+                   const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct GNUNET_CONNECTION_Handle *connection = cls;
+
+  GNUNET_CONNECTION_destroy (connection);
+}
+
+
 /**
  * Ask the server to disconnect from the given client.
 /**
  * Ask the server to disconnect from the given client.
- * This is the same as returning GNUNET_SYSERR from a message
+ * This is the same as returning #GNUNET_SYSERR from a message
  * handler, except that it allows dropping of a client even
  * when not handling a message from that client.
  *
  * handler, except that it allows dropping of a client even
  * when not handling a message from that client.
  *
@@ -1121,87 +1520,85 @@ GNUNET_SERVER_disconnect_notify_cancel (struct GNUNET_SERVER_Handle *server,
 void
 GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client)
 {
 void
 GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client)
 {
-  struct GNUNET_SERVER_Client *prev;
-  struct GNUNET_SERVER_Client *pos;
-  struct GNUNET_SERVER_Handle *server;
+  struct GNUNET_SERVER_Handle *server = client->server;
   struct NotifyList *n;
   struct NotifyList *n;
-  unsigned int rc;
 
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Client is being disconnected from the server.\n");
 
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Client is being disconnected from the server.\n");
-  if (client->restart_task != GNUNET_SCHEDULER_NO_TASK)
+  if (NULL != client->restart_task)
   {
     GNUNET_SCHEDULER_cancel (client->restart_task);
   {
     GNUNET_SCHEDULER_cancel (client->restart_task);
-    client->restart_task = GNUNET_SCHEDULER_NO_TASK;
+    client->restart_task = NULL;
   }
   }
-  if (client->warn_task != GNUNET_SCHEDULER_NO_TASK)
+  if (NULL != client->warn_task)
   {
     GNUNET_SCHEDULER_cancel (client->warn_task);
   {
     GNUNET_SCHEDULER_cancel (client->warn_task);
-    client->warn_task = GNUNET_SCHEDULER_NO_TASK;
+    client->warn_task = NULL;
   }
   if (GNUNET_YES == client->receive_pending)
   {
     GNUNET_CONNECTION_receive_cancel (client->connection);
     client->receive_pending = GNUNET_NO;
   }
   }
   if (GNUNET_YES == client->receive_pending)
   {
     GNUNET_CONNECTION_receive_cancel (client->connection);
     client->receive_pending = GNUNET_NO;
   }
-
-  rc = client->reference_count;
-  if (client->shutdown_now != GNUNET_YES)
+  client->shutdown_now = GNUNET_YES;
+  client->reference_count++; /* make sure nobody else clean up client... */
+  if ( (NULL != client->mst) &&
+       (NULL != server) )
   {
   {
-    server = client->server;
-    client->shutdown_now = GNUNET_YES;
-    prev = NULL;
-    pos = server->clients;
-    while ((pos != NULL) && (pos != client))
-    {
-      prev = pos;
-      pos = pos->next;
-    }
-    GNUNET_assert (pos != NULL);
-    if (prev == NULL)
-      server->clients = pos->next;
+    GNUNET_CONTAINER_DLL_remove (server->clients_head,
+                                server->clients_tail,
+                                client);
+    if (NULL != server->mst_destroy)
+      server->mst_destroy (server->mst_cls,
+                           client->mst);
     else
     else
-      prev->next = pos->next;
-    if (client->restart_task != GNUNET_SCHEDULER_NO_TASK)
-    {
-      GNUNET_SCHEDULER_cancel (client->restart_task);
-      client->restart_task = GNUNET_SCHEDULER_NO_TASK;
-    }
-    if (client->warn_task != GNUNET_SCHEDULER_NO_TASK)
-    {
-      GNUNET_SCHEDULER_cancel (client->warn_task);
-      client->warn_task = GNUNET_SCHEDULER_NO_TASK;
-    }
-    n = server->disconnect_notify_list;
-    while (n != NULL)
-    {
-      n->callback (n->callback_cls, client);
-      n = n->next;
-    }
+      GNUNET_SERVER_mst_destroy (client->mst);
+    client->mst = NULL;
+    for (n = server->disconnect_notify_list_head; NULL != n; n = n->next)
+      n->callback (n->callback_cls,
+                   client);
   }
   }
-  if (rc > 0)
+  client->reference_count--;
+  if (client->reference_count > 0)
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG,
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "RC still positive, not destroying everything.\n");
+         "RC of %p still positive, not destroying everything.\n",
+         client);
+    client->server = NULL;
     return;
   }
     return;
   }
-  if (client->in_process_client_buffer == GNUNET_YES)
+  if (GNUNET_YES == client->in_process_client_buffer)
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG,
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "Still processing inputs, not destroying everything.\n");
+         "Still processing inputs of %p, not destroying everything.\n",
+         client);
     return;
   }
     return;
   }
-
-  if (client->persist == GNUNET_YES)
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "RC of %p now zero, destroying everything.\n",
+       client);
+  if (GNUNET_YES == client->persist)
     GNUNET_CONNECTION_persist_ (client->connection);
     GNUNET_CONNECTION_persist_ (client->connection);
-  GNUNET_CONNECTION_destroy (client->connection);
-
-  if (client->server->mst_destroy != NULL)
-    client->server->mst_destroy (client->server->mst_cls, client->mst);
-  else
-    GNUNET_SERVER_mst_destroy (client->mst);
-
+  if (NULL != client->th.cth)
+    GNUNET_SERVER_notify_transmit_ready_cancel (&client->th);
+  (void) GNUNET_SCHEDULER_add_now (&destroy_connection,
+                                  client->connection);
+  /* need to cancel again, as it might have been re-added
+     in the meantime (i.e. during callbacks) */
+  if (NULL != client->warn_task)
+  {
+    GNUNET_SCHEDULER_cancel (client->warn_task);
+    client->warn_task = NULL;
+  }
+  if (GNUNET_YES == client->receive_pending)
+  {
+    GNUNET_CONNECTION_receive_cancel (client->connection);
+    client->receive_pending = GNUNET_NO;
+  }
   GNUNET_free (client);
   GNUNET_free (client);
+  /* we might be in soft-shutdown, test if we're done */
+  if (NULL != server)
+    test_monitor_clients (server);
 }
 
 
 }
 
 
@@ -1211,7 +1608,7 @@ GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client)
  * instead of potentially buffering multiple messages.
  *
  * @param client handle to the client
  * instead of potentially buffering multiple messages.
  *
  * @param client handle to the client
- * @return GNUNET_OK on success
+ * @return #GNUNET_OK on success
  */
 int
 GNUNET_SERVER_client_disable_corking (struct GNUNET_SERVER_Client *client)
  */
 int
 GNUNET_SERVER_client_disable_corking (struct GNUNET_SERVER_Client *client)
@@ -1224,7 +1621,7 @@ GNUNET_SERVER_client_disable_corking (struct GNUNET_SERVER_Client *client)
  * Wrapper for transmission notification that calls the original
  * callback and update the last activity time for our connection.
  *
  * Wrapper for transmission notification that calls the original
  * callback and update the last activity time for our connection.
  *
- * @param cls the 'struct GNUNET_SERVER_Client'
+ * @param cls the `struct GNUNET_SERVER_Client *`
  * @param size number of bytes we can transmit
  * @param buf where to copy the message
  * @return number of bytes actually transmitted
  * @param size number of bytes we can transmit
  * @param buf where to copy the message
  * @return number of bytes actually transmitted
@@ -1233,12 +1630,13 @@ static size_t
 transmit_ready_callback_wrapper (void *cls, size_t size, void *buf)
 {
   struct GNUNET_SERVER_Client *client = cls;
 transmit_ready_callback_wrapper (void *cls, size_t size, void *buf)
 {
   struct GNUNET_SERVER_Client *client = cls;
-  size_t ret;
+  GNUNET_CONNECTION_TransmitReadyNotify callback;
 
 
-  ret = client->callback (client->callback_cls, size, buf);
-  if (ret > 0)
-    client->last_activity = GNUNET_TIME_absolute_get ();
-  return ret;
+  client->th.cth = NULL;
+  callback = client->th.callback;
+  client->th.callback = NULL;
+  client->last_activity = GNUNET_TIME_absolute_get ();
+  return callback (client->th.callback_cls, size, buf);
 }
 
 
 }
 
 
@@ -1251,25 +1649,42 @@ transmit_ready_callback_wrapper (void *cls, size_t size, void *buf)
  * @param timeout after how long should we give up (and call
  *        notify with buf NULL and size 0)?
  * @param callback function to call when space is available
  * @param timeout after how long should we give up (and call
  *        notify with buf NULL and size 0)?
  * @param callback function to call when space is available
- * @param callback_cls closure for callback
+ * @param callback_cls closure for @a callback
  * @return non-NULL if the notify callback was queued; can be used
  * @return non-NULL if the notify callback was queued; can be used
- *           to cancel the request using
- *           GNUNET_CONNECTION_notify_transmit_ready_cancel.
+ *         to cancel the request using
+ *         #GNUNET_SERVER_notify_transmit_ready_cancel().
  *         NULL if we are already going to notify someone else (busy)
  */
  *         NULL if we are already going to notify someone else (busy)
  */
-struct GNUNET_CONNECTION_TransmitHandle *
+struct GNUNET_SERVER_TransmitHandle *
 GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client,
                                      size_t size,
                                      struct GNUNET_TIME_Relative timeout,
 GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client,
                                      size_t size,
                                      struct GNUNET_TIME_Relative timeout,
-                                     GNUNET_CONNECTION_TransmitReadyNotify
-                                     callback, void *callback_cls)
+                                     GNUNET_CONNECTION_TransmitReadyNotify callback,
+                                     void *callback_cls)
 {
 {
-  client->callback_cls = callback_cls;
-  client->callback = callback;
-  return GNUNET_CONNECTION_notify_transmit_ready (client->connection, size,
-                                                  timeout,
-                                                  &transmit_ready_callback_wrapper,
-                                                  client);
+  if (NULL != client->th.callback)
+    return NULL;
+  client->th.callback_cls = callback_cls;
+  client->th.callback = callback;
+  client->th.cth = GNUNET_CONNECTION_notify_transmit_ready (client->connection, size,
+                                                           timeout,
+                                                           &transmit_ready_callback_wrapper,
+                                                           client);
+  return &client->th;
+}
+
+
+/**
+ * Abort transmission request.
+ *
+ * @param th request to abort
+ */
+void
+GNUNET_SERVER_notify_transmit_ready_cancel (struct GNUNET_SERVER_TransmitHandle *th)
+{
+  GNUNET_CONNECTION_notify_transmit_ready_cancel (th->cth);
+  th->cth = NULL;
+  th->callback = NULL;
 }
 
 
 }
 
 
@@ -1292,24 +1707,28 @@ GNUNET_SERVER_client_persist_ (struct GNUNET_SERVER_Client *client)
  * GNUNET_SERVER_MessageCallback (or its respective continuations).
  *
  * @param client client we were processing a message of
  * GNUNET_SERVER_MessageCallback (or its respective continuations).
  *
  * @param client client we were processing a message of
- * @param success GNUNET_OK to keep the connection open and
+ * @param success #GNUNET_OK to keep the connection open and
  *                          continue to receive
  *                          continue to receive
- *                GNUNET_NO to close the connection (normal behavior)
- *                GNUNET_SYSERR to close the connection (signal
+ *                #GNUNET_NO to close the connection (normal behavior)
+ *                #GNUNET_SYSERR to close the connection (signal
  *                          serious error)
  */
 void
  *                          serious error)
  */
 void
-GNUNET_SERVER_receive_done (struct GNUNET_SERVER_Client *client, int success)
+GNUNET_SERVER_receive_done (struct GNUNET_SERVER_Client *client,
+                           int success)
 {
 {
-  if (client == NULL)
+  if (NULL == client)
     return;
   GNUNET_assert (client->suspended > 0);
   client->suspended--;
     return;
   GNUNET_assert (client->suspended > 0);
   client->suspended--;
-  if (success != GNUNET_OK)
+  if (GNUNET_OK != success)
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "GNUNET_SERVER_receive_done called with failure indication\n");
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "GNUNET_SERVER_receive_done called with failure indication\n");
-    GNUNET_SERVER_client_disconnect (client);
+    if ( (client->reference_count > 0) || (client->suspended > 0) )
+      client->shutdown_now = GNUNET_YES;
+    else
+      GNUNET_SERVER_client_disconnect (client);
     return;
   }
   if (client->suspended > 0)
     return;
   }
   if (client->suspended > 0)
@@ -1318,44 +1737,28 @@ GNUNET_SERVER_receive_done (struct GNUNET_SERVER_Client *client, int success)
          "GNUNET_SERVER_receive_done called, but more clients pending\n");
     return;
   }
          "GNUNET_SERVER_receive_done called, but more clients pending\n");
     return;
   }
-  if (GNUNET_SCHEDULER_NO_TASK != client->warn_task)
+  if (NULL != client->warn_task)
   {
     GNUNET_SCHEDULER_cancel (client->warn_task);
   {
     GNUNET_SCHEDULER_cancel (client->warn_task);
-    client->warn_task = GNUNET_SCHEDULER_NO_TASK;
+    client->warn_task = NULL;
   }
   }
-  if (client->in_process_client_buffer == GNUNET_YES)
+  if (GNUNET_YES == client->in_process_client_buffer)
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "GNUNET_SERVER_receive_done called while still in processing loop\n");
     return;
   }
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "GNUNET_SERVER_receive_done called while still in processing loop\n");
     return;
   }
-  if ((client->server == NULL) || (GNUNET_YES == client->shutdown_now))
+  if ((NULL == client->server) || (GNUNET_YES == client->shutdown_now))
   {
     GNUNET_SERVER_client_disconnect (client);
     return;
   }
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "GNUNET_SERVER_receive_done causes restart in reading from the socket\n");
   {
     GNUNET_SERVER_client_disconnect (client);
     return;
   }
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "GNUNET_SERVER_receive_done causes restart in reading from the socket\n");
-  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == client->restart_task);
-  client->restart_task = GNUNET_SCHEDULER_add_now (&restart_processing, client);
+  GNUNET_assert (NULL == client->restart_task);
+  client->restart_task = GNUNET_SCHEDULER_add_now (&restart_processing,
+                                                   client);
 }
 
 
 }
 
 
-/**
- * Configure this server's connections to continue handling client
- * requests as usual even after we get a shutdown signal.  The change
- * only applies to clients that connect to the server from the outside
- * using TCP after this call.  Clients managed previously or those
- * added using GNUNET_SERVER_connect_socket and
- * GNUNET_SERVER_connect_callback are not affected by this option.
- *
- * @param h server handle
- * @param do_ignore GNUNET_YES to ignore, GNUNET_NO to restore default
- */
-void
-GNUNET_SERVER_ignore_shutdown (struct GNUNET_SERVER_Handle *h, int do_ignore)
-{
-  h->clients_ignore_shutdown = do_ignore;
-}
-
 /* end of server.c */
 /* end of server.c */