- using de/serialization functionality
[oweals/gnunet.git] / src / gns / test_gns_twopeer.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 gns/test_gns_twopeer.c
22  * @brief base testcase for testing DHT service with
23  *        two running peers.
24  *
25  * This testcase starts peers using the GNUNET_TESTING_daemons_start
26  * function call.  On peer start, connects to the peers DHT service
27  * by calling GNUNET_DHT_connected.  Once notified about all peers
28  * being started (by the peers_started_callback function), calls
29  * GNUNET_TESTING_connect_topology, which connects the peers in a
30  * "straight line" topology.  On notification that all peers have
31  * been properly connected, calls the do_get function which initiates
32  * a GNUNET_DHT_get from the *second* peer. Once the GNUNET_DHT_get
33  * function starts, runs the do_put function to insert data at the first peer.
34  *   If the GET is successful, schedules finish_testing
35  * to stop the test and shut down peers.  If GET is unsuccessful
36  * after GET_TIMEOUT seconds, prints an error message and shuts down
37  * the peers.
38  */
39 #include "platform.h"
40 #include "gnunet_testing_lib.h"
41 #include "gnunet_core_service.h"
42 #include "gnunet_dht_service.h"
43 #include "block_dns.h"
44 #include "gnunet_signatures.h"
45
46 /* DEFINES */
47 #define VERBOSE GNUNET_YES
48
49 /* Timeout for entire testcase */
50 #define TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 40)
51
52 /* If number of peers not in config file, use this number */
53 #define DEFAULT_NUM_PEERS 2
54
55 /* Globals */
56
57 /**
58  * Directory to store temp data in, defined in config file
59  */
60 static char *test_directory;
61
62 /**
63  * Variable used to store the number of connections we should wait for.
64  */
65 static unsigned int expected_connections;
66
67 /**
68  * Variable used to keep track of how many peers aren't yet started.
69  */
70 static unsigned long long peers_left;
71
72 struct GNUNET_TESTING_Daemon *d1;
73 struct GNUNET_TESTING_Daemon *d2;
74
75
76 /**
77  * Total number of peers to run, set based on config file.
78  */
79 static unsigned long long num_peers;
80
81 /**
82  * Global used to count how many connections we have currently
83  * been notified about (how many times has topology_callback been called
84  * with success?)
85  */
86 static unsigned int total_connections;
87
88 /**
89  * Global used to count how many failed connections we have
90  * been notified about (how many times has topology_callback
91  * been called with failure?)
92  */
93 static unsigned int failed_connections;
94
95 /* Task handle to use to schedule test failure */
96 GNUNET_SCHEDULER_TaskIdentifier die_task;
97
98 GNUNET_SCHEDULER_TaskIdentifier bob_task;
99
100 /* Global return value (0 for success, anything else for failure) */
101 static int ok;
102
103 int bob_online, alice_online;
104
105 /**
106  * Check whether peers successfully shut down.
107  */
108 void
109 shutdown_callback (void *cls, const char *emsg)
110 {
111   if (emsg != NULL)
112   {
113     if (ok == 0)
114       ok = 2;
115   }
116 }
117
118 /**
119  * Function scheduled to be run on the successful completion of this
120  * testcase.  Specifically, called when our get request completes.
121  */
122 static void
123 finish_testing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
124 {
125   ok = 0;
126   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutting down peer1!\n");
127   GNUNET_TESTING_daemon_stop (d1, TIMEOUT, &shutdown_callback, NULL,
128                               GNUNET_YES, GNUNET_NO);
129   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutting down peer2!\n");
130   GNUNET_TESTING_daemon_stop (d2, TIMEOUT, &shutdown_callback, NULL,
131                               GNUNET_YES, GNUNET_NO);
132   GNUNET_SCHEDULER_cancel(bob_task);
133   GNUNET_SCHEDULER_cancel(die_task);
134 }
135
136 /**
137  * Continuation for the GNUNET_DHT_get_stop call, so that we don't shut
138  * down the peers without freeing memory associated with GET request.
139  */
140 static void
141 end_badly_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
142 {
143   if (d1 != NULL)
144     GNUNET_TESTING_daemon_stop (d1, TIMEOUT, &shutdown_callback, NULL,
145                                 GNUNET_YES, GNUNET_NO);
146   if (d2 != NULL)
147     GNUNET_TESTING_daemon_stop (d2, TIMEOUT, &shutdown_callback, NULL,
148                                 GNUNET_YES, GNUNET_NO);
149 }
150
151 /**
152  * Check if the get_handle is being used, if so stop the request.  Either
153  * way, schedule the end_badly_cont function which actually shuts down the
154  * test.
155  */
156 static void
157 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
158 {
159   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Failing test with error: `%s'!\n",
160               (char *) cls);
161   GNUNET_SCHEDULER_cancel(bob_task);
162   GNUNET_SCHEDULER_add_now (&end_badly_cont, NULL);
163   ok = 1;
164 }
165
166 static void
167 do_lookup(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
168 {
169   //do lookup here
170   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
171                                 (GNUNET_TIME_UNIT_SECONDS, 30),
172                                  &finish_testing, NULL);
173 }
174
175 static void
176 gns_started(void *cls, const struct GNUNET_PeerIdentity *id,
177             const struct GNUNET_CONFIGURATION_Handle *cfg,
178             struct GNUNET_TESTING_Daemon *d, const char *emsg)
179 {
180   if (NULL != emsg)
181   {
182     if (d == d1)
183       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "GNS failed to start alice\n");
184     else
185       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "GNS failed to start bob\n");
186     return;
187   }
188   if (d == d1)
189   {
190     /* start gns for bob */
191     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "GNS started on alice\n");
192     GNUNET_TESTING_daemon_start_service (d2, "gns", TIMEOUT, &gns_started,
193                                         NULL);
194     return;
195   }
196
197   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "GNS started on bob\n");
198
199   /* start the lookup tests */
200   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
201                                   (GNUNET_TIME_UNIT_SECONDS, 1),
202                                   &do_lookup, NULL);
203 }
204
205 /**
206  * This function is called whenever a connection attempt is finished between two of
207  * the started peers (started with GNUNET_TESTING_daemons_start).  The total
208  * number of times this function is called should equal the number returned
209  * from the GNUNET_TESTING_connect_topology call.
210  *
211  * The emsg variable is NULL on success (peers connected), and non-NULL on
212  * failure (peers failed to connect).
213  */
214 void
215 notify_connect (void *cls, const struct GNUNET_PeerIdentity *first,
216                    const struct GNUNET_PeerIdentity *second, uint32_t distance,
217                    const struct GNUNET_CONFIGURATION_Handle *first_cfg,
218                    const struct GNUNET_CONFIGURATION_Handle *second_cfg,
219                    struct GNUNET_TESTING_Daemon *first_daemon,
220                    struct GNUNET_TESTING_Daemon *second_daemon,
221                    const char *emsg)
222 {
223   if (emsg == NULL)
224   {
225     total_connections++;
226 #if VERBOSE
227     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
228                 "connected peer %s to peer %s, distance %u\n",
229                 first_daemon->shortname, second_daemon->shortname, distance);
230 #endif
231   }
232 #if VERBOSE
233   else
234   {
235     failed_connections++;
236     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
237                 "Failed to connect peer %s to peer %s with error :\n%s\n",
238                 first_daemon->shortname, second_daemon->shortname, emsg);
239   }
240 #endif
241
242   if (total_connections == expected_connections)
243   {
244 #if VERBOSE
245     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
246                 "Created %d total connections, which is our target number!  Starting next phase of testing.\n",
247                 total_connections);
248 #endif
249     GNUNET_SCHEDULER_cancel (die_task);
250     die_task =
251         GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, "from test lookup");
252     
253     /* start gns for alice */
254     GNUNET_TESTING_daemon_start_service (d1, "gns", TIMEOUT, &gns_started, NULL);
255     
256   }
257   else if (total_connections + failed_connections == expected_connections)
258   {
259     GNUNET_SCHEDULER_cancel (die_task);
260     die_task =
261         GNUNET_SCHEDULER_add_now (&end_badly,
262                                   "from topology_callback (too many failed connections)");
263   }
264 }
265
266 /**
267  * Set up some data, and call API PUT function
268  */
269 static void
270 alice_idle (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
271 {
272   
273   alice_online = 1;
274   if (!bob_online)
275   {
276     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
277                                   (GNUNET_TIME_UNIT_SECONDS, 2),
278                                    &alice_idle, NULL);
279     return;
280   }
281
282   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connecting peers\n");
283   GNUNET_TESTING_daemons_connect (d1, d2, TIMEOUT, 5, 1,
284                                          &notify_connect, NULL);
285 }
286
287 static void
288 bob_idle (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
289 {
290   /* he's lazy FIXME forever */
291   bob_online = 1;
292   bob_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
293                                   (GNUNET_TIME_UNIT_SECONDS, 20),
294                                    &bob_idle, NULL);
295 }
296
297
298
299
300 /**
301  * Callback which is called whenever a peer is started (as a result of the
302  * GNUNET_TESTING_daemons_start call.
303  *
304  * @param cls closure argument given to GNUNET_TESTING_daemons_start
305  * @param id the GNUNET_PeerIdentity of the started peer
306  * @param cfg the configuration for this specific peer (needed to connect
307  *            to the DHT)
308  * @param d the handle to the daemon started
309  * @param emsg NULL if peer started, non-NULL on error
310  */
311 static void
312 alice_started (void *cls, const struct GNUNET_PeerIdentity *id,
313                         const struct GNUNET_CONFIGURATION_Handle *cfg,
314                         struct GNUNET_TESTING_Daemon *d, const char *emsg)
315 {
316   if (emsg != NULL)
317   {
318     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
319                 "Failed to start daemon with error: `%s'\n", emsg);
320     return;
321   }
322   GNUNET_assert (id != NULL);
323   
324   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
325                                 (GNUNET_TIME_UNIT_SECONDS, 2),
326                                 &alice_idle, NULL);
327 }
328
329 static void
330 bob_started (void *cls, const struct GNUNET_PeerIdentity *id,
331                         const struct GNUNET_CONFIGURATION_Handle *cfg,
332                         struct GNUNET_TESTING_Daemon *d, const char *emsg)
333 {
334   if (emsg != NULL)
335   {
336     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
337                 "Failed to start daemon with error: `%s'\n", emsg);
338     return;
339   }
340   GNUNET_assert (id != NULL);
341   
342   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
343                                 (GNUNET_TIME_UNIT_SECONDS, 2),
344                                 &bob_idle, NULL);
345 }
346
347 static void
348 run (void *cls, char *const *args, const char *cfgfile,
349      const struct GNUNET_CONFIGURATION_Handle *cfg)
350 {
351
352   /* Get path from configuration file */
353   if (GNUNET_YES !=
354       GNUNET_CONFIGURATION_get_value_string (cfg, "paths", "servicehome",
355                                              &test_directory))
356   {
357     ok = 404;
358     return;
359   }
360
361   /* Get number of peers to start from configuration (should be two) */
362   if (GNUNET_SYSERR ==
363       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
364                                              &num_peers))
365     num_peers = DEFAULT_NUM_PEERS;
366
367   /* Set peers_left so we know when all peers started */
368   peers_left = num_peers;
369
370   /* Set up a task to end testing if peer start fails */
371   die_task =
372       GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly,
373                                     "didn't start all daemons in reasonable amount of time!!!");
374   
375   alice_online = 0;
376   bob_online = 0;
377   expected_connections = 1;
378   
379   /* Start alice */
380   d1 = GNUNET_TESTING_daemon_start(cfg, TIMEOUT, GNUNET_NO, NULL, NULL, 0,
381                                    NULL, NULL, NULL, &alice_started, NULL);
382   
383   /* Somebody care to explain? */
384   uint16_t port = 6000;
385   uint32_t upnum = 23;
386   uint32_t fdnum = 42;
387   
388   
389   /**
390    * Modify some config options for bob
391    * namely swap keys and disable dns hijacking
392    */
393   struct GNUNET_CONFIGURATION_Handle *cfg2 = GNUNET_TESTING_create_cfg(cfg,
394                                               23, &port, &upnum,
395                                               NULL, &fdnum);
396   
397   GNUNET_CONFIGURATION_set_value_string (cfg2, "paths", "servicehome",
398                                          "/tmp/test-gnunetd-gns-peer-2/");
399   GNUNET_CONFIGURATION_set_value_string (cfg2, "gns", "HIJACK_DNS",
400                                         "NO");
401   GNUNET_CONFIGURATION_set_value_string (cfg2, "gns", "ZONEKEY",
402                                          "/tmp/bobkey");
403   GNUNET_CONFIGURATION_set_value_string (cfg2, "gns", "TRUSTED",
404                                          "alice:/tmp/alicekey");
405   
406   //Start bob
407   d2 = GNUNET_TESTING_daemon_start(cfg2, TIMEOUT, GNUNET_NO, NULL, NULL, 0,
408                                    NULL, NULL, NULL, &bob_started, NULL);
409
410
411 }
412
413 static int
414 check ()
415 {
416   int ret;
417
418   /* Arguments for GNUNET_PROGRAM_run */
419   char *const argv[] = { "test-gns-twopeer",    /* Name to give running binary */
420     "-c",
421     "test_gns_twopeer.conf",       /* Config file to use */
422 #if VERBOSE
423     "-L", "DEBUG",
424 #endif
425     NULL
426   };
427   struct GNUNET_GETOPT_CommandLineOption options[] = {
428     GNUNET_GETOPT_OPTION_END
429   };
430   /* Run the run function as a new program */
431   ret =
432       GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv,
433                           "test-gns-twopeer", "nohelp", options, &run,
434                           &ok);
435   if (ret != GNUNET_OK)
436   {
437     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
438                 "`test-gns-twopeer': Failed with error code %d\n", ret);
439   }
440   return ok;
441 }
442
443 int
444 main (int argc, char *argv[])
445 {
446   int ret;
447
448   GNUNET_log_setup ("test-gns-twopeer",
449 #if VERBOSE
450                     "DEBUG",
451 #else
452                     "WARNING",
453 #endif
454                     NULL);
455   ret = check ();
456   /**
457    * Need to remove base directory, subdirectories taken care
458    * of by the testing framework.
459    */
460   return ret;
461 }
462
463 /* end of test_gns_twopeer.c */