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