From 8ca9b2754d10419c9d8c041549d5747ec482b83a Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 16 Jan 2022 14:01:23 +0100 Subject: -DHT: add gnunet-dht-hello for bootstrapping --- src/dht/.gitignore | 1 + src/dht/Makefile.am | 14 ++- src/dht/dht_api.c | 157 +++++++++++++++++++++++++++- src/dht/gnunet-dht-hello.c | 178 ++++++++++++++++++++++++++++++++ src/dht/gnunet-service-dht_clients.c | 101 +++++++++++++++++- src/dht/gnunet-service-dht_neighbours.c | 8 +- src/dht/gnunet-service-dht_neighbours.h | 12 +++ src/include/gnunet_dht_service.h | 57 ++++++++++ src/include/gnunet_protocols.h | 15 ++- 9 files changed, 530 insertions(+), 13 deletions(-) create mode 100644 src/dht/gnunet-dht-hello.c diff --git a/src/dht/.gitignore b/src/dht/.gitignore index 25b1daf28..bd8af1217 100644 --- a/src/dht/.gitignore +++ b/src/dht/.gitignore @@ -10,3 +10,4 @@ test_dht_monitor test_dht_multipeer test_dht_tools.py test_dht_twopeer +gnunet-dht-hello diff --git a/src/dht/Makefile.am b/src/dht/Makefile.am index ab4ec1b3c..6d1090901 100644 --- a/src/dht/Makefile.am +++ b/src/dht/Makefile.am @@ -50,7 +50,8 @@ libexec_PROGRAMS = \ bin_PROGRAMS = \ gnunet-dht-monitor \ gnunet-dht-get \ - gnunet-dht-put + gnunet-dht-put \ + gnunet-dht-hello noinst_PROGRAMS = \ gnunet-dht-profiler @@ -80,7 +81,14 @@ gnunet_dht_get_SOURCES = \ gnunet-dht-get.c gnunet_dht_get_LDADD = \ libgnunetdht.la \ - $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/util/libgnunetutil.la +gnunet_dht_get_LDFLAGS = \ + $(GN_LIBINTL) + +gnunet_dht_hello_SOURCES = \ + gnunet-dht-hello.c +gnunet_dht_hello_LDADD = \ + libgnunetdht.la \ $(top_builddir)/src/util/libgnunetutil.la gnunet_dht_get_LDFLAGS = \ $(GN_LIBINTL) @@ -89,7 +97,6 @@ gnunet_dht_put_SOURCES = \ gnunet-dht-put.c gnunet_dht_put_LDADD = \ libgnunetdht.la \ - $(top_builddir)/src/core/libgnunetcore.la \ $(top_builddir)/src/util/libgnunetutil.la gnunet_dht_put_LDFLAGS = \ $(GN_LIBINTL) @@ -98,7 +105,6 @@ gnunet_dht_monitor_SOURCES = \ gnunet-dht-monitor.c gnunet_dht_monitor_LDADD = \ libgnunetdht.la \ - $(top_builddir)/src/core/libgnunetcore.la \ $(top_builddir)/src/util/libgnunetutil.la gnunet_dht_monitor_LDFLAGS = \ $(GN_LIBINTL) diff --git a/src/dht/dht_api.c b/src/dht/dht_api.c index a0c30d5e7..cae8de726 100644 --- a/src/dht/dht_api.c +++ b/src/dht/dht_api.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2009, 2010, 2011, 2012, 2016, 2018 GNUnet e.V. + Copyright (C) 2009-2012, 2016, 2018, 2022 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 @@ -196,6 +196,40 @@ struct GNUNET_DHT_MonitorHandle }; +/** + * Handle to get a HELLO URL from the DHT for manual bootstrapping. + */ +struct GNUNET_DHT_HelloGetHandle +{ + + /** + * DLL. + */ + struct GNUNET_DHT_HelloGetHandle *next; + + /** + * DLL. + */ + struct GNUNET_DHT_HelloGetHandle *prev; + + /** + * Function to call with the result. + */ + GNUNET_DHT_HelloGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Connection to the DHT service. + */ + struct GNUNET_DHT_Handle *dht_handle; + +}; + + /** * Connection to the DHT service. */ @@ -231,6 +265,16 @@ struct GNUNET_DHT_Handle */ struct GNUNET_DHT_PutHandle *put_tail; + /** + * DLL. + */ + struct GNUNET_DHT_HelloGetHandle *hgh_head; + + /** + * DLL. + */ + struct GNUNET_DHT_HelloGetHandle *hgh_tail; + /** * Hash map containing the current outstanding unique GET requests * (values are of type `struct GNUNET_DHT_GetHandle`). @@ -819,6 +863,53 @@ handle_client_result (void *cls, } +/** + * Process a client HELLO message received from the service. + * + * @param cls The DHT handle. + * @param hdr HELLO URL message from the service. + * @return #GNUNET_OK if @a hdr is well-formed + */ +static enum GNUNET_GenericReturnValue +check_client_hello (void *cls, + const struct GNUNET_MessageHeader *hdr) +{ + uint16_t len = ntohs (hdr->size); + const char *buf = (const char *) &hdr[1]; + + (void) cls; + if ('\0' != buf[len - sizeof (*hdr) - 1]) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Process a client HELLO message received from the service. + * + * @param cls The DHT handle. + * @param hdr HELLO URL message from the service. + */ +static void +handle_client_hello (void *cls, + const struct GNUNET_MessageHeader *hdr) +{ + struct GNUNET_DHT_Handle *handle = cls; + const char *url = (const char *) &hdr[1]; + struct GNUNET_DHT_HelloGetHandle *hgh; + + while (NULL != (hgh = handle->hgh_head)) + { + hgh->cb (hgh->cb_cls, + url); + GNUNET_DHT_hello_get_cancel (hgh); + } +} + + /** * Process a MQ PUT transmission notification. * @@ -866,6 +957,10 @@ try_connect (struct GNUNET_DHT_Handle *h) GNUNET_MESSAGE_TYPE_DHT_CLIENT_RESULT, struct GNUNET_DHT_ClientResultMessage, h), + GNUNET_MQ_hd_var_size (client_hello, + GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_URL, + struct GNUNET_MessageHeader, + h), GNUNET_MQ_handler_end () }; @@ -1257,4 +1352,64 @@ GNUNET_DHT_verify_path (const struct GNUNET_HashCode *key, } +struct GNUNET_DHT_HelloGetHandle * +GNUNET_DHT_hello_get (struct GNUNET_DHT_Handle *dht_handle, + GNUNET_DHT_HelloGetCallback cb, + void *cb_cls) +{ + struct GNUNET_DHT_HelloGetHandle *hgh; + struct GNUNET_MessageHeader *hdr; + struct GNUNET_MQ_Envelope *env; + + hgh = GNUNET_new (struct GNUNET_DHT_HelloGetHandle); + hgh->cb = cb; + hgh->cb_cls = cb_cls; + hgh->dht_handle = dht_handle; + GNUNET_CONTAINER_DLL_insert (dht_handle->hgh_head, + dht_handle->hgh_tail, + hgh); + env = GNUNET_MQ_msg (hdr, + GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_GET); + GNUNET_MQ_send (dht_handle->mq, + env); + return hgh; +} + + +void +GNUNET_DHT_hello_get_cancel (struct GNUNET_DHT_HelloGetHandle *hgh) +{ + struct GNUNET_DHT_Handle *dht_handle = hgh->dht_handle; + + GNUNET_CONTAINER_DLL_remove (dht_handle->hgh_head, + dht_handle->hgh_tail, + hgh); + GNUNET_free (hgh); +} + + +void +GNUNET_DHT_hello_offer (struct GNUNET_DHT_Handle *dht_handle, + const char *url, + GNUNET_SCHEDULER_TaskCallback cb, + void *cb_cls) +{ + struct GNUNET_MessageHeader *hdr; + size_t slen = strlen (url) + 1; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg_extra (hdr, + slen, + GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_URL); + memcpy (&hdr[1], + url, + slen); + GNUNET_MQ_notify_sent (env, + cb, + cb_cls); + GNUNET_MQ_send (dht_handle->mq, + env); +} + + /* end of dht_api.c */ diff --git a/src/dht/gnunet-dht-hello.c b/src/dht/gnunet-dht-hello.c new file mode 100644 index 000000000..369ed5643 --- /dev/null +++ b/src/dht/gnunet-dht-hello.c @@ -0,0 +1,178 @@ +/* + This file is part of GNUnet. + Copyright (C) 2022 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 dht/gnunet-dht-hello.c + * @brief Obtain HELLO from DHT for bootstrapping + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_dht_service.h" + +#define LOG(kind, ...) GNUNET_log_from (kind, "dht-clients", __VA_ARGS__) + +/** + * Handle to the DHT + */ +static struct GNUNET_DHT_Handle *dht_handle; + +/** + * Handle to the DHT hello get operation. + */ +static struct GNUNET_DHT_HelloGetHandle *get_hello_handle; + +/** + * Global status value + */ +static int global_ret; + + +/** + * Task run to clean up on shutdown. + * + * @param cls unused + */ +static void +cleanup_task (void *cls) +{ + if (NULL != get_hello_handle) + { + GNUNET_DHT_hello_get_cancel (get_hello_handle); + get_hello_handle = NULL; + } + if (NULL != dht_handle) + { + GNUNET_DHT_disconnect (dht_handle); + dht_handle = NULL; + } +} + + +/** + * Task run when we are finished. Triggers shutdown. + * + * @param cls unused + */ +static void +hello_done_cb (void *cls) +{ + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Function called on our HELLO. + * + * @param cls closure + * @param url the HELLO URL + */ +static void +hello_result_cb (void *cls, + const char *url) +{ + get_hello_handle = NULL; + fprintf (stdout, + "%s\n", + url); + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Main function that will be run by the scheduler. + * + * @param cls closure, NULL + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param cfg configuration + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + (void) cls; + (void) cfgfile; + GNUNET_SCHEDULER_add_shutdown (&cleanup_task, + NULL); + if (NULL == (dht_handle = GNUNET_DHT_connect (cfg, + 1))) + { + fprintf (stderr, + _ ("Failed to connect to DHT service!\n")); + global_ret = EXIT_NOTCONFIGURED; + GNUNET_SCHEDULER_shutdown (); + return; + } + if (NULL == args[0]) + { + get_hello_handle = GNUNET_DHT_hello_get (dht_handle, + &hello_result_cb, + NULL); + GNUNET_break (NULL != get_hello_handle); + } + else + { + GNUNET_DHT_hello_offer (dht_handle, + args[0], + &hello_done_cb, + NULL); + } +} + + +/** + * Entry point for gnunet-dht-hello + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, + char *const *argv) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + enum GNUNET_GenericReturnValue iret; + + if (GNUNET_OK != + GNUNET_STRINGS_get_utf8_args (argc, argv, + &argc, &argv)) + return 2; + iret = GNUNET_PROGRAM_run ( + argc, + argv, + "gnunet-dht-hello [URL]", + gettext_noop ( + "Obtain HELLO from DHT or provide HELLO to DHT for bootstrapping"), + options, + &run, + NULL); + if (GNUNET_SYSERR == iret) + return EXIT_FAILURE; + if (GNUNET_NO == iret) + return EXIT_SUCCESS; + return global_ret; +} + + +/* end of gnunet-dht-hello.c */ diff --git a/src/dht/gnunet-service-dht_clients.c b/src/dht/gnunet-service-dht_clients.c index bc39e004a..88db7b0ea 100644 --- a/src/dht/gnunet-service-dht_clients.c +++ b/src/dht/gnunet-service-dht_clients.c @@ -1060,6 +1060,95 @@ GDS_CLIENTS_handle_reply (const struct GDS_DATACACHE_BlockData *bd, } +/* **************** HELLO logic ***************** */ + +/** + * Handler for HELLO GET message. Reply to client + * with a URL of our HELLO. + * + * @param cls the client we received this message from + * @param msg the actual message received + * + */ +static void +handle_dht_local_hello_get (void *cls, + const struct GNUNET_MessageHeader *msg) +{ + struct ClientHandle *ch = cls; + char *url = GNUNET_HELLO_builder_to_url (GDS_my_hello, + &GDS_my_private_key); + size_t slen = strlen (url) + 1; + struct GNUNET_MessageHeader *hdr; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg_extra (hdr, + slen, + GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_URL); + memcpy (&hdr[1], + url, + slen); + GNUNET_free (url); + GNUNET_MQ_send (ch->mq, + env); +} + + +/** + * Process a client HELLO message received from the service. + * + * @param cls the client we received this message from + * @param hdr HELLO URL message from the service. + * @return #GNUNET_OK if @a hdr is well-formed + */ +static enum GNUNET_GenericReturnValue +check_dht_local_hello_offer (void *cls, + const struct GNUNET_MessageHeader *hdr) +{ + uint16_t len = ntohs (hdr->size); + const char *buf = (const char *) &hdr[1]; + + (void) cls; + if ('\0' != buf[len - sizeof (*hdr) - 1]) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handler for HELLO OFFER message. Try to use the + * HELLO to connect to another peer. + * + * @param cls the client we received this message from + * @param msg the actual message received + */ +static void +handle_dht_local_hello_offer (void *cls, + const struct GNUNET_MessageHeader *msg) +{ + struct ClientHandle *ch = cls; + const char *url = (const char *) &msg[1]; + struct GNUNET_HELLO_Builder *b; + struct GNUNET_PeerIdentity pid; + + b = GNUNET_HELLO_builder_from_url (url); + if (NULL == b) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (ch->client); + return; + } + GNUNET_SERVICE_client_continue (ch->client); + GNUNET_HELLO_builder_iterate (b, + &pid, + &GDS_try_connect, + &pid); + GNUNET_HELLO_builder_free (b); +} + + /* ************* logic for monitors ************** */ @@ -1330,8 +1419,8 @@ response_action (void *cls, bd->put_path_length * sizeof(struct GNUNET_DHT_PathElement)); GNUNET_memcpy (path, resp_ctx->get_path, - resp_ctx->get_path_length * sizeof(struct - GNUNET_DHT_PathElement)); + resp_ctx->get_path_length + * sizeof(struct GNUNET_DHT_PathElement)); GNUNET_memcpy (&path[resp_ctx->get_path_length], bd->data, bd->data_size); @@ -1504,6 +1593,14 @@ GDS_CLIENTS_stop (void) GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET_RESULTS_KNOWN, \ struct GNUNET_DHT_ClientGetResultSeenMessage, \ NULL), \ + GNUNET_MQ_hd_fixed_size (dht_local_hello_get, \ + GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_GET, \ + struct GNUNET_MessageHeader, \ + NULL), \ + GNUNET_MQ_hd_var_size (dht_local_hello_offer, \ + GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_URL, \ + struct GNUNET_MessageHeader, \ + NULL), \ GNUNET_MQ_handler_end ()) diff --git a/src/dht/gnunet-service-dht_neighbours.c b/src/dht/gnunet-service-dht_neighbours.c index 213308ca6..fc025fa18 100644 --- a/src/dht/gnunet-service-dht_neighbours.c +++ b/src/dht/gnunet-service-dht_neighbours.c @@ -2246,9 +2246,9 @@ check_dht_p2p_result (void *cls, * @param cls closure pointing to a `struct GNUNET_PeerIdentity *` * @param uri one of the URIs */ -static void -try_connect (void *cls, - const char *uri) +void +GDS_try_connect (void *cls, + const char *uri) { const struct GNUNET_PeerIdentity *pid = cls; struct GNUNET_HashCode phash; @@ -2367,7 +2367,7 @@ handle_dht_p2p_result (void *cls, if (GNUNET_YES != disable_try_connect) GNUNET_HELLO_builder_iterate (b, &pid, - &try_connect, + &GDS_try_connect, &pid); GNUNET_HELLO_builder_free (b); } diff --git a/src/dht/gnunet-service-dht_neighbours.h b/src/dht/gnunet-service-dht_neighbours.h index fecdbe9f1..5c327e019 100644 --- a/src/dht/gnunet-service-dht_neighbours.h +++ b/src/dht/gnunet-service-dht_neighbours.h @@ -132,6 +132,18 @@ GDS_am_closest_peer (const struct GNUNET_HashCode *key, const struct GNUNET_CONTAINER_BloomFilter *bloom); +/** + * Callback function used to extract URIs from a builder. + * Called when we should consider connecting to a peer. + * + * @param cls closure pointing to a `struct GNUNET_PeerIdentity *` + * @param uri one of the URIs + */ +void +GDS_try_connect (void *cls, + const char *uri); + + /** * Function to call when we connect to a peer and can henceforth transmit to * that peer. diff --git a/src/include/gnunet_dht_service.h b/src/include/gnunet_dht_service.h index abe5a03e4..e0f9f6fc3 100644 --- a/src/include/gnunet_dht_service.h +++ b/src/include/gnunet_dht_service.h @@ -512,6 +512,63 @@ GNUNET_DHT_verify_path (const struct GNUNET_HashCode *key, const struct GNUNET_PeerIdentity *me); +/** + * Handle to get a HELLO URL from the DHT for manual bootstrapping. + */ +struct GNUNET_DHT_HelloGetHandle; + + +/** + * Signature called with the result of a HELLO GET operation. + * + * @param cls closure + * @param hello_url the resulting HELLO URL, NULL on error + */ +typedef void +(*GNUNET_DHT_HelloGetCallback)(void *cls, + const char *hello_url); + + +/** + * Obtain HELLO URL of the DHT identified by @a dht_handle. + * + * @param dht_handle DHT to query + * @param cb function to call with the result + * @param cb_cls closure for @a cb + * @return NULL on failure + */ +struct GNUNET_DHT_HelloGetHandle * +GNUNET_DHT_hello_get (struct GNUNET_DHT_Handle *dht_handle, + GNUNET_DHT_HelloGetCallback cb, + void *cb_cls); + + +/** + * Cancel hello get operation. + * + * @param[in] hgh operation to cancel. + */ +void +GNUNET_DHT_hello_get_cancel (struct GNUNET_DHT_HelloGetHandle *hgh); + + +/** + * Offer HELLO URL of the DHT identified by @a dht_handle. + * Callback may be invoked once, only way to cancel is to + * disconnect @a dht_handle. + * + * @param dht_handle DHT to query + * @param url URL with a HELLO to offer to the DHT + * @param cb function called when done + * @param cb_cls closure for @a cb + */ +void +GNUNET_DHT_hello_offer (struct GNUNET_DHT_Handle *dht_handle, + const char *url, + GNUNET_SCHEDULER_TaskCallback cb, + void *cb_cls); + + #if 0 /* keep Emacsens' auto-indent happy */ { #endif diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h index cddacc8a6..f2892e125 100644 --- a/src/include/gnunet_protocols.h +++ b/src/include/gnunet_protocols.h @@ -665,9 +665,20 @@ extern "C" { #define GNUNET_MESSAGE_TYPE_DHT_P2P_HELLO 157 /** - * DHT wants to use CORE to transmit data. + * Encapsulation of DHT messages in CORE service. */ -#define GNUNET_MESSAGE_TYPE_DHT_CORE 143 +#define GNUNET_MESSAGE_TYPE_DHT_CORE 158 + +/** + * HELLO URL send between client and service (in + * either direction). + */ +#define GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_URL 159 + +/** + * Client requests DHT service's HELLO URL. + */ +#define GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_GET 161 /******************************************************************************* -- cgit v1.2.3