aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Fuchs <christian.fuchs@cfuchs.net>2013-01-31 08:52:46 +0000
committerChristian Fuchs <christian.fuchs@cfuchs.net>2013-01-31 08:52:46 +0000
commit729c3ac4bf294e9f358269197f5d7ff60ea18854 (patch)
tree96fdc4e2258402f29f45ccd10e9f220db9e30f55 /src
parenta41c9ae56159e89e05770588bcb8ab23cc52a87a (diff)
downloadgnunet-729c3ac4bf294e9f358269197f5d7ff60ea18854.tar.gz
gnunet-729c3ac4bf294e9f358269197f5d7ff60ea18854.zip
added gnunet-helper-exit-windows, which currently is just a copy of
gnunet-helper-vpn-windows
Diffstat (limited to 'src')
-rw-r--r--src/exit/Makefile.am20
-rw-r--r--src/exit/gnunet-helper-exit-windows.c1523
2 files changed, 1539 insertions, 4 deletions
diff --git a/src/exit/Makefile.am b/src/exit/Makefile.am
index be3a66a34..eb2a7a9aa 100644
--- a/src/exit/Makefile.am
+++ b/src/exit/Makefile.am
@@ -1,7 +1,8 @@
1INCLUDES = -I$(top_srcdir)/src/include 1INCLUDES = -I$(top_srcdir)/src/include
2 2
3if MINGW 3if MINGW
4 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols 4 WINFLAGS = -Wl,--no-undefined, --export-all-symbols
5 EXITBIN = gnunet-helper-exit
5endif 6endif
6 7
7if USE_COVERAGE 8if USE_COVERAGE
@@ -30,9 +31,20 @@ libexec_PROGRAMS = \
30 gnunet-daemon-exit \ 31 gnunet-daemon-exit \
31 $(EXITBIN) 32 $(EXITBIN)
32 33
33gnunet_helper_exit_SOURCES = \ 34if MINGW
34 gnunet-helper-exit.c 35 gnunet_helper_exit_LDFLAGS = \
35 36 -no-undefined -Wl,--export-all-symbols
37
38 gnunet_helper_exit_LDADD = \
39 -lsetupapi -lnewdev -lshell32 -liconv -lstdc++ \
40 -lcomdlg32 -lgdi32 -liphlpapi
41
42 gnunet_helper_exit_SOURCES = \
43 gnunet-helper-exit-windows.c
44else
45 gnunet_helper_exit_SOURCES = \
46 gnunet-helper-exit.c
47endif
36gnunet_daemon_exit_SOURCES = \ 48gnunet_daemon_exit_SOURCES = \
37 gnunet-daemon-exit.c exit.h 49 gnunet-daemon-exit.c exit.h
38gnunet_daemon_exit_LDADD = \ 50gnunet_daemon_exit_LDADD = \
diff --git a/src/exit/gnunet-helper-exit-windows.c b/src/exit/gnunet-helper-exit-windows.c
new file mode 100644
index 000000000..4918df15a
--- /dev/null
+++ b/src/exit/gnunet-helper-exit-windows.c
@@ -0,0 +1,1523 @@
1/*
2 This file is part of GNUnet.
3 (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., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21/**
22 * @file exit/gnunet-helper-exit-windows.c
23 * @brief the helper for the exit service in win32 builds.
24 * Opens a virtual network-interface, sends data received on the if to stdout,
25 * sends data received on stdin to the interface
26 * @author Christian M. Fuchs
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 */
33
34#include <stdio.h>
35#include <windows.h>
36#include <setupapi.h>
37#include <ddk/cfgmgr32.h>
38#include <ddk/newdev.h>
39#include <Winsock2.h>
40#include <time.h>
41#include "platform.h"
42#include "tap-windows.h"
43/**
44 * Need 'struct GNUNET_MessageHeader'.
45 */
46#include "gnunet_common.h"
47
48/**
49 * Need VPN message types.
50 */
51#include "gnunet_protocols.h"
52
53/**
54 * Should we print (interesting|debug) messages that can happen during
55 * normal operation?
56 */
57//#define DEBUG GNUNET_YES
58
59/**
60 * Maximum size of a GNUnet message (GNUNET_SERVER_MAX_MESSAGE_SIZE)
61 */
62#define MAX_SIZE 65536
63
64/**
65 * Name or Path+Name of our driver in Unicode.
66 * The .sys and .cat files HAVE to be in the same location as this file!
67 */
68#define INF_FILE "share/gnunet/tapw32/OemWin2k.inf"
69
70/**
71 * Hardware ID used in the inf-file.
72 * This might change over time, as openvpn advances their driver
73 */
74#define HARDWARE_ID "tap0901"
75
76/**
77 * Minimum major-id of the driver version we can work with
78 */
79#define TAP_WIN_MIN_MAJOR 9
80
81/**
82 * Minimum minor-id of the driver version we can work with.
83 * v <= 7 has buggy IPv6.
84 * v == 8 is broken for small IPv4 Packets
85 */
86#define TAP_WIN_MIN_MINOR 9
87
88/**
89 * Time in seconds to wait for our virtual device to go up after telling it to do so.
90 *
91 * openvpn doesn't specify a value, 4 seems sane for testing, even for openwrt
92 * (in fact, 4 was chosen by a fair dice roll...)
93 */
94#define TAP32_POSTUP_WAITTIME 4
95
96/**
97 * Location of the network interface list resides in registry.
98 */
99#define INTERFACE_REGISTRY_LOCATION "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
100
101/**
102 * Our local process' PID. Used for creating a sufficiently unique additional
103 * hardware ID for our device.
104 */
105static char secondary_hwid[LINE_LEN / 2];
106
107/**
108 * Device's visible Name, used to identify a network device in netsh.
109 * eg: "Local Area Connection 9"
110 */
111static char device_visible_name[256];
112
113/**
114 * This is our own local instance of a virtual network interface
115 * It is (somewhat) equivalent to using tun/tap in unixoid systems
116 *
117 * Upon initialization, we create such an device node.
118 * Upon termination, we remove it again.
119 *
120 * If we crash this device might stay around.
121 */
122static HDEVINFO DeviceInfo = INVALID_HANDLE_VALUE;
123
124/**
125 * Registry Key we hand over to windows to spawn a new virtual interface
126 */
127static SP_DEVINFO_DATA DeviceNode;
128
129/**
130 * GUID of our virtual device in the form of
131 * {12345678-1234-1234-1234-123456789abc} - in hex
132 */
133static char device_guid[256];
134
135
136/**
137 * Possible states of an IO facility.
138 */
139enum IO_State
140{
141
142 /**
143 * overlapped I/O is ready for work
144 */
145 IOSTATE_READY = 0,
146
147 /**
148 * overlapped I/O has been queued
149 */
150 IOSTATE_QUEUED,
151
152 /**
153 * overlapped I/O has finished, but is waiting for it's write-partner
154 */
155 IOSTATE_WAITING,
156
157 /**
158 * there is a full buffer waiting
159 */
160 IOSTATE_RESUME,
161
162 /**
163 * Operlapped IO states for facility objects
164 * overlapped I/O has failed, stop processing
165 */
166 IOSTATE_FAILED
167
168};
169
170
171/**
172 * A IO Object + read/writebuffer + buffer-size for windows asynchronous IO handling
173 */
174struct io_facility
175{
176 /**
177 * The mode the state machine associated with this object is in.
178 */
179 enum IO_State facility_state;
180
181 /**
182 * If the path is open or blocked in general (used for quickly checking)
183 */
184 BOOL path_open; // BOOL is winbool (int), NOT boolean (unsigned char)!
185
186 /**
187 * Windows Object-Handle (used for accessing TAP and STDIN/STDOUT)
188 */
189 HANDLE handle;
190
191 /**
192 * Overlaped IO structure used for asynchronous IO in windows.
193 */
194 OVERLAPPED overlapped;
195
196 /**
197 * Buffer for reading things to and writing from...
198 */
199 unsigned char buffer[MAX_SIZE];
200
201 /**
202 * How much of this buffer was used when reading or how much data can be written
203 */
204 DWORD buffer_size;
205
206 /**
207 * Amount of data written, is compared to buffer_size.
208 */
209 DWORD buffer_size_written;
210};
211
212/**
213 * ReOpenFile is only available as of XP SP2 and 2003 SP1
214 */
215WINBASEAPI HANDLE WINAPI ReOpenFile (HANDLE, DWORD, DWORD, DWORD);
216
217
218/**
219 * Wrapper for executing a shellcommand in windows.
220 *
221 * @param command - the command + parameters to execute
222 * @return * exitcode of the program executed,
223 * * EINVAL (cmd/file not found)
224 * * EPIPE (could not read STDOUT)
225 */
226static int
227execute_shellcommand (const char *command)
228{
229 FILE *pipe;
230
231 if ( (NULL == command) ||
232 (NULL == (pipe = _popen (command, "rt"))) )
233 return EINVAL;
234
235#ifdef DEBUG
236 fprintf (stderr, "DEBUG: Command output: \n");
237 char output[LINE_LEN];
238 while (NULL != fgets (output, sizeof (output), pipe))
239 fprintf (stderr, "%s", output);
240#endif
241
242 return _pclose (pipe);
243}
244
245
246/**
247 * @brief Sets the IPv6-Address given in address on the interface dev
248 *
249 * @param address the IPv6-Address
250 * @param prefix_len the length of the network-prefix
251 */
252static int
253set_address6 (const char *address, unsigned long prefix_len)
254{
255 int ret = EINVAL;
256 char command[LINE_LEN];
257 struct sockaddr_in6 sa6;
258
259 /*
260 * parse the new address
261 */
262 memset (&sa6, 0, sizeof (struct sockaddr_in6));
263 sa6.sin6_family = AF_INET6;
264 if (1 != inet_pton (AF_INET6, address, &sa6.sin6_addr.s6_addr))
265 {
266 fprintf (stderr, "ERROR: Failed to parse address `%s': %s\n", address,
267 strerror (errno));
268 return -1;
269 }
270
271 /*
272 * prepare the command
273 */
274 snprintf (command, LINE_LEN,
275 "netsh interface ipv6 add address \"%s\" %s/%d store=active",
276 device_visible_name, address, prefix_len);
277 /*
278 * Set the address
279 */
280 ret = execute_shellcommand (command);
281
282 /* Did it work?*/
283 if (0 != ret)
284 fprintf (stderr, "FATAL: Setting IPv6 address failed: %s\n", strerror (ret));
285 return ret;
286}
287
288
289/**
290 * @brief Removes the IPv6-Address given in address from the interface dev
291 *
292 * @param dev the interface to remove
293 * @param address the IPv4-Address
294 * @param mask the netmask
295 */
296static void
297remove_address6 (const char *address)
298{
299 char command[LINE_LEN];
300 int ret = EINVAL;
301
302 // sanity checking was already done in set_address6
303 /*
304 * prepare the command
305 */
306 snprintf (command, LINE_LEN,
307 "netsh interface ipv6 delete address \"%s\" store=persistent",
308 device_visible_name, address);
309 /*
310 * Set the address
311 */
312 ret = execute_shellcommand (command);
313
314 /* Did it work?*/
315 if (0 != ret)
316 fprintf (stderr, "FATAL: removing IPv6 address failed: %s\n", strerror (ret));
317}
318
319
320/**
321 * @brief Sets the IPv4-Address given in address on the interface dev
322 *
323 * @param dev the interface to configure
324 * @param address the IPv4-Address
325 * @param mask the netmask
326 */
327static int
328set_address4 (const char *address, const char *mask)
329{
330 int ret = EINVAL;
331 char command[LINE_LEN];
332
333 struct sockaddr_in addr;
334 addr.sin_family = AF_INET;
335
336 /*
337 * Parse the address
338 */
339 if (1 != inet_pton (AF_INET, address, &addr.sin_addr.s_addr))
340 {
341 fprintf (stderr, "ERROR: Failed to parse address `%s': %s\n", address,
342 strerror (errno));
343 return -1;
344 }
345 // Set Device to Subnet-Mode?
346 // do we really need tun.c:2925 ?
347
348 /*
349 * prepare the command
350 */
351 snprintf (command, LINE_LEN,
352 "netsh interface ipv4 add address \"%s\" %s %s store=active",
353 device_visible_name, address, mask);
354 /*
355 * Set the address
356 */
357 ret = execute_shellcommand (command);
358
359 /* Did it work?*/
360 if (0 != ret)
361 fprintf (stderr, "FATAL: Setting IPv4 address failed: %s\n", strerror (ret));
362 return ret;
363}
364
365
366/**
367 * @brief Removes the IPv4-Address given in address from the interface dev
368 *
369 * @param dev the interface to remove
370 * @param address the IPv4-Address
371 * @param mask the netmask
372 */
373static void
374remove_address4 (const char *address)
375{
376 char command[LINE_LEN];
377 int ret = EINVAL;
378
379 // sanity checking was already done in set_address4
380
381 /*
382 * prepare the command
383 */
384 snprintf (command, LINE_LEN,
385 "netsh interface ipv4 delete address \"%s\" gateway=all store=persistent",
386 device_visible_name, address);
387 /*
388 * Set the address
389 */
390 ret = execute_shellcommand (command);
391
392 /* Did it work?*/
393 if (0 != ret)
394 fprintf (stderr, "FATAL: removing IPv4 address failed: %s\n", strerror (ret));
395}
396
397
398/**
399 * Setup a new virtual interface to use for tunneling.
400 *
401 * @return: TRUE if setup was successful, else FALSE
402 */
403static BOOL
404setup_interface ()
405{
406 /*
407 * where to find our inf-file. (+ the "full" path, after windows found")
408 *
409 * We do not directly input all the props here, because openvpn will update
410 * these details over time.
411 */
412 char inf_file_path[MAX_PATH];
413 char * temp_inf_filename;
414 char hwidlist[LINE_LEN + 4];
415 char class_name[128];
416 GUID class_guid;
417 int str_length = 0;
418
419 /**
420 * Set the device's hardware ID and add it to a list.
421 * This information will later on identify this device in registry.
422 */
423 strncpy (hwidlist, HARDWARE_ID, LINE_LEN);
424 /**
425 * this is kind of over-complicated, but allows keeps things independent of
426 * how the openvpn-hwid is actually stored.
427 *
428 * A HWID list is double-\0 terminated and \0 separated
429 */
430 str_length = strlen (hwidlist) + 1;
431 strncpy (&hwidlist[str_length], secondary_hwid, LINE_LEN);
432 str_length += strlen (&hwidlist[str_length]) + 1;
433
434 /**
435 * Locate the inf-file, we need to store it somewhere where the system can
436 * find it. A good choice would be CWD/PDW or %WINDIR$\system32\
437 *
438 * TODO: How about win64 in the future?
439 * We need to use a different driver for amd64/i386 !
440 */
441 GetFullPathNameA (INF_FILE, MAX_PATH, inf_file_path, &temp_inf_filename);
442
443 fprintf (stderr, "INFO: Located our driver's .inf file at %s\n", inf_file_path);
444 /**
445 * Bootstrap our device info using the drivers inf-file
446 */
447 if (!SetupDiGetINFClassA (inf_file_path,
448 &class_guid,
449 class_name, sizeof (class_name) / sizeof (char),
450 NULL))
451 return FALSE;
452
453 /**
454 * Collect all the other needed information...
455 * let the system fill our this form
456 */
457 DeviceInfo = SetupDiCreateDeviceInfoList (&class_guid, NULL);
458 if (DeviceInfo == INVALID_HANDLE_VALUE)
459 return FALSE;
460
461 DeviceNode.cbSize = sizeof (SP_DEVINFO_DATA);
462 if (!SetupDiCreateDeviceInfoA (DeviceInfo,
463 class_name,
464 &class_guid,
465 NULL,
466 0,
467 DICD_GENERATE_ID,
468 &DeviceNode))
469 return FALSE;
470
471 /* Deploy all the information collected into the registry */
472 if (!SetupDiSetDeviceRegistryPropertyA (DeviceInfo,
473 &DeviceNode,
474 SPDRP_HARDWAREID,
475 (LPBYTE) hwidlist,
476 str_length * sizeof (char)))
477 return FALSE;
478
479 /* Install our new class(=device) into the system */
480 if (!SetupDiCallClassInstaller (DIF_REGISTERDEVICE,
481 DeviceInfo,
482 &DeviceNode))
483 return FALSE;
484
485 /* This system call tends to take a while (several seconds!) on
486 "modern" Windoze systems */
487 if (!UpdateDriverForPlugAndPlayDevicesA (NULL,
488 secondary_hwid,
489 inf_file_path,
490 INSTALLFLAG_FORCE | INSTALLFLAG_NONINTERACTIVE,
491 NULL)) //reboot required? NEVER!
492 return FALSE;
493
494 fprintf (stderr, "DEBUG: successfully created a network device\n");
495 return TRUE;
496}
497
498
499/**
500 * Remove our new virtual interface to use for tunneling.
501 * This function must be called AFTER setup_interface!
502 *
503 * @return: TRUE if destruction was successful, else FALSE
504 */
505static BOOL
506remove_interface ()
507{
508 SP_REMOVEDEVICE_PARAMS remove;
509
510 if (INVALID_HANDLE_VALUE == DeviceInfo)
511 return FALSE;
512
513 remove.ClassInstallHeader.cbSize = sizeof (SP_CLASSINSTALL_HEADER);
514 remove.HwProfile = 0;
515 remove.Scope = DI_REMOVEDEVICE_GLOBAL;
516 remove.ClassInstallHeader.InstallFunction = DIF_REMOVE;
517 /*
518 * 1. Prepare our existing device information set, and place the
519 * uninstall related information into the structure
520 */
521 if (!SetupDiSetClassInstallParamsA (DeviceInfo,
522 (PSP_DEVINFO_DATA) & DeviceNode,
523 &remove.ClassInstallHeader,
524 sizeof (remove)))
525 return FALSE;
526 /*
527 * 2. Uninstall the virtual interface using the class installer
528 */
529 if (!SetupDiCallClassInstaller (DIF_REMOVE,
530 DeviceInfo,
531 (PSP_DEVINFO_DATA) & DeviceNode))
532 return FALSE;
533
534 SetupDiDestroyDeviceInfoList (DeviceInfo);
535
536 fprintf (stderr, "DEBUG: removed interface successfully\n");
537
538 return TRUE;
539}
540
541
542/**
543 * Do all the lookup necessary to retrieve the inteface's actual name
544 * off the registry.
545 *
546 * @return: TRUE if we were able to lookup the interface's name, else FALSE
547 */
548static BOOL
549resolve_interface_name ()
550{
551 SP_DEVINFO_LIST_DETAIL_DATA device_details;
552 char pnp_instance_id [MAX_DEVICE_ID_LEN];
553 HKEY adapter_key_handle;
554 LONG status;
555 DWORD len;
556 int i = 0;
557 int retrys;
558 BOOL retval = FALSE;
559 char adapter[] = INTERFACE_REGISTRY_LOCATION;
560
561 /* We can obtain the PNP instance ID from our setupapi handle */
562 device_details.cbSize = sizeof (device_details);
563 if (CR_SUCCESS != CM_Get_Device_ID_ExA (DeviceNode.DevInst,
564 (PCHAR) pnp_instance_id,
565 MAX_DEVICE_ID_LEN,
566 0, //must be 0
567 NULL)) //hMachine, we are local
568 return FALSE;
569
570 fprintf (stderr, "DEBUG: Resolving interface name for network device %s\n",pnp_instance_id);
571
572 /* Registry is incredibly slow, retry for up to 30 seconds to allow registry to refresh */
573 for (retrys = 0; retrys < 120 && !retval; retrys++)
574 {
575 /* sleep for 250ms*/
576 Sleep (250);
577
578 /* Now we can use this ID to locate the correct networks interface in registry */
579 if (ERROR_SUCCESS != RegOpenKeyExA (
580 HKEY_LOCAL_MACHINE,
581 adapter,
582 0,
583 KEY_READ,
584 &adapter_key_handle))
585 return FALSE;
586
587 /* Of course there is a multitude of entries here, with arbitrary names,
588 * thus we need to iterate through there.
589 */
590 while (!retval)
591 {
592 char instance_key[256];
593 char query_key [256];
594 HKEY instance_key_handle;
595 char pnpinstanceid_name[] = "PnpInstanceID";
596 char pnpinstanceid_value[256];
597 char adaptername_name[] = "Name";
598 DWORD data_type;
599
600 len = 256 * sizeof (char);
601 /* optain a subkey of {4D36E972-E325-11CE-BFC1-08002BE10318} */
602 status = RegEnumKeyExA (
603 adapter_key_handle,
604 i,
605 instance_key,
606 &len,
607 NULL,
608 NULL,
609 NULL,
610 NULL);
611
612 /* this may fail due to one of two reasons:
613 * we are at the end of the list*/
614 if (ERROR_NO_MORE_ITEMS == status)
615 break;
616 // * we found a broken registry key, continue with the next key.
617 if (ERROR_SUCCESS != status)
618 goto cleanup;
619
620 /* prepare our new query string: */
621 snprintf (query_key, 256, "%s\\%s\\Connection",
622 adapter,
623 instance_key);
624
625 /* look inside instance_key\\Connection */
626 status = RegOpenKeyExA (
627 HKEY_LOCAL_MACHINE,
628 query_key,
629 0,
630 KEY_READ,
631 &instance_key_handle);
632
633 if (status != ERROR_SUCCESS)
634 goto cleanup;
635
636 /* now, read our PnpInstanceID */
637 len = sizeof (pnpinstanceid_value);
638 status = RegQueryValueExA (instance_key_handle,
639 pnpinstanceid_name,
640 NULL, //reserved, always NULL according to MSDN
641 &data_type,
642 (LPBYTE) pnpinstanceid_value,
643 &len);
644
645 if (status != ERROR_SUCCESS || data_type != REG_SZ)
646 goto cleanup;
647
648 /* compare the value we got to our devices PNPInstanceID*/
649 if (0 != strncmp (pnpinstanceid_value, pnp_instance_id,
650 sizeof (pnpinstanceid_value) / sizeof (char)))
651 goto cleanup;
652
653 len = sizeof (device_visible_name);
654 status = RegQueryValueExA (
655 instance_key_handle,
656 adaptername_name,
657 NULL, //reserved, always NULL according to MSDN
658 &data_type,
659 (LPBYTE) device_visible_name,
660 &len);
661
662 if (status != ERROR_SUCCESS || data_type != REG_SZ)
663 goto cleanup;
664
665 /*
666 * we have successfully found OUR instance,
667 * save the device GUID before exiting
668 */
669
670 strncpy (device_guid, instance_key, 256);
671 retval = TRUE;
672 fprintf (stderr, "DEBUG: Interface Name lookup succeeded on retry %d, got \"%s\" %s\n", retrys, device_visible_name, device_guid);
673
674cleanup:
675 RegCloseKey (instance_key_handle);
676
677 ++i;
678 }
679
680 RegCloseKey (adapter_key_handle);
681 }
682 return retval;
683}
684
685
686/**
687 * Determines the version of the installed TAP32 driver and checks if it's sufficiently new for GNUNET
688 *
689 * @param handle the handle to our tap device
690 * @return TRUE if the version is sufficient, else FALSE
691 */
692static BOOL
693check_tapw32_version (HANDLE handle)
694{
695 ULONG version[3];
696 DWORD len;
697 memset (&(version), 0, sizeof (version));
698
699
700 if (DeviceIoControl (handle, TAP_WIN_IOCTL_GET_VERSION,
701 &version, sizeof (version),
702 &version, sizeof (version), &len, NULL))
703 fprintf (stderr, "INFO: TAP-Windows Driver Version %d.%d %s\n",
704 (int) version[0],
705 (int) version[1],
706 (version[2] ? "(DEBUG)" : ""));
707
708 if (version[0] != TAP_WIN_MIN_MAJOR || version[1] < TAP_WIN_MIN_MINOR){
709 fprintf (stderr, "FATAL: This version of gnunet requires a TAP-Windows driver that is at least version %d.%d\n",
710 TAP_WIN_MIN_MAJOR,
711 TAP_WIN_MIN_MINOR);
712 return FALSE;
713 }
714
715 return TRUE;
716}
717
718
719/**
720 * Creates a tun-interface called dev;
721 *
722 * @return the fd to the tun or -1 on error
723 */
724static HANDLE
725init_tun ()
726{
727 char device_path[256];
728 HANDLE handle;
729
730 if (! setup_interface ())
731 {
732 errno = ENODEV;
733 return INVALID_HANDLE_VALUE;
734 }
735
736 if (! resolve_interface_name ())
737 {
738 errno = ENODEV;
739 return INVALID_HANDLE_VALUE;
740 }
741
742 /* Open Windows TAP-Windows adapter */
743 snprintf (device_path, sizeof (device_path), "%s%s%s",
744 USERMODEDEVICEDIR,
745 device_guid,
746 TAP_WIN_SUFFIX);
747
748 handle = CreateFile (
749 device_path,
750 GENERIC_READ | GENERIC_WRITE,
751 0, /* was: FILE_SHARE_READ */
752 0,
753 OPEN_EXISTING,
754 FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
755 0
756 );
757
758 if (handle == INVALID_HANDLE_VALUE)
759 {
760 fprintf (stderr, "FATAL: CreateFile failed on TAP device: %s\n", device_path);
761 return handle;
762 }
763
764 /* get driver version info */
765 if (! check_tapw32_version (handle))
766 {
767 CloseHandle (handle);
768 return INVALID_HANDLE_VALUE;
769 }
770
771 /* TODO (opt?): get MTU-Size */
772
773 fprintf (stderr, "DEBUG: successfully opened TAP device\n");
774 return handle;
775}
776
777
778/**
779 * Brings a TAP device up and sets it to connected state.
780 *
781 * @param handle the handle to our TAP device
782 * @return True if the operation succeeded, else false
783 */
784static BOOL
785tun_up (HANDLE handle)
786{
787 ULONG status = TRUE;
788 DWORD len;
789 if (! DeviceIoControl (handle, TAP_WIN_IOCTL_SET_MEDIA_STATUS,
790 &status, sizeof (status),
791 &status, sizeof (status), &len, NULL))
792 {
793 fprintf (stderr, "FATAL: TAP driver ignored request to UP interface (DeviceIoControl call)\n");
794 return FALSE;
795 }
796
797 /* Wait for the device to go UP, might take some time. */
798 Sleep (TAP32_POSTUP_WAITTIME * 1000);
799 fprintf (stderr, "DEBUG: successfully set TAP device to UP\n");
800
801 return TRUE;
802
803}
804
805
806/**
807 * Attempts to read off an input facility (tap or named pipe) in overlapped mode.
808 *
809 * 1.
810 * If the input facility is in IOSTATE_READY, it will issue a new read operation to the
811 * input handle. Then it goes into IOSTATE_QUEUED state.
812 * In case the read succeeded instantly the input facility enters 3.
813 *
814 * 2.
815 * If the input facility is in IOSTATE_QUEUED state, it will check if the queued read has finished already.
816 * If it has finished, go to state 3.
817 * If it has failed, set IOSTATE_FAILED
818 *
819 * 3.
820 * If the output facility is in state IOSTATE_READY, the read-buffer is copied to the output buffer.
821 * The input facility enters state IOSTATE_READY
822 * The output facility enters state IOSTATE_READY
823 * If the output facility is in state IOSTATE_QUEUED, the input facility enters IOSTATE_WAITING
824 *
825 * IOSTATE_WAITING is reset by the output facility, once it has completed.
826 *
827 * @param input_facility input named pipe or file to work with.
828 * @param output_facility output pipe or file to hand over data to.
829 * @return false if an event reset was impossible (OS error), else true
830 */
831static BOOL
832attempt_read_tap (struct io_facility * input_facility,
833 struct io_facility * output_facility)
834{
835 struct GNUNET_MessageHeader * hdr;
836 unsigned short size;
837 BOOL status;
838
839 switch (input_facility->facility_state)
840 {
841 case IOSTATE_READY:
842 {
843 if (! ResetEvent (input_facility->overlapped.hEvent))
844 {
845 return FALSE;
846 }
847
848 input_facility->buffer_size = 0;
849 status = ReadFile (input_facility->handle,
850 input_facility->buffer,
851 sizeof (input_facility->buffer) - sizeof (struct GNUNET_MessageHeader),
852 &input_facility->buffer_size,
853 &input_facility->overlapped);
854
855 /* Check how the task is handled */
856 if (status)
857 {/* async event processed immediately*/
858
859 /* reset event manually*/
860 if (! SetEvent (input_facility->overlapped.hEvent))
861 return FALSE;
862
863 fprintf (stderr, "DEBUG: tap read succeeded immediately\n");
864
865 /* we successfully read something from the TAP and now need to
866 * send it our via STDOUT. Is that possible at the moment? */
867 if ((IOSTATE_READY == output_facility->facility_state ||
868 IOSTATE_WAITING == output_facility->facility_state)
869 && 0 < input_facility->buffer_size)
870 { /* hand over this buffers content and apply message header for gnunet */
871 hdr = (struct GNUNET_MessageHeader *) output_facility->buffer;
872 size = input_facility->buffer_size + sizeof (struct GNUNET_MessageHeader);
873
874 memcpy (output_facility->buffer + sizeof (struct GNUNET_MessageHeader),
875 input_facility->buffer,
876 input_facility->buffer_size);
877
878 output_facility->buffer_size = size;
879 hdr->size = htons (size);
880 hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
881 output_facility->facility_state = IOSTATE_READY;
882 }
883 else if (0 < input_facility->buffer_size)
884 { /* If we have have read our buffer, wait for our write-partner*/
885 input_facility->facility_state = IOSTATE_WAITING;
886 // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish?
887 }
888 }
889 else /* operation was either queued or failed*/
890 {
891 int err = GetLastError ();
892 if (ERROR_IO_PENDING == err)
893 { /* operation queued */
894 input_facility->facility_state = IOSTATE_QUEUED;
895 }
896 else
897 { /* error occurred, let the rest of the elements finish */
898 input_facility->path_open = FALSE;
899 input_facility->facility_state = IOSTATE_FAILED;
900 if (IOSTATE_WAITING == output_facility->facility_state)
901 output_facility->path_open = FALSE;
902
903 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish\n");
904 }
905 }
906 }
907 return TRUE;
908 // We are queued and should check if the read has finished
909 case IOSTATE_QUEUED:
910 {
911 // there was an operation going on already, check if that has completed now.
912 status = GetOverlappedResult (input_facility->handle,
913 &input_facility->overlapped,
914 &input_facility->buffer_size,
915 FALSE);
916 if (status)
917 {/* successful return for a queued operation */
918 if (! ResetEvent (input_facility->overlapped.hEvent))
919 return FALSE;
920
921 fprintf (stderr, "DEBUG: tap read succeeded delayed\n");
922
923 /* we successfully read something from the TAP and now need to
924 * send it our via STDOUT. Is that possible at the moment? */
925 if ((IOSTATE_READY == output_facility->facility_state ||
926 IOSTATE_WAITING == output_facility->facility_state)
927 && 0 < input_facility->buffer_size)
928 { /* hand over this buffers content and apply message header for gnunet */
929 hdr = (struct GNUNET_MessageHeader *) output_facility->buffer;
930 size = input_facility->buffer_size + sizeof (struct GNUNET_MessageHeader);
931
932 memcpy (output_facility->buffer + sizeof (struct GNUNET_MessageHeader),
933 input_facility->buffer,
934 input_facility->buffer_size);
935
936 output_facility->buffer_size = size;
937 hdr->size = htons(size);
938 hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
939 output_facility->facility_state = IOSTATE_READY;
940 input_facility->facility_state = IOSTATE_READY;
941 }
942 else if (0 < input_facility->buffer_size)
943 { /* If we have have read our buffer, wait for our write-partner*/
944 input_facility->facility_state = IOSTATE_WAITING;
945 // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish?
946 }
947 }
948 else
949 { /* operation still pending/queued or failed? */
950 int err = GetLastError ();
951 if ((ERROR_IO_INCOMPLETE != err) && (ERROR_IO_PENDING != err))
952 { /* error occurred, let the rest of the elements finish */
953 input_facility->path_open = FALSE;
954 input_facility->facility_state = IOSTATE_FAILED;
955 if (IOSTATE_WAITING == output_facility->facility_state)
956 output_facility->path_open = FALSE;
957 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish\n");
958 }
959 }
960 }
961 return TRUE;
962 case IOSTATE_RESUME:
963 hdr = (struct GNUNET_MessageHeader *) output_facility->buffer;
964 size = input_facility->buffer_size + sizeof (struct GNUNET_MessageHeader);
965
966 memcpy (output_facility->buffer + sizeof (struct GNUNET_MessageHeader),
967 input_facility->buffer,
968 input_facility->buffer_size);
969
970 output_facility->buffer_size = size;
971 hdr->size = htons (size);
972 hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
973 output_facility->facility_state = IOSTATE_READY;
974 input_facility->facility_state = IOSTATE_READY;
975 return TRUE;
976 default:
977 return TRUE;
978 }
979}
980
981/**
982 * Attempts to read off an input facility (tap or named pipe) in overlapped mode.
983 *
984 * 1.
985 * If the input facility is in IOSTATE_READY, it will issue a new read operation to the
986 * input handle. Then it goes into IOSTATE_QUEUED state.
987 * In case the read succeeded instantly the input facility enters 3.
988 *
989 * 2.
990 * If the input facility is in IOSTATE_QUEUED state, it will check if the queued read has finished already.
991 * If it has finished, go to state 3.
992 * If it has failed, set IOSTATE_FAILED
993 *
994 * 3.
995 * If the facility is finished with ready
996 * The read-buffer is copied to the output buffer, except for the GNUNET_MessageHeader.
997 * The input facility enters state IOSTATE_READY
998 * The output facility enters state IOSTATE_READY
999 * If the output facility is in state IOSTATE_QUEUED, the input facility enters IOSTATE_WAITING
1000 *
1001 * IOSTATE_WAITING is reset by the output facility, once it has completed.
1002 *
1003 * @param input_facility input named pipe or file to work with.
1004 * @param output_facility output pipe or file to hand over data to.
1005 * @return false if an event reset was impossible (OS error), else true
1006 */
1007static BOOL
1008attempt_read_stdin (struct io_facility * input_facility,
1009 struct io_facility * output_facility)
1010{
1011 struct GNUNET_MessageHeader * hdr;
1012 BOOL status;
1013 switch (input_facility->facility_state)
1014 {
1015 case IOSTATE_READY:
1016 {
1017 if (! ResetEvent (input_facility->overlapped.hEvent))
1018 return FALSE;
1019 input_facility->buffer_size = 0;
1020 status = ReadFile (input_facility->handle,
1021 input_facility->buffer,
1022 sizeof (input_facility->buffer),
1023 &input_facility->buffer_size,
1024 &input_facility->overlapped);
1025
1026 /* Check how the task is handled */
1027 if (status && (sizeof (struct GNUNET_MessageHeader) < input_facility->buffer_size))
1028 {/* async event processed immediately*/
1029 hdr = (struct GNUNET_MessageHeader *) input_facility->buffer;
1030
1031 /* reset event manually*/
1032 if (! SetEvent (input_facility->overlapped.hEvent))
1033 return FALSE;
1034
1035 fprintf (stderr, "DEBUG: stdin read succeeded immediately\n");
1036
1037 if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER ||
1038 ntohs (hdr->size) > sizeof (input_facility->buffer))
1039 {
1040 fprintf (stderr, "WARNING: Protocol violation, got GNUnet Message type %h, size %h\n", ntohs (hdr->type), ntohs (hdr->size));
1041 input_facility->facility_state = IOSTATE_READY;
1042 return TRUE;
1043 }
1044 if (ntohs (hdr->size) > input_facility->buffer_size);
1045 // TODO: add support for partial read
1046
1047 /* we successfully read something from the TAP and now need to
1048 * send it our via STDOUT. Is that possible at the moment? */
1049 if (sizeof (struct GNUNET_MessageHeader) < input_facility->buffer_size)
1050 {
1051 if (IOSTATE_READY == output_facility->facility_state ||
1052 IOSTATE_WAITING == output_facility->facility_state)
1053 {
1054 /* hand over this buffers content and strip gnunet message header */
1055 memcpy (output_facility->buffer,
1056 input_facility->buffer + sizeof (struct GNUNET_MessageHeader),
1057 input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader));
1058 output_facility->buffer_size = input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader);
1059 output_facility->facility_state = IOSTATE_READY;
1060
1061 }
1062 else if (IOSTATE_QUEUED == output_facility->facility_state)
1063 /* If we have have read our buffer, wait for our write-partner*/
1064 input_facility->facility_state = IOSTATE_WAITING;
1065 // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish?
1066 }
1067 }
1068 else if (status && 0 >= input_facility->buffer_size)
1069 {
1070 if (! SetEvent (input_facility->overlapped.hEvent))
1071 return FALSE;
1072
1073 input_facility->facility_state = IOSTATE_READY;
1074 }
1075 else /* operation was either queued or failed*/
1076 {
1077 int err = GetLastError ();
1078 if (ERROR_IO_PENDING == err)
1079 { /* operation queued */
1080 input_facility->facility_state = IOSTATE_QUEUED;
1081 }
1082 else
1083 { /* error occurred, let the rest of the elements finish */
1084 input_facility->path_open = FALSE;
1085 input_facility->facility_state = IOSTATE_FAILED;
1086 if (IOSTATE_WAITING == output_facility->facility_state)
1087 output_facility->path_open = FALSE;
1088
1089 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish\n");
1090 }
1091 }
1092 }
1093 return TRUE;
1094 // We are queued and should check if the read has finished
1095 case IOSTATE_QUEUED:
1096 {
1097 // there was an operation going on already, check if that has completed now.
1098 status = GetOverlappedResult (input_facility->handle,
1099 &input_facility->overlapped,
1100 &input_facility->buffer_size,
1101 FALSE);
1102 if (status)
1103 {/* successful return for a queued operation */
1104 hdr = (struct GNUNET_MessageHeader *) input_facility->buffer;
1105
1106 if (! ResetEvent (input_facility->overlapped.hEvent))
1107 return FALSE;
1108
1109 fprintf (stderr, "DEBUG: stdin read succeeded delayed\n");
1110
1111 if ((ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER) ||
1112 (ntohs (hdr->size) > sizeof (input_facility->buffer)))
1113 {
1114 fprintf (stderr, "WARNING: Protocol violation, got GNUnet Message type %h, size %h\n", ntohs (hdr->type), ntohs (hdr->size));
1115 input_facility->facility_state = IOSTATE_READY;
1116 return TRUE;
1117 }
1118 if (ntohs (hdr->size) > input_facility->buffer_size );
1119 // TODO: add support for partial read
1120
1121 /* we successfully read something from the TAP and now need to
1122 * send it our via STDOUT. Is that possible at the moment? */
1123 if ((IOSTATE_READY == output_facility->facility_state ||
1124 IOSTATE_WAITING == output_facility->facility_state)
1125 && sizeof(struct GNUNET_MessageHeader) < input_facility->buffer_size)
1126 { /* hand over this buffers content and strip gnunet message header */
1127 memcpy (output_facility->buffer,
1128 input_facility->buffer + sizeof(struct GNUNET_MessageHeader),
1129 input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader));
1130 output_facility->buffer_size = input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader);
1131 output_facility->facility_state = IOSTATE_READY;
1132 input_facility->facility_state = IOSTATE_READY;
1133 }
1134 else if (sizeof(struct GNUNET_MessageHeader) < input_facility->buffer_size)
1135 { /* If we have have read our buffer, wait for our write-partner*/
1136 input_facility->facility_state = IOSTATE_WAITING;
1137 // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish?
1138 }
1139 else if (sizeof(struct GNUNET_MessageHeader) >= input_facility->buffer_size)
1140 input_facility->facility_state = IOSTATE_READY;
1141 }
1142 else
1143 { /* operation still pending/queued or failed? */
1144 int err = GetLastError ();
1145 if ((ERROR_IO_INCOMPLETE != err) && (ERROR_IO_PENDING != err))
1146 { /* error occurred, let the rest of the elements finish */
1147 input_facility->path_open = FALSE;
1148 input_facility->facility_state = IOSTATE_FAILED;
1149 if (IOSTATE_WAITING == output_facility->facility_state)
1150 output_facility->path_open = FALSE;
1151 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish\n");
1152 }
1153 }
1154 }
1155 return TRUE;
1156 case IOSTATE_RESUME: /* Our buffer was filled already but our write facility was busy. */
1157 memcpy (output_facility->buffer,
1158 input_facility->buffer + sizeof (struct GNUNET_MessageHeader),
1159 input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader));
1160 output_facility->buffer_size = input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader);
1161 output_facility->facility_state = IOSTATE_READY;
1162 input_facility->facility_state = IOSTATE_READY;
1163 return TRUE;
1164 default:
1165 return TRUE;
1166 }
1167}
1168
1169/**
1170 * Attempts to write to an output facility (tap or named pipe) in overlapped mode.
1171 *
1172 * TODO: high level description
1173 *
1174 * @param output_facility output pipe or file to hand over data to.
1175 * @param input_facility input named pipe or file to work with.
1176 * @return false if an event reset was impossible (OS error), else true
1177 */
1178static BOOL
1179attempt_write (struct io_facility * output_facility,
1180 struct io_facility * input_facility)
1181{
1182 BOOL status;
1183
1184 switch (output_facility->facility_state)
1185 {
1186 case IOSTATE_READY:
1187
1188 if (! ResetEvent (output_facility->overlapped.hEvent))
1189 return FALSE;
1190
1191 output_facility->buffer_size_written = 0;
1192 status = WriteFile (output_facility->handle,
1193 output_facility->buffer,
1194 output_facility->buffer_size,
1195 &output_facility->buffer_size_written,
1196 &output_facility->overlapped);
1197
1198 /* Check how the task was handled */
1199 if (status &&
1200 output_facility->buffer_size_written == output_facility->buffer_size)
1201 {/* async event processed immediately*/
1202
1203 fprintf (stderr, "DEBUG: write succeeded immediately\n");
1204
1205 /* reset event manually*/
1206 if (! SetEvent (output_facility->overlapped.hEvent))
1207 return FALSE;
1208
1209 /* we are now waiting for our buffer to be filled*/
1210 output_facility->facility_state = IOSTATE_WAITING;
1211 output_facility->buffer_size = 0;
1212 output_facility->buffer_size_written = 0;
1213
1214 /* we successfully wrote something and now need to reset our reader */
1215 if (IOSTATE_WAITING == input_facility->facility_state)
1216 input_facility->facility_state = IOSTATE_RESUME;
1217 else if (IOSTATE_FAILED == input_facility->facility_state)
1218 output_facility->path_open = FALSE;
1219 }
1220 else /* operation was either queued or failed*/
1221 {
1222 int err = GetLastError ();
1223 if (ERROR_IO_PENDING == err)
1224 { /* operation queued */
1225 output_facility->facility_state = IOSTATE_QUEUED;
1226 }
1227 else
1228 { /* error occurred, close this path */
1229 output_facility->path_open = FALSE;
1230 output_facility->facility_state = IOSTATE_FAILED;
1231 fprintf (stderr, "FATAL: Write to handle failed, exiting\n");
1232 }
1233 }
1234 return TRUE;
1235 case IOSTATE_QUEUED:
1236 // there was an operation going on already, check if that has completed now.
1237 status = GetOverlappedResult (output_facility->handle,
1238 &output_facility->overlapped,
1239 &output_facility->buffer_size_written,
1240 FALSE);
1241 if (status &&
1242 output_facility->buffer_size_written == output_facility->buffer_size)
1243 {/* successful return for a queued operation */
1244 if (! ResetEvent (output_facility->overlapped.hEvent))
1245 return FALSE;
1246
1247 fprintf (stderr, "DEBUG: write succeeded delayed\n");
1248
1249 /* we are now waiting for our buffer to be filled*/
1250 output_facility->facility_state = IOSTATE_WAITING;
1251 output_facility->buffer_size = 0;
1252 output_facility->buffer_size_written = 0;
1253
1254 /* we successfully wrote something and now need to reset our reader */
1255 if (IOSTATE_WAITING == input_facility->facility_state)
1256 input_facility->facility_state = IOSTATE_RESUME;
1257 else if (IOSTATE_FAILED == input_facility->facility_state)
1258 output_facility->path_open = FALSE;
1259 }
1260 else
1261 { /* operation still pending/queued or failed? */
1262 int err = GetLastError ();
1263 if ((ERROR_IO_INCOMPLETE != err) && (ERROR_IO_PENDING != err))
1264 { /* error occurred, close this path */
1265 output_facility->path_open = FALSE;
1266 output_facility->facility_state = IOSTATE_FAILED;
1267 fprintf (stderr, "FATAL: Write to handle failed, exiting\n");
1268 }
1269 }
1270 default:
1271 return TRUE;
1272 }
1273}
1274
1275
1276/**
1277 * Initialize a overlapped structure
1278 *
1279 * @param elem the element to initilize
1280 * @param initial_state the initial state for this instance
1281 * @param signaled if the hEvent created should default to signaled or not
1282 * @return true on success, else false
1283 */
1284static BOOL
1285initialize_io_facility (struct io_facility * elem,
1286 int initial_state,
1287 BOOL signaled)
1288{
1289 elem->path_open = TRUE;
1290 elem->handle = INVALID_HANDLE_VALUE;
1291 elem->facility_state = initial_state;
1292 elem->buffer_size = 0;
1293 elem->overlapped.hEvent = CreateEvent (NULL, TRUE, signaled, NULL);
1294 if (NULL == elem->overlapped.hEvent)
1295 return FALSE;
1296
1297 return TRUE;
1298}
1299
1300
1301/**
1302 * Start forwarding to and from the tunnel.
1303 *
1304 * @param fd_tun tunnel FD
1305 */
1306static void
1307run (HANDLE tap_handle)
1308{
1309 /* IO-Facility for reading from our virtual interface */
1310 struct io_facility tap_read;
1311 /* IO-Facility for writing to our virtual interface */
1312 struct io_facility tap_write;
1313 /* IO-Facility for reading from stdin */
1314 struct io_facility std_in;
1315 /* IO-Facility for writing to stdout */
1316 struct io_facility std_out;
1317
1318 HANDLE parent_std_in_handle = GetStdHandle (STD_INPUT_HANDLE);
1319 HANDLE parent_std_out_handle = GetStdHandle (STD_OUTPUT_HANDLE);
1320
1321 /* tun up: */
1322 /* we do this HERE and not beforehand (in init_tun()), in contrast to openvpn
1323 * to remove the need to flush the arp cache, handle DHCP and wrong IPs.
1324 *
1325 * DHCP and such are all features we will never use in gnunet afaik.
1326 * But for openvpn those are essential.
1327 */
1328 if (! tun_up (tap_handle))
1329 return;
1330
1331 /* Initialize our overlapped IO structures*/
1332 if (! (initialize_io_facility (&tap_read, IOSTATE_READY, FALSE)
1333 && initialize_io_facility (&tap_write, IOSTATE_WAITING, TRUE)
1334 && initialize_io_facility (&std_in, IOSTATE_READY, FALSE)
1335 && initialize_io_facility (&std_out, IOSTATE_WAITING, TRUE)))
1336 goto teardown_final;
1337
1338 /* Handles for STDIN and STDOUT */
1339 tap_read.handle = tap_handle;
1340 tap_write.handle = tap_handle;
1341
1342#ifdef DEBUG_TO_CONSOLE
1343 /* Debug output to console STDIN/STDOUT*/
1344 std_in.handle = parent_std_in_handle;
1345 std_out.handle = parent_std_out_handle;
1346
1347#else
1348 fprintf (stderr, "DEBUG: reopening stdin/out for overlapped IO\n");
1349 /*
1350 * Find out the types of our handles.
1351 * This part is a problem, because in windows we need to handle files,
1352 * pipes and the console differently.
1353 */
1354 if ((FILE_TYPE_PIPE != GetFileType (parent_std_in_handle)) ||
1355 (FILE_TYPE_PIPE != GetFileType (parent_std_out_handle)))
1356 {
1357 fprintf (stderr, "ERROR: stdin/stdout must be named pipes\n");
1358 goto teardown;
1359 }
1360
1361 std_in.handle = ReOpenFile (parent_std_in_handle,
1362 GENERIC_READ,
1363 FILE_SHARE_WRITE | FILE_SHARE_READ,
1364 FILE_FLAG_OVERLAPPED);
1365
1366 if (INVALID_HANDLE_VALUE == std_in.handle)
1367 {
1368 fprintf (stderr, "FATAL: Could not reopen stdin for in overlapped mode, has to be a named pipe\n");
1369 goto teardown;
1370 }
1371
1372 std_out.handle = ReOpenFile (parent_std_out_handle,
1373 GENERIC_WRITE,
1374 FILE_SHARE_READ,
1375 FILE_FLAG_OVERLAPPED);
1376
1377 if (INVALID_HANDLE_VALUE == std_out.handle)
1378 {
1379 fprintf (stderr, "FATAL: Could not reopen stdout for in overlapped mode, has to be a named pipe\n");
1380 goto teardown;
1381 }
1382#endif
1383
1384 fprintf (stderr, "DEBUG: mainloop has begun\n");
1385
1386 while (std_out.path_open || tap_write.path_open)
1387 {
1388 /* perform READ from stdin if possible */
1389 if (std_in.path_open && (! attempt_read_stdin (&std_in, &tap_write)))
1390 break;
1391
1392 /* perform READ from tap if possible */
1393 if (tap_read.path_open && (! attempt_read_tap (&tap_read, &std_out)))
1394 break;
1395
1396 /* perform WRITE to tap if possible */
1397 if (tap_write.path_open && (! attempt_write (&tap_write, &std_in)))
1398 break;
1399
1400 /* perform WRITE to STDOUT if possible */
1401 if (std_out.path_open && (! attempt_write (&std_out, &tap_read)))
1402 break;
1403 }
1404
1405teardown:
1406
1407 fprintf (stderr, "DEBUG: teardown initiated\n");
1408
1409 CancelIo (tap_handle);
1410 CancelIo (std_in.handle);
1411 CancelIo (std_out.handle);
1412
1413teardown_final:
1414
1415 CloseHandle (tap_handle);
1416}
1417
1418
1419/**
1420 * Open VPN tunnel interface.
1421 *
1422 * @param argc must be 6
1423 * @param argv 0: binary name (gnunet-helper-vpn)
1424 * 1: tunnel interface prefix (gnunet-vpn)
1425 * 2: IPv6 address (::1), "-" to disable
1426 * 3: IPv6 netmask length in bits (64), ignored if #2 is "-"
1427 * 4: IPv4 address (1.2.3.4), "-" to disable
1428 * 5: IPv4 netmask (255.255.0.0), ignored if #4 is "-"
1429 */
1430int
1431main (int argc, char **argv)
1432{
1433 char hwid[LINE_LEN];
1434 HANDLE handle;
1435 int global_ret = 0;
1436 BOOL have_ip4 = FALSE;
1437 BOOL have_ip6 = FALSE;
1438
1439 if (6 != argc)
1440 {
1441 fprintf (stderr, "FATAL: must supply 5 arguments\nUsage:\ngnunet-helper-vpn <if name prefix> <address6 or \"-\"> <netbits6> <address4 or \"-\"> <netmask4>\n", argv[0]);
1442 return 1;
1443 }
1444
1445 strncpy (hwid, argv[1], LINE_LEN);
1446 hwid[LINE_LEN - 1] = '\0';
1447
1448 /*
1449 * We use our PID for finding/resolving the control-panel name of our virtual
1450 * device. PIDs are (of course) unique at runtime, thus we can safely use it
1451 * as additional hardware-id for our device.
1452 */
1453 snprintf (secondary_hwid, LINE_LEN / 2, "%s-%d",
1454 hwid,
1455 _getpid ());
1456
1457 if (INVALID_HANDLE_VALUE == (handle = init_tun ()))
1458 {
1459 fprintf (stderr, "FATAL: could not initialize virtual-interface %s with IPv6 %s/%s and IPv4 %s/%s\n",
1460 hwid,
1461 argv[2],
1462 argv[3],
1463 argv[4],
1464 argv[5]);
1465 global_ret = -1;
1466 goto cleanup;
1467 }
1468
1469 fprintf (stderr, "DEBUG: Setting IPs, if needed\n");
1470 if (0 != strcmp (argv[2], "-"))
1471 {
1472 const char *address = argv[2];
1473 long prefix_len = atol (argv[3]);
1474
1475 if ((prefix_len < 1) || (prefix_len > 127))
1476 {
1477 fprintf (stderr, "FATAL: ipv6 prefix_len out of range\n");
1478 global_ret = -1;
1479 goto cleanup;
1480 }
1481
1482 fprintf (stderr, "DEBUG: Setting IP6 address: %s/%d\n",address,prefix_len);
1483 if (0 != (global_ret = set_address6 (address, prefix_len)))
1484 goto cleanup;
1485
1486 have_ip6 = TRUE;
1487 }
1488
1489 if (0 != strcmp (argv[4], "-"))
1490 {
1491 const char *address = argv[4];
1492 const char *mask = argv[5];
1493
1494 fprintf (stderr, "DEBUG: Setting IP4 address: %s/%s\n",address,mask);
1495 if (0 != (global_ret = set_address4 (address, mask)))
1496 goto cleanup;
1497
1498 have_ip4 = TRUE;
1499 }
1500
1501 run (handle);
1502 global_ret = 0;
1503cleanup:
1504
1505 if (have_ip4)
1506 {
1507 const char *address = argv[4];
1508 fprintf (stderr, "DEBUG: Removing IP4 address\n");
1509 remove_address4 (address);
1510 }
1511 if (have_ip6)
1512 {
1513 const char *address = argv[2];
1514 fprintf (stderr, "DEBUG: Removing IP6 address\n");
1515 remove_address6 (address);
1516 }
1517
1518 fprintf (stderr, "DEBUG: removing interface\n");
1519 remove_interface ();
1520 fprintf (stderr, "DEBUG: graceful exit completed\n");
1521
1522 return global_ret;
1523}