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 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.
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.
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/>.
20 * @file namestore/plugin_namestore_flat.c
21 * @brief file-based namestore backend
22 * @author Martin Schanzenbach
23 * @author Christian Grothoff
27 #include "gnunet_namestore_plugin.h"
28 #include "gnunet_namestore_service.h"
29 #include "gnunet_gnsrecord_lib.h"
30 #include "namestore.h"
33 * Context for all functions in this plugin.
38 const struct GNUNET_CONFIGURATION_Handle *cfg;
48 struct GNUNET_CONTAINER_MultiHashMap *hm;
58 struct GNUNET_CRYPTO_EcdsaPrivateKey private_key;
63 uint32_t record_count;
73 struct GNUNET_GNSRECORD_Data *record_data;
85 * Initialize the database connections and associated
86 * data structures (create tables and indices
89 * @param plugin the plugin context (state for this module)
90 * @return #GNUNET_OK on success
93 database_setup (struct Plugin *plugin)
97 char *zone_private_key;
98 char *record_data_b64;
104 size_t record_data_size;
106 struct GNUNET_HashCode hkey;
107 struct GNUNET_DISK_FileHandle *fh;
108 struct FlatFileEntry *entry;
111 GNUNET_CONFIGURATION_get_value_filename (plugin->cfg,
116 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
119 return GNUNET_SYSERR;
122 GNUNET_DISK_file_test (afsdir))
125 GNUNET_DISK_directory_create_for_file (afsdir))
128 GNUNET_free (afsdir);
129 return GNUNET_SYSERR;
132 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
135 /* Load data from file into hashmap */
136 plugin->hm = GNUNET_CONTAINER_multihashmap_create (10,
138 fh = GNUNET_DISK_file_open (afsdir,
139 GNUNET_DISK_OPEN_CREATE |
140 GNUNET_DISK_OPEN_READWRITE,
141 GNUNET_DISK_PERM_USER_WRITE |
142 GNUNET_DISK_PERM_USER_READ);
145 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
146 _("Unable to initialize file: %s.\n"),
148 return GNUNET_SYSERR;
151 GNUNET_DISK_file_size (afsdir,
156 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
157 _("Unable to get filesize: %s.\n"),
159 GNUNET_DISK_file_close (fh);
160 return GNUNET_SYSERR;
163 buffer = GNUNET_malloc (size + 1);
165 GNUNET_DISK_file_read (fh,
169 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
170 _("Unable to read file: %s.\n"),
172 GNUNET_free (buffer);
173 GNUNET_DISK_file_close (fh);
174 return GNUNET_SYSERR;
177 GNUNET_DISK_file_close (fh);
181 line = strtok (buffer, "\n");
184 zone_private_key = strtok (line, ",");
185 if (NULL == zone_private_key)
187 rvalue = strtok (NULL, ",");
190 record_count = strtok (NULL, ",");
191 if (NULL == record_count)
193 record_data_b64 = strtok (NULL, ",");
194 if (NULL == record_data_b64)
196 label = strtok (NULL, ",");
199 line = strtok (NULL, "\n");
200 entry = GNUNET_new (struct FlatFileEntry);
202 unsigned long long ll;
204 if (1 != sscanf (rvalue,
208 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
209 "Error parsing entry\n");
213 entry->rvalue = (uint64_t) ll;
218 if (1 != sscanf (record_count,
222 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
223 "Error parsing entry\n");
227 entry->record_count = (uint32_t) ui;
229 entry->label = GNUNET_strdup (label);
231 = GNUNET_STRINGS_base64_decode (record_data_b64,
232 strlen (record_data_b64),
233 (void **) &record_data);
235 GNUNET_new_array (entry->record_count,
236 struct GNUNET_GNSRECORD_Data);
238 GNUNET_GNSRECORD_records_deserialize (record_data_size,
243 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
244 "Unable to deserialize record %s\n",
246 GNUNET_free (entry->label);
248 GNUNET_free (record_data);
251 GNUNET_free (record_data);
254 struct GNUNET_CRYPTO_EcdsaPrivateKey *private_key;
256 GNUNET_STRINGS_base64_decode (zone_private_key,
257 strlen (zone_private_key),
258 (void**)&private_key);
259 entry->private_key = *private_key;
260 GNUNET_free (private_key);
267 key_len = strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
268 key = GNUNET_malloc (strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
272 GNUNET_memcpy (key+strlen(label),
274 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
275 GNUNET_CRYPTO_hash (key,
281 GNUNET_CONTAINER_multihashmap_put (plugin->hm,
284 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
291 GNUNET_free (buffer);
297 * Store values in hashmap in file and free data
299 * @param plugin the plugin context
300 * @param key key in the map
301 * @param value a `struct FlatFileEntry`
304 store_and_free_entries (void *cls,
305 const struct GNUNET_HashCode *key,
308 struct GNUNET_DISK_FileHandle *fh = cls;
309 struct FlatFileEntry *entry = value;
311 char *zone_private_key;
312 char *record_data_b64;
316 GNUNET_STRINGS_base64_encode (&entry->private_key,
317 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey),
319 data_size = GNUNET_GNSRECORD_records_get_size (entry->record_count,
324 GNUNET_free (zone_private_key);
325 return GNUNET_SYSERR;
327 if (data_size >= UINT16_MAX)
330 GNUNET_free (zone_private_key);
331 return GNUNET_SYSERR;
334 char data[data_size];
337 ret = GNUNET_GNSRECORD_records_serialize (entry->record_count,
345 GNUNET_free (zone_private_key);
346 return GNUNET_SYSERR;
348 GNUNET_STRINGS_base64_encode (data,
352 GNUNET_asprintf (&line,
353 "%s,%llu,%u,%s,%s\n",
355 (unsigned long long) entry->rvalue,
356 (unsigned int) entry->record_count,
359 GNUNET_free (record_data_b64);
360 GNUNET_free (zone_private_key);
362 GNUNET_DISK_file_write (fh,
367 GNUNET_free (entry->label);
368 GNUNET_free (entry->record_data);
375 * Shutdown database connection and associate data
377 * @param plugin the plugin context (state for this module)
380 database_shutdown (struct Plugin *plugin)
382 struct GNUNET_DISK_FileHandle *fh;
384 fh = GNUNET_DISK_file_open (plugin->fn,
385 GNUNET_DISK_OPEN_CREATE |
386 GNUNET_DISK_OPEN_TRUNCATE |
387 GNUNET_DISK_OPEN_READWRITE,
388 GNUNET_DISK_PERM_USER_WRITE |
389 GNUNET_DISK_PERM_USER_READ);
392 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
393 _("Unable to initialize file: %s.\n"),
398 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
399 &store_and_free_entries,
401 GNUNET_CONTAINER_multihashmap_destroy (plugin->hm);
402 GNUNET_DISK_file_close (fh);
407 * Store a record in the datastore. Removes any existing record in the
408 * same zone with the same name.
410 * @param cls closure (internal context for the plugin)
411 * @param zone_key private key of the zone
412 * @param label name that is being mapped (at most 255 characters long)
413 * @param rd_count number of entries in @a rd array
414 * @param rd array of records with data to store
415 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
418 namestore_flat_store_records (void *cls,
419 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
421 unsigned int rd_count,
422 const struct GNUNET_GNSRECORD_Data *rd)
424 struct Plugin *plugin = cls;
428 struct GNUNET_HashCode hkey;
429 struct FlatFileEntry *entry;
431 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
433 key_len = strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
434 key = GNUNET_malloc (key_len);
438 GNUNET_memcpy (key + strlen(label),
440 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
441 GNUNET_CRYPTO_hash (key,
444 GNUNET_CONTAINER_multihashmap_remove_all (plugin->hm,
448 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
453 entry = GNUNET_new (struct FlatFileEntry);
454 GNUNET_asprintf (&entry->label,
457 GNUNET_memcpy (&entry->private_key,
459 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
460 entry->rvalue = rvalue;
461 entry->record_count = rd_count;
462 entry->record_data = GNUNET_new_array (rd_count,
463 struct GNUNET_GNSRECORD_Data);
464 for (unsigned int i = 0; i < rd_count; i++)
466 entry->record_data[i].expiration_time = rd[i].expiration_time;
467 entry->record_data[i].record_type = rd[i].record_type;
468 entry->record_data[i].flags = rd[i].flags;
469 entry->record_data[i].data_size = rd[i].data_size;
470 entry->record_data[i].data = GNUNET_malloc (rd[i].data_size);
471 GNUNET_memcpy ((char*)entry->record_data[i].data,
475 return GNUNET_CONTAINER_multihashmap_put (plugin->hm,
478 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
483 * Lookup records in the datastore for which we are the authority.
485 * @param cls closure (internal context for the plugin)
486 * @param zone private key of the zone
487 * @param label name of the record in the zone
488 * @param iter function to call with the result
489 * @param iter_cls closure for @a iter
490 * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR
493 namestore_flat_lookup_records (void *cls,
494 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
496 GNUNET_NAMESTORE_RecordIterator iter,
499 struct Plugin *plugin = cls;
500 struct FlatFileEntry *entry;
501 struct GNUNET_HashCode hkey;
508 return GNUNET_SYSERR;
510 key_len = strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
511 key = GNUNET_malloc (key_len);
515 GNUNET_memcpy (key+strlen(label),
517 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
518 GNUNET_CRYPTO_hash (key,
523 entry = GNUNET_CONTAINER_multihashmap_get (plugin->hm,
540 * Closure for #iterate_zones.
542 struct IterateContext
545 * How many more records should we skip before returning results?
550 * How many more records should we return?
555 * What is the position of the current entry, counting
563 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone;
566 * Function to call on each record.
568 GNUNET_NAMESTORE_RecordIterator iter;
571 * Closure for @e iter.
579 * Helper function for #namestore_flat_iterate_records().
581 * @param cls a `struct IterateContext`
583 * @param value a `struct FlatFileEntry`
584 * @return #GNUNET_YES to continue the iteration
587 iterate_zones (void *cls,
588 const struct GNUNET_HashCode *key,
591 struct IterateContext *ic = cls;
592 struct FlatFileEntry *entry = value;
597 if ( (NULL != ic->zone) &&
598 (0 != memcmp (&entry->private_key,
600 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey))) )
608 ic->iter (ic->iter_cls,
622 * Iterate over the results for a particular key and zone in the
623 * datastore. Will return at most one result to the iterator.
625 * @param cls closure (internal context for the plugin)
626 * @param zone hash of public key of the zone, NULL to iterate over all zones
627 * @param serial serial number to exclude in the list of all matching records
628 * @param limit maximum number of results to return to @a iter
629 * @param iter function to call with the result
630 * @param iter_cls closure for @a iter
631 * @return #GNUNET_OK on success, #GNUNET_NO if there were no more results, #GNUNET_SYSERR on error
634 namestore_flat_iterate_records (void *cls,
635 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
638 GNUNET_NAMESTORE_RecordIterator iter,
641 struct Plugin *plugin = cls;
642 struct IterateContext ic;
648 ic.iter_cls = iter_cls;
650 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
653 return (0 == ic.limit) ? GNUNET_OK : GNUNET_NO;
658 * Closure for #zone_to_name.
660 struct ZoneToNameContext
662 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone;
663 const struct GNUNET_CRYPTO_EcdsaPublicKey *value_zone;
664 GNUNET_NAMESTORE_RecordIterator iter;
672 zone_to_name (void *cls,
673 const struct GNUNET_HashCode *key,
676 struct ZoneToNameContext *ztn = cls;
677 struct FlatFileEntry *entry = value;
680 if (0 != memcmp (&entry->private_key,
682 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
685 for (unsigned int i = 0; i < entry->record_count; i++)
687 if (GNUNET_GNSRECORD_TYPE_PKEY != entry->record_data[i].record_type)
689 if (0 == memcmp (ztn->value_zone,
690 entry->record_data[i].data,
691 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
693 ztn->iter (ztn->iter_cls,
699 ztn->result_found = GNUNET_YES;
707 * Look for an existing PKEY delegation record for a given public key.
708 * Returns at most one result to the iterator.
710 * @param cls closure (internal context for the plugin)
711 * @param zone private key of the zone to look up in, never NULL
712 * @param value_zone public key of the target zone (value), never NULL
713 * @param iter function to call with the result
714 * @param iter_cls closure for @a iter
715 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
718 namestore_flat_zone_to_name (void *cls,
719 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
720 const struct GNUNET_CRYPTO_EcdsaPublicKey *value_zone,
721 GNUNET_NAMESTORE_RecordIterator iter,
724 struct Plugin *plugin = cls;
725 struct ZoneToNameContext ztn = {
727 .iter_cls = iter_cls,
729 .value_zone = value_zone,
730 .result_found = GNUNET_NO
733 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
734 "Performing reverse lookup for `%s'\n",
735 GNUNET_GNSRECORD_z2s (value_zone));
736 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
739 return ztn.result_found;
744 * Entry point for the plugin.
746 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
747 * @return NULL on error, otherwise the plugin context
750 libgnunet_plugin_namestore_flat_init (void *cls)
752 static struct Plugin plugin;
753 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
754 struct GNUNET_NAMESTORE_PluginFunctions *api;
756 if (NULL != plugin.cfg)
757 return NULL; /* can only initialize once! */
760 sizeof (struct Plugin));
762 if (GNUNET_OK != database_setup (&plugin))
764 database_shutdown (&plugin);
767 api = GNUNET_new (struct GNUNET_NAMESTORE_PluginFunctions);
769 api->store_records = &namestore_flat_store_records;
770 api->iterate_records = &namestore_flat_iterate_records;
771 api->zone_to_name = &namestore_flat_zone_to_name;
772 api->lookup_records = &namestore_flat_lookup_records;
773 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
774 _("flat file database running\n"));
780 * Exit point from the plugin.
782 * @param cls the plugin context (as returned by "init")
783 * @return always NULL
786 libgnunet_plugin_namestore_flat_done (void *cls)
788 struct GNUNET_NAMESTORE_PluginFunctions *api = cls;
789 struct Plugin *plugin = api->cls;
791 database_shutdown (plugin);
794 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
795 "flat file plugin is finished\n");
799 /* end of plugin_namestore_flat.c */