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_transport_service.h"
29 #include "gnunet_service_core.h"
30 #include "gnunet_service_core_clients.h"
31 #include "gnunet_service_core_sessions.h"
32 #include "gnunet_service_core_typemap.h"
36 * Data structure for each client connected to the core service.
41 * Clients are kept in a linked list.
43 struct GSC_Client *next;
46 * Clients are kept in a linked list.
48 struct GSC_Client *prev;
51 * Handle for the client with the server API.
53 struct GNUNET_SERVER_Client *client_handle;
56 * Array of the types of messages this peer cares
57 * about (with "tcnt" entries). Allocated as part
58 * of this client struct, do not free!
60 const uint16_t *types;
63 * Map of peer identities to active transmission requests of this
64 * client to the peer (of type 'struct GSC_ClientActiveRequest').
66 struct GNUNET_CONTAINER_MultiHashMap *requests;
69 * Options for messages this client cares about,
70 * see GNUNET_CORE_OPTION_ values.
75 * Number of types of incoming messages this client
76 * specifically cares about. Size of the "types" array.
84 * Head of linked list of our clients.
86 static struct GSC_Client *client_head;
89 * Tail of linked list of our clients.
91 static struct GSC_Client *client_tail;
94 * Context for notifications we need to send to our clients.
96 static struct GNUNET_SERVER_NotificationContext *notifier;
99 * Tokenizer for messages received from clients.
101 static struct GNUNET_SERVER_MessageStreamTokenizer *client_mst;
105 * Lookup our client struct given the server's client handle.
107 * @param client server client handle to look up
108 * @return our client handle for the client
110 static struct GSC_Client *
111 find_client (struct GNUNET_SERVER_Client *client)
113 struct GSC_Client *c;
116 while ((c != NULL) && (c->client_handle != client))
123 * Send a message to one of our clients.
125 * @param client target for the message
126 * @param msg message to transmit
127 * @param can_drop could this message be dropped if the
128 * client's queue is getting too large?
131 send_to_client (struct GSC_Client *client,
132 const struct GNUNET_MessageHeader *msg,
135 #if DEBUG_CORE_CLIENT
136 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
137 "Preparing to send %u bytes of message of type %u to client.\n",
138 (unsigned int) ntohs (msg->size),
139 (unsigned int) ntohs (msg->type));
141 GNUNET_SERVER_notification_context_unicast (notifier, client->client_handle,
147 * Send a message to one of our clients.
149 * @param client target for the message
150 * @param msg message to transmit
151 * @param can_drop could this message be dropped if the
152 * client's queue is getting too large?
155 GSC_CLIENTS_send_to_client (struct GNUNET_SERVER_Client *client,
156 const struct GNUNET_MessageHeader *msg,
159 struct GSC_Client *c;
161 c = find_client (client);
167 send_to_client (c, msg, can_drop);
172 * Test if the client is interested in messages of the given type.
174 * @param type message type
175 * @param c client to test
176 * @return GNUNET_YES if 'c' is interested, GNUNET_NO if not.
179 type_match (uint16_t type,
180 struct GSC_Client *c)
184 for (i=0;i<c->tcnt;i++)
185 if (type == c->types[i])
192 * Send a message to all of our current clients that have the right
195 * @param msg message to multicast
196 * @param can_drop can this message be discarded if the queue is too long
197 * @param options mask to use
198 * @param type type of the embedded message, 0 for none
201 send_to_all_clients (const struct GNUNET_MessageHeader *msg,
206 struct GSC_Client *c;
208 for (c = client_head; c != NULL; c = c->next)
210 if ( (0 != (c->options & options)) &&
211 (GNUNET_YES == type_match (type, c)) )
212 continue; /* both match, wait for only type match */
213 if ( (0 == (c->options & options)) &&
214 (GNUNET_YES != type_match (type, c)) )
215 continue; /* neither match, skip entirely */
216 #if DEBUG_CORE_CLIENT > 1
217 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
218 "Sending message of type %u to client.\n",
219 (unsigned int) ntohs (msg->type));
221 send_to_client (c, msg, can_drop);
227 * Handle CORE_INIT request.
230 * @param client new client that sent INIT
231 * @param message the 'struct InitMessage' (presumably)
234 handle_client_init (void *cls, struct GNUNET_SERVER_Client *client,
235 const struct GNUNET_MessageHeader *message)
237 const struct InitMessage *im;
238 struct InitReplyMessage irm;
239 struct GSC_Client *c;
241 const uint16_t *types;
245 /* check that we don't have an entry already */
246 c = find_client (client);
250 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
253 msize = ntohs (message->size);
254 if (msize < sizeof (struct InitMessage))
257 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
260 GNUNET_SERVER_notification_context_add (notifier, client);
261 im = (const struct InitMessage *) message;
262 types = (const uint16_t *) &im[1];
263 msize -= sizeof (struct InitMessage);
264 c = GNUNET_malloc (sizeof (struct GSC_Client) + msize);
265 c->client_handle = client;
266 c->tcnt = msize / sizeof (uint16_t);
267 c->options = ntohl (im->options);
268 c->types = (const uint16_t *) &c[1];
269 wtypes = (uint16_t *) & c[1];
270 for (i = 0; i < c->tcnt; i++)
271 wtypes[i] = ntohs (types[i]);
272 GSC_TYPEMAP_add (wtypes, c->tcnt);
273 GNUNET_CONTAINER_DLL_insert (client_head,
276 #if DEBUG_CORE_CLIENT
277 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
278 "Client connecting to core service is interested in %u message types\n",
279 (unsigned int) c->tcnt);
281 /* send init reply message */
282 irm.header.size = htons (sizeof (struct InitReplyMessage));
283 irm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY);
284 irm.reserved = htonl (0);
285 irm.publicKey = GSC_my_public_key;
286 send_to_client (c, &irm.header, GNUNET_NO);
287 if (0 != (c->options & GNUNET_CORE_OPTION_SEND_CONNECT))
288 GSC_SESSIONS_notify_client_about_sessions (c);
289 GNUNET_SERVER_receive_done (client, GNUNET_OK);
294 * Handle CORE_SEND_REQUEST message.
297 * @param client new client that sent CORE_SEND_REQUEST
298 * @param message the 'struct InitMessage' (presumably)
301 handle_client_send_request (void *cls, struct GNUNET_SERVER_Client *client,
302 const struct GNUNET_MessageHeader *message)
304 const struct SendMessageRequest *req;
305 struct GSC_Client *c;
306 struct GSC_ClientActiveRequest *car;
308 req = (const struct SendMessageRequest *) message;
309 c = find_client (client);
312 /* client did not send INIT first! */
314 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
317 if (c->requests == NULL)
318 c->requests = GNUNET_CONTAINER_multihashmap_create (16);
319 car = GNUNET_CONTAINER_multihashmap_get (c->requests, &req->peer.hashPubKey);
322 /* create new entry */
323 car = GNUNET_malloc (sizeof (struct GSC_ClientActiveRequest));
324 GNUNET_assert (GNUNET_OK ==
325 GNUNET_CONTAINER_multihashmap_put (c->requests,
326 &req->peer.hashPubKey,
328 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
331 car->target = req->peer;
332 GNUNET_SERVER_client_keep (client);
333 car->client_handle = client;
334 car->deadline = GNUNET_TIME_absolute_ntoh (req->deadline);
335 car->priority = ntohl (req->priority);
336 car->msize = ntohs (req->size);
337 car->smr_id = req->smr_id;
339 memcmp (&req->peer, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity)))
340 GSC_CLIENTS_solicit_request (car);
342 GSC_SESSIONS_queue_request (car);
343 GNUNET_SERVER_receive_done (client, GNUNET_OK);
348 * Handle CORE_SEND request.
351 * @param client the client issuing the request
352 * @param message the "struct SendMessage"
355 handle_client_send (void *cls, struct GNUNET_SERVER_Client *client,
356 const struct GNUNET_MessageHeader *message)
358 const struct SendMessage *sm;
359 struct GSC_Client *c;
360 struct GSC_ClientActiveRequest *car;
363 msize = ntohs (message->size);
365 sizeof (struct SendMessage) + sizeof (struct GNUNET_MessageHeader))
368 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
371 sm = (const struct SendMessage *) message;
372 msize -= sizeof (struct SendMessage);
373 GNUNET_break (0 == ntohl (sm->reserved));
374 c = find_client (client);
377 /* client did not send INIT first! */
379 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
382 car = GNUNET_CONTAINER_multihashmap_get (c->requests, &sm->peer.hashPubKey);
385 /* client did not request transmission first! */
387 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
390 GNUNET_assert (GNUNET_YES ==
391 GNUNET_CONTAINER_multihashmap_remove (c->requests,
392 &sm->peer.hashPubKey,
394 GNUNET_SERVER_mst_receive (client_mst,
400 memcmp (&car->peer, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity)))
401 GSC_SESSIONS_dequeue_request (car);
403 GNUNET_SERVER_receive_done (client, GNUNET_OK);
408 * Functions with this signature are called whenever a complete
409 * message is received by the tokenizer. Used by the 'client_mst' for
410 * dispatching messages from clients to either the SESSION subsystem
411 * or other CLIENT (for loopback).
414 * @param client reservation request ('struct GSC_ClientActiveRequest')
415 * @param message the actual message
418 client_tokenizer_callback (void *cls, void *client,
419 const struct GNUNET_MessageHeader *message)
421 struct GSC_ClientActiveRequest *car = client;
424 memcmp (&car->peer, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity)))
425 GDS_CLIENTS_deliver_message (&GSC_my_identity, &payload->header);
427 GSC_SESSIONS_transmit (car, &payload->header);
432 * Free client request records.
435 * @param key identity of peer for which this is an active request
436 * @param value the 'struct GSC_ClientActiveRequest' to free
437 * @return GNUNET_YES (continue iteration)
440 destroy_active_client_request (void *cls, const GNUNET_HashCode * key,
443 struct GSC_ClientActiveRequest *car = value;
445 GNUNET_assert (GNUNET_YES ==
446 GNUNET_CONTAINER_multihashmap_remove (car->client->requests,
449 GSC_SESSIONS_dequeue_request (car);
456 * A client disconnected, clean up.
459 * @param client identification of the client
462 handle_client_disconnect (void *cls,
463 struct GNUNET_SERVER_Client *client)
465 struct GSC_Client *c;
469 #if DEBUG_CORE_CLIENT
470 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
471 "Client %p has disconnected from core service.\n", client);
473 c = find_client (client);
475 return; /* client never sent INIT */
476 GNUNET_CONTAINER_DLL_remove (client_head,
479 if (c->requests != NULL)
481 GNUNET_CONTAINER_multihashmap_iterate (c->requests,
482 &destroy_active_client_request,
484 GNUNET_CONTAINER_multihashmap_destroy (c->requests);
486 GSC_TYPEMAP_remove (c->types, c->tcnt);
492 * Tell a client that we are ready to receive the message.
494 * @param car request that is now ready; the responsibility
495 * for the handle remains shared between CLIENTS
496 * and SESSIONS after this call.
499 GSC_CLIENTS_solicit_request (struct GSC_ClientActiveRequest *car)
501 struct GSC_Client *c;
502 struct SendMessageReady smr;
505 smr.header.size = htons (sizeof (struct SendMessageReady));
506 smr.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SEND_READY);
507 smr.size = htons (car->msize);
508 smr.smr_id = car->smr_id;
510 send_to_client (c, &smr.header, GNUNET_NO);
515 * Tell a client that we will never be ready to receive the
516 * given message in time (disconnect or timeout).
518 * @param car request that now permanently failed; the
519 * responsibility for the handle is now returned
520 * to CLIENTS (SESSIONS is done with it).
523 GSC_CLIENTS_reject_request (struct GSC_ClientActiveRequest *car)
525 GNUNET_assert (GNUNET_YES ==
526 destroy_active_client_request (NULL, &car->peer.hashPubKey, car));
531 * Notify a particular client about a change to existing connection to
532 * one of our neighbours (check if the client is interested). Called
533 * from 'GSC_SESSIONS_notify_client_about_sessions'.
535 * @param client client to notify
536 * @param neighbour identity of the neighbour that changed status
537 * @param atsi performance information about neighbour
538 * @param atsi_count number of entries in 'ats' array
539 * @param tmap_old previous type map for the neighbour, NULL for disconnect
540 * @param tmap_new updated type map for the neighbour, NULL for disconnect
543 GDS_CLIENTS_notify_client_about_neighbour (struct GSC_Client *client,
544 const struct GNUNET_PeerIdentity *neighbour,
545 const struct GNUNET_TRANSPORT_ATS_Information *atsi,
546 unsigned int atsi_count,
547 const struct GSC_TypeMap *tmap_old,
548 const struct GSC_TypeMap *tmap_new)
550 struct ConnectNotifyMessage *cnm;
552 char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
553 struct GNUNET_TRANSPORT_ATS_Information *a;
554 struct DisconnectNotifyMessage dcm;
558 old_match = GSC_TYPEMAP_test_match (tmap_old, client->types, client->tcnt);
559 new_match = GSC_TYPEMAP_test_match (tmap_new, client->types, client->tcnt);
560 if (old_match == new_match)
561 return; /* no change */
562 if (old_match == GNUNET_NO)
566 sizeof (struct ConnectNotifyMessage) +
567 (n->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information);
568 if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
571 /* recovery strategy: throw away performance data */
572 GNUNET_array_grow (n->ats, n->ats_count, 0);
574 sizeof (struct ConnectNotifyMessage) +
575 (n->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information);
577 cnm = (struct ConnectNotifyMessage *) buf;
578 cnm->header.size = htons (size);
579 cnm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
580 cnm->ats_count = htonl (atsi);
583 sizeof (struct GNUNET_TRANSPORT_ATS_Information) * atsi_count);
584 a[ats_count].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
585 a[ats_count].value = htonl (0);
586 #if DEBUG_CORE_CLIENT
587 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
588 "Sending `%s' message to client.\n",
592 send_to_client (client, &cnm->header, GNUNET_NO);
596 /* send disconnect */
597 dcm.header.size = htons (sizeof (struct DisconnectNotifyMessage));
598 dcm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT);
599 dcm.reserved = htonl (0);
601 send_to_client (client, &cnm.header, GNUNET_NO);
607 * Notify all clients about a change to existing session.
608 * Called from SESSIONS whenever there is a change in sessions
609 * or types processed by the respective peer.
611 * @param neighbour identity of the neighbour that changed status
612 * @param atsi performance information about neighbour
613 * @param atsi_count number of entries in 'ats' array
614 * @param tmap_old previous type map for the neighbour, NULL for disconnect
615 * @param tmap_new updated type map for the neighbour, NULL for disconnect
618 GDS_CLIENTS_notify_clients_about_neighbour (const struct GNUNET_PeerIdentity *neighbour,
619 const struct GNUNET_TRANSPORT_ATS_Information *atsi,
620 unsigned int atsi_count,
621 const struct GSC_TypeMap *tmap_old,
622 const struct GSC_TypeMap *tmap_new)
624 struct GSC_Client *c;
626 for (c = client_head; c != NULL; c = c->next)
627 GDS_CLIENTS_notify_client_about_neighbour (c, neighbour, atsi,
634 * Deliver P2P message to interested clients. Caller must have checked
635 * that the sending peer actually lists the given message type as one
638 * @param sender peer who sent us the message
639 * @param atsi performance information about neighbour
640 * @param atsi_count number of entries in 'ats' array
641 * @param msg the message
642 * @param msize number of bytes to transmit
643 * @param options options for checking which clients should
644 * receive the message
647 GSC_CLIENTS_deliver_message (const struct GNUNET_PeerIdentity *sender,
648 const struct GNUNET_TRANSPORT_ATS_Information *atsi,
649 unsigned int atsi_count,
650 const struct GNUNET_MessageHeader *msg,
654 size_t size = msize + sizeof (struct NotifyTrafficMessage) +
655 (sender->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information);
657 struct NotifyTrafficMessage *ntm;
658 struct GNUNET_TRANSPORT_ATS_Information *a;
663 GNUNET_snprintf (buf, sizeof (buf),
664 gettext_noop ("# bytes of messages of type %u received"),
665 (unsigned int) ntohs (msg->type));
666 GNUNET_STATISTICS_update (stats, buf, msize, GNUNET_NO);
668 GNUNET_assert (GNUNET_YES == sender->is_connected);
669 GNUNET_break (sender->status == PEER_STATE_KEY_CONFIRMED);
670 if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
673 /* recovery strategy: throw performance data away... */
674 GNUNET_array_grow (sender->ats, sender->ats_count, 0);
676 msize + sizeof (struct NotifyTrafficMessage) +
677 (sender->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information);
680 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
681 "Core service passes message from `%4s' of type %u to client.\n",
683 (unsigned int) ntohs (msg->type));
685 ntm = (struct NotifyTrafficMessage *) buf;
686 ntm->header.size = htons (size);
687 ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND);
688 ntm->ats_count = htonl (atsi_count);
689 ntm->peer = sender->peer;
692 sizeof (struct GNUNET_TRANSPORT_ATS_Information) * sender->atsi_count);
693 a[atsi_count].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
694 a[atsi_count].value = htonl (0);
695 memcpy (&ats[atsi_count + 1], msg, msize);
696 send_to_all_clients (&ntm->header, GNUNET_YES,
697 options, ntohs (msg->type));
702 * Initialize clients subsystem.
704 * @param server handle to server clients connect to
707 GSC_CLIENTS_init (struct GNUNET_SERVER_Handle *server)
709 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
710 {&handle_client_init, NULL,
711 GNUNET_MESSAGE_TYPE_CORE_INIT, 0},
712 {&GSC_SESSIONS_handle_client_iterate_peers, NULL,
713 GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS,
714 sizeof (struct GNUNET_MessageHeader)},
715 {&GSC_SESSIONS_handle_client_have_peer, NULL,
716 GNUNET_MESSAGE_TYPE_CORE_PEER_CONNECTED,
717 sizeof (struct GNUNET_MessageHeader) +
718 sizeof (struct GNUNET_PeerIdentity)},
719 {&GSC_SESSIONS_handle_client_request_info, NULL,
720 GNUNET_MESSAGE_TYPE_CORE_REQUEST_INFO,
721 sizeof (struct RequestInfoMessage)},
722 {&handle_client_send_request, NULL,
723 GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST,
724 sizeof (struct SendMessageRequest)},
725 {&handle_client_send, NULL,
726 GNUNET_MESSAGE_TYPE_CORE_SEND, 0},
730 /* setup notification */
731 client_mst = GNUNET_SERVER_mst_create (&client_tokenizer_callback, NULL);
733 GNUNET_SERVER_notification_context_create (server, MAX_NOTIFY_QUEUE);
734 GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
735 GNUNET_SERVER_add_handlers (server, handlers);
740 * Shutdown clients subsystem.
745 struct GSC_Client *c;
747 while (NULL != (c = client_head))
748 handle_client_disconnect (NULL, c->client_handle);
749 GNUNET_SERVER_notification_context_destroy (notifier);
751 GNUNET_SERVER_MST_destroy (client_mst);
755 /* end of gnunet-service-core_clients.c */