aboutsummaryrefslogtreecommitdiff
path: root/src/plugin
diff options
context:
space:
mode:
authort3sserakt <t3ss@posteo.de>2023-10-20 20:18:18 +0200
committert3sserakt <t3ss@posteo.de>2023-10-20 20:18:18 +0200
commit692fd292fcb8096f7c8260046e53046f41a666d1 (patch)
tree9e67ad32e6cfc99f8bbe424f882688bd48d52373 /src/plugin
parent9376034c29f27f7b7baff17c767b0c68e7ced37c (diff)
parent9862fc36d1c97aaed13c8aa17ebadfca420a5943 (diff)
downloadgnunet-692fd292fcb8096f7c8260046e53046f41a666d1.tar.gz
gnunet-692fd292fcb8096f7c8260046e53046f41a666d1.zip
Merge branch 'master' of ssh://git.gnunet.org/gnunet
Diffstat (limited to 'src/plugin')
-rw-r--r--src/plugin/Makefile.am10
-rw-r--r--src/plugin/datacache/Makefile.am76
-rw-r--r--src/plugin/datacache/datacache-0001.sql48
-rw-r--r--src/plugin/datacache/datacache-drop.sql25
-rw-r--r--src/plugin/datacache/meson.build30
-rw-r--r--src/plugin/datacache/plugin_datacache_heap.c530
-rw-r--r--src/plugin/datacache/plugin_datacache_postgres.c616
-rw-r--r--src/plugin/datacache/plugin_datacache_sqlite.c1063
-rw-r--r--src/plugin/datacache/plugin_datacache_template.c172
-rw-r--r--src/plugin/datastore/Makefile.am150
-rw-r--r--src/plugin/datastore/datastore-0001.sql49
-rw-r--r--src/plugin/datastore/datastore-drop.sql25
-rw-r--r--src/plugin/datastore/meson.build75
-rw-r--r--src/plugin/datastore/perf_plugin_datastore.c573
-rw-r--r--src/plugin/datastore/perf_plugin_datastore_data_heap.conf7
-rw-r--r--src/plugin/datastore/perf_plugin_datastore_data_postgres.conf10
-rw-r--r--src/plugin/datastore/perf_plugin_datastore_data_sqlite.conf4
-rw-r--r--src/plugin/datastore/plugin_datastore_heap.c944
-rw-r--r--src/plugin/datastore/plugin_datastore_postgres.c930
-rw-r--r--src/plugin/datastore/plugin_datastore_sqlite.c1375
-rw-r--r--src/plugin/datastore/plugin_datastore_template.c274
-rw-r--r--src/plugin/datastore/test_plugin_datastore.c478
-rw-r--r--src/plugin/datastore/test_plugin_datastore_data_heap.conf6
-rw-r--r--src/plugin/datastore/test_plugin_datastore_data_postgres.conf10
-rw-r--r--src/plugin/datastore/test_plugin_datastore_data_sqlite.conf4
-rw-r--r--src/plugin/dht/Makefile.am27
-rw-r--r--src/plugin/dht/meson.build9
-rw-r--r--src/plugin/dht/plugin_block_dht.c311
-rw-r--r--src/plugin/dhtu/.gitignore1
-rw-r--r--src/plugin/dhtu/Makefile.am81
-rw-r--r--src/plugin/dhtu/dhtu.conf7
-rw-r--r--src/plugin/dhtu/meson.build61
-rw-r--r--src/plugin/dhtu/plugin_dhtu_gnunet.c603
-rw-r--r--src/plugin/dhtu/plugin_dhtu_ip.c1171
-rw-r--r--src/plugin/dhtu/test_dhtu_ip.c45
-rw-r--r--src/plugin/dhtu/testing_dhtu_cmd_send.c119
-rw-r--r--src/plugin/dns/Makefile.am25
-rw-r--r--src/plugin/dns/meson.build6
-rw-r--r--src/plugin/dns/plugin_block_dns.c290
-rw-r--r--src/plugin/fs/Makefile.am1
-rw-r--r--src/plugin/gns/Makefile.am2
-rw-r--r--src/plugin/gns/meson.build2
-rw-r--r--src/plugin/meson.build10
-rw-r--r--src/plugin/messenger/Makefile.am26
-rw-r--r--src/plugin/messenger/meson.build7
-rw-r--r--src/plugin/messenger/plugin_gnsrecord_messenger.c298
-rw-r--r--src/plugin/namecache/Makefile.am9
-rw-r--r--src/plugin/namestore/Makefile.am29
-rw-r--r--src/plugin/namestore/meson.build20
-rw-r--r--src/plugin/namestore/plugin_rest_namestore.c1364
-rw-r--r--src/plugin/peerstore/Makefile.am67
-rw-r--r--src/plugin/peerstore/meson.build9
-rw-r--r--src/plugin/peerstore/plugin_peerstore_flat.c606
-rw-r--r--src/plugin/peerstore/plugin_peerstore_sqlite.c702
-rw-r--r--src/plugin/peerstore/test_plugin_peerstore.c224
-rw-r--r--src/plugin/peerstore/test_plugin_peerstore_flat.conf5
-rw-r--r--src/plugin/peerstore/test_plugin_peerstore_sqlite.conf2
-rw-r--r--src/plugin/reclaim/Makefile.am6
-rw-r--r--src/plugin/reclaim/meson.build8
-rw-r--r--src/plugin/regex/Makefile.am40
-rw-r--r--src/plugin/regex/meson.build23
-rw-r--r--src/plugin/regex/plugin_block_regex.c380
-rw-r--r--src/plugin/regex/regex_block_lib.c431
-rw-r--r--src/plugin/regex/regex_block_lib.h193
-rw-r--r--src/plugin/revocation/Makefile.am3
-rw-r--r--src/plugin/revocation/meson.build2
-rw-r--r--src/plugin/revocation/plugin_block_revocation.c28
-rw-r--r--src/plugin/seti/Makefile.am25
-rw-r--r--src/plugin/seti/meson.build7
-rw-r--r--src/plugin/seti/plugin_block_seti_test.c197
-rw-r--r--src/plugin/setu/Makefile.am21
-rw-r--r--src/plugin/setu/meson.build7
-rw-r--r--src/plugin/setu/plugin_block_setu_test.c195
73 files changed, 11647 insertions, 3542 deletions
diff --git a/src/plugin/Makefile.am b/src/plugin/Makefile.am
index 1a1ecffa7..78e06fd63 100644
--- a/src/plugin/Makefile.am
+++ b/src/plugin/Makefile.am
@@ -1,10 +1,16 @@
1SUBDIRS = \ 1SUBDIRS = \
2 block \ 2 block \
3 dns \
3 gnsrecord \ 4 gnsrecord \
4 dhtu \ 5 peerstore \
6 datacache \
7 datastore \
5 namecache \ 8 namecache \
6 namestore \ 9 namestore \
10 dht \
7 revocation \ 11 revocation \
12 regex \
8 gns \ 13 gns \
9 fs \ 14 fs \
10 reclaim 15 reclaim \
16 messenger
diff --git a/src/plugin/datacache/Makefile.am b/src/plugin/datacache/Makefile.am
new file mode 100644
index 000000000..b9ae06975
--- /dev/null
+++ b/src/plugin/datacache/Makefile.am
@@ -0,0 +1,76 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4plugindir = $(libdir)/gnunet
5
6pkgcfgdir= $(pkgdatadir)/config.d/
7
8sqldir = $(prefix)/share/gnunet/sql/
9
10sql_DATA = \
11 datacache-0001.sql \
12 datacache-drop.sql
13
14
15if USE_COVERAGE
16 AM_CFLAGS = --coverage -O0
17 XLIBS = -lgcov
18endif
19
20if HAVE_SQLITE
21 SQLITE_PLUGIN = libgnunet_plugin_datacache_sqlite.la
22endif
23if HAVE_POSTGRESQL
24 POSTGRES_PLUGIN = libgnunet_plugin_datacache_postgres.la
25endif
26
27plugin_LTLIBRARIES = \
28 $(SQLITE_PLUGIN) \
29 $(POSTGRES_PLUGIN) \
30 libgnunet_plugin_datacache_heap.la
31
32# Real plugins should of course go into
33# plugin_LTLIBRARIES
34noinst_LTLIBRARIES = \
35 libgnunet_plugin_datacache_template.la
36
37
38libgnunet_plugin_datacache_sqlite_la_SOURCES = \
39 plugin_datacache_sqlite.c
40libgnunet_plugin_datacache_sqlite_la_LIBADD = \
41 $(top_builddir)/src/lib/sq/libgnunetsq.la \
42 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lsqlite3 \
43 $(LTLIBINTL)
44libgnunet_plugin_datacache_sqlite_la_LDFLAGS = \
45 $(GN_PLUGIN_LDFLAGS)
46
47libgnunet_plugin_datacache_heap_la_SOURCES = \
48 plugin_datacache_heap.c
49libgnunet_plugin_datacache_heap_la_LIBADD = \
50 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
51 $(LTLIBINTL)
52libgnunet_plugin_datacache_heap_la_LDFLAGS = \
53 $(GN_PLUGIN_LDFLAGS)
54
55libgnunet_plugin_datacache_postgres_la_SOURCES = \
56 plugin_datacache_postgres.c
57libgnunet_plugin_datacache_postgres_la_LIBADD = \
58 $(top_builddir)/src/lib/pq/libgnunetpq.la \
59 $(top_builddir)/src/lib/util/libgnunetutil.la \
60 $(GN_PLUGIN_LDFLAGS) -lpq
61libgnunet_plugin_datacache_postgres_la_CPPFLAGS = \
62 $(POSTGRESQL_CPPFLAGS) $(AM_CPPFLAGS)
63libgnunet_plugin_datacache_postgres_la_LDFLAGS = \
64 $(GN_PLUGIN_LDFLAGS) $(POSTGRESQL_LDFLAGS)
65
66libgnunet_plugin_datacache_template_la_SOURCES = \
67 plugin_datacache_template.c
68libgnunet_plugin_datacache_template_la_LIBADD = \
69 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
70 $(LTLIBINTL)
71libgnunet_plugin_datacache_template_la_LDFLAGS = \
72 $(GN_PLUGIN_LDFLAGS)
73
74
75EXTRA_DIST = \
76 $(sql_DATA)
diff --git a/src/plugin/datacache/datacache-0001.sql b/src/plugin/datacache/datacache-0001.sql
new file mode 100644
index 000000000..6567de3c2
--- /dev/null
+++ b/src/plugin/datacache/datacache-0001.sql
@@ -0,0 +1,48 @@
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('datacache-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 gn180dc (
31 oid BIGINT GENERATED BY DEFAULT AS IDENTITY,
32 type INT4 NOT NULL,
33 ro INT4 NOT NULL,
34 prox INT4 NOT NULL,
35 expiration_time INT8 NOT NULL,
36 key BYTEA NOT NULL CHECK(LENGTH(key)=64),
37 trunc BYTEA NOT NULL CHECK(LENGTH(trunc)=32),
38 value BYTEA NOT NULL,
39 path BYTEA DEFAULT NULL);
40
41CREATE INDEX IF NOT EXISTS idx_oid
42 ON gn180dc (oid);
43CREATE INDEX IF NOT EXISTS idx_key
44 ON gn180dc (key);
45CREATE INDEX IF NOT EXISTS idx_dt
46 ON gn180dc (expiration_time);
47
48COMMIT;
diff --git a/src/plugin/datacache/datacache-drop.sql b/src/plugin/datacache/datacache-drop.sql
new file mode 100644
index 000000000..2dd84bca8
--- /dev/null
+++ b/src/plugin/datacache/datacache-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('datacache-0001');
22
23DROP SCHEMA datacache CASCADE;
24
25COMMIT;
diff --git a/src/plugin/datacache/meson.build b/src/plugin/datacache/meson.build
new file mode 100644
index 000000000..3c9e7d330
--- /dev/null
+++ b/src/plugin/datacache/meson.build
@@ -0,0 +1,30 @@
1install_data ('datacache-0001.sql',
2 'datacache-drop.sql',
3 install_dir: get_option('datadir')/'gnunet'/'sql')
4
5shared_module('gnunet_plugin_datacache_sqlite',
6 ['plugin_datacache_sqlite.c'],
7 dependencies: [libgnunetutil_dep,
8 sqlite_dep,
9 libgnunetsq_dep],
10 include_directories: [incdir, configuration_inc],
11 install: true,
12 install_dir: get_option('libdir')/'gnunet')
13
14shared_module('gnunet_plugin_datacache_heap',
15 ['plugin_datacache_heap.c'],
16 dependencies: [libgnunetutil_dep],
17 include_directories: [incdir, configuration_inc],
18 install: true,
19 install_dir: get_option('libdir')/'gnunet')
20
21if pq_dep.found()
22 shared_module('gnunet_plugin_datacache_postgres',
23 ['plugin_datacache_postgres.c'],
24 dependencies: [libgnunetutil_dep,
25 pq_dep,
26 libgnunetpq_dep],
27 include_directories: [incdir, configuration_inc],
28 install: true,
29 install_dir: get_option('libdir')/'gnunet')
30endif
diff --git a/src/plugin/datacache/plugin_datacache_heap.c b/src/plugin/datacache/plugin_datacache_heap.c
new file mode 100644
index 000000000..0dd8e47f8
--- /dev/null
+++ b/src/plugin/datacache/plugin_datacache_heap.c
@@ -0,0 +1,530 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012, 2015, 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 datacache/plugin_datacache_heap.c
23 * @brief heap-only implementation of a database backend for the datacache
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_datacache_plugin.h"
29
30#define LOG(kind, ...) GNUNET_log_from (kind, "datacache-heap", __VA_ARGS__)
31
32#define LOG_STRERROR_FILE(kind, op, fn) GNUNET_log_from_strerror_file (kind, \
33 "datacache-heap", \
34 op, fn)
35
36#define NUM_HEAPS 24
37
38/**
39 * Context for all functions in this plugin.
40 */
41struct Plugin
42{
43 /**
44 * Our execution environment.
45 */
46 struct GNUNET_DATACACHE_PluginEnvironment *env;
47
48 /**
49 * Our hash map.
50 */
51 struct GNUNET_CONTAINER_MultiHashMap *map;
52
53 /**
54 * Heaps sorted by distance.
55 */
56 struct GNUNET_CONTAINER_Heap *heaps[NUM_HEAPS];
57};
58
59
60/**
61 * Entry in the hash map.
62 */
63struct Value
64{
65 /**
66 * Block data.
67 */
68 struct GNUNET_DATACACHE_Block block;
69
70 /**
71 * Corresponding node in the heap.
72 */
73 struct GNUNET_CONTAINER_HeapNode *hn;
74
75 /**
76 * Put path as a non-const pointer.
77 */
78 struct GNUNET_DHT_PathElement *put_path;
79
80 /**
81 * How close is the hash to us? Determines which heap we are in!
82 */
83 uint32_t distance;
84
85};
86
87
88#define OVERHEAD (sizeof(struct Value) + 64)
89
90
91/**
92 * Closure for #put_cb().
93 */
94struct PutContext
95{
96 /**
97 * Block data.
98 */
99 const struct GNUNET_DATACACHE_Block *block;
100
101 /**
102 * Value to set to true if an equivalent block was found.
103 */
104 bool found;
105};
106
107
108/**
109 * Function called during PUT to detect if an equivalent block
110 * already exists.
111 *
112 * @param cls the `struct PutContext`
113 * @param key the key for the value(s)
114 * @param value an existing value
115 * @return #GNUNET_YES if not found (to continue to iterate)
116 */
117static enum GNUNET_GenericReturnValue
118put_cb (void *cls,
119 const struct GNUNET_HashCode *key,
120 void *value)
121{
122 struct PutContext *put_ctx = cls;
123 struct Value *val = value;
124
125 if ((val->block.data_size == put_ctx->block->data_size) &&
126 (val->block.type == put_ctx->block->type) &&
127 (0 == memcmp (val->block.data,
128 put_ctx->block->data,
129 put_ctx->block->data_size)))
130 {
131 put_ctx->found = true;
132 val->block.expiration_time
133 = GNUNET_TIME_absolute_max (val->block.expiration_time,
134 put_ctx->block->expiration_time);
135 /* replace old path with new path */
136 GNUNET_free (val->put_path);
137 val->put_path = GNUNET_memdup (put_ctx->block->put_path,
138 put_ctx->block->put_path_length
139 * sizeof (struct GNUNET_DHT_PathElement));
140 val->block.put_path = val->put_path;
141 val->block.put_path_length = put_ctx->block->put_path_length;
142 GNUNET_CONTAINER_heap_update_cost (val->hn,
143 val->block.expiration_time.abs_value_us);
144 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
145 "Got same value for key %s and type %u (size %u vs %u)\n",
146 GNUNET_h2s (key),
147 (unsigned int) val->block.type,
148 (unsigned int) val->block.data_size,
149 (unsigned int) put_ctx->block->data_size);
150 return GNUNET_NO;
151 }
152 return GNUNET_YES;
153}
154
155
156/**
157 * Store an item in the datastore.
158 *
159 * @param cls closure (our `struct Plugin`)
160 * @param xor_distance how close is @a key to our PID?
161 * @param block data to store
162 * @return 0 if duplicate, -1 on error, number of bytes used otherwise
163 */
164static ssize_t
165heap_plugin_put (void *cls,
166 uint32_t xor_distance,
167 const struct GNUNET_DATACACHE_Block *block)
168{
169 struct Plugin *plugin = cls;
170 struct Value *val;
171 struct PutContext put_ctx = {
172 .block = block,
173 .found = false
174 };
175
176 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
177 "Storing %u bytes under key %s with path length %u\n",
178 (unsigned int) block->data_size,
179 GNUNET_h2s (&block->key),
180 block->put_path_length);
181 GNUNET_CONTAINER_multihashmap_get_multiple (plugin->map,
182 &block->key,
183 &put_cb,
184 &put_ctx);
185 if (GNUNET_YES == put_ctx.found)
186 return 0;
187 val = GNUNET_malloc (sizeof(struct Value)
188 + block->data_size);
189 GNUNET_memcpy (&val[1],
190 block->data,
191 block->data_size);
192 val->block = *block;
193 val->block.data = &val[1];
194 if (xor_distance >= NUM_HEAPS)
195 val->distance = NUM_HEAPS - 1;
196 else
197 val->distance = xor_distance;
198 if (0 != block->put_path_length)
199 {
200 val->put_path
201 = GNUNET_memdup (block->put_path,
202 block->put_path_length
203 * sizeof (struct GNUNET_DHT_PathElement));
204 val->block.put_path = val->put_path;
205 }
206 (void) GNUNET_CONTAINER_multihashmap_put (plugin->map,
207 &val->block.key,
208 val,
209 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
210 val->hn = GNUNET_CONTAINER_heap_insert (
211 plugin->heaps[val->distance],
212 val,
213 val->block.expiration_time.abs_value_us);
214 return val->block.data_size + OVERHEAD;
215}
216
217
218/**
219 * Closure for #get_cb().
220 */
221struct GetContext
222{
223 /**
224 * Function to call for each result.
225 */
226 GNUNET_DATACACHE_Iterator iter;
227
228 /**
229 * Closure for @e iter.
230 */
231 void *iter_cls;
232
233 /**
234 * Number of results found.
235 */
236 unsigned int cnt;
237
238 /**
239 * Block type requested.
240 */
241 enum GNUNET_BLOCK_Type type;
242};
243
244
245/**
246 * Function called during GET to find matching blocks.
247 * Only matches by type.
248 *
249 * @param cls the `struct GetContext`
250 * @param key the key for the value(s)
251 * @param value an existing value
252 * @return #GNUNET_YES to continue to iterate
253 */
254static enum GNUNET_GenericReturnValue
255get_cb (void *cls,
256 const struct GNUNET_HashCode *key,
257 void *value)
258{
259 struct GetContext *get_ctx = cls;
260 struct Value *val = value;
261 enum GNUNET_GenericReturnValue ret;
262
263 if ( (get_ctx->type != val->block.type) &&
264 (GNUNET_BLOCK_TYPE_ANY != get_ctx->type) )
265 {
266 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
267 "Result for key %s does not match block type %d\n",
268 GNUNET_h2s (key),
269 get_ctx->type);
270 return GNUNET_OK;
271 }
272 if (GNUNET_TIME_absolute_is_past (val->block.expiration_time))
273 {
274 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
275 "Result for key %s is expired\n",
276 GNUNET_h2s (key));
277 return GNUNET_OK;
278 }
279 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
280 "Found result for key %s\n",
281 GNUNET_h2s (key));
282 if (NULL != get_ctx->iter)
283 ret = get_ctx->iter (get_ctx->iter_cls,
284 &val->block);
285 else
286 ret = GNUNET_YES;
287 get_ctx->cnt++;
288 return ret;
289}
290
291
292/**
293 * Iterate over the results for a particular key
294 * in the datastore.
295 *
296 * @param cls closure (our `struct Plugin`)
297 * @param key
298 * @param type entries of which type are relevant?
299 * @param iter maybe NULL (to just count)
300 * @param iter_cls closure for @a iter
301 * @return the number of results found
302 */
303static unsigned int
304heap_plugin_get (void *cls,
305 const struct GNUNET_HashCode *key,
306 enum GNUNET_BLOCK_Type type,
307 GNUNET_DATACACHE_Iterator iter,
308 void *iter_cls)
309{
310 struct Plugin *plugin = cls;
311 struct GetContext get_ctx;
312
313 get_ctx.type = type;
314 get_ctx.iter = iter;
315 get_ctx.iter_cls = iter_cls;
316 get_ctx.cnt = 0;
317 GNUNET_CONTAINER_multihashmap_get_multiple (plugin->map,
318 key,
319 &get_cb,
320 &get_ctx);
321 return get_ctx.cnt;
322}
323
324
325/**
326 * Delete the entry with the lowest expiration value
327 * from the datacache right now.
328 *
329 * @param cls closure (our `struct Plugin`)
330 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
331 */
332static enum GNUNET_GenericReturnValue
333heap_plugin_del (void *cls)
334{
335 struct Plugin *plugin = cls;
336 struct Value *val;
337
338 for (unsigned int i = 0; i < NUM_HEAPS; i++)
339 {
340 val = GNUNET_CONTAINER_heap_remove_root (plugin->heaps[i]);
341 if (NULL != val)
342 break;
343 }
344 if (NULL == val)
345 return GNUNET_SYSERR;
346 GNUNET_assert (GNUNET_YES ==
347 GNUNET_CONTAINER_multihashmap_remove (plugin->map,
348 &val->block.key,
349 val));
350 plugin->env->delete_notify (plugin->env->cls,
351 &val->block.key,
352 val->block.data_size + OVERHEAD);
353 GNUNET_free (val->put_path);
354 GNUNET_free (val);
355 return GNUNET_OK;
356}
357
358
359/**
360 * Closure for #find_closest().
361 */
362struct GetClosestContext
363{
364 struct Value **values;
365
366 const struct GNUNET_HashCode *key;
367
368 enum GNUNET_BLOCK_Type type;
369
370 unsigned int num_results;
371
372};
373
374
375static enum GNUNET_GenericReturnValue
376find_closest (void *cls,
377 const struct GNUNET_HashCode *key,
378 void *value)
379{
380 struct GetClosestContext *gcc = cls;
381 struct Value *val = value;
382 unsigned int j;
383
384 if (1 != GNUNET_CRYPTO_hash_cmp (key,
385 gcc->key))
386 return GNUNET_OK; /* useless */
387 if ( (val->block.type != gcc->type) &&
388 (GNUNET_BLOCK_TYPE_ANY != gcc->type) )
389 return GNUNET_OK; /* useless */
390 j = gcc->num_results;
391 for (unsigned int i = 0; i < gcc->num_results; i++)
392 {
393 if (NULL == gcc->values[i])
394 {
395 j = i;
396 break;
397 }
398 if (1 ==
399 GNUNET_CRYPTO_hash_cmp (&gcc->values[i]->block.key,
400 key))
401 {
402 j = i;
403 break;
404 }
405 }
406 if (j == gcc->num_results)
407 return GNUNET_OK;
408 gcc->values[j] = val;
409 return GNUNET_OK;
410}
411
412
413/**
414 * Iterate over the results that are "close" to a particular key in
415 * the datacache. "close" is defined as numerically larger than @a
416 * key (when interpreted as a circular address space), with small
417 * distance.
418 *
419 * @param cls closure (internal context for the plugin)
420 * @param key area of the keyspace to look into
421 * @param type desired block type for the replies
422 * @param num_results number of results that should be returned to @a iter
423 * @param iter maybe NULL (to just count)
424 * @param iter_cls closure for @a iter
425 * @return the number of results found
426 */
427static unsigned int
428heap_plugin_get_closest (void *cls,
429 const struct GNUNET_HashCode *key,
430 enum GNUNET_BLOCK_Type type,
431 unsigned int num_results,
432 GNUNET_DATACACHE_Iterator iter,
433 void *iter_cls)
434{
435 struct Plugin *plugin = cls;
436 struct Value *values[num_results];
437 struct GetClosestContext gcc = {
438 .values = values,
439 .type = type,
440 .num_results = num_results * 2,
441 .key = key
442 };
443
444 GNUNET_CONTAINER_multihashmap_iterate (plugin->map,
445 &find_closest,
446 &gcc);
447 for (unsigned int i = 0; i < num_results * 2; i++)
448 {
449 if (NULL == values[i])
450 return i;
451 if ( (NULL != iter) &&
452 (GNUNET_SYSERR ==
453 iter (iter_cls,
454 &values[i]->block)) )
455 {
456 LOG (GNUNET_ERROR_TYPE_DEBUG,
457 "Ending iteration (client error)\n");
458 return i;
459 }
460 }
461 return num_results * 2;
462}
463
464
465/**
466 * Entry point for the plugin.
467 *
468 * @param cls closure (the `struct GNUNET_DATACACHE_PluginEnvironmnet`)
469 * @return the plugin's closure (our `struct Plugin`)
470 */
471void *
472libgnunet_plugin_datacache_heap_init (void *cls)
473{
474 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
475 struct GNUNET_DATACACHE_PluginFunctions *api;
476 struct Plugin *plugin;
477
478 plugin = GNUNET_new (struct Plugin);
479 plugin->map = GNUNET_CONTAINER_multihashmap_create (1024, /* FIXME: base on quota! */
480 GNUNET_YES);
481 for (unsigned int i = 0; i < NUM_HEAPS; i++)
482 plugin->heaps[i] = GNUNET_CONTAINER_heap_create (
483 GNUNET_CONTAINER_HEAP_ORDER_MIN);
484 plugin->env = env;
485 api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
486 api->cls = plugin;
487 api->get = &heap_plugin_get;
488 api->put = &heap_plugin_put;
489 api->del = &heap_plugin_del;
490 api->get_closest = &heap_plugin_get_closest;
491 LOG (GNUNET_ERROR_TYPE_INFO,
492 _ ("Heap datacache running\n"));
493 return api;
494}
495
496
497/**
498 * Exit point from the plugin.
499 *
500 * @param cls closure (our "struct Plugin")
501 * @return NULL
502 */
503void *
504libgnunet_plugin_datacache_heap_done (void *cls)
505{
506 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
507 struct Plugin *plugin = api->cls;
508 struct Value *val;
509
510 for (unsigned int i = 0; i < NUM_HEAPS; i++)
511 {
512 while (NULL != (val = GNUNET_CONTAINER_heap_remove_root (plugin->heaps[i])))
513 {
514 GNUNET_assert (GNUNET_YES ==
515 GNUNET_CONTAINER_multihashmap_remove (plugin->map,
516 &val->block.key,
517 val));
518 GNUNET_free (val->put_path);
519 GNUNET_free (val);
520 }
521 GNUNET_CONTAINER_heap_destroy (plugin->heaps[i]);
522 }
523 GNUNET_CONTAINER_multihashmap_destroy (plugin->map);
524 GNUNET_free (plugin);
525 GNUNET_free (api);
526 return NULL;
527}
528
529
530/* end of plugin_datacache_heap.c */
diff --git a/src/plugin/datacache/plugin_datacache_postgres.c b/src/plugin/datacache/plugin_datacache_postgres.c
new file mode 100644
index 000000000..8bfd04aea
--- /dev/null
+++ b/src/plugin/datacache/plugin_datacache_postgres.c
@@ -0,0 +1,616 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2006, 2009, 2010, 2012, 2015, 2017, 2018, 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 datacache/plugin_datacache_postgres.c
23 * @brief postgres for an implementation of a database backend for the datacache
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_pq_lib.h"
29#include "gnunet_datacache_plugin.h"
30
31#define LOG(kind, ...) GNUNET_log_from (kind, "datacache-postgres", __VA_ARGS__)
32
33/**
34 * Per-entry overhead estimate
35 */
36#define OVERHEAD (sizeof(struct GNUNET_HashCode) + 24)
37
38/**
39 * Context for all functions in this plugin.
40 */
41struct Plugin
42{
43 /**
44 * Our execution environment.
45 */
46 struct GNUNET_DATACACHE_PluginEnvironment *env;
47
48 /**
49 * Native Postgres database handle.
50 */
51 struct GNUNET_PQ_Context *dbh;
52
53 /**
54 * Number of key-value pairs in the database.
55 */
56 unsigned int num_items;
57};
58
59
60/**
61 * @brief Get a database handle
62 *
63 * @param plugin global context
64 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
65 */
66static enum GNUNET_GenericReturnValue
67init_connection (struct Plugin *plugin)
68{
69 struct GNUNET_PQ_PreparedStatement ps[] = {
70 GNUNET_PQ_make_prepare ("getkt",
71 "SELECT expiration_time,type,ro,value,trunc,path"
72 " FROM datacache.gn180dc"
73 " WHERE key=$1 AND type=$2 AND expiration_time >= $3"),
74 GNUNET_PQ_make_prepare ("getk",
75 "SELECT expiration_time,type,ro,value,trunc,path"
76 " FROM datacache.gn180dc"
77 " WHERE key=$1 AND expiration_time >= $2"),
78 GNUNET_PQ_make_prepare ("getex",
79 "SELECT LENGTH(value) AS len,oid,key"
80 " FROM datacache.gn180dc"
81 " WHERE expiration_time < $1"
82 " ORDER BY expiration_time ASC LIMIT 1"),
83 GNUNET_PQ_make_prepare ("getm",
84 "SELECT LENGTH(value) AS len,oid,key"
85 " FROM datacache.gn180dc"
86 " ORDER BY prox ASC, expiration_time ASC LIMIT 1"),
87 GNUNET_PQ_make_prepare ("get_closest",
88 "(SELECT expiration_time,type,ro,value,trunc,path,key"
89 " FROM datacache.gn180dc"
90 " WHERE key >= $1"
91 " AND expiration_time >= $2"
92 " AND ( (type = $3) OR ( 0 = $3) )"
93 " ORDER BY key ASC"
94 " LIMIT $4)"
95 " UNION "
96 "(SELECT expiration_time,type,ro,value,trunc,path,key"
97 " FROM datacache.gn180dc"
98 " WHERE key <= $1"
99 " AND expiration_time >= $2"
100 " AND ( (type = $3) OR ( 0 = $3) )"
101 " ORDER BY key DESC"
102 " LIMIT $4)"),
103 GNUNET_PQ_make_prepare ("delrow",
104 "DELETE FROM datacache.gn180dc"
105 " WHERE oid=$1"),
106 GNUNET_PQ_make_prepare ("put",
107 "INSERT INTO datacache.gn180dc"
108 " (type, ro, prox, expiration_time, key, value, trunc, path) "
109 "VALUES ($1, $2, $3, $4, $5, $6, $7, $8)"),
110 GNUNET_PQ_PREPARED_STATEMENT_END
111 };
112
113 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->env->cfg,
114 "datacache-postgres",
115 "datacache-",
116 NULL,
117 ps);
118 if (NULL == plugin->dbh)
119 return GNUNET_SYSERR;
120 return GNUNET_OK;
121}
122
123
124/**
125 * Store an item in the datastore.
126 *
127 * @param cls closure (our `struct Plugin`)
128 * @param prox proximity of @a key to my PID
129 * @param block data to store
130 * @return 0 if duplicate, -1 on error, number of bytes used otherwise
131 */
132static ssize_t
133postgres_plugin_put (void *cls,
134 uint32_t prox,
135 const struct GNUNET_DATACACHE_Block *block)
136{
137 struct Plugin *plugin = cls;
138 uint32_t type32 = (uint32_t) block->type;
139 uint32_t ro32 = (uint32_t) block->type;
140 struct GNUNET_PQ_QueryParam params[] = {
141 GNUNET_PQ_query_param_uint32 (&type32),
142 GNUNET_PQ_query_param_uint32 (&ro32),
143 GNUNET_PQ_query_param_uint32 (&prox),
144 GNUNET_PQ_query_param_absolute_time (&block->expiration_time),
145 GNUNET_PQ_query_param_auto_from_type (&block->key),
146 GNUNET_PQ_query_param_fixed_size (block->data,
147 block->data_size),
148 GNUNET_PQ_query_param_auto_from_type (&block->trunc_peer),
149 (0 == block->put_path_length)
150 ? GNUNET_PQ_query_param_null ()
151 : GNUNET_PQ_query_param_fixed_size (
152 block->put_path,
153 block->put_path_length
154 * sizeof(struct GNUNET_DHT_PathElement)),
155 GNUNET_PQ_query_param_end
156 };
157 enum GNUNET_DB_QueryStatus ret;
158
159 ret = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
160 "put",
161 params);
162 if (0 > ret)
163 return -1;
164 plugin->num_items++;
165 return block->data_size + OVERHEAD;
166}
167
168
169/**
170 * Closure for #handle_results.
171 */
172struct HandleResultContext
173{
174 /**
175 * Function to call on each result, may be NULL.
176 */
177 GNUNET_DATACACHE_Iterator iter;
178
179 /**
180 * Closure for @e iter.
181 */
182 void *iter_cls;
183
184 /**
185 * Key used.
186 */
187 const struct GNUNET_HashCode *key;
188};
189
190
191/**
192 * Function to be called with the results of a SELECT statement
193 * that has returned @a num_results results. Parse the result
194 * and call the callback given in @a cls
195 *
196 * @param cls closure of type `struct HandleResultContext`
197 * @param result the postgres result
198 * @param num_results the number of results in @a result
199 */
200static void
201handle_results (void *cls,
202 PGresult *result,
203 unsigned int num_results)
204{
205 struct HandleResultContext *hrc = cls;
206
207 for (unsigned int i = 0; i < num_results; i++)
208 {
209 uint32_t type32;
210 uint32_t bro32;
211 void *data;
212 struct GNUNET_DATACACHE_Block block;
213 void *path = NULL;
214 size_t path_size = 0;
215 struct GNUNET_PQ_ResultSpec rs[] = {
216 GNUNET_PQ_result_spec_absolute_time ("expiration_time",
217 &block.expiration_time),
218 GNUNET_PQ_result_spec_uint32 ("type",
219 &type32),
220 GNUNET_PQ_result_spec_uint32 ("ro",
221 &bro32),
222 GNUNET_PQ_result_spec_variable_size ("value",
223 &data,
224 &block.data_size),
225 GNUNET_PQ_result_spec_auto_from_type ("trunc",
226 &block.trunc_peer),
227 GNUNET_PQ_result_spec_allow_null (
228 GNUNET_PQ_result_spec_variable_size ("path",
229 &path,
230 &path_size),
231 NULL),
232 GNUNET_PQ_result_spec_end
233 };
234
235 if (GNUNET_YES !=
236 GNUNET_PQ_extract_result (result,
237 rs,
238 i))
239 {
240 GNUNET_break (0);
241 return;
242 }
243 if (0 != (path_size % sizeof(struct GNUNET_DHT_PathElement)))
244 {
245 GNUNET_break (0);
246 path_size = 0;
247 path = NULL;
248 }
249 block.data = data;
250 block.put_path = path;
251 block.put_path_length
252 = path_size / sizeof (struct GNUNET_DHT_PathElement);
253 block.type = (enum GNUNET_BLOCK_Type) type32;
254 block.ro = (enum GNUNET_DHT_RouteOption) bro32;
255 block.key = *hrc->key;
256 LOG (GNUNET_ERROR_TYPE_DEBUG,
257 "Found result of size %u bytes and type %u in database\n",
258 (unsigned int) block.data_size,
259 (unsigned int) block.type);
260 if ( (NULL != hrc->iter) &&
261 (GNUNET_SYSERR ==
262 hrc->iter (hrc->iter_cls,
263 &block)) )
264 {
265 LOG (GNUNET_ERROR_TYPE_DEBUG,
266 "Ending iteration (client error)\n");
267 GNUNET_PQ_cleanup_result (rs);
268 return;
269 }
270 GNUNET_PQ_cleanup_result (rs);
271 }
272}
273
274
275/**
276 * Iterate over the results for a particular key
277 * in the datastore.
278 *
279 * @param cls closure (our `struct Plugin`)
280 * @param key key to look for
281 * @param type entries of which type are relevant?
282 * @param iter maybe NULL (to just count)
283 * @param iter_cls closure for @a iter
284 * @return the number of results found
285 */
286static unsigned int
287postgres_plugin_get (void *cls,
288 const struct GNUNET_HashCode *key,
289 enum GNUNET_BLOCK_Type type,
290 GNUNET_DATACACHE_Iterator iter,
291 void *iter_cls)
292{
293 struct Plugin *plugin = cls;
294 uint32_t type32 = (uint32_t) type;
295 struct GNUNET_TIME_Absolute now = { 0 };
296 struct GNUNET_PQ_QueryParam paramk[] = {
297 GNUNET_PQ_query_param_auto_from_type (key),
298 GNUNET_PQ_query_param_absolute_time (&now),
299 GNUNET_PQ_query_param_end
300 };
301 struct GNUNET_PQ_QueryParam paramkt[] = {
302 GNUNET_PQ_query_param_auto_from_type (key),
303 GNUNET_PQ_query_param_uint32 (&type32),
304 GNUNET_PQ_query_param_absolute_time (&now),
305 GNUNET_PQ_query_param_end
306 };
307 enum GNUNET_DB_QueryStatus res;
308 struct HandleResultContext hr_ctx;
309
310 now = GNUNET_TIME_absolute_get ();
311 hr_ctx.iter = iter;
312 hr_ctx.iter_cls = iter_cls;
313 hr_ctx.key = key;
314 res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
315 (0 == type) ? "getk" : "getkt",
316 (0 == type) ? paramk : paramkt,
317 &handle_results,
318 &hr_ctx);
319 if (res < 0)
320 return 0;
321 return res;
322}
323
324
325/**
326 * Delete the entry with the lowest expiration value
327 * from the datacache right now.
328 *
329 * @param cls closure (our `struct Plugin`)
330 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
331 */
332static enum GNUNET_GenericReturnValue
333postgres_plugin_del (void *cls)
334{
335 struct Plugin *plugin = cls;
336 struct GNUNET_PQ_QueryParam pempty[] = {
337 GNUNET_PQ_query_param_end
338 };
339 uint32_t size;
340 uint64_t oid;
341 struct GNUNET_HashCode key;
342 struct GNUNET_PQ_ResultSpec rs[] = {
343 GNUNET_PQ_result_spec_uint32 ("len",
344 &size),
345 GNUNET_PQ_result_spec_uint64 ("oid",
346 &oid),
347 GNUNET_PQ_result_spec_auto_from_type ("key",
348 &key),
349 GNUNET_PQ_result_spec_end
350 };
351 enum GNUNET_DB_QueryStatus res;
352 struct GNUNET_PQ_QueryParam dparam[] = {
353 GNUNET_PQ_query_param_uint64 (&oid),
354 GNUNET_PQ_query_param_end
355 };
356 struct GNUNET_TIME_Absolute now;
357 struct GNUNET_PQ_QueryParam xparam[] = {
358 GNUNET_PQ_query_param_absolute_time (&now),
359 GNUNET_PQ_query_param_end
360 };
361
362 now = GNUNET_TIME_absolute_get ();
363 res = GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh,
364 "getex",
365 xparam,
366 rs);
367 if (0 >= res)
368 res = GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh,
369 "getm",
370 pempty,
371 rs);
372 if (0 > res)
373 return GNUNET_SYSERR;
374 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == res)
375 {
376 /* no result */
377 LOG (GNUNET_ERROR_TYPE_DEBUG,
378 "Ending iteration (no more results)\n");
379 return 0;
380 }
381 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
382 "delrow",
383 dparam);
384 if (0 > res)
385 {
386 GNUNET_PQ_cleanup_result (rs);
387 return GNUNET_SYSERR;
388 }
389 plugin->num_items--;
390 plugin->env->delete_notify (plugin->env->cls,
391 &key,
392 size + OVERHEAD);
393 GNUNET_PQ_cleanup_result (rs);
394 return GNUNET_OK;
395}
396
397
398/**
399 * Closure for #extract_result_cb.
400 */
401struct ExtractResultContext
402{
403 /**
404 * Function to call for each result found.
405 */
406 GNUNET_DATACACHE_Iterator iter;
407
408 /**
409 * Closure for @e iter.
410 */
411 void *iter_cls;
412};
413
414
415/**
416 * Function to be called with the results of a SELECT statement
417 * that has returned @a num_results results. Calls the `iter`
418 * from @a cls for each result.
419 *
420 * @param cls closure with the `struct ExtractResultContext`
421 * @param result the postgres result
422 * @param num_results the number of results in @a result
423 */
424static void
425extract_result_cb (void *cls,
426 PGresult *result,
427 unsigned int num_results)
428{
429 struct ExtractResultContext *erc = cls;
430
431 if (NULL == erc->iter)
432 return;
433 for (unsigned int i = 0; i < num_results; i++)
434 {
435 uint32_t type32;
436 uint32_t bro32;
437 struct GNUNET_DATACACHE_Block block;
438 void *data;
439 void *path;
440 size_t path_size;
441 struct GNUNET_PQ_ResultSpec rs[] = {
442 GNUNET_PQ_result_spec_absolute_time ("expiration_time",
443 &block.expiration_time),
444 GNUNET_PQ_result_spec_uint32 ("type",
445 &type32),
446 GNUNET_PQ_result_spec_uint32 ("ro",
447 &bro32),
448 GNUNET_PQ_result_spec_variable_size ("value",
449 &data,
450 &block.data_size),
451 GNUNET_PQ_result_spec_auto_from_type ("trunc",
452 &block.trunc_peer),
453 GNUNET_PQ_result_spec_variable_size ("path",
454 &path,
455 &path_size),
456 GNUNET_PQ_result_spec_auto_from_type ("key",
457 &block.key),
458 GNUNET_PQ_result_spec_end
459 };
460
461 if (GNUNET_YES !=
462 GNUNET_PQ_extract_result (result,
463 rs,
464 i))
465 {
466 GNUNET_break (0);
467 return;
468 }
469 if (0 != (path_size % sizeof(struct GNUNET_DHT_PathElement)))
470 {
471 GNUNET_break (0);
472 path_size = 0;
473 path = NULL;
474 }
475 block.type = (enum GNUNET_BLOCK_Type) type32;
476 block.ro = (enum GNUNET_DHT_RouteOption) bro32;
477 block.data = data;
478 block.put_path = path;
479 block.put_path_length = path_size / sizeof (struct GNUNET_DHT_PathElement);
480 LOG (GNUNET_ERROR_TYPE_DEBUG,
481 "Found result of size %u bytes and type %u in database\n",
482 (unsigned int) block.data_size,
483 (unsigned int) block.type);
484 if ( (NULL != erc->iter) &&
485 (GNUNET_SYSERR ==
486 erc->iter (erc->iter_cls,
487 &block)) )
488 {
489 LOG (GNUNET_ERROR_TYPE_DEBUG,
490 "Ending iteration (client error)\n");
491 GNUNET_PQ_cleanup_result (rs);
492 break;
493 }
494 GNUNET_PQ_cleanup_result (rs);
495 }
496}
497
498
499/**
500 * Iterate over the results that are "close" to a particular key in
501 * the datacache. "close" is defined as numerically larger than @a
502 * key (when interpreted as a circular address space), with small
503 * distance.
504 *
505 * @param cls closure (internal context for the plugin)
506 * @param key area of the keyspace to look into
507 * @param type desired block type for the replies
508 * @param num_results number of results that should be returned to @a iter
509 * @param iter maybe NULL (to just count)
510 * @param iter_cls closure for @a iter
511 * @return the number of results found
512 */
513static unsigned int
514postgres_plugin_get_closest (void *cls,
515 const struct GNUNET_HashCode *key,
516 enum GNUNET_BLOCK_Type type,
517 unsigned int num_results,
518 GNUNET_DATACACHE_Iterator iter,
519 void *iter_cls)
520{
521 struct Plugin *plugin = cls;
522 uint32_t num_results32 = (uint32_t) num_results;
523 uint32_t type32 = (uint32_t) type;
524 struct GNUNET_TIME_Absolute now;
525 struct GNUNET_PQ_QueryParam params[] = {
526 GNUNET_PQ_query_param_auto_from_type (key),
527 GNUNET_PQ_query_param_absolute_time (&now),
528 GNUNET_PQ_query_param_uint32 (&type32),
529 GNUNET_PQ_query_param_uint32 (&num_results32),
530 GNUNET_PQ_query_param_end
531 };
532 enum GNUNET_DB_QueryStatus res;
533 struct ExtractResultContext erc;
534
535 erc.iter = iter;
536 erc.iter_cls = iter_cls;
537 now = GNUNET_TIME_absolute_get ();
538 res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
539 "get_closest",
540 params,
541 &extract_result_cb,
542 &erc);
543 if (0 > res)
544 {
545 LOG (GNUNET_ERROR_TYPE_DEBUG,
546 "Ending iteration (postgres error)\n");
547 return 0;
548 }
549 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == res)
550 {
551 /* no result */
552 LOG (GNUNET_ERROR_TYPE_DEBUG,
553 "Ending iteration (no more results)\n");
554 return 0;
555 }
556 return res;
557}
558
559
560/**
561 * Entry point for the plugin.
562 *
563 * @param cls closure (the `struct GNUNET_DATACACHE_PluginEnvironmnet`)
564 * @return the plugin's closure (our `struct Plugin`)
565 */
566void *
567libgnunet_plugin_datacache_postgres_init (void *cls)
568{
569 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
570 struct GNUNET_DATACACHE_PluginFunctions *api;
571 struct Plugin *plugin;
572
573 plugin = GNUNET_new (struct Plugin);
574 plugin->env = env;
575
576 if (GNUNET_OK != init_connection (plugin))
577 {
578 GNUNET_free (plugin);
579 return NULL;
580 }
581
582 api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
583 api->cls = plugin;
584 api->get = &postgres_plugin_get;
585 api->put = &postgres_plugin_put;
586 api->del = &postgres_plugin_del;
587 api->get_closest = &postgres_plugin_get_closest;
588 LOG (GNUNET_ERROR_TYPE_INFO,
589 "Postgres datacache running\n");
590 return api;
591}
592
593
594/**
595 * Exit point from the plugin.
596 *
597 * @param cls closure (our `struct Plugin`)
598 * @return NULL
599 */
600void *
601libgnunet_plugin_datacache_postgres_done (void *cls)
602{
603 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
604 struct Plugin *plugin = api->cls;
605
606 GNUNET_break (GNUNET_OK ==
607 GNUNET_PQ_exec_sql (plugin->dbh,
608 "datacache-drop"));
609 GNUNET_PQ_disconnect (plugin->dbh);
610 GNUNET_free (plugin);
611 GNUNET_free (api);
612 return NULL;
613}
614
615
616/* end of plugin_datacache_postgres.c */
diff --git a/src/plugin/datacache/plugin_datacache_sqlite.c b/src/plugin/datacache/plugin_datacache_sqlite.c
new file mode 100644
index 000000000..1c6f24a82
--- /dev/null
+++ b/src/plugin/datacache/plugin_datacache_sqlite.c
@@ -0,0 +1,1063 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2006, 2009, 2015, 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 datacache/plugin_datacache_sqlite.c
23 * @brief sqlite for an implementation of a database backend for the datacache
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_datacache_plugin.h"
29#include "gnunet_sq_lib.h"
30#include <sqlite3.h>
31
32#define LOG(kind, ...) GNUNET_log_from (kind, "datacache-sqlite", __VA_ARGS__)
33
34#define LOG_STRERROR_FILE(kind, op, fn) \
35 GNUNET_log_from_strerror_file (kind, "datacache-sqlite", op, fn)
36
37
38/**
39 * How much overhead do we assume per entry in the
40 * datacache?
41 */
42#define OVERHEAD (sizeof(struct GNUNET_HashCode) + 36)
43
44/**
45 * Context for all functions in this plugin.
46 */
47struct Plugin
48{
49 /**
50 * Our execution environment.
51 */
52 struct GNUNET_DATACACHE_PluginEnvironment *env;
53
54 /**
55 * Handle to the sqlite database.
56 */
57 sqlite3 *dbh;
58
59 /**
60 * Filename used for the DB.
61 */
62 char *fn;
63
64 /**
65 * Prepared statement for #sqlite_plugin_put.
66 */
67 sqlite3_stmt *insert_stmt;
68
69 /**
70 * Prepared statement for #sqlite_plugin_get.
71 */
72 sqlite3_stmt *get_count_stmt;
73
74 /**
75 * Prepared statement for #sqlite_plugin_get.
76 */
77 sqlite3_stmt *get_count_any_stmt;
78
79 /**
80 * Prepared statement for #sqlite_plugin_get.
81 */
82 sqlite3_stmt *get_stmt;
83
84 /**
85 * Prepared statement for #sqlite_plugin_get.
86 */
87 sqlite3_stmt *get_any_stmt;
88
89 /**
90 * Prepared statement for #sqlite_plugin_del.
91 */
92 sqlite3_stmt *del_select_stmt;
93
94 /**
95 * Prepared statement for #sqlite_plugin_del.
96 */
97 sqlite3_stmt *del_expired_stmt;
98
99 /**
100 * Prepared statement for #sqlite_plugin_del.
101 */
102 sqlite3_stmt *del_stmt;
103
104 /**
105 * Prepared statement for #sqlite_plugin_get_closest.
106 */
107 sqlite3_stmt *get_closest_stmt;
108
109 /**
110 * Number of key-value pairs in the database.
111 */
112 unsigned int num_items;
113};
114
115
116/**
117 * Log an error message at log-level @a level that indicates
118 * a failure of the command @a cmd with the error from the database @a db
119 *
120 * @param db database handle
121 * @param level log level
122 * @param cmd failed command
123 */
124#define LOG_SQLITE(db, level, cmd) \
125 do \
126 { \
127 LOG (level, \
128 _ ("`%s' failed at %s:%d with error: %s\n"), \
129 cmd, \
130 __FILE__, \
131 __LINE__, \
132 sqlite3_errmsg (db)); \
133 } while (0)
134
135
136/**
137 * Execute SQL statement.
138 *
139 * @param db database handle
140 * @param cmd SQL command to execute
141 */
142#define SQLITE3_EXEC(db, cmd) \
143 do \
144 { \
145 emsg = NULL; \
146 if (SQLITE_OK != \
147 sqlite3_exec (db, cmd, NULL, NULL, &emsg)) \
148 { \
149 LOG (GNUNET_ERROR_TYPE_ERROR, \
150 _ ("`%s' failed at %s:%d with error: %s\n"), \
151 "sqlite3_exec", \
152 __FILE__, \
153 __LINE__, \
154 emsg); \
155 sqlite3_free (emsg); \
156 } \
157 } while (0)
158
159
160/**
161 * @brief Prepare a SQL statement
162 *
163 * @param dbh database handle
164 * @param zSql SQL statement text
165 * @param[out] ppStmt set to the prepared statement
166 * @return 0 on success
167 */
168static int
169sq_prepare (sqlite3 *dbh,
170 const char *zSql, /* SQL statement, UTF-8 encoded */
171 sqlite3_stmt **ppStmt)
172{ /* OUT: Statement handle */
173 char *dummy;
174
175 return sqlite3_prepare (dbh,
176 zSql,
177 strlen (zSql),
178 ppStmt,
179 (const char **) &dummy);
180}
181
182
183/**
184 * Store an item in the datastore.
185 *
186 * @param cls closure (our `struct Plugin`)
187 * @param xor_distance how close is @a key to our PID?
188 * @param block data to store
189 * @return 0 if duplicate, -1 on error, number of bytes used otherwise
190 */
191static ssize_t
192sqlite_plugin_put (void *cls,
193 uint32_t xor_distance,
194 const struct GNUNET_DATACACHE_Block *block)
195{
196 struct Plugin *plugin = cls;
197 uint32_t type32 = (uint32_t) block->type;
198 uint32_t ro32 = (uint32_t) block->ro;
199 struct GNUNET_SQ_QueryParam params[] = {
200 GNUNET_SQ_query_param_uint32 (&type32),
201 GNUNET_SQ_query_param_uint32 (&ro32),
202 GNUNET_SQ_query_param_absolute_time (&block->expiration_time),
203 GNUNET_SQ_query_param_auto_from_type (&block->key),
204 GNUNET_SQ_query_param_uint32 (&xor_distance),
205 GNUNET_SQ_query_param_fixed_size (block->data,
206 block->data_size),
207 GNUNET_SQ_query_param_fixed_size (block->put_path,
208 block->put_path_length
209 * sizeof(struct GNUNET_DHT_PathElement)),
210 GNUNET_SQ_query_param_auto_from_type (&block->trunc_peer),
211 GNUNET_SQ_query_param_end
212 };
213
214 LOG (GNUNET_ERROR_TYPE_DEBUG,
215 "Processing PUT of %u bytes with key `%s' and expiration %s\n",
216 (unsigned int) block->data_size,
217 GNUNET_h2s (&block->key),
218 GNUNET_STRINGS_relative_time_to_string (
219 GNUNET_TIME_absolute_get_remaining (
220 block->expiration_time),
221 GNUNET_YES));
222 if (GNUNET_OK !=
223 GNUNET_SQ_bind (plugin->insert_stmt,
224 params))
225 {
226 LOG_SQLITE (plugin->dbh,
227 GNUNET_ERROR_TYPE_ERROR,
228 "sqlite3_bind_xxx");
229 GNUNET_SQ_reset (plugin->dbh,
230 plugin->insert_stmt);
231 return -1;
232 }
233 if (SQLITE_DONE !=
234 sqlite3_step (plugin->insert_stmt))
235 {
236 LOG_SQLITE (plugin->dbh,
237 GNUNET_ERROR_TYPE_ERROR,
238 "sqlite3_step");
239 GNUNET_SQ_reset (plugin->dbh,
240 plugin->insert_stmt);
241 return -1;
242 }
243 plugin->num_items++;
244 GNUNET_SQ_reset (plugin->dbh,
245 plugin->insert_stmt);
246 return block->data_size + OVERHEAD;
247}
248
249
250/**
251 * Iterate over the results for a particular key
252 * in the datastore.
253 *
254 * @param cls closure (our `struct Plugin`)
255 * @param key
256 * @param iter maybe NULL (to just count)
257 * @param iter_cls closure for @a iter
258 * @return the number of results found
259 */
260static unsigned int
261get_any (void *cls,
262 const struct GNUNET_HashCode *key,
263 GNUNET_DATACACHE_Iterator iter,
264 void *iter_cls)
265{
266 struct Plugin *plugin = cls;
267 struct GNUNET_TIME_Absolute now;
268 unsigned int cnt;
269 uint32_t off;
270 uint32_t btype32;
271 uint32_t bro32;
272 unsigned int total;
273 struct GNUNET_DATACACHE_Block block;
274 void *path;
275 void *data;
276 size_t path_size;
277 struct GNUNET_SQ_QueryParam params_count[] = {
278 GNUNET_SQ_query_param_auto_from_type (key),
279 GNUNET_SQ_query_param_absolute_time (&now),
280 GNUNET_SQ_query_param_end
281 };
282 struct GNUNET_SQ_QueryParam params_select[] = {
283 GNUNET_SQ_query_param_auto_from_type (key),
284 GNUNET_SQ_query_param_absolute_time (&now),
285 GNUNET_SQ_query_param_uint32 (&off),
286 GNUNET_SQ_query_param_end
287 };
288 struct GNUNET_SQ_ResultSpec rs[] = {
289 GNUNET_SQ_result_spec_variable_size (&data,
290 &block.data_size),
291 GNUNET_SQ_result_spec_absolute_time (&block.expiration_time),
292 GNUNET_SQ_result_spec_variable_size (&path,
293 &path_size),
294 GNUNET_SQ_result_spec_auto_from_type (&block.trunc_peer),
295 GNUNET_SQ_result_spec_uint32 (&btype32),
296 GNUNET_SQ_result_spec_uint32 (&bro32),
297 GNUNET_SQ_result_spec_end
298 };
299
300 now = GNUNET_TIME_absolute_get ();
301 LOG (GNUNET_ERROR_TYPE_DEBUG,
302 "Processing GET for key `%s'\n",
303 GNUNET_h2s (key));
304
305 if (GNUNET_OK !=
306 GNUNET_SQ_bind (plugin->get_count_any_stmt,
307 params_count))
308 {
309 LOG_SQLITE (plugin->dbh,
310 GNUNET_ERROR_TYPE_ERROR,
311 "sqlite3_bind_xxx");
312 GNUNET_SQ_reset (plugin->dbh,
313 plugin->get_count_any_stmt);
314 return 0;
315 }
316 if (SQLITE_ROW !=
317 sqlite3_step (plugin->get_count_any_stmt))
318 {
319 LOG_SQLITE (plugin->dbh,
320 GNUNET_ERROR_TYPE_ERROR,
321 "sqlite_step");
322 GNUNET_SQ_reset (plugin->dbh,
323 plugin->get_count_any_stmt);
324 LOG (GNUNET_ERROR_TYPE_DEBUG,
325 "No content found when processing GET for key `%s'\n",
326 GNUNET_h2s (key));
327 return 0;
328 }
329 total = sqlite3_column_int (plugin->get_count_any_stmt,
330 0);
331 GNUNET_SQ_reset (plugin->dbh,
332 plugin->get_count_any_stmt);
333 if ( (0 == total) ||
334 (NULL == iter) )
335 {
336 if (0 == total)
337 LOG (GNUNET_ERROR_TYPE_DEBUG,
338 "No content found when processing GET for key `%s'\n",
339 GNUNET_h2s (key));
340 return total;
341 }
342
343 cnt = 0;
344 block.key = *key;
345 off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
346 total);
347 while (cnt < total)
348 {
349 off = (off + 1) % total;
350 if (GNUNET_OK !=
351 GNUNET_SQ_bind (plugin->get_any_stmt,
352 params_select))
353 {
354 LOG_SQLITE (plugin->dbh,
355 GNUNET_ERROR_TYPE_ERROR,
356 "sqlite3_bind_xxx");
357 GNUNET_SQ_reset (plugin->dbh,
358 plugin->get_any_stmt);
359 return cnt;
360 }
361 if (SQLITE_ROW !=
362 sqlite3_step (plugin->get_any_stmt))
363 break;
364 if (GNUNET_OK !=
365 GNUNET_SQ_extract_result (plugin->get_any_stmt,
366 rs))
367 {
368 GNUNET_break (0);
369 GNUNET_SQ_reset (plugin->dbh,
370 plugin->get_any_stmt);
371 break;
372 }
373 if (0 != path_size % sizeof(struct GNUNET_DHT_PathElement))
374 {
375 GNUNET_break (0);
376 path_size = 0;
377 path = NULL;
378 }
379 block.data = data;
380 block.put_path = path;
381 block.put_path_length = path_size / sizeof(struct GNUNET_DHT_PathElement);
382 block.type = (enum GNUNET_BLOCK_Type) btype32;
383 block.ro = (enum GNUNET_DHT_RouteOption) bro32;
384 cnt++;
385 LOG (GNUNET_ERROR_TYPE_DEBUG,
386 "Found %u-byte result when processing GET for key `%s'\n",
387 (unsigned int) block.data_size,
388 GNUNET_h2s (&block.key));
389 if (GNUNET_OK !=
390 iter (iter_cls,
391 &block))
392 {
393 GNUNET_SQ_cleanup_result (rs);
394 GNUNET_SQ_reset (plugin->dbh,
395 plugin->get_any_stmt);
396 break;
397 }
398 GNUNET_SQ_cleanup_result (rs);
399 GNUNET_SQ_reset (plugin->dbh,
400 plugin->get_any_stmt);
401 }
402 GNUNET_SQ_reset (plugin->dbh,
403 plugin->get_any_stmt);
404 return cnt;
405}
406
407
408/**
409 * Iterate over the results for a particular key
410 * in the datastore.
411 *
412 * @param cls closure (our `struct Plugin`)
413 * @param key
414 * @param type entries of which type are relevant?
415 * @param iter maybe NULL (to just count)
416 * @param iter_cls closure for @a iter
417 * @return the number of results found
418 */
419static unsigned int
420get_typed (void *cls,
421 const struct GNUNET_HashCode *key,
422 enum GNUNET_BLOCK_Type type,
423 GNUNET_DATACACHE_Iterator iter,
424 void *iter_cls)
425{
426 struct Plugin *plugin = cls;
427 uint32_t type32 = type;
428 struct GNUNET_TIME_Absolute now;
429 unsigned int cnt;
430 uint32_t off;
431 uint32_t bro32;
432 unsigned int total;
433 struct GNUNET_DATACACHE_Block block;
434 void *path;
435 void *data;
436 size_t path_size;
437 struct GNUNET_SQ_QueryParam params_count[] = {
438 GNUNET_SQ_query_param_auto_from_type (key),
439 GNUNET_SQ_query_param_uint32 (&type32),
440 GNUNET_SQ_query_param_absolute_time (&now),
441 GNUNET_SQ_query_param_end
442 };
443 struct GNUNET_SQ_QueryParam params_select[] = {
444 GNUNET_SQ_query_param_auto_from_type (key),
445 GNUNET_SQ_query_param_uint32 (&type32),
446 GNUNET_SQ_query_param_absolute_time (&now),
447 GNUNET_SQ_query_param_uint32 (&off),
448 GNUNET_SQ_query_param_end
449 };
450 struct GNUNET_SQ_ResultSpec rs[] = {
451 GNUNET_SQ_result_spec_variable_size (&data,
452 &block.data_size),
453 GNUNET_SQ_result_spec_absolute_time (&block.expiration_time),
454 GNUNET_SQ_result_spec_variable_size (&path,
455 &path_size),
456 GNUNET_SQ_result_spec_auto_from_type (&block.trunc_peer),
457 GNUNET_SQ_result_spec_uint32 (&bro32),
458 GNUNET_SQ_result_spec_end
459 };
460
461 now = GNUNET_TIME_absolute_get ();
462 LOG (GNUNET_ERROR_TYPE_DEBUG,
463 "Processing GET for key `%s'\n",
464 GNUNET_h2s (key));
465
466 if (GNUNET_OK !=
467 GNUNET_SQ_bind (plugin->get_count_stmt,
468 params_count))
469 {
470 LOG_SQLITE (plugin->dbh,
471 GNUNET_ERROR_TYPE_ERROR,
472 "sqlite3_bind_xxx");
473 GNUNET_SQ_reset (plugin->dbh,
474 plugin->get_count_stmt);
475 return 0;
476 }
477 if (SQLITE_ROW !=
478 sqlite3_step (plugin->get_count_stmt))
479 {
480 LOG_SQLITE (plugin->dbh,
481 GNUNET_ERROR_TYPE_ERROR,
482 "sqlite_step");
483 GNUNET_SQ_reset (plugin->dbh,
484 plugin->get_count_stmt);
485 LOG (GNUNET_ERROR_TYPE_DEBUG,
486 "No content found when processing GET for key `%s'\n",
487 GNUNET_h2s (key));
488 return 0;
489 }
490 total = sqlite3_column_int (plugin->get_count_stmt,
491 0);
492 GNUNET_SQ_reset (plugin->dbh,
493 plugin->get_count_stmt);
494 if ( (0 == total) ||
495 (NULL == iter) )
496 {
497 if (0 == total)
498 LOG (GNUNET_ERROR_TYPE_DEBUG,
499 "No content found when processing GET for key `%s'\n",
500 GNUNET_h2s (key));
501 return total;
502 }
503
504 cnt = 0;
505 block.key = *key;
506 off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
507 total);
508 while (cnt < total)
509 {
510 off = (off + 1) % total;
511 if (GNUNET_OK !=
512 GNUNET_SQ_bind (plugin->get_stmt,
513 params_select))
514 {
515 LOG_SQLITE (plugin->dbh,
516 GNUNET_ERROR_TYPE_ERROR,
517 "sqlite3_bind_xxx");
518 GNUNET_SQ_reset (plugin->dbh,
519 plugin->get_stmt);
520 return cnt;
521 }
522 if (SQLITE_ROW !=
523 sqlite3_step (plugin->get_stmt))
524 break;
525 if (GNUNET_OK !=
526 GNUNET_SQ_extract_result (plugin->get_stmt,
527 rs))
528 {
529 GNUNET_break (0);
530 GNUNET_SQ_reset (plugin->dbh,
531 plugin->get_stmt);
532 break;
533 }
534 if (0 != path_size % sizeof(struct GNUNET_DHT_PathElement))
535 {
536 GNUNET_break (0);
537 path_size = 0;
538 path = NULL;
539 }
540 block.data = data;
541 block.put_path = path;
542 block.put_path_length = path_size / sizeof(struct GNUNET_DHT_PathElement);
543 block.type = type;
544 block.ro = (enum GNUNET_DHT_RouteOption) bro32;
545 cnt++;
546 LOG (GNUNET_ERROR_TYPE_DEBUG,
547 "Found %u-byte result when processing GET for key `%s'\n",
548 (unsigned int) block.data_size,
549 GNUNET_h2s (&block.key));
550 if ( (NULL != iter) &&
551 (GNUNET_OK !=
552 iter (iter_cls,
553 &block)) )
554 {
555 GNUNET_SQ_cleanup_result (rs);
556 GNUNET_SQ_reset (plugin->dbh,
557 plugin->get_stmt);
558 break;
559 }
560 GNUNET_SQ_cleanup_result (rs);
561 GNUNET_SQ_reset (plugin->dbh,
562 plugin->get_stmt);
563 }
564 GNUNET_SQ_reset (plugin->dbh,
565 plugin->get_stmt);
566 return cnt;
567}
568
569
570/**
571 * Iterate over the results for a particular key
572 * in the datastore.
573 *
574 * @param cls closure (our `struct Plugin`)
575 * @param key
576 * @param type entries of which type are relevant?
577 * @param iter maybe NULL (to just count)
578 * @param iter_cls closure for @a iter
579 * @return the number of results found
580 */
581static unsigned int
582sqlite_plugin_get (void *cls,
583 const struct GNUNET_HashCode *key,
584 enum GNUNET_BLOCK_Type type,
585 GNUNET_DATACACHE_Iterator iter,
586 void *iter_cls)
587{
588 if (GNUNET_BLOCK_TYPE_ANY == type)
589 return get_any (cls,
590 key,
591 iter,
592 iter_cls);
593 return get_typed (cls,
594 key,
595 type,
596 iter,
597 iter_cls);
598}
599
600
601/**
602 * Delete the entry with the lowest expiration value
603 * from the datacache right now.
604 *
605 * @param cls closure (our `struct Plugin`)
606 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
607 */
608static enum GNUNET_GenericReturnValue
609sqlite_plugin_del (void *cls)
610{
611 struct Plugin *plugin = cls;
612 uint64_t rowid;
613 void *data;
614 size_t data_size;
615 struct GNUNET_HashCode hc;
616 struct GNUNET_TIME_Absolute now;
617 struct GNUNET_SQ_ResultSpec rs[] = {
618 GNUNET_SQ_result_spec_uint64 (&rowid),
619 GNUNET_SQ_result_spec_auto_from_type (&hc),
620 GNUNET_SQ_result_spec_variable_size (&data,
621 &data_size),
622 GNUNET_SQ_result_spec_end
623 };
624 struct GNUNET_SQ_QueryParam params[] = {
625 GNUNET_SQ_query_param_uint64 (&rowid),
626 GNUNET_SQ_query_param_end
627 };
628 struct GNUNET_SQ_QueryParam time_params[] = {
629 GNUNET_SQ_query_param_absolute_time (&now),
630 GNUNET_SQ_query_param_end
631 };
632
633 LOG (GNUNET_ERROR_TYPE_DEBUG,
634 "Processing DEL\n");
635 now = GNUNET_TIME_absolute_get ();
636 if (GNUNET_OK !=
637 GNUNET_SQ_bind (plugin->del_expired_stmt,
638 time_params))
639 {
640 LOG_SQLITE (plugin->dbh,
641 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
642 "sqlite3_bind");
643 GNUNET_SQ_reset (plugin->dbh,
644 plugin->del_expired_stmt);
645 return GNUNET_SYSERR;
646 }
647 if ( (SQLITE_ROW !=
648 sqlite3_step (plugin->del_expired_stmt)) ||
649 (GNUNET_OK !=
650 GNUNET_SQ_extract_result (plugin->del_expired_stmt,
651 rs)))
652 {
653 GNUNET_SQ_reset (plugin->dbh,
654 plugin->del_expired_stmt);
655 if (SQLITE_ROW !=
656 sqlite3_step (plugin->del_select_stmt))
657 {
658 LOG_SQLITE (plugin->dbh,
659 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
660 "sqlite3_step");
661 GNUNET_SQ_reset (plugin->dbh,
662 plugin->del_select_stmt);
663 return GNUNET_SYSERR;
664 }
665 if (GNUNET_OK !=
666 GNUNET_SQ_extract_result (plugin->del_select_stmt,
667 rs))
668 {
669 GNUNET_SQ_reset (plugin->dbh,
670 plugin->del_select_stmt);
671 GNUNET_break (0);
672 return GNUNET_SYSERR;
673 }
674 }
675 GNUNET_SQ_cleanup_result (rs);
676 GNUNET_SQ_reset (plugin->dbh,
677 plugin->del_select_stmt);
678 if (GNUNET_OK !=
679 GNUNET_SQ_bind (plugin->del_stmt,
680 params))
681 {
682 LOG_SQLITE (plugin->dbh,
683 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
684 "sqlite3_bind");
685 GNUNET_SQ_reset (plugin->dbh,
686 plugin->del_stmt);
687 return GNUNET_SYSERR;
688 }
689 if (SQLITE_DONE !=
690 sqlite3_step (plugin->del_stmt))
691 {
692 LOG_SQLITE (plugin->dbh,
693 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
694 "sqlite3_step");
695 GNUNET_SQ_reset (plugin->dbh,
696 plugin->del_stmt);
697 return GNUNET_SYSERR;
698 }
699 plugin->num_items--;
700 plugin->env->delete_notify (plugin->env->cls,
701 &hc,
702 data_size + OVERHEAD);
703 GNUNET_SQ_reset (plugin->dbh,
704 plugin->del_stmt);
705 return GNUNET_OK;
706}
707
708
709/**
710 * Iterate over the results that are "close" to a particular key in
711 * the datacache. "close" is defined as numerically larger than @a
712 * key (when interpreted as a circular address space), with small
713 * distance.
714 *
715 * @param cls closure (internal context for the plugin)
716 * @param key area of the keyspace to look into
717 * @param type desired block type for the replies
718 * @param num_results number of results that should be returned to @a iter
719 * @param iter maybe NULL (to just count)
720 * @param iter_cls closure for @a iter
721 * @return the number of results found
722 */
723static unsigned int
724sqlite_plugin_get_closest (void *cls,
725 const struct GNUNET_HashCode *key,
726 enum GNUNET_BLOCK_Type type,
727 unsigned int num_results,
728 GNUNET_DATACACHE_Iterator iter,
729 void *iter_cls)
730{
731 struct Plugin *plugin = cls;
732 uint32_t type32 = type;
733 uint32_t num_results32 = num_results;
734 struct GNUNET_TIME_Absolute now;
735 void *data;
736 void *path;
737 size_t path_size;
738 unsigned int cnt;
739 uint32_t bro32;
740 struct GNUNET_DATACACHE_Block block;
741 uint32_t rtype32;
742 struct GNUNET_SQ_QueryParam params[] = {
743 GNUNET_SQ_query_param_auto_from_type (key),
744 GNUNET_SQ_query_param_absolute_time (&now),
745 GNUNET_SQ_query_param_uint32 (&type32),
746 GNUNET_SQ_query_param_uint32 (&num_results32),
747 GNUNET_SQ_query_param_end
748 };
749 struct GNUNET_SQ_ResultSpec rs[] = {
750 GNUNET_SQ_result_spec_variable_size (&data,
751 &block.data_size),
752 GNUNET_SQ_result_spec_absolute_time (&block.expiration_time),
753 GNUNET_SQ_result_spec_variable_size (&path,
754 &path_size),
755 GNUNET_SQ_result_spec_auto_from_type (&block.trunc_peer),
756 GNUNET_SQ_result_spec_uint32 (&rtype32),
757 GNUNET_SQ_result_spec_uint32 (&bro32),
758 GNUNET_SQ_result_spec_auto_from_type (&block.key),
759 GNUNET_SQ_result_spec_end
760 };
761
762 now = GNUNET_TIME_absolute_get ();
763 LOG (GNUNET_ERROR_TYPE_DEBUG,
764 "Processing GET_CLOSEST for key `%s'\n",
765 GNUNET_h2s (key));
766 if (GNUNET_OK !=
767 GNUNET_SQ_bind (plugin->get_closest_stmt,
768 params))
769 {
770 LOG_SQLITE (plugin->dbh,
771 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
772 "sqlite3_bind_xxx");
773 GNUNET_SQ_reset (plugin->dbh,
774 plugin->get_closest_stmt);
775 return 0;
776 }
777 cnt = 0;
778 while (SQLITE_ROW ==
779 sqlite3_step (plugin->get_closest_stmt))
780 {
781 if (GNUNET_OK !=
782 GNUNET_SQ_extract_result (plugin->get_closest_stmt,
783 rs))
784 {
785 GNUNET_break (0);
786 break;
787 }
788 if (0 != path_size % sizeof(struct GNUNET_DHT_PathElement))
789 {
790 GNUNET_break (0);
791 path_size = 0;
792 path = NULL;
793 }
794 block.put_path_length
795 = path_size / sizeof(struct GNUNET_DHT_PathElement);
796 block.put_path = path;
797 block.data = data;
798 block.type = (enum GNUNET_BLOCK_Type) rtype32;
799 block.ro = (enum GNUNET_DHT_RouteOption) bro32;
800 cnt++;
801 LOG (GNUNET_ERROR_TYPE_DEBUG,
802 "Found %u-byte result at %s when processing GET_CLOSE\n",
803 (unsigned int) block.data_size,
804 GNUNET_h2s (&block.key));
805
806 if (GNUNET_OK !=
807 iter (iter_cls,
808 &block))
809 {
810 GNUNET_SQ_cleanup_result (rs);
811 break;
812 }
813 GNUNET_SQ_cleanup_result (rs);
814 }
815 GNUNET_SQ_reset (plugin->dbh,
816 plugin->get_closest_stmt);
817 return cnt;
818}
819
820
821/**
822 * Entry point for the plugin.
823 *
824 * @param cls closure (the `struct GNUNET_DATACACHE_PluginEnvironment`)
825 * @return the plugin's closure (our `struct Plugin`)
826 */
827void *
828libgnunet_plugin_datacache_sqlite_init (void *cls)
829{
830 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
831 struct GNUNET_DATACACHE_PluginFunctions *api;
832 struct Plugin *plugin;
833 char *fn;
834 char *fn_utf8;
835 sqlite3 *dbh;
836 char *emsg;
837
838 if (GNUNET_YES ==
839 GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
840 "datacache-sqlite",
841 "IN_MEMORY"))
842 {
843 if (SQLITE_OK !=
844 sqlite3_open (":memory:",
845 &dbh))
846 return NULL;
847 fn_utf8 = NULL;
848 }
849 else
850 {
851 fn = GNUNET_DISK_mktemp ("gnunet-datacache");
852 if (NULL == fn)
853 {
854 GNUNET_break (0);
855 return NULL;
856 }
857 /* fn should be UTF-8-encoded. If it isn't, it's a bug. */
858 fn_utf8 = GNUNET_strdup (fn);
859 if (SQLITE_OK !=
860 sqlite3_open (fn_utf8,
861 &dbh))
862 {
863 GNUNET_free (fn);
864 GNUNET_free (fn_utf8);
865 return NULL;
866 }
867 GNUNET_free (fn);
868 }
869
870 SQLITE3_EXEC (dbh, "PRAGMA temp_store=MEMORY");
871 SQLITE3_EXEC (dbh, "PRAGMA locking_mode=EXCLUSIVE");
872 SQLITE3_EXEC (dbh, "PRAGMA journal_mode=OFF");
873 SQLITE3_EXEC (dbh, "PRAGMA synchronous=OFF");
874 SQLITE3_EXEC (dbh, "PRAGMA page_size=4092");
875 if (GNUNET_YES ==
876 GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
877 "datacache-sqlite",
878 "IN_MEMORY"))
879 SQLITE3_EXEC (dbh, "PRAGMA sqlite_temp_store=3");
880
881 SQLITE3_EXEC (dbh,
882 "CREATE TABLE ds180 ("
883 " type INTEGER NOT NULL DEFAULT 0,"
884 " ro INTEGER NOT NULL DEFAULT 0,"
885 " expire INTEGER NOT NULL,"
886 " key BLOB NOT NULL DEFAULT '',"
887 " prox INTEGER NOT NULL,"
888 " value BLOB NOT NULL,"
889 " trunc BLOB NOT NULL,"
890 " path BLOB DEFAULT '')");
891 SQLITE3_EXEC (dbh,
892 "CREATE INDEX idx_hashidx"
893 " ON ds180 (key,type,expire)");
894 SQLITE3_EXEC (dbh,
895 "CREATE INDEX idx_prox_expire"
896 " ON ds180 (prox,expire)");
897 SQLITE3_EXEC (dbh,
898 "CREATE INDEX idx_expire_only"
899 " ON ds180 (expire)");
900 plugin = GNUNET_new (struct Plugin);
901 plugin->env = env;
902 plugin->dbh = dbh;
903 plugin->fn = fn_utf8;
904
905 if ((SQLITE_OK !=
906 sq_prepare (plugin->dbh,
907 "INSERT INTO ds180"
908 " (type, ro, expire, key, prox, value, path, trunc)"
909 " VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
910 &plugin->insert_stmt)) ||
911 (SQLITE_OK !=
912 sq_prepare (plugin->dbh,
913 "SELECT COUNT(*) FROM ds180 "
914 "WHERE key=?"
915 " AND type=?"
916 " AND expire >= ?",
917 &plugin->get_count_stmt)) ||
918 (SQLITE_OK !=
919 sq_prepare (plugin->dbh,
920 "SELECT COUNT(*) FROM ds180 "
921 "WHERE key=? AND expire >= ?",
922 &plugin->get_count_any_stmt)) ||
923 (SQLITE_OK !=
924 sq_prepare (plugin->dbh,
925 "SELECT value,expire,path,trunc,ro"
926 " FROM ds180"
927 " WHERE key=?"
928 " AND type=?"
929 " AND expire >= ?"
930 " LIMIT 1 OFFSET ?",
931 &plugin->get_stmt)) ||
932 (SQLITE_OK !=
933 sq_prepare (plugin->dbh,
934 "SELECT value,expire,path,trunc,type,ro"
935 " FROM ds180"
936 " WHERE key=?"
937 " AND expire >= ?"
938 " LIMIT 1 OFFSET ?",
939 &plugin->get_any_stmt)) ||
940 (SQLITE_OK !=
941 sq_prepare (plugin->dbh,
942 "SELECT _ROWID_,key,value FROM ds180"
943 " WHERE expire < ?1"
944 " ORDER BY expire ASC LIMIT 1",
945 &plugin->del_expired_stmt)) ||
946 (SQLITE_OK !=
947 sq_prepare (plugin->dbh,
948 "SELECT _ROWID_,key,value FROM ds180"
949 " ORDER BY prox ASC, expire ASC LIMIT 1",
950 &plugin->del_select_stmt)) ||
951 (SQLITE_OK !=
952 sq_prepare (plugin->dbh,
953 "DELETE FROM ds180 WHERE _ROWID_=?",
954 &plugin->del_stmt)) ||
955 (SQLITE_OK !=
956 sq_prepare (plugin->dbh,
957 "SELECT * FROM ("
958 " SELECT value,expire,path,trunc,type,ro,key"
959 " FROM ds180 "
960 " WHERE key>=?1 "
961 " AND expire >= ?2"
962 " AND ( (type=?3) or (0 == ?3) )"
963 " ORDER BY KEY ASC LIMIT ?4)"
964 "UNION "
965 "SELECT * FROM ("
966 " SELECT value,expire,path,trunc,type,ro,key"
967 " FROM ds180 "
968 " WHERE key<=?1 "
969 " AND expire >= ?2"
970 " AND ( (type=?3) or (0 == ?3) )"
971 " ORDER BY KEY DESC LIMIT ?4)",
972 &plugin->get_closest_stmt)))
973 {
974 LOG_SQLITE (plugin->dbh,
975 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
976 "sq_prepare");
977 GNUNET_break (SQLITE_OK ==
978 sqlite3_close (plugin->dbh));
979 GNUNET_free (plugin);
980 return NULL;
981 }
982
983 api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
984 api->cls = plugin;
985 api->get = &sqlite_plugin_get;
986 api->put = &sqlite_plugin_put;
987 api->del = &sqlite_plugin_del;
988 api->get_closest = &sqlite_plugin_get_closest;
989 LOG (GNUNET_ERROR_TYPE_INFO,
990 "Sqlite datacache running\n");
991 return api;
992}
993
994
995/**
996 * Exit point from the plugin.
997 *
998 * @param cls closure (our `struct Plugin`)
999 * @return NULL
1000 */
1001void *
1002libgnunet_plugin_datacache_sqlite_done (void *cls)
1003{
1004 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
1005 struct Plugin *plugin = api->cls;
1006 int result;
1007
1008#if SQLITE_VERSION_NUMBER >= 3007000
1009 sqlite3_stmt *stmt;
1010#endif
1011
1012#if ! WINDOWS || defined(__CYGWIN__)
1013 if ( (NULL != plugin->fn) &&
1014 (0 != unlink (plugin->fn)) )
1015 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1016 "unlink",
1017 plugin->fn);
1018 GNUNET_free (plugin->fn);
1019#endif
1020 sqlite3_finalize (plugin->insert_stmt);
1021 sqlite3_finalize (plugin->get_count_stmt);
1022 sqlite3_finalize (plugin->get_count_any_stmt);
1023 sqlite3_finalize (plugin->get_stmt);
1024 sqlite3_finalize (plugin->get_any_stmt);
1025 sqlite3_finalize (plugin->del_select_stmt);
1026 sqlite3_finalize (plugin->del_expired_stmt);
1027 sqlite3_finalize (plugin->del_stmt);
1028 sqlite3_finalize (plugin->get_closest_stmt);
1029 result = sqlite3_close (plugin->dbh);
1030#if SQLITE_VERSION_NUMBER >= 3007000
1031 if (SQLITE_BUSY == result)
1032 {
1033 LOG (GNUNET_ERROR_TYPE_WARNING,
1034 _ (
1035 "Tried to close sqlite without finalizing all prepared statements.\n"));
1036 stmt = sqlite3_next_stmt (plugin->dbh,
1037 NULL);
1038 while (NULL != stmt)
1039 {
1040 result = sqlite3_finalize (stmt);
1041 if (result != SQLITE_OK)
1042 LOG (GNUNET_ERROR_TYPE_WARNING,
1043 "Failed to close statement %p: %d\n",
1044 stmt,
1045 result);
1046 stmt = sqlite3_next_stmt (plugin->dbh,
1047 NULL);
1048 }
1049 result = sqlite3_close (plugin->dbh);
1050 }
1051#endif
1052 if (SQLITE_OK != result)
1053 LOG_SQLITE (plugin->dbh,
1054 GNUNET_ERROR_TYPE_ERROR,
1055 "sqlite3_close");
1056
1057 GNUNET_free (plugin);
1058 GNUNET_free (api);
1059 return NULL;
1060}
1061
1062
1063/* end of plugin_datacache_sqlite.c */
diff --git a/src/plugin/datacache/plugin_datacache_template.c b/src/plugin/datacache/plugin_datacache_template.c
new file mode 100644
index 000000000..1bd712d39
--- /dev/null
+++ b/src/plugin/datacache/plugin_datacache_template.c
@@ -0,0 +1,172 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2006, 2009, 2015, 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 datacache/plugin_datacache_template.c
23 * @brief template for an implementation of a database backend for the datacache
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_datacache_plugin.h"
29
30
31/**
32 * Context for all functions in this plugin.
33 */
34struct Plugin
35{
36 /**
37 * Our execution environment.
38 */
39 struct GNUNET_DATACACHE_PluginEnvironment *env;
40};
41
42
43/**
44 * Store an item in the datastore.
45 *
46 * @param cls closure (our `struct Plugin`)
47 * @param xor_distance distance of @a key to our PID
48 * @param block data to store
49 * @return 0 if duplicate, -1 on error, number of bytes used otherwise
50 */
51static ssize_t
52template_plugin_put (void *cls,
53 uint32_t xor_distance,
54 const struct GNUNET_DATACACHE_Block *block)
55{
56 GNUNET_break (0);
57 return -1;
58}
59
60
61/**
62 * Iterate over the results for a particular key
63 * in the datastore.
64 *
65 * @param cls closure (our `struct Plugin`)
66 * @param key
67 * @param type entries of which type are relevant?
68 * @param iter maybe NULL (to just count)
69 * @param iter_cls closure for @a iter
70 * @return the number of results found
71 */
72static unsigned int
73template_plugin_get (void *cls,
74 const struct GNUNET_HashCode *key,
75 enum GNUNET_BLOCK_Type type,
76 GNUNET_DATACACHE_Iterator iter,
77 void *iter_cls)
78{
79 GNUNET_break (0);
80 return 0;
81}
82
83
84/**
85 * Delete the entry with the lowest expiration value
86 * from the datacache right now.
87 *
88 * @param cls closure (our `struct Plugin`)
89 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
90 */
91static enum GNUNET_GenericReturnValue
92template_plugin_del (void *cls)
93{
94 GNUNET_break (0);
95 return GNUNET_SYSERR;
96}
97
98
99/**
100 * Iterate over the results that are "close" to a particular key in
101 * the datacache. "close" is defined as numerically larger than @a
102 * key (when interpreted as a circular address space), with small
103 * distance.
104 *
105 * @param cls closure (internal context for the plugin)
106 * @param key area of the keyspace to look into
107 * @param type desired block type for the replies
108 * @param num_results number of results that should be returned to @a iter
109 * @param iter maybe NULL (to just count)
110 * @param iter_cls closure for @a iter
111 * @return the number of results found
112 */
113static unsigned int
114template_plugin_get_closest (void *cls,
115 const struct GNUNET_HashCode *key,
116 enum GNUNET_BLOCK_Type type,
117 unsigned int num_results,
118 GNUNET_DATACACHE_Iterator iter,
119 void *iter_cls)
120{
121 GNUNET_break (0);
122 return 0;
123}
124
125
126/**
127 * Entry point for the plugin.
128 *
129 * @param cls closure (the `struct GNUNET_DATACACHE_PluginEnvironmnet`)
130 * @return the plugin's closure (our `struct Plugin`)
131 */
132void *
133libgnunet_plugin_datacache_template_init (void *cls)
134{
135 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
136 struct GNUNET_DATACACHE_PluginFunctions *api;
137 struct Plugin *plugin;
138
139 plugin = GNUNET_new (struct Plugin);
140 plugin->env = env;
141 api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
142 api->cls = plugin;
143 api->get = &template_plugin_get;
144 api->put = &template_plugin_put;
145 api->del = &template_plugin_del;
146 api->get_closest = &template_plugin_get_closest;
147 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
148 "template",
149 "Template datacache running\n");
150 return api;
151}
152
153
154/**
155 * Exit point from the plugin.
156 *
157 * @param cls closure (our `struct Plugin`)
158 * @return NULL
159 */
160void *
161libgnunet_plugin_datacache_template_done (void *cls)
162{
163 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
164 struct Plugin *plugin = api->cls;
165
166 GNUNET_free (plugin);
167 GNUNET_free (api);
168 return NULL;
169}
170
171
172/* end of plugin_datacache_template.c */
diff --git a/src/plugin/datastore/Makefile.am b/src/plugin/datastore/Makefile.am
new file mode 100644
index 000000000..1f4ab59c8
--- /dev/null
+++ b/src/plugin/datastore/Makefile.am
@@ -0,0 +1,150 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4plugindir = $(libdir)/gnunet
5
6pkgcfgdir= $(pkgdatadir)/config.d/
7
8libexecdir= $(pkglibdir)/libexec/
9
10sqldir = $(prefix)/share/gnunet/sql/
11
12sql_DATA = \
13 datastore-0001.sql \
14 datastore-drop.sql
15
16if USE_COVERAGE
17 AM_CFLAGS = --coverage -O0
18 XLIBS = -lgcov
19endif
20
21
22if HAVE_SQLITE
23 SQLITE_PLUGIN = libgnunet_plugin_datastore_sqlite.la
24if HAVE_BENCHMARKS
25 SQLITE_BENCHMARKS = \
26 perf_plugin_datastore_sqlite
27endif
28 SQLITE_TESTS = \
29 test_plugin_datastore_sqlite \
30 $(SQLITE_BENCHMARKS)
31endif
32if HAVE_POSTGRESQL
33 POSTGRES_PLUGIN = libgnunet_plugin_datastore_postgres.la
34if HAVE_BENCHMARKS
35 POSTGRES_BENCHMARKS = \
36 perf_plugin_datastore_postgres
37endif
38 POSTGRES_TESTS = \
39 test_plugin_datastore_postgres \
40 $(POSTGRES_BENCHMARKS)
41endif
42
43plugin_LTLIBRARIES = \
44 $(SQLITE_PLUGIN) \
45 $(POSTGRES_PLUGIN) \
46 libgnunet_plugin_datastore_heap.la
47
48# Real plugins should of course go into
49# plugin_LTLIBRARIES
50noinst_LTLIBRARIES = \
51 libgnunet_plugin_datastore_template.la
52
53
54libgnunet_plugin_datastore_sqlite_la_SOURCES = \
55 plugin_datastore_sqlite.c
56libgnunet_plugin_datastore_sqlite_la_LIBADD = \
57 $(top_builddir)/src/lib/sq/libgnunetsq.la \
58 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lsqlite3 \
59 $(LTLIBINTL)
60libgnunet_plugin_datastore_sqlite_la_LDFLAGS = \
61 $(GN_PLUGIN_LDFLAGS)
62
63
64libgnunet_plugin_datastore_heap_la_SOURCES = \
65 plugin_datastore_heap.c
66libgnunet_plugin_datastore_heap_la_LIBADD = \
67 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
68 $(LTLIBINTL)
69libgnunet_plugin_datastore_heap_la_LDFLAGS = \
70 $(GN_PLUGIN_LDFLAGS)
71
72
73libgnunet_plugin_datastore_postgres_la_SOURCES = \
74 plugin_datastore_postgres.c
75libgnunet_plugin_datastore_postgres_la_LIBADD = \
76 $(top_builddir)/src/lib/pq/libgnunetpq.la \
77 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lpq
78libgnunet_plugin_datastore_postgres_la_LDFLAGS = \
79 $(GN_PLUGIN_LDFLAGS) $(POSTGRESQL_LDFLAGS)
80libgnunet_plugin_datastore_postgres_la_CPPFLAGS = \
81 $(POSTGRESQL_CPPFLAGS) $(AM_CPPFLAGS)
82
83
84libgnunet_plugin_datastore_template_la_SOURCES = \
85 plugin_datastore_template.c
86libgnunet_plugin_datastore_template_la_LIBADD = \
87 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
88 $(LTLIBINTL)
89libgnunet_plugin_datastore_template_la_LDFLAGS = \
90 $(GN_PLUGIN_LDFLAGS)
91
92check_PROGRAMS = \
93 perf_plugin_datastore_heap \
94 test_plugin_datastore_heap \
95 $(SQLITE_TESTS) \
96 $(POSTGRES_TESTS)
97
98if ENABLE_TEST_RUN
99AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
100TESTS = $(check_PROGRAMS)
101endif
102
103perf_plugin_datastore_heap_SOURCES = \
104 perf_plugin_datastore.c
105perf_plugin_datastore_heap_LDADD = \
106 $(top_builddir)/src/service/testing/libgnunettesting.la \
107 $(top_builddir)/src/lib/util/libgnunetutil.la
108
109test_plugin_datastore_heap_SOURCES = \
110 test_plugin_datastore.c
111test_plugin_datastore_heap_LDADD = \
112 $(top_builddir)/src/service/testing/libgnunettesting.la \
113 $(top_builddir)/src/lib/util/libgnunetutil.la
114
115
116perf_plugin_datastore_sqlite_SOURCES = \
117 perf_plugin_datastore.c
118perf_plugin_datastore_sqlite_LDADD = \
119 $(top_builddir)/src/service/testing/libgnunettesting.la \
120 $(top_builddir)/src/lib/util/libgnunetutil.la
121
122test_plugin_datastore_sqlite_SOURCES = \
123 test_plugin_datastore.c
124test_plugin_datastore_sqlite_LDADD = \
125 $(top_builddir)/src/service/testing/libgnunettesting.la \
126 $(top_builddir)/src/lib/util/libgnunetutil.la
127
128
129test_plugin_datastore_postgres_SOURCES = \
130 test_plugin_datastore.c
131test_plugin_datastore_postgres_LDADD = \
132 $(top_builddir)/src/service/testing/libgnunettesting.la \
133 $(top_builddir)/src/lib/util/libgnunetutil.la
134
135perf_plugin_datastore_postgres_SOURCES = \
136 perf_plugin_datastore.c
137perf_plugin_datastore_postgres_LDADD = \
138 $(top_builddir)/src/service/testing/libgnunettesting.la \
139 $(top_builddir)/src/lib/util/libgnunetutil.la
140
141
142EXTRA_DIST = \
143 test_defaults.conf \
144 perf_plugin_datastore_data_sqlite.conf \
145 test_plugin_datastore_data_sqlite.conf \
146 perf_plugin_datastore_data_heap.conf \
147 test_plugin_datastore_data_heap.conf \
148 perf_plugin_datastore_data_postgres.conf \
149 test_plugin_datastore_data_postgres.conf \
150 $(sql_DATA)
diff --git a/src/plugin/datastore/datastore-0001.sql b/src/plugin/datastore/datastore-0001.sql
new file mode 100644
index 000000000..0d4758be2
--- /dev/null
+++ b/src/plugin/datastore/datastore-0001.sql
@@ -0,0 +1,49 @@
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('datastore-0001', NULL, NULL);
22
23-------------------- Schema ----------------------------
24
25CREATE SCHEMA datastore;
26COMMENT ON SCHEMA datastore IS 'gnunet-datastore data';
27
28SET search_path TO datastore;
29
30CREATE TABLE IF NOT EXISTS gn090 (
31 repl INTEGER NOT NULL DEFAULT 0,
32 type INTEGER NOT NULL DEFAULT 0,
33 prio INTEGER NOT NULL DEFAULT 0,
34 anonLevel INTEGER NOT NULL DEFAULT 0,
35 expire BIGINT NOT NULL DEFAULT 0,
36 rvalue BIGINT NOT NULL DEFAULT 0,
37 hash BYTEA NOT NULL DEFAULT '',
38 vhash BYTEA NOT NULL DEFAULT '',
39 value BYTEA NOT NULL DEFAULT '',
40 oid BIGINT GENERATED BY DEFAULT AS IDENTITY);
41
42CREATE INDEX IF NOT EXISTS oid_hash ON gn090 (oid);
43CREATE INDEX IF NOT EXISTS idx_hash ON gn090 (hash);
44CREATE INDEX IF NOT EXISTS idx_prio_anon ON gn090 (prio,anonLevel);
45CREATE INDEX IF NOT EXISTS idx_prio_hash_anon ON gn090 (prio,hash,anonLevel);
46CREATE INDEX IF NOT EXISTS idx_repl_rvalue ON gn090 (repl,rvalue);
47CREATE INDEX IF NOT EXISTS idx_expire_hash ON gn090 (expire,hash);
48
49COMMIT;
diff --git a/src/plugin/datastore/datastore-drop.sql b/src/plugin/datastore/datastore-drop.sql
new file mode 100644
index 000000000..67fee303d
--- /dev/null
+++ b/src/plugin/datastore/datastore-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('datastore-0001');
22
23DROP SCHEMA datastore CASCADE;
24
25COMMIT;
diff --git a/src/plugin/datastore/meson.build b/src/plugin/datastore/meson.build
new file mode 100644
index 000000000..6769cb78b
--- /dev/null
+++ b/src/plugin/datastore/meson.build
@@ -0,0 +1,75 @@
1install_data ('datastore-0001.sql',
2 'datastore-drop.sql',
3 install_dir: get_option('datadir')/'gnunet'/'sql')
4
5shared_module('gnunet_plugin_datastore_sqlite',
6 ['plugin_datastore_sqlite.c'],
7 dependencies: [libgnunetutil_dep,
8 sqlite_dep,
9 libgnunetsq_dep],
10 include_directories: [incdir, configuration_inc],
11 install: true,
12 install_dir: get_option('libdir')/'gnunet')
13
14shared_module('gnunet_plugin_datastore_heap',
15 ['plugin_datastore_heap.c'],
16 dependencies: [libgnunetutil_dep],
17 include_directories: [incdir, configuration_inc],
18 install: true,
19 install_dir: get_option('libdir')/'gnunet')
20
21if pq_dep.found()
22 shared_module('gnunet_plugin_datastore_postgres',
23 ['plugin_datastore_postgres.c'],
24 dependencies: [libgnunetutil_dep,
25 pq_dep,
26 libgnunetpq_dep],
27 include_directories: [incdir, configuration_inc],
28 install: true,
29 install_dir: get_option('libdir')/'gnunet')
30endif
31
32testds_plugin_sqlite = executable ('test_plugin_datastore_sqlite',
33 ['test_plugin_datastore.c'],
34 dependencies: [
35 libgnunetutil_dep,
36 ],
37 include_directories: [incdir, configuration_inc],
38 install: false)
39
40testds_plugin_heap = executable ('test_plugin_datastore_heap',
41 ['test_plugin_datastore.c'],
42 dependencies: [
43 libgnunetutil_dep,
44 ],
45 include_directories: [incdir, configuration_inc],
46 install: false)
47
48testds_plugin_pq = executable ('test_plugin_datastore_postgres',
49 ['test_plugin_datastore.c'],
50 dependencies: [
51 libgnunetutil_dep,
52 ],
53 include_directories: [incdir, configuration_inc],
54 install: false)
55
56configure_file(input : 'test_defaults.conf',
57 output : 'test_defaults.conf',
58 copy: true)
59configure_file(input : 'test_plugin_datastore_data_sqlite.conf',
60 output : 'test_plugin_datastore_data_sqlite.conf',
61 copy: true)
62configure_file(input : 'test_plugin_datastore_data_heap.conf',
63 output : 'test_plugin_datastore_data_heap.conf',
64 copy: true)
65configure_file(input : 'test_plugin_datastore_data_postgres.conf',
66 output : 'test_plugin_datastore_data_postgres.conf',
67 copy: true)
68
69test('test_plugin_datastore_sqlite', testds_plugin_sqlite,
70 suite: 'datastore', workdir: meson.current_build_dir())
71test('test_plugin_datastore_heap', testds_plugin_heap,
72 suite: 'datastore', workdir: meson.current_build_dir())
73test('test_plugin_datastore_postgres', testds_plugin_pq,
74 suite: 'datastore', workdir: meson.current_build_dir())
75
diff --git a/src/plugin/datastore/perf_plugin_datastore.c b/src/plugin/datastore/perf_plugin_datastore.c
new file mode 100644
index 000000000..8e63b08e6
--- /dev/null
+++ b/src/plugin/datastore/perf_plugin_datastore.c
@@ -0,0 +1,573 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006, 2007, 2009, 2011 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 perf_plugin_datastore.c
22 * @brief Profile database plugin directly, focusing on iterators.
23 * @author Christian Grothoff
24 */
25
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_protocols.h"
29#include "gnunet_datastore_plugin.h"
30#include "gnunet_testing_lib.h"
31#include <gauger.h>
32
33/**
34 * Target datastore size (in bytes). Realistic sizes are
35 * more like 16 GB (not the default of 16 MB); however,
36 * those take too long to run them in the usual "make check"
37 * sequence. Hence the value used for shipping is tiny.
38 */
39#define MAX_SIZE 1024LL * 1024 * 16 * 1
40
41#define ITERATIONS 2
42
43/**
44 * Number of put operations equivalent to 1/10th of MAX_SIZE
45 */
46#define PUT_10 (MAX_SIZE / 32 / 1024 / ITERATIONS)
47
48static char category[256];
49
50static unsigned int hits[PUT_10 / 8 + 1];
51
52static unsigned long long stored_bytes;
53
54static unsigned long long stored_entries;
55
56static unsigned long long stored_ops;
57
58static const char *plugin_name;
59
60static int ok;
61
62enum RunPhase
63{
64 RP_ERROR = 0,
65 RP_PUT,
66 RP_REP_GET,
67 RP_ZA_GET,
68 RP_EXP_GET,
69 RP_DONE
70};
71
72
73struct CpsRunContext
74{
75 unsigned int i;
76 struct GNUNET_TIME_Absolute start;
77 struct GNUNET_TIME_Absolute end;
78 const struct GNUNET_CONFIGURATION_Handle *cfg;
79 struct GNUNET_DATASTORE_PluginFunctions *api;
80 enum RunPhase phase;
81 unsigned int cnt;
82 unsigned int iter;
83 uint64_t offset;
84};
85
86
87/**
88 * Function called by plugins to notify us about a
89 * change in their disk utilization.
90 *
91 * @param cls closure (NULL)
92 * @param delta change in disk utilization,
93 * 0 for "reset to empty"
94 */
95static void
96disk_utilization_change_cb (void *cls, int delta)
97{
98}
99
100
101static void
102test (void *cls);
103
104
105/**
106 * Put continuation.
107 *
108 * @param cls closure
109 * @param key key for the item stored
110 * @param size size of the item stored
111 * @param status #GNUNET_OK or #GNUNET_SYSERROR
112 * @param msg error message on error
113 */
114static void
115put_continuation (void *cls,
116 const struct GNUNET_HashCode *key,
117 uint32_t size,
118 int status,
119 const char *msg)
120{
121 struct CpsRunContext *crc = cls;
122
123 if (GNUNET_OK != status)
124 {
125 fprintf (stderr, "ERROR: `%s'\n", msg);
126 }
127 else
128 {
129 stored_bytes += size;
130 stored_ops++;
131 stored_entries++;
132 }
133 GNUNET_SCHEDULER_add_now (&test, crc);
134}
135
136
137static void
138do_put (struct CpsRunContext *crc)
139{
140 char value[65536];
141 size_t size;
142 static struct GNUNET_HashCode key;
143 static int i;
144 unsigned int prio;
145
146 if (0 == i)
147 crc->start = GNUNET_TIME_absolute_get ();
148 if (PUT_10 == i)
149 {
150 i = 0;
151 crc->end = GNUNET_TIME_absolute_get ();
152 {
153 printf ("%s took %s for %llu items\n", "Storing an item",
154 GNUNET_STRINGS_relative_time_to_string (
155 GNUNET_TIME_absolute_get_difference (crc->start,
156 crc
157 ->end),
158 GNUNET_YES),
159 PUT_10);
160 if (PUT_10 > 0)
161 GAUGER (category, "Storing an item",
162 (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL
163 / PUT_10,
164 "ms/item");
165 }
166 crc->i++;
167 crc->start = GNUNET_TIME_absolute_get ();
168 crc->phase++;
169 GNUNET_SCHEDULER_add_now (&test, crc);
170 return;
171 }
172 /* most content is 32k */
173 size = 32 * 1024;
174 if (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16) == 0) /* but some of it is less! */
175 size = 8 + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 32 * 1024);
176 size = size - (size & 7); /* always multiple of 8 */
177
178 /* generate random key */
179 key.bits[0] = (unsigned int) GNUNET_TIME_absolute_get ().abs_value_us;
180 GNUNET_CRYPTO_hash (&key, sizeof(struct GNUNET_HashCode), &key);
181 memset (value, i, size);
182 if (i > 255)
183 memset (value, i - 255, size / 2);
184 value[0] = crc->i;
185 GNUNET_memcpy (&value[4], &i, sizeof(i));
186 prio = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100);
187 crc->api->put (crc->api->cls,
188 &key,
189 false /* absent */,
190 size,
191 value,
192 1 + i % 4 /* type */,
193 prio,
194 i % 4 /* anonymity */,
195 0 /* replication */,
196 GNUNET_TIME_relative_to_absolute
197 (GNUNET_TIME_relative_multiply
198 (GNUNET_TIME_UNIT_MILLISECONDS,
199 60 * 60 * 60 * 1000
200 + GNUNET_CRYPTO_random_u32
201 (GNUNET_CRYPTO_QUALITY_WEAK, 1000))),
202 put_continuation,
203 crc);
204 i++;
205}
206
207
208static int
209iterate_zeros (void *cls,
210 const struct GNUNET_HashCode *key,
211 uint32_t size,
212 const void *data,
213 enum GNUNET_BLOCK_Type type,
214 uint32_t priority,
215 uint32_t anonymity,
216 uint32_t replication,
217 struct GNUNET_TIME_Absolute expiration,
218 uint64_t uid)
219{
220 struct CpsRunContext *crc = cls;
221 int i;
222 const char *cdata = data;
223
224 GNUNET_assert (key != NULL);
225 GNUNET_assert (size >= 8);
226 GNUNET_memcpy (&i, &cdata[4], sizeof(i));
227 hits[i / 8] |= (1 << (i % 8));
228
229 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
230 "Found result %d type=%u, priority=%u, size=%u, expire=%s\n",
231 i,
232 type, priority, size,
233 GNUNET_STRINGS_absolute_time_to_string (expiration));
234 crc->cnt++;
235 if (crc->cnt == PUT_10 / 4 - 1)
236 {
237 unsigned int bc;
238
239 bc = 0;
240 for (i = 0; i < PUT_10; i++)
241 if (0 != (hits[i / 8] & (1 << (i % 8))))
242 bc++;
243
244 crc->end = GNUNET_TIME_absolute_get ();
245 printf ("%s took %s yielding %u/%u items\n",
246 "Select random zero-anonymity item",
247 GNUNET_STRINGS_relative_time_to_string (
248 GNUNET_TIME_absolute_get_difference (crc->start,
249 crc
250 ->end),
251 GNUNET_YES),
252 bc, crc->cnt);
253 if (crc->cnt > 0)
254 GAUGER (category, "Select random zero-anonymity item",
255 (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL
256 / crc->cnt,
257 "ms/item");
258 memset (hits, 0, sizeof(hits));
259 crc->phase++;
260 crc->cnt = 0;
261 crc->start = GNUNET_TIME_absolute_get ();
262 }
263 GNUNET_SCHEDULER_add_now (&test, crc);
264 return GNUNET_OK;
265}
266
267
268static int
269expiration_get (void *cls,
270 const struct GNUNET_HashCode *key,
271 uint32_t size,
272 const void *data,
273 enum GNUNET_BLOCK_Type type,
274 uint32_t priority,
275 uint32_t anonymity,
276 uint32_t replication,
277 struct GNUNET_TIME_Absolute expiration,
278 uint64_t uid)
279{
280 struct CpsRunContext *crc = cls;
281 int i;
282 const char *cdata = data;
283
284 GNUNET_assert (size >= 8);
285 GNUNET_memcpy (&i, &cdata[4], sizeof(i));
286 hits[i / 8] |= (1 << (i % 8));
287 crc->cnt++;
288 if (PUT_10 <= crc->cnt)
289 {
290 unsigned int bc;
291
292 bc = 0;
293 for (i = 0; i < PUT_10; i++)
294 if (0 != (hits[i / 8] & (1 << (i % 8))))
295 bc++;
296
297 crc->end = GNUNET_TIME_absolute_get ();
298 printf ("%s took %s yielding %u/%u items\n",
299 "Selecting and deleting by expiration",
300 GNUNET_STRINGS_relative_time_to_string (
301 GNUNET_TIME_absolute_get_difference (crc->start,
302 crc
303 ->end),
304 GNUNET_YES),
305 bc, (unsigned int) PUT_10);
306 if (crc->cnt > 0)
307 GAUGER (category, "Selecting and deleting by expiration",
308 (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL
309 / crc->cnt,
310 "ms/item");
311 memset (hits, 0, sizeof(hits));
312 if (++crc->iter == ITERATIONS)
313 crc->phase++;
314 else
315 crc->phase = RP_PUT;
316 crc->cnt = 0;
317 crc->start = GNUNET_TIME_absolute_get ();
318 }
319 GNUNET_SCHEDULER_add_now (&test, crc);
320 return GNUNET_NO;
321}
322
323
324static int
325replication_get (void *cls,
326 const struct GNUNET_HashCode *key,
327 uint32_t size,
328 const void *data,
329 enum GNUNET_BLOCK_Type type,
330 uint32_t priority,
331 uint32_t anonymity,
332 uint32_t replication,
333 struct GNUNET_TIME_Absolute expiration,
334 uint64_t uid)
335{
336 struct CpsRunContext *crc = cls;
337 int i;
338 const char *cdata = data;
339
340 GNUNET_assert (NULL != key);
341 GNUNET_assert (size >= 8);
342 GNUNET_memcpy (&i, &cdata[4], sizeof(i));
343 hits[i / 8] |= (1 << (i % 8));
344 crc->cnt++;
345 if (PUT_10 <= crc->cnt)
346 {
347 unsigned int bc;
348
349 bc = 0;
350 for (i = 0; i < PUT_10; i++)
351 if (0 != (hits[i / 8] & (1 << (i % 8))))
352 bc++;
353
354 crc->end = GNUNET_TIME_absolute_get ();
355 printf ("%s took %s yielding %u/%u items\n",
356 "Selecting random item for replication",
357 GNUNET_STRINGS_relative_time_to_string (
358 GNUNET_TIME_absolute_get_difference (crc->start,
359 crc
360 ->end),
361 GNUNET_YES),
362 bc, (unsigned int) PUT_10);
363 if (crc->cnt > 0)
364 GAUGER (category, "Selecting random item for replication",
365 (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL
366 / crc->cnt,
367 "ms/item");
368 memset (hits, 0, sizeof(hits));
369 crc->phase++;
370 crc->offset = 0;
371 crc->cnt = 0;
372 crc->start = GNUNET_TIME_absolute_get ();
373 }
374
375 GNUNET_SCHEDULER_add_now (&test, crc);
376 return GNUNET_OK;
377}
378
379
380/**
381 * Function called when the service shuts
382 * down. Unloads our datastore plugin.
383 *
384 * @param api api to unload
385 * @param cfg configuration to use
386 */
387static void
388unload_plugin (struct GNUNET_DATASTORE_PluginFunctions *api,
389 const struct GNUNET_CONFIGURATION_Handle *cfg)
390{
391 char *name;
392 char *libname;
393
394 if (GNUNET_OK !=
395 GNUNET_CONFIGURATION_get_value_string (cfg, "DATASTORE", "DATABASE",
396 &name))
397 {
398 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
399 _ ("No `%s' specified for `%s' in configuration!\n"),
400 "DATABASE",
401 "DATASTORE");
402 return;
403 }
404 GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
405 GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api));
406 GNUNET_free (libname);
407 GNUNET_free (name);
408}
409
410
411/**
412 * Last task run during shutdown. Disconnects us from
413 * the transport and core.
414 */
415static void
416cleaning_task (void *cls)
417{
418 struct CpsRunContext *crc = cls;
419
420 unload_plugin (crc->api, crc->cfg);
421 GNUNET_free (crc);
422}
423
424
425static void
426test (void *cls)
427{
428 struct CpsRunContext *crc = cls;
429
430 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
431 "In phase %d, iteration %u\n", crc->phase, crc->cnt);
432 switch (crc->phase)
433 {
434 case RP_ERROR:
435 GNUNET_break (0);
436 crc->api->drop (crc->api->cls);
437 ok = 1;
438 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
439 &cleaning_task, crc);
440 break;
441
442 case RP_PUT:
443 do_put (crc);
444 break;
445
446 case RP_REP_GET:
447 crc->api->get_replication (crc->api->cls, &replication_get, crc);
448 break;
449
450 case RP_ZA_GET:
451 crc->api->get_zero_anonymity (crc->api->cls, crc->offset++, 1,
452 &iterate_zeros, crc);
453 break;
454
455 case RP_EXP_GET:
456 crc->api->get_expiration (crc->api->cls, &expiration_get, crc);
457 break;
458
459 case RP_DONE:
460 crc->api->drop (crc->api->cls);
461 ok = 0;
462 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
463 &cleaning_task, crc);
464 break;
465 }
466}
467
468
469/**
470 * Load the datastore plugin.
471 */
472static struct GNUNET_DATASTORE_PluginFunctions *
473load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg)
474{
475 static struct GNUNET_DATASTORE_PluginEnvironment env;
476 struct GNUNET_DATASTORE_PluginFunctions *ret;
477 char *name;
478 char *libname;
479
480 if (GNUNET_OK !=
481 GNUNET_CONFIGURATION_get_value_string (cfg, "DATASTORE", "DATABASE",
482 &name))
483 {
484 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
485 _ ("No `%s' specified for `%s' in configuration!\n"),
486 "DATABASE",
487 "DATASTORE");
488 return NULL;
489 }
490 env.cfg = cfg;
491 env.duc = &disk_utilization_change_cb;
492 env.cls = NULL;
493 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Loading `%s' datastore plugin\n"),
494 name);
495 GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
496 if (NULL == (ret = GNUNET_PLUGIN_load (libname, &env)))
497 {
498 fprintf (stderr, "Failed to load plugin `%s'!\n", name);
499 GNUNET_free (name);
500 GNUNET_free (libname);
501 return NULL;
502 }
503 GNUNET_free (libname);
504 GNUNET_free (name);
505 return ret;
506}
507
508
509static void
510run (void *cls, char *const *args, const char *cfgfile,
511 const struct GNUNET_CONFIGURATION_Handle *c)
512{
513 struct GNUNET_DATASTORE_PluginFunctions *api;
514 struct CpsRunContext *crc;
515
516 if (NULL == c)
517 {
518 GNUNET_break (0);
519 return;
520 }
521 api = load_plugin (c);
522 if (api == NULL)
523 {
524 fprintf (stderr,
525 "%s",
526 "Could not initialize plugin, assuming database not configured. Test not run!\n");
527 return;
528 }
529 crc = GNUNET_new (struct CpsRunContext);
530 crc->api = api;
531 crc->cfg = c;
532 crc->phase = RP_PUT;
533 ok = 2;
534 GNUNET_SCHEDULER_add_now (&test, crc);
535}
536
537
538int
539main (int argc, char *argv[])
540{
541 char dir_name[PATH_MAX];
542 char cfg_name[PATH_MAX];
543 char *const xargv[] = {
544 "perf-plugin-datastore",
545 "-c",
546 cfg_name,
547 NULL
548 };
549 struct GNUNET_GETOPT_CommandLineOption options[] = {
550 GNUNET_GETOPT_OPTION_END
551 };
552
553 plugin_name = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]);
554 GNUNET_snprintf (dir_name, sizeof(dir_name), "/tmp/perf-gnunet-datastore-%s",
555 plugin_name);
556 GNUNET_DISK_directory_remove (dir_name);
557 GNUNET_log_setup ("perf-plugin-datastore",
558 "WARNING",
559 NULL);
560 GNUNET_snprintf (category, sizeof(category), "DATASTORE-%s", plugin_name);
561 GNUNET_snprintf (cfg_name, sizeof(cfg_name),
562 "perf_plugin_datastore_data_%s.conf", plugin_name);
563 GNUNET_PROGRAM_run ((sizeof(xargv) / sizeof(char *)) - 1, xargv,
564 "perf-plugin-datastore", "nohelp", options, &run, NULL);
565 if (ok != 0)
566 fprintf (stderr, "Missed some testcases: %u\n", ok);
567 GNUNET_DISK_directory_remove (dir_name);
568
569 return ok;
570}
571
572
573/* end of perf_plugin_datastore.c */
diff --git a/src/plugin/datastore/perf_plugin_datastore_data_heap.conf b/src/plugin/datastore/perf_plugin_datastore_data_heap.conf
new file mode 100644
index 000000000..873cf9606
--- /dev/null
+++ b/src/plugin/datastore/perf_plugin_datastore_data_heap.conf
@@ -0,0 +1,7 @@
1@INLINE@ test_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/perf-gnunet-datastore-heap/
4
5
6[datastore]
7DATABASE = heap
diff --git a/src/plugin/datastore/perf_plugin_datastore_data_postgres.conf b/src/plugin/datastore/perf_plugin_datastore_data_postgres.conf
new file mode 100644
index 000000000..7683887a8
--- /dev/null
+++ b/src/plugin/datastore/perf_plugin_datastore_data_postgres.conf
@@ -0,0 +1,10 @@
1@INLINE@ test_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/perf-gnunet-datastore-postgres/
4
5[datastore]
6DATABASE = postgres
7
8[datastore-postgres]
9CONFIG = dbname=gnunetcheck
10
diff --git a/src/plugin/datastore/perf_plugin_datastore_data_sqlite.conf b/src/plugin/datastore/perf_plugin_datastore_data_sqlite.conf
new file mode 100644
index 000000000..888e020a6
--- /dev/null
+++ b/src/plugin/datastore/perf_plugin_datastore_data_sqlite.conf
@@ -0,0 +1,4 @@
1@INLINE@ test_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/perf-gnunet-datastore-sqlite/
4
diff --git a/src/plugin/datastore/plugin_datastore_heap.c b/src/plugin/datastore/plugin_datastore_heap.c
new file mode 100644
index 000000000..a827a2763
--- /dev/null
+++ b/src/plugin/datastore/plugin_datastore_heap.c
@@ -0,0 +1,944 @@
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/**
22 * @file datastore/plugin_datastore_heap.c
23 * @brief heap-based datastore backend; usually we want the datastore
24 * to be persistent, and storing data in the heap is obviously
25 * NOT going to be persistent; still, this plugin is useful for
26 * testing/benchmarking --- but never for production!
27 * @author Christian Grothoff
28 */
29
30#include "platform.h"
31#include "gnunet_datastore_plugin.h"
32
33
34/**
35 * A value that we are storing.
36 */
37struct Value
38{
39 /**
40 * Key for the value.
41 */
42 struct GNUNET_HashCode key;
43
44 /**
45 * Pointer to the value's data (allocated at the end of this struct).
46 */
47 const void *data;
48
49 /**
50 * Entry for this value in the 'expire' heap.
51 */
52 struct GNUNET_CONTAINER_HeapNode *expire_heap;
53
54 /**
55 * Entry for this value in the 'replication' heap.
56 */
57 struct GNUNET_CONTAINER_HeapNode *replication_heap;
58
59 /**
60 * Expiration time for this value.
61 */
62 struct GNUNET_TIME_Absolute expiration;
63
64 /**
65 * Offset of this value in the array of the 'struct ZeroAnonByType';
66 * only used if anonymity is zero.
67 */
68 unsigned int zero_anon_offset;
69
70 /**
71 * Number of bytes in 'data'.
72 */
73 uint32_t size;
74
75 /**
76 * Priority of the value.
77 */
78 uint32_t priority;
79
80 /**
81 * Anonymity level for the value.
82 */
83 uint32_t anonymity;
84
85 /**
86 * Replication level for the value.
87 */
88 uint32_t replication;
89
90 /**
91 * Type of 'data'.
92 */
93 enum GNUNET_BLOCK_Type type;
94};
95
96
97/**
98 * We organize 0-anonymity values in arrays "by type".
99 */
100struct ZeroAnonByType
101{
102 /**
103 * We keep these in a DLL.
104 */
105 struct ZeroAnonByType *next;
106
107 /**
108 * We keep these in a DLL.
109 */
110 struct ZeroAnonByType *prev;
111
112 /**
113 * Array of 0-anonymity items of the given type.
114 */
115 struct Value **array;
116
117 /**
118 * Allocated size of the array.
119 */
120 unsigned int array_size;
121
122 /**
123 * First unused offset in 'array'.
124 */
125 unsigned int array_pos;
126
127 /**
128 * Type of all of the values in 'array'.
129 */
130 enum GNUNET_BLOCK_Type type;
131};
132
133
134/**
135 * Context for all functions in this plugin.
136 */
137struct Plugin
138{
139 /**
140 * Our execution environment.
141 */
142 struct GNUNET_DATASTORE_PluginEnvironment *env;
143
144 /**
145 * Mapping from keys to 'struct Value's.
146 */
147 struct GNUNET_CONTAINER_MultiHashMap *keyvalue;
148
149 /**
150 * Heap organized by minimum expiration time.
151 */
152 struct GNUNET_CONTAINER_Heap *by_expiration;
153
154 /**
155 * Heap organized by maximum replication value.
156 */
157 struct GNUNET_CONTAINER_Heap *by_replication;
158
159 /**
160 * Head of list of arrays containing zero-anonymity values by type.
161 */
162 struct ZeroAnonByType *zero_head;
163
164 /**
165 * Tail of list of arrays containing zero-anonymity values by type.
166 */
167 struct ZeroAnonByType *zero_tail;
168
169 /**
170 * Size of all values we're storing.
171 */
172 unsigned long long size;
173};
174
175
176/**
177 * Get an estimate of how much space the database is
178 * currently using.
179 *
180 * @param cls our "struct Plugin*"
181 * @return number of bytes used on disk
182 */
183static void
184heap_plugin_estimate_size (void *cls, unsigned long long *estimate)
185{
186 struct Plugin *plugin = cls;
187
188 if (NULL != estimate)
189 *estimate = plugin->size;
190}
191
192
193/**
194 * Closure for iterator for updating.
195 */
196struct UpdateContext
197{
198 /**
199 * Number of bytes in 'data'.
200 */
201 uint32_t size;
202
203 /**
204 * Pointer to the data.
205 */
206 const void *data;
207
208 /**
209 * Priority of the value.
210 */
211 uint32_t priority;
212
213 /**
214 * Replication level for the value.
215 */
216 uint32_t replication;
217
218 /**
219 * Expiration time for this value.
220 */
221 struct GNUNET_TIME_Absolute expiration;
222
223 /**
224 * True if the value was found and updated.
225 */
226 bool updated;
227};
228
229
230/**
231 * Update the matching value.
232 *
233 * @param cls the 'struct UpdateContext'
234 * @param key unused
235 * @param val the 'struct Value'
236 * @return GNUNET_YES (continue iteration), GNUNET_NO if value was found
237 */
238static int
239update_iterator (void *cls,
240 const struct GNUNET_HashCode *key,
241 void *val)
242{
243 struct UpdateContext *uc = cls;
244 struct Value *value = val;
245
246 if (value->size != uc->size)
247 return GNUNET_YES;
248 if (0 != memcmp (value->data, uc->data, uc->size))
249 return GNUNET_YES;
250 uc->expiration = GNUNET_TIME_absolute_max (value->expiration,
251 uc->expiration);
252 if (value->expiration.abs_value_us != uc->expiration.abs_value_us)
253 {
254 value->expiration = uc->expiration;
255 GNUNET_CONTAINER_heap_update_cost (value->expire_heap,
256 value->expiration.abs_value_us);
257 }
258 /* Saturating adds, don't overflow */
259 if (value->priority > UINT32_MAX - uc->priority)
260 value->priority = UINT32_MAX;
261 else
262 value->priority += uc->priority;
263 if (value->replication > UINT32_MAX - uc->replication)
264 value->replication = UINT32_MAX;
265 else
266 value->replication += uc->replication;
267 uc->updated = true;
268 return GNUNET_NO;
269}
270
271
272/**
273 * Store an item in the datastore.
274 *
275 * @param cls closure
276 * @param key key for the item
277 * @param absent true if the key was not found in the bloom filter
278 * @param size number of bytes in data
279 * @param data content stored
280 * @param type type of the content
281 * @param priority priority of the content
282 * @param anonymity anonymity-level for the content
283 * @param replication replication-level for the content
284 * @param expiration expiration time for the content
285 * @param cont continuation called with success or failure status
286 * @param cont_cls continuation closure
287 */
288static void
289heap_plugin_put (void *cls,
290 const struct GNUNET_HashCode *key,
291 bool absent,
292 uint32_t size,
293 const void *data,
294 enum GNUNET_BLOCK_Type type,
295 uint32_t priority,
296 uint32_t anonymity,
297 uint32_t replication,
298 struct GNUNET_TIME_Absolute expiration,
299 PluginPutCont cont,
300 void *cont_cls)
301{
302 struct Plugin *plugin = cls;
303 struct Value *value;
304
305 if (! absent)
306 {
307 struct UpdateContext uc;
308
309 uc.size = size;
310 uc.data = data;
311 uc.priority = priority;
312 uc.replication = replication;
313 uc.expiration = expiration;
314 uc.updated = false;
315 GNUNET_CONTAINER_multihashmap_get_multiple (plugin->keyvalue,
316 key,
317 &update_iterator,
318 &uc);
319 if (uc.updated)
320 {
321 cont (cont_cls, key, size, GNUNET_NO, NULL);
322 return;
323 }
324 }
325 value = GNUNET_malloc (sizeof(struct Value) + size);
326 value->key = *key;
327 value->data = &value[1];
328 value->expire_heap = GNUNET_CONTAINER_heap_insert (plugin->by_expiration,
329 value,
330 expiration.abs_value_us);
331 value->replication_heap = GNUNET_CONTAINER_heap_insert (
332 plugin->by_replication,
333 value,
334 replication);
335 value->expiration = expiration;
336 if (0 == anonymity)
337 {
338 struct ZeroAnonByType *zabt;
339
340 for (zabt = plugin->zero_head; NULL != zabt; zabt = zabt->next)
341 if (zabt->type == type)
342 break;
343 if (NULL == zabt)
344 {
345 zabt = GNUNET_new (struct ZeroAnonByType);
346 zabt->type = type;
347 GNUNET_CONTAINER_DLL_insert (plugin->zero_head,
348 plugin->zero_tail,
349 zabt);
350 }
351 if (zabt->array_size == zabt->array_pos)
352 {
353 GNUNET_array_grow (zabt->array,
354 zabt->array_size,
355 zabt->array_size * 2 + 4);
356 }
357 value->zero_anon_offset = zabt->array_pos;
358 zabt->array[zabt->array_pos++] = value;
359 }
360 value->size = size;
361 value->priority = priority;
362 value->anonymity = anonymity;
363 value->replication = replication;
364 value->type = type;
365 GNUNET_memcpy (&value[1], data, size);
366 GNUNET_CONTAINER_multihashmap_put (plugin->keyvalue,
367 &value->key,
368 value,
369 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
370 plugin->size += size;
371 cont (cont_cls, key, size, GNUNET_OK, NULL);
372}
373
374
375/**
376 * Delete the given value, removing it from the plugin's data
377 * structures.
378 *
379 * @param plugin the plugin
380 * @param value value to delete
381 */
382static void
383delete_value (struct Plugin *plugin,
384 struct Value *value)
385{
386 GNUNET_assert (GNUNET_YES ==
387 GNUNET_CONTAINER_multihashmap_remove (plugin->keyvalue,
388 &value->key,
389 value));
390 GNUNET_assert (value == GNUNET_CONTAINER_heap_remove_node (
391 value->expire_heap));
392 GNUNET_assert (value == GNUNET_CONTAINER_heap_remove_node (
393 value->replication_heap));
394 if (0 == value->anonymity)
395 {
396 struct ZeroAnonByType *zabt;
397
398 for (zabt = plugin->zero_head; NULL != zabt; zabt = zabt->next)
399 if (zabt->type == value->type)
400 break;
401 GNUNET_assert (NULL != zabt);
402 zabt->array[value->zero_anon_offset] = zabt->array[--zabt->array_pos];
403 zabt->array[value->zero_anon_offset]->zero_anon_offset =
404 value->zero_anon_offset;
405 if (0 == zabt->array_pos)
406 {
407 GNUNET_array_grow (zabt->array,
408 zabt->array_size,
409 0);
410 GNUNET_CONTAINER_DLL_remove (plugin->zero_head,
411 plugin->zero_tail,
412 zabt);
413 GNUNET_free (zabt);
414 }
415 }
416 plugin->size -= value->size;
417 GNUNET_free (value);
418}
419
420
421/**
422 * Closure for iterator called during 'get_key'.
423 */
424struct GetContext
425{
426 /**
427 * Lowest uid to consider.
428 */
429 uint64_t next_uid;
430
431 /**
432 * Value with lowest uid >= next_uid found so far.
433 */
434 struct Value *value;
435
436 /**
437 * Requested type.
438 */
439 enum GNUNET_BLOCK_Type type;
440
441 /**
442 * If true, return a random value
443 */
444 bool random;
445};
446
447
448/**
449 * Obtain the matching value with the lowest uid >= next_uid.
450 *
451 * @param cls the 'struct GetContext'
452 * @param key unused
453 * @param val the 'struct Value'
454 * @return GNUNET_YES (continue iteration), GNUNET_NO if result was found
455 */
456static int
457get_iterator (void *cls,
458 const struct GNUNET_HashCode *key,
459 void *val)
460{
461 struct GetContext *gc = cls;
462 struct Value *value = val;
463
464 if ((gc->type != GNUNET_BLOCK_TYPE_ANY) &&
465 (gc->type != value->type))
466 return GNUNET_OK;
467 if (gc->random)
468 {
469 gc->value = value;
470 return GNUNET_NO;
471 }
472 if ((uint64_t) (intptr_t) value < gc->next_uid)
473 return GNUNET_OK;
474 if ((NULL != gc->value) &&
475 (value > gc->value))
476 return GNUNET_OK;
477 gc->value = value;
478 return GNUNET_OK;
479}
480
481
482/**
483 * Get one of the results for a particular key in the datastore.
484 *
485 * @param cls closure
486 * @param next_uid return the result with lowest uid >= next_uid
487 * @param random if true, return a random result instead of using next_uid
488 * @param key maybe NULL (to match all entries)
489 * @param type entries of which type are relevant?
490 * Use 0 for any type.
491 * @param proc function to call on the matching value;
492 * will be called with NULL if nothing matches
493 * @param proc_cls closure for @a proc
494 */
495static void
496heap_plugin_get_key (void *cls,
497 uint64_t next_uid,
498 bool random,
499 const struct GNUNET_HashCode *key,
500 enum GNUNET_BLOCK_Type type,
501 PluginDatumProcessor proc,
502 void *proc_cls)
503{
504 struct Plugin *plugin = cls;
505 struct GetContext gc;
506
507 gc.value = NULL;
508 gc.next_uid = next_uid;
509 gc.random = random;
510 gc.type = type;
511 if (NULL == key)
512 {
513 GNUNET_CONTAINER_multihashmap_iterate (plugin->keyvalue,
514 &get_iterator,
515 &gc);
516 }
517 else
518 {
519 GNUNET_CONTAINER_multihashmap_get_multiple (plugin->keyvalue,
520 key,
521 &get_iterator,
522 &gc);
523 }
524 if (NULL == gc.value)
525 {
526 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
527 return;
528 }
529 GNUNET_assert (GNUNET_OK ==
530 proc (proc_cls,
531 &gc.value->key,
532 gc.value->size,
533 &gc.value[1],
534 gc.value->type,
535 gc.value->priority,
536 gc.value->anonymity,
537 gc.value->replication,
538 gc.value->expiration,
539 (uint64_t) (intptr_t) gc.value));
540}
541
542
543/**
544 * Get a random item for replication. Returns a single, not expired,
545 * random item from those with the highest replication counters. The
546 * item's replication counter is decremented by one IF it was positive
547 * before. Call 'proc' with all values ZERO or NULL if the datastore
548 * is empty.
549 *
550 * @param cls closure
551 * @param proc function to call the value (once only).
552 * @param proc_cls closure for proc
553 */
554static void
555heap_plugin_get_replication (void *cls,
556 PluginDatumProcessor proc,
557 void *proc_cls)
558{
559 struct Plugin *plugin = cls;
560 struct Value *value;
561
562 value = GNUNET_CONTAINER_heap_remove_root (plugin->by_replication);
563 if (NULL == value)
564 {
565 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
566 return;
567 }
568 if (value->replication > 0)
569 {
570 value->replication--;
571 value->replication_heap = GNUNET_CONTAINER_heap_insert (
572 plugin->by_replication,
573 value,
574 value->replication);
575 }
576 else
577 {
578 /* need a better way to pick a random item, replication level is always 0 */
579 value->replication_heap = GNUNET_CONTAINER_heap_insert (
580 plugin->by_replication,
581 value,
582 value->replication);
583 value = GNUNET_CONTAINER_heap_walk_get_next (plugin->by_replication);
584 }
585 GNUNET_assert (GNUNET_OK ==
586 proc (proc_cls,
587 &value->key,
588 value->size,
589 &value[1],
590 value->type,
591 value->priority,
592 value->anonymity,
593 value->replication,
594 value->expiration,
595 (uint64_t) (intptr_t) value));
596}
597
598
599/**
600 * Get a random item for expiration. Call 'proc' with all values ZERO
601 * or NULL if the datastore is empty.
602 *
603 * @param cls closure
604 * @param proc function to call the value (once only).
605 * @param proc_cls closure for proc
606 */
607static void
608heap_plugin_get_expiration (void *cls, PluginDatumProcessor proc,
609 void *proc_cls)
610{
611 struct Plugin *plugin = cls;
612 struct Value *value;
613
614 value = GNUNET_CONTAINER_heap_peek (plugin->by_expiration);
615 if (NULL == value)
616 {
617 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
618 return;
619 }
620 if (GNUNET_NO ==
621 proc (proc_cls,
622 &value->key,
623 value->size,
624 &value[1],
625 value->type,
626 value->priority,
627 value->anonymity,
628 value->replication,
629 value->expiration,
630 (uint64_t) (intptr_t) value))
631 delete_value (plugin, value);
632}
633
634
635/**
636 * Call the given processor on an item with zero anonymity.
637 *
638 * @param cls our "struct Plugin*"
639 * @param next_uid return the result with lowest uid >= next_uid
640 * @param type entries of which type should be considered?
641 * Must not be zero (ANY).
642 * @param proc function to call on each matching value;
643 * will be called with NULL if no value matches
644 * @param proc_cls closure for proc
645 */
646static void
647heap_plugin_get_zero_anonymity (void *cls, uint64_t next_uid,
648 enum GNUNET_BLOCK_Type type,
649 PluginDatumProcessor proc, void *proc_cls)
650{
651 struct Plugin *plugin = cls;
652 struct ZeroAnonByType *zabt;
653 struct Value *value = NULL;
654
655 for (zabt = plugin->zero_head; NULL != zabt; zabt = zabt->next)
656 {
657 if ((type != GNUNET_BLOCK_TYPE_ANY) &&
658 (type != zabt->type))
659 continue;
660 for (int i = 0; i < zabt->array_pos; ++i)
661 {
662 if ((uint64_t) (intptr_t) zabt->array[i] < next_uid)
663 continue;
664 if ((NULL != value) &&
665 (zabt->array[i] > value))
666 continue;
667 value = zabt->array[i];
668 }
669 }
670 if (NULL == value)
671 {
672 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
673 return;
674 }
675 GNUNET_assert (GNUNET_OK ==
676 proc (proc_cls,
677 &value->key,
678 value->size,
679 &value[1],
680 value->type,
681 value->priority,
682 value->anonymity,
683 value->replication,
684 value->expiration,
685 (uint64_t) (intptr_t) value));
686}
687
688
689/**
690 * Drop database.
691 */
692static void
693heap_plugin_drop (void *cls)
694{
695 /* nothing needs to be done */
696}
697
698
699/**
700 * Closure for the 'return_value' function.
701 */
702struct GetAllContext
703{
704 /**
705 * Function to call.
706 */
707 PluginKeyProcessor proc;
708
709 /**
710 * Closure for 'proc'.
711 */
712 void *proc_cls;
713};
714
715
716/**
717 * Callback invoked to call callback on each value.
718 *
719 * @param cls the plugin
720 * @param key unused
721 * @param val the value
722 * @return GNUNET_OK (continue to iterate)
723 */
724static int
725return_value (void *cls,
726 const struct GNUNET_HashCode *key,
727 void *val)
728{
729 struct GetAllContext *gac = cls;
730
731 gac->proc (gac->proc_cls,
732 key,
733 1);
734 return GNUNET_OK;
735}
736
737
738/**
739 * Get all of the keys in the datastore.
740 *
741 * @param cls closure
742 * @param proc function to call on each key
743 * @param proc_cls closure for proc
744 */
745static void
746heap_get_keys (void *cls,
747 PluginKeyProcessor proc,
748 void *proc_cls)
749{
750 struct Plugin *plugin = cls;
751 struct GetAllContext gac;
752
753 gac.proc = proc;
754 gac.proc_cls = proc_cls;
755 GNUNET_CONTAINER_multihashmap_iterate (plugin->keyvalue,
756 &return_value,
757 &gac);
758 proc (proc_cls, NULL, 0);
759}
760
761
762/**
763 * Closure for iterator called during 'remove_key'.
764 */
765struct RemoveContext
766{
767 /**
768 * Value found.
769 */
770 struct Value *value;
771
772 /**
773 * Size of data.
774 */
775 uint32_t size;
776
777 /**
778 * Data to remove.
779 */
780 const void *data;
781};
782
783
784/**
785 * Obtain the matching value with the lowest uid >= next_uid.
786 *
787 * @param cls the 'struct GetContext'
788 * @param key unused
789 * @param val the 'struct Value'
790 * @return GNUNET_YES (continue iteration), GNUNET_NO if result was found
791 */
792static int
793remove_iterator (void *cls,
794 const struct GNUNET_HashCode *key,
795 void *val)
796{
797 struct RemoveContext *rc = cls;
798 struct Value *value = val;
799
800 if (value->size != rc->size)
801 return GNUNET_YES;
802 if (0 != memcmp (value->data, rc->data, rc->size))
803 return GNUNET_YES;
804 rc->value = value;
805 return GNUNET_NO;
806}
807
808
809/**
810 * Remove a particular key in the datastore.
811 *
812 * @param cls closure
813 * @param key key for the content
814 * @param size number of bytes in data
815 * @param data content stored
816 * @param cont continuation called with success or failure status
817 * @param cont_cls continuation closure for @a cont
818 */
819static void
820heap_plugin_remove_key (void *cls,
821 const struct GNUNET_HashCode *key,
822 uint32_t size,
823 const void *data,
824 PluginRemoveCont cont,
825 void *cont_cls)
826{
827 struct Plugin *plugin = cls;
828 struct RemoveContext rc;
829
830 rc.value = NULL;
831 rc.size = size;
832 rc.data = data;
833 GNUNET_CONTAINER_multihashmap_get_multiple (plugin->keyvalue,
834 key,
835 &remove_iterator,
836 &rc);
837 if (NULL == rc.value)
838 {
839 cont (cont_cls,
840 key,
841 size,
842 GNUNET_NO,
843 NULL);
844 return;
845 }
846 delete_value (plugin,
847 rc.value);
848 cont (cont_cls,
849 key,
850 size,
851 GNUNET_OK,
852 NULL);
853}
854
855
856/**
857 * Entry point for the plugin.
858 *
859 * @param cls the "struct GNUNET_DATASTORE_PluginEnvironment*"
860 * @return our "struct Plugin*"
861 */
862void *
863libgnunet_plugin_datastore_heap_init (void *cls)
864{
865 struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
866 struct GNUNET_DATASTORE_PluginFunctions *api;
867 struct Plugin *plugin;
868 unsigned long long esize;
869
870 if (GNUNET_OK !=
871 GNUNET_CONFIGURATION_get_value_number (env->cfg,
872 "datastore-heap",
873 "HASHMAPSIZE",
874 &esize))
875 esize = 128 * 1024;
876 plugin = GNUNET_new (struct Plugin);
877 plugin->env = env;
878 plugin->keyvalue = GNUNET_CONTAINER_multihashmap_create (esize, GNUNET_YES);
879 plugin->by_expiration = GNUNET_CONTAINER_heap_create (
880 GNUNET_CONTAINER_HEAP_ORDER_MIN);
881 plugin->by_replication = GNUNET_CONTAINER_heap_create (
882 GNUNET_CONTAINER_HEAP_ORDER_MAX);
883 api = GNUNET_new (struct GNUNET_DATASTORE_PluginFunctions);
884 api->cls = plugin;
885 api->estimate_size = &heap_plugin_estimate_size;
886 api->put = &heap_plugin_put;
887 api->get_key = &heap_plugin_get_key;
888 api->get_replication = &heap_plugin_get_replication;
889 api->get_expiration = &heap_plugin_get_expiration;
890 api->get_zero_anonymity = &heap_plugin_get_zero_anonymity;
891 api->drop = &heap_plugin_drop;
892 api->get_keys = &heap_get_keys;
893 api->remove_key = &heap_plugin_remove_key;
894 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "heap",
895 _ ("Heap database running\n"));
896 return api;
897}
898
899
900/**
901 * Callback invoked to free all value.
902 *
903 * @param cls the plugin
904 * @param key unused
905 * @param val the value
906 * @return GNUNET_OK (continue to iterate)
907 */
908static int
909free_value (void *cls,
910 const struct GNUNET_HashCode *key,
911 void *val)
912{
913 struct Plugin *plugin = cls;
914 struct Value *value = val;
915
916 delete_value (plugin, value);
917 return GNUNET_OK;
918}
919
920
921/**
922 * Exit point from the plugin.
923 * @param cls our "struct Plugin*"
924 * @return always NULL
925 */
926void *
927libgnunet_plugin_datastore_heap_done (void *cls)
928{
929 struct GNUNET_DATASTORE_PluginFunctions *api = cls;
930 struct Plugin *plugin = api->cls;
931
932 GNUNET_CONTAINER_multihashmap_iterate (plugin->keyvalue,
933 &free_value,
934 plugin);
935 GNUNET_CONTAINER_multihashmap_destroy (plugin->keyvalue);
936 GNUNET_CONTAINER_heap_destroy (plugin->by_expiration);
937 GNUNET_CONTAINER_heap_destroy (plugin->by_replication);
938 GNUNET_free (plugin);
939 GNUNET_free (api);
940 return NULL;
941}
942
943
944/* end of plugin_datastore_heap.c */
diff --git a/src/plugin/datastore/plugin_datastore_postgres.c b/src/plugin/datastore/plugin_datastore_postgres.c
new file mode 100644
index 000000000..5fcacc17b
--- /dev/null
+++ b/src/plugin/datastore/plugin_datastore_postgres.c
@@ -0,0 +1,930 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2009-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 datastore/plugin_datastore_postgres.c
23 * @brief postgres-based datastore backend
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_datastore_plugin.h"
28#include "gnunet_pq_lib.h"
29
30
31/**
32 * After how many ms "busy" should a DB operation fail for good?
33 * A low value makes sure that we are more responsive to requests
34 * (especially PUTs). A high value guarantees a higher success
35 * rate (SELECTs in iterate can take several seconds despite LIMIT=1).
36 *
37 * The default value of 1s should ensure that users do not experience
38 * huge latencies while at the same time allowing operations to succeed
39 * with reasonable probability.
40 */
41#define BUSY_TIMEOUT GNUNET_TIME_UNIT_SECONDS
42
43
44/**
45 * Context for all functions in this plugin.
46 */
47struct Plugin
48{
49 /**
50 * Our execution environment.
51 */
52 struct GNUNET_DATASTORE_PluginEnvironment *env;
53
54 /**
55 * Native Postgres database handle.
56 */
57 struct GNUNET_PQ_Context *dbh;
58};
59
60
61/**
62 * @brief Get a database handle
63 *
64 * @param plugin global context
65 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
66 */
67static enum GNUNET_GenericReturnValue
68init_connection (struct Plugin *plugin)
69{
70#define RESULT_COLUMNS "repl, type, prio, anonLevel, expire, hash, value, oid"
71 struct GNUNET_PQ_PreparedStatement ps[] = {
72 GNUNET_PQ_make_prepare ("get",
73 "SELECT " RESULT_COLUMNS
74 " FROM datastore.gn090"
75 " WHERE oid >= $1::bigint AND"
76 " (rvalue >= $2 OR 0 = $3::smallint) AND"
77 " (hash = $4 OR 0 = $5::smallint) AND"
78 " (type = $6 OR 0 = $7::smallint)"
79 " ORDER BY oid ASC LIMIT 1"),
80 GNUNET_PQ_make_prepare ("put",
81 "INSERT INTO datastore.gn090"
82 " (repl, type, prio, anonLevel, expire, rvalue, hash, vhash, value) "
83 "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)"),
84 GNUNET_PQ_make_prepare ("update",
85 "UPDATE datastore.gn090"
86 " SET prio = prio + $1,"
87 " repl = repl + $2,"
88 " expire = GREATEST(expire, $3)"
89 " WHERE hash = $4 AND vhash = $5"),
90 GNUNET_PQ_make_prepare ("decrepl",
91 "UPDATE datastore.gn090"
92 " SET repl = GREATEST (repl - 1, 0)"
93 " WHERE oid = $1"),
94 GNUNET_PQ_make_prepare ("select_non_anonymous",
95 "SELECT " RESULT_COLUMNS
96 " FROM datastore.gn090"
97 " WHERE anonLevel = 0 AND type = $1 AND oid >= $2::bigint"
98 " ORDER BY oid ASC LIMIT 1"),
99 GNUNET_PQ_make_prepare ("select_expiration_order",
100 "(SELECT " RESULT_COLUMNS
101 " FROM datastore.gn090"
102 " WHERE expire < $1 ORDER BY prio ASC LIMIT 1) "
103 "UNION "
104 "(SELECT " RESULT_COLUMNS
105 " FROM datastore.gn090"
106 " ORDER BY prio ASC LIMIT 1)"
107 " ORDER BY expire ASC LIMIT 1"),
108 GNUNET_PQ_make_prepare ("select_replication_order",
109 "SELECT " RESULT_COLUMNS
110 " FROM datastore.gn090"
111 " ORDER BY repl DESC,RANDOM() LIMIT 1"),
112 GNUNET_PQ_make_prepare ("delrow",
113 "DELETE FROM datastore.gn090"
114 " WHERE oid=$1"),
115 GNUNET_PQ_make_prepare ("remove",
116 "DELETE FROM datastore.gn090"
117 " WHERE hash = $1 AND"
118 " value = $2"),
119 GNUNET_PQ_make_prepare ("get_keys",
120 "SELECT hash"
121 " FROM datastore.gn090"),
122 GNUNET_PQ_make_prepare ("estimate_size",
123 "SELECT CASE WHEN NOT EXISTS"
124 " (SELECT 1 FROM datastore.gn090)"
125 " THEN 0"
126 " ELSE (SELECT SUM(LENGTH(value))+256*COUNT(*)"
127 " FROM datastore.gn090)"
128 "END AS total"),
129 GNUNET_PQ_PREPARED_STATEMENT_END
130 };
131#undef RESULT_COLUMNS
132
133 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->env->cfg,
134 "datastore-postgres",
135 "datastore-",
136 NULL,
137 ps);
138 if (NULL == plugin->dbh)
139 return GNUNET_SYSERR;
140 return GNUNET_OK;
141}
142
143
144/**
145 * Get an estimate of how much space the database is
146 * currently using.
147 *
148 * @param cls our `struct Plugin *`
149 * @return number of bytes used on disk
150 */
151static void
152postgres_plugin_estimate_size (void *cls,
153 unsigned long long *estimate)
154{
155 struct Plugin *plugin = cls;
156 uint64_t total;
157 struct GNUNET_PQ_QueryParam params[] = {
158 GNUNET_PQ_query_param_end
159 };
160 struct GNUNET_PQ_ResultSpec rs[] = {
161 GNUNET_PQ_result_spec_uint64 ("total",
162 &total),
163 GNUNET_PQ_result_spec_end
164 };
165 enum GNUNET_DB_QueryStatus ret;
166
167 if (NULL == estimate)
168 return;
169 ret = GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh,
170 "estimate_size",
171 params,
172 rs);
173 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ret)
174 {
175 *estimate = 0LL;
176 return;
177 }
178 *estimate = total;
179}
180
181
182/**
183 * Store an item in the datastore.
184 *
185 * @param cls closure with the `struct Plugin`
186 * @param key key for the item
187 * @param absent true if the key was not found in the bloom filter
188 * @param size number of bytes in data
189 * @param data content stored
190 * @param type type of the content
191 * @param priority priority of the content
192 * @param anonymity anonymity-level for the content
193 * @param replication replication-level for the content
194 * @param expiration expiration time for the content
195 * @param cont continuation called with success or failure status
196 * @param cont_cls continuation closure
197 */
198static void
199postgres_plugin_put (void *cls,
200 const struct GNUNET_HashCode *key,
201 bool absent,
202 uint32_t size,
203 const void *data,
204 enum GNUNET_BLOCK_Type type,
205 uint32_t priority,
206 uint32_t anonymity,
207 uint32_t replication,
208 struct GNUNET_TIME_Absolute expiration,
209 PluginPutCont cont,
210 void *cont_cls)
211{
212 struct Plugin *plugin = cls;
213 struct GNUNET_HashCode vhash;
214 enum GNUNET_DB_QueryStatus ret;
215
216 GNUNET_CRYPTO_hash (data,
217 size,
218 &vhash);
219 if (! absent)
220 {
221 struct GNUNET_PQ_QueryParam params[] = {
222 GNUNET_PQ_query_param_uint32 (&priority),
223 GNUNET_PQ_query_param_uint32 (&replication),
224 GNUNET_PQ_query_param_absolute_time (&expiration),
225 GNUNET_PQ_query_param_auto_from_type (key),
226 GNUNET_PQ_query_param_auto_from_type (&vhash),
227 GNUNET_PQ_query_param_end
228 };
229 ret = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
230 "update",
231 params);
232 if (0 > ret)
233 {
234 cont (cont_cls,
235 key,
236 size,
237 GNUNET_SYSERR,
238 _ ("Postgresql exec failure"));
239 return;
240 }
241 bool affected = (0 != ret);
242 if (affected)
243 {
244 cont (cont_cls,
245 key,
246 size,
247 GNUNET_NO,
248 NULL);
249 return;
250 }
251 }
252
253 {
254 uint32_t utype = (uint32_t) type;
255 uint64_t rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
256 UINT64_MAX);
257 struct GNUNET_PQ_QueryParam params[] = {
258 GNUNET_PQ_query_param_uint32 (&replication),
259 GNUNET_PQ_query_param_uint32 (&utype),
260 GNUNET_PQ_query_param_uint32 (&priority),
261 GNUNET_PQ_query_param_uint32 (&anonymity),
262 GNUNET_PQ_query_param_absolute_time (&expiration),
263 GNUNET_PQ_query_param_uint64 (&rvalue),
264 GNUNET_PQ_query_param_auto_from_type (key),
265 GNUNET_PQ_query_param_auto_from_type (&vhash),
266 GNUNET_PQ_query_param_fixed_size (data, size),
267 GNUNET_PQ_query_param_end
268 };
269
270 ret = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
271 "put",
272 params);
273 if (0 > ret)
274 {
275 cont (cont_cls,
276 key,
277 size,
278 GNUNET_SYSERR,
279 "Postgresql exec failure");
280 return;
281 }
282 }
283 plugin->env->duc (plugin->env->cls,
284 size + GNUNET_DATASTORE_ENTRY_OVERHEAD);
285 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
286 "datastore-postgres",
287 "Stored %u bytes in database\n",
288 (unsigned int) size);
289 cont (cont_cls,
290 key,
291 size,
292 GNUNET_OK,
293 NULL);
294}
295
296
297/**
298 * Closure for #process_result.
299 */
300struct ProcessResultContext
301{
302 /**
303 * The plugin handle.
304 */
305 struct Plugin *plugin;
306
307 /**
308 * Function to call on each result.
309 */
310 PluginDatumProcessor proc;
311
312 /**
313 * Closure for @e proc.
314 */
315 void *proc_cls;
316};
317
318
319/**
320 * Function invoked to process the result and call the processor of @a
321 * cls.
322 *
323 * @param cls our `struct ProcessResultContext`
324 * @param res result from exec
325 * @param num_results number of results in @a res
326 */
327static void
328process_result (void *cls,
329 PGresult *res,
330 unsigned int num_results)
331{
332 struct ProcessResultContext *prc = cls;
333 struct Plugin *plugin = prc->plugin;
334
335 if (0 == num_results)
336 {
337 /* no result */
338 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
339 "datastore-postgres",
340 "Ending iteration (no more results)\n");
341 prc->proc (prc->proc_cls, NULL, 0, NULL, 0, 0, 0, 0,
342 GNUNET_TIME_UNIT_ZERO_ABS, 0);
343 return;
344 }
345 if (1 != num_results)
346 {
347 GNUNET_break (0);
348 prc->proc (prc->proc_cls, NULL, 0, NULL, 0, 0, 0, 0,
349 GNUNET_TIME_UNIT_ZERO_ABS, 0);
350 return;
351 }
352 /* Technically we don't need the loop here, but nicer in case
353 we ever relax the condition above. */
354 for (unsigned int i = 0; i < num_results; i++)
355 {
356 int iret;
357 uint64_t rowid;
358 uint32_t utype;
359 uint32_t anonymity;
360 uint32_t replication;
361 uint32_t priority;
362 size_t size;
363 void *data;
364 struct GNUNET_TIME_Absolute expiration_time;
365 struct GNUNET_HashCode key;
366 struct GNUNET_PQ_ResultSpec rs[] = {
367 GNUNET_PQ_result_spec_uint32 ("repl", &replication),
368 GNUNET_PQ_result_spec_uint32 ("type", &utype),
369 GNUNET_PQ_result_spec_uint32 ("prio", &priority),
370 GNUNET_PQ_result_spec_uint32 ("anonLevel", &anonymity),
371 GNUNET_PQ_result_spec_absolute_time ("expire", &expiration_time),
372 GNUNET_PQ_result_spec_auto_from_type ("hash", &key),
373 GNUNET_PQ_result_spec_variable_size ("value", &data, &size),
374 GNUNET_PQ_result_spec_uint64 ("oid", &rowid),
375 GNUNET_PQ_result_spec_end
376 };
377
378 if (GNUNET_OK !=
379 GNUNET_PQ_extract_result (res,
380 rs,
381 i))
382 {
383 GNUNET_break (0);
384 prc->proc (prc->proc_cls, NULL, 0, NULL, 0, 0, 0, 0,
385 GNUNET_TIME_UNIT_ZERO_ABS, 0);
386 return;
387 }
388
389 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
390 "datastore-postgres",
391 "Found result of size %u bytes and type %u in database\n",
392 (unsigned int) size,
393 (unsigned int) utype);
394 iret = prc->proc (prc->proc_cls,
395 &key,
396 size,
397 data,
398 (enum GNUNET_BLOCK_Type) utype,
399 priority,
400 anonymity,
401 replication,
402 expiration_time,
403 rowid);
404 if (iret == GNUNET_NO)
405 {
406 struct GNUNET_PQ_QueryParam param[] = {
407 GNUNET_PQ_query_param_uint64 (&rowid),
408 GNUNET_PQ_query_param_end
409 };
410
411 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
412 "Processor asked for item %u to be removed.\n",
413 (unsigned int) rowid);
414 if (0 <
415 GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
416 "delrow",
417 param))
418 {
419 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
420 "datastore-postgres",
421 "Deleting %u bytes from database\n",
422 (unsigned int) size);
423 plugin->env->duc (plugin->env->cls,
424 -(size + GNUNET_DATASTORE_ENTRY_OVERHEAD));
425 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
426 "datastore-postgres",
427 "Deleted %u bytes from database\n",
428 (unsigned int) size);
429 }
430 }
431 GNUNET_PQ_cleanup_result (rs);
432 } /* for (i) */
433}
434
435
436/**
437 * Get one of the results for a particular key in the datastore.
438 *
439 * @param cls closure with the `struct Plugin`
440 * @param next_uid return the result with lowest uid >= next_uid
441 * @param random if true, return a random result instead of using next_uid
442 * @param key maybe NULL (to match all entries)
443 * @param type entries of which type are relevant?
444 * Use 0 for any type.
445 * @param proc function to call on the matching value;
446 * will be called with NULL if nothing matches
447 * @param proc_cls closure for @a proc
448 */
449static void
450postgres_plugin_get_key (void *cls,
451 uint64_t next_uid,
452 bool random,
453 const struct GNUNET_HashCode *key,
454 enum GNUNET_BLOCK_Type type,
455 PluginDatumProcessor proc,
456 void *proc_cls)
457{
458 struct Plugin *plugin = cls;
459 uint32_t utype = type;
460 uint16_t use_rvalue = random;
461 uint16_t use_key = NULL != key;
462 uint16_t use_type = GNUNET_BLOCK_TYPE_ANY != type;
463 uint64_t rvalue;
464 struct GNUNET_PQ_QueryParam params[] = {
465 GNUNET_PQ_query_param_uint64 (&next_uid),
466 GNUNET_PQ_query_param_uint64 (&rvalue),
467 GNUNET_PQ_query_param_uint16 (&use_rvalue),
468 GNUNET_PQ_query_param_auto_from_type (key),
469 GNUNET_PQ_query_param_uint16 (&use_key),
470 GNUNET_PQ_query_param_uint32 (&utype),
471 GNUNET_PQ_query_param_uint16 (&use_type),
472 GNUNET_PQ_query_param_end
473 };
474 struct ProcessResultContext prc;
475 enum GNUNET_DB_QueryStatus res;
476
477 if (random)
478 {
479 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
480 UINT64_MAX);
481 next_uid = 0;
482 }
483 else
484 {
485 rvalue = 0;
486 }
487 prc.plugin = plugin;
488 prc.proc = proc;
489 prc.proc_cls = proc_cls;
490
491 res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
492 "get",
493 params,
494 &process_result,
495 &prc);
496 if (0 > res)
497 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0,
498 GNUNET_TIME_UNIT_ZERO_ABS, 0);
499}
500
501
502/**
503 * Select a subset of the items in the datastore and call
504 * the given iterator for each of them.
505 *
506 * @param cls our `struct Plugin *`
507 * @param next_uid return the result with lowest uid >= next_uid
508 * @param type entries of which type should be considered?
509 * Must not be zero (ANY).
510 * @param proc function to call on the matching value;
511 * will be called with NULL if no value matches
512 * @param proc_cls closure for @a proc
513 */
514static void
515postgres_plugin_get_zero_anonymity (void *cls,
516 uint64_t next_uid,
517 enum GNUNET_BLOCK_Type type,
518 PluginDatumProcessor proc,
519 void *proc_cls)
520{
521 struct Plugin *plugin = cls;
522 uint32_t utype = type;
523 struct GNUNET_PQ_QueryParam params[] = {
524 GNUNET_PQ_query_param_uint32 (&utype),
525 GNUNET_PQ_query_param_uint64 (&next_uid),
526 GNUNET_PQ_query_param_end
527 };
528 struct ProcessResultContext prc;
529 enum GNUNET_DB_QueryStatus res;
530
531 prc.plugin = plugin;
532 prc.proc = proc;
533 prc.proc_cls = proc_cls;
534 res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
535 "select_non_anonymous",
536 params,
537 &process_result,
538 &prc);
539 if (0 > res)
540 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0,
541 GNUNET_TIME_UNIT_ZERO_ABS, 0);
542}
543
544
545/**
546 * Context for #repl_iter() function.
547 */
548struct ReplCtx
549{
550 /**
551 * Plugin handle.
552 */
553 struct Plugin *plugin;
554
555 /**
556 * Function to call for the result (or the NULL).
557 */
558 PluginDatumProcessor proc;
559
560 /**
561 * Closure for @e proc.
562 */
563 void *proc_cls;
564};
565
566
567/**
568 * Wrapper for the iterator for 'sqlite_plugin_replication_get'.
569 * Decrements the replication counter and calls the original
570 * iterator.
571 *
572 * @param cls closure with the `struct ReplCtx *`
573 * @param key key for the content
574 * @param size number of bytes in @a data
575 * @param data content stored
576 * @param type type of the content
577 * @param priority priority of the content
578 * @param anonymity anonymity-level for the content
579 * @param replication replication-level for the content
580 * @param expiration expiration time for the content
581 * @param uid unique identifier for the datum;
582 * maybe 0 if no unique identifier is available
583 * @return #GNUNET_SYSERR to abort the iteration,
584 * #GNUNET_OK to continue
585 * (continue on call to "next", of course),
586 * #GNUNET_NO to delete the item and continue (if supported)
587 */
588static int
589repl_proc (void *cls,
590 const struct GNUNET_HashCode *key,
591 uint32_t size,
592 const void *data,
593 enum GNUNET_BLOCK_Type type,
594 uint32_t priority,
595 uint32_t anonymity,
596 uint32_t replication,
597 struct GNUNET_TIME_Absolute expiration,
598 uint64_t uid)
599{
600 struct ReplCtx *rc = cls;
601 struct Plugin *plugin = rc->plugin;
602 int ret;
603 struct GNUNET_PQ_QueryParam params[] = {
604 GNUNET_PQ_query_param_uint64 (&uid),
605 GNUNET_PQ_query_param_end
606 };
607 enum GNUNET_DB_QueryStatus qret;
608
609 ret = rc->proc (rc->proc_cls,
610 key,
611 size,
612 data,
613 type,
614 priority,
615 anonymity,
616 replication,
617 expiration,
618 uid);
619 if (NULL == key)
620 return ret;
621 qret = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
622 "decrepl",
623 params);
624 if (0 > qret)
625 return GNUNET_SYSERR;
626 return ret;
627}
628
629
630/**
631 * Get a random item for replication. Returns a single, not expired,
632 * random item from those with the highest replication counters. The
633 * item's replication counter is decremented by one IF it was positive
634 * before. Call @a proc with all values ZERO or NULL if the datastore
635 * is empty.
636 *
637 * @param cls closure with the `struct Plugin`
638 * @param proc function to call the value (once only).
639 * @param proc_cls closure for @a proc
640 */
641static void
642postgres_plugin_get_replication (void *cls,
643 PluginDatumProcessor proc,
644 void *proc_cls)
645{
646 struct Plugin *plugin = cls;
647 struct GNUNET_PQ_QueryParam params[] = {
648 GNUNET_PQ_query_param_end
649 };
650 struct ReplCtx rc;
651 struct ProcessResultContext prc;
652 enum GNUNET_DB_QueryStatus res;
653
654 rc.plugin = plugin;
655 rc.proc = proc;
656 rc.proc_cls = proc_cls;
657 prc.plugin = plugin;
658 prc.proc = &repl_proc;
659 prc.proc_cls = &rc;
660 res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
661 "select_replication_order",
662 params,
663 &process_result,
664 &prc);
665 if (0 > res)
666 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0,
667 GNUNET_TIME_UNIT_ZERO_ABS, 0);
668}
669
670
671/**
672 * Get a random item for expiration. Call @a proc with all values
673 * ZERO or NULL if the datastore is empty.
674 *
675 * @param cls closure with the `struct Plugin`
676 * @param proc function to call the value (once only).
677 * @param proc_cls closure for @a proc
678 */
679static void
680postgres_plugin_get_expiration (void *cls,
681 PluginDatumProcessor proc,
682 void *proc_cls)
683{
684 struct Plugin *plugin = cls;
685 struct GNUNET_TIME_Absolute now = { 0 };
686 struct GNUNET_PQ_QueryParam params[] = {
687 GNUNET_PQ_query_param_absolute_time (&now),
688 GNUNET_PQ_query_param_end
689 };
690 struct ProcessResultContext prc;
691
692 now = GNUNET_TIME_absolute_get ();
693 prc.plugin = plugin;
694 prc.proc = proc;
695 prc.proc_cls = proc_cls;
696 (void) GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
697 "select_expiration_order",
698 params,
699 &process_result,
700 &prc);
701}
702
703
704/**
705 * Closure for #process_keys.
706 */
707struct ProcessKeysContext
708{
709 /**
710 * Function to call for each key.
711 */
712 PluginKeyProcessor proc;
713
714 /**
715 * Closure for @e proc.
716 */
717 void *proc_cls;
718};
719
720
721/**
722 * Function to be called with the results of a SELECT statement
723 * that has returned @a num_results results.
724 *
725 * @param cls closure with a `struct ProcessKeysContext`
726 * @param result the postgres result
727 * @param num_results the number of results in @a result
728 */
729static void
730process_keys (void *cls,
731 PGresult *result,
732 unsigned int num_results)
733{
734 struct ProcessKeysContext *pkc = cls;
735
736 for (unsigned i = 0; i < num_results; i++)
737 {
738 struct GNUNET_HashCode key;
739 struct GNUNET_PQ_ResultSpec rs[] = {
740 GNUNET_PQ_result_spec_auto_from_type ("hash",
741 &key),
742 GNUNET_PQ_result_spec_end
743 };
744
745 if (GNUNET_OK !=
746 GNUNET_PQ_extract_result (result,
747 rs,
748 i))
749 {
750 GNUNET_break (0);
751 continue;
752 }
753 pkc->proc (pkc->proc_cls,
754 &key,
755 1);
756 GNUNET_PQ_cleanup_result (rs);
757 }
758}
759
760
761/**
762 * Get all of the keys in the datastore.
763 *
764 * @param cls closure with the `struct Plugin *`
765 * @param proc function to call on each key
766 * @param proc_cls closure for @a proc
767 */
768static void
769postgres_plugin_get_keys (void *cls,
770 PluginKeyProcessor proc,
771 void *proc_cls)
772{
773 struct Plugin *plugin = cls;
774 struct GNUNET_PQ_QueryParam params[] = {
775 GNUNET_PQ_query_param_end
776 };
777 struct ProcessKeysContext pkc;
778
779 pkc.proc = proc;
780 pkc.proc_cls = proc_cls;
781 (void) GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
782 "get_keys",
783 params,
784 &process_keys,
785 &pkc);
786 proc (proc_cls,
787 NULL,
788 0);
789}
790
791
792/**
793 * Drop database.
794 *
795 * @param cls closure with the `struct Plugin *`
796 */
797static void
798postgres_plugin_drop (void *cls)
799{
800 struct Plugin *plugin = cls;
801 struct GNUNET_PQ_ExecuteStatement es[] = {
802 GNUNET_PQ_make_execute ("DROP TABLE gn090"),
803 GNUNET_PQ_EXECUTE_STATEMENT_END
804 };
805
806 if (GNUNET_OK !=
807 GNUNET_PQ_exec_statements (plugin->dbh,
808 es))
809 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
810 "postgres",
811 _ ("Failed to drop table from database.\n"));
812}
813
814
815/**
816 * Remove a particular key in the datastore.
817 *
818 * @param cls closure
819 * @param key key for the content
820 * @param size number of bytes in data
821 * @param data content stored
822 * @param cont continuation called with success or failure status
823 * @param cont_cls continuation closure for @a cont
824 */
825static void
826postgres_plugin_remove_key (void *cls,
827 const struct GNUNET_HashCode *key,
828 uint32_t size,
829 const void *data,
830 PluginRemoveCont cont,
831 void *cont_cls)
832{
833 struct Plugin *plugin = cls;
834 enum GNUNET_DB_QueryStatus ret;
835 struct GNUNET_PQ_QueryParam params[] = {
836 GNUNET_PQ_query_param_auto_from_type (key),
837 GNUNET_PQ_query_param_fixed_size (data, size),
838 GNUNET_PQ_query_param_end
839 };
840
841 ret = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
842 "remove",
843 params);
844 if (0 > ret)
845 {
846 cont (cont_cls,
847 key,
848 size,
849 GNUNET_SYSERR,
850 _ ("Postgresql exec failure"));
851 return;
852 }
853 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == ret)
854 {
855 cont (cont_cls,
856 key,
857 size,
858 GNUNET_NO,
859 NULL);
860 return;
861 }
862 plugin->env->duc (plugin->env->cls,
863 -(size + GNUNET_DATASTORE_ENTRY_OVERHEAD));
864 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
865 "datastore-postgres",
866 "Deleted %u bytes from database\n",
867 (unsigned int) size);
868 cont (cont_cls,
869 key,
870 size,
871 GNUNET_OK,
872 NULL);
873}
874
875
876/**
877 * Entry point for the plugin.
878 *
879 * @param cls the `struct GNUNET_DATASTORE_PluginEnvironment*`
880 * @return our `struct Plugin *`
881 */
882void *
883libgnunet_plugin_datastore_postgres_init (void *cls)
884{
885 struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
886 struct GNUNET_DATASTORE_PluginFunctions *api;
887 struct Plugin *plugin;
888
889 plugin = GNUNET_new (struct Plugin);
890 plugin->env = env;
891 if (GNUNET_OK != init_connection (plugin))
892 {
893 GNUNET_free (plugin);
894 return NULL;
895 }
896 api = GNUNET_new (struct GNUNET_DATASTORE_PluginFunctions);
897 api->cls = plugin;
898 api->estimate_size = &postgres_plugin_estimate_size;
899 api->put = &postgres_plugin_put;
900 api->get_key = &postgres_plugin_get_key;
901 api->get_replication = &postgres_plugin_get_replication;
902 api->get_expiration = &postgres_plugin_get_expiration;
903 api->get_zero_anonymity = &postgres_plugin_get_zero_anonymity;
904 api->get_keys = &postgres_plugin_get_keys;
905 api->drop = &postgres_plugin_drop;
906 api->remove_key = &postgres_plugin_remove_key;
907 return api;
908}
909
910
911/**
912 * Exit point from the plugin.
913 *
914 * @param cls our `struct Plugin *`
915 * @return always NULL
916 */
917void *
918libgnunet_plugin_datastore_postgres_done (void *cls)
919{
920 struct GNUNET_DATASTORE_PluginFunctions *api = cls;
921 struct Plugin *plugin = api->cls;
922
923 GNUNET_PQ_disconnect (plugin->dbh);
924 GNUNET_free (plugin);
925 GNUNET_free (api);
926 return NULL;
927}
928
929
930/* end of plugin_datastore_postgres.c */
diff --git a/src/plugin/datastore/plugin_datastore_sqlite.c b/src/plugin/datastore/plugin_datastore_sqlite.c
new file mode 100644
index 000000000..5ea9da4cb
--- /dev/null
+++ b/src/plugin/datastore/plugin_datastore_sqlite.c
@@ -0,0 +1,1375 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2009, 2011, 2017 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 datastore/plugin_datastore_sqlite.c
23 * @brief sqlite-based datastore backend
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_datastore_plugin.h"
29#include "gnunet_sq_lib.h"
30#include <sqlite3.h>
31
32
33/**
34 * We allocate items on the stack at times. To prevent a stack
35 * overflow, we impose a limit on the maximum size for the data per
36 * item. 64k should be enough.
37 */
38#define MAX_ITEM_SIZE 65536
39
40/**
41 * After how many ms "busy" should a DB operation fail for good?
42 * A low value makes sure that we are more responsive to requests
43 * (especially PUTs). A high value guarantees a higher success
44 * rate (SELECTs in iterate can take several seconds despite LIMIT=1).
45 *
46 * The default value of 250ms should ensure that users do not experience
47 * huge latencies while at the same time allowing operations to succeed
48 * with reasonable probability.
49 */
50#define BUSY_TIMEOUT_MS 250
51
52
53/**
54 * Log an error message at log-level 'level' that indicates
55 * a failure of the command 'cmd' on file 'filename'
56 * with the message given by strerror(errno).
57 */
58#define LOG_SQLITE(db, level, cmd) \
59 do \
60 { \
61 GNUNET_log_from (level, \
62 "sqlite", \
63 _ ("`%s' failed at %s:%d with error: %s\n"), \
64 cmd, \
65 __FILE__, \
66 __LINE__, \
67 sqlite3_errmsg (db->dbh)); \
68 } while (0)
69
70
71/**
72 * Log an error message at log-level 'level' that indicates
73 * a failure of the command 'cmd' on file 'filename'
74 * with the message given by strerror(errno).
75 */
76#define LOG_SQLITE_MSG(db, msg, level, cmd) \
77 do \
78 { \
79 GNUNET_log_from (level, \
80 "sqlite", \
81 _ ("`%s' failed at %s:%d with error: %s\n"), \
82 cmd, \
83 __FILE__, \
84 __LINE__, \
85 sqlite3_errmsg (db->dbh)); \
86 GNUNET_asprintf (msg, \
87 _ ("`%s' failed at %s:%u with error: %s"), \
88 cmd, \
89 __FILE__, \
90 __LINE__, \
91 sqlite3_errmsg (db->dbh)); \
92 } while (0)
93
94
95/**
96 * Context for all functions in this plugin.
97 */
98struct Plugin
99{
100 /**
101 * Our execution environment.
102 */
103 struct GNUNET_DATASTORE_PluginEnvironment *env;
104
105 /**
106 * Database filename.
107 */
108 char *fn;
109
110 /**
111 * Native SQLite database handle.
112 */
113 sqlite3 *dbh;
114
115 /**
116 * Precompiled SQL for remove_key.
117 */
118 sqlite3_stmt *remove;
119
120 /**
121 * Precompiled SQL for deletion.
122 */
123 sqlite3_stmt *delRow;
124
125 /**
126 * Precompiled SQL for update.
127 */
128 sqlite3_stmt *update;
129
130 /**
131 * Get maximum repl value in database.
132 */
133 sqlite3_stmt *maxRepl;
134
135 /**
136 * Precompiled SQL for replication decrement.
137 */
138 sqlite3_stmt *updRepl;
139
140 /**
141 * Precompiled SQL for replication selection.
142 */
143 sqlite3_stmt *selRepl;
144
145 /**
146 * Precompiled SQL for expiration selection.
147 */
148 sqlite3_stmt *selExpi;
149
150 /**
151 * Precompiled SQL for expiration selection.
152 */
153 sqlite3_stmt *selZeroAnon;
154
155 /**
156 * Precompiled SQL for insertion.
157 */
158 sqlite3_stmt *insertContent;
159
160 /**
161 * Precompiled SQL for selection
162 */
163 sqlite3_stmt *get[8];
164
165 /**
166 * Should the database be dropped on shutdown?
167 */
168 int drop_on_shutdown;
169};
170
171
172/**
173 * @brief Prepare a SQL statement
174 *
175 * @param dbh handle to the database
176 * @param zSql SQL statement, UTF-8 encoded
177 * @param ppStmt set to the prepared statement
178 * @return 0 on success
179 */
180static int
181sq_prepare (sqlite3 *dbh, const char *zSql, sqlite3_stmt **ppStmt)
182{
183 char *dummy;
184 int result;
185
186 result = sqlite3_prepare_v2 (dbh,
187 zSql,
188 strlen (zSql),
189 ppStmt,
190 (const char **) &dummy);
191 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
192 "sqlite",
193 "Prepared `%s' / %p: %d\n",
194 zSql,
195 *ppStmt,
196 result);
197 return result;
198}
199
200
201/**
202 * Create our database indices.
203 *
204 * @param dbh handle to the database
205 */
206static void
207create_indices (sqlite3 *dbh)
208{
209 /* create indices */
210 if (
211 0 !=
212 (SQLITE_OK !=
213 sqlite3_exec (dbh,
214 "CREATE INDEX IF NOT EXISTS idx_hash ON gn091 (hash)",
215 NULL,
216 NULL,
217 NULL))
218 + (SQLITE_OK !=
219 sqlite3_exec (
220 dbh,
221 "CREATE INDEX IF NOT EXISTS idx_anon_type ON gn091 (anonLevel ASC,type)",
222 NULL,
223 NULL,
224 NULL))
225 + (SQLITE_OK !=
226 sqlite3_exec (dbh,
227 "CREATE INDEX IF NOT EXISTS idx_expire ON gn091 (expire ASC)",
228 NULL,
229 NULL,
230 NULL))
231 + (SQLITE_OK !=
232 sqlite3_exec (
233 dbh,
234 "CREATE INDEX IF NOT EXISTS idx_repl_rvalue ON gn091 (repl,rvalue)",
235 NULL,
236 NULL,
237 NULL)))
238 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
239 "sqlite",
240 "Failed to create indices: %s\n",
241 sqlite3_errmsg (dbh));
242}
243
244
245#if 0
246#define CHECK(a) GNUNET_break (a)
247#define ENULL NULL
248#else
249#define ENULL &e
250#define ENULL_DEFINED 1
251#define CHECK(a) \
252 if (! (a)) \
253 { \
254 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", e); \
255 sqlite3_free (e); \
256 e = NULL; \
257 }
258#endif
259
260
261/**
262 * Initialize the database connections and associated
263 * data structures (create tables and indices
264 * as needed as well).
265 *
266 * @param cfg our configuration
267 * @param plugin the plugin context (state for this module)
268 * @return #GNUNET_OK on success
269 */
270static int
271database_setup (const struct GNUNET_CONFIGURATION_Handle *cfg,
272 struct Plugin *plugin)
273{
274 sqlite3_stmt *stmt;
275 char *afsdir;
276
277#if ENULL_DEFINED
278 char *e;
279#endif
280
281 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
282 "datastore-sqlite",
283 "FILENAME",
284 &afsdir))
285 {
286 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
287 "datastore-sqlite",
288 "FILENAME");
289 return GNUNET_SYSERR;
290 }
291 if (GNUNET_OK != GNUNET_DISK_file_test (afsdir))
292 {
293 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (afsdir))
294 {
295 GNUNET_break (0);
296 GNUNET_free (afsdir);
297 return GNUNET_SYSERR;
298 }
299 /* database is new or got deleted, reset payload to zero! */
300 if (NULL != plugin->env->duc)
301 plugin->env->duc (plugin->env->cls, 0);
302 }
303 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
304 plugin->fn = afsdir;
305
306 /* Open database and precompile statements */
307 if (SQLITE_OK != sqlite3_open (plugin->fn, &plugin->dbh))
308 {
309 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
310 "sqlite",
311 _ ("Unable to initialize SQLite: %s.\n"),
312 sqlite3_errmsg (plugin->dbh));
313 return GNUNET_SYSERR;
314 }
315 CHECK (
316 SQLITE_OK ==
317 sqlite3_exec (plugin->dbh, "PRAGMA temp_store=MEMORY", NULL, NULL, ENULL));
318 CHECK (
319 SQLITE_OK ==
320 sqlite3_exec (plugin->dbh, "PRAGMA synchronous=OFF", NULL, NULL, ENULL));
321 CHECK (SQLITE_OK == sqlite3_exec (plugin->dbh,
322 "PRAGMA legacy_file_format=OFF",
323 NULL,
324 NULL,
325 ENULL));
326 CHECK (SQLITE_OK == sqlite3_exec (plugin->dbh,
327 "PRAGMA auto_vacuum=INCREMENTAL",
328 NULL,
329 NULL,
330 ENULL));
331 CHECK (SQLITE_OK == sqlite3_exec (plugin->dbh,
332 "PRAGMA locking_mode=EXCLUSIVE",
333 NULL,
334 NULL,
335 ENULL));
336 CHECK (
337 SQLITE_OK ==
338 sqlite3_exec (plugin->dbh, "PRAGMA page_size=4096", NULL, NULL, ENULL));
339
340 CHECK (SQLITE_OK == sqlite3_busy_timeout (plugin->dbh, BUSY_TIMEOUT_MS));
341
342
343 /* We have to do it here, because otherwise precompiling SQL might fail */
344 CHECK (SQLITE_OK ==
345 sq_prepare (plugin->dbh,
346 "SELECT 1 FROM sqlite_master WHERE tbl_name = 'gn091'",
347 &stmt));
348
349 /* FIXME: SQLite does not have unsigned integers! This is ok for the type column because
350 * we only test equality on it and can cast it to/from uint32_t. For repl, prio, and anonLevel
351 * we do math or inequality tests, so we can't handle the entire range of uint32_t.
352 * This will also cause problems for expiration times after 294247-01-10-04:00:54 UTC.
353 */if ((SQLITE_DONE == sqlite3_step (stmt)) &&
354 (SQLITE_OK != sqlite3_exec (plugin->dbh,
355 "CREATE TABLE gn091 ("
356 " repl INT4 NOT NULL DEFAULT 0,"
357 " type INT4 NOT NULL DEFAULT 0,"
358 " prio INT4 NOT NULL DEFAULT 0,"
359 " anonLevel INT4 NOT NULL DEFAULT 0,"
360 " expire INT8 NOT NULL DEFAULT 0,"
361 " rvalue INT8 NOT NULL,"
362 " hash TEXT NOT NULL DEFAULT '',"
363 " vhash TEXT NOT NULL DEFAULT '',"
364 " value BLOB NOT NULL DEFAULT '')",
365 NULL,
366 NULL,
367 NULL)))
368 {
369 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite3_exec");
370 sqlite3_finalize (stmt);
371 return GNUNET_SYSERR;
372 }
373 sqlite3_finalize (stmt);
374 create_indices (plugin->dbh);
375
376#define RESULT_COLUMNS \
377 "repl, type, prio, anonLevel, expire, hash, value, _ROWID_"
378 if (
379 (SQLITE_OK != sq_prepare (plugin->dbh,
380 "UPDATE gn091 "
381 "SET prio = prio + ?, "
382 "repl = repl + ?, "
383 "expire = MAX(expire, ?) "
384 "WHERE hash = ? AND vhash = ?",
385 &plugin->update)) ||
386 (SQLITE_OK != sq_prepare (plugin->dbh,
387 "UPDATE gn091 "
388 "SET repl = MAX (0, repl - 1) WHERE _ROWID_ = ?",
389 &plugin->updRepl)) ||
390 (SQLITE_OK != sq_prepare (plugin->dbh,
391 "SELECT " RESULT_COLUMNS " FROM gn091 "
392 "WHERE repl=?2 AND "
393 " (rvalue>=?1 OR "
394 " NOT EXISTS (SELECT 1 FROM gn091 "
395 "WHERE repl=?2 AND rvalue>=?1 LIMIT 1) ) "
396 "ORDER BY rvalue ASC LIMIT 1",
397 &plugin->selRepl)) ||
398 (SQLITE_OK != sq_prepare (plugin->dbh,
399 "SELECT MAX(repl) FROM gn091",
400 &plugin->maxRepl)) ||
401 (SQLITE_OK !=
402 sq_prepare (plugin->dbh,
403 "SELECT " RESULT_COLUMNS " FROM gn091 "
404 "WHERE NOT EXISTS (SELECT 1 FROM gn091 WHERE expire < ?1 LIMIT 1) OR (expire < ?1) "
405 "ORDER BY expire ASC LIMIT 1",
406 &plugin->selExpi)) ||
407 (SQLITE_OK != sq_prepare (plugin->dbh,
408 "SELECT " RESULT_COLUMNS " FROM gn091 "
409 "WHERE _ROWID_ >= ? AND "
410 "anonLevel = 0 AND "
411 "type = ? "
412 "ORDER BY _ROWID_ ASC LIMIT 1",
413 &plugin->selZeroAnon)) ||
414 (SQLITE_OK !=
415 sq_prepare (plugin->dbh,
416 "INSERT INTO gn091 (repl, type, prio, anonLevel, expire, rvalue, hash, vhash, value) "
417 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
418 &plugin->insertContent)) ||
419 (SQLITE_OK != sq_prepare (plugin->dbh,
420 "SELECT " RESULT_COLUMNS " FROM gn091 "
421 "WHERE _ROWID_ >= ?1 "
422 "ORDER BY _ROWID_ ASC LIMIT 1",
423 &plugin->get[0])) ||
424 (SQLITE_OK != sq_prepare (plugin->dbh,
425 "SELECT " RESULT_COLUMNS " FROM gn091 "
426 "WHERE _ROWID_ >= ?1 AND "
427 "type = ?4 "
428 "ORDER BY _ROWID_ ASC LIMIT 1",
429 &plugin->get[1])) ||
430 (SQLITE_OK != sq_prepare (plugin->dbh,
431 "SELECT " RESULT_COLUMNS " FROM gn091 "
432 "WHERE _ROWID_ >= ?1 AND "
433 "hash = ?3 "
434 "ORDER BY _ROWID_ ASC LIMIT 1",
435 &plugin->get[2])) ||
436 (SQLITE_OK != sq_prepare (plugin->dbh,
437 "SELECT " RESULT_COLUMNS " FROM gn091 "
438 "WHERE _ROWID_ >= ?1 AND "
439 "hash = ?3 AND "
440 "type = ?4 "
441 "ORDER BY _ROWID_ ASC LIMIT 1",
442 &plugin->get[3])) ||
443 (SQLITE_OK != sq_prepare (plugin->dbh,
444 "SELECT " RESULT_COLUMNS " FROM gn091 "
445 "WHERE _ROWID_ >= ?1 AND "
446 "rvalue >= ?2 "
447 "ORDER BY _ROWID_ ASC LIMIT 1",
448 &plugin->get[4])) ||
449 (SQLITE_OK != sq_prepare (plugin->dbh,
450 "SELECT " RESULT_COLUMNS " FROM gn091 "
451 "WHERE _ROWID_ >= ?1 AND "
452 "rvalue >= ?2 AND "
453 "type = ?4 "
454 "ORDER BY _ROWID_ ASC LIMIT 1",
455 &plugin->get[5])) ||
456 (SQLITE_OK != sq_prepare (plugin->dbh,
457 "SELECT " RESULT_COLUMNS " FROM gn091 "
458 "WHERE _ROWID_ >= ?1 AND "
459 "rvalue >= ?2 AND "
460 "hash = ?3 "
461 "ORDER BY _ROWID_ ASC LIMIT 1",
462 &plugin->get[6])) ||
463 (SQLITE_OK != sq_prepare (plugin->dbh,
464 "SELECT " RESULT_COLUMNS " FROM gn091 "
465 "WHERE _ROWID_ >= ?1 AND "
466 "rvalue >= ?2 AND "
467 "hash = ?3 AND "
468 "type = ?4 "
469 "ORDER BY _ROWID_ ASC LIMIT 1",
470 &plugin->get[7])) ||
471 (SQLITE_OK != sq_prepare (plugin->dbh,
472 "DELETE FROM gn091 WHERE _ROWID_ = ?",
473 &plugin->delRow)) ||
474 (SQLITE_OK != sq_prepare (plugin->dbh,
475 "DELETE FROM gn091 "
476 "WHERE hash = ? AND "
477 "value = ? ",
478 &plugin->remove)) ||
479 false)
480 {
481 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "precompiling");
482 return GNUNET_SYSERR;
483 }
484 return GNUNET_OK;
485}
486
487
488/**
489 * Shutdown database connection and associate data
490 * structures.
491 *
492 * @param plugin the plugin context (state for this module)
493 */
494static void
495database_shutdown (struct Plugin *plugin)
496{
497 int result;
498
499#if SQLITE_VERSION_NUMBER >= 3007000
500 sqlite3_stmt *stmt;
501#endif
502
503 if (NULL != plugin->remove)
504 sqlite3_finalize (plugin->remove);
505 if (NULL != plugin->delRow)
506 sqlite3_finalize (plugin->delRow);
507 if (NULL != plugin->update)
508 sqlite3_finalize (plugin->update);
509 if (NULL != plugin->updRepl)
510 sqlite3_finalize (plugin->updRepl);
511 if (NULL != plugin->selRepl)
512 sqlite3_finalize (plugin->selRepl);
513 if (NULL != plugin->maxRepl)
514 sqlite3_finalize (plugin->maxRepl);
515 if (NULL != plugin->selExpi)
516 sqlite3_finalize (plugin->selExpi);
517 if (NULL != plugin->selZeroAnon)
518 sqlite3_finalize (plugin->selZeroAnon);
519 if (NULL != plugin->insertContent)
520 sqlite3_finalize (plugin->insertContent);
521 for (int i = 0; i < 8; ++i)
522 if (NULL != plugin->get[i])
523 sqlite3_finalize (plugin->get[i]);
524 result = sqlite3_close (plugin->dbh);
525#if SQLITE_VERSION_NUMBER >= 3007000
526 if (result == SQLITE_BUSY)
527 {
528 GNUNET_log_from (
529 GNUNET_ERROR_TYPE_WARNING,
530 "sqlite",
531 _ (
532 "Tried to close sqlite without finalizing all prepared statements.\n"));
533 stmt = sqlite3_next_stmt (plugin->dbh, NULL);
534 while (NULL != stmt)
535 {
536 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
537 "sqlite",
538 "Closing statement %p\n",
539 stmt);
540 result = sqlite3_finalize (stmt);
541 if (result != SQLITE_OK)
542 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
543 "sqlite",
544 "Failed to close statement %p: %d\n",
545 stmt,
546 result);
547 stmt = sqlite3_next_stmt (plugin->dbh, NULL);
548 }
549 result = sqlite3_close (plugin->dbh);
550 }
551#endif
552 if (SQLITE_OK != result)
553 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite3_close");
554 GNUNET_free (plugin->fn);
555}
556
557
558/**
559 * Delete the database entry with the given
560 * row identifier.
561 *
562 * @param plugin the plugin context (state for this module)
563 * @param rid the ID of the row to delete
564 */
565static int
566delete_by_rowid (struct Plugin *plugin, uint64_t rid)
567{
568 struct GNUNET_SQ_QueryParam params[] = { GNUNET_SQ_query_param_uint64 (&rid),
569 GNUNET_SQ_query_param_end };
570
571 if (GNUNET_OK != GNUNET_SQ_bind (plugin->delRow, params))
572 return GNUNET_SYSERR;
573 if (SQLITE_DONE != sqlite3_step (plugin->delRow))
574 {
575 LOG_SQLITE (plugin,
576 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
577 "sqlite3_step");
578 GNUNET_SQ_reset (plugin->dbh, plugin->delRow);
579 return GNUNET_SYSERR;
580 }
581 GNUNET_SQ_reset (plugin->dbh, plugin->delRow);
582 return GNUNET_OK;
583}
584
585
586/**
587 * Store an item in the datastore.
588 *
589 * @param cls closure
590 * @param key key for the item
591 * @param absent true if the key was not found in the bloom filter
592 * @param size number of bytes in @a data
593 * @param data content stored
594 * @param type type of the content
595 * @param priority priority of the content
596 * @param anonymity anonymity-level for the content
597 * @param replication replication-level for the content
598 * @param expiration expiration time for the content
599 * @param cont continuation called with success or failure status
600 * @param cont_cls continuation closure
601 */
602static void
603sqlite_plugin_put (void *cls,
604 const struct GNUNET_HashCode *key,
605 bool absent,
606 uint32_t size,
607 const void *data,
608 enum GNUNET_BLOCK_Type type,
609 uint32_t priority,
610 uint32_t anonymity,
611 uint32_t replication,
612 struct GNUNET_TIME_Absolute expiration,
613 PluginPutCont cont,
614 void *cont_cls)
615{
616 struct Plugin *plugin = cls;
617 struct GNUNET_HashCode vhash;
618 char *msg = NULL;
619
620 GNUNET_CRYPTO_hash (data, size, &vhash);
621
622 if (! absent)
623 {
624 struct GNUNET_SQ_QueryParam params[] =
625 { GNUNET_SQ_query_param_uint32 (&priority),
626 GNUNET_SQ_query_param_uint32 (&replication),
627 GNUNET_SQ_query_param_absolute_time (&expiration),
628 GNUNET_SQ_query_param_auto_from_type (key),
629 GNUNET_SQ_query_param_auto_from_type (&vhash),
630 GNUNET_SQ_query_param_end };
631
632 if (GNUNET_OK != GNUNET_SQ_bind (plugin->update, params))
633 {
634 cont (cont_cls, key, size, GNUNET_SYSERR, _ ("sqlite bind failure"));
635 return;
636 }
637 if (SQLITE_DONE != sqlite3_step (plugin->update))
638 {
639 LOG_SQLITE_MSG (plugin,
640 &msg,
641 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
642 "sqlite3_step");
643 cont (cont_cls, key, size, GNUNET_SYSERR, msg);
644 GNUNET_free (msg);
645 return;
646 }
647 int changes = sqlite3_changes (plugin->dbh);
648 GNUNET_SQ_reset (plugin->dbh, plugin->update);
649 if (0 != changes)
650 {
651 cont (cont_cls, key, size, GNUNET_NO, NULL);
652 return;
653 }
654 }
655
656 uint64_t rvalue;
657 uint32_t type32 = (uint32_t) type;
658 struct GNUNET_SQ_QueryParam params[] =
659 { GNUNET_SQ_query_param_uint32 (&replication),
660 GNUNET_SQ_query_param_uint32 (&type32),
661 GNUNET_SQ_query_param_uint32 (&priority),
662 GNUNET_SQ_query_param_uint32 (&anonymity),
663 GNUNET_SQ_query_param_absolute_time (&expiration),
664 GNUNET_SQ_query_param_uint64 (&rvalue),
665 GNUNET_SQ_query_param_auto_from_type (key),
666 GNUNET_SQ_query_param_auto_from_type (&vhash),
667 GNUNET_SQ_query_param_fixed_size (data, size),
668 GNUNET_SQ_query_param_end };
669 int n;
670 int ret;
671 sqlite3_stmt *stmt;
672
673 if (size > MAX_ITEM_SIZE)
674 {
675 cont (cont_cls, key, size, GNUNET_SYSERR, _ ("Data too large"));
676 return;
677 }
678 GNUNET_log_from (
679 GNUNET_ERROR_TYPE_DEBUG,
680 "sqlite",
681 "Storing in database block with type %u/key `%s'/priority %u/expiration in %s (%s).\n",
682 type,
683 GNUNET_h2s (key),
684 priority,
685 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (
686 expiration),
687 GNUNET_YES),
688 GNUNET_STRINGS_absolute_time_to_string (expiration));
689 stmt = plugin->insertContent;
690 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
691 if (GNUNET_OK != GNUNET_SQ_bind (stmt, params))
692 {
693 cont (cont_cls, key, size, GNUNET_SYSERR, NULL);
694 return;
695 }
696 n = sqlite3_step (stmt);
697 switch (n)
698 {
699 case SQLITE_DONE:
700 if (NULL != plugin->env->duc)
701 plugin->env->duc (plugin->env->cls,
702 size + GNUNET_DATASTORE_ENTRY_OVERHEAD);
703 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
704 "sqlite",
705 "Stored new entry (%u bytes)\n",
706 size + GNUNET_DATASTORE_ENTRY_OVERHEAD);
707 ret = GNUNET_OK;
708 break;
709
710 case SQLITE_BUSY:
711 GNUNET_break (0);
712 LOG_SQLITE_MSG (plugin,
713 &msg,
714 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
715 "sqlite3_step");
716 ret = GNUNET_SYSERR;
717 break;
718
719 default:
720 LOG_SQLITE_MSG (plugin,
721 &msg,
722 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
723 "sqlite3_step");
724 GNUNET_SQ_reset (plugin->dbh, stmt);
725 database_shutdown (plugin);
726 database_setup (plugin->env->cfg, plugin);
727 cont (cont_cls, key, size, GNUNET_SYSERR, msg);
728 GNUNET_free (msg);
729 return;
730 }
731 GNUNET_SQ_reset (plugin->dbh, stmt);
732 cont (cont_cls, key, size, ret, msg);
733 GNUNET_free (msg);
734}
735
736
737/**
738 * Execute statement that gets a row and call the callback
739 * with the result. Resets the statement afterwards.
740 *
741 * @param plugin the plugin
742 * @param stmt the statement
743 * @param proc processor to call
744 * @param proc_cls closure for @a proc
745 */
746static void
747execute_get (struct Plugin *plugin,
748 sqlite3_stmt *stmt,
749 PluginDatumProcessor proc,
750 void *proc_cls)
751{
752 int n;
753 struct GNUNET_TIME_Absolute expiration;
754 uint32_t replication;
755 uint32_t type;
756 uint32_t priority;
757 uint32_t anonymity;
758 uint64_t rowid;
759 void *value;
760 size_t value_size;
761 struct GNUNET_HashCode key;
762 int ret;
763 struct GNUNET_SQ_ResultSpec rs[] =
764 { GNUNET_SQ_result_spec_uint32 (&replication),
765 GNUNET_SQ_result_spec_uint32 (&type),
766 GNUNET_SQ_result_spec_uint32 (&priority),
767 GNUNET_SQ_result_spec_uint32 (&anonymity),
768 GNUNET_SQ_result_spec_absolute_time (&expiration),
769 GNUNET_SQ_result_spec_auto_from_type (&key),
770 GNUNET_SQ_result_spec_variable_size (&value, &value_size),
771 GNUNET_SQ_result_spec_uint64 (&rowid),
772 GNUNET_SQ_result_spec_end };
773
774 n = sqlite3_step (stmt);
775 switch (n)
776 {
777 case SQLITE_ROW:
778 if (GNUNET_OK != GNUNET_SQ_extract_result (stmt, rs))
779 {
780 GNUNET_break (0);
781 break;
782 }
783 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
784 "sqlite",
785 "Found reply in database with expiration %s\n",
786 GNUNET_STRINGS_absolute_time_to_string (expiration));
787 ret = proc (proc_cls,
788 &key,
789 value_size,
790 value,
791 type,
792 priority,
793 anonymity,
794 replication,
795 expiration,
796 rowid);
797 GNUNET_SQ_cleanup_result (rs);
798 GNUNET_SQ_reset (plugin->dbh, stmt);
799 if ((GNUNET_NO == ret) && (GNUNET_OK == delete_by_rowid (plugin, rowid)) &&
800 (NULL != plugin->env->duc))
801 plugin->env->duc (plugin->env->cls,
802 -(value_size + GNUNET_DATASTORE_ENTRY_OVERHEAD));
803 return;
804
805 case SQLITE_DONE:
806 /* database must be empty */
807 break;
808
809 case SQLITE_BUSY:
810 case SQLITE_ERROR:
811 case SQLITE_MISUSE:
812 default:
813 LOG_SQLITE (plugin,
814 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
815 "sqlite3_step");
816 if (SQLITE_OK != sqlite3_reset (stmt))
817 LOG_SQLITE (plugin,
818 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
819 "sqlite3_reset");
820 GNUNET_break (0);
821 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
822 database_shutdown (plugin);
823 database_setup (plugin->env->cfg, plugin);
824 return;
825 }
826 GNUNET_SQ_reset (plugin->dbh, stmt);
827 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
828}
829
830
831/**
832 * Select a subset of the items in the datastore and call
833 * the given processor for the item.
834 *
835 * @param cls our plugin context
836 * @param next_uid return the result with lowest uid >= next_uid
837 * @param type entries of which type should be considered?
838 * Must not be zero (ANY).
839 * @param proc function to call on the matching value;
840 * will be called with NULL if no value matches
841 * @param proc_cls closure for @a proc
842 */
843static void
844sqlite_plugin_get_zero_anonymity (void *cls,
845 uint64_t next_uid,
846 enum GNUNET_BLOCK_Type type,
847 PluginDatumProcessor proc,
848 void *proc_cls)
849{
850 struct Plugin *plugin = cls;
851 uint32_t type32 = type;
852 struct GNUNET_SQ_QueryParam params[] = { GNUNET_SQ_query_param_uint64 (
853 &next_uid),
854 GNUNET_SQ_query_param_uint32 (
855 &type32),
856 GNUNET_SQ_query_param_end };
857
858 GNUNET_assert (type != GNUNET_BLOCK_TYPE_ANY);
859 if (GNUNET_OK != GNUNET_SQ_bind (plugin->selZeroAnon, params))
860 {
861 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
862 return;
863 }
864 execute_get (plugin, plugin->selZeroAnon, proc, proc_cls);
865}
866
867
868/**
869 * Get results for a particular key in the datastore.
870 *
871 * @param cls closure
872 * @param next_uid return the result with lowest uid >= next_uid
873 * @param random if true, return a random result instead of using next_uid
874 * @param key maybe NULL (to match all entries)
875 * @param type entries of which type are relevant?
876 * Use 0 for any type.
877 * @param proc function to call on the matching value;
878 * will be called with NULL if nothing matches
879 * @param proc_cls closure for @a proc
880 */
881static void
882sqlite_plugin_get_key (void *cls,
883 uint64_t next_uid,
884 bool random,
885 const struct GNUNET_HashCode *key,
886 enum GNUNET_BLOCK_Type type,
887 PluginDatumProcessor proc,
888 void *proc_cls)
889{
890 struct Plugin *plugin = cls;
891 uint64_t rvalue;
892 int use_rvalue = random;
893 uint32_t type32 = (uint32_t) type;
894 int use_type = GNUNET_BLOCK_TYPE_ANY != type;
895 int use_key = NULL != key;
896 sqlite3_stmt *stmt = plugin->get[use_rvalue * 4 + use_key * 2 + use_type];
897 struct GNUNET_SQ_QueryParam params[] =
898 { GNUNET_SQ_query_param_uint64 (&next_uid),
899 GNUNET_SQ_query_param_uint64 (&rvalue),
900 GNUNET_SQ_query_param_auto_from_type (key),
901 GNUNET_SQ_query_param_uint32 (&type32),
902 GNUNET_SQ_query_param_end };
903
904 /* SQLite doesn't like it when you try to bind a parameter greater than the
905 * last numbered parameter, but unused parameters in the middle are OK.
906 */
907 if (! use_type)
908 {
909 params[3] = (struct GNUNET_SQ_QueryParam) GNUNET_SQ_query_param_end;
910 if (! use_key)
911 {
912 params[2] = (struct GNUNET_SQ_QueryParam) GNUNET_SQ_query_param_end;
913 if (! use_rvalue)
914 params[1] = (struct GNUNET_SQ_QueryParam) GNUNET_SQ_query_param_end;
915 }
916 }
917 if (random)
918 {
919 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
920 next_uid = 0;
921 }
922 else
923 rvalue = 0;
924
925 if (GNUNET_OK != GNUNET_SQ_bind (stmt, params))
926 {
927 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
928 return;
929 }
930 execute_get (plugin, stmt, proc, proc_cls);
931}
932
933
934/**
935 * Context for #repl_proc() function.
936 */
937struct ReplCtx
938{
939 /**
940 * Function to call for the result (or the NULL).
941 */
942 PluginDatumProcessor proc;
943
944 /**
945 * Closure for @e proc.
946 */
947 void *proc_cls;
948
949 /**
950 * UID to use.
951 */
952 uint64_t uid;
953
954 /**
955 * Yes if UID was set.
956 */
957 int have_uid;
958};
959
960
961/**
962 * Wrapper for the processor for #sqlite_plugin_get_replication().
963 * Decrements the replication counter and calls the original
964 * processor.
965 *
966 * @param cls closure
967 * @param key key for the content
968 * @param size number of bytes in @a data
969 * @param data content stored
970 * @param type type of the content
971 * @param priority priority of the content
972 * @param anonymity anonymity-level for the content
973 * @param replication replication-level for the content
974 * @param expiration expiration time for the content
975 * @param uid unique identifier for the datum;
976 * maybe 0 if no unique identifier is available
977 * @return #GNUNET_OK for normal return,
978 * #GNUNET_NO to delete the item
979 */
980static int
981repl_proc (void *cls,
982 const struct GNUNET_HashCode *key,
983 uint32_t size,
984 const void *data,
985 enum GNUNET_BLOCK_Type type,
986 uint32_t priority,
987 uint32_t anonymity,
988 uint32_t replication,
989 struct GNUNET_TIME_Absolute expiration,
990 uint64_t uid)
991{
992 struct ReplCtx *rc = cls;
993 int ret;
994
995 if (GNUNET_SYSERR == rc->have_uid)
996 rc->have_uid = GNUNET_NO;
997 ret = rc->proc (rc->proc_cls,
998 key,
999 size,
1000 data,
1001 type,
1002 priority,
1003 anonymity,
1004 replication,
1005 expiration,
1006 uid);
1007 if (NULL != key)
1008 {
1009 rc->uid = uid;
1010 rc->have_uid = GNUNET_YES;
1011 }
1012 return ret;
1013}
1014
1015
1016/**
1017 * Get a random item for replication. Returns a single random item
1018 * from those with the highest replication counters. The item's
1019 * replication counter is decremented by one IF it was positive before.
1020 * Call @a proc with all values ZERO or NULL if the datastore is empty.
1021 *
1022 * @param cls closure
1023 * @param proc function to call the value (once only).
1024 * @param proc_cls closure for @a proc
1025 */
1026static void
1027sqlite_plugin_get_replication (void *cls,
1028 PluginDatumProcessor proc,
1029 void *proc_cls)
1030{
1031 struct Plugin *plugin = cls;
1032 struct ReplCtx rc;
1033 uint64_t rvalue = 0;
1034 uint32_t repl;
1035 struct GNUNET_SQ_QueryParam params_sel_repl[] =
1036 { GNUNET_SQ_query_param_uint64 (&rvalue),
1037 GNUNET_SQ_query_param_uint32 (&repl),
1038 GNUNET_SQ_query_param_end };
1039 struct GNUNET_SQ_QueryParam params_upd_repl[] =
1040 { GNUNET_SQ_query_param_uint64 (&rc.uid), GNUNET_SQ_query_param_end };
1041
1042 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1043 "datastore-sqlite",
1044 "Getting random block based on replication order.\n");
1045 if (SQLITE_ROW != sqlite3_step (plugin->maxRepl))
1046 {
1047 GNUNET_SQ_reset (plugin->dbh, plugin->maxRepl);
1048 /* DB empty */
1049 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1050 return;
1051 }
1052 repl = sqlite3_column_int (plugin->maxRepl, 0);
1053 GNUNET_SQ_reset (plugin->dbh, plugin->maxRepl);
1054 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
1055 if (GNUNET_OK != GNUNET_SQ_bind (plugin->selRepl, params_sel_repl))
1056 {
1057 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1058 return;
1059 }
1060 rc.have_uid = GNUNET_SYSERR;
1061 rc.proc = proc;
1062 rc.proc_cls = proc_cls;
1063 execute_get (plugin, plugin->selRepl, &repl_proc, &rc);
1064 if (GNUNET_YES == rc.have_uid)
1065 {
1066 if (GNUNET_OK != GNUNET_SQ_bind (plugin->updRepl, params_upd_repl))
1067 {
1068 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1069 return;
1070 }
1071 if (SQLITE_DONE != sqlite3_step (plugin->updRepl))
1072 LOG_SQLITE (plugin,
1073 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1074 "sqlite3_step");
1075 GNUNET_SQ_reset (plugin->dbh, plugin->updRepl);
1076 }
1077 if (GNUNET_SYSERR == rc.have_uid)
1078 {
1079 /* proc was not called at all so far, do it now. */
1080 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1081 }
1082}
1083
1084
1085/**
1086 * Get a random item that has expired or has low priority.
1087 * Call @a proc with all values ZERO or NULL if the datastore is empty.
1088 *
1089 * @param cls closure
1090 * @param proc function to call the value (once only).
1091 * @param proc_cls closure for @a proc
1092 */
1093static void
1094sqlite_plugin_get_expiration (void *cls,
1095 PluginDatumProcessor proc,
1096 void *proc_cls)
1097{
1098 struct Plugin *plugin = cls;
1099 sqlite3_stmt *stmt;
1100 struct GNUNET_TIME_Absolute now = { 0 };
1101 struct GNUNET_SQ_QueryParam params[] = { GNUNET_SQ_query_param_absolute_time (
1102 &now),
1103 GNUNET_SQ_query_param_end };
1104
1105 GNUNET_log_from (
1106 GNUNET_ERROR_TYPE_DEBUG,
1107 "sqlite",
1108 "Getting random block based on expiration and priority order.\n");
1109 now = GNUNET_TIME_absolute_get ();
1110 stmt = plugin->selExpi;
1111 if (GNUNET_OK != GNUNET_SQ_bind (stmt, params))
1112 {
1113 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1114 return;
1115 }
1116 execute_get (plugin, stmt, proc, proc_cls);
1117}
1118
1119
1120/**
1121 * Get all of the keys in the datastore.
1122 *
1123 * @param cls closure
1124 * @param proc function to call on each key
1125 * @param proc_cls closure for @a proc
1126 */
1127static void
1128sqlite_plugin_get_keys (void *cls, PluginKeyProcessor proc, void *proc_cls)
1129{
1130 struct Plugin *plugin = cls;
1131 struct GNUNET_HashCode key;
1132 struct GNUNET_SQ_ResultSpec results[] =
1133 { GNUNET_SQ_result_spec_auto_from_type (&key), GNUNET_SQ_result_spec_end };
1134 sqlite3_stmt *stmt;
1135 int ret;
1136
1137 GNUNET_assert (NULL != proc);
1138 if (SQLITE_OK != sq_prepare (plugin->dbh, "SELECT hash FROM gn091", &stmt))
1139 {
1140 LOG_SQLITE (plugin,
1141 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1142 "sqlite_prepare");
1143 proc (proc_cls, NULL, 0);
1144 return;
1145 }
1146 while (SQLITE_ROW == (ret = sqlite3_step (stmt)))
1147 {
1148 if (GNUNET_OK == GNUNET_SQ_extract_result (stmt, results))
1149 proc (proc_cls, &key, 1);
1150 else
1151 GNUNET_break (0);
1152 }
1153 if (SQLITE_DONE != ret)
1154 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite_step");
1155 sqlite3_finalize (stmt);
1156 proc (proc_cls, NULL, 0);
1157}
1158
1159
1160/**
1161 * Drop database.
1162 *
1163 * @param cls our plugin context
1164 */
1165static void
1166sqlite_plugin_drop (void *cls)
1167{
1168 struct Plugin *plugin = cls;
1169
1170 plugin->drop_on_shutdown = GNUNET_YES;
1171}
1172
1173
1174/**
1175 * Remove a particular key in the datastore.
1176 *
1177 * @param cls closure
1178 * @param key key for the content
1179 * @param size number of bytes in data
1180 * @param data content stored
1181 * @param cont continuation called with success or failure status
1182 * @param cont_cls continuation closure for @a cont
1183 */
1184static void
1185sqlite_plugin_remove_key (void *cls,
1186 const struct GNUNET_HashCode *key,
1187 uint32_t size,
1188 const void *data,
1189 PluginRemoveCont cont,
1190 void *cont_cls)
1191{
1192 struct Plugin *plugin = cls;
1193 struct GNUNET_SQ_QueryParam params[] =
1194 { GNUNET_SQ_query_param_auto_from_type (key),
1195 GNUNET_SQ_query_param_fixed_size (data, size),
1196 GNUNET_SQ_query_param_end };
1197
1198 if (GNUNET_OK != GNUNET_SQ_bind (plugin->remove, params))
1199 {
1200 cont (cont_cls, key, size, GNUNET_SYSERR, "bind failed");
1201 return;
1202 }
1203 if (SQLITE_DONE != sqlite3_step (plugin->remove))
1204 {
1205 LOG_SQLITE (plugin,
1206 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1207 "sqlite3_step");
1208 GNUNET_SQ_reset (plugin->dbh, plugin->remove);
1209 cont (cont_cls, key, size, GNUNET_SYSERR, "sqlite3_step failed");
1210 return;
1211 }
1212 int changes = sqlite3_changes (plugin->dbh);
1213 GNUNET_SQ_reset (plugin->dbh, plugin->remove);
1214 if (0 == changes)
1215 {
1216 cont (cont_cls, key, size, GNUNET_NO, NULL);
1217 return;
1218 }
1219 if (NULL != plugin->env->duc)
1220 plugin->env->duc (plugin->env->cls,
1221 -(size + GNUNET_DATASTORE_ENTRY_OVERHEAD));
1222 cont (cont_cls, key, size, GNUNET_OK, NULL);
1223}
1224
1225
1226/**
1227 * Get an estimate of how much space the database is
1228 * currently using.
1229 *
1230 * @param cls the `struct Plugin`
1231 * @return the size of the database on disk (estimate)
1232 */
1233static void
1234sqlite_plugin_estimate_size (void *cls, unsigned long long *estimate)
1235{
1236 struct Plugin *plugin = cls;
1237 sqlite3_stmt *stmt;
1238 uint64_t pages;
1239 uint64_t page_size;
1240
1241#if ENULL_DEFINED
1242 char *e;
1243#endif
1244
1245 if (NULL == estimate)
1246 return;
1247 if (SQLITE_VERSION_NUMBER < 3006000)
1248 {
1249 GNUNET_log_from (
1250 GNUNET_ERROR_TYPE_WARNING,
1251 "datastore-sqlite",
1252 _ ("sqlite version to old to determine size, assuming zero\n"));
1253 *estimate = 0;
1254 return;
1255 }
1256 CHECK (SQLITE_OK == sqlite3_exec (plugin->dbh, "VACUUM", NULL, NULL, ENULL));
1257 CHECK (SQLITE_OK == sqlite3_exec (plugin->dbh,
1258 "PRAGMA auto_vacuum=INCREMENTAL",
1259 NULL,
1260 NULL,
1261 ENULL));
1262 if (SQLITE_OK != sq_prepare (plugin->dbh, "PRAGMA page_count", &stmt))
1263 {
1264 GNUNET_log_from (
1265 GNUNET_ERROR_TYPE_WARNING,
1266 "datastore-sqlite",
1267 _("error preparing statement\n"));
1268 return;
1269 }
1270 if (SQLITE_ROW == sqlite3_step (stmt))
1271 pages = sqlite3_column_int64 (stmt, 0);
1272 else
1273 pages = 0;
1274 sqlite3_finalize (stmt);
1275 if (SQLITE_OK != sq_prepare (plugin->dbh, "PRAGMA page_size", &stmt))
1276 {
1277 GNUNET_log_from (
1278 GNUNET_ERROR_TYPE_WARNING,
1279 "datastore-sqlite",
1280 _("error preparing statement\n"));
1281 return;
1282 }
1283 if (SQLITE_ROW != sqlite3_step (stmt))
1284 {
1285 GNUNET_log_from (
1286 GNUNET_ERROR_TYPE_WARNING,
1287 "datastore-sqlite",
1288 _("error stepping\n"));
1289 return;
1290 }
1291 page_size = sqlite3_column_int64 (stmt, 0);
1292 sqlite3_finalize (stmt);
1293 GNUNET_log (
1294 GNUNET_ERROR_TYPE_INFO,
1295 _ (
1296 "Using sqlite page utilization to estimate payload (%llu pages of size %llu bytes)\n"),
1297 (unsigned long long) pages,
1298 (unsigned long long) page_size);
1299 *estimate = pages * page_size;
1300}
1301
1302
1303/**
1304 * Entry point for the plugin.
1305 *
1306 * @param cls the `struct GNUNET_DATASTORE_PluginEnvironment *`
1307 * @return NULL on error, othrewise the plugin context
1308 */
1309void *
1310libgnunet_plugin_datastore_sqlite_init (void *cls)
1311{
1312 static struct Plugin plugin;
1313 struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
1314 struct GNUNET_DATASTORE_PluginFunctions *api;
1315
1316 if (NULL != plugin.env)
1317 return NULL; /* can only initialize once! */
1318 memset (&plugin, 0, sizeof(struct Plugin));
1319 plugin.env = env;
1320 if (GNUNET_OK != database_setup (env->cfg, &plugin))
1321 {
1322 database_shutdown (&plugin);
1323 return NULL;
1324 }
1325 api = GNUNET_new (struct GNUNET_DATASTORE_PluginFunctions);
1326 api->cls = &plugin;
1327 api->estimate_size = &sqlite_plugin_estimate_size;
1328 api->put = &sqlite_plugin_put;
1329 api->get_key = &sqlite_plugin_get_key;
1330 api->get_replication = &sqlite_plugin_get_replication;
1331 api->get_expiration = &sqlite_plugin_get_expiration;
1332 api->get_zero_anonymity = &sqlite_plugin_get_zero_anonymity;
1333 api->get_keys = &sqlite_plugin_get_keys;
1334 api->drop = &sqlite_plugin_drop;
1335 api->remove_key = &sqlite_plugin_remove_key;
1336 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1337 "sqlite",
1338 _ ("Sqlite database running\n"));
1339 return api;
1340}
1341
1342
1343/**
1344 * Exit point from the plugin.
1345 *
1346 * @param cls the plugin context (as returned by "init")
1347 * @return always NULL
1348 */
1349void *
1350libgnunet_plugin_datastore_sqlite_done (void *cls)
1351{
1352 char *fn;
1353 struct GNUNET_DATASTORE_PluginFunctions *api = cls;
1354 struct Plugin *plugin = api->cls;
1355
1356 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1357 "sqlite",
1358 "sqlite plugin is done\n");
1359 fn = NULL;
1360 if (plugin->drop_on_shutdown)
1361 fn = GNUNET_strdup (plugin->fn);
1362 database_shutdown (plugin);
1363 plugin->env = NULL;
1364 GNUNET_free (api);
1365 if (NULL != fn)
1366 {
1367 if (0 != unlink (fn))
1368 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
1369 GNUNET_free (fn);
1370 }
1371 return NULL;
1372}
1373
1374
1375/* end of plugin_datastore_sqlite.c */
diff --git a/src/plugin/datastore/plugin_datastore_template.c b/src/plugin/datastore/plugin_datastore_template.c
new file mode 100644
index 000000000..2b455f8cb
--- /dev/null
+++ b/src/plugin/datastore/plugin_datastore_template.c
@@ -0,0 +1,274 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2009, 2011 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 datastore/plugin_datastore_template.c
23 * @brief template-based datastore backend
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_datastore_plugin.h"
29
30
31/**
32 * Context for all functions in this plugin.
33 */
34struct Plugin
35{
36 /**
37 * Our execution environment.
38 */
39 struct GNUNET_DATASTORE_PluginEnvironment *env;
40};
41
42
43/**
44 * Get an estimate of how much space the database is
45 * currently using.
46 *
47 * @param cls our "struct Plugin*"
48 * @return number of bytes used on disk
49 */
50static void
51template_plugin_estimate_size (void *cls, unsigned long long *estimate)
52{
53 if (NULL == estimate)
54 return;
55 GNUNET_break (0);
56 *estimate = 0;
57}
58
59
60/**
61 * Store an item in the datastore.
62 *
63 * @param cls closure
64 * @param key key for the item
65 * @param absent true if the key was not found in the bloom filter
66 * @param size number of bytes in data
67 * @param data content stored
68 * @param type type of the content
69 * @param priority priority of the content
70 * @param anonymity anonymity-level for the content
71 * @param replication replication-level for the content
72 * @param expiration expiration time for the content
73 * @param cont continuation called with success or failure status
74 * @param cont_cls continuation closure
75 */
76static void
77template_plugin_put (void *cls,
78 const struct GNUNET_HashCode *key,
79 bool absent,
80 uint32_t size,
81 const void *data,
82 enum GNUNET_BLOCK_Type type,
83 uint32_t priority,
84 uint32_t anonymity,
85 uint32_t replication,
86 struct GNUNET_TIME_Absolute expiration,
87 PluginPutCont cont,
88 void *cont_cls)
89{
90 GNUNET_break (0);
91 cont (cont_cls, key, size, GNUNET_SYSERR, "not implemented");
92}
93
94
95/**
96 * Get one of the results for a particular key in the datastore.
97 *
98 * @param cls closure
99 * @param next_uid return the result with lowest uid >= next_uid
100 * @param random if true, return a random result instead of using next_uid
101 * @param key maybe NULL (to match all entries)
102 * @param type entries of which type are relevant?
103 * Use 0 for any type.
104 * @param proc function to call on each matching value;
105 * will be called with NULL if nothing matches
106 * @param proc_cls closure for proc
107 */
108static void
109template_plugin_get_key (void *cls,
110 uint64_t next_uid,
111 bool random,
112 const struct GNUNET_HashCode *key,
113 enum GNUNET_BLOCK_Type type,
114 PluginDatumProcessor proc,
115 void *proc_cls)
116{
117 GNUNET_break (0);
118}
119
120
121/**
122 * Get a random item for replication. Returns a single, not expired,
123 * random item from those with the highest replication counters. The
124 * item's replication counter is decremented by one IF it was positive
125 * before. Call 'proc' with all values ZERO or NULL if the datastore
126 * is empty.
127 *
128 * @param cls closure
129 * @param proc function to call the value (once only).
130 * @param proc_cls closure for proc
131 */
132static void
133template_plugin_get_replication (void *cls, PluginDatumProcessor proc,
134 void *proc_cls)
135{
136 GNUNET_break (0);
137}
138
139
140/**
141 * Get a random item for expiration. Call 'proc' with all values ZERO
142 * or NULL if the datastore is empty.
143 *
144 * @param cls closure
145 * @param proc function to call the value (once only).
146 * @param proc_cls closure for proc
147 */
148static void
149template_plugin_get_expiration (void *cls, PluginDatumProcessor proc,
150 void *proc_cls)
151{
152 GNUNET_break (0);
153}
154
155
156/**
157 * Call the given processor on an item with zero anonymity.
158 *
159 * @param cls our "struct Plugin*"
160 * @param next_uid return the result with lowest uid >= next_uid
161 * @param type entries of which type should be considered?
162 * Must not be zero (ANY).
163 * @param proc function to call on the matching value;
164 * will be called with NULL if no value matches
165 * @param proc_cls closure for proc
166 */
167static void
168template_plugin_get_zero_anonymity (void *cls, uint64_t next_uid,
169 enum GNUNET_BLOCK_Type type,
170 PluginDatumProcessor proc, void *proc_cls)
171{
172 GNUNET_break (0);
173}
174
175
176/**
177 * Drop database.
178 */
179static void
180template_plugin_drop (void *cls)
181{
182 GNUNET_break (0);
183}
184
185
186/**
187 * Get all of the keys in the datastore.
188 *
189 * @param cls closure
190 * @param proc function to call on each key
191 * @param proc_cls closure for proc
192 */
193static void
194template_get_keys (void *cls,
195 PluginKeyProcessor proc,
196 void *proc_cls)
197{
198 proc (proc_cls, NULL, 0);
199}
200
201
202/**
203 * Remove a particular key in the datastore.
204 *
205 * @param cls closure
206 * @param key key for the content
207 * @param size number of bytes in data
208 * @param data content stored
209 * @param cont continuation called with success or failure status
210 * @param cont_cls continuation closure for @a cont
211 */
212static void
213template_plugin_remove_key (void *cls,
214 const struct GNUNET_HashCode *key,
215 uint32_t size,
216 const void *data,
217 PluginRemoveCont cont,
218 void *cont_cls)
219{
220 GNUNET_break (0);
221 cont (cont_cls, key, size, GNUNET_SYSERR, "not implemented");
222}
223
224
225/**
226 * Entry point for the plugin.
227 *
228 * @param cls the "struct GNUNET_DATASTORE_PluginEnvironment*"
229 * @return our "struct Plugin*"
230 */
231void *
232libgnunet_plugin_datastore_template_init (void *cls)
233{
234 struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
235 struct GNUNET_DATASTORE_PluginFunctions *api;
236 struct Plugin *plugin;
237
238 plugin = GNUNET_new (struct Plugin);
239 plugin->env = env;
240 api = GNUNET_new (struct GNUNET_DATASTORE_PluginFunctions);
241 api->cls = plugin;
242 api->estimate_size = &template_plugin_estimate_size;
243 api->put = &template_plugin_put;
244 api->get_key = &template_plugin_get_key;
245 api->get_replication = &template_plugin_get_replication;
246 api->get_expiration = &template_plugin_get_expiration;
247 api->get_zero_anonymity = &template_plugin_get_zero_anonymity;
248 api->drop = &template_plugin_drop;
249 api->get_keys = &template_get_keys;
250 api->remove_key = &template_plugin_remove_key;
251 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "template",
252 _ ("Template database running\n"));
253 return api;
254}
255
256
257/**
258 * Exit point from the plugin.
259 * @param cls our "struct Plugin*"
260 * @return always NULL
261 */
262void *
263libgnunet_plugin_datastore_template_done (void *cls)
264{
265 struct GNUNET_DATASTORE_PluginFunctions *api = cls;
266 struct Plugin *plugin = api->cls;
267
268 GNUNET_free (plugin);
269 GNUNET_free (api);
270 return NULL;
271}
272
273
274/* end of plugin_datastore_template.c */
diff --git a/src/plugin/datastore/test_plugin_datastore.c b/src/plugin/datastore/test_plugin_datastore.c
new file mode 100644
index 000000000..7de1acf2d
--- /dev/null
+++ b/src/plugin/datastore/test_plugin_datastore.c
@@ -0,0 +1,478 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011 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 test_plugin_datastore.c
22 * @brief Test database plugin directly, calling each API function once
23 * @author Christian Grothoff
24 */
25
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_protocols.h"
29#include "gnunet_datastore_plugin.h"
30#include "gnunet_testing_lib.h"
31
32/**
33 * Number of put operations to perform.
34 */
35#define PUT_10 10
36
37static unsigned long long stored_bytes;
38
39static unsigned long long stored_entries;
40
41static unsigned long long stored_ops;
42
43static const char *plugin_name;
44
45static int ok;
46
47enum RunPhase
48{
49 RP_ERROR = 0,
50 RP_PUT,
51 RP_GET,
52 RP_ITER_ZERO,
53 RP_REPL_GET,
54 RP_EXPI_GET,
55 RP_REMOVE,
56 RP_DROP
57};
58
59
60struct CpsRunContext
61{
62 const struct GNUNET_CONFIGURATION_Handle *cfg;
63 struct GNUNET_DATASTORE_PluginFunctions *api;
64 enum RunPhase phase;
65 unsigned int cnt;
66 unsigned int i;
67};
68
69
70/**
71 * Function called by plugins to notify us about a
72 * change in their disk utilization.
73 *
74 * @param cls closure (NULL)
75 * @param delta change in disk utilization,
76 * 0 for "reset to empty"
77 */
78static void
79disk_utilization_change_cb (void *cls, int delta)
80{
81 /* do nothing */
82}
83
84
85static void
86test (void *cls);
87
88
89/**
90 * Put continuation.
91 *
92 * @param cls closure
93 * @param key key for the item stored
94 * @param size size of the item stored
95 * @param status #GNUNET_OK or #GNUNET_SYSERROR
96 * @param msg error message on error
97 */
98static void
99put_continuation (void *cls,
100 const struct GNUNET_HashCode *key,
101 uint32_t size,
102 int status,
103 const char *msg)
104{
105 struct CpsRunContext *crc = cls;
106 static unsigned long long os;
107 unsigned long long cs;
108
109 if (GNUNET_OK != status)
110 {
111 fprintf (stderr,
112 "ERROR: `%s'\n",
113 msg);
114 }
115 else
116 {
117 crc->api->estimate_size (crc->api->cls,
118 &cs);
119 GNUNET_assert (os <= cs);
120 os = cs;
121 stored_bytes += size;
122 stored_ops++;
123 stored_entries++;
124 }
125 GNUNET_SCHEDULER_add_now (&test, crc);
126}
127
128
129static void
130gen_key (int i, struct GNUNET_HashCode *key)
131{
132 memset (key, 0, sizeof(struct GNUNET_HashCode));
133 key->bits[0] = (unsigned int) i;
134 GNUNET_CRYPTO_hash (key, sizeof(struct GNUNET_HashCode), key);
135}
136
137
138static void
139do_put (struct CpsRunContext *crc)
140{
141 char value[65536];
142 size_t size;
143 struct GNUNET_HashCode key;
144 unsigned int prio;
145 static int i;
146
147 if (PUT_10 == i)
148 {
149 i = 0;
150 crc->phase++;
151 GNUNET_SCHEDULER_add_now (&test, crc);
152 return;
153 }
154 /* most content is 32k */
155 size = 32 * 1024;
156
157 if ((0 != i) && (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16) ==
158 0) ) /* but some of it is less! */
159 size = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 32 * 1024);
160 size = size - (size & 7); /* always multiple of 8 */
161
162 /* generate random key */
163 gen_key (i, &key);
164 memset (value, i, size);
165 if (i > 255)
166 memset (value, i - 255, size / 2);
167 value[0] = crc->i;
168 prio = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100);
169 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
170 "putting type %u, anon %u under key %s\n", i + 1, i,
171 GNUNET_h2s (&key));
172 crc->api->put (crc->api->cls,
173 &key,
174 false /* absent */,
175 size,
176 value, i + 1 /* type */,
177 prio,
178 i /* anonymity */,
179 0 /* replication */,
180 GNUNET_TIME_relative_to_absolute
181 (GNUNET_TIME_relative_multiply
182 (GNUNET_TIME_UNIT_MILLISECONDS,
183 60 * 60 * 60 * 1000
184 + GNUNET_CRYPTO_random_u32
185 (GNUNET_CRYPTO_QUALITY_WEAK, 1000))),
186 put_continuation,
187 crc);
188 i++;
189}
190
191
192static uint64_t guid;
193
194
195static int
196iterate_one_shot (void *cls,
197 const struct GNUNET_HashCode *key,
198 uint32_t size,
199 const void *data,
200 enum GNUNET_BLOCK_Type type,
201 uint32_t priority,
202 uint32_t anonymity,
203 uint32_t replication,
204 struct GNUNET_TIME_Absolute expiration,
205 uint64_t uid)
206{
207 struct CpsRunContext *crc = cls;
208
209 GNUNET_assert (NULL != key);
210 guid = uid;
211 crc->phase++;
212 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
213 "Found result type=%u, priority=%u, size=%u, expire=%s, key %s\n",
214 (unsigned int) type,
215 (unsigned int) priority,
216 (unsigned int) size,
217 GNUNET_STRINGS_absolute_time_to_string (expiration),
218 GNUNET_h2s (key));
219 GNUNET_SCHEDULER_add_now (&test,
220 crc);
221 return GNUNET_OK;
222}
223
224
225static void
226remove_continuation (void *cls,
227 const struct GNUNET_HashCode *key,
228 uint32_t size,
229 int status,
230 const char *msg)
231{
232 struct CpsRunContext *crc = cls;
233
234 GNUNET_assert (NULL != key);
235 GNUNET_assert (32768 == size);
236 GNUNET_assert (GNUNET_OK == status);
237 GNUNET_assert (NULL == msg);
238 crc->phase++;
239 GNUNET_SCHEDULER_add_now (&test,
240 crc);
241}
242
243
244/**
245 * Function called when the service shuts
246 * down. Unloads our datastore plugin.
247 *
248 * @param api api to unload
249 * @param cfg configuration to use
250 */
251static void
252unload_plugin (struct GNUNET_DATASTORE_PluginFunctions *api,
253 const struct GNUNET_CONFIGURATION_Handle *cfg)
254{
255 char *name;
256 char *libname;
257
258 if (GNUNET_OK !=
259 GNUNET_CONFIGURATION_get_value_string (cfg,
260 "DATASTORE",
261 "DATABASE",
262 &name))
263 {
264 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
265 _ ("No `%s' specified for `%s' in configuration!\n"),
266 "DATABASE",
267 "DATASTORE");
268 return;
269 }
270 GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
271 GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api));
272 GNUNET_free (libname);
273 GNUNET_free (name);
274}
275
276
277/**
278 * Last task run during shutdown. Disconnects us from
279 * the transport and core.
280 */
281static void
282cleaning_task (void *cls)
283{
284 struct CpsRunContext *crc = cls;
285
286 unload_plugin (crc->api, crc->cfg);
287 GNUNET_free (crc);
288}
289
290
291static void
292test (void *cls)
293{
294 struct CpsRunContext *crc = cls;
295 struct GNUNET_HashCode key;
296
297 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
298 "In phase %d, iteration %u\n", crc->phase, crc->cnt);
299 switch (crc->phase)
300 {
301 case RP_ERROR:
302 ok = 1;
303 GNUNET_break (0);
304 crc->api->drop (crc->api->cls);
305 GNUNET_SCHEDULER_add_now (&cleaning_task, crc);
306 break;
307
308 case RP_PUT:
309 do_put (crc);
310 break;
311
312 case RP_GET:
313 if (crc->cnt == 1)
314 {
315 crc->cnt = 0;
316 crc->phase++;
317 GNUNET_SCHEDULER_add_now (&test, crc);
318 break;
319 }
320 gen_key (5, &key);
321 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
322 "Looking for %s\n",
323 GNUNET_h2s (&key));
324 crc->api->get_key (crc->api->cls,
325 0,
326 false,
327 &key,
328 GNUNET_BLOCK_TYPE_ANY,
329 &iterate_one_shot,
330 crc);
331 break;
332
333 case RP_ITER_ZERO:
334 if (crc->cnt == 1)
335 {
336 crc->cnt = 0;
337 crc->phase++;
338 GNUNET_SCHEDULER_add_now (&test, crc);
339 break;
340 }
341 crc->api->get_zero_anonymity (crc->api->cls, 0, 1, &iterate_one_shot, crc);
342 break;
343
344 case RP_REPL_GET:
345 crc->api->get_replication (crc->api->cls, &iterate_one_shot, crc);
346 break;
347
348 case RP_EXPI_GET:
349 crc->api->get_expiration (crc->api->cls, &iterate_one_shot, crc);
350 break;
351
352 case RP_REMOVE:
353 {
354 struct GNUNET_HashCode key;
355 uint32_t size = 32768;
356 char value[size];
357
358 gen_key (0, &key);
359 memset (value, 0, size);
360 value[0] = crc->i;
361 crc->api->remove_key (crc->api->cls,
362 &key,
363 size,
364 value,
365 &remove_continuation,
366 crc);
367 break;
368 }
369
370 case RP_DROP:
371 crc->api->drop (crc->api->cls);
372 GNUNET_SCHEDULER_add_now (&cleaning_task, crc);
373 break;
374 }
375}
376
377
378/**
379 * Load the datastore plugin.
380 */
381static struct GNUNET_DATASTORE_PluginFunctions *
382load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg)
383{
384 static struct GNUNET_DATASTORE_PluginEnvironment env;
385 struct GNUNET_DATASTORE_PluginFunctions *ret;
386 char *name;
387 char *libname;
388
389 if (GNUNET_OK !=
390 GNUNET_CONFIGURATION_get_value_string (cfg,
391 "DATASTORE",
392 "DATABASE",
393 &name))
394 {
395 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
396 _ ("No `%s' specified for `%s' in configuration!\n"),
397 "DATABASE",
398 "DATASTORE");
399 return NULL;
400 }
401 env.cfg = cfg;
402 env.duc = &disk_utilization_change_cb;
403 env.cls = NULL;
404 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Loading `%s' datastore plugin\n"),
405 name);
406 GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
407 if (NULL == (ret = GNUNET_PLUGIN_load (libname, &env)))
408 {
409 fprintf (stderr, "Failed to load plugin `%s'!\n", name);
410 GNUNET_free (libname);
411 GNUNET_free (name);
412 ok = 77; /* mark test as skipped */
413 return NULL;
414 }
415 GNUNET_free (libname);
416 GNUNET_free (name);
417 return ret;
418}
419
420
421static void
422run (void *cls, char *const *args, const char *cfgfile,
423 const struct GNUNET_CONFIGURATION_Handle *c)
424{
425 struct GNUNET_DATASTORE_PluginFunctions *api;
426 struct CpsRunContext *crc;
427
428 api = load_plugin (c);
429 if (api == NULL)
430 {
431 fprintf (stderr,
432 "%s",
433 "Could not initialize plugin, assuming database not configured. Test not run!\n");
434 return;
435 }
436 crc = GNUNET_new (struct CpsRunContext);
437 crc->api = api;
438 crc->cfg = c;
439 crc->phase = RP_PUT;
440 GNUNET_SCHEDULER_add_now (&test, crc);
441}
442
443
444int
445main (int argc, char *argv[])
446{
447 char dir_name[PATH_MAX];
448 char cfg_name[PATH_MAX];
449 char *const xargv[] = {
450 "test-plugin-datastore",
451 "-c",
452 cfg_name,
453 NULL
454 };
455 static struct GNUNET_GETOPT_CommandLineOption options[] = {
456 GNUNET_GETOPT_OPTION_END
457 };
458
459 /* determine name of plugin to use */
460 plugin_name = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]);
461 GNUNET_snprintf (dir_name, sizeof(dir_name),
462 "/tmp/test-gnunet-datastore-plugin-%s", plugin_name);
463 GNUNET_DISK_directory_remove (dir_name);
464 GNUNET_log_setup ("test-plugin-datastore",
465 "WARNING",
466 NULL);
467 GNUNET_snprintf (cfg_name, sizeof(cfg_name),
468 "test_plugin_datastore_data_%s.conf", plugin_name);
469 GNUNET_PROGRAM_run ((sizeof(xargv) / sizeof(char *)) - 1, xargv,
470 "test-plugin-datastore", "nohelp", options, &run, NULL);
471 if ((0 != ok) && (77 != ok))
472 fprintf (stderr, "Missed some testcases: %u\n", ok);
473 GNUNET_DISK_directory_remove (dir_name);
474 return ok;
475}
476
477
478/* end of test_plugin_datastore.c */
diff --git a/src/plugin/datastore/test_plugin_datastore_data_heap.conf b/src/plugin/datastore/test_plugin_datastore_data_heap.conf
new file mode 100644
index 000000000..b1ea8ff67
--- /dev/null
+++ b/src/plugin/datastore/test_plugin_datastore_data_heap.conf
@@ -0,0 +1,6 @@
1@INLINE@ test_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-datastore-plugin-heap/
4
5[datastore]
6DATABASE = heap
diff --git a/src/plugin/datastore/test_plugin_datastore_data_postgres.conf b/src/plugin/datastore/test_plugin_datastore_data_postgres.conf
new file mode 100644
index 000000000..d0e29437f
--- /dev/null
+++ b/src/plugin/datastore/test_plugin_datastore_data_postgres.conf
@@ -0,0 +1,10 @@
1@INLINE@ test_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-datastore-plugin-postgres/
4
5[datastore]
6DATABASE = postgres
7
8[datastore-postgres]
9CONFIG = dbname=gnunetcheck
10
diff --git a/src/plugin/datastore/test_plugin_datastore_data_sqlite.conf b/src/plugin/datastore/test_plugin_datastore_data_sqlite.conf
new file mode 100644
index 000000000..ca837c77a
--- /dev/null
+++ b/src/plugin/datastore/test_plugin_datastore_data_sqlite.conf
@@ -0,0 +1,4 @@
1@INLINE@ test_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-datastore-plugin-sqlite/
4
diff --git a/src/plugin/dht/Makefile.am b/src/plugin/dht/Makefile.am
new file mode 100644
index 000000000..4df810066
--- /dev/null
+++ b/src/plugin/dht/Makefile.am
@@ -0,0 +1,27 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4plugindir = $(libdir)/gnunet
5
6pkgcfgdir= $(pkgdatadir)/config.d/
7
8libexecdir= $(pkglibdir)/libexec/
9
10if USE_COVERAGE
11 AM_CFLAGS = --coverage -O0
12 XLIB = -lgcov
13endif
14
15plugin_LTLIBRARIES = \
16 libgnunet_plugin_block_dht.la
17
18libgnunet_plugin_block_dht_la_SOURCES = \
19 plugin_block_dht.c
20libgnunet_plugin_block_dht_la_LIBADD = \
21 $(top_builddir)/src/lib/hello/libgnunethello.la \
22 $(top_builddir)/src/lib/block/libgnunetblock.la \
23 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \
24 $(top_builddir)/src/lib/util/libgnunetutil.la \
25 $(LTLIBINTL)
26libgnunet_plugin_block_dht_la_LDFLAGS = \
27 $(GN_PLUGIN_LDFLAGS)
diff --git a/src/plugin/dht/meson.build b/src/plugin/dht/meson.build
new file mode 100644
index 000000000..81b829dac
--- /dev/null
+++ b/src/plugin/dht/meson.build
@@ -0,0 +1,9 @@
1shared_module('gnunet_plugin_block_dht',
2 ['plugin_block_dht.c'],
3 dependencies: [libgnunetutil_dep,
4 libgnunethello_dep,
5 libgnunetblock_dep,
6 libgnunetblockgroup_dep],
7 include_directories: [incdir, configuration_inc],
8 install:true,
9 install_dir: get_option('libdir')/'gnunet')
diff --git a/src/plugin/dht/plugin_block_dht.c b/src/plugin/dht/plugin_block_dht.c
new file mode 100644
index 000000000..aa5ffc719
--- /dev/null
+++ b/src/plugin/dht/plugin_block_dht.c
@@ -0,0 +1,311 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2010, 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 dht/plugin_block_dht.c
23 * @brief block plugin for DHT internals (right now, find-peer requests only);
24 * other plugins should be used to store "useful" data in the
25 * DHT (see fs block plugin)
26 * @author Christian Grothoff
27 */
28#include "platform.h"
29#include "gnunet_constants.h"
30#include "gnunet_hello_uri_lib.h"
31#include "gnunet_block_plugin.h"
32#include "gnunet_block_group_lib.h"
33
34#define DEBUG_DHT GNUNET_EXTRA_LOGGING
35
36/**
37 * Number of bits we set per entry in the bloomfilter.
38 * Do not change!
39 */
40#define BLOOMFILTER_K 16
41
42
43/**
44 * Create a new block group.
45 *
46 * @param ctx block context in which the block group is created
47 * @param type type of the block for which we are creating the group
48 * @param raw_data optional serialized prior state of the group, NULL if unavailable/fresh
49 * @param raw_data_size number of bytes in @a raw_data, 0 if unavailable/fresh
50 * @param va variable arguments specific to @a type
51 * @return block group handle, NULL if block groups are not supported
52 * by this @a type of block (this is not an error)
53 */
54static struct GNUNET_BLOCK_Group *
55block_plugin_dht_create_group (void *cls,
56 enum GNUNET_BLOCK_Type type,
57 const void *raw_data,
58 size_t raw_data_size,
59 va_list va)
60{
61 unsigned int bf_size;
62 const char *guard;
63
64 guard = va_arg (va, const char *);
65 if (0 == strcmp (guard,
66 "seen-set-size"))
67 bf_size = GNUNET_BLOCK_GROUP_compute_bloomfilter_size (va_arg (va,
68 unsigned int),
69 BLOOMFILTER_K);
70 else if (0 == strcmp (guard,
71 "filter-size"))
72 bf_size = va_arg (va, unsigned int);
73 else
74 {
75 GNUNET_break (0);
76 bf_size = 8;
77 }
78 GNUNET_break (NULL == va_arg (va, const char *));
79 return GNUNET_BLOCK_GROUP_bf_create (cls,
80 bf_size,
81 BLOOMFILTER_K,
82 type,
83 raw_data,
84 raw_data_size);
85}
86
87
88/**
89 * Function called to validate a query.
90 *
91 * @param cls closure
92 * @param type block type
93 * @param query original query (hash)
94 * @param xquery extrended query data (can be NULL, depending on type)
95 * @param xquery_size number of bytes in @a xquery
96 * @return #GNUNET_OK if the query is fine, #GNUNET_NO if not
97 */
98static enum GNUNET_GenericReturnValue
99block_plugin_dht_check_query (void *cls,
100 enum GNUNET_BLOCK_Type type,
101 const struct GNUNET_HashCode *query,
102 const void *xquery,
103 size_t xquery_size)
104{
105 switch (type)
106 {
107 case GNUNET_BLOCK_TYPE_DHT_HELLO:
108 if (0 != xquery_size)
109 {
110 GNUNET_break_op (0);
111 return GNUNET_NO;
112 }
113 return GNUNET_OK;
114 default:
115 GNUNET_break (0);
116 return GNUNET_SYSERR;
117 }
118}
119
120
121/**
122 * Function called to validate a block for storage.
123 *
124 * @param cls closure
125 * @param type block type
126 * @param block block data to validate
127 * @param block_size number of bytes in @a block
128 * @return #GNUNET_OK if the block is fine, #GNUNET_NO if not
129 */
130static enum GNUNET_GenericReturnValue
131block_plugin_dht_check_block (void *cls,
132 enum GNUNET_BLOCK_Type type,
133 const void *block,
134 size_t block_size)
135{
136 switch (type)
137 {
138 case GNUNET_BLOCK_TYPE_DHT_HELLO:
139 {
140 struct GNUNET_HELLO_Builder *b;
141 struct GNUNET_PeerIdentity pid;
142 struct GNUNET_HashCode h_pid;
143
144 b = GNUNET_HELLO_builder_from_block (block,
145 block_size);
146 if (NULL == b)
147 {
148 GNUNET_break (0);
149 return GNUNET_NO;
150 }
151 GNUNET_HELLO_builder_iterate (b,
152 &pid,
153 NULL, NULL);
154 GNUNET_CRYPTO_hash (&pid,
155 sizeof (pid),
156 &h_pid);
157 GNUNET_HELLO_builder_free (b);
158 return GNUNET_OK;
159 }
160 default:
161 GNUNET_break (0);
162 return GNUNET_SYSERR;
163 }
164}
165
166
167/**
168 * Function called to validate a reply to a request. Note that it is assumed
169 * that the reply has already been matched to the key (and signatures checked)
170 * as it would be done with the GetKeyFunction and the
171 * BlockEvaluationFunction.
172 *
173 * @param cls closure
174 * @param type block type
175 * @param group which block group to use for evaluation
176 * @param query original query (hash)
177 * @param xquery extrended query data (can be NULL, depending on type)
178 * @param xquery_size number of bytes in @a xquery
179 * @param reply_block response to validate
180 * @param reply_block_size number of bytes in @a reply_block
181 * @return characterization of result
182 */
183static enum GNUNET_BLOCK_ReplyEvaluationResult
184block_plugin_dht_check_reply (
185 void *cls,
186 enum GNUNET_BLOCK_Type type,
187 struct GNUNET_BLOCK_Group *group,
188 const struct GNUNET_HashCode *query,
189 const void *xquery,
190 size_t xquery_size,
191 const void *reply_block,
192 size_t reply_block_size)
193{
194 switch (type)
195 {
196 case GNUNET_BLOCK_TYPE_DHT_HELLO:
197 {
198 struct GNUNET_HELLO_Builder *b;
199 struct GNUNET_PeerIdentity pid;
200 struct GNUNET_HashCode h_pid;
201
202 b = GNUNET_HELLO_builder_from_block (reply_block,
203 reply_block_size);
204 GNUNET_assert (NULL != b);
205 GNUNET_HELLO_builder_iterate (b,
206 &pid,
207 NULL, NULL);
208 GNUNET_CRYPTO_hash (&pid,
209 sizeof (pid),
210 &h_pid);
211 GNUNET_HELLO_builder_free (b);
212 if (GNUNET_YES ==
213 GNUNET_BLOCK_GROUP_bf_test_and_set (group,
214 &h_pid))
215 return GNUNET_BLOCK_REPLY_OK_DUPLICATE;
216 return GNUNET_BLOCK_REPLY_OK_MORE;
217 }
218 default:
219 return GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED;
220 }
221}
222
223
224/**
225 * Function called to obtain the key for a block.
226 *
227 * @param cls closure
228 * @param type block type
229 * @param block block to get the key for
230 * @param block_size number of bytes @a block
231 * @param[out] key set to the key (query) for the given block
232 * @return #GNUNET_OK on success, #GNUNET_SYSERR if type not supported
233 * (or if extracting a key from a block of this type does not work)
234 */
235static enum GNUNET_GenericReturnValue
236block_plugin_dht_get_key (void *cls,
237 enum GNUNET_BLOCK_Type type,
238 const void *block,
239 size_t block_size,
240 struct GNUNET_HashCode *key)
241{
242 switch (type)
243 {
244 case GNUNET_BLOCK_TYPE_DHT_HELLO:
245 {
246 struct GNUNET_HELLO_Builder *b;
247 struct GNUNET_PeerIdentity pid;
248
249 b = GNUNET_HELLO_builder_from_block (block,
250 block_size);
251 if (NULL == b)
252 {
253 GNUNET_break (0);
254 memset (key,
255 0,
256 sizeof (*key));
257 return GNUNET_OK;
258 }
259 GNUNET_HELLO_builder_iterate (b,
260 &pid,
261 NULL, NULL);
262 GNUNET_CRYPTO_hash (&pid,
263 sizeof (pid),
264 key);
265 GNUNET_HELLO_builder_free (b);
266 return GNUNET_OK;
267 }
268 default:
269 GNUNET_break (0);
270 return GNUNET_SYSERR;
271 }
272}
273
274
275/**
276 * Entry point for the plugin.
277 */
278void *
279libgnunet_plugin_block_dht_init (void *cls)
280{
281 static enum GNUNET_BLOCK_Type types[] = {
282 GNUNET_BLOCK_TYPE_DHT_HELLO,
283 GNUNET_BLOCK_TYPE_ANY /* end of list */
284 };
285 struct GNUNET_BLOCK_PluginFunctions *api;
286
287 api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions);
288 api->get_key = &block_plugin_dht_get_key;
289 api->check_query = &block_plugin_dht_check_query;
290 api->check_block = &block_plugin_dht_check_block;
291 api->check_reply = &block_plugin_dht_check_reply;
292 api->create_group = &block_plugin_dht_create_group;
293 api->types = types;
294 return api;
295}
296
297
298/**
299 * Exit point from the plugin.
300 */
301void *
302libgnunet_plugin_block_dht_done (void *cls)
303{
304 struct GNUNET_BLOCK_PluginFunctions *api = cls;
305
306 GNUNET_free (api);
307 return NULL;
308}
309
310
311/* end of plugin_block_dht.c */
diff --git a/src/plugin/dhtu/.gitignore b/src/plugin/dhtu/.gitignore
deleted file mode 100644
index 21f1a7c9b..000000000
--- a/src/plugin/dhtu/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
1test_dhtu_ip
diff --git a/src/plugin/dhtu/Makefile.am b/src/plugin/dhtu/Makefile.am
deleted file mode 100644
index 500d5a7b1..000000000
--- a/src/plugin/dhtu/Makefile.am
+++ /dev/null
@@ -1,81 +0,0 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4plugindir = $(libdir)/gnunet
5
6pkgcfgdir= $(pkgdatadir)/config.d/
7
8if USE_COVERAGE
9 AM_CFLAGS = --coverage -O0
10 XLIBS = -lgcov
11endif
12
13pkgcfg_DATA = \
14 dhtu.conf
15
16plugin_LTLIBRARIES = \
17 libgnunet_plugin_dhtu_gnunet.la
18
19if !OPENBSD
20plugin_LTLIBRARIES += libgnunet_plugin_dhtu_ip.la
21
22libgnunet_plugin_dhtu_ip_la_SOURCES = \
23 plugin_dhtu_ip.c
24libgnunet_plugin_dhtu_ip_la_LIBADD = \
25 $(top_builddir)/src/lib/util/libgnunetutil.la \
26 $(XLIBS) \
27 $(LTLIBINTL)
28libgnunet_plugin_dhtu_ip_la_LDFLAGS = \
29 $(GN_PLUGIN_LDFLAGS)
30endif
31
32
33libgnunet_plugin_dhtu_gnunet_la_SOURCES = \
34 plugin_dhtu_gnunet.c
35libgnunet_plugin_dhtu_gnunet_la_LIBADD = \
36 $(top_builddir)/src/service/core/libgnunetcore.la \
37 $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \
38 $(top_builddir)/src/service/transport/libgnunettransportapplication.la \
39 $(top_builddir)/src/lib/hello/libgnunethello.la \
40 $(top_builddir)/src/service/nse/libgnunetnse.la \
41 $(top_builddir)/src/lib/util/libgnunetutil.la \
42 $(XLIBS) \
43 $(LTLIBINTL)
44libgnunet_plugin_dhtu_gnunet_la_LDFLAGS = \
45 $(GN_PLUGIN_LDFLAGS)
46
47
48lib_LTLIBRARIES = \
49 libgnunettestingdhtu.la
50
51libgnunettestingdhtu_la_SOURCES = \
52 testing_dhtu_cmd_send.c
53libgnunettestingdhtu_la_LIBADD = \
54 $(top_builddir)/src/service/testing/libgnunettesting.la \
55 $(top_builddir)/src/service/arm/libgnunetarm.la \
56 $(top_builddir)/src/lib/util/libgnunetutil.la \
57 $(LTLIBINTL)
58libgnunettestingdhtu_la_LDFLAGS = \
59 $(GN_LIB_LDFLAGS) \
60 -version-info 0:0:0
61
62
63
64test_dhtu_ip_SOURCES = \
65 test_dhtu_ip.c
66test_dhtu_ip_LDADD = \
67 $(top_builddir)/src/service/testing/libgnunettesting.la \
68 $(top_builddir)/src/service/arm/libgnunetarm.la \
69 $(top_builddir)/src/lib/util/libgnunetutil.la
70
71check_PROGRAMS = \
72 test_dhtu_ip
73
74EXTRA_DIST = \
75 dhtu.conf
76
77if ENABLE_TEST_RUN
78AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
79TESTS = \
80 $(check_PROGRAMS)
81endif
diff --git a/src/plugin/dhtu/dhtu.conf b/src/plugin/dhtu/dhtu.conf
deleted file mode 100644
index ea5ade752..000000000
--- a/src/plugin/dhtu/dhtu.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1[dhtu-gnunet]
2ENABLED = YES
3
4[dhtu-ip]
5ENABLED = NO
6NSE = 4
7UDP_PORT = 6666
diff --git a/src/plugin/dhtu/meson.build b/src/plugin/dhtu/meson.build
deleted file mode 100644
index b2525e782..000000000
--- a/src/plugin/dhtu/meson.build
+++ /dev/null
@@ -1,61 +0,0 @@
1libgnunetplugindhtuip_src = ['plugin_dhtu_ip.c']
2libgnunetplugindhtugnunet_src = ['plugin_dhtu_gnunet.c']
3
4configure_file(input : 'dhtu.conf',
5 output : 'dhtu.conf',
6 configuration : cdata,
7 install: true,
8 install_dir: pkgcfgdir)
9
10if get_option('monolith')
11 foreach p : libgnunetplugindhtuip_src + libgnunetplugindhtugnunet_src
12 gnunet_src += 'dhtu/' + p
13 endforeach
14 subdir_done()
15endif
16
17libgnunettestingdhtu = library('gnunettestingdhtu',
18 ['testing_dhtu_cmd_send.c'],
19 soversion: '0',
20 version: '0.0.0',
21 dependencies: [
22 libgnunetutil_dep,
23 libgnunetarm_dep,
24 libgnunettesting_dep
25 ],
26 include_directories: [incdir, configuration_inc],
27 install: true,
28 install_dir: get_option('libdir'))
29libgnunettestingdhtu_dep = declare_dependency(link_with : libgnunettestingdhtu)
30
31shared_module('gnunet_plugin_dhtu_ip',
32 libgnunetplugindhtuip_src,
33 dependencies: [libgnunetutil_dep, m_dep],
34 include_directories: [incdir, configuration_inc],
35 install: true,
36 install_dir: get_option('libdir')/'gnunet')
37
38shared_module('gnunet_plugin_dhtu_gnunet',
39 libgnunetplugindhtugnunet_src,
40 dependencies: [libgnunetutil_dep,
41 m_dep,
42 libgnunetcore_dep,
43 libgnunethello_dep,
44 libgnunetpeerstore_dep,
45 libgnunetnse_dep,
46 ],
47 include_directories: [incdir, configuration_inc],
48 install: true,
49 install_dir: get_option('libdir')/'gnunet')
50
51testdhtu_ip = executable('test_dhtu_ip',
52 ['test_dhtu_ip.c'],
53 dependencies: [libgnunetutil_dep,
54 libgnunettesting_dep],
55 include_directories: [incdir, configuration_inc],
56 install: false)
57
58test('test_dhtu_ip', testdhtu_ip, suite: 'dhtu',
59 workdir: meson.current_build_dir())
60
61
diff --git a/src/plugin/dhtu/plugin_dhtu_gnunet.c b/src/plugin/dhtu/plugin_dhtu_gnunet.c
deleted file mode 100644
index 75f466916..000000000
--- a/src/plugin/dhtu/plugin_dhtu_gnunet.c
+++ /dev/null
@@ -1,603 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021 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 * @author Christian Grothoff
23 *
24 * @file plugin_dhtu_gnunet.c
25 * @brief plain IP based DHT network underlay
26 */
27#include "platform.h"
28#include "gnunet_dhtu_plugin.h"
29#include "gnunet_core_service.h"
30#include "gnunet_transport_application_service.h"
31#include "gnunet_hello_uri_lib.h"
32#include "gnunet_peerstore_service.h"
33#include "gnunet_nse_service.h"
34
35/**
36 * Opaque handle that the underlay offers for our address to be used when
37 * sending messages to another peer.
38 */
39struct GNUNET_DHTU_Source
40{
41
42 /**
43 * Application context for this source.
44 */
45 void *app_ctx;
46
47};
48
49
50/**
51 * Opaque handle that the underlay offers for the target peer when sending
52 * messages to another peer.
53 */
54struct GNUNET_DHTU_Target
55{
56
57 /**
58 * Application context for this target.
59 */
60 void *app_ctx;
61
62 /**
63 * Our plugin with its environment.
64 */
65 struct Plugin *plugin;
66
67 /**
68 * CORE MQ to send messages to this peer.
69 */
70 struct GNUNET_MQ_Handle *mq;
71
72 /**
73 * Head of preferences expressed for this target.
74 */
75 struct GNUNET_DHTU_PreferenceHandle *ph_head;
76
77 /**
78 * Tail of preferences expressed for this target.
79 */
80 struct GNUNET_DHTU_PreferenceHandle *ph_tail;
81
82 /**
83 * Transport suggest handle.
84 */
85 struct GNUNET_TRANSPORT_ApplicationSuggestHandle *ash;
86
87 /**
88 * Identity of this peer.
89 */
90 struct GNUNET_PeerIdentity pid;
91
92 /**
93 * Preference counter, length of the @a ph_head DLL.
94 */
95 unsigned int ph_count;
96
97};
98
99
100/**
101 * Opaque handle expressing a preference of the DHT to
102 * keep a particular target connected.
103 */
104struct GNUNET_DHTU_PreferenceHandle
105{
106 /**
107 * Kept in a DLL.
108 */
109 struct GNUNET_DHTU_PreferenceHandle *next;
110
111 /**
112 * Kept in a DLL.
113 */
114 struct GNUNET_DHTU_PreferenceHandle *prev;
115
116 /**
117 * Target a preference was expressed for.
118 */
119 struct GNUNET_DHTU_Target *target;
120};
121
122
123/**
124 * Closure for all plugin functions.
125 */
126struct Plugin
127{
128
129 /**
130 * Our "source" address. Traditional CORE API does not tell us which source
131 * it is, so they are all identical.
132 */
133 struct GNUNET_DHTU_Source src;
134
135 /**
136 * Callbacks into the DHT.
137 */
138 struct GNUNET_DHTU_PluginEnvironment *env;
139
140 /**
141 * Handle to the PEERSTORE service.
142 */
143 struct GNUNET_PEERSTORE_Handle *peerstore;
144
145 /**
146 * Handle to the CORE service.
147 */
148 struct GNUNET_CORE_Handle *core;
149
150 /**
151 * Handle to Transport service.
152 */
153 struct GNUNET_TRANSPORT_ApplicationHandle *transport;
154
155 /**
156 * Handle to the NSE service.
157 */
158 struct GNUNET_NSE_Handle *nse;
159
160 /**
161 * Our peerstore notification context. We use notification
162 * to instantly learn about new peers as they are discovered.
163 */
164 struct GNUNET_PEERSTORE_NotifyContext *peerstore_notify;
165
166 /**
167 * Identity of this peer.
168 */
169 struct GNUNET_PeerIdentity my_identity;
170
171 /**
172 * Our private key.
173 */
174 struct GNUNET_CRYPTO_EddsaPrivateKey *my_priv;
175
176};
177
178
179//#include "../peerinfo-tool/gnunet-peerinfo_plugins.c"
180
181
182/**
183 * Request creation of a session with a peer at the given @a address.
184 *
185 * @param cls closure (internal context for the plugin)
186 * @param pid target identity of the peer to connect to
187 * @param address target address to connect to
188 */
189static void
190gnunet_try_connect (void *cls,
191 const struct GNUNET_PeerIdentity *pid,
192 const char *address)
193{
194 struct Plugin *plugin = cls;
195 enum GNUNET_NetworkType nt = 0;
196 char *addr;
197 const char *eou;
198 int pfx_len;
199
200 eou = strstr (address,
201 "://");
202 if (NULL == eou)
203 {
204 GNUNET_break (0);
205 return;
206 }
207 pfx_len = eou - address;
208 eou += 3;
209 GNUNET_asprintf (&addr,
210 "%.*s-%s",
211 pfx_len,
212 address,
213 eou);
214 GNUNET_TRANSPORT_application_validate (plugin->transport,
215 pid,
216 nt,
217 addr);
218 GNUNET_free (addr);
219}
220
221
222/**
223 * Request underlay to keep the connection to @a target alive if possible.
224 * Hold may be called multiple times to express a strong preference to
225 * keep a connection, say because a @a target is in multiple tables.
226 *
227 * @param cls closure
228 * @param target connection to keep alive
229 */
230static struct GNUNET_DHTU_PreferenceHandle *
231gnunet_hold (void *cls,
232 struct GNUNET_DHTU_Target *target)
233{
234 struct Plugin *plugin = cls;
235 struct GNUNET_DHTU_PreferenceHandle *ph;
236
237 ph = GNUNET_new (struct GNUNET_DHTU_PreferenceHandle);
238 ph->target = target;
239 GNUNET_CONTAINER_DLL_insert (target->ph_head,
240 target->ph_tail,
241 ph);
242 target->ph_count++;
243 if (NULL != target->ash)
244 GNUNET_TRANSPORT_application_suggest_cancel (target->ash);
245 target->ash
246 = GNUNET_TRANSPORT_application_suggest (plugin->transport,
247 &target->pid,
248 GNUNET_MQ_PRIO_BEST_EFFORT,
249 GNUNET_BANDWIDTH_ZERO);
250 return ph;
251}
252
253
254/**
255 * Do no long request underlay to keep the connection alive.
256 *
257 * @param cls closure
258 * @param target connection to keep alive
259 */
260static void
261gnunet_drop (struct GNUNET_DHTU_PreferenceHandle *ph)
262{
263 struct GNUNET_DHTU_Target *target = ph->target;
264 struct Plugin *plugin = target->plugin;
265
266 GNUNET_CONTAINER_DLL_remove (target->ph_head,
267 target->ph_tail,
268 ph);
269 target->ph_count--;
270 GNUNET_free (ph);
271 if (NULL != target->ash)
272 GNUNET_TRANSPORT_application_suggest_cancel (target->ash);
273 if (0 == target->ph_count)
274 target->ash = NULL;
275 else
276 target->ash
277 = GNUNET_TRANSPORT_application_suggest (plugin->transport,
278 &target->pid,
279 GNUNET_MQ_PRIO_BEST_EFFORT,
280 GNUNET_BANDWIDTH_ZERO);
281}
282
283
284/**
285 * Send message to some other participant over the network. Note that
286 * sending is not guaranteeing that the other peer actually received the
287 * message. For any given @a target, the DHT must wait for the @a
288 * finished_cb to be called before calling send() again.
289 *
290 * @param cls closure (internal context for the plugin)
291 * @param target receiver identification
292 * @param msg message
293 * @param msg_size number of bytes in @a msg
294 * @param finished_cb function called once transmission is done
295 * (not called if @a target disconnects, then only the
296 * disconnect_cb is called).
297 * @param finished_cb_cls closure for @a finished_cb
298 */
299static void
300gnunet_send (void *cls,
301 struct GNUNET_DHTU_Target *target,
302 const void *msg,
303 size_t msg_size,
304 GNUNET_SCHEDULER_TaskCallback finished_cb,
305 void *finished_cb_cls)
306{
307 struct GNUNET_MQ_Envelope *env;
308 struct GNUNET_MessageHeader *cmsg;
309
310 env = GNUNET_MQ_msg_extra (cmsg,
311 msg_size,
312 GNUNET_MESSAGE_TYPE_DHT_CORE);
313 GNUNET_MQ_notify_sent (env,
314 finished_cb,
315 finished_cb_cls);
316 memcpy (&cmsg[1],
317 msg,
318 msg_size);
319 GNUNET_MQ_send (target->mq,
320 env);
321}
322
323
324/**
325 * Method called whenever a given peer connects.
326 *
327 * @param cls closure
328 * @param peer peer identity this notification is about
329 * @return closure associated with @a peer. given to mq callbacks and
330 * #GNUNET_CORE_DisconnectEventHandler
331 */
332static void *
333core_connect_cb (void *cls,
334 const struct GNUNET_PeerIdentity *peer,
335 struct GNUNET_MQ_Handle *mq)
336{
337 struct Plugin *plugin = cls;
338 struct GNUNET_DHTU_Target *target;
339
340 target = GNUNET_new (struct GNUNET_DHTU_Target);
341 target->plugin = plugin;
342 target->mq = mq;
343 target->pid = *peer;
344 plugin->env->connect_cb (plugin->env->cls,
345 target,
346 &target->pid,
347 &target->app_ctx);
348 return target;
349}
350
351
352/**
353 * Method called whenever a peer disconnects.
354 *
355 * @param cls closure
356 * @param peer peer identity this notification is about
357 * @param peer_cls closure associated with peer. given in
358 * #GNUNET_CORE_ConnectEventHandler
359 */
360static void
361core_disconnect_cb (void *cls,
362 const struct GNUNET_PeerIdentity *peer,
363 void *peer_cls)
364{
365 struct Plugin *plugin = cls;
366 struct GNUNET_DHTU_Target *target = peer_cls;
367
368 plugin->env->disconnect_cb (target->app_ctx);
369 if (NULL != target->ash)
370 GNUNET_TRANSPORT_application_suggest_cancel (target->ash);
371 GNUNET_free (target);
372}
373
374
375static void
376add_addr (void *cls,
377 const char *addr)
378{
379 struct Plugin *plugin = cls;
380
381 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG ,
382 "peerinfo_cb addr %s\n",
383 addr);
384 plugin->env->address_add_cb (plugin->env->cls,
385 addr,
386 &plugin->src,
387 &plugin->src.app_ctx);
388}
389
390
391/**
392 * Find the @a hello for our identity and then pass
393 * it to the DHT as a URL. Note that we only
394 * add addresses, never remove them, due to limitations
395 * of the current peerinfo/core/transport APIs.
396 * This will change with TNG.
397 *
398 * @param cls a `struct Plugin`
399 * @param peer id of the peer, NULL for last call
400 * @param hello hello message for the peer (can be NULL)
401 * @param err_msg message
402 */
403static void
404peerinfo_cb (void *cls,
405 const struct GNUNET_PeerIdentity *peer,
406 const struct GNUNET_MessageHeader *hello,
407 const char *emsg)
408{
409 struct Plugin *plugin = cls;
410 struct GNUNET_HELLO_Builder *builder;
411
412 if (NULL == hello)
413 return;
414 if (NULL == peer)
415 return;
416 if (0 !=
417 GNUNET_memcmp (peer,
418 &plugin->my_identity))
419 return;
420 builder = GNUNET_HELLO_builder_from_msg (hello);
421 GNUNET_HELLO_builder_iterate (builder,
422 (struct GNUNET_PeerIdentity *) peer,
423 add_addr,
424 plugin);
425 GNUNET_HELLO_builder_free (builder);
426}
427
428
429/**
430 * Function called after #GNUNET_CORE_connect has succeeded (or failed
431 * for good). Note that the private key of the peer is intentionally
432 * not exposed here; if you need it, your process should try to read
433 * the private key file directly (which should work if you are
434 * authorized...). Implementations of this function must not call
435 * #GNUNET_CORE_disconnect (other than by scheduling a new task to
436 * do this later).
437 *
438 * @param cls closure
439 * @param my_identity ID of this peer, NULL if we failed
440 */
441static void
442 core_init_cb (void *cls,
443 const struct GNUNET_PeerIdentity *my_identity)
444{
445 struct Plugin *plugin = cls;
446
447 plugin->my_identity = *my_identity;
448 plugin->peerstore_notify =
449 GNUNET_PEERSTORE_hello_changed_notify (plugin->peerstore,
450 GNUNET_NO,
451 &peerinfo_cb,
452 plugin);
453}
454
455
456/**
457 * Anything goes, always return #GNUNET_OK.
458 *
459 * @param cls unused
460 * @param msg message to check
461 * @return #GNUNET_OK if all is fine
462 */
463static int
464check_core_message (void *cls,
465 const struct GNUNET_MessageHeader *msg)
466{
467 (void) cls;
468 (void) msg;
469 return GNUNET_OK;
470}
471
472
473/**
474 * Handle message from CORE for the DHT. Passes it to the
475 * DHT logic.
476 *
477 * @param cls a `struct GNUNET_DHTU_Target` of the sender
478 * @param msg the message we received
479 */
480static void
481handle_core_message (void *cls,
482 const struct GNUNET_MessageHeader *msg)
483{
484 struct GNUNET_DHTU_Target *origin = cls;
485 struct Plugin *plugin = origin->plugin;
486
487 plugin->env->receive_cb (plugin->env->cls,
488 &origin->app_ctx,
489 &plugin->src.app_ctx,
490 &msg[1],
491 ntohs (msg->size) - sizeof (*msg));
492}
493
494
495/**
496 * Callback to call when network size estimate is updated.
497 *
498 * @param cls closure
499 * @param timestamp time when the estimate was received from the server (or created by the server)
500 * @param logestimate the log(Base 2) value of the current network size estimate
501 * @param std_dev standard deviation for the estimate
502 */
503static void
504nse_cb (void *cls,
505 struct GNUNET_TIME_Absolute timestamp,
506 double logestimate,
507 double std_dev)
508{
509 struct Plugin *plugin = cls;
510
511 plugin->env->network_size_cb (plugin->env->cls,
512 timestamp,
513 logestimate,
514 std_dev);
515}
516
517
518/**
519 * Exit point from the plugin.
520 *
521 * @param cls closure (our `struct Plugin`)
522 * @return NULL
523 */
524void *
525libgnunet_plugin_dhtu_gnunet_done (void *cls)
526{
527 struct GNUNET_DHTU_PluginFunctions *api = cls;
528 struct Plugin *plugin = api->cls;
529
530 if (NULL != plugin->nse)
531 GNUNET_NSE_disconnect (plugin->nse);
532 plugin->env->network_size_cb (plugin->env->cls,
533 GNUNET_TIME_UNIT_FOREVER_ABS,
534 0.0,
535 0.0);
536 if (NULL != plugin->core)
537 GNUNET_CORE_disconnect (plugin->core);
538 if (NULL != plugin->transport)
539 GNUNET_TRANSPORT_application_done (plugin->transport);
540 if (NULL != plugin->peerstore_notify)
541 GNUNET_PEERSTORE_hello_changed_notify_cancel (plugin->peerstore_notify);
542 if (NULL != plugin->peerstore)
543 GNUNET_PEERSTORE_disconnect (plugin->peerstore, GNUNET_YES);
544 //GPI_plugins_unload ();
545 GNUNET_free (plugin->my_priv);
546 GNUNET_free (plugin);
547 GNUNET_free (api);
548 return NULL;
549}
550
551
552/**
553 * Entry point for the plugin.
554 *
555 * @param cls closure (the `struct GNUNET_DHTU_PluginEnvironment`)
556 * @return the plugin's API
557 */
558void *
559libgnunet_plugin_dhtu_gnunet_init (void *cls)
560{
561 struct GNUNET_DHTU_PluginEnvironment *env = cls;
562 struct GNUNET_DHTU_PluginFunctions *api;
563 struct Plugin *plugin;
564 struct GNUNET_MQ_MessageHandler handlers[] = {
565 GNUNET_MQ_hd_var_size (core_message,
566 GNUNET_MESSAGE_TYPE_DHT_CORE,
567 struct GNUNET_MessageHeader,
568 NULL),
569 GNUNET_MQ_handler_end ()
570 };
571
572 plugin = GNUNET_new (struct Plugin);
573 plugin->my_priv = GNUNET_CRYPTO_eddsa_key_create_from_configuration (env->cfg);
574 plugin->env = env;
575 api = GNUNET_new (struct GNUNET_DHTU_PluginFunctions);
576 api->cls = plugin;
577 api->try_connect = &gnunet_try_connect;
578 api->hold = &gnunet_hold;
579 api->drop = &gnunet_drop;
580 api->send = &gnunet_send;
581 plugin->peerstore = GNUNET_PEERSTORE_connect (env->cfg);
582 plugin->transport = GNUNET_TRANSPORT_application_init (env->cfg);
583 plugin->core = GNUNET_CORE_connect (env->cfg,
584 plugin,
585 &core_init_cb,
586 &core_connect_cb,
587 &core_disconnect_cb,
588 handlers);
589 plugin->nse = GNUNET_NSE_connect (env->cfg,
590 &nse_cb,
591 plugin);
592 if ( (NULL == plugin->transport) ||
593 (NULL == plugin->core) ||
594 (NULL == plugin->nse) )
595 {
596 GNUNET_break (0);
597 GNUNET_free (api);
598 libgnunet_plugin_dhtu_gnunet_done (plugin);
599 return NULL;
600 }
601 //GPI_plugins_load (env->cfg);
602 return api;
603}
diff --git a/src/plugin/dhtu/plugin_dhtu_ip.c b/src/plugin/dhtu/plugin_dhtu_ip.c
deleted file mode 100644
index 06d0f0f60..000000000
--- a/src/plugin/dhtu/plugin_dhtu_ip.c
+++ /dev/null
@@ -1,1171 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021, 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 * @author Christian Grothoff
23 *
24 * @file plugin_dhtu_ip.c
25 * @brief plain IP based DHT network underlay
26 */
27#include "platform.h"
28#include "gnunet_dhtu_plugin.h"
29
30/**
31 * How frequently should we re-scan our local interfaces for IPs?
32 */
33#define SCAN_FREQ GNUNET_TIME_UNIT_MINUTES
34
35/**
36 * Maximum number of concurrently active destinations to support.
37 */
38#define MAX_DESTS 256
39
40
41/**
42 * Opaque handle that the underlay offers for our address to be used when
43 * sending messages to another peer.
44 */
45struct GNUNET_DHTU_Source
46{
47
48 /**
49 * Kept in a DLL.
50 */
51 struct GNUNET_DHTU_Source *next;
52
53 /**
54 * Kept in a DLL.
55 */
56 struct GNUNET_DHTU_Source *prev;
57
58 /**
59 * Application context for this source.
60 */
61 void *app_ctx;
62
63 /**
64 * Address in URL form ("ip+udp://$PID/$IP:$PORT")
65 */
66 char *address;
67
68 /**
69 * My actual address.
70 */
71 struct sockaddr_storage addr;
72
73 /**
74 * Number of bytes in @a addr.
75 */
76 socklen_t addrlen;
77
78 /**
79 * Last generation this address was observed.
80 */
81 unsigned int scan_generation;
82
83};
84
85
86/**
87 * Opaque handle that the underlay offers for the target peer when sending
88 * messages to another peer.
89 */
90struct GNUNET_DHTU_Target
91{
92
93 /**
94 * Kept in a DLL.
95 */
96 struct GNUNET_DHTU_Target *next;
97
98 /**
99 * Kept in a DLL.
100 */
101 struct GNUNET_DHTU_Target *prev;
102
103 /**
104 * Application context for this target.
105 */
106 void *app_ctx;
107
108 /**
109 * Head of preferences expressed for this target.
110 */
111 struct GNUNET_DHTU_PreferenceHandle *ph_head;
112
113 /**
114 * Tail of preferences expressed for this target.
115 */
116 struct GNUNET_DHTU_PreferenceHandle *ph_tail;
117
118 /**
119 * Peer's identity.
120 */
121 struct GNUNET_PeerIdentity pid;
122
123 /**
124 * Target IP address.
125 */
126 struct sockaddr_storage addr;
127
128 /**
129 * Number of bytes in @a addr.
130 */
131 socklen_t addrlen;
132
133 /**
134 * Preference counter, length of the @a ph_head DLL.
135 */
136 unsigned int ph_count;
137
138};
139
140/**
141 * Opaque handle expressing a preference of the DHT to
142 * keep a particular target connected.
143 */
144struct GNUNET_DHTU_PreferenceHandle
145{
146 /**
147 * Kept in a DLL.
148 */
149 struct GNUNET_DHTU_PreferenceHandle *next;
150
151 /**
152 * Kept in a DLL.
153 */
154 struct GNUNET_DHTU_PreferenceHandle *prev;
155
156 /**
157 * Target a preference was expressed for.
158 */
159 struct GNUNET_DHTU_Target *target;
160};
161
162
163/**
164 * Closure for all plugin functions.
165 */
166struct Plugin
167{
168 /**
169 * Callbacks into the DHT.
170 */
171 struct GNUNET_DHTU_PluginEnvironment *env;
172
173 /**
174 * Head of sources where we receive traffic.
175 */
176 struct GNUNET_DHTU_Source *src_head;
177
178 /**
179 * Tail of sources where we receive traffic.
180 */
181 struct GNUNET_DHTU_Source *src_tail;
182
183 /**
184 * Head of destinations that are active. Sorted by
185 * last use, with latest used at the head.
186 */
187 struct GNUNET_DHTU_Target *dst_head;
188
189 /**
190 * Tail of destinations that are active.
191 */
192 struct GNUNET_DHTU_Target *dst_tail;
193
194 /**
195 * Map from hashes of sockaddrs to targets.
196 */
197 struct GNUNET_CONTAINER_MultiHashMap *dsts;
198
199 /**
200 * Task that scans for IP address changes.
201 */
202 struct GNUNET_SCHEDULER_Task *scan_task;
203
204 /**
205 * Task that reads incoming UDP packets.
206 */
207 struct GNUNET_SCHEDULER_Task *read_task;
208
209 /**
210 * Port we bind to.
211 */
212 char *port;
213
214 /**
215 * My UDP socket.
216 */
217 struct GNUNET_NETWORK_Handle *sock;
218
219 /**
220 * My identity.
221 */
222 struct GNUNET_PeerIdentity my_id;
223
224 /**
225 * How often have we scanned for IPs?
226 */
227 unsigned int scan_generation;
228
229 /**
230 * Port as a 16-bit value.
231 */
232 uint16_t port16;
233};
234
235
236/**
237 * Create a target to which we may send traffic.
238 *
239 * @param plugin our plugin
240 * @param pid presumed identity of the target
241 * @param addr target address
242 * @param addrlen number of bytes in @a addr
243 * @return new target object
244 */
245static struct GNUNET_DHTU_Target *
246create_target (struct Plugin *plugin,
247 const struct GNUNET_PeerIdentity *pid,
248 const struct sockaddr *addr,
249 socklen_t addrlen)
250{
251 struct GNUNET_DHTU_Target *dst;
252
253 if (MAX_DESTS <=
254 GNUNET_CONTAINER_multihashmap_size (plugin->dsts))
255 {
256 struct GNUNET_HashCode key;
257
258 dst = NULL;
259 for (struct GNUNET_DHTU_Target *pos = plugin->dst_head;
260 NULL != pos;
261 pos = pos->next)
262 {
263 /* >= here assures we remove oldest entries first */
264 if ( (NULL == dst) ||
265 (dst->ph_count >= pos->ph_count) )
266 dst = pos;
267 }
268 GNUNET_assert (NULL != dst);
269 plugin->env->disconnect_cb (dst->app_ctx);
270 GNUNET_CRYPTO_hash (&dst->addr,
271 dst->addrlen,
272 &key);
273 GNUNET_assert (GNUNET_YES ==
274 GNUNET_CONTAINER_multihashmap_remove (plugin->dsts,
275 &key,
276 dst));
277 GNUNET_CONTAINER_DLL_remove (plugin->dst_head,
278 plugin->dst_tail,
279 dst);
280 GNUNET_assert (NULL == dst->ph_head);
281 GNUNET_free (dst);
282 }
283 dst = GNUNET_new (struct GNUNET_DHTU_Target);
284 dst->addrlen = addrlen;
285 dst->pid = *pid;
286 memcpy (&dst->addr,
287 addr,
288 addrlen);
289 GNUNET_CONTAINER_DLL_insert (plugin->dst_head,
290 plugin->dst_tail,
291 dst);
292 plugin->env->connect_cb (plugin->env->cls,
293 dst,
294 &dst->pid,
295 &dst->app_ctx);
296 return dst;
297}
298
299
300/**
301 * Find target matching @a addr. If none exists,
302 * create one!
303 *
304 * @param plugin the plugin handle
305 * @param pid presumed identity of the target
306 * @param addr socket address to find
307 * @param addrlen number of bytes in @a addr
308 * @return matching target object
309 */
310static struct GNUNET_DHTU_Target *
311find_target (struct Plugin *plugin,
312 const struct GNUNET_PeerIdentity *pid,
313 const void *addr,
314 size_t addrlen)
315{
316 struct GNUNET_HashCode key;
317 struct GNUNET_DHTU_Target *dst;
318
319 GNUNET_CRYPTO_hash (addr,
320 addrlen,
321 &key);
322 dst = GNUNET_CONTAINER_multihashmap_get (plugin->dsts,
323 &key);
324 if (NULL == dst)
325 {
326 dst = create_target (plugin,
327 pid,
328 (const struct sockaddr *) addr,
329 addrlen);
330 GNUNET_assert (GNUNET_YES ==
331 GNUNET_CONTAINER_multihashmap_put (
332 plugin->dsts,
333 &key,
334 dst,
335 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
336 }
337 else
338 {
339 /* move to head of DLL */
340 GNUNET_CONTAINER_DLL_remove (plugin->dst_head,
341 plugin->dst_tail,
342 dst);
343 GNUNET_CONTAINER_DLL_insert (plugin->dst_head,
344 plugin->dst_tail,
345 dst);
346
347 }
348 return dst;
349}
350
351
352/**
353 * Request creation of a session with a peer at the given @a address.
354 *
355 * @param cls closure (internal context for the plugin)
356 * @param pid identity of the target peer
357 * @param address target address to connect to
358 */
359static void
360ip_try_connect (void *cls,
361 const struct GNUNET_PeerIdentity *pid,
362 const char *address)
363{
364 struct Plugin *plugin = cls;
365 char *colon;
366 const char *port;
367 char *addr;
368 struct addrinfo hints = {
369 .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV
370 };
371 struct addrinfo *result = NULL;
372
373 if (0 !=
374 strncmp (address,
375 "ip+",
376 strlen ("ip+")))
377 return;
378 address += strlen ("ip+");
379 if (0 !=
380 strncmp (address,
381 "udp://",
382 strlen ("udp://")))
383 return;
384 address += strlen ("udp://");
385 addr = GNUNET_strdup (address);
386 colon = strchr (addr, ':');
387 if (NULL == colon)
388 {
389 port = plugin->port;
390 }
391 else
392 {
393 *colon = '\0';
394 port = colon + 1;
395 }
396 if (0 !=
397 getaddrinfo (addr,
398 port,
399 &hints,
400 &result))
401 {
402 GNUNET_break (0);
403 GNUNET_free (addr);
404 return;
405 }
406 GNUNET_free (addr);
407 (void) find_target (plugin,
408 pid,
409 result->ai_addr,
410 result->ai_addrlen);
411 freeaddrinfo (result);
412}
413
414
415/**
416 * Request underlay to keep the connection to @a target alive if possible.
417 * Hold may be called multiple times to express a strong preference to
418 * keep a connection, say because a @a target is in multiple tables.
419 *
420 * @param cls closure
421 * @param target connection to keep alive
422 */
423static struct GNUNET_DHTU_PreferenceHandle *
424ip_hold (void *cls,
425 struct GNUNET_DHTU_Target *target)
426{
427 struct GNUNET_DHTU_PreferenceHandle *ph;
428
429 ph = GNUNET_new (struct GNUNET_DHTU_PreferenceHandle);
430 ph->target = target;
431 GNUNET_CONTAINER_DLL_insert (target->ph_head,
432 target->ph_tail,
433 ph);
434 target->ph_count++;
435 return ph;
436}
437
438
439/**
440 * Do no long request underlay to keep the connection alive.
441 *
442 * @param cls closure
443 * @param target connection to keep alive
444 */
445static void
446ip_drop (struct GNUNET_DHTU_PreferenceHandle *ph)
447{
448 struct GNUNET_DHTU_Target *target = ph->target;
449
450 GNUNET_CONTAINER_DLL_remove (target->ph_head,
451 target->ph_tail,
452 ph);
453 target->ph_count--;
454 GNUNET_free (ph);
455}
456
457
458/**
459 * Send message to some other participant over the network. Note that
460 * sending is not guaranteeing that the other peer actually received the
461 * message. For any given @a target, the DHT must wait for the @a
462 * finished_cb to be called before calling send() again.
463 *
464 * @param cls closure (internal context for the plugin)
465 * @param target receiver identification
466 * @param msg message
467 * @param msg_size number of bytes in @a msg
468 * @param finished_cb function called once transmission is done
469 * (not called if @a target disconnects, then only the
470 * disconnect_cb is called).
471 * @param finished_cb_cls closure for @a finished_cb
472 */
473static void
474ip_send (void *cls,
475 struct GNUNET_DHTU_Target *target,
476 const void *msg,
477 size_t msg_size,
478 GNUNET_SCHEDULER_TaskCallback finished_cb,
479 void *finished_cb_cls)
480{
481 struct Plugin *plugin = cls;
482 char buf[sizeof (plugin->my_id) + msg_size];
483
484 memcpy (buf,
485 &plugin->my_id,
486 sizeof (plugin->my_id));
487 memcpy (&buf[sizeof (plugin->my_id)],
488 msg,
489 msg_size);
490 GNUNET_NETWORK_socket_sendto (plugin->sock,
491 buf,
492 sizeof (buf),
493 (const struct sockaddr *) &target->addr,
494 target->addrlen);
495 finished_cb (finished_cb_cls);
496}
497
498
499/**
500 * Create a new source on which we may be receiving traffic.
501 *
502 * @param plugin our plugin
503 * @param addr our address
504 * @param addrlen number of bytes in @a addr
505 * @return new source object
506 */
507static struct GNUNET_DHTU_Source *
508create_source (struct Plugin *plugin,
509 const struct sockaddr *addr,
510 socklen_t addrlen)
511{
512 struct GNUNET_DHTU_Source *src;
513
514 src = GNUNET_new (struct GNUNET_DHTU_Source);
515 src->addrlen = addrlen;
516 memcpy (&src->addr,
517 addr,
518 addrlen);
519 src->scan_generation = plugin->scan_generation;
520 switch (addr->sa_family)
521 {
522 case AF_INET:
523 {
524 const struct sockaddr_in *s4 = (const struct sockaddr_in *) addr;
525 char buf[INET_ADDRSTRLEN];
526
527 GNUNET_assert (sizeof (struct sockaddr_in) == addrlen);
528 GNUNET_asprintf (&src->address,
529 "ip+udp://%s:%u",
530 inet_ntop (AF_INET,
531 &s4->sin_addr,
532 buf,
533 sizeof (buf)),
534 ntohs (s4->sin_port));
535 }
536 break;
537 case AF_INET6:
538 {
539 const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *) addr;
540 char buf[INET6_ADDRSTRLEN];
541
542 GNUNET_assert (sizeof (struct sockaddr_in6) == addrlen);
543 GNUNET_asprintf (&src->address,
544 "ip+udp://[%s]:%u",
545 inet_ntop (AF_INET6,
546 &s6->sin6_addr,
547 buf,
548 sizeof (buf)),
549 ntohs (s6->sin6_port));
550 }
551 break;
552 default:
553 GNUNET_break (0);
554 GNUNET_free (src);
555 return NULL;
556 }
557 GNUNET_CONTAINER_DLL_insert (plugin->src_head,
558 plugin->src_tail,
559 src);
560 plugin->env->address_add_cb (plugin->env->cls,
561 src->address,
562 src,
563 &src->app_ctx);
564 return src;
565}
566
567
568/**
569 * Compare two addresses excluding the ports for equality. Only compares IP
570 * address. Must only be called on AF_INET or AF_INET6 addresses.
571 *
572 * @param a1 address to compare
573 * @param a2 address to compare
574 * @param alen number of bytes in @a a1 and @a a2
575 * @return 0 if @a a1 == @a a2.
576 */
577static int
578addrcmp_np (const struct sockaddr *a1,
579 const struct sockaddr *a2,
580 size_t alen)
581{
582 GNUNET_assert (a1->sa_family == a2->sa_family);
583 switch (a1->sa_family)
584 {
585 case AF_INET:
586 GNUNET_assert (sizeof (struct sockaddr_in) == alen);
587 {
588 const struct sockaddr_in *s1 = (const struct sockaddr_in *) a1;
589 const struct sockaddr_in *s2 = (const struct sockaddr_in *) a2;
590
591 if (s1->sin_addr.s_addr != s2->sin_addr.s_addr)
592 return 1;
593 break;
594 }
595 case AF_INET6:
596 GNUNET_assert (sizeof (struct sockaddr_in6) == alen);
597 {
598 const struct sockaddr_in6 *s1 = (const struct sockaddr_in6 *) a1;
599 const struct sockaddr_in6 *s2 = (const struct sockaddr_in6 *) a2;
600
601 if (0 != GNUNET_memcmp (&s1->sin6_addr,
602 &s2->sin6_addr))
603 return 1;
604 break;
605 }
606 default:
607 GNUNET_assert (0);
608 }
609 return 0;
610}
611
612
613/**
614 * Compare two addresses for equality. Only
615 * compares IP address and port. Must only be
616 * called on AF_INET or AF_INET6 addresses.
617 *
618 * @param a1 address to compare
619 * @param a2 address to compare
620 * @param alen number of bytes in @a a1 and @a a2
621 * @return 0 if @a a1 == @a a2.
622 */
623static int
624addrcmp (const struct sockaddr *a1,
625 const struct sockaddr *a2,
626 size_t alen)
627{
628 GNUNET_assert (a1->sa_family == a2->sa_family);
629 switch (a1->sa_family)
630 {
631 case AF_INET:
632 GNUNET_assert (sizeof (struct sockaddr_in) == alen);
633 {
634 const struct sockaddr_in *s1 = (const struct sockaddr_in *) a1;
635 const struct sockaddr_in *s2 = (const struct sockaddr_in *) a2;
636
637 if (s1->sin_port != s2->sin_port)
638 return 1;
639 if (s1->sin_addr.s_addr != s2->sin_addr.s_addr)
640 return 1;
641 break;
642 }
643 case AF_INET6:
644 GNUNET_assert (sizeof (struct sockaddr_in6) == alen);
645 {
646 const struct sockaddr_in6 *s1 = (const struct sockaddr_in6 *) a1;
647 const struct sockaddr_in6 *s2 = (const struct sockaddr_in6 *) a2;
648
649 if (s1->sin6_port != s2->sin6_port)
650 return 1;
651 if (0 != GNUNET_memcmp (&s1->sin6_addr,
652 &s2->sin6_addr))
653 return 1;
654 break;
655 }
656 default:
657 GNUNET_assert (0);
658 }
659 return 0;
660}
661
662
663/**
664 * Callback function invoked for each interface found.
665 *
666 * @param cls closure
667 * @param name name of the interface (can be NULL for unknown)
668 * @param isDefault is this presumably the default interface
669 * @param addr address of this interface (can be NULL for unknown or unassigned)
670 * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
671 * @param netmask the network mask (can be NULL for unknown or unassigned)
672 * @param addrlen length of the address
673 * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort
674 */
675static enum GNUNET_GenericReturnValue
676process_ifcs (void *cls,
677 const char *name,
678 int isDefault,
679 const struct sockaddr *addr,
680 const struct sockaddr *broadcast_addr,
681 const struct sockaddr *netmask,
682 socklen_t addrlen)
683{
684 struct Plugin *plugin = cls;
685 struct GNUNET_DHTU_Source *src;
686
687 for (src = plugin->src_head;
688 NULL != src;
689 src = src->next)
690 {
691 if ( (addrlen == src->addrlen) &&
692 (0 == addrcmp_np (addr,
693 (const struct sockaddr *) &src->addr,
694 addrlen)) )
695 {
696 src->scan_generation = plugin->scan_generation;
697 return GNUNET_OK;
698 }
699 }
700 switch (addr->sa_family)
701 {
702 case AF_INET:
703 {
704 struct sockaddr_in v4;
705
706 GNUNET_assert (sizeof(v4) == addrlen);
707 memcpy (&v4,
708 addr,
709 addrlen);
710 v4.sin_port = htons (plugin->port16);
711 (void) create_source (plugin,
712 (const struct sockaddr *) &v4,
713 sizeof (v4));
714 break;
715 }
716 case AF_INET6:
717 {
718 struct sockaddr_in6 v6;
719
720 GNUNET_assert (sizeof(v6) == addrlen);
721 memcpy (&v6,
722 addr,
723 addrlen);
724 v6.sin6_port = htons (plugin->port16);
725 (void) create_source (plugin,
726 (const struct sockaddr *) &v6,
727 sizeof (v6));
728 break;
729 }
730 }
731 return GNUNET_OK;
732}
733
734
735/**
736 * Scan network interfaces for IP address changes.
737 *
738 * @param cls a `struct Plugin`
739 */
740static void
741scan (void *cls)
742{
743 struct Plugin *plugin = cls;
744 struct GNUNET_DHTU_Source *next;
745
746 plugin->scan_generation++;
747 GNUNET_OS_network_interfaces_list (&process_ifcs,
748 plugin);
749 for (struct GNUNET_DHTU_Source *src = plugin->src_head;
750 NULL != src;
751 src = next)
752 {
753 next = src->next;
754 if (src->scan_generation >= plugin->scan_generation)
755 continue;
756 GNUNET_CONTAINER_DLL_remove (plugin->src_head,
757 plugin->src_tail,
758 src);
759 plugin->env->address_del_cb (src->app_ctx);
760 GNUNET_free (src->address);
761 GNUNET_free (src);
762 }
763 plugin->scan_task = GNUNET_SCHEDULER_add_delayed (SCAN_FREQ,
764 &scan,
765 plugin);
766}
767
768
769/**
770 * Find our source matching @a addr. If none exists,
771 * create one!
772 *
773 * @param plugin the plugin handle
774 * @param addr socket address to find
775 * @param addrlen number of bytes in @a addr
776 * @return matching source object
777 */
778static struct GNUNET_DHTU_Source *
779find_source (struct Plugin *plugin,
780 const void *addr,
781 size_t addrlen)
782{
783 for (struct GNUNET_DHTU_Source *src = plugin->src_head;
784 NULL != src;
785 src = src->next)
786 {
787 if ( (addrlen == src->addrlen) &&
788 (0 == addrcmp (addr,
789 (const struct sockaddr *) &src->addr,
790 addrlen)) )
791 return src;
792 }
793
794 return create_source (plugin,
795 (const struct sockaddr *) addr,
796 addrlen);
797}
798
799
800/**
801 * UDP socket is ready to receive. Read.
802 *
803 * @param cls our `struct Plugin *`
804 */
805static void
806read_cb (void *cls)
807{
808 struct Plugin *plugin = cls;
809 ssize_t ret;
810 const struct GNUNET_PeerIdentity *pid;
811 char buf[65536] GNUNET_ALIGN;
812 struct sockaddr_storage sa;
813 struct iovec iov = {
814 .iov_base = buf,
815 .iov_len = sizeof (buf)
816 };
817 char ctl[128];
818 struct msghdr mh = {
819 .msg_name = &sa,
820 .msg_namelen = sizeof (sa),
821 .msg_iov = &iov,
822 .msg_iovlen = 1,
823 .msg_control = ctl,
824 .msg_controllen = sizeof (ctl)
825 };
826 struct GNUNET_DHTU_Target *dst = NULL;
827 struct GNUNET_DHTU_Source *src = NULL;
828
829 ret = recvmsg (GNUNET_NETWORK_get_fd (plugin->sock),
830 &mh,
831 MSG_DONTWAIT);
832 plugin->read_task = GNUNET_SCHEDULER_add_read_net (
833 GNUNET_TIME_UNIT_FOREVER_REL,
834 plugin->sock,
835 &read_cb,
836 plugin);
837 if (ret < 0)
838 return; /* read failure, hopefully EAGAIN */
839 if (ret < sizeof (*pid))
840 {
841 GNUNET_break_op (0);
842 return;
843 }
844 /* find IP where we received message */
845 for (struct cmsghdr *cmsg = CMSG_FIRSTHDR (&mh);
846 NULL != cmsg;
847 cmsg = CMSG_NXTHDR (&mh,
848 cmsg))
849 {
850 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
851 "Got CMSG level %u (%d/%d), type %u (%d/%d)\n",
852 cmsg->cmsg_level,
853 (cmsg->cmsg_level == IPPROTO_IP),
854 (cmsg->cmsg_level == IPPROTO_IPV6),
855 cmsg->cmsg_type,
856 (cmsg->cmsg_type == IP_PKTINFO),
857 (cmsg->cmsg_type == IPV6_PKTINFO));
858 if ( (cmsg->cmsg_level == IPPROTO_IP) &&
859 (cmsg->cmsg_type == IP_PKTINFO) )
860 {
861 if (CMSG_LEN (sizeof (struct in_pktinfo)) ==
862 cmsg->cmsg_len)
863 {
864 struct in_pktinfo pi;
865
866 memcpy (&pi,
867 CMSG_DATA (cmsg),
868 sizeof (pi));
869 {
870 struct sockaddr_in sa = {
871 .sin_family = AF_INET,
872 .sin_addr = pi.ipi_addr,
873 .sin_port = htons (plugin->port16)
874 };
875
876 src = find_source (plugin,
877 &sa,
878 sizeof (sa));
879 /* For sources we discovered by reading,
880 force the generation far into the future */
881 src->scan_generation = plugin->scan_generation + 60;
882 }
883 break;
884 }
885 else
886 GNUNET_break (0);
887 }
888 if ( (cmsg->cmsg_level == IPPROTO_IPV6) &&
889 (cmsg->cmsg_type == IPV6_PKTINFO) )
890 {
891 if (CMSG_LEN (sizeof (struct in6_pktinfo)) ==
892 cmsg->cmsg_len)
893 {
894 struct in6_pktinfo pi;
895
896 memcpy (&pi,
897 CMSG_DATA (cmsg),
898 sizeof (pi));
899 {
900 struct sockaddr_in6 sa = {
901 .sin6_family = AF_INET6,
902 .sin6_addr = pi.ipi6_addr,
903 .sin6_port = htons (plugin->port16),
904 .sin6_scope_id = pi.ipi6_ifindex
905 };
906
907 src = find_source (plugin,
908 &sa,
909 sizeof (sa));
910 /* For sources we discovered by reading,
911 force the generation far into the future */
912 src->scan_generation = plugin->scan_generation + 60;
913 break;
914 }
915 }
916 else
917 GNUNET_break (0);
918 }
919 }
920 if (NULL == src)
921 {
922 GNUNET_break (0);
923 return;
924 }
925 pid = (const struct GNUNET_PeerIdentity *) buf;
926 dst = find_target (plugin,
927 pid,
928 &sa,
929 mh.msg_namelen);
930 if (NULL == dst)
931 {
932 GNUNET_break (0);
933 return;
934 }
935 plugin->env->receive_cb (plugin->env->cls,
936 &dst->app_ctx,
937 &src->app_ctx,
938 &buf[sizeof(*pid)],
939 ret - sizeof (*pid));
940}
941
942
943/**
944 * Entry point for the plugin.
945 *
946 * @param cls closure (the `struct GNUNET_DHTU_PluginEnvironment`)
947 * @return the plugin's API
948 */
949void *
950libgnunet_plugin_dhtu_ip_init (void *cls)
951{
952 struct GNUNET_DHTU_PluginEnvironment *env = cls;
953 struct GNUNET_DHTU_PluginFunctions *api;
954 struct Plugin *plugin;
955 char *port;
956 unsigned int nport;
957 int sock;
958 int af;
959 unsigned long long nse;
960
961 if (GNUNET_OK !=
962 GNUNET_CONFIGURATION_get_value_number (env->cfg,
963 "DHTU-IP",
964 "NSE",
965 &nse))
966 {
967 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
968 "DHTU-IP",
969 "NSE");
970 return NULL;
971 }
972 if (GNUNET_OK !=
973 GNUNET_CONFIGURATION_get_value_string (env->cfg,
974 "DHTU-IP",
975 "UDP_PORT",
976 &port))
977 {
978 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
979 "DHTU-IP",
980 "UDP_PORT");
981 return NULL;
982 }
983 {
984 char dummy;
985
986 if ( (1 != sscanf (port,
987 "%u%c",
988 &nport,
989 &dummy)) ||
990 (nport > UINT16_MAX) )
991 {
992 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
993 "DHTU-IP",
994 "UDP_PORT",
995 "must be number below 65536");
996 GNUNET_free (port);
997 return NULL;
998 }
999 }
1000 plugin = GNUNET_new (struct Plugin);
1001 plugin->env = env;
1002 plugin->port = port;
1003 plugin->port16 = (uint16_t) nport;
1004 if (GNUNET_OK !=
1005 GNUNET_CRYPTO_get_peer_identity (env->cfg,
1006 &plugin->my_id))
1007 {
1008 GNUNET_free (plugin);
1009 return NULL;
1010 }
1011 af = AF_INET6;
1012 sock = socket (af,
1013 SOCK_DGRAM,
1014 IPPROTO_UDP);
1015 if (-1 == sock)
1016 {
1017 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1018 "socket");
1019 GNUNET_free (plugin->port);
1020 GNUNET_free (plugin);
1021 return NULL;
1022 }
1023 switch (af)
1024 {
1025 case AF_INET:
1026 {
1027 int on = 1;
1028
1029 if (0 !=
1030 setsockopt (sock,
1031 IPPROTO_IP,
1032 IP_PKTINFO,
1033 &on,
1034 sizeof (on)))
1035 {
1036 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1037 "setsockopt");
1038 }
1039 }
1040 {
1041 struct sockaddr_in sa = {
1042 .sin_family = AF_INET,
1043 .sin_port = htons ((uint16_t) nport)
1044 };
1045
1046 if (0 !=
1047 bind (sock,
1048 (const struct sockaddr *) &sa,
1049 sizeof (sa)))
1050 {
1051 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1052 "socket");
1053 GNUNET_break (0 ==
1054 close (sock));
1055 GNUNET_free (plugin->port);
1056 GNUNET_free (plugin);
1057 return NULL;
1058 }
1059 }
1060 break;
1061 case AF_INET6:
1062 {
1063 int on = 1;
1064
1065 if (0 !=
1066 setsockopt (sock,
1067 IPPROTO_IPV6,
1068 IPV6_RECVPKTINFO,
1069 &on,
1070 sizeof (on)))
1071 {
1072 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1073 "setsockopt");
1074 }
1075 }
1076 {
1077 struct sockaddr_in6 sa = {
1078 .sin6_family = AF_INET6,
1079 .sin6_port = htons ((uint16_t) nport)
1080 };
1081
1082 if (0 !=
1083 bind (sock,
1084 (const struct sockaddr *) &sa,
1085 sizeof (sa)))
1086 {
1087 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1088 "socket");
1089 GNUNET_break (0 ==
1090 close (sock));
1091 GNUNET_free (plugin->port);
1092 GNUNET_free (plugin);
1093 return NULL;
1094 }
1095 }
1096 break;
1097 }
1098 plugin->dsts = GNUNET_CONTAINER_multihashmap_create (128,
1099 GNUNET_NO);
1100 plugin->sock = GNUNET_NETWORK_socket_box_native (sock);
1101 plugin->read_task = GNUNET_SCHEDULER_add_read_net (
1102 GNUNET_TIME_UNIT_FOREVER_REL,
1103 plugin->sock,
1104 &read_cb,
1105 plugin);
1106 env->network_size_cb (env->cls,
1107 GNUNET_TIME_UNIT_ZERO_ABS,
1108 log (nse) / log (2),
1109 -1.0 /* stddev */);
1110 plugin->scan_task = GNUNET_SCHEDULER_add_now (&scan,
1111 plugin);
1112 api = GNUNET_new (struct GNUNET_DHTU_PluginFunctions);
1113 api->cls = plugin;
1114 api->try_connect = &ip_try_connect;
1115 api->hold = &ip_hold;
1116 api->drop = &ip_drop;
1117 api->send = &ip_send;
1118 return api;
1119}
1120
1121
1122/**
1123 * Exit point from the plugin.
1124 *
1125 * @param cls closure (our `struct Plugin`)
1126 * @return NULL
1127 */
1128void *
1129libgnunet_plugin_dhtu_ip_done (void *cls)
1130{
1131 struct GNUNET_DHTU_PluginFunctions *api = cls;
1132 struct Plugin *plugin = api->cls;
1133 struct GNUNET_DHTU_Source *src;
1134 struct GNUNET_DHTU_Target *dst;
1135
1136 while (NULL != (dst = plugin->dst_head))
1137 {
1138 plugin->env->disconnect_cb (dst->app_ctx);
1139 GNUNET_assert (NULL == dst->ph_head);
1140 GNUNET_CONTAINER_DLL_remove (plugin->dst_head,
1141 plugin->dst_tail,
1142 dst);
1143 GNUNET_free (dst);
1144 }
1145 while (NULL != (src = plugin->src_head))
1146 {
1147 plugin->env->address_del_cb (src->app_ctx);
1148 GNUNET_CONTAINER_DLL_remove (plugin->src_head,
1149 plugin->src_tail,
1150 src);
1151 GNUNET_free (src->address);
1152 GNUNET_free (src);
1153 }
1154 plugin->env->network_size_cb (plugin->env->cls,
1155 GNUNET_TIME_UNIT_FOREVER_ABS,
1156 0.0,
1157 0.0);
1158 GNUNET_CONTAINER_multihashmap_destroy (plugin->dsts);
1159 if (NULL != plugin->read_task)
1160 {
1161 GNUNET_SCHEDULER_cancel (plugin->read_task);
1162 plugin->read_task = NULL;
1163 }
1164 GNUNET_SCHEDULER_cancel (plugin->scan_task);
1165 GNUNET_break (GNUNET_OK ==
1166 GNUNET_NETWORK_socket_close (plugin->sock));
1167 GNUNET_free (plugin->port);
1168 GNUNET_free (plugin);
1169 GNUNET_free (api);
1170 return NULL;
1171}
diff --git a/src/plugin/dhtu/test_dhtu_ip.c b/src/plugin/dhtu/test_dhtu_ip.c
deleted file mode 100644
index 030b17b5f..000000000
--- a/src/plugin/dhtu/test_dhtu_ip.c
+++ /dev/null
@@ -1,45 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021 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 dhtu/test_dhtu_ip.c
23 * @brief Test case for the DHTU implementation for IP
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_testing_netjail_lib.h"
28#include "gnunet_util_lib.h"
29
30#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120)
31
32#define CONFIG_FILE "test_dhtu_ip.conf"
33
34
35int
36main (int argc,
37 char *const *argv)
38{
39 struct GNUNET_TESTING_Command commands[] = {
40 GNUNET_TESTING_cmd_end ()
41 };
42
43 return GNUNET_TESTING_main (commands,
44 TIMEOUT);
45}
diff --git a/src/plugin/dhtu/testing_dhtu_cmd_send.c b/src/plugin/dhtu/testing_dhtu_cmd_send.c
deleted file mode 100644
index 45d166b14..000000000
--- a/src/plugin/dhtu/testing_dhtu_cmd_send.c
+++ /dev/null
@@ -1,119 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021 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 testing/testing_dhtu_cmd_send.c
23 * @brief use DHTU to send a message
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_testing_ng_lib.h"
28#include "gnunet_testing_netjail_lib.h"
29
30
31/**
32 * State for the 'send' command.
33 */
34struct SendState
35{
36
37 /**
38 * Mandatory context for async commands.
39 */
40 struct GNUNET_TESTING_AsyncContext ac;
41
42};
43
44
45/**
46 *
47 *
48 * @param cls a `struct SendState`
49 */
50static void
51send_cleanup (void *cls)
52{
53 struct SendState *ss = cls;
54
55 GNUNET_free (ss);
56}
57
58
59/**
60 * Return trains of the ``send`` command.
61 *
62 * @param cls closure.
63 * @param[out] ret result
64 * @param trait name of the trait.
65 * @param index index number of the object to offer.
66 * @return #GNUNET_OK on success.
67 * #GNUNET_NO if no trait was found
68 */
69static enum GNUNET_GenericReturnValue
70send_traits (void *cls,
71 const void **ret,
72 const char *trait,
73 unsigned int index)
74{
75 return GNUNET_NO;
76}
77
78
79/**
80 * Run the 'send' command.
81 *
82 * @param cls closure.
83 * @param is interpreter state.
84 */
85static void
86send_run (void *cls,
87 struct GNUNET_TESTING_Interpreter *is)
88{
89 struct SendState *ss = cls;
90
91#if 0
92 other_cmd = GNUNET_TESTING_interpreter_lookup_command (ss->other_label);
93 GNUNET_TESTING_get_trait_XXX (other_cmd,
94 &data);
95#endif
96 GNUNET_TESTING_async_finish (&ss->ac);
97}
98
99
100struct GNUNET_TESTING_Command
101GNUNET_TESTING_DHTU_cmd_send (const char *label)
102{
103 struct SendState *ss;
104
105 ss = GNUNET_new (struct SendState);
106
107 {
108 struct GNUNET_TESTING_Command cmd = {
109 .cls = ss,
110 .run = &send_run,
111 .ac = &ss->ac,
112 .cleanup = &send_cleanup,
113 .traits = &send_traits
114 };
115 strncpy (cmd.label, label, GNUNET_TESTING_CMD_MAX_LABEL_LENGTH);
116
117 return cmd;
118 }
119}
diff --git a/src/plugin/dns/Makefile.am b/src/plugin/dns/Makefile.am
new file mode 100644
index 000000000..8b5843159
--- /dev/null
+++ b/src/plugin/dns/Makefile.am
@@ -0,0 +1,25 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage -O0
6endif
7
8pkgcfgdir= $(pkgdatadir)/config.d/
9
10libexecdir= $(pkglibdir)/libexec/
11
12plugindir = $(libdir)/gnunet
13
14plugin_LTLIBRARIES = \
15 libgnunet_plugin_block_dns.la
16
17libgnunet_plugin_block_dns_la_SOURCES = \
18 plugin_block_dns.c
19libgnunet_plugin_block_dns_la_LIBADD = \
20 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \
21 $(top_builddir)/src/lib/block/libgnunetblock.la \
22 $(top_builddir)/src/lib/util/libgnunetutil.la
23libgnunet_plugin_block_dns_la_LDFLAGS = \
24 $(GN_LIBINTL) \
25 $(top_builddir)/src/block/$(GN_PLUGIN_LDFLAGS)
diff --git a/src/plugin/dns/meson.build b/src/plugin/dns/meson.build
new file mode 100644
index 000000000..974b2c38d
--- /dev/null
+++ b/src/plugin/dns/meson.build
@@ -0,0 +1,6 @@
1shared_module('gnunet_plugin_block_dns',
2 ['plugin_block_dns.c'],
3 dependencies: [libgnunetutil_dep, libgnunetblockgroup_dep],
4 include_directories: [incdir, configuration_inc],
5 install: true,
6 install_dir: get_option('libdir')/'gnunet')
diff --git a/src/plugin/dns/plugin_block_dns.c b/src/plugin/dns/plugin_block_dns.c
new file mode 100644
index 000000000..1bbd7f750
--- /dev/null
+++ b/src/plugin/dns/plugin_block_dns.c
@@ -0,0 +1,290 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013, 2017 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 dns/plugin_block_dns.c
23 * @brief block plugin for advertising a DNS exit service
24 * @author Christian Grothoff
25 *
26 * Note that this plugin might more belong with EXIT and PT
27 * as those two are using this type of block. Still, this
28 * might be a natural enough place for people to find the code...
29 */
30#include "platform.h"
31#include "gnunet_block_plugin.h"
32#include "block_dns.h"
33#include "gnunet_signatures.h"
34#include "gnunet_block_group_lib.h"
35
36
37/**
38 * Number of bits we set per entry in the bloomfilter.
39 * Do not change!
40 */
41#define BLOOMFILTER_K 16
42
43
44/**
45 * Create a new block group.
46 *
47 * @param ctx block context in which the block group is created
48 * @param type type of the block for which we are creating the group
49 * @param raw_data optional serialized prior state of the group, NULL if unavailable/fresh
50 * @param raw_data_size number of bytes in @a raw_data, 0 if unavailable/fresh
51 * @param va variable arguments specific to @a type
52 * @return block group handle, NULL if block groups are not supported
53 * by this @a type of block (this is not an error)
54 */
55static struct GNUNET_BLOCK_Group *
56block_plugin_dns_create_group (void *cls,
57 enum GNUNET_BLOCK_Type type,
58 const void *raw_data,
59 size_t raw_data_size,
60 va_list va)
61{
62 unsigned int bf_size;
63 const char *guard;
64
65 guard = va_arg (va, const char *);
66 if (0 == strcmp (guard,
67 "seen-set-size"))
68 bf_size = GNUNET_BLOCK_GROUP_compute_bloomfilter_size (va_arg (va, unsigned
69 int),
70 BLOOMFILTER_K);
71 else if (0 == strcmp (guard,
72 "filter-size"))
73 bf_size = va_arg (va, unsigned int);
74 else
75 {
76 GNUNET_break (0);
77 bf_size = 8;
78 }
79 GNUNET_break (NULL == va_arg (va, const char *));
80 return GNUNET_BLOCK_GROUP_bf_create (cls,
81 bf_size,
82 BLOOMFILTER_K,
83 type,
84 raw_data,
85 raw_data_size);
86}
87
88
89/**
90 * Function called to validate a query.
91 *
92 * @param cls closure
93 * @param type block type
94 * @param query original query (hash)
95 * @param xquery extrended query data (can be NULL, depending on type)
96 * @param xquery_size number of bytes in @a xquery
97 * @return #GNUNET_OK if the query is fine, #GNUNET_NO if not
98 */
99static enum GNUNET_GenericReturnValue
100block_plugin_dns_check_query (void *cls,
101 enum GNUNET_BLOCK_Type type,
102 const struct GNUNET_HashCode *query,
103 const void *xquery,
104 size_t xquery_size)
105{
106 switch (type)
107 {
108 case GNUNET_BLOCK_TYPE_DNS:
109 if (0 != xquery_size)
110 {
111 GNUNET_break_op (0);
112 return GNUNET_NO;
113 }
114 return GNUNET_OK;
115 default:
116 GNUNET_break (0);
117 return GNUNET_SYSERR;
118 }
119}
120
121
122/**
123 * Function called to validate a block for storage.
124 *
125 * @param cls closure
126 * @param type block type
127 * @param block block data to validate
128 * @param block_size number of bytes in @a block
129 * @return #GNUNET_OK if the block is fine, #GNUNET_NO if not
130 */
131static enum GNUNET_GenericReturnValue
132block_plugin_dns_check_block (void *cls,
133 enum GNUNET_BLOCK_Type type,
134 const void *block,
135 size_t block_size)
136{
137 const struct GNUNET_DNS_Advertisement *ad;
138
139 switch (type)
140 {
141 case GNUNET_BLOCK_TYPE_DNS:
142 if (sizeof(struct GNUNET_DNS_Advertisement) != block_size)
143 {
144 GNUNET_break_op (0);
145 return GNUNET_NO;
146 }
147 ad = block;
148
149 if (ntohl (ad->purpose.size) !=
150 sizeof(struct GNUNET_DNS_Advertisement)
151 - sizeof(struct GNUNET_CRYPTO_EddsaSignature))
152 {
153 GNUNET_break_op (0);
154 return GNUNET_NO;
155 }
156 if (GNUNET_TIME_absolute_is_past (
157 GNUNET_TIME_absolute_ntoh (ad->expiration_time)))
158 {
159 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
160 "DNS advertisement has expired\n");
161 return GNUNET_NO;
162 }
163 if (GNUNET_OK !=
164 GNUNET_CRYPTO_eddsa_verify_ (GNUNET_SIGNATURE_PURPOSE_DNS_RECORD,
165 &ad->purpose,
166 &ad->signature,
167 &ad->peer.public_key))
168 {
169 GNUNET_break_op (0);
170 return GNUNET_NO;
171 }
172 return GNUNET_OK;
173 default:
174 GNUNET_break (0);
175 return GNUNET_SYSERR;
176 }
177}
178
179
180/**
181 * Function called to validate a reply to a request. Note that it is assumed
182 * that the reply has already been matched to the key (and signatures checked)
183 * as it would be done with the GetKeyFunction and the
184 * BlockEvaluationFunction.
185 *
186 * @param cls closure
187 * @param type block type
188 * @param group which block group to use for evaluation
189 * @param query original query (hash)
190 * @param xquery extrended query data (can be NULL, depending on type)
191 * @param xquery_size number of bytes in @a xquery
192 * @param reply_block response to validate
193 * @param reply_block_size number of bytes in @a reply_block
194 * @return characterization of result
195 */
196static enum GNUNET_BLOCK_ReplyEvaluationResult
197block_plugin_dns_check_reply (
198 void *cls,
199 enum GNUNET_BLOCK_Type type,
200 struct GNUNET_BLOCK_Group *group,
201 const struct GNUNET_HashCode *query,
202 const void *xquery,
203 size_t xquery_size,
204 const void *reply_block,
205 size_t reply_block_size)
206{
207 struct GNUNET_HashCode phash;
208
209 switch (type)
210 {
211 case GNUNET_BLOCK_TYPE_DNS:
212 GNUNET_CRYPTO_hash (reply_block,
213 reply_block_size,
214 &phash);
215 if (GNUNET_YES ==
216 GNUNET_BLOCK_GROUP_bf_test_and_set (group,
217 &phash))
218 return GNUNET_BLOCK_REPLY_OK_DUPLICATE;
219 return GNUNET_BLOCK_REPLY_OK_MORE;
220 default:
221 GNUNET_break (0);
222 return GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED;
223 }
224}
225
226
227/**
228 * Function called to obtain the key for a block.
229 *
230 * @param cls closure
231 * @param type block type
232 * @param block block to get the key for
233 * @param block_size number of bytes in @a block
234 * @param key set to the key (query) for the given block
235 * @return #GNUNET_OK on success, #GNUNET_SYSERR if type not supported
236 * (or if extracting a key from a block of this type does not work)
237 */
238static enum GNUNET_GenericReturnValue
239block_plugin_dns_get_key (void *cls,
240 enum GNUNET_BLOCK_Type type,
241 const void *block,
242 size_t block_size,
243 struct GNUNET_HashCode *key)
244{
245 if (GNUNET_BLOCK_TYPE_DNS != type)
246 {
247 GNUNET_break (0);
248 return GNUNET_SYSERR;
249 }
250 return GNUNET_NO;
251}
252
253
254/**
255 * Entry point for the plugin.
256 */
257void *
258libgnunet_plugin_block_dns_init (void *cls)
259{
260 static enum GNUNET_BLOCK_Type types[] = {
261 GNUNET_BLOCK_TYPE_DNS,
262 GNUNET_BLOCK_TYPE_ANY /* end of list */
263 };
264 struct GNUNET_BLOCK_PluginFunctions *api;
265
266 api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions);
267 api->get_key = &block_plugin_dns_get_key;
268 api->check_query = &block_plugin_dns_check_query;
269 api->check_block = &block_plugin_dns_check_block;
270 api->check_reply = &block_plugin_dns_check_reply;
271 api->create_group = &block_plugin_dns_create_group;
272 api->types = types;
273 return api;
274}
275
276
277/**
278 * Exit point from the plugin.
279 */
280void *
281libgnunet_plugin_block_dns_done (void *cls)
282{
283 struct GNUNET_BLOCK_PluginFunctions *api = cls;
284
285 GNUNET_free (api);
286 return NULL;
287}
288
289
290/* end of plugin_block_dns.c */
diff --git a/src/plugin/fs/Makefile.am b/src/plugin/fs/Makefile.am
index 928997e4d..7c0e55cba 100644
--- a/src/plugin/fs/Makefile.am
+++ b/src/plugin/fs/Makefile.am
@@ -20,7 +20,6 @@ libgnunet_plugin_block_fs_la_SOURCES = \
20libgnunet_plugin_block_fs_la_LIBADD = \ 20libgnunet_plugin_block_fs_la_LIBADD = \
21 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \ 21 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \
22 $(top_builddir)/src/lib/block/libgnunetblock.la \ 22 $(top_builddir)/src/lib/block/libgnunetblock.la \
23 $(top_builddir)/src/service/fs/libgnunetfs.la \
24 $(top_builddir)/src/lib/util/libgnunetutil.la \ 23 $(top_builddir)/src/lib/util/libgnunetutil.la \
25 $(LTLIBINTL) 24 $(LTLIBINTL)
26libgnunet_plugin_block_fs_la_LDFLAGS = \ 25libgnunet_plugin_block_fs_la_LDFLAGS = \
diff --git a/src/plugin/gns/Makefile.am b/src/plugin/gns/Makefile.am
index ebbb9aa51..8dc9ef2b2 100644
--- a/src/plugin/gns/Makefile.am
+++ b/src/plugin/gns/Makefile.am
@@ -34,7 +34,6 @@ libgnunet_plugin_gnsrecord_gns_la_SOURCES = \
34 plugin_gnsrecord_gns.c 34 plugin_gnsrecord_gns.c
35libgnunet_plugin_gnsrecord_gns_la_LIBADD = \ 35libgnunet_plugin_gnsrecord_gns_la_LIBADD = \
36 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ 36 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
37 $(top_builddir)/src/service/identity/libgnunetidentity.la \
38 $(top_builddir)/src/lib/util/libgnunetutil.la \ 37 $(top_builddir)/src/lib/util/libgnunetutil.la \
39 $(LTLIBINTL) 38 $(LTLIBINTL)
40libgnunet_plugin_gnsrecord_gns_la_LDFLAGS = \ 39libgnunet_plugin_gnsrecord_gns_la_LDFLAGS = \
@@ -47,7 +46,6 @@ libgnunet_plugin_block_gns_la_LIBADD = \
47 $(top_builddir)/src/lib/util/libgnunetutil.la \ 46 $(top_builddir)/src/lib/util/libgnunetutil.la \
48 $(top_builddir)/src/lib/block/libgnunetblock.la \ 47 $(top_builddir)/src/lib/block/libgnunetblock.la \
49 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \ 48 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \
50 $(top_builddir)/src/service/identity/libgnunetidentity.la \
51 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la 49 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la
52libgnunet_plugin_block_gns_la_LDFLAGS = \ 50libgnunet_plugin_block_gns_la_LDFLAGS = \
53 $(GN_LIBINTL) \ 51 $(GN_LIBINTL) \
diff --git a/src/plugin/gns/meson.build b/src/plugin/gns/meson.build
index 0e551b22e..2cdf770f8 100644
--- a/src/plugin/gns/meson.build
+++ b/src/plugin/gns/meson.build
@@ -2,7 +2,7 @@ shared_module('gnunet_plugin_gnsrecord_gns',
2 ['plugin_gnsrecord_gns.c'], 2 ['plugin_gnsrecord_gns.c'],
3 dependencies: [libgnunetutil_dep, 3 dependencies: [libgnunetutil_dep,
4 libgnunetgnsrecord_dep, 4 libgnunetgnsrecord_dep,
5 libgnunetidentity_dep], 5 ],
6 include_directories: [incdir, configuration_inc], 6 include_directories: [incdir, configuration_inc],
7 install: true, 7 install: true,
8 install_dir: get_option('libdir')/'gnunet') 8 install_dir: get_option('libdir')/'gnunet')
diff --git a/src/plugin/meson.build b/src/plugin/meson.build
index 6ebae59d7..d4f797c54 100644
--- a/src/plugin/meson.build
+++ b/src/plugin/meson.build
@@ -1,9 +1,17 @@
1subdir('block') 1subdir('block')
2subdir('dhtu') 2subdir('dns')
3subdir('gnsrecord') 3subdir('gnsrecord')
4subdir('datacache')
5subdir('datastore')
6subdir('peerstore')
4subdir('namecache') 7subdir('namecache')
5subdir('namestore') 8subdir('namestore')
9subdir('dht')
10subdir('seti')
11subdir('setu')
12subdir('regex')
6subdir('revocation') 13subdir('revocation')
7subdir('gns') 14subdir('gns')
8subdir('fs') 15subdir('fs')
9subdir('reclaim') 16subdir('reclaim')
17subdir('messenger')
diff --git a/src/plugin/messenger/Makefile.am b/src/plugin/messenger/Makefile.am
new file mode 100644
index 000000000..e3c69ea33
--- /dev/null
+++ b/src/plugin/messenger/Makefile.am
@@ -0,0 +1,26 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage -O0
6 XLIB = -lgcov
7endif
8
9pkgcfgdir= $(pkgdatadir)/config.d/
10
11libexecdir= $(pkglibdir)/libexec/
12
13plugin_LTLIBRARIES = \
14 libgnunet_plugin_gnsrecord_messenger.la
15
16
17libgnunet_plugin_gnsrecord_messenger_la_SOURCES = \
18 plugin_gnsrecord_messenger.c
19libgnunet_plugin_gnsrecord_messenger_la_LIBADD = \
20 $(top_builddir)/src/lib/util/libgnunetutil.la \
21 $(LTLIBINTL)
22libgnunet_plugin_gnsrecord_messenger_la_LDFLAGS = \
23 $(GN_PLUGIN_LDFLAGS)
24
25
26plugindir = $(libdir)/gnunet
diff --git a/src/plugin/messenger/meson.build b/src/plugin/messenger/meson.build
new file mode 100644
index 000000000..17dd9bd32
--- /dev/null
+++ b/src/plugin/messenger/meson.build
@@ -0,0 +1,7 @@
1shared_module('gnunet_plugin_gnsrecord_messenger',
2 ['plugin_gnsrecord_messenger.c'],
3 dependencies: [libgnunetutil_dep, libgnunetgnsrecord_dep,
4 ],
5 include_directories: [incdir, configuration_inc],
6 install: true,
7 install_dir: get_option('libdir')/'gnunet')
diff --git a/src/plugin/messenger/plugin_gnsrecord_messenger.c b/src/plugin/messenger/plugin_gnsrecord_messenger.c
new file mode 100644
index 000000000..e09a0330d
--- /dev/null
+++ b/src/plugin/messenger/plugin_gnsrecord_messenger.c
@@ -0,0 +1,298 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2021--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 * @author Tobias Frisch
22 * @file src/messenger/plugin_gnsrecord_messenger.c
23 * @brief Plugin to provide the API for useful GNS records to improve
24 * the usability of the messenger service.
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_gnsrecord_lib.h"
30#include "gnunet_messenger_service.h"
31#include "gnunet_gnsrecord_plugin.h"
32
33
34/**
35 * Convert the 'value' of a record to a string.
36 *
37 * @param cls closure, unused
38 * @param type type of the record
39 * @param data value in binary encoding
40 * @param data_size number of bytes in @a data
41 * @return NULL on error, otherwise human-readable representation of the value
42 */
43static char *
44messenger_value_to_string (void *cls,
45 uint32_t type,
46 const void *data,
47 size_t data_size)
48{
49 (void) cls;
50 switch (type)
51 {
52 case GNUNET_GNSRECORD_TYPE_MESSENGER_ROOM_ENTRY:
53 {
54 if (data_size != sizeof(struct GNUNET_MESSENGER_RoomEntryRecord))
55 {
56 GNUNET_break_op (0);
57 return NULL;
58 }
59
60 const struct GNUNET_MESSENGER_RoomEntryRecord *record = data;
61
62 char *door = GNUNET_CRYPTO_eddsa_public_key_to_string (&(record->door.public_key));
63 char *key = GNUNET_STRINGS_data_to_string_alloc (&(record->key), sizeof(struct GNUNET_HashCode));
64
65 char *ret;
66 GNUNET_asprintf (&ret, "%s-%s", key, door);
67 GNUNET_free (key);
68 GNUNET_free (door);
69 return ret;
70 }
71 case GNUNET_GNSRECORD_TYPE_MESSENGER_ROOM_DETAILS:
72 {
73 if (data_size != sizeof(struct GNUNET_MESSENGER_RoomDetailsRecord))
74 {
75 GNUNET_break_op (0);
76 return NULL;
77 }
78
79 const struct GNUNET_MESSENGER_RoomDetailsRecord *record = data;
80
81 char *name = GNUNET_strndup(record->name, 256);
82 char *flags = GNUNET_STRINGS_data_to_string_alloc (&(record->flags), sizeof(uint32_t));
83
84 char *ret;
85 GNUNET_asprintf (&ret, "%s-%s", flags, name);
86 GNUNET_free (flags);
87 GNUNET_free (name);
88 return ret;
89 }
90 default:
91 return NULL;
92 }
93}
94
95
96/**
97 * Convert human-readable version of a 'value' of a record to the binary
98 * representation.
99 *
100 * @param cls closure, unused
101 * @param type type of the record
102 * @param s human-readable string
103 * @param data set to value in binary encoding (will be allocated)
104 * @param data_size set to number of bytes in @a data
105 * @return #GNUNET_OK on success
106 */
107static int
108messenger_string_to_value (void *cls,
109 uint32_t type,
110 const char *s,
111 void **data,
112 size_t *data_size)
113{
114 (void) cls;
115 if (NULL == s)
116 {
117 GNUNET_break (0);
118 return GNUNET_SYSERR;
119 }
120
121 switch (type)
122 {
123 case GNUNET_GNSRECORD_TYPE_MESSENGER_ROOM_ENTRY:
124 {
125 char key [103];
126 const char *dash;
127 struct GNUNET_PeerIdentity door;
128
129 if ((NULL == (dash = strchr (s, '-'))) ||
130 (1 != sscanf (s, "%103s-", key)) ||
131 (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (dash + 1,
132 strlen (dash + 1),
133 &(door.public_key))))
134 {
135 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
136 _ ("Unable to parse MESSENGER_ROOM_ENTRY record `%s'\n"),
137 s);
138 return GNUNET_SYSERR;
139 }
140
141 struct GNUNET_MESSENGER_RoomEntryRecord *record = GNUNET_new (
142 struct GNUNET_MESSENGER_RoomEntryRecord
143 );
144
145 if (GNUNET_OK != GNUNET_STRINGS_string_to_data (key,
146 strlen (key),
147 &(record->key),
148 sizeof(struct GNUNET_HashCode)))
149 {
150 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
151 _ ("Unable to parse MESSENGER_ROOM_ENTRY record `%s'\n"),
152 s);
153 GNUNET_free (record);
154 return GNUNET_SYSERR;
155 }
156
157 record->door = door;
158 *data = record;
159 *data_size = sizeof(struct GNUNET_MESSENGER_RoomEntryRecord);
160 return GNUNET_OK;
161 }
162 case GNUNET_GNSRECORD_TYPE_MESSENGER_ROOM_DETAILS:
163 {
164 char flags [7];
165 const char *dash;
166
167 if ((NULL == (dash = strchr (s, '-'))) ||
168 (1 != sscanf (s, "%7s-", flags)) ||
169 (strlen (dash + 1) > 256))
170 {
171 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
172 _ ("Unable to parse MESSENGER_ROOM_DETAILS record `%s'\n"),
173 s);
174 return GNUNET_SYSERR;
175 }
176
177 struct GNUNET_MESSENGER_RoomDetailsRecord *record = GNUNET_new (
178 struct GNUNET_MESSENGER_RoomDetailsRecord
179 );
180
181 if (GNUNET_OK != GNUNET_STRINGS_string_to_data (flags,
182 strlen (flags),
183 &(record->flags),
184 sizeof(uint32_t)))
185 {
186 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
187 _ ("Unable to parse MESSENGER_ROOM_DETAILS record `%s'\n"),
188 s);
189 GNUNET_free (record);
190 return GNUNET_SYSERR;
191 }
192
193 GNUNET_memcpy(record->name, dash + 1, strlen(dash + 1));
194
195 *data = record;
196 *data_size = sizeof(struct GNUNET_MESSENGER_RoomDetailsRecord);
197 return GNUNET_OK;
198 }
199 default:
200 return GNUNET_SYSERR;
201 }
202}
203
204
205/**
206 * Mapping of record type numbers to human-readable
207 * record type names.
208 */
209static struct
210{
211 const char *name;
212 uint32_t number;
213} name_map[] = {
214 { "MESSENGER_ROOM_ENTRY", GNUNET_GNSRECORD_TYPE_MESSENGER_ROOM_ENTRY },
215 { "MESSENGER_ROOM_DETAILS", GNUNET_GNSRECORD_TYPE_MESSENGER_ROOM_DETAILS },
216 { NULL, UINT32_MAX }
217};
218
219
220/**
221 * Convert a type name (e.g. "AAAA") to the corresponding number.
222 *
223 * @param cls closure, unused
224 * @param gns_typename name to convert
225 * @return corresponding number, UINT32_MAX on error
226 */
227static uint32_t
228messenger_typename_to_number (void *cls,
229 const char *gns_typename)
230{
231 unsigned int i;
232
233 (void) cls;
234 i = 0;
235 while ((name_map[i].name != NULL) &&
236 (0 != strcasecmp (gns_typename, name_map[i].name)))
237 i++;
238 return name_map[i].number;
239}
240
241
242/**
243 * Convert a type number to the corresponding type string (e.g. 1 to "A")
244 *
245 * @param cls closure, unused
246 * @param type number of a type to convert
247 * @return corresponding typestring, NULL on error
248 */
249static const char *
250messenger_number_to_typename (void *cls,
251 uint32_t type)
252{
253 unsigned int i;
254
255 (void) cls;
256 i = 0;
257 while ((name_map[i].name != NULL) &&
258 (type != name_map[i].number))
259 i++;
260 return name_map[i].name;
261}
262
263
264/**
265 * Entry point for the plugin.
266 *
267 * @param cls NULL
268 * @return the exported block API
269 */
270void *
271libgnunet_plugin_gnsrecord_messenger_init (void *cls)
272{
273 struct GNUNET_GNSRECORD_PluginFunctions *api;
274
275 (void) cls;
276 api = GNUNET_new (struct GNUNET_GNSRECORD_PluginFunctions);
277 api->value_to_string = &messenger_value_to_string;
278 api->string_to_value = &messenger_string_to_value;
279 api->typename_to_number = &messenger_typename_to_number;
280 api->number_to_typename = &messenger_number_to_typename;
281 return api;
282}
283
284
285/**
286 * Exit point from the plugin.
287 *
288 * @param cls the return value from #libgnunet_plugin_block_test_init
289 * @return NULL
290 */
291void *
292libgnunet_plugin_gnsrecord_messenger_done (void *cls)
293{
294 struct GNUNET_GNSRECORD_PluginFunctions *api = cls;
295
296 GNUNET_free (api);
297 return NULL;
298}
diff --git a/src/plugin/namecache/Makefile.am b/src/plugin/namecache/Makefile.am
index 0ccbf9ea1..bf8fc9b8d 100644
--- a/src/plugin/namecache/Makefile.am
+++ b/src/plugin/namecache/Makefile.am
@@ -56,10 +56,7 @@ plugin_LTLIBRARIES = \
56libgnunet_plugin_namecache_flat_la_SOURCES = \ 56libgnunet_plugin_namecache_flat_la_SOURCES = \
57 plugin_namecache_flat.c 57 plugin_namecache_flat.c
58libgnunet_plugin_namecache_flat_la_LIBADD = \ 58libgnunet_plugin_namecache_flat_la_LIBADD = \
59 $(top_builddir)/src/service/namecache/libgnunetnamecache.la \
60 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
61 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ 59 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
62 $(top_builddir)/src/service/identity/libgnunetidentity.la \
63 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \ 60 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
64 $(LTLIBINTL) 61 $(LTLIBINTL)
65libgnunet_plugin_namecache_flat_la_LDFLAGS = \ 62libgnunet_plugin_namecache_flat_la_LDFLAGS = \
@@ -68,11 +65,8 @@ libgnunet_plugin_namecache_flat_la_LDFLAGS = \
68libgnunet_plugin_namecache_sqlite_la_SOURCES = \ 65libgnunet_plugin_namecache_sqlite_la_SOURCES = \
69 plugin_namecache_sqlite.c 66 plugin_namecache_sqlite.c
70libgnunet_plugin_namecache_sqlite_la_LIBADD = \ 67libgnunet_plugin_namecache_sqlite_la_LIBADD = \
71 $(top_builddir)/src/service/namecache/libgnunetnamecache.la \
72 $(top_builddir)/src/lib/sq/libgnunetsq.la \ 68 $(top_builddir)/src/lib/sq/libgnunetsq.la \
73 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
74 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ 69 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
75 $(top_builddir)/src/service/identity/libgnunetidentity.la \
76 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lsqlite3 \ 70 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lsqlite3 \
77 $(LTLIBINTL) 71 $(LTLIBINTL)
78libgnunet_plugin_namecache_sqlite_la_LDFLAGS = \ 72libgnunet_plugin_namecache_sqlite_la_LDFLAGS = \
@@ -82,11 +76,8 @@ libgnunet_plugin_namecache_sqlite_la_LDFLAGS = \
82libgnunet_plugin_namecache_postgres_la_SOURCES = \ 76libgnunet_plugin_namecache_postgres_la_SOURCES = \
83 plugin_namecache_postgres.c 77 plugin_namecache_postgres.c
84libgnunet_plugin_namecache_postgres_la_LIBADD = \ 78libgnunet_plugin_namecache_postgres_la_LIBADD = \
85 $(top_builddir)/src/service/namecache/libgnunetnamecache.la \
86 $(top_builddir)/src/lib/pq/libgnunetpq.la \ 79 $(top_builddir)/src/lib/pq/libgnunetpq.la \
87 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
88 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ 80 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
89 $(top_builddir)/src/service/identity/libgnunetidentity.la \
90 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lpq \ 81 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lpq \
91 $(LTLIBINTL) 82 $(LTLIBINTL)
92libgnunet_plugin_namecache_postgres_la_LDFLAGS = \ 83libgnunet_plugin_namecache_postgres_la_LDFLAGS = \
diff --git a/src/plugin/namestore/Makefile.am b/src/plugin/namestore/Makefile.am
index e413c1b08..03b743d7a 100644
--- a/src/plugin/namestore/Makefile.am
+++ b/src/plugin/namestore/Makefile.am
@@ -76,37 +76,16 @@ TESTS = \
76 $(check_SCRIPTS) 76 $(check_SCRIPTS)
77endif 77endif
78 78
79REST_PLUGIN = libgnunet_plugin_rest_namestore.la
80
81plugin_LTLIBRARIES = \ 79plugin_LTLIBRARIES = \
82 $(SQLITE_PLUGIN) \ 80 $(SQLITE_PLUGIN) \
83 $(POSTGRES_PLUGIN) \ 81 $(POSTGRES_PLUGIN)
84 $(REST_PLUGIN)
85
86
87libgnunet_plugin_rest_namestore_la_SOURCES = \
88 plugin_rest_namestore.c
89libgnunet_plugin_rest_namestore_la_LIBADD = \
90 $(top_builddir)/src/service/namestore/libgnunetnamestore.la \
91 $(top_builddir)/src/service/rest/libgnunetrest.la \
92 $(top_builddir)/src/service/identity/libgnunetidentity.la \
93 $(top_builddir)/src/lib/json/libgnunetjson.la \
94 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
95 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecordjson.la \
96 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
97 $(LTLIBINTL) -ljansson $(MHD_LIBS)
98libgnunet_plugin_rest_namestore_la_LDFLAGS = \
99 $(GN_PLUGIN_LDFLAGS)
100libgnunet_plugin_rest_namestore_la_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS)
101 82
102 83
103libgnunet_plugin_namestore_sqlite_la_SOURCES = \ 84libgnunet_plugin_namestore_sqlite_la_SOURCES = \
104 plugin_namestore_sqlite.c 85 plugin_namestore_sqlite.c
105libgnunet_plugin_namestore_sqlite_la_LIBADD = \ 86libgnunet_plugin_namestore_sqlite_la_LIBADD = \
106 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ 87 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
107 $(top_builddir)/src/service/identity/libgnunetidentity.la \
108 $(top_builddir)/src/lib/sq/libgnunetsq.la \ 88 $(top_builddir)/src/lib/sq/libgnunetsq.la \
109 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
110 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lsqlite3 \ 89 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lsqlite3 \
111 $(LTLIBINTL) 90 $(LTLIBINTL)
112libgnunet_plugin_namestore_sqlite_la_LDFLAGS = \ 91libgnunet_plugin_namestore_sqlite_la_LDFLAGS = \
@@ -116,9 +95,7 @@ libgnunet_plugin_namestore_postgres_la_SOURCES = \
116 plugin_namestore_postgres.c 95 plugin_namestore_postgres.c
117libgnunet_plugin_namestore_postgres_la_LIBADD = \ 96libgnunet_plugin_namestore_postgres_la_LIBADD = \
118 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ 97 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
119 $(top_builddir)/src/service/identity/libgnunetidentity.la \
120 $(top_builddir)/src/lib/pq/libgnunetpq.la \ 98 $(top_builddir)/src/lib/pq/libgnunetpq.la \
121 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
122 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lpq \ 99 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lpq \
123 $(LTLIBINTL) 100 $(LTLIBINTL)
124libgnunet_plugin_namestore_postgres_la_LDFLAGS = \ 101libgnunet_plugin_namestore_postgres_la_LDFLAGS = \
@@ -422,9 +399,6 @@ test_plugin_namestore_postgres_LDADD = \
422 $(top_builddir)/src/service/testing/libgnunettesting.la \ 399 $(top_builddir)/src/service/testing/libgnunettesting.la \
423 $(top_builddir)/src/lib/util/libgnunetutil.la 400 $(top_builddir)/src/lib/util/libgnunetutil.la
424 401
425check_SCRIPTS = \
426 test_plugin_rest_namestore.sh
427
428EXTRA_DIST = \ 402EXTRA_DIST = \
429 test_common.c \ 403 test_common.c \
430 test_namestore_api.conf \ 404 test_namestore_api.conf \
@@ -434,5 +408,4 @@ EXTRA_DIST = \
434 perf_namestore_api_sqlite.conf \ 408 perf_namestore_api_sqlite.conf \
435 test_plugin_namestore_sqlite.conf \ 409 test_plugin_namestore_sqlite.conf \
436 test_plugin_namestore_postgres.conf \ 410 test_plugin_namestore_postgres.conf \
437 $(check_SCRIPTS) \
438 $(sql_DATA) 411 $(sql_DATA)
diff --git a/src/plugin/namestore/meson.build b/src/plugin/namestore/meson.build
index 32db8c104..d51b4b730 100644
--- a/src/plugin/namestore/meson.build
+++ b/src/plugin/namestore/meson.build
@@ -1,26 +1,10 @@
1shared_module('gnunet_plugin_rest_namestore', 1libgnunetpluginnamestore_sqlite_src = ['plugin_namestore_sqlite.c']
2 ['plugin_rest_namestore.c'],
3 dependencies: [libgnunetrest_dep,
4 libgnunetidentity_dep,
5 libgnunetgnsrecordjson_dep,
6 libgnunetgnsrecord_dep,
7 libgnunetnamestore_dep,
8 libgnunetjson_dep,
9 libgnunetutil_dep,
10 json_dep,
11 mhd_dep],
12 include_directories: [incdir, configuration_inc],
13 install: true,
14 install_dir: get_option('libdir') / 'gnunet')
15
16 2
17shared_module('gnunet_plugin_namestore_sqlite', 3shared_module('gnunet_plugin_namestore_sqlite',
18 libgnunetpluginnamestore_sqlite_src, 4 libgnunetpluginnamestore_sqlite_src,
19 dependencies: [libgnunetutil_dep, 5 dependencies: [libgnunetutil_dep,
20 libgnunetgnsrecord_dep, 6 libgnunetgnsrecord_dep,
21 libgnunetidentity_dep,
22 libgnunetsq_dep, 7 libgnunetsq_dep,
23 libgnunetstatistics_dep,
24 sqlite_dep], 8 sqlite_dep],
25 include_directories: [incdir, configuration_inc], 9 include_directories: [incdir, configuration_inc],
26 install: true, 10 install: true,
@@ -31,9 +15,7 @@ if pq_dep.found()
31 ['plugin_namestore_postgres.c'], 15 ['plugin_namestore_postgres.c'],
32 dependencies: [libgnunetutil_dep, 16 dependencies: [libgnunetutil_dep,
33 libgnunetgnsrecord_dep, 17 libgnunetgnsrecord_dep,
34 libgnunetidentity_dep,
35 libgnunetpq_dep, 18 libgnunetpq_dep,
36 libgnunetstatistics_dep,
37 pq_dep], 19 pq_dep],
38 include_directories: [incdir, configuration_inc], 20 include_directories: [incdir, configuration_inc],
39 install: true, 21 install: true,
diff --git a/src/plugin/namestore/plugin_rest_namestore.c b/src/plugin/namestore/plugin_rest_namestore.c
deleted file mode 100644
index 31e78e6dd..000000000
--- a/src/plugin/namestore/plugin_rest_namestore.c
+++ /dev/null
@@ -1,1364 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-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 * @author Martin Schanzenbach
22 * @author Philippe Buschmann
23 * @file namestore/plugin_rest_namestore.c
24 * @brief GNUnet Namestore REST plugin
25 */
26
27#include "platform.h"
28#include "gnunet_error_codes.h"
29#include "gnunet_rest_plugin.h"
30#include "gnunet_gns_service.h"
31#include "gnunet_namestore_service.h"
32#include "gnunet_identity_service.h"
33#include "gnunet_rest_lib.h"
34#include "gnunet_gnsrecord_json_lib.h"
35#include "microhttpd.h"
36#include <jansson.h>
37
38/**
39 * Namestore namespace
40 */
41#define GNUNET_REST_API_NS_NAMESTORE "/namestore"
42
43/**
44 * Namestore import API namespace
45 */
46#define GNUNET_REST_API_NS_NAMESTORE_IMPORT "/namestore/import"
47
48/**
49 * State while collecting all egos
50 */
51#define ID_REST_STATE_INIT 0
52
53/**
54 * Done collecting egos
55 */
56#define ID_REST_STATE_POST_INIT 1
57/**
58 * The configuration handle
59 */
60const struct GNUNET_CONFIGURATION_Handle *cfg;
61
62/**
63 * HTTP methods allows for this plugin
64 */
65static char *allow_methods;
66
67/**
68 * Ego list
69 */
70static struct EgoEntry *ego_head;
71
72/**
73 * Ego list
74 */
75static struct EgoEntry *ego_tail;
76
77/**
78 * The processing state
79 */
80static int state;
81
82/**
83 * Handle to NAMESTORE
84 */
85static struct GNUNET_NAMESTORE_Handle *ns_handle;
86
87/**
88 * Handle to Identity service.
89 */
90static struct GNUNET_IDENTITY_Handle *identity_handle;
91
92/**
93 * @brief struct returned by the initialization function of the plugin
94 */
95struct Plugin
96{
97 const struct GNUNET_CONFIGURATION_Handle *cfg;
98};
99
100/**
101 * The default namestore ego
102 */
103struct EgoEntry
104{
105 /**
106 * DLL
107 */
108 struct EgoEntry *next;
109
110 /**
111 * DLL
112 */
113 struct EgoEntry *prev;
114
115 /**
116 * Ego Identifier
117 */
118 char *identifier;
119
120 /**
121 * Public key string
122 */
123 char *keystring;
124
125 /**
126 * The Ego
127 */
128 struct GNUNET_IDENTITY_Ego *ego;
129};
130
131
132enum UpdateStrategy
133{
134 UPDATE_STRATEGY_REPLACE,
135 UPDATE_STRATEGY_APPEND
136};
137
138/**
139 * The request handle
140 */
141struct RequestHandle
142{
143 /**
144 * DLL
145 */
146 struct RequestHandle *next;
147
148 /**
149 * DLL
150 */
151 struct RequestHandle *prev;
152
153 /**
154 * Records to store
155 */
156 char *record_name;
157
158 /**
159 * Record type filter
160 */
161 uint32_t record_type;
162
163 /**
164 * How to update the record set
165 */
166 enum UpdateStrategy update_strategy;
167
168 /**
169 * Records to store
170 */
171 struct GNUNET_GNSRECORD_Data *rd;
172
173 /**
174 * Number of records in rd
175 */
176 unsigned int rd_count;
177
178 /**
179 * RecordInfo array
180 */
181 struct GNUNET_NAMESTORE_RecordInfo *ri;
182
183 /**
184 * Size of record info
185 */
186 unsigned int rd_set_count;
187
188 /**
189 * Position of record info
190 */
191 unsigned int rd_set_pos;
192
193 /**
194 * NAMESTORE Operation
195 */
196 struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
197
198 /**
199 * For bulk import, we need a dedicated Namestore handle
200 */
201 struct GNUNET_NAMESTORE_Handle *nc;
202
203 /**
204 * Response object
205 */
206 json_t *resp_object;
207
208
209 /**
210 * Handle to NAMESTORE it
211 */
212 struct GNUNET_NAMESTORE_ZoneIterator *list_it;
213
214 /**
215 * Private key for the zone
216 */
217 const struct GNUNET_CRYPTO_PrivateKey *zone_pkey;
218
219 /**
220 * IDENTITY Operation
221 */
222 struct EgoEntry *ego_entry;
223
224 /**
225 * IDENTITY Operation
226 */
227 struct GNUNET_IDENTITY_Operation *op;
228
229 /**
230 * Rest connection
231 */
232 struct GNUNET_REST_RequestHandle *rest_handle;
233
234 /**
235 * Desired timeout for the lookup (default is no timeout).
236 */
237 struct GNUNET_TIME_Relative timeout;
238
239 /**
240 * ID of a task associated with the resolution process.
241 */
242 struct GNUNET_SCHEDULER_Task *timeout_task;
243
244 /**
245 * The plugin result processor
246 */
247 GNUNET_REST_ResultProcessor proc;
248
249 /**
250 * The closure of the result processor
251 */
252 void *proc_cls;
253
254 /**
255 * The url
256 */
257 char *url;
258
259 /**
260 * Error code
261 */
262 enum GNUNET_ErrorCode ec;
263
264};
265
266/**
267 * DLL
268 */
269static struct RequestHandle *requests_head;
270
271/**
272 * DLL
273 */
274static struct RequestHandle *requests_tail;
275
276
277/**
278 * Cleanup lookup handle
279 * @param cls Handle to clean up
280 */
281static void
282cleanup_handle (void *cls)
283{
284 struct RequestHandle *handle = cls;
285
286 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
287 if (NULL != handle->timeout_task)
288 {
289 GNUNET_SCHEDULER_cancel (handle->timeout_task);
290 handle->timeout_task = NULL;
291 }
292 if (NULL != handle->record_name)
293 GNUNET_free (handle->record_name);
294 if (NULL != handle->url)
295 GNUNET_free (handle->url);
296 if (NULL != handle->rd)
297 {
298 for (int i = 0; i < handle->rd_count; i++)
299 {
300 if (NULL != handle->rd[i].data)
301 GNUNET_free_nz ((void *) handle->rd[i].data);
302 }
303 GNUNET_free (handle->rd);
304 }
305 if (NULL != handle->timeout_task)
306 GNUNET_SCHEDULER_cancel (handle->timeout_task);
307 if (NULL != handle->list_it)
308 GNUNET_NAMESTORE_zone_iteration_stop (handle->list_it);
309 if (NULL != handle->ns_qe)
310 GNUNET_NAMESTORE_cancel (handle->ns_qe);
311 if (NULL != handle->nc)
312 GNUNET_NAMESTORE_disconnect (handle->nc);
313 if (NULL != handle->resp_object)
314 {
315 json_decref (handle->resp_object);
316 }
317 GNUNET_CONTAINER_DLL_remove (requests_head,
318 requests_tail,
319 handle);
320 GNUNET_free (handle);
321}
322
323
324/**
325 * Task run on errors. Reports an error and cleans up everything.
326 *
327 * @param cls the `struct RequestHandle`
328 */
329static void
330do_error (void *cls)
331{
332 struct RequestHandle *handle = cls;
333 struct MHD_Response *resp;
334 json_t *json_error = json_object ();
335 char *response;
336 const char* emsg;
337 int response_code;
338
339 emsg = GNUNET_ErrorCode_get_hint (handle->ec);
340 json_object_set_new (json_error, "error", json_string (emsg));
341 json_object_set_new (json_error, "error_code", json_integer (handle->ec));
342 response_code = GNUNET_ErrorCode_get_http_status (handle->ec);
343 if (0 == response_code)
344 response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
345 response = json_dumps (json_error, 0);
346 resp = GNUNET_REST_create_response (response);
347 GNUNET_assert (MHD_YES ==
348 MHD_add_response_header (resp, "Content-Type",
349 "application/json"));
350 handle->proc (handle->proc_cls, resp, response_code);
351 json_decref (json_error);
352 GNUNET_free (response);
353 cleanup_handle (handle);
354}
355
356
357/**
358 * Get EgoEntry from list with either a public key or a name
359 * If public key and name are not NULL, it returns the public key result first
360 *
361 * @param handle the RequestHandle
362 * @param pubkey the public key of an identity (only one can be NULL)
363 * @param name the name of an identity (only one can be NULL)
364 * @return EgoEntry or NULL if not found
365 */
366struct EgoEntry *
367get_egoentry_namestore (struct RequestHandle *handle, char *name)
368{
369 struct EgoEntry *ego_entry;
370 char *copy = GNUNET_strdup (name);
371 char *tmp;
372
373 if (NULL == name)
374 return NULL;
375 tmp = strtok (copy, "/");
376 if (NULL == tmp)
377 return NULL;
378 for (ego_entry = ego_head; NULL != ego_entry;
379 ego_entry = ego_entry->next)
380 {
381 if (0 != strcasecmp (tmp, ego_entry->identifier))
382 continue;
383 GNUNET_free (copy);
384 return ego_entry;
385 }
386 GNUNET_free (copy);
387 return NULL;
388}
389
390
391/**
392 * Does internal server error when iteration failed.
393 *
394 * @param cls the `struct RequestHandle`
395 */
396static void
397namestore_iteration_error (void *cls)
398{
399 struct RequestHandle *handle = cls;
400
401 handle->ec = GNUNET_EC_NAMESTORE_ITERATION_FAILED;
402 GNUNET_SCHEDULER_add_now (&do_error, handle);
403 return;
404}
405
406
407static void
408create_finished (void *cls, enum GNUNET_ErrorCode ec)
409{
410 struct RequestHandle *handle = cls;
411 struct MHD_Response *resp;
412
413 handle->ns_qe = NULL;
414 handle->ec = ec;
415 if (GNUNET_EC_NONE != ec)
416 {
417 GNUNET_SCHEDULER_add_now (&do_error, handle);
418 return;
419 }
420 resp = GNUNET_REST_create_response (NULL);
421 handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
422 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
423}
424
425
426static void
427del_finished (void *cls, enum GNUNET_ErrorCode ec)
428{
429 struct RequestHandle *handle = cls;
430
431 handle->ns_qe = NULL;
432 handle->ec = ec;
433 if (GNUNET_EC_NONE != ec)
434 {
435 GNUNET_SCHEDULER_add_now (&do_error, handle);
436 return;
437 }
438 handle->proc (handle->proc_cls,
439 GNUNET_REST_create_response (NULL),
440 MHD_HTTP_NO_CONTENT);
441 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
442}
443
444
445/**
446 * Iteration over all results finished, build final
447 * response.
448 *
449 * @param cls the `struct RequestHandle`
450 */
451static void
452namestore_list_finished (void *cls)
453{
454 struct RequestHandle *handle = cls;
455 char *result_str;
456 struct MHD_Response *resp;
457
458 handle->list_it = NULL;
459
460 if (NULL == handle->resp_object)
461 {
462 handle->ec = GNUNET_EC_NAMESTORE_ZONE_EMPTY;
463 GNUNET_SCHEDULER_add_now (&do_error, handle);
464 return;
465 }
466 result_str = json_dumps (handle->resp_object, 0);
467 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
468 resp = GNUNET_REST_create_response (result_str);
469 GNUNET_assert (MHD_YES ==
470 MHD_add_response_header (resp, "Content-Type",
471 "application/json"));
472 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
473 GNUNET_free (result_str);
474 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
475}
476
477
478/**
479 * Create a response with requested records
480 *
481 * @param handle the RequestHandle
482 */
483static void
484namestore_list_iteration (void *cls,
485 const struct GNUNET_CRYPTO_PrivateKey *zone_key,
486 const char *rname,
487 unsigned int rd_len,
488 const struct GNUNET_GNSRECORD_Data *rd,
489 struct GNUNET_TIME_Absolute expiry)
490{
491 struct RequestHandle *handle = cls;
492 struct GNUNET_GNSRECORD_Data rd_filtered[rd_len];
493 json_t *record_obj;
494 int i = 0;
495 int j = 0;
496
497 if (rd_len == 0)
498 {
499 /** skip **/
500 GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, 1);
501 return;
502 }
503
504 for (i = 0; i < rd_len; i++)
505 {
506 if ((GNUNET_GNSRECORD_TYPE_ANY != handle->record_type) &&
507 (rd[i].record_type != handle->record_type))
508 continue; /* Apply filter */
509 rd_filtered[j] = rd[i];
510 rd_filtered[j].data = rd[i].data;
511 j++;
512 }
513 /** Only add if not empty **/
514 if (j > 0)
515 {
516 if (NULL == handle->resp_object)
517 handle->resp_object = json_array ();
518 record_obj = GNUNET_GNSRECORD_JSON_from_gnsrecord (rname,
519 rd_filtered,
520 j);
521 json_array_append_new (handle->resp_object, record_obj);
522 }
523 GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, 1);
524}
525
526
527/**
528 * Handle lookup error
529 *
530 * @param cls the request handle
531 */
532static void
533ns_lookup_error_cb (void *cls)
534{
535 struct RequestHandle *handle = cls;
536
537 handle->ec = GNUNET_EC_NAMESTORE_LOOKUP_ERROR;
538 GNUNET_SCHEDULER_add_now (&do_error, handle);
539}
540
541
542static void
543ns_get_lookup_cb (void *cls,
544 const struct GNUNET_CRYPTO_PrivateKey *zone,
545 const char *label,
546 unsigned int rd_len,
547 const struct GNUNET_GNSRECORD_Data *rd)
548{
549 struct RequestHandle *handle = cls;
550 struct GNUNET_GNSRECORD_Data rd_filtered[rd_len];
551 int i = 0;
552 int j = 0;
553
554 handle->ns_qe = NULL;
555 for (i = 0; i < rd_len; i++)
556 {
557 if ((GNUNET_GNSRECORD_TYPE_ANY != handle->record_type) &&
558 (rd[i].record_type != handle->record_type))
559 continue; /* Apply filter */
560 rd_filtered[j] = rd[i];
561 rd_filtered[j].data = rd[i].data;
562 j++;
563 }
564 /** Return 404 if no set was found **/
565 if (j == 0)
566 {
567 handle->ec = GNUNET_EC_NAMESTORE_RECORD_NOT_FOUND;
568 GNUNET_SCHEDULER_add_now (&do_error, handle);
569 return;
570 }
571 handle->resp_object = GNUNET_GNSRECORD_JSON_from_gnsrecord (label,
572 rd_filtered,
573 j);
574 GNUNET_SCHEDULER_add_now (&namestore_list_finished, handle);
575}
576
577
578/**
579 * Handle namestore GET request
580 *
581 * @param con_handle the connection handle
582 * @param url the url
583 * @param cls the RequestHandle
584 */
585void
586namestore_get (struct GNUNET_REST_RequestHandle *con_handle,
587 const char *url,
588 void *cls)
589{
590 struct RequestHandle *handle = cls;
591 struct EgoEntry *ego_entry;
592 struct GNUNET_HashCode key;
593 enum GNUNET_GNSRECORD_Filter filter_flags;
594 char *egoname;
595 char *labelname;
596 char *typename;
597 char *boolstring;
598
599 egoname = NULL;
600 ego_entry = NULL;
601
602 // set zone to name if given
603 if (strlen (GNUNET_REST_API_NS_NAMESTORE) + 1 >= strlen (handle->url))
604 {
605 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
606 GNUNET_SCHEDULER_add_now (&do_error, handle);
607 return;
608 }
609 egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE) + 1];
610 ego_entry = get_egoentry_namestore (handle, egoname);
611 if (NULL == ego_entry)
612 {
613 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
614 GNUNET_SCHEDULER_add_now (&do_error, handle);
615 return;
616 }
617 handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
618
619 GNUNET_CRYPTO_hash ("record_type", strlen ("record_type"), &key);
620 handle->record_type = GNUNET_GNSRECORD_TYPE_ANY;
621 if (GNUNET_YES ==
622 GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, &key))
623 {
624 typename = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map,
625 &key);
626 if (NULL != typename)
627 handle->record_type = GNUNET_GNSRECORD_typename_to_number (typename);
628 }
629 GNUNET_CRYPTO_hash ("omit_private", strlen ("omit_private"), &key);
630 filter_flags = GNUNET_GNSRECORD_FILTER_NONE;
631 if (GNUNET_YES ==
632 GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, &key))
633 {
634 boolstring = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map,
635 &key);
636 if ((0 == strcmp (boolstring, "1")) ||
637 (0 == strcmp (boolstring, "yes")) ||
638 (0 == strcmp (boolstring, "true")))
639 filter_flags = GNUNET_GNSRECORD_FILTER_OMIT_PRIVATE;
640 }
641 GNUNET_CRYPTO_hash ("include_maintenance", strlen ("include_maintenance"),
642 &key);
643 if (GNUNET_YES ==
644 GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, &key))
645 {
646 boolstring = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map,
647 &key);
648 if ((0 == strcmp (boolstring, "1")) ||
649 (0 == strcmp (boolstring, "yes")) ||
650 (0 == strcmp (boolstring, "true")))
651 filter_flags |= GNUNET_GNSRECORD_FILTER_INCLUDE_MAINTENANCE;
652 }
653 labelname = &egoname[strlen (ego_entry->identifier)];
654 if (1 >= strlen (labelname))
655 {
656 /* Iterate over all records */
657 handle->list_it =
658 GNUNET_NAMESTORE_zone_iteration_start2 (ns_handle,
659 handle->zone_pkey,
660 &namestore_iteration_error,
661 handle,
662 &namestore_list_iteration,
663 handle,
664 &namestore_list_finished,
665 handle,
666 filter_flags);
667 if (NULL == handle->list_it)
668 {
669 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
670 GNUNET_SCHEDULER_add_now (&do_error, handle);
671 return;
672 }
673 return;
674 }
675 handle->record_name = GNUNET_strdup (labelname + 1);
676 handle->ns_qe = GNUNET_NAMESTORE_records_lookup2 (ns_handle,
677 handle->zone_pkey,
678 handle->record_name,
679 &ns_lookup_error_cb,
680 handle,
681 &ns_get_lookup_cb,
682 handle,
683 filter_flags);
684 if (NULL == handle->ns_qe)
685 {
686 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
687 GNUNET_SCHEDULER_add_now (&do_error, handle);
688 return;
689 }
690}
691
692
693static void
694ns_lookup_cb (void *cls,
695 const struct GNUNET_CRYPTO_PrivateKey *zone,
696 const char *label,
697 unsigned int rd_count,
698 const struct GNUNET_GNSRECORD_Data *rd)
699{
700 struct RequestHandle *handle = cls;
701 struct GNUNET_GNSRECORD_Data rd_new[rd_count + handle->rd_count];
702 int i = 0;
703 int j = 0;
704
705 if (UPDATE_STRATEGY_APPEND == handle->update_strategy)
706 {
707 for (i = 0; i < rd_count; i++)
708 rd_new[i] = rd[i];
709 }
710 for (j = 0; j < handle->rd_count; j++)
711 rd_new[i + j] = handle->rd[j];
712 handle->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle,
713 handle->zone_pkey,
714 handle->record_name,
715 i + j,
716 rd_new,
717 &create_finished,
718 handle);
719 if (NULL == handle->ns_qe)
720 {
721 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
722 GNUNET_SCHEDULER_add_now (&do_error, handle);
723 return;
724 }
725}
726
727
728static void
729bulk_tx_commit_cb (void *cls, enum GNUNET_ErrorCode ec)
730{
731 struct RequestHandle *handle = cls;
732 struct MHD_Response *resp;
733
734 handle->ns_qe = NULL;
735 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
736 "Commit finished (%d)\n", ec);
737 handle->ec = ec;
738 if (GNUNET_EC_NONE != ec)
739 {
740 GNUNET_SCHEDULER_add_now (&do_error, handle);
741 return;
742 }
743 resp = GNUNET_REST_create_response (NULL);
744 handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
745 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
746}
747
748
749static void
750import_next_cb (void *cls, enum GNUNET_ErrorCode ec)
751{
752 struct RequestHandle *handle = cls;
753
754 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
755 "Import finished (%d)\n", ec);
756 handle->ns_qe = NULL;
757 handle->ec = ec;
758 if (GNUNET_EC_NONE != ec)
759 {
760 GNUNET_SCHEDULER_add_now (&do_error, handle);
761 return;
762 }
763 unsigned int remaining = handle->rd_set_count - handle->rd_set_pos;
764 if (0 == remaining)
765 {
766 handle->ns_qe = GNUNET_NAMESTORE_transaction_commit (handle->nc,
767 &bulk_tx_commit_cb,
768 handle);
769 return;
770 }
771 unsigned int sent_rds = 0;
772 // Find the smallest set of records we can send with our message size
773 // restriction of 16 bit
774 handle->ns_qe = GNUNET_NAMESTORE_records_store2 (handle->nc,
775 handle->zone_pkey,
776 remaining,
777 &handle->ri[handle->
778 rd_set_pos],
779 &sent_rds,
780 &import_next_cb,
781 handle);
782 if ((NULL == handle->ns_qe) && (0 == sent_rds))
783 {
784 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
785 GNUNET_SCHEDULER_add_now (&do_error, handle);
786 return;
787 }
788 handle->rd_set_pos += sent_rds;
789}
790
791static void
792bulk_tx_start (void *cls, enum GNUNET_ErrorCode ec)
793{
794 struct RequestHandle *handle = cls;
795 json_t *data_js;
796 json_error_t err;
797
798 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
799 "Transaction started...\n");
800 handle->ec = ec;
801 if (GNUNET_EC_NONE != ec)
802 {
803 GNUNET_SCHEDULER_add_now (&do_error, handle);
804 return;
805 }
806 if (0 >= handle->rest_handle->data_size)
807 {
808 handle->ec = GNUNET_EC_NAMESTORE_NO_RECORDS_GIVEN;
809 GNUNET_SCHEDULER_add_now (&do_error, handle);
810 return;
811 }
812 char term_data[handle->rest_handle->data_size + 1];
813 term_data[handle->rest_handle->data_size] = '\0';
814 GNUNET_memcpy (term_data,
815 handle->rest_handle->data,
816 handle->rest_handle->data_size);
817 data_js = json_loads (term_data, JSON_DECODE_ANY, &err);
818 if (NULL == data_js)
819 {
820 handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID;
821 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
822 "Error parsing data: %s", err.text);
823 GNUNET_SCHEDULER_add_now (&do_error, handle);
824 return;
825 }
826 if (! json_is_array (data_js))
827 {
828 handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID;
829 GNUNET_SCHEDULER_add_now (&do_error, handle);
830 json_decref (data_js);
831 return;
832 }
833 handle->rd_set_count = json_array_size (data_js);
834 handle->ri = GNUNET_malloc (handle->rd_set_count
835 * sizeof (struct GNUNET_NAMESTORE_RecordInfo));
836 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
837 "Got record set of size %d\n", handle->rd_set_count);
838 char *albl;
839 size_t index;
840 json_t *value;
841 json_array_foreach (data_js, index, value) {
842 {
843 struct GNUNET_GNSRECORD_Data *rd;
844 struct GNUNET_JSON_Specification gnsspec[] =
845 { GNUNET_GNSRECORD_JSON_spec_gnsrecord (&rd,
846 &handle->ri[index].a_rd_count,
847 &albl),
848 GNUNET_JSON_spec_end () };
849 if (GNUNET_OK != GNUNET_JSON_parse (value, gnsspec, NULL, NULL))
850 {
851 handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID;
852 GNUNET_SCHEDULER_add_now (&do_error, handle);
853 json_decref (data_js);
854 return;
855 }
856 handle->ri[index].a_rd = rd;
857 handle->ri[index].a_label = albl;
858 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
859 "Parsed record set for name %s\n",
860 handle->ri[index].a_label);
861 }
862 }
863 // json_decref (data_js);
864
865 unsigned int sent_rds = 0;
866 // Find the smallest set of records we can send with our message size
867 // restriction of 16 bit
868 handle->ns_qe = GNUNET_NAMESTORE_records_store2 (handle->nc,
869 handle->zone_pkey,
870 handle->rd_set_count,
871 handle->ri,
872 &sent_rds,
873 &import_next_cb,
874 handle);
875 if ((NULL == handle->ns_qe) && (0 == sent_rds))
876 {
877 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
878 GNUNET_SCHEDULER_add_now (&do_error, handle);
879 return;
880 }
881 handle->rd_set_pos += sent_rds;
882}
883
884
885/**
886 * Handle namestore POST import
887 *
888 * @param con_handle the connection handle
889 * @param url the url
890 * @param cls the RequestHandle
891 */
892void
893namestore_import (struct GNUNET_REST_RequestHandle *con_handle,
894 const char *url,
895 void *cls)
896{
897 struct RequestHandle *handle = cls;
898 struct EgoEntry *ego_entry;
899 char *egoname;
900
901 // set zone to name if given
902 if (strlen (GNUNET_REST_API_NS_NAMESTORE_IMPORT) + 1 >= strlen (
903 handle->url))
904 {
905 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
906 GNUNET_SCHEDULER_add_now (&do_error, handle);
907 return;
908 }
909 ego_entry = NULL;
910
911 egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE_IMPORT) + 1];
912 ego_entry = get_egoentry_namestore (handle, egoname);
913
914 if (NULL == ego_entry)
915 {
916 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
917 GNUNET_SCHEDULER_add_now (&do_error, handle);
918 return;
919 }
920 handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
921
922 // We need a per-client connection for a transactional bulk import
923 handle->nc = GNUNET_NAMESTORE_connect (cfg);
924 if (NULL == handle->nc)
925 {
926 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
927 GNUNET_SCHEDULER_add_now (&do_error, handle);
928 return;
929 }
930 handle->ns_qe = GNUNET_NAMESTORE_transaction_begin (handle->nc,
931 &bulk_tx_start,
932 handle);
933}
934
935/**
936 * Handle namestore POST/PUT request
937 *
938 * @param con_handle the connection handle
939 * @param url the url
940 * @param cls the RequestHandle
941 */
942void
943namestore_add_or_update (struct GNUNET_REST_RequestHandle *con_handle,
944 const char *url,
945 void *cls)
946{
947 struct RequestHandle *handle = cls;
948 struct EgoEntry *ego_entry;
949 char *egoname;
950 json_t *data_js;
951 json_error_t err;
952
953 char term_data[handle->rest_handle->data_size + 1];
954
955 if (0 >= handle->rest_handle->data_size)
956 {
957 handle->ec = GNUNET_EC_NAMESTORE_NO_RECORDS_GIVEN;
958 GNUNET_SCHEDULER_add_now (&do_error, handle);
959 return;
960 }
961 term_data[handle->rest_handle->data_size] = '\0';
962 GNUNET_memcpy (term_data,
963 handle->rest_handle->data,
964 handle->rest_handle->data_size);
965 data_js = json_loads (term_data, JSON_DECODE_ANY, &err);
966 struct GNUNET_JSON_Specification gnsspec[] =
967 { GNUNET_GNSRECORD_JSON_spec_gnsrecord (&handle->rd, &handle->rd_count,
968 &handle->record_name),
969 GNUNET_JSON_spec_end () };
970 if (GNUNET_OK != GNUNET_JSON_parse (data_js, gnsspec, NULL, NULL))
971 {
972 handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID;
973 GNUNET_SCHEDULER_add_now (&do_error, handle);
974 json_decref (data_js);
975 return;
976 }
977 GNUNET_JSON_parse_free (gnsspec);
978 if (0 >= strlen (handle->record_name))
979 {
980 handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID;
981 GNUNET_SCHEDULER_add_now (&do_error, handle);
982 json_decref (data_js);
983 return;
984 }
985 json_decref (data_js);
986
987 egoname = NULL;
988 ego_entry = NULL;
989
990 // set zone to name if given
991 if (strlen (GNUNET_REST_API_NS_NAMESTORE) + 1 >= strlen (handle->url))
992 {
993 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
994 GNUNET_SCHEDULER_add_now (&do_error, handle);
995 return;
996 }
997 egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE) + 1];
998 ego_entry = get_egoentry_namestore (handle, egoname);
999
1000 if (NULL == ego_entry)
1001 {
1002 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
1003 GNUNET_SCHEDULER_add_now (&do_error, handle);
1004 return;
1005 }
1006 handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1007 handle->ns_qe = GNUNET_NAMESTORE_records_lookup (ns_handle,
1008 handle->zone_pkey,
1009 handle->record_name,
1010 &ns_lookup_error_cb,
1011 handle,
1012 &ns_lookup_cb,
1013 handle);
1014 if (NULL == handle->ns_qe)
1015 {
1016 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
1017 GNUNET_SCHEDULER_add_now (&do_error, handle);
1018 return;
1019 }
1020}
1021
1022
1023/**
1024 * Handle namestore PUT request
1025 *
1026 * @param con_handle the connection handle
1027 * @param url the url
1028 * @param cls the RequestHandle
1029 */
1030void
1031namestore_update (struct GNUNET_REST_RequestHandle *con_handle,
1032 const char *url,
1033 void *cls)
1034{
1035 struct RequestHandle *handle = cls;
1036 handle->update_strategy = UPDATE_STRATEGY_REPLACE;
1037 namestore_add_or_update (con_handle, url, cls);
1038}
1039
1040
1041/**
1042 * Handle namestore POST request
1043 *
1044 * @param con_handle the connection handle
1045 * @param url the url
1046 * @param cls the RequestHandle
1047 */
1048void
1049namestore_add (struct GNUNET_REST_RequestHandle *con_handle,
1050 const char *url,
1051 void *cls)
1052{
1053 struct RequestHandle *handle = cls;
1054 handle->update_strategy = UPDATE_STRATEGY_APPEND;
1055 namestore_add_or_update (con_handle, url, cls);
1056}
1057
1058
1059/**
1060 * Handle namestore DELETE request
1061 *
1062 * @param con_handle the connection handle
1063 * @param url the url
1064 * @param cls the RequestHandle
1065 */
1066void
1067namestore_delete (struct GNUNET_REST_RequestHandle *con_handle,
1068 const char *url,
1069 void *cls)
1070{
1071 struct RequestHandle *handle = cls;
1072 struct EgoEntry *ego_entry;
1073 char *egoname;
1074 char *labelname;
1075
1076 egoname = NULL;
1077 ego_entry = NULL;
1078
1079 // set zone to name if given
1080 if (strlen (GNUNET_REST_API_NS_NAMESTORE) + 1 >= strlen (handle->url))
1081 {
1082 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
1083 GNUNET_SCHEDULER_add_now (&do_error, handle);
1084 return;
1085 }
1086 egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE) + 1];
1087 ego_entry = get_egoentry_namestore (handle, egoname);
1088 if (NULL == ego_entry)
1089 {
1090 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
1091 GNUNET_SCHEDULER_add_now (&do_error, handle);
1092 return;
1093 }
1094 handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1095 labelname = &egoname[strlen (ego_entry->identifier)];
1096 // set zone to name if given
1097 if (1 >= strlen (labelname))
1098 {
1099 /* label is only "/" */
1100 handle->ec = GNUNET_EC_NAMESTORE_NO_LABEL_GIVEN;
1101 GNUNET_SCHEDULER_add_now (&do_error, handle);
1102 }
1103
1104 handle->record_name = GNUNET_strdup (labelname + 1);
1105 handle->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle,
1106 handle->zone_pkey,
1107 handle->record_name,
1108 0,
1109 NULL,
1110 &del_finished,
1111 handle);
1112 if (NULL == handle->ns_qe)
1113 {
1114 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
1115 GNUNET_SCHEDULER_add_now (&do_error, handle);
1116 return;
1117 }
1118}
1119
1120
1121/**
1122 * Respond to OPTIONS request
1123 *
1124 * @param con_handle the connection handle
1125 * @param url the url
1126 * @param cls the RequestHandle
1127 */
1128static void
1129options_cont (struct GNUNET_REST_RequestHandle *con_handle,
1130 const char *url,
1131 void *cls)
1132{
1133 struct MHD_Response *resp;
1134 struct RequestHandle *handle = cls;
1135
1136 // independent of path return all options
1137 resp = GNUNET_REST_create_response (NULL);
1138 GNUNET_assert (MHD_YES ==
1139 MHD_add_response_header (resp,
1140 "Access-Control-Allow-Methods",
1141 allow_methods));
1142 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1143 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
1144 return;
1145}
1146
1147
1148static void
1149list_ego (void *cls,
1150 struct GNUNET_IDENTITY_Ego *ego,
1151 void **ctx,
1152 const char *identifier)
1153{
1154 struct EgoEntry *ego_entry;
1155 struct GNUNET_CRYPTO_PublicKey pk;
1156
1157 if ((NULL == ego) && (ID_REST_STATE_INIT == state))
1158 {
1159 state = ID_REST_STATE_POST_INIT;
1160 return;
1161 }
1162 if (NULL == ego)
1163 {
1164 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1165 "Called with NULL ego\n");
1166 return;
1167 }
1168 if (ID_REST_STATE_INIT == state)
1169 {
1170 ego_entry = GNUNET_new (struct EgoEntry);
1171 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1172 ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk);
1173 ego_entry->ego = ego;
1174 ego_entry->identifier = GNUNET_strdup (identifier);
1175 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
1176 ego_tail,
1177 ego_entry);
1178 }
1179 /* Ego renamed or added */
1180 if (identifier != NULL)
1181 {
1182 for (ego_entry = ego_head; NULL != ego_entry;
1183 ego_entry = ego_entry->next)
1184 {
1185 if (ego_entry->ego == ego)
1186 {
1187 /* Rename */
1188 GNUNET_free (ego_entry->identifier);
1189 ego_entry->identifier = GNUNET_strdup (identifier);
1190 break;
1191 }
1192 }
1193 if (NULL == ego_entry)
1194 {
1195 /* Add */
1196 ego_entry = GNUNET_new (struct EgoEntry);
1197 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1198 ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk);
1199 ego_entry->ego = ego;
1200 ego_entry->identifier = GNUNET_strdup (identifier);
1201 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
1202 ego_tail,
1203 ego_entry);
1204 }
1205 }
1206 else
1207 {
1208 /* Delete */
1209 for (ego_entry = ego_head; NULL != ego_entry;
1210 ego_entry = ego_entry->next)
1211 {
1212 if (ego_entry->ego == ego)
1213 break;
1214 }
1215 if (NULL == ego_entry)
1216 return; /* Not found */
1217
1218 GNUNET_CONTAINER_DLL_remove (ego_head,
1219 ego_tail,
1220 ego_entry);
1221 GNUNET_free (ego_entry->identifier);
1222 GNUNET_free (ego_entry->keystring);
1223 GNUNET_free (ego_entry);
1224 return;
1225 }
1226
1227}
1228
1229
1230/**
1231 * Function processing the REST call
1232 *
1233 * @param method HTTP method
1234 * @param url URL of the HTTP request
1235 * @param data body of the HTTP request (optional)
1236 * @param data_size length of the body
1237 * @param proc callback function for the result
1238 * @param proc_cls closure for callback function
1239 * @return GNUNET_OK if request accepted
1240 */
1241static enum GNUNET_GenericReturnValue
1242rest_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
1243 GNUNET_REST_ResultProcessor proc,
1244 void *proc_cls)
1245{
1246 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
1247 struct GNUNET_REST_RequestHandlerError err;
1248 static const struct GNUNET_REST_RequestHandler handlers[] =
1249 { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_NAMESTORE, &namestore_get },
1250 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_NAMESTORE_IMPORT,
1251 &namestore_import },
1252 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_NAMESTORE, &namestore_add },
1253 { MHD_HTTP_METHOD_PUT, GNUNET_REST_API_NS_NAMESTORE, &namestore_update },
1254 { MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_NAMESTORE,
1255 &namestore_delete },
1256 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_NAMESTORE, &options_cont },
1257 GNUNET_REST_HANDLER_END };
1258
1259 handle->ec = GNUNET_EC_NONE;
1260 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1261 handle->proc_cls = proc_cls;
1262 handle->proc = proc;
1263 handle->rest_handle = rest_handle;
1264 handle->zone_pkey = NULL;
1265 handle->timeout_task =
1266 GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_error, handle);
1267 handle->url = GNUNET_strdup (rest_handle->url);
1268 if (handle->url[strlen (handle->url) - 1] == '/')
1269 handle->url[strlen (handle->url) - 1] = '\0';
1270 GNUNET_CONTAINER_DLL_insert (requests_head,
1271 requests_tail,
1272 handle);
1273 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
1274 if (GNUNET_NO ==
1275 GNUNET_REST_handle_request (handle->rest_handle, handlers, &err,
1276 handle))
1277 {
1278 cleanup_handle (handle);
1279 return GNUNET_NO;
1280 }
1281
1282 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
1283 return GNUNET_YES;
1284}
1285
1286
1287/**
1288 * Entry point for the plugin.
1289 *
1290 * @param cls Config info
1291 * @return NULL on error, otherwise the plugin context
1292 */
1293void *
1294libgnunet_plugin_rest_namestore_init (void *cls)
1295{
1296 static struct Plugin plugin;
1297 struct GNUNET_REST_Plugin *api;
1298
1299 cfg = cls;
1300 if (NULL != plugin.cfg)
1301 return NULL; /* can only initialize once! */
1302 memset (&plugin, 0, sizeof(struct Plugin));
1303 plugin.cfg = cfg;
1304 api = GNUNET_new (struct GNUNET_REST_Plugin);
1305 api->cls = &plugin;
1306 api->name = GNUNET_REST_API_NS_NAMESTORE;
1307 api->process_request = &rest_process_request;
1308 state = ID_REST_STATE_INIT;
1309 GNUNET_asprintf (&allow_methods,
1310 "%s, %s, %s, %s, %s",
1311 MHD_HTTP_METHOD_GET,
1312 MHD_HTTP_METHOD_POST,
1313 MHD_HTTP_METHOD_PUT,
1314 MHD_HTTP_METHOD_DELETE,
1315 MHD_HTTP_METHOD_OPTIONS);
1316 ns_handle = GNUNET_NAMESTORE_connect (cfg);
1317 identity_handle = GNUNET_IDENTITY_connect (cfg, &list_ego, NULL);
1318
1319 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ (
1320 "Namestore REST API initialized\n"));
1321 return api;
1322}
1323
1324
1325/**
1326 * Exit point from the plugin.
1327 *
1328 * @param cls the plugin context (as returned by "init")
1329 * @return always NULL
1330 */
1331void *
1332libgnunet_plugin_rest_namestore_done (void *cls)
1333{
1334 struct GNUNET_REST_Plugin *api = cls;
1335 struct Plugin *plugin = api->cls;
1336 struct RequestHandle *request;
1337 struct EgoEntry *ego_entry;
1338 struct EgoEntry *ego_tmp;
1339
1340 plugin->cfg = NULL;
1341 while (NULL != (request = requests_head))
1342 do_error (request);
1343 if (NULL != identity_handle)
1344 GNUNET_IDENTITY_disconnect (identity_handle);
1345 if (NULL != ns_handle)
1346 GNUNET_NAMESTORE_disconnect (ns_handle);
1347
1348 for (ego_entry = ego_head; NULL != ego_entry;)
1349 {
1350 ego_tmp = ego_entry;
1351 ego_entry = ego_entry->next;
1352 GNUNET_free (ego_tmp->identifier);
1353 GNUNET_free (ego_tmp->keystring);
1354 GNUNET_free (ego_tmp);
1355 }
1356
1357 GNUNET_free (allow_methods);
1358 GNUNET_free (api);
1359 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Namestore REST plugin is finished\n");
1360 return NULL;
1361}
1362
1363
1364/* end of plugin_rest_namestore.c */
diff --git a/src/plugin/peerstore/Makefile.am b/src/plugin/peerstore/Makefile.am
new file mode 100644
index 000000000..225e9a71e
--- /dev/null
+++ b/src/plugin/peerstore/Makefile.am
@@ -0,0 +1,67 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4plugindir = $(libdir)/gnunet
5
6pkgcfgdir= $(pkgdatadir)/config.d/
7
8libexecdir= $(pkglibdir)/libexec/
9
10if USE_COVERAGE
11 AM_CFLAGS = -fprofile-arcs -ftest-coverage
12endif
13
14if HAVE_EXPERIMENTAL
15FLAT_PLUGIN = libgnunet_plugin_peerstore_flat.la
16FLAT_TESTS = test_plugin_peerstore_flat
17libgnunet_plugin_peerstore_flat_la_SOURCES = \
18 plugin_peerstore_flat.c
19libgnunet_plugin_peerstore_flat_la_LIBADD = \
20 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
21 $(LTLIBINTL)
22libgnunet_plugin_peerstore_flat_la_LDFLAGS = \
23 $(GN_PLUGIN_LDFLAGS)
24endif
25
26if HAVE_SQLITE
27SQLITE_PLUGIN = libgnunet_plugin_peerstore_sqlite.la
28SQLITE_TESTS = test_plugin_peerstore_sqlite
29libgnunet_plugin_peerstore_sqlite_la_SOURCES = \
30 plugin_peerstore_sqlite.c
31libgnunet_plugin_peerstore_sqlite_la_LIBADD = \
32 $(top_builddir)/src/lib/sq/libgnunetsq.la \
33 $(top_builddir)/src/lib/util/libgnunetutil.la \
34 $(XLIBS) -lsqlite3 \
35 $(LTLIBINTL)
36libgnunet_plugin_peerstore_sqlite_la_LDFLAGS = \
37 $(GN_PLUGIN_LDFLAGS)
38endif
39
40plugin_LTLIBRARIES = \
41 $(SQLITE_PLUGIN) \
42 $(FLAT_PLUGIN)
43
44test_plugin_peerstore_sqlite_SOURCES = \
45 test_plugin_peerstore.c
46test_plugin_peerstore_sqlite_LDADD = \
47 $(top_builddir)/src/service/testing/libgnunettesting.la \
48 $(top_builddir)/src/lib/util/libgnunetutil.la
49
50test_plugin_peerstore_flat_SOURCES = \
51 test_plugin_peerstore.c
52test_plugin_peerstore_flat_LDADD = \
53 $(top_builddir)/src/service/testing/libgnunettesting.la \
54 $(top_builddir)/src/lib/util/libgnunetutil.la
55
56check_PROGRAMS = \
57 $(SQLITE_TESTS) \
58 $(FLAT_TESTS)
59
60EXTRA_DIST = \
61 test_plugin_peerstore_flat.conf \
62 test_plugin_peerstore_sqlite.conf
63
64if ENABLE_TEST_RUN
65AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
66TESTS = $(check_PROGRAMS)
67endif
diff --git a/src/plugin/peerstore/meson.build b/src/plugin/peerstore/meson.build
new file mode 100644
index 000000000..1d22ca798
--- /dev/null
+++ b/src/plugin/peerstore/meson.build
@@ -0,0 +1,9 @@
1shared_module('gnunet_plugin_peerstore_sqlite',
2 ['plugin_peerstore_sqlite.c'],
3 dependencies: [libgnunetutil_dep,
4 libgnunetsq_dep,
5 sqlite_dep],
6 include_directories: [incdir,
7 configuration_inc],
8 install: true,
9 install_dir: get_option('libdir')/'gnunet')
diff --git a/src/plugin/peerstore/plugin_peerstore_flat.c b/src/plugin/peerstore/plugin_peerstore_flat.c
new file mode 100644
index 000000000..cc304d4db
--- /dev/null
+++ b/src/plugin/peerstore/plugin_peerstore_flat.c
@@ -0,0 +1,606 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2015 Christian Grothoff (and other contributing authors)
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 peerstore/plugin_peerstore_flat.c
23 * @brief flat file-based peerstore backend
24 * @author Martin Schanzenbach
25 */
26
27#include "platform.h"
28#include "gnunet_peerstore_plugin.h"
29#include "gnunet_peerstore_service.h"
30#include "../../service/peerstore/peerstore.h"
31
32/**
33 * Context for all functions in this plugin.
34 */
35struct Plugin
36{
37 /**
38 * Configuration handle
39 */
40 const struct GNUNET_CONFIGURATION_Handle *cfg;
41
42 /**
43 * HashMap
44 */
45 struct GNUNET_CONTAINER_MultiHashMap *hm;
46
47 /**
48 * Iterator
49 */
50 GNUNET_PEERSTORE_Processor iter;
51
52 /**
53 * Iterator cls
54 */
55 void *iter_cls;
56
57 /**
58 * iterator key
59 */
60 const char *iter_key;
61
62 /**
63 * Iterator peer
64 */
65 const struct GNUNET_PeerIdentity *iter_peer;
66
67 /**
68 * Iterator subsystem
69 */
70 const char *iter_sub_system;
71
72 /**
73 * Iterator time
74 */
75 struct GNUNET_TIME_Absolute iter_now;
76
77 /**
78 * Deleted entries
79 */
80 uint64_t deleted_entries;
81
82 /**
83 * Expired entries
84 */
85 uint64_t exp_changes;
86
87 /**
88 * Database filename.
89 */
90 char *fn;
91
92 /**
93 * Result found bool
94 */
95 int iter_result_found;
96};
97
98
99static int
100delete_entries (void *cls,
101 const struct GNUNET_HashCode *key,
102 void *value)
103{
104 struct Plugin *plugin = cls;
105 struct GNUNET_PEERSTORE_Record *entry = value;
106
107 if (0 != strcmp (plugin->iter_key, entry->key))
108 return GNUNET_YES;
109 if (0 != memcmp (plugin->iter_peer,
110 &entry->peer,
111 sizeof(struct GNUNET_PeerIdentity)))
112 return GNUNET_YES;
113 if (0 != strcmp (plugin->iter_sub_system, entry->sub_system))
114 return GNUNET_YES;
115
116 GNUNET_CONTAINER_multihashmap_remove (plugin->hm, key, value);
117 plugin->deleted_entries++;
118 return GNUNET_YES;
119}
120
121
122/**
123 * Delete records with the given key
124 *
125 * @param cls closure (internal context for the plugin)
126 * @param sub_system name of sub system
127 * @param peer Peer identity (can be NULL)
128 * @param key entry key string (can be NULL)
129 * @return number of deleted records
130 */
131static int
132peerstore_flat_delete_records (void *cls, const char *sub_system,
133 const struct GNUNET_PeerIdentity *peer,
134 const char *key)
135{
136 struct Plugin *plugin = cls;
137
138 plugin->iter_sub_system = sub_system;
139 plugin->iter_peer = peer;
140 plugin->iter_key = key;
141 plugin->deleted_entries = 0;
142
143 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
144 &delete_entries,
145 plugin);
146 return plugin->deleted_entries;
147}
148
149
150static int
151expire_entries (void *cls,
152 const struct GNUNET_HashCode *key,
153 void *value)
154{
155 struct Plugin *plugin = cls;
156 struct GNUNET_PEERSTORE_Record *entry = value;
157
158 if (entry->expiry.abs_value_us < plugin->iter_now.abs_value_us)
159 {
160 GNUNET_CONTAINER_multihashmap_remove (plugin->hm, key, value);
161 plugin->exp_changes++;
162 }
163 return GNUNET_YES;
164}
165
166
167/**
168 * Delete expired records (expiry < now)
169 *
170 * @param cls closure (internal context for the plugin)
171 * @param now time to use as reference
172 * @param cont continuation called with the number of records expired
173 * @param cont_cls continuation closure
174 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and cont is not
175 * called
176 */
177static int
178peerstore_flat_expire_records (void *cls, struct GNUNET_TIME_Absolute now,
179 GNUNET_PEERSTORE_Continuation cont,
180 void *cont_cls)
181{
182 struct Plugin *plugin = cls;
183
184 plugin->exp_changes = 0;
185 plugin->iter_now = now;
186
187 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
188 &expire_entries,
189 plugin);
190 if (NULL != cont)
191 {
192 cont (cont_cls, plugin->exp_changes);
193 }
194 return GNUNET_OK;
195}
196
197
198static int
199iterate_entries (void *cls,
200 const struct GNUNET_HashCode *key,
201 void *value)
202{
203 struct Plugin *plugin = cls;
204 struct GNUNET_PEERSTORE_Record *entry = value;
205
206 if ((NULL != plugin->iter_peer) &&
207 (0 != memcmp (plugin->iter_peer,
208 &entry->peer,
209 sizeof(struct GNUNET_PeerIdentity))))
210 {
211 return GNUNET_YES;
212 }
213 if ((NULL != plugin->iter_key) &&
214 (0 != strcmp (plugin->iter_key,
215 entry->key)))
216 {
217 return GNUNET_YES;
218 }
219 if (NULL != plugin->iter)
220 plugin->iter (plugin->iter_cls, entry, NULL);
221 plugin->iter_result_found = GNUNET_YES;
222 return GNUNET_YES;
223}
224
225
226/**
227 * Iterate over the records given an optional peer id
228 * and/or key.
229 *
230 * @param cls closure (internal context for the plugin)
231 * @param sub_system name of sub system
232 * @param peer Peer identity (can be NULL)
233 * @param key entry key string (can be NULL)
234 * @param iter function to call asynchronously with the results, terminated
235 * by a NULL result
236 * @param iter_cls closure for @a iter
237 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and iter is not
238 * called
239 */
240static int
241peerstore_flat_iterate_records (void *cls, const char *sub_system,
242 const struct GNUNET_PeerIdentity *peer,
243 const char *key,
244 GNUNET_PEERSTORE_Processor iter,
245 void *iter_cls)
246{
247 struct Plugin *plugin = cls;
248
249 plugin->iter = iter;
250 plugin->iter_cls = iter_cls;
251 plugin->iter_peer = peer;
252 plugin->iter_sub_system = sub_system;
253 plugin->iter_key = key;
254
255 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
256 &iterate_entries,
257 plugin);
258 if (NULL != iter)
259 iter (iter_cls, NULL, NULL);
260 return GNUNET_OK;
261}
262
263
264/**
265 * Store a record in the peerstore.
266 * Key is the combination of sub system and peer identity.
267 * One key can store multiple values.
268 *
269 * @param cls closure (internal context for the plugin)
270 * @param sub_system name of the GNUnet sub system responsible
271 * @param peer peer identity
272 * @param key record key string
273 * @param value value to be stored
274 * @param size size of value to be stored
275 * @param expiry absolute time after which the record is (possibly) deleted
276 * @param options options related to the store operation
277 * @param cont continuation called when record is stored
278 * @param cont_cls continuation closure
279 * @return #GNUNET_OK on success, else #GNUNET_SYSERR and cont is not called
280 */
281static int
282peerstore_flat_store_record (void *cls, const char *sub_system,
283 const struct GNUNET_PeerIdentity *peer,
284 const char *key, const void *value, size_t size,
285 struct GNUNET_TIME_Absolute expiry,
286 enum GNUNET_PEERSTORE_StoreOption options,
287 GNUNET_PEERSTORE_Continuation cont,
288 void *cont_cls)
289{
290 struct Plugin *plugin = cls;
291 struct GNUNET_HashCode hkey;
292 struct GNUNET_PEERSTORE_Record *entry;
293 const char *peer_id;
294
295
296 entry = GNUNET_new (struct GNUNET_PEERSTORE_Record);
297 entry->sub_system = GNUNET_strdup (sub_system);
298 entry->key = GNUNET_strdup (key);
299 entry->value = GNUNET_malloc (size);
300 GNUNET_memcpy (entry->value, value, size);
301 entry->value_size = size;
302 entry->peer = *peer;
303 entry->expiry = expiry;
304
305 peer_id = GNUNET_i2s (peer);
306 GNUNET_CRYPTO_hash (peer_id,
307 strlen (peer_id),
308 &hkey);
309
310 if (GNUNET_PEERSTORE_STOREOPTION_REPLACE == options)
311 {
312 peerstore_flat_delete_records (cls, sub_system, peer, key);
313 }
314
315 GNUNET_CONTAINER_multihashmap_put (plugin->hm,
316 &hkey,
317 entry,
318 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
319 if (NULL != cont)
320 {
321 cont (cont_cls, GNUNET_OK);
322 }
323 return GNUNET_OK;
324}
325
326
327/**
328 * Initialize the database connections and associated
329 * data structures (create tables and indices
330 * as needed as well).
331 *
332 * @param plugin the plugin context (state for this module)
333 * @return GNUNET_OK on success
334 */
335static int
336database_setup (struct Plugin *plugin)
337{
338 char *afsdir;
339 char *key;
340 char *sub_system;
341 const char *peer_id;
342 char *peer;
343 char *value;
344 char *expiry;
345 struct GNUNET_DISK_FileHandle *fh;
346 struct GNUNET_PEERSTORE_Record *entry;
347 struct GNUNET_HashCode hkey;
348 uint64_t size;
349 char *buffer;
350 char *line;
351
352 if (GNUNET_OK !=
353 GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, "peerstore-flat",
354 "FILENAME", &afsdir))
355 {
356 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "peerstore-flat",
357 "FILENAME");
358 return GNUNET_SYSERR;
359 }
360 if (GNUNET_OK != GNUNET_DISK_file_test (afsdir))
361 {
362 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (afsdir))
363 {
364 GNUNET_break (0);
365 GNUNET_free (afsdir);
366 return GNUNET_SYSERR;
367 }
368 }
369 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
370 plugin->fn = afsdir;
371
372 fh = GNUNET_DISK_file_open (afsdir,
373 GNUNET_DISK_OPEN_CREATE
374 | GNUNET_DISK_OPEN_READWRITE,
375 GNUNET_DISK_PERM_USER_WRITE
376 | GNUNET_DISK_PERM_USER_READ);
377 if (NULL == fh)
378 {
379 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
380 _ ("Unable to initialize file: %s.\n"),
381 afsdir);
382 return GNUNET_SYSERR;
383 }
384
385 /* Load data from file into hashmap */
386 plugin->hm = GNUNET_CONTAINER_multihashmap_create (10,
387 GNUNET_NO);
388
389 if (GNUNET_SYSERR == GNUNET_DISK_file_size (afsdir,
390 &size,
391 GNUNET_YES,
392 GNUNET_YES))
393 {
394 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
395 _ ("Unable to get filesize: %s.\n"),
396 afsdir);
397 return GNUNET_SYSERR;
398 }
399
400 buffer = GNUNET_malloc (size + 1);
401
402 if (GNUNET_SYSERR == GNUNET_DISK_file_read (fh,
403 buffer,
404 size))
405 {
406 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
407 _ ("Unable to read file: %s.\n"),
408 afsdir);
409 GNUNET_DISK_file_close (fh);
410 GNUNET_free (buffer);
411 return GNUNET_SYSERR;
412 }
413
414 buffer[size] = '\0';
415 GNUNET_DISK_file_close (fh);
416 if (0 < size)
417 {
418 line = strtok (buffer, "\n");
419 while (line != NULL)
420 {
421 sub_system = strtok (line, ",");
422 if (NULL == sub_system)
423 break;
424 peer = strtok (NULL, ",");
425 if (NULL == peer)
426 break;
427 key = strtok (NULL, ",");
428 if (NULL == key)
429 break;
430 value = strtok (NULL, ",");
431 if (NULL == value)
432 break;
433 expiry = strtok (NULL, ",");
434 if (NULL == expiry)
435 break;
436 entry = GNUNET_new (struct GNUNET_PEERSTORE_Record);
437 entry->sub_system = GNUNET_strdup (sub_system);
438 entry->key = GNUNET_strdup (key);
439 {
440 size_t s;
441 char *o;
442
443 o = NULL;
444 s = GNUNET_STRINGS_base64_decode (peer,
445 strlen (peer),
446 (void **) &o);
447 if (sizeof(struct GNUNET_PeerIdentity) == s)
448 GNUNET_memcpy (&entry->peer,
449 o,
450 s);
451 else
452 GNUNET_break (0);
453 GNUNET_free (o);
454 }
455 entry->value_size = GNUNET_STRINGS_base64_decode (value,
456 strlen (value),
457 (void **) &entry->value);
458 if (GNUNET_SYSERR ==
459 GNUNET_STRINGS_fancy_time_to_absolute (expiry,
460 &entry->expiry))
461 {
462 GNUNET_free (entry->sub_system);
463 GNUNET_free (entry->key);
464 GNUNET_free (entry);
465 break;
466 }
467 peer_id = GNUNET_i2s (&entry->peer);
468 GNUNET_CRYPTO_hash (peer_id,
469 strlen (peer_id),
470 &hkey);
471
472 GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (plugin->hm,
473 &hkey,
474 entry,
475 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
476 }
477 }
478 GNUNET_free (buffer);
479 return GNUNET_OK;
480}
481
482
483static int
484store_and_free_entries (void *cls,
485 const struct GNUNET_HashCode *key,
486 void *value)
487{
488 struct GNUNET_DISK_FileHandle *fh = cls;
489 struct GNUNET_PEERSTORE_Record *entry = value;
490 char *line;
491 char *peer;
492 const char *expiry;
493 char *val;
494
495 GNUNET_STRINGS_base64_encode (entry->value,
496 entry->value_size,
497 &val);
498 expiry = GNUNET_STRINGS_absolute_time_to_string (entry->expiry);
499 GNUNET_STRINGS_base64_encode ((char *) &entry->peer,
500 sizeof(struct GNUNET_PeerIdentity),
501 &peer);
502 GNUNET_asprintf (&line,
503 "%s,%s,%s,%s,%s",
504 entry->sub_system,
505 peer,
506 entry->key,
507 val,
508 expiry);
509 GNUNET_free (val);
510 GNUNET_free (peer);
511 GNUNET_DISK_file_write (fh,
512 line,
513 strlen (line));
514 GNUNET_free (entry->sub_system);
515 GNUNET_free (entry->key);
516 GNUNET_free (entry->value);
517 GNUNET_free (entry);
518 GNUNET_free (line);
519 return GNUNET_YES;
520}
521
522
523/**
524 * Shutdown database connection and associate data
525 * structures.
526 * @param plugin the plugin context (state for this module)
527 */
528static void
529database_shutdown (struct Plugin *plugin)
530{
531 struct GNUNET_DISK_FileHandle *fh;
532
533 fh = GNUNET_DISK_file_open (plugin->fn,
534 GNUNET_DISK_OPEN_CREATE
535 | GNUNET_DISK_OPEN_TRUNCATE
536 | GNUNET_DISK_OPEN_READWRITE,
537 GNUNET_DISK_PERM_USER_WRITE
538 | GNUNET_DISK_PERM_USER_READ);
539 if (NULL == fh)
540 {
541 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
542 _ ("Unable to initialize file: %s.\n"),
543 plugin->fn);
544 return;
545 }
546 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
547 &store_and_free_entries,
548 fh);
549 GNUNET_CONTAINER_multihashmap_destroy (plugin->hm);
550 GNUNET_DISK_file_close (fh);
551}
552
553
554/**
555 * Entry point for the plugin.
556 *
557 * @param cls The struct GNUNET_CONFIGURATION_Handle.
558 * @return NULL on error, otherwise the plugin context
559 */
560void *
561libgnunet_plugin_peerstore_flat_init (void *cls)
562{
563 static struct Plugin plugin;
564 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
565 struct GNUNET_PEERSTORE_PluginFunctions *api;
566
567 if (NULL != plugin.cfg)
568 return NULL; /* can only initialize once! */
569 memset (&plugin, 0, sizeof(struct Plugin));
570 plugin.cfg = cfg;
571 if (GNUNET_OK != database_setup (&plugin))
572 {
573 database_shutdown (&plugin);
574 return NULL;
575 }
576 api = GNUNET_new (struct GNUNET_PEERSTORE_PluginFunctions);
577 api->cls = &plugin;
578 api->store_record = &peerstore_flat_store_record;
579 api->iterate_records = &peerstore_flat_iterate_records;
580 api->expire_records = &peerstore_flat_expire_records;
581 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Flat plugin is running\n");
582 return api;
583}
584
585
586/**
587 * Exit point from the plugin.
588 *
589 * @param cls The plugin context (as returned by "init")
590 * @return Always NULL
591 */
592void *
593libgnunet_plugin_peerstore_flat_done (void *cls)
594{
595 struct GNUNET_PEERSTORE_PluginFunctions *api = cls;
596 struct Plugin *plugin = api->cls;
597
598 database_shutdown (plugin);
599 plugin->cfg = NULL;
600 GNUNET_free (api);
601 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Flat plugin is finished\n");
602 return NULL;
603}
604
605
606/* end of plugin_peerstore_sqlite.c */
diff --git a/src/plugin/peerstore/plugin_peerstore_sqlite.c b/src/plugin/peerstore/plugin_peerstore_sqlite.c
new file mode 100644
index 000000000..7d06d2c63
--- /dev/null
+++ b/src/plugin/peerstore/plugin_peerstore_sqlite.c
@@ -0,0 +1,702 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2013, 2017 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 peerstore/plugin_peerstore_sqlite.c
23 * @brief sqlite-based peerstore backend
24 * @author Omar Tarabai
25 * @author Christian Grothoff
26 */
27
28#include "platform.h"
29#include "gnunet_peerstore_plugin.h"
30#include "gnunet_peerstore_service.h"
31#include "gnunet_sq_lib.h"
32#include "../../service/peerstore/peerstore.h"
33#include <sqlite3.h>
34
35/**
36 * After how many ms "busy" should a DB operation fail for good? A
37 * low value makes sure that we are more responsive to requests
38 * (especially PUTs). A high value guarantees a higher success rate
39 * (SELECTs in iterate can take several seconds despite LIMIT=1).
40 *
41 * The default value of 1s should ensure that users do not experience
42 * huge latencies while at the same time allowing operations to
43 * succeed with reasonable probability.
44 */
45#define BUSY_TIMEOUT_MS 1000
46
47/**
48 * Log an error message at log-level 'level' that indicates
49 * a failure of the command 'cmd' on file 'filename'
50 * with the message given by strerror(errno).
51 */
52#define LOG_SQLITE(db, level, cmd) do { GNUNET_log_from (level, \
53 "peerstore-sqlite", _ ( \
54 "`%s' failed at %s:%d with error: %s\n"), \
55 cmd, \
56 __FILE__, __LINE__, \
57 sqlite3_errmsg ( \
58 db->dbh)); \
59} while (0)
60
61#define LOG(kind, ...) GNUNET_log_from (kind, "peerstore-sqlite", __VA_ARGS__)
62
63/**
64 * Context for all functions in this plugin.
65 */
66struct Plugin
67{
68 /**
69 * Configuration handle
70 */
71 const struct GNUNET_CONFIGURATION_Handle *cfg;
72
73 /**
74 * Database filename.
75 */
76 char *fn;
77
78 /**
79 * Native SQLite database handle.
80 */
81 sqlite3 *dbh;
82
83 /**
84 * Precompiled SQL for inserting into peerstoredata
85 */
86 sqlite3_stmt *insert_peerstoredata;
87
88 /**
89 * Precompiled SQL for selecting from peerstoredata
90 */
91 sqlite3_stmt *select_peerstoredata;
92
93 /**
94 * Precompiled SQL for selecting from peerstoredata
95 */
96 sqlite3_stmt *select_peerstoredata_by_pid;
97
98 /**
99 * Precompiled SQL for selecting from peerstoredata
100 */
101 sqlite3_stmt *select_peerstoredata_by_key;
102
103 /**
104 * Precompiled SQL for selecting from peerstoredata
105 */
106 sqlite3_stmt *select_peerstoredata_by_all;
107
108 /**
109 * Precompiled SQL for deleting expired
110 * records from peerstoredata
111 */
112 sqlite3_stmt *expire_peerstoredata;
113
114 /**
115 * Precompiled SQL for deleting records
116 * with given key
117 */
118 sqlite3_stmt *delete_peerstoredata;
119};
120
121
122/**
123 * Delete records with the given key
124 *
125 * @param cls closure (internal context for the plugin)
126 * @param sub_system name of sub system
127 * @param peer Peer identity (can be NULL)
128 * @param key entry key string (can be NULL)
129 * @return number of deleted records, #GNUNE_SYSERR on error
130 */
131static int
132peerstore_sqlite_delete_records (void *cls,
133 const char *sub_system,
134 const struct GNUNET_PeerIdentity *peer,
135 const char *key)
136{
137 struct Plugin *plugin = cls;
138 sqlite3_stmt *stmt = plugin->delete_peerstoredata;
139 struct GNUNET_SQ_QueryParam params[] = {
140 GNUNET_SQ_query_param_string (sub_system),
141 GNUNET_SQ_query_param_auto_from_type (peer),
142 GNUNET_SQ_query_param_string (key),
143 GNUNET_SQ_query_param_end
144 };
145 int ret;
146
147 if (GNUNET_OK !=
148 GNUNET_SQ_bind (stmt,
149 params))
150 {
151 LOG_SQLITE (plugin,
152 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
153 "sqlite3_bind");
154 GNUNET_SQ_reset (plugin->dbh,
155 stmt);
156 return GNUNET_SYSERR;
157 }
158 if (SQLITE_DONE !=
159 sqlite3_step (stmt))
160 {
161 LOG_SQLITE (plugin,
162 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
163 "sqlite3_step");
164 ret = GNUNET_SYSERR;
165 }
166 else
167 {
168 ret = sqlite3_changes (plugin->dbh);
169 }
170 GNUNET_SQ_reset (plugin->dbh,
171 stmt);
172 return ret;
173}
174
175
176/**
177 * Delete expired records (expiry < now)
178 *
179 * @param cls closure (internal context for the plugin)
180 * @param now time to use as reference
181 * @param cont continuation called with the number of records expired
182 * @param cont_cls continuation closure
183 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and cont is not
184 * called
185 */
186static int
187peerstore_sqlite_expire_records (void *cls, struct GNUNET_TIME_Absolute now,
188 GNUNET_PEERSTORE_Continuation cont,
189 void *cont_cls)
190{
191 struct Plugin *plugin = cls;
192 sqlite3_stmt *stmt = plugin->expire_peerstoredata;
193 struct GNUNET_SQ_QueryParam params[] = {
194 GNUNET_SQ_query_param_absolute_time (&now),
195 GNUNET_SQ_query_param_end
196 };
197
198 if (GNUNET_OK !=
199 GNUNET_SQ_bind (stmt,
200 params))
201 {
202 LOG_SQLITE (plugin,
203 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
204 "sqlite3_bind");
205 GNUNET_SQ_reset (plugin->dbh,
206 stmt);
207 return GNUNET_SYSERR;
208 }
209 if (SQLITE_DONE != sqlite3_step (stmt))
210 {
211 LOG_SQLITE (plugin,
212 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
213 "sqlite3_step");
214 GNUNET_SQ_reset (plugin->dbh,
215 stmt);
216 return GNUNET_SYSERR;
217 }
218 if (NULL != cont)
219 cont (cont_cls,
220 sqlite3_changes (plugin->dbh));
221 GNUNET_SQ_reset (plugin->dbh,
222 stmt);
223 return GNUNET_OK;
224}
225
226
227/**
228 * Iterate over the records given an optional peer id
229 * and/or key.
230 *
231 * @param cls closure (internal context for the plugin)
232 * @param sub_system name of sub system
233 * @param peer Peer identity (can be NULL)
234 * @param key entry key string (can be NULL)
235 * @param iter function to call asynchronously with the results, terminated
236 * by a NULL result
237 * @param iter_cls closure for @a iter
238 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and iter is not
239 * called
240 */
241static int
242peerstore_sqlite_iterate_records (void *cls,
243 const char *sub_system,
244 const struct GNUNET_PeerIdentity *peer,
245 const char *key,
246 GNUNET_PEERSTORE_Processor iter,
247 void *iter_cls)
248{
249 struct Plugin *plugin = cls;
250 sqlite3_stmt *stmt;
251 int err = 0;
252 int sret;
253 struct GNUNET_PEERSTORE_Record rec;
254
255 LOG (GNUNET_ERROR_TYPE_DEBUG,
256 "Executing iterate request on sqlite db.\n");
257 if (NULL == peer)
258 {
259 if (NULL == key)
260 {
261 struct GNUNET_SQ_QueryParam params[] = {
262 GNUNET_SQ_query_param_string (sub_system),
263 GNUNET_SQ_query_param_end
264 };
265
266 stmt = plugin->select_peerstoredata;
267 err = GNUNET_SQ_bind (stmt,
268 params);
269 }
270 else
271 {
272 struct GNUNET_SQ_QueryParam params[] = {
273 GNUNET_SQ_query_param_string (sub_system),
274 GNUNET_SQ_query_param_string (key),
275 GNUNET_SQ_query_param_end
276 };
277
278 stmt = plugin->select_peerstoredata_by_key;
279 err = GNUNET_SQ_bind (stmt,
280 params);
281 }
282 }
283 else
284 {
285 if (NULL == key)
286 {
287 struct GNUNET_SQ_QueryParam params[] = {
288 GNUNET_SQ_query_param_string (sub_system),
289 GNUNET_SQ_query_param_auto_from_type (peer),
290 GNUNET_SQ_query_param_end
291 };
292
293 stmt = plugin->select_peerstoredata_by_pid;
294 err = GNUNET_SQ_bind (stmt,
295 params);
296 }
297 else
298 {
299 struct GNUNET_SQ_QueryParam params[] = {
300 GNUNET_SQ_query_param_string (sub_system),
301 GNUNET_SQ_query_param_auto_from_type (peer),
302 GNUNET_SQ_query_param_string (key),
303 GNUNET_SQ_query_param_end
304 };
305
306 stmt = plugin->select_peerstoredata_by_all;
307 err = GNUNET_SQ_bind (stmt,
308 params);
309 }
310 }
311
312 if (GNUNET_OK != err)
313 {
314 LOG_SQLITE (plugin,
315 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
316 "sqlite3_bind_XXXX");
317 GNUNET_SQ_reset (plugin->dbh,
318 stmt);
319 return GNUNET_SYSERR;
320 }
321
322 err = 0;
323 while (SQLITE_ROW == (sret = sqlite3_step (stmt)))
324 {
325 LOG (GNUNET_ERROR_TYPE_DEBUG,
326 "Returning a matched record.\n");
327 struct GNUNET_SQ_ResultSpec rs[] = {
328 GNUNET_SQ_result_spec_string (&rec.sub_system),
329 GNUNET_SQ_result_spec_auto_from_type (&rec.peer),
330 GNUNET_SQ_result_spec_string (&rec.key),
331 GNUNET_SQ_result_spec_variable_size (&rec.value, &rec.value_size),
332 GNUNET_SQ_result_spec_absolute_time (&rec.expiry),
333 GNUNET_SQ_result_spec_end
334 };
335
336 if (GNUNET_OK !=
337 GNUNET_SQ_extract_result (stmt,
338 rs))
339 {
340 GNUNET_break (0);
341 break;
342 }
343 if (NULL != iter)
344 iter (iter_cls,
345 &rec,
346 NULL);
347 GNUNET_SQ_cleanup_result (rs);
348 }
349 if (SQLITE_DONE != sret)
350 {
351 LOG_SQLITE (plugin,
352 GNUNET_ERROR_TYPE_ERROR,
353 "sqlite_step");
354 err = 1;
355 }
356 GNUNET_SQ_reset (plugin->dbh,
357 stmt);
358 if (NULL != iter)
359 iter (iter_cls,
360 NULL,
361 err ? "sqlite error" : NULL);
362 return GNUNET_OK;
363}
364
365
366/**
367 * Store a record in the peerstore.
368 * Key is the combination of sub system and peer identity.
369 * One key can store multiple values.
370 *
371 * @param cls closure (internal context for the plugin)
372 * @param sub_system name of the GNUnet sub system responsible
373 * @param peer peer identity
374 * @param key record key string
375 * @param value value to be stored
376 * @param size size of value to be stored
377 * @param expiry absolute time after which the record is (possibly) deleted
378 * @param options options related to the store operation
379 * @param cont continuation called when record is stored
380 * @param cont_cls continuation closure
381 * @return #GNUNET_OK on success, else #GNUNET_SYSERR and cont is not called
382 */
383static int
384peerstore_sqlite_store_record (void *cls,
385 const char *sub_system,
386 const struct GNUNET_PeerIdentity *peer,
387 const char *key,
388 const void *value,
389 size_t size,
390 struct GNUNET_TIME_Absolute expiry,
391 enum GNUNET_PEERSTORE_StoreOption options,
392 GNUNET_PEERSTORE_Continuation cont,
393 void *cont_cls)
394{
395 struct Plugin *plugin = cls;
396 sqlite3_stmt *stmt = plugin->insert_peerstoredata;
397 struct GNUNET_SQ_QueryParam params[] = {
398 GNUNET_SQ_query_param_string (sub_system),
399 GNUNET_SQ_query_param_auto_from_type (peer),
400 GNUNET_SQ_query_param_string (key),
401 GNUNET_SQ_query_param_fixed_size (value, size),
402 GNUNET_SQ_query_param_absolute_time (&expiry),
403 GNUNET_SQ_query_param_end
404 };
405
406 if (GNUNET_PEERSTORE_STOREOPTION_REPLACE == options)
407 {
408 peerstore_sqlite_delete_records (cls,
409 sub_system,
410 peer,
411 key);
412 }
413 if (GNUNET_OK !=
414 GNUNET_SQ_bind (stmt,
415 params))
416 LOG_SQLITE (plugin,
417 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
418 "sqlite3_bind");
419 else if (SQLITE_DONE != sqlite3_step (stmt))
420 {
421 LOG_SQLITE (plugin,
422 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
423 "sqlite3_step");
424 }
425 GNUNET_SQ_reset (plugin->dbh,
426 stmt);
427 if (NULL != cont)
428 cont (cont_cls,
429 GNUNET_OK);
430 return GNUNET_OK;
431}
432
433
434/**
435 * @brief Prepare a SQL statement
436 *
437 * @param dbh handle to the database
438 * @param sql SQL statement, UTF-8 encoded
439 * @return 0 on success
440 */
441static int
442sql_exec (sqlite3 *dbh,
443 const char *sql)
444{
445 int result;
446
447 result = sqlite3_exec (dbh,
448 sql,
449 NULL,
450 NULL,
451 NULL);
452 LOG (GNUNET_ERROR_TYPE_DEBUG,
453 "Executed `%s' / %d\n",
454 sql,
455 result);
456 if (SQLITE_OK != result)
457 LOG (GNUNET_ERROR_TYPE_ERROR,
458 _ ("Error executing SQL query: %s\n %s\n"),
459 sqlite3_errmsg (dbh),
460 sql);
461 return result;
462}
463
464
465/**
466 * @brief Prepare a SQL statement
467 *
468 * @param dbh handle to the database
469 * @param sql SQL statement, UTF-8 encoded
470 * @param stmt set to the prepared statement
471 * @return 0 on success
472 */
473static int
474sql_prepare (sqlite3 *dbh,
475 const char *sql,
476 sqlite3_stmt **stmt)
477{
478 char *tail;
479 int result;
480
481 result = sqlite3_prepare_v2 (dbh,
482 sql,
483 strlen (sql),
484 stmt,
485 (const char **) &tail);
486 LOG (GNUNET_ERROR_TYPE_DEBUG,
487 "Prepared `%s' / %p: %d\n",
488 sql,
489 *stmt,
490 result);
491 if (SQLITE_OK != result)
492 LOG (GNUNET_ERROR_TYPE_ERROR,
493 _ ("Error preparing SQL query: %s\n %s\n"),
494 sqlite3_errmsg (dbh),
495 sql);
496 return result;
497}
498
499
500/**
501 * Initialize the database connections and associated
502 * data structures (create tables and indices
503 * as needed as well).
504 *
505 * @param plugin the plugin context (state for this module)
506 * @return #GNUNET_OK on success
507 */
508static int
509database_setup (struct Plugin *plugin)
510{
511 char *filename;
512
513 if (GNUNET_OK !=
514 GNUNET_CONFIGURATION_get_value_filename (plugin->cfg,
515 "peerstore-sqlite",
516 "FILENAME",
517 &filename))
518 {
519 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
520 "peerstore-sqlite",
521 "FILENAME");
522 return GNUNET_SYSERR;
523 }
524 if (GNUNET_OK != GNUNET_DISK_file_test (filename))
525 {
526 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (filename))
527 {
528 GNUNET_break (0);
529 GNUNET_free (filename);
530 return GNUNET_SYSERR;
531 }
532 }
533 /* filename should be UTF-8-encoded. If it isn't, it's a bug */
534 plugin->fn = filename;
535 /* Open database and precompile statements */
536 if (SQLITE_OK != sqlite3_open (plugin->fn,
537 &plugin->dbh))
538 {
539 LOG (GNUNET_ERROR_TYPE_ERROR,
540 _ ("Unable to initialize SQLite: %s.\n"),
541 sqlite3_errmsg (plugin->dbh));
542 return GNUNET_SYSERR;
543 }
544 sql_exec (plugin->dbh,
545 "PRAGMA temp_store=MEMORY");
546 sql_exec (plugin->dbh,
547 "PRAGMA synchronous=OFF");
548 sql_exec (plugin->dbh,
549 "PRAGMA legacy_file_format=OFF");
550 sql_exec (plugin->dbh,
551 "PRAGMA auto_vacuum=INCREMENTAL");
552 sql_exec (plugin->dbh,
553 "PRAGMA encoding=\"UTF-8\"");
554 sql_exec (plugin->dbh,
555 "PRAGMA page_size=4096");
556 sqlite3_busy_timeout (plugin->dbh,
557 BUSY_TIMEOUT_MS);
558 /* Create tables */
559 sql_exec (plugin->dbh,
560 "CREATE TABLE IF NOT EXISTS peerstoredata (\n"
561 " sub_system TEXT NOT NULL,\n"
562 " peer_id BLOB NOT NULL,\n"
563 " key TEXT NOT NULL,\n"
564 " value BLOB NULL,\n"
565 " expiry INT8 NOT NULL" ");");
566 /* Create Indices */
567 if (SQLITE_OK !=
568 sqlite3_exec (plugin->dbh,
569 "CREATE INDEX IF NOT EXISTS peerstoredata_key_index ON peerstoredata (sub_system, peer_id, key)",
570 NULL,
571 NULL,
572 NULL))
573 {
574 LOG (GNUNET_ERROR_TYPE_ERROR,
575 _ ("Unable to create indices: %s.\n"),
576 sqlite3_errmsg (plugin->dbh));
577 return GNUNET_SYSERR;
578 }
579 /* Prepare statements */
580
581 sql_prepare (plugin->dbh,
582 "INSERT INTO peerstoredata (sub_system, peer_id, key, value, expiry)"
583 " VALUES (?,?,?,?,?);",
584 &plugin->insert_peerstoredata);
585 sql_prepare (plugin->dbh,
586 "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata"
587 " WHERE sub_system = ?",
588 &plugin->select_peerstoredata);
589 sql_prepare (plugin->dbh,
590 "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata"
591 " WHERE sub_system = ?"
592 " AND peer_id = ?",
593 &plugin->select_peerstoredata_by_pid);
594 sql_prepare (plugin->dbh,
595 "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata"
596 " WHERE sub_system = ?"
597 " AND key = ?",
598 &plugin->select_peerstoredata_by_key);
599 sql_prepare (plugin->dbh,
600 "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata"
601 " WHERE sub_system = ?"
602 " AND peer_id = ?" " AND key = ?",
603 &plugin->select_peerstoredata_by_all);
604 sql_prepare (plugin->dbh,
605 "DELETE FROM peerstoredata"
606 " WHERE expiry < ?",
607 &plugin->expire_peerstoredata);
608 sql_prepare (plugin->dbh,
609 "DELETE FROM peerstoredata"
610 " WHERE sub_system = ?"
611 " AND peer_id = ?" " AND key = ?",
612 &plugin->delete_peerstoredata);
613 return GNUNET_OK;
614}
615
616
617/**
618 * Shutdown database connection and associate data
619 * structures.
620 * @param plugin the plugin context (state for this module)
621 */
622static void
623database_shutdown (struct Plugin *plugin)
624{
625 int result;
626 sqlite3_stmt *stmt;
627
628 while (NULL != (stmt = sqlite3_next_stmt (plugin->dbh,
629 NULL)))
630 {
631 result = sqlite3_finalize (stmt);
632 if (SQLITE_OK != result)
633 LOG (GNUNET_ERROR_TYPE_WARNING,
634 "Failed to close statement %p: %d\n",
635 stmt,
636 result);
637 }
638 if (SQLITE_OK != sqlite3_close (plugin->dbh))
639 LOG_SQLITE (plugin,
640 GNUNET_ERROR_TYPE_ERROR,
641 "sqlite3_close");
642 GNUNET_free (plugin->fn);
643}
644
645
646/**
647 * Entry point for the plugin.
648 *
649 * @param cls The struct GNUNET_CONFIGURATION_Handle.
650 * @return NULL on error, otherwise the plugin context
651 */
652void *
653libgnunet_plugin_peerstore_sqlite_init (void *cls)
654{
655 static struct Plugin plugin;
656 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
657 struct GNUNET_PEERSTORE_PluginFunctions *api;
658
659 if (NULL != plugin.cfg)
660 return NULL; /* can only initialize once! */
661 memset (&plugin,
662 0,
663 sizeof(struct Plugin));
664 plugin.cfg = cfg;
665 if (GNUNET_OK != database_setup (&plugin))
666 {
667 database_shutdown (&plugin);
668 return NULL;
669 }
670 api = GNUNET_new (struct GNUNET_PEERSTORE_PluginFunctions);
671 api->cls = &plugin;
672 api->store_record = &peerstore_sqlite_store_record;
673 api->iterate_records = &peerstore_sqlite_iterate_records;
674 api->expire_records = &peerstore_sqlite_expire_records;
675 LOG (GNUNET_ERROR_TYPE_DEBUG,
676 "Sqlite plugin is running\n");
677 return api;
678}
679
680
681/**
682 * Exit point from the plugin.
683 *
684 * @param cls The plugin context (as returned by "init")
685 * @return Always NULL
686 */
687void *
688libgnunet_plugin_peerstore_sqlite_done (void *cls)
689{
690 struct GNUNET_PEERSTORE_PluginFunctions *api = cls;
691 struct Plugin *plugin = api->cls;
692
693 database_shutdown (plugin);
694 plugin->cfg = NULL;
695 GNUNET_free (api);
696 LOG (GNUNET_ERROR_TYPE_DEBUG,
697 "Sqlite plugin is finished\n");
698 return NULL;
699}
700
701
702/* end of plugin_peerstore_sqlite.c */
diff --git a/src/plugin/peerstore/test_plugin_peerstore.c b/src/plugin/peerstore/test_plugin_peerstore.c
new file mode 100644
index 000000000..bce62dda9
--- /dev/null
+++ b/src/plugin/peerstore/test_plugin_peerstore.c
@@ -0,0 +1,224 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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 * @file namestore/test_plugin_namestore.c
22 * @brief Test for the namestore plugins
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_peerstore_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
39static struct GNUNET_PEERSTORE_PluginFunctions *psp;
40
41static struct GNUNET_PeerIdentity p1;
42
43
44/**
45 * Function called when the service shuts down. Unloads our namestore
46 * plugin.
47 *
48 * @param api api to unload
49 */
50static void
51unload_plugin (struct GNUNET_PEERSTORE_PluginFunctions *api)
52{
53 char *libname;
54
55 GNUNET_asprintf (&libname,
56 "libgnunet_plugin_peer_%s",
57 plugin_name);
58 GNUNET_break (NULL ==
59 GNUNET_PLUGIN_unload (libname,
60 api));
61 GNUNET_free (libname);
62}
63
64
65/**
66 * Load the namestore plugin.
67 *
68 * @param cfg configuration to pass
69 * @return NULL on error
70 */
71static struct GNUNET_PEERSTORE_PluginFunctions *
72load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg)
73{
74 struct GNUNET_PEERSTORE_PluginFunctions *ret;
75 char *libname;
76
77 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
78 _ ("Loading `%s' peer plugin\n"),
79 plugin_name);
80 GNUNET_asprintf (&libname,
81 "libgnunet_plugin_peerstore_%s",
82 plugin_name);
83 if (NULL == (ret = GNUNET_PLUGIN_load (libname,
84 (void *) cfg)))
85 {
86 fprintf (stderr,
87 "Failed to load plugin `%s'!\n",
88 plugin_name);
89 GNUNET_free (libname);
90 return NULL;
91 }
92 GNUNET_free (libname);
93 return ret;
94}
95
96
97static void
98test_record (void *cls,
99 const struct GNUNET_PEERSTORE_Record *record,
100 const char *error)
101{
102 const struct GNUNET_PeerIdentity *id = cls;
103 const char*testval = "test_val";
104
105 if (NULL == record)
106 {
107 unload_plugin (psp);
108 return;
109 }
110 GNUNET_assert (0 == memcmp (&record->peer,
111 id,
112 sizeof(struct GNUNET_PeerIdentity)));
113 GNUNET_assert (0 == strcmp ("subsys",
114 record->sub_system));
115 GNUNET_assert (0 == strcmp ("key",
116 record->key));
117 GNUNET_assert (0 == memcmp (testval,
118 record->value,
119 strlen (testval)));
120 ok = 0;
121}
122
123
124static void
125get_record (struct GNUNET_PEERSTORE_PluginFunctions *psp,
126 const struct GNUNET_PeerIdentity *identity)
127{
128 GNUNET_assert (GNUNET_OK ==
129 psp->iterate_records (psp->cls,
130 "subsys",
131 identity,
132 "key",
133 &test_record,
134 (void *) identity));
135}
136
137
138static void
139store_cont (void *cls,
140 int status)
141{
142 GNUNET_assert (GNUNET_OK == status);
143 get_record (psp,
144 &p1);
145}
146
147
148static void
149put_record (struct GNUNET_PEERSTORE_PluginFunctions *psp,
150 const struct GNUNET_PeerIdentity *identity)
151{
152 GNUNET_assert (GNUNET_OK ==
153 psp->store_record (psp->cls,
154 "subsys",
155 identity,
156 "key",
157 "test_value",
158 strlen ("test_value"),
159 GNUNET_TIME_absolute_get (),
160 GNUNET_PEERSTORE_STOREOPTION_REPLACE,
161 &store_cont,
162 NULL));
163}
164
165
166static void
167run (void *cls,
168 char *const *args,
169 const char *cfgfile,
170 const struct GNUNET_CONFIGURATION_Handle *cfg)
171{
172 ok = 1;
173 psp = load_plugin (cfg);
174 if (NULL == psp)
175 {
176 fprintf (stderr,
177 "%s",
178 "Failed to initialize peerstore. Database likely not setup, skipping test.\n");
179 return;
180 }
181 memset (&p1, 1, sizeof(p1));
182 put_record (psp,
183 &p1);
184}
185
186
187int
188main (int argc, char *argv[])
189{
190 char cfg_name[PATH_MAX];
191 char *const xargv[] = {
192 "test-plugin-peerstore",
193 "-c",
194 cfg_name,
195 NULL
196 };
197 struct GNUNET_GETOPT_CommandLineOption options[] = {
198 GNUNET_GETOPT_OPTION_END
199 };
200
201 GNUNET_log_setup ("test-plugin-peerstore",
202 "WARNING",
203 NULL);
204 plugin_name = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]);
205 GNUNET_snprintf (cfg_name,
206 sizeof(cfg_name),
207 "test_plugin_peerstore_%s.conf",
208 plugin_name);
209 GNUNET_PROGRAM_run ((sizeof(xargv) / sizeof(char *)) - 1,
210 xargv,
211 "test-plugin-peerstore",
212 "nohelp",
213 options,
214 &run,
215 NULL);
216 if (ok != 0)
217 fprintf (stderr,
218 "Missed some testcases: %d\n",
219 ok);
220 return ok;
221}
222
223
224/* end of test_plugin_peerstore.c */
diff --git a/src/plugin/peerstore/test_plugin_peerstore_flat.conf b/src/plugin/peerstore/test_plugin_peerstore_flat.conf
new file mode 100644
index 000000000..c55b1e9d6
--- /dev/null
+++ b/src/plugin/peerstore/test_plugin_peerstore_flat.conf
@@ -0,0 +1,5 @@
1[peerstore-flat]
2FILENAME = $GNUNET_TMP/gnunet-test-plugin-namestore-flat/flatdb
3
4[peerstore]
5# PREFIX = valgrind --log-file=/home/schanzen/dev/gnunet/src/peerstore/vg_log
diff --git a/src/plugin/peerstore/test_plugin_peerstore_sqlite.conf b/src/plugin/peerstore/test_plugin_peerstore_sqlite.conf
new file mode 100644
index 000000000..dcf1fc1a4
--- /dev/null
+++ b/src/plugin/peerstore/test_plugin_peerstore_sqlite.conf
@@ -0,0 +1,2 @@
1[peerstore-sqlite]
2FILENAME = $GNUNET_TMP/gnunet-test-plugin-peerstore-sqlite/sqlite.db
diff --git a/src/plugin/reclaim/Makefile.am b/src/plugin/reclaim/Makefile.am
index 0b5159c6b..e8ac2f94d 100644
--- a/src/plugin/reclaim/Makefile.am
+++ b/src/plugin/reclaim/Makefile.am
@@ -59,11 +59,11 @@ endif
59 59
60 60
61libgnunet_plugin_reclaim_credential_jwt_la_SOURCES = \ 61libgnunet_plugin_reclaim_credential_jwt_la_SOURCES = \
62 plugin_reclaim_credential_jwt.c 62 plugin_reclaim_credential_jwt.c \
63 $(top_builddir)/src/service/reclaim/reclaim_attribute.c \
64 $(top_builddir)/src/service/reclaim/reclaim_credential.c
63libgnunet_plugin_reclaim_credential_jwt_la_LIBADD = \ 65libgnunet_plugin_reclaim_credential_jwt_la_LIBADD = \
64 $(top_builddir)/src/service/identity/libgnunetidentity.la \
65 $(top_builddir)/src/lib/util/libgnunetutil.la \ 66 $(top_builddir)/src/lib/util/libgnunetutil.la \
66 $(top_builddir)/src/service/reclaim/libgnunetreclaim.la \
67 -ljansson\ 67 -ljansson\
68 $(LTLIBINTL) 68 $(LTLIBINTL)
69libgnunet_plugin_reclaim_credential_jwt_la_LDFLAGS = \ 69libgnunet_plugin_reclaim_credential_jwt_la_LDFLAGS = \
diff --git a/src/plugin/reclaim/meson.build b/src/plugin/reclaim/meson.build
index 51346b108..df0289ca2 100644
--- a/src/plugin/reclaim/meson.build
+++ b/src/plugin/reclaim/meson.build
@@ -7,9 +7,7 @@ shared_module('gnunet_plugin_gnsrecord_reclaim',
7 7
8shared_module('gnunet_plugin_reclaim_attribute_basic', 8shared_module('gnunet_plugin_reclaim_attribute_basic',
9 ['plugin_reclaim_attribute_basic.c'], 9 ['plugin_reclaim_attribute_basic.c'],
10 dependencies: [libgnunetrest_dep, 10 dependencies: [
11 libgnunetidentity_dep,
12 libgnunetreclaim_dep,
13 libgnunetjson_dep, 11 libgnunetjson_dep,
14 libgnunetutil_dep, 12 libgnunetutil_dep,
15 json_dep], 13 json_dep],
@@ -18,9 +16,7 @@ shared_module('gnunet_plugin_reclaim_attribute_basic',
18 install_dir: get_option('libdir') / 'gnunet') 16 install_dir: get_option('libdir') / 'gnunet')
19shared_module('gnunet_plugin_reclaim_credential_jwt', 17shared_module('gnunet_plugin_reclaim_credential_jwt',
20 ['plugin_reclaim_credential_jwt.c'], 18 ['plugin_reclaim_credential_jwt.c'],
21 dependencies: [libgnunetrest_dep, 19 dependencies: [
22 libgnunetidentity_dep,
23 libgnunetreclaim_dep,
24 libgnunetjson_dep, 20 libgnunetjson_dep,
25 libgnunetutil_dep, 21 libgnunetutil_dep,
26 json_dep], 22 json_dep],
diff --git a/src/plugin/regex/Makefile.am b/src/plugin/regex/Makefile.am
new file mode 100644
index 000000000..a39cfc927
--- /dev/null
+++ b/src/plugin/regex/Makefile.am
@@ -0,0 +1,40 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage
6endif
7
8pkgcfgdir= $(pkgdatadir)/config.d/
9
10libexecdir= $(pkglibdir)/libexec/
11
12plugindir = $(libdir)/gnunet
13
14lib_LTLIBRARIES = \
15 libgnunetregexblock.la
16
17libgnunetregexblock_la_SOURCES = \
18 regex_block_lib.c regex_block_lib.h
19libgnunetregexblock_la_LIBADD = \
20 $(top_builddir)/src/lib/util/libgnunetutil.la \
21 $(XLIB) \
22 $(LTLIBINTL)
23libgnunetregexblock_la_LDFLAGS = \
24 $(GN_LIB_LDFLAGS) \
25 -version-info 1:0:0
26
27
28plugin_LTLIBRARIES = \
29 libgnunet_plugin_block_regex.la
30
31libgnunet_plugin_block_regex_la_SOURCES = \
32 plugin_block_regex.c
33libgnunet_plugin_block_regex_la_LIBADD = \
34 libgnunetregexblock.la \
35 $(top_builddir)/src/lib/block/libgnunetblock.la \
36 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \
37 $(top_builddir)/src/lib/util/libgnunetutil.la
38libgnunet_plugin_block_regex_la_LDFLAGS = \
39 $(GN_LIBINTL) \
40 $(GN_PLUGIN_LDFLAGS)
diff --git a/src/plugin/regex/meson.build b/src/plugin/regex/meson.build
new file mode 100644
index 000000000..0e1b4b08d
--- /dev/null
+++ b/src/plugin/regex/meson.build
@@ -0,0 +1,23 @@
1libgnunetregexblock_src = ['regex_block_lib.c']
2
3libgnunetregexblock = library('gnunetregexblock',
4 libgnunetregexblock_src,
5 soversion: '1',
6 version: '1.0.0',
7 dependencies: libgnunetutil_dep,
8 include_directories: [incdir, configuration_inc],
9 install: true,
10 install_dir: get_option('libdir'))
11libgnunetregexblock_dep = declare_dependency(link_with : libgnunetregexblock)
12
13shared_module('gnunet_plugin_block_regex',
14 ['plugin_block_regex.c'],
15 dependencies: [libgnunetutil_dep,
16 libgnunetregexblock_dep,
17 libgnunetblock_dep,
18 libgnunetblockgroup_dep],
19 include_directories: [incdir, configuration_inc],
20 install:true,
21 install_dir: get_option('libdir')/'gnunet')
22
23
diff --git a/src/plugin/regex/plugin_block_regex.c b/src/plugin/regex/plugin_block_regex.c
new file mode 100644
index 000000000..5f23a32df
--- /dev/null
+++ b/src/plugin/regex/plugin_block_regex.c
@@ -0,0 +1,380 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 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 regex/plugin_block_regex.c
23 * @brief blocks used for regex storage and search
24 * @author Bartlomiej Polot
25 */
26#include "platform.h"
27#include "gnunet_block_plugin.h"
28#include "gnunet_block_group_lib.h"
29#include "block_regex.h"
30#include "regex_block_lib.h"
31#include "gnunet_signatures.h"
32
33
34/**
35 * Number of bits we set per entry in the bloomfilter.
36 * Do not change!
37 */
38#define BLOOMFILTER_K 16
39
40
41/**
42 * How big is the BF we use for REGEX blocks?
43 */
44#define REGEX_BF_SIZE 8
45
46
47/**
48 * Create a new block group.
49 *
50 * @param ctx block context in which the block group is created
51 * @param type type of the block for which we are creating the group
52 * @param raw_data optional serialized prior state of the group, NULL if unavailable/fresh
53 * @param raw_data_size number of bytes in @a raw_data, 0 if unavailable/fresh
54 * @param va variable arguments specific to @a type
55 * @return block group handle, NULL if block groups are not supported
56 * by this @a type of block (this is not an error)
57 */
58static struct GNUNET_BLOCK_Group *
59block_plugin_regex_create_group (void *cls,
60 enum GNUNET_BLOCK_Type type,
61 const void *raw_data,
62 size_t raw_data_size,
63 va_list va)
64{
65 unsigned int bf_size;
66 const char *guard;
67
68 guard = va_arg (va, const char *);
69 if (0 == strcmp (guard,
70 "seen-set-size"))
71 bf_size = GNUNET_BLOCK_GROUP_compute_bloomfilter_size (va_arg (va, unsigned
72 int),
73 BLOOMFILTER_K);
74 else if (0 == strcmp (guard,
75 "filter-size"))
76 bf_size = va_arg (va, unsigned int);
77 else
78 {
79 GNUNET_break (0);
80 bf_size = REGEX_BF_SIZE;
81 }
82 GNUNET_break (NULL == va_arg (va, const char *));
83 return GNUNET_BLOCK_GROUP_bf_create (cls,
84 bf_size,
85 BLOOMFILTER_K,
86 type,
87 raw_data,
88 raw_data_size);
89}
90
91
92/**
93 * Function called to validate a query.
94 *
95 * @param cls closure
96 * @param ctx block context
97 * @param type block type
98 * @param query original query (hash)
99 * @param xquery extrended query data (can be NULL, depending on type)
100 * @param xquery_size number of bytes in @a xquery
101 * @return #GNUNET_OK if the query is fine, #GNUNET_NO if not
102 */
103static enum GNUNET_GenericReturnValue
104block_plugin_regex_check_query (void *cls,
105 enum GNUNET_BLOCK_Type type,
106 const struct GNUNET_HashCode *query,
107 const void *xquery,
108 size_t xquery_size)
109{
110 switch (type)
111 {
112 case GNUNET_BLOCK_TYPE_REGEX:
113 if (0 != xquery_size)
114 {
115 const char *s;
116
117 s = (const char *) xquery;
118 if ('\0' != s[xquery_size - 1]) /* must be valid 0-terminated string */
119 {
120 GNUNET_break_op (0);
121 return GNUNET_NO;
122 }
123 }
124 return GNUNET_OK;
125 case GNUNET_BLOCK_TYPE_REGEX_ACCEPT:
126 if (0 != xquery_size)
127 {
128 GNUNET_break_op (0);
129 return GNUNET_NO;
130 }
131 return GNUNET_OK;
132 default:
133 GNUNET_break (0);
134 return GNUNET_SYSERR;
135 }
136}
137
138
139/**
140 * Function called to validate a block for storage.
141 *
142 * @param cls closure
143 * @param type block type
144 * @param block block data to validate
145 * @param block_size number of bytes in @a block
146 * @return #GNUNET_OK if the block is fine, #GNUNET_NO if not
147 */
148static enum GNUNET_GenericReturnValue
149block_plugin_regex_check_block (void *cls,
150 enum GNUNET_BLOCK_Type type,
151 const void *block,
152 size_t block_size)
153{
154 switch (type)
155 {
156 case GNUNET_BLOCK_TYPE_REGEX:
157 if (GNUNET_SYSERR ==
158 REGEX_BLOCK_check (block,
159 block_size,
160 NULL,
161 NULL))
162 return GNUNET_NO;
163 return GNUNET_OK;
164 case GNUNET_BLOCK_TYPE_REGEX_ACCEPT:
165 {
166 const struct RegexAcceptBlock *rba;
167
168 if (sizeof(struct RegexAcceptBlock) != block_size)
169 {
170 GNUNET_break_op (0);
171 return GNUNET_NO;
172 }
173 rba = block;
174 if (ntohl (rba->purpose.size) !=
175 sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
176 + sizeof(struct GNUNET_TIME_AbsoluteNBO)
177 + sizeof(struct GNUNET_HashCode))
178 {
179 GNUNET_break_op (0);
180 return GNUNET_NO;
181 }
182 if (GNUNET_TIME_absolute_is_past (GNUNET_TIME_absolute_ntoh (
183 rba->expiration_time)))
184 {
185 return GNUNET_NO;
186 }
187 if (GNUNET_OK !=
188 GNUNET_CRYPTO_eddsa_verify_ (GNUNET_SIGNATURE_PURPOSE_REGEX_ACCEPT,
189 &rba->purpose,
190 &rba->signature,
191 &rba->peer.public_key))
192 {
193 GNUNET_break_op (0);
194 return GNUNET_NO;
195 }
196 return GNUNET_OK;
197 }
198 default:
199 GNUNET_break (0);
200 return GNUNET_SYSERR;
201 }
202}
203
204
205/**
206 * Function called to validate a reply to a request. Note that it is assumed
207 * that the reply has already been matched to the key (and signatures checked)
208 * as it would be done with the GetKeyFunction and the
209 * BlockEvaluationFunction.
210 *
211 * @param cls closure
212 * @param type block type
213 * @param group which block group to use for evaluation
214 * @param query original query (hash)
215 * @param xquery extrended query data (can be NULL, depending on type)
216 * @param xquery_size number of bytes in @a xquery
217 * @param reply_block response to validate
218 * @param reply_block_size number of bytes in @a reply_block
219 * @return characterization of result
220 */
221static enum GNUNET_BLOCK_ReplyEvaluationResult
222block_plugin_regex_check_reply (
223 void *cls,
224 enum GNUNET_BLOCK_Type type,
225 struct GNUNET_BLOCK_Group *group,
226 const struct GNUNET_HashCode *query,
227 const void *xquery,
228 size_t xquery_size,
229 const void *reply_block,
230 size_t reply_block_size)
231{
232 struct GNUNET_HashCode chash;
233
234 switch (type)
235 {
236 case GNUNET_BLOCK_TYPE_REGEX:
237 if (0 != xquery_size)
238 {
239 const char *s;
240
241 s = (const char *) xquery;
242 GNUNET_assert ('\0' == s[xquery_size - 1]);
243 }
244 switch (REGEX_BLOCK_check (reply_block,
245 reply_block_size,
246 query,
247 xquery))
248 {
249 case GNUNET_SYSERR:
250 GNUNET_assert (0);
251 case GNUNET_NO:
252 /* xquery mismatch, can happen */
253 return GNUNET_BLOCK_REPLY_IRRELEVANT;
254 default:
255 break;
256 }
257 GNUNET_CRYPTO_hash (reply_block,
258 reply_block_size,
259 &chash);
260 if (GNUNET_YES ==
261 GNUNET_BLOCK_GROUP_bf_test_and_set (group,
262 &chash))
263 return GNUNET_BLOCK_REPLY_OK_DUPLICATE;
264 return GNUNET_BLOCK_REPLY_OK_MORE;
265 case GNUNET_BLOCK_TYPE_REGEX_ACCEPT:
266 {
267 const struct RegexAcceptBlock *rba;
268
269 GNUNET_assert (sizeof(struct RegexAcceptBlock) == reply_block_size);
270 rba = reply_block;
271 GNUNET_assert (ntohl (rba->purpose.size) ==
272 sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
273 + sizeof(struct GNUNET_TIME_AbsoluteNBO)
274 + sizeof(struct GNUNET_HashCode));
275 GNUNET_CRYPTO_hash (reply_block,
276 reply_block_size,
277 &chash);
278 if (GNUNET_YES ==
279 GNUNET_BLOCK_GROUP_bf_test_and_set (group,
280 &chash))
281 return GNUNET_BLOCK_REPLY_OK_DUPLICATE;
282 return GNUNET_BLOCK_REPLY_OK_MORE;
283 }
284 default:
285 GNUNET_break (0);
286 return GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED;
287 }
288 return GNUNET_BLOCK_REPLY_OK_MORE;
289}
290
291
292/**
293 * Function called to obtain the key for a block.
294 *
295 * @param cls closure
296 * @param type block type
297 * @param block block to get the key for
298 * @param block_size number of bytes in @a block
299 * @param key set to the key (query) for the given block
300 * @return #GNUNET_OK on success, #GNUNET_SYSERR if type not supported,
301 * #GNUNET_NO if extracting a key from a block of this type does not work
302 */
303static enum GNUNET_GenericReturnValue
304block_plugin_regex_get_key (void *cls,
305 enum GNUNET_BLOCK_Type type,
306 const void *block,
307 size_t block_size,
308 struct GNUNET_HashCode *key)
309{
310 switch (type)
311 {
312 case GNUNET_BLOCK_TYPE_REGEX:
313 if (GNUNET_OK !=
314 REGEX_BLOCK_get_key (block,
315 block_size,
316 key))
317 {
318 GNUNET_break_op (0);
319 memset (key,
320 0,
321 sizeof (*key));
322 return GNUNET_OK;
323 }
324 return GNUNET_OK;
325 case GNUNET_BLOCK_TYPE_REGEX_ACCEPT:
326 if (sizeof(struct RegexAcceptBlock) != block_size)
327 {
328 GNUNET_break_op (0);
329 memset (key,
330 0,
331 sizeof (*key));
332 return GNUNET_OK;
333 }
334 *key = ((struct RegexAcceptBlock *) block)->key;
335 return GNUNET_OK;
336 default:
337 GNUNET_break (0);
338 return GNUNET_SYSERR;
339 }
340}
341
342
343/**
344 * Entry point for the plugin.
345 */
346void *
347libgnunet_plugin_block_regex_init (void *cls)
348{
349 static const enum GNUNET_BLOCK_Type types[] = {
350 GNUNET_BLOCK_TYPE_REGEX,
351 GNUNET_BLOCK_TYPE_REGEX_ACCEPT,
352 GNUNET_BLOCK_TYPE_ANY /* end of list */
353 };
354 struct GNUNET_BLOCK_PluginFunctions *api;
355
356 api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions);
357 api->get_key = &block_plugin_regex_get_key;
358 api->check_query = &block_plugin_regex_check_query;
359 api->check_block = &block_plugin_regex_check_block;
360 api->check_reply = &block_plugin_regex_check_reply;
361 api->create_group = &block_plugin_regex_create_group;
362 api->types = types;
363 return api;
364}
365
366
367/**
368 * Exit point from the plugin.
369 */
370void *
371libgnunet_plugin_block_regex_done (void *cls)
372{
373 struct GNUNET_BLOCK_PluginFunctions *api = cls;
374
375 GNUNET_free (api);
376 return NULL;
377}
378
379
380/* end of plugin_block_regex.c */
diff --git a/src/plugin/regex/regex_block_lib.c b/src/plugin/regex/regex_block_lib.c
new file mode 100644
index 000000000..048d6d743
--- /dev/null
+++ b/src/plugin/regex/regex_block_lib.c
@@ -0,0 +1,431 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012,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 * @author Bartlomiej Polot
22 * @file regex/regex_block_lib.c
23 * @brief functions for manipulating non-accept blocks stored for
24 * regex in the DHT
25 */
26#include "platform.h"
27#include "regex_block_lib.h"
28#include "gnunet_constants.h"
29
30#define LOG(kind, ...) GNUNET_log_from (kind, "regex-bck", __VA_ARGS__)
31
32GNUNET_NETWORK_STRUCT_BEGIN
33
34/**
35 * Information for each edge.
36 */
37struct EdgeInfo
38{
39 /**
40 * Index of the destination of this edge in the
41 * unique destinations array.
42 */
43 uint16_t destination_index GNUNET_PACKED;
44
45 /**
46 * Number of bytes the token for this edge takes in the
47 * token area.
48 */
49 uint16_t token_length GNUNET_PACKED;
50};
51
52
53/**
54 * @brief Block to announce a regex state.
55 */
56struct RegexBlock
57{
58 /**
59 * Length of the proof regex string.
60 */
61 uint16_t proof_len GNUNET_PACKED;
62
63 /**
64 * Is this state an accepting state?
65 */
66 int16_t is_accepting GNUNET_PACKED;
67
68 /**
69 * Number of edges parting from this state.
70 */
71 uint16_t num_edges GNUNET_PACKED;
72
73 /**
74 * Number of unique destinations reachable from this state.
75 */
76 uint16_t num_destinations GNUNET_PACKED;
77
78 /* followed by 'struct GNUNET_HashCode[num_destinations]' */
79
80 /* followed by 'struct EdgeInfo[edge_destination_indices]' */
81
82 /* followed by 'char proof[n_proof]', NOT 0-terminated */
83
84 /* followed by 'char tokens[num_edges][edge_info[k].token_length]';
85 essentially all of the tokens one after the other in the
86 order of the edges; tokens are NOT 0-terminated */
87};
88
89
90GNUNET_NETWORK_STRUCT_END
91
92
93/**
94 * Test if this block is marked as being an accept state.
95 *
96 * @param block block to test
97 * @param size number of bytes in block
98 * @return #GNUNET_YES if the block is accepting, #GNUNET_NO if not
99 */
100int
101GNUNET_BLOCK_is_accepting (const struct RegexBlock *block,
102 size_t size)
103{
104 if (size < sizeof(struct RegexBlock))
105 {
106 GNUNET_break_op (0);
107 return GNUNET_SYSERR;
108 }
109 return ntohs (block->is_accepting);
110}
111
112
113int
114REGEX_BLOCK_check_proof (const char *proof,
115 size_t proof_len,
116 const struct GNUNET_HashCode *key)
117{
118 struct GNUNET_HashCode key_check;
119
120 if ((NULL == proof) || (NULL == key))
121 {
122 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Proof check failed, was NULL.\n");
123 return GNUNET_NO;
124 }
125 GNUNET_CRYPTO_hash (proof, proof_len, &key_check);
126 return (0 ==
127 GNUNET_CRYPTO_hash_cmp (key, &key_check)) ? GNUNET_OK : GNUNET_NO;
128}
129
130
131/**
132 * Struct to keep track of the xquery while iterating all the edges in a block.
133 */
134struct CheckEdgeContext
135{
136 /**
137 * Xquery: string we are looking for.
138 */
139 const char *xquery;
140
141 /**
142 * Has any edge matched the xquery so far? (GNUNET_OK / GNUNET_NO)
143 */
144 int found;
145};
146
147
148/**
149 * Iterator over all edges in a block, checking for a presence of a given query.
150 *
151 * @param cls Closure, (xquery context).
152 * @param token Token that follows to next state.
153 * @param len Length of token.
154 * @param key Hash of next state.
155 *
156 * @return #GNUNET_YES, to keep iterating
157 */
158static int
159check_edge (void *cls,
160 const char *token,
161 size_t len,
162 const struct GNUNET_HashCode *key)
163{
164 struct CheckEdgeContext *ctx = cls;
165
166 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
167 "edge %.*s [%u]: %s\n",
168 (int) len,
169 token,
170 (unsigned int) len,
171 GNUNET_h2s (key));
172 if (NULL == ctx->xquery)
173 return GNUNET_YES;
174 if (strlen (ctx->xquery) < len)
175 return GNUNET_YES; /* too long */
176 if (0 == strncmp (ctx->xquery, token, len))
177 ctx->found = GNUNET_OK;
178 return GNUNET_YES; /* keep checking for malformed data! */
179}
180
181
182int
183REGEX_BLOCK_check (const struct RegexBlock *block,
184 size_t size,
185 const struct GNUNET_HashCode *query,
186 const char *xquery)
187{
188 struct GNUNET_HashCode key;
189 struct CheckEdgeContext ctx;
190 int res;
191
192 LOG (GNUNET_ERROR_TYPE_DEBUG,
193 "Block check\n");
194 if (GNUNET_OK !=
195 REGEX_BLOCK_get_key (block, size,
196 &key))
197 {
198 GNUNET_break_op (0);
199 return GNUNET_SYSERR;
200 }
201 if ((NULL != query) &&
202 (0 != GNUNET_memcmp (&key,
203 query)) )
204 {
205 GNUNET_break_op (0);
206 return GNUNET_SYSERR;
207 }
208 if ((GNUNET_YES == ntohs (block->is_accepting)) &&
209 ((NULL == xquery) || ('\0' == xquery[0])))
210 {
211 LOG (GNUNET_ERROR_TYPE_DEBUG,
212 " out! Is accepting: %u, xquery %p\n",
213 ntohs (block->is_accepting),
214 xquery);
215 return GNUNET_OK;
216 }
217 ctx.xquery = xquery;
218 ctx.found = GNUNET_NO;
219 res = REGEX_BLOCK_iterate (block, size, &check_edge, &ctx);
220 if (GNUNET_SYSERR == res)
221 return GNUNET_SYSERR;
222 if (NULL == xquery)
223 return GNUNET_YES;
224 LOG (GNUNET_ERROR_TYPE_DEBUG, "Result %d\n", ctx.found);
225 return ctx.found;
226}
227
228
229int
230REGEX_BLOCK_get_key (const struct RegexBlock *block,
231 size_t block_len,
232 struct GNUNET_HashCode *key)
233{
234 uint16_t len;
235 const struct GNUNET_HashCode *destinations;
236 const struct EdgeInfo *edges;
237 uint16_t num_destinations;
238 uint16_t num_edges;
239 size_t total;
240
241 if (block_len < sizeof(struct RegexBlock))
242 {
243 GNUNET_break_op (0);
244 return GNUNET_SYSERR;
245 }
246 num_destinations = ntohs (block->num_destinations);
247 num_edges = ntohs (block->num_edges);
248 len = ntohs (block->proof_len);
249 destinations = (const struct GNUNET_HashCode *) &block[1];
250 edges = (const struct EdgeInfo *) &destinations[num_destinations];
251 total = sizeof(struct RegexBlock) + num_destinations * sizeof(struct
252 GNUNET_HashCode)
253 + num_edges * sizeof(struct EdgeInfo) + len;
254 if (block_len < total)
255 {
256 GNUNET_break_op (0);
257 return GNUNET_SYSERR;
258 }
259 GNUNET_CRYPTO_hash (&edges[num_edges], len, key);
260 return GNUNET_OK;
261}
262
263
264int
265REGEX_BLOCK_iterate (const struct RegexBlock *block,
266 size_t size,
267 REGEX_INTERNAL_EgdeIterator iterator,
268 void *iter_cls)
269{
270 uint16_t len;
271 const struct GNUNET_HashCode *destinations;
272 const struct EdgeInfo *edges;
273 const char *aux;
274 uint16_t num_destinations;
275 uint16_t num_edges;
276 size_t total;
277 unsigned int n;
278 size_t off;
279
280 LOG (GNUNET_ERROR_TYPE_DEBUG, "Block iterate\n");
281 if (size < sizeof(struct RegexBlock))
282 {
283 GNUNET_break_op (0);
284 return GNUNET_SYSERR;
285 }
286 num_destinations = ntohs (block->num_destinations);
287 num_edges = ntohs (block->num_edges);
288 len = ntohs (block->proof_len);
289 destinations = (const struct GNUNET_HashCode *) &block[1];
290 edges = (const struct EdgeInfo *) &destinations[num_destinations];
291 aux = (const char *) &edges[num_edges];
292 total = sizeof(struct RegexBlock) + num_destinations * sizeof(struct
293 GNUNET_HashCode)
294 + num_edges * sizeof(struct EdgeInfo) + len;
295 if (size < total)
296 {
297 GNUNET_break_op (0);
298 return GNUNET_SYSERR;
299 }
300 for (n = 0; n < num_edges; n++)
301 total += ntohs (edges[n].token_length);
302 if (size != total)
303 {
304 fprintf (stderr, "Expected %u, got %u\n",
305 (unsigned int) size,
306 (unsigned int) total);
307 GNUNET_break_op (0);
308 return GNUNET_SYSERR;
309 }
310 off = len;
311 LOG (GNUNET_ERROR_TYPE_DEBUG,
312 "Start iterating block of size %lu, proof %u, off %lu edges %u\n",
313 (unsigned long) size, len, (unsigned long) off, n);
314 /* &aux[off] always points to our token */
315 for (n = 0; n < num_edges; n++)
316 {
317 LOG (GNUNET_ERROR_TYPE_DEBUG,
318 "Edge %u/%u, off %lu tokenlen %u (%.*s)\n",
319 n + 1, num_edges, (unsigned long) off,
320 ntohs (edges[n].token_length), ntohs (edges[n].token_length),
321 &aux[off]);
322 if (NULL != iterator)
323 if (GNUNET_NO == iterator (iter_cls,
324 &aux[off],
325 ntohs (edges[n].token_length),
326 &destinations[ntohs (
327 edges[n].destination_index)]))
328 return GNUNET_OK;
329 off += ntohs (edges[n].token_length);
330 }
331 return GNUNET_OK;
332}
333
334
335/**
336 * Construct a regex block to be stored in the DHT.
337 *
338 * @param proof proof string for the block
339 * @param num_edges number of edges in the block
340 * @param edges the edges of the block
341 * @param accepting is this an accepting state
342 * @param rsize set to the size of the returned block (OUT-only)
343 * @return the regex block, NULL on error
344 */
345struct RegexBlock *
346REGEX_BLOCK_create (const char *proof,
347 unsigned int num_edges,
348 const struct REGEX_BLOCK_Edge *edges,
349 int accepting,
350 size_t *rsize)
351{
352 struct RegexBlock *block;
353 struct GNUNET_HashCode destinations[1024]; /* 1024 = 64k/64 bytes/key == absolute MAX */
354 uint16_t destination_indices[num_edges];
355 struct GNUNET_HashCode *dests;
356 struct EdgeInfo *edgeinfos;
357 size_t off;
358 size_t len;
359 size_t total;
360 size_t slen;
361 unsigned int unique_destinations;
362 unsigned int j;
363 unsigned int i;
364 char *aux;
365
366 len = strlen (proof);
367 if (len > UINT16_MAX)
368 {
369 GNUNET_break (0);
370 return NULL;
371 }
372 unique_destinations = 0;
373 total = sizeof(struct RegexBlock) + len;
374 for (i = 0; i < num_edges; i++)
375 {
376 slen = strlen (edges[i].label);
377 if (slen > UINT16_MAX)
378 {
379 GNUNET_break (0);
380 return NULL;
381 }
382 total += slen;
383 for (j = 0; j < unique_destinations; j++)
384 if (0 == memcmp (&destinations[j],
385 &edges[i].destination,
386 sizeof(struct GNUNET_HashCode)))
387 break;
388 if (j >= 1024)
389 {
390 GNUNET_break (0);
391 return NULL;
392 }
393 destination_indices[i] = j;
394 if (j == unique_destinations)
395 destinations[unique_destinations++] = edges[i].destination;
396 }
397 total += num_edges * sizeof(struct EdgeInfo) + unique_destinations
398 * sizeof(struct GNUNET_HashCode);
399 if (total >= GNUNET_CONSTANTS_MAX_BLOCK_SIZE)
400 {
401 GNUNET_break (0);
402 return NULL;
403 }
404 block = GNUNET_malloc (total);
405 block->proof_len = htons (len);
406 block->is_accepting = htons (accepting);
407 block->num_edges = htons (num_edges);
408 block->num_destinations = htons (unique_destinations);
409 dests = (struct GNUNET_HashCode *) &block[1];
410 GNUNET_memcpy (dests, destinations, sizeof(struct GNUNET_HashCode)
411 * unique_destinations);
412 edgeinfos = (struct EdgeInfo *) &dests[unique_destinations];
413 aux = (char *) &edgeinfos[num_edges];
414 off = len;
415 GNUNET_memcpy (aux, proof, len);
416 for (i = 0; i < num_edges; i++)
417 {
418 slen = strlen (edges[i].label);
419 edgeinfos[i].token_length = htons ((uint16_t) slen);
420 edgeinfos[i].destination_index = htons (destination_indices[i]);
421 GNUNET_memcpy (&aux[off],
422 edges[i].label,
423 slen);
424 off += slen;
425 }
426 *rsize = total;
427 return block;
428}
429
430
431/* end of regex_block_lib.c */
diff --git a/src/plugin/regex/regex_block_lib.h b/src/plugin/regex/regex_block_lib.h
new file mode 100644
index 000000000..11029b9af
--- /dev/null
+++ b/src/plugin/regex/regex_block_lib.h
@@ -0,0 +1,193 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012,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 * @author Bartlomiej Polot
23 * @file regex/regex_block_lib.h
24 * @brief common function to manipulate blocks stored by regex in the DHT
25 */
26
27#ifndef REGEX_BLOCK_LIB_H_
28#define REGEX_BLOCK_LIB_H_
29
30#ifdef __cplusplus
31extern "C"
32{
33#if 0
34/* keep Emacsens' auto-indent happy */
35}
36#endif
37#endif
38
39#include "platform.h"
40#include "block_regex.h"
41
42
43/**
44 * Representation of a Regex node (and edges) in the DHT.
45 */
46struct RegexBlock;
47
48
49/**
50 * Edge representation.
51 */
52struct REGEX_BLOCK_Edge
53{
54 /**
55 * Label of the edge. FIXME: might want to not consume exactly
56 * multiples of 8 bits, need length!
57 */
58 const char *label;
59
60 /**
61 * Destination of the edge.
62 */
63 struct GNUNET_HashCode destination;
64};
65
66
67/**
68 * Check if the given 'proof' matches the given 'key'.
69 *
70 * @param proof partial regex of a state
71 * @param proof_len number of bytes in @a proof
72 * @param key hash of a state.
73 * @return #GNUNET_OK if the proof is valid for the given key.
74 */
75int
76REGEX_BLOCK_check_proof (const char *proof,
77 size_t proof_len,
78 const struct GNUNET_HashCode *key);
79
80
81/**
82 * Check if the regex block is well formed, including all edges.
83 *
84 * @param block The start of the block.
85 * @param size The size of the @a block.
86 * @param query the query for the @a block
87 * @param xquery String describing the edge we are looking for.
88 * Can be NULL in case this is a put block.
89 * @return #GNUNET_OK in case it's fine.
90 * #GNUNET_NO in case the xquery exists and is not found (IRRELEVANT).
91 * #GNUNET_SYSERR if the block is invalid.
92 */
93int
94REGEX_BLOCK_check (const struct RegexBlock *block,
95 size_t size,
96 const struct GNUNET_HashCode *query,
97 const char *xquery);
98
99
100/* FIXME: might want to use 'struct REGEX_BLOCK_Edge' here instead of 3 arguments! */
101
102/**
103 * Iterator over edges in a block.
104 *
105 * @param cls Closure.
106 * @param token Token that follows to next state.
107 * @param len Length of token.
108 * @param key Hash of next state.
109 * @return #GNUNET_YES if should keep iterating, #GNUNET_NO otherwise.
110 */
111typedef int
112(*REGEX_INTERNAL_EgdeIterator)(void *cls,
113 const char *token,
114 size_t len,
115 const struct GNUNET_HashCode *key);
116
117
118/**
119 * Iterate over all edges of a block of a regex state.
120 *
121 * @param block Block to iterate over.
122 * @param size Size of block.
123 * @param iterator Function to call on each edge in the block.
124 * @param iter_cls Closure for the @a iterator.
125 * @return #GNUNET_SYSERR if an error has been encountered.
126 * #GNUNET_OK if no error has been encountered.
127 * Note that if the iterator stops the iteration by returning
128 * #GNUNET_NO, the block will no longer be checked for further errors.
129 * The return value will be #GNUNET_OK meaning that no errors were
130 * found until the edge last notified to the iterator, but there might
131 * be errors in further edges.
132 */
133int
134REGEX_BLOCK_iterate (const struct RegexBlock *block,
135 size_t size,
136 REGEX_INTERNAL_EgdeIterator iterator,
137 void *iter_cls);
138
139
140/**
141 * Obtain the key that a particular block is to be stored under.
142 *
143 * @param block block to get the key from
144 * @param block_len number of bytes in @a block
145 * @param key where to store the key
146 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the block is malformed
147 */
148int
149REGEX_BLOCK_get_key (const struct RegexBlock *block,
150 size_t block_len,
151 struct GNUNET_HashCode *key);
152
153
154/**
155 * Test if this block is marked as being an accept state.
156 *
157 * @param block block to test
158 * @param size number of bytes in block
159 * @return #GNUNET_YES if the block is accepting, #GNUNET_NO if not
160 */
161int
162GNUNET_BLOCK_is_accepting (const struct RegexBlock *block,
163 size_t block_len);
164
165
166/**
167 * Construct a regex block to be stored in the DHT.
168 *
169 * @param proof proof string for the block
170 * @param num_edges number of edges in the block
171 * @param edges the edges of the block
172 * @param accepting is this an accepting state
173 * @param rsize set to the size of the returned block (OUT-only)
174 * @return the regex block, NULL on error
175 */
176struct RegexBlock *
177REGEX_BLOCK_create (const char *proof,
178 unsigned int num_edges,
179 const struct REGEX_BLOCK_Edge *edges,
180 int accepting,
181 size_t *rsize);
182
183
184#if 0 /* keep Emacsens' auto-indent happy */
185{
186#endif
187#ifdef __cplusplus
188}
189#endif
190
191/* ifndef REGEX_BLOCK_LIB_H */
192#endif
193/* end of regex_block_lib.h */
diff --git a/src/plugin/revocation/Makefile.am b/src/plugin/revocation/Makefile.am
index db6df02c9..99373f8d7 100644
--- a/src/plugin/revocation/Makefile.am
+++ b/src/plugin/revocation/Makefile.am
@@ -18,11 +18,10 @@ plugin_LTLIBRARIES = \
18libgnunet_plugin_block_revocation_la_SOURCES = \ 18libgnunet_plugin_block_revocation_la_SOURCES = \
19 plugin_block_revocation.c 19 plugin_block_revocation.c
20libgnunet_plugin_block_revocation_la_LIBADD = \ 20libgnunet_plugin_block_revocation_la_LIBADD = \
21 $(top_builddir)/src/service/revocation/libgnunetrevocation.la \ 21 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
22 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \ 22 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \
23 $(top_builddir)/src/lib/block/libgnunetblock.la \ 23 $(top_builddir)/src/lib/block/libgnunetblock.la \
24 $(top_builddir)/src/lib/util/libgnunetutil.la \ 24 $(top_builddir)/src/lib/util/libgnunetutil.la \
25 $(top_builddir)/src/service/identity/libgnunetidentity.la \
26 $(LTLIBINTL) 25 $(LTLIBINTL)
27libgnunet_plugin_block_revocation_la_LDFLAGS = \ 26libgnunet_plugin_block_revocation_la_LDFLAGS = \
28 $(GN_PLUGIN_LDFLAGS) 27 $(GN_PLUGIN_LDFLAGS)
diff --git a/src/plugin/revocation/meson.build b/src/plugin/revocation/meson.build
index 7f8c8ec5e..c4405e9b8 100644
--- a/src/plugin/revocation/meson.build
+++ b/src/plugin/revocation/meson.build
@@ -1,8 +1,6 @@
1shared_module('gnunet_plugin_block_revocation', 1shared_module('gnunet_plugin_block_revocation',
2 ['plugin_block_revocation.c'], 2 ['plugin_block_revocation.c'],
3 dependencies: [libgnunetutil_dep, 3 dependencies: [libgnunetutil_dep,
4 libgnunetidentity_dep,
5 libgnunetrevocation_dep,
6 libgnunetblock_dep], 4 libgnunetblock_dep],
7 include_directories: [incdir, configuration_inc], 5 include_directories: [incdir, configuration_inc],
8 install: true, 6 install: true,
diff --git a/src/plugin/revocation/plugin_block_revocation.c b/src/plugin/revocation/plugin_block_revocation.c
index 4c5991507..c4332184e 100644
--- a/src/plugin/revocation/plugin_block_revocation.c
+++ b/src/plugin/revocation/plugin_block_revocation.c
@@ -27,8 +27,8 @@
27#include "platform.h" 27#include "platform.h"
28#include "gnunet_signatures.h" 28#include "gnunet_signatures.h"
29#include "gnunet_block_plugin.h" 29#include "gnunet_block_plugin.h"
30#include "gnunet_gnsrecord_lib.h"
30#include "gnunet_block_group_lib.h" 31#include "gnunet_block_group_lib.h"
31// FIXME try to avoid this include somehow
32#include "../../service/revocation/revocation.h" 32#include "../../service/revocation/revocation.h"
33#include "gnunet_revocation_service.h" 33#include "gnunet_revocation_service.h"
34 34
@@ -93,8 +93,8 @@ block_plugin_revocation_check_block (void *cls,
93{ 93{
94 struct InternalContext *ic = cls; 94 struct InternalContext *ic = cls;
95 const struct RevokeMessage *rm = block; 95 const struct RevokeMessage *rm = block;
96 const struct GNUNET_REVOCATION_PowP *pow 96 const struct GNUNET_GNSRECORD_PowP *pow
97 = (const struct GNUNET_REVOCATION_PowP *) &rm[1]; 97 = (const struct GNUNET_GNSRECORD_PowP *) &rm[1];
98 struct GNUNET_CRYPTO_PublicKey pk; 98 struct GNUNET_CRYPTO_PublicKey pk;
99 size_t pklen; 99 size_t pklen;
100 size_t left; 100 size_t left;
@@ -117,9 +117,9 @@ block_plugin_revocation_check_block (void *cls,
117 left = block_size - sizeof (*rm) - sizeof (*pow); 117 left = block_size - sizeof (*rm) - sizeof (*pow);
118 if (GNUNET_SYSERR == 118 if (GNUNET_SYSERR ==
119 GNUNET_CRYPTO_read_public_key_from_buffer (&pow[1], 119 GNUNET_CRYPTO_read_public_key_from_buffer (&pow[1],
120 left, 120 left,
121 &pk, 121 &pk,
122 &pklen)) 122 &pklen))
123 { 123 {
124 GNUNET_break_op (0); 124 GNUNET_break_op (0);
125 return GNUNET_NO; 125 return GNUNET_NO;
@@ -130,9 +130,9 @@ block_plugin_revocation_check_block (void *cls,
130 return GNUNET_NO; 130 return GNUNET_NO;
131 } 131 }
132 if (GNUNET_YES != 132 if (GNUNET_YES !=
133 GNUNET_REVOCATION_check_pow (pow, 133 GNUNET_GNSRECORD_check_pow (pow,
134 ic->matching_bits, 134 ic->matching_bits,
135 ic->epoch_duration)) 135 ic->epoch_duration))
136 { 136 {
137 GNUNET_break_op (0); 137 GNUNET_break_op (0);
138 return GNUNET_NO; 138 return GNUNET_NO;
@@ -203,8 +203,8 @@ block_plugin_revocation_get_key (void *cls,
203 struct GNUNET_HashCode *key) 203 struct GNUNET_HashCode *key)
204{ 204{
205 const struct RevokeMessage *rm = block; 205 const struct RevokeMessage *rm = block;
206 const struct GNUNET_REVOCATION_PowP *pow 206 const struct GNUNET_GNSRECORD_PowP *pow
207 = (const struct GNUNET_REVOCATION_PowP *) &rm[1]; 207 = (const struct GNUNET_GNSRECORD_PowP *) &rm[1];
208 struct GNUNET_CRYPTO_PublicKey pk; 208 struct GNUNET_CRYPTO_PublicKey pk;
209 size_t pklen; 209 size_t pklen;
210 size_t left; 210 size_t left;
@@ -226,9 +226,9 @@ block_plugin_revocation_get_key (void *cls,
226 } 226 }
227 left = block_size - sizeof (*rm) - sizeof (*pow); 227 left = block_size - sizeof (*rm) - sizeof (*pow);
228 if (GNUNET_SYSERR == GNUNET_CRYPTO_read_public_key_from_buffer (&pow[1], 228 if (GNUNET_SYSERR == GNUNET_CRYPTO_read_public_key_from_buffer (&pow[1],
229 left, 229 left,
230 &pk, 230 &pk,
231 &pklen)) 231 &pklen))
232 { 232 {
233 GNUNET_break_op (0); 233 GNUNET_break_op (0);
234 return GNUNET_NO; 234 return GNUNET_NO;
diff --git a/src/plugin/seti/Makefile.am b/src/plugin/seti/Makefile.am
new file mode 100644
index 000000000..e6f579a65
--- /dev/null
+++ b/src/plugin/seti/Makefile.am
@@ -0,0 +1,25 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4pkgcfgdir= $(pkgdatadir)/config.d/
5
6libexecdir= $(pkglibdir)/libexec/
7
8plugindir = $(libdir)/gnunet
9
10if USE_COVERAGE
11 AM_CFLAGS = -fprofile-arcs -ftest-coverage
12endif
13
14plugin_LTLIBRARIES = \
15 libgnunet_plugin_block_seti_test.la
16
17libgnunet_plugin_block_seti_test_la_SOURCES = \
18 plugin_block_seti_test.c
19libgnunet_plugin_block_seti_test_la_LIBADD = \
20 $(top_builddir)/src/lib/block/libgnunetblock.la \
21 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \
22 $(top_builddir)/src/lib/util/libgnunetutil.la \
23 $(LTLIBINTL)
24libgnunet_plugin_block_seti_test_la_LDFLAGS = \
25 $(GN_PLUGIN_LDFLAGS)
diff --git a/src/plugin/seti/meson.build b/src/plugin/seti/meson.build
new file mode 100644
index 000000000..880936164
--- /dev/null
+++ b/src/plugin/seti/meson.build
@@ -0,0 +1,7 @@
1shared_module('gnunet_plugin_block_seti_test',
2 ['plugin_block_seti_test.c'],
3 dependencies: libgnunetutil_dep,
4 include_directories: [incdir, configuration_inc],
5 install:true,
6 install_dir: get_option('libdir')/'gnunet')
7
diff --git a/src/plugin/seti/plugin_block_seti_test.c b/src/plugin/seti/plugin_block_seti_test.c
new file mode 100644
index 000000000..5b9196cef
--- /dev/null
+++ b/src/plugin/seti/plugin_block_seti_test.c
@@ -0,0 +1,197 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017 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 seti/plugin_block_seti_test.c
23 * @brief set test block, recognizes elements with non-zero first byte as invalid
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_block_plugin.h"
29#include "gnunet_block_group_lib.h"
30
31
32/**
33 * Function called to validate a query.
34 *
35 * @param cls closure
36 * @param ctx block context
37 * @param type block type
38 * @param query original query (hash)
39 * @param xquery extrended query data (can be NULL, depending on type)
40 * @param xquery_size number of bytes in @a xquery
41 * @return #GNUNET_OK if the query is fine, #GNUNET_NO if not
42 */
43static enum GNUNET_GenericReturnValue
44block_plugin_seti_test_check_query (void *cls,
45 enum GNUNET_BLOCK_Type type,
46 const struct GNUNET_HashCode *query,
47 const void *xquery,
48 size_t xquery_size)
49{
50 if (GNUNET_BLOCK_TYPE_SETI_TEST != type)
51 {
52 GNUNET_break (0);
53 return GNUNET_SYSERR;
54 }
55 if (0 != xquery_size)
56 {
57 GNUNET_break_op (0);
58 return GNUNET_NO;
59 }
60 return GNUNET_OK;
61}
62
63
64/**
65 * Function called to validate a block for storage.
66 *
67 * @param cls closure
68 * @param type block type
69 * @param block block data to validate
70 * @param block_size number of bytes in @a block
71 * @return #GNUNET_OK if the block is fine, #GNUNET_NO if not
72 */
73static enum GNUNET_GenericReturnValue
74block_plugin_seti_test_check_block (void *cls,
75 enum GNUNET_BLOCK_Type type,
76 const void *block,
77 size_t block_size)
78{
79 (void) cls;
80 if (GNUNET_BLOCK_TYPE_SETI_TEST != type)
81 {
82 GNUNET_break (0);
83 return GNUNET_SYSERR;
84 }
85 if ((NULL == block) ||
86 (0 == block_size) ||
87 (0 != ((char *) block)[0]))
88 return GNUNET_SYSERR;
89 return GNUNET_OK;
90}
91
92
93/**
94 * Function called to validate a reply to a request. Note that it is assumed
95 * that the reply has already been matched to the key (and signatures checked)
96 * as it would be done with the GetKeyFunction and the
97 * BlockEvaluationFunction.
98 *
99 * @param cls closure
100 * @param type block type
101 * @param group which block group to use for evaluation
102 * @param query original query (hash)
103 * @param xquery extrended query data (can be NULL, depending on type)
104 * @param xquery_size number of bytes in @a xquery
105 * @param reply_block response to validate
106 * @param reply_block_size number of bytes in @a reply_block
107 * @return characterization of result
108 */
109static enum GNUNET_BLOCK_ReplyEvaluationResult
110block_plugin_seti_test_check_reply (void *cls,
111 enum GNUNET_BLOCK_Type type,
112 struct GNUNET_BLOCK_Group *group,
113 const struct GNUNET_HashCode *query,
114 const void *xquery,
115 size_t xquery_size,
116 const void *reply_block,
117 size_t reply_block_size)
118{
119 (void) cls;
120 (void) xquery;
121 (void) xquery_size;
122 if (GNUNET_BLOCK_TYPE_SETI_TEST != type)
123 {
124 GNUNET_break (0);
125 return GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED;
126 }
127 if ( (NULL == reply_block) ||
128 (0 == reply_block_size) ||
129 (0 != ((char *) reply_block)[0]) )
130 GNUNET_assert (0);
131 return GNUNET_BLOCK_REPLY_OK_MORE;
132}
133
134
135/**
136 * Function called to obtain the key for a block.
137 *
138 * @param cls closure
139 * @param type block type
140 * @param block block to get the key for
141 * @param block_size number of bytes in block
142 * @param key set to the key (query) for the given block
143 * @return #GNUNET_OK on success, #GNUNET_SYSERR if type not supported
144 * (or if extracting a key from a block of this type does not work)
145 */
146static enum GNUNET_GenericReturnValue
147block_plugin_seti_test_get_key (void *cls,
148 enum GNUNET_BLOCK_Type type,
149 const void *block,
150 size_t block_size,
151 struct GNUNET_HashCode *key)
152{
153 if (GNUNET_BLOCK_TYPE_SETI_TEST != type)
154 {
155 GNUNET_break (0);
156 return GNUNET_SYSERR;
157 }
158 return GNUNET_NO;
159}
160
161
162/**
163 * Entry point for the plugin.
164 */
165void *
166libgnunet_plugin_block_seti_test_init (void *cls)
167{
168 static enum GNUNET_BLOCK_Type types[] = {
169 GNUNET_BLOCK_TYPE_SETI_TEST,
170 GNUNET_BLOCK_TYPE_ANY /* end of list */
171 };
172 struct GNUNET_BLOCK_PluginFunctions *api;
173
174 api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions);
175 api->get_key = &block_plugin_seti_test_get_key;
176 api->check_query = &block_plugin_seti_test_check_query;
177 api->check_block = &block_plugin_seti_test_check_block;
178 api->check_reply = &block_plugin_seti_test_check_reply;
179 api->types = types;
180 return api;
181}
182
183
184/**
185 * Exit point from the plugin.
186 */
187void *
188libgnunet_plugin_block_seti_test_done (void *cls)
189{
190 struct GNUNET_BLOCK_PluginFunctions *api = cls;
191
192 GNUNET_free (api);
193 return NULL;
194}
195
196
197/* end of plugin_block_seti_test.c */
diff --git a/src/plugin/setu/Makefile.am b/src/plugin/setu/Makefile.am
new file mode 100644
index 000000000..90a14cce0
--- /dev/null
+++ b/src/plugin/setu/Makefile.am
@@ -0,0 +1,21 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4pkgcfgdir= $(pkgdatadir)/config.d/
5
6libexecdir= $(pkglibdir)/libexec/
7
8plugindir = $(libdir)/gnunet
9
10plugin_LTLIBRARIES = \
11 libgnunet_plugin_block_setu_test.la
12
13libgnunet_plugin_block_setu_test_la_SOURCES = \
14 plugin_block_setu_test.c
15libgnunet_plugin_block_setu_test_la_LIBADD = \
16 $(top_builddir)/src/lib/block/libgnunetblock.la \
17 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \
18 $(top_builddir)/src/lib/util/libgnunetutil.la \
19 $(LTLIBINTL)
20libgnunet_plugin_block_setu_test_la_LDFLAGS = \
21 $(GN_PLUGIN_LDFLAGS)
diff --git a/src/plugin/setu/meson.build b/src/plugin/setu/meson.build
new file mode 100644
index 000000000..e22738ab6
--- /dev/null
+++ b/src/plugin/setu/meson.build
@@ -0,0 +1,7 @@
1shared_module('gnunet_plugin_block_setu_test',
2 ['plugin_block_setu_test.c'],
3 dependencies: libgnunetutil_dep,
4 include_directories: [incdir, configuration_inc],
5 install:true,
6 install_dir: get_option('libdir')/'gnunet')
7
diff --git a/src/plugin/setu/plugin_block_setu_test.c b/src/plugin/setu/plugin_block_setu_test.c
new file mode 100644
index 000000000..178ad3314
--- /dev/null
+++ b/src/plugin/setu/plugin_block_setu_test.c
@@ -0,0 +1,195 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017 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 setu/plugin_block_setu_test.c
23 * @brief set test block, recognizes elements with non-zero first byte as invalid
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_block_plugin.h"
28#include "gnunet_block_group_lib.h"
29
30
31/**
32 * Function called to validate a query.
33 *
34 * @param cls closure
35 * @param ctx block context
36 * @param type block type
37 * @param query original query (hash)
38 * @param xquery extrended query data (can be NULL, depending on type)
39 * @param xquery_size number of bytes in @a xquery
40 * @return #GNUNET_OK if the query is fine, #GNUNET_NO if not
41 */
42static enum GNUNET_GenericReturnValue
43block_plugin_setu_test_check_query (void *cls,
44 enum GNUNET_BLOCK_Type type,
45 const struct GNUNET_HashCode *query,
46 const void *xquery,
47 size_t xquery_size)
48{
49 if (GNUNET_BLOCK_TYPE_SETU_TEST != type)
50 {
51 GNUNET_break (0);
52 return GNUNET_SYSERR;
53 }
54 if (0 != xquery_size)
55 {
56 GNUNET_break_op (0);
57 return GNUNET_NO;
58 }
59 return GNUNET_OK;
60}
61
62
63/**
64 * Function called to validate a block for storage.
65 *
66 * @param cls closure
67 * @param type block type
68 * @param block block data to validate
69 * @param block_size number of bytes in @a block
70 * @return #GNUNET_OK if the block is fine, #GNUNET_NO if not
71 */
72static enum GNUNET_GenericReturnValue
73block_plugin_setu_test_check_block (void *cls,
74 enum GNUNET_BLOCK_Type type,
75 const void *block,
76 size_t block_size)
77{
78 if (GNUNET_BLOCK_TYPE_SETU_TEST != type)
79 {
80 GNUNET_break (0);
81 return GNUNET_SYSERR;
82 }
83 if ( (NULL == block) ||
84 (0 == block_size) ||
85 (0 != ((char *) block)[0]) )
86 return GNUNET_NO;
87 return GNUNET_OK;
88}
89
90
91/**
92 * Function called to validate a reply to a request. Note that it is assumed
93 * that the reply has already been matched to the key (and signatures checked)
94 * as it would be done with the GetKeyFunction and the
95 * BlockEvaluationFunction.
96 *
97 * @param cls closure
98 * @param type block type
99 * @param group which block group to use for evaluation
100 * @param query original query (hash)
101 * @param xquery extrended query data (can be NULL, depending on type)
102 * @param xquery_size number of bytes in @a xquery
103 * @param reply_block response to validate
104 * @param reply_block_size number of bytes in @a reply_block
105 * @return characterization of result
106 */
107static enum GNUNET_BLOCK_ReplyEvaluationResult
108block_plugin_setu_test_check_reply (void *cls,
109 enum GNUNET_BLOCK_Type type,
110 struct GNUNET_BLOCK_Group *group,
111 const struct GNUNET_HashCode *query,
112 const void *xquery,
113 size_t xquery_size,
114 const void *reply_block,
115 size_t reply_block_size)
116{
117 (void) cls;
118 (void) xquery;
119 (void) xquery_size;
120 if (GNUNET_BLOCK_TYPE_SETU_TEST != type)
121 {
122 GNUNET_break (0);
123 return GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED;
124 }
125 if ( (NULL == reply_block) ||
126 (0 == reply_block_size) ||
127 (0 != ((char *) reply_block)[0]) )
128 GNUNET_assert (0);
129 return GNUNET_BLOCK_REPLY_OK_MORE;
130}
131
132
133/**
134 * Function called to obtain the key for a block.
135 *
136 * @param cls closure
137 * @param type block type
138 * @param block block to get the key for
139 * @param block_size number of bytes in block
140 * @param key set to the key (query) for the given block
141 * @return #GNUNET_OK on success, #GNUNET_SYSERR if type not supported
142 * (or if extracting a key from a block of this type does not work)
143 */
144static enum GNUNET_GenericReturnValue
145block_plugin_setu_test_get_key (void *cls,
146 enum GNUNET_BLOCK_Type type,
147 const void *block,
148 size_t block_size,
149 struct GNUNET_HashCode *key)
150{
151 if (GNUNET_BLOCK_TYPE_SETU_TEST != type)
152 {
153 GNUNET_break (0);
154 return GNUNET_SYSERR;
155 }
156 return GNUNET_NO;
157}
158
159
160/**
161 * Entry point for the plugin.
162 */
163void *
164libgnunet_plugin_block_setu_test_init (void *cls)
165{
166 static enum GNUNET_BLOCK_Type types[] = {
167 GNUNET_BLOCK_TYPE_SETU_TEST,
168 GNUNET_BLOCK_TYPE_ANY /* end of list */
169 };
170 struct GNUNET_BLOCK_PluginFunctions *api;
171
172 api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions);
173 api->get_key = &block_plugin_setu_test_get_key;
174 api->check_query = &block_plugin_setu_test_check_query;
175 api->check_block = &block_plugin_setu_test_check_block;
176 api->check_reply = &block_plugin_setu_test_check_reply;
177 api->types = types;
178 return api;
179}
180
181
182/**
183 * Exit point from the plugin.
184 */
185void *
186libgnunet_plugin_block_setu_test_done (void *cls)
187{
188 struct GNUNET_BLOCK_PluginFunctions *api = cls;
189
190 GNUNET_free (api);
191 return NULL;
192}
193
194
195/* end of plugin_block_setu_test.c */