- adapt mesh to strided regex implementation
[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             {
545               buftun_size = 0;
546               continue;
547             }
548           fprintf (stderr, "read-error: %s\n", strerror (errno));
549           return;
550         }
551         if (0 == buftun_size)
552         {
553           fprintf (stderr, "EOF on tun\n");
554           return;
555         }
556         buftun_read = buftun;
557         {
558           struct GNUNET_MessageHeader *hdr =
559               (struct GNUNET_MessageHeader *) buftun;
560           buftun_size += sizeof (struct GNUNET_MessageHeader);
561           hdr->type = htons (GNUNET_MESSAGE_TYPE_DNS_HELPER);
562           hdr->size = htons (buftun_size);
563         }
564       }
565       else if (FD_ISSET (1, &fds_w))
566       {
567         ssize_t written = write (1, buftun_read, buftun_size);
568
569         if (-1 == written)
570         {
571           if ( (errno == EINTR) ||
572                (errno == EAGAIN) )
573             continue;
574           fprintf (stderr, "write-error to stdout: %s\n", strerror (errno));
575           return;
576         }
577         if (0 == written)
578         {
579           fprintf (stderr, "write returned 0\n");
580           return;
581         }
582         buftun_size -= written;
583         buftun_read += written;        
584       }
585
586       if (FD_ISSET (0, &fds_r))
587       {
588         bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos);
589         if (-1 == bufin_size)
590         {
591           bufin_read = NULL;
592           if ( (errno == EINTR) ||
593                (errno == EAGAIN) )
594             continue;
595           fprintf (stderr, "read-error: %s\n", strerror (errno));
596           return;
597         }
598         if (0 == bufin_size)
599         {
600           bufin_read = NULL;
601           fprintf (stderr, "EOF on stdin\n");
602           return;
603         }
604         {
605           struct GNUNET_MessageHeader *hdr;
606
607 PROCESS_BUFFER:
608           bufin_rpos += bufin_size;
609           if (bufin_rpos < sizeof (struct GNUNET_MessageHeader))
610             continue;
611           hdr = (struct GNUNET_MessageHeader *) bufin;
612           if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_DNS_HELPER)
613           {
614             fprintf (stderr, "protocol violation!\n");
615             return;
616           }
617           if (ntohs (hdr->size) > bufin_rpos)
618             continue;
619           bufin_read = bufin + sizeof (struct GNUNET_MessageHeader);
620           bufin_size = ntohs (hdr->size) - sizeof (struct GNUNET_MessageHeader);
621           bufin_rpos -= bufin_size + sizeof (struct GNUNET_MessageHeader);
622         }
623       }
624       else if (FD_ISSET (fd_tun, &fds_w))
625       {
626         ssize_t written = write (fd_tun, bufin_read, bufin_size);
627
628         if (-1 == written)
629         {
630           if ( (errno == EINTR) ||
631                (errno == EAGAIN) )
632             continue;
633           fprintf (stderr, "write-error to tun: %s\n", strerror (errno));
634           return;
635         }
636         if (0 == written)
637         {
638           fprintf (stderr, "write returned 0\n");
639           return;
640         }
641         {
642           bufin_size -= written;
643           bufin_read += written;
644           if (0 == bufin_size)
645           {
646             memmove (bufin, bufin_read, bufin_rpos);
647             bufin_read = NULL;  /* start reading again */
648             bufin_size = 0;
649             goto PROCESS_BUFFER;
650           }
651         }
652       }
653     }
654   }
655 }
656
657
658 /**
659  * Main function of "gnunet-helper-dns", which opens a VPN tunnel interface,
660  * redirects all outgoing DNS traffic (except from the specified port) to that
661  * interface and then passes traffic from and to the interface via stdin/stdout.
662  *
663  * Once stdin/stdout close or have other errors, the tunnel is closed and the
664  * DNS traffic redirection is stopped.
665  *
666  * @param argc number of arguments
667  * @param argv 0: binary name (should be "gnunet-helper-vpn")
668  *             1: tunnel interface name (typically "gnunet-dns")
669  *             2: IPv6 address for the tunnel ("FE80::1")
670  *             3: IPv6 netmask length in bits ("64")
671  *             4: IPv4 address for the tunnel ("1.2.3.4")
672  *             5: IPv4 netmask ("255.255.0.0")
673  * @return 0 on success, otherwise code indicating type of error:
674  *         1 wrong number of arguments
675  *         2 invalid arguments (i.e. port number / prefix length wrong)
676  *         3 iptables not executable
677  *         4 ip not executable
678  *         5 failed to initialize tunnel interface
679  *         6 failed to initialize control pipe
680  *         8 failed to change routing table, cleanup successful
681  *         9-23 failed to change routing table and failed to undo some changes to routing table
682  *         24 failed to drop privs
683  *         25-39 failed to drop privs and then failed to undo some changes to routing table
684  *         40 failed to regain privs
685  *         41-55 failed to regain prisv and then failed to undo some changes to routing table
686  *         255 failed to handle kill signal properly
687  */
688 int
689 main (int argc, char *const*argv)
690 {
691   int r;
692   char dev[IFNAMSIZ];
693   char mygid[32];
694   int fd_tun;
695
696   if (6 != argc)
697   {
698     fprintf (stderr, "Fatal: must supply 6 arguments!\n");
699     return 1;
700   }
701
702   /* verify that the binaries were care about are executable */
703   if (0 == access ("/sbin/iptables", X_OK))
704     sbin_iptables = "/sbin/iptables";
705   else if (0 == access ("/usr/sbin/iptables", X_OK))
706     sbin_iptables = "/usr/sbin/iptables";
707   else
708   {
709     fprintf (stderr, 
710              "Fatal: executable iptables not found in approved directories: %s\n",
711              strerror (errno));
712     return 3;
713   }
714   if (0 == access ("/sbin/ip", X_OK))
715     sbin_ip = "/sbin/ip";
716   else if (0 == access ("/usr/sbin/ip", X_OK))
717     sbin_ip = "/usr/sbin/ip";
718   else
719   {
720     fprintf (stderr,
721              "Fatal: executable ip not found in approved directories: %s\n",
722              strerror (errno));
723     return 4;
724   }
725   if (0 == access ("/sbin/sysctl", X_OK))
726     sbin_sysctl = "/sbin/sysctl";
727   else if (0 == access ("/usr/sbin/sysctl", X_OK))
728     sbin_sysctl = "/usr/sbin/sysctl";
729   else
730   {
731     fprintf (stderr,
732              "Fatal: executable sysctl not found in approved directories: %s\n",
733              strerror (errno));
734     return 5;
735   }
736
737   /* setup 'mygid' string */
738   snprintf (mygid, sizeof (mygid), "%d", (int) getegid());
739
740   /* do not die on SIGPIPE */
741   if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
742   {
743     fprintf (stderr, "Failed to protect against SIGPIPE: %s\n",
744              strerror (errno));
745     return 7;
746   }
747
748   /* setup pipe to shutdown nicely on SIGINT */
749   if (0 != pipe (cpipe))
750   {
751     fprintf (stderr, 
752              "Fatal: could not setup control pipe: %s\n",
753              strerror (errno));
754     return 6;
755   }
756   if (cpipe[0] >= FD_SETSIZE)
757   {
758     fprintf (stderr, "Pipe file descriptor to large: %d", cpipe[0]);
759     (void) close (cpipe[0]);
760     (void) close (cpipe[1]);
761     return 6;
762   }
763   {
764     /* make pipe non-blocking, as we theoretically could otherwise block
765        in the signal handler */
766     int flags = fcntl (cpipe[1], F_GETFL);
767     if (-1 == flags)
768     {
769       fprintf (stderr, "Failed to read flags for pipe: %s", strerror (errno));
770       (void) close (cpipe[0]);
771       (void) close (cpipe[1]);
772       return 6;
773     }
774     flags |= O_NONBLOCK;
775     if (0 != fcntl (cpipe[1], F_SETFL, flags))
776     {
777       fprintf (stderr, "Failed to make pipe non-blocking: %s", strerror (errno));
778       (void) close (cpipe[0]);
779       (void) close (cpipe[1]);
780       return 6;
781     }
782   }
783   if ( (SIG_ERR == signal (SIGTERM, &signal_handler)) ||
784        (SIG_ERR == signal (SIGINT, &signal_handler)) ||
785        (SIG_ERR == signal (SIGHUP, &signal_handler)) )       
786   { 
787     fprintf (stderr, 
788              "Fatal: could not initialize signal handler: %s\n",
789              strerror (errno));
790     (void) close (cpipe[0]);
791     (void) close (cpipe[1]);
792     return 7;   
793   }
794
795
796   /* get interface name */
797   strncpy (dev, argv[1], IFNAMSIZ);
798   dev[IFNAMSIZ - 1] = '\0';
799
800   /* Disable rp filtering */
801   {
802     char *const sysctl_args[] = {"sysctl", "-w",
803       "net.ipv4.conf.all.rp_filter=0", NULL};
804     char *const sysctl_args2[] = {"sysctl", "-w",
805       "net.ipv4.conf.default.rp_filter=0", NULL};
806     if ((0 != fork_and_exec (sbin_sysctl, sysctl_args)) ||
807         (0 != fork_and_exec (sbin_sysctl, sysctl_args2)))
808     {
809       fprintf (stderr,
810                "Failed to disable rp filtering.\n");
811       return 5;
812     }
813   }
814   
815   
816   /* now open virtual interface (first part that requires root) */
817   if (-1 == (fd_tun = init_tun (dev)))
818   {
819     fprintf (stderr, "Fatal: could not initialize tun-interface\n");
820     (void) signal (SIGTERM, SIG_IGN);
821     (void) signal (SIGINT, SIG_IGN);
822     (void) signal (SIGHUP, SIG_IGN);
823     (void) close (cpipe[0]);
824     (void) close (cpipe[1]);
825     return 5;
826   }
827
828   /* now set interface addresses */
829   {
830     const char *address = argv[2];
831     long prefix_len = atol (argv[3]);
832
833     if ((prefix_len < 1) || (prefix_len > 127))
834     {
835       fprintf (stderr, "Fatal: prefix_len out of range\n");
836       (void) signal (SIGTERM, SIG_IGN);
837       (void) signal (SIGINT, SIG_IGN);
838       (void) signal (SIGHUP, SIG_IGN);
839       (void) close (cpipe[0]);
840       (void) close (cpipe[1]);
841       return 2;
842     }
843     set_address6 (dev, address, prefix_len);
844   }
845
846   {
847     const char *address = argv[4];
848     const char *mask = argv[5];
849
850     set_address4 (dev, address, mask);
851   }
852
853   
854   /* update routing tables -- next part why we need SUID! */
855   /* Forward everything from our EGID (which should only be held
856      by the 'gnunet-service-dns') and with destination
857      to port 53 on UDP, without hijacking */
858   r = 8; /* failed to fully setup routing table */
859   {
860     char *const mangle_args[] = 
861       {
862         "iptables", "-m", "owner", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
863         "udp", "--gid-owner", mygid, "--dport", DNS_PORT, "-j",
864         "ACCEPT", NULL
865       };
866     if (0 != fork_and_exec (sbin_iptables, mangle_args))
867       goto cleanup_rest;
868   }    
869   /* Mark all of the other DNS traffic using our mark DNS_MARK */
870   {
871     char *const mark_args[] =
872       {
873         "iptables", "-t", "mangle", "-I", "OUTPUT", "2", "-p",
874         "udp", "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK,
875         NULL
876       };
877     if (0 != fork_and_exec (sbin_iptables, mark_args))
878       goto cleanup_mangle_1;
879   }
880   /* Forward all marked DNS traffic to our DNS_TABLE */
881   {
882     char *const forward_args[] =
883       {
884         "ip", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
885       };
886     if (0 != fork_and_exec (sbin_ip, forward_args))
887       goto cleanup_mark_2;
888   }
889   /* Finally, add rule in our forwarding table to pass to our virtual interface */
890   {
891     char *const route_args[] =
892       {
893         "ip", "route", "add", "default", "dev", dev,
894         "table", DNS_TABLE, NULL
895       };
896     if (0 != fork_and_exec (sbin_ip, route_args))
897       goto cleanup_forward_3;
898   }
899
900   /* drop privs *except* for the saved UID; this is not perfect, but better
901      than doing nothing */
902   uid_t uid = getuid ();
903 #ifdef HAVE_SETRESUID
904   if (0 != setresuid (uid, uid, 0))
905   {
906     fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
907     r = 24;
908     goto cleanup_route_4;
909   }
910 #else
911   /* Note: no 'setuid' here as we must keep our saved UID as root */
912   if (0 != seteuid (uid)) 
913   {
914     fprintf (stderr, "Failed to seteuid: %s\n", strerror (errno));
915     r = 24;
916     goto cleanup_route_4;
917   }
918 #endif
919
920   r = 0; /* did fully setup routing table (if nothing else happens, we were successful!) */
921
922   /* now forward until we hit a problem */
923    run (fd_tun);
924   
925   /* now need to regain privs so we can remove the firewall rules we added! */
926 #ifdef HAVE_SETRESUID
927   if (0 != setresuid (uid, 0, 0))
928   {
929     fprintf (stderr, "Failed to setresuid back to root: %s\n", strerror (errno));
930     r = 40;
931     goto cleanup_route_4;
932   }
933 #else
934   if (0 != seteuid (0)) 
935   {
936     fprintf (stderr, "Failed to seteuid back to root: %s\n", strerror (errno));
937     r = 40;
938     goto cleanup_route_4;
939   }
940 #endif
941  
942   /* update routing tables again -- this is why we could not fully drop privs */
943   /* now undo updating of routing tables; normal exit or clean-up-on-error case */
944  cleanup_route_4:
945   {
946     char *const route_clean_args[] =                     
947       {
948         "ip", "route", "del", "default", "dev", dev,
949         "table", DNS_TABLE, NULL
950       };
951     if (0 != fork_and_exec (sbin_ip, route_clean_args))
952       r += 1;
953   }
954  cleanup_forward_3:
955   {
956     char *const forward_clean_args[] =
957       {
958         "ip", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
959       };
960     if (0 != fork_and_exec (sbin_ip, forward_clean_args))
961       r += 2;   
962   }
963  cleanup_mark_2:
964   {
965     char *const mark_clean_args[] =
966       {
967         "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
968         "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK, NULL
969       };
970     if (0 != fork_and_exec (sbin_iptables, mark_clean_args))
971       r += 4;
972   }     
973  cleanup_mangle_1:
974   {
975     char *const mangle_clean_args[] =
976       {
977         "iptables", "-m", "owner", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
978          "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT",
979         NULL
980       };
981     if (0 != fork_and_exec (sbin_iptables, mangle_clean_args))
982       r += 8;
983   }
984
985  cleanup_rest:
986   /* close virtual interface */
987   (void) close (fd_tun);
988   /* remove signal handler so we can close the pipes */
989   (void) signal (SIGTERM, SIG_IGN);
990   (void) signal (SIGINT, SIG_IGN);
991   (void) signal (SIGHUP, SIG_IGN);
992   (void) close (cpipe[0]);
993   (void) close (cpipe[1]);
994   return r;
995 }
996
997 /* end of gnunet-helper-dns.c */