123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- /*
- * This file is part of GNUnet
- * Copyright (C) 2009-2015 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/>.
- SPDX-License-Identifier: AGPL3.0-or-later
- */
- /**
- * @file namecache/plugin_namecache_flat.c
- * @brief flat file-based namecache backend
- * @author Martin Schanzenbach
- */
- #include "platform.h"
- #include "gnunet_namecache_plugin.h"
- #include "gnunet_namecache_service.h"
- #include "gnunet_gnsrecord_lib.h"
- #include "namecache.h"
- /**
- * Context for all functions in this plugin.
- */
- struct Plugin
- {
- const struct GNUNET_CONFIGURATION_Handle *cfg;
- /**
- * Database filename.
- */
- char *fn;
- /**
- * HashMap
- */
- struct GNUNET_CONTAINER_MultiHashMap *hm;
- };
- struct FlatFileEntry
- {
- /**
- * Block
- */
- struct GNUNET_GNSRECORD_Block *block;
- /**
- * query
- */
- struct GNUNET_HashCode query;
- };
- /**
- * Initialize the database connections and associated
- * data structures (create tables and indices
- * as needed as well).
- *
- * @param plugin the plugin context (state for this module)
- * @return #GNUNET_OK on success
- */
- static int
- database_setup (struct Plugin *plugin)
- {
- char *afsdir;
- char* block_buffer;
- char* buffer;
- char* line;
- char* query;
- char* block;
- size_t size;
- struct FlatFileEntry *entry;
- struct GNUNET_DISK_FileHandle *fh;
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_filename (plugin->cfg,
- "namecache-flat",
- "FILENAME",
- &afsdir))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "namecache-flat", "FILENAME");
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK != GNUNET_DISK_file_test (afsdir))
- {
- if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (afsdir))
- {
- GNUNET_break (0);
- GNUNET_free (afsdir);
- return GNUNET_SYSERR;
- }
- }
- /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
- plugin->fn = afsdir;
- /* Load data from file into hashmap */
- plugin->hm = GNUNET_CONTAINER_multihashmap_create (10,
- GNUNET_NO);
- fh = GNUNET_DISK_file_open (afsdir,
- GNUNET_DISK_OPEN_CREATE |
- GNUNET_DISK_OPEN_READWRITE,
- GNUNET_DISK_PERM_USER_WRITE |
- GNUNET_DISK_PERM_USER_READ);
- if (NULL == fh)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Unable to initialize file: %s.\n"),
- afsdir);
- return GNUNET_SYSERR;
- }
- if (GNUNET_SYSERR == GNUNET_DISK_file_size (afsdir,
- &size,
- GNUNET_YES,
- GNUNET_YES))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Unable to get filesize: %s.\n"),
- afsdir);
- GNUNET_DISK_file_close (fh);
- return GNUNET_SYSERR;
- }
- if (0 == size)
- {
- GNUNET_DISK_file_close (fh);
- return GNUNET_OK;
- }
- buffer = GNUNET_malloc (size + 1);
- if (GNUNET_SYSERR == GNUNET_DISK_file_read (fh,
- buffer,
- size))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Unable to read file: %s.\n"),
- afsdir);
- GNUNET_free (buffer);
- GNUNET_DISK_file_close (fh);
- return GNUNET_SYSERR;
- }
- buffer[size] = '\0';
- GNUNET_DISK_file_close (fh);
- if (0 < size)
- {
- line = strtok (buffer, "\n");
- while (line != NULL) {
- query = strtok (line, ",");
- if (NULL == query)
- break;
- block = strtok (NULL, ",");
- if (NULL == block)
- break;
- line = strtok (NULL, "\n");
- entry = GNUNET_malloc (sizeof (struct FlatFileEntry));
- GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_hash_from_string (query,
- &entry->query));
- GNUNET_STRINGS_base64_decode (block,
- strlen (block),
- (void**)&block_buffer);
- entry->block = (struct GNUNET_GNSRECORD_Block *) block_buffer;
- if (GNUNET_OK !=
- GNUNET_CONTAINER_multihashmap_put (plugin->hm,
- &entry->query,
- entry,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
- {
- GNUNET_free (entry);
- GNUNET_break (0);
- }
- }
- }
- GNUNET_free (buffer);
- return GNUNET_OK;
- }
- /**
- * Store values in hashmap in file and free data
- *
- * @param plugin the plugin context
- */
- static int
- store_and_free_entries (void *cls,
- const struct GNUNET_HashCode *key,
- void *value)
- {
- struct GNUNET_DISK_FileHandle *fh = cls;
- struct FlatFileEntry *entry = value;
- char *line;
- char *block_b64;
- struct GNUNET_CRYPTO_HashAsciiEncoded query;
- size_t block_size;
- block_size = ntohl (entry->block->purpose.size) +
- sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
- sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
- GNUNET_STRINGS_base64_encode ((char*)entry->block,
- block_size,
- &block_b64);
- GNUNET_CRYPTO_hash_to_enc (&entry->query,
- &query);
- GNUNET_asprintf (&line,
- "%s,%s\n",
- (char*)&query,
- block_b64);
- GNUNET_free (block_b64);
- GNUNET_DISK_file_write (fh,
- line,
- strlen (line));
- GNUNET_free (entry->block);
- GNUNET_free (entry);
- GNUNET_free (line);
- return GNUNET_YES;
- }
- /**
- * Shutdown database connection and associate data
- * structures.
- * @param plugin the plugin context (state for this module)
- */
- static void
- database_shutdown (struct Plugin *plugin)
- {
- struct GNUNET_DISK_FileHandle *fh;
- fh = GNUNET_DISK_file_open (plugin->fn,
- GNUNET_DISK_OPEN_CREATE |
- GNUNET_DISK_OPEN_TRUNCATE |
- GNUNET_DISK_OPEN_READWRITE,
- GNUNET_DISK_PERM_USER_WRITE |
- GNUNET_DISK_PERM_USER_READ);
- if (NULL == fh)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Unable to initialize file: %s.\n"),
- plugin->fn);
- return;
- }
- GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
- &store_and_free_entries,
- fh);
- GNUNET_CONTAINER_multihashmap_destroy (plugin->hm);
- GNUNET_DISK_file_close (fh);
- }
- static int
- expire_blocks (void *cls,
- const struct GNUNET_HashCode *key,
- void *value)
- {
- struct Plugin *plugin = cls;
- struct FlatFileEntry *entry = value;
- struct GNUNET_TIME_Absolute now;
- struct GNUNET_TIME_Absolute expiration;
- now = GNUNET_TIME_absolute_get ();
- expiration = GNUNET_TIME_absolute_ntoh (entry->block->expiration_time);
- if (0 == GNUNET_TIME_absolute_get_difference (now,
- expiration).rel_value_us)
- {
- GNUNET_CONTAINER_multihashmap_remove_all (plugin->hm, key);
- }
- return GNUNET_YES;
- }
- /**
- * Removes any expired block.
- *
- * @param plugin the plugin
- */
- static void
- namecache_expire_blocks (struct Plugin *plugin)
- {
- GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
- &expire_blocks,
- plugin);
- }
- /**
- * Cache a block in the datastore.
- *
- * @param cls closure (internal context for the plugin)
- * @param block block to cache
- * @return #GNUNET_OK on success, else #GNUNET_SYSERR
- */
- static int
- namecache_cache_block (void *cls,
- const struct GNUNET_GNSRECORD_Block *block)
- {
- struct Plugin *plugin = cls;
- struct GNUNET_HashCode query;
- struct FlatFileEntry *entry;
- size_t block_size;
- namecache_expire_blocks (plugin);
- GNUNET_CRYPTO_hash (&block->derived_key,
- sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
- &query);
- block_size = ntohl (block->purpose.size) +
- sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
- sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
- if (block_size > 64 * 65536)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- entry = GNUNET_malloc (sizeof (struct FlatFileEntry));
- entry->block = GNUNET_malloc (block_size);
- GNUNET_memcpy (entry->block, block, block_size);
- GNUNET_CONTAINER_multihashmap_remove_all (plugin->hm, &query);
- if (GNUNET_OK !=
- GNUNET_CONTAINER_multihashmap_put (plugin->hm,
- &query,
- entry,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
- {
- GNUNET_free (entry);
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Caching block under derived key `%s'\n",
- GNUNET_h2s_full (&query));
- return GNUNET_OK;
- }
- /**
- * Get the block for a particular zone and label in the
- * datastore. Will return at most one result to the iterator.
- *
- * @param cls closure (internal context for the plugin)
- * @param query hash of public key derived from the zone and the label
- * @param iter function to call with the result
- * @param iter_cls closure for @a iter
- * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
- */
- static int
- namecache_lookup_block (void *cls,
- const struct GNUNET_HashCode *query,
- GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls)
- {
- struct Plugin *plugin = cls;
- const struct GNUNET_GNSRECORD_Block *block;
- block = GNUNET_CONTAINER_multihashmap_get (plugin->hm, query);
- if (NULL == block)
- return GNUNET_NO;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Found block under derived key `%s'\n",
- GNUNET_h2s_full (query));
- iter (iter_cls, block);
- return GNUNET_YES;
- }
- /**
- * Entry point for the plugin.
- *
- * @param cls the "struct GNUNET_NAMECACHE_PluginEnvironment*"
- * @return NULL on error, otherwise the plugin context
- */
- void *
- libgnunet_plugin_namecache_flat_init (void *cls)
- {
- static struct Plugin plugin;
- const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
- struct GNUNET_NAMECACHE_PluginFunctions *api;
- if (NULL != plugin.cfg)
- return NULL; /* can only initialize once! */
- memset (&plugin, 0, sizeof (struct Plugin));
- plugin.cfg = cfg;
- if (GNUNET_OK != database_setup (&plugin))
- {
- database_shutdown (&plugin);
- return NULL;
- }
- api = GNUNET_new (struct GNUNET_NAMECACHE_PluginFunctions);
- api->cls = &plugin;
- api->cache_block = &namecache_cache_block;
- api->lookup_block = &namecache_lookup_block;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("flat plugin running\n"));
- return api;
- }
- /**
- * Exit point from the plugin.
- *
- * @param cls the plugin context (as returned by "init")
- * @return always NULL
- */
- void *
- libgnunet_plugin_namecache_flat_done (void *cls)
- {
- struct GNUNET_NAMECACHE_PluginFunctions *api = cls;
- struct Plugin *plugin = api->cls;
- database_shutdown (plugin);
- plugin->cfg = NULL;
- GNUNET_free (api);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "flat plugin is finished\n");
- return NULL;
- }
- /* end of plugin_namecache_sqlite.c */
|