c5d36958fa4d94ae7da8695713872db575b21c5a
[oweals/gnunet.git] / src / dns / gnunet-helper-dns.c
1 /*
2    This file is part of GNUnet.
3    (C) 2010, 2011, 2012 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., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file dns/gnunet-helper-dns.c
23  * @brief helper to install firewall rules to hijack all DNS traffic
24  *        and send it to our virtual interface (except for DNS traffic
25  *        that originates on the specified port).  We then
26  *        allow interacting with our virtual interface via stdin/stdout. 
27  * @author Philipp Tölke
28  * @author Christian Grothoff
29  *
30  * This program alters the Linux firewall rules so that DNS traffic
31  * that ordinarily exits the system can be intercepted and managed by
32  * a virtual interface.  In order to achieve this, DNS traffic is
33  * marked with the DNS_MARK given in below and re-routed to a custom
34  * table with the DNS_TABLE ID given below.  Systems and
35  * administrators must take care to not cause conflicts with these
36  * values (it was deemed safest to hardcode them as passing these
37  * values as arguments might permit messing with arbitrary firewall
38  * rules, which would be dangerous).
39  *
40  * The code first sets up the virtual interface, then begins to
41  * redirect the DNS traffic to it, and then on errors or SIGTERM shuts
42  * down the virtual interface and removes the rules for the traffic
43  * redirection.
44  *
45  *
46  * Note that having this binary SUID is only partially safe: it will
47  * allow redirecting (and intercepting / mangling) of all DNS traffic
48  * originating from this system by any user who is able to run it.
49  * Furthermore, this code will make it trivial to DoS all DNS traffic
50  * originating from the current system, simply by sending it to
51  * nowhere (redirect stdout to /dev/null).
52  *
53  * Naturally, neither of these problems can be helped as this is the
54  * fundamental purpose of the binary.  Certifying that this code is
55  * "safe" thus only means that it doesn't allow anything else (such
56  * as local priv. escalation, etc.). 
57  *
58  * The following list of people have reviewed this code and considered
59  * it safe (within specifications) since the last modification (if you
60  * reviewed it, please have your name added to the list):
61  *
62  * - Christian Grothoff 
63  */
64 #include "platform.h"
65
66 #include <linux/if_tun.h>
67
68 /**
69  * Need 'struct GNUNET_MessageHeader'.
70  */
71 #include "gnunet_common.h"
72
73 /**
74  * Need DNS message types.
75  */
76 #include "gnunet_protocols.h"
77
78 /**
79  * Maximum size of a GNUnet message (GNUNET_SERVER_MAX_MESSAGE_SIZE)
80  */
81 #define MAX_SIZE 65536
82
83 #ifndef _LINUX_IN6_H
84 /**
85  * This is in linux/include/net/ipv6.h, but not always exported...
86  */
87 struct in6_ifreq
88 {
89   struct in6_addr ifr6_addr;
90   uint32_t ifr6_prefixlen;
91   unsigned int ifr6_ifindex;
92 };
93 #endif
94
95 /**
96  * Name and full path of IPTABLES binary.
97  */
98 #define SBIN_IPTABLES "/sbin/iptables"
99
100 /**
101  * Name and full path of IPTABLES binary.
102  */
103 #define SBIN_IP "/sbin/ip"
104
105 /**
106  * Port for DNS traffic.
107  */
108 #define DNS_PORT "53"
109
110 /**
111  * Marker we set for our hijacked DNS traffic.  We use GNUnet's
112  * port (2086) plus the DNS port (53) in HEX to make a 32-bit mark
113  * (which is hopefully long enough to not collide); so
114  * 0x08260035 = 136708149 (hopefully unique enough...).
115  */
116 #define DNS_MARK "136708149"
117
118 /**
119  * Table we use for our DNS rules.  0-255 is the range and
120  * 0, 253, 254 and 255 are already reserved.  As this is about
121  * DNS and as "53" is likely (fingers crossed!) high enough to
122  * not usually conflict with a normal user's setup, we use 53
123  * to give a hint that this has something to do with DNS.
124  */
125 #define DNS_TABLE "53"
126
127
128 /**
129  * Control pipe for shutdown via signal. [0] is the read end,
130  * [1] is the write end.
131  */
132 static int cpipe[2];
133
134
135 /**
136  * Signal handler called to initiate "nice" shutdown.  Signals select
137  * loop via non-bocking pipe 'cpipe'.
138  *
139  * @param signal signal number of the signal (not used)
140  */
141 static void
142 signal_handler (int signal)
143 {
144   /* ignore return value, as the signal handler could theoretically
145      be called many times before the shutdown can actually happen */
146   (void) write (cpipe[1], "K", 1);
147 }
148
149
150 /**
151  * Run the given command and wait for it to complete.
152  * 
153  * @param file name of the binary to run
154  * @param cmd command line arguments (as given to 'execv')
155  * @return 0 on success, 1 on any error
156  */
157 static int
158 fork_and_exec (const char *file, 
159                char *const cmd[])
160 {
161   int status;
162   pid_t pid;
163   pid_t ret;
164
165   pid = fork ();
166   if (-1 == pid)
167   {
168     fprintf (stderr, 
169              "fork failed: %s\n", 
170              strerror (errno));
171     return 1;
172   }
173   if (0 == pid)
174   {
175     /* we are the child process */
176     (void) execv (file, cmd);
177     /* can only get here on error */
178     fprintf (stderr, 
179              "exec `%s' failed: %s\n", 
180              file,
181              strerror (errno));
182     _exit (1);
183   }
184   /* keep running waitpid as long as the only error we get is 'EINTR' */
185   while ( (-1 == (ret = waitpid (pid, &status, 0))) &&
186           (errno == EINTR) ); 
187   if (-1 == ret)
188   {
189     fprintf (stderr, 
190              "waitpid failed: %s\n", 
191              strerror (errno));
192     return 1;
193   }
194   if (! (WIFEXITED (status) && (0 == WEXITSTATUS (status))))
195     return 1;
196   /* child process completed and returned success, we're happy */
197   return 0;
198 }
199
200
201 /**
202  * Creates a tun-interface called dev;
203  *
204  * @param dev is asumed to point to a char[IFNAMSIZ]
205  *        if *dev == '\\0', uses the name supplied by the kernel;
206  * @return the fd to the tun or -1 on error
207  */
208 static int
209 init_tun (char *dev)
210 {
211   struct ifreq ifr;
212   int fd;
213
214   if (NULL == dev)
215   {
216     errno = EINVAL;
217     return -1;
218   }
219
220   if (-1 == (fd = open ("/dev/net/tun", O_RDWR)))
221   {
222     fprintf (stderr, "Error opening `%s': %s\n", "/dev/net/tun",
223              strerror (errno));
224     return -1;
225   }
226
227   if (fd >= FD_SETSIZE)
228   {
229     fprintf (stderr, "File descriptor to large: %d", fd);
230     return -1;
231   }
232
233   memset (&ifr, 0, sizeof (ifr));
234   ifr.ifr_flags = IFF_TUN;
235
236   if ('\0' != *dev)
237     strncpy (ifr.ifr_name, dev, IFNAMSIZ);
238
239   if (-1 == ioctl (fd, TUNSETIFF, (void *) &ifr))
240   {
241     fprintf (stderr, "Error with ioctl on `%s': %s\n", "/dev/net/tun",
242              strerror (errno));
243     (void) close (fd);
244     return -1;
245   }
246   strcpy (dev, ifr.ifr_name);
247   return fd;
248 }
249
250
251 /**
252  * @brief Sets the IPv6-Address given in address on the interface dev
253  *
254  * @param dev the interface to configure
255  * @param address the IPv6-Address
256  * @param prefix_len the length of the network-prefix
257  */
258 static void
259 set_address6 (const char *dev, const char *address, unsigned long prefix_len)
260 {
261   struct ifreq ifr;
262   struct in6_ifreq ifr6;
263   struct sockaddr_in6 sa6;
264   int fd;
265
266   /*
267    * parse the new address
268    */
269   memset (&sa6, 0, sizeof (struct sockaddr_in6));
270   sa6.sin6_family = AF_INET6;
271   if (1 != inet_pton (AF_INET6, address, sa6.sin6_addr.s6_addr))
272   {
273     fprintf (stderr, "Failed to parse address `%s': %s\n", address,
274              strerror (errno));
275     exit (1);
276   }
277
278   if (-1 == (fd = socket (PF_INET6, SOCK_DGRAM, 0)))
279   {
280     fprintf (stderr, "Error creating socket: %s\n", strerror (errno));
281     exit (1);
282   }
283
284   memset (&ifr, 0, sizeof (struct ifreq));
285   /*
286    * Get the index of the if
287    */
288   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
289   if (-1 == ioctl (fd, SIOGIFINDEX, &ifr))
290   {
291     fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
292     (void) close (fd);
293     exit (1);
294   }
295
296   memset (&ifr6, 0, sizeof (struct in6_ifreq));
297   ifr6.ifr6_addr = sa6.sin6_addr;
298   ifr6.ifr6_ifindex = ifr.ifr_ifindex;
299   ifr6.ifr6_prefixlen = prefix_len;
300
301   /*
302    * Set the address
303    */
304   if (-1 == ioctl (fd, SIOCSIFADDR, &ifr6))
305   {
306     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
307              strerror (errno));
308     (void) close (fd);
309     exit (1);
310   }
311
312   /*
313    * Get the flags
314    */
315   if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
316   {
317     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
318              strerror (errno));
319     (void) close (fd);
320     exit (1);
321   }
322
323   /*
324    * Add the UP and RUNNING flags
325    */
326   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
327   if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
328   {
329     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
330              strerror (errno));
331     (void) close (fd);
332     exit (1);
333   }
334
335   if (0 != close (fd))
336   {
337     fprintf (stderr, "close failed: %s\n", strerror (errno));
338     exit (1);
339   }
340 }
341
342
343 /**
344  * @brief Sets the IPv4-Address given in address on the interface dev
345  *
346  * @param dev the interface to configure
347  * @param address the IPv4-Address
348  * @param mask the netmask
349  */
350 static void
351 set_address4 (const char *dev, const char *address, const char *mask)
352 {
353   int fd;
354   struct sockaddr_in *addr;
355   struct ifreq ifr;
356
357   memset (&ifr, 0, sizeof (struct ifreq));
358   addr = (struct sockaddr_in *) &(ifr.ifr_addr);
359   addr->sin_family = AF_INET;
360
361   /*
362    * Parse the address
363    */
364   if (1 != inet_pton (AF_INET, address, &addr->sin_addr.s_addr))
365   {
366     fprintf (stderr, "Failed to parse address `%s': %s\n", address,
367              strerror (errno));
368     exit (1);
369   }
370
371   if (-1 == (fd = socket (PF_INET, SOCK_DGRAM, 0)))
372   {
373     fprintf (stderr, "Error creating socket: %s\n", strerror (errno));
374     exit (1);
375   }
376
377   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
378
379   /*
380    * Set the address
381    */
382   if (-1 == ioctl (fd, SIOCSIFADDR, &ifr))
383   {
384     fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
385     (void) close (fd);
386     exit (1);
387   }
388
389   /*
390    * Parse the netmask
391    */
392   addr = (struct sockaddr_in *) &(ifr.ifr_netmask);
393   if (1 != inet_pton (AF_INET, mask, &addr->sin_addr.s_addr))
394   {
395     fprintf (stderr, "Failed to parse address `%s': %s\n", mask,
396              strerror (errno));
397     (void) close (fd);
398     exit (1);
399   }
400
401   /*
402    * Set the netmask
403    */
404   if (-1 == ioctl (fd, SIOCSIFNETMASK, &ifr))
405   {
406     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
407              strerror (errno));
408     (void) close (fd);
409     exit (1);
410   }
411
412   /*
413    * Get the flags
414    */
415   if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
416   {
417     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
418              strerror (errno));
419     (void) close (fd);
420     exit (1);
421   }
422
423   /*
424    * Add the UP and RUNNING flags
425    */
426   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
427   if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
428   {
429     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
430              strerror (errno));
431     (void) close (fd);
432     exit (1);
433   }
434
435   if (0 != close (fd))
436   {
437     fprintf (stderr, "close failed: %s\n", strerror (errno));
438     (void) close (fd);
439     exit (1);
440   }
441 }
442
443
444 /**
445  * Start forwarding to and from the tunnel.  This function runs with
446  * "reduced" priviledges (saved UID is still 0, but effective UID is
447  * the real user ID).
448  *
449  * @param fd_tun tunnel FD
450  */
451 static void
452 run (int fd_tun)
453 {
454   /*
455    * The buffer filled by reading from fd_tun
456    */
457   unsigned char buftun[MAX_SIZE];
458   ssize_t buftun_size = 0;
459   unsigned char *buftun_read = NULL;
460
461   /*
462    * The buffer filled by reading from stdin
463    */
464   unsigned char bufin[MAX_SIZE];
465   ssize_t bufin_size = 0;
466   size_t bufin_rpos = 0;
467   unsigned char *bufin_read = NULL;
468   fd_set fds_w;
469   fd_set fds_r;
470   int max;
471
472   while (1)
473   {
474     FD_ZERO (&fds_w);
475     FD_ZERO (&fds_r);
476
477     /*
478      * We are supposed to read and the buffer is empty
479      * -> select on read from tun
480      */
481     if (0 == buftun_size)
482       FD_SET (fd_tun, &fds_r);
483
484     /*
485      * We are supposed to read and the buffer is not empty
486      * -> select on write to stdout
487      */
488     if (0 != buftun_size)
489       FD_SET (1, &fds_w);
490
491     /*
492      * We are supposed to write and the buffer is empty
493      * -> select on read from stdin
494      */
495     if (NULL == bufin_read)
496       FD_SET (0, &fds_r);
497
498     /*
499      * We are supposed to write and the buffer is not empty
500      * -> select on write to tun
501      */
502     if (NULL != bufin_read)
503       FD_SET (fd_tun, &fds_w);
504
505     FD_SET (cpipe[0], &fds_r);
506     max = (fd_tun > cpipe[0]) ? fd_tun : cpipe[0];
507
508     int r = select (max + 1, &fds_r, &fds_w, NULL, NULL);
509
510     if (-1 == r)
511     {
512       if (EINTR == errno)
513         continue;
514       fprintf (stderr, "select failed: %s\n", strerror (errno));
515       return;
516     }
517
518     if (r > 0)
519     {
520       if (FD_ISSET (cpipe[0], &fds_r))
521         return; /* aborted by signal */
522
523       if (FD_ISSET (fd_tun, &fds_r))
524       {
525         buftun_size =
526             read (fd_tun, buftun + sizeof (struct GNUNET_MessageHeader),
527                   MAX_SIZE - sizeof (struct GNUNET_MessageHeader));
528         if (-1 == buftun_size)
529         {
530           if ( (errno == EINTR) ||
531                (errno == EAGAIN) )
532             continue;
533           fprintf (stderr, "read-error: %s\n", strerror (errno));
534           return;
535         }
536         if (0 == buftun_size)
537         {
538           fprintf (stderr, "EOF on tun\n");
539           return;
540         }
541         buftun_read = buftun;
542         {
543           struct GNUNET_MessageHeader *hdr =
544               (struct GNUNET_MessageHeader *) buftun;
545           buftun_size += sizeof (struct GNUNET_MessageHeader);
546           hdr->type = htons (GNUNET_MESSAGE_TYPE_DNS_HELPER);
547           hdr->size = htons (buftun_size);
548         }
549       }
550       else if (FD_ISSET (1, &fds_w))
551       {
552         ssize_t written = write (1, buftun_read, buftun_size);
553
554         if (-1 == written)
555         {
556           if ( (errno == EINTR) ||
557                (errno == EAGAIN) )
558             continue;
559           fprintf (stderr, "write-error to stdout: %s\n", strerror (errno));
560           return;
561         }
562         if (0 == written)
563         {
564           fprintf (stderr, "write returned 0\n");
565           return;
566         }
567         buftun_size -= written;
568         buftun_read += written;        
569       }
570
571       if (FD_ISSET (0, &fds_r))
572       {
573         bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos);
574         if (-1 == bufin_size)
575         {
576           if ( (errno == EINTR) ||
577                (errno == EAGAIN) )
578             continue;
579           fprintf (stderr, "read-error: %s\n", strerror (errno));
580           return;
581         }
582         if (0 == bufin_size)
583         {
584           fprintf (stderr, "EOF on stdin\n");
585           return;
586         }
587         {
588           struct GNUNET_MessageHeader *hdr;
589
590 PROCESS_BUFFER:
591           bufin_rpos += bufin_size;
592           if (bufin_rpos < sizeof (struct GNUNET_MessageHeader))
593             continue;
594           hdr = (struct GNUNET_MessageHeader *) bufin;
595           if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_DNS_HELPER)
596           {
597             fprintf (stderr, "protocol violation!\n");
598             return;
599           }
600           if (ntohs (hdr->size) > bufin_rpos)
601             continue;
602           bufin_read = bufin + sizeof (struct GNUNET_MessageHeader);
603           bufin_size = ntohs (hdr->size) - sizeof (struct GNUNET_MessageHeader);
604           bufin_rpos -= bufin_size + sizeof (struct GNUNET_MessageHeader);
605         }
606       }
607       else if (FD_ISSET (fd_tun, &fds_w))
608       {
609         ssize_t written = write (fd_tun, bufin_read, bufin_size);
610
611         if (-1 == written)
612         {
613           if ( (errno == EINTR) ||
614                (errno == EAGAIN) )
615             continue;
616           fprintf (stderr, "write-error to tun: %s\n", strerror (errno));
617           return;
618         }
619         if (0 == written)
620         {
621           fprintf (stderr, "write returned 0\n");
622           return;
623         }
624         {
625           bufin_size -= written;
626           bufin_read += written;
627           if (0 == bufin_size)
628           {
629             memmove (bufin, bufin_read, bufin_rpos);
630             bufin_read = NULL;  /* start reading again */
631             bufin_size = 0;
632             goto PROCESS_BUFFER;
633           }
634         }
635       }
636     }
637   }
638 }
639
640
641 /**
642  * Main function of "gnunet-helper-dns", which opens a VPN tunnel interface,
643  * redirects all outgoing DNS traffic (except from the specified port) to that
644  * interface and then passes traffic from and to the interface via stdin/stdout.
645  *
646  * Once stdin/stdout close or have other errors, the tunnel is closed and the
647  * DNS traffic redirection is stopped.
648  *
649  * @param argc number of arguments
650  * @param argv 0: binary name (should be "gnunet-helper-vpn")
651  *             1: tunnel interface name (typically "gnunet-dns")
652  *             2: IPv6 address for the tunnel ("FE80::1")
653  *             3: IPv6 netmask length in bits ("64")
654  *             4: IPv4 address for the tunnel ("1.2.3.4")
655  *             5: IPv4 netmask ("255.255.0.0")
656  *             6: PORT to not hijack ("55533")
657  * @return 0 on success, otherwise code indicating type of error:
658  *         1 wrong number of arguments
659  *         2 invalid arguments (i.e. port number / prefix length wrong)
660  *         3 iptables not executable
661  *         4 ip not executable
662  *         5 failed to initialize tunnel interface
663  *         6 failed to initialize control pipe
664  *         8 failed to change routing table, cleanup successful
665  *         9-23 failed to undo some changes to routing table
666  *         24 failed to drop privs
667  *         25-39 failed to drop privs and then failed to undo some changes to routing table
668  *         40 failed to regain privs
669  *         41-55 failed to regain prisv and then failed to undo some changes to routing table
670  *         255 failed to handle kill signal properly
671  */
672 int
673 main (int argc, char *const*argv)
674 {
675   unsigned int port;
676   char localport[6];
677   int r;
678   char dev[IFNAMSIZ];
679   int fd_tun;
680
681   if (7 != argc)
682   {
683     fprintf (stderr, "Fatal: must supply 6 arguments!\n");
684     return 1;
685   }
686
687   /* verify that the binaries were care about are executable */
688   if (0 != access (SBIN_IPTABLES, X_OK))
689   {
690     fprintf (stderr, 
691              "`%s' is not executable: %s\n", 
692              SBIN_IPTABLES,
693              strerror (errno));
694     return 3;
695   }
696   if (0 != access (SBIN_IP, X_OK))
697   {
698     fprintf (stderr, 
699              "`%s' is not executable: %s\n", 
700              SBIN_IP,
701              strerror (errno));
702     return 4;
703   }
704
705   /* validate port number */
706   port = atoi (argv[6]);
707   if ( (port == 0) || (port >= 65536) )
708   {
709     fprintf (stderr, 
710              "Port `%u' is invalid\n",
711              port);
712     return 2;
713   }
714   /* print port number to string for command-line use*/
715   (void) snprintf (localport,
716                    sizeof (localport), 
717                    "%u", 
718                    port);
719
720   /* do not die on SIGPIPE */
721   if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
722   {
723     fprintf (stderr, "Failed to protect against SIGPIPE: %s\n",
724              strerror (errno));
725     return 7;
726   }
727
728   /* setup pipe to shutdown nicely on SIGINT */
729   if (0 != pipe (cpipe))
730   {
731     fprintf (stderr, 
732              "Fatal: could not setup control pipe: %s\n",
733              strerror (errno));
734     return 6;
735   }
736   if (cpipe[0] >= FD_SETSIZE)
737   {
738     fprintf (stderr, "Pipe file descriptor to large: %d", cpipe[0]);
739     (void) close (cpipe[0]);
740     (void) close (cpipe[1]);
741     return 6;
742   }
743   {
744     /* make pipe non-blocking, as we theoretically could otherwise block
745        in the signal handler */
746     int flags = fcntl (cpipe[1], F_GETFL);
747     if (-1 == flags)
748     {
749       fprintf (stderr, "Failed to read flags for pipe: %s", strerror (errno));
750       (void) close (cpipe[0]);
751       (void) close (cpipe[1]);
752       return 6;
753     }
754     flags |= O_NONBLOCK;
755     if (0 != fcntl (cpipe[1], F_SETFL, flags))
756     {
757       fprintf (stderr, "Failed to make pipe non-blocking: %s", strerror (errno));
758       (void) close (cpipe[0]);
759       (void) close (cpipe[1]);
760       return 6;
761     }
762   }
763   if (SIG_ERR == signal (SIGINT, &signal_handler))
764   { 
765     fprintf (stderr, 
766              "Fatal: could not initialize signal handler: %s\n",
767              strerror (errno));
768     (void) close (cpipe[0]);
769     (void) close (cpipe[1]);
770     return 7;   
771   }
772
773
774   /* get interface name */
775   strncpy (dev, argv[1], IFNAMSIZ);
776   dev[IFNAMSIZ - 1] = '\0';
777
778   /* now open virtual interface (first part that requires root) */
779   if (-1 == (fd_tun = init_tun (dev)))
780   {
781     fprintf (stderr, "Fatal: could not initialize tun-interface\n");
782     (void) signal (SIGINT, SIG_IGN);
783     (void) close (cpipe[0]);
784     (void) close (cpipe[1]);
785     return 5;
786   }
787
788   /* now set interface addresses */
789   {
790     const char *address = argv[2];
791     long prefix_len = atol (argv[3]);
792
793     if ((prefix_len < 1) || (prefix_len > 127))
794     {
795       fprintf (stderr, "Fatal: prefix_len out of range\n");
796       (void) signal (SIGINT, SIG_IGN);
797       (void) close (cpipe[0]);
798       (void) close (cpipe[1]);
799       return 2;
800     }
801     set_address6 (dev, address, prefix_len);
802   }
803
804   {
805     const char *address = argv[4];
806     const char *mask = argv[5];
807
808     set_address4 (dev, address, mask);
809   }
810   
811   /* update routing tables -- next part why we need SUID! */
812   /* Forward everything from the given local port (with destination
813      to port 53, and only for UDP) without hijacking */
814   r = 8; /* failed to fully setup routing table */
815   {
816     char *const mangle_args[] = 
817       {
818         "iptables", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
819         "udp", "--sport", localport, "--dport", DNS_PORT, "-j",
820         "ACCEPT", NULL
821       };
822     if (0 != fork_and_exec (SBIN_IPTABLES, mangle_args))
823       goto cleanup_mangle_1;
824   }    
825   /* Mark all of the other DNS traffic using our mark DNS_MARK */
826   {
827     char *const mark_args[] =
828       {
829         "iptables", "-t", "mangle", "-I", "OUTPUT", DNS_TABLE, "-p",
830         "udp", "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK,
831         NULL
832       };
833     if (0 != fork_and_exec (SBIN_IPTABLES, mark_args))
834       goto cleanup_mark_2;
835   }
836   /* Forward all marked DNS traffic to our DNS_TABLE */
837   {
838     char *const forward_args[] =
839       {
840         "ip", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
841       };
842     if (0 != fork_and_exec (SBIN_IP, forward_args))
843       goto cleanup_forward_3;
844   }
845   /* Finally, add rule in our forwarding table to pass to our virtual interface */
846   {
847     char *const route_args[] =
848       {
849         "ip", "route", "add", "default", "via", dev,
850         "table", DNS_TABLE, NULL
851       };
852     if (0 != fork_and_exec (SBIN_IP, route_args))
853       goto cleanup_route_4;
854   }
855
856   /* drop privs *except* for the saved UID; this is not perfect, but better
857      than doing nothing */
858   uid_t uid = getuid ();
859 #ifdef HAVE_SETRESUID
860   if (0 != setresuid (uid, uid, 0))
861   {
862     fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
863     r = 24;
864     goto cleanup_route_4;
865   }
866 #else
867   /* Note: no 'setuid' here as we must keep our saved UID as root */
868   if (0 != seteuid (uid)) 
869   {
870     fprintf (stderr, "Failed to seteuid: %s\n", strerror (errno));
871     r = 24;
872     goto cleanup_route_4;
873   }
874 #endif
875
876   r = 0; /* did fully setup routing table (if nothing else happens, we were successful!) */
877
878   /* now forward until we hit a problem */
879    run (fd_tun);
880   (void) close (fd_tun);
881   
882   /* now need to regain privs so we can remove the firewall rules we added! */
883 #ifdef HAVE_SETRESUID
884   if (0 != setresuid (uid, 0, 0))
885   {
886     fprintf (stderr, "Failed to setresuid back to root: %s\n", strerror (errno));
887     r = 40;
888     goto cleanup_route_4;
889   }
890 #else
891   if (0 != seteuid (0)) 
892   {
893     fprintf (stderr, "Failed to seteuid back to root: %s\n", strerror (errno));
894     r = 40;
895     goto cleanup_route_4;
896   }
897 #endif
898  
899   /* update routing tables again -- this is why we could not fully drop privs */
900   /* now undo updating of routing tables; normal exit or clean-up-on-error case */
901  cleanup_route_4:
902   {
903     char *const route_clean_args[] =                     
904       {
905         "ip", "route", "del", "default", "via", dev,
906         "table", DNS_TABLE, NULL
907       };
908     if (0 != fork_and_exec (SBIN_IP, route_clean_args))
909       r += 1;
910   }
911  cleanup_forward_3:
912   {
913     char *const forward_clean_args[] =
914       {
915         "ip", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
916       };
917     if (0 != fork_and_exec (SBIN_IP, forward_clean_args))
918       r += 2;   
919   }
920  cleanup_mark_2:
921   {
922     char *const mark_clean_args[] =
923       {
924         "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
925         "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK, NULL
926       };
927     if (0 != fork_and_exec (SBIN_IPTABLES, mark_clean_args))
928       r += 4;
929   }     
930  cleanup_mangle_1:
931   {
932     char *const mangle_clean_args[] =
933       {
934         "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
935         "--sport", localport, "--dport", DNS_PORT, "-j", "ACCEPT",
936         NULL
937       };
938     if (0 != fork_and_exec (SBIN_IPTABLES, mangle_clean_args))
939       r += 8;
940   }
941
942   /* remove SIGINT handler so we can close the pipes */
943   (void) signal (SIGINT, SIG_IGN);
944   (void) close (cpipe[0]);
945   (void) close (cpipe[1]);
946   return r;
947 }
948
949 /* end of gnunet-helper-hijack-dns.c */