aboutsummaryrefslogtreecommitdiff
path: root/src/dht
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2011-09-27 20:47:21 +0000
committerChristian Grothoff <christian@grothoff.org>2011-09-27 20:47:21 +0000
commit18ad006e1f9b322d4760fb1473e8182f0d6ae7f0 (patch)
treeb27c918b33f8ce5fa5651299c9f93ff6e963c3ed /src/dht
parent89bc96f3fc0509f872c8372caac099d81ed36622 (diff)
downloadgnunet-18ad006e1f9b322d4760fb1473e8182f0d6ae7f0.tar.gz
gnunet-18ad006e1f9b322d4760fb1473e8182f0d6ae7f0.zip
DCE
Diffstat (limited to 'src/dht')
-rw-r--r--src/dht/Makefile.am70
-rw-r--r--src/dht/dhtlog.c93
-rw-r--r--src/dht/dhtlog.h394
-rw-r--r--src/dht/gnunet-dht-get.c3
-rw-r--r--src/dht/gnunet-service-dht.c4773
-rw-r--r--src/dht/plugin_dhtlog_dummy.c347
-rw-r--r--src/dht/plugin_dhtlog_mysql.c1612
-rw-r--r--src/dht/plugin_dhtlog_mysql_dump.c964
8 files changed, 2 insertions, 8254 deletions
diff --git a/src/dht/Makefile.am b/src/dht/Makefile.am
index 9f04943e5..147248234 100644
--- a/src/dht/Makefile.am
+++ b/src/dht/Makefile.am
@@ -5,10 +5,6 @@ endif
5 5
6plugindir = $(libdir)/gnunet 6plugindir = $(libdir)/gnunet
7 7
8if HAVE_MYSQL
9 MYSQL_PLUGIN = libgnunet_plugin_dhtlog_mysql.la
10endif
11
12if HAVE_ZLIB 8if HAVE_ZLIB
13 ZLIB_LNK = -lz 9 ZLIB_LNK = -lz
14endif 10endif
@@ -20,56 +16,8 @@ endif
20 16
21lib_LTLIBRARIES = \ 17lib_LTLIBRARIES = \
22 libgnunetdht.la \ 18 libgnunetdht.la \
23 libgnunetdhtnew.la \ 19 libgnunetdhtnew.la
24 libgnunetdhtlog.la
25
26plugin_LTLIBRARIES = \
27 $(MYSQL_PLUGIN) \
28 libgnunet_plugin_dhtlog_dummy.la \
29 libgnunet_plugin_dhtlog_mysql_dump.la \
30 libgnunet_plugin_dhtlog_mysql_dump_load.la
31
32
33libgnunet_plugin_dhtlog_mysql_la_SOURCES = \
34 plugin_dhtlog_mysql.c
35libgnunet_plugin_dhtlog_mysql_la_LIBADD = \
36 $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) -lz -lsqlite3
37libgnunet_plugin_dhtlog_mysql_la_LDFLAGS = \
38 $(GN_PLUGIN_LDFLAGS) $(MYSQL_LDFLAGS) -lmysqlclient
39libgnunet_plugin_dhtlog_mysql_la_CPFLAGS = \
40 $(MYSQL_CPPFLAGS)
41
42libgnunet_plugin_dhtlog_dummy_la_SOURCES = \
43 plugin_dhtlog_dummy.c
44libgnunet_plugin_dhtlog_dummy_la_LIBADD = \
45 $(top_builddir)/src/util/libgnunetutil.la \
46 $(XLIB)
47libgnunet_plugin_dhtlog_dummy_la_LDFLAGS = \
48 $(GN_PLUGIN_LDFLAGS)
49
50libgnunet_plugin_dhtlog_mysql_dump_la_SOURCES = \
51 plugin_dhtlog_mysql_dump.c
52libgnunet_plugin_dhtlog_mysql_dump_la_LIBADD = \
53 $(top_builddir)/src/util/libgnunetutil.la \
54 $(XLIB)
55libgnunet_plugin_dhtlog_mysql_dump_la_LDFLAGS = \
56 $(GN_PLUGIN_LDFLAGS)
57 20
58libgnunet_plugin_dhtlog_mysql_dump_load_la_SOURCES = \
59 plugin_dhtlog_mysql_dump_load.c
60libgnunet_plugin_dhtlog_mysql_dump_load_la_LIBADD = \
61 $(top_builddir)/src/util/libgnunetutil.la \
62 $(XLIB)
63libgnunet_plugin_dhtlog_mysql_dump_load_la_LDFLAGS = \
64 $(GN_PLUGIN_LDFLAGS)
65
66libgnunetdhtlog_la_SOURCES = \
67 dhtlog.c dhtlog.h
68libgnunetdhtlog_la_LIBADD = \
69 $(top_builddir)/src/util/libgnunetutil.la
70libgnunetdhtlog_la_LDFLAGS = \
71 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
72 -version-info 0:0:0
73 21
74libgnunetdht_la_SOURCES = \ 22libgnunetdht_la_SOURCES = \
75 dht_api.c dht.h \ 23 dht_api.c dht.h \
@@ -92,7 +40,6 @@ libgnunetdhtnew_la_LDFLAGS = \
92 -version-info 0:0:0 40 -version-info 0:0:0
93 41
94bin_PROGRAMS = \ 42bin_PROGRAMS = \
95 gnunet-service-dht \
96 gnunet-service-dht-new \ 43 gnunet-service-dht-new \
97 gnunet-dht-get \ 44 gnunet-dht-get \
98 gnunet-dht-put 45 gnunet-dht-put
@@ -102,21 +49,6 @@ noinst_PROGRAMS = \
102 gnunet-dht-driver 49 gnunet-dht-driver
103endif 50endif
104 51
105gnunet_service_dht_SOURCES = \
106 gnunet-service-dht.c
107gnunet_service_dht_LDADD = \
108 $(top_builddir)/src/statistics/libgnunetstatistics.la \
109 $(top_builddir)/src/core/libgnunetcore.la \
110 $(top_builddir)/src/nse/libgnunetnse.la \
111 $(top_builddir)/src/transport/libgnunettransport.la \
112 $(top_builddir)/src/hello/libgnunethello.la \
113 $(top_builddir)/src/block/libgnunetblock.la \
114 $(top_builddir)/src/datacache/libgnunetdatacache.la \
115 $(top_builddir)/src/util/libgnunetutil.la \
116 $(top_builddir)/src/dht/libgnunetdhtlog.la -lm
117gnunet_service_dht_DEPENDENCIES = \
118 libgnunetdhtlog.la
119
120gnunet_service_dht_new_SOURCES = \ 52gnunet_service_dht_new_SOURCES = \
121 gnunet-service-dht-new.c gnunet-service-dht.h \ 53 gnunet-service-dht-new.c gnunet-service-dht.h \
122 gnunet-service-dht_clients.c gnunet-service-dht_clients.h \ 54 gnunet-service-dht_clients.c gnunet-service-dht_clients.h \
diff --git a/src/dht/dhtlog.c b/src/dht/dhtlog.c
deleted file mode 100644
index d56ba6469..000000000
--- a/src/dht/dhtlog.c
+++ /dev/null
@@ -1,93 +0,0 @@
1/*
2 This file is part of GNUnet.
3 (C) 2006 - 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/dht/dhtlog.c
23 * @brief Plugin loaded to load logging
24 * to record DHT operations
25 * @author Nathan Evans
26 *
27 * Database: Loaded by plugin MySQL
28 */
29
30#include "platform.h"
31#include "gnunet_util_lib.h"
32#include "dhtlog.h"
33
34static char *libname;
35
36/*
37 * Provides the dhtlog api
38 *
39 * @param c the configuration to use to connect to a server
40 *
41 * @return the handle to the server, or NULL on error
42 */
43struct GNUNET_DHTLOG_Handle *
44GNUNET_DHTLOG_connect (const struct GNUNET_CONFIGURATION_Handle *c)
45{
46 struct GNUNET_DHTLOG_Plugin *plugin;
47 struct GNUNET_DHTLOG_Handle *api;
48 char *plugin_name;
49
50 plugin = GNUNET_malloc (sizeof (struct GNUNET_DHTLOG_Plugin));
51 plugin->cfg = c;
52 if (GNUNET_OK ==
53 GNUNET_CONFIGURATION_get_value_string (c, "DHTLOG", "PLUGIN",
54 &plugin_name))
55 {
56 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Loading `%s' dhtlog plugin\n"),
57 plugin_name);
58 GNUNET_asprintf (&libname, "libgnunet_plugin_dhtlog_%s", plugin_name);
59 GNUNET_PLUGIN_load (libname, plugin);
60 }
61
62 if (plugin->dhtlog_api == NULL)
63 {
64 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
65 _("Failed to load dhtlog plugin for `%s'\n"), plugin_name);
66 GNUNET_free (plugin_name);
67 GNUNET_free (plugin);
68 return NULL;
69 }
70
71 api = plugin->dhtlog_api;
72 GNUNET_free (plugin_name);
73 GNUNET_free (plugin);
74 return api;
75}
76
77/**
78 * Shutdown the module.
79 */
80void
81GNUNET_DHTLOG_disconnect (struct GNUNET_DHTLOG_Handle *api)
82{
83#if DEBUG_DHTLOG
84 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MySQL DHT Logger: database shutdown\n");
85#endif
86 if (api != NULL)
87 {
88 GNUNET_PLUGIN_unload (libname, api);
89 }
90 GNUNET_free_non_null (libname);
91}
92
93/* end of dhtlog.c */
diff --git a/src/dht/dhtlog.h b/src/dht/dhtlog.h
deleted file mode 100644
index cdab36305..000000000
--- a/src/dht/dhtlog.h
+++ /dev/null
@@ -1,394 +0,0 @@
1/*
2 This file is part of GNUnet
3 (C) 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/dht/dhtlog.h
23 *
24 * @brief dhtlog is a service that implements logging of dht operations
25 * for testing
26 * @author Nathan Evans
27 */
28
29#ifndef GNUNET_DHTLOG_SERVICE_H
30#define GNUNET_DHTLOG_SERVICE_H
31
32#include "gnunet_util_lib.h"
33
34#ifdef __cplusplus
35extern "C"
36{
37#if 0 /* keep Emacsens' auto-indent happy */
38}
39#endif
40#endif
41
42typedef enum
43{
44 /**
45 * Type for a DHT GET message
46 */
47 DHTLOG_GET = 1,
48
49 /**
50 * Type for a DHT PUT message
51 */
52 DHTLOG_PUT = 2,
53
54 /**
55 * Type for a DHT FIND PEER message
56 */
57 DHTLOG_FIND_PEER = 3,
58
59 /**
60 * Type for a DHT RESULT message
61 */
62 DHTLOG_RESULT = 4,
63
64 /**
65 * Generic DHT ROUTE message
66 */
67 DHTLOG_ROUTE = 5,
68
69} DHTLOG_MESSAGE_TYPES;
70
71struct GNUNET_DHTLOG_TrialInfo
72{
73 /**
74 * Outside of database identifier for the trial.
75 */
76 unsigned int other_identifier;
77
78 /** Number of nodes in the trial */
79 unsigned int num_nodes;
80
81 /** Type of initial topology */
82 unsigned int topology;
83
84 /** Topology to blacklist peers to */
85 unsigned int blacklist_topology;
86
87 /** Initially connect peers in this topology */
88 unsigned int connect_topology;
89
90 /** Option to modify connect topology */
91 unsigned int connect_topology_option;
92
93 /** Modifier for the connect option */
94 float connect_topology_option_modifier;
95
96 /** Percentage parameter used for certain topologies */
97 float topology_percentage;
98
99 /** Probability parameter used for certain topologies */
100 float topology_probability;
101
102 /** Number of puts in the trial */
103 unsigned int puts;
104
105 /** Number of gets in the trial */
106 unsigned int gets;
107
108 /** Concurrent puts/gets in the trial (max allowed) */
109 unsigned int concurrent;
110
111 /** How long between initial connection and issuing puts/gets */
112 unsigned int settle_time;
113
114 /** How many times to do put/get loop */
115 unsigned int num_rounds;
116
117 /** Number of malicious getters */
118 unsigned int malicious_getters;
119
120 /** Number of malicious putters */
121 unsigned int malicious_putters;
122
123 /** Number of malicious droppers */
124 unsigned int malicious_droppers;
125
126 /** Frequency of malicious get requests */
127 unsigned int malicious_get_frequency;
128
129 /** Frequency of malicious put requests */
130 unsigned int malicious_put_frequency;
131
132 /** Stop forwarding put/find_peer requests when peer is closer than others */
133 unsigned int stop_closest;
134
135 /** Stop forwarding get requests when data found */
136 unsigned int stop_found;
137
138 /**
139 * Routing behaves as it would in Kademlia (modified to work recursively,
140 * and with our other GNUnet constraints).
141 */
142 unsigned int strict_kademlia;
143
144 /** Number of gets that were reported successful */
145 unsigned int gets_succeeded;
146
147 /** Message for this trial */
148 char *message;
149};
150
151struct GNUNET_DHTLOG_Handle
152{
153
154 /*
155 * Inserts the specified query into the dhttests.queries table
156 *
157 * @param sqlqueruid inserted query uid
158 * @param queryid dht query id
159 * @param type type of the query
160 * @param hops number of hops query traveled
161 * @param succeeded whether or not query was successful
162 * @param node the node the query hit
163 * @param key the key of the query
164 *
165 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
166 */
167 int (*insert_query) (unsigned long long *sqlqueryuid,
168 unsigned long long queryid, DHTLOG_MESSAGE_TYPES type,
169 unsigned int hops, int succeeded,
170 const struct GNUNET_PeerIdentity * node,
171 const GNUNET_HashCode * key);
172
173 /*
174 * Inserts the specified trial into the dhttests.trials table
175 *
176 * @param trial_info general information about this trial
177 *
178 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
179 */
180 int (*insert_trial) (struct GNUNET_DHTLOG_TrialInfo * trial_info);
181
182 /*
183 * Inserts the specified stats into the dhttests.node_statistics table
184 *
185 * @param peer the peer inserting the statistic
186 * @param route_requests route requests seen
187 * @param route_forwards route requests forwarded
188 * @param result_requests route result requests seen
189 * @param client_requests client requests initiated
190 * @param result_forwards route results forwarded
191 * @param gets get requests handled
192 * @param puts put requests handle
193 * @param data_inserts data inserted at this node
194 * @param find_peer_requests find peer requests seen
195 * @param find_peers_started find peer requests initiated at this node
196 * @param gets_started get requests initiated at this node
197 * @param puts_started put requests initiated at this node
198 * @param find_peer_responses_received find peer responses received locally
199 * @param get_responses_received get responses received locally
200 * @param find_peer_responses_sent find peer responses sent from this node
201 * @param get_responses_sent get responses sent from this node
202 *
203 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
204 */
205 int (*insert_stat) (const struct GNUNET_PeerIdentity * peer,
206 unsigned int route_requests, unsigned int route_forwards,
207 unsigned int result_requests,
208 unsigned int client_requests,
209 unsigned int result_forwards, unsigned int gets,
210 unsigned int puts, unsigned int data_inserts,
211 unsigned int find_peer_requests,
212 unsigned int find_peers_started,
213 unsigned int gets_started, unsigned int puts_started,
214 unsigned int find_peer_responses_received,
215 unsigned int get_responses_received,
216 unsigned int find_peer_responses_sent,
217 unsigned int get_responses_sent);
218
219 /*
220 * Update dhttests.trials table with current server time as end time
221 *
222 * @param gets_succeeded how many gets did the trial report successful
223 *
224 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
225 */
226 int (*update_trial) (unsigned int gets_succeeded);
227
228 /*
229 * Update dhttests.nodes table setting the identified
230 * node as a malicious dropper.
231 *
232 * @param peer the peer that was set to be malicious
233 *
234 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
235 */
236 int (*set_malicious) (struct GNUNET_PeerIdentity * peer);
237
238 /*
239 * Records the current topology (number of connections, time, trial)
240 *
241 * @param num_connections how many connections are in the topology
242 *
243 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
244 */
245 int (*insert_topology) (int num_connections);
246
247 /*
248 * Records a connection between two peers in the current topology
249 *
250 * @param first one side of the connection
251 * @param second other side of the connection
252 *
253 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
254 */
255 int (*insert_extended_topology) (const struct GNUNET_PeerIdentity * first,
256 const struct GNUNET_PeerIdentity * second);
257
258 /*
259 * Inserts the specified stats into the dhttests.generic_stats table
260 *
261 * @param peer the peer inserting the statistic
262 * @param name the name of the statistic
263 * @param section the section of the statistic
264 * @param value the value of the statistic
265 *
266 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
267 */
268 int (*add_generic_stat) (const struct GNUNET_PeerIdentity * peer,
269 const char *name, const char *section,
270 uint64_t value);
271
272 /*
273 * Inserts the specified round into the dhttests.rounds table
274 *
275 * @param round_type the type of round that is being started
276 * @param round_count counter for the round (if applicable)
277 *
278 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
279 */
280 int (*insert_round) (unsigned int round_type, unsigned int round_count);
281
282 /*
283 * Inserts the specified round results into the
284 * dhttests.processed_round_details table
285 *
286 * @param round_type the type of round that is being started
287 * @param round_count counter for the round (if applicable)
288 * @param num_messages the total number of messages initiated
289 * @param num_messages_succeeded the number of messages that succeeded
290 *
291 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
292 */
293 int (*insert_round_details) (unsigned int round_type,
294 unsigned int round_count,
295 unsigned int num_messages,
296 unsigned int num_messages_succeeded);
297
298 /*
299 * Update dhttests.trials table with total connections information
300 *
301 * @param totalConnections the number of connections
302 *
303 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
304 */
305 int (*update_connections) (unsigned int totalConnections);
306
307 /*
308 * Update dhttests.trials table with total connections information
309 *
310 * @param connections the number of connections
311 *
312 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
313 */
314 int (*update_topology) (unsigned int connections);
315
316 /*
317 * Inserts the specified route information into the dhttests.routes table
318 *
319 * @param sqlqueruid inserted query uid
320 * @param queryid dht query id
321 * @param type type of the query
322 * @param hops number of hops query traveled
323 * @param succeeded whether or not query was successful
324 * @param node the node the query hit
325 * @param key the key of the query
326 * @param from_node the node that sent the message to node
327 * @param to_node next node to forward message to
328 *
329 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
330 */
331 int (*insert_route) (unsigned long long *sqlqueryuid,
332 unsigned long long queryid, unsigned int type,
333 unsigned int hops, int succeeded,
334 const struct GNUNET_PeerIdentity * node,
335 const GNUNET_HashCode * key,
336 const struct GNUNET_PeerIdentity * from_node,
337 const struct GNUNET_PeerIdentity * to_node);
338
339 /*
340 * Inserts the specified node into the dhttests.nodes table
341 *
342 * @param nodeuid the inserted node uid
343 * @param node the node to insert
344 *
345 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
346 */
347 int (*insert_node) (unsigned long long *nodeuid,
348 struct GNUNET_PeerIdentity * node);
349
350 /*
351 * Inserts the specified dhtkey into the dhttests.dhtkeys table,
352 * stores return value of dhttests.dhtkeys.dhtkeyuid into dhtkeyuid
353 *
354 * @param dhtkeyuid return value
355 * @param dhtkey hashcode of key to insert
356 *
357 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
358 */
359 int (*insert_dhtkey) (unsigned long long *dhtkeyuid,
360 const GNUNET_HashCode * dhtkey);
361
362};
363
364struct GNUNET_DHTLOG_Plugin
365{
366 const struct GNUNET_CONFIGURATION_Handle *cfg;
367
368 struct GNUNET_DHTLOG_Handle *dhtlog_api;
369};
370
371/**
372 * Connect to mysql server using the DHT log plugin.
373 *
374 * @param c a configuration to use
375 */
376struct GNUNET_DHTLOG_Handle *
377GNUNET_DHTLOG_connect (const struct GNUNET_CONFIGURATION_Handle *c);
378
379/**
380 * Shutdown the module.
381 */
382void
383GNUNET_DHTLOG_disconnect (struct GNUNET_DHTLOG_Handle *api);
384
385
386#if 0 /* keep Emacsens' auto-indent happy */
387{
388#endif
389#ifdef __cplusplus
390}
391#endif
392
393/* end of dhtlog.h */
394#endif
diff --git a/src/dht/gnunet-dht-get.c b/src/dht/gnunet-dht-get.c
index 1e75a742a..0d291e5e7 100644
--- a/src/dht/gnunet-dht-get.c
+++ b/src/dht/gnunet-dht-get.c
@@ -61,7 +61,6 @@ static int verbose;
61 */ 61 */
62static struct GNUNET_DHT_Handle *dht_handle; 62static struct GNUNET_DHT_Handle *dht_handle;
63 63
64
65/** 64/**
66 * Global handle of the configuration 65 * Global handle of the configuration
67 */ 66 */
@@ -121,7 +120,7 @@ cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
121 * @param size number of bytes in data 120 * @param size number of bytes in data
122 * @param data pointer to the result data 121 * @param data pointer to the result data
123 */ 122 */
124void 123static void
125get_result_iterator (void *cls, struct GNUNET_TIME_Absolute exp, 124get_result_iterator (void *cls, struct GNUNET_TIME_Absolute exp,
126 const GNUNET_HashCode * key, 125 const GNUNET_HashCode * key,
127 const struct GNUNET_PeerIdentity *get_path, 126 const struct GNUNET_PeerIdentity *get_path,
diff --git a/src/dht/gnunet-service-dht.c b/src/dht/gnunet-service-dht.c
deleted file mode 100644
index 2650baeb2..000000000
--- a/src/dht/gnunet-service-dht.c
+++ /dev/null
@@ -1,4773 +0,0 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
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 * TODO:
28 * - decide which 'benchmark'/test functions to keep (malicious code, kademlia, etc.)
29 * - decide on 'stop_on_closest', 'stop_on_found', 'do_find_peer', 'paper_forwarding'
30 * - use OPTION_MULTIPLE instead of linked list for the forward_list.hashmap
31 * - use different 'struct DHT_MessageContext' for the different types of
32 * messages (currently rather confusing, especially with things like
33 * peer bloom filters occuring when processing replies).
34 * - why do we have request UIDs again?
35 */
36
37#include "platform.h"
38#include "gnunet_block_lib.h"
39#include "gnunet_constants.h"
40#include "gnunet_protocols.h"
41#include "gnunet_nse_service.h"
42#include "gnunet_core_service.h"
43#include "gnunet_util_lib.h"
44#include "gnunet_datacache_lib.h"
45#include "gnunet_transport_service.h"
46#include "gnunet_hello_lib.h"
47#include "gnunet_dht_service.h"
48#include "gnunet_statistics_service.h"
49#include "dhtlog.h"
50#include "dht.h"
51#include <fenv.h>
52
53
54/**
55 * How many buckets will we allow total.
56 */
57#define MAX_BUCKETS sizeof (GNUNET_HashCode) * 8
58
59/**
60 * Should the DHT issue FIND_PEER requests to get better routing tables?
61 */
62#define DEFAULT_DO_FIND_PEER GNUNET_YES
63
64/**
65 * Defines whether find peer requests send their HELLO's outgoing,
66 * or expect replies to contain hellos.
67 */
68#define FIND_PEER_WITH_HELLO GNUNET_YES
69
70/**
71 * What is the maximum number of peers in a given bucket.
72 */
73#define DEFAULT_BUCKET_SIZE 4
74
75#define DEFAULT_CORE_QUEUE_SIZE 32
76
77/**
78 * Minimum number of peers we need for "good" routing,
79 * any less than this and we will allow messages to
80 * travel much further through the network!
81 */
82#define MINIMUM_PEER_THRESHOLD 20
83
84/**
85 * Number of requests we track at most (for routing replies).
86 */
87#define DHT_MAX_RECENT (1024 * 16)
88
89/**
90 * How long do we wait at most when queueing messages with core
91 * that we are sending on behalf of other peers.
92 */
93#define DHT_DEFAULT_P2P_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
94
95/**
96 * Default importance for handling messages on behalf of other peers.
97 */
98#define DHT_DEFAULT_P2P_IMPORTANCE 0
99
100/**
101 * How long to keep recent requests around by default.
102 */
103#define DEFAULT_RECENT_REMOVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
104
105/**
106 * Default time to wait to send find peer messages sent by the dht service.
107 */
108#define DHT_DEFAULT_FIND_PEER_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
109
110/**
111 * Default importance for find peer messages sent by the dht service.
112 */
113#define DHT_DEFAULT_FIND_PEER_IMPORTANCE 8
114
115/**
116 * Default replication parameter for find peer messages sent by the dht service.
117 */
118#define DHT_DEFAULT_FIND_PEER_REPLICATION 4
119
120/**
121 * How long at least to wait before sending another find peer request.
122 */
123#define DHT_MINIMUM_FIND_PEER_INTERVAL GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 2)
124
125/**
126 * How long at most to wait before sending another find peer request.
127 */
128#define DHT_MAXIMUM_FIND_PEER_INTERVAL GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 8)
129
130/**
131 * How often to update our preference levels for peers in our routing tables.
132 */
133#define DHT_DEFAULT_PREFERENCE_INTERVAL GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 2)
134
135/**
136 * How long at most on average will we allow a reply forward to take
137 * (before we quit sending out new requests)
138 */
139#define MAX_REQUEST_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
140
141/**
142 * How many initial requests to send out (in true Kademlia fashion)
143 */
144#define DEFAULT_KADEMLIA_REPLICATION 3
145
146/**
147 * Default frequency for sending malicious get messages
148 */
149#define DEFAULT_MALICIOUS_GET_FREQUENCY GNUNET_TIME_UNIT_SECONDS
150
151/**
152 * Default frequency for sending malicious put messages
153 */
154#define DEFAULT_MALICIOUS_PUT_FREQUENCY GNUNET_TIME_UNIT_SECONDS
155
156/**
157 * How many time differences between requesting a core send and
158 * the actual callback to remember.
159 */
160#define MAX_REPLY_TIMES 8
161
162
163/**
164 * Linked list of messages to send to clients.
165 */
166struct P2PPendingMessage
167{
168 /**
169 * Pointer to next item in the list
170 */
171 struct P2PPendingMessage *next;
172
173 /**
174 * Pointer to previous item in the list
175 */
176 struct P2PPendingMessage *prev;
177
178 /**
179 * Message importance level.
180 */
181 unsigned int importance;
182
183 /**
184 * Time when this request was scheduled to be sent.
185 */
186 struct GNUNET_TIME_Absolute scheduled;
187
188 /**
189 * How long to wait before sending message.
190 */
191 struct GNUNET_TIME_Relative timeout;
192
193 /**
194 * Actual message to be sent; // avoid allocation
195 */
196 const struct GNUNET_MessageHeader *msg; // msg = (cast) &pm[1]; // memcpy (&pm[1], data, len);
197
198};
199
200
201/**
202 * Per-peer information.
203 */
204struct PeerInfo
205{
206 /**
207 * Next peer entry (DLL)
208 */
209 struct PeerInfo *next;
210
211 /**
212 * Prev peer entry (DLL)
213 */
214 struct PeerInfo *prev;
215
216 /**
217 * Count of outstanding messages for peer.
218 */
219 unsigned int pending_count;
220
221 /**
222 * Head of pending messages to be sent to this peer.
223 */
224 struct P2PPendingMessage *head;
225
226 /**
227 * Tail of pending messages to be sent to this peer.
228 */
229 struct P2PPendingMessage *tail;
230
231 /**
232 * Core handle for sending messages to this peer.
233 */
234 struct GNUNET_CORE_TransmitHandle *th;
235
236 /**
237 * Task for scheduling message sends.
238 */
239 GNUNET_SCHEDULER_TaskIdentifier send_task;
240
241 /**
242 * Task for scheduling preference updates
243 */
244 GNUNET_SCHEDULER_TaskIdentifier preference_task;
245
246 /**
247 * Preference update context
248 */
249 struct GNUNET_CORE_InformationRequestContext *info_ctx;
250
251 /**
252 * What is the identity of the peer?
253 */
254 struct GNUNET_PeerIdentity id;
255
256#if 0
257 /**
258 * What is the average latency for replies received?
259 */
260 struct GNUNET_TIME_Relative latency;
261
262 /**
263 * Transport level distance to peer.
264 */
265 unsigned int distance;
266#endif
267
268 /**
269 * Task for scheduling periodic ping messages for this peer.
270 */
271 GNUNET_SCHEDULER_TaskIdentifier ping_task;
272};
273
274
275/**
276 * Peers are grouped into buckets.
277 */
278struct PeerBucket
279{
280 /**
281 * Head of DLL
282 */
283 struct PeerInfo *head;
284
285 /**
286 * Tail of DLL
287 */
288 struct PeerInfo *tail;
289
290 /**
291 * Number of peers in the bucket.
292 */
293 unsigned int peers_size;
294};
295
296
297/**
298 * Linked list of messages to send to clients.
299 */
300struct PendingMessage
301{
302 /**
303 * Pointer to next item in the list
304 */
305 struct PendingMessage *next;
306
307 /**
308 * Pointer to previous item in the list
309 */
310 struct PendingMessage *prev;
311
312 /**
313 * Actual message to be sent; // avoid allocation
314 */
315 const struct GNUNET_MessageHeader *msg; // msg = (cast) &pm[1]; // memcpy (&pm[1], data, len);
316
317};
318
319
320/**
321 * Struct containing information about a client,
322 * handle to connect to it, and any pending messages
323 * that need to be sent to it.
324 */
325struct ClientList
326{
327 /**
328 * Linked list of active clients
329 */
330 struct ClientList *next;
331
332 /**
333 * The handle to this client
334 */
335 struct GNUNET_SERVER_Client *client_handle;
336
337 /**
338 * Handle to the current transmission request, NULL
339 * if none pending.
340 */
341 struct GNUNET_CONNECTION_TransmitHandle *transmit_handle;
342
343 /**
344 * Linked list of pending messages for this client
345 */
346 struct PendingMessage *pending_head;
347
348 /**
349 * Tail of linked list of pending messages for this client
350 */
351 struct PendingMessage *pending_tail;
352};
353
354
355/**
356 * Context containing information about a DHT message received.
357 */
358struct DHT_MessageContext
359{
360 /**
361 * The client this request was received from.
362 * (NULL if received from another peer)
363 */
364 struct ClientList *client;
365
366 /**
367 * The peer this request was received from.
368 */
369 struct GNUNET_PeerIdentity peer;
370
371 /**
372 * Bloomfilter for this routing request.
373 */
374 struct GNUNET_CONTAINER_BloomFilter *bloom;
375
376 /**
377 * extended query (see gnunet_block_lib.h).
378 */
379 const void *xquery;
380
381 /**
382 * Bloomfilter to filter out duplicate replies.
383 */
384 struct GNUNET_CONTAINER_BloomFilter *reply_bf;
385
386 /**
387 * The key this request was about
388 */
389 GNUNET_HashCode key;
390
391 /**
392 * How long should we wait to transmit this request?
393 */
394 struct GNUNET_TIME_Relative timeout;
395
396 /**
397 * The unique identifier of this request
398 */
399 uint64_t unique_id;
400
401 /**
402 * Number of bytes in xquery.
403 */
404 size_t xquery_size;
405
406 /**
407 * Mutator value for the reply_bf, see gnunet_block_lib.h
408 */
409 uint32_t reply_bf_mutator;
410
411 /**
412 * Desired replication level
413 */
414 uint32_t replication;
415
416 /**
417 * Network size estimate, either ours or the sum of
418 * those routed to thus far. =~ Log of number of peers
419 * chosen from for this request.
420 */
421 uint32_t network_size;
422
423 /**
424 * Any message options for this request
425 */
426 uint32_t msg_options;
427
428 /**
429 * How many hops has the message already traversed?
430 */
431 uint32_t hop_count;
432
433 /**
434 * How many peer identities are present in the path history?
435 */
436 uint32_t path_history_len;
437
438 /**
439 * Path history.
440 */
441 char *path_history;
442
443 /**
444 * How important is this message?
445 */
446 unsigned int importance;
447
448 /**
449 * Should we (still) forward the request on to other peers?
450 */
451 int do_forward;
452
453 /**
454 * Did we forward this message? (may need to remember it!)
455 */
456 int forwarded;
457
458 /**
459 * Are we the closest known peer to this key (out of our neighbors?)
460 */
461 int closest;
462};
463
464
465/**
466 * Record used for remembering what peers are waiting for what
467 * responses (based on search key).
468 */
469struct DHTRouteSource
470{
471 /**
472 * This is a DLL.
473 */
474 struct DHTRouteSource *next;
475
476 /**
477 * This is a DLL.
478 */
479 struct DHTRouteSource *prev;
480
481 /**
482 * UID of the request, 0 if from another peer.
483 */
484 uint64_t uid;
485
486 /**
487 * Source of the request. Replies should be forwarded to
488 * this peer.
489 */
490 struct GNUNET_PeerIdentity source;
491
492 /**
493 * If this was a local request, remember the client; otherwise NULL.
494 */
495 struct ClientList *client;
496
497 /**
498 * Pointer to this nodes heap location (for removal)
499 */
500 struct GNUNET_CONTAINER_HeapNode *hnode;
501
502 /**
503 * Back pointer to the record storing this information.
504 */
505 struct DHTQueryRecord *record;
506
507 /**
508 * Task to remove this entry on timeout.
509 */
510 GNUNET_SCHEDULER_TaskIdentifier delete_task;
511
512 /**
513 * Bloomfilter of peers we have already sent back as
514 * replies to the initial request. Allows us to not
515 * forward the same peer multiple times for a find peer
516 * request.
517 */
518 struct GNUNET_CONTAINER_BloomFilter *find_peers_responded;
519
520};
521
522
523/**
524 * Entry in the DHT routing table.
525 */
526struct DHTQueryRecord
527{
528 /**
529 * Head of DLL for result forwarding.
530 */
531 struct DHTRouteSource *head;
532
533 /**
534 * Tail of DLL for result forwarding.
535 */
536 struct DHTRouteSource *tail;
537
538 /**
539 * Key that the record concerns.
540 */
541 GNUNET_HashCode key;
542
543};
544
545
546/**
547 * Context used to calculate the number of find peer messages
548 * per X time units since our last scheduled find peer message
549 * was sent. If we have seen too many messages, delay or don't
550 * send our own out.
551 */
552struct FindPeerMessageContext
553{
554 unsigned int count;
555
556 struct GNUNET_TIME_Absolute start;
557
558 struct GNUNET_TIME_Absolute end;
559};
560
561
562/**
563 * DHT Routing results structure
564 */
565struct DHTResults
566{
567 /**
568 * Min heap for removal upon reaching limit
569 */
570 struct GNUNET_CONTAINER_Heap *minHeap;
571
572
573 /**
574 * Hashmap for fast key based lookup
575 */
576 struct GNUNET_CONTAINER_MultiHashMap *hashmap;
577};
578
579
580/**
581 * DHT structure for recent requests.
582 */
583struct RecentRequests
584{
585 /**
586 * Min heap for removal upon reaching limit
587 */
588 struct GNUNET_CONTAINER_Heap *minHeap;
589
590#if HAVE_UID_FOR_TESTING > 1
591 /**
592 * Hashmap for key based lookup
593 */
594 struct GNUNET_CONTAINER_MultiHashMap *hashmap;
595#endif
596
597};
598
599
600struct RecentRequest
601{
602 /**
603 * Position of this node in the min heap.
604 */
605 struct GNUNET_CONTAINER_HeapNode *heap_node;
606
607 /**
608 * Bloomfilter containing entries for peers
609 * we forwarded this request to.
610 */
611 struct GNUNET_CONTAINER_BloomFilter *bloom;
612
613 /**
614 * Timestamp of this request, for ordering
615 * the min heap.
616 */
617 struct GNUNET_TIME_Absolute timestamp;
618
619 /**
620 * Key of this request.
621 */
622 GNUNET_HashCode key;
623
624 /**
625 * Unique identifier for this request, 0 if from another peer.
626 */
627 uint64_t uid;
628
629 /**
630 * Task to remove this entry on timeout.
631 */
632 GNUNET_SCHEDULER_TaskIdentifier remove_task;
633};
634
635
636/**
637 * log of the current network size estimate, used as the point where
638 * we switch between random and deterministic routing. Default
639 * value of 4.0 is used if NSE module is not available (i.e. not
640 * configured).
641 */
642static double log_of_network_size_estimate = 4.0;
643
644/**
645 * Recent requests by hash/uid and by time inserted.
646 */
647static struct RecentRequests recent;
648
649/**
650 * Context to use to calculate find peer rates.
651 */
652static struct FindPeerMessageContext find_peer_context;
653
654/**
655 * Don't use our routing algorithm, always route
656 * to closest peer; initially send requests to 3
657 * peers.
658 */
659static int strict_kademlia;
660
661/**
662 * Routing option to end routing when closest peer found.
663 */
664static int stop_on_closest;
665
666/**
667 * Routing option to end routing when data is found.
668 */
669static int stop_on_found;
670
671/**
672 * Whether DHT needs to manage find peer requests, or
673 * an external force will do it on behalf of the DHT.
674 */
675static int do_find_peer;
676
677/**
678 * Use exactly the forwarding formula as described in
679 * the paper if set to GNUNET_YES, otherwise use the
680 * slightly modified version.
681 */
682static int paper_forwarding;
683
684/**
685 * PUT Peer Identities of peers we know about into
686 * the datacache.
687 */
688static int put_peer_identities;
689
690/**
691 * Use the "real" distance metric when selecting the
692 * next routing hop. Can be less accurate.
693 */
694static int use_real_distance;
695
696/**
697 * How many peers have we added since we sent out our last
698 * find peer request?
699 */
700static unsigned int newly_found_peers;
701
702/**
703 * Container of active queries we should remember
704 */
705static struct DHTResults forward_list;
706
707/**
708 * Handle to the datacache service (for inserting/retrieving data)
709 */
710static struct GNUNET_DATACACHE_Handle *datacache;
711
712/**
713 * Handle for the statistics service.
714 */
715struct GNUNET_STATISTICS_Handle *stats;
716
717/**
718 * Handle to get our current HELLO.
719 */
720static struct GNUNET_TRANSPORT_GetHelloHandle *ghh;
721
722/**
723 * The configuration the DHT service is running with
724 */
725static const struct GNUNET_CONFIGURATION_Handle *cfg;
726
727/**
728 * Handle to the core service
729 */
730static struct GNUNET_CORE_Handle *coreAPI;
731
732/**
733 * Handle to the transport service, for getting our hello
734 */
735static struct GNUNET_TRANSPORT_Handle *transport_handle;
736
737/**
738 * The identity of our peer.
739 */
740static struct GNUNET_PeerIdentity my_identity;
741
742/**
743 * Short id of the peer, for printing
744 */
745static char *my_short_id;
746
747/**
748 * Our HELLO
749 */
750static struct GNUNET_MessageHeader *my_hello;
751
752/**
753 * Task to run when we shut down, cleaning up all our trash
754 */
755static GNUNET_SCHEDULER_TaskIdentifier cleanup_task;
756
757/**
758 * The lowest currently used bucket.
759 */
760static unsigned int lowest_bucket; /* Initially equal to MAX_BUCKETS - 1 */
761
762/**
763 * The buckets (Kademlia routing table, complete with growth).
764 * Array of size MAX_BUCKET_SIZE.
765 */
766static struct PeerBucket k_buckets[MAX_BUCKETS];
767
768/**
769 * Hash map of all known peers, for easy removal from k_buckets on disconnect.
770 */
771static struct GNUNET_CONTAINER_MultiHashMap *all_known_peers;
772
773/**
774 * Recently seen find peer requests.
775 */
776static struct GNUNET_CONTAINER_MultiHashMap *recent_find_peer_requests;
777
778/**
779 * Maximum size for each bucket.
780 */
781static unsigned int bucket_size = DEFAULT_BUCKET_SIZE;
782
783/**
784 * List of active clients.
785 */
786static struct ClientList *client_list;
787
788/**
789 * Handle to the DHT logger.
790 */
791static struct GNUNET_DHTLOG_Handle *dhtlog_handle;
792
793/**
794 * Whether or not to send routing debugging information
795 * to the dht logging server
796 */
797static unsigned int debug_routes;
798
799/**
800 * Whether or not to send FULL route information to
801 * logging server
802 */
803static unsigned int debug_routes_extended;
804
805/**
806 * GNUNET_YES or GNUNET_NO, whether or not to act as
807 * a malicious node which drops all messages
808 */
809static unsigned int malicious_dropper;
810
811/**
812 * GNUNET_YES or GNUNET_NO, whether or not to act as
813 * a malicious node which sends out lots of GETS
814 */
815static unsigned int malicious_getter;
816
817/**
818 * GNUNET_YES or GNUNET_NO, whether or not to act as
819 * a malicious node which sends out lots of PUTS
820 */
821static unsigned int malicious_putter;
822
823/**
824 * Frequency for malicious get requests.
825 */
826static struct GNUNET_TIME_Relative malicious_get_frequency;
827
828/**
829 * Frequency for malicious put requests.
830 */
831static struct GNUNET_TIME_Relative malicious_put_frequency;
832
833/**
834 * Kademlia replication
835 */
836static unsigned long long kademlia_replication;
837
838/**
839 * Reply times for requests, if we are busy, don't send any
840 * more requests!
841 */
842static struct GNUNET_TIME_Relative reply_times[MAX_REPLY_TIMES];
843
844/**
845 * Current counter for replies.
846 */
847static unsigned int reply_counter;
848
849/**
850 * Our handle to the BLOCK library.
851 */
852static struct GNUNET_BLOCK_Context *block_context;
853
854/**
855 * Network size estimation handle.
856 */
857static struct GNUNET_NSE_Handle *nse;
858
859
860/**
861 * Callback that is called when network size estimate is updated.
862 *
863 * @param cls closure
864 * @param timestamp time when the estimate was received from the server (or created by the server)
865 * @param logestimate the log(Base 2) value of the current network size estimate
866 * @param std_dev standard deviation for the estimate
867 *
868 */
869static void
870update_network_size_estimate (void *cls, struct GNUNET_TIME_Absolute timestamp,
871 double logestimate, double std_dev)
872{
873 log_of_network_size_estimate = logestimate;
874}
875
876
877/**
878 * Forward declaration.
879 */
880static size_t
881send_generic_reply (void *cls, size_t size, void *buf);
882
883
884/** Declare here so retry_core_send is aware of it */
885static size_t
886core_transmit_notify (void *cls, size_t size, void *buf);
887
888
889/**
890 * Convert unique ID to hash code.
891 *
892 * @param uid unique ID to convert
893 * @param hash set to uid (extended with zeros)
894 */
895static void
896hash_from_uid (uint64_t uid, GNUNET_HashCode * hash)
897{
898 memset (hash, 0, sizeof (GNUNET_HashCode));
899 *((uint64_t *) hash) = uid;
900}
901
902
903/**
904 * Given the largest send delay, artificially decrease it
905 * so the next time around we may have a chance at sending
906 * again.
907 */
908static void
909decrease_max_send_delay (struct GNUNET_TIME_Relative max_time)
910{
911 unsigned int i;
912
913 for (i = 0; i < MAX_REPLY_TIMES; i++)
914 {
915 if (reply_times[i].rel_value == max_time.rel_value)
916 {
917 reply_times[i].rel_value = reply_times[i].rel_value / 2;
918 return;
919 }
920 }
921}
922
923
924/**
925 * Find the maximum send time of the recently sent values.
926 *
927 * @return the average time between asking core to send a message
928 * and when the buffer for copying it is passed
929 */
930static struct GNUNET_TIME_Relative
931get_max_send_delay ()
932{
933 unsigned int i;
934 struct GNUNET_TIME_Relative max_time;
935
936 max_time = GNUNET_TIME_relative_get_zero ();
937
938 for (i = 0; i < MAX_REPLY_TIMES; i++)
939 {
940 if (reply_times[i].rel_value > max_time.rel_value)
941 max_time.rel_value = reply_times[i].rel_value;
942 }
943#if DEBUG_DHT
944 if (max_time.rel_value > MAX_REQUEST_TIME.rel_value)
945 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Max send delay was %llu\n",
946 (unsigned long long) max_time.rel_value);
947#endif
948 return max_time;
949}
950
951
952static void
953increment_stats (const char *value)
954{
955 if (stats == NULL)
956 return;
957 GNUNET_STATISTICS_update (stats, value, 1, GNUNET_NO);
958}
959
960
961static void
962decrement_stats (const char *value)
963{
964 if (stats == NULL)
965 return;
966 GNUNET_STATISTICS_update (stats, value, -1, GNUNET_NO);
967}
968
969
970/**
971 * Try to send another message from our core send list
972 */
973static void
974try_core_send (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
975{
976 struct PeerInfo *peer = cls;
977 struct P2PPendingMessage *pending;
978 size_t ssize;
979
980 peer->send_task = GNUNET_SCHEDULER_NO_TASK;
981
982 if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
983 return;
984
985 if (peer->th != NULL)
986 return; /* Message send already in progress */
987
988 pending = peer->head;
989 if (pending != NULL)
990 {
991 ssize = ntohs (pending->msg->size);
992#if DEBUG_DHT > 1
993 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
994 "`%s:%s': Calling notify_transmit_ready with size %d for peer %s\n",
995 my_short_id, "DHT", ssize, GNUNET_i2s (&peer->id));
996#endif
997 pending->scheduled = GNUNET_TIME_absolute_get ();
998 reply_counter++;
999 if (reply_counter >= MAX_REPLY_TIMES)
1000 reply_counter = 0;
1001 peer->th =
1002 GNUNET_CORE_notify_transmit_ready (coreAPI, GNUNET_YES,
1003 pending->importance,
1004 pending->timeout, &peer->id, ssize,
1005 &core_transmit_notify, peer);
1006 if (peer->th == NULL)
1007 increment_stats ("# notify transmit ready failed");
1008 }
1009}
1010
1011
1012/**
1013 * Function called to send a request out to another peer.
1014 * Called both for locally initiated requests and those
1015 * received from other peers.
1016 *
1017 * @param msg the encapsulated message
1018 * @param peer the peer to forward the message to
1019 * @param msg_ctx the context of the message (hop count, bloom, etc.)
1020 */
1021static void
1022forward_result_message (const struct GNUNET_MessageHeader *msg,
1023 struct PeerInfo *peer,
1024 struct DHT_MessageContext *msg_ctx)
1025{
1026 struct GNUNET_DHT_P2PRouteResultMessage *result_message;
1027 struct P2PPendingMessage *pending;
1028 size_t msize;
1029 size_t psize;
1030 char *path_start;
1031 char *path_offset;
1032
1033#if DEBUG_PATH
1034 unsigned int i;
1035#endif
1036
1037 increment_stats (STAT_RESULT_FORWARDS);
1038 msize =
1039 sizeof (struct GNUNET_DHT_P2PRouteResultMessage) + ntohs (msg->size) +
1040 (sizeof (struct GNUNET_PeerIdentity) * msg_ctx->path_history_len);
1041 GNUNET_assert (msize <= GNUNET_SERVER_MAX_MESSAGE_SIZE);
1042 psize = sizeof (struct P2PPendingMessage) + msize;
1043 pending = GNUNET_malloc (psize);
1044 pending->msg = (struct GNUNET_MessageHeader *) &pending[1];
1045 pending->importance = DHT_SEND_PRIORITY;
1046 pending->timeout = GNUNET_TIME_relative_get_forever ();
1047 result_message = (struct GNUNET_DHT_P2PRouteResultMessage *) pending->msg;
1048 result_message->header.size = htons (msize);
1049 result_message->header.type =
1050 htons (GNUNET_MESSAGE_TYPE_DHT_P2P_ROUTE_RESULT);
1051 result_message->outgoing_path_length = htonl (msg_ctx->path_history_len);
1052 if (msg_ctx->path_history_len > 0)
1053 {
1054 /* End of pending is where enc_msg starts */
1055 path_start = (char *) &pending[1];
1056 /* Offset by the size of the enc_msg */
1057 path_start += ntohs (msg->size);
1058 memcpy (path_start, msg_ctx->path_history,
1059 msg_ctx->path_history_len * (sizeof (struct GNUNET_PeerIdentity)));
1060#if DEBUG_PATH
1061 for (i = 0; i < msg_ctx->path_history_len; i++)
1062 {
1063 path_offset =
1064 &msg_ctx->path_history[i * sizeof (struct GNUNET_PeerIdentity)];
1065 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1066 "(forward_result) Key %s Found peer %d:%s\n",
1067 GNUNET_h2s (&msg_ctx->key), i,
1068 GNUNET_i2s ((struct GNUNET_PeerIdentity *) path_offset));
1069 }
1070#endif
1071 }
1072 result_message->options = htonl (msg_ctx->msg_options);
1073 result_message->hop_count = htonl (msg_ctx->hop_count + 1);
1074#if HAVE_UID_FOR_TESTING
1075 result_message->unique_id = GNUNET_htonll (msg_ctx->unique_id);
1076#endif
1077 memcpy (&result_message->key, &msg_ctx->key, sizeof (GNUNET_HashCode));
1078 /* Copy the enc_msg, then the path history as well! */
1079 memcpy (&result_message[1], msg, ntohs (msg->size));
1080 path_offset = (char *) &result_message[1];
1081 path_offset += ntohs (msg->size);
1082 /* If we have path history, copy it to the end of the whole thing */
1083 if (msg_ctx->path_history_len > 0)
1084 memcpy (path_offset, msg_ctx->path_history,
1085 msg_ctx->path_history_len * (sizeof (struct GNUNET_PeerIdentity)));
1086#if DEBUG_DHT > 1
1087 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1088 "%s:%s Adding pending message size %d for peer %s\n", my_short_id,
1089 "DHT", msize, GNUNET_i2s (&peer->id));
1090#endif
1091 peer->pending_count++;
1092 increment_stats ("# pending messages scheduled");
1093 GNUNET_CONTAINER_DLL_insert_after (peer->head, peer->tail, peer->tail,
1094 pending);
1095 if (peer->send_task == GNUNET_SCHEDULER_NO_TASK)
1096 peer->send_task = GNUNET_SCHEDULER_add_now (&try_core_send, peer);
1097}
1098
1099
1100/**
1101 * Called when core is ready to send a message we asked for
1102 * out to the destination.
1103 *
1104 * @param cls closure (NULL)
1105 * @param size number of bytes available in buf
1106 * @param buf where the callee should write the message
1107 * @return number of bytes written to buf
1108 */
1109static size_t
1110core_transmit_notify (void *cls, size_t size, void *buf)
1111{
1112 struct PeerInfo *peer = cls;
1113 char *cbuf = buf;
1114 struct P2PPendingMessage *pending;
1115
1116 size_t off;
1117 size_t msize;
1118
1119 peer->th = NULL;
1120 if (buf == NULL)
1121 {
1122 /* client disconnected */
1123#if DEBUG_DHT
1124 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "`%s:%s': buffer was NULL\n",
1125 my_short_id, "DHT");
1126#endif
1127 return 0;
1128 }
1129
1130 if (peer->head == NULL)
1131 return 0;
1132
1133 off = 0;
1134 pending = peer->head;
1135#if DUMB
1136 reply_times[reply_counter] =
1137 GNUNET_TIME_absolute_get_difference (pending->scheduled,
1138 GNUNET_TIME_absolute_get ());
1139 msize = ntohs (pending->msg->size);
1140 if (msize <= size)
1141 {
1142 off = msize;
1143 memcpy (cbuf, pending->msg, msize);
1144 peer->pending_count--;
1145 increment_stats ("# pending messages sent");
1146 GNUNET_assert (peer->pending_count >= 0);
1147 GNUNET_CONTAINER_DLL_remove (peer->head, peer->tail, pending);
1148 GNUNET_free (pending);
1149 }
1150#else
1151 while (NULL != pending &&
1152 (size - off >= (msize = ntohs (pending->msg->size))))
1153 {
1154 memcpy (&cbuf[off], pending->msg, msize);
1155 off += msize;
1156 peer->pending_count--;
1157 increment_stats ("# pending messages sent");
1158 GNUNET_CONTAINER_DLL_remove (peer->head, peer->tail, pending);
1159 GNUNET_free (pending);
1160 pending = peer->head;
1161 }
1162#endif
1163 if ((peer->head != NULL) && (peer->send_task == GNUNET_SCHEDULER_NO_TASK))
1164 peer->send_task = GNUNET_SCHEDULER_add_now (&try_core_send, peer);
1165
1166 return off;
1167}
1168
1169
1170/**
1171 * Compute the distance between have and target as a 32-bit value.
1172 * Differences in the lower bits must count stronger than differences
1173 * in the higher bits.
1174 *
1175 * @return 0 if have==target, otherwise a number
1176 * that is larger as the distance between
1177 * the two hash codes increases
1178 */
1179static unsigned int
1180distance (const GNUNET_HashCode * target, const GNUNET_HashCode * have)
1181{
1182 unsigned int bucket;
1183 unsigned int msb;
1184 unsigned int lsb;
1185 unsigned int i;
1186
1187 /* We have to represent the distance between two 2^9 (=512)-bit
1188 * numbers as a 2^5 (=32)-bit number with "0" being used for the
1189 * two numbers being identical; furthermore, we need to
1190 * guarantee that a difference in the number of matching
1191 * bits is always represented in the result.
1192 *
1193 * We use 2^32/2^9 numerical values to distinguish between
1194 * hash codes that have the same LSB bit distance and
1195 * use the highest 2^9 bits of the result to signify the
1196 * number of (mis)matching LSB bits; if we have 0 matching
1197 * and hence 512 mismatching LSB bits we return -1 (since
1198 * 512 itself cannot be represented with 9 bits) */
1199
1200 /* first, calculate the most significant 9 bits of our
1201 * result, aka the number of LSBs */
1202 bucket = GNUNET_CRYPTO_hash_matching_bits (target, have);
1203 /* bucket is now a value between 0 and 512 */
1204 if (bucket == 512)
1205 return 0; /* perfect match */
1206 if (bucket == 0)
1207 return (unsigned int) -1; /* LSB differs; use max (if we did the bit-shifting
1208 * below, we'd end up with max+1 (overflow)) */
1209
1210 /* calculate the most significant bits of the final result */
1211 msb = (512 - bucket) << (32 - 9);
1212 /* calculate the 32-9 least significant bits of the final result by
1213 * looking at the differences in the 32-9 bits following the
1214 * mismatching bit at 'bucket' */
1215 lsb = 0;
1216 for (i = bucket + 1;
1217 (i < sizeof (GNUNET_HashCode) * 8) && (i < bucket + 1 + 32 - 9); i++)
1218 {
1219 if (GNUNET_CRYPTO_hash_get_bit (target, i) !=
1220 GNUNET_CRYPTO_hash_get_bit (have, i))
1221 lsb |= (1 << (bucket + 32 - 9 - i)); /* first bit set will be 10,
1222 * last bit set will be 31 -- if
1223 * i does not reach 512 first... */
1224 }
1225 return msb | lsb;
1226}
1227
1228
1229/**
1230 * Return a number that is larger the closer the
1231 * "have" GNUNET_hash code is to the "target".
1232 *
1233 * @return inverse distance metric, non-zero.
1234 * Must fudge the value if NO bits match.
1235 */
1236static unsigned int
1237inverse_distance (const GNUNET_HashCode * target, const GNUNET_HashCode * have)
1238{
1239 if (GNUNET_CRYPTO_hash_matching_bits (target, have) == 0)
1240 return 1; /* Never return 0! */
1241 return ((unsigned int) -1) - distance (target, have);
1242}
1243
1244
1245/**
1246 * Find the optimal bucket for this key, regardless
1247 * of the current number of buckets in use.
1248 *
1249 * @param hc the hashcode to compare our identity to
1250 *
1251 * @return the proper bucket index, or GNUNET_SYSERR
1252 * on error (same hashcode)
1253 */
1254static int
1255find_bucket (const GNUNET_HashCode * hc)
1256{
1257 unsigned int bits;
1258
1259 bits = GNUNET_CRYPTO_hash_matching_bits (&my_identity.hashPubKey, hc);
1260 if (bits == MAX_BUCKETS)
1261 return GNUNET_SYSERR;
1262 return MAX_BUCKETS - bits - 1;
1263}
1264
1265
1266/**
1267 * Find which k-bucket this peer should go into,
1268 * taking into account the size of the k-bucket
1269 * array. This means that if more bits match than
1270 * there are currently buckets, lowest_bucket will
1271 * be returned.
1272 *
1273 * @param hc GNUNET_HashCode we are finding the bucket for.
1274 *
1275 * @return the proper bucket index for this key,
1276 * or GNUNET_SYSERR on error (same hashcode)
1277 */
1278static int
1279find_current_bucket (const GNUNET_HashCode * hc)
1280{
1281 int actual_bucket;
1282
1283 actual_bucket = find_bucket (hc);
1284 if (actual_bucket == GNUNET_SYSERR) /* hc and our peer identity match! */
1285 return lowest_bucket;
1286 if (actual_bucket < lowest_bucket) /* actual_bucket not yet used */
1287 return lowest_bucket;
1288 return actual_bucket;
1289}
1290
1291
1292/**
1293 * Find a routing table entry from a peer identity
1294 *
1295 * @param peer the peer identity to look up
1296 *
1297 * @return the routing table entry, or NULL if not found
1298 */
1299static struct PeerInfo *
1300find_peer_by_id (const struct GNUNET_PeerIdentity *peer)
1301{
1302 int bucket;
1303 struct PeerInfo *pos;
1304
1305 bucket = find_current_bucket (&peer->hashPubKey);
1306
1307 if (0 == memcmp (&my_identity, peer, sizeof (struct GNUNET_PeerIdentity)))
1308 return NULL;
1309
1310 pos = k_buckets[bucket].head;
1311 while (pos != NULL)
1312 {
1313 if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
1314 return pos;
1315 pos = pos->next;
1316 }
1317 return NULL; /* No such peer. */
1318}
1319
1320/* Forward declaration */
1321static void
1322update_core_preference (void *cls,
1323 const struct GNUNET_SCHEDULER_TaskContext *tc);
1324
1325
1326/**
1327 * Function called with statistics about the given peer.
1328 *
1329 * @param cls closure
1330 * @param peer identifies the peer
1331 * @param bpm_out set to the current bandwidth limit (sending) for this peer
1332 * @param amount set to the amount that was actually reserved or unreserved;
1333 * either the full requested amount or zero (no partial reservations)
1334 * @param res_delay if the reservation could not be satisfied (amount was 0), how
1335 * long should the client wait until re-trying?
1336 * @param preference current traffic preference for the given peer
1337 */
1338static void
1339update_core_preference_finish (void *cls,
1340 const struct GNUNET_PeerIdentity *peer,
1341 struct GNUNET_BANDWIDTH_Value32NBO bpm_out,
1342 int32_t amount,
1343 struct GNUNET_TIME_Relative res_delay,
1344 uint64_t preference)
1345{
1346 struct PeerInfo *peer_info = cls;
1347
1348 peer_info->info_ctx = NULL;
1349 GNUNET_SCHEDULER_add_delayed (DHT_DEFAULT_PREFERENCE_INTERVAL,
1350 &update_core_preference, peer_info);
1351}
1352
1353static void
1354update_core_preference (void *cls,
1355 const struct GNUNET_SCHEDULER_TaskContext *tc)
1356{
1357 struct PeerInfo *peer = cls;
1358 uint64_t preference;
1359 unsigned int matching;
1360
1361 if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
1362 {
1363 return;
1364 }
1365 matching =
1366 GNUNET_CRYPTO_hash_matching_bits (&my_identity.hashPubKey,
1367 &peer->id.hashPubKey);
1368 if (matching >= 64)
1369 {
1370#if DEBUG_DHT
1371 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1372 "Peer identifier matches by %u bits, only shifting as much as we can!\n",
1373 matching);
1374#endif
1375 matching = 63;
1376 }
1377 preference = 1LL << matching;
1378 peer->info_ctx =
1379 GNUNET_CORE_peer_change_preference (coreAPI, &peer->id,
1380 GNUNET_TIME_UNIT_FOREVER_REL,
1381 GNUNET_BANDWIDTH_VALUE_MAX, 0,
1382 preference,
1383 &update_core_preference_finish, peer);
1384}
1385
1386
1387/**
1388 * Given a peer and its corresponding bucket,
1389 * remove it from that bucket. Does not free
1390 * the PeerInfo struct, nor cancel messages
1391 * or free messages waiting to be sent to this
1392 * peer!
1393 *
1394 * @param peer the peer to remove
1395 * @param bucket the bucket the peer belongs to
1396 */
1397static void
1398remove_peer (struct PeerInfo *peer, unsigned int bucket)
1399{
1400 GNUNET_assert (k_buckets[bucket].peers_size > 0);
1401 GNUNET_CONTAINER_DLL_remove (k_buckets[bucket].head, k_buckets[bucket].tail,
1402 peer);
1403 k_buckets[bucket].peers_size--;
1404#if CHANGE_LOWEST
1405 if ((bucket == lowest_bucket) && (k_buckets[lowest_bucket].peers_size == 0) &&
1406 (lowest_bucket < MAX_BUCKETS - 1))
1407 lowest_bucket++;
1408#endif
1409}
1410
1411/**
1412 * Removes peer from a bucket, then frees associated
1413 * resources and frees peer.
1414 *
1415 * @param peer peer to be removed and freed
1416 * @param bucket which bucket this peer belongs to
1417 */
1418static void
1419delete_peer (struct PeerInfo *peer, unsigned int bucket)
1420{
1421 struct P2PPendingMessage *pos;
1422 struct P2PPendingMessage *next;
1423
1424 remove_peer (peer, bucket); /* First remove the peer from its bucket */
1425 if (peer->send_task != GNUNET_SCHEDULER_NO_TASK)
1426 GNUNET_SCHEDULER_cancel (peer->send_task);
1427 if ((peer->th != NULL) && (coreAPI != NULL))
1428 GNUNET_CORE_notify_transmit_ready_cancel (peer->th);
1429
1430 pos = peer->head;
1431 while (pos != NULL) /* Remove any pending messages for this peer */
1432 {
1433 increment_stats
1434 ("# dht pending messages discarded (due to disconnect/shutdown)");
1435 next = pos->next;
1436 GNUNET_free (pos);
1437 pos = next;
1438 }
1439
1440 GNUNET_assert (GNUNET_CONTAINER_multihashmap_contains
1441 (all_known_peers, &peer->id.hashPubKey));
1442 GNUNET_assert (GNUNET_YES ==
1443 GNUNET_CONTAINER_multihashmap_remove (all_known_peers,
1444 &peer->id.hashPubKey,
1445 peer));
1446 GNUNET_free (peer);
1447 decrement_stats (STAT_PEERS_KNOWN);
1448}
1449
1450
1451/**
1452 * Iterator over hash map entries.
1453 *
1454 * @param cls closure
1455 * @param key current key code
1456 * @param value PeerInfo of the peer to move to new lowest bucket
1457 * @return GNUNET_YES if we should continue to
1458 * iterate,
1459 * GNUNET_NO if not.
1460 */
1461static int
1462move_lowest_bucket (void *cls, const GNUNET_HashCode * key, void *value)
1463{
1464 struct PeerInfo *peer = value;
1465 int new_bucket;
1466
1467 GNUNET_assert (lowest_bucket > 0);
1468 new_bucket = lowest_bucket - 1;
1469 remove_peer (peer, lowest_bucket);
1470 GNUNET_CONTAINER_DLL_insert_after (k_buckets[new_bucket].head,
1471 k_buckets[new_bucket].tail,
1472 k_buckets[new_bucket].tail, peer);
1473 k_buckets[new_bucket].peers_size++;
1474 return GNUNET_YES;
1475}
1476
1477
1478/**
1479 * The current lowest bucket is full, so change the lowest
1480 * bucket to the next lower down, and move any appropriate
1481 * entries in the current lowest bucket to the new bucket.
1482 */
1483static void
1484enable_next_bucket ()
1485{
1486 struct GNUNET_CONTAINER_MultiHashMap *to_remove;
1487 struct PeerInfo *pos;
1488
1489 GNUNET_assert (lowest_bucket > 0);
1490 to_remove = GNUNET_CONTAINER_multihashmap_create (bucket_size);
1491 pos = k_buckets[lowest_bucket].head;
1492
1493 /* Populate the array of peers which should be in the next lowest bucket */
1494 while (pos != NULL)
1495 {
1496 if (find_bucket (&pos->id.hashPubKey) < lowest_bucket)
1497 GNUNET_CONTAINER_multihashmap_put (to_remove, &pos->id.hashPubKey, pos,
1498 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
1499 pos = pos->next;
1500 }
1501
1502 /* Remove peers from lowest bucket, insert into next lowest bucket */
1503 GNUNET_CONTAINER_multihashmap_iterate (to_remove, &move_lowest_bucket, NULL);
1504 GNUNET_CONTAINER_multihashmap_destroy (to_remove);
1505 lowest_bucket = lowest_bucket - 1;
1506}
1507
1508
1509/**
1510 * Find the closest peer in our routing table to the
1511 * given hashcode.
1512 *
1513 * @return The closest peer in our routing table to the
1514 * key, or NULL on error.
1515 */
1516static struct PeerInfo *
1517find_closest_peer (const GNUNET_HashCode * hc)
1518{
1519 struct PeerInfo *pos;
1520 struct PeerInfo *current_closest;
1521 unsigned int lowest_distance;
1522 unsigned int temp_distance;
1523 int bucket;
1524 int count;
1525
1526 lowest_distance = -1;
1527
1528 if (k_buckets[lowest_bucket].peers_size == 0)
1529 return NULL;
1530
1531 current_closest = NULL;
1532 for (bucket = lowest_bucket; bucket < MAX_BUCKETS; bucket++)
1533 {
1534 pos = k_buckets[bucket].head;
1535 count = 0;
1536 while ((pos != NULL) && (count < bucket_size))
1537 {
1538 temp_distance = distance (&pos->id.hashPubKey, hc);
1539 if (temp_distance <= lowest_distance)
1540 {
1541 lowest_distance = temp_distance;
1542 current_closest = pos;
1543 }
1544 pos = pos->next;
1545 count++;
1546 }
1547 }
1548 GNUNET_assert (current_closest != NULL);
1549 return current_closest;
1550}
1551
1552
1553/**
1554 * Function called to send a request out to another peer.
1555 * Called both for locally initiated requests and those
1556 * received from other peers.
1557 *
1558 * @param msg the encapsulated message
1559 * @param peer the peer to forward the message to
1560 * @param msg_ctx the context of the message (hop count, bloom, etc.)
1561 */
1562static void
1563forward_message (const struct GNUNET_MessageHeader *msg, struct PeerInfo *peer,
1564 struct DHT_MessageContext *msg_ctx)
1565{
1566 struct GNUNET_DHT_P2PRouteMessage *route_message;
1567 struct P2PPendingMessage *pending;
1568 size_t msize;
1569 size_t psize;
1570 char *route_path;
1571
1572 increment_stats (STAT_ROUTE_FORWARDS);
1573 GNUNET_assert (peer != NULL);
1574 if ((msg_ctx->closest != GNUNET_YES) &&
1575 (peer == find_closest_peer (&msg_ctx->key)))
1576 increment_stats (STAT_ROUTE_FORWARDS_CLOSEST);
1577
1578 msize =
1579 sizeof (struct GNUNET_DHT_P2PRouteMessage) + ntohs (msg->size) +
1580 (msg_ctx->path_history_len * sizeof (struct GNUNET_PeerIdentity));
1581 GNUNET_assert (msize <= GNUNET_SERVER_MAX_MESSAGE_SIZE);
1582 psize = sizeof (struct P2PPendingMessage) + msize;
1583 pending = GNUNET_malloc (psize);
1584 pending->msg = (struct GNUNET_MessageHeader *) &pending[1];
1585 pending->importance = msg_ctx->importance;
1586 pending->timeout = msg_ctx->timeout;
1587 route_message = (struct GNUNET_DHT_P2PRouteMessage *) pending->msg;
1588 route_message->header.size = htons (msize);
1589 route_message->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_P2P_ROUTE);
1590 route_message->options = htonl (msg_ctx->msg_options);
1591 route_message->hop_count = htonl (msg_ctx->hop_count + 1);
1592 route_message->network_size = htonl (msg_ctx->network_size);
1593 route_message->desired_replication_level = htonl (msg_ctx->replication);
1594#if HAVE_UID_FOR_TESTING
1595 route_message->unique_id = GNUNET_htonll (msg_ctx->unique_id);
1596#endif
1597 if (msg_ctx->bloom != NULL)
1598 GNUNET_assert (GNUNET_OK ==
1599 GNUNET_CONTAINER_bloomfilter_get_raw_data (msg_ctx->bloom,
1600 route_message->
1601 bloomfilter,
1602 DHT_BLOOM_SIZE));
1603 memcpy (&route_message->key, &msg_ctx->key, sizeof (GNUNET_HashCode));
1604 memcpy (&route_message[1], msg, ntohs (msg->size));
1605 if (GNUNET_DHT_RO_RECORD_ROUTE ==
1606 (msg_ctx->msg_options & GNUNET_DHT_RO_RECORD_ROUTE))
1607 {
1608 route_message->outgoing_path_length = htonl (msg_ctx->path_history_len);
1609 /* Set pointer to start of enc_msg */
1610 route_path = (char *) &route_message[1];
1611 /* Offset to the end of the enc_msg */
1612 route_path += ntohs (msg->size);
1613 /* Copy the route_path after enc_msg */
1614 memcpy (route_path, msg_ctx->path_history,
1615 msg_ctx->path_history_len * sizeof (struct GNUNET_PeerIdentity));
1616 }
1617#if DEBUG_DHT > 1
1618 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1619 "%s:%s Adding pending message size %d for peer %s\n", my_short_id,
1620 "DHT", msize, GNUNET_i2s (&peer->id));
1621#endif
1622 peer->pending_count++;
1623 increment_stats ("# pending messages scheduled");
1624 GNUNET_CONTAINER_DLL_insert_after (peer->head, peer->tail, peer->tail,
1625 pending);
1626 if (peer->send_task == GNUNET_SCHEDULER_NO_TASK)
1627 peer->send_task = GNUNET_SCHEDULER_add_now (&try_core_send, peer);
1628}
1629
1630
1631/**
1632 * Task run to check for messages that need to be sent to a client.
1633 *
1634 * @param client a ClientList, containing the client and any messages to be sent to it
1635 */
1636static void
1637process_pending_messages (struct ClientList *client)
1638{
1639 if ((client->pending_head == NULL) || (client->transmit_handle != NULL))
1640 return;
1641 client->transmit_handle =
1642 GNUNET_SERVER_notify_transmit_ready (client->client_handle,
1643 ntohs (client->pending_head->
1644 msg->size),
1645 GNUNET_TIME_UNIT_FOREVER_REL,
1646 &send_generic_reply, client);
1647}
1648
1649/**
1650 * Callback called as a result of issuing a GNUNET_SERVER_notify_transmit_ready
1651 * request. A ClientList is passed as closure, take the head of the list
1652 * and copy it into buf, which has the result of sending the message to the
1653 * client.
1654 *
1655 * @param cls closure to this call
1656 * @param size maximum number of bytes available to send
1657 * @param buf where to copy the actual message to
1658 *
1659 * @return the number of bytes actually copied, 0 indicates failure
1660 */
1661static size_t
1662send_generic_reply (void *cls, size_t size, void *buf)
1663{
1664 struct ClientList *client = cls;
1665 char *cbuf = buf;
1666 struct PendingMessage *reply;
1667 size_t off;
1668 size_t msize;
1669
1670 client->transmit_handle = NULL;
1671 if (buf == NULL)
1672 {
1673 /* client disconnected */
1674 return 0;
1675 }
1676 off = 0;
1677 while ((NULL != (reply = client->pending_head)) &&
1678 (size >= off + (msize = ntohs (reply->msg->size))))
1679 {
1680 GNUNET_CONTAINER_DLL_remove (client->pending_head, client->pending_tail,
1681 reply);
1682 memcpy (&cbuf[off], reply->msg, msize);
1683 GNUNET_free (reply);
1684 off += msize;
1685 }
1686 process_pending_messages (client);
1687#if DEBUG_DHT
1688 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1689 "Transmitted %u bytes of replies to client\n",
1690 (unsigned int) off);
1691#endif
1692 return off;
1693}
1694
1695
1696/**
1697 * Add a PendingMessage to the clients list of messages to be sent
1698 *
1699 * @param client the active client to send the message to
1700 * @param pending_message the actual message to send
1701 */
1702static void
1703add_pending_message (struct ClientList *client,
1704 struct PendingMessage *pending_message)
1705{
1706 GNUNET_CONTAINER_DLL_insert_after (client->pending_head, client->pending_tail,
1707 client->pending_tail, pending_message);
1708 process_pending_messages (client);
1709}
1710
1711
1712/**
1713 * Called when a reply needs to be sent to a client, as
1714 * a result it found to a GET or FIND PEER request.
1715 *
1716 * @param client the client to send the reply to
1717 * @param message the encapsulated message to send
1718 * @param msg_ctx the context of the received message
1719 */
1720static void
1721send_reply_to_client (struct ClientList *client,
1722 const struct GNUNET_MessageHeader *message,
1723 struct DHT_MessageContext *msg_ctx)
1724{
1725 struct GNUNET_DHT_RouteResultMessage *reply;
1726 struct PendingMessage *pending_message;
1727 uint16_t msize;
1728 size_t tsize;
1729 char *reply_offset;
1730
1731#if DEBUG_PATH
1732 char *path_offset;
1733 unsigned int i;
1734#endif
1735#if DEBUG_DHT
1736 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "`%s:%s': Sending reply to client.\n",
1737 my_short_id, "DHT");
1738#endif
1739 msize = ntohs (message->size);
1740 tsize =
1741 sizeof (struct GNUNET_DHT_RouteResultMessage) + msize +
1742 (msg_ctx->path_history_len * sizeof (struct GNUNET_PeerIdentity));
1743 if (tsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
1744 {
1745 GNUNET_break_op (0);
1746 return;
1747 }
1748 pending_message = GNUNET_malloc (sizeof (struct PendingMessage) + tsize);
1749 pending_message->msg = (struct GNUNET_MessageHeader *) &pending_message[1];
1750 reply = (struct GNUNET_DHT_RouteResultMessage *) &pending_message[1];
1751 reply->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_LOCAL_ROUTE_RESULT);
1752 reply->header.size = htons (tsize);
1753 reply->outgoing_path_length = htonl (msg_ctx->path_history_len);
1754 reply->unique_id = GNUNET_htonll (msg_ctx->unique_id);
1755 memcpy (&reply->key, &msg_ctx->key, sizeof (GNUNET_HashCode));
1756 reply_offset = (char *) &reply[1];
1757 memcpy (&reply[1], message, msize);
1758 if (msg_ctx->path_history_len > 0)
1759 {
1760 reply_offset += msize;
1761 memcpy (reply_offset, msg_ctx->path_history,
1762 msg_ctx->path_history_len * sizeof (struct GNUNET_PeerIdentity));
1763 }
1764#if DEBUG_PATH
1765 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1766 "Returning message with outgoing path length %d\n",
1767 msg_ctx->path_history_len);
1768 for (i = 0; i < msg_ctx->path_history_len; i++)
1769 {
1770 path_offset =
1771 &msg_ctx->path_history[i * sizeof (struct GNUNET_PeerIdentity)];
1772 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found peer %d:%s\n", i,
1773 GNUNET_i2s ((struct GNUNET_PeerIdentity *) path_offset));
1774 }
1775#endif
1776 add_pending_message (client, pending_message);
1777}
1778
1779/**
1780 * Consider whether or not we would like to have this peer added to
1781 * our routing table. Check whether bucket for this peer is full,
1782 * if so return negative; if not return positive. Since peers are
1783 * only added on CORE level connect, this doesn't actually add the
1784 * peer to the routing table.
1785 *
1786 * @param peer the peer we are considering adding
1787 *
1788 * @return GNUNET_YES if we want this peer, GNUNET_NO if not (bucket
1789 * already full)
1790 */
1791static int
1792consider_peer (struct GNUNET_PeerIdentity *peer)
1793{
1794 int bucket;
1795
1796 if ((GNUNET_YES ==
1797 GNUNET_CONTAINER_multihashmap_contains (all_known_peers,
1798 &peer->hashPubKey)) ||
1799 (0 == memcmp (&my_identity, peer, sizeof (struct GNUNET_PeerIdentity))))
1800 return GNUNET_NO; /* We already know this peer (are connected even!) */
1801 bucket = find_current_bucket (&peer->hashPubKey);
1802
1803 if ((k_buckets[bucket].peers_size < bucket_size) ||
1804 ((bucket == lowest_bucket) && (lowest_bucket > 0)))
1805 return GNUNET_YES;
1806
1807 return GNUNET_NO;
1808}
1809
1810
1811/**
1812 * Task used to remove forwarding entries, either
1813 * after timeout, when full, or on shutdown.
1814 *
1815 * @param cls the entry to remove
1816 * @param tc context, reason, etc.
1817 */
1818static void
1819remove_forward_entry (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1820{
1821 struct DHTRouteSource *source_info = cls;
1822 struct DHTQueryRecord *record;
1823
1824 source_info = GNUNET_CONTAINER_heap_remove_node (source_info->hnode);
1825 record = source_info->record;
1826 GNUNET_CONTAINER_DLL_remove (record->head, record->tail, source_info);
1827
1828 if (record->head == NULL) /* No more entries in DLL */
1829 {
1830 GNUNET_assert (GNUNET_YES ==
1831 GNUNET_CONTAINER_multihashmap_remove (forward_list.hashmap,
1832 &record->key, record));
1833 GNUNET_free (record);
1834 }
1835 if (source_info->find_peers_responded != NULL)
1836 GNUNET_CONTAINER_bloomfilter_free (source_info->find_peers_responded);
1837 GNUNET_free (source_info);
1838}
1839
1840/**
1841 * Main function that handles whether or not to route a result
1842 * message to other peers, or to send to our local client.
1843 *
1844 * @param msg the result message to be routed
1845 * @param msg_ctx context of the message we are routing
1846 *
1847 * @return the number of peers the message was routed to,
1848 * GNUNET_SYSERR on failure
1849 */
1850static int
1851route_result_message (struct GNUNET_MessageHeader *msg,
1852 struct DHT_MessageContext *msg_ctx)
1853{
1854 struct GNUNET_PeerIdentity new_peer;
1855 struct DHTQueryRecord *record;
1856 struct DHTRouteSource *pos;
1857 struct PeerInfo *peer_info;
1858 const struct GNUNET_MessageHeader *hello_msg;
1859
1860#if DEBUG_DHT > 1
1861 unsigned int i;
1862#endif
1863
1864 increment_stats (STAT_RESULTS);
1865 /**
1866 * If a find peer result message is received and contains a valid
1867 * HELLO for another peer, offer it to the transport service.
1868 */
1869 if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_DHT_FIND_PEER_RESULT)
1870 {
1871 if (ntohs (msg->size) <= sizeof (struct GNUNET_MessageHeader))
1872 GNUNET_break_op (0);
1873
1874 hello_msg = &msg[1];
1875 if ((ntohs (hello_msg->type) != GNUNET_MESSAGE_TYPE_HELLO) ||
1876 (GNUNET_SYSERR ==
1877 GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *) hello_msg,
1878 &new_peer)))
1879 {
1880 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1881 "%s:%s Received non-HELLO message type in find peer result message!\n",
1882 my_short_id, "DHT");
1883 GNUNET_break_op (0);
1884 return GNUNET_NO;
1885 }
1886 else /* We have a valid hello, and peer id stored in new_peer */
1887 {
1888 find_peer_context.count++;
1889 increment_stats (STAT_FIND_PEER_REPLY);
1890 if (GNUNET_YES == consider_peer (&new_peer))
1891 {
1892 increment_stats (STAT_HELLOS_PROVIDED);
1893 GNUNET_TRANSPORT_offer_hello (transport_handle, hello_msg, NULL, NULL);
1894 GNUNET_CORE_peer_request_connect (coreAPI, &new_peer, NULL, NULL);
1895 }
1896 }
1897 }
1898
1899 if (malicious_dropper == GNUNET_YES)
1900 record = NULL;
1901 else
1902 record =
1903 GNUNET_CONTAINER_multihashmap_get (forward_list.hashmap, &msg_ctx->key);
1904
1905 if (record == NULL) /* No record of this message! */
1906 {
1907#if DEBUG_DHT
1908 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1909 "`%s:%s': Have no record of response key %s uid %llu\n",
1910 my_short_id, "DHT", GNUNET_h2s (&msg_ctx->key),
1911 msg_ctx->unique_id);
1912#endif
1913#if DEBUG_DHT_ROUTING
1914 if ((debug_routes_extended) && (dhtlog_handle != NULL))
1915 {
1916 dhtlog_handle->insert_route (NULL, msg_ctx->unique_id, DHTLOG_RESULT,
1917 msg_ctx->hop_count, GNUNET_SYSERR,
1918 &my_identity, &msg_ctx->key, &msg_ctx->peer,
1919 NULL);
1920 }
1921#endif
1922 return 0;
1923 }
1924
1925 pos = record->head;
1926 while (pos != NULL)
1927 {
1928#if STRICT_FORWARDING
1929 if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_DHT_FIND_PEER_RESULT) /* If we have already forwarded this peer id, don't do it again! */
1930 {
1931 if (GNUNET_YES ==
1932 GNUNET_CONTAINER_bloomfilter_test (pos->find_peers_responded,
1933 &new_peer.hashPubKey))
1934 {
1935 increment_stats ("# find peer responses NOT forwarded (bloom match)");
1936 pos = pos->next;
1937 continue;
1938 }
1939 else
1940 {
1941 GNUNET_CONTAINER_bloomfilter_add (pos->find_peers_responded,
1942 &new_peer.hashPubKey);
1943 }
1944 }
1945#endif
1946
1947 if (0 == memcmp (&pos->source, &my_identity, sizeof (struct GNUNET_PeerIdentity))) /* Local client (or DHT) initiated request! */
1948 {
1949#if DEBUG_DHT
1950 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1951 "`%s:%s': Sending response key %s uid %llu to client\n",
1952 my_short_id, "DHT", GNUNET_h2s (&msg_ctx->key),
1953 msg_ctx->unique_id);
1954#endif
1955#if DEBUG_DHT_ROUTING
1956 if ((debug_routes_extended) && (dhtlog_handle != NULL))
1957 {
1958 dhtlog_handle->insert_route (NULL, msg_ctx->unique_id, DHTLOG_RESULT,
1959 msg_ctx->hop_count, GNUNET_YES,
1960 &my_identity, &msg_ctx->key, &msg_ctx->peer,
1961 NULL);
1962 }
1963#endif
1964 increment_stats (STAT_RESULTS_TO_CLIENT);
1965 if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_DHT_GET_RESULT)
1966 increment_stats (STAT_GET_REPLY);
1967#if DEBUG_DHT > 1
1968 for (i = 0; i < msg_ctx->path_history_len; i++)
1969 {
1970 char *path_offset;
1971
1972 path_offset =
1973 &msg_ctx->path_history[i * sizeof (struct GNUNET_PeerIdentity)];
1974 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1975 "(before client) Key %s Found peer %d:%s\n",
1976 GNUNET_h2s (&msg_ctx->key), i,
1977 GNUNET_i2s ((struct GNUNET_PeerIdentity *) path_offset));
1978 }
1979#endif
1980 send_reply_to_client (pos->client, msg, msg_ctx);
1981 }
1982 else /* Send to peer */
1983 {
1984 peer_info = find_peer_by_id (&pos->source);
1985 if (peer_info == NULL) /* Didn't find the peer in our routing table, perhaps peer disconnected! */
1986 {
1987 pos = pos->next;
1988 continue;
1989 }
1990#if DEBUG_DHT
1991 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1992 "`%s:%s': Forwarding response key %s uid %llu to peer %s\n",
1993 my_short_id, "DHT", GNUNET_h2s (&msg_ctx->key),
1994 msg_ctx->unique_id, GNUNET_i2s (&peer_info->id));
1995#endif
1996#if DEBUG_DHT_ROUTING
1997 if ((debug_routes_extended) && (dhtlog_handle != NULL))
1998 {
1999 dhtlog_handle->insert_route (NULL, msg_ctx->unique_id, DHTLOG_RESULT,
2000 msg_ctx->hop_count, GNUNET_NO,
2001 &my_identity, &msg_ctx->key,
2002 &msg_ctx->peer, &pos->source);
2003 }
2004#endif
2005 forward_result_message (msg, peer_info, msg_ctx);
2006 /* Try removing forward entries after sending once, only allows ONE response per request */
2007 if (pos->delete_task != GNUNET_SCHEDULER_NO_TASK)
2008 {
2009 GNUNET_SCHEDULER_cancel (pos->delete_task);
2010 pos->delete_task =
2011 GNUNET_SCHEDULER_add_now (&remove_forward_entry, pos);
2012 }
2013 }
2014 pos = pos->next;
2015 }
2016 return 0;
2017}
2018
2019
2020/**
2021 * Iterator for local get request results,
2022 *
2023 * @param cls closure for iterator, a DatacacheGetContext
2024 * @param exp when does this value expire?
2025 * @param key the key this data is stored under
2026 * @param size the size of the data identified by key
2027 * @param data the actual data
2028 * @param type the type of the data
2029 *
2030 * @return GNUNET_OK to continue iteration, anything else
2031 * to stop iteration.
2032 */
2033static int
2034datacache_get_iterator (void *cls, struct GNUNET_TIME_Absolute exp,
2035 const GNUNET_HashCode * key, size_t size,
2036 const char *data, enum GNUNET_BLOCK_Type type)
2037{
2038 struct DHT_MessageContext *msg_ctx = cls;
2039 struct DHT_MessageContext new_msg_ctx;
2040 struct GNUNET_DHT_GetResultMessage *get_result;
2041 enum GNUNET_BLOCK_EvaluationResult eval;
2042 const struct DHTPutEntry *put_entry;
2043 int get_size;
2044 char *path_offset;
2045
2046#if DEBUG_PATH
2047 unsigned int i;
2048#endif
2049
2050#if DEBUG_DHT
2051 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2052 "`%s:%s': Received `%s' response from datacache\n", my_short_id,
2053 "DHT", "GET");
2054#endif
2055
2056 put_entry = (const struct DHTPutEntry *) data;
2057
2058 if (size !=
2059 sizeof (struct DHTPutEntry) + put_entry->data_size +
2060 (put_entry->path_length * sizeof (struct GNUNET_PeerIdentity)))
2061 {
2062 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2063 "Path + data size doesn't add up for data inserted into datacache!\nData size %d, path length %d, expected %d, got %d\n",
2064 put_entry->data_size, put_entry->path_length,
2065 sizeof (struct DHTPutEntry) + put_entry->data_size +
2066 (put_entry->path_length * sizeof (struct GNUNET_PeerIdentity)),
2067 size);
2068 msg_ctx->do_forward = GNUNET_NO;
2069 return GNUNET_OK;
2070 }
2071
2072 eval =
2073 GNUNET_BLOCK_evaluate (block_context, type, key, &msg_ctx->reply_bf,
2074 msg_ctx->reply_bf_mutator, msg_ctx->xquery,
2075 msg_ctx->xquery_size, &put_entry[1],
2076 put_entry->data_size);
2077
2078 switch (eval)
2079 {
2080 case GNUNET_BLOCK_EVALUATION_OK_LAST:
2081 msg_ctx->do_forward = GNUNET_NO;
2082 case GNUNET_BLOCK_EVALUATION_OK_MORE:
2083 memcpy (&new_msg_ctx, msg_ctx, sizeof (struct DHT_MessageContext));
2084 if (GNUNET_DHT_RO_RECORD_ROUTE ==
2085 (msg_ctx->msg_options & GNUNET_DHT_RO_RECORD_ROUTE))
2086 {
2087 new_msg_ctx.msg_options = GNUNET_DHT_RO_RECORD_ROUTE;
2088#if DEBUG_PATH
2089 for (i = 0; i < new_msg_ctx.path_history_len; i++)
2090 {
2091 path_offset =
2092 &new_msg_ctx.path_history[i * sizeof (struct GNUNET_PeerIdentity)];
2093 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2094 "(get_iterator) Key %s Found peer %d:%s\n",
2095 GNUNET_h2s (&msg_ctx->key), i,
2096 GNUNET_i2s ((struct GNUNET_PeerIdentity *) path_offset));
2097 }
2098#endif
2099 }
2100
2101 get_size =
2102 sizeof (struct GNUNET_DHT_GetResultMessage) + put_entry->data_size +
2103 (put_entry->path_length * sizeof (struct GNUNET_PeerIdentity));
2104 get_result = GNUNET_malloc (get_size);
2105 get_result->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_GET_RESULT);
2106 get_result->header.size = htons (get_size);
2107 get_result->expiration = GNUNET_TIME_absolute_hton (exp);
2108 get_result->type = htons (type);
2109 get_result->put_path_length = htons (put_entry->path_length);
2110 path_offset = (char *) &put_entry[1];
2111 path_offset += put_entry->data_size;
2112#if DEBUG_PATH
2113 for (i = 0; i < put_entry->path_length; i++)
2114 {
2115 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2116 "(get_iterator PUT path) Key %s Found peer %d:%s\n",
2117 GNUNET_h2s (&msg_ctx->key), i,
2118 GNUNET_i2s ((struct GNUNET_PeerIdentity *)
2119 &path_offset[i *
2120 sizeof (struct
2121 GNUNET_PeerIdentity)]));
2122 }
2123#endif
2124 /* Copy the actual data and the path_history to the end of the get result */
2125 memcpy (&get_result[1], &put_entry[1],
2126 put_entry->data_size +
2127 (put_entry->path_length * sizeof (struct GNUNET_PeerIdentity)));
2128 new_msg_ctx.peer = my_identity;
2129 new_msg_ctx.bloom = NULL;
2130 new_msg_ctx.hop_count = 0;
2131 new_msg_ctx.importance = DHT_DEFAULT_P2P_IMPORTANCE + 2; /* Make result routing a higher priority */
2132 new_msg_ctx.timeout = DHT_DEFAULT_P2P_TIMEOUT;
2133 increment_stats (STAT_GET_RESPONSE_START);
2134 route_result_message (&get_result->header, &new_msg_ctx);
2135 GNUNET_free (get_result);
2136 break;
2137 case GNUNET_BLOCK_EVALUATION_OK_DUPLICATE:
2138#if DEBUG_DHT
2139 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "`%s:%s': Duplicate block error\n",
2140 my_short_id, "DHT");
2141#endif
2142 break;
2143 case GNUNET_BLOCK_EVALUATION_RESULT_INVALID:
2144#if DEBUG_DHT
2145 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "`%s:%s': Invalid request error\n",
2146 my_short_id, "DHT");
2147#endif
2148 break;
2149 case GNUNET_BLOCK_EVALUATION_REQUEST_VALID:
2150#if DEBUG_DHT
2151 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2152 "`%s:%s': Valid request, no results.\n", my_short_id, "DHT");
2153#endif
2154 GNUNET_break (0);
2155 break;
2156 case GNUNET_BLOCK_EVALUATION_REQUEST_INVALID:
2157 GNUNET_break_op (0);
2158 msg_ctx->do_forward = GNUNET_NO;
2159 break;
2160 case GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED:
2161#if DEBUG_DHT
2162 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2163 "`%s:%s': Unsupported block type (%u) in response!\n",
2164 my_short_id, "DHT", type);
2165#endif
2166 /* msg_ctx->do_forward = GNUNET_NO; // not sure... */
2167 break;
2168 }
2169 return GNUNET_OK;
2170}
2171
2172
2173/**
2174 * Main function that handles whether or not to route a message to other
2175 * peers.
2176 *
2177 * @param msg the message to be routed
2178 * @param msg_ctx the context containing all pertinent information about the message
2179 */
2180static void
2181route_message (const struct GNUNET_MessageHeader *msg,
2182 struct DHT_MessageContext *msg_ctx);
2183
2184
2185/**
2186 * Server handler for all dht get requests, look for data,
2187 * if found, send response either to clients or other peers.
2188 *
2189 * @param msg the actual get message
2190 * @param msg_ctx struct containing pertinent information about the get request
2191 *
2192 * @return number of items found for GET request
2193 */
2194static unsigned int
2195handle_dht_get (const struct GNUNET_MessageHeader *msg,
2196 struct DHT_MessageContext *msg_ctx)
2197{
2198 const struct GNUNET_DHT_GetMessage *get_msg;
2199 uint16_t msize;
2200 uint16_t bf_size;
2201 unsigned int results;
2202 const char *end;
2203 enum GNUNET_BLOCK_Type type;
2204
2205 msize = ntohs (msg->size);
2206 if (msize < sizeof (struct GNUNET_DHT_GetMessage))
2207 {
2208 GNUNET_break (0);
2209 return 0;
2210 }
2211 get_msg = (const struct GNUNET_DHT_GetMessage *) msg;
2212 bf_size = ntohs (get_msg->bf_size);
2213 msg_ctx->xquery_size = ntohs (get_msg->xquery_size);
2214 msg_ctx->reply_bf_mutator = get_msg->bf_mutator;
2215 if (msize !=
2216 sizeof (struct GNUNET_DHT_GetMessage) + bf_size + msg_ctx->xquery_size)
2217 {
2218 GNUNET_break_op (0);
2219 return 0;
2220 }
2221 end = (const char *) &get_msg[1];
2222 if (msg_ctx->xquery_size == 0)
2223 {
2224 msg_ctx->xquery = NULL;
2225 }
2226 else
2227 {
2228 msg_ctx->xquery = (const void *) end;
2229 end += msg_ctx->xquery_size;
2230 }
2231 if (bf_size == 0)
2232 {
2233 msg_ctx->reply_bf = NULL;
2234 }
2235 else
2236 {
2237 msg_ctx->reply_bf =
2238 GNUNET_CONTAINER_bloomfilter_init (end, bf_size,
2239 GNUNET_DHT_GET_BLOOMFILTER_K);
2240 }
2241 type = (enum GNUNET_BLOCK_Type) ntohl (get_msg->type);
2242#if DEBUG_DHT
2243 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2244 "`%s:%s': Received `%s' request, message type %u, key %s, uid %llu\n",
2245 my_short_id, "DHT", "GET", type, GNUNET_h2s (&msg_ctx->key),
2246 msg_ctx->unique_id);
2247#endif
2248 increment_stats (STAT_GETS);
2249 results = 0;
2250#if HAVE_MALICIOUS
2251 if (type == GNUNET_BLOCK_DHT_MALICIOUS_MESSAGE_TYPE)
2252 {
2253 GNUNET_CONTAINER_bloomfilter_free (msg_ctx->reply_bf);
2254 return results;
2255 }
2256#endif
2257 msg_ctx->do_forward = GNUNET_YES;
2258 if (datacache != NULL)
2259 results =
2260 GNUNET_DATACACHE_get (datacache, &msg_ctx->key, type,
2261 &datacache_get_iterator, msg_ctx);
2262#if DEBUG_DHT
2263 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2264 "`%s:%s': Found %d results for `%s' request uid %llu\n",
2265 my_short_id, "DHT", results, "GET", msg_ctx->unique_id);
2266#endif
2267 if (results >= 1)
2268 {
2269#if DEBUG_DHT_ROUTING
2270 if ((debug_routes) && (dhtlog_handle != NULL))
2271 {
2272 dhtlog_handle->insert_query (NULL, msg_ctx->unique_id, DHTLOG_GET,
2273 msg_ctx->hop_count, GNUNET_YES, &my_identity,
2274 &msg_ctx->key);
2275 }
2276
2277 if ((debug_routes_extended) && (dhtlog_handle != NULL))
2278 {
2279 dhtlog_handle->insert_route (NULL, msg_ctx->unique_id, DHTLOG_ROUTE,
2280 msg_ctx->hop_count, GNUNET_YES, &my_identity,
2281 &msg_ctx->key, &msg_ctx->peer, NULL);
2282 }
2283#endif
2284 }
2285 else
2286 {
2287 /* check query valid */
2288 if (GNUNET_BLOCK_EVALUATION_REQUEST_INVALID ==
2289 GNUNET_BLOCK_evaluate (block_context, type, &msg_ctx->key,
2290 &msg_ctx->reply_bf, msg_ctx->reply_bf_mutator,
2291 msg_ctx->xquery, msg_ctx->xquery_size, NULL, 0))
2292 {
2293 GNUNET_break_op (0);
2294 msg_ctx->do_forward = GNUNET_NO;
2295 }
2296 }
2297
2298 if (msg_ctx->hop_count == 0) /* Locally initiated request */
2299 {
2300#if DEBUG_DHT_ROUTING
2301 if ((debug_routes) && (dhtlog_handle != NULL))
2302 {
2303 dhtlog_handle->insert_query (NULL, msg_ctx->unique_id, DHTLOG_GET,
2304 msg_ctx->hop_count, GNUNET_NO, &my_identity,
2305 &msg_ctx->key);
2306 }
2307#endif
2308 }
2309 if (msg_ctx->do_forward == GNUNET_YES)
2310 route_message (msg, msg_ctx);
2311 GNUNET_CONTAINER_bloomfilter_free (msg_ctx->reply_bf);
2312 return results;
2313}
2314
2315
2316static void
2317remove_recent_find_peer (void *cls,
2318 const struct GNUNET_SCHEDULER_TaskContext *tc)
2319{
2320 GNUNET_HashCode *key = cls;
2321
2322 GNUNET_assert (GNUNET_YES ==
2323 GNUNET_CONTAINER_multihashmap_remove
2324 (recent_find_peer_requests, key, NULL));
2325 GNUNET_free (key);
2326}
2327
2328
2329/**
2330 * Server handler for initiating local dht find peer requests
2331 *
2332 * @param find_msg the actual find peer message
2333 * @param msg_ctx struct containing pertinent information about the request
2334 *
2335 */
2336static void
2337handle_dht_find_peer (const struct GNUNET_MessageHeader *find_msg,
2338 struct DHT_MessageContext *msg_ctx)
2339{
2340 struct GNUNET_MessageHeader *find_peer_result;
2341 struct GNUNET_DHT_FindPeerMessage *find_peer_message;
2342 struct DHT_MessageContext *new_msg_ctx;
2343 struct GNUNET_CONTAINER_BloomFilter *incoming_bloom;
2344 size_t hello_size;
2345 size_t tsize;
2346 GNUNET_HashCode *recent_hash;
2347 struct GNUNET_MessageHeader *other_hello;
2348 size_t other_hello_size;
2349 struct GNUNET_PeerIdentity peer_id;
2350
2351 find_peer_message = (struct GNUNET_DHT_FindPeerMessage *) find_msg;
2352 GNUNET_break_op (ntohs (find_msg->size) >=
2353 (sizeof (struct GNUNET_DHT_FindPeerMessage)));
2354 if (ntohs (find_msg->size) < sizeof (struct GNUNET_DHT_FindPeerMessage))
2355 return;
2356 other_hello = NULL;
2357 other_hello_size = 0;
2358 if (ntohs (find_msg->size) > sizeof (struct GNUNET_DHT_FindPeerMessage))
2359 {
2360 other_hello_size =
2361 ntohs (find_msg->size) - sizeof (struct GNUNET_DHT_FindPeerMessage);
2362 other_hello = GNUNET_malloc (other_hello_size);
2363 memcpy (other_hello, &find_peer_message[1], other_hello_size);
2364 if ((GNUNET_HELLO_size ((struct GNUNET_HELLO_Message *) other_hello) == 0)
2365 || (GNUNET_OK !=
2366 GNUNET_HELLO_get_id ((struct GNUNET_HELLO_Message *) other_hello,
2367 &peer_id)))
2368 {
2369 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2370 "Received invalid HELLO message in find peer request!\n");
2371 GNUNET_free (other_hello);
2372 return;
2373 }
2374#if FIND_PEER_WITH_HELLO
2375 if (GNUNET_YES == consider_peer (&peer_id))
2376 {
2377 increment_stats (STAT_HELLOS_PROVIDED);
2378 GNUNET_TRANSPORT_offer_hello (transport_handle, other_hello, NULL, NULL);
2379 GNUNET_CORE_peer_request_connect (coreAPI, &peer_id, NULL, NULL);
2380 route_message (find_msg, msg_ctx);
2381 GNUNET_free (other_hello);
2382 return;
2383 }
2384 else /* We don't want this peer! */
2385 {
2386 route_message (find_msg, msg_ctx);
2387 GNUNET_free (other_hello);
2388 return;
2389 }
2390#endif
2391 }
2392
2393#if DEBUG_DHT
2394 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2395 "`%s:%s': Received `%s' request from client, key %s (msg size %d, we expected %d)\n",
2396 my_short_id, "DHT", "FIND PEER", GNUNET_h2s (&msg_ctx->key),
2397 ntohs (find_msg->size), sizeof (struct GNUNET_MessageHeader));
2398#endif
2399 if (my_hello == NULL)
2400 {
2401#if DEBUG_DHT
2402 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2403 "`%s': Our HELLO is null, can't return.\n", "DHT");
2404#endif
2405 GNUNET_free_non_null (other_hello);
2406 route_message (find_msg, msg_ctx);
2407 return;
2408 }
2409
2410 incoming_bloom =
2411 GNUNET_CONTAINER_bloomfilter_init (find_peer_message->bloomfilter,
2412 DHT_BLOOM_SIZE, DHT_BLOOM_K);
2413 if (GNUNET_YES ==
2414 GNUNET_CONTAINER_bloomfilter_test (incoming_bloom,
2415 &my_identity.hashPubKey))
2416 {
2417 increment_stats (STAT_BLOOM_FIND_PEER);
2418 GNUNET_CONTAINER_bloomfilter_free (incoming_bloom);
2419 GNUNET_free_non_null (other_hello);
2420 route_message (find_msg, msg_ctx);
2421 return; /* We match the bloomfilter, do not send a response to this peer (they likely already know us!) */
2422 }
2423 GNUNET_CONTAINER_bloomfilter_free (incoming_bloom);
2424
2425#if RESTRICT_FIND_PEER
2426
2427 /**
2428 * Ignore any find peer requests from a peer we have seen very recently.
2429 */
2430 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (recent_find_peer_requests, &msg_ctx->key)) /* We have recently responded to a find peer request for this peer! */
2431 {
2432 increment_stats ("# dht find peer requests ignored (recently seen!)");
2433 GNUNET_free_non_null (other_hello);
2434 return;
2435 }
2436
2437 /**
2438 * Use this check to only allow the peer to respond to find peer requests if
2439 * it would be beneficial to have the requesting peer in this peers routing
2440 * table. Can be used to thwart peers flooding the network with find peer
2441 * requests that we don't care about. However, if a new peer is joining
2442 * the network and has no other peers this is a problem (assume all buckets
2443 * full, no one will respond!).
2444 */
2445 memcpy (&peer_id.hashPubKey, &msg_ctx->key, sizeof (GNUNET_HashCode));
2446 if (GNUNET_NO == consider_peer (&peer_id))
2447 {
2448 increment_stats ("# dht find peer requests ignored (do not need!)");
2449 GNUNET_free_non_null (other_hello);
2450 route_message (find_msg, msg_ctx);
2451 return;
2452 }
2453#endif
2454
2455 recent_hash = GNUNET_malloc (sizeof (GNUNET_HashCode));
2456 memcpy (recent_hash, &msg_ctx->key, sizeof (GNUNET_HashCode));
2457 if (GNUNET_SYSERR !=
2458 GNUNET_CONTAINER_multihashmap_put (recent_find_peer_requests,
2459 &msg_ctx->key, NULL,
2460 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
2461 {
2462#if DEBUG_DHT
2463 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2464 "Adding recent remove task for key `%s`!\n",
2465 GNUNET_h2s (&msg_ctx->key));
2466#endif
2467 /* Only add a task if there wasn't one for this key already! */
2468 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
2469 (GNUNET_TIME_UNIT_SECONDS, 30),
2470 &remove_recent_find_peer, recent_hash);
2471 }
2472 else
2473 {
2474 GNUNET_free (recent_hash);
2475#if DEBUG_DHT
2476 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2477 "Received duplicate find peer request too soon!\n");
2478#endif
2479 }
2480
2481 /* Simplistic find_peer functionality, always return our hello */
2482 hello_size = ntohs (my_hello->size);
2483 tsize = hello_size + sizeof (struct GNUNET_MessageHeader);
2484
2485 if (tsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
2486 {
2487 GNUNET_break_op (0);
2488 GNUNET_free_non_null (other_hello);
2489 return;
2490 }
2491
2492 find_peer_result = GNUNET_malloc (tsize);
2493 find_peer_result->type = htons (GNUNET_MESSAGE_TYPE_DHT_FIND_PEER_RESULT);
2494 find_peer_result->size = htons (tsize);
2495 memcpy (&find_peer_result[1], my_hello, hello_size);
2496#if DEBUG_DHT
2497 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2498 "`%s': Sending hello size %d to requesting peer.\n", "DHT",
2499 hello_size);
2500#endif
2501
2502 new_msg_ctx = GNUNET_malloc (sizeof (struct DHT_MessageContext));
2503 memcpy (new_msg_ctx, msg_ctx, sizeof (struct DHT_MessageContext));
2504 new_msg_ctx->peer = my_identity;
2505 new_msg_ctx->bloom =
2506 GNUNET_CONTAINER_bloomfilter_init (NULL, DHT_BLOOM_SIZE, DHT_BLOOM_K);
2507 new_msg_ctx->hop_count = 0;
2508 new_msg_ctx->importance = DHT_DEFAULT_P2P_IMPORTANCE + 2; /* Make find peer requests a higher priority */
2509 new_msg_ctx->timeout = DHT_DEFAULT_P2P_TIMEOUT;
2510 increment_stats (STAT_FIND_PEER_ANSWER);
2511 if (GNUNET_DHT_RO_RECORD_ROUTE ==
2512 (msg_ctx->msg_options & GNUNET_DHT_RO_RECORD_ROUTE))
2513 {
2514 new_msg_ctx->msg_options = GNUNET_DHT_RO_RECORD_ROUTE;
2515 new_msg_ctx->path_history_len = msg_ctx->path_history_len;
2516 /* Assign to previous msg_ctx path history, caller should free after our return */
2517 new_msg_ctx->path_history = msg_ctx->path_history;
2518 }
2519 route_result_message (find_peer_result, new_msg_ctx);
2520 GNUNET_free (new_msg_ctx);
2521#if DEBUG_DHT_ROUTING
2522 if ((debug_routes) && (dhtlog_handle != NULL))
2523 {
2524 dhtlog_handle->insert_query (NULL, msg_ctx->unique_id, DHTLOG_FIND_PEER,
2525 msg_ctx->hop_count, GNUNET_YES, &my_identity,
2526 &msg_ctx->key);
2527 }
2528#endif
2529 GNUNET_free_non_null (other_hello);
2530 GNUNET_free (find_peer_result);
2531 route_message (find_msg, msg_ctx);
2532}
2533
2534
2535/**
2536 * Server handler for initiating local dht put requests
2537 *
2538 * @param msg the actual put message
2539 * @param msg_ctx struct containing pertinent information about the request
2540 */
2541static void
2542handle_dht_put (const struct GNUNET_MessageHeader *msg,
2543 struct DHT_MessageContext *msg_ctx)
2544{
2545 const struct GNUNET_DHT_PutMessage *put_msg;
2546 struct DHTPutEntry *put_entry;
2547 unsigned int put_size;
2548 char *path_offset;
2549 enum GNUNET_BLOCK_Type put_type;
2550 size_t data_size;
2551 int ret;
2552 GNUNET_HashCode key;
2553 struct DHTQueryRecord *record;
2554
2555 GNUNET_assert (ntohs (msg->size) >= sizeof (struct GNUNET_DHT_PutMessage));
2556
2557 put_msg = (const struct GNUNET_DHT_PutMessage *) msg;
2558 put_type = (enum GNUNET_BLOCK_Type) ntohl (put_msg->type);
2559#if HAVE_MALICIOUS
2560 if (put_type == GNUNET_BLOCK_DHT_MALICIOUS_MESSAGE_TYPE)
2561 {
2562#if DEBUG_DHT_ROUTING
2563 if ((debug_routes_extended) && (dhtlog_handle != NULL))
2564 {
2565 /** Log routes that die due to high load! */
2566 dhtlog_handle->insert_route (NULL, msg_ctx->unique_id, DHTLOG_ROUTE,
2567 msg_ctx->hop_count, GNUNET_SYSERR,
2568 &my_identity, &msg_ctx->key, &msg_ctx->peer,
2569 NULL);
2570 }
2571#endif
2572 return;
2573 }
2574#endif
2575 data_size =
2576 ntohs (put_msg->header.size) - sizeof (struct GNUNET_DHT_PutMessage);
2577 ret =
2578 GNUNET_BLOCK_get_key (block_context, put_type, &put_msg[1], data_size,
2579 &key);
2580 if (GNUNET_NO == ret)
2581 {
2582#if DEBUG_DHT_ROUTING
2583 if ((debug_routes_extended) && (dhtlog_handle != NULL))
2584 {
2585 dhtlog_handle->insert_route (NULL, msg_ctx->unique_id, DHTLOG_ROUTE,
2586 msg_ctx->hop_count, GNUNET_SYSERR,
2587 &my_identity, &msg_ctx->key, &msg_ctx->peer,
2588 NULL);
2589 }
2590#endif
2591 /* invalid reply */
2592 GNUNET_break_op (0);
2593 return;
2594 }
2595 if ((GNUNET_YES == ret) &&
2596 (0 != memcmp (&key, &msg_ctx->key, sizeof (GNUNET_HashCode))))
2597 {
2598#if DEBUG_DHT_ROUTING
2599 if ((debug_routes_extended) && (dhtlog_handle != NULL))
2600 {
2601 dhtlog_handle->insert_route (NULL, msg_ctx->unique_id, DHTLOG_ROUTE,
2602 msg_ctx->hop_count, GNUNET_SYSERR,
2603 &my_identity, &msg_ctx->key, &msg_ctx->peer,
2604 NULL);
2605 }
2606#endif
2607 /* invalid wrapper: key mismatch! */
2608 GNUNET_break_op (0);
2609 return;
2610 }
2611 /* ret == GNUNET_SYSERR means that there is no known relationship between
2612 * data and the key, so we cannot check it */
2613#if DEBUG_DHT
2614 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2615 "`%s:%s': Received `%s' request (inserting data!), message type %d, key %s, uid %llu\n",
2616 my_short_id, "DHT", "PUT", put_type, GNUNET_h2s (&msg_ctx->key),
2617 msg_ctx->unique_id);
2618#endif
2619#if DEBUG_DHT_ROUTING
2620 if (msg_ctx->hop_count == 0) /* Locally initiated request */
2621 {
2622 if ((debug_routes) && (dhtlog_handle != NULL))
2623 {
2624 dhtlog_handle->insert_query (NULL, msg_ctx->unique_id, DHTLOG_PUT,
2625 msg_ctx->hop_count, GNUNET_NO, &my_identity,
2626 &msg_ctx->key);
2627 }
2628 }
2629#endif
2630
2631 record = GNUNET_CONTAINER_multihashmap_get(forward_list.hashmap,
2632 &msg_ctx->key);
2633 if (NULL != record)
2634 {
2635 struct DHTRouteSource *pos;
2636 struct GNUNET_DHT_GetResultMessage *get_result;
2637 struct DHT_MessageContext new_msg_ctx;
2638 size_t get_size;
2639
2640 pos = record->head;
2641 while (pos != NULL)
2642 {
2643 /* TODO: do only for local started requests? or also for remote peers? */
2644 /* TODO: include this in statistics? under what? */
2645 /* TODO: reverse order of path_history? */
2646 if (NULL == pos->client)
2647 {
2648 pos = pos->next;
2649 continue;
2650 }
2651
2652 memcpy (&new_msg_ctx, msg_ctx, sizeof (struct DHT_MessageContext));
2653 if (GNUNET_DHT_RO_RECORD_ROUTE ==
2654 (msg_ctx->msg_options & GNUNET_DHT_RO_RECORD_ROUTE))
2655 {
2656 new_msg_ctx.msg_options = GNUNET_DHT_RO_RECORD_ROUTE;
2657#if DEBUG_PATH
2658 for (i = 0; i < new_msg_ctx.path_history_len; i++)
2659 {
2660 path_offset =
2661 &new_msg_ctx.path_history[i * sizeof (struct GNUNET_PeerIdentity)];
2662 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2663 "(put for active get) Key %s Found peer %d:%s\n",
2664 GNUNET_h2s (&msg_ctx->key), i,
2665 GNUNET_i2s ((struct GNUNET_PeerIdentity *) path_offset));
2666 }
2667#endif
2668 }
2669
2670 get_size =
2671 sizeof (struct GNUNET_DHT_GetResultMessage) + data_size +
2672 (msg_ctx->path_history_len * sizeof (struct GNUNET_PeerIdentity));
2673 get_result = GNUNET_malloc (get_size);
2674 get_result->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_GET_RESULT);
2675 get_result->header.size = htons (get_size);
2676 get_result->expiration = put_msg->expiration;
2677 get_result->type = put_msg->type;
2678 get_result->put_path_length = htons (msg_ctx->path_history_len);
2679
2680 /* Copy the actual data and the path_history to the end of the get result */
2681 memcpy (&get_result[1], &put_msg[1], data_size);
2682 path_offset = (char *) &get_result[1];
2683 path_offset += data_size;
2684 memcpy (path_offset, msg_ctx->path_history,
2685 msg_ctx->path_history_len * sizeof (struct GNUNET_PeerIdentity));
2686 new_msg_ctx.peer = my_identity;
2687 new_msg_ctx.bloom = NULL;
2688 new_msg_ctx.hop_count = 0;
2689 /* Make result routing a higher priority */
2690 new_msg_ctx.importance = DHT_DEFAULT_P2P_IMPORTANCE + 2;
2691 new_msg_ctx.timeout = DHT_DEFAULT_P2P_TIMEOUT;
2692 new_msg_ctx.unique_id = pos->uid;
2693 send_reply_to_client(pos->client, &get_result->header, &new_msg_ctx);
2694 GNUNET_free (get_result);
2695 pos = pos->next;
2696 }
2697 }
2698
2699 if (msg_ctx->closest != GNUNET_YES)
2700 {
2701 route_message (msg, msg_ctx);
2702 return;
2703 }
2704
2705#if DEBUG_DHT
2706 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2707 "`%s:%s': Received `%s' request (inserting data!), message type %d, key %s, uid %llu\n",
2708 my_short_id, "DHT", "PUT", put_type, GNUNET_h2s (&msg_ctx->key),
2709 msg_ctx->unique_id);
2710#endif
2711
2712#if DEBUG_DHT_ROUTING
2713 if ((debug_routes_extended) && (dhtlog_handle != NULL))
2714 {
2715 dhtlog_handle->insert_route (NULL, msg_ctx->unique_id, DHTLOG_ROUTE,
2716 msg_ctx->hop_count, GNUNET_YES, &my_identity,
2717 &msg_ctx->key, &msg_ctx->peer, NULL);
2718 }
2719
2720 if ((debug_routes) && (dhtlog_handle != NULL))
2721 {
2722 dhtlog_handle->insert_query (NULL, msg_ctx->unique_id, DHTLOG_PUT,
2723 msg_ctx->hop_count, GNUNET_YES, &my_identity,
2724 &msg_ctx->key);
2725 }
2726#endif
2727
2728 increment_stats (STAT_PUTS_INSERTED);
2729 if (datacache != NULL)
2730 {
2731 /* Put size is actual data size plus struct overhead plus path length (if any) */
2732 put_size =
2733 data_size + sizeof (struct DHTPutEntry) +
2734 (msg_ctx->path_history_len * sizeof (struct GNUNET_PeerIdentity));
2735 put_entry = GNUNET_malloc (put_size);
2736 put_entry->data_size = data_size;
2737 put_entry->path_length = msg_ctx->path_history_len;
2738 /* Copy data to end of put entry */
2739 memcpy (&put_entry[1], &put_msg[1], data_size);
2740 if (msg_ctx->path_history_len > 0)
2741 {
2742 /* Copy path after data */
2743 path_offset = (char *) &put_entry[1];
2744 path_offset += data_size;
2745 memcpy (path_offset, msg_ctx->path_history,
2746 msg_ctx->path_history_len * sizeof (struct GNUNET_PeerIdentity));
2747 }
2748
2749 ret =
2750 GNUNET_DATACACHE_put (datacache, &msg_ctx->key, put_size,
2751 (const char *) put_entry, put_type,
2752 GNUNET_TIME_absolute_ntoh (put_msg->expiration));
2753 GNUNET_free (put_entry);
2754 }
2755 else
2756 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2757 "`%s:%s': %s request received, but have no datacache!\n",
2758 my_short_id, "DHT", "PUT");
2759
2760 if (stop_on_closest == GNUNET_NO)
2761 route_message (msg, msg_ctx);
2762}
2763
2764
2765/**
2766 * To how many peers should we (on average)
2767 * forward the request to obtain the desired
2768 * target_replication count (on average).
2769 *
2770 * returns: target_replication / (est. hops) + (target_replication * hop_count)
2771 * where est. hops is typically 2 * the routing table depth
2772 *
2773 * @param hop_count number of hops the message has traversed
2774 * @param target_replication the number of total paths desired
2775 *
2776 * @return Some number of peers to forward the message to
2777 */
2778static unsigned int
2779get_forward_count (unsigned int hop_count, size_t target_replication)
2780{
2781 uint32_t random_value;
2782 unsigned int forward_count;
2783 float target_value;
2784
2785 /**
2786 * If we are behaving in strict kademlia mode, send multiple initial requests,
2787 * but then only send to 1 or 0 peers based strictly on the number of hops.
2788 */
2789 if (strict_kademlia == GNUNET_YES)
2790 {
2791 if (hop_count == 0)
2792 return kademlia_replication;
2793 if (hop_count < log_of_network_size_estimate * 2.0)
2794 return 1;
2795 return 0;
2796 }
2797
2798 if (hop_count > log_of_network_size_estimate * 2.0)
2799 {
2800 if (GNUNET_YES == paper_forwarding)
2801 {
2802 /* Once we have reached our ideal number of hops, don't stop forwarding! */
2803 return 1;
2804 }
2805#if DEBUG_DHT
2806 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2807 "Hop count too high (est %f, lowest %d), NOT Forwarding request\n",
2808 log_of_network_size_estimate * 2.0, lowest_bucket);
2809#endif
2810 return 0;
2811 }
2812
2813 if (GNUNET_YES == paper_forwarding)
2814 {
2815 /* FIXME: re-run replication trials with this formula */
2816 target_value =
2817 1 + (target_replication - 1.0) / (log_of_network_size_estimate +
2818 ((float) (target_replication - 1.0) *
2819 hop_count));
2820 /* Set forward count to floor of target_value */
2821 forward_count = (unsigned int) target_value;
2822 /* Subtract forward_count (floor) from target_value (yields value between 0 and 1) */
2823 target_value = target_value - forward_count;
2824 random_value =
2825 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, UINT32_MAX);
2826
2827 if (random_value < (target_value * UINT32_MAX))
2828 forward_count += 1;
2829 }
2830 else
2831 {
2832 random_value = 0;
2833 forward_count = 1;
2834 target_value =
2835 target_replication / (log_of_network_size_estimate +
2836 ((float) target_replication * hop_count));
2837 if (target_value > 1)
2838 {
2839 /* Set forward count to floor of target_value */
2840 forward_count = (unsigned int) target_value;
2841 /* Subtract forward_count (floor) from target_value (yields value between 0 and 1) */
2842 target_value = target_value - forward_count;
2843 }
2844 else
2845 random_value =
2846 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, UINT32_MAX);
2847
2848 if (random_value < (target_value * UINT32_MAX))
2849 forward_count += 1;
2850 }
2851
2852 return forward_count;
2853}
2854
2855
2856/**
2857 * Check whether my identity is closer than any known peers.
2858 * If a non-null bloomfilter is given, check if this is the closest
2859 * peer that hasn't already been routed to.
2860 *
2861 * @param target hash code to check closeness to
2862 * @param bloom bloomfilter, exclude these entries from the decision
2863 * @return GNUNET_YES if node location is closest,
2864 * GNUNET_NO otherwise.
2865 */
2866static int
2867am_closest_peer (const GNUNET_HashCode * target,
2868 struct GNUNET_CONTAINER_BloomFilter *bloom)
2869{
2870 int bits;
2871 int other_bits;
2872 int bucket_num;
2873 int count;
2874 struct PeerInfo *pos;
2875 unsigned int my_distance;
2876
2877 if (0 == memcmp (&my_identity.hashPubKey, target, sizeof (GNUNET_HashCode)))
2878 return GNUNET_YES;
2879
2880 bucket_num = find_current_bucket (target);
2881
2882 bits = GNUNET_CRYPTO_hash_matching_bits (&my_identity.hashPubKey, target);
2883 my_distance = distance (&my_identity.hashPubKey, target);
2884 pos = k_buckets[bucket_num].head;
2885 count = 0;
2886 while ((pos != NULL) && (count < bucket_size))
2887 {
2888 if ((bloom != NULL) &&
2889 (GNUNET_YES ==
2890 GNUNET_CONTAINER_bloomfilter_test (bloom, &pos->id.hashPubKey)))
2891 {
2892 pos = pos->next;
2893 continue; /* Skip already checked entries */
2894 }
2895
2896 other_bits = GNUNET_CRYPTO_hash_matching_bits (&pos->id.hashPubKey, target);
2897 if (other_bits > bits)
2898 return GNUNET_NO;
2899 else if (other_bits == bits) /* We match the same number of bits */
2900 {
2901 if (strict_kademlia != GNUNET_YES) /* Return that we at as close as any other peer */
2902 return GNUNET_YES;
2903 if (distance (&pos->id.hashPubKey, target) < my_distance) /* Check all known peers, only return if we are the true closest */
2904 return GNUNET_NO;
2905 }
2906 pos = pos->next;
2907 }
2908
2909 /* No peers closer, we are the closest! */
2910 return GNUNET_YES;
2911}
2912
2913
2914/**
2915 * Select a peer from the routing table that would be a good routing
2916 * destination for sending a message for "target". The resulting peer
2917 * must not be in the set of blocked peers.<p>
2918 *
2919 * Note that we should not ALWAYS select the closest peer to the
2920 * target, peers further away from the target should be chosen with
2921 * exponentially declining probability.
2922 *
2923 * @param target the key we are selecting a peer to route to
2924 * @param bloom a bloomfilter containing entries this request has seen already
2925 * @param hops how many hops has this message traversed thus far
2926 *
2927 * @return Peer to route to, or NULL on error
2928 */
2929static struct PeerInfo *
2930select_peer (const GNUNET_HashCode * target,
2931 struct GNUNET_CONTAINER_BloomFilter *bloom, unsigned int hops)
2932{
2933 unsigned int bc;
2934 unsigned int count;
2935 unsigned int selected;
2936 struct PeerInfo *pos;
2937 unsigned int distance;
2938 unsigned int largest_distance;
2939 struct PeerInfo *chosen;
2940
2941 /** If we are doing kademlia routing (saves some cycles) */
2942 if ((strict_kademlia == GNUNET_YES) || (hops >= log_of_network_size_estimate))
2943 {
2944 /* greedy selection (closest peer that is not in bloomfilter) */
2945 largest_distance = 0;
2946 chosen = NULL;
2947 for (bc = lowest_bucket; bc < MAX_BUCKETS; bc++)
2948 {
2949 pos = k_buckets[bc].head;
2950 count = 0;
2951 while ((pos != NULL) && (count < bucket_size))
2952 {
2953 /* If we are doing strict Kademlia routing, then checking the bloomfilter is basically cheating! */
2954 if (GNUNET_NO ==
2955 GNUNET_CONTAINER_bloomfilter_test (bloom, &pos->id.hashPubKey))
2956 {
2957 distance = inverse_distance (target, &pos->id.hashPubKey);
2958 if (distance > largest_distance)
2959 {
2960 chosen = pos;
2961 largest_distance = distance;
2962 }
2963 }
2964 count++;
2965 pos = pos->next;
2966 }
2967 }
2968 if ((largest_distance > 0) && (chosen != NULL))
2969 {
2970 GNUNET_CONTAINER_bloomfilter_add (bloom, &chosen->id.hashPubKey);
2971 return chosen;
2972 }
2973 return NULL; /* no peer available or we are the closest */
2974 }
2975
2976
2977 /* select "random" peer */
2978 /* count number of peers that are available and not filtered */
2979 count = 0;
2980 for (bc = lowest_bucket; bc < MAX_BUCKETS; bc++)
2981 {
2982 pos = k_buckets[bc].head;
2983 while ((pos != NULL) && (count < bucket_size))
2984 {
2985 if (GNUNET_YES ==
2986 GNUNET_CONTAINER_bloomfilter_test (bloom, &pos->id.hashPubKey))
2987 {
2988 pos = pos->next;
2989 increment_stats ("# peer blocked from selection by Bloom filter");
2990 continue; /* Ignore bloomfiltered peers */
2991 }
2992 count++;
2993 pos = pos->next;
2994 }
2995 }
2996 if (count == 0) /* No peers to select from! */
2997 {
2998 increment_stats ("# failed to select peer");
2999 return NULL;
3000 }
3001 /* Now actually choose a peer */
3002 selected = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, count);
3003 count = 0;
3004 for (bc = lowest_bucket; bc < MAX_BUCKETS; bc++)
3005 {
3006 pos = k_buckets[bc].head;
3007 while ((pos != NULL) && (count < bucket_size))
3008 {
3009 if (GNUNET_YES ==
3010 GNUNET_CONTAINER_bloomfilter_test (bloom, &pos->id.hashPubKey))
3011 {
3012 pos = pos->next;
3013 continue; /* Ignore bloomfiltered peers */
3014 }
3015 if (0 == selected--)
3016 return pos;
3017 pos = pos->next;
3018 }
3019 }
3020 GNUNET_break (0);
3021 return NULL;
3022}
3023
3024
3025/**
3026 * Task used to remove recent entries, either
3027 * after timeout, when full, or on shutdown.
3028 *
3029 * @param cls the entry to remove
3030 * @param tc context, reason, etc.
3031 */
3032static void
3033remove_recent (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3034{
3035 struct RecentRequest *req = cls;
3036 static GNUNET_HashCode hash;
3037
3038 GNUNET_assert (req != NULL);
3039 hash_from_uid (req->uid, &hash);
3040#if HAVE_UID_FOR_TESTING > 1
3041 GNUNET_assert (GNUNET_YES ==
3042 GNUNET_CONTAINER_multihashmap_remove (recent.hashmap, &hash,
3043 req));
3044#endif
3045 GNUNET_CONTAINER_heap_remove_node (req->heap_node);
3046 GNUNET_CONTAINER_bloomfilter_free (req->bloom);
3047 GNUNET_free (req);
3048
3049 /*
3050 * if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) && (0 == GNUNET_CONTAINER_multihashmap_size(recent.hashmap)) && (0 == GNUNET_CONTAINER_heap_get_size(recent.minHeap)))
3051 * {
3052 * GNUNET_CONTAINER_multihashmap_destroy(recent.hashmap);
3053 * GNUNET_CONTAINER_heap_destroy(recent.minHeap);
3054 * }
3055 */
3056}
3057
3058/**
3059 * Remember this routing request so that if a reply is
3060 * received we can either forward it to the correct peer
3061 * or return the result locally.
3062 *
3063 * @param msg_ctx Context of the route request
3064 *
3065 * @return GNUNET_YES if this response was cached, GNUNET_NO if not
3066 */
3067static int
3068cache_response (struct DHT_MessageContext *msg_ctx)
3069{
3070 struct DHTQueryRecord *record;
3071 struct DHTRouteSource *source_info;
3072 struct DHTRouteSource *pos;
3073 struct GNUNET_TIME_Absolute now;
3074 unsigned int current_size;
3075
3076 current_size = GNUNET_CONTAINER_multihashmap_size (forward_list.hashmap);
3077
3078#if DELETE_WHEN_FULL
3079 while (current_size >= MAX_OUTSTANDING_FORWARDS)
3080 {
3081 source_info = GNUNET_CONTAINER_heap_remove_root (forward_list.minHeap);
3082 GNUNET_assert (source_info != NULL);
3083 record = source_info->record;
3084 GNUNET_CONTAINER_DLL_remove (record->head, record->tail, source_info);
3085 if (record->head == NULL) /* No more entries in DLL */
3086 {
3087 GNUNET_assert (GNUNET_YES ==
3088 GNUNET_CONTAINER_multihashmap_remove (forward_list.hashmap,
3089 &record->key,
3090 record));
3091 GNUNET_free (record);
3092 }
3093 if (source_info->delete_task != GNUNET_SCHEDULER_NO_TASK)
3094 {
3095 GNUNET_SCHEDULER_cancel (source_info->delete_task);
3096 source_info->delete_task = GNUNET_SCHEDULER_NO_TASK;
3097 }
3098 if (source_info->find_peers_responded != NULL)
3099 GNUNET_CONTAINER_bloomfilter_free (source_info->find_peers_responded);
3100 GNUNET_free (source_info);
3101 current_size = GNUNET_CONTAINER_multihashmap_size (forward_list.hashmap);
3102 }
3103#endif
3104 /** Non-local request and have too many outstanding forwards, discard! */
3105 if ((current_size >= MAX_OUTSTANDING_FORWARDS) && (msg_ctx->client == NULL))
3106 return GNUNET_NO;
3107
3108 now = GNUNET_TIME_absolute_get ();
3109 record =
3110 GNUNET_CONTAINER_multihashmap_get (forward_list.hashmap, &msg_ctx->key);
3111 if (record != NULL) /* Already know this request! */
3112 {
3113 pos = record->head;
3114 while (pos != NULL)
3115 {
3116 if (0 ==
3117 memcmp (&msg_ctx->peer, &pos->source,
3118 sizeof (struct GNUNET_PeerIdentity)))
3119 break; /* Already have this peer in reply list! */
3120 pos = pos->next;
3121 }
3122 if ((pos != NULL) && (pos->client == msg_ctx->client)) /* Seen this already */
3123 {
3124 GNUNET_CONTAINER_heap_update_cost (forward_list.minHeap, pos->hnode,
3125 now.abs_value);
3126 return GNUNET_NO;
3127 }
3128 }
3129 else
3130 {
3131 record = GNUNET_malloc (sizeof (struct DHTQueryRecord));
3132 GNUNET_assert (GNUNET_OK ==
3133 GNUNET_CONTAINER_multihashmap_put (forward_list.hashmap,
3134 &msg_ctx->key, record,
3135 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
3136 memcpy (&record->key, &msg_ctx->key, sizeof (GNUNET_HashCode));
3137 }
3138
3139 source_info = GNUNET_malloc (sizeof (struct DHTRouteSource));
3140 source_info->record = record;
3141 source_info->delete_task =
3142 GNUNET_SCHEDULER_add_delayed (DHT_FORWARD_TIMEOUT, &remove_forward_entry,
3143 source_info);
3144 source_info->find_peers_responded =
3145 GNUNET_CONTAINER_bloomfilter_init (NULL, DHT_BLOOM_SIZE, DHT_BLOOM_K);
3146 source_info->source = msg_ctx->peer;
3147 GNUNET_CONTAINER_DLL_insert_after (record->head, record->tail, record->tail,
3148 source_info);
3149 if (msg_ctx->client != NULL) /* For local request, set timeout so high it effectively never gets pushed out */
3150 {
3151 source_info->client = msg_ctx->client;
3152 now = GNUNET_TIME_absolute_get_forever ();
3153 }
3154 source_info->hnode =
3155 GNUNET_CONTAINER_heap_insert (forward_list.minHeap, source_info,
3156 now.abs_value);
3157 source_info->uid = msg_ctx->unique_id;
3158#if DEBUG_DHT > 1
3159 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3160 "`%s:%s': Created new forward source info for %s uid %llu\n",
3161 my_short_id, "DHT", GNUNET_h2s (&msg_ctx->key),
3162 msg_ctx->unique_id);
3163#endif
3164 return GNUNET_YES;
3165}
3166
3167
3168/**
3169 * Main function that handles whether or not to route a message to other
3170 * peers.
3171 *
3172 * @param msg the message to be routed
3173 * @param msg_ctx the context containing all pertinent information about the message
3174 */
3175static void
3176route_message (const struct GNUNET_MessageHeader *msg,
3177 struct DHT_MessageContext *msg_ctx)
3178{
3179 int i;
3180 struct PeerInfo *selected;
3181
3182#if DEBUG_DHT_ROUTING > 1
3183 struct PeerInfo *nearest;
3184#endif
3185 unsigned int target_forward_count;
3186 unsigned int forward_count;
3187 struct RecentRequest *recent_req;
3188#if HAVE_UID_FOR_TESTING > 1
3189 GNUNET_HashCode unique_hash;
3190#endif
3191 char *stat_forward_count;
3192 char *temp_stat_str;
3193
3194#if DEBUG_DHT_ROUTING
3195 int ret;
3196#endif
3197
3198 if (malicious_dropper == GNUNET_YES)
3199 {
3200#if DEBUG_DHT_ROUTING
3201 if ((debug_routes_extended) && (dhtlog_handle != NULL))
3202 {
3203 dhtlog_handle->insert_route (NULL, msg_ctx->unique_id, DHTLOG_ROUTE,
3204 msg_ctx->hop_count, GNUNET_SYSERR,
3205 &my_identity, &msg_ctx->key, &msg_ctx->peer,
3206 NULL);
3207 }
3208#endif
3209 if (msg_ctx->bloom != NULL)
3210 {
3211 GNUNET_CONTAINER_bloomfilter_free (msg_ctx->bloom);
3212 msg_ctx->bloom = NULL;
3213 }
3214 return;
3215 }
3216
3217 increment_stats (STAT_ROUTES);
3218 target_forward_count =
3219 get_forward_count (msg_ctx->hop_count, msg_ctx->replication);
3220 GNUNET_asprintf (&stat_forward_count, "# forward counts of %d",
3221 target_forward_count);
3222 increment_stats (stat_forward_count);
3223 GNUNET_free (stat_forward_count);
3224 if (msg_ctx->bloom == NULL)
3225 msg_ctx->bloom =
3226 GNUNET_CONTAINER_bloomfilter_init (NULL, DHT_BLOOM_SIZE, DHT_BLOOM_K);
3227
3228 if ((stop_on_closest == GNUNET_YES) && (msg_ctx->closest == GNUNET_YES) &&
3229 (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_DHT_PUT))
3230 target_forward_count = 0;
3231
3232 /**
3233 * NOTICE: In Kademlia, a find peer request goes no further if the peer doesn't return
3234 * any closer peers (which is being checked for below). Since we are doing recursive
3235 * routing we have no choice but to stop forwarding in this case. This means that at
3236 * any given step the request may NOT be forwarded to alpha peers (because routes will
3237 * stop and the parallel route will not be aware of it). Of course, assuming that we
3238 * have fulfilled the Kademlia requirements for routing table fullness this will never
3239 * ever ever be a problem.
3240 *
3241 * However, is this fair?
3242 *
3243 * Since we use these requests to build our routing tables (and we build them in the
3244 * testing driver) we will ignore this restriction for FIND_PEER messages so that
3245 * routing tables still get constructed.
3246 */
3247 if ((GNUNET_YES == strict_kademlia) && (msg_ctx->closest == GNUNET_YES) &&
3248 (msg_ctx->hop_count > 0) &&
3249 (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_DHT_FIND_PEER))
3250 target_forward_count = 0;
3251
3252#if HAVE_UID_FOR_TESTING > 1
3253 /* BUG HERE: recent uses unique_id! So if all unique-IDs are 0, we get
3254 easily into trouble!!! Also, this should not even be necessary... */
3255 hash_from_uid (msg_ctx->unique_id, &unique_hash);
3256 if (GNUNET_YES ==
3257 GNUNET_CONTAINER_multihashmap_contains (recent.hashmap, &unique_hash))
3258 {
3259 recent_req =
3260 GNUNET_CONTAINER_multihashmap_get (recent.hashmap, &unique_hash);
3261 GNUNET_assert (recent_req != NULL);
3262 if (0 != memcmp (&recent_req->key, &msg_ctx->key, sizeof (GNUNET_HashCode)))
3263 increment_stats (STAT_DUPLICATE_UID);
3264 else
3265 {
3266 increment_stats (STAT_RECENT_SEEN);
3267 GNUNET_CONTAINER_bloomfilter_or2 (msg_ctx->bloom, recent_req->bloom,
3268 DHT_BLOOM_SIZE);
3269 }
3270 }
3271 else
3272#endif
3273 {
3274 recent_req = GNUNET_malloc (sizeof (struct RecentRequest));
3275 recent_req->uid = msg_ctx->unique_id;
3276 memcpy (&recent_req->key, &msg_ctx->key, sizeof (GNUNET_HashCode));
3277 recent_req->remove_task =
3278 GNUNET_SCHEDULER_add_delayed (DEFAULT_RECENT_REMOVAL, &remove_recent,
3279 recent_req);
3280 recent_req->heap_node =
3281 GNUNET_CONTAINER_heap_insert (recent.minHeap, recent_req,
3282 GNUNET_TIME_absolute_get ().abs_value);
3283 recent_req->bloom =
3284 GNUNET_CONTAINER_bloomfilter_init (NULL, DHT_BLOOM_SIZE, DHT_BLOOM_K);
3285#if HAVE_UID_FOR_TESTING > 1
3286 GNUNET_CONTAINER_multihashmap_put (recent.hashmap, &unique_hash, recent_req,
3287 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
3288#endif
3289 }
3290
3291 if (GNUNET_CONTAINER_heap_get_size (recent.minHeap) > DHT_MAX_RECENT)
3292 {
3293 recent_req = GNUNET_CONTAINER_heap_peek (recent.minHeap);
3294 GNUNET_assert (recent_req != NULL);
3295 GNUNET_SCHEDULER_cancel (recent_req->remove_task);
3296 recent_req->remove_task =
3297 GNUNET_SCHEDULER_add_now (&remove_recent, recent_req);
3298 }
3299
3300 forward_count = 0;
3301 for (i = 0; i < target_forward_count; i++)
3302 {
3303 selected = select_peer (&msg_ctx->key, msg_ctx->bloom, msg_ctx->hop_count);
3304 if (selected == NULL)
3305 break;
3306 forward_count++;
3307 if (GNUNET_CRYPTO_hash_matching_bits
3308 (&selected->id.hashPubKey,
3309 &msg_ctx->key) >=
3310 GNUNET_CRYPTO_hash_matching_bits (&my_identity.hashPubKey,
3311 &msg_ctx->key))
3312 GNUNET_asprintf (&temp_stat_str,
3313 "# requests routed to close(r) peer hop %u",
3314 msg_ctx->hop_count);
3315 else
3316 GNUNET_asprintf (&temp_stat_str,
3317 "# requests routed to less close peer hop %u",
3318 msg_ctx->hop_count);
3319 if (temp_stat_str != NULL)
3320 {
3321 increment_stats (temp_stat_str);
3322 GNUNET_free (temp_stat_str);
3323 }
3324 GNUNET_CONTAINER_bloomfilter_add (msg_ctx->bloom,
3325 &selected->id.hashPubKey);
3326#if DEBUG_DHT_ROUTING > 1
3327 nearest = find_closest_peer (&msg_ctx->key);
3328 nearest_buf = GNUNET_strdup (GNUNET_i2s (&nearest->id));
3329 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3330 "`%s:%s': Forwarding request key %s uid %llu to peer %s (closest %s, bits %d, distance %u)\n",
3331 my_short_id, "DHT", GNUNET_h2s (&msg_ctx->key),
3332 msg_ctx->unique_id, GNUNET_i2s (&selected->id), nearest_buf,
3333 GNUNET_CRYPTO_hash_matching_bits (&nearest->id.hashPubKey,
3334 msg_ctx->key),
3335 distance (&nearest->id.hashPubKey, msg_ctx->key));
3336 GNUNET_free (nearest_buf);
3337#endif
3338#if DEBUG_DHT_ROUTING
3339 if ((debug_routes_extended) && (dhtlog_handle != NULL))
3340 {
3341 dhtlog_handle->insert_route (NULL, msg_ctx->unique_id, DHTLOG_ROUTE,
3342 msg_ctx->hop_count, GNUNET_NO,
3343 &my_identity, &msg_ctx->key, &msg_ctx->peer,
3344 &selected->id);
3345 }
3346#endif
3347 forward_message (msg, selected, msg_ctx);
3348 }
3349
3350 if (msg_ctx->bloom != NULL)
3351 {
3352 GNUNET_CONTAINER_bloomfilter_or2 (recent_req->bloom, msg_ctx->bloom,
3353 DHT_BLOOM_SIZE);
3354 GNUNET_CONTAINER_bloomfilter_free (msg_ctx->bloom);
3355 msg_ctx->bloom = NULL;
3356 }
3357
3358#if DEBUG_DHT_ROUTING
3359 if (forward_count == 0)
3360 ret = GNUNET_SYSERR;
3361 else
3362 ret = GNUNET_NO;
3363
3364 if ((debug_routes_extended) && (dhtlog_handle != NULL))
3365 {
3366 dhtlog_handle->insert_route (NULL, msg_ctx->unique_id, DHTLOG_ROUTE,
3367 msg_ctx->hop_count, ret, &my_identity,
3368 &msg_ctx->key, &msg_ctx->peer, NULL);
3369 }
3370#endif
3371}
3372
3373
3374/**
3375 * Main function that handles whether or not to route a message to other
3376 * peers.
3377 *
3378 * @param msg the message to be routed
3379 * @param msg_ctx the context containing all pertinent information about the message
3380 */
3381static void
3382demultiplex_message (const struct GNUNET_MessageHeader *msg,
3383 struct DHT_MessageContext *msg_ctx)
3384{
3385 /* FIXME: Should we use closest excluding those we won't route to (the bloomfilter problem)? */
3386 msg_ctx->closest = am_closest_peer (&msg_ctx->key, msg_ctx->bloom);
3387
3388 switch (ntohs (msg->type))
3389 {
3390 case GNUNET_MESSAGE_TYPE_DHT_GET: /* Add to hashmap of requests seen, search for data (always) */
3391 cache_response (msg_ctx);
3392 handle_dht_get (msg, msg_ctx);
3393 break;
3394 case GNUNET_MESSAGE_TYPE_DHT_PUT: /* Check if closest, if so insert data. */
3395 increment_stats (STAT_PUTS);
3396 handle_dht_put (msg, msg_ctx);
3397 break;
3398 case GNUNET_MESSAGE_TYPE_DHT_FIND_PEER: /* Check if closest and not started by us, check options, add to requests seen */
3399 increment_stats (STAT_FIND_PEER);
3400 if (((msg_ctx->hop_count > 0) &&
3401 (0 !=
3402 memcmp (&msg_ctx->peer, &my_identity,
3403 sizeof (struct GNUNET_PeerIdentity)))) ||
3404 (msg_ctx->client != NULL))
3405 {
3406 cache_response (msg_ctx);
3407 if ((msg_ctx->closest == GNUNET_YES) ||
3408 (msg_ctx->msg_options == GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE))
3409 handle_dht_find_peer (msg, msg_ctx);
3410 }
3411 else
3412 route_message (msg, msg_ctx);
3413#if DEBUG_DHT_ROUTING
3414 if (msg_ctx->hop_count == 0) /* Locally initiated request */
3415 {
3416 if ((debug_routes) && (dhtlog_handle != NULL))
3417 {
3418 dhtlog_handle->insert_dhtkey (NULL, &msg_ctx->key);
3419 dhtlog_handle->insert_query (NULL, msg_ctx->unique_id, DHTLOG_FIND_PEER,
3420 msg_ctx->hop_count, GNUNET_NO,
3421 &my_identity, &msg_ctx->key);
3422 }
3423 }
3424#endif
3425 break;
3426 default:
3427 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3428 "`%s': Message type (%d) not handled, forwarding anyway!\n",
3429 "DHT", ntohs (msg->type));
3430 route_message (msg, msg_ctx);
3431 }
3432}
3433
3434
3435/**
3436 * Iterator over hash map entries.
3437 *
3438 * @param cls client to search for in source routes
3439 * @param key current key code (ignored)
3440 * @param value value in the hash map, a DHTQueryRecord
3441 * @return GNUNET_YES if we should continue to
3442 * iterate,
3443 * GNUNET_NO if not.
3444 */
3445static int
3446find_client_records (void *cls, const GNUNET_HashCode * key, void *value)
3447{
3448 struct ClientList *client = cls;
3449 struct DHTQueryRecord *record = value;
3450 struct DHTRouteSource *pos;
3451
3452 pos = record->head;
3453 while (pos != NULL)
3454 {
3455 if (pos->client == client)
3456 break;
3457 pos = pos->next;
3458 }
3459 if (pos != NULL)
3460 {
3461 GNUNET_CONTAINER_DLL_remove (record->head, record->tail, pos);
3462 GNUNET_CONTAINER_heap_remove_node (pos->hnode);
3463 if (pos->delete_task != GNUNET_SCHEDULER_NO_TASK)
3464 {
3465 GNUNET_SCHEDULER_cancel (pos->delete_task);
3466 pos->delete_task = GNUNET_SCHEDULER_NO_TASK;
3467 }
3468 if (pos->find_peers_responded != NULL)
3469 GNUNET_CONTAINER_bloomfilter_free (pos->find_peers_responded);
3470 GNUNET_free (pos);
3471 }
3472 if (record->head == NULL) /* No more entries in DLL */
3473 {
3474 GNUNET_assert (GNUNET_YES ==
3475 GNUNET_CONTAINER_multihashmap_remove (forward_list.hashmap,
3476 &record->key, record));
3477 GNUNET_free (record);
3478 }
3479 return GNUNET_YES;
3480}
3481
3482/**
3483 * Functions with this signature are called whenever a client
3484 * is disconnected on the network level.
3485 *
3486 * @param cls closure (NULL for dht)
3487 * @param client identification of the client; NULL
3488 * for the last call when the server is destroyed
3489 */
3490static void
3491handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
3492{
3493 struct ClientList *pos = client_list;
3494 struct ClientList *prev;
3495 struct ClientList *found;
3496 struct PendingMessage *reply;
3497
3498 prev = NULL;
3499 found = NULL;
3500 while (pos != NULL)
3501 {
3502 if (pos->client_handle == client)
3503 {
3504 if (prev != NULL)
3505 prev->next = pos->next;
3506 else
3507 client_list = pos->next;
3508 found = pos;
3509 break;
3510 }
3511 prev = pos;
3512 pos = pos->next;
3513 }
3514
3515 if (found != NULL)
3516 {
3517 if (found->transmit_handle != NULL)
3518 GNUNET_CONNECTION_notify_transmit_ready_cancel (found->transmit_handle);
3519
3520 while (NULL != (reply = found->pending_head))
3521 {
3522 GNUNET_CONTAINER_DLL_remove (found->pending_head, found->pending_tail,
3523 reply);
3524 GNUNET_free (reply);
3525 }
3526 GNUNET_CONTAINER_multihashmap_iterate (forward_list.hashmap,
3527 &find_client_records, found);
3528 GNUNET_free (found);
3529 }
3530}
3531
3532/**
3533 * Find a client if it exists, add it otherwise.
3534 *
3535 * @param client the server handle to the client
3536 *
3537 * @return the client if found, a new client otherwise
3538 */
3539static struct ClientList *
3540find_active_client (struct GNUNET_SERVER_Client *client)
3541{
3542 struct ClientList *pos = client_list;
3543 struct ClientList *ret;
3544
3545 while (pos != NULL)
3546 {
3547 if (pos->client_handle == client)
3548 return pos;
3549 pos = pos->next;
3550 }
3551
3552 ret = GNUNET_malloc (sizeof (struct ClientList));
3553 ret->client_handle = client;
3554 ret->next = client_list;
3555 client_list = ret;
3556
3557 return ret;
3558}
3559
3560#if HAVE_MALICIOUS
3561/**
3562 * Task to send a malicious put message across the network.
3563 *
3564 * @param cls closure for this task
3565 * @param tc the context under which the task is running
3566 */
3567static void
3568malicious_put_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3569{
3570 static struct GNUNET_DHT_PutMessage put_message;
3571 static struct DHT_MessageContext msg_ctx;
3572 static GNUNET_HashCode key;
3573 uint32_t random_key;
3574
3575 if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
3576 return;
3577 put_message.header.size = htons (sizeof (struct GNUNET_DHT_PutMessage));
3578 put_message.header.type = htons (GNUNET_MESSAGE_TYPE_DHT_PUT);
3579 put_message.type = htonl (GNUNET_BLOCK_DHT_MALICIOUS_MESSAGE_TYPE);
3580 put_message.expiration =
3581 GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_forever ());
3582 memset (&msg_ctx, 0, sizeof (struct DHT_MessageContext));
3583 random_key =
3584 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX);
3585 GNUNET_CRYPTO_hash (&random_key, sizeof (uint32_t), &key);
3586 memcpy (&msg_ctx.key, &key, sizeof (GNUNET_HashCode));
3587 msg_ctx.unique_id =
3588 GNUNET_ntohll (GNUNET_CRYPTO_random_u64
3589 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX));
3590 msg_ctx.replication = ntohl (DHT_DEFAULT_FIND_PEER_REPLICATION);
3591 msg_ctx.msg_options = ntohl (0);
3592 msg_ctx.network_size = log_of_network_size_estimate;
3593 msg_ctx.peer = my_identity;
3594 msg_ctx.importance = DHT_DEFAULT_P2P_IMPORTANCE;
3595 msg_ctx.timeout = DHT_DEFAULT_P2P_TIMEOUT;
3596#if DEBUG_DHT_ROUTING
3597 if (dhtlog_handle != NULL)
3598 dhtlog_handle->insert_dhtkey (NULL, &key);
3599#endif
3600 increment_stats (STAT_PUT_START);
3601 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3602 "%s:%s Sending malicious PUT message with hash %s\n", my_short_id,
3603 "DHT", GNUNET_h2s (&key));
3604 demultiplex_message (&put_message.header, &msg_ctx);
3605 GNUNET_SCHEDULER_add_delayed (malicious_put_frequency, &malicious_put_task,
3606 NULL);
3607}
3608
3609
3610/**
3611 * Task to send a malicious put message across the network.
3612 *
3613 * @param cls closure for this task
3614 * @param tc the context under which the task is running
3615 */
3616static void
3617malicious_get_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3618{
3619 static struct GNUNET_DHT_GetMessage get_message;
3620 struct DHT_MessageContext msg_ctx;
3621 static GNUNET_HashCode key;
3622 uint32_t random_key;
3623
3624 if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
3625 return;
3626
3627 get_message.header.size = htons (sizeof (struct GNUNET_DHT_GetMessage));
3628 get_message.header.type = htons (GNUNET_MESSAGE_TYPE_DHT_GET);
3629 get_message.type = htonl (GNUNET_BLOCK_DHT_MALICIOUS_MESSAGE_TYPE);
3630 memset (&msg_ctx, 0, sizeof (struct DHT_MessageContext));
3631 random_key =
3632 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX);
3633 GNUNET_CRYPTO_hash (&random_key, sizeof (uint32_t), &key);
3634 memcpy (&msg_ctx.key, &key, sizeof (GNUNET_HashCode));
3635 msg_ctx.unique_id =
3636 GNUNET_ntohll (GNUNET_CRYPTO_random_u64
3637 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX));
3638 msg_ctx.replication = ntohl (DHT_DEFAULT_FIND_PEER_REPLICATION);
3639 msg_ctx.msg_options = ntohl (0);
3640 msg_ctx.network_size = log_of_network_size_estimate;
3641 msg_ctx.peer = my_identity;
3642 msg_ctx.importance = DHT_DEFAULT_P2P_IMPORTANCE;
3643 msg_ctx.timeout = DHT_DEFAULT_P2P_TIMEOUT;
3644#if DEBUG_DHT_ROUTING
3645 if (dhtlog_handle != NULL)
3646 dhtlog_handle->insert_dhtkey (NULL, &key);
3647#endif
3648 increment_stats (STAT_GET_START);
3649 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3650 "%s:%s Sending malicious GET message with hash %s\n", my_short_id,
3651 "DHT", GNUNET_h2s (&key));
3652 demultiplex_message (&get_message.header, &msg_ctx);
3653 GNUNET_SCHEDULER_add_delayed (malicious_get_frequency, &malicious_get_task,
3654 NULL);
3655}
3656#endif
3657
3658
3659/**
3660 * Iterator over hash map entries.
3661 *
3662 * @param cls closure
3663 * @param key current key code
3664 * @param value value in the hash map
3665 * @return GNUNET_YES if we should continue to
3666 * iterate,
3667 * GNUNET_NO if not.
3668 */
3669static int
3670add_known_to_bloom (void *cls, const GNUNET_HashCode * key, void *value)
3671{
3672 struct GNUNET_CONTAINER_BloomFilter *bloom = cls;
3673
3674 GNUNET_CONTAINER_bloomfilter_add (bloom, key);
3675 return GNUNET_YES;
3676}
3677
3678/**
3679 * Task to send a find peer message for our own peer identifier
3680 * so that we can find the closest peers in the network to ourselves
3681 * and attempt to connect to them.
3682 *
3683 * @param cls closure for this task
3684 * @param tc the context under which the task is running
3685 */
3686static void
3687send_find_peer_message (void *cls,
3688 const struct GNUNET_SCHEDULER_TaskContext *tc)
3689{
3690 struct GNUNET_DHT_FindPeerMessage *find_peer_msg;
3691 struct DHT_MessageContext msg_ctx;
3692 struct GNUNET_TIME_Relative next_send_time;
3693 struct GNUNET_CONTAINER_BloomFilter *temp_bloom;
3694
3695 if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
3696 return;
3697
3698 if ((newly_found_peers > bucket_size) && (GNUNET_YES == do_find_peer)) /* If we are finding peers already, no need to send out our request right now! */
3699 {
3700 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3701 "Have %d newly found peers since last find peer message sent!\n",
3702 newly_found_peers);
3703 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
3704 &send_find_peer_message, NULL);
3705 newly_found_peers = 0;
3706 return;
3707 }
3708
3709 increment_stats (STAT_FIND_PEER_START);
3710#if FIND_PEER_WITH_HELLO
3711 find_peer_msg =
3712 GNUNET_malloc (sizeof (struct GNUNET_DHT_FindPeerMessage) +
3713 GNUNET_HELLO_size ((struct GNUNET_HELLO_Message *)
3714 my_hello));
3715 find_peer_msg->header.size =
3716 htons (sizeof (struct GNUNET_DHT_FindPeerMessage) +
3717 GNUNET_HELLO_size ((struct GNUNET_HELLO_Message *) my_hello));
3718 memcpy (&find_peer_msg[1], my_hello,
3719 GNUNET_HELLO_size ((struct GNUNET_HELLO_Message *) my_hello));
3720#else
3721 find_peer_msg = GNUNET_malloc (sizeof (struct GNUNET_DHT_FindPeerMessage));
3722 find_peer_msg->header.size =
3723 htons (sizeof (struct GNUNET_DHT_FindPeerMessage));
3724#endif
3725 find_peer_msg->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_FIND_PEER);
3726 temp_bloom =
3727 GNUNET_CONTAINER_bloomfilter_init (NULL, DHT_BLOOM_SIZE, DHT_BLOOM_K);
3728 GNUNET_CONTAINER_multihashmap_iterate (all_known_peers, &add_known_to_bloom,
3729 temp_bloom);
3730 GNUNET_assert (GNUNET_OK ==
3731 GNUNET_CONTAINER_bloomfilter_get_raw_data (temp_bloom,
3732 find_peer_msg->
3733 bloomfilter,
3734 DHT_BLOOM_SIZE));
3735 GNUNET_CONTAINER_bloomfilter_free (temp_bloom);
3736 memset (&msg_ctx, 0, sizeof (struct DHT_MessageContext));
3737 memcpy (&msg_ctx.key, &my_identity.hashPubKey, sizeof (GNUNET_HashCode));
3738 msg_ctx.unique_id =
3739 GNUNET_ntohll (GNUNET_CRYPTO_random_u64
3740 (GNUNET_CRYPTO_QUALITY_STRONG, UINT64_MAX));
3741 msg_ctx.replication = DHT_DEFAULT_FIND_PEER_REPLICATION;
3742 msg_ctx.msg_options = GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE;
3743 msg_ctx.network_size = log_of_network_size_estimate;
3744 msg_ctx.peer = my_identity;
3745 msg_ctx.importance = DHT_DEFAULT_FIND_PEER_IMPORTANCE;
3746 msg_ctx.timeout = DHT_DEFAULT_FIND_PEER_TIMEOUT;
3747
3748 demultiplex_message (&find_peer_msg->header, &msg_ctx);
3749 GNUNET_free (find_peer_msg);
3750 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3751 "`%s:%s': Sent `%s' request to some (?) peers\n", my_short_id,
3752 "DHT", "FIND PEER");
3753 if (newly_found_peers < bucket_size)
3754 {
3755 next_send_time.rel_value =
3756 (DHT_MAXIMUM_FIND_PEER_INTERVAL.rel_value / 2) +
3757 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_STRONG,
3758 DHT_MAXIMUM_FIND_PEER_INTERVAL.rel_value / 2);
3759 }
3760 else
3761 {
3762 next_send_time.rel_value =
3763 DHT_MINIMUM_FIND_PEER_INTERVAL.rel_value +
3764 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_STRONG,
3765 DHT_MAXIMUM_FIND_PEER_INTERVAL.rel_value -
3766 DHT_MINIMUM_FIND_PEER_INTERVAL.rel_value);
3767 }
3768
3769 GNUNET_assert (next_send_time.rel_value != 0);
3770 find_peer_context.count = 0;
3771 newly_found_peers = 0;
3772 find_peer_context.start = GNUNET_TIME_absolute_get ();
3773 if (GNUNET_YES == do_find_peer)
3774 {
3775 GNUNET_SCHEDULER_add_delayed (next_send_time, &send_find_peer_message,
3776 NULL);
3777 }
3778}
3779
3780/**
3781 * Handler for any generic DHT messages, calls the appropriate handler
3782 * depending on message type, sends confirmation if responses aren't otherwise
3783 * expected.
3784 *
3785 * @param cls closure for the service
3786 * @param client the client we received this message from
3787 * @param message the actual message received
3788 */
3789static void
3790handle_dht_local_route_request (void *cls, struct GNUNET_SERVER_Client *client,
3791 const struct GNUNET_MessageHeader *message)
3792{
3793 const struct GNUNET_DHT_RouteMessage *dht_msg =
3794 (const struct GNUNET_DHT_RouteMessage *) message;
3795 const struct GNUNET_MessageHeader *enc_msg;
3796 struct DHT_MessageContext msg_ctx;
3797
3798 enc_msg = (const struct GNUNET_MessageHeader *) &dht_msg[1];
3799#if DEBUG_DHT
3800 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3801 "`%s:%s': Received `%s' request from client, message type %d, key %s, uid %llu\n",
3802 my_short_id, "DHT", "GENERIC", ntohs (message->type),
3803 GNUNET_h2s (&dht_msg->key), GNUNET_ntohll (dht_msg->unique_id));
3804#endif
3805#if DEBUG_DHT_ROUTING
3806 if (dhtlog_handle != NULL)
3807 dhtlog_handle->insert_dhtkey (NULL, &dht_msg->key);
3808#endif
3809
3810 memset (&msg_ctx, 0, sizeof (struct DHT_MessageContext));
3811 msg_ctx.client = find_active_client (client);
3812 memcpy (&msg_ctx.key, &dht_msg->key, sizeof (GNUNET_HashCode));
3813 msg_ctx.unique_id = GNUNET_ntohll (dht_msg->unique_id);
3814 msg_ctx.replication = ntohl (dht_msg->desired_replication_level);
3815 msg_ctx.msg_options = ntohl (dht_msg->options);
3816 if (GNUNET_DHT_RO_RECORD_ROUTE ==
3817 (msg_ctx.msg_options & GNUNET_DHT_RO_RECORD_ROUTE))
3818 {
3819 msg_ctx.path_history = GNUNET_malloc (sizeof (struct GNUNET_PeerIdentity));
3820 memcpy (msg_ctx.path_history, &my_identity,
3821 sizeof (struct GNUNET_PeerIdentity));
3822 msg_ctx.path_history_len = 1;
3823 }
3824 msg_ctx.network_size = log_of_network_size_estimate;
3825 msg_ctx.peer = my_identity;
3826 msg_ctx.importance = DHT_DEFAULT_P2P_IMPORTANCE + 4; /* Make local routing a higher priority */
3827 msg_ctx.timeout = DHT_DEFAULT_P2P_TIMEOUT;
3828
3829 if (ntohs (enc_msg->type) == GNUNET_MESSAGE_TYPE_DHT_GET)
3830 increment_stats (STAT_GET_START);
3831 else if (ntohs (enc_msg->type) == GNUNET_MESSAGE_TYPE_DHT_PUT)
3832 increment_stats (STAT_PUT_START);
3833 else if (ntohs (enc_msg->type) == GNUNET_MESSAGE_TYPE_DHT_FIND_PEER)
3834 increment_stats (STAT_FIND_PEER_START);
3835
3836 if (GNUNET_YES == malicious_dropper)
3837 {
3838 if (ntohs (enc_msg->type) == GNUNET_MESSAGE_TYPE_DHT_GET)
3839 {
3840#if DEBUG_DHT_ROUTING
3841 if ((debug_routes) && (dhtlog_handle != NULL))
3842 {
3843 dhtlog_handle->insert_query (NULL, msg_ctx.unique_id, DHTLOG_GET,
3844 msg_ctx.hop_count, GNUNET_NO, &my_identity,
3845 &msg_ctx.key);
3846 }
3847#endif
3848 }
3849 else if (ntohs (enc_msg->type) == GNUNET_MESSAGE_TYPE_DHT_PUT)
3850 {
3851#if DEBUG_DHT_ROUTING
3852 if ((debug_routes) && (dhtlog_handle != NULL))
3853 {
3854 dhtlog_handle->insert_query (NULL, msg_ctx.unique_id, DHTLOG_PUT,
3855 msg_ctx.hop_count, GNUNET_NO, &my_identity,
3856 &msg_ctx.key);
3857 }
3858#endif
3859 }
3860 GNUNET_SERVER_receive_done (client, GNUNET_OK);
3861 GNUNET_free_non_null (msg_ctx.path_history);
3862 return;
3863 }
3864
3865 demultiplex_message (enc_msg, &msg_ctx);
3866 GNUNET_SERVER_receive_done (client, GNUNET_OK);
3867
3868}
3869
3870/**
3871 * Handler for any locally received DHT control messages,
3872 * sets malicious flags mostly for now.
3873 *
3874 * @param cls closure for the service
3875 * @param client the client we received this message from
3876 * @param message the actual message received
3877 *
3878 */
3879static void
3880handle_dht_control_message (void *cls, struct GNUNET_SERVER_Client *client,
3881 const struct GNUNET_MessageHeader *message)
3882{
3883 const struct GNUNET_DHT_ControlMessage *dht_control_msg =
3884 (const struct GNUNET_DHT_ControlMessage *) message;
3885
3886#if DEBUG_DHT
3887 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3888 "`%s:%s': Received `%s' request from client, command %d\n",
3889 my_short_id, "DHT", "CONTROL", ntohs (dht_control_msg->command));
3890#endif
3891
3892 switch (ntohs (dht_control_msg->command))
3893 {
3894 case GNUNET_MESSAGE_TYPE_DHT_FIND_PEER:
3895 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3896 "Sending self seeking find peer request!\n");
3897 GNUNET_SCHEDULER_add_now (&send_find_peer_message, NULL);
3898 break;
3899#if HAVE_MALICIOUS
3900 case GNUNET_MESSAGE_TYPE_DHT_MALICIOUS_GET:
3901 if (ntohs (dht_control_msg->variable) > 0)
3902 malicious_get_frequency.rel_value = ntohs (dht_control_msg->variable);
3903 if (malicious_get_frequency.rel_value == 0)
3904 malicious_get_frequency = DEFAULT_MALICIOUS_GET_FREQUENCY;
3905 if (malicious_getter != GNUNET_YES)
3906 GNUNET_SCHEDULER_add_now (&malicious_get_task, NULL);
3907 malicious_getter = GNUNET_YES;
3908 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3909 "%s:%s Initiating malicious GET behavior, frequency %llu\n",
3910 my_short_id, "DHT", malicious_get_frequency.rel_value);
3911 break;
3912 case GNUNET_MESSAGE_TYPE_DHT_MALICIOUS_PUT:
3913 if (ntohs (dht_control_msg->variable) > 0)
3914 malicious_put_frequency.rel_value = ntohs (dht_control_msg->variable);
3915 if (malicious_put_frequency.rel_value == 0)
3916 malicious_put_frequency = DEFAULT_MALICIOUS_PUT_FREQUENCY;
3917 if (malicious_putter != GNUNET_YES)
3918 GNUNET_SCHEDULER_add_now (&malicious_put_task, NULL);
3919 malicious_putter = GNUNET_YES;
3920 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3921 "%s:%s Initiating malicious PUT behavior, frequency %d\n",
3922 my_short_id, "DHT", malicious_put_frequency);
3923 break;
3924 case GNUNET_MESSAGE_TYPE_DHT_MALICIOUS_DROP:
3925#if DEBUG_DHT_ROUTING
3926 if ((malicious_dropper != GNUNET_YES) && (dhtlog_handle != NULL))
3927 dhtlog_handle->set_malicious (&my_identity);
3928#endif
3929 malicious_dropper = GNUNET_YES;
3930 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3931 "%s:%s Initiating malicious DROP behavior\n", my_short_id,
3932 "DHT");
3933 break;
3934#endif
3935 default:
3936 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3937 "%s:%s Unknown control command type `%d'!\n", my_short_id,
3938 "DHT", ntohs (dht_control_msg->command));
3939 break;
3940 }
3941
3942 GNUNET_SERVER_receive_done (client, GNUNET_OK);
3943}
3944
3945/**
3946 * Handler for any generic DHT stop messages, calls the appropriate handler
3947 * depending on message type (if processed locally)
3948 *
3949 * @param cls closure for the service
3950 * @param client the client we received this message from
3951 * @param message the actual message received
3952 *
3953 */
3954static void
3955handle_dht_local_route_stop (void *cls, struct GNUNET_SERVER_Client *client,
3956 const struct GNUNET_MessageHeader *message)
3957{
3958
3959 const struct GNUNET_DHT_StopMessage *dht_stop_msg =
3960 (const struct GNUNET_DHT_StopMessage *) message;
3961 struct DHTQueryRecord *record;
3962 struct DHTRouteSource *pos;
3963
3964#if DEBUG_DHT
3965 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3966 "`%s:%s': Received `%s' request from client, uid %llu\n",
3967 my_short_id, "DHT", "GENERIC STOP",
3968 GNUNET_ntohll (dht_stop_msg->unique_id));
3969#endif
3970 record =
3971 GNUNET_CONTAINER_multihashmap_get (forward_list.hashmap,
3972 &dht_stop_msg->key);
3973 if (record != NULL)
3974 {
3975 pos = record->head;
3976
3977 while (pos != NULL)
3978 {
3979 /* If the client is non-null (local request) and the client matches the requesting client, remove the entry. */
3980 if ((pos->client != NULL) && (pos->client->client_handle == client))
3981 {
3982 if (pos->delete_task != GNUNET_SCHEDULER_NO_TASK)
3983 GNUNET_SCHEDULER_cancel (pos->delete_task);
3984 pos->delete_task =
3985 GNUNET_SCHEDULER_add_now (&remove_forward_entry, pos);
3986 }
3987 pos = pos->next;
3988 }
3989 }
3990
3991 GNUNET_SERVER_receive_done (client, GNUNET_OK);
3992}
3993
3994
3995/**
3996 * Core handler for p2p route requests.
3997 *
3998 * @param cls closure
3999 * @param message message
4000 * @param peer peer identity this notification is about
4001 * @param atsi performance data
4002 * @return GNUNET_OK to keep the connection open,
4003 * GNUNET_SYSERR to close it (signal serious error)
4004 */
4005static int
4006handle_dht_p2p_route_request (void *cls, const struct GNUNET_PeerIdentity *peer,
4007 const struct GNUNET_MessageHeader *message,
4008 const struct GNUNET_TRANSPORT_ATS_Information
4009 *atsi)
4010{
4011#if DEBUG_DHT
4012 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4013 "`%s:%s': Received P2P request from peer %s\n", my_short_id,
4014 "DHT", GNUNET_i2s (peer));
4015#endif
4016 struct GNUNET_DHT_P2PRouteMessage *incoming =
4017 (struct GNUNET_DHT_P2PRouteMessage *) message;
4018 struct GNUNET_MessageHeader *enc_msg =
4019 (struct GNUNET_MessageHeader *) &incoming[1];
4020 struct DHT_MessageContext *msg_ctx;
4021 char *route_path;
4022 int path_size;
4023
4024 if (ntohs (enc_msg->size) >= GNUNET_SERVER_MAX_MESSAGE_SIZE - 1)
4025 {
4026 GNUNET_break_op (0);
4027 return GNUNET_YES;
4028 }
4029
4030 if (malicious_dropper == GNUNET_YES)
4031 {
4032#if DEBUG_DHT_ROUTING
4033 if ((debug_routes_extended) && (dhtlog_handle != NULL))
4034 {
4035 /** Log routes that die due to high load! */
4036 dhtlog_handle->insert_route (NULL,
4037#if HAVE_UID_FOR_TESTING
4038 GNUNET_ntohll (incoming->unique_id),
4039#else
4040 0,
4041#endif
4042 DHTLOG_ROUTE, ntohl (incoming->hop_count),
4043 GNUNET_SYSERR, &my_identity, &incoming->key,
4044 peer, NULL);
4045 }
4046#endif
4047 return GNUNET_YES;
4048 }
4049
4050 if (get_max_send_delay ().rel_value > MAX_REQUEST_TIME.rel_value)
4051 {
4052 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4053 "Sending of previous replies took too long, backing off!\n");
4054 increment_stats ("# route requests dropped due to high load");
4055 decrease_max_send_delay (get_max_send_delay ());
4056#if DEBUG_DHT_ROUTING
4057 if ((debug_routes_extended) && (dhtlog_handle != NULL))
4058 {
4059 /** Log routes that die due to high load! */
4060 dhtlog_handle->insert_route (NULL,
4061#if HAVE_UID_FOR_TESTING
4062 GNUNET_ntohll (incoming->unique_id),
4063#else
4064 0,
4065#endif
4066 DHTLOG_ROUTE, ntohl (incoming->hop_count),
4067 GNUNET_SYSERR, &my_identity, &incoming->key,
4068 peer, NULL);
4069 }
4070#endif
4071 return GNUNET_YES;
4072 }
4073 msg_ctx = GNUNET_malloc (sizeof (struct DHT_MessageContext));
4074 msg_ctx->bloom =
4075 GNUNET_CONTAINER_bloomfilter_init (incoming->bloomfilter, DHT_BLOOM_SIZE,
4076 DHT_BLOOM_K);
4077 GNUNET_assert (msg_ctx->bloom != NULL);
4078 msg_ctx->hop_count = ntohl (incoming->hop_count);
4079 memcpy (&msg_ctx->key, &incoming->key, sizeof (GNUNET_HashCode));
4080 msg_ctx->replication = ntohl (incoming->desired_replication_level);
4081#if HAVE_UID_FOR_TESTING
4082 msg_ctx->unique_id = GNUNET_ntohll (incoming->unique_id);
4083#endif
4084 msg_ctx->msg_options = ntohl (incoming->options);
4085 if (GNUNET_DHT_RO_RECORD_ROUTE ==
4086 (msg_ctx->msg_options & GNUNET_DHT_RO_RECORD_ROUTE))
4087 {
4088 path_size =
4089 ntohl (incoming->outgoing_path_length) *
4090 sizeof (struct GNUNET_PeerIdentity);
4091 if (ntohs (message->size) !=
4092 (sizeof (struct GNUNET_DHT_P2PRouteMessage) + ntohs (enc_msg->size) +
4093 path_size))
4094 {
4095 GNUNET_break_op (0);
4096 GNUNET_free (msg_ctx);
4097 return GNUNET_YES;
4098 }
4099 route_path = (char *) &incoming[1];
4100 route_path = route_path + ntohs (enc_msg->size);
4101 msg_ctx->path_history =
4102 GNUNET_malloc (sizeof (struct GNUNET_PeerIdentity) + path_size);
4103 memcpy (msg_ctx->path_history, route_path, path_size);
4104 memcpy (&msg_ctx->path_history[path_size], &my_identity,
4105 sizeof (struct GNUNET_PeerIdentity));
4106 msg_ctx->path_history_len = ntohl (incoming->outgoing_path_length) + 1;
4107 }
4108 msg_ctx->network_size = ntohl (incoming->network_size);
4109 msg_ctx->peer = *peer;
4110 msg_ctx->importance = DHT_DEFAULT_P2P_IMPORTANCE;
4111 msg_ctx->timeout = DHT_DEFAULT_P2P_TIMEOUT;
4112 demultiplex_message (enc_msg, msg_ctx);
4113 if (msg_ctx->bloom != NULL)
4114 {
4115 GNUNET_CONTAINER_bloomfilter_free (msg_ctx->bloom);
4116 msg_ctx->bloom = NULL;
4117 }
4118 GNUNET_free (msg_ctx);
4119 return GNUNET_YES;
4120}
4121
4122
4123/**
4124 * Core handler for p2p route results.
4125 *
4126 * @param cls closure
4127 * @param message message
4128 * @param peer peer identity this notification is about
4129 * @param atsi performance data
4130 *
4131 */
4132static int
4133handle_dht_p2p_route_result (void *cls, const struct GNUNET_PeerIdentity *peer,
4134 const struct GNUNET_MessageHeader *message,
4135 const struct GNUNET_TRANSPORT_ATS_Information
4136 *atsi)
4137{
4138#if DEBUG_DHT
4139 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4140 "`%s:%s': Received request from peer %s\n", my_short_id, "DHT",
4141 GNUNET_i2s (peer));
4142#endif
4143 const struct GNUNET_DHT_P2PRouteResultMessage *incoming =
4144 (const struct GNUNET_DHT_P2PRouteResultMessage *) message;
4145 struct GNUNET_MessageHeader *enc_msg =
4146 (struct GNUNET_MessageHeader *) &incoming[1];
4147 struct DHT_MessageContext msg_ctx;
4148
4149#if DEBUG_PATH
4150 char *path_offset;
4151 unsigned int i;
4152#endif
4153 if (ntohs (enc_msg->size) >= GNUNET_SERVER_MAX_MESSAGE_SIZE - 1)
4154 {
4155 GNUNET_break_op (0);
4156 return GNUNET_YES;
4157 }
4158
4159 if (malicious_dropper == GNUNET_YES)
4160 {
4161#if DEBUG_DHT_ROUTING
4162 if ((debug_routes_extended) && (dhtlog_handle != NULL))
4163 {
4164 /** Log routes that die due to high load! */
4165 dhtlog_handle->insert_route (NULL,
4166#if HAVE_UID_FOR_TESTING
4167 GNUNET_ntohll (incoming->unique_id),
4168#else
4169 0,
4170#endif
4171 DHTLOG_ROUTE, ntohl (incoming->hop_count),
4172 GNUNET_SYSERR, &my_identity, &incoming->key,
4173 peer, NULL);
4174 }
4175#endif
4176 return GNUNET_YES;
4177 }
4178
4179 memset (&msg_ctx, 0, sizeof (struct DHT_MessageContext));
4180 memcpy (&msg_ctx.key, &incoming->key, sizeof (GNUNET_HashCode));
4181#if HAVE_UID_FOR_TESTING
4182 msg_ctx.unique_id = GNUNET_ntohll (incoming->unique_id);
4183#endif
4184 msg_ctx.msg_options = ntohl (incoming->options);
4185 msg_ctx.hop_count = ntohl (incoming->hop_count);
4186 msg_ctx.peer = *peer;
4187 msg_ctx.importance = DHT_DEFAULT_P2P_IMPORTANCE + 2; /* Make result routing a higher priority */
4188 msg_ctx.timeout = DHT_DEFAULT_P2P_TIMEOUT;
4189 if ((GNUNET_DHT_RO_RECORD_ROUTE ==
4190 (msg_ctx.msg_options & GNUNET_DHT_RO_RECORD_ROUTE)) &&
4191 (ntohl (incoming->outgoing_path_length) > 0))
4192 {
4193 if (ntohs (message->size) -
4194 sizeof (struct GNUNET_DHT_P2PRouteResultMessage) -
4195 ntohs (enc_msg->size) !=
4196 ntohl (incoming->outgoing_path_length) *
4197 sizeof (struct GNUNET_PeerIdentity))
4198 {
4199#if DEBUG_DHT
4200 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4201 "Return message indicated a path was included, but sizes are wrong: Total size %d, enc size %d, left %d, expected %d\n",
4202 ntohs (message->size), ntohs (enc_msg->size),
4203 ntohs (message->size) -
4204 sizeof (struct GNUNET_DHT_P2PRouteResultMessage) -
4205 ntohs (enc_msg->size),
4206 ntohl (incoming->outgoing_path_length) *
4207 sizeof (struct GNUNET_PeerIdentity));
4208#endif
4209 GNUNET_break_op (0);
4210 return GNUNET_NO;
4211 }
4212 msg_ctx.path_history = (char *) &incoming[1];
4213 msg_ctx.path_history += ntohs (enc_msg->size);
4214 msg_ctx.path_history_len = ntohl (incoming->outgoing_path_length);
4215#if DEBUG_PATH
4216 for (i = 0; i < msg_ctx.path_history_len; i++)
4217 {
4218 path_offset =
4219 &msg_ctx.path_history[i * sizeof (struct GNUNET_PeerIdentity)];
4220 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4221 "(handle_p2p_route_result) Key %s Found peer %d:%s\n",
4222 GNUNET_h2s (&msg_ctx.key), i,
4223 GNUNET_i2s ((struct GNUNET_PeerIdentity *) path_offset));
4224 }
4225#endif
4226 }
4227 route_result_message (enc_msg, &msg_ctx);
4228 return GNUNET_YES;
4229}
4230
4231
4232/**
4233 * Receive the HELLO from transport service,
4234 * free current and replace if necessary.
4235 *
4236 * @param cls NULL
4237 * @param message HELLO message of peer
4238 */
4239static void
4240process_hello (void *cls, const struct GNUNET_MessageHeader *message)
4241{
4242#if DEBUG_DHT
4243 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4244 "Received our `%s' from transport service\n", "HELLO");
4245#endif
4246
4247 GNUNET_assert (message != NULL);
4248 GNUNET_free_non_null (my_hello);
4249 my_hello = GNUNET_malloc (ntohs (message->size));
4250 memcpy (my_hello, message, ntohs (message->size));
4251}
4252
4253
4254/**
4255 * Task run during shutdown.
4256 *
4257 * @param cls unused
4258 * @param tc unused
4259 */
4260static void
4261shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
4262{
4263 int bucket_count;
4264 struct PeerInfo *pos;
4265
4266 if (NULL != ghh)
4267 {
4268 GNUNET_TRANSPORT_get_hello_cancel (ghh);
4269 ghh = NULL;
4270 }
4271 if (transport_handle != NULL)
4272 {
4273 GNUNET_free_non_null (my_hello);
4274 GNUNET_TRANSPORT_disconnect (transport_handle);
4275 transport_handle = NULL;
4276 }
4277 if (NULL != nse)
4278 {
4279 GNUNET_NSE_disconnect (nse);
4280 nse = NULL;
4281 }
4282 if (coreAPI != NULL)
4283 {
4284#if DEBUG_DHT
4285 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s:%s Disconnecting core!\n",
4286 my_short_id, "DHT");
4287#endif
4288 GNUNET_CORE_disconnect (coreAPI);
4289 coreAPI = NULL;
4290 }
4291 for (bucket_count = lowest_bucket; bucket_count < MAX_BUCKETS; bucket_count++)
4292 {
4293 while (k_buckets[bucket_count].head != NULL)
4294 {
4295 pos = k_buckets[bucket_count].head;
4296#if DEBUG_DHT
4297 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4298 "%s:%s Removing peer %s from bucket %d!\n", my_short_id,
4299 "DHT", GNUNET_i2s (&pos->id), bucket_count);
4300#endif
4301 delete_peer (pos, bucket_count);
4302 }
4303 }
4304 if (datacache != NULL)
4305 {
4306#if DEBUG_DHT
4307 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s:%s Destroying datacache!\n",
4308 my_short_id, "DHT");
4309#endif
4310 GNUNET_DATACACHE_destroy (datacache);
4311 datacache = NULL;
4312 }
4313 if (stats != NULL)
4314 {
4315 GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
4316 stats = NULL;
4317 }
4318 if (dhtlog_handle != NULL)
4319 {
4320 GNUNET_DHTLOG_disconnect (dhtlog_handle);
4321 dhtlog_handle = NULL;
4322 }
4323 if (block_context != NULL)
4324 {
4325 GNUNET_BLOCK_context_destroy (block_context);
4326 block_context = NULL;
4327 }
4328 GNUNET_free_non_null (my_short_id);
4329 my_short_id = NULL;
4330}
4331
4332
4333/**
4334 * To be called on core init/fail.
4335 *
4336 * @param cls service closure
4337 * @param server handle to the server for this service
4338 * @param identity the public identity of this peer
4339 * @param publicKey the public key of this peer
4340 */
4341static void
4342core_init (void *cls, struct GNUNET_CORE_Handle *server,
4343 const struct GNUNET_PeerIdentity *identity,
4344 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
4345{
4346
4347 if (server == NULL)
4348 {
4349#if DEBUG_DHT
4350 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s: Connection to core FAILED!\n",
4351 "dht", GNUNET_i2s (identity));
4352#endif
4353 GNUNET_SCHEDULER_cancel (cleanup_task);
4354 GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
4355 return;
4356 }
4357#if DEBUG_DHT
4358 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4359 "%s: Core connection initialized, I am peer: %s\n", "dht",
4360 GNUNET_i2s (identity));
4361#endif
4362
4363 /* Copy our identity so we can use it */
4364 memcpy (&my_identity, identity, sizeof (struct GNUNET_PeerIdentity));
4365 if (my_short_id != NULL)
4366 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4367 "%s Receive CORE INIT message but have already been initialized! Did CORE fail?\n",
4368 "DHT SERVICE");
4369 my_short_id = GNUNET_strdup (GNUNET_i2s (&my_identity));
4370 if (dhtlog_handle != NULL)
4371 dhtlog_handle->insert_node (NULL, &my_identity);
4372}
4373
4374
4375static struct GNUNET_SERVER_MessageHandler plugin_handlers[] = {
4376 {&handle_dht_local_route_request, NULL, GNUNET_MESSAGE_TYPE_DHT_LOCAL_ROUTE,
4377 0},
4378 {&handle_dht_local_route_stop, NULL,
4379 GNUNET_MESSAGE_TYPE_DHT_LOCAL_ROUTE_STOP, 0},
4380 {&handle_dht_control_message, NULL, GNUNET_MESSAGE_TYPE_DHT_CONTROL, 0},
4381 {NULL, NULL, 0, 0}
4382};
4383
4384
4385static struct GNUNET_CORE_MessageHandler core_handlers[] = {
4386 {&handle_dht_p2p_route_request, GNUNET_MESSAGE_TYPE_DHT_P2P_ROUTE, 0},
4387 {&handle_dht_p2p_route_result, GNUNET_MESSAGE_TYPE_DHT_P2P_ROUTE_RESULT, 0},
4388 {NULL, 0, 0}
4389};
4390
4391
4392/**
4393 * Method called whenever a peer connects.
4394 *
4395 * @param cls closure
4396 * @param peer peer identity this notification is about
4397 * @param atsi performance data
4398 */
4399static void
4400handle_core_connect (void *cls, const struct GNUNET_PeerIdentity *peer,
4401 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
4402{
4403 struct PeerInfo *ret;
4404 struct DHTPutEntry *put_entry;
4405 int peer_bucket;
4406
4407 /* Check for connect to self message */
4408 if (0 == memcmp (&my_identity, peer, sizeof (struct GNUNET_PeerIdentity)))
4409 return;
4410
4411#if DEBUG_DHT
4412 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4413 "%s:%s Receives core connect message for peer %s distance %d!\n",
4414 my_short_id, "dht", GNUNET_i2s (peer), distance);
4415#endif
4416
4417 if (GNUNET_YES ==
4418 GNUNET_CONTAINER_multihashmap_contains (all_known_peers,
4419 &peer->hashPubKey))
4420 {
4421#if DEBUG_DHT
4422 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4423 "%s:%s Received %s message for peer %s, but already have peer in RT!",
4424 my_short_id, "DHT", "CORE CONNECT", GNUNET_i2s (peer));
4425#endif
4426 GNUNET_break (0);
4427 return;
4428 }
4429
4430 if ((datacache != NULL) && (GNUNET_YES == put_peer_identities))
4431 {
4432 put_entry =
4433 GNUNET_malloc (sizeof (struct DHTPutEntry) +
4434 sizeof (struct GNUNET_PeerIdentity));
4435 put_entry->path_length = 0;
4436 put_entry->data_size = sizeof (struct GNUNET_PeerIdentity);
4437 memcpy (&put_entry[1], peer, sizeof (struct GNUNET_PeerIdentity));
4438 GNUNET_DATACACHE_put (datacache, &peer->hashPubKey,
4439 sizeof (struct DHTPutEntry) +
4440 sizeof (struct GNUNET_PeerIdentity),
4441 (char *) put_entry, GNUNET_BLOCK_TYPE_DHT_HELLO,
4442 GNUNET_TIME_absolute_get_forever ());
4443 GNUNET_free (put_entry);
4444 }
4445 else if (datacache == NULL)
4446 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4447 "DHT has no connection to datacache!\n");
4448
4449 peer_bucket = find_current_bucket (&peer->hashPubKey);
4450 GNUNET_assert (peer_bucket >= lowest_bucket);
4451 GNUNET_assert (peer_bucket < MAX_BUCKETS);
4452 ret = GNUNET_malloc (sizeof (struct PeerInfo));
4453#if 0
4454 ret->latency = latency;
4455 ret->distance = distance;
4456#endif
4457 ret->id = *peer;
4458 GNUNET_CONTAINER_DLL_insert_after (k_buckets[peer_bucket].head,
4459 k_buckets[peer_bucket].tail,
4460 k_buckets[peer_bucket].tail, ret);
4461 k_buckets[peer_bucket].peers_size++;
4462#if DO_UPDATE_PREFERENCE
4463 if ((GNUNET_CRYPTO_hash_matching_bits
4464 (&my_identity.hashPubKey, &peer->hashPubKey) > 0) &&
4465 (k_buckets[peer_bucket].peers_size <= bucket_size))
4466 ret->preference_task =
4467 GNUNET_SCHEDULER_add_now (&update_core_preference, ret);
4468#endif
4469 if ((k_buckets[lowest_bucket].peers_size) >= bucket_size)
4470 enable_next_bucket ();
4471 newly_found_peers++;
4472 GNUNET_CONTAINER_multihashmap_put (all_known_peers, &peer->hashPubKey, ret,
4473 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
4474 increment_stats (STAT_PEERS_KNOWN);
4475
4476#if DEBUG_DHT
4477 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4478 "%s:%s Adding peer to routing list: %s\n", my_short_id, "DHT",
4479 ret == NULL ? "NOT ADDED" : "PEER ADDED");
4480#endif
4481}
4482
4483
4484/**
4485 * Method called whenever a peer disconnects.
4486 *
4487 * @param cls closure
4488 * @param peer peer identity this notification is about
4489 */
4490static void
4491handle_core_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
4492{
4493 struct PeerInfo *to_remove;
4494 int current_bucket;
4495
4496 /* Check for disconnect from self message */
4497 if (0 == memcmp (&my_identity, peer, sizeof (struct GNUNET_PeerIdentity)))
4498 return;
4499#if DEBUG_DHT
4500 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4501 "%s:%s: Received peer disconnect message for peer `%s' from %s\n",
4502 my_short_id, "DHT", GNUNET_i2s (peer), "CORE");
4503#endif
4504
4505 if (GNUNET_YES !=
4506 GNUNET_CONTAINER_multihashmap_contains (all_known_peers,
4507 &peer->hashPubKey))
4508 {
4509 GNUNET_break (0);
4510#if DEBUG_DHT
4511 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4512 "%s:%s: do not have peer `%s' in RT, can't disconnect!\n",
4513 my_short_id, "DHT", GNUNET_i2s (peer));
4514#endif
4515 return;
4516 }
4517 increment_stats (STAT_DISCONNECTS);
4518 GNUNET_assert (GNUNET_CONTAINER_multihashmap_contains
4519 (all_known_peers, &peer->hashPubKey));
4520 to_remove =
4521 GNUNET_CONTAINER_multihashmap_get (all_known_peers, &peer->hashPubKey);
4522 GNUNET_assert (to_remove != NULL);
4523 if (NULL != to_remove->info_ctx)
4524 {
4525 GNUNET_CORE_peer_change_preference_cancel (to_remove->info_ctx);
4526 to_remove->info_ctx = NULL;
4527 }
4528 GNUNET_assert (0 ==
4529 memcmp (peer, &to_remove->id,
4530 sizeof (struct GNUNET_PeerIdentity)));
4531 current_bucket = find_current_bucket (&to_remove->id.hashPubKey);
4532 delete_peer (to_remove, current_bucket);
4533}
4534
4535
4536/**
4537 * Process dht requests.
4538 *
4539 * @param cls closure
4540 * @param server the initialized server
4541 * @param c configuration to use
4542 */
4543static void
4544run (void *cls, struct GNUNET_SERVER_Handle *server,
4545 const struct GNUNET_CONFIGURATION_Handle *c)
4546{
4547 struct GNUNET_TIME_Relative next_send_time;
4548 unsigned long long temp_config_num;
4549
4550 cfg = c;
4551 datacache = GNUNET_DATACACHE_create (cfg, "dhtcache");
4552 GNUNET_SERVER_add_handlers (server, plugin_handlers);
4553 GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
4554 nse = GNUNET_NSE_connect (cfg, &update_network_size_estimate, NULL);
4555 coreAPI = GNUNET_CORE_connect (cfg, /* Main configuration */
4556 DEFAULT_CORE_QUEUE_SIZE, /* queue size */
4557 NULL, /* Closure passed to DHT functions */
4558 &core_init, /* Call core_init once connected */
4559 &handle_core_connect, /* Handle connects */
4560 &handle_core_disconnect, /* remove peers on disconnects */
4561 NULL, /* Do we care about "status" updates? */
4562 NULL, /* Don't want notified about all incoming messages */
4563 GNUNET_NO, /* For header only inbound notification */
4564 NULL, /* Don't want notified about all outbound messages */
4565 GNUNET_NO, /* For header only outbound notification */
4566 core_handlers); /* Register these handlers */
4567
4568 if (coreAPI == NULL)
4569 return;
4570 transport_handle =
4571 GNUNET_TRANSPORT_connect (cfg, NULL, NULL, NULL, NULL, NULL);
4572 if (transport_handle != NULL)
4573 ghh = GNUNET_TRANSPORT_get_hello (transport_handle, &process_hello, NULL);
4574 else
4575 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4576 "Failed to connect to transport service!\n");
4577 block_context = GNUNET_BLOCK_context_create (cfg);
4578 lowest_bucket = MAX_BUCKETS - 1;
4579 forward_list.hashmap =
4580 GNUNET_CONTAINER_multihashmap_create (MAX_OUTSTANDING_FORWARDS / 10);
4581 forward_list.minHeap =
4582 GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
4583 all_known_peers = GNUNET_CONTAINER_multihashmap_create (MAX_BUCKETS / 8);
4584 GNUNET_assert (all_known_peers != NULL);
4585 if (GNUNET_YES ==
4586 GNUNET_CONFIGURATION_get_value_yesno (cfg, "dht_testing",
4587 "mysql_logging"))
4588 {
4589 debug_routes = GNUNET_YES;
4590 }
4591
4592 if (GNUNET_YES ==
4593 GNUNET_CONFIGURATION_get_value_yesno (cfg, "dht", "strict_kademlia"))
4594 {
4595 strict_kademlia = GNUNET_YES;
4596 }
4597
4598 if (GNUNET_YES ==
4599 GNUNET_CONFIGURATION_get_value_yesno (cfg, "dht", "stop_on_closest"))
4600 {
4601 stop_on_closest = GNUNET_YES;
4602 }
4603
4604 if (GNUNET_YES ==
4605 GNUNET_CONFIGURATION_get_value_yesno (cfg, "dht", "stop_found"))
4606 {
4607 stop_on_found = GNUNET_YES;
4608 }
4609
4610 if (GNUNET_YES ==
4611 GNUNET_CONFIGURATION_get_value_yesno (cfg, "dht", "malicious_getter"))
4612 {
4613 malicious_getter = GNUNET_YES;
4614 if (GNUNET_NO ==
4615 GNUNET_CONFIGURATION_get_value_time (cfg, "DHT",
4616 "MALICIOUS_GET_FREQUENCY",
4617 &malicious_get_frequency))
4618 malicious_get_frequency = DEFAULT_MALICIOUS_GET_FREQUENCY;
4619 }
4620
4621 if (GNUNET_YES ==
4622 GNUNET_CONFIGURATION_get_value_yesno (cfg, "dht", "malicious_putter"))
4623 {
4624 malicious_putter = GNUNET_YES;
4625 if (GNUNET_NO ==
4626 GNUNET_CONFIGURATION_get_value_time (cfg, "DHT",
4627 "MALICIOUS_PUT_FREQUENCY",
4628 &malicious_put_frequency))
4629 malicious_put_frequency = DEFAULT_MALICIOUS_PUT_FREQUENCY;
4630 }
4631
4632 if (GNUNET_OK ==
4633 GNUNET_CONFIGURATION_get_value_number (cfg, "DHT", "bucket_size",
4634 &temp_config_num))
4635 {
4636 bucket_size = (unsigned int) temp_config_num;
4637 }
4638
4639 if (GNUNET_OK !=
4640 GNUNET_CONFIGURATION_get_value_number (cfg, "DHT", "kad_alpha",
4641 &kademlia_replication))
4642 {
4643 kademlia_replication = DEFAULT_KADEMLIA_REPLICATION;
4644 }
4645
4646 if (GNUNET_YES ==
4647 GNUNET_CONFIGURATION_get_value_yesno (cfg, "dht", "malicious_dropper"))
4648 {
4649 malicious_dropper = GNUNET_YES;
4650 }
4651
4652 if (GNUNET_NO ==
4653 GNUNET_CONFIGURATION_get_value_yesno (cfg, "dht", "do_find_peer"))
4654 {
4655 do_find_peer = GNUNET_NO;
4656 }
4657 else
4658 do_find_peer = GNUNET_YES;
4659
4660 if (GNUNET_YES ==
4661 GNUNET_CONFIGURATION_get_value_yesno (cfg, "dht", "use_real_distance"))
4662 use_real_distance = GNUNET_YES;
4663
4664 if (GNUNET_YES ==
4665 GNUNET_CONFIGURATION_get_value_yesno (cfg, "dht_testing",
4666 "mysql_logging_extended"))
4667 {
4668 debug_routes = GNUNET_YES;
4669 debug_routes_extended = GNUNET_YES;
4670 }
4671
4672#if DEBUG_DHT_ROUTING
4673 if (GNUNET_YES == debug_routes)
4674 {
4675 dhtlog_handle = GNUNET_DHTLOG_connect (cfg);
4676 if (dhtlog_handle == NULL)
4677 {
4678 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4679 "Could not connect to mysql logging server, logging will not happen!");
4680 }
4681 }
4682#endif
4683
4684 if (GNUNET_YES ==
4685 GNUNET_CONFIGURATION_get_value_yesno (cfg, "dht", "paper_forwarding"))
4686 paper_forwarding = GNUNET_YES;
4687
4688 if (GNUNET_YES ==
4689 GNUNET_CONFIGURATION_get_value_yesno (cfg, "dht", "put_peer_identities"))
4690 put_peer_identities = GNUNET_YES;
4691
4692 stats = GNUNET_STATISTICS_create ("dht", cfg);
4693
4694 if (stats != NULL)
4695 {
4696 GNUNET_STATISTICS_set (stats, STAT_ROUTES, 0, GNUNET_NO);
4697 GNUNET_STATISTICS_set (stats, STAT_ROUTE_FORWARDS, 0, GNUNET_NO);
4698 GNUNET_STATISTICS_set (stats, STAT_ROUTE_FORWARDS_CLOSEST, 0, GNUNET_NO);
4699 GNUNET_STATISTICS_set (stats, STAT_RESULTS, 0, GNUNET_NO);
4700 GNUNET_STATISTICS_set (stats, STAT_RESULTS_TO_CLIENT, 0, GNUNET_NO);
4701 GNUNET_STATISTICS_set (stats, STAT_RESULT_FORWARDS, 0, GNUNET_NO);
4702 GNUNET_STATISTICS_set (stats, STAT_GETS, 0, GNUNET_NO);
4703 GNUNET_STATISTICS_set (stats, STAT_PUTS, 0, GNUNET_NO);
4704 GNUNET_STATISTICS_set (stats, STAT_PUTS_INSERTED, 0, GNUNET_NO);
4705 GNUNET_STATISTICS_set (stats, STAT_FIND_PEER, 0, GNUNET_NO);
4706 GNUNET_STATISTICS_set (stats, STAT_FIND_PEER_START, 0, GNUNET_NO);
4707 GNUNET_STATISTICS_set (stats, STAT_GET_START, 0, GNUNET_NO);
4708 GNUNET_STATISTICS_set (stats, STAT_PUT_START, 0, GNUNET_NO);
4709 GNUNET_STATISTICS_set (stats, STAT_FIND_PEER_REPLY, 0, GNUNET_NO);
4710 GNUNET_STATISTICS_set (stats, STAT_FIND_PEER_ANSWER, 0, GNUNET_NO);
4711 GNUNET_STATISTICS_set (stats, STAT_BLOOM_FIND_PEER, 0, GNUNET_NO);
4712 GNUNET_STATISTICS_set (stats, STAT_GET_REPLY, 0, GNUNET_NO);
4713 GNUNET_STATISTICS_set (stats, STAT_GET_RESPONSE_START, 0, GNUNET_NO);
4714 GNUNET_STATISTICS_set (stats, STAT_HELLOS_PROVIDED, 0, GNUNET_NO);
4715 GNUNET_STATISTICS_set (stats, STAT_DISCONNECTS, 0, GNUNET_NO);
4716 }
4717 if (GNUNET_YES == do_find_peer)
4718 {
4719 next_send_time.rel_value =
4720 DHT_MINIMUM_FIND_PEER_INTERVAL.rel_value +
4721 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_STRONG,
4722 (DHT_MAXIMUM_FIND_PEER_INTERVAL.rel_value /
4723 2) -
4724 DHT_MINIMUM_FIND_PEER_INTERVAL.rel_value);
4725 find_peer_context.start = GNUNET_TIME_absolute_get ();
4726 GNUNET_SCHEDULER_add_delayed (next_send_time, &send_find_peer_message,
4727 &find_peer_context);
4728 }
4729
4730 /* Scheduled the task to clean up when shutdown is called */
4731 cleanup_task =
4732 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
4733 &shutdown_task, NULL);
4734}
4735
4736
4737/**
4738 * The main function for the dht service.
4739 *
4740 * @param argc number of arguments from the command line
4741 * @param argv command line arguments
4742 * @return 0 ok, 1 on error
4743 */
4744int
4745main (int argc, char *const *argv)
4746{
4747 int ret;
4748
4749#if HAVE_UID_FOR_TESTING > 1
4750 recent.hashmap = GNUNET_CONTAINER_multihashmap_create (DHT_MAX_RECENT / 2);
4751#endif
4752 recent.minHeap =
4753 GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
4754 recent_find_peer_requests =
4755 GNUNET_CONTAINER_multihashmap_create (MAX_BUCKETS / 8);
4756 ret =
4757 (GNUNET_OK ==
4758 GNUNET_SERVICE_run (argc, argv, "dht", GNUNET_SERVICE_OPTION_NONE, &run,
4759 NULL)) ? 0 : 1;
4760#if HAVE_UID_FOR_TESTING > 1
4761 GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap_size (recent.hashmap));
4762 GNUNET_CONTAINER_multihashmap_destroy (recent.hashmap);
4763 recent.hashmap = NULL;
4764#endif
4765 GNUNET_assert (0 == GNUNET_CONTAINER_heap_get_size (recent.minHeap));
4766 GNUNET_CONTAINER_heap_destroy (recent.minHeap);
4767 recent.minHeap = NULL;
4768 GNUNET_CONTAINER_multihashmap_destroy (recent_find_peer_requests);
4769 recent_find_peer_requests = NULL;
4770 return ret;
4771}
4772
4773/* end of gnunet-service-dht.c */
diff --git a/src/dht/plugin_dhtlog_dummy.c b/src/dht/plugin_dhtlog_dummy.c
deleted file mode 100644
index 563d97e46..000000000
--- a/src/dht/plugin_dhtlog_dummy.c
+++ /dev/null
@@ -1,347 +0,0 @@
1/*
2 This file is part of GNUnet.
3 (C) 2006 - 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/dht/plugin_dhtlog_dummy.c
23 * @brief Dummy logging plugin to test logging calls
24 * @author Nathan Evans
25 *
26 * Database: NONE
27 */
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31#include "dhtlog.h"
32
33#define DEBUG_DHTLOG GNUNET_NO
34
35/*
36 * Inserts the specified trial into the dhttests.trials table
37 *
38 * @param trial_info struct containing the data to insert about this trial
39 *
40 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
41 */
42int
43add_trial (struct GNUNET_DHTLOG_TrialInfo *trial_info)
44{
45 return GNUNET_OK;
46}
47
48/*
49 * Inserts the specified round into the dhttests.rounds table
50 *
51 * @param round_type the type of round that is being started
52 * @param round_count counter for the round (if applicable)
53 *
54 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
55 */
56int
57add_round (unsigned int round_type, unsigned int round_count)
58{
59 return GNUNET_OK;
60}
61
62/*
63 * Inserts the specified round results into the
64 * dhttests.processed_round_details table
65 *
66 * @param round_type the type of round that is being started
67 * @param round_count counter for the round (if applicable)
68 * @param num_messages the total number of messages initiated
69 * @param num_messages_succeeded the number of messages that succeeded
70 *
71 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
72 */
73int
74add_round_details (unsigned int round_type, unsigned int round_count,
75 unsigned int num_messages,
76 unsigned int num_messages_succeded)
77{
78 return GNUNET_OK;
79}
80
81/*
82 * Inserts the specified dhtkey into the dhttests.dhtkeys table,
83 * stores return value of dhttests.dhtkeys.dhtkeyuid into dhtkeyuid
84 *
85 * @param dhtkeyuid return value
86 * @param dhtkey hashcode of key to insert
87 *
88 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
89 */
90int
91add_dhtkey (unsigned long long *dhtkeyuid, const GNUNET_HashCode * dhtkey)
92{
93 *dhtkeyuid = 1171;
94 return GNUNET_OK;
95}
96
97
98/*
99 * Inserts the specified node into the dhttests.nodes table
100 *
101 * @param nodeuid the inserted node uid
102 * @param node the node to insert
103 *
104 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
105 */
106int
107add_node (unsigned long long *nodeuid, struct GNUNET_PeerIdentity *node)
108{
109 *nodeuid = 1337;
110 return GNUNET_OK;
111}
112
113/*
114 * Update dhttests.trials table with current server time as end time
115 *
116 * @param gets_succeeded how many gets did the testcase report as successful
117 *
118 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
119 */
120int
121update_trials (unsigned int gets_succeeded)
122{
123 return GNUNET_OK;
124}
125
126
127/*
128 * Inserts the specified stats into the dhttests.generic_stats table
129 *
130 * @param peer the peer inserting the statistic
131 * @param name the name of the statistic
132 * @param section the section of the statistic
133 * @param value the value of the statistic
134 *
135 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
136 */
137int
138add_generic_stat (const struct GNUNET_PeerIdentity *peer, const char *name,
139 const char *section, uint64_t value)
140{
141 return GNUNET_OK;
142}
143
144/*
145 * Update dhttests.trials table with total connections information
146 *
147 * @param totalConnections the number of connections
148 *
149 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
150 */
151int
152add_connections (unsigned int totalConnections)
153{
154 return GNUNET_OK;
155}
156
157/*
158 * Inserts the specified query into the dhttests.queries table
159 *
160 * @param sqlqueruid inserted query uid
161 * @param queryid dht query id
162 * @param type type of the query
163 * @param hops number of hops query traveled
164 * @param succeeded whether or not query was successful
165 * @param node the node the query hit
166 * @param key the key of the query
167 *
168 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
169 */
170int
171add_query (unsigned long long *sqlqueryuid, unsigned long long queryid,
172 unsigned int type, unsigned int hops, int succeeded,
173 const struct GNUNET_PeerIdentity *node, const GNUNET_HashCode * key)
174{
175 *sqlqueryuid = 17;
176 return GNUNET_OK;
177}
178
179/*
180 * Inserts the specified route information into the dhttests.routes table
181 *
182 * @param sqlqueruid inserted query uid
183 * @param queryid dht query id
184 * @param type type of the query
185 * @param hops number of hops query traveled
186 * @param succeeded whether or not query was successful
187 * @param node the node the query hit
188 * @param key the key of the query
189 * @param from_node the node that sent the message to node
190 * @param to_node next node to forward message to
191 *
192 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
193 */
194int
195add_route (unsigned long long *sqlqueryuid, unsigned long long queryid,
196 unsigned int type, unsigned int hops, int succeeded,
197 const struct GNUNET_PeerIdentity *node, const GNUNET_HashCode * key,
198 const struct GNUNET_PeerIdentity *from_node,
199 const struct GNUNET_PeerIdentity *to_node)
200{
201 *sqlqueryuid = 18;
202 return GNUNET_OK;
203}
204
205
206/*
207 * Records the current topology (number of connections, time, trial)
208 *
209 * @param num_connections how many connections are in the topology
210 *
211 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
212 */
213int
214add_topology (int num_connections)
215{
216 return GNUNET_OK;
217}
218
219/*
220 * Records a connection between two peers in the current topology
221 *
222 * @param first one side of the connection
223 * @param second other side of the connection
224 *
225 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
226 */
227int
228add_extended_topology (const struct GNUNET_PeerIdentity *first,
229 const struct GNUNET_PeerIdentity *second)
230{
231 return GNUNET_OK;
232}
233
234/*
235 * Update dhttests.topology table with total connections information
236 *
237 * @param totalConnections the number of connections
238 *
239 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
240 */
241int
242update_topology (unsigned int connections)
243{
244 return GNUNET_OK;
245}
246
247/*
248 * Update dhttests.nodes table setting the identified
249 * node as a malicious dropper.
250 *
251 * @param peer the peer that was set to be malicious
252 *
253 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
254 */
255int
256set_malicious (struct GNUNET_PeerIdentity *peer)
257{
258 return GNUNET_OK;
259}
260
261/*
262 * Inserts the specified stats into the dhttests.node_statistics table
263 *
264 * @param peer the peer inserting the statistic
265 * @param route_requests route requests seen
266 * @param route_forwards route requests forwarded
267 * @param result_requests route result requests seen
268 * @param client_requests client requests initiated
269 * @param result_forwards route results forwarded
270 * @param gets get requests handled
271 * @param puts put requests handle
272 * @param data_inserts data inserted at this node
273 * @param find_peer_requests find peer requests seen
274 * @param find_peers_started find peer requests initiated at this node
275 * @param gets_started get requests initiated at this node
276 * @param puts_started put requests initiated at this node
277 * @param find_peer_responses_received find peer responses received locally
278 * @param get_responses_received get responses received locally
279 * @param find_peer_responses_sent find peer responses sent from this node
280 * @param get_responses_sent get responses sent from this node
281 *
282 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
283 */
284int
285insert_stat (const struct GNUNET_PeerIdentity *peer,
286 unsigned int route_requests, unsigned int route_forwards,
287 unsigned int result_requests, unsigned int client_requests,
288 unsigned int result_forwards, unsigned int gets, unsigned int puts,
289 unsigned int data_inserts, unsigned int find_peer_requests,
290 unsigned int find_peers_started, unsigned int gets_started,
291 unsigned int puts_started,
292 unsigned int find_peer_responses_received,
293 unsigned int get_responses_received,
294 unsigned int find_peer_responses_sent,
295 unsigned int get_responses_sent)
296{
297 return GNUNET_OK;
298}
299
300/*
301 * Provides the dhtlog api
302 *
303 * @param c the configuration to use to connect to a server
304 *
305 * @return the handle to the server, or NULL on error
306 */
307void *
308libgnunet_plugin_dhtlog_dummy_init (void *cls)
309{
310 struct GNUNET_DHTLOG_Plugin *plugin = cls;
311
312#if DEBUG_DHTLOG
313 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "DUMMY DHT Logger: initializing.\n");
314#endif
315 GNUNET_assert (plugin->dhtlog_api == NULL);
316 plugin->dhtlog_api = GNUNET_malloc (sizeof (struct GNUNET_DHTLOG_Handle));
317 plugin->dhtlog_api->add_generic_stat = &add_generic_stat;
318 plugin->dhtlog_api->insert_round = &add_round;
319 plugin->dhtlog_api->insert_round_details = &add_round_details;
320 plugin->dhtlog_api->insert_stat = &insert_stat;
321 plugin->dhtlog_api->insert_trial = &add_trial;
322 plugin->dhtlog_api->insert_query = &add_query;
323 plugin->dhtlog_api->update_trial = &update_trials;
324 plugin->dhtlog_api->set_malicious = &set_malicious;
325 plugin->dhtlog_api->insert_route = &add_route;
326 plugin->dhtlog_api->insert_node = &add_node;
327 plugin->dhtlog_api->insert_dhtkey = &add_dhtkey;
328 plugin->dhtlog_api->update_connections = &add_connections;
329 plugin->dhtlog_api->insert_topology = &add_topology;
330 plugin->dhtlog_api->update_topology = &update_topology;
331 plugin->dhtlog_api->insert_extended_topology = &add_extended_topology;
332 return plugin;
333}
334
335/**
336 * Shutdown the plugin.
337 */
338void *
339libgnunet_plugin_dhtlog_dummy_done (void *cls)
340{
341#if DEBUG_DHTLOG
342 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "DUMMY DHT Logger: shutdown\n");
343#endif
344 return NULL;
345}
346
347/* end of plugin_dhtlog_dummy.c */
diff --git a/src/dht/plugin_dhtlog_mysql.c b/src/dht/plugin_dhtlog_mysql.c
deleted file mode 100644
index 33028fb09..000000000
--- a/src/dht/plugin_dhtlog_mysql.c
+++ /dev/null
@@ -1,1612 +0,0 @@
1/*
2 This file is part of GNUnet.
3 (C) 2006 - 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/dht/plugin_dhtlog_mysql.c
23 * @brief MySQL logging plugin to record DHT operations to MySQL server
24 * @author Nathan Evans
25 *
26 * Database: MySQL
27 */
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31#include "dhtlog.h"
32#include <mysql/mysql.h>
33
34
35#define DEBUG_DHTLOG GNUNET_YES
36
37/**
38 * Maximum number of supported parameters for a prepared
39 * statement. Increase if needed.
40 */
41#define MAX_PARAM 32
42
43/**
44 * A generic statement handle to use
45 * for prepared statements. This way,
46 * once the statement is initialized
47 * we don't redo work.
48 */
49struct StatementHandle
50{
51 /**
52 * Internal statement
53 */
54 MYSQL_STMT *statement;
55
56 /**
57 * Textual query
58 */
59 char *query;
60
61 /**
62 * Whether or not the handle is valid
63 */
64 int valid;
65};
66
67/**
68 * Type of a callback that will be called for each
69 * data set returned from MySQL.
70 *
71 * @param cls user-defined argument
72 * @param num_values number of elements in values
73 * @param values values returned by MySQL
74 * @return GNUNET_OK to continue iterating, GNUNET_SYSERR to abort
75 */
76typedef int (*GNUNET_MysqlDataProcessor) (void *cls, unsigned int num_values,
77 MYSQL_BIND * values);
78
79static unsigned long max_varchar_len;
80
81/**
82 * The configuration the DHT service is running with
83 */
84static const struct GNUNET_CONFIGURATION_Handle *cfg;
85
86static unsigned long long current_trial = 0; /* I like to assign 0, just to remember */
87
88/**
89 * Connection to the MySQL Server.
90 */
91static MYSQL *conn;
92
93#define INSERT_QUERIES_STMT "INSERT INTO queries (trialuid, querytype, hops, dhtkeyuid, dhtqueryid, succeeded, nodeuid, time) "\
94 "VALUES (?, ?, ?, ?, ?, ?, ?, NOW())"
95static struct StatementHandle *insert_query;
96
97#define INSERT_ROUTES_STMT "INSERT INTO routes (trialuid, querytype, hops, dhtkeyuid, dhtqueryid, succeeded, nodeuid, from_node, to_node) "\
98 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
99static struct StatementHandle *insert_route;
100
101#define INSERT_NODES_STMT "INSERT INTO nodes (trialuid, nodeid, nodebits) "\
102 "VALUES (?, ?, ?)"
103static struct StatementHandle *insert_node;
104
105#define INSERT_ROUNDS_STMT "INSERT INTO rounds (trialuid, round_type, round_count, starttime) "\
106 "VALUES (?, ?, ?, NOW())"
107
108static struct StatementHandle *insert_round;
109
110#define INSERT_ROUND_DETAILS_STMT "INSERT INTO rounds (trialuid, round_type, round_count, starttime, endtime, num_messages, num_messages_succeeded) "\
111 "VALUES (?, ?, ?, NOW(), NOW(), ?, ?)"
112
113static struct StatementHandle *insert_round_details;
114
115#define INSERT_TRIALS_STMT "INSERT INTO trials"\
116 "(starttime, other_trial_identifier, numnodes, topology,"\
117 "topology_percentage, topology_probability,"\
118 "blacklist_topology, connect_topology, connect_topology_option,"\
119 "connect_topology_option_modifier, puts, gets, "\
120 "concurrent, settle_time, num_rounds, malicious_getters,"\
121 "malicious_putters, malicious_droppers, malicious_get_frequency,"\
122 "malicious_put_frequency, stop_closest, stop_found, strict_kademlia, "\
123 "gets_succeeded, message) "\
124 "VALUES (NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
125
126static struct StatementHandle *insert_trial;
127
128#define INSERT_STAT_STMT "INSERT INTO node_statistics"\
129 "(trialuid, nodeuid, route_requests,"\
130 "route_forwards, result_requests,"\
131 "client_results, result_forwards, gets,"\
132 "puts, data_inserts, find_peer_requests, "\
133 "find_peers_started, gets_started, puts_started, find_peer_responses_received,"\
134 "get_responses_received, find_peer_responses_sent, get_responses_sent) "\
135 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
136
137static struct StatementHandle *insert_stat;
138
139#define INSERT_GENERIC_STAT_STMT "INSERT INTO generic_stats" \
140 "(trialuid, nodeuid, section, name, value)"\
141 "VALUES (?, ?, ?, ?, ?)"
142static struct StatementHandle *insert_generic_stat;
143
144#define INSERT_DHTKEY_STMT "INSERT INTO dhtkeys (dhtkey, trialuid, keybits) "\
145 "VALUES (?, ?, ?)"
146static struct StatementHandle *insert_dhtkey;
147
148#define UPDATE_TRIALS_STMT "UPDATE trials set endtime=NOW(), gets_succeeded = ? where trialuid = ?"
149static struct StatementHandle *update_trial;
150
151#define UPDATE_CONNECTIONS_STMT "UPDATE trials set totalConnections = ? where trialuid = ?"
152static struct StatementHandle *update_connection;
153
154#define GET_TRIAL_STMT "SELECT MAX( trialuid ) FROM trials"
155static struct StatementHandle *get_trial;
156
157#define GET_TOPOLOGY_STMT "SELECT MAX( topology_uid ) FROM topology"
158static struct StatementHandle *get_topology;
159
160#define GET_DHTKEYUID_STMT "SELECT dhtkeyuid FROM dhtkeys where dhtkey = ? and trialuid = ?"
161static struct StatementHandle *get_dhtkeyuid;
162
163#define GET_NODEUID_STMT "SELECT nodeuid FROM nodes where trialuid = ? and nodeid = ?"
164static struct StatementHandle *get_nodeuid;
165
166#define INSERT_TOPOLOGY_STMT "INSERT INTO topology (trialuid, date, connections) "\
167 "VALUES (?, NOW(), ?)"
168static struct StatementHandle *insert_topology;
169
170#define EXTEND_TOPOLOGY_STMT "INSERT INTO extended_topology (topology_uid, uid_first, uid_second) "\
171 "VALUES (?, ?, ?)"
172static struct StatementHandle *extend_topology;
173
174#define SET_MALICIOUS_STMT "update nodes set malicious_dropper = 1 where trialuid = ? and nodeid = ?"
175static struct StatementHandle *update_node_malicious;
176
177#define UPDATE_TOPOLOGY_STMT "update topology set connections = ? where topology_uid = ?"
178static struct StatementHandle *update_topology;
179
180/**
181 * Run a query (not a select statement)
182 *
183 * @return GNUNET_OK if executed, GNUNET_SYSERR if an error occurred
184 */
185int
186run_statement (const char *statement)
187{
188 mysql_query (conn, statement);
189 if (mysql_error (conn)[0])
190 {
191 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "mysql_query");
192 return GNUNET_SYSERR;
193 }
194 return GNUNET_OK;
195}
196
197/*
198 * Creates tables if they don't already exist for dht logging
199 */
200static int
201itable ()
202{
203#define MRUNS(a) (GNUNET_OK != run_statement (a) )
204
205 if (MRUNS
206 ("CREATE TABLE IF NOT EXISTS `dhtkeys` ("
207 "dhtkeyuid int(10) unsigned NOT NULL auto_increment COMMENT 'Unique Key given to each query',"
208 "`dhtkey` varchar(255) NOT NULL COMMENT 'The ASCII value of the key being searched for',"
209 "trialuid int(10) unsigned NOT NULL," "keybits blob NOT NULL,"
210 "UNIQUE KEY `dhtkeyuid` (`dhtkeyuid`)"
211 ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
212 return GNUNET_SYSERR;
213
214 if (MRUNS
215 ("CREATE TABLE IF NOT EXISTS `nodes` ("
216 "`nodeuid` int(10) unsigned NOT NULL auto_increment,"
217 "`trialuid` int(10) unsigned NOT NULL," "`nodeid` varchar(255) NOT NULL,"
218 "`nodebits` blob NOT NULL," "PRIMARY KEY (`nodeuid`)"
219 ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
220 return GNUNET_SYSERR;
221
222 if (MRUNS
223 ("CREATE TABLE IF NOT EXISTS `queries` ("
224 "`trialuid` int(10) unsigned NOT NULL,"
225 "`queryuid` int(10) unsigned NOT NULL auto_increment,"
226 "`dhtqueryid` bigint(20) NOT NULL,"
227 "`querytype` enum('1','2','3','4','5') NOT NULL,"
228 "`hops` int(10) unsigned NOT NULL," "`succeeded` tinyint NOT NULL,"
229 "`nodeuid` int(10) unsigned NOT NULL,"
230 "`time` timestamp NOT NULL default CURRENT_TIMESTAMP,"
231 "`dhtkeyuid` int(10) unsigned NOT NULL," "PRIMARY KEY (`queryuid`)"
232 ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
233 return GNUNET_SYSERR;
234
235 if (MRUNS
236 ("CREATE TABLE IF NOT EXISTS `routes` ("
237 "`trialuid` int(10) unsigned NOT NULL,"
238 "`queryuid` int(10) unsigned NOT NULL auto_increment,"
239 "`dhtqueryid` bigint(20) NOT NULL,"
240 "`querytype` enum('1','2','3','4','5') NOT NULL,"
241 "`hops` int(10) unsigned NOT NULL," "`succeeded` tinyint NOT NULL,"
242 "`nodeuid` int(10) unsigned NOT NULL,"
243 "`time` timestamp NOT NULL default CURRENT_TIMESTAMP,"
244 "`dhtkeyuid` int(10) unsigned NOT NULL,"
245 "`from_node` int(10) unsigned NOT NULL,"
246 "`to_node` int(10) unsigned NOT NULL," "PRIMARY KEY (`queryuid`)"
247 ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
248 return GNUNET_SYSERR;
249
250 if (MRUNS
251 ("CREATE TABLE IF NOT EXISTS `trials` ("
252 "`trialuid` int(10) unsigned NOT NULL auto_increment,"
253 "`other_trial_identifier` int(10) unsigned NOT NULL default '0',"
254 "`numnodes` int(10) unsigned NOT NULL," "`topology` int(10) NOT NULL,"
255 "`blacklist_topology` int(11) NOT NULL,"
256 "`connect_topology` int(11) NOT NULL,"
257 "`connect_topology_option` int(11) NOT NULL,"
258 "`topology_percentage` float NOT NULL,"
259 "`topology_probability` float NOT NULL,"
260 "`connect_topology_option_modifier` float NOT NULL,"
261 "`starttime` datetime NOT NULL," "`endtime` datetime NOT NULL,"
262 "`puts` int(10) unsigned NOT NULL," "`gets` int(10) unsigned NOT NULL,"
263 "`concurrent` int(10) unsigned NOT NULL,"
264 "`settle_time` int(10) unsigned NOT NULL,"
265 "`totalConnections` int(10) unsigned NOT NULL,"
266 "`message` text NOT NULL," "`num_rounds` int(10) unsigned NOT NULL,"
267 "`malicious_getters` int(10) unsigned NOT NULL,"
268 "`malicious_putters` int(10) unsigned NOT NULL,"
269 "`malicious_droppers` int(10) unsigned NOT NULL,"
270 "`topology_modifier` double NOT NULL,"
271 "`malicious_get_frequency` int(10) unsigned NOT NULL,"
272 "`malicious_put_frequency` int(10) unsigned NOT NULL,"
273 "`stop_closest` int(10) unsigned NOT NULL,"
274 "`stop_found` int(10) unsigned NOT NULL,"
275 "`strict_kademlia` int(10) unsigned NOT NULL,"
276 "`gets_succeeded` int(10) unsigned NOT NULL,"
277 "PRIMARY KEY (`trialuid`)," "UNIQUE KEY `trialuid` (`trialuid`)"
278 ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
279 return GNUNET_SYSERR;
280
281 if (MRUNS
282 ("CREATE TABLE IF NOT EXISTS `topology` ("
283 "`topology_uid` int(10) unsigned NOT NULL AUTO_INCREMENT,"
284 "`trialuid` int(10) unsigned NOT NULL," "`date` datetime NOT NULL,"
285 "`connections` int(10) unsigned NOT NULL,"
286 "PRIMARY KEY (`topology_uid`)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
287 return GNUNET_SYSERR;
288
289 if (MRUNS
290 ("CREATE TABLE IF NOT EXISTS `extended_topology` ("
291 "`extended_uid` int(10) unsigned NOT NULL AUTO_INCREMENT,"
292 "`topology_uid` int(10) unsigned NOT NULL,"
293 "`uid_first` int(10) unsigned NOT NULL,"
294 "`uid_second` int(10) unsigned NOT NULL," "PRIMARY KEY (`extended_uid`)"
295 ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
296 return GNUNET_SYSERR;
297
298 if (MRUNS
299 ("CREATE TABLE IF NOT EXISTS `node_statistics` ("
300 "`stat_uid` int(10) unsigned NOT NULL AUTO_INCREMENT,"
301 "`trialuid` int(10) unsigned NOT NULL,"
302 "`nodeuid` int(10) unsigned NOT NULL,"
303 "`route_requests` int(10) unsigned NOT NULL,"
304 "`route_forwards` int(10) unsigned NOT NULL,"
305 "`result_requests` int(10) unsigned NOT NULL,"
306 "`client_results` int(10) unsigned NOT NULL,"
307 "`result_forwards` int(10) unsigned NOT NULL,"
308 "`gets` int(10) unsigned NOT NULL," "`puts` int(10) unsigned NOT NULL,"
309 "`data_inserts` int(10) unsigned NOT NULL,"
310 "`find_peer_requests` int(10) unsigned NOT NULL,"
311 "`find_peers_started` int(10) unsigned NOT NULL,"
312 "`gets_started` int(10) unsigned NOT NULL,"
313 "`puts_started` int(10) unsigned NOT NULL,"
314 "`find_peer_responses_received` int(10) unsigned NOT NULL,"
315 "`get_responses_received` int(10) unsigned NOT NULL,"
316 "`find_peer_responses_sent` int(10) unsigned NOT NULL,"
317 "`get_responses_sent` int(10) unsigned NOT NULL,"
318 "PRIMARY KEY (`stat_uid`)"
319 ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;"))
320 return GNUNET_SYSERR;
321
322 if (MRUNS ("SET AUTOCOMMIT = 1"))
323 return GNUNET_SYSERR;
324
325 return GNUNET_OK;
326#undef MRUNS
327}
328
329/**
330 * Create a prepared statement.
331 *
332 * @return NULL on error
333 */
334struct StatementHandle *
335prepared_statement_create (const char *statement)
336{
337 struct StatementHandle *ret;
338
339 ret = GNUNET_malloc (sizeof (struct StatementHandle));
340 ret->query = GNUNET_strdup (statement);
341 return ret;
342}
343
344/**
345 * Close a prepared statement.
346 *
347 * @return NULL on error
348 */
349void
350prepared_statement_close (struct StatementHandle *s)
351{
352 if (s == NULL)
353 {
354 return;
355 }
356
357 GNUNET_free_non_null (s->query);
358
359 if (s->valid == GNUNET_YES)
360 mysql_stmt_close (s->statement);
361 GNUNET_free (s);
362}
363
364/*
365 * Initialize the prepared statements for use with dht test logging
366 */
367static int
368iopen (struct GNUNET_DHTLOG_Plugin *plugin)
369{
370 int ret;
371 my_bool reconnect;
372 unsigned int timeout;
373 char *user;
374 char *password;
375 char *server;
376 char *database;
377 unsigned long long port;
378
379 conn = mysql_init (NULL);
380 if (conn == NULL)
381 return GNUNET_SYSERR;
382
383 if (GNUNET_OK !=
384 GNUNET_CONFIGURATION_get_value_string (plugin->cfg, "MYSQL", "DATABASE",
385 &database))
386 {
387 database = GNUNET_strdup ("gnunet");
388 }
389
390 if (GNUNET_OK !=
391 GNUNET_CONFIGURATION_get_value_string (plugin->cfg, "MYSQL", "USER",
392 &user))
393 {
394 user = GNUNET_strdup ("dht");
395 }
396
397 if (GNUNET_OK !=
398 GNUNET_CONFIGURATION_get_value_string (plugin->cfg, "MYSQL", "PASSWORD",
399 &password))
400 {
401 password = GNUNET_strdup ("dhttest**");
402 }
403
404 if (GNUNET_OK !=
405 GNUNET_CONFIGURATION_get_value_string (plugin->cfg, "MYSQL", "SERVER",
406 &server))
407 {
408 server = GNUNET_strdup ("localhost");
409 }
410
411 if (GNUNET_OK !=
412 GNUNET_CONFIGURATION_get_value_number (plugin->cfg, "MYSQL", "MYSQL_PORT",
413 &port))
414 {
415 port = 0;
416 }
417
418 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
419 "Connecting to mysql with: user %s, pass %s, server %s, database %s, port %d\n",
420 user, password, server, database, port);
421
422 reconnect = 0;
423 timeout = 60; /* in seconds */
424 mysql_options (conn, MYSQL_OPT_RECONNECT, &reconnect);
425 mysql_options (conn, MYSQL_OPT_CONNECT_TIMEOUT, (const void *) &timeout);
426 mysql_options (conn, MYSQL_SET_CHARSET_NAME, "UTF8");
427 mysql_options (conn, MYSQL_OPT_READ_TIMEOUT, (const void *) &timeout);
428 mysql_options (conn, MYSQL_OPT_WRITE_TIMEOUT, (const void *) &timeout);
429 mysql_real_connect (conn, server, user, password, database,
430 (unsigned int) port, NULL, CLIENT_IGNORE_SIGPIPE);
431
432 GNUNET_free_non_null (server);
433 GNUNET_free_non_null (password);
434 GNUNET_free_non_null (user);
435 GNUNET_free_non_null (database);
436
437 if (mysql_error (conn)[0])
438 {
439 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "mysql_real_connect");
440 return GNUNET_SYSERR;
441 }
442
443#if OLD
444 db = GNUNET_MYSQL_database_open (coreAPI->ectx, coreAPI->cfg);
445 if (db == NULL)
446 return GNUNET_SYSERR;
447#endif
448
449 ret = itable ();
450
451#define PINIT(a,b) (NULL == (a = prepared_statement_create(b)))
452 if (PINIT (insert_query, INSERT_QUERIES_STMT) ||
453 PINIT (insert_route, INSERT_ROUTES_STMT) ||
454 PINIT (insert_trial, INSERT_TRIALS_STMT) ||
455 PINIT (insert_round, INSERT_ROUNDS_STMT) ||
456 PINIT (insert_round_details, INSERT_ROUND_DETAILS_STMT) ||
457 PINIT (insert_stat, INSERT_STAT_STMT) ||
458 PINIT (insert_generic_stat, INSERT_GENERIC_STAT_STMT) ||
459 PINIT (insert_node, INSERT_NODES_STMT) ||
460 PINIT (insert_dhtkey, INSERT_DHTKEY_STMT) ||
461 PINIT (update_trial, UPDATE_TRIALS_STMT) ||
462 PINIT (get_dhtkeyuid, GET_DHTKEYUID_STMT) ||
463 PINIT (get_nodeuid, GET_NODEUID_STMT) ||
464 PINIT (update_connection, UPDATE_CONNECTIONS_STMT) ||
465 PINIT (get_trial, GET_TRIAL_STMT) ||
466 PINIT (get_topology, GET_TOPOLOGY_STMT) ||
467 PINIT (insert_topology, INSERT_TOPOLOGY_STMT) ||
468 PINIT (update_topology, UPDATE_TOPOLOGY_STMT) ||
469 PINIT (extend_topology, EXTEND_TOPOLOGY_STMT) ||
470 PINIT (update_node_malicious, SET_MALICIOUS_STMT))
471 {
472 return GNUNET_SYSERR;
473 }
474#undef PINIT
475
476 return ret;
477}
478
479static int
480return_ok (void *cls, unsigned int num_values, MYSQL_BIND * values)
481{
482 return GNUNET_OK;
483}
484
485/**
486 * Prepare a statement for running.
487 *
488 * @return GNUNET_OK on success
489 */
490static int
491prepare_statement (struct StatementHandle *ret)
492{
493 if (GNUNET_YES == ret->valid)
494 return GNUNET_OK;
495
496 ret->statement = mysql_stmt_init (conn);
497 if (ret->statement == NULL)
498 return GNUNET_SYSERR;
499
500 if (mysql_stmt_prepare (ret->statement, ret->query, strlen (ret->query)))
501 {
502 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "mysql_stmt_prepare `%s', %s",
503 ret->query, mysql_error (conn));
504 mysql_stmt_close (ret->statement);
505 ret->statement = NULL;
506 return GNUNET_SYSERR;
507 }
508 ret->valid = GNUNET_YES;
509 return GNUNET_OK;
510}
511
512/**
513 * Bind the parameters for the given MySQL statement
514 * and run it.
515 *
516 * @param s statement to bind and run
517 * @param ap arguments for the binding
518 * @return GNUNET_SYSERR on error, GNUNET_OK on success
519 */
520static int
521init_params (struct StatementHandle *s, va_list ap)
522{
523 MYSQL_BIND qbind[MAX_PARAM];
524 unsigned int pc;
525 unsigned int off;
526 enum enum_field_types ft;
527
528 pc = mysql_stmt_param_count (s->statement);
529 if (pc > MAX_PARAM)
530 {
531 /* increase internal constant! */
532 GNUNET_break (0);
533 return GNUNET_SYSERR;
534 }
535 memset (qbind, 0, sizeof (qbind));
536 off = 0;
537 ft = 0;
538 while ((pc > 0) && (-1 != (ft = va_arg (ap, enum enum_field_types))))
539 {
540 qbind[off].buffer_type = ft;
541 switch (ft)
542 {
543 case MYSQL_TYPE_FLOAT:
544 qbind[off].buffer = va_arg (ap, float *);
545
546 break;
547 case MYSQL_TYPE_LONGLONG:
548 qbind[off].buffer = va_arg (ap, unsigned long long *);
549 qbind[off].is_unsigned = va_arg (ap, int);
550
551 break;
552 case MYSQL_TYPE_LONG:
553 qbind[off].buffer = va_arg (ap, unsigned int *);
554 qbind[off].is_unsigned = va_arg (ap, int);
555
556 break;
557 case MYSQL_TYPE_VAR_STRING:
558 case MYSQL_TYPE_STRING:
559 case MYSQL_TYPE_BLOB:
560 qbind[off].buffer = va_arg (ap, void *);
561 qbind[off].buffer_length = va_arg (ap, unsigned long);
562 qbind[off].length = va_arg (ap, unsigned long *);
563
564 break;
565 default:
566 /* unsupported type */
567 GNUNET_break (0);
568 return GNUNET_SYSERR;
569 }
570 pc--;
571 off++;
572 }
573 if (!((pc == 0) && (ft != -1) && (va_arg (ap, int) == -1)))
574 {
575 GNUNET_break (0);
576 return GNUNET_SYSERR;
577 }
578 if (mysql_stmt_bind_param (s->statement, qbind))
579 {
580 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
581 _("`%s' failed at %s:%d with error: %s\n"),
582 "mysql_stmt_bind_param", __FILE__, __LINE__,
583 mysql_stmt_error (s->statement));
584 return GNUNET_SYSERR;
585 }
586
587 if (mysql_stmt_execute (s->statement))
588 {
589 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
590 _("`%s' failed at %s:%d with error: %s\n"),
591 "mysql_stmt_execute", __FILE__, __LINE__,
592 mysql_stmt_error (s->statement));
593 return GNUNET_SYSERR;
594 }
595
596 return GNUNET_OK;
597}
598
599/**
600 * Run a prepared SELECT statement.
601 *
602 * @param s handle to the statement we should execute
603 * @param result_size number of results in set
604 * @param results pointer to already initialized MYSQL_BIND
605 * array (of sufficient size) for passing results
606 * @param processor function to call on each result
607 * @param processor_cls extra argument to processor
608 * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
609 * values (size + buffer-reference for pointers); terminated
610 * with "-1"
611 *
612 * @return GNUNET_SYSERR on error, otherwise
613 * the number of successfully affected (or queried) rows
614 */
615int
616prepared_statement_run_select (struct StatementHandle *s,
617 unsigned int result_size, MYSQL_BIND * results,
618 GNUNET_MysqlDataProcessor processor,
619 void *processor_cls, ...)
620{
621 va_list ap;
622 int ret;
623 unsigned int rsize;
624 int total;
625
626 if (GNUNET_OK != prepare_statement (s))
627 {
628 GNUNET_break (0);
629 return GNUNET_SYSERR;
630 }
631 va_start (ap, processor_cls);
632 if (GNUNET_OK != init_params (s, ap))
633 {
634 GNUNET_break (0);
635 va_end (ap);
636 return GNUNET_SYSERR;
637 }
638 va_end (ap);
639 rsize = mysql_stmt_field_count (s->statement);
640 if (rsize > result_size)
641 {
642 GNUNET_break (0);
643 return GNUNET_SYSERR;
644 }
645 if (mysql_stmt_bind_result (s->statement, results))
646 {
647 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
648 _("`%s' failed at %s:%d with error: %s\n"),
649 "mysql_stmt_bind_result", __FILE__, __LINE__,
650 mysql_stmt_error (s->statement));
651 return GNUNET_SYSERR;
652 }
653
654 total = 0;
655 while (1)
656 {
657 ret = mysql_stmt_fetch (s->statement);
658 if (ret == MYSQL_NO_DATA)
659 break;
660 if (ret != 0)
661 {
662 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
663 _("`%s' failed at %s:%d with error: %s\n"),
664 "mysql_stmt_fetch", __FILE__, __LINE__,
665 mysql_stmt_error (s->statement));
666 return GNUNET_SYSERR;
667 }
668 if (processor != NULL)
669 if (GNUNET_OK != processor (processor_cls, rsize, results))
670 break;
671 total++;
672 }
673 mysql_stmt_reset (s->statement);
674 return total;
675}
676
677
678static int
679get_node_uid (unsigned long long *nodeuid, const GNUNET_HashCode * peerHash)
680{
681 MYSQL_BIND rbind[1];
682 struct GNUNET_CRYPTO_HashAsciiEncoded encPeer;
683 unsigned long long p_len;
684
685 memset (rbind, 0, sizeof (rbind));
686 rbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
687 rbind[0].buffer = nodeuid;
688 rbind[0].is_unsigned = GNUNET_YES;
689
690 GNUNET_CRYPTO_hash_to_enc (peerHash, &encPeer);
691 p_len = strlen ((char *) &encPeer);
692
693 if (1 !=
694 prepared_statement_run_select (get_nodeuid, 1, rbind, return_ok, NULL,
695 MYSQL_TYPE_LONGLONG, &current_trial,
696 GNUNET_YES, MYSQL_TYPE_VAR_STRING,
697 &encPeer, max_varchar_len, &p_len, -1))
698 {
699#if DEBUG_DHTLOG
700 fprintf (stderr, "FAILED\n");
701#endif
702 return GNUNET_SYSERR;
703 }
704 return GNUNET_OK;
705}
706
707static int
708get_current_trial (unsigned long long *trialuid)
709{
710 MYSQL_BIND rbind[1];
711
712 memset (rbind, 0, sizeof (rbind));
713 rbind[0].buffer_type = MYSQL_TYPE_LONG;
714 rbind[0].is_unsigned = 1;
715 rbind[0].buffer = trialuid;
716
717 if ((GNUNET_OK !=
718 prepared_statement_run_select (get_trial, 1, rbind, return_ok, NULL,
719 -1)))
720 {
721 return GNUNET_SYSERR;
722 }
723
724 return GNUNET_OK;
725}
726
727static int
728get_current_topology (unsigned long long *topologyuid)
729{
730 MYSQL_BIND rbind[1];
731
732 memset (rbind, 0, sizeof (rbind));
733 rbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
734 rbind[0].is_unsigned = 1;
735 rbind[0].buffer = topologyuid;
736
737 if ((GNUNET_OK !=
738 prepared_statement_run_select (get_topology, 1, rbind, return_ok, NULL,
739 -1)))
740 {
741 return GNUNET_SYSERR;
742 }
743
744 return GNUNET_OK;
745}
746
747static int
748get_dhtkey_uid (unsigned long long *dhtkeyuid, const GNUNET_HashCode * key)
749{
750 MYSQL_BIND rbind[1];
751 struct GNUNET_CRYPTO_HashAsciiEncoded encKey;
752 unsigned long long k_len;
753
754 memset (rbind, 0, sizeof (rbind));
755 rbind[0].buffer_type = MYSQL_TYPE_LONG;
756 rbind[0].is_unsigned = 1;
757 rbind[0].buffer = dhtkeyuid;
758 GNUNET_CRYPTO_hash_to_enc (key, &encKey);
759 k_len = strlen ((char *) &encKey);
760#if DEBUG_DHTLOG
761 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
762 "Searching for dhtkey `%s' in trial %llu\n", GNUNET_h2s (key),
763 current_trial);
764#endif
765 if ((GNUNET_OK !=
766 prepared_statement_run_select (get_dhtkeyuid, 1, rbind, return_ok, NULL,
767 MYSQL_TYPE_VAR_STRING, &encKey,
768 max_varchar_len, &k_len,
769 MYSQL_TYPE_LONGLONG, &current_trial,
770 GNUNET_YES, -1)))
771 {
772 return GNUNET_SYSERR;
773 }
774
775 return GNUNET_OK;
776}
777
778/**
779 * Run a prepared statement that does NOT produce results.
780 *
781 * @param s handle to the statement we should execute
782 * @param insert_id NULL or address where to store the row ID of whatever
783 * was inserted (only for INSERT statements!)
784 * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
785 * values (size + buffer-reference for pointers); terminated
786 * with "-1"
787 *
788 * @return GNUNET_SYSERR on error, otherwise
789 * the number of successfully affected rows
790 */
791int
792prepared_statement_run (struct StatementHandle *s,
793 unsigned long long *insert_id, ...)
794{
795 va_list ap;
796 int affected;
797
798 if (GNUNET_OK != prepare_statement (s))
799 {
800 GNUNET_break (0);
801 return GNUNET_SYSERR;
802 }
803 GNUNET_assert (s->valid == GNUNET_YES);
804 if (s->statement == NULL)
805 return GNUNET_SYSERR;
806
807 va_start (ap, insert_id);
808
809 if (GNUNET_OK != init_params (s, ap))
810 {
811 va_end (ap);
812 return GNUNET_SYSERR;
813 }
814
815 va_end (ap);
816 affected = mysql_stmt_affected_rows (s->statement);
817 if (NULL != insert_id)
818 *insert_id = (unsigned long long) mysql_stmt_insert_id (s->statement);
819 mysql_stmt_reset (s->statement);
820
821 return affected;
822}
823
824/*
825 * Inserts the specified trial into the dhttests.trials table
826 *
827 * @param trial_info struct containing the data to insert about this trial
828 *
829 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
830 */
831int
832add_trial (struct GNUNET_DHTLOG_TrialInfo *trial_info)
833{
834 MYSQL_STMT *stmt;
835 int ret;
836 unsigned long long m_len;
837
838 m_len = strlen (trial_info->message);
839
840 stmt = mysql_stmt_init (conn);
841 if (GNUNET_OK !=
842 (ret =
843 prepared_statement_run (insert_trial, &current_trial, MYSQL_TYPE_LONG,
844 &trial_info->other_identifier, GNUNET_YES,
845 MYSQL_TYPE_LONG, &trial_info->num_nodes,
846 GNUNET_YES, MYSQL_TYPE_LONG,
847 &trial_info->topology, GNUNET_YES,
848 MYSQL_TYPE_FLOAT,
849 &trial_info->topology_percentage,
850 MYSQL_TYPE_FLOAT,
851 &trial_info->topology_probability,
852 MYSQL_TYPE_LONG, &trial_info->blacklist_topology,
853 GNUNET_YES, MYSQL_TYPE_LONG,
854 &trial_info->connect_topology, GNUNET_YES,
855 MYSQL_TYPE_LONG,
856 &trial_info->connect_topology_option, GNUNET_YES,
857 MYSQL_TYPE_FLOAT,
858 &trial_info->connect_topology_option_modifier,
859 MYSQL_TYPE_LONG, &trial_info->puts, GNUNET_YES,
860 MYSQL_TYPE_LONG, &trial_info->gets, GNUNET_YES,
861 MYSQL_TYPE_LONG, &trial_info->concurrent,
862 GNUNET_YES, MYSQL_TYPE_LONG,
863 &trial_info->settle_time, GNUNET_YES,
864 MYSQL_TYPE_LONG, &trial_info->num_rounds,
865 GNUNET_YES, MYSQL_TYPE_LONG,
866 &trial_info->malicious_getters, GNUNET_YES,
867 MYSQL_TYPE_LONG, &trial_info->malicious_putters,
868 GNUNET_YES, MYSQL_TYPE_LONG,
869 &trial_info->malicious_droppers, GNUNET_YES,
870 MYSQL_TYPE_LONG,
871 &trial_info->malicious_get_frequency, GNUNET_YES,
872 MYSQL_TYPE_LONG,
873 &trial_info->malicious_put_frequency, GNUNET_YES,
874 MYSQL_TYPE_LONG, &trial_info->stop_closest,
875 GNUNET_YES, MYSQL_TYPE_LONG,
876 &trial_info->stop_found, GNUNET_YES,
877 MYSQL_TYPE_LONG, &trial_info->strict_kademlia,
878 GNUNET_YES, MYSQL_TYPE_LONG,
879 &trial_info->gets_succeeded, GNUNET_YES,
880 MYSQL_TYPE_BLOB, trial_info->message,
881 max_varchar_len + max_varchar_len, &m_len, -1)))
882 {
883 if (ret == GNUNET_SYSERR)
884 {
885 mysql_stmt_close (stmt);
886 return GNUNET_SYSERR;
887 }
888 }
889
890 get_current_trial (&current_trial);
891
892 mysql_stmt_close (stmt);
893 return GNUNET_OK;
894}
895
896/*
897 * Inserts the specified round into the dhttests.rounds table
898 *
899 * @param round_type the type of round that is being started
900 * @param round_count counter for the round (if applicable)
901 *
902 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
903 */
904int
905add_round (unsigned int round_type, unsigned int round_count)
906{
907
908 MYSQL_STMT *stmt;
909 int ret;
910
911 stmt = mysql_stmt_init (conn);
912 ret =
913 prepared_statement_run (insert_round, NULL, MYSQL_TYPE_LONGLONG,
914 &current_trial, GNUNET_YES, MYSQL_TYPE_LONG,
915 &round_type, GNUNET_YES, MYSQL_TYPE_LONG,
916 &round_count, GNUNET_YES, -1);
917 mysql_stmt_close (stmt);
918 if (ret != GNUNET_OK)
919 return GNUNET_SYSERR;
920 return ret;
921}
922
923/*
924 * Inserts the specified round results into the
925 * dhttests.processed_round_details table
926 *
927 * @param round_type the type of round that is being started
928 * @param round_count counter for the round (if applicable)
929 * @param num_messages the total number of messages initiated
930 * @param num_messages_succeeded the number of messages that succeeded
931 *
932 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
933 */
934int
935add_round_details (unsigned int round_type, unsigned int round_count,
936 unsigned int num_messages,
937 unsigned int num_messages_succeeded)
938{
939 MYSQL_STMT *stmt;
940 int ret;
941
942 stmt = mysql_stmt_init (conn);
943 ret =
944 prepared_statement_run (insert_round_details, NULL, MYSQL_TYPE_LONGLONG,
945 &current_trial, GNUNET_YES, MYSQL_TYPE_LONG,
946 &round_type, GNUNET_YES, MYSQL_TYPE_LONG,
947 &round_count, GNUNET_YES, MYSQL_TYPE_LONG,
948 &num_messages, GNUNET_YES, MYSQL_TYPE_LONG,
949 &num_messages_succeeded, GNUNET_YES, -1);
950 mysql_stmt_close (stmt);
951 if (ret != GNUNET_OK)
952 return GNUNET_SYSERR;
953 return ret;
954}
955
956/*
957 * Inserts the specified stats into the dhttests.node_statistics table
958 *
959 * @param peer the peer inserting the statistic
960 * @param route_requests route requests seen
961 * @param route_forwards route requests forwarded
962 * @param result_requests route result requests seen
963 * @param client_requests client requests initiated
964 * @param result_forwards route results forwarded
965 * @param gets get requests handled
966 * @param puts put requests handle
967 * @param data_inserts data inserted at this node
968 * @param find_peer_requests find peer requests seen
969 * @param find_peers_started find peer requests initiated at this node
970 * @param gets_started get requests initiated at this node
971 * @param puts_started put requests initiated at this node
972 * @param find_peer_responses_received find peer responses received locally
973 * @param get_responses_received get responses received locally
974 * @param find_peer_responses_sent find peer responses sent from this node
975 * @param get_responses_sent get responses sent from this node
976 *
977 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
978 */
979int
980add_stat (const struct GNUNET_PeerIdentity *peer, unsigned int route_requests,
981 unsigned int route_forwards, unsigned int result_requests,
982 unsigned int client_requests, unsigned int result_forwards,
983 unsigned int gets, unsigned int puts, unsigned int data_inserts,
984 unsigned int find_peer_requests, unsigned int find_peers_started,
985 unsigned int gets_started, unsigned int puts_started,
986 unsigned int find_peer_responses_received,
987 unsigned int get_responses_received,
988 unsigned int find_peer_responses_sent,
989 unsigned int get_responses_sent)
990{
991 MYSQL_STMT *stmt;
992 int ret;
993 unsigned long long peer_uid;
994 unsigned long long return_uid;
995
996 if (peer == NULL)
997 return GNUNET_SYSERR;
998
999 if (GNUNET_OK != get_node_uid (&peer_uid, &peer->hashPubKey))
1000 {
1001 return GNUNET_SYSERR;
1002 }
1003
1004 stmt = mysql_stmt_init (conn);
1005 if (GNUNET_OK !=
1006 (ret =
1007 prepared_statement_run (insert_stat, &return_uid, MYSQL_TYPE_LONGLONG,
1008 &current_trial, GNUNET_YES, MYSQL_TYPE_LONGLONG,
1009 &peer_uid, GNUNET_YES, MYSQL_TYPE_LONG,
1010 &route_requests, GNUNET_YES, MYSQL_TYPE_LONG,
1011 &route_forwards, GNUNET_YES, MYSQL_TYPE_LONG,
1012 &result_requests, GNUNET_YES, MYSQL_TYPE_LONG,
1013 &client_requests, GNUNET_YES, MYSQL_TYPE_LONG,
1014 &result_forwards, GNUNET_YES, MYSQL_TYPE_LONG,
1015 &gets, GNUNET_YES, MYSQL_TYPE_LONG, &puts,
1016 GNUNET_YES, MYSQL_TYPE_LONG, &data_inserts,
1017 GNUNET_YES, MYSQL_TYPE_LONG, &find_peer_requests,
1018 GNUNET_YES, MYSQL_TYPE_LONG, &find_peers_started,
1019 GNUNET_YES, MYSQL_TYPE_LONG, &gets_started,
1020 GNUNET_YES, MYSQL_TYPE_LONG, &puts_started,
1021 GNUNET_YES, MYSQL_TYPE_LONG,
1022 &find_peer_responses_received, GNUNET_YES,
1023 MYSQL_TYPE_LONG, &get_responses_received,
1024 GNUNET_YES, MYSQL_TYPE_LONG,
1025 &find_peer_responses_sent, GNUNET_YES,
1026 MYSQL_TYPE_LONG, &get_responses_sent, GNUNET_YES,
1027 -1)))
1028 {
1029 if (ret == GNUNET_SYSERR)
1030 {
1031 mysql_stmt_close (stmt);
1032 return GNUNET_SYSERR;
1033 }
1034 }
1035
1036 mysql_stmt_close (stmt);
1037 return GNUNET_OK;
1038}
1039
1040/*
1041 * Inserts the specified stats into the dhttests.generic_stats table
1042 *
1043 * @param peer the peer inserting the statistic
1044 * @param name the name of the statistic
1045 * @param section the section of the statistic
1046 * @param value the value of the statistic
1047 *
1048 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1049 */
1050int
1051add_generic_stat (const struct GNUNET_PeerIdentity *peer, const char *name,
1052 const char *section, uint64_t value)
1053{
1054 unsigned long long peer_uid;
1055 unsigned long long section_len;
1056 unsigned long long name_len;
1057 int ret;
1058
1059 if (peer == NULL)
1060 return GNUNET_SYSERR;
1061
1062 if (GNUNET_OK != get_node_uid (&peer_uid, &peer->hashPubKey))
1063 {
1064 return GNUNET_SYSERR;
1065 }
1066
1067 section_len = strlen (section);
1068 name_len = strlen (name);
1069
1070 if (GNUNET_OK !=
1071 (ret =
1072 prepared_statement_run (insert_generic_stat, NULL, MYSQL_TYPE_LONGLONG,
1073 &current_trial, GNUNET_YES, MYSQL_TYPE_LONGLONG,
1074 &peer_uid, GNUNET_YES, MYSQL_TYPE_VAR_STRING,
1075 &section, max_varchar_len, &section_len,
1076 MYSQL_TYPE_VAR_STRING, &name, max_varchar_len,
1077 &name_len, MYSQL_TYPE_LONGLONG, &value,
1078 GNUNET_YES, -1)))
1079 {
1080 if (ret == GNUNET_SYSERR)
1081 {
1082 return GNUNET_SYSERR;
1083 }
1084 }
1085 return GNUNET_OK;
1086}
1087
1088/*
1089 * Inserts the specified dhtkey into the dhttests.dhtkeys table,
1090 * stores return value of dhttests.dhtkeys.dhtkeyuid into dhtkeyuid
1091 *
1092 * @param dhtkeyuid return value
1093 * @param dhtkey hashcode of key to insert
1094 *
1095 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1096 */
1097int
1098add_dhtkey (unsigned long long *dhtkeyuid, const GNUNET_HashCode * dhtkey)
1099{
1100
1101 int ret;
1102 struct GNUNET_CRYPTO_HashAsciiEncoded encKey;
1103 unsigned long long k_len;
1104 unsigned long long h_len;
1105 unsigned long long curr_dhtkeyuid;
1106
1107 GNUNET_CRYPTO_hash_to_enc (dhtkey, &encKey);
1108 k_len = strlen ((char *) &encKey);
1109 h_len = sizeof (GNUNET_HashCode);
1110 curr_dhtkeyuid = 0;
1111 ret = get_dhtkey_uid (&curr_dhtkeyuid, dhtkey);
1112 if (curr_dhtkeyuid != 0) /* dhtkey already exists */
1113 {
1114 if (dhtkeyuid != NULL)
1115 *dhtkeyuid = curr_dhtkeyuid;
1116 return GNUNET_OK;
1117 }
1118 else if (ret == GNUNET_SYSERR)
1119 {
1120#if DEBUG_DHTLOG
1121 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to get dhtkeyuid!\n");
1122#endif
1123 }
1124
1125 if (GNUNET_OK !=
1126 (ret =
1127 prepared_statement_run (insert_dhtkey, dhtkeyuid, MYSQL_TYPE_VAR_STRING,
1128 &encKey, max_varchar_len, &k_len,
1129 MYSQL_TYPE_LONG, &current_trial, GNUNET_YES,
1130 MYSQL_TYPE_BLOB, dhtkey,
1131 sizeof (GNUNET_HashCode), &h_len, -1)))
1132 {
1133 if (ret == GNUNET_SYSERR)
1134 {
1135 return GNUNET_SYSERR;
1136 }
1137 }
1138
1139 return GNUNET_OK;
1140}
1141
1142
1143
1144/*
1145 * Inserts the specified node into the dhttests.nodes table
1146 *
1147 * @param nodeuid the inserted node uid
1148 * @param node the node to insert
1149 *
1150 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1151 */
1152int
1153add_node (unsigned long long *nodeuid, struct GNUNET_PeerIdentity *node)
1154{
1155 struct GNUNET_CRYPTO_HashAsciiEncoded encPeer;
1156 unsigned long p_len;
1157 unsigned long h_len;
1158 int ret;
1159
1160 if (node == NULL)
1161 return GNUNET_SYSERR;
1162
1163 GNUNET_CRYPTO_hash_to_enc (&node->hashPubKey, &encPeer);
1164 p_len = (unsigned long) strlen ((char *) &encPeer);
1165 h_len = sizeof (GNUNET_HashCode);
1166 if (GNUNET_OK !=
1167 (ret =
1168 prepared_statement_run (insert_node, nodeuid, MYSQL_TYPE_LONGLONG,
1169 &current_trial, GNUNET_YES,
1170 MYSQL_TYPE_VAR_STRING, &encPeer, max_varchar_len,
1171 &p_len, MYSQL_TYPE_BLOB, &node->hashPubKey,
1172 sizeof (GNUNET_HashCode), &h_len, -1)))
1173 {
1174 if (ret == GNUNET_SYSERR)
1175 {
1176 return GNUNET_SYSERR;
1177 }
1178 }
1179 return GNUNET_OK;
1180}
1181
1182/*
1183 * Update dhttests.trials table with current server time as end time
1184 *
1185 * @param gets_succeeded how many gets did the testcase report as successful
1186 *
1187 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1188 */
1189int
1190update_trials (unsigned int gets_succeeded)
1191{
1192 int ret;
1193
1194 if (GNUNET_OK !=
1195 (ret =
1196 prepared_statement_run (update_trial, NULL, MYSQL_TYPE_LONG,
1197 &gets_succeeded, GNUNET_YES, MYSQL_TYPE_LONGLONG,
1198 &current_trial, GNUNET_YES, -1)))
1199 {
1200 if (ret == GNUNET_SYSERR)
1201 {
1202 return GNUNET_SYSERR;
1203 }
1204 }
1205 if (ret > 0)
1206 return GNUNET_OK;
1207 else
1208 return GNUNET_SYSERR;
1209}
1210
1211
1212/*
1213 * Update dhttests.nodes table setting the identified
1214 * node as a malicious dropper.
1215 *
1216 * @param peer the peer that was set to be malicious
1217 *
1218 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1219 */
1220int
1221set_malicious (struct GNUNET_PeerIdentity *peer)
1222{
1223 unsigned long long p_len;
1224 int ret;
1225 char *temp_str;
1226
1227 temp_str = GNUNET_strdup (GNUNET_h2s_full (&peer->hashPubKey));
1228 p_len = strlen (temp_str);
1229
1230 if (GNUNET_OK !=
1231 (ret =
1232 prepared_statement_run (update_node_malicious, NULL, MYSQL_TYPE_LONGLONG,
1233 &current_trial, GNUNET_YES,
1234 MYSQL_TYPE_VAR_STRING, temp_str, max_varchar_len,
1235 &p_len, -1)))
1236 {
1237 if (ret == GNUNET_SYSERR)
1238 {
1239 return GNUNET_SYSERR;
1240 }
1241 }
1242 return GNUNET_OK;
1243}
1244
1245
1246/*
1247 * Update dhttests.trials table with total connections information
1248 *
1249 * @param totalConnections the number of connections
1250 *
1251 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1252 */
1253int
1254add_connections (unsigned int totalConnections)
1255{
1256 int ret;
1257
1258 if (GNUNET_OK !=
1259 (ret =
1260 prepared_statement_run (update_connection, NULL, MYSQL_TYPE_LONG,
1261 &totalConnections, GNUNET_YES,
1262 MYSQL_TYPE_LONGLONG, &current_trial, GNUNET_YES,
1263 -1)))
1264 {
1265 if (ret == GNUNET_SYSERR)
1266 {
1267 return GNUNET_SYSERR;
1268 }
1269 }
1270 if (ret > 0)
1271 return GNUNET_OK;
1272 else
1273 return GNUNET_SYSERR;
1274}
1275
1276/*
1277 * Inserts the specified query into the dhttests.queries table
1278 *
1279 * @param sqlqueruid inserted query uid
1280 * @param queryid dht query id
1281 * @param type type of the query
1282 * @param hops number of hops query traveled
1283 * @param succeeded whether or not query was successful
1284 * @param node the node the query hit
1285 * @param key the key of the query
1286 *
1287 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1288 */
1289int
1290add_query (unsigned long long *sqlqueryuid, unsigned long long queryid,
1291 unsigned int type, unsigned int hops, int succeeded,
1292 const struct GNUNET_PeerIdentity *node, const GNUNET_HashCode * key)
1293{
1294 int ret;
1295 unsigned long long peer_uid, key_uid;
1296
1297 peer_uid = 0;
1298 key_uid = 0;
1299
1300 if ((node != NULL) &&
1301 (GNUNET_OK == get_node_uid (&peer_uid, &node->hashPubKey)))
1302 {
1303
1304 }
1305 else
1306 {
1307 return GNUNET_SYSERR;
1308 }
1309
1310 if ((key != NULL) && (GNUNET_OK == get_dhtkey_uid (&key_uid, key)))
1311 {
1312
1313 }
1314 else if ((key != NULL) && (key->bits[(512 / 8 / sizeof (unsigned int)) - 1] == 42)) /* Malicious marker */
1315 {
1316 key_uid = 0;
1317 }
1318 else
1319 {
1320 return GNUNET_SYSERR;
1321 }
1322
1323 if (GNUNET_OK !=
1324 (ret =
1325 prepared_statement_run (insert_query, sqlqueryuid, MYSQL_TYPE_LONGLONG,
1326 &current_trial, GNUNET_YES, MYSQL_TYPE_LONG,
1327 &type, GNUNET_NO, MYSQL_TYPE_LONG, &hops,
1328 GNUNET_YES, MYSQL_TYPE_LONGLONG, &key_uid,
1329 GNUNET_YES, MYSQL_TYPE_LONGLONG, &queryid,
1330 GNUNET_YES, MYSQL_TYPE_LONG, &succeeded,
1331 GNUNET_NO, MYSQL_TYPE_LONGLONG, &peer_uid,
1332 GNUNET_YES, -1)))
1333 {
1334 if (ret == GNUNET_SYSERR)
1335 {
1336 return GNUNET_SYSERR;
1337 }
1338 }
1339 if (ret > 0)
1340 return GNUNET_OK;
1341 else
1342 return GNUNET_SYSERR;
1343}
1344
1345/*
1346 * Inserts the specified route information into the dhttests.routes table
1347 *
1348 * @param sqlqueruid inserted query uid
1349 * @param queryid dht query id
1350 * @param type type of the query
1351 * @param hops number of hops query traveled
1352 * @param succeeded whether or not query was successful
1353 * @param node the node the query hit
1354 * @param key the key of the query
1355 * @param from_node the node that sent the message to node
1356 * @param to_node next node to forward message to
1357 *
1358 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1359 */
1360int
1361add_route (unsigned long long *sqlqueryuid, unsigned long long queryid,
1362 unsigned int type, unsigned int hops, int succeeded,
1363 const struct GNUNET_PeerIdentity *node, const GNUNET_HashCode * key,
1364 const struct GNUNET_PeerIdentity *from_node,
1365 const struct GNUNET_PeerIdentity *to_node)
1366{
1367 unsigned long long peer_uid = 0;
1368 unsigned long long key_uid = 0;
1369 unsigned long long from_uid = 0;
1370 unsigned long long to_uid = 0;
1371 int ret;
1372
1373 if (from_node != NULL)
1374 get_node_uid (&from_uid, &from_node->hashPubKey);
1375
1376 if (to_node != NULL)
1377 get_node_uid (&to_uid, &to_node->hashPubKey);
1378 else
1379 to_uid = 0;
1380
1381 if ((node != NULL))
1382 {
1383 if (1 != get_node_uid (&peer_uid, &node->hashPubKey))
1384 {
1385 return GNUNET_SYSERR;
1386 }
1387 }
1388 else
1389 return GNUNET_SYSERR;
1390
1391 if ((key != NULL))
1392 {
1393 if (1 != get_dhtkey_uid (&key_uid, key))
1394 {
1395 return GNUNET_SYSERR;
1396 }
1397 }
1398 else
1399 return GNUNET_SYSERR;
1400
1401 if (GNUNET_OK !=
1402 (ret =
1403 prepared_statement_run (insert_route, sqlqueryuid, MYSQL_TYPE_LONGLONG,
1404 &current_trial, GNUNET_YES, MYSQL_TYPE_LONG,
1405 &type, GNUNET_NO, MYSQL_TYPE_LONG, &hops,
1406 GNUNET_YES, MYSQL_TYPE_LONGLONG, &key_uid,
1407 GNUNET_YES, MYSQL_TYPE_LONGLONG, &queryid,
1408 GNUNET_YES, MYSQL_TYPE_LONG, &succeeded,
1409 GNUNET_NO, MYSQL_TYPE_LONGLONG, &peer_uid,
1410 GNUNET_YES, MYSQL_TYPE_LONGLONG, &from_uid,
1411 GNUNET_YES, MYSQL_TYPE_LONGLONG, &to_uid,
1412 GNUNET_YES, -1)))
1413 {
1414 if (ret == GNUNET_SYSERR)
1415 {
1416 return GNUNET_SYSERR;
1417 }
1418 }
1419 if (ret > 0)
1420 return GNUNET_OK;
1421 else
1422 return GNUNET_SYSERR;
1423}
1424
1425/*
1426 * Update dhttests.topology table with total connections information
1427 *
1428 * @param totalConnections the number of connections
1429 *
1430 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1431 */
1432int
1433update_current_topology (unsigned int connections)
1434{
1435 int ret;
1436 unsigned long long topologyuid;
1437
1438 get_current_topology (&topologyuid);
1439
1440 if (GNUNET_OK !=
1441 (ret =
1442 prepared_statement_run (update_topology, NULL, MYSQL_TYPE_LONG,
1443 &connections, GNUNET_YES, MYSQL_TYPE_LONGLONG,
1444 &topologyuid, GNUNET_YES, -1)))
1445 {
1446 if (ret == GNUNET_SYSERR)
1447 {
1448 return GNUNET_SYSERR;
1449 }
1450 }
1451 if (ret > 0)
1452 return GNUNET_OK;
1453 return GNUNET_SYSERR;
1454
1455}
1456
1457/*
1458 * Records the current topology (number of connections, time, trial)
1459 *
1460 * @param num_connections how many connections are in the topology
1461 *
1462 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1463 */
1464int
1465add_topology (int num_connections)
1466{
1467 int ret;
1468
1469 if (GNUNET_OK !=
1470 (ret =
1471 prepared_statement_run (insert_topology, NULL, MYSQL_TYPE_LONGLONG,
1472 &current_trial, GNUNET_YES, MYSQL_TYPE_LONG,
1473 &num_connections, GNUNET_YES, -1)))
1474 {
1475 if (ret == GNUNET_SYSERR)
1476 {
1477 return GNUNET_SYSERR;
1478 }
1479 }
1480 if (ret > 0)
1481 return GNUNET_OK;
1482 return GNUNET_SYSERR;
1483}
1484
1485/*
1486 * Records a connection between two peers in the current topology
1487 *
1488 * @param first one side of the connection
1489 * @param second other side of the connection
1490 *
1491 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1492 */
1493int
1494add_extended_topology (const struct GNUNET_PeerIdentity *first,
1495 const struct GNUNET_PeerIdentity *second)
1496{
1497 int ret;
1498 unsigned long long first_uid;
1499 unsigned long long second_uid;
1500 unsigned long long topologyuid;
1501
1502 if (GNUNET_OK != get_current_topology (&topologyuid))
1503 return GNUNET_SYSERR;
1504 if (GNUNET_OK != get_node_uid (&first_uid, &first->hashPubKey))
1505 return GNUNET_SYSERR;
1506 if (GNUNET_OK != get_node_uid (&second_uid, &second->hashPubKey))
1507 return GNUNET_SYSERR;
1508
1509 if (GNUNET_OK !=
1510 (ret =
1511 prepared_statement_run (extend_topology, NULL, MYSQL_TYPE_LONGLONG,
1512 &topologyuid, GNUNET_YES, MYSQL_TYPE_LONGLONG,
1513 &first_uid, GNUNET_YES, MYSQL_TYPE_LONGLONG,
1514 &second_uid, GNUNET_YES, -1)))
1515 {
1516 if (ret == GNUNET_SYSERR)
1517 {
1518 return GNUNET_SYSERR;
1519 }
1520 }
1521 if (ret > 0)
1522 return GNUNET_OK;
1523 return GNUNET_SYSERR;
1524}
1525
1526
1527/*
1528 * Provides the dhtlog api
1529 *
1530 * @param c the configuration to use to connect to a server
1531 *
1532 * @return the handle to the server, or NULL on error
1533 */
1534void *
1535libgnunet_plugin_dhtlog_mysql_init (void *cls)
1536{
1537 struct GNUNET_DHTLOG_Plugin *plugin = cls;
1538
1539 cfg = plugin->cfg;
1540 max_varchar_len = 255;
1541#if DEBUG_DHTLOG
1542 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1543 "MySQL DHT Logger: initializing database\n");
1544#endif
1545
1546 if (iopen (plugin) != GNUNET_OK)
1547 {
1548 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1549 _
1550 ("Failed to initialize MySQL database connection for dhtlog.\n"));
1551 return NULL;
1552 }
1553
1554 GNUNET_assert (plugin->dhtlog_api == NULL);
1555 plugin->dhtlog_api = GNUNET_malloc (sizeof (struct GNUNET_DHTLOG_Handle));
1556 plugin->dhtlog_api->insert_trial = &add_trial;
1557 plugin->dhtlog_api->insert_stat = &add_stat;
1558 plugin->dhtlog_api->insert_round = &add_round;
1559 plugin->dhtlog_api->insert_round_details = &add_round_details;
1560 plugin->dhtlog_api->add_generic_stat = &add_generic_stat;
1561 plugin->dhtlog_api->insert_query = &add_query;
1562 plugin->dhtlog_api->update_trial = &update_trials;
1563 plugin->dhtlog_api->insert_route = &add_route;
1564 plugin->dhtlog_api->insert_node = &add_node;
1565 plugin->dhtlog_api->insert_dhtkey = &add_dhtkey;
1566 plugin->dhtlog_api->update_connections = &add_connections;
1567 plugin->dhtlog_api->insert_topology = &add_topology;
1568 plugin->dhtlog_api->update_topology = &update_current_topology;
1569 plugin->dhtlog_api->insert_extended_topology = &add_extended_topology;
1570 plugin->dhtlog_api->set_malicious = &set_malicious;
1571 get_current_trial (&current_trial);
1572
1573 return plugin;
1574}
1575
1576/**
1577 * Shutdown the plugin.
1578 */
1579void *
1580libgnunet_plugin_dhtlog_mysql_done (void *cls)
1581{
1582 struct GNUNET_DHTLOG_Handle *dhtlog_api = cls;
1583
1584 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MySQL DHT Logger: database shutdown\n");
1585 GNUNET_assert (dhtlog_api != NULL);
1586 prepared_statement_close (insert_query);
1587 prepared_statement_close (insert_route);
1588 prepared_statement_close (insert_trial);
1589 prepared_statement_close (insert_round);
1590 prepared_statement_close (insert_round_details);
1591 prepared_statement_close (insert_node);
1592 prepared_statement_close (insert_dhtkey);
1593 prepared_statement_close (update_trial);
1594 prepared_statement_close (get_dhtkeyuid);
1595 prepared_statement_close (get_nodeuid);
1596 prepared_statement_close (update_connection);
1597 prepared_statement_close (get_trial);
1598 prepared_statement_close (get_topology);
1599 prepared_statement_close (insert_topology);
1600 prepared_statement_close (update_topology);
1601 prepared_statement_close (extend_topology);
1602 prepared_statement_close (update_node_malicious);
1603
1604 if (conn != NULL)
1605 mysql_close (conn);
1606 conn = NULL;
1607 mysql_library_end ();
1608 GNUNET_free (dhtlog_api);
1609 return NULL;
1610}
1611
1612/* end of plugin_dhtlog_mysql.c */
diff --git a/src/dht/plugin_dhtlog_mysql_dump.c b/src/dht/plugin_dhtlog_mysql_dump.c
deleted file mode 100644
index bbb1da0c9..000000000
--- a/src/dht/plugin_dhtlog_mysql_dump.c
+++ /dev/null
@@ -1,964 +0,0 @@
1/*
2 This file is part of GNUnet.
3 (C) 2006 - 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/dht/plugin_dhtlog_mysql_dump.c
23 * @brief MySQL logging plugin to record DHT operations to MySQL server,
24 * but write all queries to file instead of the actual server
25 * so that they can be imported later. The idea is that connecting
26 * to the MySQL server X times can be really problematic, but hopefully
27 * writing to a single file is more reliable.
28 * @author Nathan Evans
29 *
30 * Database: MySQL
31 */
32
33#include "platform.h"
34#include "gnunet_util_lib.h"
35#include "dhtlog.h"
36
37
38#define DEBUG_DHTLOG GNUNET_YES
39
40/**
41 * Maximum number of supported parameters for a prepared
42 * statement. Increase if needed.
43 */
44#define MAX_PARAM 32
45
46
47static unsigned long max_varchar_len;
48
49/**
50 * The configuration the DHT service is running with
51 */
52static const struct GNUNET_CONFIGURATION_Handle *cfg;
53
54#define INSERT_QUERIES_STMT "prepare insert_query from 'INSERT INTO queries (trialuid, querytype, hops, dhtkeyuid, dhtqueryid, succeeded, nodeuid, time) "\
55 "VALUES (@temp_trial, ?, ?, ?, ?, ?, ?, ?)'"
56
57#define INSERT_ROUTES_STMT "prepare insert_route from 'INSERT INTO routes (trialuid, querytype, hops, dhtkeyuid, dhtqueryid, succeeded, nodeuid, from_node, to_node) "\
58 "VALUES (@temp_trial, ?, ?, ?, ?, ?, ?, ?, ?)'"
59
60#define INSERT_NODES_STMT "prepare insert_node from 'INSERT ignore INTO nodes (trialuid, nodeid) "\
61 "VALUES (@temp_trial, ?)'"
62
63#define INSERT_TOPOLOGY_STMT "prepare insert_topology from 'INSERT INTO topology (trialuid, date, connections) "\
64 "VALUES (@temp_trial, ?, ?)'"
65
66#define INSERT_ROUND_STMT "prepare insert_round from 'INSERT INTO rounds (trialuid, round_type, round_count, starttime) VALUES (@temp_trial, @rtype, @rcount, @curr_time)'"
67
68#define INSERT_ROUND_DETAILS_STMT "prepare insert_round_details from 'INSERT INTO processed_trial_rounds "\
69 "(trialuid, round_type, round_count, starttime, endtime, num_messages, num_messages_succeeded)"\
70 "VALUES (@temp_trial, @rtype, @rcount, @curr_time, @curr_time, @totalmsgs, @msgssucceeded)'"
71
72#define EXTEND_TOPOLOGY_STMT "prepare extend_topology from 'INSERT INTO extended_topology (topology_uid, uid_first, uid_second) "\
73 "VALUES (@temp_topology, ?, ?)'"
74
75#define UPDATE_TOPOLOGY_STMT "prepare update_topology from 'update topology set connections = ? where topology_uid = @temp_topology'"
76
77#define SET_MALICIOUS_STMT "prepare set_malicious from 'update nodes set malicious_dropper = 1 where trialuid = @temp_trial and nodeid = @temp_node'"
78
79#define INSERT_TRIALS_STMT "prepare insert_trial from 'INSERT INTO trials"\
80 "(starttime, other_trial_identifier, numnodes, topology,"\
81 "topology_percentage, topology_probability,"\
82 "blacklist_topology, connect_topology, connect_topology_option,"\
83 "connect_topology_option_modifier, puts, gets, "\
84 "concurrent, settle_time, num_rounds, malicious_getters,"\
85 "malicious_putters, malicious_droppers, malicious_get_frequency,"\
86 "malicious_put_frequency, stop_closest, stop_found, strict_kademlia, "\
87 "gets_succeeded, message) "\
88 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'"
89
90#define INSERT_GENERIC_STAT_STMT "prepare insert_generic_stat from 'INSERT INTO generic_stats" \
91 "(trialuid, nodeuid, section, name, value)"\
92 "VALUES (@temp_trial, @temp_node, @temp_section, @temp_stat, @temp_value)'"
93
94#define INSERT_STAT_STMT "prepare insert_stat from 'INSERT INTO node_statistics"\
95 "(trialuid, nodeuid, route_requests,"\
96 "route_forwards, result_requests,"\
97 "client_results, result_forwards, gets,"\
98 "puts, data_inserts, find_peer_requests, "\
99 "find_peers_started, gets_started, puts_started, find_peer_responses_received,"\
100 "get_responses_received, find_peer_responses_sent, get_responses_sent) "\
101 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'"
102
103#define INSERT_DHTKEY_STMT "prepare insert_dhtkey from 'INSERT ignore INTO dhtkeys (dhtkey, trialuid) "\
104 "VALUES (?, @temp_trial)'"
105
106#define UPDATE_TRIALS_STMT "prepare update_trial from 'UPDATE trials set endtime= ?, gets_succeeded = ? where trialuid = @temp_trial'"
107
108#define UPDATE_CONNECTIONS_STMT "prepare update_conn from 'UPDATE trials set totalConnections = ? where trialuid = @temp_trial'"
109
110#define GET_TRIAL_STMT "prepare select_trial from 'SELECT MAX( trialuid ) FROM trials into @temp_trial'"
111
112#define GET_TOPOLOGY_STMT "prepare select_topology from 'SELECT MAX( topology_uid ) FROM topology into @temp_topology'"
113
114#define GET_DHTKEYUID_STMT "prepare get_dhtkeyuid from 'SELECT dhtkeyuid FROM dhtkeys where dhtkey = ? and trialuid = @temp_trial'"
115
116#define GET_NODEUID_STMT "prepare get_nodeuid from 'SELECT nodeuid FROM nodes where trialuid = @temp_trial and nodeid = ?'"
117
118#define DATE_STR_SIZE 50
119
120/**
121 * File to dump all sql statements to.
122 */
123FILE *outfile;
124
125
126static char *
127get_sql_time ()
128{
129 static char date[DATE_STR_SIZE];
130 time_t timetmp;
131 struct tm *tmptr;
132
133 time (&timetmp);
134 memset (date, 0, DATE_STR_SIZE);
135 tmptr = localtime (&timetmp);
136 if (NULL != tmptr)
137 strftime (date, DATE_STR_SIZE, "%Y-%m-%d %H:%M:%S", tmptr);
138 else
139 strcpy (date, "");
140
141 return date;
142}
143
144/**
145 * Create a prepared statement.
146 *
147 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
148 */
149static int
150prepared_statement_create (const char *statement)
151{
152 if (fprintf (outfile, "%s;\n", statement) > 0)
153 return GNUNET_OK;
154
155 return GNUNET_SYSERR;
156}
157
158/*
159 * Initialize the prepared statements for use with dht test logging
160 */
161static int
162iopen ()
163{
164#define PINIT(a) (GNUNET_OK != (prepared_statement_create(a)))
165 if (PINIT (INSERT_QUERIES_STMT) || PINIT (INSERT_ROUTES_STMT) ||
166 PINIT (INSERT_ROUND_STMT) || PINIT (INSERT_ROUND_DETAILS_STMT) ||
167 PINIT (INSERT_TRIALS_STMT) || PINIT (SET_MALICIOUS_STMT) ||
168 PINIT (INSERT_GENERIC_STAT_STMT) || PINIT (INSERT_STAT_STMT) ||
169 PINIT (INSERT_NODES_STMT) || PINIT (INSERT_DHTKEY_STMT) ||
170 PINIT (UPDATE_TRIALS_STMT) || PINIT (GET_DHTKEYUID_STMT) ||
171 PINIT (GET_NODEUID_STMT) || PINIT (UPDATE_CONNECTIONS_STMT) ||
172 PINIT (INSERT_TOPOLOGY_STMT) || PINIT (EXTEND_TOPOLOGY_STMT) ||
173 PINIT (UPDATE_TOPOLOGY_STMT) || PINIT (GET_TRIAL_STMT) ||
174 PINIT (GET_TOPOLOGY_STMT))
175 {
176 return GNUNET_SYSERR;
177 }
178#undef PINIT
179
180 return GNUNET_OK;
181}
182
183/*
184 * Inserts the specified round into the dhttests.rounds table
185 *
186 * @param round_type the type of round that is being started
187 * @param round_count counter for the round (if applicable)
188 *
189 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
190 */
191int
192add_round (unsigned int round_type, unsigned int round_count)
193{
194 int ret;
195
196 if (outfile == NULL)
197 return GNUNET_SYSERR;
198
199 ret =
200 fprintf (outfile,
201 "set @curr_time = \"%s\", @rtype = \"%u\", @rcount = \"%u\";\n",
202 get_sql_time (), round_type, round_count);
203
204 if (ret < 0)
205 return GNUNET_SYSERR;
206 ret = fprintf (outfile, "execute insert_round;\n");
207
208 if (ret >= 0)
209 return GNUNET_OK;
210 return GNUNET_SYSERR;
211}
212
213/*
214 * Inserts the specified round results into the
215 * dhttests.processed_round_details table
216 *
217 * @param round_type the type of round that is being started
218 * @param round_count counter for the round (if applicable)
219 * @param num_messages the total number of messages initiated
220 * @param num_messages_succeeded the number of messages that succeeded
221 *
222 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
223 */
224int
225add_round_details (unsigned int round_type, unsigned int round_count,
226 unsigned int num_messages,
227 unsigned int num_messages_succeeded)
228{
229 int ret;
230
231 if (outfile == NULL)
232 return GNUNET_SYSERR;
233
234 ret =
235 fprintf (outfile,
236 "set @curr_time = \"%s\", @rtype = \"%u\", @rcount = \"%u\", @totalmsgs = \"%u\", @msgssucceeded = \"%u\";\n",
237 get_sql_time (), round_type, round_count, num_messages,
238 num_messages_succeeded);
239
240 if (ret < 0)
241 return GNUNET_SYSERR;
242 ret = fprintf (outfile, "execute insert_round_details;\n");
243
244 if (ret >= 0)
245 return GNUNET_OK;
246 return GNUNET_SYSERR;
247}
248
249/*
250 * Records the current topology (number of connections, time, trial)
251 *
252 * @param num_connections how many connections are in the topology
253 *
254 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
255 */
256int
257add_topology (int num_connections)
258{
259 int ret;
260
261 if (outfile == NULL)
262 return GNUNET_SYSERR;
263
264 ret =
265 fprintf (outfile, "set @date = \"%s\", @num = %d;\n", get_sql_time (),
266 num_connections);
267
268 if (ret < 0)
269 return GNUNET_SYSERR;
270 ret = fprintf (outfile, "execute insert_topology using " "@date, @num;\n");
271 if (ret < 0)
272 return GNUNET_SYSERR;
273
274 ret = fprintf (outfile, "execute select_topology;\n");
275
276 if (ret >= 0)
277 return GNUNET_OK;
278 return GNUNET_SYSERR;
279}
280
281/*
282 * Records a connection between two peers in the current topology
283 *
284 * @param first one side of the connection
285 * @param second other side of the connection
286 *
287 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
288 */
289int
290add_extended_topology (const struct GNUNET_PeerIdentity *first,
291 const struct GNUNET_PeerIdentity *second)
292{
293 int ret;
294
295 if (outfile == NULL)
296 return GNUNET_SYSERR;
297
298 if (first != NULL)
299 ret =
300 fprintf (outfile,
301 "select nodeuid from nodes where trialuid = @temp_trial and nodeid = \"%s\" into @temp_first_node;\n",
302 GNUNET_h2s_full (&first->hashPubKey));
303 else
304 ret = fprintf (outfile, "set @temp_first_node = 0;\n");
305
306 if (ret < 0)
307 return GNUNET_SYSERR;
308
309 if (second != NULL)
310 ret =
311 fprintf (outfile,
312 "select nodeuid from nodes where trialuid = @temp_trial and nodeid = \"%s\" into @temp_second_node;\n",
313 GNUNET_h2s_full (&second->hashPubKey));
314 else
315 ret = fprintf (outfile, "set @temp_second_node = 0;\n");
316
317 if (ret < 0)
318 return GNUNET_SYSERR;
319
320 ret =
321 fprintf (outfile,
322 "execute extend_topology using "
323 "@temp_first_node, @temp_second_node;\n");
324
325 if (ret >= 0)
326 return GNUNET_OK;
327 return GNUNET_SYSERR;
328}
329
330
331/*
332 * Inserts the specified trial into the dhttests.trials table
333 *
334 * @param trial_info struct containing the data to insert about this trial
335 *
336 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
337 */
338int
339add_trial (struct GNUNET_DHTLOG_TrialInfo *trial_info)
340{
341 int ret;
342
343 if (outfile == NULL)
344 return GNUNET_SYSERR;
345
346 ret =
347 fprintf (outfile,
348 "set @date = \"%s\", @oid = %u, @num = %u, @topology = %u, @bl = %u, "
349 "@connect = %u, @c_t_o = %u, @c_t_o_m = %f, @t_p = %f, "
350 "@t_pr = %f, @puts = %u, @gets = %u, "
351 "@concurrent = %u, @settle = %u, @rounds = %u, "
352 "@m_gets = %u, @m_puts = %u, @m_drops = %u, "
353 "@m_g_f = %u, @m_p_f = %u, @s_c = %u, @s_f = %u,"
354 "@s_k = %u, @g_s = %u, @message = \"%s\";\n", get_sql_time (),
355 trial_info->other_identifier, trial_info->num_nodes,
356 trial_info->topology, trial_info->blacklist_topology,
357 trial_info->connect_topology,
358 trial_info->connect_topology_option,
359 trial_info->connect_topology_option_modifier,
360 trial_info->topology_percentage,
361 trial_info->topology_probability, trial_info->puts,
362 trial_info->gets, trial_info->concurrent,
363 trial_info->settle_time, trial_info->num_rounds,
364 trial_info->malicious_getters, trial_info->malicious_putters,
365 trial_info->malicious_droppers,
366 trial_info->malicious_get_frequency,
367 trial_info->malicious_put_frequency, trial_info->stop_closest,
368 trial_info->stop_found, trial_info->strict_kademlia,
369 trial_info->gets_succeeded, trial_info->message);
370
371 if (ret < 0)
372 return GNUNET_SYSERR;
373 ret =
374 fprintf (outfile,
375 "execute insert_trial using "
376 "@date, @oid, @num, @topology, @t_p, @t_pr,"
377 " @bl, @connect, @c_t_o," "@c_t_o_m, @puts, @gets,"
378 "@concurrent, @settle, @rounds," "@m_gets, @m_puts, @m_drops,"
379 "@m_g_f, @m_p_f, @s_c, @s_f," "@s_k, @g_s, @message;\n");
380 if (ret < 0)
381 return GNUNET_SYSERR;
382 ret = fprintf (outfile, "execute select_trial;\n");
383
384 if (ret >= 0)
385 return GNUNET_OK;
386 return GNUNET_SYSERR;
387}
388
389
390/*
391 * Inserts the specified stats into the dhttests.generic_stats table
392 *
393 * @param peer the peer inserting the statistic
394 * @param name the name of the statistic
395 * @param section the section of the statistic
396 * @param value the value of the statistic
397 *
398 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
399 */
400int
401add_generic_stat (const struct GNUNET_PeerIdentity *peer, const char *name,
402 const char *section, uint64_t value)
403{
404 int ret;
405
406 if (outfile == NULL)
407 return GNUNET_SYSERR;
408
409 if (peer != NULL)
410 ret =
411 fprintf (outfile,
412 "select nodeuid from nodes where trialuid = @temp_trial and nodeid = \"%s\" into @temp_node;\n",
413 GNUNET_h2s_full (&peer->hashPubKey));
414 else
415 ret = fprintf (outfile, "set @temp_node = 0;\n");
416
417 if (ret < 0)
418 return GNUNET_SYSERR;
419
420 ret =
421 fprintf (outfile,
422 "set @temp_section = \"%s\", @temp_stat = \"%s\", @temp_value = %llu;\n",
423 section, name, (unsigned long long) value);
424
425 if (ret < 0)
426 return GNUNET_SYSERR;
427
428 ret = fprintf (outfile, "execute insert_generic_stat;\n");
429
430 if (ret < 0)
431 return GNUNET_SYSERR;
432 return GNUNET_OK;
433}
434
435
436/*
437 * Inserts the specified stats into the dhttests.node_statistics table
438 *
439 * @param peer the peer inserting the statistic
440 * @param route_requests route requests seen
441 * @param route_forwards route requests forwarded
442 * @param result_requests route result requests seen
443 * @param client_requests client requests initiated
444 * @param result_forwards route results forwarded
445 * @param gets get requests handled
446 * @param puts put requests handle
447 * @param data_inserts data inserted at this node
448 * @param find_peer_requests find peer requests seen
449 * @param find_peers_started find peer requests initiated at this node
450 * @param gets_started get requests initiated at this node
451 * @param puts_started put requests initiated at this node
452 * @param find_peer_responses_received find peer responses received locally
453 * @param get_responses_received get responses received locally
454 * @param find_peer_responses_sent find peer responses sent from this node
455 * @param get_responses_sent get responses sent from this node
456 *
457 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
458 */
459int
460add_stat (const struct GNUNET_PeerIdentity *peer, unsigned int route_requests,
461 unsigned int route_forwards, unsigned int result_requests,
462 unsigned int client_requests, unsigned int result_forwards,
463 unsigned int gets, unsigned int puts, unsigned int data_inserts,
464 unsigned int find_peer_requests, unsigned int find_peers_started,
465 unsigned int gets_started, unsigned int puts_started,
466 unsigned int find_peer_responses_received,
467 unsigned int get_responses_received,
468 unsigned int find_peer_responses_sent,
469 unsigned int get_responses_sent)
470{
471 int ret;
472
473 if (outfile == NULL)
474 return GNUNET_SYSERR;
475
476 if (peer != NULL)
477 ret =
478 fprintf (outfile,
479 "select nodeuid from nodes where trialuid = @temp_trial and nodeid = \"%s\" into @temp_node;\n",
480 GNUNET_h2s_full (&peer->hashPubKey));
481 else
482 ret = fprintf (outfile, "set @temp_node = 0;\n");
483 if (ret < 0)
484 return GNUNET_SYSERR;
485
486 ret =
487 fprintf (outfile,
488 "set @r_r = %u, @r_f = %u, @res_r = %u, @c_r = %u, "
489 "@res_f = %u, @gets = %u, @puts = %u, @d_i = %u, "
490 "@f_p_r = %u, @f_p_s = %u, @g_s = %u, @p_s = %u, "
491 "@f_p_r_r = %u, @g_r_r = %u, @f_p_r_s = %u, @g_r_s = %u;\n",
492 route_requests, route_forwards, result_requests, client_requests,
493 result_forwards, gets, puts, data_inserts, find_peer_requests,
494 find_peers_started, gets_started, puts_started,
495 find_peer_responses_received, get_responses_received,
496 find_peer_responses_sent, get_responses_sent);
497
498 if (ret < 0)
499 return GNUNET_SYSERR;
500
501 ret =
502 fprintf (outfile,
503 "execute insert_stat using "
504 "@temp_trial, @temp_node, @r_r, @r_f, @res_r, @c_r, "
505 "@res_f, @gets, @puts, @d_i, " "@f_p_r, @f_p_s, @g_s, @p_s, "
506 "@f_p_r_r, @g_r_r, @f_p_r_s, @g_r_s;\n");
507 if (ret < 0)
508 return GNUNET_SYSERR;
509 return GNUNET_OK;
510}
511
512/*
513 * Inserts the specified dhtkey into the dhttests.dhtkeys table,
514 * stores return value of dhttests.dhtkeys.dhtkeyuid into dhtkeyuid
515 *
516 * @param dhtkeyuid return value
517 * @param dhtkey hashcode of key to insert
518 *
519 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
520 */
521int
522add_dhtkey (unsigned long long *dhtkeyuid, const GNUNET_HashCode * dhtkey)
523{
524 int ret;
525
526 if (dhtkeyuid != NULL)
527 *dhtkeyuid = 0;
528
529 if (outfile == NULL)
530 return GNUNET_SYSERR;
531
532 if (dhtkey != NULL)
533 ret =
534 fprintf (outfile, "set @dhtkey = \"%s\";\n", GNUNET_h2s_full (dhtkey));
535 else
536 ret = fprintf (outfile, "set @dhtkey = XXXXX;\n");
537
538 if (ret < 0)
539 return GNUNET_SYSERR;
540 ret = fprintf (outfile, "execute insert_dhtkey using @dhtkey;\n");
541
542 if (ret >= 0)
543 return GNUNET_OK;
544 return GNUNET_SYSERR;
545}
546
547/*
548 * Inserts the specified node into the dhttests.nodes table
549 *
550 * @param nodeuid the inserted node uid
551 * @param node the node to insert
552 *
553 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
554 */
555int
556add_node (unsigned long long *nodeuid, struct GNUNET_PeerIdentity *node)
557{
558 int ret;
559
560 if (node == NULL)
561 return GNUNET_SYSERR;
562
563 if (outfile == NULL)
564 return GNUNET_SYSERR;
565
566 ret =
567 fprintf (outfile, "set @node = \"%s\";\n",
568 GNUNET_h2s_full (&node->hashPubKey));
569
570 if (ret < 0)
571 return GNUNET_SYSERR;
572
573 ret = fprintf (outfile, "execute insert_node using @node;\n");
574
575 if (ret >= 0)
576 return GNUNET_OK;
577 return GNUNET_SYSERR;
578}
579
580/*
581 * Update dhttests.trials table with current server time as end time
582 *
583 * @param gets_succeeded how many gets did the testcase report as successful
584 *
585 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
586 */
587int
588update_trials (unsigned int gets_succeeded)
589{
590 int ret;
591
592 if (outfile == NULL)
593 return GNUNET_SYSERR;
594
595 ret =
596 fprintf (outfile, "set @date = \"%s\", @g_s = %u;\n", get_sql_time (),
597 gets_succeeded);
598
599 if (ret < 0)
600 return GNUNET_SYSERR;
601
602 ret = fprintf (outfile, "execute update_trial using @date, @g_s;\n");
603
604 if (ret >= 0)
605 return GNUNET_OK;
606 else
607 return GNUNET_SYSERR;
608}
609
610
611/*
612 * Update dhttests.nodes table setting the identified
613 * node as a malicious dropper.
614 *
615 * @param peer the peer that was set to be malicious
616 *
617 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
618 */
619int
620set_malicious (struct GNUNET_PeerIdentity *peer)
621{
622 int ret;
623
624 if (outfile == NULL)
625 return GNUNET_SYSERR;
626
627 ret =
628 fprintf (outfile, "set @temp_node = \"%s\";\n",
629 GNUNET_h2s_full (&peer->hashPubKey));
630
631 if (ret < 0)
632 return GNUNET_SYSERR;
633
634 ret = fprintf (outfile, "execute set_malicious;\n");
635
636 if (ret >= 0)
637 return GNUNET_OK;
638 else
639 return GNUNET_SYSERR;
640}
641
642
643/*
644 * Update dhttests.trials table with total connections information
645 *
646 * @param totalConnections the number of connections
647 *
648 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
649 */
650int
651add_connections (unsigned int totalConnections)
652{
653 int ret;
654
655 if (outfile == NULL)
656 return GNUNET_SYSERR;
657
658 ret = fprintf (outfile, "set @conns = %u;\n", totalConnections);
659
660 if (ret < 0)
661 return GNUNET_SYSERR;
662
663 ret = fprintf (outfile, "execute update_conn using @conns;\n");
664
665 if (ret >= 0)
666 return GNUNET_OK;
667 else
668 return GNUNET_SYSERR;
669}
670
671
672/*
673 * Update dhttests.topology table with total connections information
674 *
675 * @param totalConnections the number of connections
676 *
677 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
678 */
679int
680update_topology (unsigned int connections)
681{
682 int ret;
683
684 if (outfile == NULL)
685 return GNUNET_SYSERR;
686
687 ret = fprintf (outfile, "set @temp_conns = %u;\n", connections);
688
689 if (ret < 0)
690 return GNUNET_SYSERR;
691
692 ret = fprintf (outfile, "execute update_topology using @temp_conns;\n");
693
694 if (ret >= 0)
695 return GNUNET_OK;
696 else
697 return GNUNET_SYSERR;
698}
699
700/*
701 * Inserts the specified query into the dhttests.queries table
702 *
703 * @param sqlqueruid inserted query uid
704 * @param queryid dht query id
705 * @param type type of the query
706 * @param hops number of hops query traveled
707 * @param succeeded whether or not query was successful
708 * @param node the node the query hit
709 * @param key the key of the query
710 *
711 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
712 */
713int
714add_query (unsigned long long *sqlqueryuid, unsigned long long queryid,
715 unsigned int type, unsigned int hops, int succeeded,
716 const struct GNUNET_PeerIdentity *node, const GNUNET_HashCode * key)
717{
718 int ret;
719
720 if (outfile == NULL)
721 return GNUNET_SYSERR;
722
723 if (sqlqueryuid != NULL)
724 *sqlqueryuid = 0;
725
726 if (key != NULL)
727 ret =
728 fprintf (outfile,
729 "select dhtkeyuid from dhtkeys where trialuid = @temp_trial and dhtkey = \"%s\" into @temp_dhtkey;\n",
730 GNUNET_h2s_full (key));
731 else
732 ret = fprintf (outfile, "set @temp_dhtkey = 0;\n");
733
734 if (ret < 0)
735 return GNUNET_SYSERR;
736
737 if (node != NULL)
738 ret =
739 fprintf (outfile,
740 "select nodeuid from nodes where trialuid = @temp_trial and nodeid = \"%s\" into @temp_node;\n",
741 GNUNET_h2s_full (&node->hashPubKey));
742 else
743 ret = fprintf (outfile, "set @temp_node = 0;\n");
744
745 if (ret < 0)
746 return GNUNET_SYSERR;
747
748 ret =
749 fprintf (outfile,
750 "set @qid = %llu, @type = %u, @hops = %u, @succ = %d, @time = \"%s\";\n",
751 queryid, type, hops, succeeded, get_sql_time ());
752
753 if (ret < 0)
754 return GNUNET_SYSERR;
755
756 ret =
757 fprintf (outfile,
758 "execute insert_query using @type, @hops, @temp_dhtkey, @qid, @succ, @temp_node, @time;\n");
759
760 if (ret >= 0)
761 return GNUNET_OK;
762 else
763 return GNUNET_SYSERR;
764}
765
766/*
767 * Inserts the specified route information into the dhttests.routes table
768 *
769 * @param sqlqueruid inserted query uid
770 * @param queryid dht query id
771 * @param type type of the query
772 * @param hops number of hops query traveled
773 * @param succeeded whether or not query was successful
774 * @param node the node the query hit
775 * @param key the key of the query
776 * @param from_node the node that sent the message to node
777 * @param to_node next node to forward message to
778 *
779 * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
780 */
781int
782add_route (unsigned long long *sqlqueryuid, unsigned long long queryid,
783 unsigned int type, unsigned int hops, int succeeded,
784 const struct GNUNET_PeerIdentity *node, const GNUNET_HashCode * key,
785 const struct GNUNET_PeerIdentity *from_node,
786 const struct GNUNET_PeerIdentity *to_node)
787{
788 int ret;
789
790 if (outfile == NULL)
791 return GNUNET_SYSERR;
792
793 if (sqlqueryuid != NULL)
794 *sqlqueryuid = 0;
795
796 if (key != NULL)
797 ret =
798 fprintf (outfile,
799 "select dhtkeyuid from dhtkeys where trialuid = @temp_trial and dhtkey = \"%s\" into @temp_dhtkey;\n",
800 GNUNET_h2s_full (key));
801 else
802 ret = fprintf (outfile, "set @temp_dhtkey = 0;\n");
803
804 if (ret < 0)
805 return GNUNET_SYSERR;
806
807 if (node != NULL)
808 ret =
809 fprintf (outfile,
810 "select nodeuid from nodes where trialuid = @temp_trial and nodeid = \"%s\" into @temp_node;\n",
811 GNUNET_h2s_full (&node->hashPubKey));
812 else
813 ret = fprintf (outfile, "set @temp_node = 0;\n");
814
815 if (ret < 0)
816 return GNUNET_SYSERR;
817
818 if (from_node != NULL)
819 ret =
820 fprintf (outfile,
821 "select nodeuid from nodes where trialuid = @temp_trial and nodeid = \"%s\" into @temp_from_node;\n",
822 GNUNET_h2s_full (&from_node->hashPubKey));
823 else
824 ret = fprintf (outfile, "set @temp_from_node = 0;\n");
825
826 if (ret < 0)
827 return GNUNET_SYSERR;
828
829 if (to_node != NULL)
830 ret =
831 fprintf (outfile,
832 "select nodeuid from nodes where trialuid = @temp_trial and nodeid = \"%s\" into @temp_to_node;\n",
833 GNUNET_h2s_full (&to_node->hashPubKey));
834 else
835 ret = fprintf (outfile, "set @temp_to_node = 0;\n");
836
837 if (ret < 0)
838 return GNUNET_SYSERR;
839
840 ret =
841 fprintf (outfile,
842 "set @qid = %llu, @type = %u, @hops = %u, @succ = %d;\n",
843 queryid, type, hops, succeeded);
844
845 if (ret < 0)
846 return GNUNET_SYSERR;
847
848 ret =
849 fprintf (outfile,
850 "execute insert_route using @type, @hops, @temp_dhtkey, @qid, @succ, @temp_node, @temp_from_node, @temp_to_node;\n");
851
852 if (ret >= 0)
853 return GNUNET_OK;
854 else
855 return GNUNET_SYSERR;
856}
857
858/*
859 * Provides the dhtlog api
860 *
861 * @param c the configuration to use to connect to a server
862 *
863 * @return the handle to the server, or NULL on error
864 */
865void *
866libgnunet_plugin_dhtlog_mysql_dump_init (void *cls)
867{
868 struct GNUNET_DHTLOG_Plugin *plugin = cls;
869 char *outfile_name;
870 char *outfile_path;
871 char *fn;
872 int dirwarn;
873
874 cfg = plugin->cfg;
875 max_varchar_len = 255;
876
877 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
878 "MySQL (DUMP) DHT Logger: initializing\n");
879
880 if (GNUNET_OK !=
881 GNUNET_CONFIGURATION_get_value_string (plugin->cfg, "MYSQLDUMP", "PATH",
882 &outfile_path))
883 {
884 outfile_path = GNUNET_strdup ("");
885 }
886
887 GNUNET_asprintf (&outfile_name, "%s%s-%d", outfile_path, "mysqldump",
888 getpid ());
889
890 fn = GNUNET_STRINGS_filename_expand (outfile_name);
891
892 if (fn == NULL)
893 {
894 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
895 _("Failed to get full path for `%s'\n"), outfile_name);
896 GNUNET_free (outfile_path);
897 GNUNET_free (outfile_name);
898 return NULL;
899 }
900
901 dirwarn = (GNUNET_OK != GNUNET_DISK_directory_create_for_file (fn));
902 outfile = FOPEN (fn, "w");
903
904 if (outfile == NULL)
905 {
906 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "fopen", fn);
907 if (dirwarn)
908 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
909 _("Failed to create or access directory for log file `%s'\n"),
910 fn);
911 GNUNET_free (outfile_path);
912 GNUNET_free (outfile_name);
913 GNUNET_free (fn);
914 return NULL;
915 }
916
917 GNUNET_free (outfile_path);
918 GNUNET_free (outfile_name);
919 GNUNET_free (fn);
920
921 if (iopen () != GNUNET_OK)
922 {
923 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
924 _("Failed to create file for dhtlog.\n"));
925 fclose (outfile);
926 return NULL;
927 }
928 GNUNET_assert (plugin->dhtlog_api == NULL);
929 plugin->dhtlog_api = GNUNET_malloc (sizeof (struct GNUNET_DHTLOG_Handle));
930 plugin->dhtlog_api->insert_trial = &add_trial;
931 plugin->dhtlog_api->insert_round = &add_round;
932 plugin->dhtlog_api->insert_round_details = &add_round_details;
933 plugin->dhtlog_api->insert_stat = &add_stat;
934 plugin->dhtlog_api->insert_query = &add_query;
935 plugin->dhtlog_api->update_trial = &update_trials;
936 plugin->dhtlog_api->insert_route = &add_route;
937 plugin->dhtlog_api->insert_node = &add_node;
938 plugin->dhtlog_api->insert_dhtkey = &add_dhtkey;
939 plugin->dhtlog_api->update_connections = &add_connections;
940 plugin->dhtlog_api->insert_topology = &add_topology;
941 plugin->dhtlog_api->insert_extended_topology = &add_extended_topology;
942 plugin->dhtlog_api->update_topology = &update_topology;
943 plugin->dhtlog_api->set_malicious = &set_malicious;
944 plugin->dhtlog_api->add_generic_stat = &add_generic_stat;
945
946 return plugin;
947}
948
949/**
950 * Shutdown the plugin.
951 */
952void *
953libgnunet_plugin_dhtlog_mysql_dump_done (void *cls)
954{
955 struct GNUNET_DHTLOG_Handle *dhtlog_api = cls;
956
957 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MySQL DHT Logger: database shutdown\n");
958 GNUNET_assert (dhtlog_api != NULL);
959
960 GNUNET_free (dhtlog_api);
961 return NULL;
962}
963
964/* end of plugin_dhtlog_mysql.c */