5ef70407c9454d785f88563287da796a8fb6fb1b
[oweals/gnunet.git] / src / dht / test_dht_api.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 dht/test_dht_api.c
22  * @brief base test case for dht api
23  *
24  * This test case tests DHT api to DUMMY DHT service communication.
25  *
26  */
27 #include "platform.h"
28 #include "gnunet_common.h"
29 #include "gnunet_hello_lib.h"
30 #include "gnunet_getopt_lib.h"
31 #include "gnunet_os_lib.h"
32 #include "gnunet_program_lib.h"
33 #include "gnunet_scheduler_lib.h"
34 #include "gnunet_dht_service.h"
35 #include "gnunet_hello_lib.h"
36
37 #define VERBOSE GNUNET_NO
38
39 #define VERBOSE_ARM GNUNET_NO
40
41 #define START_ARM GNUNET_YES
42
43 /**
44  * How long until we really give up on a particular testcase portion?
45  */
46 #define TOTAL_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 360)
47
48 /**
49  * How long until we give up on any particular operation (and retry)?
50  */
51 #define BASE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3)
52
53 #define MTYPE 12345
54
55 struct RetryContext
56 {
57   /**
58    * When to really abort the operation.
59    */
60   struct GNUNET_TIME_Absolute real_timeout;
61
62   /**
63    * What timeout to set for the current attempt (increases)
64    */
65   struct GNUNET_TIME_Relative next_timeout;
66
67   /**
68    * The context of the peer we are dealing with.
69    */
70   struct PeerContext *peer_ctx;
71
72   /**
73    * The task identifier of the retry task, so it can be cancelled.
74    */
75   GNUNET_SCHEDULER_TaskIdentifier retry_task;
76
77 };
78
79 struct PeerContext
80 {
81   struct GNUNET_CONFIGURATION_Handle *cfg;
82   struct GNUNET_DHT_Handle *dht_handle;
83   struct GNUNET_PeerIdentity id;
84   struct GNUNET_DHT_GetHandle *get_handle;
85   struct GNUNET_DHT_FindPeerHandle *find_peer_handle;
86
87 #if START_ARM
88   GNUNET_OS_Process *arm_proc;
89 #endif
90 };
91
92 static struct PeerContext p1;
93
94 struct RetryContext retry_context;
95
96 static struct GNUNET_SCHEDULER_Handle *sched;
97
98 static int ok;
99
100 GNUNET_SCHEDULER_TaskIdentifier die_task;
101
102 #if VERBOSE
103 #define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
104 #else
105 #define OKPP do { ok++; } while (0)
106 #endif
107
108
109 static void
110 end (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
111 {
112   GNUNET_SCHEDULER_cancel (sched, die_task);
113   die_task = GNUNET_SCHEDULER_NO_TASK;
114   GNUNET_DHT_disconnect (p1.dht_handle);
115   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
116               "DHT disconnected, returning success!\n");
117   ok = 0;
118 }
119
120 static void
121 stop_arm (struct PeerContext *p)
122 {
123 #if START_ARM
124   if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM))
125     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
126   GNUNET_OS_process_wait (p->arm_proc);
127   GNUNET_OS_process_close (p->arm_proc);
128   p->arm_proc = NULL;
129 #endif
130   GNUNET_CONFIGURATION_destroy (p->cfg);
131 }
132
133
134 static void
135 end_badly ()
136 {
137   /* do work here */
138 #if VERBOSE
139   fprintf (stderr, "Ending on an unhappy note.\n");
140 #endif
141
142   if ( (retry_context.peer_ctx != NULL) && 
143        (retry_context.peer_ctx->find_peer_handle != NULL) )
144     {
145       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Stopping find peer request!\n");
146       GNUNET_DHT_find_peer_stop(retry_context.peer_ctx->find_peer_handle);
147     }
148   if ( (retry_context.peer_ctx != NULL) && 
149        (retry_context.peer_ctx->get_handle != NULL) )
150     {
151       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Stopping get request!\n");
152       GNUNET_DHT_get_stop (retry_context.peer_ctx->get_handle);
153     }
154   if (retry_context.retry_task != GNUNET_SCHEDULER_NO_TASK)
155     GNUNET_SCHEDULER_cancel(sched, retry_context.retry_task);
156   GNUNET_DHT_disconnect (p1.dht_handle);
157   ok = 1;
158 }
159
160
161 /**
162  * Signature of the main function of a task.
163  *
164  * @param cls closure
165  * @param tc context information (why was this task triggered now)
166  */
167 void
168 test_find_peer_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
169 {
170   struct PeerContext *peer = cls;
171
172   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called test_find_peer_stop!\n");
173   if (tc->reason == GNUNET_SCHEDULER_REASON_TIMEOUT)
174     {
175       GNUNET_break (0);
176       GNUNET_SCHEDULER_cancel (sched, die_task);
177       GNUNET_SCHEDULER_add_now (sched, &end_badly, NULL);
178       return;
179     }
180
181   GNUNET_assert (peer->dht_handle != NULL);
182
183   GNUNET_DHT_find_peer_stop (peer->find_peer_handle);
184
185 #if HAVE_MALICIOUS
186   GNUNET_DHT_set_malicious_getter (peer->dht_handle, GNUNET_TIME_UNIT_SECONDS);
187   GNUNET_DHT_set_malicious_putter (peer->dht_handle, GNUNET_TIME_UNIT_SECONDS);
188   GNUNET_DHT_set_malicious_dropper (peer->dht_handle);
189 #endif
190   GNUNET_SCHEDULER_add_delayed(sched, 
191                                GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 1), 
192                                &end, &p1);
193 }
194
195
196 /**
197  * Iterator called on each result obtained from a find peer
198  * operation
199  *
200  * @param cls closure (NULL)
201  * @param peer the peer we learned about
202  * @param reply response
203  */
204 void test_find_peer_processor (void *cls,
205                                const struct GNUNET_HELLO_Message *hello)
206 {
207   struct RetryContext *retry_ctx = cls;
208   struct GNUNET_PeerIdentity peer;
209
210   if (GNUNET_OK == GNUNET_HELLO_get_id(hello, &peer))
211     {
212       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
213                   "test_find_peer_processor called (peer `%s'), stopping find peer request!\n", GNUNET_i2s(&peer));
214
215       if (retry_ctx->retry_task != GNUNET_SCHEDULER_NO_TASK)
216         {
217           GNUNET_SCHEDULER_cancel(sched, retry_ctx->retry_task);
218           retry_ctx->retry_task = GNUNET_SCHEDULER_NO_TASK;
219         }
220
221       GNUNET_SCHEDULER_add_continuation (sched, &test_find_peer_stop, &p1,
222                                          GNUNET_SCHEDULER_REASON_PREREQ_DONE);
223     }
224   else
225     {
226       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
227                   "received find peer request, but hello_get_id failed!\n");
228     }
229
230 }
231
232 /**
233  * Retry the find_peer task on timeout. (Forward declaration)
234  *
235  * @param cls closure
236  * @param tc context information (why was this task triggered now?)
237  */
238 void
239 retry_find_peer_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
240
241 /**
242  * Retry the find_peer task on timeout.
243  *
244  * @param cls closure
245  * @param tc context information (why was this task triggered now)
246  */
247 void
248 retry_find_peer (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
249 {
250   struct RetryContext *retry_ctx = cls;
251   GNUNET_HashCode hash;
252   memset (&hash, 42, sizeof (GNUNET_HashCode));
253
254   if (GNUNET_TIME_absolute_get_remaining(retry_ctx->real_timeout).rel_value > 0)
255     {
256       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
257                   "test_find_peer timed out, retrying!\n");
258       retry_ctx->next_timeout = GNUNET_TIME_relative_multiply(retry_ctx->next_timeout, 2);
259       retry_ctx->peer_ctx->find_peer_handle 
260         = GNUNET_DHT_find_peer_start (retry_ctx->peer_ctx->dht_handle, 
261                                       retry_ctx->next_timeout, &hash,
262                                       GNUNET_DHT_RO_NONE,
263                                       &test_find_peer_processor, retry_ctx);
264     }
265   else
266     {
267       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
268                   "test_find_peer timed out for good, failing!\n");
269
270       retry_ctx->peer_ctx->find_peer_handle = NULL;
271     }
272
273   if (retry_ctx->peer_ctx->find_peer_handle == NULL)
274     {
275       GNUNET_break (0);
276       GNUNET_SCHEDULER_cancel (sched, die_task);
277       GNUNET_SCHEDULER_add_now (sched, &end_badly, &p1);
278       return;
279     }
280   retry_ctx->retry_task = GNUNET_SCHEDULER_add_delayed(sched, retry_ctx->next_timeout, &retry_find_peer_stop, retry_ctx);
281 }
282
283 /**
284  * Retry the find_peer task on timeout.
285  *
286  * @param cls closure
287  * @param tc context information (why was this task triggered now?)
288  */
289 void
290 retry_find_peer_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
291 {
292   struct RetryContext *retry_ctx = cls;
293   GNUNET_HashCode hash;
294   memset (&hash, 42, sizeof (GNUNET_HashCode));
295
296   if (retry_ctx->peer_ctx->find_peer_handle != NULL)
297     {
298       GNUNET_DHT_find_peer_stop(retry_ctx->peer_ctx->find_peer_handle);
299       retry_ctx->peer_ctx->find_peer_handle = NULL;
300     }  
301   GNUNET_SCHEDULER_add_now (sched, &retry_find_peer, retry_ctx);
302 }
303
304 /**
305  * Entry point for test of find_peer functionality.
306  *
307  * @param cls closure
308  * @param tc context information (why was this task triggered now)
309  */
310 void
311 test_find_peer (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
312 {
313   struct PeerContext *peer = cls;
314   GNUNET_HashCode hash;
315   memset (&hash, 42, sizeof (GNUNET_HashCode));
316
317   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called test_find_peer!\n");
318   GNUNET_assert (peer->dht_handle != NULL);
319
320   retry_context.real_timeout = GNUNET_TIME_relative_to_absolute(TOTAL_TIMEOUT);
321   retry_context.next_timeout = BASE_TIMEOUT;
322   retry_context.peer_ctx = peer;
323
324   peer->find_peer_handle
325     = GNUNET_DHT_find_peer_start (peer->dht_handle, retry_context.next_timeout, 
326                                   &hash,
327                                   GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
328                                   &test_find_peer_processor, &retry_context);
329
330   if (peer->find_peer_handle == NULL)
331     {
332       GNUNET_break (0);
333       GNUNET_SCHEDULER_cancel (sched, die_task);
334       GNUNET_SCHEDULER_add_now (sched, &end_badly, &p1);
335       return;
336     }
337   retry_context.retry_task = GNUNET_SCHEDULER_add_delayed(sched, retry_context.next_timeout, &retry_find_peer_stop, &retry_context);
338 }
339
340 /**
341  * Signature of the main function of a task.
342  *
343  * @param cls closure
344  * @param tc context information (why was this task triggered now)
345  */
346 void
347 test_get_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
348 {
349   struct PeerContext *peer = cls;
350
351   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called test_get_stop!\n");
352   if (tc->reason == GNUNET_SCHEDULER_REASON_TIMEOUT)
353     {
354       GNUNET_break (0);
355       GNUNET_SCHEDULER_cancel (sched, die_task);
356       GNUNET_SCHEDULER_add_now (sched, &end_badly, NULL);
357       return;
358     }
359   GNUNET_assert (peer->dht_handle != NULL);
360   GNUNET_DHT_get_stop (peer->get_handle);
361   GNUNET_SCHEDULER_add_now(sched, 
362                            &test_find_peer, 
363                            &p1);
364 }
365
366 void
367 test_get_iterator (void *cls,
368                    struct GNUNET_TIME_Absolute exp,
369                    const GNUNET_HashCode * key,
370                    const struct GNUNET_PeerIdentity * const *get_path,
371                    const struct GNUNET_PeerIdentity * const *put_path,
372                    enum GNUNET_BLOCK_Type type, 
373                    size_t size, const void *data)
374 {
375   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
376               "test_get_iterator called (we got a result), stopping get request!\n");
377
378   GNUNET_SCHEDULER_add_continuation (sched, &test_get_stop, &p1,
379                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
380 }
381
382 /**
383  * Signature of the main function of a task.
384  *
385  * @param cls closure
386  * @param tc context information (why was this task triggered now)
387  */
388 void
389 test_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
390 {
391   struct PeerContext *peer = cls;
392   GNUNET_HashCode hash;
393   memset (&hash, 42, sizeof (GNUNET_HashCode));
394
395   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called test_get!\n");
396
397   GNUNET_assert (peer->dht_handle != NULL);
398   retry_context.real_timeout = GNUNET_TIME_relative_to_absolute(TOTAL_TIMEOUT);
399   retry_context.next_timeout = BASE_TIMEOUT;
400
401   peer->get_handle =
402     GNUNET_DHT_get_start (peer->dht_handle, 
403                           TOTAL_TIMEOUT,
404                           GNUNET_BLOCK_TYPE_TEST,
405                           &hash,
406                           GNUNET_DHT_RO_NONE,
407                           NULL, 0,
408                           NULL, 0,
409                           &test_get_iterator, NULL);
410
411   if (peer->get_handle == NULL)
412     {
413       GNUNET_break (0);
414       GNUNET_SCHEDULER_cancel (sched, die_task);
415       GNUNET_SCHEDULER_add_now (sched, &end_badly, &p1);
416       return;
417     }
418
419   retry_context.peer_ctx = peer;
420 }
421
422 /**
423  * Signature of the main function of a task.
424  *
425  * @param cls closure
426  * @param tc context information (why was this task triggered now)
427  */
428 void
429 test_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
430 {
431   struct PeerContext *peer = cls;
432   GNUNET_HashCode hash;
433   char *data;
434   size_t data_size = 42;
435   memset (&hash, 42, sizeof (GNUNET_HashCode));
436   data = GNUNET_malloc (data_size);
437   memset (data, 43, data_size);
438   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called test_put!\n");
439   peer->dht_handle = GNUNET_DHT_connect (sched, peer->cfg, 100);
440
441   GNUNET_assert (peer->dht_handle != NULL);
442
443   GNUNET_DHT_put (peer->dht_handle, &hash, 
444                   GNUNET_DHT_RO_NONE,
445                   GNUNET_BLOCK_TYPE_TEST,
446                   data_size, data,
447                   GNUNET_TIME_relative_to_absolute (TOTAL_TIMEOUT),
448                   TOTAL_TIMEOUT,
449                   &test_get, &p1);
450   GNUNET_free(data);
451 }
452
453 static void
454 setup_peer (struct PeerContext *p, const char *cfgname)
455 {
456   p->cfg = GNUNET_CONFIGURATION_create ();
457 #if START_ARM
458   p->arm_proc = GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
459                                         "gnunet-service-arm",
460 #if VERBOSE_ARM
461                                         "-L", "DEBUG",
462 #endif
463                                         "-c", cfgname, NULL);
464 #endif
465   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
466
467 }
468
469 static void
470 run (void *cls,
471      struct GNUNET_SCHEDULER_Handle *s,
472      char *const *args,
473      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
474 {
475   GNUNET_assert (ok == 1);
476   OKPP;
477   sched = s;
478
479   die_task = GNUNET_SCHEDULER_add_delayed (sched,
480                                            GNUNET_TIME_relative_multiply
481                                            (GNUNET_TIME_UNIT_MINUTES, 1),
482                                            &end_badly, NULL);
483
484   setup_peer (&p1, "test_dht_api_peer1.conf");
485
486   GNUNET_SCHEDULER_add_delayed (sched,
487                                 GNUNET_TIME_relative_multiply
488                                 (GNUNET_TIME_UNIT_SECONDS, 1), &test_put,
489                                 &p1);
490 }
491
492 static int
493 check ()
494 {
495
496   char *const argv[] = { "test-dht-api",
497     "-c",
498     "test_dht_api_data.conf",
499 #if VERBOSE
500     "-L", "DEBUG",
501 #endif
502     NULL
503   };
504
505   struct GNUNET_GETOPT_CommandLineOption options[] = {
506     GNUNET_GETOPT_OPTION_END
507   };
508
509   ok = 1;
510   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
511                       argv, "test-dht-api", "nohelp", options, &run, &ok);
512   stop_arm (&p1);
513   return ok;
514 }
515
516
517 int
518 main (int argc, char *argv[])
519 {
520   int ret;
521 #ifdef MINGW
522   return GNUNET_SYSERR;
523 #endif
524
525   GNUNET_log_setup ("test-dht-api",
526 #if VERBOSE
527                     "DEBUG",
528 #else
529                     "WARNING",
530 #endif
531                     NULL);
532   ret = check ();
533
534   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-dht-peer-1");
535
536   return ret;
537 }
538
539 /* end of test_dht_api.c */