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