/* 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 . SPDX-License-Identifier: AGPL3.0-or-later */ /** * @file transport/transport_api_manipulation.c * @brief library to access the low-level P2P IO service * @author Christian Grothoff */ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_constants.h" #include "gnunet_arm_service.h" #include "gnunet_hello_lib.h" #include "gnunet_protocols.h" #include "gnunet_transport_service.h" #include "transport.h" #define LOG(kind, ...) GNUNET_log_from (kind, "transport-api", __VA_ARGS__) /** * Handle for the transport service (includes all of the * state for the transport service). */ struct GNUNET_TRANSPORT_ManipulationHandle { /** * My client connection to the transport service. */ struct GNUNET_MQ_Handle *mq; /** * My configuration. */ const struct GNUNET_CONFIGURATION_Handle *cfg; /** * ID of the task trying to reconnect to the service. */ struct GNUNET_SCHEDULER_Task *reconnect_task; /** * Delay until we try to reconnect. */ struct GNUNET_TIME_Relative reconnect_delay; /** * Reconnect in progress */ int reconnecting; }; /** * Function that will schedule the job that will try * to connect us again to the client. * * @param h transport service to reconnect */ static void disconnect_and_schedule_reconnect (struct GNUNET_TRANSPORT_ManipulationHandle *h); /** * 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_TRANSPORT_ManipulationHandle *` * @param error error code */ static void mq_error_handler (void *cls, enum GNUNET_MQ_Error error) { struct GNUNET_TRANSPORT_ManipulationHandle *h = cls; LOG (GNUNET_ERROR_TYPE_DEBUG, "Error receiving from transport service, disconnecting temporarily.\n"); h->reconnecting = GNUNET_YES; disconnect_and_schedule_reconnect (h); } /** * Try again to connect to transport service. * * @param cls the handle to the transport service */ static void reconnect (void *cls) { struct GNUNET_TRANSPORT_ManipulationHandle *h = cls; struct GNUNET_MQ_MessageHandler handlers[] = { GNUNET_MQ_handler_end () }; struct GNUNET_MQ_Envelope *env; struct StartMessage *s; h->reconnect_task = NULL; LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to transport service.\n"); GNUNET_assert (NULL == h->mq); h->reconnecting = GNUNET_NO; h->mq = GNUNET_CLIENT_connect (h->cfg, "transport", handlers, &mq_error_handler, h); if (NULL == h->mq) return; env = GNUNET_MQ_msg (s, GNUNET_MESSAGE_TYPE_TRANSPORT_START); GNUNET_MQ_send (h->mq, env); } /** * Function that will schedule the job that will try * to connect us again to the client. * * @param h transport service to reconnect */ static void disconnect_and_schedule_reconnect (struct GNUNET_TRANSPORT_ManipulationHandle *h) { GNUNET_assert (NULL == h->reconnect_task); if (NULL != h->mq) { GNUNET_MQ_destroy (h->mq); h->mq = NULL; } h->reconnect_task = GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h); h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay); } /** * Set transport metrics for a peer and a direction. * * @param handle transport handle * @param peer the peer to set the metric for * @param prop the performance metrics to set * @param delay_in inbound delay to introduce * @param delay_out outbound delay to introduce * * Note: Delay restrictions in receiving direction will be enforced * with one message delay. */ void GNUNET_TRANSPORT_manipulation_set (struct GNUNET_TRANSPORT_ManipulationHandle *handle, const struct GNUNET_PeerIdentity *peer, const struct GNUNET_ATS_Properties *prop, struct GNUNET_TIME_Relative delay_in, struct GNUNET_TIME_Relative delay_out) { struct GNUNET_MQ_Envelope *env; struct TrafficMetricMessage *msg; if (NULL == handle->mq) return; env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_TRANSPORT_TRAFFIC_METRIC); msg->reserved = htonl (0); msg->peer = *peer; GNUNET_ATS_properties_hton (&msg->properties, prop); msg->delay_in = GNUNET_TIME_relative_hton (delay_in); msg->delay_out = GNUNET_TIME_relative_hton (delay_out); GNUNET_MQ_send (handle->mq, env); } /** * Connect to the transport service. Note that the connection may * complete (or fail) asynchronously. * * @param cfg configuration to use * @return NULL on error */ struct GNUNET_TRANSPORT_ManipulationHandle * GNUNET_TRANSPORT_manipulation_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) { struct GNUNET_TRANSPORT_ManipulationHandle *h; h = GNUNET_new (struct GNUNET_TRANSPORT_ManipulationHandle); h->cfg = cfg; LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to transport service.\n"); reconnect (h); if (NULL == h->mq) { GNUNET_free (h); return NULL; } return h; } /** * Disconnect from the transport service. * * @param handle handle to the service as returned from #GNUNET_TRANSPORT_manipulation_connect() */ void GNUNET_TRANSPORT_manipulation_disconnect (struct GNUNET_TRANSPORT_ManipulationHandle * handle) { if (NULL == handle->reconnect_task) disconnect_and_schedule_reconnect (handle); /* and now we stop trying to connect again... */ if (NULL != handle->reconnect_task) { GNUNET_SCHEDULER_cancel (handle->reconnect_task); handle->reconnect_task = NULL; } GNUNET_free (handle); } /* end of transport_api_manipulation.c */