rehash-service.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. /* rehash --- a decentralised hash<->hash store
  2. Copyright © 2020 Maxime Devos <maxime.devos@student.kuleuven.be>
  3. This file is part of rehash.
  4. rehash is free software; you can redistribute it and/or modify it
  5. under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 3 of the License, or (at
  7. your option) any later version.
  8. rehash is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with rehash. If not, see <http://www.gnu.org/licenses/>. */
  14. #include "platform.h"
  15. #include <stdio.h>
  16. #include <gnunet/gnunet_service_lib.h>
  17. #include <gnunet/gnunet_container_lib.h>
  18. #include <gnunet/gnunet_dht_service.h>
  19. #include <gnunet/gnunet_datastore_service.h>
  20. #include "rehash_service.h"
  21. #include "extra_gnunet_protocols.h"
  22. #include "rehash.h"
  23. #include "rehash_crypto.h"
  24. #include "rehash_dht.h"
  25. /* FIXME no magic constants
  26. An upper bound on hash sizes found in the wild. */
  27. #define MAGIC 64
  28. static struct GNUNET_DHT_Handle *dht_handle;
  29. static struct GNUNET_DATASTORE_Handle *ds_handle;
  30. /* TODO bring old data back into the DHT */
  31. /* TODO monitor the network for known bad hashes,
  32. and kill them when passing the local peer. */
  33. enum ServerContextType
  34. {
  35. SERVER_CONTEXT_GET,
  36. SERVER_CONTEXT_PUT,
  37. };
  38. struct ClientContext;
  39. /** Information about in-process hash->hash lookup
  40. requests. */
  41. struct GetContext
  42. {
  43. enum ServerContextType ctx;
  44. uint32_t request_id;
  45. struct ClientContext *client;
  46. /* TODO the API allows controlling
  47. where to find hashes ( )*/
  48. /* Never NULL actually. */
  49. struct GNUNET_DHT_GetHandle *dht_get;
  50. /* If NULL, the input hash was not present
  51. in the datastore. */
  52. struct GNUNET_DATASTORE_QueueEntry *datastore_get;
  53. };
  54. /** Information about in-process hash->hash insertions. */
  55. struct PutContext
  56. {
  57. enum ServerContextType ctx;
  58. uint32_t request_id;
  59. struct ClientContext *client;
  60. /* If NULL, the mapping has been send onto
  61. the DHT. Once datastore_put is completed,
  62. the task FIXME??? will periodically
  63. reenter the hashes into the DHT. */
  64. struct GNUNET_DHT_PutHandle *dht_put;
  65. /* If NULL, the mapping has been inserted into
  66. the datastore. */
  67. struct GNUNET_DATASTORE_QueueEntry *datastore_put;
  68. };
  69. struct ClientContext
  70. {
  71. struct GNUNET_SERVICE_Client *client;
  72. /* Requests from C->S */
  73. struct GNUNET_CONTAINER_MultiHashMap32 *requests;
  74. };
  75. /**
  76. * Callback run when shutting down the rehash service
  77. */
  78. static void
  79. shutdown_task (void *cls)
  80. {
  81. /* FIXME perhaps let store requests complete first? */
  82. GNUNET_DHT_disconnect (dht_handle);
  83. GNUNET_DATASTORE_disconnect (ds_handle, GNUNET_NO);
  84. /* prevent accidental use-after-free */
  85. dht_handle = NULL;
  86. ds_handle = NULL;
  87. }
  88. /**
  89. * Callback to initialise the rehash service
  90. */
  91. static void
  92. init_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg,
  93. struct GNUNET_SERVICE_Handle *sh)
  94. {
  95. /* TODO: reasonable length */
  96. dht_handle = GNUNET_DHT_connect (cfg, 16);
  97. ds_handle = GNUNET_DATASTORE_connect (cfg);
  98. /* TODO: what to do in these cases,
  99. and is this possible? */
  100. GNUNET_assert(dht_handle != NULL);
  101. GNUNET_assert(ds_handle != NULL);
  102. GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
  103. }
  104. /**
  105. * Callback for when a client connects to the service.
  106. */
  107. static void *
  108. connect_cb(void *cls, struct GNUNET_SERVICE_Client *c,
  109. struct GNUNET_MQ_Handle *mq)
  110. {
  111. struct ClientContext *ctx;
  112. ctx = GNUNET_new(struct ClientContext);
  113. ctx->client = c;
  114. /* length is a wild guess */
  115. ctx->requests = GNUNET_CONTAINER_multihashmap32_create (8);
  116. /* TODO when is this possible */
  117. GNUNET_assert(ctx->requests != NULL);
  118. return ctx;
  119. }
  120. /**
  121. * Callback for when a client is disconnected from the service.
  122. */
  123. static void
  124. disconnect_cb(void *cls, struct GNUNET_SERVICE_Client *c, void *internal_cls)
  125. {
  126. /* TODO free elements of cls->requests, abort requests ... */
  127. }
  128. static int
  129. check_get (void *cls, const struct REHASH_GetMessage *msg)
  130. {
  131. struct ClientContext *ctx;
  132. uint16_t header_size;
  133. uint32_t input_length; /* TODO perhaps uint16_t */
  134. ctx = cls;
  135. header_size = ntohs(msg->header.size);
  136. input_length = ntohl(msg->input_length);
  137. if (header_size - sizeof(struct REHASH_GetMessage) != input_length)
  138. {
  139. GNUNET_break(0);
  140. return GNUNET_SYSERR;
  141. }
  142. /* Detect duplicate message ids */
  143. if (GNUNET_CONTAINER_multihashmap32_contains (ctx->requests, msg->request_id))
  144. /* TODO check if GNUNET_break below is redundant */
  145. return GNUNET_SYSERR;
  146. /* Detect unsupported options */
  147. if (ntohl(msg->options) > 1)
  148. {
  149. GNUNET_break(0);
  150. return GNUNET_SYSERR;
  151. }
  152. return GNUNET_OK;
  153. }
  154. /* Inform the client a hash has been found */
  155. static void
  156. inform_hash_found (struct GetContext *ctx,
  157. struct GNUNET_TIME_Absolute exp,
  158. size_t size,
  159. const void *data)
  160. {
  161. struct GNUNET_SERVICE_Client *client;
  162. /* TODO: send a REHASH_ResultMessage */
  163. struct GNUNET_MQ_Envelope *ev;
  164. struct REHASH_ResultMessage *msg;
  165. /* Prevent buffer overflow. */
  166. if (size > MAGIC) {
  167. GNUNET_break (0);
  168. return;
  169. }
  170. client = ctx->client->client;
  171. /* Inform the client of the found message */
  172. ev = GNUNET_MQ_msg_extra (msg, size, GNUNET_MESSAGE_TYPE_REHASH_CLIENT_RESULT);
  173. msg->request_id = ctx->request_id;
  174. msg->output_length = htonl (size);
  175. msg->exp = GNUNET_TIME_absolute_hton (exp);
  176. memcpy(&msg[1], data, size);
  177. GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), ev);
  178. }
  179. static void
  180. dht_get_iterator(void *cls,
  181. struct GNUNET_TIME_Absolute exp,
  182. const struct GNUNET_HashCode *key,
  183. const struct GNUNET_PeerIdentity *get_path,
  184. unsigned int get_path_length,
  185. const struct GNUNET_PeerIdentity *put_path,
  186. unsigned int put_path_length,
  187. enum GNUNET_BLOCK_Type type,
  188. size_t size,
  189. const void *data)
  190. {
  191. /* TODO: who should free get_path, put_path?*/
  192. struct GetContext *ctx;
  193. ctx = cls;
  194. GNUNET_assert (ctx->ctx == SERVER_CONTEXT_GET);
  195. inform_hash_found (ctx, exp, size, data);
  196. }
  197. /**
  198. * Callback for when a hash->hash mapping has been found
  199. * in the datastore.
  200. */
  201. static void
  202. datastore_get_cb (void *cls,
  203. const struct GNUNET_HashCode *key,
  204. size_t size,
  205. const void *data,
  206. enum GNUNET_BLOCK_Type type,
  207. uint32_t priority,
  208. uint32_t anonymity,
  209. uint32_t replication,
  210. struct GNUNET_TIME_Absolute expiration,
  211. uint64_t uid)
  212. {
  213. struct GetContext *ctx;
  214. GNUNET_assert (ctx->ctx == SERVER_CONTEXT_GET);
  215. /* TODO should ctx->datastore_get be freed? */
  216. ctx->datastore_get = NULL;
  217. if ((data == NULL) || (key == NULL))
  218. /* No entry found!
  219. Or our request was dropped, not sure. */
  220. /* TODO figure out exact condition */
  221. return;
  222. /* prevent overflow*/
  223. if (size > MAGIC)
  224. return;
  225. inform_hash_found (ctx, expiration, size, data);
  226. }
  227. static void
  228. handle_get (void *cls, const struct REHASH_GetMessage *msg)
  229. {
  230. int ret;
  231. struct GetContext *ctx;
  232. struct ClientContext *c;
  233. struct GNUNET_DHT_GetHandle *h;
  234. struct GNUNET_HashCode key;
  235. c = cls;
  236. /* TODO: define protocols for anonymous GET */
  237. GNUNET_assert(ntohl(msg->anonymity_level) == 0);
  238. GNUNET_assert(0 && "understood get!");
  239. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  240. "Client started a search for a hash\n");
  241. if (GNUNET_OK !=
  242. REHASH_obfuscated_query_from_hash
  243. (ntohl(msg->out_type),
  244. ntohl(msg->in_type),
  245. (const char *) &msg[1],
  246. ntohl(msg->input_length),
  247. &key))
  248. /* TODO bweh? disconnect? */
  249. return;
  250. /* TODO: desired replication level */
  251. ctx = GNUNET_new(struct GetContext);
  252. *ctx = (struct GetContext) {};
  253. ctx->ctx = SERVER_CONTEXT_GET;
  254. ctx->client = c;
  255. ctx->request_id = msg->request_id;
  256. /* Remember the request */
  257. ret = GNUNET_CONTAINER_multihashmap32_put
  258. (c->requests, ctx->request_id, ctx,
  259. GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
  260. GNUNET_assert (ret == GNUNET_OK);
  261. /* Start looking in the DHT
  262. TODO: maybe look in the datastore first
  263. before bothering the network? */
  264. ctx->dht_get = REHASH_dht_get_start
  265. (dht_handle, &key, 1, GNUNET_DHT_RO_NONE,
  266. &dht_get_iterator, ctx);
  267. /* Start looking in the datastore.
  268. Technically redundant, as the mappings
  269. in the datastore are periodically
  270. entered into the DHT. However, there
  271. are realistic situations where waiting
  272. on the DHT to catch up may not be ideal:
  273. E.g. the DHT service (or dhtcache maybe?)
  274. might have recently restarted (e.g. due to a
  275. system upgrade) and the peer isn't connected
  276. to useful other peers (e.g. network is down). */
  277. ctx->datastore_get = GNUNET_DATASTORE_get_key
  278. (ds_handle, 0, 0, &key,
  279. GNUNET_BLOCK_TYPE_REHASH,
  280. /* FIXME priority and what's this about dropping requests? */
  281. 100,
  282. 100,
  283. &datastore_get_cb,
  284. cls);
  285. /* Note: datastore_get can be NULL */
  286. /* Allow more queries, and possibly aborts */
  287. GNUNET_SERVICE_client_continue (ctx->client->client);
  288. /* TODO: free h eventually */
  289. }
  290. static int
  291. check_get_stop (void *cls, const struct REHASH_GetStopMessage *msg)
  292. {
  293. GNUNET_assert(0);
  294. }
  295. static void
  296. handle_get_stop (void *cls, const struct REHASH_GetStopMessage *msg)
  297. {
  298. }
  299. static int
  300. check_put (void *cls, const struct REHASH_PutMessage *msg)
  301. {
  302. struct ClientContext *ctx;
  303. uint32_t input_length;
  304. uint32_t output_length;
  305. ctx = cls;
  306. input_length = ntohl(msg->input_length);
  307. output_length = ntohl(msg->output_length);
  308. /* Prevent overflow TODO no magic values
  309. TODO not really invalid necessarily */
  310. if (input_length > MAGIC)
  311. return GNUNET_SYSERR;
  312. if (output_length > MAGIC)
  313. return GNUNET_SYSERR;
  314. if (input_length + output_length + sizeof(*msg)
  315. != ntohs(msg->header.size))
  316. return GNUNET_SYSERR;
  317. /* Detect duplicate message ids */
  318. if (GNUNET_CONTAINER_multihashmap32_contains (ctx->requests, msg->request_id))
  319. /* TODO check if GNUNET_break below is redundant */
  320. return GNUNET_SYSERR;
  321. /* TODO prevent saving of hashes of incorrect lengths */
  322. return GNUNET_OK;
  323. }
  324. /* A part of a hash->hash insertion seems to have
  325. succeeded, perhaps tell that to the client? */
  326. static void
  327. perhaps_complete_put (struct PutContext *ctx)
  328. {
  329. struct REHASH_PutStatusMessage *msg;
  330. struct GNUNET_SERVICE_Client *client;
  331. struct GNUNET_MQ_Envelope *ev;
  332. uint32_t request_id;
  333. if (ctx->datastore_put || ctx->dht_put)
  334. /* Still something to do! */
  335. return;
  336. /* Done! (This actually is somewhat optimistic,
  337. as the DHT and datastore service may perform
  338. the actual insertion in the background.
  339. The status update is still useful though:
  340. consider a misconfigured system (or a memory loaded
  341. system where the OOM killer killed some
  342. critical GNUnet services).
  343. In that case, it is useful for the client to know if
  344. an insertion was ‘probably successful’,.
  345. even if there is a tiny race window. Don't expect
  346. hard guarantees of rehash, use a database or
  347. file system instead if these are required. )*/
  348. client = ctx->client->client;
  349. request_id = ctx->request_id;
  350. GNUNET_free (ctx);
  351. /* Inform the client of the found message */
  352. /* TODO ordering abort / done messages.
  353. What if the client just sent an abort?*/
  354. ev = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_REHASH_PUT_DONE);
  355. msg->request_id = request_id;
  356. msg->flags = htonl(REHASH_PUT_COMPLETED);
  357. /* ev is consumed by GNUNET_MQ_send */
  358. GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), ev);
  359. }
  360. static void
  361. datastore_put_cb (void *cls,
  362. int32_t success,
  363. struct GNUNET_TIME_Absolute min_expiration,
  364. const char *msg)
  365. {
  366. /* TODO: do something I guess?
  367. SYSERR: failure
  368. NO: already exists
  369. YES: exists
  370. msg: error message */
  371. struct PutContext *ctx;
  372. ctx = cls;
  373. /* TODO should this be aborted? */
  374. ctx->datastore_put = NULL;
  375. perhaps_complete_put (ctx);
  376. }
  377. static void
  378. dht_put_cb (void *cls)
  379. {
  380. struct PutContext *ctx;
  381. ctx = cls;
  382. /* This has been freed */
  383. ctx->dht_put = NULL;
  384. perhaps_complete_put (ctx);
  385. }
  386. static void
  387. handle_put (void *cls, const struct REHASH_PutMessage *msg)
  388. {
  389. int ret;
  390. struct ClientContext *client;
  391. struct PutContext *ctx;
  392. struct GNUNET_HashCode query;
  393. char *dest;
  394. const char *out_data;
  395. const char *in_data;
  396. ssize_t expected_length;
  397. ssize_t serialised_length;
  398. GNUNET_assert(ntohl(msg->anonymity_level) == 0);
  399. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  400. "Client entered a hash->hash mapping to save\n");
  401. /* TODO also put into datastore */
  402. if (GNUNET_OK !=
  403. REHASH_obfuscated_query_from_hash
  404. (ntohl(msg->out_type),
  405. ntohl(msg->in_type),
  406. (const char *) &msg[1],
  407. ntohl(msg->input_length),
  408. &query))
  409. {
  410. /* TODO: ? TODO */
  411. GNUNET_break(0);
  412. return;
  413. }
  414. /* Prepare data to put into the DHT and datastore */
  415. in_data = (const char *) &msg[1];
  416. out_data = in_data + ntohl(msg->input_length);
  417. expected_length = REHASH_data_size_for_mapping
  418. (ntohl(msg->output_length));
  419. dest = GNUNET_malloc (expected_length);
  420. serialised_length = REHASH_data_for_mapping
  421. (ntohl(msg->out_type),
  422. ntohl(msg->in_type),
  423. out_data,
  424. in_data,
  425. ntohl(msg->output_length),
  426. ntohl(msg->input_length),
  427. dest,
  428. expected_length);
  429. if (serialised_length != expected_length)
  430. {
  431. GNUNET_free (dest);
  432. GNUNET_break (0);
  433. /* TODO error message */
  434. /* TODO interaction with aborting */
  435. /* FIXME either disconnect the client
  436. or resume the client! */
  437. return;
  438. }
  439. client = cls;
  440. /* Allow aborting the request */
  441. ctx = GNUNET_new(struct PutContext);
  442. *ctx = (struct PutContext) {};
  443. ctx->ctx = SERVER_CONTEXT_PUT;
  444. ctx->request_id = msg->request_id;
  445. ctx->client = client;
  446. /* Remember the request */
  447. ret = GNUNET_CONTAINER_multihashmap32_put
  448. (client->requests, ctx->request_id, ctx,
  449. GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
  450. GNUNET_assert (ret == GNUNET_OK);
  451. /* Put data into the DHT */
  452. ctx->dht_put = REHASH_dht_put
  453. (dht_handle,
  454. &query,
  455. ntohl(msg->replication_level),
  456. GNUNET_DHT_RO_NONE,
  457. serialised_length,
  458. dest,
  459. GNUNET_TIME_absolute_ntoh(msg->expiration_time),
  460. /* TODO callback */
  461. &dht_put_cb,
  462. ctx);
  463. ctx->datastore_put = GNUNET_DATASTORE_put
  464. (ds_handle,
  465. 0 /* reservation id*/,
  466. &query,
  467. serialised_length,
  468. dest,
  469. GNUNET_BLOCK_TYPE_REHASH,
  470. /* FIXME priority */
  471. 1000 /* ntohl (msg->priority) */,
  472. ntohl (msg->anonymity_level),
  473. ntohl (msg->replication_level),
  474. GNUNET_TIME_absolute_ntoh (msg->expiration_time),
  475. /* XXX ??? queue properties */
  476. 100,
  477. 100,
  478. &datastore_put_cb,
  479. ctx);
  480. /* TODO: has dest to be kept not-freed
  481. while datastore_put and dht_put are running? */
  482. GNUNET_free (dest);
  483. /* TODO free h eventually (at abort perhaps?) */
  484. GNUNET_SERVICE_client_continue (client->client);
  485. }
  486. GNUNET_SERVICE_MAIN
  487. ("rehash",
  488. GNUNET_SERVICE_OPTION_NONE,
  489. &init_cb,
  490. &connect_cb,
  491. &disconnect_cb,
  492. NULL,
  493. /* TODO MQ handlers! */
  494. GNUNET_MQ_hd_var_size (get,
  495. GNUNET_MESSAGE_TYPE_REHASH_CLIENT_GET,
  496. struct REHASH_GetMessage,
  497. NULL),
  498. GNUNET_MQ_hd_var_size (get_stop,
  499. GNUNET_MESSAGE_TYPE_REHASH_CLIENT_GET_STOP,
  500. struct REHASH_GetStopMessage,
  501. NULL),
  502. GNUNET_MQ_hd_var_size (put,
  503. GNUNET_MESSAGE_TYPE_REHASH_CLIENT_PUT,
  504. struct REHASH_PutMessage,
  505. NULL),
  506. GNUNET_MQ_handler_end ());