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