Check return value of GNUNET_STATISTICS_get
[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 @e 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_MultiPeerMap *requests;
77
78   /**
79    * Map containing all peers that this client knows we're connected to.
80    */
81   struct GNUNET_CONTAINER_MultiPeerMap *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 @e 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 @a 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   int tm;
227
228   for (c = client_head; NULL != c; c = c->next)
229   {
230     tm = type_match (type, c);
231     if (!  ( (0 != (c->options & options)) ||
232              ( (0 != (options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) &&
233                (GNUNET_YES == tm) ) ) )
234       continue;  /* neither options nor type match permit the message */
235     if ( (0 != (options & GNUNET_CORE_OPTION_SEND_HDR_INBOUND)) &&
236          ( (0 != (c->options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) ||
237            (GNUNET_YES == tm) ) )
238       continue;
239     if ( (0 != (options & GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND)) &&
240          (0 != (c->options & GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND)) )
241       continue;
242     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
243                 "Sending %u message with %u bytes to client interested in messages of type %u.\n",
244                 options,
245                 ntohs (msg->size),
246                 (unsigned int) type);
247     GNUNET_assert ( (0 == (c->options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) ||
248                     (GNUNET_YES != tm) ||
249                     (GNUNET_YES ==
250                      GNUNET_CONTAINER_multipeermap_contains (c->connectmap,
251                                                              partner)) );
252     send_to_client (c, msg, can_drop);
253   }
254 }
255
256
257 /**
258  * Handle #GNUNET_MESSAGE_TYPE_CORE_INIT request.
259  *
260  * @param cls unused
261  * @param client new client that sent #GNUNET_MESSAGE_TYPE_CORE_INIT
262  * @param message the `struct InitMessage` (presumably)
263  */
264 static void
265 handle_client_init (void *cls,
266                     struct GNUNET_SERVER_Client *client,
267                     const struct GNUNET_MessageHeader *message)
268 {
269   const struct InitMessage *im;
270   struct InitReplyMessage irm;
271   struct GSC_Client *c;
272   uint16_t msize;
273   const uint16_t *types;
274   uint16_t *wtypes;
275   unsigned int i;
276
277   /* check that we don't have an entry already */
278   c = find_client (client);
279   if (NULL != c)
280   {
281     GNUNET_break (0);
282     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
283     return;
284   }
285   msize = ntohs (message->size);
286   if (msize < sizeof (struct InitMessage))
287   {
288     GNUNET_break (0);
289     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
290     return;
291   }
292   GNUNET_SERVER_notification_context_add (notifier, client);
293   im = (const struct InitMessage *) message;
294   types = (const uint16_t *) &im[1];
295   msize -= sizeof (struct InitMessage);
296   c = GNUNET_malloc (sizeof (struct GSC_Client) + msize);
297   c->client_handle = client;
298   c->tcnt = msize / sizeof (uint16_t);
299   c->options = ntohl (im->options);
300   all_client_options |= c->options;
301   c->types = (const uint16_t *) &c[1];
302   c->connectmap = GNUNET_CONTAINER_multipeermap_create (16, GNUNET_NO);
303   GNUNET_assert (GNUNET_YES ==
304                  GNUNET_CONTAINER_multipeermap_put (c->connectmap,
305                                                     &GSC_my_identity,
306                                                     NULL,
307                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
308   wtypes = (uint16_t *) & c[1];
309   for (i = 0; i < c->tcnt; i++)
310     wtypes[i] = ntohs (types[i]);
311   GSC_TYPEMAP_add (wtypes, c->tcnt);
312   GNUNET_CONTAINER_DLL_insert (client_head, client_tail, c);
313   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
314               "Client connecting to core service is interested in %u message types\n",
315               (unsigned int) c->tcnt);
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 #GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST message.
329  *
330  * @param cls unused
331  * @param client new client that sent a #GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST
332  * @param message the `struct SendMessageRequest` (presumably)
333  */
334 static void
335 handle_client_send_request (void *cls,
336                             struct GNUNET_SERVER_Client *client,
337                             const struct GNUNET_MessageHeader *message)
338 {
339   const struct SendMessageRequest *req;
340   struct GSC_Client *c;
341   struct GSC_ClientActiveRequest *car;
342   int is_loopback;
343
344   req = (const struct SendMessageRequest *) message;
345   c = find_client (client);
346   if (NULL == c)
347   {
348     /* client did not send INIT first! */
349     GNUNET_break (0);
350     GNUNET_SERVER_receive_done (client,
351                                 GNUNET_SYSERR);
352     return;
353   }
354   if (NULL == c->requests)
355     c->requests = GNUNET_CONTAINER_multipeermap_create (16,
356                                                         GNUNET_NO);
357   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
358               "Client asked for transmission to `%s'\n",
359               GNUNET_i2s (&req->peer));
360   is_loopback =
361       (0 ==
362        memcmp (&req->peer,
363                &GSC_my_identity,
364                sizeof (struct GNUNET_PeerIdentity)));
365   if ((! is_loopback) &&
366       (GNUNET_YES !=
367        GNUNET_CONTAINER_multipeermap_contains (c->connectmap,
368                                                &req->peer)))
369   {
370     /* neighbour must have disconnected since request was issued,
371      * ignore (client will realize it once it processes the
372      * disconnect notification) */
373     GNUNET_STATISTICS_update (GSC_stats,
374                               gettext_noop
375                               ("# send requests dropped (disconnected)"), 1,
376                               GNUNET_NO);
377     GNUNET_SERVER_receive_done (client,
378                                 GNUNET_OK);
379     return;
380   }
381
382   car = GNUNET_CONTAINER_multipeermap_get (c->requests,
383                                            &req->peer);
384   if (NULL == car)
385   {
386     /* create new entry */
387     car = GNUNET_new (struct GSC_ClientActiveRequest);
388     GNUNET_assert (GNUNET_OK ==
389                    GNUNET_CONTAINER_multipeermap_put (c->requests,
390                                                       &req->peer,
391                                                       car,
392                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
393     car->client_handle = c;
394   }
395   else
396   {
397     /* dequeue and recycle memory from pending request, there can only
398        be at most one per client and peer */
399     GSC_SESSIONS_dequeue_request (car);
400   }
401   car->target = req->peer;
402   car->deadline = GNUNET_TIME_absolute_ntoh (req->deadline);
403   car->priority = (enum GNUNET_CORE_Priority) ntohl (req->priority);
404   car->msize = ntohs (req->size);
405   car->smr_id = req->smr_id;
406   car->was_solicited = GNUNET_NO;
407   if (is_loopback)
408   {
409     /* loopback, satisfy immediately */
410     GSC_CLIENTS_solicit_request (car);
411     GNUNET_SERVER_receive_done (client, GNUNET_OK);
412     return;
413   }
414   GSC_SESSIONS_queue_request (car);
415   GNUNET_SERVER_receive_done (client, GNUNET_OK);
416 }
417
418
419 /**
420  * Closure for the #client_tokenizer_callback().
421  */
422 struct TokenizerContext
423 {
424
425   /**
426    * Active request handle for the message.
427    */
428   struct GSC_ClientActiveRequest *car;
429
430   /**
431    * How important is this message.
432    */
433   enum GNUNET_CORE_Priority priority;
434
435   /**
436    * Is corking allowed (set only once we have the real message).
437    */
438   int cork;
439
440 };
441
442
443 /**
444  * Handle CORE_SEND request.
445  *
446  * @param cls unused
447  * @param client the client issuing the request
448  * @param message the `struct SendMessage`
449  */
450 static void
451 handle_client_send (void *cls,
452                     struct GNUNET_SERVER_Client *client,
453                     const struct GNUNET_MessageHeader *message)
454 {
455   const struct SendMessage *sm;
456   struct GSC_Client *c;
457   struct TokenizerContext tc;
458   uint16_t msize;
459
460   msize = ntohs (message->size);
461   if (msize <
462       sizeof (struct SendMessage) + sizeof (struct GNUNET_MessageHeader))
463   {
464     GNUNET_break (0);
465     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
466     return;
467   }
468   sm = (const struct SendMessage *) message;
469   msize -= sizeof (struct SendMessage);
470   GNUNET_break (0 == ntohl (sm->reserved));
471   c = find_client (client);
472   if (NULL == c)
473   {
474     /* client did not send INIT first! */
475     GNUNET_break (0);
476     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
477     return;
478   }
479   tc.car =
480       GNUNET_CONTAINER_multipeermap_get (c->requests, &sm->peer);
481   if (NULL == tc.car)
482   {
483     /* Must have been that we first approved the request, then got disconnected
484      * (which triggered removal of the 'car') and now the client gives us a message
485      * just *before* the client learns about the disconnect.  Theoretically, we
486      * might also now be *again* connected.  So this can happen (but should be
487      * rare).  If it does happen, the message is discarded. */
488     GNUNET_STATISTICS_update (GSC_stats,
489                               gettext_noop
490                               ("# messages discarded (session disconnected)"),
491                               1, GNUNET_NO);
492     GNUNET_SERVER_receive_done (client, GNUNET_OK);
493     return;
494   }
495   GNUNET_assert (GNUNET_YES ==
496                  GNUNET_CONTAINER_multipeermap_remove (c->requests,
497                                                        &sm->peer,
498                                                        tc.car));
499   tc.cork = ntohl (sm->cork);
500   tc.priority = (enum GNUNET_CORE_Priority) ntohl (sm->priority);
501   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
502               "Client asked for transmission of %u bytes to `%s' %s\n",
503               msize,
504               GNUNET_i2s (&sm->peer), tc.cork ? "now" : "");
505   GNUNET_SERVER_mst_receive (client_mst, &tc,
506                              (const char *) &sm[1], msize,
507                              GNUNET_YES, GNUNET_NO);
508   if (0 !=
509       memcmp (&tc.car->target, &GSC_my_identity,
510               sizeof (struct GNUNET_PeerIdentity)))
511     GSC_SESSIONS_dequeue_request (tc.car);
512   GNUNET_free (tc.car);
513   GNUNET_SERVER_receive_done (client, GNUNET_OK);
514 }
515
516
517 /**
518  * Functions with this signature are called whenever a complete
519  * message is received by the tokenizer.  Used by the 'client_mst' for
520  * dispatching messages from clients to either the SESSION subsystem
521  * or other CLIENT (for loopback).
522  *
523  * @param cls closure
524  * @param client reservation request (`struct GSC_ClientActiveRequest`)
525  * @param message the actual message
526  */
527 static int
528 client_tokenizer_callback (void *cls, void *client,
529                            const struct GNUNET_MessageHeader *message)
530 {
531   struct TokenizerContext *tc = client;
532   struct GSC_ClientActiveRequest *car = tc->car;
533   char buf[92];
534
535   GNUNET_snprintf (buf, sizeof (buf),
536                    gettext_noop ("# bytes of messages of type %u received"),
537                    (unsigned int) ntohs (message->type));
538   GNUNET_STATISTICS_update (GSC_stats, buf, ntohs (message->size), GNUNET_NO);
539   if (0 ==
540       memcmp (&car->target, &GSC_my_identity,
541               sizeof (struct GNUNET_PeerIdentity)))
542   {
543     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
544                 "Delivering message of type %u to myself\n",
545                 ntohs (message->type));
546     GSC_CLIENTS_deliver_message (&GSC_my_identity, message,
547                                  ntohs (message->size),
548                                  GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND);
549     GSC_CLIENTS_deliver_message (&GSC_my_identity, message,
550                                  sizeof (struct GNUNET_MessageHeader),
551                                  GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND);
552     GSC_CLIENTS_deliver_message (&GSC_my_identity, message,
553                                  ntohs (message->size),
554                                  GNUNET_CORE_OPTION_SEND_FULL_INBOUND);
555     GSC_CLIENTS_deliver_message (&GSC_my_identity, message,
556                                  sizeof (struct GNUNET_MessageHeader),
557                                  GNUNET_CORE_OPTION_SEND_HDR_INBOUND);
558   }
559   else
560   {
561     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
562                 "Delivering message of type %u to %s\n",
563                 ntohs (message->type),
564                 GNUNET_i2s (&car->target));
565     GSC_CLIENTS_deliver_message (&car->target, message,
566                                  ntohs (message->size),
567                                  GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND);
568     GSC_CLIENTS_deliver_message (&car->target, message,
569                                  sizeof (struct GNUNET_MessageHeader),
570                                  GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND);
571     GSC_SESSIONS_transmit (car,
572                            message,
573                            tc->cork,
574                            tc->priority);
575   }
576   return GNUNET_OK;
577 }
578
579
580 /**
581  * Free client request records.
582  *
583  * @param cls NULL
584  * @param key identity of peer for which this is an active request
585  * @param value the `struct GSC_ClientActiveRequest` to free
586  * @return #GNUNET_YES (continue iteration)
587  */
588 static int
589 destroy_active_client_request (void *cls,
590                                const struct GNUNET_PeerIdentity *key,
591                                void *value)
592 {
593   struct GSC_ClientActiveRequest *car = value;
594
595   GNUNET_assert (GNUNET_YES ==
596                  GNUNET_CONTAINER_multipeermap_remove (car->
597                                                        client_handle->requests,
598                                                        &car->target,
599                                                        car));
600   GSC_SESSIONS_dequeue_request (car);
601   GNUNET_free (car);
602   return GNUNET_YES;
603 }
604
605
606 /**
607  * A client disconnected, clean up.
608  *
609  * @param cls closure
610  * @param client identification of the client
611  */
612 static void
613 handle_client_disconnect (void *cls,
614                           struct GNUNET_SERVER_Client *client)
615 {
616   struct GSC_Client *c;
617
618   if (NULL == client)
619     return;
620   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
621               "Client %p has disconnected from core service.\n", client);
622   c = find_client (client);
623   if (c == NULL)
624     return;                     /* client never sent INIT */
625   GNUNET_CONTAINER_DLL_remove (client_head, client_tail, c);
626   if (c->requests != NULL)
627   {
628     GNUNET_CONTAINER_multipeermap_iterate (c->requests,
629                                            &destroy_active_client_request,
630                                            NULL);
631     GNUNET_CONTAINER_multipeermap_destroy (c->requests);
632   }
633   GNUNET_CONTAINER_multipeermap_destroy (c->connectmap);
634   c->connectmap = NULL;
635   GSC_TYPEMAP_remove (c->types, c->tcnt);
636   GNUNET_free (c);
637
638   /* recalculate 'all_client_options' */
639   all_client_options = 0;
640   for (c = client_head; NULL != c ; c = c->next)
641     all_client_options |= c->options;
642 }
643
644
645 /**
646  * Tell a client that we are ready to receive the message.
647  *
648  * @param car request that is now ready; the responsibility
649  *        for the handle remains shared between CLIENTS
650  *        and SESSIONS after this call.
651  */
652 void
653 GSC_CLIENTS_solicit_request (struct GSC_ClientActiveRequest *car)
654 {
655   struct GSC_Client *c;
656   struct SendMessageReady smr;
657
658   c = car->client_handle;
659   if (GNUNET_YES !=
660       GNUNET_CONTAINER_multipeermap_contains (c->connectmap,
661                                               &car->target))
662   {
663     /* connection has gone down since, drop request */
664     GNUNET_assert (0 !=
665                    memcmp (&car->target, &GSC_my_identity,
666                            sizeof (struct GNUNET_PeerIdentity)));
667     GSC_SESSIONS_dequeue_request (car);
668     GSC_CLIENTS_reject_request (car);
669     return;
670   }
671   smr.header.size = htons (sizeof (struct SendMessageReady));
672   smr.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SEND_READY);
673   smr.size = htons (car->msize);
674   smr.smr_id = car->smr_id;
675   smr.peer = car->target;
676   send_to_client (c, &smr.header, GNUNET_NO);
677 }
678
679
680 /**
681  * Tell a client that we will never be ready to receive the
682  * given message in time (disconnect or timeout).
683  *
684  * @param car request that now permanently failed; the
685  *        responsibility for the handle is now returned
686  *        to CLIENTS (SESSIONS is done with it).
687  */
688 void
689 GSC_CLIENTS_reject_request (struct GSC_ClientActiveRequest *car)
690 {
691   GNUNET_assert (GNUNET_YES ==
692                  GNUNET_CONTAINER_multipeermap_remove (car->
693                                                        client_handle->requests,
694                                                        &car->target,
695                                                        car));
696   GNUNET_free (car);
697 }
698
699
700 /**
701  * Notify a particular client about a change to existing connection to
702  * one of our neighbours (check if the client is interested).  Called
703  * from 'GSC_SESSIONS_notify_client_about_sessions'.
704  *
705  * @param client client to notify
706  * @param neighbour identity of the neighbour that changed status
707  * @param tmap_old previous type map for the neighbour, NULL for connect
708  * @param tmap_new updated type map for the neighbour, NULL for disconnect
709  */
710 void
711 GSC_CLIENTS_notify_client_about_neighbour (struct GSC_Client *client,
712                                            const struct GNUNET_PeerIdentity *neighbour,
713                                            const struct GSC_TypeMap *tmap_old,
714                                            const struct GSC_TypeMap *tmap_new)
715 {
716   struct ConnectNotifyMessage *cnm;
717   size_t size;
718   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
719   struct DisconnectNotifyMessage dcm;
720   int old_match;
721   int new_match;
722
723   old_match = GSC_TYPEMAP_test_match (tmap_old, client->types, client->tcnt);
724   new_match = GSC_TYPEMAP_test_match (tmap_new, client->types, client->tcnt);
725   if (old_match == new_match)
726   {
727     GNUNET_assert (old_match ==
728                    GNUNET_CONTAINER_multipeermap_contains (client->connectmap,
729                                                            neighbour));
730     return;                     /* no change */
731   }
732   if (old_match == GNUNET_NO)
733   {
734     /* send connect */
735     GNUNET_assert (GNUNET_NO ==
736                    GNUNET_CONTAINER_multipeermap_contains (client->connectmap,
737                                                            neighbour));
738     GNUNET_assert (GNUNET_YES ==
739                    GNUNET_CONTAINER_multipeermap_put (client->connectmap,
740                                                       neighbour,
741                                                       NULL,
742                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
743     size = sizeof (struct ConnectNotifyMessage);
744     cnm = (struct ConnectNotifyMessage *) buf;
745     cnm->header.size = htons (size);
746     cnm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
747     cnm->reserved = htonl (0);
748     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
749                 "Sending `%s' message to client.\n",
750                 "NOTIFY_CONNECT");
751     cnm->peer = *neighbour;
752     send_to_client (client, &cnm->header, GNUNET_NO);
753   }
754   else
755   {
756     /* send disconnect */
757     GNUNET_assert (GNUNET_YES ==
758                    GNUNET_CONTAINER_multipeermap_contains (client->connectmap,
759                                                            neighbour));
760     GNUNET_assert (GNUNET_YES ==
761                    GNUNET_CONTAINER_multipeermap_remove (client->connectmap,
762                                                          neighbour,
763                                                          NULL));
764     dcm.header.size = htons (sizeof (struct DisconnectNotifyMessage));
765     dcm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT);
766     dcm.reserved = htonl (0);
767     dcm.peer = *neighbour;
768     send_to_client (client, &dcm.header, GNUNET_NO);
769   }
770 }
771
772
773 /**
774  * Notify all clients about a change to existing session.
775  * Called from SESSIONS whenever there is a change in sessions
776  * or types processed by the respective peer.
777  *
778  * @param neighbour identity of the neighbour that changed status
779  * @param tmap_old previous type map for the neighbour, NULL for connect
780  * @param tmap_new updated type map for the neighbour, NULL for disconnect
781  */
782 void
783 GSC_CLIENTS_notify_clients_about_neighbour (const struct GNUNET_PeerIdentity *neighbour,
784                                             const struct GSC_TypeMap *tmap_old,
785                                             const struct GSC_TypeMap *tmap_new)
786 {
787   struct GSC_Client *c;
788
789   for (c = client_head; NULL != c; c = c->next)
790     GSC_CLIENTS_notify_client_about_neighbour (c, neighbour,
791                                                tmap_old, tmap_new);
792 }
793
794
795 /**
796  * Deliver P2P message to interested clients.  Caller must have checked
797  * that the sending peer actually lists the given message type as one
798  * of its types.
799  *
800  * @param sender peer who sent us the message
801  * @param msg the message
802  * @param msize number of bytes to transmit
803  * @param options options for checking which clients should
804  *        receive the message
805  */
806 void
807 GSC_CLIENTS_deliver_message (const struct GNUNET_PeerIdentity *sender,
808                              const struct GNUNET_MessageHeader *msg,
809                              uint16_t msize,
810                              uint32_t options)
811 {
812   size_t size = msize + sizeof (struct NotifyTrafficMessage);
813   char buf[size] GNUNET_ALIGN;
814   struct NotifyTrafficMessage *ntm;
815
816   if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
817   {
818     GNUNET_break (0);
819     /* recovery strategy: throw performance data away... */
820     size = msize + sizeof (struct NotifyTrafficMessage);
821   }
822   if (! ( (0 != (all_client_options & options)) ||
823           (0 != (options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) ))
824     return; /* no client cares about this message notification */
825   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
826               "Core service passes message from `%4s' of type %u to client.\n",
827               GNUNET_i2s (sender),
828               (unsigned int) ntohs (msg->type));
829   GSC_SESSIONS_add_to_typemap (sender, ntohs (msg->type));
830   ntm = (struct NotifyTrafficMessage *) buf;
831   ntm->header.size = htons (size);
832   if (0 != (options & (GNUNET_CORE_OPTION_SEND_FULL_INBOUND | GNUNET_CORE_OPTION_SEND_HDR_INBOUND)))
833     ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND);
834   else
835     ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND);
836   ntm->peer = *sender;
837   memcpy (&ntm[1],
838           msg,
839           msize);
840   send_to_all_clients (sender,
841                        &ntm->header,
842                        GNUNET_YES,
843                        options,
844                        ntohs (msg->type));
845 }
846
847
848 /**
849  * Initialize clients subsystem.
850  *
851  * @param server handle to server clients connect to
852  */
853 void
854 GSC_CLIENTS_init (struct GNUNET_SERVER_Handle *server)
855 {
856   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
857     {&handle_client_init, NULL,
858      GNUNET_MESSAGE_TYPE_CORE_INIT, 0},
859     {&GSC_KX_handle_client_monitor_peers, NULL,
860      GNUNET_MESSAGE_TYPE_CORE_MONITOR_PEERS,
861      sizeof (struct GNUNET_MessageHeader)},
862     {&handle_client_send_request, NULL,
863      GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST,
864      sizeof (struct SendMessageRequest)},
865     {&handle_client_send, NULL,
866      GNUNET_MESSAGE_TYPE_CORE_SEND, 0},
867     {NULL, NULL, 0, 0}
868   };
869
870   /* setup notification */
871   client_mst = GNUNET_SERVER_mst_create (&client_tokenizer_callback, NULL);
872   notifier =
873       GNUNET_SERVER_notification_context_create (server, MAX_NOTIFY_QUEUE);
874   GNUNET_SERVER_disconnect_notify (server,
875                                    &handle_client_disconnect, NULL);
876   GNUNET_SERVER_add_handlers (server, handlers);
877 }
878
879
880 /**
881  * Shutdown clients subsystem.
882  */
883 void
884 GSC_CLIENTS_done ()
885 {
886   struct GSC_Client *c;
887
888   while (NULL != (c = client_head))
889     handle_client_disconnect (NULL, c->client_handle);
890   if (NULL != notifier)
891   {
892     GNUNET_SERVER_notification_context_destroy (notifier);
893     notifier = NULL;
894   }
895   if (NULL != client_mst)
896   {
897     GNUNET_SERVER_mst_destroy (client_mst);
898     client_mst = NULL;
899   }
900 }
901
902 /* end of gnunet-service-core_clients.c */