Make gnurl and curl mutually exclusive.
[oweals/gnunet.git] / src / pt / test_gns_vpn.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2007, 2009, 2011, 2012, 2015, 2017 Christian Grothoff
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21 /**
22  * @file test_gns_vpn.c
23  * @brief testcase for accessing VPN services via GNS
24  * @author Martin Schanzenbach
25  * @author Christian Grothoff
26  *
27  * This test requires libcurl/libgnurl *with* support for C-ARES.
28  * This is NOT the default on most platforms, which means the test
29  * will be skipped in many cases.   Compile libcurl/libgnurl with
30  * "--enable-ares" to get this test to pass.
31  *
32  * Furthermore, the test relies on gnunet-dns2gns being able to bind
33  * to port 53.  This means that 'setcap' has to have worked during
34  * 'make install'.  If this failed, but everything else is OK, the
35  * test may FAIL hard even though it is just an installation issue (we
36  * cannot conveniently test for the setcap to have worked).  However,
37  * you should get a warning that gnunet-dns2gns failed to 'bind'.
38  */
39 #include "platform.h"
40 /* Just included for the right curl.h */
41 #include "gnunet_curl_lib.h"
42 #include <microhttpd.h>
43 #include "gnunet_identity_service.h"
44 #include "gnunet_namestore_service.h"
45 #include "gnunet_gnsrecord_lib.h"
46 #include "gnunet_gns_service.h"
47 #include "gnunet_testing_lib.h"
48
49 #define PORT 8080
50 #define TEST_DOMAIN "www.gnu"
51
52 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
53
54 /**
55  * Return value for #main().
56  */
57 static int global_ret;
58
59 static struct GNUNET_NAMESTORE_Handle *namestore;
60
61 static struct MHD_Daemon *mhd;
62
63 static struct GNUNET_SCHEDULER_Task *mhd_task_id;
64
65 static struct GNUNET_SCHEDULER_Task *curl_task_id;
66
67 static struct GNUNET_SCHEDULER_Task *timeout_task;
68
69 static struct GNUNET_IDENTITY_Handle *identity;
70
71 static struct GNUNET_NAMESTORE_QueueEntry *qe;
72
73 static CURL *curl;
74
75 static CURLM *multi;
76
77 static char *url;
78
79 static struct GNUNET_PeerIdentity id;
80
81 /**
82  * IP address of the ultimate destination.
83  */
84 static const char *dest_ip;
85
86 /**
87  * Address family of the dest_ip.
88  */
89 static int dest_af;
90
91 /**
92  * Address family to use by the curl client.
93  */
94 static int src_af;
95
96 static int use_v6;
97
98
99 struct CBC
100 {
101   char buf[1024];
102   size_t pos;
103 };
104
105 static struct CBC cbc;
106
107
108 static size_t
109 copy_buffer (void *ptr,
110              size_t size,
111              size_t nmemb,
112              void *ctx)
113 {
114   struct CBC *cbc = ctx;
115
116   if (cbc->pos + size * nmemb > sizeof(cbc->buf))
117     return 0;                   /* overflow */
118   GNUNET_memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
119   cbc->pos += size * nmemb;
120   return size * nmemb;
121 }
122
123
124 static int
125 mhd_ahc (void *cls,
126           struct MHD_Connection *connection,
127           const char *url,
128           const char *method,
129           const char *version,
130           const char *upload_data, size_t *upload_data_size,
131           void **unused)
132 {
133   static int ptr;
134   struct MHD_Response *response;
135   int ret;
136
137   if (0 != strcmp ("GET", method))
138     return MHD_NO;              /* unexpected method */
139   if (&ptr != *unused)
140   {
141     *unused = &ptr;
142     return MHD_YES;
143   }
144   *unused = NULL;
145   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MHD sends respose for request to URL `%s'\n", url);
146   response = MHD_create_response_from_buffer (strlen (url),
147                                               (void *) url,
148                                               MHD_RESPMEM_MUST_COPY);
149   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
150   MHD_destroy_response (response);
151   if (ret == MHD_NO)
152     abort ();
153   return ret;
154 }
155
156
157 static void
158 do_shutdown (void *cls)
159 {
160   if (NULL != mhd_task_id)
161   {
162     GNUNET_SCHEDULER_cancel (mhd_task_id);
163     mhd_task_id = NULL;
164   }
165   if (NULL != curl_task_id)
166   {
167     GNUNET_SCHEDULER_cancel (curl_task_id);
168     curl_task_id = NULL;
169   }
170   if (NULL != timeout_task)
171   {
172     GNUNET_SCHEDULER_cancel (timeout_task);
173     timeout_task = NULL;
174   }
175   if (NULL != mhd)
176   {
177     MHD_stop_daemon (mhd);
178     mhd = NULL;
179   }
180   if (NULL != identity)
181   {
182     GNUNET_IDENTITY_disconnect (identity);
183     identity = NULL;
184   }
185   if (NULL != qe)
186   {
187     GNUNET_NAMESTORE_cancel (qe);
188     qe = NULL;
189   }
190   if (NULL != namestore)
191   {
192     GNUNET_NAMESTORE_disconnect (namestore);
193     namestore = NULL;
194   }
195   GNUNET_free_non_null (url);
196   url = NULL;
197 }
198
199
200 static void
201 do_timeout (void *cls)
202 {
203   timeout_task = NULL;
204   GNUNET_SCHEDULER_shutdown ();
205 }
206
207
208 /**
209  * Function to run the HTTP client.
210  */
211 static void
212 curl_main (void);
213
214
215 static void
216 curl_task (void *cls)
217 {
218   curl_task_id = NULL;
219   curl_main ();
220 }
221
222
223 static void
224 curl_main ()
225 {
226   fd_set rs;
227   fd_set ws;
228   fd_set es;
229   int max;
230   struct GNUNET_NETWORK_FDSet nrs;
231   struct GNUNET_NETWORK_FDSet nws;
232   struct GNUNET_TIME_Relative delay;
233   long timeout;
234   int running;
235   struct CURLMsg *msg;
236
237   max = 0;
238   FD_ZERO (&rs);
239   FD_ZERO (&ws);
240   FD_ZERO (&es);
241   curl_multi_perform (multi, &running);
242   if (running == 0)
243   {
244     GNUNET_assert (NULL != (msg = curl_multi_info_read (multi, &running)));
245     if (msg->msg == CURLMSG_DONE)
246     {
247       if (msg->data.result != CURLE_OK)
248       {
249         fprintf (stderr,
250                  "%s failed at %s:%d: `%s'\n",
251                  "curl_multi_perform",
252                 __FILE__,
253                 __LINE__, curl_easy_strerror (msg->data.result));
254         global_ret = 1;
255       }
256     }
257     curl_multi_remove_handle (multi, curl);
258     curl_multi_cleanup (multi);
259     curl_easy_cleanup (curl);
260     curl = NULL;
261     multi = NULL;
262     if (cbc.pos != strlen ("/hello_world"))
263     {
264       GNUNET_break (0);
265       global_ret = 2;
266     }
267     if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
268     {
269       GNUNET_break (0);
270       global_ret = 3;
271     }
272     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
273                 "Download complete, shutting down!\n");
274     GNUNET_SCHEDULER_shutdown ();
275     return;
276   }
277   GNUNET_assert (CURLM_OK == curl_multi_fdset (multi, &rs, &ws, &es, &max));
278   if ( (CURLM_OK != curl_multi_timeout (multi, &timeout)) ||
279        (-1 == timeout) )
280     delay = GNUNET_TIME_UNIT_SECONDS;
281   else
282     delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, (unsigned int) timeout);
283   GNUNET_NETWORK_fdset_copy_native (&nrs,
284                                     &rs,
285                                     max + 1);
286   GNUNET_NETWORK_fdset_copy_native (&nws,
287                                     &ws,
288                                     max + 1);
289   curl_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
290                                               delay,
291                                               &nrs,
292                                               &nws,
293                                               &curl_task,
294                                               NULL);
295 }
296
297
298 static void
299 start_curl (void *cls)
300 {
301   CURLcode ec;
302
303   curl_task_id = NULL;
304   GNUNET_asprintf (&url,
305                    "http://%s/hello_world",
306                    TEST_DOMAIN);
307   curl = curl_easy_init ();
308   curl_easy_setopt (curl, CURLOPT_URL, url);
309   curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, &copy_buffer);
310   curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbc);
311   curl_easy_setopt (curl, CURLOPT_FAILONERROR, 1);
312   curl_easy_setopt (curl, CURLOPT_TIMEOUT, 150L);
313   curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 150L);
314   curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1);
315   if (CURLE_OK !=
316       (ec = curl_easy_setopt (curl,
317                               CURLOPT_DNS_SERVERS,
318                               "127.0.0.1:53")))
319   {
320     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
321                 "curl build without support for CURLOPT_DNS_SERVERS (%s), cannot run test\n",
322                 curl_easy_strerror (ec));
323     global_ret = 77;
324     GNUNET_SCHEDULER_shutdown ();
325     return;
326   }
327   multi = curl_multi_init ();
328   GNUNET_assert (multi != NULL);
329   GNUNET_assert (CURLM_OK == curl_multi_add_handle (multi, curl));
330   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
331               "Beginning HTTP download from `%s'\n",
332               url);
333   curl_main ();
334 }
335
336
337 /**
338  * Callback invoked from the namestore service once record is
339  * created.
340  *
341  * @param cls closure
342  * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
343  *                will match 'result_af' from the request
344  * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
345  *                that the VPN allocated for the redirection;
346  *                traffic to this IP will now be redirected to the
347  *                specified target peer; NULL on error
348  */
349 static void
350 commence_testing (void *cls,
351                   int32_t success,
352                   const char *emsg)
353 {
354   qe = NULL;
355   if ( (NULL != emsg) &&
356        (GNUNET_YES != success) )
357   {
358     fprintf (stderr,
359              "NS failed to create record %s\n",
360              emsg);
361     GNUNET_SCHEDULER_shutdown ();
362     return;
363   }
364
365   /* wait a little bit before downloading, as we just created the record */
366   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
367               "Launching cURL request\n");
368   curl_task_id
369     = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
370                                     &start_curl,
371                                     NULL);
372 }
373
374
375 /**
376  * Function to keep the HTTP server running.
377  */
378 static void
379 mhd_main (void);
380
381
382 static void
383 mhd_task (void *cls)
384 {
385   mhd_task_id = NULL;
386   MHD_run (mhd);
387   mhd_main ();
388 }
389
390
391 static void
392 mhd_main ()
393 {
394   struct GNUNET_NETWORK_FDSet nrs;
395   struct GNUNET_NETWORK_FDSet nws;
396   fd_set rs;
397   fd_set ws;
398   fd_set es;
399   int max_fd;
400   unsigned MHD_LONG_LONG timeout;
401   struct GNUNET_TIME_Relative delay;
402
403   GNUNET_assert (NULL == mhd_task_id);
404   FD_ZERO (&rs);
405   FD_ZERO (&ws);
406   FD_ZERO (&es);
407   max_fd = -1;
408   GNUNET_assert (MHD_YES ==
409                  MHD_get_fdset (mhd, &rs, &ws, &es, &max_fd));
410   if (MHD_YES == MHD_get_timeout (mhd, &timeout))
411     delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
412                                            (unsigned int) timeout);
413   else
414     delay = GNUNET_TIME_UNIT_FOREVER_REL;
415   GNUNET_NETWORK_fdset_copy_native (&nrs,
416                                     &rs,
417                                     max_fd + 1);
418   GNUNET_NETWORK_fdset_copy_native (&nws,
419                                     &ws,
420                                     max_fd + 1);
421   mhd_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
422                                              delay,
423                                              &nrs,
424                                              &nws,
425                                              &mhd_task,
426                                              NULL);
427 }
428
429
430
431 /**
432  * Open '/dev/null' and make the result the given
433  * file descriptor.
434  *
435  * @param target_fd desired FD to point to /dev/null
436  * @param flags open flags (O_RDONLY, O_WRONLY)
437  */
438 static void
439 open_dev_null (int target_fd,
440                int flags)
441 {
442   int fd;
443
444   fd = open ("/dev/null", flags);
445   if (-1 == fd)
446     abort ();
447   if (fd == target_fd)
448     return;
449   if (-1 == dup2 (fd, target_fd))
450   {
451     (void) close (fd);
452     abort ();
453   }
454   (void) close (fd);
455 }
456
457
458 /**
459  * Run the given command and wait for it to complete.
460  *
461  * @param file name of the binary to run
462  * @param cmd command line arguments (as given to 'execv')
463  * @return 0 on success, 1 on any error
464  */
465 static int
466 fork_and_exec (const char *file,
467                char *const cmd[])
468 {
469   int status;
470   pid_t pid;
471   pid_t ret;
472
473   pid = fork ();
474   if (-1 == pid)
475   {
476     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
477                          "fork");
478     return 1;
479   }
480   if (0 == pid)
481   {
482     /* we are the child process */
483     /* close stdin/stdout to not cause interference
484        with the helper's main protocol! */
485     (void) close (0);
486     open_dev_null (0, O_RDONLY);
487     (void) close (1);
488     open_dev_null (1, O_WRONLY);
489     (void) execv (file, cmd);
490     /* can only get here on error */
491     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
492                               "exec",
493                               file);
494     _exit (1);
495   }
496   /* keep running waitpid as long as the only error we get is 'EINTR' */
497   while ( (-1 == (ret = waitpid (pid, &status, 0))) &&
498           (errno == EINTR) );
499   if (-1 == ret)
500   {
501     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
502                          "waitpid");
503     return 1;
504   }
505   if (! (WIFEXITED (status) &&
506          (0 == WEXITSTATUS (status))) )
507   {
508     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
509                 "Process `%s` returned status code %d/%d.\n",
510                 file,
511                 WIFEXITED (status),
512                 WEXITSTATUS (status));
513     return 1;
514   }
515   /* child process completed and returned success, we're happy */
516   return 0;
517 }
518
519
520
521 /**
522  * Method called to inform about the egos of this peer.
523  *
524  * When used with #GNUNET_IDENTITY_connect, this function is
525  * initially called for all egos and then again whenever a
526  * ego's name changes or if it is deleted.  At the end of
527  * the initial pass over all egos, the function is once called
528  * with 'NULL' for @a ego. That does NOT mean that the callback won't
529  * be invoked in the future or that there was an error.
530  *
531  * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
532  * this function is only called ONCE, and 'NULL' being passed in
533  * @a ego does indicate an error (i.e. name is taken or no default
534  * value is known).  If @a ego is non-NULL and if '*ctx'
535  * is set in those callbacks, the value WILL be passed to a subsequent
536  * call to the identity callback of #GNUNET_IDENTITY_connect (if
537  * that one was not NULL).
538  *
539  * When an identity is renamed, this function is called with the
540  * (known) @a ego but the NEW @a name.
541  *
542  * When an identity is deleted, this function is called with the
543  * (known) ego and "NULL" for the @a name.  In this case,
544  * the @a ego is henceforth invalid (and the @a ctx should also be
545  * cleaned up).
546  *
547  * @param cls closure
548  * @param ego ego handle
549  * @param ctx context for application to store data for this ego
550  *                 (during the lifetime of this process, initially NULL)
551  * @param name name assigned by the user for this ego,
552  *                   NULL if the user just deleted the ego and it
553  *                   must thus no longer be used
554  */
555 static void
556 identity_cb (void *cls,
557              struct GNUNET_IDENTITY_Ego *ego,
558              void **ctx,
559              const char *name)
560 {
561   const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key;
562   struct GNUNET_GNSRECORD_Data rd;
563   char *rd_string;
564   char *peername;
565
566   if (NULL == name)
567     return;
568   if (NULL == ego)
569   {
570     if (NULL == qe)
571     {
572       fprintf (stderr,
573                "Failed to find master-zone ego\n");
574       GNUNET_SCHEDULER_shutdown ();
575       return;
576     }
577     GNUNET_IDENTITY_disconnect (identity);
578     identity = NULL;
579     return;
580   }
581   GNUNET_assert (NULL != name);
582   if (0 != strcmp (name,
583                    "master-zone"))
584   {
585     fprintf (stderr,
586              "Unexpected name %s\n",
587              name);
588     return;
589   }
590   zone_key = GNUNET_IDENTITY_ego_get_private_key (ego);
591   rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
592   peername = GNUNET_strdup (GNUNET_i2s_full (&id));
593   GNUNET_asprintf (&rd_string,
594                    "6 %s %s",
595                    peername,
596                    "www");
597   GNUNET_free (peername);
598   GNUNET_assert (GNUNET_OK ==
599                  GNUNET_GNSRECORD_string_to_value (GNUNET_GNSRECORD_TYPE_VPN,
600                                                    rd_string,
601                                                    (void**) &rd.data,
602                                                    &rd.data_size));
603   rd.record_type = GNUNET_GNSRECORD_TYPE_VPN;
604
605   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
606               "Creating `www` record\n");
607   qe = GNUNET_NAMESTORE_records_store (namestore,
608                                        zone_key,
609                                        "www",
610                                        1, &rd,
611                                        &commence_testing,
612                                        NULL);
613   GNUNET_free ((void**)rd.data);
614   GNUNET_free (rd_string);
615 }
616
617
618 static void
619 run (void *cls,
620      const struct GNUNET_CONFIGURATION_Handle *cfg,
621      struct GNUNET_TESTING_Peer *peer)
622 {
623   enum MHD_FLAG flags;
624
625   char *bin;
626   char *bin_identity;
627   char *bin_gns;
628   char *bin_arm;
629   char *config;
630
631   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
632               "Test logic starting...\n");
633   if (GNUNET_OK !=
634       GNUNET_CONFIGURATION_get_value_string (cfg,
635                                              "arm",
636                                              "CONFIG",
637                                              &config))
638   {
639     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
640              "Failed to locate configuration file. Skipping test.\n");
641     GNUNET_SCHEDULER_shutdown ();
642     return;
643   }
644
645   char *const identity_args[] =
646   {
647     "gnunet-identity",
648     "-C", "master-zone",
649     "-c", config,
650     NULL
651   };
652   char *const identity2_args[] =
653   {
654     "gnunet-identity",
655     "-e", "master-zone",
656     "-s", "gns-master",
657     "-c", config,
658     NULL
659   };
660   char *const identity3_args[] =
661   {
662     "gnunet-identity",
663     "-e", "master-zone",
664     "-s", "dns2gns",
665     "-c", config,
666     NULL
667   };
668   char *const arm_args[] =
669   {
670     "gnunet-arm",
671     "-i", "dns2gns",
672     "-c", config,
673     NULL
674   };
675   char *const gns_args[] =
676   {
677     "gnunet-gns",
678     "-u", "www.gnu",
679     "-c", config,
680     NULL
681   };
682
683   GNUNET_TESTING_peer_get_identity (peer,
684                                     &id);
685   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
686                                  NULL);
687   timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
688                                                &do_timeout,
689                                                NULL);
690   bin = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
691   GNUNET_asprintf (&bin_identity,
692                    "%s/%s",
693                    bin,
694                    "gnunet-identity");
695   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
696               "Creating `master-zone` ego\n");
697   if (0 != fork_and_exec (bin_identity, identity_args))
698   {
699     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
700                 "Failed to run `gnunet-identity -C`. Skipping test.\n");
701     GNUNET_SCHEDULER_shutdown ();
702     GNUNET_free (bin_identity);
703     GNUNET_free (config);
704     GNUNET_free (bin);
705     return;
706   }
707   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
708               "Setting `master-zone` ego as default for `gns-master` and `dns2gns`\n");
709   if (0 != fork_and_exec (bin_identity, identity2_args))
710   {
711     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
712                 "Failed to run `gnunet-identity -e`. Skipping test.\n");
713     GNUNET_SCHEDULER_shutdown ();
714     GNUNET_free (bin_identity);
715     GNUNET_free (config);
716     GNUNET_free (bin);
717     return;
718   }
719   if (0 != fork_and_exec (bin_identity, identity3_args))
720   {
721     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
722                 "Failed to run `gnunet-identity -e`. Skipping test.\n");
723     GNUNET_SCHEDULER_shutdown ();
724     GNUNET_free (bin_identity);
725     GNUNET_free (config);
726     GNUNET_free (bin);
727     return;
728   }
729   GNUNET_free (bin_identity);
730
731   /* do lookup just to launch GNS service */
732   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
733               "Resolving `www.gnu` zone entry to launch GNS (will yield no answer yet)\n");
734   GNUNET_asprintf (&bin_gns,
735                    "%s/%s",
736                    bin,
737                    "gnunet-gns");
738   if (0 != fork_and_exec (bin_gns,
739                           gns_args))
740   {
741     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
742                 "Failed to run `gnunet-gns -u. Skipping test.\n");
743     GNUNET_SCHEDULER_shutdown ();
744     GNUNET_free (bin_gns);
745     GNUNET_free (config);
746     GNUNET_free (bin);
747     return;
748   }
749   GNUNET_free (bin_gns);
750
751   GNUNET_asprintf (&bin_arm,
752                    "%s/%s",
753                    bin,
754                    "gnunet-arm");
755   if (0 != fork_and_exec (bin_arm,
756                           arm_args))
757   {
758     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
759                 "Failed to run `gnunet-arm -i dns2gns. Skipping test.\n");
760     GNUNET_SCHEDULER_shutdown ();
761     GNUNET_free (bin_arm);
762     GNUNET_free (config);
763     GNUNET_free (bin);
764     return;
765   }
766   GNUNET_free (bin_arm);
767
768   GNUNET_free (config);
769   GNUNET_free (bin);
770   sleep (1); /* give dns2gns chance to really run */
771
772   namestore = GNUNET_NAMESTORE_connect (cfg);
773   GNUNET_assert (NULL != namestore);
774   flags = MHD_USE_DEBUG;
775   if (GNUNET_YES == use_v6)
776     flags |= MHD_USE_DUAL_STACK;
777   mhd = MHD_start_daemon (flags,
778                           PORT,
779                           NULL, NULL,
780                           &mhd_ahc, NULL,
781                           MHD_OPTION_END);
782   GNUNET_assert (NULL != mhd);
783   mhd_main ();
784
785   identity = GNUNET_IDENTITY_connect (cfg,
786                                       &identity_cb,
787                                       NULL);
788 }
789
790
791 int
792 main (int argc,
793       char *const *argv)
794 {
795   char *bin_vpn;
796   char *bin_exit;
797
798   GNUNET_log_setup ("test-gns-vpn",
799                     "WARNING",
800                     NULL);
801   if (0 != ACCESS ("/dev/net/tun", R_OK))
802   {
803     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
804                               "access",
805                               "/dev/net/tun");
806     fprintf (stderr,
807              "WARNING: System unable to run test, skipping.\n");
808     return 77;
809   }
810
811   bin_vpn = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-vpn");
812   bin_exit = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-exit");
813   if ( (0 != geteuid ()) &&
814        ( (GNUNET_YES !=
815           GNUNET_OS_check_helper_binary (bin_vpn,
816                                          GNUNET_YES,
817                                          "-d gnunet-vpn - - 169.1.3.3.7 255.255.255.0")) || //ipv4 only please!
818          (GNUNET_YES !=
819           GNUNET_OS_check_helper_binary (bin_exit,
820                                          GNUNET_YES,
821                                          "-d gnunet-vpn - - - 169.1.3.3.7 255.255.255.0")) ) ) //no nat, ipv4 only
822   {
823     fprintf (stderr,
824              "WARNING: gnunet-helper-{exit,vpn} binaries in $PATH are not SUID, refusing to run test (as it would have to fail).\n");
825     fprintf (stderr,
826              "Change $PATH ('.' in $PATH before $GNUNET_PREFIX/bin is problematic) or permissions (run 'make install' as root) to fix this!\n");
827     GNUNET_free (bin_vpn);
828     GNUNET_free (bin_exit);
829     return 77;
830   }
831   GNUNET_free (bin_vpn);
832   GNUNET_free (bin_exit);
833
834   dest_ip = "169.254.86.1";
835   dest_af = AF_INET;
836   src_af = AF_INET;
837
838   if (GNUNET_OK == GNUNET_NETWORK_test_pf (PF_INET6))
839     use_v6 = GNUNET_YES;
840   else
841     use_v6 = GNUNET_NO;
842
843   if ( (GNUNET_OK != GNUNET_NETWORK_test_pf (src_af)) ||
844        (GNUNET_OK != GNUNET_NETWORK_test_pf (dest_af)) )
845   {
846     fprintf (stderr,
847              "Required address families not supported by this system, skipping test.\n");
848     return 77;
849   }
850   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
851   {
852     fprintf (stderr, "failed to initialize curl\n");
853     return 2;
854   }
855
856
857   if (0 !=
858       GNUNET_TESTING_peer_run ("test_gns_vpn",
859                                "test_gns_vpn.conf",
860                                &run,
861                                NULL))
862     return 1;
863   GNUNET_DISK_directory_remove ("/tmp/gnunet-test-vpn");
864   return global_ret;
865 }
866
867 /* end of test_gns_vpn.c */