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