-fixes, cleanup
[oweals/gnunet.git] / src / gns / test_gns_dht_threepeer.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_dht_threepeer.c
22  * @brief tests dht lookup over 3 peers
23  *
24  * topology:
25  * alice <----> bob <-----> dave
26  *
27  * alice queries for www.buddy.bob.gnunet
28  *
29  */
30 #include "platform.h"
31 #include "gnunet_disk_lib.h"
32 #include "gnunet_testing_lib.h"
33 #include "gnunet_core_service.h"
34 #include "gnunet_dht_service.h"
35 #include "block_dns.h"
36 #include "gnunet_signatures.h"
37 #include "gnunet_namestore_service.h"
38 #include "gnunet_dnsparser_lib.h"
39 #include "gnunet_gns_service.h"
40
41 /* DEFINES */
42 #define VERBOSE GNUNET_YES
43
44 /* Timeout for entire testcase */
45 #define TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 80)
46
47 /* If number of peers not in config file, use this number */
48 #define DEFAULT_NUM_PEERS 2
49
50 #define TEST_DOMAIN "www.buddy.bob.gnunet"
51 #define TEST_IP "1.1.1.1"
52
53 /* Globals */
54
55 /**
56  * Directory to store temp data in, defined in config file
57  */
58 static char *test_directory;
59
60 /**
61  * Variable used to store the number of connections we should wait for.
62  */
63 static unsigned int expected_connections;
64
65 /**
66  * Variable used to keep track of how many peers aren't yet started.
67  */
68 static unsigned long long peers_left;
69
70 struct GNUNET_TESTING_Daemon *d1;
71 struct GNUNET_TESTING_Daemon *d2;
72 struct GNUNET_TESTING_Daemon *d3;
73
74
75 /**
76  * Total number of peers to run, set based on config file.
77  */
78 static unsigned long long num_peers;
79
80 /**
81  * Global used to count how many connections we have currently
82  * been notified about (how many times has topology_callback been called
83  * with success?)
84  */
85 static unsigned int total_connections;
86
87 /**
88  * Global used to count how many failed connections we have
89  * been notified about (how many times has topology_callback
90  * been called with failure?)
91  */
92 static unsigned int failed_connections;
93
94 /* Task handle to use to schedule test failure */
95 GNUNET_SCHEDULER_TaskIdentifier die_task;
96
97 GNUNET_SCHEDULER_TaskIdentifier bob_task;
98
99 /* Global return value (0 for success, anything else for failure) */
100 static int ok;
101
102 int bob_online, alice_online, dave_online;
103
104 struct GNUNET_CONFIGURATION_Handle *cfg_alice;
105 struct GNUNET_CONFIGURATION_Handle *cfg_bob;
106 struct GNUNET_CONFIGURATION_Handle *cfg_dave;
107
108 /**
109  * Check whether peers successfully shut down.
110  */
111 void
112 shutdown_callback (void *cls, const char *emsg)
113 {
114   if (emsg != NULL)
115   {
116     if (ok == 0)
117       ok = 2;
118   }
119 }
120
121 /**
122  * Function scheduled to be run on the successful completion of this
123  * testcase.  Specifically, called when our get request completes.
124  */
125 static void
126 finish_testing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
127 {
128   ok = 0;
129   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutting down alice!\n");
130   GNUNET_TESTING_daemon_stop (d1, TIMEOUT, &shutdown_callback, NULL,
131                               GNUNET_YES, GNUNET_NO);
132   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutting down bob!\n");
133   GNUNET_TESTING_daemon_stop (d2, TIMEOUT, &shutdown_callback, NULL,
134                               GNUNET_YES, GNUNET_NO);
135   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutting down dave\n");
136   GNUNET_TESTING_daemon_stop (d3, TIMEOUT, &shutdown_callback, NULL,
137                               GNUNET_YES, GNUNET_NO);
138   GNUNET_DISK_file_copy ("testdb/sqlite-alice.db.bak",
139                          "testdb/sqlite-alice.db.bak");
140   GNUNET_SCHEDULER_cancel(die_task);
141 }
142
143 /**
144  * Continuation for the GNUNET_DHT_get_stop call, so that we don't shut
145  * down the peers without freeing memory associated with GET request.
146  */
147 static void
148 end_badly_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
149 {
150   if (d1 != NULL)
151     GNUNET_TESTING_daemon_stop (d1, TIMEOUT, &shutdown_callback, NULL,
152                                 GNUNET_YES, GNUNET_NO);
153   if (d2 != NULL)
154     GNUNET_TESTING_daemon_stop (d2, TIMEOUT, &shutdown_callback, NULL,
155                                 GNUNET_YES, GNUNET_NO);
156 ;
157   if (d3 != NULL)
158     GNUNET_TESTING_daemon_stop (d3, TIMEOUT, &shutdown_callback, NULL,
159                                 GNUNET_YES, GNUNET_NO);
160 }
161
162 /**
163  * Check if the get_handle is being used, if so stop the request.  Either
164  * way, schedule the end_badly_cont function which actually shuts down the
165  * test.
166  */
167 static void
168 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
169 {
170   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Failing test with error: `%s'!\n",
171               (char *) cls);
172   GNUNET_SCHEDULER_add_now (&end_badly_cont, NULL);
173   ok = 1;
174 }
175
176 static void
177 commence_testing(void)
178 {
179   struct hostent *he;
180   struct in_addr a;
181   char* addr;
182
183   he = gethostbyname (TEST_DOMAIN);
184
185   if (he)
186   {
187     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "name: %s\n", he->h_name);
188     while (*he->h_addr_list)
189     {
190       bcopy(*he->h_addr_list++, (char *) &a, sizeof(a));
191       addr = inet_ntoa(a);
192       GNUNET_log(GNUNET_ERROR_TYPE_INFO, "address: %s\n", addr);
193       if (strcmp(addr, TEST_IP) == 0)
194         ok = 0;
195     }
196   }
197   else
198     ok = 1;
199   //do lookup here
200   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
201                                 (GNUNET_TIME_UNIT_SECONDS, 30),
202                                  &finish_testing, NULL);
203 }
204
205
206 /**
207  * This function is called whenever a connection attempt is finished between two of
208  * the started peers (started with GNUNET_TESTING_daemons_start).  The total
209  * number of times this function is called should equal the number returned
210  * from the GNUNET_TESTING_connect_topology call.
211  *
212  * The emsg variable is NULL on success (peers connected), and non-NULL on
213  * failure (peers failed to connect).
214  */
215 void
216 notify_connect (void *cls, const struct GNUNET_PeerIdentity *first,
217                    const struct GNUNET_PeerIdentity *second, uint32_t distance,
218                    const struct GNUNET_CONFIGURATION_Handle *first_cfg,
219                    const struct GNUNET_CONFIGURATION_Handle *second_cfg,
220                    struct GNUNET_TESTING_Daemon *first_daemon,
221                    struct GNUNET_TESTING_Daemon *second_daemon,
222                    const char *emsg)
223 {
224   if (emsg == NULL)
225   {
226     total_connections++;
227 #if VERBOSE
228     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
229                 "connected peer %s to peer %s, distance %u\n",
230                 first_daemon->shortname, second_daemon->shortname, distance);
231 #endif
232   }
233 #if VERBOSE
234   else
235   {
236     failed_connections++;
237     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
238                 "Failed to connect peer %s to peer %s with error :\n%s\n",
239                 first_daemon->shortname, second_daemon->shortname, emsg);
240   }
241 #endif
242
243   if (total_connections == expected_connections)
244   {
245 #if VERBOSE
246     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
247                 "Created %d total connections, which is our target number!  Starting next phase of testing.\n",
248                 total_connections);
249 #endif
250     GNUNET_SCHEDULER_cancel (die_task);
251     die_task =
252         GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, "from connect");
253     commence_testing();
254     
255   }
256   else if (total_connections + failed_connections == expected_connections)
257   {
258     GNUNET_SCHEDULER_cancel (die_task);
259     die_task =
260         GNUNET_SCHEDULER_add_now (&end_badly,
261                                   "from topology_callback (too many failed connections)");
262   }
263   else
264   {
265     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connecting peers dave, bob\n");
266     GNUNET_TESTING_daemons_connect (d3, d2, TIMEOUT, 5, 1,
267                                          &notify_connect, NULL);
268   }
269 }
270
271 /**
272  * Set up some data, and call API PUT function
273  */
274 static void
275 connect_ab (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
276 {
277   
278   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connecting peers alice, bob\n");
279   GNUNET_TESTING_daemons_connect (d1, d2, TIMEOUT, 5, 1,
280                                          &notify_connect, NULL);
281 }
282
283
284 static void
285 dave_started (void *cls, const struct GNUNET_PeerIdentity *id,
286                         const struct GNUNET_CONFIGURATION_Handle *cfg,
287                         struct GNUNET_TESTING_Daemon *d, const char *emsg)
288 {
289   if (emsg != NULL)
290   {
291     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
292                 "Failed to start daemon with error: `%s'\n", emsg);
293     return;
294   }
295   GNUNET_assert (id != NULL);
296   
297   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
298                                 (GNUNET_TIME_UNIT_SECONDS, 2),
299                                 &connect_ab, NULL);
300 }
301
302
303
304 static void
305 bob_started (void *cls, const struct GNUNET_PeerIdentity *id,
306                         const struct GNUNET_CONFIGURATION_Handle *cfg,
307                         struct GNUNET_TESTING_Daemon *d, const char *emsg)
308 {
309   if (emsg != NULL)
310   {
311     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
312                 "Failed to start daemon with error: `%s'\n", emsg);
313     return;
314   }
315   GNUNET_assert (id != NULL);
316   
317   //Start bob
318   d3 = GNUNET_TESTING_daemon_start(cfg_dave, TIMEOUT, GNUNET_NO, NULL, NULL, 0,
319                                    NULL, NULL, NULL, &dave_started, NULL);
320
321 }
322
323
324
325 /**
326  * Callback which is called whenever a peer is started (as a result of the
327  * GNUNET_TESTING_daemons_start call.
328  *
329  * @param cls closure argument given to GNUNET_TESTING_daemons_start
330  * @param id the GNUNET_PeerIdentity of the started peer
331  * @param cfg the configuration for this specific peer (needed to connect
332  *            to the DHT)
333  * @param d the handle to the daemon started
334  * @param emsg NULL if peer started, non-NULL on error
335  */
336 static void
337 alice_started (void *cls, const struct GNUNET_PeerIdentity *id,
338                         const struct GNUNET_CONFIGURATION_Handle *cfg,
339                         struct GNUNET_TESTING_Daemon *d, const char *emsg)
340 {
341   if (emsg != NULL)
342   {
343     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
344                 "Failed to start daemon with error: `%s'\n", emsg);
345     return;
346   }
347   GNUNET_assert (id != NULL);
348   
349   //Start bob
350   d2 = GNUNET_TESTING_daemon_start(cfg_bob, TIMEOUT, GNUNET_NO, NULL, NULL, 0,
351                                    NULL, NULL, NULL, &bob_started, NULL);
352   
353 }
354
355 static void
356 run (void *cls, char *const *args, const char *cfgfile,
357      const struct GNUNET_CONFIGURATION_Handle *cfg)
358 {
359
360   /* Get path from configuration file */
361   if (GNUNET_YES !=
362       GNUNET_CONFIGURATION_get_value_string (cfg, "paths", "servicehome",
363                                              &test_directory))
364   {
365     ok = 404;
366     return;
367   }
368
369   /* Get number of peers to start from configuration (should be two) */
370   if (GNUNET_SYSERR ==
371       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
372                                              &num_peers))
373     num_peers = DEFAULT_NUM_PEERS;
374
375   /* Set peers_left so we know when all peers started */
376   peers_left = num_peers;
377   
378   /**
379    * Modify some config options for peers
380    */
381   cfg_alice = GNUNET_CONFIGURATION_create();
382   GNUNET_CONFIGURATION_load (cfg_alice, "test_gns_dht_alice.conf");
383
384   cfg_bob = GNUNET_CONFIGURATION_create();
385   GNUNET_CONFIGURATION_load (cfg_bob, "test_gns_dht_bob.conf");
386   
387   cfg_dave = GNUNET_CONFIGURATION_create();
388   GNUNET_CONFIGURATION_load (cfg_dave, "test_gns_dht_dave.conf");
389
390   GNUNET_CONFIGURATION_load (cfg_alice, "test_gns_dht_alice.conf");
391   GNUNET_CONFIGURATION_load (cfg_bob, "test_gns_dht_bob.conf");
392   GNUNET_CONFIGURATION_load (cfg_dave, "test_gns_dht_dave.conf");
393
394   /* Set up a task to end testing if peer start fails */
395   die_task =
396       GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly,
397                                     "didn't start all daemons in reasonable amount of time!!!");
398   
399   alice_online = 0;
400   bob_online = 0;
401   dave_online = 0;
402   expected_connections = 2;
403   
404   /* Start alice */
405   d1 = GNUNET_TESTING_daemon_start(cfg_alice, TIMEOUT, GNUNET_NO, NULL, NULL, 0,
406                                    NULL, NULL, NULL, &alice_started, NULL);
407   
408   
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 */