move bit distance function into util
[oweals/gnunet.git] / src / dht / plugin_dhtlog_mysql.c
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  */
49 struct 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  */
76 typedef int (*GNUNET_MysqlDataProcessor) (void *cls,
77                                           unsigned int num_values,
78                                           MYSQL_BIND * values);
79
80 static unsigned long max_varchar_len;
81
82 /**
83  * The configuration the DHT service is running with
84  */
85 static const struct GNUNET_CONFIGURATION_Handle *cfg;
86
87 static unsigned long long current_trial = 0;    /* I like to assign 0, just to remember */
88
89 /**
90  * Connection to the MySQL Server.
91  */
92 static MYSQL *conn;
93
94 #define INSERT_QUERIES_STMT "INSERT INTO queries (trialuid, querytype, hops, dhtkeyuid, dhtqueryid, succeeded, nodeuid, time) "\
95                           "VALUES (?, ?, ?, ?, ?, ?, ?, NOW())"
96 static struct StatementHandle *insert_query;
97
98 #define INSERT_ROUTES_STMT "INSERT INTO routes (trialuid, querytype, hops, dhtkeyuid, dhtqueryid, succeeded, nodeuid, from_node, to_node) "\
99                           "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
100 static struct StatementHandle *insert_route;
101
102 #define INSERT_NODES_STMT "INSERT INTO nodes (trialuid, nodeid, nodebits) "\
103                           "VALUES (?, ?, ?)"
104 static struct StatementHandle *insert_node;
105
106 #define INSERT_ROUNDS_STMT "INSERT INTO rounds (trialuid, round_type, round_count, starttime) "\
107                           "VALUES (?, ?, ?, NOW())"
108
109 static struct StatementHandle *insert_round;
110
111 #define INSERT_TRIALS_STMT "INSERT INTO trials"\
112                             "(starttime, other_trial_identifier, numnodes, topology,"\
113                             "topology_percentage, topology_probability,"\
114                             "blacklist_topology, connect_topology, connect_topology_option,"\
115                             "connect_topology_option_modifier, puts, gets, "\
116                             "concurrent, settle_time, num_rounds, malicious_getters,"\
117                             "malicious_putters, malicious_droppers, malicious_get_frequency,"\
118                             "malicious_put_frequency, stop_closest, stop_found, strict_kademlia, "\
119                             "gets_succeeded, message) "\
120                             "VALUES (NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
121
122 static struct StatementHandle *insert_trial;
123
124 #define INSERT_STAT_STMT "INSERT INTO node_statistics"\
125                             "(trialuid, nodeuid, route_requests,"\
126                             "route_forwards, result_requests,"\
127                             "client_results, result_forwards, gets,"\
128                             "puts, data_inserts, find_peer_requests, "\
129                             "find_peers_started, gets_started, puts_started, find_peer_responses_received,"\
130                             "get_responses_received, find_peer_responses_sent, get_responses_sent) "\
131                             "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
132
133 static struct StatementHandle *insert_stat;
134
135 #define INSERT_GENERIC_STAT_STMT "INSERT INTO generic_stats" \
136                                  "(trialuid, nodeuid, section, name, value)"\
137                                  "VALUES (?, ?, ?, ?, ?)"
138 static struct StatementHandle *insert_generic_stat;
139
140 #define INSERT_DHTKEY_STMT "INSERT INTO dhtkeys (dhtkey, trialuid, keybits) "\
141                           "VALUES (?, ?, ?)"
142 static struct StatementHandle *insert_dhtkey;
143
144 #define UPDATE_TRIALS_STMT "UPDATE trials set endtime=NOW(), gets_succeeded = ? where trialuid = ?"
145 static struct StatementHandle *update_trial;
146
147 #define UPDATE_CONNECTIONS_STMT "UPDATE trials set totalConnections = ? where trialuid = ?"
148 static struct StatementHandle *update_connection;
149
150 #define GET_TRIAL_STMT "SELECT MAX( trialuid ) FROM trials"
151 static struct StatementHandle *get_trial;
152
153 #define GET_TOPOLOGY_STMT "SELECT MAX( topology_uid ) FROM topology"
154 static struct StatementHandle *get_topology;
155
156 #define GET_DHTKEYUID_STMT "SELECT dhtkeyuid FROM dhtkeys where dhtkey = ? and trialuid = ?"
157 static struct StatementHandle *get_dhtkeyuid;
158
159 #define GET_NODEUID_STMT "SELECT nodeuid FROM nodes where trialuid = ? and nodeid = ?"
160 static struct StatementHandle *get_nodeuid;
161
162 #define INSERT_TOPOLOGY_STMT "INSERT INTO topology (trialuid, date, connections) "\
163                              "VALUES (?, NOW(), ?)"
164 static struct StatementHandle *insert_topology;
165
166 #define EXTEND_TOPOLOGY_STMT "INSERT INTO extended_topology (topology_uid, uid_first, uid_second) "\
167                              "VALUES (?, ?, ?)"
168 static struct StatementHandle *extend_topology;
169
170 #define SET_MALICIOUS_STMT "update nodes set malicious_dropper = 1  where trialuid = ? and nodeid = ?"
171 static struct StatementHandle *update_node_malicious;
172
173 #define UPDATE_TOPOLOGY_STMT "update topology set connections = ?  where topology_uid = ?"
174 static struct StatementHandle *update_topology;
175
176 /**
177  * Run a query (not a select statement)
178  *
179  * @return GNUNET_OK if executed, GNUNET_SYSERR if an error occurred
180  */
181 int
182 run_statement (const char *statement)
183 {
184   mysql_query (conn, statement);
185   if (mysql_error (conn)[0])
186     {
187       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
188                  "mysql_query");
189       return GNUNET_SYSERR;
190     }
191   return GNUNET_OK;
192 }
193
194 /*
195  * Creates tables if they don't already exist for dht logging
196  */
197 static int
198 itable ()
199 {
200 #define MRUNS(a) (GNUNET_OK != run_statement (a) )
201
202   if (MRUNS ("CREATE TABLE IF NOT EXISTS `dhtkeys` ("
203              "dhtkeyuid int(10) unsigned NOT NULL auto_increment COMMENT 'Unique Key given to each query',"
204              "`dhtkey` varchar(255) NOT NULL COMMENT 'The ASCII value of the key being searched for',"
205              "trialuid int(10) unsigned NOT NULL,"
206              "keybits blob NOT NULL,"
207              "UNIQUE KEY `dhtkeyuid` (`dhtkeyuid`)"
208              ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
209     return GNUNET_SYSERR;
210
211   if (MRUNS ("CREATE TABLE IF NOT EXISTS `nodes` ("
212              "`nodeuid` int(10) unsigned NOT NULL auto_increment,"
213              "`trialuid` int(10) unsigned NOT NULL,"
214              "`nodeid` varchar(255) NOT NULL,"
215              "`nodebits` blob NOT NULL,"
216              "PRIMARY KEY  (`nodeuid`)"
217              ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
218     return GNUNET_SYSERR;
219
220   if (MRUNS ("CREATE TABLE IF NOT EXISTS `queries` ("
221              "`trialuid` int(10) unsigned NOT NULL,"
222              "`queryuid` int(10) unsigned NOT NULL auto_increment,"
223              "`dhtqueryid` bigint(20) NOT NULL,"
224              "`querytype` enum('1','2','3','4','5') NOT NULL,"
225              "`hops` int(10) unsigned NOT NULL,"
226              "`succeeded` tinyint NOT NULL,"
227              "`nodeuid` int(10) unsigned NOT NULL,"
228              "`time` timestamp NOT NULL default CURRENT_TIMESTAMP,"
229              "`dhtkeyuid` int(10) unsigned NOT NULL,"
230              "PRIMARY KEY  (`queryuid`)"
231              ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
232     return GNUNET_SYSERR;
233
234   if (MRUNS ("CREATE TABLE IF NOT EXISTS `routes` ("
235              "`trialuid` int(10) unsigned NOT NULL,"
236              "`queryuid` int(10) unsigned NOT NULL auto_increment,"
237              "`dhtqueryid` bigint(20) NOT NULL,"
238              "`querytype` enum('1','2','3','4','5') NOT NULL,"
239              "`hops` int(10) unsigned NOT NULL,"
240              "`succeeded` tinyint NOT NULL,"
241              "`nodeuid` int(10) unsigned NOT NULL,"
242              "`time` timestamp NOT NULL default CURRENT_TIMESTAMP,"
243              "`dhtkeyuid` int(10) unsigned NOT NULL,"
244              "`from_node` int(10) unsigned NOT NULL,"
245              "`to_node` int(10) unsigned NOT NULL,"
246              "PRIMARY KEY  (`queryuid`)"
247              ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
248     return GNUNET_SYSERR;
249
250   if (MRUNS ("CREATE TABLE IF NOT EXISTS `trials` ("
251               "`trialuid` int(10) unsigned NOT NULL auto_increment,"
252               "`other_trial_identifier` int(10) unsigned NOT NULL default '0',"
253               "`numnodes` int(10) unsigned NOT NULL,"
254               "`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,"
262               "`endtime` datetime NOT NULL,"
263               "`puts` int(10) unsigned NOT NULL,"
264               "`gets` int(10) unsigned NOT NULL,"
265               "`concurrent` int(10) unsigned NOT NULL,"
266               "`settle_time` int(10) unsigned NOT NULL,"
267               "`totalConnections` int(10) unsigned NOT NULL,"
268               "`message` text NOT NULL,"
269               "`num_rounds` int(10) unsigned NOT NULL,"
270               "`malicious_getters` int(10) unsigned NOT NULL,"
271               "`malicious_putters` int(10) unsigned NOT NULL,"
272               "`malicious_droppers` int(10) unsigned NOT NULL,"
273               "`topology_modifier` double NOT NULL,"
274               "`malicious_get_frequency` int(10) unsigned NOT NULL,"
275               "`malicious_put_frequency` int(10) unsigned NOT NULL,"
276               "`stop_closest` int(10) unsigned NOT NULL,"
277               "`stop_found` int(10) unsigned NOT NULL,"
278               "`strict_kademlia` int(10) unsigned NOT NULL,"
279               "`gets_succeeded` int(10) unsigned NOT NULL,"
280               "PRIMARY KEY  (`trialuid`),"
281               "UNIQUE KEY `trialuid` (`trialuid`)"
282               ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
283     return GNUNET_SYSERR;
284
285   if (MRUNS ("CREATE TABLE IF NOT EXISTS `topology` ("
286               "`topology_uid` int(10) unsigned NOT NULL AUTO_INCREMENT,"
287               "`trialuid` int(10) unsigned NOT NULL,"
288               "`date` datetime NOT NULL,"
289               "`connections` int(10) unsigned NOT NULL,"
290               "PRIMARY KEY (`topology_uid`)) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
291     return GNUNET_SYSERR;
292
293   if (MRUNS ("CREATE TABLE IF NOT EXISTS `extended_topology` ("
294              "`extended_uid` int(10) unsigned NOT NULL AUTO_INCREMENT,"
295              "`topology_uid` int(10) unsigned NOT NULL,"
296              "`uid_first` int(10) unsigned NOT NULL,"
297              "`uid_second` int(10) unsigned NOT NULL,"
298              "PRIMARY KEY (`extended_uid`)"
299              ") ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
300     return GNUNET_SYSERR;
301
302   if (MRUNS ("CREATE TABLE IF NOT EXISTS `node_statistics` ("
303               "`stat_uid` int(10) unsigned NOT NULL AUTO_INCREMENT,"
304               "`trialuid` int(10) unsigned NOT NULL,"
305               "`nodeuid` int(10) unsigned NOT NULL,"
306               "`route_requests` int(10) unsigned NOT NULL,"
307               "`route_forwards` int(10) unsigned NOT NULL,"
308               "`result_requests` int(10) unsigned NOT NULL,"
309               "`client_results` int(10) unsigned NOT NULL,"
310               "`result_forwards` int(10) unsigned NOT NULL,"
311               "`gets` int(10) unsigned NOT NULL,"
312               "`puts` int(10) unsigned NOT NULL,"
313               "`data_inserts` int(10) unsigned NOT NULL,"
314               "`find_peer_requests` int(10) unsigned NOT NULL,"
315               "`find_peers_started` int(10) unsigned NOT NULL,"
316               "`gets_started` int(10) unsigned NOT NULL,"
317               "`puts_started` int(10) unsigned NOT NULL,"
318               "`find_peer_responses_received` int(10) unsigned NOT NULL,"
319               "`get_responses_received` int(10) unsigned NOT NULL,"
320               "`find_peer_responses_sent` int(10) unsigned NOT NULL,"
321               "`get_responses_sent` int(10) unsigned NOT NULL,"
322              "PRIMARY KEY (`stat_uid`)"
323             ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;"))
324     return GNUNET_SYSERR;
325
326   if (MRUNS ("SET AUTOCOMMIT = 1"))
327     return GNUNET_SYSERR;
328
329   return GNUNET_OK;
330 #undef MRUNS
331 }
332
333 /**
334  * Create a prepared statement.
335  *
336  * @return NULL on error
337  */
338 struct StatementHandle *
339 prepared_statement_create (const char *statement)
340 {
341   struct StatementHandle *ret;
342
343   ret = GNUNET_malloc (sizeof (struct StatementHandle));
344   ret->query = GNUNET_strdup (statement);
345   return ret;
346 }
347
348 /**
349  * Close a prepared statement.
350  *
351  * @return NULL on error
352  */
353 void
354 prepared_statement_close (struct StatementHandle *s)
355 {
356   if (s == NULL)
357     {
358       return;
359     }
360
361   GNUNET_free_non_null(s->query);
362
363   if (s->valid == GNUNET_YES)
364     mysql_stmt_close(s->statement);
365   GNUNET_free(s);
366 }
367
368 /*
369  * Initialize the prepared statements for use with dht test logging
370  */
371 static int
372 iopen (struct GNUNET_DHTLOG_Plugin *plugin)
373 {
374   int ret;
375   my_bool reconnect;
376   unsigned int timeout;
377   char *user;
378   char *password;
379   char *server;
380   char *database;
381   unsigned long long port;
382
383   conn = mysql_init (NULL);
384   if (conn == NULL)
385     return GNUNET_SYSERR;
386
387   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (plugin->cfg,
388                                                          "MYSQL", "DATABASE",
389                                                          &database))
390     {
391       database = GNUNET_strdup("gnunet");
392     }
393
394   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (plugin->cfg,
395                                                           "MYSQL", "USER", &user))
396     {
397       user = GNUNET_strdup("dht");
398     }
399
400   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (plugin->cfg,
401                                                           "MYSQL", "PASSWORD", &password))
402     {
403       password = GNUNET_strdup("dhttest**");
404     }
405
406   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (plugin->cfg,
407                                                           "MYSQL", "SERVER", &server))
408     {
409       server = GNUNET_strdup("localhost");
410     }
411
412   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (plugin->cfg,
413                                                           "MYSQL", "MYSQL_PORT", &port))
414     {
415       port = 0;
416     }
417
418   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to mysql with: user %s, pass %s, server %s, database %s, port %d\n",
419               user, password, server, database, port);
420
421   reconnect = 0;
422   timeout = 60; /* in seconds */
423   mysql_options (conn, MYSQL_OPT_RECONNECT, &reconnect);
424   mysql_options (conn,
425                  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,
430                       database, (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,
440                  "mysql_real_connect");
441       return GNUNET_SYSERR;
442     }
443
444 #if OLD
445   db = GNUNET_MYSQL_database_open (coreAPI->ectx, coreAPI->cfg);
446   if (db == NULL)
447     return GNUNET_SYSERR;
448 #endif
449
450   ret = itable ();
451
452 #define PINIT(a,b) (NULL == (a = prepared_statement_create(b)))
453   if (PINIT (insert_query, INSERT_QUERIES_STMT) ||
454       PINIT (insert_route, INSERT_ROUTES_STMT) ||
455       PINIT (insert_trial, INSERT_TRIALS_STMT) ||
456       PINIT (insert_round, INSERT_ROUNDS_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
479 static int
480 return_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  */
490 static int
491 prepare_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,
503                  "mysql_stmt_prepare `%s', %s", 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  */
520 static int
521 init_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           break;
546         case MYSQL_TYPE_LONGLONG:
547           qbind[off].buffer = va_arg (ap, unsigned long long *);
548           qbind[off].is_unsigned = va_arg (ap, int);
549           break;
550         case MYSQL_TYPE_LONG:
551           qbind[off].buffer = va_arg (ap, unsigned int *);
552           qbind[off].is_unsigned = va_arg (ap, int);
553           break;
554         case MYSQL_TYPE_VAR_STRING:
555         case MYSQL_TYPE_STRING:
556         case MYSQL_TYPE_BLOB:
557           qbind[off].buffer = va_arg (ap, void *);
558           qbind[off].buffer_length = va_arg (ap, unsigned long);
559           qbind[off].length = va_arg (ap, unsigned long *);
560           break;
561         default:
562           /* unsupported type */
563           GNUNET_break (0);
564           return GNUNET_SYSERR;
565         }
566       pc--;
567       off++;
568     }
569   if (!((pc == 0) && (ft != -1) && (va_arg (ap, int) == -1)))
570     {
571       GNUNET_break (0);
572       return GNUNET_SYSERR;
573     }
574   if (mysql_stmt_bind_param (s->statement, qbind))
575     {
576       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
577                   _("`%s' failed at %s:%d with error: %s\n"),
578                   "mysql_stmt_bind_param",
579                    __FILE__, __LINE__, mysql_stmt_error (s->statement));
580       return GNUNET_SYSERR;
581     }
582
583   if (mysql_stmt_execute (s->statement))
584     {
585     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
586                _("`%s' failed at %s:%d with error: %s\n"),
587                "mysql_stmt_execute",
588                __FILE__, __LINE__, mysql_stmt_error (s->statement));
589       return GNUNET_SYSERR;
590     }
591
592   return GNUNET_OK;
593 }
594
595 /**
596  * Run a prepared SELECT statement.
597  *
598  * @param s handle to the statement we should execute
599  * @param result_size number of results in set
600  * @param results pointer to already initialized MYSQL_BIND
601  *        array (of sufficient size) for passing results
602  * @param processor function to call on each result
603  * @param processor_cls extra argument to processor
604  * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
605  *        values (size + buffer-reference for pointers); terminated
606  *        with "-1"
607  *
608  * @return GNUNET_SYSERR on error, otherwise
609  *         the number of successfully affected (or queried) rows
610  */
611 int
612 prepared_statement_run_select (struct StatementHandle *s,
613                                unsigned int result_size,
614                                MYSQL_BIND * results,
615                                GNUNET_MysqlDataProcessor
616                                processor, void *processor_cls,
617                                ...)
618 {
619   va_list ap;
620   int ret;
621   unsigned int rsize;
622   int total;
623
624   if (GNUNET_OK != prepare_statement (s))
625     {
626       GNUNET_break (0);
627       return GNUNET_SYSERR;
628     }
629   va_start (ap, processor_cls);
630   if (GNUNET_OK != init_params (s, ap))
631     {
632       GNUNET_break (0);
633       va_end (ap);
634       return GNUNET_SYSERR;
635     }
636   va_end (ap);
637   rsize = mysql_stmt_field_count (s->statement);
638   if (rsize > result_size)
639     {
640       GNUNET_break (0);
641       return GNUNET_SYSERR;
642     }
643   if (mysql_stmt_bind_result (s->statement, results))
644     {
645       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
646                   _("`%s' failed at %s:%d with error: %s\n"),
647                   "mysql_stmt_bind_result",
648                   __FILE__, __LINE__, mysql_stmt_error (s->statement));
649       return GNUNET_SYSERR;
650     }
651
652   total = 0;
653   while (1)
654     {
655       ret = mysql_stmt_fetch (s->statement);
656       if (ret == MYSQL_NO_DATA)
657         break;
658       if (ret != 0)
659         {
660           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
661                        _("`%s' failed at %s:%d with error: %s\n"),
662                        "mysql_stmt_fetch",
663                        __FILE__, __LINE__, mysql_stmt_error (s->statement));
664           return GNUNET_SYSERR;
665         }
666       if (processor != NULL)
667         if (GNUNET_OK != processor (processor_cls, rsize, results))
668           break;
669       total++;
670     }
671   mysql_stmt_reset (s->statement);
672   return total;
673 }
674
675
676 static int
677 get_node_uid (unsigned long long *nodeuid, const GNUNET_HashCode * peerHash)
678 {
679   MYSQL_BIND rbind[1];
680   struct GNUNET_CRYPTO_HashAsciiEncoded encPeer;
681   unsigned long long p_len;
682
683   int ret;
684   memset (rbind, 0, sizeof (rbind));
685   rbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
686   rbind[0].buffer = nodeuid;
687   rbind[0].is_unsigned = GNUNET_YES;
688
689   GNUNET_CRYPTO_hash_to_enc (peerHash, &encPeer);
690   p_len = strlen ((char *) &encPeer);
691
692   if (1 != (ret = prepared_statement_run_select (get_nodeuid,
693                                                               1,
694                                                               rbind,
695                                                               return_ok,
696                                                               NULL,
697                                                               MYSQL_TYPE_LONGLONG,
698                                                               &current_trial,
699                                                               GNUNET_YES,
700                                                               MYSQL_TYPE_VAR_STRING,
701                                                               &encPeer,
702                                                               max_varchar_len,
703                                                               &p_len, -1)))
704     {
705 #if DEBUG_DHTLOG
706       fprintf (stderr, "FAILED\n");
707 #endif
708       return GNUNET_SYSERR;
709     }
710   return GNUNET_OK;
711 }
712
713 static int
714 get_current_trial (unsigned long long *trialuid)
715 {
716   MYSQL_BIND rbind[1];
717
718   memset (rbind, 0, sizeof (rbind));
719   rbind[0].buffer_type = MYSQL_TYPE_LONG;
720   rbind[0].is_unsigned = 1;
721   rbind[0].buffer = trialuid;
722
723   if ((GNUNET_OK !=
724        prepared_statement_run_select (get_trial,
725                                       1,
726                                       rbind,
727                                       return_ok, NULL, -1)))
728     {
729       return GNUNET_SYSERR;
730     }
731
732   return GNUNET_OK;
733 }
734
735 static int
736 get_current_topology (unsigned long long *topologyuid)
737 {
738   MYSQL_BIND rbind[1];
739
740   memset (rbind, 0, sizeof (rbind));
741   rbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
742   rbind[0].is_unsigned = 1;
743   rbind[0].buffer = topologyuid;
744
745   if ((GNUNET_OK !=
746        prepared_statement_run_select (get_topology,
747                                       1,
748                                       rbind,
749                                       return_ok, NULL, -1)))
750     {
751       return GNUNET_SYSERR;
752     }
753
754   return GNUNET_OK;
755 }
756
757 static int
758 get_dhtkey_uid (unsigned long long *dhtkeyuid, const GNUNET_HashCode * key)
759 {
760   MYSQL_BIND rbind[1];
761   struct GNUNET_CRYPTO_HashAsciiEncoded encKey;
762   unsigned long long k_len;
763   memset (rbind, 0, sizeof (rbind));
764   rbind[0].buffer_type = MYSQL_TYPE_LONG;
765   rbind[0].is_unsigned = 1;
766   rbind[0].buffer = dhtkeyuid;
767   GNUNET_CRYPTO_hash_to_enc (key, &encKey);
768   k_len = strlen ((char *) &encKey);
769 #if DEBUG_DHTLOG
770   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Searching for dhtkey `%s' in trial %llu\n", GNUNET_h2s(key), current_trial);
771 #endif
772   if ((GNUNET_OK !=
773        prepared_statement_run_select (get_dhtkeyuid,
774                                       1,
775                                       rbind,
776                                       return_ok, NULL,
777                                       MYSQL_TYPE_VAR_STRING,
778                                       &encKey,
779                                       max_varchar_len,
780                                       &k_len,
781                                       MYSQL_TYPE_LONGLONG,
782                                       &current_trial,
783                                       GNUNET_YES, -1)))
784     {
785       return GNUNET_SYSERR;
786     }
787
788   return GNUNET_OK;
789 }
790
791 /**
792  * Run a prepared statement that does NOT produce results.
793  *
794  * @param s handle to the statement we should execute
795  * @param insert_id NULL or address where to store the row ID of whatever
796  *        was inserted (only for INSERT statements!)
797  * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
798  *        values (size + buffer-reference for pointers); terminated
799  *        with "-1"
800  *
801  * @return GNUNET_SYSERR on error, otherwise
802  *         the number of successfully affected rows
803  */
804 int
805 prepared_statement_run (struct StatementHandle *s,
806                         unsigned long long *insert_id, ...)
807 {
808   va_list ap;
809   int affected;
810
811   if (GNUNET_OK != prepare_statement(s))
812     {
813       GNUNET_break(0);
814       return GNUNET_SYSERR;
815     }
816   GNUNET_assert(s->valid == GNUNET_YES);
817   if (s->statement == NULL)
818     return GNUNET_SYSERR;
819
820   va_start (ap, insert_id);
821
822   if (GNUNET_OK != init_params (s, ap))
823     {
824       va_end (ap);
825       return GNUNET_SYSERR;
826     }
827
828   va_end (ap);
829   affected = mysql_stmt_affected_rows (s->statement);
830   if (NULL != insert_id)
831     *insert_id = (unsigned long long) mysql_stmt_insert_id (s->statement);
832   mysql_stmt_reset (s->statement);
833
834   return affected;
835 }
836
837 /*
838  * Inserts the specified trial into the dhttests.trials table
839  *
840  * @param trial_info struct containing the data to insert about this trial
841  *
842  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
843  */
844 int add_trial (struct GNUNET_DHTLOG_TrialInfo *trial_info)
845 {
846   MYSQL_STMT *stmt;
847   int ret;
848   unsigned long long m_len;
849   m_len = strlen (trial_info->message);
850
851   stmt = mysql_stmt_init(conn);
852   if (GNUNET_OK !=
853       (ret = prepared_statement_run (insert_trial, &trial_info->trialuid,
854                                      MYSQL_TYPE_LONG, &trial_info->other_identifier, GNUNET_YES,
855                                      MYSQL_TYPE_LONG, &trial_info->num_nodes, GNUNET_YES,
856                                      MYSQL_TYPE_LONG, &trial_info->topology, GNUNET_YES,
857                                      MYSQL_TYPE_FLOAT, &trial_info->topology_percentage,
858                                      MYSQL_TYPE_FLOAT, &trial_info->topology_probability,
859                                      MYSQL_TYPE_LONG, &trial_info->blacklist_topology, GNUNET_YES,
860                                      MYSQL_TYPE_LONG, &trial_info->connect_topology, GNUNET_YES,
861                                      MYSQL_TYPE_LONG, &trial_info->connect_topology_option, GNUNET_YES,
862                                      MYSQL_TYPE_FLOAT, &trial_info->connect_topology_option_modifier,
863                                      MYSQL_TYPE_LONG, &trial_info->puts, GNUNET_YES,
864                                      MYSQL_TYPE_LONG, &trial_info->gets, GNUNET_YES,
865                                      MYSQL_TYPE_LONG, &trial_info->concurrent, GNUNET_YES,
866                                      MYSQL_TYPE_LONG, &trial_info->settle_time, GNUNET_YES,
867                                      MYSQL_TYPE_LONG, &trial_info->num_rounds, GNUNET_YES,
868                                      MYSQL_TYPE_LONG, &trial_info->malicious_getters, GNUNET_YES,
869                                      MYSQL_TYPE_LONG, &trial_info->malicious_putters, GNUNET_YES,
870                                      MYSQL_TYPE_LONG, &trial_info->malicious_droppers, GNUNET_YES,
871                                      MYSQL_TYPE_LONG, &trial_info->malicious_get_frequency, GNUNET_YES,
872                                      MYSQL_TYPE_LONG, &trial_info->malicious_put_frequency, GNUNET_YES,
873                                      MYSQL_TYPE_LONG, &trial_info->stop_closest, GNUNET_YES,
874                                      MYSQL_TYPE_LONG, &trial_info->stop_found, GNUNET_YES,
875                                      MYSQL_TYPE_LONG, &trial_info->strict_kademlia, GNUNET_YES,
876                                      MYSQL_TYPE_LONG, &trial_info->gets_succeeded, GNUNET_YES,
877                                      MYSQL_TYPE_BLOB, trial_info->message, max_varchar_len +
878                                      max_varchar_len, &m_len,
879                                       -1)))
880     {
881       if (ret == GNUNET_SYSERR)
882         {
883           mysql_stmt_close(stmt);
884           return GNUNET_SYSERR;
885         }
886     }
887
888   get_current_trial (&current_trial);
889
890   mysql_stmt_close(stmt);
891   return GNUNET_OK;
892 }
893
894 /*
895  * Inserts the specified round into the dhttests.rounds table
896  *
897  * @param round_type the type of round that is being started
898  * @param round_count counter for the round (if applicable)
899  *
900  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
901  */
902 int add_round (unsigned int round_type, unsigned int round_count)
903 {
904
905   MYSQL_STMT *stmt;
906   int ret;
907
908   stmt = mysql_stmt_init(conn);
909   ret = prepared_statement_run (insert_round,
910                                 NULL,
911                                 MYSQL_TYPE_LONGLONG, &current_trial, GNUNET_YES,
912                                 MYSQL_TYPE_LONG, &round_type, GNUNET_YES,
913                                 MYSQL_TYPE_LONG, &round_count, GNUNET_YES, -1);
914   mysql_stmt_close(stmt);
915   if (ret != GNUNET_OK)
916     return GNUNET_SYSERR;
917   return ret;
918 }
919
920 /*
921  * Inserts the specified stats into the dhttests.node_statistics table
922  *
923  * @param peer the peer inserting the statistic
924  * @param route_requests route requests seen
925  * @param route_forwards route requests forwarded
926  * @param result_requests route result requests seen
927  * @param client_requests client requests initiated
928  * @param result_forwards route results forwarded
929  * @param gets get requests handled
930  * @param puts put requests handle
931  * @param data_inserts data inserted at this node
932  * @param find_peer_requests find peer requests seen
933  * @param find_peers_started find peer requests initiated at this node
934  * @param gets_started get requests initiated at this node
935  * @param puts_started put requests initiated at this node
936  * @param find_peer_responses_received find peer responses received locally
937  * @param get_responses_received get responses received locally
938  * @param find_peer_responses_sent find peer responses sent from this node
939  * @param get_responses_sent get responses sent from this node
940  *
941  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
942  */
943 int
944 add_stat (const struct GNUNET_PeerIdentity *peer, unsigned int route_requests,
945           unsigned int route_forwards, unsigned int result_requests,
946           unsigned int client_requests, unsigned int result_forwards,
947           unsigned int gets, unsigned int puts,
948           unsigned int data_inserts, unsigned int find_peer_requests,
949           unsigned int find_peers_started, unsigned int gets_started,
950           unsigned int puts_started, unsigned int find_peer_responses_received,
951           unsigned int get_responses_received, unsigned int find_peer_responses_sent,
952           unsigned int get_responses_sent)
953 {
954   MYSQL_STMT *stmt;
955   int ret;
956   unsigned long long peer_uid;
957   unsigned long long return_uid;
958   if (peer == NULL)
959     return GNUNET_SYSERR;
960
961   if (GNUNET_OK != get_node_uid (&peer_uid, &peer->hashPubKey))
962     {
963       return GNUNET_SYSERR;
964     }
965
966   stmt = mysql_stmt_init(conn);
967   if (GNUNET_OK !=
968       (ret = prepared_statement_run (insert_stat,
969                                      &return_uid,
970                                      MYSQL_TYPE_LONGLONG, &current_trial, GNUNET_YES,
971                                      MYSQL_TYPE_LONGLONG, &peer_uid, GNUNET_YES,
972                                      MYSQL_TYPE_LONG, &route_requests, GNUNET_YES,
973                                      MYSQL_TYPE_LONG, &route_forwards, GNUNET_YES,
974                                      MYSQL_TYPE_LONG, &result_requests, GNUNET_YES,
975                                      MYSQL_TYPE_LONG, &client_requests, GNUNET_YES,
976                                      MYSQL_TYPE_LONG, &result_forwards, GNUNET_YES,
977                                      MYSQL_TYPE_LONG, &gets, GNUNET_YES,
978                                      MYSQL_TYPE_LONG, &puts, GNUNET_YES,
979                                      MYSQL_TYPE_LONG, &data_inserts, GNUNET_YES,
980                                      MYSQL_TYPE_LONG, &find_peer_requests, GNUNET_YES,
981                                      MYSQL_TYPE_LONG, &find_peers_started, GNUNET_YES,
982                                      MYSQL_TYPE_LONG, &gets_started, GNUNET_YES,
983                                      MYSQL_TYPE_LONG, &puts_started, GNUNET_YES,
984                                      MYSQL_TYPE_LONG, &find_peer_responses_received, GNUNET_YES,
985                                      MYSQL_TYPE_LONG, &get_responses_received, GNUNET_YES,
986                                      MYSQL_TYPE_LONG, &find_peer_responses_sent, GNUNET_YES,
987                                      MYSQL_TYPE_LONG, &get_responses_sent, GNUNET_YES,
988                                      -1)))
989     {
990       if (ret == GNUNET_SYSERR)
991         {
992           mysql_stmt_close(stmt);
993           return GNUNET_SYSERR;
994         }
995     }
996
997   mysql_stmt_close(stmt);
998   return GNUNET_OK;
999 }
1000
1001 /*
1002  * Inserts the specified stats into the dhttests.generic_stats table
1003  *
1004  * @param peer the peer inserting the statistic
1005  * @param name the name of the statistic
1006  * @param section the section of the statistic
1007  * @param value the value of the statistic
1008  *
1009  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1010  */
1011 int
1012 add_generic_stat (const struct GNUNET_PeerIdentity *peer,
1013                   const char *name,
1014                   const char *section, uint64_t value)
1015 {
1016   unsigned long long peer_uid;
1017   unsigned long long section_len;
1018   unsigned long long name_len;
1019   int ret;
1020   if (peer == NULL)
1021     return GNUNET_SYSERR;
1022
1023   if (GNUNET_OK != get_node_uid (&peer_uid, &peer->hashPubKey))
1024     {
1025       return GNUNET_SYSERR;
1026     }
1027
1028   section_len = strlen(section);
1029   name_len = strlen(name);
1030
1031   if (GNUNET_OK !=
1032       (ret = prepared_statement_run (insert_generic_stat,
1033                                      NULL,
1034                                      MYSQL_TYPE_LONGLONG, &current_trial, GNUNET_YES,
1035                                      MYSQL_TYPE_LONGLONG, &peer_uid, GNUNET_YES,
1036                                      MYSQL_TYPE_VAR_STRING, &section, max_varchar_len, &section_len,
1037                                      MYSQL_TYPE_VAR_STRING, &name, max_varchar_len, &name_len,
1038                                      MYSQL_TYPE_LONGLONG, &value, GNUNET_YES,
1039                                      -1)))
1040     {
1041       if (ret == GNUNET_SYSERR)
1042         {
1043           return GNUNET_SYSERR;
1044         }
1045     }
1046   return GNUNET_OK;
1047 }
1048
1049 /*
1050  * Inserts the specified dhtkey into the dhttests.dhtkeys table,
1051  * stores return value of dhttests.dhtkeys.dhtkeyuid into dhtkeyuid
1052  *
1053  * @param dhtkeyuid return value
1054  * @param dhtkey hashcode of key to insert
1055  *
1056  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1057  */
1058 int
1059 add_dhtkey (unsigned long long *dhtkeyuid, const GNUNET_HashCode * dhtkey)
1060 {
1061
1062   int ret;
1063   struct GNUNET_CRYPTO_HashAsciiEncoded encKey;
1064   unsigned long long k_len;
1065   unsigned long long h_len;
1066   unsigned long long curr_dhtkeyuid;
1067   GNUNET_CRYPTO_hash_to_enc (dhtkey, &encKey);
1068   k_len = strlen ((char *) &encKey);
1069   h_len = sizeof (GNUNET_HashCode);
1070   curr_dhtkeyuid = 0;
1071   ret = get_dhtkey_uid(&curr_dhtkeyuid, dhtkey);
1072   if (curr_dhtkeyuid != 0) /* dhtkey already exists */
1073     {
1074       if (dhtkeyuid != NULL)
1075         *dhtkeyuid = curr_dhtkeyuid;
1076       return GNUNET_OK;
1077     }
1078   else if (ret == GNUNET_SYSERR)
1079     {
1080 #if DEBUG_DHTLOG
1081       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Failed to get dhtkeyuid!\n");
1082 #endif
1083     }
1084
1085   if (GNUNET_OK !=
1086       (ret = prepared_statement_run (insert_dhtkey,
1087                                      dhtkeyuid,
1088                                      MYSQL_TYPE_VAR_STRING,
1089                                      &encKey,
1090                                      max_varchar_len,
1091                                      &k_len,
1092                                      MYSQL_TYPE_LONG,
1093                                      &current_trial,
1094                                      GNUNET_YES,
1095                                      MYSQL_TYPE_BLOB,
1096                                      dhtkey,
1097                                      sizeof (GNUNET_HashCode),
1098                                      &h_len, -1)))
1099     {
1100       if (ret == GNUNET_SYSERR)
1101         {
1102           return GNUNET_SYSERR;
1103         }
1104     }
1105
1106   return GNUNET_OK;
1107 }
1108
1109
1110
1111 /*
1112  * Inserts the specified node into the dhttests.nodes table
1113  *
1114  * @param nodeuid the inserted node uid
1115  * @param node the node to insert
1116  *
1117  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1118  */
1119 int
1120 add_node (unsigned long long *nodeuid, struct GNUNET_PeerIdentity * node)
1121 {
1122   struct GNUNET_CRYPTO_HashAsciiEncoded encPeer;
1123   unsigned long p_len;
1124   unsigned long h_len;
1125   int ret;
1126
1127   if (node == NULL)
1128     return GNUNET_SYSERR;
1129
1130   GNUNET_CRYPTO_hash_to_enc (&node->hashPubKey, &encPeer);
1131   p_len = (unsigned long) strlen ((char *) &encPeer);
1132   h_len = sizeof (GNUNET_HashCode);
1133   if (GNUNET_OK !=
1134       (ret = prepared_statement_run (insert_node,
1135                                      nodeuid,
1136                                      MYSQL_TYPE_LONGLONG, &current_trial, GNUNET_YES,
1137                                      MYSQL_TYPE_VAR_STRING, &encPeer, max_varchar_len, &p_len,
1138                                      MYSQL_TYPE_BLOB, &node->hashPubKey, sizeof (GNUNET_HashCode),
1139                                      &h_len, -1)))
1140     {
1141       if (ret == GNUNET_SYSERR)
1142         {
1143           return GNUNET_SYSERR;
1144         }
1145     }
1146   return GNUNET_OK;
1147 }
1148
1149 /*
1150  * Update dhttests.trials table with current server time as end time
1151  *
1152  * @param trialuid trial to update
1153  * @param gets_succeeded how many gets did the testcase report as successful
1154  *
1155  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1156  */
1157 int
1158 update_trials (unsigned long long trialuid,
1159                unsigned int gets_succeeded)
1160 {
1161   int ret;
1162
1163   if (GNUNET_OK !=
1164       (ret = prepared_statement_run (update_trial,
1165                                     NULL,
1166                                     MYSQL_TYPE_LONG, &gets_succeeded, GNUNET_YES,
1167                                     MYSQL_TYPE_LONGLONG, &trialuid, GNUNET_YES,
1168                                     -1)))
1169     {
1170       if (ret == GNUNET_SYSERR)
1171         {
1172           return GNUNET_SYSERR;
1173         }
1174     }
1175   if (ret > 0)
1176     return GNUNET_OK;
1177   else
1178     return GNUNET_SYSERR;
1179 }
1180
1181
1182 /*
1183  * Update dhttests.nodes table setting the identified
1184  * node as a malicious dropper.
1185  *
1186  * @param peer the peer that was set to be malicious
1187  *
1188  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1189  */
1190 int set_malicious (struct GNUNET_PeerIdentity *peer)
1191 {
1192   unsigned long long p_len;
1193   int ret;
1194   char *temp_str;
1195
1196   temp_str = GNUNET_strdup(GNUNET_h2s_full(&peer->hashPubKey));
1197   p_len = strlen(temp_str);
1198
1199   if (GNUNET_OK !=
1200       (ret = prepared_statement_run (update_node_malicious,
1201                                     NULL,
1202                                     MYSQL_TYPE_LONGLONG, &current_trial, GNUNET_YES,
1203                                     MYSQL_TYPE_VAR_STRING, temp_str, max_varchar_len, &p_len,
1204                                     -1)))
1205     {
1206       if (ret == GNUNET_SYSERR)
1207         {
1208           return GNUNET_SYSERR;
1209         }
1210     }
1211   return GNUNET_OK;
1212 }
1213
1214
1215 /*
1216  * Update dhttests.trials table with total connections information
1217  *
1218  * @param trialuid the trialuid to update
1219  * @param totalConnections the number of connections
1220  *
1221  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1222  */
1223 int
1224 add_connections (unsigned long long trialuid, unsigned int totalConnections)
1225 {
1226   int ret;
1227
1228   if (GNUNET_OK !=
1229       (ret = prepared_statement_run (update_connection,
1230                                                   NULL,
1231                                                   MYSQL_TYPE_LONG,
1232                                                   &totalConnections,
1233                                                   GNUNET_YES,
1234                                                   MYSQL_TYPE_LONGLONG,
1235                                                   &trialuid, GNUNET_YES, -1)))
1236     {
1237       if (ret == GNUNET_SYSERR)
1238         {
1239           return GNUNET_SYSERR;
1240         }
1241     }
1242   if (ret > 0)
1243     return GNUNET_OK;
1244   else
1245     return GNUNET_SYSERR;
1246 }
1247
1248 /*
1249  * Inserts the specified query into the dhttests.queries table
1250  *
1251  * @param sqlqueruid inserted query uid
1252  * @param queryid dht query id
1253  * @param type type of the query
1254  * @param hops number of hops query traveled
1255  * @param succeeded whether or not query was successful
1256  * @param node the node the query hit
1257  * @param key the key of the query
1258  *
1259  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1260  */
1261 int
1262 add_query (unsigned long long *sqlqueryuid, unsigned long long queryid,
1263            unsigned int type, unsigned int hops, int succeeded,
1264            const struct GNUNET_PeerIdentity * node, const GNUNET_HashCode * key)
1265 {
1266   int ret;
1267   unsigned long long peer_uid, key_uid;
1268   peer_uid = 0;
1269   key_uid = 0;
1270
1271   if ((node != NULL)
1272       && (GNUNET_OK == get_node_uid (&peer_uid, &node->hashPubKey)))
1273     {
1274
1275     }
1276   else
1277     {
1278       return GNUNET_SYSERR;
1279     }
1280
1281   if ((key != NULL) && (GNUNET_OK == get_dhtkey_uid (&key_uid, key)))
1282     {
1283
1284     }
1285   else if ((key != NULL) && (key->bits[(512 / 8 / sizeof (unsigned int)) - 1] == 42))   /* Malicious marker */
1286     {
1287       key_uid = 0;
1288     }
1289   else
1290     {
1291       return GNUNET_SYSERR;
1292     }
1293
1294   if (GNUNET_OK !=
1295       (ret = prepared_statement_run (insert_query,
1296                                                   sqlqueryuid,
1297                                                   MYSQL_TYPE_LONGLONG,
1298                                                   &current_trial,
1299                                                   GNUNET_YES,
1300                                                   MYSQL_TYPE_LONG,
1301                                                   &type,
1302                                                   GNUNET_NO,
1303                                                   MYSQL_TYPE_LONG,
1304                                                   &hops,
1305                                                   GNUNET_YES,
1306                                                   MYSQL_TYPE_LONGLONG,
1307                                                   &key_uid,
1308                                                   GNUNET_YES,
1309                                                   MYSQL_TYPE_LONGLONG,
1310                                                   &queryid,
1311                                                   GNUNET_YES,
1312                                                   MYSQL_TYPE_LONG,
1313                                                   &succeeded,
1314                                                   GNUNET_NO,
1315                                                   MYSQL_TYPE_LONGLONG,
1316                                                   &peer_uid, GNUNET_YES, -1)))
1317     {
1318       if (ret == GNUNET_SYSERR)
1319         {
1320           return GNUNET_SYSERR;
1321         }
1322     }
1323   if (ret > 0)
1324     return GNUNET_OK;
1325   else
1326     return GNUNET_SYSERR;
1327 }
1328
1329 /*
1330  * Inserts the specified route information into the dhttests.routes table
1331  *
1332  * @param sqlqueruid inserted query uid
1333  * @param queryid dht query id
1334  * @param type type of the query
1335  * @param hops number of hops query traveled
1336  * @param succeeded whether or not query was successful
1337  * @param node the node the query hit
1338  * @param key the key of the query
1339  * @param from_node the node that sent the message to node
1340  * @param to_node next node to forward message to
1341  *
1342  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1343  */
1344 int
1345 add_route (unsigned long long *sqlqueryuid, unsigned long long queryid,
1346            unsigned int type, unsigned int hops,
1347            int succeeded, const struct GNUNET_PeerIdentity * node,
1348            const GNUNET_HashCode * key, const struct GNUNET_PeerIdentity * from_node,
1349            const struct GNUNET_PeerIdentity * to_node)
1350 {
1351   unsigned long long peer_uid = 0;
1352   unsigned long long key_uid = 0;
1353   unsigned long long from_uid = 0;
1354   unsigned long long to_uid = 0;
1355   int ret;
1356
1357   if (from_node != NULL)
1358     get_node_uid (&from_uid, &from_node->hashPubKey);
1359   else
1360     from_uid = 0;
1361
1362   if (to_node != NULL)
1363     get_node_uid (&to_uid, &to_node->hashPubKey);
1364   else
1365     to_uid = 0;
1366
1367   if ((node != NULL))
1368     {
1369       if (1 != get_node_uid (&peer_uid, &node->hashPubKey))
1370         {
1371           return GNUNET_SYSERR;
1372         }
1373     }
1374   else
1375     return GNUNET_SYSERR;
1376
1377   if ((key != NULL))
1378     {
1379       if (1 != get_dhtkey_uid (&key_uid, key))
1380         {
1381           return GNUNET_SYSERR;
1382         }
1383     }
1384   else
1385     return GNUNET_SYSERR;
1386
1387   if (GNUNET_OK !=
1388       (ret = prepared_statement_run (insert_route,
1389                                     sqlqueryuid,
1390                                     MYSQL_TYPE_LONGLONG,
1391                                     &current_trial,
1392                                     GNUNET_YES,
1393                                     MYSQL_TYPE_LONG,
1394                                     &type,
1395                                     GNUNET_NO,
1396                                     MYSQL_TYPE_LONG,
1397                                     &hops,
1398                                     GNUNET_YES,
1399                                     MYSQL_TYPE_LONGLONG,
1400                                     &key_uid,
1401                                     GNUNET_YES,
1402                                     MYSQL_TYPE_LONGLONG,
1403                                     &queryid,
1404                                     GNUNET_YES,
1405                                     MYSQL_TYPE_LONG,
1406                                     &succeeded,
1407                                     GNUNET_NO,
1408                                     MYSQL_TYPE_LONGLONG,
1409                                     &peer_uid,
1410                                     GNUNET_YES,
1411                                     MYSQL_TYPE_LONGLONG,
1412                                     &from_uid,
1413                                     GNUNET_YES,
1414                                     MYSQL_TYPE_LONGLONG,
1415                                     &to_uid, GNUNET_YES, -1)))
1416     {
1417       if (ret == GNUNET_SYSERR)
1418         {
1419           return GNUNET_SYSERR;
1420         }
1421     }
1422   if (ret > 0)
1423     return GNUNET_OK;
1424   else
1425     return GNUNET_SYSERR;
1426 }
1427
1428 /*
1429  * Update dhttests.topology table with total connections information
1430  *
1431  * @param totalConnections the number of connections
1432  *
1433  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1434  */
1435 int
1436 update_current_topology (unsigned int connections)
1437 {
1438   int ret;
1439   unsigned long long topologyuid;
1440
1441   get_current_topology(&topologyuid);
1442
1443   if (GNUNET_OK !=
1444       (ret = prepared_statement_run (update_topology,
1445                                      NULL,
1446                                      MYSQL_TYPE_LONG,
1447                                      &connections,
1448                                      GNUNET_YES,
1449                                      MYSQL_TYPE_LONGLONG,
1450                                      &topologyuid, GNUNET_YES, -1)))
1451     {
1452       if (ret == GNUNET_SYSERR)
1453         {
1454           return GNUNET_SYSERR;
1455         }
1456     }
1457   if (ret > 0)
1458     return GNUNET_OK;
1459   else
1460     return GNUNET_SYSERR;
1461   return GNUNET_OK;
1462 }
1463
1464 /*
1465  * Records the current topology (number of connections, time, trial)
1466  *
1467  * @param num_connections how many connections are in the topology
1468  *
1469  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1470  */
1471 int
1472 add_topology (int num_connections)
1473 {
1474   int ret;
1475
1476   if (GNUNET_OK !=
1477       (ret = prepared_statement_run (insert_topology,
1478                                      NULL,
1479                                      MYSQL_TYPE_LONGLONG, &current_trial, GNUNET_YES,
1480                                      MYSQL_TYPE_LONG, &num_connections, GNUNET_YES,
1481                                      -1)))
1482     {
1483       if (ret == GNUNET_SYSERR)
1484         {
1485           return GNUNET_SYSERR;
1486         }
1487     }
1488   if (ret > 0)
1489     return GNUNET_OK;
1490   else
1491     return GNUNET_SYSERR;
1492   return GNUNET_OK;
1493 }
1494
1495 /*
1496  * Records a connection between two peers in the current topology
1497  *
1498  * @param first one side of the connection
1499  * @param second other side of the connection
1500  *
1501  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1502  */
1503 int
1504 add_extended_topology (const struct GNUNET_PeerIdentity *first, const struct GNUNET_PeerIdentity *second)
1505 {
1506   int ret;
1507   unsigned long long first_uid;
1508   unsigned long long second_uid;
1509   unsigned long long topologyuid;
1510
1511   if (GNUNET_OK != get_current_topology(&topologyuid))
1512     return GNUNET_SYSERR;
1513   if (GNUNET_OK != get_node_uid(&first_uid, &first->hashPubKey))
1514     return GNUNET_SYSERR;
1515   if (GNUNET_OK != get_node_uid(&second_uid, &second->hashPubKey))
1516     return GNUNET_SYSERR;
1517
1518   if (GNUNET_OK !=
1519       (ret = prepared_statement_run (extend_topology,
1520                                      NULL,
1521                                      MYSQL_TYPE_LONGLONG,
1522                                      &topologyuid,
1523                                      GNUNET_YES,
1524                                      MYSQL_TYPE_LONGLONG,
1525                                      &first_uid,
1526                                      GNUNET_YES,
1527                                      MYSQL_TYPE_LONGLONG,
1528                                      &second_uid,
1529                                      GNUNET_YES,-1)))
1530     {
1531       if (ret == GNUNET_SYSERR)
1532         {
1533           return GNUNET_SYSERR;
1534         }
1535     }
1536   if (ret > 0)
1537     return GNUNET_OK;
1538   else
1539     return GNUNET_SYSERR;
1540   return GNUNET_OK;
1541 }
1542
1543
1544 /*
1545  * Provides the dhtlog api
1546  *
1547  * @param c the configuration to use to connect to a server
1548  *
1549  * @return the handle to the server, or NULL on error
1550  */
1551 void *
1552 libgnunet_plugin_dhtlog_mysql_init (void * cls)
1553 {
1554   struct GNUNET_DHTLOG_Plugin *plugin = cls;
1555
1556   cfg = plugin->cfg;
1557   max_varchar_len = 255;
1558 #if DEBUG_DHTLOG
1559   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MySQL DHT Logger: initializing database\n");
1560 #endif
1561
1562   if (iopen (plugin) != GNUNET_OK)
1563     {
1564       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1565                   _("Failed to initialize MySQL database connection for dhtlog.\n"));
1566       return NULL;
1567     }
1568
1569   GNUNET_assert(plugin->dhtlog_api == NULL);
1570   plugin->dhtlog_api = GNUNET_malloc(sizeof(struct GNUNET_DHTLOG_Handle));
1571   plugin->dhtlog_api->insert_trial = &add_trial;
1572   plugin->dhtlog_api->insert_stat = &add_stat;
1573   plugin->dhtlog_api->insert_round = &add_round;
1574   plugin->dhtlog_api->add_generic_stat = &add_generic_stat;
1575   plugin->dhtlog_api->insert_query = &add_query;
1576   plugin->dhtlog_api->update_trial = &update_trials;
1577   plugin->dhtlog_api->insert_route = &add_route;
1578   plugin->dhtlog_api->insert_node = &add_node;
1579   plugin->dhtlog_api->insert_dhtkey = &add_dhtkey;
1580   plugin->dhtlog_api->update_connections = &add_connections;
1581   plugin->dhtlog_api->insert_topology = &add_topology;
1582   plugin->dhtlog_api->update_topology = &update_current_topology;
1583   plugin->dhtlog_api->insert_extended_topology = &add_extended_topology;
1584   plugin->dhtlog_api->set_malicious = &set_malicious;
1585   plugin->dhtlog_api->add_generic_stat = &add_generic_stat;
1586   get_current_trial (&current_trial);
1587
1588   return plugin;
1589 }
1590
1591 /**
1592  * Shutdown the plugin.
1593  */
1594 void *
1595 libgnunet_plugin_dhtlog_mysql_done (void * cls)
1596 {
1597   struct GNUNET_DHTLOG_Handle *dhtlog_api = cls;
1598
1599   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1600               "MySQL DHT Logger: database shutdown\n");
1601   GNUNET_assert(dhtlog_api != NULL);
1602   prepared_statement_close(insert_query);
1603   prepared_statement_close(insert_route);
1604   prepared_statement_close(insert_trial);
1605   prepared_statement_close(insert_round);
1606   prepared_statement_close(insert_node);
1607   prepared_statement_close(insert_dhtkey);
1608   prepared_statement_close(update_trial);
1609   prepared_statement_close(get_dhtkeyuid);
1610   prepared_statement_close(get_nodeuid);
1611   prepared_statement_close(update_connection);
1612   prepared_statement_close(get_trial);
1613   prepared_statement_close(get_topology);
1614   prepared_statement_close(insert_topology);
1615   prepared_statement_close(update_topology);
1616   prepared_statement_close(extend_topology);
1617   prepared_statement_close(update_node_malicious);
1618
1619   if (conn != NULL)
1620     mysql_close (conn);
1621   conn = NULL;
1622   mysql_library_end();
1623   GNUNET_free(dhtlog_api);
1624   return NULL;
1625 }
1626
1627 /* end of plugin_dhtlog_mysql.c */