minor blurb change
[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 static char *user;
90
91 static char *password;
92
93 static char *server;
94
95 static char *database;
96
97 static unsigned long long port;
98
99 /**
100  * Connection to the MySQL Server.
101  */
102 static MYSQL *conn;
103
104 #define INSERT_QUERIES_STMT "INSERT INTO queries (trialuid, querytype, hops, dhtkeyuid, dhtqueryid, succeeded, nodeuid) "\
105                           "VALUES (?, ?, ?, ?, ?, ?, ?)"
106 static struct StatementHandle *insert_query;
107
108 #define INSERT_ROUTES_STMT "INSERT INTO routes (trialuid, querytype, hops, dhtkeyuid, dhtqueryid, succeeded, nodeuid, from_node, to_node) "\
109                           "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
110 static struct StatementHandle *insert_route;
111
112 #define INSERT_NODES_STMT "INSERT INTO nodes (trialuid, nodeid, nodebits) "\
113                           "VALUES (?, ?, ?)"
114 static struct StatementHandle *insert_node;
115
116 #define INSERT_TRIALS_STMT "INSERT INTO trials"\
117                             "(starttime, 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, message) "\
123                             "VALUES (NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
124
125 static struct StatementHandle *insert_trial;
126
127 #define INSERT_DHTKEY_STMT "INSERT INTO dhtkeys (dhtkey, trialuid, keybits) "\
128                           "VALUES (?, ?, ?)"
129 static struct StatementHandle *insert_dhtkey;
130
131 #define UPDATE_TRIALS_STMT "UPDATE trials set endtime=NOW(), total_messages_dropped = ?, total_bytes_dropped = ?, unknownPeers = ? where trialuid = ?"
132 static struct StatementHandle *update_trial;
133
134 #define UPDATE_CONNECTIONS_STMT "UPDATE trials set totalConnections = ? where trialuid = ?"
135 static struct StatementHandle *update_connection;
136
137 #define GET_TRIAL_STMT "SELECT MAX( trialuid ) FROM trials"
138 static struct StatementHandle *get_trial;
139
140 #define GET_DHTKEYUID_STMT "SELECT dhtkeyuid FROM dhtkeys where dhtkey = ? and trialuid = ?"
141 static struct StatementHandle *get_dhtkeyuid;
142
143 #define GET_NODEUID_STMT "SELECT nodeuid FROM nodes where trialuid = ? and nodeid = ?"
144 static struct StatementHandle *get_nodeuid;
145
146 /**
147  * Run a query (not a select statement)
148  *
149  * @return GNUNET_OK if executed, GNUNET_SYSERR if an error occurred
150  */
151 int
152 run_statement (const char *statement)
153 {
154   mysql_query (conn, statement);
155   if (mysql_error (conn)[0])
156     {
157       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
158                  "mysql_query");
159       return GNUNET_SYSERR;
160     }
161   return GNUNET_OK;
162 }
163
164 /*
165  * Creates tables if they don't already exist for dht logging
166  */
167 static int
168 itable ()
169 {
170 #define MRUNS(a) (GNUNET_OK != run_statement (a) )
171
172   if (MRUNS ("CREATE TABLE IF NOT EXISTS `dhtkeys` ("
173              "dhtkeyuid int(10) unsigned NOT NULL auto_increment COMMENT 'Unique Key given to each query',"
174              "`dhtkey` varchar(255) NOT NULL COMMENT 'The ASCII value of the key being searched for',"
175              "trialuid int(10) unsigned NOT NULL,"
176              "keybits blob NOT NULL,"
177              "UNIQUE KEY `dhtkeyuid` (`dhtkeyuid`)"
178              ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
179     return GNUNET_SYSERR;
180
181   if (MRUNS ("CREATE TABLE IF NOT EXISTS `nodes` ("
182              "`nodeuid` int(10) unsigned NOT NULL auto_increment,"
183              "`trialuid` int(10) unsigned NOT NULL,"
184              "`nodeid` varchar(255) NOT NULL,"
185              "`nodebits` blob NOT NULL,"
186              "PRIMARY KEY  (`nodeuid`)"
187              ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
188     return GNUNET_SYSERR;
189
190   if (MRUNS ("CREATE TABLE IF NOT EXISTS `queries` ("
191              "`trialuid` int(10) unsigned NOT NULL,"
192              "`queryuid` int(10) unsigned NOT NULL auto_increment,"
193              "`dhtqueryid` bigint(20) NOT NULL,"
194              "`querytype` enum('1','2','3','4','5') NOT NULL,"
195              "`hops` int(10) unsigned NOT NULL,"
196              "`succeeded` tinyint NOT NULL,"
197              "`nodeuid` int(10) unsigned NOT NULL,"
198              "`time` timestamp NOT NULL default CURRENT_TIMESTAMP,"
199              "`dhtkeyuid` int(10) unsigned NOT NULL,"
200              "PRIMARY KEY  (`queryuid`)"
201              ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
202     return GNUNET_SYSERR;
203
204   if (MRUNS ("CREATE TABLE IF NOT EXISTS `routes` ("
205              "`trialuid` int(10) unsigned NOT NULL,"
206              "`queryuid` int(10) unsigned NOT NULL auto_increment,"
207              "`dhtqueryid` bigint(20) NOT NULL,"
208              "`querytype` enum('1','2','3','4','5') NOT NULL,"
209              "`hops` int(10) unsigned NOT NULL,"
210              "`succeeded` tinyint NOT NULL,"
211              "`nodeuid` int(10) unsigned NOT NULL,"
212              "`time` timestamp NOT NULL default CURRENT_TIMESTAMP,"
213              "`dhtkeyuid` int(10) unsigned NOT NULL,"
214              "`from_node` int(10) unsigned NOT NULL,"
215              "`to_node` int(10) unsigned NOT NULL,"
216              "PRIMARY KEY  (`queryuid`)"
217              ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
218     return GNUNET_SYSERR;
219
220   if (MRUNS ("CREATE TABLE IF NOT EXISTS `trials` ("
221              "`trialuid` int(10) unsigned NOT NULL auto_increment,"
222              "`numnodes` int(10) unsigned NOT NULL,"
223              "`topology` int(10) NOT NULL,"
224              "`starttime` datetime NOT NULL,"
225              "`endtime` datetime NOT NULL,"
226              "`puts` int(10) unsigned NOT NULL,"
227              "`gets` int(10) unsigned NOT NULL,"
228              "`concurrent` int(10) unsigned NOT NULL,"
229              "`settle_time` int(10) unsigned NOT NULL,"
230              "`totalConnections` int(10) unsigned NOT NULL,"
231              "`message` text NOT NULL,"
232              "`num_rounds` int(10) unsigned NOT NULL,"
233              "`malicious_getters` int(10) unsigned NOT NULL,"
234              "`malicious_putters` int(10) unsigned NOT NULL,"
235              "`malicious_droppers` int(10) unsigned NOT NULL,"
236              "`totalMessagesDropped` int(10) unsigned NOT NULL,"
237              "`totalBytesDropped` int(10) unsigned NOT NULL,"
238              "`topology_modifier` double NOT NULL,"
239              "`logNMultiplier` double NOT NULL,"
240              "`maxnetbps` bigint(20) unsigned NOT NULL,"
241              "`unknownPeers` int(10) unsigned NOT NULL,"
242              "PRIMARY KEY  (`trialuid`),"
243              "UNIQUE KEY `trialuid` (`trialuid`)"
244              ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
245     return GNUNET_SYSERR;
246
247   if (MRUNS ("SET AUTOCOMMIT = 1"))
248     return GNUNET_SYSERR;
249
250   return GNUNET_OK;
251 #undef MRUNS
252 }
253
254 /**
255  * Create a prepared statement.
256  *
257  * @return NULL on error
258  */
259 struct StatementHandle *
260 prepared_statement_create (const char *statement)
261 {
262   struct StatementHandle *ret;
263
264   ret = GNUNET_malloc (sizeof (struct StatementHandle));
265   ret->query = GNUNET_strdup (statement);
266   return ret;
267 }
268
269 /**
270  * Create a prepared statement.
271  *
272  * @return NULL on error
273  */
274 void
275 prepared_statement_close (struct StatementHandle *s)
276 {
277   if (s == NULL)
278     return;
279
280   if (s->query != NULL)
281     GNUNET_free(s->query);
282   if (s->valid == GNUNET_YES)
283     mysql_stmt_close(s->statement);
284   GNUNET_free(s);
285 }
286
287 /*
288  * Initialize the prepared statements for use with dht test logging
289  */
290 static int
291 iopen ()
292 {
293   int ret;
294
295   conn = mysql_init (NULL);
296   if (conn == NULL)
297     return GNUNET_SYSERR;
298
299   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to mysql with: user %s, pass %s, server %s, database %s, port %d\n",
300               user, password, server, database, port);
301
302   mysql_real_connect (conn, server, user, password,
303                       database, (unsigned int) port, NULL, 0);
304
305   if (mysql_error (conn)[0])
306     {
307       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
308                  "mysql_real_connect");
309       return GNUNET_SYSERR;
310     }
311
312 #if OLD
313   db = GNUNET_MYSQL_database_open (coreAPI->ectx, coreAPI->cfg);
314   if (db == NULL)
315     return GNUNET_SYSERR;
316 #endif
317
318   ret = itable ();
319
320 #define PINIT(a,b) (NULL == (a = prepared_statement_create(b)))
321   if (PINIT (insert_query, INSERT_QUERIES_STMT) ||
322       PINIT (insert_route, INSERT_ROUTES_STMT) ||
323       PINIT (insert_trial, INSERT_TRIALS_STMT) ||
324       PINIT (insert_node, INSERT_NODES_STMT) ||
325       PINIT (insert_dhtkey, INSERT_DHTKEY_STMT) ||
326       PINIT (update_trial, UPDATE_TRIALS_STMT) ||
327       PINIT (get_dhtkeyuid, GET_DHTKEYUID_STMT) ||
328       PINIT (get_nodeuid, GET_NODEUID_STMT) ||
329       PINIT (update_connection, UPDATE_CONNECTIONS_STMT) ||
330       PINIT (get_trial, GET_TRIAL_STMT))
331     {
332       return GNUNET_SYSERR;
333     }
334 #undef PINIT
335
336   return ret;
337 }
338
339 static int
340 return_ok (void *cls, unsigned int num_values, MYSQL_BIND * values)
341 {
342   return GNUNET_OK;
343 }
344
345 /**
346  * Prepare a statement for running.
347  *
348  * @return GNUNET_OK on success
349  */
350 static int
351 prepare_statement (struct StatementHandle *ret)
352 {
353   if (GNUNET_YES == ret->valid)
354     return GNUNET_OK;
355
356   ret->statement = mysql_stmt_init (conn);
357   if (ret->statement == NULL)
358     return GNUNET_SYSERR;
359
360   if (mysql_stmt_prepare (ret->statement, ret->query, strlen (ret->query)))
361     {
362       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
363                  "mysql_stmt_prepare `%s', %s", ret->query, mysql_error(conn));
364       mysql_stmt_close (ret->statement);
365       ret->statement = NULL;
366       return GNUNET_SYSERR;
367     }
368   ret->valid = GNUNET_YES;
369   return GNUNET_OK;
370 }
371
372 /**
373  * Bind the parameters for the given MySQL statement
374  * and run it.
375  *
376  * @param s statement to bind and run
377  * @param ap arguments for the binding
378  * @return GNUNET_SYSERR on error, GNUNET_OK on success
379  */
380 static int
381 init_params (struct StatementHandle *s, va_list ap)
382 {
383   MYSQL_BIND qbind[MAX_PARAM];
384   unsigned int pc;
385   unsigned int off;
386   enum enum_field_types ft;
387
388   pc = mysql_stmt_param_count (s->statement);
389   if (pc > MAX_PARAM)
390     {
391       /* increase internal constant! */
392       GNUNET_break (0);
393       return GNUNET_SYSERR;
394     }
395   memset (qbind, 0, sizeof (qbind));
396   off = 0;
397   ft = 0;
398   while ((pc > 0) && (-1 != (ft = va_arg (ap, enum enum_field_types))))
399     {
400       qbind[off].buffer_type = ft;
401       switch (ft)
402         {
403         case MYSQL_TYPE_FLOAT:
404           qbind[off].buffer = va_arg (ap, float *);
405           break;
406         case MYSQL_TYPE_LONGLONG:
407           qbind[off].buffer = va_arg (ap, unsigned long long *);
408           qbind[off].is_unsigned = va_arg (ap, int);
409           break;
410         case MYSQL_TYPE_LONG:
411           qbind[off].buffer = va_arg (ap, unsigned int *);
412           qbind[off].is_unsigned = va_arg (ap, int);
413           break;
414         case MYSQL_TYPE_VAR_STRING:
415         case MYSQL_TYPE_STRING:
416         case MYSQL_TYPE_BLOB:
417           qbind[off].buffer = va_arg (ap, void *);
418           qbind[off].buffer_length = va_arg (ap, unsigned long);
419           qbind[off].length = va_arg (ap, unsigned long *);
420           break;
421         default:
422           /* unsupported type */
423           GNUNET_break (0);
424           return GNUNET_SYSERR;
425         }
426       pc--;
427       off++;
428     }
429   if (!((pc == 0) && (ft != -1) && (va_arg (ap, int) == -1)))
430     {
431       GNUNET_break (0);
432       return GNUNET_SYSERR;
433     }
434   if (mysql_stmt_bind_param (s->statement, qbind))
435     {
436       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
437                   _("`%s' failed at %s:%d with error: %s\n"),
438                   "mysql_stmt_bind_param",
439                    __FILE__, __LINE__, mysql_stmt_error (s->statement));
440       return GNUNET_SYSERR;
441     }
442
443   if (mysql_stmt_execute (s->statement))
444     {
445     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
446                _("`%s' failed at %s:%d with error: %s\n"),
447                "mysql_stmt_execute",
448                __FILE__, __LINE__, mysql_stmt_error (s->statement));
449       return GNUNET_SYSERR;
450     }
451
452   return GNUNET_OK;
453 }
454
455 /**
456  * Run a prepared SELECT statement.
457  *
458  * @param result_size number of elements in results array
459  * @param results pointer to already initialized MYSQL_BIND
460  *        array (of sufficient size) for passing results
461  * @param processor function to call on each result
462  * @param processor_cls extra argument to processor
463  * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
464  *        values (size + buffer-reference for pointers); terminated
465  *        with "-1"
466  * @return GNUNET_SYSERR on error, otherwise
467  *         the number of successfully affected (or queried) rows
468  */
469 int
470 prepared_statement_run_select (struct StatementHandle
471                                *s, unsigned int result_size,
472                                MYSQL_BIND * results,
473                                GNUNET_MysqlDataProcessor
474                                processor, void *processor_cls,
475                                ...)
476 {
477   va_list ap;
478   int ret;
479   unsigned int rsize;
480   int total;
481
482   if (GNUNET_OK != prepare_statement (s))
483     {
484       GNUNET_break (0);
485       return GNUNET_SYSERR;
486     }
487   va_start (ap, processor_cls);
488   if (GNUNET_OK != init_params (s, ap))
489     {
490       GNUNET_break (0);
491       va_end (ap);
492       return GNUNET_SYSERR;
493     }
494   va_end (ap);
495   rsize = mysql_stmt_field_count (s->statement);
496   if (rsize > result_size)
497     {
498       GNUNET_break (0);
499       return GNUNET_SYSERR;
500     }
501   if (mysql_stmt_bind_result (s->statement, results))
502     {
503       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
504                   _("`%s' failed at %s:%d with error: %s\n"),
505                   "mysql_stmt_bind_result",
506                   __FILE__, __LINE__, mysql_stmt_error (s->statement));
507       return GNUNET_SYSERR;
508     }
509
510   total = 0;
511   while (1)
512     {
513       ret = mysql_stmt_fetch (s->statement);
514       if (ret == MYSQL_NO_DATA)
515         break;
516       if (ret != 0)
517         {
518           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
519                        _("`%s' failed at %s:%d with error: %s\n"),
520                        "mysql_stmt_fetch",
521                        __FILE__, __LINE__, mysql_stmt_error (s->statement));
522           return GNUNET_SYSERR;
523         }
524       if (processor != NULL)
525         if (GNUNET_OK != processor (processor_cls, rsize, results))
526           break;
527       total++;
528     }
529   mysql_stmt_reset (s->statement);
530   return total;
531 }
532
533 static int
534 get_current_trial (unsigned long long *trialuid)
535 {
536   MYSQL_BIND rbind[1];
537
538   memset (rbind, 0, sizeof (rbind));
539   rbind[0].buffer_type = MYSQL_TYPE_LONG;
540   rbind[0].is_unsigned = 1;
541   rbind[0].buffer = trialuid;
542
543   if ((GNUNET_OK !=
544        prepared_statement_run_select (get_trial,
545                                       1,
546                                       rbind,
547                                       return_ok, NULL, -1)))
548     {
549       return GNUNET_SYSERR;
550     }
551
552   return GNUNET_OK;
553 }
554
555
556 /**
557  * Run a prepared statement that does NOT produce results.
558  *
559  * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
560  *        values (size + buffer-reference for pointers); terminated
561  *        with "-1"
562  * @param insert_id NULL or address where to store the row ID of whatever
563  *        was inserted (only for INSERT statements!)
564  * @return GNUNET_SYSERR on error, otherwise
565  *         the number of successfully affected rows
566  */
567 int
568 prepared_statement_run (struct StatementHandle *s,
569                         unsigned long long *insert_id, ...)
570 {
571   va_list ap;
572   int affected;
573
574   if (GNUNET_OK != prepare_statement(s))
575     {
576       GNUNET_break(0);
577       return GNUNET_SYSERR;
578     }
579   GNUNET_assert(s->valid == GNUNET_YES);
580   if (s->statement == NULL)
581     return GNUNET_SYSERR;
582
583   va_start (ap, insert_id);
584
585   if (mysql_stmt_prepare (s->statement, s->query, strlen (s->query)))
586       {
587         GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "mysql_stmt_prepare ERROR");
588         return GNUNET_SYSERR;
589       }
590
591   if (GNUNET_OK != init_params (s, ap))
592     {
593       va_end (ap);
594       return GNUNET_SYSERR;
595     }
596
597   va_end (ap);
598   affected = mysql_stmt_affected_rows (s->statement);
599   if (NULL != insert_id)
600     *insert_id = (unsigned long long) mysql_stmt_insert_id (s->statement);
601   mysql_stmt_reset (s->statement);
602
603   return affected;
604 }
605
606 /*
607  * Inserts the specified trial into the dhttests.trials table
608  *
609  * @param trialuid return the trialuid of the newly inserted trial
610  * @param num_nodes how many nodes are in the trial
611  * @param topology integer representing topology for this trial
612  * @param blacklist_topology integer representing blacklist topology for this trial
613  * @param connect_topology integer representing connect topology for this trial
614  * @param connect_topology_option integer representing connect topology option
615  * @param connect_topology_option_modifier float to modify connect option
616  * @param topology_percentage percentage modifier for certain topologies
617  * @param topology_probability probability modifier for certain topologies
618  * @param puts number of puts to perform
619  * @param gets number of gets to perform
620  * @param concurrent number of concurrent requests
621  * @param settle_time time to wait between creating topology and starting testing
622  * @param num_rounds number of times to repeat the trial
623  * @param malicious_getters number of malicious GET peers in the trial
624  * @param malicious_putters number of malicious PUT peers in the trial
625  * @param malicious_droppers number of malicious DROP peers in the trial
626  * @param message string to put into DB for this trial
627  *
628  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
629  */
630 int
631 add_trial (unsigned long long *trialuid, int num_nodes, int topology,
632            int blacklist_topology, int connect_topology,
633            int connect_topology_option, float connect_topology_option_modifier,
634            float topology_percentage, float topology_probability,
635            int puts, int gets, int concurrent, int settle_time,
636            int num_rounds, int malicious_getters, int malicious_putters,
637            int malicious_droppers, char *message)
638 {
639   MYSQL_STMT *stmt;
640   int ret;
641   unsigned long long m_len;
642   m_len = strlen (message);
643
644   stmt = mysql_stmt_init(conn);
645   if (GNUNET_OK !=
646       (ret = prepared_statement_run (insert_trial,
647                                       trialuid,
648                                       MYSQL_TYPE_LONG,
649                                       &num_nodes,
650                                       GNUNET_YES,
651                                       MYSQL_TYPE_LONG,
652                                       &topology,
653                                       GNUNET_YES,
654                                       MYSQL_TYPE_FLOAT,
655                                       &topology_percentage,
656                                       MYSQL_TYPE_FLOAT,
657                                       &topology_probability,
658                                       MYSQL_TYPE_LONG,
659                                       &blacklist_topology,
660                                       GNUNET_YES,
661                                       MYSQL_TYPE_LONG,
662                                       &connect_topology,
663                                       GNUNET_YES,
664                                       MYSQL_TYPE_LONG,
665                                       &connect_topology_option,
666                                       GNUNET_YES,
667                                       MYSQL_TYPE_FLOAT,
668                                       &connect_topology_option_modifier,
669                                       MYSQL_TYPE_LONG,
670                                       &puts,
671                                       GNUNET_YES,
672                                       MYSQL_TYPE_LONG,
673                                       &gets,
674                                       GNUNET_YES,
675                                       MYSQL_TYPE_LONG,
676                                       &concurrent,
677                                       GNUNET_YES,
678                                       MYSQL_TYPE_LONG,
679                                       &settle_time,
680                                       GNUNET_YES,
681                                       MYSQL_TYPE_LONG,
682                                       &num_rounds,
683                                       GNUNET_YES,
684                                       MYSQL_TYPE_LONG,
685                                       &malicious_getters,
686                                       GNUNET_YES,
687                                       MYSQL_TYPE_LONG,
688                                       &malicious_putters,
689                                       GNUNET_YES,
690                                       MYSQL_TYPE_LONG,
691                                       &malicious_droppers,
692                                       GNUNET_YES,
693                                       MYSQL_TYPE_BLOB,
694                                       message,
695                                       max_varchar_len +
696                                       max_varchar_len, &m_len,
697                                       -1)))
698     {
699       if (ret == GNUNET_SYSERR)
700         {
701           mysql_stmt_close(stmt);
702           return GNUNET_SYSERR;
703         }
704     }
705
706   get_current_trial (&current_trial);
707 #if DEBUG_DHTLOG
708   fprintf (stderr, "Current trial is %llu\n", current_trial);
709 #endif
710   mysql_stmt_close(stmt);
711   return GNUNET_OK;
712 }
713
714 static int
715 get_dhtkey_uid (unsigned long long *dhtkeyuid, const GNUNET_HashCode * key)
716 {
717   MYSQL_BIND rbind[1];
718   struct GNUNET_CRYPTO_HashAsciiEncoded encKey;
719   unsigned long long k_len;
720   memset (rbind, 0, sizeof (rbind));
721   rbind[0].buffer_type = MYSQL_TYPE_LONG;
722   rbind[0].is_unsigned = 1;
723   rbind[0].buffer = dhtkeyuid;
724   GNUNET_CRYPTO_hash_to_enc (key, &encKey);
725   k_len = strlen ((char *) &encKey);
726
727   if ((GNUNET_OK !=
728        prepared_statement_run_select (get_dhtkeyuid,
729                                       1,
730                                       rbind,
731                                       return_ok, NULL,
732                                       MYSQL_TYPE_VAR_STRING,
733                                       &encKey,
734                                       max_varchar_len,
735                                       &k_len,
736                                       MYSQL_TYPE_LONGLONG,
737                                       &current_trial,
738                                       GNUNET_YES, -1)))
739     {
740       return GNUNET_SYSERR;
741     }
742
743   return GNUNET_OK;
744 }
745
746 /*
747  * Inserts the specified dhtkey into the dhttests.dhtkeys table,
748  * stores return value of dhttests.dhtkeys.dhtkeyuid into dhtkeyuid
749  *
750  * @param dhtkeyuid return value
751  * @param dhtkey hashcode of key to insert
752  *
753  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
754  */
755 int
756 add_dhtkey (unsigned long long *dhtkeyuid, const GNUNET_HashCode * dhtkey)
757 {
758
759   int ret;
760   struct GNUNET_CRYPTO_HashAsciiEncoded encKey;
761   unsigned long long k_len;
762   unsigned long long h_len;
763   unsigned long long curr_dhtkeyuid;
764   GNUNET_CRYPTO_hash_to_enc (dhtkey, &encKey);
765   k_len = strlen ((char *) &encKey);
766   h_len = sizeof (GNUNET_HashCode);
767   curr_dhtkeyuid = 0;
768   ret = get_dhtkey_uid(&curr_dhtkeyuid, dhtkey);
769   if (curr_dhtkeyuid != 0) /* dhtkey already exists */
770     {
771       if (dhtkeyuid != NULL)
772         *dhtkeyuid = curr_dhtkeyuid;
773       return GNUNET_OK;
774     }
775
776   if (GNUNET_OK !=
777       (ret = prepared_statement_run (insert_dhtkey,
778                                      dhtkeyuid,
779                                      MYSQL_TYPE_VAR_STRING,
780                                      &encKey,
781                                      max_varchar_len,
782                                      &k_len,
783                                      MYSQL_TYPE_LONG,
784                                      &current_trial,
785                                      GNUNET_YES,
786                                      MYSQL_TYPE_BLOB,
787                                      dhtkey,
788                                      sizeof (GNUNET_HashCode),
789                                      &h_len, -1)))
790     {
791       if (ret == GNUNET_SYSERR)
792         {
793           return GNUNET_SYSERR;
794         }
795     }
796
797   return GNUNET_OK;
798 }
799
800
801 static int
802 get_node_uid (unsigned long long *nodeuid, const GNUNET_HashCode * peerHash)
803 {
804   MYSQL_BIND rbind[1];
805   struct GNUNET_CRYPTO_HashAsciiEncoded encPeer;
806   unsigned long long p_len;
807
808   int ret;
809   memset (rbind, 0, sizeof (rbind));
810   rbind[0].buffer_type = MYSQL_TYPE_LONG;
811   rbind[0].buffer = nodeuid;
812   rbind[0].is_unsigned = GNUNET_YES;
813
814   GNUNET_CRYPTO_hash_to_enc (peerHash, &encPeer);
815   p_len = strlen ((char *) &encPeer);
816
817   if (1 != (ret = prepared_statement_run_select (get_nodeuid,
818                                                               1,
819                                                               rbind,
820                                                               return_ok,
821                                                               NULL,
822                                                               MYSQL_TYPE_LONG,
823                                                               &current_trial,
824                                                               GNUNET_YES,
825                                                               MYSQL_TYPE_VAR_STRING,
826                                                               &encPeer,
827                                                               max_varchar_len,
828                                                               &p_len, -1)))
829     {
830 #if DEBUG_DHTLOG
831       fprintf (stderr, "FAILED\n");
832 #endif
833       return GNUNET_SYSERR;
834     }
835   return GNUNET_OK;
836 }
837
838
839 /*
840  * Inserts the specified node into the dhttests.nodes table
841  *
842  * @param nodeuid the inserted node uid
843  * @param node the node to insert
844  *
845  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
846  */
847 int
848 add_node (unsigned long long *nodeuid, struct GNUNET_PeerIdentity * node)
849 {
850   struct GNUNET_CRYPTO_HashAsciiEncoded encPeer;
851   unsigned long p_len;
852   unsigned long h_len;
853   int ret;
854
855   if (node == NULL)
856     return GNUNET_SYSERR;
857
858   GNUNET_CRYPTO_hash_to_enc (&node->hashPubKey, &encPeer);
859   p_len = (unsigned long) strlen ((char *) &encPeer);
860   h_len = sizeof (GNUNET_HashCode);
861   if (GNUNET_OK !=
862       (ret = prepared_statement_run (insert_node,
863                                                   nodeuid,
864                                                   MYSQL_TYPE_LONGLONG,
865                                                   &current_trial,
866                                                   GNUNET_YES,
867                                                   MYSQL_TYPE_VAR_STRING,
868                                                   &encPeer,
869                                                   max_varchar_len,
870                                                   &p_len,
871                                                   MYSQL_TYPE_BLOB,
872                                                   &node->hashPubKey,
873                                                   sizeof (GNUNET_HashCode),
874                                                   &h_len, -1)))
875     {
876       if (ret == GNUNET_SYSERR)
877         {
878           return GNUNET_SYSERR;
879         }
880     }
881   return GNUNET_OK;
882 }
883
884 /*
885  * Update dhttests.trials table with current server time as end time
886  *
887  * @param trialuid trial to update
888  * @param totalMessagesDropped stats value for messages dropped
889  * @param totalBytesDropped stats value for total bytes dropped
890  * @param unknownPeers stats value for unknown peers
891  *
892  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
893  */
894 int
895 update_trials (unsigned long long trialuid,
896                unsigned long long totalMessagesDropped,
897                unsigned long long totalBytesDropped,
898                unsigned long long unknownPeers)
899 {
900   int ret;
901 #if DEBUG_DHTLOG
902   if (trialuid != current_trial)
903     {
904       fprintf (stderr,
905                _("Trialuid to update is not equal to current_trial\n"));
906     }
907 #endif
908   if (GNUNET_OK !=
909       (ret = prepared_statement_run (update_trial,
910                                     NULL,
911                                     MYSQL_TYPE_LONGLONG,
912                                     &totalMessagesDropped,
913                                     GNUNET_YES,
914                                     MYSQL_TYPE_LONGLONG,
915                                     &totalBytesDropped,
916                                     GNUNET_YES,
917                                     MYSQL_TYPE_LONGLONG,
918                                     &unknownPeers,
919                                     GNUNET_YES,
920                                     MYSQL_TYPE_LONGLONG,
921                                     &trialuid, GNUNET_YES, -1)))
922     {
923       if (ret == GNUNET_SYSERR)
924         {
925           return GNUNET_SYSERR;
926         }
927     }
928   if (ret > 0)
929     return GNUNET_OK;
930   else
931     return GNUNET_SYSERR;
932 }
933
934
935 /*
936  * Update dhttests.trials table with total connections information
937  *
938  * @param trialuid the trialuid to update
939  * @param totalConnections the number of connections
940  *
941  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
942  */
943 int
944 add_connections (unsigned long long trialuid, unsigned int totalConnections)
945 {
946   int ret;
947 #if DEBUG_DHTLOG
948   if (trialuid != current_trial)
949     {
950       fprintf (stderr,
951                _("Trialuid to update is not equal to current_trial(!)(?)\n"));
952     }
953 #endif
954   if (GNUNET_OK !=
955       (ret = prepared_statement_run (update_connection,
956                                                   NULL,
957                                                   MYSQL_TYPE_LONG,
958                                                   &totalConnections,
959                                                   GNUNET_YES,
960                                                   MYSQL_TYPE_LONGLONG,
961                                                   &trialuid, GNUNET_YES, -1)))
962     {
963       if (ret == GNUNET_SYSERR)
964         {
965           return GNUNET_SYSERR;
966         }
967     }
968   if (ret > 0)
969     return GNUNET_OK;
970   else
971     return GNUNET_SYSERR;
972 }
973
974 /*
975  * Inserts the specified query into the dhttests.queries table
976  *
977  * @param sqlqueruid inserted query uid
978  * @param queryid dht query id
979  * @param type type of the query
980  * @param hops number of hops query traveled
981  * @param succeeded whether or not query was successful
982  * @param node the node the query hit
983  * @param key the key of the query
984  *
985  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
986  */
987 int
988 add_query (unsigned long long *sqlqueryuid, unsigned long long queryid,
989            unsigned int type, unsigned int hops, int succeeded,
990            const struct GNUNET_PeerIdentity * node, const GNUNET_HashCode * key)
991 {
992   int ret;
993   unsigned long long peer_uid, key_uid;
994   peer_uid = 0;
995   key_uid = 0;
996
997   if ((node != NULL)
998       && (GNUNET_OK == get_node_uid (&peer_uid, &node->hashPubKey)))
999     {
1000
1001     }
1002   else
1003     {
1004       return GNUNET_SYSERR;
1005     }
1006
1007   if ((key != NULL) && (GNUNET_OK == get_dhtkey_uid (&key_uid, key)))
1008     {
1009
1010     }
1011   else if ((key != NULL) && (key->bits[(512 / 8 / sizeof (unsigned int)) - 1] == 42))   /* Malicious marker */
1012     {
1013       key_uid = 0;
1014     }
1015   else
1016     {
1017       return GNUNET_SYSERR;
1018     }
1019
1020   if (GNUNET_OK !=
1021       (ret = prepared_statement_run (insert_query,
1022                                                   sqlqueryuid,
1023                                                   MYSQL_TYPE_LONGLONG,
1024                                                   &current_trial,
1025                                                   GNUNET_YES,
1026                                                   MYSQL_TYPE_LONG,
1027                                                   &type,
1028                                                   GNUNET_NO,
1029                                                   MYSQL_TYPE_LONG,
1030                                                   &hops,
1031                                                   GNUNET_YES,
1032                                                   MYSQL_TYPE_LONGLONG,
1033                                                   &key_uid,
1034                                                   GNUNET_YES,
1035                                                   MYSQL_TYPE_LONGLONG,
1036                                                   &queryid,
1037                                                   GNUNET_YES,
1038                                                   MYSQL_TYPE_LONG,
1039                                                   &succeeded,
1040                                                   GNUNET_NO,
1041                                                   MYSQL_TYPE_LONGLONG,
1042                                                   &peer_uid, GNUNET_YES, -1)))
1043     {
1044       if (ret == GNUNET_SYSERR)
1045         {
1046           return GNUNET_SYSERR;
1047         }
1048     }
1049   if (ret > 0)
1050     return GNUNET_OK;
1051   else
1052     return GNUNET_SYSERR;
1053 }
1054
1055 /*
1056  * Inserts the specified route information into the dhttests.routes table
1057  *
1058  * @param sqlqueruid inserted query uid
1059  * @param queryid dht query id
1060  * @param type type of the query
1061  * @param hops number of hops query traveled
1062  * @param succeeded whether or not query was successful
1063  * @param node the node the query hit
1064  * @param key the key of the query
1065  * @param from_node the node that sent the message to node
1066  * @param to_node next node to forward message to
1067  *
1068  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1069  */
1070 int
1071 add_route (unsigned long long *sqlqueryuid, unsigned long long queryid,
1072            unsigned int type, unsigned int hops,
1073            int succeeded, const struct GNUNET_PeerIdentity * node,
1074            const GNUNET_HashCode * key, const struct GNUNET_PeerIdentity * from_node,
1075            const struct GNUNET_PeerIdentity * to_node)
1076 {
1077   unsigned long long peer_uid = 0;
1078   unsigned long long key_uid = 0;
1079   unsigned long long from_uid = 0;
1080   unsigned long long to_uid = 0;
1081   int ret;
1082
1083   if (from_node != NULL)
1084     get_node_uid (&from_uid, &from_node->hashPubKey);
1085   else
1086     from_uid = 0;
1087
1088   if (to_node != NULL)
1089     get_node_uid (&to_uid, &to_node->hashPubKey);
1090   else
1091     to_uid = 0;
1092
1093   if ((node != NULL))
1094     {
1095       if (1 != get_node_uid (&peer_uid, &node->hashPubKey))
1096         {
1097           return GNUNET_SYSERR;
1098         }
1099     }
1100   else
1101     return GNUNET_SYSERR;
1102
1103   if ((key != NULL))
1104     {
1105       if (1 != get_dhtkey_uid (&key_uid, key))
1106         {
1107           return GNUNET_SYSERR;
1108         }
1109     }
1110   else
1111     return GNUNET_SYSERR;
1112
1113   if (GNUNET_OK !=
1114       (ret = prepared_statement_run (insert_route,
1115                                     sqlqueryuid,
1116                                     MYSQL_TYPE_LONGLONG,
1117                                     &current_trial,
1118                                     GNUNET_YES,
1119                                     MYSQL_TYPE_LONG,
1120                                     &type,
1121                                     GNUNET_NO,
1122                                     MYSQL_TYPE_LONG,
1123                                     &hops,
1124                                     GNUNET_YES,
1125                                     MYSQL_TYPE_LONGLONG,
1126                                     &key_uid,
1127                                     GNUNET_YES,
1128                                     MYSQL_TYPE_LONGLONG,
1129                                     &queryid,
1130                                     GNUNET_YES,
1131                                     MYSQL_TYPE_LONG,
1132                                     &succeeded,
1133                                     GNUNET_NO,
1134                                     MYSQL_TYPE_LONGLONG,
1135                                     &peer_uid,
1136                                     GNUNET_YES,
1137                                     MYSQL_TYPE_LONGLONG,
1138                                     &from_uid,
1139                                     GNUNET_YES,
1140                                     MYSQL_TYPE_LONGLONG,
1141                                     &to_uid, GNUNET_YES, -1)))
1142     {
1143       if (ret == GNUNET_SYSERR)
1144         {
1145           return GNUNET_SYSERR;
1146         }
1147     }
1148   if (ret > 0)
1149     return GNUNET_OK;
1150   else
1151     return GNUNET_SYSERR;
1152 }
1153
1154 /*
1155  * Provides the dhtlog api
1156  *
1157  * @param c the configuration to use to connect to a server
1158  *
1159  * @return the handle to the server, or NULL on error
1160  */
1161 void *
1162 libgnunet_plugin_dhtlog_mysql_init (void * cls)
1163 {
1164   struct GNUNET_DHTLOG_Plugin *plugin = cls;
1165
1166   cfg = plugin->cfg;
1167   max_varchar_len = 255;
1168 #if DEBUG_DHTLOG
1169   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MySQL DHT Logger: initializing database\n");
1170 #endif
1171
1172   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (plugin->cfg,
1173                                                          "MYSQL", "DATABASE",
1174                                                          &database))
1175     {
1176       database = GNUNET_strdup("gnunet");
1177     }
1178
1179   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (plugin->cfg,
1180                                                           "MYSQL", "USER", &user))
1181     {
1182       user = GNUNET_strdup("dht");
1183     }
1184
1185   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (plugin->cfg,
1186                                                           "MYSQL", "PASSWORD", &password))
1187     {
1188       password = GNUNET_strdup("dhttest**");
1189     }
1190
1191   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (plugin->cfg,
1192                                                           "MYSQL", "SERVER", &server))
1193     {
1194       server = GNUNET_strdup("localhost");
1195     }
1196
1197   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (plugin->cfg,
1198                                                           "MYSQL", "MYSQL_PORT", &port))
1199     {
1200       port = 0;
1201     }
1202
1203   if (iopen () != GNUNET_OK)
1204     {
1205       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1206                   _("Failed to initialize MySQL database connection for dhtlog.\n"));
1207       return NULL;
1208     }
1209   GNUNET_assert(plugin->dhtlog_api == NULL);
1210   plugin->dhtlog_api = GNUNET_malloc(sizeof(struct GNUNET_DHTLOG_Handle));
1211   plugin->dhtlog_api->insert_trial = &add_trial;
1212   plugin->dhtlog_api->insert_query = &add_query;
1213   plugin->dhtlog_api->update_trial = &update_trials;
1214   plugin->dhtlog_api->insert_route = &add_route;
1215   plugin->dhtlog_api->insert_node = &add_node;
1216   plugin->dhtlog_api->insert_dhtkey = &add_dhtkey;
1217   plugin->dhtlog_api->update_connections = &add_connections;
1218   get_current_trial (&current_trial);
1219
1220   return NULL;
1221 }
1222
1223 /**
1224  * Shutdown the plugin.
1225  */
1226 void *
1227 libgnunet_plugin_dhtlog_mysql_done (void * cls)
1228 {
1229   struct GNUNET_DHTLOG_Handle *dhtlog_api = cls;
1230 #if DEBUG_DHTLOG
1231   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1232               "MySQL DHT Logger: database shutdown\n");
1233 #endif
1234   GNUNET_assert(dhtlog_api != NULL);
1235   prepared_statement_close(insert_query);
1236   prepared_statement_close(insert_route);
1237   prepared_statement_close(insert_trial);
1238   prepared_statement_close(insert_node);
1239   prepared_statement_close(insert_dhtkey);
1240   prepared_statement_close(update_trial);
1241   prepared_statement_close(get_dhtkeyuid);
1242   prepared_statement_close(get_nodeuid);
1243   prepared_statement_close(update_connection);
1244   prepared_statement_close(get_trial);
1245
1246   if (conn != NULL)
1247     mysql_close (conn);
1248
1249   GNUNET_free(dhtlog_api);
1250   return NULL;
1251 }
1252
1253 /* end of plugin_dhtlog_mysql.c */