aboutsummaryrefslogtreecommitdiff
path: root/src/service
diff options
context:
space:
mode:
Diffstat (limited to 'src/service')
-rw-r--r--src/service/Makefile.am5
-rw-r--r--src/service/datacache/Makefile.am68
-rw-r--r--src/service/datacache/datacache-0001.sql48
-rw-r--r--src/service/datacache/datacache-drop.sql25
-rw-r--r--src/service/datacache/meson.build33
-rw-r--r--src/service/datacache/plugin_datacache_heap.c530
-rw-r--r--src/service/datacache/plugin_datacache_postgres.c616
-rw-r--r--src/service/datacache/plugin_datacache_sqlite.c1063
-rw-r--r--src/service/datacache/plugin_datacache_template.c172
-rw-r--r--src/service/datastore/Makefile.am117
-rw-r--r--src/service/datastore/datastore-0001.sql49
-rw-r--r--src/service/datastore/datastore-drop.sql25
-rw-r--r--src/service/datastore/meson.build70
-rw-r--r--src/service/datastore/perf_plugin_datastore.c573
-rw-r--r--src/service/datastore/perf_plugin_datastore_data_heap.conf7
-rw-r--r--src/service/datastore/perf_plugin_datastore_data_postgres.conf10
-rw-r--r--src/service/datastore/perf_plugin_datastore_data_sqlite.conf4
-rw-r--r--src/service/datastore/plugin_datastore_heap.c944
-rw-r--r--src/service/datastore/plugin_datastore_postgres.c930
-rw-r--r--src/service/datastore/plugin_datastore_sqlite.c1375
-rw-r--r--src/service/datastore/plugin_datastore_template.c274
-rw-r--r--src/service/datastore/test_plugin_datastore.c478
-rw-r--r--src/service/datastore/test_plugin_datastore_data_heap.conf6
-rw-r--r--src/service/datastore/test_plugin_datastore_data_postgres.conf10
-rw-r--r--src/service/datastore/test_plugin_datastore_data_sqlite.conf4
-rw-r--r--src/service/dht/Makefile.am15
-rw-r--r--src/service/dht/meson.build10
-rw-r--r--src/service/dht/plugin_block_dht.c311
-rw-r--r--src/service/dhtu/.gitignore1
-rw-r--r--src/service/dhtu/Makefile.am81
-rw-r--r--src/service/dhtu/dhtu.conf7
-rw-r--r--src/service/dhtu/meson.build61
-rw-r--r--src/service/dhtu/plugin_dhtu_gnunet.c603
-rw-r--r--src/service/dhtu/plugin_dhtu_ip.c1171
-rw-r--r--src/service/dhtu/test_dhtu_ip.c45
-rw-r--r--src/service/dhtu/testing_dhtu_cmd_send.c119
-rw-r--r--src/service/dns/Makefile.am14
-rw-r--r--src/service/dns/meson.build10
-rw-r--r--src/service/dns/plugin_block_dns.c290
-rw-r--r--src/service/meson.build6
-rw-r--r--src/service/messenger/Makefile.am13
-rw-r--r--src/service/messenger/meson.build8
-rw-r--r--src/service/messenger/plugin_gnsrecord_messenger.c298
-rw-r--r--src/service/namestore/meson.build2
-rw-r--r--src/service/peerstore/Makefile.am52
-rw-r--r--src/service/peerstore/meson.build10
-rw-r--r--src/service/peerstore/plugin_peerstore_flat.c606
-rw-r--r--src/service/peerstore/plugin_peerstore_sqlite.c702
-rw-r--r--src/service/peerstore/test_plugin_peerstore.c224
-rw-r--r--src/service/peerstore/test_plugin_peerstore_flat.conf5
-rw-r--r--src/service/peerstore/test_plugin_peerstore_sqlite.conf2
-rw-r--r--src/service/regex/Makefile.am40
-rw-r--r--src/service/regex/meson.build24
-rw-r--r--src/service/regex/plugin_block_regex.c380
-rw-r--r--src/service/regex/regex_block_lib.c431
-rw-r--r--src/service/regex/regex_block_lib.h193
-rw-r--r--src/service/regex/regex_internal_dht.c1
-rw-r--r--src/service/regex/regex_internal_lib.h2
-rw-r--r--src/service/rest/Makefile.am27
-rw-r--r--src/service/rest/config_plugin.c438
-rw-r--r--src/service/rest/config_plugin.h37
-rw-r--r--src/service/rest/copying_plugin.c233
-rw-r--r--src/service/rest/copying_plugin.h36
-rw-r--r--src/service/rest/gns_plugin.c486
-rw-r--r--src/service/rest/gns_plugin.h36
-rw-r--r--src/service/rest/gnunet-rest-server.c153
-rw-r--r--src/service/rest/identity_plugin.c1287
-rw-r--r--src/service/rest/identity_plugin.h36
-rw-r--r--src/service/rest/json_reclaim.c398
-rw-r--r--src/service/rest/json_reclaim.h57
-rw-r--r--src/service/rest/meson.build26
-rw-r--r--src/service/rest/namestore_plugin.c1364
-rw-r--r--src/service/rest/namestore_plugin.h37
-rw-r--r--src/service/rest/oidc_helper.c1026
-rw-r--r--src/service/rest/oidc_helper.h196
-rw-r--r--src/service/rest/openid_plugin.c3169
-rw-r--r--src/service/rest/openid_plugin.h36
-rw-r--r--src/service/rest/pabc_plugin.c667
-rw-r--r--src/service/rest/reclaim_plugin.c1565
-rw-r--r--src/service/rest/reclaim_plugin.h36
-rw-r--r--src/service/revocation/Makefile.am4
-rw-r--r--src/service/revocation/gnunet-service-revocation.c22
-rw-r--r--src/service/revocation/meson.build5
-rw-r--r--src/service/revocation/revocation.h9
-rw-r--r--src/service/revocation/revocation_api.c440
-rw-r--r--src/service/seti/Makefile.am14
-rw-r--r--src/service/seti/meson.build6
-rw-r--r--src/service/seti/plugin_block_seti_test.c197
-rw-r--r--src/service/setu/Makefile.am14
-rw-r--r--src/service/setu/meson.build6
-rw-r--r--src/service/setu/plugin_block_setu_test.c195
-rw-r--r--src/service/topology/gnunet-daemon-topology.c30
92 files changed, 13451 insertions, 12010 deletions
diff --git a/src/service/Makefile.am b/src/service/Makefile.am
index 8e661e432..041442725 100644
--- a/src/service/Makefile.am
+++ b/src/service/Makefile.am
@@ -13,9 +13,9 @@ SUBDIRS = \
13 topology \ 13 topology \
14 dns \ 14 dns \
15 identity \ 15 identity \
16 rest \
17 datacache \ 16 datacache \
18 datastore \ 17 datastore \
18 dhtu \
19 dht \ 19 dht \
20 namecache \ 20 namecache \
21 namestore \ 21 namestore \
@@ -31,5 +31,6 @@ SUBDIRS = \
31 exit \ 31 exit \
32 pt \ 32 pt \
33 reclaim \ 33 reclaim \
34 messenger 34 messenger \
35 rest
35 36
diff --git a/src/service/datacache/Makefile.am b/src/service/datacache/Makefile.am
index 9caeac543..022b45253 100644
--- a/src/service/datacache/Makefile.am
+++ b/src/service/datacache/Makefile.am
@@ -8,25 +8,11 @@ pkgcfgdir= $(pkgdatadir)/config.d/
8dist_pkgcfg_DATA = \ 8dist_pkgcfg_DATA = \
9 datacache.conf 9 datacache.conf
10 10
11sqldir = $(prefix)/share/gnunet/sql/
12
13sql_DATA = \
14 datacache-0001.sql \
15 datacache-drop.sql
16
17
18if USE_COVERAGE 11if USE_COVERAGE
19 AM_CFLAGS = --coverage -O0 12 AM_CFLAGS = --coverage -O0
20 XLIBS = -lgcov 13 XLIBS = -lgcov
21endif 14endif
22 15
23if HAVE_SQLITE
24 SQLITE_PLUGIN = libgnunet_plugin_datacache_sqlite.la
25endif
26if HAVE_POSTGRESQL
27 POSTGRES_PLUGIN = libgnunet_plugin_datacache_postgres.la
28endif
29
30lib_LTLIBRARIES = \ 16lib_LTLIBRARIES = \
31 libgnunetdatacache.la 17 libgnunetdatacache.la
32 18
@@ -41,57 +27,6 @@ libgnunetdatacache_la_LDFLAGS = \
41 -version-info 0:1:0 27 -version-info 0:1:0
42 28
43 29
44plugin_LTLIBRARIES = \
45 $(SQLITE_PLUGIN) \
46 $(POSTGRES_PLUGIN) \
47 libgnunet_plugin_datacache_heap.la
48
49# Real plugins should of course go into
50# plugin_LTLIBRARIES
51noinst_LTLIBRARIES = \
52 libgnunet_plugin_datacache_template.la
53
54
55libgnunet_plugin_datacache_sqlite_la_SOURCES = \
56 plugin_datacache_sqlite.c
57libgnunet_plugin_datacache_sqlite_la_LIBADD = \
58 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
59 $(top_builddir)/src/lib/sq/libgnunetsq.la \
60 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lsqlite3 \
61 $(LTLIBINTL)
62libgnunet_plugin_datacache_sqlite_la_LDFLAGS = \
63 $(GN_PLUGIN_LDFLAGS)
64
65libgnunet_plugin_datacache_heap_la_SOURCES = \
66 plugin_datacache_heap.c
67libgnunet_plugin_datacache_heap_la_LIBADD = \
68 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
69 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
70 $(LTLIBINTL)
71libgnunet_plugin_datacache_heap_la_LDFLAGS = \
72 $(GN_PLUGIN_LDFLAGS)
73
74libgnunet_plugin_datacache_postgres_la_SOURCES = \
75 plugin_datacache_postgres.c
76libgnunet_plugin_datacache_postgres_la_LIBADD = \
77 $(top_builddir)/src/lib/pq/libgnunetpq.la \
78 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
79 $(top_builddir)/src/lib/util/libgnunetutil.la \
80 $(GN_PLUGIN_LDFLAGS) -lpq
81libgnunet_plugin_datacache_postgres_la_CPPFLAGS = \
82 $(POSTGRESQL_CPPFLAGS) $(AM_CPPFLAGS)
83libgnunet_plugin_datacache_postgres_la_LDFLAGS = \
84 $(GN_PLUGIN_LDFLAGS) $(POSTGRESQL_LDFLAGS)
85
86libgnunet_plugin_datacache_template_la_SOURCES = \
87 plugin_datacache_template.c
88libgnunet_plugin_datacache_template_la_LIBADD = \
89 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
90 $(LTLIBINTL)
91libgnunet_plugin_datacache_template_la_LDFLAGS = \
92 $(GN_PLUGIN_LDFLAGS)
93
94
95if HAVE_SQLITE 30if HAVE_SQLITE
96SQLITE_TESTS = \ 31SQLITE_TESTS = \
97 test_datacache_sqlite \ 32 test_datacache_sqlite \
@@ -166,5 +101,4 @@ test_datacache_quota_postgres_LDADD = \
166EXTRA_DIST = \ 101EXTRA_DIST = \
167 test_datacache_data_sqlite.conf \ 102 test_datacache_data_sqlite.conf \
168 test_datacache_data_heap.conf \ 103 test_datacache_data_heap.conf \
169 test_datacache_data_postgres.conf \ 104 test_datacache_data_postgres.conf
170 $(sql_DATA)
diff --git a/src/service/datacache/datacache-0001.sql b/src/service/datacache/datacache-0001.sql
deleted file mode 100644
index 6567de3c2..000000000
--- a/src/service/datacache/datacache-0001.sql
+++ /dev/null
@@ -1,48 +0,0 @@
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/service/datacache/datacache-drop.sql b/src/service/datacache/datacache-drop.sql
deleted file mode 100644
index 2dd84bca8..000000000
--- a/src/service/datacache/datacache-drop.sql
+++ /dev/null
@@ -1,25 +0,0 @@
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/service/datacache/meson.build b/src/service/datacache/meson.build
index 528885c0d..f50036c03 100644
--- a/src/service/datacache/meson.build
+++ b/src/service/datacache/meson.build
@@ -5,9 +5,6 @@ configure_file(input : 'datacache.conf',
5 configuration : cdata, 5 configuration : cdata,
6 install: true, 6 install: true,
7 install_dir: pkgcfgdir) 7 install_dir: pkgcfgdir)
8install_data ('datacache-0001.sql',
9 'datacache-drop.sql',
10 install_dir: get_option('datadir')/'gnunet'/'sql')
11 8
12if get_option('monolith') 9if get_option('monolith')
13 foreach p : libgnunetdatacache_src 10 foreach p : libgnunetdatacache_src
@@ -27,36 +24,6 @@ libgnunetdatacache_dep = declare_dependency(link_with : libgnunetdatacache)
27pkg.generate(libgnunetdatacache, url: 'https://www.gnunet.org', 24pkg.generate(libgnunetdatacache, url: 'https://www.gnunet.org',
28 description : 'Provides datacache API for temporary storage to disk') 25 description : 'Provides datacache API for temporary storage to disk')
29 26
30shared_module('gnunet_plugin_datacache_sqlite',
31 ['plugin_datacache_sqlite.c'],
32 dependencies: [libgnunetutil_dep,
33 libgnunetdatacache_dep,
34 sqlite_dep,
35 libgnunetsq_dep],
36 include_directories: [incdir, configuration_inc],
37 install: true,
38 install_dir: get_option('libdir')/'gnunet')
39
40shared_module('gnunet_plugin_datacache_heap',
41 ['plugin_datacache_heap.c'],
42 dependencies: [libgnunetutil_dep,
43 libgnunetdatacache_dep],
44 include_directories: [incdir, configuration_inc],
45 install: true,
46 install_dir: get_option('libdir')/'gnunet')
47
48if pq_dep.found()
49 shared_module('gnunet_plugin_datacache_postgres',
50 ['plugin_datacache_postgres.c'],
51 dependencies: [libgnunetutil_dep,
52 libgnunetdatacache_dep,
53 pq_dep,
54 libgnunetpq_dep],
55 include_directories: [incdir, configuration_inc],
56 install: true,
57 install_dir: get_option('libdir')/'gnunet')
58endif
59
60testdc_sqlite = executable ('test_datacache_sqlite', 27testdc_sqlite = executable ('test_datacache_sqlite',
61 ['test_datacache.c'], 28 ['test_datacache.c'],
62 dependencies: [ 29 dependencies: [
diff --git a/src/service/datacache/plugin_datacache_heap.c b/src/service/datacache/plugin_datacache_heap.c
deleted file mode 100644
index 0dd8e47f8..000000000
--- a/src/service/datacache/plugin_datacache_heap.c
+++ /dev/null
@@ -1,530 +0,0 @@
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/service/datacache/plugin_datacache_postgres.c b/src/service/datacache/plugin_datacache_postgres.c
deleted file mode 100644
index 8bfd04aea..000000000
--- a/src/service/datacache/plugin_datacache_postgres.c
+++ /dev/null
@@ -1,616 +0,0 @@
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/service/datacache/plugin_datacache_sqlite.c b/src/service/datacache/plugin_datacache_sqlite.c
deleted file mode 100644
index 1c6f24a82..000000000
--- a/src/service/datacache/plugin_datacache_sqlite.c
+++ /dev/null
@@ -1,1063 +0,0 @@
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/service/datacache/plugin_datacache_template.c b/src/service/datacache/plugin_datacache_template.c
deleted file mode 100644
index 1bd712d39..000000000
--- a/src/service/datacache/plugin_datacache_template.c
+++ /dev/null
@@ -1,172 +0,0 @@
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/service/datastore/Makefile.am b/src/service/datastore/Makefile.am
index 9ae5004c6..22f98482a 100644
--- a/src/service/datastore/Makefile.am
+++ b/src/service/datastore/Makefile.am
@@ -10,12 +10,6 @@ libexecdir= $(pkglibdir)/libexec/
10pkgcfg_DATA = \ 10pkgcfg_DATA = \
11 datastore.conf 11 datastore.conf
12 12
13sqldir = $(prefix)/share/gnunet/sql/
14
15sql_DATA = \
16 datastore-0001.sql \
17 datastore-drop.sql
18
19if USE_COVERAGE 13if USE_COVERAGE
20 AM_CFLAGS = --coverage -O0 14 AM_CFLAGS = --coverage -O0
21 XLIBS = -lgcov 15 XLIBS = -lgcov
@@ -46,89 +40,30 @@ gnunet_service_datastore_LDADD = \
46 $(GN_LIBINTL) 40 $(GN_LIBINTL)
47 41
48if HAVE_SQLITE 42if HAVE_SQLITE
49 SQLITE_PLUGIN = libgnunet_plugin_datastore_sqlite.la
50if HAVE_BENCHMARKS 43if HAVE_BENCHMARKS
51 SQLITE_BENCHMARKS = \ 44 SQLITE_BENCHMARKS = \
52 perf_datastore_api_sqlite \ 45 perf_datastore_api_sqlite
53 perf_plugin_datastore_sqlite
54endif 46endif
55 SQLITE_TESTS = \ 47 SQLITE_TESTS = \
56 test_datastore_api_sqlite \ 48 test_datastore_api_sqlite \
57 test_datastore_api_management_sqlite \ 49 test_datastore_api_management_sqlite \
58 test_plugin_datastore_sqlite \
59 $(SQLITE_BENCHMARKS) 50 $(SQLITE_BENCHMARKS)
60endif 51endif
61if HAVE_POSTGRESQL 52if HAVE_POSTGRESQL
62 POSTGRES_PLUGIN = libgnunet_plugin_datastore_postgres.la
63if HAVE_BENCHMARKS 53if HAVE_BENCHMARKS
64 POSTGRES_BENCHMARKS = \ 54 POSTGRES_BENCHMARKS = \
65 perf_datastore_api_postgres \ 55 perf_datastore_api_postgres
66 perf_plugin_datastore_postgres
67endif 56endif
68 POSTGRES_TESTS = \ 57 POSTGRES_TESTS = \
69 test_datastore_api_postgres \ 58 test_datastore_api_postgres \
70 test_datastore_api_management_postgres \ 59 test_datastore_api_management_postgres \
71 test_plugin_datastore_postgres \
72 $(POSTGRES_BENCHMARKS) 60 $(POSTGRES_BENCHMARKS)
73endif 61endif
74 62
75plugin_LTLIBRARIES = \
76 $(SQLITE_PLUGIN) \
77 $(POSTGRES_PLUGIN) \
78 libgnunet_plugin_datastore_heap.la
79
80# Real plugins should of course go into
81# plugin_LTLIBRARIES
82noinst_LTLIBRARIES = \
83 libgnunet_plugin_datastore_template.la
84
85
86libgnunet_plugin_datastore_sqlite_la_SOURCES = \
87 plugin_datastore_sqlite.c
88libgnunet_plugin_datastore_sqlite_la_LIBADD = \
89 $(top_builddir)/src/lib/sq/libgnunetsq.la \
90 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
91 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lsqlite3 \
92 $(LTLIBINTL)
93libgnunet_plugin_datastore_sqlite_la_LDFLAGS = \
94 $(GN_PLUGIN_LDFLAGS)
95
96
97libgnunet_plugin_datastore_heap_la_SOURCES = \
98 plugin_datastore_heap.c
99libgnunet_plugin_datastore_heap_la_LIBADD = \
100 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
101 $(LTLIBINTL)
102libgnunet_plugin_datastore_heap_la_LDFLAGS = \
103 $(GN_PLUGIN_LDFLAGS)
104
105
106libgnunet_plugin_datastore_postgres_la_SOURCES = \
107 plugin_datastore_postgres.c
108libgnunet_plugin_datastore_postgres_la_LIBADD = \
109 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
110 $(top_builddir)/src/lib/pq/libgnunetpq.la \
111 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lpq
112libgnunet_plugin_datastore_postgres_la_LDFLAGS = \
113 $(GN_PLUGIN_LDFLAGS) $(POSTGRESQL_LDFLAGS)
114libgnunet_plugin_datastore_postgres_la_CPPFLAGS = \
115 $(POSTGRESQL_CPPFLAGS) $(AM_CPPFLAGS)
116
117
118libgnunet_plugin_datastore_template_la_SOURCES = \
119 plugin_datastore_template.c
120libgnunet_plugin_datastore_template_la_LIBADD = \
121 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
122 $(LTLIBINTL)
123libgnunet_plugin_datastore_template_la_LDFLAGS = \
124 $(GN_PLUGIN_LDFLAGS)
125
126check_PROGRAMS = \ 63check_PROGRAMS = \
127 test_datastore_api_heap \ 64 test_datastore_api_heap \
128 test_datastore_api_management_heap \ 65 test_datastore_api_management_heap \
129 perf_datastore_api_heap \ 66 perf_datastore_api_heap \
130 perf_plugin_datastore_heap \
131 test_plugin_datastore_heap \
132 $(SQLITE_TESTS) \ 67 $(SQLITE_TESTS) \
133 $(POSTGRES_TESTS) 68 $(POSTGRES_TESTS)
134 69
@@ -158,19 +93,6 @@ perf_datastore_api_heap_LDADD = \
158 libgnunetdatastore.la \ 93 libgnunetdatastore.la \
159 $(top_builddir)/src/lib/util/libgnunetutil.la 94 $(top_builddir)/src/lib/util/libgnunetutil.la
160 95
161perf_plugin_datastore_heap_SOURCES = \
162 perf_plugin_datastore.c
163perf_plugin_datastore_heap_LDADD = \
164 $(top_builddir)/src/service/testing/libgnunettesting.la \
165 $(top_builddir)/src/lib/util/libgnunetutil.la
166
167test_plugin_datastore_heap_SOURCES = \
168 test_plugin_datastore.c
169test_plugin_datastore_heap_LDADD = \
170 $(top_builddir)/src/service/testing/libgnunettesting.la \
171 $(top_builddir)/src/lib/util/libgnunetutil.la
172
173
174test_datastore_api_sqlite_SOURCES = \ 96test_datastore_api_sqlite_SOURCES = \
175 test_datastore_api.c 97 test_datastore_api.c
176test_datastore_api_sqlite_LDADD = \ 98test_datastore_api_sqlite_LDADD = \
@@ -192,19 +114,6 @@ perf_datastore_api_sqlite_LDADD = \
192 libgnunetdatastore.la \ 114 libgnunetdatastore.la \
193 $(top_builddir)/src/lib/util/libgnunetutil.la 115 $(top_builddir)/src/lib/util/libgnunetutil.la
194 116
195perf_plugin_datastore_sqlite_SOURCES = \
196 perf_plugin_datastore.c
197perf_plugin_datastore_sqlite_LDADD = \
198 $(top_builddir)/src/service/testing/libgnunettesting.la \
199 $(top_builddir)/src/lib/util/libgnunetutil.la
200
201test_plugin_datastore_sqlite_SOURCES = \
202 test_plugin_datastore.c
203test_plugin_datastore_sqlite_LDADD = \
204 $(top_builddir)/src/service/testing/libgnunettesting.la \
205 $(top_builddir)/src/lib/util/libgnunetutil.la
206
207
208test_datastore_api_postgres_SOURCES = \ 117test_datastore_api_postgres_SOURCES = \
209 test_datastore_api.c 118 test_datastore_api.c
210test_datastore_api_postgres_LDADD = \ 119test_datastore_api_postgres_LDADD = \
@@ -226,28 +135,8 @@ perf_datastore_api_postgres_LDADD = \
226 libgnunetdatastore.la \ 135 libgnunetdatastore.la \
227 $(top_builddir)/src/lib/util/libgnunetutil.la 136 $(top_builddir)/src/lib/util/libgnunetutil.la
228 137
229test_plugin_datastore_postgres_SOURCES = \
230 test_plugin_datastore.c
231test_plugin_datastore_postgres_LDADD = \
232 $(top_builddir)/src/service/testing/libgnunettesting.la \
233 $(top_builddir)/src/lib/util/libgnunetutil.la
234
235perf_plugin_datastore_postgres_SOURCES = \
236 perf_plugin_datastore.c
237perf_plugin_datastore_postgres_LDADD = \
238 $(top_builddir)/src/service/testing/libgnunettesting.la \
239 $(top_builddir)/src/lib/util/libgnunetutil.la
240
241
242EXTRA_DIST = \ 138EXTRA_DIST = \
243 test_defaults.conf \ 139 test_defaults.conf \
244 test_datastore_api_data_sqlite.conf \ 140 test_datastore_api_data_sqlite.conf \
245 perf_plugin_datastore_data_sqlite.conf \
246 test_plugin_datastore_data_sqlite.conf \
247 test_datastore_api_data_heap.conf \ 141 test_datastore_api_data_heap.conf \
248 perf_plugin_datastore_data_heap.conf \ 142 test_datastore_api_data_postgres.conf
249 test_plugin_datastore_data_heap.conf \
250 test_datastore_api_data_postgres.conf \
251 perf_plugin_datastore_data_postgres.conf \
252 test_plugin_datastore_data_postgres.conf \
253 $(sql_DATA)
diff --git a/src/service/datastore/datastore-0001.sql b/src/service/datastore/datastore-0001.sql
deleted file mode 100644
index 0d4758be2..000000000
--- a/src/service/datastore/datastore-0001.sql
+++ /dev/null
@@ -1,49 +0,0 @@
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/service/datastore/datastore-drop.sql b/src/service/datastore/datastore-drop.sql
deleted file mode 100644
index 67fee303d..000000000
--- a/src/service/datastore/datastore-drop.sql
+++ /dev/null
@@ -1,25 +0,0 @@
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/service/datastore/meson.build b/src/service/datastore/meson.build
index 9dde488a0..b81d8bb86 100644
--- a/src/service/datastore/meson.build
+++ b/src/service/datastore/meson.build
@@ -8,10 +8,6 @@ configure_file(input : 'datastore.conf.in',
8 install: true, 8 install: true,
9 install_dir: pkgcfgdir) 9 install_dir: pkgcfgdir)
10 10
11install_data ('datastore-0001.sql',
12 'datastore-drop.sql',
13 install_dir: get_option('datadir')/'gnunet'/'sql')
14
15if get_option('monolith') 11if get_option('monolith')
16 foreach p : libgnunetdatastore_src + gnunetservicedatastore_src 12 foreach p : libgnunetdatastore_src + gnunetservicedatastore_src
17 gnunet_src += 'datastore/' + p 13 gnunet_src += 'datastore/' + p
@@ -32,36 +28,6 @@ libgnunetdatastore_dep = declare_dependency(link_with : libgnunetdatastore)
32pkg.generate(libgnunetdatastore, url: 'https://www.gnunet.org', 28pkg.generate(libgnunetdatastore, url: 'https://www.gnunet.org',
33 description : 'Management API for the datastore for persistent storage to disk') 29 description : 'Management API for the datastore for persistent storage to disk')
34 30
35shared_module('gnunet_plugin_datastore_sqlite',
36 ['plugin_datastore_sqlite.c'],
37 dependencies: [libgnunetutil_dep,
38 libgnunetdatastore_dep,
39 sqlite_dep,
40 libgnunetsq_dep],
41 include_directories: [incdir, configuration_inc],
42 install: true,
43 install_dir: get_option('libdir')/'gnunet')
44
45shared_module('gnunet_plugin_datastore_heap',
46 ['plugin_datastore_heap.c'],
47 dependencies: [libgnunetutil_dep,
48 libgnunetdatacache_dep],
49 include_directories: [incdir, configuration_inc],
50 install: true,
51 install_dir: get_option('libdir')/'gnunet')
52
53if pq_dep.found()
54 shared_module('gnunet_plugin_datastore_postgres',
55 ['plugin_datastore_postgres.c'],
56 dependencies: [libgnunetutil_dep,
57 libgnunetdatastore_dep,
58 pq_dep,
59 libgnunetpq_dep],
60 include_directories: [incdir, configuration_inc],
61 install: true,
62 install_dir: get_option('libdir')/'gnunet')
63endif
64
65executable ('gnunet-service-datastore', 31executable ('gnunet-service-datastore',
66 gnunetservicedatastore_src, 32 gnunetservicedatastore_src,
67 dependencies: [libgnunetdatastore_dep, 33 dependencies: [libgnunetdatastore_dep,
@@ -92,16 +58,6 @@ testds_mgmt_sqlite = executable ('test_datastore_api_management_sqlite',
92 include_directories: [incdir, configuration_inc], 58 include_directories: [incdir, configuration_inc],
93 install: false) 59 install: false)
94 60
95testds_plugin_sqlite = executable ('test_plugin_datastore_sqlite',
96 ['test_plugin_datastore.c'],
97 dependencies: [
98 libgnunetdatastore_dep,
99 libgnunetutil_dep,
100 libgnunettesting_dep
101 ],
102 include_directories: [incdir, configuration_inc],
103 install: false)
104
105testds_heap = executable ('test_datastore_api_heap', 61testds_heap = executable ('test_datastore_api_heap',
106 ['test_datastore_api.c'], 62 ['test_datastore_api.c'],
107 dependencies: [ 63 dependencies: [
@@ -122,16 +78,6 @@ testds_mgmt_heap = executable ('test_datastore_api_management_heap',
122 include_directories: [incdir, configuration_inc], 78 include_directories: [incdir, configuration_inc],
123 install: false) 79 install: false)
124 80
125testds_plugin_heap = executable ('test_plugin_datastore_heap',
126 ['test_plugin_datastore.c'],
127 dependencies: [
128 libgnunetdatastore_dep,
129 libgnunetutil_dep,
130 libgnunettesting_dep
131 ],
132 include_directories: [incdir, configuration_inc],
133 install: false)
134
135testds_pq = executable ('test_datastore_api_postgres', 81testds_pq = executable ('test_datastore_api_postgres',
136 ['test_datastore_api.c'], 82 ['test_datastore_api.c'],
137 dependencies: [ 83 dependencies: [
@@ -142,16 +88,6 @@ testds_pq = executable ('test_datastore_api_postgres',
142 include_directories: [incdir, configuration_inc], 88 include_directories: [incdir, configuration_inc],
143 install: false) 89 install: false)
144 90
145testds_plugin_pq = executable ('test_plugin_datastore_postgres',
146 ['test_plugin_datastore.c'],
147 dependencies: [
148 libgnunetdatastore_dep,
149 libgnunetutil_dep,
150 libgnunettesting_dep
151 ],
152 include_directories: [incdir, configuration_inc],
153 install: false)
154
155testds_mgmt_pq = executable ('test_datastore_api_management_postgres', 91testds_mgmt_pq = executable ('test_datastore_api_management_postgres',
156 ['test_datastore_api_management.c'], 92 ['test_datastore_api_management.c'],
157 dependencies: [ 93 dependencies: [
@@ -179,18 +115,12 @@ test('test_datastore_api_sqlite', testds_sqlite,
179 suite: 'datastore', workdir: meson.current_build_dir()) 115 suite: 'datastore', workdir: meson.current_build_dir())
180test('test_datastore_api_management_sqlite', testds_mgmt_sqlite, 116test('test_datastore_api_management_sqlite', testds_mgmt_sqlite,
181 suite: 'datastore', workdir: meson.current_build_dir()) 117 suite: 'datastore', workdir: meson.current_build_dir())
182test('test_plugin_datastore_sqlite', testds_plugin_sqlite,
183 suite: 'datastore', workdir: meson.current_build_dir())
184test('test_datastore_api_heap', testds_heap, 118test('test_datastore_api_heap', testds_heap,
185 suite: 'datastore', workdir: meson.current_build_dir()) 119 suite: 'datastore', workdir: meson.current_build_dir())
186test('test_datastore_api_management_heap', testds_mgmt_heap, 120test('test_datastore_api_management_heap', testds_mgmt_heap,
187 suite: 'datastore', workdir: meson.current_build_dir()) 121 suite: 'datastore', workdir: meson.current_build_dir())
188test('test_plugin_datastore_heap', testds_plugin_heap,
189 suite: 'datastore', workdir: meson.current_build_dir())
190test('test_datastore_api_postgres', testds_pq, 122test('test_datastore_api_postgres', testds_pq,
191 suite: 'datastore', workdir: meson.current_build_dir()) 123 suite: 'datastore', workdir: meson.current_build_dir())
192test('test_datastore_api_management_postgres', testds_mgmt_pq, 124test('test_datastore_api_management_postgres', testds_mgmt_pq,
193 suite: 'datastore', workdir: meson.current_build_dir()) 125 suite: 'datastore', workdir: meson.current_build_dir())
194test('test_plugin_datastore_postgres', testds_plugin_pq,
195 suite: 'datastore', workdir: meson.current_build_dir())
196 126
diff --git a/src/service/datastore/perf_plugin_datastore.c b/src/service/datastore/perf_plugin_datastore.c
deleted file mode 100644
index 8e63b08e6..000000000
--- a/src/service/datastore/perf_plugin_datastore.c
+++ /dev/null
@@ -1,573 +0,0 @@
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/service/datastore/perf_plugin_datastore_data_heap.conf b/src/service/datastore/perf_plugin_datastore_data_heap.conf
deleted file mode 100644
index 873cf9606..000000000
--- a/src/service/datastore/perf_plugin_datastore_data_heap.conf
+++ /dev/null
@@ -1,7 +0,0 @@
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/service/datastore/perf_plugin_datastore_data_postgres.conf b/src/service/datastore/perf_plugin_datastore_data_postgres.conf
deleted file mode 100644
index 7683887a8..000000000
--- a/src/service/datastore/perf_plugin_datastore_data_postgres.conf
+++ /dev/null
@@ -1,10 +0,0 @@
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/service/datastore/perf_plugin_datastore_data_sqlite.conf b/src/service/datastore/perf_plugin_datastore_data_sqlite.conf
deleted file mode 100644
index 888e020a6..000000000
--- a/src/service/datastore/perf_plugin_datastore_data_sqlite.conf
+++ /dev/null
@@ -1,4 +0,0 @@
1@INLINE@ test_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/perf-gnunet-datastore-sqlite/
4
diff --git a/src/service/datastore/plugin_datastore_heap.c b/src/service/datastore/plugin_datastore_heap.c
deleted file mode 100644
index a827a2763..000000000
--- a/src/service/datastore/plugin_datastore_heap.c
+++ /dev/null
@@ -1,944 +0,0 @@
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/service/datastore/plugin_datastore_postgres.c b/src/service/datastore/plugin_datastore_postgres.c
deleted file mode 100644
index 5fcacc17b..000000000
--- a/src/service/datastore/plugin_datastore_postgres.c
+++ /dev/null
@@ -1,930 +0,0 @@
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/service/datastore/plugin_datastore_sqlite.c b/src/service/datastore/plugin_datastore_sqlite.c
deleted file mode 100644
index 5ea9da4cb..000000000
--- a/src/service/datastore/plugin_datastore_sqlite.c
+++ /dev/null
@@ -1,1375 +0,0 @@
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/service/datastore/plugin_datastore_template.c b/src/service/datastore/plugin_datastore_template.c
deleted file mode 100644
index 2b455f8cb..000000000
--- a/src/service/datastore/plugin_datastore_template.c
+++ /dev/null
@@ -1,274 +0,0 @@
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/service/datastore/test_plugin_datastore.c b/src/service/datastore/test_plugin_datastore.c
deleted file mode 100644
index 7de1acf2d..000000000
--- a/src/service/datastore/test_plugin_datastore.c
+++ /dev/null
@@ -1,478 +0,0 @@
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/service/datastore/test_plugin_datastore_data_heap.conf b/src/service/datastore/test_plugin_datastore_data_heap.conf
deleted file mode 100644
index b1ea8ff67..000000000
--- a/src/service/datastore/test_plugin_datastore_data_heap.conf
+++ /dev/null
@@ -1,6 +0,0 @@
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/service/datastore/test_plugin_datastore_data_postgres.conf b/src/service/datastore/test_plugin_datastore_data_postgres.conf
deleted file mode 100644
index d0e29437f..000000000
--- a/src/service/datastore/test_plugin_datastore_data_postgres.conf
+++ /dev/null
@@ -1,10 +0,0 @@
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/service/datastore/test_plugin_datastore_data_sqlite.conf b/src/service/datastore/test_plugin_datastore_data_sqlite.conf
deleted file mode 100644
index ca837c77a..000000000
--- a/src/service/datastore/test_plugin_datastore_data_sqlite.conf
+++ /dev/null
@@ -1,4 +0,0 @@
1@INLINE@ test_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-datastore-plugin-sqlite/
4
diff --git a/src/service/dht/Makefile.am b/src/service/dht/Makefile.am
index bf3d26235..53df04faf 100644
--- a/src/service/dht/Makefile.am
+++ b/src/service/dht/Makefile.am
@@ -29,21 +29,6 @@ libgnunetdht_la_LDFLAGS = \
29 -version-info 4:0:0 29 -version-info 4:0:0
30 30
31 31
32plugin_LTLIBRARIES = \
33 libgnunet_plugin_block_dht.la
34
35libgnunet_plugin_block_dht_la_SOURCES = \
36 plugin_block_dht.c
37libgnunet_plugin_block_dht_la_LIBADD = \
38 $(top_builddir)/src/lib/hello/libgnunethello.la \
39 $(top_builddir)/src/lib/block/libgnunetblock.la \
40 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \
41 $(top_builddir)/src/lib/util/libgnunetutil.la \
42 $(LTLIBINTL)
43libgnunet_plugin_block_dht_la_LDFLAGS = \
44 $(GN_PLUGIN_LDFLAGS)
45
46
47libexec_PROGRAMS = \ 32libexec_PROGRAMS = \
48 gnunet-service-dht 33 gnunet-service-dht
49 34
diff --git a/src/service/dht/meson.build b/src/service/dht/meson.build
index 49ace7e0b..3972a0983 100644
--- a/src/service/dht/meson.build
+++ b/src/service/dht/meson.build
@@ -29,16 +29,6 @@ libgnunetdht_dep = declare_dependency(link_with : libgnunetdht)
29pkg.generate(libgnunetdht, url: 'https://www.gnunet.org', 29pkg.generate(libgnunetdht, url: 'https://www.gnunet.org',
30 description : 'Provides API for the R5N distributed hash table') 30 description : 'Provides API for the R5N distributed hash table')
31 31
32shared_module('gnunet_plugin_block_dht',
33 ['plugin_block_dht.c'],
34 dependencies: [libgnunetutil_dep,
35 libgnunethello_dep,
36 libgnunetblock_dep,
37 libgnunetblockgroup_dep],
38 include_directories: [incdir, configuration_inc],
39 install:true,
40 install_dir: get_option('libdir')/'gnunet')
41
42executable ('gnunet-service-dht', 32executable ('gnunet-service-dht',
43 gnunetservicedht_src, 33 gnunetservicedht_src,
44 dependencies: [libgnunetdht_dep, libgnunetutil_dep, 34 dependencies: [libgnunetdht_dep, libgnunetutil_dep,
diff --git a/src/service/dht/plugin_block_dht.c b/src/service/dht/plugin_block_dht.c
deleted file mode 100644
index aa5ffc719..000000000
--- a/src/service/dht/plugin_block_dht.c
+++ /dev/null
@@ -1,311 +0,0 @@
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/service/dhtu/.gitignore b/src/service/dhtu/.gitignore
new file mode 100644
index 000000000..21f1a7c9b
--- /dev/null
+++ b/src/service/dhtu/.gitignore
@@ -0,0 +1 @@
test_dhtu_ip
diff --git a/src/service/dhtu/Makefile.am b/src/service/dhtu/Makefile.am
new file mode 100644
index 000000000..500d5a7b1
--- /dev/null
+++ b/src/service/dhtu/Makefile.am
@@ -0,0 +1,81 @@
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/service/dhtu/dhtu.conf b/src/service/dhtu/dhtu.conf
new file mode 100644
index 000000000..ea5ade752
--- /dev/null
+++ b/src/service/dhtu/dhtu.conf
@@ -0,0 +1,7 @@
1[dhtu-gnunet]
2ENABLED = YES
3
4[dhtu-ip]
5ENABLED = NO
6NSE = 4
7UDP_PORT = 6666
diff --git a/src/service/dhtu/meson.build b/src/service/dhtu/meson.build
new file mode 100644
index 000000000..b2525e782
--- /dev/null
+++ b/src/service/dhtu/meson.build
@@ -0,0 +1,61 @@
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/service/dhtu/plugin_dhtu_gnunet.c b/src/service/dhtu/plugin_dhtu_gnunet.c
new file mode 100644
index 000000000..75f466916
--- /dev/null
+++ b/src/service/dhtu/plugin_dhtu_gnunet.c
@@ -0,0 +1,603 @@
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/service/dhtu/plugin_dhtu_ip.c b/src/service/dhtu/plugin_dhtu_ip.c
new file mode 100644
index 000000000..06d0f0f60
--- /dev/null
+++ b/src/service/dhtu/plugin_dhtu_ip.c
@@ -0,0 +1,1171 @@
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/service/dhtu/test_dhtu_ip.c b/src/service/dhtu/test_dhtu_ip.c
new file mode 100644
index 000000000..030b17b5f
--- /dev/null
+++ b/src/service/dhtu/test_dhtu_ip.c
@@ -0,0 +1,45 @@
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/service/dhtu/testing_dhtu_cmd_send.c b/src/service/dhtu/testing_dhtu_cmd_send.c
new file mode 100644
index 000000000..45d166b14
--- /dev/null
+++ b/src/service/dhtu/testing_dhtu_cmd_send.c
@@ -0,0 +1,119 @@
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/service/dns/Makefile.am b/src/service/dns/Makefile.am
index 38ee78c3a..0d07b9e5e 100644
--- a/src/service/dns/Makefile.am
+++ b/src/service/dns/Makefile.am
@@ -29,9 +29,6 @@ noinst_PROGRAMS = \
29 gnunet-dns-redirector \ 29 gnunet-dns-redirector \
30 gnunet-zonewalk 30 gnunet-zonewalk
31 31
32plugin_LTLIBRARIES = \
33 libgnunet_plugin_block_dns.la
34
35if LINUX 32if LINUX
36check_SCRIPTS = \ 33check_SCRIPTS = \
37 test_gnunet_dns.sh 34 test_gnunet_dns.sh
@@ -77,17 +74,6 @@ libgnunetdns_la_LDFLAGS = \
77 $(GN_LIB_LDFLAGS) \ 74 $(GN_LIB_LDFLAGS) \
78 -version-info 0:0:0 75 -version-info 0:0:0
79 76
80libgnunet_plugin_block_dns_la_SOURCES = \
81 plugin_block_dns.c
82libgnunet_plugin_block_dns_la_LIBADD = \
83 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \
84 $(top_builddir)/src/lib/block/libgnunetblock.la \
85 $(top_builddir)/src/lib/util/libgnunetutil.la
86libgnunet_plugin_block_dns_la_LDFLAGS = \
87 $(GN_LIBINTL) \
88 $(top_builddir)/src/block/$(GN_PLUGIN_LDFLAGS)
89
90
91if ENABLE_TEST_RUN 77if ENABLE_TEST_RUN
92AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; 78AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
93TESTS = $(check_PROGRAMS) $(check_SCRIPTS) 79TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
diff --git a/src/service/dns/meson.build b/src/service/dns/meson.build
index fe0f6e954..e1b7f7670 100644
--- a/src/service/dns/meson.build
+++ b/src/service/dns/meson.build
@@ -1,5 +1,4 @@
1libgnunetdns_src = ['dns_api.c'] 1libgnunetdns_src = ['dns_api.c']
2libgnunetpluginblockdns_src = ['plugin_block_dns.c']
3 2
4gnunetservicedns_src = ['gnunet-service-dns.c'] 3gnunetservicedns_src = ['gnunet-service-dns.c']
5gnunetdnsredirector_src = ['gnunet-dns-monitor.c'] 4gnunetdnsredirector_src = ['gnunet-dns-monitor.c']
@@ -12,7 +11,7 @@ configure_file(input : 'dns.conf.in',
12 install_dir: pkgcfgdir) 11 install_dir: pkgcfgdir)
13 12
14if get_option('monolith') 13if get_option('monolith')
15 foreach p : libgnunetdns_src + gnunetservicedns_src + libgnunetpluginblockdns_src 14 foreach p : libgnunetdns_src + gnunetservicedns_src
16 gnunet_src += 'dns/' + p 15 gnunet_src += 'dns/' + p
17 endforeach 16 endforeach
18endif 17endif
@@ -29,13 +28,6 @@ libgnunetdns_dep = declare_dependency(link_with : libgnunetdns)
29pkg.generate(libgnunetdns, url: 'https://www.gnunet.org', 28pkg.generate(libgnunetdns, url: 'https://www.gnunet.org',
30 description : 'Provides API to access GNUnet\'s DNS service (to intercept and manipulate DNS queries)') 29 description : 'Provides API to access GNUnet\'s DNS service (to intercept and manipulate DNS queries)')
31 30
32shared_module('gnunet_plugin_block_dns',
33 libgnunetpluginblockdns_src,
34 dependencies: [libgnunetutil_dep, libgnunetblockgroup_dep],
35 include_directories: [incdir, configuration_inc],
36 install: true,
37 install_dir: get_option('libdir')/'gnunet')
38
39executable ('gnunet-service-dns', 31executable ('gnunet-service-dns',
40 gnunetservicedns_src, 32 gnunetservicedns_src,
41 dependencies: [libgnunetdns_dep, libgnunetutil_dep, 33 dependencies: [libgnunetdns_dep, libgnunetutil_dep,
diff --git a/src/service/dns/plugin_block_dns.c b/src/service/dns/plugin_block_dns.c
deleted file mode 100644
index 1bbd7f750..000000000
--- a/src/service/dns/plugin_block_dns.c
+++ /dev/null
@@ -1,290 +0,0 @@
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/service/meson.build b/src/service/meson.build
index 6acbc3b10..b089c9794 100644
--- a/src/service/meson.build
+++ b/src/service/meson.build
@@ -2,7 +2,6 @@ subdir('util')
2subdir('arm') 2subdir('arm')
3subdir('statistics') 3subdir('statistics')
4subdir('testing') 4subdir('testing')
5subdir('rest')
6subdir('datacache') 5subdir('datacache')
7subdir('datastore') 6subdir('datastore')
8subdir('peerstore') 7subdir('peerstore')
@@ -11,6 +10,7 @@ subdir('nat-auto')
11subdir('transport') 10subdir('transport')
12subdir('core') 11subdir('core')
13subdir('nse') 12subdir('nse')
13subdir('dhtu')
14subdir('dht') 14subdir('dht')
15subdir('hostlist') 15subdir('hostlist')
16subdir('regex') 16subdir('regex')
@@ -30,6 +30,7 @@ subdir('exit')
30subdir('pt') 30subdir('pt')
31subdir('reclaim') 31subdir('reclaim')
32subdir('messenger') 32subdir('messenger')
33subdir('rest')
33 34
34if not get_option('monolith') 35if not get_option('monolith')
35 subdir_done() 36 subdir_done()
@@ -42,7 +43,10 @@ libgnunet = library('gnunet',
42 libgnunetjson_dep, 43 libgnunetjson_dep,
43 libgnunetblock_dep, 44 libgnunetblock_dep,
44 libgnunetblockgroup_dep, 45 libgnunetblockgroup_dep,
46 libgnunetregexblock_dep,
45 libgnunetgnsrecord_dep, 47 libgnunetgnsrecord_dep,
48 libgnunetgnsrecordjson_dep,
49 jose_dep,
46 libgnunetcurl_dep], 50 libgnunetcurl_dep],
47 c_args: ['-DHAVE_GNUNET_MONOLITH=1'], 51 c_args: ['-DHAVE_GNUNET_MONOLITH=1'],
48 include_directories: [incdir, configuration_inc, exitdir]) 52 include_directories: [incdir, configuration_inc, exitdir])
diff --git a/src/service/messenger/Makefile.am b/src/service/messenger/Makefile.am
index d778d6e0c..c2ae65a08 100644
--- a/src/service/messenger/Makefile.am
+++ b/src/service/messenger/Makefile.am
@@ -10,19 +10,6 @@ pkgcfgdir= $(pkgdatadir)/config.d/
10 10
11libexecdir= $(pkglibdir)/libexec/ 11libexecdir= $(pkglibdir)/libexec/
12 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
26pkgcfg_DATA = \ 13pkgcfg_DATA = \
27 messenger.conf 14 messenger.conf
28 15
diff --git a/src/service/messenger/meson.build b/src/service/messenger/meson.build
index a6de2f516..3c05d7760 100644
--- a/src/service/messenger/meson.build
+++ b/src/service/messenger/meson.build
@@ -55,14 +55,6 @@ libgnunetmessenger_dep = declare_dependency(link_with : libgnunetmessenger)
55pkg.generate(libgnunetmessenger, url: 'https://www.gnunet.org', 55pkg.generate(libgnunetmessenger, url: 'https://www.gnunet.org',
56 description : 'Provides API to access the GNUnet Messenger subsystem') 56 description : 'Provides API to access the GNUnet Messenger subsystem')
57 57
58shared_module('gnunet_plugin_gnsrecord_messenger',
59 ['plugin_gnsrecord_messenger.c'],
60 dependencies: [libgnunetutil_dep, libgnunetgnsrecord_dep,
61 libgnunetmessenger_dep],
62 include_directories: [incdir, configuration_inc],
63 install: true,
64 install_dir: get_option('libdir')/'gnunet')
65
66executable ('gnunet-service-messenger', 58executable ('gnunet-service-messenger',
67 gnunetservicemessenger_src, 59 gnunetservicemessenger_src,
68 dependencies: [libgnunetmessenger_dep, 60 dependencies: [libgnunetmessenger_dep,
diff --git a/src/service/messenger/plugin_gnsrecord_messenger.c b/src/service/messenger/plugin_gnsrecord_messenger.c
deleted file mode 100644
index e09a0330d..000000000
--- a/src/service/messenger/plugin_gnsrecord_messenger.c
+++ /dev/null
@@ -1,298 +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 * @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/service/namestore/meson.build b/src/service/namestore/meson.build
index 879eaef09..912b312fa 100644
--- a/src/service/namestore/meson.build
+++ b/src/service/namestore/meson.build
@@ -1,7 +1,5 @@
1libgnunetnamestore_src = ['namestore_api.c', 'namestore_api_monitor.c'] 1libgnunetnamestore_src = ['namestore_api.c', 'namestore_api_monitor.c']
2libgnunetpluginnamestore_sqlite_src = ['plugin_namestore_sqlite.c']
3 2
4gnunetnamestore_src = ['gnunet-namestore.c']
5gnunetservicenamestore_src = ['gnunet-service-namestore.c'] 3gnunetservicenamestore_src = ['gnunet-service-namestore.c']
6 4
7configure_file(input : 'namestore.conf.in', 5configure_file(input : 'namestore.conf.in',
diff --git a/src/service/peerstore/Makefile.am b/src/service/peerstore/Makefile.am
index 28948b7db..de301bbcf 100644
--- a/src/service/peerstore/Makefile.am
+++ b/src/service/peerstore/Makefile.am
@@ -51,63 +51,15 @@ libgnunetpeerstore_la_LDFLAGS = \
51 $(GN_LIBINTL) \ 51 $(GN_LIBINTL) \
52 $(GN_LIB_LDFLAGS) 52 $(GN_LIB_LDFLAGS)
53 53
54if HAVE_EXPERIMENTAL
55FLAT_PLUGIN = libgnunet_plugin_peerstore_flat.la
56FLAT_TESTS = test_plugin_peerstore_flat
57libgnunet_plugin_peerstore_flat_la_SOURCES = \
58 plugin_peerstore_flat.c
59libgnunet_plugin_peerstore_flat_la_LIBADD = \
60 libgnunetpeerstore.la \
61 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
62 $(LTLIBINTL)
63libgnunet_plugin_peerstore_flat_la_LDFLAGS = \
64 $(GN_PLUGIN_LDFLAGS)
65endif
66
67if HAVE_SQLITE
68SQLITE_PLUGIN = libgnunet_plugin_peerstore_sqlite.la
69SQLITE_TESTS = test_plugin_peerstore_sqlite
70libgnunet_plugin_peerstore_sqlite_la_SOURCES = \
71 plugin_peerstore_sqlite.c
72libgnunet_plugin_peerstore_sqlite_la_LIBADD = \
73 libgnunetpeerstore.la \
74 $(top_builddir)/src/lib/sq/libgnunetsq.la \
75 $(top_builddir)/src/lib/util/libgnunetutil.la \
76 $(XLIBS) -lsqlite3 \
77 $(LTLIBINTL)
78libgnunet_plugin_peerstore_sqlite_la_LDFLAGS = \
79 $(GN_PLUGIN_LDFLAGS)
80endif
81
82plugin_LTLIBRARIES = \
83 $(SQLITE_PLUGIN) \
84 $(FLAT_PLUGIN)
85
86test_plugin_peerstore_sqlite_SOURCES = \
87 test_plugin_peerstore.c
88test_plugin_peerstore_sqlite_LDADD = \
89 $(top_builddir)/src/service/testing/libgnunettesting.la \
90 $(top_builddir)/src/lib/util/libgnunetutil.la
91
92test_plugin_peerstore_flat_SOURCES = \
93 test_plugin_peerstore.c
94test_plugin_peerstore_flat_LDADD = \
95 $(top_builddir)/src/service/testing/libgnunettesting.la \
96 $(top_builddir)/src/lib/util/libgnunetutil.la
97
98check_PROGRAMS = \ 54check_PROGRAMS = \
99 test_peerstore_api_store \ 55 test_peerstore_api_store \
100 test_peerstore_api_iterate \ 56 test_peerstore_api_iterate \
101 test_peerstore_api_watch \ 57 test_peerstore_api_watch \
102 test_peerstore_api_sync \ 58 test_peerstore_api_sync \
103 perf_peerstore_store \ 59 perf_peerstore_store
104 $(SQLITE_TESTS) \
105 $(FLAT_TESTS)
106 60
107EXTRA_DIST = \ 61EXTRA_DIST = \
108 test_peerstore_api_data.conf \ 62 test_peerstore_api_data.conf
109 test_plugin_peerstore_flat.conf \
110 test_plugin_peerstore_sqlite.conf
111 63
112if ENABLE_TEST_RUN 64if ENABLE_TEST_RUN
113AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; 65AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
diff --git a/src/service/peerstore/meson.build b/src/service/peerstore/meson.build
index 7d8710c4b..db70b0b9e 100644
--- a/src/service/peerstore/meson.build
+++ b/src/service/peerstore/meson.build
@@ -28,16 +28,6 @@ pkg.generate(libgnunetpeerstore, url: 'https://www.gnunet.org',
28 description : 'Provides API for accessing the peerstore service') 28 description : 'Provides API for accessing the peerstore service')
29libgnunetpeerstore_dep = declare_dependency(link_with : libgnunetpeerstore) 29libgnunetpeerstore_dep = declare_dependency(link_with : libgnunetpeerstore)
30 30
31shared_module('gnunet_plugin_peerstore_sqlite',
32 ['plugin_peerstore_sqlite.c'],
33 dependencies: [libgnunetutil_dep,
34 libgnunetpeerstore_dep,
35 libgnunetsq_dep,
36 sqlite_dep],
37 include_directories: [incdir, configuration_inc],
38 install: true,
39 install_dir: get_option('libdir')/'gnunet')
40
41executable ('gnunet-service-peerstore', 31executable ('gnunet-service-peerstore',
42 gnunetservicepeerstore_src, 32 gnunetservicepeerstore_src,
43 dependencies: [libgnunetpeerstore_dep, 33 dependencies: [libgnunetpeerstore_dep,
diff --git a/src/service/peerstore/plugin_peerstore_flat.c b/src/service/peerstore/plugin_peerstore_flat.c
deleted file mode 100644
index cc5b8b76b..000000000
--- a/src/service/peerstore/plugin_peerstore_flat.c
+++ /dev/null
@@ -1,606 +0,0 @@
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 "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/service/peerstore/plugin_peerstore_sqlite.c b/src/service/peerstore/plugin_peerstore_sqlite.c
deleted file mode 100644
index ad69efdf4..000000000
--- a/src/service/peerstore/plugin_peerstore_sqlite.c
+++ /dev/null
@@ -1,702 +0,0 @@
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 "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/service/peerstore/test_plugin_peerstore.c b/src/service/peerstore/test_plugin_peerstore.c
deleted file mode 100644
index bce62dda9..000000000
--- a/src/service/peerstore/test_plugin_peerstore.c
+++ /dev/null
@@ -1,224 +0,0 @@
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/service/peerstore/test_plugin_peerstore_flat.conf b/src/service/peerstore/test_plugin_peerstore_flat.conf
deleted file mode 100644
index c55b1e9d6..000000000
--- a/src/service/peerstore/test_plugin_peerstore_flat.conf
+++ /dev/null
@@ -1,5 +0,0 @@
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/service/peerstore/test_plugin_peerstore_sqlite.conf b/src/service/peerstore/test_plugin_peerstore_sqlite.conf
deleted file mode 100644
index dcf1fc1a4..000000000
--- a/src/service/peerstore/test_plugin_peerstore_sqlite.conf
+++ /dev/null
@@ -1,2 +0,0 @@
1[peerstore-sqlite]
2FILENAME = $GNUNET_TMP/gnunet-test-plugin-peerstore-sqlite/sqlite.db
diff --git a/src/service/regex/Makefile.am b/src/service/regex/Makefile.am
index 14cdb054b..255988889 100644
--- a/src/service/regex/Makefile.am
+++ b/src/service/regex/Makefile.am
@@ -31,27 +31,15 @@ REGEX_INTERNAL_TEST = \
31gnunet_service_regex_SOURCES = \ 31gnunet_service_regex_SOURCES = \
32 $(REGEX_INTERNAL) gnunet-service-regex.c 32 $(REGEX_INTERNAL) gnunet-service-regex.c
33gnunet_service_regex_LDADD = -lm \ 33gnunet_service_regex_LDADD = -lm \
34 libgnunetregexblock.la \ 34 $(top_builddir)/src/plugin/regex/libgnunetregexblock.la \
35 $(top_builddir)/src/service/dht/libgnunetdht.la \ 35 $(top_builddir)/src/service/dht/libgnunetdht.la \
36 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ 36 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
37 $(top_builddir)/src/lib/util/libgnunetutil.la \ 37 $(top_builddir)/src/lib/util/libgnunetutil.la \
38 $(GN_LIBINTL) 38 $(GN_LIBINTL)
39 39
40lib_LTLIBRARIES = \ 40lib_LTLIBRARIES = \
41 libgnunetregexblock.la \
42 libgnunetregex.la 41 libgnunetregex.la
43 42
44libgnunetregexblock_la_SOURCES = \
45 regex_block_lib.c regex_block_lib.h
46libgnunetregexblock_la_LIBADD = \
47 $(top_builddir)/src/lib/util/libgnunetutil.la \
48 $(XLIB) \
49 $(LTLIBINTL)
50libgnunetregexblock_la_LDFLAGS = \
51 $(GN_LIB_LDFLAGS) \
52 -version-info 1:0:0
53
54
55libgnunetregex_la_SOURCES = \ 43libgnunetregex_la_SOURCES = \
56 regex_api_announce.c \ 44 regex_api_announce.c \
57 regex_api_search.c \ 45 regex_api_search.c \
@@ -64,20 +52,6 @@ libgnunetregex_la_LDFLAGS = \
64 -version-info 3:1:0 52 -version-info 3:1:0
65 53
66 54
67plugin_LTLIBRARIES = \
68 libgnunet_plugin_block_regex.la
69
70libgnunet_plugin_block_regex_la_SOURCES = \
71 plugin_block_regex.c
72libgnunet_plugin_block_regex_la_LIBADD = \
73 libgnunetregexblock.la \
74 $(top_builddir)/src/lib/block/libgnunetblock.la \
75 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \
76 $(top_builddir)/src/lib/util/libgnunetutil.la
77libgnunet_plugin_block_regex_la_LDFLAGS = \
78 $(GN_LIBINTL) \
79 $(GN_PLUGIN_LDFLAGS)
80
81# FIXME we phased out mysql. If we want to keep, needs rewrite 55# FIXME we phased out mysql. If we want to keep, needs rewrite
82#if HAVE_MYSQL 56#if HAVE_MYSQL
83#noinst_mysql_progs = \ 57#noinst_mysql_progs = \
@@ -100,7 +74,7 @@ perf_regex_SOURCES = \
100 $(REGEX_INTERNAL_TEST) perf-regex.c 74 $(REGEX_INTERNAL_TEST) perf-regex.c
101perf_regex_LDADD = -lm \ 75perf_regex_LDADD = -lm \
102 $(top_builddir)/src/service/dht/libgnunetdht.la \ 76 $(top_builddir)/src/service/dht/libgnunetdht.la \
103 libgnunetregexblock.la \ 77 $(top_builddir)/src/plugin/regex/libgnunetregexblock.la \
104 $(top_builddir)/src/lib/util/libgnunetutil.la \ 78 $(top_builddir)/src/lib/util/libgnunetutil.la \
105 $(top_builddir)/src/service/statistics/libgnunetstatistics.la 79 $(top_builddir)/src/service/statistics/libgnunetstatistics.la
106perf_regex_LDFLAGS = \ 80perf_regex_LDFLAGS = \
@@ -110,7 +84,7 @@ gnunet_daemon_regexprofiler_SOURCES = \
110 $(REGEX_INTERNAL_TEST) gnunet-daemon-regexprofiler.c 84 $(REGEX_INTERNAL_TEST) gnunet-daemon-regexprofiler.c
111gnunet_daemon_regexprofiler_LDADD = -lm \ 85gnunet_daemon_regexprofiler_LDADD = -lm \
112 $(top_builddir)/src/service/dht/libgnunetdht.la \ 86 $(top_builddir)/src/service/dht/libgnunetdht.la \
113 libgnunetregexblock.la \ 87 $(top_builddir)/src/plugin/regex/libgnunetregexblock.la \
114 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ 88 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
115 $(top_builddir)/src/lib/util/libgnunetutil.la 89 $(top_builddir)/src/lib/util/libgnunetutil.la
116gnunet_daemon_regexprofiler_LDFLAGS = \ 90gnunet_daemon_regexprofiler_LDFLAGS = \
@@ -133,7 +107,7 @@ test_regex_eval_api_SOURCES = \
133 $(REGEX_INTERNAL_TEST) test_regex_eval_api.c 107 $(REGEX_INTERNAL_TEST) test_regex_eval_api.c
134test_regex_eval_api_LDADD = -lm \ 108test_regex_eval_api_LDADD = -lm \
135 $(top_builddir)/src/service/dht/libgnunetdht.la \ 109 $(top_builddir)/src/service/dht/libgnunetdht.la \
136 libgnunetregexblock.la \ 110 $(top_builddir)/src/plugin/regex/libgnunetregexblock.la \
137 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ 111 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
138 $(top_builddir)/src/lib/util/libgnunetutil.la 112 $(top_builddir)/src/lib/util/libgnunetutil.la
139 113
@@ -154,7 +128,7 @@ test_regex_api_LDADD = -lm \
154test_regex_iterate_api_SOURCES = \ 128test_regex_iterate_api_SOURCES = \
155 $(REGEX_INTERNAL) test_regex_iterate_api.c 129 $(REGEX_INTERNAL) test_regex_iterate_api.c
156test_regex_iterate_api_LDADD = -lm \ 130test_regex_iterate_api_LDADD = -lm \
157 libgnunetregexblock.la \ 131 $(top_builddir)/src/plugin/regex/libgnunetregexblock.la \
158 $(top_builddir)/src/service/dht/libgnunetdht.la \ 132 $(top_builddir)/src/service/dht/libgnunetdht.la \
159 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ 133 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
160 $(top_builddir)/src/lib/util/libgnunetutil.la 134 $(top_builddir)/src/lib/util/libgnunetutil.la
@@ -163,7 +137,7 @@ test_regex_proofs_SOURCES = \
163 $(REGEX_INTERNAL_TEST) test_regex_proofs.c 137 $(REGEX_INTERNAL_TEST) test_regex_proofs.c
164test_regex_proofs_LDADD = -lm \ 138test_regex_proofs_LDADD = -lm \
165 $(top_builddir)/src/service/dht/libgnunetdht.la \ 139 $(top_builddir)/src/service/dht/libgnunetdht.la \
166 libgnunetregexblock.la \ 140 $(top_builddir)/src/plugin/regex/libgnunetregexblock.la \
167 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ 141 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
168 $(top_builddir)/src/lib/util/libgnunetutil.la 142 $(top_builddir)/src/lib/util/libgnunetutil.la
169 143
@@ -171,7 +145,7 @@ test_regex_graph_api_SOURCES = \
171 $(REGEX_INTERNAL_TEST) test_regex_graph_api.c 145 $(REGEX_INTERNAL_TEST) test_regex_graph_api.c
172test_regex_graph_api_LDADD = -lm \ 146test_regex_graph_api_LDADD = -lm \
173 $(top_builddir)/src/service/dht/libgnunetdht.la \ 147 $(top_builddir)/src/service/dht/libgnunetdht.la \
174 libgnunetregexblock.la \ 148 $(top_builddir)/src/plugin/regex/libgnunetregexblock.la \
175 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ 149 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
176 $(top_builddir)/src/lib/util/libgnunetutil.la 150 $(top_builddir)/src/lib/util/libgnunetutil.la
177 151
diff --git a/src/service/regex/meson.build b/src/service/regex/meson.build
index da607ca47..3c25a5148 100644
--- a/src/service/regex/meson.build
+++ b/src/service/regex/meson.build
@@ -1,7 +1,5 @@
1libgnunetregex_src = ['regex_api_announce.c', 1libgnunetregex_src = ['regex_api_announce.c',
2 'regex_api_search.c'] 2 'regex_api_search.c']
3libgnunetregexblock_src = ['regex_block_lib.c']
4
5gnunetserviceregex_src = ['gnunet-service-regex.c', 3gnunetserviceregex_src = ['gnunet-service-regex.c',
6 'regex_internal_dht.c', 4 'regex_internal_dht.c',
7 'regex_internal.c'] 5 'regex_internal.c']
@@ -14,7 +12,7 @@ configure_file(input : 'regex.conf.in',
14 12
15 13
16if get_option('monolith') 14if get_option('monolith')
17 foreach p : libgnunetregex_src + libgnunetregexblock_src + gnunetserviceregex_src 15 foreach p : libgnunetregex_src + gnunetserviceregex_src
18 gnunet_src += 'regex/' + p 16 gnunet_src += 'regex/' + p
19 endforeach 17 endforeach
20endif 18endif
@@ -30,26 +28,6 @@ libgnunetregex = library('gnunetregex',
30pkg.generate(libgnunetregex, url: 'https://www.gnunet.org', 28pkg.generate(libgnunetregex, url: 'https://www.gnunet.org',
31 description : 'Provides API for accessing the regex service') 29 description : 'Provides API for accessing the regex service')
32libgnunetregex_dep = declare_dependency(link_with : libgnunetregex) 30libgnunetregex_dep = declare_dependency(link_with : libgnunetregex)
33libgnunetregexblock = library('gnunetregexblock',
34 libgnunetregexblock_src,
35 soversion: '1',
36 version: '1.0.0',
37 dependencies: libgnunetutil_dep,
38 include_directories: [incdir, configuration_inc],
39 install: true,
40 install_dir: get_option('libdir'))
41libgnunetregexblock_dep = declare_dependency(link_with : libgnunetregexblock)
42
43shared_module('gnunet_plugin_block_regex',
44 ['plugin_block_regex.c'],
45 dependencies: [libgnunetutil_dep,
46 libgnunetregexblock_dep,
47 libgnunetblock_dep,
48 libgnunetblockgroup_dep],
49 include_directories: [incdir, configuration_inc],
50 install:true,
51 install_dir: get_option('libdir')/'gnunet')
52
53 31
54executable ('gnunet-service-regex', 32executable ('gnunet-service-regex',
55 gnunetserviceregex_src, 33 gnunetserviceregex_src,
diff --git a/src/service/regex/plugin_block_regex.c b/src/service/regex/plugin_block_regex.c
deleted file mode 100644
index 5f23a32df..000000000
--- a/src/service/regex/plugin_block_regex.c
+++ /dev/null
@@ -1,380 +0,0 @@
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/service/regex/regex_block_lib.c b/src/service/regex/regex_block_lib.c
deleted file mode 100644
index 048d6d743..000000000
--- a/src/service/regex/regex_block_lib.c
+++ /dev/null
@@ -1,431 +0,0 @@
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/service/regex/regex_block_lib.h b/src/service/regex/regex_block_lib.h
deleted file mode 100644
index 11029b9af..000000000
--- a/src/service/regex/regex_block_lib.h
+++ /dev/null
@@ -1,193 +0,0 @@
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/service/regex/regex_internal_dht.c b/src/service/regex/regex_internal_dht.c
index bda979edb..9850f106f 100644
--- a/src/service/regex/regex_internal_dht.c
+++ b/src/service/regex/regex_internal_dht.c
@@ -25,7 +25,6 @@
25 */ 25 */
26#include "platform.h" 26#include "platform.h"
27#include "regex_internal_lib.h" 27#include "regex_internal_lib.h"
28#include "regex_block_lib.h"
29#include "gnunet_dht_service.h" 28#include "gnunet_dht_service.h"
30#include "gnunet_statistics_service.h" 29#include "gnunet_statistics_service.h"
31#include "gnunet_constants.h" 30#include "gnunet_constants.h"
diff --git a/src/service/regex/regex_internal_lib.h b/src/service/regex/regex_internal_lib.h
index 9baf84995..bfa3fc97d 100644
--- a/src/service/regex/regex_internal_lib.h
+++ b/src/service/regex/regex_internal_lib.h
@@ -29,7 +29,7 @@
29#include "gnunet_util_lib.h" 29#include "gnunet_util_lib.h"
30#include "gnunet_dht_service.h" 30#include "gnunet_dht_service.h"
31#include "gnunet_statistics_service.h" 31#include "gnunet_statistics_service.h"
32#include "regex_block_lib.h" 32#include "../../plugin/regex/regex_block_lib.h"
33 33
34#ifdef __cplusplus 34#ifdef __cplusplus
35extern "C" 35extern "C"
diff --git a/src/service/rest/Makefile.am b/src/service/rest/Makefile.am
index 401be36f4..60769e366 100644
--- a/src/service/rest/Makefile.am
+++ b/src/service/rest/Makefile.am
@@ -24,10 +24,35 @@ libexec_PROGRAMS = \
24EXTRA_DIST = \ 24EXTRA_DIST = \
25 rest.conf 25 rest.conf
26 26
27if HAVE_JOSE
28OPENID_PLUGIN = \
29 openid_plugin.c \
30 oidc_helper.c
31OPENID_JOSE_LIBS = -ljose
32endif
33
27gnunet_rest_server_SOURCES = \ 34gnunet_rest_server_SOURCES = \
28 gnunet-rest-server.c 35 gnunet-rest-server.c \
36 config_plugin.c \
37 copying_plugin.c \
38 identity_plugin.c \
39 namestore_plugin.c \
40 gns_plugin.c \
41 $(OPENID_PLUGIN) \
42 reclaim_plugin.c json_reclaim.c
29gnunet_rest_server_LDADD = \ 43gnunet_rest_server_LDADD = \
30 $(top_builddir)/src/lib/util/libgnunetutil.la \ 44 $(top_builddir)/src/lib/util/libgnunetutil.la \
45 $(top_builddir)/src/lib/json/libgnunetjson.la \
46 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
47 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecordjson.la \
48 $(top_builddir)/src/service/identity/libgnunetidentity.la \
49 $(top_builddir)/src/service/namestore/libgnunetnamestore.la \
50 $(top_builddir)/src/service/gns/libgnunetgns.la \
51 $(top_builddir)/src/service/reclaim/libgnunetreclaim.la \
52 libgnunetrest.la \
53 $(OPENID_JOSE_LIBS) \
54 -ljansson \
55 $(LIBGCRYPT_LIBS) \
31 $(GN_LIBINTL) $(MHD_LIBS) 56 $(GN_LIBINTL) $(MHD_LIBS)
32gnunet_rest_server_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS) 57gnunet_rest_server_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS)
33 58
diff --git a/src/service/rest/config_plugin.c b/src/service/rest/config_plugin.c
new file mode 100644
index 000000000..fd0ac011a
--- /dev/null
+++ b/src/service/rest/config_plugin.c
@@ -0,0 +1,438 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2018 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 * @file gns/plugin_rest_config.c
23 * @brief REST plugin for configuration
24 *
25 */
26
27#include "platform.h"
28#include "gnunet_rest_plugin.h"
29#include <gnunet_rest_lib.h>
30#include <gnunet_util_lib.h>
31#include <jansson.h>
32
33#define GNUNET_REST_API_NS_CONFIG "/config"
34
35/**
36 * @brief struct returned by the initialization function of the plugin
37 */
38struct Plugin
39{
40 const struct GNUNET_CONFIGURATION_Handle *cfg;
41};
42
43const struct GNUNET_CONFIGURATION_Handle *config_cfg;
44
45struct RequestHandle
46{
47 /**
48 * DLL
49 */
50 struct RequestHandle *next;
51
52 /**
53 * DLL
54 */
55 struct RequestHandle *prev;
56
57 /**
58 * Handle to rest request
59 */
60 struct GNUNET_REST_RequestHandle *rest_handle;
61
62 /**
63 * The plugin result processor
64 */
65 GNUNET_REST_ResultProcessor proc;
66
67 /**
68 * The closure of the result processor
69 */
70 void *proc_cls;
71
72 /**
73 * HTTP response code
74 */
75 int response_code;
76
77 /**
78 * The URL
79 */
80 char *url;
81
82};
83
84/**
85 * DLL
86 */
87static struct RequestHandle *requests_head;
88
89/**
90 * DLL
91 */
92static struct RequestHandle *requests_tail;
93
94
95/**
96 * Cleanup request handle.
97 *
98 * @param handle Handle to clean up
99 */
100static void
101cleanup_handle (struct RequestHandle *handle)
102{
103 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
104 if (NULL != handle->url)
105 GNUNET_free (handle->url);
106 GNUNET_CONTAINER_DLL_remove (requests_head,
107 requests_tail,
108 handle);
109 GNUNET_free (handle);
110}
111
112
113/**
114 * Task run on shutdown. Cleans up everything.
115 *
116 * @param cls unused
117 */
118static void
119do_error (void *cls)
120{
121 struct RequestHandle *handle = cls;
122 struct MHD_Response *resp;
123
124 resp = GNUNET_REST_create_response (NULL);
125 handle->proc (handle->proc_cls, resp, handle->response_code);
126 cleanup_handle (handle);
127}
128
129
130static void
131add_sections (void *cls,
132 const char *section,
133 const char *option,
134 const char *value)
135{
136 json_t *sections_obj = cls;
137 json_t *sec_obj;
138
139 sec_obj = json_object_get (sections_obj, section);
140 if (NULL != sec_obj)
141 {
142 json_object_set_new (sec_obj, option, json_string (value));
143 return;
144 }
145 sec_obj = json_object ();
146 json_object_set_new (sec_obj, option, json_string (value));
147 json_object_set_new (sections_obj, section, sec_obj);
148}
149
150
151static void
152add_section_contents (void *cls,
153 const char *section,
154 const char *option,
155 const char *value)
156{
157 json_t *section_obj = cls;
158
159 json_object_set_new (section_obj, option, json_string (value));
160}
161
162
163/**
164 * Handle rest request
165 *
166 * @param handle the lookup handle
167 */
168static void
169get_cont (struct GNUNET_REST_RequestHandle *con_handle,
170 const char *url,
171 void *cls)
172{
173 struct MHD_Response *resp;
174 struct RequestHandle *handle = cls;
175 const char *section;
176 char *response;
177 json_t *result;
178
179 if (strlen (GNUNET_REST_API_NS_CONFIG) > strlen (handle->url))
180 {
181 handle->response_code = MHD_HTTP_BAD_REQUEST;
182 GNUNET_SCHEDULER_add_now (&do_error, handle);
183 return;
184 }
185 if (strlen (GNUNET_REST_API_NS_CONFIG) == strlen (handle->url))
186 {
187 result = json_object ();
188 GNUNET_CONFIGURATION_iterate (config_cfg, &add_sections, result);
189 }
190 else
191 {
192 result = json_object ();
193 section = &handle->url[strlen (GNUNET_REST_API_NS_CONFIG) + 1];
194 GNUNET_CONFIGURATION_iterate_section_values (config_cfg,
195 section,
196 &add_section_contents,
197 result);
198 }
199 response = json_dumps (result, 0);
200 resp = GNUNET_REST_create_response (response);
201 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
202 "Content-Type",
203 "application/json"));
204 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
205 cleanup_handle (handle);
206 GNUNET_free (response);
207 json_decref (result);
208}
209
210
211struct GNUNET_CONFIGURATION_Handle *
212set_value (struct GNUNET_CONFIGURATION_Handle *config,
213 const char *section,
214 const char *option,
215 json_t *value)
216{
217 if (json_is_string (value))
218 GNUNET_CONFIGURATION_set_value_string (config, section, option,
219 json_string_value (value));
220 else if (json_is_number (value))
221 GNUNET_CONFIGURATION_set_value_number (config, section, option,
222 json_integer_value (value));
223 else if (json_is_null (value))
224 GNUNET_CONFIGURATION_set_value_string (config, section, option, NULL);
225 else if (json_is_true (value))
226 GNUNET_CONFIGURATION_set_value_string (config, section, option, "yes");
227 else if (json_is_false (value))
228 GNUNET_CONFIGURATION_set_value_string (config, section, option, "no");
229 else
230 return NULL;
231 return config; // for error handling (0 -> success, 1 -> error)
232}
233
234
235/**
236 * Handle REST POST request
237 *
238 * @param handle the lookup handle
239 */
240static void
241set_cont (struct GNUNET_REST_RequestHandle *con_handle,
242 const char *url,
243 void *cls)
244{
245 struct RequestHandle *handle = cls;
246 char term_data[handle->rest_handle->data_size + 1];
247 struct GNUNET_CONFIGURATION_Handle *out = GNUNET_CONFIGURATION_dup (config_cfg);
248
249 json_error_t err;
250 json_t *data_json;
251 const char *section;
252 const char *option;
253 json_t *sec_obj;
254 json_t *value;
255 char *cfg_fn;
256
257 // invalid url
258 if (strlen (GNUNET_REST_API_NS_CONFIG) > strlen (handle->url))
259 {
260 handle->response_code = MHD_HTTP_BAD_REQUEST;
261 GNUNET_SCHEDULER_add_now (&do_error, handle);
262 return;
263 }
264
265 // extract data from handle
266 term_data[handle->rest_handle->data_size] = '\0';
267 GNUNET_memcpy (term_data,
268 handle->rest_handle->data,
269 handle->rest_handle->data_size);
270 data_json = json_loads (term_data, JSON_DECODE_ANY, &err);
271
272 if (NULL == data_json)
273 {
274 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
275 "Unable to parse JSON Object from %s\n",
276 term_data);
277 GNUNET_SCHEDULER_add_now (&do_error, handle);
278 return;
279 }
280
281 // POST /config => {<section> : {<option> : <value>}}
282 if (strlen (GNUNET_REST_API_NS_CONFIG) == strlen (handle->url)) // POST /config
283 {
284 // iterate over sections
285 json_object_foreach (data_json, section, sec_obj)
286 {
287 // iterate over options
288 json_object_foreach (sec_obj, option, value)
289 {
290 out = set_value (out, section, option, value);
291 if (NULL == out)
292 {
293 handle->response_code = MHD_HTTP_BAD_REQUEST;
294 GNUNET_SCHEDULER_add_now (&do_error, handle);
295 json_decref (data_json);
296 return;
297 }
298 }
299 }
300 }
301 else // POST /config/<section> => {<option> : <value>}
302 {
303 // extract the "<section>" part from the url
304 section = &handle->url[strlen (GNUNET_REST_API_NS_CONFIG) + 1];
305 // iterate over options
306 json_object_foreach (data_json, option, value)
307 {
308 out = set_value (out, section, option, value);
309 if (NULL == out)
310 {
311 handle->response_code = MHD_HTTP_BAD_REQUEST;
312 GNUNET_SCHEDULER_add_now (&do_error, handle);
313 json_decref (data_json);
314 return;
315 }
316 }
317 }
318 json_decref (data_json);
319
320
321 // get cfg file path
322 cfg_fn = NULL;
323 const char *xdg = getenv ("XDG_CONFIG_HOME");
324 if (NULL != xdg)
325 GNUNET_asprintf (&cfg_fn,
326 "%s%s%s",
327 xdg,
328 DIR_SEPARATOR_STR,
329 GNUNET_OS_project_data_get ()->config_file);
330 else
331 cfg_fn = GNUNET_strdup (GNUNET_OS_project_data_get ()->user_config_file);
332
333 GNUNET_CONFIGURATION_write (out, cfg_fn);
334 config_cfg = out;
335 handle->proc (handle->proc_cls,
336 GNUNET_REST_create_response (NULL),
337 MHD_HTTP_OK);
338 GNUNET_free (cfg_fn);
339 cleanup_handle (handle);
340}
341
342
343/**
344 * Handle rest request
345 *
346 * @param handle the lookup handle
347 */
348static void
349options_cont (struct GNUNET_REST_RequestHandle *con_handle,
350 const char *url,
351 void *cls)
352{
353 struct MHD_Response *resp;
354 struct RequestHandle *handle = cls;
355
356 resp = GNUNET_REST_create_response (NULL);
357 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
358 "Access-Control-Allow-Methods",
359 MHD_HTTP_METHOD_GET));
360 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
361 cleanup_handle (handle);
362}
363
364
365enum GNUNET_GenericReturnValue
366REST_config_process_request (void *plugin,
367 struct GNUNET_REST_RequestHandle *conndata_handle,
368 GNUNET_REST_ResultProcessor proc,
369 void *proc_cls)
370{
371 static const struct GNUNET_REST_RequestHandler handlers[] = {
372 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_CONFIG, &get_cont },
373 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_CONFIG, &set_cont },
374 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_CONFIG, &options_cont },
375 GNUNET_REST_HANDLER_END
376 };
377 (void) plugin;
378 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
379 struct GNUNET_REST_RequestHandlerError err;
380
381 handle->proc_cls = proc_cls;
382 handle->proc = proc;
383 handle->rest_handle = conndata_handle;
384 handle->url = GNUNET_strdup (conndata_handle->url);
385 if (handle->url[strlen (handle->url) - 1] == '/')
386 handle->url[strlen (handle->url) - 1] = '\0';
387 GNUNET_CONTAINER_DLL_insert (requests_head,
388 requests_tail,
389 handle);
390 if (GNUNET_NO ==
391 GNUNET_REST_handle_request (conndata_handle, handlers, &err, handle))
392 {
393 cleanup_handle (handle);
394 return GNUNET_NO;
395 }
396 return GNUNET_YES;
397}
398
399void
400REST_config_done (struct GNUNET_REST_Plugin *api)
401{
402 struct Plugin *plugin;
403
404 while (NULL != requests_head)
405 cleanup_handle (requests_head);
406 plugin = api->cls;
407 plugin->cfg = NULL;
408 GNUNET_free (api);
409 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CONFIG REST plugin is finished\n");
410}
411
412
413/**
414 * Entry point for the plugin.
415 *
416 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
417 * @return NULL on error, otherwise the plugin context
418 */
419void *
420REST_config_init (const struct GNUNET_CONFIGURATION_Handle *c)
421{
422 static struct Plugin plugin;
423
424 config_cfg = c;
425 struct GNUNET_REST_Plugin *api;
426
427 memset (&plugin, 0, sizeof(struct Plugin));
428 plugin.cfg = c;
429 api = GNUNET_new (struct GNUNET_REST_Plugin);
430 api->cls = &plugin;
431 api->name = GNUNET_REST_API_NS_CONFIG;
432 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("CONFIG REST API initialized\n"));
433 return api;
434}
435
436
437
438/* end of plugin_rest_config.c */
diff --git a/src/service/rest/config_plugin.h b/src/service/rest/config_plugin.h
new file mode 100644
index 000000000..c959a94f2
--- /dev/null
+++ b/src/service/rest/config_plugin.h
@@ -0,0 +1,37 @@
1#include "platform.h"
2#include "gnunet_util_lib.h"
3#include "gnunet_rest_plugin.h"
4
5/**
6 * Function processing the REST call
7 *
8 * @param method HTTP method
9 * @param url URL of the HTTP request
10 * @param data body of the HTTP request (optional)
11 * @param data_size length of the body
12 * @param proc callback function for the result
13 * @param proc_cls closure for @a proc
14 * @return #GNUNET_OK if request accepted
15 */
16enum GNUNET_GenericReturnValue
17REST_config_process_request (void *plugin,
18 struct GNUNET_REST_RequestHandle *conndata_handle,
19 GNUNET_REST_ResultProcessor proc,
20 void *proc_cls);
21
22/**
23 * Entry point for the plugin.
24 *
25 */
26void *
27REST_config_init (const struct GNUNET_CONFIGURATION_Handle *cfg);
28
29/**
30 * Exit point from the plugin.
31 *
32 * @param cls the plugin context (as returned by "init")
33 * @return always NULL
34 */
35void
36REST_config_done (struct GNUNET_REST_Plugin *api);
37
diff --git a/src/service/rest/copying_plugin.c b/src/service/rest/copying_plugin.c
new file mode 100644
index 000000000..d907f6729
--- /dev/null
+++ b/src/service/rest/copying_plugin.c
@@ -0,0 +1,233 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2018 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 * @file gns/plugin_rest_copying.c
23 * @brief REST plugin that serves licensing information.
24 *
25 */
26
27#include "platform.h"
28#include "gnunet_rest_plugin.h"
29#include <gnunet_rest_lib.h>
30
31#define GNUNET_REST_API_NS_COPYING "/copying"
32
33#define GNUNET_REST_COPYING_TEXT \
34 "GNU Affero General Public License version 3 or later. See also: <http://www.gnu.org/licenses/>"
35
36/**
37 * @brief struct returned by the initialization function of the plugin
38 */
39struct Plugin
40{
41 const struct GNUNET_CONFIGURATION_Handle *cfg;
42};
43
44struct RequestHandle
45{
46 /**
47 * DLL
48 */
49 struct RequestHandle *next;
50
51 /**
52 * DLL
53 */
54 struct RequestHandle *prev;
55
56 /**
57 * Handle to rest request
58 */
59 struct GNUNET_REST_RequestHandle *rest_handle;
60
61 /**
62 * The plugin result processor
63 */
64 GNUNET_REST_ResultProcessor proc;
65
66 /**
67 * The closure of the result processor
68 */
69 void *proc_cls;
70
71 /**
72 * HTTP response code
73 */
74 int response_code;
75};
76
77/**
78 * DLL
79 */
80static struct RequestHandle *requests_head;
81
82/**
83 * DLL
84 */
85static struct RequestHandle *requests_tail;
86
87/**
88 * Cleanup request handle.
89 *
90 * @param handle Handle to clean up
91 */
92static void
93cleanup_handle (struct RequestHandle *handle)
94{
95 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
96 "Cleaning up\n");
97 GNUNET_CONTAINER_DLL_remove (requests_head,
98 requests_tail,
99 handle);
100 GNUNET_free (handle);
101}
102
103
104/**
105 * Handle rest request
106 *
107 * @param handle the lookup handle
108 */
109static void
110get_cont (struct GNUNET_REST_RequestHandle *con_handle,
111 const char*url,
112 void *cls)
113{
114 struct MHD_Response *resp;
115 struct RequestHandle *handle = cls;
116
117 resp = GNUNET_REST_create_response (GNUNET_REST_COPYING_TEXT);
118 handle->proc (handle->proc_cls,
119 resp,
120 MHD_HTTP_OK);
121 cleanup_handle (handle);
122}
123
124
125/**
126 * Handle rest request
127 *
128 * @param handle the lookup handle
129 */
130static void
131options_cont (struct GNUNET_REST_RequestHandle *con_handle,
132 const char*url,
133 void *cls)
134{
135 struct MHD_Response *resp;
136 struct RequestHandle *handle = cls;
137
138 resp = GNUNET_REST_create_response (NULL);
139 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
140 "Access-Control-Allow-Methods",
141 MHD_HTTP_METHOD_GET));
142 handle->proc (handle->proc_cls,
143 resp,
144 MHD_HTTP_OK);
145 cleanup_handle (handle);
146}
147
148
149/**
150 * Function processing the REST call
151 *
152 * @param method HTTP method
153 * @param url URL of the HTTP request
154 * @param data body of the HTTP request (optional)
155 * @param data_size length of the body
156 * @param proc callback function for the result
157 * @param proc_cls closure for @a proc
158 * @return #GNUNET_OK if request accepted
159 */
160enum GNUNET_GenericReturnValue
161REST_copying_process_request (void *plugin,
162 struct GNUNET_REST_RequestHandle *conndata_handle,
163 GNUNET_REST_ResultProcessor proc,
164 void *proc_cls)
165{
166 static const struct GNUNET_REST_RequestHandler handlers[] = {
167 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_COPYING, &get_cont },
168 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_COPYING, &options_cont },
169 GNUNET_REST_HANDLER_END
170 };
171 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
172 struct GNUNET_REST_RequestHandlerError err;
173
174 handle->proc_cls = proc_cls;
175 handle->proc = proc;
176 handle->rest_handle = conndata_handle;
177 GNUNET_CONTAINER_DLL_insert (requests_head,
178 requests_tail,
179 handle);
180 return GNUNET_REST_handle_request (conndata_handle,
181 handlers,
182 &err,
183 handle);
184}
185
186
187/**
188 * Entry point for the plugin.
189 *
190 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
191 * @return NULL on error, otherwise the plugin context
192 */
193void*
194REST_copying_init (const struct GNUNET_CONFIGURATION_Handle *c)
195{
196 static struct Plugin plugin;
197
198 struct GNUNET_REST_Plugin *api;
199
200 if (NULL != plugin.cfg)
201 return NULL; /* can only initialize once! */
202 memset (&plugin, 0, sizeof(struct Plugin));
203 plugin.cfg = c;
204 api = GNUNET_new (struct GNUNET_REST_Plugin);
205 api->cls = &plugin;
206 api->name = GNUNET_REST_API_NS_COPYING;
207 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
208 _ ("COPYING REST API initialized\n"));
209 return api;
210}
211
212
213/**
214 * Exit point from the plugin.
215 *
216 * @param cls the plugin context (as returned by "init")
217 * @return always NULL
218 */
219void
220REST_copying_done (struct GNUNET_REST_Plugin *api)
221{
222 struct Plugin *plugin = api->cls;
223
224 while (NULL != requests_head)
225 cleanup_handle (requests_head);
226 plugin->cfg = NULL;
227 GNUNET_free (api);
228 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
229 "COPYING REST plugin is finished\n");
230}
231
232
233/* end of plugin_rest_copying.c */
diff --git a/src/service/rest/copying_plugin.h b/src/service/rest/copying_plugin.h
new file mode 100644
index 000000000..4ba1a2e36
--- /dev/null
+++ b/src/service/rest/copying_plugin.h
@@ -0,0 +1,36 @@
1#include "gnunet_rest_plugin.h"
2/**
3 * Function processing the REST call
4 *
5 * @param method HTTP method
6 * @param url URL of the HTTP request
7 * @param data body of the HTTP request (optional)
8 * @param data_size length of the body
9 * @param proc callback function for the result
10 * @param proc_cls closure for @a proc
11 * @return #GNUNET_OK if request accepted
12 */
13enum GNUNET_GenericReturnValue
14REST_copying_process_request (void *plugin,
15 struct GNUNET_REST_RequestHandle *conndata_handle,
16 GNUNET_REST_ResultProcessor proc,
17 void *proc_cls);
18
19/**
20 * Entry point for the plugin.
21 *
22 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
23 * @return NULL on error, otherwise the plugin context
24 */
25void*
26REST_copying_init (const struct GNUNET_CONFIGURATION_Handle *c);
27
28
29/**
30 * Exit point from the plugin.
31 *
32 * @param cls the plugin context (as returned by "init")
33 * @return always NULL
34 */
35void
36REST_copying_done (struct GNUNET_REST_Plugin *api);
diff --git a/src/service/rest/gns_plugin.c b/src/service/rest/gns_plugin.c
new file mode 100644
index 000000000..0ea89d0cd
--- /dev/null
+++ b/src/service/rest/gns_plugin.c
@@ -0,0 +1,486 @@
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 Philippe Buschmann
22 * @file gns/plugin_rest_gns.c
23 * @brief GNUnet Gns REST plugin
24 */
25
26#include "platform.h"
27#include "gnunet_rest_plugin.h"
28#include "gnunet_rest_lib.h"
29#include "gnunet_json_lib.h"
30#include "gnunet_gnsrecord_lib.h"
31#include "gnunet_gnsrecord_json_lib.h"
32#include "gnunet_gns_service.h"
33#include "microhttpd.h"
34#include <jansson.h>
35
36/**
37 * Rest API GNS Namespace
38 */
39#define GNUNET_REST_API_NS_GNS "/gns"
40
41/**
42 * Rest API GNS Parameter record_type
43 */
44#define GNUNET_REST_GNS_PARAM_RECORD_TYPE "record_type"
45
46/**
47 * Rest API GNS ERROR Unknown Error
48 */
49#define GNUNET_REST_GNS_ERROR_UNKNOWN "Unknown Error"
50
51/**
52 * Rest API GNS ERROR Record not found
53 */
54#define GNUNET_REST_GNS_NOT_FOUND "Record not found"
55
56/**
57 * The configuration handle
58 */
59const struct GNUNET_CONFIGURATION_Handle *gns_cfg;
60
61/**
62 * HTTP methods allows for this plugin
63 */
64static char *allow_methods;
65
66/**
67 * Connection to GNS
68 */
69static struct GNUNET_GNS_Handle *gns;
70
71/**
72 * @brief struct returned by the initialization function of the plugin
73 */
74struct Plugin
75{
76 const struct GNUNET_CONFIGURATION_Handle *cfg;
77};
78
79/**
80 * The request handle
81 */
82struct RequestHandle
83{
84 /**
85 * DLL
86 */
87 struct RequestHandle *next;
88
89 /**
90 * DLL
91 */
92 struct RequestHandle *prev;
93
94 /**
95 * Active GNS lookup
96 */
97 struct GNUNET_GNS_LookupWithTldRequest *gns_lookup;
98
99 /**
100 * Name to look up
101 */
102 char *name;
103
104 /**
105 * Record type to look up
106 */
107 int record_type;
108
109 /**
110 * Rest connection
111 */
112 struct GNUNET_REST_RequestHandle *rest_handle;
113
114 /**
115 * Desired timeout for the lookup (default is no timeout).
116 */
117 struct GNUNET_TIME_Relative timeout;
118
119 /**
120 * ID of a task associated with the resolution process.
121 */
122 struct GNUNET_SCHEDULER_Task *timeout_task;
123
124 /**
125 * The plugin result processor
126 */
127 GNUNET_REST_ResultProcessor proc;
128
129 /**
130 * The closure of the result processor
131 */
132 void *proc_cls;
133
134 /**
135 * The url
136 */
137 char *url;
138
139 /**
140 * Error response message
141 */
142 char *emsg;
143
144 /**
145 * Response code
146 */
147 int response_code;
148};
149
150/**
151 * DLL
152 */
153static struct RequestHandle *requests_head;
154
155/**
156 * DLL
157 */
158static struct RequestHandle *requests_tail;
159
160/**
161 * Cleanup lookup handle
162 * @param cls `struct RequestHandle` to clean up
163 */
164static void
165cleanup_handle (void *cls)
166{
167 struct RequestHandle *handle = cls;
168
169 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
170
171 if (NULL != handle->gns_lookup)
172 {
173 GNUNET_GNS_lookup_with_tld_cancel (handle->gns_lookup);
174 handle->gns_lookup = NULL;
175 }
176 if (NULL != handle->timeout_task)
177 {
178 GNUNET_SCHEDULER_cancel (handle->timeout_task);
179 handle->timeout_task = NULL;
180 }
181 if (NULL != handle->url)
182 GNUNET_free (handle->url);
183 if (NULL != handle->name)
184 GNUNET_free (handle->name);
185 if (NULL != handle->emsg)
186 GNUNET_free (handle->emsg);
187
188 GNUNET_CONTAINER_DLL_remove (requests_head,
189 requests_tail,
190 handle);
191 GNUNET_free (handle);
192}
193
194
195/**
196 * Task run on errors. Reports an error and cleans up everything.
197 *
198 * @param cls the `struct RequestHandle`
199 */
200static void
201do_error (void *cls)
202{
203 struct RequestHandle *handle = cls;
204 struct MHD_Response *resp;
205 json_t *json_error = json_object ();
206 char *response;
207
208 if (NULL != handle->timeout_task)
209 GNUNET_SCHEDULER_cancel (handle->timeout_task);
210 handle->timeout_task = NULL;
211 if (NULL == handle->emsg)
212 handle->emsg = GNUNET_strdup (GNUNET_REST_GNS_ERROR_UNKNOWN);
213
214 json_object_set_new (json_error, "error", json_string (handle->emsg));
215
216 if (0 == handle->response_code)
217 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
218 response = json_dumps (json_error, 0);
219 resp = GNUNET_REST_create_response (response);
220 MHD_add_response_header (resp, "Content-Type", "application/json");
221 handle->proc (handle->proc_cls, resp, handle->response_code);
222 json_decref (json_error);
223 GNUNET_free (response);
224 cleanup_handle (handle);
225}
226
227
228static void
229do_timeout (void *cls)
230{
231 struct RequestHandle *handle = cls;
232
233 handle->timeout_task = NULL;
234 handle->response_code = MHD_HTTP_REQUEST_TIMEOUT;
235 do_error (handle);
236}
237
238
239/**
240 * Iterator called on obtained result for a GNS lookup.
241 *
242 * @param cls closure with the object
243 * @param was_gns #GNUNET_NO if name was not a GNS name
244 * @param rd_count number of records in @a rd
245 * @param rd the records in reply
246 */
247static void
248handle_gns_response (void *cls,
249 int was_gns,
250 uint32_t rd_count,
251 const struct GNUNET_GNSRECORD_Data *rd)
252{
253 struct RequestHandle *handle = cls;
254 struct MHD_Response *resp;
255 json_t *result_obj;
256 char *result;
257
258 handle->gns_lookup = NULL;
259
260 if (GNUNET_NO == was_gns)
261 {
262 handle->response_code = MHD_HTTP_NOT_FOUND;
263 handle->emsg = GNUNET_strdup (GNUNET_REST_GNS_NOT_FOUND);
264 GNUNET_SCHEDULER_add_now (&do_error, handle);
265 return;
266 }
267
268 result_obj = GNUNET_GNSRECORD_JSON_from_gnsrecord (handle->name, rd,
269 rd_count);
270
271 result = json_dumps (result_obj, 0);
272 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result);
273 resp = GNUNET_REST_create_response (result);
274 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
275 "Content-Type",
276 "application/json"));
277 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
278 GNUNET_free (result);
279 json_decref (result_obj);
280 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
281}
282
283
284/**
285 * Handle gns GET request
286 *
287 * @param con_handle the connection handle
288 * @param url the url
289 * @param cls the RequestHandle
290 */
291void
292get_gns_cont (struct GNUNET_REST_RequestHandle *con_handle,
293 const char *url,
294 void *cls)
295{
296 struct RequestHandle *handle = cls;
297 struct GNUNET_HashCode key;
298 char *record_type;
299 char *name;
300
301 name = NULL;
302 handle->name = NULL;
303 if (strlen (GNUNET_REST_API_NS_GNS) < strlen (handle->url))
304 {
305 name = &handle->url[strlen (GNUNET_REST_API_NS_GNS) + 1];
306 }
307
308 if (NULL == name)
309 {
310 handle->response_code = MHD_HTTP_NOT_FOUND;
311 handle->emsg = GNUNET_strdup (GNUNET_REST_GNS_NOT_FOUND);
312 GNUNET_SCHEDULER_add_now (&do_error, handle);
313 return;
314 }
315 if (0 >= strlen (name))
316 {
317 handle->response_code = MHD_HTTP_NOT_FOUND;
318 handle->emsg = GNUNET_strdup (GNUNET_REST_GNS_NOT_FOUND);
319 GNUNET_SCHEDULER_add_now (&do_error, handle);
320 return;
321 }
322 handle->name = GNUNET_strdup (name);
323
324 handle->record_type = UINT32_MAX;
325 GNUNET_CRYPTO_hash (GNUNET_REST_GNS_PARAM_RECORD_TYPE,
326 strlen (GNUNET_REST_GNS_PARAM_RECORD_TYPE),
327 &key);
328 if (GNUNET_YES ==
329 GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, &key))
330 {
331 record_type =
332 GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, &key);
333 handle->record_type = GNUNET_GNSRECORD_typename_to_number (record_type);
334 }
335
336 if (UINT32_MAX == handle->record_type)
337 {
338 handle->record_type = GNUNET_GNSRECORD_TYPE_ANY;
339 }
340
341 handle->gns_lookup = GNUNET_GNS_lookup_with_tld (gns,
342 handle->name,
343 handle->record_type,
344 GNUNET_GNS_LO_DEFAULT,
345 &handle_gns_response,
346 handle);
347}
348
349
350/**
351 * Respond to OPTIONS request
352 *
353 * @param con_handle the connection handle
354 * @param url the url
355 * @param cls the RequestHandle
356 */
357static void
358options_cont (struct GNUNET_REST_RequestHandle *con_handle,
359 const char *url,
360 void *cls)
361{
362 struct MHD_Response *resp;
363 struct RequestHandle *handle = cls;
364
365 // independent of path return all options
366 resp = GNUNET_REST_create_response (NULL);
367 MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
368 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
369 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
370 return;
371}
372
373
374/**
375 * Function processing the REST call
376 *
377 * @param method HTTP method
378 * @param url URL of the HTTP request
379 * @param data body of the HTTP request (optional)
380 * @param data_size length of the body
381 * @param proc callback function for the result
382 * @param proc_cls closure for callback function
383 * @return GNUNET_OK if request accepted
384 */
385enum GNUNET_GenericReturnValue
386REST_gns_process_request (void *plugin,
387 struct GNUNET_REST_RequestHandle *rest_handle,
388 GNUNET_REST_ResultProcessor proc,
389 void *proc_cls)
390{
391 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
392 struct GNUNET_REST_RequestHandlerError err;
393 static const struct GNUNET_REST_RequestHandler handlers[] =
394 { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_GNS, &get_gns_cont },
395 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_GNS, &options_cont },
396 GNUNET_REST_HANDLER_END };
397
398 handle->response_code = 0;
399 handle->timeout =
400 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60);
401 handle->proc_cls = proc_cls;
402 handle->proc = proc;
403 handle->rest_handle = rest_handle;
404 handle->url = GNUNET_strdup (rest_handle->url);
405 handle->timeout_task =
406 GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
407 GNUNET_CONTAINER_DLL_insert (requests_head,
408 requests_tail,
409 handle);
410 if (handle->url[strlen (handle->url) - 1] == '/')
411 handle->url[strlen (handle->url) - 1] = '\0';
412 if (GNUNET_NO ==
413 GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
414 {
415 cleanup_handle (handle);
416 return GNUNET_NO;
417 }
418
419
420 return GNUNET_YES;
421}
422
423
424/**
425 * Entry point for the plugin.
426 *
427 * @param cls Config info
428 * @return NULL on error, otherwise the plugin context
429 */
430void *
431REST_gns_init (const struct GNUNET_CONFIGURATION_Handle *c)
432{
433 static struct Plugin plugin;
434 struct GNUNET_REST_Plugin *api;
435
436 gns_cfg = c;
437 memset (&plugin, 0, sizeof(struct Plugin));
438 plugin.cfg = gns_cfg;
439 api = GNUNET_new (struct GNUNET_REST_Plugin);
440 api->cls = &plugin;
441 api->name = GNUNET_REST_API_NS_GNS;
442 GNUNET_asprintf (&allow_methods,
443 "%s, %s, %s, %s, %s",
444 MHD_HTTP_METHOD_GET,
445 MHD_HTTP_METHOD_POST,
446 MHD_HTTP_METHOD_PUT,
447 MHD_HTTP_METHOD_DELETE,
448 MHD_HTTP_METHOD_OPTIONS);
449 gns = GNUNET_GNS_connect (gns_cfg);
450
451 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Gns REST API initialized\n"));
452 return api;
453}
454
455
456/**
457 * Exit point from the plugin.
458 *
459 * @param cls the plugin context (as returned by "init")
460 * @return always NULL
461 */
462void *
463REST_gns_done (void *cls)
464{
465 struct GNUNET_REST_Plugin *api = cls;
466 struct RequestHandle *request;
467 struct Plugin *plugin;
468
469 while (NULL != (request = requests_head))
470 do_error (request);
471
472 if (NULL != gns)
473 GNUNET_GNS_disconnect (gns);
474
475 plugin = api->cls;
476
477 plugin->cfg = NULL;
478
479 GNUNET_free (allow_methods);
480 GNUNET_free (api);
481 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Gns REST plugin is finished\n");
482 return NULL;
483}
484
485
486/* end of plugin_rest_gns.c */
diff --git a/src/service/rest/gns_plugin.h b/src/service/rest/gns_plugin.h
new file mode 100644
index 000000000..13878c139
--- /dev/null
+++ b/src/service/rest/gns_plugin.h
@@ -0,0 +1,36 @@
1#include "gnunet_rest_plugin.h"
2/**
3 * Function processing the REST call
4 *
5 * @param method HTTP method
6 * @param url URL of the HTTP request
7 * @param data body of the HTTP request (optional)
8 * @param data_size length of the body
9 * @param proc callback function for the result
10 * @param proc_cls closure for @a proc
11 * @return #GNUNET_OK if request accepted
12 */
13enum GNUNET_GenericReturnValue
14REST_gns_process_request (void *plugin,
15 struct GNUNET_REST_RequestHandle *conndata_handle,
16 GNUNET_REST_ResultProcessor proc,
17 void *proc_cls);
18
19/**
20 * Entry point for the plugin.
21 *
22 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
23 * @return NULL on error, otherwise the plugin context
24 */
25void*
26REST_gns_init (const struct GNUNET_CONFIGURATION_Handle *c);
27
28
29/**
30 * Exit point from the plugin.
31 *
32 * @param cls the plugin context (as returned by "init")
33 * @return always NULL
34 */
35void
36REST_gns_done (struct GNUNET_REST_Plugin *api);
diff --git a/src/service/rest/gnunet-rest-server.c b/src/service/rest/gnunet-rest-server.c
index 5163c2271..ce81704ae 100644
--- a/src/service/rest/gnunet-rest-server.c
+++ b/src/service/rest/gnunet-rest-server.c
@@ -29,6 +29,16 @@
29#include "gnunet_rest_plugin.h" 29#include "gnunet_rest_plugin.h"
30#include "gnunet_mhd_compat.h" 30#include "gnunet_mhd_compat.h"
31 31
32#include "config_plugin.h"
33#include "copying_plugin.h"
34#include "identity_plugin.h"
35#include "namestore_plugin.h"
36#include "gns_plugin.h"
37#ifdef HAVE_JOSE
38#include "openid_plugin.h"
39#endif
40#include "reclaim_plugin.h"
41
32/** 42/**
33 * Default Socks5 listen port. 43 * Default Socks5 listen port.
34 */ 44 */
@@ -54,7 +64,7 @@
54 * After how long do we clean up unused MHD SSL/TLS instances? 64 * After how long do we clean up unused MHD SSL/TLS instances?
55 */ 65 */
56#define MHD_CACHE_TIMEOUT \ 66#define MHD_CACHE_TIMEOUT \
57 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) 67 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
58 68
59#define GN_REST_STATE_INIT 0 69#define GN_REST_STATE_INIT 0
60#define GN_REST_STATE_PROCESSING 1 70#define GN_REST_STATE_PROCESSING 1
@@ -178,7 +188,12 @@ struct PluginListEntry
178 /** 188 /**
179 * The plugin 189 * The plugin
180 */ 190 */
181 struct GNUNET_REST_Plugin *plugin; 191 void *plugin;
192
193 /**
194 * Request function
195 */
196 GNUNET_REST_ProcessingFunction process_request;
182}; 197};
183 198
184/** 199/**
@@ -240,6 +255,21 @@ static struct AcceptedRequest *req_list_head;
240 */ 255 */
241static struct AcceptedRequest *req_list_tail; 256static struct AcceptedRequest *req_list_tail;
242 257
258
259/**
260 * plugins
261 */
262
263struct GNUNET_REST_Plugin *config_plugin;
264struct GNUNET_REST_Plugin *copying_plugin;
265struct GNUNET_REST_Plugin *identity_plugin;
266struct GNUNET_REST_Plugin *namestore_plugin;
267struct GNUNET_REST_Plugin *gns_plugin;
268#ifdef HAVE_JOSE
269struct GNUNET_REST_Plugin *openid_plugin;
270#endif
271struct GNUNET_REST_Plugin *reclaim_plugin;
272
243/* ************************* Global helpers ********************* */ 273/* ************************* Global helpers ********************* */
244 274
245 275
@@ -294,6 +324,7 @@ cleanup_url_map (void *cls, const struct GNUNET_HashCode *key, void *value)
294 return GNUNET_YES; 324 return GNUNET_YES;
295} 325}
296 326
327
297static void 328static void
298cleanup_handle (struct MhdConnectionHandle *handle) 329cleanup_handle (struct MhdConnectionHandle *handle)
299{ 330{
@@ -323,6 +354,7 @@ cleanup_handle (struct MhdConnectionHandle *handle)
323 GNUNET_free (handle); 354 GNUNET_free (handle);
324} 355}
325 356
357
326static void 358static void
327cleanup_ar (struct AcceptedRequest *ar) 359cleanup_ar (struct AcceptedRequest *ar)
328{ 360{
@@ -334,7 +366,8 @@ cleanup_ar (struct AcceptedRequest *ar)
334 { 366 {
335 GNUNET_NETWORK_socket_free_memory_only_ (ar->sock); 367 GNUNET_NETWORK_socket_free_memory_only_ (ar->sock);
336 } 368 }
337 else { 369 else
370 {
338 GNUNET_NETWORK_socket_close (ar->sock); 371 GNUNET_NETWORK_socket_close (ar->sock);
339 } 372 }
340 ar->sock = NULL; 373 ar->sock = NULL;
@@ -344,6 +377,7 @@ cleanup_ar (struct AcceptedRequest *ar)
344 GNUNET_free (ar); 377 GNUNET_free (ar);
345} 378}
346 379
380
347static int 381static int
348header_iterator (void *cls, 382header_iterator (void *cls,
349 enum MHD_ValueKind kind, 383 enum MHD_ValueKind kind,
@@ -561,9 +595,10 @@ create_response (void *cls,
561 con_handle->state = GN_REST_STATE_PROCESSING; 595 con_handle->state = GN_REST_STATE_PROCESSING;
562 for (ple = plugins_head; NULL != ple; ple = ple->next) 596 for (ple = plugins_head; NULL != ple; ple = ple->next)
563 { 597 {
564 if (GNUNET_YES == ple->plugin->process_request (rest_conndata_handle, 598 if (GNUNET_YES == ple->process_request (ple->plugin,
565 &plugin_callback, 599 rest_conndata_handle,
566 con_handle)) 600 &plugin_callback,
601 con_handle))
567 break; /* Request handled */ 602 break; /* Request handled */
568 } 603 }
569 if (NULL == ple) 604 if (NULL == ple)
@@ -603,8 +638,8 @@ create_response (void *cls,
603 strlen ("chrome-extension://")))) 638 strlen ("chrome-extension://"))))
604 { 639 {
605 GNUNET_assert (MHD_NO != MHD_add_response_header (con_handle->response, 640 GNUNET_assert (MHD_NO != MHD_add_response_header (con_handle->response,
606 MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, 641 MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
607 origin)); 642 origin));
608 } 643 }
609 } 644 }
610 if (NULL != allow_origins) 645 if (NULL != allow_origins)
@@ -615,9 +650,10 @@ create_response (void *cls,
615 { 650 {
616 if (0 == strncmp (allow_origin, origin, strlen (allow_origin))) 651 if (0 == strncmp (allow_origin, origin, strlen (allow_origin)))
617 { 652 {
618 GNUNET_assert (MHD_NO != MHD_add_response_header (con_handle->response, 653 GNUNET_assert (MHD_NO != MHD_add_response_header (
619 MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, 654 con_handle->response,
620 allow_origin)); 655 MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
656 allow_origin));
621 break; 657 break;
622 } 658 }
623 allow_origin = strtok (NULL, ","); 659 allow_origin = strtok (NULL, ",");
@@ -628,14 +664,14 @@ create_response (void *cls,
628 if (NULL != allow_credentials) 664 if (NULL != allow_credentials)
629 { 665 {
630 GNUNET_assert (MHD_NO != MHD_add_response_header (con_handle->response, 666 GNUNET_assert (MHD_NO != MHD_add_response_header (con_handle->response,
631 "Access-Control-Allow-Credentials", 667 "Access-Control-Allow-Credentials",
632 allow_credentials)); 668 allow_credentials));
633 } 669 }
634 if (NULL != allow_headers) 670 if (NULL != allow_headers)
635 { 671 {
636 GNUNET_assert (MHD_NO != MHD_add_response_header (con_handle->response, 672 GNUNET_assert (MHD_NO != MHD_add_response_header (con_handle->response,
637 "Access-Control-Allow-Headers", 673 "Access-Control-Allow-Headers",
638 allow_headers)); 674 allow_headers));
639 } 675 }
640 run_mhd_now (); 676 run_mhd_now ();
641 { 677 {
@@ -757,6 +793,7 @@ schedule_httpd ()
757 GNUNET_NETWORK_fdset_destroy (wws); 793 GNUNET_NETWORK_fdset_destroy (wws);
758} 794}
759 795
796
760/** 797/**
761 * Function called when MHD first processes an incoming connection. 798 * Function called when MHD first processes an incoming connection.
762 * Gives us the respective URI information. 799 * Gives us the respective URI information.
@@ -791,7 +828,6 @@ mhd_log_callback (void *cls,
791} 828}
792 829
793 830
794
795/** 831/**
796 * Function called when MHD decides that we are done with a connection. 832 * Function called when MHD decides that we are done with a connection.
797 * 833 *
@@ -823,6 +859,7 @@ mhd_completed_cb (void *cls,
823 *con_cls = NULL; 859 *con_cls = NULL;
824} 860}
825 861
862
826/** 863/**
827 * Function called when MHD connection is opened or closed. 864 * Function called when MHD connection is opened or closed.
828 * 865 *
@@ -886,7 +923,6 @@ mhd_connection_cb (void *cls,
886} 923}
887 924
888 925
889
890/** 926/**
891 * Task run whenever HTTP server operations are pending. 927 * Task run whenever HTTP server operations are pending.
892 * 928 *
@@ -977,10 +1013,17 @@ do_shutdown (void *cls)
977 GNUNET_CONTAINER_DLL_remove (plugins_head, 1013 GNUNET_CONTAINER_DLL_remove (plugins_head,
978 plugins_tail, 1014 plugins_tail,
979 ple); 1015 ple);
980 GNUNET_PLUGIN_unload (ple->libname, ple->plugin);
981 GNUNET_free (ple->libname); 1016 GNUNET_free (ple->libname);
982 GNUNET_free (ple); 1017 GNUNET_free (ple);
983 } 1018 }
1019 REST_config_done (config_plugin);
1020 REST_copying_done (copying_plugin);
1021 REST_identity_done (identity_plugin);
1022 REST_gns_done (gns_plugin);
1023#ifdef HAVE_JOSE
1024 REST_openid_done (openid_plugin);
1025#endif
1026 REST_reclaim_done (reclaim_plugin);
984 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutting down...\n"); 1027 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutting down...\n");
985 kill_httpd (); 1028 kill_httpd ();
986 GNUNET_free (allow_credentials); 1029 GNUNET_free (allow_credentials);
@@ -1065,28 +1108,30 @@ bind_v6 ()
1065 * @param libname the name of the library loaded 1108 * @param libname the name of the library loaded
1066 * @param lib_ret the object returned by the plugin initializer 1109 * @param lib_ret the object returned by the plugin initializer
1067 */ 1110 */
1068static void 1111static enum GNUNET_GenericReturnValue
1069load_plugin (void *cls, const char *libname, void *lib_ret) 1112setup_plugin (const char *name,
1113 GNUNET_REST_ProcessingFunction proc,
1114 void *plugin_cls)
1070{ 1115{
1071 struct GNUNET_REST_Plugin *plugin = lib_ret;
1072 struct PluginListEntry *ple; 1116 struct PluginListEntry *ple;
1073 1117
1074 if (NULL == lib_ret) 1118 if (NULL == plugin_cls)
1075 { 1119 {
1076 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1120 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1077 "Could not load plugin `%s'\n", 1121 "Could not load plugin\n");
1078 libname); 1122 return GNUNET_SYSERR;
1079 return;
1080 } 1123 }
1081 GNUNET_assert (1 < strlen (plugin->name)); 1124 GNUNET_assert (1 < strlen (name));
1082 GNUNET_assert ('/' == *plugin->name); 1125 GNUNET_assert ('/' == *name);
1083 ple = GNUNET_new (struct PluginListEntry); 1126 ple = GNUNET_new (struct PluginListEntry);
1084 ple->libname = GNUNET_strdup (libname); 1127 ple->libname = GNUNET_strdup (name);
1085 ple->plugin = plugin; 1128 ple->plugin = plugin_cls;
1129 ple->process_request = proc;
1086 GNUNET_CONTAINER_DLL_insert (plugins_head, 1130 GNUNET_CONTAINER_DLL_insert (plugins_head,
1087 plugins_tail, 1131 plugins_tail,
1088 ple); 1132 ple);
1089 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loaded plugin `%s'\n", libname); 1133 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loaded plugin `%s'\n", name);
1134 return GNUNET_OK;
1090} 1135}
1091 1136
1092 1137
@@ -1201,7 +1246,8 @@ run (void *cls,
1201 memset (basic_auth_secret_tmp, 0, 16); 1246 memset (basic_auth_secret_tmp, 0, 16);
1202 if (GNUNET_SYSERR == GNUNET_DISK_fn_read (basic_auth_file, 1247 if (GNUNET_SYSERR == GNUNET_DISK_fn_read (basic_auth_file,
1203 basic_auth_secret_tmp, 1248 basic_auth_secret_tmp,
1204 sizeof (basic_auth_secret_tmp) - 1)) 1249 sizeof (basic_auth_secret_tmp)
1250 - 1))
1205 { 1251 {
1206 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1252 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1207 "Unable to read basic auth secret file.\n"); 1253 "Unable to read basic auth secret file.\n");
@@ -1330,12 +1376,45 @@ run (void *cls,
1330 GNUNET_SCHEDULER_shutdown (); 1376 GNUNET_SCHEDULER_shutdown ();
1331 return; 1377 return;
1332 } 1378 }
1333/* Load plugins */ 1379 /* Load plugins */
1334 GNUNET_PLUGIN_load_all_in_context (GNUNET_OS_project_data_default (), 1380 // FIXME: Use per-plugin rest plugin structs
1335 "libgnunet_plugin_rest", 1381 struct GNUNET_REST_Plugin *config_plugin = REST_config_init (cfg);
1336 (void *) cfg, 1382 if (GNUNET_OK != setup_plugin (config_plugin->name, &REST_config_process_request, config_plugin))
1337 &load_plugin, 1383 {
1338 NULL); 1384 GNUNET_SCHEDULER_shutdown ();
1385 }
1386 struct GNUNET_REST_Plugin *copying_plugin = REST_copying_init (cfg);
1387 if (GNUNET_OK != setup_plugin (copying_plugin->name, &REST_copying_process_request, copying_plugin))
1388 {
1389 GNUNET_SCHEDULER_shutdown ();
1390 }
1391 struct GNUNET_REST_Plugin *identity_plugin = REST_identity_init (cfg);
1392 if (GNUNET_OK != setup_plugin (identity_plugin->name, &REST_identity_process_request, identity_plugin))
1393 {
1394 GNUNET_SCHEDULER_shutdown ();
1395 }
1396 struct GNUNET_REST_Plugin *namestore_plugin = REST_namestore_init (cfg);
1397 if (GNUNET_OK != setup_plugin (namestore_plugin->name, &REST_namestore_process_request, namestore_plugin))
1398 {
1399 GNUNET_SCHEDULER_shutdown ();
1400 }
1401 struct GNUNET_REST_Plugin *gns_plugin = REST_gns_init (cfg);
1402 if (GNUNET_OK != setup_plugin (gns_plugin->name, &REST_gns_process_request, gns_plugin))
1403 {
1404 GNUNET_SCHEDULER_shutdown ();
1405 }
1406#ifdef HAVE_JOSE
1407 struct GNUNET_REST_Plugin *openid_plugin = REST_openid_init (cfg);
1408 if (GNUNET_OK != setup_plugin (openid_plugin->name, &REST_openid_process_request, openid_plugin))
1409 {
1410 GNUNET_SCHEDULER_shutdown ();
1411 }
1412#endif
1413 struct GNUNET_REST_Plugin *reclaim_plugin = REST_reclaim_init (cfg);
1414 if (GNUNET_OK != setup_plugin (reclaim_plugin->name, &REST_reclaim_process_request, reclaim_plugin))
1415 {
1416 GNUNET_SCHEDULER_shutdown ();
1417 }
1339 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); 1418 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
1340} 1419}
1341 1420
diff --git a/src/service/rest/identity_plugin.c b/src/service/rest/identity_plugin.c
new file mode 100644
index 000000000..f6c9dd792
--- /dev/null
+++ b/src/service/rest/identity_plugin.c
@@ -0,0 +1,1287 @@
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 identity/plugin_rest_identity.c
24 * @brief GNUnet Identity REST plugin
25 */
26
27#include "platform.h"
28#include "gnunet_rest_plugin.h"
29#include "gnunet_identity_service.h"
30#include "gnunet_rest_lib.h"
31#include "../../service/identity/identity.h"
32#include "gnunet_util_lib.h"
33#include "microhttpd.h"
34#include <jansson.h>
35
36/**
37 * Identity Namespace
38 */
39#define GNUNET_REST_API_NS_IDENTITY "/identity"
40
41/**
42 * Identity Namespace with public key specifier
43 */
44#define GNUNET_REST_API_NS_IDENTITY_PUBKEY "/identity/pubkey"
45
46/**
47 * Identity Namespace with public key specifier
48 */
49#define GNUNET_REST_API_NS_IDENTITY_NAME "/identity/name"
50
51/**
52 * Identity Namespace with sign specifier
53 */
54#define GNUNET_REST_API_NS_SIGN "/sign"
55
56/**
57 * Parameter public key
58 */
59#define GNUNET_REST_IDENTITY_PARAM_PUBKEY "pubkey"
60
61/**
62 * Parameter private key
63 */
64#define GNUNET_REST_IDENTITY_PARAM_PRIVKEY "privkey"
65
66/**
67 * Parameter name
68 */
69#define GNUNET_REST_IDENTITY_PARAM_NAME "name"
70
71/**
72 * Parameter new name
73 */
74#define GNUNET_REST_IDENTITY_PARAM_NEWNAME "newname"
75
76/**
77 * Error message Missing identity name
78 */
79#define GNUNET_REST_IDENTITY_MISSING_NAME "Missing identity name"
80
81/**
82 * Error message Missing identity name
83 */
84#define GNUNET_REST_IDENTITY_MISSING_PUBKEY "Missing identity public key"
85
86/**
87 * Error message No data
88 */
89#define GNUNET_REST_ERROR_NO_DATA "No data"
90
91/**
92 * Error message Data invalid
93 */
94#define GNUNET_REST_ERROR_DATA_INVALID "Data invalid"
95
96/**
97 * State while collecting all egos
98 */
99#define ID_REST_STATE_INIT 0
100
101/**
102 * Done collecting egos
103 */
104#define ID_REST_STATE_POST_INIT 1
105
106/**
107 * The configuration handle
108 */
109const struct GNUNET_CONFIGURATION_Handle *id_cfg;
110
111/**
112 * HTTP methods allows for this plugin
113 */
114static char *allow_methods;
115
116/**
117 * Ego list
118 */
119static struct EgoEntry *ego_head;
120
121/**
122 * Ego list
123 */
124static struct EgoEntry *ego_tail;
125
126/**
127 * The processing state
128 */
129static int state;
130
131/**
132 * Handle to Identity service.
133 */
134static struct GNUNET_IDENTITY_Handle *identity_handle;
135
136/**
137 * @brief struct returned by the initialization function of the plugin
138 */
139struct Plugin
140{
141 const struct GNUNET_CONFIGURATION_Handle *cfg;
142};
143
144/**
145 * The ego list
146 */
147struct EgoEntry
148{
149 /**
150 * DLL
151 */
152 struct EgoEntry *next;
153
154 /**
155 * DLL
156 */
157 struct EgoEntry *prev;
158
159 /**
160 * Ego Identifier
161 */
162 char *identifier;
163
164 /**
165 * Public key string
166 */
167 char *keystring;
168
169 /**
170 * The Ego
171 */
172 struct GNUNET_IDENTITY_Ego *ego;
173};
174
175/**
176 * The request handle
177 */
178struct RequestHandle
179{
180 /**
181 * DLL
182 */
183 struct RequestHandle *next;
184
185 /**
186 * DLL
187 */
188 struct RequestHandle *prev;
189
190 /**
191 * The data from the REST request
192 */
193 const char *data;
194
195 /**
196 * The name to look up
197 */
198 char *name;
199
200 /**
201 * the length of the REST data
202 */
203 size_t data_size;
204
205 /**
206 * IDENTITY Operation
207 */
208 struct GNUNET_IDENTITY_Operation *op;
209
210 /**
211 * Rest connection
212 */
213 struct GNUNET_REST_RequestHandle *rest_handle;
214
215 /**
216 * Desired timeout for the lookup (default is no timeout).
217 */
218 struct GNUNET_TIME_Relative timeout;
219
220 /**
221 * ID of a task associated with the resolution process.
222 */
223 struct GNUNET_SCHEDULER_Task *timeout_task;
224
225 /**
226 * The plugin result processor
227 */
228 GNUNET_REST_ResultProcessor proc;
229
230 /**
231 * The closure of the result processor
232 */
233 void *proc_cls;
234
235 /**
236 * The url
237 */
238 char *url;
239
240 /**
241 * Error code
242 */
243 enum GNUNET_ErrorCode ec;
244};
245
246/**
247 * DLL
248 */
249static struct RequestHandle *requests_head;
250
251/**
252 * DLL
253 */
254static struct RequestHandle *requests_tail;
255
256/**
257 * Cleanup lookup handle
258 * @param cls Handle to clean up
259 */
260static void
261cleanup_handle (void *cls)
262{
263 struct RequestHandle *handle = cls;
264
265 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
266 if (NULL != handle->timeout_task)
267 {
268 GNUNET_SCHEDULER_cancel (handle->timeout_task);
269 handle->timeout_task = NULL;
270 }
271
272 if (NULL != handle->url)
273 GNUNET_free (handle->url);
274 if (NULL != handle->name)
275 GNUNET_free (handle->name);
276 GNUNET_CONTAINER_DLL_remove (requests_head,
277 requests_tail,
278 handle);
279 GNUNET_free (handle);
280}
281
282
283/**
284 * Task run on errors. Reports an error and cleans up everything.
285 *
286 * @param cls the `struct RequestHandle`
287 */
288static void
289do_error (void *cls)
290{
291 struct RequestHandle *handle = cls;
292 struct MHD_Response *resp;
293 json_t *json_error = json_object ();
294 char *response;
295 int response_code;
296
297 json_object_set_new (json_error, "error",
298 json_string (GNUNET_ErrorCode_get_hint (handle->ec)));
299 json_object_set_new (json_error, "error_code", json_integer (handle->ec));
300 response_code = GNUNET_ErrorCode_get_http_status (handle->ec);
301 if (0 == response_code)
302 response_code = MHD_HTTP_OK;
303 response = json_dumps (json_error, 0);
304 resp = GNUNET_REST_create_response (response);
305 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
306 "Content-Type",
307 "application/json"));
308 handle->proc (handle->proc_cls, resp, response_code);
309 json_decref (json_error);
310 GNUNET_free (response);
311 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
312}
313
314
315/**
316 * Get EgoEntry from list with either a public key or a name
317 * If public key and name are not NULL, it returns the public key result first
318 *
319 * @param handle the RequestHandle
320 * @param pubkey the public key of an identity (only one can be NULL)
321 * @param name the name of an identity (only one can be NULL)
322 * @return EgoEntry or NULL if not found
323 */
324struct EgoEntry *
325get_egoentry (struct RequestHandle *handle, char *pubkey, char *name)
326{
327 struct EgoEntry *ego_entry;
328
329 if (NULL != pubkey)
330 {
331 for (ego_entry = ego_head; NULL != ego_entry;
332 ego_entry = ego_entry->next)
333 {
334 if (0 != strcasecmp (pubkey, ego_entry->keystring))
335 continue;
336 return ego_entry;
337 }
338 }
339 if (NULL != name)
340 {
341 for (ego_entry = ego_head; NULL != ego_entry;
342 ego_entry = ego_entry->next)
343 {
344 if (0 != strcasecmp (name, ego_entry->identifier))
345 continue;
346 return ego_entry;
347 }
348 }
349 return NULL;
350}
351
352
353/**
354 * Handle identity GET request - responds with all identities
355 *
356 * @param con_handle the connection handle
357 * @param url the url
358 * @param cls the RequestHandle
359 */
360void
361ego_get_all (struct GNUNET_REST_RequestHandle *con_handle,
362 const char *url,
363 void *cls)
364{
365 struct RequestHandle *handle = cls;
366 struct EgoEntry *ego_entry;
367 struct MHD_Response *resp;
368 struct GNUNET_HashCode key;
369 json_t *json_root;
370 json_t *json_ego;
371 char *result_str;
372 char *privkey_str;
373
374 json_root = json_array ();
375 // Return ego/egos
376 for (ego_entry = ego_head; NULL != ego_entry;
377 ego_entry = ego_entry->next)
378 {
379 json_ego = json_object ();
380 json_object_set_new (json_ego,
381 GNUNET_REST_IDENTITY_PARAM_PUBKEY,
382 json_string (ego_entry->keystring));
383 GNUNET_CRYPTO_hash ("private", strlen ("private"), &key);
384 if (GNUNET_YES ==
385 GNUNET_CONTAINER_multihashmap_contains (
386 handle->rest_handle->url_param_map, &key))
387 {
388 privkey_str = GNUNET_CRYPTO_private_key_to_string (
389 GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego));
390 json_object_set_new (json_ego,
391 GNUNET_REST_IDENTITY_PARAM_PRIVKEY,
392 json_string (privkey_str));
393 GNUNET_free (privkey_str);
394 }
395
396 json_object_set_new (json_ego,
397 GNUNET_REST_IDENTITY_PARAM_NAME,
398 json_string (ego_entry->identifier));
399 json_array_append (json_root, json_ego);
400 json_decref (json_ego);
401 }
402
403 result_str = json_dumps (json_root, 0);
404 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
405 resp = GNUNET_REST_create_response (result_str);
406 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
407 "Content-Type",
408 "application/json"));
409 json_decref (json_root);
410 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
411 GNUNET_free (result_str);
412 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
413}
414
415
416/**
417 * Responds with the ego_entry identity
418 *
419 * @param handle the struct RequestHandle
420 * @param ego_entry the struct EgoEntry for the response
421 */
422void
423ego_get_response (struct RequestHandle *handle, struct EgoEntry *ego_entry)
424{
425 struct MHD_Response *resp;
426 struct GNUNET_HashCode key;
427 json_t *json_ego;
428 char *result_str;
429 char *privkey_str;
430
431 json_ego = json_object ();
432 json_object_set_new (json_ego,
433 GNUNET_REST_IDENTITY_PARAM_PUBKEY,
434 json_string (ego_entry->keystring));
435 json_object_set_new (json_ego,
436 GNUNET_REST_IDENTITY_PARAM_NAME,
437 json_string (ego_entry->identifier));
438 GNUNET_CRYPTO_hash ("private", strlen ("private"), &key);
439 if (GNUNET_YES ==
440 GNUNET_CONTAINER_multihashmap_contains (
441 handle->rest_handle->url_param_map, &key))
442 {
443 privkey_str = GNUNET_CRYPTO_private_key_to_string (
444 GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego));
445 json_object_set_new (json_ego,
446 GNUNET_REST_IDENTITY_PARAM_PRIVKEY,
447 json_string (privkey_str));
448 GNUNET_free (privkey_str);
449 }
450
451 result_str = json_dumps (json_ego, 0);
452 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
453 resp = GNUNET_REST_create_response (result_str);
454 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
455 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
456 "Content-Type",
457 "application/json"));
458 json_decref (json_ego);
459 GNUNET_free (result_str);
460 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
461}
462
463
464/**
465 * Handle identity GET request with a public key
466 *
467 * @param con_handle the connection handle
468 * @param url the url
469 * @param cls the RequestHandle
470 */
471void
472ego_get_pubkey (struct GNUNET_REST_RequestHandle *con_handle,
473 const char *url,
474 void *cls)
475{
476 struct RequestHandle *handle = cls;
477 struct EgoEntry *ego_entry;
478 char *keystring;
479
480 keystring = NULL;
481
482 if (strlen (GNUNET_REST_API_NS_IDENTITY_PUBKEY) >= strlen (handle->url))
483 {
484 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
485 GNUNET_SCHEDULER_add_now (&do_error, handle);
486 return;
487 }
488 keystring = &handle->url[strlen (GNUNET_REST_API_NS_IDENTITY_PUBKEY) + 1];
489 ego_entry = get_egoentry (handle, keystring, NULL);
490
491 if (NULL == ego_entry)
492 {
493 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
494 GNUNET_SCHEDULER_add_now (&do_error, handle);
495 return;
496 }
497
498 ego_get_response (handle, ego_entry);
499}
500
501
502/**
503 * Handle identity GET request with a name
504 *
505 * @param con_handle the connection handle
506 * @param url the url
507 * @param cls the RequestHandle
508 */
509void
510ego_get_name (struct GNUNET_REST_RequestHandle *con_handle,
511 const char *url,
512 void *cls)
513{
514 struct RequestHandle *handle = cls;
515 struct EgoEntry *ego_entry;
516 char *egoname;
517
518 egoname = NULL;
519
520 if (strlen (GNUNET_REST_API_NS_IDENTITY_NAME) >= strlen (handle->url))
521 {
522 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
523 GNUNET_SCHEDULER_add_now (&do_error, handle);
524 return;
525 }
526 egoname = &handle->url[strlen (GNUNET_REST_API_NS_IDENTITY_NAME) + 1];
527 ego_entry = get_egoentry (handle, NULL, egoname);
528
529 if (NULL == ego_entry)
530 {
531 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
532 GNUNET_SCHEDULER_add_now (&do_error, handle);
533 return;
534 }
535
536 ego_get_response (handle, ego_entry);
537}
538
539
540/**
541 * Processing finished
542 *
543 * @param cls request handle
544 * @param ec error code
545 */
546static void
547do_finished (void *cls, enum GNUNET_ErrorCode ec)
548{
549 struct RequestHandle *handle = cls;
550 struct MHD_Response *resp;
551 int response_code;
552
553 handle->op = NULL;
554 handle->ec = ec;
555 if (GNUNET_EC_NONE != ec)
556 {
557 GNUNET_SCHEDULER_add_now (&do_error, handle);
558 return;
559 }
560 if (GNUNET_EC_NONE == handle->ec)
561 response_code = MHD_HTTP_NO_CONTENT;
562 else
563 response_code = GNUNET_ErrorCode_get_http_status (ec);
564 resp = GNUNET_REST_create_response (NULL);
565 handle->proc (handle->proc_cls, resp, response_code);
566 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
567}
568
569
570/**
571 * Processing finished, when creating an ego.
572 *
573 * @param cls request handle
574 * @param pk private key of the ego, or NULL on error
575 * @param ec error code
576 */
577static void
578do_finished_create (void *cls,
579 const struct GNUNET_CRYPTO_PrivateKey *pk,
580 enum GNUNET_ErrorCode ec)
581{
582 struct RequestHandle *handle = cls;
583
584 (void) pk;
585 do_finished (handle, ec);
586}
587
588
589/**
590 * Processing edit ego with EgoEntry ego_entry
591 *
592 * @param handle the struct RequestHandle
593 * @param ego_entry the struct EgoEntry we want to edit
594 */
595void
596ego_edit (struct RequestHandle *handle, struct EgoEntry *ego_entry)
597{
598 json_t *data_js;
599 json_error_t err;
600 char *newname;
601 char term_data[handle->data_size + 1];
602 int json_state;
603
604 // if no data
605 if (0 >= handle->data_size)
606 {
607 handle->ec = GNUNET_EC_IDENTITY_INVALID;
608 GNUNET_SCHEDULER_add_now (&do_error, handle);
609 return;
610 }
611 // if not json
612 term_data[handle->data_size] = '\0';
613 GNUNET_memcpy (term_data, handle->data, handle->data_size);
614 data_js = json_loads (term_data, JSON_DECODE_ANY, &err);
615
616 if (NULL == data_js)
617 {
618 handle->ec = GNUNET_EC_IDENTITY_INVALID;
619 GNUNET_SCHEDULER_add_now (&do_error, handle);
620 return;
621 }
622
623 newname = NULL;
624 // NEW NAME
625 json_state = 0;
626 json_state = json_unpack (data_js,
627 "{s:s!}",
628 GNUNET_REST_IDENTITY_PARAM_NEWNAME,
629 &newname);
630 // Change name with pubkey or name identifier
631 if (0 != json_state)
632 {
633 handle->ec = GNUNET_EC_IDENTITY_INVALID;
634 GNUNET_SCHEDULER_add_now (&do_error, handle);
635 json_decref (data_js);
636 return;
637 }
638
639 if (NULL == newname)
640 {
641 handle->ec = GNUNET_EC_IDENTITY_INVALID;
642 GNUNET_SCHEDULER_add_now (&do_error, handle);
643 json_decref (data_js);
644 return;
645 }
646
647 if (0 >= strlen (newname))
648 {
649 handle->ec = GNUNET_EC_IDENTITY_INVALID;
650 GNUNET_SCHEDULER_add_now (&do_error, handle);
651 json_decref (data_js);
652 return;
653 }
654
655 handle->op = GNUNET_IDENTITY_rename (identity_handle,
656 ego_entry->identifier,
657 newname,
658 &do_finished,
659 handle);
660 if (NULL == handle->op)
661 {
662 handle->ec = GNUNET_EC_UNKNOWN;
663 GNUNET_SCHEDULER_add_now (&do_error, handle);
664 json_decref (data_js);
665 return;
666 }
667 json_decref (data_js);
668 return;
669}
670
671
672/**
673 * Handle identity PUT request with public key
674 *
675 * @param con_handle the connection handle
676 * @param url the url
677 * @param cls the RequestHandle
678 */
679void
680ego_edit_pubkey (struct GNUNET_REST_RequestHandle *con_handle,
681 const char *url,
682 void *cls)
683{
684 struct RequestHandle *handle = cls;
685 struct EgoEntry *ego_entry;
686 char *keystring;
687
688 keystring = NULL;
689
690 if (strlen (GNUNET_REST_API_NS_IDENTITY_PUBKEY) >= strlen (handle->url))
691 {
692 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
693 GNUNET_SCHEDULER_add_now (&do_error, handle);
694 return;
695 }
696 keystring = &handle->url[strlen (GNUNET_REST_API_NS_IDENTITY_PUBKEY) + 1];
697 ego_entry = get_egoentry (handle, keystring, NULL);
698
699 if (NULL == ego_entry)
700 {
701 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
702 GNUNET_SCHEDULER_add_now (&do_error, handle);
703 return;
704 }
705
706 ego_edit (handle, ego_entry);
707}
708
709
710/**
711 * Handle identity PUT request with name
712 *
713 * @param con_handle the connection handle
714 * @param url the url
715 * @param cls the RequestHandle
716 */
717void
718ego_edit_name (struct GNUNET_REST_RequestHandle *con_handle,
719 const char *url,
720 void *cls)
721{
722 struct RequestHandle *handle = cls;
723 struct EgoEntry *ego_entry;
724 char *name;
725
726 name = NULL;
727
728 if (strlen (GNUNET_REST_API_NS_IDENTITY_NAME) >= strlen (handle->url))
729 {
730 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
731 GNUNET_SCHEDULER_add_now (&do_error, handle);
732 return;
733 }
734 name = &handle->url[strlen (GNUNET_REST_API_NS_IDENTITY_NAME) + 1];
735 ego_entry = get_egoentry (handle, NULL, name);
736
737 if (NULL == ego_entry)
738 {
739 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
740 GNUNET_SCHEDULER_add_now (&do_error, handle);
741 return;
742 }
743
744 ego_edit (handle, ego_entry);
745}
746
747
748/**
749 * Handle identity POST request
750 *
751 * @param con_handle the connection handle
752 * @param url the url
753 * @param cls the RequestHandle
754 */
755void
756ego_create (struct GNUNET_REST_RequestHandle *con_handle,
757 const char *url,
758 void *cls)
759{
760 struct RequestHandle *handle = cls;
761 json_t *data_js;
762 json_error_t err;
763 char *egoname;
764 char *privkey;
765 struct GNUNET_CRYPTO_PrivateKey pk;
766 struct GNUNET_CRYPTO_PrivateKey *pk_ptr;
767 int json_unpack_state;
768 char term_data[handle->data_size + 1];
769
770 if (strlen (GNUNET_REST_API_NS_IDENTITY) != strlen (handle->url))
771 {
772 GNUNET_SCHEDULER_add_now (&do_error, handle);
773 return;
774 }
775
776 if (0 >= handle->data_size)
777 {
778 handle->ec = GNUNET_EC_IDENTITY_INVALID;
779 GNUNET_SCHEDULER_add_now (&do_error, handle);
780 return;
781 }
782 term_data[handle->data_size] = '\0';
783 GNUNET_memcpy (term_data, handle->data, handle->data_size);
784 data_js = json_loads (term_data, JSON_DECODE_ANY, &err);
785 if (NULL == data_js)
786 {
787 handle->ec = GNUNET_EC_IDENTITY_INVALID;
788 GNUNET_SCHEDULER_add_now (&do_error, handle);
789 json_decref (data_js);
790 return;
791 }
792 json_unpack_state = 0;
793 privkey = NULL;
794 json_unpack_state =
795 json_unpack (data_js, "{s:s, s?:s!}",
796 GNUNET_REST_IDENTITY_PARAM_NAME, &egoname,
797 GNUNET_REST_IDENTITY_PARAM_PRIVKEY, &privkey);
798 if (0 != json_unpack_state)
799 {
800 handle->ec = GNUNET_EC_IDENTITY_INVALID;
801 GNUNET_SCHEDULER_add_now (&do_error, handle);
802 json_decref (data_js);
803 return;
804 }
805
806 if (NULL == egoname)
807 {
808 handle->ec = GNUNET_EC_IDENTITY_INVALID;
809 GNUNET_SCHEDULER_add_now (&do_error, handle);
810 json_decref (data_js);
811 return;
812 }
813 if (0 >= strlen (egoname))
814 {
815 handle->ec = GNUNET_EC_IDENTITY_INVALID;
816 json_decref (data_js);
817 GNUNET_SCHEDULER_add_now (&do_error, handle);
818 return;
819 }
820 GNUNET_STRINGS_utf8_tolower (egoname, egoname);
821 handle->name = GNUNET_strdup (egoname);
822 if (NULL != privkey)
823 {
824 GNUNET_STRINGS_string_to_data (privkey,
825 strlen (privkey),
826 &pk,
827 sizeof(struct
828 GNUNET_CRYPTO_PrivateKey));
829 pk_ptr = &pk;
830 }
831 else
832 pk_ptr = NULL;
833 json_decref (data_js);
834 handle->op = GNUNET_IDENTITY_create (identity_handle,
835 handle->name,
836 pk_ptr,
837 GNUNET_PUBLIC_KEY_TYPE_ECDSA,
838 &do_finished_create,
839 handle);
840}
841
842
843/**
844 * Handle identity DELETE request with public key
845 *
846 * @param con_handle the connection handle
847 * @param url the url
848 * @param cls the RequestHandle
849 */
850void
851ego_delete_pubkey (struct GNUNET_REST_RequestHandle *con_handle,
852 const char *url,
853 void *cls)
854{
855 struct RequestHandle *handle = cls;
856 struct EgoEntry *ego_entry;
857 char *keystring;
858
859 keystring = NULL;
860
861 if (strlen (GNUNET_REST_API_NS_IDENTITY_PUBKEY) >= strlen (handle->url))
862 {
863 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
864 GNUNET_SCHEDULER_add_now (&do_error, handle);
865 return;
866 }
867 keystring = &handle->url[strlen (GNUNET_REST_API_NS_IDENTITY_PUBKEY) + 1];
868 ego_entry = get_egoentry (handle, keystring, NULL);
869
870 if (NULL == ego_entry)
871 {
872 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
873 GNUNET_SCHEDULER_add_now (&do_error, handle);
874 return;
875 }
876
877 handle->op = GNUNET_IDENTITY_delete (identity_handle,
878 ego_entry->identifier,
879 &do_finished,
880 handle);
881}
882
883
884/**
885 * Handle identity DELETE request with name
886 *
887 * @param con_handle the connection handle
888 * @param url the url
889 * @param cls the RequestHandle
890 */
891void
892ego_delete_name (struct GNUNET_REST_RequestHandle *con_handle,
893 const char *url,
894 void *cls)
895{
896 struct RequestHandle *handle = cls;
897 struct EgoEntry *ego_entry;
898 char *name;
899
900 name = NULL;
901
902 if (strlen (GNUNET_REST_API_NS_IDENTITY_NAME) >= strlen (handle->url))
903 {
904 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
905 GNUNET_SCHEDULER_add_now (&do_error, handle);
906 return;
907 }
908 name = &handle->url[strlen (GNUNET_REST_API_NS_IDENTITY_NAME) + 1];
909 ego_entry = get_egoentry (handle, NULL, name);
910
911 if (NULL == ego_entry)
912 {
913 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
914 GNUNET_SCHEDULER_add_now (&do_error, handle);
915 return;
916 }
917
918 handle->op = GNUNET_IDENTITY_delete (identity_handle,
919 ego_entry->identifier,
920 &do_finished,
921 handle);
922}
923
924
925struct ego_sign_data_cls
926{
927 void *data;
928 struct RequestHandle *handle;
929};
930
931void
932ego_sign_data_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego)
933{
934 struct RequestHandle *handle = ((struct ego_sign_data_cls *) cls)->handle;
935 unsigned char *data
936 = (unsigned char *) ((struct ego_sign_data_cls *) cls)->data; // data is url decoded
937 struct MHD_Response *resp;
938 struct GNUNET_CRYPTO_EddsaSignature sig;
939 char *sig_str;
940 char *result;
941
942 if (ego == NULL)
943 {
944 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
945 GNUNET_SCHEDULER_add_now (&do_error, handle);
946 return;
947 }
948
949 if (ntohl (ego->pk.type) != GNUNET_PUBLIC_KEY_TYPE_EDDSA)
950 {
951 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
952 GNUNET_SCHEDULER_add_now (&do_error, handle);
953 return;
954 }
955
956 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign_raw (&(ego->pk.eddsa_key),
957 (void *) data,
958 strlen ( (char*) data),
959 &sig))
960 {
961 handle->ec = GNUNET_EC_UNKNOWN;
962 GNUNET_SCHEDULER_add_now (&do_error, handle);
963 return;
964 }
965
966 GNUNET_STRINGS_base64url_encode (&sig,
967 sizeof (struct GNUNET_CRYPTO_EddsaSignature),
968 &sig_str);
969
970 GNUNET_asprintf (&result,
971 "{\"signature\": \"%s\"}",
972 sig_str);
973
974 resp = GNUNET_REST_create_response (result);
975 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
976
977 free (data);
978 free (sig_str);
979 free (result);
980 free (cls);
981 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
982}
983
984
985/**
986 *
987 * @param con_handle the connection handle
988 * @param url the url
989 * @param cls the RequestHandle
990 */
991void
992ego_sign_data (struct GNUNET_REST_RequestHandle *con_handle,
993 const char *url,
994 void *cls)
995{
996 // TODO: replace with precompiler #define
997 const char *username_key = "user";
998 const char *data_key = "data";
999
1000 struct RequestHandle *handle = cls;
1001 struct GNUNET_HashCode cache_key_username;
1002 struct GNUNET_HashCode cache_key_data;
1003 char *username;
1004 char *data;
1005
1006 struct ego_sign_data_cls *cls2;
1007
1008 GNUNET_CRYPTO_hash (username_key, strlen (username_key), &cache_key_username);
1009 GNUNET_CRYPTO_hash (data_key, strlen (data_key), &cache_key_data);
1010
1011 if ((GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (
1012 handle->rest_handle->url_param_map,
1013 &cache_key_username)) ||
1014 (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (
1015 handle->rest_handle->url_param_map,
1016 &cache_key_data)))
1017 {
1018 handle->ec = GNUNET_EC_UNKNOWN;
1019 GNUNET_SCHEDULER_add_now (&do_error, handle);
1020 return;
1021 }
1022
1023 username = (char *) GNUNET_CONTAINER_multihashmap_get (
1024 handle->rest_handle->url_param_map,
1025 &cache_key_username);
1026
1027 data = (char *) GNUNET_CONTAINER_multihashmap_get (
1028 handle->rest_handle->url_param_map,
1029 &cache_key_data);
1030
1031 cls2 = malloc (sizeof(struct ego_sign_data_cls));
1032 cls2->data = (void *) GNUNET_strdup (data);
1033 cls2->handle = handle;
1034
1035 GNUNET_IDENTITY_ego_lookup (id_cfg,
1036 username,
1037 ego_sign_data_cb,
1038 cls2);
1039}
1040
1041
1042/**
1043 * Respond to OPTIONS request
1044 *
1045 * @param con_handle the connection handle
1046 * @param url the url
1047 * @param cls the RequestHandle
1048 */
1049static void
1050options_cont (struct GNUNET_REST_RequestHandle *con_handle,
1051 const char *url,
1052 void *cls)
1053{
1054 struct MHD_Response *resp;
1055 struct RequestHandle *handle = cls;
1056
1057 // For now, independent of path return all options
1058 resp = GNUNET_REST_create_response (NULL);
1059 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
1060 "Access-Control-Allow-Methods",
1061 allow_methods));
1062 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1063 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
1064 return;
1065}
1066
1067
1068static void
1069list_ego (void *cls,
1070 struct GNUNET_IDENTITY_Ego *ego,
1071 void **ctx,
1072 const char *identifier)
1073{
1074 struct EgoEntry *ego_entry;
1075 struct GNUNET_CRYPTO_PublicKey pk;
1076
1077 if ((NULL == ego) && (ID_REST_STATE_INIT == state))
1078 {
1079 state = ID_REST_STATE_POST_INIT;
1080 return;
1081 }
1082 if (NULL == ego)
1083 {
1084 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1085 "Called with NULL ego\n");
1086 return;
1087 }
1088 if (ID_REST_STATE_INIT == state)
1089 {
1090 ego_entry = GNUNET_new (struct EgoEntry);
1091 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1092 ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk);
1093 ego_entry->ego = ego;
1094 ego_entry->identifier = GNUNET_strdup (identifier);
1095 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
1096 ego_tail,
1097 ego_entry);
1098 }
1099 /* Ego renamed or added */
1100 if (identifier != NULL)
1101 {
1102 for (ego_entry = ego_head; NULL != ego_entry;
1103 ego_entry = ego_entry->next)
1104 {
1105 if (ego_entry->ego == ego)
1106 {
1107 /* Rename */
1108 GNUNET_free (ego_entry->identifier);
1109 ego_entry->identifier = GNUNET_strdup (identifier);
1110 break;
1111 }
1112 }
1113 if (NULL == ego_entry)
1114 {
1115 /* Add */
1116 ego_entry = GNUNET_new (struct EgoEntry);
1117 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1118 ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk);
1119 ego_entry->ego = ego;
1120 ego_entry->identifier = GNUNET_strdup (identifier);
1121 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
1122 ego_tail,
1123 ego_entry);
1124 }
1125 }
1126 else
1127 {
1128 /* Delete */
1129 for (ego_entry = ego_head; NULL != ego_entry;
1130 ego_entry = ego_entry->next)
1131 {
1132 if (ego_entry->ego == ego)
1133 break;
1134 }
1135 if (NULL == ego_entry)
1136 return; /* Not found */
1137
1138 GNUNET_CONTAINER_DLL_remove (ego_head,
1139 ego_tail,
1140 ego_entry);
1141 GNUNET_free (ego_entry->identifier);
1142 GNUNET_free (ego_entry->keystring);
1143 GNUNET_free (ego_entry);
1144 return;
1145 }
1146
1147}
1148
1149
1150/**
1151 * Function processing the REST call
1152 *
1153 * @param method HTTP method
1154 * @param url URL of the HTTP request
1155 * @param data body of the HTTP request (optional)
1156 * @param data_size length of the body
1157 * @param proc callback function for the result
1158 * @param proc_cls closure for callback function
1159 * @return GNUNET_OK if request accepted
1160 */
1161enum GNUNET_GenericReturnValue
1162REST_identity_process_request (void* plugin,
1163 struct GNUNET_REST_RequestHandle *rest_handle,
1164 GNUNET_REST_ResultProcessor proc,
1165 void *proc_cls)
1166{
1167 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
1168 struct GNUNET_REST_RequestHandlerError err;
1169 static const struct GNUNET_REST_RequestHandler handlers[] =
1170 { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_PUBKEY,
1171 &ego_get_pubkey },
1172 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_NAME, &ego_get_name },
1173 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY, &ego_get_all },
1174 { MHD_HTTP_METHOD_PUT,
1175 GNUNET_REST_API_NS_IDENTITY_PUBKEY,
1176 &ego_edit_pubkey },
1177 { MHD_HTTP_METHOD_PUT, GNUNET_REST_API_NS_IDENTITY_NAME, &ego_edit_name },
1178 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY, &ego_create },
1179 { MHD_HTTP_METHOD_DELETE,
1180 GNUNET_REST_API_NS_IDENTITY_PUBKEY,
1181 &ego_delete_pubkey },
1182 { MHD_HTTP_METHOD_DELETE,
1183 GNUNET_REST_API_NS_IDENTITY_NAME,
1184 &ego_delete_name },
1185 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_IDENTITY, &options_cont },
1186 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_SIGN, &ego_sign_data},
1187 GNUNET_REST_HANDLER_END };
1188
1189
1190 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1191 handle->proc_cls = proc_cls;
1192 handle->proc = proc;
1193 handle->rest_handle = rest_handle;
1194 handle->data = rest_handle->data;
1195 handle->data_size = rest_handle->data_size;
1196
1197 handle->url = GNUNET_strdup (rest_handle->url);
1198 if (handle->url[strlen (handle->url) - 1] == '/')
1199 handle->url[strlen (handle->url) - 1] = '\0';
1200 handle->timeout_task =
1201 GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_error, handle);
1202 GNUNET_CONTAINER_DLL_insert (requests_head,
1203 requests_tail,
1204 handle);
1205 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
1206 if (GNUNET_NO ==
1207 GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
1208 {
1209 cleanup_handle (handle);
1210 return GNUNET_NO;
1211 }
1212
1213 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
1214 return GNUNET_YES;
1215}
1216
1217
1218/**
1219 * Entry point for the plugin.
1220 *
1221 * @param cls Config info
1222 * @return NULL on error, otherwise the plugin context
1223 */
1224void *
1225REST_identity_init (const struct GNUNET_CONFIGURATION_Handle *c)
1226{
1227 static struct Plugin plugin;
1228 struct GNUNET_REST_Plugin *api;
1229
1230 id_cfg = c;
1231 if (NULL != plugin.cfg)
1232 return NULL; /* can only initialize once! */
1233 memset (&plugin, 0, sizeof(struct Plugin));
1234 plugin.cfg = c;
1235 api = GNUNET_new (struct GNUNET_REST_Plugin);
1236 api->cls = &plugin;
1237 api->name = GNUNET_REST_API_NS_IDENTITY;
1238 GNUNET_asprintf (&allow_methods,
1239 "%s, %s, %s, %s, %s",
1240 MHD_HTTP_METHOD_GET,
1241 MHD_HTTP_METHOD_POST,
1242 MHD_HTTP_METHOD_PUT,
1243 MHD_HTTP_METHOD_DELETE,
1244 MHD_HTTP_METHOD_OPTIONS);
1245 state = ID_REST_STATE_INIT;
1246 identity_handle = GNUNET_IDENTITY_connect (id_cfg, &list_ego, NULL);
1247
1248 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Identity REST API initialized\n"));
1249 return api;
1250}
1251
1252
1253/**
1254 * Exit point from the plugin.
1255 *
1256 * @param cls the plugin context (as returned by "init")
1257 * @return always NULL
1258 */
1259void
1260REST_identity_done (struct GNUNET_REST_Plugin *api)
1261{
1262 struct Plugin *plugin = api->cls;
1263 struct EgoEntry *ego_entry;
1264 struct EgoEntry *ego_tmp;
1265
1266 plugin->cfg = NULL;
1267 while (NULL != requests_head)
1268 cleanup_handle (requests_head);
1269 if (NULL != identity_handle)
1270 GNUNET_IDENTITY_disconnect (identity_handle);
1271
1272 for (ego_entry = ego_head; NULL != ego_entry;)
1273 {
1274 ego_tmp = ego_entry;
1275 ego_entry = ego_entry->next;
1276 GNUNET_free (ego_tmp->identifier);
1277 GNUNET_free (ego_tmp->keystring);
1278 GNUNET_free (ego_tmp);
1279 }
1280
1281 GNUNET_free (allow_methods);
1282 GNUNET_free (api);
1283 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Identity REST plugin is finished\n");
1284}
1285
1286
1287/* end of plugin_rest_identity.c */
diff --git a/src/service/rest/identity_plugin.h b/src/service/rest/identity_plugin.h
new file mode 100644
index 000000000..0886156c1
--- /dev/null
+++ b/src/service/rest/identity_plugin.h
@@ -0,0 +1,36 @@
1#include "gnunet_rest_plugin.h"
2/**
3 * Function processing the REST call
4 *
5 * @param method HTTP method
6 * @param url URL of the HTTP request
7 * @param data body of the HTTP request (optional)
8 * @param data_size length of the body
9 * @param proc callback function for the result
10 * @param proc_cls closure for @a proc
11 * @return #GNUNET_OK if request accepted
12 */
13enum GNUNET_GenericReturnValue
14REST_identity_process_request (void *plugin,
15 struct GNUNET_REST_RequestHandle *conndata_handle,
16 GNUNET_REST_ResultProcessor proc,
17 void *proc_cls);
18
19/**
20 * Entry point for the plugin.
21 *
22 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
23 * @return NULL on error, otherwise the plugin context
24 */
25void*
26REST_identity_init (const struct GNUNET_CONFIGURATION_Handle *c);
27
28
29/**
30 * Exit point from the plugin.
31 *
32 * @param cls the plugin context (as returned by "init")
33 * @return always NULL
34 */
35void
36REST_identity_done (struct GNUNET_REST_Plugin *api);
diff --git a/src/service/rest/json_reclaim.c b/src/service/rest/json_reclaim.c
new file mode 100644
index 000000000..b1ca7a4a5
--- /dev/null
+++ b/src/service/rest/json_reclaim.c
@@ -0,0 +1,398 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2018 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 rest-plugins/json_reclaim.c
23 * @brief JSON handling of reclaim data
24 * @author Martin Schanzenbach
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_json_lib.h"
29#include "gnunet_reclaim_lib.h"
30#include "gnunet_reclaim_service.h"
31
32
33/**
34 * Parse given JSON object to a claim
35 *
36 * @param cls closure, NULL
37 * @param root the json object representing data
38 * @param spec where to write the data
39 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
40 */
41static int
42parse_attr (void *cls, json_t *root, struct GNUNET_JSON_Specification *spec)
43{
44 struct GNUNET_RECLAIM_Attribute *attr;
45 const char *name_str = NULL;
46 const char *val_str = NULL;
47 const char *type_str = NULL;
48 const char *id_str = NULL;
49 const char *cred_str = NULL;
50 const char *flag_str = NULL;
51 char *data;
52 int unpack_state;
53 uint32_t type;
54 size_t data_size;
55
56 GNUNET_assert (NULL != root);
57
58 if (! json_is_object (root))
59 {
60 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
61 "Error json is not array nor object!\n");
62 return GNUNET_SYSERR;
63 }
64 // interpret single attribute
65 unpack_state = json_unpack (root,
66 "{s:s, s?s, s?s, s:s, s:s, s?s!}",
67 "name",
68 &name_str,
69 "id",
70 &id_str,
71 "credential",
72 &cred_str,
73 "type",
74 &type_str,
75 "value",
76 &val_str,
77 "flag",
78 &flag_str);
79 if ((0 != unpack_state) || (NULL == name_str) || (NULL == val_str) ||
80 (NULL == type_str))
81 {
82 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
83 "Error json object has a wrong format!\n");
84 return GNUNET_SYSERR;
85 }
86 type = GNUNET_RECLAIM_attribute_typename_to_number (type_str);
87 if (GNUNET_SYSERR ==
88 (GNUNET_RECLAIM_attribute_string_to_value (type,
89 val_str,
90 (void **) &data,
91 &data_size)))
92 {
93 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Attribute value invalid!\n");
94 return GNUNET_SYSERR;
95 }
96 attr = GNUNET_RECLAIM_attribute_new (name_str, NULL,
97 type, data, data_size);
98 GNUNET_free (data);
99 if ((NULL != cred_str) && (0 != strlen (cred_str)))
100 {
101 GNUNET_STRINGS_string_to_data (cred_str,
102 strlen (cred_str),
103 &attr->credential,
104 sizeof(attr->credential));
105 }
106 if ((NULL == id_str) || (0 == strlen (id_str)))
107 memset (&attr->id, 0, sizeof (attr->id));
108 else
109 GNUNET_STRINGS_string_to_data (id_str,
110 strlen (id_str),
111 &attr->id,
112 sizeof(attr->id));
113
114 *(struct GNUNET_RECLAIM_Attribute **) spec->ptr = attr;
115 return GNUNET_OK;
116}
117
118
119/**
120 * Cleanup data left from parsing RSA public key.
121 *
122 * @param cls closure, NULL
123 * @param[out] spec where to free the data
124 */
125static void
126clean_attr (void *cls, struct GNUNET_JSON_Specification *spec)
127{
128 struct GNUNET_RECLAIM_Attribute **attr;
129
130 attr = (struct GNUNET_RECLAIM_Attribute **) spec->ptr;
131 if (NULL != *attr)
132 {
133 GNUNET_free (*attr);
134 *attr = NULL;
135 }
136}
137
138
139/**
140 * JSON Specification for Reclaim claims.
141 *
142 * @param ticket struct of GNUNET_RECLAIM_Attribute to fill
143 * @return JSON Specification
144 */
145struct GNUNET_JSON_Specification
146GNUNET_RECLAIM_JSON_spec_attribute (struct GNUNET_RECLAIM_Attribute **attr)
147{
148 struct GNUNET_JSON_Specification ret = { .parser = &parse_attr,
149 .cleaner = &clean_attr,
150 .cls = NULL,
151 .field = NULL,
152 .ptr = attr,
153 .ptr_size = 0,
154 .size_ptr = NULL };
155
156 *attr = NULL;
157 return ret;
158}
159
160
161/**
162 * Parse given JSON object to a ticket
163 *
164 * @param cls closure, NULL
165 * @param root the json object representing data
166 * @param spec where to write the data
167 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
168 */
169static int
170parse_ticket (void *cls, json_t *root, struct GNUNET_JSON_Specification *spec)
171{
172 struct GNUNET_RECLAIM_Ticket *ticket;
173 const char *rnd_str;
174 const char *aud_str;
175 const char *id_str;
176 int unpack_state;
177
178 GNUNET_assert (NULL != root);
179
180 if (! json_is_object (root))
181 {
182 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
183 "Error json is not array nor object!\n");
184 return GNUNET_SYSERR;
185 }
186 // interpret single ticket
187 unpack_state = json_unpack (root,
188 "{s:s, s:s, s:s!}",
189 "rnd",
190 &rnd_str,
191 "audience",
192 &aud_str,
193 "issuer",
194 &id_str);
195 if (0 != unpack_state)
196 {
197 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
198 "Error json object has a wrong format!\n");
199 return GNUNET_SYSERR;
200 }
201 ticket = GNUNET_new (struct GNUNET_RECLAIM_Ticket);
202 if (GNUNET_OK != GNUNET_STRINGS_string_to_data (rnd_str,
203 strlen (rnd_str),
204 &ticket->rnd,
205 sizeof(ticket->rnd)))
206 {
207 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Rnd invalid\n");
208 GNUNET_free (ticket);
209 return GNUNET_SYSERR;
210 }
211 if (GNUNET_OK !=
212 GNUNET_STRINGS_string_to_data (id_str,
213 strlen (id_str),
214 &ticket->identity,
215 sizeof(ticket->identity)))
216 {
217 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Identity invalid\n");
218 GNUNET_free (ticket);
219 return GNUNET_SYSERR;
220 }
221
222 if (GNUNET_OK !=
223 GNUNET_STRINGS_string_to_data (aud_str,
224 strlen (aud_str),
225 &ticket->audience,
226 sizeof(ticket->audience)))
227 {
228 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Audience invalid\n");
229 GNUNET_free (ticket);
230 return GNUNET_SYSERR;
231 }
232
233 *(struct GNUNET_RECLAIM_Ticket **) spec->ptr = ticket;
234 return GNUNET_OK;
235}
236
237
238/**
239 * Cleanup data left from parsing RSA public key.
240 *
241 * @param cls closure, NULL
242 * @param[out] spec where to free the data
243 */
244static void
245clean_ticket (void *cls, struct GNUNET_JSON_Specification *spec)
246{
247 struct GNUNET_RECLAIM_Ticket **ticket;
248
249 ticket = (struct GNUNET_RECLAIM_Ticket **) spec->ptr;
250 if (NULL != *ticket)
251 {
252 GNUNET_free (*ticket);
253 *ticket = NULL;
254 }
255}
256
257
258/**
259 * JSON Specification for Reclaim tickets.
260 *
261 * @param ticket struct of GNUNET_RECLAIM_Ticket to fill
262 * @return JSON Specification
263 */
264struct GNUNET_JSON_Specification
265GNUNET_RECLAIM_JSON_spec_ticket (struct GNUNET_RECLAIM_Ticket **ticket)
266{
267 struct GNUNET_JSON_Specification ret = { .parser = &parse_ticket,
268 .cleaner = &clean_ticket,
269 .cls = NULL,
270 .field = NULL,
271 .ptr = ticket,
272 .ptr_size = 0,
273 .size_ptr = NULL };
274
275 *ticket = NULL;
276 return ret;
277}
278
279
280/**
281 * Parse given JSON object to a credential claim
282 *
283 * @param cls closure, NULL
284 * @param root the json object representing data
285 * @param spec where to write the data
286 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
287 */
288static int
289parse_credential (void *cls, json_t *root, struct GNUNET_JSON_Specification *spec)
290{
291 struct GNUNET_RECLAIM_Credential *cred;
292 const char *name_str = NULL;
293 const char *type_str = NULL;
294 const char *id_str = NULL;
295 json_t *val_json;
296 char *data = NULL;
297 char *val_str = NULL;
298 int unpack_state;
299 uint32_t type;
300 size_t data_size;
301
302 GNUNET_assert (NULL != root);
303
304 if (! json_is_object (root))
305 {
306 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
307 "Error json is not array nor object!\n");
308 return GNUNET_SYSERR;
309 }
310 // interpret single attribute
311 unpack_state = json_unpack (root,
312 "{s:s, s?s, s:s, s:o!}",
313 "name",
314 &name_str,
315 "id",
316 &id_str,
317 "type",
318 &type_str,
319 "value",
320 &val_json);
321 if ((0 != unpack_state) || (NULL == name_str) || (NULL == val_json) ||
322 (NULL == type_str))
323 {
324 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
325 "Error json object has a wrong format!\n");
326 return GNUNET_SYSERR;
327 }
328 if (json_is_string (val_json)) {
329 val_str = GNUNET_strdup (json_string_value (val_json));
330 } else {
331 val_str = json_dumps (val_json, JSON_COMPACT);
332 }
333 type = GNUNET_RECLAIM_credential_typename_to_number (type_str);
334 if (GNUNET_SYSERR ==
335 (GNUNET_RECLAIM_credential_string_to_value (type,
336 val_str,
337 (void **) &data,
338 &data_size)))
339 {
340 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Credential value invalid!\n");
341 return GNUNET_SYSERR;
342 }
343 cred = GNUNET_RECLAIM_credential_new (name_str, type, data, data_size);
344 GNUNET_free (data);
345 if ((NULL == id_str) || (0 == strlen (id_str)))
346 memset (&cred->id, 0, sizeof (cred->id));
347 else
348 GNUNET_STRINGS_string_to_data (id_str,
349 strlen (id_str),
350 &cred->id,
351 sizeof(cred->id));
352
353 *(struct GNUNET_RECLAIM_Credential **) spec->ptr = cred;
354 return GNUNET_OK;
355}
356
357
358/**
359 * Cleanup data left from parsing RSA public key.
360 *
361 * @param cls closure, NULL
362 * @param[out] spec where to free the data
363 */
364static void
365clean_credential (void *cls, struct GNUNET_JSON_Specification *spec)
366{
367 struct GNUNET_RECLAIM_Credential **attr;
368
369 attr = (struct GNUNET_RECLAIM_Credential **) spec->ptr;
370 if (NULL != *attr)
371 {
372 GNUNET_free (*attr);
373 *attr = NULL;
374 }
375}
376
377
378/**
379 * JSON Specification for credential claims.
380 *
381 * @param attr struct of GNUNET_RECLAIM_Credential to fill
382 * @return JSON Specification
383 */
384struct GNUNET_JSON_Specification
385GNUNET_RECLAIM_JSON_spec_credential (struct
386 GNUNET_RECLAIM_Credential **cred)
387{
388 struct GNUNET_JSON_Specification ret = { .parser = &parse_credential,
389 .cleaner = &clean_credential,
390 .cls = NULL,
391 .field = NULL,
392 .ptr = cred,
393 .ptr_size = 0,
394 .size_ptr = NULL };
395
396 *cred = NULL;
397 return ret;
398}
diff --git a/src/service/rest/json_reclaim.h b/src/service/rest/json_reclaim.h
new file mode 100644
index 000000000..613ddf873
--- /dev/null
+++ b/src/service/rest/json_reclaim.h
@@ -0,0 +1,57 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2018 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 rest-plugins/json_reclaim.h
23 * @brief JSON handling of reclaim data
24 * @author Martin Schanzenbach
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_json_lib.h"
29#include "gnunet_reclaim_service.h"
30#include "gnunet_reclaim_lib.h"
31
32/**
33 * JSON Specification for Reclaim claims.
34 *
35 * @param attr struct of GNUNET_RECLAIM_Attribute to fill
36 * @return JSON Specification
37 */
38struct GNUNET_JSON_Specification
39GNUNET_RECLAIM_JSON_spec_attribute (struct GNUNET_RECLAIM_Attribute **attr);
40
41/**
42 * JSON Specification for Reclaim tickets.
43 *
44 * @param ticket struct of GNUNET_RECLAIM_Ticket to fill
45 * @return JSON Specification
46 */
47struct GNUNET_JSON_Specification
48GNUNET_RECLAIM_JSON_spec_ticket (struct GNUNET_RECLAIM_Ticket **ticket);
49
50/**
51 * JSON Specification for credentials.
52 *
53 * @param cred struct of GNUNET_RECLAIM_Credential to fill
54 * @return JSON Specification
55 */
56struct GNUNET_JSON_Specification
57GNUNET_RECLAIM_JSON_spec_credential (struct GNUNET_RECLAIM_Credential **cred);
diff --git a/src/service/rest/meson.build b/src/service/rest/meson.build
index 316724fdb..347cbb852 100644
--- a/src/service/rest/meson.build
+++ b/src/service/rest/meson.build
@@ -1,6 +1,16 @@
1libgnunetrest_src = ['rest.c'] 1libgnunetrest_src = ['rest.c']
2 2
3gnunetservicerest_src = ['gnunet-rest-server.c'] 3gnunetservicerest_src = ['gnunet-rest-server.c',
4 'config_plugin.c',
5 'copying_plugin.c',
6 'identity_plugin.c',
7 'namestore_plugin.c',
8 'gns_plugin.c',
9 'openid_plugin.c',
10 'oidc_helper.c',
11 'json_reclaim.c',
12 'reclaim_plugin.c',
13 ]
4 14
5configure_file(input : 'rest.conf', 15configure_file(input : 'rest.conf',
6 output : 'rest.conf', 16 output : 'rest.conf',
@@ -30,7 +40,19 @@ libgnunetrest_dep = declare_dependency(link_with : libgnunetrest)
30 40
31executable ('gnunet-rest-server', 41executable ('gnunet-rest-server',
32 gnunetservicerest_src, 42 gnunetservicerest_src,
33 dependencies: [libgnunetrest_dep, libgnunetutil_dep, mhd_dep], 43 dependencies: [libgnunetrest_dep,
44 libgnunetutil_dep,
45 libgnunetidentity_dep,
46 libgnunetgns_dep,
47 libgnunetreclaim_dep,
48 libgnunetnamestore_dep,
49 libgnunetjson_dep,
50 libgnunetgnsrecord_dep,
51 libgnunetgnsrecordjson_dep,
52 json_dep,
53 jose_dep,
54 gcrypt_dep,
55 mhd_dep],
34 include_directories: [incdir, configuration_inc], 56 include_directories: [incdir, configuration_inc],
35 install: true, 57 install: true,
36 install_dir: get_option('libdir') / 'gnunet' / 'libexec') 58 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
diff --git a/src/service/rest/namestore_plugin.c b/src/service/rest/namestore_plugin.c
new file mode 100644
index 000000000..8c5b8b824
--- /dev/null
+++ b/src/service/rest/namestore_plugin.c
@@ -0,0 +1,1364 @@
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 *ns_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
791
792static void
793bulk_tx_start (void *cls, enum GNUNET_ErrorCode ec)
794{
795 struct RequestHandle *handle = cls;
796 json_t *data_js;
797 json_error_t err;
798
799 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
800 "Transaction started...\n");
801 handle->ec = ec;
802 if (GNUNET_EC_NONE != ec)
803 {
804 GNUNET_SCHEDULER_add_now (&do_error, handle);
805 return;
806 }
807 if (0 >= handle->rest_handle->data_size)
808 {
809 handle->ec = GNUNET_EC_NAMESTORE_NO_RECORDS_GIVEN;
810 GNUNET_SCHEDULER_add_now (&do_error, handle);
811 return;
812 }
813 char term_data[handle->rest_handle->data_size + 1];
814 term_data[handle->rest_handle->data_size] = '\0';
815 GNUNET_memcpy (term_data,
816 handle->rest_handle->data,
817 handle->rest_handle->data_size);
818 data_js = json_loads (term_data, JSON_DECODE_ANY, &err);
819 if (NULL == data_js)
820 {
821 handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID;
822 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
823 "Error parsing data: %s", err.text);
824 GNUNET_SCHEDULER_add_now (&do_error, handle);
825 return;
826 }
827 if (! json_is_array (data_js))
828 {
829 handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID;
830 GNUNET_SCHEDULER_add_now (&do_error, handle);
831 json_decref (data_js);
832 return;
833 }
834 handle->rd_set_count = json_array_size (data_js);
835 handle->ri = GNUNET_malloc (handle->rd_set_count
836 * sizeof (struct GNUNET_NAMESTORE_RecordInfo));
837 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
838 "Got record set of size %d\n", handle->rd_set_count);
839 char *albl;
840 size_t index;
841 json_t *value;
842 json_array_foreach (data_js, index, value) {
843 {
844 struct GNUNET_GNSRECORD_Data *rd;
845 struct GNUNET_JSON_Specification gnsspec[] =
846 { GNUNET_GNSRECORD_JSON_spec_gnsrecord (&rd,
847 &handle->ri[index].a_rd_count,
848 &albl),
849 GNUNET_JSON_spec_end () };
850 if (GNUNET_OK != GNUNET_JSON_parse (value, gnsspec, NULL, NULL))
851 {
852 handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID;
853 GNUNET_SCHEDULER_add_now (&do_error, handle);
854 json_decref (data_js);
855 return;
856 }
857 handle->ri[index].a_rd = rd;
858 handle->ri[index].a_label = albl;
859 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
860 "Parsed record set for name %s\n",
861 handle->ri[index].a_label);
862 }
863 }
864 // json_decref (data_js);
865
866 unsigned int sent_rds = 0;
867 // Find the smallest set of records we can send with our message size
868 // restriction of 16 bit
869 handle->ns_qe = GNUNET_NAMESTORE_records_store2 (handle->nc,
870 handle->zone_pkey,
871 handle->rd_set_count,
872 handle->ri,
873 &sent_rds,
874 &import_next_cb,
875 handle);
876 if ((NULL == handle->ns_qe) && (0 == sent_rds))
877 {
878 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
879 GNUNET_SCHEDULER_add_now (&do_error, handle);
880 return;
881 }
882 handle->rd_set_pos += sent_rds;
883}
884
885
886/**
887 * Handle namestore POST import
888 *
889 * @param con_handle the connection handle
890 * @param url the url
891 * @param cls the RequestHandle
892 */
893void
894namestore_import (struct GNUNET_REST_RequestHandle *con_handle,
895 const char *url,
896 void *cls)
897{
898 struct RequestHandle *handle = cls;
899 struct EgoEntry *ego_entry;
900 char *egoname;
901
902 // set zone to name if given
903 if (strlen (GNUNET_REST_API_NS_NAMESTORE_IMPORT) + 1 >= strlen (
904 handle->url))
905 {
906 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
907 GNUNET_SCHEDULER_add_now (&do_error, handle);
908 return;
909 }
910 ego_entry = NULL;
911
912 egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE_IMPORT) + 1];
913 ego_entry = get_egoentry_namestore (handle, egoname);
914
915 if (NULL == ego_entry)
916 {
917 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
918 GNUNET_SCHEDULER_add_now (&do_error, handle);
919 return;
920 }
921 handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
922
923 // We need a per-client connection for a transactional bulk import
924 handle->nc = GNUNET_NAMESTORE_connect (ns_cfg);
925 if (NULL == handle->nc)
926 {
927 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
928 GNUNET_SCHEDULER_add_now (&do_error, handle);
929 return;
930 }
931 handle->ns_qe = GNUNET_NAMESTORE_transaction_begin (handle->nc,
932 &bulk_tx_start,
933 handle);
934}
935
936
937/**
938 * Handle namestore POST/PUT request
939 *
940 * @param con_handle the connection handle
941 * @param url the url
942 * @param cls the RequestHandle
943 */
944void
945namestore_add_or_update (struct GNUNET_REST_RequestHandle *con_handle,
946 const char *url,
947 void *cls)
948{
949 struct RequestHandle *handle = cls;
950 struct EgoEntry *ego_entry;
951 char *egoname;
952 json_t *data_js;
953 json_error_t err;
954
955 char term_data[handle->rest_handle->data_size + 1];
956
957 if (0 >= handle->rest_handle->data_size)
958 {
959 handle->ec = GNUNET_EC_NAMESTORE_NO_RECORDS_GIVEN;
960 GNUNET_SCHEDULER_add_now (&do_error, handle);
961 return;
962 }
963 term_data[handle->rest_handle->data_size] = '\0';
964 GNUNET_memcpy (term_data,
965 handle->rest_handle->data,
966 handle->rest_handle->data_size);
967 data_js = json_loads (term_data, JSON_DECODE_ANY, &err);
968 struct GNUNET_JSON_Specification gnsspec[] =
969 { GNUNET_GNSRECORD_JSON_spec_gnsrecord (&handle->rd, &handle->rd_count,
970 &handle->record_name),
971 GNUNET_JSON_spec_end () };
972 if (GNUNET_OK != GNUNET_JSON_parse (data_js, gnsspec, NULL, NULL))
973 {
974 handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID;
975 GNUNET_SCHEDULER_add_now (&do_error, handle);
976 json_decref (data_js);
977 return;
978 }
979 GNUNET_JSON_parse_free (gnsspec);
980 if (0 >= strlen (handle->record_name))
981 {
982 handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID;
983 GNUNET_SCHEDULER_add_now (&do_error, handle);
984 json_decref (data_js);
985 return;
986 }
987 json_decref (data_js);
988
989 egoname = NULL;
990 ego_entry = NULL;
991
992 // set zone to name if given
993 if (strlen (GNUNET_REST_API_NS_NAMESTORE) + 1 >= strlen (handle->url))
994 {
995 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
996 GNUNET_SCHEDULER_add_now (&do_error, handle);
997 return;
998 }
999 egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE) + 1];
1000 ego_entry = get_egoentry_namestore (handle, egoname);
1001
1002 if (NULL == ego_entry)
1003 {
1004 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
1005 GNUNET_SCHEDULER_add_now (&do_error, handle);
1006 return;
1007 }
1008 handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1009 handle->ns_qe = GNUNET_NAMESTORE_records_lookup (ns_handle,
1010 handle->zone_pkey,
1011 handle->record_name,
1012 &ns_lookup_error_cb,
1013 handle,
1014 &ns_lookup_cb,
1015 handle);
1016 if (NULL == handle->ns_qe)
1017 {
1018 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
1019 GNUNET_SCHEDULER_add_now (&do_error, handle);
1020 return;
1021 }
1022}
1023
1024
1025/**
1026 * Handle namestore PUT request
1027 *
1028 * @param con_handle the connection handle
1029 * @param url the url
1030 * @param cls the RequestHandle
1031 */
1032void
1033namestore_update (struct GNUNET_REST_RequestHandle *con_handle,
1034 const char *url,
1035 void *cls)
1036{
1037 struct RequestHandle *handle = cls;
1038 handle->update_strategy = UPDATE_STRATEGY_REPLACE;
1039 namestore_add_or_update (con_handle, url, cls);
1040}
1041
1042
1043/**
1044 * Handle namestore POST request
1045 *
1046 * @param con_handle the connection handle
1047 * @param url the url
1048 * @param cls the RequestHandle
1049 */
1050void
1051namestore_add (struct GNUNET_REST_RequestHandle *con_handle,
1052 const char *url,
1053 void *cls)
1054{
1055 struct RequestHandle *handle = cls;
1056 handle->update_strategy = UPDATE_STRATEGY_APPEND;
1057 namestore_add_or_update (con_handle, url, cls);
1058}
1059
1060
1061/**
1062 * Handle namestore DELETE request
1063 *
1064 * @param con_handle the connection handle
1065 * @param url the url
1066 * @param cls the RequestHandle
1067 */
1068void
1069namestore_delete (struct GNUNET_REST_RequestHandle *con_handle,
1070 const char *url,
1071 void *cls)
1072{
1073 struct RequestHandle *handle = cls;
1074 struct EgoEntry *ego_entry;
1075 char *egoname;
1076 char *labelname;
1077
1078 egoname = NULL;
1079 ego_entry = NULL;
1080
1081 // set zone to name if given
1082 if (strlen (GNUNET_REST_API_NS_NAMESTORE) + 1 >= strlen (handle->url))
1083 {
1084 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
1085 GNUNET_SCHEDULER_add_now (&do_error, handle);
1086 return;
1087 }
1088 egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE) + 1];
1089 ego_entry = get_egoentry_namestore (handle, egoname);
1090 if (NULL == ego_entry)
1091 {
1092 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
1093 GNUNET_SCHEDULER_add_now (&do_error, handle);
1094 return;
1095 }
1096 handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1097 labelname = &egoname[strlen (ego_entry->identifier)];
1098 // set zone to name if given
1099 if (1 >= strlen (labelname))
1100 {
1101 /* label is only "/" */
1102 handle->ec = GNUNET_EC_NAMESTORE_NO_LABEL_GIVEN;
1103 GNUNET_SCHEDULER_add_now (&do_error, handle);
1104 }
1105
1106 handle->record_name = GNUNET_strdup (labelname + 1);
1107 handle->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle,
1108 handle->zone_pkey,
1109 handle->record_name,
1110 0,
1111 NULL,
1112 &del_finished,
1113 handle);
1114 if (NULL == handle->ns_qe)
1115 {
1116 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
1117 GNUNET_SCHEDULER_add_now (&do_error, handle);
1118 return;
1119 }
1120}
1121
1122
1123/**
1124 * Respond to OPTIONS request
1125 *
1126 * @param con_handle the connection handle
1127 * @param url the url
1128 * @param cls the RequestHandle
1129 */
1130static void
1131options_cont (struct GNUNET_REST_RequestHandle *con_handle,
1132 const char *url,
1133 void *cls)
1134{
1135 struct MHD_Response *resp;
1136 struct RequestHandle *handle = cls;
1137
1138 // independent of path return all options
1139 resp = GNUNET_REST_create_response (NULL);
1140 GNUNET_assert (MHD_YES ==
1141 MHD_add_response_header (resp,
1142 "Access-Control-Allow-Methods",
1143 allow_methods));
1144 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1145 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
1146 return;
1147}
1148
1149
1150static void
1151list_ego (void *cls,
1152 struct GNUNET_IDENTITY_Ego *ego,
1153 void **ctx,
1154 const char *identifier)
1155{
1156 struct EgoEntry *ego_entry;
1157 struct GNUNET_CRYPTO_PublicKey pk;
1158
1159 if ((NULL == ego) && (ID_REST_STATE_INIT == state))
1160 {
1161 state = ID_REST_STATE_POST_INIT;
1162 return;
1163 }
1164 if (NULL == ego)
1165 {
1166 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1167 "Called with NULL ego\n");
1168 return;
1169 }
1170 if (ID_REST_STATE_INIT == state)
1171 {
1172 ego_entry = GNUNET_new (struct EgoEntry);
1173 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1174 ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk);
1175 ego_entry->ego = ego;
1176 ego_entry->identifier = GNUNET_strdup (identifier);
1177 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
1178 ego_tail,
1179 ego_entry);
1180 }
1181 /* Ego renamed or added */
1182 if (identifier != NULL)
1183 {
1184 for (ego_entry = ego_head; NULL != ego_entry;
1185 ego_entry = ego_entry->next)
1186 {
1187 if (ego_entry->ego == ego)
1188 {
1189 /* Rename */
1190 GNUNET_free (ego_entry->identifier);
1191 ego_entry->identifier = GNUNET_strdup (identifier);
1192 break;
1193 }
1194 }
1195 if (NULL == ego_entry)
1196 {
1197 /* Add */
1198 ego_entry = GNUNET_new (struct EgoEntry);
1199 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1200 ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk);
1201 ego_entry->ego = ego;
1202 ego_entry->identifier = GNUNET_strdup (identifier);
1203 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
1204 ego_tail,
1205 ego_entry);
1206 }
1207 }
1208 else
1209 {
1210 /* Delete */
1211 for (ego_entry = ego_head; NULL != ego_entry;
1212 ego_entry = ego_entry->next)
1213 {
1214 if (ego_entry->ego == ego)
1215 break;
1216 }
1217 if (NULL == ego_entry)
1218 return; /* Not found */
1219
1220 GNUNET_CONTAINER_DLL_remove (ego_head,
1221 ego_tail,
1222 ego_entry);
1223 GNUNET_free (ego_entry->identifier);
1224 GNUNET_free (ego_entry->keystring);
1225 GNUNET_free (ego_entry);
1226 return;
1227 }
1228
1229}
1230
1231
1232/**
1233 * Function processing the REST call
1234 *
1235 * @param method HTTP method
1236 * @param url URL of the HTTP request
1237 * @param data body of the HTTP request (optional)
1238 * @param data_size length of the body
1239 * @param proc callback function for the result
1240 * @param proc_cls closure for callback function
1241 * @return GNUNET_OK if request accepted
1242 */
1243enum GNUNET_GenericReturnValue
1244REST_namestore_process_request (void *plugin,
1245 struct GNUNET_REST_RequestHandle *rest_handle,
1246 GNUNET_REST_ResultProcessor proc,
1247 void *proc_cls)
1248{
1249 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
1250 struct GNUNET_REST_RequestHandlerError err;
1251 static const struct GNUNET_REST_RequestHandler handlers[] =
1252 { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_NAMESTORE, &namestore_get },
1253 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_NAMESTORE_IMPORT,
1254 &namestore_import },
1255 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_NAMESTORE, &namestore_add },
1256 { MHD_HTTP_METHOD_PUT, GNUNET_REST_API_NS_NAMESTORE, &namestore_update },
1257 { MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_NAMESTORE,
1258 &namestore_delete },
1259 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_NAMESTORE, &options_cont },
1260 GNUNET_REST_HANDLER_END };
1261
1262 handle->ec = GNUNET_EC_NONE;
1263 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1264 handle->proc_cls = proc_cls;
1265 handle->proc = proc;
1266 handle->rest_handle = rest_handle;
1267 handle->zone_pkey = NULL;
1268 handle->timeout_task =
1269 GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_error, handle);
1270 handle->url = GNUNET_strdup (rest_handle->url);
1271 if (handle->url[strlen (handle->url) - 1] == '/')
1272 handle->url[strlen (handle->url) - 1] = '\0';
1273 GNUNET_CONTAINER_DLL_insert (requests_head,
1274 requests_tail,
1275 handle);
1276 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
1277 if (GNUNET_NO ==
1278 GNUNET_REST_handle_request (handle->rest_handle, handlers, &err,
1279 handle))
1280 {
1281 cleanup_handle (handle);
1282 return GNUNET_NO;
1283 }
1284
1285 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
1286 return GNUNET_YES;
1287}
1288
1289
1290/**
1291 * Entry point for the plugin.
1292 *
1293 * @param cls Config info
1294 * @return NULL on error, otherwise the plugin context
1295 */
1296void *
1297REST_namestore_init (const struct GNUNET_CONFIGURATION_Handle *c)
1298{
1299 static struct Plugin plugin;
1300 struct GNUNET_REST_Plugin *api;
1301
1302 ns_cfg = c;
1303 if (NULL != plugin.cfg)
1304 return NULL; /* can only initialize once! */
1305 memset (&plugin, 0, sizeof(struct Plugin));
1306 plugin.cfg = c;
1307 api = GNUNET_new (struct GNUNET_REST_Plugin);
1308 api->cls = &plugin;
1309 api->name = GNUNET_REST_API_NS_NAMESTORE;
1310 state = ID_REST_STATE_INIT;
1311 GNUNET_asprintf (&allow_methods,
1312 "%s, %s, %s, %s, %s",
1313 MHD_HTTP_METHOD_GET,
1314 MHD_HTTP_METHOD_POST,
1315 MHD_HTTP_METHOD_PUT,
1316 MHD_HTTP_METHOD_DELETE,
1317 MHD_HTTP_METHOD_OPTIONS);
1318 ns_handle = GNUNET_NAMESTORE_connect (ns_cfg);
1319 identity_handle = GNUNET_IDENTITY_connect (ns_cfg, &list_ego, NULL);
1320
1321 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ (
1322 "Namestore REST API initialized\n"));
1323 return api;
1324}
1325
1326
1327/**
1328 * Exit point from the plugin.
1329 *
1330 * @param cls the plugin context (as returned by "init")
1331 * @return always NULL
1332 */
1333void
1334REST_namestore_done (struct GNUNET_REST_Plugin *api)
1335{
1336 struct Plugin *plugin = api->cls;
1337 struct RequestHandle *request;
1338 struct EgoEntry *ego_entry;
1339 struct EgoEntry *ego_tmp;
1340
1341 plugin->cfg = NULL;
1342 while (NULL != (request = requests_head))
1343 do_error (request);
1344 if (NULL != identity_handle)
1345 GNUNET_IDENTITY_disconnect (identity_handle);
1346 if (NULL != ns_handle)
1347 GNUNET_NAMESTORE_disconnect (ns_handle);
1348
1349 for (ego_entry = ego_head; NULL != ego_entry;)
1350 {
1351 ego_tmp = ego_entry;
1352 ego_entry = ego_entry->next;
1353 GNUNET_free (ego_tmp->identifier);
1354 GNUNET_free (ego_tmp->keystring);
1355 GNUNET_free (ego_tmp);
1356 }
1357
1358 GNUNET_free (allow_methods);
1359 GNUNET_free (api);
1360 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Namestore REST plugin is finished\n");
1361}
1362
1363
1364/* end of plugin_rest_namestore.c */
diff --git a/src/service/rest/namestore_plugin.h b/src/service/rest/namestore_plugin.h
new file mode 100644
index 000000000..2bdf77546
--- /dev/null
+++ b/src/service/rest/namestore_plugin.h
@@ -0,0 +1,37 @@
1#include "gnunet_rest_plugin.h"
2/**
3 * Function processing the REST call
4 *
5 * @param method HTTP method
6 * @param url URL of the HTTP request
7 * @param data body of the HTTP request (optional)
8 * @param data_size length of the body
9 * @param proc callback function for the result
10 * @param proc_cls closure for @a proc
11 * @return #GNUNET_OK if request accepted
12 */
13enum GNUNET_GenericReturnValue
14REST_namestore_process_request (void *plugin,
15 struct GNUNET_REST_RequestHandle *
16 conndata_handle,
17 GNUNET_REST_ResultProcessor proc,
18 void *proc_cls);
19
20/**
21 * Entry point for the plugin.
22 *
23 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
24 * @return NULL on error, otherwise the plugin context
25 */
26void*
27REST_namestore_init (const struct GNUNET_CONFIGURATION_Handle *c);
28
29
30/**
31 * Exit point from the plugin.
32 *
33 * @param cls the plugin context (as returned by "init")
34 * @return always NULL
35 */
36void
37REST_namestore_done (struct GNUNET_REST_Plugin *api);
diff --git a/src/service/rest/oidc_helper.c b/src/service/rest/oidc_helper.c
new file mode 100644
index 000000000..a2da7312b
--- /dev/null
+++ b/src/service/rest/oidc_helper.c
@@ -0,0 +1,1026 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2010-2015 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file reclaim/oidc_helper.c
23 * @brief helper library for OIDC related functions
24 * @author Martin Schanzenbach
25 * @author Tristan Schwieren
26 */
27#include "platform.h"
28#include <inttypes.h>
29#include <jansson.h>
30#include <jose/jose.h>
31#include "gnunet_util_lib.h"
32#include "gnunet_reclaim_lib.h"
33#include "gnunet_reclaim_service.h"
34#include "gnunet_signatures.h"
35#include "oidc_helper.h"
36// #include "benchmark.h"
37#include <gcrypt.h>
38
39GNUNET_NETWORK_STRUCT_BEGIN
40
41/**
42 * The signature used to generate the authorization code
43 */
44struct OIDC_Parameters
45{
46 /**
47 * The reclaim ticket
48 */
49 struct GNUNET_RECLAIM_Ticket ticket;
50
51 /**
52 * The nonce length
53 */
54 uint32_t nonce_len GNUNET_PACKED;
55
56 /**
57 * The length of the PKCE code_challenge
58 */
59 uint32_t code_challenge_len GNUNET_PACKED;
60
61 /**
62 * The length of the attributes list
63 */
64 uint32_t attr_list_len GNUNET_PACKED;
65
66 /**
67 * The length of the presentation list
68 */
69 uint32_t pres_list_len GNUNET_PACKED;
70};
71
72GNUNET_NETWORK_STRUCT_END
73
74/**
75 * Standard claims represented by the "profile" scope in OIDC
76 */
77static char OIDC_profile_claims[14][32] = {
78 "name", "family_name", "given_name", "middle_name", "nickname",
79 "preferred_username", "profile", "picture", "website", "gender", "birthdate",
80 "zoneinfo", "locale", "updated_at"
81};
82
83/**
84 * Standard claims represented by the "email" scope in OIDC
85 */
86static char OIDC_email_claims[2][16] = {
87 "email", "email_verified"
88};
89
90/**
91 * Standard claims represented by the "phone" scope in OIDC
92 */
93static char OIDC_phone_claims[2][32] = {
94 "phone_number", "phone_number_verified"
95};
96
97/**
98 * Standard claims represented by the "address" scope in OIDC
99 */
100static char OIDC_address_claims[5][32] = {
101 "street_address", "locality", "region", "postal_code", "country"
102};
103
104static enum GNUNET_GenericReturnValue
105is_claim_in_address_scope (const char *claim)
106{
107 int i;
108 for (i = 0; i < 5; i++)
109 {
110 if (0 == strcmp (claim, OIDC_address_claims[i]))
111 {
112 return GNUNET_YES;
113 }
114 }
115 return GNUNET_NO;
116}
117
118
119static char *
120create_jwt_hmac_header (void)
121{
122 json_t *root;
123 char *json_str;
124
125 root = json_object ();
126 json_object_set_new (root, JWT_ALG, json_string (JWT_ALG_VALUE_HMAC));
127 json_object_set_new (root, JWT_TYP, json_string (JWT_TYP_VALUE));
128
129 json_str = json_dumps (root, JSON_INDENT (0) | JSON_COMPACT);
130 json_decref (root);
131 return json_str;
132}
133
134
135static void
136replace_char (char *str, char find, char replace)
137{
138 char *current_pos = strchr (str, find);
139
140 while (current_pos)
141 {
142 *current_pos = replace;
143 current_pos = strchr (current_pos, find);
144 }
145}
146
147
148// RFC4648
149static void
150fix_base64 (char *str)
151{
152 // Replace + with -
153 replace_char (str, '+', '-');
154
155 // Replace / with _
156 replace_char (str, '/', '_');
157}
158
159
160static json_t*
161generate_userinfo_json (const struct GNUNET_CRYPTO_PublicKey *sub_key,
162 const struct GNUNET_RECLAIM_AttributeList *attrs,
163 const struct
164 GNUNET_RECLAIM_PresentationList *presentations)
165{
166 struct GNUNET_RECLAIM_AttributeListEntry *le;
167 struct GNUNET_RECLAIM_PresentationListEntry *ple;
168 char *subject;
169 char *source_name;
170 char *attr_val_str;
171 char *pres_val_str;
172 json_t *body;
173 json_t *aggr_names;
174 json_t *aggr_sources;
175 json_t *aggr_sources_jwt;
176 json_t *addr_claim = NULL;
177 int num_presentations = 0;
178 for (le = attrs->list_head; NULL != le; le = le->next)
179 {
180 if (GNUNET_NO == GNUNET_RECLAIM_id_is_zero (&le->attribute->credential))
181 num_presentations++;
182 }
183
184 subject =
185 GNUNET_STRINGS_data_to_string_alloc (sub_key,
186 sizeof(struct
187 GNUNET_CRYPTO_PublicKey));
188 body = json_object ();
189 aggr_names = json_object ();
190 aggr_sources = json_object ();
191
192 // iss REQUIRED case sensitive server uri with https
193 // The issuer is the local reclaim instance (e.g.
194 // https://reclaim.id/api/openid)
195 json_object_set_new (body, "iss", json_string (SERVER_ADDRESS));
196 // sub REQUIRED public key identity, not exceed 255 ASCII length
197 json_object_set_new (body, "sub", json_string (subject));
198 GNUNET_free (subject);
199 pres_val_str = NULL;
200 source_name = NULL;
201 int i = 0;
202 for (ple = presentations->list_head; NULL != ple; ple = ple->next)
203 {
204 // New presentation
205 GNUNET_asprintf (&source_name,
206 "src%d",
207 i);
208 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
209 "Adding new presentation source #%d\n", i);
210 aggr_sources_jwt = json_object ();
211 pres_val_str =
212 GNUNET_RECLAIM_presentation_value_to_string (ple->presentation->type,
213 ple->presentation->data,
214 ple->presentation->data_size);
215 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
216 "Presentation is: %s\n", pres_val_str);
217 json_object_set_new (aggr_sources_jwt,
218 GNUNET_RECLAIM_presentation_number_to_typename (
219 ple->presentation->type),
220 json_string (pres_val_str) );
221 json_object_set_new (aggr_sources, source_name, aggr_sources_jwt);
222 GNUNET_free (pres_val_str);
223 GNUNET_free (source_name);
224 source_name = NULL;
225 i++;
226 }
227
228 int addr_is_aggregated = GNUNET_NO;
229 int addr_is_normal = GNUNET_NO;
230 for (le = attrs->list_head; NULL != le; le = le->next)
231 {
232 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
233 "Processing %s for userinfo body\n",
234 le->attribute->name);
235 if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&le->attribute->credential))
236 {
237 attr_val_str =
238 GNUNET_RECLAIM_attribute_value_to_string (le->attribute->type,
239 le->attribute->data,
240 le->attribute->data_size);
241 /**
242 * There is this weird quirk that the individual address claim(s) must be
243 * inside a JSON object of the "address" claim.
244 */
245 if (GNUNET_YES == is_claim_in_address_scope (le->attribute->name))
246 {
247 if (GNUNET_YES == addr_is_aggregated)
248 {
249 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
250 "Address is set as aggregated claim. Skipping self-issued value...\n");
251 GNUNET_free (attr_val_str);
252 continue;
253 }
254 addr_is_normal = GNUNET_YES;
255
256 if (NULL == addr_claim)
257 {
258 addr_claim = json_object ();
259 json_object_set_new (body, "address", addr_claim);
260 }
261 json_object_set_new (addr_claim, le->attribute->name,
262 json_string (attr_val_str));
263
264 }
265 else
266 {
267 json_object_set_new (body, le->attribute->name,
268 json_string (attr_val_str));
269 }
270 GNUNET_free (attr_val_str);
271 }
272 else
273 {
274 // Check if presentation is there
275 int j = 0;
276 for (ple = presentations->list_head; NULL != ple; ple = ple->next)
277 {
278 if (GNUNET_YES ==
279 GNUNET_RECLAIM_id_is_equal (&ple->presentation->credential_id,
280 &le->attribute->credential))
281 break;
282 j++;
283 }
284 if (NULL == ple)
285 {
286 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
287 "Presentation for `%s' missing...\n",
288 le->attribute->name);
289 continue;
290 }
291 /**
292 * There is this weird quirk that the individual address claim(s) must be
293 * inside a JSON object of the "address" claim.
294 */
295 if (GNUNET_YES == is_claim_in_address_scope (le->attribute->name))
296 {
297 if (GNUNET_YES == addr_is_normal)
298 {
299 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
300 "Address is already set as normal claim. Skipping attested value...\n");
301 continue;
302 }
303 addr_is_aggregated = GNUNET_YES;
304 /** This is/can only be set once! **/
305 if (NULL != addr_claim)
306 continue;
307 addr_claim = json_object ();
308 GNUNET_asprintf (&source_name,
309 "src%d",
310 j);
311 json_object_set_new (aggr_names, "address",
312 json_string (source_name));
313 GNUNET_free (source_name);
314 }
315 else
316 {
317 // Presentation exists, hence take the respective source str
318 GNUNET_asprintf (&source_name,
319 "src%d",
320 j);
321 json_object_set_new (aggr_names, le->attribute->name,
322 json_string (source_name));
323 GNUNET_free (source_name);
324 }
325 }
326 }
327 if (0 != i)
328 {
329 json_object_set_new (body, "_claim_names", aggr_names);
330 json_object_set_new (body, "_claim_sources", aggr_sources);
331 }
332
333 return body;
334}
335
336
337/**
338 * Generate userinfo JSON as string
339 *
340 * @param sub_key the subject (user)
341 * @param attrs user attribute list
342 * @param presentations credential presentation list (may be empty)
343 * @return Userinfo JSON
344 */
345char *
346OIDC_generate_userinfo (const struct GNUNET_CRYPTO_PublicKey *sub_key,
347 const struct GNUNET_RECLAIM_AttributeList *attrs,
348 const struct
349 GNUNET_RECLAIM_PresentationList *presentations)
350{
351 char *body_str;
352 json_t*body = generate_userinfo_json (sub_key,
353 attrs,
354 presentations);
355 body_str = json_dumps (body, JSON_INDENT (0) | JSON_COMPACT);
356 json_decref (body);
357 return body_str;
358}
359
360
361char *
362generate_id_token_body (const struct GNUNET_CRYPTO_PublicKey *aud_key,
363 const struct GNUNET_CRYPTO_PublicKey *sub_key,
364 const struct GNUNET_RECLAIM_AttributeList *attrs,
365 const struct
366 GNUNET_RECLAIM_PresentationList *presentations,
367 const struct GNUNET_TIME_Relative *expiration_time,
368 const char *nonce)
369{
370 struct GNUNET_TIME_Absolute exp_time;
371 struct GNUNET_TIME_Absolute time_now;
372 json_t *body;
373 char *audience;
374 char *subject;
375 char *body_str;
376
377 body = generate_userinfo_json (sub_key,
378 attrs,
379 presentations);
380 // iat REQUIRED time now
381 time_now = GNUNET_TIME_absolute_get ();
382 // exp REQUIRED time expired from config
383 exp_time = GNUNET_TIME_absolute_add (time_now, *expiration_time);
384 // auth_time only if max_age
385 // nonce only if nonce
386 // OPTIONAL acr,amr,azp
387 subject =
388 GNUNET_STRINGS_data_to_string_alloc (sub_key,
389 sizeof(struct
390 GNUNET_CRYPTO_PublicKey));
391 audience =
392 GNUNET_STRINGS_data_to_string_alloc (aud_key,
393 sizeof(struct
394 GNUNET_CRYPTO_PublicKey));
395
396 // aud REQUIRED public key client_id must be there
397 json_object_set_new (body, "aud", json_string (audience));
398 // iat
399 json_object_set_new (body,
400 "iat",
401 json_integer (time_now.abs_value_us / (1000 * 1000)));
402 // exp
403 json_object_set_new (body,
404 "exp",
405 json_integer (exp_time.abs_value_us / (1000 * 1000)));
406 // nbf
407 json_object_set_new (body,
408 "nbf",
409 json_integer (time_now.abs_value_us / (1000 * 1000)));
410 // nonce
411 if (NULL != nonce)
412 json_object_set_new (body, "nonce", json_string (nonce));
413
414 // Error checking
415 body_str = json_dumps (body, JSON_INDENT (2) | JSON_COMPACT);
416 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"ID-Token: %s\n", body_str);
417
418 json_decref (body);
419 GNUNET_free (subject);
420 GNUNET_free (audience);
421
422 return body_str;
423}
424
425
426char *
427OIDC_generate_id_token_rsa (const struct GNUNET_CRYPTO_PublicKey *aud_key,
428 const struct GNUNET_CRYPTO_PublicKey *sub_key,
429 const struct GNUNET_RECLAIM_AttributeList *attrs,
430 const struct
431 GNUNET_RECLAIM_PresentationList *presentations,
432 const struct GNUNET_TIME_Relative *expiration_time,
433 const char *nonce,
434 const json_t *secret_rsa_key)
435{
436 json_t *jws;
437 char *body_str;
438 char *result;
439
440 // Generate the body of the JSON Web Signature
441 body_str = generate_id_token_body (aud_key,
442 sub_key,
443 attrs,
444 presentations,
445 expiration_time,
446 nonce);
447
448 if (NULL == body_str)
449 {
450 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
451 "Body for the JWS could not be generated\n");
452 return NULL;
453 }
454
455 // Creating the JSON Web Signature.
456 jws = json_pack ("{s:o}", "payload",
457 jose_b64_enc (body_str, strlen (body_str)));
458 GNUNET_free (body_str);
459
460 if (! jose_jws_sig (NULL, jws, NULL, secret_rsa_key))
461 {
462 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
463 "Signature generation failed\n");
464 return NULL;
465 }
466
467 // Encoding JSON as compact JSON Web Signature
468 GNUNET_asprintf (&result, "%s.%s.%s",
469 json_string_value (json_object_get (jws, "protected")),
470 json_string_value (json_object_get (jws, "payload")),
471 json_string_value (json_object_get (jws, "signature")) );
472
473 json_decref (jws);
474 return result;
475}
476
477/**
478 * Create a JWT using HMAC (HS256) from attributes
479 *
480 * @param aud_key the public of the audience
481 * @param sub_key the public key of the subject
482 * @param attrs the attribute list
483 * @param presentations credential presentation list (may be empty)
484 * @param expiration_time the validity of the token
485 * @param secret_key the key used to sign the JWT
486 * @return a new base64-encoded JWT string.
487 */
488char *
489OIDC_generate_id_token_hmac (const struct GNUNET_CRYPTO_PublicKey *aud_key,
490 const struct GNUNET_CRYPTO_PublicKey *sub_key,
491 const struct GNUNET_RECLAIM_AttributeList *attrs,
492 const struct
493 GNUNET_RECLAIM_PresentationList *presentations,
494 const struct GNUNET_TIME_Relative *expiration_time,
495 const char *nonce,
496 const char *secret_key)
497{
498 struct GNUNET_HashCode signature;
499 char *header;
500 char *header_base64;
501 char *body_str;
502 char *body_base64;
503 char *signature_target;
504 char *signature_base64;
505 char *result;
506
507 // Generate and encode Header
508 header = create_jwt_hmac_header ();
509 if (NULL == header)
510 {
511 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
512 "Header for the JWS could not be generated\n");
513 return NULL;
514 }
515 GNUNET_STRINGS_base64url_encode (header, strlen (header), &header_base64);
516 GNUNET_free (header);
517 fix_base64 (header_base64);
518
519 // Generate and encode the body of the JSON Web Signature
520 body_str = generate_id_token_body (aud_key,
521 sub_key,
522 attrs,
523 presentations,
524 expiration_time,
525 nonce);
526
527 if (NULL == body_str)
528 {
529 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
530 "Body for the JWS could not be generated\n");
531 GNUNET_free (header_base64);
532 return NULL;
533 }
534
535 GNUNET_STRINGS_base64url_encode (body_str, strlen (body_str), &body_base64);
536 fix_base64 (body_base64);
537
538 /**
539 * Creating the JWT signature. This might not be
540 * standards compliant, check.
541 */
542 GNUNET_asprintf (&signature_target, "%s.%s", header_base64, body_base64);
543 GNUNET_CRYPTO_hmac_raw (secret_key,
544 strlen (secret_key),
545 signature_target,
546 strlen (signature_target),
547 &signature);
548 GNUNET_STRINGS_base64url_encode ((const char *) &signature,
549 sizeof(struct GNUNET_HashCode),
550 &signature_base64);
551 fix_base64 (signature_base64);
552
553 GNUNET_asprintf (&result,
554 "%s.%s.%s",
555 header_base64,
556 body_base64,
557 signature_base64);
558
559 GNUNET_free (header_base64);
560 GNUNET_free (body_str);
561 GNUNET_free (body_base64);
562 GNUNET_free (signature_target);
563 GNUNET_free (signature_base64);
564 return result;
565}
566
567
568/**
569 * Builds an OIDC authorization code including
570 * a reclaim ticket and nonce
571 *
572 * @param issuer the issuer of the ticket, used to sign the ticket and nonce
573 * @param ticket the ticket to include in the code
574 * @param attrs list of attributes which are shared
575 * @param presentations credential presentation list (may be empty)
576 * @param nonce the nonce to include in the code
577 * @param code_challenge PKCE code challenge
578 * @return a new authorization code (caller must free)
579 */
580char *
581OIDC_build_authz_code (const struct GNUNET_CRYPTO_PrivateKey *issuer,
582 const struct GNUNET_RECLAIM_Ticket *ticket,
583 const struct GNUNET_RECLAIM_AttributeList *attrs,
584 const struct
585 GNUNET_RECLAIM_PresentationList *presentations,
586 const char *nonce_str,
587 const char *code_challenge)
588{
589 struct OIDC_Parameters params;
590 char *code_payload;
591 char *payload;
592 char *tmp;
593 char *code_str;
594 char *buf_ptr = NULL;
595 size_t payload_len;
596 size_t code_payload_len;
597 size_t attr_list_len = 0;
598 size_t pres_list_len = 0;
599 size_t code_challenge_len = 0;
600 uint32_t nonce_len = 0;
601 struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
602
603 /** PLAINTEXT **/
604 // Assign ticket
605 memset (&params, 0, sizeof(params));
606 params.ticket = *ticket;
607 // Assign nonce
608 payload_len = sizeof(struct OIDC_Parameters);
609 if ((NULL != nonce_str) && (strcmp ("", nonce_str) != 0))
610 {
611 nonce_len = strlen (nonce_str);
612 payload_len += nonce_len;
613 }
614 params.nonce_len = htonl (nonce_len);
615 // Assign code challenge
616 if (NULL != code_challenge)
617 code_challenge_len = strlen (code_challenge);
618 payload_len += code_challenge_len;
619 params.code_challenge_len = htonl (code_challenge_len);
620 // Assign attributes
621 if (NULL != attrs)
622 {
623 // Get length
624 attr_list_len = GNUNET_RECLAIM_attribute_list_serialize_get_size (attrs);
625 params.attr_list_len = htonl (attr_list_len);
626 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
627 "Length of serialized attributes: %lu\n",
628 attr_list_len);
629 // Get serialized attributes
630 payload_len += attr_list_len;
631 }
632 if (NULL != presentations)
633 {
634 // Get length
635 // FIXME only add presentations relevant for attribute list!!!
636 // This is important because of the distinction between id_token and
637 // userinfo in OIDC
638 pres_list_len =
639 GNUNET_RECLAIM_presentation_list_serialize_get_size (presentations);
640 params.pres_list_len = htonl (pres_list_len);
641 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
642 "Length of serialized presentations: %lu\n",
643 pres_list_len);
644 // Get serialized attributes
645 payload_len += pres_list_len;
646 }
647
648 // Get plaintext length
649 payload = GNUNET_malloc (payload_len);
650 memcpy (payload, &params, sizeof(params));
651 tmp = payload + sizeof(params);
652 if (0 < code_challenge_len)
653 {
654 memcpy (tmp, code_challenge, code_challenge_len);
655 tmp += code_challenge_len;
656 }
657 if (0 < nonce_len)
658 {
659 memcpy (tmp, nonce_str, nonce_len);
660 tmp += nonce_len;
661 }
662 if (0 < attr_list_len)
663 GNUNET_RECLAIM_attribute_list_serialize (attrs, tmp);
664 tmp += attr_list_len;
665 if (0 < pres_list_len)
666 GNUNET_RECLAIM_presentation_list_serialize (presentations, tmp);
667 tmp += pres_list_len;
668
669 /** END **/
670
671 // Get length
672 code_payload_len = sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
673 + payload_len + sizeof(struct
674 GNUNET_CRYPTO_Signature);
675 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
676 "Length of data to encode: %lu\n",
677 code_payload_len);
678
679 // Initialize code payload
680 code_payload = GNUNET_malloc (code_payload_len);
681 GNUNET_assert (NULL != code_payload);
682 purpose = (struct GNUNET_CRYPTO_EccSignaturePurpose *) code_payload;
683 purpose->size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
684 + payload_len);
685 purpose->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN);
686 // Store pubkey
687 buf_ptr = (char *) &purpose[1];
688 memcpy (buf_ptr, payload, payload_len);
689 GNUNET_free (payload);
690 buf_ptr += payload_len;
691 // Sign and store signature
692 if (GNUNET_SYSERR ==
693 GNUNET_CRYPTO_sign_ (issuer,
694 purpose,
695 (struct GNUNET_CRYPTO_Signature *)
696 buf_ptr))
697 {
698 GNUNET_break (0);
699 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to sign code\n");
700 GNUNET_free (code_payload);
701 return NULL;
702 }
703 GNUNET_STRINGS_base64url_encode (code_payload, code_payload_len, &code_str);
704 GNUNET_free (code_payload);
705 return code_str;
706}
707
708
709enum GNUNET_GenericReturnValue
710check_code_challenge (const char *code_challenge,
711 uint32_t code_challenge_len,
712 const char *code_verifier)
713{
714 char *code_verifier_hash;
715 char *expected_code_challenge;
716
717 if (0 == code_challenge_len) /* Only check if this code requires a CV */
718 return GNUNET_OK;
719 if (NULL == code_verifier)
720 {
721 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
722 "Expected code verifier!\n");
723 return GNUNET_SYSERR;
724 }
725 code_verifier_hash = GNUNET_malloc (256 / 8);
726 // hash code verifier
727 gcry_md_hash_buffer (GCRY_MD_SHA256,
728 code_verifier_hash,
729 code_verifier,
730 strlen (code_verifier));
731 // encode code verifier
732 GNUNET_STRINGS_base64url_encode (code_verifier_hash, 256 / 8,
733 &expected_code_challenge);
734 GNUNET_free (code_verifier_hash);
735 if (0 !=
736 strncmp (expected_code_challenge, code_challenge, code_challenge_len))
737 {
738 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
739 "Invalid code verifier! Expected: %s, Got: %.*s\n",
740 expected_code_challenge,
741 code_challenge_len,
742 code_challenge);
743 GNUNET_free (expected_code_challenge);
744 return GNUNET_SYSERR;
745 }
746 GNUNET_free (expected_code_challenge);
747 return GNUNET_OK;
748}
749
750
751/**
752 * Parse reclaim ticket and nonce from
753 * authorization code.
754 * This also verifies the signature in the code.
755 *
756 * @param audience the expected audience of the code
757 * @param code the string representation of the code
758 * @param code_verfier PKCE code verifier. Optional, must be provided
759 * if used in request.
760 * @param ticket where to store the ticket
761 * @param attrs the attributes in the code
762 * @param presentations credential presentation list
763 * @param nonce_str where to store the nonce (if contained)
764 * @return GNUNET_OK if successful, else GNUNET_SYSERR
765 */
766int
767OIDC_parse_authz_code (const struct GNUNET_CRYPTO_PublicKey *audience,
768 const char *code,
769 const char *code_verifier,
770 struct GNUNET_RECLAIM_Ticket *ticket,
771 struct GNUNET_RECLAIM_AttributeList **attrs,
772 struct GNUNET_RECLAIM_PresentationList **presentations,
773 char **nonce_str,
774 enum OIDC_VerificationOptions opts)
775{
776 char *code_payload;
777 char *ptr;
778 char *plaintext;
779 char *attrs_ser;
780 char *presentations_ser;
781 char *code_challenge;
782 struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
783 struct GNUNET_CRYPTO_Signature *signature;
784 uint32_t code_challenge_len;
785 uint32_t attrs_ser_len;
786 uint32_t pres_ser_len;
787 size_t plaintext_len;
788 size_t code_payload_len;
789 uint32_t nonce_len = 0;
790 struct OIDC_Parameters *params;
791
792 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to decode `%s'\n", code);
793 code_payload = NULL;
794 code_payload_len =
795 GNUNET_STRINGS_base64url_decode (code, strlen (code),
796 (void **) &code_payload);
797 if (code_payload_len < sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
798 + sizeof(struct OIDC_Parameters)
799 + sizeof(struct GNUNET_CRYPTO_Signature))
800 {
801 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Authorization code malformed\n");
802 GNUNET_free (code_payload);
803 return GNUNET_SYSERR;
804 }
805
806 purpose = (struct GNUNET_CRYPTO_EccSignaturePurpose *) code_payload;
807 plaintext_len = code_payload_len;
808 plaintext_len -= sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose);
809 ptr = (char *) &purpose[1];
810 plaintext_len -= sizeof(struct GNUNET_CRYPTO_Signature);
811 plaintext = ptr;
812 ptr += plaintext_len;
813 signature = (struct GNUNET_CRYPTO_Signature *) ptr;
814 params = (struct OIDC_Parameters *) plaintext;
815
816 // cmp code_challenge code_verifier
817 code_challenge_len = ntohl (params->code_challenge_len);
818 code_challenge = ((char *) &params[1]);
819 if (! (opts & OIDC_VERIFICATION_NO_CODE_VERIFIER))
820 {
821 if (GNUNET_OK != check_code_challenge (code_challenge,
822 code_challenge_len,
823 code_verifier))
824 {
825 GNUNET_free (code_payload);
826 return GNUNET_SYSERR;
827 }
828 }
829 nonce_len = ntohl (params->nonce_len);
830 if (0 != nonce_len)
831 {
832 *nonce_str = GNUNET_strndup (code_challenge + code_challenge_len,
833 nonce_len);
834 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got nonce: %s\n", *nonce_str);
835 }
836
837 // Ticket
838 memcpy (ticket, &params->ticket, sizeof(params->ticket));
839 // Signature
840 // GNUNET_CRYPTO_ecdsa_key_get_public (ecdsa_priv, &ecdsa_pub);
841 if (0 != GNUNET_memcmp (audience, &ticket->audience))
842 {
843 GNUNET_free (code_payload);
844 if (NULL != *nonce_str)
845 GNUNET_free (*nonce_str);
846 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
847 "Audience in ticket does not match client!\n");
848 return GNUNET_SYSERR;
849 }
850 if (GNUNET_OK !=
851 GNUNET_CRYPTO_signature_verify_ (
852 GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN,
853 purpose,
854 signature,
855 &(ticket->identity)))
856 {
857 GNUNET_free (code_payload);
858 if (NULL != *nonce_str)
859 GNUNET_free (*nonce_str);
860 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Signature of AuthZ code invalid!\n");
861 return GNUNET_SYSERR;
862 }
863 // Attributes
864 attrs_ser = ((char *) &params[1]) + code_challenge_len + nonce_len;
865 attrs_ser_len = ntohl (params->attr_list_len);
866 *attrs = GNUNET_RECLAIM_attribute_list_deserialize (attrs_ser, attrs_ser_len);
867 presentations_ser = ((char*) attrs_ser) + attrs_ser_len;
868 pres_ser_len = ntohl (params->pres_list_len);
869 *presentations =
870 GNUNET_RECLAIM_presentation_list_deserialize (presentations_ser,
871 pres_ser_len);
872
873 GNUNET_free (code_payload);
874 return GNUNET_OK;
875}
876
877
878/**
879 * Build a token response for a token request
880 * TODO: Maybe we should add the scope here?
881 *
882 * @param access_token the access token to include
883 * @param id_token the id_token to include
884 * @param expiration_time the expiration time of the token(s)
885 * @param token_response where to store the response
886 */
887void
888OIDC_build_token_response (const char *access_token,
889 const char *id_token,
890 const struct GNUNET_TIME_Relative *expiration_time,
891 char **token_response)
892{
893 json_t *root_json;
894
895 root_json = json_object ();
896
897 GNUNET_assert (NULL != access_token);
898 GNUNET_assert (NULL != id_token);
899 GNUNET_assert (NULL != expiration_time);
900 json_object_set_new (root_json, "access_token", json_string (access_token));
901 json_object_set_new (root_json, "token_type", json_string ("Bearer"));
902 json_object_set_new (root_json,
903 "expires_in",
904 json_integer (expiration_time->rel_value_us
905 / (1000 * 1000)));
906 json_object_set_new (root_json, "id_token", json_string (id_token));
907 *token_response = json_dumps (root_json, JSON_INDENT (0) | JSON_COMPACT);
908 json_decref (root_json);
909}
910
911
912/**
913 * Generate a new access token
914 */
915char *
916OIDC_access_token_new (const struct GNUNET_RECLAIM_Ticket *ticket)
917{
918 char *access_token;
919
920 GNUNET_STRINGS_base64_encode (ticket,
921 sizeof(*ticket),
922 &access_token);
923 return access_token;
924}
925
926
927/**
928 * Parse an access token
929 */
930int
931OIDC_access_token_parse (const char *token,
932 struct GNUNET_RECLAIM_Ticket **ticket)
933{
934 size_t sret;
935 char *decoded;
936 sret = GNUNET_STRINGS_base64_decode (token,
937 strlen (token),
938 (void**) &decoded);
939 if (sizeof (struct GNUNET_RECLAIM_Ticket) != sret)
940 {
941 GNUNET_free (decoded);
942 return GNUNET_SYSERR;
943 }
944 *ticket = (struct GNUNET_RECLAIM_Ticket *) decoded;
945 return GNUNET_OK;
946}
947
948
949/**
950 * Checks if a claim is implicitly requested through standard
951 * scope(s) or explicitly through non-standard scope.
952 *
953 * @param scopes the scopes which have been requested
954 * @param attr the attribute name to check
955 * @return GNUNET_YES if attribute is implcitly requested
956 */
957enum GNUNET_GenericReturnValue
958OIDC_check_scopes_for_claim_request (const char*scopes,
959 const char*attr)
960{
961 char *scope_variables;
962 char *scope_variable;
963 char delimiter[] = " ";
964 int i;
965
966 scope_variables = GNUNET_strdup (scopes);
967 scope_variable = strtok (scope_variables, delimiter);
968 while (NULL != scope_variable)
969 {
970 if (0 == strcmp ("profile", scope_variable))
971 {
972 for (i = 0; i < 14; i++)
973 {
974 if (0 == strcmp (attr, OIDC_profile_claims[i]))
975 {
976 GNUNET_free (scope_variables);
977 return GNUNET_YES;
978 }
979 }
980 }
981 else if (0 == strcmp ("address", scope_variable))
982 {
983 for (i = 0; i < 5; i++)
984 {
985 if (0 == strcmp (attr, OIDC_address_claims[i]))
986 {
987 GNUNET_free (scope_variables);
988 return GNUNET_YES;
989 }
990 }
991 }
992 else if (0 == strcmp ("email", scope_variable))
993 {
994 for (i = 0; i < 2; i++)
995 {
996 if (0 == strcmp (attr, OIDC_email_claims[i]))
997 {
998 GNUNET_free (scope_variables);
999 return GNUNET_YES;
1000 }
1001 }
1002 }
1003 else if (0 == strcmp ("phone", scope_variable))
1004 {
1005 for (i = 0; i < 2; i++)
1006 {
1007 if (0 == strcmp (attr, OIDC_phone_claims[i]))
1008 {
1009 GNUNET_free (scope_variables);
1010 return GNUNET_YES;
1011 }
1012 }
1013
1014 }
1015 else if (0 == strcmp (attr, scope_variable))
1016 {
1017 /** attribute matches requested scope **/
1018 GNUNET_free (scope_variables);
1019 return GNUNET_YES;
1020 }
1021 scope_variable = strtok (NULL, delimiter);
1022 }
1023 GNUNET_free (scope_variables);
1024 return GNUNET_NO;
1025
1026}
diff --git a/src/service/rest/oidc_helper.h b/src/service/rest/oidc_helper.h
new file mode 100644
index 000000000..de788fbdb
--- /dev/null
+++ b/src/service/rest/oidc_helper.h
@@ -0,0 +1,196 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2010-2015 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file reclaim/oidc_helper.h
23 * @brief helper library for OIDC related functions
24 * @author Martin Schanzenbach
25 */
26
27#ifndef JWT_H
28#define JWT_H
29
30#define JWT_ALG "alg"
31#define JWT_TYP "typ"
32#define JWT_TYP_VALUE "jwt"
33
34#define JWT_ALG_VALUE_HMAC "HS512"
35#define JWT_ALG_VALUE_RSA "RS256"
36
37#define SERVER_ADDRESS "http://localhost:7776"
38
39enum OIDC_VerificationOptions
40{
41 /**
42 * Strict verification
43 */
44 OIDC_VERIFICATION_DEFAULT = 0,
45
46 /**
47 * Do not check code verifier even if expected
48 */
49 OIDC_VERIFICATION_NO_CODE_VERIFIER = 1
50};
51
52/**
53 * Create a JWT using RSA256 algorithm from attributes
54 *
55 * @param aud_key the public of the audience
56 * @param sub_key the public key of the subject
57 * @param attrs the attribute list
58 * @param presentations credential presentation list (may be empty)
59 * @param expiration_time the validity of the token
60 * @param secret_rsa_key the key used to sign the JWT
61 * @return a new base64-encoded JWT string.
62 */
63char *
64OIDC_generate_id_token_rsa (const struct GNUNET_CRYPTO_PublicKey *aud_key,
65 const struct GNUNET_CRYPTO_PublicKey *sub_key,
66 const struct GNUNET_RECLAIM_AttributeList *attrs,
67 const struct
68 GNUNET_RECLAIM_PresentationList *presentations,
69 const struct GNUNET_TIME_Relative *expiration_time,
70 const char *nonce,
71 const json_t *secret_rsa_key);
72
73/**
74 * Create a JWT using HMAC (HS256) from attributes
75 *
76 * @param aud_key the public of the audience
77 * @param sub_key the public key of the subject
78 * @param attrs the attribute list
79 * @param presentations credential presentation list (may be empty)
80 * @param expiration_time the validity of the token
81 * @param secret_key the key used to sign the JWT
82 * @return a new base64-encoded JWT string.
83 */
84char*
85OIDC_generate_id_token_hmac (const struct GNUNET_CRYPTO_PublicKey *aud_key,
86 const struct GNUNET_CRYPTO_PublicKey *sub_key,
87 const struct GNUNET_RECLAIM_AttributeList *attrs,
88 const struct
89 GNUNET_RECLAIM_PresentationList *presentations,
90 const struct GNUNET_TIME_Relative *expiration_time,
91 const char *nonce,
92 const char *secret_key);
93
94/**
95 * Builds an OIDC authorization code including
96 * a reclaim ticket and nonce
97 *
98 * @param issuer the issuer of the ticket, used to sign the ticket and nonce
99 * @param ticket the ticket to include in the code
100 * @param attrs list of attributes to share
101 * @param presentations credential presentation list
102 * @param nonce the nonce to include in the code
103 * @param code_challenge PKCE code challenge
104 * @param opts verification options
105 * @return a new authorization code (caller must free)
106 */
107char*
108OIDC_build_authz_code (const struct GNUNET_CRYPTO_PrivateKey *issuer,
109 const struct GNUNET_RECLAIM_Ticket *ticket,
110 const struct GNUNET_RECLAIM_AttributeList *attrs,
111 const struct
112 GNUNET_RECLAIM_PresentationList *presentations,
113 const char *nonce,
114 const char *code_challenge);
115
116/**
117 * Parse reclaim ticket and nonce from
118 * authorization code.
119 * This also verifies the signature in the code.
120 *
121 * @param ecdsa_priv the audience of the ticket
122 * @param code the string representation of the code
123 * @param code_verfier PKCE code verifier
124 * @param ticket where to store the ticket
125 * @param attrs the attributes found in the code
126 * @param presentations credential presentation list
127 * @param nonce where to store the nonce
128 * @return GNUNET_OK if successful, else GNUNET_SYSERR
129 */
130int
131OIDC_parse_authz_code (const struct GNUNET_CRYPTO_PublicKey *ecdsa_pub,
132 const char *code,
133 const char *code_verifier,
134 struct GNUNET_RECLAIM_Ticket *ticket,
135 struct GNUNET_RECLAIM_AttributeList **attrs,
136 struct GNUNET_RECLAIM_PresentationList **presentations,
137 char **nonce,
138 enum OIDC_VerificationOptions opts);
139
140/**
141 * Build a token response for a token request
142 * TODO: Maybe we should add the scope here?
143 *
144 * @param access_token the access token to include
145 * @param id_token the id_token to include
146 * @param expiration_time the expiration time of the token(s)
147 * @param token_response where to store the response
148 */
149void
150OIDC_build_token_response (const char *access_token,
151 const char *id_token,
152 const struct GNUNET_TIME_Relative *expiration_time,
153 char **token_response);
154
155/**
156 * Generate a new access token
157 */
158char*
159OIDC_access_token_new (const struct GNUNET_RECLAIM_Ticket *ticket);
160
161/**
162 * Parse an access token
163 */
164int
165OIDC_access_token_parse (const char *token,
166 struct GNUNET_RECLAIM_Ticket **ticket);
167
168
169/**
170 * Checks if a claim is implicitly requested through standard
171 * scope(s)
172 *
173 * @param scopes the scopes which have been requested
174 * @param attr the attribute name to check
175 * @return GNUNET_YES if attribute is implcitly requested
176 */
177enum GNUNET_GenericReturnValue
178OIDC_check_scopes_for_claim_request (const char *scopes,
179 const char *attr);
180
181
182/**
183 * Generate userinfo JSON as string
184 *
185 * @param sub_key the subject (user)
186 * @param attrs user attribute list
187 * @param presentations credential presentation list
188 * @return Userinfo JSON
189 */
190char *
191OIDC_generate_userinfo (const struct GNUNET_CRYPTO_PublicKey *sub_key,
192 const struct GNUNET_RECLAIM_AttributeList *attrs,
193 const struct
194 GNUNET_RECLAIM_PresentationList *presentations);
195
196#endif
diff --git a/src/service/rest/openid_plugin.c b/src/service/rest/openid_plugin.c
new file mode 100644
index 000000000..a4f082d2a
--- /dev/null
+++ b/src/service/rest/openid_plugin.c
@@ -0,0 +1,3169 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2018 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 * @author Tristan Schwieren
24 * @file identity/plugin_rest_openid_connect.c
25 * @brief GNUnet Namestore REST plugin
26 *
27 */
28#include "platform.h"
29#include <inttypes.h>
30#include <jansson.h>
31#include <jose/jose.h>
32
33#include "gnunet_util_lib.h"
34#include "gnunet_gns_service.h"
35#include "gnunet_gnsrecord_lib.h"
36#include "gnunet_identity_service.h"
37#include "gnunet_namestore_service.h"
38#include "gnunet_reclaim_lib.h"
39#include "gnunet_reclaim_service.h"
40#include "gnunet_rest_lib.h"
41#include "gnunet_rest_plugin.h"
42#include "gnunet_signatures.h"
43#include "microhttpd.h"
44#include "oidc_helper.h"
45
46/**
47 * REST root namespace
48 */
49#define GNUNET_REST_API_NS_OIDC "/openid"
50
51/**
52 * OIDC config
53 */
54#define GNUNET_REST_API_NS_OIDC_CONFIG "/.well-known/openid-configuration"
55
56/**
57 * Authorize endpoint
58 */
59#define GNUNET_REST_API_NS_AUTHORIZE "/openid/authorize"
60
61/**
62 * Token endpoint
63 */
64#define GNUNET_REST_API_NS_TOKEN "/openid/token"
65
66/**
67 * JSON Web Keys endpoint
68 */
69#define GNUNET_REST_API_JWKS "/jwks.json"
70
71/**
72 * UserInfo endpoint
73 */
74#define GNUNET_REST_API_NS_USERINFO "/openid/userinfo"
75
76/**
77 * Login namespace
78 */
79#define GNUNET_REST_API_NS_LOGIN "/openid/login"
80
81/**
82 * State while collecting all egos
83 */
84#define ID_REST_STATE_INIT 0
85
86/**
87 * Done collecting egos
88 */
89#define ID_REST_STATE_POST_INIT 1
90
91/**
92 * OIDC grant_type key
93 */
94#define OIDC_GRANT_TYPE_KEY "grant_type"
95
96/**
97 * OIDC grant_type key
98 */
99#define OIDC_GRANT_TYPE_VALUE "authorization_code"
100
101/**
102 * OIDC code key
103 */
104#define OIDC_CODE_KEY "code"
105
106/**
107 * OIDC response_type key
108 */
109#define OIDC_RESPONSE_TYPE_KEY "response_type"
110
111/**
112 * OIDC client_id key
113 */
114#define OIDC_CLIENT_ID_KEY "client_id"
115
116/**
117 * OIDC scope key
118 */
119#define OIDC_SCOPE_KEY "scope"
120
121/**
122 * OIDC redirect_uri key
123 */
124#define OIDC_REDIRECT_URI_KEY "redirect_uri"
125
126/**
127 * OIDC state key
128 */
129#define OIDC_STATE_KEY "state"
130
131/**
132 * OIDC nonce key
133 */
134#define OIDC_NONCE_KEY "nonce"
135
136/**
137 * OIDC claims key
138 */
139#define OIDC_CLAIMS_KEY "claims"
140
141/**
142 * OIDC PKCE code challenge
143 */
144#define OIDC_CODE_CHALLENGE_KEY "code_challenge"
145
146/**
147 * OIDC PKCE code verifier
148 */
149#define OIDC_CODE_VERIFIER_KEY "code_verifier"
150
151/**
152 * OIDC cookie expiration (in seconds)
153 */
154#define OIDC_COOKIE_EXPIRATION 3
155
156/**
157 * OIDC cookie header key
158 */
159#define OIDC_COOKIE_HEADER_KEY "cookie"
160
161/**
162 * OIDC cookie header information key
163 */
164#define OIDC_AUTHORIZATION_HEADER_KEY "authorization"
165
166/**
167 * OIDC cookie header information key
168 */
169#define OIDC_COOKIE_HEADER_INFORMATION_KEY "Identity="
170
171/**
172 * OIDC cookie header if user cancelled
173 */
174#define OIDC_COOKIE_HEADER_ACCESS_DENIED "Identity=Denied"
175
176/**
177 * OIDC expected response_type while authorizing
178 */
179#define OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE "code"
180
181/**
182 * OIDC expected scope part while authorizing
183 */
184#define OIDC_EXPECTED_AUTHORIZATION_SCOPE "openid"
185
186/**
187 * OIDC error key for invalid client
188 */
189#define OIDC_ERROR_KEY_INVALID_CLIENT "invalid_client"
190
191/**
192 * OIDC error key for invalid scopes
193 */
194#define OIDC_ERROR_KEY_INVALID_SCOPE "invalid_scope"
195
196/**
197 * OIDC error key for invalid requests
198 */
199#define OIDC_ERROR_KEY_INVALID_REQUEST "invalid_request"
200
201/**
202 * OIDC error key for invalid tokens
203 */
204#define OIDC_ERROR_KEY_INVALID_TOKEN "invalid_token"
205
206/**
207 * OIDC error key for invalid cookies
208 */
209#define OIDC_ERROR_KEY_INVALID_COOKIE "invalid_cookie"
210
211/**
212 * OIDC error key for generic server errors
213 */
214#define OIDC_ERROR_KEY_SERVER_ERROR "server_error"
215
216/**
217 * OIDC error key for unsupported grants
218 */
219#define OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE "unsupported_grant_type"
220
221/**
222 * OIDC error key for unsupported response types
223 */
224#define OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE "unsupported_response_type"
225
226/**
227 * OIDC error key for unauthorized clients
228 */
229#define OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT "unauthorized_client"
230
231/**
232 * OIDC error key for denied access
233 */
234#define OIDC_ERROR_KEY_ACCESS_DENIED "access_denied"
235
236/**
237 * OIDC key store file name
238 */
239#define OIDC_JWK_RSA_FILENAME "jwk_rsa.json"
240
241/**
242 * How long to wait for a consume in userinfo endpoint
243 */
244#define CONSUME_TIMEOUT GNUNET_TIME_relative_multiply ( \
245 GNUNET_TIME_UNIT_SECONDS,2)
246
247/**
248 * OIDC ignored parameter array
249 */
250static char *OIDC_ignored_parameter_array[] = { "display",
251 "prompt",
252 "ui_locales",
253 "response_mode",
254 "id_token_hint",
255 "login_hint",
256 "acr_values" };
257
258/**
259 * OIDC hashmap for cached access tokens and codes
260 */
261struct GNUNET_CONTAINER_MultiHashMap *oidc_code_cache;
262
263/**
264 * OIDC hashmap that keeps track of issued cookies
265 */
266struct GNUNET_CONTAINER_MultiHashMap *OIDC_cookie_jar_map;
267
268/**
269 * The configuration handle
270 */
271const struct GNUNET_CONFIGURATION_Handle *oid_cfg;
272
273/**
274 * HTTP methods allows for this plugin
275 */
276static char *allow_methods;
277
278/**
279 * Ego list
280 */
281static struct EgoEntry *ego_head;
282
283/**
284 * Ego list
285 */
286static struct EgoEntry *ego_tail;
287
288/**
289 * The processing state
290 */
291static int state;
292
293/**
294 * Handle to Identity service.
295 */
296static struct GNUNET_IDENTITY_Handle *identity_handle;
297
298/**
299 * GNS handle
300 */
301static struct GNUNET_GNS_Handle *gns_handle;
302
303/**
304 * Identity Provider
305 */
306static struct GNUNET_RECLAIM_Handle *idp;
307
308/**
309 * Timeout for consume call on userinfo
310 */
311static struct GNUNET_TIME_Relative consume_timeout;
312
313/**
314 * @brief struct returned by the initialization function of the plugin
315 */
316struct Plugin
317{
318 const struct GNUNET_CONFIGURATION_Handle *cfg;
319};
320
321/**
322 * @brief The RSA key used by the oidc enpoint
323 */
324json_t *oidc_jwk;
325
326/**
327 * OIDC needed variables
328 */
329struct OIDC_Variables
330{
331 /**
332 * The RP client public key
333 */
334 struct GNUNET_CRYPTO_PublicKey client_pkey;
335
336 /**
337 * The OIDC client id of the RP
338 */
339 char *client_id;
340
341 /**
342 * The OIDC redirect uri
343 */
344 char *redirect_uri;
345
346 /**
347 * The list of oidc scopes
348 */
349 char *scope;
350
351 /**
352 * The OIDC state
353 */
354 char *state;
355
356 /**
357 * The OIDC nonce
358 */
359 char *nonce;
360
361 /**
362 * The OIDC claims
363 */
364 char *claims;
365
366 /**
367 * The OIDC response type
368 */
369 char *response_type;
370
371 /**
372 * The identity chosen by the user to login
373 */
374 char *login_identity;
375
376 /**
377 * User cancelled authorization/login
378 */
379 int user_cancelled;
380
381 /**
382 * The PKCE code_challenge
383 */
384 char *code_challenge;
385
386 /**
387 * The PKCE code_verifier
388 */
389 char *code_verifier;
390
391};
392
393/**
394 * The ego list
395 */
396struct EgoEntry
397{
398 /**
399 * DLL
400 */
401 struct EgoEntry *next;
402
403 /**
404 * DLL
405 */
406 struct EgoEntry *prev;
407
408 /**
409 * Ego Identifier
410 */
411 char *identifier;
412
413 /**
414 * Public key string
415 */
416 char *keystring;
417
418 /**
419 * The Ego
420 */
421 struct GNUNET_IDENTITY_Ego *ego;
422};
423
424
425struct RequestHandle
426{
427 /**
428 * DLL
429 */
430 struct RequestHandle *next;
431
432 /**
433 * DLL
434 */
435 struct RequestHandle *prev;
436
437 /**
438 * Selected ego
439 */
440 struct EgoEntry *ego_entry;
441
442 /**
443 * Pointer to ego private key
444 */
445 struct GNUNET_CRYPTO_PrivateKey priv_key;
446
447 /**
448 * OIDC variables
449 */
450 struct OIDC_Variables *oidc;
451
452 /**
453 * GNS lookup op
454 */
455 struct GNUNET_GNS_LookupRequest *gns_op;
456
457 /**
458 * Rest connection
459 */
460 struct GNUNET_REST_RequestHandle *rest_handle;
461
462 /**
463 * Attribute claim list for id_token
464 */
465 struct GNUNET_RECLAIM_AttributeList *attr_idtoken_list;
466
467 /**
468 * Attribute claim list for userinfo
469 */
470 struct GNUNET_RECLAIM_AttributeList *attr_userinfo_list;
471
472 /**
473 * Credentials
474 */
475 struct GNUNET_RECLAIM_CredentialList *credentials;
476
477 /**
478 * Presentations
479 */
480 struct GNUNET_RECLAIM_PresentationList *presentations;
481
482 /**
483 * IDENTITY Operation
484 */
485 struct GNUNET_IDENTITY_Operation *op;
486
487
488 /**
489 * Idp Operation
490 */
491 struct GNUNET_RECLAIM_Operation *idp_op;
492
493 /**
494 * Timeout task for consume
495 */
496 struct GNUNET_SCHEDULER_Task *consume_timeout_op;
497
498 /**
499 * Attribute iterator
500 */
501 struct GNUNET_RECLAIM_AttributeIterator *attr_it;
502
503 /**
504 * Credential iterator
505 */
506 struct GNUNET_RECLAIM_CredentialIterator *cred_it;
507
508
509 /**
510 * Ticket iterator
511 */
512 struct GNUNET_RECLAIM_TicketIterator *ticket_it;
513
514 /**
515 * A ticket
516 */
517 struct GNUNET_RECLAIM_Ticket ticket;
518
519 /**
520 * Desired timeout for the lookup (default is no timeout).
521 */
522 struct GNUNET_TIME_Relative timeout;
523
524 /**
525 * ID of a task associated with the resolution process.
526 */
527 struct GNUNET_SCHEDULER_Task *timeout_task;
528
529 /**
530 * The plugin result processor
531 */
532 GNUNET_REST_ResultProcessor proc;
533
534 /**
535 * The closure of the result processor
536 */
537 void *proc_cls;
538
539 /**
540 * The url
541 */
542 char *url;
543
544 /**
545 * The passed access token
546 */
547 char *access_token;
548
549 /**
550 * The tld for redirect
551 */
552 char *tld;
553
554 /**
555 * The redirect prefix
556 */
557 char *redirect_prefix;
558
559 /**
560 * The redirect suffix
561 */
562 char *redirect_suffix;
563
564 /**
565 * Error response message
566 */
567 char *emsg;
568
569 /**
570 * Error response description
571 */
572 char *edesc;
573
574 /**
575 * Response code
576 */
577 int response_code;
578
579 /**
580 * Public client
581 */
582 int public_client;
583};
584
585/**
586 * DLL
587 */
588static struct RequestHandle *requests_head;
589
590/**
591 * DLL
592 */
593static struct RequestHandle *requests_tail;
594
595
596/**
597 * Cleanup lookup handle
598 * @param handle Handle to clean up
599 */
600static void
601cleanup_handle (struct RequestHandle *handle)
602{
603
604 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
605 if (NULL != handle->timeout_task)
606 GNUNET_SCHEDULER_cancel (handle->timeout_task);
607 if (NULL != handle->attr_it)
608 GNUNET_RECLAIM_get_attributes_stop (handle->attr_it);
609 if (NULL != handle->cred_it)
610 GNUNET_RECLAIM_get_credentials_stop (handle->cred_it);
611 if (NULL != handle->ticket_it)
612 GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it);
613 if (NULL != handle->idp_op)
614 GNUNET_RECLAIM_cancel (handle->idp_op);
615 if (NULL != handle->consume_timeout_op)
616 GNUNET_SCHEDULER_cancel (handle->consume_timeout_op);
617 GNUNET_free (handle->url);
618 GNUNET_free (handle->tld);
619 GNUNET_free (handle->redirect_prefix);
620 GNUNET_free (handle->redirect_suffix);
621 GNUNET_free (handle->emsg);
622 GNUNET_free (handle->edesc);
623 if (NULL != handle->gns_op)
624 GNUNET_GNS_lookup_cancel (handle->gns_op);
625 if (NULL != handle->oidc)
626 {
627 GNUNET_free (handle->oidc->client_id);
628 GNUNET_free (handle->oidc->login_identity);
629 GNUNET_free (handle->oidc->nonce);
630 GNUNET_free (handle->oidc->redirect_uri);
631 GNUNET_free (handle->oidc->response_type);
632 GNUNET_free (handle->oidc->scope);
633 GNUNET_free (handle->oidc->state);
634 if (NULL != handle->oidc->claims)
635 GNUNET_free (handle->oidc->claims);
636 if (NULL != handle->oidc->code_challenge)
637 GNUNET_free (handle->oidc->code_challenge);
638 GNUNET_free (handle->oidc);
639 }
640 if (NULL!=handle->attr_idtoken_list)
641 GNUNET_RECLAIM_attribute_list_destroy (handle->attr_idtoken_list);
642 if (NULL!=handle->attr_userinfo_list)
643 GNUNET_RECLAIM_attribute_list_destroy (handle->attr_userinfo_list);
644 if (NULL!=handle->credentials)
645 GNUNET_RECLAIM_credential_list_destroy (handle->credentials);
646 if (NULL!=handle->presentations)
647 GNUNET_RECLAIM_presentation_list_destroy (handle->presentations);
648 GNUNET_CONTAINER_DLL_remove (requests_head,
649 requests_tail,
650 handle);
651 if (NULL != handle->access_token)
652 GNUNET_free (handle->access_token);
653 GNUNET_free (handle);
654}
655
656
657/**
658 * Task run on error, sends error message. Cleans up everything.
659 *
660 * @param cls the `struct RequestHandle`
661 */
662static void
663do_error (void *cls)
664{
665 struct RequestHandle *handle = cls;
666 struct MHD_Response *resp;
667 char *json_error;
668
669 GNUNET_asprintf (&json_error,
670 "{ \"error\" : \"%s\", \"error_description\" : \"%s\"%s%s%s}",
671 handle->emsg,
672 (NULL != handle->edesc) ? handle->edesc : "",
673 (NULL != handle->oidc->state) ? ", \"state\":\"" : "",
674 (NULL != handle->oidc->state) ? handle->oidc->state : "",
675 (NULL != handle->oidc->state) ? "\"" : "");
676 if (0 == handle->response_code)
677 handle->response_code = MHD_HTTP_BAD_REQUEST;
678 resp = GNUNET_REST_create_response (json_error);
679 if (MHD_HTTP_UNAUTHORIZED == handle->response_code)
680 GNUNET_assert (MHD_NO !=
681 MHD_add_response_header (resp,
682 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
683 "Basic"));
684 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
685 MHD_HTTP_HEADER_CONTENT_TYPE,
686 "application/json"));
687 handle->proc (handle->proc_cls, resp, handle->response_code);
688 cleanup_handle (handle);
689 GNUNET_free (json_error);
690}
691
692
693/**
694 * Task run on error in userinfo endpoint, sends error header. Cleans up
695 * everything
696 *
697 * @param cls the `struct RequestHandle`
698 */
699static void
700do_userinfo_error (void *cls)
701{
702 struct RequestHandle *handle = cls;
703 struct MHD_Response *resp;
704 char *error;
705
706 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
707 "Error: %s\n", handle->edesc);
708 GNUNET_asprintf (&error,
709 "error=\"%s\", error_description=\"%s\"",
710 handle->emsg,
711 (NULL != handle->edesc) ? handle->edesc : "");
712 resp = GNUNET_REST_create_response ("");
713 GNUNET_assert (MHD_NO !=
714 MHD_add_response_header (resp,
715 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
716 "Bearer"));
717 handle->proc (handle->proc_cls, resp, handle->response_code);
718 cleanup_handle (handle);
719 GNUNET_free (error);
720}
721
722
723/**
724 * Task run on error, sends error message and redirects. Cleans up everything.
725 *
726 * @param cls the `struct RequestHandle`
727 */
728static void
729do_redirect_error (void *cls)
730{
731 struct RequestHandle *handle = cls;
732 struct MHD_Response *resp;
733 char *redirect;
734
735 GNUNET_asprintf (&redirect,
736 "%s?error=%s&error_description=%s%s%s",
737 handle->oidc->redirect_uri,
738 handle->emsg,
739 handle->edesc,
740 (NULL != handle->oidc->state) ? "&state=" : "",
741 (NULL != handle->oidc->state) ? handle->oidc->state : "");
742 resp = GNUNET_REST_create_response ("");
743 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
744 "Location", redirect));
745 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
746 cleanup_handle (handle);
747 GNUNET_free (redirect);
748}
749
750
751/**
752 * Task run on timeout, sends error message. Cleans up everything.
753 *
754 * @param cls the `struct RequestHandle`
755 */
756static void
757do_timeout (void *cls)
758{
759 struct RequestHandle *handle = cls;
760
761 handle->timeout_task = NULL;
762 do_error (handle);
763}
764
765
766/**
767 * Respond to OPTIONS request
768 *
769 * @param con_handle the connection handle
770 * @param url the url
771 * @param cls the RequestHandle
772 */
773static void
774options_cont (struct GNUNET_REST_RequestHandle *con_handle,
775 const char *url,
776 void *cls)
777{
778 struct MHD_Response *resp;
779 struct RequestHandle *handle = cls;
780
781 // For now, independent of path return all options
782 resp = GNUNET_REST_create_response (NULL);
783 MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
784 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
785 cleanup_handle (handle);
786 return;
787}
788
789
790/**
791 * Interprets cookie header and pass its identity keystring to handle
792 */
793static void
794cookie_identity_interpretation (struct RequestHandle *handle)
795{
796 struct GNUNET_HashCode cache_key;
797 char *cookies;
798 struct GNUNET_TIME_Absolute current_time, *relog_time;
799 char delimiter[] = "; ";
800 char *tmp_cookies;
801 char *token;
802 char *value;
803
804 // gets identity of login try with cookie
805 GNUNET_CRYPTO_hash (OIDC_COOKIE_HEADER_KEY,
806 strlen (OIDC_COOKIE_HEADER_KEY),
807 &cache_key);
808 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
809 ->header_param_map,
810 &cache_key))
811 {
812 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No cookie found\n");
813 return;
814 }
815 // splits cookies and find 'Identity' cookie
816 tmp_cookies =
817 GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
818 &cache_key);
819 cookies = GNUNET_strdup (tmp_cookies);
820 token = strtok (cookies, delimiter);
821 handle->oidc->user_cancelled = GNUNET_NO;
822 handle->oidc->login_identity = NULL;
823 if (NULL == token)
824 {
825 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
826 "Unable to parse cookie: %s\n",
827 cookies);
828 GNUNET_free (cookies);
829 return;
830 }
831
832 while (NULL != token)
833 {
834 if (0 == strcmp (token, OIDC_COOKIE_HEADER_ACCESS_DENIED))
835 {
836 handle->oidc->user_cancelled = GNUNET_YES;
837 GNUNET_free (cookies);
838 return;
839 }
840 if (NULL != strstr (token, OIDC_COOKIE_HEADER_INFORMATION_KEY))
841 break;
842 token = strtok (NULL, delimiter);
843 }
844 if (NULL == token)
845 {
846 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
847 "No cookie value to process: %s\n",
848 cookies);
849 GNUNET_free (cookies);
850 return;
851 }
852 GNUNET_CRYPTO_hash (token, strlen (token), &cache_key);
853 if (GNUNET_NO ==
854 GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key))
855 {
856 GNUNET_log (
857 GNUNET_ERROR_TYPE_WARNING,
858 "Found cookie `%s', but no corresponding expiration entry present...\n",
859 token);
860 GNUNET_free (cookies);
861 return;
862 }
863 relog_time =
864 GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
865 current_time = GNUNET_TIME_absolute_get ();
866 // 30 min after old login -> redirect to login
867 if (current_time.abs_value_us > relog_time->abs_value_us)
868 {
869 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
870 "Found cookie `%s', but it is expired.\n",
871 token);
872 GNUNET_free (cookies);
873 return;
874 }
875 value = strtok (token, OIDC_COOKIE_HEADER_INFORMATION_KEY);
876 GNUNET_assert (NULL != value);
877 handle->oidc->login_identity = GNUNET_strdup (value);
878 GNUNET_free (cookies);
879}
880
881
882/**
883 * @brief Read the the JSON Web Key in the given file and return it.
884 * Return NULL and emit warning if JSON can not be decoded or the key is
885 * invalid
886 *
887 * @param filename the file to read the JWK from
888 * @return json_t* the reed JWK
889 */
890json_t *
891read_jwk_from_file (const char *filename)
892{
893 json_t *jwk;
894 json_error_t error;
895
896 jwk = json_load_file (filename, JSON_DECODE_ANY, &error);
897
898 if (! jwk)
899 {
900 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
901 ("Could not read OIDC RSA key from config file; %s\n"),
902 error.text);
903 }
904
905 return jwk;
906}
907
908
909/**
910 * @brief Write the JWK to file. If unsuccessful emit warning
911 *
912 * @param filename the name of the file the JWK is writen to
913 * @param jwk the JWK that is going to be written
914 * @return int Return GNUNET_OK if write is sucessfull
915 */
916static int
917write_jwk_to_file (const char *filename,
918 json_t *jwk)
919{
920 if (json_dump_file (jwk, filename, JSON_INDENT (2)))
921 {
922 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
923 ("Could not write OIDC RSA key to file %s\n"),
924 filename);
925 return GNUNET_ERROR_TYPE_WARNING;
926 }
927 else
928 return GNUNET_OK;
929}
930
931
932/**
933 * @brief Generate a new RSA JSON Web Key
934 *
935 * @return json_t* the generated JWK
936 */
937json_t *
938generate_jwk ()
939{
940 json_t *jwk;
941 jwk = json_pack ("{s:s,s:i}", "kty", "RSA", "bits", 2048);
942 jose_jwk_gen (NULL, jwk);
943 json_incref (jwk);
944 return jwk;
945}
946
947
948/**
949 * Return the path to the oidc directory path
950 *
951 * @param cls the RequestHandle
952 */
953char *
954get_oidc_dir_path (void *cls)
955{
956 char *oidc_directory;
957 struct RequestHandle *handle = cls;
958
959 // Read OIDC directory from config
960 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (oid_cfg,
961 "reclaim-rest-plugin",
962 "oidc_dir",
963 &oidc_directory))
964 {
965 // Could not read Config file
966 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
967 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
968 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
969 GNUNET_SCHEDULER_add_now (&do_error, handle);
970 return NULL;
971 }
972
973 return oidc_directory;
974}
975
976
977/**
978 * Return the path to the RSA JWK key file
979 *
980 * @param cls the RequestHandle
981 */
982char *
983get_oidc_jwk_path (void *cls)
984{
985 char *oidc_directory;
986 char *oidc_jwk_path;
987
988 oidc_directory = get_oidc_dir_path (cls);
989
990 // Create path to file
991 GNUNET_asprintf (&oidc_jwk_path, "%s/%s", oidc_directory,
992 OIDC_JWK_RSA_FILENAME);
993
994 return oidc_jwk_path;
995}
996
997
998/**
999 * Redirects to login page stored in configuration file
1000 */
1001static void
1002login_redirect (void *cls)
1003{
1004 char *login_base_url;
1005 char *new_redirect;
1006 char *tmp;
1007 struct MHD_Response *resp;
1008 struct GNUNET_Buffer buf = { 0 };
1009 struct RequestHandle *handle = cls;
1010
1011 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (oid_cfg,
1012 "reclaim-rest-plugin",
1013 "address",
1014 &login_base_url))
1015 {
1016 GNUNET_buffer_write_str (&buf, login_base_url);
1017 GNUNET_buffer_write_fstr (&buf,
1018 "?%s=%s",
1019 OIDC_RESPONSE_TYPE_KEY,
1020 handle->oidc->response_type);
1021 GNUNET_buffer_write_fstr (&buf,
1022 "&%s=%s",
1023 OIDC_CLIENT_ID_KEY,
1024 handle->oidc->client_id);
1025 GNUNET_STRINGS_urlencode (handle->oidc->redirect_uri,
1026 strlen (handle->oidc->redirect_uri),
1027 &tmp);
1028 GNUNET_buffer_write_fstr (&buf,
1029 "&%s=%s",
1030 OIDC_REDIRECT_URI_KEY,
1031 tmp);
1032 GNUNET_free (tmp);
1033 GNUNET_STRINGS_urlencode (handle->oidc->scope,
1034 strlen (handle->oidc->scope),
1035 &tmp);
1036 GNUNET_buffer_write_fstr (&buf,
1037 "&%s=%s",
1038 OIDC_SCOPE_KEY,
1039 tmp);
1040 GNUNET_free (tmp);
1041 if (NULL != handle->oidc->state)
1042 {
1043 GNUNET_STRINGS_urlencode (handle->oidc->state,
1044 strlen (handle->oidc->state),
1045 &tmp);
1046 GNUNET_buffer_write_fstr (&buf,
1047 "&%s=%s",
1048 OIDC_STATE_KEY,
1049 handle->oidc->state);
1050 GNUNET_free (tmp);
1051 }
1052 if (NULL != handle->oidc->code_challenge)
1053 {
1054 GNUNET_buffer_write_fstr (&buf,
1055 "&%s=%s",
1056 OIDC_CODE_CHALLENGE_KEY,
1057 handle->oidc->code_challenge);
1058 }
1059 if (NULL != handle->oidc->nonce)
1060 {
1061 GNUNET_buffer_write_fstr (&buf,
1062 "&%s=%s",
1063 OIDC_NONCE_KEY,
1064 handle->oidc->nonce);
1065 }
1066 if (NULL != handle->oidc->claims)
1067 {
1068 GNUNET_STRINGS_urlencode (handle->oidc->claims,
1069 strlen (handle->oidc->claims),
1070 &tmp);
1071 GNUNET_buffer_write_fstr (&buf,
1072 "&%s=%s",
1073 OIDC_CLAIMS_KEY,
1074 tmp);
1075 GNUNET_free (tmp);
1076 }
1077 new_redirect = GNUNET_buffer_reap_str (&buf);
1078 resp = GNUNET_REST_create_response ("");
1079 MHD_add_response_header (resp, "Location", new_redirect);
1080 GNUNET_free (login_base_url);
1081 }
1082 else
1083 {
1084 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1085 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1086 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1087 GNUNET_SCHEDULER_add_now (&do_error, handle);
1088 return;
1089 }
1090 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1091 GNUNET_free (new_redirect);
1092 cleanup_handle (handle);
1093}
1094
1095
1096/**
1097 * Does internal server error when iteration failed.
1098 */
1099static void
1100oidc_iteration_error (void *cls)
1101{
1102 struct RequestHandle *handle = cls;
1103
1104 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1105 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1106 GNUNET_SCHEDULER_add_now (&do_error, handle);
1107}
1108
1109
1110/**
1111 * Issues ticket and redirects to relying party with the authorization code as
1112 * parameter. Otherwise redirects with error
1113 */
1114static void
1115oidc_ticket_issue_cb (void *cls,
1116 const struct GNUNET_RECLAIM_Ticket *ticket,
1117 const struct
1118 GNUNET_RECLAIM_PresentationList *presentation)
1119{
1120 struct RequestHandle *handle = cls;
1121 struct MHD_Response *resp;
1122 char *ticket_str;
1123 char *redirect_uri;
1124 char *code_string;
1125
1126 handle->idp_op = NULL;
1127 if (NULL == ticket)
1128 {
1129 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1130 handle->edesc = GNUNET_strdup ("Server cannot generate ticket.");
1131 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1132 return;
1133 }
1134 handle->ticket = *ticket;
1135 ticket_str =
1136 GNUNET_STRINGS_data_to_string_alloc (&handle->ticket,
1137 sizeof(struct GNUNET_RECLAIM_Ticket));
1138 code_string = OIDC_build_authz_code (&handle->priv_key,
1139 &handle->ticket,
1140 handle->attr_idtoken_list,
1141 presentation,
1142 handle->oidc->nonce,
1143 handle->oidc->code_challenge);
1144 if ((NULL != handle->redirect_prefix) && (NULL != handle->redirect_suffix) &&
1145 (NULL != handle->tld))
1146 {
1147 GNUNET_asprintf (&redirect_uri,
1148 "%s.%s/%s%s%s=%s&state=%s",
1149 handle->redirect_prefix,
1150 handle->tld,
1151 handle->redirect_suffix,
1152 (NULL == strchr (handle->redirect_suffix, '?') ? "?" :
1153 "&"),
1154 handle->oidc->response_type,
1155 code_string,
1156 handle->oidc->state);
1157 }
1158 else
1159 {
1160 GNUNET_asprintf (&redirect_uri,
1161 "%s%s%s=%s&state=%s",
1162 handle->oidc->redirect_uri,
1163 (NULL == strchr (handle->oidc->redirect_uri, '?') ? "?" :
1164 "&"),
1165 handle->oidc->response_type,
1166 code_string,
1167 handle->oidc->state);
1168 }
1169 resp = GNUNET_REST_create_response ("");
1170 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
1171 "Location", redirect_uri));
1172 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1173 cleanup_handle (handle);
1174 GNUNET_free (redirect_uri);
1175 GNUNET_free (ticket_str);
1176 GNUNET_free (code_string);
1177}
1178
1179
1180static struct GNUNET_RECLAIM_AttributeList*
1181attribute_list_merge (struct GNUNET_RECLAIM_AttributeList *list_a,
1182 struct GNUNET_RECLAIM_AttributeList *list_b)
1183{
1184 struct GNUNET_RECLAIM_AttributeList *merged_list;
1185 struct GNUNET_RECLAIM_AttributeListEntry *le_a;
1186 struct GNUNET_RECLAIM_AttributeListEntry *le_b;
1187 struct GNUNET_RECLAIM_AttributeListEntry *le_m;
1188
1189 merged_list = GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
1190 for (le_a = list_a->list_head; NULL != le_a; le_a = le_a->next)
1191 {
1192 le_m = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1193 le_m->attribute = GNUNET_RECLAIM_attribute_new (le_a->attribute->name,
1194 &le_a->attribute->
1195 credential,
1196 le_a->attribute->type,
1197 le_a->attribute->data,
1198 le_a->attribute->data_size);
1199 le_m->attribute->id = le_a->attribute->id;
1200 le_m->attribute->flag = le_a->attribute->flag;
1201 le_m->attribute->credential = le_a->attribute->credential;
1202 GNUNET_CONTAINER_DLL_insert (merged_list->list_head,
1203 merged_list->list_tail,
1204 le_m);
1205 }
1206 le_m = NULL;
1207 for (le_b = list_b->list_head; NULL != le_b; le_b = le_b->next)
1208 {
1209 for (le_m = merged_list->list_head; NULL != le_m; le_m = le_m->next)
1210 {
1211 if (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (&le_m->attribute->id,
1212 &le_b->attribute->id))
1213 break; /** Attribute already in list **/
1214 }
1215 if (NULL != le_m)
1216 continue; /** Attribute already in list **/
1217 le_m = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1218 le_m->attribute = GNUNET_RECLAIM_attribute_new (le_b->attribute->name,
1219 &le_b->attribute->
1220 credential,
1221 le_b->attribute->type,
1222 le_b->attribute->data,
1223 le_b->attribute->data_size);
1224 le_m->attribute->id = le_b->attribute->id;
1225 le_m->attribute->flag = le_b->attribute->flag;
1226 le_m->attribute->credential = le_b->attribute->credential;
1227 GNUNET_CONTAINER_DLL_insert (merged_list->list_head,
1228 merged_list->list_tail,
1229 le_m);
1230 }
1231 return merged_list;
1232}
1233
1234
1235static void
1236oidc_cred_collect_finished_cb (void *cls)
1237{
1238 struct RequestHandle *handle = cls;
1239 struct GNUNET_RECLAIM_AttributeList *merged_list;
1240 struct GNUNET_RECLAIM_AttributeListEntry *le_m;
1241
1242 handle->cred_it = NULL;
1243 merged_list = attribute_list_merge (handle->attr_idtoken_list,
1244 handle->attr_userinfo_list);
1245 for (le_m = merged_list->list_head; NULL != le_m; le_m = le_m->next)
1246 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1247 "List Attribute in ticket to issue: %s\n",
1248 le_m->attribute->name);
1249 handle->idp_op = GNUNET_RECLAIM_ticket_issue (idp,
1250 &handle->priv_key,
1251 &handle->oidc->client_pkey,
1252 merged_list,
1253 &oidc_ticket_issue_cb,
1254 handle);
1255 GNUNET_RECLAIM_attribute_list_destroy (merged_list);
1256}
1257
1258
1259/**
1260 * Collects all attributes for an ego if in scope parameter
1261 */
1262static void
1263oidc_cred_collect (void *cls,
1264 const struct GNUNET_CRYPTO_PublicKey *identity,
1265 const struct GNUNET_RECLAIM_Credential *cred)
1266{
1267 struct RequestHandle *handle = cls;
1268 struct GNUNET_RECLAIM_AttributeListEntry *le;
1269 struct GNUNET_RECLAIM_CredentialListEntry *ale;
1270
1271 for (ale = handle->credentials->list_head; NULL != ale; ale = ale->next)
1272 {
1273 if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&ale->credential->id,
1274 &cred->id))
1275 continue;
1276 /** Credential already in list **/
1277 GNUNET_RECLAIM_get_credentials_next (handle->cred_it);
1278 return;
1279 }
1280
1281 for (le = handle->attr_idtoken_list->list_head; NULL != le; le = le->next)
1282 {
1283 if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&le->attribute->credential,
1284 &cred->id))
1285 continue;
1286 /** Credential matches for attribute, add **/
1287 ale = GNUNET_new (struct GNUNET_RECLAIM_CredentialListEntry);
1288 ale->credential = GNUNET_RECLAIM_credential_new (cred->name,
1289 cred->type,
1290 cred->data,
1291 cred->data_size);
1292 GNUNET_CONTAINER_DLL_insert (handle->credentials->list_head,
1293 handle->credentials->list_tail,
1294 ale);
1295 }
1296 GNUNET_RECLAIM_get_credentials_next (handle->cred_it);
1297}
1298
1299
1300static void
1301oidc_attr_collect_finished_cb (void *cls)
1302{
1303 struct RequestHandle *handle = cls;
1304
1305 handle->attr_it = NULL;
1306 handle->ticket_it = NULL;
1307 if (NULL == handle->attr_idtoken_list->list_head)
1308 {
1309 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1310 handle->edesc = GNUNET_strdup ("The requested scope is not available.");
1311 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1312 return;
1313 }
1314 handle->credentials = GNUNET_new (struct GNUNET_RECLAIM_CredentialList);
1315 handle->cred_it =
1316 GNUNET_RECLAIM_get_credentials_start (idp,
1317 &handle->priv_key,
1318 &oidc_iteration_error,
1319 handle,
1320 &oidc_cred_collect,
1321 handle,
1322 &oidc_cred_collect_finished_cb,
1323 handle);
1324
1325}
1326
1327
1328static int
1329attr_in_claims_request (struct RequestHandle *handle,
1330 const char *attr_name,
1331 const char *claims_parameter)
1332{
1333 int ret = GNUNET_NO;
1334 json_t *root;
1335 json_error_t error;
1336 json_t *claims_j;
1337 const char *key;
1338 json_t *value;
1339
1340 /** Check if attribute is requested through a scope **/
1341 if (GNUNET_YES == OIDC_check_scopes_for_claim_request (handle->oidc->scope,
1342 attr_name))
1343 return GNUNET_YES;
1344
1345 /** Try claims parameter if not in scope */
1346 if (NULL != handle->oidc->claims)
1347 {
1348 root = json_loads (handle->oidc->claims, JSON_DECODE_ANY, &error);
1349 claims_j = json_object_get (root, claims_parameter);
1350 /* obj is a JSON object */
1351 if (NULL != claims_j)
1352 {
1353 json_object_foreach (claims_j, key, value) {
1354 if (0 != strcmp (attr_name, key))
1355 continue;
1356 ret = GNUNET_YES;
1357 break;
1358 }
1359 }
1360 json_decref (root);
1361 }
1362 return ret;
1363}
1364
1365
1366static int
1367attr_in_idtoken_request (struct RequestHandle *handle,
1368 const char *attr_name)
1369{
1370 return attr_in_claims_request (handle, attr_name, "id_token");
1371}
1372
1373
1374static int
1375attr_in_userinfo_request (struct RequestHandle *handle,
1376 const char *attr_name)
1377{
1378 return attr_in_claims_request (handle, attr_name, "userinfo");
1379}
1380
1381
1382/**
1383 * Collects all attributes for an ego if in scope parameter
1384 */
1385static void
1386oidc_attr_collect (void *cls,
1387 const struct GNUNET_CRYPTO_PublicKey *identity,
1388 const struct GNUNET_RECLAIM_Attribute *attr)
1389{
1390 struct RequestHandle *handle = cls;
1391 struct GNUNET_RECLAIM_AttributeListEntry *le;
1392 if (GNUNET_YES == attr_in_idtoken_request (handle, attr->name))
1393 {
1394 le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1395 le->attribute = GNUNET_RECLAIM_attribute_new (attr->name,
1396 &attr->credential,
1397 attr->type,
1398 attr->data,
1399 attr->data_size);
1400 le->attribute->id = attr->id;
1401 le->attribute->flag = attr->flag;
1402 le->attribute->credential = attr->credential;
1403 GNUNET_CONTAINER_DLL_insert (handle->attr_idtoken_list->list_head,
1404 handle->attr_idtoken_list->list_tail,
1405 le);
1406 }
1407 if (GNUNET_YES == attr_in_userinfo_request (handle, attr->name))
1408 {
1409 le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1410 le->attribute = GNUNET_RECLAIM_attribute_new (attr->name,
1411 &attr->credential,
1412 attr->type,
1413 attr->data,
1414 attr->data_size);
1415 le->attribute->id = attr->id;
1416 le->attribute->flag = attr->flag;
1417 le->attribute->credential = attr->credential;
1418 GNUNET_CONTAINER_DLL_insert (handle->attr_userinfo_list->list_head,
1419 handle->attr_userinfo_list->list_tail,
1420 le);
1421 }
1422
1423 GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1424}
1425
1426
1427/**
1428 * Checks time and cookie and redirects accordingly
1429 */
1430static void
1431code_redirect (void *cls)
1432{
1433 struct RequestHandle *handle = cls;
1434 struct GNUNET_TIME_Absolute current_time;
1435 struct GNUNET_TIME_Absolute *relog_time;
1436 struct GNUNET_CRYPTO_PublicKey pubkey;
1437 struct GNUNET_CRYPTO_PublicKey ego_pkey;
1438 struct GNUNET_HashCode cache_key;
1439 char *identity_cookie;
1440
1441 GNUNET_asprintf (&identity_cookie,
1442 "Identity=%s",
1443 handle->oidc->login_identity);
1444 GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key);
1445 GNUNET_free (identity_cookie);
1446 // No login time for identity -> redirect to login
1447 if (GNUNET_YES ==
1448 GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key))
1449 {
1450 relog_time =
1451 GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1452 current_time = GNUNET_TIME_absolute_get ();
1453 // 30 min after old login -> redirect to login
1454 if (current_time.abs_value_us <= relog_time->abs_value_us)
1455 {
1456 if (GNUNET_OK !=
1457 GNUNET_CRYPTO_public_key_from_string (handle->oidc
1458 ->login_identity,
1459 &pubkey))
1460 {
1461 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_COOKIE);
1462 handle->edesc =
1463 GNUNET_strdup ("The cookie of a login identity is not valid");
1464 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1465 return;
1466 }
1467 // iterate over egos and compare their public key
1468 for (handle->ego_entry = ego_head; NULL != handle->ego_entry;
1469 handle->ego_entry = handle->ego_entry->next)
1470 {
1471 GNUNET_IDENTITY_ego_get_public_key (handle->ego_entry->ego, &ego_pkey);
1472 if (0 == GNUNET_memcmp (&ego_pkey, &pubkey))
1473 {
1474 handle->priv_key =
1475 *GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego);
1476 handle->attr_idtoken_list =
1477 GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
1478 handle->attr_userinfo_list =
1479 GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
1480 handle->attr_it =
1481 GNUNET_RECLAIM_get_attributes_start (idp,
1482 &handle->priv_key,
1483 &oidc_iteration_error,
1484 handle,
1485 &oidc_attr_collect,
1486 handle,
1487 &oidc_attr_collect_finished_cb,
1488 handle);
1489 return;
1490 }
1491 }
1492 GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1493 return;
1494 }
1495 }
1496}
1497
1498
1499static void
1500build_redirect (void *cls)
1501{
1502 struct RequestHandle *handle = cls;
1503 struct MHD_Response *resp;
1504 char *redirect_uri;
1505
1506 if (GNUNET_YES == handle->oidc->user_cancelled)
1507 {
1508 if ((NULL != handle->redirect_prefix) &&
1509 (NULL != handle->redirect_suffix) && (NULL != handle->tld))
1510 {
1511 GNUNET_asprintf (&redirect_uri,
1512 "%s.%s/%s?error=%s&error_description=%s&state=%s",
1513 handle->redirect_prefix,
1514 handle->tld,
1515 handle->redirect_suffix,
1516 "access_denied",
1517 "User denied access",
1518 handle->oidc->state);
1519 }
1520 else
1521 {
1522 GNUNET_asprintf (&redirect_uri,
1523 "%s?error=%s&error_description=%s&state=%s",
1524 handle->oidc->redirect_uri,
1525 "access_denied",
1526 "User denied access",
1527 handle->oidc->state);
1528 }
1529 resp = GNUNET_REST_create_response ("");
1530 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
1531 "Location",
1532 redirect_uri));
1533 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1534 cleanup_handle (handle);
1535 GNUNET_free (redirect_uri);
1536 return;
1537 }
1538 GNUNET_SCHEDULER_add_now (&code_redirect, handle);
1539}
1540
1541
1542static void
1543lookup_redirect_uri_result (void *cls,
1544 uint32_t rd_count,
1545 const struct GNUNET_GNSRECORD_Data *rd)
1546{
1547 struct RequestHandle *handle = cls;
1548 char *tmp;
1549 char *tmp_key_str;
1550 char *pos;
1551 struct GNUNET_CRYPTO_PublicKey redirect_zone;
1552
1553 handle->gns_op = NULL;
1554 if (0 == rd_count)
1555 {
1556 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1557 handle->edesc =
1558 GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1559 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1560 return;
1561 }
1562 for (int i = 0; i < rd_count; i++)
1563 {
1564 if (GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT != rd[i].record_type)
1565 continue;
1566 if (0 != strncmp (rd[i].data, handle->oidc->redirect_uri, rd[i].data_size))
1567 continue;
1568 tmp = GNUNET_strndup (rd[i].data, rd[i].data_size);
1569 if (NULL == strstr (tmp, handle->oidc->client_id))
1570 {
1571 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1572 "Redirect uri %s does not contain client_id %s\n",
1573 tmp,
1574 handle->oidc->client_id);
1575 }
1576 else
1577 {
1578 pos = strrchr (tmp, (unsigned char) '.');
1579 if (NULL == pos)
1580 {
1581 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1582 "Redirect uri %s contains client_id but is malformed\n",
1583 tmp);
1584 GNUNET_free (tmp);
1585 continue;
1586 }
1587 *pos = '\0';
1588 handle->redirect_prefix = GNUNET_strdup (tmp);
1589 tmp_key_str = pos + 1;
1590 pos = strchr (tmp_key_str, (unsigned char) '/');
1591 if (NULL == pos)
1592 {
1593 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1594 "Redirect uri %s contains client_id but is malformed\n",
1595 tmp);
1596 GNUNET_free (tmp);
1597 continue;
1598 }
1599 *pos = '\0';
1600 handle->redirect_suffix = GNUNET_strdup (pos + 1);
1601
1602 GNUNET_STRINGS_string_to_data (tmp_key_str,
1603 strlen (tmp_key_str),
1604 &redirect_zone,
1605 sizeof(redirect_zone));
1606 }
1607 GNUNET_SCHEDULER_add_now (&build_redirect, handle);
1608 GNUNET_free (tmp);
1609 return;
1610 }
1611 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1612 handle->edesc =
1613 GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1614 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1615}
1616
1617
1618/**
1619 * Initiate redirect back to client.
1620 */
1621static void
1622client_redirect (void *cls)
1623{
1624 struct RequestHandle *handle = cls;
1625
1626 /* Lookup client redirect uri to verify request */
1627 handle->gns_op =
1628 GNUNET_GNS_lookup (gns_handle,
1629 GNUNET_GNS_EMPTY_LABEL_AT,
1630 &handle->oidc->client_pkey,
1631 GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT,
1632 GNUNET_GNS_LO_DEFAULT,
1633 &lookup_redirect_uri_result,
1634 handle);
1635}
1636
1637
1638static char *
1639get_url_parameter_copy (const struct RequestHandle *handle, const char *key)
1640{
1641 struct GNUNET_HashCode hc;
1642 char *value;
1643 char *res;
1644
1645 GNUNET_CRYPTO_hash (key, strlen (key), &hc);
1646 if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1647 ->url_param_map,
1648 &hc))
1649 return NULL;
1650 value =
1651 GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, &hc);
1652 if (NULL == value)
1653 return NULL;
1654 GNUNET_STRINGS_urldecode (value, strlen (value), &res);
1655 return res;
1656}
1657
1658
1659/**
1660 * Iteration over all results finished, build final
1661 * response.
1662 *
1663 * @param cls the `struct RequestHandle`
1664 */
1665static void
1666build_authz_response (void *cls)
1667{
1668 struct RequestHandle *handle = cls;
1669 struct GNUNET_HashCode cache_key;
1670
1671 char *expected_scope;
1672 char delimiter[] = " ";
1673 int number_of_ignored_parameter, iterator;
1674
1675
1676 // REQUIRED value: redirect_uri
1677 handle->oidc->redirect_uri =
1678 get_url_parameter_copy (handle, OIDC_REDIRECT_URI_KEY);
1679 if (NULL == handle->oidc->redirect_uri)
1680 {
1681 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1682 handle->edesc = GNUNET_strdup ("missing parameter redirect_uri");
1683 GNUNET_SCHEDULER_add_now (&do_error, handle);
1684 return;
1685 }
1686
1687 // REQUIRED value: response_type
1688 handle->oidc->response_type =
1689 get_url_parameter_copy (handle, OIDC_RESPONSE_TYPE_KEY);
1690 if (NULL == handle->oidc->response_type)
1691 {
1692 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1693 handle->edesc = GNUNET_strdup ("missing parameter response_type");
1694 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1695 return;
1696 }
1697
1698 // REQUIRED value: scope
1699 handle->oidc->scope = get_url_parameter_copy (handle, OIDC_SCOPE_KEY);
1700 if (NULL == handle->oidc->scope)
1701 {
1702 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1703 handle->edesc = GNUNET_strdup ("missing parameter scope");
1704 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1705 return;
1706 }
1707
1708 // OPTIONAL value: nonce
1709 handle->oidc->nonce = get_url_parameter_copy (handle, OIDC_NONCE_KEY);
1710
1711 // OPTIONAL value: claims
1712 handle->oidc->claims = get_url_parameter_copy (handle, OIDC_CLAIMS_KEY);
1713
1714 // TODO check other values if needed
1715 number_of_ignored_parameter =
1716 sizeof(OIDC_ignored_parameter_array) / sizeof(char *);
1717 for (iterator = 0; iterator < number_of_ignored_parameter; iterator++)
1718 {
1719 GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
1720 strlen (OIDC_ignored_parameter_array[iterator]),
1721 &cache_key);
1722 if (GNUNET_YES ==
1723 GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1724 ->url_param_map,
1725 &cache_key))
1726 {
1727 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_ACCESS_DENIED);
1728 GNUNET_asprintf (&handle->edesc,
1729 "Server will not handle parameter: %s",
1730 OIDC_ignored_parameter_array[iterator]);
1731 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1732 return;
1733 }
1734 }
1735
1736 // We only support authorization code flows.
1737 if (0 != strcmp (handle->oidc->response_type,
1738 OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE))
1739 {
1740 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE);
1741 handle->edesc = GNUNET_strdup ("The authorization server does not support "
1742 "obtaining this authorization code.");
1743 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1744 return;
1745 }
1746
1747 // Checks if scope contains 'openid'
1748 expected_scope = GNUNET_strdup (handle->oidc->scope);
1749 char *test;
1750 test = strtok (expected_scope, delimiter);
1751 while (NULL != test)
1752 {
1753 if (0 == strcmp (OIDC_EXPECTED_AUTHORIZATION_SCOPE, expected_scope))
1754 break;
1755 test = strtok (NULL, delimiter);
1756 }
1757 if (NULL == test)
1758 {
1759 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1760 handle->edesc =
1761 GNUNET_strdup ("The requested scope is invalid, unknown, or malformed.");
1762 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1763 GNUNET_free (expected_scope);
1764 return;
1765 }
1766
1767 GNUNET_free (expected_scope);
1768 if ((NULL == handle->oidc->login_identity) &&
1769 (GNUNET_NO == handle->oidc->user_cancelled))
1770 GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1771 else
1772 GNUNET_SCHEDULER_add_now (&client_redirect, handle);
1773}
1774
1775
1776/**
1777 * Iterate over tlds in config
1778 */
1779static void
1780tld_iter (void *cls, const char *section, const char *option, const char *value)
1781{
1782 struct RequestHandle *handle = cls;
1783 struct GNUNET_CRYPTO_PublicKey pkey;
1784
1785 if (GNUNET_OK !=
1786 GNUNET_CRYPTO_public_key_from_string (value, &pkey))
1787 {
1788 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Skipping non key %s\n", value);
1789 return;
1790 }
1791 if (0 == GNUNET_memcmp (&pkey, &handle->oidc->client_pkey))
1792 handle->tld = GNUNET_strdup (option + 1);
1793}
1794
1795
1796/**
1797 * Responds to authorization GET and url-encoded POST request
1798 *
1799 * @param con_handle the connection handle
1800 * @param url the url
1801 * @param cls the RequestHandle
1802 */
1803static void
1804authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1805 const char *url,
1806 void *cls)
1807{
1808 struct RequestHandle *handle = cls;
1809 struct EgoEntry *tmp_ego;
1810 const struct GNUNET_CRYPTO_PrivateKey *priv_key;
1811 struct GNUNET_CRYPTO_PublicKey pkey;
1812
1813 cookie_identity_interpretation (handle);
1814
1815 // RECOMMENDED value: state - REQUIRED for answers
1816 handle->oidc->state = get_url_parameter_copy (handle, OIDC_STATE_KEY);
1817
1818 // REQUIRED value: client_id
1819 handle->oidc->client_id = get_url_parameter_copy (handle, OIDC_CLIENT_ID_KEY);
1820 if (NULL == handle->oidc->client_id)
1821 {
1822 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1823 handle->edesc = GNUNET_strdup ("missing parameter client_id");
1824 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1825 GNUNET_SCHEDULER_add_now (&do_error, handle);
1826 return;
1827 }
1828
1829 // OPTIONAL value: code_challenge
1830 handle->oidc->code_challenge = get_url_parameter_copy (handle,
1831 OIDC_CODE_CHALLENGE_KEY);
1832 if (NULL == handle->oidc->code_challenge)
1833 {
1834 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1835 "OAuth authorization request does not contain PKCE parameters!\n");
1836 }
1837
1838 if (GNUNET_OK !=
1839 GNUNET_CRYPTO_public_key_from_string (handle->oidc->client_id,
1840 &handle->oidc->client_pkey))
1841 {
1842 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT);
1843 handle->edesc = GNUNET_strdup ("The client is not authorized to request an "
1844 "authorization code using this method.");
1845 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1846 GNUNET_SCHEDULER_add_now (&do_error, handle);
1847 return;
1848 }
1849
1850 // If we know this identity, translated the corresponding TLD
1851 // TODO: We might want to have a reverse lookup functionality for TLDs?
1852 for (tmp_ego = ego_head; NULL != tmp_ego; tmp_ego = tmp_ego->next)
1853 {
1854 priv_key = GNUNET_IDENTITY_ego_get_private_key (tmp_ego->ego);
1855 GNUNET_CRYPTO_key_get_public (priv_key, &pkey);
1856 if (0 == GNUNET_memcmp (&pkey, &handle->oidc->client_pkey))
1857 {
1858 handle->tld = GNUNET_strdup (tmp_ego->identifier);
1859 handle->ego_entry = ego_tail;
1860 }
1861 }
1862 if (NULL == handle->tld)
1863 GNUNET_CONFIGURATION_iterate_section_values (oid_cfg, "gns", tld_iter,
1864 handle);
1865 if (NULL == handle->tld)
1866 handle->tld = GNUNET_strdup (handle->oidc->client_id);
1867 GNUNET_SCHEDULER_add_now (&build_authz_response, handle);
1868}
1869
1870
1871/**
1872 * Combines an identity with a login time and responds OK to login request
1873 *
1874 * @param con_handle the connection handle
1875 * @param url the url
1876 * @param cls the RequestHandle
1877 */
1878static void
1879login_cont (struct GNUNET_REST_RequestHandle *con_handle,
1880 const char *url,
1881 void *cls)
1882{
1883 struct MHD_Response *resp = GNUNET_REST_create_response ("");
1884 struct RequestHandle *handle = cls;
1885 struct GNUNET_HashCode cache_key;
1886 struct GNUNET_TIME_Absolute *current_time;
1887 struct GNUNET_TIME_Absolute *last_time;
1888 char *cookie;
1889 char *header_val;
1890 json_t *root;
1891 json_error_t error;
1892 json_t *identity;
1893 char term_data[handle->rest_handle->data_size + 1];
1894
1895 term_data[handle->rest_handle->data_size] = '\0';
1896 GNUNET_memcpy (term_data,
1897 handle->rest_handle->data,
1898 handle->rest_handle->data_size);
1899 root = json_loads (term_data, JSON_DECODE_ANY, &error);
1900 identity = json_object_get (root, "identity");
1901 if (! json_is_string (identity))
1902 {
1903 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1904 "Error parsing json string from %s\n",
1905 term_data);
1906 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1907 json_decref (root);
1908 cleanup_handle (handle);
1909 return;
1910 }
1911 GNUNET_asprintf (&cookie, "Identity=%s", json_string_value (identity));
1912 GNUNET_asprintf (&header_val,
1913 "%s;Max-Age=%d",
1914 cookie,
1915 OIDC_COOKIE_EXPIRATION);
1916 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
1917 "Set-Cookie", header_val));
1918 GNUNET_assert (MHD_NO !=
1919 MHD_add_response_header (resp,
1920 "Access-Control-Allow-Methods",
1921 "POST"));
1922 GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key);
1923
1924 if (0 != strcmp (json_string_value (identity), "Denied"))
1925 {
1926 current_time = GNUNET_new (struct GNUNET_TIME_Absolute);
1927 *current_time = GNUNET_TIME_relative_to_absolute (
1928 GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (),
1929 OIDC_COOKIE_EXPIRATION));
1930 last_time =
1931 GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1932 GNUNET_free (last_time);
1933 GNUNET_CONTAINER_multihashmap_put (OIDC_cookie_jar_map,
1934 &cache_key,
1935 current_time,
1936 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1937 }
1938 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1939 GNUNET_free (cookie);
1940 GNUNET_free (header_val);
1941 json_decref (root);
1942 cleanup_handle (handle);
1943}
1944
1945
1946static int
1947parse_credentials_basic_auth (struct RequestHandle *handle,
1948 char **client_id,
1949 char **client_secret)
1950{
1951 struct GNUNET_HashCode cache_key;
1952 char *authorization;
1953 char *credentials;
1954 char *basic_authorization;
1955 char *client_id_tmp;
1956 char *pass;
1957
1958 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1959 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1960 &cache_key);
1961 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1962 ->header_param_map,
1963 &cache_key))
1964 return GNUNET_SYSERR;
1965 authorization =
1966 GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
1967 &cache_key);
1968
1969 // split header in "Basic" and [content]
1970 credentials = strtok (authorization, " ");
1971 if ((NULL == credentials) || (0 != strcmp ("Basic", credentials)))
1972 return GNUNET_SYSERR;
1973 credentials = strtok (NULL, " ");
1974 if (NULL == credentials)
1975 return GNUNET_SYSERR;
1976 GNUNET_STRINGS_base64_decode (credentials,
1977 strlen (credentials),
1978 (void **) &basic_authorization);
1979
1980 if (NULL == basic_authorization)
1981 return GNUNET_SYSERR;
1982 client_id_tmp = strtok (basic_authorization, ":");
1983 if (NULL == client_id_tmp)
1984 {
1985 GNUNET_free (basic_authorization);
1986 return GNUNET_SYSERR;
1987 }
1988 pass = strtok (NULL, ":");
1989 if (NULL == pass)
1990 {
1991 GNUNET_free (basic_authorization);
1992 return GNUNET_SYSERR;
1993 }
1994 *client_id = strdup (client_id_tmp);
1995 *client_secret = strdup (pass);
1996 GNUNET_free (basic_authorization);
1997 return GNUNET_OK;
1998}
1999
2000
2001static int
2002parse_credentials_post_body (struct RequestHandle *handle,
2003 char **client_id,
2004 char **client_secret)
2005{
2006 struct GNUNET_HashCode cache_key;
2007 char *client_id_tmp;
2008 char *pass;
2009
2010 GNUNET_CRYPTO_hash ("client_id",
2011 strlen ("client_id"),
2012 &cache_key);
2013 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
2014 ->url_param_map,
2015 &cache_key))
2016 return GNUNET_SYSERR;
2017 client_id_tmp = GNUNET_CONTAINER_multihashmap_get (
2018 handle->rest_handle->url_param_map,
2019 &cache_key);
2020 if (NULL == client_id_tmp)
2021 return GNUNET_SYSERR;
2022 *client_id = strdup (client_id_tmp);
2023 GNUNET_CRYPTO_hash ("client_secret",
2024 strlen ("client_secret"),
2025 &cache_key);
2026 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
2027 ->url_param_map,
2028 &cache_key))
2029 {
2030 GNUNET_free (*client_id);
2031 *client_id = NULL;
2032 return GNUNET_SYSERR;
2033 }
2034 pass = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
2035 &cache_key);
2036 if (NULL == pass)
2037 {
2038 GNUNET_free (*client_id);
2039 *client_id = NULL;
2040 return GNUNET_SYSERR;
2041 }
2042 *client_secret = strdup (pass);
2043 return GNUNET_OK;
2044}
2045
2046
2047static int
2048check_authorization (struct RequestHandle *handle,
2049 struct GNUNET_CRYPTO_PublicKey *cid)
2050{
2051 char *expected_pass;
2052 char *received_cid;
2053 char *received_cpw;
2054 char *pkce_cv;
2055
2056 if (GNUNET_OK == parse_credentials_basic_auth (handle,
2057 &received_cid,
2058 &received_cpw))
2059 {
2060 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2061 "Received client credentials in HTTP AuthZ header\n");
2062 }
2063 else if (GNUNET_OK == parse_credentials_post_body (handle,
2064 &received_cid,
2065 &received_cpw))
2066 {
2067 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2068 "Received client credentials in POST body\n");
2069 }
2070 else
2071 {
2072 /** Allow public clients with PKCE **/
2073 pkce_cv = get_url_parameter_copy (handle, OIDC_CODE_VERIFIER_KEY);
2074 if (NULL == pkce_cv)
2075 {
2076 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
2077 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2078 return GNUNET_SYSERR;
2079 }
2080 handle->public_client = GNUNET_YES;
2081 GNUNET_free (pkce_cv);
2082 received_cid = get_url_parameter_copy (handle, OIDC_CLIENT_ID_KEY);
2083 GNUNET_STRINGS_string_to_data (received_cid,
2084 strlen (received_cid),
2085 cid,
2086 sizeof(struct GNUNET_CRYPTO_PublicKey));
2087 GNUNET_free (received_cid);
2088 return GNUNET_OK;
2089
2090 }
2091
2092 // check client password
2093 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (oid_cfg,
2094 "reclaim-rest-plugin",
2095 "OIDC_CLIENT_HMAC_SECRET",
2096 &expected_pass))
2097 {
2098 if (0 != strcmp (expected_pass, received_cpw))
2099 {
2100 GNUNET_free (expected_pass);
2101 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
2102 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2103 GNUNET_free (received_cpw);
2104 GNUNET_free (received_cid);
2105 return GNUNET_SYSERR;
2106 }
2107 GNUNET_free (expected_pass);
2108 }
2109 else
2110 {
2111 GNUNET_free (received_cpw);
2112 GNUNET_free (received_cid);
2113 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
2114 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
2115 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
2116 return GNUNET_SYSERR;
2117 }
2118 // check client_id
2119 for (handle->ego_entry = ego_head; NULL != handle->ego_entry;
2120 handle->ego_entry = handle->ego_entry->next)
2121 {
2122 if (0 == strcmp (handle->ego_entry->keystring, received_cid))
2123 break;
2124 }
2125 if (NULL == handle->ego_entry)
2126 {
2127 GNUNET_free (received_cpw);
2128 GNUNET_free (received_cid);
2129 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
2130 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2131 return GNUNET_SYSERR;
2132 }
2133 GNUNET_STRINGS_string_to_data (received_cid,
2134 strlen (received_cid),
2135 cid,
2136 sizeof(struct GNUNET_CRYPTO_PublicKey));
2137
2138 GNUNET_free (received_cpw);
2139 GNUNET_free (received_cid);
2140 return GNUNET_OK;
2141}
2142
2143
2144const struct EgoEntry *
2145find_ego (struct RequestHandle *handle,
2146 struct GNUNET_CRYPTO_PublicKey *test_key)
2147{
2148 struct EgoEntry *ego_entry;
2149 struct GNUNET_CRYPTO_PublicKey pub_key;
2150
2151 for (ego_entry = ego_head; NULL != ego_entry;
2152 ego_entry = ego_entry->next)
2153 {
2154 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pub_key);
2155 if (0 == GNUNET_memcmp (&pub_key, test_key))
2156 return ego_entry;
2157 }
2158 return NULL;
2159}
2160
2161
2162/**
2163 * Responds to token url-encoded POST request
2164 *
2165 * @param con_handle the connection handle
2166 * @param url the url
2167 * @param cls the RequestHandle
2168 */
2169static void
2170token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2171 const char *url,
2172 void *cls)
2173{
2174 struct RequestHandle *handle = cls;
2175 const struct EgoEntry *ego_entry = NULL;
2176 struct GNUNET_TIME_Relative expiration_time;
2177 struct GNUNET_RECLAIM_AttributeList *cl = NULL;
2178 struct GNUNET_RECLAIM_PresentationList *pl = NULL;
2179 struct GNUNET_RECLAIM_Ticket ticket;
2180 struct GNUNET_CRYPTO_PublicKey cid;
2181 struct GNUNET_HashCode cache_key;
2182 struct MHD_Response *resp = NULL;
2183 char *grant_type = NULL;
2184 char *code = NULL;
2185 char *json_response = NULL;
2186 char *id_token = NULL;
2187 char *access_token = NULL;
2188 char *jwa = NULL;
2189 char *jwt_secret = NULL;
2190 char *nonce = NULL;
2191 char *code_verifier = NULL;
2192 json_t *oidc_jwk = NULL;
2193 char *oidc_jwk_path = NULL;
2194 char *oidc_directory = NULL;
2195 char *tmp_at = NULL;
2196
2197 /*
2198 * Check Authorization
2199 */
2200 if (GNUNET_SYSERR == check_authorization (handle, &cid))
2201 {
2202 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2203 "OIDC authorization for token endpoint failed\n");
2204 GNUNET_SCHEDULER_add_now (&do_error, handle);
2205 return;
2206 }
2207
2208 /*
2209 * Check parameter
2210 */
2211
2212 // TODO Do not allow multiple equal parameter names
2213 // REQUIRED grant_type
2214 GNUNET_CRYPTO_hash (OIDC_GRANT_TYPE_KEY,
2215 strlen (OIDC_GRANT_TYPE_KEY),
2216 &cache_key);
2217 grant_type = get_url_parameter_copy (handle, OIDC_GRANT_TYPE_KEY);
2218 if (NULL == grant_type)
2219 {
2220 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2221 handle->edesc = GNUNET_strdup ("missing parameter grant_type");
2222 handle->response_code = MHD_HTTP_BAD_REQUEST;
2223 GNUNET_SCHEDULER_add_now (&do_error, handle);
2224 return;
2225 }
2226
2227 // Check parameter grant_type == "authorization_code"
2228 if (0 != strcmp (OIDC_GRANT_TYPE_VALUE, grant_type))
2229 {
2230 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE);
2231 handle->response_code = MHD_HTTP_BAD_REQUEST;
2232 GNUNET_free (grant_type);
2233 GNUNET_SCHEDULER_add_now (&do_error, handle);
2234 return;
2235 }
2236 GNUNET_free (grant_type);
2237 // REQUIRED code
2238 code = get_url_parameter_copy (handle, OIDC_CODE_KEY);
2239 if (NULL == code)
2240 {
2241 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2242 handle->edesc = GNUNET_strdup ("missing parameter code");
2243 handle->response_code = MHD_HTTP_BAD_REQUEST;
2244 GNUNET_SCHEDULER_add_now (&do_error, handle);
2245 return;
2246 }
2247 ego_entry = find_ego (handle, &cid);
2248 if (NULL == ego_entry)
2249 {
2250 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2251 handle->edesc = GNUNET_strdup ("Unknown client");
2252 handle->response_code = MHD_HTTP_BAD_REQUEST;
2253 GNUNET_free (code);
2254 GNUNET_SCHEDULER_add_now (&do_error, handle);
2255 return;
2256 }
2257
2258 // REQUIRED code verifier
2259 code_verifier = get_url_parameter_copy (handle, OIDC_CODE_VERIFIER_KEY);
2260 if (NULL == code_verifier)
2261 {
2262 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2263 "OAuth authorization request does not contain PKCE parameters!\n");
2264
2265 }
2266
2267 // decode code
2268 if (GNUNET_OK != OIDC_parse_authz_code (&cid, code, code_verifier, &ticket,
2269 &cl, &pl, &nonce,
2270 OIDC_VERIFICATION_DEFAULT))
2271 {
2272 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2273 handle->edesc = GNUNET_strdup ("invalid code");
2274 handle->response_code = MHD_HTTP_BAD_REQUEST;
2275 GNUNET_free (code);
2276 if (NULL != code_verifier)
2277 GNUNET_free (code_verifier);
2278 GNUNET_SCHEDULER_add_now (&do_error, handle);
2279 return;
2280 }
2281 if (NULL != code_verifier)
2282 GNUNET_free (code_verifier);
2283
2284 // create jwt
2285 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (oid_cfg,
2286 "reclaim-rest-plugin",
2287 "expiration_time",
2288 &expiration_time))
2289 {
2290 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
2291 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
2292 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
2293 GNUNET_free (code);
2294 if (NULL != nonce)
2295 GNUNET_free (nonce);
2296 GNUNET_RECLAIM_attribute_list_destroy (cl);
2297 GNUNET_RECLAIM_presentation_list_destroy (pl);
2298 GNUNET_SCHEDULER_add_now (&do_error, handle);
2299 return;
2300 }
2301
2302 // Check if HMAC or RSA should be used
2303 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (oid_cfg,
2304 "reclaim-rest-plugin",
2305 "oidc_json_web_algorithm",
2306 &jwa))
2307 {
2308 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2309 "Could not read OIDC JSON Web Algorithm config attribute."
2310 "Defaulting to RS256.");
2311 jwa = JWT_ALG_VALUE_RSA;
2312 }
2313
2314 if (! strcmp (jwa, JWT_ALG_VALUE_RSA))
2315 {
2316 // Replace for now
2317 oidc_jwk_path = get_oidc_jwk_path (cls);
2318 oidc_jwk = read_jwk_from_file (oidc_jwk_path);
2319
2320 // Check if secret JWK exists
2321 if (! oidc_jwk)
2322 {
2323 // Generate and save a new key
2324 oidc_jwk = generate_jwk ();
2325 oidc_directory = get_oidc_dir_path (cls);
2326
2327 // Create new oidc directory
2328 if (GNUNET_OK != GNUNET_DISK_directory_create (oidc_directory))
2329 {
2330 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2331 ("Failed to create directory `%s' for storing oidc data\n"),
2332 oidc_directory);
2333 }
2334 else
2335 {
2336 write_jwk_to_file (oidc_jwk_path, oidc_jwk);
2337 }
2338 }
2339
2340 // Generate oidc token
2341 id_token = OIDC_generate_id_token_rsa (&ticket.audience,
2342 &ticket.identity,
2343 cl,
2344 pl,
2345 &expiration_time,
2346 (NULL != nonce) ? nonce : NULL,
2347 oidc_jwk);
2348 }
2349 else if (! strcmp (jwa, JWT_ALG_VALUE_HMAC))
2350 {
2351 // TODO OPTIONAL acr,amr,azp
2352 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (oid_cfg,
2353 "reclaim-rest-plugin",
2354 "jwt_secret",
2355 &jwt_secret))
2356 {
2357 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2358 handle->edesc = GNUNET_strdup ("No signing secret configured!");
2359 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
2360 GNUNET_free (code);
2361 GNUNET_RECLAIM_attribute_list_destroy (cl);
2362 GNUNET_RECLAIM_presentation_list_destroy (pl);
2363 if (NULL != nonce)
2364 GNUNET_free (nonce);
2365 GNUNET_SCHEDULER_add_now (&do_error, handle);
2366 return;
2367 }
2368
2369 id_token = OIDC_generate_id_token_hmac (&ticket.audience,
2370 &ticket.identity,
2371 cl,
2372 pl,
2373 &expiration_time,
2374 (NULL != nonce) ? nonce : NULL,
2375 jwt_secret);
2376
2377 GNUNET_free (jwt_secret);
2378 }
2379 else
2380 {
2381 // TODO: OPTION NOT FOUND ERROR
2382 }
2383
2384 if (NULL != nonce)
2385 GNUNET_free (nonce);
2386 access_token = OIDC_access_token_new (&ticket);
2387 /**
2388 * Store mapping from access token to code so we can later
2389 * fall back on the provided attributes in userinfo one time.
2390 */
2391 GNUNET_CRYPTO_hash (access_token,
2392 strlen (access_token),
2393 &cache_key);
2394 /**
2395 * Note to future self: This cache has the following purpose:
2396 * Some OIDC plugins call the userendpoint right after receiving an
2397 * ID token and access token. There are reasons why this would make sense.
2398 * Others not so much.
2399 * In any case, in order to smoothen out the user experience upon login
2400 * (authorization), we speculatively cache the next
2401 * userinfo response in case the actual resolution through reclaim/GNS
2402 * takes too long.
2403 */
2404 tmp_at = GNUNET_CONTAINER_multihashmap_get (oidc_code_cache,
2405 &cache_key);
2406 GNUNET_CONTAINER_multihashmap_put (oidc_code_cache,
2407 &cache_key,
2408 code,
2409 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
2410 /* If there was a previous code in there, free the old value */
2411 if (NULL != tmp_at)
2412 {
2413 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2414 "OIDC access token already issued. Cleanup.\n");
2415 GNUNET_free (tmp_at);
2416 }
2417
2418 OIDC_build_token_response (access_token,
2419 id_token,
2420 &expiration_time,
2421 &json_response);
2422
2423 resp = GNUNET_REST_create_response (json_response);
2424 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
2425 "Cache-Control",
2426 "no-store"));
2427 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
2428 "Pragma", "no-cache"));
2429 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
2430 "Content-Type",
2431 "application/json"));
2432 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2433 GNUNET_RECLAIM_attribute_list_destroy (cl);
2434 GNUNET_RECLAIM_presentation_list_destroy (pl);
2435 GNUNET_free (access_token);
2436 GNUNET_free (json_response);
2437 GNUNET_free (id_token);
2438 cleanup_handle (handle);
2439}
2440
2441
2442/**
2443 * Collects claims and stores them in handle
2444 */
2445static void
2446consume_ticket (void *cls,
2447 const struct GNUNET_CRYPTO_PublicKey *identity,
2448 const struct GNUNET_RECLAIM_Attribute *attr,
2449 const struct GNUNET_RECLAIM_Presentation *presentation)
2450{
2451 struct RequestHandle *handle = cls;
2452 struct GNUNET_RECLAIM_AttributeListEntry *ale;
2453 struct GNUNET_RECLAIM_PresentationListEntry *atle;
2454 struct MHD_Response *resp;
2455 struct GNUNET_HashCode cache_key;
2456 char *result_str;
2457 char *cached_code;
2458
2459 if (NULL != handle->consume_timeout_op)
2460 GNUNET_SCHEDULER_cancel (handle->consume_timeout_op);
2461 handle->consume_timeout_op = NULL;
2462 handle->idp_op = NULL;
2463
2464 /**
2465 * We received a reply. In any case clear the cache.
2466 */
2467 GNUNET_CRYPTO_hash (handle->access_token,
2468 strlen (handle->access_token),
2469 &cache_key);
2470 cached_code = GNUNET_CONTAINER_multihashmap_get (oidc_code_cache,
2471 &cache_key);
2472 if (NULL != cached_code)
2473 {
2474 GNUNET_assert (GNUNET_YES ==
2475 GNUNET_CONTAINER_multihashmap_remove (oidc_code_cache,
2476 &cache_key,
2477 cached_code));
2478 GNUNET_free (cached_code);
2479 }
2480
2481
2482 if (NULL == identity)
2483 {
2484 result_str = OIDC_generate_userinfo (&handle->ticket.identity,
2485 handle->attr_userinfo_list,
2486 handle->presentations);
2487 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Userinfo: %s\n", result_str);
2488 resp = GNUNET_REST_create_response (result_str);
2489 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2490 GNUNET_free (result_str);
2491 cleanup_handle (handle);
2492 return;
2493 }
2494 ale = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
2495 ale->attribute = GNUNET_RECLAIM_attribute_new (attr->name,
2496 &attr->credential,
2497 attr->type,
2498 attr->data,
2499 attr->data_size);
2500 ale->attribute->id = attr->id;
2501 ale->attribute->flag = attr->flag;
2502 ale->attribute->credential = attr->credential;
2503 GNUNET_CONTAINER_DLL_insert (handle->attr_userinfo_list->list_head,
2504 handle->attr_userinfo_list->list_tail,
2505 ale);
2506 if (NULL == presentation)
2507 return;
2508 for (atle = handle->presentations->list_head;
2509 NULL != atle; atle = atle->next)
2510 {
2511 if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (
2512 &atle->presentation->credential_id,
2513 &presentation->credential_id))
2514 continue;
2515 break; /** already in list **/
2516 }
2517 if (NULL == atle)
2518 {
2519 /** Credential matches for attribute, add **/
2520 atle = GNUNET_new (struct GNUNET_RECLAIM_PresentationListEntry);
2521 atle->presentation = GNUNET_RECLAIM_presentation_new (presentation->type,
2522 presentation->data,
2523 presentation->
2524 data_size);
2525 atle->presentation->credential_id = presentation->credential_id;
2526 GNUNET_CONTAINER_DLL_insert (handle->presentations->list_head,
2527 handle->presentations->list_tail,
2528 atle);
2529 }
2530}
2531
2532
2533static void
2534consume_fail (void *cls)
2535{
2536 struct RequestHandle *handle = cls;
2537 struct GNUNET_HashCode cache_key;
2538 struct GNUNET_RECLAIM_AttributeList *cl = NULL;
2539 struct GNUNET_RECLAIM_PresentationList *pl = NULL;
2540 struct GNUNET_RECLAIM_Ticket ticket;
2541 struct MHD_Response *resp;
2542 char *nonce;
2543 char *cached_code;
2544 char *result_str;
2545
2546
2547 handle->consume_timeout_op = NULL;
2548 if (NULL != handle->idp_op)
2549 GNUNET_RECLAIM_cancel (handle->idp_op);
2550 handle->idp_op = NULL;
2551
2552 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2553 "Ticket consumptioned timed out. Using cache...\n");
2554 GNUNET_CRYPTO_hash (handle->access_token,
2555 strlen (handle->access_token),
2556 &cache_key);
2557 cached_code = GNUNET_CONTAINER_multihashmap_get (oidc_code_cache,
2558 &cache_key);
2559 if (NULL == cached_code)
2560 {
2561 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2562 handle->edesc = GNUNET_strdup ("No Access Token in cache!");
2563 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2564 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2565 return;
2566 }
2567 /**
2568 * Remove the cached item
2569 */
2570 GNUNET_assert (GNUNET_YES ==
2571 GNUNET_CONTAINER_multihashmap_remove (oidc_code_cache,
2572 &cache_key,
2573 cached_code));
2574
2575 // decode code
2576 if (GNUNET_OK != OIDC_parse_authz_code (&handle->ticket.audience,
2577 cached_code, NULL, &ticket,
2578 &cl, &pl, &nonce,
2579 OIDC_VERIFICATION_NO_CODE_VERIFIER))
2580 {
2581 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2582 handle->edesc = GNUNET_strdup ("invalid code");
2583 handle->response_code = MHD_HTTP_BAD_REQUEST;
2584 GNUNET_free (cached_code);
2585 if (NULL != nonce)
2586 GNUNET_free (nonce);
2587 GNUNET_SCHEDULER_add_now (&do_error, handle);
2588 return;
2589 }
2590
2591 GNUNET_free (cached_code);
2592
2593 result_str = OIDC_generate_userinfo (&handle->ticket.identity,
2594 cl,
2595 pl);
2596 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Userinfo: %s\n", result_str);
2597 resp = GNUNET_REST_create_response (result_str);
2598 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2599 GNUNET_free (result_str);
2600 GNUNET_free (nonce);
2601 GNUNET_RECLAIM_attribute_list_destroy (cl);
2602 GNUNET_RECLAIM_presentation_list_destroy (pl);
2603 cleanup_handle (handle);
2604}
2605
2606
2607/**
2608 * Responds to userinfo GET and url-encoded POST request
2609 *
2610 * @param con_handle the connection handle
2611 * @param url the url
2612 * @param cls the RequestHandle
2613 */
2614static void
2615userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2616 const char *url,
2617 void *cls)
2618{
2619 // TODO expiration time
2620 struct RequestHandle *handle = cls;
2621 struct GNUNET_RECLAIM_Ticket *ticket;
2622 char delimiter[] = " ";
2623 struct GNUNET_HashCode cache_key;
2624 char *authorization;
2625 char *authorization_type;
2626 char *authorization_access_token;
2627 const struct EgoEntry *aud_ego;
2628 const struct GNUNET_CRYPTO_PrivateKey *privkey;
2629
2630 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting userinfo\n");
2631 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
2632 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
2633 &cache_key);
2634 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
2635 ->header_param_map,
2636 &cache_key))
2637 {
2638 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2639 handle->edesc = GNUNET_strdup ("No Access Token");
2640 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2641 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2642 return;
2643 }
2644 authorization =
2645 GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
2646 &cache_key);
2647
2648 // split header in "Bearer" and access_token
2649 authorization = GNUNET_strdup (authorization);
2650 authorization_type = strtok (authorization, delimiter);
2651 if ((NULL == authorization_type) ||
2652 (0 != strcmp ("Bearer", authorization_type)))
2653 {
2654 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2655 handle->edesc = GNUNET_strdup ("No Access Token");
2656 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2657 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2658 GNUNET_free (authorization);
2659 return;
2660 }
2661 authorization_access_token = strtok (NULL, delimiter);
2662 if (NULL == authorization_access_token)
2663 {
2664 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2665 handle->edesc = GNUNET_strdup ("Access token missing");
2666 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2667 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2668 GNUNET_free (authorization);
2669 return;
2670 }
2671
2672 if (GNUNET_OK != OIDC_access_token_parse (authorization_access_token,
2673 &ticket))
2674 {
2675 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2676 handle->edesc = GNUNET_strdup ("The access token is invalid");
2677 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2678 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2679 GNUNET_free (authorization);
2680 return;
2681
2682 }
2683 GNUNET_assert (NULL != ticket);
2684 handle->ticket = *ticket;
2685 GNUNET_free (ticket);
2686 aud_ego = find_ego (handle, &handle->ticket.audience);
2687 if (NULL == aud_ego)
2688 {
2689 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2690 handle->edesc = GNUNET_strdup ("The access token expired");
2691 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2692 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2693 GNUNET_free (authorization);
2694 return;
2695 }
2696 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Consuming ticket\n");
2697 privkey = GNUNET_IDENTITY_ego_get_private_key (aud_ego->ego);
2698 handle->attr_userinfo_list =
2699 GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
2700 handle->presentations =
2701 GNUNET_new (struct GNUNET_RECLAIM_PresentationList);
2702
2703 /* If the consume takes too long, we use values from the cache */
2704 handle->access_token = GNUNET_strdup (authorization_access_token);
2705 handle->consume_timeout_op = GNUNET_SCHEDULER_add_delayed (consume_timeout,
2706 &consume_fail,
2707 handle);
2708 handle->idp_op = GNUNET_RECLAIM_ticket_consume (idp,
2709 privkey,
2710 &handle->ticket,
2711 &consume_ticket,
2712 handle);
2713 GNUNET_free (authorization);
2714}
2715
2716
2717/**
2718 * Responds to /jwks.json
2719 *
2720 * @param con_handle the connection handle
2721 * @param url the url
2722 * @param cls the RequestHandle
2723 */
2724static void
2725jwks_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2726 const char *url,
2727 void *cls)
2728{
2729 char *oidc_directory;
2730 char *oidc_jwk_path;
2731 char *oidc_jwk_pub_str;
2732 json_t *oidc_jwk;
2733 struct MHD_Response *resp;
2734 struct RequestHandle *handle = cls;
2735
2736 oidc_jwk_path = get_oidc_jwk_path (cls);
2737 oidc_jwk = read_jwk_from_file (oidc_jwk_path);
2738
2739 // Check if secret JWK exists
2740 if (! oidc_jwk)
2741 {
2742 // Generate and save a new key
2743 oidc_jwk = generate_jwk ();
2744 oidc_directory = get_oidc_dir_path (cls);
2745
2746 // Create new oidc directory
2747 if (GNUNET_OK != GNUNET_DISK_directory_create (oidc_directory))
2748 {
2749 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2750 ("Failed to create directory `%s' for storing oidc data\n"),
2751 oidc_directory);
2752 }
2753 else
2754 {
2755 write_jwk_to_file (oidc_jwk_path, oidc_jwk);
2756 }
2757 }
2758
2759 // Convert secret JWK to public JWK
2760 jose_jwk_pub (NULL, oidc_jwk);
2761
2762 // Encode JWK as string and return to API endpoint
2763 oidc_jwk_pub_str = json_dumps (oidc_jwk, JSON_INDENT (1));
2764 resp = GNUNET_REST_create_response (oidc_jwk_pub_str);
2765 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2766 json_decref (oidc_jwk);
2767 GNUNET_free (oidc_jwk_pub_str);
2768 GNUNET_free (oidc_jwk_pub_str);
2769 cleanup_handle (handle);
2770}
2771
2772
2773/**
2774 * If listing is enabled, prints information about the egos.
2775 *
2776 * This function is initially called for all egos and then again
2777 * whenever a ego's identifier changes or if it is deleted. At the
2778 * end of the initial pass over all egos, the function is once called
2779 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
2780 * be invoked in the future or that there was an error.
2781 *
2782 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get', this
2783 * function is only called ONCE, and 'NULL' being passed in 'ego' does
2784 * indicate an error (for example because name is taken or no default value is
2785 * known). If 'ego' is non-NULL and if '*ctx' is set in those callbacks, the
2786 * value WILL be passed to a subsequent call to the identity callback of
2787 * 'GNUNET_IDENTITY_connect' (if that one was not NULL).
2788 *
2789 * When an identity is renamed, this function is called with the
2790 * (known) ego but the NEW identifier.
2791 *
2792 * When an identity is deleted, this function is called with the
2793 * (known) ego and "NULL" for the 'identifier'. In this case,
2794 * the 'ego' is henceforth invalid (and the 'ctx' should also be
2795 * cleaned up).
2796 *
2797 * @param cls closure
2798 * @param ego ego handle
2799 * @param ctx context for application to store data for this ego
2800 * (during the lifetime of this process, initially NULL)
2801 * @param identifier identifier assigned by the user for this ego,
2802 * NULL if the user just deleted the ego and it
2803 * must thus no longer be used
2804 */
2805static void
2806list_ego (void *cls,
2807 struct GNUNET_IDENTITY_Ego *ego,
2808 void **ctx,
2809 const char *identifier)
2810{
2811 struct EgoEntry *ego_entry;
2812 struct GNUNET_CRYPTO_PublicKey pk;
2813
2814 if (NULL == ego)
2815 {
2816 state = ID_REST_STATE_POST_INIT;
2817 return;
2818 }
2819 if (ID_REST_STATE_INIT == state)
2820
2821 {
2822 ego_entry = GNUNET_new (struct EgoEntry);
2823 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2824 ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk);
2825 ego_entry->ego = ego;
2826 ego_entry->identifier = GNUNET_strdup (identifier);
2827 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
2828 ego_tail,
2829 ego_entry);
2830 return;
2831 }
2832 /* Ego renamed or added */
2833 if (identifier != NULL)
2834 {
2835 for (ego_entry = ego_head; NULL != ego_entry;
2836 ego_entry = ego_entry->next)
2837 {
2838 if (ego_entry->ego == ego)
2839 {
2840 /* Rename */
2841 GNUNET_free (ego_entry->identifier);
2842 ego_entry->identifier = GNUNET_strdup (identifier);
2843 break;
2844 }
2845 }
2846 if (NULL == ego_entry)
2847 {
2848 /* Add */
2849 ego_entry = GNUNET_new (struct EgoEntry);
2850 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2851 ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk);
2852 ego_entry->ego = ego;
2853 ego_entry->identifier = GNUNET_strdup (identifier);
2854 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
2855 ego_tail,
2856 ego_entry);
2857 }
2858 }
2859 else
2860 {
2861 /* Delete */
2862 for (ego_entry = ego_head; NULL != ego_entry;
2863 ego_entry = ego_entry->next)
2864 {
2865 if (ego_entry->ego == ego)
2866 break;
2867 }
2868 if (NULL == ego_entry)
2869 return; /* Not found */
2870
2871 GNUNET_CONTAINER_DLL_remove (ego_head,
2872 ego_tail,
2873 ego_entry);
2874 GNUNET_free (ego_entry->identifier);
2875 GNUNET_free (ego_entry->keystring);
2876 GNUNET_free (ego_entry);
2877 return;
2878 }
2879}
2880
2881
2882static void
2883oidc_config_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2884 const char *url,
2885 void *cls)
2886{
2887 json_t *oidc_config;
2888 json_t *auth_methods;
2889 json_t *sig_algs;
2890 json_t *scopes;
2891 json_t *response_types;
2892 json_t *sub_types;
2893 json_t *claim_types;
2894 char *oidc_config_str;
2895 struct MHD_Response *resp;
2896 struct RequestHandle *handle = cls;
2897
2898 oidc_config = json_object ();
2899 // FIXME get from config?
2900 json_object_set_new (oidc_config,
2901 "issuer", json_string ("http://localhost:7776"));
2902 json_object_set_new (oidc_config,
2903 "authorization_endpoint",
2904 json_string ("https://api.reclaim/openid/authorize"));
2905 json_object_set_new (oidc_config,
2906 "token_endpoint",
2907 json_string ("http://localhost:7776/openid/token"));
2908 auth_methods = json_array ();
2909 json_array_append_new (auth_methods,
2910 json_string ("client_secret_basic"));
2911 json_array_append_new (auth_methods,
2912 json_string ("client_secret_post"));
2913 json_object_set_new (oidc_config,
2914 "token_endpoint_auth_methods_supported",
2915 auth_methods);
2916 sig_algs = json_array ();
2917 json_array_append_new (sig_algs,
2918 json_string ("HS512"));
2919 json_array_append_new (sig_algs,
2920 json_string ("RS256"));
2921 json_object_set_new (oidc_config,
2922 "id_token_signing_alg_values_supported",
2923 sig_algs);
2924 json_object_set_new (oidc_config,
2925 "jwks_uri",
2926 json_string ("http://localhost:7776/jwks.json"));
2927 json_object_set_new (oidc_config,
2928 "userinfo_endpoint",
2929 json_string ("http://localhost:7776/openid/userinfo"));
2930 scopes = json_array ();
2931 json_array_append_new (scopes,
2932 json_string ("openid"));
2933 json_array_append_new (scopes,
2934 json_string ("profile"));
2935 json_array_append_new (scopes,
2936 json_string ("email"));
2937 json_array_append_new (scopes,
2938 json_string ("address"));
2939 json_array_append_new (scopes,
2940 json_string ("phone"));
2941 json_object_set_new (oidc_config,
2942 "scopes_supported",
2943 scopes);
2944 response_types = json_array ();
2945 json_array_append_new (response_types,
2946 json_string ("code"));
2947 json_object_set_new (oidc_config,
2948 "response_types_supported",
2949 response_types);
2950 sub_types = json_array ();
2951 json_array_append_new (sub_types,
2952 json_string ("public")); /* no pairwise support */
2953 json_object_set_new (oidc_config,
2954 "subject_types_supported",
2955 sub_types);
2956 claim_types = json_array ();
2957 json_array_append_new (claim_types,
2958 json_string ("normal"));
2959 json_array_append_new (claim_types,
2960 json_string ("aggregated"));
2961 json_object_set_new (oidc_config,
2962 "claim_types_supported",
2963 claim_types);
2964 json_object_set_new (oidc_config,
2965 "claims_parameter_supported",
2966 json_boolean (1));
2967 oidc_config_str = json_dumps (oidc_config, JSON_INDENT (1));
2968 resp = GNUNET_REST_create_response (oidc_config_str);
2969 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2970 json_decref (oidc_config);
2971 GNUNET_free (oidc_config_str);
2972 cleanup_handle (handle);
2973}
2974
2975
2976/**
2977 * Respond to OPTIONS request
2978 *
2979 * @param con_handle the connection handle
2980 * @param url the url
2981 * @param cls the RequestHandle
2982 */
2983static void
2984oidc_config_cors (struct GNUNET_REST_RequestHandle *con_handle,
2985 const char *url,
2986 void *cls)
2987{
2988 struct MHD_Response *resp;
2989 struct RequestHandle *handle = cls;
2990
2991 // For now, independent of path return all options
2992 resp = GNUNET_REST_create_response (NULL);
2993 GNUNET_assert (MHD_NO !=
2994 MHD_add_response_header (resp,
2995 "Access-Control-Allow-Methods",
2996 allow_methods));
2997 GNUNET_assert (MHD_NO !=
2998 MHD_add_response_header (resp,
2999 "Access-Control-Allow-Origin",
3000 "*"));
3001 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
3002 cleanup_handle (handle);
3003 return;
3004}
3005
3006
3007enum GNUNET_GenericReturnValue
3008REST_openid_process_request (void *plugin,
3009 struct GNUNET_REST_RequestHandle *rest_handle,
3010 GNUNET_REST_ResultProcessor proc,
3011 void *proc_cls)
3012{
3013 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
3014 struct GNUNET_REST_RequestHandlerError err;
3015 static const struct GNUNET_REST_RequestHandler handlers[] =
3016 { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint },
3017 { MHD_HTTP_METHOD_POST,
3018 GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint }, // url-encoded
3019 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont },
3020 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint },
3021 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
3022 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
3023 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_JWKS, &jwks_endpoint },
3024 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_OIDC_CONFIG,
3025 &oidc_config_endpoint },
3026 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC_CONFIG,
3027 &oidc_config_cors },
3028 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC, &options_cont },
3029 GNUNET_REST_HANDLER_END };
3030
3031 handle->oidc = GNUNET_new (struct OIDC_Variables);
3032 if (NULL == OIDC_cookie_jar_map)
3033 OIDC_cookie_jar_map = GNUNET_CONTAINER_multihashmap_create (10,
3034 GNUNET_NO);
3035 if (NULL == oidc_code_cache)
3036 oidc_code_cache = GNUNET_CONTAINER_multihashmap_create (10,
3037 GNUNET_NO);
3038
3039 handle->response_code = 0;
3040 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
3041 handle->proc_cls = proc_cls;
3042 handle->proc = proc;
3043 handle->rest_handle = rest_handle;
3044 handle->url = GNUNET_strdup (rest_handle->url);
3045 handle->timeout_task =
3046 GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
3047 GNUNET_CONTAINER_DLL_insert (requests_head,
3048 requests_tail,
3049 handle);
3050 if (handle->url[strlen (handle->url) - 1] == '/')
3051 handle->url[strlen (handle->url) - 1] = '\0';
3052 if (GNUNET_NO ==
3053 GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
3054 return GNUNET_NO;
3055
3056 return GNUNET_YES;
3057}
3058
3059
3060/**
3061 * Entry point for the plugin.
3062 *
3063 * @param cls Config info
3064 * @return NULL on error, otherwise the plugin context
3065 */
3066void *
3067REST_openid_init (const struct GNUNET_CONFIGURATION_Handle *c)
3068{
3069 static struct Plugin plugin;
3070 struct GNUNET_REST_Plugin *api;
3071
3072 oid_cfg = c;
3073 if (NULL != plugin.cfg)
3074 return NULL; /* can only initialize once! */
3075 memset (&plugin, 0, sizeof(struct Plugin));
3076 plugin.cfg = oid_cfg;
3077 api = GNUNET_new (struct GNUNET_REST_Plugin);
3078 api->cls = &plugin;
3079 api->name = GNUNET_REST_API_NS_OIDC;
3080 identity_handle = GNUNET_IDENTITY_connect (oid_cfg, &list_ego, NULL);
3081 gns_handle = GNUNET_GNS_connect (oid_cfg);
3082 idp = GNUNET_RECLAIM_connect (oid_cfg);
3083 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (oid_cfg,
3084 "reclaim-rest-plugin",
3085 "OIDC_USERINFO_CONSUME_TIMEOUT",
3086 &consume_timeout))
3087 {
3088 consume_timeout = CONSUME_TIMEOUT;
3089 }
3090
3091
3092 state = ID_REST_STATE_INIT;
3093 GNUNET_asprintf (&allow_methods,
3094 "%s, %s, %s, %s, %s",
3095 MHD_HTTP_METHOD_GET,
3096 MHD_HTTP_METHOD_POST,
3097 MHD_HTTP_METHOD_PUT,
3098 MHD_HTTP_METHOD_DELETE,
3099 MHD_HTTP_METHOD_OPTIONS);
3100
3101 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3102 _ ("OpenID Connect REST API initialized\n"));
3103 return api;
3104}
3105
3106
3107static int
3108cleanup_hashmap (void *cls, const struct GNUNET_HashCode *key, void *value)
3109{
3110 GNUNET_free (value);
3111 return GNUNET_YES;
3112}
3113
3114
3115/**
3116 * Exit point from the plugin.
3117 *
3118 * @param cls the plugin context (as returned by "init")
3119 * @return always NULL
3120 */
3121void *
3122REST_openid_done (void *cls)
3123{
3124 struct GNUNET_REST_Plugin *api = cls;
3125 struct Plugin *plugin = api->cls;
3126 struct EgoEntry *ego_entry;
3127
3128 plugin->cfg = NULL;
3129 while (NULL != requests_head)
3130 cleanup_handle (requests_head);
3131 if (NULL != OIDC_cookie_jar_map)
3132 {
3133 GNUNET_CONTAINER_multihashmap_iterate (OIDC_cookie_jar_map,
3134 &cleanup_hashmap,
3135 NULL);
3136 GNUNET_CONTAINER_multihashmap_destroy (OIDC_cookie_jar_map);
3137 }
3138 if (NULL != oidc_code_cache)
3139 {
3140 GNUNET_CONTAINER_multihashmap_iterate (oidc_code_cache,
3141 &cleanup_hashmap,
3142 NULL);
3143 GNUNET_CONTAINER_multihashmap_destroy (oidc_code_cache);
3144 }
3145
3146 GNUNET_free (allow_methods);
3147 if (NULL != gns_handle)
3148 GNUNET_GNS_disconnect (gns_handle);
3149 if (NULL != identity_handle)
3150 GNUNET_IDENTITY_disconnect (identity_handle);
3151 if (NULL != idp)
3152 GNUNET_RECLAIM_disconnect (idp);
3153 while (NULL != (ego_entry = ego_head))
3154 {
3155 GNUNET_CONTAINER_DLL_remove (ego_head,
3156 ego_tail,
3157 ego_entry);
3158 GNUNET_free (ego_entry->identifier);
3159 GNUNET_free (ego_entry->keystring);
3160 GNUNET_free (ego_entry);
3161 }
3162 GNUNET_free (api);
3163 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3164 "OpenID Connect REST plugin is finished\n");
3165 return NULL;
3166}
3167
3168
3169/* end of plugin_rest_openid_connect.c */
diff --git a/src/service/rest/openid_plugin.h b/src/service/rest/openid_plugin.h
new file mode 100644
index 000000000..65545ac66
--- /dev/null
+++ b/src/service/rest/openid_plugin.h
@@ -0,0 +1,36 @@
1#include "gnunet_rest_plugin.h"
2/**
3 * Function processing the REST call
4 *
5 * @param method HTTP method
6 * @param url URL of the HTTP request
7 * @param data body of the HTTP request (optional)
8 * @param data_size length of the body
9 * @param proc callback function for the result
10 * @param proc_cls closure for @a proc
11 * @return #GNUNET_OK if request accepted
12 */
13enum GNUNET_GenericReturnValue
14REST_openid_process_request (void *plugin,
15 struct GNUNET_REST_RequestHandle *conndata_handle,
16 GNUNET_REST_ResultProcessor proc,
17 void *proc_cls);
18
19/**
20 * Entry point for the plugin.
21 *
22 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
23 * @return NULL on error, otherwise the plugin context
24 */
25void*
26REST_openid_init (const struct GNUNET_CONFIGURATION_Handle *c);
27
28
29/**
30 * Exit point from the plugin.
31 *
32 * @param cls the plugin context (as returned by "init")
33 * @return always NULL
34 */
35void
36REST_openid_done (struct GNUNET_REST_Plugin *api);
diff --git a/src/service/rest/pabc_plugin.c b/src/service/rest/pabc_plugin.c
new file mode 100644
index 000000000..4b7d21df3
--- /dev/null
+++ b/src/service/rest/pabc_plugin.c
@@ -0,0 +1,667 @@
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 * @file reclaim/plugin_rest_pabc.c
23 * @brief GNUnet pabc REST plugin
24 *
25 */
26#include "platform.h"
27#include "microhttpd.h"
28#include <inttypes.h>
29#include <jansson.h>
30#include <pabc/pabc.h>
31#include "gnunet_reclaim_lib.h"
32#include "gnunet_reclaim_service.h"
33#include "gnunet_rest_lib.h"
34#include "gnunet_rest_plugin.h"
35#include "gnunet_signatures.h"
36#include "pabc_helper.h"
37
38/**
39 * REST root namespace
40 */
41#define GNUNET_REST_API_NS_PABC "/pabc"
42
43/**
44 * Credential request endpoint
45 */
46#define GNUNET_REST_API_NS_PABC_CR "/pabc/cr"
47
48/**
49 * The configuration handle
50 */
51const struct GNUNET_CONFIGURATION_Handle *cfg;
52
53/**
54 * HTTP methods allows for this plugin
55 */
56static char *allow_methods;
57
58/**
59 * @brief struct returned by the initialization function of the plugin
60 */
61struct Plugin
62{
63 const struct GNUNET_CONFIGURATION_Handle *cfg;
64};
65
66
67struct RequestHandle
68{
69 /**
70 * DLL
71 */
72 struct RequestHandle *next;
73
74 /**
75 * DLL
76 */
77 struct RequestHandle *prev;
78
79 /**
80 * Rest connection
81 */
82 struct GNUNET_REST_RequestHandle *rest_handle;
83
84 /**
85 * Desired timeout for the lookup (default is no timeout).
86 */
87 struct GNUNET_TIME_Relative timeout;
88
89 /**
90 * ID of a task associated with the resolution process.
91 */
92 struct GNUNET_SCHEDULER_Task *timeout_task;
93
94 /**
95 * The plugin result processor
96 */
97 GNUNET_REST_ResultProcessor proc;
98
99 /**
100 * The closure of the result processor
101 */
102 void *proc_cls;
103
104 /**
105 * The url
106 */
107 char *url;
108
109 /**
110 * Error response message
111 */
112 char *emsg;
113
114 /**
115 * Response code
116 */
117 int response_code;
118
119 /**
120 * Response object
121 */
122 json_t *resp_object;
123};
124
125/**
126 * DLL
127 */
128static struct RequestHandle *requests_head;
129
130/**
131 * DLL
132 */
133static struct RequestHandle *requests_tail;
134
135
136/**
137 * Cleanup lookup handle
138 * @param handle Handle to clean up
139 */
140static void
141cleanup_handle (void *cls)
142{
143 struct RequestHandle *handle = cls;
144
145 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
146 if (NULL != handle->resp_object)
147 json_decref (handle->resp_object);
148 if (NULL != handle->timeout_task)
149 GNUNET_SCHEDULER_cancel (handle->timeout_task);
150 if (NULL != handle->url)
151 GNUNET_free (handle->url);
152 if (NULL != handle->emsg)
153 GNUNET_free (handle->emsg);
154 GNUNET_CONTAINER_DLL_remove (requests_head,
155 requests_tail,
156 handle);
157 GNUNET_free (handle);
158}
159
160
161/**
162 * Task run on error, sends error message. Cleans up everything.
163 *
164 * @param cls the `struct RequestHandle`
165 */
166static void
167do_error (void *cls)
168{
169 struct RequestHandle *handle = cls;
170 struct MHD_Response *resp;
171 char *json_error;
172
173 GNUNET_asprintf (&json_error, "{ \"error\" : \"%s\" }", handle->emsg);
174 if (0 == handle->response_code)
175 {
176 handle->response_code = MHD_HTTP_BAD_REQUEST;
177 }
178 resp = GNUNET_REST_create_response (json_error);
179 MHD_add_response_header (resp, "Content-Type", "application/json");
180 handle->proc (handle->proc_cls, resp, handle->response_code);
181 cleanup_handle (handle);
182 GNUNET_free (json_error);
183}
184
185
186/**
187 * Task run on timeout, sends error message. Cleans up everything.
188 *
189 * @param cls the `struct RequestHandle`
190 */
191static void
192do_timeout (void *cls)
193{
194 struct RequestHandle *handle = cls;
195
196 handle->timeout_task = NULL;
197 do_error (handle);
198}
199
200
201static void
202return_response (void *cls)
203{
204 char *result_str;
205 struct RequestHandle *handle = cls;
206 struct MHD_Response *resp;
207
208 result_str = json_dumps (handle->resp_object, 0);
209 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
210 resp = GNUNET_REST_create_response (result_str);
211 MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
212 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
213 GNUNET_free (result_str);
214 cleanup_handle (handle);
215}
216
217
218static enum pabc_status
219set_attributes_from_idtoken (const struct pabc_context *ctx,
220 const struct pabc_public_parameters *pp,
221 struct pabc_user_context *usr_ctx,
222 const char *id_token)
223{
224 json_t *payload_json;
225 json_t *value;
226 json_error_t json_err;
227 const char *key;
228 const char *jwt_body;
229 char *decoded_jwt;
230 char delim[] = ".";
231 char *jwt_string;
232 const char *pabc_key;
233 enum pabc_status status;
234
235 // FIXME parse JWT
236 jwt_string = GNUNET_strndup (id_token, strlen (id_token));
237 jwt_body = strtok (jwt_string, delim);
238 jwt_body = strtok (NULL, delim);
239 GNUNET_STRINGS_base64url_decode (jwt_body, strlen (jwt_body),
240 (void **) &decoded_jwt);
241 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Decoded ID Token: %s\n", decoded_jwt);
242 payload_json = json_loads (decoded_jwt, JSON_DECODE_ANY, &json_err);
243 GNUNET_free (decoded_jwt);
244
245 json_object_foreach (payload_json, key, value)
246 {
247 pabc_key = key;
248 if (0 == strcmp ("iss", key))
249 pabc_key = "issuer"; // rename
250 if (0 == strcmp ("sub", key))
251 pabc_key = "subject"; // rename
252 if (0 == strcmp ("jti", key))
253 continue;
254 if (0 == strcmp ("exp", key))
255 pabc_key = "expiration"; // rename
256 if (0 == strcmp ("iat", key))
257 continue;
258 if (0 == strcmp ("nbf", key))
259 continue;
260 if (0 == strcmp ("aud", key))
261 continue;
262 char *tmp_val;
263 if (json_is_string (value))
264 tmp_val = GNUNET_strdup (json_string_value (value));
265 else
266 tmp_val = json_dumps (value, JSON_ENCODE_ANY);
267 if (NULL == tmp_val)
268 {
269 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
270 "Unable to encode JSON value for `%s'\n", key);
271 continue;
272 }
273 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
274 "Setting `%s' to `%s'\n", key, tmp_val);
275 status = pabc_set_attribute_value_by_name (ctx, pp, usr_ctx,
276 pabc_key,
277 tmp_val);
278 GNUNET_free (tmp_val);
279 if (PABC_OK != status)
280 {
281 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
282 "Failed to set attribute `%s'.\n", key);
283 }
284 }
285 GNUNET_free (jwt_string);
286 return PABC_OK;
287}
288
289
290static enum GNUNET_GenericReturnValue
291setup_new_user_context (struct pabc_context *ctx,
292 struct pabc_public_parameters *pp,
293 struct pabc_user_context **usr_ctx)
294{
295 if (PABC_OK != pabc_new_user_context (ctx, pp, usr_ctx))
296 return GNUNET_SYSERR;
297
298 if (PABC_OK != pabc_populate_user_context (ctx, *usr_ctx))
299 {
300 pabc_free_user_context (ctx, pp, usr_ctx);
301 return GNUNET_SYSERR;
302 }
303 return GNUNET_OK;
304}
305
306
307static void
308cr_cont (struct GNUNET_REST_RequestHandle *con_handle,
309 const char *url,
310 void *cls)
311{
312 struct RequestHandle *handle = cls;
313 char term_data[handle->rest_handle->data_size + 1];
314 char *response_str;
315 json_t *data_json;
316 json_t *nonce_json;
317 json_t *pp_json;
318 json_t *idtoken_json;
319 json_t *iss_json;
320 json_t *identity_json;
321 json_error_t err;
322 struct pabc_public_parameters *pp = NULL;
323 struct pabc_context *ctx = NULL;
324 struct pabc_user_context *usr_ctx = NULL;
325 struct pabc_credential_request *cr = NULL;
326 struct pabc_nonce *nonce = NULL;
327 enum pabc_status status;
328
329
330 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
331 "Credential request...\n");
332
333 if (0 >= handle->rest_handle->data_size)
334 {
335 GNUNET_SCHEDULER_add_now (&do_error, handle);
336 return;
337 }
338
339 term_data[handle->rest_handle->data_size] = '\0';
340 GNUNET_memcpy (term_data,
341 handle->rest_handle->data,
342 handle->rest_handle->data_size);
343 data_json = json_loads (term_data, JSON_DECODE_ANY, &err);
344 if (NULL == data_json)
345 {
346 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
347 "Unable to parse %s\n", term_data);
348 GNUNET_SCHEDULER_add_now (&do_error, handle);
349 return;
350 }
351 if (! json_is_object (data_json))
352 {
353 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
354 "Unable to parse %s\n", term_data);
355 json_decref (data_json);
356 GNUNET_SCHEDULER_add_now (&do_error, handle);
357 return;
358 }
359
360 nonce_json = json_object_get (data_json, "nonce");
361 if (NULL == nonce_json)
362 {
363 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
364 "Unable to parse nonce\n");
365 json_decref (data_json);
366 GNUNET_SCHEDULER_add_now (&do_error, handle);
367 return;
368 }
369 iss_json = json_object_get (data_json, "issuer");
370 if (NULL == iss_json)
371 {
372 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
373 "Unable to parse issuer\n");
374 json_decref (data_json);
375 GNUNET_SCHEDULER_add_now (&do_error, handle);
376 return;
377 }
378 identity_json = json_object_get (data_json, "identity");
379 if (NULL == identity_json)
380 {
381 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
382 "Unable to parse identity\n");
383 json_decref (data_json);
384 GNUNET_SCHEDULER_add_now (&do_error, handle);
385 return;
386 }
387 idtoken_json = json_object_get (data_json, "id_token");
388 if (NULL == idtoken_json)
389 {
390 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
391 "Unable to parse id_token\n");
392 json_decref (data_json);
393 GNUNET_SCHEDULER_add_now (&do_error, handle);
394 return;
395 }
396 pp_json = json_object_get (data_json, "public_params");
397 if (NULL == pp_json)
398 {
399 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
400 "Unable to parse public parameters\n");
401 json_decref (data_json);
402 GNUNET_SCHEDULER_add_now (&do_error, handle);
403 return;
404 }
405
406 PABC_ASSERT (pabc_new_ctx (&ctx));
407 char *pp_str = json_dumps (pp_json, JSON_ENCODE_ANY);
408 status = pabc_decode_and_new_public_parameters (ctx,
409 &pp,
410 pp_str);
411 char *ppid;
412 GNUNET_assert (PABC_OK == pabc_cred_get_ppid_from_pp (pp_str, &ppid));
413 GNUNET_free (pp_str);
414 if (status != PABC_OK)
415 {
416 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
417 "Failed to read public parameters: %s\n",
418 pp_str);
419 json_decref (data_json);
420 GNUNET_SCHEDULER_add_now (&do_error, handle);
421 return;
422 }
423 // (Over)write parameters
424 status = PABC_write_public_parameters (json_string_value (iss_json),
425 pp);
426 if (status != PABC_OK)
427 {
428 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
429 "Failed to write public parameters.\n");
430 json_decref (data_json);
431 GNUNET_SCHEDULER_add_now (&do_error, handle);
432 return;
433 }
434 status = PABC_read_usr_ctx (json_string_value (identity_json),
435 json_string_value (iss_json),
436 ctx, pp, &usr_ctx);
437 if (PABC_OK != status)
438 {
439 if (GNUNET_OK != setup_new_user_context (ctx, pp, &usr_ctx))
440 {
441 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to setup user context.\n");
442 pabc_free_public_parameters (ctx, &pp);
443 json_decref (data_json);
444 GNUNET_SCHEDULER_add_now (&do_error, handle);
445 return;
446 }
447 PABC_write_usr_ctx (json_string_value (identity_json),
448 json_string_value (iss_json),
449 ctx, pp, usr_ctx);
450 }
451
452 // Set attributes from JWT to context
453 status = set_attributes_from_idtoken (ctx,
454 pp,
455 usr_ctx,
456 json_string_value (idtoken_json));
457 if (status != PABC_OK)
458 {
459 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to set attributes.\n");
460 pabc_free_user_context (ctx, pp, &usr_ctx);
461 pabc_free_public_parameters (ctx, &pp);
462 json_decref (data_json);
463 GNUNET_SCHEDULER_add_now (&do_error, handle);
464 return;
465 }
466
467
468 // nonce
469 status = pabc_new_nonce (ctx, &nonce);
470 if (status != PABC_OK)
471 {
472 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to allocate nonce.\n");
473 pabc_free_user_context (ctx, pp, &usr_ctx);
474 pabc_free_public_parameters (ctx, &pp);
475 json_decref (data_json);
476 GNUNET_SCHEDULER_add_now (&do_error, handle);
477 return;
478 }
479 char *nonce_str = json_dumps (nonce_json, JSON_ENCODE_ANY);
480 status = pabc_decode_nonce (ctx, nonce, nonce_str);
481 if (status != PABC_OK)
482 {
483 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to decode nonce.\n");
484 pabc_free_nonce (ctx, &nonce);
485 pabc_free_user_context (ctx, pp, &usr_ctx);
486 pabc_free_public_parameters (ctx, &pp);
487 json_decref (data_json);
488 GNUNET_SCHEDULER_add_now (&do_error, handle);
489 return;
490 }
491
492 // cr
493 status = pabc_new_credential_request (ctx, pp, &cr);
494 if (PABC_OK != status)
495 {
496 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to allocate cr.\n");
497 pabc_free_nonce (ctx, &nonce);
498 pabc_free_user_context (ctx, pp, &usr_ctx);
499 pabc_free_public_parameters (ctx, &pp);
500 json_decref (data_json);
501 GNUNET_SCHEDULER_add_now (&do_error, handle);
502 return;
503 }
504
505 status = pabc_gen_credential_request (ctx, pp, usr_ctx, nonce, cr);
506 if (PABC_OK != status)
507 {
508 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to generate cr.\n");
509 pabc_free_nonce (ctx, &nonce);
510 pabc_free_credential_request (ctx, pp, &cr);
511 pabc_free_user_context (ctx, pp, &usr_ctx);
512 pabc_free_public_parameters (ctx, &pp);
513 json_decref (data_json);
514 GNUNET_SCHEDULER_add_now (&do_error, handle);
515 return;
516 }
517 handle->resp_object = json_object ();
518 GNUNET_assert (PABC_OK == pabc_cred_encode_cr (ctx, pp, cr,
519 json_string_value (
520 identity_json),
521 ppid, &response_str));
522 if (PABC_OK != status)
523 {
524 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to serialize cr.\n");
525 pabc_free_nonce (ctx, &nonce);
526 pabc_free_credential_request (ctx, pp, &cr);
527 pabc_free_user_context (ctx, pp, &usr_ctx);
528 pabc_free_public_parameters (ctx, &pp);
529 json_decref (data_json);
530 GNUNET_SCHEDULER_add_now (&do_error, handle);
531 return;
532 }
533 json_decref (handle->resp_object);
534 handle->resp_object = json_loads (response_str, JSON_DECODE_ANY, &err);
535 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s\n", response_str);
536 GNUNET_free (response_str);
537
538 // clean up
539 pabc_free_nonce (ctx, &nonce);
540 pabc_free_credential_request (ctx, pp, &cr);
541 pabc_free_user_context (ctx, pp, &usr_ctx);
542 pabc_free_public_parameters (ctx, &pp);
543 GNUNET_SCHEDULER_add_now (&return_response, handle);
544 json_decref (data_json);
545}
546
547
548/**
549 * Respond to OPTIONS request
550 *
551 * @param con_handle the connection handle
552 * @param url the url
553 * @param cls the RequestHandle
554 */
555static void
556options_cont (struct GNUNET_REST_RequestHandle *con_handle,
557 const char *url,
558 void *cls)
559{
560 struct MHD_Response *resp;
561 struct RequestHandle *handle = cls;
562
563 // For now, independent of path return all options
564 resp = GNUNET_REST_create_response (NULL);
565 MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
566 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
567 cleanup_handle (handle);
568 return;
569}
570
571
572static enum GNUNET_GenericReturnValue
573rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
574 GNUNET_REST_ResultProcessor proc,
575 void *proc_cls)
576{
577 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
578 struct GNUNET_REST_RequestHandlerError err;
579 static const struct GNUNET_REST_RequestHandler handlers[] = {
580 {MHD_HTTP_METHOD_POST,
581 GNUNET_REST_API_NS_PABC_CR, &cr_cont },
582 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_PABC, &options_cont },
583 GNUNET_REST_HANDLER_END
584 };
585
586 handle->response_code = 0;
587 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
588 handle->proc_cls = proc_cls;
589 handle->proc = proc;
590 handle->rest_handle = rest_handle;
591
592 handle->url = GNUNET_strdup (rest_handle->url);
593 if (handle->url[strlen (handle->url) - 1] == '/')
594 handle->url[strlen (handle->url) - 1] = '\0';
595 handle->timeout_task =
596 GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
597 GNUNET_CONTAINER_DLL_insert (requests_head,
598 requests_tail,
599 handle);
600 if (GNUNET_NO ==
601 GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
602 {
603 cleanup_handle (handle);
604 return GNUNET_NO;
605 }
606
607 return GNUNET_YES;
608}
609
610
611/**
612 * Entry point for the plugin.
613 *
614 * @param cls Config info
615 * @return NULL on error, otherwise the plugin context
616 */
617void *
618libgnunet_plugin_rest_pabc_init (void *cls)
619{
620 static struct Plugin plugin;
621 struct GNUNET_REST_Plugin *api;
622
623 cfg = cls;
624 if (NULL != plugin.cfg)
625 return NULL; /* can only initialize once! */
626 memset (&plugin, 0, sizeof(struct Plugin));
627 plugin.cfg = cfg;
628 api = GNUNET_new (struct GNUNET_REST_Plugin);
629 api->cls = &plugin;
630 api->name = GNUNET_REST_API_NS_PABC;
631 api->process_request = &rest_identity_process_request;
632 GNUNET_asprintf (&allow_methods,
633 "%s, %s",
634 MHD_HTTP_METHOD_POST,
635 MHD_HTTP_METHOD_OPTIONS);
636 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
637 _ ("Identity Provider REST API initialized\n"));
638 return api;
639}
640
641
642/**
643 * Exit point from the plugin.
644 *
645 * @param cls the plugin context (as returned by "init")
646 * @return always NULL
647 */
648void *
649libgnunet_plugin_rest_reclaim_done (void *cls)
650{
651 struct GNUNET_REST_Plugin *api = cls;
652 struct Plugin *plugin = api->cls;
653 struct RequestHandle *request;
654
655 plugin->cfg = NULL;
656 while (NULL != (request = requests_head))
657 do_error (request);
658
659 GNUNET_free (allow_methods);
660 GNUNET_free (api);
661 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
662 "PABC REST plugin is finished\n");
663 return NULL;
664}
665
666
667/* end of plugin_rest_reclaim.c */
diff --git a/src/service/rest/reclaim_plugin.c b/src/service/rest/reclaim_plugin.c
new file mode 100644
index 000000000..677a6a676
--- /dev/null
+++ b/src/service/rest/reclaim_plugin.c
@@ -0,0 +1,1565 @@
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 reclaim/plugin_rest_reclaim.c
24 * @brief GNUnet reclaim REST plugin
25 *
26 */
27#include "platform.h"
28#include "microhttpd.h"
29#include <inttypes.h>
30#include <jansson.h>
31#include "gnunet_gns_service.h"
32#include "gnunet_gnsrecord_lib.h"
33#include "gnunet_identity_service.h"
34#include "gnunet_reclaim_lib.h"
35#include "gnunet_reclaim_service.h"
36#include "gnunet_rest_lib.h"
37#include "gnunet_rest_plugin.h"
38#include "gnunet_signatures.h"
39#include "json_reclaim.h"
40/**
41 * REST root namespace
42 */
43#define GNUNET_REST_API_NS_RECLAIM "/reclaim"
44
45/**
46 * Attribute namespace
47 */
48#define GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES "/reclaim/attributes"
49
50/**
51 * Credential namespace
52 */
53#define GNUNET_REST_API_NS_RECLAIM_CREDENTIAL "/reclaim/credential"
54
55/**
56 * Ticket namespace
57 */
58#define GNUNET_REST_API_NS_IDENTITY_TICKETS "/reclaim/tickets"
59
60/**
61 * Revoke namespace
62 */
63#define GNUNET_REST_API_NS_IDENTITY_REVOKE "/reclaim/revoke"
64
65/**
66 * Revoke namespace
67 */
68#define GNUNET_REST_API_NS_IDENTITY_CONSUME "/reclaim/consume"
69
70/**
71 * State while collecting all egos
72 */
73#define ID_REST_STATE_INIT 0
74
75/**
76 * Done collecting egos
77 */
78#define ID_REST_STATE_POST_INIT 1
79
80/**
81 * The configuration handle
82 */
83const struct GNUNET_CONFIGURATION_Handle *rcfg;
84
85/**
86 * HTTP methods allows for this plugin
87 */
88static char *allow_methods;
89
90/**
91 * Ego list
92 */
93static struct EgoEntry *ego_head;
94
95/**
96 * Ego list
97 */
98static struct EgoEntry *ego_tail;
99
100/**
101 * The processing state
102 */
103static int state;
104
105/**
106 * Handle to Identity service.
107 */
108static struct GNUNET_IDENTITY_Handle *identity_handle;
109
110/**
111 * Identity Provider
112 */
113static struct GNUNET_RECLAIM_Handle *idp;
114
115/**
116 * @brief struct returned by the initialization function of the plugin
117 */
118struct Plugin
119{
120 const struct GNUNET_CONFIGURATION_Handle *cfg;
121};
122
123/**
124 * The ego list
125 */
126struct EgoEntry
127{
128 /**
129 * DLL
130 */
131 struct EgoEntry *next;
132
133 /**
134 * DLL
135 */
136 struct EgoEntry *prev;
137
138 /**
139 * Ego Identifier
140 */
141 char *identifier;
142
143 /**
144 * Public key string
145 */
146 char *keystring;
147
148 /**
149 * The Ego
150 */
151 struct GNUNET_IDENTITY_Ego *ego;
152};
153
154
155struct RequestHandle
156{
157 /**
158 * DLL
159 */
160 struct RequestHandle *next;
161
162 /**
163 * DLL
164 */
165 struct RequestHandle *prev;
166
167 /**
168 * Selected ego
169 */
170 struct EgoEntry *ego_entry;
171
172 /**
173 * Pointer to ego private key
174 */
175 struct GNUNET_CRYPTO_PrivateKey priv_key;
176
177 /**
178 * Rest connection
179 */
180 struct GNUNET_REST_RequestHandle *rest_handle;
181
182 /**
183 * Attribute claim list
184 */
185 struct GNUNET_RECLAIM_AttributeList *attr_list;
186
187 /**
188 * IDENTITY Operation
189 */
190 struct GNUNET_IDENTITY_Operation *op;
191
192 /**
193 * Idp Operation
194 */
195 struct GNUNET_RECLAIM_Operation *idp_op;
196
197 /**
198 * Attribute iterator
199 */
200 struct GNUNET_RECLAIM_AttributeIterator *attr_it;
201
202 /**
203 * Attribute iterator
204 */
205 struct GNUNET_RECLAIM_CredentialIterator *cred_it;
206
207 /**
208 * Ticket iterator
209 */
210 struct GNUNET_RECLAIM_TicketIterator *ticket_it;
211
212 /**
213 * A ticket
214 */
215 struct GNUNET_RECLAIM_Ticket ticket;
216
217 /**
218 * Desired timeout for the lookup (default is no timeout).
219 */
220 struct GNUNET_TIME_Relative timeout;
221
222 /**
223 * ID of a task associated with the resolution process.
224 */
225 struct GNUNET_SCHEDULER_Task *timeout_task;
226
227 /**
228 * The plugin result processor
229 */
230 GNUNET_REST_ResultProcessor proc;
231
232 /**
233 * The closure of the result processor
234 */
235 void *proc_cls;
236
237 /**
238 * The url
239 */
240 char *url;
241
242 /**
243 * Error response message
244 */
245 char *emsg;
246
247 /**
248 * Response code
249 */
250 int response_code;
251
252 /**
253 * Response object
254 */
255 json_t *resp_object;
256};
257
258/**
259 * DLL
260 */
261static struct RequestHandle *requests_head;
262
263/**
264 * DLL
265 */
266static struct RequestHandle *requests_tail;
267
268
269/**
270 * Cleanup lookup handle
271 * @param handle Handle to clean up
272 */
273static void
274cleanup_handle (void *cls)
275{
276 struct RequestHandle *handle = cls;
277
278 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
279 if (NULL != handle->resp_object)
280 json_decref (handle->resp_object);
281 if (NULL != handle->timeout_task)
282 GNUNET_SCHEDULER_cancel (handle->timeout_task);
283 if (NULL != handle->attr_it)
284 GNUNET_RECLAIM_get_attributes_stop (handle->attr_it);
285 if (NULL != handle->cred_it)
286 GNUNET_RECLAIM_get_credentials_stop (handle->cred_it);
287 if (NULL != handle->ticket_it)
288 GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it);
289 if (NULL != handle->url)
290 GNUNET_free (handle->url);
291 if (NULL != handle->emsg)
292 GNUNET_free (handle->emsg);
293 if (NULL != handle->attr_list)
294 GNUNET_RECLAIM_attribute_list_destroy (handle->attr_list);
295 GNUNET_CONTAINER_DLL_remove (requests_head,
296 requests_tail,
297 handle);
298 GNUNET_free (handle);
299}
300
301
302/**
303 * Task run on error, sends error message. Cleans up everything.
304 *
305 * @param cls the `struct RequestHandle`
306 */
307static void
308do_error (void *cls)
309{
310 struct RequestHandle *handle = cls;
311 struct MHD_Response *resp;
312 char *json_error;
313
314 GNUNET_asprintf (&json_error, "{ \"error\" : \"%s\" }", handle->emsg);
315 if (0 == handle->response_code)
316 {
317 handle->response_code = MHD_HTTP_BAD_REQUEST;
318 }
319 resp = GNUNET_REST_create_response (json_error);
320 GNUNET_assert (MHD_NO != MHD_add_response_header (resp, "Content-Type",
321 "application/json"));
322 handle->proc (handle->proc_cls, resp, handle->response_code);
323 cleanup_handle (handle);
324 GNUNET_free (json_error);
325}
326
327
328/**
329 * Task run on timeout, sends error message. Cleans up everything.
330 *
331 * @param cls the `struct RequestHandle`
332 */
333static void
334do_timeout (void *cls)
335{
336 struct RequestHandle *handle = cls;
337
338 handle->timeout_task = NULL;
339 do_error (handle);
340}
341
342
343static void
344collect_error_cb (void *cls)
345{
346 GNUNET_SCHEDULER_add_now (&do_error, cls);
347}
348
349
350static void
351finished_cont (void *cls, int32_t success, const char *emsg)
352{
353 struct RequestHandle *handle = cls;
354 struct MHD_Response *resp;
355
356 handle->idp_op = NULL;
357 if (GNUNET_OK != success)
358 {
359 GNUNET_SCHEDULER_add_now (&do_error, handle);
360 return;
361 }
362 resp = GNUNET_REST_create_response (emsg);
363 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
364 "Content-Type",
365 "application/json"));
366 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
367 "Access-Control-Allow-Methods",
368 allow_methods));
369 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
370 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
371}
372
373
374static void
375delete_finished_cb (void *cls, int32_t success, const char *emsg)
376{
377 struct RequestHandle *handle = cls;
378 struct MHD_Response *resp;
379
380 if (GNUNET_OK != success)
381 {
382 GNUNET_SCHEDULER_add_now (&do_error, handle);
383 return;
384 }
385 resp = GNUNET_REST_create_response (emsg);
386 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
387 "Access-Control-Allow-Methods",
388 allow_methods));
389 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
390 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
391}
392
393
394/**
395 * Return attributes for identity
396 *
397 * @param cls the request handle
398 */
399static void
400return_response (void *cls)
401{
402 char *result_str;
403 struct RequestHandle *handle = cls;
404 struct MHD_Response *resp;
405
406 result_str = json_dumps (handle->resp_object, 0);
407 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
408 resp = GNUNET_REST_create_response (result_str);
409 GNUNET_assert (MHD_NO !=
410 MHD_add_response_header (resp,
411 "Access-Control-Allow-Methods",
412 allow_methods));
413 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
414 GNUNET_free (result_str);
415 cleanup_handle (handle);
416}
417
418
419static void
420collect_finished_cb (void *cls)
421{
422 struct RequestHandle *handle = cls;
423
424 // Done
425 handle->attr_it = NULL;
426 handle->cred_it = NULL;
427 handle->ticket_it = NULL;
428 GNUNET_SCHEDULER_add_now (&return_response, handle);
429}
430
431
432/**
433 * Collect all attributes for an ego
434 *
435 */
436static void
437ticket_collect (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket)
438{
439 json_t *json_resource;
440 struct RequestHandle *handle = cls;
441 json_t *value;
442 char *tmp;
443
444 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding ticket\n");
445 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd, sizeof(ticket->rnd));
446 json_resource = json_object ();
447 GNUNET_free (tmp);
448 json_array_append (handle->resp_object, json_resource);
449
450 tmp =
451 GNUNET_STRINGS_data_to_string_alloc (&ticket->identity,
452 sizeof(struct
453 GNUNET_CRYPTO_PublicKey));
454 value = json_string (tmp);
455 json_object_set_new (json_resource, "issuer", value);
456 GNUNET_free (tmp);
457 tmp =
458 GNUNET_STRINGS_data_to_string_alloc (&ticket->audience,
459 sizeof(struct
460 GNUNET_CRYPTO_PublicKey));
461 value = json_string (tmp);
462 json_object_set_new (json_resource, "audience", value);
463 GNUNET_free (tmp);
464 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd, sizeof(ticket->rnd));
465 value = json_string (tmp);
466 json_object_set_new (json_resource, "rnd", value);
467 GNUNET_free (tmp);
468 GNUNET_RECLAIM_ticket_iteration_next (handle->ticket_it);
469}
470
471
472static void
473add_credential_cont (struct GNUNET_REST_RequestHandle *con_handle,
474 const char *url,
475 void *cls)
476{
477 struct RequestHandle *handle = cls;
478 const struct GNUNET_CRYPTO_PrivateKey *identity_priv;
479 const char *identity;
480 struct EgoEntry *ego_entry;
481 struct GNUNET_RECLAIM_Credential *attribute;
482 struct GNUNET_TIME_Relative exp;
483 char term_data[handle->rest_handle->data_size + 1];
484 json_t *data_json;
485 json_error_t err;
486 struct GNUNET_JSON_Specification attrspec[] =
487 { GNUNET_RECLAIM_JSON_spec_credential (&attribute),
488 GNUNET_JSON_spec_end () };
489
490 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
491 "Adding an credential for %s.\n",
492 handle->url);
493 if (strlen (GNUNET_REST_API_NS_RECLAIM_CREDENTIAL) >= strlen (
494 handle->url))
495 {
496 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
497 GNUNET_SCHEDULER_add_now (&do_error, handle);
498 return;
499 }
500 identity = handle->url + strlen (
501 GNUNET_REST_API_NS_RECLAIM_CREDENTIAL) + 1;
502
503 for (ego_entry = ego_head; NULL != ego_entry;
504 ego_entry = ego_entry->next)
505 if (0 == strcmp (identity, ego_entry->identifier))
506 break;
507
508 if (NULL == ego_entry)
509 {
510 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Identity unknown (%s)\n", identity);
511 return;
512 }
513 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
514
515 if (0 >= handle->rest_handle->data_size)
516 {
517 GNUNET_SCHEDULER_add_now (&do_error, handle);
518 return;
519 }
520
521 term_data[handle->rest_handle->data_size] = '\0';
522 GNUNET_memcpy (term_data,
523 handle->rest_handle->data,
524 handle->rest_handle->data_size);
525 data_json = json_loads (term_data, JSON_DECODE_ANY, &err);
526 if (GNUNET_OK != GNUNET_JSON_parse (data_json, attrspec, NULL, NULL))
527 {
528 json_decref (data_json);
529 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
530 "Unable to parse JSON from %s\n",
531 term_data);
532 GNUNET_SCHEDULER_add_now (&do_error, handle);
533 return;
534 }
535 json_decref (data_json);
536 if (NULL == attribute)
537 {
538 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
539 "Unable to parse credential from %s\n",
540 term_data);
541 GNUNET_SCHEDULER_add_now (&do_error, handle);
542 return;
543 }
544 /**
545 * New ID for attribute
546 */
547 if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&attribute->id))
548 GNUNET_RECLAIM_id_generate (&attribute->id);
549 exp = GNUNET_TIME_UNIT_HOURS;
550 handle->idp_op = GNUNET_RECLAIM_credential_store (idp,
551 identity_priv,
552 attribute,
553 &exp,
554 &finished_cont,
555 handle);
556 GNUNET_JSON_parse_free (attrspec);
557}
558
559
560/**
561 * Collect all credentials for an ego
562 *
563 */
564static void
565cred_collect (void *cls,
566 const struct GNUNET_CRYPTO_PublicKey *identity,
567 const struct GNUNET_RECLAIM_Credential *cred)
568{
569 struct RequestHandle *handle = cls;
570 struct GNUNET_RECLAIM_AttributeList *attrs;
571 struct GNUNET_RECLAIM_AttributeListEntry *ale;
572 struct GNUNET_TIME_Absolute exp;
573 json_t *attr_obj;
574 json_t *cred_obj;
575 const char *type;
576 char *tmp_value;
577 char *id_str;
578 char *issuer;
579
580
581 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding credential: %s\n",
582 cred->name);
583 attrs = GNUNET_RECLAIM_credential_get_attributes (cred);
584 issuer = GNUNET_RECLAIM_credential_get_issuer (cred);
585 tmp_value = GNUNET_RECLAIM_credential_value_to_string (cred->type,
586 cred->data,
587 cred->data_size);
588 cred_obj = json_object ();
589 json_object_set_new (cred_obj, "value", json_string (tmp_value));
590 json_object_set_new (cred_obj, "name", json_string (cred->name));
591 type = GNUNET_RECLAIM_credential_number_to_typename (cred->type);
592 json_object_set_new (cred_obj, "type", json_string (type));
593 if (NULL != issuer)
594 {
595 json_object_set_new (cred_obj, "issuer", json_string (issuer));
596 GNUNET_free (issuer);
597 }
598 if (GNUNET_OK == GNUNET_RECLAIM_credential_get_expiration (cred,
599 &exp))
600 {
601 json_object_set_new (cred_obj, "expiration", json_integer (
602 exp.abs_value_us));
603 }
604 id_str = GNUNET_STRINGS_data_to_string_alloc (&cred->id,
605 sizeof(cred->id));
606 json_object_set_new (cred_obj, "id", json_string (id_str));
607 GNUNET_free (tmp_value);
608 GNUNET_free (id_str);
609 if (NULL != attrs)
610 {
611 json_t *attr_arr = json_array ();
612 for (ale = attrs->list_head; NULL != ale; ale = ale->next)
613 {
614 tmp_value =
615 GNUNET_RECLAIM_attribute_value_to_string (ale->attribute->type,
616 ale->attribute->data,
617 ale->attribute->data_size);
618 attr_obj = json_object ();
619 json_object_set_new (attr_obj, "value", json_string (tmp_value));
620 json_object_set_new (attr_obj, "name", json_string (
621 ale->attribute->name));
622
623 json_object_set_new (attr_obj, "flag", json_string ("1")); // FIXME
624 type = GNUNET_RECLAIM_attribute_number_to_typename (ale->attribute->type);
625 json_object_set_new (attr_obj, "type", json_string (type));
626 json_object_set_new (attr_obj, "id", json_string (""));
627 json_object_set_new (attr_obj, "credential", json_string (""));
628 json_array_append_new (attr_arr, attr_obj);
629 GNUNET_free (tmp_value);
630 }
631 json_object_set_new (cred_obj, "attributes", attr_arr);
632 }
633 json_array_append_new (handle->resp_object, cred_obj);
634 if (NULL != attrs)
635 GNUNET_RECLAIM_attribute_list_destroy (attrs);
636 GNUNET_RECLAIM_get_credentials_next (handle->cred_it);
637}
638
639
640/**
641 * Lists credential for identity request
642 *
643 * @param con_handle the connection handle
644 * @param url the url
645 * @param cls the RequestHandle
646 */
647static void
648list_credential_cont (struct GNUNET_REST_RequestHandle *con_handle,
649 const char *url,
650 void *cls)
651{
652 struct RequestHandle *handle = cls;
653 const struct GNUNET_CRYPTO_PrivateKey *priv_key;
654 struct EgoEntry *ego_entry;
655 char *identity;
656
657 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
658 "Getting credentials for %s.\n",
659 handle->url);
660 if (strlen (GNUNET_REST_API_NS_RECLAIM_CREDENTIAL) >= strlen (
661 handle->url))
662 {
663 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
664 GNUNET_SCHEDULER_add_now (&do_error, handle);
665 return;
666 }
667 identity = handle->url + strlen (
668 GNUNET_REST_API_NS_RECLAIM_CREDENTIAL) + 1;
669
670 for (ego_entry = ego_head; NULL != ego_entry;
671 ego_entry = ego_entry->next)
672 if (0 == strcmp (identity, ego_entry->identifier))
673 break;
674 handle->resp_object = json_array ();
675
676
677 if (NULL == ego_entry)
678 {
679 // Done
680 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n", identity);
681 GNUNET_SCHEDULER_add_now (&return_response, handle);
682 return;
683 }
684 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
685 handle->cred_it = GNUNET_RECLAIM_get_credentials_start (idp,
686 priv_key,
687 &collect_error_cb,
688 handle,
689 &cred_collect,
690 handle,
691 &
692 collect_finished_cb,
693 handle);
694}
695
696
697/**
698 * Deletes credential from an identity
699 *
700 * @param con_handle the connection handle
701 * @param url the url
702 * @param cls the RequestHandle
703 */
704static void
705delete_credential_cont (struct GNUNET_REST_RequestHandle *con_handle,
706 const char *url,
707 void *cls)
708{
709 struct RequestHandle *handle = cls;
710 const struct GNUNET_CRYPTO_PrivateKey *priv_key;
711 struct GNUNET_RECLAIM_Credential attr;
712 struct EgoEntry *ego_entry;
713 char *identity_id_str;
714 char *identity;
715 char *id;
716
717 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleting credential.\n");
718 if (strlen (GNUNET_REST_API_NS_RECLAIM_CREDENTIAL) >= strlen (
719 handle->url))
720 {
721 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
722 GNUNET_SCHEDULER_add_now (&do_error, handle);
723 return;
724 }
725 identity_id_str =
726 strdup (handle->url + strlen (
727 GNUNET_REST_API_NS_RECLAIM_CREDENTIAL) + 1);
728 identity = strtok (identity_id_str, "/");
729 id = strtok (NULL, "/");
730 if ((NULL == identity) || (NULL == id))
731 {
732 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed request.\n");
733 GNUNET_free (identity_id_str);
734 GNUNET_SCHEDULER_add_now (&do_error, handle);
735 return;
736 }
737
738 for (ego_entry = ego_head; NULL != ego_entry;
739 ego_entry = ego_entry->next)
740 if (0 == strcmp (identity, ego_entry->identifier))
741 break;
742 handle->resp_object = json_array ();
743 if (NULL == ego_entry)
744 {
745 // Done
746 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n", identity);
747 GNUNET_free (identity_id_str);
748 GNUNET_SCHEDULER_add_now (&return_response, handle);
749 return;
750 }
751 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
752 memset (&attr, 0, sizeof(struct GNUNET_RECLAIM_Credential));
753 GNUNET_STRINGS_string_to_data (id, strlen (id), &attr.id, sizeof(attr.id));
754 attr.name = "";
755 handle->idp_op = GNUNET_RECLAIM_credential_delete (idp,
756 priv_key,
757 &attr,
758 &delete_finished_cb,
759 handle);
760 GNUNET_free (identity_id_str);
761}
762
763
764/**
765 * List tickets for identity request
766 *
767 * @param con_handle the connection handle
768 * @param url the url
769 * @param cls the RequestHandle
770 */
771static void
772list_tickets_cont (struct GNUNET_REST_RequestHandle *con_handle,
773 const char *url,
774 void *cls)
775{
776 const struct GNUNET_CRYPTO_PrivateKey *priv_key;
777 struct RequestHandle *handle = cls;
778 struct EgoEntry *ego_entry;
779 char *identity;
780
781 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
782 "Getting tickets for %s.\n",
783 handle->url);
784 if (strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) >= strlen (handle->url))
785 {
786 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
787 GNUNET_SCHEDULER_add_now (&do_error, handle);
788 return;
789 }
790 identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) + 1;
791
792 for (ego_entry = ego_head; NULL != ego_entry;
793 ego_entry = ego_entry->next)
794 if (0 == strcmp (identity, ego_entry->identifier))
795 break;
796 handle->resp_object = json_array ();
797
798 if (NULL == ego_entry)
799 {
800 // Done
801 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n", identity);
802 GNUNET_SCHEDULER_add_now (&return_response, handle);
803 return;
804 }
805 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
806 handle->ticket_it =
807 GNUNET_RECLAIM_ticket_iteration_start (idp,
808 priv_key,
809 &collect_error_cb,
810 handle,
811 &ticket_collect,
812 handle,
813 &collect_finished_cb,
814 handle);
815}
816
817
818static void
819add_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
820 const char *url,
821 void *cls)
822{
823 const struct GNUNET_CRYPTO_PrivateKey *identity_priv;
824 const char *identity;
825 struct RequestHandle *handle = cls;
826 struct EgoEntry *ego_entry;
827 struct GNUNET_RECLAIM_Attribute *attribute;
828 struct GNUNET_TIME_Relative exp;
829 char term_data[handle->rest_handle->data_size + 1];
830 json_t *data_json;
831 json_error_t err;
832 struct GNUNET_JSON_Specification attrspec[] =
833 { GNUNET_RECLAIM_JSON_spec_attribute (&attribute), GNUNET_JSON_spec_end () };
834
835 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
836 "Adding an attribute for %s.\n",
837 handle->url);
838 if (strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) >= strlen (handle->url))
839 {
840 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
841 GNUNET_SCHEDULER_add_now (&do_error, handle);
842 return;
843 }
844 identity = handle->url + strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) + 1;
845
846 for (ego_entry = ego_head; NULL != ego_entry;
847 ego_entry = ego_entry->next)
848 if (0 == strcmp (identity, ego_entry->identifier))
849 break;
850
851 if (NULL == ego_entry)
852 {
853 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Identity unknown (%s)\n", identity);
854 return;
855 }
856 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
857
858 if (0 >= handle->rest_handle->data_size)
859 {
860 GNUNET_SCHEDULER_add_now (&do_error, handle);
861 return;
862 }
863
864 term_data[handle->rest_handle->data_size] = '\0';
865 GNUNET_memcpy (term_data,
866 handle->rest_handle->data,
867 handle->rest_handle->data_size);
868 data_json = json_loads (term_data, JSON_DECODE_ANY, &err);
869 GNUNET_assert (GNUNET_OK ==
870 GNUNET_JSON_parse (data_json, attrspec, NULL, NULL));
871 json_decref (data_json);
872 if (NULL == attribute)
873 {
874 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
875 "Unable to parse attribute from %s\n",
876 term_data);
877 GNUNET_SCHEDULER_add_now (&do_error, handle);
878 return;
879 }
880 /**
881 * New ID for attribute
882 */
883 if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&attribute->id))
884 GNUNET_RECLAIM_id_generate (&attribute->id);
885 exp = GNUNET_TIME_UNIT_HOURS;
886 handle->idp_op = GNUNET_RECLAIM_attribute_store (idp,
887 identity_priv,
888 attribute,
889 &exp,
890 &finished_cont,
891 handle);
892 GNUNET_JSON_parse_free (attrspec);
893}
894
895
896/**
897 * Parse a JWT and return the respective claim value as Attribute
898 *
899 * @param cred the jwt credential
900 * @param claim the name of the claim in the JWT
901 *
902 * @return a GNUNET_RECLAIM_Attribute, containing the new value
903 */
904struct GNUNET_RECLAIM_Attribute *
905parse_jwt (const struct GNUNET_RECLAIM_Credential *cred,
906 const char *claim)
907{
908 char *jwt_string;
909 struct GNUNET_RECLAIM_Attribute *attr;
910 char delim[] = ".";
911 const char *type_str = NULL;
912 const char *val_str = NULL;
913 char *data;
914 size_t data_size;
915 uint32_t type;
916 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Parsing JWT attributes.\n");
917 char *decoded_jwt;
918 json_t *json_val;
919 json_error_t *json_err = NULL;
920
921 jwt_string = GNUNET_RECLAIM_credential_value_to_string (cred->type,
922 cred->data,
923 cred->data_size);
924 char *jwt_body = strtok (jwt_string, delim);
925 jwt_body = strtok (NULL, delim);
926 GNUNET_STRINGS_base64_decode (jwt_body, strlen (jwt_body),
927 (void **) &decoded_jwt);
928 json_val = json_loads (decoded_jwt, JSON_DECODE_ANY, json_err);
929 const char *key;
930 json_t *value;
931 json_object_foreach (json_val, key, value) {
932 if (0 == strcasecmp (key,claim))
933 {
934 val_str = json_dumps (value, JSON_ENCODE_ANY);
935 }
936 }
937 type_str = "String";
938 type = GNUNET_RECLAIM_attribute_typename_to_number (type_str);
939 if (GNUNET_SYSERR == GNUNET_RECLAIM_attribute_string_to_value (type,val_str,
940 (void **) &data,
941 &data_size))
942 {
943 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
944 "Attribute value from JWT Parser invalid!\n");
945 GNUNET_RECLAIM_attribute_string_to_value (type,
946 "Error: Referenced Claim Name not Found",
947 (void **) &data,
948 &data_size);
949 attr = GNUNET_RECLAIM_attribute_new (claim, &cred->id,
950 type, data, data_size);
951 attr->id = cred->id;
952 attr->flag = 1;
953 }
954 else
955 {
956 attr = GNUNET_RECLAIM_attribute_new (claim, &cred->id,
957 type, data, data_size);
958 attr->id = cred->id;
959 attr->flag = 1;
960 }
961 return attr;
962}
963
964
965/**
966 * Collect all attributes for an ego
967 *
968 */
969static void
970attr_collect (void *cls,
971 const struct GNUNET_CRYPTO_PublicKey *identity,
972 const struct GNUNET_RECLAIM_Attribute *attr)
973{
974 struct RequestHandle *handle = cls;
975 json_t *attr_obj;
976 const char *type;
977 char *id_str;
978
979 char *tmp_value;
980 tmp_value = GNUNET_RECLAIM_attribute_value_to_string (attr->type,
981 attr->data,
982 attr->data_size);
983 attr_obj = json_object ();
984 json_object_set_new (attr_obj, "value", json_string (tmp_value));
985 json_object_set_new (attr_obj, "name", json_string (attr->name));
986
987 if (GNUNET_RECLAIM_id_is_zero (&attr->credential))
988 json_object_set_new (attr_obj, "flag", json_string ("0"));
989 else
990 json_object_set_new (attr_obj, "flag", json_string ("1"));
991 type = GNUNET_RECLAIM_attribute_number_to_typename (attr->type);
992 json_object_set_new (attr_obj, "type", json_string (type));
993 id_str = GNUNET_STRINGS_data_to_string_alloc (&attr->id,
994 sizeof(attr->id));
995 json_object_set_new (attr_obj, "id", json_string (id_str));
996 GNUNET_free (id_str);
997 id_str = GNUNET_STRINGS_data_to_string_alloc (&attr->credential,
998 sizeof(attr->credential));
999 json_object_set_new (attr_obj, "credential", json_string (id_str));
1000 GNUNET_free (id_str);
1001 json_array_append (handle->resp_object, attr_obj);
1002 json_decref (attr_obj);
1003 GNUNET_free (tmp_value);
1004 GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1005}
1006
1007
1008/**
1009 * List attributes for identity request
1010 *
1011 * @param con_handle the connection handle
1012 * @param url the url
1013 * @param cls the RequestHandle
1014 */
1015static void
1016list_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
1017 const char *url,
1018 void *cls)
1019{
1020 const struct GNUNET_CRYPTO_PrivateKey *priv_key;
1021 struct RequestHandle *handle = cls;
1022 struct EgoEntry *ego_entry;
1023 char *identity;
1024
1025 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1026 "Getting attributes for %s.\n",
1027 handle->url);
1028 if (strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) >= strlen (handle->url))
1029 {
1030 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
1031 GNUNET_SCHEDULER_add_now (&do_error, handle);
1032 return;
1033 }
1034 identity = handle->url + strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) + 1;
1035
1036 for (ego_entry = ego_head; NULL != ego_entry;
1037 ego_entry = ego_entry->next)
1038 if (0 == strcmp (identity, ego_entry->identifier))
1039 break;
1040 handle->resp_object = json_array ();
1041
1042
1043 if (NULL == ego_entry)
1044 {
1045 // Done
1046 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n", identity);
1047 GNUNET_SCHEDULER_add_now (&return_response, handle);
1048 return;
1049 }
1050 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1051 handle->attr_it = GNUNET_RECLAIM_get_attributes_start (idp,
1052 priv_key,
1053 &collect_error_cb,
1054 handle,
1055 &attr_collect,
1056 handle,
1057 &collect_finished_cb,
1058 handle);
1059}
1060
1061
1062/**
1063 * List attributes for identity request
1064 *
1065 * @param con_handle the connection handle
1066 * @param url the url
1067 * @param cls the RequestHandle
1068 */
1069static void
1070delete_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
1071 const char *url,
1072 void *cls)
1073{
1074 const struct GNUNET_CRYPTO_PrivateKey *priv_key;
1075 struct RequestHandle *handle = cls;
1076 struct GNUNET_RECLAIM_Attribute attr;
1077 struct EgoEntry *ego_entry;
1078 char *identity_id_str;
1079 char *identity;
1080 char *id;
1081
1082 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleting attributes.\n");
1083 if (strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) >= strlen (handle->url))
1084 {
1085 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
1086 GNUNET_SCHEDULER_add_now (&do_error, handle);
1087 return;
1088 }
1089 identity_id_str =
1090 strdup (handle->url + strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) + 1);
1091 identity = strtok (identity_id_str, "/");
1092 id = strtok (NULL, "/");
1093 if ((NULL == identity) || (NULL == id))
1094 {
1095 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed request.\n");
1096 GNUNET_free (identity_id_str);
1097 GNUNET_SCHEDULER_add_now (&do_error, handle);
1098 return;
1099 }
1100
1101 for (ego_entry = ego_head; NULL != ego_entry;
1102 ego_entry = ego_entry->next)
1103 if (0 == strcmp (identity, ego_entry->identifier))
1104 break;
1105 handle->resp_object = json_array ();
1106 if (NULL == ego_entry)
1107 {
1108 // Done
1109 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n", identity);
1110 GNUNET_free (identity_id_str);
1111 GNUNET_SCHEDULER_add_now (&return_response, handle);
1112 return;
1113 }
1114 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1115 memset (&attr, 0, sizeof(struct GNUNET_RECLAIM_Attribute));
1116 GNUNET_STRINGS_string_to_data (id, strlen (id), &attr.id, sizeof(attr.id));
1117 attr.name = "";
1118 handle->idp_op = GNUNET_RECLAIM_attribute_delete (idp,
1119 priv_key,
1120 &attr,
1121 &delete_finished_cb,
1122 handle);
1123 GNUNET_free (identity_id_str);
1124}
1125
1126
1127static void
1128revoke_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
1129 const char *url,
1130 void *cls)
1131{
1132 const struct GNUNET_CRYPTO_PrivateKey *identity_priv;
1133 struct RequestHandle *handle = cls;
1134 struct EgoEntry *ego_entry;
1135 struct GNUNET_RECLAIM_Ticket *ticket = NULL;
1136 struct GNUNET_CRYPTO_PublicKey tmp_pk;
1137 char term_data[handle->rest_handle->data_size + 1];
1138 json_t *data_json;
1139 json_error_t err;
1140 struct GNUNET_JSON_Specification tktspec[] =
1141 { GNUNET_RECLAIM_JSON_spec_ticket (&ticket), GNUNET_JSON_spec_end () };
1142
1143 if (0 >= handle->rest_handle->data_size)
1144 {
1145 GNUNET_SCHEDULER_add_now (&do_error, handle);
1146 return;
1147 }
1148
1149 term_data[handle->rest_handle->data_size] = '\0';
1150 GNUNET_memcpy (term_data,
1151 handle->rest_handle->data,
1152 handle->rest_handle->data_size);
1153 data_json = json_loads (term_data, JSON_DECODE_ANY, &err);
1154 if ((NULL == data_json) ||
1155 (GNUNET_OK != GNUNET_JSON_parse (data_json, tktspec, NULL, NULL)))
1156 {
1157 handle->emsg = GNUNET_strdup ("Not a ticket!\n");
1158 GNUNET_SCHEDULER_add_now (&do_error, handle);
1159 GNUNET_JSON_parse_free (tktspec);
1160 if (NULL != data_json)
1161 json_decref (data_json);
1162 return;
1163 }
1164 json_decref (data_json);
1165 if (NULL == ticket)
1166 {
1167 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1168 "Unable to parse ticket from %s\n",
1169 term_data);
1170 GNUNET_SCHEDULER_add_now (&do_error, handle);
1171 return;
1172 }
1173
1174 for (ego_entry = ego_head; NULL != ego_entry;
1175 ego_entry = ego_entry->next)
1176 {
1177 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &tmp_pk);
1178 if (0 == memcmp (&ticket->identity,
1179 &tmp_pk,
1180 sizeof(struct GNUNET_CRYPTO_PublicKey)))
1181 break;
1182 }
1183 if (NULL == ego_entry)
1184 {
1185 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Identity unknown\n");
1186 GNUNET_JSON_parse_free (tktspec);
1187 return;
1188 }
1189 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1190
1191 handle->idp_op = GNUNET_RECLAIM_ticket_revoke (idp,
1192 identity_priv,
1193 ticket,
1194 &finished_cont,
1195 handle);
1196 GNUNET_JSON_parse_free (tktspec);
1197}
1198
1199
1200static void
1201consume_cont (void *cls,
1202 const struct GNUNET_CRYPTO_PublicKey *identity,
1203 const struct GNUNET_RECLAIM_Attribute *attr,
1204 const struct GNUNET_RECLAIM_Presentation *presentation)
1205{
1206 struct RequestHandle *handle = cls;
1207 char *val_str;
1208 json_t *value;
1209
1210 if (NULL == identity)
1211 {
1212 GNUNET_SCHEDULER_add_now (&return_response, handle);
1213 return;
1214 }
1215
1216 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n", attr->name);
1217 val_str = GNUNET_RECLAIM_attribute_value_to_string (attr->type,
1218 attr->data,
1219 attr->data_size);
1220 if (NULL == val_str)
1221 {
1222 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1223 "Failed to parse value for: %s\n",
1224 attr->name);
1225 return;
1226 }
1227 value = json_string (val_str);
1228 json_object_set_new (handle->resp_object, attr->name, value);
1229 json_decref (value);
1230 GNUNET_free (val_str);
1231}
1232
1233
1234static void
1235consume_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
1236 const char *url,
1237 void *cls)
1238{
1239 const struct GNUNET_CRYPTO_PrivateKey *identity_priv;
1240 struct RequestHandle *handle = cls;
1241 struct EgoEntry *ego_entry;
1242 struct GNUNET_RECLAIM_Ticket *ticket;
1243 struct GNUNET_CRYPTO_PublicKey tmp_pk;
1244 char term_data[handle->rest_handle->data_size + 1];
1245 json_t *data_json;
1246 json_error_t err;
1247 struct GNUNET_JSON_Specification tktspec[] =
1248 { GNUNET_RECLAIM_JSON_spec_ticket (&ticket), GNUNET_JSON_spec_end () };
1249
1250 if (0 >= handle->rest_handle->data_size)
1251 {
1252 GNUNET_SCHEDULER_add_now (&do_error, handle);
1253 return;
1254 }
1255
1256 term_data[handle->rest_handle->data_size] = '\0';
1257 GNUNET_memcpy (term_data,
1258 handle->rest_handle->data,
1259 handle->rest_handle->data_size);
1260 data_json = json_loads (term_data, JSON_DECODE_ANY, &err);
1261 if (NULL == data_json)
1262 {
1263 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1264 "Unable to parse JSON Object from %s\n",
1265 term_data);
1266 GNUNET_SCHEDULER_add_now (&do_error, handle);
1267 return;
1268 }
1269 if (GNUNET_OK != GNUNET_JSON_parse (data_json, tktspec, NULL, NULL))
1270 {
1271 handle->emsg = GNUNET_strdup ("Not a ticket!\n");
1272 GNUNET_SCHEDULER_add_now (&do_error, handle);
1273 GNUNET_JSON_parse_free (tktspec);
1274 json_decref (data_json);
1275 return;
1276 }
1277 for (ego_entry = ego_head; NULL != ego_entry;
1278 ego_entry = ego_entry->next)
1279 {
1280 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &tmp_pk);
1281 if (0 == memcmp (&ticket->audience,
1282 &tmp_pk,
1283 sizeof(struct GNUNET_CRYPTO_PublicKey)))
1284 break;
1285 }
1286 if (NULL == ego_entry)
1287 {
1288 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Identity unknown\n");
1289 GNUNET_JSON_parse_free (tktspec);
1290 return;
1291 }
1292 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1293 handle->resp_object = json_object ();
1294 handle->idp_op = GNUNET_RECLAIM_ticket_consume (idp,
1295 identity_priv,
1296 ticket,
1297 &consume_cont,
1298 handle);
1299 GNUNET_JSON_parse_free (tktspec);
1300}
1301
1302
1303/**
1304 * Respond to OPTIONS request
1305 *
1306 * @param con_handle the connection handle
1307 * @param url the url
1308 * @param cls the RequestHandle
1309 */
1310static void
1311options_cont (struct GNUNET_REST_RequestHandle *con_handle,
1312 const char *url,
1313 void *cls)
1314{
1315 struct MHD_Response *resp;
1316 struct RequestHandle *handle = cls;
1317
1318 // For now, independent of path return all options
1319 resp = GNUNET_REST_create_response (NULL);
1320 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
1321 "Access-Control-Allow-Methods",
1322 allow_methods));
1323 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1324 cleanup_handle (handle);
1325 return;
1326}
1327
1328
1329/**
1330 * If listing is enabled, prints information about the egos.
1331 *
1332 * This function is initially called for all egos and then again
1333 * whenever a ego's identifier changes or if it is deleted. At the
1334 * end of the initial pass over all egos, the function is once called
1335 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
1336 * be invoked in the future or that there was an error.
1337 *
1338 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get', this
1339 * function is only called ONCE, and 'NULL' being passed in 'ego' does
1340 * indicate an error (for example because name is taken or no default value is
1341 * known). If 'ego' is non-NULL and if '*ctx' is set in those callbacks, the
1342 * value WILL be passed to a subsequent call to the identity callback of
1343 * 'GNUNET_IDENTITY_connect' (if that one was not NULL).
1344 *
1345 * When an identity is renamed, this function is called with the
1346 * (known) ego but the NEW identifier.
1347 *
1348 * When an identity is deleted, this function is called with the
1349 * (known) ego and "NULL" for the 'identifier'. In this case,
1350 * the 'ego' is henceforth invalid (and the 'ctx' should also be
1351 * cleaned up).
1352 *
1353 * @param cls closure
1354 * @param ego ego handle
1355 * @param ctx context for application to store data for this ego
1356 * (during the lifetime of this process, initially NULL)
1357 * @param identifier identifier assigned by the user for this ego,
1358 * NULL if the user just deleted the ego and it
1359 * must thus no longer be used
1360 */
1361static void
1362list_ego (void *cls,
1363 struct GNUNET_IDENTITY_Ego *ego,
1364 void **ctx,
1365 const char *identifier)
1366{
1367 struct EgoEntry *ego_entry;
1368 struct GNUNET_CRYPTO_PublicKey pk;
1369
1370 if (NULL == ego)
1371 {
1372 state = ID_REST_STATE_POST_INIT;
1373 return;
1374 }
1375 if (ID_REST_STATE_INIT == state)
1376 {
1377 ego_entry = GNUNET_new (struct EgoEntry);
1378 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1379 ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk);
1380 ego_entry->ego = ego;
1381 ego_entry->identifier = GNUNET_strdup (identifier);
1382 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
1383 ego_tail,
1384 ego_entry);
1385 }
1386 /* Ego renamed or added */
1387 if (identifier != NULL)
1388 {
1389 for (ego_entry = ego_head; NULL != ego_entry;
1390 ego_entry = ego_entry->next)
1391 {
1392 if (ego_entry->ego == ego)
1393 {
1394 /* Rename */
1395 GNUNET_free (ego_entry->identifier);
1396 ego_entry->identifier = GNUNET_strdup (identifier);
1397 break;
1398 }
1399 }
1400 if (NULL == ego_entry)
1401 {
1402 /* Add */
1403 ego_entry = GNUNET_new (struct EgoEntry);
1404 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1405 ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk);
1406 ego_entry->ego = ego;
1407 ego_entry->identifier = GNUNET_strdup (identifier);
1408 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
1409 ego_tail,
1410 ego_entry);
1411 }
1412 }
1413 else
1414 {
1415 /* Delete */
1416 for (ego_entry = ego_head; NULL != ego_entry;
1417 ego_entry = ego_entry->next)
1418 {
1419 if (ego_entry->ego == ego)
1420 break;
1421 }
1422 if (NULL == ego_entry)
1423 return; /* Not found */
1424
1425 GNUNET_CONTAINER_DLL_remove (ego_head,
1426 ego_tail,
1427 ego_entry);
1428 GNUNET_free (ego_entry->identifier);
1429 GNUNET_free (ego_entry->keystring);
1430 GNUNET_free (ego_entry);
1431 return;
1432 }
1433
1434}
1435
1436
1437enum GNUNET_GenericReturnValue
1438REST_reclaim_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
1439 GNUNET_REST_ResultProcessor proc,
1440 void *proc_cls)
1441{
1442 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
1443 struct GNUNET_REST_RequestHandlerError err;
1444 static const struct GNUNET_REST_RequestHandler handlers[] =
1445 { { MHD_HTTP_METHOD_GET,
1446 GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES, &list_attribute_cont },
1447 { MHD_HTTP_METHOD_POST,
1448 GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES, &add_attribute_cont },
1449 { MHD_HTTP_METHOD_DELETE,
1450 GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES, &delete_attribute_cont },
1451 { MHD_HTTP_METHOD_GET,
1452 GNUNET_REST_API_NS_RECLAIM_CREDENTIAL, &list_credential_cont },
1453 { MHD_HTTP_METHOD_POST,
1454 GNUNET_REST_API_NS_RECLAIM_CREDENTIAL, &add_credential_cont },
1455 { MHD_HTTP_METHOD_DELETE,
1456 GNUNET_REST_API_NS_RECLAIM_CREDENTIAL, &delete_credential_cont },
1457 { MHD_HTTP_METHOD_GET,
1458 GNUNET_REST_API_NS_IDENTITY_TICKETS, &list_tickets_cont },
1459 { MHD_HTTP_METHOD_POST,
1460 GNUNET_REST_API_NS_IDENTITY_REVOKE, &revoke_ticket_cont },
1461 { MHD_HTTP_METHOD_POST,
1462 GNUNET_REST_API_NS_IDENTITY_CONSUME, &consume_ticket_cont },
1463 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_RECLAIM, &options_cont },
1464 GNUNET_REST_HANDLER_END};
1465
1466 handle->response_code = 0;
1467 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1468 handle->proc_cls = proc_cls;
1469 handle->proc = proc;
1470 handle->rest_handle = rest_handle;
1471
1472 handle->url = GNUNET_strdup (rest_handle->url);
1473 if (handle->url[strlen (handle->url) - 1] == '/')
1474 handle->url[strlen (handle->url) - 1] = '\0';
1475 handle->timeout_task =
1476 GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
1477 GNUNET_CONTAINER_DLL_insert (requests_head,
1478 requests_tail,
1479 handle);
1480 if (GNUNET_NO ==
1481 GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
1482 {
1483 cleanup_handle (handle);
1484 return GNUNET_NO;
1485 }
1486
1487 return GNUNET_YES;
1488}
1489
1490
1491/**
1492 * Entry point for the plugin.
1493 *
1494 * @param cls Config info
1495 * @return NULL on error, otherwise the plugin context
1496 */
1497void *
1498REST_reclaim_init (struct GNUNET_CONFIGURATION_Handle *c)
1499{
1500 static struct Plugin plugin;
1501 struct GNUNET_REST_Plugin *api;
1502
1503 rcfg = c;
1504 if (NULL != plugin.cfg)
1505 return NULL; /* can only initialize once! */
1506 memset (&plugin, 0, sizeof(struct Plugin));
1507 plugin.cfg = rcfg;
1508 api = GNUNET_new (struct GNUNET_REST_Plugin);
1509 api->cls = &plugin;
1510 api->name = GNUNET_REST_API_NS_RECLAIM;
1511 GNUNET_asprintf (&allow_methods,
1512 "%s, %s, %s, %s, %s",
1513 MHD_HTTP_METHOD_GET,
1514 MHD_HTTP_METHOD_POST,
1515 MHD_HTTP_METHOD_PUT,
1516 MHD_HTTP_METHOD_DELETE,
1517 MHD_HTTP_METHOD_OPTIONS);
1518 identity_handle = GNUNET_IDENTITY_connect (rcfg, &list_ego, NULL);
1519 state = ID_REST_STATE_INIT;
1520 idp = GNUNET_RECLAIM_connect (rcfg);
1521 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1522 _ ("Identity Provider REST API initialized\n"));
1523 return api;
1524}
1525
1526
1527/**
1528 * Exit point from the plugin.
1529 *
1530 * @param cls the plugin context (as returned by "init")
1531 * @return always NULL
1532 */
1533void *
1534REST_reclaim_done (struct GNUNET_REST_Plugin *api)
1535{
1536 struct Plugin *plugin = api->cls;
1537 struct RequestHandle *request;
1538 struct EgoEntry *ego_entry;
1539 struct EgoEntry *ego_tmp;
1540
1541 plugin->cfg = NULL;
1542 while (NULL != (request = requests_head))
1543 do_error (request);
1544 if (NULL != idp)
1545 GNUNET_RECLAIM_disconnect (idp);
1546 if (NULL != identity_handle)
1547 GNUNET_IDENTITY_disconnect (identity_handle);
1548 for (ego_entry = ego_head; NULL != ego_entry;)
1549 {
1550 ego_tmp = ego_entry;
1551 ego_entry = ego_entry->next;
1552 GNUNET_free (ego_tmp->identifier);
1553 GNUNET_free (ego_tmp->keystring);
1554 GNUNET_free (ego_tmp);
1555 }
1556
1557 GNUNET_free (allow_methods);
1558 GNUNET_free (api);
1559 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1560 "Identity Provider REST plugin is finished\n");
1561 return NULL;
1562}
1563
1564
1565/* end of plugin_rest_reclaim.c */
diff --git a/src/service/rest/reclaim_plugin.h b/src/service/rest/reclaim_plugin.h
new file mode 100644
index 000000000..102fd827e
--- /dev/null
+++ b/src/service/rest/reclaim_plugin.h
@@ -0,0 +1,36 @@
1#include "gnunet_rest_plugin.h"
2/**
3 * Function processing the REST call
4 *
5 * @param method HTTP method
6 * @param url URL of the HTTP request
7 * @param data body of the HTTP request (optional)
8 * @param data_size length of the body
9 * @param proc callback function for the result
10 * @param proc_cls closure for @a proc
11 * @return #GNUNET_OK if request accepted
12 */
13enum GNUNET_GenericReturnValue
14REST_reclaim_process_request (void *plugin,
15 struct GNUNET_REST_RequestHandle *conndata_handle,
16 GNUNET_REST_ResultProcessor proc,
17 void *proc_cls);
18
19/**
20 * Entry point for the plugin.
21 *
22 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
23 * @return NULL on error, otherwise the plugin context
24 */
25void*
26REST_reclaim_init (const struct GNUNET_CONFIGURATION_Handle *c);
27
28
29/**
30 * Exit point from the plugin.
31 *
32 * @param cls the plugin context (as returned by "init")
33 * @return always NULL
34 */
35void
36REST_reclaim_done (struct GNUNET_REST_Plugin *api);
diff --git a/src/service/revocation/Makefile.am b/src/service/revocation/Makefile.am
index 726c75ab6..18bf21fb0 100644
--- a/src/service/revocation/Makefile.am
+++ b/src/service/revocation/Makefile.am
@@ -29,7 +29,7 @@ libgnunetrevocation_la_SOURCES = \
29 revocation_api.c revocation.h 29 revocation_api.c revocation.h
30libgnunetrevocation_la_LIBADD = \ 30libgnunetrevocation_la_LIBADD = \
31 $(top_builddir)/src/lib/util/libgnunetutil.la \ 31 $(top_builddir)/src/lib/util/libgnunetutil.la \
32 $(top_builddir)/src/service/identity/libgnunetidentity.la \ 32 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
33 $(LIBGCRYPT_LIBS) \ 33 $(LIBGCRYPT_LIBS) \
34 $(GN_LIBINTL) $(XLIB) -lgcrypt 34 $(GN_LIBINTL) $(XLIB) -lgcrypt
35libgnunetrevocation_la_LDFLAGS = \ 35libgnunetrevocation_la_LDFLAGS = \
@@ -46,7 +46,7 @@ gnunet_service_revocation_LDADD = \
46 $(top_builddir)/src/service/core/libgnunetcore.la \ 46 $(top_builddir)/src/service/core/libgnunetcore.la \
47 $(top_builddir)/src/service/setu/libgnunetsetu.la \ 47 $(top_builddir)/src/service/setu/libgnunetsetu.la \
48 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ 48 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
49 $(top_builddir)/src/service/identity/libgnunetidentity.la \ 49 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
50 $(top_builddir)/src/lib/util/libgnunetutil.la \ 50 $(top_builddir)/src/lib/util/libgnunetutil.la \
51 -lm \ 51 -lm \
52 $(GN_LIBINTL) 52 $(GN_LIBINTL)
diff --git a/src/service/revocation/gnunet-service-revocation.c b/src/service/revocation/gnunet-service-revocation.c
index 3755b87e5..8a8610758 100644
--- a/src/service/revocation/gnunet-service-revocation.c
+++ b/src/service/revocation/gnunet-service-revocation.c
@@ -38,6 +38,7 @@
38#include "platform.h" 38#include "platform.h"
39#include <math.h> 39#include <math.h>
40#include "gnunet_util_lib.h" 40#include "gnunet_util_lib.h"
41#include "gnunet_gnsrecord_lib.h"
41#include "gnunet_block_lib.h" 42#include "gnunet_block_lib.h"
42#include "gnunet_constants.h" 43#include "gnunet_constants.h"
43#include "gnunet_protocols.h" 44#include "gnunet_protocols.h"
@@ -172,13 +173,13 @@ new_peer_entry (const struct GNUNET_PeerIdentity *peer)
172static enum GNUNET_GenericReturnValue 173static enum GNUNET_GenericReturnValue
173verify_revoke_message (const struct RevokeMessage *rm) 174verify_revoke_message (const struct RevokeMessage *rm)
174{ 175{
175 const struct GNUNET_REVOCATION_PowP *pow 176 const struct GNUNET_GNSRECORD_PowP *pow
176 = (const struct GNUNET_REVOCATION_PowP *) &rm[1]; 177 = (const struct GNUNET_GNSRECORD_PowP *) &rm[1];
177 178
178 if (GNUNET_YES != 179 if (GNUNET_YES !=
179 GNUNET_REVOCATION_check_pow (pow, 180 GNUNET_GNSRECORD_check_pow (pow,
180 (unsigned int) revocation_work_required, 181 (unsigned int) revocation_work_required,
181 epoch_duration)) 182 epoch_duration))
182 { 183 {
183 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 184 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
184 "Proof of work invalid!\n"); 185 "Proof of work invalid!\n");
@@ -221,6 +222,7 @@ client_disconnect_cb (void *cls,
221 GNUNET_assert (client == app_cls); 222 GNUNET_assert (client == app_cls);
222} 223}
223 224
225
224static int 226static int
225check_query_message (void *cls, 227check_query_message (void *cls,
226 const struct QueryMessage *qm) 228 const struct QueryMessage *qm)
@@ -261,7 +263,7 @@ handle_query_message (void *cls,
261 key_len = ntohl (qm->key_len); 263 key_len = ntohl (qm->key_len);
262 if ((GNUNET_SYSERR == 264 if ((GNUNET_SYSERR ==
263 GNUNET_CRYPTO_read_public_key_from_buffer (&qm[1], key_len, 265 GNUNET_CRYPTO_read_public_key_from_buffer (&qm[1], key_len,
264 &zone, &read)) || 266 &zone, &read)) ||
265 (read != key_len)) 267 (read != key_len))
266 { 268 {
267 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 269 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -342,8 +344,8 @@ publicize_rm (const struct RevokeMessage *rm)
342 struct GNUNET_HashCode hc; 344 struct GNUNET_HashCode hc;
343 struct GNUNET_SETU_Element e; 345 struct GNUNET_SETU_Element e;
344 ssize_t pklen; 346 ssize_t pklen;
345 const struct GNUNET_REVOCATION_PowP *pow 347 const struct GNUNET_GNSRECORD_PowP *pow
346 = (const struct GNUNET_REVOCATION_PowP *) &rm[1]; 348 = (const struct GNUNET_GNSRECORD_PowP *) &rm[1];
347 const struct GNUNET_CRYPTO_PublicKey *pk 349 const struct GNUNET_CRYPTO_PublicKey *pk
348 = (const struct GNUNET_CRYPTO_PublicKey *) &pow[1]; 350 = (const struct GNUNET_CRYPTO_PublicKey *) &pow[1];
349 351
@@ -980,8 +982,8 @@ run (void *cls,
980 GNUNET_free (fn); 982 GNUNET_free (fn);
981 return; 983 return;
982 } 984 }
983 struct GNUNET_REVOCATION_PowP *pow = (struct 985 struct GNUNET_GNSRECORD_PowP *pow = (struct
984 GNUNET_REVOCATION_PowP *) &rm[1]; 986 GNUNET_GNSRECORD_PowP *) &rm[1];
985 ssize_t ksize; 987 ssize_t ksize;
986 pk = (const struct GNUNET_CRYPTO_PublicKey *) &pow[1]; 988 pk = (const struct GNUNET_CRYPTO_PublicKey *) &pow[1];
987 ksize = GNUNET_CRYPTO_public_key_get_length (pk); 989 ksize = GNUNET_CRYPTO_public_key_get_length (pk);
diff --git a/src/service/revocation/meson.build b/src/service/revocation/meson.build
index 14e352ffa..57f19f115 100644
--- a/src/service/revocation/meson.build
+++ b/src/service/revocation/meson.build
@@ -19,7 +19,9 @@ libgnunetrevocation = library('gnunetrevocation',
19 libgnunetrevocation_src, 19 libgnunetrevocation_src,
20 soversion: '0', 20 soversion: '0',
21 version: '0.0.0', 21 version: '0.0.0',
22 dependencies: [libgnunetutil_dep, libgnunetidentity_dep], 22 dependencies: [libgnunetutil_dep,
23 libgnunetgnsrecord_dep,
24 libgnunetidentity_dep],
23 include_directories: [incdir, configuration_inc], 25 include_directories: [incdir, configuration_inc],
24 install: true, 26 install: true,
25 install_dir: get_option('libdir')) 27 install_dir: get_option('libdir'))
@@ -34,6 +36,7 @@ executable ('gnunet-service-revocation',
34 libgnunetstatistics_dep, 36 libgnunetstatistics_dep,
35 libgnunetcore_dep, 37 libgnunetcore_dep,
36 libgnunetsetu_dep, 38 libgnunetsetu_dep,
39 libgnunetgnsrecord_dep,
37 libgnunetidentity_dep], 40 libgnunetidentity_dep],
38 include_directories: [incdir, configuration_inc], 41 include_directories: [incdir, configuration_inc],
39 install: true, 42 install: true,
diff --git a/src/service/revocation/revocation.h b/src/service/revocation/revocation.h
index 90b8c7da0..cbb36acfb 100644
--- a/src/service/revocation/revocation.h
+++ b/src/service/revocation/revocation.h
@@ -112,13 +112,4 @@ struct RevocationResponseMessage
112 112
113GNUNET_NETWORK_STRUCT_END 113GNUNET_NETWORK_STRUCT_END
114 114
115/**
116 * Create the revocation metadata to sign for a revocation message
117 *
118 * @param pow the PoW to sign
119 * @return the signature purpose
120 */
121struct GNUNET_REVOCATION_SignaturePurposePS *
122REV_create_signature_message (const struct GNUNET_REVOCATION_PowP *pow);
123
124#endif 115#endif
diff --git a/src/service/revocation/revocation_api.c b/src/service/revocation/revocation_api.c
index 3b7d83710..0e3641af8 100644
--- a/src/service/revocation/revocation_api.c
+++ b/src/service/revocation/revocation_api.c
@@ -52,60 +52,6 @@ struct GNUNET_REVOCATION_Query
52 52
53 53
54/** 54/**
55 * Helper struct that holds a found pow nonce
56 * and the corresponding number of leading zeros.
57 */
58struct BestPow
59{
60 /**
61 * PoW nonce
62 */
63 uint64_t pow;
64
65 /**
66 * Corresponding zero bits in hash
67 */
68 unsigned int bits;
69};
70
71
72/**
73 * The handle to a PoW calculation.
74 * Used in iterative PoW rounds.
75 */
76struct GNUNET_REVOCATION_PowCalculationHandle
77{
78 /**
79 * Current set of found PoWs
80 */
81 struct BestPow best[POW_COUNT];
82
83 /**
84 * The final PoW result data structure.
85 */
86 struct GNUNET_REVOCATION_PowP *pow;
87
88 /**
89 * The current nonce to try
90 */
91 uint64_t current_pow;
92
93 /**
94 * Epochs how long the PoW should be valid.
95 * This is added on top of the difficulty in the PoW.
96 */
97 unsigned int epochs;
98
99 /**
100 * The difficulty (leading zeros) to achieve.
101 */
102 unsigned int difficulty;
103
104};
105
106static struct GNUNET_CRYPTO_PowSalt salt = { "GnsRevocationPow" };
107
108/**
109 * Generic error handler, called with the appropriate 55 * Generic error handler, called with the appropriate
110 * error code and the same closure specified at the creation of 56 * error code and the same closure specified at the creation of
111 * the message queue. 57 * the message queue.
@@ -301,7 +247,7 @@ handle_revocation_response (void *cls,
301 */ 247 */
302struct GNUNET_REVOCATION_Handle * 248struct GNUNET_REVOCATION_Handle *
303GNUNET_REVOCATION_revoke (const struct GNUNET_CONFIGURATION_Handle *cfg, 249GNUNET_REVOCATION_revoke (const struct GNUNET_CONFIGURATION_Handle *cfg,
304 const struct GNUNET_REVOCATION_PowP *pow, 250 const struct GNUNET_GNSRECORD_PowP *pow,
305 GNUNET_REVOCATION_Callback func, 251 GNUNET_REVOCATION_Callback func,
306 void *func_cls) 252 void *func_cls)
307{ 253{
@@ -339,9 +285,9 @@ GNUNET_REVOCATION_revoke (const struct GNUNET_CONFIGURATION_Handle *cfg,
339 GNUNET_free (h); 285 GNUNET_free (h);
340 return NULL; 286 return NULL;
341 } 287 }
342 if (GNUNET_YES != GNUNET_REVOCATION_check_pow (pow, 288 if (GNUNET_YES != GNUNET_GNSRECORD_check_pow (pow,
343 (unsigned int) matching_bits, 289 (unsigned int) matching_bits,
344 epoch_duration)) 290 epoch_duration))
345 { 291 {
346 GNUNET_break (0); 292 GNUNET_break (0);
347 GNUNET_free (h); 293 GNUNET_free (h);
@@ -361,7 +307,7 @@ GNUNET_REVOCATION_revoke (const struct GNUNET_CONFIGURATION_Handle *cfg,
361 } 307 }
362 h->func = func; 308 h->func = func;
363 h->func_cls = func_cls; 309 h->func_cls = func_cls;
364 size_t extra_len = GNUNET_REVOCATION_proof_get_size (pow); 310 size_t extra_len = GNUNET_GNSRECORD_proof_get_size (pow);
365 env = GNUNET_MQ_msg_extra (rm, 311 env = GNUNET_MQ_msg_extra (rm,
366 extra_len, 312 extra_len,
367 GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE); 313 GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE);
@@ -385,380 +331,4 @@ GNUNET_REVOCATION_revoke_cancel (struct GNUNET_REVOCATION_Handle *h)
385} 331}
386 332
387 333
388/**
389 * Calculate the average zeros in the pows.
390 *
391 * @param ph the PowHandle
392 * @return the average number of zeros.
393 */
394static unsigned int
395calculate_score (const struct GNUNET_REVOCATION_PowCalculationHandle *ph)
396{
397 double sum = 0.0;
398 for (unsigned int j = 0; j<POW_COUNT; j++)
399 sum += ph->best[j].bits;
400 double avg = sum / POW_COUNT;
401 return avg;
402}
403
404
405struct GNUNET_REVOCATION_SignaturePurposePS *
406REV_create_signature_message (const struct GNUNET_REVOCATION_PowP *pow)
407{
408 struct GNUNET_REVOCATION_SignaturePurposePS *spurp;
409 const struct GNUNET_CRYPTO_PublicKey *pk;
410 size_t ksize;
411
412 pk = (const struct GNUNET_CRYPTO_PublicKey *) &pow[1];
413 ksize = GNUNET_CRYPTO_public_key_get_length (pk);
414 spurp = GNUNET_malloc (sizeof (*spurp) + ksize);
415 spurp->timestamp = pow->timestamp;
416 spurp->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_GNS_REVOCATION);
417 spurp->purpose.size = htonl (sizeof(*spurp) + ksize);
418 GNUNET_CRYPTO_write_public_key_to_buffer (pk,
419 (char*) &spurp[1],
420 ksize);
421 return spurp;
422}
423
424
425enum GNUNET_GenericReturnValue
426check_signature_identity (const struct GNUNET_REVOCATION_PowP *pow,
427 const struct GNUNET_CRYPTO_PublicKey *key)
428{
429 struct GNUNET_REVOCATION_SignaturePurposePS *spurp;
430 unsigned char *sig;
431 size_t ksize;
432 int ret;
433
434 ksize = GNUNET_CRYPTO_public_key_get_length (key);
435 spurp = REV_create_signature_message (pow);
436 sig = ((unsigned char*) &pow[1] + ksize);
437 ret =
438 GNUNET_CRYPTO_signature_verify_raw_ (
439 GNUNET_SIGNATURE_PURPOSE_GNS_REVOCATION,
440 &spurp->purpose,
441 sig,
442 key);
443 GNUNET_free (spurp);
444 return ret == GNUNET_OK ? GNUNET_OK : GNUNET_SYSERR;
445}
446
447
448enum GNUNET_GenericReturnValue
449check_signature (const struct GNUNET_REVOCATION_PowP *pow)
450{
451 const struct GNUNET_CRYPTO_PublicKey *pk;
452
453 pk = (const struct GNUNET_CRYPTO_PublicKey *) &pow[1];
454 return check_signature_identity (pow, pk);
455}
456
457
458/**
459 * Check if the given proof-of-work is valid.
460 *
461 * @param pow proof of work
462 * @param difficulty how many bits must match (configuration) LSD0001: D
463 * @param epoch_duration length of single epoch in configuration
464 * @return #GNUNET_YES if the @a pow is acceptable, #GNUNET_NO if not
465 */
466enum GNUNET_GenericReturnValue
467GNUNET_REVOCATION_check_pow (const struct GNUNET_REVOCATION_PowP *pow,
468 unsigned int difficulty,
469 struct GNUNET_TIME_Relative epoch_duration)
470{
471 char buf[sizeof(struct GNUNET_CRYPTO_PublicKey)
472 + sizeof (struct GNUNET_TIME_AbsoluteNBO)
473 + sizeof (uint64_t)] GNUNET_ALIGN;
474 struct GNUNET_HashCode result;
475 struct GNUNET_TIME_Absolute ts;
476 struct GNUNET_TIME_Absolute exp;
477 struct GNUNET_TIME_Relative ttl;
478 struct GNUNET_TIME_Relative buffer;
479 /* LSD0001: D' */
480 unsigned int score = 0;
481 unsigned int tmp_score = 0;
482 unsigned int epochs;
483 uint64_t pow_val;
484 ssize_t pklen;
485 const struct GNUNET_CRYPTO_PublicKey *pk;
486
487 pk = (const struct GNUNET_CRYPTO_PublicKey *) &pow[1];
488
489 /**
490 * Check if signature valid
491 */
492 if (GNUNET_OK != check_signature (pow))
493 {
494 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
495 "Proof of work signature invalid!\n");
496 return GNUNET_SYSERR;
497 }
498
499 /**
500 * First, check if PoW set is strictly monotically increasing
501 */
502 for (unsigned int i = 0; i < POW_COUNT - 1; i++)
503 {
504 if (GNUNET_ntohll (pow->pow[i]) >= GNUNET_ntohll (pow->pow[i + 1]))
505 return GNUNET_NO;
506 }
507 GNUNET_memcpy (&buf[sizeof(uint64_t)],
508 &pow->timestamp,
509 sizeof (uint64_t));
510 pklen = GNUNET_CRYPTO_public_key_get_length (pk);
511 if (0 > pklen)
512 {
513 GNUNET_break (0);
514 return GNUNET_NO;
515 }
516 GNUNET_memcpy (&buf[sizeof(uint64_t) * 2],
517 pk,
518 pklen);
519 for (unsigned int i = 0; i < POW_COUNT; i++)
520 {
521 pow_val = GNUNET_ntohll (pow->pow[i]);
522 GNUNET_memcpy (buf, &pow->pow[i], sizeof(uint64_t));
523 GNUNET_CRYPTO_pow_hash (&salt,
524 buf,
525 sizeof(buf),
526 &result);
527 tmp_score = GNUNET_CRYPTO_hash_count_leading_zeros (&result);
528 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
529 "Score %u with %" PRIu64 " (#%u)\n",
530 tmp_score, pow_val, i);
531
532 score += tmp_score;
533
534 }
535 score = score / POW_COUNT;
536 if (score < difficulty)
537 return GNUNET_NO;
538 /* LSD0001: (D'-D+1) */
539 epochs = score - difficulty + 1;
540
541 /**
542 * Check expiration
543 */
544 ts = GNUNET_TIME_absolute_ntoh (pow->timestamp);
545 ttl = GNUNET_TIME_relative_multiply (epoch_duration,
546 epochs);
547 /**
548 * Extend by 10% for unsynchronized clocks
549 */
550 buffer = GNUNET_TIME_relative_divide (epoch_duration,
551 10);
552 exp = GNUNET_TIME_absolute_add (ts, ttl);
553 exp = GNUNET_TIME_absolute_add (exp,
554 buffer);
555
556 if (0 != GNUNET_TIME_absolute_get_remaining (ts).rel_value_us)
557 return GNUNET_NO; /* Not yet valid. */
558 /* Revert to actual start time */
559 ts = GNUNET_TIME_absolute_add (ts,
560 buffer);
561
562 if (0 == GNUNET_TIME_absolute_get_remaining (exp).rel_value_us)
563 return GNUNET_NO; /* expired */
564 return GNUNET_YES;
565}
566
567
568enum GNUNET_GenericReturnValue
569sign_pow_identity (const struct GNUNET_CRYPTO_PrivateKey *key,
570 struct GNUNET_REVOCATION_PowP *pow)
571{
572 struct GNUNET_TIME_Absolute ts = GNUNET_TIME_absolute_get ();
573 struct GNUNET_REVOCATION_SignaturePurposePS *rp;
574 const struct GNUNET_CRYPTO_PublicKey *pk;
575 size_t ksize;
576 char *sig;
577
578 /**
579 * Predate the validity period to prevent rejections due to
580 * unsynchronized clocks
581 */
582 ts = GNUNET_TIME_absolute_subtract (ts,
583 GNUNET_TIME_UNIT_WEEKS);
584 pk = (const struct GNUNET_CRYPTO_PublicKey *) &pow[1];
585 ksize = GNUNET_CRYPTO_public_key_get_length (pk);
586 pow->timestamp = GNUNET_TIME_absolute_hton (ts);
587 rp = REV_create_signature_message (pow);
588 sig = ((char*) &pow[1]) + ksize;
589 int result = GNUNET_CRYPTO_sign_raw_ (key,
590 &rp->purpose,
591 (void*) sig);
592 GNUNET_free (rp);
593 if (result == GNUNET_SYSERR)
594 return GNUNET_NO;
595 else
596 return result;
597}
598
599
600enum GNUNET_GenericReturnValue
601sign_pow (const struct GNUNET_CRYPTO_PrivateKey *key,
602 struct GNUNET_REVOCATION_PowP *pow)
603{
604 struct GNUNET_CRYPTO_PublicKey *pk;
605
606 pk = (struct GNUNET_CRYPTO_PublicKey *) &pow[1];
607 GNUNET_CRYPTO_key_get_public (key, pk);
608 return sign_pow_identity (key, pow);
609}
610
611
612/**
613 * Initializes a fresh PoW computation.
614 *
615 * @param key the key to calculate the PoW for.
616 * @param[out] pow starting point for PoW calculation (not yet valid)
617 */
618void
619GNUNET_REVOCATION_pow_init (const struct GNUNET_CRYPTO_PrivateKey *key,
620 struct GNUNET_REVOCATION_PowP *pow)
621{
622 GNUNET_assert (GNUNET_OK == sign_pow (key, pow));
623}
624
625
626struct GNUNET_REVOCATION_PowCalculationHandle*
627GNUNET_REVOCATION_pow_start (struct GNUNET_REVOCATION_PowP *pow,
628 int epochs,
629 unsigned int difficulty)
630{
631 struct GNUNET_REVOCATION_PowCalculationHandle *pc;
632 struct GNUNET_TIME_Relative ttl;
633
634
635 pc = GNUNET_new (struct GNUNET_REVOCATION_PowCalculationHandle);
636 pc->pow = pow;
637 ttl = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS,
638 epochs);
639 pc->pow->ttl = GNUNET_TIME_relative_hton (ttl);
640 pc->current_pow = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
641 UINT64_MAX);
642 pc->difficulty = difficulty;
643 pc->epochs = epochs;
644 return pc;
645}
646
647
648/**
649 * Comparison function for quicksort
650 *
651 * @param a left element
652 * @param b right element
653 * @return a-b
654 */
655static int
656cmp_pow_value (const void *a, const void *b)
657{
658 return (GNUNET_ntohll (*(uint64_t*) a) - GNUNET_ntohll (*(uint64_t*) b));
659}
660
661
662/**
663 * Calculate a key revocation valid for broadcasting for a number
664 * of epochs.
665 *
666 * @param pc handle to the PoW, initially called with NULL.
667 * @param epochs number of epochs for which the revocation must be valid.
668 * @param pow current pow value to try
669 * @param difficulty current base difficulty to achieve
670 * @return #GNUNET_YES if the @a pow is acceptable, #GNUNET_NO if not
671 */
672enum GNUNET_GenericReturnValue
673GNUNET_REVOCATION_pow_round (struct GNUNET_REVOCATION_PowCalculationHandle *pc)
674{
675 char buf[sizeof(struct GNUNET_CRYPTO_PublicKey)
676 + sizeof (uint64_t)
677 + sizeof (uint64_t)] GNUNET_ALIGN;
678 struct GNUNET_HashCode result;
679 const struct GNUNET_CRYPTO_PublicKey *pk;
680 unsigned int zeros;
681 int ret;
682 uint64_t pow_nbo;
683 ssize_t ksize;
684
685 pc->current_pow++;
686 pk = (const struct GNUNET_CRYPTO_PublicKey *) &(pc->pow[1]);
687
688 /**
689 * Do not try duplicates
690 */
691 for (unsigned int i = 0; i < POW_COUNT; i++)
692 if (pc->current_pow == pc->best[i].pow)
693 return GNUNET_NO;
694 pow_nbo = GNUNET_htonll (pc->current_pow);
695 GNUNET_memcpy (buf, &pow_nbo, sizeof(uint64_t));
696 GNUNET_memcpy (&buf[sizeof(uint64_t)],
697 &pc->pow->timestamp,
698 sizeof (uint64_t));
699 ksize = GNUNET_CRYPTO_public_key_get_length (pk);
700 GNUNET_assert (0 < ksize);
701 GNUNET_memcpy (&buf[sizeof(uint64_t) * 2],
702 pk,
703 ksize);
704 GNUNET_CRYPTO_pow_hash (&salt,
705 buf,
706 sizeof(buf),
707 &result);
708 zeros = GNUNET_CRYPTO_hash_count_leading_zeros (&result);
709 for (unsigned int i = 0; i < POW_COUNT; i++)
710 {
711 if (pc->best[i].bits < zeros)
712 {
713 pc->best[i].bits = zeros;
714 pc->best[i].pow = pc->current_pow;
715 pc->pow->pow[i] = pow_nbo;
716 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
717 "New best score %u with %" PRIu64 " (#%u)\n",
718 zeros, pc->current_pow, i);
719
720 break;
721 }
722 }
723 ret = calculate_score (pc) >= pc->difficulty + pc->epochs ? GNUNET_YES :
724 GNUNET_NO;
725 if (GNUNET_YES == ret)
726 {
727 /* Sort POWs) */
728 qsort (pc->pow->pow, POW_COUNT, sizeof (uint64_t), &cmp_pow_value);
729 }
730 return ret;
731}
732
733
734/**
735 * Stop a PoW calculation
736 *
737 * @param pc the calculation to clean up
738 * @return #GNUNET_YES if pow valid, #GNUNET_NO if pow was set but is not
739 * valid
740 */
741void
742GNUNET_REVOCATION_pow_stop (struct GNUNET_REVOCATION_PowCalculationHandle *pc)
743{
744 GNUNET_free (pc);
745}
746
747
748size_t
749GNUNET_REVOCATION_proof_get_size (const struct GNUNET_REVOCATION_PowP *pow)
750{
751 size_t size;
752 size_t ksize;
753 const struct GNUNET_CRYPTO_PublicKey *pk;
754
755 size = sizeof (struct GNUNET_REVOCATION_PowP);
756 pk = (const struct GNUNET_CRYPTO_PublicKey *) &pow[1];
757 ksize = GNUNET_CRYPTO_public_key_get_length (pk);
758 size += ksize;
759 size += GNUNET_CRYPTO_signature_get_raw_length_by_type (pk->type);
760 return size;
761}
762
763
764/* end of revocation_api.c */ 334/* end of revocation_api.c */
diff --git a/src/service/seti/Makefile.am b/src/service/seti/Makefile.am
index c2d31df9c..00923d56d 100644
--- a/src/service/seti/Makefile.am
+++ b/src/service/seti/Makefile.am
@@ -68,19 +68,5 @@ test_seti_api_LDADD = \
68 $(top_builddir)/src/service/testing/libgnunettesting.la \ 68 $(top_builddir)/src/service/testing/libgnunettesting.la \
69 libgnunetseti.la 69 libgnunetseti.la
70 70
71plugin_LTLIBRARIES = \
72 libgnunet_plugin_block_seti_test.la
73
74libgnunet_plugin_block_seti_test_la_SOURCES = \
75 plugin_block_seti_test.c
76libgnunet_plugin_block_seti_test_la_LIBADD = \
77 $(top_builddir)/src/lib/block/libgnunetblock.la \
78 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \
79 $(top_builddir)/src/lib/util/libgnunetutil.la \
80 $(LTLIBINTL)
81libgnunet_plugin_block_seti_test_la_LDFLAGS = \
82 $(GN_PLUGIN_LDFLAGS)
83
84
85EXTRA_DIST = \ 71EXTRA_DIST = \
86 test_seti.conf 72 test_seti.conf
diff --git a/src/service/seti/meson.build b/src/service/seti/meson.build
index 8538917d8..1743e327d 100644
--- a/src/service/seti/meson.build
+++ b/src/service/seti/meson.build
@@ -26,12 +26,6 @@ libgnunetseti = library('gnunetseti',
26pkg.generate(libgnunetseti, url: 'https://www.gnunet.org', 26pkg.generate(libgnunetseti, url: 'https://www.gnunet.org',
27 description : 'Provides API for accessing the set intersection service') 27 description : 'Provides API for accessing the set intersection service')
28libgnunetseti_dep = declare_dependency(link_with : libgnunetseti) 28libgnunetseti_dep = declare_dependency(link_with : libgnunetseti)
29shared_module('gnunet_plugin_block_seti_test',
30 ['plugin_block_seti_test.c'],
31 dependencies: libgnunetutil_dep,
32 include_directories: [incdir, configuration_inc],
33 install:true,
34 install_dir: get_option('libdir')/'gnunet')
35executable ('gnunet-service-seti', 29executable ('gnunet-service-seti',
36 gnunetserviceseti_src, 30 gnunetserviceseti_src,
37 dependencies: [libgnunetseti_dep, 31 dependencies: [libgnunetseti_dep,
diff --git a/src/service/seti/plugin_block_seti_test.c b/src/service/seti/plugin_block_seti_test.c
deleted file mode 100644
index 5b9196cef..000000000
--- a/src/service/seti/plugin_block_seti_test.c
+++ /dev/null
@@ -1,197 +0,0 @@
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/service/setu/Makefile.am b/src/service/setu/Makefile.am
index f7f8ed394..f0cf9fdbc 100644
--- a/src/service/setu/Makefile.am
+++ b/src/service/setu/Makefile.am
@@ -88,19 +88,5 @@ perf_setu_api_LDADD = \
88 libgnunetsetu.la 88 libgnunetsetu.la
89 89
90 90
91plugin_LTLIBRARIES = \
92 libgnunet_plugin_block_setu_test.la
93
94libgnunet_plugin_block_setu_test_la_SOURCES = \
95 plugin_block_setu_test.c
96libgnunet_plugin_block_setu_test_la_LIBADD = \
97 $(top_builddir)/src/lib/block/libgnunetblock.la \
98 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \
99 $(top_builddir)/src/lib/util/libgnunetutil.la \
100 $(LTLIBINTL)
101libgnunet_plugin_block_setu_test_la_LDFLAGS = \
102 $(GN_PLUGIN_LDFLAGS)
103
104
105EXTRA_DIST = \ 91EXTRA_DIST = \
106 test_setu.conf 92 test_setu.conf
diff --git a/src/service/setu/meson.build b/src/service/setu/meson.build
index 974f16274..ccfa5762b 100644
--- a/src/service/setu/meson.build
+++ b/src/service/setu/meson.build
@@ -28,12 +28,6 @@ libgnunetsetu = library('gnunetsetu',
28pkg.generate(libgnunetsetu, url: 'https://www.gnunet.org', 28pkg.generate(libgnunetsetu, url: 'https://www.gnunet.org',
29 description : 'Provides API for accessing the set union service') 29 description : 'Provides API for accessing the set union service')
30libgnunetsetu_dep = declare_dependency(link_with : libgnunetsetu) 30libgnunetsetu_dep = declare_dependency(link_with : libgnunetsetu)
31shared_module('gnunet_plugin_block_setu_test',
32 ['plugin_block_setu_test.c'],
33 dependencies: libgnunetutil_dep,
34 include_directories: [incdir, configuration_inc],
35 install:true,
36 install_dir: get_option('libdir')/'gnunet')
37executable ('gnunet-service-setu', 31executable ('gnunet-service-setu',
38 gnunetservicesetu_src, 32 gnunetservicesetu_src,
39 dependencies: [libgnunetsetu_dep, 33 dependencies: [libgnunetsetu_dep,
diff --git a/src/service/setu/plugin_block_setu_test.c b/src/service/setu/plugin_block_setu_test.c
deleted file mode 100644
index 178ad3314..000000000
--- a/src/service/setu/plugin_block_setu_test.c
+++ /dev/null
@@ -1,195 +0,0 @@
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 */
diff --git a/src/service/topology/gnunet-daemon-topology.c b/src/service/topology/gnunet-daemon-topology.c
index efabac0fc..71cc5bd19 100644
--- a/src/service/topology/gnunet-daemon-topology.c
+++ b/src/service/topology/gnunet-daemon-topology.c
@@ -641,35 +641,26 @@ consider_for_advertising (const struct GNUNET_MessageHeader *hello)
641 int num_addresses_old; 641 int num_addresses_old;
642 int num_addresses_new; 642 int num_addresses_new;
643 struct GNUNET_HELLO_Builder *builder = GNUNET_HELLO_builder_from_msg (hello); 643 struct GNUNET_HELLO_Builder *builder = GNUNET_HELLO_builder_from_msg (hello);
644 struct GNUNET_PeerIdentity *pid = GNUNET_HELLO_builder_get_id (builder); 644 struct GNUNET_PeerIdentity pid;
645 struct Peer *peer; 645 struct Peer *peer;
646 uint16_t size; 646 uint16_t size;
647 647
648 GNUNET_HELLO_builder_iterate (builder, 648 GNUNET_HELLO_builder_iterate (builder,
649 pid, 649 &pid,
650 &address_iterator, 650 &address_iterator,
651 &num_addresses_new); 651 &num_addresses_new);
652 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 652 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
653 "consider 0 for %s\n", 653 "consider 0 for %s\n",
654 GNUNET_i2s (pid)); 654 GNUNET_i2s (&pid));
655 if (0 == num_addresses_new) 655 if (0 == num_addresses_new)
656 { 656 {
657 GNUNET_HELLO_builder_free (builder); 657 GNUNET_HELLO_builder_free (builder);
658 return; /* no point in advertising this one... */ 658 return; /* no point in advertising this one... */
659 } 659 }
660 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 660 peer = GNUNET_CONTAINER_multipeermap_get (peers, &pid);
661 "consider 1\n");
662 if (NULL == pid)
663 {
664 GNUNET_HELLO_builder_free (builder);
665 return;
666 }
667 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
668 "consider 2\n");
669 peer = GNUNET_CONTAINER_multipeermap_get (peers, pid);
670 if (NULL == peer) 661 if (NULL == peer)
671 { 662 {
672 peer = make_peer (pid, hello); 663 peer = make_peer (&pid, hello);
673 } 664 }
674 else if (NULL != peer->hello) 665 else if (NULL != peer->hello)
675 { 666 {
@@ -679,10 +670,10 @@ consider_for_advertising (const struct GNUNET_MessageHeader *hello)
679 struct GNUNET_TIME_Absolute old_hello_exp = 670 struct GNUNET_TIME_Absolute old_hello_exp =
680 GNUNET_HELLO_builder_get_expiration_time (peer->hello); 671 GNUNET_HELLO_builder_get_expiration_time (peer->hello);
681 struct GNUNET_HELLO_Builder *builder_old = GNUNET_HELLO_builder_from_msg (peer->hello); 672 struct GNUNET_HELLO_Builder *builder_old = GNUNET_HELLO_builder_from_msg (peer->hello);
682 struct GNUNET_PeerIdentity *pid_old = GNUNET_HELLO_builder_get_id (builder_old); 673 struct GNUNET_PeerIdentity pid_old;
683 674
684 GNUNET_HELLO_builder_iterate (builder_old, 675 GNUNET_HELLO_builder_iterate (builder_old,
685 pid_old, 676 &pid_old,
686 &address_iterator, 677 &address_iterator,
687 &num_addresses_old); 678 &num_addresses_old);
688 if (GNUNET_TIME_absolute_cmp (new_hello_exp, >, now) && 679 if (GNUNET_TIME_absolute_cmp (new_hello_exp, >, now) &&
@@ -710,7 +701,7 @@ consider_for_advertising (const struct GNUNET_MessageHeader *hello)
710 } 701 }
711 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 702 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
712 "Found HELLO from peer `%s' for advertising\n", 703 "Found HELLO from peer `%s' for advertising\n",
713 GNUNET_i2s (pid)); 704 GNUNET_i2s (&pid));
714 if (NULL != peer->filter) 705 if (NULL != peer->filter)
715 { 706 {
716 GNUNET_CONTAINER_bloomfilter_free (peer->filter); 707 GNUNET_CONTAINER_bloomfilter_free (peer->filter);
@@ -828,7 +819,7 @@ check_hello (void *cls, const struct GNUNET_MessageHeader *message)
828{ 819{
829 struct GNUNET_HELLO_Builder *builder = GNUNET_HELLO_builder_from_msg ( 820 struct GNUNET_HELLO_Builder *builder = GNUNET_HELLO_builder_from_msg (
830 message); 821 message);
831 struct GNUNET_PeerIdentity *pid = GNUNET_HELLO_builder_get_id (builder); 822 const struct GNUNET_PeerIdentity *pid = GNUNET_HELLO_builder_get_id (builder);
832 823
833 if (NULL == pid) 824 if (NULL == pid)
834 { 825 {
@@ -858,10 +849,8 @@ handle_hello (void *cls, const struct GNUNET_MessageHeader *message)
858{ 849{
859 struct GNUNET_PEERSTORE_StoreHelloContext *shc; 850 struct GNUNET_PEERSTORE_StoreHelloContext *shc;
860 const struct GNUNET_PeerIdentity *other = cls; 851 const struct GNUNET_PeerIdentity *other = cls;
861 struct Peer *peer;
862 struct GNUNET_HELLO_Builder *builder = GNUNET_HELLO_builder_from_msg ( 852 struct GNUNET_HELLO_Builder *builder = GNUNET_HELLO_builder_from_msg (
863 message); 853 message);
864 struct GNUNET_PeerIdentity *pid = GNUNET_HELLO_builder_get_id (builder);
865 854
866 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 855 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
867 "Received encrypted HELLO from peer `%s'", 856 "Received encrypted HELLO from peer `%s'",
@@ -871,6 +860,7 @@ handle_hello (void *cls, const struct GNUNET_MessageHeader *message)
871 1, 860 1,
872 GNUNET_NO); 861 GNUNET_NO);
873 GNUNET_HELLO_builder_from_msg (message); 862 GNUNET_HELLO_builder_from_msg (message);
863 // FIXME this is not working shc uninitialized
874 shc = GNUNET_PEERSTORE_hello_add (ps, message, &shc_cont, shc); 864 shc = GNUNET_PEERSTORE_hello_add (ps, message, &shc_cont, shc);
875 GNUNET_HELLO_builder_free (builder); 865 GNUNET_HELLO_builder_free (builder);
876} 866}