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