test_gns_proxy.c 12 KB

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