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