846eac96a24b19e83053565afb4a4ed00158d80d
[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_OPEN_READWRITE,
347                               GNUNET_DISK_PERM_USER_WRITE |
348                               GNUNET_DISK_PERM_USER_READ);
349   if (NULL == fh)
350   {
351     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
352          _("Unable to initialize file: %s.\n"),
353          plugin->fn);
354     return;
355   }
356
357   GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
358                                          &store_and_free_entries,
359                                          fh);
360   GNUNET_CONTAINER_multihashmap_destroy (plugin->hm);
361   GNUNET_DISK_file_close (fh);
362 }
363
364
365 /**
366  * Store a record in the datastore.  Removes any existing record in the
367  * same zone with the same name.
368  *
369  * @param cls closure (internal context for the plugin)
370  * @param zone_key private key of the zone
371  * @param label name that is being mapped (at most 255 characters long)
372  * @param rd_count number of entries in @a rd array
373  * @param rd array of records with data to store
374  * @return #GNUNET_OK on success, else #GNUNET_SYSERR
375  */
376 static int
377 namestore_store_records (void *cls,
378                          const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
379                          const char *label,
380                          unsigned int rd_count,
381                          const struct GNUNET_GNSRECORD_Data *rd)
382 {
383   struct Plugin *plugin = cls;
384   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
385   uint64_t rvalue;
386   size_t data_size;
387   unsigned int i;
388   char *key_str;
389   struct GNUNET_HashCode hkey;
390   struct FlatFileEntry *entry;
391
392   memset (&pkey, 0, sizeof (pkey));
393   for (i=0;i<rd_count;i++)
394     if (GNUNET_GNSRECORD_TYPE_PKEY == rd[i].record_type)
395     {
396       GNUNET_break (sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) == rd[i].data_size);
397       memcpy (&pkey,
398               rd[i].data,
399               rd[i].data_size);
400       break;
401     }
402   rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
403   data_size = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
404   if (data_size > 64 * 65536)
405   {
406     GNUNET_break (0);
407     return GNUNET_SYSERR;
408   }
409   char data[data_size];
410
411   if (data_size != GNUNET_GNSRECORD_records_serialize (rd_count, rd,
412                                                        data_size, data))
413   {
414     GNUNET_break (0);
415     return GNUNET_SYSERR;
416   }
417
418   key_str = GNUNET_malloc (strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
419   memcpy (key_str, label, strlen (label));
420   memcpy (key_str+strlen(label),
421           zone_key,
422           sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
423   GNUNET_CRYPTO_hash (key_str,
424                       strlen (key_str),
425                       &hkey);
426
427   GNUNET_CONTAINER_multihashmap_remove_all (plugin->hm, &hkey);
428
429   if (0 != rd_count)
430   {
431     entry = GNUNET_malloc (sizeof (struct FlatFileEntry));
432     entry->private_key = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
433     memcpy (&entry->private_key,
434             zone_key,
435             sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
436     entry->pkey = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
437     memcpy (entry->pkey,
438             &pkey,
439             sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
440     entry->rvalue = rvalue;
441     entry->record_count = rd_count;
442     entry->record_data = GNUNET_malloc (data_size);
443     memcpy (&entry->record_data, data, data_size);
444     return GNUNET_CONTAINER_multihashmap_put (plugin->hm,
445                                               &hkey,
446                                               entry,
447                                               GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
448   }
449   return GNUNET_NO;
450 }
451
452
453 /**
454  * Lookup records in the datastore for which we are the authority.
455  *
456  * @param cls closure (internal context for the plugin)
457  * @param zone private key of the zone
458  * @param label name of the record in the zone
459  * @param iter function to call with the result
460  * @param iter_cls closure for @a iter
461  * @return #GNUNET_OK on success, else #GNUNET_SYSERR
462  */
463 static int
464 namestore_lookup_records (void *cls,
465                           const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
466                           const char *label,
467                           GNUNET_NAMESTORE_RecordIterator iter,
468                           void *iter_cls)
469 {
470   struct Plugin *plugin = cls;
471   struct FlatFileEntry *entry;
472   struct GNUNET_HashCode hkey;
473   char *key_str;
474
475   if (NULL == zone)
476   {
477     return GNUNET_SYSERR;
478   }
479   key_str = GNUNET_malloc (strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
480   memcpy (key_str, label, strlen (label));
481   memcpy (key_str+strlen(label),
482           zone,
483           sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
484   GNUNET_CRYPTO_hash (key_str,
485                       strlen (key_str),
486                       &hkey);
487   GNUNET_free (key_str);
488
489   entry = GNUNET_CONTAINER_multihashmap_get (plugin->hm, &hkey);
490
491   if (NULL == entry)
492     return GNUNET_NO;
493   if (NULL != iter)
494     iter (iter_cls, entry->private_key, entry->label, entry->record_count, entry->record_data);
495   return GNUNET_YES;
496 }
497
498
499 static int
500 iterate_zones (void *cls,
501                const struct GNUNET_HashCode *key,
502                void *value)
503 {
504   struct Plugin *plugin = cls;
505   struct FlatFileEntry *entry = value;
506
507
508   if ((plugin->target_offset > plugin->offset) ||
509       (0 != memcmp (entry->private_key,
510                     plugin->iter_zone,
511                     sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))) {
512     plugin->offset++;
513     return GNUNET_YES;
514   }
515
516   plugin->iter (plugin->iter_cls,
517                 entry->private_key,
518                 entry->label,
519                 entry->record_count,
520                 entry->record_data);
521   plugin->iter_result_found = GNUNET_YES;
522   return GNUNET_NO;
523 }
524
525 /**
526  * Iterate over the results for a particular key and zone in the
527  * datastore.  Will return at most one result to the iterator.
528  *
529  * @param cls closure (internal context for the plugin)
530  * @param zone hash of public key of the zone, NULL to iterate over all zones
531  * @param offset offset in the list of all matching records
532  * @param iter function to call with the result
533  * @param iter_cls closure for @a iter
534  * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
535  */
536 static int
537 namestore_iterate_records (void *cls,
538                            const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
539                            uint64_t offset,
540                            GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
541 {
542   struct Plugin *plugin = cls;
543   plugin->target_offset = offset;
544   plugin->offset = 0;
545   plugin->iter = iter;
546   plugin->iter_cls = cls;
547   plugin->iter_zone = zone;
548   plugin->iter_result_found = GNUNET_NO;
549   GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
550                                          &iterate_zones,
551                                          plugin);
552   return plugin->iter_result_found;
553 }
554
555 static int
556 zone_to_name (void *cls,
557               const struct GNUNET_HashCode *key,
558               void *value)
559 {
560   struct Plugin *plugin = cls;
561   struct FlatFileEntry *entry = value;
562   int i;
563
564   if (0 != memcmp (entry->private_key,
565                    plugin->iter_zone,
566                    sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
567     return GNUNET_YES;
568
569   for (i = 0; i < entry->record_count; i++) {
570     if (GNUNET_GNSRECORD_TYPE_PKEY != entry->record_data[i].record_type)
571       continue;
572     if (0 == memcmp (plugin->iter_pkey,
573                      entry->record_data[i].data,
574                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
575     {
576       plugin->iter (plugin->iter_cls,
577                     entry->private_key,
578                     entry->label,
579                     entry->record_count,
580                     entry->record_data);
581       plugin->iter_result_found = GNUNET_YES;
582
583     }
584   }
585
586   return GNUNET_YES;
587 }
588
589 /**
590  * Look for an existing PKEY delegation record for a given public key.
591  * Returns at most one result to the iterator.
592  *
593  * @param cls closure (internal context for the plugin)
594  * @param zone private key of the zone to look up in, never NULL
595  * @param value_zone public key of the target zone (value), never NULL
596  * @param iter function to call with the result
597  * @param iter_cls closure for @a iter
598  * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
599  */
600 static int
601 namestore_zone_to_name (void *cls,
602                         const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
603                         const struct GNUNET_CRYPTO_EcdsaPublicKey *value_zone,
604                         GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
605 {
606   struct Plugin *plugin = cls;
607
608   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
609        "Performing reverse lookup for `%s'\n",
610        GNUNET_GNSRECORD_z2s (value_zone));
611
612   GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
613                                          &zone_to_name,
614                                          plugin);
615
616
617   return plugin->iter_result_found;
618 }
619
620
621 /**
622  * Entry point for the plugin.
623  *
624  * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
625  * @return NULL on error, otherwise the plugin context
626  */
627 void *
628 libgnunet_plugin_namestore_flat_init (void *cls)
629 {
630   static struct Plugin plugin;
631   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
632   struct GNUNET_NAMESTORE_PluginFunctions *api;
633
634   if (NULL != plugin.cfg)
635     return NULL;                /* can only initialize once! */
636   memset (&plugin, 0, sizeof (struct Plugin));
637   plugin.cfg = cfg;
638   if (GNUNET_OK != database_setup (&plugin))
639   {
640     database_shutdown (&plugin);
641     return NULL;
642   }
643   api = GNUNET_new (struct GNUNET_NAMESTORE_PluginFunctions);
644   api->cls = &plugin;
645   api->store_records = &namestore_store_records;
646   api->iterate_records = &namestore_iterate_records;
647   api->zone_to_name = &namestore_zone_to_name;
648   api->lookup_records = &namestore_lookup_records;
649   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
650        _("flat file database running\n"));
651   return api;
652 }
653
654
655 /**
656  * Exit point from the plugin.
657  *
658  * @param cls the plugin context (as returned by "init")
659  * @return always NULL
660  */
661 void *
662 libgnunet_plugin_namestore_flat_done (void *cls)
663 {
664   struct GNUNET_NAMESTORE_PluginFunctions *api = cls;
665   struct Plugin *plugin = api->cls;
666
667   database_shutdown (plugin);
668   plugin->cfg = NULL;
669   GNUNET_free (api);
670   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
671        "flat file plugin is finished\n");
672   return NULL;
673 }
674
675 /* end of plugin_namestore_flat.c */