aboutsummaryrefslogtreecommitdiff
path: root/src/service/dht
diff options
context:
space:
mode:
Diffstat (limited to 'src/service/dht')
-rw-r--r--src/service/dht/.gitignore8
-rw-r--r--src/service/dht/Makefile.am77
-rw-r--r--src/service/dht/dht.conf.in35
-rw-r--r--src/service/dht/dht.h427
-rw-r--r--src/service/dht/dht_api.c1501
-rw-r--r--src/service/dht/dhtu.conf7
-rwxr-xr-xsrc/service/dht/dhtu_testbed_connect.sh35
-rw-r--r--src/service/dht/dhtu_testbed_deploy.conf26
-rwxr-xr-xsrc/service/dht/dhtu_testbed_deploy.sh95
-rw-r--r--src/service/dht/gnunet-service-dht.c562
-rw-r--r--src/service/dht/gnunet-service-dht.h212
-rw-r--r--src/service/dht/gnunet-service-dht_clients.c1705
-rw-r--r--src/service/dht/gnunet-service-dht_datacache.c291
-rw-r--r--src/service/dht/gnunet-service-dht_datacache.h115
-rw-r--r--src/service/dht/gnunet-service-dht_neighbours.c3009
-rw-r--r--src/service/dht/gnunet-service-dht_neighbours.h226
-rw-r--r--src/service/dht/gnunet-service-dht_routing.c429
-rw-r--r--src/service/dht/gnunet-service-dht_routing.h87
-rw-r--r--src/service/dht/gnunet_dht_profiler.c1032
-rw-r--r--src/service/dht/meson.build97
-rw-r--r--src/service/dht/plugin_dhtu_gnunet.c627
-rw-r--r--src/service/dht/plugin_dhtu_gnunet.h44
-rw-r--r--src/service/dht/plugin_dhtu_ip.c1170
-rw-r--r--src/service/dht/plugin_dhtu_ip.h44
-rw-r--r--src/service/dht/test_dht_2dtorus.conf37
-rw-r--r--src/service/dht/test_dht_api.c193
-rw-r--r--src/service/dht/test_dht_api_data.conf44
-rw-r--r--src/service/dht/test_dht_api_peer1.conf41
-rw-r--r--src/service/dht/test_dht_line.conf38
-rw-r--r--src/service/dht/test_dht_multipeer.conf40
-rw-r--r--src/service/dht/test_dht_multipeer_topology.dat11
-rw-r--r--src/service/dht/test_dht_tools.conf157
-rw-r--r--src/service/dht/test_dht_tools.py.in149
-rwxr-xr-xsrc/service/dht/test_dht_tools.sh65
-rw-r--r--src/service/dht/test_dhtu_ip.c45
-rw-r--r--src/service/dht/testing_dhtu_cmd_send.c115
36 files changed, 12796 insertions, 0 deletions
diff --git a/src/service/dht/.gitignore b/src/service/dht/.gitignore
new file mode 100644
index 000000000..939cf3f5f
--- /dev/null
+++ b/src/service/dht/.gitignore
@@ -0,0 +1,8 @@
1gnunet-service-dht
2test_dht_2dtorus
3test_dht_api
4test_dht_line
5test_dht_monitor
6test_dht_multipeer
7test_dht_tools.py
8test_dht_twopeer
diff --git a/src/service/dht/Makefile.am b/src/service/dht/Makefile.am
new file mode 100644
index 000000000..e16139650
--- /dev/null
+++ b/src/service/dht/Makefile.am
@@ -0,0 +1,77 @@
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
13dist_pkgcfg_DATA = \
14 dhtu.conf
15
16if USE_COVERAGE
17 AM_CFLAGS = --coverage -O0
18 XLIB = -lgcov
19endif
20
21lib_LTLIBRARIES = \
22 libgnunetdht.la
23
24libgnunetdht_la_SOURCES = \
25 dht_api.c dht.h
26libgnunetdht_la_LIBADD = \
27 $(top_builddir)/src/lib/util/libgnunetutil.la \
28 $(XLIB) \
29 $(LTLIBINTL)
30libgnunetdht_la_LDFLAGS = \
31 $(GN_LIB_LDFLAGS) \
32 -version-info 4:0:0
33
34
35libexec_PROGRAMS = \
36 gnunet-service-dht
37
38gnunet_service_dht_SOURCES = \
39 gnunet-service-dht.c gnunet-service-dht.h \
40 plugin_dhtu_gnunet.h plugin_dhtu_ip.h \
41 plugin_dhtu_gnunet.c plugin_dhtu_ip.c \
42 gnunet-service-dht_datacache.c gnunet-service-dht_datacache.h \
43 gnunet-service-dht_neighbours.c gnunet-service-dht_neighbours.h \
44 gnunet-service-dht_routing.c gnunet-service-dht_routing.h
45gnunet_service_dht_LDADD = \
46 libgnunetdht.la \
47 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
48 $(top_builddir)/src/service/core/libgnunetcore.la \
49 $(top_builddir)/src/service/nse/libgnunetnse.la \
50 $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \
51 $(top_builddir)/src/lib/hello/libgnunethello.la \
52 $(top_builddir)/src/lib/block/libgnunetblock.la \
53 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \
54 $(top_builddir)/src/service/transport/libgnunettransportapplication.la \
55 $(top_builddir)/src/service/datacache/libgnunetdatacache.la \
56 $(top_builddir)/src/lib/util/libgnunetutil.la \
57 -lm
58gnunet_service_dht_LDFLAGS = \
59 $(GN_LIBINTL)
60
61if ENABLE_TEST_RUN
62AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
63endif
64
65test_dht_api_SOURCES = \
66 test_dht_api.c
67test_dht_api_LDADD = \
68 $(top_builddir)/src/lib/util/libgnunetutil.la \
69 $(top_builddir)/src/service/testing/libgnunettesting.la \
70 $(top_builddir)/src/lib/hello/libgnunethello.la \
71 libgnunetdht.la
72
73EXTRA_DIST = \
74 gnunet-service-dht_clients.c \
75 dhtu_testbed_connect.sh \
76 dhtu_testbed_deploy.conf \
77 dhtu_testbed_deploy.sh
diff --git a/src/service/dht/dht.conf.in b/src/service/dht/dht.conf.in
new file mode 100644
index 000000000..405ef1917
--- /dev/null
+++ b/src/service/dht/dht.conf.in
@@ -0,0 +1,35 @@
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/service/dht/dht.h b/src/service/dht/dht.h
new file mode 100644
index 000000000..6a137defe
--- /dev/null
+++ b/src/service/dht/dht.h
@@ -0,0 +1,427 @@
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/service/dht/dht_api.c b/src/service/dht/dht_api.c
new file mode 100644
index 000000000..6a218e29d
--- /dev/null
+++ b/src/service/dht/dht_api.c
@@ -0,0 +1,1501 @@
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_constants.h"
30#include "gnunet_signatures.h"
31#include "gnunet_arm_service.h"
32#include "gnunet_protocols.h"
33#include "gnunet_dht_service.h"
34#include "dht.h"
35
36#define LOG(kind, ...) GNUNET_log_from (kind, "dht-api", __VA_ARGS__)
37
38
39/**
40 * Handle to a PUT request.
41 */
42struct GNUNET_DHT_PutHandle
43{
44 /**
45 * Kept in a DLL.
46 */
47 struct GNUNET_DHT_PutHandle *next;
48
49 /**
50 * Kept in a DLL.
51 */
52 struct GNUNET_DHT_PutHandle *prev;
53
54 /**
55 * Continuation to call when done.
56 */
57 GNUNET_SCHEDULER_TaskCallback cont;
58
59 /**
60 * Main handle to this DHT api
61 */
62 struct GNUNET_DHT_Handle *dht_handle;
63
64 /**
65 * Closure for @e cont.
66 */
67 void *cont_cls;
68
69 /**
70 * Envelope from the PUT operation.
71 */
72 struct GNUNET_MQ_Envelope *env;
73};
74
75/**
76 * Handle to a GET request
77 */
78struct GNUNET_DHT_GetHandle
79{
80 /**
81 * Iterator to call on data receipt
82 */
83 GNUNET_DHT_GetIterator iter;
84
85 /**
86 * Closure for @e iter.
87 */
88 void *iter_cls;
89
90 /**
91 * Main handle to this DHT api
92 */
93 struct GNUNET_DHT_Handle *dht_handle;
94
95 /**
96 * Array of hash codes over the results that we have already
97 * seen.
98 */
99 struct GNUNET_HashCode *seen_results;
100
101 /**
102 * Key that this get request is for
103 */
104 struct GNUNET_HashCode key;
105
106 /**
107 * Unique identifier for this request (for key collisions).
108 */
109 uint64_t unique_id;
110
111 /**
112 * Size of the extended query, allocated at the end of this struct.
113 */
114 size_t xquery_size;
115
116 /**
117 * Desired replication level.
118 */
119 uint32_t desired_replication_level;
120
121 /**
122 * Type of the block we are looking for.
123 */
124 enum GNUNET_BLOCK_Type type;
125
126 /**
127 * Routing options.
128 */
129 enum GNUNET_DHT_RouteOption options;
130
131 /**
132 * Size of the @e seen_results array. Note that not
133 * all positions might be used (as we over-allocate).
134 */
135 unsigned int seen_results_size;
136
137 /**
138 * Offset into the @e seen_results array marking the
139 * end of the positions that are actually used.
140 */
141 unsigned int seen_results_end;
142};
143
144
145/**
146 * Handle to a monitoring request.
147 */
148struct GNUNET_DHT_MonitorHandle
149{
150 /**
151 * DLL.
152 */
153 struct GNUNET_DHT_MonitorHandle *next;
154
155 /**
156 * DLL.
157 */
158 struct GNUNET_DHT_MonitorHandle *prev;
159
160 /**
161 * Main handle to this DHT api.
162 */
163 struct GNUNET_DHT_Handle *dht_handle;
164
165 /**
166 * Type of block looked for.
167 */
168 enum GNUNET_BLOCK_Type type;
169
170 /**
171 * Key being looked for, NULL == all.
172 */
173 struct GNUNET_HashCode *key;
174
175 /**
176 * Callback for each received message of type get.
177 */
178 GNUNET_DHT_MonitorGetCB get_cb;
179
180 /**
181 * Callback for each received message of type get response.
182 */
183 GNUNET_DHT_MonitorGetRespCB get_resp_cb;
184
185 /**
186 * Callback for each received message of type put.
187 */
188 GNUNET_DHT_MonitorPutCB put_cb;
189
190 /**
191 * Closure for @e get_cb, @e put_cb and @e get_resp_cb.
192 */
193 void *cb_cls;
194};
195
196
197/**
198 * Handle to get a HELLO URL from the DHT for manual bootstrapping.
199 */
200struct GNUNET_DHT_HelloGetHandle
201{
202
203 /**
204 * DLL.
205 */
206 struct GNUNET_DHT_HelloGetHandle *next;
207
208 /**
209 * DLL.
210 */
211 struct GNUNET_DHT_HelloGetHandle *prev;
212
213 /**
214 * Function to call with the result.
215 */
216 GNUNET_DHT_HelloGetCallback cb;
217
218 /**
219 * Closure for @a cb.
220 */
221 void *cb_cls;
222
223 /**
224 * Connection to the DHT service.
225 */
226 struct GNUNET_DHT_Handle *dht_handle;
227
228};
229
230
231/**
232 * Connection to the DHT service.
233 */
234struct GNUNET_DHT_Handle
235{
236 /**
237 * Configuration to use.
238 */
239 const struct GNUNET_CONFIGURATION_Handle *cfg;
240
241 /**
242 * Connection to DHT service.
243 */
244 struct GNUNET_MQ_Handle *mq;
245
246 /**
247 * Head of linked list of messages we would like to monitor.
248 */
249 struct GNUNET_DHT_MonitorHandle *monitor_head;
250
251 /**
252 * Tail of linked list of messages we would like to monitor.
253 */
254 struct GNUNET_DHT_MonitorHandle *monitor_tail;
255
256 /**
257 * Head of active PUT requests.
258 */
259 struct GNUNET_DHT_PutHandle *put_head;
260
261 /**
262 * Tail of active PUT requests.
263 */
264 struct GNUNET_DHT_PutHandle *put_tail;
265
266 /**
267 * DLL.
268 */
269 struct GNUNET_DHT_HelloGetHandle *hgh_head;
270
271 /**
272 * DLL.
273 */
274 struct GNUNET_DHT_HelloGetHandle *hgh_tail;
275
276 /**
277 * Hash map containing the current outstanding unique GET requests
278 * (values are of type `struct GNUNET_DHT_GetHandle`).
279 */
280 struct GNUNET_CONTAINER_MultiHashMap *active_requests;
281
282 /**
283 * Task for trying to reconnect.
284 */
285 struct GNUNET_SCHEDULER_Task *reconnect_task;
286
287 /**
288 * How quickly should we retry? Used for exponential back-off on
289 * connect-errors.
290 */
291 struct GNUNET_TIME_Relative retry_time;
292
293 /**
294 * Generator for unique ids.
295 */
296 uint64_t uid_gen;
297};
298
299
300/**
301 * Try to (re)connect to the DHT service.
302 *
303 * @param h DHT handle to reconnect
304 * @return #GNUNET_YES on success, #GNUNET_NO on failure.
305 */
306static enum GNUNET_GenericReturnValue
307try_connect (struct GNUNET_DHT_Handle *h);
308
309
310/**
311 * Send GET message for a @a get_handle to DHT.
312 *
313 * @param gh GET to generate messages for.
314 */
315static void
316send_get (struct GNUNET_DHT_GetHandle *gh)
317{
318 struct GNUNET_DHT_Handle *h = gh->dht_handle;
319 struct GNUNET_MQ_Envelope *env;
320 struct GNUNET_DHT_ClientGetMessage *get_msg;
321
322 env = GNUNET_MQ_msg_extra (get_msg,
323 gh->xquery_size,
324 GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET);
325 get_msg->options = htonl ((uint32_t) gh->options);
326 get_msg->desired_replication_level = htonl (gh->desired_replication_level);
327 get_msg->type = htonl (gh->type);
328 get_msg->key = gh->key;
329 get_msg->unique_id = gh->unique_id;
330 GNUNET_memcpy (&get_msg[1],
331 &gh[1],
332 gh->xquery_size);
333 GNUNET_MQ_send (h->mq,
334 env);
335}
336
337
338/**
339 * Send GET message(s) for indicating which results are already known
340 * for a @a get_handle to DHT. Complex as we need to send the list of
341 * known results, which means we may need multiple messages to block
342 * known results from the result set.
343 *
344 * @param gh GET to generate messages for
345 * @param transmission_offset_start at which offset should we start?
346 */
347static void
348send_get_known_results (struct GNUNET_DHT_GetHandle *gh,
349 unsigned int transmission_offset_start)
350{
351 struct GNUNET_DHT_Handle *h = gh->dht_handle;
352 struct GNUNET_MQ_Envelope *env;
353 struct GNUNET_DHT_ClientGetResultSeenMessage *msg;
354 unsigned int delta;
355 unsigned int max;
356 unsigned int transmission_offset;
357
358 max = (GNUNET_MAX_MESSAGE_SIZE - sizeof(*msg))
359 / sizeof(struct GNUNET_HashCode);
360 transmission_offset = transmission_offset_start;
361 while (transmission_offset < gh->seen_results_end)
362 {
363 delta = gh->seen_results_end - transmission_offset;
364 if (delta > max)
365 delta = max;
366 env = GNUNET_MQ_msg_extra (msg,
367 delta * sizeof(struct GNUNET_HashCode),
368 GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET_RESULTS_KNOWN);
369 msg->key = gh->key;
370 msg->unique_id = gh->unique_id;
371 GNUNET_memcpy (&msg[1],
372 &gh->seen_results[transmission_offset],
373 sizeof(struct GNUNET_HashCode) * delta);
374 GNUNET_MQ_send (h->mq,
375 env);
376 transmission_offset += delta;
377 }
378}
379
380
381/**
382 * Add the GET request corresponding to the given route handle
383 * to the pending queue (if it is not already in there).
384 *
385 * @param cls the `struct GNUNET_DHT_Handle *`
386 * @param key key for the request (not used)
387 * @param value the `struct GNUNET_DHT_GetHandle *`
388 * @return #GNUNET_YES (always)
389 */
390static enum GNUNET_GenericReturnValue
391add_get_request_to_pending (void *cls,
392 const struct GNUNET_HashCode *key,
393 void *value)
394{
395 struct GNUNET_DHT_Handle *handle = cls;
396 struct GNUNET_DHT_GetHandle *gh = value;
397
398 LOG (GNUNET_ERROR_TYPE_DEBUG,
399 "Retransmitting request related to %s to DHT %p\n",
400 GNUNET_h2s (key),
401 handle);
402 send_get (gh);
403 send_get_known_results (gh, 0);
404 return GNUNET_YES;
405}
406
407
408/**
409 * Send #GNUNET_MESSAGE_TYPE_DHT_MONITOR_START message.
410 *
411 * @param mh monitor handle to generate start message for
412 */
413static void
414send_monitor_start (struct GNUNET_DHT_MonitorHandle *mh)
415{
416 struct GNUNET_DHT_Handle *h = mh->dht_handle;
417 struct GNUNET_MQ_Envelope *env;
418 struct GNUNET_DHT_MonitorStartStopMessage *m;
419
420 env = GNUNET_MQ_msg (m,
421 GNUNET_MESSAGE_TYPE_DHT_MONITOR_START);
422 m->type = htonl (mh->type);
423 m->get = htons (NULL != mh->get_cb);
424 m->get_resp = htons (NULL != mh->get_resp_cb);
425 m->put = htons (NULL != mh->put_cb);
426 if (NULL != mh->key)
427 {
428 m->filter_key = htons (1);
429 m->key = *mh->key;
430 }
431 GNUNET_MQ_send (h->mq,
432 env);
433}
434
435
436/**
437 * Try reconnecting to the dht service.
438 *
439 * @param cls a `struct GNUNET_DHT_Handle`
440 */
441static void
442try_reconnect (void *cls)
443{
444 struct GNUNET_DHT_Handle *h = cls;
445 struct GNUNET_DHT_MonitorHandle *mh;
446
447 LOG (GNUNET_ERROR_TYPE_DEBUG,
448 "Reconnecting with DHT %p\n",
449 h);
450 h->retry_time = GNUNET_TIME_STD_BACKOFF (h->retry_time);
451 h->reconnect_task = NULL;
452 if (GNUNET_YES != try_connect (h))
453 {
454 LOG (GNUNET_ERROR_TYPE_WARNING,
455 "DHT reconnect failed!\n");
456 h->reconnect_task
457 = GNUNET_SCHEDULER_add_delayed (h->retry_time,
458 &try_reconnect,
459 h);
460 return;
461 }
462 GNUNET_CONTAINER_multihashmap_iterate (h->active_requests,
463 &add_get_request_to_pending,
464 h);
465 for (mh = h->monitor_head; NULL != mh; mh = mh->next)
466 send_monitor_start (mh);
467}
468
469
470/**
471 * Try reconnecting to the DHT service.
472 *
473 * @param h handle to dht to (possibly) disconnect and reconnect
474 */
475static void
476do_disconnect (struct GNUNET_DHT_Handle *h)
477{
478 struct GNUNET_DHT_PutHandle *ph;
479 GNUNET_SCHEDULER_TaskCallback cont;
480 void *cont_cls;
481
482 if (NULL == h->mq)
483 return;
484 GNUNET_MQ_destroy (h->mq);
485 h->mq = NULL;
486 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
487 "Disconnecting from DHT service, will try to reconnect in %s\n",
488 GNUNET_STRINGS_relative_time_to_string (h->retry_time,
489 GNUNET_YES));
490 /* notify client about all PUTs that (may) have failed due to disconnect */
491 while (NULL != (ph = h->put_head))
492 {
493 cont = ph->cont;
494 cont_cls = ph->cont_cls;
495 ph->env = NULL;
496 GNUNET_DHT_put_cancel (ph);
497 if (NULL != cont)
498 cont (cont_cls);
499 }
500 GNUNET_assert (NULL == h->reconnect_task);
501 h->reconnect_task
502 = GNUNET_SCHEDULER_add_delayed (h->retry_time,
503 &try_reconnect,
504 h);
505}
506
507
508/**
509 * Generic error handler, called with the appropriate error code and
510 * the same closure specified at the creation of the message queue.
511 * Not every message queue implementation supports an error handler.
512 *
513 * @param cls closure with the `struct GNUNET_DHT_Handle *`
514 * @param error error code
515 */
516static void
517mq_error_handler (void *cls,
518 enum GNUNET_MQ_Error error)
519{
520 struct GNUNET_DHT_Handle *h = cls;
521
522 do_disconnect (h);
523}
524
525
526/**
527 * Process a get monitor message from the service.
528 *
529 * @param cls The DHT handle.
530 * @param msg Monitor get message from the service.
531 */
532static void
533handle_monitor_get (void *cls,
534 const struct GNUNET_DHT_MonitorGetMessage *msg)
535{
536 struct GNUNET_DHT_Handle *handle = cls;
537 enum GNUNET_DHT_RouteOption ro
538 = (enum GNUNET_DHT_RouteOption) ntohl (msg->options);
539
540 for (struct GNUNET_DHT_MonitorHandle *mh = handle->monitor_head;
541 NULL != mh;
542 mh = mh->next)
543 {
544 if (NULL == mh->get_cb)
545 continue;
546 if ( (GNUNET_BLOCK_TYPE_ANY != mh->type) &&
547 (mh->type != ntohl (msg->type)))
548 continue;
549 if ( (NULL != mh->key) &&
550 (0 != GNUNET_memcmp (mh->key,
551 &msg->key)) )
552 continue;
553 mh->get_cb (mh->cb_cls,
554 ro,
555 (enum GNUNET_BLOCK_Type) ntohl (msg->type),
556 ntohl (msg->hop_count),
557 ntohl (msg->desired_replication_level),
558 &msg->key);
559 }
560}
561
562
563/**
564 * Validate a get response monitor message from the service.
565 *
566 * @param cls The DHT handle.
567 * @param msg monitor get response message from the service
568 * @return #GNUNET_OK if everything went fine,
569 * #GNUNET_SYSERR if the message is malformed.
570 */
571static enum GNUNET_GenericReturnValue
572check_monitor_get_resp (void *cls,
573 const struct GNUNET_DHT_MonitorGetRespMessage *msg)
574{
575 size_t msize = ntohs (msg->header.size) - sizeof(*msg);
576 uint32_t getl = ntohl (msg->get_path_length);
577 uint32_t putl = ntohl (msg->put_path_length);
578 enum GNUNET_DHT_RouteOption ro
579 = (enum GNUNET_DHT_RouteOption) ntohl (msg->options);
580 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
581
582 if (truncated)
583 {
584 if (msize < sizeof (struct GNUNET_PeerIdentity))
585 {
586 GNUNET_break (0);
587 return GNUNET_SYSERR;
588 }
589 msize -= sizeof (struct GNUNET_PeerIdentity);
590 }
591 if ((getl + putl < getl) ||
592 ((msize / sizeof(struct GNUNET_DHT_PathElement)) < getl + putl))
593 {
594 GNUNET_break (0);
595 return GNUNET_SYSERR;
596 }
597 return GNUNET_OK;
598}
599
600
601/**
602 * Process a get response monitor message from the service.
603 *
604 * @param cls The DHT handle.
605 * @param msg monitor get response message from the service
606 */
607static void
608handle_monitor_get_resp (void *cls,
609 const struct GNUNET_DHT_MonitorGetRespMessage *msg)
610{
611 struct GNUNET_DHT_Handle *handle = cls;
612 size_t msize = ntohs (msg->header.size) - sizeof(*msg);
613 enum GNUNET_DHT_RouteOption ro
614 = (enum GNUNET_DHT_RouteOption) ntohl (msg->options);
615 uint32_t getl = ntohl (msg->get_path_length);
616 uint32_t putl = ntohl (msg->put_path_length);
617 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
618 const struct GNUNET_PeerIdentity *trunc_peer
619 = truncated
620 ? (const struct GNUNET_PeerIdentity *) &msg[1]
621 : NULL;
622 const struct GNUNET_DHT_PathElement *path
623 = truncated
624 ? (const struct GNUNET_DHT_PathElement *) &trunc_peer[1]
625 : (const struct GNUNET_DHT_PathElement *) &msg[1];
626
627 if (truncated)
628 msize -= sizeof (struct GNUNET_PeerIdentity);
629 msize -= sizeof(struct GNUNET_DHT_PathElement) * (putl + getl);
630 for (struct GNUNET_DHT_MonitorHandle *mh = handle->monitor_head;
631 NULL != mh;
632 mh = mh->next)
633 {
634 if (NULL == mh->get_resp_cb)
635 continue;
636 if ( (GNUNET_BLOCK_TYPE_ANY != mh->type) &&
637 (mh->type != ntohl (msg->type)) )
638 continue;
639 if ( (NULL != mh->key) &&
640 (0 != GNUNET_memcmp (mh->key,
641 &msg->key)) )
642 continue;
643 mh->get_resp_cb (mh->cb_cls,
644 (enum GNUNET_BLOCK_Type) ntohl (msg->type),
645 trunc_peer,
646 &path[putl],
647 getl,
648 path,
649 putl,
650 GNUNET_TIME_absolute_ntoh (msg->expiration_time),
651 &msg->key,
652 (const void *) &path[getl + putl],
653 msize);
654 }
655}
656
657
658/**
659 * Check validity of a put monitor message from the service.
660 *
661 * @param cls The DHT handle.
662 * @param msg Monitor put message from the service.
663 * @return #GNUNET_OK if everything went fine,
664 * #GNUNET_SYSERR if the message is malformed.
665 */
666static enum GNUNET_GenericReturnValue
667check_monitor_put (void *cls,
668 const struct GNUNET_DHT_MonitorPutMessage *msg)
669{
670 size_t msize = ntohs (msg->header.size) - sizeof(*msg);
671 uint32_t putl = ntohl (msg->put_path_length);
672 enum GNUNET_DHT_RouteOption ro
673 = (enum GNUNET_DHT_RouteOption) ntohl (msg->options);
674 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
675
676 if (truncated)
677 {
678 if (msize < sizeof (struct GNUNET_PeerIdentity))
679 {
680 GNUNET_break (0);
681 return GNUNET_SYSERR;
682 }
683 msize -= sizeof (struct GNUNET_PeerIdentity);
684 }
685 if ((msize / sizeof(struct GNUNET_DHT_PathElement)) < putl)
686 {
687 GNUNET_break (0);
688 return GNUNET_SYSERR;
689 }
690 return GNUNET_OK;
691}
692
693
694/**
695 * Process a put monitor message from the service.
696 *
697 * @param cls The DHT handle.
698 * @param msg Monitor put message from the service.
699 */
700static void
701handle_monitor_put (void *cls,
702 const struct GNUNET_DHT_MonitorPutMessage *msg)
703{
704 struct GNUNET_DHT_Handle *handle = cls;
705 size_t msize = ntohs (msg->header.size) - sizeof(*msg);
706 uint32_t putl = ntohl (msg->put_path_length);
707 enum GNUNET_DHT_RouteOption ro
708 = (enum GNUNET_DHT_RouteOption) ntohl (msg->options);
709 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
710 const struct GNUNET_PeerIdentity *trunc_peer
711 = truncated
712 ? (const struct GNUNET_PeerIdentity *) &msg[1]
713 : NULL;
714 const struct GNUNET_DHT_PathElement *path
715 = truncated
716 ? (const struct GNUNET_DHT_PathElement *) &trunc_peer[1]
717 : (const struct GNUNET_DHT_PathElement *) &msg[1];
718
719 if (truncated)
720 msize -= sizeof (struct GNUNET_PeerIdentity);
721 msize -= sizeof(struct GNUNET_DHT_PathElement) * putl;
722 for (struct GNUNET_DHT_MonitorHandle *mh = handle->monitor_head;
723 NULL != mh;
724 mh = mh->next)
725 {
726 if (NULL == mh->put_cb)
727 continue;
728 if ( (GNUNET_BLOCK_TYPE_ANY != mh->type) &&
729 (mh->type != ntohl (msg->type)) )
730 continue;
731 if ( (NULL != mh->key) &&
732 (0 != GNUNET_memcmp (mh->key,
733 &msg->key)) )
734 continue;
735 mh->put_cb (mh->cb_cls,
736 ro,
737 (enum GNUNET_BLOCK_Type) ntohl (msg->type),
738 ntohl (msg->hop_count),
739 ntohl (msg->desired_replication_level),
740 trunc_peer,
741 putl,
742 path,
743 GNUNET_TIME_absolute_ntoh (msg->expiration_time),
744 &msg->key,
745 (const void *) &path[putl],
746 msize);
747 }
748}
749
750
751/**
752 * Verify that client result message received from the service is well-formed.
753 *
754 * @param cls The DHT handle.
755 * @param msg Monitor put message from the service.
756 * @return #GNUNET_OK if everything went fine,
757 * #GNUNET_SYSERR if the message is malformed.
758 */
759static enum GNUNET_GenericReturnValue
760check_client_result (void *cls,
761 const struct GNUNET_DHT_ClientResultMessage *msg)
762{
763 size_t msize = ntohs (msg->header.size) - sizeof(*msg);
764 uint32_t put_path_length = ntohl (msg->put_path_length);
765 uint32_t get_path_length = ntohl (msg->get_path_length);
766 enum GNUNET_DHT_RouteOption ro
767 = (enum GNUNET_DHT_RouteOption) ntohl (msg->options);
768 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
769 size_t meta_length;
770
771 if (truncated)
772 {
773 if (msize < sizeof (struct GNUNET_PeerIdentity))
774 {
775 GNUNET_break (0);
776 return GNUNET_SYSERR;
777 }
778 msize -= sizeof (struct GNUNET_PeerIdentity);
779 }
780 meta_length = msize / sizeof(struct GNUNET_DHT_PathElement);
781 if ( (get_path_length + put_path_length >
782 meta_length) ||
783 (get_path_length + put_path_length <
784 get_path_length) )
785 {
786 GNUNET_break (0);
787 return GNUNET_SYSERR;
788 }
789 return GNUNET_OK;
790}
791
792
793/**
794 * Process a given reply that might match the given request.
795 *
796 * @param cls the `struct GNUNET_DHT_ClientResultMessage`
797 * @param key query of the request
798 * @param value the `struct GNUNET_DHT_GetHandle` of a request matching the same key
799 * @return #GNUNET_YES to continue to iterate over all results
800 */
801static enum GNUNET_GenericReturnValue
802process_client_result (void *cls,
803 const struct GNUNET_HashCode *key,
804 void *value)
805{
806 const struct GNUNET_DHT_ClientResultMessage *crm = cls;
807 struct GNUNET_DHT_GetHandle *get_handle = value;
808 size_t msize = ntohs (crm->header.size) - sizeof(*crm);
809 uint16_t type = ntohl (crm->type);
810 enum GNUNET_DHT_RouteOption ro
811 = (enum GNUNET_DHT_RouteOption) ntohl (crm->options);
812 bool truncated
813 = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
814 uint32_t put_path_length
815 = ntohl (crm->put_path_length);
816 uint32_t get_path_length
817 = ntohl (crm->get_path_length);
818 const struct GNUNET_PeerIdentity *trunc_peer
819 = truncated
820 ? (const struct GNUNET_PeerIdentity *) &crm[1]
821 : NULL;
822 const struct GNUNET_DHT_PathElement *put_path
823 = truncated
824 ? (const struct GNUNET_DHT_PathElement *) &trunc_peer[1]
825 : (const struct GNUNET_DHT_PathElement *) &crm[1];
826 const struct GNUNET_DHT_PathElement *get_path
827 = &put_path[put_path_length];
828 const void *data
829 = &get_path[get_path_length];
830 size_t meta_length
831 = sizeof(struct GNUNET_DHT_PathElement)
832 * (get_path_length + put_path_length);
833 size_t data_length
834 = msize - meta_length;
835 struct GNUNET_HashCode hc;
836
837 if (truncated)
838 data_length -= sizeof (struct GNUNET_PeerIdentity);
839 if (crm->unique_id != get_handle->unique_id)
840 {
841 /* UID mismatch */
842 LOG (GNUNET_ERROR_TYPE_DEBUG,
843 "Ignoring reply for %s: UID mismatch: %llu/%llu\n",
844 GNUNET_h2s (key),
845 (unsigned long long) crm->unique_id,
846 (unsigned long long) get_handle->unique_id);
847 return GNUNET_YES;
848 }
849 if ( (get_handle->type != GNUNET_BLOCK_TYPE_ANY) &&
850 (get_handle->type != type) )
851 {
852 /* type mismatch */
853 GNUNET_break (0);
854 return GNUNET_YES;
855 }
856
857 {
858 char *pp;
859 char *gp;
860
861 gp = GNUNET_DHT_pp2s (get_path,
862 get_path_length);
863 pp = GNUNET_DHT_pp2s (put_path,
864 put_path_length);
865 LOG (GNUNET_ERROR_TYPE_DEBUG,
866 "Giving %u byte reply for %s to application (GP: %s, PP: %s)\n",
867 (unsigned int) data_length,
868 GNUNET_h2s (key),
869 gp,
870 pp);
871 GNUNET_free (gp);
872 GNUNET_free (pp);
873 }
874 /* remember that we've seen this result */
875 GNUNET_CRYPTO_hash (data,
876 data_length,
877 &hc);
878 if (get_handle->seen_results_size == get_handle->seen_results_end)
879 GNUNET_array_grow (get_handle->seen_results,
880 get_handle->seen_results_size,
881 get_handle->seen_results_size * 2 + 1);
882 get_handle->seen_results[get_handle->seen_results_end++] = hc;
883 /* no need to block it explicitly, service already knows about it! */
884 get_handle->iter (get_handle->iter_cls,
885 GNUNET_TIME_absolute_ntoh (crm->expiration),
886 key,
887 trunc_peer,
888 get_path,
889 get_path_length,
890 put_path,
891 put_path_length,
892 type,
893 data_length,
894 data);
895 return GNUNET_YES;
896}
897
898
899/**
900 * Process a client result message received from the service.
901 *
902 * @param cls The DHT handle.
903 * @param msg Monitor put message from the service.
904 */
905static void
906handle_client_result (void *cls,
907 const struct GNUNET_DHT_ClientResultMessage *msg)
908{
909 struct GNUNET_DHT_Handle *handle = cls;
910
911 GNUNET_CONTAINER_multihashmap_get_multiple (handle->active_requests,
912 &msg->key,
913 &process_client_result,
914 (void *) msg);
915}
916
917
918/**
919 * Process a client HELLO message received from the service.
920 *
921 * @param cls The DHT handle.
922 * @param hdr HELLO URL message from the service.
923 * @return #GNUNET_OK if @a hdr is well-formed
924 */
925static enum GNUNET_GenericReturnValue
926check_client_hello (void *cls,
927 const struct GNUNET_MessageHeader *hdr)
928{
929 uint16_t len = ntohs (hdr->size);
930 const char *buf = (const char *) &hdr[1];
931
932 (void) cls;
933 if ('\0' != buf[len - sizeof (*hdr) - 1])
934 {
935 GNUNET_break (0);
936 return GNUNET_SYSERR;
937 }
938 return GNUNET_OK;
939}
940
941
942/**
943 * Process a client HELLO message received from the service.
944 *
945 * @param cls The DHT handle.
946 * @param hdr HELLO URL message from the service.
947 */
948static void
949handle_client_hello (void *cls,
950 const struct GNUNET_MessageHeader *hdr)
951{
952 struct GNUNET_DHT_Handle *handle = cls;
953 const char *url = (const char *) &hdr[1];
954 struct GNUNET_DHT_HelloGetHandle *hgh;
955
956 while (NULL != (hgh = handle->hgh_head))
957 {
958 hgh->cb (hgh->cb_cls,
959 url);
960 GNUNET_DHT_hello_get_cancel (hgh);
961 }
962}
963
964
965/**
966 * Process a MQ PUT transmission notification.
967 *
968 * @param cls The DHT handle.
969 */
970static void
971handle_put_cont (void *cls)
972{
973 struct GNUNET_DHT_PutHandle *ph = cls;
974 GNUNET_SCHEDULER_TaskCallback cont;
975 void *cont_cls;
976
977 cont = ph->cont;
978 cont_cls = ph->cont_cls;
979 ph->env = NULL;
980 GNUNET_DHT_put_cancel (ph);
981 if (NULL != cont)
982 cont (cont_cls);
983}
984
985
986/**
987 * Try to (re)connect to the DHT service.
988 *
989 * @param h DHT handle to reconnect
990 * @return #GNUNET_YES on success, #GNUNET_NO on failure.
991 */
992static enum GNUNET_GenericReturnValue
993try_connect (struct GNUNET_DHT_Handle *h)
994{
995 struct GNUNET_MQ_MessageHandler handlers[] = {
996 GNUNET_MQ_hd_fixed_size (monitor_get,
997 GNUNET_MESSAGE_TYPE_DHT_MONITOR_GET,
998 struct GNUNET_DHT_MonitorGetMessage,
999 h),
1000 GNUNET_MQ_hd_var_size (monitor_get_resp,
1001 GNUNET_MESSAGE_TYPE_DHT_MONITOR_GET_RESP,
1002 struct GNUNET_DHT_MonitorGetRespMessage,
1003 h),
1004 GNUNET_MQ_hd_var_size (monitor_put,
1005 GNUNET_MESSAGE_TYPE_DHT_MONITOR_PUT,
1006 struct GNUNET_DHT_MonitorPutMessage,
1007 h),
1008 GNUNET_MQ_hd_var_size (client_result,
1009 GNUNET_MESSAGE_TYPE_DHT_CLIENT_RESULT,
1010 struct GNUNET_DHT_ClientResultMessage,
1011 h),
1012 GNUNET_MQ_hd_var_size (client_hello,
1013 GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_URL,
1014 struct GNUNET_MessageHeader,
1015 h),
1016 GNUNET_MQ_handler_end ()
1017 };
1018
1019 if (NULL != h->mq)
1020 return GNUNET_OK;
1021 h->mq = GNUNET_CLIENT_connect (h->cfg,
1022 "dht",
1023 handlers,
1024 &mq_error_handler,
1025 h);
1026 if (NULL == h->mq)
1027 {
1028 LOG (GNUNET_ERROR_TYPE_WARNING,
1029 "Failed to connect to the DHT service!\n");
1030 return GNUNET_NO;
1031 }
1032 return GNUNET_YES;
1033}
1034
1035
1036struct GNUNET_DHT_Handle *
1037GNUNET_DHT_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
1038 unsigned int ht_len)
1039{
1040 struct GNUNET_DHT_Handle *handle;
1041
1042 handle = GNUNET_new (struct GNUNET_DHT_Handle);
1043 handle->cfg = cfg;
1044 handle->uid_gen
1045 = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1046 UINT64_MAX);
1047 handle->active_requests
1048 = GNUNET_CONTAINER_multihashmap_create (ht_len,
1049 GNUNET_YES);
1050 if (GNUNET_NO == try_connect (handle))
1051 {
1052 GNUNET_DHT_disconnect (handle);
1053 return NULL;
1054 }
1055 return handle;
1056}
1057
1058
1059void
1060GNUNET_DHT_disconnect (struct GNUNET_DHT_Handle *handle)
1061{
1062 struct GNUNET_DHT_PutHandle *ph;
1063
1064 GNUNET_assert (0 ==
1065 GNUNET_CONTAINER_multihashmap_size (handle->active_requests));
1066 while (NULL != (ph = handle->put_head))
1067 {
1068 if (NULL != ph->cont)
1069 ph->cont (ph->cont_cls);
1070 GNUNET_DHT_put_cancel (ph);
1071 }
1072 if (NULL != handle->mq)
1073 {
1074 GNUNET_MQ_destroy (handle->mq);
1075 handle->mq = NULL;
1076 }
1077 if (NULL != handle->reconnect_task)
1078 {
1079 GNUNET_SCHEDULER_cancel (handle->reconnect_task);
1080 handle->reconnect_task = NULL;
1081 }
1082 GNUNET_CONTAINER_multihashmap_destroy (handle->active_requests);
1083 GNUNET_free (handle);
1084}
1085
1086
1087struct GNUNET_DHT_PutHandle *
1088GNUNET_DHT_put (struct GNUNET_DHT_Handle *handle,
1089 const struct GNUNET_HashCode *key,
1090 uint32_t desired_replication_level,
1091 enum GNUNET_DHT_RouteOption options,
1092 enum GNUNET_BLOCK_Type type,
1093 size_t size,
1094 const void *data,
1095 struct GNUNET_TIME_Absolute exp,
1096 GNUNET_SCHEDULER_TaskCallback cont,
1097 void *cont_cls)
1098{
1099 struct GNUNET_MQ_Envelope *env;
1100 struct GNUNET_DHT_ClientPutMessage *put_msg;
1101 size_t msize;
1102 struct GNUNET_DHT_PutHandle *ph;
1103
1104 msize = sizeof(struct GNUNET_DHT_ClientPutMessage) + size;
1105 if ((msize >= GNUNET_MAX_MESSAGE_SIZE) ||
1106 (size >= GNUNET_MAX_MESSAGE_SIZE))
1107 {
1108 GNUNET_break (0);
1109 return NULL;
1110 }
1111 if (NULL == handle->mq)
1112 return NULL;
1113 LOG (GNUNET_ERROR_TYPE_DEBUG,
1114 "Sending PUT for %s to DHT via %p\n",
1115 GNUNET_h2s (key),
1116 handle);
1117 ph = GNUNET_new (struct GNUNET_DHT_PutHandle);
1118 ph->dht_handle = handle;
1119 ph->cont = cont;
1120 ph->cont_cls = cont_cls;
1121 GNUNET_CONTAINER_DLL_insert_tail (handle->put_head,
1122 handle->put_tail,
1123 ph);
1124 env = GNUNET_MQ_msg_extra (put_msg,
1125 size,
1126 GNUNET_MESSAGE_TYPE_DHT_CLIENT_PUT);
1127 GNUNET_MQ_notify_sent (env,
1128 &handle_put_cont,
1129 ph);
1130 ph->env = env;
1131 put_msg->type = htonl ((uint32_t) type);
1132 put_msg->options = htonl ((uint32_t) options);
1133 put_msg->desired_replication_level = htonl (desired_replication_level);
1134 put_msg->expiration = GNUNET_TIME_absolute_hton (exp);
1135 put_msg->key = *key;
1136 GNUNET_memcpy (&put_msg[1],
1137 data,
1138 size);
1139 GNUNET_MQ_send (handle->mq,
1140 env);
1141 return ph;
1142}
1143
1144
1145void
1146GNUNET_DHT_put_cancel (struct GNUNET_DHT_PutHandle *ph)
1147{
1148 struct GNUNET_DHT_Handle *handle = ph->dht_handle;
1149
1150 if (NULL != ph->env)
1151 GNUNET_MQ_notify_sent (ph->env,
1152 NULL,
1153 NULL);
1154 GNUNET_CONTAINER_DLL_remove (handle->put_head,
1155 handle->put_tail,
1156 ph);
1157 GNUNET_free (ph);
1158}
1159
1160
1161struct GNUNET_DHT_GetHandle *
1162GNUNET_DHT_get_start (struct GNUNET_DHT_Handle *handle,
1163 enum GNUNET_BLOCK_Type type,
1164 const struct GNUNET_HashCode *key,
1165 uint32_t desired_replication_level,
1166 enum GNUNET_DHT_RouteOption options,
1167 const void *xquery,
1168 size_t xquery_size,
1169 GNUNET_DHT_GetIterator iter,
1170 void *iter_cls)
1171{
1172 struct GNUNET_DHT_GetHandle *gh;
1173 size_t msize;
1174
1175 msize = sizeof(struct GNUNET_DHT_ClientGetMessage) + xquery_size;
1176 if ((msize >= GNUNET_MAX_MESSAGE_SIZE) ||
1177 (xquery_size >= GNUNET_MAX_MESSAGE_SIZE))
1178 {
1179 GNUNET_break (0);
1180 return NULL;
1181 }
1182 LOG (GNUNET_ERROR_TYPE_DEBUG,
1183 "Sending query for %s to DHT %p\n",
1184 GNUNET_h2s (key),
1185 handle);
1186 gh = GNUNET_malloc (sizeof(struct GNUNET_DHT_GetHandle)
1187 + xquery_size);
1188 gh->iter = iter;
1189 gh->iter_cls = iter_cls;
1190 gh->dht_handle = handle;
1191 gh->key = *key;
1192 gh->unique_id = ++handle->uid_gen;
1193 gh->xquery_size = xquery_size;
1194 gh->desired_replication_level = desired_replication_level;
1195 gh->type = type;
1196 gh->options = options;
1197 GNUNET_memcpy (&gh[1],
1198 xquery,
1199 xquery_size);
1200 GNUNET_CONTAINER_multihashmap_put (handle->active_requests,
1201 &gh->key,
1202 gh,
1203 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1204 if (NULL != handle->mq)
1205 send_get (gh);
1206 return gh;
1207}
1208
1209
1210void
1211GNUNET_DHT_get_filter_known_results (struct GNUNET_DHT_GetHandle *get_handle,
1212 unsigned int num_results,
1213 const struct GNUNET_HashCode *results)
1214{
1215 unsigned int needed;
1216 unsigned int had;
1217
1218 had = get_handle->seen_results_end;
1219 needed = had + num_results;
1220 if (needed > get_handle->seen_results_size)
1221 GNUNET_array_grow (get_handle->seen_results,
1222 get_handle->seen_results_size,
1223 needed);
1224 GNUNET_memcpy (&get_handle->seen_results[get_handle->seen_results_end],
1225 results,
1226 num_results * sizeof(struct GNUNET_HashCode));
1227 get_handle->seen_results_end += num_results;
1228 if (NULL != get_handle->dht_handle->mq)
1229 send_get_known_results (get_handle,
1230 had);
1231}
1232
1233
1234void
1235GNUNET_DHT_get_stop (struct GNUNET_DHT_GetHandle *get_handle)
1236{
1237 struct GNUNET_DHT_Handle *handle = get_handle->dht_handle;
1238
1239 LOG (GNUNET_ERROR_TYPE_DEBUG,
1240 "Sending STOP for %s to DHT via %p\n",
1241 GNUNET_h2s (&get_handle->key),
1242 handle);
1243 if (NULL != handle->mq)
1244 {
1245 struct GNUNET_MQ_Envelope *env;
1246 struct GNUNET_DHT_ClientGetStopMessage *stop_msg;
1247
1248 env = GNUNET_MQ_msg (stop_msg,
1249 GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET_STOP);
1250 stop_msg->reserved = htonl (0);
1251 stop_msg->unique_id = get_handle->unique_id;
1252 stop_msg->key = get_handle->key;
1253 GNUNET_MQ_send (handle->mq,
1254 env);
1255 }
1256 GNUNET_assert (GNUNET_YES ==
1257 GNUNET_CONTAINER_multihashmap_remove (handle->active_requests,
1258 &get_handle->key,
1259 get_handle));
1260 GNUNET_array_grow (get_handle->seen_results,
1261 get_handle->seen_results_end,
1262 0);
1263 GNUNET_free (get_handle);
1264}
1265
1266
1267struct GNUNET_DHT_MonitorHandle *
1268GNUNET_DHT_monitor_start (struct GNUNET_DHT_Handle *handle,
1269 enum GNUNET_BLOCK_Type type,
1270 const struct GNUNET_HashCode *key,
1271 GNUNET_DHT_MonitorGetCB get_cb,
1272 GNUNET_DHT_MonitorGetRespCB get_resp_cb,
1273 GNUNET_DHT_MonitorPutCB put_cb,
1274 void *cb_cls)
1275{
1276 struct GNUNET_DHT_MonitorHandle *mh;
1277
1278 mh = GNUNET_new (struct GNUNET_DHT_MonitorHandle);
1279 mh->get_cb = get_cb;
1280 mh->get_resp_cb = get_resp_cb;
1281 mh->put_cb = put_cb;
1282 mh->cb_cls = cb_cls;
1283 mh->type = type;
1284 mh->dht_handle = handle;
1285 if (NULL != key)
1286 {
1287 mh->key = GNUNET_new (struct GNUNET_HashCode);
1288 *mh->key = *key;
1289 }
1290 GNUNET_CONTAINER_DLL_insert (handle->monitor_head,
1291 handle->monitor_tail,
1292 mh);
1293 if (NULL != handle->mq)
1294 send_monitor_start (mh);
1295 return mh;
1296}
1297
1298
1299void
1300GNUNET_DHT_monitor_stop (struct GNUNET_DHT_MonitorHandle *mh)
1301{
1302 struct GNUNET_DHT_Handle *handle = mh->dht_handle;
1303 struct GNUNET_DHT_MonitorStartStopMessage *m;
1304 struct GNUNET_MQ_Envelope *env;
1305
1306 GNUNET_CONTAINER_DLL_remove (handle->monitor_head,
1307 handle->monitor_tail,
1308 mh);
1309 env = GNUNET_MQ_msg (m,
1310 GNUNET_MESSAGE_TYPE_DHT_MONITOR_STOP);
1311 m->type = htonl (mh->type);
1312 m->get = htons (NULL != mh->get_cb);
1313 m->get_resp = htons (NULL != mh->get_resp_cb);
1314 m->put = htons (NULL != mh->put_cb);
1315 if (NULL != mh->key)
1316 {
1317 m->filter_key = htons (1);
1318 m->key = *mh->key;
1319 }
1320 GNUNET_MQ_send (handle->mq,
1321 env);
1322 GNUNET_free (mh->key);
1323 GNUNET_free (mh);
1324}
1325
1326
1327char *
1328GNUNET_DHT_pp2s (const struct GNUNET_DHT_PathElement *path,
1329 unsigned int path_len)
1330{
1331 char *buf;
1332 size_t off;
1333 size_t plen = path_len * 5 + 1;
1334
1335 GNUNET_assert (path_len < UINT32_MAX / 5);
1336 off = 0;
1337 buf = GNUNET_malloc (plen);
1338 for (unsigned int i = 0; i < path_len; i++)
1339 {
1340 off += GNUNET_snprintf (&buf[off],
1341 plen - off,
1342 "%s%s",
1343 GNUNET_i2s (&path[i].pred),
1344 (i == path_len - 1) ? "" : "-");
1345 }
1346 return buf;
1347}
1348
1349
1350unsigned int
1351GNUNET_DHT_verify_path (const void *data,
1352 size_t data_size,
1353 struct GNUNET_TIME_Absolute exp_time,
1354 const struct GNUNET_PeerIdentity *bpid,
1355 const struct GNUNET_DHT_PathElement *put_path,
1356 unsigned int put_path_len,
1357 const struct GNUNET_DHT_PathElement *get_path,
1358 unsigned int get_path_len,
1359 const struct GNUNET_PeerIdentity *me)
1360{
1361 static struct GNUNET_PeerIdentity zero;
1362 struct GNUNET_DHT_HopSignature hs = {
1363 .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_DHT_HOP),
1364 .purpose.size = htonl (sizeof (hs)),
1365 .expiration_time = GNUNET_TIME_absolute_hton (exp_time)
1366 };
1367 unsigned int i;
1368
1369 if (0 == get_path_len + put_path_len)
1370 return 0;
1371 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1372 "%s is verifying signatures with GPL: %u PPL: %u!\n",
1373 GNUNET_i2s (me),
1374 get_path_len,
1375 put_path_len);
1376 for (unsigned int j = 0; j<put_path_len; j++)
1377 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1378 "PP%u=%s\n",
1379 j,
1380 GNUNET_i2s (&put_path[j].pred));
1381 for (unsigned int j = 0; j<get_path_len; j++)
1382 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1383 "GP%u=%s\n",
1384 j,
1385 GNUNET_i2s (&get_path[j].pred));
1386 GNUNET_CRYPTO_hash (data,
1387 data_size,
1388 &hs.h_data);
1389 i = put_path_len + get_path_len;
1390 while (i > 0)
1391 {
1392 const struct GNUNET_PeerIdentity *pred;
1393 const struct GNUNET_PeerIdentity *succ;
1394 const struct GNUNET_DHT_PathElement *pe;
1395
1396 i--;
1397 if (0 == i)
1398 {
1399 pred = (NULL == bpid) ? &zero : bpid;
1400 }
1401 else
1402 {
1403 unsigned int off = i - 1;
1404
1405 pred = (off >= put_path_len)
1406 ? &get_path[off - put_path_len].pred
1407 : &put_path[off].pred;
1408 }
1409 if (i == get_path_len + put_path_len - 1)
1410 {
1411 succ = me;
1412 }
1413 else
1414 {
1415 unsigned int off = i + 1;
1416
1417 succ = (off >= put_path_len)
1418 ? &get_path[off - put_path_len].pred
1419 : &put_path[off].pred;
1420 }
1421 hs.pred = *pred;
1422 hs.succ = *succ;
1423 pe = (i >= put_path_len)
1424 ? &get_path[i - put_path_len]
1425 : &put_path[i];
1426 if (GNUNET_OK !=
1427 GNUNET_CRYPTO_eddsa_verify (
1428 GNUNET_SIGNATURE_PURPOSE_DHT_HOP,
1429 &hs,
1430 &pe->sig,
1431 &pe->pred.public_key))
1432 {
1433 GNUNET_break_op (0);
1434 return i + 1;
1435 }
1436 }
1437 return i;
1438}
1439
1440
1441struct GNUNET_DHT_HelloGetHandle *
1442GNUNET_DHT_hello_get (struct GNUNET_DHT_Handle *dht_handle,
1443 GNUNET_DHT_HelloGetCallback cb,
1444 void *cb_cls)
1445{
1446 struct GNUNET_DHT_HelloGetHandle *hgh;
1447 struct GNUNET_MessageHeader *hdr;
1448 struct GNUNET_MQ_Envelope *env;
1449
1450 hgh = GNUNET_new (struct GNUNET_DHT_HelloGetHandle);
1451 hgh->cb = cb;
1452 hgh->cb_cls = cb_cls;
1453 hgh->dht_handle = dht_handle;
1454 GNUNET_CONTAINER_DLL_insert (dht_handle->hgh_head,
1455 dht_handle->hgh_tail,
1456 hgh);
1457 env = GNUNET_MQ_msg (hdr,
1458 GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_GET);
1459 GNUNET_MQ_send (dht_handle->mq,
1460 env);
1461 return hgh;
1462}
1463
1464
1465void
1466GNUNET_DHT_hello_get_cancel (struct GNUNET_DHT_HelloGetHandle *hgh)
1467{
1468 struct GNUNET_DHT_Handle *dht_handle = hgh->dht_handle;
1469
1470 GNUNET_CONTAINER_DLL_remove (dht_handle->hgh_head,
1471 dht_handle->hgh_tail,
1472 hgh);
1473 GNUNET_free (hgh);
1474}
1475
1476
1477void
1478GNUNET_DHT_hello_offer (struct GNUNET_DHT_Handle *dht_handle,
1479 const char *url,
1480 GNUNET_SCHEDULER_TaskCallback cb,
1481 void *cb_cls)
1482{
1483 struct GNUNET_MessageHeader *hdr;
1484 size_t slen = strlen (url) + 1;
1485 struct GNUNET_MQ_Envelope *env;
1486
1487 env = GNUNET_MQ_msg_extra (hdr,
1488 slen,
1489 GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_URL);
1490 memcpy (&hdr[1],
1491 url,
1492 slen);
1493 GNUNET_MQ_notify_sent (env,
1494 cb,
1495 cb_cls);
1496 GNUNET_MQ_send (dht_handle->mq,
1497 env);
1498}
1499
1500
1501/* end of dht_api.c */
diff --git a/src/service/dht/dhtu.conf b/src/service/dht/dhtu.conf
new file mode 100644
index 000000000..ea5ade752
--- /dev/null
+++ b/src/service/dht/dhtu.conf
@@ -0,0 +1,7 @@
1[dhtu-gnunet]
2ENABLED = YES
3
4[dhtu-ip]
5ENABLED = NO
6NSE = 4
7UDP_PORT = 6666
diff --git a/src/service/dht/dhtu_testbed_connect.sh b/src/service/dht/dhtu_testbed_connect.sh
new file mode 100755
index 000000000..b0ba474bf
--- /dev/null
+++ b/src/service/dht/dhtu_testbed_connect.sh
@@ -0,0 +1,35 @@
1#!/bin/bash
2# This file is in the public domain.
3
4set -eu
5
6GNUNET_TMP="$(gnunet-config -f -s PATHS -o GNUNET_TMP)"
7
8# Helper script for dhtu_testbed_deploy.sh.
9# Do not invoke directly.
10
11n=$1
12CFG="$GNUNET_TMP/deployment/${n}.conf"
13HELLO=`gnunet-dht-hello -c $CFG`
14
15# Create dense topology:
16#for OFF in `seq 1 $MAX`
17#do
18# TCFG="$GNUNET_TMP/deployment/${OFF}.conf"
19# gnunet-dht-hello -c $TCFG $HELLO
20#done
21#exit 0
22
23# Create sparse topology:
24R=1
25while test `expr $R \* $R \* $R` -lt $MAX
26do
27 END=`expr $R \* $R`
28 for M in `seq $R $R $END`
29 do
30 OFF=`expr \( $n + $M \) % $MAX`
31 TCFG="$GNUNET_TMP/deployment/${OFF}.conf"
32 gnunet-dht-hello -c $TCFG $HELLO
33 done
34 R=`expr $R + 1`
35done
diff --git a/src/service/dht/dhtu_testbed_deploy.conf b/src/service/dht/dhtu_testbed_deploy.conf
new file mode 100644
index 000000000..efabd97d3
--- /dev/null
+++ b/src/service/dht/dhtu_testbed_deploy.conf
@@ -0,0 +1,26 @@
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=$GNUNET_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/service/dht/dhtu_testbed_deploy.sh b/src/service/dht/dhtu_testbed_deploy.sh
new file mode 100755
index 000000000..e8ac8d5d1
--- /dev/null
+++ b/src/service/dht/dhtu_testbed_deploy.sh
@@ -0,0 +1,95 @@
1#!/bin/bash
2# This file is in the public domain.
3
4# Getting location for temporary files
5GNUNET_TMP="$(gnunet-config -f -s PATHS -o GNUNET_TMP)"
6
7# We will use UDP ports above this number.
8MINPORT=10000
9
10# Cleanup to run whenever we exit
11function cleanup()
12{
13 for n in `jobs -p`
14 do
15 kill $n 2> /dev/null || true
16 done
17 wait
18}
19
20# Install cleanup handler (except for kill -9)
21trap cleanup EXIT
22
23if test -z "$1"
24then
25 echo "Call with the number of peers to launch."
26 exit 1
27fi
28
29echo -n "Testing for GNU parallel ..."
30
31if test ! -x `which parallel`
32then
33 echo "This script requires GNU parallel"
34 exit 1
35fi
36
37parallel -V | grep "GNU parallel" > /dev/null || exit 1
38
39echo " OK"
40
41
42
43if test ! -x `which gnunet-service-dht`
44then
45 echo "This script requires gnunet-service-dht in \$PATH"
46 exit 1
47fi
48
49if test ! -x `which gnunet-dht-hello`
50then
51 echo "This script requires gnunet-dht-hello in \$PATH"
52 exit 1
53fi
54
55MAX=`expr $1 - 1`
56
57export GNUNET_FORCE_LOG="dht*;;;;DEBUG"
58
59echo -n "Starting $1 peers "
60mkdir -p "$GNUNET_TMP/deployment"
61for n in `seq 0 $MAX`
62do
63 PORT=`expr $MINPORT + $n`
64 CFG="$GNUNET_TMP/deployment/${n}.conf"
65 cat dhtu_testbed_deploy.conf | sed -e "s/%N%/$PORT/" > $CFG
66 gnunet-service-dht -c $CFG -L DEBUG &> "$GNUNET_TMP/deployment/$n.log" &
67 echo -n "."
68done
69
70echo ""
71echo "$1 peers ready".
72
73unset GNUNET_FORCE_LOG
74
75function connect()
76{
77 n=$1
78}
79
80echo -n "Connecting peers ..."
81
82export MAX
83if test 0 != $MAX
84then
85 seq 0 $MAX | parallel ./dhtu_testbed_connect.sh :::
86fi
87
88
89echo ""
90echo "Network ready. Press ENTER to terminate the testbed!"
91echo "Interact with peers using '-c $GNUNET_TMP/deployment/\$N.conf'"
92
93read
94
95exit 0
diff --git a/src/service/dht/gnunet-service-dht.c b/src/service/dht/gnunet-service-dht.c
new file mode 100644
index 000000000..4579bfc2b
--- /dev/null
+++ b/src/service/dht/gnunet-service-dht.c
@@ -0,0 +1,562 @@
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_common.h"
29#include "gnunet_block_lib.h"
30#include "gnunet_util_lib.h"
31#include "gnunet_hello_uri_lib.h"
32#include "gnunet_statistics_service.h"
33#include "gnunet-service-dht.h"
34#include "gnunet-service-dht_datacache.h"
35#include "gnunet-service-dht_neighbours.h"
36#include "gnunet-service-dht_routing.h"
37#include "plugin_dhtu_ip.h"
38#include "plugin_dhtu_gnunet.h"
39
40/**
41 * How often do we broadcast our HELLO to neighbours if
42 * nothing special happens?
43 */
44#define HELLO_FREQUENCY GNUNET_TIME_UNIT_HOURS
45
46
47/**
48 * Information we keep per underlay.
49 */
50struct GDS_Underlay
51{
52
53 /**
54 * Kept in a DLL.
55 */
56 struct GDS_Underlay *next;
57
58 /**
59 * Kept in a DLL.
60 */
61 struct GDS_Underlay *prev;
62
63 /**
64 * Environment for this underlay.
65 */
66 struct GNUNET_DHTU_PluginEnvironment env;
67
68 /**
69 * Underlay API handle.
70 */
71 struct GNUNET_DHTU_PluginFunctions *dhtu;
72
73 /**
74 * current network size estimate for this underlay.
75 */
76 double network_size_estimate;
77
78 /**
79 * Name of the underlay (i.e. "gnunet" or "ip").
80 */
81 char *name;
82};
83
84
85/**
86 * An address of this peer.
87 */
88struct MyAddress
89{
90 /**
91 * Kept in a DLL.
92 */
93 struct MyAddress *next;
94
95 /**
96 * Kept in a DLL.
97 */
98 struct MyAddress *prev;
99
100 /**
101 * Underlay handle for the address.
102 */
103 struct GNUNET_DHTU_Source *source;
104
105 /**
106 * Textual representation of the address.
107 */
108 char *url;
109
110 /**
111 * Underlay of this address.
112 */
113 struct GDS_Underlay *u;
114};
115
116
117/**
118 * Our HELLO
119 */
120struct GNUNET_HELLO_Builder *GDS_my_hello;
121
122/**
123 * Identity of this peer.
124 */
125struct GNUNET_PeerIdentity GDS_my_identity;
126
127/**
128 * Hash of the identity of this peer.
129 */
130struct GNUNET_HashCode GDS_my_identity_hash;
131
132/**
133 * Our private key.
134 */
135struct GNUNET_CRYPTO_EddsaPrivateKey GDS_my_private_key;
136
137/**
138 * Task broadcasting our HELLO.
139 */
140static struct GNUNET_SCHEDULER_Task *hello_task;
141
142/**
143 * Handles for the DHT underlays.
144 */
145static struct GDS_Underlay *u_head;
146
147/**
148 * Handles for the DHT underlays.
149 */
150static struct GDS_Underlay *u_tail;
151
152/**
153 * Head of addresses of this peer.
154 */
155static struct MyAddress *a_head;
156
157/**
158 * Tail of addresses of this peer.
159 */
160static struct MyAddress *a_tail;
161
162/**
163 * log of the current network size estimate, used as the point where
164 * we switch between random and deterministic routing.
165 */
166static double log_of_network_size_estimate;
167
168
169/**
170 * Callback that is called when network size estimate is updated.
171 *
172 * @param cls a `struct GDS_Underlay`
173 * @param timestamp time when the estimate was received from the server (or created by the server)
174 * @param logestimate the log(Base 2) value of the current network size estimate
175 * @param std_dev standard deviation for the estimate
176 *
177 */
178static void
179update_network_size_estimate (void *cls,
180 struct GNUNET_TIME_Absolute timestamp,
181 double logestimate,
182 double std_dev)
183{
184 struct GDS_Underlay *u = cls;
185 double sum = 0.0;
186
187 GNUNET_STATISTICS_update (GDS_stats,
188 "# Network size estimates received",
189 1,
190 GNUNET_NO);
191 /* do not allow estimates < 0.5 */
192 u->network_size_estimate = pow (2.0,
193 GNUNET_MAX (0.5,
194 logestimate));
195 for (struct GDS_Underlay *p = u_head; NULL != p; p = p->next)
196 sum += p->network_size_estimate;
197 if (sum <= 2.0)
198 log_of_network_size_estimate = 0.5;
199 else
200 log_of_network_size_estimate = log2 (sum);
201}
202
203
204/**
205 * Return the current NSE
206 *
207 * @return the current NSE as a logarithm
208 */
209double
210GDS_NSE_get (void)
211{
212 return log_of_network_size_estimate;
213}
214
215
216#include "gnunet-service-dht_clients.c"
217
218
219/**
220 * Task run periodically to broadcast our HELLO.
221 *
222 * @param cls NULL
223 */
224static void
225broadcast_hello (void *cls)
226{
227 struct GNUNET_MessageHeader *hello;
228
229 (void) cls;
230 /* TODO: randomize! */
231 hello_task = GNUNET_SCHEDULER_add_delayed (HELLO_FREQUENCY,
232 &broadcast_hello,
233 NULL);
234 hello = GNUNET_HELLO_builder_to_dht_hello_msg (GDS_my_hello,
235 &GDS_my_private_key,
236 GNUNET_TIME_UNIT_ZERO);
237 if (NULL == hello)
238 {
239 GNUNET_break (0);
240 return;
241 }
242 GDS_NEIGHBOURS_broadcast (hello);
243 GNUNET_free (hello);
244}
245
246
247/**
248 * Function to call with new addresses of this peer.
249 *
250 * @param cls the closure
251 * @param address address under which we are likely reachable,
252 * 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/"
253 * @param source handle for sending from this address, NULL if we can only receive
254 * @param[out] ctx storage space for DHT to use in association with this address
255 */
256static void
257u_address_add (void *cls,
258 const char *address,
259 struct GNUNET_DHTU_Source *source,
260 void **ctx)
261{
262 struct GDS_Underlay *u = cls;
263 struct MyAddress *a;
264 enum GNUNET_GenericReturnValue add_success;
265
266 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
267 "Underlay adds address %s for this peer\n",
268 address);
269 if (GNUNET_OK != (add_success = GNUNET_HELLO_builder_add_address (
270 GDS_my_hello,
271 address)))
272 {
273 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
274 "Adding address `%s' from underlay %s\n",
275 address,
276 GNUNET_NO == add_success ? "not done" : "failed");
277 return;
278 }
279 a = GNUNET_new (struct MyAddress);
280 a->source = source;
281 a->url = GNUNET_strdup (address);
282 a->u = u;
283 GNUNET_CONTAINER_DLL_insert (a_head,
284 a_tail,
285 a);
286 *ctx = a;
287
288 if (NULL != hello_task)
289 GNUNET_SCHEDULER_cancel (hello_task);
290 hello_task = GNUNET_SCHEDULER_add_now (&broadcast_hello,
291 NULL);
292}
293
294
295/**
296 * Function to call with expired addresses of this peer.
297 *
298 * @param[in] ctx storage space used by the DHT in association with this address
299 */
300static void
301u_address_del (void *ctx)
302{
303 struct MyAddress *a = ctx;
304
305 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
306 "Underlay deletes address %s for this peer\n",
307 a->url);
308 GNUNET_HELLO_builder_del_address (GDS_my_hello,
309 a->url);
310 GNUNET_CONTAINER_DLL_remove (a_head,
311 a_tail,
312 a);
313 GNUNET_free (a->url);
314 GNUNET_free (a);
315 if (NULL != hello_task)
316 GNUNET_SCHEDULER_cancel (hello_task);
317 hello_task = GNUNET_SCHEDULER_add_now (&broadcast_hello,
318 NULL);
319}
320
321
322void
323GDS_u_try_connect (const struct GNUNET_PeerIdentity *pid,
324 const char *address)
325{
326 for (struct GDS_Underlay *u = u_head;
327 NULL != u;
328 u = u->next)
329 u->dhtu->try_connect (u->dhtu->cls,
330 pid,
331 address);
332}
333
334
335void
336GDS_u_send (struct GDS_Underlay *u,
337 struct GNUNET_DHTU_Target *target,
338 const void *msg,
339 size_t msg_size,
340 GNUNET_SCHEDULER_TaskCallback finished_cb,
341 void *finished_cb_cls)
342{
343 u->dhtu->send (u->dhtu->cls,
344 target,
345 msg,
346 msg_size,
347 finished_cb,
348 finished_cb_cls);
349}
350
351
352void
353GDS_u_drop (struct GDS_Underlay *u,
354 struct GNUNET_DHTU_PreferenceHandle *ph)
355{
356 u->dhtu->drop (ph);
357}
358
359
360struct GNUNET_DHTU_PreferenceHandle *
361GDS_u_hold (struct GDS_Underlay *u,
362 struct GNUNET_DHTU_Target *target)
363{
364 return u->dhtu->hold (u->dhtu->cls,
365 target);
366}
367
368
369/**
370 * Task run during shutdown.
371 *
372 * @param cls unused
373 */
374static void
375shutdown_task (void *cls)
376{
377 struct GDS_Underlay *u;
378
379 while (NULL != (u = u_head))
380 {
381 GNUNET_CONTAINER_DLL_remove (u_head,
382 u_tail,
383 u);
384 if (0 == strcmp (u->name, "gnunet"))
385 {
386 DHTU_gnunet_done (u->dhtu);
387 }
388 else if (0 == strcmp (u->name, "ip"))
389 {
390 DHTU_ip_done (u->dhtu);
391 }
392 else
393 {
394 GNUNET_assert (0);
395 }
396 GNUNET_free (u->name);
397 GNUNET_free (u);
398 }
399 GDS_NEIGHBOURS_done ();
400 GDS_DATACACHE_done ();
401 GDS_ROUTING_done ();
402 if (NULL != GDS_block_context)
403 {
404 GNUNET_BLOCK_context_destroy (GDS_block_context);
405 GDS_block_context = NULL;
406 }
407 GDS_CLIENTS_stop ();
408 if (NULL != GDS_stats)
409 {
410 GNUNET_STATISTICS_destroy (GDS_stats,
411 GNUNET_YES);
412 GDS_stats = NULL;
413 }
414 if (NULL != GDS_my_hello)
415 {
416 GNUNET_HELLO_builder_free (GDS_my_hello);
417 GDS_my_hello = NULL;
418 }
419 if (NULL != hello_task)
420 {
421 GNUNET_SCHEDULER_cancel (hello_task);
422 hello_task = NULL;
423 }
424}
425
426
427/**
428 * Function iterating over all configuration sections.
429 * Loads plugins for enabled DHT underlays.
430 *
431 * @param cls NULL
432 * @param section configuration section to inspect
433 */
434static void
435load_underlay (void *cls,
436 const char *section)
437{
438 struct GDS_Underlay *u;
439
440 (void) cls;
441 if (0 != strncasecmp (section,
442 "dhtu-",
443 strlen ("dhtu-")))
444 return;
445 if (GNUNET_YES !=
446 GNUNET_CONFIGURATION_get_value_yesno (GDS_cfg,
447 section,
448 "ENABLED"))
449 return;
450 section += strlen ("dhtu-");
451 u = GNUNET_new (struct GDS_Underlay);
452 u->env.cls = u;
453 u->env.cfg = GDS_cfg;
454 u->env.address_add_cb = &u_address_add;
455 u->env.address_del_cb = &u_address_del;
456 u->env.network_size_cb = &update_network_size_estimate;
457 u->env.connect_cb = &GDS_u_connect;
458 u->env.disconnect_cb = &GDS_u_disconnect;
459 u->env.receive_cb = &GDS_u_receive;
460
461 /** NOTE: This is not pretty, but it allows us to avoid
462 dynamically loading plugins **/
463 if (0 == strcmp (section, "gnunet"))
464 {
465 u->dhtu = DHTU_gnunet_init (&u->env);
466 }
467 else if (0 == strcmp (section, "ip"))
468 {
469 u->dhtu = DHTU_ip_init (&u->env);
470 }
471 if (NULL == u->dhtu)
472 {
473 GNUNET_free (u);
474 return;
475 }
476 u->name = GNUNET_strdup (section);
477 GNUNET_CONTAINER_DLL_insert (u_head,
478 u_tail,
479 u);
480}
481
482
483/**
484 * Process dht requests.
485 *
486 * @param cls closure
487 * @param c configuration to use
488 * @param service the initialized service
489 */
490static void
491run (void *cls,
492 const struct GNUNET_CONFIGURATION_Handle *c,
493 struct GNUNET_SERVICE_Handle *service)
494{
495 GDS_cfg = c;
496 GDS_service = service;
497 {
498 char *keyfile;
499
500 if (GNUNET_OK !=
501 GNUNET_CONFIGURATION_get_value_filename (GDS_cfg,
502 "PEER",
503 "PRIVATE_KEY",
504 &keyfile))
505 {
506 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
507 "PEER",
508 "PRIVATE_KEY");
509 GNUNET_SCHEDULER_shutdown ();
510 return;
511 }
512 if (GNUNET_SYSERR ==
513 GNUNET_CRYPTO_eddsa_key_from_file (keyfile,
514 GNUNET_YES,
515 &GDS_my_private_key))
516 {
517 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
518 "Failed to setup peer's private key\n");
519 GNUNET_free (keyfile);
520 GNUNET_SCHEDULER_shutdown ();
521 return;
522 }
523 GNUNET_free (keyfile);
524 }
525 GNUNET_CRYPTO_eddsa_key_get_public (&GDS_my_private_key,
526 &GDS_my_identity.public_key);
527 GDS_my_hello = GNUNET_HELLO_builder_new (&GDS_my_identity);
528 GNUNET_CRYPTO_hash (&GDS_my_identity,
529 sizeof(struct GNUNET_PeerIdentity),
530 &GDS_my_identity_hash);
531 GDS_block_context = GNUNET_BLOCK_context_create (GDS_cfg);
532 GDS_stats = GNUNET_STATISTICS_create ("dht",
533 GDS_cfg);
534 GDS_CLIENTS_init ();
535 GDS_ROUTING_init ();
536 GDS_DATACACHE_init ();
537 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
538 NULL);
539 if (GNUNET_OK !=
540 GDS_NEIGHBOURS_init ())
541 {
542 GNUNET_SCHEDULER_shutdown ();
543 return;
544 }
545 GNUNET_CONFIGURATION_iterate_sections (GDS_cfg,
546 &load_underlay,
547 NULL);
548 if (NULL == u_head)
549 {
550 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
551 "No DHT underlays configured!\n");
552 GNUNET_SCHEDULER_shutdown ();
553 return;
554 }
555}
556
557
558/* Finally, define the main method */
559GDS_DHT_SERVICE_INIT ("dht", &run);
560
561
562/* end of gnunet-service-dht.c */
diff --git a/src/service/dht/gnunet-service-dht.h b/src/service/dht/gnunet-service-dht.h
new file mode 100644
index 000000000..893c90109
--- /dev/null
+++ b/src/service/dht/gnunet-service-dht.h
@@ -0,0 +1,212 @@
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
33
34#define DEBUG_DHT GNUNET_EXTRA_LOGGING
35
36/**
37 * Information we keep per underlay.
38 */
39struct GDS_Underlay;
40
41/**
42 * Configuration we use.
43 */
44extern const struct GNUNET_CONFIGURATION_Handle *GDS_cfg;
45
46/**
47 * Handle for the service.
48 */
49extern struct GNUNET_SERVICE_Handle *GDS_service;
50
51/**
52 * Our handle to the BLOCK library.
53 */
54extern struct GNUNET_BLOCK_Context *GDS_block_context;
55
56/**
57 * Handle for the statistics service.
58 */
59extern struct GNUNET_STATISTICS_Handle *GDS_stats;
60
61/**
62 * Our HELLO builder.
63 */
64extern struct GNUNET_HELLO_Builder *GDS_my_hello;
65
66/**
67 * Identity of this peer.
68 */
69extern struct GNUNET_PeerIdentity GDS_my_identity;
70
71/**
72 * Hash of the identity of this peer.
73 */
74extern struct GNUNET_HashCode GDS_my_identity_hash;
75
76/**
77 * Our private key.
78 */
79extern struct GNUNET_CRYPTO_EddsaPrivateKey GDS_my_private_key;
80
81
82/**
83 * Ask all underlays to connect to peer @a pid at @a address.
84 *
85 * @param pid identity of the peer we would connect to
86 * @param address an address of @a pid
87 */
88void
89GDS_u_try_connect (const struct GNUNET_PeerIdentity *pid,
90 const char *address);
91
92
93/**
94 * Send message to some other participant over the network. Note that
95 * sending is not guaranteeing that the other peer actually received the
96 * message. For any given @a target, the DHT must wait for the @a
97 * finished_cb to be called before calling send() again.
98 *
99 * @param u underlay to use for transmission
100 * @param target receiver identification
101 * @param msg message
102 * @param msg_size number of bytes in @a msg
103 * @param finished_cb function called once transmission is done
104 * (not called if @a target disconnects, then only the
105 * disconnect_cb is called).
106 * @param finished_cb_cls closure for @a finished_cb
107 */
108void
109GDS_u_send (struct GDS_Underlay *u,
110 struct GNUNET_DHTU_Target *target,
111 const void *msg,
112 size_t msg_size,
113 GNUNET_SCHEDULER_TaskCallback finished_cb,
114 void *finished_cb_cls);
115
116
117/**
118 * Drop a hold @a ph from underlay @a u.
119 *
120 * @param u the underlay controlling the hold
121 * @param ph the preference handle
122 */
123void
124GDS_u_drop (struct GDS_Underlay *u,
125 struct GNUNET_DHTU_PreferenceHandle *ph);
126
127
128/**
129 * Create a hold on @a target at underlay @a u.
130 *
131 * @param u the underlay controlling the target
132 * @param target the peer to hold the connection to
133 */
134struct GNUNET_DHTU_PreferenceHandle *
135GDS_u_hold (struct GDS_Underlay *u,
136 struct GNUNET_DHTU_Target *target);
137
138
139/**
140 * Handle a reply we've received from another peer. If the reply
141 * matches any of our pending queries, forward it to the respective
142 * client(s).
143 *
144 * @param bd block details
145 * @param query_hash hash of the original query, might not match key in @a bd
146 * @param get_path_length number of entries in @a get_path
147 * @param get_path path the reply has taken
148 * @return true on success, false on failures
149 */
150bool
151GDS_CLIENTS_handle_reply (const struct GNUNET_DATACACHE_Block *bd,
152 const struct GNUNET_HashCode *query_hash,
153 unsigned int get_path_length,
154 const struct GNUNET_DHT_PathElement *get_path);
155
156
157/**
158 * Check if some client is monitoring GET messages and notify
159 * them in that case. If tracked, @a path should include the local peer.
160 *
161 *
162 * @param options Options, for instance RecordRoute, DemultiplexEverywhere.
163 * @param type The type of data in the request.
164 * @param hop_count Hop count so far.
165 * @param desired_replication_level Desired replication level.
166 * @param key Key of the requested data.
167 */
168void
169GDS_CLIENTS_process_get (enum GNUNET_DHT_RouteOption options,
170 enum GNUNET_BLOCK_Type type,
171 uint32_t hop_count,
172 uint32_t desired_replication_level,
173 const struct GNUNET_HashCode *key);
174
175
176/**
177 * Check if some client is monitoring GET RESP messages and notify
178 * them in that case.
179 *
180 * @param bd block details
181 * @param get_path Peers on GET path (or NULL if not recorded).
182 * @param get_path_length number of entries in @a get_path.
183 */
184void
185GDS_CLIENTS_process_get_resp (const struct GNUNET_DATACACHE_Block *bd,
186 const struct GNUNET_DHT_PathElement *get_path,
187 unsigned int get_path_length);
188
189
190/**
191 * Check if some client is monitoring PUT messages and notify
192 * them in that case. The @a path should include our own
193 * peer ID (if recorded).
194 *
195 * @param bd details about the block
196 * @param hop_count Hop count so far.
197 * @param desired_replication_level Desired replication level.
198 */
199void
200GDS_CLIENTS_process_put (const struct GNUNET_DATACACHE_Block *bd,
201 uint32_t hop_count,
202 uint32_t desired_replication_level);
203
204/**
205 * Return the current NSE
206 *
207 * @return the current NSE as a logarithm
208 */
209double
210GDS_NSE_get (void);
211
212#endif
diff --git a/src/service/dht/gnunet-service-dht_clients.c b/src/service/dht/gnunet-service-dht_clients.c
new file mode 100644
index 000000000..c666265fe
--- /dev/null
+++ b/src/service/dht/gnunet-service-dht_clients.c
@@ -0,0 +1,1705 @@
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_hello_uri_lib.h"
31#include "gnunet_statistics_service.h"
32#include "gnunet-service-dht.h"
33#include "gnunet-service-dht_datacache.h"
34#include "gnunet-service-dht_neighbours.h"
35#include "dht.h"
36
37
38/**
39 * Enable slow sanity checks to debug issues.
40 * 0: do not check
41 * 1: check all external inputs
42 * 2: check internal computations as well
43 */
44#define SANITY_CHECKS 0
45
46/**
47 * Should routing details be logged to stderr (for debugging)?
48 */
49#define LOG_TRAFFIC(kind, ...) GNUNET_log_from (kind, "dht-traffic", \
50 __VA_ARGS__)
51
52#define LOG(kind, ...) GNUNET_log_from (kind, "dht-clients", __VA_ARGS__)
53
54
55/**
56 * Struct containing information about a client,
57 * handle to connect to it, and any pending messages
58 * that need to be sent to it.
59 */
60struct ClientHandle;
61
62
63/**
64 * Entry in the local forwarding map for a client's GET request.
65 */
66struct ClientQueryRecord
67{
68 /**
69 * The key this request was about
70 */
71 struct GNUNET_HashCode key;
72
73 /**
74 * Kept in a DLL with @e client.
75 */
76 struct ClientQueryRecord *next;
77
78 /**
79 * Kept in a DLL with @e client.
80 */
81 struct ClientQueryRecord *prev;
82
83 /**
84 * Client responsible for the request.
85 */
86 struct ClientHandle *ch;
87
88 /**
89 * Extended query (see gnunet_block_lib.h), allocated at the end of this struct.
90 */
91 const void *xquery;
92
93 /**
94 * Array of (hashes of) replies we have already seen for this request.
95 */
96 struct GNUNET_HashCode *seen_replies;
97
98 /**
99 * Pointer to this nodes heap location in the retry-heap (for fast removal)
100 */
101 struct GNUNET_CONTAINER_HeapNode *hnode;
102
103 /**
104 * What's the delay between re-try operations that we currently use for this
105 * request?
106 */
107 struct GNUNET_TIME_Relative retry_frequency;
108
109 /**
110 * What's the next time we should re-try this request?
111 */
112 struct GNUNET_TIME_Absolute retry_time;
113
114 /**
115 * The unique identifier of this request
116 */
117 uint64_t unique_id;
118
119 /**
120 * Number of bytes in xquery.
121 */
122 size_t xquery_size;
123
124 /**
125 * Number of entries in @e seen_replies.
126 */
127 unsigned int seen_replies_count;
128
129 /**
130 * Desired replication level
131 */
132 uint32_t replication;
133
134 /**
135 * Any message options for this request
136 */
137 enum GNUNET_DHT_RouteOption msg_options;
138
139 /**
140 * The type for the data for the GET request.
141 */
142 enum GNUNET_BLOCK_Type type;
143};
144
145
146/**
147 * Struct containing parameters of monitoring requests.
148 */
149struct ClientMonitorRecord
150{
151 /**
152 * Next element in DLL.
153 */
154 struct ClientMonitorRecord *next;
155
156 /**
157 * Previous element in DLL.
158 */
159 struct ClientMonitorRecord *prev;
160
161 /**
162 * Client to notify of these requests.
163 */
164 struct ClientHandle *ch;
165
166 /**
167 * Key of data of interest. All bits zero for 'all'.
168 */
169 struct GNUNET_HashCode key;
170
171 /**
172 * Type of blocks that are of interest
173 */
174 enum GNUNET_BLOCK_Type type;
175
176 /**
177 * Flag whether to notify about GET messages.
178 */
179 int16_t get;
180
181 /**
182 * Flag whether to notify about GET_REPONSE messages.
183 */
184 int16_t get_resp;
185
186 /**
187 * Flag whether to notify about PUT messages.
188 */
189 uint16_t put;
190
191};
192
193
194/**
195 * Struct containing information about a client,
196 * handle to connect to it, and any pending messages
197 * that need to be sent to it.
198 */
199struct ClientHandle
200{
201 /**
202 * Linked list of active queries of this client.
203 */
204 struct ClientQueryRecord *cqr_head;
205
206 /**
207 * Linked list of active queries of this client.
208 */
209 struct ClientQueryRecord *cqr_tail;
210
211 /**
212 * The handle to this client
213 */
214 struct GNUNET_SERVICE_Client *client;
215
216 /**
217 * The message queue to this client
218 */
219 struct GNUNET_MQ_Handle *mq;
220};
221
222
223/**
224 * Our handle to the BLOCK library.
225 */
226struct GNUNET_BLOCK_Context *GDS_block_context;
227
228/**
229 * Handle for the statistics service.
230 */
231struct GNUNET_STATISTICS_Handle *GDS_stats;
232
233/**
234 * Handle for the service.
235 */
236struct GNUNET_SERVICE_Handle *GDS_service;
237
238/**
239 * The configuration the DHT service is running with
240 */
241const struct GNUNET_CONFIGURATION_Handle *GDS_cfg;
242
243/**
244 * List of active monitoring requests.
245 */
246static struct ClientMonitorRecord *monitor_head;
247
248/**
249 * List of active monitoring requests.
250 */
251static struct ClientMonitorRecord *monitor_tail;
252
253/**
254 * Hashmap for fast key based lookup, maps keys to `struct ClientQueryRecord` entries.
255 */
256static struct GNUNET_CONTAINER_MultiHashMap *forward_map;
257
258/**
259 * Heap with all of our client's request, sorted by retry time (earliest on top).
260 */
261static struct GNUNET_CONTAINER_Heap *retry_heap;
262
263/**
264 * Task that re-transmits requests (using retry_heap).
265 */
266static struct GNUNET_SCHEDULER_Task *retry_task;
267
268
269/**
270 * Free data structures associated with the given query.
271 *
272 * @param record record to remove
273 */
274static void
275remove_client_query_record (struct ClientQueryRecord *record)
276{
277 struct ClientHandle *ch = record->ch;
278
279 GNUNET_CONTAINER_DLL_remove (ch->cqr_head,
280 ch->cqr_tail,
281 record);
282 GNUNET_assert (GNUNET_YES ==
283 GNUNET_CONTAINER_multihashmap_remove (forward_map,
284 &record->key,
285 record));
286 if (NULL != record->hnode)
287 GNUNET_CONTAINER_heap_remove_node (record->hnode);
288 GNUNET_array_grow (record->seen_replies,
289 record->seen_replies_count,
290 0);
291 GNUNET_free (record);
292}
293
294
295/**
296 * Functions with this signature are called whenever a local client is
297 * connects to us.
298 *
299 * @param cls closure (NULL for dht)
300 * @param client identification of the client
301 * @param mq message queue for talking to @a client
302 * @return our `struct ClientHandle` for @a client
303 */
304static void *
305client_connect_cb (void *cls,
306 struct GNUNET_SERVICE_Client *client,
307 struct GNUNET_MQ_Handle *mq)
308{
309 struct ClientHandle *ch;
310
311 (void) cls;
312 ch = GNUNET_new (struct ClientHandle);
313 ch->client = client;
314 ch->mq = mq;
315 return ch;
316}
317
318
319/**
320 * Functions with this signature are called whenever a client
321 * is disconnected on the network level.
322 *
323 * @param cls closure (NULL for dht)
324 * @param client identification of the client
325 * @param app_ctx our `struct ClientHandle` for @a client
326 */
327static void
328client_disconnect_cb (void *cls,
329 struct GNUNET_SERVICE_Client *client,
330 void *app_ctx)
331{
332 struct ClientHandle *ch = app_ctx;
333
334 (void) cls;
335 (void) client;
336 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
337 "Local client %p disconnects\n",
338 ch);
339 {
340 struct ClientMonitorRecord *next;
341
342 for (struct ClientMonitorRecord *monitor = monitor_head;
343 NULL != monitor;
344 monitor = next)
345 {
346 next = monitor->next;
347 if (monitor->ch != ch)
348 continue;
349 GNUNET_CONTAINER_DLL_remove (monitor_head,
350 monitor_tail,
351 monitor);
352 GNUNET_free (monitor);
353 }
354 }
355
356 {
357 struct ClientQueryRecord *cqr;
358
359 while (NULL != (cqr = ch->cqr_head))
360 remove_client_query_record (cqr);
361 }
362 GNUNET_free (ch);
363}
364
365
366/**
367 * Route the given request via the DHT. This includes updating
368 * the bloom filter and retransmission times, building the P2P
369 * message and initiating the routing operation.
370 *
371 * @param cqr request to transmit
372 */
373static void
374transmit_request (struct ClientQueryRecord *cqr)
375{
376 struct GNUNET_BLOCK_Group *bg;
377 struct GNUNET_CONTAINER_BloomFilter *peer_bf;
378
379 GNUNET_STATISTICS_update (GDS_stats,
380 "# GET requests from clients injected",
381 1,
382 GNUNET_NO);
383 bg = GNUNET_BLOCK_group_create (GDS_block_context,
384 cqr->type,
385 NULL, /* raw data */
386 0, /* raw data size */
387 "seen-set-size",
388 cqr->seen_replies_count,
389 NULL);
390 GNUNET_BLOCK_group_set_seen (bg,
391 cqr->seen_replies,
392 cqr->seen_replies_count);
393 peer_bf
394 = GNUNET_CONTAINER_bloomfilter_init (NULL,
395 DHT_BLOOM_SIZE,
396 GNUNET_CONSTANTS_BLOOMFILTER_K);
397 LOG (GNUNET_ERROR_TYPE_DEBUG,
398 "Initiating GET for %s, replication %u, already have %u replies\n",
399 GNUNET_h2s (&cqr->key),
400 cqr->replication,
401 cqr->seen_replies_count);
402 GDS_NEIGHBOURS_handle_get (cqr->type,
403 cqr->msg_options,
404 cqr->replication,
405 0 /* hop count */,
406 &cqr->key,
407 cqr->xquery,
408 cqr->xquery_size,
409 bg,
410 peer_bf);
411 GNUNET_BLOCK_group_destroy (bg);
412 GNUNET_CONTAINER_bloomfilter_free (peer_bf);
413
414 /* Exponential back-off for retries.
415 * max. is #GNUNET_TIME_STD_EXPONENTIAL_BACKOFF_THRESHOLD (15 min) */
416 cqr->retry_frequency = GNUNET_TIME_STD_BACKOFF (cqr->retry_frequency);
417 cqr->retry_time = GNUNET_TIME_relative_to_absolute (cqr->retry_frequency);
418}
419
420
421/**
422 * Task that looks at the #retry_heap and transmits all of the requests
423 * on the heap that are ready for transmission. Then re-schedules
424 * itself (unless the heap is empty).
425 *
426 * @param cls unused
427 */
428static void
429transmit_next_request_task (void *cls)
430{
431 struct ClientQueryRecord *cqr;
432
433 (void) cls;
434 retry_task = NULL;
435 while (NULL != (cqr = GNUNET_CONTAINER_heap_remove_root (retry_heap)))
436 {
437 cqr->hnode = NULL;
438 if (! GNUNET_TIME_absolute_is_past (cqr->retry_time))
439 {
440 cqr->hnode
441 = GNUNET_CONTAINER_heap_insert (retry_heap,
442 cqr,
443 cqr->retry_time.abs_value_us);
444 retry_task
445 = GNUNET_SCHEDULER_add_at (cqr->retry_time,
446 &transmit_next_request_task,
447 NULL);
448 return;
449 }
450 transmit_request (cqr);
451 cqr->hnode
452 = GNUNET_CONTAINER_heap_insert (retry_heap,
453 cqr,
454 cqr->retry_time.abs_value_us);
455 }
456}
457
458
459/**
460 * Check DHT PUT messages from the client.
461 *
462 * @param cls the client we received this message from
463 * @param dht_msg the actual message received
464 * @return #GNUNET_OK (always)
465 */
466static enum GNUNET_GenericReturnValue
467check_dht_local_put (void *cls,
468 const struct GNUNET_DHT_ClientPutMessage *dht_msg)
469{
470 uint32_t replication_level = ntohl (dht_msg->desired_replication_level);
471
472 (void) cls;
473 if (replication_level > GNUNET_DHT_MAXIMUM_REPLICATION_LEVEL)
474 {
475 GNUNET_break_op (0);
476 return GNUNET_SYSERR;
477 }
478 return GNUNET_OK;
479}
480
481
482/**
483 * Handler for PUT messages.
484 *
485 * @param cls the client we received this message from
486 * @param dht_msg the actual message received
487 */
488static void
489handle_dht_local_put (void *cls,
490 const struct GNUNET_DHT_ClientPutMessage *dht_msg)
491{
492 struct ClientHandle *ch = cls;
493 uint16_t size = ntohs (dht_msg->header.size);
494 uint32_t replication_level
495 = ntohl (dht_msg->desired_replication_level);
496 struct GNUNET_DATACACHE_Block bd = {
497 .key = dht_msg->key,
498 .expiration_time = GNUNET_TIME_absolute_ntoh (dht_msg->expiration),
499 .data = &dht_msg[1],
500 .data_size = size - sizeof (*dht_msg),
501 .type = ntohl (dht_msg->type),
502 .ro = (enum GNUNET_DHT_RouteOption) ntohl (dht_msg->options)
503 };
504
505 LOG (GNUNET_ERROR_TYPE_DEBUG,
506 "Handling local PUT of %lu-bytes for query %s of type %u\n",
507 (unsigned long) (size - sizeof(struct GNUNET_DHT_ClientPutMessage)),
508 GNUNET_h2s (&dht_msg->key),
509 (unsigned int) bd.type);
510#if SANITY_CHECKS > 0
511 if (GNUNET_OK !=
512 GNUNET_BLOCK_check_block (GDS_block_context,
513 bd.type,
514 bd.data,
515 bd.data_size))
516 {
517 GNUNET_break (0);
518 return;
519 }
520#endif
521 GNUNET_STATISTICS_update (GDS_stats,
522 "# PUT requests received from clients",
523 1,
524 GNUNET_NO);
525 LOG_TRAFFIC (GNUNET_ERROR_TYPE_DEBUG,
526 "CLIENT-PUT %s\n",
527 GNUNET_h2s_full (&dht_msg->key));
528 /* give to local clients */
529 GNUNET_break (GDS_CLIENTS_handle_reply (&bd,
530 &bd.key,
531 0, NULL /* get path */));
532
533 {
534 struct GNUNET_CONTAINER_BloomFilter *peer_bf;
535
536 peer_bf
537 = GNUNET_CONTAINER_bloomfilter_init (NULL,
538 DHT_BLOOM_SIZE,
539 GNUNET_CONSTANTS_BLOOMFILTER_K);
540 /* store locally */
541 if ( (0 != (bd.ro & GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE)) ||
542 (GDS_am_closest_peer (&dht_msg->key,
543 peer_bf)))
544 GDS_DATACACHE_handle_put (&bd);
545 /* route to other peers */
546 if (GNUNET_OK !=
547 GDS_NEIGHBOURS_handle_put (&bd,
548 replication_level,
549 0 /* hop count */,
550 peer_bf))
551 {
552 GNUNET_STATISTICS_update (GDS_stats,
553 "# Local PUT requests not routed",
554 1,
555 GNUNET_NO);
556 }
557 GNUNET_CONTAINER_bloomfilter_free (peer_bf);
558 }
559 GDS_CLIENTS_process_put (
560 &bd,
561 0, /* hop count */
562 replication_level);
563 GNUNET_SERVICE_client_continue (ch->client);
564}
565
566
567/**
568 * Handle a result from local datacache for a GET operation.
569 *
570 * @param cls the `struct ClientHandle` of the client doing the query
571 * @param bd details about the block that was found
572 */
573static void
574handle_local_result (void *cls,
575 const struct GNUNET_DATACACHE_Block *bd)
576{
577 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
578 "Datacache provided result for query key %s\n",
579 GNUNET_h2s (&bd->key));
580 GNUNET_break (GDS_CLIENTS_handle_reply (bd,
581 &bd->key,
582 0, NULL /* get_path */));
583}
584
585
586/**
587 * Check DHT GET messages from the client.
588 *
589 * @param cls the client we received this message from
590 * @param get the actual message received
591 * @return #GNUNET_OK (always)
592 */
593static enum GNUNET_GenericReturnValue
594check_dht_local_get (void *cls,
595 const struct GNUNET_DHT_ClientGetMessage *get)
596{
597 (void) cls;
598 (void) get;
599 /* always well-formed */
600 return GNUNET_OK;
601}
602
603
604/**
605 * Handler for DHT GET messages from the client.
606 *
607 * @param cls the client we received this message from
608 * @param get the actual message received
609 */
610static void
611handle_dht_local_get (void *cls,
612 const struct GNUNET_DHT_ClientGetMessage *get)
613{
614 struct ClientHandle *ch = cls;
615 struct ClientQueryRecord *cqr;
616 uint16_t size = ntohs (get->header.size);
617 const char *xquery = (const char *) &get[1];
618 size_t xquery_size = size - sizeof(struct GNUNET_DHT_ClientGetMessage);
619
620 LOG (GNUNET_ERROR_TYPE_DEBUG,
621 "Received GET request for %s from local client %p, xq: %.*s\n",
622 GNUNET_h2s (&get->key),
623 ch->client,
624 (int) xquery_size,
625 xquery);
626 GNUNET_STATISTICS_update (GDS_stats,
627 "# GET requests received from clients",
628 1,
629 GNUNET_NO);
630 LOG_TRAFFIC (GNUNET_ERROR_TYPE_DEBUG,
631 "CLIENT-GET %s\n",
632 GNUNET_h2s_full (&get->key));
633
634 cqr = GNUNET_malloc (sizeof(struct ClientQueryRecord) + xquery_size);
635 cqr->key = get->key;
636 cqr->ch = ch;
637 cqr->xquery = (const void *) &cqr[1];
638 GNUNET_memcpy (&cqr[1],
639 xquery,
640 xquery_size);
641 cqr->hnode = GNUNET_CONTAINER_heap_insert (retry_heap,
642 cqr,
643 0);
644 cqr->retry_frequency = GNUNET_TIME_UNIT_SECONDS;
645 cqr->retry_time = GNUNET_TIME_absolute_get ();
646 cqr->unique_id = get->unique_id;
647 cqr->xquery_size = xquery_size;
648 cqr->replication = ntohl (get->desired_replication_level);
649 cqr->msg_options = (enum GNUNET_DHT_RouteOption) ntohl (get->options);
650 cqr->type = ntohl (get->type);
651 GNUNET_CONTAINER_DLL_insert (ch->cqr_head,
652 ch->cqr_tail,
653 cqr);
654 GNUNET_CONTAINER_multihashmap_put (forward_map,
655 &cqr->key,
656 cqr,
657 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
658 GDS_CLIENTS_process_get (cqr->msg_options,
659 cqr->type,
660 0, /* hop count */
661 cqr->replication,
662 &get->key);
663 /* start remote requests */
664 if (NULL != retry_task)
665 GNUNET_SCHEDULER_cancel (retry_task);
666 retry_task = GNUNET_SCHEDULER_add_now (&transmit_next_request_task,
667 NULL);
668 /* perform local lookup */
669 GDS_DATACACHE_handle_get (&get->key,
670 cqr->type,
671 cqr->xquery,
672 xquery_size,
673 NULL,
674 &handle_local_result,
675 ch);
676 GNUNET_SERVICE_client_continue (ch->client);
677}
678
679
680/**
681 * Closure for #find_by_unique_id().
682 */
683struct FindByUniqueIdContext
684{
685 /**
686 * Where to store the result, if found.
687 */
688 struct ClientQueryRecord *cqr;
689
690 /**
691 * Unique ID to look for.
692 */
693 uint64_t unique_id;
694};
695
696
697/**
698 * Function called for each existing DHT record for the given
699 * query. Checks if it matches the UID given in the closure
700 * and if so returns the entry as a result.
701 *
702 * @param cls the search context
703 * @param key query for the lookup (not used)
704 * @param value the `struct ClientQueryRecord`
705 * @return #GNUNET_YES to continue iteration (result not yet found)
706 */
707static enum GNUNET_GenericReturnValue
708find_by_unique_id (void *cls,
709 const struct GNUNET_HashCode *key,
710 void *value)
711{
712 struct FindByUniqueIdContext *fui_ctx = cls;
713 struct ClientQueryRecord *cqr = value;
714
715 if (cqr->unique_id != fui_ctx->unique_id)
716 return GNUNET_YES;
717 fui_ctx->cqr = cqr;
718 return GNUNET_NO;
719}
720
721
722/**
723 * Check "GET result seen" messages from the client.
724 *
725 * @param cls the client we received this message from
726 * @param seen the actual message received
727 * @return #GNUNET_OK if @a seen is well-formed
728 */
729static enum GNUNET_GenericReturnValue
730check_dht_local_get_result_seen (
731 void *cls,
732 const struct GNUNET_DHT_ClientGetResultSeenMessage *seen)
733{
734 uint16_t size = ntohs (seen->header.size);
735 unsigned int hash_count =
736 (size - sizeof(*seen))
737 / sizeof(struct GNUNET_HashCode);
738
739 if (size != sizeof(*seen) + hash_count * sizeof(struct GNUNET_HashCode))
740 {
741 GNUNET_break (0);
742 return GNUNET_SYSERR;
743 }
744 return GNUNET_OK;
745}
746
747
748/**
749 * Handler for "GET result seen" messages from the client.
750 *
751 * @param cls the client we received this message from
752 * @param seen the actual message received
753 */
754static void
755handle_dht_local_get_result_seen (
756 void *cls,
757 const struct GNUNET_DHT_ClientGetResultSeenMessage *seen)
758{
759 struct ClientHandle *ch = cls;
760 uint16_t size = ntohs (seen->header.size);
761 unsigned int hash_count = (size - sizeof(*seen))
762 / sizeof(struct GNUNET_HashCode);
763 const struct GNUNET_HashCode *hc = (const struct GNUNET_HashCode*) &seen[1];
764 struct FindByUniqueIdContext fui_ctx = {
765 .unique_id = seen->unique_id
766 };
767 unsigned int old_count;
768 struct ClientQueryRecord *cqr;
769
770 GNUNET_CONTAINER_multihashmap_get_multiple (forward_map,
771 &seen->key,
772 &find_by_unique_id,
773 &fui_ctx);
774 if (NULL == (cqr = fui_ctx.cqr))
775 {
776 GNUNET_break (0);
777 GNUNET_SERVICE_client_drop (ch->client);
778 return;
779 }
780 /* finally, update 'seen' list */
781 old_count = cqr->seen_replies_count;
782 GNUNET_array_grow (cqr->seen_replies,
783 cqr->seen_replies_count,
784 cqr->seen_replies_count + hash_count);
785 GNUNET_memcpy (&cqr->seen_replies[old_count],
786 hc,
787 sizeof(struct GNUNET_HashCode) * hash_count);
788}
789
790
791/**
792 * Closure for #remove_by_unique_id().
793 */
794struct RemoveByUniqueIdContext
795{
796 /**
797 * Client that issued the removal request.
798 */
799 struct ClientHandle *ch;
800
801 /**
802 * Unique ID of the request.
803 */
804 uint64_t unique_id;
805};
806
807
808/**
809 * Iterator over hash map entries that frees all entries
810 * that match the given client and unique ID.
811 *
812 * @param cls unique ID and client to search for in source routes
813 * @param key current key code
814 * @param value value in the hash map, a ClientQueryRecord
815 * @return #GNUNET_YES (we should continue to iterate)
816 */
817static enum GNUNET_GenericReturnValue
818remove_by_unique_id (void *cls,
819 const struct GNUNET_HashCode *key,
820 void *value)
821{
822 const struct RemoveByUniqueIdContext *ctx = cls;
823 struct ClientQueryRecord *cqr = value;
824
825 if (cqr->unique_id != ctx->unique_id)
826 return GNUNET_YES;
827 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
828 "Removing client %p's record for key %s (by unique id)\n",
829 ctx->ch->client,
830 GNUNET_h2s (key));
831 remove_client_query_record (cqr);
832 return GNUNET_YES;
833}
834
835
836/**
837 * Handler for any generic DHT stop messages, calls the appropriate handler
838 * depending on message type (if processed locally)
839 *
840 * @param cls client we received this message from
841 * @param dht_stop_msg the actual message received
842 *
843 */
844static void
845handle_dht_local_get_stop (
846 void *cls,
847 const struct GNUNET_DHT_ClientGetStopMessage *dht_stop_msg)
848{
849 struct ClientHandle *ch = cls;
850 struct RemoveByUniqueIdContext ctx;
851
852 GNUNET_STATISTICS_update (GDS_stats,
853 "# GET STOP requests received from clients",
854 1,
855 GNUNET_NO);
856 LOG (GNUNET_ERROR_TYPE_DEBUG,
857 "Received GET STOP request for %s from local client %p\n",
858 GNUNET_h2s (&dht_stop_msg->key),
859 ch->client);
860 ctx.ch = ch;
861 ctx.unique_id = dht_stop_msg->unique_id;
862 GNUNET_CONTAINER_multihashmap_get_multiple (forward_map,
863 &dht_stop_msg->key,
864 &remove_by_unique_id,
865 &ctx);
866 GNUNET_SERVICE_client_continue (ch->client);
867}
868
869
870/**
871 * Closure for #forward_reply()
872 */
873struct ForwardReplyContext
874{
875 /**
876 * Block details.
877 */
878 const struct GNUNET_DATACACHE_Block *bd;
879
880 /**
881 * GET path taken.
882 */
883 const struct GNUNET_DHT_PathElement *get_path;
884
885 /**
886 * Number of entries in @e get_path.
887 */
888 unsigned int get_path_length;
889
890};
891
892
893/**
894 * Iterator over hash map entries that send a given reply to
895 * each of the matching clients. With some tricky recycling
896 * of the buffer.
897 *
898 * @param cls the `struct ForwardReplyContext`
899 * @param query_hash hash of the query for which this may be a reply
900 * @param value value in the hash map, a ClientQueryRecord
901 * @return #GNUNET_YES (we should continue to iterate),
902 * if the result is mal-formed, #GNUNET_NO
903 */
904static enum GNUNET_GenericReturnValue
905forward_reply (void *cls,
906 const struct GNUNET_HashCode *query_hash,
907 void *value)
908{
909 struct ForwardReplyContext *frc = cls;
910 struct ClientQueryRecord *record = value;
911 const struct GNUNET_DATACACHE_Block *bd = frc->bd;
912 struct GNUNET_MQ_Envelope *env;
913 struct GNUNET_DHT_ClientResultMessage *reply;
914 enum GNUNET_BLOCK_ReplyEvaluationResult eval;
915 bool do_free;
916 struct GNUNET_HashCode ch;
917 struct GNUNET_DHT_PathElement *paths;
918 bool truncated = (0 != (bd->ro & GNUNET_DHT_RO_TRUNCATED));
919 size_t xsize = bd->data_size;
920
921 LOG_TRAFFIC (GNUNET_ERROR_TYPE_DEBUG,
922 "CLIENT-RESULT %s\n",
923 GNUNET_h2s_full (&bd->key));
924 if ( (record->type != GNUNET_BLOCK_TYPE_ANY) &&
925 (record->type != bd->type) )
926 {
927 LOG (GNUNET_ERROR_TYPE_DEBUG,
928 "Record type mismatch, not passing request for key %s to local client\n",
929 GNUNET_h2s (&bd->key));
930 GNUNET_STATISTICS_update (GDS_stats,
931 "# Key match, type mismatches in REPLY to CLIENT",
932 1,
933 GNUNET_NO);
934 return GNUNET_YES; /* type mismatch */
935 }
936 if ( (0 == (record->msg_options & GNUNET_DHT_RO_FIND_APPROXIMATE)) &&
937 (0 != GNUNET_memcmp (&bd->key,
938 query_hash)) )
939 {
940 GNUNET_STATISTICS_update (GDS_stats,
941 "# Inexact key match, but exact match required",
942 1,
943 GNUNET_NO);
944 return GNUNET_YES; /* type mismatch */
945 }
946 GNUNET_CRYPTO_hash (bd->data,
947 bd->data_size,
948 &ch);
949 for (unsigned int i = 0; i < record->seen_replies_count; i++)
950 if (0 ==
951 GNUNET_memcmp (&record->seen_replies[i],
952 &ch))
953 {
954 LOG (GNUNET_ERROR_TYPE_DEBUG,
955 "Duplicate reply, not passing request for key %s to local client\n",
956 GNUNET_h2s (&bd->key));
957 GNUNET_STATISTICS_update (GDS_stats,
958 "# Duplicate REPLIES to CLIENT request dropped",
959 1,
960 GNUNET_NO);
961 return GNUNET_YES; /* duplicate */
962 }
963 eval
964 = GNUNET_BLOCK_check_reply (GDS_block_context,
965 record->type,
966 NULL,
967 &bd->key,
968 record->xquery,
969 record->xquery_size,
970 bd->data,
971 bd->data_size);
972 LOG (GNUNET_ERROR_TYPE_DEBUG,
973 "Evaluation result is %d for key %s for local client's query\n",
974 (int) eval,
975 GNUNET_h2s (&bd->key));
976 switch (eval)
977 {
978 case GNUNET_BLOCK_REPLY_OK_LAST:
979 do_free = true;
980 break;
981 case GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED:
982 case GNUNET_BLOCK_REPLY_OK_MORE:
983 GNUNET_array_append (record->seen_replies,
984 record->seen_replies_count,
985 ch);
986 do_free = false;
987 break;
988 case GNUNET_BLOCK_REPLY_OK_DUPLICATE:
989 /* should be impossible to encounter here */
990 GNUNET_break (0);
991 return GNUNET_YES;
992 case GNUNET_BLOCK_REPLY_IRRELEVANT:
993 return GNUNET_YES;
994 default:
995 GNUNET_break (0);
996 return GNUNET_NO;
997 }
998 GNUNET_STATISTICS_update (GDS_stats,
999 "# RESULTS queued for clients",
1000 1,
1001 GNUNET_NO);
1002 xsize += (frc->get_path_length + bd->put_path_length)
1003 * sizeof(struct GNUNET_DHT_PathElement);
1004 if (truncated)
1005 xsize += sizeof (struct GNUNET_PeerIdentity);
1006
1007#if SUPER_REDUNDANT_CHECK
1008 GNUNET_break (0 ==
1009 GNUNET_DHT_verify_path (bd->data,
1010 bd->data_size,
1011 bd->expiration_time,
1012 truncated
1013 ? &bd->trunc_peer
1014 : NULL,
1015 bd->put_path,
1016 bd->put_path_length,
1017 frc->get_path,
1018 frc->get_path_length,
1019 &GDS_my_identity));
1020#endif
1021
1022 env = GNUNET_MQ_msg_extra (reply,
1023 xsize,
1024 GNUNET_MESSAGE_TYPE_DHT_CLIENT_RESULT);
1025 reply->type = htonl (bd->type);
1026 reply->options = htonl (bd->ro);
1027 reply->get_path_length = htonl (frc->get_path_length);
1028 reply->put_path_length = htonl (bd->put_path_length);
1029 reply->unique_id = record->unique_id;
1030 reply->expiration = GNUNET_TIME_absolute_hton (bd->expiration_time);
1031 reply->key = *query_hash;
1032 if (truncated)
1033 {
1034 void *tgt = &reply[1];
1035
1036 GNUNET_memcpy (tgt,
1037 &bd->trunc_peer,
1038 sizeof (struct GNUNET_PeerIdentity));
1039 paths = (struct GNUNET_DHT_PathElement *)
1040 (tgt + sizeof (struct GNUNET_PeerIdentity));
1041 }
1042 else
1043 {
1044 paths = (struct GNUNET_DHT_PathElement *) &reply[1];
1045 }
1046 GNUNET_memcpy (paths,
1047 bd->put_path,
1048 sizeof(struct GNUNET_DHT_PathElement)
1049 * bd->put_path_length);
1050 GNUNET_memcpy (&paths[bd->put_path_length],
1051 frc->get_path,
1052 sizeof(struct GNUNET_DHT_PathElement)
1053 * frc->get_path_length);
1054 GNUNET_memcpy (&paths[frc->get_path_length + bd->put_path_length],
1055 bd->data,
1056 bd->data_size);
1057 LOG (GNUNET_ERROR_TYPE_DEBUG,
1058 "Sending reply to query %s for client %p\n",
1059 GNUNET_h2s (query_hash),
1060 record->ch->client);
1061 GNUNET_MQ_send (record->ch->mq,
1062 env);
1063 if (GNUNET_YES == do_free)
1064 remove_client_query_record (record);
1065 return GNUNET_YES;
1066}
1067
1068
1069bool
1070GDS_CLIENTS_handle_reply (const struct GNUNET_DATACACHE_Block *bd,
1071 const struct GNUNET_HashCode *query_hash,
1072 unsigned int get_path_length,
1073 const struct GNUNET_DHT_PathElement *get_path)
1074{
1075 struct ForwardReplyContext frc;
1076 size_t msize = sizeof (struct GNUNET_DHT_ClientResultMessage)
1077 + bd->data_size
1078 + (get_path_length + bd->put_path_length)
1079 * sizeof(struct GNUNET_DHT_PathElement);
1080#if SANITY_CHECKS > 1
1081 bool truncated = (0 != (bd->ro & GNUNET_DHT_RO_TRUNCATED));
1082#endif
1083
1084 if (msize >= GNUNET_MAX_MESSAGE_SIZE)
1085 {
1086 GNUNET_break (0);
1087 return false;
1088 }
1089#if SANITY_CHECKS > 1
1090 if (0 !=
1091 GNUNET_DHT_verify_path (bd->data,
1092 bd->data_size,
1093 bd->expiration_time,
1094 truncated
1095 ? &bd->trunc_peer
1096 : NULL,
1097 bd->put_path,
1098 bd->put_path_length,
1099 get_path,
1100 get_path_length,
1101 &GDS_my_identity))
1102 {
1103 GNUNET_break (0);
1104 return false;
1105 }
1106#endif
1107 frc.bd = bd;
1108 frc.get_path = get_path;
1109 frc.get_path_length = get_path_length;
1110 LOG (GNUNET_ERROR_TYPE_DEBUG,
1111 "Forwarding reply for query hash %s with GPL %u and PPL %u to client\n",
1112 GNUNET_h2s (query_hash),
1113 get_path_length,
1114 bd->put_path_length);
1115 if (0 ==
1116 GNUNET_CONTAINER_multihashmap_get_multiple (forward_map,
1117 query_hash,
1118 &forward_reply,
1119 &frc))
1120 {
1121 LOG (GNUNET_ERROR_TYPE_DEBUG,
1122 "No matching client for reply for query %s\n",
1123 GNUNET_h2s (query_hash));
1124 GNUNET_STATISTICS_update (GDS_stats,
1125 "# REPLIES ignored for CLIENTS (no match)",
1126 1,
1127 GNUNET_NO);
1128 }
1129 return true;
1130}
1131
1132
1133/* **************** HELLO logic ***************** */
1134
1135/**
1136 * Handler for HELLO GET message. Reply to client
1137 * with a URL of our HELLO.
1138 *
1139 * @param cls the client we received this message from
1140 * @param msg the actual message received
1141 *
1142 */
1143static void
1144handle_dht_local_hello_get (void *cls,
1145 const struct GNUNET_MessageHeader *msg)
1146{
1147 struct ClientHandle *ch = cls;
1148 char *url = GNUNET_HELLO_builder_to_url (GDS_my_hello,
1149 &GDS_my_private_key);
1150 size_t slen = strlen (url) + 1;
1151 struct GNUNET_MessageHeader *hdr;
1152 struct GNUNET_MQ_Envelope *env;
1153
1154 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1155 "Handling request from local client for my HELLO\n");
1156 env = GNUNET_MQ_msg_extra (hdr,
1157 slen,
1158 GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_URL);
1159 memcpy (&hdr[1],
1160 url,
1161 slen);
1162 GNUNET_free (url);
1163 GNUNET_MQ_send (ch->mq,
1164 env);
1165 GNUNET_SERVICE_client_continue (ch->client);
1166}
1167
1168
1169/**
1170 * Process a client HELLO message received from the service.
1171 *
1172 * @param cls the client we received this message from
1173 * @param hdr HELLO URL message from the service.
1174 * @return #GNUNET_OK if @a hdr is well-formed
1175 */
1176static enum GNUNET_GenericReturnValue
1177check_dht_local_hello_offer (void *cls,
1178 const struct GNUNET_MessageHeader *hdr)
1179{
1180 uint16_t len = ntohs (hdr->size);
1181 const char *buf = (const char *) &hdr[1];
1182
1183 (void) cls;
1184 if ('\0' != buf[len - sizeof (*hdr) - 1])
1185 {
1186 GNUNET_break (0);
1187 return GNUNET_SYSERR;
1188 }
1189 return GNUNET_OK;
1190}
1191
1192
1193/**
1194 * Handler for HELLO OFFER message. Try to use the
1195 * HELLO to connect to another peer.
1196 *
1197 * @param cls the client we received this message from
1198 * @param msg the actual message received
1199 */
1200static void
1201handle_dht_local_hello_offer (void *cls,
1202 const struct GNUNET_MessageHeader *msg)
1203{
1204 struct ClientHandle *ch = cls;
1205 const char *url = (const char *) &msg[1];
1206 struct GNUNET_HELLO_Builder *b;
1207
1208 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1209 "Local client provided HELLO URL %s\n",
1210 url);
1211 b = GNUNET_HELLO_builder_from_url (url);
1212 if (NULL == b)
1213 {
1214 GNUNET_break (0);
1215 GNUNET_SERVICE_client_drop (ch->client);
1216 return;
1217 }
1218 GNUNET_SERVICE_client_continue (ch->client);
1219 GNUNET_HELLO_builder_iterate (b,
1220 &GDS_try_connect,
1221 NULL);
1222 GNUNET_HELLO_builder_free (b);
1223}
1224
1225
1226/* ************* logic for monitors ************** */
1227
1228
1229/**
1230 * Handler for monitor start messages
1231 *
1232 * @param cls the client we received this message from
1233 * @param msg the actual message received
1234 *
1235 */
1236static void
1237handle_dht_local_monitor (void *cls,
1238 const struct GNUNET_DHT_MonitorStartStopMessage *msg)
1239{
1240 struct ClientHandle *ch = cls;
1241 struct ClientMonitorRecord *r;
1242
1243 r = GNUNET_new (struct ClientMonitorRecord);
1244 r->ch = ch;
1245 r->type = ntohl (msg->type);
1246 r->get = ntohs (msg->get);
1247 r->get_resp = ntohs (msg->get_resp);
1248 r->put = ntohs (msg->put);
1249 if (0 != ntohs (msg->filter_key))
1250 r->key = msg->key;
1251 GNUNET_CONTAINER_DLL_insert (monitor_head,
1252 monitor_tail,
1253 r);
1254 GNUNET_SERVICE_client_continue (ch->client);
1255}
1256
1257
1258/**
1259 * Handler for monitor stop messages
1260 *
1261 * @param cls the client we received this message from
1262 * @param msg the actual message received
1263 */
1264static void
1265handle_dht_local_monitor_stop (
1266 void *cls,
1267 const struct GNUNET_DHT_MonitorStartStopMessage *msg)
1268{
1269 struct ClientHandle *ch = cls;
1270
1271 GNUNET_SERVICE_client_continue (ch->client);
1272 for (struct ClientMonitorRecord *r = monitor_head;
1273 NULL != r;
1274 r = r->next)
1275 {
1276 bool keys_match;
1277
1278 keys_match =
1279 (GNUNET_is_zero (&r->key))
1280 ? (0 == ntohs (msg->filter_key))
1281 : ( (0 != ntohs (msg->filter_key)) &&
1282 (! GNUNET_memcmp (&r->key,
1283 &msg->key)) );
1284 if ( (ch == r->ch) &&
1285 (ntohl (msg->type) == r->type) &&
1286 (r->get == msg->get) &&
1287 (r->get_resp == msg->get_resp) &&
1288 (r->put == msg->put) &&
1289 keys_match)
1290 {
1291 GNUNET_CONTAINER_DLL_remove (monitor_head,
1292 monitor_tail,
1293 r);
1294 GNUNET_free (r);
1295 return; /* Delete only ONE entry */
1296 }
1297 }
1298}
1299
1300
1301/**
1302 * Function to call by #for_matching_monitors().
1303 *
1304 * @param cls closure
1305 * @param m a matching monitor
1306 */
1307typedef void
1308(*MonitorAction)(void *cls,
1309 struct ClientMonitorRecord *m);
1310
1311
1312/**
1313 * Call @a cb on all monitors that watch for blocks of @a type
1314 * and key @a key.
1315 *
1316 * @param type the type to match
1317 * @param key the key to match
1318 * @param cb function to call
1319 * @param cb_cls closure for @a cb
1320 */
1321static void
1322for_matching_monitors (enum GNUNET_BLOCK_Type type,
1323 const struct GNUNET_HashCode *key,
1324 MonitorAction cb,
1325 void *cb_cls)
1326{
1327 struct ClientHandle **cl = NULL;
1328 unsigned int cl_size = 0;
1329
1330 for (struct ClientMonitorRecord *m = monitor_head;
1331 NULL != m;
1332 m = m->next)
1333 {
1334 bool found = false;
1335
1336 if ( (GNUNET_BLOCK_TYPE_ANY != m->type) &&
1337 (m->type != type) )
1338 continue;
1339 if ( (! GNUNET_is_zero (&m->key)) &&
1340 (0 ==
1341 GNUNET_memcmp (key,
1342 &m->key)) )
1343 continue;
1344 /* Don't send duplicates */
1345 for (unsigned i = 0; i < cl_size; i++)
1346 if (cl[i] == m->ch)
1347 {
1348 found = true;
1349 break;
1350 }
1351 if (found)
1352 continue;
1353 GNUNET_array_append (cl,
1354 cl_size,
1355 m->ch);
1356 cb (cb_cls,
1357 m);
1358 }
1359 GNUNET_free (cl);
1360}
1361
1362
1363/**
1364 * Closure for #get_action();
1365 */
1366struct GetActionContext
1367{
1368 enum GNUNET_DHT_RouteOption options;
1369 enum GNUNET_BLOCK_Type type;
1370 uint32_t hop_count;
1371 uint32_t desired_replication_level;
1372 struct GNUNET_PeerIdentity trunc_peer;
1373 const struct GNUNET_HashCode *key;
1374};
1375
1376
1377/**
1378 * Function called on monitors that match a GET.
1379 * Sends the GET notification to the monitor.
1380 *
1381 * @param cls a `struct GetActionContext`
1382 * @param m a matching monitor
1383 */
1384static void
1385get_action (void *cls,
1386 struct ClientMonitorRecord *m)
1387{
1388 struct GetActionContext *gac = cls;
1389 struct GNUNET_MQ_Envelope *env;
1390 struct GNUNET_DHT_MonitorGetMessage *mmsg;
1391
1392 env = GNUNET_MQ_msg (mmsg,
1393 GNUNET_MESSAGE_TYPE_DHT_MONITOR_GET);
1394 mmsg->options = htonl (gac->options);
1395 mmsg->type = htonl (gac->type);
1396 mmsg->hop_count = htonl (gac->hop_count);
1397 mmsg->desired_replication_level = htonl (gac->desired_replication_level);
1398 mmsg->key = *gac->key;
1399 GNUNET_MQ_send (m->ch->mq,
1400 env);
1401}
1402
1403
1404void
1405GDS_CLIENTS_process_get (enum GNUNET_DHT_RouteOption options,
1406 enum GNUNET_BLOCK_Type type,
1407 uint32_t hop_count,
1408 uint32_t desired_replication_level,
1409 const struct GNUNET_HashCode *key)
1410{
1411 struct GetActionContext gac = {
1412 .options = options,
1413 .type = type,
1414 .hop_count = hop_count,
1415 .desired_replication_level = desired_replication_level,
1416 .key = key
1417 };
1418
1419 for_matching_monitors (type,
1420 key,
1421 &get_action,
1422 &gac);
1423}
1424
1425
1426/**
1427 * Closure for response_action().
1428 */
1429struct ResponseActionContext
1430{
1431 const struct GNUNET_DATACACHE_Block *bd;
1432 const struct GNUNET_DHT_PathElement *get_path;
1433 unsigned int get_path_length;
1434};
1435
1436
1437/**
1438 * Function called on monitors that match a response.
1439 * Sends the response notification to the monitor.
1440 *
1441 * @param cls a `struct ResponseActionContext`
1442 * @param m a matching monitor
1443 */
1444static void
1445response_action (void *cls,
1446 struct ClientMonitorRecord *m)
1447{
1448 const struct ResponseActionContext *resp_ctx = cls;
1449 const struct GNUNET_DATACACHE_Block *bd = resp_ctx->bd;
1450 bool truncated = (0 != (bd->ro & GNUNET_DHT_RO_TRUNCATED));
1451 struct GNUNET_MQ_Envelope *env;
1452 struct GNUNET_DHT_MonitorGetRespMessage *mmsg;
1453 struct GNUNET_DHT_PathElement *path;
1454 size_t msize;
1455
1456 msize = bd->data_size;
1457 msize += (resp_ctx->get_path_length + bd->put_path_length)
1458 * sizeof(struct GNUNET_DHT_PathElement);
1459 if (truncated)
1460 msize += sizeof (struct GNUNET_PeerIdentity);
1461 env = GNUNET_MQ_msg_extra (mmsg,
1462 msize,
1463 GNUNET_MESSAGE_TYPE_DHT_MONITOR_GET_RESP);
1464 mmsg->type = htonl (bd->type);
1465 mmsg->put_path_length = htonl (bd->put_path_length);
1466 mmsg->get_path_length = htonl (resp_ctx->get_path_length);
1467 mmsg->expiration_time = GNUNET_TIME_absolute_hton (bd->expiration_time);
1468 mmsg->key = bd->key;
1469 if (truncated)
1470 {
1471 void *tgt = &mmsg[1];
1472
1473 GNUNET_memcpy (tgt,
1474 &bd->trunc_peer,
1475 sizeof (struct GNUNET_PeerIdentity));
1476 path = (struct GNUNET_DHT_PathElement *)
1477 (tgt + sizeof (struct GNUNET_PeerIdentity));
1478 }
1479 else
1480 {
1481 path = (struct GNUNET_DHT_PathElement *) &mmsg[1];
1482 }
1483 GNUNET_memcpy (path,
1484 bd->put_path,
1485 bd->put_path_length * sizeof(struct GNUNET_DHT_PathElement));
1486 GNUNET_memcpy (path,
1487 resp_ctx->get_path,
1488 resp_ctx->get_path_length
1489 * sizeof(struct GNUNET_DHT_PathElement));
1490 GNUNET_memcpy (&path[resp_ctx->get_path_length],
1491 bd->data,
1492 bd->data_size);
1493 GNUNET_MQ_send (m->ch->mq,
1494 env);
1495}
1496
1497
1498void
1499GDS_CLIENTS_process_get_resp (const struct GNUNET_DATACACHE_Block *bd,
1500 const struct GNUNET_DHT_PathElement *get_path,
1501 unsigned int get_path_length)
1502{
1503 struct ResponseActionContext rac = {
1504 .bd = bd,
1505 .get_path = get_path,
1506 .get_path_length = get_path_length
1507 };
1508
1509 for_matching_monitors (bd->type,
1510 &bd->key,
1511 &response_action,
1512 &rac);
1513}
1514
1515
1516/**
1517 * Closure for put_action().
1518 */
1519struct PutActionContext
1520{
1521 const struct GNUNET_DATACACHE_Block *bd;
1522 uint32_t hop_count;
1523 uint32_t desired_replication_level;
1524};
1525
1526
1527/**
1528 * Function called on monitors that match a PUT.
1529 * Sends the PUT notification to the monitor.
1530 *
1531 * @param cls a `struct PutActionContext`
1532 * @param m a matching monitor
1533 */
1534static void
1535put_action (void *cls,
1536 struct ClientMonitorRecord *m)
1537{
1538 const struct PutActionContext *put_ctx = cls;
1539 const struct GNUNET_DATACACHE_Block *bd = put_ctx->bd;
1540 bool truncated = (0 != (bd->ro & GNUNET_DHT_RO_TRUNCATED));
1541 struct GNUNET_MQ_Envelope *env;
1542 struct GNUNET_DHT_MonitorPutMessage *mmsg;
1543 struct GNUNET_DHT_PathElement *msg_path;
1544 size_t msize;
1545
1546 msize = bd->data_size
1547 + bd->put_path_length
1548 * sizeof(struct GNUNET_DHT_PathElement);
1549 if (truncated)
1550 msize += sizeof (struct GNUNET_PeerIdentity);
1551 env = GNUNET_MQ_msg_extra (mmsg,
1552 msize,
1553 GNUNET_MESSAGE_TYPE_DHT_MONITOR_PUT);
1554 mmsg->options = htonl (bd->ro);
1555 mmsg->type = htonl (bd->type);
1556 mmsg->hop_count = htonl (put_ctx->hop_count);
1557 mmsg->desired_replication_level = htonl (put_ctx->desired_replication_level);
1558 mmsg->put_path_length = htonl (bd->put_path_length);
1559 mmsg->key = bd->key;
1560 mmsg->expiration_time = GNUNET_TIME_absolute_hton (bd->expiration_time);
1561 if (truncated)
1562 {
1563 void *tgt = &mmsg[1];
1564
1565 GNUNET_memcpy (tgt,
1566 &bd->trunc_peer,
1567 sizeof (struct GNUNET_PeerIdentity));
1568 msg_path = (struct GNUNET_DHT_PathElement *)
1569 (tgt + sizeof (struct GNUNET_PeerIdentity));
1570 }
1571 else
1572 {
1573 msg_path = (struct GNUNET_DHT_PathElement *) &mmsg[1];
1574 }
1575 GNUNET_memcpy (msg_path,
1576 bd->put_path,
1577 bd->put_path_length * sizeof(struct GNUNET_DHT_PathElement));
1578 GNUNET_memcpy (&msg_path[bd->put_path_length],
1579 bd->data,
1580 bd->data_size);
1581 GNUNET_MQ_send (m->ch->mq,
1582 env);
1583}
1584
1585
1586void
1587GDS_CLIENTS_process_put (const struct GNUNET_DATACACHE_Block *bd,
1588 uint32_t hop_count,
1589 uint32_t desired_replication_level)
1590{
1591 struct PutActionContext put_ctx = {
1592 .bd = bd,
1593 .hop_count = hop_count,
1594 .desired_replication_level = desired_replication_level
1595 };
1596
1597 for_matching_monitors (bd->type,
1598 &bd->key,
1599 &put_action,
1600 &put_ctx);
1601}
1602
1603
1604/* ********************** Initialization logic ***************** */
1605
1606
1607/**
1608 * Initialize client subsystem.
1609 */
1610static void
1611GDS_CLIENTS_init (void)
1612{
1613 forward_map
1614 = GNUNET_CONTAINER_multihashmap_create (1024,
1615 GNUNET_YES);
1616 retry_heap
1617 = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
1618}
1619
1620
1621/**
1622 * Shutdown client subsystem.
1623 */
1624static void
1625GDS_CLIENTS_stop (void)
1626{
1627 if (NULL != retry_task)
1628 {
1629 GNUNET_SCHEDULER_cancel (retry_task);
1630 retry_task = NULL;
1631 }
1632}
1633
1634
1635/**
1636 * Define "main" method using service macro.
1637 *
1638 * @param name name of the service, like "dht" or "xdht"
1639 * @param run name of the initializaton method for the service
1640 */
1641#define GDS_DHT_SERVICE_INIT(name, run) \
1642 GNUNET_SERVICE_MAIN \
1643 (name, \
1644 GNUNET_SERVICE_OPTION_NONE, \
1645 run, \
1646 &client_connect_cb, \
1647 &client_disconnect_cb, \
1648 NULL, \
1649 GNUNET_MQ_hd_var_size (dht_local_put, \
1650 GNUNET_MESSAGE_TYPE_DHT_CLIENT_PUT, \
1651 struct GNUNET_DHT_ClientPutMessage, \
1652 NULL), \
1653 GNUNET_MQ_hd_var_size (dht_local_get, \
1654 GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET, \
1655 struct GNUNET_DHT_ClientGetMessage, \
1656 NULL), \
1657 GNUNET_MQ_hd_fixed_size (dht_local_get_stop, \
1658 GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET_STOP, \
1659 struct GNUNET_DHT_ClientGetStopMessage, \
1660 NULL), \
1661 GNUNET_MQ_hd_fixed_size (dht_local_monitor, \
1662 GNUNET_MESSAGE_TYPE_DHT_MONITOR_START, \
1663 struct GNUNET_DHT_MonitorStartStopMessage, \
1664 NULL), \
1665 GNUNET_MQ_hd_fixed_size (dht_local_monitor_stop, \
1666 GNUNET_MESSAGE_TYPE_DHT_MONITOR_STOP, \
1667 struct GNUNET_DHT_MonitorStartStopMessage, \
1668 NULL), \
1669 GNUNET_MQ_hd_var_size (dht_local_get_result_seen, \
1670 GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET_RESULTS_KNOWN, \
1671 struct GNUNET_DHT_ClientGetResultSeenMessage, \
1672 NULL), \
1673 GNUNET_MQ_hd_fixed_size (dht_local_hello_get, \
1674 GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_GET, \
1675 struct GNUNET_MessageHeader, \
1676 NULL), \
1677 GNUNET_MQ_hd_var_size (dht_local_hello_offer, \
1678 GNUNET_MESSAGE_TYPE_DHT_CLIENT_HELLO_URL, \
1679 struct GNUNET_MessageHeader, \
1680 NULL), \
1681 GNUNET_MQ_handler_end ())
1682
1683
1684/**
1685 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1686 */
1687void __attribute__ ((destructor))
1688GDS_CLIENTS_done ()
1689{
1690 if (NULL != retry_heap)
1691 {
1692 GNUNET_assert (0 == GNUNET_CONTAINER_heap_get_size (retry_heap));
1693 GNUNET_CONTAINER_heap_destroy (retry_heap);
1694 retry_heap = NULL;
1695 }
1696 if (NULL != forward_map)
1697 {
1698 GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap_size (forward_map));
1699 GNUNET_CONTAINER_multihashmap_destroy (forward_map);
1700 forward_map = NULL;
1701 }
1702}
1703
1704
1705/* end of gnunet-service-dht_clients.c */
diff --git a/src/service/dht/gnunet-service-dht_datacache.c b/src/service/dht/gnunet-service-dht_datacache.c
new file mode 100644
index 000000000..dcb6308a9
--- /dev/null
+++ b/src/service/dht/gnunet-service-dht_datacache.c
@@ -0,0 +1,291 @@
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/service/dht/gnunet-service-dht_datacache.h b/src/service/dht/gnunet-service-dht_datacache.h
new file mode 100644
index 000000000..b15e5378f
--- /dev/null
+++ b/src/service/dht/gnunet-service-dht_datacache.h
@@ -0,0 +1,115 @@
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/service/dht/gnunet-service-dht_neighbours.c b/src/service/dht/gnunet-service-dht_neighbours.c
new file mode 100644
index 000000000..15a4b21f7
--- /dev/null
+++ b/src/service/dht/gnunet-service-dht_neighbours.c
@@ -0,0 +1,3009 @@
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_uri_lib.h"
32#include "gnunet-service-dht.h"
33#include "gnunet-service-dht_neighbours.h"
34#include "gnunet-service-dht_routing.h"
35#include "dht.h"
36
37#define LOG_TRAFFIC(kind, ...) GNUNET_log_from (kind, "dht-traffic", \
38 __VA_ARGS__)
39
40/**
41 * Enable slow sanity checks to debug issues.
42 *
43 * TODO: might want to eventually implement probabilistic
44 * load-based path verification, but for now it is all or nothing
45 * based on this define.
46 *
47 * 0: do not check -- if signatures become performance critical
48 * 1: check all external inputs -- normal production for now
49 * 2: check internal computations as well -- for debugging
50 */
51#define SANITY_CHECKS 2
52
53/**
54 * How many buckets will we allow in total.
55 */
56#define MAX_BUCKETS sizeof(struct GNUNET_HashCode) * 8
57
58/**
59 * What is the maximum number of peers in a given bucket.
60 */
61#define DEFAULT_BUCKET_SIZE 8
62
63/**
64 * Desired replication level for FIND PEER requests
65 */
66#define FIND_PEER_REPLICATION_LEVEL 4
67
68/**
69 * Maximum allowed number of pending messages per peer.
70 */
71#define MAXIMUM_PENDING_PER_PEER 64
72
73/**
74 * How long at least to wait before sending another find peer request.
75 * This is basically the frequency at which we will usually send out
76 * requests when we are 'perfectly' connected.
77 */
78#define DHT_MINIMUM_FIND_PEER_INTERVAL GNUNET_TIME_relative_multiply ( \
79 GNUNET_TIME_UNIT_MINUTES, 2)
80
81
82/**
83 * How long to additionally wait on average per #bucket_size to send out the
84 * FIND PEER requests if we did successfully connect (!) to a a new peer and
85 * added it to a bucket (as counted in #newly_found_peers). This time is
86 * Multiplied by 100 * newly_found_peers / bucket_size to get the new delay
87 * for finding peers (the #DHT_MINIMUM_FIND_PEER_INTERVAL is still added on
88 * top). Also the range in which we randomize, so the effective value
89 * is half of the number given here.
90 */
91#define DHT_AVG_FIND_PEER_INTERVAL GNUNET_TIME_relative_multiply ( \
92 GNUNET_TIME_UNIT_SECONDS, 6)
93
94/**
95 * How long at most to wait for transmission of a GET request to another peer?
96 */
97#define GET_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2)
98
99
100GNUNET_NETWORK_STRUCT_BEGIN
101
102/**
103 * P2P PUT message
104 */
105struct PeerPutMessage
106{
107 /**
108 * Type: #GNUNET_MESSAGE_TYPE_DHT_P2P_PUT
109 */
110 struct GNUNET_MessageHeader header;
111
112 /**
113 * Content type, must not be zero.
114 */
115 uint32_t type GNUNET_PACKED;
116
117 /**
118 * Processing options
119 */
120 uint16_t options GNUNET_PACKED;
121
122 /**
123 * Hop count
124 */
125 uint16_t hop_count GNUNET_PACKED;
126
127 /**
128 * Replication level for this message
129 */
130 uint16_t desired_replication_level GNUNET_PACKED;
131
132 /**
133 * Length of the PUT path that follows (if tracked).
134 */
135 uint16_t put_path_length GNUNET_PACKED;
136
137 /**
138 * When does the content expire?
139 */
140 struct GNUNET_TIME_AbsoluteNBO expiration_time;
141
142 /**
143 * Bloomfilter (for peer identities) to stop circular routes
144 */
145 char bloomfilter[DHT_BLOOM_SIZE];
146
147 /**
148 * The key we are storing under.
149 */
150 struct GNUNET_HashCode key;
151
152 /* trunc_peer (if truncated) */
153
154 /* put path (if tracked) */
155
156 /* sender_sig (if path tracking is on) */
157
158 /* Payload */
159};
160
161
162/**
163 * P2P Result message
164 */
165struct PeerResultMessage
166{
167 /**
168 * Type: #GNUNET_MESSAGE_TYPE_DHT_P2P_RESULT
169 */
170 struct GNUNET_MessageHeader header;
171
172 /**
173 * Content type.
174 */
175 uint32_t type GNUNET_PACKED;
176
177 /**
178 * Always 0.
179 */
180 uint16_t reserved GNUNET_PACKED;
181
182 /**
183 * Message options, actually an 'enum GNUNET_DHT_RouteOption' value in NBO.
184 */
185 uint16_t options GNUNET_PACKED;
186
187 /**
188 * Length of the PUT path that follows (if tracked).
189 */
190 uint16_t put_path_length GNUNET_PACKED;
191
192 /**
193 * Length of the GET path that follows (if tracked).
194 */
195 uint16_t get_path_length GNUNET_PACKED;
196
197 /**
198 * When does the content expire?
199 */
200 struct GNUNET_TIME_AbsoluteNBO expiration_time;
201
202 /**
203 * The key of the corresponding GET request.
204 */
205 struct GNUNET_HashCode key;
206
207 /* trunc_peer (if truncated) */
208
209 /* put path (if tracked) */
210
211 /* get path (if tracked) */
212
213 /* sender_sig (if path tracking is on) */
214
215 /* Payload */
216};
217
218
219/**
220 * P2P GET message
221 */
222struct PeerGetMessage
223{
224 /**
225 * Type: #GNUNET_MESSAGE_TYPE_DHT_P2P_GET
226 */
227 struct GNUNET_MessageHeader header;
228
229 /**
230 * Desired content type.
231 */
232 uint32_t type GNUNET_PACKED;
233
234 /**
235 * Processing options
236 */
237 uint16_t options GNUNET_PACKED;
238
239 /**
240 * Hop count
241 */
242 uint16_t hop_count GNUNET_PACKED;
243
244 /**
245 * Desired replication level for this request.
246 */
247 uint16_t desired_replication_level GNUNET_PACKED;
248
249 /**
250 * Size of the result filter.
251 */
252 uint16_t result_filter_size GNUNET_PACKED;
253
254 /**
255 * Bloomfilter (for peer identities) to stop circular routes
256 */
257 char bloomfilter[DHT_BLOOM_SIZE];
258
259 /**
260 * The key we are looking for.
261 */
262 struct GNUNET_HashCode key;
263
264 /* result bloomfilter */
265
266 /* xquery */
267
268};
269GNUNET_NETWORK_STRUCT_END
270
271
272/**
273 * Entry for a peer in a bucket.
274 */
275struct PeerInfo;
276
277
278/**
279 * List of targets that we can use to reach this peer.
280 */
281struct Target
282{
283 /**
284 * Kept in a DLL.
285 */
286 struct Target *next;
287
288 /**
289 * Kept in a DLL.
290 */
291 struct Target *prev;
292
293 /**
294 * Handle for sending messages to this peer.
295 */
296 struct GNUNET_DHTU_Target *utarget;
297
298 /**
299 * Underlay providing this target.
300 */
301 struct GDS_Underlay *u;
302
303 /**
304 * Peer this is a target for.
305 */
306 struct PeerInfo *pi;
307
308 /**
309 * Handle used to 'hold' the connection to this peer.
310 */
311 struct GNUNET_DHTU_PreferenceHandle *ph;
312
313 /**
314 * Set to number of messages are waiting for the transmission to finish.
315 */
316 unsigned int load;
317
318 /**
319 * Set to @a true if the target was dropped, but we could not clean
320 * up yet because @e busy was also true.
321 */
322 bool dropped;
323
324};
325
326
327/**
328 * Entry for a peer in a bucket.
329 */
330struct PeerInfo
331{
332 /**
333 * What is the identity of the peer?
334 */
335 struct GNUNET_PeerIdentity id;
336
337 /**
338 * Hash of @e id.
339 */
340 struct GNUNET_HashCode phash;
341
342 /**
343 * When does our HELLO from this peer expire?
344 */
345 struct GNUNET_TIME_Absolute hello_expiration;
346
347 /**
348 * Next peer entry (DLL)
349 */
350 struct PeerInfo *next;
351
352 /**
353 * Prev peer entry (DLL)
354 */
355 struct PeerInfo *prev;
356
357 /**
358 * Head of DLL of targets for this peer.
359 */
360 struct Target *t_head;
361
362 /**
363 * Tail of DLL of targets for this peer.
364 */
365 struct Target *t_tail;
366
367 /**
368 * Block with a HELLO of this peer.
369 */
370 void *hello;
371
372 /**
373 * Number of bytes in @e hello.
374 */
375 size_t hello_size;
376
377 /**
378 * Which bucket is this peer in?
379 */
380 int peer_bucket;
381};
382
383
384/**
385 * Peers are grouped into buckets.
386 */
387struct PeerBucket
388{
389 /**
390 * Head of DLL
391 */
392 struct PeerInfo *head;
393
394 /**
395 * Tail of DLL
396 */
397 struct PeerInfo *tail;
398
399 /**
400 * Number of peers in the bucket.
401 */
402 unsigned int peers_size;
403};
404
405
406/**
407 * Do we cache all results that we are routing in the local datacache?
408 */
409static int cache_results;
410
411/**
412 * The lowest currently used bucket, initially 0 (for 0-bits matching bucket).
413 */
414static unsigned int closest_bucket;
415
416/**
417 * How many peers have we added since we sent out our last
418 * find peer request?
419 */
420static unsigned int newly_found_peers;
421
422/**
423 * Option for testing that disables the 'connect' function of the DHT.
424 */
425static int disable_try_connect;
426
427/**
428 * The buckets. Array of size #MAX_BUCKETS. Offset 0 means 0 bits matching.
429 */
430static struct PeerBucket k_buckets[MAX_BUCKETS];
431
432/**
433 * Hash map of all CORE-connected peers, for easy removal from
434 * #k_buckets on disconnect. Values are of type `struct PeerInfo`.
435 */
436static struct GNUNET_CONTAINER_MultiPeerMap *all_connected_peers;
437
438/**
439 * Maximum size for each bucket.
440 */
441static unsigned int bucket_size = DEFAULT_BUCKET_SIZE;
442
443/**
444 * Task that sends FIND PEER requests.
445 */
446static struct GNUNET_SCHEDULER_Task *find_peer_task;
447
448
449/**
450 * Function called whenever we finished sending to a target.
451 * Marks the transmission as finished (and the target as ready
452 * for the next message).
453 *
454 * @param cls a `struct Target *`
455 */
456static void
457send_done_cb (void *cls)
458{
459 struct Target *t = cls;
460 struct PeerInfo *pi = t->pi; /* NULL if t->dropped! */
461
462 GNUNET_assert (t->load > 0);
463 t->load--;
464 if (0 < t->load)
465 return;
466 if (t->dropped)
467 {
468 GNUNET_free (t);
469 return;
470 }
471 /* move target back to the front */
472 GNUNET_CONTAINER_DLL_remove (pi->t_head,
473 pi->t_tail,
474 t);
475 GNUNET_CONTAINER_DLL_insert (pi->t_head,
476 pi->t_tail,
477 t);
478}
479
480
481/**
482 * Send @a msg to @a pi.
483 *
484 * @param pi where to send the message
485 * @param msg message to send
486 */
487static void
488do_send (struct PeerInfo *pi,
489 const struct GNUNET_MessageHeader *msg)
490{
491 struct Target *t;
492
493 for (t = pi->t_head;
494 NULL != t;
495 t = t->next)
496 if (t->load < MAXIMUM_PENDING_PER_PEER)
497 break;
498 if (NULL == t)
499 {
500 /* all targets busy, drop message */
501 GNUNET_STATISTICS_update (GDS_stats,
502 "# messages dropped (underlays busy)",
503 1,
504 GNUNET_NO);
505 return;
506 }
507 t->load++;
508 /* rotate busy targets to the end */
509 if (MAXIMUM_PENDING_PER_PEER == t->load)
510 {
511 GNUNET_CONTAINER_DLL_remove (pi->t_head,
512 pi->t_tail,
513 t);
514 GNUNET_CONTAINER_DLL_insert_tail (pi->t_head,
515 pi->t_tail,
516 t);
517 }
518 GDS_u_send (t->u,
519 t->utarget,
520 msg,
521 ntohs (msg->size),
522 &send_done_cb,
523 t);
524}
525
526
527/**
528 * Sign that we are routing a message from @a pred to @a succ.
529 * (So the route is $PRED->us->$SUCC).
530 *
531 * @param data payload (the block)
532 * @param data_size number of bytes in @a data
533 * @param exp_time expiration time of @a data
534 * @param pred predecessor peer ID
535 * @param succ successor peer ID
536 * @param[out] sig where to write the signature
537 * (of purpose #GNUNET_SIGNATURE_PURPOSE_DHT_PUT_HOP)
538 */
539static void
540sign_path (const void *data,
541 size_t data_size,
542 struct GNUNET_TIME_Absolute exp_time,
543 const struct GNUNET_PeerIdentity *pred,
544 const struct GNUNET_PeerIdentity *succ,
545 struct GNUNET_CRYPTO_EddsaSignature *sig)
546{
547 struct GNUNET_DHT_HopSignature hs = {
548 .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_DHT_HOP),
549 .purpose.size = htonl (sizeof (hs)),
550 .expiration_time = GNUNET_TIME_absolute_hton (exp_time),
551 .succ = *succ
552 };
553
554 if (NULL != pred)
555 hs.pred = *pred;
556 GNUNET_CRYPTO_hash (data,
557 data_size,
558 &hs.h_data);
559 GNUNET_CRYPTO_eddsa_sign (&GDS_my_private_key,
560 &hs,
561 sig);
562}
563
564
565/**
566 * Find the optimal bucket for this key.
567 *
568 * @param hc the hashcode to compare our identity to
569 * @return the proper bucket index, or -1
570 * on error (same hashcode)
571 */
572static int
573find_bucket (const struct GNUNET_HashCode *hc)
574{
575 struct GNUNET_HashCode xor;
576 unsigned int bits;
577
578 GNUNET_CRYPTO_hash_xor (hc,
579 &GDS_my_identity_hash,
580 &xor);
581 bits = GNUNET_CRYPTO_hash_count_leading_zeros (&xor);
582 if (bits == MAX_BUCKETS)
583 {
584 /* How can all bits match? Got my own ID? */
585 GNUNET_break (0);
586 return -1;
587 }
588 return MAX_BUCKETS - bits - 1;
589}
590
591
592/**
593 * Add each of the peers we already know to the Bloom filter of
594 * the request so that we don't get duplicate HELLOs.
595 *
596 * @param cls the `struct GNUNET_BLOCK_Group`
597 * @param key peer identity to add to the bloom filter
598 * @param value the peer information
599 * @return #GNUNET_YES (we should continue to iterate)
600 */
601static enum GNUNET_GenericReturnValue
602add_known_to_bloom (void *cls,
603 const struct GNUNET_PeerIdentity *key,
604 void *value)
605{
606 struct GNUNET_BLOCK_Group *bg = cls;
607 struct PeerInfo *pi = value;
608
609 GNUNET_BLOCK_group_set_seen (bg,
610 &pi->phash,
611 1);
612 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
613 "Adding known peer (%s) to Bloom filter for FIND PEER\n",
614 GNUNET_i2s (key));
615 return GNUNET_YES;
616}
617
618
619/**
620 * Task to send a find peer message for our own peer identifier
621 * so that we can find the closest peers in the network to ourselves
622 * and attempt to connect to them.
623 *
624 * @param cls closure for this task, NULL
625 */
626static void
627send_find_peer_message (void *cls)
628{
629 (void) cls;
630
631 /* Compute when to do this again (and if we should
632 even send a message right now) */
633 {
634 struct GNUNET_TIME_Relative next_send_time;
635 bool done_early;
636
637 find_peer_task = NULL;
638 done_early = (newly_found_peers > bucket_size);
639 /* schedule next round, taking longer if we found more peers
640 in the last round. */
641 next_send_time.rel_value_us =
642 DHT_MINIMUM_FIND_PEER_INTERVAL.rel_value_us
643 + GNUNET_CRYPTO_random_u64 (
644 GNUNET_CRYPTO_QUALITY_WEAK,
645 GNUNET_TIME_relative_multiply (
646 DHT_AVG_FIND_PEER_INTERVAL,
647 1 + 100 * (1 + newly_found_peers) / bucket_size).rel_value_us);
648 newly_found_peers = 0;
649 GNUNET_assert (NULL == find_peer_task);
650 find_peer_task =
651 GNUNET_SCHEDULER_add_delayed (next_send_time,
652 &send_find_peer_message,
653 NULL);
654 if (done_early)
655 return;
656 }
657
658 /* actually send 'find peer' request */
659 {
660 struct GNUNET_BLOCK_Group *bg;
661 struct GNUNET_CONTAINER_BloomFilter *peer_bf;
662
663 bg = GNUNET_BLOCK_group_create (GDS_block_context,
664 GNUNET_BLOCK_TYPE_DHT_HELLO,
665 NULL,
666 0,
667 "seen-set-size",
668 GNUNET_CONTAINER_multipeermap_size (
669 all_connected_peers),
670 NULL);
671 GNUNET_CONTAINER_multipeermap_iterate (all_connected_peers,
672 &add_known_to_bloom,
673 bg);
674 peer_bf
675 = GNUNET_CONTAINER_bloomfilter_init (NULL,
676 DHT_BLOOM_SIZE,
677 GNUNET_CONSTANTS_BLOOMFILTER_K);
678 if (GNUNET_OK !=
679 GDS_NEIGHBOURS_handle_get (GNUNET_BLOCK_TYPE_DHT_HELLO,
680 GNUNET_DHT_RO_FIND_APPROXIMATE
681 | GNUNET_DHT_RO_RECORD_ROUTE,
682 FIND_PEER_REPLICATION_LEVEL,
683 0, /* hop count */
684 &GDS_my_identity_hash,
685 NULL, 0, /* xquery */
686 bg,
687 peer_bf))
688 {
689 GNUNET_STATISTICS_update (GDS_stats,
690 "# Failed to initiate FIND PEER lookup",
691 1,
692 GNUNET_NO);
693 }
694 else
695 {
696 GNUNET_STATISTICS_update (GDS_stats,
697 "# FIND PEER messages initiated",
698 1,
699 GNUNET_NO);
700 }
701 GNUNET_CONTAINER_bloomfilter_free (peer_bf);
702 GNUNET_BLOCK_group_destroy (bg);
703 }
704}
705
706
707/**
708 * The list of the first #bucket_size peers of @a bucket
709 * changed. We should thus make sure we have called 'hold'
710 * all of the first bucket_size peers!
711 *
712 * @param[in,out] bucket the bucket where the peer set changed
713 */
714static void
715update_hold (struct PeerBucket *bucket)
716{
717 unsigned int off = 0;
718
719 /* find the peer -- we just go over all of them, should
720 be hardly any more expensive than just finding the 'right'
721 one. */
722 for (struct PeerInfo *pos = bucket->head;
723 NULL != pos;
724 pos = pos->next)
725 {
726 if (off > bucket_size)
727 break; /* We only hold up to #bucket_size peers per bucket */
728 off++;
729 for (struct Target *tp = pos->t_head;
730 NULL != tp;
731 tp = tp->next)
732 if (NULL == tp->ph)
733 tp->ph = GDS_u_hold (tp->u,
734 tp->utarget);
735 }
736}
737
738
739void
740GDS_u_connect (void *cls,
741 struct GNUNET_DHTU_Target *target,
742 const struct GNUNET_PeerIdentity *pid,
743 void **ctx)
744{
745 struct GDS_Underlay *u = cls;
746 struct PeerInfo *pi;
747 struct PeerBucket *bucket;
748 bool do_hold = false;
749
750 /* Check for connect to self message */
751 if (0 == GNUNET_memcmp (&GDS_my_identity,
752 pid))
753 return;
754 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
755 "Connected to peer %s\n",
756 GNUNET_i2s (pid));
757 pi = GNUNET_CONTAINER_multipeermap_get (all_connected_peers,
758 pid);
759 if (NULL == pi)
760 {
761 GNUNET_STATISTICS_update (GDS_stats,
762 "# peers connected",
763 1,
764 GNUNET_NO);
765 pi = GNUNET_new (struct PeerInfo);
766 pi->id = *pid;
767 GNUNET_CRYPTO_hash (pid,
768 sizeof(*pid),
769 &pi->phash);
770 pi->peer_bucket = find_bucket (&pi->phash);
771 GNUNET_assert ( (pi->peer_bucket >= 0) &&
772 ((unsigned int) pi->peer_bucket < MAX_BUCKETS));
773 bucket = &k_buckets[pi->peer_bucket];
774 GNUNET_CONTAINER_DLL_insert_tail (bucket->head,
775 bucket->tail,
776 pi);
777 bucket->peers_size++;
778 closest_bucket = GNUNET_MAX (closest_bucket,
779 (unsigned int) pi->peer_bucket + 1);
780 GNUNET_assert (GNUNET_OK ==
781 GNUNET_CONTAINER_multipeermap_put (all_connected_peers,
782 &pi->id,
783 pi,
784 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
785 if (bucket->peers_size <= bucket_size)
786 {
787 newly_found_peers++;
788 do_hold = true;
789 }
790 if ( (1 == GNUNET_CONTAINER_multipeermap_size (all_connected_peers)) &&
791 (GNUNET_YES != disable_try_connect) )
792 {
793 /* got a first connection, good time to start with FIND PEER requests... */
794 GNUNET_assert (NULL == find_peer_task);
795 find_peer_task = GNUNET_SCHEDULER_add_now (&send_find_peer_message,
796 NULL);
797 }
798 }
799 {
800 struct Target *t;
801
802 t = GNUNET_new (struct Target);
803 t->u = u;
804 t->utarget = target;
805 t->pi = pi;
806 GNUNET_CONTAINER_DLL_insert (pi->t_head,
807 pi->t_tail,
808 t);
809 *ctx = t;
810
811 }
812 if (do_hold)
813 update_hold (bucket);
814}
815
816
817void
818GDS_u_disconnect (void *ctx)
819{
820 struct Target *t = ctx;
821 struct PeerInfo *pi;
822 struct PeerBucket *bucket;
823 bool was_held = false;
824
825 /* Check for disconnect from self message (on shutdown) */
826 if (NULL == t)
827 return;
828 pi = t->pi;
829 GNUNET_CONTAINER_DLL_remove (pi->t_head,
830 pi->t_tail,
831 t);
832 if (NULL != t->ph)
833 {
834 GDS_u_drop (t->u,
835 t->ph);
836 t->ph = NULL;
837 was_held = true;
838 }
839 if (t->load > 0)
840 {
841 t->dropped = true;
842 t->pi = NULL;
843 }
844 else
845 {
846 GNUNET_free (t);
847 }
848 if (NULL != pi->t_head)
849 return; /* got other connections still */
850 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
851 "Disconnected from peer %s\n",
852 GNUNET_i2s (&pi->id));
853 GNUNET_STATISTICS_update (GDS_stats,
854 "# peers connected",
855 -1,
856 GNUNET_NO);
857 GNUNET_assert (GNUNET_YES ==
858 GNUNET_CONTAINER_multipeermap_remove (all_connected_peers,
859 &pi->id,
860 pi));
861 if ( (0 == GNUNET_CONTAINER_multipeermap_size (all_connected_peers)) &&
862 (GNUNET_YES != disable_try_connect))
863 {
864 GNUNET_SCHEDULER_cancel (find_peer_task);
865 find_peer_task = NULL;
866 }
867 GNUNET_assert (pi->peer_bucket >= 0);
868 bucket = &k_buckets[pi->peer_bucket];
869 GNUNET_CONTAINER_DLL_remove (bucket->head,
870 bucket->tail,
871 pi);
872 GNUNET_assert (bucket->peers_size > 0);
873 bucket->peers_size--;
874 if ( (was_held) &&
875 (bucket->peers_size >= bucket_size - 1) )
876 update_hold (bucket);
877 while ( (closest_bucket > 0) &&
878 (0 == k_buckets[closest_bucket - 1].peers_size))
879 closest_bucket--;
880 GNUNET_free (pi->hello);
881 GNUNET_free (pi);
882}
883
884
885/**
886 * To how many peers should we (on average) forward the request to
887 * obtain the desired target_replication count (on average).
888 *
889 * @param hop_count number of hops the message has traversed
890 * @param target_replication the number of total paths desired
891 * @return Some number of peers to forward the message to
892 */
893static unsigned int
894get_forward_count (uint16_t hop_count,
895 uint16_t target_replication)
896{
897 uint32_t random_value;
898 uint32_t forward_count;
899 float target_value;
900 float rm1;
901
902 if (hop_count > GDS_NSE_get () * 4.0)
903 {
904 /* forcefully terminate */
905 GNUNET_STATISTICS_update (GDS_stats,
906 "# requests TTL-dropped",
907 1,
908 GNUNET_NO);
909 return 0;
910 }
911 if (hop_count > GDS_NSE_get () * 2.0)
912 {
913 /* Once we have reached our ideal number of hops, only forward to 1 peer */
914 return 1;
915 }
916 /* bound by system-wide maximum and minimum */
917 if (0 == target_replication)
918 target_replication = 1; /* 0 is verboten */
919 target_replication =
920 GNUNET_MIN (GNUNET_DHT_MAXIMUM_REPLICATION_LEVEL,
921 target_replication);
922 rm1 = target_replication - 1.0;
923 target_value =
924 1 + (rm1) / (GDS_NSE_get () + (rm1 * hop_count));
925
926 /* Set forward count to floor of target_value */
927 forward_count = (uint32_t) target_value;
928 /* Subtract forward_count (floor) from target_value (yields value between 0 and 1) */
929 target_value = target_value - forward_count;
930 random_value = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
931 UINT32_MAX);
932 if (random_value < (target_value * UINT32_MAX))
933 forward_count++;
934 return GNUNET_MIN (forward_count,
935 GNUNET_DHT_MAXIMUM_REPLICATION_LEVEL);
936}
937
938
939/**
940 * Check whether my identity is closer than any known peers. If a
941 * non-null bloomfilter is given, check if this is the closest peer
942 * that hasn't already been routed to.
943 *
944 * @param key hash code to check closeness to
945 * @param bloom bloomfilter, exclude these entries from the decision
946 * @return #GNUNET_YES if node location is closest,
947 * #GNUNET_NO otherwise.
948 */
949enum GNUNET_GenericReturnValue
950GDS_am_closest_peer (const struct GNUNET_HashCode *key,
951 const struct GNUNET_CONTAINER_BloomFilter *bloom)
952{
953 if (0 == GNUNET_memcmp (&GDS_my_identity_hash,
954 key))
955 return GNUNET_YES;
956 for (int bucket_num = find_bucket (key);
957 bucket_num < closest_bucket;
958 bucket_num++)
959 {
960 unsigned int count = 0;
961
962 GNUNET_assert (bucket_num >= 0);
963 for (struct PeerInfo *pos = k_buckets[bucket_num].head;
964 NULL != pos;
965 pos = pos->next)
966 {
967 if (count >= bucket_size)
968 break; /* we only consider first #bucket_size entries per bucket */
969 count++;
970 if ( (NULL != bloom) &&
971 (GNUNET_YES ==
972 GNUNET_CONTAINER_bloomfilter_test (bloom,
973 &pos->phash)) )
974 continue; /* Ignore filtered peers */
975 /* All peers in this bucket must be closer than us, as
976 they mismatch with our PID on the pivotal bit. So
977 because an unfiltered peer exists, we are not the
978 closest. */
979 int delta = GNUNET_CRYPTO_hash_xorcmp (&pos->phash,
980 &GDS_my_identity_hash,
981 key);
982 switch (delta)
983 {
984 case -1: /* pos closer */
985 return GNUNET_NO;
986 case 0: /* identical, impossible! */
987 GNUNET_assert (0);
988 break;
989 case 1: /* I am closer */
990 break;
991 }
992 }
993 }
994 /* No closer (unfiltered) peers found; we must be the closest! */
995 return GNUNET_YES;
996}
997
998
999/**
1000 * Select a peer from the routing table that would be a good routing
1001 * destination for sending a message for @a key. The resulting peer
1002 * must not be in the set of @a bloom blocked peers.
1003 *
1004 * Note that we should not ALWAYS select the closest peer to the
1005 * target, we do a "random" peer selection if the number of @a hops
1006 * is below the logarithm of the network size estimate.
1007 *
1008 * In all cases, we only consider at most the first #bucket_size peers of any
1009 * #k_buckets. The other peers in the bucket are there because GNUnet doesn't
1010 * really allow the DHT to "reject" connections, but we only use the first
1011 * #bucket_size, even if more exist. (The idea is to ensure that those
1012 * connections are frequently used, and for others to be not used by the DHT,
1013 * and thus possibly dropped by transport due to disuse).
1014 *
1015 * @param key the key we are selecting a peer to route to
1016 * @param bloom a Bloom filter containing entries this request has seen already
1017 * @param hops how many hops has this message traversed thus far
1018 * @return Peer to route to, or NULL on error
1019 */
1020static struct PeerInfo *
1021select_peer (const struct GNUNET_HashCode *key,
1022 const struct GNUNET_CONTAINER_BloomFilter *bloom,
1023 uint32_t hops)
1024{
1025 if (0 == closest_bucket)
1026 {
1027 GNUNET_STATISTICS_update (GDS_stats,
1028 "# Peer selection failed",
1029 1,
1030 GNUNET_NO);
1031 return NULL; /* we have zero connections */
1032 }
1033 if (hops >= GDS_NSE_get ())
1034 {
1035 /* greedy selection (closest peer that is not in Bloom filter) */
1036 struct PeerInfo *chosen = NULL;
1037 int best_bucket;
1038 int bucket_offset;
1039
1040 {
1041 struct GNUNET_HashCode xor;
1042
1043 GNUNET_CRYPTO_hash_xor (key,
1044 &GDS_my_identity_hash,
1045 &xor);
1046 best_bucket = GNUNET_CRYPTO_hash_count_leading_zeros (&xor);
1047 }
1048 if (best_bucket >= closest_bucket)
1049 bucket_offset = closest_bucket - 1;
1050 else
1051 bucket_offset = best_bucket;
1052 while (-1 != bucket_offset)
1053 {
1054 struct PeerBucket *bucket = &k_buckets[bucket_offset];
1055 unsigned int count = 0;
1056
1057 for (struct PeerInfo *pos = bucket->head;
1058 NULL != pos;
1059 pos = pos->next)
1060 {
1061 if (count >= bucket_size)
1062 break; /* we only consider first #bucket_size entries per bucket */
1063 count++;
1064 if ( (NULL != bloom) &&
1065 (GNUNET_YES ==
1066 GNUNET_CONTAINER_bloomfilter_test (bloom,
1067 &pos->phash)) )
1068 {
1069 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1070 "Excluded peer `%s' due to BF match in greedy routing for %s\n",
1071 GNUNET_i2s (&pos->id),
1072 GNUNET_h2s (key));
1073 continue;
1074 }
1075 if (NULL == chosen)
1076 {
1077 /* First candidate */
1078 chosen = pos;
1079 }
1080 else
1081 {
1082 int delta = GNUNET_CRYPTO_hash_xorcmp (&pos->phash,
1083 &chosen->phash,
1084 key);
1085 switch (delta)
1086 {
1087 case -1: /* pos closer */
1088 chosen = pos;
1089 break;
1090 case 0: /* identical, impossible! */
1091 GNUNET_assert (0);
1092 break;
1093 case 1: /* chosen closer */
1094 break;
1095 }
1096 }
1097 count++;
1098 } /* for all (#bucket_size) peers in bucket */
1099 if (NULL != chosen)
1100 break;
1101
1102 /* If we chose nothing in first iteration, first go through deeper
1103 buckets (best chance to find a good match), and if we still found
1104 nothing, then to shallower buckets. Terminate on any match in the
1105 current bucket, as this search order guarantees that it can only get
1106 worse as we keep going. */
1107 if (bucket_offset > best_bucket)
1108 {
1109 /* Go through more deeper buckets */
1110 bucket_offset++;
1111 if (bucket_offset == closest_bucket)
1112 {
1113 /* Can't go any deeper, if nothing selected,
1114 go for shallower buckets */
1115 bucket_offset = best_bucket - 1;
1116 }
1117 }
1118 else
1119 {
1120 /* We're either at the 'best_bucket' or already moving
1121 on to shallower buckets. */
1122 if (bucket_offset == best_bucket)
1123 bucket_offset++; /* go for deeper buckets */
1124 else
1125 bucket_offset--; /* go for shallower buckets */
1126 }
1127 } /* for applicable buckets (starting at best match) */
1128 if (NULL == chosen)
1129 {
1130 GNUNET_STATISTICS_update (GDS_stats,
1131 "# Peer selection failed",
1132 1,
1133 GNUNET_NO);
1134 return NULL;
1135 }
1136 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1137 "Selected peer `%s' in greedy routing for %s\n",
1138 GNUNET_i2s (&chosen->id),
1139 GNUNET_h2s (key));
1140 return chosen;
1141 } /* end of 'greedy' peer selection */
1142
1143 /* select "random" peer */
1144 /* count number of peers that are available and not filtered,
1145 but limit to at most #bucket_size peers, starting with
1146 those 'furthest' from us. */
1147 {
1148 unsigned int total = 0;
1149 unsigned int selected;
1150
1151 for (unsigned int bc = 0; bc < closest_bucket; bc++)
1152 {
1153 struct PeerBucket *bucket = &k_buckets[bc];
1154 unsigned int count = 0;
1155
1156 for (struct PeerInfo *pos = bucket->head;
1157 NULL != pos;
1158 pos = pos->next)
1159 {
1160 count++;
1161 if (count > bucket_size)
1162 break; /* limits search to #bucket_size peers per bucket */
1163 if ( (NULL != bloom) &&
1164 (GNUNET_YES ==
1165 GNUNET_CONTAINER_bloomfilter_test (bloom,
1166 &pos->phash)) )
1167 {
1168 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1169 "Excluded peer `%s' due to BF match in random routing for %s\n",
1170 GNUNET_i2s (&pos->id),
1171 GNUNET_h2s (key));
1172 continue; /* Ignore filtered peers */
1173 }
1174 total++;
1175 } /* for all peers in bucket */
1176 } /* for all buckets */
1177 if (0 == total) /* No peers to select from! */
1178 {
1179 GNUNET_STATISTICS_update (GDS_stats,
1180 "# Peer selection failed",
1181 1,
1182 GNUNET_NO);
1183 return NULL;
1184 }
1185
1186 /* Now actually choose a peer */
1187 selected = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1188 total);
1189 for (unsigned int bc = 0; bc < closest_bucket; bc++)
1190 {
1191 unsigned int count = 0;
1192
1193 for (struct PeerInfo *pos = k_buckets[bc].head;
1194 pos != NULL;
1195 pos = pos->next)
1196 {
1197 count++;
1198 if (count > bucket_size)
1199 break; /* limits search to #bucket_size peers per bucket */
1200
1201 if ( (NULL != bloom) &&
1202 (GNUNET_YES ==
1203 GNUNET_CONTAINER_bloomfilter_test (bloom,
1204 &pos->phash)) )
1205 continue; /* Ignore bloomfiltered peers */
1206 if (0 == selected--)
1207 {
1208 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1209 "Selected peer `%s' in random routing for %s\n",
1210 GNUNET_i2s (&pos->id),
1211 GNUNET_h2s (key));
1212 return pos;
1213 }
1214 } /* for peers in bucket */
1215 } /* for all buckets */
1216 } /* random peer selection scope */
1217 GNUNET_break (0);
1218 return NULL;
1219}
1220
1221
1222/**
1223 * Compute the set of peers that the given request should be
1224 * forwarded to.
1225 *
1226 * @param key routing key
1227 * @param[in,out] bloom Bloom filter excluding peers as targets,
1228 * all selected peers will be added to the Bloom filter
1229 * @param hop_count number of hops the request has traversed so far
1230 * @param target_replication desired number of replicas
1231 * @param[out] targets where to store an array of target peers (to be
1232 * free()ed by the caller)
1233 * @return number of peers returned in @a targets.
1234 */
1235static unsigned int
1236get_target_peers (const struct GNUNET_HashCode *key,
1237 struct GNUNET_CONTAINER_BloomFilter *bloom,
1238 uint16_t hop_count,
1239 uint16_t target_replication,
1240 struct PeerInfo ***targets)
1241{
1242 unsigned int target;
1243 unsigned int off;
1244 struct PeerInfo **rtargets;
1245
1246 GNUNET_assert (NULL != bloom);
1247 target = get_forward_count (hop_count,
1248 target_replication);
1249 if (0 == target)
1250 {
1251 *targets = NULL;
1252 return 0;
1253 }
1254 rtargets = GNUNET_new_array (target,
1255 struct PeerInfo *);
1256 for (off = 0; off < target; off++)
1257 {
1258 struct PeerInfo *nxt;
1259
1260 nxt = select_peer (key,
1261 bloom,
1262 hop_count);
1263 if (NULL == nxt)
1264 break;
1265 rtargets[off] = nxt;
1266 GNUNET_break (GNUNET_NO ==
1267 GNUNET_CONTAINER_bloomfilter_test (bloom,
1268 &nxt->phash));
1269 GNUNET_CONTAINER_bloomfilter_add (bloom,
1270 &nxt->phash);
1271 }
1272 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1273 "Selected %u/%u peers at hop %u for %s (target was %u)\n",
1274 off,
1275 GNUNET_CONTAINER_multipeermap_size (all_connected_peers),
1276 (unsigned int) hop_count,
1277 GNUNET_h2s (key),
1278 target);
1279 if (0 == off)
1280 {
1281 GNUNET_free (rtargets);
1282 *targets = NULL;
1283 return 0;
1284 }
1285 *targets = rtargets;
1286 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1287 "Forwarding query `%s' to %u peers (goal was %u peers)\n",
1288 GNUNET_h2s (key),
1289 off,
1290 target);
1291 return off;
1292}
1293
1294
1295/**
1296 * If we got a HELLO, consider it for our own routing table
1297 *
1298 * @param bd block data we got
1299 */
1300static void
1301hello_check (const struct GNUNET_DATACACHE_Block *bd)
1302{
1303 struct GNUNET_HELLO_Builder *b;
1304
1305 if (GNUNET_BLOCK_TYPE_DHT_HELLO != bd->type)
1306 return;
1307
1308 b = GNUNET_HELLO_builder_from_block (bd->data,
1309 bd->data_size);
1310 if (GNUNET_YES != disable_try_connect)
1311 {
1312 GNUNET_HELLO_builder_iterate (b,
1313 &GDS_try_connect,
1314 NULL);
1315 }
1316 GNUNET_HELLO_builder_free (b);
1317}
1318
1319
1320enum GNUNET_GenericReturnValue
1321GDS_NEIGHBOURS_handle_put (const struct GNUNET_DATACACHE_Block *bd,
1322 uint16_t desired_replication_level,
1323 uint16_t hop_count,
1324 struct GNUNET_CONTAINER_BloomFilter *bf)
1325{
1326 unsigned int target_count;
1327 struct PeerInfo **targets;
1328 size_t msize;
1329 unsigned int skip_count;
1330 enum GNUNET_DHT_RouteOption ro = bd->ro;
1331 unsigned int put_path_length = bd->put_path_length;
1332 const struct GNUNET_DHT_PathElement *put_path = bd->put_path;
1333 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
1334 bool tracking = (0 != (ro & GNUNET_DHT_RO_RECORD_ROUTE));
1335 const struct GNUNET_PeerIdentity *trunc_peer
1336 = truncated
1337 ? &bd->trunc_peer
1338 : NULL;
1339
1340#if SANITY_CHECKS > 1
1341 unsigned int failure_offset;
1342
1343 failure_offset
1344 = GNUNET_DHT_verify_path (bd->data,
1345 bd->data_size,
1346 bd->expiration_time,
1347 trunc_peer,
1348 put_path,
1349 put_path_length,
1350 NULL, 0, /* get_path */
1351 &GDS_my_identity);
1352 if (0 != failure_offset)
1353 {
1354 GNUNET_break_op (0);
1355 truncated = true;
1356 trunc_peer = &put_path[failure_offset - 1].pred;
1357 put_path = &put_path[failure_offset];
1358 put_path_length = put_path_length - failure_offset;
1359 ro |= GNUNET_DHT_RO_TRUNCATED;
1360 }
1361#endif
1362 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1363 "Adding myself (%s) to PUT bloomfilter for %s with RO(%s/%s)\n",
1364 GNUNET_i2s (&GDS_my_identity),
1365 GNUNET_h2s (&bd->key),
1366 (bd->ro & GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE) ? "x" : "-",
1367 (bd->ro & GNUNET_DHT_RO_RECORD_ROUTE) ? "R" : "-");
1368
1369 /* if we got a HELLO, consider it for our own routing table */
1370 hello_check (bd);
1371 GNUNET_assert (NULL != bf);
1372 GNUNET_CONTAINER_bloomfilter_add (bf,
1373 &GDS_my_identity_hash);
1374 GNUNET_STATISTICS_update (GDS_stats,
1375 "# PUT requests routed",
1376 1,
1377 GNUNET_NO);
1378 if (bd->data_size
1379 > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE
1380 - sizeof(struct PeerPutMessage))
1381 {
1382 GNUNET_break (0);
1383 return GNUNET_SYSERR;
1384 }
1385 msize = bd->data_size + sizeof(struct PeerPutMessage);
1386 if (tracking)
1387 {
1388 if (msize + sizeof (struct GNUNET_CRYPTO_EddsaSignature)
1389 > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE)
1390 {
1391 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1392 "Discarding message that is too large due to tracking\n");
1393 return GNUNET_NO;
1394 }
1395 msize += sizeof (struct GNUNET_CRYPTO_EddsaSignature);
1396 }
1397 else
1398 {
1399 /* If tracking is disabled, also discard any path we might have
1400 gotten from some broken peer */
1401 GNUNET_break_op (0 == put_path_length);
1402 put_path_length = 0;
1403 }
1404 if (truncated)
1405 msize += sizeof (struct GNUNET_PeerIdentity);
1406 if (msize + put_path_length * sizeof(struct GNUNET_DHT_PathElement)
1407 > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE)
1408 {
1409 unsigned int mlen;
1410 unsigned int ppl;
1411
1412 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1413 "Truncating path that is too large due\n");
1414 mlen = GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE - msize;
1415 if (! truncated)
1416 {
1417 /* We need extra space for the truncation, consider that,
1418 too! */
1419 truncated = true;
1420 mlen -= sizeof (struct GNUNET_PeerIdentity);
1421 msize += sizeof (struct GNUNET_PeerIdentity);
1422 }
1423 /* compute maximum length of path we can keep */
1424 ppl = mlen / sizeof (struct GNUNET_DHT_PathElement);
1425 GNUNET_assert (put_path_length - ppl > 0);
1426 trunc_peer = &put_path[put_path_length - ppl - 1].pred;
1427 put_path = &put_path[put_path_length - ppl];
1428 put_path_length = ppl;
1429 ro |= GNUNET_DHT_RO_TRUNCATED;
1430 }
1431 else
1432 {
1433 msize += bd->put_path_length * sizeof(struct GNUNET_DHT_PathElement);
1434 }
1435 target_count
1436 = get_target_peers (&bd->key,
1437 bf,
1438 hop_count,
1439 desired_replication_level,
1440 &targets);
1441 if (0 == target_count)
1442 {
1443 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1444 "Routing PUT for %s terminates after %u hops at %s\n",
1445 GNUNET_h2s (&bd->key),
1446 (unsigned int) hop_count,
1447 GNUNET_i2s (&GDS_my_identity));
1448 return GNUNET_NO;
1449 }
1450 skip_count = 0;
1451 for (unsigned int i = 0; i < target_count; i++)
1452 {
1453 struct PeerInfo *target = targets[i];
1454 struct PeerPutMessage *ppm;
1455 char buf[msize] GNUNET_ALIGN;
1456 struct GNUNET_DHT_PathElement *pp;
1457 void *data;
1458
1459 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1460 "Routing PUT for %s after %u hops to %s\n",
1461 GNUNET_h2s (&bd->key),
1462 (unsigned int) hop_count,
1463 GNUNET_i2s (&target->id));
1464 ppm = (struct PeerPutMessage *) buf;
1465 ppm->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_P2P_PUT);
1466 ppm->header.size = htons (sizeof (buf));
1467 ppm->type = htonl (bd->type);
1468 ppm->options = htons (ro);
1469 ppm->hop_count = htons (hop_count + 1);
1470 ppm->desired_replication_level = htons (desired_replication_level);
1471 ppm->put_path_length = htons (put_path_length);
1472 ppm->expiration_time = GNUNET_TIME_absolute_hton (bd->expiration_time);
1473 GNUNET_break (GNUNET_YES ==
1474 GNUNET_CONTAINER_bloomfilter_test (bf,
1475 &target->phash));
1476 GNUNET_assert (GNUNET_OK ==
1477 GNUNET_CONTAINER_bloomfilter_get_raw_data (bf,
1478 ppm->bloomfilter,
1479 DHT_BLOOM_SIZE));
1480 ppm->key = bd->key;
1481 if (truncated)
1482 {
1483 void *tgt = &ppm[1];
1484
1485 GNUNET_memcpy (tgt,
1486 trunc_peer,
1487 sizeof (struct GNUNET_PeerIdentity));
1488 pp = (struct GNUNET_DHT_PathElement *)
1489 (tgt + sizeof (struct GNUNET_PeerIdentity));
1490 }
1491 else
1492 {
1493 pp = (struct GNUNET_DHT_PathElement *) &ppm[1];
1494 }
1495 GNUNET_memcpy (pp,
1496 put_path,
1497 sizeof (struct GNUNET_DHT_PathElement) * put_path_length);
1498 if (tracking)
1499 {
1500 void *tgt = &pp[put_path_length];
1501 struct GNUNET_CRYPTO_EddsaSignature last_sig;
1502
1503 if (0 == put_path_length)
1504 {
1505 /* Note that the signature in 'put_path' was not initialized before,
1506 so this is crucial to avoid sending garbage. */
1507 sign_path (bd->data,
1508 bd->data_size,
1509 bd->expiration_time,
1510 trunc_peer,
1511 &target->id,
1512 &last_sig);
1513 }
1514 else
1515 {
1516 sign_path (bd->data,
1517 bd->data_size,
1518 bd->expiration_time,
1519 &pp[put_path_length - 1].pred,
1520 &target->id,
1521 &last_sig);
1522 }
1523 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1524 "Signing PUT PATH %u => %s\n",
1525 put_path_length,
1526 GNUNET_B2S (&last_sig));
1527 memcpy (tgt,
1528 &last_sig,
1529 sizeof (last_sig));
1530 data = tgt + sizeof (last_sig);
1531 }
1532 else /* ! tracking */
1533 {
1534 data = &ppm[1];
1535 }
1536 GNUNET_memcpy (data,
1537 bd->data,
1538 bd->data_size);
1539 do_send (target,
1540 &ppm->header);
1541 }
1542 GNUNET_free (targets);
1543 GNUNET_STATISTICS_update (GDS_stats,
1544 "# PUT messages queued for transmission",
1545 target_count - skip_count,
1546 GNUNET_NO);
1547 return (skip_count < target_count) ? GNUNET_OK : GNUNET_NO;
1548}
1549
1550
1551enum GNUNET_GenericReturnValue
1552GDS_NEIGHBOURS_handle_get (enum GNUNET_BLOCK_Type type,
1553 enum GNUNET_DHT_RouteOption options,
1554 uint16_t desired_replication_level,
1555 uint16_t hop_count,
1556 const struct GNUNET_HashCode *key,
1557 const void *xquery,
1558 size_t xquery_size,
1559 struct GNUNET_BLOCK_Group *bg,
1560 struct GNUNET_CONTAINER_BloomFilter *peer_bf)
1561{
1562 unsigned int target_count;
1563 struct PeerInfo **targets;
1564 size_t msize;
1565 size_t result_filter_size;
1566 void *result_filter;
1567 unsigned int skip_count;
1568
1569 GNUNET_assert (NULL != peer_bf);
1570 GNUNET_STATISTICS_update (GDS_stats,
1571 "# GET requests routed",
1572 1,
1573 GNUNET_NO);
1574 target_count = get_target_peers (key,
1575 peer_bf,
1576 hop_count,
1577 desired_replication_level,
1578 &targets);
1579 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1580 "Adding myself (%s) to GET bloomfilter for %s with RO(%s/%s)\n",
1581 GNUNET_i2s (&GDS_my_identity),
1582 GNUNET_h2s (key),
1583 (options & GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE) ? "x" : "-",
1584 (options & GNUNET_DHT_RO_RECORD_ROUTE) ? "R" : "-");
1585
1586 GNUNET_CONTAINER_bloomfilter_add (peer_bf,
1587 &GDS_my_identity_hash);
1588 if (0 == target_count)
1589 {
1590 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1591 "Routing GET for %s terminates after %u hops at %s\n",
1592 GNUNET_h2s (key),
1593 (unsigned int) hop_count,
1594 GNUNET_i2s (&GDS_my_identity));
1595 return GNUNET_NO;
1596 }
1597 if (GNUNET_OK !=
1598 GNUNET_BLOCK_group_serialize (bg,
1599 &result_filter,
1600 &result_filter_size))
1601 {
1602 result_filter = NULL;
1603 result_filter_size = 0;
1604 }
1605 msize = xquery_size + result_filter_size;
1606 if (msize + sizeof(struct PeerGetMessage) >= GNUNET_MAX_MESSAGE_SIZE)
1607 {
1608 GNUNET_break (0);
1609 GNUNET_free (result_filter);
1610 GNUNET_free (targets);
1611 return GNUNET_NO;
1612 }
1613 /* forward request */
1614 skip_count = 0;
1615 for (unsigned int i = 0; i < target_count; i++)
1616 {
1617 struct PeerInfo *target = targets[i];
1618 struct PeerGetMessage *pgm;
1619 char buf[sizeof (*pgm) + msize] GNUNET_ALIGN;
1620 char *rf;
1621
1622 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1623 "Routing GET for %s after %u hops to %s\n",
1624 GNUNET_h2s (key),
1625 (unsigned int) hop_count,
1626 GNUNET_i2s (&target->id));
1627 pgm = (struct PeerGetMessage *) buf;
1628 pgm->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_P2P_GET);
1629 pgm->header.size = htons (sizeof (buf));
1630 pgm->type = htonl (type);
1631 pgm->options = htons (options);
1632 pgm->hop_count = htons (hop_count + 1);
1633 pgm->desired_replication_level = htons (desired_replication_level);
1634 pgm->result_filter_size = htons ((uint16_t) result_filter_size);
1635 GNUNET_break (GNUNET_YES ==
1636 GNUNET_CONTAINER_bloomfilter_test (peer_bf,
1637 &target->phash));
1638 GNUNET_assert (GNUNET_OK ==
1639 GNUNET_CONTAINER_bloomfilter_get_raw_data (peer_bf,
1640 pgm->bloomfilter,
1641 DHT_BLOOM_SIZE));
1642 pgm->key = *key;
1643 rf = (char *) &pgm[1];
1644 GNUNET_memcpy (rf,
1645 result_filter,
1646 result_filter_size);
1647 GNUNET_memcpy (&rf[result_filter_size],
1648 xquery,
1649 xquery_size);
1650 do_send (target,
1651 &pgm->header);
1652 }
1653 GNUNET_STATISTICS_update (GDS_stats,
1654 "# GET messages queued for transmission",
1655 target_count - skip_count,
1656 GNUNET_NO);
1657 GNUNET_free (targets);
1658 GNUNET_free (result_filter);
1659 return (skip_count < target_count) ? GNUNET_OK : GNUNET_NO;
1660}
1661
1662
1663struct PeerInfo *
1664GDS_NEIGHBOURS_lookup_peer (const struct GNUNET_PeerIdentity *target)
1665{
1666 return GNUNET_CONTAINER_multipeermap_get (all_connected_peers,
1667 target);
1668}
1669
1670
1671bool
1672GDS_NEIGHBOURS_handle_reply (struct PeerInfo *pi,
1673 const struct GNUNET_DATACACHE_Block *bd,
1674 const struct GNUNET_HashCode *query_hash,
1675 unsigned int get_path_length,
1676 const struct GNUNET_DHT_PathElement *get_path)
1677{
1678 struct GNUNET_DHT_PathElement *paths;
1679 size_t msize;
1680 unsigned int ppl = bd->put_path_length;
1681 const struct GNUNET_DHT_PathElement *put_path = bd->put_path;
1682 enum GNUNET_DHT_RouteOption ro = bd->ro;
1683 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
1684 const struct GNUNET_PeerIdentity *trunc_peer
1685 = truncated
1686 ? &bd->trunc_peer
1687 : NULL;
1688 bool tracking = (0 != (ro & GNUNET_DHT_RO_RECORD_ROUTE));
1689#if SANITY_CHECKS > 1
1690 unsigned int failure_offset;
1691
1692 failure_offset
1693 = GNUNET_DHT_verify_path (bd->data,
1694 bd->data_size,
1695 bd->expiration_time,
1696 trunc_peer,
1697 put_path,
1698 ppl,
1699 get_path,
1700 get_path_length,
1701 &GDS_my_identity);
1702 if (0 != failure_offset)
1703 {
1704 GNUNET_assert (failure_offset <= ppl + get_path_length);
1705 GNUNET_break_op (0);
1706 if (failure_offset < ppl)
1707 {
1708 trunc_peer = &put_path[failure_offset - 1].pred;
1709 put_path += failure_offset;
1710 ppl -= failure_offset;
1711 truncated = true;
1712 ro |= GNUNET_DHT_RO_TRUNCATED;
1713 }
1714 else
1715 {
1716 failure_offset -= ppl;
1717 if (0 == failure_offset)
1718 trunc_peer = &put_path[ppl - 1].pred;
1719 else
1720 trunc_peer = &get_path[failure_offset - 1].pred;
1721 ppl = 0;
1722 put_path = NULL;
1723 truncated = true;
1724 ro |= GNUNET_DHT_RO_TRUNCATED;
1725 get_path += failure_offset;
1726 get_path_length -= failure_offset;
1727 }
1728 }
1729#endif
1730 msize = bd->data_size + sizeof (struct PeerResultMessage);
1731 if (msize > GNUNET_MAX_MESSAGE_SIZE)
1732 {
1733 GNUNET_break_op (0);
1734 return false;
1735 }
1736 if (truncated)
1737 msize += sizeof (struct GNUNET_PeerIdentity);
1738 if (tracking)
1739 msize += sizeof (struct GNUNET_CRYPTO_EddsaSignature);
1740 if (msize < bd->data_size)
1741 {
1742 GNUNET_break_op (0);
1743 return false;
1744 }
1745 if ( (GNUNET_MAX_MESSAGE_SIZE - msize)
1746 / sizeof(struct GNUNET_DHT_PathElement)
1747 < (get_path_length + ppl) )
1748 {
1749 get_path_length = 0;
1750 ppl = 0;
1751 }
1752 if ( (get_path_length > UINT16_MAX) ||
1753 (ppl > UINT16_MAX) )
1754 {
1755 GNUNET_break (0);
1756 get_path_length = 0;
1757 ppl = 0;
1758 }
1759 msize += (get_path_length + ppl)
1760 * sizeof(struct GNUNET_DHT_PathElement);
1761 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1762 "Forwarding reply for key %s to peer %s\n",
1763 GNUNET_h2s (query_hash),
1764 GNUNET_i2s (&pi->id));
1765 GNUNET_STATISTICS_update (GDS_stats,
1766 "# RESULT messages queued for transmission",
1767 1,
1768 GNUNET_NO);
1769 {
1770 struct PeerResultMessage *prm;
1771 char buf[msize] GNUNET_ALIGN;
1772 void *data;
1773
1774 prm = (struct PeerResultMessage *) buf;
1775 prm->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_P2P_RESULT);
1776 prm->header.size = htons (sizeof (buf));
1777 prm->type = htonl ((uint32_t) bd->type);
1778 prm->reserved = htons (0);
1779 prm->options = htons ((uint16_t) ro);
1780 prm->put_path_length = htons ((uint16_t) ppl);
1781 prm->get_path_length = htons ((uint16_t) get_path_length);
1782 prm->expiration_time = GNUNET_TIME_absolute_hton (bd->expiration_time);
1783 prm->key = *query_hash;
1784 if (truncated)
1785 {
1786 void *tgt = &prm[1];
1787
1788 GNUNET_memcpy (tgt,
1789 trunc_peer,
1790 sizeof (struct GNUNET_PeerIdentity));
1791 paths = (struct GNUNET_DHT_PathElement *)
1792 (tgt + sizeof (struct GNUNET_PeerIdentity));
1793 }
1794 else
1795 {
1796 paths = (struct GNUNET_DHT_PathElement *) &prm[1];
1797 }
1798 if (NULL != put_path)
1799 {
1800 GNUNET_memcpy (paths,
1801 put_path,
1802 ppl * sizeof(struct GNUNET_DHT_PathElement));
1803 }
1804 else
1805 {
1806 GNUNET_assert (0 == ppl);
1807 }
1808 if (NULL != get_path)
1809 {
1810 GNUNET_memcpy (&paths[ppl],
1811 get_path,
1812 get_path_length * sizeof(struct GNUNET_DHT_PathElement));
1813 }
1814 else
1815 {
1816 GNUNET_assert (0 == get_path_length);
1817 }
1818 if (tracking)
1819 {
1820 struct GNUNET_CRYPTO_EddsaSignature sig;
1821 void *tgt = &paths[get_path_length + ppl];
1822 const struct GNUNET_PeerIdentity *pred;
1823
1824 if (ppl + get_path_length > 0)
1825 pred = &paths[ppl + get_path_length - 1].pred;
1826 else if (truncated)
1827 pred = trunc_peer;
1828 else
1829 pred = NULL; /* we are first! */
1830 /* Note that the last signature in 'paths' was not initialized before,
1831 so this is crucial to avoid sending garbage. */
1832 sign_path (bd->data,
1833 bd->data_size,
1834 bd->expiration_time,
1835 pred,
1836 &pi->id,
1837 &sig);
1838 memcpy (tgt,
1839 &sig,
1840 sizeof (sig));
1841 data = tgt + sizeof (sig);
1842 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1843 "Signing GET PATH %u/%u of %s => %s\n",
1844 ppl,
1845 get_path_length,
1846 GNUNET_h2s (query_hash),
1847 GNUNET_B2S (&sig));
1848#if SANITY_CHECKS > 1
1849 {
1850 struct GNUNET_DHT_PathElement xpaths[get_path_length + 1];
1851
1852 memcpy (xpaths,
1853 &paths[ppl],
1854 get_path_length * sizeof (struct GNUNET_DHT_PathElement));
1855 xpaths[get_path_length].sig = sig;
1856 xpaths[get_path_length].pred = GDS_my_identity;
1857 if (0 !=
1858 GNUNET_DHT_verify_path (bd->data,
1859 bd->data_size,
1860 bd->expiration_time,
1861 trunc_peer,
1862 paths,
1863 ppl,
1864 xpaths,
1865 get_path_length + 1,
1866 &pi->id))
1867 {
1868 GNUNET_break (0);
1869 return false;
1870 }
1871 }
1872#endif
1873 }
1874 else
1875 {
1876 data = &prm[1];
1877 }
1878 GNUNET_memcpy (data,
1879 bd->data,
1880 bd->data_size);
1881 do_send (pi,
1882 &prm->header);
1883 }
1884 return true;
1885}
1886
1887
1888/**
1889 * Check validity of a p2p put request.
1890 *
1891 * @param cls closure with the `struct PeerInfo` of the sender
1892 * @param put message
1893 * @return #GNUNET_OK if the message is valid
1894 */
1895static enum GNUNET_GenericReturnValue
1896check_dht_p2p_put (void *cls,
1897 const struct PeerPutMessage *put)
1898{
1899 enum GNUNET_DHT_RouteOption ro
1900 = (enum GNUNET_DHT_RouteOption) ntohs (put->options);
1901 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
1902 bool has_path = (0 != (ro & GNUNET_DHT_RO_RECORD_ROUTE));
1903 uint16_t msize = ntohs (put->header.size);
1904 uint16_t putlen = ntohs (put->put_path_length);
1905 size_t xsize = (has_path
1906 ? sizeof (struct GNUNET_CRYPTO_EddsaSignature)
1907 : 0)
1908 + (truncated
1909 ? sizeof (struct GNUNET_PeerIdentity)
1910 : 0);
1911 size_t var_meta_size
1912 = putlen * sizeof(struct GNUNET_DHT_PathElement)
1913 + xsize;
1914
1915 (void) cls;
1916 if ( (msize <
1917 sizeof (struct PeerPutMessage) + var_meta_size) ||
1918 (putlen >
1919 (GNUNET_MAX_MESSAGE_SIZE
1920 - sizeof (struct PeerPutMessage)
1921 - xsize)
1922 / sizeof(struct GNUNET_DHT_PathElement)) )
1923 {
1924 GNUNET_break_op (0);
1925 return GNUNET_SYSERR;
1926 }
1927 if (GNUNET_BLOCK_TYPE_ANY == htonl (put->type))
1928 {
1929 GNUNET_break_op (0);
1930 return GNUNET_SYSERR;
1931 }
1932 return GNUNET_OK;
1933}
1934
1935
1936/**
1937 * Core handler for p2p put requests.
1938 *
1939 * @param cls closure with the `struct Target` of the sender
1940 * @param put message
1941 */
1942static void
1943handle_dht_p2p_put (void *cls,
1944 const struct PeerPutMessage *put)
1945{
1946 struct Target *t = cls;
1947 struct PeerInfo *peer = t->pi;
1948 enum GNUNET_DHT_RouteOption ro
1949 = (enum GNUNET_DHT_RouteOption) ntohs (put->options);
1950 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
1951 bool has_path = (0 != (ro & GNUNET_DHT_RO_RECORD_ROUTE));
1952 uint16_t msize = ntohs (put->header.size);
1953 uint16_t putlen = ntohs (put->put_path_length);
1954 const struct GNUNET_PeerIdentity *trunc_peer
1955 = truncated
1956 ? (const struct GNUNET_PeerIdentity *) &put[1]
1957 : NULL;
1958 const struct GNUNET_DHT_PathElement *put_path
1959 = truncated
1960 ? (const struct GNUNET_DHT_PathElement *) &trunc_peer[1]
1961 : (const struct GNUNET_DHT_PathElement *) &put[1];
1962 const struct GNUNET_CRYPTO_EddsaSignature *last_sig
1963 = has_path
1964 ? (const struct GNUNET_CRYPTO_EddsaSignature *) &put_path[putlen]
1965 : NULL;
1966 const char *data
1967 = has_path
1968 ? (const char *) &last_sig[1]
1969 : (const char *) &put_path[putlen];
1970 size_t var_meta_size
1971 = putlen * sizeof(struct GNUNET_DHT_PathElement)
1972 + (has_path ? sizeof (*last_sig) : 0)
1973 + (truncated ? sizeof (*trunc_peer) : 0);
1974 struct GNUNET_DATACACHE_Block bd = {
1975 .key = put->key,
1976 .expiration_time = GNUNET_TIME_absolute_ntoh (put->expiration_time),
1977 .type = ntohl (put->type),
1978 .ro = ro,
1979 .data_size = msize - sizeof(*put) - var_meta_size,
1980 .data = data
1981 };
1982
1983 if (NULL != trunc_peer)
1984 bd.trunc_peer = *trunc_peer;
1985 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1986 "PUT for `%s' from %s with RO (%s/%s)\n",
1987 GNUNET_h2s (&put->key),
1988 GNUNET_i2s (&peer->id),
1989 (bd.ro & GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE) ? "x" : "-",
1990 has_path ? "R" : "-");
1991 if (GNUNET_TIME_absolute_is_past (bd.expiration_time))
1992 {
1993 GNUNET_STATISTICS_update (GDS_stats,
1994 "# Expired PUTs discarded",
1995 1,
1996 GNUNET_NO);
1997 return;
1998 }
1999 {
2000 /* Only call 'check_block' if that keeps our CPU load (from
2001 the cryptography) below 50% on average */
2002 static struct GNUNET_TIME_Relative avg_latency;
2003 static struct GNUNET_TIME_Absolute next_time;
2004
2005 if (GNUNET_TIME_absolute_is_past (next_time))
2006 {
2007 struct GNUNET_TIME_Absolute now
2008 = GNUNET_TIME_absolute_get ();
2009 struct GNUNET_TIME_Relative latency;
2010 struct GNUNET_TIME_Relative delta;
2011
2012 if (GNUNET_NO ==
2013 GNUNET_BLOCK_check_block (GDS_block_context,
2014 bd.type,
2015 bd.data,
2016 bd.data_size))
2017 {
2018 GNUNET_break_op (0);
2019 return;
2020 }
2021 latency = GNUNET_TIME_absolute_get_duration (now);
2022 /* Use *moving average* to estimate check_block latency */
2023 avg_latency
2024 = GNUNET_TIME_relative_divide (
2025 GNUNET_TIME_relative_add (
2026 GNUNET_TIME_relative_multiply (avg_latency,
2027 7),
2028 latency),
2029 8);
2030 /* average delay = 50% of avg_latency => 50% CPU load from crypto (at most) */
2031 delta.rel_value_us
2032 = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
2033 avg_latency.rel_value_us > 0
2034 ? avg_latency.rel_value_us
2035 : 1LLU);
2036 next_time = GNUNET_TIME_relative_to_absolute (delta);
2037 }
2038 }
2039 if (! has_path)
2040 putlen = 0;
2041 GNUNET_STATISTICS_update (GDS_stats,
2042 "# P2P PUT requests received",
2043 1,
2044 GNUNET_NO);
2045 GNUNET_STATISTICS_update (GDS_stats,
2046 "# P2P PUT bytes received",
2047 msize,
2048 GNUNET_NO);
2049 {
2050 struct GNUNET_HashCode test_key;
2051 enum GNUNET_GenericReturnValue ret;
2052
2053 ret = GNUNET_BLOCK_get_key (GDS_block_context,
2054 bd.type,
2055 bd.data,
2056 bd.data_size,
2057 &test_key);
2058 switch (ret)
2059 {
2060 case GNUNET_YES:
2061 if (0 != GNUNET_memcmp (&test_key,
2062 &bd.key))
2063 {
2064 GNUNET_break_op (0);
2065 return;
2066 }
2067 break;
2068 case GNUNET_NO:
2069 /* cannot verify, good luck */
2070 break;
2071 case GNUNET_SYSERR:
2072 /* block type not supported, good luck */
2073 break;
2074 }
2075 }
2076
2077 {
2078 struct GNUNET_CONTAINER_BloomFilter *bf;
2079 struct GNUNET_DHT_PathElement pp[putlen + 1];
2080
2081 bf = GNUNET_CONTAINER_bloomfilter_init (put->bloomfilter,
2082 DHT_BLOOM_SIZE,
2083 GNUNET_CONSTANTS_BLOOMFILTER_K);
2084 GNUNET_break_op (GNUNET_YES ==
2085 GNUNET_CONTAINER_bloomfilter_test (bf,
2086 &peer->phash));
2087 /* extend 'put path' by sender */
2088 bd.put_path = pp;
2089 bd.put_path_length = putlen + 1;
2090 if (has_path)
2091 {
2092 unsigned int failure_offset;
2093
2094 GNUNET_memcpy (pp,
2095 put_path,
2096 putlen * sizeof(struct GNUNET_DHT_PathElement));
2097 pp[putlen].pred = peer->id;
2098 pp[putlen].sig = *last_sig;
2099#if SANITY_CHECKS
2100 /* TODO: might want to eventually implement probabilistic
2101 load-based path verification, but for now it is all or nothing */
2102 failure_offset
2103 = GNUNET_DHT_verify_path (bd.data,
2104 bd.data_size,
2105 bd.expiration_time,
2106 trunc_peer,
2107 pp,
2108 putlen + 1,
2109 NULL, 0, /* get_path */
2110 &GDS_my_identity);
2111#else
2112 failure_offset = 0;
2113#endif
2114 if (0 != failure_offset)
2115 {
2116 GNUNET_break_op (0);
2117 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2118 "Recorded put path invalid at offset %u, truncating\n",
2119 failure_offset);
2120 GNUNET_assert (failure_offset <= putlen + 1);
2121 bd.put_path = &pp[failure_offset];
2122 bd.put_path_length = (putlen + 1) - failure_offset;
2123 bd.ro |= GNUNET_DHT_RO_TRUNCATED;
2124 bd.trunc_peer = pp[failure_offset - 1].pred;
2125 }
2126 }
2127 else
2128 {
2129 bd.put_path_length = 0;
2130 }
2131
2132 /* give to local clients */
2133 GNUNET_break (GDS_CLIENTS_handle_reply (&bd,
2134 &bd.key,
2135 0, NULL /* get path */));
2136
2137 /* store locally */
2138 if ( (0 != (bd.ro & GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE)) ||
2139 (GDS_am_closest_peer (&put->key,
2140 bf)) )
2141 GDS_DATACACHE_handle_put (&bd);
2142 {
2143 enum GNUNET_GenericReturnValue forwarded;
2144
2145 /* route to other peers */
2146 forwarded
2147 = GDS_NEIGHBOURS_handle_put (&bd,
2148 ntohs (put->desired_replication_level),
2149 ntohs (put->hop_count),
2150 bf);
2151 /* notify monitoring clients */
2152 bd.ro |= ((GNUNET_OK == forwarded)
2153 ? GNUNET_DHT_RO_LAST_HOP
2154 : 0);
2155 GDS_CLIENTS_process_put (&bd,
2156 ntohs (put->hop_count),
2157 ntohs (put->desired_replication_level));
2158 }
2159 GNUNET_CONTAINER_bloomfilter_free (bf);
2160 }
2161}
2162
2163
2164/**
2165 * We have received a request for a HELLO. Sends our
2166 * HELLO back.
2167 *
2168 * @param pi sender of the request
2169 * @param key peers close to this key are desired
2170 * @param bg group for filtering peers
2171 */
2172static void
2173handle_find_my_hello (struct PeerInfo *pi,
2174 const struct GNUNET_HashCode *query_hash,
2175 struct GNUNET_BLOCK_Group *bg)
2176{
2177 size_t block_size = 0;
2178
2179 /* TODO: consider caching our HELLO block for a bit, to
2180 avoid signing too often here... */
2181 GNUNET_break (GNUNET_NO ==
2182 GNUNET_HELLO_builder_to_block (GDS_my_hello,
2183 &GDS_my_private_key,
2184 NULL,
2185 &block_size,
2186 GNUNET_TIME_UNIT_ZERO));
2187 {
2188 char block[block_size];
2189
2190 if (GNUNET_OK !=
2191 GNUNET_HELLO_builder_to_block (GDS_my_hello,
2192 &GDS_my_private_key,
2193 block,
2194 &block_size,
2195 GNUNET_TIME_UNIT_ZERO))
2196 {
2197 GNUNET_STATISTICS_update (GDS_stats,
2198 "# FIND PEER requests ignored due to lack of HELLO",
2199 1,
2200 GNUNET_NO);
2201 }
2202 else if (GNUNET_BLOCK_REPLY_OK_MORE ==
2203 GNUNET_BLOCK_check_reply (GDS_block_context,
2204 GNUNET_BLOCK_TYPE_DHT_HELLO,
2205 bg,
2206 &GDS_my_identity_hash,
2207 NULL, 0,
2208 block,
2209 block_size))
2210 {
2211 struct GNUNET_DATACACHE_Block bd = {
2212 .type = GNUNET_BLOCK_TYPE_DHT_HELLO,
2213 .expiration_time
2214 = GNUNET_TIME_relative_to_absolute (
2215 GNUNET_HELLO_ADDRESS_EXPIRATION),
2216 .key = GDS_my_identity_hash,
2217 .data = block,
2218 .data_size = block_size
2219 };
2220
2221 GNUNET_break (GDS_NEIGHBOURS_handle_reply (pi,
2222 &bd,
2223 query_hash,
2224 0, NULL /* get path */));
2225 }
2226 else
2227 {
2228 GNUNET_STATISTICS_update (GDS_stats,
2229 "# FIND PEER requests ignored due to Bloomfilter",
2230 1,
2231 GNUNET_NO);
2232 }
2233 }
2234}
2235
2236
2237/**
2238 * We have received a request for nearby HELLOs. Sends matching
2239 * HELLOs back.
2240 *
2241 * @param pi sender of the request
2242 * @param key peers close to this key are desired
2243 * @param bg group for filtering peers
2244 */
2245static void
2246handle_find_local_hello (struct PeerInfo *pi,
2247 const struct GNUNET_HashCode *query_hash,
2248 struct GNUNET_BLOCK_Group *bg)
2249{
2250 /* Force non-random selection by hop count */
2251 struct PeerInfo *peer;
2252
2253 peer = select_peer (query_hash,
2254 NULL,
2255 GDS_NSE_get () + 1);
2256 if ( (NULL != peer->hello) &&
2257 (! GNUNET_TIME_absolute_is_past (peer->hello_expiration)) &&
2258 (GNUNET_BLOCK_REPLY_OK_MORE ==
2259 GNUNET_BLOCK_check_reply (
2260 GDS_block_context,
2261 GNUNET_BLOCK_TYPE_DHT_HELLO,
2262 bg,
2263 &peer->phash,
2264 NULL, 0, /* xquery */
2265 peer->hello,
2266 peer->hello_size)) )
2267 {
2268 struct GNUNET_DATACACHE_Block bd = {
2269 .type = GNUNET_BLOCK_TYPE_DHT_HELLO,
2270 .expiration_time = peer->hello_expiration,
2271 .key = peer->phash,
2272 .data = peer->hello,
2273 .data_size = peer->hello_size
2274 };
2275
2276 GNUNET_break (GDS_NEIGHBOURS_handle_reply (pi,
2277 &bd,
2278 query_hash,
2279 0, NULL /* get path */));
2280 }
2281}
2282
2283
2284/**
2285 * Handle an exact result from local datacache for a GET operation.
2286 *
2287 * @param cls the `struct PeerInfo` for which this is a reply
2288 * @param bd details about the block we found locally
2289 */
2290static void
2291handle_local_result (void *cls,
2292 const struct GNUNET_DATACACHE_Block *bd)
2293{
2294 struct PeerInfo *peer = cls;
2295
2296 GNUNET_break (GDS_NEIGHBOURS_handle_reply (peer,
2297 bd,
2298 &bd->key,
2299 0, NULL /* get path */));
2300}
2301
2302
2303/**
2304 * Check validity of p2p get request.
2305 *
2306 * @param cls closure with the `struct Target` of the sender
2307 * @param get the message
2308 * @return #GNUNET_OK if the message is well-formed
2309 */
2310static enum GNUNET_GenericReturnValue
2311check_dht_p2p_get (void *cls,
2312 const struct PeerGetMessage *get)
2313{
2314 uint16_t msize = ntohs (get->header.size);
2315 uint16_t result_filter_size = ntohs (get->result_filter_size);
2316
2317 (void) cls;
2318 if (msize < sizeof(*get) + result_filter_size)
2319 {
2320 GNUNET_break_op (0);
2321 return GNUNET_SYSERR;
2322 }
2323 return GNUNET_OK;
2324}
2325
2326
2327/**
2328 * Core handler for p2p get requests.
2329 *
2330 * @param cls closure with the `struct Target` of the sender
2331 * @param get the message
2332 */
2333static void
2334handle_dht_p2p_get (void *cls,
2335 const struct PeerGetMessage *get)
2336{
2337 struct Target *t = cls;
2338 struct PeerInfo *peer = t->pi;
2339 uint16_t msize = ntohs (get->header.size);
2340 uint16_t result_filter_size = ntohs (get->result_filter_size);
2341 uint16_t hop_count = ntohs (get->hop_count);
2342 enum GNUNET_BLOCK_Type type = (enum GNUNET_BLOCK_Type) ntohl (get->type);
2343 enum GNUNET_DHT_RouteOption options = (enum GNUNET_DHT_RouteOption) ntohs (
2344 get->options);
2345 enum GNUNET_BLOCK_ReplyEvaluationResult eval = GNUNET_BLOCK_REPLY_OK_MORE;
2346 const void *result_filter = (const void *) &get[1];
2347 const void *xquery = result_filter + result_filter_size;
2348 size_t xquery_size = msize - sizeof (*get) - result_filter_size;
2349
2350 /* parse and validate message */
2351 GNUNET_STATISTICS_update (GDS_stats,
2352 "# P2P GET requests received",
2353 1,
2354 GNUNET_NO);
2355 GNUNET_STATISTICS_update (GDS_stats,
2356 "# P2P GET bytes received",
2357 msize,
2358 GNUNET_NO);
2359 if (GNUNET_NO ==
2360 GNUNET_BLOCK_check_query (GDS_block_context,
2361 type,
2362 &get->key,
2363 xquery,
2364 xquery_size))
2365 {
2366 /* request invalid */
2367 GNUNET_break_op (0);
2368 return;
2369 }
2370
2371 {
2372 struct GNUNET_BLOCK_Group *bg;
2373 struct GNUNET_CONTAINER_BloomFilter *peer_bf;
2374
2375 peer_bf = GNUNET_CONTAINER_bloomfilter_init (get->bloomfilter,
2376 DHT_BLOOM_SIZE,
2377 GNUNET_CONSTANTS_BLOOMFILTER_K);
2378 GNUNET_break_op (GNUNET_YES ==
2379 GNUNET_CONTAINER_bloomfilter_test (peer_bf,
2380 &peer->phash));
2381 bg = GNUNET_BLOCK_group_create (GDS_block_context,
2382 type,
2383 result_filter,
2384 result_filter_size,
2385 "filter-size",
2386 result_filter_size,
2387 NULL);
2388 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2389 "GET for %s at %s after %u hops\n",
2390 GNUNET_h2s (&get->key),
2391 GNUNET_i2s (&GDS_my_identity),
2392 (unsigned int) hop_count);
2393 /* local lookup (this may update the bg) */
2394 if ( (0 != (options & GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE)) ||
2395 (GDS_am_closest_peer (&get->key,
2396 peer_bf)) )
2397 {
2398 if ( (GNUNET_BLOCK_TYPE_DHT_HELLO == type) ||
2399 (GNUNET_BLOCK_TYPE_ANY == type) )
2400 {
2401 GNUNET_STATISTICS_update (GDS_stats,
2402 "# P2P HELLO lookup requests processed",
2403 1,
2404 GNUNET_NO);
2405 handle_find_my_hello (peer,
2406 &get->key,
2407 bg);
2408 if (0 != (options & GNUNET_DHT_RO_FIND_APPROXIMATE))
2409 handle_find_local_hello (peer,
2410 &get->key,
2411 bg);
2412 }
2413 if (GNUNET_BLOCK_TYPE_DHT_HELLO != type)
2414 {
2415 if (0 != (options & GNUNET_DHT_RO_FIND_APPROXIMATE))
2416 eval = GDS_DATACACHE_get_closest (&get->key,
2417 type,
2418 xquery,
2419 xquery_size,
2420 bg,
2421 &handle_local_result,
2422 peer);
2423 else
2424 eval = GDS_DATACACHE_handle_get (&get->key,
2425 type,
2426 xquery,
2427 xquery_size,
2428 bg,
2429 &handle_local_result,
2430 peer);
2431 }
2432 }
2433 else
2434 {
2435 GNUNET_STATISTICS_update (GDS_stats,
2436 "# P2P GET requests ONLY routed",
2437 1,
2438 GNUNET_NO);
2439 }
2440
2441 /* remember request for routing replies
2442 TODO: why should we do this if GNUNET_BLOCK_REPLY_OK_LAST == eval?
2443 */
2444 GDS_ROUTING_add (&peer->id,
2445 type,
2446 bg, /* bg now owned by routing, but valid at least until end of this function! */
2447 options,
2448 &get->key,
2449 xquery,
2450 xquery_size);
2451
2452 /* P2P forwarding */
2453 {
2454 bool forwarded = false;
2455 uint16_t desired_replication_level = ntohs (
2456 get->desired_replication_level);
2457
2458 if (eval != GNUNET_BLOCK_REPLY_OK_LAST)
2459 forwarded = (GNUNET_OK ==
2460 GDS_NEIGHBOURS_handle_get (type,
2461 options,
2462 desired_replication_level,
2463 hop_count,
2464 &get->key,
2465 xquery,
2466 xquery_size,
2467 bg,
2468 peer_bf));
2469 GDS_CLIENTS_process_get (
2470 options
2471 | (forwarded
2472 ? 0
2473 : GNUNET_DHT_RO_LAST_HOP),
2474 type,
2475 hop_count,
2476 desired_replication_level,
2477 &get->key);
2478 }
2479 /* clean up; note that 'bg' is owned by routing now! */
2480 GNUNET_CONTAINER_bloomfilter_free (peer_bf);
2481 }
2482}
2483
2484
2485/**
2486 * Process a reply, after the @a get_path has been updated.
2487 *
2488 * @param bd block details
2489 * @param query_hash hash of the original query, might not match key in @a bd
2490 * @param get_path_length number of entries in @a get_path
2491 * @param get_path path the reply has taken
2492 * @return true on success
2493 */
2494static bool
2495process_reply_with_path (const struct GNUNET_DATACACHE_Block *bd,
2496 const struct GNUNET_HashCode *query_hash,
2497 unsigned int get_path_length,
2498 const struct GNUNET_DHT_PathElement *get_path)
2499{
2500 /* forward to local clients */
2501 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2502 "Forwarding reply to local clients\n");
2503 if (! GDS_CLIENTS_handle_reply (bd,
2504 query_hash,
2505 get_path_length,
2506 get_path))
2507 {
2508 GNUNET_break (0);
2509 return false;
2510 }
2511 GDS_CLIENTS_process_get_resp (bd,
2512 get_path,
2513 get_path_length);
2514 if (GNUNET_YES == cache_results)
2515 {
2516 struct GNUNET_DHT_PathElement xput_path[GNUNET_NZL (get_path_length
2517 + bd->put_path_length)];
2518 struct GNUNET_DATACACHE_Block bdx = *bd;
2519
2520 if (NULL != bd->put_path)
2521 GNUNET_memcpy (xput_path,
2522 bd->put_path,
2523 bd->put_path_length * sizeof(struct
2524 GNUNET_DHT_PathElement));
2525 GNUNET_memcpy (&xput_path[bd->put_path_length],
2526 get_path,
2527 get_path_length * sizeof(struct GNUNET_DHT_PathElement));
2528 bdx.put_path = xput_path;
2529 bdx.put_path_length += get_path_length;
2530 GDS_DATACACHE_handle_put (&bdx);
2531 }
2532 /* forward to other peers */
2533 GDS_ROUTING_process (bd,
2534 query_hash,
2535 get_path_length,
2536 get_path);
2537 return true;
2538}
2539
2540
2541/**
2542 * Check validity of p2p result message.
2543 *
2544 * @param cls closure
2545 * @param prm message
2546 * @return #GNUNET_YES if the message is well-formed
2547 */
2548static enum GNUNET_GenericReturnValue
2549check_dht_p2p_result (void *cls,
2550 const struct PeerResultMessage *prm)
2551{
2552 uint16_t msize = ntohs (prm->header.size) - sizeof (*prm);
2553 enum GNUNET_DHT_RouteOption ro
2554 = (enum GNUNET_DHT_RouteOption) ntohs (prm->options);
2555 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
2556 bool tracked = (0 != (ro & GNUNET_DHT_RO_RECORD_ROUTE));
2557
2558 uint16_t get_path_length = ntohs (prm->get_path_length);
2559 uint16_t put_path_length = ntohs (prm->put_path_length);
2560 size_t vsize = (truncated ? sizeof (struct GNUNET_PeerIdentity) : 0)
2561 + (tracked ? sizeof (struct GNUNET_CRYPTO_EddsaSignature) : 0);
2562
2563 (void) cls;
2564 if ( (msize < vsize) ||
2565 (msize - vsize <
2566 (get_path_length + put_path_length)
2567 * sizeof(struct GNUNET_DHT_PathElement)) ||
2568 (get_path_length >
2569 GNUNET_MAX_MESSAGE_SIZE / sizeof(struct GNUNET_DHT_PathElement)) ||
2570 (put_path_length >
2571 GNUNET_MAX_MESSAGE_SIZE / sizeof(struct GNUNET_DHT_PathElement)) )
2572 {
2573 GNUNET_break_op (0);
2574 return GNUNET_SYSERR;
2575 }
2576 return GNUNET_OK;
2577}
2578
2579
2580/**
2581 * Core handler for p2p result messages.
2582 *
2583 * @param cls closure
2584 * @param prm message
2585 */
2586static void
2587handle_dht_p2p_result (void *cls,
2588 const struct PeerResultMessage *prm)
2589{
2590 struct Target *t = cls;
2591 struct PeerInfo *peer = t->pi;
2592 uint16_t msize = ntohs (prm->header.size) - sizeof (*prm);
2593 enum GNUNET_DHT_RouteOption ro
2594 = (enum GNUNET_DHT_RouteOption) ntohs (prm->options);
2595 bool truncated = (0 != (ro & GNUNET_DHT_RO_TRUNCATED));
2596 bool tracked = (0 != (ro & GNUNET_DHT_RO_RECORD_ROUTE));
2597 uint16_t get_path_length = ntohs (prm->get_path_length);
2598 uint16_t put_path_length = ntohs (prm->put_path_length);
2599 const struct GNUNET_PeerIdentity *trunc_peer
2600 = truncated
2601 ? (const struct GNUNET_PeerIdentity *) &prm[1]
2602 : NULL;
2603 const struct GNUNET_DHT_PathElement *put_path
2604 = truncated
2605 ? (const struct GNUNET_DHT_PathElement *) &trunc_peer[1]
2606 : (const struct GNUNET_DHT_PathElement *) &prm[1];
2607 const struct GNUNET_DHT_PathElement *get_path
2608 = &put_path[put_path_length];
2609 const struct GNUNET_CRYPTO_EddsaSignature *last_sig
2610 = tracked
2611 ? (const struct GNUNET_CRYPTO_EddsaSignature *) &get_path[get_path_length]
2612 : NULL;
2613 const void *data
2614 = tracked
2615 ? (const void *) &last_sig[1]
2616 : (const void *) &get_path[get_path_length];
2617 size_t vsize = (truncated ? sizeof (struct GNUNET_PeerIdentity) : 0)
2618 + (tracked ? sizeof (struct GNUNET_CRYPTO_EddsaSignature) : 0);
2619 struct GNUNET_DATACACHE_Block bd = {
2620 .expiration_time = GNUNET_TIME_absolute_ntoh (prm->expiration_time),
2621 .put_path = put_path,
2622 .put_path_length = put_path_length,
2623 .key = prm->key,
2624 .type = ntohl (prm->type),
2625 .ro = ro,
2626 .data = data,
2627 .data_size = msize - vsize - (get_path_length + put_path_length)
2628 * sizeof(struct GNUNET_DHT_PathElement)
2629 };
2630
2631 /* parse and validate message */
2632 if (GNUNET_TIME_absolute_is_past (bd.expiration_time))
2633 {
2634 GNUNET_STATISTICS_update (GDS_stats,
2635 "# Expired results discarded",
2636 1,
2637 GNUNET_NO);
2638 return;
2639 }
2640 if (GNUNET_OK !=
2641 GNUNET_BLOCK_check_block (GDS_block_context,
2642 bd.type,
2643 bd.data,
2644 bd.data_size))
2645 {
2646 GNUNET_break_op (0);
2647 return;
2648 }
2649 GNUNET_STATISTICS_update (GDS_stats,
2650 "# P2P RESULTS received",
2651 1,
2652 GNUNET_NO);
2653 GNUNET_STATISTICS_update (GDS_stats,
2654 "# P2P RESULT bytes received",
2655 msize,
2656 GNUNET_NO);
2657 {
2658 enum GNUNET_GenericReturnValue ret;
2659
2660 ret = GNUNET_BLOCK_get_key (GDS_block_context,
2661 bd.type,
2662 bd.data,
2663 bd.data_size,
2664 &bd.key);
2665 if (GNUNET_NO == ret)
2666 bd.key = prm->key;
2667 }
2668
2669 /* if we got a HELLO, consider it for our own routing table */
2670 hello_check (&bd);
2671
2672 /* Need to append 'peer' to 'get_path' */
2673 if (tracked)
2674 {
2675 struct GNUNET_DHT_PathElement xget_path[get_path_length + 1];
2676 struct GNUNET_DHT_PathElement *gp = xget_path;
2677 unsigned int failure_offset;
2678
2679 GNUNET_memcpy (xget_path,
2680 get_path,
2681 get_path_length * sizeof(struct GNUNET_DHT_PathElement));
2682 xget_path[get_path_length].pred = peer->id;
2683 /* use memcpy(), as last_sig may not be aligned */
2684 memcpy (&xget_path[get_path_length].sig,
2685 last_sig,
2686 sizeof (*last_sig));
2687#if SANITY_CHECKS
2688 /* TODO: might want to eventually implement probabilistic
2689 load-based path verification, but for now it is all or nothing */
2690 failure_offset
2691 = GNUNET_DHT_verify_path (bd.data,
2692 bd.data_size,
2693 bd.expiration_time,
2694 trunc_peer,
2695 put_path,
2696 put_path_length,
2697 gp,
2698 get_path_length + 1,
2699 &GDS_my_identity);
2700#else
2701 failure_offset = 0;
2702#endif
2703 if (0 != failure_offset)
2704 {
2705 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2706 "Recorded path invalid at offset %u, truncating\n",
2707 failure_offset);
2708 GNUNET_assert (failure_offset <= bd.put_path_length + get_path_length
2709 + 1);
2710 if (failure_offset < bd.put_path_length)
2711 {
2712 /* failure on put path */
2713 trunc_peer = &bd.put_path[failure_offset - 1].pred;
2714 bd.ro |= GNUNET_DHT_RO_TRUNCATED;
2715 bd.put_path = &bd.put_path[failure_offset];
2716 bd.put_path_length -= failure_offset;
2717 truncated = true;
2718 }
2719 else
2720 {
2721 /* failure on get path */
2722 failure_offset -= bd.put_path_length;
2723 if (0 == failure_offset)
2724 trunc_peer = &bd.put_path[bd.put_path_length - 1].pred;
2725 else
2726 trunc_peer = &gp[failure_offset - 1].pred;
2727 get_path_length -= failure_offset;
2728 gp = &gp[failure_offset];
2729 bd.put_path_length = 0;
2730 bd.put_path = NULL;
2731 bd.ro |= GNUNET_DHT_RO_TRUNCATED;
2732 truncated = true;
2733 }
2734 }
2735 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2736 "Extending GET path of length %u with %s\n",
2737 get_path_length,
2738 GNUNET_i2s (&peer->id));
2739 if (truncated)
2740 {
2741 GNUNET_assert (NULL != trunc_peer);
2742 bd.trunc_peer = *trunc_peer;
2743 }
2744 GNUNET_break (process_reply_with_path (&bd,
2745 &prm->key,
2746 get_path_length + 1,
2747 gp));
2748 }
2749 else
2750 {
2751 if (truncated)
2752 {
2753 GNUNET_assert (NULL != trunc_peer);
2754 bd.trunc_peer = *trunc_peer;
2755 }
2756 GNUNET_break (process_reply_with_path (&bd,
2757 &prm->key,
2758 0,
2759 NULL));
2760 }
2761}
2762
2763
2764/**
2765 * Check validity of a p2p hello message.
2766 *
2767 * @param cls closure
2768 * @param hello message
2769 * @return #GNUNET_YES if the message is well-formed
2770 */
2771static enum GNUNET_GenericReturnValue
2772check_dht_p2p_hello (void *cls,
2773 const struct GNUNET_MessageHeader *hello)
2774{
2775 struct Target *t = cls;
2776 struct PeerInfo *peer = t->pi;
2777 enum GNUNET_GenericReturnValue ret;
2778 size_t hellob_size;
2779 void *hellob;
2780 struct GNUNET_TIME_Absolute expiration;
2781
2782 ret = GNUNET_HELLO_dht_msg_to_block (hello,
2783 &peer->id,
2784 &hellob,
2785 &hellob_size,
2786 &expiration);
2787 GNUNET_free (hellob);
2788 return ret;
2789}
2790
2791
2792/**
2793 * Core handler for p2p HELLO messages.
2794 *
2795 * @param cls closure
2796 * @param hello message
2797 */
2798static void
2799handle_dht_p2p_hello (void *cls,
2800 const struct GNUNET_MessageHeader *hello)
2801{
2802 struct Target *t = cls;
2803 struct PeerInfo *peer = t->pi;
2804
2805 GNUNET_free (peer->hello);
2806 peer->hello_size = 0;
2807 GNUNET_break (GNUNET_OK ==
2808 GNUNET_HELLO_dht_msg_to_block (hello,
2809 &peer->id,
2810 &peer->hello,
2811 &peer->hello_size,
2812 &peer->hello_expiration));
2813}
2814
2815
2816void
2817GDS_u_receive (void *cls,
2818 void **tctx,
2819 void **sctx,
2820 const void *message,
2821 size_t message_size)
2822{
2823 struct Target *t = *tctx;
2824 struct GNUNET_MQ_MessageHandler core_handlers[] = {
2825 GNUNET_MQ_hd_var_size (dht_p2p_get,
2826 GNUNET_MESSAGE_TYPE_DHT_P2P_GET,
2827 struct PeerGetMessage,
2828 t),
2829 GNUNET_MQ_hd_var_size (dht_p2p_put,
2830 GNUNET_MESSAGE_TYPE_DHT_P2P_PUT,
2831 struct PeerPutMessage,
2832 t),
2833 GNUNET_MQ_hd_var_size (dht_p2p_result,
2834 GNUNET_MESSAGE_TYPE_DHT_P2P_RESULT,
2835 struct PeerResultMessage,
2836 t),
2837 GNUNET_MQ_hd_var_size (dht_p2p_hello,
2838 GNUNET_MESSAGE_TYPE_DHT_P2P_HELLO,
2839 struct GNUNET_MessageHeader,
2840 t),
2841 GNUNET_MQ_handler_end ()
2842 };
2843 const struct GNUNET_MessageHeader *mh = message;
2844
2845 (void) cls; /* the 'struct GDS_Underlay' */
2846 (void) sctx; /* our receiver address */
2847 if (NULL == t)
2848 {
2849 /* Received message claiming to originate from myself?
2850 Ignore! */
2851 GNUNET_break_op (0);
2852 return;
2853 }
2854 if (message_size < sizeof (*mh))
2855 {
2856 GNUNET_break_op (0);
2857 return;
2858 }
2859 if (message_size != ntohs (mh->size))
2860 {
2861 GNUNET_break_op (0);
2862 return;
2863 }
2864 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2865 "Handling message of type %u from peer %s\n",
2866 ntohs (mh->type),
2867 GNUNET_i2s (&t->pi->id));
2868 if (GNUNET_OK !=
2869 GNUNET_MQ_handle_message (core_handlers,
2870 mh))
2871 {
2872 GNUNET_break_op (0);
2873 return;
2874 }
2875}
2876
2877
2878/**
2879 * Callback function used to extract URIs from a builder.
2880 * Called when we should consider connecting to a peer.
2881 *
2882 * @param cls closure pointing to a `struct GNUNET_PeerIdentity *`
2883 * @param uri one of the URIs
2884 */
2885void
2886GDS_try_connect (void *cls,
2887 const struct GNUNET_PeerIdentity *pid,
2888 const char *uri)
2889{
2890 (void) cls;
2891 struct GNUNET_HashCode phash;
2892 int peer_bucket;
2893 struct PeerBucket *bucket;
2894
2895 if (0 == GNUNET_memcmp (&GDS_my_identity,
2896 pid))
2897 {
2898 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2899 "Got a HELLO for my own PID, ignoring it\n");
2900 return; /* that's us! */
2901 }
2902 GNUNET_CRYPTO_hash (pid,
2903 sizeof(*pid),
2904 &phash);
2905 peer_bucket = find_bucket (&phash);
2906 GNUNET_assert ( (peer_bucket >= 0) &&
2907 ((unsigned int) peer_bucket < MAX_BUCKETS));
2908 bucket = &k_buckets[peer_bucket];
2909 for (struct PeerInfo *pi = bucket->head;
2910 NULL != pi;
2911 pi = pi->next)
2912 if (0 ==
2913 GNUNET_memcmp (&pi->id,
2914 pid))
2915 {
2916 /* already connected */
2917 GDS_u_try_connect (pid,
2918 uri);
2919 return;
2920 }
2921 if (bucket->peers_size >= bucket_size)
2922 return; /* do not care */
2923 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2924 "Discovered peer %s at %s suitable for bucket %d (%u/%u), trying to connect\n",
2925 GNUNET_i2s (pid),
2926 uri,
2927 peer_bucket,
2928 bucket->peers_size,
2929 bucket_size);
2930 /* new peer that we like! */
2931 GDS_u_try_connect (pid,
2932 uri);
2933}
2934
2935
2936/**
2937 * Send @a msg to all peers in our buckets.
2938 *
2939 * @param msg message to broadcast
2940 */
2941void
2942GDS_NEIGHBOURS_broadcast (const struct GNUNET_MessageHeader *msg)
2943{
2944 for (unsigned int bc = 0; bc<closest_bucket; bc++)
2945 {
2946 struct PeerBucket *bucket = &k_buckets[bc];
2947 unsigned int count = 0;
2948
2949 for (struct PeerInfo *pos = bucket->head;
2950 NULL != pos;
2951 pos = pos->next)
2952 {
2953 if (count >= bucket_size)
2954 break; /* we only consider first #bucket_size entries per bucket */
2955 count++;
2956 do_send (pos,
2957 msg);
2958 }
2959 }
2960}
2961
2962
2963enum GNUNET_GenericReturnValue
2964GDS_NEIGHBOURS_init ()
2965{
2966
2967 unsigned long long temp_config_num;
2968
2969 disable_try_connect
2970 = GNUNET_CONFIGURATION_get_value_yesno (GDS_cfg,
2971 "DHT",
2972 "DISABLE_TRY_CONNECT");
2973 if (GNUNET_OK ==
2974 GNUNET_CONFIGURATION_get_value_number (GDS_cfg,
2975 "DHT",
2976 "bucket_size",
2977 &temp_config_num))
2978 bucket_size = (unsigned int) temp_config_num;
2979 cache_results
2980 = GNUNET_CONFIGURATION_get_value_yesno (GDS_cfg,
2981 "DHT",
2982 "CACHE_RESULTS");
2983 all_connected_peers = GNUNET_CONTAINER_multipeermap_create (256,
2984 GNUNET_YES);
2985 return GNUNET_OK;
2986}
2987
2988
2989void
2990GDS_NEIGHBOURS_done ()
2991{
2992 if (NULL == all_connected_peers)
2993 return;
2994 GNUNET_assert (0 ==
2995 GNUNET_CONTAINER_multipeermap_size (all_connected_peers));
2996 GNUNET_CONTAINER_multipeermap_destroy (all_connected_peers);
2997 all_connected_peers = NULL;
2998 GNUNET_assert (NULL == find_peer_task);
2999}
3000
3001
3002struct GNUNET_PeerIdentity *
3003GDS_NEIGHBOURS_get_id ()
3004{
3005 return &GDS_my_identity;
3006}
3007
3008
3009/* end of gnunet-service-dht_neighbours.c */
diff --git a/src/service/dht/gnunet-service-dht_neighbours.h b/src/service/dht/gnunet-service-dht_neighbours.h
new file mode 100644
index 000000000..7a6788068
--- /dev/null
+++ b/src/service/dht/gnunet-service-dht_neighbours.h
@@ -0,0 +1,226 @@
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 bd details about the reply
105 * @param query_hash query that was used for the request
106 * @param get_path_length number of entries in put_path
107 * @param get_path peers this reply has traversed so far (if tracked)
108 * @return true on success
109 */
110bool
111GDS_NEIGHBOURS_handle_reply (struct PeerInfo *pi,
112 const struct GNUNET_DATACACHE_Block *bd,
113 const struct GNUNET_HashCode *query_hash,
114 unsigned int get_path_length,
115 const struct GNUNET_DHT_PathElement *get_path);
116
117
118/**
119 * Check whether my identity is closer than any known peers. If a
120 * non-null bloomfilter is given, check if this is the closest peer
121 * that hasn't already been routed to.
122 *
123 * @param key hash code to check closeness to
124 * @param bloom bloomfilter, exclude these entries from the decision
125 * @return #GNUNET_YES if node location is closest,
126 * #GNUNET_NO otherwise.
127 */
128enum GNUNET_GenericReturnValue
129GDS_am_closest_peer (const struct GNUNET_HashCode *key,
130 const struct GNUNET_CONTAINER_BloomFilter *bloom);
131
132
133/**
134 * Callback function used to extract URIs from a builder.
135 * Called when we should consider connecting to a peer.
136 *
137 * @param cls closure
138 * @param pid pointing to a `struct GNUNET_PeerIdentity *`
139 * @param uri one of the URIs
140 */
141void
142GDS_try_connect (void *cls,
143 const struct GNUNET_PeerIdentity *pid,
144 const char *uri);
145
146
147/**
148 * Function to call when we connect to a peer and can henceforth transmit to
149 * that peer.
150 *
151 * @param cls the closure, must be a `struct GDS_Underlay`
152 * @param target handle to the target,
153 * pointer will remain valid until @e disconnect_cb is called
154 * @param pid peer identity,
155 * pointer will remain valid until @e disconnect_cb is called
156 * @param[out] ctx storage space for DHT to use in association with this target
157 */
158void
159GDS_u_connect (void *cls,
160 struct GNUNET_DHTU_Target *target,
161 const struct GNUNET_PeerIdentity *pid,
162 void **ctx);
163
164
165/**
166 * Function to call when we disconnected from a peer and can henceforth
167 * cannot transmit to that peer anymore.
168 *
169 * @param[in] ctx storage space used by the DHT in association with this target
170 */
171void
172GDS_u_disconnect (void *ctx);
173
174
175/**
176 * Function to call when we receive a message.
177 *
178 * @param cls the closure
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/service/dht/gnunet-service-dht_routing.c b/src/service/dht/gnunet-service-dht_routing.c
new file mode 100644
index 000000000..d81a2b2e1
--- /dev/null
+++ b/src/service/dht/gnunet-service-dht_routing.c
@@ -0,0 +1,429 @@
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 */
345void
346GDS_ROUTING_add (const struct GNUNET_PeerIdentity *sender,
347 enum GNUNET_BLOCK_Type type,
348 struct GNUNET_BLOCK_Group *bg,
349 enum GNUNET_DHT_RouteOption options,
350 const struct GNUNET_HashCode *key,
351 const void *xquery,
352 size_t xquery_size)
353{
354 struct RecentRequest *recent_req;
355
356 while (GNUNET_CONTAINER_heap_get_size (recent_heap) >= DHT_MAX_RECENT)
357 expire_oldest_entry ();
358 GNUNET_STATISTICS_update (GDS_stats,
359 "# Entries added to routing table",
360 1,
361 GNUNET_NO);
362 recent_req = GNUNET_malloc (sizeof(struct RecentRequest) + xquery_size);
363 recent_req->peer = *sender;
364 recent_req->key = *key;
365 recent_req->bg = bg;
366 recent_req->type = type;
367 recent_req->options = options;
368 recent_req->xquery = &recent_req[1];
369 GNUNET_memcpy (&recent_req[1],
370 xquery,
371 xquery_size);
372 recent_req->xquery_size = xquery_size;
373 if (GNUNET_SYSERR ==
374 GNUNET_CONTAINER_multihashmap_get_multiple (recent_map,
375 key,
376 &try_combine_recent,
377 recent_req))
378 {
379 GNUNET_STATISTICS_update (GDS_stats,
380 "# DHT requests combined",
381 1,
382 GNUNET_NO);
383 return;
384 }
385 recent_req->heap_node
386 = GNUNET_CONTAINER_heap_insert (
387 recent_heap,
388 recent_req,
389 GNUNET_TIME_absolute_get ().abs_value_us);
390 (void) GNUNET_CONTAINER_multihashmap_put (
391 recent_map,
392 key,
393 recent_req,
394 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
395}
396
397
398/**
399 * Initialize routing subsystem.
400 */
401void
402GDS_ROUTING_init ()
403{
404 recent_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
405 recent_map = GNUNET_CONTAINER_multihashmap_create (DHT_MAX_RECENT * 4 / 3,
406 GNUNET_NO);
407}
408
409
410/**
411 * Shutdown routing subsystem.
412 */
413void
414GDS_ROUTING_done ()
415{
416 while (GNUNET_CONTAINER_heap_get_size (recent_heap) > 0)
417 expire_oldest_entry ();
418 GNUNET_assert (0 ==
419 GNUNET_CONTAINER_heap_get_size (recent_heap));
420 GNUNET_CONTAINER_heap_destroy (recent_heap);
421 recent_heap = NULL;
422 GNUNET_assert (0 ==
423 GNUNET_CONTAINER_multihashmap_size (recent_map));
424 GNUNET_CONTAINER_multihashmap_destroy (recent_map);
425 recent_map = NULL;
426}
427
428
429/* end of gnunet-service-dht_routing.c */
diff --git a/src/service/dht/gnunet-service-dht_routing.h b/src/service/dht/gnunet-service-dht_routing.h
new file mode 100644
index 000000000..08c7de870
--- /dev/null
+++ b/src/service/dht/gnunet-service-dht_routing.h
@@ -0,0 +1,87 @@
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/service/dht/gnunet_dht_profiler.c b/src/service/dht/gnunet_dht_profiler.c
new file mode 100644
index 000000000..55a34bdf0
--- /dev/null
+++ b/src/service/dht/gnunet_dht_profiler.c
@@ -0,0 +1,1032 @@
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/service/dht/meson.build b/src/service/dht/meson.build
new file mode 100644
index 000000000..45157243d
--- /dev/null
+++ b/src/service/dht/meson.build
@@ -0,0 +1,97 @@
1libgnunetdht_src = ['dht_api.c']
2
3gnunetservicedht_src = ['gnunet-service-dht.c',
4 'plugin_dhtu_gnunet.c',
5 'plugin_dhtu_ip.c',
6 'gnunet-service-dht_datacache.c',
7 'gnunet-service-dht_neighbours.c',
8 'gnunet-service-dht_routing.c']
9
10configure_file(input : 'dht.conf.in',
11 output : 'dht.conf',
12 configuration : cdata,
13 install: true,
14 install_dir: pkgcfgdir)
15
16configure_file(input : 'dhtu.conf',
17 output : 'dhtu.conf',
18 configuration : cdata,
19 install: true,
20 install_dir: pkgcfgdir)
21
22if get_option('monolith')
23 foreach p : libgnunetdht_src + gnunetservicedht_src
24 gnunet_src += 'dht/' + p
25 endforeach
26endif
27
28libgnunetdht = library('gnunetdht',
29 libgnunetdht_src,
30 soversion: '4',
31 version: '4.0.0',
32 dependencies: libgnunetutil_dep,
33 include_directories: [incdir, configuration_inc],
34 install: true,
35 install_dir: get_option('libdir'))
36libgnunetdht_dep = declare_dependency(link_with : libgnunetdht)
37pkg.generate(libgnunetdht, url: 'https://www.gnunet.org',
38 description : 'Provides API for the R5N distributed hash table')
39
40executable ('gnunet-service-dht',
41 gnunetservicedht_src,
42 dependencies: [libgnunetdht_dep, libgnunetutil_dep,
43 libgnunetblock_dep,
44 libgnunetcore_dep,
45 libgnunetnse_dep,
46 libgnunettransportapplication_dep,
47 libgnunetpeerstore_dep,
48 m_dep,
49 libgnunetdatacache_dep,
50 libgnunetstatistics_dep,
51 libgnunetblockgroup_dep,
52 libgnunethello_dep],
53 include_directories: [incdir, configuration_inc],
54 install: true,
55 install_dir: get_option('libdir')/'gnunet'/'libexec')
56
57libgnunettestingdhtu = library('gnunettestingdhtu',
58 ['testing_dhtu_cmd_send.c'],
59 soversion: '0',
60 version: '0.0.0',
61 dependencies: [
62 libgnunetutil_dep,
63 libgnunetarm_dep,
64 libgnunettesting_dep
65 ],
66 include_directories: [incdir, configuration_inc],
67 install: true,
68 install_dir: get_option('libdir'))
69libgnunettestingdhtu_dep = declare_dependency(link_with : libgnunettestingdhtu)
70
71configure_file(input : 'test_dht_api_data.conf',
72 output : 'test_dht_api_data.conf',
73 copy: true)
74
75testdht_api = executable ('test_dht_api',
76 ['test_dht_api.c'],
77 dependencies: [libgnunetdht_dep,
78 libgnunetutil_dep,
79 libgnunettesting_dep,
80 libgnunethello_dep],
81 include_directories: [incdir, configuration_inc],
82 install: false)
83
84test('test_dht_api', testdht_api, suite: 'dht',
85 workdir: meson.current_build_dir())
86
87testdhtu_ip = executable('test_dhtu_ip',
88 ['test_dhtu_ip.c'],
89 dependencies: [libgnunetutil_dep,
90 libgnunettesting_dep],
91 include_directories: [incdir, configuration_inc],
92 install: false)
93
94test('test_dhtu_ip', testdhtu_ip, suite: 'dhtu',
95 workdir: meson.current_build_dir())
96
97
diff --git a/src/service/dht/plugin_dhtu_gnunet.c b/src/service/dht/plugin_dhtu_gnunet.c
new file mode 100644
index 000000000..99d1a0fda
--- /dev/null
+++ b/src/service/dht/plugin_dhtu_gnunet.c
@@ -0,0 +1,627 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @author Christian Grothoff
23 *
24 * @file plugin_dhtu_gnunet.c
25 * @brief plain IP based DHT network underlay
26 */
27#include "platform.h"
28#include "gnunet_dhtu_plugin.h"
29#include "gnunet_core_service.h"
30#include "gnunet_transport_application_service.h"
31#include "gnunet_hello_uri_lib.h"
32#include "gnunet_peerstore_service.h"
33#include "gnunet_nse_service.h"
34#include "plugin_dhtu_gnunet.h"
35
36/**
37 * Opaque handle that the underlay offers for our address to be used when
38 * sending messages to another peer.
39 */
40struct GNUNET_DHTU_Source
41{
42
43 /**
44 * Application context for this source.
45 */
46 void *app_ctx;
47
48};
49
50
51/**
52 * Opaque handle that the underlay offers for the target peer when sending
53 * messages to another peer.
54 */
55struct GNUNET_DHTU_Target
56{
57
58 /**
59 * Application context for this target.
60 */
61 void *app_ctx;
62
63 /**
64 * Our plugin with its environment.
65 */
66 struct Plugin *plugin;
67
68 /**
69 * CORE MQ to send messages to this peer.
70 */
71 struct GNUNET_MQ_Handle *mq;
72
73 /**
74 * Head of preferences expressed for this target.
75 */
76 struct GNUNET_DHTU_PreferenceHandle *ph_head;
77
78 /**
79 * Tail of preferences expressed for this target.
80 */
81 struct GNUNET_DHTU_PreferenceHandle *ph_tail;
82
83 /**
84 * Transport suggest handle.
85 */
86 struct GNUNET_TRANSPORT_ApplicationSuggestHandle *ash;
87
88 /**
89 * Identity of this peer.
90 */
91 struct GNUNET_PeerIdentity pid;
92
93 /**
94 * Preference counter, length of the @a ph_head DLL.
95 */
96 unsigned int ph_count;
97
98};
99
100
101/**
102 * Opaque handle expressing a preference of the DHT to
103 * keep a particular target connected.
104 */
105struct GNUNET_DHTU_PreferenceHandle
106{
107 /**
108 * Kept in a DLL.
109 */
110 struct GNUNET_DHTU_PreferenceHandle *next;
111
112 /**
113 * Kept in a DLL.
114 */
115 struct GNUNET_DHTU_PreferenceHandle *prev;
116
117 /**
118 * Target a preference was expressed for.
119 */
120 struct GNUNET_DHTU_Target *target;
121};
122
123
124/**
125 * Closure for all plugin functions.
126 */
127struct Plugin
128{
129
130 /**
131 * Our "source" address. Traditional CORE API does not tell us which source
132 * it is, so they are all identical.
133 */
134 struct GNUNET_DHTU_Source src;
135
136 /**
137 * Callbacks into the DHT.
138 */
139 struct GNUNET_DHTU_PluginEnvironment *env;
140
141 /**
142 * Handle to the PEERSTORE service.
143 */
144 struct GNUNET_PEERSTORE_Handle *peerstore;
145
146 /**
147 * Handle to the CORE service.
148 */
149 struct GNUNET_CORE_Handle *core;
150
151 /**
152 * Handle to Transport service.
153 */
154 struct GNUNET_TRANSPORT_ApplicationHandle *transport;
155
156 /**
157 * Handle to the NSE service.
158 */
159 struct GNUNET_NSE_Handle *nse;
160
161 /**
162 * Our peerstore notification context. We use notification
163 * to instantly learn about new peers as they are discovered.
164 */
165 struct GNUNET_PEERSTORE_Monitor *peerstore_notify;
166
167 /**
168 * Identity of this peer.
169 */
170 struct GNUNET_PeerIdentity my_identity;
171
172 /**
173 * Our private key.
174 */
175 struct GNUNET_CRYPTO_EddsaPrivateKey *my_priv;
176
177};
178
179
180// #include "../peerinfo-tool/gnunet-peerinfo_plugins.c"
181
182
183/**
184 * Request creation of a session with a peer at the given @a address.
185 *
186 * @param cls closure (internal context for the plugin)
187 * @param pid target identity of the peer to connect to
188 * @param address target address to connect to
189 */
190static void
191gnunet_try_connect (void *cls,
192 const struct GNUNET_PeerIdentity *pid,
193 const char *address)
194{
195 struct Plugin *plugin = cls;
196 enum GNUNET_NetworkType nt = 0;
197 char *addr;
198 const char *eou;
199 int pfx_len;
200
201 eou = strstr (address,
202 "://");
203 if (NULL == eou)
204 {
205 GNUNET_break (0);
206 return;
207 }
208 pfx_len = eou - address;
209 eou += 3;
210 GNUNET_asprintf (&addr,
211 "%.*s-%s",
212 pfx_len,
213 address,
214 eou);
215 GNUNET_TRANSPORT_application_validate (plugin->transport,
216 pid,
217 nt,
218 addr);
219 GNUNET_free (addr);
220}
221
222
223/**
224 * Request underlay to keep the connection to @a target alive if possible.
225 * Hold may be called multiple times to express a strong preference to
226 * keep a connection, say because a @a target is in multiple tables.
227 *
228 * @param cls closure
229 * @param target connection to keep alive
230 */
231static struct GNUNET_DHTU_PreferenceHandle *
232gnunet_hold (void *cls,
233 struct GNUNET_DHTU_Target *target)
234{
235 struct Plugin *plugin = cls;
236 struct GNUNET_DHTU_PreferenceHandle *ph;
237
238 ph = GNUNET_new (struct GNUNET_DHTU_PreferenceHandle);
239 ph->target = target;
240 GNUNET_CONTAINER_DLL_insert (target->ph_head,
241 target->ph_tail,
242 ph);
243 target->ph_count++;
244 if (NULL != target->ash)
245 GNUNET_TRANSPORT_application_suggest_cancel (target->ash);
246 target->ash
247 = GNUNET_TRANSPORT_application_suggest (plugin->transport,
248 &target->pid,
249 GNUNET_MQ_PRIO_BEST_EFFORT,
250 GNUNET_BANDWIDTH_ZERO);
251 return ph;
252}
253
254
255/**
256 * Do no long request underlay to keep the connection alive.
257 *
258 * @param cls closure
259 * @param target connection to keep alive
260 */
261static void
262gnunet_drop (struct GNUNET_DHTU_PreferenceHandle *ph)
263{
264 struct GNUNET_DHTU_Target *target = ph->target;
265 struct Plugin *plugin = target->plugin;
266
267 GNUNET_CONTAINER_DLL_remove (target->ph_head,
268 target->ph_tail,
269 ph);
270 target->ph_count--;
271 GNUNET_free (ph);
272 if (NULL != target->ash)
273 GNUNET_TRANSPORT_application_suggest_cancel (target->ash);
274 if (0 == target->ph_count)
275 target->ash = NULL;
276 else
277 target->ash
278 = GNUNET_TRANSPORT_application_suggest (plugin->transport,
279 &target->pid,
280 GNUNET_MQ_PRIO_BEST_EFFORT,
281 GNUNET_BANDWIDTH_ZERO);
282}
283
284
285/**
286 * Send message to some other participant over the network. Note that
287 * sending is not guaranteeing that the other peer actually received the
288 * message. For any given @a target, the DHT must wait for the @a
289 * finished_cb to be called before calling send() again.
290 *
291 * @param cls closure (internal context for the plugin)
292 * @param target receiver identification
293 * @param msg message
294 * @param msg_size number of bytes in @a msg
295 * @param finished_cb function called once transmission is done
296 * (not called if @a target disconnects, then only the
297 * disconnect_cb is called).
298 * @param finished_cb_cls closure for @a finished_cb
299 */
300static void
301gnunet_send (void *cls,
302 struct GNUNET_DHTU_Target *target,
303 const void *msg,
304 size_t msg_size,
305 GNUNET_SCHEDULER_TaskCallback finished_cb,
306 void *finished_cb_cls)
307{
308 struct GNUNET_MQ_Envelope *env;
309 struct GNUNET_MessageHeader *cmsg;
310
311 env = GNUNET_MQ_msg_extra (cmsg,
312 msg_size,
313 GNUNET_MESSAGE_TYPE_DHT_CORE);
314 GNUNET_MQ_notify_sent (env,
315 finished_cb,
316 finished_cb_cls);
317 memcpy (&cmsg[1],
318 msg,
319 msg_size);
320 GNUNET_MQ_send (target->mq,
321 env);
322}
323
324
325/**
326 * Method called whenever a given peer connects.
327 *
328 * @param cls closure
329 * @param peer peer identity this notification is about
330 * @return closure associated with @a peer. given to mq callbacks and
331 * #GNUNET_CORE_DisconnectEventHandler
332 */
333static void *
334core_connect_cb (void *cls,
335 const struct GNUNET_PeerIdentity *peer,
336 struct GNUNET_MQ_Handle *mq)
337{
338 struct Plugin *plugin = cls;
339 struct GNUNET_DHTU_Target *target;
340
341 target = GNUNET_new (struct GNUNET_DHTU_Target);
342 target->plugin = plugin;
343 target->mq = mq;
344 target->pid = *peer;
345 plugin->env->connect_cb (plugin->env->cls,
346 target,
347 &target->pid,
348 &target->app_ctx);
349 return target;
350}
351
352
353/**
354 * Method called whenever a peer disconnects.
355 *
356 * @param cls closure
357 * @param peer peer identity this notification is about
358 * @param peer_cls closure associated with peer. given in
359 * #GNUNET_CORE_ConnectEventHandler
360 */
361static void
362core_disconnect_cb (void *cls,
363 const struct GNUNET_PeerIdentity *peer,
364 void *peer_cls)
365{
366 struct Plugin *plugin = cls;
367 struct GNUNET_DHTU_Target *target = peer_cls;
368
369 plugin->env->disconnect_cb (target->app_ctx);
370 if (NULL != target->ash)
371 GNUNET_TRANSPORT_application_suggest_cancel (target->ash);
372 GNUNET_free (target);
373}
374
375
376static void
377add_addr (void *cls,
378 const struct GNUNET_PeerIdentity *pid,
379 const char *addr)
380{
381 struct Plugin *plugin = cls;
382
383 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
384 "peerinfo_cb addr %s\n",
385 addr);
386 plugin->env->address_add_cb (plugin->env->cls,
387 addr,
388 &plugin->src,
389 &plugin->src.app_ctx);
390}
391
392
393/**
394 * Find the @a hello for our identity and then pass
395 * it to the DHT as a URL. Note that we only
396 * add addresses, never remove them, due to limitations
397 * of the current peerinfo/core/transport APIs.
398 * This will change with TNG.
399 *
400 * @param cls a `struct Plugin`
401 * @param peer id of the peer, NULL for last call
402 * @param hello hello message for the peer (can be NULL)
403 * @param err_msg message
404 */
405static void
406peerinfo_cb (void *cls,
407 const struct GNUNET_PEERSTORE_Record *record,
408 const char *emsg)
409{
410 struct Plugin *plugin = cls;
411 struct GNUNET_HELLO_Builder *builder;
412 struct GNUNET_MessageHeader *hello;
413
414 hello = record->value;
415 if (NULL == hello)
416 return;
417 if (0 !=
418 GNUNET_memcmp (&record->peer,
419 &plugin->my_identity))
420 {
421 GNUNET_PEERSTORE_monitor_next (plugin->peerstore_notify, 1);
422 return;
423 }
424 builder = GNUNET_HELLO_builder_from_msg (hello);
425 GNUNET_HELLO_builder_iterate (builder,
426 add_addr,
427 plugin);
428 GNUNET_HELLO_builder_free (builder);
429 GNUNET_PEERSTORE_monitor_next (plugin->peerstore_notify, 1);
430}
431
432
433static void
434error_cb (void *cls)
435{
436 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
437 "Error in PEERSTORE monitoring\n");
438}
439
440
441static void
442sync_cb (void *cls)
443{
444 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
445 "Done with initial PEERSTORE iteration during monitoring\n");
446}
447
448
449/**
450 * Function called after #GNUNET_CORE_connect has succeeded (or failed
451 * for good). Note that the private key of the peer is intentionally
452 * not exposed here; if you need it, your process should try to read
453 * the private key file directly (which should work if you are
454 * authorized...). Implementations of this function must not call
455 * #GNUNET_CORE_disconnect (other than by scheduling a new task to
456 * do this later).
457 *
458 * @param cls closure
459 * @param my_identity ID of this peer, NULL if we failed
460 */
461static void
462core_init_cb (void *cls,
463 const struct GNUNET_PeerIdentity *my_identity)
464{
465 struct Plugin *plugin = cls;
466
467 plugin->my_identity = *my_identity;
468 plugin->peerstore_notify = GNUNET_PEERSTORE_monitor_start (plugin->env->cfg,
469 GNUNET_YES,
470 "peerstore",
471 NULL,
472 GNUNET_PEERSTORE_HELLO_KEY,
473 &error_cb,
474 NULL,
475 &sync_cb,
476 NULL,
477 &peerinfo_cb,
478 plugin);
479}
480
481
482/**
483 * Anything goes, always return #GNUNET_OK.
484 *
485 * @param cls unused
486 * @param msg message to check
487 * @return #GNUNET_OK if all is fine
488 */
489static int
490check_core_message (void *cls,
491 const struct GNUNET_MessageHeader *msg)
492{
493 (void) cls;
494 (void) msg;
495 return GNUNET_OK;
496}
497
498
499/**
500 * Handle message from CORE for the DHT. Passes it to the
501 * DHT logic.
502 *
503 * @param cls a `struct GNUNET_DHTU_Target` of the sender
504 * @param msg the message we received
505 */
506static void
507handle_core_message (void *cls,
508 const struct GNUNET_MessageHeader *msg)
509{
510 struct GNUNET_DHTU_Target *origin = cls;
511 struct Plugin *plugin = origin->plugin;
512
513 plugin->env->receive_cb (plugin->env->cls,
514 &origin->app_ctx,
515 &plugin->src.app_ctx,
516 &msg[1],
517 ntohs (msg->size) - sizeof (*msg));
518}
519
520
521/**
522 * Callback to call when network size estimate is updated.
523 *
524 * @param cls closure
525 * @param timestamp time when the estimate was received from the server (or created by the server)
526 * @param logestimate the log(Base 2) value of the current network size estimate
527 * @param std_dev standard deviation for the estimate
528 */
529static void
530nse_cb (void *cls,
531 struct GNUNET_TIME_Absolute timestamp,
532 double logestimate,
533 double std_dev)
534{
535 struct Plugin *plugin = cls;
536
537 plugin->env->network_size_cb (plugin->env->cls,
538 timestamp,
539 logestimate,
540 std_dev);
541}
542
543
544/**
545 * Exit point from the plugin.
546 *
547 * @param cls closure (our `struct Plugin`)
548 * @return NULL
549 */
550void *
551DHTU_gnunet_done (struct GNUNET_DHTU_PluginFunctions *api)
552{
553 struct Plugin *plugin = api->cls;
554
555 if (NULL != plugin->nse)
556 GNUNET_NSE_disconnect (plugin->nse);
557 plugin->env->network_size_cb (plugin->env->cls,
558 GNUNET_TIME_UNIT_FOREVER_ABS,
559 0.0,
560 0.0);
561 if (NULL != plugin->core)
562 GNUNET_CORE_disconnect (plugin->core);
563 if (NULL != plugin->transport)
564 GNUNET_TRANSPORT_application_done (plugin->transport);
565 if (NULL != plugin->peerstore_notify)
566 GNUNET_PEERSTORE_monitor_stop (plugin->peerstore_notify);
567 if (NULL != plugin->peerstore)
568 GNUNET_PEERSTORE_disconnect (plugin->peerstore);
569 // GPI_plugins_unload ();
570 GNUNET_free (plugin->my_priv);
571 GNUNET_free (plugin);
572 GNUNET_free (api);
573 return NULL;
574}
575
576
577/**
578 * Entry point for the plugin.
579 *
580 * @param cls closure (the `struct GNUNET_DHTU_PluginEnvironment`)
581 * @return the plugin's API
582 */
583struct GNUNET_DHTU_PluginFunctions *
584DHTU_gnunet_init (struct GNUNET_DHTU_PluginEnvironment *env)
585{
586 struct GNUNET_DHTU_PluginFunctions *api;
587 struct Plugin *plugin;
588 struct GNUNET_MQ_MessageHandler handlers[] = {
589 GNUNET_MQ_hd_var_size (core_message,
590 GNUNET_MESSAGE_TYPE_DHT_CORE,
591 struct GNUNET_MessageHeader,
592 NULL),
593 GNUNET_MQ_handler_end ()
594 };
595
596 plugin = GNUNET_new (struct Plugin);
597 plugin->my_priv = GNUNET_CRYPTO_eddsa_key_create_from_configuration (
598 env->cfg);
599 plugin->env = env;
600 api = GNUNET_new (struct GNUNET_DHTU_PluginFunctions);
601 api->cls = plugin;
602 api->try_connect = &gnunet_try_connect;
603 api->hold = &gnunet_hold;
604 api->drop = &gnunet_drop;
605 api->send = &gnunet_send;
606 plugin->peerstore = GNUNET_PEERSTORE_connect (env->cfg);
607 plugin->transport = GNUNET_TRANSPORT_application_init (env->cfg);
608 plugin->core = GNUNET_CORE_connect (env->cfg,
609 plugin,
610 &core_init_cb,
611 &core_connect_cb,
612 &core_disconnect_cb,
613 handlers);
614 plugin->nse = GNUNET_NSE_connect (env->cfg,
615 &nse_cb,
616 plugin);
617 if ( (NULL == plugin->transport) ||
618 (NULL == plugin->core) ||
619 (NULL == plugin->nse) )
620 {
621 GNUNET_break (0);
622 DHTU_gnunet_done (api);
623 return NULL;
624 }
625 // GPI_plugins_load (env->cfg);
626 return api;
627}
diff --git a/src/service/dht/plugin_dhtu_gnunet.h b/src/service/dht/plugin_dhtu_gnunet.h
new file mode 100644
index 000000000..a4a594742
--- /dev/null
+++ b/src/service/dht/plugin_dhtu_gnunet.h
@@ -0,0 +1,44 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2024 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#ifndef PLUGIN_DHTU_GNUNET_H
22#define PLUGIN_DHTU_GNUNET_H
23#include "gnunet_dhtu_plugin.h"
24
25/**
26 * Exit point from the plugin.
27 *
28 * @param cls closure (our `struct Plugin`)
29 * @return NULL
30 */
31void *
32DHTU_gnunet_done (struct GNUNET_DHTU_PluginFunctions *p);
33
34
35/**
36 * Entry point for the plugin.
37 *
38 * @param cls closure (the `struct GNUNET_DHTU_PluginEnvironment`)
39 * @return the plugin's API
40 */
41struct GNUNET_DHTU_PluginFunctions *
42DHTU_gnunet_init (struct GNUNET_DHTU_PluginEnvironment *e);
43
44#endif
diff --git a/src/service/dht/plugin_dhtu_ip.c b/src/service/dht/plugin_dhtu_ip.c
new file mode 100644
index 000000000..3c01257dc
--- /dev/null
+++ b/src/service/dht/plugin_dhtu_ip.c
@@ -0,0 +1,1170 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021, 2022 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @author Christian Grothoff
23 *
24 * @file plugin_dhtu_ip.c
25 * @brief plain IP based DHT network underlay
26 */
27#include "platform.h"
28#include "gnunet_dhtu_plugin.h"
29#include "plugin_dhtu_ip.h"
30
31/**
32 * How frequently should we re-scan our local interfaces for IPs?
33 */
34#define SCAN_FREQ GNUNET_TIME_UNIT_MINUTES
35
36/**
37 * Maximum number of concurrently active destinations to support.
38 */
39#define MAX_DESTS 256
40
41
42/**
43 * Opaque handle that the underlay offers for our address to be used when
44 * sending messages to another peer.
45 */
46struct GNUNET_DHTU_Source
47{
48
49 /**
50 * Kept in a DLL.
51 */
52 struct GNUNET_DHTU_Source *next;
53
54 /**
55 * Kept in a DLL.
56 */
57 struct GNUNET_DHTU_Source *prev;
58
59 /**
60 * Application context for this source.
61 */
62 void *app_ctx;
63
64 /**
65 * Address in URL form ("ip+udp://$PID/$IP:$PORT")
66 */
67 char *address;
68
69 /**
70 * My actual address.
71 */
72 struct sockaddr_storage addr;
73
74 /**
75 * Number of bytes in @a addr.
76 */
77 socklen_t addrlen;
78
79 /**
80 * Last generation this address was observed.
81 */
82 unsigned int scan_generation;
83
84};
85
86
87/**
88 * Opaque handle that the underlay offers for the target peer when sending
89 * messages to another peer.
90 */
91struct GNUNET_DHTU_Target
92{
93
94 /**
95 * Kept in a DLL.
96 */
97 struct GNUNET_DHTU_Target *next;
98
99 /**
100 * Kept in a DLL.
101 */
102 struct GNUNET_DHTU_Target *prev;
103
104 /**
105 * Application context for this target.
106 */
107 void *app_ctx;
108
109 /**
110 * Head of preferences expressed for this target.
111 */
112 struct GNUNET_DHTU_PreferenceHandle *ph_head;
113
114 /**
115 * Tail of preferences expressed for this target.
116 */
117 struct GNUNET_DHTU_PreferenceHandle *ph_tail;
118
119 /**
120 * Peer's identity.
121 */
122 struct GNUNET_PeerIdentity pid;
123
124 /**
125 * Target IP address.
126 */
127 struct sockaddr_storage addr;
128
129 /**
130 * Number of bytes in @a addr.
131 */
132 socklen_t addrlen;
133
134 /**
135 * Preference counter, length of the @a ph_head DLL.
136 */
137 unsigned int ph_count;
138
139};
140
141/**
142 * Opaque handle expressing a preference of the DHT to
143 * keep a particular target connected.
144 */
145struct GNUNET_DHTU_PreferenceHandle
146{
147 /**
148 * Kept in a DLL.
149 */
150 struct GNUNET_DHTU_PreferenceHandle *next;
151
152 /**
153 * Kept in a DLL.
154 */
155 struct GNUNET_DHTU_PreferenceHandle *prev;
156
157 /**
158 * Target a preference was expressed for.
159 */
160 struct GNUNET_DHTU_Target *target;
161};
162
163
164/**
165 * Closure for all plugin functions.
166 */
167struct Plugin
168{
169 /**
170 * Callbacks into the DHT.
171 */
172 struct GNUNET_DHTU_PluginEnvironment *env;
173
174 /**
175 * Head of sources where we receive traffic.
176 */
177 struct GNUNET_DHTU_Source *src_head;
178
179 /**
180 * Tail of sources where we receive traffic.
181 */
182 struct GNUNET_DHTU_Source *src_tail;
183
184 /**
185 * Head of destinations that are active. Sorted by
186 * last use, with latest used at the head.
187 */
188 struct GNUNET_DHTU_Target *dst_head;
189
190 /**
191 * Tail of destinations that are active.
192 */
193 struct GNUNET_DHTU_Target *dst_tail;
194
195 /**
196 * Map from hashes of sockaddrs to targets.
197 */
198 struct GNUNET_CONTAINER_MultiHashMap *dsts;
199
200 /**
201 * Task that scans for IP address changes.
202 */
203 struct GNUNET_SCHEDULER_Task *scan_task;
204
205 /**
206 * Task that reads incoming UDP packets.
207 */
208 struct GNUNET_SCHEDULER_Task *read_task;
209
210 /**
211 * Port we bind to.
212 */
213 char *port;
214
215 /**
216 * My UDP socket.
217 */
218 struct GNUNET_NETWORK_Handle *sock;
219
220 /**
221 * My identity.
222 */
223 struct GNUNET_PeerIdentity my_id;
224
225 /**
226 * How often have we scanned for IPs?
227 */
228 unsigned int scan_generation;
229
230 /**
231 * Port as a 16-bit value.
232 */
233 uint16_t port16;
234};
235
236
237/**
238 * Create a target to which we may send traffic.
239 *
240 * @param plugin our plugin
241 * @param pid presumed identity of the target
242 * @param addr target address
243 * @param addrlen number of bytes in @a addr
244 * @return new target object
245 */
246static struct GNUNET_DHTU_Target *
247create_target (struct Plugin *plugin,
248 const struct GNUNET_PeerIdentity *pid,
249 const struct sockaddr *addr,
250 socklen_t addrlen)
251{
252 struct GNUNET_DHTU_Target *dst;
253
254 if (MAX_DESTS <=
255 GNUNET_CONTAINER_multihashmap_size (plugin->dsts))
256 {
257 struct GNUNET_HashCode key;
258
259 dst = NULL;
260 for (struct GNUNET_DHTU_Target *pos = plugin->dst_head;
261 NULL != pos;
262 pos = pos->next)
263 {
264 /* >= here assures we remove oldest entries first */
265 if ( (NULL == dst) ||
266 (dst->ph_count >= pos->ph_count) )
267 dst = pos;
268 }
269 GNUNET_assert (NULL != dst);
270 plugin->env->disconnect_cb (dst->app_ctx);
271 GNUNET_CRYPTO_hash (&dst->addr,
272 dst->addrlen,
273 &key);
274 GNUNET_assert (GNUNET_YES ==
275 GNUNET_CONTAINER_multihashmap_remove (plugin->dsts,
276 &key,
277 dst));
278 GNUNET_CONTAINER_DLL_remove (plugin->dst_head,
279 plugin->dst_tail,
280 dst);
281 GNUNET_assert (NULL == dst->ph_head);
282 GNUNET_free (dst);
283 }
284 dst = GNUNET_new (struct GNUNET_DHTU_Target);
285 dst->addrlen = addrlen;
286 dst->pid = *pid;
287 memcpy (&dst->addr,
288 addr,
289 addrlen);
290 GNUNET_CONTAINER_DLL_insert (plugin->dst_head,
291 plugin->dst_tail,
292 dst);
293 plugin->env->connect_cb (plugin->env->cls,
294 dst,
295 &dst->pid,
296 &dst->app_ctx);
297 return dst;
298}
299
300
301/**
302 * Find target matching @a addr. If none exists,
303 * create one!
304 *
305 * @param plugin the plugin handle
306 * @param pid presumed identity of the target
307 * @param addr socket address to find
308 * @param addrlen number of bytes in @a addr
309 * @return matching target object
310 */
311static struct GNUNET_DHTU_Target *
312find_target (struct Plugin *plugin,
313 const struct GNUNET_PeerIdentity *pid,
314 const void *addr,
315 size_t addrlen)
316{
317 struct GNUNET_HashCode key;
318 struct GNUNET_DHTU_Target *dst;
319
320 GNUNET_CRYPTO_hash (addr,
321 addrlen,
322 &key);
323 dst = GNUNET_CONTAINER_multihashmap_get (plugin->dsts,
324 &key);
325 if (NULL == dst)
326 {
327 dst = create_target (plugin,
328 pid,
329 (const struct sockaddr *) addr,
330 addrlen);
331 GNUNET_assert (GNUNET_YES ==
332 GNUNET_CONTAINER_multihashmap_put (
333 plugin->dsts,
334 &key,
335 dst,
336 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
337 }
338 else
339 {
340 /* move to head of DLL */
341 GNUNET_CONTAINER_DLL_remove (plugin->dst_head,
342 plugin->dst_tail,
343 dst);
344 GNUNET_CONTAINER_DLL_insert (plugin->dst_head,
345 plugin->dst_tail,
346 dst);
347
348 }
349 return dst;
350}
351
352
353/**
354 * Request creation of a session with a peer at the given @a address.
355 *
356 * @param cls closure (internal context for the plugin)
357 * @param pid identity of the target peer
358 * @param address target address to connect to
359 */
360static void
361ip_try_connect (void *cls,
362 const struct GNUNET_PeerIdentity *pid,
363 const char *address)
364{
365 struct Plugin *plugin = cls;
366 char *colon;
367 const char *port;
368 char *addr;
369 struct addrinfo hints = {
370 .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV
371 };
372 struct addrinfo *result = NULL;
373
374 if (0 !=
375 strncmp (address,
376 "ip+",
377 strlen ("ip+")))
378 return;
379 address += strlen ("ip+");
380 if (0 !=
381 strncmp (address,
382 "udp://",
383 strlen ("udp://")))
384 return;
385 address += strlen ("udp://");
386 addr = GNUNET_strdup (address);
387 colon = strchr (addr, ':');
388 if (NULL == colon)
389 {
390 port = plugin->port;
391 }
392 else
393 {
394 *colon = '\0';
395 port = colon + 1;
396 }
397 if (0 !=
398 getaddrinfo (addr,
399 port,
400 &hints,
401 &result))
402 {
403 GNUNET_break (0);
404 GNUNET_free (addr);
405 return;
406 }
407 GNUNET_free (addr);
408 (void) find_target (plugin,
409 pid,
410 result->ai_addr,
411 result->ai_addrlen);
412 freeaddrinfo (result);
413}
414
415
416/**
417 * Request underlay to keep the connection to @a target alive if possible.
418 * Hold may be called multiple times to express a strong preference to
419 * keep a connection, say because a @a target is in multiple tables.
420 *
421 * @param cls closure
422 * @param target connection to keep alive
423 */
424static struct GNUNET_DHTU_PreferenceHandle *
425ip_hold (void *cls,
426 struct GNUNET_DHTU_Target *target)
427{
428 struct GNUNET_DHTU_PreferenceHandle *ph;
429
430 ph = GNUNET_new (struct GNUNET_DHTU_PreferenceHandle);
431 ph->target = target;
432 GNUNET_CONTAINER_DLL_insert (target->ph_head,
433 target->ph_tail,
434 ph);
435 target->ph_count++;
436 return ph;
437}
438
439
440/**
441 * Do no long request underlay to keep the connection alive.
442 *
443 * @param cls closure
444 * @param target connection to keep alive
445 */
446static void
447ip_drop (struct GNUNET_DHTU_PreferenceHandle *ph)
448{
449 struct GNUNET_DHTU_Target *target = ph->target;
450
451 GNUNET_CONTAINER_DLL_remove (target->ph_head,
452 target->ph_tail,
453 ph);
454 target->ph_count--;
455 GNUNET_free (ph);
456}
457
458
459/**
460 * Send message to some other participant over the network. Note that
461 * sending is not guaranteeing that the other peer actually received the
462 * message. For any given @a target, the DHT must wait for the @a
463 * finished_cb to be called before calling send() again.
464 *
465 * @param cls closure (internal context for the plugin)
466 * @param target receiver identification
467 * @param msg message
468 * @param msg_size number of bytes in @a msg
469 * @param finished_cb function called once transmission is done
470 * (not called if @a target disconnects, then only the
471 * disconnect_cb is called).
472 * @param finished_cb_cls closure for @a finished_cb
473 */
474static void
475ip_send (void *cls,
476 struct GNUNET_DHTU_Target *target,
477 const void *msg,
478 size_t msg_size,
479 GNUNET_SCHEDULER_TaskCallback finished_cb,
480 void *finished_cb_cls)
481{
482 struct Plugin *plugin = cls;
483 char buf[sizeof (plugin->my_id) + msg_size];
484
485 memcpy (buf,
486 &plugin->my_id,
487 sizeof (plugin->my_id));
488 memcpy (&buf[sizeof (plugin->my_id)],
489 msg,
490 msg_size);
491 GNUNET_NETWORK_socket_sendto (plugin->sock,
492 buf,
493 sizeof (buf),
494 (const struct sockaddr *) &target->addr,
495 target->addrlen);
496 finished_cb (finished_cb_cls);
497}
498
499
500/**
501 * Create a new source on which we may be receiving traffic.
502 *
503 * @param plugin our plugin
504 * @param addr our address
505 * @param addrlen number of bytes in @a addr
506 * @return new source object
507 */
508static struct GNUNET_DHTU_Source *
509create_source (struct Plugin *plugin,
510 const struct sockaddr *addr,
511 socklen_t addrlen)
512{
513 struct GNUNET_DHTU_Source *src;
514
515 src = GNUNET_new (struct GNUNET_DHTU_Source);
516 src->addrlen = addrlen;
517 memcpy (&src->addr,
518 addr,
519 addrlen);
520 src->scan_generation = plugin->scan_generation;
521 switch (addr->sa_family)
522 {
523 case AF_INET:
524 {
525 const struct sockaddr_in *s4 = (const struct sockaddr_in *) addr;
526 char buf[INET_ADDRSTRLEN];
527
528 GNUNET_assert (sizeof (struct sockaddr_in) == addrlen);
529 GNUNET_asprintf (&src->address,
530 "ip+udp://%s:%u",
531 inet_ntop (AF_INET,
532 &s4->sin_addr,
533 buf,
534 sizeof (buf)),
535 ntohs (s4->sin_port));
536 }
537 break;
538 case AF_INET6:
539 {
540 const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *) addr;
541 char buf[INET6_ADDRSTRLEN];
542
543 GNUNET_assert (sizeof (struct sockaddr_in6) == addrlen);
544 GNUNET_asprintf (&src->address,
545 "ip+udp://[%s]:%u",
546 inet_ntop (AF_INET6,
547 &s6->sin6_addr,
548 buf,
549 sizeof (buf)),
550 ntohs (s6->sin6_port));
551 }
552 break;
553 default:
554 GNUNET_break (0);
555 GNUNET_free (src);
556 return NULL;
557 }
558 GNUNET_CONTAINER_DLL_insert (plugin->src_head,
559 plugin->src_tail,
560 src);
561 plugin->env->address_add_cb (plugin->env->cls,
562 src->address,
563 src,
564 &src->app_ctx);
565 return src;
566}
567
568
569/**
570 * Compare two addresses excluding the ports for equality. Only compares IP
571 * address. Must only be called on AF_INET or AF_INET6 addresses.
572 *
573 * @param a1 address to compare
574 * @param a2 address to compare
575 * @param alen number of bytes in @a a1 and @a a2
576 * @return 0 if @a a1 == @a a2.
577 */
578static int
579addrcmp_np (const struct sockaddr *a1,
580 const struct sockaddr *a2,
581 size_t alen)
582{
583 GNUNET_assert (a1->sa_family == a2->sa_family);
584 switch (a1->sa_family)
585 {
586 case AF_INET:
587 GNUNET_assert (sizeof (struct sockaddr_in) == alen);
588 {
589 const struct sockaddr_in *s1 = (const struct sockaddr_in *) a1;
590 const struct sockaddr_in *s2 = (const struct sockaddr_in *) a2;
591
592 if (s1->sin_addr.s_addr != s2->sin_addr.s_addr)
593 return 1;
594 break;
595 }
596 case AF_INET6:
597 GNUNET_assert (sizeof (struct sockaddr_in6) == alen);
598 {
599 const struct sockaddr_in6 *s1 = (const struct sockaddr_in6 *) a1;
600 const struct sockaddr_in6 *s2 = (const struct sockaddr_in6 *) a2;
601
602 if (0 != GNUNET_memcmp (&s1->sin6_addr,
603 &s2->sin6_addr))
604 return 1;
605 break;
606 }
607 default:
608 GNUNET_assert (0);
609 }
610 return 0;
611}
612
613
614/**
615 * Compare two addresses for equality. Only
616 * compares IP address and port. Must only be
617 * called on AF_INET or AF_INET6 addresses.
618 *
619 * @param a1 address to compare
620 * @param a2 address to compare
621 * @param alen number of bytes in @a a1 and @a a2
622 * @return 0 if @a a1 == @a a2.
623 */
624static int
625addrcmp (const struct sockaddr *a1,
626 const struct sockaddr *a2,
627 size_t alen)
628{
629 GNUNET_assert (a1->sa_family == a2->sa_family);
630 switch (a1->sa_family)
631 {
632 case AF_INET:
633 GNUNET_assert (sizeof (struct sockaddr_in) == alen);
634 {
635 const struct sockaddr_in *s1 = (const struct sockaddr_in *) a1;
636 const struct sockaddr_in *s2 = (const struct sockaddr_in *) a2;
637
638 if (s1->sin_port != s2->sin_port)
639 return 1;
640 if (s1->sin_addr.s_addr != s2->sin_addr.s_addr)
641 return 1;
642 break;
643 }
644 case AF_INET6:
645 GNUNET_assert (sizeof (struct sockaddr_in6) == alen);
646 {
647 const struct sockaddr_in6 *s1 = (const struct sockaddr_in6 *) a1;
648 const struct sockaddr_in6 *s2 = (const struct sockaddr_in6 *) a2;
649
650 if (s1->sin6_port != s2->sin6_port)
651 return 1;
652 if (0 != GNUNET_memcmp (&s1->sin6_addr,
653 &s2->sin6_addr))
654 return 1;
655 break;
656 }
657 default:
658 GNUNET_assert (0);
659 }
660 return 0;
661}
662
663
664/**
665 * Callback function invoked for each interface found.
666 *
667 * @param cls closure
668 * @param name name of the interface (can be NULL for unknown)
669 * @param isDefault is this presumably the default interface
670 * @param addr address of this interface (can be NULL for unknown or unassigned)
671 * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
672 * @param netmask the network mask (can be NULL for unknown or unassigned)
673 * @param addrlen length of the address
674 * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort
675 */
676static enum GNUNET_GenericReturnValue
677process_ifcs (void *cls,
678 const char *name,
679 int isDefault,
680 const struct sockaddr *addr,
681 const struct sockaddr *broadcast_addr,
682 const struct sockaddr *netmask,
683 socklen_t addrlen)
684{
685 struct Plugin *plugin = cls;
686 struct GNUNET_DHTU_Source *src;
687
688 for (src = plugin->src_head;
689 NULL != src;
690 src = src->next)
691 {
692 if ( (addrlen == src->addrlen) &&
693 (0 == addrcmp_np (addr,
694 (const struct sockaddr *) &src->addr,
695 addrlen)) )
696 {
697 src->scan_generation = plugin->scan_generation;
698 return GNUNET_OK;
699 }
700 }
701 switch (addr->sa_family)
702 {
703 case AF_INET:
704 {
705 struct sockaddr_in v4;
706
707 GNUNET_assert (sizeof(v4) == addrlen);
708 memcpy (&v4,
709 addr,
710 addrlen);
711 v4.sin_port = htons (plugin->port16);
712 (void) create_source (plugin,
713 (const struct sockaddr *) &v4,
714 sizeof (v4));
715 break;
716 }
717 case AF_INET6:
718 {
719 struct sockaddr_in6 v6;
720
721 GNUNET_assert (sizeof(v6) == addrlen);
722 memcpy (&v6,
723 addr,
724 addrlen);
725 v6.sin6_port = htons (plugin->port16);
726 (void) create_source (plugin,
727 (const struct sockaddr *) &v6,
728 sizeof (v6));
729 break;
730 }
731 }
732 return GNUNET_OK;
733}
734
735
736/**
737 * Scan network interfaces for IP address changes.
738 *
739 * @param cls a `struct Plugin`
740 */
741static void
742scan (void *cls)
743{
744 struct Plugin *plugin = cls;
745 struct GNUNET_DHTU_Source *next;
746
747 plugin->scan_generation++;
748 GNUNET_OS_network_interfaces_list (&process_ifcs,
749 plugin);
750 for (struct GNUNET_DHTU_Source *src = plugin->src_head;
751 NULL != src;
752 src = next)
753 {
754 next = src->next;
755 if (src->scan_generation >= plugin->scan_generation)
756 continue;
757 GNUNET_CONTAINER_DLL_remove (plugin->src_head,
758 plugin->src_tail,
759 src);
760 plugin->env->address_del_cb (src->app_ctx);
761 GNUNET_free (src->address);
762 GNUNET_free (src);
763 }
764 plugin->scan_task = GNUNET_SCHEDULER_add_delayed (SCAN_FREQ,
765 &scan,
766 plugin);
767}
768
769
770/**
771 * Find our source matching @a addr. If none exists,
772 * create one!
773 *
774 * @param plugin the plugin handle
775 * @param addr socket address to find
776 * @param addrlen number of bytes in @a addr
777 * @return matching source object
778 */
779static struct GNUNET_DHTU_Source *
780find_source (struct Plugin *plugin,
781 const void *addr,
782 size_t addrlen)
783{
784 for (struct GNUNET_DHTU_Source *src = plugin->src_head;
785 NULL != src;
786 src = src->next)
787 {
788 if ( (addrlen == src->addrlen) &&
789 (0 == addrcmp (addr,
790 (const struct sockaddr *) &src->addr,
791 addrlen)) )
792 return src;
793 }
794
795 return create_source (plugin,
796 (const struct sockaddr *) addr,
797 addrlen);
798}
799
800
801/**
802 * UDP socket is ready to receive. Read.
803 *
804 * @param cls our `struct Plugin *`
805 */
806static void
807read_cb (void *cls)
808{
809 struct Plugin *plugin = cls;
810 ssize_t ret;
811 const struct GNUNET_PeerIdentity *pid;
812 char buf[65536] GNUNET_ALIGN;
813 struct sockaddr_storage sa;
814 struct iovec iov = {
815 .iov_base = buf,
816 .iov_len = sizeof (buf)
817 };
818 char ctl[128];
819 struct msghdr mh = {
820 .msg_name = &sa,
821 .msg_namelen = sizeof (sa),
822 .msg_iov = &iov,
823 .msg_iovlen = 1,
824 .msg_control = ctl,
825 .msg_controllen = sizeof (ctl)
826 };
827 struct GNUNET_DHTU_Target *dst = NULL;
828 struct GNUNET_DHTU_Source *src = NULL;
829
830 ret = recvmsg (GNUNET_NETWORK_get_fd (plugin->sock),
831 &mh,
832 MSG_DONTWAIT);
833 plugin->read_task = GNUNET_SCHEDULER_add_read_net (
834 GNUNET_TIME_UNIT_FOREVER_REL,
835 plugin->sock,
836 &read_cb,
837 plugin);
838 if (ret < 0)
839 return; /* read failure, hopefully EAGAIN */
840 if (ret < sizeof (*pid))
841 {
842 GNUNET_break_op (0);
843 return;
844 }
845 /* find IP where we received message */
846 for (struct cmsghdr *cmsg = CMSG_FIRSTHDR (&mh);
847 NULL != cmsg;
848 cmsg = CMSG_NXTHDR (&mh,
849 cmsg))
850 {
851 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
852 "Got CMSG level %u (%d/%d), type %u (%d/%d)\n",
853 cmsg->cmsg_level,
854 (cmsg->cmsg_level == IPPROTO_IP),
855 (cmsg->cmsg_level == IPPROTO_IPV6),
856 cmsg->cmsg_type,
857 (cmsg->cmsg_type == IP_PKTINFO),
858 (cmsg->cmsg_type == IPV6_PKTINFO));
859 if ( (cmsg->cmsg_level == IPPROTO_IP) &&
860 (cmsg->cmsg_type == IP_PKTINFO) )
861 {
862 if (CMSG_LEN (sizeof (struct in_pktinfo)) ==
863 cmsg->cmsg_len)
864 {
865 struct in_pktinfo pi;
866
867 memcpy (&pi,
868 CMSG_DATA (cmsg),
869 sizeof (pi));
870 {
871 struct sockaddr_in sa = {
872 .sin_family = AF_INET,
873 .sin_addr = pi.ipi_addr,
874 .sin_port = htons (plugin->port16)
875 };
876
877 src = find_source (plugin,
878 &sa,
879 sizeof (sa));
880 /* For sources we discovered by reading,
881 force the generation far into the future */
882 src->scan_generation = plugin->scan_generation + 60;
883 }
884 break;
885 }
886 else
887 GNUNET_break (0);
888 }
889 if ( (cmsg->cmsg_level == IPPROTO_IPV6) &&
890 (cmsg->cmsg_type == IPV6_PKTINFO) )
891 {
892 if (CMSG_LEN (sizeof (struct in6_pktinfo)) ==
893 cmsg->cmsg_len)
894 {
895 struct in6_pktinfo pi;
896
897 memcpy (&pi,
898 CMSG_DATA (cmsg),
899 sizeof (pi));
900 {
901 struct sockaddr_in6 sa = {
902 .sin6_family = AF_INET6,
903 .sin6_addr = pi.ipi6_addr,
904 .sin6_port = htons (plugin->port16),
905 .sin6_scope_id = pi.ipi6_ifindex
906 };
907
908 src = find_source (plugin,
909 &sa,
910 sizeof (sa));
911 /* For sources we discovered by reading,
912 force the generation far into the future */
913 src->scan_generation = plugin->scan_generation + 60;
914 break;
915 }
916 }
917 else
918 GNUNET_break (0);
919 }
920 }
921 if (NULL == src)
922 {
923 GNUNET_break (0);
924 return;
925 }
926 pid = (const struct GNUNET_PeerIdentity *) buf;
927 dst = find_target (plugin,
928 pid,
929 &sa,
930 mh.msg_namelen);
931 if (NULL == dst)
932 {
933 GNUNET_break (0);
934 return;
935 }
936 plugin->env->receive_cb (plugin->env->cls,
937 &dst->app_ctx,
938 &src->app_ctx,
939 &buf[sizeof(*pid)],
940 ret - sizeof (*pid));
941}
942
943
944/**
945 * Entry point for the plugin.
946 *
947 * @param cls closure (the `struct GNUNET_DHTU_PluginEnvironment`)
948 * @return the plugin's API
949 */
950struct GNUNET_DHTU_PluginFunctions *
951DHTU_ip_init (struct GNUNET_DHTU_PluginEnvironment *env)
952{
953 struct GNUNET_DHTU_PluginFunctions *api;
954 struct Plugin *plugin;
955 char *port;
956 unsigned int nport;
957 int sock;
958 int af;
959 unsigned long long nse;
960
961 if (GNUNET_OK !=
962 GNUNET_CONFIGURATION_get_value_number (env->cfg,
963 "DHTU-IP",
964 "NSE",
965 &nse))
966 {
967 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
968 "DHTU-IP",
969 "NSE");
970 return NULL;
971 }
972 if (GNUNET_OK !=
973 GNUNET_CONFIGURATION_get_value_string (env->cfg,
974 "DHTU-IP",
975 "UDP_PORT",
976 &port))
977 {
978 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
979 "DHTU-IP",
980 "UDP_PORT");
981 return NULL;
982 }
983 {
984 char dummy;
985
986 if ( (1 != sscanf (port,
987 "%u%c",
988 &nport,
989 &dummy)) ||
990 (nport > UINT16_MAX) )
991 {
992 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
993 "DHTU-IP",
994 "UDP_PORT",
995 "must be number below 65536");
996 GNUNET_free (port);
997 return NULL;
998 }
999 }
1000 plugin = GNUNET_new (struct Plugin);
1001 plugin->env = env;
1002 plugin->port = port;
1003 plugin->port16 = (uint16_t) nport;
1004 if (GNUNET_OK !=
1005 GNUNET_CRYPTO_get_peer_identity (env->cfg,
1006 &plugin->my_id))
1007 {
1008 GNUNET_free (plugin);
1009 return NULL;
1010 }
1011 af = AF_INET6;
1012 sock = socket (af,
1013 SOCK_DGRAM,
1014 IPPROTO_UDP);
1015 if (-1 == sock)
1016 {
1017 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1018 "socket");
1019 GNUNET_free (plugin->port);
1020 GNUNET_free (plugin);
1021 return NULL;
1022 }
1023 switch (af)
1024 {
1025 case AF_INET:
1026 {
1027 int on = 1;
1028
1029 if (0 !=
1030 setsockopt (sock,
1031 IPPROTO_IP,
1032 IP_PKTINFO,
1033 &on,
1034 sizeof (on)))
1035 {
1036 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1037 "setsockopt");
1038 }
1039 }
1040 {
1041 struct sockaddr_in sa = {
1042 .sin_family = AF_INET,
1043 .sin_port = htons ((uint16_t) nport)
1044 };
1045
1046 if (0 !=
1047 bind (sock,
1048 (const struct sockaddr *) &sa,
1049 sizeof (sa)))
1050 {
1051 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1052 "socket");
1053 GNUNET_break (0 ==
1054 close (sock));
1055 GNUNET_free (plugin->port);
1056 GNUNET_free (plugin);
1057 return NULL;
1058 }
1059 }
1060 break;
1061 case AF_INET6:
1062 {
1063 int on = 1;
1064
1065 if (0 !=
1066 setsockopt (sock,
1067 IPPROTO_IPV6,
1068 IPV6_RECVPKTINFO,
1069 &on,
1070 sizeof (on)))
1071 {
1072 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1073 "setsockopt");
1074 }
1075 }
1076 {
1077 struct sockaddr_in6 sa = {
1078 .sin6_family = AF_INET6,
1079 .sin6_port = htons ((uint16_t) nport)
1080 };
1081
1082 if (0 !=
1083 bind (sock,
1084 (const struct sockaddr *) &sa,
1085 sizeof (sa)))
1086 {
1087 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1088 "socket");
1089 GNUNET_break (0 ==
1090 close (sock));
1091 GNUNET_free (plugin->port);
1092 GNUNET_free (plugin);
1093 return NULL;
1094 }
1095 }
1096 break;
1097 }
1098 plugin->dsts = GNUNET_CONTAINER_multihashmap_create (128,
1099 GNUNET_NO);
1100 plugin->sock = GNUNET_NETWORK_socket_box_native (sock);
1101 plugin->read_task = GNUNET_SCHEDULER_add_read_net (
1102 GNUNET_TIME_UNIT_FOREVER_REL,
1103 plugin->sock,
1104 &read_cb,
1105 plugin);
1106 env->network_size_cb (env->cls,
1107 GNUNET_TIME_UNIT_ZERO_ABS,
1108 log (nse) / log (2),
1109 -1.0 /* stddev */);
1110 plugin->scan_task = GNUNET_SCHEDULER_add_now (&scan,
1111 plugin);
1112 api = GNUNET_new (struct GNUNET_DHTU_PluginFunctions);
1113 api->cls = plugin;
1114 api->try_connect = &ip_try_connect;
1115 api->hold = &ip_hold;
1116 api->drop = &ip_drop;
1117 api->send = &ip_send;
1118 return api;
1119}
1120
1121
1122/**
1123 * Exit point from the plugin.
1124 *
1125 * @param cls closure (our `struct Plugin`)
1126 * @return NULL
1127 */
1128void *
1129DHTU_ip_done (struct GNUNET_DHTU_PluginFunctions *api)
1130{
1131 struct Plugin *plugin = api->cls;
1132 struct GNUNET_DHTU_Source *src;
1133 struct GNUNET_DHTU_Target *dst;
1134
1135 while (NULL != (dst = plugin->dst_head))
1136 {
1137 plugin->env->disconnect_cb (dst->app_ctx);
1138 GNUNET_assert (NULL == dst->ph_head);
1139 GNUNET_CONTAINER_DLL_remove (plugin->dst_head,
1140 plugin->dst_tail,
1141 dst);
1142 GNUNET_free (dst);
1143 }
1144 while (NULL != (src = plugin->src_head))
1145 {
1146 plugin->env->address_del_cb (src->app_ctx);
1147 GNUNET_CONTAINER_DLL_remove (plugin->src_head,
1148 plugin->src_tail,
1149 src);
1150 GNUNET_free (src->address);
1151 GNUNET_free (src);
1152 }
1153 plugin->env->network_size_cb (plugin->env->cls,
1154 GNUNET_TIME_UNIT_FOREVER_ABS,
1155 0.0,
1156 0.0);
1157 GNUNET_CONTAINER_multihashmap_destroy (plugin->dsts);
1158 if (NULL != plugin->read_task)
1159 {
1160 GNUNET_SCHEDULER_cancel (plugin->read_task);
1161 plugin->read_task = NULL;
1162 }
1163 GNUNET_SCHEDULER_cancel (plugin->scan_task);
1164 GNUNET_break (GNUNET_OK ==
1165 GNUNET_NETWORK_socket_close (plugin->sock));
1166 GNUNET_free (plugin->port);
1167 GNUNET_free (plugin);
1168 GNUNET_free (api);
1169 return NULL;
1170}
diff --git a/src/service/dht/plugin_dhtu_ip.h b/src/service/dht/plugin_dhtu_ip.h
new file mode 100644
index 000000000..f240f5559
--- /dev/null
+++ b/src/service/dht/plugin_dhtu_ip.h
@@ -0,0 +1,44 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2024 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#ifndef PLUGIN_DHTU_IP_H
22#define PLUGIN_DHTU_IP_H
23#include "gnunet_dhtu_plugin.h"
24
25/**
26 * Exit point from the plugin.
27 *
28 * @param cls closure (our `struct Plugin`)
29 * @return NULL
30 */
31void *
32DHTU_ip_done (struct GNUNET_DHTU_PluginFunctions *p);
33
34
35/**
36 * Entry point for the plugin.
37 *
38 * @param cls closure (the `struct IP_DHTU_PluginEnvironment`)
39 * @return the plugin's API
40 */
41struct GNUNET_DHTU_PluginFunctions *
42DHTU_ip_init (struct GNUNET_DHTU_PluginEnvironment *env);
43
44#endif
diff --git a/src/service/dht/test_dht_2dtorus.conf b/src/service/dht/test_dht_2dtorus.conf
new file mode 100644
index 000000000..ed3d1301e
--- /dev/null
+++ b/src/service/dht/test_dht_2dtorus.conf
@@ -0,0 +1,37 @@
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/service/dht/test_dht_api.c b/src/service/dht/test_dht_api.c
new file mode 100644
index 000000000..0b51477fe
--- /dev/null
+++ b/src/service/dht/test_dht_api.c
@@ -0,0 +1,193 @@
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_testing_lib.h"
30#include "gnunet_dht_service.h"
31
32
33/**
34 * How long until we really give up on a particular testcase portion?
35 */
36#define TOTAL_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, \
37 60)
38
39static struct GNUNET_DHT_Handle *dht_handle;
40
41static struct GNUNET_DHT_GetHandle *get_handle;
42
43static struct GNUNET_DHT_PutHandle *put_handle;
44
45static int ok = 1;
46
47static struct GNUNET_SCHEDULER_Task *die_task;
48
49
50static void
51do_shutdown (void *cls)
52{
53 if (NULL != die_task)
54 {
55 GNUNET_SCHEDULER_cancel (die_task);
56 die_task = NULL;
57 }
58 if (NULL != put_handle)
59 {
60 GNUNET_DHT_put_cancel (put_handle);
61 put_handle = NULL;
62 }
63 if (NULL != get_handle)
64 {
65 GNUNET_DHT_get_stop (get_handle);
66 get_handle = NULL;
67 }
68 GNUNET_DHT_disconnect (dht_handle);
69 dht_handle = NULL;
70}
71
72
73static void
74end_badly (void *cls)
75{
76 die_task = NULL;
77 fprintf (stderr,
78 "%s",
79 "Ending on an unhappy note.\n");
80 GNUNET_SCHEDULER_shutdown ();
81 ok = 1;
82}
83
84
85static void
86test_get_iterator (void *cls,
87 struct GNUNET_TIME_Absolute exp,
88 const struct GNUNET_HashCode *key,
89 const struct GNUNET_PeerIdentity *trunc_peer,
90 const struct GNUNET_DHT_PathElement *get_path,
91 unsigned int get_path_length,
92 const struct GNUNET_DHT_PathElement *put_path,
93 unsigned int put_path_length,
94 enum GNUNET_BLOCK_Type type,
95 size_t size,
96 const void *data)
97{
98 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
99 "test_get_iterator called (we got a result), stopping get request!\n");
100 GNUNET_SCHEDULER_shutdown ();
101 ok = 0;
102}
103
104
105/**
106 * Signature of the main function of a task.
107 *
108 * @param cls closure
109 */
110static void
111test_get (void *cls)
112{
113 struct GNUNET_HashCode hash;
114
115 put_handle = NULL;
116 memset (&hash,
117 42,
118 sizeof(struct GNUNET_HashCode));
119 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
120 "Called test_get!\n");
121 GNUNET_assert (dht_handle != NULL);
122 get_handle = GNUNET_DHT_get_start (dht_handle,
123 GNUNET_BLOCK_TYPE_TEST,
124 &hash,
125 1,
126 GNUNET_DHT_RO_NONE,
127 NULL,
128 0,
129 &test_get_iterator,
130 NULL);
131
132 if (NULL == get_handle)
133 {
134 GNUNET_break (0);
135 ok = 1;
136 GNUNET_SCHEDULER_shutdown ();
137 return;
138 }
139}
140
141
142static void
143run (void *cls,
144 const struct GNUNET_CONFIGURATION_Handle *cfg,
145 struct GNUNET_TESTING_Peer *peer)
146{
147 struct GNUNET_HashCode hash;
148 char *data;
149 size_t data_size = 42;
150
151 GNUNET_assert (ok == 1);
152 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
153 NULL);
154 die_task = GNUNET_SCHEDULER_add_delayed (TOTAL_TIMEOUT,
155 &end_badly,
156 NULL);
157 memset (&hash,
158 42,
159 sizeof(struct GNUNET_HashCode));
160 data = GNUNET_malloc (data_size);
161 memset (data, 43, data_size);
162 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
163 "Called test_put!\n");
164 dht_handle = GNUNET_DHT_connect (cfg,
165 100);
166 GNUNET_assert (NULL != dht_handle);
167 put_handle = GNUNET_DHT_put (dht_handle,
168 &hash,
169 1,
170 GNUNET_DHT_RO_NONE,
171 GNUNET_BLOCK_TYPE_TEST,
172 data_size,
173 data,
174 GNUNET_TIME_relative_to_absolute (TOTAL_TIMEOUT),
175 &test_get,
176 NULL);
177 GNUNET_free (data);
178}
179
180
181int
182main (int argc,
183 char *argv[])
184{
185 if (0 != GNUNET_TESTING_peer_run ("test-dht-api",
186 "test_dht_api_data.conf",
187 &run, NULL))
188 return 1;
189 return ok;
190}
191
192
193/* end of test_dht_api.c */
diff --git a/src/service/dht/test_dht_api_data.conf b/src/service/dht/test_dht_api_data.conf
new file mode 100644
index 000000000..90a02d452
--- /dev/null
+++ b/src/service/dht/test_dht_api_data.conf
@@ -0,0 +1,44 @@
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/service/dht/test_dht_api_peer1.conf b/src/service/dht/test_dht_api_peer1.conf
new file mode 100644
index 000000000..b802b2fed
--- /dev/null
+++ b/src/service/dht/test_dht_api_peer1.conf
@@ -0,0 +1,41 @@
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/service/dht/test_dht_line.conf b/src/service/dht/test_dht_line.conf
new file mode 100644
index 000000000..6e4da8021
--- /dev/null
+++ b/src/service/dht/test_dht_line.conf
@@ -0,0 +1,38 @@
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/service/dht/test_dht_multipeer.conf b/src/service/dht/test_dht_multipeer.conf
new file mode 100644
index 000000000..29bd0d9bd
--- /dev/null
+++ b/src/service/dht/test_dht_multipeer.conf
@@ -0,0 +1,40 @@
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/service/dht/test_dht_multipeer_topology.dat b/src/service/dht/test_dht_multipeer_topology.dat
new file mode 100644
index 000000000..2268c85cb
--- /dev/null
+++ b/src/service/dht/test_dht_multipeer_topology.dat
@@ -0,0 +1,11 @@
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/service/dht/test_dht_tools.conf b/src/service/dht/test_dht_tools.conf
new file mode 100644
index 000000000..05f7cc0e6
--- /dev/null
+++ b/src/service/dht/test_dht_tools.conf
@@ -0,0 +1,157 @@
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/service/dht/test_dht_tools.py.in b/src/service/dht/test_dht_tools.py.in
new file mode 100644
index 000000000..84e297081
--- /dev/null
+++ b/src/service/dht/test_dht_tools.py.in
@@ -0,0 +1,149 @@
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/service/dht/test_dht_tools.sh b/src/service/dht/test_dht_tools.sh
new file mode 100755
index 000000000..462866f87
--- /dev/null
+++ b/src/service/dht/test_dht_tools.sh
@@ -0,0 +1,65 @@
1#!/bin/sh
2# This file is in the public domain.
3
4GNUNET_TMP="$(gnunet-config -f -s PATHS -o GNUNET_TMP)"
5out=`mktemp $GNUNET_TMP/test-gnunet-dht-logXXXXXXXX`
6tempcfg=`mktemp $GNUNET_TMP/test-dht-tools.XXXXXXXX`
7checkout="check.out"
8armexe="gnunet-arm -c $tempcfg "
9putexe="gnunet-dht-put -c $tempcfg "
10getexe="gnunet-dht-get -c $tempcfg "
11peerinfo="gnunet-peerinfo -c $tempcfg -sq"
12stop_arm()
13{
14 if ! $armexe $DEBUG -e -d > $out ; then
15 echo "FAIL: error running $armexe"
16 echo "Command output was:"
17 cat $out
18 rm -f $out $tempcfg
19 exit 1
20 fi
21 rm -f $out $tempcfg
22}
23
24cp test_dht_tools.conf $tempcfg
25
26echo -n "TEST: Starting ARM..."
27if ! $armexe $DEBUG -s > $out ; then
28 echo "FAIL: error running $armexe"
29 echo "Command output was:"
30 cat $out
31 stop_arm
32 exit 1
33fi
34echo "PASS"
35
36echo -n "TEST: Testing put..."
37if ! $putexe -k testkey -d testdata -t 8 > $out ; then
38 echo "FAIL: error running $putexe"
39 echo "Command output was:"
40 cat $out
41 stop_arm
42 exit 1
43fi
44echo "PASS"
45
46echo -n "TEST: Testing get..."
47echo "Result 0, type 8:" > $checkout
48echo "testdata" >> $checkout
49
50if ! $getexe -k testkey -T 100ms -t 8 > $out ; then
51 echo "FAIL: error running $putexe"
52 echo "Command output was:"
53 cat $out
54 stop_arm
55 exit 1
56fi
57
58if ! diff --strip-trailing-cr -q $out $checkout ; then
59 echo "FAIL: $out and $checkout differ:"
60 diff --strip-trailing-cr $out $checkout
61 stop_arm
62 exit 1
63fi
64echo "PASS"
65stop_arm
diff --git a/src/service/dht/test_dhtu_ip.c b/src/service/dht/test_dhtu_ip.c
new file mode 100644
index 000000000..030b17b5f
--- /dev/null
+++ b/src/service/dht/test_dhtu_ip.c
@@ -0,0 +1,45 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file dhtu/test_dhtu_ip.c
23 * @brief Test case for the DHTU implementation for IP
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_testing_netjail_lib.h"
28#include "gnunet_util_lib.h"
29
30#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120)
31
32#define CONFIG_FILE "test_dhtu_ip.conf"
33
34
35int
36main (int argc,
37 char *const *argv)
38{
39 struct GNUNET_TESTING_Command commands[] = {
40 GNUNET_TESTING_cmd_end ()
41 };
42
43 return GNUNET_TESTING_main (commands,
44 TIMEOUT);
45}
diff --git a/src/service/dht/testing_dhtu_cmd_send.c b/src/service/dht/testing_dhtu_cmd_send.c
new file mode 100644
index 000000000..88449659c
--- /dev/null
+++ b/src/service/dht/testing_dhtu_cmd_send.c
@@ -0,0 +1,115 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file testing/testing_dhtu_cmd_send.c
23 * @brief use DHTU to send a message
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_testing_ng_lib.h"
28#include "gnunet_testing_netjail_lib.h"
29
30
31/**
32 * State for the 'send' command.
33 */
34struct SendState
35{
36
37 /**
38 * Mandatory context for async commands.
39 */
40 struct GNUNET_TESTING_AsyncContext ac;
41
42};
43
44
45/**
46 *
47 *
48 * @param cls a `struct SendState`
49 */
50static void
51send_cleanup (void *cls)
52{
53 struct SendState *ss = cls;
54
55 GNUNET_free (ss);
56}
57
58
59/**
60 * Return trains of the ``send`` command.
61 *
62 * @param cls closure.
63 * @param[out] ret result
64 * @param trait name of the trait.
65 * @param index index number of the object to offer.
66 * @return #GNUNET_OK on success.
67 * #GNUNET_NO if no trait was found
68 */
69static enum GNUNET_GenericReturnValue
70send_traits (void *cls,
71 const void **ret,
72 const char *trait,
73 unsigned int index)
74{
75 return GNUNET_NO;
76}
77
78
79/**
80 * Run the 'send' command.
81 *
82 * @param cls closure.
83 * @param is interpreter state.
84 */
85static void
86send_run (void *cls,
87 struct GNUNET_TESTING_Interpreter *is)
88{
89 struct SendState *ss = cls;
90
91 GNUNET_TESTING_async_finish (&ss->ac);
92}
93
94
95struct GNUNET_TESTING_Command
96GNUNET_TESTING_DHTU_cmd_send (const char *label)
97{
98 struct SendState *ss;
99
100 ss = GNUNET_new (struct SendState);
101
102 {
103 struct GNUNET_TESTING_Command cmd = {
104 .cls = ss,
105 .run = &send_run,
106 .ac = &ss->ac,
107 .cleanup = &send_cleanup,
108 .traits = &send_traits
109 };
110
111 GNUNET_TESTING_set_label (&cmd.label,
112 label);
113 return cmd;
114 }
115}