fb6ece5ecbc25bcc544d05e910e13f5beba9f5d9
[oweals/gnunet.git] / src / namestore / plugin_namestore_flat.c
1  /*
2   * This file is part of GNUnet
3   * Copyright (C) 2009-2015 Christian Grothoff (and other contributing authors)
4   *
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.
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   * General Public License for more details.
14   *
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.
19   */
20
21 /**
22  * @file namestore/plugin_namestore_flat.c
23  * @brief file-based namestore backend
24  * @author Martin Schanzenbach
25  */
26
27 #include "platform.h"
28 #include "gnunet_namestore_plugin.h"
29 #include "gnunet_namestore_service.h"
30 #include "gnunet_gnsrecord_lib.h"
31 #include "namestore.h"
32
33 /**
34  * Context for all functions in this plugin.
35  */
36 struct Plugin
37 {
38
39   const struct GNUNET_CONFIGURATION_Handle *cfg;
40
41   /**
42    * Database filename.
43    */
44   char *fn;
45
46   /**
47    * HashMap
48    */
49   struct GNUNET_CONTAINER_MultiHashMap *hm;
50
51   /**
52    * Offset
53    */
54   uint32_t offset;
55
56   /**
57    * Target Offset
58    */
59   uint32_t target_offset;
60
61   /**
62    * Iterator closure
63    */
64   void *iter_cls;
65
66   /**
67    * Iterator
68    */
69   GNUNET_NAMESTORE_RecordIterator iter;
70
71   /**
72    * Zone to iterate
73    */
74   const struct GNUNET_CRYPTO_EcdsaPrivateKey *iter_zone;
75
76   /**
77    * PKEY to look for in zone to name
78    */
79   struct GNUNET_CRYPTO_EcdsaPublicKey *iter_pkey;
80
81   /**
82    * Iteration result found
83    */
84   int iter_result_found;
85
86 };
87
88 struct FlatFileEntry
89 {
90   /**
91    * Entry zone
92    */
93   struct GNUNET_CRYPTO_EcdsaPrivateKey *private_key;
94
95   /**
96    * Entry zone pkey
97    */
98   struct GNUNET_CRYPTO_EcdsaPublicKey *pkey;
99
100   /**
101    * Record cound
102    */
103   uint32_t record_count;
104
105   /**
106    * Rvalue
107    */
108   uint64_t rvalue;
109
110   /**
111    * Record data
112    */
113   struct GNUNET_GNSRECORD_Data *record_data;
114
115   /**
116    * Label
117    */
118   char *label;
119
120
121 };
122
123
124 /**
125  * Initialize the database connections and associated
126  * data structures (create tables and indices
127  * as needed as well).
128  *
129  * @param plugin the plugin context (state for this module)
130  * @return #GNUNET_OK on success
131  */
132 static int
133 database_setup (struct Plugin *plugin)
134 {
135   char *afsdir;
136   char *key_str;
137   char *record_data;
138   char *zone_private_key;
139   char *pkey;
140   char *record_data_b64;
141   char *buffer;
142   char *line;
143   char *label;
144   char *rvalue;
145   char *record_count;
146   size_t record_data_size;
147   size_t size;
148   struct GNUNET_HashCode hkey;
149   struct GNUNET_DISK_FileHandle *fh;
150   struct FlatFileEntry *entry; 
151
152   if (GNUNET_OK !=
153       GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, 
154                                                "namestore-flat",
155                                                "FILENAME", &afsdir))
156   {
157     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
158                                "namestore-flat", "FILENAME");
159     return GNUNET_SYSERR;
160   }
161   if (GNUNET_OK != GNUNET_DISK_file_test (afsdir))
162   {
163     if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (afsdir))
164     {
165       GNUNET_break (0);
166       GNUNET_free (afsdir);
167       return GNUNET_SYSERR;
168     }
169   }
170   /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
171   plugin->fn = afsdir;
172
173   /* Load data from file into hashmap */
174   plugin->hm = GNUNET_CONTAINER_multihashmap_create (10,
175                                                      GNUNET_NO);
176   fh = GNUNET_DISK_file_open (afsdir,
177                               GNUNET_DISK_OPEN_CREATE |
178                               GNUNET_DISK_OPEN_READWRITE,
179                               GNUNET_DISK_PERM_USER_WRITE |
180                               GNUNET_DISK_PERM_USER_READ);
181   if (NULL == fh)
182   {
183     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
184          _("Unable to initialize file: %s.\n"),
185          afsdir);
186     return GNUNET_SYSERR;
187   }
188
189   if (GNUNET_SYSERR == GNUNET_DISK_file_size (afsdir,
190                                               &size,
191                                               GNUNET_YES,
192                                               GNUNET_YES))
193   {
194     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
195          _("Unable to get filesize: %s.\n"),
196          afsdir);
197     return GNUNET_SYSERR;
198   }
199
200   buffer = GNUNET_malloc (size);
201
202   if (GNUNET_SYSERR == GNUNET_DISK_file_read (fh,
203                                               buffer,
204                                               size))
205   {
206     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
207          _("Unable to read file: %s.\n"),
208          afsdir);
209     return GNUNET_SYSERR;
210   }
211
212   GNUNET_DISK_file_close (fh);
213
214   line = strtok ("\n", buffer);
215   while (line != NULL) {
216     zone_private_key = strtok (",", line);
217     pkey = strtok (NULL, line);
218     rvalue = strtok (NULL, line);
219     record_count = strtok (NULL, line);
220     record_data_b64 = strtok (NULL, line);
221     label = strtok (NULL, line);
222     line = strtok ("\n", buffer);
223     entry = GNUNET_malloc (sizeof (struct FlatFileEntry));
224     GNUNET_CRYPTO_ecdsa_public_key_from_string (pkey,
225                                                 strlen (pkey),
226                                                 entry->pkey);
227     sscanf (rvalue, "%lu", &entry->rvalue);
228     sscanf (record_count, "%u", &entry->record_count);
229     entry->label = GNUNET_strdup (label);
230     record_data_size = GNUNET_STRINGS_base64_decode (record_data_b64,
231                                                      strlen (record_data_b64),
232                                                      &record_data);
233     entry->record_data = 
234       GNUNET_malloc (sizeof (struct GNUNET_GNSRECORD_Data) * entry->record_count);
235     GNUNET_GNSRECORD_records_deserialize (record_data_size,
236                                           record_data,
237                                           entry->record_count,
238                                           entry->record_data);
239     GNUNET_free (record_data);
240     GNUNET_STRINGS_base64_decode (zone_private_key,
241                                   strlen (zone_private_key),
242                                   (char**)&entry->private_key);
243     key_str = GNUNET_malloc (strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
244     memcpy (key_str, label, strlen (label));
245     memcpy (key_str+strlen(label),
246             entry->private_key,
247             sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
248     GNUNET_CRYPTO_hash (key_str,
249                         strlen (key_str),
250                         &hkey);
251     GNUNET_free (key_str);
252     if (GNUNET_OK != 
253         GNUNET_CONTAINER_multihashmap_put (plugin->hm,
254                                            &hkey,
255                                            entry,
256                                            GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
257     {
258       GNUNET_free (entry);
259       GNUNET_break (0);
260     }
261   }
262   GNUNET_free (buffer);
263   return GNUNET_OK;
264 }
265
266
267 /**
268  * Store values in hashmap in file and free data
269  *
270  * @param plugin the plugin context
271  */
272 static int
273 store_and_free_entries (void *cls,
274                         const struct GNUNET_HashCode *key,
275                         void *value)
276 {
277   struct GNUNET_DISK_FileHandle *fh = cls;
278   struct FlatFileEntry *entry = value;
279   char *line;
280   char *zone_private_key;
281   char *pkey;
282   char *rvalue;
283   char *record_count;
284   char *record_data_buf;
285   char *record_data_b64;
286   size_t record_data_len;
287
288   GNUNET_STRINGS_base64_encode ((char*)entry->private_key,
289                                 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey),
290                                 &zone_private_key);
291   pkey = GNUNET_CRYPTO_ecdsa_public_key_to_string (entry->pkey);
292   GNUNET_asprintf (&rvalue, "%hhu", entry->rvalue);
293   GNUNET_asprintf (&record_count, "%u", entry->record_count);
294
295   record_data_len = GNUNET_GNSRECORD_records_get_size (entry->record_count,
296                                                        entry->record_data);
297
298   record_data_buf = GNUNET_malloc (record_data_len);
299   GNUNET_GNSRECORD_records_serialize (entry->record_count,
300                                       entry->record_data,
301                                       record_data_len,
302                                       record_data_buf);
303
304   GNUNET_STRINGS_base64_encode (record_data_buf,
305                                 strlen (record_data_buf),
306                                 &record_data_b64);
307
308   GNUNET_asprintf (&line,
309                    "%s,%s,%s,%s,%s,%s\n",
310                    zone_private_key,
311                    pkey,
312                    rvalue,
313                    record_count,
314                    record_data_b64,
315                    entry->label);
316
317   GNUNET_free (rvalue);
318   GNUNET_free (record_count);
319   GNUNET_free (record_data_buf);
320   GNUNET_free (record_data_b64);
321
322   GNUNET_DISK_file_write (fh,
323                           line,
324                           strlen (line));
325
326   GNUNET_free (entry->private_key);
327   GNUNET_free (entry->pkey);
328   GNUNET_free (entry->label);
329   GNUNET_free (entry->record_data);
330   GNUNET_free (entry);
331   return GNUNET_YES;
332 }
333
334 /**
335  * Shutdown database connection and associate data
336  * structures.
337  * @param plugin the plugin context (state for this module)
338  */
339 static void
340 database_shutdown (struct Plugin *plugin)
341 {
342   struct GNUNET_DISK_FileHandle *fh;
343   fh = GNUNET_DISK_file_open (plugin->fn,
344                               GNUNET_DISK_OPEN_CREATE |
345                               GNUNET_DISK_OPEN_TRUNCATE,
346                               GNUNET_DISK_PERM_USER_WRITE |
347                               GNUNET_DISK_PERM_USER_READ);
348   if (NULL == fh)
349   {
350     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
351          _("Unable to initialize file: %s.\n"),
352          plugin->fn);
353     return;
354   }
355
356   GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
357                                          &store_and_free_entries,
358                                          fh);
359   GNUNET_CONTAINER_multihashmap_destroy (plugin->hm);
360   GNUNET_DISK_file_close (fh);
361 }
362
363
364 /**
365  * Store a record in the datastore.  Removes any existing record in the
366  * same zone with the same name.
367  *
368  * @param cls closure (internal context for the plugin)
369  * @param zone_key private key of the zone
370  * @param label name that is being mapped (at most 255 characters long)
371  * @param rd_count number of entries in @a rd array
372  * @param rd array of records with data to store
373  * @return #GNUNET_OK on success, else #GNUNET_SYSERR
374  */
375 static int
376 namestore_store_records (void *cls,
377                          const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
378                          const char *label,
379                          unsigned int rd_count,
380                          const struct GNUNET_GNSRECORD_Data *rd)
381 {
382   struct Plugin *plugin = cls;
383   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
384   uint64_t rvalue;
385   size_t data_size;
386   unsigned int i;
387   char *key_str;
388   struct GNUNET_HashCode hkey;
389   struct FlatFileEntry *entry;
390
391   memset (&pkey, 0, sizeof (pkey));
392   for (i=0;i<rd_count;i++)
393     if (GNUNET_GNSRECORD_TYPE_PKEY == rd[i].record_type)
394     {
395       GNUNET_break (sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) == rd[i].data_size);
396       memcpy (&pkey,
397               rd[i].data,
398               rd[i].data_size);
399       break;
400     }
401   rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
402   data_size = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
403   if (data_size > 64 * 65536)
404   {
405     GNUNET_break (0);
406     return GNUNET_SYSERR;
407   }
408   char data[data_size];
409
410   if (data_size != GNUNET_GNSRECORD_records_serialize (rd_count, rd,
411                                                        data_size, data))
412   {
413     GNUNET_break (0);
414     return GNUNET_SYSERR;
415   }
416
417   key_str = GNUNET_malloc (strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
418   memcpy (key_str, label, strlen (label));
419   memcpy (key_str+strlen(label),
420           zone_key,
421           sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
422   GNUNET_CRYPTO_hash (key_str,
423                       strlen (key_str),
424                       &hkey);
425
426   GNUNET_CONTAINER_multihashmap_remove_all (plugin->hm, &hkey);
427
428   if (0 != rd_count)
429   {
430     entry = GNUNET_malloc (sizeof (struct FlatFileEntry));
431     entry->private_key = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
432     memcpy (&entry->private_key,
433             zone_key,
434             sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
435     entry->pkey = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
436     memcpy (entry->pkey,
437             &pkey,
438             sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
439     entry->rvalue = rvalue;
440     entry->record_count = rd_count;
441     entry->record_data = GNUNET_malloc (data_size);
442     memcpy (&entry->record_data, data, data_size);
443     return GNUNET_CONTAINER_multihashmap_put (plugin->hm,
444                                               &hkey,
445                                               entry,
446                                               GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
447   }
448   return GNUNET_NO;
449 }
450
451
452 /**
453  * Lookup records in the datastore for which we are the authority.
454  *
455  * @param cls closure (internal context for the plugin)
456  * @param zone private key of the zone
457  * @param label name of the record in the zone
458  * @param iter function to call with the result
459  * @param iter_cls closure for @a iter
460  * @return #GNUNET_OK on success, else #GNUNET_SYSERR
461  */
462 static int
463 namestore_lookup_records (void *cls,
464                           const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
465                           const char *label,
466                           GNUNET_NAMESTORE_RecordIterator iter,
467                           void *iter_cls)
468 {
469   struct Plugin *plugin = cls;
470   struct FlatFileEntry *entry;
471   struct GNUNET_HashCode hkey;
472   char *key_str;
473
474   if (NULL == zone)
475   {
476     return GNUNET_SYSERR;
477   }
478   key_str = GNUNET_malloc (strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
479   memcpy (key_str, label, strlen (label));
480   memcpy (key_str+strlen(label),
481           zone,
482           sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
483   GNUNET_CRYPTO_hash (key_str,
484                       strlen (key_str),
485                       &hkey);
486   GNUNET_free (key_str);
487
488   entry = GNUNET_CONTAINER_multihashmap_get (plugin->hm, &hkey);
489
490   if (NULL == entry)
491     return GNUNET_NO;
492   if (NULL != iter)
493     iter (iter_cls, entry->private_key, entry->label, entry->record_count, entry->record_data);
494   return GNUNET_YES;
495 }
496
497
498 static int
499 iterate_zones (void *cls,
500                const struct GNUNET_HashCode *key,
501                void *value)
502 {
503   struct Plugin *plugin = cls;
504   struct FlatFileEntry *entry = value;
505
506
507   if ((plugin->target_offset > plugin->offset) ||
508       (0 != memcmp (entry->private_key,
509                     plugin->iter_zone,
510                     sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))) {
511     plugin->offset++;
512     return GNUNET_YES;
513   }
514
515   plugin->iter (plugin->iter_cls,
516                 entry->private_key,
517                 entry->label,
518                 entry->record_count,
519                 entry->record_data);
520   plugin->iter_result_found = GNUNET_YES;
521   return GNUNET_NO;
522 }
523
524 /**
525  * Iterate over the results for a particular key and zone in the
526  * datastore.  Will return at most one result to the iterator.
527  *
528  * @param cls closure (internal context for the plugin)
529  * @param zone hash of public key of the zone, NULL to iterate over all zones
530  * @param offset offset in the list of all matching records
531  * @param iter function to call with the result
532  * @param iter_cls closure for @a iter
533  * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
534  */
535 static int
536 namestore_iterate_records (void *cls,
537                            const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
538                            uint64_t offset,
539                            GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
540 {
541   struct Plugin *plugin = cls;
542   plugin->target_offset = offset;
543   plugin->offset = 0;
544   plugin->iter = iter;
545   plugin->iter_cls = cls;
546   plugin->iter_zone = zone;
547   plugin->iter_result_found = GNUNET_NO;
548   GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
549                                          &iterate_zones,
550                                          plugin);
551   return plugin->iter_result_found;
552 }
553
554 static int
555 zone_to_name (void *cls,
556               const struct GNUNET_HashCode *key,
557               void *value)
558 {
559   struct Plugin *plugin = cls;
560   struct FlatFileEntry *entry = value;
561   int i;
562
563   if (0 != memcmp (entry->private_key,
564                    plugin->iter_zone,
565                    sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
566     return GNUNET_YES;
567
568   for (i = 0; i < entry->record_count; i++) {
569     if (GNUNET_GNSRECORD_TYPE_PKEY != entry->record_data[i].record_type)
570       continue;
571     if (0 == memcmp (plugin->iter_pkey,
572                      entry->record_data[i].data,
573                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
574     {
575       plugin->iter (plugin->iter_cls,
576                     entry->private_key,
577                     entry->label,
578                     entry->record_count,
579                     entry->record_data);
580       plugin->iter_result_found = GNUNET_YES;
581
582     }
583   }
584
585   return GNUNET_YES;
586 }
587
588 /**
589  * Look for an existing PKEY delegation record for a given public key.
590  * Returns at most one result to the iterator.
591  *
592  * @param cls closure (internal context for the plugin)
593  * @param zone private key of the zone to look up in, never NULL
594  * @param value_zone public key of the target zone (value), never NULL
595  * @param iter function to call with the result
596  * @param iter_cls closure for @a iter
597  * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
598  */
599 static int
600 namestore_zone_to_name (void *cls,
601                         const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
602                         const struct GNUNET_CRYPTO_EcdsaPublicKey *value_zone,
603                         GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
604 {
605   struct Plugin *plugin = cls;
606
607   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
608        "Performing reverse lookup for `%s'\n",
609        GNUNET_GNSRECORD_z2s (value_zone));
610
611   GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
612                                          &zone_to_name,
613                                          plugin);
614
615
616   return plugin->iter_result_found;
617 }
618
619
620 /**
621  * Entry point for the plugin.
622  *
623  * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
624  * @return NULL on error, otherwise the plugin context
625  */
626 void *
627 libgnunet_plugin_namestore_flat_init (void *cls)
628 {
629   static struct Plugin plugin;
630   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
631   struct GNUNET_NAMESTORE_PluginFunctions *api;
632
633   if (NULL != plugin.cfg)
634     return NULL;                /* can only initialize once! */
635   memset (&plugin, 0, sizeof (struct Plugin));
636   plugin.cfg = cfg;
637   if (GNUNET_OK != database_setup (&plugin))
638   {
639     database_shutdown (&plugin);
640     return NULL;
641   }
642   api = GNUNET_new (struct GNUNET_NAMESTORE_PluginFunctions);
643   api->cls = &plugin;
644   api->store_records = &namestore_store_records;
645   api->iterate_records = &namestore_iterate_records;
646   api->zone_to_name = &namestore_zone_to_name;
647   api->lookup_records = &namestore_lookup_records;
648   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
649        _("flat file database running\n"));
650   return api;
651 }
652
653
654 /**
655  * Exit point from the plugin.
656  *
657  * @param cls the plugin context (as returned by "init")
658  * @return always NULL
659  */
660 void *
661 libgnunet_plugin_namestore_flat_done (void *cls)
662 {
663   struct GNUNET_NAMESTORE_PluginFunctions *api = cls;
664   struct Plugin *plugin = api->cls;
665
666   database_shutdown (plugin);
667   plugin->cfg = NULL;
668   GNUNET_free (api);
669   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
670        "flat file plugin is finished\n");
671   return NULL;
672 }
673
674 /* end of plugin_namestore_flat.c */