- LRN's patch
[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 #include "gnunet_namestore_service.h"
46 #include "gnunet_dnsparser_lib.h"
47 #include "gnunet_gns_service.h"
48
49 /* DEFINES */
50 #define VERBOSE GNUNET_YES
51
52 /* Timeout for entire testcase */
53 #define TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 80)
54
55 /* If number of peers not in config file, use this number */
56 #define DEFAULT_NUM_PEERS 2
57
58 /* Globals */
59
60 /**
61  * Directory to store temp data in, defined in config file
62  */
63 static char *test_directory;
64
65 /**
66  * Variable used to store the number of connections we should wait for.
67  */
68 static unsigned int expected_connections;
69
70 /**
71  * Variable used to keep track of how many peers aren't yet started.
72  */
73 static unsigned long long peers_left;
74
75 struct GNUNET_TESTING_Daemon *d1;
76 struct GNUNET_TESTING_Daemon *d2;
77
78
79 /**
80  * Total number of peers to run, set based on config file.
81  */
82 static unsigned long long num_peers;
83
84 /**
85  * Global used to count how many connections we have currently
86  * been notified about (how many times has topology_callback been called
87  * with success?)
88  */
89 static unsigned int total_connections;
90
91 /**
92  * Global used to count how many failed connections we have
93  * been notified about (how many times has topology_callback
94  * been called with failure?)
95  */
96 static unsigned int failed_connections;
97
98 /* Task handle to use to schedule test failure */
99 GNUNET_SCHEDULER_TaskIdentifier die_task;
100
101 GNUNET_SCHEDULER_TaskIdentifier bob_task;
102
103 /* Global return value (0 for success, anything else for failure) */
104 static int ok;
105
106 int bob_online, alice_online;
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 peer1!\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 peer2!\n");
133   GNUNET_TESTING_daemon_stop (d2, TIMEOUT, &shutdown_callback, NULL,
134                               GNUNET_YES, GNUNET_NO);
135   GNUNET_SCHEDULER_cancel(bob_task);
136   GNUNET_SCHEDULER_cancel(die_task);
137 }
138
139 /**
140  * Continuation for the GNUNET_DHT_get_stop call, so that we don't shut
141  * down the peers without freeing memory associated with GET request.
142  */
143 static void
144 end_badly_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
145 {
146   if (d1 != NULL)
147     GNUNET_TESTING_daemon_stop (d1, TIMEOUT, &shutdown_callback, NULL,
148                                 GNUNET_YES, GNUNET_NO);
149   if (d2 != NULL)
150     GNUNET_TESTING_daemon_stop (d2, TIMEOUT, &shutdown_callback, NULL,
151                                 GNUNET_YES, GNUNET_NO);
152 }
153
154 /**
155  * Check if the get_handle is being used, if so stop the request.  Either
156  * way, schedule the end_badly_cont function which actually shuts down the
157  * test.
158  */
159 static void
160 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
161 {
162   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Failing test with error: `%s'!\n",
163               (char *) cls);
164   GNUNET_SCHEDULER_cancel(bob_task);
165   GNUNET_SCHEDULER_add_now (&end_badly_cont, NULL);
166   ok = 1;
167 }
168
169 static void
170 do_lookup(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
171 {
172   //do lookup here
173   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
174                                 (GNUNET_TIME_UNIT_SECONDS, 30),
175                                  &finish_testing, NULL);
176 }
177
178 static void
179 gns_started(void *cls, const struct GNUNET_PeerIdentity *id,
180             const struct GNUNET_CONFIGURATION_Handle *cfg,
181             struct GNUNET_TESTING_Daemon *d, const char *emsg)
182 {
183   if (NULL != emsg)
184   {
185     if (d == d1)
186       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "GNS failed to start alice\n");
187     else
188       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "GNS failed to start bob\n");
189     return;
190   }
191   if (d == d1)
192   {
193     /* start gns for bob */
194     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "GNS started on alice\n");
195     GNUNET_TESTING_daemon_start_service (d2, "gns", TIMEOUT, &gns_started,
196                                         NULL);
197     return;
198   }
199
200   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "GNS started on bob\n");
201
202   /* start the lookup tests */
203   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
204                                   (GNUNET_TIME_UNIT_SECONDS, 1),
205                                   &do_lookup, NULL);
206 }
207
208 /**
209  * This function is called whenever a connection attempt is finished between two of
210  * the started peers (started with GNUNET_TESTING_daemons_start).  The total
211  * number of times this function is called should equal the number returned
212  * from the GNUNET_TESTING_connect_topology call.
213  *
214  * The emsg variable is NULL on success (peers connected), and non-NULL on
215  * failure (peers failed to connect).
216  */
217 void
218 notify_connect (void *cls, const struct GNUNET_PeerIdentity *first,
219                    const struct GNUNET_PeerIdentity *second, uint32_t distance,
220                    const struct GNUNET_CONFIGURATION_Handle *first_cfg,
221                    const struct GNUNET_CONFIGURATION_Handle *second_cfg,
222                    struct GNUNET_TESTING_Daemon *first_daemon,
223                    struct GNUNET_TESTING_Daemon *second_daemon,
224                    const char *emsg)
225 {
226   if (emsg == NULL)
227   {
228     total_connections++;
229 #if VERBOSE
230     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
231                 "connected peer %s to peer %s, distance %u\n",
232                 first_daemon->shortname, second_daemon->shortname, distance);
233 #endif
234   }
235 #if VERBOSE
236   else
237   {
238     failed_connections++;
239     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
240                 "Failed to connect peer %s to peer %s with error :\n%s\n",
241                 first_daemon->shortname, second_daemon->shortname, emsg);
242   }
243 #endif
244
245   if (total_connections == expected_connections)
246   {
247 #if VERBOSE
248     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
249                 "Created %d total connections, which is our target number!  Starting next phase of testing.\n",
250                 total_connections);
251 #endif
252     GNUNET_SCHEDULER_cancel (die_task);
253     die_task =
254         GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, "from test lookup");
255     
256     /* start gns for alice */
257     GNUNET_TESTING_daemon_start_service (d1, "gns", TIMEOUT, &gns_started, NULL);
258     
259   }
260   else if (total_connections + failed_connections == expected_connections)
261   {
262     GNUNET_SCHEDULER_cancel (die_task);
263     die_task =
264         GNUNET_SCHEDULER_add_now (&end_badly,
265                                   "from topology_callback (too many failed connections)");
266   }
267 }
268
269 /**
270  * Set up some data, and call API PUT function
271  */
272 static void
273 alice_idle (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
274 {
275   
276   alice_online = 1;
277   if (!bob_online)
278   {
279     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
280                                   (GNUNET_TIME_UNIT_SECONDS, 2),
281                                    &alice_idle, NULL);
282     return;
283   }
284
285   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connecting peers\n");
286   GNUNET_TESTING_daemons_connect (d1, d2, TIMEOUT, 5, 1,
287                                          &notify_connect, NULL);
288 }
289
290 static void
291 bob_idle (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
292 {
293   /* he's lazy FIXME forever */
294   bob_online = 1;
295   bob_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
296                                   (GNUNET_TIME_UNIT_SECONDS, 20),
297                                    &bob_idle, NULL);
298 }
299
300
301
302
303 /**
304  * Callback which is called whenever a peer is started (as a result of the
305  * GNUNET_TESTING_daemons_start call.
306  *
307  * @param cls closure argument given to GNUNET_TESTING_daemons_start
308  * @param id the GNUNET_PeerIdentity of the started peer
309  * @param cfg the configuration for this specific peer (needed to connect
310  *            to the DHT)
311  * @param d the handle to the daemon started
312  * @param emsg NULL if peer started, non-NULL on error
313  */
314 static void
315 alice_started (void *cls, const struct GNUNET_PeerIdentity *id,
316                         const struct GNUNET_CONFIGURATION_Handle *cfg,
317                         struct GNUNET_TESTING_Daemon *d, const char *emsg)
318 {
319   if (emsg != NULL)
320   {
321     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
322                 "Failed to start daemon with error: `%s'\n", emsg);
323     return;
324   }
325   GNUNET_assert (id != NULL);
326   
327   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
328                                 (GNUNET_TIME_UNIT_SECONDS, 2),
329                                 &alice_idle, NULL);
330 }
331
332 static void
333 bob_started (void *cls, const struct GNUNET_PeerIdentity *id,
334                         const struct GNUNET_CONFIGURATION_Handle *cfg,
335                         struct GNUNET_TESTING_Daemon *d, const char *emsg)
336 {
337   if (emsg != NULL)
338   {
339     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
340                 "Failed to start daemon with error: `%s'\n", emsg);
341     return;
342   }
343   GNUNET_assert (id != NULL);
344   
345   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
346                                 (GNUNET_TIME_UNIT_SECONDS, 2),
347                                 &bob_idle, NULL);
348 }
349
350 static void
351 run (void *cls, char *const *args, const char *cfgfile,
352      const struct GNUNET_CONFIGURATION_Handle *cfg)
353 {
354
355   struct GNUNET_NAMESTORE_Handle* namestore_handle;
356   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded alice_pkey, bob_pkey;
357   struct GNUNET_CRYPTO_RsaPrivateKey *alice_key, *bob_key;
358   char* bob_keyfile;
359   char* alice_keyfile;
360
361   /* Get path from configuration file */
362   if (GNUNET_YES !=
363       GNUNET_CONFIGURATION_get_value_string (cfg, "paths", "servicehome",
364                                              &test_directory))
365   {
366     ok = 404;
367     return;
368   }
369
370   /* Get number of peers to start from configuration (should be two) */
371   if (GNUNET_SYSERR ==
372       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
373                                              &num_peers))
374     num_peers = DEFAULT_NUM_PEERS;
375
376   /* Set peers_left so we know when all peers started */
377   peers_left = num_peers;
378   
379   /* Somebody care to explain? */
380   uint16_t port = 6000;
381   uint32_t upnum = 23;
382   uint32_t fdnum = 42;
383   
384   /**
385    * Modify some config options for bob
386    * namely swap keys and disable dns hijacking
387    */
388   struct GNUNET_CONFIGURATION_Handle *cfg2 = GNUNET_TESTING_create_cfg(cfg,
389                                               23, &port, &upnum,
390                                               NULL, &fdnum);
391   
392   GNUNET_CONFIGURATION_set_value_string (cfg2, "paths", "servicehome",
393                                          "/tmp/test-gnunetd-gns-peer-2/");
394   GNUNET_CONFIGURATION_set_value_string (cfg2, "gns", "HIJACK_DNS",
395                                         "NO");
396   GNUNET_CONFIGURATION_set_value_string (cfg2, "gns", "ZONEKEY",
397                                          "/tmp/bobkey");
398   
399   /* put records into namestore */
400   namestore_handle = GNUNET_NAMESTORE_connect(cfg);
401   if (NULL == namestore_handle)
402   {
403     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Failed to connect to namestore\n");
404     ok = -1;
405     return;
406   }
407
408   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "gns",
409                                                           "ZONEKEY",
410                                                           &alice_keyfile))
411   {
412     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Failed to get alice's key from cfg\n");
413     ok = -1;
414     return;
415   }
416
417   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg2, "gns",
418                                                           "ZONEKEY",
419                                                           &bob_keyfile))
420   {
421     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Failed to get bob's key from cfg\n");
422     ok = -1;
423     return;
424   }
425   
426   alice_key = GNUNET_CRYPTO_rsa_key_create_from_file (alice_keyfile);
427   bob_key = GNUNET_CRYPTO_rsa_key_create_from_file (bob_keyfile);
428
429   GNUNET_CRYPTO_rsa_key_get_public (alice_key, &alice_pkey);
430   GNUNET_CRYPTO_rsa_key_get_public (bob_key, &bob_pkey);
431
432   struct GNUNET_NAMESTORE_RecordData rd;
433   rd.data = &bob_pkey;
434   rd.expiration = GNUNET_TIME_absolute_get_forever ();
435   rd.data_size = sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded);
436   rd.record_type = GNUNET_GNS_RECORD_PKEY;
437
438   GNUNET_NAMESTORE_record_create (namestore_handle,
439                                   alice_key,
440                                   "bob",
441                                   &rd,
442                                   NULL,
443                                   NULL);
444   
445   rd.data = &alice_pkey;
446   GNUNET_NAMESTORE_record_create (namestore_handle,
447                                   bob_key,
448                                   "alice",
449                                   &rd,
450                                   NULL,
451                                   NULL);
452
453   char* ip = "127.0.0.1";
454   struct in_addr *web = GNUNET_malloc(sizeof(struct in_addr));
455   GNUNET_assert(1 == inet_pton (AF_INET, ip, web));
456
457   rd.data_size = sizeof(struct in_addr);
458   rd.data = web;
459   rd.record_type = GNUNET_DNSPARSER_TYPE_A;
460
461   GNUNET_NAMESTORE_record_create (namestore_handle,
462                                   bob_key,
463                                   "www",
464                                   &rd,
465                                   NULL,
466                                   NULL);
467   
468   /* Set up a task to end testing if peer start fails */
469   die_task =
470       GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly,
471                                     "didn't start all daemons in reasonable amount of time!!!");
472   
473   alice_online = 0;
474   bob_online = 0;
475   expected_connections = 1;
476   
477   /* Start alice */
478   d1 = GNUNET_TESTING_daemon_start(cfg, TIMEOUT, GNUNET_NO, NULL, NULL, 0,
479                                    NULL, NULL, NULL, &alice_started, NULL);
480   
481   
482   //Start bob
483   d2 = GNUNET_TESTING_daemon_start(cfg2, TIMEOUT, GNUNET_NO, NULL, NULL, 0,
484                                    NULL, NULL, NULL, &bob_started, NULL);
485
486
487 }
488
489 static int
490 check ()
491 {
492   int ret;
493
494   /* Arguments for GNUNET_PROGRAM_run */
495   char *const argv[] = { "test-gns-twopeer",    /* Name to give running binary */
496     "-c",
497     "test_gns_twopeer.conf",       /* Config file to use */
498 #if VERBOSE
499     "-L", "DEBUG",
500 #endif
501     NULL
502   };
503   struct GNUNET_GETOPT_CommandLineOption options[] = {
504     GNUNET_GETOPT_OPTION_END
505   };
506   /* Run the run function as a new program */
507   ret =
508       GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv,
509                           "test-gns-twopeer", "nohelp", options, &run,
510                           &ok);
511   if (ret != GNUNET_OK)
512   {
513     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
514                 "`test-gns-twopeer': Failed with error code %d\n", ret);
515   }
516   return ok;
517 }
518
519 int
520 main (int argc, char *argv[])
521 {
522   int ret;
523
524   GNUNET_log_setup ("test-gns-twopeer",
525 #if VERBOSE
526                     "DEBUG",
527 #else
528                     "WARNING",
529 #endif
530                     NULL);
531   ret = check ();
532   /**
533    * Need to remove base directory, subdirectories taken care
534    * of by the testing framework.
535    */
536   return ret;
537 }
538
539 /* end of test_gns_twopeer.c */