test_gnunet_vpn.c 13 KB

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