test_dht_twopeer.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. /*
  2. This file is part of GNUnet.
  3. (C) 2009 Christian Grothoff (and other contributing authors)
  4. GNUnet is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published
  6. by the Free Software Foundation; either version 3, or (at your
  7. option) any later version.
  8. GNUnet is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNUnet; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  15. Boston, MA 02111-1307, USA.
  16. */
  17. /**
  18. * @file dht/test_dht_twopeer.c
  19. * @brief base testcase for testing DHT service with
  20. * two running peers
  21. */
  22. #include "platform.h"
  23. #include "gnunet_testing_lib.h"
  24. #include "gnunet_core_service.h"
  25. #include "gnunet_dht_service.h"
  26. /* DEFINES */
  27. #define VERBOSE GNUNET_NO
  28. #define MAX_GET_ATTEMPTS 10
  29. #define TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 5)
  30. #define DEFAULT_NUM_PEERS 2
  31. /* Structs */
  32. struct PeerGetContext
  33. {
  34. struct GNUNET_PeerIdentity *peer;
  35. struct GNUNET_DHT_Handle *dht_handle;
  36. struct GNUNET_DHT_GetHandle *get_handle;
  37. unsigned int get_attempts;
  38. GNUNET_SCHEDULER_TaskIdentifier retry_task;
  39. };
  40. /* Globals */
  41. static char *test_directory;
  42. static struct PeerGetContext curr_get_ctx;
  43. static unsigned int expected_connections;
  44. static unsigned long long peers_left;
  45. static struct GNUNET_TESTING_PeerGroup *pg;
  46. static unsigned long long num_peers;
  47. static unsigned int total_gets;
  48. static unsigned int gets_succeeded;
  49. static unsigned int total_connections;
  50. static unsigned int failed_connections;
  51. GNUNET_SCHEDULER_TaskIdentifier die_task;
  52. static int ok;
  53. static struct GNUNET_PeerIdentity peer1id;
  54. static struct GNUNET_PeerIdentity peer2id;
  55. static struct GNUNET_DHT_Handle *peer1dht;
  56. static struct GNUNET_DHT_Handle *peer2dht;
  57. /**
  58. * Check whether peers successfully shut down.
  59. */
  60. void
  61. shutdown_callback (void *cls, const char *emsg)
  62. {
  63. if (emsg != NULL)
  64. {
  65. if (ok == 0)
  66. ok = 2;
  67. }
  68. }
  69. static void
  70. finish_testing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  71. {
  72. GNUNET_assert (pg != NULL);
  73. GNUNET_assert (peer1dht != NULL);
  74. GNUNET_assert (peer2dht != NULL);
  75. GNUNET_DHT_disconnect (peer1dht);
  76. GNUNET_DHT_disconnect (peer2dht);
  77. GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
  78. ok = 0;
  79. }
  80. static void
  81. end_badly_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  82. {
  83. if (peer1dht != NULL)
  84. GNUNET_DHT_disconnect (peer1dht);
  85. if (peer2dht != NULL)
  86. GNUNET_DHT_disconnect (peer2dht);
  87. if (pg != NULL)
  88. GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
  89. if (curr_get_ctx.retry_task != GNUNET_SCHEDULER_NO_TASK)
  90. GNUNET_SCHEDULER_cancel (curr_get_ctx.retry_task);
  91. }
  92. static void
  93. end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  94. {
  95. if (curr_get_ctx.retry_task != GNUNET_SCHEDULER_NO_TASK)
  96. GNUNET_SCHEDULER_cancel (curr_get_ctx.retry_task);
  97. if (curr_get_ctx.get_handle != NULL)
  98. {
  99. GNUNET_DHT_get_stop (curr_get_ctx.get_handle);
  100. }
  101. GNUNET_SCHEDULER_add_now (&end_badly_cont, NULL);
  102. ok = 1;
  103. }
  104. /* Forward declaration */
  105. static void
  106. do_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
  107. /**
  108. * Iterator called on each result obtained for a DHT
  109. * operation that expects a reply
  110. *
  111. * @param cls closure
  112. * @param exp when will this value expire
  113. * @param key key of the result
  114. * @param type type of the result
  115. * @param size number of bytes in data
  116. * @param data pointer to the result data
  117. */
  118. void
  119. get_result_iterator (void *cls, struct GNUNET_TIME_Absolute exp,
  120. const GNUNET_HashCode * key,
  121. const struct GNUNET_PeerIdentity *const *get_path,
  122. const struct GNUNET_PeerIdentity *const *put_path,
  123. enum GNUNET_BLOCK_Type type, size_t size, const void *data)
  124. {
  125. struct PeerGetContext *get_context = cls;
  126. if (0 !=
  127. memcmp (&get_context->peer->hashPubKey, key, sizeof (GNUNET_HashCode)))
  128. {
  129. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  130. "Key returned is not the same key as was searched for!\n");
  131. GNUNET_SCHEDULER_cancel (die_task);
  132. GNUNET_SCHEDULER_add_now (&end_badly, "key mismatch in get response!\n");
  133. return;
  134. }
  135. if (get_context->retry_task != GNUNET_SCHEDULER_NO_TASK)
  136. {
  137. GNUNET_SCHEDULER_cancel (get_context->retry_task);
  138. get_context->retry_task = GNUNET_SCHEDULER_NO_TASK;
  139. }
  140. if (get_context->peer == &peer2id)
  141. {
  142. get_context->peer = &peer1id;
  143. get_context->dht_handle = peer2dht;
  144. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  145. "Received first correct GET request response!\n");
  146. GNUNET_DHT_get_stop (get_context->get_handle);
  147. GNUNET_SCHEDULER_add_now (&do_get, get_context);
  148. }
  149. else
  150. {
  151. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  152. "Received second correct GET request response!\n");
  153. GNUNET_SCHEDULER_cancel (die_task);
  154. GNUNET_DHT_get_stop (get_context->get_handle);
  155. GNUNET_SCHEDULER_add_now (&finish_testing, NULL);
  156. }
  157. }
  158. static void
  159. stop_retry_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
  160. static void
  161. get_stop_finished (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  162. {
  163. struct PeerGetContext *get_context = cls;
  164. if (get_context->get_attempts < MAX_GET_ATTEMPTS)
  165. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  166. "Get attempt %u failed, retrying request!\n",
  167. get_context->get_attempts);
  168. else
  169. {
  170. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  171. "Too many attempts failed, ending test!\n",
  172. get_context->get_attempts);
  173. GNUNET_SCHEDULER_cancel (die_task);
  174. GNUNET_SCHEDULER_add_now (&end_badly, "key mismatch in get response!\n");
  175. return;
  176. }
  177. get_context->get_attempts++;
  178. get_context->retry_task =
  179. GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
  180. (GNUNET_TIME_UNIT_SECONDS, 10),
  181. &stop_retry_get, get_context);
  182. get_context->get_handle =
  183. GNUNET_DHT_get_start (get_context->dht_handle,
  184. GNUNET_TIME_relative_multiply
  185. (GNUNET_TIME_UNIT_SECONDS, 5),
  186. GNUNET_BLOCK_TYPE_DHT_HELLO,
  187. &get_context->peer->hashPubKey,
  188. DEFAULT_GET_REPLICATION, GNUNET_DHT_RO_NONE, NULL,
  189. 0, NULL, 0, &get_result_iterator, get_context);
  190. }
  191. static void
  192. stop_retry_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  193. {
  194. struct PeerGetContext *get_context = cls;
  195. get_context->retry_task = GNUNET_SCHEDULER_NO_TASK;
  196. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  197. "Get attempt %u failed, canceling request!\n",
  198. get_context->get_attempts);
  199. GNUNET_DHT_get_stop (get_context->get_handle);
  200. get_context->get_handle = NULL;
  201. GNUNET_SCHEDULER_add_now (&get_stop_finished, get_context);
  202. }
  203. static void
  204. do_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  205. {
  206. struct PeerGetContext *get_context = cls;
  207. get_context->retry_task =
  208. GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
  209. (GNUNET_TIME_UNIT_SECONDS, 10),
  210. &stop_retry_get, get_context);
  211. get_context->get_handle =
  212. GNUNET_DHT_get_start (get_context->dht_handle,
  213. GNUNET_TIME_relative_multiply
  214. (GNUNET_TIME_UNIT_SECONDS, 5),
  215. GNUNET_BLOCK_TYPE_DHT_HELLO,
  216. &get_context->peer->hashPubKey,
  217. DEFAULT_GET_REPLICATION, GNUNET_DHT_RO_NONE, NULL,
  218. 0, NULL, 0, &get_result_iterator, get_context);
  219. }
  220. void
  221. topology_callback (void *cls, const struct GNUNET_PeerIdentity *first,
  222. const struct GNUNET_PeerIdentity *second, uint32_t distance,
  223. const struct GNUNET_CONFIGURATION_Handle *first_cfg,
  224. const struct GNUNET_CONFIGURATION_Handle *second_cfg,
  225. struct GNUNET_TESTING_Daemon *first_daemon,
  226. struct GNUNET_TESTING_Daemon *second_daemon,
  227. const char *emsg)
  228. {
  229. if (emsg == NULL)
  230. {
  231. total_connections++;
  232. #if VERBOSE
  233. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  234. "connected peer %s to peer %s, distance %u\n",
  235. first_daemon->shortname, second_daemon->shortname, distance);
  236. #endif
  237. }
  238. #if VERBOSE
  239. else
  240. {
  241. failed_connections++;
  242. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  243. "Failed to connect peer %s to peer %s with error :\n%s\n",
  244. first_daemon->shortname, second_daemon->shortname, emsg);
  245. }
  246. #endif
  247. if (total_connections == expected_connections)
  248. {
  249. #if VERBOSE
  250. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  251. "Created %d total connections, which is our target number! Starting next phase of testing.\n",
  252. total_connections);
  253. #endif
  254. GNUNET_SCHEDULER_cancel (die_task);
  255. die_task =
  256. GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, "from test gets");
  257. curr_get_ctx.dht_handle = peer1dht;
  258. curr_get_ctx.peer = &peer2id;
  259. GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
  260. (GNUNET_TIME_UNIT_SECONDS, 2), &do_get,
  261. &curr_get_ctx);
  262. }
  263. else if (total_connections + failed_connections == expected_connections)
  264. {
  265. GNUNET_SCHEDULER_cancel (die_task);
  266. die_task =
  267. GNUNET_SCHEDULER_add_now (&end_badly,
  268. "from topology_callback (too many failed connections)");
  269. }
  270. }
  271. static void
  272. connect_topology (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  273. {
  274. expected_connections = -1;
  275. if ((pg != NULL) && (peers_left == 0))
  276. expected_connections =
  277. GNUNET_TESTING_connect_topology (pg, GNUNET_TESTING_TOPOLOGY_CLIQUE,
  278. GNUNET_TESTING_TOPOLOGY_OPTION_ALL,
  279. 0.0, TIMEOUT, 12, NULL, NULL);
  280. GNUNET_SCHEDULER_cancel (die_task);
  281. if (expected_connections == GNUNET_SYSERR)
  282. die_task =
  283. GNUNET_SCHEDULER_add_now (&end_badly,
  284. "from connect topology (bad return)");
  285. die_task =
  286. GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly,
  287. "from connect topology (timeout)");
  288. }
  289. static void
  290. peers_started_callback (void *cls, const struct GNUNET_PeerIdentity *id,
  291. const struct GNUNET_CONFIGURATION_Handle *cfg,
  292. struct GNUNET_TESTING_Daemon *d, const char *emsg)
  293. {
  294. if (emsg != NULL)
  295. {
  296. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  297. "Failed to start daemon with error: `%s'\n", emsg);
  298. return;
  299. }
  300. GNUNET_assert (id != NULL);
  301. if (peers_left == num_peers)
  302. {
  303. memcpy (&peer1id, id, sizeof (struct GNUNET_PeerIdentity));
  304. peer1dht = GNUNET_DHT_connect (cfg, 100);
  305. if (peer1dht == NULL)
  306. {
  307. GNUNET_SCHEDULER_cancel (die_task);
  308. GNUNET_SCHEDULER_add_now (&end_badly, "Failed to get dht handle!\n");
  309. }
  310. }
  311. else
  312. {
  313. memcpy (&peer2id, id, sizeof (struct GNUNET_PeerIdentity));
  314. peer2dht = GNUNET_DHT_connect (cfg, 100);
  315. if (peer2dht == NULL)
  316. {
  317. GNUNET_SCHEDULER_cancel (die_task);
  318. GNUNET_SCHEDULER_add_now (&end_badly, "Failed to get dht handle!\n");
  319. }
  320. }
  321. peers_left--;
  322. if (peers_left == 0)
  323. {
  324. #if VERBOSE
  325. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  326. "All %d daemons started, now connecting peers!\n", num_peers);
  327. #endif
  328. GNUNET_SCHEDULER_cancel (die_task);
  329. /* Set up task in case topology creation doesn't finish
  330. * within a reasonable amount of time */
  331. die_task =
  332. GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly,
  333. "from peers_started_callback");
  334. GNUNET_SCHEDULER_add_now (&connect_topology, NULL);
  335. ok = 0;
  336. }
  337. }
  338. static void
  339. run (void *cls, char *const *args, const char *cfgfile,
  340. const struct GNUNET_CONFIGURATION_Handle *cfg)
  341. {
  342. if (GNUNET_YES !=
  343. GNUNET_CONFIGURATION_get_value_string (cfg, "paths", "servicehome",
  344. &test_directory))
  345. {
  346. ok = 404;
  347. return;
  348. }
  349. if (GNUNET_SYSERR ==
  350. GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
  351. &num_peers))
  352. num_peers = DEFAULT_NUM_PEERS;
  353. peers_left = num_peers;
  354. total_gets = num_peers;
  355. gets_succeeded = 0;
  356. /* Set up a task to end testing if peer start fails */
  357. die_task =
  358. GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly,
  359. "didn't start all daemons in reasonable amount of time!!!");
  360. pg = GNUNET_TESTING_daemons_start (cfg, num_peers, 10, num_peers, TIMEOUT,
  361. NULL, NULL, &peers_started_callback, NULL,
  362. &topology_callback, NULL, NULL);
  363. }
  364. static int
  365. check ()
  366. {
  367. int ret;
  368. char *const argv[] = { "test-dht-twopeer",
  369. "-c",
  370. "test_dht_twopeer_data.conf",
  371. #if VERBOSE
  372. "-L", "DEBUG",
  373. #endif
  374. NULL
  375. };
  376. struct GNUNET_GETOPT_CommandLineOption options[] = {
  377. GNUNET_GETOPT_OPTION_END
  378. };
  379. ret =
  380. GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv,
  381. "test-dht-twopeer", "nohelp", options, &run, &ok);
  382. if (ret != GNUNET_OK)
  383. {
  384. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  385. "`test-dht-twopeer': Failed with error code %d\n", ret);
  386. }
  387. return ok;
  388. }
  389. int
  390. main (int argc, char *argv[])
  391. {
  392. int ret;
  393. GNUNET_log_setup ("test-dht-twopeer",
  394. #if VERBOSE
  395. "DEBUG",
  396. #else
  397. "WARNING",
  398. #endif
  399. NULL);
  400. ret = check ();
  401. /**
  402. * Need to remove base directory, subdirectories taken care
  403. * of by the testing framework.
  404. */
  405. if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK)
  406. {
  407. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  408. "Failed to remove testing directory %s\n", test_directory);
  409. }
  410. return ret;
  411. }
  412. /* end of test_dht_twopeer.c */