73536c252af4d2c74e407fa49e73d3e6608344c7
[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 static const char *sbin_iptables;
99
100 /**
101  * Name and full path of IPTABLES binary.
102  */
103 static const char *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 change routing table and 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     sbin_iptables = "/sbin/iptables";
690   else if (0 == access ("/usr/sbin/iptables", X_OK))
691     sbin_iptables = "/usr/sbin/iptables";
692   else
693   {
694     fprintf (stderr, 
695              "Fatal: executable iptables not found in approved directories: %s\n",
696              strerror (errno));
697     return 3;
698   }
699   if (0 == access ("/sbin/ip", X_OK))
700     sbin_ip = "/sbin/ip";
701   else if (0 == access ("/usr/sbin/ip", X_OK))
702     sbin_ip = "/usr/sbin/ip";
703   else
704   {
705     fprintf (stderr,
706              "Fatal: executable ip not found in approved directories: %s\n",
707              strerror (errno));
708     return 4;
709   }
710
711   /* validate port number */
712   port = atoi (argv[6]);
713   if ( (port == 0) || (port >= 65536) )
714   {
715     fprintf (stderr, 
716              "Port `%u' is invalid\n",
717              port);
718     return 2;
719   }
720   /* print port number to string for command-line use*/
721   (void) snprintf (localport,
722                    sizeof (localport), 
723                    "%u", 
724                    port);
725
726   /* do not die on SIGPIPE */
727   if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
728   {
729     fprintf (stderr, "Failed to protect against SIGPIPE: %s\n",
730              strerror (errno));
731     return 7;
732   }
733
734   /* setup pipe to shutdown nicely on SIGINT */
735   if (0 != pipe (cpipe))
736   {
737     fprintf (stderr, 
738              "Fatal: could not setup control pipe: %s\n",
739              strerror (errno));
740     return 6;
741   }
742   if (cpipe[0] >= FD_SETSIZE)
743   {
744     fprintf (stderr, "Pipe file descriptor to large: %d", cpipe[0]);
745     (void) close (cpipe[0]);
746     (void) close (cpipe[1]);
747     return 6;
748   }
749   {
750     /* make pipe non-blocking, as we theoretically could otherwise block
751        in the signal handler */
752     int flags = fcntl (cpipe[1], F_GETFL);
753     if (-1 == flags)
754     {
755       fprintf (stderr, "Failed to read flags for pipe: %s", strerror (errno));
756       (void) close (cpipe[0]);
757       (void) close (cpipe[1]);
758       return 6;
759     }
760     flags |= O_NONBLOCK;
761     if (0 != fcntl (cpipe[1], F_SETFL, flags))
762     {
763       fprintf (stderr, "Failed to make pipe non-blocking: %s", strerror (errno));
764       (void) close (cpipe[0]);
765       (void) close (cpipe[1]);
766       return 6;
767     }
768   }
769   if ( (SIG_ERR == signal (SIGTERM, &signal_handler)) ||
770        (SIG_ERR == signal (SIGINT, &signal_handler)) ||
771        (SIG_ERR == signal (SIGHUP, &signal_handler)) )       
772   { 
773     fprintf (stderr, 
774              "Fatal: could not initialize signal handler: %s\n",
775              strerror (errno));
776     (void) close (cpipe[0]);
777     (void) close (cpipe[1]);
778     return 7;   
779   }
780
781
782   /* get interface name */
783   strncpy (dev, argv[1], IFNAMSIZ);
784   dev[IFNAMSIZ - 1] = '\0';
785
786   /* now open virtual interface (first part that requires root) */
787   if (-1 == (fd_tun = init_tun (dev)))
788   {
789     fprintf (stderr, "Fatal: could not initialize tun-interface\n");
790     (void) signal (SIGTERM, SIG_IGN);
791     (void) signal (SIGINT, SIG_IGN);
792     (void) signal (SIGHUP, SIG_IGN);
793     (void) close (cpipe[0]);
794     (void) close (cpipe[1]);
795     return 5;
796   }
797
798   /* now set interface addresses */
799   {
800     const char *address = argv[2];
801     long prefix_len = atol (argv[3]);
802
803     if ((prefix_len < 1) || (prefix_len > 127))
804     {
805       fprintf (stderr, "Fatal: prefix_len out of range\n");
806       (void) signal (SIGTERM, SIG_IGN);
807       (void) signal (SIGINT, SIG_IGN);
808       (void) signal (SIGHUP, SIG_IGN);
809       (void) close (cpipe[0]);
810       (void) close (cpipe[1]);
811       return 2;
812     }
813     set_address6 (dev, address, prefix_len);
814   }
815
816   {
817     const char *address = argv[4];
818     const char *mask = argv[5];
819
820     set_address4 (dev, address, mask);
821   }
822   
823   /* update routing tables -- next part why we need SUID! */
824   /* Forward everything from the given local port (with destination
825      to port 53, and only for UDP) without hijacking */
826   r = 8; /* failed to fully setup routing table */
827   {
828     char *const mangle_args[] = 
829       {
830         "iptables", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
831         "udp", "--sport", localport, "--dport", DNS_PORT, "-j",
832         "ACCEPT", NULL
833       };
834     if (0 != fork_and_exec (sbin_iptables, mangle_args))
835       goto cleanup_rest;
836   }    
837   /* Mark all of the other DNS traffic using our mark DNS_MARK */
838   {
839     char *const mark_args[] =
840       {
841         "iptables", "-t", "mangle", "-I", "OUTPUT", "2", "-p",
842         "udp", "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK,
843         NULL
844       };
845     if (0 != fork_and_exec (sbin_iptables, mark_args))
846       goto cleanup_mangle_1;
847   }
848   /* Forward all marked DNS traffic to our DNS_TABLE */
849   {
850     char *const forward_args[] =
851       {
852         "ip", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
853       };
854     if (0 != fork_and_exec (sbin_ip, forward_args))
855       goto cleanup_mark_2;
856   }
857   /* Finally, add rule in our forwarding table to pass to our virtual interface */
858   {
859     char *const route_args[] =
860       {
861         "ip", "route", "add", "default", "dev", dev,
862         "table", DNS_TABLE, NULL
863       };
864     if (0 != fork_and_exec (sbin_ip, route_args))
865       goto cleanup_forward_3;
866   }
867
868   /* drop privs *except* for the saved UID; this is not perfect, but better
869      than doing nothing */
870   uid_t uid = getuid ();
871 #ifdef HAVE_SETRESUID
872   if (0 != setresuid (uid, uid, 0))
873   {
874     fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
875     r = 24;
876     goto cleanup_route_4;
877   }
878 #else
879   /* Note: no 'setuid' here as we must keep our saved UID as root */
880   if (0 != seteuid (uid)) 
881   {
882     fprintf (stderr, "Failed to seteuid: %s\n", strerror (errno));
883     r = 24;
884     goto cleanup_route_4;
885   }
886 #endif
887
888   r = 0; /* did fully setup routing table (if nothing else happens, we were successful!) */
889
890   /* now forward until we hit a problem */
891    run (fd_tun);
892   
893   /* now need to regain privs so we can remove the firewall rules we added! */
894 #ifdef HAVE_SETRESUID
895   if (0 != setresuid (uid, 0, 0))
896   {
897     fprintf (stderr, "Failed to setresuid back to root: %s\n", strerror (errno));
898     r = 40;
899     goto cleanup_route_4;
900   }
901 #else
902   if (0 != seteuid (0)) 
903   {
904     fprintf (stderr, "Failed to seteuid back to root: %s\n", strerror (errno));
905     r = 40;
906     goto cleanup_route_4;
907   }
908 #endif
909  
910   /* update routing tables again -- this is why we could not fully drop privs */
911   /* now undo updating of routing tables; normal exit or clean-up-on-error case */
912  cleanup_route_4:
913   {
914     char *const route_clean_args[] =                     
915       {
916         "ip", "route", "del", "default", "dev", dev,
917         "table", DNS_TABLE, NULL
918       };
919     if (0 != fork_and_exec (sbin_ip, route_clean_args))
920       r += 1;
921   }
922  cleanup_forward_3:
923   {
924     char *const forward_clean_args[] =
925       {
926         "ip", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
927       };
928     if (0 != fork_and_exec (sbin_ip, forward_clean_args))
929       r += 2;   
930   }
931  cleanup_mark_2:
932   {
933     char *const mark_clean_args[] =
934       {
935         "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
936         "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK, NULL
937       };
938     if (0 != fork_and_exec (sbin_iptables, mark_clean_args))
939       r += 4;
940   }     
941  cleanup_mangle_1:
942   {
943     char *const mangle_clean_args[] =
944       {
945         "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
946         "--sport", localport, "--dport", DNS_PORT, "-j", "ACCEPT",
947         NULL
948       };
949     if (0 != fork_and_exec (sbin_iptables, mangle_clean_args))
950       r += 8;
951   }
952
953  cleanup_rest:
954   /* close virtual interface */
955   (void) close (fd_tun);
956   /* remove signal handler so we can close the pipes */
957   (void) signal (SIGTERM, SIG_IGN);
958   (void) signal (SIGINT, SIG_IGN);
959   (void) signal (SIGHUP, SIG_IGN);
960   (void) close (cpipe[0]);
961   (void) close (cpipe[1]);
962   return r;
963 }
964
965 /* end of gnunet-helper-dns.c */