-fix
[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     (void) close (fd);
237     return -1;
238   }
239
240   memset (&ifr, 0, sizeof (ifr));
241   ifr.ifr_flags = IFF_TUN;
242
243   if ('\0' != *dev)
244     strncpy (ifr.ifr_name, dev, IFNAMSIZ);
245
246   if (-1 == ioctl (fd, TUNSETIFF, (void *) &ifr))
247   {
248     fprintf (stderr, "Error with ioctl on `%s': %s\n", "/dev/net/tun",
249              strerror (errno));
250     (void) close (fd);
251     return -1;
252   }
253   strcpy (dev, ifr.ifr_name);
254   return fd;
255 }
256
257
258 /**
259  * @brief Sets the IPv6-Address given in address on the interface dev
260  *
261  * @param dev the interface to configure
262  * @param address the IPv6-Address
263  * @param prefix_len the length of the network-prefix
264  */
265 static void
266 set_address6 (const char *dev, const char *address, unsigned long prefix_len)
267 {
268   struct ifreq ifr;
269   struct in6_ifreq ifr6;
270   struct sockaddr_in6 sa6;
271   int fd;
272
273   /*
274    * parse the new address
275    */
276   memset (&sa6, 0, sizeof (struct sockaddr_in6));
277   sa6.sin6_family = AF_INET6;
278   if (1 != inet_pton (AF_INET6, address, sa6.sin6_addr.s6_addr))
279   {
280     fprintf (stderr, "Failed to parse address `%s': %s\n", address,
281              strerror (errno));
282     exit (1);
283   }
284
285   if (-1 == (fd = socket (PF_INET6, SOCK_DGRAM, 0)))
286   {
287     fprintf (stderr, "Error creating socket: %s\n", strerror (errno));
288     exit (1);
289   }
290
291   memset (&ifr, 0, sizeof (struct ifreq));
292   /*
293    * Get the index of the if
294    */
295   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
296   if (-1 == ioctl (fd, SIOGIFINDEX, &ifr))
297   {
298     fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
299     (void) close (fd);
300     exit (1);
301   }
302
303   memset (&ifr6, 0, sizeof (struct in6_ifreq));
304   ifr6.ifr6_addr = sa6.sin6_addr;
305   ifr6.ifr6_ifindex = ifr.ifr_ifindex;
306   ifr6.ifr6_prefixlen = prefix_len;
307
308   /*
309    * Set the address
310    */
311   if (-1 == ioctl (fd, SIOCSIFADDR, &ifr6))
312   {
313     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
314              strerror (errno));
315     (void) close (fd);
316     exit (1);
317   }
318
319   /*
320    * Get the flags
321    */
322   if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
323   {
324     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
325              strerror (errno));
326     (void) close (fd);
327     exit (1);
328   }
329
330   /*
331    * Add the UP and RUNNING flags
332    */
333   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
334   if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
335   {
336     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
337              strerror (errno));
338     (void) close (fd);
339     exit (1);
340   }
341
342   if (0 != close (fd))
343   {
344     fprintf (stderr, "close failed: %s\n", strerror (errno));
345     exit (1);
346   }
347 }
348
349
350 /**
351  * @brief Sets the IPv4-Address given in address on the interface dev
352  *
353  * @param dev the interface to configure
354  * @param address the IPv4-Address
355  * @param mask the netmask
356  */
357 static void
358 set_address4 (const char *dev, const char *address, const char *mask)
359 {
360   int fd;
361   struct sockaddr_in *addr;
362   struct ifreq ifr;
363
364   memset (&ifr, 0, sizeof (struct ifreq));
365   addr = (struct sockaddr_in *) &(ifr.ifr_addr);
366   addr->sin_family = AF_INET;
367
368   /*
369    * Parse the address
370    */
371   if (1 != inet_pton (AF_INET, address, &addr->sin_addr.s_addr))
372   {
373     fprintf (stderr, "Failed to parse address `%s': %s\n", address,
374              strerror (errno));
375     exit (1);
376   }
377
378   if (-1 == (fd = socket (PF_INET, SOCK_DGRAM, 0)))
379   {
380     fprintf (stderr, "Error creating socket: %s\n", strerror (errno));
381     exit (1);
382   }
383
384   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
385
386   /*
387    * Set the address
388    */
389   if (-1 == ioctl (fd, SIOCSIFADDR, &ifr))
390   {
391     fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
392     (void) close (fd);
393     exit (1);
394   }
395
396   /*
397    * Parse the netmask
398    */
399   addr = (struct sockaddr_in *) &(ifr.ifr_netmask);
400   if (1 != inet_pton (AF_INET, mask, &addr->sin_addr.s_addr))
401   {
402     fprintf (stderr, "Failed to parse address `%s': %s\n", mask,
403              strerror (errno));
404     (void) close (fd);
405     exit (1);
406   }
407
408   /*
409    * Set the netmask
410    */
411   if (-1 == ioctl (fd, SIOCSIFNETMASK, &ifr))
412   {
413     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
414              strerror (errno));
415     (void) close (fd);
416     exit (1);
417   }
418
419   /*
420    * Get the flags
421    */
422   if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
423   {
424     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
425              strerror (errno));
426     (void) close (fd);
427     exit (1);
428   }
429
430   /*
431    * Add the UP and RUNNING flags
432    */
433   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
434   if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
435   {
436     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
437              strerror (errno));
438     (void) close (fd);
439     exit (1);
440   }
441
442   if (0 != close (fd))
443   {
444     fprintf (stderr, "close failed: %s\n", strerror (errno));
445     (void) close (fd);
446     exit (1);
447   }
448 }
449
450
451 /**
452  * Start forwarding to and from the tunnel.  This function runs with
453  * "reduced" priviledges (saved UID is still 0, but effective UID is
454  * the real user ID).
455  *
456  * @param fd_tun tunnel FD
457  */
458 static void
459 run (int fd_tun)
460 {
461   /*
462    * The buffer filled by reading from fd_tun
463    */
464   unsigned char buftun[MAX_SIZE];
465   ssize_t buftun_size = 0;
466   unsigned char *buftun_read = NULL;
467
468   /*
469    * The buffer filled by reading from stdin
470    */
471   unsigned char bufin[MAX_SIZE];
472   ssize_t bufin_size = 0;
473   size_t bufin_rpos = 0;
474   unsigned char *bufin_read = NULL;
475   fd_set fds_w;
476   fd_set fds_r;
477   int max;
478
479   while (1)
480   {
481     FD_ZERO (&fds_w);
482     FD_ZERO (&fds_r);
483
484     /*
485      * We are supposed to read and the buffer is empty
486      * -> select on read from tun
487      */
488     if (0 == buftun_size)
489       FD_SET (fd_tun, &fds_r);
490
491     /*
492      * We are supposed to read and the buffer is not empty
493      * -> select on write to stdout
494      */
495     if (0 != buftun_size)
496       FD_SET (1, &fds_w);
497
498     /*
499      * We are supposed to write and the buffer is empty
500      * -> select on read from stdin
501      */
502     if (NULL == bufin_read)
503       FD_SET (0, &fds_r);
504
505     /*
506      * We are supposed to write and the buffer is not empty
507      * -> select on write to tun
508      */
509     if (NULL != bufin_read)
510       FD_SET (fd_tun, &fds_w);
511
512     FD_SET (cpipe[0], &fds_r);
513     max = (fd_tun > cpipe[0]) ? fd_tun : cpipe[0];
514
515     int r = select (max + 1, &fds_r, &fds_w, NULL, NULL);
516
517     if (-1 == r)
518     {
519       if (EINTR == errno)
520         continue;
521       fprintf (stderr, "select failed: %s\n", strerror (errno));
522       return;
523     }
524
525     if (r > 0)
526     {
527       if (FD_ISSET (cpipe[0], &fds_r))
528         return; /* aborted by signal */
529
530       if (FD_ISSET (fd_tun, &fds_r))
531       {
532         buftun_size =
533             read (fd_tun, buftun + sizeof (struct GNUNET_MessageHeader),
534                   MAX_SIZE - sizeof (struct GNUNET_MessageHeader));
535         if (-1 == buftun_size)
536         {
537           if ( (errno == EINTR) ||
538                (errno == EAGAIN) )
539             continue;
540           fprintf (stderr, "read-error: %s\n", strerror (errno));
541           return;
542         }
543         if (0 == buftun_size)
544         {
545           fprintf (stderr, "EOF on tun\n");
546           return;
547         }
548         buftun_read = buftun;
549         {
550           struct GNUNET_MessageHeader *hdr =
551               (struct GNUNET_MessageHeader *) buftun;
552           buftun_size += sizeof (struct GNUNET_MessageHeader);
553           hdr->type = htons (GNUNET_MESSAGE_TYPE_DNS_HELPER);
554           hdr->size = htons (buftun_size);
555         }
556       }
557       else if (FD_ISSET (1, &fds_w))
558       {
559         ssize_t written = write (1, buftun_read, buftun_size);
560
561         if (-1 == written)
562         {
563           if ( (errno == EINTR) ||
564                (errno == EAGAIN) )
565             continue;
566           fprintf (stderr, "write-error to stdout: %s\n", strerror (errno));
567           return;
568         }
569         if (0 == written)
570         {
571           fprintf (stderr, "write returned 0\n");
572           return;
573         }
574         buftun_size -= written;
575         buftun_read += written;        
576       }
577
578       if (FD_ISSET (0, &fds_r))
579       {
580         bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos);
581         if (-1 == bufin_size)
582         {
583           bufin_read = NULL;
584           if ( (errno == EINTR) ||
585                (errno == EAGAIN) )
586             continue;
587           fprintf (stderr, "read-error: %s\n", strerror (errno));
588           return;
589         }
590         if (0 == bufin_size)
591         {
592           bufin_read = NULL;
593           fprintf (stderr, "EOF on stdin\n");
594           return;
595         }
596         {
597           struct GNUNET_MessageHeader *hdr;
598
599 PROCESS_BUFFER:
600           bufin_rpos += bufin_size;
601           if (bufin_rpos < sizeof (struct GNUNET_MessageHeader))
602             continue;
603           hdr = (struct GNUNET_MessageHeader *) bufin;
604           if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_DNS_HELPER)
605           {
606             fprintf (stderr, "protocol violation!\n");
607             return;
608           }
609           if (ntohs (hdr->size) > bufin_rpos)
610             continue;
611           bufin_read = bufin + sizeof (struct GNUNET_MessageHeader);
612           bufin_size = ntohs (hdr->size) - sizeof (struct GNUNET_MessageHeader);
613           bufin_rpos -= bufin_size + sizeof (struct GNUNET_MessageHeader);
614         }
615       }
616       else if (FD_ISSET (fd_tun, &fds_w))
617       {
618         ssize_t written = write (fd_tun, bufin_read, bufin_size);
619
620         if (-1 == written)
621         {
622           if ( (errno == EINTR) ||
623                (errno == EAGAIN) )
624             continue;
625           fprintf (stderr, "write-error to tun: %s\n", strerror (errno));
626           return;
627         }
628         if (0 == written)
629         {
630           fprintf (stderr, "write returned 0\n");
631           return;
632         }
633         {
634           bufin_size -= written;
635           bufin_read += written;
636           if (0 == bufin_size)
637           {
638             memmove (bufin, bufin_read, bufin_rpos);
639             bufin_read = NULL;  /* start reading again */
640             bufin_size = 0;
641             goto PROCESS_BUFFER;
642           }
643         }
644       }
645     }
646   }
647 }
648
649
650 /**
651  * Main function of "gnunet-helper-dns", which opens a VPN tunnel interface,
652  * redirects all outgoing DNS traffic (except from the specified port) to that
653  * interface and then passes traffic from and to the interface via stdin/stdout.
654  *
655  * Once stdin/stdout close or have other errors, the tunnel is closed and the
656  * DNS traffic redirection is stopped.
657  *
658  * @param argc number of arguments
659  * @param argv 0: binary name (should be "gnunet-helper-vpn")
660  *             1: tunnel interface name (typically "gnunet-dns")
661  *             2: IPv6 address for the tunnel ("FE80::1")
662  *             3: IPv6 netmask length in bits ("64")
663  *             4: IPv4 address for the tunnel ("1.2.3.4")
664  *             5: IPv4 netmask ("255.255.0.0")
665  * @return 0 on success, otherwise code indicating type of error:
666  *         1 wrong number of arguments
667  *         2 invalid arguments (i.e. port number / prefix length wrong)
668  *         3 iptables not executable
669  *         4 ip not executable
670  *         5 failed to initialize tunnel interface
671  *         6 failed to initialize control pipe
672  *         8 failed to change routing table, cleanup successful
673  *         9-23 failed to change routing table and failed to undo some changes to routing table
674  *         24 failed to drop privs
675  *         25-39 failed to drop privs and then failed to undo some changes to routing table
676  *         40 failed to regain privs
677  *         41-55 failed to regain prisv and then failed to undo some changes to routing table
678  *         255 failed to handle kill signal properly
679  */
680 int
681 main (int argc, char *const*argv)
682 {
683   int r;
684   char dev[IFNAMSIZ];
685   char mygid[32];
686   int fd_tun;
687
688   if (6 != argc)
689   {
690     fprintf (stderr, "Fatal: must supply 6 arguments!\n");
691     return 1;
692   }
693
694   /* verify that the binaries were care about are executable */
695   if (0 == access ("/sbin/iptables", X_OK))
696     sbin_iptables = "/sbin/iptables";
697   else if (0 == access ("/usr/sbin/iptables", X_OK))
698     sbin_iptables = "/usr/sbin/iptables";
699   else
700   {
701     fprintf (stderr, 
702              "Fatal: executable iptables not found in approved directories: %s\n",
703              strerror (errno));
704     return 3;
705   }
706   if (0 == access ("/sbin/ip", X_OK))
707     sbin_ip = "/sbin/ip";
708   else if (0 == access ("/usr/sbin/ip", X_OK))
709     sbin_ip = "/usr/sbin/ip";
710   else
711   {
712     fprintf (stderr,
713              "Fatal: executable ip not found in approved directories: %s\n",
714              strerror (errno));
715     return 4;
716   }
717
718   /* setup 'mygid' string */
719   snprintf (mygid, sizeof (mygid), "%d", (int) getegid());
720
721   /* do not die on SIGPIPE */
722   if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
723   {
724     fprintf (stderr, "Failed to protect against SIGPIPE: %s\n",
725              strerror (errno));
726     return 7;
727   }
728
729   /* setup pipe to shutdown nicely on SIGINT */
730   if (0 != pipe (cpipe))
731   {
732     fprintf (stderr, 
733              "Fatal: could not setup control pipe: %s\n",
734              strerror (errno));
735     return 6;
736   }
737   if (cpipe[0] >= FD_SETSIZE)
738   {
739     fprintf (stderr, "Pipe file descriptor to large: %d", cpipe[0]);
740     (void) close (cpipe[0]);
741     (void) close (cpipe[1]);
742     return 6;
743   }
744   {
745     /* make pipe non-blocking, as we theoretically could otherwise block
746        in the signal handler */
747     int flags = fcntl (cpipe[1], F_GETFL);
748     if (-1 == flags)
749     {
750       fprintf (stderr, "Failed to read flags for pipe: %s", strerror (errno));
751       (void) close (cpipe[0]);
752       (void) close (cpipe[1]);
753       return 6;
754     }
755     flags |= O_NONBLOCK;
756     if (0 != fcntl (cpipe[1], F_SETFL, flags))
757     {
758       fprintf (stderr, "Failed to make pipe non-blocking: %s", strerror (errno));
759       (void) close (cpipe[0]);
760       (void) close (cpipe[1]);
761       return 6;
762     }
763   }
764   if ( (SIG_ERR == signal (SIGTERM, &signal_handler)) ||
765        (SIG_ERR == signal (SIGINT, &signal_handler)) ||
766        (SIG_ERR == signal (SIGHUP, &signal_handler)) )       
767   { 
768     fprintf (stderr, 
769              "Fatal: could not initialize signal handler: %s\n",
770              strerror (errno));
771     (void) close (cpipe[0]);
772     (void) close (cpipe[1]);
773     return 7;   
774   }
775
776
777   /* get interface name */
778   strncpy (dev, argv[1], IFNAMSIZ);
779   dev[IFNAMSIZ - 1] = '\0';
780
781   /* now open virtual interface (first part that requires root) */
782   if (-1 == (fd_tun = init_tun (dev)))
783   {
784     fprintf (stderr, "Fatal: could not initialize tun-interface\n");
785     (void) signal (SIGTERM, SIG_IGN);
786     (void) signal (SIGINT, SIG_IGN);
787     (void) signal (SIGHUP, SIG_IGN);
788     (void) close (cpipe[0]);
789     (void) close (cpipe[1]);
790     return 5;
791   }
792
793   /* now set interface addresses */
794   {
795     const char *address = argv[2];
796     long prefix_len = atol (argv[3]);
797
798     if ((prefix_len < 1) || (prefix_len > 127))
799     {
800       fprintf (stderr, "Fatal: prefix_len out of range\n");
801       (void) signal (SIGTERM, SIG_IGN);
802       (void) signal (SIGINT, SIG_IGN);
803       (void) signal (SIGHUP, SIG_IGN);
804       (void) close (cpipe[0]);
805       (void) close (cpipe[1]);
806       return 2;
807     }
808     set_address6 (dev, address, prefix_len);
809   }
810
811   {
812     const char *address = argv[4];
813     const char *mask = argv[5];
814
815     set_address4 (dev, address, mask);
816   }
817   
818   /* update routing tables -- next part why we need SUID! */
819   /* Forward everything from our EGID (which should only be held
820      by the 'gnunet-service-dns') and with destination
821      to port 53 on UDP, without hijacking */
822   r = 8; /* failed to fully setup routing table */
823   {
824     char *const mangle_args[] = 
825       {
826         "iptables", "-m", "owner", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
827         "udp", "--gid-owner", mygid, "--dport", DNS_PORT, "-j",
828         "ACCEPT", NULL
829       };
830     if (0 != fork_and_exec (sbin_iptables, mangle_args))
831       goto cleanup_rest;
832   }    
833   /* Mark all of the other DNS traffic using our mark DNS_MARK */
834   {
835     char *const mark_args[] =
836       {
837         "iptables", "-t", "mangle", "-I", "OUTPUT", "2", "-p",
838         "udp", "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK,
839         NULL
840       };
841     if (0 != fork_and_exec (sbin_iptables, mark_args))
842       goto cleanup_mangle_1;
843   }
844   /* Forward all marked DNS traffic to our DNS_TABLE */
845   {
846     char *const forward_args[] =
847       {
848         "ip", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
849       };
850     if (0 != fork_and_exec (sbin_ip, forward_args))
851       goto cleanup_mark_2;
852   }
853   /* Finally, add rule in our forwarding table to pass to our virtual interface */
854   {
855     char *const route_args[] =
856       {
857         "ip", "route", "add", "default", "dev", dev,
858         "table", DNS_TABLE, NULL
859       };
860     if (0 != fork_and_exec (sbin_ip, route_args))
861       goto cleanup_forward_3;
862   }
863
864   /* drop privs *except* for the saved UID; this is not perfect, but better
865      than doing nothing */
866   uid_t uid = getuid ();
867 #ifdef HAVE_SETRESUID
868   if (0 != setresuid (uid, uid, 0))
869   {
870     fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
871     r = 24;
872     goto cleanup_route_4;
873   }
874 #else
875   /* Note: no 'setuid' here as we must keep our saved UID as root */
876   if (0 != seteuid (uid)) 
877   {
878     fprintf (stderr, "Failed to seteuid: %s\n", strerror (errno));
879     r = 24;
880     goto cleanup_route_4;
881   }
882 #endif
883
884   r = 0; /* did fully setup routing table (if nothing else happens, we were successful!) */
885
886   /* now forward until we hit a problem */
887    run (fd_tun);
888   
889   /* now need to regain privs so we can remove the firewall rules we added! */
890 #ifdef HAVE_SETRESUID
891   if (0 != setresuid (uid, 0, 0))
892   {
893     fprintf (stderr, "Failed to setresuid back to root: %s\n", strerror (errno));
894     r = 40;
895     goto cleanup_route_4;
896   }
897 #else
898   if (0 != seteuid (0)) 
899   {
900     fprintf (stderr, "Failed to seteuid back to root: %s\n", strerror (errno));
901     r = 40;
902     goto cleanup_route_4;
903   }
904 #endif
905  
906   /* update routing tables again -- this is why we could not fully drop privs */
907   /* now undo updating of routing tables; normal exit or clean-up-on-error case */
908  cleanup_route_4:
909   {
910     char *const route_clean_args[] =                     
911       {
912         "ip", "route", "del", "default", "dev", dev,
913         "table", DNS_TABLE, NULL
914       };
915     if (0 != fork_and_exec (sbin_ip, route_clean_args))
916       r += 1;
917   }
918  cleanup_forward_3:
919   {
920     char *const forward_clean_args[] =
921       {
922         "ip", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
923       };
924     if (0 != fork_and_exec (sbin_ip, forward_clean_args))
925       r += 2;   
926   }
927  cleanup_mark_2:
928   {
929     char *const mark_clean_args[] =
930       {
931         "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
932         "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK, NULL
933       };
934     if (0 != fork_and_exec (sbin_iptables, mark_clean_args))
935       r += 4;
936   }     
937  cleanup_mangle_1:
938   {
939     char *const mangle_clean_args[] =
940       {
941         "iptables", "-m", "owner", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
942          "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT",
943         NULL
944       };
945     if (0 != fork_and_exec (sbin_iptables, mangle_clean_args))
946       r += 8;
947   }
948
949  cleanup_rest:
950   /* close virtual interface */
951   (void) close (fd_tun);
952   /* remove signal handler so we can close the pipes */
953   (void) signal (SIGTERM, SIG_IGN);
954   (void) signal (SIGINT, SIG_IGN);
955   (void) signal (SIGHUP, SIG_IGN);
956   (void) close (cpipe[0]);
957   (void) close (cpipe[1]);
958   return r;
959 }
960
961 /* end of gnunet-helper-dns.c */