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