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