/* This file is part of GNUnet. Copyright (C) 2009, 2010, 2011, 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 dht/gnunet-service-dht.c * @brief GNUnet DHT service * @author Christian Grothoff * @author Nathan Evans */ #include "platform.h" #include "gnunet_block_lib.h" #include "gnunet_util_lib.h" #include "gnunet_hello_lib.h" #include "gnunet_hello_uri_lib.h" #include "gnunet_dht_service.h" #include "gnunet_statistics_service.h" #include "gnunet-service-dht.h" #include "gnunet-service-dht_datacache.h" #include "gnunet-service-dht_neighbours.h" #include "gnunet-service-dht_routing.h" /** * How often do we broadcast our HELLO to neighbours if * nothing special happens? */ #define HELLO_FREQUENCY GNUNET_TIME_UNIT_HOURS /** * Information we keep per underlay. */ struct GDS_Underlay { /** * Kept in a DLL. */ struct GDS_Underlay *next; /** * Kept in a DLL. */ struct GDS_Underlay *prev; /** * Environment for this underlay. */ struct GNUNET_DHTU_PluginEnvironment env; /** * Underlay API handle. */ struct GNUNET_DHTU_PluginFunctions *dhtu; /** * current network size estimate for this underlay. */ double network_size_estimate; /** * Name of the underlay (i.e. "gnunet" or "ip"). */ char *name; /** * Name of the library providing the underlay. */ char *libname; }; /** * An address of this peer. */ struct MyAddress { /** * Kept in a DLL. */ struct MyAddress *next; /** * Kept in a DLL. */ struct MyAddress *prev; /** * Underlay handle for the address. */ struct GNUNET_DHTU_Source *source; /** * Textual representation of the address. */ char *url; /** * Underlay of this address. */ struct GDS_Underlay *u; }; /** * Our HELLO */ struct GNUNET_HELLO_Builder *GDS_my_hello; /** * Identity of this peer. */ struct GNUNET_PeerIdentity GDS_my_identity; /** * Hash of the identity of this peer. */ struct GNUNET_HashCode GDS_my_identity_hash; /** * Our private key. */ struct GNUNET_CRYPTO_EddsaPrivateKey GDS_my_private_key; /** * Task broadcasting our HELLO. */ static struct GNUNET_SCHEDULER_Task *hello_task; /** * Handles for the DHT underlays. */ static struct GDS_Underlay *u_head; /** * Handles for the DHT underlays. */ static struct GDS_Underlay *u_tail; /** * Head of addresses of this peer. */ static struct MyAddress *a_head; /** * Tail of addresses of this peer. */ static struct MyAddress *a_tail; /** * log of the current network size estimate, used as the point where * we switch between random and deterministic routing. */ static double log_of_network_size_estimate; /** * Callback that is called when network size estimate is updated. * * @param cls a `struct GDS_Underlay` * @param timestamp time when the estimate was received from the server (or created by the server) * @param logestimate the log(Base 2) value of the current network size estimate * @param std_dev standard deviation for the estimate * */ static void update_network_size_estimate (void *cls, struct GNUNET_TIME_Absolute timestamp, double logestimate, double std_dev) { struct GDS_Underlay *u = cls; double sum = 0.0; GNUNET_STATISTICS_update (GDS_stats, "# Network size estimates received", 1, GNUNET_NO); /* do not allow estimates < 0.5 */ u->network_size_estimate = pow (2.0, GNUNET_MAX (0.5, logestimate)); for (struct GDS_Underlay *p = u_head; NULL != p; p = p->next) sum += p->network_size_estimate; if (sum <= 2.0) log_of_network_size_estimate = 0.5; else log_of_network_size_estimate = log2 (sum); } /** * Return the current NSE * * @return the current NSE as a logarithm */ double GDS_NSE_get (void) { return log_of_network_size_estimate; } #include "gnunet-service-dht_clients.c" /** * Task run periodically to broadcast our HELLO. * * @param cls NULL */ static void broadcast_hello (void *cls) { struct GNUNET_MessageHeader *hello; (void) cls; /* TODO: randomize! */ hello_task = GNUNET_SCHEDULER_add_delayed (HELLO_FREQUENCY, &broadcast_hello, NULL); hello = GNUNET_HELLO_builder_to_dht_hello_msg (GDS_my_hello, &GDS_my_private_key); if (NULL == hello) { GNUNET_break (0); return; } GDS_NEIGHBOURS_broadcast (hello); GNUNET_free (hello); } /** * Function to call with new addresses of this peer. * * @param cls the closure * @param address address under which we are likely reachable, * pointer will remain valid until @e address_del_cb is called; to be used for HELLOs. Example: "ip+udp://$PID/1.1.1.1:2086/" * @param source handle for sending from this address, NULL if we can only receive * @param[out] ctx storage space for DHT to use in association with this address */ static void u_address_add (void *cls, const char *address, struct GNUNET_DHTU_Source *source, void **ctx) { struct GDS_Underlay *u = cls; struct MyAddress *a; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Underlay adds address %s for this peer\n", address); a = GNUNET_new (struct MyAddress); a->source = source; a->url = GNUNET_strdup (address); a->u = u; GNUNET_CONTAINER_DLL_insert (a_head, a_tail, a); *ctx = a; GNUNET_HELLO_builder_add_address (GDS_my_hello, address); if (NULL != hello_task) GNUNET_SCHEDULER_cancel (hello_task); hello_task = GNUNET_SCHEDULER_add_now (&broadcast_hello, NULL); } /** * Function to call with expired addresses of this peer. * * @param[in] ctx storage space used by the DHT in association with this address */ static void u_address_del (void *ctx) { struct MyAddress *a = ctx; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Underlay deletes address %s for this peer\n", a->url); GNUNET_HELLO_builder_del_address (GDS_my_hello, a->url); GNUNET_CONTAINER_DLL_remove (a_head, a_tail, a); GNUNET_free (a->url); GNUNET_free (a); if (NULL != hello_task) GNUNET_SCHEDULER_cancel (hello_task); hello_task = GNUNET_SCHEDULER_add_now (&broadcast_hello, NULL); } void GDS_u_try_connect (const struct GNUNET_PeerIdentity *pid, const char *address) { for (struct GDS_Underlay *u = u_head; NULL != u; u = u->next) u->dhtu->try_connect (u->dhtu->cls, pid, address); } void GDS_u_send (struct GDS_Underlay *u, struct GNUNET_DHTU_Target *target, const void *msg, size_t msg_size, GNUNET_SCHEDULER_TaskCallback finished_cb, void *finished_cb_cls) { u->dhtu->send (u->dhtu->cls, target, msg, msg_size, finished_cb, finished_cb_cls); } void GDS_u_drop (struct GDS_Underlay *u, struct GNUNET_DHTU_PreferenceHandle *ph) { u->dhtu->drop (ph); } struct GNUNET_DHTU_PreferenceHandle * GDS_u_hold (struct GDS_Underlay *u, struct GNUNET_DHTU_Target *target) { return u->dhtu->hold (u->dhtu->cls, target); } /** * Task run during shutdown. * * @param cls unused */ static void shutdown_task (void *cls) { struct GDS_Underlay *u; while (NULL != (u = u_head)) { GNUNET_PLUGIN_unload (u->libname, u->dhtu); GNUNET_CONTAINER_DLL_remove (u_head, u_tail, u); GNUNET_free (u->name); GNUNET_free (u->libname); GNUNET_free (u); } GDS_NEIGHBOURS_done (); GDS_DATACACHE_done (); GDS_ROUTING_done (); if (NULL != GDS_block_context) { GNUNET_BLOCK_context_destroy (GDS_block_context); GDS_block_context = NULL; } GDS_CLIENTS_stop (); if (NULL != GDS_stats) { GNUNET_STATISTICS_destroy (GDS_stats, GNUNET_YES); GDS_stats = NULL; } if (NULL != GDS_my_hello) { GNUNET_HELLO_builder_free (GDS_my_hello); GDS_my_hello = NULL; } if (NULL != hello_task) { GNUNET_SCHEDULER_cancel (hello_task); hello_task = NULL; } } /** * Function iterating over all configuration sections. * Loads plugins for enabled DHT underlays. * * @param cls NULL * @param section configuration section to inspect */ static void load_underlay (void *cls, const char *section) { struct GDS_Underlay *u; char *libname; (void) cls; if (0 != strncasecmp (section, "dhtu-", strlen ("dhtu-"))) return; if (GNUNET_YES != GNUNET_CONFIGURATION_get_value_yesno (GDS_cfg, section, "ENABLED")) return; section += strlen ("dhtu-"); u = GNUNET_new (struct GDS_Underlay); u->env.cls = u; u->env.cfg = GDS_cfg; u->env.address_add_cb = &u_address_add; u->env.address_del_cb = &u_address_del; u->env.network_size_cb = &update_network_size_estimate; u->env.connect_cb = &GDS_u_connect; u->env.disconnect_cb = &GDS_u_disconnect; u->env.receive_cb = &GDS_u_receive; GNUNET_asprintf (&libname, "libgnunet_plugin_dhtu_%s", section); u->dhtu = GNUNET_PLUGIN_load (libname, &u->env); if (NULL == u->dhtu) { GNUNET_free (libname); GNUNET_free (u); return; } u->libname = libname; u->name = GNUNET_strdup (section); GNUNET_CONTAINER_DLL_insert (u_head, u_tail, u); } /** * Process dht requests. * * @param cls closure * @param c configuration to use * @param service the initialized service */ static void run (void *cls, const struct GNUNET_CONFIGURATION_Handle *c, struct GNUNET_SERVICE_Handle *service) { GDS_cfg = c; GDS_service = service; { char *keyfile; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (GDS_cfg, "PEER", "PRIVATE_KEY", &keyfile)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "PEER", "PRIVATE_KEY"); GNUNET_SCHEDULER_shutdown (); return; } if (GNUNET_SYSERR == GNUNET_CRYPTO_eddsa_key_from_file (keyfile, GNUNET_YES, &GDS_my_private_key)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to setup peer's private key\n"); GNUNET_free (keyfile); GNUNET_SCHEDULER_shutdown (); return; } GNUNET_free (keyfile); } GNUNET_CRYPTO_eddsa_key_get_public (&GDS_my_private_key, &GDS_my_identity.public_key); GDS_my_hello = GNUNET_HELLO_builder_new (&GDS_my_identity); GNUNET_CRYPTO_hash (&GDS_my_identity, sizeof(struct GNUNET_PeerIdentity), &GDS_my_identity_hash); GDS_block_context = GNUNET_BLOCK_context_create (GDS_cfg); GDS_stats = GNUNET_STATISTICS_create ("dht", GDS_cfg); GDS_CLIENTS_init (); GDS_ROUTING_init (); GDS_DATACACHE_init (); GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); if (GNUNET_OK != GDS_NEIGHBOURS_init ()) { GNUNET_SCHEDULER_shutdown (); return; } GNUNET_CONFIGURATION_iterate_sections (GDS_cfg, &load_underlay, NULL); if (NULL == u_head) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No DHT underlays configured!\n"); GNUNET_SCHEDULER_shutdown (); return; } } /* Finally, define the main method */ GDS_DHT_SERVICE_INIT ("dht", &run); /* end of gnunet-service-dht.c */