diff options
Diffstat (limited to 'src/core/gnunet-service-core_clients.c')
-rw-r--r-- | src/core/gnunet-service-core_clients.c | 887 |
1 files changed, 887 insertions, 0 deletions
diff --git a/src/core/gnunet-service-core_clients.c b/src/core/gnunet-service-core_clients.c new file mode 100644 index 0000000..4098b45 --- /dev/null +++ b/src/core/gnunet-service-core_clients.c @@ -0,0 +1,887 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file core/gnunet-service-core_clients.c + * @brief code for managing interactions with clients of core service + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_transport_service.h" +#include "gnunet-service-core.h" +#include "gnunet-service-core_clients.h" +#include "gnunet-service-core_sessions.h" +#include "gnunet-service-core_typemap.h" +#include "core.h" + + +/** + * How many messages do we queue up at most for optional + * notifications to a client? (this can cause notifications + * about outgoing messages to be dropped). + */ +#define MAX_NOTIFY_QUEUE 1024 + + +/** + * Data structure for each client connected to the core service. + */ +struct GSC_Client +{ + /** + * Clients are kept in a linked list. + */ + struct GSC_Client *next; + + /** + * Clients are kept in a linked list. + */ + struct GSC_Client *prev; + + /** + * Handle for the client with the server API. + */ + struct GNUNET_SERVER_Client *client_handle; + + /** + * Array of the types of messages this peer cares + * about (with "tcnt" entries). Allocated as part + * of this client struct, do not free! + */ + const uint16_t *types; + + /** + * Map of peer identities to active transmission requests of this + * client to the peer (of type 'struct GSC_ClientActiveRequest'). + */ + struct GNUNET_CONTAINER_MultiHashMap *requests; + + /** + * Map containing all peers that this client knows we're connected to. + */ + struct GNUNET_CONTAINER_MultiHashMap *connectmap; + + /** + * Options for messages this client cares about, + * see GNUNET_CORE_OPTION_ values. + */ + uint32_t options; + + /** + * Number of types of incoming messages this client + * specifically cares about. Size of the "types" array. + */ + unsigned int tcnt; + +}; + + +/** + * Head of linked list of our clients. + */ +static struct GSC_Client *client_head; + +/** + * Tail of linked list of our clients. + */ +static struct GSC_Client *client_tail; + +/** + * Context for notifications we need to send to our clients. + */ +static struct GNUNET_SERVER_NotificationContext *notifier; + +/** + * Tokenizer for messages received from clients. + */ +static struct GNUNET_SERVER_MessageStreamTokenizer *client_mst; + + +/** + * Lookup our client struct given the server's client handle. + * + * @param client server client handle to look up + * @return our client handle for the client + */ +static struct GSC_Client * +find_client (struct GNUNET_SERVER_Client *client) +{ + struct GSC_Client *c; + + c = client_head; + while ((c != NULL) && (c->client_handle != client)) + c = c->next; + return c; +} + + +/** + * Send a message to one of our clients. + * + * @param client target for the message + * @param msg message to transmit + * @param can_drop could this message be dropped if the + * client's queue is getting too large? + */ +static void +send_to_client (struct GSC_Client *client, + const struct GNUNET_MessageHeader *msg, int can_drop) +{ +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Preparing to send %u bytes of message of type %u to client.\n", + (unsigned int) ntohs (msg->size), + (unsigned int) ntohs (msg->type)); +#endif + GNUNET_SERVER_notification_context_unicast (notifier, client->client_handle, + msg, can_drop); +} + + +/** + * Send a message to one of our clients. + * + * @param client target for the message + * @param msg message to transmit + * @param can_drop could this message be dropped if the + * client's queue is getting too large? + */ +void +GSC_CLIENTS_send_to_client (struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *msg, + int can_drop) +{ + struct GSC_Client *c; + + c = find_client (client); + if (NULL == c) + { + GNUNET_break (0); + return; + } + send_to_client (c, msg, can_drop); +} + + +/** + * Test if the client is interested in messages of the given type. + * + * @param type message type + * @param c client to test + * @return GNUNET_YES if 'c' is interested, GNUNET_NO if not. + */ +static int +type_match (uint16_t type, struct GSC_Client *c) +{ + unsigned int i; + + if (c->tcnt == 0) + return GNUNET_YES; /* peer without handlers matches ALL */ + for (i = 0; i < c->tcnt; i++) + if (type == c->types[i]) + return GNUNET_YES; + return GNUNET_NO; +} + + +/** + * Send a message to all of our current clients that have the right + * options set. + * + * @param sender origin of the message (used to check that this peer is + * known to be connected to the respective client) + * @param msg message to multicast + * @param can_drop can this message be discarded if the queue is too long + * @param options mask to use + * @param type type of the embedded message, 0 for none + */ +static void +send_to_all_clients (const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_MessageHeader *msg, int can_drop, + int options, uint16_t type) +{ + struct GSC_Client *c; + + for (c = client_head; c != NULL; c = c->next) + { + if ((0 == (options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) && + (GNUNET_YES == type_match (type, c))) + continue; /* not the full message, but we'd like the full one! */ + if ((0 == (c->options & options)) && (GNUNET_YES != type_match (type, c))) + continue; /* neither options nor type match permit the message */ +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending message to client interested in messages of type %u.\n", + (unsigned int) type); +#endif + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_contains (c->connectmap, + &sender->hashPubKey)); + send_to_client (c, msg, can_drop); + } +} + + +/** + * Handle CORE_INIT request. + * + * @param cls unused + * @param client new client that sent INIT + * @param message the 'struct InitMessage' (presumably) + */ +static void +handle_client_init (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct InitMessage *im; + struct InitReplyMessage irm; + struct GSC_Client *c; + uint16_t msize; + const uint16_t *types; + uint16_t *wtypes; + unsigned int i; + + /* check that we don't have an entry already */ + c = find_client (client); + if (NULL != c) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + msize = ntohs (message->size); + if (msize < sizeof (struct InitMessage)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_SERVER_notification_context_add (notifier, client); + im = (const struct InitMessage *) message; + types = (const uint16_t *) &im[1]; + msize -= sizeof (struct InitMessage); + c = GNUNET_malloc (sizeof (struct GSC_Client) + msize); + c->client_handle = client; + c->tcnt = msize / sizeof (uint16_t); + c->options = ntohl (im->options); + c->types = (const uint16_t *) &c[1]; + c->connectmap = GNUNET_CONTAINER_multihashmap_create (16); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_put (c->connectmap, + &GSC_my_identity.hashPubKey, + NULL, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + wtypes = (uint16_t *) & c[1]; + for (i = 0; i < c->tcnt; i++) + wtypes[i] = ntohs (types[i]); + GSC_TYPEMAP_add (wtypes, c->tcnt); + GNUNET_CONTAINER_DLL_insert (client_head, client_tail, c); +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client connecting to core service is interested in %u message types\n", + (unsigned int) c->tcnt); +#endif + /* send init reply message */ + irm.header.size = htons (sizeof (struct InitReplyMessage)); + irm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY); + irm.reserved = htonl (0); + irm.my_identity = GSC_my_identity; + send_to_client (c, &irm.header, GNUNET_NO); + GSC_SESSIONS_notify_client_about_sessions (c); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Handle CORE_SEND_REQUEST message. + * + * @param cls unused + * @param client new client that sent CORE_SEND_REQUEST + * @param message the 'struct SendMessageRequest' (presumably) + */ +static void +handle_client_send_request (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct SendMessageRequest *req; + struct GSC_Client *c; + struct GSC_ClientActiveRequest *car; + int is_loopback; + + req = (const struct SendMessageRequest *) message; + c = find_client (client); + if (c == NULL) + { + /* client did not send INIT first! */ + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + if (c->requests == NULL) + c->requests = GNUNET_CONTAINER_multihashmap_create (16); +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client asked for transmission to `%s'\n", + GNUNET_i2s (&req->peer)); +#endif + is_loopback = + (0 == + memcmp (&req->peer, &GSC_my_identity, + sizeof (struct GNUNET_PeerIdentity))); + if ((!is_loopback) && + (GNUNET_YES != + GNUNET_CONTAINER_multihashmap_contains (c->connectmap, + &req->peer.hashPubKey))) + { + /* neighbour must have disconnected since request was issued, + * ignore (client will realize it once it processes the + * disconnect notification) */ + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop + ("# send requests dropped (disconnected)"), 1, + GNUNET_NO); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + + car = GNUNET_CONTAINER_multihashmap_get (c->requests, &req->peer.hashPubKey); + if (car == NULL) + { + /* create new entry */ + car = GNUNET_malloc (sizeof (struct GSC_ClientActiveRequest)); + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (c->requests, + &req->peer.hashPubKey, + car, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); + car->client_handle = c; + } + else + { + GSC_SESSIONS_dequeue_request (car); + } + car->target = req->peer; + car->deadline = GNUNET_TIME_absolute_ntoh (req->deadline); + car->priority = ntohl (req->priority); + car->msize = ntohs (req->size); + car->smr_id = req->smr_id; + car->was_solicited = GNUNET_NO; + if (is_loopback) + { + /* loopback, satisfy immediately */ + GSC_CLIENTS_solicit_request (car); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + GSC_SESSIONS_queue_request (car); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Closure for the 'client_tokenizer_callback'. + */ +struct TokenizerContext +{ + + /** + * Active request handle for the message. + */ + struct GSC_ClientActiveRequest *car; + + /** + * Is corking allowed (set only once we have the real message). + */ + int cork; + +}; + + +/** + * Handle CORE_SEND request. + * + * @param cls unused + * @param client the client issuing the request + * @param message the "struct SendMessage" + */ +static void +handle_client_send (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct SendMessage *sm; + struct GSC_Client *c; + struct TokenizerContext tc; + uint16_t msize; + + msize = ntohs (message->size); + if (msize < + sizeof (struct SendMessage) + sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + sm = (const struct SendMessage *) message; + msize -= sizeof (struct SendMessage); + GNUNET_break (0 == ntohl (sm->reserved)); + c = find_client (client); + if (c == NULL) + { + /* client did not send INIT first! */ + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + tc.car = + GNUNET_CONTAINER_multihashmap_get (c->requests, &sm->peer.hashPubKey); + if (NULL == tc.car) + { + /* Must have been that we first approved the request, then got disconnected + * (which triggered removal of the 'car') and now the client gives us a message + * just *before* the client learns about the disconnect. Theoretically, we + * might also now be *again* connected. So this can happen (but should be + * rare). If it does happen, the message is discarded. */ + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop + ("# messages discarded (session disconnected)"), + 1, GNUNET_NO); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (c->requests, + &sm->peer.hashPubKey, + tc.car)); + tc.cork = ntohl (sm->cork); +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client asked for transmission of %u bytes to `%s' %s\n", msize, + GNUNET_i2s (&sm->peer), tc.cork ? "now" : ""); +#endif + GNUNET_SERVER_mst_receive (client_mst, &tc, (const char *) &sm[1], msize, + GNUNET_YES, GNUNET_NO); + if (0 != + memcmp (&tc.car->target, &GSC_my_identity, + sizeof (struct GNUNET_PeerIdentity))) + GSC_SESSIONS_dequeue_request (tc.car); + GNUNET_free (tc.car); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Functions with this signature are called whenever a complete + * message is received by the tokenizer. Used by the 'client_mst' for + * dispatching messages from clients to either the SESSION subsystem + * or other CLIENT (for loopback). + * + * @param cls closure + * @param client reservation request ('struct GSC_ClientActiveRequest') + * @param message the actual message + */ +static void +client_tokenizer_callback (void *cls, void *client, + const struct GNUNET_MessageHeader *message) +{ + struct TokenizerContext *tc = client; + struct GSC_ClientActiveRequest *car = tc->car; + + if (0 == + memcmp (&car->target, &GSC_my_identity, + sizeof (struct GNUNET_PeerIdentity))) + { +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Delivering message of type %u to myself\n", + ntohs (message->type)); +#endif + GSC_CLIENTS_deliver_message (&GSC_my_identity, NULL, 0, message, + ntohs (message->size), + GNUNET_CORE_OPTION_SEND_FULL_INBOUND | + GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND); + GSC_CLIENTS_deliver_message (&GSC_my_identity, NULL, 0, message, + sizeof (struct GNUNET_MessageHeader), + GNUNET_CORE_OPTION_SEND_HDR_INBOUND | + GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND); + } + else + { +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Delivering message of type %u to %s\n", ntohs (message->type), + GNUNET_i2s (&car->target)); +#endif + GSC_SESSIONS_transmit (car, message, tc->cork); + } +} + + +/** + * Free client request records. + * + * @param cls NULL + * @param key identity of peer for which this is an active request + * @param value the 'struct GSC_ClientActiveRequest' to free + * @return GNUNET_YES (continue iteration) + */ +static int +destroy_active_client_request (void *cls, const GNUNET_HashCode * key, + void *value) +{ + struct GSC_ClientActiveRequest *car = value; + + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (car-> + client_handle->requests, + &car->target.hashPubKey, + car)); + GSC_SESSIONS_dequeue_request (car); + GNUNET_free (car); + return GNUNET_YES; +} + + +/** + * A client disconnected, clean up. + * + * @param cls closure + * @param client identification of the client + */ +static void +handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client) +{ + struct GSC_Client *c; + + if (client == NULL) + return; +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client %p has disconnected from core service.\n", client); +#endif + c = find_client (client); + if (c == NULL) + return; /* client never sent INIT */ + GNUNET_CONTAINER_DLL_remove (client_head, client_tail, c); + if (c->requests != NULL) + { + GNUNET_CONTAINER_multihashmap_iterate (c->requests, + &destroy_active_client_request, + NULL); + GNUNET_CONTAINER_multihashmap_destroy (c->requests); + } + GNUNET_CONTAINER_multihashmap_destroy (c->connectmap); + c->connectmap = NULL; + GSC_TYPEMAP_remove (c->types, c->tcnt); + GNUNET_free (c); +} + + +/** + * Tell a client that we are ready to receive the message. + * + * @param car request that is now ready; the responsibility + * for the handle remains shared between CLIENTS + * and SESSIONS after this call. + */ +void +GSC_CLIENTS_solicit_request (struct GSC_ClientActiveRequest *car) +{ + struct GSC_Client *c; + struct SendMessageReady smr; + + c = car->client_handle; + if (GNUNET_YES != + GNUNET_CONTAINER_multihashmap_contains (c->connectmap, + &car->target.hashPubKey)) + { + /* connection has gone down since, drop request */ + GNUNET_assert (0 != + memcmp (&car->target, &GSC_my_identity, + sizeof (struct GNUNET_PeerIdentity))); + GSC_SESSIONS_dequeue_request (car); + GSC_CLIENTS_reject_request (car); + return; + } + smr.header.size = htons (sizeof (struct SendMessageReady)); + smr.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SEND_READY); + smr.size = htons (car->msize); + smr.smr_id = car->smr_id; + smr.peer = car->target; + send_to_client (c, &smr.header, GNUNET_NO); +} + + +/** + * Tell a client that we will never be ready to receive the + * given message in time (disconnect or timeout). + * + * @param car request that now permanently failed; the + * responsibility for the handle is now returned + * to CLIENTS (SESSIONS is done with it). + */ +void +GSC_CLIENTS_reject_request (struct GSC_ClientActiveRequest *car) +{ + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (car-> + client_handle->requests, + &car->target.hashPubKey, + car)); + GNUNET_free (car); +} + + +/** + * Notify a particular client about a change to existing connection to + * one of our neighbours (check if the client is interested). Called + * from 'GSC_SESSIONS_notify_client_about_sessions'. + * + * @param client client to notify + * @param neighbour identity of the neighbour that changed status + * @param atsi performance information about neighbour + * @param atsi_count number of entries in 'ats' array + * @param tmap_old previous type map for the neighbour, NULL for disconnect + * @param tmap_new updated type map for the neighbour, NULL for disconnect + */ +void +GSC_CLIENTS_notify_client_about_neighbour (struct GSC_Client *client, + const struct GNUNET_PeerIdentity + *neighbour, + const struct GNUNET_ATS_Information + *atsi, unsigned int atsi_count, + const struct GSC_TypeMap *tmap_old, + const struct GSC_TypeMap *tmap_new) +{ + struct ConnectNotifyMessage *cnm; + size_t size; + char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1]; + struct GNUNET_ATS_Information *a; + struct DisconnectNotifyMessage dcm; + int old_match; + int new_match; + + old_match = GSC_TYPEMAP_test_match (tmap_old, client->types, client->tcnt); + new_match = GSC_TYPEMAP_test_match (tmap_new, client->types, client->tcnt); + if (old_match == new_match) + { + GNUNET_assert (old_match == + GNUNET_CONTAINER_multihashmap_contains (client->connectmap, + &neighbour->hashPubKey)); + return; /* no change */ + } + if (old_match == GNUNET_NO) + { + /* send connect */ + GNUNET_assert (GNUNET_NO == + GNUNET_CONTAINER_multihashmap_contains (client->connectmap, + &neighbour->hashPubKey)); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_put (client->connectmap, + &neighbour->hashPubKey, + NULL, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + size = + sizeof (struct ConnectNotifyMessage) + + (atsi_count) * sizeof (struct GNUNET_ATS_Information); + if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE) + { + GNUNET_break (0); + /* recovery strategy: throw away performance data */ + atsi_count = 0; + size = sizeof (struct ConnectNotifyMessage); + } + cnm = (struct ConnectNotifyMessage *) buf; + cnm->header.size = htons (size); + cnm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT); + cnm->ats_count = htonl (atsi_count); + a = (struct GNUNET_ATS_Information *) &cnm[1]; + memcpy (a, atsi, sizeof (struct GNUNET_ATS_Information) * atsi_count); +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message to client.\n", + "NOTIFY_CONNECT"); +#endif + cnm->peer = *neighbour; + send_to_client (client, &cnm->header, GNUNET_NO); + } + else + { + /* send disconnect */ + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_contains (client->connectmap, + &neighbour->hashPubKey)); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (client->connectmap, + &neighbour->hashPubKey, + NULL)); + dcm.header.size = htons (sizeof (struct DisconnectNotifyMessage)); + dcm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT); + dcm.reserved = htonl (0); + dcm.peer = *neighbour; + send_to_client (client, &dcm.header, GNUNET_NO); + } +} + + +/** + * Notify all clients about a change to existing session. + * Called from SESSIONS whenever there is a change in sessions + * or types processed by the respective peer. + * + * @param neighbour identity of the neighbour that changed status + * @param atsi performance information about neighbour + * @param atsi_count number of entries in 'ats' array + * @param tmap_old previous type map for the neighbour, NULL for disconnect + * @param tmap_new updated type map for the neighbour, NULL for disconnect + */ +void +GSC_CLIENTS_notify_clients_about_neighbour (const struct GNUNET_PeerIdentity + *neighbour, + const struct GNUNET_ATS_Information + *atsi, unsigned int atsi_count, + const struct GSC_TypeMap *tmap_old, + const struct GSC_TypeMap *tmap_new) +{ + struct GSC_Client *c; + + for (c = client_head; c != NULL; c = c->next) + GSC_CLIENTS_notify_client_about_neighbour (c, neighbour, atsi, atsi_count, + tmap_old, tmap_new); +} + + +/** + * Deliver P2P message to interested clients. Caller must have checked + * that the sending peer actually lists the given message type as one + * of its types. + * + * @param sender peer who sent us the message + * @param atsi performance information about neighbour + * @param atsi_count number of entries in 'ats' array + * @param msg the message + * @param msize number of bytes to transmit + * @param options options for checking which clients should + * receive the message + */ +void +GSC_CLIENTS_deliver_message (const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count, + const struct GNUNET_MessageHeader *msg, + uint16_t msize, int options) +{ + size_t size = + msize + sizeof (struct NotifyTrafficMessage) + + atsi_count * sizeof (struct GNUNET_ATS_Information); + char buf[size]; + struct NotifyTrafficMessage *ntm; + struct GNUNET_ATS_Information *a; + + if (0 == options) + { + GNUNET_snprintf (buf, sizeof (buf), + gettext_noop ("# bytes of messages of type %u received"), + (unsigned int) ntohs (msg->type)); + GNUNET_STATISTICS_update (GSC_stats, buf, msize, GNUNET_NO); + } + if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE) + { + GNUNET_break (0); + /* recovery strategy: throw performance data away... */ + atsi_count = 0; + size = msize + sizeof (struct NotifyTrafficMessage); + } +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service passes message from `%4s' of type %u to client.\n", + GNUNET_i2s (sender), (unsigned int) ntohs (msg->type)); +#endif + GSC_SESSIONS_add_to_typemap (sender, ntohs (msg->type)); + ntm = (struct NotifyTrafficMessage *) buf; + ntm->header.size = htons (size); + ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND); + ntm->ats_count = htonl (atsi_count); + ntm->peer = *sender; + a = &ntm->ats; + memcpy (a, atsi, sizeof (struct GNUNET_ATS_Information) * atsi_count); + a[atsi_count].type = htonl (GNUNET_ATS_ARRAY_TERMINATOR); + a[atsi_count].value = htonl (0); + memcpy (&a[atsi_count + 1], msg, msize); + send_to_all_clients (sender, &ntm->header, GNUNET_YES, options, + ntohs (msg->type)); +} + + +/** + * Initialize clients subsystem. + * + * @param server handle to server clients connect to + */ +void +GSC_CLIENTS_init (struct GNUNET_SERVER_Handle *server) +{ + static const struct GNUNET_SERVER_MessageHandler handlers[] = { + {&handle_client_init, NULL, + GNUNET_MESSAGE_TYPE_CORE_INIT, 0}, + {&GSC_SESSIONS_handle_client_iterate_peers, NULL, + GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS, + sizeof (struct GNUNET_MessageHeader)}, + {&GSC_SESSIONS_handle_client_have_peer, NULL, + GNUNET_MESSAGE_TYPE_CORE_PEER_CONNECTED, + sizeof (struct GNUNET_MessageHeader) + + sizeof (struct GNUNET_PeerIdentity)}, + {&handle_client_send_request, NULL, + GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST, + sizeof (struct SendMessageRequest)}, + {&handle_client_send, NULL, + GNUNET_MESSAGE_TYPE_CORE_SEND, 0}, + {NULL, NULL, 0, 0} + }; + + /* setup notification */ + client_mst = GNUNET_SERVER_mst_create (&client_tokenizer_callback, NULL); + notifier = + GNUNET_SERVER_notification_context_create (server, MAX_NOTIFY_QUEUE); + GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL); + GNUNET_SERVER_add_handlers (server, handlers); +} + + +/** + * Shutdown clients subsystem. + */ +void +GSC_CLIENTS_done () +{ + struct GSC_Client *c; + + while (NULL != (c = client_head)) + handle_client_disconnect (NULL, c->client_handle); + if (NULL != notifier) + { + GNUNET_SERVER_notification_context_destroy (notifier); + notifier = NULL; + } + GNUNET_SERVER_mst_destroy (client_mst); + client_mst = NULL; +} + +/* end of gnunet-service-core_clients.c */ |