aboutsummaryrefslogtreecommitdiff
path: root/src/plugin/namecache
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugin/namecache')
-rw-r--r--src/plugin/namecache/Makefile.am105
-rw-r--r--src/plugin/namecache/meson.build22
-rw-r--r--src/plugin/namecache/namecache-0001.sql42
-rw-r--r--src/plugin/namecache/namecache-drop.sql25
-rw-r--r--src/plugin/namecache/plugin_namecache_flat.c429
-rw-r--r--src/plugin/namecache/plugin_namecache_postgres.c315
-rw-r--r--src/plugin/namecache/plugin_namecache_sqlite.c589
-rw-r--r--src/plugin/namecache/test_plugin_namecache.c135
-rw-r--r--src/plugin/namecache/test_plugin_namecache_flat.conf2
-rw-r--r--src/plugin/namecache/test_plugin_namecache_postgres.conf3
-rw-r--r--src/plugin/namecache/test_plugin_namecache_sqlite.conf2
11 files changed, 1669 insertions, 0 deletions
diff --git a/src/plugin/namecache/Makefile.am b/src/plugin/namecache/Makefile.am
new file mode 100644
index 000000000..980aeebe6
--- /dev/null
+++ b/src/plugin/namecache/Makefile.am
@@ -0,0 +1,105 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include $(POSTGRESQL_CPPFLAGS)
3
4plugindir = $(libdir)/gnunet
5
6pkgcfgdir= $(pkgdatadir)/config.d/
7
8libexecdir= $(pkglibdir)/libexec/
9
10sqldir = $(prefix)/share/gnunet/sql/
11
12sql_DATA = \
13 namecache-0001.sql \
14 namecache-drop.sql
15
16
17if USE_COVERAGE
18 AM_CFLAGS = --coverage -O0
19 XLIBS = -lgcov
20endif
21
22if HAVE_EXPERIMENTAL
23FLAT_PLUGIN = libgnunet_plugin_namecache_flat.la
24FLAT_TESTS = test_plugin_namecache_flat
25endif
26
27if HAVE_SQLITE
28SQLITE_PLUGIN = libgnunet_plugin_namecache_sqlite.la
29SQLITE_TESTS = test_plugin_namecache_sqlite
30endif
31
32if HAVE_POSTGRESQL
33POSTGRES_PLUGIN = libgnunet_plugin_namecache_postgres.la
34POSTGRES_TESTS = test_plugin_namecache_postgres
35endif
36
37if HAVE_SQLITE
38check_PROGRAMS = \
39 $(SQLITE_TESTS) \
40 $(POSTGRES_TESTS) \
41 $(FLAT_TESTS) \
42 $(TESTING_TESTS)
43endif
44
45if ENABLE_TEST_RUN
46AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
47TESTS = \
48 $(check_PROGRAMS)
49endif
50
51plugin_LTLIBRARIES = \
52 $(SQLITE_PLUGIN) \
53 $(FLAT_PLUGIN) \
54 $(POSTGRES_PLUGIN)
55
56libgnunet_plugin_namecache_flat_la_SOURCES = \
57 plugin_namecache_flat.c
58libgnunet_plugin_namecache_flat_la_LIBADD = \
59 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
60 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
61 $(LTLIBINTL)
62libgnunet_plugin_namecache_flat_la_LDFLAGS = \
63 $(GN_PLUGIN_LDFLAGS)
64
65libgnunet_plugin_namecache_sqlite_la_SOURCES = \
66 plugin_namecache_sqlite.c
67libgnunet_plugin_namecache_sqlite_la_LIBADD = \
68 $(top_builddir)/src/lib/sq/libgnunetsq.la \
69 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
70 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lsqlite3 \
71 $(LTLIBINTL)
72libgnunet_plugin_namecache_sqlite_la_LDFLAGS = \
73 $(GN_PLUGIN_LDFLAGS)
74
75
76libgnunet_plugin_namecache_postgres_la_SOURCES = \
77 plugin_namecache_postgres.c
78libgnunet_plugin_namecache_postgres_la_LIBADD = \
79 $(top_builddir)/src/lib/pq/libgnunetpq.la \
80 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
81 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lpq \
82 $(LTLIBINTL)
83libgnunet_plugin_namecache_postgres_la_LDFLAGS = \
84 $(GN_PLUGIN_LDFLAGS) $(POSTGRESQL_LDFLAGS)
85
86test_plugin_namecache_flat_SOURCES = \
87 test_plugin_namecache.c
88test_plugin_namecache_flat_LDADD = \
89 $(top_builddir)/src/lib/util/libgnunetutil.la
90
91test_plugin_namecache_sqlite_SOURCES = \
92 test_plugin_namecache.c
93test_plugin_namecache_sqlite_LDADD = \
94 $(top_builddir)/src/lib/util/libgnunetutil.la
95
96test_plugin_namecache_postgres_SOURCES = \
97 test_plugin_namecache.c
98test_plugin_namecache_postgres_LDADD = \
99 $(top_builddir)/src/lib/util/libgnunetutil.la
100
101EXTRA_DIST = \
102 test_plugin_namecache_sqlite.conf \
103 test_plugin_namecache_postgres.conf \
104 test_plugin_namecache_flat.conf \
105 $(sql_DATA)
diff --git a/src/plugin/namecache/meson.build b/src/plugin/namecache/meson.build
new file mode 100644
index 000000000..51504314d
--- /dev/null
+++ b/src/plugin/namecache/meson.build
@@ -0,0 +1,22 @@
1shared_module('gnunet_plugin_namecache_sqlite',
2 ['plugin_namecache_sqlite.c'],
3 dependencies: [libgnunetutil_dep,
4 libgnunetgnsrecord_dep,
5 sqlite_dep,
6 libgnunetsq_dep],
7 include_directories: [incdir, configuration_inc],
8 install: true,
9 install_dir: get_option('libdir')/'gnunet')
10
11if pq_dep.found()
12 shared_module('gnunet_plugin_namecache_postgres',
13 ['plugin_namecache_postgres.c'],
14 dependencies: [libgnunetutil_dep,
15 libgnunetgnsrecord_dep,
16 pq_dep,
17 libgnunetpq_dep],
18 include_directories: [incdir, configuration_inc],
19 install: true,
20 install_dir: get_option('libdir')/'gnunet')
21endif
22
diff --git a/src/plugin/namecache/namecache-0001.sql b/src/plugin/namecache/namecache-0001.sql
new file mode 100644
index 000000000..8509b078f
--- /dev/null
+++ b/src/plugin/namecache/namecache-0001.sql
@@ -0,0 +1,42 @@
1--
2-- This file is part of GNUnet
3-- Copyright (C) 2014--2022 GNUnet e.V.
4--
5-- GNUnet is free software; you can redistribute it and/or modify it under the
6-- terms of the GNU General Public License as published by the Free Software
7-- Foundation; either version 3, or (at your option) any later version.
8--
9-- GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY
10-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12--
13-- You should have received a copy of the GNU General Public License along with
14-- GNUnet; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15--
16
17-- Everything in one big transaction
18BEGIN;
19
20-- Check patch versioning is in place.
21SELECT _v.register_patch('namecache-0001', NULL, NULL);
22
23-------------------- Schema ----------------------------
24
25CREATE SCHEMA datacache;
26COMMENT ON SCHEMA datacache IS 'gnunet-datacache data';
27
28SET search_path TO datacache;
29
30CREATE TABLE IF NOT EXISTS ns096blocks (
31 query BYTEA NOT NULL DEFAULT '',
32 block BYTEA NOT NULL DEFAULT '',
33 expiration_time BIGINT NOT NULL DEFAULT 0);
34
35CREATE INDEX ir_query_hash
36 ON ns096blocks (query,expiration_time);
37
38CREATE INDEX ir_block_expiration
39 ON ns096blocks (expiration_time);
40
41
42COMMIT;
diff --git a/src/plugin/namecache/namecache-drop.sql b/src/plugin/namecache/namecache-drop.sql
new file mode 100644
index 000000000..197ee78c1
--- /dev/null
+++ b/src/plugin/namecache/namecache-drop.sql
@@ -0,0 +1,25 @@
1--
2-- This file is part of GNUnet
3-- Copyright (C) 2014--2022 GNUnet e.V.
4--
5-- GNUnet is free software; you can redistribute it and/or modify it under the
6-- terms of the GNU General Public License as published by the Free Software
7-- Foundation; either version 3, or (at your option) any later version.
8--
9-- GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY
10-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12--
13-- You should have received a copy of the GNU General Public License along with
14-- GNUnet; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15--
16
17-- Everything in one big transaction
18BEGIN;
19
20
21SELECT _v.unregister_patch('namecache-0001');
22
23DROP SCHEMA namecache CASCADE;
24
25COMMIT;
diff --git a/src/plugin/namecache/plugin_namecache_flat.c b/src/plugin/namecache/plugin_namecache_flat.c
new file mode 100644
index 000000000..ba118bf02
--- /dev/null
+++ b/src/plugin/namecache/plugin_namecache_flat.c
@@ -0,0 +1,429 @@
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
32/**
33 * Context for all functions in this plugin.
34 */
35struct Plugin
36{
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
50struct FlatFileEntry
51{
52 /**
53 * Block
54 */
55 struct GNUNET_GNSRECORD_Block *block;
56
57 /**
58 * query
59 */
60 struct GNUNET_HashCode query;
61};
62
63/**
64 * Initialize the database connections and associated
65 * data structures (create tables and indices
66 * as needed as well).
67 *
68 * @param plugin the plugin context (state for this module)
69 * @return #GNUNET_OK on success
70 */
71static int
72database_setup (struct Plugin *plugin)
73{
74 char *afsdir;
75 char*block_buffer;
76 char*buffer;
77 char*line;
78 char*query;
79 char*block;
80 uint64_t size;
81 struct FlatFileEntry *entry;
82 struct GNUNET_DISK_FileHandle *fh;
83
84 if (GNUNET_OK !=
85 GNUNET_CONFIGURATION_get_value_filename (plugin->cfg,
86 "namecache-flat",
87 "FILENAME",
88 &afsdir))
89 {
90 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
91 "namecache-flat", "FILENAME");
92 return GNUNET_SYSERR;
93 }
94 if (GNUNET_OK != GNUNET_DISK_file_test (afsdir))
95 {
96 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (afsdir))
97 {
98 GNUNET_break (0);
99 GNUNET_free (afsdir);
100 return GNUNET_SYSERR;
101 }
102 }
103 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
104 plugin->fn = afsdir;
105
106 /* Load data from file into hashmap */
107 plugin->hm = GNUNET_CONTAINER_multihashmap_create (10,
108 GNUNET_NO);
109 fh = GNUNET_DISK_file_open (afsdir,
110 GNUNET_DISK_OPEN_CREATE
111 | GNUNET_DISK_OPEN_READWRITE,
112 GNUNET_DISK_PERM_USER_WRITE
113 | GNUNET_DISK_PERM_USER_READ);
114 if (NULL == fh)
115 {
116 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
117 _ ("Unable to initialize file: %s.\n"),
118 afsdir);
119 return GNUNET_SYSERR;
120 }
121
122 if (GNUNET_SYSERR == GNUNET_DISK_file_size (afsdir,
123 &size,
124 GNUNET_YES,
125 GNUNET_YES))
126 {
127 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
128 _ ("Unable to get filesize: %s.\n"),
129 afsdir);
130 GNUNET_DISK_file_close (fh);
131 return GNUNET_SYSERR;
132 }
133
134 if (0 == size)
135 {
136 GNUNET_DISK_file_close (fh);
137 return GNUNET_OK;
138 }
139
140 buffer = GNUNET_malloc (size + 1);
141
142 if (GNUNET_SYSERR == GNUNET_DISK_file_read (fh,
143 buffer,
144 size))
145 {
146 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
147 _ ("Unable to read file: %s.\n"),
148 afsdir);
149 GNUNET_free (buffer);
150 GNUNET_DISK_file_close (fh);
151 return GNUNET_SYSERR;
152 }
153 buffer[size] = '\0';
154
155 GNUNET_DISK_file_close (fh);
156 if (0 < size)
157 {
158 line = strtok (buffer, "\n");
159 while (line != NULL)
160 {
161 query = strtok (line, ",");
162 if (NULL == query)
163 break;
164 block = strtok (NULL, ",");
165 if (NULL == block)
166 break;
167 line = strtok (NULL, "\n");
168 entry = GNUNET_malloc (sizeof(struct FlatFileEntry));
169 GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_hash_from_string (query,
170 &entry->query));
171 GNUNET_STRINGS_base64_decode (block,
172 strlen (block),
173 (void **) &block_buffer);
174 entry->block = (struct GNUNET_GNSRECORD_Block *) block_buffer;
175 if (GNUNET_OK !=
176 GNUNET_CONTAINER_multihashmap_put (plugin->hm,
177 &entry->query,
178 entry,
179 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
180 {
181 GNUNET_free (entry);
182 GNUNET_break (0);
183 }
184 }
185 }
186 GNUNET_free (buffer);
187 return GNUNET_OK;
188}
189
190
191/**
192 * Store values in hashmap in file and free data
193 *
194 * @param plugin the plugin context
195 */
196static int
197store_and_free_entries (void *cls,
198 const struct GNUNET_HashCode *key,
199 void *value)
200{
201 struct GNUNET_DISK_FileHandle *fh = cls;
202 struct FlatFileEntry *entry = value;
203
204 char *line;
205 char *block_b64;
206 struct GNUNET_CRYPTO_HashAsciiEncoded query;
207 size_t block_size;
208
209 block_size = GNUNET_GNSRECORD_block_get_size (entry->block);
210 GNUNET_STRINGS_base64_encode ((char *) entry->block,
211 block_size,
212 &block_b64);
213 GNUNET_CRYPTO_hash_to_enc (&entry->query,
214 &query);
215 GNUNET_asprintf (&line,
216 "%s,%s\n",
217 (char *) &query,
218 block_b64);
219
220 GNUNET_free (block_b64);
221
222 GNUNET_DISK_file_write (fh,
223 line,
224 strlen (line));
225
226 GNUNET_free (entry->block);
227 GNUNET_free (entry);
228 GNUNET_free (line);
229 return GNUNET_YES;
230}
231
232
233/**
234 * Shutdown database connection and associate data
235 * structures.
236 * @param plugin the plugin context (state for this module)
237 */
238static void
239database_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
265static int
266expire_blocks (void *cls,
267 const struct GNUNET_HashCode *key,
268 void *value)
269{
270 struct Plugin *plugin = cls;
271 struct FlatFileEntry *entry = value;
272 struct GNUNET_TIME_Absolute now;
273 struct GNUNET_TIME_Absolute expiration;
274
275 now = GNUNET_TIME_absolute_get ();
276 expiration = GNUNET_GNSRECORD_block_get_expiration (entry->block);
277
278 if (0 == GNUNET_TIME_absolute_get_difference (now,
279 expiration).rel_value_us)
280 {
281 GNUNET_CONTAINER_multihashmap_remove_all (plugin->hm, key);
282 }
283 return GNUNET_YES;
284}
285
286
287/**
288 * Removes any expired block.
289 *
290 * @param plugin the plugin
291 */
292static void
293namecache_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 */
308static int
309namecache_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_GNSRECORD_query_from_block (block,
319 &query);
320 block_size = GNUNET_GNSRECORD_block_get_size (block);
321 if (block_size > 64 * 65536)
322 {
323 GNUNET_break (0);
324 return GNUNET_SYSERR;
325 }
326 entry = GNUNET_malloc (sizeof(struct FlatFileEntry));
327 entry->block = GNUNET_malloc (block_size);
328 GNUNET_memcpy (entry->block, block, block_size);
329 GNUNET_CONTAINER_multihashmap_remove_all (plugin->hm, &query);
330 if (GNUNET_OK !=
331 GNUNET_CONTAINER_multihashmap_put (plugin->hm,
332 &query,
333 entry,
334 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
335 {
336 GNUNET_free (entry);
337 GNUNET_break (0);
338 return GNUNET_SYSERR;
339 }
340 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
341 "Caching block under derived key `%s'\n",
342 GNUNET_h2s_full (&query));
343 return GNUNET_OK;
344}
345
346
347/**
348 * Get the block for a particular zone and label in the
349 * datastore. Will return at most one result to the iterator.
350 *
351 * @param cls closure (internal context for the plugin)
352 * @param query hash of public key derived from the zone and the label
353 * @param iter function to call with the result
354 * @param iter_cls closure for @a iter
355 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
356 */
357static int
358namecache_lookup_block (void *cls,
359 const struct GNUNET_HashCode *query,
360 GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls)
361{
362 struct Plugin *plugin = cls;
363 const struct GNUNET_GNSRECORD_Block *block;
364
365 block = GNUNET_CONTAINER_multihashmap_get (plugin->hm, query);
366 if (NULL == block)
367 return GNUNET_NO;
368 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
369 "Found block under derived key `%s'\n",
370 GNUNET_h2s_full (query));
371 iter (iter_cls, block);
372 return GNUNET_YES;
373}
374
375
376/**
377 * Entry point for the plugin.
378 *
379 * @param cls the "struct GNUNET_NAMECACHE_PluginEnvironment*"
380 * @return NULL on error, otherwise the plugin context
381 */
382void *
383libgnunet_plugin_namecache_flat_init (void *cls)
384{
385 static struct Plugin plugin;
386 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
387 struct GNUNET_NAMECACHE_PluginFunctions *api;
388
389 if (NULL != plugin.cfg)
390 return NULL; /* can only initialize once! */
391 memset (&plugin, 0, sizeof(struct Plugin));
392 plugin.cfg = cfg;
393 if (GNUNET_OK != database_setup (&plugin))
394 {
395 database_shutdown (&plugin);
396 return NULL;
397 }
398 api = GNUNET_new (struct GNUNET_NAMECACHE_PluginFunctions);
399 api->cls = &plugin;
400 api->cache_block = &namecache_cache_block;
401 api->lookup_block = &namecache_lookup_block;
402 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
403 _ ("flat plugin running\n"));
404 return api;
405}
406
407
408/**
409 * Exit point from the plugin.
410 *
411 * @param cls the plugin context (as returned by "init")
412 * @return always NULL
413 */
414void *
415libgnunet_plugin_namecache_flat_done (void *cls)
416{
417 struct GNUNET_NAMECACHE_PluginFunctions *api = cls;
418 struct Plugin *plugin = api->cls;
419
420 database_shutdown (plugin);
421 plugin->cfg = NULL;
422 GNUNET_free (api);
423 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
424 "flat plugin is finished\n");
425 return NULL;
426}
427
428
429/* end of plugin_namecache_sqlite.c */
diff --git a/src/plugin/namecache/plugin_namecache_postgres.c b/src/plugin/namecache/plugin_namecache_postgres.c
new file mode 100644
index 000000000..7e2925d1a
--- /dev/null
+++ b/src/plugin/namecache/plugin_namecache_postgres.c
@@ -0,0 +1,315 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2009-2013, 2016, 2017, 2022 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_postgres.c
23 * @brief postgres-based namecache backend
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_namecache_plugin.h"
28#include "gnunet_namecache_service.h"
29#include "gnunet_gnsrecord_lib.h"
30#include "gnunet_pq_lib.h"
31
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "namecache-postgres", __VA_ARGS__)
34
35
36/**
37 * Context for all functions in this plugin.
38 */
39struct Plugin
40{
41 const struct GNUNET_CONFIGURATION_Handle *cfg;
42
43 /**
44 * Postgres database handle.
45 */
46 struct GNUNET_PQ_Context *dbh;
47};
48
49
50/**
51 * Initialize the database connections and associated
52 * data structures (create tables and indices
53 * as needed as well).
54 *
55 * @param plugin the plugin context (state for this module)
56 * @return #GNUNET_OK on success
57 */
58static enum GNUNET_GenericReturnValue
59database_setup (struct Plugin *plugin)
60{
61 struct GNUNET_PQ_PreparedStatement ps[] = {
62 GNUNET_PQ_make_prepare ("cache_block",
63 "INSERT INTO namecache.ns096blocks"
64 " (query, block, expiration_time)"
65 " VALUES"
66 " ($1, $2, $3)"),
67 GNUNET_PQ_make_prepare ("expire_blocks",
68 "DELETE FROM namecache.ns096blocks"
69 " WHERE expiration_time<$1"),
70 GNUNET_PQ_make_prepare ("delete_block",
71 "DELETE FROM namecache.ns096blocks"
72 " WHERE query=$1 AND expiration_time<=$2"),
73 GNUNET_PQ_make_prepare ("lookup_block",
74 "SELECT block"
75 " FROM namecache.ns096blocks"
76 " WHERE query=$1"
77 " ORDER BY expiration_time DESC LIMIT 1"),
78 GNUNET_PQ_PREPARED_STATEMENT_END
79 };
80
81 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
82 "namecache-postgres",
83 "namecache-",
84 NULL,
85 ps);
86 if (NULL == plugin->dbh)
87 return GNUNET_SYSERR;
88 return GNUNET_OK;
89}
90
91
92/**
93 * Removes any expired block.
94 *
95 * @param plugin the plugin
96 */
97static void
98namecache_postgres_expire_blocks (struct Plugin *plugin)
99{
100 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
101 struct GNUNET_PQ_QueryParam params[] = {
102 GNUNET_PQ_query_param_absolute_time (&now),
103 GNUNET_PQ_query_param_end
104 };
105 enum GNUNET_DB_QueryStatus res;
106
107 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
108 "expire_blocks",
109 params);
110 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != res);
111}
112
113
114/**
115 * Delete older block in the datastore.
116 *
117 * @param plugin the plugin
118 * @param query query for the block
119 * @param expiration_time how old does the block have to be for deletion
120 */
121static void
122delete_old_block (struct Plugin *plugin,
123 const struct GNUNET_HashCode *query,
124 struct GNUNET_TIME_Absolute expiration_time)
125{
126 struct GNUNET_PQ_QueryParam params[] = {
127 GNUNET_PQ_query_param_auto_from_type (query),
128 GNUNET_PQ_query_param_absolute_time (&expiration_time),
129 GNUNET_PQ_query_param_end
130 };
131 enum GNUNET_DB_QueryStatus res;
132
133 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
134 "delete_block",
135 params);
136 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != res);
137}
138
139
140/**
141 * Cache a block in the datastore.
142 *
143 * @param cls closure (internal context for the plugin)
144 * @param block block to cache
145 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
146 */
147static enum GNUNET_GenericReturnValue
148namecache_postgres_cache_block (void *cls,
149 const struct GNUNET_GNSRECORD_Block *block)
150{
151 struct Plugin *plugin = cls;
152 struct GNUNET_HashCode query;
153 size_t block_size = GNUNET_GNSRECORD_block_get_size (block);
154 struct GNUNET_TIME_Absolute exp;
155 exp = GNUNET_GNSRECORD_block_get_expiration (block);
156 struct GNUNET_PQ_QueryParam params[] = {
157 GNUNET_PQ_query_param_auto_from_type (&query),
158 GNUNET_PQ_query_param_fixed_size (block, block_size),
159 GNUNET_PQ_query_param_absolute_time (&exp),
160 GNUNET_PQ_query_param_end
161 };
162 enum GNUNET_DB_QueryStatus res;
163
164 namecache_postgres_expire_blocks (plugin);
165 GNUNET_GNSRECORD_query_from_block (block,
166 &query);
167 if (block_size > 64 * 65536)
168 {
169 GNUNET_break (0);
170 return GNUNET_SYSERR;
171 }
172 delete_old_block (plugin,
173 &query,
174 exp);
175
176 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
177 "cache_block",
178 params);
179 if (0 > res)
180 return GNUNET_SYSERR;
181 return GNUNET_OK;
182}
183
184
185/**
186 * Get the block for a particular zone and label in the
187 * datastore. Will return at most one result to the iterator.
188 *
189 * @param cls closure (internal context for the plugin)
190 * @param query hash of public key derived from the zone and the label
191 * @param iter function to call with the result
192 * @param iter_cls closure for @a iter
193 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
194 */
195static enum GNUNET_GenericReturnValue
196namecache_postgres_lookup_block (void *cls,
197 const struct GNUNET_HashCode *query,
198 GNUNET_NAMECACHE_BlockCallback iter,
199 void *iter_cls)
200{
201 struct Plugin *plugin = cls;
202 size_t bsize;
203 struct GNUNET_GNSRECORD_Block *block;
204 struct GNUNET_PQ_QueryParam params[] = {
205 GNUNET_PQ_query_param_auto_from_type (query),
206 GNUNET_PQ_query_param_end
207 };
208 struct GNUNET_PQ_ResultSpec rs[] = {
209 GNUNET_PQ_result_spec_variable_size ("block",
210 (void **) &block,
211 &bsize),
212 GNUNET_PQ_result_spec_end
213 };
214 enum GNUNET_DB_QueryStatus res;
215
216 res = GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh,
217 "lookup_block",
218 params,
219 rs);
220 if (0 > res)
221 {
222 LOG (GNUNET_ERROR_TYPE_WARNING,
223 "Failing lookup block in namecache (postgres error)\n");
224 return GNUNET_SYSERR;
225 }
226 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == res)
227 {
228 /* no result */
229 LOG (GNUNET_ERROR_TYPE_DEBUG,
230 "Ending iteration (no more results)\n");
231 return GNUNET_NO;
232 }
233 if ((bsize < sizeof(*block)))
234 {
235 GNUNET_break (0);
236 LOG (GNUNET_ERROR_TYPE_DEBUG,
237 "Failing lookup (corrupt block)\n");
238 GNUNET_PQ_cleanup_result (rs);
239 return GNUNET_SYSERR;
240 }
241 iter (iter_cls,
242 block);
243 GNUNET_PQ_cleanup_result (rs);
244 return GNUNET_OK;
245}
246
247
248/**
249 * Shutdown database connection and associate data
250 * structures.
251 *
252 * @param plugin the plugin context (state for this module)
253 */
254static void
255database_shutdown (struct Plugin *plugin)
256{
257 GNUNET_PQ_disconnect (plugin->dbh);
258 plugin->dbh = NULL;
259}
260
261
262/**
263 * Entry point for the plugin.
264 *
265 * @param cls the `struct GNUNET_NAMECACHE_PluginEnvironment *`
266 * @return NULL on error, otherwise the plugin context
267 */
268void *
269libgnunet_plugin_namecache_postgres_init (void *cls)
270{
271 static struct Plugin plugin;
272 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
273 struct GNUNET_NAMECACHE_PluginFunctions *api;
274
275 if (NULL != plugin.cfg)
276 return NULL; /* can only initialize once! */
277 memset (&plugin, 0, sizeof(struct Plugin));
278 plugin.cfg = cfg;
279 if (GNUNET_OK != database_setup (&plugin))
280 {
281 database_shutdown (&plugin);
282 return NULL;
283 }
284 api = GNUNET_new (struct GNUNET_NAMECACHE_PluginFunctions);
285 api->cls = &plugin;
286 api->cache_block = &namecache_postgres_cache_block;
287 api->lookup_block = &namecache_postgres_lookup_block;
288 LOG (GNUNET_ERROR_TYPE_INFO,
289 "Postgres namecache plugin running\n");
290 return api;
291}
292
293
294/**
295 * Exit point from the plugin.
296 *
297 * @param cls the plugin context (as returned by "init")
298 * @return always NULL
299 */
300void *
301libgnunet_plugin_namecache_postgres_done (void *cls)
302{
303 struct GNUNET_NAMECACHE_PluginFunctions *api = cls;
304 struct Plugin *plugin = api->cls;
305
306 database_shutdown (plugin);
307 plugin->cfg = NULL;
308 GNUNET_free (api);
309 LOG (GNUNET_ERROR_TYPE_DEBUG,
310 "Postgres namecache plugin is finished\n");
311 return NULL;
312}
313
314
315/* end of plugin_namecache_postgres.c */
diff --git a/src/plugin/namecache/plugin_namecache_sqlite.c b/src/plugin/namecache/plugin_namecache_sqlite.c
new file mode 100644
index 000000000..d8b485381
--- /dev/null
+++ b/src/plugin/namecache/plugin_namecache_sqlite.c
@@ -0,0 +1,589 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2009-2013 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_sqlite.c
23 * @brief sqlite-based namecache backend
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_sq_lib.h"
28#include "gnunet_namecache_plugin.h"
29#include "gnunet_namecache_service.h"
30#include "gnunet_gnsrecord_lib.h"
31#include <sqlite3.h>
32
33/**
34 * After how many ms "busy" should a DB operation fail for good? A
35 * low value makes sure that we are more responsive to requests
36 * (especially PUTs). A high value guarantees a higher success rate
37 * (SELECTs in iterate can take several seconds despite LIMIT=1).
38 *
39 * The default value of 1s should ensure that users do not experience
40 * huge latencies while at the same time allowing operations to
41 * succeed with reasonable probability.
42 */
43#define BUSY_TIMEOUT_MS 1000
44
45
46/**
47 * Log an error message at log-level 'level' that indicates
48 * a failure of the command 'cmd' on file 'filename'
49 * with the message given by strerror(errno).
50 */
51#define LOG_SQLITE(db, level, cmd) do { GNUNET_log_from (level, \
52 "namecache-sqlite", _ ( \
53 "`%s' failed at %s:%d with error: %s\n"), \
54 cmd, \
55 __FILE__, __LINE__, \
56 sqlite3_errmsg ( \
57 db->dbh)); \
58} while (0)
59
60#define LOG(kind, ...) GNUNET_log_from (kind, "namecache-sqlite", __VA_ARGS__)
61
62
63/**
64 * Context for all functions in this plugin.
65 */
66struct Plugin
67{
68 const struct GNUNET_CONFIGURATION_Handle *cfg;
69
70 /**
71 * Database filename.
72 */
73 char *fn;
74
75 /**
76 * Native SQLite database handle.
77 */
78 sqlite3 *dbh;
79
80 /**
81 * Precompiled SQL for caching a block
82 */
83 sqlite3_stmt *cache_block;
84
85 /**
86 * Precompiled SQL for deleting an older block
87 */
88 sqlite3_stmt *delete_block;
89
90 /**
91 * Precompiled SQL for looking up a block
92 */
93 sqlite3_stmt *lookup_block;
94
95 /**
96 * Precompiled SQL for removing expired blocks
97 */
98 sqlite3_stmt *expire_blocks;
99};
100
101
102/**
103 * Initialize the database connections and associated
104 * data structures (create tables and indices
105 * as needed as well).
106 *
107 * @param plugin the plugin context (state for this module)
108 * @return #GNUNET_OK on success
109 */
110static int
111database_setup (struct Plugin *plugin)
112{
113 struct GNUNET_SQ_ExecuteStatement es[] = {
114 GNUNET_SQ_make_try_execute ("PRAGMA temp_store=MEMORY"),
115 GNUNET_SQ_make_try_execute ("PRAGMA synchronous=NORMAL"),
116 GNUNET_SQ_make_try_execute ("PRAGMA legacy_file_format=OFF"),
117 GNUNET_SQ_make_try_execute ("PRAGMA auto_vacuum=INCREMENTAL"),
118 GNUNET_SQ_make_try_execute ("PRAGMA encoding=\"UTF-8\""),
119 GNUNET_SQ_make_try_execute ("PRAGMA locking_mode=EXCLUSIVE"),
120 GNUNET_SQ_make_try_execute ("PRAGMA page_size=4092"),
121 GNUNET_SQ_make_try_execute ("PRAGMA journal_mode=WAL"),
122 GNUNET_SQ_make_execute ("CREATE TABLE IF NOT EXISTS ns096blocks ("
123 " query BLOB NOT NULL,"
124 " block BLOB NOT NULL,"
125 " expiration_time INT8 NOT NULL"
126 ")"),
127 GNUNET_SQ_make_execute ("CREATE INDEX IF NOT EXISTS ir_query_hash "
128 "ON ns096blocks (query,expiration_time)"),
129 GNUNET_SQ_make_execute ("CREATE INDEX IF NOT EXISTS ir_block_expiration "
130 "ON ns096blocks (expiration_time)"),
131 GNUNET_SQ_EXECUTE_STATEMENT_END
132 };
133 struct GNUNET_SQ_PrepareStatement ps[] = {
134 GNUNET_SQ_make_prepare (
135 "INSERT INTO ns096blocks (query,block,expiration_time) VALUES (?, ?, ?)",
136 &plugin->cache_block),
137 GNUNET_SQ_make_prepare ("DELETE FROM ns096blocks WHERE expiration_time<?",
138 &plugin->expire_blocks),
139 GNUNET_SQ_make_prepare (
140 "DELETE FROM ns096blocks WHERE query=? AND expiration_time<=?",
141 &plugin->delete_block),
142 GNUNET_SQ_make_prepare ("SELECT block FROM ns096blocks WHERE query=? "
143 "ORDER BY expiration_time DESC LIMIT 1",
144 &plugin->lookup_block),
145 GNUNET_SQ_PREPARE_END
146 };
147 char *afsdir;
148
149 if (GNUNET_OK !=
150 GNUNET_CONFIGURATION_get_value_filename (plugin->cfg,
151 "namecache-sqlite",
152 "FILENAME",
153 &afsdir))
154 {
155 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
156 "namecache-sqlite",
157 "FILENAME");
158 return GNUNET_SYSERR;
159 }
160 if (GNUNET_OK !=
161 GNUNET_DISK_file_test (afsdir))
162 {
163 if (GNUNET_OK !=
164 GNUNET_DISK_directory_create_for_file (afsdir))
165 {
166 GNUNET_break (0);
167 GNUNET_free (afsdir);
168 return GNUNET_SYSERR;
169 }
170 }
171 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
172 plugin->fn = afsdir;
173
174 /* Open database and precompile statements */
175 if (SQLITE_OK !=
176 sqlite3_open (plugin->fn, &plugin->dbh))
177 {
178 LOG (GNUNET_ERROR_TYPE_ERROR,
179 _ ("Unable to initialize SQLite: %s.\n"),
180 sqlite3_errmsg (plugin->dbh));
181 return GNUNET_SYSERR;
182 }
183 if (GNUNET_OK !=
184 GNUNET_SQ_exec_statements (plugin->dbh,
185 es))
186 {
187 GNUNET_break (0);
188 LOG (GNUNET_ERROR_TYPE_ERROR,
189 _ ("Failed to setup database at `%s'\n"),
190 plugin->fn);
191 return GNUNET_SYSERR;
192 }
193 GNUNET_break (SQLITE_OK ==
194 sqlite3_busy_timeout (plugin->dbh,
195 BUSY_TIMEOUT_MS));
196
197 if (GNUNET_OK !=
198 GNUNET_SQ_prepare (plugin->dbh,
199 ps))
200 {
201 GNUNET_break (0);
202 LOG (GNUNET_ERROR_TYPE_ERROR,
203 _ ("Failed to setup database at `%s'\n"),
204 plugin->fn);
205 return GNUNET_SYSERR;
206 }
207
208 return GNUNET_OK;
209}
210
211
212/**
213 * Shutdown database connection and associate data
214 * structures.
215 * @param plugin the plugin context (state for this module)
216 */
217static void
218database_shutdown (struct Plugin *plugin)
219{
220 int result;
221 sqlite3_stmt *stmt;
222
223 if (NULL != plugin->cache_block)
224 sqlite3_finalize (plugin->cache_block);
225 if (NULL != plugin->lookup_block)
226 sqlite3_finalize (plugin->lookup_block);
227 if (NULL != plugin->expire_blocks)
228 sqlite3_finalize (plugin->expire_blocks);
229 if (NULL != plugin->delete_block)
230 sqlite3_finalize (plugin->delete_block);
231 result = sqlite3_close (plugin->dbh);
232 if (result == SQLITE_BUSY)
233 {
234 LOG (GNUNET_ERROR_TYPE_WARNING,
235 _ (
236 "Tried to close sqlite without finalizing all prepared statements.\n"));
237 stmt = sqlite3_next_stmt (plugin->dbh,
238 NULL);
239 while (stmt != NULL)
240 {
241 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
242 "sqlite",
243 "Closing statement %p\n",
244 stmt);
245 result = sqlite3_finalize (stmt);
246 if (result != SQLITE_OK)
247 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
248 "sqlite",
249 "Failed to close statement %p: %d\n",
250 stmt,
251 result);
252 stmt = sqlite3_next_stmt (plugin->dbh,
253 NULL);
254 }
255 result = sqlite3_close (plugin->dbh);
256 }
257 if (SQLITE_OK != result)
258 LOG_SQLITE (plugin,
259 GNUNET_ERROR_TYPE_ERROR,
260 "sqlite3_close");
261
262 GNUNET_free (plugin->fn);
263}
264
265
266/**
267 * Removes any expired block.
268 *
269 * @param plugin the plugin
270 */
271static void
272namecache_sqlite_expire_blocks (struct Plugin *plugin)
273{
274 struct GNUNET_TIME_Absolute now;
275 now = GNUNET_TIME_absolute_get ();
276 struct GNUNET_SQ_QueryParam params[] = {
277 GNUNET_SQ_query_param_absolute_time (&now),
278 GNUNET_SQ_query_param_end
279 };
280 int n;
281
282 if (GNUNET_OK !=
283 GNUNET_SQ_bind (plugin->expire_blocks,
284 params))
285 {
286 LOG_SQLITE (plugin,
287 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
288 "sqlite3_bind_XXXX");
289 GNUNET_SQ_reset (plugin->dbh,
290 plugin->expire_blocks);
291 return;
292 }
293 n = sqlite3_step (plugin->expire_blocks);
294 GNUNET_SQ_reset (plugin->dbh,
295 plugin->expire_blocks);
296 switch (n)
297 {
298 case SQLITE_DONE:
299 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
300 "sqlite",
301 "Records expired\n");
302 return;
303
304 case SQLITE_BUSY:
305 LOG_SQLITE (plugin,
306 GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
307 "sqlite3_step");
308 return;
309
310 default:
311 LOG_SQLITE (plugin,
312 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
313 "sqlite3_step");
314 return;
315 }
316}
317
318
319/**
320 * Cache a block in the datastore.
321 *
322 * @param cls closure (internal context for the plugin)
323 * @param block block to cache
324 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
325 */
326static int
327namecache_sqlite_cache_block (void *cls,
328 const struct GNUNET_GNSRECORD_Block *block)
329{
330 static struct GNUNET_TIME_Absolute last_expire;
331 struct Plugin *plugin = cls;
332 struct GNUNET_HashCode query;
333 struct GNUNET_TIME_Absolute expiration;
334 size_t block_size = GNUNET_GNSRECORD_block_get_size (block);
335 struct GNUNET_SQ_QueryParam del_params[] = {
336 GNUNET_SQ_query_param_auto_from_type (&query),
337 GNUNET_SQ_query_param_absolute_time (&expiration),
338 GNUNET_SQ_query_param_end
339 };
340 struct GNUNET_SQ_QueryParam ins_params[] = {
341 GNUNET_SQ_query_param_auto_from_type (&query),
342 GNUNET_SQ_query_param_fixed_size (block,
343 block_size),
344 GNUNET_SQ_query_param_absolute_time (&expiration),
345 GNUNET_SQ_query_param_end
346 };
347 int n;
348
349 /* run expiration of old cache entries once per hour */
350 if (GNUNET_TIME_absolute_get_duration (last_expire).rel_value_us >
351 GNUNET_TIME_UNIT_HOURS.rel_value_us)
352 {
353 last_expire = GNUNET_TIME_absolute_get ();
354 namecache_sqlite_expire_blocks (plugin);
355 }
356 GNUNET_assert (GNUNET_OK ==
357 GNUNET_GNSRECORD_query_from_block (block, &query));
358 expiration = GNUNET_GNSRECORD_block_get_expiration (block);
359 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
360 "Caching new version of block %s (expires %s)\n",
361 GNUNET_h2s (&query),
362 GNUNET_STRINGS_absolute_time_to_string (expiration));
363 if (block_size > 64 * 65536)
364 {
365 GNUNET_break (0);
366 return GNUNET_SYSERR;
367 }
368
369 /* delete old version of the block */
370 if (GNUNET_OK !=
371 GNUNET_SQ_bind (plugin->delete_block,
372 del_params))
373 {
374 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
375 "sqlite3_bind_XXXX");
376 GNUNET_SQ_reset (plugin->dbh,
377 plugin->delete_block);
378 return GNUNET_SYSERR;
379 }
380 n = sqlite3_step (plugin->delete_block);
381 switch (n)
382 {
383 case SQLITE_DONE:
384 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
385 "sqlite",
386 "Old block deleted\n");
387 break;
388
389 case SQLITE_BUSY:
390 LOG_SQLITE (plugin,
391 GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
392 "sqlite3_step");
393 break;
394
395 default:
396 LOG_SQLITE (plugin,
397 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
398 "sqlite3_step");
399 break;
400 }
401 GNUNET_SQ_reset (plugin->dbh,
402 plugin->delete_block);
403
404 /* insert new version of the block */
405 if (GNUNET_OK !=
406 GNUNET_SQ_bind (plugin->cache_block,
407 ins_params))
408 {
409 LOG_SQLITE (plugin,
410 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
411 "sqlite3_bind_XXXX");
412 GNUNET_SQ_reset (plugin->dbh,
413 plugin->cache_block);
414 return GNUNET_SYSERR;
415 }
416 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
417 "Caching block under derived key `%s'\n",
418 GNUNET_h2s_full (&query));
419 n = sqlite3_step (plugin->cache_block);
420 GNUNET_SQ_reset (plugin->dbh,
421 plugin->cache_block);
422 switch (n)
423 {
424 case SQLITE_DONE:
425 LOG (GNUNET_ERROR_TYPE_DEBUG,
426 "Record stored\n");
427 return GNUNET_OK;
428
429 case SQLITE_BUSY:
430 LOG_SQLITE (plugin,
431 GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
432 "sqlite3_step");
433 return GNUNET_NO;
434
435 default:
436 LOG_SQLITE (plugin,
437 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
438 "sqlite3_step");
439 return GNUNET_SYSERR;
440 }
441}
442
443
444/**
445 * Get the block for a particular zone and label in the
446 * datastore. Will return at most one result to the iterator.
447 *
448 * @param cls closure (internal context for the plugin)
449 * @param query hash of public key derived from the zone and the label
450 * @param iter function to call with the result
451 * @param iter_cls closure for @a iter
452 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
453 */
454static int
455namecache_sqlite_lookup_block (void *cls,
456 const struct GNUNET_HashCode *query,
457 GNUNET_NAMECACHE_BlockCallback iter,
458 void *iter_cls)
459{
460 struct Plugin *plugin = cls;
461 int ret;
462 int sret;
463 size_t block_size;
464 const struct GNUNET_GNSRECORD_Block *block;
465 struct GNUNET_SQ_QueryParam params[] = {
466 GNUNET_SQ_query_param_auto_from_type (query),
467 GNUNET_SQ_query_param_end
468 };
469 struct GNUNET_SQ_ResultSpec rs[] = {
470 GNUNET_SQ_result_spec_variable_size ((void **) &block,
471 &block_size),
472 GNUNET_SQ_result_spec_end
473 };
474
475 if (GNUNET_OK !=
476 GNUNET_SQ_bind (plugin->lookup_block,
477 params))
478 {
479 LOG_SQLITE (plugin,
480 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
481 "sqlite3_bind_XXXX");
482 GNUNET_SQ_reset (plugin->dbh,
483 plugin->lookup_block);
484 return GNUNET_SYSERR;
485 }
486 ret = GNUNET_NO;
487 if (SQLITE_ROW ==
488 (sret = sqlite3_step (plugin->lookup_block)))
489 {
490 if (GNUNET_OK !=
491 GNUNET_SQ_extract_result (plugin->lookup_block,
492 rs))
493 {
494 GNUNET_break (0);
495 ret = GNUNET_SYSERR;
496 }
497 else if ((block_size < sizeof(struct GNUNET_GNSRECORD_Block)))
498 {
499 GNUNET_break (0);
500 GNUNET_SQ_cleanup_result (rs);
501 ret = GNUNET_SYSERR;
502 }
503 else
504 {
505 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
506 "Found block under derived key `%s'\n",
507 GNUNET_h2s_full (query));
508 iter (iter_cls,
509 block);
510 GNUNET_SQ_cleanup_result (rs);
511 ret = GNUNET_YES;
512 }
513 }
514 else
515 {
516 if (SQLITE_DONE != sret)
517 {
518 LOG_SQLITE (plugin,
519 GNUNET_ERROR_TYPE_ERROR,
520 "sqlite_step");
521 ret = GNUNET_SYSERR;
522 }
523 else
524 {
525 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
526 "No block found under derived key `%s'\n",
527 GNUNET_h2s_full (query));
528 }
529 }
530 GNUNET_SQ_reset (plugin->dbh,
531 plugin->lookup_block);
532 return ret;
533}
534
535
536/**
537 * Entry point for the plugin.
538 *
539 * @param cls the "struct GNUNET_NAMECACHE_PluginEnvironment*"
540 * @return NULL on error, otherwise the plugin context
541 */
542void *
543libgnunet_plugin_namecache_sqlite_init (void *cls)
544{
545 static struct Plugin plugin;
546 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
547 struct GNUNET_NAMECACHE_PluginFunctions *api;
548
549 if (NULL != plugin.cfg)
550 return NULL; /* can only initialize once! */
551 memset (&plugin, 0, sizeof(struct Plugin));
552 plugin.cfg = cfg;
553 if (GNUNET_OK != database_setup (&plugin))
554 {
555 database_shutdown (&plugin);
556 return NULL;
557 }
558 api = GNUNET_new (struct GNUNET_NAMECACHE_PluginFunctions);
559 api->cls = &plugin;
560 api->cache_block = &namecache_sqlite_cache_block;
561 api->lookup_block = &namecache_sqlite_lookup_block;
562 LOG (GNUNET_ERROR_TYPE_INFO,
563 _ ("Sqlite database running\n"));
564 return api;
565}
566
567
568/**
569 * Exit point from the plugin.
570 *
571 * @param cls the plugin context (as returned by "init")
572 * @return always NULL
573 */
574void *
575libgnunet_plugin_namecache_sqlite_done (void *cls)
576{
577 struct GNUNET_NAMECACHE_PluginFunctions *api = cls;
578 struct Plugin *plugin = api->cls;
579
580 database_shutdown (plugin);
581 plugin->cfg = NULL;
582 GNUNET_free (api);
583 LOG (GNUNET_ERROR_TYPE_DEBUG,
584 "sqlite plugin is finished\n");
585 return NULL;
586}
587
588
589/* end of plugin_namecache_sqlite.c */
diff --git a/src/plugin/namecache/test_plugin_namecache.c b/src/plugin/namecache/test_plugin_namecache.c
new file mode 100644
index 000000000..141698a23
--- /dev/null
+++ b/src/plugin/namecache/test_plugin_namecache.c
@@ -0,0 +1,135 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012 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 * @file namecache/test_plugin_namecache.c
22 * @brief Test for the namecache plugins
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_namecache_plugin.h"
28#include "gnunet_testing_lib.h"
29
30
31static int ok;
32
33/**
34 * Name of plugin under test.
35 */
36static const char *plugin_name;
37
38
39/**
40 * Function called when the service shuts down. Unloads our namecache
41 * plugin.
42 *
43 * @param api api to unload
44 */
45static void
46unload_plugin (struct GNUNET_NAMECACHE_PluginFunctions *api)
47{
48 char *libname;
49
50 GNUNET_asprintf (&libname, "libgnunet_plugin_namecache_%s", plugin_name);
51 GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api));
52 GNUNET_free (libname);
53}
54
55
56/**
57 * Load the namecache plugin.
58 *
59 * @param cfg configuration to pass
60 * @return NULL on error
61 */
62static struct GNUNET_NAMECACHE_PluginFunctions *
63load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg)
64{
65 struct GNUNET_NAMECACHE_PluginFunctions *ret;
66 char *libname;
67
68 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Loading `%s' namecache plugin\n"),
69 plugin_name);
70 GNUNET_asprintf (&libname, "libgnunet_plugin_namecache_%s", plugin_name);
71 if (NULL == (ret = GNUNET_PLUGIN_load (libname, (void *) cfg)))
72 {
73 fprintf (stderr, "Failed to load plugin `%s'!\n", plugin_name);
74 GNUNET_free (libname);
75 return NULL;
76 }
77 GNUNET_free (libname);
78 return ret;
79}
80
81
82static void
83run (void *cls, char *const *args, const char *cfgfile,
84 const struct GNUNET_CONFIGURATION_Handle *cfg)
85{
86 struct GNUNET_NAMECACHE_PluginFunctions *nsp;
87
88 ok = 0;
89 nsp = load_plugin (cfg);
90 if (NULL == nsp)
91 {
92 fprintf (stderr,
93 "%s",
94 "Failed to initialize namecache. Database likely not setup, skipping test.\n");
95 return;
96 }
97
98 unload_plugin (nsp);
99}
100
101
102int
103main (int argc, char *argv[])
104{
105 char cfg_name[PATH_MAX];
106 char *const xargv[] = {
107 "test-plugin-namecache",
108 "-c",
109 cfg_name,
110 NULL
111 };
112 struct GNUNET_GETOPT_CommandLineOption options[] = {
113 GNUNET_GETOPT_OPTION_END
114 };
115
116 plugin_name = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]);
117 GNUNET_snprintf (cfg_name, sizeof(cfg_name), "test_plugin_namecache_%s.conf",
118 plugin_name);
119
120 GNUNET_DISK_purge_cfg_dir (cfg_name, "GNUNET_TEST_HOME");
121
122 GNUNET_log_setup ("test-plugin-namecache",
123 "WARNING",
124 NULL);
125 GNUNET_PROGRAM_run ((sizeof(xargv) / sizeof(char *)) - 1, xargv,
126 "test-plugin-namecache", "nohelp", options, &run, NULL);
127
128 if (ok != 0)
129 fprintf (stderr, "Missed some testcases: %d\n", ok);
130 GNUNET_DISK_purge_cfg_dir (cfg_name, "GNUNET_TEST_HOME");
131 return ok;
132}
133
134
135/* end of test_plugin_namecache.c */
diff --git a/src/plugin/namecache/test_plugin_namecache_flat.conf b/src/plugin/namecache/test_plugin_namecache_flat.conf
new file mode 100644
index 000000000..efe77e37a
--- /dev/null
+++ b/src/plugin/namecache/test_plugin_namecache_flat.conf
@@ -0,0 +1,2 @@
1[namecache-flat]
2FILENAME = $GNUNET_TMP/gnunet-test-plugin-namecache-flat/flatdb
diff --git a/src/plugin/namecache/test_plugin_namecache_postgres.conf b/src/plugin/namecache/test_plugin_namecache_postgres.conf
new file mode 100644
index 000000000..8473857d5
--- /dev/null
+++ b/src/plugin/namecache/test_plugin_namecache_postgres.conf
@@ -0,0 +1,3 @@
1[namestore-postgres]
2CONFIG = connect_timeout=10; dbname=gnunetcheck
3TEMPORARY_TABLE = YES
diff --git a/src/plugin/namecache/test_plugin_namecache_sqlite.conf b/src/plugin/namecache/test_plugin_namecache_sqlite.conf
new file mode 100644
index 000000000..24eecd286
--- /dev/null
+++ b/src/plugin/namecache/test_plugin_namecache_sqlite.conf
@@ -0,0 +1,2 @@
1[namestore-sqlite]
2FILENAME = $GNUNET_TMP/gnunet-test-plugin-namestore-sqlite/sqlite.db