From ca90313490f4233ce9d209abbdcc2d78d16b8326 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 22 Nov 2018 17:29:01 +0100 Subject: add new hello generation support logic to tng --- src/hello/Makefile.am | 4 +- src/hello/hello-ng.c | 178 +++++++++++++++++++++++ src/include/gnunet_hello_lib.h | 37 +++++ src/include/gnunet_signatures.h | 6 + src/peerstore/plugin_peerstore_sqlite.c | 2 +- src/transport/Makefile.am | 2 + src/transport/gnunet-service-tng.c | 245 ++++++++++++++++++++++++++------ 7 files changed, 426 insertions(+), 48 deletions(-) create mode 100644 src/hello/hello-ng.c diff --git a/src/hello/Makefile.am b/src/hello/Makefile.am index 79003301b..00357f9e1 100644 --- a/src/hello/Makefile.am +++ b/src/hello/Makefile.am @@ -13,7 +13,9 @@ endif lib_LTLIBRARIES = libgnunethello.la libgnunethello_la_SOURCES = \ - hello.c address.c + hello.c \ + address.c \ + hello-ng.c libgnunethello_la_LIBADD = \ $(top_builddir)/src/util/libgnunetutil.la $(XLIB) \ $(LTLIBINTL) diff --git a/src/hello/hello-ng.c b/src/hello/hello-ng.c new file mode 100644 index 000000000..425095f9c --- /dev/null +++ b/src/hello/hello-ng.c @@ -0,0 +1,178 @@ +/* + This file is part of GNUnet. + Copyright (C) 2018 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 hello/hello-ng.c + * @brief helper library for handling HELLOs + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_signatures.h" +#include "gnunet_hello_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_util_lib.h" + +/** + * Binary block we sign when we sign an address. + */ +struct SignedAddress +{ + /** + * Purpose must be #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_ADDRESS + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * When does the address expire. + */ + struct GNUNET_TIME_AbsoluteNBO expiration; + + /** + * Hash of the address. + */ + struct GNUNET_HashCode h_addr; +}; + + +/** + * Build address record by signing raw information with private key. + * + * @param address text address at @a communicator to sign + * @param expiration how long is @a address valid + * @param private_key signing key to use + * @param result[out] where to write address record (allocated) + * @param result_size[out] set to size of @a result + */ +void +GNUNET_HELLO_sign_address (const char *address, + struct GNUNET_TIME_Absolute expiration, + const struct GNUNET_CRYPTO_EddsaPrivateKey *private_key, + void **result, + size_t *result_size) +{ + struct SignedAddress sa; + struct GNUNET_CRYPTO_EddsaSignature sig; + char *sig_str; + + sa.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_ADDRESS); + sa.purpose.size = htonl (sizeof (sa)); + sa.expiration = GNUNET_TIME_absolute_hton (expiration); + GNUNET_CRYPTO_hash (address, + strlen (address), + &sa.h_addr); + GNUNET_assert (GNUNET_YES == + GNUNET_CRYPTO_eddsa_sign (private_key, + &sa.purpose, + &sig)); + sig_str = NULL; + (void) GNUNET_STRINGS_base64_encode (&sig, + sizeof (sig), + &sig_str); + *result_size = 1 + GNUNET_asprintf ((char **) result, + "%s;%llu;%s", + sig_str, + (unsigned long long) expiration.abs_value_us, + address); + GNUNET_free (sig_str); +} + + +/** + * Check signature and extract address record. + * + * @param raw raw signed address + * @param raw_size size of @a raw + * @param public_key public key to use for signature verification + * @param expiration[out] how long is the address valid + * @return NULL on error, otherwise the address + */ +char * +GNUNET_HELLO_extract_address (const void *raw, + size_t raw_size, + const struct GNUNET_CRYPTO_EddsaPublicKey *public_key, + struct GNUNET_TIME_Absolute *expiration) +{ + const char *raws = raw; + unsigned long long raw_us; + const char *sc; + const char *sc2; + const char *raw_addr; + struct GNUNET_TIME_Absolute raw_expiration; + struct SignedAddress sa; + struct GNUNET_CRYPTO_EddsaSignature *sig; + + if ('\0' != raws[raw_size]) + { + GNUNET_break_op (0); + return NULL; + } + if (NULL == (sc = strchr (raws, + ';'))) + { + GNUNET_break_op (0); + return NULL; + } + if (NULL == (sc2 = strchr (sc + 1, + ';'))) + { + GNUNET_break_op (0); + return NULL; + } + if (1 != sscanf (sc + 1, + "%llu;", + &raw_us)) + { + GNUNET_break_op (0); + return NULL; + } + raw_expiration.abs_value_us = raw_us; + if (0 == GNUNET_TIME_absolute_get_remaining (raw_expiration).rel_value_us) + return NULL; /* expired */ + sig = NULL; + if (sizeof (struct GNUNET_CRYPTO_EddsaSignature) != + GNUNET_STRINGS_base64_decode (raws, + sc - raws, + (void **) &sig)) + { + GNUNET_break_op (0); + GNUNET_free_non_null (sig); + return NULL; + } + raw_addr = sc2 + 1; + + sa.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_ADDRESS); + sa.purpose.size = htonl (sizeof (sa)); + sa.expiration = GNUNET_TIME_absolute_hton (raw_expiration); + GNUNET_CRYPTO_hash (raw_addr, + strlen (raw_addr), + &sa.h_addr); + if (GNUNET_YES != + GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_ADDRESS, + &sa.purpose, + sig, + public_key)) + { + GNUNET_break_op (0); + GNUNET_free (sig); + return NULL; + } + GNUNET_free (sig); + return GNUNET_strdup (raw_addr); +} + + diff --git a/src/include/gnunet_hello_lib.h b/src/include/gnunet_hello_lib.h index 2a961e524..e19419f25 100644 --- a/src/include/gnunet_hello_lib.h +++ b/src/include/gnunet_hello_lib.h @@ -473,6 +473,43 @@ GNUNET_HELLO_parse_uri (const char *uri, GNUNET_HELLO_TransportPluginsFind plugins_find); + +/* NG API */ + +/** + * Build address record by signing raw information with private key. + * + * @param address text address to sign + * @param expiration how long is @a address valid + * @param private_key signing key to use + * @param result[out] where to write address record (allocated) + * @param result_size[out] set to size of @a result + */ +void +GNUNET_HELLO_sign_address (const char *address, + struct GNUNET_TIME_Absolute expiration, + const struct GNUNET_CRYPTO_EddsaPrivateKey *private_key, + void **result, + size_t *result_size); + + +/** + * Check signature and extract address record. + * + * @param raw raw signed address + * @param raw_size size of @a raw + * @param public_key public key to use for signature verification + * @param expiration[out] how long is the address valid + * @return NULL on error, otherwise the address + */ +char * +GNUNET_HELLO_extract_address (const void *raw, + size_t raw_size, + const struct GNUNET_CRYPTO_EddsaPublicKey *public_key, + struct GNUNET_TIME_Absolute *expiration); + + + #if 0 /* keep Emacsens' auto-indent happy */ { #endif diff --git a/src/include/gnunet_signatures.h b/src/include/gnunet_signatures.h index 829f8be7e..03c97f199 100644 --- a/src/include/gnunet_signatures.h +++ b/src/include/gnunet_signatures.h @@ -188,6 +188,12 @@ extern "C" */ #define GNUNET_SIGNATURE_PURPOSE_CREDENTIAL 28 +/** + * Signature by a peer affirming that this is one of its + * addresses (for the given time period). + */ +#define GNUNET_SIGNATURE_PURPOSE_TRANSPORT_ADDRESS 29 + #if 0 /* keep Emacsens' auto-indent happy */ { #endif diff --git a/src/peerstore/plugin_peerstore_sqlite.c b/src/peerstore/plugin_peerstore_sqlite.c index 7ecb7bacd..e09274989 100644 --- a/src/peerstore/plugin_peerstore_sqlite.c +++ b/src/peerstore/plugin_peerstore_sqlite.c @@ -496,7 +496,7 @@ sql_prepare (sqlite3 *dbh, * as needed as well). * * @param plugin the plugin context (state for this module) - * @return GNUNET_OK on success + * @return #GNUNET_OK on success */ static int database_setup (struct Plugin *plugin) diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am index deeb39b48..0693a0b9f 100644 --- a/src/transport/Makefile.am +++ b/src/transport/Makefile.am @@ -328,6 +328,8 @@ gnunet_service_tng_SOURCES = \ gnunet-service-tng.c gnunet_service_tng_LDADD = \ $(top_builddir)/src/ats/libgnunetats.la \ + $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/hello/libgnunethello.la \ $(top_builddir)/src/statistics/libgnunetstatistics.la \ $(top_builddir)/src/util/libgnunetutil.la \ $(GN_LIBINTL) diff --git a/src/transport/gnunet-service-tng.c b/src/transport/gnunet-service-tng.c index c7bdfd77c..e5fb51bd2 100644 --- a/src/transport/gnunet-service-tng.c +++ b/src/transport/gnunet-service-tng.c @@ -21,8 +21,6 @@ * @author Christian Grothoff * * TODO: - * - make *our* collected addresses available somehow somewhere - * => Choices: in peerstore or revive/keep peerinfo? * - MTU information is missing for queues! * - start supporting monitor logic (add functions to signal monitors!) * - manage fragmentation/defragmentation, retransmission, track RTT, loss, etc. @@ -33,7 +31,7 @@ #include "gnunet_util_lib.h" #include "gnunet_statistics_service.h" #include "gnunet_transport_service.h" -#include "gnunet_peerinfo_service.h" +#include "gnunet_peerstore_service.h" #include "gnunet_ats_service.h" #include "gnunet-service-transport.h" #include "transport.h" @@ -262,6 +260,16 @@ struct AddressListEntry */ const char *address; + /** + * Current context for storing this address in the peerstore. + */ + struct GNUNET_PEERSTORE_StoreContext *sc; + + /** + * Task to periodically do @e st operation. + */ + struct GNUNET_SCHEDULER_Task *st; + /** * What is a typical lifetime the communicator expects this * address to have? (Always from now.) @@ -425,6 +433,11 @@ struct GNUNET_CRYPTO_EddsaPrivateKey *GST_my_private_key; */ static struct GNUNET_CONTAINER_MultiPeerMap *neighbours; +/** + * Database for peer's HELLOs. + */ +static struct GNUNET_PEERSTORE_Handle *peerstore; + /** * Lookup neighbour record for peer @a pid. @@ -469,6 +482,78 @@ client_connect_cb (void *cls, } +/** + * Release memory used by @a neighbour. + * + * @param neighbour neighbour entry to free + */ +static void +free_neighbour (struct Neighbour *neighbour) +{ + GNUNET_assert (NULL == neighbour->queue_head); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (neighbours, + &neighbour->pid, + neighbour)); + GNUNET_free (neighbour); +} + + +/** + * Free @a queue. + * + * @param queue the queue to free + */ +static void +free_queue (struct Queue *queue) +{ + struct Neighbour *neighbour = queue->neighbour; + struct TransportClient *tc = queue->tc; + + GNUNET_CONTAINER_MDLL_remove (neighbour, + neighbour->queue_head, + neighbour->queue_tail, + queue); + GNUNET_CONTAINER_MDLL_remove (client, + tc->details.communicator.queue_head, + tc->details.communicator.queue_tail, + queue); + GNUNET_free (queue); + if (NULL == neighbour->queue_head) + { + // FIXME: notify cores/monitors! + free_neighbour (neighbour); + } +} + + +/** + * Free @a ale + * + * @param ale address list entry to free + */ +static void +free_address_list_entry (struct AddressListEntry *ale) +{ + struct TransportClient *tc = ale->tc; + + GNUNET_CONTAINER_DLL_remove (tc->details.communicator.addr_head, + tc->details.communicator.addr_tail, + ale); + if (NULL != ale->sc) + { + GNUNET_PEERSTORE_store_cancel (ale->sc); + ale->sc = NULL; + } + if (NULL != ale->st) + { + GNUNET_SCHEDULER_cancel (ale->st); + ale->st = NULL; + } + GNUNET_free (ale); +} + + /** * Called whenever a client is disconnected. Frees our * resources associated with that client. @@ -511,7 +596,16 @@ client_disconnect_cb (void *cls, case CT_MONITOR: break; case CT_COMMUNICATOR: - GNUNET_free (tc->details.communicator.address_prefix); + { + struct Queue *q; + struct AddressListEntry *ale; + + while (NULL != (q = tc->details.communicator.queue_head)) + free_queue (q); + while (NULL != (ale = tc->details.communicator.addr_head)) + free_address_list_entry (ale); + GNUNET_free (tc->details.communicator.address_prefix); + } break; } GNUNET_free (tc); @@ -788,6 +882,81 @@ check_add_address (void *cls, } +/** + * Ask peerstore to store our address. + * + * @param cls an `struct AddressListEntry *` + */ +static void +store_pi (void *cls); + + +/** + * Function called when peerstore is done storing our address. + */ +static void +peerstore_store_cb (void *cls, + int success) +{ + struct AddressListEntry *ale = cls; + + ale->sc = NULL; + if (GNUNET_YES != success) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to store our own address `%s' in peerstore!\n", + ale->address); + /* refresh period is 1/4 of expiration time, that should be plenty + without being excessive. */ + ale->st = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide (ale->expiration, + 4ULL), + &store_pi, + ale); +} + + +/** + * Ask peerstore to store our address. + * + * @param cls an `struct AddressListEntry *` + */ +static void +store_pi (void *cls) +{ + struct AddressListEntry *ale = cls; + void *addr; + size_t addr_len; + struct GNUNET_TIME_Absolute expiration; + + ale->st = NULL; + expiration = GNUNET_TIME_relative_to_absolute (ale->expiration); + GNUNET_HELLO_sign_address (ale->address, + expiration, + GST_my_private_key, + &addr, + &addr_len); + ale->sc = GNUNET_PEERSTORE_store (peerstore, + "transport", + &GST_my_identity, + "hello", + addr, + addr_len, + expiration, + GNUNET_PEERSTORE_STOREOPTION_MULTIPLE, + &peerstore_store_cb, + ale); + GNUNET_free (addr); + if (NULL == ale->sc) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to store our address `%s' with peerstore\n", + ale->address); + ale->st = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &store_pi, + ale); + } +} + + /** * Address of our peer added. Process the request. * @@ -815,7 +984,8 @@ handle_add_address (void *cls, GNUNET_CONTAINER_DLL_insert (tc->details.communicator.addr_head, tc->details.communicator.addr_tail, ale); - // FIXME: notify somebody?! + ale->st = GNUNET_SCHEDULER_add_now (&store_pi, + ale); GNUNET_SERVICE_client_continue (tc->client); } @@ -845,11 +1015,7 @@ handle_del_address (void *cls, if (dam->aid != ale->aid) continue; GNUNET_assert (ale->tc == tc); - GNUNET_CONTAINER_DLL_remove (tc->details.communicator.addr_head, - tc->details.communicator.addr_tail, - ale); - // FIXME: notify somebody? - GNUNET_free (ale); + free_address_list_entry (ale); GNUNET_SERVICE_client_continue (tc->client); } GNUNET_break (0); @@ -996,23 +1162,6 @@ handle_add_queue_message (void *cls, } -/** - * Release memory used by @a neighbour. - * - * @param neighbour neighbour entry to free - */ -static void -free_neighbour (struct Neighbour *neighbour) -{ - GNUNET_assert (NULL == neighbour->queue_head); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multipeermap_remove (neighbours, - &neighbour->pid, - neighbour)); - GNUNET_free (neighbour); -} - - /** * Queue to a peer went down. Process the request. * @@ -1042,20 +1191,7 @@ handle_del_queue_message (void *cls, &neighbour->pid, sizeof (struct GNUNET_PeerIdentity))) ) continue; - GNUNET_CONTAINER_MDLL_remove (neighbour, - neighbour->queue_head, - neighbour->queue_tail, - queue); - GNUNET_CONTAINER_MDLL_remove (client, - tc->details.communicator.queue_head, - tc->details.communicator.queue_tail, - queue); - GNUNET_free (queue); - if (NULL == neighbour->queue_head) - { - // FIXME: notify cores/monitors! - free_neighbour (neighbour); - } + free_queue (queue); GNUNET_SERVICE_client_continue (tc->client); return; } @@ -1094,7 +1230,7 @@ handle_send_message_ack (void *cls, */ static void handle_monitor_start (void *cls, - const struct GNUNET_TRANSPORT_MonitorStart *start) + const struct GNUNET_TRANSPORT_MonitorStart *start) { struct TransportClient *tc = cls; @@ -1147,20 +1283,30 @@ do_shutdown (void *cls) { (void) cls; + GNUNET_CONTAINER_multipeermap_iterate (neighbours, + &free_neighbour_cb, + NULL); + /* FIXME: if this assertion fails (likely!), make sure we + clean up clients *before* doing the rest of the + shutdown! (i.e. by scheduling rest asynchronously!) */ + GNUNET_assert (NULL == clients_head); + if (NULL != peerstore) + { + GNUNET_PEERSTORE_disconnect (peerstore, + GNUNET_NO); + peerstore = NULL; + } if (NULL != GST_stats) { GNUNET_STATISTICS_destroy (GST_stats, GNUNET_NO); GST_stats = NULL; - } + } if (NULL != GST_my_private_key) { GNUNET_free (GST_my_private_key); GST_my_private_key = NULL; } - GNUNET_CONTAINER_multipeermap_iterate (neighbours, - &free_neighbour_cb, - NULL); GNUNET_CONTAINER_multipeermap_destroy (neighbours); } @@ -1200,6 +1346,13 @@ run (void *cls, GST_cfg); GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); + peerstore = GNUNET_PEERSTORE_connect (GST_cfg); + if (NULL == peerstore) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } /* start subsystems */ } -- cgit v1.2.3