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