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