cleaning up set handlers, eliminating 2nd level demultiplexing and improving use...
[oweals/gnunet.git] / src / vpn / gnunet-helper-vpn.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010, 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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file vpn/gnunet-helper-vpn.c
23  * @brief the helper for the VPN service. Opens a virtual network-interface,
24  * sends data received on the if to stdout, sends data received on stdin to the
25  * interface
26  * @author Philipp Tölke
27  * @author Christian Grothoff
28  *
29  * The following list of people have reviewed this code and considered
30  * it safe since the last modification (if you reviewed it, please
31  * have your name added to the list):
32  *
33  * - Philipp Tölke
34  */
35 #include "platform.h"
36 #include <linux/if_tun.h>
37
38 /**
39  * Need 'struct GNUNET_MessageHeader'.
40  */
41 #include "gnunet_crypto_lib.h"
42 #include "gnunet_common.h"
43
44 /**
45  * Need VPN message types.
46  */
47 #include "gnunet_protocols.h"
48
49 /**
50  * Should we print (interesting|debug) messages that can happen during
51  * normal operation?
52  */
53 #define DEBUG GNUNET_NO
54
55 /**
56  * Maximum size of a GNUnet message (GNUNET_SERVER_MAX_MESSAGE_SIZE)
57  */
58 #define MAX_SIZE 65536
59
60 #ifndef _LINUX_IN6_H
61 /**
62  * This is in linux/include/net/ipv6.h, but not always exported...
63  */
64 struct in6_ifreq
65 {
66   struct in6_addr ifr6_addr;
67   uint32_t ifr6_prefixlen;
68   unsigned int ifr6_ifindex;
69 };
70 #endif
71
72
73 /**
74  * Creates a tun-interface called dev;
75  *
76  * @param dev is asumed to point to a char[IFNAMSIZ]
77  *        if *dev == '\\0', uses the name supplied by the kernel;
78  * @return the fd to the tun or -1 on error
79  */
80 static int
81 init_tun (char *dev)
82 {
83   struct ifreq ifr;
84   int fd;
85
86   if (NULL == dev)
87   {
88     errno = EINVAL;
89     return -1;
90   }
91
92   if (-1 == (fd = open ("/dev/net/tun", O_RDWR)))
93   {
94     fprintf (stderr,
95              "Error opening `%s': %s\n",
96              "/dev/net/tun",
97              strerror (errno));
98     return -1;
99   }
100
101   if (fd >= FD_SETSIZE)
102   {
103     fprintf (stderr,
104              "File descriptor to large: %d",
105              fd);
106     (void) close (fd);
107     return -1;
108   }
109
110   memset (&ifr, 0, sizeof (ifr));
111   ifr.ifr_flags = IFF_TUN;
112
113   if ('\0' != *dev)
114     strncpy (ifr.ifr_name,
115              dev,
116              IFNAMSIZ);
117
118   if (-1 == ioctl (fd,
119                    TUNSETIFF,
120                    (void *) &ifr))
121   {
122     fprintf (stderr,
123              "Error with ioctl on `%s': %s\n",
124              "/dev/net/tun",
125              strerror (errno));
126     (void) close (fd);
127     return -1;
128   }
129   strcpy (dev, ifr.ifr_name);
130   return fd;
131 }
132
133
134 /**
135  * @brief Sets the IPv6-Address given in address on the interface dev
136  *
137  * @param dev the interface to configure
138  * @param address the IPv6-Address
139  * @param prefix_len the length of the network-prefix
140  */
141 static void
142 set_address6 (const char *dev,
143               const char *address,
144               unsigned long prefix_len)
145 {
146   struct ifreq ifr;
147   struct in6_ifreq ifr6;
148   struct sockaddr_in6 sa6;
149   int fd;
150
151   /*
152    * parse the new address
153    */
154   memset (&sa6, 0, sizeof (struct sockaddr_in6));
155   sa6.sin6_family = AF_INET6;
156   if (1 != inet_pton (AF_INET6, address, sa6.sin6_addr.s6_addr))
157   {
158     fprintf (stderr,
159              "Failed to parse IPv6 address `%s'\n",
160              address);
161     exit (1);
162   }
163
164   if (-1 == (fd = socket (PF_INET6, SOCK_DGRAM, 0)))
165   {
166     fprintf (stderr,
167              "Error creating socket: %s\n",
168              strerror (errno));
169     exit (1);
170   }
171
172   memset (&ifr, 0, sizeof (struct ifreq));
173   /*
174    * Get the index of the if
175    */
176   strncpy (ifr.ifr_name,
177            dev,
178            IFNAMSIZ);
179   if (-1 == ioctl (fd,
180                    SIOGIFINDEX,
181                    &ifr))
182   {
183     fprintf (stderr,
184              "ioctl failed at %d: %s\n",
185              __LINE__,
186              strerror (errno));
187     (void) close (fd);
188     exit (1);
189   }
190
191   memset (&ifr6, 0, sizeof (struct in6_ifreq));
192   ifr6.ifr6_addr = sa6.sin6_addr;
193   ifr6.ifr6_ifindex = ifr.ifr_ifindex;
194   ifr6.ifr6_prefixlen = prefix_len;
195
196   /*
197    * Set the address
198    */
199   if (-1 == ioctl (fd,
200                    SIOCSIFADDR,
201                    &ifr6))
202   {
203     fprintf (stderr,
204              "ioctl failed at line %d: %s\n",
205              __LINE__,
206              strerror (errno));
207     (void) close (fd);
208     exit (1);
209   }
210
211   /*
212    * Get the flags
213    */
214   if (-1 == ioctl (fd,
215                    SIOCGIFFLAGS,
216                    &ifr))
217   {
218     fprintf (stderr,
219              "ioctl failed at line %d: %s\n",
220              __LINE__,
221              strerror (errno));
222     (void) close (fd);
223     exit (1);
224   }
225
226   /*
227    * Add the UP and RUNNING flags
228    */
229   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
230   if (-1 == ioctl (fd,
231                    SIOCSIFFLAGS,
232                    &ifr))
233   {
234     fprintf (stderr,
235              "ioctl failed at line %d: %s\n",
236              __LINE__,
237              strerror (errno));
238     (void) close (fd);
239     exit (1);
240   }
241
242   if (0 != close (fd))
243   {
244     fprintf (stderr,
245              "close failed: %s\n",
246              strerror (errno));
247     exit (1);
248   }
249 }
250
251
252 /**
253  * @brief Sets the IPv4-Address given in address on the interface dev
254  *
255  * @param dev the interface to configure
256  * @param address the IPv4-Address
257  * @param mask the netmask
258  */
259 static void
260 set_address4 (const char *dev,
261               const char *address,
262               const char *mask)
263 {
264   int fd;
265   struct sockaddr_in *addr;
266   struct ifreq ifr;
267
268   memset (&ifr, 0, sizeof (struct ifreq));
269   addr = (struct sockaddr_in *) &(ifr.ifr_addr);
270   addr->sin_family = AF_INET;
271
272   /*
273    * Parse the address
274    */
275   if (1 != inet_pton (AF_INET,
276                       address,
277                       &addr->sin_addr.s_addr))
278   {
279     fprintf (stderr,
280              "Failed to parse IPv4 address `%s'\n",
281              address);
282     exit (1);
283   }
284
285   if (-1 == (fd = socket (PF_INET, SOCK_DGRAM, 0)))
286   {
287     fprintf (stderr,
288              "Error creating socket: %s\n",
289              strerror (errno));
290     exit (1);
291   }
292
293   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
294
295   /*
296    * Set the address
297    */
298   if (-1 == ioctl (fd, SIOCSIFADDR, &ifr))
299   {
300     fprintf (stderr,
301              "ioctl failed at %d: %s\n",
302              __LINE__,
303              strerror (errno));
304     (void) close (fd);
305     exit (1);
306   }
307
308   /*
309    * Parse the netmask
310    */
311   addr = (struct sockaddr_in *) &(ifr.ifr_netmask);
312   if (1 != inet_pton (AF_INET,
313                       mask,
314                       &addr->sin_addr.s_addr))
315   {
316     fprintf (stderr,
317              "Failed to parse IPv4 address mask `%s'\n",
318              mask);
319     (void) close (fd);
320     exit (1);
321   }
322
323   /*
324    * Set the netmask
325    */
326   if (-1 == ioctl (fd, SIOCSIFNETMASK, &ifr))
327   {
328     fprintf (stderr,
329              "ioctl failed at line %d: %s\n",
330              __LINE__,
331              strerror (errno));
332     (void) close (fd);
333     exit (1);
334   }
335
336   /*
337    * Get the flags
338    */
339   if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
340   {
341     fprintf (stderr,
342              "ioctl failed at line %d: %s\n",
343              __LINE__,
344              strerror (errno));
345     (void) close (fd);
346     exit (1);
347   }
348
349   /*
350    * Add the UP and RUNNING flags
351    */
352   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
353   if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
354   {
355     fprintf (stderr,
356              "ioctl failed at line %d: %s\n",
357              __LINE__,
358              strerror (errno));
359     (void) close (fd);
360     exit (1);
361   }
362
363   if (0 != close (fd))
364   {
365     fprintf (stderr,
366              "close failed: %s\n",
367              strerror (errno));
368     (void) close (fd);
369     exit (1);
370   }
371 }
372
373
374 /**
375  * Start forwarding to and from the tunnel.
376  *
377  * @param fd_tun tunnel FD
378  */
379 static void
380 run (int fd_tun)
381 {
382   /*
383    * The buffer filled by reading from fd_tun
384    */
385   unsigned char buftun[MAX_SIZE];
386   ssize_t buftun_size = 0;
387   unsigned char *buftun_read = NULL;
388
389   /*
390    * The buffer filled by reading from stdin
391    */
392   unsigned char bufin[MAX_SIZE];
393   ssize_t bufin_size = 0;
394   size_t bufin_rpos = 0;
395   unsigned char *bufin_read = NULL;
396
397   fd_set fds_w;
398   fd_set fds_r;
399
400   /* read refers to reading from fd_tun, writing to stdout */
401   int read_open = 1;
402
403   /* write refers to reading from stdin, writing to fd_tun */
404   int write_open = 1;
405
406   while ((1 == read_open) && (1 == write_open))
407   {
408     FD_ZERO (&fds_w);
409     FD_ZERO (&fds_r);
410
411     /*
412      * We are supposed to read and the buffer is empty
413      * -> select on read from tun
414      */
415     if (read_open && (0 == buftun_size))
416       FD_SET (fd_tun, &fds_r);
417
418     /*
419      * We are supposed to read and the buffer is not empty
420      * -> select on write to stdout
421      */
422     if (read_open && (0 != buftun_size))
423       FD_SET (1, &fds_w);
424
425     /*
426      * We are supposed to write and the buffer is empty
427      * -> select on read from stdin
428      */
429     if (write_open && (NULL == bufin_read))
430       FD_SET (0, &fds_r);
431
432     /*
433      * We are supposed to write and the buffer is not empty
434      * -> select on write to tun
435      */
436     if (write_open && (NULL != bufin_read))
437       FD_SET (fd_tun, &fds_w);
438
439     int r = select (fd_tun + 1, &fds_r, &fds_w, NULL, NULL);
440
441     if (-1 == r)
442     {
443       if (EINTR == errno)
444         continue;
445       fprintf (stderr,
446                "select failed: %s\n",
447                strerror (errno));
448       exit (1);
449     }
450
451     if (r > 0)
452     {
453       if (FD_ISSET (fd_tun, &fds_r))
454       {
455         buftun_size =
456             read (fd_tun, buftun + sizeof (struct GNUNET_MessageHeader),
457                   MAX_SIZE - sizeof (struct GNUNET_MessageHeader));
458         if (-1 == buftun_size)
459         {
460           fprintf (stderr,
461                    "read-error: %s\n",
462                    strerror (errno));
463           shutdown (fd_tun, SHUT_RD);
464           shutdown (1, SHUT_WR);
465           read_open = 0;
466           buftun_size = 0;
467         }
468         else if (0 == buftun_size)
469         {
470           fprintf (stderr, "EOF on tun\n");
471           shutdown (fd_tun, SHUT_RD);
472           shutdown (1, SHUT_WR);
473           read_open = 0;
474           buftun_size = 0;
475         }
476         else
477         {
478           buftun_read = buftun;
479           struct GNUNET_MessageHeader *hdr =
480               (struct GNUNET_MessageHeader *) buftun;
481           buftun_size += sizeof (struct GNUNET_MessageHeader);
482           hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
483           hdr->size = htons (buftun_size);
484         }
485       }
486       else if (FD_ISSET (1, &fds_w))
487       {
488         ssize_t written = write (1,
489                                  buftun_read,
490                                  buftun_size);
491
492         if (-1 == written)
493         {
494 #if !DEBUG
495           if (errno != EPIPE)
496 #endif
497             fprintf (stderr,
498                      "write-error to stdout: %s\n",
499                      strerror (errno));
500           shutdown (fd_tun, SHUT_RD);
501           shutdown (1, SHUT_WR);
502           read_open = 0;
503           buftun_size = 0;
504         }
505         else if (0 == written)
506         {
507           fprintf (stderr,
508                    "write returned 0!?\n");
509           exit (1);
510         }
511         else
512         {
513           buftun_size -= written;
514           buftun_read += written;
515         }
516       }
517
518       if (FD_ISSET (0, &fds_r))
519       {
520         bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos);
521         if (-1 == bufin_size)
522         {
523           fprintf (stderr,
524                    "read-error: %s\n",
525                    strerror (errno));
526           shutdown (0, SHUT_RD);
527           shutdown (fd_tun, SHUT_WR);
528           write_open = 0;
529           bufin_size = 0;
530         }
531         else if (0 == bufin_size)
532         {
533 #if DEBUG
534           fprintf (stderr, "EOF on stdin\n");
535 #endif
536           shutdown (0, SHUT_RD);
537           shutdown (fd_tun, SHUT_WR);
538           write_open = 0;
539           bufin_size = 0;
540         }
541         else
542         {
543           struct GNUNET_MessageHeader *hdr;
544
545 PROCESS_BUFFER:
546           bufin_rpos += bufin_size;
547           if (bufin_rpos < sizeof (struct GNUNET_MessageHeader))
548             continue;
549           hdr = (struct GNUNET_MessageHeader *) bufin;
550           if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER)
551           {
552             fprintf (stderr,
553                      "protocol violation!\n");
554             exit (1);
555           }
556           if (ntohs (hdr->size) > bufin_rpos)
557             continue;
558           bufin_read = bufin + sizeof (struct GNUNET_MessageHeader);
559           bufin_size = ntohs (hdr->size) - sizeof (struct GNUNET_MessageHeader);
560           bufin_rpos -= bufin_size + sizeof (struct GNUNET_MessageHeader);
561         }
562       }
563       else if (FD_ISSET (fd_tun, &fds_w))
564       {
565         ssize_t written = write (fd_tun,
566                                  bufin_read,
567                                  bufin_size);
568
569         if (-1 == written)
570         {
571           fprintf (stderr,
572                    "write-error to tun: %s\n",
573                    strerror (errno));
574           shutdown (0, SHUT_RD);
575           shutdown (fd_tun, SHUT_WR);
576           write_open = 0;
577           bufin_size = 0;
578         }
579         else if (0 == written)
580         {
581           fprintf (stderr, "write returned 0!?\n");
582           exit (1);
583         }
584         else
585         {
586           bufin_size -= written;
587           bufin_read += written;
588           if (0 == bufin_size)
589           {
590             memmove (bufin, bufin_read, bufin_rpos);
591             bufin_read = NULL;  /* start reading again */
592             bufin_size = 0;
593             goto PROCESS_BUFFER;
594           }
595         }
596       }
597     }
598   }
599 }
600
601
602 /**
603  * Open VPN tunnel interface.
604  *
605  * @param argc must be 6
606  * @param argv 0: binary name (gnunet-helper-vpn)
607  *             1: tunnel interface name (gnunet-vpn)
608  *             2: IPv6 address (::1), "-" to disable
609  *             3: IPv6 netmask length in bits (64), ignored if #2 is "-"
610  *             4: IPv4 address (1.2.3.4), "-" to disable
611  *             5: IPv4 netmask (255.255.0.0), ignored if #4 is "-"
612  */
613 int
614 main (int argc, char **argv)
615 {
616   char dev[IFNAMSIZ];
617   int fd_tun;
618   int global_ret;
619
620   if (6 != argc)
621   {
622     fprintf (stderr, "Fatal: must supply 5 arguments!\n");
623     return 1;
624   }
625
626   strncpy (dev,
627            argv[1],
628            IFNAMSIZ);
629   dev[IFNAMSIZ - 1] = '\0';
630
631   if (-1 == (fd_tun = init_tun (dev)))
632   {
633     fprintf (stderr,
634              "Fatal: could not initialize tun-interface `%s'  with IPv6 %s/%s and IPv4 %s/%s\n",
635              dev,
636              argv[2],
637              argv[3],
638              argv[4],
639              argv[5]);
640     return 1;
641   }
642
643   if (0 != strcmp (argv[2], "-"))
644   {
645     const char *address = argv[2];
646     long prefix_len = atol (argv[3]);
647
648     if ((prefix_len < 1) || (prefix_len > 127))
649     {
650       fprintf (stderr,
651                "Fatal: prefix_len out of range\n");
652       return 1;
653     }
654
655     set_address6 (dev,
656                   address,
657                   prefix_len);
658   }
659
660   if (0 != strcmp (argv[4], "-"))
661   {
662     const char *address = argv[4];
663     const char *mask = argv[5];
664
665     set_address4 (dev, address, mask);
666   }
667
668   uid_t uid = getuid ();
669 #ifdef HAVE_SETRESUID
670   if (0 != setresuid (uid, uid, uid))
671   {
672     fprintf (stderr,
673              "Failed to setresuid: %s\n",
674              strerror (errno));
675     global_ret = 2;
676     goto cleanup;
677   }
678 #else
679   if (0 != (setuid (uid) | seteuid (uid)))
680   {
681     fprintf (stderr,
682              "Failed to setuid: %s\n",
683              strerror (errno));
684     global_ret = 2;
685     goto cleanup;
686   }
687 #endif
688
689   if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
690   {
691     fprintf (stderr,
692              "Failed to protect against SIGPIPE: %s\n",
693              strerror (errno));
694     /* no exit, we might as well die with SIGPIPE should it ever happen */
695   }
696   run (fd_tun);
697   global_ret = 0;
698  cleanup:
699   close (fd_tun);
700   return global_ret;
701 }