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