additional assertions to help with #1840 and #1835
[oweals/gnunet.git] / src / core / gnunet-service-core_clients.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file core/gnunet-service-core_clients.c
23  * @brief code for managing interactions with clients of core service
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_statistics_service.h"
29 #include "gnunet_transport_service.h"
30 #include "gnunet-service-core.h"
31 #include "gnunet-service-core_clients.h"
32 #include "gnunet-service-core_sessions.h"
33 #include "gnunet-service-core_typemap.h"
34 #include "core.h"
35
36 #define DEBUG_CONNECTS GNUNET_YES
37
38 /**
39  * How many messages do we queue up at most for optional
40  * notifications to a client?  (this can cause notifications
41  * about outgoing messages to be dropped).
42  */
43 #define MAX_NOTIFY_QUEUE 1024
44
45
46 /**
47  * Data structure for each client connected to the core service.
48  */
49 struct GSC_Client
50 {
51   /**
52    * Clients are kept in a linked list.
53    */
54   struct GSC_Client *next;
55
56   /**
57    * Clients are kept in a linked list.
58    */
59   struct GSC_Client *prev;
60
61   /**
62    * Handle for the client with the server API.
63    */
64   struct GNUNET_SERVER_Client *client_handle;
65
66   /**
67    * Array of the types of messages this peer cares
68    * about (with "tcnt" entries).  Allocated as part
69    * of this client struct, do not free!
70    */
71   const uint16_t *types;
72
73   /**
74    * Map of peer identities to active transmission requests of this
75    * client to the peer (of type 'struct GSC_ClientActiveRequest').
76    */
77   struct GNUNET_CONTAINER_MultiHashMap *requests;
78
79 #if DEBUG_CONNECTS
80   /**
81    * Map containing all peers that this client knows we're connected to.
82    */
83   struct GNUNET_CONTAINER_MultiHashMap *connectmap;
84 #endif
85
86   /**
87    * Options for messages this client cares about,
88    * see GNUNET_CORE_OPTION_ values.
89    */
90   uint32_t options;
91
92   /**
93    * Number of types of incoming messages this client
94    * specifically cares about.  Size of the "types" array.
95    */
96   unsigned int tcnt;
97
98 };
99
100
101 /**
102  * Head of linked list of our clients.
103  */
104 static struct GSC_Client *client_head;
105
106 /**
107  * Tail of linked list of our clients.
108  */
109 static struct GSC_Client *client_tail;
110
111 /**
112  * Context for notifications we need to send to our clients.
113  */
114 static struct GNUNET_SERVER_NotificationContext *notifier;
115
116 /**
117  * Tokenizer for messages received from clients.
118  */
119 static struct GNUNET_SERVER_MessageStreamTokenizer *client_mst;
120
121
122 /**
123  * Lookup our client struct given the server's client handle.
124  *
125  * @param client server client handle to look up
126  * @return our client handle for the client
127  */
128 static struct GSC_Client *
129 find_client (struct GNUNET_SERVER_Client *client)
130 {
131   struct GSC_Client *c;
132
133   c = client_head;
134   while ((c != NULL) && (c->client_handle != client))
135     c = c->next;
136   return c;
137 }
138
139
140 /**
141  * Send a message to one of our clients.
142  *
143  * @param client target for the message
144  * @param msg message to transmit
145  * @param can_drop could this message be dropped if the
146  *        client's queue is getting too large?
147  */
148 static void
149 send_to_client (struct GSC_Client *client, 
150                 const struct GNUNET_MessageHeader *msg,
151                 int can_drop)
152 {
153 #if DEBUG_CORE
154   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
155               "Preparing to send %u bytes of message of type %u to client.\n",
156               (unsigned int) ntohs (msg->size),
157               (unsigned int) ntohs (msg->type));
158 #endif
159   GNUNET_SERVER_notification_context_unicast (notifier, client->client_handle,
160                                               msg, can_drop);
161 }
162
163
164 /**
165  * Send a message to one of our clients.
166  *
167  * @param client target for the message
168  * @param msg message to transmit
169  * @param can_drop could this message be dropped if the
170  *        client's queue is getting too large?
171  */
172 void
173 GSC_CLIENTS_send_to_client (struct GNUNET_SERVER_Client *client,
174                             const struct GNUNET_MessageHeader *msg,
175                             int can_drop)
176 {
177   struct GSC_Client *c;
178
179   c = find_client (client);
180   if (NULL == c)
181   {
182     GNUNET_break (0);
183     return;
184   }
185   send_to_client (c, msg, can_drop);
186 }
187
188
189 /**
190  * Test if the client is interested in messages of the given type.
191  *
192  * @param type message type
193  * @param c client to test
194  * @return GNUNET_YES if 'c' is interested, GNUNET_NO if not.
195  */
196 static int
197 type_match (uint16_t type,
198             struct GSC_Client *c)
199 {
200   unsigned int i;
201
202   if (c->tcnt == 0)
203     return GNUNET_YES; /* peer without handlers matches ALL */
204   for (i=0;i<c->tcnt;i++)
205     if (type == c->types[i])
206       return GNUNET_YES;
207   return GNUNET_NO;
208 }
209
210
211 /**
212  * Send a message to all of our current clients that have the right
213  * options set.
214  *
215  * @param msg message to multicast
216  * @param can_drop can this message be discarded if the queue is too long
217  * @param options mask to use
218  * @param type type of the embedded message, 0 for none
219  */
220 static void
221 send_to_all_clients (const struct GNUNET_PeerIdentity *sender,
222                      const struct GNUNET_MessageHeader *msg, 
223                      int can_drop,
224                      int options,
225                      uint16_t type)
226 {
227   struct GSC_Client *c;
228
229   for (c = client_head; c != NULL; c = c->next)
230   {
231     if ( (0 == (options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) &&
232          (GNUNET_YES == type_match (type, c)) )
233       continue; /* not the full message, but we'd like the full one! */
234     if ( (0 == (c->options & options)) &&
235          (GNUNET_YES != type_match (type, c)) )
236       continue; /* neither options nor type match permit the message */
237 #if DEBUG_CORE
238     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
239                 "Sending message to client interested in messages of type %u.\n",
240                 (unsigned int) type);
241 #endif
242 #if DEBUG_CONNECTS
243     GNUNET_assert (GNUNET_YES ==
244                    GNUNET_CONTAINER_multihashmap_contains (c->connectmap,
245                                                            &sender->hashPubKey));
246 #endif
247     send_to_client (c, msg, can_drop);
248   }
249 }
250
251
252 /**
253  * Handle CORE_INIT request.
254  *
255  * @param cls unused
256  * @param client new client that sent INIT
257  * @param message the 'struct InitMessage' (presumably)
258  */
259 static void
260 handle_client_init (void *cls, struct GNUNET_SERVER_Client *client,
261                     const struct GNUNET_MessageHeader *message)
262 {
263   const struct InitMessage *im;
264   struct InitReplyMessage irm;
265   struct GSC_Client *c;
266   uint16_t msize;
267   const uint16_t *types;
268   uint16_t *wtypes;
269   unsigned int i;
270
271   /* check that we don't have an entry already */
272   c = find_client (client);
273   if (NULL != c)
274   {
275     GNUNET_break (0);
276     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
277     return;
278   }
279   msize = ntohs (message->size);
280   if (msize < sizeof (struct InitMessage))
281   {
282     GNUNET_break (0);
283     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
284     return;
285   }
286   GNUNET_SERVER_notification_context_add (notifier, client);
287   im = (const struct InitMessage *) message;
288   types = (const uint16_t *) &im[1];
289   msize -= sizeof (struct InitMessage);
290   c = GNUNET_malloc (sizeof (struct GSC_Client) + msize);
291   c->client_handle = client;
292   c->tcnt = msize / sizeof (uint16_t);
293   c->options = ntohl (im->options);
294   c->types = (const uint16_t *) &c[1];
295 #if DEBUG_CONNECTS
296   c->connectmap = GNUNET_CONTAINER_multihashmap_create (16);
297   GNUNET_assert (GNUNET_YES ==
298                  GNUNET_CONTAINER_multihashmap_put (c->connectmap,
299                                                     &GSC_my_identity.hashPubKey,
300                                                     NULL,
301                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
302 #endif
303
304   wtypes = (uint16_t *) & c[1];
305   for (i = 0; i < c->tcnt; i++)
306     wtypes[i] = ntohs (types[i]);
307   GSC_TYPEMAP_add (wtypes, c->tcnt);
308   GNUNET_CONTAINER_DLL_insert (client_head,
309                                client_tail,
310                                c);
311 #if DEBUG_CORE
312   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
313               "Client connecting to core service is interested in %u message types\n", 
314               (unsigned int) c->tcnt);
315 #endif
316   /* send init reply message */
317   irm.header.size = htons (sizeof (struct InitReplyMessage));
318   irm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY);
319   irm.reserved = htonl (0);
320   irm.my_identity = GSC_my_identity;
321   send_to_client (c, &irm.header, GNUNET_NO);
322   GSC_SESSIONS_notify_client_about_sessions (c);
323   GNUNET_SERVER_receive_done (client, GNUNET_OK);
324 }
325
326
327 /**
328  * Handle CORE_SEND_REQUEST message.
329  *
330  * @param cls unused
331  * @param client new client that sent CORE_SEND_REQUEST
332  * @param message the 'struct SendMessageRequest' (presumably)
333  */
334 static void
335 handle_client_send_request (void *cls, struct GNUNET_SERVER_Client *client,
336                             const struct GNUNET_MessageHeader *message)
337 {
338   const struct SendMessageRequest *req;
339   struct GSC_Client *c;
340   struct GSC_ClientActiveRequest *car;
341
342   req = (const struct SendMessageRequest *) message;
343   c = find_client (client);
344   if (c == NULL)
345   {
346     /* client did not send INIT first! */
347     GNUNET_break (0);
348     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
349     return;
350   }
351 #if DEBUG_CONNECTS
352   GNUNET_assert (GNUNET_YES ==
353                  GNUNET_CONTAINER_multihashmap_contains (c->connectmap,
354                                                          &req->peer.hashPubKey));
355 #endif
356   if (c->requests == NULL)
357     c->requests = GNUNET_CONTAINER_multihashmap_create (16);
358 #if DEBUG_CORE
359   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
360               "Client asked for transmission to `%s'\n",
361               GNUNET_i2s (&req->peer));
362 #endif
363   car = GNUNET_CONTAINER_multihashmap_get (c->requests, &req->peer.hashPubKey);
364   if (car == NULL)
365   {
366     /* create new entry */
367     car = GNUNET_malloc (sizeof (struct GSC_ClientActiveRequest));
368     GNUNET_assert (GNUNET_OK ==
369                    GNUNET_CONTAINER_multihashmap_put (c->requests,
370                                                       &req->peer.hashPubKey,
371                                                       car,
372                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
373     car->client_handle = c;
374   }
375   else
376   {
377     GSC_SESSIONS_dequeue_request (car);
378   }
379   car->target = req->peer;
380   car->deadline = GNUNET_TIME_absolute_ntoh (req->deadline);
381   car->priority = ntohl (req->priority);
382   car->msize = ntohs (req->size);
383   car->smr_id = req->smr_id;
384   car->was_solicited = GNUNET_NO;
385   if (0 ==
386       memcmp (&req->peer, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity)))
387     GSC_CLIENTS_solicit_request (car);
388   else
389     GSC_SESSIONS_queue_request (car);
390   GNUNET_SERVER_receive_done (client, GNUNET_OK);
391 }
392
393
394 /**
395  * Closure for the 'client_tokenizer_callback'.
396  */
397 struct TokenizerContext
398 {
399
400   /**
401    * Active request handle for the message.
402    */ 
403   struct GSC_ClientActiveRequest *car;
404
405   /**
406    * Is corking allowed (set only once we have the real message).
407    */
408   int cork;
409
410 };
411
412
413 /**
414  * Handle CORE_SEND request.
415  *
416  * @param cls unused
417  * @param client the client issuing the request
418  * @param message the "struct SendMessage"
419  */
420 static void
421 handle_client_send (void *cls, struct GNUNET_SERVER_Client *client,
422                     const struct GNUNET_MessageHeader *message)
423 {
424   const struct SendMessage *sm;
425   struct GSC_Client *c;
426   struct TokenizerContext tc;
427   uint16_t msize;
428
429   msize = ntohs (message->size);
430   if (msize <
431       sizeof (struct SendMessage) + sizeof (struct GNUNET_MessageHeader))
432   {
433     GNUNET_break (0);
434     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
435     return;
436   }
437   sm = (const struct SendMessage *) message;
438   msize -= sizeof (struct SendMessage);
439   GNUNET_break (0 == ntohl (sm->reserved));
440   c = find_client (client);
441   if (c == NULL)
442   {
443     /* client did not send INIT first! */
444     GNUNET_break (0);
445     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
446     return;
447   }
448   tc.car = GNUNET_CONTAINER_multihashmap_get (c->requests, &sm->peer.hashPubKey);
449   if (NULL == tc.car)
450   {
451     /* client did not request transmission first! */
452     GNUNET_break (0);
453     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
454     return;
455   }
456   GNUNET_assert (GNUNET_YES ==
457                  GNUNET_CONTAINER_multihashmap_remove (c->requests, 
458                                                        &sm->peer.hashPubKey,
459                                                        tc.car));
460   tc.cork = ntohl (sm->cork);
461 #if DEBUG_CORE
462   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
463               "Client asked for transmission of %u bytes to `%s' %s\n",
464               msize,
465               GNUNET_i2s (&sm->peer),
466               tc.cork ? "now" : "");
467 #endif
468   GNUNET_SERVER_mst_receive (client_mst,
469                              &tc, 
470                              (const char*) &sm[1], msize,
471                              GNUNET_YES,
472                              GNUNET_NO);
473   if (0 !=
474       memcmp (&tc.car->target, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity)))  
475     GSC_SESSIONS_dequeue_request (tc.car);
476   GNUNET_free (tc.car);  
477   GNUNET_SERVER_receive_done (client, GNUNET_OK);
478 }
479
480
481 /**
482  * Functions with this signature are called whenever a complete
483  * message is received by the tokenizer.  Used by the 'client_mst' for
484  * dispatching messages from clients to either the SESSION subsystem
485  * or other CLIENT (for loopback).
486  *
487  * @param cls closure
488  * @param client reservation request ('struct GSC_ClientActiveRequest')
489  * @param message the actual message
490  */
491 static void
492 client_tokenizer_callback (void *cls, void *client,
493                            const struct GNUNET_MessageHeader *message)
494 {
495   struct TokenizerContext *tc = client;
496   struct GSC_ClientActiveRequest *car = tc->car;
497
498   if (0 ==
499       memcmp (&car->target, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity)))  
500   {
501 #if DEBUG_CORE
502     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
503                 "Delivering message of type %u to myself\n",
504                 ntohs (message->type));
505 #endif
506     GSC_CLIENTS_deliver_message (&GSC_my_identity, 
507                                  NULL, 0,
508                                  message,
509                                  ntohs (message->size),
510                                  GNUNET_CORE_OPTION_SEND_FULL_INBOUND | GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND);  
511     GSC_CLIENTS_deliver_message (&GSC_my_identity, 
512                                  NULL, 0,
513                                  message,
514                                  sizeof (struct GNUNET_MessageHeader),
515                                  GNUNET_CORE_OPTION_SEND_HDR_INBOUND | GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND);  
516   }
517   else
518   {
519 #if DEBUG_CORE
520     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
521                 "Delivering message of type %u to %s\n",
522                 ntohs (message->type),
523                 GNUNET_i2s (&car->target));
524 #endif
525     GSC_SESSIONS_transmit (car, message, tc->cork);
526   }
527 }
528
529
530 /**
531  * Free client request records.
532  *
533  * @param cls NULL
534  * @param key identity of peer for which this is an active request
535  * @param value the 'struct GSC_ClientActiveRequest' to free
536  * @return GNUNET_YES (continue iteration)
537  */
538 static int
539 destroy_active_client_request (void *cls, const GNUNET_HashCode * key,
540                                void *value)
541 {
542   struct GSC_ClientActiveRequest *car = value;
543
544   GNUNET_assert (GNUNET_YES ==
545                  GNUNET_CONTAINER_multihashmap_remove (car->client_handle->requests,
546                                                        &car->target.hashPubKey,
547                                                        car));
548   GSC_SESSIONS_dequeue_request (car);
549   GNUNET_free (car);
550   return GNUNET_YES;
551 }
552
553
554 /**
555  * A client disconnected, clean up.
556  *
557  * @param cls closure
558  * @param client identification of the client
559  */
560 static void
561 handle_client_disconnect (void *cls,
562                           struct GNUNET_SERVER_Client *client)
563 {
564   struct GSC_Client *c;
565
566   if (client == NULL)
567     return;
568 #if DEBUG_CORE
569   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
570               "Client %p has disconnected from core service.\n", client);
571 #endif
572   c = find_client (client);
573   if (c == NULL)
574     return; /* client never sent INIT */
575   GNUNET_CONTAINER_DLL_remove (client_head,
576                                client_tail,
577                                c);
578   if (c->requests != NULL)
579   {
580     GNUNET_CONTAINER_multihashmap_iterate (c->requests,
581                                            &destroy_active_client_request,
582                                            NULL);
583     GNUNET_CONTAINER_multihashmap_destroy (c->requests);
584   }
585 #if DEBUG_CONNECTS
586   GNUNET_CONTAINER_multihashmap_destroy (c->connectmap);
587 #endif
588   GSC_TYPEMAP_remove (c->types, c->tcnt);
589   GNUNET_free (c);
590 }
591
592
593 /**
594  * Tell a client that we are ready to receive the message.
595  *
596  * @param car request that is now ready; the responsibility
597  *        for the handle remains shared between CLIENTS
598  *        and SESSIONS after this call.
599  */
600 void
601 GSC_CLIENTS_solicit_request (struct GSC_ClientActiveRequest *car)
602 {
603   struct GSC_Client *c;
604   struct SendMessageReady smr;
605
606   c = car->client_handle;
607   smr.header.size = htons (sizeof (struct SendMessageReady));
608   smr.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SEND_READY);
609   smr.size = htons (car->msize);
610   smr.smr_id = car->smr_id;
611   smr.peer = car->target;
612 #if DEBUG_CONNECTS
613   GNUNET_assert (GNUNET_YES ==
614                  GNUNET_CONTAINER_multihashmap_contains (c->connectmap,
615                                                          &car->target.hashPubKey));
616 #endif
617   send_to_client (c, &smr.header, GNUNET_NO);
618 }
619
620
621 /**
622  * Tell a client that we will never be ready to receive the
623  * given message in time (disconnect or timeout).
624  *
625  * @param car request that now permanently failed; the
626  *        responsibility for the handle is now returned
627  *        to CLIENTS (SESSIONS is done with it).
628  */
629 void
630 GSC_CLIENTS_reject_request (struct GSC_ClientActiveRequest *car)
631 {
632 #if DEBUG_CONNECTS
633   GNUNET_assert (GNUNET_YES ==
634                  GNUNET_CONTAINER_multihashmap_contains (car->client_handle->connectmap,
635                                                          &car->target.hashPubKey));
636 #endif
637   GNUNET_assert (GNUNET_YES ==
638                  GNUNET_CONTAINER_multihashmap_remove (car->client_handle->requests,
639                                                        &car->target.hashPubKey,
640                                                        car));
641   GNUNET_free (car);
642 }
643
644
645 /**
646  * Notify a particular client about a change to existing connection to
647  * one of our neighbours (check if the client is interested).  Called
648  * from 'GSC_SESSIONS_notify_client_about_sessions'.
649  *
650  * @param client client to notify
651  * @param neighbour identity of the neighbour that changed status
652  * @param atsi performance information about neighbour
653  * @param atsi_count number of entries in 'ats' array
654  * @param tmap_old previous type map for the neighbour, NULL for disconnect
655  * @param tmap_new updated type map for the neighbour, NULL for disconnect
656  * @param is_new GNUNET_YES if this is a completely new neighbour
657  */
658 void
659 GSC_CLIENTS_notify_client_about_neighbour (struct GSC_Client *client,
660                                            const struct GNUNET_PeerIdentity *neighbour,
661                                            const struct GNUNET_ATS_Information *atsi,
662                                            unsigned int atsi_count,
663                                            const struct GSC_TypeMap *tmap_old,
664                                            const struct GSC_TypeMap *tmap_new)
665 {
666   struct ConnectNotifyMessage *cnm;
667   size_t size;
668   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
669   struct GNUNET_ATS_Information *a;
670   struct DisconnectNotifyMessage dcm;
671   int old_match;
672   int new_match;
673
674   old_match = GSC_TYPEMAP_test_match (tmap_old, client->types, client->tcnt);
675   new_match = GSC_TYPEMAP_test_match (tmap_new, client->types, client->tcnt);
676   if (client->tcnt == 0)
677   {
678     /* empty list matches ALL (if not NULL) */
679     if (tmap_new != NULL)
680       new_match = GNUNET_YES; 
681     if (tmap_old != NULL)
682       old_match = GNUNET_YES;
683   }
684   if (old_match == new_match)
685     return; /* no change */
686   if (old_match == GNUNET_NO)
687   {
688     /* send connect */  
689 #if DEBUG_CONNECTS
690     GNUNET_assert (GNUNET_NO ==
691                    GNUNET_CONTAINER_multihashmap_contains (client->connectmap,
692                                                            &neighbour->hashPubKey));
693     GNUNET_assert (GNUNET_YES ==
694                    GNUNET_CONTAINER_multihashmap_put (client->connectmap,
695                                                       &neighbour->hashPubKey,
696                                                       NULL,
697                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
698 #endif
699     size =
700       sizeof (struct ConnectNotifyMessage) +
701       (atsi_count) * sizeof (struct GNUNET_ATS_Information);
702     if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
703       {
704         GNUNET_break (0);
705         /* recovery strategy: throw away performance data */
706         atsi_count = 0;
707         size = sizeof (struct ConnectNotifyMessage);
708       }
709     cnm = (struct ConnectNotifyMessage *) buf;
710     cnm->header.size = htons (size);
711     cnm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
712     cnm->ats_count = htonl (atsi_count);
713     a = (struct GNUNET_ATS_Information* ) &cnm[1];
714     memcpy (a, atsi,
715             sizeof (struct GNUNET_ATS_Information) * atsi_count);
716 #if DEBUG_CORE
717     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
718                 "Sending `%s' message to client.\n",
719                 "NOTIFY_CONNECT");
720 #endif
721     cnm->peer = *neighbour;
722     send_to_client (client, &cnm->header, GNUNET_NO);
723   }
724   else
725   {
726     /* send disconnect */
727 #if DEBUG_CONNECTS
728     GNUNET_assert (GNUNET_YES ==
729                    GNUNET_CONTAINER_multihashmap_contains (client->connectmap,
730                                                            &neighbour->hashPubKey));
731     GNUNET_assert (GNUNET_YES ==
732                    GNUNET_CONTAINER_multihashmap_remove (client->connectmap,
733                                                          &neighbour->hashPubKey,
734                                                          NULL));
735 #endif
736     dcm.header.size = htons (sizeof (struct DisconnectNotifyMessage));
737     dcm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT);
738     dcm.reserved = htonl (0);
739     dcm.peer = *neighbour;
740     send_to_client (client, &dcm.header, GNUNET_NO);
741   }
742 }
743
744
745 /**
746  * Notify all clients about a change to existing session.
747  * Called from SESSIONS whenever there is a change in sessions
748  * or types processed by the respective peer.
749  *
750  * @param neighbour identity of the neighbour that changed status
751  * @param atsi performance information about neighbour
752  * @param atsi_count number of entries in 'ats' array
753  * @param tmap_old previous type map for the neighbour, NULL for disconnect
754  * @param tmap_new updated type map for the neighbour, NULL for disconnect
755  */
756 void
757 GSC_CLIENTS_notify_clients_about_neighbour (const struct GNUNET_PeerIdentity *neighbour,
758                                             const struct GNUNET_ATS_Information *atsi,
759                                             unsigned int atsi_count,
760                                             const struct GSC_TypeMap *tmap_old,
761                                             const struct GSC_TypeMap *tmap_new)
762 {
763   struct GSC_Client *c;
764
765   for (c = client_head; c != NULL; c = c->next)
766     GSC_CLIENTS_notify_client_about_neighbour (c, neighbour, atsi,
767                                                atsi_count, 
768                                                tmap_old, tmap_new);
769 }
770
771
772 /**
773  * Deliver P2P message to interested clients.  Caller must have checked
774  * that the sending peer actually lists the given message type as one 
775  * of its types.
776  *
777  * @param sender peer who sent us the message 
778  * @param atsi performance information about neighbour
779  * @param atsi_count number of entries in 'ats' array
780  * @param msg the message
781  * @param msize number of bytes to transmit
782  * @param options options for checking which clients should
783  *        receive the message
784  */
785 void
786 GSC_CLIENTS_deliver_message (const struct GNUNET_PeerIdentity *sender,
787                              const struct GNUNET_ATS_Information *atsi,
788                              unsigned int atsi_count,
789                              const struct GNUNET_MessageHeader *msg,
790                              uint16_t msize,
791                              int options)
792 {
793   size_t size = msize + sizeof (struct NotifyTrafficMessage) +
794       atsi_count * sizeof (struct GNUNET_ATS_Information);
795   char buf[size];
796   struct NotifyTrafficMessage *ntm;
797   struct GNUNET_ATS_Information *a;
798
799   if (0 == options)
800   {
801     GNUNET_snprintf (buf, sizeof (buf),
802                      gettext_noop ("# bytes of messages of type %u received"),
803                      (unsigned int) ntohs (msg->type));
804     GNUNET_STATISTICS_update (GSC_stats, buf, msize, GNUNET_NO);
805   }
806   if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
807   {
808     GNUNET_break (0);
809     /* recovery strategy: throw performance data away... */
810     atsi_count = 0;
811     size = msize + sizeof (struct NotifyTrafficMessage);
812   }
813 #if DEBUG_CORE
814   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
815               "Core service passes message from `%4s' of type %u to client.\n",
816               GNUNET_i2s (sender),
817               (unsigned int) ntohs (msg->type));
818 #endif
819   GSC_SESSIONS_add_to_typemap (sender, ntohs (msg->type));
820   ntm = (struct NotifyTrafficMessage *) buf;
821   ntm->header.size = htons (size);
822   ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND);
823   ntm->ats_count = htonl (atsi_count);
824   ntm->peer = *sender;
825   a = &ntm->ats;
826   memcpy (a, atsi,
827           sizeof (struct GNUNET_ATS_Information) * atsi_count);
828   a[atsi_count].type = htonl (GNUNET_ATS_ARRAY_TERMINATOR);
829   a[atsi_count].value = htonl (0);
830   memcpy (&a[atsi_count + 1], msg, msize);
831   send_to_all_clients (sender,
832                        &ntm->header, GNUNET_YES, 
833                        options, ntohs (msg->type));
834 }
835
836
837 /**
838  * Initialize clients subsystem.
839  *
840  * @param server handle to server clients connect to
841  */
842 void
843 GSC_CLIENTS_init (struct GNUNET_SERVER_Handle *server)
844 {
845   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
846     {&handle_client_init, NULL,
847      GNUNET_MESSAGE_TYPE_CORE_INIT, 0},
848     {&GSC_SESSIONS_handle_client_iterate_peers, NULL,
849      GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS,
850      sizeof (struct GNUNET_MessageHeader)},
851     {&GSC_SESSIONS_handle_client_have_peer, NULL,
852      GNUNET_MESSAGE_TYPE_CORE_PEER_CONNECTED,
853      sizeof (struct GNUNET_MessageHeader) +
854      sizeof (struct GNUNET_PeerIdentity)},
855     {&handle_client_send_request, NULL,
856      GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST,
857      sizeof (struct SendMessageRequest)},
858     {&handle_client_send, NULL,
859      GNUNET_MESSAGE_TYPE_CORE_SEND, 0},
860     {NULL, NULL, 0, 0}
861   };
862
863   /* setup notification */
864   client_mst = GNUNET_SERVER_mst_create (&client_tokenizer_callback, NULL);
865   notifier =
866       GNUNET_SERVER_notification_context_create (server, MAX_NOTIFY_QUEUE);
867   GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
868   GNUNET_SERVER_add_handlers (server, handlers);
869 }
870
871
872 /**
873  * Shutdown clients subsystem.
874  */
875 void
876 GSC_CLIENTS_done ()
877 {
878   struct GSC_Client *c;
879
880   while (NULL != (c = client_head))  
881     handle_client_disconnect (NULL, c->client_handle);
882   if (NULL != notifier)
883   {
884     GNUNET_SERVER_notification_context_destroy (notifier);
885     notifier = NULL;
886   }
887   GNUNET_SERVER_mst_destroy (client_mst);
888   client_mst = NULL;
889 }
890
891 /* end of gnunet-service-core_clients.c */