test_gnunet_vpn.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  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_gnunet_vpn.c
  19. * @brief testcase for tunneling HTTP over the GNUnet VPN
  20. * @author Christian Grothoff
  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_vpn_service.h"
  30. #include "gnunet_testing_lib.h"
  31. #define PORT 48080
  32. #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300)
  33. /**
  34. * Return value for 'main'.
  35. */
  36. static int global_ret;
  37. static struct GNUNET_VPN_Handle *vpn;
  38. static struct MHD_Daemon *mhd;
  39. static struct GNUNET_SCHEDULER_Task * mhd_task_id;
  40. static struct GNUNET_SCHEDULER_Task * curl_task_id;
  41. static struct GNUNET_SCHEDULER_Task * ctrl_c_task_id;
  42. static struct GNUNET_VPN_RedirectionRequest *rr;
  43. static CURL *curl;
  44. static CURLM *multi;
  45. static char *url;
  46. /**
  47. * IP address of the ultimate destination.
  48. */
  49. static const char *dest_ip;
  50. /**
  51. * Address family of the dest_ip.
  52. */
  53. static int dest_af;
  54. /**
  55. * Address family to use by the curl client.
  56. */
  57. static int src_af;
  58. struct CBC
  59. {
  60. char buf[1024];
  61. size_t pos;
  62. };
  63. static struct CBC cbc;
  64. static size_t
  65. copy_buffer (void *ptr, size_t size, size_t nmemb, void *ctx)
  66. {
  67. struct CBC *cbc = ctx;
  68. if (cbc->pos + size * nmemb > sizeof (cbc->buf))
  69. return 0; /* overflow */
  70. memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
  71. cbc->pos += size * nmemb;
  72. return size * nmemb;
  73. }
  74. static int
  75. mhd_ahc (void *cls, struct MHD_Connection *connection, const char *url,
  76. const char *method, const char *version, const char *upload_data,
  77. size_t * upload_data_size, void **unused)
  78. {
  79. static int ptr;
  80. struct MHD_Response *response;
  81. int ret;
  82. if (0 != strcmp ("GET", method))
  83. return MHD_NO; /* unexpected method */
  84. if (&ptr != *unused)
  85. {
  86. *unused = &ptr;
  87. return MHD_YES;
  88. }
  89. *unused = NULL;
  90. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  91. "MHD sends respose for request to URL `%s'\n", url);
  92. response =
  93. MHD_create_response_from_buffer (strlen (url), (void *) url,
  94. MHD_RESPMEM_MUST_COPY);
  95. ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
  96. MHD_destroy_response (response);
  97. if (ret == MHD_NO)
  98. abort ();
  99. return ret;
  100. }
  101. static void
  102. do_shutdown ()
  103. {
  104. if (mhd_task_id != NULL)
  105. {
  106. GNUNET_SCHEDULER_cancel (mhd_task_id);
  107. mhd_task_id = NULL;
  108. }
  109. if (curl_task_id != NULL)
  110. {
  111. GNUNET_SCHEDULER_cancel (curl_task_id);
  112. curl_task_id = NULL;
  113. }
  114. if (ctrl_c_task_id != NULL)
  115. {
  116. GNUNET_SCHEDULER_cancel (ctrl_c_task_id);
  117. ctrl_c_task_id = NULL;
  118. }
  119. if (NULL != mhd)
  120. {
  121. MHD_stop_daemon (mhd);
  122. mhd = NULL;
  123. }
  124. if (NULL != rr)
  125. {
  126. GNUNET_VPN_cancel_request (rr);
  127. rr = NULL;
  128. }
  129. if (NULL != vpn)
  130. {
  131. GNUNET_VPN_disconnect (vpn);
  132. vpn = NULL;
  133. }
  134. GNUNET_free_non_null (url);
  135. url = NULL;
  136. }
  137. /**
  138. * Function to run the HTTP client.
  139. */
  140. static void
  141. curl_main (void *cls,
  142. const struct GNUNET_SCHEDULER_TaskContext *tc)
  143. {
  144. fd_set rs;
  145. fd_set ws;
  146. fd_set es;
  147. int max;
  148. struct GNUNET_NETWORK_FDSet nrs;
  149. struct GNUNET_NETWORK_FDSet nws;
  150. struct GNUNET_TIME_Relative delay;
  151. long timeout;
  152. int running;
  153. struct CURLMsg *msg;
  154. curl_task_id = NULL;
  155. max = 0;
  156. FD_ZERO (&rs);
  157. FD_ZERO (&ws);
  158. FD_ZERO (&es);
  159. curl_multi_perform (multi, &running);
  160. if (running == 0)
  161. {
  162. GNUNET_assert (NULL != (msg = curl_multi_info_read (multi, &running)));
  163. if (msg->msg == CURLMSG_DONE)
  164. {
  165. if (msg->data.result != CURLE_OK)
  166. {
  167. fprintf (stderr, "%s failed at %s:%d: `%s'\n", "curl_multi_perform",
  168. __FILE__, __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. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  186. "You might want to check if your host-based firewall is blocking the connections.\n");
  187. global_ret = 3;
  188. }
  189. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download complete, shutting down!\n");
  190. do_shutdown ();
  191. return;
  192. }
  193. GNUNET_assert (CURLM_OK == curl_multi_fdset (multi, &rs, &ws, &es, &max));
  194. if ((CURLM_OK != curl_multi_timeout (multi, &timeout)) || (-1 == timeout))
  195. delay = GNUNET_TIME_UNIT_SECONDS;
  196. else
  197. delay =
  198. GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
  199. (unsigned int) timeout);
  200. GNUNET_NETWORK_fdset_copy_native (&nrs, &rs, max + 1);
  201. GNUNET_NETWORK_fdset_copy_native (&nws, &ws, max + 1);
  202. curl_task_id =
  203. GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, delay,
  204. &nrs, &nws, &curl_main, NULL);
  205. }
  206. /**
  207. * Callback invoked from the VPN service once a redirection is
  208. * available. Provides the IP address that can now be used to
  209. * reach the requested destination (in our case, the MHD server)
  210. *
  211. * @param cls closure
  212. * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
  213. * will match 'result_af' from the request
  214. * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
  215. * that the VPN allocated for the redirection;
  216. * traffic to this IP will now be redirected to the
  217. * specified target peer; NULL on error
  218. */
  219. static void
  220. allocation_cb (void *cls, int af, const void *address)
  221. {
  222. char ips[INET6_ADDRSTRLEN];
  223. rr = NULL;
  224. if (src_af != af)
  225. {
  226. fprintf (stderr, "VPN failed to allocate appropriate address\n");
  227. GNUNET_SCHEDULER_shutdown ();
  228. return;
  229. }
  230. if (AF_INET6 == af)
  231. GNUNET_asprintf (&url,
  232. "http://[%s]:%u/hello_world",
  233. inet_ntop (af, address, ips, sizeof (ips)),
  234. (unsigned int) PORT);
  235. else
  236. GNUNET_asprintf (&url,
  237. "http://%s:%u/hello_world",
  238. inet_ntop (af, address, ips, sizeof (ips)),
  239. (unsigned int) PORT);
  240. curl = curl_easy_init ();
  241. curl_easy_setopt (curl, CURLOPT_URL, url);
  242. curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, &copy_buffer);
  243. curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbc);
  244. curl_easy_setopt (curl, CURLOPT_FAILONERROR, 1);
  245. curl_easy_setopt (curl, CURLOPT_TIMEOUT, 150L);
  246. curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 15L);
  247. curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1);
  248. curl_easy_setopt (curl, CURLOPT_VERBOSE, 0);
  249. multi = curl_multi_init ();
  250. GNUNET_assert (multi != NULL);
  251. GNUNET_assert (CURLM_OK == curl_multi_add_handle (multi, curl));
  252. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  253. "Beginning HTTP download from `%s'\n",
  254. url);
  255. GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
  256. &curl_main,
  257. NULL);
  258. }
  259. /**
  260. * Function to keep the HTTP server running.
  261. */
  262. static void
  263. mhd_main (void);
  264. static void
  265. mhd_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  266. {
  267. mhd_task_id = NULL;
  268. MHD_run (mhd);
  269. mhd_main ();
  270. }
  271. static void
  272. ctrl_c_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  273. {
  274. ctrl_c_task_id = NULL;
  275. do_shutdown ();
  276. GNUNET_break (0);
  277. global_ret = 1;
  278. }
  279. static void
  280. mhd_main ()
  281. {
  282. struct GNUNET_NETWORK_FDSet nrs;
  283. struct GNUNET_NETWORK_FDSet nws;
  284. fd_set rs;
  285. fd_set ws;
  286. fd_set es;
  287. int max_fd;
  288. unsigned MHD_LONG_LONG timeout;
  289. struct GNUNET_TIME_Relative delay;
  290. GNUNET_assert (NULL == mhd_task_id);
  291. FD_ZERO (&rs);
  292. FD_ZERO (&ws);
  293. FD_ZERO (&es);
  294. max_fd = -1;
  295. GNUNET_assert (MHD_YES == MHD_get_fdset (mhd, &rs, &ws, &es, &max_fd));
  296. if (MHD_YES == MHD_get_timeout (mhd, &timeout))
  297. delay =
  298. GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
  299. (unsigned int) timeout);
  300. else
  301. delay = GNUNET_TIME_UNIT_FOREVER_REL;
  302. GNUNET_NETWORK_fdset_copy_native (&nrs, &rs, max_fd + 1);
  303. GNUNET_NETWORK_fdset_copy_native (&nws, &ws, max_fd + 1);
  304. mhd_task_id =
  305. GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, delay,
  306. &nrs, &nws, &mhd_task, NULL);
  307. }
  308. static void
  309. run (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg,
  310. struct GNUNET_TESTING_Peer *peer)
  311. {
  312. struct in_addr v4;
  313. struct in6_addr v6;
  314. void *addr;
  315. enum MHD_FLAG flags;
  316. vpn = GNUNET_VPN_connect (cfg);
  317. GNUNET_assert (NULL != vpn);
  318. flags = MHD_USE_DEBUG;
  319. if (AF_INET6 == dest_af)
  320. flags |= MHD_USE_IPv6;
  321. mhd =
  322. MHD_start_daemon (flags, PORT, NULL, NULL, &mhd_ahc, NULL,
  323. MHD_OPTION_END);
  324. GNUNET_assert (NULL != mhd);
  325. mhd_main ();
  326. addr = NULL;
  327. switch (dest_af)
  328. {
  329. case AF_INET:
  330. GNUNET_assert (1 == inet_pton (dest_af, dest_ip, &v4));
  331. addr = &v4;
  332. break;
  333. case AF_INET6:
  334. GNUNET_assert (1 == inet_pton (dest_af, dest_ip, &v6));
  335. addr = &v6;
  336. break;
  337. default:
  338. GNUNET_assert (0);
  339. }
  340. rr = GNUNET_VPN_redirect_to_ip (vpn, src_af, dest_af, addr,
  341. GNUNET_TIME_UNIT_FOREVER_ABS, &allocation_cb,
  342. NULL);
  343. ctrl_c_task_id =
  344. GNUNET_SCHEDULER_add_delayed (TIMEOUT, &ctrl_c_shutdown, NULL);
  345. }
  346. int
  347. main (int argc, char *const *argv)
  348. {
  349. const char *type;
  350. const char *bin;
  351. char *vpn_binary;
  352. char *exit_binary;
  353. int ret=0;
  354. #ifndef MINGW
  355. if (0 != ACCESS ("/dev/net/tun", R_OK))
  356. {
  357. GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "access",
  358. "/dev/net/tun");
  359. fprintf (stderr,
  360. "WARNING: System unable to run test, skipping.\n");
  361. return 0;
  362. }
  363. #endif
  364. vpn_binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-vpn");
  365. exit_binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-exit");
  366. if ((GNUNET_YES != (ret = GNUNET_OS_check_helper_binary (vpn_binary, GNUNET_YES, "-d gnunet-vpn - - 169.1.3.3.7 255.255.255.0"))) || //ipv4 only please!
  367. (GNUNET_YES != (ret = GNUNET_OS_check_helper_binary (exit_binary, GNUNET_YES, "-d gnunet-vpn - - - 169.1.3.3.7 255.255.255.0")))) //no nat, ipv4 only
  368. {
  369. GNUNET_free (vpn_binary);
  370. GNUNET_free (exit_binary);
  371. fprintf (stderr,
  372. "WARNING: gnunet-helper-{exit,vpn} binaries are not SUID, refusing to run test (as it would have to fail). %d\n", ret);
  373. return 0;
  374. }
  375. GNUNET_free (vpn_binary);
  376. GNUNET_free (exit_binary);
  377. bin = argv[0];
  378. if (NULL != strstr (bin, "lt-"))
  379. bin = strstr (bin, "lt-") + 4;
  380. type = strstr (bin, "-");
  381. if (NULL == type)
  382. {
  383. fprintf (stderr,
  384. "invalid binary name\n");
  385. return 1;
  386. }
  387. type++;
  388. /* on Windows, .exe is suffixed to these binaries,
  389. * thus cease comparison after the 6th char.
  390. */
  391. if (0 == strncmp (type, "4_to_6",6))
  392. {
  393. dest_ip = "FC5A:04E1:C2BA::1";
  394. dest_af = AF_INET6;
  395. src_af = AF_INET;
  396. }
  397. else if (0 == strncmp (type, "6_to_4",6))
  398. {
  399. dest_ip = "169.254.86.1";
  400. dest_af = AF_INET;
  401. src_af = AF_INET6;
  402. }
  403. else if (0 == strncmp (type, "4_over",6))
  404. {
  405. dest_ip = "169.254.86.1";
  406. dest_af = AF_INET;
  407. src_af = AF_INET;
  408. }
  409. else if (0 == strncmp (type, "6_over",6))
  410. {
  411. dest_ip = "FC5A:04E1:C2BA::1";
  412. dest_af = AF_INET6;
  413. src_af = AF_INET6;
  414. }
  415. else
  416. {
  417. fprintf (stderr, "invalid binary suffix `%s'\n", type);
  418. return 1;
  419. }
  420. if ((GNUNET_OK != GNUNET_NETWORK_test_pf (src_af)) ||
  421. (GNUNET_OK != GNUNET_NETWORK_test_pf (dest_af)))
  422. {
  423. fprintf (stderr,
  424. "Required address families not supported by this system, skipping test.\n");
  425. return 0;
  426. }
  427. if (0 != curl_global_init (CURL_GLOBAL_WIN32))
  428. {
  429. fprintf (stderr, "failed to initialize curl\n");
  430. return 2;
  431. }
  432. if (0 !=
  433. GNUNET_TESTING_peer_run ("test-gnunet-vpn", "test_gnunet_vpn.conf", &run,
  434. NULL))
  435. return 1;
  436. GNUNET_DISK_directory_remove ("/tmp/gnunet-test-vpn");
  437. return global_ret;
  438. }
  439. /* end of test_gnunet_vpn.c */