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