2 This file is part of GNUnet.
3 (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file core/gnunet-service-core_clients.c
23 * @brief code for managing interactions with clients of core service
24 * @author Christian Grothoff
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"
37 * How many messages do we queue up at most for optional
38 * notifications to a client? (this can cause notifications
39 * about outgoing messages to be dropped).
41 #define MAX_NOTIFY_QUEUE 1024
45 * Data structure for each client connected to the core service.
50 * Clients are kept in a linked list.
52 struct GSC_Client *next;
55 * Clients are kept in a linked list.
57 struct GSC_Client *prev;
60 * Handle for the client with the server API.
62 struct GNUNET_SERVER_Client *client_handle;
65 * Array of the types of messages this peer cares
66 * about (with "tcnt" entries). Allocated as part
67 * of this client struct, do not free!
69 const uint16_t *types;
72 * Map of peer identities to active transmission requests of this
73 * client to the peer (of type 'struct GSC_ClientActiveRequest').
75 struct GNUNET_CONTAINER_MultiHashMap *requests;
78 * Options for messages this client cares about,
79 * see GNUNET_CORE_OPTION_ values.
84 * Number of types of incoming messages this client
85 * specifically cares about. Size of the "types" array.
93 * Head of linked list of our clients.
95 static struct GSC_Client *client_head;
98 * Tail of linked list of our clients.
100 static struct GSC_Client *client_tail;
103 * Context for notifications we need to send to our clients.
105 static struct GNUNET_SERVER_NotificationContext *notifier;
108 * Tokenizer for messages received from clients.
110 static struct GNUNET_SERVER_MessageStreamTokenizer *client_mst;
114 * Lookup our client struct given the server's client handle.
116 * @param client server client handle to look up
117 * @return our client handle for the client
119 static struct GSC_Client *
120 find_client (struct GNUNET_SERVER_Client *client)
122 struct GSC_Client *c;
125 while ((c != NULL) && (c->client_handle != client))
132 * Send a message to one of our clients.
134 * @param client target for the message
135 * @param msg message to transmit
136 * @param can_drop could this message be dropped if the
137 * client's queue is getting too large?
140 send_to_client (struct GSC_Client *client,
141 const struct GNUNET_MessageHeader *msg,
145 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
146 "Preparing to send %u bytes of message of type %u to client.\n",
147 (unsigned int) ntohs (msg->size),
148 (unsigned int) ntohs (msg->type));
150 GNUNET_SERVER_notification_context_unicast (notifier, client->client_handle,
156 * Send a message to one of our clients.
158 * @param client target for the message
159 * @param msg message to transmit
160 * @param can_drop could this message be dropped if the
161 * client's queue is getting too large?
164 GSC_CLIENTS_send_to_client (struct GNUNET_SERVER_Client *client,
165 const struct GNUNET_MessageHeader *msg,
168 struct GSC_Client *c;
170 c = find_client (client);
176 send_to_client (c, msg, can_drop);
181 * Test if the client is interested in messages of the given type.
183 * @param type message type
184 * @param c client to test
185 * @return GNUNET_YES if 'c' is interested, GNUNET_NO if not.
188 type_match (uint16_t type,
189 struct GSC_Client *c)
194 return GNUNET_YES; /* peer without handlers matches ALL */
195 for (i=0;i<c->tcnt;i++)
196 if (type == c->types[i])
203 * Send a message to all of our current clients that have the right
206 * @param msg message to multicast
207 * @param can_drop can this message be discarded if the queue is too long
208 * @param options mask to use
209 * @param type type of the embedded message, 0 for none
212 send_to_all_clients (const struct GNUNET_MessageHeader *msg,
217 struct GSC_Client *c;
219 for (c = client_head; c != NULL; c = c->next)
221 if ( (0 == (options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) &&
222 (GNUNET_YES == type_match (type, c)) )
223 continue; /* not the full message, but we'd like the full one! */
224 if ( (0 == (c->options & options)) &&
225 (GNUNET_YES != type_match (type, c)) )
226 continue; /* neither options nor type match permit the message */
228 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
229 "Sending message to client interested in messages of type %u.\n",
230 (unsigned int) type);
232 send_to_client (c, msg, can_drop);
238 * Handle CORE_INIT request.
241 * @param client new client that sent INIT
242 * @param message the 'struct InitMessage' (presumably)
245 handle_client_init (void *cls, struct GNUNET_SERVER_Client *client,
246 const struct GNUNET_MessageHeader *message)
248 const struct InitMessage *im;
249 struct InitReplyMessage irm;
250 struct GSC_Client *c;
252 const uint16_t *types;
256 /* check that we don't have an entry already */
257 c = find_client (client);
261 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
264 msize = ntohs (message->size);
265 if (msize < sizeof (struct InitMessage))
268 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
271 GNUNET_SERVER_notification_context_add (notifier, client);
272 im = (const struct InitMessage *) message;
273 types = (const uint16_t *) &im[1];
274 msize -= sizeof (struct InitMessage);
275 c = GNUNET_malloc (sizeof (struct GSC_Client) + msize);
276 c->client_handle = client;
277 c->tcnt = msize / sizeof (uint16_t);
278 c->options = ntohl (im->options);
279 c->types = (const uint16_t *) &c[1];
280 wtypes = (uint16_t *) & c[1];
281 for (i = 0; i < c->tcnt; i++)
282 wtypes[i] = ntohs (types[i]);
283 GSC_TYPEMAP_add (wtypes, c->tcnt);
284 GNUNET_CONTAINER_DLL_insert (client_head,
288 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
289 "Client connecting to core service is interested in %u message types\n",
290 (unsigned int) c->tcnt);
292 /* send init reply message */
293 irm.header.size = htons (sizeof (struct InitReplyMessage));
294 irm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY);
295 irm.reserved = htonl (0);
296 irm.my_identity = GSC_my_identity;
297 send_to_client (c, &irm.header, GNUNET_NO);
298 if (0 != (c->options & GNUNET_CORE_OPTION_SEND_CONNECT))
299 GSC_SESSIONS_notify_client_about_sessions (c);
300 GNUNET_SERVER_receive_done (client, GNUNET_OK);
305 * Handle CORE_SEND_REQUEST message.
308 * @param client new client that sent CORE_SEND_REQUEST
309 * @param message the 'struct InitMessage' (presumably)
312 handle_client_send_request (void *cls, struct GNUNET_SERVER_Client *client,
313 const struct GNUNET_MessageHeader *message)
315 const struct SendMessageRequest *req;
316 struct GSC_Client *c;
317 struct GSC_ClientActiveRequest *car;
319 req = (const struct SendMessageRequest *) message;
320 c = find_client (client);
323 /* client did not send INIT first! */
325 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
328 if (c->requests == NULL)
329 c->requests = GNUNET_CONTAINER_multihashmap_create (16);
331 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
332 "Client asked for transmission to `%s'\n",
333 GNUNET_i2s (&req->peer));
335 car = GNUNET_CONTAINER_multihashmap_get (c->requests, &req->peer.hashPubKey);
338 /* create new entry */
339 car = GNUNET_malloc (sizeof (struct GSC_ClientActiveRequest));
340 GNUNET_assert (GNUNET_OK ==
341 GNUNET_CONTAINER_multihashmap_put (c->requests,
342 &req->peer.hashPubKey,
344 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
345 car->client_handle = c;
349 GSC_SESSIONS_dequeue_request (car);
351 car->target = req->peer;
352 car->deadline = GNUNET_TIME_absolute_ntoh (req->deadline);
353 car->priority = ntohl (req->priority);
354 car->msize = ntohs (req->size);
355 car->smr_id = req->smr_id;
356 car->was_solicited = GNUNET_NO;
358 memcmp (&req->peer, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity)))
359 GSC_CLIENTS_solicit_request (car);
361 GSC_SESSIONS_queue_request (car);
362 GNUNET_SERVER_receive_done (client, GNUNET_OK);
367 * Closure for the 'client_tokenizer_callback'.
369 struct TokenizerContext
373 * Active request handle for the message.
375 struct GSC_ClientActiveRequest *car;
378 * Is corking allowed (set only once we have the real message).
386 * Handle CORE_SEND request.
389 * @param client the client issuing the request
390 * @param message the "struct SendMessage"
393 handle_client_send (void *cls, struct GNUNET_SERVER_Client *client,
394 const struct GNUNET_MessageHeader *message)
396 const struct SendMessage *sm;
397 struct GSC_Client *c;
398 struct TokenizerContext tc;
401 msize = ntohs (message->size);
403 sizeof (struct SendMessage) + sizeof (struct GNUNET_MessageHeader))
406 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
409 sm = (const struct SendMessage *) message;
410 msize -= sizeof (struct SendMessage);
411 GNUNET_break (0 == ntohl (sm->reserved));
412 c = find_client (client);
415 /* client did not send INIT first! */
417 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
420 tc.car = GNUNET_CONTAINER_multihashmap_get (c->requests, &sm->peer.hashPubKey);
423 /* client did not request transmission first! */
425 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
428 GNUNET_assert (GNUNET_YES ==
429 GNUNET_CONTAINER_multihashmap_remove (c->requests,
430 &sm->peer.hashPubKey,
432 tc.cork = ntohl (sm->cork);
434 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
435 "Client asked for transmission of %u bytes to `%s' %s\n",
437 GNUNET_i2s (&sm->peer),
438 tc.cork ? "now" : "");
440 GNUNET_SERVER_mst_receive (client_mst,
442 (const char*) &sm[1], msize,
446 memcmp (&tc.car->target, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity)))
447 GSC_SESSIONS_dequeue_request (tc.car);
448 GNUNET_free (tc.car);
449 GNUNET_SERVER_receive_done (client, GNUNET_OK);
454 * Functions with this signature are called whenever a complete
455 * message is received by the tokenizer. Used by the 'client_mst' for
456 * dispatching messages from clients to either the SESSION subsystem
457 * or other CLIENT (for loopback).
460 * @param client reservation request ('struct GSC_ClientActiveRequest')
461 * @param message the actual message
464 client_tokenizer_callback (void *cls, void *client,
465 const struct GNUNET_MessageHeader *message)
467 struct TokenizerContext *tc = client;
468 struct GSC_ClientActiveRequest *car = tc->car;
471 memcmp (&car->target, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity)))
474 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
475 "Delivering message of type %u to myself\n",
476 ntohs (message->type));
478 GSC_CLIENTS_deliver_message (&GSC_my_identity,
481 ntohs (message->size),
482 GNUNET_CORE_OPTION_SEND_FULL_INBOUND | GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND);
483 GSC_CLIENTS_deliver_message (&GSC_my_identity,
486 sizeof (struct GNUNET_MessageHeader),
487 GNUNET_CORE_OPTION_SEND_HDR_INBOUND | GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND);
492 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
493 "Delivering message of type %u to %s\n",
494 ntohs (message->type),
495 GNUNET_i2s (&car->target));
497 GSC_SESSIONS_transmit (car, message, tc->cork);
503 * Free client request records.
506 * @param key identity of peer for which this is an active request
507 * @param value the 'struct GSC_ClientActiveRequest' to free
508 * @return GNUNET_YES (continue iteration)
511 destroy_active_client_request (void *cls, const GNUNET_HashCode * key,
514 struct GSC_ClientActiveRequest *car = value;
516 GNUNET_assert (GNUNET_YES ==
517 GNUNET_CONTAINER_multihashmap_remove (car->client_handle->requests,
518 &car->target.hashPubKey,
520 GSC_SESSIONS_dequeue_request (car);
527 * A client disconnected, clean up.
530 * @param client identification of the client
533 handle_client_disconnect (void *cls,
534 struct GNUNET_SERVER_Client *client)
536 struct GSC_Client *c;
541 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
542 "Client %p has disconnected from core service.\n", client);
544 c = find_client (client);
546 return; /* client never sent INIT */
547 GNUNET_CONTAINER_DLL_remove (client_head,
550 if (c->requests != NULL)
552 GNUNET_CONTAINER_multihashmap_iterate (c->requests,
553 &destroy_active_client_request,
555 GNUNET_CONTAINER_multihashmap_destroy (c->requests);
557 GSC_TYPEMAP_remove (c->types, c->tcnt);
563 * Tell a client that we are ready to receive the message.
565 * @param car request that is now ready; the responsibility
566 * for the handle remains shared between CLIENTS
567 * and SESSIONS after this call.
570 GSC_CLIENTS_solicit_request (struct GSC_ClientActiveRequest *car)
572 struct GSC_Client *c;
573 struct SendMessageReady smr;
575 c = car->client_handle;
576 smr.header.size = htons (sizeof (struct SendMessageReady));
577 smr.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SEND_READY);
578 smr.size = htons (car->msize);
579 smr.smr_id = car->smr_id;
580 smr.peer = car->target;
581 send_to_client (c, &smr.header, GNUNET_NO);
586 * Tell a client that we will never be ready to receive the
587 * given message in time (disconnect or timeout).
589 * @param car request that now permanently failed; the
590 * responsibility for the handle is now returned
591 * to CLIENTS (SESSIONS is done with it).
594 GSC_CLIENTS_reject_request (struct GSC_ClientActiveRequest *car)
596 GNUNET_assert (GNUNET_YES ==
597 GNUNET_CONTAINER_multihashmap_remove (car->client_handle->requests,
598 &car->target.hashPubKey,
605 * Notify a particular client about a change to existing connection to
606 * one of our neighbours (check if the client is interested). Called
607 * from 'GSC_SESSIONS_notify_client_about_sessions'.
609 * @param client client to notify
610 * @param neighbour identity of the neighbour that changed status
611 * @param atsi performance information about neighbour
612 * @param atsi_count number of entries in 'ats' array
613 * @param tmap_old previous type map for the neighbour, NULL for disconnect
614 * @param tmap_new updated type map for the neighbour, NULL for disconnect
615 * @param is_new GNUNET_YES if this is a completely new neighbour
618 GSC_CLIENTS_notify_client_about_neighbour (struct GSC_Client *client,
619 const struct GNUNET_PeerIdentity *neighbour,
620 const struct GNUNET_ATS_Information *atsi,
621 unsigned int atsi_count,
622 const struct GSC_TypeMap *tmap_old,
623 const struct GSC_TypeMap *tmap_new,
626 struct ConnectNotifyMessage *cnm;
628 char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
629 struct GNUNET_ATS_Information *a;
630 struct DisconnectNotifyMessage dcm;
634 old_match = GSC_TYPEMAP_test_match (tmap_old, client->types, client->tcnt);
635 new_match = GSC_TYPEMAP_test_match (tmap_new, client->types, client->tcnt);
636 if (client->tcnt == 0)
638 /* empty list matches ALL (if not NULL) */
639 if (tmap_new != NULL)
640 new_match = GNUNET_YES;
641 if (tmap_old != NULL)
642 old_match = GNUNET_YES;
644 if (GNUNET_YES == is_new)
645 old_match = GNUNET_NO;
646 if (old_match == new_match)
647 return; /* no change */
648 if (old_match == GNUNET_NO)
652 sizeof (struct ConnectNotifyMessage) +
653 (atsi_count) * sizeof (struct GNUNET_ATS_Information);
654 if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
657 /* recovery strategy: throw away performance data */
659 size = sizeof (struct ConnectNotifyMessage);
661 cnm = (struct ConnectNotifyMessage *) buf;
662 cnm->header.size = htons (size);
663 cnm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
664 cnm->ats_count = htonl (atsi_count);
667 sizeof (struct GNUNET_ATS_Information) * atsi_count);
668 a[atsi_count].type = htonl (GNUNET_ATS_ARRAY_TERMINATOR);
669 a[atsi_count].value = htonl (0);
671 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
672 "Sending `%s' message to client.\n",
675 cnm->peer = *neighbour;
676 send_to_client (client, &cnm->header, GNUNET_NO);
680 /* send disconnect */
681 dcm.header.size = htons (sizeof (struct DisconnectNotifyMessage));
682 dcm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT);
683 dcm.reserved = htonl (0);
684 dcm.peer = *neighbour;
685 send_to_client (client, &dcm.header, GNUNET_NO);
691 * Notify all clients about a change to existing session.
692 * Called from SESSIONS whenever there is a change in sessions
693 * or types processed by the respective peer.
695 * @param neighbour identity of the neighbour that changed status
696 * @param atsi performance information about neighbour
697 * @param atsi_count number of entries in 'ats' array
698 * @param tmap_old previous type map for the neighbour, NULL for disconnect
699 * @param tmap_new updated type map for the neighbour, NULL for disconnect
702 GSC_CLIENTS_notify_clients_about_neighbour (const struct GNUNET_PeerIdentity *neighbour,
703 const struct GNUNET_ATS_Information *atsi,
704 unsigned int atsi_count,
705 const struct GSC_TypeMap *tmap_old,
706 const struct GSC_TypeMap *tmap_new)
708 struct GSC_Client *c;
710 for (c = client_head; c != NULL; c = c->next)
711 GSC_CLIENTS_notify_client_about_neighbour (c, neighbour, atsi,
719 * Deliver P2P message to interested clients. Caller must have checked
720 * that the sending peer actually lists the given message type as one
723 * @param sender peer who sent us the message
724 * @param atsi performance information about neighbour
725 * @param atsi_count number of entries in 'ats' array
726 * @param msg the message
727 * @param msize number of bytes to transmit
728 * @param options options for checking which clients should
729 * receive the message
732 GSC_CLIENTS_deliver_message (const struct GNUNET_PeerIdentity *sender,
733 const struct GNUNET_ATS_Information *atsi,
734 unsigned int atsi_count,
735 const struct GNUNET_MessageHeader *msg,
739 size_t size = msize + sizeof (struct NotifyTrafficMessage) +
740 atsi_count * sizeof (struct GNUNET_ATS_Information);
742 struct NotifyTrafficMessage *ntm;
743 struct GNUNET_ATS_Information *a;
747 GNUNET_snprintf (buf, sizeof (buf),
748 gettext_noop ("# bytes of messages of type %u received"),
749 (unsigned int) ntohs (msg->type));
750 GNUNET_STATISTICS_update (GSC_stats, buf, msize, GNUNET_NO);
752 if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
755 /* recovery strategy: throw performance data away... */
757 size = msize + sizeof (struct NotifyTrafficMessage);
760 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
761 "Core service passes message from `%4s' of type %u to client.\n",
763 (unsigned int) ntohs (msg->type));
765 GSC_SESSIONS_add_to_typemap (sender, ntohs (msg->type));
766 ntm = (struct NotifyTrafficMessage *) buf;
767 ntm->header.size = htons (size);
768 ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND);
769 ntm->ats_count = htonl (atsi_count);
773 sizeof (struct GNUNET_ATS_Information) * atsi_count);
774 a[atsi_count].type = htonl (GNUNET_ATS_ARRAY_TERMINATOR);
775 a[atsi_count].value = htonl (0);
776 memcpy (&a[atsi_count + 1], msg, msize);
777 send_to_all_clients (&ntm->header, GNUNET_YES,
778 options, ntohs (msg->type));
783 * Initialize clients subsystem.
785 * @param server handle to server clients connect to
788 GSC_CLIENTS_init (struct GNUNET_SERVER_Handle *server)
790 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
791 {&handle_client_init, NULL,
792 GNUNET_MESSAGE_TYPE_CORE_INIT, 0},
793 {&GSC_SESSIONS_handle_client_iterate_peers, NULL,
794 GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS,
795 sizeof (struct GNUNET_MessageHeader)},
796 {&GSC_SESSIONS_handle_client_have_peer, NULL,
797 GNUNET_MESSAGE_TYPE_CORE_PEER_CONNECTED,
798 sizeof (struct GNUNET_MessageHeader) +
799 sizeof (struct GNUNET_PeerIdentity)},
800 {&handle_client_send_request, NULL,
801 GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST,
802 sizeof (struct SendMessageRequest)},
803 {&handle_client_send, NULL,
804 GNUNET_MESSAGE_TYPE_CORE_SEND, 0},
808 /* setup notification */
809 client_mst = GNUNET_SERVER_mst_create (&client_tokenizer_callback, NULL);
811 GNUNET_SERVER_notification_context_create (server, MAX_NOTIFY_QUEUE);
812 GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
813 GNUNET_SERVER_add_handlers (server, handlers);
818 * Shutdown clients subsystem.
823 struct GSC_Client *c;
825 while (NULL != (c = client_head))
826 handle_client_disconnect (NULL, c->client_handle);
827 if (NULL != notifier)
829 GNUNET_SERVER_notification_context_destroy (notifier);
832 GNUNET_SERVER_mst_destroy (client_mst);
836 /* end of gnunet-service-core_clients.c */