/* This file is part of GNUnet. Copyright (C) 2009--2013, 2016 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ /** * @file dv/dv_api.c * @brief library to access the DV service * @author Christian Grothoff * @author Nathan Evans */ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_dv_service.h" #include "gnunet_protocols.h" #include "dv.h" #include "gnunet_transport_plugin.h" #define LOG(kind,...) GNUNET_log_from (kind, "dv-api",__VA_ARGS__) /** * Information we track for each peer. */ struct ConnectedPeer { /** * Identity of the peer. */ struct GNUNET_PeerIdentity pid; }; /** * Handle to the DV service. */ struct GNUNET_DV_ServiceHandle { /** * Connection to DV service. */ struct GNUNET_MQ_Handle *mq; /** * Our configuration. */ const struct GNUNET_CONFIGURATION_Handle *cfg; /** * Closure for the callbacks. */ void *cls; /** * Function to call on connect events. */ GNUNET_DV_ConnectCallback connect_cb; /** * Function to call on distance change events. */ GNUNET_DV_DistanceChangedCallback distance_cb; /** * Function to call on disconnect events. */ GNUNET_DV_DisconnectCallback disconnect_cb; /** * Function to call on receiving messages events. */ GNUNET_DV_MessageReceivedCallback message_cb; /** * Information tracked per connected peer. Maps peer * identities to `struct ConnectedPeer` entries. */ struct GNUNET_CONTAINER_MultiPeerMap *peers; }; /** * Disconnect and then reconnect to the DV service. * * @param sh service handle */ static void reconnect (struct GNUNET_DV_ServiceHandle *sh); /** * We got disconnected from the service and thus all of the * connections need to be torn down. * * @param cls the `struct GNUNET_DV_ServiceHandle` * @param key a peer identity * @param value a `struct ConnectedPeer` to clean up * @return #GNUNET_OK (continue to iterate) */ static int cleanup_send_cb (void *cls, const struct GNUNET_PeerIdentity *key, void *value) { struct GNUNET_DV_ServiceHandle *sh = cls; struct ConnectedPeer *peer = value; GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multipeermap_remove (sh->peers, key, peer)); sh->disconnect_cb (sh->cls, key); GNUNET_free (peer); return GNUNET_OK; } /** * Handles a message sent from the DV service to us. * Parse it out and give it to the plugin. * * @param cls the handle to the DV API * @param cm the message that was received */ static void handle_connect (void *cls, const struct GNUNET_DV_ConnectMessage *cm) { struct GNUNET_DV_ServiceHandle *sh = cls; struct ConnectedPeer *peer; peer = GNUNET_CONTAINER_multipeermap_get (sh->peers, &cm->peer); if (NULL != peer) { GNUNET_break (0); reconnect (sh); return; } peer = GNUNET_new (struct ConnectedPeer); peer->pid = cm->peer; GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multipeermap_put (sh->peers, &peer->pid, peer, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); sh->connect_cb (sh->cls, &cm->peer, ntohl (cm->distance), (enum GNUNET_NetworkType) ntohl (cm->network)); } /** * Handles a message sent from the DV service to us. * Parse it out and give it to the plugin. * * @param cls the handle to the DV API * @param dm the message that was received */ static void handle_disconnect (void *cls, const struct GNUNET_DV_DisconnectMessage *dm) { struct GNUNET_DV_ServiceHandle *sh = cls; struct ConnectedPeer *peer; peer = GNUNET_CONTAINER_multipeermap_get (sh->peers, &dm->peer); if (NULL == peer) { GNUNET_break (0); reconnect (sh); return; } cleanup_send_cb (sh, &dm->peer, peer); } /** * Handles a message sent from the DV service to us. * Parse it out and give it to the plugin. * * @param cls the handle to the DV API * @param msg the message that was received */ static void handle_distance_update (void *cls, const struct GNUNET_DV_DistanceUpdateMessage *dum) { struct GNUNET_DV_ServiceHandle *sh = cls; struct ConnectedPeer *peer; peer = GNUNET_CONTAINER_multipeermap_get (sh->peers, &dum->peer); if (NULL == peer) { GNUNET_break (0); reconnect (sh); return; } sh->distance_cb (sh->cls, &dum->peer, ntohl (dum->distance), (enum GNUNET_NetworkType) ntohl (dum->network)); } /** * Handles a message sent from the DV service to us. * Parse it out and give it to the plugin. * * @param cls the handle to the DV API * @param rm the message that was received */ static int check_received (void *cls, const struct GNUNET_DV_ReceivedMessage *rm) { struct GNUNET_DV_ServiceHandle *sh = cls; const struct GNUNET_MessageHeader *payload; if (NULL == GNUNET_CONTAINER_multipeermap_get (sh->peers, &rm->sender)) { GNUNET_break (0); return GNUNET_SYSERR; } if (ntohs (rm->header.size) - sizeof (struct GNUNET_DV_ReceivedMessage) < sizeof (*payload)) { GNUNET_break (0); return GNUNET_SYSERR; } payload = (const struct GNUNET_MessageHeader *) &rm[1]; if (ntohs (rm->header.size) != sizeof (struct GNUNET_DV_ReceivedMessage) + ntohs (payload->size)) { GNUNET_break (0); return GNUNET_SYSERR; } return GNUNET_OK; } /** * Handles a message sent from the DV service to us. * Parse it out and give it to the plugin. * * @param cls the handle to the DV API * @param rm the message that was received */ static void handle_received (void *cls, const struct GNUNET_DV_ReceivedMessage *rm) { struct GNUNET_DV_ServiceHandle *sh = cls; const struct GNUNET_MessageHeader *payload; payload = (const struct GNUNET_MessageHeader *) &rm[1]; sh->message_cb (sh->cls, &rm->sender, ntohl (rm->distance), payload); } /** * Generic error handler, called with the appropriate error code and * the same closure specified at the creation of the message queue. * Not every message queue implementation supports an error handler. * * @param cls closure with the `struct GNUNET_DV_ServiceHandle *` * @param error error code */ static void mq_error_handler (void *cls, enum GNUNET_MQ_Error error) { struct GNUNET_DV_ServiceHandle *sh = cls; reconnect (sh); } /** * Disconnect and then reconnect to the DV service. * * @param sh service handle */ static void reconnect (struct GNUNET_DV_ServiceHandle *sh) { struct GNUNET_MQ_MessageHandler handlers[] = { GNUNET_MQ_hd_fixed_size (connect, GNUNET_MESSAGE_TYPE_DV_CONNECT, struct GNUNET_DV_ConnectMessage, sh), GNUNET_MQ_hd_fixed_size (disconnect, GNUNET_MESSAGE_TYPE_DV_DISCONNECT, struct GNUNET_DV_DisconnectMessage, sh), GNUNET_MQ_hd_fixed_size (distance_update, GNUNET_MESSAGE_TYPE_DV_DISTANCE_CHANGED, struct GNUNET_DV_DistanceUpdateMessage, sh), GNUNET_MQ_hd_var_size (received, GNUNET_MESSAGE_TYPE_DV_RECV, struct GNUNET_DV_ReceivedMessage, sh), GNUNET_MQ_handler_end () }; struct GNUNET_MessageHeader *sm; struct GNUNET_MQ_Envelope *env; if (NULL != sh->mq) { GNUNET_MQ_destroy (sh->mq); sh->mq = NULL; } GNUNET_CONTAINER_multipeermap_iterate (sh->peers, &cleanup_send_cb, sh); LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to DV service\n"); sh->mq = GNUNET_CLIENT_connect (sh->cfg, "dv", handlers, &mq_error_handler, sh); if (NULL == sh->mq) { GNUNET_break (0); return; } env = GNUNET_MQ_msg (sm, GNUNET_MESSAGE_TYPE_DV_START); GNUNET_MQ_send (sh->mq, env); } /** * Connect to the DV service. * * @param cfg configuration * @param cls closure for callbacks * @param connect_cb function to call on connects * @param distance_cb function to call if distances change * @param disconnect_cb function to call on disconnects * @param message_cb function to call if we receive messages * @return handle to access the service */ struct GNUNET_DV_ServiceHandle * GNUNET_DV_service_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, void *cls, GNUNET_DV_ConnectCallback connect_cb, GNUNET_DV_DistanceChangedCallback distance_cb, GNUNET_DV_DisconnectCallback disconnect_cb, GNUNET_DV_MessageReceivedCallback message_cb) { struct GNUNET_DV_ServiceHandle *sh; sh = GNUNET_new (struct GNUNET_DV_ServiceHandle); sh->cfg = cfg; sh->cls = cls; sh->connect_cb = connect_cb; sh->distance_cb = distance_cb; sh->disconnect_cb = disconnect_cb; sh->message_cb = message_cb; sh->peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_YES); reconnect (sh); return sh; } /** * Disconnect from DV service. * * @param sh service handle */ void GNUNET_DV_service_disconnect (struct GNUNET_DV_ServiceHandle *sh) { if (NULL == sh) return; if (NULL != sh->mq) { GNUNET_MQ_destroy (sh->mq); sh->mq = NULL; } GNUNET_CONTAINER_multipeermap_iterate (sh->peers, &cleanup_send_cb, sh); GNUNET_CONTAINER_multipeermap_destroy (sh->peers); GNUNET_free (sh); } /** * Send a message via DV service. * * @param sh service handle * @param target intended recpient * @param msg message payload */ void GNUNET_DV_send (struct GNUNET_DV_ServiceHandle *sh, const struct GNUNET_PeerIdentity *target, const struct GNUNET_MessageHeader *msg) { struct GNUNET_DV_SendMessage *sm; struct ConnectedPeer *peer; struct GNUNET_MQ_Envelope *env; if (ntohs (msg->size) + sizeof (*sm) >= GNUNET_MAX_MESSAGE_SIZE) { GNUNET_break (0); return; } LOG (GNUNET_ERROR_TYPE_DEBUG, "Asked to send %u bytes of type %u to %s\n", (unsigned int) ntohs (msg->size), (unsigned int) ntohs (msg->type), GNUNET_i2s (target)); peer = GNUNET_CONTAINER_multipeermap_get (sh->peers, target); if (NULL == peer) { GNUNET_break (0); return; } GNUNET_assert (NULL != sh->mq); env = GNUNET_MQ_msg_nested_mh (sm, GNUNET_MESSAGE_TYPE_DV_SEND, msg); sm->target = *target; GNUNET_MQ_send (sh->mq, env); } /* end of dv_api.c */