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 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.
17 * @file namestore/plugin_namestore_flat.c
18 * @brief file-based namestore backend
19 * @author Martin Schanzenbach
20 * @author Christian Grothoff
24 #include "gnunet_namestore_plugin.h"
25 #include "gnunet_namestore_service.h"
26 #include "gnunet_gnsrecord_lib.h"
27 #include "namestore.h"
30 * Context for all functions in this plugin.
35 const struct GNUNET_CONFIGURATION_Handle *cfg;
45 struct GNUNET_CONTAINER_MultiHashMap *hm;
55 struct GNUNET_CRYPTO_EcdsaPrivateKey *private_key;
60 uint32_t record_count;
70 struct GNUNET_GNSRECORD_Data *record_data;
82 * Initialize the database connections and associated
83 * data structures (create tables and indices
86 * @param plugin the plugin context (state for this module)
87 * @return #GNUNET_OK on success
90 database_setup (struct Plugin *plugin)
95 char *zone_private_key;
96 char *record_data_b64;
102 size_t record_data_size;
105 struct GNUNET_HashCode hkey;
106 struct GNUNET_DISK_FileHandle *fh;
107 struct FlatFileEntry *entry;
110 GNUNET_CONFIGURATION_get_value_filename (plugin->cfg,
115 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
118 return GNUNET_SYSERR;
121 GNUNET_DISK_file_test (afsdir))
124 GNUNET_DISK_directory_create_for_file (afsdir))
127 GNUNET_free (afsdir);
128 return GNUNET_SYSERR;
131 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
134 /* Load data from file into hashmap */
135 plugin->hm = GNUNET_CONTAINER_multihashmap_create (10,
137 fh = GNUNET_DISK_file_open (afsdir,
138 GNUNET_DISK_OPEN_CREATE |
139 GNUNET_DISK_OPEN_READWRITE,
140 GNUNET_DISK_PERM_USER_WRITE |
141 GNUNET_DISK_PERM_USER_READ);
144 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
145 _("Unable to initialize file: %s.\n"),
147 return GNUNET_SYSERR;
150 GNUNET_DISK_file_size (afsdir,
155 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
156 _("Unable to get filesize: %s.\n"),
158 GNUNET_DISK_file_close (fh);
159 return GNUNET_SYSERR;
162 buffer = GNUNET_malloc (size + 1);
164 GNUNET_DISK_file_read (fh,
168 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
169 _("Unable to read file: %s.\n"),
171 GNUNET_free (buffer);
172 GNUNET_DISK_file_close (fh);
173 return GNUNET_SYSERR;
176 GNUNET_DISK_file_close (fh);
180 line = strtok (buffer, "\n");
183 zone_private_key = strtok (line, ",");
184 if (NULL == zone_private_key)
186 rvalue = strtok (NULL, ",");
189 record_count = strtok (NULL, ",");
190 if (NULL == record_count)
192 record_data_b64 = strtok (NULL, ",");
193 if (NULL == record_data_b64)
195 label = strtok (NULL, ",");
198 line = strtok (NULL, "\n");
199 entry = GNUNET_new (struct FlatFileEntry);
201 unsigned long long ll;
203 if (1 != sscanf (rvalue,
207 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
208 "Error parsing entry\n");
212 entry->rvalue = (uint64_t) ll;
217 if (1 != sscanf (record_count,
221 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
222 "Error parsing entry\n");
226 entry->record_count = (uint32_t) ui;
228 entry->label = GNUNET_strdup (label);
230 = GNUNET_STRINGS_base64_decode (record_data_b64,
231 strlen (record_data_b64),
234 GNUNET_new_array (entry->record_count,
235 struct GNUNET_GNSRECORD_Data);
237 GNUNET_GNSRECORD_records_deserialize (record_data_size,
242 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
243 "Unable to deserialize record %s\n",
245 GNUNET_free (entry->label);
247 GNUNET_free (record_data);
250 GNUNET_free (record_data);
251 GNUNET_STRINGS_base64_decode (zone_private_key,
252 strlen (zone_private_key),
253 (char**)&entry->private_key);
254 key_len = strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
255 key = GNUNET_malloc (strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
259 GNUNET_memcpy (key+strlen(label),
261 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
262 GNUNET_CRYPTO_hash (key,
267 GNUNET_CONTAINER_multihashmap_put (plugin->hm,
270 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
277 GNUNET_free (buffer);
283 * Store values in hashmap in file and free data
285 * @param plugin the plugin context
286 * @param key key in the map
287 * @param value a `struct FlatFileEntry`
290 store_and_free_entries (void *cls,
291 const struct GNUNET_HashCode *key,
294 struct GNUNET_DISK_FileHandle *fh = cls;
295 struct FlatFileEntry *entry = value;
297 char *zone_private_key;
298 char *record_data_b64;
302 GNUNET_STRINGS_base64_encode ((char*)entry->private_key,
303 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey),
305 data_size = GNUNET_GNSRECORD_records_get_size (entry->record_count,
310 GNUNET_free (zone_private_key);
311 return GNUNET_SYSERR;
313 if (data_size >= UINT16_MAX)
316 GNUNET_free (zone_private_key);
317 return GNUNET_SYSERR;
320 char data[data_size];
323 ret = GNUNET_GNSRECORD_records_serialize (entry->record_count,
331 GNUNET_free (zone_private_key);
332 return GNUNET_SYSERR;
334 GNUNET_STRINGS_base64_encode (data,
338 GNUNET_asprintf (&line,
339 "%s,%llu,%u,%s,%s\n",
341 (unsigned long long) entry->rvalue,
342 (unsigned int) entry->record_count,
345 GNUNET_free (record_data_b64);
346 GNUNET_free (zone_private_key);
348 GNUNET_DISK_file_write (fh,
353 GNUNET_free (entry->private_key);
354 GNUNET_free (entry->label);
355 GNUNET_free (entry->record_data);
362 * Shutdown database connection and associate data
364 * @param plugin the plugin context (state for this module)
367 database_shutdown (struct Plugin *plugin)
369 struct GNUNET_DISK_FileHandle *fh;
371 fh = GNUNET_DISK_file_open (plugin->fn,
372 GNUNET_DISK_OPEN_CREATE |
373 GNUNET_DISK_OPEN_TRUNCATE |
374 GNUNET_DISK_OPEN_READWRITE,
375 GNUNET_DISK_PERM_USER_WRITE |
376 GNUNET_DISK_PERM_USER_READ);
379 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
380 _("Unable to initialize file: %s.\n"),
385 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
386 &store_and_free_entries,
388 GNUNET_CONTAINER_multihashmap_destroy (plugin->hm);
389 GNUNET_DISK_file_close (fh);
394 * Store a record in the datastore. Removes any existing record in the
395 * same zone with the same name.
397 * @param cls closure (internal context for the plugin)
398 * @param zone_key private key of the zone
399 * @param label name that is being mapped (at most 255 characters long)
400 * @param rd_count number of entries in @a rd array
401 * @param rd array of records with data to store
402 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
405 namestore_flat_store_records (void *cls,
406 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
408 unsigned int rd_count,
409 const struct GNUNET_GNSRECORD_Data *rd)
411 struct Plugin *plugin = cls;
415 struct GNUNET_HashCode hkey;
416 struct FlatFileEntry *entry;
418 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
420 key_len = strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
421 key = GNUNET_malloc (key_len);
425 GNUNET_memcpy (key + strlen(label),
427 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
428 GNUNET_CRYPTO_hash (key,
431 GNUNET_CONTAINER_multihashmap_remove_all (plugin->hm,
435 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
440 entry = GNUNET_new (struct FlatFileEntry);
441 entry->private_key = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey);
442 GNUNET_asprintf (&entry->label,
445 GNUNET_memcpy (entry->private_key,
447 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
448 entry->rvalue = rvalue;
449 entry->record_count = rd_count;
450 entry->record_data = GNUNET_new_array (rd_count,
451 struct GNUNET_GNSRECORD_Data);
452 for (unsigned int i = 0; i < rd_count; i++)
454 entry->record_data[i].expiration_time = rd[i].expiration_time;
455 entry->record_data[i].record_type = rd[i].record_type;
456 entry->record_data[i].flags = rd[i].flags;
457 entry->record_data[i].data_size = rd[i].data_size;
458 entry->record_data[i].data = GNUNET_malloc (rd[i].data_size);
459 GNUNET_memcpy ((char*)entry->record_data[i].data,
463 return GNUNET_CONTAINER_multihashmap_put (plugin->hm,
466 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
471 * Lookup records in the datastore for which we are the authority.
473 * @param cls closure (internal context for the plugin)
474 * @param zone private key of the zone
475 * @param label name of the record in the zone
476 * @param iter function to call with the result
477 * @param iter_cls closure for @a iter
478 * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR
481 namestore_flat_lookup_records (void *cls,
482 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
484 GNUNET_NAMESTORE_RecordIterator iter,
487 struct Plugin *plugin = cls;
488 struct FlatFileEntry *entry;
489 struct GNUNET_HashCode hkey;
496 return GNUNET_SYSERR;
498 key_len = strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
499 key = GNUNET_malloc (key_len);
503 GNUNET_memcpy (key+strlen(label),
505 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
506 GNUNET_CRYPTO_hash (key,
511 entry = GNUNET_CONTAINER_multihashmap_get (plugin->hm,
528 * Closure for #iterate_zones.
530 struct IterateContext
533 * How many more records should we skip before returning results?
538 * How many more records should we return?
543 * What is the position of the current entry, counting
551 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone;
554 * Function to call on each record.
556 GNUNET_NAMESTORE_RecordIterator iter;
559 * Closure for @e iter.
567 * Helper function for #namestore_flat_iterate_records().
569 * @param cls a `struct IterateContext`
571 * @param value a `struct FlatFileEntry`
572 * @return #GNUNET_YES to continue the iteration
575 iterate_zones (void *cls,
576 const struct GNUNET_HashCode *key,
579 struct IterateContext *ic = cls;
580 struct FlatFileEntry *entry = value;
585 if ( (NULL != ic->zone) &&
586 (0 != memcmp (entry->private_key,
588 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey))) )
596 ic->iter (ic->iter_cls,
610 * Iterate over the results for a particular key and zone in the
611 * datastore. Will return at most one result to the iterator.
613 * @param cls closure (internal context for the plugin)
614 * @param zone hash of public key of the zone, NULL to iterate over all zones
615 * @param serial serial number to exclude in the list of all matching records
616 * @param limit maximum number of results to return to @a iter
617 * @param iter function to call with the result
618 * @param iter_cls closure for @a iter
619 * @return #GNUNET_OK on success, #GNUNET_NO if there were no more results, #GNUNET_SYSERR on error
622 namestore_flat_iterate_records (void *cls,
623 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
626 GNUNET_NAMESTORE_RecordIterator iter,
629 struct Plugin *plugin = cls;
630 struct IterateContext ic;
636 ic.iter_cls = iter_cls;
638 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
641 return (0 == ic.limit) ? GNUNET_OK : GNUNET_NO;
646 * Closure for #zone_to_name.
648 struct ZoneToNameContext
650 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone;
651 const struct GNUNET_CRYPTO_EcdsaPublicKey *value_zone;
652 GNUNET_NAMESTORE_RecordIterator iter;
660 zone_to_name (void *cls,
661 const struct GNUNET_HashCode *key,
664 struct ZoneToNameContext *ztn = cls;
665 struct FlatFileEntry *entry = value;
668 if (0 != memcmp (entry->private_key,
670 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
673 for (unsigned int i = 0; i < entry->record_count; i++)
675 if (GNUNET_GNSRECORD_TYPE_PKEY != entry->record_data[i].record_type)
677 if (0 == memcmp (ztn->value_zone,
678 entry->record_data[i].data,
679 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
681 ztn->iter (ztn->iter_cls,
687 ztn->result_found = GNUNET_YES;
695 * Look for an existing PKEY delegation record for a given public key.
696 * Returns at most one result to the iterator.
698 * @param cls closure (internal context for the plugin)
699 * @param zone private key of the zone to look up in, never NULL
700 * @param value_zone public key of the target zone (value), never NULL
701 * @param iter function to call with the result
702 * @param iter_cls closure for @a iter
703 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
706 namestore_flat_zone_to_name (void *cls,
707 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
708 const struct GNUNET_CRYPTO_EcdsaPublicKey *value_zone,
709 GNUNET_NAMESTORE_RecordIterator iter,
712 struct Plugin *plugin = cls;
713 struct ZoneToNameContext ztn = {
715 .iter_cls = iter_cls,
717 .value_zone = value_zone,
718 .result_found = GNUNET_NO
721 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
722 "Performing reverse lookup for `%s'\n",
723 GNUNET_GNSRECORD_z2s (value_zone));
724 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
727 return ztn.result_found;
732 * Entry point for the plugin.
734 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
735 * @return NULL on error, otherwise the plugin context
738 libgnunet_plugin_namestore_flat_init (void *cls)
740 static struct Plugin plugin;
741 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
742 struct GNUNET_NAMESTORE_PluginFunctions *api;
744 if (NULL != plugin.cfg)
745 return NULL; /* can only initialize once! */
748 sizeof (struct Plugin));
750 if (GNUNET_OK != database_setup (&plugin))
752 database_shutdown (&plugin);
755 api = GNUNET_new (struct GNUNET_NAMESTORE_PluginFunctions);
757 api->store_records = &namestore_flat_store_records;
758 api->iterate_records = &namestore_flat_iterate_records;
759 api->zone_to_name = &namestore_flat_zone_to_name;
760 api->lookup_records = &namestore_flat_lookup_records;
761 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
762 _("flat file database running\n"));
768 * Exit point from the plugin.
770 * @param cls the plugin context (as returned by "init")
771 * @return always NULL
774 libgnunet_plugin_namestore_flat_done (void *cls)
776 struct GNUNET_NAMESTORE_PluginFunctions *api = cls;
777 struct Plugin *plugin = api->cls;
779 database_shutdown (plugin);
782 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
783 "flat file plugin is finished\n");
787 /* end of plugin_namestore_flat.c */