aboutsummaryrefslogtreecommitdiff
path: root/src/vpn
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2012-01-24 10:18:13 +0000
committerChristian Grothoff <christian@grothoff.org>2012-01-24 10:18:13 +0000
commit77d74ef24d2f0bf071e74d4d8131909ac06c5404 (patch)
tree1feeca1ced41c8e7c6a2c7242569d88aa0134011 /src/vpn
parentd23a2ea6fbe415b0e56249d55fb2b178d76629f2 (diff)
downloadgnunet-77d74ef24d2f0bf071e74d4d8131909ac06c5404.tar.gz
gnunet-77d74ef24d2f0bf071e74d4d8131909ac06c5404.zip
-a first VPN testcase
Diffstat (limited to 'src/vpn')
-rw-r--r--src/vpn/Makefile.am28
-rw-r--r--src/vpn/test_gnunet_vpn.c438
-rw-r--r--src/vpn/test_gnunet_vpn.conf13
3 files changed, 468 insertions, 11 deletions
diff --git a/src/vpn/Makefile.am b/src/vpn/Makefile.am
index 786f94d6a..fea89724d 100644
--- a/src/vpn/Makefile.am
+++ b/src/vpn/Makefile.am
@@ -33,6 +33,17 @@ bin_PROGRAMS = \
33 $(VPNBIN) gnunet-service-vpn gnunet-vpn 33 $(VPNBIN) gnunet-service-vpn gnunet-vpn
34 34
35 35
36if HAVE_MHD
37 VPN_TEST = test_gnunet_vpn
38endif
39
40check_PROGRAMS = $(VPN_TEST)
41
42if ENABLE_TEST_RUN
43TESTS = $(check_PROGRAMS)
44endif
45
46
36gnunet_helper_vpn_SOURCES = \ 47gnunet_helper_vpn_SOURCES = \
37 gnunet-helper-vpn.c 48 gnunet-helper-vpn.c
38 49
@@ -64,14 +75,9 @@ libgnunetvpn_la_LDFLAGS = \
64 $(GN_LIB_LDFLAGS) 75 $(GN_LIB_LDFLAGS)
65 76
66 77
67if ENABLE_TEST_RUN 78test_gnunet_vpn_SOURCES = \
68#TESTS = \ 79 test_gnunet_vpn.c
69# test_transport_api_tcp 80test_gnunet_vpn_LDADD = -lmicrohttpd @LIBCURL@ \
70endif 81 $(top_builddir)/src/vpn/libgnunetvpn.la \
71 82 $(top_builddir)/src/arm/libgnunetarm.la \
72#test_transport_api_tcp_SOURCES = \ 83 $(top_builddir)/src/util/libgnunetutil.la
73# test_transport_api.c
74#test_transport_api_tcp_LDADD = \
75# $(top_builddir)/src/transport/libgnunettransport.la \
76# $(top_builddir)/src/util/libgnunetutil.la
77
diff --git a/src/vpn/test_gnunet_vpn.c b/src/vpn/test_gnunet_vpn.c
new file mode 100644
index 000000000..8cdd888b9
--- /dev/null
+++ b/src/vpn/test_gnunet_vpn.c
@@ -0,0 +1,438 @@
1/*
2 This file is part of GNUnet
3 (C) 2007, 2009, 2011, 2012 Christian Grothoff
4
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.
9
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.
14
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.
19*/
20
21/**
22 * @file test_gnunet_vpn.c
23 * @brief testcase for tunneling HTTP over the GNUnet VPN
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include <curl/curl.h>
28#include <microhttpd.h>
29#include "gnunet_vpn_service.h"
30#include "gnunet_arm_service.h"
31
32#define PORT 48080
33
34struct PeerContext
35{
36 struct GNUNET_CONFIGURATION_Handle *cfg;
37 struct GNUNET_PeerIdentity id;
38#if START_ARM
39 struct GNUNET_OS_Process *arm_proc;
40#endif
41};
42
43static struct PeerContext p1;
44
45/**
46 * Return value for 'main'.
47 */
48static int global_ret;
49
50static struct GNUNET_VPN_Handle *vpn;
51
52static struct MHD_Daemon *mhd;
53
54static GNUNET_SCHEDULER_TaskIdentifier mhd_task_id;
55
56static GNUNET_SCHEDULER_TaskIdentifier curl_task_id;
57
58static GNUNET_SCHEDULER_TaskIdentifier ctrl_c_task_id;
59
60static struct GNUNET_VPN_RedirectionRequest *rr;
61
62static CURL *curl;
63
64static CURLM *multi;
65
66static char *url;
67
68struct CBC
69{
70 char *buf;
71 size_t pos;
72 size_t size;
73};
74
75static struct CBC cbc;
76
77
78
79static size_t
80copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
81{
82 struct CBC *cbc = ctx;
83
84 if (cbc->pos + size * nmemb > cbc->size)
85 return 0; /* overflow */
86 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
87 cbc->pos += size * nmemb;
88 return size * nmemb;
89}
90
91
92static int
93mhd_ahc (void *cls,
94 struct MHD_Connection *connection,
95 const char *url,
96 const char *method,
97 const char *version,
98 const char *upload_data, size_t *upload_data_size,
99 void **unused)
100{
101 static int ptr;
102 struct MHD_Response *response;
103 int ret;
104
105 if (0 != strcmp ("GET", method))
106 return MHD_NO; /* unexpected method */
107 if (&ptr != *unused)
108 {
109 *unused = &ptr;
110 return MHD_YES;
111 }
112 *unused = NULL;
113 response = MHD_create_response_from_buffer (strlen (url),
114 (void *) url,
115 MHD_RESPMEM_MUST_COPY);
116 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
117 MHD_destroy_response (response);
118 if (ret == MHD_NO)
119 abort ();
120 return ret;
121}
122
123
124static void
125do_shutdown ()
126{
127 GNUNET_SCHEDULER_cancel (mhd_task_id);
128 mhd_task_id = GNUNET_SCHEDULER_NO_TASK;
129 MHD_stop_daemon (mhd);
130 if (NULL != rr)
131 {
132 GNUNET_VPN_cancel_request (rr);
133 rr = NULL;
134 }
135 GNUNET_VPN_disconnect (vpn);
136 vpn = NULL;
137 GNUNET_free_non_null (url);
138 url = NULL;
139}
140
141
142/**
143 * Function to run the HTTP client.
144 */
145static void
146curl_main (void);
147
148
149static void
150curl_task (void *cls,
151 const struct GNUNET_SCHEDULER_TaskContext *tc)
152{
153 curl_task_id = GNUNET_SCHEDULER_NO_TASK;
154
155}
156
157
158static void
159curl_main ()
160{
161 fd_set rs;
162 fd_set ws;
163 fd_set es;
164 int max;
165 struct GNUNET_NETWORK_FDSet nrs;
166 struct GNUNET_NETWORK_FDSet nws;
167 struct GNUNET_TIME_Relative delay;
168 long timeout;
169 int running;
170 struct CURLMsg *msg;
171
172 max = 0;
173 FD_ZERO (&rs);
174 FD_ZERO (&ws);
175 FD_ZERO (&es);
176 curl_multi_perform (multi, &running);
177 if (running == 0)
178 {
179 GNUNET_assert (NULL != (msg = curl_multi_info_read (multi, &running)));
180 if (msg->msg == CURLMSG_DONE)
181 {
182 if (msg->data.result != CURLE_OK)
183 printf ("%s failed at %s:%d: `%s'\n",
184 "curl_multi_perform",
185 __FILE__,
186 __LINE__, curl_easy_strerror (msg->data.result));
187 global_ret = 1;
188 }
189 curl_multi_remove_handle (multi, curl);
190 curl_multi_cleanup (multi);
191 curl_easy_cleanup (curl);
192 curl = NULL;
193 multi = NULL;
194 if (cbc.pos != strlen ("/hello_world"))
195 global_ret = 2;
196 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
197 global_ret = 3;
198 do_shutdown ();
199 return;
200 }
201 GNUNET_assert (CURLM_OK == curl_multi_fdset (multi, &rs, &ws, &es, &max));
202
203 if ( (CURLM_OK != curl_multi_timeout (multi, &timeout)) ||
204 (-1 == timeout) )
205 delay = GNUNET_TIME_UNIT_FOREVER_REL;
206 else
207 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, (unsigned int) timeout);
208 GNUNET_NETWORK_fdset_copy_native (&nrs,
209 &rs,
210 max);
211 GNUNET_NETWORK_fdset_copy_native (&nws,
212 &ws,
213 max);
214 curl_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
215 GNUNET_SCHEDULER_NO_TASK,
216 delay,
217 &nrs,
218 &nws,
219 &curl_task,
220 NULL);
221}
222
223
224/**
225 * Callback invoked from the VPN service once a redirection is
226 * available. Provides the IP address that can now be used to
227 * reach the requested destination (in our case, the MHD server)
228 *
229 * @param cls closure
230 * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
231 * will match 'result_af' from the request
232 * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
233 * that the VPN allocated for the redirection;
234 * traffic to this IP will now be redirected to the
235 * specified target peer; NULL on error
236 */
237static void
238allocation_cb (void *cls,
239 int af,
240 const void *address)
241{
242 char ips[INET_ADDRSTRLEN];
243
244 rr = NULL;
245 GNUNET_assert (AF_INET == af);
246 GNUNET_asprintf (&url,
247 "http://%s:%u/hello_world",
248 inet_ntop (af, address, ips, sizeof (ips)),
249 (unsigned int) PORT);
250 curl = curl_easy_init ();
251 curl_easy_setopt (curl, CURLOPT_URL, "http://127.0.0.1:1082/hello_world");
252 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, &copyBuffer);
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
259 multi = curl_multi_init ();
260 GNUNET_assert (multi != NULL);
261 GNUNET_assert (CURLM_OK == curl_multi_add_handle (multi, curl));
262 curl_main ();
263}
264
265
266/**
267 * Function to keep the HTTP server running.
268 */
269static void
270mhd_main (void);
271
272
273static void
274mhd_task (void *cls,
275 const struct GNUNET_SCHEDULER_TaskContext *tc)
276{
277 mhd_task_id = GNUNET_SCHEDULER_NO_TASK;
278 MHD_run (mhd);
279 mhd_main ();
280}
281
282
283static void
284ctrl_c_shutdown (void *cls,
285 const struct GNUNET_SCHEDULER_TaskContext *tc)
286{
287 ctrl_c_task_id = GNUNET_SCHEDULER_NO_TASK;
288 do_shutdown ();
289 global_ret = 1;
290}
291
292
293static void
294mhd_main ()
295{
296 struct GNUNET_NETWORK_FDSet nrs;
297 struct GNUNET_NETWORK_FDSet nws;
298 fd_set rs;
299 fd_set ws;
300 int max_fd;
301 unsigned MHD_LONG_LONG timeout;
302 struct GNUNET_TIME_Relative delay;
303
304 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == mhd_task_id);
305 FD_ZERO (&rs);
306 FD_ZERO (&ws);
307 max_fd = -1;
308 GNUNET_assert (MHD_YES ==
309 MHD_get_fdset (mhd, &rs, &ws, NULL, &max_fd));
310 if (MHD_YES == MHD_get_timeout (mhd, &timeout))
311 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
312 (unsigned int) timeout);
313 else
314 delay = GNUNET_TIME_UNIT_FOREVER_REL;
315 GNUNET_NETWORK_fdset_copy_native (&nrs,
316 &rs,
317 max_fd);
318 GNUNET_NETWORK_fdset_copy_native (&nws,
319 &ws,
320 max_fd);
321 mhd_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
322 GNUNET_SCHEDULER_NO_TASK,
323 delay,
324 &nrs,
325 &nws,
326 &mhd_task,
327 NULL);
328 ctrl_c_task_id = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
329 &ctrl_c_shutdown,
330 NULL);
331}
332
333
334static void
335run (void *cls, char *const *args, const char *cfgfile,
336 const struct GNUNET_CONFIGURATION_Handle *cfg)
337{
338 struct sockaddr_in v4;
339
340 vpn = GNUNET_VPN_connect (cfg);
341 GNUNET_assert (NULL != vpn);
342 v4.sin_family = AF_INET;
343 v4.sin_port = htons (PORT);
344 GNUNET_assert (1 == inet_pton (AF_INET, "127.0.0.1", &v4.sin_addr));
345 mhd = MHD_start_daemon (MHD_USE_DEBUG,
346 PORT,
347 NULL, NULL,
348 &mhd_ahc, NULL,
349 MHD_OPTION_SOCK_ADDR, &v4,
350 MHD_OPTION_END);
351 mhd_main ();
352 rr = GNUNET_VPN_redirect_to_ip (vpn,
353 AF_INET,
354 AF_INET,
355 &v4,
356 GNUNET_YES,
357 GNUNET_TIME_UNIT_FOREVER_ABS,
358 &allocation_cb, NULL);
359}
360
361
362static void
363setup_peer (struct PeerContext *p, const char *cfgname)
364{
365 p->cfg = GNUNET_CONFIGURATION_create ();
366#if START_ARM
367 p->arm_proc =
368 GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
369 "gnunet-service-arm",
370#if VERBOSE
371 "-L", "DEBUG",
372#endif
373 "-c", cfgname, NULL);
374#endif
375 GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
376}
377
378
379static void
380stop_peer (struct PeerContext *p)
381{
382#if START_ARM
383 if (NULL != p->arm_proc)
384 {
385 if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM))
386 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
387 if (GNUNET_OS_process_wait (p->arm_proc) != GNUNET_OK)
388 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
389 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM process %u stopped\n",
390 GNUNET_OS_process_get_pid (p->arm_proc));
391 GNUNET_OS_process_close (p->arm_proc);
392 p->arm_proc = NULL;
393 }
394#endif
395 GNUNET_CONFIGURATION_destroy (p->cfg);
396}
397
398
399int
400main (int argc, char *const *argv)
401{
402 char *const argvx[] = {
403 "test_gnunet_vpn",
404 "-c",
405 "test_gnunet_vpn.conf",
406#if VERBOSE
407 "-L", "DEBUG",
408#endif
409 NULL
410 };
411 struct GNUNET_GETOPT_CommandLineOption options[] = {
412 GNUNET_GETOPT_OPTION_END
413 };
414
415 /* not technically true -- proper SUID and a chdir here would
416 likely do -- but this will avoid getting (useless) reports
417 from users where the test fails due to insufficient
418 permissions */
419 if (0 != getuid ())
420 {
421 fprintf (stderr, "This testcase can only be run as root\n");
422 return 0;
423 }
424 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
425 return 2;
426 setup_peer (&p1, "test_gnunet_vpn.conf");
427 GNUNET_log_setup ("test_gnunet_vpn",
428#if VERBOSE
429 "DEBUG",
430#else
431 "WARNING",
432#endif
433 NULL);
434 GNUNET_PROGRAM_run ((sizeof (argvx) / sizeof (char *)) - 1, argvx,
435 "test_gnunet_vpn", "nohelp", options, &run, NULL);
436 stop_peer (&p1);
437 return global_ret;
438}
diff --git a/src/vpn/test_gnunet_vpn.conf b/src/vpn/test_gnunet_vpn.conf
new file mode 100644
index 000000000..e4eaffa9b
--- /dev/null
+++ b/src/vpn/test_gnunet_vpn.conf
@@ -0,0 +1,13 @@
1[arm]
2DEFAULTSERVICES = statistics exit
3PORT = 0
4ALLOW_SHUTDOWN = YES
5
6[exit]
7EXIT_IPV4 = YES
8EXIT_IPV6 = YES
9
10# FIXME: with this, the test will fail if the
11# interface does not exist; can we use 'lo'?
12EXIT_IFNAME = eth1
13