remove CYGWIN codeblocks, drop vendored Windows openvpn, drop win32 specific files.
[oweals/gnunet.git] / src / namecache / plugin_namecache_flat.c
1 /*
2  * This file is part of GNUnet
3  * Copyright (C) 2009-2015 GNUnet e.V.
4  *
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.
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  * Affero General Public License for more details.
14  *
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/>.
17
18     SPDX-License-Identifier: AGPL3.0-or-later
19  */
20
21 /**
22  * @file namecache/plugin_namecache_flat.c
23  * @brief flat file-based namecache backend
24  * @author Martin Schanzenbach
25  */
26
27 #include "platform.h"
28 #include "gnunet_namecache_plugin.h"
29 #include "gnunet_namecache_service.h"
30 #include "gnunet_gnsrecord_lib.h"
31 #include "namecache.h"
32
33 /**
34  * Context for all functions in this plugin.
35  */
36 struct Plugin {
37   const struct GNUNET_CONFIGURATION_Handle *cfg;
38
39   /**
40    * Database filename.
41    */
42   char *fn;
43
44   /**
45    * HashMap
46    */
47   struct GNUNET_CONTAINER_MultiHashMap *hm;
48 };
49
50 struct FlatFileEntry {
51   /**
52    * Block
53    */
54   struct GNUNET_GNSRECORD_Block *block;
55
56   /**
57    * query
58    */
59   struct GNUNET_HashCode query;
60 };
61
62 /**
63  * Initialize the database connections and associated
64  * data structures (create tables and indices
65  * as needed as well).
66  *
67  * @param plugin the plugin context (state for this module)
68  * @return #GNUNET_OK on success
69  */
70 static int
71 database_setup(struct Plugin *plugin)
72 {
73   char *afsdir;
74   char* block_buffer;
75   char* buffer;
76   char* line;
77   char* query;
78   char* block;
79   size_t size;
80   struct FlatFileEntry *entry;
81   struct GNUNET_DISK_FileHandle *fh;
82
83   if (GNUNET_OK !=
84       GNUNET_CONFIGURATION_get_value_filename(plugin->cfg,
85                                               "namecache-flat",
86                                               "FILENAME",
87                                               &afsdir))
88     {
89       GNUNET_log_config_missing(GNUNET_ERROR_TYPE_ERROR,
90                                 "namecache-flat", "FILENAME");
91       return GNUNET_SYSERR;
92     }
93   if (GNUNET_OK != GNUNET_DISK_file_test(afsdir))
94     {
95       if (GNUNET_OK != GNUNET_DISK_directory_create_for_file(afsdir))
96         {
97           GNUNET_break(0);
98           GNUNET_free(afsdir);
99           return GNUNET_SYSERR;
100         }
101     }
102   /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
103   plugin->fn = afsdir;
104
105   /* Load data from file into hashmap */
106   plugin->hm = GNUNET_CONTAINER_multihashmap_create(10,
107                                                     GNUNET_NO);
108   fh = GNUNET_DISK_file_open(afsdir,
109                              GNUNET_DISK_OPEN_CREATE |
110                              GNUNET_DISK_OPEN_READWRITE,
111                              GNUNET_DISK_PERM_USER_WRITE |
112                              GNUNET_DISK_PERM_USER_READ);
113   if (NULL == fh)
114     {
115       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
116                  _("Unable to initialize file: %s.\n"),
117                  afsdir);
118       return GNUNET_SYSERR;
119     }
120
121   if (GNUNET_SYSERR == GNUNET_DISK_file_size(afsdir,
122                                              &size,
123                                              GNUNET_YES,
124                                              GNUNET_YES))
125     {
126       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
127                  _("Unable to get filesize: %s.\n"),
128                  afsdir);
129       GNUNET_DISK_file_close(fh);
130       return GNUNET_SYSERR;
131     }
132
133   if (0 == size)
134     {
135       GNUNET_DISK_file_close(fh);
136       return GNUNET_OK;
137     }
138
139   buffer = GNUNET_malloc(size + 1);
140
141   if (GNUNET_SYSERR == GNUNET_DISK_file_read(fh,
142                                              buffer,
143                                              size))
144     {
145       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
146                  _("Unable to read file: %s.\n"),
147                  afsdir);
148       GNUNET_free(buffer);
149       GNUNET_DISK_file_close(fh);
150       return GNUNET_SYSERR;
151     }
152   buffer[size] = '\0';
153
154   GNUNET_DISK_file_close(fh);
155   if (0 < size)
156     {
157       line = strtok(buffer, "\n");
158       while (line != NULL)
159         {
160           query = strtok(line, ",");
161           if (NULL == query)
162             break;
163           block = strtok(NULL, ",");
164           if (NULL == block)
165             break;
166           line = strtok(NULL, "\n");
167           entry = GNUNET_malloc(sizeof(struct FlatFileEntry));
168           GNUNET_assert(GNUNET_OK == GNUNET_CRYPTO_hash_from_string(query,
169                                                                     &entry->query));
170           GNUNET_STRINGS_base64_decode(block,
171                                        strlen(block),
172                                        (void**)&block_buffer);
173           entry->block = (struct GNUNET_GNSRECORD_Block *)block_buffer;
174           if (GNUNET_OK !=
175               GNUNET_CONTAINER_multihashmap_put(plugin->hm,
176                                                 &entry->query,
177                                                 entry,
178                                                 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
179             {
180               GNUNET_free(entry);
181               GNUNET_break(0);
182             }
183         }
184     }
185   GNUNET_free(buffer);
186   return GNUNET_OK;
187 }
188
189 /**
190  * Store values in hashmap in file and free data
191  *
192  * @param plugin the plugin context
193  */
194 static int
195 store_and_free_entries(void *cls,
196                        const struct GNUNET_HashCode *key,
197                        void *value)
198 {
199   struct GNUNET_DISK_FileHandle *fh = cls;
200   struct FlatFileEntry *entry = value;
201
202   char *line;
203   char *block_b64;
204   struct GNUNET_CRYPTO_HashAsciiEncoded query;
205   size_t block_size;
206
207   block_size = ntohl(entry->block->purpose.size) +
208                sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey) +
209                sizeof(struct GNUNET_CRYPTO_EcdsaSignature);
210
211   GNUNET_STRINGS_base64_encode((char*)entry->block,
212                                block_size,
213                                &block_b64);
214   GNUNET_CRYPTO_hash_to_enc(&entry->query,
215                             &query);
216   GNUNET_asprintf(&line,
217                   "%s,%s\n",
218                   (char*)&query,
219                   block_b64);
220
221   GNUNET_free(block_b64);
222
223   GNUNET_DISK_file_write(fh,
224                          line,
225                          strlen(line));
226
227   GNUNET_free(entry->block);
228   GNUNET_free(entry);
229   GNUNET_free(line);
230   return GNUNET_YES;
231 }
232
233 /**
234  * Shutdown database connection and associate data
235  * structures.
236  * @param plugin the plugin context (state for this module)
237  */
238 static void
239 database_shutdown(struct Plugin *plugin)
240 {
241   struct GNUNET_DISK_FileHandle *fh;
242
243   fh = GNUNET_DISK_file_open(plugin->fn,
244                              GNUNET_DISK_OPEN_CREATE |
245                              GNUNET_DISK_OPEN_TRUNCATE |
246                              GNUNET_DISK_OPEN_READWRITE,
247                              GNUNET_DISK_PERM_USER_WRITE |
248                              GNUNET_DISK_PERM_USER_READ);
249   if (NULL == fh)
250     {
251       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
252                  _("Unable to initialize file: %s.\n"),
253                  plugin->fn);
254       return;
255     }
256
257   GNUNET_CONTAINER_multihashmap_iterate(plugin->hm,
258                                         &store_and_free_entries,
259                                         fh);
260   GNUNET_CONTAINER_multihashmap_destroy(plugin->hm);
261   GNUNET_DISK_file_close(fh);
262 }
263
264 static int
265 expire_blocks(void *cls,
266               const struct GNUNET_HashCode *key,
267               void *value)
268 {
269   struct Plugin *plugin = cls;
270   struct FlatFileEntry *entry = value;
271   struct GNUNET_TIME_Absolute now;
272   struct GNUNET_TIME_Absolute expiration;
273
274   now = GNUNET_TIME_absolute_get();
275   expiration = GNUNET_TIME_absolute_ntoh(entry->block->expiration_time);
276
277   if (0 == GNUNET_TIME_absolute_get_difference(now,
278                                                expiration).rel_value_us)
279     {
280       GNUNET_CONTAINER_multihashmap_remove_all(plugin->hm, key);
281     }
282   return GNUNET_YES;
283 }
284
285
286
287 /**
288  * Removes any expired block.
289  *
290  * @param plugin the plugin
291  */
292 static void
293 namecache_expire_blocks(struct Plugin *plugin)
294 {
295   GNUNET_CONTAINER_multihashmap_iterate(plugin->hm,
296                                         &expire_blocks,
297                                         plugin);
298 }
299
300
301 /**
302  * Cache a block in the datastore.
303  *
304  * @param cls closure (internal context for the plugin)
305  * @param block block to cache
306  * @return #GNUNET_OK on success, else #GNUNET_SYSERR
307  */
308 static int
309 namecache_cache_block(void *cls,
310                       const struct GNUNET_GNSRECORD_Block *block)
311 {
312   struct Plugin *plugin = cls;
313   struct GNUNET_HashCode query;
314   struct FlatFileEntry *entry;
315   size_t block_size;
316
317   namecache_expire_blocks(plugin);
318   GNUNET_CRYPTO_hash(&block->derived_key,
319                      sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
320                      &query);
321   block_size = ntohl(block->purpose.size) +
322                sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey) +
323                sizeof(struct GNUNET_CRYPTO_EcdsaSignature);
324   if (block_size > 64 * 65536)
325     {
326       GNUNET_break(0);
327       return GNUNET_SYSERR;
328     }
329   entry = GNUNET_malloc(sizeof(struct FlatFileEntry));
330   entry->block = GNUNET_malloc(block_size);
331   GNUNET_memcpy(entry->block, block, block_size);
332   GNUNET_CONTAINER_multihashmap_remove_all(plugin->hm, &query);
333   if (GNUNET_OK !=
334       GNUNET_CONTAINER_multihashmap_put(plugin->hm,
335                                         &query,
336                                         entry,
337                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
338     {
339       GNUNET_free(entry);
340       GNUNET_break(0);
341       return GNUNET_SYSERR;
342     }
343   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
344              "Caching block under derived key `%s'\n",
345              GNUNET_h2s_full(&query));
346   return GNUNET_OK;
347 }
348
349
350 /**
351  * Get the block for a particular zone and label in the
352  * datastore.  Will return at most one result to the iterator.
353  *
354  * @param cls closure (internal context for the plugin)
355  * @param query hash of public key derived from the zone and the label
356  * @param iter function to call with the result
357  * @param iter_cls closure for @a iter
358  * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
359  */
360 static int
361 namecache_lookup_block(void *cls,
362                        const struct GNUNET_HashCode *query,
363                        GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls)
364 {
365   struct Plugin *plugin = cls;
366   const struct GNUNET_GNSRECORD_Block *block;
367
368   block = GNUNET_CONTAINER_multihashmap_get(plugin->hm, query);
369   if (NULL == block)
370     return GNUNET_NO;
371   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
372              "Found block under derived key `%s'\n",
373              GNUNET_h2s_full(query));
374   iter(iter_cls, block);
375   return GNUNET_YES;
376 }
377
378
379 /**
380  * Entry point for the plugin.
381  *
382  * @param cls the "struct GNUNET_NAMECACHE_PluginEnvironment*"
383  * @return NULL on error, otherwise the plugin context
384  */
385 void *
386 libgnunet_plugin_namecache_flat_init(void *cls)
387 {
388   static struct Plugin plugin;
389   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
390   struct GNUNET_NAMECACHE_PluginFunctions *api;
391
392   if (NULL != plugin.cfg)
393     return NULL;                /* can only initialize once! */
394   memset(&plugin, 0, sizeof(struct Plugin));
395   plugin.cfg = cfg;
396   if (GNUNET_OK != database_setup(&plugin))
397     {
398       database_shutdown(&plugin);
399       return NULL;
400     }
401   api = GNUNET_new(struct GNUNET_NAMECACHE_PluginFunctions);
402   api->cls = &plugin;
403   api->cache_block = &namecache_cache_block;
404   api->lookup_block = &namecache_lookup_block;
405   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
406              _("flat plugin running\n"));
407   return api;
408 }
409
410
411 /**
412  * Exit point from the plugin.
413  *
414  * @param cls the plugin context (as returned by "init")
415  * @return always NULL
416  */
417 void *
418 libgnunet_plugin_namecache_flat_done(void *cls)
419 {
420   struct GNUNET_NAMECACHE_PluginFunctions *api = cls;
421   struct Plugin *plugin = api->cls;
422
423   database_shutdown(plugin);
424   plugin->cfg = NULL;
425   GNUNET_free(api);
426   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
427              "flat plugin is finished\n");
428   return NULL;
429 }
430
431 /* end of plugin_namecache_sqlite.c */