123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563 |
- /* rehash --- a decentralised hash<->hash store
- Copyright © 2020 Maxime Devos <maxime.devos@student.kuleuven.be>
- This file is part of rehash.
- rehash is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or (at
- your option) any later version.
- rehash 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 General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with rehash. If not, see <http://www.gnu.org/licenses/>. */
- #include "platform.h"
- #include <stdio.h>
- #include <gnunet/gnunet_service_lib.h>
- #include <gnunet/gnunet_container_lib.h>
- #include <gnunet/gnunet_dht_service.h>
- #include <gnunet/gnunet_datastore_service.h>
- #include "rehash_service.h"
- #include "extra_gnunet_protocols.h"
- #include "rehash.h"
- #include "rehash_crypto.h"
- #include "rehash_dht.h"
- /* FIXME no magic constants
- An upper bound on hash sizes found in the wild. */
- #define MAGIC 64
- static struct GNUNET_DHT_Handle *dht_handle;
- static struct GNUNET_DATASTORE_Handle *ds_handle;
- /* TODO bring old data back into the DHT */
- /* TODO monitor the network for known bad hashes,
- and kill them when passing the local peer. */
- enum ServerContextType
- {
- SERVER_CONTEXT_GET,
- SERVER_CONTEXT_PUT,
- };
- struct ClientContext;
- /** Information about in-process hash->hash lookup
- requests. */
- struct GetContext
- {
- enum ServerContextType ctx;
- uint32_t request_id;
- struct ClientContext *client;
- /* TODO the API allows controlling
- where to find hashes ( )*/
- /* Never NULL actually. */
- struct GNUNET_DHT_GetHandle *dht_get;
- /* If NULL, the input hash was not present
- in the datastore. */
- struct GNUNET_DATASTORE_QueueEntry *datastore_get;
- };
- /** Information about in-process hash->hash insertions. */
- struct PutContext
- {
- enum ServerContextType ctx;
- uint32_t request_id;
- struct ClientContext *client;
- /* If NULL, the mapping has been send onto
- the DHT. Once datastore_put is completed,
- the task FIXME??? will periodically
- reenter the hashes into the DHT. */
- struct GNUNET_DHT_PutHandle *dht_put;
- /* If NULL, the mapping has been inserted into
- the datastore. */
- struct GNUNET_DATASTORE_QueueEntry *datastore_put;
- };
- struct ClientContext
- {
- struct GNUNET_SERVICE_Client *client;
- /* Requests from C->S */
- struct GNUNET_CONTAINER_MultiHashMap32 *requests;
- };
- /**
- * Callback run when shutting down the rehash service
- */
- static void
- shutdown_task (void *cls)
- {
- /* FIXME perhaps let store requests complete first? */
- GNUNET_DHT_disconnect (dht_handle);
- GNUNET_DATASTORE_disconnect (ds_handle, GNUNET_NO);
- /* prevent accidental use-after-free */
- dht_handle = NULL;
- ds_handle = NULL;
- }
- /**
- * Callback to initialise the rehash service
- */
- static void
- init_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg,
- struct GNUNET_SERVICE_Handle *sh)
- {
- /* TODO: reasonable length */
- dht_handle = GNUNET_DHT_connect (cfg, 16);
- ds_handle = GNUNET_DATASTORE_connect (cfg);
- /* TODO: what to do in these cases,
- and is this possible? */
- GNUNET_assert(dht_handle != NULL);
- GNUNET_assert(ds_handle != NULL);
- GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
- }
- /**
- * Callback for when a client connects to the service.
- */
- static void *
- connect_cb(void *cls, struct GNUNET_SERVICE_Client *c,
- struct GNUNET_MQ_Handle *mq)
- {
- struct ClientContext *ctx;
- ctx = GNUNET_new(struct ClientContext);
- ctx->client = c;
- /* length is a wild guess */
- ctx->requests = GNUNET_CONTAINER_multihashmap32_create (8);
- /* TODO when is this possible */
- GNUNET_assert(ctx->requests != NULL);
- return ctx;
- }
- /**
- * Callback for when a client is disconnected from the service.
- */
- static void
- disconnect_cb(void *cls, struct GNUNET_SERVICE_Client *c, void *internal_cls)
- {
- /* TODO free elements of cls->requests, abort requests ... */
- }
- static int
- check_get (void *cls, const struct REHASH_GetMessage *msg)
- {
- struct ClientContext *ctx;
- uint16_t header_size;
- uint32_t input_length; /* TODO perhaps uint16_t */
- ctx = cls;
- header_size = ntohs(msg->header.size);
- input_length = ntohl(msg->input_length);
- if (header_size - sizeof(struct REHASH_GetMessage) != input_length)
- {
- GNUNET_break(0);
- return GNUNET_SYSERR;
- }
- /* Detect duplicate message ids */
- if (GNUNET_CONTAINER_multihashmap32_contains (ctx->requests, msg->request_id))
- /* TODO check if GNUNET_break below is redundant */
- return GNUNET_SYSERR;
- /* Detect unsupported options */
- if (ntohl(msg->options) > 1)
- {
- GNUNET_break(0);
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
- }
- /* Inform the client a hash has been found */
- static void
- inform_hash_found (struct GetContext *ctx,
- struct GNUNET_TIME_Absolute exp,
- size_t size,
- const void *data)
- {
- struct GNUNET_SERVICE_Client *client;
- /* TODO: send a REHASH_ResultMessage */
- struct GNUNET_MQ_Envelope *ev;
- struct REHASH_ResultMessage *msg;
- /* Prevent buffer overflow. */
- if (size > MAGIC) {
- GNUNET_break (0);
- return;
- }
- client = ctx->client->client;
- /* Inform the client of the found message */
- ev = GNUNET_MQ_msg_extra (msg, size, GNUNET_MESSAGE_TYPE_REHASH_CLIENT_RESULT);
- msg->request_id = ctx->request_id;
- msg->output_length = htonl (size);
- msg->exp = GNUNET_TIME_absolute_hton (exp);
- memcpy(&msg[1], data, size);
- GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), ev);
- }
- static void
- dht_get_iterator(void *cls,
- struct GNUNET_TIME_Absolute exp,
- const struct GNUNET_HashCode *key,
- const struct GNUNET_PeerIdentity *get_path,
- unsigned int get_path_length,
- const struct GNUNET_PeerIdentity *put_path,
- unsigned int put_path_length,
- enum GNUNET_BLOCK_Type type,
- size_t size,
- const void *data)
- {
- /* TODO: who should free get_path, put_path?*/
- struct GetContext *ctx;
- ctx = cls;
- GNUNET_assert (ctx->ctx == SERVER_CONTEXT_GET);
- inform_hash_found (ctx, exp, size, data);
- }
- /**
- * Callback for when a hash->hash mapping has been found
- * in the datastore.
- */
- static void
- datastore_get_cb (void *cls,
- const struct GNUNET_HashCode *key,
- size_t size,
- const void *data,
- enum GNUNET_BLOCK_Type type,
- uint32_t priority,
- uint32_t anonymity,
- uint32_t replication,
- struct GNUNET_TIME_Absolute expiration,
- uint64_t uid)
- {
- struct GetContext *ctx;
- GNUNET_assert (ctx->ctx == SERVER_CONTEXT_GET);
- /* TODO should ctx->datastore_get be freed? */
- ctx->datastore_get = NULL;
- if ((data == NULL) || (key == NULL))
- /* No entry found!
- Or our request was dropped, not sure. */
- /* TODO figure out exact condition */
- return;
- /* prevent overflow*/
- if (size > MAGIC)
- return;
- inform_hash_found (ctx, expiration, size, data);
- }
- static void
- handle_get (void *cls, const struct REHASH_GetMessage *msg)
- {
- int ret;
- struct GetContext *ctx;
- struct ClientContext *c;
- struct GNUNET_DHT_GetHandle *h;
- struct GNUNET_HashCode key;
- c = cls;
- /* TODO: define protocols for anonymous GET */
- GNUNET_assert(ntohl(msg->anonymity_level) == 0);
- GNUNET_assert(0 && "understood get!");
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Client started a search for a hash\n");
- if (GNUNET_OK !=
- REHASH_obfuscated_query_from_hash
- (ntohl(msg->out_type),
- ntohl(msg->in_type),
- (const char *) &msg[1],
- ntohl(msg->input_length),
- &key))
- /* TODO bweh? disconnect? */
- return;
- /* TODO: desired replication level */
- ctx = GNUNET_new(struct GetContext);
- *ctx = (struct GetContext) {};
- ctx->ctx = SERVER_CONTEXT_GET;
- ctx->client = c;
- ctx->request_id = msg->request_id;
- /* Remember the request */
- ret = GNUNET_CONTAINER_multihashmap32_put
- (c->requests, ctx->request_id, ctx,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
- GNUNET_assert (ret == GNUNET_OK);
- /* Start looking in the DHT
- TODO: maybe look in the datastore first
- before bothering the network? */
- ctx->dht_get = REHASH_dht_get_start
- (dht_handle, &key, 1, GNUNET_DHT_RO_NONE,
- &dht_get_iterator, ctx);
- /* Start looking in the datastore.
- Technically redundant, as the mappings
- in the datastore are periodically
- entered into the DHT. However, there
- are realistic situations where waiting
- on the DHT to catch up may not be ideal:
- E.g. the DHT service (or dhtcache maybe?)
- might have recently restarted (e.g. due to a
- system upgrade) and the peer isn't connected
- to useful other peers (e.g. network is down). */
- ctx->datastore_get = GNUNET_DATASTORE_get_key
- (ds_handle, 0, 0, &key,
- GNUNET_BLOCK_TYPE_REHASH,
- /* FIXME priority and what's this about dropping requests? */
- 100,
- 100,
- &datastore_get_cb,
- cls);
- /* Note: datastore_get can be NULL */
- /* Allow more queries, and possibly aborts */
- GNUNET_SERVICE_client_continue (ctx->client->client);
- /* TODO: free h eventually */
- }
- static int
- check_get_stop (void *cls, const struct REHASH_GetStopMessage *msg)
- {
- GNUNET_assert(0);
- }
- static void
- handle_get_stop (void *cls, const struct REHASH_GetStopMessage *msg)
- {
- }
- static int
- check_put (void *cls, const struct REHASH_PutMessage *msg)
- {
- struct ClientContext *ctx;
- uint32_t input_length;
- uint32_t output_length;
- ctx = cls;
- input_length = ntohl(msg->input_length);
- output_length = ntohl(msg->output_length);
- /* Prevent overflow TODO no magic values
- TODO not really invalid necessarily */
- if (input_length > MAGIC)
- return GNUNET_SYSERR;
- if (output_length > MAGIC)
- return GNUNET_SYSERR;
- if (input_length + output_length + sizeof(*msg)
- != ntohs(msg->header.size))
- return GNUNET_SYSERR;
- /* Detect duplicate message ids */
- if (GNUNET_CONTAINER_multihashmap32_contains (ctx->requests, msg->request_id))
- /* TODO check if GNUNET_break below is redundant */
- return GNUNET_SYSERR;
- /* TODO prevent saving of hashes of incorrect lengths */
- return GNUNET_OK;
- }
- /* A part of a hash->hash insertion seems to have
- succeeded, perhaps tell that to the client? */
- static void
- perhaps_complete_put (struct PutContext *ctx)
- {
- struct REHASH_PutStatusMessage *msg;
- struct GNUNET_SERVICE_Client *client;
- struct GNUNET_MQ_Envelope *ev;
- uint32_t request_id;
- if (ctx->datastore_put || ctx->dht_put)
- /* Still something to do! */
- return;
- /* Done! (This actually is somewhat optimistic,
- as the DHT and datastore service may perform
- the actual insertion in the background.
- The status update is still useful though:
- consider a misconfigured system (or a memory loaded
- system where the OOM killer killed some
- critical GNUnet services).
- In that case, it is useful for the client to know if
- an insertion was ‘probably successful’,.
- even if there is a tiny race window. Don't expect
- hard guarantees of rehash, use a database or
- file system instead if these are required. )*/
- client = ctx->client->client;
- request_id = ctx->request_id;
- GNUNET_free (ctx);
- /* Inform the client of the found message */
- /* TODO ordering abort / done messages.
- What if the client just sent an abort?*/
- ev = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_REHASH_PUT_DONE);
- msg->request_id = request_id;
- msg->flags = htonl(REHASH_PUT_COMPLETED);
- /* ev is consumed by GNUNET_MQ_send */
- GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), ev);
- }
- static void
- datastore_put_cb (void *cls,
- int32_t success,
- struct GNUNET_TIME_Absolute min_expiration,
- const char *msg)
- {
- /* TODO: do something I guess?
- SYSERR: failure
- NO: already exists
- YES: exists
- msg: error message */
- struct PutContext *ctx;
- ctx = cls;
- /* TODO should this be aborted? */
- ctx->datastore_put = NULL;
- perhaps_complete_put (ctx);
- }
- static void
- dht_put_cb (void *cls)
- {
- struct PutContext *ctx;
- ctx = cls;
- /* This has been freed */
- ctx->dht_put = NULL;
- perhaps_complete_put (ctx);
- }
- static void
- handle_put (void *cls, const struct REHASH_PutMessage *msg)
- {
- int ret;
- struct ClientContext *client;
- struct PutContext *ctx;
- struct GNUNET_HashCode query;
- char *dest;
- const char *out_data;
- const char *in_data;
- ssize_t expected_length;
- ssize_t serialised_length;
- GNUNET_assert(ntohl(msg->anonymity_level) == 0);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Client entered a hash->hash mapping to save\n");
- /* TODO also put into datastore */
- if (GNUNET_OK !=
- REHASH_obfuscated_query_from_hash
- (ntohl(msg->out_type),
- ntohl(msg->in_type),
- (const char *) &msg[1],
- ntohl(msg->input_length),
- &query))
- {
- /* TODO: ? TODO */
- GNUNET_break(0);
- return;
- }
- /* Prepare data to put into the DHT and datastore */
- in_data = (const char *) &msg[1];
- out_data = in_data + ntohl(msg->input_length);
- expected_length = REHASH_data_size_for_mapping
- (ntohl(msg->output_length));
- dest = GNUNET_malloc (expected_length);
- serialised_length = REHASH_data_for_mapping
- (ntohl(msg->out_type),
- ntohl(msg->in_type),
- out_data,
- in_data,
- ntohl(msg->output_length),
- ntohl(msg->input_length),
- dest,
- expected_length);
- if (serialised_length != expected_length)
- {
- GNUNET_free (dest);
- GNUNET_break (0);
- /* TODO error message */
- /* TODO interaction with aborting */
- /* FIXME either disconnect the client
- or resume the client! */
- return;
- }
- client = cls;
- /* Allow aborting the request */
- ctx = GNUNET_new(struct PutContext);
- *ctx = (struct PutContext) {};
- ctx->ctx = SERVER_CONTEXT_PUT;
- ctx->request_id = msg->request_id;
- ctx->client = client;
- /* Remember the request */
- ret = GNUNET_CONTAINER_multihashmap32_put
- (client->requests, ctx->request_id, ctx,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
- GNUNET_assert (ret == GNUNET_OK);
- /* Put data into the DHT */
- ctx->dht_put = REHASH_dht_put
- (dht_handle,
- &query,
- ntohl(msg->replication_level),
- GNUNET_DHT_RO_NONE,
- serialised_length,
- dest,
- GNUNET_TIME_absolute_ntoh(msg->expiration_time),
- /* TODO callback */
- &dht_put_cb,
- ctx);
- ctx->datastore_put = GNUNET_DATASTORE_put
- (ds_handle,
- 0 /* reservation id*/,
- &query,
- serialised_length,
- dest,
- GNUNET_BLOCK_TYPE_REHASH,
- /* FIXME priority */
- 1000 /* ntohl (msg->priority) */,
- ntohl (msg->anonymity_level),
- ntohl (msg->replication_level),
- GNUNET_TIME_absolute_ntoh (msg->expiration_time),
- /* XXX ??? queue properties */
- 100,
- 100,
- &datastore_put_cb,
- ctx);
- /* TODO: has dest to be kept not-freed
- while datastore_put and dht_put are running? */
- GNUNET_free (dest);
- /* TODO free h eventually (at abort perhaps?) */
- GNUNET_SERVICE_client_continue (client->client);
- }
- GNUNET_SERVICE_MAIN
- ("rehash",
- GNUNET_SERVICE_OPTION_NONE,
- &init_cb,
- &connect_cb,
- &disconnect_cb,
- NULL,
- /* TODO MQ handlers! */
- GNUNET_MQ_hd_var_size (get,
- GNUNET_MESSAGE_TYPE_REHASH_CLIENT_GET,
- struct REHASH_GetMessage,
- NULL),
- GNUNET_MQ_hd_var_size (get_stop,
- GNUNET_MESSAGE_TYPE_REHASH_CLIENT_GET_STOP,
- struct REHASH_GetStopMessage,
- NULL),
- GNUNET_MQ_hd_var_size (put,
- GNUNET_MESSAGE_TYPE_REHASH_CLIENT_PUT,
- struct REHASH_PutMessage,
- NULL),
- GNUNET_MQ_handler_end ());
|