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