diff options
Diffstat (limited to 'src/exit/gnunet-helper-exit.c')
-rw-r--r-- | src/exit/gnunet-helper-exit.c | 859 |
1 files changed, 0 insertions, 859 deletions
diff --git a/src/exit/gnunet-helper-exit.c b/src/exit/gnunet-helper-exit.c deleted file mode 100644 index 4f32ea1f1..000000000 --- a/src/exit/gnunet-helper-exit.c +++ /dev/null | |||
@@ -1,859 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2010, 2011, 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 exit/gnunet-helper-exit.c | ||
23 | * | ||
24 | * @brief the helper for exit nodes. Opens a virtual | ||
25 | * network-interface, sends data received on the if to stdout, sends | ||
26 | * data received on stdin to the interface. The code also enables | ||
27 | * IPv4/IPv6 forwarding and NAT on the current system (the latter on | ||
28 | * an interface specified on the command-line); these changes to the | ||
29 | * network configuration are NOT automatically undone when the program | ||
30 | * is stopped (this is because we cannot be sure that some other | ||
31 | * application didn't enable them before or after us; also, these | ||
32 | * changes should be mostly harmless as it simply turns the system | ||
33 | * into a router). | ||
34 | * | ||
35 | * @author Philipp Tölke | ||
36 | * @author Christian Grothoff | ||
37 | * | ||
38 | * The following list of people have reviewed this code and considered | ||
39 | * it safe since the last modification (if you reviewed it, please | ||
40 | * have your name added to the list): | ||
41 | * | ||
42 | * - Philipp Tölke | ||
43 | */ | ||
44 | #include "platform.h" | ||
45 | |||
46 | #ifdef IF_TUN_HDR | ||
47 | #include IF_TUN_HDR | ||
48 | #endif | ||
49 | |||
50 | #if defined(BSD) || defined(SOLARIS) | ||
51 | #define ifr_netmask ifr_ifru.ifru_addr | ||
52 | #define SIOGIFINDEX SIOCGIFINDEX | ||
53 | #endif | ||
54 | |||
55 | /** | ||
56 | * Need 'struct GNUNET_MessageHeader'. | ||
57 | */ | ||
58 | #include "gnunet_crypto_lib.h" | ||
59 | #include "gnunet_common.h" | ||
60 | |||
61 | /** | ||
62 | * Need VPN message types. | ||
63 | */ | ||
64 | #include "gnunet_protocols.h" | ||
65 | |||
66 | /** | ||
67 | * Should we print (interesting|debug) messages that can happen during | ||
68 | * normal operation? | ||
69 | */ | ||
70 | #define DEBUG GNUNET_NO | ||
71 | |||
72 | /** | ||
73 | * Maximum size of a GNUnet message (GNUNET_MAX_MESSAGE_SIZE) | ||
74 | */ | ||
75 | #define MAX_SIZE 65536 | ||
76 | |||
77 | /** | ||
78 | * Path to 'sysctl' binary. | ||
79 | */ | ||
80 | static const char *sbin_sysctl; | ||
81 | |||
82 | /** | ||
83 | * Path to 'iptables' binary. | ||
84 | */ | ||
85 | static const char *sbin_iptables; | ||
86 | |||
87 | |||
88 | #if ! defined(__ANDROID__) | ||
89 | #if ! defined(_LINUX_IN6_H) && defined(__linux__) | ||
90 | /** | ||
91 | * This is in linux/include/net/ipv6.h, but not always exported. | ||
92 | */ | ||
93 | struct in6_ifreq | ||
94 | { | ||
95 | struct in6_addr ifr6_addr; | ||
96 | uint32_t ifr6_prefixlen; /* __u32 in the original */ | ||
97 | int ifr6_ifindex; | ||
98 | }; | ||
99 | #endif | ||
100 | #endif | ||
101 | |||
102 | |||
103 | /** | ||
104 | * Open '/dev/null' and make the result the given | ||
105 | * file descriptor. | ||
106 | * | ||
107 | * @param target_fd desired FD to point to /dev/null | ||
108 | * @param flags open flags (O_RDONLY, O_WRONLY) | ||
109 | */ | ||
110 | static void | ||
111 | open_dev_null (int target_fd, | ||
112 | int flags) | ||
113 | { | ||
114 | int fd; | ||
115 | |||
116 | fd = open ("/dev/null", flags); | ||
117 | if (-1 == fd) | ||
118 | abort (); | ||
119 | if (fd == target_fd) | ||
120 | return; | ||
121 | if (-1 == dup2 (fd, target_fd)) | ||
122 | { | ||
123 | (void) close (fd); | ||
124 | abort (); | ||
125 | } | ||
126 | (void) close (fd); | ||
127 | } | ||
128 | |||
129 | |||
130 | /** | ||
131 | * Run the given command and wait for it to complete. | ||
132 | * | ||
133 | * @param file name of the binary to run | ||
134 | * @param cmd command line arguments (as given to 'execv') | ||
135 | * @return 0 on success, 1 on any error | ||
136 | */ | ||
137 | static int | ||
138 | fork_and_exec (const char *file, | ||
139 | char *const cmd[]) | ||
140 | { | ||
141 | int status; | ||
142 | pid_t pid; | ||
143 | pid_t ret; | ||
144 | |||
145 | pid = fork (); | ||
146 | if (-1 == pid) | ||
147 | { | ||
148 | fprintf (stderr, | ||
149 | "fork failed: %s\n", | ||
150 | strerror (errno)); | ||
151 | return 1; | ||
152 | } | ||
153 | if (0 == pid) | ||
154 | { | ||
155 | /* we are the child process */ | ||
156 | /* close stdin/stdout to not cause interference | ||
157 | with the helper's main protocol! */ | ||
158 | (void) close (0); | ||
159 | open_dev_null (0, O_RDONLY); | ||
160 | (void) close (1); | ||
161 | open_dev_null (1, O_WRONLY); | ||
162 | (void) execv (file, cmd); | ||
163 | /* can only get here on error */ | ||
164 | fprintf (stderr, | ||
165 | "exec `%s' failed: %s\n", | ||
166 | file, | ||
167 | strerror (errno)); | ||
168 | _exit (1); | ||
169 | } | ||
170 | /* keep running waitpid as long as the only error we get is 'EINTR' */ | ||
171 | while ((-1 == (ret = waitpid (pid, &status, 0))) && | ||
172 | (errno == EINTR)) | ||
173 | ; | ||
174 | if (-1 == ret) | ||
175 | { | ||
176 | fprintf (stderr, | ||
177 | "waitpid failed: %s\n", | ||
178 | strerror (errno)); | ||
179 | return 1; | ||
180 | } | ||
181 | if (! (WIFEXITED (status) && (0 == WEXITSTATUS (status)))) | ||
182 | return 1; | ||
183 | /* child process completed and returned success, we're happy */ | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | |||
188 | /** | ||
189 | * Creates a tun-interface called dev; | ||
190 | * | ||
191 | * @param dev is assumed to point to a char[IFNAMSIZ] | ||
192 | * if *dev == '\\0', uses the name supplied by the kernel; | ||
193 | * @return the fd to the tun or -1 on error | ||
194 | */ | ||
195 | #ifdef IFF_TUN /* LINUX */ | ||
196 | static int | ||
197 | init_tun (char *dev) | ||
198 | { | ||
199 | struct ifreq ifr; | ||
200 | int fd; | ||
201 | |||
202 | if (NULL == dev) | ||
203 | { | ||
204 | errno = EINVAL; | ||
205 | return -1; | ||
206 | } | ||
207 | |||
208 | if (-1 == (fd = open ("/dev/net/tun", O_RDWR))) | ||
209 | { | ||
210 | fprintf (stderr, "Error opening `%s': %s\n", "/dev/net/tun", | ||
211 | strerror (errno)); | ||
212 | return -1; | ||
213 | } | ||
214 | |||
215 | if (fd >= FD_SETSIZE) | ||
216 | { | ||
217 | fprintf (stderr, "File descriptor to large: %d", fd); | ||
218 | (void) close (fd); | ||
219 | return -1; | ||
220 | } | ||
221 | |||
222 | memset (&ifr, 0, sizeof(ifr)); | ||
223 | ifr.ifr_flags = IFF_TUN; | ||
224 | |||
225 | if ('\0' != *dev) | ||
226 | strncpy (ifr.ifr_name, dev, IFNAMSIZ); | ||
227 | |||
228 | if (-1 == ioctl (fd, TUNSETIFF, (void *) &ifr)) | ||
229 | { | ||
230 | fprintf (stderr, | ||
231 | "Error with ioctl on `%s': %s\n", "/dev/net/tun", | ||
232 | strerror (errno)); | ||
233 | (void) close (fd); | ||
234 | return -1; | ||
235 | } | ||
236 | strcpy (dev, ifr.ifr_name); | ||
237 | return fd; | ||
238 | } | ||
239 | |||
240 | |||
241 | #else /* BSD et al, including DARWIN */ | ||
242 | |||
243 | #ifdef SIOCIFCREATE | ||
244 | static int | ||
245 | init_tun (char *dev) | ||
246 | { | ||
247 | int fd; | ||
248 | int s; | ||
249 | struct ifreq ifr; | ||
250 | |||
251 | fd = open (dev, O_RDWR); | ||
252 | if (fd == -1) | ||
253 | { | ||
254 | s = socket (AF_INET, SOCK_DGRAM, 0); | ||
255 | if (s < 0) | ||
256 | return -1; | ||
257 | memset (&ifr, 0, sizeof(ifr)); | ||
258 | strncpy (ifr.ifr_name, dev + 5, sizeof(ifr.ifr_name) - 1); | ||
259 | if (! ioctl (s, SIOCIFCREATE, &ifr)) | ||
260 | fd = open (dev, O_RDWR); | ||
261 | close (s); | ||
262 | } | ||
263 | return fd; | ||
264 | } | ||
265 | |||
266 | |||
267 | #else | ||
268 | #define init_tun(dev) open (dev, O_RDWR) | ||
269 | #endif | ||
270 | #endif /* !IFF_TUN (BSD) */ | ||
271 | |||
272 | /** | ||
273 | * @brief Sets the IPv6-Address given in address on the interface dev | ||
274 | * | ||
275 | * @param dev the interface to configure | ||
276 | * @param address the IPv6-Address | ||
277 | * @param prefix_len the length of the network-prefix | ||
278 | */ | ||
279 | static void | ||
280 | set_address6 (const char *dev, const char *address, unsigned long prefix_len) | ||
281 | { | ||
282 | struct ifreq ifr; | ||
283 | struct sockaddr_in6 sa6; | ||
284 | int fd; | ||
285 | struct in6_ifreq ifr6; | ||
286 | |||
287 | /* | ||
288 | * parse the new address | ||
289 | */ | ||
290 | memset (&sa6, 0, sizeof(struct sockaddr_in6)); | ||
291 | sa6.sin6_family = AF_INET6; | ||
292 | if (1 != inet_pton (AF_INET6, address, &sa6.sin6_addr)) | ||
293 | { | ||
294 | fprintf (stderr, "Failed to parse address `%s': %s\n", address, | ||
295 | strerror (errno)); | ||
296 | exit (1); | ||
297 | } | ||
298 | |||
299 | if (-1 == (fd = socket (PF_INET6, SOCK_DGRAM, 0))) | ||
300 | { | ||
301 | fprintf (stderr, "Error creating socket: %s\n", strerror (errno)); | ||
302 | exit (1); | ||
303 | } | ||
304 | |||
305 | memset (&ifr, 0, sizeof(struct ifreq)); | ||
306 | /* | ||
307 | * Get the index of the if | ||
308 | */ | ||
309 | strncpy (ifr.ifr_name, dev, IFNAMSIZ); | ||
310 | if (-1 == ioctl (fd, SIOGIFINDEX, &ifr)) | ||
311 | { | ||
312 | fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno)); | ||
313 | (void) close (fd); | ||
314 | exit (1); | ||
315 | } | ||
316 | |||
317 | memset (&ifr6, 0, sizeof(struct in6_ifreq)); | ||
318 | ifr6.ifr6_addr = sa6.sin6_addr; | ||
319 | ifr6.ifr6_ifindex = ifr.ifr_ifindex; | ||
320 | ifr6.ifr6_prefixlen = prefix_len; | ||
321 | |||
322 | /* | ||
323 | * Set the address | ||
324 | */ | ||
325 | if (-1 == ioctl (fd, SIOCSIFADDR, &ifr6)) | ||
326 | { | ||
327 | fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__, | ||
328 | strerror (errno)); | ||
329 | (void) close (fd); | ||
330 | exit (1); | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * Get the flags | ||
335 | */ | ||
336 | if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr)) | ||
337 | { | ||
338 | fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__, | ||
339 | strerror (errno)); | ||
340 | (void) close (fd); | ||
341 | exit (1); | ||
342 | } | ||
343 | |||
344 | /* | ||
345 | * Add the UP and RUNNING flags | ||
346 | */ | ||
347 | ifr.ifr_flags |= IFF_UP | IFF_RUNNING; | ||
348 | if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr)) | ||
349 | { | ||
350 | fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__, | ||
351 | strerror (errno)); | ||
352 | (void) close (fd); | ||
353 | exit (1); | ||
354 | } | ||
355 | |||
356 | if (0 != close (fd)) | ||
357 | { | ||
358 | fprintf (stderr, "close failed: %s\n", strerror (errno)); | ||
359 | exit (1); | ||
360 | } | ||
361 | } | ||
362 | |||
363 | |||
364 | /** | ||
365 | * @brief Sets the IPv4-Address given in address on the interface dev | ||
366 | * | ||
367 | * @param dev the interface to configure | ||
368 | * @param address the IPv4-Address | ||
369 | * @param mask the netmask | ||
370 | */ | ||
371 | static void | ||
372 | set_address4 (const char *dev, const char *address, const char *mask) | ||
373 | { | ||
374 | int fd; | ||
375 | struct sockaddr_in *addr; | ||
376 | struct ifreq ifr; | ||
377 | |||
378 | memset (&ifr, 0, sizeof(struct ifreq)); | ||
379 | addr = (struct sockaddr_in *) &(ifr.ifr_addr); | ||
380 | addr->sin_family = AF_INET; | ||
381 | |||
382 | /* | ||
383 | * Parse the address | ||
384 | */ | ||
385 | if (1 != inet_pton (AF_INET, address, &addr->sin_addr.s_addr)) | ||
386 | { | ||
387 | fprintf (stderr, "Failed to parse address `%s': %s\n", address, | ||
388 | strerror (errno)); | ||
389 | exit (1); | ||
390 | } | ||
391 | |||
392 | if (-1 == (fd = socket (PF_INET, SOCK_DGRAM, 0))) | ||
393 | { | ||
394 | fprintf (stderr, "Error creating socket: %s\n", strerror (errno)); | ||
395 | exit (1); | ||
396 | } | ||
397 | |||
398 | strncpy (ifr.ifr_name, dev, IFNAMSIZ); | ||
399 | |||
400 | /* | ||
401 | * Set the address | ||
402 | */ | ||
403 | if (-1 == ioctl (fd, SIOCSIFADDR, &ifr)) | ||
404 | { | ||
405 | fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno)); | ||
406 | (void) close (fd); | ||
407 | exit (1); | ||
408 | } | ||
409 | |||
410 | /* | ||
411 | * Parse the netmask | ||
412 | */ | ||
413 | addr = (struct sockaddr_in *) &(ifr.ifr_netmask); | ||
414 | if (1 != inet_pton (AF_INET, mask, &addr->sin_addr.s_addr)) | ||
415 | { | ||
416 | fprintf (stderr, "Failed to parse address `%s': %s\n", mask, | ||
417 | strerror (errno)); | ||
418 | (void) close (fd); | ||
419 | exit (1); | ||
420 | } | ||
421 | |||
422 | /* | ||
423 | * Set the netmask | ||
424 | */ | ||
425 | if (-1 == ioctl (fd, SIOCSIFNETMASK, &ifr)) | ||
426 | { | ||
427 | fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__, | ||
428 | strerror (errno)); | ||
429 | (void) close (fd); | ||
430 | exit (1); | ||
431 | } | ||
432 | |||
433 | /* | ||
434 | * Get the flags | ||
435 | */ | ||
436 | if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr)) | ||
437 | { | ||
438 | fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__, | ||
439 | strerror (errno)); | ||
440 | (void) close (fd); | ||
441 | exit (1); | ||
442 | } | ||
443 | |||
444 | /* | ||
445 | * Add the UP and RUNNING flags | ||
446 | */ | ||
447 | ifr.ifr_flags |= IFF_UP | IFF_RUNNING; | ||
448 | if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr)) | ||
449 | { | ||
450 | fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__, | ||
451 | strerror (errno)); | ||
452 | (void) close (fd); | ||
453 | exit (1); | ||
454 | } | ||
455 | |||
456 | if (0 != close (fd)) | ||
457 | { | ||
458 | fprintf (stderr, "close failed: %s\n", strerror (errno)); | ||
459 | (void) close (fd); | ||
460 | exit (1); | ||
461 | } | ||
462 | } | ||
463 | |||
464 | |||
465 | /** | ||
466 | * Start forwarding to and from the tunnel. | ||
467 | * | ||
468 | * @param fd_tun tunnel FD | ||
469 | */ | ||
470 | static void | ||
471 | run (int fd_tun) | ||
472 | { | ||
473 | /* | ||
474 | * The buffer filled by reading from fd_tun | ||
475 | */ | ||
476 | unsigned char buftun[MAX_SIZE]; | ||
477 | ssize_t buftun_size = 0; | ||
478 | unsigned char *buftun_read = NULL; | ||
479 | |||
480 | /* | ||
481 | * The buffer filled by reading from stdin | ||
482 | */ | ||
483 | unsigned char bufin[MAX_SIZE]; | ||
484 | ssize_t bufin_size = 0; | ||
485 | size_t bufin_rpos = 0; | ||
486 | unsigned char *bufin_read = NULL; | ||
487 | |||
488 | fd_set fds_w; | ||
489 | fd_set fds_r; | ||
490 | |||
491 | /* read refers to reading from fd_tun, writing to stdout */ | ||
492 | int read_open = 1; | ||
493 | |||
494 | /* write refers to reading from stdin, writing to fd_tun */ | ||
495 | int write_open = 1; | ||
496 | |||
497 | while ((1 == read_open) && (1 == write_open)) | ||
498 | { | ||
499 | FD_ZERO (&fds_w); | ||
500 | FD_ZERO (&fds_r); | ||
501 | |||
502 | /* | ||
503 | * We are supposed to read and the buffer is empty | ||
504 | * -> select on read from tun | ||
505 | */ | ||
506 | if (read_open && (0 == buftun_size)) | ||
507 | FD_SET (fd_tun, &fds_r); | ||
508 | |||
509 | /* | ||
510 | * We are supposed to read and the buffer is not empty | ||
511 | * -> select on write to stdout | ||
512 | */ | ||
513 | if (read_open && (0 != buftun_size)) | ||
514 | FD_SET (1, &fds_w); | ||
515 | |||
516 | /* | ||
517 | * We are supposed to write and the buffer is empty | ||
518 | * -> select on read from stdin | ||
519 | */ | ||
520 | if (write_open && (NULL == bufin_read)) | ||
521 | FD_SET (0, &fds_r); | ||
522 | |||
523 | /* | ||
524 | * We are supposed to write and the buffer is not empty | ||
525 | * -> select on write to tun | ||
526 | */ | ||
527 | if (write_open && (NULL != bufin_read)) | ||
528 | FD_SET (fd_tun, &fds_w); | ||
529 | |||
530 | int r = select (fd_tun + 1, &fds_r, &fds_w, NULL, NULL); | ||
531 | |||
532 | if (-1 == r) | ||
533 | { | ||
534 | if (EINTR == errno) | ||
535 | continue; | ||
536 | fprintf (stderr, "select failed: %s\n", strerror (errno)); | ||
537 | exit (1); | ||
538 | } | ||
539 | |||
540 | if (r > 0) | ||
541 | { | ||
542 | if (FD_ISSET (fd_tun, &fds_r)) | ||
543 | { | ||
544 | buftun_size = | ||
545 | read (fd_tun, buftun + sizeof(struct GNUNET_MessageHeader), | ||
546 | MAX_SIZE - sizeof(struct GNUNET_MessageHeader)); | ||
547 | if (-1 == buftun_size) | ||
548 | { | ||
549 | fprintf (stderr, | ||
550 | "read-error: %s\n", | ||
551 | strerror (errno)); | ||
552 | shutdown (fd_tun, SHUT_RD); | ||
553 | shutdown (1, SHUT_WR); | ||
554 | read_open = 0; | ||
555 | buftun_size = 0; | ||
556 | } | ||
557 | else if (0 == buftun_size) | ||
558 | { | ||
559 | #if DEBUG | ||
560 | fprintf (stderr, "EOF on tun\n"); | ||
561 | #endif | ||
562 | shutdown (fd_tun, SHUT_RD); | ||
563 | shutdown (1, SHUT_WR); | ||
564 | read_open = 0; | ||
565 | buftun_size = 0; | ||
566 | } | ||
567 | else | ||
568 | { | ||
569 | buftun_read = buftun; | ||
570 | struct GNUNET_MessageHeader *hdr = | ||
571 | (struct GNUNET_MessageHeader *) buftun; | ||
572 | buftun_size += sizeof(struct GNUNET_MessageHeader); | ||
573 | hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER); | ||
574 | hdr->size = htons (buftun_size); | ||
575 | } | ||
576 | } | ||
577 | else if (FD_ISSET (1, &fds_w)) | ||
578 | { | ||
579 | ssize_t written = write (1, buftun_read, buftun_size); | ||
580 | |||
581 | if (-1 == written) | ||
582 | { | ||
583 | #if ! DEBUG | ||
584 | if (errno != EPIPE) | ||
585 | #endif | ||
586 | fprintf (stderr, | ||
587 | "write-error to stdout: %s\n", | ||
588 | strerror (errno)); | ||
589 | shutdown (fd_tun, SHUT_RD); | ||
590 | shutdown (1, SHUT_WR); | ||
591 | read_open = 0; | ||
592 | buftun_size = 0; | ||
593 | } | ||
594 | else if (0 == written) | ||
595 | { | ||
596 | fprintf (stderr, "write returned 0!?\n"); | ||
597 | exit (1); | ||
598 | } | ||
599 | else | ||
600 | { | ||
601 | buftun_size -= written; | ||
602 | buftun_read += written; | ||
603 | } | ||
604 | } | ||
605 | |||
606 | if (FD_ISSET (0, &fds_r)) | ||
607 | { | ||
608 | bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos); | ||
609 | if (-1 == bufin_size) | ||
610 | { | ||
611 | fprintf (stderr, "read-error: %s\n", strerror (errno)); | ||
612 | shutdown (0, SHUT_RD); | ||
613 | shutdown (fd_tun, SHUT_WR); | ||
614 | write_open = 0; | ||
615 | bufin_size = 0; | ||
616 | } | ||
617 | else if (0 == bufin_size) | ||
618 | { | ||
619 | #if DEBUG | ||
620 | fprintf (stderr, "EOF on stdin\n"); | ||
621 | #endif | ||
622 | shutdown (0, SHUT_RD); | ||
623 | shutdown (fd_tun, SHUT_WR); | ||
624 | write_open = 0; | ||
625 | bufin_size = 0; | ||
626 | } | ||
627 | else | ||
628 | { | ||
629 | struct GNUNET_MessageHeader *hdr; | ||
630 | |||
631 | PROCESS_BUFFER: | ||
632 | bufin_rpos += bufin_size; | ||
633 | if (bufin_rpos < sizeof(struct GNUNET_MessageHeader)) | ||
634 | continue; | ||
635 | hdr = (struct GNUNET_MessageHeader *) bufin; | ||
636 | if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER) | ||
637 | { | ||
638 | fprintf (stderr, "protocol violation!\n"); | ||
639 | exit (1); | ||
640 | } | ||
641 | if (ntohs (hdr->size) > bufin_rpos) | ||
642 | continue; | ||
643 | bufin_read = bufin + sizeof(struct GNUNET_MessageHeader); | ||
644 | bufin_size = ntohs (hdr->size) - sizeof(struct GNUNET_MessageHeader); | ||
645 | bufin_rpos -= bufin_size + sizeof(struct GNUNET_MessageHeader); | ||
646 | } | ||
647 | } | ||
648 | else if (FD_ISSET (fd_tun, &fds_w)) | ||
649 | { | ||
650 | ssize_t written = write (fd_tun, bufin_read, bufin_size); | ||
651 | |||
652 | if (-1 == written) | ||
653 | { | ||
654 | fprintf (stderr, "write-error to tun: %s\n", strerror (errno)); | ||
655 | shutdown (0, SHUT_RD); | ||
656 | shutdown (fd_tun, SHUT_WR); | ||
657 | write_open = 0; | ||
658 | bufin_size = 0; | ||
659 | } | ||
660 | else if (0 == written) | ||
661 | { | ||
662 | fprintf (stderr, "write returned 0!?\n"); | ||
663 | exit (1); | ||
664 | } | ||
665 | else | ||
666 | { | ||
667 | bufin_size -= written; | ||
668 | bufin_read += written; | ||
669 | if (0 == bufin_size) | ||
670 | { | ||
671 | memmove (bufin, bufin_read, bufin_rpos); | ||
672 | bufin_read = NULL; /* start reading again */ | ||
673 | bufin_size = 0; | ||
674 | goto PROCESS_BUFFER; | ||
675 | } | ||
676 | } | ||
677 | } | ||
678 | } | ||
679 | } | ||
680 | } | ||
681 | |||
682 | |||
683 | /** | ||
684 | * Open VPN tunnel interface. | ||
685 | * | ||
686 | * @param argc must be 6 | ||
687 | * @param argv 0: binary name ("gnunet-helper-exit") | ||
688 | * 1: tunnel interface name ("gnunet-exit") | ||
689 | * 2: "physical" interface name ("eth0"), or "-" to not setup NAT | ||
690 | * and routing | ||
691 | * 3: IPv6 address ("::1"), or "-" to skip IPv6 | ||
692 | * 4: IPv6 netmask length in bits ("64") [ignored if #4 is "-"] | ||
693 | * 5: IPv4 address ("1.2.3.4"), or "-" to skip IPv4 | ||
694 | * 6: IPv4 netmask ("255.255.0.0") [ignored if #4 is "-"] | ||
695 | */ | ||
696 | int | ||
697 | main (int argc, char **argv) | ||
698 | { | ||
699 | char dev[IFNAMSIZ]; | ||
700 | int fd_tun; | ||
701 | int global_ret; | ||
702 | |||
703 | if (7 != argc) | ||
704 | { | ||
705 | fprintf (stderr, "Fatal: must supply 6 arguments!\n"); | ||
706 | return 1; | ||
707 | } | ||
708 | if ((0 == strcmp (argv[3], "-")) && | ||
709 | (0 == strcmp (argv[5], "-"))) | ||
710 | { | ||
711 | fprintf (stderr, "Fatal: disabling both IPv4 and IPv6 makes no sense.\n"); | ||
712 | return 1; | ||
713 | } | ||
714 | if (0 != strcmp (argv[2], "-")) | ||
715 | { | ||
716 | #ifdef IPTABLES | ||
717 | if (0 == access (IPTABLES, X_OK)) | ||
718 | sbin_iptables = IPTABLES; | ||
719 | else | ||
720 | #endif | ||
721 | if (0 == access ("/sbin/iptables", X_OK)) | ||
722 | sbin_iptables = "/sbin/iptables"; | ||
723 | else if (0 == access ("/usr/sbin/iptables", X_OK)) | ||
724 | sbin_iptables = "/usr/sbin/iptables"; | ||
725 | else | ||
726 | { | ||
727 | fprintf (stderr, | ||
728 | "Fatal: executable iptables not found in approved directories: %s\n", | ||
729 | strerror (errno)); | ||
730 | return 1; | ||
731 | } | ||
732 | #ifdef SYSCTL | ||
733 | if (0 == access (SYSCTL, X_OK)) | ||
734 | sbin_sysctl = SYSCTL; | ||
735 | else | ||
736 | #endif | ||
737 | if (0 == access ("/sbin/sysctl", X_OK)) | ||
738 | sbin_sysctl = "/sbin/sysctl"; | ||
739 | else if (0 == access ("/usr/sbin/sysctl", X_OK)) | ||
740 | sbin_sysctl = "/usr/sbin/sysctl"; | ||
741 | else | ||
742 | { | ||
743 | fprintf (stderr, | ||
744 | "Fatal: executable sysctl not found in approved directories: %s\n", | ||
745 | strerror (errno)); | ||
746 | return 1; | ||
747 | } | ||
748 | } | ||
749 | |||
750 | strncpy (dev, argv[1], IFNAMSIZ); | ||
751 | dev[IFNAMSIZ - 1] = '\0'; | ||
752 | |||
753 | if (-1 == (fd_tun = init_tun (dev))) | ||
754 | { | ||
755 | fprintf (stderr, | ||
756 | "Fatal: could not initialize tun-interface `%s' with IPv6 %s/%s and IPv4 %s/%s\n", | ||
757 | dev, | ||
758 | argv[3], | ||
759 | argv[4], | ||
760 | argv[5], | ||
761 | argv[6]); | ||
762 | return 1; | ||
763 | } | ||
764 | |||
765 | if (0 != strcmp (argv[3], "-")) | ||
766 | { | ||
767 | { | ||
768 | const char *address = argv[3]; | ||
769 | long prefix_len = atol (argv[4]); | ||
770 | |||
771 | if ((prefix_len < 1) || (prefix_len > 127)) | ||
772 | { | ||
773 | fprintf (stderr, "Fatal: prefix_len out of range\n"); | ||
774 | return 1; | ||
775 | } | ||
776 | set_address6 (dev, address, prefix_len); | ||
777 | } | ||
778 | if (0 != strcmp (argv[2], "-")) | ||
779 | { | ||
780 | char *const sysctl_args[] = { | ||
781 | "sysctl", "-w", "net.ipv6.conf.all.forwarding=1", NULL | ||
782 | }; | ||
783 | if (0 != fork_and_exec (sbin_sysctl, | ||
784 | sysctl_args)) | ||
785 | { | ||
786 | fprintf (stderr, | ||
787 | "Failed to enable IPv6 forwarding. Will continue anyway.\n"); | ||
788 | } | ||
789 | } | ||
790 | } | ||
791 | |||
792 | if (0 != strcmp (argv[5], "-")) | ||
793 | { | ||
794 | { | ||
795 | const char *address = argv[5]; | ||
796 | const char *mask = argv[6]; | ||
797 | |||
798 | set_address4 (dev, address, mask); | ||
799 | } | ||
800 | if (0 != strcmp (argv[2], "-")) | ||
801 | { | ||
802 | { | ||
803 | char *const sysctl_args[] = { | ||
804 | "sysctl", "-w", "net.ipv4.ip_forward=1", NULL | ||
805 | }; | ||
806 | if (0 != fork_and_exec (sbin_sysctl, | ||
807 | sysctl_args)) | ||
808 | { | ||
809 | fprintf (stderr, | ||
810 | "Failed to enable IPv4 forwarding. Will continue anyway.\n"); | ||
811 | } | ||
812 | } | ||
813 | { | ||
814 | char *const iptables_args[] = { | ||
815 | "iptables", "-t", "nat", "-A", "POSTROUTING", "-o", argv[2], "-j", | ||
816 | "MASQUERADE", NULL | ||
817 | }; | ||
818 | if (0 != fork_and_exec (sbin_iptables, | ||
819 | iptables_args)) | ||
820 | { | ||
821 | fprintf (stderr, | ||
822 | "Failed to enable IPv4 masquerading (NAT). Will continue anyway.\n"); | ||
823 | } | ||
824 | } | ||
825 | } | ||
826 | } | ||
827 | |||
828 | uid_t uid = getuid (); | ||
829 | #ifdef HAVE_SETRESUID | ||
830 | if (0 != setresuid (uid, uid, uid)) | ||
831 | { | ||
832 | fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno)); | ||
833 | global_ret = 2; | ||
834 | goto cleanup; | ||
835 | } | ||
836 | #else | ||
837 | if (0 != (setuid (uid) | seteuid (uid))) | ||
838 | { | ||
839 | fprintf (stderr, "Failed to setuid: %s\n", strerror (errno)); | ||
840 | global_ret = 2; | ||
841 | goto cleanup; | ||
842 | } | ||
843 | #endif | ||
844 | |||
845 | if (SIG_ERR == signal (SIGPIPE, SIG_IGN)) | ||
846 | { | ||
847 | fprintf (stderr, "Failed to protect against SIGPIPE: %s\n", | ||
848 | strerror (errno)); | ||
849 | /* no exit, we might as well die with SIGPIPE should it ever happen */ | ||
850 | } | ||
851 | run (fd_tun); | ||
852 | global_ret = 0; | ||
853 | cleanup: | ||
854 | (void) close (fd_tun); | ||
855 | return global_ret; | ||
856 | } | ||
857 | |||
858 | |||
859 | /* end of gnunet-helper-exit.c */ | ||