|
@@ -0,0 +1,366 @@
|
|
|
+/*
|
|
|
+ This file is part of GNUnet.
|
|
|
+ Copyright (C) 2010--2019 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 <http://www.gnu.org/licenses/>.
|
|
|
+
|
|
|
+ SPDX-License-Identifier: AGPL3.0-or-later
|
|
|
+*/
|
|
|
+/**
|
|
|
+ * @file transport/transport_api2_application.c
|
|
|
+ * @brief enable clients to ask TRANSPORT about establishing connections to peers
|
|
|
+ * @author Christian Grothoff
|
|
|
+ * @author Matthias Wachs
|
|
|
+ */
|
|
|
+#include "platform.h"
|
|
|
+#include "gnunet_transport_application_service.h"
|
|
|
+#include "gnunet_transport_core_service.h"
|
|
|
+#include "transport.h"
|
|
|
+
|
|
|
+
|
|
|
+#define LOG(kind,...) GNUNET_log_from(kind, "transport-application-api", __VA_ARGS__)
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * Handle for TRANSPORT address suggestion requests.
|
|
|
+ */
|
|
|
+struct GNUNET_TRANSPORT_ApplicationSuggestHandle
|
|
|
+{
|
|
|
+ /**
|
|
|
+ * ID of the peer for which address suggestion was requested.
|
|
|
+ */
|
|
|
+ struct GNUNET_PeerIdentity id;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Connecitivity handle this suggestion handle belongs to.
|
|
|
+ */
|
|
|
+ struct GNUNET_TRANSPORT_ApplicationHandle *ch;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * What preference is being expressed?
|
|
|
+ */
|
|
|
+ enum GNUNET_MQ_PreferenceKind pk;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * How much bandwidth does the client expect?
|
|
|
+ */
|
|
|
+ struct GNUNET_BANDWIDTH_Value32NBO bw;
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * Handle to the TRANSPORT subsystem for application management.
|
|
|
+ */
|
|
|
+struct GNUNET_TRANSPORT_ApplicationHandle
|
|
|
+{
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Our configuration.
|
|
|
+ */
|
|
|
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Map with the identities of all the peers for which we would
|
|
|
+ * like to have address suggestions. The key is the PID, the
|
|
|
+ * value is currently the `struct GNUNET_TRANSPORT_ApplicationSuggestHandle`
|
|
|
+ */
|
|
|
+ struct GNUNET_CONTAINER_MultiPeerMap *sug_requests;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Message queue for sending requests to the TRANSPORT service.
|
|
|
+ */
|
|
|
+ struct GNUNET_MQ_Handle *mq;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Task to trigger reconnect.
|
|
|
+ */
|
|
|
+ struct GNUNET_SCHEDULER_Task *task;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Reconnect backoff delay.
|
|
|
+ */
|
|
|
+ struct GNUNET_TIME_Relative backoff;
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * Re-establish the connection to the TRANSPORT service.
|
|
|
+ *
|
|
|
+ * @param ch handle to use to re-connect.
|
|
|
+ */
|
|
|
+static void
|
|
|
+reconnect (struct GNUNET_TRANSPORT_ApplicationHandle *ch);
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * Re-establish the connection to the TRANSPORT service.
|
|
|
+ *
|
|
|
+ * @param cls handle to use to re-connect.
|
|
|
+ */
|
|
|
+static void
|
|
|
+reconnect_task (void *cls)
|
|
|
+{
|
|
|
+ struct GNUNET_TRANSPORT_ApplicationHandle *ch = cls;
|
|
|
+
|
|
|
+ ch->task = NULL;
|
|
|
+ reconnect (ch);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * Disconnect from TRANSPORT and then reconnect.
|
|
|
+ *
|
|
|
+ * @param ch our handle
|
|
|
+ */
|
|
|
+static void
|
|
|
+force_reconnect (struct GNUNET_TRANSPORT_ApplicationHandle *ch)
|
|
|
+{
|
|
|
+ if (NULL != ch->mq)
|
|
|
+ {
|
|
|
+ GNUNET_MQ_destroy (ch->mq);
|
|
|
+ ch->mq = NULL;
|
|
|
+ }
|
|
|
+ ch->backoff = GNUNET_TIME_STD_BACKOFF (ch->backoff);
|
|
|
+ ch->task = GNUNET_SCHEDULER_add_delayed (ch->backoff,
|
|
|
+ &reconnect_task,
|
|
|
+ ch);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * We encountered an error handling the MQ to the
|
|
|
+ * TRANSPORT service. Reconnect.
|
|
|
+ *
|
|
|
+ * @param cls the `struct GNUNET_TRANSPORT_ApplicationHandle`
|
|
|
+ * @param error details about the error
|
|
|
+ */
|
|
|
+static void
|
|
|
+error_handler (void *cls,
|
|
|
+ enum GNUNET_MQ_Error error)
|
|
|
+{
|
|
|
+ struct GNUNET_TRANSPORT_ApplicationHandle *ch = cls;
|
|
|
+
|
|
|
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
+ "TRANSPORT connection died (code %d), reconnecting\n",
|
|
|
+ (int) error);
|
|
|
+ force_reconnect (ch);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * Transmit request for an address suggestion.
|
|
|
+ *
|
|
|
+ * @param cls the `struct GNUNET_TRANSPORT_ApplicationHandle`
|
|
|
+ * @param peer peer to ask for an address suggestion for
|
|
|
+ * @param value the `struct GNUNET_TRANSPORT_SuggestHandle`
|
|
|
+ * @return #GNUNET_OK (continue to iterate), #GNUNET_SYSERR on
|
|
|
+ * failure (message queue no longer exists)
|
|
|
+ */
|
|
|
+static int
|
|
|
+transmit_suggestion (void *cls,
|
|
|
+ const struct GNUNET_PeerIdentity *peer,
|
|
|
+ void *value)
|
|
|
+{
|
|
|
+ struct GNUNET_TRANSPORT_ApplicationHandle *ch = cls;
|
|
|
+ struct GNUNET_TRANSPORT_ApplicationSuggestHandle *sh = value;
|
|
|
+ struct GNUNET_MQ_Envelope *ev;
|
|
|
+ struct ExpressPreferenceMessage *m;
|
|
|
+
|
|
|
+ if (NULL == ch->mq)
|
|
|
+ return GNUNET_SYSERR;
|
|
|
+ ev = GNUNET_MQ_msg (m,
|
|
|
+ GNUNET_MESSAGE_TYPE_TRANSPORT_SUGGEST);
|
|
|
+ m->pk = htonl ((uint32_t) sh->pk);
|
|
|
+ m->bw = sh->bw;
|
|
|
+ m->peer = *peer;
|
|
|
+ GNUNET_MQ_send (ch->mq, ev);
|
|
|
+ return GNUNET_OK;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * Re-establish the connection to the TRANSPORT service.
|
|
|
+ *
|
|
|
+ * @param ch handle to use to re-connect.
|
|
|
+ */
|
|
|
+static void
|
|
|
+reconnect (struct GNUNET_TRANSPORT_ApplicationHandle *ch)
|
|
|
+{
|
|
|
+ static const struct GNUNET_MQ_MessageHandler handlers[] = {
|
|
|
+ { NULL, 0, 0 }
|
|
|
+ };
|
|
|
+
|
|
|
+ GNUNET_assert (NULL == ch->mq);
|
|
|
+ ch->mq = GNUNET_CLIENT_connect (ch->cfg,
|
|
|
+ "transport",
|
|
|
+ handlers,
|
|
|
+ &error_handler,
|
|
|
+ ch);
|
|
|
+ if (NULL == ch->mq)
|
|
|
+ {
|
|
|
+ force_reconnect (ch);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests,
|
|
|
+ &transmit_suggestion,
|
|
|
+ ch);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * Initialize the TRANSPORT application suggestion client handle.
|
|
|
+ *
|
|
|
+ * @param cfg configuration to use
|
|
|
+ * @return transport application handle, NULL on error
|
|
|
+ */
|
|
|
+struct GNUNET_TRANSPORT_ApplicationHandle *
|
|
|
+GNUNET_TRANSPORT_application_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
|
|
|
+{
|
|
|
+ struct GNUNET_TRANSPORT_ApplicationHandle *ch;
|
|
|
+
|
|
|
+ ch = GNUNET_new (struct GNUNET_TRANSPORT_ApplicationHandle);
|
|
|
+ ch->cfg = cfg;
|
|
|
+ ch->sug_requests = GNUNET_CONTAINER_multipeermap_create (32,
|
|
|
+ GNUNET_YES);
|
|
|
+ reconnect (ch);
|
|
|
+ return ch;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function called to free all `struct GNUNET_TRANSPORT_ApplicationSuggestHandle`s
|
|
|
+ * in the map.
|
|
|
+ *
|
|
|
+ * @param cls NULL
|
|
|
+ * @param key the key
|
|
|
+ * @param value the value to free
|
|
|
+ * @return #GNUNET_OK (continue to iterate)
|
|
|
+ */
|
|
|
+static int
|
|
|
+free_sug_handle (void *cls,
|
|
|
+ const struct GNUNET_PeerIdentity *key,
|
|
|
+ void *value)
|
|
|
+{
|
|
|
+ struct GNUNET_TRANSPORT_ApplicationSuggestHandle *cur = value;
|
|
|
+
|
|
|
+ GNUNET_free (cur);
|
|
|
+ return GNUNET_OK;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * Client is done with TRANSPORT application management, release resources.
|
|
|
+ *
|
|
|
+ * @param ch handle to release
|
|
|
+ */
|
|
|
+void
|
|
|
+GNUNET_TRANSPORT_application_done (struct GNUNET_TRANSPORT_ApplicationHandle *ch)
|
|
|
+{
|
|
|
+ if (NULL != ch->mq)
|
|
|
+ {
|
|
|
+ GNUNET_MQ_destroy (ch->mq);
|
|
|
+ ch->mq = NULL;
|
|
|
+ }
|
|
|
+ if (NULL != ch->task)
|
|
|
+ {
|
|
|
+ GNUNET_SCHEDULER_cancel (ch->task);
|
|
|
+ ch->task = NULL;
|
|
|
+ }
|
|
|
+ GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests,
|
|
|
+ &free_sug_handle,
|
|
|
+ NULL);
|
|
|
+ GNUNET_CONTAINER_multipeermap_destroy (ch->sug_requests);
|
|
|
+ GNUNET_free (ch);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * We would like to receive address suggestions for a peer. TRANSPORT will
|
|
|
+ * respond with a call to the continuation immediately containing an address or
|
|
|
+ * no address if none is available. TRANSPORT can suggest more addresses until we call
|
|
|
+ * #GNUNET_TRANSPORT_application_suggest_cancel().
|
|
|
+ *
|
|
|
+ * @param ch handle
|
|
|
+ * @param peer identity of the peer we need an address for
|
|
|
+ * @param pk what kind of application will the application require (can be
|
|
|
+ * #GNUNET_MQ_PREFERENCE_NONE, we will still try to connect)
|
|
|
+ * @param bw desired bandwith, can be zero (we will still try to connect)
|
|
|
+ * @return suggest handle, NULL if a request is already pending
|
|
|
+ */
|
|
|
+struct GNUNET_TRANSPORT_ApplicationSuggestHandle *
|
|
|
+GNUNET_TRANSPORT_application_suggest (struct GNUNET_TRANSPORT_ApplicationHandle *ch,
|
|
|
+ const struct GNUNET_PeerIdentity *peer,
|
|
|
+ enum GNUNET_MQ_PreferenceKind pk,
|
|
|
+ struct GNUNET_BANDWIDTH_Value32NBO bw)
|
|
|
+{
|
|
|
+ struct GNUNET_TRANSPORT_ApplicationSuggestHandle *s;
|
|
|
+
|
|
|
+ s = GNUNET_new (struct GNUNET_TRANSPORT_ApplicationSuggestHandle);
|
|
|
+ s->ch = ch;
|
|
|
+ s->id = *peer;
|
|
|
+ s->pk = pk;
|
|
|
+ s->bw = bw;
|
|
|
+ (void) GNUNET_CONTAINER_multipeermap_put (ch->sug_requests,
|
|
|
+ &s->id,
|
|
|
+ s,
|
|
|
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
|
|
|
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
+ "Requesting TRANSPORT to suggest address for `%s'\n",
|
|
|
+ GNUNET_i2s (peer));
|
|
|
+ if (NULL == ch->mq)
|
|
|
+ return s;
|
|
|
+ GNUNET_assert (GNUNET_OK ==
|
|
|
+ transmit_suggestion (ch,
|
|
|
+ &s->id,
|
|
|
+ s));
|
|
|
+ return s;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * We no longer care about being connected to a peer.
|
|
|
+ *
|
|
|
+ * @param sh handle to stop
|
|
|
+ */
|
|
|
+void
|
|
|
+GNUNET_TRANSPORT_application_suggest_cancel (struct GNUNET_TRANSPORT_ApplicationSuggestHandle *sh)
|
|
|
+{
|
|
|
+ struct GNUNET_TRANSPORT_ApplicationHandle *ch = sh->ch;
|
|
|
+ struct GNUNET_MQ_Envelope *ev;
|
|
|
+ struct ExpressPreferenceMessage *m;
|
|
|
+
|
|
|
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
+ "Telling TRANSPORT we no longer care for an address for `%s'\n",
|
|
|
+ GNUNET_i2s (&sh->id));
|
|
|
+ GNUNET_assert (GNUNET_OK ==
|
|
|
+ GNUNET_CONTAINER_multipeermap_remove (ch->sug_requests,
|
|
|
+ &sh->id,
|
|
|
+ sh));
|
|
|
+ if (NULL == ch->mq)
|
|
|
+ {
|
|
|
+ GNUNET_free (sh);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ev = GNUNET_MQ_msg (m,
|
|
|
+ GNUNET_MESSAGE_TYPE_TRANSPORT_SUGGEST_CANCEL);
|
|
|
+ m->pk = htonl ((uint32_t) sh->pk);
|
|
|
+ m->bw = sh->bw;
|
|
|
+ m->peer = sh->id;
|
|
|
+ GNUNET_MQ_send (ch->mq,
|
|
|
+ ev);
|
|
|
+ GNUNET_free (sh);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/* end of transport_api2_application.c */
|