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