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