codesonar fixes
[oweals/gnunet.git] / src / testing / test_testing_topology_blacklist.c
1 /*
2      This file is part of GNUnet.
3      (C) 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 3, 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  * @file testing/test_testing_topology_blacklist.c
22  * @brief base testcase for testing transport level blacklisting
23  */
24 #include "platform.h"
25 #include "gnunet_testing_lib.h"
26 #include "gnunet_core_service.h"
27
28 #define VERBOSE GNUNET_NO
29
30 /**
31  * How long until we fail the whole testcase?
32  */
33 #define TEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600)
34
35 /**
36  * How long until we give up on starting the peers? (Must be longer than the connect timeout!)
37  */
38 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300)
39
40 #define DEFAULT_NUM_PEERS 4
41
42 #define MAX_OUTSTANDING_CONNECTIONS 300
43
44 static int ok;
45
46 static unsigned long long num_peers;
47
48 static unsigned int total_connections;
49
50 static unsigned int failed_connections;
51
52 static unsigned int expected_connections;
53
54 static unsigned int expected_failed_connections;
55
56 static unsigned long long peers_left;
57
58 static struct GNUNET_TESTING_PeerGroup *pg;
59
60 const struct GNUNET_CONFIGURATION_Handle *main_cfg;
61
62 GNUNET_SCHEDULER_TaskIdentifier die_task;
63
64 static char *dotOutFileName;
65
66 static FILE *dotOutFile;
67
68 static char *blacklist_transports;
69
70 static enum GNUNET_TESTING_Topology topology = GNUNET_TESTING_TOPOLOGY_CLIQUE; /* Overlay should allow all connections */
71
72 static enum GNUNET_TESTING_Topology blacklist_topology = GNUNET_TESTING_TOPOLOGY_RING; /* Blacklist underlay into a ring */
73
74 static enum GNUNET_TESTING_Topology connection_topology = GNUNET_TESTING_TOPOLOGY_NONE; /* NONE actually means connect all allowed peers */
75
76 static enum GNUNET_TESTING_TopologyOption connect_topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_ALL; /* Try to connect all possible OVERLAY connections */
77
78 static double connect_topology_option_modifier = 0.0;
79
80 static char *test_directory;
81
82 #define MTYPE 12345
83
84 struct GNUNET_TestMessage
85 {
86   /**
87    * Header of the message
88    */
89   struct GNUNET_MessageHeader header;
90
91   /**
92    * Unique identifier for this message.
93    */
94   uint32_t uid;
95 };
96
97
98 /**
99  * Check whether peers successfully shut down.
100  */
101 void shutdown_callback (void *cls,
102                         const char *emsg)
103 {
104   if (emsg != NULL)
105     {
106 #if VERBOSE
107       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
108                   "Shutdown of peers failed!\n");
109 #endif
110       if (ok == 0)
111         ok = 666;
112     }
113   else
114     {
115 #if VERBOSE
116       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
117                   "All peers successfully shut down!\n");
118 #endif
119     }
120 }
121
122 static void
123 finish_testing ()
124 {
125   GNUNET_assert (pg != NULL);
126
127 #if VERBOSE
128   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
129               "Called finish testing, stopping daemons.\n");
130 #endif
131   sleep(1);
132 #if VERBOSE
133           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
134                       "Calling daemons_stop\n");
135 #endif
136   GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
137 #if VERBOSE
138           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
139                       "daemons_stop finished\n");
140 #endif
141   if (dotOutFile != NULL)
142     {
143       fprintf(dotOutFile, "}");
144       fclose(dotOutFile);
145     }
146
147   ok = 0;
148 }
149
150 static void
151 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
152 {
153   char *msg = cls;
154   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
155               "End badly was called (%s)... stopping daemons.\n", msg);
156
157   if (pg != NULL)
158     {
159       GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
160       ok = 7331;                /* Opposite of leet */
161     }
162   else
163     ok = 401;                   /* Never got peers started */
164
165   if (dotOutFile != NULL)
166     {
167       fprintf(dotOutFile, "}");
168       fclose(dotOutFile);
169     }
170 }
171
172
173
174 void
175 topology_callback (void *cls,
176                    const struct GNUNET_PeerIdentity *first,
177                    const struct GNUNET_PeerIdentity *second,
178                    uint32_t distance,
179                    const struct GNUNET_CONFIGURATION_Handle *first_cfg,
180                    const struct GNUNET_CONFIGURATION_Handle *second_cfg,
181                    struct GNUNET_TESTING_Daemon *first_daemon,
182                    struct GNUNET_TESTING_Daemon *second_daemon,
183                    const char *emsg)
184 {
185   if (emsg == NULL)
186     {
187       total_connections++;
188 #if VERBOSE
189       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "connected peer %s to peer %s\n",
190                first_daemon->shortname,
191                second_daemon->shortname);
192 #endif
193       if (dotOutFile != NULL)
194         fprintf(dotOutFile, "\tn%s -- n%s;\n", first_daemon->shortname, second_daemon->shortname);
195     }
196 #if VERBOSE
197   else
198     {
199       failed_connections++;
200       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to connect peer %s to peer %s with error :\n%s\n",
201                first_daemon->shortname,
202                second_daemon->shortname, emsg);
203     }
204 #endif
205
206   if (total_connections == expected_connections)
207     {
208 #if VERBOSE
209       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
210                   "Created %d total connections, which is our target number (that's bad)!\n",
211                   total_connections);
212 #endif
213
214       GNUNET_SCHEDULER_cancel (die_task);
215       die_task = GNUNET_SCHEDULER_NO_TASK;
216       die_task = GNUNET_SCHEDULER_add_now (&end_badly, "from topology_callback (too many successful connections)");
217     }
218   else if (total_connections + failed_connections == expected_connections)
219     {
220       if ((failed_connections == expected_failed_connections) && (total_connections == expected_connections - expected_failed_connections))
221         {
222           GNUNET_SCHEDULER_cancel (die_task);
223           die_task = GNUNET_SCHEDULER_NO_TASK;
224           die_task = GNUNET_SCHEDULER_add_now (&finish_testing, NULL);
225         }
226       else
227         {
228           GNUNET_SCHEDULER_cancel (die_task);
229           die_task = GNUNET_SCHEDULER_add_now (&end_badly, "from topology_callback (wrong number of failed connections)");
230         }
231     }
232   else
233     {
234 #if VERBOSE
235       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
236                   "Have %d total connections, %d failed connections, Want %d (failed) and %d (successful)\n",
237                   total_connections, failed_connections, expected_failed_connections, expected_connections - expected_failed_connections);
238 #endif
239     }
240 }
241
242 static void
243 connect_topology ()
244 {
245   expected_connections = -1;
246   if ((pg != NULL) && (peers_left == 0))
247     {
248       expected_connections = GNUNET_TESTING_connect_topology (pg, connection_topology, connect_topology_option, connect_topology_option_modifier, NULL, NULL);
249 #if VERBOSE
250       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
251                   "Have %d expected connections\n", expected_connections);
252 #endif
253     }
254
255   GNUNET_SCHEDULER_cancel (die_task);
256   if (expected_connections == GNUNET_SYSERR)
257     {
258       die_task = GNUNET_SCHEDULER_add_now (&end_badly, "from connect topology (bad return)");
259     }
260
261   die_task = GNUNET_SCHEDULER_add_delayed (TEST_TIMEOUT,
262                                            &end_badly, "from connect topology (timeout)");
263 }
264
265 static void
266 create_topology ()
267 {
268   peers_left = num_peers; /* Reset counter */
269   if (GNUNET_TESTING_create_topology (pg, topology, blacklist_topology, blacklist_transports) != GNUNET_SYSERR)
270     {
271 #if VERBOSE
272       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
273                   "Topology set up, now starting peers!\n");
274 #endif
275       GNUNET_TESTING_daemons_continue_startup(pg);
276     }
277   else
278     {
279       GNUNET_SCHEDULER_cancel (die_task);
280       die_task = GNUNET_SCHEDULER_add_now (&end_badly, "from create topology (bad return)");
281     }
282   GNUNET_SCHEDULER_cancel (die_task);
283   die_task = GNUNET_SCHEDULER_add_delayed (TEST_TIMEOUT,
284                                            &end_badly, "from continue startup (timeout)");
285 }
286
287
288 static void
289 peers_started_callback (void *cls,
290        const struct GNUNET_PeerIdentity *id,
291        const struct GNUNET_CONFIGURATION_Handle *cfg,
292        struct GNUNET_TESTING_Daemon *d, const char *emsg)
293 {
294   if (emsg != NULL)
295     {
296       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to start daemon with error: `%s'\n",
297                   emsg);
298       return;
299     }
300   GNUNET_assert (id != NULL);
301 #if VERBOSE
302   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Started daemon %llu out of %llu\n",
303               (num_peers - peers_left) + 1, num_peers);
304 #endif
305   peers_left--;
306   if (peers_left == 0)
307     {
308 #if VERBOSE
309       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
310                   "All %d daemons started, now creating topology!\n",
311                   num_peers);
312 #endif
313       GNUNET_SCHEDULER_cancel (die_task);
314       /* Set up task in case topology creation doesn't finish
315        * within a reasonable amount of time */
316       die_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
317                                                (GNUNET_TIME_UNIT_MINUTES, 5),
318                                                &end_badly, "from peers_started_callback");
319       connect_topology ();
320       ok = 0;
321     }
322 }
323
324 /**
325  * Callback indicating that the hostkey was created for a peer.
326  *
327  * @param cls NULL
328  * @param id the peer identity
329  * @param d the daemon handle (pretty useless at this point, remove?)
330  * @param emsg non-null on failure
331  */
332 void hostkey_callback (void *cls,
333                        const struct GNUNET_PeerIdentity *id,
334                        struct GNUNET_TESTING_Daemon *d,
335                        const char *emsg)
336 {
337   if (emsg != NULL)
338     {
339       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Hostkey callback received error: %s\n", emsg);
340     }
341
342 #if VERBOSE
343     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
344                 "Hostkey created for peer `%s'\n",
345                 GNUNET_i2s(id));
346 #endif
347     peers_left--;
348     if (peers_left == 0)
349       {
350 #if VERBOSE
351         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
352                     "All %d hostkeys created, now creating topology!\n",
353                     num_peers);
354 #endif
355         GNUNET_SCHEDULER_cancel (die_task);
356         /* Set up task in case topology creation doesn't finish
357          * within a reasonable amount of time */
358         die_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
359                                                  (GNUNET_TIME_UNIT_MINUTES, 5),
360                                                  &end_badly, "from hostkey_callback");
361         GNUNET_SCHEDULER_add_now(&create_topology, NULL);
362         ok = 0;
363       }
364 }
365
366 static void
367 run (void *cls,
368      char *const *args,
369      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
370 {
371   unsigned long long topology_num;
372   unsigned long long connect_topology_num;
373   unsigned long long blacklist_topology_num;
374   unsigned long long connect_topology_option_num;
375   char *connect_topology_option_modifier_string;
376   ok = 1;
377
378   dotOutFile = fopen (dotOutFileName, "w");
379   if (dotOutFile != NULL)
380     {
381       fprintf (dotOutFile, "strict graph G {\n");
382     }
383
384 #if VERBOSE
385   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
386               "Starting daemons based on config file %s\n", cfgfile);
387 #endif
388
389   if (GNUNET_YES != GNUNET_CONFIGURATION_get_value_string(cfg, "paths", "servicehome", &test_directory))
390     {
391       ok = 404;
392       if (dotOutFile != NULL)
393         {
394           fclose(dotOutFile);
395         }
396       return;
397     }
398
399   if (GNUNET_YES ==
400       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "topology",
401                                              &topology_num))
402     topology = topology_num;
403
404   if (GNUNET_YES ==
405       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "connect_topology",
406                                              &connect_topology_num))
407     connection_topology = connect_topology_num;
408
409   if (GNUNET_YES ==
410         GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "connect_topology_option",
411                                                &connect_topology_option_num))
412     connect_topology_option = connect_topology_option_num;
413
414   if (GNUNET_YES ==
415         GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "connect_topology_option_modifier",
416                                                &connect_topology_option_modifier_string))
417     {
418       if (sscanf(connect_topology_option_modifier_string, "%lf", &connect_topology_option_modifier) != 1)
419       {
420         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
421         _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
422         connect_topology_option_modifier_string,
423         "connect_topology_option_modifier",
424         "TESTING");
425         GNUNET_free (connect_topology_option_modifier_string);
426         ok = 707;
427         if (dotOutFile != NULL)
428           {
429             fclose(dotOutFile);
430           }
431         return;
432       }
433       GNUNET_free (connect_topology_option_modifier_string);
434     }
435
436   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "blacklist_transports",
437                                          &blacklist_transports))
438   {
439     GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "No transports specified for blacklisting in blacklist testcase (this shouldn't happen!)\n");
440     ok = 808;
441     if (dotOutFile != NULL)
442       {
443         fclose(dotOutFile);
444       }
445     return;
446   }
447
448   if (GNUNET_YES ==
449       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "blacklist_topology",
450                                              &blacklist_topology_num))
451     blacklist_topology = blacklist_topology_num;
452
453   if (GNUNET_SYSERR ==
454       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
455                                              &num_peers))
456     num_peers = DEFAULT_NUM_PEERS;
457
458   main_cfg = cfg;
459
460   GNUNET_assert(num_peers > 0 && num_peers < (unsigned int)-1);
461   peers_left = num_peers;
462
463   /* For this specific test we only really want a CLIQUE topology as the
464    * overlay allowed topology, and a RING topology as the underlying connection
465    * allowed topology.  So we will expect only num_peers * 2 connections to
466    * work, and (num_peers * (num_peers - 1)) - (num_peers * 2) to fail.
467    */
468   expected_connections = num_peers * (num_peers - 1);
469   expected_failed_connections = expected_connections - (num_peers * 2);
470
471
472   /* Set up a task to end testing if peer start fails */
473   die_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
474                                            (GNUNET_TIME_UNIT_MINUTES, 5),
475                                            &end_badly, "didn't start all daemons in reasonable amount of time!!!");
476
477   pg = GNUNET_TESTING_daemons_start (cfg,
478                                      peers_left, TIMEOUT, &hostkey_callback, NULL, &peers_started_callback, NULL,
479                                      &topology_callback, NULL, NULL);
480
481 }
482
483 static int
484 check ()
485 {
486   int ret;
487   char *const argv[] = {"test-testing-topology-blacklist",
488     "-c",
489     "test_testing_data_topology_blacklist.conf",
490 #if VERBOSE
491     "-L", "DEBUG",
492 #endif
493     NULL
494   };
495   struct GNUNET_GETOPT_CommandLineOption options[] = {
496     GNUNET_GETOPT_OPTION_END
497   };
498   ret = GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
499                       argv, "test-testing-topology-blacklist", "nohelp",
500                       options, &run, &ok);
501   if (ret != GNUNET_OK)
502     {
503       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "`test-testing-topology-blacklist': Failed with error code %d\n", ret);
504     }
505
506   return ok;
507 }
508
509 int
510 main (int argc, char *argv[])
511 {
512   int ret;
513
514   GNUNET_log_setup ("test_testing_topology_blacklist",
515 #if VERBOSE
516                     "DEBUG",
517 #else
518                     "WARNING",
519 #endif
520                     NULL);
521   ret = check ();
522
523   /**
524    * Need to remove base directory, subdirectories taken care
525    * of by the testing framework.
526    */
527   if (test_directory != NULL)
528     {
529       if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK)
530         {
531           GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to remove testing directory %s\n", test_directory);
532         }
533     }
534
535   return ret;
536 }
537
538 /* end of test_testing_topology_blacklist.c */