test_gns_proxy.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. /*
  2. This file is part of GNUnet
  3. (C) 2007, 2009, 2011, 2012 Christian Grothoff
  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 test_gns_proxy.c
  19. * @brief testcase for accessing SOCKS5 GNS proxy
  20. * @author Martin Schanzenbach
  21. */
  22. #include "platform.h"
  23. #include <curl/curl.h>
  24. #include <microhttpd.h>
  25. #include "gnunet_namestore_service.h"
  26. #include "gnunet_gns_service.h"
  27. #include "gnunet_testing_lib.h"
  28. #include "gnunet_os_lib.h"
  29. #define PORT 8080
  30. #define TEST_DOMAIN "www.gnu"
  31. #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300)
  32. /**
  33. * Return value for 'main'.
  34. */
  35. static int global_ret;
  36. static struct GNUNET_NAMESTORE_Handle *namestore;
  37. static struct MHD_Daemon *mhd;
  38. static GNUNET_SCHEDULER_TaskIdentifier mhd_task_id;
  39. static GNUNET_SCHEDULER_TaskIdentifier curl_task_id;
  40. static CURL *curl;
  41. static CURLM *multi;
  42. static char *url;
  43. static struct GNUNET_OS_Process *proxy_proc;
  44. static char* tmp_cfgfile;
  45. struct CBC
  46. {
  47. char buf[1024];
  48. size_t pos;
  49. };
  50. static struct CBC cbc;
  51. static size_t
  52. copy_buffer (void *ptr, size_t size, size_t nmemb, void *ctx)
  53. {
  54. struct CBC *cbc = ctx;
  55. if (cbc->pos + size * nmemb > sizeof(cbc->buf))
  56. return 0; /* overflow */
  57. memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
  58. cbc->pos += size * nmemb;
  59. return size * nmemb;
  60. }
  61. static int
  62. mhd_ahc (void *cls,
  63. struct MHD_Connection *connection,
  64. const char *url,
  65. const char *method,
  66. const char *version,
  67. const char *upload_data, size_t *upload_data_size,
  68. void **unused)
  69. {
  70. static int ptr;
  71. struct MHD_Response *response;
  72. int ret;
  73. if (0 != strcmp ("GET", method))
  74. return MHD_NO; /* unexpected method */
  75. if (&ptr != *unused)
  76. {
  77. *unused = &ptr;
  78. return MHD_YES;
  79. }
  80. *unused = NULL;
  81. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MHD sends respose for request to URL `%s'\n", url);
  82. response = MHD_create_response_from_buffer (strlen (url),
  83. (void *) url,
  84. MHD_RESPMEM_MUST_COPY);
  85. ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
  86. MHD_destroy_response (response);
  87. if (ret == MHD_NO)
  88. abort ();
  89. return ret;
  90. }
  91. static void
  92. do_shutdown ()
  93. {
  94. if (mhd_task_id != GNUNET_SCHEDULER_NO_TASK)
  95. {
  96. GNUNET_SCHEDULER_cancel (mhd_task_id);
  97. mhd_task_id = GNUNET_SCHEDULER_NO_TASK;
  98. }
  99. if (curl_task_id != GNUNET_SCHEDULER_NO_TASK)
  100. {
  101. GNUNET_SCHEDULER_cancel (curl_task_id);
  102. curl_task_id = GNUNET_SCHEDULER_NO_TASK;
  103. }
  104. if (NULL != mhd)
  105. {
  106. MHD_stop_daemon (mhd);
  107. mhd = NULL;
  108. }
  109. GNUNET_free_non_null (url);
  110. if (NULL != tmp_cfgfile)
  111. {
  112. if (0 != remove (tmp_cfgfile))
  113. GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "remove", tmp_cfgfile);
  114. GNUNET_free (tmp_cfgfile);
  115. tmp_cfgfile = NULL;
  116. }
  117. if (NULL != proxy_proc)
  118. {
  119. (void) GNUNET_OS_process_kill (proxy_proc, SIGKILL);
  120. GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (proxy_proc));
  121. GNUNET_OS_process_destroy (proxy_proc);
  122. proxy_proc = NULL;
  123. }
  124. url = NULL;
  125. GNUNET_SCHEDULER_shutdown ();
  126. }
  127. /**
  128. * Function to run the HTTP client.
  129. */
  130. static void
  131. curl_main (void);
  132. static void
  133. curl_task (void *cls,
  134. const struct GNUNET_SCHEDULER_TaskContext *tc)
  135. {
  136. curl_task_id = GNUNET_SCHEDULER_NO_TASK;
  137. curl_main ();
  138. }
  139. static void
  140. curl_main ()
  141. {
  142. fd_set rs;
  143. fd_set ws;
  144. fd_set es;
  145. int max;
  146. struct GNUNET_NETWORK_FDSet nrs;
  147. struct GNUNET_NETWORK_FDSet nws;
  148. struct GNUNET_TIME_Relative delay;
  149. long timeout;
  150. int running;
  151. struct CURLMsg *msg;
  152. max = 0;
  153. FD_ZERO (&rs);
  154. FD_ZERO (&ws);
  155. FD_ZERO (&es);
  156. curl_multi_perform (multi, &running);
  157. if (running == 0)
  158. {
  159. GNUNET_assert (NULL != (msg = curl_multi_info_read (multi, &running)));
  160. if (msg->msg == CURLMSG_DONE)
  161. {
  162. if (msg->data.result != CURLE_OK)
  163. {
  164. fprintf (stderr,
  165. "%s failed at %s:%d: `%s'\n",
  166. "curl_multi_perform",
  167. __FILE__,
  168. __LINE__, curl_easy_strerror (msg->data.result));
  169. global_ret = 1;
  170. }
  171. }
  172. curl_multi_remove_handle (multi, curl);
  173. curl_multi_cleanup (multi);
  174. curl_easy_cleanup (curl);
  175. curl = NULL;
  176. multi = NULL;
  177. if (cbc.pos != strlen ("/hello_world"))
  178. {
  179. GNUNET_break (0);
  180. global_ret = 2;
  181. }
  182. if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
  183. {
  184. GNUNET_break (0);
  185. global_ret = 3;
  186. }
  187. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download complete, shutting down!\n");
  188. do_shutdown ();
  189. return;
  190. }
  191. GNUNET_assert (CURLM_OK == curl_multi_fdset (multi, &rs, &ws, &es, &max));
  192. if ( (CURLM_OK != curl_multi_timeout (multi, &timeout)) ||
  193. (-1 == timeout) )
  194. delay = GNUNET_TIME_UNIT_SECONDS;
  195. else
  196. delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, (unsigned int) timeout);
  197. GNUNET_NETWORK_fdset_copy_native (&nrs,
  198. &rs,
  199. max + 1);
  200. GNUNET_NETWORK_fdset_copy_native (&nws,
  201. &ws,
  202. max + 1);
  203. curl_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
  204. delay,
  205. &nrs,
  206. &nws,
  207. &curl_task,
  208. NULL);
  209. }
  210. static void
  211. start_curl (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  212. {
  213. GNUNET_asprintf (&url,
  214. "http://%s:%d/hello_world",
  215. TEST_DOMAIN, PORT);
  216. curl = curl_easy_init ();
  217. curl_easy_setopt (curl, CURLOPT_URL, url);
  218. curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, &copy_buffer);
  219. curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbc);
  220. curl_easy_setopt (curl, CURLOPT_FAILONERROR, 1);
  221. curl_easy_setopt (curl, CURLOPT_TIMEOUT, 150L);
  222. curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 15L);
  223. curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1);
  224. curl_easy_setopt (curl, CURLOPT_PROXY, "socks5h://127.0.0.1:7777");
  225. multi = curl_multi_init ();
  226. GNUNET_assert (multi != NULL);
  227. GNUNET_assert (CURLM_OK == curl_multi_add_handle (multi, curl));
  228. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Beginning HTTP download from `%s'\n", url);
  229. curl_main ();
  230. }
  231. static void
  232. disco_ns (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  233. {
  234. GNUNET_NAMESTORE_disconnect (namestore);
  235. }
  236. /**
  237. * Callback invoked from the namestore service once record is
  238. * created.
  239. *
  240. * @param cls closure
  241. * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
  242. * will match 'result_af' from the request
  243. * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
  244. * that the VPN allocated for the redirection;
  245. * traffic to this IP will now be redirected to the
  246. * specified target peer; NULL on error
  247. */
  248. static void
  249. commence_testing (void *cls, int32_t success, const char *emsg)
  250. {
  251. GNUNET_SCHEDULER_add_now (&disco_ns, NULL);
  252. if ((emsg != NULL) && (GNUNET_YES != success))
  253. {
  254. fprintf (stderr,
  255. "NS failed to create record %s\n", emsg);
  256. GNUNET_SCHEDULER_shutdown ();
  257. return;
  258. }
  259. GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1), start_curl, NULL);
  260. }
  261. /**
  262. * Function to keep the HTTP server running.
  263. */
  264. static void
  265. mhd_main (void);
  266. static void
  267. mhd_task (void *cls,
  268. const struct GNUNET_SCHEDULER_TaskContext *tc)
  269. {
  270. mhd_task_id = GNUNET_SCHEDULER_NO_TASK;
  271. MHD_run (mhd);
  272. mhd_main ();
  273. }
  274. static void
  275. mhd_main ()
  276. {
  277. struct GNUNET_NETWORK_FDSet nrs;
  278. struct GNUNET_NETWORK_FDSet nws;
  279. fd_set rs;
  280. fd_set ws;
  281. fd_set es;
  282. int max_fd;
  283. unsigned MHD_LONG_LONG timeout;
  284. struct GNUNET_TIME_Relative delay;
  285. GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == mhd_task_id);
  286. FD_ZERO (&rs);
  287. FD_ZERO (&ws);
  288. FD_ZERO (&es);
  289. max_fd = -1;
  290. GNUNET_assert (MHD_YES ==
  291. MHD_get_fdset (mhd, &rs, &ws, &es, &max_fd));
  292. if (MHD_YES == MHD_get_timeout (mhd, &timeout))
  293. delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
  294. (unsigned int) timeout);
  295. else
  296. delay = GNUNET_TIME_UNIT_FOREVER_REL;
  297. GNUNET_NETWORK_fdset_copy_native (&nrs,
  298. &rs,
  299. max_fd + 1);
  300. GNUNET_NETWORK_fdset_copy_native (&nws,
  301. &ws,
  302. max_fd + 1);
  303. mhd_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
  304. delay,
  305. &nrs,
  306. &nws,
  307. &mhd_task,
  308. NULL);
  309. }
  310. static void
  311. run (void *cls,
  312. const struct GNUNET_CONFIGURATION_Handle *cfg,
  313. struct GNUNET_TESTING_Peer *peer)
  314. {
  315. enum MHD_FLAG flags;
  316. struct GNUNET_CRYPTO_EcdsaPrivateKey *host_key;
  317. struct GNUNET_GNSRECORD_Data rd;
  318. char *zone_keyfile;
  319. namestore = GNUNET_NAMESTORE_connect (cfg);
  320. GNUNET_assert (NULL != namestore);
  321. flags = MHD_USE_DEBUG;
  322. mhd = MHD_start_daemon (flags,
  323. PORT,
  324. NULL, NULL,
  325. &mhd_ahc, NULL,
  326. MHD_OPTION_END);
  327. GNUNET_assert (NULL != mhd);
  328. mhd_main ();
  329. tmp_cfgfile = GNUNET_DISK_mktemp ("test_gns_proxy_tmp.conf");
  330. if (NULL == tmp_cfgfile)
  331. {
  332. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  333. "Failed to create tmp cfg!\n");
  334. do_shutdown ();
  335. return;
  336. }
  337. if (GNUNET_OK != GNUNET_CONFIGURATION_write ((struct GNUNET_CONFIGURATION_Handle *)cfg,
  338. tmp_cfgfile))
  339. {
  340. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  341. "Failed to write tmp cfg\n");
  342. do_shutdown ();
  343. return;
  344. }
  345. proxy_proc = GNUNET_OS_start_process (GNUNET_NO,
  346. GNUNET_OS_INHERIT_STD_ALL,
  347. NULL,
  348. NULL,
  349. NULL,
  350. "gnunet-gns-proxy",
  351. "gnunet-gns-proxy",
  352. "-c", tmp_cfgfile, NULL);
  353. if (NULL == proxy_proc)
  354. {
  355. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  356. "Unable to start proxy\n");
  357. do_shutdown ();
  358. return;
  359. }
  360. if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
  361. "ZONEKEY",
  362. &zone_keyfile))
  363. {
  364. GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Failed to get key from cfg\n");
  365. return;
  366. }
  367. host_key = GNUNET_CRYPTO_ecdsa_key_create_from_file (zone_keyfile);
  368. rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
  369. GNUNET_assert (GNUNET_OK == GNUNET_GNSRECORD_string_to_value (GNUNET_DNSPARSER_TYPE_A,
  370. "127.0.0.1",
  371. (void**)&rd.data,
  372. &rd.data_size));
  373. rd.record_type = GNUNET_DNSPARSER_TYPE_A;
  374. GNUNET_NAMESTORE_record_create (namestore,
  375. host_key,
  376. "www",
  377. &rd,
  378. &commence_testing,
  379. NULL);
  380. GNUNET_free ((void**)rd.data);
  381. GNUNET_free (zone_keyfile);
  382. GNUNET_free (host_key);
  383. }
  384. int
  385. main (int argc, char *const *argv)
  386. {
  387. char *binary;
  388. if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary ("gnunet-gns-proxy", GNUNET_NO, NULL))
  389. {
  390. fprintf (stderr, "Proxy binary not in PATH... skipping!\n");
  391. return 0;
  392. }
  393. binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-dns");
  394. if (GNUNET_YES != GNUNET_OS_check_helper_binary (binary, GNUNET_YES, NULL)) // TODO: once we have a windows-testcase, add test parameters here
  395. {
  396. fprintf (stderr, "DNS helper binary has wrong permissions... skipping!\n");
  397. GNUNET_free (binary);
  398. return 0;
  399. }
  400. GNUNET_free (binary);
  401. if (0 != curl_global_init (CURL_GLOBAL_WIN32))
  402. {
  403. fprintf (stderr, "failed to initialize curl\n");
  404. return 2;
  405. }
  406. if (0 != GNUNET_TESTING_peer_run ("test-gnunet-gns-proxy",
  407. "test_gns_proxy.conf",
  408. &run, NULL))
  409. return 1;
  410. GNUNET_DISK_directory_remove ("/tmp/gnunet-test-gns-proxy");
  411. return global_ret;
  412. }
  413. /* end of test_gns_vpn.c */