2 * This file is part of GNUnet
3 * Copyright (C) 2009-2015, 2018 GNUnet e.V.
5 * GNUnet is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published
7 * by the Free Software Foundation; either version 3, or (at your
8 * option) any later version.
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 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GNUnet; see the file COPYING. If not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 * @file namestore/plugin_namestore_flat.c
23 * @brief file-based namestore backend
24 * @author Martin Schanzenbach
25 * @author Christian Grothoff
29 #include "gnunet_namestore_plugin.h"
30 #include "gnunet_namestore_service.h"
31 #include "gnunet_gnsrecord_lib.h"
32 #include "namestore.h"
35 * Context for all functions in this plugin.
40 const struct GNUNET_CONFIGURATION_Handle *cfg;
50 struct GNUNET_CONTAINER_MultiHashMap *hm;
60 struct GNUNET_CRYPTO_EcdsaPrivateKey *private_key;
65 uint32_t record_count;
75 struct GNUNET_GNSRECORD_Data *record_data;
87 * Initialize the database connections and associated
88 * data structures (create tables and indices
91 * @param plugin the plugin context (state for this module)
92 * @return #GNUNET_OK on success
95 database_setup (struct Plugin *plugin)
100 char *zone_private_key;
101 char *record_data_b64;
107 size_t record_data_size;
110 struct GNUNET_HashCode hkey;
111 struct GNUNET_DISK_FileHandle *fh;
112 struct FlatFileEntry *entry;
115 GNUNET_CONFIGURATION_get_value_filename (plugin->cfg,
120 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
123 return GNUNET_SYSERR;
126 GNUNET_DISK_file_test (afsdir))
129 GNUNET_DISK_directory_create_for_file (afsdir))
132 GNUNET_free (afsdir);
133 return GNUNET_SYSERR;
136 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
139 /* Load data from file into hashmap */
140 plugin->hm = GNUNET_CONTAINER_multihashmap_create (10,
142 fh = GNUNET_DISK_file_open (afsdir,
143 GNUNET_DISK_OPEN_CREATE |
144 GNUNET_DISK_OPEN_READWRITE,
145 GNUNET_DISK_PERM_USER_WRITE |
146 GNUNET_DISK_PERM_USER_READ);
149 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
150 _("Unable to initialize file: %s.\n"),
152 return GNUNET_SYSERR;
155 GNUNET_DISK_file_size (afsdir,
160 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
161 _("Unable to get filesize: %s.\n"),
163 GNUNET_DISK_file_close (fh);
164 return GNUNET_SYSERR;
167 buffer = GNUNET_malloc (size + 1);
169 GNUNET_DISK_file_read (fh,
173 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
174 _("Unable to read file: %s.\n"),
176 GNUNET_free (buffer);
177 GNUNET_DISK_file_close (fh);
178 return GNUNET_SYSERR;
181 GNUNET_DISK_file_close (fh);
185 line = strtok (buffer, "\n");
188 zone_private_key = strtok (line, ",");
189 if (NULL == zone_private_key)
191 rvalue = strtok (NULL, ",");
194 record_count = strtok (NULL, ",");
195 if (NULL == record_count)
197 record_data_b64 = strtok (NULL, ",");
198 if (NULL == record_data_b64)
200 label = strtok (NULL, ",");
203 line = strtok (NULL, "\n");
204 entry = GNUNET_new (struct FlatFileEntry);
206 unsigned long long ll;
208 if (1 != sscanf (rvalue,
212 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
213 "Error parsing entry\n");
217 entry->rvalue = (uint64_t) ll;
222 if (1 != sscanf (record_count,
226 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
227 "Error parsing entry\n");
231 entry->record_count = (uint32_t) ui;
233 entry->label = GNUNET_strdup (label);
235 = GNUNET_STRINGS_base64_decode (record_data_b64,
236 strlen (record_data_b64),
239 GNUNET_new_array (entry->record_count,
240 struct GNUNET_GNSRECORD_Data);
242 GNUNET_GNSRECORD_records_deserialize (record_data_size,
247 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
248 "Unable to deserialize record %s\n",
250 GNUNET_free (entry->label);
252 GNUNET_free (record_data);
255 GNUNET_free (record_data);
256 GNUNET_STRINGS_base64_decode (zone_private_key,
257 strlen (zone_private_key),
258 (char**)&entry->private_key);
259 key_len = strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
260 key = GNUNET_malloc (strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
264 GNUNET_memcpy (key+strlen(label),
266 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
267 GNUNET_CRYPTO_hash (key,
272 GNUNET_CONTAINER_multihashmap_put (plugin->hm,
275 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
282 GNUNET_free (buffer);
288 * Store values in hashmap in file and free data
290 * @param plugin the plugin context
291 * @param key key in the map
292 * @param value a `struct FlatFileEntry`
295 store_and_free_entries (void *cls,
296 const struct GNUNET_HashCode *key,
299 struct GNUNET_DISK_FileHandle *fh = cls;
300 struct FlatFileEntry *entry = value;
302 char *zone_private_key;
303 char *record_data_b64;
307 GNUNET_STRINGS_base64_encode ((char*)entry->private_key,
308 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey),
310 data_size = GNUNET_GNSRECORD_records_get_size (entry->record_count,
315 GNUNET_free (zone_private_key);
316 return GNUNET_SYSERR;
318 if (data_size >= UINT16_MAX)
321 GNUNET_free (zone_private_key);
322 return GNUNET_SYSERR;
325 char data[data_size];
328 ret = GNUNET_GNSRECORD_records_serialize (entry->record_count,
336 GNUNET_free (zone_private_key);
337 return GNUNET_SYSERR;
339 GNUNET_STRINGS_base64_encode (data,
343 GNUNET_asprintf (&line,
344 "%s,%llu,%u,%s,%s\n",
346 (unsigned long long) entry->rvalue,
347 (unsigned int) entry->record_count,
350 GNUNET_free (record_data_b64);
351 GNUNET_free (zone_private_key);
353 GNUNET_DISK_file_write (fh,
358 GNUNET_free (entry->private_key);
359 GNUNET_free (entry->label);
360 GNUNET_free (entry->record_data);
367 * Shutdown database connection and associate data
369 * @param plugin the plugin context (state for this module)
372 database_shutdown (struct Plugin *plugin)
374 struct GNUNET_DISK_FileHandle *fh;
376 fh = GNUNET_DISK_file_open (plugin->fn,
377 GNUNET_DISK_OPEN_CREATE |
378 GNUNET_DISK_OPEN_TRUNCATE |
379 GNUNET_DISK_OPEN_READWRITE,
380 GNUNET_DISK_PERM_USER_WRITE |
381 GNUNET_DISK_PERM_USER_READ);
384 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
385 _("Unable to initialize file: %s.\n"),
390 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
391 &store_and_free_entries,
393 GNUNET_CONTAINER_multihashmap_destroy (plugin->hm);
394 GNUNET_DISK_file_close (fh);
399 * Store a record in the datastore. Removes any existing record in the
400 * same zone with the same name.
402 * @param cls closure (internal context for the plugin)
403 * @param zone_key private key of the zone
404 * @param label name that is being mapped (at most 255 characters long)
405 * @param rd_count number of entries in @a rd array
406 * @param rd array of records with data to store
407 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
410 namestore_flat_store_records (void *cls,
411 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
413 unsigned int rd_count,
414 const struct GNUNET_GNSRECORD_Data *rd)
416 struct Plugin *plugin = cls;
420 struct GNUNET_HashCode hkey;
421 struct FlatFileEntry *entry;
423 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
425 key_len = strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
426 key = GNUNET_malloc (key_len);
430 GNUNET_memcpy (key + strlen(label),
432 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
433 GNUNET_CRYPTO_hash (key,
436 GNUNET_CONTAINER_multihashmap_remove_all (plugin->hm,
440 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
445 entry = GNUNET_new (struct FlatFileEntry);
446 entry->private_key = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey);
447 GNUNET_asprintf (&entry->label,
450 GNUNET_memcpy (entry->private_key,
452 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
453 entry->rvalue = rvalue;
454 entry->record_count = rd_count;
455 entry->record_data = GNUNET_new_array (rd_count,
456 struct GNUNET_GNSRECORD_Data);
457 for (unsigned int i = 0; i < rd_count; i++)
459 entry->record_data[i].expiration_time = rd[i].expiration_time;
460 entry->record_data[i].record_type = rd[i].record_type;
461 entry->record_data[i].flags = rd[i].flags;
462 entry->record_data[i].data_size = rd[i].data_size;
463 entry->record_data[i].data = GNUNET_malloc (rd[i].data_size);
464 GNUNET_memcpy ((char*)entry->record_data[i].data,
468 return GNUNET_CONTAINER_multihashmap_put (plugin->hm,
471 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
476 * Lookup records in the datastore for which we are the authority.
478 * @param cls closure (internal context for the plugin)
479 * @param zone private key of the zone
480 * @param label name of the record in the zone
481 * @param iter function to call with the result
482 * @param iter_cls closure for @a iter
483 * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR
486 namestore_flat_lookup_records (void *cls,
487 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
489 GNUNET_NAMESTORE_RecordIterator iter,
492 struct Plugin *plugin = cls;
493 struct FlatFileEntry *entry;
494 struct GNUNET_HashCode hkey;
501 return GNUNET_SYSERR;
503 key_len = strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
504 key = GNUNET_malloc (key_len);
508 GNUNET_memcpy (key+strlen(label),
510 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
511 GNUNET_CRYPTO_hash (key,
516 entry = GNUNET_CONTAINER_multihashmap_get (plugin->hm,
533 * Closure for #iterate_zones.
535 struct IterateContext
538 * How many more records should we skip before returning results?
543 * How many more records should we return?
548 * What is the position of the current entry, counting
556 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone;
559 * Function to call on each record.
561 GNUNET_NAMESTORE_RecordIterator iter;
564 * Closure for @e iter.
572 * Helper function for #namestore_flat_iterate_records().
574 * @param cls a `struct IterateContext`
576 * @param value a `struct FlatFileEntry`
577 * @return #GNUNET_YES to continue the iteration
580 iterate_zones (void *cls,
581 const struct GNUNET_HashCode *key,
584 struct IterateContext *ic = cls;
585 struct FlatFileEntry *entry = value;
590 if ( (NULL != ic->zone) &&
591 (0 != memcmp (entry->private_key,
593 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey))) )
601 ic->iter (ic->iter_cls,
615 * Iterate over the results for a particular key and zone in the
616 * datastore. Will return at most one result to the iterator.
618 * @param cls closure (internal context for the plugin)
619 * @param zone hash of public key of the zone, NULL to iterate over all zones
620 * @param serial serial number to exclude in the list of all matching records
621 * @param limit maximum number of results to return to @a iter
622 * @param iter function to call with the result
623 * @param iter_cls closure for @a iter
624 * @return #GNUNET_OK on success, #GNUNET_NO if there were no more results, #GNUNET_SYSERR on error
627 namestore_flat_iterate_records (void *cls,
628 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
631 GNUNET_NAMESTORE_RecordIterator iter,
634 struct Plugin *plugin = cls;
635 struct IterateContext ic;
641 ic.iter_cls = iter_cls;
643 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
646 return (0 == ic.limit) ? GNUNET_OK : GNUNET_NO;
651 * Closure for #zone_to_name.
653 struct ZoneToNameContext
655 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone;
656 const struct GNUNET_CRYPTO_EcdsaPublicKey *value_zone;
657 GNUNET_NAMESTORE_RecordIterator iter;
665 zone_to_name (void *cls,
666 const struct GNUNET_HashCode *key,
669 struct ZoneToNameContext *ztn = cls;
670 struct FlatFileEntry *entry = value;
673 if (0 != memcmp (entry->private_key,
675 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
678 for (unsigned int i = 0; i < entry->record_count; i++)
680 if (GNUNET_GNSRECORD_TYPE_PKEY != entry->record_data[i].record_type)
682 if (0 == memcmp (ztn->value_zone,
683 entry->record_data[i].data,
684 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
686 ztn->iter (ztn->iter_cls,
692 ztn->result_found = GNUNET_YES;
700 * Look for an existing PKEY delegation record for a given public key.
701 * Returns at most one result to the iterator.
703 * @param cls closure (internal context for the plugin)
704 * @param zone private key of the zone to look up in, never NULL
705 * @param value_zone public key of the target zone (value), never NULL
706 * @param iter function to call with the result
707 * @param iter_cls closure for @a iter
708 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
711 namestore_flat_zone_to_name (void *cls,
712 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
713 const struct GNUNET_CRYPTO_EcdsaPublicKey *value_zone,
714 GNUNET_NAMESTORE_RecordIterator iter,
717 struct Plugin *plugin = cls;
718 struct ZoneToNameContext ztn = {
720 .iter_cls = iter_cls,
722 .value_zone = value_zone,
723 .result_found = GNUNET_NO
726 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
727 "Performing reverse lookup for `%s'\n",
728 GNUNET_GNSRECORD_z2s (value_zone));
729 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
732 return ztn.result_found;
737 * Entry point for the plugin.
739 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
740 * @return NULL on error, otherwise the plugin context
743 libgnunet_plugin_namestore_flat_init (void *cls)
745 static struct Plugin plugin;
746 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
747 struct GNUNET_NAMESTORE_PluginFunctions *api;
749 if (NULL != plugin.cfg)
750 return NULL; /* can only initialize once! */
753 sizeof (struct Plugin));
755 if (GNUNET_OK != database_setup (&plugin))
757 database_shutdown (&plugin);
760 api = GNUNET_new (struct GNUNET_NAMESTORE_PluginFunctions);
762 api->store_records = &namestore_flat_store_records;
763 api->iterate_records = &namestore_flat_iterate_records;
764 api->zone_to_name = &namestore_flat_zone_to_name;
765 api->lookup_records = &namestore_flat_lookup_records;
766 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
767 _("flat file database running\n"));
773 * Exit point from the plugin.
775 * @param cls the plugin context (as returned by "init")
776 * @return always NULL
779 libgnunet_plugin_namestore_flat_done (void *cls)
781 struct GNUNET_NAMESTORE_PluginFunctions *api = cls;
782 struct Plugin *plugin = api->cls;
784 database_shutdown (plugin);
787 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
788 "flat file plugin is finished\n");
792 /* end of plugin_namestore_flat.c */