curly wars / auto-indentation
[oweals/gnunet.git] / src / core / gnunet-service-core_clients.c
index a0d5da65c34c220628cc5d878ab618d4e6ea7d77..3201b71f0e0c3b9659d16e42bc29755391974f56 100644 (file)
  */
 #include "platform.h"
 #include "gnunet_util_lib.h"
+#include "gnunet_statistics_service.h"
 #include "gnunet_transport_service.h"
-#include "gnunet_service_core.h"
-#include "gnunet_service_core_clients.h"
-#include "gnunet_service_core_sessions.h"
-#include "gnunet_service_core_typemap.h"
+#include "gnunet-service-core.h"
+#include "gnunet-service-core_clients.h"
+#include "gnunet-service-core_sessions.h"
+#include "gnunet-service-core_typemap.h"
+#include "core.h"
 
+#define DEBUG_CONNECTS GNUNET_YES
+
+/**
+ * How many messages do we queue up at most for optional
+ * notifications to a client?  (this can cause notifications
+ * about outgoing messages to be dropped).
+ */
+#define MAX_NOTIFY_QUEUE 1024
 
 
 /**
  * Data structure for each client connected to the core service.
  */
-struct Client
+struct GSC_Client
 {
   /**
    * Clients are kept in a linked list.
    */
-  struct Client *next;
+  struct GSC_Client *next;
 
   /**
    * Clients are kept in a linked list.
    */
-  struct Client *prev;
+  struct GSC_Client *prev;
 
   /**
    * Handle for the client with the server API.
@@ -62,10 +72,17 @@ struct Client
 
   /**
    * Map of peer identities to active transmission requests of this
-   * client to the peer (of type 'struct ClientActiveRequest').
+   * client to the peer (of type 'struct GSC_ClientActiveRequest').
    */
   struct GNUNET_CONTAINER_MultiHashMap *requests;
 
+#if DEBUG_CONNECTS
+  /**
+   * Map containing all peers that this client knows we're connected to.
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *connectmap;
+#endif
+
   /**
    * Options for messages this client cares about,
    * see GNUNET_CORE_OPTION_ values.
@@ -84,12 +101,12 @@ struct Client
 /**
  * Head of linked list of our clients.
  */
-static struct Client *client_head;
+static struct GSC_Client *client_head;
 
 /**
  * Tail of linked list of our clients.
  */
-static struct Client *client_tail;
+static struct GSC_Client *client_tail;
 
 /**
  * Context for notifications we need to send to our clients.
@@ -108,10 +125,10 @@ static struct GNUNET_SERVER_MessageStreamTokenizer *client_mst;
  * @param client server client handle to look up
  * @return our client handle for the client
  */
-static struct Client *
+static struct GSC_Client *
 find_client (struct GNUNET_SERVER_Client *client)
 {
-  struct Client *c;
+  struct GSC_Client *c;
 
   c = client_head;
   while ((c != NULL) && (c->client_handle != client))
@@ -129,11 +146,10 @@ find_client (struct GNUNET_SERVER_Client *client)
  *        client's queue is getting too large?
  */
 static void
-send_to_client (struct Client *client, 
-               const struct GNUNET_MessageHeader *msg,
-                int can_drop)
+send_to_client (struct GSC_Client *client,
+                const struct GNUNET_MessageHeader *msg, int can_drop)
 {
-#if DEBUG_CORE_CLIENT
+#if DEBUG_CORE
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Preparing to send %u bytes of message of type %u to client.\n",
               (unsigned int) ntohs (msg->size),
@@ -144,6 +160,31 @@ send_to_client (struct Client *client,
 }
 
 
+/**
+ * Send a message to one of our clients.
+ *
+ * @param client target for the message
+ * @param msg message to transmit
+ * @param can_drop could this message be dropped if the
+ *        client's queue is getting too large?
+ */
+void
+GSC_CLIENTS_send_to_client (struct GNUNET_SERVER_Client *client,
+                            const struct GNUNET_MessageHeader *msg,
+                            int can_drop)
+{
+  struct GSC_Client *c;
+
+  c = find_client (client);
+  if (NULL == c)
+  {
+    GNUNET_break (0);
+    return;
+  }
+  send_to_client (c, msg, can_drop);
+}
+
+
 /**
  * Test if the client is interested in messages of the given type.
  *
@@ -152,12 +193,13 @@ send_to_client (struct Client *client,
  * @return GNUNET_YES if 'c' is interested, GNUNET_NO if not.
  */
 static int
-type_match (uint16_t type,
-           struct Client *c)
+type_match (uint16_t type, struct GSC_Client *c)
 {
   unsigned int i;
 
-  for (i=0;i<c->tcnt;i++)
+  if (c->tcnt == 0)
+    return GNUNET_YES;          /* peer without handlers matches ALL */
+  for (i = 0; i < c->tcnt; i++)
     if (type == c->types[i])
       return GNUNET_YES;
   return GNUNET_NO;
@@ -174,22 +216,28 @@ type_match (uint16_t type,
  * @param type type of the embedded message, 0 for none
  */
 static void
-send_to_all_clients (const struct GNUNET_MessageHeader *msg, 
-                    int can_drop,
-                     int options,
-                    uint16_t type)
+send_to_all_clients (const struct GNUNET_PeerIdentity *sender,
+                     const struct GNUNET_MessageHeader *msg, int can_drop,
+                     int options, uint16_t type)
 {
-  struct Client *c;
+  struct GSC_Client *c;
 
   for (c = client_head; c != NULL; c = c->next)
   {
-    if ( (0 == (c->options & options)) &&
-        (GNUNET_YES != type_match (type, c)) )
-      continue;
-#if DEBUG_CORE_CLIENT > 1
+    if ((0 == (options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) &&
+        (GNUNET_YES == type_match (type, c)))
+      continue;                 /* not the full message, but we'd like the full one! */
+    if ((0 == (c->options & options)) && (GNUNET_YES != type_match (type, c)))
+      continue;                 /* neither options nor type match permit the message */
+#if DEBUG_CORE
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-               "Sending message of type %u to client.\n",
-               (unsigned int) ntohs (msg->type));
+                "Sending message to client interested in messages of type %u.\n",
+                (unsigned int) type);
+#endif
+#if DEBUG_CONNECTS
+    GNUNET_assert (GNUNET_YES ==
+                   GNUNET_CONTAINER_multihashmap_contains (c->connectmap,
+                                                           &sender->hashPubKey));
 #endif
     send_to_client (c, msg, can_drop);
   }
@@ -209,7 +257,7 @@ handle_client_init (void *cls, struct GNUNET_SERVER_Client *client,
 {
   const struct InitMessage *im;
   struct InitReplyMessage irm;
-  struct Client *c;
+  struct GSC_Client *c;
   uint16_t msize;
   const uint16_t *types;
   uint16_t *wtypes;
@@ -234,31 +282,37 @@ handle_client_init (void *cls, struct GNUNET_SERVER_Client *client,
   im = (const struct InitMessage *) message;
   types = (const uint16_t *) &im[1];
   msize -= sizeof (struct InitMessage);
-  c = GNUNET_malloc (sizeof (struct Client) + msize);
+  c = GNUNET_malloc (sizeof (struct GSC_Client) + msize);
   c->client_handle = client;
   c->tcnt = msize / sizeof (uint16_t);
   c->options = ntohl (im->options);
   c->types = (const uint16_t *) &c[1];
+#if DEBUG_CONNECTS
+  c->connectmap = GNUNET_CONTAINER_multihashmap_create (16);
+  GNUNET_assert (GNUNET_YES ==
+                 GNUNET_CONTAINER_multihashmap_put (c->connectmap,
+                                                    &GSC_my_identity.hashPubKey,
+                                                    NULL,
+                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+#endif
+
   wtypes = (uint16_t *) & c[1];
   for (i = 0; i < c->tcnt; i++)
     wtypes[i] = ntohs (types[i]);
   GSC_TYPEMAP_add (wtypes, c->tcnt);
-  GNUNET_CONTAINER_DLL_insert (client_head,
-                              client_tail,
-                              c);
-#if DEBUG_CORE_CLIENT
+  GNUNET_CONTAINER_DLL_insert (client_head, client_tail, c);
+#if DEBUG_CORE
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Client connecting to core service is interested in %u message types\n", 
+              "Client connecting to core service is interested in %u message types\n",
               (unsigned int) c->tcnt);
 #endif
   /* send init reply message */
   irm.header.size = htons (sizeof (struct InitReplyMessage));
   irm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY);
   irm.reserved = htonl (0);
-  irm.publicKey = GSC_my_public_key;
+  irm.my_identity = GSC_my_identity;
   send_to_client (c, &irm.header, GNUNET_NO);
-  if (0 != (c->options & GNUNET_CORE_OPTION_SEND_CONNECT))
-    GSC_SESSIONS_notify_client_about_sessions (c);
+  GSC_SESSIONS_notify_client_about_sessions (c);
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
@@ -268,15 +322,15 @@ handle_client_init (void *cls, struct GNUNET_SERVER_Client *client,
  *
  * @param cls unused
  * @param client new client that sent CORE_SEND_REQUEST
- * @param message the 'struct InitMessage' (presumably)
+ * @param message the 'struct SendMessageRequest' (presumably)
  */
 static void
 handle_client_send_request (void *cls, struct GNUNET_SERVER_Client *client,
                             const struct GNUNET_MessageHeader *message)
 {
   const struct SendMessageRequest *req;
-  struct Client *c;
-  struct ClientActiveRequest *car;
+  struct GSC_Client *c;
+  struct GSC_ClientActiveRequest *car;
 
   req = (const struct SendMessageRequest *) message;
   c = find_client (client);
@@ -289,27 +343,36 @@ handle_client_send_request (void *cls, struct GNUNET_SERVER_Client *client,
   }
   if (c->requests == NULL)
     c->requests = GNUNET_CONTAINER_multihashmap_create (16);
+#if DEBUG_CORE
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Client asked for transmission to `%s'\n",
+              GNUNET_i2s (&req->peer));
+#endif
   car = GNUNET_CONTAINER_multihashmap_get (c->requests, &req->peer.hashPubKey);
   if (car == NULL)
   {
     /* create new entry */
-    car = GNUNET_malloc (sizeof (struct ClientActiveRequest));
+    car = GNUNET_malloc (sizeof (struct GSC_ClientActiveRequest));
     GNUNET_assert (GNUNET_OK ==
                    GNUNET_CONTAINER_multihashmap_put (c->requests,
                                                       &req->peer.hashPubKey,
                                                       car,
                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
-    car->client = c;
+    car->client_handle = c;
+  }
+  else
+  {
+    GSC_SESSIONS_dequeue_request (car);
   }
   car->target = req->peer;
-  GNUNET_SERVER_client_keep (client);
-  car->client_handle = client;
   car->deadline = GNUNET_TIME_absolute_ntoh (req->deadline);
   car->priority = ntohl (req->priority);
   car->msize = ntohs (req->size);
   car->smr_id = req->smr_id;
+  car->was_solicited = GNUNET_NO;
   if (0 ==
-      memcmp (&req->peer, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity)))
+      memcmp (&req->peer, &GSC_my_identity,
+              sizeof (struct GNUNET_PeerIdentity)))
     GSC_CLIENTS_solicit_request (car);
   else
     GSC_SESSIONS_queue_request (car);
@@ -317,6 +380,25 @@ handle_client_send_request (void *cls, struct GNUNET_SERVER_Client *client,
 }
 
 
+/**
+ * Closure for the 'client_tokenizer_callback'.
+ */
+struct TokenizerContext
+{
+
+  /**
+   * Active request handle for the message.
+   */
+  struct GSC_ClientActiveRequest *car;
+
+  /**
+   * Is corking allowed (set only once we have the real message).
+   */
+  int cork;
+
+};
+
+
 /**
  * Handle CORE_SEND request.
  *
@@ -329,8 +411,8 @@ handle_client_send (void *cls, struct GNUNET_SERVER_Client *client,
                     const struct GNUNET_MessageHeader *message)
 {
   const struct SendMessage *sm;
-  struct Client *c;
-  struct ClientActiveRequest *car;
+  struct GSC_Client *c;
+  struct TokenizerContext tc;
   uint16_t msize;
 
   msize = ntohs (message->size);
@@ -352,27 +434,39 @@ handle_client_send (void *cls, struct GNUNET_SERVER_Client *client,
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
   }
-  car = GNUNET_CONTAINER_multihashmap_get (c->requests, &sm->peer.hashPubKey);
-  if (NULL == car)
+  tc.car =
+      GNUNET_CONTAINER_multihashmap_get (c->requests, &sm->peer.hashPubKey);
+  if (NULL == tc.car)
   {
-    /* client did not request transmission first! */
-    GNUNET_break (0);
-    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    /* Must have been that we first approved the request, then got disconnected
+     * (which triggered removal of the 'car') and now the client gives us a message
+     * just *before* the client learns about the disconnect.  Theoretically, we
+     * might also now be *again* connected.  So this can happen (but should be
+     * rare).  If it does happen, the message is discarded. */
+    GNUNET_STATISTICS_update (GSC_stats,
+                              gettext_noop
+                              ("# messages discarded (session disconnected)"),
+                              1, GNUNET_NO);
+    GNUNET_SERVER_receive_done (client, GNUNET_OK);
     return;
   }
   GNUNET_assert (GNUNET_YES ==
-                GNUNET_CONTAINER_multihashmap_remove (c->requests, 
-                                                      &sm->peer.hashPubKey,
-                                                      car));
-  GNUNET_SERVER_mst_receive (client_mst,
-                            car, 
-                            &sm[1], msize,
-                            GNUNET_YES,
-                            GNUNET_NO);
+                 GNUNET_CONTAINER_multihashmap_remove (c->requests,
+                                                       &sm->peer.hashPubKey,
+                                                       tc.car));
+  tc.cork = ntohl (sm->cork);
+#if DEBUG_CORE
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Client asked for transmission of %u bytes to `%s' %s\n", msize,
+              GNUNET_i2s (&sm->peer), tc.cork ? "now" : "");
+#endif
+  GNUNET_SERVER_mst_receive (client_mst, &tc, (const char *) &sm[1], msize,
+                             GNUNET_YES, GNUNET_NO);
   if (0 !=
-      memcmp (&car->peer, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity)))  
-    GSC_SESSIONS_dequeue_request (car);
-  GNUNET_free (car);  
+      memcmp (&tc.car->target, &GSC_my_identity,
+              sizeof (struct GNUNET_PeerIdentity)))
+    GSC_SESSIONS_dequeue_request (tc.car);
+  GNUNET_free (tc.car);
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
@@ -384,20 +478,43 @@ handle_client_send (void *cls, struct GNUNET_SERVER_Client *client,
  * or other CLIENT (for loopback).
  *
  * @param cls closure
- * @param client reservation request ('struct ClientActiveRequest')
+ * @param client reservation request ('struct GSC_ClientActiveRequest')
  * @param message the actual message
  */
 static void
 client_tokenizer_callback (void *cls, void *client,
-                          const struct GNUNET_MessageHeader *message)
+                           const struct GNUNET_MessageHeader *message)
 {
-  struct ClientActiveRequest *car = client;
+  struct TokenizerContext *tc = client;
+  struct GSC_ClientActiveRequest *car = tc->car;
 
   if (0 ==
-      memcmp (&car->peer, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity)))  
-    GDS_CLIENTS_deliver_message (&GSC_my_identity, &payload->header);  
+      memcmp (&car->target, &GSC_my_identity,
+              sizeof (struct GNUNET_PeerIdentity)))
+  {
+#if DEBUG_CORE
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Delivering message of type %u to myself\n",
+                ntohs (message->type));
+#endif
+    GSC_CLIENTS_deliver_message (&GSC_my_identity, NULL, 0, message,
+                                 ntohs (message->size),
+                                 GNUNET_CORE_OPTION_SEND_FULL_INBOUND |
+                                 GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND);
+    GSC_CLIENTS_deliver_message (&GSC_my_identity, NULL, 0, message,
+                                 sizeof (struct GNUNET_MessageHeader),
+                                 GNUNET_CORE_OPTION_SEND_HDR_INBOUND |
+                                 GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND);
+  }
   else
-    GSC_SESSIONS_transmit (car, &payload->header);
+  {
+#if DEBUG_CORE
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Delivering message of type %u to %s\n", ntohs (message->type),
+                GNUNET_i2s (&car->target));
+#endif
+    GSC_SESSIONS_transmit (car, message, tc->cork);
+  }
 }
 
 
@@ -406,15 +523,20 @@ client_tokenizer_callback (void *cls, void *client,
  *
  * @param cls NULL
  * @param key identity of peer for which this is an active request
- * @param value the 'struct ClientActiveRequest' to free
+ * @param value the 'struct GSC_ClientActiveRequest' to free
  * @return GNUNET_YES (continue iteration)
  */
 static int
 destroy_active_client_request (void *cls, const GNUNET_HashCode * key,
                                void *value)
 {
-  struct ClientActiveRequest *car = value;
+  struct GSC_ClientActiveRequest *car = value;
 
+  GNUNET_assert (GNUNET_YES ==
+                 GNUNET_CONTAINER_multihashmap_remove (car->
+                                                       client_handle->requests,
+                                                       &car->target.hashPubKey,
+                                                       car));
   GSC_SESSIONS_dequeue_request (car);
   GNUNET_free (car);
   return GNUNET_YES;
@@ -428,23 +550,20 @@ destroy_active_client_request (void *cls, const GNUNET_HashCode * key,
  * @param client identification of the client
  */
 static void
-handle_client_disconnect (void *cls,
-                         struct GNUNET_SERVER_Client *client)
+handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
 {
-  struct Client *c;
+  struct GSC_Client *c;
 
   if (client == NULL)
     return;
-#if DEBUG_CORE_CLIENT
+#if DEBUG_CORE
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Client %p has disconnected from core service.\n", client);
 #endif
   c = find_client (client);
   if (c == NULL)
-    return; /* client never sent INIT */
-  GNUNET_CONTAINER_DLL_remove (client_head,
-                              client_tail,
-                              c);
+    return;                     /* client never sent INIT */
+  GNUNET_CONTAINER_DLL_remove (client_head, client_tail, c);
   if (c->requests != NULL)
   {
     GNUNET_CONTAINER_multihashmap_iterate (c->requests,
@@ -452,19 +571,14 @@ handle_client_disconnect (void *cls,
                                            NULL);
     GNUNET_CONTAINER_multihashmap_destroy (c->requests);
   }
+#if DEBUG_CONNECTS
+  GNUNET_CONTAINER_multihashmap_destroy (c->connectmap);
+#endif
   GSC_TYPEMAP_remove (c->types, c->tcnt);
   GNUNET_free (c);
 }
 
 
-
-
-
-
-// FIXME from here.......................................
-
-
-
 /**
  * Tell a client that we are ready to receive the message.
  *
@@ -475,6 +589,22 @@ handle_client_disconnect (void *cls,
 void
 GSC_CLIENTS_solicit_request (struct GSC_ClientActiveRequest *car)
 {
+  struct GSC_Client *c;
+  struct SendMessageReady smr;
+
+  c = car->client_handle;
+  smr.header.size = htons (sizeof (struct SendMessageReady));
+  smr.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SEND_READY);
+  smr.size = htons (car->msize);
+  smr.smr_id = car->smr_id;
+  smr.peer = car->target;
+#if DEBUG_CONNECTS
+  GNUNET_assert (GNUNET_YES ==
+                 GNUNET_CONTAINER_multihashmap_contains (c->connectmap,
+                                                         &car->
+                                                         target.hashPubKey));
+#endif
+  send_to_client (c, &smr.header, GNUNET_NO);
 }
 
 
@@ -489,458 +619,197 @@ GSC_CLIENTS_solicit_request (struct GSC_ClientActiveRequest *car)
 void
 GSC_CLIENTS_reject_request (struct GSC_ClientActiveRequest *car)
 {
+  GNUNET_assert (GNUNET_YES ==
+                 GNUNET_CONTAINER_multihashmap_remove (car->
+                                                       client_handle->requests,
+                                                       &car->target.hashPubKey,
+                                                       car));
+  GNUNET_free (car);
 }
 
 
-
-
 /**
- * Notify client about an existing connection to one of our neighbours.
+ * Notify a particular client about a change to existing connection to
+ * one of our neighbours (check if the client is interested).  Called
+ * from 'GSC_SESSIONS_notify_client_about_sessions'.
+ *
+ * @param client client to notify
+ * @param neighbour identity of the neighbour that changed status
+ * @param atsi performance information about neighbour
+ * @param atsi_count number of entries in 'ats' array
+ * @param tmap_old previous type map for the neighbour, NULL for disconnect
+ * @param tmap_new updated type map for the neighbour, NULL for disconnect
+ * @param is_new GNUNET_YES if this is a completely new neighbour
  */
-static int
-notify_client_about_neighbour (void *cls, const GNUNET_HashCode * key,
-                               void *value)
+void
+GSC_CLIENTS_notify_client_about_neighbour (struct GSC_Client *client,
+                                           const struct GNUNET_PeerIdentity
+                                           *neighbour,
+                                           const struct GNUNET_ATS_Information
+                                           *atsi, unsigned int atsi_count,
+                                           const struct GSC_TypeMap *tmap_old,
+                                           const struct GSC_TypeMap *tmap_new)
 {
-  struct Client *c = cls;
-  struct Neighbour *n = value;
+  struct ConnectNotifyMessage *cnm;
   size_t size;
   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
-  struct GNUNET_TRANSPORT_ATS_Information *ats;
-  struct ConnectNotifyMessage *cnm;
-
-  size =
-      sizeof (struct ConnectNotifyMessage) +
-      (n->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information);
-  if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+  struct GNUNET_ATS_Information *a;
+  struct DisconnectNotifyMessage dcm;
+  int old_match;
+  int new_match;
+
+  old_match = GSC_TYPEMAP_test_match (tmap_old, client->types, client->tcnt);
+  new_match = GSC_TYPEMAP_test_match (tmap_new, client->types, client->tcnt);
+  if (old_match == new_match)
   {
-    GNUNET_break (0);
-    /* recovery strategy: throw away performance data */
-    GNUNET_array_grow (n->ats, n->ats_count, 0);
-    size =
-        sizeof (struct ConnectNotifyMessage) +
-        (n->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information);
+    GNUNET_assert (old_match ==
+                   GNUNET_CONTAINER_multihashmap_contains (client->connectmap,
+                                                           &neighbour->hashPubKey));
+    return;                     /* no change */
   }
-  cnm = (struct ConnectNotifyMessage *) buf;
-  cnm->header.size = htons (size);
-  cnm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
-  cnm->ats_count = htonl (n->ats_count);
-  ats = &cnm->ats;
-  memcpy (ats, n->ats,
-          sizeof (struct GNUNET_TRANSPORT_ATS_Information) * n->ats_count);
-  ats[n->ats_count].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
-  ats[n->ats_count].value = htonl (0);
-  if (n->status == PEER_STATE_KEY_CONFIRMED)
+  if (old_match == GNUNET_NO)
   {
-#if DEBUG_CORE_CLIENT
+    /* send connect */
+#if DEBUG_CONNECTS
+    GNUNET_assert (GNUNET_NO ==
+                   GNUNET_CONTAINER_multihashmap_contains (client->connectmap,
+                                                           &neighbour->hashPubKey));
+    GNUNET_assert (GNUNET_YES ==
+                   GNUNET_CONTAINER_multihashmap_put (client->connectmap,
+                                                      &neighbour->hashPubKey,
+                                                      NULL,
+                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+#endif
+    size =
+        sizeof (struct ConnectNotifyMessage) +
+        (atsi_count) * sizeof (struct GNUNET_ATS_Information);
+    if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+    {
+      GNUNET_break (0);
+      /* recovery strategy: throw away performance data */
+      atsi_count = 0;
+      size = sizeof (struct ConnectNotifyMessage);
+    }
+    cnm = (struct ConnectNotifyMessage *) buf;
+    cnm->header.size = htons (size);
+    cnm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
+    cnm->ats_count = htonl (atsi_count);
+    a = (struct GNUNET_ATS_Information *) &cnm[1];
+    memcpy (a, atsi, sizeof (struct GNUNET_ATS_Information) * atsi_count);
+#if DEBUG_CORE
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message to client.\n",
                 "NOTIFY_CONNECT");
 #endif
-    cnm->peer = n->peer;
-    send_to_client (c, &cnm->header, GNUNET_NO);
+    cnm->peer = *neighbour;
+    send_to_client (client, &cnm->header, GNUNET_NO);
   }
-  return GNUNET_OK;
-}
-
-
-
-/**
- * Helper function for handle_client_iterate_peers.
- *
- * @param cls the 'struct GNUNET_SERVER_TransmitContext' to queue replies
- * @param key identity of the connected peer
- * @param value the 'struct Neighbour' for the peer
- * @return GNUNET_OK (continue to iterate)
- */
-static int
-queue_connect_message (void *cls, const GNUNET_HashCode * key, void *value)
-{
-  struct GNUNET_SERVER_TransmitContext *tc = cls;
-  struct Neighbour *n = value;
-  char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
-  struct GNUNET_TRANSPORT_ATS_Information *ats;
-  size_t size;
-  struct ConnectNotifyMessage *cnm;
-
-  cnm = (struct ConnectNotifyMessage *) buf;
-  if (n->status != PEER_STATE_KEY_CONFIRMED)
-    return GNUNET_OK;
-  size =
-      sizeof (struct ConnectNotifyMessage) +
-      (n->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information);
-  if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+  else
   {
-    GNUNET_break (0);
-    /* recovery strategy: throw away performance data */
-    GNUNET_array_grow (n->ats, n->ats_count, 0);
-    size =
-        sizeof (struct PeerStatusNotifyMessage) +
-        n->ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information);
-  }
-  cnm = (struct ConnectNotifyMessage *) buf;
-  cnm->header.size = htons (size);
-  cnm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
-  cnm->ats_count = htonl (n->ats_count);
-  ats = &cnm->ats;
-  memcpy (ats, n->ats,
-          n->ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information));
-  ats[n->ats_count].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
-  ats[n->ats_count].value = htonl (0);
-#if DEBUG_CORE_CLIENT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message to client.\n",
-              "NOTIFY_CONNECT");
+    /* send disconnect */
+#if DEBUG_CONNECTS
+    GNUNET_assert (GNUNET_YES ==
+                   GNUNET_CONTAINER_multihashmap_contains (client->connectmap,
+                                                           &neighbour->hashPubKey));
+    GNUNET_assert (GNUNET_YES ==
+                   GNUNET_CONTAINER_multihashmap_remove (client->connectmap,
+                                                         &neighbour->hashPubKey,
+                                                         NULL));
 #endif
-  cnm->peer = n->peer;
-  GNUNET_SERVER_transmit_context_append_message (tc, &cnm->header);
-  return GNUNET_OK;
-}
-
-
-/**
- * Handle CORE_ITERATE_PEERS request.
- *
- * @param cls unused
- * @param client client sending the iteration request
- * @param message iteration request message
- */
-static void
-handle_client_iterate_peers (void *cls, struct GNUNET_SERVER_Client *client,
-                             const struct GNUNET_MessageHeader *message)
-{
-  struct GNUNET_MessageHeader done_msg;
-  struct GNUNET_SERVER_TransmitContext *tc;
-  int msize;
-
-  /* notify new client about existing neighbours */
-
-  msize = ntohs (message->size);
-  tc = GNUNET_SERVER_transmit_context_create (client);
-  if (msize == sizeof (struct GNUNET_MessageHeader))
-    GNUNET_CONTAINER_multihashmap_iterate (neighbours, &queue_connect_message,
-                                           tc);
-  else
-    GNUNET_break (0);
-
-  done_msg.size = htons (sizeof (struct GNUNET_MessageHeader));
-  done_msg.type = htons (GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS_END);
-  GNUNET_SERVER_transmit_context_append_message (tc, &done_msg);
-  GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
-}
-
-
-/**
- * Handle CORE_PEER_CONNECTED request.  Notify client about existing neighbours.
- *
- * @param cls unused
- * @param client client sending the iteration request
- * @param message iteration request message
- */
-static void
-handle_client_have_peer (void *cls, struct GNUNET_SERVER_Client *client,
-                         const struct GNUNET_MessageHeader *message)
-{
-  struct GNUNET_MessageHeader done_msg;
-  struct GNUNET_SERVER_TransmitContext *tc;
-  struct GNUNET_PeerIdentity *peer;
-
-  tc = GNUNET_SERVER_transmit_context_create (client);
-  peer = (struct GNUNET_PeerIdentity *) &message[1];
-  GNUNET_CONTAINER_multihashmap_get_multiple (neighbours, &peer->hashPubKey,
-                                              &queue_connect_message, tc);
-  done_msg.size = htons (sizeof (struct GNUNET_MessageHeader));
-  done_msg.type = htons (GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS_END);
-  GNUNET_SERVER_transmit_context_append_message (tc, &done_msg);
-  GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
+    dcm.header.size = htons (sizeof (struct DisconnectNotifyMessage));
+    dcm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT);
+    dcm.reserved = htonl (0);
+    dcm.peer = *neighbour;
+    send_to_client (client, &dcm.header, GNUNET_NO);
+  }
 }
 
 
 /**
- * Handle REQUEST_INFO request.
+ * Notify all clients about a change to existing session.
+ * Called from SESSIONS whenever there is a change in sessions
+ * or types processed by the respective peer.
  *
- * @param cls unused
- * @param client client sending the request
- * @param message iteration request message
+ * @param neighbour identity of the neighbour that changed status
+ * @param atsi performance information about neighbour
+ * @param atsi_count number of entries in 'ats' array
+ * @param tmap_old previous type map for the neighbour, NULL for disconnect
+ * @param tmap_new updated type map for the neighbour, NULL for disconnect
  */
-static void
-handle_client_request_info (void *cls, struct GNUNET_SERVER_Client *client,
-                            const struct GNUNET_MessageHeader *message)
+void
+GSC_CLIENTS_notify_clients_about_neighbour (const struct GNUNET_PeerIdentity
+                                            *neighbour,
+                                            const struct GNUNET_ATS_Information
+                                            *atsi, unsigned int atsi_count,
+                                            const struct GSC_TypeMap *tmap_old,
+                                            const struct GSC_TypeMap *tmap_new)
 {
-  const struct RequestInfoMessage *rcm;
-  struct Client *pos;
-  struct Neighbour *n;
-  struct ConfigurationInfoMessage cim;
-  int32_t want_reserv;
-  int32_t got_reserv;
-  unsigned long long old_preference;
-  struct GNUNET_TIME_Relative rdelay;
-
-  rdelay = GNUNET_TIME_relative_get_zero ();
-#if DEBUG_CORE_CLIENT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Core service receives `%s' request.\n",
-              "REQUEST_INFO");
-#endif
-  pos = clients;
-  while (pos != NULL)
-  {
-    if (client == pos->client_handle)
-      break;
-    pos = pos->next;
-  }
-  if (pos == NULL)
-  {
-    GNUNET_break (0);
-    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
-    return;
-  }
+  struct GSC_Client *c;
 
-  rcm = (const struct RequestInfoMessage *) message;
-  n = find_neighbour (&rcm->peer);
-  memset (&cim, 0, sizeof (cim));
-  if ((n != NULL) && (GNUNET_YES == n->is_connected))
-  {
-    want_reserv = ntohl (rcm->reserve_inbound);
-    if (n->bw_out_internal_limit.value__ != rcm->limit_outbound.value__)
-    {
-      n->bw_out_internal_limit = rcm->limit_outbound;
-      if (n->bw_out.value__ !=
-          GNUNET_BANDWIDTH_value_min (n->bw_out_internal_limit,
-                                      n->bw_out_external_limit).value__)
-      {
-        n->bw_out =
-            GNUNET_BANDWIDTH_value_min (n->bw_out_internal_limit,
-                                        n->bw_out_external_limit);
-        GNUNET_BANDWIDTH_tracker_update_quota (&n->available_recv_window,
-                                               n->bw_out);
-        GNUNET_TRANSPORT_set_quota (transport, &n->peer, n->bw_in, n->bw_out);
-        handle_peer_status_change (n);
-      }
-    }
-    if (want_reserv < 0)
-    {
-      got_reserv = want_reserv;
-    }
-    else if (want_reserv > 0)
-    {
-      rdelay =
-          GNUNET_BANDWIDTH_tracker_get_delay (&n->available_recv_window,
-                                              want_reserv);
-      if (rdelay.rel_value == 0)
-        got_reserv = want_reserv;
-      else
-        got_reserv = 0;         /* all or nothing */
-    }
-    else
-      got_reserv = 0;
-    GNUNET_BANDWIDTH_tracker_consume (&n->available_recv_window, got_reserv);
-    old_preference = n->current_preference;
-    n->current_preference += GNUNET_ntohll (rcm->preference_change);
-    if (old_preference > n->current_preference)
-    {
-      /* overflow; cap at maximum value */
-      n->current_preference = ULLONG_MAX;
-    }
-    update_preference_sum (n->current_preference - old_preference);
-#if DEBUG_CORE_QUOTA
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Received reservation request for %d bytes for peer `%4s', reserved %d bytes, suggesting delay of %llu ms\n",
-                (int) want_reserv, GNUNET_i2s (&rcm->peer), (int) got_reserv,
-                (unsigned long long) rdelay.rel_value);
-#endif
-    cim.reserved_amount = htonl (got_reserv);
-    cim.reserve_delay = GNUNET_TIME_relative_hton (rdelay);
-    cim.bw_out = n->bw_out;
-    cim.preference = n->current_preference;
-  }
-  else
-  {
-    /* Technically, this COULD happen (due to asynchronous behavior),
-     * but it should be rare, so we should generate an info event
-     * to help diagnosis of serious errors that might be masked by this */
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                _
-                ("Client asked for preference change with peer `%s', which is not connected!\n"),
-                GNUNET_i2s (&rcm->peer));
-    GNUNET_SERVER_receive_done (client, GNUNET_OK);
-    return;
-  }
-  cim.header.size = htons (sizeof (struct ConfigurationInfoMessage));
-  cim.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_CONFIGURATION_INFO);
-  cim.peer = rcm->peer;
-  cim.rim_id = rcm->rim_id;
-#if DEBUG_CORE_CLIENT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message to client.\n",
-              "CONFIGURATION_INFO");
-#endif
-  send_to_client (pos, &cim.header, GNUNET_NO);
-  GNUNET_SERVER_receive_done (client, GNUNET_OK);
+  for (c = client_head; c != NULL; c = c->next)
+    GSC_CLIENTS_notify_client_about_neighbour (c, neighbour, atsi, atsi_count,
+                                               tmap_old, tmap_new);
 }
 
 
