reduce loop counters to more practical levels
[oweals/gnunet.git] / src / core / gnunet-service-core.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2010, 2011, 2016 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file core/gnunet-service-core.c
23  * @brief high-level P2P messaging
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include <gcrypt.h>
28 #include "gnunet_util_lib.h"
29 #include "gnunet-service-core.h"
30 #include "gnunet-service-core_kx.h"
31 #include "gnunet-service-core_sessions.h"
32 #include "gnunet-service-core_typemap.h"
33
34 /**
35  * How many messages do we queue up at most for any client? This can
36  * cause messages to be dropped if clients do not process them fast
37  * enough!  Note that this is a soft limit; we try
38  * to keep a few larger messages above the limit.
39  */
40 #define SOFT_MAX_QUEUE 128
41
42 /**
43  * How many messages do we queue up at most for any client? This can
44  * cause messages to be dropped if clients do not process them fast
45  * enough!  Note that this is the hard limit.
46  */
47 #define HARD_MAX_QUEUE 256
48
49
50 /**
51  * Data structure for each client connected to the CORE service.
52  */
53 struct GSC_Client
54 {
55   /**
56    * Clients are kept in a linked list.
57    */
58   struct GSC_Client *next;
59
60   /**
61    * Clients are kept in a linked list.
62    */
63   struct GSC_Client *prev;
64
65   /**
66    * Handle for the client with the server API.
67    */
68   struct GNUNET_SERVICE_Client *client;
69
70   /**
71    * Message queue to talk to @e client.
72    */
73   struct GNUNET_MQ_Handle *mq;
74
75   /**
76    * Array of the types of messages this peer cares
77    * about (with @e tcnt entries).  Allocated as part
78    * of this client struct, do not free!
79    */
80   uint16_t *types;
81
82   /**
83    * Map of peer identities to active transmission requests of this
84    * client to the peer (of type `struct GSC_ClientActiveRequest`).
85    */
86   struct GNUNET_CONTAINER_MultiPeerMap *requests;
87
88   /**
89    * Map containing all peers that this client knows we're connected to.
90    */
91   struct GNUNET_CONTAINER_MultiPeerMap *connectmap;
92
93   /**
94    * Options for messages this client cares about,
95    * see GNUNET_CORE_OPTION_ values.
96    */
97   uint32_t options;
98
99   /**
100    * Have we gotten the #GNUNET_MESSAGE_TYPE_CORE_INIT message
101    * from this client already?
102    */
103   int got_init;
104
105   /**
106    * Number of types of incoming messages this client
107    * specifically cares about.  Size of the @e types array.
108    */
109   unsigned int tcnt;
110
111 };
112
113
114 /**
115  * Our identity.
116  */
117 struct GNUNET_PeerIdentity GSC_my_identity;
118
119 /**
120  * Our configuration.
121  */
122 const struct GNUNET_CONFIGURATION_Handle *GSC_cfg;
123
124 /**
125  * For creating statistics.
126  */
127 struct GNUNET_STATISTICS_Handle *GSC_stats;
128
129 /**
130  * Big "or" of all client options.
131  */
132 static uint32_t all_client_options;
133
134 /**
135  * Head of linked list of our clients.
136  */
137 static struct GSC_Client *client_head;
138
139 /**
140  * Tail of linked list of our clients.
141  */
142 static struct GSC_Client *client_tail;
143
144
145 /**
146  * Test if the client is interested in messages of the given type.
147  *
148  * @param type message type
149  * @param c client to test
150  * @return #GNUNET_YES if @a c is interested, #GNUNET_NO if not.
151  */
152 static int
153 type_match (uint16_t type,
154             struct GSC_Client *c)
155 {
156   if ( (0 == c->tcnt) &&
157        (0 != c->options) )
158     return GNUNET_YES;          /* peer without handlers and inbound/outbond
159                                    callbacks matches ALL */
160   if (NULL == c->types)
161     return GNUNET_NO;
162   for (unsigned int i = 0; i < c->tcnt; i++)
163     if (type == c->types[i])
164       return GNUNET_YES;
165   return GNUNET_NO;
166 }
167
168
169 /**
170  * Check #GNUNET_MESSAGE_TYPE_CORE_INIT request.
171  *
172  * @param cls client that sent #GNUNET_MESSAGE_TYPE_CORE_INIT
173  * @param im the `struct InitMessage`
174  * @return #GNUNET_OK if @a im is well-formed
175  */
176 static int
177 check_client_init (void *cls,
178                    const struct InitMessage *im)
179 {
180   return GNUNET_OK;
181 }
182
183
184 /**
185  * Handle #GNUNET_MESSAGE_TYPE_CORE_INIT request.
186  *
187  * @param cls client that sent #GNUNET_MESSAGE_TYPE_CORE_INIT
188  * @param im the `struct InitMessage`
189  */
190 static void
191 handle_client_init (void *cls,
192                     const struct InitMessage *im)
193 {
194   struct GSC_Client *c = cls;
195   struct GNUNET_MQ_Envelope *env;
196   struct InitReplyMessage *irm;
197   uint16_t msize;
198   const uint16_t *types;
199
200   /* check that we don't have an entry already */
201   msize = ntohs (im->header.size) - sizeof (struct InitMessage);
202   types = (const uint16_t *) &im[1];
203   c->tcnt = msize / sizeof (uint16_t);
204   c->options = ntohl (im->options);
205   c->got_init = GNUNET_YES;
206   all_client_options |= c->options;
207   c->types = GNUNET_malloc (msize);
208   GNUNET_assert (GNUNET_YES ==
209                  GNUNET_CONTAINER_multipeermap_put (c->connectmap,
210                                                     &GSC_my_identity,
211                                                     NULL,
212                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
213   for (unsigned int i = 0; i < c->tcnt; i++)
214     c->types[i] = ntohs (types[i]);
215   GSC_TYPEMAP_add (c->types,
216                    c->tcnt);
217   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
218               "Client connecting to core service is interested in %u message types\n",
219               (unsigned int) c->tcnt);
220   /* send init reply message */
221   env = GNUNET_MQ_msg (irm,
222                        GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY);
223   irm->reserved = htonl (0);
224   irm->my_identity = GSC_my_identity;
225   GNUNET_MQ_send (c->mq,
226                   env);
227   GSC_SESSIONS_notify_client_about_sessions (c);
228   GNUNET_SERVICE_client_continue (c->client);
229 }
230
231
232 /**
233  * We will never be ready to transmit the given message in (disconnect
234  * or invalid request).  Frees resources associated with @a car.  We
235  * don't explicitly tell the client, he'll learn with the disconnect
236  * (or violated the protocol).
237  *
238  * @param car request that now permanently failed; the
239  *        responsibility for the handle is now returned
240  *        to CLIENTS (SESSIONS is done with it).
241  * @param drop_client #GNUNET_YES if the client violated the protocol
242  *        and we should thus drop the connection
243  */
244 void
245 GSC_CLIENTS_reject_request (struct GSC_ClientActiveRequest *car,
246                             int drop_client)
247 {
248   GNUNET_assert (GNUNET_YES ==
249                  GNUNET_CONTAINER_multipeermap_remove (car->
250                                                        client_handle->requests,
251                                                        &car->target,
252                                                        car));
253   if (GNUNET_YES == drop_client)
254     GNUNET_SERVICE_client_drop (car->client_handle->client);
255   GNUNET_free (car);
256 }
257
258
259 /**
260  * Tell a client that we are ready to receive the message.
261  *
262  * @param car request that is now ready; the responsibility
263  *        for the handle remains shared between CLIENTS
264  *        and SESSIONS after this call.
265  */
266 void
267 GSC_CLIENTS_solicit_request (struct GSC_ClientActiveRequest *car)
268 {
269   struct GSC_Client *c;
270   struct GNUNET_MQ_Envelope *env;
271   struct SendMessageReady *smr;
272   struct GNUNET_TIME_Relative delay;
273   struct GNUNET_TIME_Relative left;
274
275   c = car->client_handle;
276   if (GNUNET_YES !=
277       GNUNET_CONTAINER_multipeermap_contains (c->connectmap,
278                                               &car->target))
279   {
280     /* connection has gone down since, drop request */
281     GNUNET_assert (0 !=
282                    memcmp (&car->target,
283                            &GSC_my_identity,
284                            sizeof (struct GNUNET_PeerIdentity)));
285     GSC_SESSIONS_dequeue_request (car);
286     GSC_CLIENTS_reject_request (car,
287                                 GNUNET_NO);
288     return;
289   }
290   delay = GNUNET_TIME_absolute_get_duration (car->received_time);
291   left = GNUNET_TIME_absolute_get_duration (car->deadline);
292   if (delay.rel_value_us > GNUNET_CONSTANTS_LATENCY_WARN.rel_value_us)
293     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
294                 "Client waited %s for permission to transmit to `%s'%s (priority %u)\n",
295                 GNUNET_STRINGS_relative_time_to_string (delay,
296                                                         GNUNET_YES),
297                 GNUNET_i2s (&car->target),
298                 (0 == left.rel_value_us)
299                 ? " (past deadline)"
300                 : "",
301                 car->priority);
302   env = GNUNET_MQ_msg (smr,
303                        GNUNET_MESSAGE_TYPE_CORE_SEND_READY);
304   smr->size = htons (car->msize);
305   smr->smr_id = car->smr_id;
306   smr->peer = car->target;
307   GNUNET_MQ_send (c->mq,
308                   env);
309 }
310
311
312 /**
313  * Handle #GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST message.
314  *
315  * @param cls client that sent a #GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST
316  * @param req the `struct SendMessageRequest`
317  */
318 static void
319 handle_client_send_request (void *cls,
320                             const struct SendMessageRequest *req)
321 {
322   struct GSC_Client *c = cls;
323   struct GSC_ClientActiveRequest *car;
324   int is_loopback;
325
326   if (NULL == c->requests)
327     c->requests = GNUNET_CONTAINER_multipeermap_create (16,
328                                                         GNUNET_NO);
329   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
330               "Client asked for transmission to `%s'\n",
331               GNUNET_i2s (&req->peer));
332   is_loopback =
333       (0 ==
334        memcmp (&req->peer,
335                &GSC_my_identity,
336                sizeof (struct GNUNET_PeerIdentity)));
337   if ((! is_loopback) &&
338       (GNUNET_YES !=
339        GNUNET_CONTAINER_multipeermap_contains (c->connectmap,
340                                                &req->peer)))
341   {
342     /* neighbour must have disconnected since request was issued,
343      * ignore (client will realize it once it processes the
344      * disconnect notification) */
345     GNUNET_STATISTICS_update (GSC_stats,
346                               gettext_noop
347                               ("# send requests dropped (disconnected)"), 1,
348                               GNUNET_NO);
349     GNUNET_SERVICE_client_continue (c->client);
350     return;
351   }
352
353   car = GNUNET_CONTAINER_multipeermap_get (c->requests,
354                                            &req->peer);
355   if (NULL == car)
356   {
357     /* create new entry */
358     car = GNUNET_new (struct GSC_ClientActiveRequest);
359     GNUNET_assert (GNUNET_OK ==
360                    GNUNET_CONTAINER_multipeermap_put (c->requests,
361                                                       &req->peer,
362                                                       car,
363                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
364     car->client_handle = c;
365   }
366   else
367   {
368     /* dequeue and recycle memory from pending request, there can only
369        be at most one per client and peer */
370     GNUNET_STATISTICS_update (GSC_stats,
371                               gettext_noop ("# dequeuing CAR (duplicate request)"),
372                               1,
373                               GNUNET_NO);
374     GSC_SESSIONS_dequeue_request (car);
375     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
376                 "Transmission request to `%s' was a duplicate!\n",
377                 GNUNET_i2s (&req->peer));
378   }
379   car->target = req->peer;
380   car->received_time = GNUNET_TIME_absolute_get ();
381   car->deadline = GNUNET_TIME_absolute_ntoh (req->deadline);
382   car->priority = (enum GNUNET_CORE_Priority) ntohl (req->priority);
383   car->msize = ntohs (req->size);
384   car->smr_id = req->smr_id;
385   car->was_solicited = GNUNET_NO;
386   GNUNET_SERVICE_client_continue (c->client);
387   if (is_loopback)
388   {
389     /* loopback, satisfy immediately */
390     GSC_CLIENTS_solicit_request (car);
391     return;
392   }
393   GSC_SESSIONS_queue_request (car);
394 }
395
396
397 /**
398  * Closure for the #client_tokenizer_callback().
399  */
400 struct TokenizerContext
401 {
402
403   /**
404    * Active request handle for the message.
405    */
406   struct GSC_ClientActiveRequest *car;
407
408   /**
409    * How important is this message.
410    */
411   enum GNUNET_CORE_Priority priority;
412
413   /**
414    * Is corking allowed (set only once we have the real message).
415    */
416   int cork;
417
418 };
419
420
421 /**
422  * Functions with this signature are called whenever a complete
423  * message is received by the tokenizer.  Used by
424  * #handle_client_send() for dispatching messages from clients to
425  * either the SESSION subsystem or other CLIENT (for loopback).
426  *
427  * @param cls reservation request (`struct TokenizerContext`)
428  * @param message the actual message
429  * @return #GNUNET_OK on success,
430  *    #GNUNET_NO to stop further processing (no error)
431  *    #GNUNET_SYSERR to stop further processing with error
432  */
433 static int
434 tokenized_cb (void *cls,
435               const struct GNUNET_MessageHeader *message)
436 {
437   struct TokenizerContext *tc = cls;
438   struct GSC_ClientActiveRequest *car = tc->car;
439   char buf[92];
440
441   GNUNET_snprintf (buf,
442                    sizeof (buf),
443                    gettext_noop ("# bytes of messages of type %u received"),
444                    (unsigned int) ntohs (message->type));
445   GNUNET_STATISTICS_update (GSC_stats,
446                             buf,
447                             ntohs (message->size),
448                             GNUNET_NO);
449   if (0 ==
450       memcmp (&car->target,
451               &GSC_my_identity,
452               sizeof (struct GNUNET_PeerIdentity)))
453   {
454     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
455                 "Delivering message of type %u to myself\n",
456                 ntohs (message->type));
457     GSC_CLIENTS_deliver_message (&GSC_my_identity,
458                                  message,
459                                  ntohs (message->size),
460                                  GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND);
461     GSC_CLIENTS_deliver_message (&GSC_my_identity,
462                                  message,
463                                  sizeof (struct GNUNET_MessageHeader),
464                                  GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND);
465     GSC_CLIENTS_deliver_message (&GSC_my_identity,
466                                  message,
467                                  ntohs (message->size),
468                                  GNUNET_CORE_OPTION_SEND_FULL_INBOUND);
469     GSC_CLIENTS_deliver_message (&GSC_my_identity,
470                                  message,
471                                  sizeof (struct GNUNET_MessageHeader),
472                                  GNUNET_CORE_OPTION_SEND_HDR_INBOUND);
473   }
474   else
475   {
476     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
477                 "Delivering message of type %u and size %u to %s\n",
478                 ntohs (message->type),
479                 ntohs (message->size),
480                 GNUNET_i2s (&car->target));
481     GSC_CLIENTS_deliver_message (&car->target,
482                                  message,
483                                  ntohs (message->size),
484                                  GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND);
485     GSC_CLIENTS_deliver_message (&car->target,
486                                  message,
487                                  sizeof (struct GNUNET_MessageHeader),
488                                  GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND);
489     GSC_SESSIONS_transmit (car,
490                            message,
491                            tc->cork,
492                            tc->priority);
493   }
494   return GNUNET_OK;
495 }
496
497
498 /**
499  * Check #GNUNET_MESSAGE_TYPE_CORE_SEND request.
500  *
501  * @param cls the `struct GSC_Client`
502  * @param sm the `struct SendMessage`
503  * @return #GNUNET_OK if @a sm is well-formed
504  */
505 static int
506 check_client_send (void *cls,
507                    const struct SendMessage *sm)
508 {
509   return GNUNET_OK;
510 }
511
512
513 /**
514  * Handle #GNUNET_MESSAGE_TYPE_CORE_SEND request.
515  *
516  * @param cls the `struct GSC_Client`
517  * @param sm the `struct SendMessage`
518  */
519 static void
520 handle_client_send (void *cls,
521                     const struct SendMessage *sm)
522 {
523   struct GSC_Client *c = cls;
524   struct TokenizerContext tc;
525   uint16_t msize;
526   struct GNUNET_TIME_Relative delay;
527   struct GNUNET_MessageStreamTokenizer *mst;
528
529   msize = ntohs (sm->header.size) - sizeof (struct SendMessage);
530   GNUNET_break (0 == ntohl (sm->reserved));
531   tc.car = GNUNET_CONTAINER_multipeermap_get (c->requests,
532                                               &sm->peer);
533   if (NULL == tc.car)
534   {
535     /* Must have been that we first approved the request, then got disconnected
536      * (which triggered removal of the 'car') and now the client gives us a message
537      * just *before* the client learns about the disconnect.  Theoretically, we
538      * might also now be *again* connected.  So this can happen (but should be
539      * rare).  If it does happen, the message is discarded. */
540     GNUNET_STATISTICS_update (GSC_stats,
541                               gettext_noop ("# messages discarded (session disconnected)"),
542                               1,
543                               GNUNET_NO);
544     GNUNET_SERVICE_client_continue (c->client);
545     return;
546   }
547   delay = GNUNET_TIME_absolute_get_duration (tc.car->received_time);
548   tc.cork = ntohl (sm->cork);
549   tc.priority = (enum GNUNET_CORE_Priority) ntohl (sm->priority);
550   if (delay.rel_value_us > GNUNET_CONSTANTS_LATENCY_WARN.rel_value_us)
551     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
552                 "Client waited %s for transmission of %u bytes to `%s'%s\n",
553                 GNUNET_STRINGS_relative_time_to_string (delay,
554                                                         GNUNET_YES),
555                 msize,
556                 GNUNET_i2s (&sm->peer),
557                 tc.cork ? " (cork)" : " (uncorked)");
558   else
559     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
560                 "Client waited %s for transmission of %u bytes to `%s'%s\n",
561                 GNUNET_STRINGS_relative_time_to_string (delay,
562                                                         GNUNET_YES),
563                 msize,
564                 GNUNET_i2s (&sm->peer),
565                 tc.cork ? " (cork)" : " (uncorked)");
566
567   GNUNET_assert (GNUNET_YES ==
568                  GNUNET_CONTAINER_multipeermap_remove (c->requests,
569                                                        &sm->peer,
570                                                        tc.car));
571   mst = GNUNET_MST_create (&tokenized_cb,
572                            &tc);
573   GNUNET_MST_from_buffer (mst,
574                           (const char *) &sm[1],
575                           msize,
576                           GNUNET_YES,
577                           GNUNET_NO);
578   GNUNET_MST_destroy (mst);
579   GSC_SESSIONS_dequeue_request (tc.car);
580   GNUNET_free (tc.car);
581   GNUNET_SERVICE_client_continue (c->client);
582 }
583
584
585 /**
586  * Free client request records.
587  *
588  * @param cls NULL
589  * @param key identity of peer for which this is an active request
590  * @param value the `struct GSC_ClientActiveRequest` to free
591  * @return #GNUNET_YES (continue iteration)
592  */
593 static int
594 destroy_active_client_request (void *cls,
595                                const struct GNUNET_PeerIdentity *key,
596                                void *value)
597 {
598   struct GSC_ClientActiveRequest *car = value;
599
600   GNUNET_assert (GNUNET_YES ==
601                  GNUNET_CONTAINER_multipeermap_remove (car->
602                                                        client_handle->requests,
603                                                        &car->target,
604                                                        car));
605   GSC_SESSIONS_dequeue_request (car);
606   GNUNET_free (car);
607   return GNUNET_YES;
608 }
609
610
611 /**
612  * A client connected, set up.
613  *
614  * @param cls closure
615  * @param client identification of the client
616  * @param mq message queue to talk to @a client
617  * @return our client handle
618  */
619 static void *
620 client_connect_cb (void *cls,
621                    struct GNUNET_SERVICE_Client *client,
622                    struct GNUNET_MQ_Handle *mq)
623 {
624   struct GSC_Client *c;
625
626   c = GNUNET_new (struct GSC_Client);
627   c->client = client;
628   c->mq = mq;
629   c->connectmap = GNUNET_CONTAINER_multipeermap_create (16,
630                                                         GNUNET_NO);
631   GNUNET_CONTAINER_DLL_insert (client_head,
632                                client_tail,
633                                c);
634   return c;
635 }
636
637
638 /**
639  * A client disconnected, clean up.
640  *
641  * @param cls closure
642  * @param client identification of the client
643  * @param app_ctx our `struct GST_Client` for @a client
644  */
645 static void
646 client_disconnect_cb (void *cls,
647                       struct GNUNET_SERVICE_Client *client,
648                       void *app_ctx)
649 {
650   struct GSC_Client *c = app_ctx;
651
652   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
653               "Client %p has disconnected from core service.\n",
654               client);
655   GNUNET_CONTAINER_DLL_remove (client_head,
656                                client_tail,
657                                c);
658   if (NULL != c->requests)
659   {
660     GNUNET_CONTAINER_multipeermap_iterate (c->requests,
661                                            &destroy_active_client_request,
662                                            NULL);
663     GNUNET_CONTAINER_multipeermap_destroy (c->requests);
664   }
665   GNUNET_CONTAINER_multipeermap_destroy (c->connectmap);
666   c->connectmap = NULL;
667   if (NULL != c->types)
668   {
669     GSC_TYPEMAP_remove (c->types,
670                         c->tcnt);
671     GNUNET_free (c->types);
672   }
673   GNUNET_free (c);
674
675   /* recalculate 'all_client_options' */
676   all_client_options = 0;
677   for (c = client_head; NULL != c ; c = c->next)
678     all_client_options |= c->options;
679 }
680
681
682 /**
683  * Notify a particular client about a change to existing connection to
684  * one of our neighbours (check if the client is interested).  Called
685  * from #GSC_SESSIONS_notify_client_about_sessions().
686  *
687  * @param client client to notify
688  * @param neighbour identity of the neighbour that changed status
689  * @param tmap_old previous type map for the neighbour, NULL for connect
690  * @param tmap_new updated type map for the neighbour, NULL for disconnect
691  */
692 void
693 GSC_CLIENTS_notify_client_about_neighbour (struct GSC_Client *client,
694                                            const struct GNUNET_PeerIdentity *neighbour,
695                                            const struct GSC_TypeMap *tmap_old,
696                                            const struct GSC_TypeMap *tmap_new)
697 {
698   struct GNUNET_MQ_Envelope *env;
699   int old_match;
700   int new_match;
701
702   if (GNUNET_YES != client->got_init)
703     return;
704   old_match = GSC_TYPEMAP_test_match (tmap_old,
705                                       client->types,
706                                       client->tcnt);
707   new_match = GSC_TYPEMAP_test_match (tmap_new,
708                                       client->types,
709                                       client->tcnt);
710   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
711               "Notifying client about neighbour %s (%d/%d)\n",
712               GNUNET_i2s (neighbour),
713               old_match,
714               new_match);
715   if (old_match == new_match)
716   {
717     GNUNET_assert (old_match ==
718                    GNUNET_CONTAINER_multipeermap_contains (client->connectmap,
719                                                            neighbour));
720     return;                     /* no change */
721   }
722   if (GNUNET_NO == old_match)
723   {
724     struct ConnectNotifyMessage *cnm;
725
726     /* send connect */
727     GNUNET_assert (GNUNET_NO ==
728                    GNUNET_CONTAINER_multipeermap_contains (client->connectmap,
729                                                            neighbour));
730     GNUNET_assert (GNUNET_YES ==
731                    GNUNET_CONTAINER_multipeermap_put (client->connectmap,
732                                                       neighbour,
733                                                       NULL,
734                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
735     env = GNUNET_MQ_msg (cnm,
736                          GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
737     cnm->reserved = htonl (0);
738     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
739                 "Sending NOTIFY_CONNECT message about peer %s to client.\n",
740                 GNUNET_i2s (neighbour));
741     cnm->peer = *neighbour;
742     GNUNET_MQ_send (client->mq,
743                     env);
744   }
745   else
746   {
747     struct DisconnectNotifyMessage *dcm;
748
749     /* send disconnect */
750     GNUNET_assert (GNUNET_YES ==
751                    GNUNET_CONTAINER_multipeermap_contains (client->connectmap,
752                                                            neighbour));
753     GNUNET_assert (GNUNET_YES ==
754                    GNUNET_CONTAINER_multipeermap_remove (client->connectmap,
755                                                          neighbour,
756                                                          NULL));
757     env = GNUNET_MQ_msg (dcm,
758                          GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT);
759     dcm->reserved = htonl (0);
760     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
761                 "Sending NOTIFY_DISCONNECT message about peer %s to client.\n",
762                 GNUNET_i2s (neighbour));
763     dcm->peer = *neighbour;
764     GNUNET_MQ_send (client->mq,
765                     env);
766   }
767 }
768
769
770 /**
771  * Notify all clients about a change to existing session.
772  * Called from SESSIONS whenever there is a change in sessions
773  * or types processed by the respective peer.
774  *
775  * @param neighbour identity of the neighbour that changed status
776  * @param tmap_old previous type map for the neighbour, NULL for connect
777  * @param tmap_new updated type map for the neighbour, NULL for disconnect
778  */
779 void
780 GSC_CLIENTS_notify_clients_about_neighbour (const struct GNUNET_PeerIdentity *neighbour,
781                                             const struct GSC_TypeMap *tmap_old,
782                                             const struct GSC_TypeMap *tmap_new)
783 {
784   struct GSC_Client *c;
785
786   for (c = client_head; NULL != c; c = c->next)
787     GSC_CLIENTS_notify_client_about_neighbour (c,
788                                                neighbour,
789                                                tmap_old,
790                                                tmap_new);
791 }
792
793
794 /**
795  * Deliver P2P message to interested clients.  Caller must have checked
796  * that the sending peer actually lists the given message type as one
797  * of its types.
798  *
799  * @param sender peer who sent us the message
800  * @param msg the message
801  * @param msize number of bytes to transmit
802  * @param options options for checking which clients should
803  *        receive the message
804  */
805 void
806 GSC_CLIENTS_deliver_message (const struct GNUNET_PeerIdentity *sender,
807                              const struct GNUNET_MessageHeader *msg,
808                              uint16_t msize,
809                              uint32_t options)
810 {
811   size_t size = msize + sizeof (struct NotifyTrafficMessage);
812
813   if (size >= GNUNET_MAX_MESSAGE_SIZE)
814   {
815     GNUNET_break (0);
816     return;
817   }
818   if (! ( (0 != (all_client_options & options)) ||
819           (0 != (options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) ))
820     return; /* no client cares about this message notification */
821   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
822               "Core service passes message from `%s' of type %u to client.\n",
823               GNUNET_i2s (sender),
824               (unsigned int) ntohs (msg->type));
825   GSC_SESSIONS_add_to_typemap (sender,
826                                ntohs (msg->type));
827
828   for (struct GSC_Client *c = client_head; NULL != c; c = c->next)
829   {
830     struct GNUNET_MQ_Envelope *env;
831     struct NotifyTrafficMessage *ntm;
832     uint16_t mtype;
833     unsigned int qlen;
834     int tm;
835
836     tm = type_match (ntohs (msg->type),
837                      c);
838     if (! ( (0 != (c->options & options)) ||
839             ( (0 != (options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) &&
840               (GNUNET_YES == tm) ) ) )
841       continue;  /* neither options nor type match permit the message */
842     if ( (0 != (options & GNUNET_CORE_OPTION_SEND_HDR_INBOUND)) &&
843          ( (0 != (c->options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) ||
844            (GNUNET_YES == tm) ) )
845       continue;
846     if ( (0 != (options & GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND)) &&
847          (0 != (c->options & GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND)) )
848       continue;
849
850     /* Drop messages if:
851        1) We are above the hard limit, or
852        2) We are above the soft limit, and a coin toss limited
853           to the message size (giving larger messages a
854           proportionally higher chance of being queued) falls
855           below the threshold. The threshold is based on where
856           we are between the soft and the hard limit, scaled
857           to match the range of message sizes we usually encounter
858           (i.e. up to 32k); so a 64k message has a 50% chance of
859           being kept if we are just barely below the hard max,
860           and a 99% chance of being kept if we are at the soft max.
861        The reason is to make it more likely to drop control traffic
862        (ACK, queries) which may be cummulative or highly redundant,
863        and cheap to drop than data traffic.  */
864     qlen = GNUNET_MQ_get_length (c->mq);
865     if ( (qlen >= HARD_MAX_QUEUE) ||
866          ( (qlen > SOFT_MAX_QUEUE) &&
867            ( (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
868                                         ntohs (msg->size)) ) <
869              (qlen - SOFT_MAX_QUEUE) * 0x8000 /
870              (HARD_MAX_QUEUE - SOFT_MAX_QUEUE) ) ) )
871     {
872       char buf[1024];
873
874       GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
875                   "Dropping decrypted message of type %u as client is too busy (queue full)\n",
876                   (unsigned int) ntohs (msg->type));
877       GNUNET_snprintf (buf,
878                        sizeof (buf),
879                        gettext_noop ("# messages of type %u discarded (client busy)"),
880                        (unsigned int) ntohs (msg->type));
881       GNUNET_STATISTICS_update (GSC_stats,
882                                 buf,
883                                 1,
884                                 GNUNET_NO);
885       continue;
886     }
887
888     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
889                 "Sending %u message with %u bytes to client interested in messages of type %u.\n",
890                 options,
891                 ntohs (msg->size),
892                 (unsigned int) ntohs (msg->type));
893
894     if (0 != (options & (GNUNET_CORE_OPTION_SEND_FULL_INBOUND | GNUNET_CORE_OPTION_SEND_HDR_INBOUND)))
895       mtype = GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND;
896     else
897       mtype = GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND;
898     env = GNUNET_MQ_msg_extra (ntm,
899                                msize,
900                                mtype);
901     ntm->peer = *sender;
902     GNUNET_memcpy (&ntm[1],
903                    msg,
904                    msize);
905
906     GNUNET_assert ( (0 == (c->options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) ||
907                     (GNUNET_YES != tm) ||
908                     (GNUNET_YES ==
909                      GNUNET_CONTAINER_multipeermap_contains (c->connectmap,
910                                                              sender)) );
911     GNUNET_MQ_send (c->mq,
912                     env);
913   }
914 }
915
916
917 /**
918  * Last task run during shutdown.  Disconnects us from
919  * the transport.
920  *
921  * @param cls NULL, unused
922  */
923 static void
924 shutdown_task (void *cls)
925 {
926   struct GSC_Client *c;
927
928   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
929               "Core service shutting down.\n");
930   while (NULL != (c = client_head))
931     GNUNET_SERVICE_client_drop (c->client);
932   GSC_SESSIONS_done ();
933   GSC_KX_done ();
934   GSC_TYPEMAP_done ();
935   if (NULL != GSC_stats)
936   {
937     GNUNET_STATISTICS_destroy (GSC_stats,
938                                GNUNET_NO);
939     GSC_stats = NULL;
940   }
941   GSC_cfg = NULL;
942 }
943
944
945 /**
946  * Handle #GNUNET_MESSAGE_TYPE_CORE_MONITOR_PEERS request.  For this
947  * request type, the client does not have to have transmitted an INIT
948  * request.  All current peers are returned, regardless of which
949  * message types they accept.
950  *
951  * @param cls client sending the iteration request
952  * @param message iteration request message
953  */
954 static void
955 handle_client_monitor_peers (void *cls,
956                              const struct GNUNET_MessageHeader *message)
957 {
958   struct GSC_Client *c = cls;
959
960   GNUNET_SERVICE_client_continue (c->client);
961   GSC_KX_handle_client_monitor_peers (c->mq);
962 }
963
964
965 /**
966  * Initiate core service.
967  *
968  * @param cls closure
969  * @param c configuration to use
970  * @param service the initialized service
971  */
972 static void
973 run (void *cls,
974      const struct GNUNET_CONFIGURATION_Handle *c,
975      struct GNUNET_SERVICE_Handle *service)
976 {
977   struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
978   char *keyfile;
979
980   GSC_cfg = c;
981   if (GNUNET_OK !=
982       GNUNET_CONFIGURATION_get_value_filename (GSC_cfg,
983                                                "PEER",
984                                                "PRIVATE_KEY",
985                                                &keyfile))
986   {
987     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
988                 _("Core service is lacking HOSTKEY configuration setting.  Exiting.\n"));
989     GNUNET_SCHEDULER_shutdown ();
990     return;
991   }
992   GSC_stats = GNUNET_STATISTICS_create ("core",
993                                         GSC_cfg);
994   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
995                                  NULL);
996   GNUNET_SERVICE_suspend (service);
997   GSC_TYPEMAP_init ();
998   pk = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile);
999   GNUNET_free (keyfile);
1000   GNUNET_assert (NULL != pk);
1001   if (GNUNET_OK != GSC_KX_init (pk))
1002   {
1003     GNUNET_SCHEDULER_shutdown ();
1004     return;
1005   }
1006   GSC_SESSIONS_init ();
1007   GNUNET_SERVICE_resume (service);
1008   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1009               _("Core service of `%s' ready.\n"),
1010               GNUNET_i2s (&GSC_my_identity));
1011 }
1012
1013
1014 /**
1015  * Define "main" method using service macro.
1016  */
1017 GNUNET_SERVICE_MAIN
1018 ("core",
1019  GNUNET_SERVICE_OPTION_NONE,
1020  &run,
1021  &client_connect_cb,
1022  &client_disconnect_cb,
1023  NULL,
1024  GNUNET_MQ_hd_var_size (client_init,
1025                         GNUNET_MESSAGE_TYPE_CORE_INIT,
1026                         struct InitMessage,
1027                         NULL),
1028  GNUNET_MQ_hd_fixed_size (client_monitor_peers,
1029                           GNUNET_MESSAGE_TYPE_CORE_MONITOR_PEERS,
1030                           struct GNUNET_MessageHeader,
1031                           NULL),
1032  GNUNET_MQ_hd_fixed_size (client_send_request,
1033                           GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST,
1034                           struct SendMessageRequest,
1035                           NULL),
1036  GNUNET_MQ_hd_var_size (client_send,
1037                         GNUNET_MESSAGE_TYPE_CORE_SEND,
1038                         struct SendMessage,
1039                         NULL),
1040  GNUNET_MQ_handler_end ());
1041
1042
1043 /* end of gnunet-service-core.c */