--- /dev/null
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @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);
+}
+
+
* @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.
#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"
*/
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.)
*/
static struct GNUNET_CONTAINER_MultiPeerMap *neighbours;
+/**
+ * Database for peer's HELLOs.
+ */
+static struct GNUNET_PEERSTORE_Handle *peerstore;
+
/**
* Lookup neighbour record for peer @a pid.
}
+/**
+ * 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.
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);
}
+/**
+ * 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.
*
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);
}
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);
}
-/**
- * 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.
*
&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;
}
*/
static void
handle_monitor_start (void *cls,
- const struct GNUNET_TRANSPORT_MonitorStart *start)
+ const struct GNUNET_TRANSPORT_MonitorStart *start)
{
struct TransportClient *tc = 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);
}
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 */
}