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