aboutsummaryrefslogtreecommitdiff
path: root/src/dht
diff options
context:
space:
mode:
Diffstat (limited to 'src/dht')
-rw-r--r--src/dht/.gitignore13
-rw-r--r--src/dht/Makefile.am229
-rw-r--r--src/dht/dht.conf.in35
-rw-r--r--src/dht/dht.h427
-rw-r--r--src/dht/dht_api.c1504
-rw-r--r--src/dht/dht_test_lib.c202
-rw-r--r--src/dht/dht_test_lib.h95
-rwxr-xr-xsrc/dht/dhtu_testbed_connect.sh31
-rw-r--r--src/dht/dhtu_testbed_deploy.conf26
-rwxr-xr-xsrc/dht/dhtu_testbed_deploy.sh84
-rw-r--r--src/dht/gnunet-dht-get.c330
-rw-r--r--src/dht/gnunet-dht-hello.c178
-rw-r--r--src/dht/gnunet-dht-monitor.c345
-rw-r--r--src/dht/gnunet-dht-put.c250
-rw-r--r--src/dht/gnunet-service-dht.c543
-rw-r--r--src/dht/gnunet-service-dht.h213
-rw-r--r--src/dht/gnunet-service-dht_clients.c1698
-rw-r--r--src/dht/gnunet-service-dht_datacache.c291
-rw-r--r--src/dht/gnunet-service-dht_datacache.h115
-rw-r--r--src/dht/gnunet-service-dht_neighbours.c2955
-rw-r--r--src/dht/gnunet-service-dht_neighbours.h226
-rw-r--r--src/dht/gnunet-service-dht_routing.c431
-rw-r--r--src/dht/gnunet-service-dht_routing.h87
-rw-r--r--src/dht/gnunet_dht_profiler.c1032
-rw-r--r--src/dht/plugin_block_dht.c408
-rw-r--r--src/dht/test_dht_2dtorus.conf37
-rw-r--r--src/dht/test_dht_api.c194
-rw-r--r--src/dht/test_dht_api_data.conf44
-rw-r--r--src/dht/test_dht_api_peer1.conf41
-rw-r--r--src/dht/test_dht_line.conf38
-rw-r--r--src/dht/test_dht_monitor.c439
-rw-r--r--src/dht/test_dht_monitor.conf39
-rw-r--r--src/dht/test_dht_multipeer.conf40
-rw-r--r--src/dht/test_dht_multipeer_topology.dat11
-rw-r--r--src/dht/test_dht_tools.conf157
-rw-r--r--src/dht/test_dht_tools.py.in149
-rwxr-xr-xsrc/dht/test_dht_tools.sh64
-rw-r--r--src/dht/test_dht_topo.c637
38 files changed, 0 insertions, 13638 deletions
diff --git a/src/dht/.gitignore b/src/dht/.gitignore
deleted file mode 100644
index bd8af1217..000000000
--- a/src/dht/.gitignore
+++ /dev/null
@@ -1,13 +0,0 @@
1gnunet-dht-get
2gnunet-dht-monitor
3gnunet-dht-profiler
4gnunet-dht-put
5gnunet-service-dht
6test_dht_2dtorus
7test_dht_api
8test_dht_line
9test_dht_monitor
10test_dht_multipeer
11test_dht_tools.py
12test_dht_twopeer
13gnunet-dht-hello
diff --git a/src/dht/Makefile.am b/src/dht/Makefile.am
deleted file mode 100644
index 124f95a77..000000000
--- a/src/dht/Makefile.am
+++ /dev/null
@@ -1,229 +0,0 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4plugindir = $(libdir)/gnunet
5
6pkgcfgdir= $(pkgdatadir)/config.d/
7
8libexecdir= $(pkglibdir)/libexec/
9
10pkgcfg_DATA = \
11 dht.conf
12
13if USE_COVERAGE
14 AM_CFLAGS = --coverage -O0
15 XLIB = -lgcov
16endif
17
18lib_LTLIBRARIES = \
19 libgnunetdht.la
20
21libgnunetdht_la_SOURCES = \
22 dht_api.c dht.h
23libgnunetdht_la_LIBADD = \
24 $(top_builddir)/src/util/libgnunetutil.la \
25 $(XLIB) \
26 $(LTLIBINTL)
27libgnunetdht_la_LDFLAGS = \
28 $(GN_LIB_LDFLAGS) \
29 -version-info 4:0:0
30
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/hello/libgnunethello.la \
39 $(top_builddir)/src/block/libgnunetblock.la \
40 $(top_builddir)/src/block/libgnunetblockgroup.la \
41 $(top_builddir)/src/util/libgnunetutil.la \
42 $(LTLIBINTL)
43libgnunet_plugin_block_dht_la_LDFLAGS = \
44 $(GN_PLUGIN_LDFLAGS)
45
46
47libexec_PROGRAMS = \
48 gnunet-service-dht
49
50bin_PROGRAMS = \
51 gnunet-dht-monitor \
52 gnunet-dht-get \
53 gnunet-dht-put \
54 gnunet-dht-hello
55
56noinst_PROGRAMS = \
57 gnunet-dht-profiler
58
59gnunet_service_dht_SOURCES = \
60 gnunet-service-dht.c gnunet-service-dht.h \
61 gnunet-service-dht_datacache.c gnunet-service-dht_datacache.h \
62 gnunet-service-dht_neighbours.c gnunet-service-dht_neighbours.h \
63 gnunet-service-dht_routing.c gnunet-service-dht_routing.h
64gnunet_service_dht_LDADD = \
65 libgnunetdht.la \
66 $(top_builddir)/src/statistics/libgnunetstatistics.la \
67 $(top_builddir)/src/core/libgnunetcore.la \
68 $(top_builddir)/src/nse/libgnunetnse.la \
69 $(top_builddir)/src/ats/libgnunetats.la \
70 $(top_builddir)/src/transport/libgnunettransport.la \
71 $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \
72 $(top_builddir)/src/hello/libgnunethello.la \
73 $(top_builddir)/src/block/libgnunetblock.la \
74 $(top_builddir)/src/block/libgnunetblockgroup.la \
75 $(top_builddir)/src/datacache/libgnunetdatacache.la \
76 $(top_builddir)/src/util/libgnunetutil.la \
77 -lm
78gnunet_service_dht_LDFLAGS = \
79 $(GN_LIBINTL)
80
81gnunet_dht_get_SOURCES = \
82 gnunet-dht-get.c
83gnunet_dht_get_LDADD = \
84 libgnunetdht.la \
85 $(top_builddir)/src/util/libgnunetutil.la
86gnunet_dht_get_LDFLAGS = \
87 $(GN_LIBINTL)
88
89gnunet_dht_hello_SOURCES = \
90 gnunet-dht-hello.c
91gnunet_dht_hello_LDADD = \
92 libgnunetdht.la \
93 $(top_builddir)/src/util/libgnunetutil.la
94gnunet_dht_hello_LDFLAGS = \
95 $(GN_LIBINTL)
96
97gnunet_dht_put_SOURCES = \
98 gnunet-dht-put.c
99gnunet_dht_put_LDADD = \
100 libgnunetdht.la \
101 $(top_builddir)/src/util/libgnunetutil.la
102gnunet_dht_put_LDFLAGS = \
103 $(GN_LIBINTL)
104
105gnunet_dht_monitor_SOURCES = \
106 gnunet-dht-monitor.c
107gnunet_dht_monitor_LDADD = \
108 libgnunetdht.la \
109 $(top_builddir)/src/util/libgnunetutil.la
110gnunet_dht_monitor_LDFLAGS = \
111 $(GN_LIBINTL)
112
113gnunet_dht_profiler_SOURCES = \
114 gnunet_dht_profiler.c
115gnunet_dht_profiler_LDADD = \
116 libgnunetdht.la \
117 $(top_builddir)/src/core/libgnunetcore.la \
118 $(top_builddir)/src/util/libgnunetutil.la \
119 $(top_builddir)/src/testing/libgnunettesting.la \
120 $(top_builddir)/src/testbed/libgnunettestbed.la
121gnunet_dht_profiler_LDFLAGS = \
122 $(GN_LIBINTL)
123
124noinst_LIBRARIES = libgnunetdhttest.a
125
126libgnunetdhttest_a_SOURCES = \
127 dht_test_lib.c dht_test_lib.h
128libgnunetdhttest_a_LIBADD = \
129 $(top_builddir)/src/util/libgnunetutil.la \
130 $(top_builddir)/src/testbed/libgnunettestbed.la \
131 libgnunetdht.la
132
133check_PROGRAMS = \
134 test_dht_api \
135 test_dht_twopeer \
136 test_dht_multipeer \
137 test_dht_line \
138 test_dht_2dtorus \
139 test_dht_monitor
140
141if HAVE_EXPERIMENTAL
142# These tests still do not work as testbed does
143# not support the respective topology op
144 NEW_TESTS = test_dht_2dtorus test_dht_multipeer
145endif
146
147if ENABLE_TEST_RUN
148AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
149TESTS = test_dht_api $(check_SCRIPTS) \
150 test_dht_twopeer \
151 test_dht_line \
152 test_dht_monitor \
153 $(NEW_TESTS)
154endif
155
156test_dht_api_SOURCES = \
157 test_dht_api.c
158test_dht_api_LDADD = \
159 $(top_builddir)/src/util/libgnunetutil.la \
160 $(top_builddir)/src/testing/libgnunettesting.la \
161 $(top_builddir)/src/hello/libgnunethello.la \
162 libgnunetdht.la
163
164test_dht_twopeer_SOURCES = \
165 test_dht_topo.c
166test_dht_twopeer_LDADD = \
167 libgnunetdhttest.a \
168 $(top_builddir)/src/util/libgnunetutil.la \
169 libgnunetdhttest.a \
170 $(top_builddir)/src/testbed/libgnunettestbed.la \
171 libgnunetdht.la
172
173test_dht_2dtorus_SOURCES = \
174 test_dht_topo.c
175test_dht_2dtorus_LDADD = \
176 libgnunetdhttest.a \
177 $(top_builddir)/src/util/libgnunetutil.la \
178 $(top_builddir)/src/testbed/libgnunettestbed.la \
179 libgnunetdht.la
180
181test_dht_line_SOURCES = \
182 test_dht_topo.c
183test_dht_line_LDADD = \
184 libgnunetdhttest.a \
185 $(top_builddir)/src/util/libgnunetutil.la \
186 $(top_builddir)/src/testbed/libgnunettestbed.la \
187 libgnunetdht.la
188
189test_dht_multipeer_SOURCES = \
190 test_dht_topo.c
191test_dht_multipeer_LDADD = \
192 libgnunetdhttest.a \
193 $(top_builddir)/src/util/libgnunetutil.la \
194 $(top_builddir)/src/statistics/libgnunetstatistics.la \
195 $(top_builddir)/src/testbed/libgnunettestbed.la \
196 libgnunetdht.la
197
198test_dht_monitor_SOURCES = \
199 test_dht_monitor.c
200test_dht_monitor_LDADD = \
201 libgnunetdhttest.a \
202 $(top_builddir)/src/util/libgnunetutil.la \
203 $(top_builddir)/src/testbed/libgnunettestbed.la \
204 libgnunetdht.la
205
206EXTRA_DIST = \
207 gnunet-service-dht_clients.c \
208 test_dht_api_data.conf \
209 test_dht_api_peer1.conf \
210 test_dht_monitor.conf \
211 test_dht_multipeer.conf \
212 test_dht_2dtorus.conf \
213 test_dht_line.conf \
214 test_dht_tools.conf \
215 test_dht_tools.py.in \
216 test_dht_multipeer_topology.dat \
217 dhtu_testbed_connect.sh \
218 dhtu_testbed_deploy.conf \
219 dhtu_testbed_deploy.sh
220
221if HAVE_PYTHON
222check_SCRIPTS = \
223 test_dht_tools.py
224endif
225
226SUFFIXES = .py.in .py
227.py.in.py:
228 $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/bin/dosubst.awk < $(srcdir)/$< > $@
229 chmod +x $@
diff --git a/src/dht/dht.conf.in b/src/dht/dht.conf.in
deleted file mode 100644
index 405ef1917..000000000
--- a/src/dht/dht.conf.in
+++ /dev/null
@@ -1,35 +0,0 @@
1[dht]
2IMMEDIATE_START = YES
3START_ON_DEMAND = @START_ON_DEMAND@
4@JAVAPORT@PORT = 2095
5HOSTNAME = localhost
6BINARY = gnunet-service-dht
7ACCEPT_FROM = 127.0.0.1;
8ACCEPT_FROM6 = ::1;
9BUCKET_SIZE = 4
10UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-dht.sock
11UNIX_MATCH_UID = NO
12UNIX_MATCH_GID = YES
13# DISABLE_SOCKET_FORWARDING = NO
14# USERNAME =
15# MAXBUF =
16# TIMEOUT =
17# DISABLEV6 =
18# BINDTO =
19# REJECT_FROM =
20# REJECT_FROM6 =
21# PREFIX =
22
23# Should the DHT cache results that we are routing in the DATACACHE as well?
24CACHE_RESULTS = YES
25
26# Special option to disable DHT calling 'try_connect' (for testing)
27DISABLE_TRY_CONNECT = NO
28
29
30[dhtcache]
31DATABASE = heap
32QUOTA = 50 MB
33
34# Disable RC-file for Bloom filter? (for benchmarking with limited IO availability)
35DISABLE_BF_RC = NO
diff --git a/src/dht/dht.h b/src/dht/dht.h
deleted file mode 100644
index 6a137defe..000000000
--- a/src/dht/dht.h
+++ /dev/null
@@ -1,427 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2003, 2004, 2009, 2011, 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 * @author Nathan Evans
24 * @file dht/dht.h
25 */
26
27#ifndef DHT_H
28#define DHT_H
29
30
31/**
32 * Size of the bloom filter the DHT uses to filter peers.
33 */
34#define DHT_BLOOM_SIZE 128
35
36
37GNUNET_NETWORK_STRUCT_BEGIN
38
39/**
40 * Message which indicates the DHT should cancel outstanding
41 * requests and discard any state.
42 */
43struct GNUNET_DHT_ClientGetStopMessage
44{
45 /**
46 * Type: #GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET_STOP
47 */
48 struct GNUNET_MessageHeader header;
49
50 /**
51 * Always zero.
52 */
53 uint32_t reserved GNUNET_PACKED;
54
55 /**
56 * Unique ID identifying this request
57 */
58 uint64_t unique_id GNUNET_PACKED;
59
60 /**
61 * Key of this request
62 */
63 struct GNUNET_HashCode key;
64};
65
66
67/**
68 * DHT GET message sent from clients to service. Indicates that a GET
69 * request should be issued.
70 */
71struct GNUNET_DHT_ClientGetMessage
72{
73 /**
74 * Type: #GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET
75 */
76 struct GNUNET_MessageHeader header;
77
78 /**
79 * Message options, actually an 'enum GNUNET_DHT_RouteOption' value.
80 */
81 uint32_t options GNUNET_PACKED;
82
83 /**
84 * Replication level for this message
85 */
86 uint32_t desired_replication_level GNUNET_PACKED;
87
88 /**
89 * The type for the data for the GET request; actually an 'enum
90 * GNUNET_BLOCK_Type'.
91 */
92 uint32_t type GNUNET_PACKED;
93
94 /**
95 * The key to search for
96 */
97 struct GNUNET_HashCode key GNUNET_PACKED;
98
99 /**
100 * Unique ID identifying this request, if 0 then
101 * the client will not expect a response
102 */
103 uint64_t unique_id GNUNET_PACKED;
104
105 /* Possibly followed by xquery, copied to end of this dealy do */
106};
107
108
109/**
110 * DHT GET RESULTS KNOWN message sent from clients to service. Indicates that a GET
111 * request should exclude certain results which are already known.
112 */
113struct GNUNET_DHT_ClientGetResultSeenMessage
114{
115 /**
116 * Type: #GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET_RESULTS_KNOWN
117 */
118 struct GNUNET_MessageHeader header;
119
120 /**
121 * Reserved, always 0.
122 */
123 uint32_t reserved GNUNET_PACKED;
124
125 /**
126 * The key we are searching for (to make it easy to find the corresponding
127 * GET inside the service).
128 */
129 struct GNUNET_HashCode key GNUNET_PACKED;
130
131 /**
132 * Unique ID identifying this request.
133 */
134 uint64_t unique_id GNUNET_PACKED;
135
136 /* Followed by an array of the hash codes of known results */
137};
138
139
140/**
141 * Reply to a GET send from the service to a client.
142 */
143struct GNUNET_DHT_ClientResultMessage
144{
145 /**
146 * Type: #GNUNET_MESSAGE_TYPE_DHT_CLIENT_RESULT
147 */
148 struct GNUNET_MessageHeader header;
149
150 /**
151 * The type for the data.
152 */
153 uint32_t type GNUNET_PACKED;
154
155 /**
156 * Reserved, always 0.
157 */
158 uint32_t reserved GNUNET_PACKED;
159
160 /**
161 * Message options, actually an 'enum GNUNET_DHT_RouteOption' value.
162 */
163 uint32_t options GNUNET_PACKED;
164
165 /**
166 * Number of peers recorded in the outgoing path from source to the
167 * storgage location of this message.
168 */
169 uint32_t put_path_length GNUNET_PACKED;
170
171 /**
172 * The number of peer identities recorded from the storage location
173 * to this peer.
174 */
175 uint32_t get_path_length GNUNET_PACKED;
176
177 /**
178 * Unique ID of the matching GET request.
179 */
180 uint64_t unique_id GNUNET_PACKED;
181
182 /**
183 * When does this entry expire?
184 */
185 struct GNUNET_TIME_AbsoluteNBO expiration;
186
187 /**
188 * The key that was searched for
189 */
190 struct GNUNET_HashCode key GNUNET_PACKED;
191
192 /* trunc_peer, put path, get path and actual data are copied to end of this dealy do */
193};
194
195
196/**
197 * Message to insert data into the DHT, sent from clients to DHT service.
198 */
199struct GNUNET_DHT_ClientPutMessage
200{
201 /**
202 * Type: #GNUNET_MESSAGE_TYPE_DHT_CLIENT_PUT
203 */
204 struct GNUNET_MessageHeader header;
205
206 /**
207 * The type of data to insert.
208 */
209 uint32_t type GNUNET_PACKED;
210
211 /**
212 * Message options, actually an 'enum GNUNET_DHT_RouteOption' value.
213 */
214 uint32_t options GNUNET_PACKED;
215
216 /**
217 * Replication level for this message
218 */
219 uint32_t desired_replication_level GNUNET_PACKED;
220
221 /**
222 * How long should this data persist?
223 */
224 struct GNUNET_TIME_AbsoluteNBO expiration;
225
226 /**
227 * The key to store the value under.
228 */
229 struct GNUNET_HashCode key GNUNET_PACKED;
230
231 /* DATA copied to end of this message */
232};
233
234
235/**
236 * Message to monitor put requests going through peer, DHT service -> clients.
237 */
238struct GNUNET_DHT_MonitorPutMessage
239{
240 /**
241 * Type: #GNUNET_MESSAGE_TYPE_DHT_MONITOR_PUT
242 */
243 struct GNUNET_MessageHeader header;
244
245 /**
246 * Message options, actually an 'enum GNUNET_DHT_RouteOption' value.
247 */
248 uint32_t options GNUNET_PACKED;
249
250 /**
251 * The type of data in the request.
252 */
253 uint32_t type GNUNET_PACKED;
254
255 /**
256 * Hop count so far.
257 */
258 uint32_t hop_count GNUNET_PACKED;
259
260 /**
261 * Replication level for this message
262 */
263 uint32_t desired_replication_level GNUNET_PACKED;
264
265 /**
266 * Number of peers recorded in the outgoing path from source to the
267 * storage location of this message.
268 */
269 uint32_t put_path_length GNUNET_PACKED;
270
271 /**
272 * How long should this data persist?
273 */
274 struct GNUNET_TIME_AbsoluteNBO expiration_time;
275
276 /**
277 * The key to store the value under.
278 */
279 struct GNUNET_HashCode key GNUNET_PACKED;
280
281 /* put path (if tracked) */
282
283 /* Payload */
284};
285
286
287/**
288 * Message to request monitoring messages, clients -> DHT service.
289 */
290struct GNUNET_DHT_MonitorStartStopMessage
291{
292 /**
293 * Type: #GNUNET_MESSAGE_TYPE_DHT_MONITOR_START or
294 * #GNUNET_MESSAGE_TYPE_DHT_MONITOR_STOP
295 */
296 struct GNUNET_MessageHeader header;
297
298 /**
299 * The type of data desired, GNUNET_BLOCK_TYPE_ANY for all.
300 */
301 uint32_t type GNUNET_PACKED;
302
303 /**
304 * Flag whether to notify about GET messages.
305 */
306 int16_t get GNUNET_PACKED;
307
308 /**
309 * Flag whether to notify about GET_REPONSE messages.
310 */
311 int16_t get_resp GNUNET_PACKED;
312
313 /**
314 * Flag whether to notify about PUT messages.
315 */
316 int16_t put GNUNET_PACKED;
317
318 /**
319 * Flag whether to use the provided key to filter messages.
320 */
321 int16_t filter_key GNUNET_PACKED;
322
323 /**
324 * The key to filter messages by.
325 */
326 struct GNUNET_HashCode key GNUNET_PACKED;
327};
328
329
330/**
331 * Message to monitor get requests going through peer, DHT service -> clients.
332 */
333struct GNUNET_DHT_MonitorGetMessage
334{
335 /**
336 * Type: #GNUNET_MESSAGE_TYPE_DHT_MONITOR_GET
337 */
338 struct GNUNET_MessageHeader header;
339
340 /**
341 * Message options, actually an 'enum GNUNET_DHT_RouteOption' value.
342 */
343 uint32_t options GNUNET_PACKED;
344
345 /**
346 * The type of data in the request.
347 */
348 uint32_t type GNUNET_PACKED;
349
350 /**
351 * Hop count
352 */
353 uint32_t hop_count GNUNET_PACKED;
354
355 /**
356 * Replication level for this message
357 */
358 uint32_t desired_replication_level GNUNET_PACKED;
359
360 /**
361 * Always zero.
362 */
363 uint32_t reserved GNUNET_PACKED;
364
365 /**
366 * The key to store the value under.
367 */
368 struct GNUNET_HashCode key GNUNET_PACKED;
369
370 /* get path (if tracked) */
371};
372
373/**
374 * Message to monitor get results going through peer, DHT service -> clients.
375 */
376struct GNUNET_DHT_MonitorGetRespMessage
377{
378 /**
379 * Type: #GNUNET_MESSAGE_TYPE_DHT_P2P_RESULT
380 */
381 struct GNUNET_MessageHeader header;
382
383 /**
384 * Content type.
385 */
386 uint32_t type GNUNET_PACKED;
387
388 /**
389 * Reserved, always 0.
390 */
391 uint32_t reserved GNUNET_PACKED;
392
393 /**
394 * Message options, actually an 'enum GNUNET_DHT_RouteOption' value.
395 */
396 uint32_t options GNUNET_PACKED;
397
398 /**
399 * Length of the PUT path that follows (if tracked).
400 */
401 uint32_t put_path_length GNUNET_PACKED;
402
403 /**
404 * Length of the GET path that follows (if tracked).
405 */
406 uint32_t get_path_length GNUNET_PACKED;
407
408 /**
409 * When does the content expire?
410 */
411 struct GNUNET_TIME_AbsoluteNBO expiration_time;
412
413 /**
414 * The key of the corresponding GET request.
415 */
416 struct GNUNET_HashCode key GNUNET_PACKED;
417
418 /* put path (if tracked) */
419
420 /* get path (if tracked) */
421
422 /* Payload */
423};
424
425GNUNET_NETWORK_STRUCT_END
426
427#endif
diff --git a/src/dht/dht_api.c b/src/dht/dht_api.c
deleted file mode 100644
index 5fa8f759f..000000000
--- a/src/dht/dht_api.c
+++ /dev/null
@@ -1,1504 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2012, 2016, 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 dht/dht_api.c
23 * @brief library to access the DHT service
24 * @author Christian Grothoff
25 * @author Nathan Evans
26 */
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_constants.h"
31#include "gnunet_signatures.h"
32#include "gnunet_arm_service.h"
33#include "gnunet_hello_lib.h"
34#include "gnunet_protocols.h"
35#include "gnunet_dht_service.h"
36#include "dht.h"
37
38#define LOG(kind, ...) GNUNET_log_from (kind, "dht-api", __VA_ARGS__)
39
40
41/**
42 * Handle to a PUT request.
43 */
44struct GNUNET_DHT_PutHandle
45{
46 /**
47 * Kept in a DLL.
48 */
49 struct GNUNET_DHT_PutHandle *next;
50
51 /**
52 * Kept in a DLL.
53 */
54 struct GNUNET_DHT_PutHandle *prev;
55
56 /**
57 * Continuation to call when done.
58 */
59 GNUNET_SCHEDULER_TaskCallback cont;
60
61 /**
62 * Main handle to this DHT api
63 */
64 struct GNUNET_DHT_Handle *dht_handle;
65
66 /**
67 * Closure for @e cont.
68 */
69 void *cont_cls;
70
71 /**
72 * Envelope from the PUT operation.
73 */
74 struct GNUNET_MQ_Envelope *env;
75};
76
77/**
78 * Handle to a GET request
79 */
80struct GNUNET_DHT_GetHandle
81{
82 /**
83 * Iterator to call on data receipt
84 */
85 GNUNET_DHT_GetIterator iter;
86
87 /**
88 * Closure for @e iter.
89 */
90 void *iter_cls;
91
92 /**
93 * Main handle to this DHT api
94 */
95 struct GNUNET_DHT_Handle *dht_handle;
96
97 /**
98 * Array of hash codes over the results that we have already
99 * seen.
100 */
101 struct GNUNET_HashCode *seen_results;
102
103 /**
104 * Key that this get request is for
105 */
106 struct GNUNET_HashCode key;
107
108 /**
109 * Unique identifier for this request (for key collisions).
110 */
111 uint64_t unique_id;
112
113 /**
114 * Size of the extended query, allocated at the end of this struct.
115 */
116 size_t xquery_size;
117
118 /**
119 * Desired replication level.
120 */
121 uint32_t desired_replication_level;
122
123 /**
124 * Type of the block we are looking for.
125 */
126 enum GNUNET_BLOCK_Type type;
127
128 /**
129 * Routing options.
130 */
131 enum GNUNET_DHT_RouteOption options;
132
133 /**
134 * Size of the @e seen_results array. Note that not
135 * all positions might be used (as we over-allocate).
136 */
137 unsigned int seen_results_size;
138
139 /**
140 * Offset into the @e seen_results array marking the
141 * end of the positions that are actually used.
142 */
143 unsigned int seen_results_end;
144};
145
146
147/**
148 * Handle to a monitoring request.
149 */
150struct GNUNET_DHT_MonitorHandle
151{
152 /**
153 * DLL.
154 */
155 struct GNUNET_DHT_MonitorHandle *next;
156
157 /**
158 * DLL.
159 */
160 struct GNUNET_DHT_MonitorHandle *prev;
161
162 /**
163 * Main handle to this DHT api.
164 */
165 struct GNUNET_DHT_Handle *dht_handle;
166
167 /**
168 * Type of block looked for.
169 */
170 enum GNUNET_BLOCK_Type type;
171
172 /**
173 * Key being looked for, NULL == all.
174 */
175 struct GNUNET_HashCode *key;
176
177 /**
178 * Callback for each received message of type get.
179 */
180 GNUNET_DHT_MonitorGetCB get_cb;
181
182 /**
183 * Callback for each received message of type get response.
184 */
185 GNUNET_DHT_MonitorGetRespCB get_resp_cb;
186
187 /**
188 * Callback for each received message of type put.
189 */
190 GNUNET_DHT_MonitorPutCB put_cb;
191
192 /**
193 * Closure for @e get_cb, @e put_cb and @e get_resp_cb.
194 */
195 void *cb_cls;
196};
197
198
199/**
200 * Handle to get a HELLO URL from the DHT for manual bootstrapping.
201 */
202struct GNUNET_DHT_HelloGetHandle
203{
204
205 /**
206 * DLL.
207 */
208 struct GNUNET_DHT_HelloGetHandle *next;
209
210 /**
211 * DLL.
212 */
213 struct GNUNET_DHT_HelloGetHandle *prev;
214
215 /**
216 * Function to call with the result.
217 */
218 GNUNET_DHT_HelloGetCallback cb;
219
220 /**
221 * Closure for @a cb.
222 */
223 void *cb_cls;
224
225 /**
226 * Connection to the DHT service.
227 */
228 struct GNUNET_DHT_Handle *dht_handle;
229
230};
231
232
233/**
234 * Connection to the DHT service.
235 */
236struct GNUNET_DHT_Handle
237{
238 /**
239 * Configuration to use.
240 */
241 const struct GNUNET_CONFIGURATION_Handle *cfg;
242
243 /**
244 * Connection to DHT service.
245 */
246 struct GNUNET_MQ_Handle *mq;
247
248 /**
249 * Head of linked list of messages we would like to monitor.
250 */
251 struct GNUNET_DHT_MonitorHandle *monitor_head;
252
253 /**
254 * Tail of linked list of messages we would like to monitor.
255 */
256 struct GNUNET_DHT_MonitorHandle *monitor_tail;
257
258 /**
259 * Head of active PUT requests.
260 */
261 struct GNUNET_DHT_PutHandle *put_head;
262
263 /**
264 * Tail of active PUT requests.
265 */
266 struct GNUNET_DHT_PutHandle *put_tail;
267
268 /**
269 * DLL.
270 */
271 struct GNUNET_DHT_HelloGetHandle *hgh_head;
272
273 /**
274 * DLL.
275 */
276 struct GNUNET_DHT_HelloGetHandle *hgh_tail;
277
278 /**
279 * Hash map containing the current outstanding unique GET requests
280 * (values are of type `struct GNUNET_DHT_GetHandle`).
281 */
282 struct GNUNET_CONTAINER_MultiHashMap *active_requests;
283
284 /**
285 * Task for trying to reconnect.
286 */
287 struct GNUNET_SCHEDULER_Task *reconnect_task;
288
289 /**
290 * How quickly should we retry? Used for exponential back-off on
291 * connect-errors.
292 */
293 struct GNUNET_TIME_Relative retry_time;
294
295 /**
296 * Generator for unique ids.
297 */
298 uint64_t uid_gen;
299};
300
301
302/**
303 * Try to (re)connect to the DHT service.
304 *
305 * @param h DHT handle to reconnect
306 * @return #GNUNET_YES on success, #GNUNET_NO on failure.
307 */
308static enum GNUNET_GenericReturnValue
309try_connect (struct GNUNET_DHT_Handle *h);
310
311
312/**
313 * Send GET message for a @a get_handle to DHT.
314 *
315 * @param gh GET to generate messages for.
316 */
317static void
318send_get (struct GNUNET_DHT_GetHandle *gh)
319{
320 struct GNUNET_DHT_Handle *h = gh->dht_handle;
321 struct GNUNET_MQ_Envelope *env;
322 struct GNUNET_DHT_ClientGetMessage *get_msg;
323
324 env = GNUNET_MQ_msg_extra (get_msg,
325 gh->xquery_size,
326 GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET);
327 get_msg->options = htonl ((uint32_t) gh->options);
328 get_msg->desired_replication_level = htonl (gh->desired_replication_level);
329 get_msg->type = htonl (gh->type);
330 get_msg->key = gh->key;
331 get_msg->unique_id = gh->unique_id;
332 GNUNET_memcpy (&get_msg[1],
333 &gh[1],
334 gh->xquery_size);
335 GNUNET_MQ_send (h->mq,
336 env);
337}
338
339
340/**
341 * Send GET message(s) for indicating which results are already known
342 * for a @a get_handle to DHT. Complex as we need to send the list of
343 * known results, which means we may need multiple messages to block
344 * known results from the result set.
345 *
346 * @param gh GET to generate messages for
347 * @param transmission_offset_start at which offset should we start?
348 */
349static void
350send_get_known_results (struct GNUNET_DHT_GetHandle *gh,
351 unsigned int transmission_offset_start)
352{
353 struct GNUNET_DHT_Handle *h = gh->dht_handle;
354 struct GNUNET_MQ_Envelope *env;
355 struct GNUNET_DHT_ClientGetResultSeenMessage *msg;
356 unsigned int delta;
357 unsigned int max;
358 unsigned int transmission_offset;
359
360 max = (GNUNET_MAX_MESSAGE_SIZE - sizeof(*msg))
361 / sizeof(struct GNUNET_HashCode);
362 transmission_offset = transmission_offset_start;
363 while (transmission_offset < gh->seen_results_end)
364 {
365 delta = gh->seen_results_end - transmission_offset;
366 if (delta > max)
367 delta = max;
368 env = GNUNET_MQ_msg_extra (msg,
369 delta * sizeof(struct GNUNET_HashCode),
370 GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET_RESULTS_KNOWN);
371 msg->key = gh->key;
372 msg->unique_id = gh->unique_id;
373 GNUNET_memcpy (&msg[1],
374 &gh->seen_results[transmission_offset],
375 sizeof(struct GNUNET_HashCode) * delta);
376 GNUNET_MQ_send (h->mq,
377 env);
378 transmission_offset += delta;
379 }
380}
381
382
383/**
384 * Add the GET request corresponding to the given route handle
385 * to the pending queue (if it is not already in there).
386 *
387 * @param cls the `struct GNUNET_DHT_Handle *`
388 * @param key key for the request (not used)
389 * @param value the `struct GNUNET_DHT_GetHandle *`
390 * @return #GNUNET_YES (always)
391 */
392static enum GNUNET_GenericReturnValue
393add_get_request_to_pending (void *cls,
394 const struct GNUNET_HashCode *key,
395 void *value)
396{
397 struct GNUNET_DHT_Handle *handle = cls;
398 struct GNUNET_DHT_GetHandle *gh = value;
399
400 LOG (GNUNET_ERROR_TYPE_DEBUG,
401 "Retransmitting request related to %s to DHT %p\n",
402 GNUNET_h2s (key),
403 handle);
404 send_get (gh);
405 send_get_known_results (gh, 0);
406 return GNUNET_YES;
407}
408
409
410/**
411 * Send #GNUNET_MESSAGE_TYPE_DHT_MONITOR_START message.
412 *
413 * @param mh monitor handle to generate start message for
414 */
415static void
416send_monitor_start (struct GNUNET_DHT_MonitorHandle *mh)
417{
418 struct GNUNET_DHT_Handle *h = mh->dht_handle;
419 struct GNUNET_MQ_Envelope *env;
420 struct GNUNET_DHT_MonitorStartStopMessage *m;
421
422 env = GNUNET_MQ_msg (m,
423 GNUNET_MESSAGE_TYPE_DHT_MONITOR_START);
424 m->type = htonl (mh->type);
425 m->get = htons (NULL != mh->get_cb);
426 m->get_resp = htons (NULL != mh->get_resp_cb);
427 m->put = htons (NULL != mh->put_cb);
428 if (NULL != mh->key)
429 {
430 m->filter_key = htons (1);
431 m->key = *mh->key;
432 }
433 GNUNET_MQ_send (h->mq,
434 env);
435}
436
437
438/**
439 * Try reconnecting to the dht service.
440 *
441 * @param cls a `struct GNUNET_DHT_Handle`
442 */
443static void
444try_reconnect (void *cls)
445{
446 struct GNUNET_DHT_Handle *h = cls;
447 struct GNUNET_DHT_MonitorHandle *mh;
448
449 LOG (GNUNET_ERROR_TYPE_DEBUG,
450 "Reconnecting with DHT %p\n",
451 h);
452 h->retry_time = GNUNET_TIME_STD_BACKOFF (h->retry_time);
453 h->reconnect_task = NULL;
454 if (GNUNET_YES != try_connect (h))
455 {
456 LOG (GNUNET_ERROR_TYPE_WARNING,
457 "DHT reconnect failed!\n");
458 h->reconnect_task
459 = GNUNET_SCHEDULER_add_delayed (h->retry_time,
460 &try_reconnect,
461 h);
462 return;
463 }
464 GNUNET_CONTAINER_multihashmap_iterate (h->active_requests,
465 &add_get_request_to_pending,
466 h);
467 for (mh = h->monitor_head; NULL != mh; mh = mh->next)
468 send_monitor_start (mh);
469}
470
471
472/**
473 * Try reconnecting to the DHT service.
474 *
475 * @param h handle to dht to (possibly) disconnect and reconnect
476 */
477static void
478do_disconnect (struct GNUNET_DHT_Handle *h)
479{
480 struct GNUNET_DHT_PutHandle *ph;
481 GNUNET_SCHEDULER_TaskCallback cont;
482 void *cont_cls;
483
484 if (NULL == h->mq)
485 return;
486 GNUNET_MQ_destroy (h->mq);
487 h->mq = NULL;
488 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
489 "Disconnecting from DHT service, will try to reconnect in %s\n",
490 GNUNET_STRINGS_relative_time_to_string (h->retry_time,
491 GNUNET_YES));
492 /* notify client about all PUTs that (may) have failed due to disconnect */
493 while (NULL != (ph = h->put_head))
494 {
495 cont = ph->cont;
496 cont_cls = ph->cont_cls;
497 ph->env = NULL;
498 GNUNET_DHT_put_cancel (ph);
499 if (NULL != cont)
500 cont (cont_cls);
501 }
502 GNUNET_assert (NULL == h->reconnect_task);
503 h->reconnect_task
504 = GNUNET_SCHEDULER_add_delayed (h->retry_time,
505 &try_reconnect,
506 h);
507}
508
509
510/**
511 * Generic error handler, called with the appropriate error code and
512 * the same closure specified at the creation of the message queue.
513 * Not every message queue implementation supports an error handler.
514 *
515 * @param cls closure with the `struct GNUNET_DHT_Handle *`
516 * @param error error code
517 */
518static void
519mq_error_handler (void *cls,
520 enum GNUNET_MQ_Error error)
521{
522 struct GNUNET_DHT_Handle *h = cls;
523
524 do_disconnect (h);
525}
526
527
528/**
529 * Process a get monitor message from the service.
530 *
531 * @param cls The DHT handle.
532 * @param msg Monitor get message from the service.
533 */
534static void
535handle_monitor_get (void *cls,
536 const struct GNUNET_DHT_MonitorGetMessage *msg)
537{
538 struct GNUNET_DHT_Handle *handle = cls;
539 enum GNUNET_DHT_RouteOption ro
540 = (enum GNUNET_DHT_RouteOption) ntohl (msg->options);
541
542 for (struct GNUNET_DHT_MonitorHandle *mh = handle->monitor_head;
543 NULL != mh;
544 mh = mh->next)
545 {
546 if (NULL == mh->get_cb)
547 continue;
548 if ( (GNUNET_BLOCK_TYPE_ANY != mh->type) &&
549 (mh->type != ntohl (msg->type)))
550 continue;
551 if ( (NULL != mh->key) &&
552 (0 != GNUNET_memcmp (mh->key,
553 &msg->key)) )
554 continue;
555 mh->get_cb (mh->cb_cls,
556 ro,
557 (enum GNUNET_BLOCK_Type) ntohl (msg->type),
558 ntohl (msg->hop_count),
559 ntohl (msg->desired_replication_level),
560 &msg->key);
561 }
562}
563
564
565/**
566 * Validate a get response monitor message from the service.
567 *
568 * @param cls The DHT handle.
569 * @param msg monitor get response message from the service
570 * @return #GNUNET_OK if everything went fine,
571 * #GNUNET_SYSERR if the message is malformed.
572 */
573static enum GNUNET_GenericReturnValue
574check_monitor_get_resp (void *cls,
575 const struct GNUNET_DHT_MonitorGetRespMessage *msg)
576{
577 size_t msize = ntohs (msg->header.size) - sizeof(*msg);
578 uint32_t getl = ntohl (msg->get_path_length);
579 uint32_t putl = ntohl (msg->put_path_length);
580 enum GNUNET_DHT_RouteOption ro
581 = (enum GNUNET_DHT_RouteOption) ntohl (msg->options);
582 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
583
584 if (truncated)
585 {
586 if (msize < sizeof (struct GNUNET_PeerIdentity))
587 {
588 GNUNET_break (0);
589 return GNUNET_SYSERR;
590 }
591 msize -= sizeof (struct GNUNET_PeerIdentity);
592 }
593 if ((getl + putl < getl) ||
594 ((msize / sizeof(struct GNUNET_DHT_PathElement)) < getl + putl))
595 {
596 GNUNET_break (0);
597 return GNUNET_SYSERR;
598 }
599 return GNUNET_OK;
600}
601
602
603/**
604 * Process a get response monitor message from the service.
605 *
606 * @param cls The DHT handle.
607 * @param msg monitor get response message from the service
608 */
609static void
610handle_monitor_get_resp (void *cls,
611 const struct GNUNET_DHT_MonitorGetRespMessage *msg)
612{
613 struct GNUNET_DHT_Handle *handle = cls;
614 size_t msize = ntohs (msg->header.size) - sizeof(*msg);
615 enum GNUNET_DHT_RouteOption ro
616 = (enum GNUNET_DHT_RouteOption) ntohl (msg->options);
617 uint32_t getl = ntohl (msg->get_path_length);
618 uint32_t putl = ntohl (msg->put_path_length);
619 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
620 const struct GNUNET_PeerIdentity *trunc_peer
621 = truncated
622 ? (const struct GNUNET_PeerIdentity *) &msg[1]
623 : NULL;
624 const struct GNUNET_DHT_PathElement *path
625 = truncated
626 ? (const struct GNUNET_DHT_PathElement *) &trunc_peer[1]
627 : (const struct GNUNET_DHT_PathElement *) &msg[1];
628
629 if (truncated)
630 msize -= sizeof (struct GNUNET_PeerIdentity);
631 msize -= sizeof(struct GNUNET_DHT_PathElement) * (putl + getl);
632 for (struct GNUNET_DHT_MonitorHandle *mh = handle->monitor_head;
633 NULL != mh;
634 mh = mh->next)
635 {
636 if (NULL == mh->get_resp_cb)
637 continue;
638 if ( (GNUNET_BLOCK_TYPE_ANY != mh->type) &&
639 (mh->type != ntohl (msg->type)) )
640 continue;
641 if ( (NULL != mh->key) &&
642 (0 != GNUNET_memcmp (mh->key,
643 &msg->key)) )
644 continue;
645 mh->get_resp_cb (mh->cb_cls,
646 (enum GNUNET_BLOCK_Type) ntohl (msg->type),
647 trunc_peer,
648 &path[putl],
649 getl,
650 path,
651 putl,
652 GNUNET_TIME_absolute_ntoh (msg->expiration_time),
653 &msg->key,
654 (const void *) &path[getl + putl],
655 msize);
656 }
657}
658
659
660/**
661 * Check validity of a put monitor message from the service.
662 *
663 * @param cls The DHT handle.
664 * @param msg Monitor put message from the service.
665 * @return #GNUNET_OK if everything went fine,
666 * #GNUNET_SYSERR if the message is malformed.
667 */
668static enum GNUNET_GenericReturnValue
669check_monitor_put (void *cls,
670 const struct GNUNET_DHT_MonitorPutMessage *msg)
671{
672 size_t msize = ntohs (msg->header.size) - sizeof(*msg);
673 uint32_t putl = ntohl (msg->put_path_length);
674 enum GNUNET_DHT_RouteOption ro
675 = (enum GNUNET_DHT_RouteOption) ntohl (msg->options);
676 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
677
678 if (truncated)
679 {
680 if (msize < sizeof (struct GNUNET_PeerIdentity))
681 {
682 GNUNET_break (0);
683 return GNUNET_SYSERR;
684 }
685 msize -= sizeof (struct GNUNET_PeerIdentity);
686 }
687 if ((msize / sizeof(struct GNUNET_DHT_PathElement)) < putl)
688 {
689 GNUNET_break (0);
690 return GNUNET_SYSERR;
691 }
692 return GNUNET_OK;
693}
694
695
696/**
697 * Process a put monitor message from the service.
698 *
699 * @param cls The DHT handle.
700 * @param msg Monitor put message from the service.
701 */
702static void
703handle_monitor_put (void *cls,
704 const struct GNUNET_DHT_MonitorPutMessage *msg)
705{
706 struct GNUNET_DHT_Handle *handle = cls;
707 size_t msize = ntohs (msg->header.size) - sizeof(*msg);
708 uint32_t putl = ntohl (msg->put_path_length);
709 enum GNUNET_DHT_RouteOption ro
710 = (enum GNUNET_DHT_RouteOption) ntohl (msg->options);
711 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
712 const struct GNUNET_PeerIdentity *trunc_peer
713 = truncated
714 ? (const struct GNUNET_PeerIdentity *) &msg[1]
715 : NULL;
716 const struct GNUNET_DHT_PathElement *path
717 = truncated
718 ? (const struct GNUNET_DHT_PathElement *) &trunc_peer[1]
719 : (const struct GNUNET_DHT_PathElement *) &msg[1];
720
721 if (truncated)
722 msize -= sizeof (struct GNUNET_PeerIdentity);
723 msize -= sizeof(struct GNUNET_DHT_PathElement) * putl;
724 for (struct GNUNET_DHT_MonitorHandle *mh = handle->monitor_head;
725 NULL != mh;
726 mh = mh->next)
727 {
728 if (NULL == mh->put_cb)
729 continue;
730 if ( (GNUNET_BLOCK_TYPE_ANY != mh->type) &&
731 (mh->type != ntohl (msg->type)) )
732 continue;
733 if ( (NULL != mh->key) &&
734 (0 != GNUNET_memcmp (mh->key,
735 &msg->key)) )
736 continue;
737 mh->put_cb (mh->cb_cls,
738 ro,
739 (enum GNUNET_BLOCK_Type) ntohl (msg->type),
740 ntohl (msg->hop_count),
741 ntohl (msg->desired_replication_level),
742 trunc_peer,
743 putl,
744 path,
745 GNUNET_TIME_absolute_ntoh (msg->expiration_time),
746 &msg->key,
747 (const void *) &path[putl],
748 msize);
749 }
750}
751
752
753/**
754 * Verify that client result message received from the service is well-formed.
755 *
756 * @param cls The DHT handle.
757 * @param msg Monitor put message from the service.
758 * @return #GNUNET_OK if everything went fine,
759 * #GNUNET_SYSERR if the message is malformed.
760 */
761static enum GNUNET_GenericReturnValue
762check_client_result (void *cls,
763 const struct GNUNET_DHT_ClientResultMessage *msg)
764{
765 size_t msize = ntohs (msg->header.size) - sizeof(*msg);
766 uint32_t put_path_length = ntohl (msg->put_path_length);
767 uint32_t get_path_length = ntohl (msg->get_path_length);
768 enum GNUNET_DHT_RouteOption ro
769 = (enum GNUNET_DHT_RouteOption) ntohl (msg->options);
770 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
771 size_t meta_length;
772
773 if (truncated)
774 {
775 if (msize < sizeof (struct GNUNET_PeerIdentity))
776 {
777 GNUNET_break (0);
778 return GNUNET_SYSERR;
779 }
780 msize -= sizeof (struct GNUNET_PeerIdentity);
781 }
782 meta_length = msize / sizeof(struct GNUNET_DHT_PathElement);
783 if ( (get_path_length + put_path_length >
784 meta_length) ||
785 (get_path_length + put_path_length <
786 get_path_length) )
787 {
788 GNUNET_break (0);
789 return GNUNET_SYSERR;
790 }
791 return GNUNET_OK;
792}
793
794
795/**
796 * Process a given reply that might match the given request.
797 *
798 * @param cls the `struct GNUNET_DHT_ClientResultMessage`
799 * @param key query of the request
800 * @param value the `struct GNUNET_DHT_GetHandle` of a request matching the same key
801 * @return #GNUNET_YES to continue to iterate over all results
802 */
803static enum GNUNET_GenericReturnValue
804process_client_result (void *cls,
805 const struct GNUNET_HashCode *key,
806 void *value)
807{
808 const struct GNUNET_DHT_ClientResultMessage *crm = cls;
809 struct GNUNET_DHT_GetHandle *get_handle = value;
810 size_t msize = ntohs (crm->header.size) - sizeof(*crm);
811 uint16_t type = ntohl (crm->type);
812 enum GNUNET_DHT_RouteOption ro
813 = (enum GNUNET_DHT_RouteOption) ntohl (crm->options);
814 bool truncated
815 = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
816 uint32_t put_path_length
817 = ntohl (crm->put_path_length);
818 uint32_t get_path_length
819 = ntohl (crm->get_path_length);
820 const struct GNUNET_PeerIdentity *trunc_peer
821 = truncated
822 ? (const struct GNUNET_PeerIdentity *) &crm[1]
823 : NULL;
824 const struct GNUNET_DHT_PathElement *put_path
825 = truncated
826 ? (const struct GNUNET_DHT_PathElement *) &trunc_peer[1]
827 : (const struct GNUNET_DHT_PathElement *) &crm[1];
828 const struct GNUNET_DHT_PathElement *get_path
829 = &put_path[put_path_length];
830 const void *data
831 = &get_path[get_path_length];
832 size_t meta_length
833 = sizeof(struct GNUNET_DHT_PathElement)
834 * (get_path_length + put_path_length);
835 size_t data_length
836 = msize - meta_length;
837 struct GNUNET_HashCode hc;
838
839 if (truncated)
840 data_length -= sizeof (struct GNUNET_PeerIdentity);
841 if (crm->unique_id != get_handle->unique_id)
842 {
843 /* UID mismatch */
844 LOG (GNUNET_ERROR_TYPE_DEBUG,
845 "Ignoring reply for %s: UID mismatch: %llu/%llu\n",
846 GNUNET_h2s (key),
847 (unsigned long long) crm->unique_id,
848 (unsigned long long) get_handle->unique_id);
849 return GNUNET_YES;
850 }
851 if ( (get_handle->type != GNUNET_BLOCK_TYPE_ANY) &&
852 (get_handle->type != type) )
853 {
854 /* type mismatch */
855 GNUNET_break (0);
856 return GNUNET_YES;
857 }
858
859 {
860 char *pp;
861 char *gp;
862
863 gp = GNUNET_DHT_pp2s (get_path,
864 get_path_length);
865 pp = GNUNET_DHT_pp2s (put_path,
866 put_path_length);
867 LOG (GNUNET_ERROR_TYPE_DEBUG,
868 "Giving %u byte reply for %s to application (GP: %s, PP: %s)\n",
869 (unsigned int) data_length,
870 GNUNET_h2s (key),
871 gp,
872 pp);
873 GNUNET_free (gp);
874 GNUNET_free (pp);
875 }
876 /* remember that we've seen this result */
877 GNUNET_CRYPTO_hash (data,
878 data_length,
879 &hc);
880 if (get_handle->seen_results_size == get_handle->seen_results_end)
881 GNUNET_array_grow (get_handle->seen_results,
882 get_handle->seen_results_size,
883 get_handle->seen_results_size * 2 + 1);
884 get_handle->seen_results[get_handle->seen_results_end++] = hc;
885 /* no need to block it explicitly, service already knows about it! */
886 get_handle->iter (get_handle->iter_cls,
887 GNUNET_TIME_absolute_ntoh (crm->expiration),
888 key,
889 trunc_peer,
890 get_path,
891 get_path_length,
892 put_path,
893 put_path_length,
894 type,
895 data_length,
896 data);
897 return GNUNET_YES;
898}
899
900
901/**
902 * Process a client result message received from the service.
903 *
904 * @param cls The DHT handle.
905 * @param msg Monitor put message from the service.
906 */
907static void
908handle_client_result (void *cls,
909 const struct GNUNET_DHT_ClientResultMessage *msg)
910{
911 struct GNUNET_DHT_Handle *handle = cls;
912
913 GNUNET_CONTAINER_multihashmap_get_multiple (handle->active_requests,
914 &msg->key,
915 &process_client_result,
916 (void *) msg);
917}
918
919
920/**
921 * Process a client HELLO message received from the service.
922 *
923 * @param cls The DHT handle.
924 * @param hdr HELLO URL message from the service.
925 * @return #GNUNET_OK if @a hdr is well-formed
926 */
927static enum GNUNET_GenericReturnValue
928check_client_hello (void *cls,
929 const struct GNUNET_MessageHeader *hdr)
930{
931 uint16_t len = ntohs (hdr->size);
932 const char *buf = (const char *) &hdr[1];
933
934 (void) cls;
935 if ('\0' != buf[len - sizeof (*hdr) - 1])
936 {
937 GNUNET_break (0);
938 return GNUNET_SYSERR;
939 }
940 return GNUNET_OK;
941}
942
943
944/**
945 * Process a client HELLO message received from the service.
946 *
947 * @param cls The DHT handle.
948 * @param hdr HELLO URL message from the service.
949 */
950static void
951handle_client_hello (void *cls,
952 const struct GNUNET_MessageHeader *hdr)
953{
954 struct GNUNET_DHT_Handle *handle = cls;
955 const char *url = (const char *) &hdr[1];
956 struct GNUNET_DHT_HelloGetHandle *hgh;
957
958 while (NULL != (hgh = handle->hgh_head))
959 {
960 hgh->cb (hgh->cb_cls,
961 url);
962 GNUNET_DHT_hello_get_cancel (hgh);
963 }
964}
965
966
967/**
968 * Process a MQ PUT transmission notification.
969 *
970 * @param cls The DHT handle.
971 */
972static void
973handle_put_cont (void *cls)
974{
975 struct GNUNET_DHT_PutHandle *ph = cls;
976 GNUNET_SCHEDULER_TaskCallback cont;
977 void *cont_cls;
978
979 cont = ph->cont;
980 cont_cls = ph->cont_cls;
981 ph->env = NULL;
982 GNUNET_DHT_put_cancel (ph);
983 if (NULL != cont)
984 cont (cont_cls);
985}
986
987
988/**
989 * Try to (re)connect to the DHT service.
990 *
991 * @param h DHT handle to reconnect
992 * @return #GNUNET_YES on success, #GNUNET_NO on failure.
993 */
994static enum GNUNET_GenericReturnValue
995try_connect (struct GNUNET_DHT_Handle *h)
996{
997 struct GNUNET_MQ_MessageHandler handlers[] = {
998 GNUNET_MQ_hd_fixed_size (monitor_get,
999 GNUNET_MESSAGE_TYPE_DHT_MONITOR_GET,
1000 struct GNUNET_DHT_MonitorGetMessage,
1001 h),
1002 GNUNET_MQ_hd_var_size (monitor_get_resp,
1003 GNUNET_MESSAGE_TYPE_DHT_MONITOR_GET_RESP,
1004 struct GNUNET_DHT_MonitorGetRespMessage,
1005 h),
1006 GNUNET_MQ_hd_var_size (monitor_put,
1007 GNUNET_MESSAGE_TYPE_DHT_MONITOR_PUT,
1008 struct GNUNET_DHT_MonitorPutMessage,
1009 h),
1010 GNUNET_MQ_hd_var_size (client_result,
1011 GNUNET_MESSAGE_TYPE_DHT_CLIENT_RESULT,
1012 struct GNUNET_DHT_ClientResultMessage,
1013 h),
1014 GNUNET_MQ_hd_var_size (client_hello,
1015 GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_URL,
1016 struct GNUNET_MessageHeader,
1017 h),
1018 GNUNET_MQ_handler_end ()
1019 };
1020
1021 if (NULL != h->mq)
1022 return GNUNET_OK;
1023 h->mq = GNUNET_CLIENT_connect (h->cfg,
1024 "dht",
1025 handlers,
1026 &mq_error_handler,
1027 h);
1028 if (NULL == h->mq)
1029 {
1030 LOG (GNUNET_ERROR_TYPE_WARNING,
1031 "Failed to connect to the DHT service!\n");
1032 return GNUNET_NO;
1033 }
1034 return GNUNET_YES;
1035}
1036
1037
1038struct GNUNET_DHT_Handle *
1039GNUNET_DHT_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
1040 unsigned int ht_len)
1041{
1042 struct GNUNET_DHT_Handle *handle;
1043
1044 handle = GNUNET_new (struct GNUNET_DHT_Handle);
1045 handle->cfg = cfg;
1046 handle->uid_gen
1047 = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1048 UINT64_MAX);
1049 handle->active_requests
1050 = GNUNET_CONTAINER_multihashmap_create (ht_len,
1051 GNUNET_YES);
1052 if (GNUNET_NO == try_connect (handle))
1053 {
1054 GNUNET_DHT_disconnect (handle);
1055 return NULL;
1056 }
1057 return handle;
1058}
1059
1060
1061void
1062GNUNET_DHT_disconnect (struct GNUNET_DHT_Handle *handle)
1063{
1064 struct GNUNET_DHT_PutHandle *ph;
1065
1066 GNUNET_assert (0 ==
1067 GNUNET_CONTAINER_multihashmap_size (handle->active_requests));
1068 while (NULL != (ph = handle->put_head))
1069 {
1070 if (NULL != ph->cont)
1071 ph->cont (ph->cont_cls);
1072 GNUNET_DHT_put_cancel (ph);
1073 }
1074 if (NULL != handle->mq)
1075 {
1076 GNUNET_MQ_destroy (handle->mq);
1077 handle->mq = NULL;
1078 }
1079 if (NULL != handle->reconnect_task)
1080 {
1081 GNUNET_SCHEDULER_cancel (handle->reconnect_task);
1082 handle->reconnect_task = NULL;
1083 }
1084 GNUNET_CONTAINER_multihashmap_destroy (handle->active_requests);
1085 GNUNET_free (handle);
1086}
1087
1088
1089struct GNUNET_DHT_PutHandle *
1090GNUNET_DHT_put (struct GNUNET_DHT_Handle *handle,
1091 const struct GNUNET_HashCode *key,
1092 uint32_t desired_replication_level,
1093 enum GNUNET_DHT_RouteOption options,
1094 enum GNUNET_BLOCK_Type type,
1095 size_t size,
1096 const void *data,
1097 struct GNUNET_TIME_Absolute exp,
1098 GNUNET_SCHEDULER_TaskCallback cont,
1099 void *cont_cls)
1100{
1101 struct GNUNET_MQ_Envelope *env;
1102 struct GNUNET_DHT_ClientPutMessage *put_msg;
1103 size_t msize;
1104 struct GNUNET_DHT_PutHandle *ph;
1105
1106 msize = sizeof(struct GNUNET_DHT_ClientPutMessage) + size;
1107 if ((msize >= GNUNET_MAX_MESSAGE_SIZE) ||
1108 (size >= GNUNET_MAX_MESSAGE_SIZE))
1109 {
1110 GNUNET_break (0);
1111 return NULL;
1112 }
1113 if (NULL == handle->mq)
1114 return NULL;
1115 LOG (GNUNET_ERROR_TYPE_DEBUG,
1116 "Sending PUT for %s to DHT via %p\n",
1117 GNUNET_h2s (key),
1118 handle);
1119 ph = GNUNET_new (struct GNUNET_DHT_PutHandle);
1120 ph->dht_handle = handle;
1121 ph->cont = cont;
1122 ph->cont_cls = cont_cls;
1123 GNUNET_CONTAINER_DLL_insert_tail (handle->put_head,
1124 handle->put_tail,
1125 ph);
1126 env = GNUNET_MQ_msg_extra (put_msg,
1127 size,
1128 GNUNET_MESSAGE_TYPE_DHT_CLIENT_PUT);
1129 GNUNET_MQ_notify_sent (env,
1130 &handle_put_cont,
1131 ph);
1132 ph->env = env;
1133 put_msg->type = htonl ((uint32_t) type);
1134 put_msg->options = htonl ((uint32_t) options);
1135 put_msg->desired_replication_level = htonl (desired_replication_level);
1136 put_msg->expiration = GNUNET_TIME_absolute_hton (exp);
1137 put_msg->key = *key;
1138 GNUNET_memcpy (&put_msg[1],
1139 data,
1140 size);
1141 GNUNET_MQ_send (handle->mq,
1142 env);
1143 return ph;
1144}
1145
1146
1147void
1148GNUNET_DHT_put_cancel (struct GNUNET_DHT_PutHandle *ph)
1149{
1150 struct GNUNET_DHT_Handle *handle = ph->dht_handle;
1151
1152 if (NULL != ph->env)
1153 GNUNET_MQ_notify_sent (ph->env,
1154 NULL,
1155 NULL);
1156 GNUNET_CONTAINER_DLL_remove (handle->put_head,
1157 handle->put_tail,
1158 ph);
1159 GNUNET_free (ph);
1160}
1161
1162
1163struct GNUNET_DHT_GetHandle *
1164GNUNET_DHT_get_start (struct GNUNET_DHT_Handle *handle,
1165 enum GNUNET_BLOCK_Type type,
1166 const struct GNUNET_HashCode *key,
1167 uint32_t desired_replication_level,
1168 enum GNUNET_DHT_RouteOption options,
1169 const void *xquery,
1170 size_t xquery_size,
1171 GNUNET_DHT_GetIterator iter,
1172 void *iter_cls)
1173{
1174 struct GNUNET_DHT_GetHandle *gh;
1175 size_t msize;
1176
1177 msize = sizeof(struct GNUNET_DHT_ClientGetMessage) + xquery_size;
1178 if ((msize >= GNUNET_MAX_MESSAGE_SIZE) ||
1179 (xquery_size >= GNUNET_MAX_MESSAGE_SIZE))
1180 {
1181 GNUNET_break (0);
1182 return NULL;
1183 }
1184 LOG (GNUNET_ERROR_TYPE_DEBUG,
1185 "Sending query for %s to DHT %p\n",
1186 GNUNET_h2s (key),
1187 handle);
1188 gh = GNUNET_malloc (sizeof(struct GNUNET_DHT_GetHandle)
1189 + xquery_size);
1190 gh->iter = iter;
1191 gh->iter_cls = iter_cls;
1192 gh->dht_handle = handle;
1193 gh->key = *key;
1194 gh->unique_id = ++handle->uid_gen;
1195 gh->xquery_size = xquery_size;
1196 gh->desired_replication_level = desired_replication_level;
1197 gh->type = type;
1198 gh->options = options;
1199 GNUNET_memcpy (&gh[1],
1200 xquery,
1201 xquery_size);
1202 GNUNET_CONTAINER_multihashmap_put (handle->active_requests,
1203 &gh->key,
1204 gh,
1205 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1206 if (NULL != handle->mq)
1207 send_get (gh);
1208 return gh;
1209}
1210
1211
1212void
1213GNUNET_DHT_get_filter_known_results (struct GNUNET_DHT_GetHandle *get_handle,
1214 unsigned int num_results,
1215 const struct GNUNET_HashCode *results)
1216{
1217 unsigned int needed;
1218 unsigned int had;
1219
1220 had = get_handle->seen_results_end;
1221 needed = had + num_results;
1222 if (needed > get_handle->seen_results_size)
1223 GNUNET_array_grow (get_handle->seen_results,
1224 get_handle->seen_results_size,
1225 needed);
1226 GNUNET_memcpy (&get_handle->seen_results[get_handle->seen_results_end],
1227 results,
1228 num_results * sizeof(struct GNUNET_HashCode));
1229 get_handle->seen_results_end += num_results;
1230 if (NULL != get_handle->dht_handle->mq)
1231 send_get_known_results (get_handle,
1232 had);
1233}
1234
1235
1236void
1237GNUNET_DHT_get_stop (struct GNUNET_DHT_GetHandle *get_handle)
1238{
1239 struct GNUNET_DHT_Handle *handle = get_handle->dht_handle;
1240
1241 LOG (GNUNET_ERROR_TYPE_DEBUG,
1242 "Sending STOP for %s to DHT via %p\n",
1243 GNUNET_h2s (&get_handle->key),
1244 handle);
1245 if (NULL != handle->mq)
1246 {
1247 struct GNUNET_MQ_Envelope *env;
1248 struct GNUNET_DHT_ClientGetStopMessage *stop_msg;
1249
1250 env = GNUNET_MQ_msg (stop_msg,
1251 GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET_STOP);
1252 stop_msg->reserved = htonl (0);
1253 stop_msg->unique_id = get_handle->unique_id;
1254 stop_msg->key = get_handle->key;
1255 GNUNET_MQ_send (handle->mq,
1256 env);
1257 }
1258 GNUNET_assert (GNUNET_YES ==
1259 GNUNET_CONTAINER_multihashmap_remove (handle->active_requests,
1260 &get_handle->key,
1261 get_handle));
1262 GNUNET_array_grow (get_handle->seen_results,
1263 get_handle->seen_results_end,
1264 0);
1265 GNUNET_free (get_handle);
1266}
1267
1268
1269struct GNUNET_DHT_MonitorHandle *
1270GNUNET_DHT_monitor_start (struct GNUNET_DHT_Handle *handle,
1271 enum GNUNET_BLOCK_Type type,
1272 const struct GNUNET_HashCode *key,
1273 GNUNET_DHT_MonitorGetCB get_cb,
1274 GNUNET_DHT_MonitorGetRespCB get_resp_cb,
1275 GNUNET_DHT_MonitorPutCB put_cb,
1276 void *cb_cls)
1277{
1278 struct GNUNET_DHT_MonitorHandle *mh;
1279
1280 mh = GNUNET_new (struct GNUNET_DHT_MonitorHandle);
1281 mh->get_cb = get_cb;
1282 mh->get_resp_cb = get_resp_cb;
1283 mh->put_cb = put_cb;
1284 mh->cb_cls = cb_cls;
1285 mh->type = type;
1286 mh->dht_handle = handle;
1287 if (NULL != key)
1288 {
1289 mh->key = GNUNET_new (struct GNUNET_HashCode);
1290 *mh->key = *key;
1291 }
1292 GNUNET_CONTAINER_DLL_insert (handle->monitor_head,
1293 handle->monitor_tail,
1294 mh);
1295 if (NULL != handle->mq)
1296 send_monitor_start (mh);
1297 return mh;
1298}
1299
1300
1301void
1302GNUNET_DHT_monitor_stop (struct GNUNET_DHT_MonitorHandle *mh)
1303{
1304 struct GNUNET_DHT_Handle *handle = mh->dht_handle;
1305 struct GNUNET_DHT_MonitorStartStopMessage *m;
1306 struct GNUNET_MQ_Envelope *env;
1307
1308 GNUNET_CONTAINER_DLL_remove (handle->monitor_head,
1309 handle->monitor_tail,
1310 mh);
1311 env = GNUNET_MQ_msg (m,
1312 GNUNET_MESSAGE_TYPE_DHT_MONITOR_STOP);
1313 m->type = htonl (mh->type);
1314 m->get = htons (NULL != mh->get_cb);
1315 m->get_resp = htons (NULL != mh->get_resp_cb);
1316 m->put = htons (NULL != mh->put_cb);
1317 if (NULL != mh->key)
1318 {
1319 m->filter_key = htons (1);
1320 m->key = *mh->key;
1321 }
1322 GNUNET_MQ_send (handle->mq,
1323 env);
1324 GNUNET_free (mh->key);
1325 GNUNET_free (mh);
1326}
1327
1328
1329char *
1330GNUNET_DHT_pp2s (const struct GNUNET_DHT_PathElement *path,
1331 unsigned int path_len)
1332{
1333 char *buf;
1334 size_t off;
1335 size_t plen = path_len * 5 + 1;
1336
1337 GNUNET_assert (path_len < UINT32_MAX / 5);
1338 off = 0;
1339 buf = GNUNET_malloc (plen);
1340 for (unsigned int i = 0; i < path_len; i++)
1341 {
1342 off += GNUNET_snprintf (&buf[off],
1343 plen - off,
1344 "%s%s",
1345 GNUNET_i2s (&path[i].pred),
1346 (i == path_len - 1) ? "" : "-");
1347 }
1348 return buf;
1349}
1350
1351
1352unsigned int
1353GNUNET_DHT_verify_path (const void *data,
1354 size_t data_size,
1355 struct GNUNET_TIME_Absolute exp_time,
1356 const struct GNUNET_PeerIdentity *bpid,
1357 const struct GNUNET_DHT_PathElement *put_path,
1358 unsigned int put_path_len,
1359 const struct GNUNET_DHT_PathElement *get_path,
1360 unsigned int get_path_len,
1361 const struct GNUNET_PeerIdentity *me)
1362{
1363 static struct GNUNET_PeerIdentity zero;
1364 struct GNUNET_DHT_HopSignature hs = {
1365 .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_DHT_HOP),
1366 .purpose.size = htonl (sizeof (hs)),
1367 .expiration_time = GNUNET_TIME_absolute_hton (exp_time)
1368 };
1369 unsigned int i;
1370
1371 if (0 == get_path_len + put_path_len)
1372 return 0;
1373 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1374 "%s is verifying signatures with GPL: %u PPL: %u!\n",
1375 GNUNET_i2s (me),
1376 get_path_len,
1377 put_path_len);
1378 for (unsigned int j = 0; j<put_path_len; j++)
1379 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1380 "PP%u=%s\n",
1381 j,
1382 GNUNET_i2s (&put_path[j].pred));
1383 for (unsigned int j = 0; j<get_path_len; j++)
1384 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1385 "GP%u=%s\n",
1386 j,
1387 GNUNET_i2s (&get_path[j].pred));
1388
1389 GNUNET_CRYPTO_hash (data,
1390 data_size,
1391 &hs.h_data);
1392 i = put_path_len + get_path_len;
1393 while (i > 0)
1394 {
1395 const struct GNUNET_PeerIdentity *pred;
1396 const struct GNUNET_PeerIdentity *succ;
1397 const struct GNUNET_DHT_PathElement *pe;
1398
1399 i--;
1400 if (0 == i)
1401 {
1402 pred = (NULL == bpid) ? &zero : bpid;
1403 }
1404 else
1405 {
1406 unsigned int off = i - 1;
1407
1408 pred = (off >= put_path_len)
1409 ? &get_path[off - put_path_len].pred
1410 : &put_path[off].pred;
1411 }
1412 if (i == get_path_len + put_path_len - 1)
1413 {
1414 succ = me;
1415 }
1416 else
1417 {
1418 unsigned int off = i + 1;
1419
1420 succ = (off >= put_path_len)
1421 ? &get_path[off - put_path_len].pred
1422 : &put_path[off].pred;
1423 }
1424 hs.pred = *pred;
1425 hs.succ = *succ;
1426 pe = (i >= put_path_len)
1427 ? &get_path[i - put_path_len]
1428 : &put_path[i];
1429 if (GNUNET_OK !=
1430 GNUNET_CRYPTO_eddsa_verify (
1431 GNUNET_SIGNATURE_PURPOSE_DHT_HOP,
1432 &hs,
1433 &pe->sig,
1434 &pe->pred.public_key))
1435 {
1436 GNUNET_break_op (0);
1437 return i + 1;
1438 }
1439 }
1440 return i;
1441}
1442
1443
1444struct GNUNET_DHT_HelloGetHandle *
1445GNUNET_DHT_hello_get (struct GNUNET_DHT_Handle *dht_handle,
1446 GNUNET_DHT_HelloGetCallback cb,
1447 void *cb_cls)
1448{
1449 struct GNUNET_DHT_HelloGetHandle *hgh;
1450 struct GNUNET_MessageHeader *hdr;
1451 struct GNUNET_MQ_Envelope *env;
1452
1453 hgh = GNUNET_new (struct GNUNET_DHT_HelloGetHandle);
1454 hgh->cb = cb;
1455 hgh->cb_cls = cb_cls;
1456 hgh->dht_handle = dht_handle;
1457 GNUNET_CONTAINER_DLL_insert (dht_handle->hgh_head,
1458 dht_handle->hgh_tail,
1459 hgh);
1460 env = GNUNET_MQ_msg (hdr,
1461 GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_GET);
1462 GNUNET_MQ_send (dht_handle->mq,
1463 env);
1464 return hgh;
1465}
1466
1467
1468void
1469GNUNET_DHT_hello_get_cancel (struct GNUNET_DHT_HelloGetHandle *hgh)
1470{
1471 struct GNUNET_DHT_Handle *dht_handle = hgh->dht_handle;
1472
1473 GNUNET_CONTAINER_DLL_remove (dht_handle->hgh_head,
1474 dht_handle->hgh_tail,
1475 hgh);
1476 GNUNET_free (hgh);
1477}
1478
1479
1480void
1481GNUNET_DHT_hello_offer (struct GNUNET_DHT_Handle *dht_handle,
1482 const char *url,
1483 GNUNET_SCHEDULER_TaskCallback cb,
1484 void *cb_cls)
1485{
1486 struct GNUNET_MessageHeader *hdr;
1487 size_t slen = strlen (url) + 1;
1488 struct GNUNET_MQ_Envelope *env;
1489
1490 env = GNUNET_MQ_msg_extra (hdr,
1491 slen,
1492 GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_URL);
1493 memcpy (&hdr[1],
1494 url,
1495 slen);
1496 GNUNET_MQ_notify_sent (env,
1497 cb,
1498 cb_cls);
1499 GNUNET_MQ_send (dht_handle->mq,
1500 env);
1501}
1502
1503
1504/* end of dht_api.c */
diff --git a/src/dht/dht_test_lib.c b/src/dht/dht_test_lib.c
deleted file mode 100644
index 29c5088d1..000000000
--- a/src/dht/dht_test_lib.c
+++ /dev/null
@@ -1,202 +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 * @file dht/dht_test_lib.c
22 * @author Christian Grothoff
23 * @brief library for writing DHT tests
24 */
25#include "platform.h"
26#include "dht_test_lib.h"
27
28/**
29 * Test context for a DHT Test.
30 */
31struct GNUNET_DHT_TEST_Context
32{
33 /**
34 * Array of running peers.
35 */
36 struct GNUNET_TESTBED_Peer **peers;
37
38 /**
39 * Array of handles to the DHT for each peer.
40 */
41 struct GNUNET_DHT_Handle **dhts;
42
43 /**
44 * Operation associated with the connection to the DHT.
45 */
46 struct GNUNET_TESTBED_Operation **ops;
47
48 /**
49 * Main function of the test to run once all DHTs are available.
50 */
51 GNUNET_DHT_TEST_AppMain app_main;
52
53 /**
54 * Closure for 'app_main'.
55 */
56 void *app_main_cls;
57
58 /**
59 * Number of peers running, size of the arrays above.
60 */
61 unsigned int num_peers;
62};
63
64
65/**
66 * Adapter function called to establish a connection to
67 * the DHT service.
68 *
69 * @param cls closure
70 * @param cfg configuration of the peer to connect to; will be available until
71 * GNUNET_TESTBED_operation_done() is called on the operation returned
72 * from GNUNET_TESTBED_service_connect()
73 * @return service handle to return in 'op_result', NULL on error
74 */
75static void *
76dht_connect_adapter (void *cls,
77 const struct GNUNET_CONFIGURATION_Handle *cfg)
78{
79 return GNUNET_DHT_connect (cfg, 16);
80}
81
82
83/**
84 * Adapter function called to destroy a connection to
85 * the DHT service.
86 *
87 * @param cls closure
88 * @param op_result service handle returned from the connect adapter
89 */
90static void
91dht_disconnect_adapter (void *cls,
92 void *op_result)
93{
94 struct GNUNET_DHT_Handle *dht = op_result;
95
96 GNUNET_DHT_disconnect (dht);
97}
98
99
100/**
101 * Callback to be called when a service connect operation is completed
102 *
103 * @param cls the callback closure from functions generating an operation
104 * @param op the operation that has been finished
105 * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
106 * @param emsg error message in case the operation has failed; will be NULL if
107 * operation has executed successfully.
108 */
109static void
110dht_connect_cb (void *cls,
111 struct GNUNET_TESTBED_Operation *op,
112 void *ca_result,
113 const char *emsg)
114{
115 struct GNUNET_DHT_TEST_Context *ctx = cls;
116
117 if (NULL != emsg)
118 {
119 fprintf (stderr,
120 "Failed to connect to DHT service: %s\n",
121 emsg);
122 GNUNET_SCHEDULER_shutdown ();
123 return;
124 }
125 for (unsigned int i = 0; i < ctx->num_peers; i++)
126 if (op == ctx->ops[i])
127 ctx->dhts[i] = ca_result;
128 for (unsigned int i = 0; i < ctx->num_peers; i++)
129 if (NULL == ctx->dhts[i])
130 return;
131 /* still some DHT connections missing */
132 /* all DHT connections ready! */
133 ctx->app_main (ctx->app_main_cls,
134 ctx,
135 ctx->num_peers,
136 ctx->peers,
137 ctx->dhts);
138}
139
140
141void
142GNUNET_DHT_TEST_cleanup (struct GNUNET_DHT_TEST_Context *ctx)
143{
144 for (unsigned int i = 0; i < ctx->num_peers; i++)
145 GNUNET_TESTBED_operation_done (ctx->ops[i]);
146 GNUNET_free (ctx->ops);
147 GNUNET_free (ctx->dhts);
148 GNUNET_free (ctx);
149 GNUNET_SCHEDULER_shutdown ();
150}
151
152
153static void
154dht_test_run (void *cls,
155 struct GNUNET_TESTBED_RunHandle *h,
156 unsigned int num_peers,
157 struct GNUNET_TESTBED_Peer **peers,
158 unsigned int links_succeeded,
159 unsigned int links_failed)
160{
161 struct GNUNET_DHT_TEST_Context *ctx = cls;
162
163 GNUNET_assert (num_peers == ctx->num_peers);
164 ctx->peers = peers;
165 for (unsigned int i = 0; i < num_peers; i++)
166 ctx->ops[i] = GNUNET_TESTBED_service_connect (ctx,
167 peers[i],
168 "dht",
169 &dht_connect_cb,
170 ctx,
171 &dht_connect_adapter,
172 &dht_disconnect_adapter,
173 ctx);
174}
175
176
177void
178GNUNET_DHT_TEST_run (const char *testname,
179 const char *cfgname,
180 unsigned int num_peers,
181 GNUNET_DHT_TEST_AppMain tmain,
182 void *tmain_cls)
183{
184 struct GNUNET_DHT_TEST_Context *ctx;
185
186 ctx = GNUNET_new (struct GNUNET_DHT_TEST_Context);
187 ctx->num_peers = num_peers;
188 ctx->ops = GNUNET_new_array (num_peers,
189 struct GNUNET_TESTBED_Operation *);
190 ctx->dhts = GNUNET_new_array (num_peers,
191 struct GNUNET_DHT_Handle *);
192 ctx->app_main = tmain;
193 ctx->app_main_cls = tmain_cls;
194 (void) GNUNET_TESTBED_test_run (testname,
195 cfgname,
196 num_peers,
197 0LL, NULL, NULL,
198 &dht_test_run, ctx);
199}
200
201
202/* end of dht_test_lib.c */
diff --git a/src/dht/dht_test_lib.h b/src/dht/dht_test_lib.h
deleted file mode 100644
index efffc9ef7..000000000
--- a/src/dht/dht_test_lib.h
+++ /dev/null
@@ -1,95 +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 * @file dht/dht_test_lib.h
22 * @author Christian Grothoff
23 * @brief library for writing DHT tests
24 */
25#ifndef DHT_TEST_LIB_H
26#define DHT_TEST_LIB_H
27
28#ifdef __cplusplus
29extern "C"
30{
31#if 0 /* keep Emacsens' auto-indent happy */
32}
33#endif
34#endif
35
36#include "gnunet_testbed_service.h"
37#include "gnunet_dht_service.h"
38
39/**
40 * Test context for a DHT Test.
41 */
42struct GNUNET_DHT_TEST_Context;
43
44
45/**
46 * Main function of a DHT test.
47 *
48 * @param cls closure
49 * @param ctx argument to give to GNUNET_DHT_TEST_cleanup on test end
50 * @param num_peers number of peers that are running
51 * @param peers array of peers
52 * @param dhts handle to each of the DHTs of the peers
53 */
54typedef void (*GNUNET_DHT_TEST_AppMain) (void *cls,
55 struct GNUNET_DHT_TEST_Context *ctx,
56 unsigned int num_peers,
57 struct GNUNET_TESTBED_Peer **peers,
58 struct GNUNET_DHT_Handle **dhts);
59
60
61/**
62 * Run a test using the given name, configuration file and number of
63 * peers.
64 *
65 * @param testname name of the test (for logging)
66 * @param cfgname name of the configuration file
67 * @param num_peers number of peers to start
68 * @param tmain main function to run once the testbed is ready
69 * @param tmain_cls closure for 'tmain'
70 */
71void
72GNUNET_DHT_TEST_run (const char *testname,
73 const char *cfgname,
74 unsigned int num_peers,
75 GNUNET_DHT_TEST_AppMain tmain,
76 void *tmain_cls);
77
78
79/**
80 * Clean up the testbed.
81 *
82 * @param ctx handle for the testbed
83 */
84void
85GNUNET_DHT_TEST_cleanup (struct GNUNET_DHT_TEST_Context *ctx);
86
87#if 0 /* keep Emacsens' auto-indent happy */
88{
89#endif
90#ifdef __cplusplus
91}
92#endif
93
94/* ifndef DHT_TEST_LIB_H */
95#endif
diff --git a/src/dht/dhtu_testbed_connect.sh b/src/dht/dhtu_testbed_connect.sh
deleted file mode 100755
index 871e9eb1b..000000000
--- a/src/dht/dhtu_testbed_connect.sh
+++ /dev/null
@@ -1,31 +0,0 @@
1#!/bin/bash
2# This file is in the public domain.
3
4# Helper script for dhtu_testbed_deploy.sh.
5# Do not invoke directly.
6
7n=$1
8CFG="/tmp/deployment/${n}.conf"
9HELLO=`gnunet-dht-hello -c $CFG`
10
11# Create dense topology:
12#for OFF in `seq 1 $MAX`
13#do
14# TCFG="/tmp/deployment/${OFF}.conf"
15# gnunet-dht-hello -c $TCFG $HELLO
16#done
17#exit 0
18
19# Create sparse topology:
20R=1
21while test `expr $R \* $R \* $R` -lt $MAX
22do
23 END=`expr $R \* $R`
24 for M in `seq $R $R $END`
25 do
26 OFF=`expr \( $n + $M \) % $MAX`
27 TCFG="/tmp/deployment/${OFF}.conf"
28 gnunet-dht-hello -c $TCFG $HELLO
29 done
30 R=`expr $R + 1`
31done
diff --git a/src/dht/dhtu_testbed_deploy.conf b/src/dht/dhtu_testbed_deploy.conf
deleted file mode 100644
index 59d69894a..000000000
--- a/src/dht/dhtu_testbed_deploy.conf
+++ /dev/null
@@ -1,26 +0,0 @@
1# This file is in the public domain.
2
3# Simple configuration template to deploy a DHT testbed
4# with peers using the IP underlay.
5
6[paths]
7GNUNET_DATA_HOME=/tmp/%N%
8
9[dht]
10UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-dht-%N%.sock
11
12[dhtu-ip]
13ENABLED = YES
14NSE = 10
15UDP_PORT = %N%
16
17[dhtu-gnunet]
18ENABLED = NO
19
20[statistics]
21DISABLE = YES
22
23[dhtcache]
24DATABASE = heap
25QUOTA = 50 MB
26DISABLE_BF_RC = YES
diff --git a/src/dht/dhtu_testbed_deploy.sh b/src/dht/dhtu_testbed_deploy.sh
deleted file mode 100755
index 5a7fdde15..000000000
--- a/src/dht/dhtu_testbed_deploy.sh
+++ /dev/null
@@ -1,84 +0,0 @@
1#!/bin/bash
2# This file is in the public domain.
3
4# We will use UDP ports above this number.
5MINPORT=10000
6
7# Cleanup to run whenever we exit
8function cleanup()
9{
10 for n in `jobs -p`
11 do
12 kill $n 2> /dev/null || true
13 done
14 wait
15}
16
17# Install cleanup handler (except for kill -9)
18trap cleanup EXIT
19
20if test -z "$1"
21then
22 echo "Call with the number of peers to launch."
23 exit 1
24fi
25
26if test ! -x `which parallel`
27then
28 echo "This script requires GNU parallel"
29 exit 1
30fi
31
32if test ! -x `which gnunet-service-dht`
33then
34 echo "This script requires gnunet-service-dht in \$PATH"
35 exit 1
36fi
37
38if test ! -x `which gnunet-dht-hello`
39then
40 echo "This script requires gnunet-dht-hello in \$PATH"
41 exit 1
42fi
43
44MAX=`expr $1 - 1`
45
46export GNUNET_FORCE_LOG="dht*;;;;DEBUG"
47
48echo -n "Starting $1 peers "
49mkdir -p /tmp/deployment
50for n in `seq 0 $MAX`
51do
52 PORT=`expr $MINPORT + $n`
53 CFG="/tmp/deployment/${n}.conf"
54 cat dhtu_testbed_deploy.conf | sed -e "s/%N%/$PORT/" > $CFG
55 gnunet-service-dht -c $CFG &> /tmp/deployment/$n.log &
56 echo -n "."
57done
58
59echo ""
60echo "$1 peers ready".
61
62unset GNUNET_FORCE_LOG
63
64function connect()
65{
66 n=$1
67}
68
69echo -n "Connecting peers ..."
70
71export MAX
72if test 0 != $MAX
73then
74 seq 0 $MAX | parallel ./dhtu_testbed_connect.sh :::
75fi
76
77
78echo ""
79echo "Network ready. Press ENTER to terminate the testbed!"
80echo "Interact with peers using '-c /tmp/deployment/\$N.conf'"
81
82read
83
84exit 0
diff --git a/src/dht/gnunet-dht-get.c b/src/dht/gnunet-dht-get.c
deleted file mode 100644
index 806cafd0d..000000000
--- a/src/dht/gnunet-dht-get.c
+++ /dev/null
@@ -1,330 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009, 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 * @file dht/gnunet-dht-get.c
22 * @brief search for data in DHT
23 * @author Christian Grothoff
24 * @author Nathan Evans
25 */
26#include "platform.h"
27#include "gnunet_dht_service.h"
28
29#define LOG(kind, ...) GNUNET_log_from (kind, "dht-clients", __VA_ARGS__)
30/**
31 * The type of the query
32 */
33static unsigned int query_type;
34
35/**
36 * Desired replication level
37 */
38static unsigned int replication = 5;
39
40/**
41 * The key for the query
42 */
43static char *query_key;
44
45/**
46 * User supplied timeout value
47 */
48static struct GNUNET_TIME_Relative timeout_request = { 60000 };
49
50/**
51 * Be verbose
52 */
53static unsigned int verbose;
54
55/**
56 * Use DHT demultixplex_everywhere
57 */
58static int demultixplex_everywhere;
59
60/**
61 * Use #GNUNET_DHT_RO_RECORD_ROUTE.
62 */
63static int record_route;
64
65/**
66 * Handle to the DHT
67 */
68static struct GNUNET_DHT_Handle *dht_handle;
69
70/**
71 * Global handle of the configuration
72 */
73static const struct GNUNET_CONFIGURATION_Handle *cfg;
74
75/**
76 * Handle for the get request
77 */
78static struct GNUNET_DHT_GetHandle *get_handle;
79
80/**
81 * Count of results found
82 */
83static unsigned int result_count;
84
85/**
86 * Global status value
87 */
88static int ret;
89
90/**
91 * Task scheduled to handle timeout.
92 */
93static struct GNUNET_SCHEDULER_Task *tt;
94
95
96/**
97 * Task run to clean up on shutdown.
98 *
99 * @param cls unused
100 */
101static void
102cleanup_task (void *cls)
103{
104 if (NULL != get_handle)
105 {
106 GNUNET_DHT_get_stop (get_handle);
107 get_handle = NULL;
108 }
109 if (NULL != dht_handle)
110 {
111 GNUNET_DHT_disconnect (dht_handle);
112 dht_handle = NULL;
113 }
114 if (NULL != tt)
115 {
116 GNUNET_SCHEDULER_cancel (tt);
117 tt = NULL;
118 }
119}
120
121
122/**
123 * Task run on timeout. Triggers shutdown.
124 *
125 * @param cls unused
126 */
127static void
128timeout_task (void *cls)
129{
130 tt = NULL;
131 GNUNET_SCHEDULER_shutdown ();
132}
133
134
135/**
136 * Iterator called on each result obtained for a DHT
137 * operation that expects a reply
138 *
139 * @param cls closure
140 * @param exp when will this value expire
141 * @param key key of the result
142 * @param trunc_peer peer at which the path was truncated, or NULL if not
143 * @param get_path peers on reply path (or NULL if not recorded)
144 * @param get_path_length number of entries in get_path
145 * @param put_path peers on the PUT path (or NULL if not recorded)
146 * @param put_path_length number of entries in get_path
147 * @param type type of the result
148 * @param size number of bytes in data
149 * @param data pointer to the result data
150 */
151static void
152get_result_iterator (void *cls,
153 struct GNUNET_TIME_Absolute exp,
154 const struct GNUNET_HashCode *key,
155 const struct GNUNET_PeerIdentity *trunc_peer,
156 const struct GNUNET_DHT_PathElement *get_path,
157 unsigned int get_path_length,
158 const struct GNUNET_DHT_PathElement *put_path,
159 unsigned int put_path_length,
160 enum GNUNET_BLOCK_Type type,
161 size_t size,
162 const void *data)
163{
164 fprintf (stdout,
165 (GNUNET_BLOCK_TYPE_TEST == type)
166 ? _ ("Result %d, type %d:\n%.*s\n")
167 : _ ("Result %d, type %d:\n"),
168 result_count,
169 type,
170 (int) size,
171 (char *) data);
172 if (record_route && verbose)
173 {
174 fprintf (stdout,
175 " GET path: ");
176 for (unsigned int i = 0; i < get_path_length; i++)
177 fprintf (stdout,
178 "%s%s",
179 (0 == i) ? "" : "-",
180 GNUNET_i2s (&get_path[i].pred));
181 fprintf (stdout,
182 "\n PUT path: ");
183 for (unsigned int i = 0; i < put_path_length; i++)
184 fprintf (stdout,
185 "%s%s",
186 (0 == i) ? "" : "-",
187 GNUNET_i2s (&put_path[i].pred));
188 if (NULL != trunc_peer)
189 fprintf (stdout,
190 "T%s",
191 GNUNET_i2s (trunc_peer));
192 fprintf (stdout,
193 "\n");
194 }
195 result_count++;
196}
197
198
199/**
200 * Main function that will be run by the scheduler.
201 *
202 * @param cls closure
203 * @param args remaining command-line arguments
204 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
205 * @param c configuration
206 */
207static void
208run (void *cls,
209 char *const *args,
210 const char *cfgfile,
211 const struct GNUNET_CONFIGURATION_Handle *c)
212{
213 struct GNUNET_HashCode key;
214 enum GNUNET_DHT_RouteOption ro;
215
216 cfg = c;
217 if (NULL == query_key)
218 {
219 fprintf (stderr,
220 "%s",
221 _ ("Must provide key for DHT GET!\n"));
222 ret = 1;
223 return;
224 }
225 if (NULL == (dht_handle = GNUNET_DHT_connect (cfg, 1)))
226 {
227 fprintf (stderr,
228 "%s",
229 _ ("Failed to connect to DHT service!\n"));
230 ret = 1;
231 return;
232 }
233 if (query_type == GNUNET_BLOCK_TYPE_ANY) /* Type of data not set */
234 query_type = GNUNET_BLOCK_TYPE_TEST;
235 GNUNET_CRYPTO_hash (query_key, strlen (query_key), &key);
236 if (verbose)
237 fprintf (stderr,
238 "%s `%s' \n",
239 _ ("Issuing DHT GET with key"),
240 GNUNET_h2s_full (&key));
241 GNUNET_SCHEDULER_add_shutdown (&cleanup_task, NULL);
242 tt = GNUNET_SCHEDULER_add_delayed (timeout_request, &timeout_task, NULL);
243 ro = GNUNET_DHT_RO_NONE;
244 if (demultixplex_everywhere)
245 ro |= GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE;
246 if (record_route)
247 ro |= GNUNET_DHT_RO_RECORD_ROUTE;
248 get_handle = GNUNET_DHT_get_start (dht_handle,
249 query_type,
250 &key,
251 replication,
252 ro,
253 NULL,
254 0,
255 &get_result_iterator,
256 NULL);
257}
258
259
260/**
261 * Entry point for gnunet-dht-get
262 *
263 * @param argc number of arguments from the command line
264 * @param argv command line arguments
265 * @return 0 ok, 1 on error
266 */
267int
268main (int argc, char *const *argv)
269{
270 struct GNUNET_GETOPT_CommandLineOption options[] = {
271 GNUNET_GETOPT_option_string (
272 'k',
273 "key",
274 "KEY",
275 gettext_noop ("the query key"),
276 &query_key),
277 GNUNET_GETOPT_option_uint (
278 'r',
279 "replication",
280 "LEVEL",
281 gettext_noop ("how many parallel requests (replicas) to create"),
282 &replication),
283 GNUNET_GETOPT_option_flag (
284 'R',
285 "record",
286 gettext_noop ("use DHT's record route option"),
287 &record_route),
288 GNUNET_GETOPT_option_uint (
289 't',
290 "type",
291 "TYPE",
292 gettext_noop ("the type of data to look for"),
293 &query_type),
294 GNUNET_GETOPT_option_relative_time (
295 'T',
296 "timeout",
297 "TIMEOUT",
298 gettext_noop ("how long to execute this query before giving up?"),
299 &timeout_request),
300 GNUNET_GETOPT_option_flag (
301 'x',
302 "demultiplex",
303 gettext_noop (
304 "use DHT's demultiplex everywhere option"),
305 &demultixplex_everywhere),
306 GNUNET_GETOPT_option_verbose (&verbose),
307 GNUNET_GETOPT_OPTION_END
308 };
309
310
311 if (GNUNET_OK !=
312 GNUNET_STRINGS_get_utf8_args (argc, argv,
313 &argc, &argv))
314 return 2;
315 return (GNUNET_OK ==
316 GNUNET_PROGRAM_run (
317 argc,
318 argv,
319 "gnunet-dht-get",
320 gettext_noop (
321 "Issue a GET request to the GNUnet DHT, prints results."),
322 options,
323 &run,
324 NULL))
325 ? ret
326 : 1;
327}
328
329
330/* end of gnunet-dht-get.c */
diff --git a/src/dht/gnunet-dht-hello.c b/src/dht/gnunet-dht-hello.c
deleted file mode 100644
index 369ed5643..000000000
--- a/src/dht/gnunet-dht-hello.c
+++ /dev/null
@@ -1,178 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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 * @file dht/gnunet-dht-hello.c
22 * @brief Obtain HELLO from DHT for bootstrapping
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_dht_service.h"
27
28#define LOG(kind, ...) GNUNET_log_from (kind, "dht-clients", __VA_ARGS__)
29
30/**
31 * Handle to the DHT
32 */
33static struct GNUNET_DHT_Handle *dht_handle;
34
35/**
36 * Handle to the DHT hello get operation.
37 */
38static struct GNUNET_DHT_HelloGetHandle *get_hello_handle;
39
40/**
41 * Global status value
42 */
43static int global_ret;
44
45
46/**
47 * Task run to clean up on shutdown.
48 *
49 * @param cls unused
50 */
51static void
52cleanup_task (void *cls)
53{
54 if (NULL != get_hello_handle)
55 {
56 GNUNET_DHT_hello_get_cancel (get_hello_handle);
57 get_hello_handle = NULL;
58 }
59 if (NULL != dht_handle)
60 {
61 GNUNET_DHT_disconnect (dht_handle);
62 dht_handle = NULL;
63 }
64}
65
66
67/**
68 * Task run when we are finished. Triggers shutdown.
69 *
70 * @param cls unused
71 */
72static void
73hello_done_cb (void *cls)
74{
75 GNUNET_SCHEDULER_shutdown ();
76}
77
78
79/**
80 * Function called on our HELLO.
81 *
82 * @param cls closure
83 * @param url the HELLO URL
84 */
85static void
86hello_result_cb (void *cls,
87 const char *url)
88{
89 get_hello_handle = NULL;
90 fprintf (stdout,
91 "%s\n",
92 url);
93 GNUNET_SCHEDULER_shutdown ();
94}
95
96
97/**
98 * Main function that will be run by the scheduler.
99 *
100 * @param cls closure, NULL
101 * @param args remaining command-line arguments
102 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
103 * @param cfg configuration
104 */
105static void
106run (void *cls,
107 char *const *args,
108 const char *cfgfile,
109 const struct GNUNET_CONFIGURATION_Handle *cfg)
110{
111 (void) cls;
112 (void) cfgfile;
113 GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
114 NULL);
115 if (NULL == (dht_handle = GNUNET_DHT_connect (cfg,
116 1)))
117 {
118 fprintf (stderr,
119 _ ("Failed to connect to DHT service!\n"));
120 global_ret = EXIT_NOTCONFIGURED;
121 GNUNET_SCHEDULER_shutdown ();
122 return;
123 }
124 if (NULL == args[0])
125 {
126 get_hello_handle = GNUNET_DHT_hello_get (dht_handle,
127 &hello_result_cb,
128 NULL);
129 GNUNET_break (NULL != get_hello_handle);
130 }
131 else
132 {
133 GNUNET_DHT_hello_offer (dht_handle,
134 args[0],
135 &hello_done_cb,
136 NULL);
137 }
138}
139
140
141/**
142 * Entry point for gnunet-dht-hello
143 *
144 * @param argc number of arguments from the command line
145 * @param argv command line arguments
146 * @return 0 ok, 1 on error
147 */
148int
149main (int argc,
150 char *const *argv)
151{
152 struct GNUNET_GETOPT_CommandLineOption options[] = {
153 GNUNET_GETOPT_OPTION_END
154 };
155 enum GNUNET_GenericReturnValue iret;
156
157 if (GNUNET_OK !=
158 GNUNET_STRINGS_get_utf8_args (argc, argv,
159 &argc, &argv))
160 return 2;
161 iret = GNUNET_PROGRAM_run (
162 argc,
163 argv,
164 "gnunet-dht-hello [URL]",
165 gettext_noop (
166 "Obtain HELLO from DHT or provide HELLO to DHT for bootstrapping"),
167 options,
168 &run,
169 NULL);
170 if (GNUNET_SYSERR == iret)
171 return EXIT_FAILURE;
172 if (GNUNET_NO == iret)
173 return EXIT_SUCCESS;
174 return global_ret;
175}
176
177
178/* end of gnunet-dht-hello.c */
diff --git a/src/dht/gnunet-dht-monitor.c b/src/dht/gnunet-dht-monitor.c
deleted file mode 100644
index 93ea1284a..000000000
--- a/src/dht/gnunet-dht-monitor.c
+++ /dev/null
@@ -1,345 +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 * @file dht/gnunet-dht-monitor.c
22 * @brief search for data in DHT
23 * @author Christian Grothoff
24 * @author Bartlomiej Polot
25 */
26#include "platform.h"
27#include "gnunet_dht_service.h"
28
29/**
30 * The type of the query
31 */
32static unsigned int block_type;
33
34/**
35 * The key to be monitored
36 */
37static char *query_key;
38
39/**
40 * User supplied timeout value (in seconds)
41 */
42static struct GNUNET_TIME_Relative timeout_request = { 60000 };
43
44/**
45 * Be verbose
46 */
47static int verbose;
48
49/**
50 * Handle to the DHT
51 */
52static struct GNUNET_DHT_Handle *dht_handle;
53
54/**
55 * Global handle of the configuration
56 */
57static const struct GNUNET_CONFIGURATION_Handle *cfg;
58
59/**
60 * Handle for the get request
61 */
62static struct GNUNET_DHT_MonitorHandle *monitor_handle;
63
64/**
65 * Count of messages received
66 */
67static unsigned int result_count;
68
69/**
70 * Global status value
71 */
72static int ret;
73
74/**
75 * Task scheduled to handle timeout.
76 */
77static struct GNUNET_SCHEDULER_Task *tt;
78
79
80/**
81 * Stop monitoring request and start shutdown
82 *
83 * @param cls closure (unused)
84 */
85static void
86cleanup_task (void *cls)
87{
88 if (verbose)
89 fprintf (stderr, "%s", "Cleaning up!\n");
90 if (NULL != monitor_handle)
91 {
92 GNUNET_DHT_monitor_stop (monitor_handle);
93 monitor_handle = NULL;
94 }
95 if (NULL != dht_handle)
96 {
97 GNUNET_DHT_disconnect (dht_handle);
98 dht_handle = NULL;
99 }
100 if (NULL != tt)
101 {
102 GNUNET_SCHEDULER_cancel (tt);
103 tt = NULL;
104 }
105}
106
107
108/**
109 * We hit a timeout. Stop monitoring request and start shutdown
110 *
111 * @param cls closure (unused)
112 */
113static void
114timeout_task (void *cls)
115{
116 tt = NULL;
117 GNUNET_SCHEDULER_shutdown ();
118}
119
120
121/**
122 * Callback called on each GET request going through the DHT.
123 *
124 * @param cls Closure.
125 * @param options Options, for instance RecordRoute, DemultiplexEverywhere.
126 * @param type The type of data in the request.
127 * @param hop_count Hop count so far.
128 * @param desired_replication_level Desired replication level.
129 * @param key Key of the requested data.
130 */
131static void
132get_callback (void *cls,
133 enum GNUNET_DHT_RouteOption options,
134 enum GNUNET_BLOCK_Type type,
135 uint32_t hop_count,
136 uint32_t desired_replication_level,
137 const struct GNUNET_HashCode *key)
138{
139 fprintf (stdout,
140 "GET #%u: type %d, key `%s'\n",
141 result_count,
142 (int) type,
143 GNUNET_h2s_full (key));
144 result_count++;
145}
146
147
148/**
149 * Callback called on each GET reply going through the DHT.
150 *
151 * @param cls Closure.
152 * @param type The type of data in the result.
153 * @param trunc_peer peer where the path was truncated, or NULL if the path is complete
154 * @param get_path Peers on GET path (or NULL if not recorded).
155 * @param get_path_length number of entries in get_path.
156 * @param put_path peers on the PUT path (or NULL if not recorded).
157 * @param put_path_length number of entries in get_path.
158 * @param exp Expiration time of the data.
159 * @param key Key of the data.
160 * @param data Pointer to the result data.
161 * @param size Number of bytes in data.
162 */
163static void
164get_resp_callback (void *cls,
165 enum GNUNET_BLOCK_Type type,
166 const struct GNUNET_PeerIdentity *trunc_peer,
167 const struct GNUNET_DHT_PathElement *get_path,
168 unsigned int get_path_length,
169 const struct GNUNET_DHT_PathElement *put_path,
170 unsigned int put_path_length,
171 struct GNUNET_TIME_Absolute exp,
172 const struct GNUNET_HashCode *key,
173 const void *data,
174 size_t size)
175{
176 fprintf (stdout,
177 (GNUNET_BLOCK_TYPE_TEST == type)
178 ? "RESPONSE #%u (%s): type %d, key `%s', data `%.*s'\n"
179 : "RESPONSE #%u (%s): type %d, key `%s'\n",
180 result_count,
181 GNUNET_STRINGS_absolute_time_to_string (exp),
182 (int) type,
183 GNUNET_h2s_full (key),
184 (unsigned int) size,
185 (char *) data);
186 result_count++;
187}
188
189
190/**
191 * Callback called on each PUT request going through the DHT.
192 *
193 * @param cls Closure.
194 * @param options Options, for instance RecordRoute, DemultiplexEverywhere.
195 * @param type The type of data in the request.
196 * @param hop_count Hop count so far.
197 * @param trunc_peer peer where the path was truncated, or NULL if the path is complete
198 * @param path_length number of entries in path (or 0 if not recorded).
199 * @param path peers on the PUT path (or NULL if not recorded).
200 * @param desired_replication_level Desired replication level.
201 * @param exp Expiration time of the data.
202 * @param key Key under which data is to be stored.
203 * @param data Pointer to the data carried.
204 * @param size Number of bytes in data.
205 */
206static void
207put_callback (void *cls,
208 enum GNUNET_DHT_RouteOption options,
209 enum GNUNET_BLOCK_Type type,
210 uint32_t hop_count,
211 uint32_t desired_replication_level,
212 const struct GNUNET_PeerIdentity *trunc_peer,
213 unsigned int path_length,
214 const struct GNUNET_DHT_PathElement *path,
215 struct GNUNET_TIME_Absolute exp,
216 const struct GNUNET_HashCode *key,
217 const void *data,
218 size_t size)
219{
220 fprintf (stdout,
221 (GNUNET_BLOCK_TYPE_TEST == type)
222 ? "PUT %u (%s): type %d, key `%s', data `%.*s'\n"
223 : "PUT %u (%s): type %d, key `%s'\n",
224 result_count,
225 GNUNET_STRINGS_absolute_time_to_string (exp),
226 (int) type,
227 GNUNET_h2s_full (key),
228 (unsigned int) size,
229 (char *) data);
230 result_count++;
231}
232
233
234/**
235 * Main function that will be run by the scheduler.
236 *
237 * @param cls closure
238 * @param args remaining command-line arguments
239 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
240 * @param c configuration
241 */
242static void
243run (void *cls,
244 char *const *args,
245 const char *cfgfile,
246 const struct GNUNET_CONFIGURATION_Handle *c)
247{
248 struct GNUNET_HashCode *key;
249 struct GNUNET_HashCode hc;
250
251 cfg = c;
252
253 if (NULL == (dht_handle = GNUNET_DHT_connect (cfg, 1)))
254 {
255 fprintf (stderr, "%s", _ ("Failed to connect to DHT service!\n"));
256 ret = 1;
257 return;
258 }
259 if (GNUNET_BLOCK_TYPE_ANY == block_type) /* Type of data not set */
260 block_type = GNUNET_BLOCK_TYPE_TEST;
261 if (NULL != query_key)
262 {
263 key = &hc;
264 if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (query_key, key))
265 GNUNET_CRYPTO_hash (query_key, strlen (query_key), key);
266 }
267 else
268 {
269 key = NULL;
270 }
271 if (verbose)
272 fprintf (stderr,
273 "Monitoring for %s\n",
274 GNUNET_STRINGS_relative_time_to_string (timeout_request,
275 GNUNET_NO));
276 tt = GNUNET_SCHEDULER_add_delayed (timeout_request, &timeout_task, NULL);
277 GNUNET_SCHEDULER_add_shutdown (&cleanup_task, NULL);
278 monitor_handle = GNUNET_DHT_monitor_start (dht_handle,
279 block_type,
280 key,
281 &get_callback,
282 &get_resp_callback,
283 &put_callback,
284 NULL);
285}
286
287
288/**
289 * Entry point for gnunet-dht-monitor
290 *
291 * @param argc number of arguments from the command line
292 * @param argv command line arguments
293 * @return 0 ok, 1 on error
294 */
295int
296main (int argc, char *const *argv)
297{
298 struct GNUNET_GETOPT_CommandLineOption options[] = {
299 GNUNET_GETOPT_option_string ('k',
300 "key",
301 "KEY",
302 gettext_noop ("the query key"),
303 &query_key),
304
305 GNUNET_GETOPT_option_uint ('t',
306 "type",
307 "TYPE",
308 gettext_noop ("the type of data to look for"),
309 &block_type),
310
311 GNUNET_GETOPT_option_relative_time (
312 'T',
313 "timeout",
314 "TIMEOUT",
315 gettext_noop ("how long should the monitor command run"),
316 &timeout_request),
317
318 GNUNET_GETOPT_option_flag ('V',
319 "verbose",
320 gettext_noop (
321 "be verbose (print progress information)"),
322 &verbose),
323
324 GNUNET_GETOPT_OPTION_END
325 };
326
327
328 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
329 return 2;
330
331 return (GNUNET_OK ==
332 GNUNET_PROGRAM_run (argc,
333 argv,
334 "gnunet-dht-monitor",
335 gettext_noop (
336 "Prints all packets that go through the DHT."),
337 options,
338 &run,
339 NULL))
340 ? ret
341 : 1;
342}
343
344
345/* end of gnunet-dht-monitor.c */
diff --git a/src/dht/gnunet-dht-put.c b/src/dht/gnunet-dht-put.c
deleted file mode 100644
index 531107ef2..000000000
--- a/src/dht/gnunet-dht-put.c
+++ /dev/null
@@ -1,250 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009, 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 * @file dht/gnunet-dht-put.c
22 * @brief search for data in DHT
23 * @author Christian Grothoff
24 * @author Nathan Evans
25 */
26#include "platform.h"
27#include "gnunet_dht_service.h"
28
29/**
30 * The type of the query
31 */
32static unsigned int query_type;
33
34/**
35 * The key used in the DHT
36 */
37struct GNUNET_HashCode key;
38
39/**
40 * The key for the query
41 */
42static char *query_key;
43
44/**
45 * User supplied expiration value
46 */
47static struct GNUNET_TIME_Relative expiration;
48
49/**
50 * Desired replication level.
51 */
52static unsigned int replication = 5;
53
54/**
55 * Be verbose
56 */
57static unsigned int verbose;
58
59/**
60 * Use #GNUNET_DHT_DEMULTIPLEX_EVERYWHERE.
61 */
62static int demultixplex_everywhere;
63
64/**
65 * Use #GNUNET_DHT_RO_RECORD_ROUTE.
66 */
67static int record_route;
68
69/**
70 * Handle to the DHT
71 */
72static struct GNUNET_DHT_Handle *dht_handle;
73
74
75/**
76 * Global handle of the configuration
77 */
78static const struct GNUNET_CONFIGURATION_Handle *cfg;
79
80/**
81 * Global status value
82 */
83static int ret;
84
85/**
86 * The data to insert into the dht
87 */
88static char *data;
89
90
91static void
92shutdown_task (void *cls)
93{
94 if (NULL != dht_handle)
95 {
96 GNUNET_DHT_disconnect (dht_handle);
97 dht_handle = NULL;
98 }
99}
100
101
102/**
103 * Signature of the main function of a task.
104 *
105 * @param cls closure
106 */
107static void
108message_sent_cont (void *cls)
109{
110 GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
111}
112
113
114/**
115 * Main function that will be run by the scheduler.
116 *
117 * @param cls closure
118 * @param args remaining command-line arguments
119 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
120 * @param c configuration
121 */
122static void
123run (void *cls,
124 char *const *args,
125 const char *cfgfile,
126 const struct GNUNET_CONFIGURATION_Handle *c)
127{
128 enum GNUNET_DHT_RouteOption ro;
129
130 cfg = c;
131 if ((NULL == query_key) || (NULL == data))
132 {
133 fprintf (stderr, "%s", _ ("Must provide KEY and DATA for DHT put!\n"));
134 ret = 1;
135 return;
136 }
137
138 if (NULL == (dht_handle = GNUNET_DHT_connect (cfg, 1)))
139 {
140 fprintf (stderr, _ ("Could not connect to DHT service!\n"));
141 ret = 1;
142 return;
143 }
144 if (GNUNET_BLOCK_TYPE_ANY == query_type) /* Type of data not set */
145 query_type = GNUNET_BLOCK_TYPE_TEST;
146
147 GNUNET_CRYPTO_hash (query_key, strlen (query_key), &key);
148
149 if (verbose)
150 fprintf (stderr,
151 _ ("Issuing put request for `%s' with data `%s'!\n"),
152 query_key,
153 data);
154 ro = GNUNET_DHT_RO_NONE;
155 if (demultixplex_everywhere)
156 ro |= GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE;
157 if (record_route)
158 ro |= GNUNET_DHT_RO_RECORD_ROUTE;
159 GNUNET_DHT_put (dht_handle,
160 &key,
161 replication,
162 ro,
163 query_type,
164 strlen (data),
165 data,
166 GNUNET_TIME_relative_to_absolute (expiration),
167 &message_sent_cont,
168 NULL);
169}
170
171
172/**
173 * Entry point for gnunet-dht-put
174 *
175 * @param argc number of arguments from the command line
176 * @param argv command line arguments
177 * @return 0 ok, 1 on error
178 */
179int
180main (int argc, char *const *argv)
181{
182 struct GNUNET_GETOPT_CommandLineOption options[] = {
183 GNUNET_GETOPT_option_string (
184 'd',
185 "data",
186 "DATA",
187 gettext_noop (
188 "the data to insert under the key"),
189 &data),
190 GNUNET_GETOPT_option_relative_time (
191 'e',
192 "expiration",
193 "EXPIRATION",
194 gettext_noop ("how long to store this entry in the dht (in seconds)"),
195 &expiration),
196 GNUNET_GETOPT_option_string (
197 'k',
198 "key",
199 "KEY",
200 gettext_noop ("the query key"),
201 &query_key),
202 GNUNET_GETOPT_option_flag (
203 'x',
204 "demultiplex",
205 gettext_noop (
206 "use DHT's demultiplex everywhere option"),
207 &demultixplex_everywhere),
208 GNUNET_GETOPT_option_uint (
209 'r',
210 "replication",
211 "LEVEL",
212 gettext_noop ("how many replicas to create"),
213 &replication),
214 GNUNET_GETOPT_option_flag (
215 'R',
216 "record",
217 gettext_noop ("use DHT's record route option"),
218 &record_route),
219 GNUNET_GETOPT_option_uint (
220 't',
221 "type",
222 "TYPE",
223 gettext_noop ("the type to insert data as"),
224 &query_type),
225 GNUNET_GETOPT_option_verbose (&verbose),
226 GNUNET_GETOPT_OPTION_END
227 };
228
229
230 if (GNUNET_OK !=
231 GNUNET_STRINGS_get_utf8_args (argc, argv,
232 &argc, &argv))
233 return 2;
234 expiration = GNUNET_TIME_UNIT_HOURS;
235 return (GNUNET_OK ==
236 GNUNET_PROGRAM_run (
237 argc,
238 argv,
239 "gnunet-dht-put",
240 gettext_noop (
241 "Issue a PUT request to the GNUnet DHT insert DATA under KEY."),
242 options,
243 &run,
244 NULL))
245 ? ret
246 : 1;
247}
248
249
250/* end of gnunet-dht-put.c */
diff --git a/src/dht/gnunet-service-dht.c b/src/dht/gnunet-service-dht.c
deleted file mode 100644
index 39433791d..000000000
--- a/src/dht/gnunet-service-dht.c
+++ /dev/null
@@ -1,543 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2011, 2016 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/gnunet-service-dht.c
23 * @brief GNUnet DHT service
24 * @author Christian Grothoff
25 * @author Nathan Evans
26 */
27#include "platform.h"
28#include "gnunet_block_lib.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_hello_lib.h"
31#include "gnunet_hello_uri_lib.h"
32#include "gnunet_dht_service.h"
33#include "gnunet_statistics_service.h"
34#include "gnunet-service-dht.h"
35#include "gnunet-service-dht_datacache.h"
36#include "gnunet-service-dht_neighbours.h"
37#include "gnunet-service-dht_routing.h"
38
39/**
40 * How often do we broadcast our HELLO to neighbours if
41 * nothing special happens?
42 */
43#define HELLO_FREQUENCY GNUNET_TIME_UNIT_HOURS
44
45
46/**
47 * Information we keep per underlay.
48 */
49struct GDS_Underlay
50{
51
52 /**
53 * Kept in a DLL.
54 */
55 struct GDS_Underlay *next;
56
57 /**
58 * Kept in a DLL.
59 */
60 struct GDS_Underlay *prev;
61
62 /**
63 * Environment for this underlay.
64 */
65 struct GNUNET_DHTU_PluginEnvironment env;
66
67 /**
68 * Underlay API handle.
69 */
70 struct GNUNET_DHTU_PluginFunctions *dhtu;
71
72 /**
73 * current network size estimate for this underlay.
74 */
75 double network_size_estimate;
76
77 /**
78 * Name of the underlay (i.e. "gnunet" or "ip").
79 */
80 char *name;
81
82 /**
83 * Name of the library providing the underlay.
84 */
85 char *libname;
86};
87
88
89/**
90 * An address of this peer.
91 */
92struct MyAddress
93{
94 /**
95 * Kept in a DLL.
96 */
97 struct MyAddress *next;
98
99 /**
100 * Kept in a DLL.
101 */
102 struct MyAddress *prev;
103
104 /**
105 * Underlay handle for the address.
106 */
107 struct GNUNET_DHTU_Source *source;
108
109 /**
110 * Textual representation of the address.
111 */
112 char *url;
113
114 /**
115 * Underlay of this address.
116 */
117 struct GDS_Underlay *u;
118};
119
120
121/**
122 * Our HELLO
123 */
124struct GNUNET_HELLO_Builder *GDS_my_hello;
125
126/**
127 * Identity of this peer.
128 */
129struct GNUNET_PeerIdentity GDS_my_identity;
130
131/**
132 * Hash of the identity of this peer.
133 */
134struct GNUNET_HashCode GDS_my_identity_hash;
135
136/**
137 * Our private key.
138 */
139struct GNUNET_CRYPTO_EddsaPrivateKey GDS_my_private_key;
140
141/**
142 * Task broadcasting our HELLO.
143 */
144static struct GNUNET_SCHEDULER_Task *hello_task;
145
146/**
147 * Handles for the DHT underlays.
148 */
149static struct GDS_Underlay *u_head;
150
151/**
152 * Handles for the DHT underlays.
153 */
154static struct GDS_Underlay *u_tail;
155
156/**
157 * Head of addresses of this peer.
158 */
159static struct MyAddress *a_head;
160
161/**
162 * Tail of addresses of this peer.
163 */
164static struct MyAddress *a_tail;
165
166/**
167 * log of the current network size estimate, used as the point where
168 * we switch between random and deterministic routing.
169 */
170static double log_of_network_size_estimate;
171
172
173/**
174 * Callback that is called when network size estimate is updated.
175 *
176 * @param cls a `struct GDS_Underlay`
177 * @param timestamp time when the estimate was received from the server (or created by the server)
178 * @param logestimate the log(Base 2) value of the current network size estimate
179 * @param std_dev standard deviation for the estimate
180 *
181 */
182static void
183update_network_size_estimate (void *cls,
184 struct GNUNET_TIME_Absolute timestamp,
185 double logestimate,
186 double std_dev)
187{
188 struct GDS_Underlay *u = cls;
189 double sum = 0.0;
190
191 GNUNET_STATISTICS_update (GDS_stats,
192 "# Network size estimates received",
193 1,
194 GNUNET_NO);
195 /* do not allow estimates < 0.5 */
196 u->network_size_estimate = pow (2.0,
197 GNUNET_MAX (0.5,
198 logestimate));
199 for (struct GDS_Underlay *p = u_head; NULL != p; p = p->next)
200 sum += p->network_size_estimate;
201 if (sum <= 2.0)
202 log_of_network_size_estimate = 0.5;
203 else
204 log_of_network_size_estimate = log2 (sum);
205}
206
207
208/**
209 * Return the current NSE
210 *
211 * @return the current NSE as a logarithm
212 */
213double
214GDS_NSE_get (void)
215{
216 return log_of_network_size_estimate;
217}
218
219
220#include "gnunet-service-dht_clients.c"
221
222
223/**
224 * Task run periodically to broadcast our HELLO.
225 *
226 * @param cls NULL
227 */
228static void
229broadcast_hello (void *cls)
230{
231 struct GNUNET_MessageHeader *hello;
232
233 (void) cls;
234 /* TODO: randomize! */
235 hello_task = GNUNET_SCHEDULER_add_delayed (HELLO_FREQUENCY,
236 &broadcast_hello,
237 NULL);
238 hello = GNUNET_HELLO_builder_to_dht_hello_msg (GDS_my_hello,
239 &GDS_my_private_key);
240 if (NULL == hello)
241 {
242 GNUNET_break (0);
243 return;
244 }
245 GDS_NEIGHBOURS_broadcast (hello);
246 GNUNET_free (hello);
247}
248
249
250/**
251 * Function to call with new addresses of this peer.
252 *
253 * @param cls the closure
254 * @param address address under which we are likely reachable,
255 * pointer will remain valid until @e address_del_cb is called; to be used for HELLOs. Example: "ip+udp://$PID/1.1.1.1:2086/"
256 * @param source handle for sending from this address, NULL if we can only receive
257 * @param[out] ctx storage space for DHT to use in association with this address
258 */
259static void
260u_address_add (void *cls,
261 const char *address,
262 struct GNUNET_DHTU_Source *source,
263 void **ctx)
264{
265 struct GDS_Underlay *u = cls;
266 struct MyAddress *a;
267
268 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
269 "Underlay adds address %s for this peer\n",
270 address);
271 a = GNUNET_new (struct MyAddress);
272 a->source = source;
273 a->url = GNUNET_strdup (address);
274 a->u = u;
275 GNUNET_CONTAINER_DLL_insert (a_head,
276 a_tail,
277 a);
278 *ctx = a;
279 GNUNET_HELLO_builder_add_address (GDS_my_hello,
280 address);
281 if (NULL != hello_task)
282 GNUNET_SCHEDULER_cancel (hello_task);
283 hello_task = GNUNET_SCHEDULER_add_now (&broadcast_hello,
284 NULL);
285}
286
287
288/**
289 * Function to call with expired addresses of this peer.
290 *
291 * @param[in] ctx storage space used by the DHT in association with this address
292 */
293static void
294u_address_del (void *ctx)
295{
296 struct MyAddress *a = ctx;
297
298 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
299 "Underlay deletes address %s for this peer\n",
300 a->url);
301 GNUNET_HELLO_builder_del_address (GDS_my_hello,
302 a->url);
303 GNUNET_CONTAINER_DLL_remove (a_head,
304 a_tail,
305 a);
306 GNUNET_free (a->url);
307 GNUNET_free (a);
308 if (NULL != hello_task)
309 GNUNET_SCHEDULER_cancel (hello_task);
310 hello_task = GNUNET_SCHEDULER_add_now (&broadcast_hello,
311 NULL);
312}
313
314
315void
316GDS_u_try_connect (const struct GNUNET_PeerIdentity *pid,
317 const char *address)
318{
319 for (struct GDS_Underlay *u = u_head;
320 NULL != u;
321 u = u->next)
322 u->dhtu->try_connect (u->dhtu->cls,
323 pid,
324 address);
325}
326
327
328void
329GDS_u_send (struct GDS_Underlay *u,
330 struct GNUNET_DHTU_Target *target,
331 const void *msg,
332 size_t msg_size,
333 GNUNET_SCHEDULER_TaskCallback finished_cb,
334 void *finished_cb_cls)
335{
336 u->dhtu->send (u->dhtu->cls,
337 target,
338 msg,
339 msg_size,
340 finished_cb,
341 finished_cb_cls);
342}
343
344
345void
346GDS_u_drop (struct GDS_Underlay *u,
347 struct GNUNET_DHTU_PreferenceHandle *ph)
348{
349 u->dhtu->drop (ph);
350}
351
352
353struct GNUNET_DHTU_PreferenceHandle *
354GDS_u_hold (struct GDS_Underlay *u,
355 struct GNUNET_DHTU_Target *target)
356{
357 return u->dhtu->hold (u->dhtu->cls,
358 target);
359}
360
361
362/**
363 * Task run during shutdown.
364 *
365 * @param cls unused
366 */
367static void
368shutdown_task (void *cls)
369{
370 struct GDS_Underlay *u;
371
372 while (NULL != (u = u_head))
373 {
374 GNUNET_PLUGIN_unload (u->libname,
375 u->dhtu);
376 GNUNET_CONTAINER_DLL_remove (u_head,
377 u_tail,
378 u);
379 GNUNET_free (u->name);
380 GNUNET_free (u->libname);
381 GNUNET_free (u);
382 }
383 GDS_NEIGHBOURS_done ();
384 GDS_DATACACHE_done ();
385 GDS_ROUTING_done ();
386 if (NULL != GDS_block_context)
387 {
388 GNUNET_BLOCK_context_destroy (GDS_block_context);
389 GDS_block_context = NULL;
390 }
391 GDS_CLIENTS_stop ();
392 if (NULL != GDS_stats)
393 {
394 GNUNET_STATISTICS_destroy (GDS_stats,
395 GNUNET_YES);
396 GDS_stats = NULL;
397 }
398 if (NULL != GDS_my_hello)
399 {
400 GNUNET_HELLO_builder_free (GDS_my_hello);
401 GDS_my_hello = NULL;
402 }
403 if (NULL != hello_task)
404 {
405 GNUNET_SCHEDULER_cancel (hello_task);
406 hello_task = NULL;
407 }
408}
409
410
411/**
412 * Function iterating over all configuration sections.
413 * Loads plugins for enabled DHT underlays.
414 *
415 * @param cls NULL
416 * @param section configuration section to inspect
417 */
418static void
419load_underlay (void *cls,
420 const char *section)
421{
422 struct GDS_Underlay *u;
423 char *libname;
424
425 (void) cls;
426 if (0 != strncasecmp (section,
427 "dhtu-",
428 strlen ("dhtu-")))
429 return;
430 if (GNUNET_YES !=
431 GNUNET_CONFIGURATION_get_value_yesno (GDS_cfg,
432 section,
433 "ENABLED"))
434 return;
435 section += strlen ("dhtu-");
436 u = GNUNET_new (struct GDS_Underlay);
437 u->env.cls = u;
438 u->env.cfg = GDS_cfg;
439 u->env.address_add_cb = &u_address_add;
440 u->env.address_del_cb = &u_address_del;
441 u->env.network_size_cb = &update_network_size_estimate;
442 u->env.connect_cb = &GDS_u_connect;
443 u->env.disconnect_cb = &GDS_u_disconnect;
444 u->env.receive_cb = &GDS_u_receive;
445 GNUNET_asprintf (&libname,
446 "libgnunet_plugin_dhtu_%s",
447 section);
448 u->dhtu = GNUNET_PLUGIN_load (libname,
449 &u->env);
450 if (NULL == u->dhtu)
451 {
452 GNUNET_free (libname);
453 GNUNET_free (u);
454 return;
455 }
456 u->libname = libname;
457 u->name = GNUNET_strdup (section);
458 GNUNET_CONTAINER_DLL_insert (u_head,
459 u_tail,
460 u);
461}
462
463
464/**
465 * Process dht requests.
466 *
467 * @param cls closure
468 * @param c configuration to use
469 * @param service the initialized service
470 */
471static void
472run (void *cls,
473 const struct GNUNET_CONFIGURATION_Handle *c,
474 struct GNUNET_SERVICE_Handle *service)
475{
476 GDS_cfg = c;
477 GDS_service = service;
478 {
479 char *keyfile;
480
481 if (GNUNET_OK !=
482 GNUNET_CONFIGURATION_get_value_filename (GDS_cfg,
483 "PEER",
484 "PRIVATE_KEY",
485 &keyfile))
486 {
487 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
488 "PEER",
489 "PRIVATE_KEY");
490 GNUNET_SCHEDULER_shutdown ();
491 return;
492 }
493 if (GNUNET_SYSERR ==
494 GNUNET_CRYPTO_eddsa_key_from_file (keyfile,
495 GNUNET_YES,
496 &GDS_my_private_key))
497 {
498 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
499 "Failed to setup peer's private key\n");
500 GNUNET_free (keyfile);
501 GNUNET_SCHEDULER_shutdown ();
502 return;
503 }
504 GNUNET_free (keyfile);
505 }
506 GNUNET_CRYPTO_eddsa_key_get_public (&GDS_my_private_key,
507 &GDS_my_identity.public_key);
508 GDS_my_hello = GNUNET_HELLO_builder_new (&GDS_my_identity);
509 GNUNET_CRYPTO_hash (&GDS_my_identity,
510 sizeof(struct GNUNET_PeerIdentity),
511 &GDS_my_identity_hash);
512 GDS_block_context = GNUNET_BLOCK_context_create (GDS_cfg);
513 GDS_stats = GNUNET_STATISTICS_create ("dht",
514 GDS_cfg);
515 GDS_CLIENTS_init ();
516 GDS_ROUTING_init ();
517 GDS_DATACACHE_init ();
518 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
519 NULL);
520 if (GNUNET_OK !=
521 GDS_NEIGHBOURS_init ())
522 {
523 GNUNET_SCHEDULER_shutdown ();
524 return;
525 }
526 GNUNET_CONFIGURATION_iterate_sections (GDS_cfg,
527 &load_underlay,
528 NULL);
529 if (NULL == u_head)
530 {
531 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
532 "No DHT underlays configured!\n");
533 GNUNET_SCHEDULER_shutdown ();
534 return;
535 }
536}
537
538
539/* Finally, define the main method */
540GDS_DHT_SERVICE_INIT ("dht", &run);
541
542
543/* end of gnunet-service-dht.c */
diff --git a/src/dht/gnunet-service-dht.h b/src/dht/gnunet-service-dht.h
deleted file mode 100644
index ecb79fa50..000000000
--- a/src/dht/gnunet-service-dht.h
+++ /dev/null
@@ -1,213 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2016 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/gnunet-service-dht.h
23 * @brief GNUnet DHT globals
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_DHT_H
27#define GNUNET_SERVICE_DHT_H
28
29#include "gnunet-service-dht_datacache.h"
30#include "gnunet-service-dht_neighbours.h"
31#include "gnunet_statistics_service.h"
32#include "gnunet_transport_service.h"
33
34
35#define DEBUG_DHT GNUNET_EXTRA_LOGGING
36
37/**
38 * Information we keep per underlay.
39 */
40struct GDS_Underlay;
41
42/**
43 * Configuration we use.
44 */
45extern const struct GNUNET_CONFIGURATION_Handle *GDS_cfg;
46
47/**
48 * Handle for the service.
49 */
50extern struct GNUNET_SERVICE_Handle *GDS_service;
51
52/**
53 * Our handle to the BLOCK library.
54 */
55extern struct GNUNET_BLOCK_Context *GDS_block_context;
56
57/**
58 * Handle for the statistics service.
59 */
60extern struct GNUNET_STATISTICS_Handle *GDS_stats;
61
62/**
63 * Our HELLO builder.
64 */
65extern struct GNUNET_HELLO_Builder *GDS_my_hello;
66
67/**
68 * Identity of this peer.
69 */
70extern struct GNUNET_PeerIdentity GDS_my_identity;
71
72/**
73 * Hash of the identity of this peer.
74 */
75extern struct GNUNET_HashCode GDS_my_identity_hash;
76
77/**
78 * Our private key.
79 */
80extern struct GNUNET_CRYPTO_EddsaPrivateKey GDS_my_private_key;
81
82
83/**
84 * Ask all underlays to connect to peer @a pid at @a address.
85 *
86 * @param pid identity of the peer we would connect to
87 * @param address an address of @a pid
88 */
89void
90GDS_u_try_connect (const struct GNUNET_PeerIdentity *pid,
91 const char *address);
92
93
94/**
95 * Send message to some other participant over the network. Note that
96 * sending is not guaranteeing that the other peer actually received the
97 * message. For any given @a target, the DHT must wait for the @a
98 * finished_cb to be called before calling send() again.
99 *
100 * @param u underlay to use for transmission
101 * @param target receiver identification
102 * @param msg message
103 * @param msg_size number of bytes in @a msg
104 * @param finished_cb function called once transmission is done
105 * (not called if @a target disconnects, then only the
106 * disconnect_cb is called).
107 * @param finished_cb_cls closure for @a finished_cb
108 */
109void
110GDS_u_send (struct GDS_Underlay *u,
111 struct GNUNET_DHTU_Target *target,
112 const void *msg,
113 size_t msg_size,
114 GNUNET_SCHEDULER_TaskCallback finished_cb,
115 void *finished_cb_cls);
116
117
118/**
119 * Drop a hold @a ph from underlay @a u.
120 *
121 * @param u the underlay controlling the hold
122 * @param ph the preference handle
123 */
124void
125GDS_u_drop (struct GDS_Underlay *u,
126 struct GNUNET_DHTU_PreferenceHandle *ph);
127
128
129/**
130 * Create a hold on @a target at underlay @a u.
131 *
132 * @param u the underlay controlling the target
133 * @param target the peer to hold the connection to
134 */
135struct GNUNET_DHTU_PreferenceHandle *
136GDS_u_hold (struct GDS_Underlay *u,
137 struct GNUNET_DHTU_Target *target);
138
139
140/**
141 * Handle a reply we've received from another peer. If the reply
142 * matches any of our pending queries, forward it to the respective
143 * client(s).
144 *
145 * @param bd block details
146 * @param query_hash hash of the original query, might not match key in @a bd
147 * @param trunc_peer peer at which the path was truncated, or NULL if path starts at the origin
148 * @param get_path_length number of entries in @a get_path
149 * @param get_path path the reply has taken
150 * @return true on success, false on failures
151 */
152bool
153GDS_CLIENTS_handle_reply (const struct GNUNET_DATACACHE_Block *bd,
154 const struct GNUNET_HashCode *query_hash,
155 unsigned int get_path_length,
156 const struct GNUNET_DHT_PathElement *get_path);
157
158
159/**
160 * Check if some client is monitoring GET messages and notify
161 * them in that case.
162 *
163 * @param options Options, for instance RecordRoute, DemultiplexEverywhere.
164 * @param type The type of data in the request.
165 * @param hop_count Hop count so far.
166 * @param desired_replication_level Desired replication level.
167 * @param key Key of the requested data.
168 */
169void
170GDS_CLIENTS_process_get (enum GNUNET_DHT_RouteOption options,
171 enum GNUNET_BLOCK_Type type,
172 uint32_t hop_count,
173 uint32_t desired_replication_level,
174 const struct GNUNET_HashCode *key);
175
176
177/**
178 * Check if some client is monitoring GET RESP messages and notify
179 * them in that case.
180 *
181 * @param bd block details
182 * @param get_path Peers on GET path (or NULL if not recorded).
183 * @param get_path_length number of entries in @a get_path.
184 */
185void
186GDS_CLIENTS_process_get_resp (const struct GNUNET_DATACACHE_Block *bd,
187 const struct GNUNET_DHT_PathElement *get_path,
188 unsigned int get_path_length);
189
190
191/**
192 * Check if some client is monitoring PUT messages and notify
193 * them in that case. The @a path should include our own
194 * peer ID (if recorded).
195 *
196 * @param bd details about the block
197 * @param hop_count Hop count so far.
198 * @param desired_replication_level Desired replication level.
199 */
200void
201GDS_CLIENTS_process_put (const struct GNUNET_DATACACHE_Block *bd,
202 uint32_t hop_count,
203 uint32_t desired_replication_level);
204
205/**
206 * Return the current NSE
207 *
208 * @return the current NSE as a logarithm
209 */
210double
211GDS_NSE_get (void);
212
213#endif
diff --git a/src/dht/gnunet-service-dht_clients.c b/src/dht/gnunet-service-dht_clients.c
deleted file mode 100644
index fdcc31f13..000000000
--- a/src/dht/gnunet-service-dht_clients.c
+++ /dev/null
@@ -1,1698 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2011, 2016, 2017, 2022 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file dht/gnunet-service-dht_clients.c
23 * @brief GNUnet DHT service's client management code
24 * @author Christian Grothoff
25 * @author Nathan Evans
26 */
27#include "platform.h"
28#include "gnunet_constants.h"
29#include "gnunet_protocols.h"
30#include "gnunet_statistics_service.h"
31#include "gnunet-service-dht.h"
32#include "gnunet-service-dht_datacache.h"
33#include "gnunet-service-dht_neighbours.h"
34#include "dht.h"
35
36
37/**
38 * Enable slow sanity checks to debug issues.
39 * 0: do not check
40 * 1: check all external inputs
41 * 2: check internal computations as well
42 */
43#define SANITY_CHECKS 2
44
45/**
46 * Should routing details be logged to stderr (for debugging)?
47 */
48#define LOG_TRAFFIC(kind, ...) GNUNET_log_from (kind, "dht-traffic", \
49 __VA_ARGS__)
50
51#define LOG(kind, ...) GNUNET_log_from (kind, "dht-clients", __VA_ARGS__)
52
53
54/**
55 * Struct containing information about a client,
56 * handle to connect to it, and any pending messages
57 * that need to be sent to it.
58 */
59struct ClientHandle;
60
61
62/**
63 * Entry in the local forwarding map for a client's GET request.
64 */
65struct ClientQueryRecord
66{
67 /**
68 * The key this request was about
69 */
70 struct GNUNET_HashCode key;
71
72 /**
73 * Kept in a DLL with @e client.
74 */
75 struct ClientQueryRecord *next;
76
77 /**
78 * Kept in a DLL with @e client.
79 */
80 struct ClientQueryRecord *prev;
81
82 /**
83 * Client responsible for the request.
84 */
85 struct ClientHandle *ch;
86
87 /**
88 * Extended query (see gnunet_block_lib.h), allocated at the end of this struct.
89 */
90 const void *xquery;
91
92 /**
93 * Array of (hashes of) replies we have already seen for this request.
94 */
95 struct GNUNET_HashCode *seen_replies;
96
97 /**
98 * Pointer to this nodes heap location in the retry-heap (for fast removal)
99 */
100 struct GNUNET_CONTAINER_HeapNode *hnode;
101
102 /**
103 * What's the delay between re-try operations that we currently use for this
104 * request?
105 */
106 struct GNUNET_TIME_Relative retry_frequency;
107
108 /**
109 * What's the next time we should re-try this request?
110 */
111 struct GNUNET_TIME_Absolute retry_time;
112
113 /**
114 * The unique identifier of this request
115 */
116 uint64_t unique_id;
117
118 /**
119 * Number of bytes in xquery.
120 */
121 size_t xquery_size;
122
123 /**
124 * Number of entries in @e seen_replies.
125 */
126 unsigned int seen_replies_count;
127
128 /**
129 * Desired replication level
130 */
131 uint32_t replication;
132
133 /**
134 * Any message options for this request
135 */
136 enum GNUNET_DHT_RouteOption msg_options;
137
138 /**
139 * The type for the data for the GET request.
140 */
141 enum GNUNET_BLOCK_Type type;
142};
143
144
145/**
146 * Struct containing parameters of monitoring requests.
147 */
148struct ClientMonitorRecord
149{
150 /**
151 * Next element in DLL.
152 */
153 struct ClientMonitorRecord *next;
154
155 /**
156 * Previous element in DLL.
157 */
158 struct ClientMonitorRecord *prev;
159
160 /**
161 * Client to notify of these requests.
162 */
163 struct ClientHandle *ch;
164
165 /**
166 * Key of data of interest. All bits zero for 'all'.
167 */
168 struct GNUNET_HashCode key;
169
170 /**
171 * Type of blocks that are of interest
172 */
173 enum GNUNET_BLOCK_Type type;
174
175 /**
176 * Flag whether to notify about GET messages.
177 */
178 int16_t get;
179
180 /**
181 * Flag whether to notify about GET_REPONSE messages.
182 */
183 int16_t get_resp;
184
185 /**
186 * Flag whether to notify about PUT messages.
187 */
188 uint16_t put;
189
190};
191
192
193/**
194 * Struct containing information about a client,
195 * handle to connect to it, and any pending messages
196 * that need to be sent to it.
197 */
198struct ClientHandle
199{
200 /**
201 * Linked list of active queries of this client.
202 */
203 struct ClientQueryRecord *cqr_head;
204
205 /**
206 * Linked list of active queries of this client.
207 */
208 struct ClientQueryRecord *cqr_tail;
209
210 /**
211 * The handle to this client
212 */
213 struct GNUNET_SERVICE_Client *client;
214
215 /**
216 * The message queue to this client
217 */
218 struct GNUNET_MQ_Handle *mq;
219};
220
221
222/**
223 * Our handle to the BLOCK library.
224 */
225struct GNUNET_BLOCK_Context *GDS_block_context;
226
227/**
228 * Handle for the statistics service.
229 */
230struct GNUNET_STATISTICS_Handle *GDS_stats;
231
232/**
233 * Handle for the service.
234 */
235struct GNUNET_SERVICE_Handle *GDS_service;
236
237/**
238 * The configuration the DHT service is running with
239 */
240const struct GNUNET_CONFIGURATION_Handle *GDS_cfg;
241
242/**
243 * List of active monitoring requests.
244 */
245static struct ClientMonitorRecord *monitor_head;
246
247/**
248 * List of active monitoring requests.
249 */
250static struct ClientMonitorRecord *monitor_tail;
251
252/**
253 * Hashmap for fast key based lookup, maps keys to `struct ClientQueryRecord` entries.
254 */
255static struct GNUNET_CONTAINER_MultiHashMap *forward_map;
256
257/**
258 * Heap with all of our client's request, sorted by retry time (earliest on top).
259 */
260static struct GNUNET_CONTAINER_Heap *retry_heap;
261
262/**
263 * Task that re-transmits requests (using retry_heap).
264 */
265static struct GNUNET_SCHEDULER_Task *retry_task;
266
267
268/**
269 * Free data structures associated with the given query.
270 *
271 * @param record record to remove
272 */
273static void
274remove_client_query_record (struct ClientQueryRecord *record)
275{
276 struct ClientHandle *ch = record->ch;
277
278 GNUNET_CONTAINER_DLL_remove (ch->cqr_head,
279 ch->cqr_tail,
280 record);
281 GNUNET_assert (GNUNET_YES ==
282 GNUNET_CONTAINER_multihashmap_remove (forward_map,
283 &record->key,
284 record));
285 if (NULL != record->hnode)
286 GNUNET_CONTAINER_heap_remove_node (record->hnode);
287 GNUNET_array_grow (record->seen_replies,
288 record->seen_replies_count,
289 0);
290 GNUNET_free (record);
291}
292
293
294/**
295 * Functions with this signature are called whenever a local client is
296 * connects to us.
297 *
298 * @param cls closure (NULL for dht)
299 * @param client identification of the client
300 * @param mq message queue for talking to @a client
301 * @return our `struct ClientHandle` for @a client
302 */
303static void *
304client_connect_cb (void *cls,
305 struct GNUNET_SERVICE_Client *client,
306 struct GNUNET_MQ_Handle *mq)
307{
308 struct ClientHandle *ch;
309
310 (void) cls;
311 ch = GNUNET_new (struct ClientHandle);
312 ch->client = client;
313 ch->mq = mq;
314 return ch;
315}
316
317
318/**
319 * Functions with this signature are called whenever a client
320 * is disconnected on the network level.
321 *
322 * @param cls closure (NULL for dht)
323 * @param client identification of the client
324 * @param app_ctx our `struct ClientHandle` for @a client
325 */
326static void
327client_disconnect_cb (void *cls,
328 struct GNUNET_SERVICE_Client *client,
329 void *app_ctx)
330{
331 struct ClientHandle *ch = app_ctx;
332
333 (void) cls;
334 (void) client;
335 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
336 "Local client %p disconnects\n",
337 ch);
338 {
339 struct ClientMonitorRecord *next;
340
341 for (struct ClientMonitorRecord *monitor = monitor_head;
342 NULL != monitor;
343 monitor = next)
344 {
345 next = monitor->next;
346 if (monitor->ch != ch)
347 continue;
348 GNUNET_CONTAINER_DLL_remove (monitor_head,
349 monitor_tail,
350 monitor);
351 GNUNET_free (monitor);
352 }
353 }
354
355 {
356 struct ClientQueryRecord *cqr;
357
358 while (NULL != (cqr = ch->cqr_head))
359 remove_client_query_record (cqr);
360 }
361 GNUNET_free (ch);
362}
363
364
365/**
366 * Route the given request via the DHT. This includes updating
367 * the bloom filter and retransmission times, building the P2P
368 * message and initiating the routing operation.
369 *
370 * @param cqr request to transmit
371 */
372static void
373transmit_request (struct ClientQueryRecord *cqr)
374{
375 struct GNUNET_BLOCK_Group *bg;
376 struct GNUNET_CONTAINER_BloomFilter *peer_bf;
377
378 GNUNET_STATISTICS_update (GDS_stats,
379 "# GET requests from clients injected",
380 1,
381 GNUNET_NO);
382 bg = GNUNET_BLOCK_group_create (GDS_block_context,
383 cqr->type,
384 NULL, /* raw data */
385 0, /* raw data size */
386 "seen-set-size",
387 cqr->seen_replies_count,
388 NULL);
389 GNUNET_BLOCK_group_set_seen (bg,
390 cqr->seen_replies,
391 cqr->seen_replies_count);
392 peer_bf
393 = GNUNET_CONTAINER_bloomfilter_init (NULL,
394 DHT_BLOOM_SIZE,
395 GNUNET_CONSTANTS_BLOOMFILTER_K);
396 LOG (GNUNET_ERROR_TYPE_DEBUG,
397 "Initiating GET for %s, replication %u, already have %u replies\n",
398 GNUNET_h2s (&cqr->key),
399 cqr->replication,
400 cqr->seen_replies_count);
401 GDS_NEIGHBOURS_handle_get (cqr->type,
402 cqr->msg_options,
403 cqr->replication,
404 0 /* hop count */,
405 &cqr->key,
406 cqr->xquery,
407 cqr->xquery_size,
408 bg,
409 peer_bf);
410 GNUNET_BLOCK_group_destroy (bg);
411 GNUNET_CONTAINER_bloomfilter_free (peer_bf);
412
413 /* Exponential back-off for retries.
414 * max. is #GNUNET_TIME_STD_EXPONENTIAL_BACKOFF_THRESHOLD (15 min) */
415 cqr->retry_frequency = GNUNET_TIME_STD_BACKOFF (cqr->retry_frequency);
416 cqr->retry_time = GNUNET_TIME_relative_to_absolute (cqr->retry_frequency);
417}
418
419
420/**
421 * Task that looks at the #retry_heap and transmits all of the requests
422 * on the heap that are ready for transmission. Then re-schedules
423 * itself (unless the heap is empty).
424 *
425 * @param cls unused
426 */
427static void
428transmit_next_request_task (void *cls)
429{
430 struct ClientQueryRecord *cqr;
431
432 (void) cls;
433 retry_task = NULL;
434 while (NULL != (cqr = GNUNET_CONTAINER_heap_remove_root (retry_heap)))
435 {
436 cqr->hnode = NULL;
437 if (! GNUNET_TIME_absolute_is_past (cqr->retry_time))
438 {
439 cqr->hnode
440 = GNUNET_CONTAINER_heap_insert (retry_heap,
441 cqr,
442 cqr->retry_time.abs_value_us);
443 retry_task
444 = GNUNET_SCHEDULER_add_at (cqr->retry_time,
445 &transmit_next_request_task,
446 NULL);
447 return;
448 }
449 transmit_request (cqr);
450 cqr->hnode
451 = GNUNET_CONTAINER_heap_insert (retry_heap,
452 cqr,
453 cqr->retry_time.abs_value_us);
454 }
455}
456
457
458/**
459 * Check DHT PUT messages from the client.
460 *
461 * @param cls the client we received this message from
462 * @param dht_msg the actual message received
463 * @return #GNUNET_OK (always)
464 */
465static enum GNUNET_GenericReturnValue
466check_dht_local_put (void *cls,
467 const struct GNUNET_DHT_ClientPutMessage *dht_msg)
468{
469 uint32_t replication_level = ntohl (dht_msg->desired_replication_level);
470
471 (void) cls;
472 if (replication_level > GNUNET_DHT_MAXIMUM_REPLICATION_LEVEL)
473 {
474 GNUNET_break_op (0);
475 return GNUNET_SYSERR;
476 }
477 return GNUNET_OK;
478}
479
480
481/**
482 * Handler for PUT messages.
483 *
484 * @param cls the client we received this message from
485 * @param dht_msg the actual message received
486 */
487static void
488handle_dht_local_put (void *cls,
489 const struct GNUNET_DHT_ClientPutMessage *dht_msg)
490{
491 struct ClientHandle *ch = cls;
492 uint16_t size = ntohs (dht_msg->header.size);
493 uint32_t replication_level
494 = ntohl (dht_msg->desired_replication_level);
495 struct GNUNET_DATACACHE_Block bd = {
496 .key = dht_msg->key,
497 .expiration_time = GNUNET_TIME_absolute_ntoh (dht_msg->expiration),
498 .data = &dht_msg[1],
499 .data_size = size - sizeof (*dht_msg),
500 .type = ntohl (dht_msg->type),
501 .ro = (enum GNUNET_DHT_RouteOption) ntohl (dht_msg->options)
502 };
503
504 LOG (GNUNET_ERROR_TYPE_DEBUG,
505 "Handling local PUT of %lu-bytes for query %s of type %u\n",
506 (unsigned long) (size - sizeof(struct GNUNET_DHT_ClientPutMessage)),
507 GNUNET_h2s (&dht_msg->key),
508 (unsigned int) bd.type);
509 if (GNUNET_OK !=
510 GNUNET_BLOCK_check_block (GDS_block_context,
511 bd.type,
512 bd.data,
513 bd.data_size))
514 {
515 GNUNET_break (0);
516 return;
517 }
518 GNUNET_STATISTICS_update (GDS_stats,
519 "# PUT requests received from clients",
520 1,
521 GNUNET_NO);
522 LOG_TRAFFIC (GNUNET_ERROR_TYPE_DEBUG,
523 "CLIENT-PUT %s\n",
524 GNUNET_h2s_full (&dht_msg->key));
525 /* give to local clients */
526 GNUNET_break (GDS_CLIENTS_handle_reply (&bd,
527 &bd.key,
528 0, NULL /* get path */));
529
530 {
531 struct GNUNET_CONTAINER_BloomFilter *peer_bf;
532
533 peer_bf
534 = GNUNET_CONTAINER_bloomfilter_init (NULL,
535 DHT_BLOOM_SIZE,
536 GNUNET_CONSTANTS_BLOOMFILTER_K);
537 /* store locally */
538 if ( (0 != (bd.ro & GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE)) ||
539 (GDS_am_closest_peer (&dht_msg->key,
540 peer_bf)))
541 GDS_DATACACHE_handle_put (&bd);
542 /* route to other peers */
543 if (GNUNET_OK !=
544 GDS_NEIGHBOURS_handle_put (&bd,
545 replication_level,
546 0 /* hop count */,
547 peer_bf))
548 {
549 GNUNET_STATISTICS_update (GDS_stats,
550 "# Local PUT requests not routed",
551 1,
552 GNUNET_NO);
553 }
554 GNUNET_CONTAINER_bloomfilter_free (peer_bf);
555 }
556 GDS_CLIENTS_process_put (
557 &bd,
558 0, /* hop count */
559 replication_level);
560 GNUNET_SERVICE_client_continue (ch->client);
561}
562
563
564/**
565 * Handle a result from local datacache for a GET operation.
566 *
567 * @param cls the `struct ClientHandle` of the client doing the query
568 * @param bd details about the block that was found
569 */
570static void
571handle_local_result (void *cls,
572 const struct GNUNET_DATACACHE_Block *bd)
573{
574 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
575 "Datacache provided result for query key %s\n",
576 GNUNET_h2s (&bd->key));
577 GNUNET_break (GDS_CLIENTS_handle_reply (bd,
578 &bd->key,
579 0, NULL /* get_path */));
580}
581
582
583/**
584 * Check DHT GET messages from the client.
585 *
586 * @param cls the client we received this message from
587 * @param message the actual message received
588 * @return #GNUNET_OK (always)
589 */
590static enum GNUNET_GenericReturnValue
591check_dht_local_get (void *cls,
592 const struct GNUNET_DHT_ClientGetMessage *get)
593{
594 (void) cls;
595 (void) get;
596 /* always well-formed */
597 return GNUNET_OK;
598}
599
600
601/**
602 * Handler for DHT GET messages from the client.
603 *
604 * @param cls the client we received this message from
605 * @param message the actual message received
606 */
607static void
608handle_dht_local_get (void *cls,
609 const struct GNUNET_DHT_ClientGetMessage *get)
610{
611 struct ClientHandle *ch = cls;
612 struct ClientQueryRecord *cqr;
613 uint16_t size = ntohs (get->header.size);
614 const char *xquery = (const char *) &get[1];
615 size_t xquery_size = size - sizeof(struct GNUNET_DHT_ClientGetMessage);
616
617 LOG (GNUNET_ERROR_TYPE_DEBUG,
618 "Received GET request for %s from local client %p, xq: %.*s\n",
619 GNUNET_h2s (&get->key),
620 ch->client,
621 (int) xquery_size,
622 xquery);
623 GNUNET_STATISTICS_update (GDS_stats,
624 "# GET requests received from clients",
625 1,
626 GNUNET_NO);
627 LOG_TRAFFIC (GNUNET_ERROR_TYPE_DEBUG,
628 "CLIENT-GET %s\n",
629 GNUNET_h2s_full (&get->key));
630
631 cqr = GNUNET_malloc (sizeof(struct ClientQueryRecord) + xquery_size);
632 cqr->key = get->key;
633 cqr->ch = ch;
634 cqr->xquery = (const void *) &cqr[1];
635 GNUNET_memcpy (&cqr[1],
636 xquery,
637 xquery_size);
638 cqr->hnode = GNUNET_CONTAINER_heap_insert (retry_heap,
639 cqr,
640 0);
641 cqr->retry_frequency = GNUNET_TIME_UNIT_SECONDS;
642 cqr->retry_time = GNUNET_TIME_absolute_get ();
643 cqr->unique_id = get->unique_id;
644 cqr->xquery_size = xquery_size;
645 cqr->replication = ntohl (get->desired_replication_level);
646 cqr->msg_options = (enum GNUNET_DHT_RouteOption) ntohl (get->options);
647 cqr->type = ntohl (get->type);
648 GNUNET_CONTAINER_DLL_insert (ch->cqr_head,
649 ch->cqr_tail,
650 cqr);
651 GNUNET_CONTAINER_multihashmap_put (forward_map,
652 &cqr->key,
653 cqr,
654 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
655 GDS_CLIENTS_process_get (cqr->msg_options,
656 cqr->type,
657 0, /* hop count */
658 cqr->replication,
659 &get->key);
660 /* start remote requests */
661 if (NULL != retry_task)
662 GNUNET_SCHEDULER_cancel (retry_task);
663 retry_task = GNUNET_SCHEDULER_add_now (&transmit_next_request_task,
664 NULL);
665 /* perform local lookup */
666 GDS_DATACACHE_handle_get (&get->key,
667 cqr->type,
668 cqr->xquery,
669 xquery_size,
670 NULL,
671 &handle_local_result,
672 ch);
673 GNUNET_SERVICE_client_continue (ch->client);
674}
675
676
677/**
678 * Closure for #find_by_unique_id().
679 */
680struct FindByUniqueIdContext
681{
682 /**
683 * Where to store the result, if found.
684 */
685 struct ClientQueryRecord *cqr;
686
687 /**
688 * Unique ID to look for.
689 */
690 uint64_t unique_id;
691};
692
693
694/**
695 * Function called for each existing DHT record for the given
696 * query. Checks if it matches the UID given in the closure
697 * and if so returns the entry as a result.
698 *
699 * @param cls the search context
700 * @param key query for the lookup (not used)
701 * @param value the `struct ClientQueryRecord`
702 * @return #GNUNET_YES to continue iteration (result not yet found)
703 */
704static enum GNUNET_GenericReturnValue
705find_by_unique_id (void *cls,
706 const struct GNUNET_HashCode *key,
707 void *value)
708{
709 struct FindByUniqueIdContext *fui_ctx = cls;
710 struct ClientQueryRecord *cqr = value;
711
712 if (cqr->unique_id != fui_ctx->unique_id)
713 return GNUNET_YES;
714 fui_ctx->cqr = cqr;
715 return GNUNET_NO;
716}
717
718
719/**
720 * Check "GET result seen" messages from the client.
721 *
722 * @param cls the client we received this message from
723 * @param message the actual message received
724 * @return #GNUNET_OK if @a seen is well-formed
725 */
726static enum GNUNET_GenericReturnValue
727check_dht_local_get_result_seen (
728 void *cls,
729 const struct GNUNET_DHT_ClientGetResultSeenMessage *seen)
730{
731 uint16_t size = ntohs (seen->header.size);
732 unsigned int hash_count =
733 (size - sizeof(*seen))
734 / sizeof(struct GNUNET_HashCode);
735
736 if (size != sizeof(*seen) + hash_count * sizeof(struct GNUNET_HashCode))
737 {
738 GNUNET_break (0);
739 return GNUNET_SYSERR;
740 }
741 return GNUNET_OK;
742}
743
744
745/**
746 * Handler for "GET result seen" messages from the client.
747 *
748 * @param cls the client we received this message from
749 * @param message the actual message received
750 */
751static void
752handle_dht_local_get_result_seen (
753 void *cls,
754 const struct GNUNET_DHT_ClientGetResultSeenMessage *seen)
755{
756 struct ClientHandle *ch = cls;
757 uint16_t size = ntohs (seen->header.size);
758 unsigned int hash_count = (size - sizeof(*seen))
759 / sizeof(struct GNUNET_HashCode);
760 const struct GNUNET_HashCode *hc = (const struct GNUNET_HashCode*) &seen[1];
761 struct FindByUniqueIdContext fui_ctx = {
762 .unique_id = seen->unique_id
763 };
764 unsigned int old_count;
765 struct ClientQueryRecord *cqr;
766
767 GNUNET_CONTAINER_multihashmap_get_multiple (forward_map,
768 &seen->key,
769 &find_by_unique_id,
770 &fui_ctx);
771 if (NULL == (cqr = fui_ctx.cqr))
772 {
773 GNUNET_break (0);
774 GNUNET_SERVICE_client_drop (ch->client);
775 return;
776 }
777 /* finally, update 'seen' list */
778 old_count = cqr->seen_replies_count;
779 GNUNET_array_grow (cqr->seen_replies,
780 cqr->seen_replies_count,
781 cqr->seen_replies_count + hash_count);
782 GNUNET_memcpy (&cqr->seen_replies[old_count],
783 hc,
784 sizeof(struct GNUNET_HashCode) * hash_count);
785}
786
787
788/**
789 * Closure for #remove_by_unique_id().
790 */
791struct RemoveByUniqueIdContext
792{
793 /**
794 * Client that issued the removal request.
795 */
796 struct ClientHandle *ch;
797
798 /**
799 * Unique ID of the request.
800 */
801 uint64_t unique_id;
802};
803
804
805/**
806 * Iterator over hash map entries that frees all entries
807 * that match the given client and unique ID.
808 *
809 * @param cls unique ID and client to search for in source routes
810 * @param key current key code
811 * @param value value in the hash map, a ClientQueryRecord
812 * @return #GNUNET_YES (we should continue to iterate)
813 */
814static enum GNUNET_GenericReturnValue
815remove_by_unique_id (void *cls,
816 const struct GNUNET_HashCode *key,
817 void *value)
818{
819 const struct RemoveByUniqueIdContext *ctx = cls;
820 struct ClientQueryRecord *cqr = value;
821
822 if (cqr->unique_id != ctx->unique_id)
823 return GNUNET_YES;
824 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
825 "Removing client %p's record for key %s (by unique id)\n",
826 ctx->ch->client,
827 GNUNET_h2s (key));
828 remove_client_query_record (cqr);
829 return GNUNET_YES;
830}
831
832
833/**
834 * Handler for any generic DHT stop messages, calls the appropriate handler
835 * depending on message type (if processed locally)
836 *
837 * @param cls client we received this message from
838 * @param message the actual message received
839 *
840 */
841static void
842handle_dht_local_get_stop (
843 void *cls,
844 const struct GNUNET_DHT_ClientGetStopMessage *dht_stop_msg)
845{
846 struct ClientHandle *ch = cls;
847 struct RemoveByUniqueIdContext ctx;
848
849 GNUNET_STATISTICS_update (GDS_stats,
850 "# GET STOP requests received from clients",
851 1,
852 GNUNET_NO);
853 LOG (GNUNET_ERROR_TYPE_DEBUG,
854 "Received GET STOP request for %s from local client %p\n",
855 GNUNET_h2s (&dht_stop_msg->key),
856 ch->client);
857 ctx.ch = ch;
858 ctx.unique_id = dht_stop_msg->unique_id;
859 GNUNET_CONTAINER_multihashmap_get_multiple (forward_map,
860 &dht_stop_msg->key,
861 &remove_by_unique_id,
862 &ctx);
863 GNUNET_SERVICE_client_continue (ch->client);
864}
865
866
867/**
868 * Closure for #forward_reply()
869 */
870struct ForwardReplyContext
871{
872 /**
873 * Block details.
874 */
875 const struct GNUNET_DATACACHE_Block *bd;
876
877 /**
878 * GET path taken.
879 */
880 const struct GNUNET_DHT_PathElement *get_path;
881
882 /**
883 * Number of entries in @e get_path.
884 */
885 unsigned int get_path_length;
886
887};
888
889
890/**
891 * Iterator over hash map entries that send a given reply to
892 * each of the matching clients. With some tricky recycling
893 * of the buffer.
894 *
895 * @param cls the `struct ForwardReplyContext`
896 * @param query_hash hash of the query for which this may be a reply
897 * @param value value in the hash map, a ClientQueryRecord
898 * @return #GNUNET_YES (we should continue to iterate),
899 * if the result is mal-formed, #GNUNET_NO
900 */
901static enum GNUNET_GenericReturnValue
902forward_reply (void *cls,
903 const struct GNUNET_HashCode *query_hash,
904 void *value)
905{
906 struct ForwardReplyContext *frc = cls;
907 struct ClientQueryRecord *record = value;
908 const struct GNUNET_DATACACHE_Block *bd = frc->bd;
909 struct GNUNET_MQ_Envelope *env;
910 struct GNUNET_DHT_ClientResultMessage *reply;
911 enum GNUNET_BLOCK_ReplyEvaluationResult eval;
912 bool do_free;
913 struct GNUNET_HashCode ch;
914 struct GNUNET_DHT_PathElement *paths;
915 bool truncated = (0 != (bd->ro & GNUNET_DHT_RO_TRUNCATED));
916 size_t xsize = bd->data_size;
917
918 LOG_TRAFFIC (GNUNET_ERROR_TYPE_DEBUG,
919 "CLIENT-RESULT %s\n",
920 GNUNET_h2s_full (&frc->bd->key));
921 if ( (record->type != GNUNET_BLOCK_TYPE_ANY) &&
922 (record->type != frc->bd->type) )
923 {
924 LOG (GNUNET_ERROR_TYPE_DEBUG,
925 "Record type mismatch, not passing request for key %s to local client\n",
926 GNUNET_h2s (&frc->bd->key));
927 GNUNET_STATISTICS_update (GDS_stats,
928 "# Key match, type mismatches in REPLY to CLIENT",
929 1,
930 GNUNET_NO);
931 return GNUNET_YES; /* type mismatch */
932 }
933 if ( (0 == (record->msg_options & GNUNET_DHT_RO_FIND_APPROXIMATE)) &&
934 (0 != GNUNET_memcmp (&frc->bd->key,
935 query_hash)) )
936 {
937 GNUNET_STATISTICS_update (GDS_stats,
938 "# Inexact key match, but exact match required",
939 1,
940 GNUNET_NO);
941 return GNUNET_YES; /* type mismatch */
942 }
943 GNUNET_CRYPTO_hash (frc->bd->data,
944 frc->bd->data_size,
945 &ch);
946 for (unsigned int i = 0; i < record->seen_replies_count; i++)
947 if (0 ==
948 GNUNET_memcmp (&record->seen_replies[i],
949 &ch))
950 {
951 LOG (GNUNET_ERROR_TYPE_DEBUG,
952 "Duplicate reply, not passing request for key %s to local client\n",
953 GNUNET_h2s (&frc->bd->key));
954 GNUNET_STATISTICS_update (GDS_stats,
955 "# Duplicate REPLIES to CLIENT request dropped",
956 1,
957 GNUNET_NO);
958 return GNUNET_YES; /* duplicate */
959 }
960 eval
961 = GNUNET_BLOCK_check_reply (GDS_block_context,
962 record->type,
963 NULL,
964 &frc->bd->key,
965 record->xquery,
966 record->xquery_size,
967 frc->bd->data,
968 frc->bd->data_size);
969 LOG (GNUNET_ERROR_TYPE_DEBUG,
970 "Evaluation result is %d for key %s for local client's query\n",
971 (int) eval,
972 GNUNET_h2s (&frc->bd->key));
973 switch (eval)
974 {
975 case GNUNET_BLOCK_REPLY_OK_LAST:
976 do_free = true;
977 break;
978 case GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED:
979 case GNUNET_BLOCK_REPLY_OK_MORE:
980 GNUNET_array_append (record->seen_replies,
981 record->seen_replies_count,
982 ch);
983 do_free = false;
984 break;
985 case GNUNET_BLOCK_REPLY_OK_DUPLICATE:
986 /* should be impossible to encounter here */
987 GNUNET_break (0);
988 return GNUNET_YES;
989 case GNUNET_BLOCK_REPLY_IRRELEVANT:
990 return GNUNET_YES;
991 default:
992 GNUNET_break (0);
993 return GNUNET_NO;
994 }
995 GNUNET_STATISTICS_update (GDS_stats,
996 "# RESULTS queued for clients",
997 1,
998 GNUNET_NO);
999 xsize += (frc->get_path_length + frc->bd->put_path_length)
1000 * sizeof(struct GNUNET_DHT_PathElement);
1001 if (truncated)
1002 xsize += sizeof (struct GNUNET_PeerIdentity);
1003 env = GNUNET_MQ_msg_extra (reply,
1004 xsize,
1005 GNUNET_MESSAGE_TYPE_DHT_CLIENT_RESULT);
1006 reply->type = htonl (frc->bd->type);
1007 reply->options = htonl (bd->ro);
1008 reply->get_path_length = htonl (frc->get_path_length);
1009 reply->put_path_length = htonl (frc->bd->put_path_length);
1010 reply->unique_id = record->unique_id;
1011 reply->expiration = GNUNET_TIME_absolute_hton (frc->bd->expiration_time);
1012 reply->key = *query_hash;
1013 if (truncated)
1014 {
1015 void *tgt = &reply[1];
1016
1017 GNUNET_memcpy (tgt,
1018 &bd->trunc_peer,
1019 sizeof (struct GNUNET_PeerIdentity));
1020 paths = (struct GNUNET_DHT_PathElement *)
1021 (tgt + sizeof (struct GNUNET_PeerIdentity));
1022 }
1023 else
1024 {
1025 paths = (struct GNUNET_DHT_PathElement *) &reply[1];
1026 }
1027 GNUNET_memcpy (paths,
1028 frc->bd->put_path,
1029 sizeof(struct GNUNET_DHT_PathElement)
1030 * frc->bd->put_path_length);
1031 GNUNET_memcpy (&paths[frc->bd->put_path_length],
1032 frc->get_path,
1033 sizeof(struct GNUNET_DHT_PathElement)
1034 * frc->get_path_length);
1035 GNUNET_memcpy (&paths[frc->get_path_length + frc->bd->put_path_length],
1036 frc->bd->data,
1037 frc->bd->data_size);
1038 LOG (GNUNET_ERROR_TYPE_DEBUG,
1039 "Sending reply to query %s for client %p\n",
1040 GNUNET_h2s (query_hash),
1041 record->ch->client);
1042 GNUNET_MQ_send (record->ch->mq,
1043 env);
1044 if (GNUNET_YES == do_free)
1045 remove_client_query_record (record);
1046 return GNUNET_YES;
1047}
1048
1049
1050bool
1051GDS_CLIENTS_handle_reply (const struct GNUNET_DATACACHE_Block *bd,
1052 const struct GNUNET_HashCode *query_hash,
1053 unsigned int get_path_length,
1054 const struct GNUNET_DHT_PathElement *get_path)
1055{
1056 struct ForwardReplyContext frc;
1057 size_t msize = sizeof (struct GNUNET_DHT_ClientResultMessage)
1058 + bd->data_size
1059 + (get_path_length + bd->put_path_length)
1060 * sizeof(struct GNUNET_DHT_PathElement);
1061 bool truncated = (0 != (bd->ro & GNUNET_DHT_RO_TRUNCATED));
1062
1063 if (msize >= GNUNET_MAX_MESSAGE_SIZE)
1064 {
1065 GNUNET_break (0);
1066 return false;
1067 }
1068#if SANITY_CHECKS > 1
1069 if (0 !=
1070 GNUNET_DHT_verify_path (bd->data,
1071 bd->data_size,
1072 bd->expiration_time,
1073 truncated
1074 ? &bd->trunc_peer
1075 : NULL,
1076 bd->put_path,
1077 bd->put_path_length,
1078 get_path,
1079 get_path_length,
1080 &GDS_my_identity))
1081 {
1082 GNUNET_break (0);
1083 return false;
1084 }
1085#endif
1086 frc.bd = bd;
1087 frc.get_path = get_path;
1088 frc.get_path_length = get_path_length;
1089 LOG (GNUNET_ERROR_TYPE_DEBUG,
1090 "Forwarding reply for query hash %s with GPL %u and PPL %u to client\n",
1091 GNUNET_h2s (query_hash),
1092 get_path_length,
1093 bd->put_path_length);
1094 if (0 ==
1095 GNUNET_CONTAINER_multihashmap_get_multiple (forward_map,
1096 query_hash,
1097 &forward_reply,
1098 &frc))
1099 {
1100 LOG (GNUNET_ERROR_TYPE_DEBUG,
1101 "No matching client for reply for query %s\n",
1102 GNUNET_h2s (query_hash));
1103 GNUNET_STATISTICS_update (GDS_stats,
1104 "# REPLIES ignored for CLIENTS (no match)",
1105 1,
1106 GNUNET_NO);
1107 }
1108 return true;
1109}
1110
1111
1112/* **************** HELLO logic ***************** */
1113
1114/**
1115 * Handler for HELLO GET message. Reply to client
1116 * with a URL of our HELLO.
1117 *
1118 * @param cls the client we received this message from
1119 * @param msg the actual message received
1120 *
1121 */
1122static void
1123handle_dht_local_hello_get (void *cls,
1124 const struct GNUNET_MessageHeader *msg)
1125{
1126 struct ClientHandle *ch = cls;
1127 char *url = GNUNET_HELLO_builder_to_url (GDS_my_hello,
1128 &GDS_my_private_key);
1129 size_t slen = strlen (url) + 1;
1130 struct GNUNET_MessageHeader *hdr;
1131 struct GNUNET_MQ_Envelope *env;
1132
1133 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1134 "Handling request from local client for my HELLO\n");
1135 env = GNUNET_MQ_msg_extra (hdr,
1136 slen,
1137 GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_URL);
1138 memcpy (&hdr[1],
1139 url,
1140 slen);
1141 GNUNET_free (url);
1142 GNUNET_MQ_send (ch->mq,
1143 env);
1144 GNUNET_SERVICE_client_continue (ch->client);
1145}
1146
1147
1148/**
1149 * Process a client HELLO message received from the service.
1150 *
1151 * @param cls the client we received this message from
1152 * @param hdr HELLO URL message from the service.
1153 * @return #GNUNET_OK if @a hdr is well-formed
1154 */
1155static enum GNUNET_GenericReturnValue
1156check_dht_local_hello_offer (void *cls,
1157 const struct GNUNET_MessageHeader *hdr)
1158{
1159 uint16_t len = ntohs (hdr->size);
1160 const char *buf = (const char *) &hdr[1];
1161
1162 (void) cls;
1163 if ('\0' != buf[len - sizeof (*hdr) - 1])
1164 {
1165 GNUNET_break (0);
1166 return GNUNET_SYSERR;
1167 }
1168 return GNUNET_OK;
1169}
1170
1171
1172/**
1173 * Handler for HELLO OFFER message. Try to use the
1174 * HELLO to connect to another peer.
1175 *
1176 * @param cls the client we received this message from
1177 * @param msg the actual message received
1178 */
1179static void
1180handle_dht_local_hello_offer (void *cls,
1181 const struct GNUNET_MessageHeader *msg)
1182{
1183 struct ClientHandle *ch = cls;
1184 const char *url = (const char *) &msg[1];
1185 struct GNUNET_HELLO_Builder *b;
1186 struct GNUNET_PeerIdentity pid;
1187
1188 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1189 "Local client provided HELLO URL %s\n",
1190 url);
1191 b = GNUNET_HELLO_builder_from_url (url);
1192 if (NULL == b)
1193 {
1194 GNUNET_break (0);
1195 GNUNET_SERVICE_client_drop (ch->client);
1196 return;
1197 }
1198 GNUNET_SERVICE_client_continue (ch->client);
1199 GNUNET_HELLO_builder_iterate (b,
1200 &pid,
1201 &GDS_try_connect,
1202 &pid);
1203 GNUNET_HELLO_builder_free (b);
1204}
1205
1206
1207/* ************* logic for monitors ************** */
1208
1209
1210/**
1211 * Handler for monitor start messages
1212 *
1213 * @param cls the client we received this message from
1214 * @param msg the actual message received
1215 *
1216 */
1217static void
1218handle_dht_local_monitor (void *cls,
1219 const struct GNUNET_DHT_MonitorStartStopMessage *msg)
1220{
1221 struct ClientHandle *ch = cls;
1222 struct ClientMonitorRecord *r;
1223
1224 r = GNUNET_new (struct ClientMonitorRecord);
1225 r->ch = ch;
1226 r->type = ntohl (msg->type);
1227 r->get = ntohs (msg->get);
1228 r->get_resp = ntohs (msg->get_resp);
1229 r->put = ntohs (msg->put);
1230 if (0 != ntohs (msg->filter_key))
1231 r->key = msg->key;
1232 GNUNET_CONTAINER_DLL_insert (monitor_head,
1233 monitor_tail,
1234 r);
1235 GNUNET_SERVICE_client_continue (ch->client);
1236}
1237
1238
1239/**
1240 * Handler for monitor stop messages
1241 *
1242 * @param cls the client we received this message from
1243 * @param msg the actual message received
1244 */
1245static void
1246handle_dht_local_monitor_stop (
1247 void *cls,
1248 const struct GNUNET_DHT_MonitorStartStopMessage *msg)
1249{
1250 struct ClientHandle *ch = cls;
1251
1252 GNUNET_SERVICE_client_continue (ch->client);
1253 for (struct ClientMonitorRecord *r = monitor_head;
1254 NULL != r;
1255 r = r->next)
1256 {
1257 bool keys_match;
1258
1259 keys_match =
1260 (GNUNET_is_zero (&r->key))
1261 ? (0 == ntohs (msg->filter_key))
1262 : ( (0 != ntohs (msg->filter_key)) &&
1263 (! GNUNET_memcmp (&r->key,
1264 &msg->key)) );
1265 if ( (ch == r->ch) &&
1266 (ntohl (msg->type) == r->type) &&
1267 (r->get == msg->get) &&
1268 (r->get_resp == msg->get_resp) &&
1269 (r->put == msg->put) &&
1270 keys_match)
1271 {
1272 GNUNET_CONTAINER_DLL_remove (monitor_head,
1273 monitor_tail,
1274 r);
1275 GNUNET_free (r);
1276 return; /* Delete only ONE entry */
1277 }
1278 }
1279}
1280
1281
1282/**
1283 * Function to call by #for_matching_monitors().
1284 *
1285 * @param cls closure
1286 * @param m a matching monitor
1287 */
1288typedef void
1289(*MonitorAction)(void *cls,
1290 struct ClientMonitorRecord *m);
1291
1292
1293/**
1294 * Call @a cb on all monitors that watch for blocks of @a type
1295 * and key @a key.
1296 *
1297 * @param type the type to match
1298 * @param key the key to match
1299 * @param cb function to call
1300 * @param cb_cls closure for @a cb
1301 */
1302static void
1303for_matching_monitors (enum GNUNET_BLOCK_Type type,
1304 const struct GNUNET_HashCode *key,
1305 MonitorAction cb,
1306 void *cb_cls)
1307{
1308 struct ClientHandle **cl = NULL;
1309 unsigned int cl_size = 0;
1310
1311 for (struct ClientMonitorRecord *m = monitor_head;
1312 NULL != m;
1313 m = m->next)
1314 {
1315 bool found = false;
1316
1317 if ( (GNUNET_BLOCK_TYPE_ANY != m->type) &&
1318 (m->type != type) )
1319 continue;
1320 if ( (! GNUNET_is_zero (&m->key)) &&
1321 (0 ==
1322 GNUNET_memcmp (key,
1323 &m->key)) )
1324 continue;
1325 /* Don't send duplicates */
1326 for (unsigned i = 0; i < cl_size; i++)
1327 if (cl[i] == m->ch)
1328 {
1329 found = true;
1330 break;
1331 }
1332 if (found)
1333 continue;
1334 GNUNET_array_append (cl,
1335 cl_size,
1336 m->ch);
1337 cb (cb_cls,
1338 m);
1339 }
1340 GNUNET_free (cl);
1341}
1342
1343
1344/**
1345 * Closure for #get_action();
1346 */
1347struct GetActionContext
1348{
1349 enum GNUNET_DHT_RouteOption options;
1350 enum GNUNET_BLOCK_Type type;
1351 uint32_t hop_count;
1352 uint32_t desired_replication_level;
1353 struct GNUNET_PeerIdentity trunc_peer;
1354 const struct GNUNET_HashCode *key;
1355};
1356
1357
1358/**
1359 * Function called on monitors that match a GET.
1360 * Sends the GET notification to the monitor.
1361 *
1362 * @param cls a `struct GetActionContext`
1363 * @param m a matching monitor
1364 */
1365static void
1366get_action (void *cls,
1367 struct ClientMonitorRecord *m)
1368{
1369 struct GetActionContext *gac = cls;
1370 struct GNUNET_MQ_Envelope *env;
1371 struct GNUNET_DHT_MonitorGetMessage *mmsg;
1372
1373 env = GNUNET_MQ_msg (mmsg,
1374 GNUNET_MESSAGE_TYPE_DHT_MONITOR_GET);
1375 mmsg->options = htonl (gac->options);
1376 mmsg->type = htonl (gac->type);
1377 mmsg->hop_count = htonl (gac->hop_count);
1378 mmsg->desired_replication_level = htonl (gac->desired_replication_level);
1379 mmsg->key = *gac->key;
1380 GNUNET_MQ_send (m->ch->mq,
1381 env);
1382}
1383
1384
1385/**
1386 * Check if some client is monitoring GET messages and notify
1387 * them in that case. If tracked, @a path should include the local peer.
1388 *
1389 * @param options Options, for instance RecordRoute, DemultiplexEverywhere.
1390 * @param type The type of data in the request.
1391 * @param hop_count Hop count so far.
1392 * @param desired_replication_level Desired replication level.
1393 * @param key Key of the requested data.
1394 */
1395void
1396GDS_CLIENTS_process_get (enum GNUNET_DHT_RouteOption options,
1397 enum GNUNET_BLOCK_Type type,
1398 uint32_t hop_count,
1399 uint32_t desired_replication_level,
1400 const struct GNUNET_HashCode *key)
1401{
1402 struct GetActionContext gac = {
1403 .options = options,
1404 .type = type,
1405 .hop_count = hop_count,
1406 .desired_replication_level = desired_replication_level,
1407 .key = key
1408 };
1409
1410 for_matching_monitors (type,
1411 key,
1412 &get_action,
1413 &gac);
1414}
1415
1416
1417/**
1418 * Closure for response_action().
1419 */
1420struct ResponseActionContext
1421{
1422 const struct GNUNET_DATACACHE_Block *bd;
1423 const struct GNUNET_DHT_PathElement *get_path;
1424 unsigned int get_path_length;
1425};
1426
1427
1428/**
1429 * Function called on monitors that match a response.
1430 * Sends the response notification to the monitor.
1431 *
1432 * @param cls a `struct ResponseActionContext`
1433 * @param m a matching monitor
1434 */
1435static void
1436response_action (void *cls,
1437 struct ClientMonitorRecord *m)
1438{
1439 const struct ResponseActionContext *resp_ctx = cls;
1440 const struct GNUNET_DATACACHE_Block *bd = resp_ctx->bd;
1441 bool truncated = (0 != (bd->ro & GNUNET_DHT_RO_TRUNCATED));
1442 struct GNUNET_MQ_Envelope *env;
1443 struct GNUNET_DHT_MonitorGetRespMessage *mmsg;
1444 struct GNUNET_DHT_PathElement *path;
1445 size_t msize;
1446
1447 msize = bd->data_size;
1448 msize += (resp_ctx->get_path_length + bd->put_path_length)
1449 * sizeof(struct GNUNET_DHT_PathElement);
1450 if (truncated)
1451 msize += sizeof (struct GNUNET_PeerIdentity);
1452 env = GNUNET_MQ_msg_extra (mmsg,
1453 msize,
1454 GNUNET_MESSAGE_TYPE_DHT_MONITOR_GET_RESP);
1455 mmsg->type = htonl (bd->type);
1456 mmsg->put_path_length = htonl (bd->put_path_length);
1457 mmsg->get_path_length = htonl (resp_ctx->get_path_length);
1458 mmsg->expiration_time = GNUNET_TIME_absolute_hton (bd->expiration_time);
1459 mmsg->key = bd->key;
1460 if (truncated)
1461 {
1462 void *tgt = &mmsg[1];
1463
1464 GNUNET_memcpy (tgt,
1465 &bd->trunc_peer,
1466 sizeof (struct GNUNET_PeerIdentity));
1467 path = (struct GNUNET_DHT_PathElement *)
1468 (tgt + sizeof (struct GNUNET_PeerIdentity));
1469 }
1470 else
1471 {
1472 path = (struct GNUNET_DHT_PathElement *) &mmsg[1];
1473 }
1474 GNUNET_memcpy (path,
1475 bd->put_path,
1476 bd->put_path_length * sizeof(struct GNUNET_DHT_PathElement));
1477 GNUNET_memcpy (path,
1478 resp_ctx->get_path,
1479 resp_ctx->get_path_length
1480 * sizeof(struct GNUNET_DHT_PathElement));
1481 GNUNET_memcpy (&path[resp_ctx->get_path_length],
1482 bd->data,
1483 bd->data_size);
1484 GNUNET_MQ_send (m->ch->mq,
1485 env);
1486}
1487
1488
1489void
1490GDS_CLIENTS_process_get_resp (const struct GNUNET_DATACACHE_Block *bd,
1491 const struct GNUNET_DHT_PathElement *get_path,
1492 unsigned int get_path_length)
1493{
1494 struct ResponseActionContext rac = {
1495 .bd = bd,
1496 .get_path = get_path,
1497 .get_path_length = get_path_length
1498 };
1499
1500 for_matching_monitors (bd->type,
1501 &bd->key,
1502 &response_action,
1503 &rac);
1504}
1505
1506
1507/**
1508 * Closure for put_action().
1509 */
1510struct PutActionContext
1511{
1512 const struct GNUNET_DATACACHE_Block *bd;
1513 uint32_t hop_count;
1514 uint32_t desired_replication_level;
1515};
1516
1517
1518/**
1519 * Function called on monitors that match a PUT.
1520 * Sends the PUT notification to the monitor.
1521 *
1522 * @param cls a `struct PutActionContext`
1523 * @param m a matching monitor
1524 */
1525static void
1526put_action (void *cls,
1527 struct ClientMonitorRecord *m)
1528{
1529 const struct PutActionContext *put_ctx = cls;
1530 const struct GNUNET_DATACACHE_Block *bd = put_ctx->bd;
1531 bool truncated = (0 != (bd->ro & GNUNET_DHT_RO_TRUNCATED));
1532 struct GNUNET_MQ_Envelope *env;
1533 struct GNUNET_DHT_MonitorPutMessage *mmsg;
1534 struct GNUNET_DHT_PathElement *msg_path;
1535 size_t msize;
1536
1537 msize = bd->data_size
1538 + bd->put_path_length
1539 * sizeof(struct GNUNET_DHT_PathElement);
1540 if (truncated)
1541 msize += sizeof (struct GNUNET_PeerIdentity);
1542 env = GNUNET_MQ_msg_extra (mmsg,
1543 msize,
1544 GNUNET_MESSAGE_TYPE_DHT_MONITOR_PUT);
1545 mmsg->options = htonl (bd->ro);
1546 mmsg->type = htonl (bd->type);
1547 mmsg->hop_count = htonl (put_ctx->hop_count);
1548 mmsg->desired_replication_level = htonl (put_ctx->desired_replication_level);
1549 mmsg->put_path_length = htonl (bd->put_path_length);
1550 mmsg->key = bd->key;
1551 mmsg->expiration_time = GNUNET_TIME_absolute_hton (bd->expiration_time);
1552 if (truncated)
1553 {
1554 void *tgt = &mmsg[1];
1555
1556 GNUNET_memcpy (tgt,
1557 &bd->trunc_peer,
1558 sizeof (struct GNUNET_PeerIdentity));
1559 msg_path = (struct GNUNET_DHT_PathElement *)
1560 (tgt + sizeof (struct GNUNET_PeerIdentity));
1561 }
1562 else
1563 {
1564 msg_path = (struct GNUNET_DHT_PathElement *) &mmsg[1];
1565 }
1566 GNUNET_memcpy (msg_path,
1567 bd->put_path,
1568 bd->put_path_length * sizeof(struct GNUNET_DHT_PathElement));
1569 GNUNET_memcpy (&msg_path[bd->put_path_length],
1570 bd->data,
1571 bd->data_size);
1572 GNUNET_MQ_send (m->ch->mq,
1573 env);
1574}
1575
1576
1577void
1578GDS_CLIENTS_process_put (const struct GNUNET_DATACACHE_Block *bd,
1579 uint32_t hop_count,
1580 uint32_t desired_replication_level)
1581{
1582 struct PutActionContext put_ctx = {
1583 .bd = bd,
1584 .hop_count = hop_count,
1585 .desired_replication_level = desired_replication_level
1586 };
1587
1588 for_matching_monitors (bd->type,
1589 &bd->key,
1590 &put_action,
1591 &put_ctx);
1592}
1593
1594
1595/* ********************** Initialization logic ***************** */
1596
1597
1598/**
1599 * Initialize client subsystem.
1600 *
1601 * @param server the initialized server
1602 */
1603static void
1604GDS_CLIENTS_init (void)
1605{
1606 forward_map
1607 = GNUNET_CONTAINER_multihashmap_create (1024,
1608 GNUNET_YES);
1609 retry_heap
1610 = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
1611}
1612
1613
1614/**
1615 * Shutdown client subsystem.
1616 */
1617static void
1618GDS_CLIENTS_stop (void)
1619{
1620 if (NULL != retry_task)
1621 {
1622 GNUNET_SCHEDULER_cancel (retry_task);
1623 retry_task = NULL;
1624 }
1625}
1626
1627
1628/**
1629 * Define "main" method using service macro.
1630 *
1631 * @param name name of the service, like "dht" or "xdht"
1632 * @param run name of the initializaton method for the service
1633 */
1634#define GDS_DHT_SERVICE_INIT(name, run) \
1635 GNUNET_SERVICE_MAIN \
1636 (name, \
1637 GNUNET_SERVICE_OPTION_NONE, \
1638 run, \
1639 &client_connect_cb, \
1640 &client_disconnect_cb, \
1641 NULL, \
1642 GNUNET_MQ_hd_var_size (dht_local_put, \
1643 GNUNET_MESSAGE_TYPE_DHT_CLIENT_PUT, \
1644 struct GNUNET_DHT_ClientPutMessage, \
1645 NULL), \
1646 GNUNET_MQ_hd_var_size (dht_local_get, \
1647 GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET, \
1648 struct GNUNET_DHT_ClientGetMessage, \
1649 NULL), \
1650 GNUNET_MQ_hd_fixed_size (dht_local_get_stop, \
1651 GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET_STOP, \
1652 struct GNUNET_DHT_ClientGetStopMessage, \
1653 NULL), \
1654 GNUNET_MQ_hd_fixed_size (dht_local_monitor, \
1655 GNUNET_MESSAGE_TYPE_DHT_MONITOR_START, \
1656 struct GNUNET_DHT_MonitorStartStopMessage, \
1657 NULL), \
1658 GNUNET_MQ_hd_fixed_size (dht_local_monitor_stop, \
1659 GNUNET_MESSAGE_TYPE_DHT_MONITOR_STOP, \
1660 struct GNUNET_DHT_MonitorStartStopMessage, \
1661 NULL), \
1662 GNUNET_MQ_hd_var_size (dht_local_get_result_seen, \
1663 GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET_RESULTS_KNOWN, \
1664 struct GNUNET_DHT_ClientGetResultSeenMessage, \
1665 NULL), \
1666 GNUNET_MQ_hd_fixed_size (dht_local_hello_get, \
1667 GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_GET, \
1668 struct GNUNET_MessageHeader, \
1669 NULL), \
1670 GNUNET_MQ_hd_var_size (dht_local_hello_offer, \
1671 GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_URL, \
1672 struct GNUNET_MessageHeader, \
1673 NULL), \
1674 GNUNET_MQ_handler_end ())
1675
1676
1677/**
1678 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1679 */
1680void __attribute__ ((destructor))
1681GDS_CLIENTS_done ()
1682{
1683 if (NULL != retry_heap)
1684 {
1685 GNUNET_assert (0 == GNUNET_CONTAINER_heap_get_size (retry_heap));
1686 GNUNET_CONTAINER_heap_destroy (retry_heap);
1687 retry_heap = NULL;
1688 }
1689 if (NULL != forward_map)
1690 {
1691 GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap_size (forward_map));
1692 GNUNET_CONTAINER_multihashmap_destroy (forward_map);
1693 forward_map = NULL;
1694 }
1695}
1696
1697
1698/* end of gnunet-service-dht_clients.c */
diff --git a/src/dht/gnunet-service-dht_datacache.c b/src/dht/gnunet-service-dht_datacache.c
deleted file mode 100644
index dcb6308a9..000000000
--- a/src/dht/gnunet-service-dht_datacache.c
+++ /dev/null
@@ -1,291 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2011, 2015, 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 * @file dht/gnunet-service-dht_datacache.c
22 * @brief GNUnet DHT service's datacache integration
23 * @author Christian Grothoff
24 * @author Nathan Evans
25 */
26#include "platform.h"
27#include "gnunet_datacache_lib.h"
28#include "gnunet-service-dht_datacache.h"
29#include "gnunet-service-dht_neighbours.h"
30#include "gnunet-service-dht_routing.h"
31#include "gnunet-service-dht.h"
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "dht-dhtcache", __VA_ARGS__)
34
35/**
36 * How many "closest" results to we return for migration when
37 * asked (at most)?
38 */
39#define NUM_CLOSEST 4
40
41
42/**
43 * Handle to the datacache service (for inserting/retrieving data)
44 */
45static struct GNUNET_DATACACHE_Handle *datacache;
46
47
48void
49GDS_DATACACHE_handle_put (const struct GNUNET_DATACACHE_Block *bd)
50{
51 struct GNUNET_HashCode xor;
52 enum GNUNET_GenericReturnValue r;
53
54 if (NULL == datacache)
55 {
56 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
57 "PUT request received, but have no datacache!\n");
58 return;
59 }
60 if (bd->data_size >= GNUNET_MAX_MESSAGE_SIZE)
61 {
62 GNUNET_break (0);
63 return;
64 }
65 /* Put size is actual data size plus struct overhead plus path length (if any) */
66 GNUNET_STATISTICS_update (GDS_stats,
67 "# ITEMS stored in datacache",
68 1,
69 GNUNET_NO);
70 GNUNET_CRYPTO_hash_xor (&bd->key,
71 &GDS_my_identity_hash,
72 &xor);
73 r = GNUNET_DATACACHE_put (datacache,
74 GNUNET_CRYPTO_hash_count_leading_zeros (&xor),
75 bd);
76 LOG (GNUNET_ERROR_TYPE_DEBUG,
77 "DATACACHE PUT for key %s [%lu] completed (%d) after %u hops\n",
78 GNUNET_h2s (&bd->key),
79 (unsigned long) bd->data_size,
80 r,
81 bd->put_path_length);
82}
83
84
85/**
86 * Context containing information about a GET request.
87 */
88struct GetRequestContext
89{
90 /**
91 * extended query (see gnunet_block_lib.h).
92 */
93 const void *xquery;
94
95 /**
96 * The key this request was about
97 */
98 struct GNUNET_HashCode key;
99
100 /**
101 * Block group to use to evaluate replies (updated)
102 */
103 struct GNUNET_BLOCK_Group *bg;
104
105 /**
106 * Function to call on results.
107 */
108 GDS_DATACACHE_GetCallback gc;
109
110 /**
111 * Closure for @e gc.
112 */
113 void *gc_cls;
114
115 /**
116 * Number of bytes in xquery.
117 */
118 size_t xquery_size;
119
120 /**
121 * Return value to give back.
122 */
123 enum GNUNET_BLOCK_ReplyEvaluationResult eval;
124};
125
126
127/**
128 * Iterator for local get request results,
129 *
130 * @param cls closure for iterator, a `struct GetRequestContext`
131 * @param bd block data
132 * @return #GNUNET_OK to continue iteration, anything else
133 * to stop iteration.
134 */
135static enum GNUNET_GenericReturnValue
136datacache_get_iterator (void *cls,
137 const struct GNUNET_DATACACHE_Block *bd)
138{
139 struct GetRequestContext *ctx = cls;
140 enum GNUNET_BLOCK_ReplyEvaluationResult eval;
141
142 if (GNUNET_TIME_absolute_is_past (bd->expiration_time))
143 {
144 GNUNET_break (0); /* why does datacache return expired values? */
145 return GNUNET_OK; /* skip expired record */
146 }
147 eval
148 = GNUNET_BLOCK_check_reply (GDS_block_context,
149 bd->type,
150 ctx->bg,
151 &bd->key,
152 ctx->xquery,
153 ctx->xquery_size,
154 bd->data,
155 bd->data_size);
156 LOG (GNUNET_ERROR_TYPE_DEBUG,
157 "Evaluated reply for query %s in datacache, result is %d\n",
158 GNUNET_h2s (&bd->key),
159 (int) eval);
160 ctx->eval = eval;
161 switch (eval)
162 {
163 case GNUNET_BLOCK_REPLY_OK_MORE:
164 case GNUNET_BLOCK_REPLY_OK_LAST:
165 case GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED:
166 /* forward to initiator */
167 GNUNET_STATISTICS_update (GDS_stats,
168 "# Good RESULTS found in datacache",
169 1,
170 GNUNET_NO);
171 ctx->gc (ctx->gc_cls,
172 bd);
173 break;
174 case GNUNET_BLOCK_REPLY_OK_DUPLICATE:
175 GNUNET_STATISTICS_update (GDS_stats,
176 "# Duplicate RESULTS found in datacache",
177 1,
178 GNUNET_NO);
179 break;
180 case GNUNET_BLOCK_REPLY_IRRELEVANT:
181 GNUNET_STATISTICS_update (GDS_stats,
182 "# Irrelevant RESULTS found in datacache",
183 1,
184 GNUNET_NO);
185 break;
186 }
187 return (eval == GNUNET_BLOCK_REPLY_OK_LAST) ? GNUNET_NO : GNUNET_OK;
188}
189
190
191enum GNUNET_BLOCK_ReplyEvaluationResult
192GDS_DATACACHE_handle_get (const struct GNUNET_HashCode *key,
193 enum GNUNET_BLOCK_Type type,
194 const void *xquery,
195 size_t xquery_size,
196 struct GNUNET_BLOCK_Group *bg,
197 GDS_DATACACHE_GetCallback gc,
198 void *gc_cls)
199{
200 struct GetRequestContext ctx = {
201 .eval = GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED,
202 .key = *key,
203 .xquery = xquery,
204 .xquery_size = xquery_size,
205 .bg = bg,
206 .gc = gc,
207 .gc_cls = gc_cls
208 };
209 unsigned int r;
210
211 if (NULL == datacache)
212 return GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED;
213 GNUNET_STATISTICS_update (GDS_stats,
214 "# GET requests given to datacache",
215 1,
216 GNUNET_NO);
217 r = GNUNET_DATACACHE_get (datacache,
218 key,
219 type,
220 &datacache_get_iterator,
221 &ctx);
222 LOG (GNUNET_ERROR_TYPE_DEBUG,
223 "DATACACHE GET for key %s completed (%d). %u results found.\n",
224 GNUNET_h2s (key),
225 ctx.eval,
226 r);
227 return ctx.eval;
228}
229
230
231enum GNUNET_BLOCK_ReplyEvaluationResult
232GDS_DATACACHE_get_closest (const struct GNUNET_HashCode *key,
233 enum GNUNET_BLOCK_Type type,
234 const void *xquery,
235 size_t xquery_size,
236 struct GNUNET_BLOCK_Group *bg,
237 GDS_DATACACHE_GetCallback cb,
238 void *cb_cls)
239{
240 struct GetRequestContext ctx = {
241 .eval = GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED,
242 .key = *key,
243 .xquery = xquery,
244 .xquery_size = xquery_size,
245 .bg = bg,
246 .gc = cb,
247 .gc_cls = cb_cls
248 };
249 unsigned int r;
250
251 if (NULL == datacache)
252 return GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED;
253 GNUNET_STATISTICS_update (GDS_stats,
254 "# GET closest requests given to datacache",
255 1,
256 GNUNET_NO);
257 r = GNUNET_DATACACHE_get_closest (datacache,
258 key,
259 type,
260 NUM_CLOSEST,
261 &datacache_get_iterator,
262 &ctx);
263 LOG (GNUNET_ERROR_TYPE_DEBUG,
264 "DATACACHE approximate GET for key %s completed (%d). %u results found.\n",
265 GNUNET_h2s (key),
266 ctx.eval,
267 r);
268 return ctx.eval;
269}
270
271
272void
273GDS_DATACACHE_init ()
274{
275 datacache = GNUNET_DATACACHE_create (GDS_cfg,
276 "dhtcache");
277}
278
279
280void
281GDS_DATACACHE_done ()
282{
283 if (NULL != datacache)
284 {
285 GNUNET_DATACACHE_destroy (datacache);
286 datacache = NULL;
287 }
288}
289
290
291/* end of gnunet-service-dht_datacache.c */
diff --git a/src/dht/gnunet-service-dht_datacache.h b/src/dht/gnunet-service-dht_datacache.h
deleted file mode 100644
index b15e5378f..000000000
--- a/src/dht/gnunet-service-dht_datacache.h
+++ /dev/null
@@ -1,115 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2011, 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/gnunet-service-dht_datacache.h
23 * @brief GNUnet DHT service's datacache integration
24 * @author Christian Grothoff
25 * @author Nathan Evans
26 */
27#ifndef GNUNET_SERVICE_DHT_DATACACHE_H
28#define GNUNET_SERVICE_DHT_DATACACHE_H
29
30#include "gnunet_util_lib.h"
31#include "gnunet_block_lib.h"
32#include "gnunet_dht_service.h"
33#include "gnunet_datacache_lib.h"
34
35
36/**
37 * Handle a datum we've received from another peer. Cache if
38 * possible.
39 *
40 * @param bd block data to cache
41 */
42void
43GDS_DATACACHE_handle_put (const struct GNUNET_DATACACHE_Block *bd);
44
45
46/**
47 * Handle a result for a GET operation.
48 *
49 * @param cls closure
50 * @param bd block details
51 */
52typedef void
53(*GDS_DATACACHE_GetCallback)(void *cls,
54 const struct GNUNET_DATACACHE_Block *bd);
55
56
57/**
58 * Handle a GET request we've received from another peer.
59 *
60 * @param key the query
61 * @param type requested data type
62 * @param xquery extended query
63 * @param xquery_size number of bytes in xquery
64 * @param bg block group to use for evaluation of replies
65 * @param gc function to call on the results
66 * @param gc_cls closure for @a gc
67 * @return evaluation result for the local replies
68 */
69enum GNUNET_BLOCK_ReplyEvaluationResult
70GDS_DATACACHE_handle_get (const struct GNUNET_HashCode *key,
71 enum GNUNET_BLOCK_Type type,
72 const void *xquery,
73 size_t xquery_size,
74 struct GNUNET_BLOCK_Group *bg,
75 GDS_DATACACHE_GetCallback gc,
76 void *gc_cls);
77
78
79/**
80 * Handle a request for data close to a key that we have received from
81 * another peer.
82 *
83 * @param key the location at which the peer is looking for data that is close
84 * @param type requested data type
85 * @param xquery extended query
86 * @param xquery_size number of bytes in xquery
87 * @param bg block group to use for evaluation of replies
88 * @param cb function to call with the result
89 * @param cb_cls closure for @a cb
90 * @return evaluation result for the local replies
91 */
92enum GNUNET_BLOCK_ReplyEvaluationResult
93GDS_DATACACHE_get_closest (const struct GNUNET_HashCode *key,
94 enum GNUNET_BLOCK_Type type,
95 const void *xquery,
96 size_t xquery_size,
97 struct GNUNET_BLOCK_Group *bg,
98 GDS_DATACACHE_GetCallback cb,
99 void *cb_cls);
100
101
102/**
103 * Initialize datacache subsystem.
104 */
105void
106GDS_DATACACHE_init (void);
107
108
109/**
110 * Shutdown datacache subsystem.
111 */
112void
113GDS_DATACACHE_done (void);
114
115#endif
diff --git a/src/dht/gnunet-service-dht_neighbours.c b/src/dht/gnunet-service-dht_neighbours.c
deleted file mode 100644
index cc7333a9c..000000000
--- a/src/dht/gnunet-service-dht_neighbours.c
+++ /dev/null
@@ -1,2955 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2017, 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 * @file dht/gnunet-service-dht_neighbours.c
23 * @brief GNUnet DHT service's bucket and neighbour management code
24 * @author Christian Grothoff
25 * @author Nathan Evans
26 */
27#include "platform.h"
28#include "gnunet_constants.h"
29#include "gnunet_protocols.h"
30#include "gnunet_signatures.h"
31#include "gnunet_hello_lib.h"
32#include "gnunet_hello_uri_lib.h"
33#include "gnunet-service-dht.h"
34#include "gnunet-service-dht_neighbours.h"
35#include "gnunet-service-dht_routing.h"
36#include "dht.h"
37
38#define LOG_TRAFFIC(kind, ...) GNUNET_log_from (kind, "dht-traffic", \
39 __VA_ARGS__)
40
41/**
42 * Enable slow sanity checks to debug issues.
43 *
44 * TODO: might want to eventually implement probabilistic
45 * load-based path verification, but for now it is all or nothing
46 * based on this define.
47 *
48 * 0: do not check -- if signatures become performance critical
49 * 1: check all external inputs -- normal production for now
50 * 2: check internal computations as well -- for debugging
51 */
52#define SANITY_CHECKS 2
53
54/**
55 * How many buckets will we allow in total.
56 */
57#define MAX_BUCKETS sizeof(struct GNUNET_HashCode) * 8
58
59/**
60 * What is the maximum number of peers in a given bucket.
61 */
62#define DEFAULT_BUCKET_SIZE 8
63
64/**
65 * Desired replication level for FIND PEER requests
66 */
67#define FIND_PEER_REPLICATION_LEVEL 4
68
69/**
70 * Maximum allowed number of pending messages per peer.
71 */
72#define MAXIMUM_PENDING_PER_PEER 64
73
74/**
75 * How long at least to wait before sending another find peer request.
76 * This is basically the frequency at which we will usually send out
77 * requests when we are 'perfectly' connected.
78 */
79#define DHT_MINIMUM_FIND_PEER_INTERVAL GNUNET_TIME_relative_multiply ( \
80 GNUNET_TIME_UNIT_MINUTES, 2)
81
82
83/**
84 * How long to additionally wait on average per #bucket_size to send out the
85 * FIND PEER requests if we did successfully connect (!) to a a new peer and
86 * added it to a bucket (as counted in #newly_found_peers). This time is
87 * Multiplied by 100 * newly_found_peers / bucket_size to get the new delay
88 * for finding peers (the #DHT_MINIMUM_FIND_PEER_INTERVAL is still added on
89 * top). Also the range in which we randomize, so the effective value
90 * is half of the number given here.
91 */
92#define DHT_AVG_FIND_PEER_INTERVAL GNUNET_TIME_relative_multiply ( \
93 GNUNET_TIME_UNIT_SECONDS, 6)
94
95/**
96 * How long at most to wait for transmission of a GET request to another peer?
97 */
98#define GET_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2)
99
100
101GNUNET_NETWORK_STRUCT_BEGIN
102
103/**
104 * P2P PUT message
105 */
106struct PeerPutMessage
107{
108 /**
109 * Type: #GNUNET_MESSAGE_TYPE_DHT_P2P_PUT
110 */
111 struct GNUNET_MessageHeader header;
112
113 /**
114 * Content type.
115 */
116 uint32_t type GNUNET_PACKED;
117
118 /**
119 * Processing options
120 */
121 uint16_t options GNUNET_PACKED;
122
123 /**
124 * Hop count
125 */
126 uint16_t hop_count GNUNET_PACKED;
127
128 /**
129 * Replication level for this message
130 */
131 uint16_t desired_replication_level GNUNET_PACKED;
132
133 /**
134 * Length of the PUT path that follows (if tracked).
135 */
136 uint16_t put_path_length GNUNET_PACKED;
137
138 /**
139 * When does the content expire?
140 */
141 struct GNUNET_TIME_AbsoluteNBO expiration_time;
142
143 /**
144 * Bloomfilter (for peer identities) to stop circular routes
145 */
146 char bloomfilter[DHT_BLOOM_SIZE];
147
148 /**
149 * The key we are storing under.
150 */
151 struct GNUNET_HashCode key;
152
153 /* trunc_peer (if truncated) */
154
155 /* put path (if tracked) */
156
157 /* sender_sig (if path tracking is on) */
158
159 /* Payload */
160};
161
162
163/**
164 * P2P Result message
165 */
166struct PeerResultMessage
167{
168 /**
169 * Type: #GNUNET_MESSAGE_TYPE_DHT_P2P_RESULT
170 */
171 struct GNUNET_MessageHeader header;
172
173 /**
174 * Content type.
175 */
176 uint32_t type GNUNET_PACKED;
177
178 /**
179 * Message options, actually an 'enum GNUNET_DHT_RouteOption' value in NBO.
180 */
181 uint32_t options GNUNET_PACKED;
182
183 /**
184 * Length of the PUT path that follows (if tracked).
185 */
186 uint16_t put_path_length GNUNET_PACKED;
187
188 /**
189 * Length of the GET path that follows (if tracked).
190 */
191 uint16_t get_path_length GNUNET_PACKED;
192
193 /**
194 * When does the content expire?
195 */
196 struct GNUNET_TIME_AbsoluteNBO expiration_time;
197
198 /**
199 * The key of the corresponding GET request.
200 */
201 struct GNUNET_HashCode key;
202
203 /* trunc_peer (if truncated) */
204
205 /* put path (if tracked) */
206
207 /* get path (if tracked) */
208
209 /* sender_sig (if path tracking is on) */
210
211 /* Payload */
212};
213
214
215/**
216 * P2P GET message
217 */
218struct PeerGetMessage
219{
220 /**
221 * Type: #GNUNET_MESSAGE_TYPE_DHT_P2P_GET
222 */
223 struct GNUNET_MessageHeader header;
224
225 /**
226 * Desired content type.
227 */
228 uint32_t type GNUNET_PACKED;
229
230 /**
231 * Processing options
232 */
233 uint16_t options GNUNET_PACKED;
234
235 /**
236 * Hop count
237 */
238 uint16_t hop_count GNUNET_PACKED;
239
240 /**
241 * Desired replication level for this request.
242 */
243 uint16_t desired_replication_level GNUNET_PACKED;
244
245 /**
246 * Size of the result filter.
247 */
248 uint16_t result_filter_size GNUNET_PACKED;
249
250 /**
251 * Bloomfilter (for peer identities) to stop circular routes
252 */
253 char bloomfilter[DHT_BLOOM_SIZE];
254
255 /**
256 * The key we are looking for.
257 */
258 struct GNUNET_HashCode key;
259
260 /* result bloomfilter */
261
262 /* xquery */
263
264};
265GNUNET_NETWORK_STRUCT_END
266
267
268/**
269 * Entry for a peer in a bucket.
270 */
271struct PeerInfo;
272
273
274/**
275 * List of targets that we can use to reach this peer.
276 */
277struct Target
278{
279 /**
280 * Kept in a DLL.
281 */
282 struct Target *next;
283
284 /**
285 * Kept in a DLL.
286 */
287 struct Target *prev;
288
289 /**
290 * Handle for sending messages to this peer.
291 */
292 struct GNUNET_DHTU_Target *utarget;
293
294 /**
295 * Underlay providing this target.
296 */
297 struct GDS_Underlay *u;
298
299 /**
300 * Peer this is a target for.
301 */
302 struct PeerInfo *pi;
303
304 /**
305 * Handle used to 'hold' the connection to this peer.
306 */
307 struct GNUNET_DHTU_PreferenceHandle *ph;
308
309 /**
310 * Set to number of messages are waiting for the transmission to finish.
311 */
312 unsigned int load;
313
314 /**
315 * Set to @a true if the target was dropped, but we could not clean
316 * up yet because @e busy was also true.
317 */
318 bool dropped;
319
320};
321
322
323/**
324 * Entry for a peer in a bucket.
325 */
326struct PeerInfo
327{
328 /**
329 * What is the identity of the peer?
330 */
331 struct GNUNET_PeerIdentity id;
332
333 /**
334 * Hash of @e id.
335 */
336 struct GNUNET_HashCode phash;
337
338 /**
339 * When does our HELLO from this peer expire?
340 */
341 struct GNUNET_TIME_Absolute hello_expiration;
342
343 /**
344 * Next peer entry (DLL)
345 */
346 struct PeerInfo *next;
347
348 /**
349 * Prev peer entry (DLL)
350 */
351 struct PeerInfo *prev;
352
353 /**
354 * Head of DLL of targets for this peer.
355 */
356 struct Target *t_head;
357
358 /**
359 * Tail of DLL of targets for this peer.
360 */
361 struct Target *t_tail;
362
363 /**
364 * Block with a HELLO of this peer.
365 */
366 void *hello;
367
368 /**
369 * Number of bytes in @e hello.
370 */
371 size_t hello_size;
372
373 /**
374 * Which bucket is this peer in?
375 */
376 int peer_bucket;
377};
378
379
380/**
381 * Peers are grouped into buckets.
382 */
383struct PeerBucket
384{
385 /**
386 * Head of DLL
387 */
388 struct PeerInfo *head;
389
390 /**
391 * Tail of DLL
392 */
393 struct PeerInfo *tail;
394
395 /**
396 * Number of peers in the bucket.
397 */
398 unsigned int peers_size;
399};
400
401
402/**
403 * Do we cache all results that we are routing in the local datacache?
404 */
405static int cache_results;
406
407/**
408 * The lowest currently used bucket, initially 0 (for 0-bits matching bucket).
409 */
410static unsigned int closest_bucket;
411
412/**
413 * How many peers have we added since we sent out our last
414 * find peer request?
415 */
416static unsigned int newly_found_peers;
417
418/**
419 * Option for testing that disables the 'connect' function of the DHT.
420 */
421static int disable_try_connect;
422
423/**
424 * The buckets. Array of size #MAX_BUCKETS. Offset 0 means 0 bits matching.
425 */
426static struct PeerBucket k_buckets[MAX_BUCKETS];
427
428/**
429 * Hash map of all CORE-connected peers, for easy removal from
430 * #k_buckets on disconnect. Values are of type `struct PeerInfo`.
431 */
432static struct GNUNET_CONTAINER_MultiPeerMap *all_connected_peers;
433
434/**
435 * Maximum size for each bucket.
436 */
437static unsigned int bucket_size = DEFAULT_BUCKET_SIZE;
438
439/**
440 * Task that sends FIND PEER requests.
441 */
442static struct GNUNET_SCHEDULER_Task *find_peer_task;
443
444
445/**
446 * Function called whenever we finished sending to a target.
447 * Marks the transmission as finished (and the target as ready
448 * for the next message).
449 *
450 * @param cls a `struct Target *`
451 */
452static void
453send_done_cb (void *cls)
454{
455 struct Target *t = cls;
456 struct PeerInfo *pi = t->pi; /* NULL if t->dropped! */
457
458 GNUNET_assert (t->load > 0);
459 t->load--;
460 if (0 < t->load)
461 return;
462 if (t->dropped)
463 {
464 GNUNET_free (t);
465 return;
466 }
467 /* move target back to the front */
468 GNUNET_CONTAINER_DLL_remove (pi->t_head,
469 pi->t_tail,
470 t);
471 GNUNET_CONTAINER_DLL_insert (pi->t_head,
472 pi->t_tail,
473 t);
474}
475
476
477/**
478 * Send @a msg to @a pi.
479 *
480 * @param pi where to send the message
481 * @param msg message to send
482 */
483static void
484do_send (struct PeerInfo *pi,
485 const struct GNUNET_MessageHeader *msg)
486{
487 struct Target *t;
488
489 for (t = pi->t_head;
490 NULL != t;
491 t = t->next)
492 if (t->load < MAXIMUM_PENDING_PER_PEER)
493 break;
494 if (NULL == t)
495 {
496 /* all targets busy, drop message */
497 GNUNET_STATISTICS_update (GDS_stats,
498 "# messages dropped (underlays busy)",
499 1,
500 GNUNET_NO);
501 return;
502 }
503 t->load++;
504 /* rotate busy targets to the end */
505 if (MAXIMUM_PENDING_PER_PEER == t->load)
506 {
507 GNUNET_CONTAINER_DLL_remove (pi->t_head,
508 pi->t_tail,
509 t);
510 GNUNET_CONTAINER_DLL_insert_tail (pi->t_head,
511 pi->t_tail,
512 t);
513 }
514 GDS_u_send (t->u,
515 t->utarget,
516 msg,
517 ntohs (msg->size),
518 &send_done_cb,
519 t);
520}
521
522
523/**
524 * Sign that we are routing a message from @a pred to @a succ.
525 * (So the route is $PRED->us->$SUCC).
526 *
527 * @param key key of the data (not necessarily the query hash)
528 * @param data payload (the block)
529 * @param data_size number of bytes in @a data
530 * @param exp_time expiration time of @a data
531 * @param pred predecessor peer ID
532 * @param succ successor peer ID
533 * @param[out] sig where to write the signature
534 * (of purpose #GNUNET_SIGNATURE_PURPOSE_DHT_PUT_HOP)
535 */
536static void
537sign_path (const void *data,
538 size_t data_size,
539 struct GNUNET_TIME_Absolute exp_time,
540 const struct GNUNET_PeerIdentity *pred,
541 const struct GNUNET_PeerIdentity *succ,
542 struct GNUNET_CRYPTO_EddsaSignature *sig)
543{
544 struct GNUNET_DHT_HopSignature hs = {
545 .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_DHT_HOP),
546 .purpose.size = htonl (sizeof (hs)),
547 .expiration_time = GNUNET_TIME_absolute_hton (exp_time),
548 .succ = *succ
549 };
550
551 if (NULL != pred)
552 hs.pred = *pred;
553 GNUNET_CRYPTO_hash (data,
554 data_size,
555 &hs.h_data);
556 GNUNET_CRYPTO_eddsa_sign (&GDS_my_private_key,
557 &hs,
558 sig);
559}
560
561
562/**
563 * Find the optimal bucket for this key.
564 *
565 * @param hc the hashcode to compare our identity to
566 * @return the proper bucket index, or -1
567 * on error (same hashcode)
568 */
569static int
570find_bucket (const struct GNUNET_HashCode *hc)
571{
572 struct GNUNET_HashCode xor;
573 unsigned int bits;
574
575 GNUNET_CRYPTO_hash_xor (hc,
576 &GDS_my_identity_hash,
577 &xor);
578 bits = GNUNET_CRYPTO_hash_count_leading_zeros (&xor);
579 if (bits == MAX_BUCKETS)
580 {
581 /* How can all bits match? Got my own ID? */
582 GNUNET_break (0);
583 return -1;
584 }
585 return MAX_BUCKETS - bits - 1;
586}
587
588
589/**
590 * Add each of the peers we already know to the Bloom filter of
591 * the request so that we don't get duplicate HELLOs.
592 *
593 * @param cls the `struct GNUNET_BLOCK_Group`
594 * @param key peer identity to add to the bloom filter
595 * @param value the peer information
596 * @return #GNUNET_YES (we should continue to iterate)
597 */
598static enum GNUNET_GenericReturnValue
599add_known_to_bloom (void *cls,
600 const struct GNUNET_PeerIdentity *key,
601 void *value)
602{
603 struct GNUNET_BLOCK_Group *bg = cls;
604 struct PeerInfo *pi = value;
605
606 GNUNET_BLOCK_group_set_seen (bg,
607 &pi->phash,
608 1);
609 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
610 "Adding known peer (%s) to Bloom filter for FIND PEER\n",
611 GNUNET_i2s (key));
612 return GNUNET_YES;
613}
614
615
616/**
617 * Task to send a find peer message for our own peer identifier
618 * so that we can find the closest peers in the network to ourselves
619 * and attempt to connect to them.
620 *
621 * @param cls closure for this task, NULL
622 */
623static void
624send_find_peer_message (void *cls)
625{
626 (void) cls;
627
628 /* Compute when to do this again (and if we should
629 even send a message right now) */
630 {
631 struct GNUNET_TIME_Relative next_send_time;
632 bool done_early;
633
634 find_peer_task = NULL;
635 done_early = (newly_found_peers > bucket_size);
636 /* schedule next round, taking longer if we found more peers
637 in the last round. */
638 next_send_time.rel_value_us =
639 DHT_MINIMUM_FIND_PEER_INTERVAL.rel_value_us
640 + GNUNET_CRYPTO_random_u64 (
641 GNUNET_CRYPTO_QUALITY_WEAK,
642 GNUNET_TIME_relative_multiply (
643 DHT_AVG_FIND_PEER_INTERVAL,
644 1 + 100 * (1 + newly_found_peers) / bucket_size).rel_value_us);
645 newly_found_peers = 0;
646 GNUNET_assert (NULL == find_peer_task);
647 find_peer_task =
648 GNUNET_SCHEDULER_add_delayed (next_send_time,
649 &send_find_peer_message,
650 NULL);
651 if (done_early)
652 return;
653 }
654
655 /* actually send 'find peer' request */
656 {
657 struct GNUNET_BLOCK_Group *bg;
658 struct GNUNET_CONTAINER_BloomFilter *peer_bf;
659
660 bg = GNUNET_BLOCK_group_create (GDS_block_context,
661 GNUNET_BLOCK_TYPE_DHT_URL_HELLO,
662 NULL,
663 0,
664 "filter-size",
665 DHT_BLOOM_SIZE,
666 NULL);
667 GNUNET_CONTAINER_multipeermap_iterate (all_connected_peers,
668 &add_known_to_bloom,
669 bg);
670 peer_bf
671 = GNUNET_CONTAINER_bloomfilter_init (NULL,
672 DHT_BLOOM_SIZE,
673 GNUNET_CONSTANTS_BLOOMFILTER_K);
674 if (GNUNET_OK !=
675 GDS_NEIGHBOURS_handle_get (GNUNET_BLOCK_TYPE_DHT_URL_HELLO,
676 GNUNET_DHT_RO_FIND_APPROXIMATE
677 | GNUNET_DHT_RO_RECORD_ROUTE,
678 FIND_PEER_REPLICATION_LEVEL,
679 0, /* hop count */
680 &GDS_my_identity_hash,
681 NULL, 0, /* xquery */
682 bg,
683 peer_bf))
684 {
685 GNUNET_STATISTICS_update (GDS_stats,
686 "# Failed to initiate FIND PEER lookup",
687 1,
688 GNUNET_NO);
689 }
690 else
691 {
692 GNUNET_STATISTICS_update (GDS_stats,
693 "# FIND PEER messages initiated",
694 1,
695 GNUNET_NO);
696 }
697 GNUNET_CONTAINER_bloomfilter_free (peer_bf);
698 GNUNET_BLOCK_group_destroy (bg);
699 }
700}
701
702
703/**
704 * The list of the first #bucket_size peers of @a bucket
705 * changed. We should thus make sure we have called 'hold'
706 * all of the first bucket_size peers!
707 *
708 * @param[in,out] bucket the bucket where the peer set changed
709 */
710static void
711update_hold (struct PeerBucket *bucket)
712{
713 unsigned int off = 0;
714
715 /* find the peer -- we just go over all of them, should
716 be hardly any more expensive than just finding the 'right'
717 one. */
718 for (struct PeerInfo *pos = bucket->head;
719 NULL != pos;
720 pos = pos->next)
721 {
722 if (off > bucket_size)
723 break; /* We only hold up to #bucket_size peers per bucket */
724 off++;
725 for (struct Target *tp = pos->t_head;
726 NULL != tp;
727 tp = tp->next)
728 if (NULL == tp->ph)
729 tp->ph = GDS_u_hold (tp->u,
730 tp->utarget);
731 }
732}
733
734
735void
736GDS_u_connect (void *cls,
737 struct GNUNET_DHTU_Target *target,
738 const struct GNUNET_PeerIdentity *pid,
739 void **ctx)
740{
741 struct GDS_Underlay *u = cls;
742 struct PeerInfo *pi;
743 struct PeerBucket *bucket;
744 bool do_hold = false;
745
746 /* Check for connect to self message */
747 if (0 == GNUNET_memcmp (&GDS_my_identity,
748 pid))
749 return;
750 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
751 "Connected to peer %s\n",
752 GNUNET_i2s (pid));
753 pi = GNUNET_CONTAINER_multipeermap_get (all_connected_peers,
754 pid);
755 if (NULL == pi)
756 {
757 GNUNET_STATISTICS_update (GDS_stats,
758 "# peers connected",
759 1,
760 GNUNET_NO);
761 pi = GNUNET_new (struct PeerInfo);
762 pi->id = *pid;
763 GNUNET_CRYPTO_hash (pid,
764 sizeof(*pid),
765 &pi->phash);
766 pi->peer_bucket = find_bucket (&pi->phash);
767 GNUNET_assert ( (pi->peer_bucket >= 0) &&
768 ((unsigned int) pi->peer_bucket < MAX_BUCKETS));
769 bucket = &k_buckets[pi->peer_bucket];
770 GNUNET_CONTAINER_DLL_insert_tail (bucket->head,
771 bucket->tail,
772 pi);
773 bucket->peers_size++;
774 closest_bucket = GNUNET_MAX (closest_bucket,
775 (unsigned int) pi->peer_bucket + 1);
776 GNUNET_assert (GNUNET_OK ==
777 GNUNET_CONTAINER_multipeermap_put (all_connected_peers,
778 &pi->id,
779 pi,
780 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
781 if (bucket->peers_size <= bucket_size)
782 {
783 newly_found_peers++;
784 do_hold = true;
785 }
786 if ( (1 == GNUNET_CONTAINER_multipeermap_size (all_connected_peers)) &&
787 (GNUNET_YES != disable_try_connect) )
788 {
789 /* got a first connection, good time to start with FIND PEER requests... */
790 GNUNET_assert (NULL == find_peer_task);
791 find_peer_task = GNUNET_SCHEDULER_add_now (&send_find_peer_message,
792 NULL);
793 }
794 }
795 {
796 struct Target *t;
797
798 t = GNUNET_new (struct Target);
799 t->u = u;
800 t->utarget = target;
801 t->pi = pi;
802 GNUNET_CONTAINER_DLL_insert (pi->t_head,
803 pi->t_tail,
804 t);
805 *ctx = t;
806
807 }
808 if (do_hold)
809 update_hold (bucket);
810}
811
812
813void
814GDS_u_disconnect (void *ctx)
815{
816 struct Target *t = ctx;
817 struct PeerInfo *pi;
818 struct PeerBucket *bucket;
819 bool was_held = false;
820
821 /* Check for disconnect from self message (on shutdown) */
822 if (NULL == t)
823 return;
824 pi = t->pi;
825 GNUNET_CONTAINER_DLL_remove (pi->t_head,
826 pi->t_tail,
827 t);
828 if (NULL != t->ph)
829 {
830 GDS_u_drop (t->u,
831 t->ph);
832 t->ph = NULL;
833 was_held = true;
834 }
835 if (t->load > 0)
836 {
837 t->dropped = true;
838 t->pi = NULL;
839 }
840 else
841 {
842 GNUNET_free (t);
843 }
844 if (NULL != pi->t_head)
845 return; /* got other connections still */
846 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
847 "Disconnected from peer %s\n",
848 GNUNET_i2s (&pi->id));
849 GNUNET_STATISTICS_update (GDS_stats,
850 "# peers connected",
851 -1,
852 GNUNET_NO);
853 GNUNET_assert (GNUNET_YES ==
854 GNUNET_CONTAINER_multipeermap_remove (all_connected_peers,
855 &pi->id,
856 pi));
857 if ( (0 == GNUNET_CONTAINER_multipeermap_size (all_connected_peers)) &&
858 (GNUNET_YES != disable_try_connect))
859 {
860 GNUNET_SCHEDULER_cancel (find_peer_task);
861 find_peer_task = NULL;
862 }
863 GNUNET_assert (pi->peer_bucket >= 0);
864 bucket = &k_buckets[pi->peer_bucket];
865 GNUNET_CONTAINER_DLL_remove (bucket->head,
866 bucket->tail,
867 pi);
868 GNUNET_assert (bucket->peers_size > 0);
869 bucket->peers_size--;
870 if ( (was_held) &&
871 (bucket->peers_size >= bucket_size - 1) )
872 update_hold (bucket);
873 while ( (closest_bucket > 0) &&
874 (0 == k_buckets[closest_bucket - 1].peers_size))
875 closest_bucket--;
876 GNUNET_free (pi->hello);
877 GNUNET_free (pi);
878}
879
880
881/**
882 * To how many peers should we (on average) forward the request to
883 * obtain the desired target_replication count (on average).
884 *
885 * @param hop_count number of hops the message has traversed
886 * @param target_replication the number of total paths desired
887 * @return Some number of peers to forward the message to
888 */
889static unsigned int
890get_forward_count (uint16_t hop_count,
891 uint16_t target_replication)
892{
893 uint32_t random_value;
894 uint32_t forward_count;
895 float target_value;
896 float rm1;
897
898 if (hop_count > GDS_NSE_get () * 4.0)
899 {
900 /* forcefully terminate */
901 GNUNET_STATISTICS_update (GDS_stats,
902 "# requests TTL-dropped",
903 1,
904 GNUNET_NO);
905 return 0;
906 }
907 if (hop_count > GDS_NSE_get () * 2.0)
908 {
909 /* Once we have reached our ideal number of hops, only forward to 1 peer */
910 return 1;
911 }
912 /* bound by system-wide maximum and minimum */
913 if (0 == target_replication)
914 target_replication = 1; /* 0 is verboten */
915 target_replication =
916 GNUNET_MIN (GNUNET_DHT_MAXIMUM_REPLICATION_LEVEL,
917 target_replication);
918 rm1 = target_replication - 1.0;
919 target_value =
920 1 + (rm1) / (GDS_NSE_get () + (rm1 * hop_count));
921
922 /* Set forward count to floor of target_value */
923 forward_count = (uint32_t) target_value;
924 /* Subtract forward_count (floor) from target_value (yields value between 0 and 1) */
925 target_value = target_value - forward_count;
926 random_value = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
927 UINT32_MAX);
928 if (random_value < (target_value * UINT32_MAX))
929 forward_count++;
930 return GNUNET_MIN (forward_count,
931 GNUNET_DHT_MAXIMUM_REPLICATION_LEVEL);
932}
933
934
935/**
936 * Check whether my identity is closer than any known peers. If a
937 * non-null bloomfilter is given, check if this is the closest peer
938 * that hasn't already been routed to.
939 *
940 * @param key hash code to check closeness to
941 * @param bloom bloomfilter, exclude these entries from the decision
942 * @return #GNUNET_YES if node location is closest,
943 * #GNUNET_NO otherwise.
944 */
945enum GNUNET_GenericReturnValue
946GDS_am_closest_peer (const struct GNUNET_HashCode *key,
947 const struct GNUNET_CONTAINER_BloomFilter *bloom)
948{
949 if (0 == GNUNET_memcmp (&GDS_my_identity_hash,
950 key))
951 return GNUNET_YES;
952 for (int bucket_num = find_bucket (key);
953 bucket_num < closest_bucket;
954 bucket_num++)
955 {
956 unsigned int count = 0;
957
958 GNUNET_assert (bucket_num >= 0);
959 for (struct PeerInfo *pos = k_buckets[bucket_num].head;
960 NULL != pos;
961 pos = pos->next)
962 {
963 if (count >= bucket_size)
964 break; /* we only consider first #bucket_size entries per bucket */
965 count++;
966 if ( (NULL != bloom) &&
967 (GNUNET_YES ==
968 GNUNET_CONTAINER_bloomfilter_test (bloom,
969 &pos->phash)) )
970 continue; /* Ignore filtered peers */
971 /* All peers in this bucket must be closer than us, as
972 they mismatch with our PID on the pivotal bit. So
973 because an unfiltered peer exists, we are not the
974 closest. */
975 int delta = GNUNET_CRYPTO_hash_xorcmp (&pos->phash,
976 &GDS_my_identity_hash,
977 key);
978 switch (delta)
979 {
980 case -1: /* pos closer */
981 return GNUNET_NO;
982 case 0: /* identical, impossible! */
983 GNUNET_assert (0);
984 break;
985 case 1: /* I am closer */
986 break;
987 }
988 }
989 }
990 /* No closer (unfiltered) peers found; we must be the closest! */
991 return GNUNET_YES;
992}
993
994
995/**
996 * Select a peer from the routing table that would be a good routing
997 * destination for sending a message for @a key. The resulting peer
998 * must not be in the set of @a bloom blocked peers.
999 *
1000 * Note that we should not ALWAYS select the closest peer to the
1001 * target, we do a "random" peer selection if the number of @a hops
1002 * is below the logarithm of the network size estimate.
1003 *
1004 * In all cases, we only consider at most the first #bucket_size peers of any
1005 * #k_buckets. The other peers in the bucket are there because GNUnet doesn't
1006 * really allow the DHT to "reject" connections, but we only use the first
1007 * #bucket_size, even if more exist. (The idea is to ensure that those
1008 * connections are frequently used, and for others to be not used by the DHT,
1009 * and thus possibly dropped by transport due to disuse).
1010 *
1011 * @param key the key we are selecting a peer to route to
1012 * @param bloom a Bloom filter containing entries this request has seen already
1013 * @param hops how many hops has this message traversed thus far
1014 * @return Peer to route to, or NULL on error
1015 */
1016static struct PeerInfo *
1017select_peer (const struct GNUNET_HashCode *key,
1018 const struct GNUNET_CONTAINER_BloomFilter *bloom,
1019 uint32_t hops)
1020{
1021 if (0 == closest_bucket)
1022 {
1023 GNUNET_STATISTICS_update (GDS_stats,
1024 "# Peer selection failed",
1025 1,
1026 GNUNET_NO);
1027 return NULL; /* we have zero connections */
1028 }
1029 if (hops >= GDS_NSE_get ())
1030 {
1031 /* greedy selection (closest peer that is not in Bloom filter) */
1032 struct PeerInfo *chosen = NULL;
1033 int best_bucket;
1034 int bucket_offset;
1035
1036 {
1037 struct GNUNET_HashCode xor;
1038
1039 GNUNET_CRYPTO_hash_xor (key,
1040 &GDS_my_identity_hash,
1041 &xor);
1042 best_bucket = GNUNET_CRYPTO_hash_count_leading_zeros (&xor);
1043 }
1044 if (best_bucket >= closest_bucket)
1045 bucket_offset = closest_bucket - 1;
1046 else
1047 bucket_offset = best_bucket;
1048 while (-1 != bucket_offset)
1049 {
1050 struct PeerBucket *bucket = &k_buckets[bucket_offset];
1051 unsigned int count = 0;
1052
1053 for (struct PeerInfo *pos = bucket->head;
1054 NULL != pos;
1055 pos = pos->next)
1056 {
1057 if (count >= bucket_size)
1058 break; /* we only consider first #bucket_size entries per bucket */
1059 count++;
1060 if ( (NULL != bloom) &&
1061 (GNUNET_YES ==
1062 GNUNET_CONTAINER_bloomfilter_test (bloom,
1063 &pos->phash)) )
1064 {
1065 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1066 "Excluded peer `%s' due to BF match in greedy routing for %s\n",
1067 GNUNET_i2s (&pos->id),
1068 GNUNET_h2s (key));
1069 continue;
1070 }
1071 if (NULL == chosen)
1072 {
1073 /* First candidate */
1074 chosen = pos;
1075 }
1076 else
1077 {
1078 int delta = GNUNET_CRYPTO_hash_xorcmp (&pos->phash,
1079 &chosen->phash,
1080 key);
1081 switch (delta)
1082 {
1083 case -1: /* pos closer */
1084 chosen = pos;
1085 break;
1086 case 0: /* identical, impossible! */
1087 GNUNET_assert (0);
1088 break;
1089 case 1: /* chosen closer */
1090 break;
1091 }
1092 }
1093 count++;
1094 } /* for all (#bucket_size) peers in bucket */
1095 if (NULL != chosen)
1096 break;
1097
1098 /* If we chose nothing in first iteration, first go through deeper
1099 buckets (best chance to find a good match), and if we still found
1100 nothing, then to shallower buckets. Terminate on any match in the
1101 current bucket, as this search order guarantees that it can only get
1102 worse as we keep going. */
1103 if (bucket_offset > best_bucket)
1104 {
1105 /* Go through more deeper buckets */
1106 bucket_offset++;
1107 if (bucket_offset == closest_bucket)
1108 {
1109 /* Can't go any deeper, if nothing selected,
1110 go for shallower buckets */
1111 bucket_offset = best_bucket - 1;
1112 }
1113 }
1114 else
1115 {
1116 /* We're either at the 'best_bucket' or already moving
1117 on to shallower buckets. */
1118 if (bucket_offset == best_bucket)
1119 bucket_offset++; /* go for deeper buckets */
1120 else
1121 bucket_offset--; /* go for shallower buckets */
1122 }
1123 } /* for applicable buckets (starting at best match) */
1124 if (NULL == chosen)
1125 {
1126 GNUNET_STATISTICS_update (GDS_stats,
1127 "# Peer selection failed",
1128 1,
1129 GNUNET_NO);
1130 return NULL;
1131 }
1132 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1133 "Selected peer `%s' in greedy routing for %s\n",
1134 GNUNET_i2s (&chosen->id),
1135 GNUNET_h2s (key));
1136 return chosen;
1137 } /* end of 'greedy' peer selection */
1138
1139 /* select "random" peer */
1140 /* count number of peers that are available and not filtered,
1141 but limit to at most #bucket_size peers, starting with
1142 those 'furthest' from us. */
1143 {
1144 unsigned int total = 0;
1145 unsigned int selected;
1146
1147 for (unsigned int bc = 0; bc < closest_bucket; bc++)
1148 {
1149 struct PeerBucket *bucket = &k_buckets[bc];
1150 unsigned int count = 0;
1151
1152 for (struct PeerInfo *pos = bucket->head;
1153 NULL != pos;
1154 pos = pos->next)
1155 {
1156 count++;
1157 if (count > bucket_size)
1158 break; /* limits search to #bucket_size peers per bucket */
1159 if ( (NULL != bloom) &&
1160 (GNUNET_YES ==
1161 GNUNET_CONTAINER_bloomfilter_test (bloom,
1162 &pos->phash)) )
1163 {
1164 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1165 "Excluded peer `%s' due to BF match in random routing for %s\n",
1166 GNUNET_i2s (&pos->id),
1167 GNUNET_h2s (key));
1168 continue; /* Ignore filtered peers */
1169 }
1170 total++;
1171 } /* for all peers in bucket */
1172 } /* for all buckets */
1173 if (0 == total) /* No peers to select from! */
1174 {
1175 GNUNET_STATISTICS_update (GDS_stats,
1176 "# Peer selection failed",
1177 1,
1178 GNUNET_NO);
1179 return NULL;
1180 }
1181
1182 /* Now actually choose a peer */
1183 selected = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1184 total);
1185 for (unsigned int bc = 0; bc < closest_bucket; bc++)
1186 {
1187 unsigned int count = 0;
1188
1189 for (struct PeerInfo *pos = k_buckets[bc].head;
1190 pos != NULL;
1191 pos = pos->next)
1192 {
1193 count++;
1194 if (count > bucket_size)
1195 break; /* limits search to #bucket_size peers per bucket */
1196
1197 if ( (NULL != bloom) &&
1198 (GNUNET_YES ==
1199 GNUNET_CONTAINER_bloomfilter_test (bloom,
1200 &pos->phash)) )
1201 continue; /* Ignore bloomfiltered peers */
1202 if (0 == selected--)
1203 {
1204 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1205 "Selected peer `%s' in random routing for %s\n",
1206 GNUNET_i2s (&pos->id),
1207 GNUNET_h2s (key));
1208 return pos;
1209 }
1210 } /* for peers in bucket */
1211 } /* for all buckets */
1212 } /* random peer selection scope */
1213 GNUNET_break (0);
1214 return NULL;
1215}
1216
1217
1218/**
1219 * Compute the set of peers that the given request should be
1220 * forwarded to.
1221 *
1222 * @param key routing key
1223 * @param[in,out] bloom Bloom filter excluding peers as targets,
1224 * all selected peers will be added to the Bloom filter
1225 * @param hop_count number of hops the request has traversed so far
1226 * @param target_replication desired number of replicas
1227 * @param[out] targets where to store an array of target peers (to be
1228 * free()ed by the caller)
1229 * @return number of peers returned in @a targets.
1230 */
1231static unsigned int
1232get_target_peers (const struct GNUNET_HashCode *key,
1233 struct GNUNET_CONTAINER_BloomFilter *bloom,
1234 uint16_t hop_count,
1235 uint16_t target_replication,
1236 struct PeerInfo ***targets)
1237{
1238 unsigned int target;
1239 unsigned int off;
1240 struct PeerInfo **rtargets;
1241
1242 GNUNET_assert (NULL != bloom);
1243 target = get_forward_count (hop_count,
1244 target_replication);
1245 if (0 == target)
1246 {
1247 *targets = NULL;
1248 return 0;
1249 }
1250 rtargets = GNUNET_new_array (target,
1251 struct PeerInfo *);
1252 for (off = 0; off < target; off++)
1253 {
1254 struct PeerInfo *nxt;
1255
1256 nxt = select_peer (key,
1257 bloom,
1258 hop_count);
1259 if (NULL == nxt)
1260 break;
1261 rtargets[off] = nxt;
1262 GNUNET_break (GNUNET_NO ==
1263 GNUNET_CONTAINER_bloomfilter_test (bloom,
1264 &nxt->phash));
1265 GNUNET_CONTAINER_bloomfilter_add (bloom,
1266 &nxt->phash);
1267 }
1268 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1269 "Selected %u/%u peers at hop %u for %s (target was %u)\n",
1270 off,
1271 GNUNET_CONTAINER_multipeermap_size (all_connected_peers),
1272 (unsigned int) hop_count,
1273 GNUNET_h2s (key),
1274 target);
1275 if (0 == off)
1276 {
1277 GNUNET_free (rtargets);
1278 *targets = NULL;
1279 return 0;
1280 }
1281 *targets = rtargets;
1282 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1283 "Forwarding query `%s' to %u peers (goal was %u peers)\n",
1284 GNUNET_h2s (key),
1285 off,
1286 target);
1287 return off;
1288}
1289
1290
1291/**
1292 * If we got a HELLO, consider it for our own routing table
1293 *
1294 * @param bd block data we got
1295 */
1296static void
1297hello_check (const struct GNUNET_DATACACHE_Block *bd)
1298{
1299 struct GNUNET_PeerIdentity pid;
1300 struct GNUNET_HELLO_Builder *b;
1301
1302 if (GNUNET_BLOCK_TYPE_DHT_URL_HELLO != bd->type)
1303 return;
1304
1305 b = GNUNET_HELLO_builder_from_block (bd->data,
1306 bd->data_size);
1307 if (GNUNET_YES != disable_try_connect)
1308 GNUNET_HELLO_builder_iterate (b,
1309 &pid,
1310 &GDS_try_connect,
1311 &pid);
1312 GNUNET_HELLO_builder_free (b);
1313}
1314
1315
1316enum GNUNET_GenericReturnValue
1317GDS_NEIGHBOURS_handle_put (const struct GNUNET_DATACACHE_Block *bd,
1318 uint16_t desired_replication_level,
1319 uint16_t hop_count,
1320 struct GNUNET_CONTAINER_BloomFilter *bf)
1321{
1322 unsigned int target_count;
1323 struct PeerInfo **targets;
1324 size_t msize;
1325 unsigned int skip_count;
1326 enum GNUNET_DHT_RouteOption ro = bd->ro;
1327 unsigned int put_path_length = bd->put_path_length;
1328 const struct GNUNET_DHT_PathElement *put_path = bd->put_path;
1329 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
1330 bool tracking = (0 != (ro & GNUNET_DHT_RO_RECORD_ROUTE));
1331 const struct GNUNET_PeerIdentity *trunc_peer
1332 = truncated
1333 ? &bd->trunc_peer
1334 : NULL;
1335
1336#if SANITY_CHECKS > 1
1337 unsigned int failure_offset;
1338
1339 failure_offset
1340 = GNUNET_DHT_verify_path (bd->data,
1341 bd->data_size,
1342 bd->expiration_time,
1343 trunc_peer,
1344 put_path,
1345 put_path_length,
1346 NULL, 0, /* get_path */
1347 &GDS_my_identity);
1348 if (0 != failure_offset)
1349 {
1350 GNUNET_break_op (0);
1351 truncated = true;
1352 trunc_peer = &put_path[failure_offset - 1].pred;
1353 put_path = &put_path[failure_offset];
1354 put_path_length = put_path_length - failure_offset;
1355 ro |= GNUNET_DHT_RO_TRUNCATED;
1356 }
1357#endif
1358 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1359 "Adding myself (%s) to PUT bloomfilter for %s with RO(%s/%s)\n",
1360 GNUNET_i2s (&GDS_my_identity),
1361 GNUNET_h2s (&bd->key),
1362 (bd->ro & GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE) ? "x" : "-",
1363 (bd->ro & GNUNET_DHT_RO_RECORD_ROUTE) ? "R" : "-");
1364
1365 /* if we got a HELLO, consider it for our own routing table */
1366 hello_check (bd);
1367 GNUNET_assert (NULL != bf);
1368 GNUNET_CONTAINER_bloomfilter_add (bf,
1369 &GDS_my_identity_hash);
1370 GNUNET_STATISTICS_update (GDS_stats,
1371 "# PUT requests routed",
1372 1,
1373 GNUNET_NO);
1374 if (bd->data_size
1375 > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE
1376 - sizeof(struct PeerPutMessage))
1377 {
1378 GNUNET_break (0);
1379 return GNUNET_SYSERR;
1380 }
1381 msize = bd->data_size + sizeof(struct PeerPutMessage);
1382 if (tracking)
1383 {
1384 if (msize + sizeof (struct GNUNET_CRYPTO_EddsaSignature)
1385 > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE)
1386 {
1387 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1388 "Discarding message that is too large due to tracking\n");
1389 return GNUNET_NO;
1390 }
1391 msize += sizeof (struct GNUNET_CRYPTO_EddsaSignature);
1392 }
1393 else
1394 {
1395 /* If tracking is disabled, also discard any path we might have
1396 gotten from some broken peer */
1397 GNUNET_break_op (0 == put_path_length);
1398 put_path_length = 0;
1399 }
1400 if (truncated)
1401 msize += sizeof (struct GNUNET_PeerIdentity);
1402 if (msize + put_path_length * sizeof(struct GNUNET_DHT_PathElement)
1403 > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE)
1404 {
1405 unsigned int mlen;
1406 unsigned int ppl;
1407
1408 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1409 "Truncating path that is too large due\n");
1410 mlen = GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE - msize;
1411 if (! truncated)
1412 {
1413 /* We need extra space for the truncation, consider that,
1414 too! */
1415 truncated = true;
1416 mlen -= sizeof (struct GNUNET_PeerIdentity);
1417 msize += sizeof (struct GNUNET_PeerIdentity);
1418 }
1419 /* compute maximum length of path we can keep */
1420 ppl = mlen / sizeof (struct GNUNET_DHT_PathElement);
1421 GNUNET_assert (put_path_length - ppl > 0);
1422 trunc_peer = &put_path[put_path_length - ppl - 1].pred;
1423 put_path = &put_path[put_path_length - ppl];
1424 put_path_length = ppl;
1425 ro |= GNUNET_DHT_RO_TRUNCATED;
1426 }
1427 else
1428 {
1429 msize += bd->put_path_length * sizeof(struct GNUNET_DHT_PathElement);
1430 }
1431 target_count
1432 = get_target_peers (&bd->key,
1433 bf,
1434 hop_count,
1435 desired_replication_level,
1436 &targets);
1437 if (0 == target_count)
1438 {
1439 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1440 "Routing PUT for %s terminates after %u hops at %s\n",
1441 GNUNET_h2s (&bd->key),
1442 (unsigned int) hop_count,
1443 GNUNET_i2s (&GDS_my_identity));
1444 return GNUNET_NO;
1445 }
1446 skip_count = 0;
1447 for (unsigned int i = 0; i < target_count; i++)
1448 {
1449 struct PeerInfo *target = targets[i];
1450 struct PeerPutMessage *ppm;
1451 char buf[msize] GNUNET_ALIGN;
1452 struct GNUNET_DHT_PathElement *pp;
1453 void *data;
1454
1455 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1456 "Routing PUT for %s after %u hops to %s\n",
1457 GNUNET_h2s (&bd->key),
1458 (unsigned int) hop_count,
1459 GNUNET_i2s (&target->id));
1460 ppm = (struct PeerPutMessage *) buf;
1461 ppm->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_P2P_PUT);
1462 ppm->header.size = htons (sizeof (buf));
1463 ppm->type = htonl (bd->type);
1464 ppm->options = htons (ro);
1465 ppm->hop_count = htons (hop_count + 1);
1466 ppm->desired_replication_level = htons (desired_replication_level);
1467 ppm->put_path_length = htons (put_path_length);
1468 ppm->expiration_time = GNUNET_TIME_absolute_hton (bd->expiration_time);
1469 GNUNET_break (GNUNET_YES ==
1470 GNUNET_CONTAINER_bloomfilter_test (bf,
1471 &target->phash));
1472 GNUNET_assert (GNUNET_OK ==
1473 GNUNET_CONTAINER_bloomfilter_get_raw_data (bf,
1474 ppm->bloomfilter,
1475 DHT_BLOOM_SIZE));
1476 ppm->key = bd->key;
1477 if (truncated)
1478 {
1479 void *tgt = &ppm[1];
1480
1481 GNUNET_memcpy (tgt,
1482 trunc_peer,
1483 sizeof (struct GNUNET_PeerIdentity));
1484 pp = (struct GNUNET_DHT_PathElement *)
1485 (tgt + sizeof (struct GNUNET_PeerIdentity));
1486 }
1487 else
1488 {
1489 pp = (struct GNUNET_DHT_PathElement *) &ppm[1];
1490 }
1491 GNUNET_memcpy (pp,
1492 put_path,
1493 sizeof (struct GNUNET_DHT_PathElement) * put_path_length);
1494 if (tracking)
1495 {
1496 void *tgt = &pp[put_path_length];
1497 struct GNUNET_CRYPTO_EddsaSignature last_sig;
1498
1499 if (0 == put_path_length)
1500 {
1501 /* Note that the signature in 'put_path' was not initialized before,
1502 so this is crucial to avoid sending garbage. */
1503 sign_path (bd->data,
1504 bd->data_size,
1505 bd->expiration_time,
1506 trunc_peer,
1507 &target->id,
1508 &last_sig);
1509 }
1510 else
1511 {
1512 sign_path (bd->data,
1513 bd->data_size,
1514 bd->expiration_time,
1515 &pp[put_path_length - 1].pred,
1516 &target->id,
1517 &last_sig);
1518 }
1519 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1520 "Signing PUT PATH %u => %s\n",
1521 put_path_length,
1522 GNUNET_B2S (&last_sig));
1523 memcpy (tgt,
1524 &last_sig,
1525 sizeof (last_sig));
1526 data = tgt + sizeof (last_sig);
1527 }
1528 else /* ! tracking */
1529 {
1530 data = &ppm[1];
1531 }
1532 GNUNET_memcpy (data,
1533 bd->data,
1534 bd->data_size);
1535 do_send (target,
1536 &ppm->header);
1537 }
1538 GNUNET_free (targets);
1539 GNUNET_STATISTICS_update (GDS_stats,
1540 "# PUT messages queued for transmission",
1541 target_count - skip_count,
1542 GNUNET_NO);
1543 return (skip_count < target_count) ? GNUNET_OK : GNUNET_NO;
1544}
1545
1546
1547enum GNUNET_GenericReturnValue
1548GDS_NEIGHBOURS_handle_get (enum GNUNET_BLOCK_Type type,
1549 enum GNUNET_DHT_RouteOption options,
1550 uint16_t desired_replication_level,
1551 uint16_t hop_count,
1552 const struct GNUNET_HashCode *key,
1553 const void *xquery,
1554 size_t xquery_size,
1555 struct GNUNET_BLOCK_Group *bg,
1556 struct GNUNET_CONTAINER_BloomFilter *peer_bf)
1557{
1558 unsigned int target_count;
1559 struct PeerInfo **targets;
1560 size_t msize;
1561 size_t result_filter_size;
1562 void *result_filter;
1563 unsigned int skip_count;
1564
1565 GNUNET_assert (NULL != peer_bf);
1566 GNUNET_STATISTICS_update (GDS_stats,
1567 "# GET requests routed",
1568 1,
1569 GNUNET_NO);
1570 target_count = get_target_peers (key,
1571 peer_bf,
1572 hop_count,
1573 desired_replication_level,
1574 &targets);
1575 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1576 "Adding myself (%s) to GET bloomfilter for %s with RO(%s/%s)\n",
1577 GNUNET_i2s (&GDS_my_identity),
1578 GNUNET_h2s (key),
1579 (options & GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE) ? "x" : "-",
1580 (options & GNUNET_DHT_RO_RECORD_ROUTE) ? "R" : "-");
1581
1582 GNUNET_CONTAINER_bloomfilter_add (peer_bf,
1583 &GDS_my_identity_hash);
1584 if (0 == target_count)
1585 {
1586 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1587 "Routing GET for %s terminates after %u hops at %s\n",
1588 GNUNET_h2s (key),
1589 (unsigned int) hop_count,
1590 GNUNET_i2s (&GDS_my_identity));
1591 return GNUNET_NO;
1592 }
1593 if (GNUNET_OK !=
1594 GNUNET_BLOCK_group_serialize (bg,
1595 &result_filter,
1596 &result_filter_size))
1597 {
1598 result_filter = NULL;
1599 result_filter_size = 0;
1600 }
1601 msize = xquery_size + result_filter_size;
1602 if (msize + sizeof(struct PeerGetMessage) >= GNUNET_MAX_MESSAGE_SIZE)
1603 {
1604 GNUNET_break (0);
1605 GNUNET_free (result_filter);
1606 GNUNET_free (targets);
1607 return GNUNET_NO;
1608 }
1609 /* forward request */
1610 skip_count = 0;
1611 for (unsigned int i = 0; i < target_count; i++)
1612 {
1613 struct PeerInfo *target = targets[i];
1614 struct PeerGetMessage *pgm;
1615 char buf[sizeof (*pgm) + msize] GNUNET_ALIGN;
1616 char *rf;
1617
1618 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1619 "Routing GET for %s after %u hops to %s\n",
1620 GNUNET_h2s (key),
1621 (unsigned int) hop_count,
1622 GNUNET_i2s (&target->id));
1623 pgm = (struct PeerGetMessage *) buf;
1624 pgm->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_P2P_GET);
1625 pgm->header.size = htons (sizeof (buf));
1626 pgm->type = htonl (type);
1627 pgm->options = htons (options);
1628 pgm->hop_count = htons (hop_count + 1);
1629 pgm->desired_replication_level = htons (desired_replication_level);
1630 pgm->result_filter_size = htons ((uint16_t) result_filter_size);
1631 GNUNET_break (GNUNET_YES ==
1632 GNUNET_CONTAINER_bloomfilter_test (peer_bf,
1633 &target->phash));
1634 GNUNET_assert (GNUNET_OK ==
1635 GNUNET_CONTAINER_bloomfilter_get_raw_data (peer_bf,
1636 pgm->bloomfilter,
1637 DHT_BLOOM_SIZE));
1638 pgm->key = *key;
1639 rf = (char *) &pgm[1];
1640 GNUNET_memcpy (rf,
1641 result_filter,
1642 result_filter_size);
1643 GNUNET_memcpy (&rf[result_filter_size],
1644 xquery,
1645 xquery_size);
1646 do_send (target,
1647 &pgm->header);
1648 }
1649 GNUNET_STATISTICS_update (GDS_stats,
1650 "# GET messages queued for transmission",
1651 target_count - skip_count,
1652 GNUNET_NO);
1653 GNUNET_free (targets);
1654 GNUNET_free (result_filter);
1655 return (skip_count < target_count) ? GNUNET_OK : GNUNET_NO;
1656}
1657
1658
1659struct PeerInfo *
1660GDS_NEIGHBOURS_lookup_peer (const struct GNUNET_PeerIdentity *target)
1661{
1662 return GNUNET_CONTAINER_multipeermap_get (all_connected_peers,
1663 target);
1664}
1665
1666
1667bool
1668GDS_NEIGHBOURS_handle_reply (struct PeerInfo *pi,
1669 const struct GNUNET_DATACACHE_Block *bd,
1670 const struct GNUNET_HashCode *query_hash,
1671 unsigned int get_path_length,
1672 const struct GNUNET_DHT_PathElement *get_path)
1673{
1674 struct GNUNET_DHT_PathElement *paths;
1675 size_t msize;
1676 unsigned int ppl = bd->put_path_length;
1677 const struct GNUNET_DHT_PathElement *put_path = bd->put_path;
1678 enum GNUNET_DHT_RouteOption ro = bd->ro;
1679 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
1680 const struct GNUNET_PeerIdentity *trunc_peer
1681 = truncated
1682 ? &bd->trunc_peer
1683 : NULL;
1684 bool tracking = (0 != (ro & GNUNET_DHT_RO_RECORD_ROUTE));
1685#if SANITY_CHECKS > 1
1686 unsigned int failure_offset;
1687
1688 failure_offset
1689 = GNUNET_DHT_verify_path (bd->data,
1690 bd->data_size,
1691 bd->expiration_time,
1692 trunc_peer,
1693 put_path,
1694 ppl,
1695 get_path,
1696 get_path_length,
1697 &GDS_my_identity);
1698 if (0 != failure_offset)
1699 {
1700 GNUNET_assert (failure_offset <= ppl + get_path_length);
1701 GNUNET_break_op (0);
1702 if (failure_offset < ppl)
1703 {
1704 trunc_peer = &put_path[failure_offset - 1].pred;
1705 put_path += failure_offset;
1706 ppl -= failure_offset;
1707 truncated = true;
1708 ro |= GNUNET_DHT_RO_TRUNCATED;
1709 }
1710 else
1711 {
1712 failure_offset -= ppl;
1713 if (0 == failure_offset)
1714 trunc_peer = &put_path[ppl - 1].pred;
1715 else
1716 trunc_peer = &get_path[failure_offset - 1].pred;
1717 ppl = 0;
1718 put_path = NULL;
1719 truncated = true;
1720 ro |= GNUNET_DHT_RO_TRUNCATED;
1721 get_path += failure_offset;
1722 get_path_length -= failure_offset;
1723 }
1724 }
1725#endif
1726 msize = bd->data_size + sizeof (struct PeerResultMessage);
1727 if (msize > GNUNET_MAX_MESSAGE_SIZE)
1728 {
1729 GNUNET_break_op (0);
1730 return false;
1731 }
1732 if (truncated)
1733 msize += sizeof (struct GNUNET_PeerIdentity);
1734 if (tracking)
1735 msize += sizeof (struct GNUNET_CRYPTO_EddsaSignature);
1736 if (msize < bd->data_size)
1737 {
1738 GNUNET_break_op (0);
1739 return false;
1740 }
1741 if ( (GNUNET_MAX_MESSAGE_SIZE - msize)
1742 / sizeof(struct GNUNET_DHT_PathElement)
1743 < (get_path_length + ppl) )
1744 {
1745 get_path_length = 0;
1746 ppl = 0;
1747 }
1748 if ( (get_path_length > UINT16_MAX) ||
1749 (ppl > UINT16_MAX) )
1750 {
1751 GNUNET_break (0);
1752 get_path_length = 0;
1753 ppl = 0;
1754 }
1755 msize += (get_path_length + ppl)
1756 * sizeof(struct GNUNET_DHT_PathElement);
1757 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1758 "Forwarding reply for key %s to peer %s\n",
1759 GNUNET_h2s (query_hash),
1760 GNUNET_i2s (&pi->id));
1761 GNUNET_STATISTICS_update (GDS_stats,
1762 "# RESULT messages queued for transmission",
1763 1,
1764 GNUNET_NO);
1765 {
1766 struct PeerResultMessage *prm;
1767 char buf[msize] GNUNET_ALIGN;
1768 void *data;
1769
1770 prm = (struct PeerResultMessage *) buf;
1771 prm->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_P2P_RESULT);
1772 prm->header.size = htons (sizeof (buf));
1773 prm->type = htonl ((uint32_t) bd->type);
1774 prm->options = htonl ((uint32_t) ro);
1775 prm->put_path_length = htons ((uint16_t) ppl);
1776 prm->get_path_length = htons ((uint16_t) get_path_length);
1777 prm->expiration_time = GNUNET_TIME_absolute_hton (bd->expiration_time);
1778 prm->key = *query_hash;
1779 if (truncated)
1780 {
1781 void *tgt = &prm[1];
1782
1783 GNUNET_memcpy (tgt,
1784 trunc_peer,
1785 sizeof (struct GNUNET_PeerIdentity));
1786 paths = (struct GNUNET_DHT_PathElement *)
1787 (tgt + sizeof (struct GNUNET_PeerIdentity));
1788 }
1789 else
1790 {
1791 paths = (struct GNUNET_DHT_PathElement *) &prm[1];
1792 }
1793 if (NULL != put_path)
1794 {
1795 GNUNET_memcpy (paths,
1796 put_path,
1797 ppl * sizeof(struct GNUNET_DHT_PathElement));
1798 }
1799 else
1800 {
1801 GNUNET_assert (0 == ppl);
1802 }
1803 if (NULL != get_path)
1804 {
1805 GNUNET_memcpy (&paths[ppl],
1806 get_path,
1807 get_path_length * sizeof(struct GNUNET_DHT_PathElement));
1808 }
1809 else
1810 {
1811 GNUNET_assert (0 == get_path_length);
1812 }
1813 if (tracking)
1814 {
1815 struct GNUNET_CRYPTO_EddsaSignature sig;
1816 void *tgt = &paths[get_path_length + ppl];
1817 const struct GNUNET_PeerIdentity *pred;
1818
1819 if (ppl + get_path_length > 0)
1820 pred = &paths[ppl + get_path_length - 1].pred;
1821 else if (truncated)
1822 pred = trunc_peer;
1823 else
1824 pred = NULL; /* we are first! */
1825 /* Note that the last signature in 'paths' was not initialized before,
1826 so this is crucial to avoid sending garbage. */
1827 sign_path (bd->data,
1828 bd->data_size,
1829 bd->expiration_time,
1830 pred,
1831 &pi->id,
1832 &sig);
1833 memcpy (tgt,
1834 &sig,
1835 sizeof (sig));
1836 data = tgt + sizeof (sig);
1837 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1838 "Signing GET PATH %u/%u of %s => %s\n",
1839 ppl,
1840 get_path_length,
1841 GNUNET_h2s (query_hash),
1842 GNUNET_B2S (&sig));
1843#if SANITY_CHECKS > 1
1844 {
1845 struct GNUNET_DHT_PathElement xpaths[get_path_length + 1];
1846
1847 memcpy (xpaths,
1848 &paths[ppl],
1849 get_path_length * sizeof (struct GNUNET_DHT_PathElement));
1850 xpaths[get_path_length].sig = sig;
1851 xpaths[get_path_length].pred = GDS_my_identity;
1852 if (0 !=
1853 GNUNET_DHT_verify_path (bd->data,
1854 bd->data_size,
1855 bd->expiration_time,
1856 trunc_peer,
1857 paths,
1858 ppl,
1859 xpaths,
1860 get_path_length + 1,
1861 &pi->id))
1862 {
1863 GNUNET_break (0);
1864 return false;
1865 }
1866 }
1867#endif
1868 }
1869 else
1870 {
1871 data = &prm[1];
1872 }
1873 GNUNET_memcpy (data,
1874 bd->data,
1875 bd->data_size);
1876 do_send (pi,
1877 &prm->header);
1878 }
1879 return true;
1880}
1881
1882
1883/**
1884 * Check validity of a p2p put request.
1885 *
1886 * @param cls closure with the `struct PeerInfo` of the sender
1887 * @param message message
1888 * @return #GNUNET_OK if the message is valid
1889 */
1890static enum GNUNET_GenericReturnValue
1891check_dht_p2p_put (void *cls,
1892 const struct PeerPutMessage *put)
1893{
1894 enum GNUNET_DHT_RouteOption ro
1895 = (enum GNUNET_DHT_RouteOption) ntohs (put->options);
1896 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
1897 bool has_path = (0 != (ro & GNUNET_DHT_RO_RECORD_ROUTE));
1898 uint16_t msize = ntohs (put->header.size);
1899 uint16_t putlen = ntohs (put->put_path_length);
1900 size_t xsize = (has_path
1901 ? sizeof (struct GNUNET_CRYPTO_EddsaSignature)
1902 : 0)
1903 + (truncated
1904 ? sizeof (struct GNUNET_PeerIdentity)
1905 : 0);
1906 size_t var_meta_size
1907 = putlen * sizeof(struct GNUNET_DHT_PathElement)
1908 + xsize;
1909
1910 (void) cls;
1911 if ( (msize <
1912 sizeof (struct PeerPutMessage) + var_meta_size) ||
1913 (putlen >
1914 (GNUNET_MAX_MESSAGE_SIZE
1915 - sizeof (struct PeerPutMessage)
1916 - xsize)
1917 / sizeof(struct GNUNET_DHT_PathElement)) )
1918 {
1919 GNUNET_break_op (0);
1920 return GNUNET_SYSERR;
1921 }
1922 return GNUNET_OK;
1923}
1924
1925
1926/**
1927 * Core handler for p2p put requests.
1928 *
1929 * @param cls closure with the `struct Target` of the sender
1930 * @param message message
1931 */
1932static void
1933handle_dht_p2p_put (void *cls,
1934 const struct PeerPutMessage *put)
1935{
1936 struct Target *t = cls;
1937 struct PeerInfo *peer = t->pi;
1938 enum GNUNET_DHT_RouteOption ro
1939 = (enum GNUNET_DHT_RouteOption) ntohs (put->options);
1940 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
1941 bool has_path = (0 != (ro & GNUNET_DHT_RO_RECORD_ROUTE));
1942 uint16_t msize = ntohs (put->header.size);
1943 uint16_t putlen = ntohs (put->put_path_length);
1944 const struct GNUNET_PeerIdentity *trunc_peer
1945 = truncated
1946 ? (const struct GNUNET_PeerIdentity *) &put[1]
1947 : NULL;
1948 const struct GNUNET_DHT_PathElement *put_path
1949 = truncated
1950 ? (const struct GNUNET_DHT_PathElement *) &trunc_peer[1]
1951 : (const struct GNUNET_DHT_PathElement *) &put[1];
1952 const struct GNUNET_CRYPTO_EddsaSignature *last_sig
1953 = has_path
1954 ? (const struct GNUNET_CRYPTO_EddsaSignature *) &put_path[putlen]
1955 : NULL;
1956 const char *data
1957 = has_path
1958 ? (const char *) &last_sig[1]
1959 : (const char *) &put_path[putlen];
1960 size_t var_meta_size
1961 = putlen * sizeof(struct GNUNET_DHT_PathElement)
1962 + has_path ? sizeof (*last_sig) : 0
1963 + truncated ? sizeof (*trunc_peer) : 0;
1964 struct GNUNET_DATACACHE_Block bd = {
1965 .key = put->key,
1966 .expiration_time = GNUNET_TIME_absolute_ntoh (put->expiration_time),
1967 .type = ntohl (put->type),
1968 .ro = ro,
1969 .data_size = msize - sizeof(*put) - var_meta_size,
1970 .data = data
1971 };
1972
1973 if (NULL != trunc_peer)
1974 bd.trunc_peer = *trunc_peer;
1975 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1976 "PUT for `%s' from %s with RO (%s/%s)\n",
1977 GNUNET_h2s (&put->key),
1978 GNUNET_i2s (&peer->id),
1979 (bd.ro & GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE) ? "x" : "-",
1980 has_path ? "R" : "-");
1981 if (GNUNET_TIME_absolute_is_past (bd.expiration_time))
1982 {
1983 GNUNET_STATISTICS_update (GDS_stats,
1984 "# Expired PUTs discarded",
1985 1,
1986 GNUNET_NO);
1987 return;
1988 }
1989 if (GNUNET_NO ==
1990 GNUNET_BLOCK_check_block (GDS_block_context,
1991 bd.type,
1992 bd.data,
1993 bd.data_size))
1994 {
1995 GNUNET_break_op (0);
1996 return;
1997 }
1998 if (! has_path)
1999 putlen = 0;
2000 GNUNET_STATISTICS_update (GDS_stats,
2001 "# P2P PUT requests received",
2002 1,
2003 GNUNET_NO);
2004 GNUNET_STATISTICS_update (GDS_stats,
2005 "# P2P PUT bytes received",
2006 msize,
2007 GNUNET_NO);
2008 {
2009 struct GNUNET_HashCode test_key;
2010 enum GNUNET_GenericReturnValue ret;
2011
2012 ret = GNUNET_BLOCK_get_key (GDS_block_context,
2013 bd.type,
2014 bd.data,
2015 bd.data_size,
2016 &test_key);
2017 switch (ret)
2018 {
2019 case GNUNET_YES:
2020 if (0 != GNUNET_memcmp (&test_key,
2021 &bd.key))
2022 {
2023 GNUNET_break_op (0);
2024 return;
2025 }
2026 break;
2027 case GNUNET_NO:
2028 /* cannot verify, good luck */
2029 break;
2030 case GNUNET_SYSERR:
2031 /* block type not supported, good luck */
2032 break;
2033 }
2034 }
2035
2036 {
2037 struct GNUNET_CONTAINER_BloomFilter *bf;
2038 struct GNUNET_DHT_PathElement pp[putlen + 1];
2039
2040 bf = GNUNET_CONTAINER_bloomfilter_init (put->bloomfilter,
2041 DHT_BLOOM_SIZE,
2042 GNUNET_CONSTANTS_BLOOMFILTER_K);
2043 GNUNET_break_op (GNUNET_YES ==
2044 GNUNET_CONTAINER_bloomfilter_test (bf,
2045 &peer->phash));
2046 /* extend 'put path' by sender */
2047 bd.put_path = (const struct GNUNET_DHT_PathElement *) pp;
2048 bd.put_path_length = putlen + 1;
2049 if (has_path)
2050 {
2051 unsigned int failure_offset;
2052
2053 GNUNET_memcpy (pp,
2054 put_path,
2055 putlen * sizeof(struct GNUNET_DHT_PathElement));
2056 pp[putlen].pred = peer->id;
2057 pp[putlen].sig = *last_sig;
2058#if SANITY_CHECKS
2059 /* TODO: might want to eventually implement probabilistic
2060 load-based path verification, but for now it is all or nothing */
2061 failure_offset
2062 = GNUNET_DHT_verify_path (bd.data,
2063 bd.data_size,
2064 bd.expiration_time,
2065 trunc_peer,
2066 pp,
2067 putlen + 1,
2068 NULL, 0, /* get_path */
2069 &GDS_my_identity);
2070#else
2071 failure_offset = 0;
2072#endif
2073 if (0 != failure_offset)
2074 {
2075 GNUNET_break_op (0);
2076 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2077 "Recorded put path invalid at offset %u, truncating\n",
2078 failure_offset);
2079 GNUNET_assert (failure_offset <= putlen + 1);
2080 bd.put_path = &pp[failure_offset];
2081 bd.put_path_length = putlen - failure_offset;
2082 bd.ro |= GNUNET_DHT_RO_TRUNCATED;
2083 bd.trunc_peer = pp[failure_offset - 1].pred;
2084 }
2085 }
2086 else
2087 {
2088 bd.put_path_length = 0;
2089 }
2090
2091 /* give to local clients */
2092 GNUNET_break (GDS_CLIENTS_handle_reply (&bd,
2093 &bd.key,
2094 0, NULL /* get path */));
2095
2096 /* store locally */
2097 if ( (0 != (bd.ro & GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE)) ||
2098 (GDS_am_closest_peer (&put->key,
2099 bf)) )
2100 GDS_DATACACHE_handle_put (&bd);
2101 {
2102 enum GNUNET_GenericReturnValue forwarded;
2103
2104 /* route to other peers */
2105 forwarded
2106 = GDS_NEIGHBOURS_handle_put (&bd,
2107 ntohs (put->desired_replication_level),
2108 ntohs (put->hop_count),
2109 bf);
2110 /* notify monitoring clients */
2111 bd.ro |= ((GNUNET_OK == forwarded)
2112 ? GNUNET_DHT_RO_LAST_HOP
2113 : 0);
2114 GDS_CLIENTS_process_put (&bd,
2115 ntohs (put->hop_count),
2116 ntohs (put->desired_replication_level));
2117 }
2118 GNUNET_CONTAINER_bloomfilter_free (bf);
2119 }
2120}
2121
2122
2123/**
2124 * We have received a request for a HELLO. Sends our
2125 * HELLO back.
2126 *
2127 * @param pi sender of the request
2128 * @param key peers close to this key are desired
2129 * @param bg group for filtering peers
2130 */
2131static void
2132handle_find_my_hello (struct PeerInfo *pi,
2133 const struct GNUNET_HashCode *query_hash,
2134 struct GNUNET_BLOCK_Group *bg)
2135{
2136 size_t block_size = 0;
2137
2138 /* TODO: consider caching our HELLO block for a bit, to
2139 avoid signing too often here... */
2140 GNUNET_break (GNUNET_NO ==
2141 GNUNET_HELLO_builder_to_block (GDS_my_hello,
2142 &GDS_my_private_key,
2143 NULL,
2144 &block_size));
2145 {
2146 char block[block_size];
2147
2148 if (GNUNET_OK !=
2149 GNUNET_HELLO_builder_to_block (GDS_my_hello,
2150 &GDS_my_private_key,
2151 block,
2152 &block_size))
2153 {
2154 GNUNET_STATISTICS_update (GDS_stats,
2155 "# FIND PEER requests ignored due to lack of HELLO",
2156 1,
2157 GNUNET_NO);
2158 }
2159 else if (GNUNET_BLOCK_REPLY_OK_MORE ==
2160 GNUNET_BLOCK_check_reply (GDS_block_context,
2161 GNUNET_BLOCK_TYPE_DHT_URL_HELLO,
2162 bg,
2163 &GDS_my_identity_hash,
2164 NULL, 0,
2165 block,
2166 block_size))
2167 {
2168 struct GNUNET_DATACACHE_Block bd = {
2169 .type = GNUNET_BLOCK_TYPE_DHT_URL_HELLO,
2170 .expiration_time
2171 = GNUNET_TIME_relative_to_absolute (
2172 GNUNET_HELLO_ADDRESS_EXPIRATION),
2173 .key = GDS_my_identity_hash,
2174 .data = block,
2175 .data_size = block_size
2176 };
2177
2178 GNUNET_break (GDS_NEIGHBOURS_handle_reply (pi,
2179 &bd,
2180 query_hash,
2181 0, NULL /* get path */));
2182 }
2183 else
2184 {
2185 GNUNET_STATISTICS_update (GDS_stats,
2186 "# FIND PEER requests ignored due to Bloomfilter",
2187 1,
2188 GNUNET_NO);
2189 }
2190 }
2191}
2192
2193
2194/**
2195 * We have received a request for nearby HELLOs. Sends matching
2196 * HELLOs back.
2197 *
2198 * @param pi sender of the request
2199 * @param key peers close to this key are desired
2200 * @param bg group for filtering peers
2201 */
2202static void
2203handle_find_local_hello (struct PeerInfo *pi,
2204 const struct GNUNET_HashCode *query_hash,
2205 struct GNUNET_BLOCK_Group *bg)
2206{
2207 /* Force non-random selection by hop count */
2208 struct PeerInfo *peer;
2209
2210 peer = select_peer (query_hash,
2211 NULL,
2212 GDS_NSE_get () + 1);
2213 if ( (NULL != peer->hello) &&
2214 (! GNUNET_TIME_absolute_is_past (peer->hello_expiration)) &&
2215 (GNUNET_BLOCK_REPLY_OK_MORE ==
2216 GNUNET_BLOCK_check_reply (
2217 GDS_block_context,
2218 GNUNET_BLOCK_TYPE_DHT_URL_HELLO,
2219 bg,
2220 &peer->phash,
2221 NULL, 0, /* xquery */
2222 peer->hello,
2223 peer->hello_size)) )
2224 {
2225 struct GNUNET_DATACACHE_Block bd = {
2226 .type = GNUNET_BLOCK_TYPE_DHT_URL_HELLO,
2227 .expiration_time = peer->hello_expiration,
2228 .key = peer->phash,
2229 .data = peer->hello,
2230 .data_size = peer->hello_size
2231 };
2232
2233 GNUNET_break (GDS_NEIGHBOURS_handle_reply (pi,
2234 &bd,
2235 query_hash,
2236 0, NULL /* get path */));
2237 }
2238}
2239
2240
2241/**
2242 * Handle an exact result from local datacache for a GET operation.
2243 *
2244 * @param cls the `struct PeerInfo` for which this is a reply
2245 * @param bd details about the block we found locally
2246 */
2247static void
2248handle_local_result (void *cls,
2249 const struct GNUNET_DATACACHE_Block *bd)
2250{
2251 struct PeerInfo *peer = cls;
2252
2253 GNUNET_break (GDS_NEIGHBOURS_handle_reply (peer,
2254 bd,
2255 &bd->key,
2256 0, NULL /* get path */));
2257}
2258
2259
2260/**
2261 * Check validity of p2p get request.
2262 *
2263 * @param cls closure with the `struct Target` of the sender
2264 * @param get the message
2265 * @return #GNUNET_OK if the message is well-formed
2266 */
2267static enum GNUNET_GenericReturnValue
2268check_dht_p2p_get (void *cls,
2269 const struct PeerGetMessage *get)
2270{
2271 uint16_t msize = ntohs (get->header.size);
2272 uint16_t result_filter_size = ntohs (get->result_filter_size);
2273
2274 (void) cls;
2275 if (msize < sizeof(*get) + result_filter_size)
2276 {
2277 GNUNET_break_op (0);
2278 return GNUNET_SYSERR;
2279 }
2280 return GNUNET_OK;
2281}
2282
2283
2284/**
2285 * Core handler for p2p get requests.
2286 *
2287 * @param cls closure with the `struct Target` of the sender
2288 * @param get the message
2289 */
2290static void
2291handle_dht_p2p_get (void *cls,
2292 const struct PeerGetMessage *get)
2293{
2294 struct Target *t = cls;
2295 struct PeerInfo *peer = t->pi;
2296 uint16_t msize = ntohs (get->header.size);
2297 uint16_t result_filter_size = ntohs (get->result_filter_size);
2298 uint16_t hop_count = ntohs (get->hop_count);
2299 enum GNUNET_BLOCK_Type type = (enum GNUNET_BLOCK_Type) ntohl (get->type);
2300 enum GNUNET_DHT_RouteOption options = (enum GNUNET_DHT_RouteOption) ntohs (
2301 get->options);
2302 enum GNUNET_BLOCK_ReplyEvaluationResult eval = GNUNET_BLOCK_REPLY_OK_MORE;
2303 const void *result_filter = (const void *) &get[1];
2304 const void *xquery = result_filter + result_filter_size;
2305 size_t xquery_size = msize - sizeof (*get) - result_filter_size;
2306
2307 /* parse and validate message */
2308 GNUNET_STATISTICS_update (GDS_stats,
2309 "# P2P GET requests received",
2310 1,
2311 GNUNET_NO);
2312 GNUNET_STATISTICS_update (GDS_stats,
2313 "# P2P GET bytes received",
2314 msize,
2315 GNUNET_NO);
2316 if (GNUNET_NO ==
2317 GNUNET_BLOCK_check_query (GDS_block_context,
2318 type,
2319 &get->key,
2320 xquery,
2321 xquery_size))
2322 {
2323 /* request invalid */
2324 GNUNET_break_op (0);
2325 return;
2326 }
2327
2328 {
2329 struct GNUNET_BLOCK_Group *bg;
2330 struct GNUNET_CONTAINER_BloomFilter *peer_bf;
2331
2332 peer_bf = GNUNET_CONTAINER_bloomfilter_init (get->bloomfilter,
2333 DHT_BLOOM_SIZE,
2334 GNUNET_CONSTANTS_BLOOMFILTER_K);
2335 GNUNET_break_op (GNUNET_YES ==
2336 GNUNET_CONTAINER_bloomfilter_test (peer_bf,
2337 &peer->phash));
2338 bg = GNUNET_BLOCK_group_create (GDS_block_context,
2339 type,
2340 result_filter,
2341 result_filter_size,
2342 "filter-size",
2343 result_filter_size,
2344 NULL);
2345 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2346 "GET for %s at %s after %u hops\n",
2347 GNUNET_h2s (&get->key),
2348 GNUNET_i2s (&GDS_my_identity),
2349 (unsigned int) hop_count);
2350 /* local lookup (this may update the bg) */
2351 if ( (0 != (options & GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE)) ||
2352 (GDS_am_closest_peer (&get->key,
2353 peer_bf)) )
2354 {
2355 if (GNUNET_BLOCK_TYPE_DHT_URL_HELLO == type)
2356 {
2357 GNUNET_STATISTICS_update (GDS_stats,
2358 "# P2P HELLO lookup requests processed",
2359 1,
2360 GNUNET_NO);
2361 handle_find_my_hello (peer,
2362 &get->key,
2363 bg);
2364 if (0 != (options & GNUNET_DHT_RO_FIND_APPROXIMATE))
2365 handle_find_local_hello (peer,
2366 &get->key,
2367 bg);
2368 }
2369 else
2370 {
2371 if (0 != (options & GNUNET_DHT_RO_FIND_APPROXIMATE))
2372 eval = GDS_DATACACHE_get_closest (&get->key,
2373 type,
2374 xquery,
2375 xquery_size,
2376 bg,
2377 &handle_local_result,
2378 peer);
2379 else
2380 eval = GDS_DATACACHE_handle_get (&get->key,
2381 type,
2382 xquery,
2383 xquery_size,
2384 bg,
2385 &handle_local_result,
2386 peer);
2387 }
2388 }
2389 else
2390 {
2391 GNUNET_STATISTICS_update (GDS_stats,
2392 "# P2P GET requests ONLY routed",
2393 1,
2394 GNUNET_NO);
2395 }
2396
2397 /* remember request for routing replies
2398 TODO: why should we do this if GNUNET_BLOCK_REPLY_OK_LAST == eval?
2399 */
2400 GDS_ROUTING_add (&peer->id,
2401 type,
2402 bg, /* bg now owned by routing, but valid at least until end of this function! */
2403 options,
2404 &get->key,
2405 xquery,
2406 xquery_size);
2407
2408 /* P2P forwarding */
2409 {
2410 bool forwarded = false;
2411 uint16_t desired_replication_level = ntohs (
2412 get->desired_replication_level);
2413
2414 if (eval != GNUNET_BLOCK_REPLY_OK_LAST)
2415 forwarded = (GNUNET_OK ==
2416 GDS_NEIGHBOURS_handle_get (type,
2417 options,
2418 desired_replication_level,
2419 hop_count,
2420 &get->key,
2421 xquery,
2422 xquery_size,
2423 bg,
2424 peer_bf));
2425 GDS_CLIENTS_process_get (
2426 options
2427 | (forwarded
2428 ? 0
2429 : GNUNET_DHT_RO_LAST_HOP),
2430 type,
2431 hop_count,
2432 desired_replication_level,
2433 &get->key);
2434 }
2435 /* clean up; note that 'bg' is owned by routing now! */
2436 GNUNET_CONTAINER_bloomfilter_free (peer_bf);
2437 }
2438}
2439
2440
2441/**
2442 * Process a reply, after the @a get_path has been updated.
2443 *
2444 * @param bd block details
2445 * @param query_hash hash of the original query, might not match key in @a bd
2446 * @param get_path_length number of entries in @a get_path
2447 * @param get_path path the reply has taken
2448 * @return true on success
2449 */
2450static bool
2451process_reply_with_path (const struct GNUNET_DATACACHE_Block *bd,
2452 const struct GNUNET_HashCode *query_hash,
2453 unsigned int get_path_length,
2454 const struct GNUNET_DHT_PathElement *get_path)
2455{
2456 /* forward to local clients */
2457 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2458 "Forwarding reply to local clients\n");
2459 if (! GDS_CLIENTS_handle_reply (bd,
2460 query_hash,
2461 get_path_length,
2462 get_path))
2463 {
2464 GNUNET_break (0);
2465 return false;
2466 }
2467 GDS_CLIENTS_process_get_resp (bd,
2468 get_path,
2469 get_path_length);
2470 if (GNUNET_YES == cache_results)
2471 {
2472 struct GNUNET_DHT_PathElement xput_path[GNUNET_NZL (get_path_length
2473 + bd->put_path_length)];
2474 struct GNUNET_DATACACHE_Block bdx = *bd;
2475
2476 GNUNET_memcpy (xput_path,
2477 bd->put_path,
2478 bd->put_path_length * sizeof(struct GNUNET_DHT_PathElement));
2479 GNUNET_memcpy (&xput_path[bd->put_path_length],
2480 get_path,
2481 get_path_length * sizeof(struct GNUNET_DHT_PathElement));
2482 bdx.put_path = xput_path;
2483 bdx.put_path_length += get_path_length;
2484 GDS_DATACACHE_handle_put (&bdx);
2485 }
2486 /* forward to other peers */
2487 GDS_ROUTING_process (bd,
2488 query_hash,
2489 get_path_length,
2490 get_path);
2491 return true;
2492}
2493
2494
2495/**
2496 * Check validity of p2p result message.
2497 *
2498 * @param cls closure
2499 * @param message message
2500 * @return #GNUNET_YES if the message is well-formed
2501 */
2502static enum GNUNET_GenericReturnValue
2503check_dht_p2p_result (void *cls,
2504 const struct PeerResultMessage *prm)
2505{
2506 uint16_t get_path_length = ntohs (prm->get_path_length);
2507 uint16_t put_path_length = ntohs (prm->put_path_length);
2508 uint16_t msize = ntohs (prm->header.size);
2509
2510 (void) cls;
2511 if ( (msize <
2512 sizeof(struct PeerResultMessage)
2513 + (get_path_length + put_path_length)
2514 * sizeof(struct GNUNET_DHT_PathElement)) ||
2515 (get_path_length >
2516 GNUNET_MAX_MESSAGE_SIZE / sizeof(struct GNUNET_DHT_PathElement)) ||
2517 (put_path_length >
2518 GNUNET_MAX_MESSAGE_SIZE / sizeof(struct GNUNET_DHT_PathElement)) )
2519 {
2520 GNUNET_break_op (0);
2521 return GNUNET_SYSERR;
2522 }
2523 return GNUNET_OK;
2524}
2525
2526
2527/**
2528 * Core handler for p2p result messages.
2529 *
2530 * @param cls closure
2531 * @param message message
2532 */
2533static void
2534handle_dht_p2p_result (void *cls,
2535 const struct PeerResultMessage *prm)
2536{
2537 struct Target *t = cls;
2538 struct PeerInfo *peer = t->pi;
2539 uint16_t msize = ntohs (prm->header.size) - sizeof (*prm);
2540 enum GNUNET_DHT_RouteOption ro
2541 = (enum GNUNET_DHT_RouteOption) ntohl (prm->options);
2542 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
2543 bool tracked = (0 != (ro & GNUNET_DHT_RO_RECORD_ROUTE));
2544 uint16_t get_path_length = ntohs (prm->get_path_length);
2545 uint16_t put_path_length = ntohs (prm->put_path_length);
2546 const struct GNUNET_PeerIdentity *trunc_peer
2547 = truncated
2548 ? (const struct GNUNET_PeerIdentity *) &prm[1]
2549 : NULL;
2550 const struct GNUNET_DHT_PathElement *put_path
2551 = truncated
2552 ? (const struct GNUNET_DHT_PathElement *) &trunc_peer[1]
2553 : (const struct GNUNET_DHT_PathElement *) &prm[1];
2554 const struct GNUNET_DHT_PathElement *get_path
2555 = &put_path[put_path_length];
2556 const struct GNUNET_CRYPTO_EddsaSignature *last_sig
2557 = tracked
2558 ? (const struct GNUNET_CRYPTO_EddsaSignature *) &get_path[get_path_length]
2559 : NULL;
2560 const void *data
2561 = tracked
2562 ? (const void *) &last_sig[1]
2563 : (const void *) &get_path[get_path_length];
2564 size_t vsize = (truncated ? sizeof (struct GNUNET_PeerIdentity) : 0)
2565 + (tracked ? sizeof (struct GNUNET_CRYPTO_EddsaSignature) : 0);
2566 struct GNUNET_DATACACHE_Block bd = {
2567 .expiration_time = GNUNET_TIME_absolute_ntoh (prm->expiration_time),
2568 .put_path = put_path,
2569 .put_path_length = put_path_length,
2570 .key = prm->key,
2571 .type = ntohl (prm->type),
2572 .ro = ro,
2573 .data = data,
2574 .data_size = msize - vsize - (get_path_length + put_path_length)
2575 * sizeof(struct GNUNET_DHT_PathElement)
2576 };
2577
2578 /* parse and validate message */
2579 if (GNUNET_TIME_absolute_is_past (bd.expiration_time))
2580 {
2581 GNUNET_STATISTICS_update (GDS_stats,
2582 "# Expired results discarded",
2583 1,
2584 GNUNET_NO);
2585 return;
2586 }
2587 if (GNUNET_OK !=
2588 GNUNET_BLOCK_check_block (GDS_block_context,
2589 bd.type,
2590 bd.data,
2591 bd.data_size))
2592 {
2593 GNUNET_break_op (0);
2594 return;
2595 }
2596 GNUNET_STATISTICS_update (GDS_stats,
2597 "# P2P RESULTS received",
2598 1,
2599 GNUNET_NO);
2600 GNUNET_STATISTICS_update (GDS_stats,
2601 "# P2P RESULT bytes received",
2602 msize,
2603 GNUNET_NO);
2604 {
2605 enum GNUNET_GenericReturnValue ret;
2606
2607 ret = GNUNET_BLOCK_get_key (GDS_block_context,
2608 bd.type,
2609 bd.data,
2610 bd.data_size,
2611 &bd.key);
2612 if (GNUNET_NO == ret)
2613 bd.key = prm->key;
2614 }
2615
2616 /* if we got a HELLO, consider it for our own routing table */
2617 hello_check (&bd);
2618
2619 /* Need to append 'peer' to 'get_path' */
2620 if (tracked)
2621 {
2622 struct GNUNET_DHT_PathElement xget_path[get_path_length + 1];
2623 struct GNUNET_DHT_PathElement *gp = xget_path;
2624 unsigned int failure_offset;
2625
2626 GNUNET_memcpy (xget_path,
2627 get_path,
2628 get_path_length * sizeof(struct GNUNET_DHT_PathElement));
2629 xget_path[get_path_length].pred = peer->id;
2630 /* use memcpy(), as last_sig may not be aligned */
2631 memcpy (&xget_path[get_path_length].sig,
2632 last_sig,
2633 sizeof (*last_sig));
2634#if SANITY_CHECKS
2635 /* TODO: might want to eventually implement probabilistic
2636 load-based path verification, but for now it is all or nothing */
2637 failure_offset
2638 = GNUNET_DHT_verify_path (bd.data,
2639 bd.data_size,
2640 bd.expiration_time,
2641 trunc_peer,
2642 put_path,
2643 put_path_length,
2644 gp,
2645 get_path_length + 1,
2646 &GDS_my_identity);
2647#else
2648 failure_offset = 0;
2649#endif
2650 if (0 != failure_offset)
2651 {
2652 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2653 "Recorded path invalid at offset %u, truncating\n",
2654 failure_offset);
2655 GNUNET_assert (failure_offset <= bd.put_path_length + get_path_length
2656 + 1);
2657 if (failure_offset < bd.put_path_length)
2658 {
2659 /* failure on put path */
2660 trunc_peer = &bd.put_path[failure_offset - 1].pred;
2661 bd.ro |= GNUNET_DHT_RO_TRUNCATED;
2662 bd.put_path = &bd.put_path[failure_offset];
2663 bd.put_path_length -= failure_offset;
2664 truncated = true;
2665 }
2666 else
2667 {
2668 /* failure on get path */
2669 failure_offset -= bd.put_path_length;
2670 if (0 == failure_offset)
2671 trunc_peer = &bd.put_path[bd.put_path_length - 1].pred;
2672 else
2673 trunc_peer = &gp[failure_offset - 1].pred;
2674 get_path_length -= failure_offset;
2675 gp = &gp[failure_offset];
2676 bd.put_path_length = 0;
2677 bd.put_path = NULL;
2678 bd.ro |= GNUNET_DHT_RO_TRUNCATED;
2679 truncated = true;
2680 }
2681 }
2682 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2683 "Extending GET path of length %u with %s\n",
2684 get_path_length,
2685 GNUNET_i2s (&peer->id));
2686 if (truncated)
2687 {
2688 GNUNET_assert (NULL != trunc_peer);
2689 bd.trunc_peer = *trunc_peer;
2690 }
2691 GNUNET_break (process_reply_with_path (&bd,
2692 &prm->key,
2693 get_path_length + 1,
2694 gp));
2695 }
2696 else
2697 {
2698 if (truncated)
2699 {
2700 GNUNET_assert (NULL != trunc_peer);
2701 bd.trunc_peer = *trunc_peer;
2702 }
2703 GNUNET_break (process_reply_with_path (&bd,
2704 &prm->key,
2705 0,
2706 NULL));
2707 }
2708}
2709
2710
2711/**
2712 * Check validity of a p2p hello message.
2713 *
2714 * @param cls closure
2715 * @param hello message
2716 * @return #GNUNET_YES if the message is well-formed
2717 */
2718static enum GNUNET_GenericReturnValue
2719check_dht_p2p_hello (void *cls,
2720 const struct GNUNET_MessageHeader *hello)
2721{
2722 struct Target *t = cls;
2723 struct PeerInfo *peer = t->pi;
2724 enum GNUNET_GenericReturnValue ret;
2725 size_t hellob_size;
2726 void *hellob;
2727 struct GNUNET_TIME_Absolute expiration;
2728
2729 ret = GNUNET_HELLO_dht_msg_to_block (hello,
2730 &peer->id,
2731 &hellob,
2732 &hellob_size,
2733 &expiration);
2734 GNUNET_free (hellob);
2735 return ret;
2736}
2737
2738
2739/**
2740 * Core handler for p2p HELLO messages.
2741 *
2742 * @param cls closure
2743 * @param message message
2744 */
2745static void
2746handle_dht_p2p_hello (void *cls,
2747 const struct GNUNET_MessageHeader *hello)
2748{
2749 struct Target *t = cls;
2750 struct PeerInfo *peer = t->pi;
2751
2752 GNUNET_free (peer->hello);
2753 peer->hello_size = 0;
2754 GNUNET_break (GNUNET_OK ==
2755 GNUNET_HELLO_dht_msg_to_block (hello,
2756 &peer->id,
2757 &peer->hello,
2758 &peer->hello_size,
2759 &peer->hello_expiration));
2760}
2761
2762
2763void
2764GDS_u_receive (void *cls,
2765 void **tctx,
2766 void **sctx,
2767 const void *message,
2768 size_t message_size)
2769{
2770 struct Target *t = *tctx;
2771 struct GNUNET_MQ_MessageHandler core_handlers[] = {
2772 GNUNET_MQ_hd_var_size (dht_p2p_get,
2773 GNUNET_MESSAGE_TYPE_DHT_P2P_GET,
2774 struct PeerGetMessage,
2775 t),
2776 GNUNET_MQ_hd_var_size (dht_p2p_put,
2777 GNUNET_MESSAGE_TYPE_DHT_P2P_PUT,
2778 struct PeerPutMessage,
2779 t),
2780 GNUNET_MQ_hd_var_size (dht_p2p_result,
2781 GNUNET_MESSAGE_TYPE_DHT_P2P_RESULT,
2782 struct PeerResultMessage,
2783 t),
2784 GNUNET_MQ_hd_var_size (dht_p2p_hello,
2785 GNUNET_MESSAGE_TYPE_DHT_P2P_HELLO,
2786 struct GNUNET_MessageHeader,
2787 t),
2788 GNUNET_MQ_handler_end ()
2789 };
2790 const struct GNUNET_MessageHeader *mh = message;
2791
2792 (void) cls; /* the 'struct GDS_Underlay' */
2793 (void) sctx; /* our receiver address */
2794 if (NULL == t)
2795 {
2796 /* Received message claiming to originate from myself?
2797 Ignore! */
2798 GNUNET_break_op (0);
2799 return;
2800 }
2801 if (message_size < sizeof (*mh))
2802 {
2803 GNUNET_break_op (0);
2804 return;
2805 }
2806 if (message_size != ntohs (mh->size))
2807 {
2808 GNUNET_break_op (0);
2809 return;
2810 }
2811 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2812 "Handling message of type %u from peer %s\n",
2813 ntohs (mh->type),
2814 GNUNET_i2s (&t->pi->id));
2815 if (GNUNET_OK !=
2816 GNUNET_MQ_handle_message (core_handlers,
2817 mh))
2818 {
2819 GNUNET_break_op (0);
2820 return;
2821 }
2822}
2823
2824
2825/**
2826 * Callback function used to extract URIs from a builder.
2827 * Called when we should consider connecting to a peer.
2828 *
2829 * @param cls closure pointing to a `struct GNUNET_PeerIdentity *`
2830 * @param uri one of the URIs
2831 */
2832void
2833GDS_try_connect (void *cls,
2834 const char *uri)
2835{
2836 const struct GNUNET_PeerIdentity *pid = cls;
2837 struct GNUNET_HashCode phash;
2838 int peer_bucket;
2839 struct PeerBucket *bucket;
2840
2841 if (0 == GNUNET_memcmp (&GDS_my_identity,
2842 pid))
2843 {
2844 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2845 "Got a HELLO for my own PID, ignoring it\n");
2846 return; /* that's us! */
2847 }
2848 GNUNET_CRYPTO_hash (pid,
2849 sizeof(*pid),
2850 &phash);
2851 peer_bucket = find_bucket (&phash);
2852 GNUNET_assert ( (peer_bucket >= 0) &&
2853 ((unsigned int) peer_bucket < MAX_BUCKETS));
2854 bucket = &k_buckets[peer_bucket];
2855 if (bucket->peers_size >= bucket_size)
2856 return; /* do not care */
2857 for (struct PeerInfo *pi = bucket->head;
2858 NULL != pi;
2859 pi = pi->next)
2860 if (0 ==
2861 GNUNET_memcmp (&pi->id,
2862 pid))
2863 {
2864 /* already connected */
2865 /* TODO: maybe consider 'uri' anyway as an additional
2866 alternative address??? */
2867 return;
2868 }
2869 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2870 "Discovered peer %s at %s suitable for bucket %d (%u/%u), trying to connect\n",
2871 GNUNET_i2s (pid),
2872 uri,
2873 peer_bucket,
2874 bucket->peers_size,
2875 bucket_size);
2876 /* new peer that we like! */
2877 GDS_u_try_connect (pid,
2878 uri);
2879}
2880
2881
2882/**
2883 * Send @a msg to all peers in our buckets.
2884 *
2885 * @param msg message to broadcast
2886 */
2887void
2888GDS_NEIGHBOURS_broadcast (const struct GNUNET_MessageHeader *msg)
2889{
2890 for (unsigned int bc = 0; bc<closest_bucket; bc++)
2891 {
2892 struct PeerBucket *bucket = &k_buckets[bc];
2893 unsigned int count = 0;
2894
2895 for (struct PeerInfo *pos = bucket->head;
2896 NULL != pos;
2897 pos = pos->next)
2898 {
2899 if (count >= bucket_size)
2900 break; /* we only consider first #bucket_size entries per bucket */
2901 count++;
2902 do_send (pos,
2903 msg);
2904 }
2905 }
2906}
2907
2908
2909enum GNUNET_GenericReturnValue
2910GDS_NEIGHBOURS_init ()
2911{
2912
2913 unsigned long long temp_config_num;
2914
2915 disable_try_connect
2916 = GNUNET_CONFIGURATION_get_value_yesno (GDS_cfg,
2917 "DHT",
2918 "DISABLE_TRY_CONNECT");
2919 if (GNUNET_OK ==
2920 GNUNET_CONFIGURATION_get_value_number (GDS_cfg,
2921 "DHT",
2922 "bucket_size",
2923 &temp_config_num))
2924 bucket_size = (unsigned int) temp_config_num;
2925 cache_results
2926 = GNUNET_CONFIGURATION_get_value_yesno (GDS_cfg,
2927 "DHT",
2928 "CACHE_RESULTS");
2929 all_connected_peers = GNUNET_CONTAINER_multipeermap_create (256,
2930 GNUNET_YES);
2931 return GNUNET_OK;
2932}
2933
2934
2935void
2936GDS_NEIGHBOURS_done ()
2937{
2938 if (NULL == all_connected_peers)
2939 return;
2940 GNUNET_assert (0 ==
2941 GNUNET_CONTAINER_multipeermap_size (all_connected_peers));
2942 GNUNET_CONTAINER_multipeermap_destroy (all_connected_peers);
2943 all_connected_peers = NULL;
2944 GNUNET_assert (NULL == find_peer_task);
2945}
2946
2947
2948struct GNUNET_PeerIdentity *
2949GDS_NEIGHBOURS_get_id ()
2950{
2951 return &GDS_my_identity;
2952}
2953
2954
2955/* end of gnunet-service-dht_neighbours.c */
diff --git a/src/dht/gnunet-service-dht_neighbours.h b/src/dht/gnunet-service-dht_neighbours.h
deleted file mode 100644
index 96db21b9b..000000000
--- a/src/dht/gnunet-service-dht_neighbours.h
+++ /dev/null
@@ -1,226 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2011, 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/gnunet-service-dht_neighbours.h
23 * @brief GNUnet DHT routing code
24 * @author Christian Grothoff
25 * @author Nathan Evans
26 */
27#ifndef GNUNET_SERVICE_DHT_NEIGHBOURS_H
28#define GNUNET_SERVICE_DHT_NEIGHBOURS_H
29
30#include "gnunet_util_lib.h"
31#include "gnunet_block_lib.h"
32#include "gnunet_dht_service.h"
33#include "gnunet_dhtu_plugin.h"
34#include "gnunet-service-dht_datacache.h"
35
36
37struct PeerInfo;
38
39/**
40 * Lookup peer by peer's identity.
41 *
42 * @param target peer to look up
43 * @return NULL if we are not connected to @a target
44 */
45struct PeerInfo *
46GDS_NEIGHBOURS_lookup_peer (const struct GNUNET_PeerIdentity *target);
47
48
49/**
50 * Perform a PUT operation. Forwards the given request to other
51 * peers. Does not store the data locally. Does not give the
52 * data to local clients. May do nothing if this is the only
53 * peer in the network (or if we are the closest peer in the
54 * network).
55 *
56 * @param bd data about the block
57 * @param desired_replication_level desired replication level
58 * @param hop_count how many hops has this message traversed so far
59 * @param bf Bloom filter of peers this PUT has already traversed
60 * @return #GNUNET_OK if the request was forwarded, #GNUNET_NO if not
61 */
62enum GNUNET_GenericReturnValue
63GDS_NEIGHBOURS_handle_put (const struct GNUNET_DATACACHE_Block *bd,
64 uint16_t desired_replication_level,
65 uint16_t hop_count,
66 struct GNUNET_CONTAINER_BloomFilter *bf);
67
68
69/**
70 * Perform a GET operation. Forwards the given request to other
71 * peers. Does not lookup the key locally. May do nothing if this is
72 * the only peer in the network (or if we are the closest peer in the
73 * network).
74 *
75 * @param type type of the block
76 * @param options routing options
77 * @param desired_replication_level desired replication count
78 * @param hop_count how many hops did this request traverse so far?
79 * @param key key for the content
80 * @param xquery extended query
81 * @param xquery_size number of bytes in @a xquery
82 * @param bg block group to filter replies
83 * @param peer_bf filter for peers not to select (again, updated)
84 * @return #GNUNET_OK if the request was forwarded, #GNUNET_NO if not
85 */
86enum GNUNET_GenericReturnValue
87GDS_NEIGHBOURS_handle_get (enum GNUNET_BLOCK_Type type,
88 enum GNUNET_DHT_RouteOption options,
89 uint16_t desired_replication_level,
90 uint16_t hop_count,
91 const struct GNUNET_HashCode *key,
92 const void *xquery,
93 size_t xquery_size,
94 struct GNUNET_BLOCK_Group *bg,
95 struct GNUNET_CONTAINER_BloomFilter *peer_bf);
96
97
98/**
99 * Handle a reply (route to origin). Only forwards the reply back to
100 * other peers waiting for it. Does not do local caching or
101 * forwarding to local clients.
102 *
103 * @param pi neighbour that should receive the block
104 * @param type type of the block
105 * @param bd details about the reply
106 * @param query_hash query that was used for the request
107 * @param get_path_length number of entries in put_path
108 * @param get_path peers this reply has traversed so far (if tracked)
109 * @return true on success
110 */
111bool
112GDS_NEIGHBOURS_handle_reply (struct PeerInfo *pi,
113 const struct GNUNET_DATACACHE_Block *bd,
114 const struct GNUNET_HashCode *query_hash,
115 unsigned int get_path_length,
116 const struct GNUNET_DHT_PathElement *get_path);
117
118
119/**
120 * Check whether my identity is closer than any known peers. If a
121 * non-null bloomfilter is given, check if this is the closest peer
122 * that hasn't already been routed to.
123 *
124 * @param key hash code to check closeness to
125 * @param bloom bloomfilter, exclude these entries from the decision
126 * @return #GNUNET_YES if node location is closest,
127 * #GNUNET_NO otherwise.
128 */
129enum GNUNET_GenericReturnValue
130GDS_am_closest_peer (const struct GNUNET_HashCode *key,
131 const struct GNUNET_CONTAINER_BloomFilter *bloom);
132
133
134/**
135 * Callback function used to extract URIs from a builder.
136 * Called when we should consider connecting to a peer.
137 *
138 * @param cls closure pointing to a `struct GNUNET_PeerIdentity *`
139 * @param uri one of the URIs
140 */
141void
142GDS_try_connect (void *cls,
143 const char *uri);
144
145
146/**
147 * Function to call when we connect to a peer and can henceforth transmit to
148 * that peer.
149 *
150 * @param cls the closure, must be a `struct GDS_Underlay`
151 * @param target handle to the target,
152 * pointer will remain valid until @e disconnect_cb is called
153 * @para pid peer identity,
154 * pointer will remain valid until @e disconnect_cb is called
155 * @param[out] ctx storage space for DHT to use in association with this target
156 */
157void
158GDS_u_connect (void *cls,
159 struct GNUNET_DHTU_Target *target,
160 const struct GNUNET_PeerIdentity *pid,
161 void **ctx);
162
163
164/**
165 * Function to call when we disconnected from a peer and can henceforth
166 * cannot transmit to that peer anymore.
167 *
168 * @param[in] ctx storage space used by the DHT in association with this target
169 */
170void
171GDS_u_disconnect (void *ctx);
172
173
174/**
175 * Function to call when we receive a message.
176 *
177 * @param cls the closure
178 * @param origin where the message originated from
179 * @param[in,out] tctx ctx of target address where we received the message from
180 * @param[in,out] sctx ctx of our own source address at which we received the message
181 * @param message the message we received @param message_size number of
182 * bytes in @a message
183 */
184void
185GDS_u_receive (void *cls,
186 void **tctx,
187 void **sctx,
188 const void *message,
189 size_t message_size);
190
191
192/**
193 * Send @a msg to all peers in our buckets.
194 *
195 * @param msg message to broadcast
196 */
197void
198GDS_NEIGHBOURS_broadcast (const struct GNUNET_MessageHeader *msg);
199
200
201/**
202 * Initialize neighbours subsystem.
203 *
204 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
205 */
206enum GNUNET_GenericReturnValue
207GDS_NEIGHBOURS_init (void);
208
209
210/**
211 * Shutdown neighbours subsystem.
212 */
213void
214GDS_NEIGHBOURS_done (void);
215
216
217/**
218 * Get the ID of the local node.
219 *
220 * @return identity of the local node
221 */
222struct GNUNET_PeerIdentity *
223GDS_NEIGHBOURS_get_id (void);
224
225
226#endif
diff --git a/src/dht/gnunet-service-dht_routing.c b/src/dht/gnunet-service-dht_routing.c
deleted file mode 100644
index 8f87751bb..000000000
--- a/src/dht/gnunet-service-dht_routing.c
+++ /dev/null
@@ -1,431 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011, 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/gnunet-service-dht_routing.c
23 * @brief GNUnet DHT tracking of requests for routing replies
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet-service-dht_neighbours.h"
28#include "gnunet-service-dht_routing.h"
29#include "gnunet-service-dht.h"
30#include "gnunet_block_group_lib.h"
31
32
33/**
34 * Number of requests we track at most (for routing replies).
35 * TODO: make configurable!
36 */
37#define DHT_MAX_RECENT (1024 * 128)
38
39
40/**
41 * Information we keep about all recent GET requests
42 * so that we can route replies.
43 */
44struct RecentRequest
45{
46 /**
47 * The peer this request was received from.
48 */
49 struct GNUNET_PeerIdentity peer;
50
51 /**
52 * Key of this request.
53 */
54 struct GNUNET_HashCode key;
55
56 /**
57 * Position of this node in the min heap.
58 */
59 struct GNUNET_CONTAINER_HeapNode *heap_node;
60
61 /**
62 * Block group for filtering replies.
63 */
64 struct GNUNET_BLOCK_Group *bg;
65
66 /**
67 * extended query (see gnunet_block_lib.h). Allocated at the
68 * end of this struct.
69 */
70 const void *xquery;
71
72 /**
73 * Number of bytes in xquery.
74 */
75 size_t xquery_size;
76
77 /**
78 * Type of the requested block.
79 */
80 enum GNUNET_BLOCK_Type type;
81
82 /**
83 * Request options.
84 */
85 enum GNUNET_DHT_RouteOption options;
86};
87
88
89/**
90 * Recent requests by time inserted.
91 */
92static struct GNUNET_CONTAINER_Heap *recent_heap;
93
94/**
95 * Recently seen requests by key.
96 */
97static struct GNUNET_CONTAINER_MultiHashMap *recent_map;
98
99
100/**
101 * Closure for the process() function.
102 */
103struct ProcessContext
104{
105 /**
106 * Block data.
107 */
108 const struct GNUNET_DATACACHE_Block *bd;
109
110 /**
111 * Path of the reply.
112 */
113 const struct GNUNET_DHT_PathElement *get_path;
114
115 /**
116 * Number of entries in @e get_path.
117 */
118 unsigned int get_path_length;
119
120};
121
122
123/**
124 * Forward the result to the given peer if it matches the request.
125 *
126 * @param cls the `struct ProcessContext` with the result
127 * @param query_hash the hash from the original query
128 * @param value the `struct RecentRequest` with the request
129 * @return #GNUNET_OK (continue to iterate)
130 */
131static enum GNUNET_GenericReturnValue
132process (void *cls,
133 const struct GNUNET_HashCode *query_hash,
134 void *value)
135{
136 struct ProcessContext *pc = cls;
137 struct RecentRequest *rr = value;
138 enum GNUNET_BLOCK_ReplyEvaluationResult eval;
139 unsigned int get_path_length;
140 struct GNUNET_DATACACHE_Block bdx = *pc->bd;
141
142 if ( (rr->type != GNUNET_BLOCK_TYPE_ANY) &&
143 (rr->type != pc->bd->type) )
144 return GNUNET_OK; /* type mismatch */
145 if (0 != (rr->options & GNUNET_DHT_RO_RECORD_ROUTE))
146 {
147 get_path_length = pc->get_path_length;
148 }
149 else
150 {
151 get_path_length = 0;
152 bdx.put_path_length = 0;
153 bdx.put_path = NULL;
154 }
155 if ( (0 == (rr->options & GNUNET_DHT_RO_FIND_APPROXIMATE)) &&
156 (0 != GNUNET_memcmp (query_hash,
157 &bdx.key)) )
158 {
159 GNUNET_STATISTICS_update (GDS_stats,
160 "# Inexact matches discarded in exact search",
161 1,
162 GNUNET_NO);
163 return GNUNET_OK; /* exact search, but inexact match */
164 }
165 eval = GNUNET_BLOCK_check_reply (GDS_block_context,
166 bdx.type,
167 rr->bg,
168 &bdx.key,
169 rr->xquery,
170 rr->xquery_size,
171 bdx.data,
172 bdx.data_size);
173 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
174 "Result for %s of type %d was evaluated as %d\n",
175 GNUNET_h2s (&bdx.key),
176 bdx.type,
177 eval);
178 if (GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED == eval)
179 {
180 /* If we do not know the block type, we still filter
181 exact duplicates by the block content */
182 struct GNUNET_HashCode chash;
183
184 GNUNET_CRYPTO_hash (bdx.data,
185 bdx.data_size,
186 &chash);
187 if (GNUNET_YES ==
188 GNUNET_BLOCK_GROUP_bf_test_and_set (rr->bg,
189 &chash))
190 eval = GNUNET_BLOCK_REPLY_OK_DUPLICATE;
191 else
192 eval = GNUNET_BLOCK_REPLY_OK_MORE;
193 }
194 switch (eval)
195 {
196 case GNUNET_BLOCK_REPLY_OK_MORE:
197 case GNUNET_BLOCK_REPLY_OK_LAST:
198 case GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED:
199 {
200 struct PeerInfo *pi;
201
202 GNUNET_STATISTICS_update (GDS_stats,
203 "# Good REPLIES matched against routing table",
204 1,
205 GNUNET_NO);
206 pi = GDS_NEIGHBOURS_lookup_peer (&rr->peer);
207 if (NULL == pi)
208 {
209 /* peer disconnected in the meantime, drop reply */
210 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
211 "No matching peer for reply for key %s\n",
212 GNUNET_h2s (query_hash));
213 return GNUNET_OK;
214 }
215 GNUNET_break (GDS_NEIGHBOURS_handle_reply (pi,
216 &bdx,
217 query_hash,
218 get_path_length,
219 pc->get_path));
220 }
221 break;
222 case GNUNET_BLOCK_REPLY_OK_DUPLICATE:
223 GNUNET_STATISTICS_update (GDS_stats,
224 "# Duplicate REPLIES matched against routing table",
225 1,
226 GNUNET_NO);
227 return GNUNET_OK;
228 case GNUNET_BLOCK_REPLY_IRRELEVANT:
229 GNUNET_STATISTICS_update (GDS_stats,
230 "# Irrelevant REPLIES matched against routing table",
231 1,
232 GNUNET_NO);
233 return GNUNET_OK;
234 default:
235 GNUNET_break (0);
236 return GNUNET_OK;
237 }
238 return GNUNET_OK;
239}
240
241
242/**
243 * Handle a reply (route to origin). Only forwards the reply back to
244 * other peers waiting for it. Does not do local caching or
245 * forwarding to local clients. Essentially calls
246 * GDS_NEIGHBOURS_handle_reply() for all peers that sent us a matching
247 * request recently.
248 *
249 * @param bd block details
250 * @param query_hash query used in the inquiry
251 * @param get_path_length number of entries in @a get_path
252 * @param get_path peers this reply has traversed so far (if tracked)
253 */
254void
255GDS_ROUTING_process (const struct GNUNET_DATACACHE_Block *bd,
256 const struct GNUNET_HashCode *query_hash,
257 unsigned int get_path_length,
258 const struct GNUNET_DHT_PathElement *get_path)
259{
260 struct ProcessContext pc = {
261 .bd = bd,
262 .get_path = get_path,
263 .get_path_length = get_path_length
264 };
265
266 GNUNET_CONTAINER_multihashmap_get_multiple (recent_map,
267 query_hash,
268 &process,
269 &pc);
270}
271
272
273/**
274 * Remove the oldest entry from the DHT routing table. Must only
275 * be called if it is known that there is at least one entry
276 * in the heap and hashmap.
277 */
278static void
279expire_oldest_entry (void)
280{
281 struct RecentRequest *recent_req;
282
283 GNUNET_STATISTICS_update (GDS_stats,
284 "# Old entries removed from routing table",
285 1,
286 GNUNET_NO);
287 recent_req = GNUNET_CONTAINER_heap_peek (recent_heap);
288 GNUNET_assert (recent_req != NULL);
289 GNUNET_CONTAINER_heap_remove_node (recent_req->heap_node);
290 GNUNET_BLOCK_group_destroy (recent_req->bg);
291 GNUNET_assert (GNUNET_YES ==
292 GNUNET_CONTAINER_multihashmap_remove (recent_map,
293 &recent_req->key,
294 recent_req));
295 GNUNET_free (recent_req);
296}
297
298
299/**
300 * Try to combine multiple recent requests for the same value
301 * (if they come from the same peer).
302 *
303 * @param cls the new `struct RecentRequest` (to discard upon successful combination)
304 * @param key the query
305 * @param value the existing `struct RecentRequest` (to update upon successful combination)
306 * @return #GNUNET_OK (continue to iterate),
307 * #GNUNET_SYSERR if the request was successfully combined
308 */
309static enum GNUNET_GenericReturnValue
310try_combine_recent (void *cls,
311 const struct GNUNET_HashCode *key,
312 void *value)
313{
314 struct RecentRequest *in = cls;
315 struct RecentRequest *rr = value;
316
317 if ( (0 != GNUNET_memcmp (&in->peer,
318 &rr->peer)) ||
319 (in->type != rr->type) ||
320 (in->xquery_size != rr->xquery_size) ||
321 (0 != memcmp (in->xquery,
322 rr->xquery,
323 in->xquery_size) ) )
324 return GNUNET_OK;
325 GNUNET_break (GNUNET_SYSERR !=
326 GNUNET_BLOCK_group_merge (in->bg,
327 rr->bg));
328 rr->bg = in->bg;
329 GNUNET_free (in);
330 return GNUNET_SYSERR;
331}
332
333
334/**
335 * Add a new entry to our routing table.
336 *
337 * @param sender peer that originated the request
338 * @param type type of the block
339 * @param[in] bg block group for filtering duplicate replies
340 * @param options options for processing
341 * @param key key for the content
342 * @param xquery extended query
343 * @param xquery_size number of bytes in @a xquery
344 * @param reply_bf bloomfilter to filter duplicates
345 * @param reply_bf_mutator mutator for @a reply_bf
346 */
347void
348GDS_ROUTING_add (const struct GNUNET_PeerIdentity *sender,
349 enum GNUNET_BLOCK_Type type,
350 struct GNUNET_BLOCK_Group *bg,
351 enum GNUNET_DHT_RouteOption options,
352 const struct GNUNET_HashCode *key,
353 const void *xquery,
354 size_t xquery_size)
355{
356 struct RecentRequest *recent_req;
357
358 while (GNUNET_CONTAINER_heap_get_size (recent_heap) >= DHT_MAX_RECENT)
359 expire_oldest_entry ();
360 GNUNET_STATISTICS_update (GDS_stats,
361 "# Entries added to routing table",
362 1,
363 GNUNET_NO);
364 recent_req = GNUNET_malloc (sizeof(struct RecentRequest) + xquery_size);
365 recent_req->peer = *sender;
366 recent_req->key = *key;
367 recent_req->bg = bg;
368 recent_req->type = type;
369 recent_req->options = options;
370 recent_req->xquery = &recent_req[1];
371 GNUNET_memcpy (&recent_req[1],
372 xquery,
373 xquery_size);
374 recent_req->xquery_size = xquery_size;
375 if (GNUNET_SYSERR ==
376 GNUNET_CONTAINER_multihashmap_get_multiple (recent_map,
377 key,
378 &try_combine_recent,
379 recent_req))
380 {
381 GNUNET_STATISTICS_update (GDS_stats,
382 "# DHT requests combined",
383 1,
384 GNUNET_NO);
385 return;
386 }
387 recent_req->heap_node
388 = GNUNET_CONTAINER_heap_insert (
389 recent_heap,
390 recent_req,
391 GNUNET_TIME_absolute_get ().abs_value_us);
392 (void) GNUNET_CONTAINER_multihashmap_put (
393 recent_map,
394 key,
395 recent_req,
396 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
397}
398
399
400/**
401 * Initialize routing subsystem.
402 */
403void
404GDS_ROUTING_init ()
405{
406 recent_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
407 recent_map = GNUNET_CONTAINER_multihashmap_create (DHT_MAX_RECENT * 4 / 3,
408 GNUNET_NO);
409}
410
411
412/**
413 * Shutdown routing subsystem.
414 */
415void
416GDS_ROUTING_done ()
417{
418 while (GNUNET_CONTAINER_heap_get_size (recent_heap) > 0)
419 expire_oldest_entry ();
420 GNUNET_assert (0 ==
421 GNUNET_CONTAINER_heap_get_size (recent_heap));
422 GNUNET_CONTAINER_heap_destroy (recent_heap);
423 recent_heap = NULL;
424 GNUNET_assert (0 ==
425 GNUNET_CONTAINER_multihashmap_size (recent_map));
426 GNUNET_CONTAINER_multihashmap_destroy (recent_map);
427 recent_map = NULL;
428}
429
430
431/* end of gnunet-service-dht_routing.c */
diff --git a/src/dht/gnunet-service-dht_routing.h b/src/dht/gnunet-service-dht_routing.h
deleted file mode 100644
index 08c7de870..000000000
--- a/src/dht/gnunet-service-dht_routing.h
+++ /dev/null
@@ -1,87 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011, 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/gnunet-service-dht_routing.h
23 * @brief GNUnet DHT tracking of requests for routing replies
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_DHT_ROUTING_H
27#define GNUNET_SERVICE_DHT_ROUTING_H
28
29#include "gnunet_util_lib.h"
30#include "gnunet_block_lib.h"
31#include "gnunet_dht_service.h"
32
33
34/**
35 * Handle a reply (route to origin). Only forwards the reply back to
36 * other peers waiting for it. Does not do local caching or
37 * forwarding to local clients. Essentially calls
38 * GDS_NEIGHBOURS_handle_reply() for all peers that sent us a matching
39 * request recently.
40 *
41 * @param bd block details
42 * @param query_hash query used in the inquiry
43 * @param get_path_length number of entries in @a get_path
44 * @param get_path peers this reply has traversed so far (if tracked)
45 */
46void
47GDS_ROUTING_process (const struct GNUNET_DATACACHE_Block *bd,
48 const struct GNUNET_HashCode *query_hash,
49 unsigned int get_path_length,
50 const struct GNUNET_DHT_PathElement *get_path);
51
52
53/**
54 * Add a new entry to our routing table.
55 *
56 * @param sender peer that originated the request
57 * @param type type of the block
58 * @param bg block group to evaluate replies, henceforth owned by routing
59 * @param options options for processing
60 * @param key key for the content
61 * @param xquery extended query
62 * @param xquery_size number of bytes in @a xquery
63 */
64void
65GDS_ROUTING_add (const struct GNUNET_PeerIdentity *sender,
66 enum GNUNET_BLOCK_Type type,
67 struct GNUNET_BLOCK_Group *bg,
68 enum GNUNET_DHT_RouteOption options,
69 const struct GNUNET_HashCode *key,
70 const void *xquery,
71 size_t xquery_size);
72
73
74/**
75 * Initialize routing subsystem.
76 */
77void
78GDS_ROUTING_init (void);
79
80
81/**
82 * Shutdown routing subsystem.
83 */
84void
85GDS_ROUTING_done (void);
86
87#endif
diff --git a/src/dht/gnunet_dht_profiler.c b/src/dht/gnunet_dht_profiler.c
deleted file mode 100644
index 55a34bdf0..000000000
--- a/src/dht/gnunet_dht_profiler.c
+++ /dev/null
@@ -1,1032 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2014, 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 dht/gnunet_dht_profiler.c
23 * @brief Profiler for GNUnet DHT
24 * @author Sree Harsha Totakura <sreeharsha@totakura.in>
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_testbed_service.h"
30#include "gnunet_dht_service.h"
31#include "gnunet_constants.h"
32
33
34#define MESSAGE(...) \
35 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, __VA_ARGS__)
36
37#define DEBUG(...) \
38 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
39
40/**
41 * Number of peers which should perform a PUT out of 100 peers
42 */
43static unsigned int put_probability = 100;
44
45/**
46 * Configuration
47 */
48static const struct GNUNET_CONFIGURATION_Handle *cfg;
49
50/**
51 * Name of the file with the hosts to run the test over
52 */
53static char *hosts_file;
54
55/**
56 * Context for a peer which actively does DHT PUT/GET
57 */
58struct ActiveContext;
59
60/**
61 * Context to hold data of peer
62 */
63struct Context
64{
65 /**
66 * The testbed peer this context belongs to
67 */
68 struct GNUNET_TESTBED_Peer *peer;
69
70 /**
71 * Testbed operation acting on this peer
72 */
73 struct GNUNET_TESTBED_Operation *op;
74
75 /**
76 * Active context; NULL if this peer is not an active peer
77 */
78 struct ActiveContext *ac;
79};
80
81
82/**
83 * Context for a peer which actively does DHT PUT/GET
84 */
85struct ActiveContext
86{
87 /**
88 * The linked peer context
89 */
90 struct Context *ctx;
91
92 /**
93 * Handler to the DHT service
94 */
95 struct GNUNET_DHT_Handle *dht;
96
97 /**
98 * The active context used for our DHT GET
99 */
100 struct ActiveContext *get_ac;
101
102 /**
103 * The put handle
104 */
105 struct GNUNET_DHT_PutHandle *dht_put;
106
107 /**
108 * The get handle
109 */
110 struct GNUNET_DHT_GetHandle *dht_get;
111
112 /**
113 * The hashes of the values stored via this activity context.
114 * Array of length #num_puts_per_peer.
115 */
116 struct GNUNET_HashCode *hash;
117
118 /**
119 * Delay task
120 */
121 struct GNUNET_SCHEDULER_Task *delay_task;
122
123 /**
124 * How many puts should we still issue?
125 */
126 unsigned int put_count;
127
128 /**
129 * The number of peers currently doing GET on our data
130 */
131 uint16_t nrefs;
132};
133
134
135/**
136 * An array of contexts. The size of this array should be equal to @a num_peers
137 */
138static struct Context *a_ctx;
139
140/**
141 * Array of active peers
142 */
143static struct ActiveContext *a_ac;
144
145/**
146 * The delay between rounds for collecting statistics
147 */
148static struct GNUNET_TIME_Relative delay_stats;
149
150/**
151 * The delay to start puts.
152 */
153static struct GNUNET_TIME_Relative delay_put;
154
155/**
156 * The delay to start puts.
157 */
158static struct GNUNET_TIME_Relative delay_get;
159
160/**
161 * The timeout for GET and PUT
162 */
163static struct GNUNET_TIME_Relative timeout;
164
165/**
166 * Number of peers
167 */
168static unsigned int num_peers;
169
170/**
171 * Number of active peers
172 */
173static unsigned int n_active;
174
175/**
176 * Number of DHT service connections we currently have
177 */
178static unsigned int n_dht;
179
180/**
181 * Number of DHT PUTs made
182 */
183static unsigned long long n_puts;
184
185/**
186 * Number of DHT PUTs to be made per peer.
187 */
188static unsigned int num_puts_per_peer = 1;
189
190/**
191 * Number of DHT PUTs succeeded
192 */
193static unsigned long long n_puts_ok;
194
195/**
196 * Number of DHT GETs made
197 */
198static unsigned int n_gets;
199
200/**
201 * Number of DHT GETs succeeded
202 */
203static unsigned int n_gets_ok;
204
205/**
206 * Number of DHT GETs succeeded
207 */
208static unsigned int n_gets_fail;
209
210/**
211 * Replication degree
212 */
213static unsigned int replication;
214
215/**
216 * Testbed Operation (to get stats).
217 */
218static struct GNUNET_TESTBED_Operation *bandwidth_stats_op;
219
220/**
221 * Testbed peer handles.
222 */
223static struct GNUNET_TESTBED_Peer **testbed_handles;
224
225/**
226 * Total number of messages sent by peer.
227 */
228static uint64_t outgoing_bandwidth;
229
230/**
231 * Total number of messages received by peer.
232 */
233static uint64_t incoming_bandwidth;
234
235/**
236 * Average number of hops taken to do put.
237 */
238static double average_put_path_length;
239
240/**
241 * Average number of hops taken to do get.
242 */
243static double average_get_path_length;
244
245/**
246 * Total put path length across all peers.
247 */
248static unsigned int total_put_path_length;
249
250/**
251 * Total get path length across all peers.
252 */
253static unsigned int total_get_path_length;
254
255/**
256 * Counter to keep track of peers added to peer_context lists.
257 */
258static int peers_started = 0;
259
260/**
261 * Should we do a PUT (mode = 0) or GET (mode = 1);
262 */
263static enum
264{
265 MODE_PUT = 0,
266
267 MODE_GET = 1
268} mode;
269
270
271/**
272 * Are we shutting down
273 */
274static int in_shutdown = 0;
275
276
277/**
278 * Connect to DHT services of active peers
279 */
280static void
281start_profiling (void);
282
283
284/**
285 * Shutdown task. Cleanup all resources and operations.
286 *
287 * @param cls NULL
288 */
289static void
290do_shutdown (void *cls)
291{
292 struct ActiveContext *ac;
293
294 in_shutdown = GNUNET_YES;
295 if (NULL != a_ctx)
296 {
297 for (unsigned int cnt = 0; cnt < num_peers; cnt++)
298 {
299 /* Cleanup active context if this peer is an active peer */
300 ac = a_ctx[cnt].ac;
301 if (NULL != ac)
302 {
303 if (NULL != ac->delay_task)
304 GNUNET_SCHEDULER_cancel (ac->delay_task);
305 if (NULL != ac->hash)
306 free (ac->hash);
307 if (NULL != ac->dht_put)
308 GNUNET_DHT_put_cancel (ac->dht_put);
309 if (NULL != ac->dht_get)
310 GNUNET_DHT_get_stop (ac->dht_get);
311 }
312 /* Cleanup testbed operation handle at the last as this operation may
313 contain service connection to DHT */
314 if (NULL != a_ctx[cnt].op)
315 GNUNET_TESTBED_operation_done (a_ctx[cnt].op);
316 }
317 GNUNET_free (a_ctx);
318 a_ctx = NULL;
319 }
320 // FIXME: Should we collect stats only for put/get not for other messages.
321 if (NULL != bandwidth_stats_op)
322 {
323 GNUNET_TESTBED_operation_done (bandwidth_stats_op);
324 bandwidth_stats_op = NULL;
325 }
326 GNUNET_free (a_ac);
327}
328
329
330/**
331 * Stats callback. Finish the stats testbed operation and when all stats have
332 * been iterated, shutdown the test.
333 *
334 * @param cls closure
335 * @param op the operation that has been finished
336 * @param emsg error message in case the operation has failed; will be NULL if
337 * operation has executed successfully.
338 */
339static void
340bandwidth_stats_cont (void *cls,
341 struct GNUNET_TESTBED_Operation *op,
342 const char *emsg)
343{
344 MESSAGE ("# Outgoing (core) bandwidth: %llu bytes\n",
345 (unsigned long long) outgoing_bandwidth);
346 MESSAGE ("# Incoming (core) bandwidth: %llu bytes\n",
347 (unsigned long long) incoming_bandwidth);
348 fprintf (stderr,
349 "Benchmark done. Collect data via gnunet-statistics, then press ENTER to exit.\n");
350 (void) getchar ();
351 GNUNET_SCHEDULER_shutdown ();
352}
353
354
355/**
356 * Process statistic values.
357 *
358 * @param cls closure
359 * @param peer the peer the statistic belong to
360 * @param subsystem name of subsystem that created the statistic
361 * @param name the name of the datum
362 * @param value the current value
363 * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
364 * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
365 */
366static int
367bandwidth_stats_iterator (void *cls,
368 const struct GNUNET_TESTBED_Peer *peer,
369 const char *subsystem,
370 const char *name,
371 uint64_t value,
372 int is_persistent)
373{
374 static const char *s_sent = "# bytes encrypted";
375 static const char *s_recv = "# bytes decrypted";
376
377 if (0 == strncmp (s_sent, name, strlen (s_sent)))
378 outgoing_bandwidth = outgoing_bandwidth + value;
379 else if (0 == strncmp (s_recv, name, strlen (s_recv)))
380 incoming_bandwidth = incoming_bandwidth + value;
381 return GNUNET_OK;
382}
383
384
385static void
386summarize ()
387{
388 MESSAGE ("# PUTS started: %llu\n",
389 n_puts);
390 MESSAGE ("# PUTS succeeded: %llu\n",
391 n_puts_ok);
392 MESSAGE ("# GETS made: %u\n",
393 n_gets);
394 MESSAGE ("# GETS succeeded: %u\n",
395 n_gets_ok);
396 MESSAGE ("# GETS failed: %u\n",
397 n_gets_fail);
398 MESSAGE ("# average_put_path_length: %f\n",
399 average_put_path_length);
400 MESSAGE ("# average_get_path_length: %f\n",
401 average_get_path_length);
402
403 if (NULL == testbed_handles)
404 {
405 MESSAGE ("No peers found\n");
406 return;
407 }
408 /* Collect Stats*/
409 bandwidth_stats_op = GNUNET_TESTBED_get_statistics (n_active,
410 testbed_handles,
411 "core",
412 NULL,
413 &bandwidth_stats_iterator,
414 &bandwidth_stats_cont,
415 NULL);
416}
417
418
419/**
420 * Task to cancel DHT GET.
421 *
422 * @param cls NULL
423 */
424static void
425cancel_get (void *cls)
426{
427 struct ActiveContext *ac = cls;
428 struct Context *ctx = ac->ctx;
429
430 ac->delay_task = NULL;
431 GNUNET_assert (NULL != ac->dht_get);
432 GNUNET_DHT_get_stop (ac->dht_get);
433 ac->dht_get = NULL;
434 n_gets_fail++;
435 GNUNET_assert (NULL != ctx->op);
436 GNUNET_TESTBED_operation_done (ctx->op);
437 ctx->op = NULL;
438
439 /* If profiling is complete, summarize */
440 if (n_active == n_gets_fail + n_gets_ok)
441 {
442 average_put_path_length = (double) total_put_path_length
443 / (double) n_active;
444 average_get_path_length = (double) total_get_path_length
445 / (double ) n_gets_ok;
446 summarize ();
447 }
448}
449
450
451/**
452 * Iterator called on each result obtained for a DHT
453 * operation that expects a reply
454 *
455 * @param cls closure
456 * @param exp when will this value expire
457 * @param key key of the result
458 * @param trunc_peer peer the path was truncated at, or NULL
459 * @param get_path peers on reply path (or NULL if not recorded)
460 * [0] = datastore's first neighbor, [length - 1] = local peer
461 * @param get_path_length number of entries in @a get_path
462 * @param put_path peers on the PUT path (or NULL if not recorded)
463 * [0] = origin, [length - 1] = datastore
464 * @param put_path_length number of entries in @a put_path
465 * @param type type of the result
466 * @param size number of bytes in @a data
467 * @param data pointer to the result data
468 */
469static void
470get_iter (void *cls,
471 struct GNUNET_TIME_Absolute exp,
472 const struct GNUNET_HashCode *key,
473 const struct GNUNET_PeerIdentity *trunc_peer,
474 const struct GNUNET_DHT_PathElement *get_path,
475 unsigned int get_path_length,
476 const struct GNUNET_DHT_PathElement *put_path,
477 unsigned int put_path_length,
478 enum GNUNET_BLOCK_Type type,
479 size_t size,
480 const void *data)
481{
482 struct ActiveContext *ac = cls;
483 struct ActiveContext *get_ac = ac->get_ac;
484 struct Context *ctx = ac->ctx;
485
486 /* we found the data we are looking for */
487 DEBUG ("We found a GET request; %u remaining\n",
488 n_gets - (n_gets_fail + n_gets_ok)); // FIXME: It always prints 1.
489 n_gets_ok++;
490 get_ac->nrefs--;
491 GNUNET_DHT_get_stop (ac->dht_get);
492 ac->dht_get = NULL;
493 if (ac->delay_task != NULL)
494 GNUNET_SCHEDULER_cancel (ac->delay_task);
495 ac->delay_task = NULL;
496 GNUNET_assert (NULL != ctx->op);
497 GNUNET_TESTBED_operation_done (ctx->op);
498 ctx->op = NULL;
499
500 total_put_path_length = total_put_path_length + (double) put_path_length;
501 total_get_path_length = total_get_path_length + (double) get_path_length;
502 DEBUG ("total_put_path_length = %u,put_path \n",
503 total_put_path_length);
504 /* Summarize if profiling is complete */
505 if (n_active == n_gets_fail + n_gets_ok)
506 {
507 average_put_path_length = (double) total_put_path_length
508 / (double) n_active;
509 average_get_path_length = (double) total_get_path_length
510 / (double ) n_gets_ok;
511 summarize ();
512 }
513}
514
515
516/**
517 * Task to do DHT GETs
518 *
519 * @param cls the active context
520 */
521static void
522delayed_get (void *cls)
523{
524 struct ActiveContext *ac = cls;
525 struct ActiveContext *get_ac;
526 unsigned int r;
527
528 ac->delay_task = NULL;
529 get_ac = NULL;
530 while (1)
531 {
532 r = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
533 n_active);
534 get_ac = &a_ac[r];
535 if (NULL != get_ac->hash)
536 break;
537 }
538 get_ac->nrefs++;
539 ac->get_ac = get_ac;
540 r = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
541 num_puts_per_peer);
542 DEBUG ("GET_REQUEST_START key %s \n",
543 GNUNET_h2s (&get_ac->hash[r]));
544 ac->dht_get = GNUNET_DHT_get_start (ac->dht,
545 GNUNET_BLOCK_TYPE_TEST,
546 &get_ac->hash[r],
547 1, /* replication level */
548 GNUNET_DHT_RO_NONE,
549 NULL,
550 0, /* extended query and size */
551 &get_iter,
552 ac); /* GET iterator and closure */
553 n_gets++;
554
555 /* schedule the timeout task for GET */
556 ac->delay_task = GNUNET_SCHEDULER_add_delayed (timeout,
557 &cancel_get,
558 ac);
559}
560
561
562/**
563 * Task to do DHT PUTs. If the "put_count" hits zero,
564 * we stop the TESTBED operation (connection to DHT)
565 * so that others PUTs have a chance.
566 *
567 * @param cls the active context
568 */
569static void
570delayed_put (void *cls);
571
572
573/**
574 * Conclude individual PUT operation, schedule the
575 * next one.
576 *
577 * @param cls the active context
578 */
579static void
580put_cont (void *cls)
581{
582 struct ActiveContext *ac = cls;
583
584 ac->dht_put = NULL;
585 n_puts_ok++;
586 ac->delay_task = GNUNET_SCHEDULER_add_now (&delayed_put,
587 ac);
588}
589
590
591/**
592 * Task to do DHT PUTs. If the "put_count" hits zero,
593 * we stop the TESTBED operation (connection to DHT)
594 * so that others PUTs have a chance.
595 *
596 * @param cls the active context
597 */
598static void
599delayed_put (void *cls)
600{
601 struct ActiveContext *ac = cls;
602 char block[65536];
603 size_t block_size;
604
605 ac->delay_task = NULL;
606 if (0 == ac->put_count)
607 {
608 struct Context *ctx = ac->ctx;
609 struct GNUNET_TESTBED_Operation *op;
610
611 GNUNET_assert (NULL != ctx);
612 op = ctx->op;
613 ctx->op = NULL;
614 GNUNET_TESTBED_operation_done (op);
615 return;
616 }
617
618
619 /* Generate and DHT PUT some random data */
620 block_size = 16; /* minimum */
621 /* make random payload, reserve 512 - 16 bytes for DHT headers */
622 block_size += GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
623 GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE
624 - 512);
625 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
626 block,
627 block_size);
628 ac->put_count--;
629 GNUNET_CRYPTO_hash (block,
630 block_size,
631 &ac->hash[ac->put_count]);
632 DEBUG ("PUT_REQUEST_START key %s\n",
633 GNUNET_h2s (&ac->hash[ac->put_count]));
634 ac->dht_put = GNUNET_DHT_put (ac->dht,
635 &ac->hash[ac->put_count],
636 replication,
637 GNUNET_DHT_RO_RECORD_ROUTE,
638 GNUNET_BLOCK_TYPE_TEST,
639 block_size,
640 block,
641 GNUNET_TIME_UNIT_FOREVER_ABS, /* expiration time */
642 &put_cont,
643 ac); /* continuation and its closure */
644 n_puts++;
645}
646
647
648/**
649 * Connection to DHT has been established. Call the delay task.
650 *
651 * @param cls the active context
652 * @param op the operation that has been finished
653 * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
654 * @param emsg error message in case the operation has failed; will be NULL if
655 * operation has executed successfully.
656 */
657static void
658dht_connected (void *cls,
659 struct GNUNET_TESTBED_Operation *op,
660 void *ca_result,
661 const char *emsg)
662{
663 struct ActiveContext *ac = cls;
664 struct Context *ctx = ac->ctx;
665
666 GNUNET_assert (NULL != ctx); // FIXME: Fails
667 GNUNET_assert (NULL != ctx->op);
668 GNUNET_assert (ctx->op == op);
669 ac->dht = (struct GNUNET_DHT_Handle *) ca_result;
670 if (NULL != emsg)
671 {
672 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
673 "Connection to DHT service failed: %s\n",
674 emsg);
675 GNUNET_TESTBED_operation_done (ctx->op); /* Calls dht_disconnect() */
676 ctx->op = NULL;
677 return;
678 }
679 switch (mode)
680 {
681 case MODE_PUT:
682 {
683 struct GNUNET_TIME_Relative peer_delay_put;
684
685 peer_delay_put.rel_value_us =
686 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
687 delay_put.rel_value_us);
688 ac->put_count = num_puts_per_peer;
689 ac->hash = calloc (ac->put_count,
690 sizeof(struct GNUNET_HashCode));
691 if (NULL == ac->hash)
692 {
693 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
694 "calloc");
695 GNUNET_SCHEDULER_shutdown ();
696 return;
697 }
698 ac->delay_task = GNUNET_SCHEDULER_add_delayed (peer_delay_put,
699 &delayed_put,
700 ac);
701 break;
702 }
703
704 case MODE_GET:
705 {
706 struct GNUNET_TIME_Relative peer_delay_get;
707
708 peer_delay_get.rel_value_us =
709 delay_get.rel_value_us
710 + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
711 delay_get.rel_value_us);
712 ac->delay_task = GNUNET_SCHEDULER_add_delayed (peer_delay_get,
713 &delayed_get,
714 ac);
715 break;
716 }
717 }
718}
719
720
721/**
722 * Connect to DHT service and return the DHT client handler
723 *
724 * @param cls the active context
725 * @param cfg configuration of the peer to connect to; will be available until
726 * GNUNET_TESTBED_operation_done() is called on the operation returned
727 * from GNUNET_TESTBED_service_connect()
728 * @return service handle to return in 'op_result', NULL on error
729 */
730static void *
731dht_connect (void *cls,
732 const struct GNUNET_CONFIGURATION_Handle *cfg)
733{
734 n_dht++;
735 return GNUNET_DHT_connect (cfg,
736 10);
737}
738
739
740/**
741 * Adapter function called to destroy a connection to
742 * a service.
743 *
744 * @param cls the active context
745 * @param op_result service handle returned from the connect adapter
746 */
747static void
748dht_disconnect (void *cls,
749 void *op_result)
750{
751 struct ActiveContext *ac = cls;
752
753 GNUNET_assert (NULL != ac->dht);
754 GNUNET_assert (ac->dht == op_result);
755 GNUNET_DHT_disconnect (ac->dht);
756 ac->dht = NULL;
757 n_dht--;
758 if (0 != n_dht)
759 return;
760 if (GNUNET_YES == in_shutdown)
761 return;
762 switch (mode)
763 {
764 case MODE_PUT:
765 if (n_puts_ok != ((unsigned long long) n_active) * num_puts_per_peer)
766 return;
767 /* Start GETs if all PUTs have been made */
768 mode = MODE_GET;
769 start_profiling ();
770 return;
771
772 case MODE_GET:
773 if ((n_gets_ok + n_gets_fail) != n_active)
774 return;
775 break;
776 }
777}
778
779
780/**
781 * Connect to DHT services of active peers
782 */
783static void
784start_profiling ()
785{
786 struct Context *ctx;
787
788 DEBUG ("GNUNET_TESTBED_service_connect\n");
789 GNUNET_break (GNUNET_YES != in_shutdown);
790 for (unsigned int i = 0; i < n_active; i++)
791 {
792 struct ActiveContext *ac = &a_ac[i];
793 GNUNET_assert (NULL != (ctx = ac->ctx));
794 GNUNET_assert (NULL == ctx->op);
795 ctx->op = GNUNET_TESTBED_service_connect (ctx,
796 ctx->peer,
797 "dht",
798 &dht_connected, ac,
799 &dht_connect,
800 &dht_disconnect,
801 ac);
802 }
803}
804
805
806/**
807 * Callback called when DHT service on the peer is started
808 *
809 * @param cls the context
810 * @param op the operation that has been finished
811 * @param emsg error message in case the operation has failed; will be NULL if
812 * operation has executed successfully.
813 */
814static void
815service_started (void *cls,
816 struct GNUNET_TESTBED_Operation *op,
817 const char *emsg)
818{
819 struct Context *ctx = cls;
820
821 GNUNET_assert (NULL != ctx);
822 GNUNET_assert (NULL != ctx->op);
823 GNUNET_TESTBED_operation_done (ctx->op);
824 ctx->op = NULL;
825 peers_started++;
826 DEBUG ("Peers Started = %d; num_peers = %d \n",
827 peers_started,
828 num_peers);
829 if (peers_started == num_peers)
830 start_profiling ();
831}
832
833
834/**
835 * Signature of a main function for a testcase.
836 *
837 * @param cls closure
838 * @param h the run handle
839 * @param num_peers number of peers in 'peers'
840 * @param peers handle to peers run in the testbed
841 * @param links_succeeded the number of overlay link connection attempts that
842 * succeeded
843 * @param links_failed the number of overlay link
844 */
845static void
846test_run (void *cls,
847 struct GNUNET_TESTBED_RunHandle *h,
848 unsigned int num_peers,
849 struct GNUNET_TESTBED_Peer **peers,
850 unsigned int links_succeeded,
851 unsigned int links_failed)
852{
853 unsigned int ac_cnt;
854
855 testbed_handles = peers;
856 if (NULL == peers)
857 {
858 /* exit */
859 GNUNET_assert (0);
860 }
861 MESSAGE ("%u peers started, %u/%u links up\n",
862 num_peers,
863 links_succeeded,
864 links_succeeded + links_failed);
865 a_ctx = GNUNET_new_array (num_peers,
866 struct Context);
867 /* select the peers which actively participate in profiling */
868 n_active = num_peers * put_probability / 100;
869 if (0 == n_active)
870 {
871 GNUNET_SCHEDULER_shutdown ();
872 GNUNET_free (a_ctx);
873 a_ctx = NULL;
874 return;
875 }
876
877 a_ac = GNUNET_new_array (n_active,
878 struct ActiveContext);
879 ac_cnt = 0;
880 for (unsigned int cnt = 0; cnt < num_peers && ac_cnt < n_active; cnt++)
881 {
882 if (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
883 100) >= put_probability)
884 continue;
885
886 a_ctx[cnt].ac = &a_ac[ac_cnt];
887 a_ac[ac_cnt].ctx = &a_ctx[cnt];
888 ac_cnt++;
889 }
890 n_active = ac_cnt;
891
892 /* start DHT service on all peers */
893 for (unsigned int cnt = 0; cnt < num_peers; cnt++)
894 {
895 a_ctx[cnt].peer = peers[cnt];
896 a_ctx[cnt].op = GNUNET_TESTBED_peer_manage_service (&a_ctx[cnt],
897 peers[cnt],
898 "dht",
899 &service_started,
900 &a_ctx[cnt],
901 1);
902 }
903}
904
905
906/**
907 * Main function that will be run by the scheduler.
908 *
909 * @param cls closure
910 * @param args remaining command-line arguments
911 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
912 * @param config configuration
913 */
914static void
915run (void *cls,
916 char *const *args,
917 const char *cfgfile,
918 const struct GNUNET_CONFIGURATION_Handle *config)
919{
920 uint64_t event_mask;
921
922 if (0 == num_peers)
923 {
924 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
925 _ ("Exiting as the number of peers is %u\n"),
926 num_peers);
927 return;
928 }
929 cfg = config;
930 event_mask = 0;
931 GNUNET_TESTBED_run (hosts_file,
932 cfg,
933 num_peers,
934 event_mask,
935 NULL,
936 NULL,
937 &test_run,
938 NULL);
939 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
940 NULL);
941}
942
943
944/**
945 * Main function.
946 *
947 * @return 0 on success
948 */
949int
950main (int argc,
951 char *const *argv)
952{
953 int rc;
954 struct GNUNET_GETOPT_CommandLineOption options[] = {
955 GNUNET_GETOPT_option_uint ('n',
956 "peers",
957 "COUNT",
958 gettext_noop ("number of peers to start"),
959 &num_peers),
960 GNUNET_GETOPT_option_uint ('p',
961 "peer-put-count",
962 "COUNT",
963 gettext_noop (
964 "number of PUTs to perform per peer"),
965 &num_puts_per_peer),
966 GNUNET_GETOPT_option_string ('H',
967 "hosts",
968 "FILENAME",
969 gettext_noop (
970 "name of the file with the login information for the testbed"),
971 &hosts_file),
972 GNUNET_GETOPT_option_relative_time ('D',
973 "delay",
974 "DELAY",
975 gettext_noop (
976 "delay between rounds for collecting statistics (default: 30 sec)"),
977 &delay_stats),
978 GNUNET_GETOPT_option_relative_time ('P',
979 "PUT-delay",
980 "DELAY",
981 gettext_noop (
982 "delay to start doing PUTs (default: 1 sec)"),
983 &delay_put),
984 GNUNET_GETOPT_option_relative_time ('G',
985 "GET-delay",
986 "DELAY",
987 gettext_noop (
988 "delay to start doing GETs (default: 5 min)"),
989 &delay_get),
990 GNUNET_GETOPT_option_uint ('r',
991 "replication",
992 "DEGREE",
993 gettext_noop ("replication degree for DHT PUTs"),
994 &replication),
995 GNUNET_GETOPT_option_uint ('R',
996 "random-chance",
997 "PROBABILITY",
998 gettext_noop (
999 "chance that a peer is selected at random for PUTs"),
1000 &put_probability),
1001 GNUNET_GETOPT_option_relative_time ('t',
1002 "timeout",
1003 "TIMEOUT",
1004 gettext_noop (
1005 "timeout for DHT PUT and GET requests (default: 1 min)"),
1006 &timeout),
1007 GNUNET_GETOPT_OPTION_END
1008 };
1009
1010 if (GNUNET_OK !=
1011 GNUNET_STRINGS_get_utf8_args (argc, argv,
1012 &argc, &argv))
1013 return 2;
1014 /* set default delays */
1015 delay_stats = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10);
1016 delay_put = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10);
1017 delay_get = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10);
1018 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10);
1019 replication = 1; /* default replication */
1020 rc = 0;
1021 if (GNUNET_OK !=
1022 GNUNET_PROGRAM_run (argc,
1023 argv,
1024 "gnunet-dht-profiler",
1025 gettext_noop (
1026 "Measure quality and performance of the DHT service."),
1027 options,
1028 &run,
1029 NULL))
1030 rc = 1;
1031 return rc;
1032}
diff --git a/src/dht/plugin_block_dht.c b/src/dht/plugin_block_dht.c
deleted file mode 100644
index bee16736c..000000000
--- a/src/dht/plugin_block_dht.c
+++ /dev/null
@@ -1,408 +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_lib.h"
31#include "gnunet_hello_uri_lib.h"
32#include "gnunet_block_plugin.h"
33#include "gnunet_block_group_lib.h"
34
35#define DEBUG_DHT GNUNET_EXTRA_LOGGING
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_dht_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,
69 unsigned 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_dht_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_DHT_HELLO:
109 if (0 != xquery_size)
110 {
111 GNUNET_break_op (0);
112 return GNUNET_NO;
113 }
114 return GNUNET_OK;
115 case GNUNET_BLOCK_TYPE_DHT_URL_HELLO:
116 if (0 != xquery_size)
117 {
118 GNUNET_break_op (0);
119 return GNUNET_NO;
120 }
121 return GNUNET_OK;
122 default:
123 GNUNET_break (0);
124 return GNUNET_SYSERR;
125 }
126}
127
128
129/**
130 * Function called to validate a block for storage.
131 *
132 * @param cls closure
133 * @param type block type
134 * @param block block data to validate
135 * @param block_size number of bytes in @a block
136 * @return #GNUNET_OK if the block is fine, #GNUNET_NO if not
137 */
138static enum GNUNET_GenericReturnValue
139block_plugin_dht_check_block (void *cls,
140 enum GNUNET_BLOCK_Type type,
141 const void *block,
142 size_t block_size)
143{
144 switch (type)
145 {
146 case GNUNET_BLOCK_TYPE_DHT_HELLO:
147 {
148 const struct GNUNET_HELLO_Message *hello;
149 struct GNUNET_PeerIdentity pid;
150 const struct GNUNET_MessageHeader *msg;
151
152 if (block_size < sizeof(struct GNUNET_MessageHeader))
153 {
154 GNUNET_break_op (0);
155 return GNUNET_NO;
156 }
157 msg = block;
158 if (block_size != ntohs (msg->size))
159 {
160 GNUNET_break_op (0);
161 return GNUNET_NO;
162 }
163 hello = block;
164 if (GNUNET_OK !=
165 GNUNET_HELLO_get_id (hello,
166 &pid))
167 {
168 GNUNET_break_op (0);
169 return GNUNET_NO;
170 }
171 return GNUNET_OK;
172 }
173 case GNUNET_BLOCK_TYPE_DHT_URL_HELLO:
174 {
175 struct GNUNET_HELLO_Builder *b;
176 struct GNUNET_PeerIdentity pid;
177 struct GNUNET_HashCode h_pid;
178
179 b = GNUNET_HELLO_builder_from_block (block,
180 block_size);
181 if (NULL == b)
182 {
183 GNUNET_break (0);
184 return GNUNET_NO;
185 }
186 GNUNET_HELLO_builder_iterate (b,
187 &pid,
188 NULL, NULL);
189 GNUNET_CRYPTO_hash (&pid,
190 sizeof (pid),
191 &h_pid);
192 GNUNET_HELLO_builder_free (b);
193 return GNUNET_OK;
194 }
195 default:
196 GNUNET_break (0);
197 return GNUNET_SYSERR;
198 }
199}
200
201
202/**
203 * Function called to validate a reply to a request. Note that it is assumed
204 * that the reply has already been matched to the key (and signatures checked)
205 * as it would be done with the GetKeyFunction and the
206 * BlockEvaluationFunction.
207 *
208 * @param cls closure
209 * @param type block type
210 * @param group which block group to use for evaluation
211 * @param query original query (hash)
212 * @param xquery extrended query data (can be NULL, depending on type)
213 * @param xquery_size number of bytes in @a xquery
214 * @param reply_block response to validate
215 * @param reply_block_size number of bytes in @a reply_block
216 * @return characterization of result
217 */
218static enum GNUNET_BLOCK_ReplyEvaluationResult
219block_plugin_dht_check_reply (
220 void *cls,
221 enum GNUNET_BLOCK_Type type,
222 struct GNUNET_BLOCK_Group *group,
223 const struct GNUNET_HashCode *query,
224 const void *xquery,
225 size_t xquery_size,
226 const void *reply_block,
227 size_t reply_block_size)
228{
229 switch (type)
230 {
231 case GNUNET_BLOCK_TYPE_DHT_HELLO:
232 {
233 const struct GNUNET_MessageHeader *msg = reply_block;
234 const struct GNUNET_HELLO_Message *hello = reply_block;
235 struct GNUNET_PeerIdentity pid;
236 struct GNUNET_HashCode phash;
237
238 GNUNET_assert (reply_block_size >= sizeof(struct GNUNET_MessageHeader));
239 GNUNET_assert (reply_block_size == ntohs (msg->size));
240 GNUNET_assert (GNUNET_OK ==
241 GNUNET_HELLO_get_id (hello,
242 &pid));
243 GNUNET_CRYPTO_hash (&pid,
244 sizeof(pid),
245 &phash);
246 if (GNUNET_YES ==
247 GNUNET_BLOCK_GROUP_bf_test_and_set (group,
248 &phash))
249 return GNUNET_BLOCK_REPLY_OK_DUPLICATE;
250 return GNUNET_BLOCK_REPLY_OK_MORE;
251 }
252 case GNUNET_BLOCK_TYPE_DHT_URL_HELLO:
253 {
254 struct GNUNET_HELLO_Builder *b;
255 struct GNUNET_PeerIdentity pid;
256 struct GNUNET_HashCode h_pid;
257
258 b = GNUNET_HELLO_builder_from_block (reply_block,
259 reply_block_size);
260 GNUNET_assert (NULL != b);
261 GNUNET_HELLO_builder_iterate (b,
262 &pid,
263 NULL, NULL);
264 GNUNET_CRYPTO_hash (&pid,
265 sizeof (pid),
266 &h_pid);
267 GNUNET_HELLO_builder_free (b);
268 if (GNUNET_YES ==
269 GNUNET_BLOCK_GROUP_bf_test_and_set (group,
270 &h_pid))
271 return GNUNET_BLOCK_REPLY_OK_DUPLICATE;
272 return GNUNET_BLOCK_REPLY_OK_MORE;
273 }
274 default:
275 return GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED;
276 }
277}
278
279
280/**
281 * Function called to obtain the key for a block.
282 *
283 * @param cls closure
284 * @param type block type
285 * @param block block to get the key for
286 * @param block_size number of bytes @a block
287 * @param[out] key set to the key (query) for the given block
288 * @return #GNUNET_OK on success, #GNUNET_SYSERR if type not supported
289 * (or if extracting a key from a block of this type does not work)
290 */
291static enum GNUNET_GenericReturnValue
292block_plugin_dht_get_key (void *cls,
293 enum GNUNET_BLOCK_Type type,
294 const void *block,
295 size_t block_size,
296 struct GNUNET_HashCode *key)
297{
298 switch (type)
299 {
300 case GNUNET_BLOCK_TYPE_DHT_HELLO:
301 {
302 const struct GNUNET_MessageHeader *msg;
303 const struct GNUNET_HELLO_Message *hello;
304 struct GNUNET_PeerIdentity *pid;
305
306 if (block_size < sizeof(struct GNUNET_MessageHeader))
307 {
308 GNUNET_break_op (0);
309 memset (key,
310 0,
311 sizeof (*key));
312 return GNUNET_OK;
313 }
314 msg = block;
315 if (block_size != ntohs (msg->size))
316 {
317 GNUNET_break_op (0);
318 memset (key,
319 0,
320 sizeof (*key));
321 return GNUNET_OK;
322 }
323 hello = block;
324 memset (key,
325 0,
326 sizeof(*key));
327 pid = (struct GNUNET_PeerIdentity *) key;
328 if (GNUNET_OK !=
329 GNUNET_HELLO_get_id (hello,
330 pid))
331 {
332 GNUNET_break_op (0);
333 memset (key,
334 0,
335 sizeof (*key));
336 return GNUNET_OK;
337 }
338 return GNUNET_OK;
339 }
340 case GNUNET_BLOCK_TYPE_DHT_URL_HELLO:
341 {
342 struct GNUNET_HELLO_Builder *b;
343 struct GNUNET_PeerIdentity pid;
344
345 b = GNUNET_HELLO_builder_from_block (block,
346 block_size);
347 if (NULL == b)
348 {
349 GNUNET_break (0);
350 memset (key,
351 0,
352 sizeof (*key));
353 return GNUNET_OK;
354 }
355 GNUNET_HELLO_builder_iterate (b,
356 &pid,
357 NULL, NULL);
358 GNUNET_CRYPTO_hash (&pid,
359 sizeof (pid),
360 key);
361 GNUNET_HELLO_builder_free (b);
362 return GNUNET_OK;
363 }
364 default:
365 GNUNET_break (0);
366 return GNUNET_SYSERR;
367 }
368}
369
370
371/**
372 * Entry point for the plugin.
373 */
374void *
375libgnunet_plugin_block_dht_init (void *cls)
376{
377 static enum GNUNET_BLOCK_Type types[] = {
378 GNUNET_BLOCK_TYPE_DHT_HELLO,
379 GNUNET_BLOCK_TYPE_DHT_URL_HELLO,
380 GNUNET_BLOCK_TYPE_ANY /* end of list */
381 };
382 struct GNUNET_BLOCK_PluginFunctions *api;
383
384 api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions);
385 api->get_key = &block_plugin_dht_get_key;
386 api->check_query = &block_plugin_dht_check_query;
387 api->check_block = &block_plugin_dht_check_block;
388 api->check_reply = &block_plugin_dht_check_reply;
389 api->create_group = &block_plugin_dht_create_group;
390 api->types = types;
391 return api;
392}
393
394
395/**
396 * Exit point from the plugin.
397 */
398void *
399libgnunet_plugin_block_dht_done (void *cls)
400{
401 struct GNUNET_BLOCK_PluginFunctions *api = cls;
402
403 GNUNET_free (api);
404 return NULL;
405}
406
407
408/* end of plugin_block_dht.c */
diff --git a/src/dht/test_dht_2dtorus.conf b/src/dht/test_dht_2dtorus.conf
deleted file mode 100644
index 0e546cf68..000000000
--- a/src/dht/test_dht_2dtorus.conf
+++ /dev/null
@@ -1,37 +0,0 @@
1@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf
2@INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf
3[PATHS]
4GNUNET_TEST_HOME = $GNUNET_TMP/test_dht_2dtorus/
5
6[dht]
7START_ON_DEMAND = YES
8IMMEDIATE_START = YES
9
10[dhtcache]
11QUOTA = 1 MB
12DATABASE = heap
13
14[nat]
15DISABLEV6 = YES
16RETURN_LOCAL_ADDRESSES = YES
17ENABLE_UPNP = NO
18BEHIND_NAT = NO
19ALLOW_NAT = NO
20INTERNAL_ADDRESS = 127.0.0.1
21EXTERNAL_ADDRESS = 127.0.0.1
22
23[ats]
24WAN_QUOTA_IN = 1 GB
25WAN_QUOTA_OUT = 1 GB
26
27[testbed]
28OVERLAY_TOPOLOGY = 2D_TORUS
29
30[nse]
31START_ON_DEMAND = YES
32WORKDELAY = 500 ms
33INTERVAL = 60 s
34WORKBITS = 0
35
36[transport]
37PLUGINS = unix
diff --git a/src/dht/test_dht_api.c b/src/dht/test_dht_api.c
deleted file mode 100644
index 044983b7e..000000000
--- a/src/dht/test_dht_api.c
+++ /dev/null
@@ -1,194 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2015, 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 * @file dht/test_dht_api.c
22 * @brief base test case for dht api
23 * @author Christian Grothoff
24 *
25 * This test case tests DHT api to DUMMY DHT service communication.
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_hello_lib.h"
30#include "gnunet_testing_lib.h"
31#include "gnunet_dht_service.h"
32
33
34/**
35 * How long until we really give up on a particular testcase portion?
36 */
37#define TOTAL_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, \
38 60)
39
40static struct GNUNET_DHT_Handle *dht_handle;
41
42static struct GNUNET_DHT_GetHandle *get_handle;
43
44static struct GNUNET_DHT_PutHandle *put_handle;
45
46static int ok = 1;
47
48static struct GNUNET_SCHEDULER_Task *die_task;
49
50
51static void
52do_shutdown (void *cls)
53{
54 if (NULL != die_task)
55 {
56 GNUNET_SCHEDULER_cancel (die_task);
57 die_task = NULL;
58 }
59 if (NULL != put_handle)
60 {
61 GNUNET_DHT_put_cancel (put_handle);
62 put_handle = NULL;
63 }
64 if (NULL != get_handle)
65 {
66 GNUNET_DHT_get_stop (get_handle);
67 get_handle = NULL;
68 }
69 GNUNET_DHT_disconnect (dht_handle);
70 dht_handle = NULL;
71}
72
73
74static void
75end_badly (void *cls)
76{
77 die_task = NULL;
78 fprintf (stderr,
79 "%s",
80 "Ending on an unhappy note.\n");
81 GNUNET_SCHEDULER_shutdown ();
82 ok = 1;
83}
84
85
86static void
87test_get_iterator (void *cls,
88 struct GNUNET_TIME_Absolute exp,
89 const struct GNUNET_HashCode *key,
90 const struct GNUNET_PeerIdentity *trunc_peer,
91 const struct GNUNET_DHT_PathElement *get_path,
92 unsigned int get_path_length,
93 const struct GNUNET_DHT_PathElement *put_path,
94 unsigned int put_path_length,
95 enum GNUNET_BLOCK_Type type,
96 size_t size,
97 const void *data)
98{
99 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
100 "test_get_iterator called (we got a result), stopping get request!\n");
101 GNUNET_SCHEDULER_shutdown ();
102 ok = 0;
103}
104
105
106/**
107 * Signature of the main function of a task.
108 *
109 * @param cls closure
110 */
111static void
112test_get (void *cls)
113{
114 struct GNUNET_HashCode hash;
115
116 put_handle = NULL;
117 memset (&hash,
118 42,
119 sizeof(struct GNUNET_HashCode));
120 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
121 "Called test_get!\n");
122 GNUNET_assert (dht_handle != NULL);
123 get_handle = GNUNET_DHT_get_start (dht_handle,
124 GNUNET_BLOCK_TYPE_TEST,
125 &hash,
126 1,
127 GNUNET_DHT_RO_NONE,
128 NULL,
129 0,
130 &test_get_iterator,
131 NULL);
132
133 if (NULL == get_handle)
134 {
135 GNUNET_break (0);
136 ok = 1;
137 GNUNET_SCHEDULER_shutdown ();
138 return;
139 }
140}
141
142
143static void
144run (void *cls,
145 const struct GNUNET_CONFIGURATION_Handle *cfg,
146 struct GNUNET_TESTING_Peer *peer)
147{
148 struct GNUNET_HashCode hash;
149 char *data;
150 size_t data_size = 42;
151
152 GNUNET_assert (ok == 1);
153 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
154 NULL);
155 die_task = GNUNET_SCHEDULER_add_delayed (TOTAL_TIMEOUT,
156 &end_badly,
157 NULL);
158 memset (&hash,
159 42,
160 sizeof(struct GNUNET_HashCode));
161 data = GNUNET_malloc (data_size);
162 memset (data, 43, data_size);
163 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
164 "Called test_put!\n");
165 dht_handle = GNUNET_DHT_connect (cfg,
166 100);
167 GNUNET_assert (NULL != dht_handle);
168 put_handle = GNUNET_DHT_put (dht_handle,
169 &hash,
170 1,
171 GNUNET_DHT_RO_NONE,
172 GNUNET_BLOCK_TYPE_TEST,
173 data_size,
174 data,
175 GNUNET_TIME_relative_to_absolute (TOTAL_TIMEOUT),
176 &test_get,
177 NULL);
178 GNUNET_free (data);
179}
180
181
182int
183main (int argc,
184 char *argv[])
185{
186 if (0 != GNUNET_TESTING_peer_run ("test-dht-api",
187 "test_dht_api_data.conf",
188 &run, NULL))
189 return 1;
190 return ok;
191}
192
193
194/* end of test_dht_api.c */
diff --git a/src/dht/test_dht_api_data.conf b/src/dht/test_dht_api_data.conf
deleted file mode 100644
index cb875fad4..000000000
--- a/src/dht/test_dht_api_data.conf
+++ /dev/null
@@ -1,44 +0,0 @@
1@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf
2@INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf
3
4[PATHS]
5GNUNET_TEST_HOME = $GNUNET_TMP/test-dht-api/
6
7[dhtcache]
8QUOTA = 1 MB
9DATABASE = heap
10
11[topology]
12TARGET-CONNECTION-COUNT = 16
13AUTOCONNECT = YES
14FRIENDS-ONLY = NO
15MINIMUM-FRIENDS = 0
16
17[ats]
18WAN_QUOTA_IN = 1 GB
19WAN_QUOTA_OUT = 1 GB
20
21[transport]
22plugins = tcp
23NEIGHBOUR_LIMIT = 50
24PORT = 2091
25
26[transport-tcp]
27TIMEOUT = 300 s
28
29[nat]
30DISABLEV6 = YES
31BINDTO = 127.0.0.1
32ENABLE_UPNP = NO
33BEHIND_NAT = NO
34ALLOW_NAT = NO
35INTERNAL_ADDRESS = 127.0.0.1
36EXTERNAL_ADDRESS = 127.0.0.1
37
38[dht]
39START_ON_DEMAND = YES
40IMMEDIATE_START = YES
41
42[nse]
43START_ON_DEMAND = YES
44WORKBITS = 1
diff --git a/src/dht/test_dht_api_peer1.conf b/src/dht/test_dht_api_peer1.conf
deleted file mode 100644
index 230aa0fa4..000000000
--- a/src/dht/test_dht_api_peer1.conf
+++ /dev/null
@@ -1,41 +0,0 @@
1@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf
2@INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf
3
4[dhtcache]
5QUOTA = 1 MB
6DATABASE = heap
7
8[transport]
9PLUGINS = tcp
10ACCEPT_FROM6 = ::1;
11ACCEPT_FROM = 127.0.0.1;
12NEIGHBOUR_LIMIT = 50
13PORT = 12365
14
15[ats]
16WAN_QUOTA_IN = 1 GB
17WAN_QUOTA_OUT = 1 GB
18
19[transport-tcp]
20TIMEOUT = 300 s
21BINDTO = 127.0.0.1
22
23[PATHS]
24GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-dht-peer-1/
25
26[nat]
27DISABLEV6 = YES
28ENABLE_UPNP = NO
29BEHIND_NAT = NO
30ALLOW_NAT = NO
31INTERNAL_ADDRESS = 127.0.0.1
32EXTERNAL_ADDRESS = 127.0.0.1
33USE_LOCALADDR = NO
34
35[dht]
36START_ON_DEMAND = YES
37IMMEDIATE_START = YES
38
39[nse]
40START_ON_DEMAND = YES
41WORKBITS = 1
diff --git a/src/dht/test_dht_line.conf b/src/dht/test_dht_line.conf
deleted file mode 100644
index 62e337ef2..000000000
--- a/src/dht/test_dht_line.conf
+++ /dev/null
@@ -1,38 +0,0 @@
1@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf
2@INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf
3[PATHS]
4GNUNET_TEST_HOME = $GNUNET_TMP/test_dht_line/
5
6[dht]
7START_ON_DEMAND = YES
8IMMEDIATE_START = YES
9
10[dhtcache]
11QUOTA = 1 MB
12DATABASE = heap
13
14[nat]
15DISABLEV6 = YES
16RETURN_LOCAL_ADDRESSES = YES
17USE_LOCALADDR = YES
18ENABLE_UPNP = NO
19BEHIND_NAT = NO
20ALLOW_NAT = NO
21INTERNAL_ADDRESS = 127.0.0.1
22EXTERNAL_ADDRESS = 127.0.0.1
23
24[ats]
25WAN_QUOTA_IN = 1 GB
26WAN_QUOTA_OUT = 1 GB
27
28[testbed]
29OVERLAY_TOPOLOGY = LINE
30
31[transport]
32plugins = unix
33
34[nse]
35START_ON_DEMAND = YES
36WORKDELAY = 500 ms
37INTERVAL = 60 s
38WORKBITS = 0
diff --git a/src/dht/test_dht_monitor.c b/src/dht/test_dht_monitor.c
deleted file mode 100644
index 3960a2235..000000000
--- a/src/dht/test_dht_monitor.c
+++ /dev/null
@@ -1,439 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011, 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file dht/test_dht_monitor.c
22 * @brief Test for the dht monitoring API; checks that we receive "some" monitor events
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_testbed_service.h"
27#include "gnunet_dht_service.h"
28#include "dht_test_lib.h"
29
30
31/**
32 * How long do we run the test at most?
33 */
34#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300)
35
36/**
37 * How often do we run the PUTs?
38 */
39#define PUT_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, \
40 10)
41
42
43/**
44 * Information we keep for each GET operation.
45 */
46struct GetOperation
47{
48 /**
49 * DLL.
50 */
51 struct GetOperation *next;
52
53 /**
54 * DLL.
55 */
56 struct GetOperation *prev;
57
58 /**
59 * Handle for the operation.
60 */
61 struct GNUNET_DHT_GetHandle *get;
62};
63
64
65/**
66 * Return value from 'main'.
67 */
68static int ok;
69
70/**
71 * Head of list of active GET operations.
72 */
73static struct GetOperation *get_head;
74
75/**
76 * Tail of list of active GET operations.
77 */
78static struct GetOperation *get_tail;
79
80/**
81 * Array of the testbed's peers.
82 */
83static struct GNUNET_TESTBED_Peer **my_peers;
84
85/**
86 * Number of peers to run.
87 */
88static unsigned int NUM_PEERS = 3;
89
90/**
91 * Task called to disconnect peers.
92 */
93static struct GNUNET_SCHEDULER_Task *timeout_task;
94
95/**
96 * Task to do DHT_puts
97 */
98static struct GNUNET_SCHEDULER_Task *put_task;
99
100static struct GNUNET_DHT_MonitorHandle **monitors;
101
102static unsigned int monitor_counter;
103
104
105/**
106 * Task run on success or timeout to clean up.
107 * Terminates active get operations and shuts down
108 * the testbed.
109 *
110 * @param cls the `struct GNUNET_DHT_TEST_Context`
111 */
112static void
113shutdown_task (void *cls)
114{
115 struct GNUNET_DHT_TEST_Context *ctx = cls;
116 unsigned int i;
117 struct GetOperation *get_op;
118
119 ok = (monitor_counter > NUM_PEERS) ? 0 : 2;
120 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
121 "Received %u monitor events\n",
122 monitor_counter);
123 while (NULL != (get_op = get_tail))
124 {
125 GNUNET_DHT_get_stop (get_op->get);
126 GNUNET_CONTAINER_DLL_remove (get_head,
127 get_tail,
128 get_op);
129 GNUNET_free (get_op);
130 }
131 for (i = 0; i < NUM_PEERS; i++)
132 GNUNET_DHT_monitor_stop (monitors[i]);
133 GNUNET_free (monitors);
134 GNUNET_SCHEDULER_cancel (put_task);
135 GNUNET_DHT_TEST_cleanup (ctx);
136 if (NULL != timeout_task)
137 {
138 GNUNET_SCHEDULER_cancel (timeout_task);
139 timeout_task = NULL;
140 }
141}
142
143
144/**
145 * Task run on success or timeout to clean up.
146 * Terminates active get operations and shuts down
147 * the testbed.
148 *
149 * @param cls NULL
150 */
151static void
152timeout_task_cb (void *cls)
153{
154 timeout_task = NULL;
155 GNUNET_SCHEDULER_shutdown ();
156}
157
158
159/**
160 * Iterator called on each result obtained for a DHT
161 * operation that expects a reply
162 *
163 * @param cls closure with our 'struct GetOperation'
164 * @param exp when will this value expire
165 * @param key key of the result
166 * @param trunc_peer peer the path was truncated at, or NULL
167 * @param get_path peers on reply path (or NULL if not recorded)
168 * @param get_path_length number of entries in get_path
169 * @param put_path peers on the PUT path (or NULL if not recorded)
170 * @param put_path_length number of entries in get_path
171 * @param type type of the result
172 * @param size number of bytes in data
173 * @param data pointer to the result data
174 */
175static void
176dht_get_handler (void *cls, struct GNUNET_TIME_Absolute exp,
177 const struct GNUNET_HashCode *key,
178 const struct GNUNET_PeerIdentity *trunc_peer,
179 const struct GNUNET_DHT_PathElement *get_path,
180 unsigned int get_path_length,
181 const struct GNUNET_DHT_PathElement *put_path,
182 unsigned int put_path_length,
183 enum GNUNET_BLOCK_Type type,
184 size_t size, const void *data)
185{
186 struct GetOperation *get_op = cls;
187 struct GNUNET_HashCode want;
188
189 if (sizeof(struct GNUNET_HashCode) != size)
190 {
191 GNUNET_break (0);
192 return;
193 }
194 GNUNET_CRYPTO_hash (key,
195 sizeof(*key),
196 &want);
197 if (0 != memcmp (&want, data, sizeof(want)))
198 {
199 GNUNET_break (0);
200 return;
201 }
202 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
203 "Get successful\n");
204 GNUNET_DHT_get_stop (get_op->get);
205 GNUNET_CONTAINER_DLL_remove (get_head,
206 get_tail,
207 get_op);
208 GNUNET_free (get_op);
209 if (NULL != get_head)
210 return;
211 /* all DHT GET operations successful; terminate! */
212 ok = 0;
213 GNUNET_SCHEDULER_shutdown ();
214}
215
216
217/**
218 * Task to put the id of each peer into the DHT.
219 *
220 * @param cls array with NUM_PEERS DHT handles
221 */
222static void
223do_puts (void *cls)
224{
225 struct GNUNET_DHT_Handle **hs = cls;
226 struct GNUNET_HashCode key;
227 struct GNUNET_HashCode value;
228
229 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
230 "Putting values into DHT\n");
231 for (unsigned int i = 0; i < NUM_PEERS; i++)
232 {
233 GNUNET_CRYPTO_hash (&i, sizeof(i), &key);
234 GNUNET_CRYPTO_hash (&key, sizeof(key), &value);
235 GNUNET_DHT_put (hs[i], &key, 10U,
236 GNUNET_DHT_RO_RECORD_ROUTE
237 | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
238 GNUNET_BLOCK_TYPE_TEST,
239 sizeof(value), &value,
240 GNUNET_TIME_UNIT_FOREVER_ABS,
241 NULL, NULL);
242 }
243 put_task = GNUNET_SCHEDULER_add_delayed (PUT_FREQUENCY,
244 &do_puts, hs);
245}
246
247
248/**
249 * Callback called on each GET request going through the DHT.
250 * Prints the info about the intercepted packet and increments a counter.
251 *
252 * @param cls Closure.
253 * @param options Options, for instance RecordRoute, DemultiplexEverywhere.
254 * @param type The type of data in the request.
255 * @param hop_count Hop count so far.
256 * @param desired_replication_level Desired replication level.
257 * @param key Key of the requested data.
258 */
259static void
260monitor_get_cb (void *cls,
261 enum GNUNET_DHT_RouteOption options,
262 enum GNUNET_BLOCK_Type type,
263 uint32_t hop_count,
264 uint32_t desired_replication_level,
265 const struct GNUNET_HashCode *key)
266{
267 unsigned int i;
268
269 i = (unsigned int) (long) cls;
270 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
271 "%u got a GET message for key %s\n",
272 i,
273 GNUNET_h2s (key));
274 monitor_counter++;
275}
276
277
278/**
279 * Callback called on each PUT request going through the DHT.
280 * Prints the info about the intercepted packet and increments a counter.
281 *
282 * @param cls Closure.
283 * @param options Options, for instance RecordRoute, DemultiplexEverywhere.
284 * @param type The type of data in the request.
285 * @param hop_count Hop count so far.
286 * @param trunc_peer peer the path was truncated at, or NULL
287 * @param path_length number of entries in path (or 0 if not recorded).
288 * @param path peers on the PUT path (or NULL if not recorded).
289 * @param desired_replication_level Desired replication level.
290 * @param exp Expiration time of the data.
291 * @param key Key under which data is to be stored.
292 * @param data Pointer to the data carried.
293 * @param size Number of bytes in data.
294 */
295static void
296monitor_put_cb (void *cls,
297 enum GNUNET_DHT_RouteOption options,
298 enum GNUNET_BLOCK_Type type,
299 uint32_t hop_count,
300 uint32_t desired_replication_level,
301 const struct GNUNET_PeerIdentity *trunc_peer,
302 unsigned int path_length,
303 const struct GNUNET_DHT_PathElement *path,
304 struct GNUNET_TIME_Absolute exp,
305 const struct GNUNET_HashCode *key,
306 const void *data,
307 size_t size)
308{
309 unsigned int i;
310
311 i = (unsigned int) (long) cls;
312 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
313 "%u got a PUT message for key %s with %u bytes\n",
314 i,
315 GNUNET_h2s (key),
316 (unsigned int) size);
317 monitor_counter++;
318}
319
320
321/**
322 * Callback called on each GET reply going through the DHT.
323 * Prints the info about the intercepted packet and increments a counter.
324 *
325 * @param cls Closure.
326 * @param type The type of data in the result.
327 * @param trunc_peer peer the path was truncated at, or NULL
328 * @param get_path Peers on GET path (or NULL if not recorded).
329 * @param get_path_length number of entries in get_path.
330 * @param put_path peers on the PUT path (or NULL if not recorded).
331 * @param put_path_length number of entries in get_path.
332 * @param exp Expiration time of the data.
333 * @param key Key of the data.
334 * @param data Pointer to the result data.
335 * @param size Number of bytes in data.
336 */
337static void
338monitor_res_cb (void *cls,
339 enum GNUNET_BLOCK_Type type,
340 const struct GNUNET_PeerIdentity *trunc_peer,
341 const struct GNUNET_DHT_PathElement *get_path,
342 unsigned int get_path_length,
343 const struct GNUNET_DHT_PathElement *put_path,
344 unsigned int put_path_length,
345 struct GNUNET_TIME_Absolute exp,
346 const struct GNUNET_HashCode *key,
347 const void *data,
348 size_t size)
349{
350 unsigned int i;
351
352 i = (unsigned int) (long) cls;
353 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
354 "%u got a REPLY message for key %s with %u bytes\n",
355 i,
356 GNUNET_h2s (key),
357 (unsigned int) size);
358 monitor_counter++;
359}
360
361
362/**
363 * Main function of the test.
364 *
365 * @param cls closure (NULL)
366 * @param ctx argument to give to GNUNET_DHT_TEST_cleanup on test end
367 * @param num_peers number of peers that are running
368 * @param peers array of peers
369 * @param dhts handle to each of the DHTs of the peers
370 */
371static void
372run (void *cls,
373 struct GNUNET_DHT_TEST_Context *ctx,
374 unsigned int num_peers,
375 struct GNUNET_TESTBED_Peer **peers,
376 struct GNUNET_DHT_Handle **dhts)
377{
378 unsigned int i;
379 unsigned int j;
380 struct GNUNET_HashCode key;
381 struct GetOperation *get_op;
382
383 GNUNET_assert (NUM_PEERS == num_peers);
384 my_peers = peers;
385 monitors = GNUNET_new_array (num_peers,
386 struct GNUNET_DHT_MonitorHandle *);
387 for (i = 0; i < num_peers; i++)
388 monitors[i] = GNUNET_DHT_monitor_start (dhts[i],
389 GNUNET_BLOCK_TYPE_ANY,
390 NULL,
391 &monitor_get_cb,
392 &monitor_res_cb,
393 &monitor_put_cb,
394 (void *) (long) i);
395 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
396 "Peers setup, starting test\n");
397 put_task = GNUNET_SCHEDULER_add_now (&do_puts, dhts);
398 for (i = 0; i < num_peers; i++)
399 {
400 GNUNET_CRYPTO_hash (&i, sizeof(i), &key);
401 for (j = 0; j < num_peers; j++)
402 {
403 get_op = GNUNET_new (struct GetOperation);
404 GNUNET_CONTAINER_DLL_insert (get_head,
405 get_tail,
406 get_op);
407 get_op->get = GNUNET_DHT_get_start (dhts[j],
408 GNUNET_BLOCK_TYPE_TEST, /* type */
409 &key, /*key to search */
410 4U, /* replication level */
411 GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
412 NULL, /* xquery */
413 0, /* xquery bits */
414 &dht_get_handler, get_op);
415 }
416 }
417 timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
418 &timeout_task_cb,
419 NULL);
420 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
421 ctx);
422}
423
424
425/**
426 * Main: start test
427 */
428int
429main (int xargc, char *xargv[])
430{
431 GNUNET_DHT_TEST_run ("test-dht-monitor",
432 "test_dht_monitor.conf",
433 NUM_PEERS,
434 &run, NULL);
435 return ok;
436}
437
438
439/* end of test_dht_monitor.c */
diff --git a/src/dht/test_dht_monitor.conf b/src/dht/test_dht_monitor.conf
deleted file mode 100644
index feefc2f5e..000000000
--- a/src/dht/test_dht_monitor.conf
+++ /dev/null
@@ -1,39 +0,0 @@
1@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf
2@INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf
3
4
5[dhtcache]
6QUOTA = 1 MB
7DATABASE = heap
8
9[transport]
10PLUGINS = tcp
11
12[ats]
13WAN_QUOTA_IN = 1 GB
14WAN_QUOTA_OUT = 1 GB
15
16[testbed]
17OVERLAY_TOPOLOGY = LINE
18
19[PATHS]
20GNUNET_TEST_HOME = $GNUNET_TMP/test-dht-monitor/
21
22[nat]
23DISABLEV6 = YES
24ENABLE_UPNP = NO
25BEHIND_NAT = NO
26ALLOW_NAT = NO
27INTERNAL_ADDRESS = 127.0.0.1
28EXTERNAL_ADDRESS = 127.0.0.1
29USE_LOCALADDR = YES
30RETURN_LOCAL_ADDRESSES = YES
31
32
33[dht]
34START_ON_DEMAND = YES
35IMMEDIATE_START = YES
36
37[nse]
38START_ON_DEMAND = YES
39WORKBITS = 1
diff --git a/src/dht/test_dht_multipeer.conf b/src/dht/test_dht_multipeer.conf
deleted file mode 100644
index ac35664c4..000000000
--- a/src/dht/test_dht_multipeer.conf
+++ /dev/null
@@ -1,40 +0,0 @@
1@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf
2@INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf
3
4[dht]
5START_ON_DEMAND = YES
6IMMEDIATE_START = YES
7
8[dhtcache]
9QUOTA = 1 MB
10DATABASE = heap
11
12[transport]
13PLUGINS = unix
14
15[ats]
16WAN_QUOTA_IN = 1 GB
17WAN_QUOTA_OUT = 1 GB
18
19[testbed]
20OVERLAY_TOPOLOGY = FROM_FILE
21OVERLAY_TOPOLOGY_FILE = test_dht_multipeer_topology.dat
22
23[PATHS]
24GNUNET_TEST_HOME = $GNUNET_TMP/test-dht-multipeer/
25
26[nat]
27DISABLEV6 = YES
28RETURN_LOCAL_ADDRESSES = YES
29ENABLE_UPNP = NO
30BEHIND_NAT = NO
31ALLOW_NAT = NO
32INTERNAL_ADDRESS = 127.0.0.1
33EXTERNAL_ADDRESS = 127.0.0.1
34USE_LOCALADDR = YES
35
36[nse]
37START_ON_DEMAND = YES
38WORKDELAY = 500 ms
39INTERVAL = 60 s
40WORKBITS = 0
diff --git a/src/dht/test_dht_multipeer_topology.dat b/src/dht/test_dht_multipeer_topology.dat
deleted file mode 100644
index 2268c85cb..000000000
--- a/src/dht/test_dht_multipeer_topology.dat
+++ /dev/null
@@ -1,11 +0,0 @@
10:1|6|8
21:2|7|9
32:0|3|8
43:1|4|9
54:0|2|5
65:1|3|6
76:2|4|7
87:3|5|8
98:4|6|9
109:0|5|7
11
diff --git a/src/dht/test_dht_tools.conf b/src/dht/test_dht_tools.conf
deleted file mode 100644
index 05f7cc0e6..000000000
--- a/src/dht/test_dht_tools.conf
+++ /dev/null
@@ -1,157 +0,0 @@
1[dhtcache]
2QUOTA = 1 MB
3DATABASE = heap
4
5[transport]
6PLUGINS = tcp
7ACCEPT_FROM6 = ::1;
8ACCEPT_FROM = 127.0.0.1;
9NEIGHBOUR_LIMIT = 50
10PORT = 12365
11
12[ats]
13WAN_QUOTA_IN = 1 GB
14WAN_QUOTA_OUT = 1 GB
15
16[transport-tcp]
17TIMEOUT = 300 s
18BINDTO = 127.0.0.1
19
20[PATHS]
21GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-dht-peer-1/
22
23[nat]
24DISABLEV6 = YES
25ENABLE_UPNP = NO
26BEHIND_NAT = NO
27ALLOW_NAT = NO
28INTERNAL_ADDRESS = 127.0.0.1
29EXTERNAL_ADDRESS = 127.0.0.1
30USE_LOCALADDR = NO
31
32[dht]
33START_ON_DEMAND = YES
34IMMEDIATE_START = YES
35
36[nse]
37START_ON_DEMAND = YES
38WORKBITS = 1
39# Configuration to disable autostarting of
40# all services above the 'core' level.
41# (including resolver)
42
43[dns]
44START_ON_DEMAND = NO
45
46[cadet]
47START_ON_DEMAND = NO
48
49[datastore]
50START_ON_DEMAND = NO
51
52[fs]
53START_ON_DEMAND = NO
54
55[dv]
56START_ON_DEMAND = NO
57
58[vpn]
59START_ON_DEMAND = NO
60
61[consensus]
62START_ON_DEMAND = NO
63
64[resolver]
65START_ON_DEMAND = NO
66
67[namestore]
68START_ON_DEMAND = NO
69
70[namecache]
71START_ON_DEMAND = NO
72
73[identity]
74START_ON_DEMAND = NO
75
76[revocation]
77START_ON_DEMAND = NO
78
79[conversation]
80START_ON_DEMAND = NO
81
82[peerstore]
83START_ON_DEMAND = NO
84
85[psycstore]
86START_ON_DEMAND = NO
87
88[gns]
89START_ON_DEMAND = NO
90
91[regex]
92START_ON_DEMAND = NO
93
94[set]
95START_ON_DEMAND = NO
96
97[scalarproduct-bob]
98START_ON_DEMAND = NO
99
100[scalarproduct-alice]
101START_ON_DEMAND = NO
102
103[social]
104START_ON_DEMAND = NO
105
106[psyc]
107START_ON_DEMAND = NO
108
109[rps]
110START_ON_DEMAND = NO
111
112[multicast]
113START_ON_DEMAND = NO
114
115[sensordashboard]
116START_ON_DEMAND = NO
117
118[sensor]
119START_ON_DEMAND = NO
120# Configuration file that can be included to prevent ANY of the usual
121# IMMEDIATE_START = YES to be set. Also disables NSE POW calculation.
122#
123# This configuration is included from various configuration test files.
124# Whenever a new service is added that has IMMEDIATE_START = YES for
125# production should be disabled for (most) test suites, the option should
126# be added here instead of all over the place ;-).
127
128[core]
129IMMEDIATE_START = NO
130
131[fs]
132IMMEDIATE_START = NO
133
134[dht]
135IMMEDIATE_START = NO
136
137[cadet]
138IMMEDIATE_START = NO
139
140[nse]
141IMMEDIATE_START = NO
142WORKBITS = 0
143
144[revocation]
145IMMEDIATE_START = NO
146
147[topology]
148IMMEDIATE_START = NO
149
150[hostlist]
151IMMEDIATE_START = NO
152
153[gns]
154IMMEDIATE_START = NO
155
156[zonemaster]
157IMMEDIATE_START = NO
diff --git a/src/dht/test_dht_tools.py.in b/src/dht/test_dht_tools.py.in
deleted file mode 100644
index 84e297081..000000000
--- a/src/dht/test_dht_tools.py.in
+++ /dev/null
@@ -1,149 +0,0 @@
1#!@PYTHONEXE@
2#
3# This testcase simply checks that the DHT command-line tools work.
4# It launches a single peer, stores a value "testdata" under "testkey",
5# and then gives the system 50 ms to fetch it.
6#
7# This could fail if
8# - command line tool interfaces fail
9# - DHT plugins for storage are not installed / working
10# - block plugins for verification (the test plugin) is not installed
11#
12# The code does NOT depend on DHT routing or any actual P2P functionality.
13#
14
15import os
16import sys
17import shutil
18import re
19import subprocess
20import time
21import tempfile
22
23os.environ["PATH"] = "@bindirectory@" + ":" + os.environ["PATH"]
24
25if os.name == "nt":
26 tmp = os.getenv("TEMP")
27else:
28 tmp = "/tmp"
29
30if os.name == 'nt':
31 get = './gnunet-dht-get.exe'
32 put = './gnunet-dht-put.exe'
33 arm = 'gnunet-arm.exe'
34else:
35 get = './gnunet-dht-get'
36 put = './gnunet-dht-put'
37 arm = 'gnunet-arm'
38
39cfgfile = 'test_dht_api_peer1.conf'
40run_get = [get, '-c', cfgfile]
41run_put = [put, '-c', cfgfile]
42run_arm = [arm, '-c', cfgfile]
43debug = os.getenv('DEBUG')
44if debug:
45 run_arm += [debug.split(' ')]
46
47
48def cleanup(exitcode):
49 sys.exit(exitcode)
50
51
52def sub_run(args, want_stdo=True, want_stde=False, nofail=False):
53 if want_stdo:
54 stdo = subprocess.PIPE
55 else:
56 stdo = None
57 if want_stde:
58 stde = subprocess.PIPE
59 else:
60 stde = None
61 p = subprocess.Popen(args, stdout=stdo, stderr=stde)
62 stdo, stde = p.communicate()
63 if not nofail:
64 if p.returncode != 0:
65 sys.exit(p.returncode)
66 return (p.returncode, stdo, stde)
67
68
69def fail(result):
70 print(result)
71 r_arm(['-e'], want_stdo=False)
72 cleanup(1)
73
74
75def r_something(to_run, extra_args, failure=None, normal=True, **kw):
76 rc, stdo, stde = sub_run(to_run + extra_args, nofail=True, **kw)
77 if failure is not None:
78 failure(to_run + extra_args, rc, stdo, stde, normal)
79 return (rc, stdo, stde)
80
81
82def r_arm(extra_args, **kw):
83 return r_something(run_arm, extra_args, **kw)
84
85
86def r_get(extra_args, **kw):
87 return r_something(run_get, extra_args, **kw)
88
89
90def r_put(extra_args, **kw):
91 return r_something(run_put, extra_args, **kw)
92
93
94def end_arm_failure(command, rc, stdo, stde, normal):
95 if normal:
96 if rc != 0:
97 fail(
98 "FAIL: error running {}\nCommand output was:\n{}\n{}".format(
99 command, stdo, stde
100 )
101 )
102 else:
103 if rc == 0:
104 fail(
105 "FAIL: expected error while running {}\nCommand output was:\n{}\n{}"
106 .format(command, stdo, stde)
107 )
108
109
110def print_only_failure(command, rc, stdo, stde, normal):
111 if normal:
112 if rc != 0:
113 print(
114 "FAIL: error running {}\nCommand output was:\n{}\n{}".format(
115 command, stdo, stde
116 )
117 )
118 cleanup(1)
119 else:
120 if rc == 0:
121 print(
122 "FAIL: expected error while running {}\nCommand output was:\n{}\n{}"
123 .format(command, stdo, stde)
124 )
125 cleanup(1)
126
127
128print("TEST: Starting ARM...", end='')
129r_arm(['-s'], failure=end_arm_failure, want_stdo=False, want_stde=False)
130print("PASS")
131time.sleep(1)
132
133print("TEST: Testing put...", end='')
134r_put(['-k', 'testkey', '-d', 'testdata', '-t', '8'], failure=end_arm_failure)
135print("PASS")
136time.sleep(1)
137
138print("TEST: Testing get...", end='')
139rc, stdo, stde = r_get(['-k', 'testkey', '-T', '50 ms', '-t', '8'],
140 want_stdo=True,
141 failure=end_arm_failure)
142stdo = stdo.decode('utf-8').replace('\r', '').splitlines()
143expect = "Result 0, type 8:\ntestdata".splitlines()
144if len(stdo) != 2 or len(expect
145 ) != 2 or stdo[0] != expect[0] or stdo[1] != expect[1]:
146 fail("output `{}' differs from expected `{}'".format(stdo, expect))
147print("PASS")
148
149r_arm(['-e', '-d'], failure=print_only_failure)
diff --git a/src/dht/test_dht_tools.sh b/src/dht/test_dht_tools.sh
deleted file mode 100755
index 56cc99e15..000000000
--- a/src/dht/test_dht_tools.sh
+++ /dev/null
@@ -1,64 +0,0 @@
1#!/bin/sh
2# This file is in the public domain.
3
4out=`mktemp /tmp/test-gnunet-dht-logXXXXXXXX`
5tempcfg=`mktemp /tmp/test-dht-tools.XXXXXXXX`
6checkout="check.out"
7armexe="gnunet-arm -c $tempcfg "
8putexe="gnunet-dht-put -c $tempcfg "
9getexe="gnunet-dht-get -c $tempcfg "
10peerinfo="gnunet-peerinfo -c $tempcfg -sq"
11stop_arm()
12{
13 if ! $armexe $DEBUG -e -d > $out ; then
14 echo "FAIL: error running $armexe"
15 echo "Command output was:"
16 cat $out
17 rm -f $out $tempcfg
18 exit 1
19 fi
20 rm -f $out $tempcfg
21}
22
23cp test_dht_tools.conf $tempcfg
24
25echo -n "TEST: Starting ARM..."
26if ! $armexe $DEBUG -s > $out ; then
27 echo "FAIL: error running $armexe"
28 echo "Command output was:"
29 cat $out
30 stop_arm
31 exit 1
32fi
33echo "PASS"
34
35echo -n "TEST: Testing put..."
36if ! $putexe -k testkey -d testdata -t 8 > $out ; then
37 echo "FAIL: error running $putexe"
38 echo "Command output was:"
39 cat $out
40 stop_arm
41 exit 1
42fi
43echo "PASS"
44
45echo -n "TEST: Testing get..."
46echo "Result 0, type 8:" > $checkout
47echo "testdata" >> $checkout
48
49if ! $getexe -k testkey -T 100ms -t 8 > $out ; then
50 echo "FAIL: error running $putexe"
51 echo "Command output was:"
52 cat $out
53 stop_arm
54 exit 1
55fi
56
57if ! diff --strip-trailing-cr -q $out $checkout ; then
58 echo "FAIL: $out and $checkout differ:"
59 diff --strip-trailing-cr $out $checkout
60 stop_arm
61 exit 1
62fi
63echo "PASS"
64stop_arm
diff --git a/src/dht/test_dht_topo.c b/src/dht/test_dht_topo.c
deleted file mode 100644
index a8294c65d..000000000
--- a/src/dht/test_dht_topo.c
+++ /dev/null
@@ -1,637 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2016 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 dht/test_dht_topo.c
22 * @author Christian Grothoff
23 * @brief Test for the dht service: store and retrieve in various topologies.
24 * Each peer stores a value from the DHT and then each peer tries to get each
25 * value from each other peer.
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_dht_service.h"
30#include "dht_test_lib.h"
31
32/**
33 * How long until we give up on fetching the data?
34 */
35#define GET_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, \
36 120)
37
38/**
39 * How frequently do we execute the PUTs?
40 */
41#define PUT_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, \
42 5)
43
44
45/**
46 * Information we keep for each GET operation.
47 */
48struct GetOperation
49{
50 /**
51 * DLL.
52 */
53 struct GetOperation *next;
54
55 /**
56 * DLL.
57 */
58 struct GetOperation *prev;
59
60 /**
61 * Operation to fetch @a me.
62 */
63 struct GNUNET_TESTBED_Operation *to;
64
65 /**
66 * Handle for the operation.
67 */
68 struct GNUNET_DHT_GetHandle *get;
69
70 /**
71 * DHT used by this operation.
72 */
73 struct GNUNET_DHT_Handle *dht;
74
75 /**
76 * Key we are looking up.
77 */
78 struct GNUNET_HashCode key;
79
80 /**
81 * At which peer is this operation being performed?
82 */
83 struct GNUNET_PeerIdentity me;
84};
85
86
87/**
88 * Result of the test.
89 */
90static int ok;
91
92/**
93 * Task to do DHT_puts
94 */
95static struct GNUNET_SCHEDULER_Task *put_task;
96
97/**
98 * Task to do DHT_gets
99 */
100static struct GNUNET_SCHEDULER_Task *get_task;
101
102/**
103 * Task to time out / regular shutdown.
104 */
105static struct GNUNET_SCHEDULER_Task *timeout_task;
106
107/**
108 * Head of list of active GET operations.
109 */
110static struct GetOperation *get_head;
111
112/**
113 * Tail of list of active GET operations.
114 */
115static struct GetOperation *get_tail;
116
117/**
118 * Array of the testbed's peers.
119 */
120static struct GNUNET_TESTBED_Peer **my_peers;
121
122/**
123 * Number of peers to run.
124 */
125static unsigned int NUM_PEERS;
126
127
128/**
129 * Statistics we print out.
130 */
131static struct
132{
133 const char *subsystem;
134 const char *name;
135 unsigned long long total;
136} stats[] = {
137 { "core", "# bytes decrypted", 0 },
138 { "core", "# bytes encrypted", 0 },
139 { "core", "# type maps received", 0 },
140 { "core", "# session keys confirmed via PONG", 0 },
141 { "core", "# peers connected", 0 },
142 { "core", "# key exchanges initiated", 0 },
143 { "core", "# send requests dropped (disconnected)", 0 },
144 { "core", "# transmissions delayed due to corking", 0 },
145 { "core", "# messages discarded (expired prior to transmission)", 0 },
146 { "core", "# messages discarded (disconnected)", 0 },
147 { "core", "# discarded CORE_SEND requests", 0 },
148 { "core", "# discarded lower priority CORE_SEND requests", 0 },
149 { "transport", "# bytes received via TCP", 0 },
150 { "transport", "# bytes transmitted via TCP", 0 },
151 { "dht", "# PUT messages queued for transmission", 0 },
152 { "dht", "# P2P PUT requests received", 0 },
153 { "dht", "# GET messages queued for transmission", 0 },
154 { "dht", "# P2P GET requests received", 0 },
155 { "dht", "# RESULT messages queued for transmission", 0 },
156 { "dht", "# P2P RESULTS received", 0 },
157 { "dht", "# Queued messages discarded (peer disconnected)", 0 },
158 { "dht", "# Peers excluded from routing due to Bloomfilter", 0 },
159 { "dht", "# Peer selection failed", 0 },
160 { "dht", "# FIND PEER requests ignored due to Bloomfilter", 0 },
161 { "dht", "# FIND PEER requests ignored due to lack of HELLO", 0 },
162 { "dht", "# P2P FIND PEER requests processed", 0 },
163 { "dht", "# P2P GET requests ONLY routed", 0 },
164 { "dht", "# Preference updates given to core", 0 },
165 { "dht", "# REPLIES ignored for CLIENTS (no match)", 0 },
166 { "dht", "# GET requests from clients injected", 0 },
167 { "dht", "# GET requests received from clients", 0 },
168 { "dht", "# GET STOP requests received from clients", 0 },
169 { "dht", "# ITEMS stored in datacache", 0 },
170 { "dht", "# Good RESULTS found in datacache", 0 },
171 { "dht", "# GET requests given to datacache", 0 },
172 { NULL, NULL, 0 }
173};
174
175
176static struct GNUNET_DHT_TEST_Context *
177stop_ops (void)
178{
179 struct GetOperation *get_op;
180 struct GNUNET_DHT_TEST_Context *ctx = NULL;
181
182 if (NULL != timeout_task)
183 {
184 ctx = GNUNET_SCHEDULER_cancel (timeout_task);
185 timeout_task = NULL;
186 }
187 if (NULL != put_task)
188 {
189 GNUNET_SCHEDULER_cancel (put_task);
190 put_task = NULL;
191 }
192 if (NULL != get_task)
193 {
194 GNUNET_SCHEDULER_cancel (get_task);
195 get_task = NULL;
196 }
197 while (NULL != (get_op = get_tail))
198 {
199 if (NULL != get_op->to)
200 {
201 GNUNET_TESTBED_operation_done (get_op->to);
202 get_op->to = NULL;
203 }
204 if (NULL != get_op->get)
205 {
206 GNUNET_DHT_get_stop (get_op->get);
207 get_op->get = NULL;
208 }
209 GNUNET_CONTAINER_DLL_remove (get_head,
210 get_tail,
211 get_op);
212 GNUNET_free (get_op);
213 }
214 return ctx;
215}
216
217
218/**
219 * Function called once we're done processing stats.
220 *
221 * @param cls the test context
222 * @param op the stats operation
223 * @param emsg error message on failure
224 */
225static void
226stats_finished (void *cls,
227 struct GNUNET_TESTBED_Operation *op,
228 const char *emsg)
229{
230 struct GNUNET_DHT_TEST_Context *ctx = cls;
231
232 if (NULL != op)
233 GNUNET_TESTBED_operation_done (op);
234 if (NULL != emsg)
235 {
236 fprintf (stderr,
237 _ ("Gathering statistics failed: %s\n"),
238 emsg);
239 GNUNET_SCHEDULER_cancel (put_task);
240 GNUNET_DHT_TEST_cleanup (ctx);
241 return;
242 }
243 for (unsigned int i = 0; NULL != stats[i].name; i++)
244 fprintf (stderr,
245 "%6s/%60s = %12llu\n",
246 stats[i].subsystem,
247 stats[i].name,
248 stats[i].total);
249 GNUNET_DHT_TEST_cleanup (ctx);
250 GNUNET_SCHEDULER_shutdown ();
251}
252
253
254/**
255 * Function called to process statistic values from all peers.
256 *
257 * @param cls closure
258 * @param peer the peer the statistic belong to
259 * @param subsystem name of subsystem that created the statistic
260 * @param name the name of the datum
261 * @param value the current value
262 * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
263 * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
264 */
265static enum GNUNET_GenericReturnValue
266handle_stats (void *cls,
267 const struct GNUNET_TESTBED_Peer *peer,
268 const char *subsystem,
269 const char *name,
270 uint64_t value,
271 int is_persistent)
272{
273 for (unsigned int i = 0; NULL != stats[i].name; i++)
274 if ((0 == strcasecmp (subsystem,
275 stats[i].subsystem)) &&
276 (0 == strcasecmp (name,
277 stats[i].name)))
278 stats[i].total += value;
279 return GNUNET_OK;
280}
281
282
283/**
284 * Task run on shutdown to clean up. Terminates active get operations
285 * and shuts down the testbed.
286 *
287 * @param cls the 'struct GNUNET_DHT_TestContext'
288 */
289static void
290shutdown_task (void *cls)
291{
292 struct GNUNET_DHT_TEST_Context *ctx;
293
294 (void) cls;
295 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
296 "Performing shutdown\n");
297 ctx = stop_ops ();
298 if (NULL != ctx)
299 GNUNET_DHT_TEST_cleanup (ctx);
300}
301
302
303/**
304 * Task run on timeout to clean up. Terminates active get operations
305 * and shuts down the testbed.
306 *
307 * @param cls the `struct GNUNET_DHT_TestContext`
308 */
309static void
310timeout_cb (void *cls)
311{
312 struct GNUNET_DHT_TEST_Context *ctx = cls;
313
314 timeout_task = GNUNET_SCHEDULER_add_delayed (GET_TIMEOUT,
315 &timeout_cb,
316 ctx);
317 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
318 "Timeout\n");
319 GNUNET_SCHEDULER_shutdown ();
320}
321
322
323/**
324 * Iterator called on each result obtained for a DHT
325 * operation that expects a reply
326 *
327 * @param cls closure with our 'struct GetOperation'
328 * @param exp when will this value expire
329 * @param query query hash
330 * @param trunc_peer peer the path was truncated at, or NULL
331 * @param get_path peers on reply path (or NULL if not recorded)
332 * @param get_path_length number of entries in @a get_path
333 * @param put_path peers on the PUT path (or NULL if not recorded)
334 * @param put_path_length number of entries in @a put_path
335 * @param type type of the result
336 * @param size number of bytes in @a data
337 * @param data pointer to the result data
338 */
339static void
340dht_get_handler (void *cls,
341 struct GNUNET_TIME_Absolute exp,
342 const struct GNUNET_HashCode *query,
343 const struct GNUNET_PeerIdentity *trunc_peer,
344 const struct GNUNET_DHT_PathElement *get_path,
345 unsigned int get_path_length,
346 const struct GNUNET_DHT_PathElement *put_path,
347 unsigned int put_path_length,
348 enum GNUNET_BLOCK_Type type,
349 size_t size,
350 const void *data)
351{
352 struct GetOperation *get_op = cls;
353 struct GNUNET_HashCode want;
354 struct GNUNET_DHT_TEST_Context *ctx;
355
356 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
357 "GET HANDLER called on PID %s\n",
358 GNUNET_i2s (&get_op->me));
359 if (sizeof(struct GNUNET_HashCode) != size)
360 {
361 GNUNET_break (0);
362 return;
363 }
364 if (0 != GNUNET_memcmp (query,
365 &get_op->key))
366 {
367 /* exact search should only yield exact results */
368 GNUNET_break (0);
369 return;
370 }
371 GNUNET_CRYPTO_hash (query,
372 sizeof(*query),
373 &want);
374 if (0 != memcmp (&want,
375 data,
376 sizeof(want)))
377 {
378 GNUNET_break (0);
379 return;
380 }
381 if (0 !=
382 GNUNET_DHT_verify_path (data,
383 size,
384 exp,
385 trunc_peer,
386 put_path,
387 put_path_length,
388 get_path,
389 get_path_length,
390 &get_op->me))
391 {
392 GNUNET_break (0);
393 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
394 "Path signature (%u/%u) verification failed for peer %s!\n",
395 get_path_length,
396 put_path_length,
397 GNUNET_i2s (&get_op->me));
398 }
399 else
400 {
401 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
402 "Get successful\n");
403 ok--;
404 }
405 GNUNET_DHT_get_stop (get_op->get);
406 GNUNET_CONTAINER_DLL_remove (get_head,
407 get_tail,
408 get_op);
409 GNUNET_free (get_op);
410 if (NULL != get_head)
411 return;
412 /* all DHT GET operations successful; get stats! */
413 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
414 "All DHT operations successful. Obtaining stats!\n");
415 ctx = stop_ops ();
416 GNUNET_assert (NULL != ctx);
417 (void) GNUNET_TESTBED_get_statistics (NUM_PEERS,
418 my_peers,
419 NULL, NULL,
420 &handle_stats,
421 &stats_finished,
422 ctx);
423}
424
425
426/**
427 * Task to put the id of each peer into the DHT.
428 *
429 * @param cls array with NUM_PEERS DHT handles
430 * @param tc Task context
431 */
432static void
433do_puts (void *cls)
434{
435 struct GNUNET_DHT_Handle **hs = cls;
436 struct GNUNET_HashCode key;
437 struct GNUNET_HashCode value;
438
439 put_task = NULL;
440 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
441 "Putting %u values into DHT\n",
442 NUM_PEERS);
443 for (unsigned int i = 0; i < NUM_PEERS; i++)
444 {
445 GNUNET_CRYPTO_hash (&i,
446 sizeof(i),
447 &key);
448 GNUNET_CRYPTO_hash (&key,
449 sizeof(key),
450 &value);
451 GNUNET_DHT_put (hs[i],
452 &key,
453 10U,
454 GNUNET_DHT_RO_RECORD_ROUTE
455 | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
456 GNUNET_BLOCK_TYPE_TEST,
457 sizeof(value),
458 &value,
459 GNUNET_TIME_UNIT_FOREVER_ABS,
460 NULL,
461 NULL);
462 }
463 put_task = GNUNET_SCHEDULER_add_delayed (PUT_FREQUENCY,
464 &do_puts,
465 hs);
466}
467
468
469/**
470 * Callback to be called when the requested peer information is available
471 * The peer information in the callback is valid until the operation 'op' is canceled.
472 *
473 * @param cls a `struct GetOperation *`
474 * @param op the operation this callback corresponds to
475 * @param pinfo the result; will be NULL if the operation has failed
476 * @param emsg error message if the operation has failed; will be NULL if the
477 * operation is successful
478 */
479static void
480pid_cb (void *cls,
481 struct GNUNET_TESTBED_Operation *op,
482 const struct GNUNET_TESTBED_PeerInformation *pinfo,
483 const char *emsg)
484{
485 struct GetOperation *get_op = cls;
486
487 if (NULL != emsg)
488 {
489 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
490 "Testbed failure: %s\n",
491 emsg);
492 GNUNET_TESTBED_operation_done (get_op->to);
493 get_op->to = NULL;
494 GNUNET_SCHEDULER_shutdown ();
495 return;
496 }
497 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
498 "Testbed provided PID %s\n",
499 GNUNET_i2s (pinfo->result.id));
500 get_op->me = *pinfo->result.id;
501 GNUNET_TESTBED_operation_done (get_op->to);
502 get_op->to = NULL;
503 get_op->get = GNUNET_DHT_get_start (get_op->dht,
504 GNUNET_BLOCK_TYPE_TEST,
505 &get_op->key,
506 4U, /* replication level */
507 GNUNET_DHT_RO_RECORD_ROUTE
508 | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
509 NULL, /* xquery */
510 0, /* xquery bits */
511 &dht_get_handler,
512 get_op);
513}
514
515
516/**
517 * Start GET operations.
518 */
519static void
520start_get (void *cls)
521{
522 struct GNUNET_DHT_Handle **dhts = cls;
523
524 get_task = NULL;
525 for (unsigned int i = 0; i < NUM_PEERS; i++)
526 {
527 struct GNUNET_HashCode key;
528
529 GNUNET_CRYPTO_hash (&i,
530 sizeof(i),
531 &key);
532 for (unsigned int j = 0; j < NUM_PEERS; j++)
533 {
534 struct GetOperation *get_op;
535
536 get_op = GNUNET_new (struct GetOperation);
537 ok++;
538 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
539 "Starting GET %p\n",
540 get_op);
541 get_op->key = key;
542 get_op->dht = dhts[j];
543 get_op->to = GNUNET_TESTBED_peer_get_information (my_peers[j],
544 GNUNET_TESTBED_PIT_IDENTITY,
545 &pid_cb,
546 get_op);
547 GNUNET_CONTAINER_DLL_insert (get_head,
548 get_tail,
549 get_op);
550 }
551 }
552}
553
554
555/**
556 * Main function of the test.
557 *
558 * @param cls closure (NULL)
559 * @param ctx argument to give to #GNUNET_DHT_TEST_cleanup on test end
560 * @param num_peers number of @a peers that are running
561 * @param peers array of peers
562 * @param dhts handle to each of the DHTs of the peers
563 */
564static void
565run (void *cls,
566 struct GNUNET_DHT_TEST_Context *ctx,
567 unsigned int num_peers,
568 struct GNUNET_TESTBED_Peer **peers,
569 struct GNUNET_DHT_Handle **dhts)
570{
571 GNUNET_assert (NUM_PEERS == num_peers);
572 my_peers = peers;
573 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
574 "Peers setup, starting test\n");
575 put_task = GNUNET_SCHEDULER_add_now (&do_puts,
576 dhts);
577 get_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
578 &start_get,
579 dhts);
580 timeout_task = GNUNET_SCHEDULER_add_delayed (GET_TIMEOUT,
581 &timeout_cb,
582 ctx);
583 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
584 ctx);
585}
586
587
588/**
589 * Main: start test
590 */
591int
592main (int xargc, char *xargv[])
593{
594 const char *cfg_filename;
595 const char *test_name;
596
597 unsetenv ("XDG_DATA_HOME");
598 unsetenv ("XDG_CONFIG_HOME");
599 unsetenv ("XDG_CACHE_HOME");
600 if (NULL != strstr (xargv[0], "test_dht_2dtorus"))
601 {
602 cfg_filename = "test_dht_2dtorus.conf";
603 test_name = "test-dht-2dtorus";
604 NUM_PEERS = 16;
605 }
606 else if (NULL != strstr (xargv[0], "test_dht_line"))
607 {
608 cfg_filename = "test_dht_line.conf";
609 test_name = "test-dht-line";
610 NUM_PEERS = 5;
611 }
612 else if (NULL != strstr (xargv[0], "test_dht_twopeer"))
613 {
614 cfg_filename = "test_dht_line.conf";
615 test_name = "test-dht-twopeer";
616 NUM_PEERS = 2;
617 }
618 else if (NULL != strstr (xargv[0], "test_dht_multipeer"))
619 {
620 cfg_filename = "test_dht_multipeer.conf";
621 test_name = "test-dht-multipeer";
622 NUM_PEERS = 10;
623 }
624 else
625 {
626 GNUNET_break (0);
627 return 1;
628 }
629 GNUNET_DHT_TEST_run (test_name,
630 cfg_filename,
631 NUM_PEERS,
632 &run, NULL);
633 return ok;
634}
635
636
637/* end of test_dht_topo.c */