-Merge branch 'master' of ssh://gnunet.org/gnunet into gsoc2018/rest_api
[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
19 /**
20  * @file test_gns_vpn.c
21  * @brief testcase for accessing VPN services via GNS
22  * @author Martin Schanzenbach
23  * @author Christian Grothoff
24  *
25  * This test requires libcurl/libgnurl *with* support for C-ARES.
26  * This is NOT the default on most platforms, which means the test
27  * will be skipped in many cases.   Compile libcurl/libgnurl with
28  * "--enable-ares" to get this test to pass.
29  *
30  * Furthermore, the test relies on gnunet-dns2gns being able to bind
31  * to port 53.  This means that 'setcap' has to have worked during
32  * 'make install'.  If this failed, but everything else is OK, the
33  * test may FAIL hard even though it is just an installation issue (we
34  * cannot conveniently test for the setcap to have worked).  However,
35  * you should get a warning that gnunet-dns2gns failed to 'bind'.
36  */
37 #include "platform.h"
38 #if HAVE_CURL_CURL_H
39 #include <curl/curl.h>
40 #elif HAVE_GNURL_CURL_H
41 #include <gnurl/curl.h>
42 #endif
43 #include <microhttpd.h>
44 #include "gnunet_identity_service.h"
45 #include "gnunet_namestore_service.h"
46 #include "gnunet_gnsrecord_lib.h"
47 #include "gnunet_gns_service.h"
48 #include "gnunet_testing_lib.h"
49
50 #define PORT 8080
51 #define TEST_DOMAIN "www.gnu"
52
53 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
54
55 /**
56  * Return value for #main().
57  */
58 static int global_ret;
59
60 static struct GNUNET_NAMESTORE_Handle *namestore;
61
62 static struct MHD_Daemon *mhd;
63
64 static struct GNUNET_SCHEDULER_Task *mhd_task_id;
65
66 static struct GNUNET_SCHEDULER_Task *curl_task_id;
67
68 static struct GNUNET_SCHEDULER_Task *timeout_task;
69
70 static struct GNUNET_IDENTITY_Handle *identity;
71
72 static struct GNUNET_NAMESTORE_QueueEntry *qe;
73
74 static CURL *curl;
75
76 static CURLM *multi;
77
78 static char *url;
79
80 static struct GNUNET_PeerIdentity id;
81
82 /**
83  * IP address of the ultimate destination.
84  */
85 static const char *dest_ip;
86
87 /**
88  * Address family of the dest_ip.
89  */
90 static int dest_af;
91
92 /**
93  * Address family to use by the curl client.
94  */
95 static int src_af;
96
97 static int use_v6;
98
99
100 struct CBC
101 {
102   char buf[1024];
103   size_t pos;
104 };
105
106 static struct CBC cbc;
107
108
109 static size_t
110 copy_buffer (void *ptr,
111              size_t size,
112              size_t nmemb,
113              void *ctx)
114 {
115   struct CBC *cbc = ctx;
116
117   if (cbc->pos + size * nmemb > sizeof(cbc->buf))
118     return 0;                   /* overflow */
119   GNUNET_memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
120   cbc->pos += size * nmemb;
121   return size * nmemb;
122 }
123
124
125 static int
126 mhd_ahc (void *cls,
127           struct MHD_Connection *connection,
128           const char *url,
129           const char *method,
130           const char *version,
131           const char *upload_data, size_t *upload_data_size,
132           void **unused)
133 {
134   static int ptr;
135   struct MHD_Response *response;
136   int ret;
137
138   if (0 != strcmp ("GET", method))
139     return MHD_NO;              /* unexpected method */
140   if (&ptr != *unused)
141   {
142     *unused = &ptr;
143     return MHD_YES;
144   }
145   *unused = NULL;
146   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "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, (unsigned int) timeout);
284   GNUNET_NETWORK_fdset_copy_native (&nrs,
285                                     &rs,
286                                     max + 1);
287   GNUNET_NETWORK_fdset_copy_native (&nws,
288                                     &ws,
289                                     max + 1);
290   curl_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
291                                               delay,
292                                               &nrs,
293                                               &nws,
294                                               &curl_task,
295                                               NULL);
296 }
297
298
299 static void
300 start_curl (void *cls)
301 {
302   CURLcode ec;
303
304   curl_task_id = NULL;
305   GNUNET_asprintf (&url,
306                    "http://%s/hello_world",
307                    TEST_DOMAIN);
308   curl = curl_easy_init ();
309   curl_easy_setopt (curl, CURLOPT_URL, url);
310   curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, &copy_buffer);
311   curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbc);
312   curl_easy_setopt (curl, CURLOPT_FAILONERROR, 1);
313   curl_easy_setopt (curl, CURLOPT_TIMEOUT, 150L);
314   curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 150L);
315   curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1);
316   if (CURLE_OK !=
317       (ec = curl_easy_setopt (curl,
318                               CURLOPT_DNS_SERVERS,
319                               "127.0.0.1:53")))
320   {
321     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
322                 "curl build without support for CURLOPT_DNS_SERVERS (%s), cannot run test\n",
323                 curl_easy_strerror (ec));
324     global_ret = 77;
325     GNUNET_SCHEDULER_shutdown ();
326     return;
327   }
328   multi = curl_multi_init ();
329   GNUNET_assert (multi != NULL);
330   GNUNET_assert (CURLM_OK == curl_multi_add_handle (multi, curl));
331   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
332               "Beginning HTTP download from `%s'\n",
333               url);
334   curl_main ();
335 }
336
337
338 /**
339  * Callback invoked from the namestore service once record is
340  * created.
341  *
342  * @param cls closure
343  * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
344  *                will match 'result_af' from the request
345  * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
346  *                that the VPN allocated for the redirection;
347  *                traffic to this IP will now be redirected to the
348  *                specified target peer; NULL on error
349  */
350 static void
351 commence_testing (void *cls,
352                   int32_t success,
353                   const char *emsg)
354 {
355   qe = NULL;
356   if ( (NULL != emsg) &&
357        (GNUNET_YES != success) )
358   {
359     fprintf (stderr,
360              "NS failed to create record %s\n",
361              emsg);
362     GNUNET_SCHEDULER_shutdown ();
363     return;
364   }
365
366   /* wait a little bit before downloading, as we just created the record */
367   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
368               "Launching cURL request\n");
369   curl_task_id
370     = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
371                                     &start_curl,
372                                     NULL);
373 }
374
375
376 /**
377  * Function to keep the HTTP server running.
378  */
379 static void
380 mhd_main (void);
381
382
383 static void
384 mhd_task (void *cls)
385 {
386   mhd_task_id = NULL;
387   MHD_run (mhd);
388   mhd_main ();
389 }
390
391
392 static void
393 mhd_main ()
394 {
395   struct GNUNET_NETWORK_FDSet nrs;
396   struct GNUNET_NETWORK_FDSet nws;
397   fd_set rs;
398   fd_set ws;
399   fd_set es;
400   int max_fd;
401   unsigned MHD_LONG_LONG timeout;
402   struct GNUNET_TIME_Relative delay;
403
404   GNUNET_assert (NULL == mhd_task_id);
405   FD_ZERO (&rs);
406   FD_ZERO (&ws);
407   FD_ZERO (&es);
408   max_fd = -1;
409   GNUNET_assert (MHD_YES ==
410                  MHD_get_fdset (mhd, &rs, &ws, &es, &max_fd));
411   if (MHD_YES == MHD_get_timeout (mhd, &timeout))
412     delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
413                                            (unsigned int) timeout);
414   else
415     delay = GNUNET_TIME_UNIT_FOREVER_REL;
416   GNUNET_NETWORK_fdset_copy_native (&nrs,
417                                     &rs,
418                                     max_fd + 1);
419   GNUNET_NETWORK_fdset_copy_native (&nws,
420                                     &ws,
421                                     max_fd + 1);
422   mhd_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
423                                              delay,
424                                              &nrs,
425                                              &nws,
426                                              &mhd_task,
427                                              NULL);
428 }
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   if (-1 == ret)
501   {
502     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
503                          "waitpid");
504     return 1;
505   }
506   if (! (WIFEXITED (status) &&
507          (0 == WEXITSTATUS (status))) )
508   {
509     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
510                 "Process `%s` returned status code %d/%d.\n",
511                 file,
512                 WIFEXITED (status),
513                 WEXITSTATUS (status));
514     return 1;
515   }
516   /* child process completed and returned success, we're happy */
517   return 0;
518 }
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   {
648     "gnunet-identity",
649     "-C", "master-zone",
650     "-c", config,
651     NULL
652   };
653   char *const identity2_args[] =
654   {
655     "gnunet-identity",
656     "-e", "master-zone",
657     "-s", "gns-master",
658     "-c", config,
659     NULL
660   };
661   char *const identity3_args[] =
662   {
663     "gnunet-identity",
664     "-e", "master-zone",
665     "-s", "dns2gns",
666     "-c", config,
667     NULL
668   };
669   char *const arm_args[] =
670   {
671     "gnunet-arm",
672     "-i", "dns2gns",
673     "-c", config,
674     NULL
675   };
676   char *const gns_args[] =
677   {
678     "gnunet-gns",
679     "-u", "www.gnu",
680     "-c", config,
681     NULL
682   };
683
684   GNUNET_TESTING_peer_get_identity (peer,
685                                     &id);
686   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
687                                  NULL);
688   timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
689                                                &do_timeout,
690                                                NULL);
691   bin = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
692   GNUNET_asprintf (&bin_identity,
693                    "%s/%s",
694                    bin,
695                    "gnunet-identity");
696   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
697               "Creating `master-zone` ego\n");
698   if (0 != fork_and_exec (bin_identity, identity_args))
699   {
700     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
701                 "Failed to run `gnunet-identity -C`. Skipping test.\n");
702     GNUNET_SCHEDULER_shutdown ();
703     GNUNET_free (bin_identity);
704     GNUNET_free (config);
705     GNUNET_free (bin);
706     return;
707   }
708   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
709               "Setting `master-zone` ego as default for `gns-master` and `dns2gns`\n");
710   if (0 != fork_and_exec (bin_identity, identity2_args))
711   {
712     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
713                 "Failed to run `gnunet-identity -e`. Skipping test.\n");
714     GNUNET_SCHEDULER_shutdown ();
715     GNUNET_free (bin_identity);
716     GNUNET_free (config);
717     GNUNET_free (bin);
718     return;
719   }
720   if (0 != fork_and_exec (bin_identity, identity3_args))
721   {
722     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
723                 "Failed to run `gnunet-identity -e`. Skipping test.\n");
724     GNUNET_SCHEDULER_shutdown ();
725     GNUNET_free (bin_identity);
726     GNUNET_free (config);
727     GNUNET_free (bin);
728     return;
729   }
730   GNUNET_free (bin_identity);
731
732   /* do lookup just to launch GNS service */
733   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
734               "Resolving `www.gnu` zone entry to launch GNS (will yield no answer yet)\n");
735   GNUNET_asprintf (&bin_gns,
736                    "%s/%s",
737                    bin,
738                    "gnunet-gns");
739   if (0 != fork_and_exec (bin_gns,
740                           gns_args))
741   {
742     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
743                 "Failed to run `gnunet-gns -u. Skipping test.\n");
744     GNUNET_SCHEDULER_shutdown ();
745     GNUNET_free (bin_gns);
746     GNUNET_free (config);
747     GNUNET_free (bin);
748     return;
749   }
750   GNUNET_free (bin_gns);
751
752   GNUNET_asprintf (&bin_arm,
753                    "%s/%s",
754                    bin,
755                    "gnunet-arm");
756   if (0 != fork_and_exec (bin_arm,
757                           arm_args))
758   {
759     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
760                 "Failed to run `gnunet-arm -i dns2gns. Skipping test.\n");
761     GNUNET_SCHEDULER_shutdown ();
762     GNUNET_free (bin_arm);
763     GNUNET_free (config);
764     GNUNET_free (bin);
765     return;
766   }
767   GNUNET_free (bin_arm);
768
769   GNUNET_free (config);
770   GNUNET_free (bin);
771   sleep (1); /* give dns2gns chance to really run */
772
773   namestore = GNUNET_NAMESTORE_connect (cfg);
774   GNUNET_assert (NULL != namestore);
775   flags = MHD_USE_DEBUG;
776   if (GNUNET_YES == use_v6)
777     flags |= MHD_USE_DUAL_STACK;
778   mhd = MHD_start_daemon (flags,
779                           PORT,
780                           NULL, NULL,
781                           &mhd_ahc, NULL,
782                           MHD_OPTION_END);
783   GNUNET_assert (NULL != mhd);
784   mhd_main ();
785
786   identity = GNUNET_IDENTITY_connect (cfg,
787                                       &identity_cb,
788                                       NULL);
789 }
790
791
792 int
793 main (int argc,
794       char *const *argv)
795 {
796   char *bin_vpn;
797   char *bin_exit;
798
799   GNUNET_log_setup ("test-gns-vpn",
800                     "WARNING",
801                     NULL);
802   if (0 != ACCESS ("/dev/net/tun", R_OK))
803   {
804     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
805                               "access",
806                               "/dev/net/tun");
807     fprintf (stderr,
808              "WARNING: System unable to run test, skipping.\n");
809     return 77;
810   }
811
812   bin_vpn = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-vpn");
813   bin_exit = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-exit");
814   if ( (0 != geteuid ()) &&
815        ( (GNUNET_YES !=
816           GNUNET_OS_check_helper_binary (bin_vpn,
817                                          GNUNET_YES,
818                                          "-d gnunet-vpn - - 169.1.3.3.7 255.255.255.0")) || //ipv4 only please!
819          (GNUNET_YES !=
820           GNUNET_OS_check_helper_binary (bin_exit,
821                                          GNUNET_YES,
822                                          "-d gnunet-vpn - - - 169.1.3.3.7 255.255.255.0")) ) ) //no nat, ipv4 only
823   {
824     fprintf (stderr,
825              "WARNING: gnunet-helper-{exit,vpn} binaries in $PATH are not SUID, refusing to run test (as it would have to fail).\n");
826     fprintf (stderr,
827              "Change $PATH ('.' in $PATH before $GNUNET_PREFIX/bin is problematic) or permissions (run 'make install' as root) to fix this!\n");
828     GNUNET_free (bin_vpn);
829     GNUNET_free (bin_exit);
830     return 77;
831   }
832   GNUNET_free (bin_vpn);
833   GNUNET_free (bin_exit);
834
835   dest_ip = "169.254.86.1";
836   dest_af = AF_INET;
837   src_af = AF_INET;
838
839   if (GNUNET_OK == GNUNET_NETWORK_test_pf (PF_INET6))
840     use_v6 = GNUNET_YES;
841   else
842     use_v6 = GNUNET_NO;
843
844   if ( (GNUNET_OK != GNUNET_NETWORK_test_pf (src_af)) ||
845        (GNUNET_OK != GNUNET_NETWORK_test_pf (dest_af)) )
846   {
847     fprintf (stderr,
848              "Required address families not supported by this system, skipping test.\n");
849     return 77;
850   }
851   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
852   {
853     fprintf (stderr, "failed to initialize curl\n");
854     return 2;
855   }
856
857
858   if (0 !=
859       GNUNET_TESTING_peer_run ("test_gns_vpn",
860                                "test_gns_vpn.conf",
861                                &run,
862                                NULL))
863     return 1;
864   GNUNET_DISK_directory_remove ("/tmp/gnunet-test-vpn");
865   return global_ret;
866 }
867
868 /* end of test_gns_vpn.c */