plugin_namecache_flat.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. /*
  2. * This file is part of GNUnet
  3. * Copyright (C) 2009-2015 GNUnet e.V.
  4. *
  5. * GNUnet is free software: you can redistribute it and/or modify it
  6. * under the terms of the GNU Affero General Public License as published
  7. * by the Free Software Foundation, either version 3 of the License,
  8. * or (at your option) any later version.
  9. *
  10. * GNUnet is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. SPDX-License-Identifier: AGPL3.0-or-later
  18. */
  19. /**
  20. * @file namecache/plugin_namecache_flat.c
  21. * @brief flat file-based namecache backend
  22. * @author Martin Schanzenbach
  23. */
  24. #include "platform.h"
  25. #include "gnunet_namecache_plugin.h"
  26. #include "gnunet_namecache_service.h"
  27. #include "gnunet_gnsrecord_lib.h"
  28. #include "namecache.h"
  29. /**
  30. * Context for all functions in this plugin.
  31. */
  32. struct Plugin
  33. {
  34. const struct GNUNET_CONFIGURATION_Handle *cfg;
  35. /**
  36. * Database filename.
  37. */
  38. char *fn;
  39. /**
  40. * HashMap
  41. */
  42. struct GNUNET_CONTAINER_MultiHashMap *hm;
  43. };
  44. struct FlatFileEntry
  45. {
  46. /**
  47. * Block
  48. */
  49. struct GNUNET_GNSRECORD_Block *block;
  50. /**
  51. * query
  52. */
  53. struct GNUNET_HashCode query;
  54. };
  55. /**
  56. * Initialize the database connections and associated
  57. * data structures (create tables and indices
  58. * as needed as well).
  59. *
  60. * @param plugin the plugin context (state for this module)
  61. * @return #GNUNET_OK on success
  62. */
  63. static int
  64. database_setup (struct Plugin *plugin)
  65. {
  66. char *afsdir;
  67. char* block_buffer;
  68. char* buffer;
  69. char* line;
  70. char* query;
  71. char* block;
  72. size_t size;
  73. struct FlatFileEntry *entry;
  74. struct GNUNET_DISK_FileHandle *fh;
  75. if (GNUNET_OK !=
  76. GNUNET_CONFIGURATION_get_value_filename (plugin->cfg,
  77. "namecache-flat",
  78. "FILENAME",
  79. &afsdir))
  80. {
  81. GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
  82. "namecache-flat", "FILENAME");
  83. return GNUNET_SYSERR;
  84. }
  85. if (GNUNET_OK != GNUNET_DISK_file_test (afsdir))
  86. {
  87. if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (afsdir))
  88. {
  89. GNUNET_break (0);
  90. GNUNET_free (afsdir);
  91. return GNUNET_SYSERR;
  92. }
  93. }
  94. /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
  95. plugin->fn = afsdir;
  96. /* Load data from file into hashmap */
  97. plugin->hm = GNUNET_CONTAINER_multihashmap_create (10,
  98. GNUNET_NO);
  99. fh = GNUNET_DISK_file_open (afsdir,
  100. GNUNET_DISK_OPEN_CREATE |
  101. GNUNET_DISK_OPEN_READWRITE,
  102. GNUNET_DISK_PERM_USER_WRITE |
  103. GNUNET_DISK_PERM_USER_READ);
  104. if (NULL == fh)
  105. {
  106. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  107. _("Unable to initialize file: %s.\n"),
  108. afsdir);
  109. return GNUNET_SYSERR;
  110. }
  111. if (GNUNET_SYSERR == GNUNET_DISK_file_size (afsdir,
  112. &size,
  113. GNUNET_YES,
  114. GNUNET_YES))
  115. {
  116. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  117. _("Unable to get filesize: %s.\n"),
  118. afsdir);
  119. GNUNET_DISK_file_close (fh);
  120. return GNUNET_SYSERR;
  121. }
  122. if (0 == size)
  123. {
  124. GNUNET_DISK_file_close (fh);
  125. return GNUNET_OK;
  126. }
  127. buffer = GNUNET_malloc (size + 1);
  128. if (GNUNET_SYSERR == GNUNET_DISK_file_read (fh,
  129. buffer,
  130. size))
  131. {
  132. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  133. _("Unable to read file: %s.\n"),
  134. afsdir);
  135. GNUNET_free (buffer);
  136. GNUNET_DISK_file_close (fh);
  137. return GNUNET_SYSERR;
  138. }
  139. buffer[size] = '\0';
  140. GNUNET_DISK_file_close (fh);
  141. if (0 < size)
  142. {
  143. line = strtok (buffer, "\n");
  144. while (line != NULL) {
  145. query = strtok (line, ",");
  146. if (NULL == query)
  147. break;
  148. block = strtok (NULL, ",");
  149. if (NULL == block)
  150. break;
  151. line = strtok (NULL, "\n");
  152. entry = GNUNET_malloc (sizeof (struct FlatFileEntry));
  153. GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_hash_from_string (query,
  154. &entry->query));
  155. GNUNET_STRINGS_base64_decode (block,
  156. strlen (block),
  157. (void**)&block_buffer);
  158. entry->block = (struct GNUNET_GNSRECORD_Block *) block_buffer;
  159. if (GNUNET_OK !=
  160. GNUNET_CONTAINER_multihashmap_put (plugin->hm,
  161. &entry->query,
  162. entry,
  163. GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
  164. {
  165. GNUNET_free (entry);
  166. GNUNET_break (0);
  167. }
  168. }
  169. }
  170. GNUNET_free (buffer);
  171. return GNUNET_OK;
  172. }
  173. /**
  174. * Store values in hashmap in file and free data
  175. *
  176. * @param plugin the plugin context
  177. */
  178. static int
  179. store_and_free_entries (void *cls,
  180. const struct GNUNET_HashCode *key,
  181. void *value)
  182. {
  183. struct GNUNET_DISK_FileHandle *fh = cls;
  184. struct FlatFileEntry *entry = value;
  185. char *line;
  186. char *block_b64;
  187. struct GNUNET_CRYPTO_HashAsciiEncoded query;
  188. size_t block_size;
  189. block_size = ntohl (entry->block->purpose.size) +
  190. sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
  191. sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
  192. GNUNET_STRINGS_base64_encode ((char*)entry->block,
  193. block_size,
  194. &block_b64);
  195. GNUNET_CRYPTO_hash_to_enc (&entry->query,
  196. &query);
  197. GNUNET_asprintf (&line,
  198. "%s,%s\n",
  199. (char*)&query,
  200. block_b64);
  201. GNUNET_free (block_b64);
  202. GNUNET_DISK_file_write (fh,
  203. line,
  204. strlen (line));
  205. GNUNET_free (entry->block);
  206. GNUNET_free (entry);
  207. GNUNET_free (line);
  208. return GNUNET_YES;
  209. }
  210. /**
  211. * Shutdown database connection and associate data
  212. * structures.
  213. * @param plugin the plugin context (state for this module)
  214. */
  215. static void
  216. database_shutdown (struct Plugin *plugin)
  217. {
  218. struct GNUNET_DISK_FileHandle *fh;
  219. fh = GNUNET_DISK_file_open (plugin->fn,
  220. GNUNET_DISK_OPEN_CREATE |
  221. GNUNET_DISK_OPEN_TRUNCATE |
  222. GNUNET_DISK_OPEN_READWRITE,
  223. GNUNET_DISK_PERM_USER_WRITE |
  224. GNUNET_DISK_PERM_USER_READ);
  225. if (NULL == fh)
  226. {
  227. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  228. _("Unable to initialize file: %s.\n"),
  229. plugin->fn);
  230. return;
  231. }
  232. GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
  233. &store_and_free_entries,
  234. fh);
  235. GNUNET_CONTAINER_multihashmap_destroy (plugin->hm);
  236. GNUNET_DISK_file_close (fh);
  237. }
  238. static int
  239. expire_blocks (void *cls,
  240. const struct GNUNET_HashCode *key,
  241. void *value)
  242. {
  243. struct Plugin *plugin = cls;
  244. struct FlatFileEntry *entry = value;
  245. struct GNUNET_TIME_Absolute now;
  246. struct GNUNET_TIME_Absolute expiration;
  247. now = GNUNET_TIME_absolute_get ();
  248. expiration = GNUNET_TIME_absolute_ntoh (entry->block->expiration_time);
  249. if (0 == GNUNET_TIME_absolute_get_difference (now,
  250. expiration).rel_value_us)
  251. {
  252. GNUNET_CONTAINER_multihashmap_remove_all (plugin->hm, key);
  253. }
  254. return GNUNET_YES;
  255. }
  256. /**
  257. * Removes any expired block.
  258. *
  259. * @param plugin the plugin
  260. */
  261. static void
  262. namecache_expire_blocks (struct Plugin *plugin)
  263. {
  264. GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
  265. &expire_blocks,
  266. plugin);
  267. }
  268. /**
  269. * Cache a block in the datastore.
  270. *
  271. * @param cls closure (internal context for the plugin)
  272. * @param block block to cache
  273. * @return #GNUNET_OK on success, else #GNUNET_SYSERR
  274. */
  275. static int
  276. namecache_cache_block (void *cls,
  277. const struct GNUNET_GNSRECORD_Block *block)
  278. {
  279. struct Plugin *plugin = cls;
  280. struct GNUNET_HashCode query;
  281. struct FlatFileEntry *entry;
  282. size_t block_size;
  283. namecache_expire_blocks (plugin);
  284. GNUNET_CRYPTO_hash (&block->derived_key,
  285. sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
  286. &query);
  287. block_size = ntohl (block->purpose.size) +
  288. sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
  289. sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
  290. if (block_size > 64 * 65536)
  291. {
  292. GNUNET_break (0);
  293. return GNUNET_SYSERR;
  294. }
  295. entry = GNUNET_malloc (sizeof (struct FlatFileEntry));
  296. entry->block = GNUNET_malloc (block_size);
  297. GNUNET_memcpy (entry->block, block, block_size);
  298. GNUNET_CONTAINER_multihashmap_remove_all (plugin->hm, &query);
  299. if (GNUNET_OK !=
  300. GNUNET_CONTAINER_multihashmap_put (plugin->hm,
  301. &query,
  302. entry,
  303. GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
  304. {
  305. GNUNET_free (entry);
  306. GNUNET_break (0);
  307. return GNUNET_SYSERR;
  308. }
  309. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  310. "Caching block under derived key `%s'\n",
  311. GNUNET_h2s_full (&query));
  312. return GNUNET_OK;
  313. }
  314. /**
  315. * Get the block for a particular zone and label in the
  316. * datastore. Will return at most one result to the iterator.
  317. *
  318. * @param cls closure (internal context for the plugin)
  319. * @param query hash of public key derived from the zone and the label
  320. * @param iter function to call with the result
  321. * @param iter_cls closure for @a iter
  322. * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
  323. */
  324. static int
  325. namecache_lookup_block (void *cls,
  326. const struct GNUNET_HashCode *query,
  327. GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls)
  328. {
  329. struct Plugin *plugin = cls;
  330. const struct GNUNET_GNSRECORD_Block *block;
  331. block = GNUNET_CONTAINER_multihashmap_get (plugin->hm, query);
  332. if (NULL == block)
  333. return GNUNET_NO;
  334. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  335. "Found block under derived key `%s'\n",
  336. GNUNET_h2s_full (query));
  337. iter (iter_cls, block);
  338. return GNUNET_YES;
  339. }
  340. /**
  341. * Entry point for the plugin.
  342. *
  343. * @param cls the "struct GNUNET_NAMECACHE_PluginEnvironment*"
  344. * @return NULL on error, otherwise the plugin context
  345. */
  346. void *
  347. libgnunet_plugin_namecache_flat_init (void *cls)
  348. {
  349. static struct Plugin plugin;
  350. const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
  351. struct GNUNET_NAMECACHE_PluginFunctions *api;
  352. if (NULL != plugin.cfg)
  353. return NULL; /* can only initialize once! */
  354. memset (&plugin, 0, sizeof (struct Plugin));
  355. plugin.cfg = cfg;
  356. if (GNUNET_OK != database_setup (&plugin))
  357. {
  358. database_shutdown (&plugin);
  359. return NULL;
  360. }
  361. api = GNUNET_new (struct GNUNET_NAMECACHE_PluginFunctions);
  362. api->cls = &plugin;
  363. api->cache_block = &namecache_cache_block;
  364. api->lookup_block = &namecache_lookup_block;
  365. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  366. _("flat plugin running\n"));
  367. return api;
  368. }
  369. /**
  370. * Exit point from the plugin.
  371. *
  372. * @param cls the plugin context (as returned by "init")
  373. * @return always NULL
  374. */
  375. void *
  376. libgnunet_plugin_namecache_flat_done (void *cls)
  377. {
  378. struct GNUNET_NAMECACHE_PluginFunctions *api = cls;
  379. struct Plugin *plugin = api->cls;
  380. database_shutdown (plugin);
  381. plugin->cfg = NULL;
  382. GNUNET_free (api);
  383. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  384. "flat plugin is finished\n");
  385. return NULL;
  386. }
  387. /* end of plugin_namecache_sqlite.c */