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