-
-
 /**
- * Send a P2P message to a client.
+ * Deliver P2P message to interested clients.  Caller must have checked
+ * that the sending peer actually lists the given message type as one
+ * of its types.
  *
- * @param sender who sent us the message?
- * @param client who should we give the message to?
- * @param m contains the message to transmit
- * @param msize number of bytes in buf to transmit
+ * @param sender peer who sent us the message
+ * @param atsi performance information about neighbour
+ * @param atsi_count number of entries in 'ats' array
+ * @param msg the message
+ * @param msize number of bytes to transmit
+ * @param options options for checking which clients should
+ *        receive the message
  */
-static void
-send_p2p_message_to_client (struct Neighbour *sender, struct Client *client,
-                            const void *m, size_t msize)
+void
+GSC_CLIENTS_deliver_message (const struct GNUNET_PeerIdentity *sender,
+                             const struct GNUNET_ATS_Information *atsi,
+                             unsigned int atsi_count,
+                             const struct GNUNET_MessageHeader *msg,
+                             uint16_t msize, int options)
 {
   size_t size =
       msize + sizeof (struct NotifyTrafficMessage) +
-      (sender->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information);
+      atsi_count * sizeof (struct GNUNET_ATS_Information);
   char buf[size];
   struct NotifyTrafficMessage *ntm;
-  struct GNUNET_TRANSPORT_ATS_Information *ats;
+  struct GNUNET_ATS_Information *a;
 
-  GNUNET_assert (GNUNET_YES == sender->is_connected);
-  GNUNET_break (sender->status == PEER_STATE_KEY_CONFIRMED);
+  if (0 == options)
+  {
+    GNUNET_snprintf (buf, sizeof (buf),
+                     gettext_noop ("# bytes of messages of type %u received"),
+                     (unsigned int) ntohs (msg->type));
+    GNUNET_STATISTICS_update (GSC_stats, buf, msize, GNUNET_NO);
+  }
   if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
   {
     GNUNET_break (0);
     /* recovery strategy: throw performance data away... */
-    GNUNET_array_grow (sender->ats, sender->ats_count, 0);
-    size =
-        msize + sizeof (struct NotifyTrafficMessage) +
-        (sender->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information);
+    atsi_count = 0;
+    size = msize + sizeof (struct NotifyTrafficMessage);
   }
 #if DEBUG_CORE
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Core service passes message from `%4s' of type %u to client.\n",
-              GNUNET_i2s (&sender->peer),
-              (unsigned int)
-              ntohs (((const struct GNUNET_MessageHeader *) m)->type));
+              GNUNET_i2s (sender), (unsigned int) ntohs (msg->type));
 #endif
+  GSC_SESSIONS_add_to_typemap (sender, ntohs (msg->type));
   ntm = (struct NotifyTrafficMessage *) buf;
   ntm->header.size = htons (size);
   ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND);
-  ntm->ats_count = htonl (sender->ats_count);
-  ntm->peer = sender->peer;
-  ats = &ntm->ats;
-  memcpy (ats, sender->ats,
-          sizeof (struct GNUNET_TRANSPORT_ATS_Information) * sender->ats_count);
-  ats[sender->ats_count].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
-  ats[sender->ats_count].value = htonl (0);
-  memcpy (&ats[sender->ats_count + 1], m, msize);
-  send_to_client (client, &ntm->header, GNUNET_YES);
-}
-
-
-
-/**
- * Notify a particular client about a change to existing connection to
- * one of our neighbours (check if the client is interested).  Called
- * from 'GSC_SESSIONS_notify_client_about_sessions'.
- *
- * @param client client to notify
- * @param neighbour identity of the neighbour that changed status
- * @param tmap_old previous type map for the neighbour, NULL for disconnect
- * @param tmap_new updated type map for the neighbour, NULL for disconnect
- */
-void
-GDS_CLIENTS_notify_client_about_neighbour (struct GSC_Client *client,
-                                          const struct GNUNET_PeerIdentity *neighbour,
-                                          const struct GSC_TypeMap *tmap_old,
-                                          const struct GSC_TypeMap *tmap_new)
-{
-}
-
-
-/**
- * Notify client about a change to existing connection to one of our neighbours.
- *
- * @param neighbour identity of the neighbour that changed status
- * @param tmap_old previous type map for the neighbour, NULL for disconnect
- * @param tmap_new updated type map for the neighbour, NULL for disconnect
- */
-void
-GDS_CLIENTS_notify_clients_about_neighbour (const struct GNUNET_PeerIdentity *neighbour,
-                                           const struct GSC_TypeMap *tmap_old,
-                                           const struct GSC_TypeMap *tmap_new)
-{
-}
-
-
-/**
- * Deliver P2P message to interested clients.
- *
- * @param sender peer who sent us the message 
- * @param m the message
- */
-void
-GSC_CLIENTS_deliver_message (const struct GNUNET_PeerIdentity *sender,
-                            const struct GNUNET_MessageHeader *m)
-{
-  struct Neighbour *sender = client;
-  size_t msize = ntohs (m->size);
-  char buf[256];
-  struct Client *cpos;
-  uint16_t type;
-  unsigned int tpos;
-  int deliver_full;
-  int dropped;
-
-  GNUNET_break (sender->status == PEER_STATE_KEY_CONFIRMED);
-  type = ntohs (m->type);
-#if DEBUG_CORE > 1
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Received encapsulated message of type %u and size %u from `%4s'\n",
-              (unsigned int) type, ntohs (m->size), GNUNET_i2s (&sender->peer));
-#endif
-  GNUNET_snprintf (buf, sizeof (buf),
-                   gettext_noop ("# bytes of messages of type %u received"),
-                   (unsigned int) type);
-  GNUNET_STATISTICS_update (stats, buf, msize, GNUNET_NO);
-  if ((GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP == type) ||
-      (GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP == type))
-  {
-    /* FIXME: update message type map for 'Neighbour' */
-    return;
-  }
-  dropped = GNUNET_YES;
-  cpos = clients;
-  while (cpos != NULL)
-  {
-    deliver_full = GNUNET_NO;
-    if (0 != (cpos->options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND))
-      deliver_full = GNUNET_YES;
-    else
-    {
-      for (tpos = 0; tpos < cpos->tcnt; tpos++)
-      {
-        if (type != cpos->types[tpos])
-          continue;
-        deliver_full = GNUNET_YES;
-        break;
-      }
-    }
-    if (GNUNET_YES == deliver_full)
-    {
-      send_p2p_message_to_client (sender, cpos, m, msize);
-      dropped = GNUNET_NO;
-    }
-    else if (cpos->options & GNUNET_CORE_OPTION_SEND_HDR_INBOUND)
-    {
-      send_p2p_message_to_client (sender, cpos, m,
-                                  sizeof (struct GNUNET_MessageHeader));
-    }
-    cpos = cpos->next;
-  }
-  if (dropped == GNUNET_YES)
-  {
-#if DEBUG_CORE
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Message of type %u from `%4s' not delivered to any client.\n",
-                (unsigned int) type, GNUNET_i2s (&sender->peer));
-#endif
-    GNUNET_STATISTICS_update (stats,
-                              gettext_noop
-                              ("# messages not delivered to any client"), 1,
-                              GNUNET_NO);
-  }
+  ntm->ats_count = htonl (atsi_count);
+  ntm->peer = *sender;
+  a = &ntm->ats;
+  memcpy (a, atsi, sizeof (struct GNUNET_ATS_Information) * atsi_count);
+  a[atsi_count].type = htonl (GNUNET_ATS_ARRAY_TERMINATOR);
+  a[atsi_count].value = htonl (0);
+  memcpy (&a[atsi_count + 1], msg, msize);
+  send_to_all_clients (sender, &ntm->header, GNUNET_YES, options,
+                       ntohs (msg->type));
 }
 
 
@@ -955,16 +824,13 @@ GSC_CLIENTS_init (struct GNUNET_SERVER_Handle *server)
   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
     {&handle_client_init, NULL,
      GNUNET_MESSAGE_TYPE_CORE_INIT, 0},
-    {&handle_client_iterate_peers, NULL,
+    {&GSC_SESSIONS_handle_client_iterate_peers, NULL,
      GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS,
      sizeof (struct GNUNET_MessageHeader)},
-    {&handle_client_have_peer, NULL,
+    {&GSC_SESSIONS_handle_client_have_peer, NULL,
      GNUNET_MESSAGE_TYPE_CORE_PEER_CONNECTED,
      sizeof (struct GNUNET_MessageHeader) +
      sizeof (struct GNUNET_PeerIdentity)},
-    {&handle_client_request_info, NULL,
-     GNUNET_MESSAGE_TYPE_CORE_REQUEST_INFO,
-     sizeof (struct RequestInfoMessage)},
     {&handle_client_send_request, NULL,
      GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST,
      sizeof (struct SendMessageRequest)},
@@ -988,13 +854,16 @@ GSC_CLIENTS_init (struct GNUNET_SERVER_Handle *server)
 void
 GSC_CLIENTS_done ()
 {
-  struct Client *c;
+  struct GSC_Client *c;
 
-  while (NULL != (c = client_head))  
+  while (NULL != (c = client_head))
     handle_client_disconnect (NULL, c->client_handle);
-  GNUNET_SERVER_notification_context_destroy (notifier);
-  notifier = NULL;
-  GNUNET_SERVER_MST_destroy (client_mst);
+  if (NULL != notifier)
+  {
+    GNUNET_SERVER_notification_context_destroy (notifier);
+    notifier = NULL;
+  }
+  GNUNET_SERVER_mst_destroy (client_mst);
   client_mst = NULL;
 }