2 This file is part of GNUnet
3 (C) 2007, 2009, 2011, 2012 Christian Grothoff
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file test_gnunet_vpn.c
23 * @brief testcase for tunneling HTTP over the GNUnet VPN
24 * @author Christian Grothoff
27 #include <curl/curl.h>
28 #include <microhttpd.h>
29 #include "gnunet_vpn_service.h"
30 #include "gnunet_arm_service.h"
34 #define START_ARM GNUNET_YES
36 #define VERBOSE GNUNET_NO
40 struct GNUNET_CONFIGURATION_Handle *cfg;
41 struct GNUNET_PeerIdentity id;
43 struct GNUNET_OS_Process *arm_proc;
47 static struct PeerContext p1;
50 * Return value for 'main'.
52 static int global_ret;
54 static struct GNUNET_VPN_Handle *vpn;
56 static struct MHD_Daemon *mhd;
58 static GNUNET_SCHEDULER_TaskIdentifier mhd_task_id;
60 static GNUNET_SCHEDULER_TaskIdentifier curl_task_id;
62 static GNUNET_SCHEDULER_TaskIdentifier ctrl_c_task_id;
64 static struct GNUNET_VPN_RedirectionRequest *rr;
79 static struct CBC cbc;
84 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
86 struct CBC *cbc = ctx;
88 if (cbc->pos + size * nmemb > cbc->size)
89 return 0; /* overflow */
90 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
91 cbc->pos += size * nmemb;
98 struct MHD_Connection *connection,
102 const char *upload_data, size_t *upload_data_size,
106 struct MHD_Response *response;
109 if (0 != strcmp ("GET", method))
110 return MHD_NO; /* unexpected method */
117 response = MHD_create_response_from_buffer (strlen (url),
119 MHD_RESPMEM_MUST_COPY);
120 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
121 MHD_destroy_response (response);
131 if (mhd_task_id != GNUNET_SCHEDULER_NO_TASK)
133 GNUNET_SCHEDULER_cancel (mhd_task_id);
134 mhd_task_id = GNUNET_SCHEDULER_NO_TASK;
138 MHD_stop_daemon (mhd);
143 GNUNET_VPN_cancel_request (rr);
148 GNUNET_VPN_disconnect (vpn);
151 GNUNET_free_non_null (url);
157 * Function to run the HTTP client.
164 curl_task (void *cls,
165 const struct GNUNET_SCHEDULER_TaskContext *tc)
167 curl_task_id = GNUNET_SCHEDULER_NO_TASK;
179 struct GNUNET_NETWORK_FDSet nrs;
180 struct GNUNET_NETWORK_FDSet nws;
181 struct GNUNET_TIME_Relative delay;
190 curl_multi_perform (multi, &running);
193 GNUNET_assert (NULL != (msg = curl_multi_info_read (multi, &running)));
194 if (msg->msg == CURLMSG_DONE)
196 if (msg->data.result != CURLE_OK)
197 printf ("%s failed at %s:%d: `%s'\n",
198 "curl_multi_perform",
200 __LINE__, curl_easy_strerror (msg->data.result));
203 curl_multi_remove_handle (multi, curl);
204 curl_multi_cleanup (multi);
205 curl_easy_cleanup (curl);
208 if (cbc.pos != strlen ("/hello_world"))
210 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
215 GNUNET_assert (CURLM_OK == curl_multi_fdset (multi, &rs, &ws, &es, &max));
217 if ( (CURLM_OK != curl_multi_timeout (multi, &timeout)) ||
219 delay = GNUNET_TIME_UNIT_FOREVER_REL;
221 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, (unsigned int) timeout);
222 GNUNET_NETWORK_fdset_copy_native (&nrs,
225 GNUNET_NETWORK_fdset_copy_native (&nws,
228 curl_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
229 GNUNET_SCHEDULER_NO_TASK,
239 * Callback invoked from the VPN service once a redirection is
240 * available. Provides the IP address that can now be used to
241 * reach the requested destination (in our case, the MHD server)
244 * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
245 * will match 'result_af' from the request
246 * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
247 * that the VPN allocated for the redirection;
248 * traffic to this IP will now be redirected to the
249 * specified target peer; NULL on error
252 allocation_cb (void *cls,
256 char ips[INET_ADDRSTRLEN];
262 "VPN failed to allocate appropriate address\n");
263 GNUNET_SCHEDULER_shutdown ();
266 GNUNET_asprintf (&url,
267 "http://%s:%u/hello_world",
268 inet_ntop (af, address, ips, sizeof (ips)),
269 (unsigned int) PORT);
270 curl = curl_easy_init ();
271 curl_easy_setopt (curl, CURLOPT_URL, url);
272 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, ©Buffer);
273 curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbc);
274 curl_easy_setopt (curl, CURLOPT_FAILONERROR, 1);
275 curl_easy_setopt (curl, CURLOPT_TIMEOUT, 150L);
276 curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 15L);
277 curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1);
279 multi = curl_multi_init ();
280 GNUNET_assert (multi != NULL);
281 GNUNET_assert (CURLM_OK == curl_multi_add_handle (multi, curl));
283 fprintf (stderr, "Beginning HTTP download from `%s'\n", url);
289 * Function to keep the HTTP server running.
297 const struct GNUNET_SCHEDULER_TaskContext *tc)
299 mhd_task_id = GNUNET_SCHEDULER_NO_TASK;
306 ctrl_c_shutdown (void *cls,
307 const struct GNUNET_SCHEDULER_TaskContext *tc)
309 ctrl_c_task_id = GNUNET_SCHEDULER_NO_TASK;
318 struct GNUNET_NETWORK_FDSet nrs;
319 struct GNUNET_NETWORK_FDSet nws;
324 unsigned MHD_LONG_LONG timeout;
325 struct GNUNET_TIME_Relative delay;
327 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == mhd_task_id);
332 GNUNET_assert (MHD_YES ==
333 MHD_get_fdset (mhd, &rs, &ws, &es, &max_fd));
334 if (MHD_YES == MHD_get_timeout (mhd, &timeout))
335 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
336 (unsigned int) timeout);
338 delay = GNUNET_TIME_UNIT_FOREVER_REL;
339 GNUNET_NETWORK_fdset_copy_native (&nrs,
342 GNUNET_NETWORK_fdset_copy_native (&nws,
345 mhd_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
346 GNUNET_SCHEDULER_NO_TASK,
356 run (void *cls, char *const *args, const char *cfgfile,
357 const struct GNUNET_CONFIGURATION_Handle *cfg)
359 struct sockaddr_in v4;
361 vpn = GNUNET_VPN_connect (cfg);
362 GNUNET_assert (NULL != vpn);
363 v4.sin_family = AF_INET;
364 v4.sin_port = htons (PORT);
365 GNUNET_assert (1 == inet_pton (AF_INET, "127.0.0.1", &v4.sin_addr));
366 mhd = MHD_start_daemon (MHD_USE_DEBUG,
370 MHD_OPTION_SOCK_ADDR, &v4,
372 GNUNET_assert (NULL != mhd);
374 rr = GNUNET_VPN_redirect_to_ip (vpn,
379 GNUNET_TIME_UNIT_FOREVER_ABS,
380 &allocation_cb, NULL);
381 ctrl_c_task_id = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
388 setup_peer (struct PeerContext *p, const char *cfgname)
390 p->cfg = GNUNET_CONFIGURATION_create ();
393 GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
394 "gnunet-service-arm",
398 "-c", cfgname, NULL);
400 GNUNET_assert (NULL != p->arm_proc);
401 GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
406 stop_peer (struct PeerContext *p)
409 if (NULL != p->arm_proc)
411 if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM))
412 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
413 if (GNUNET_OS_process_wait (p->arm_proc) != GNUNET_OK)
414 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
415 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM process %u stopped\n",
416 GNUNET_OS_process_get_pid (p->arm_proc));
417 GNUNET_OS_process_close (p->arm_proc);
421 GNUNET_CONFIGURATION_destroy (p->cfg);
426 main (int argc, char *const *argv)
428 char *const argvx[] = {
431 "test_gnunet_vpn.conf",
437 struct GNUNET_GETOPT_CommandLineOption options[] = {
438 GNUNET_GETOPT_OPTION_END
441 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
443 setup_peer (&p1, "test_gnunet_vpn.conf");
444 GNUNET_log_setup ("test_gnunet_vpn",
451 GNUNET_PROGRAM_run ((sizeof (argvx) / sizeof (char *)) - 1, argvx,
452 "test_gnunet_vpn", "nohelp", options, &run, NULL);
454 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-vpn");