2 This file is part of GNUnet.
3 (C) 2010, 2012 Christian Grothoff
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.
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.
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.
22 * @file vpn/gnunet-helper-vpn-windows.c
23 * @brief the helper for the VPN 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
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):
37 #include <ddk/cfgmgr32.h>
38 #include <ddk/newdev.h>
42 #include "tap-windows.h"
44 * Need 'struct GNUNET_MessageHeader'.
46 #include "gnunet_common.h"
49 * Need VPN message types.
51 #include "gnunet_protocols.h"
54 * Should we print (interesting|debug) messages that can happen during
57 #define DEBUG GNUNET_NO
60 /* FIXME: define with varargs... */
61 #define LOG_DEBUG(msg) fprintf (stderr, "%s", msg);
63 #define LOG_DEBUG(msg) do {} while (0)
68 * Maximum size of a GNUnet message (GNUNET_SERVER_MAX_MESSAGE_SIZE)
70 #define MAX_SIZE 65536
73 * Name or Path+Name of our win32 driver.
74 * The .sys and .cat files HAVE to be in the same location as this file!
76 #define INF_FILE "share/gnunet/tapw32/OemWin2k.inf"
79 * Name or Path+Name of our win64 driver.
80 * The .sys and .cat files HAVE to be in the same location as this file!
82 #define INF_FILE64 "share/gnunet/tapw64/OemWin2k.inf"
85 * Hardware ID used in the inf-file.
86 * This might change over time, as openvpn advances their driver
88 #define HARDWARE_ID "tap0901"
91 * Minimum major-id of the driver version we can work with
93 #define TAP_WIN_MIN_MAJOR 9
96 * Minimum minor-id of the driver version we can work with.
97 * v <= 7 has buggy IPv6.
98 * v == 8 is broken for small IPv4 Packets
100 #define TAP_WIN_MIN_MINOR 9
103 * Time in seconds to wait for our virtual device to go up after telling it to do so.
105 * openvpn doesn't specify a value, 4 seems sane for testing, even for openwrt
106 * (in fact, 4 was chosen by a fair dice roll...)
108 #define TAP32_POSTUP_WAITTIME 4
111 * Location of the network interface list resides in registry.
113 #define INTERFACE_REGISTRY_LOCATION "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
116 * Our local process' PID. Used for creating a sufficiently unique additional
117 * hardware ID for our device.
119 static char secondary_hwid[LINE_LEN / 2];
122 * Device's visible Name, used to identify a network device in netsh.
123 * eg: "Local Area Connection 9"
125 static char device_visible_name[256];
128 * This is our own local instance of a virtual network interface
129 * It is (somewhat) equivalent to using tun/tap in unixoid systems
131 * Upon initialization, we create such an device node.
132 * Upon termination, we remove it again.
134 * If we crash this device might stay around.
136 static HDEVINFO DeviceInfo = INVALID_HANDLE_VALUE;
139 * Registry Key we hand over to windows to spawn a new virtual interface
141 static SP_DEVINFO_DATA DeviceNode;
144 * GUID of our virtual device in the form of
145 * {12345678-1234-1234-1234-123456789abc} - in hex
147 static char device_guid[256];
151 * Possible states of an IO facility.
157 * overlapped I/O is ready for work
162 * overlapped I/O has been queued
167 * overlapped I/O has finished, but is waiting for it's write-partner
172 * there is a full buffer waiting
177 * Operlapped IO states for facility objects
178 * overlapped I/O has failed, stop processing
186 * A IO Object + read/writebuffer + buffer-size for windows asynchronous IO handling
191 * The mode the state machine associated with this object is in.
193 enum IO_State facility_state;
196 * If the path is open or blocked in general (used for quickly checking)
198 BOOL path_open; // BOOL is winbool (int), NOT boolean (unsigned char)!
201 * Windows Object-Handle (used for accessing TAP and STDIN/STDOUT)
206 * Overlaped IO structure used for asynchronous IO in windows.
208 OVERLAPPED overlapped;
211 * Buffer for reading things to and writing from...
213 unsigned char buffer[MAX_SIZE];
216 * How much of this buffer was used when reading or how much data can be written
221 * Amount of data actually written or read by readfile/writefile.
223 DWORD buffer_size_processed;
226 * How much of this buffer we have writte in total
228 DWORD buffer_size_written;
232 * ReOpenFile is only available as of XP SP2 and 2003 SP1
234 WINBASEAPI HANDLE WINAPI ReOpenFile (HANDLE, DWORD, DWORD, DWORD);
237 * IsWow64Process definition for our is_win64, as this is a kernel function
239 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
242 * Determines if the host OS is win32 or win64
250 //this is a win64 binary,
252 #elif defined(_WIN32)
253 //this is a 32bit binary, and we need to check if we are running in WOW64
254 BOOL success = FALSE;
255 BOOL on_wow64 = FALSE;
256 LPFN_ISWOW64PROCESS IsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandle ("kernel32"), "IsWow64Process");
258 if (NULL != IsWow64Process)
259 success = IsWow64Process (GetCurrentProcess (), &on_wow64);
261 return success && on_wow64;
265 * Wrapper for executing a shellcommand in windows.
267 * @param command - the command + parameters to execute
268 * @return * exitcode of the program executed,
269 * * EINVAL (cmd/file not found)
270 * * EPIPE (could not read STDOUT)
273 execute_shellcommand (const char *command)
277 if ( (NULL == command) ||
278 (NULL == (pipe = _popen (command, "rt"))) )
282 fprintf (stderr, "DEBUG: Command output: \n");
283 char output[LINE_LEN];
284 while (NULL != fgets (output, sizeof (output), pipe))
285 fprintf (stderr, "%s", output);
288 return _pclose (pipe);
293 * @brief Sets the IPv6-Address given in address on the interface dev
295 * @param address the IPv6-Address
296 * @param prefix_len the length of the network-prefix
299 set_address6 (const char *address, unsigned long prefix_len)
302 char command[LINE_LEN];
303 struct sockaddr_in6 sa6;
306 * parse the new address
308 memset (&sa6, 0, sizeof (struct sockaddr_in6));
309 sa6.sin6_family = AF_INET6;
310 if (1 != inet_pton (AF_INET6, address, &sa6.sin6_addr.s6_addr))
312 fprintf (stderr, "ERROR: Failed to parse address `%s': %s\n", address,
318 * prepare the command
320 snprintf (command, LINE_LEN,
321 "netsh interface ipv6 add address \"%s\" %s/%d store=active",
322 device_visible_name, address, prefix_len);
326 ret = execute_shellcommand (command);
330 fprintf (stderr, "FATAL: Setting IPv6 address failed: %s\n", strerror (ret));
336 * @brief Removes the IPv6-Address given in address from the interface dev
338 * @param address the IPv4-Address
341 remove_address6 (const char *address)
343 char command[LINE_LEN];
346 // sanity checking was already done in set_address6
348 * prepare the command
350 snprintf (command, LINE_LEN,
351 "netsh interface ipv6 delete address \"%s\" store=persistent",
352 device_visible_name, address);
356 ret = execute_shellcommand (command);
360 fprintf (stderr, "FATAL: removing IPv6 address failed: %s\n", strerror (ret));
365 * @brief Sets the IPv4-Address given in address on the interface dev
367 * @param address the IPv4-Address
368 * @param mask the netmask
371 set_address4 (const char *address, const char *mask)
374 char command[LINE_LEN];
376 struct sockaddr_in addr;
377 addr.sin_family = AF_INET;
382 if (1 != inet_pton (AF_INET, address, &addr.sin_addr.s_addr))
384 fprintf (stderr, "ERROR: Failed to parse address `%s': %s\n", address,
388 // Set Device to Subnet-Mode? do we really need openvpn/tun.c:2925 ?
391 * prepare the command
393 snprintf (command, LINE_LEN,
394 "netsh interface ipv4 add address \"%s\" %s %s store=active",
395 device_visible_name, address, mask);
399 ret = execute_shellcommand (command);
403 fprintf (stderr, "FATAL: Setting IPv4 address failed: %s\n", strerror (ret));
409 * @brief Removes the IPv4-Address given in address from the interface dev
411 * @param address the IPv4-Address
414 remove_address4 (const char *address)
416 char command[LINE_LEN];
419 // sanity checking was already done in set_address4
422 * prepare the command
424 snprintf (command, LINE_LEN,
425 "netsh interface ipv4 delete address \"%s\" gateway=all store=persistent",
426 device_visible_name, address);
430 ret = execute_shellcommand (command);
434 fprintf (stderr, "FATAL: removing IPv4 address failed: %s\n", strerror (ret));
439 * Setup a new virtual interface to use for tunneling.
441 * @return: TRUE if setup was successful, else FALSE
447 * where to find our inf-file. (+ the "full" path, after windows found")
449 * We do not directly input all the props here, because openvpn will update
450 * these details over time.
452 char inf_file_path[MAX_PATH];
453 char * temp_inf_filename;
454 char hwidlist[LINE_LEN + 4];
455 char class_name[128];
460 * Set the device's hardware ID and add it to a list.
461 * This information will later on identify this device in registry.
463 strncpy (hwidlist, HARDWARE_ID, LINE_LEN);
465 * this is kind of over-complicated, but allows keeps things independent of
466 * how the openvpn-hwid is actually stored.
468 * A HWID list is double-\0 terminated and \0 separated
470 str_length = strlen (hwidlist) + 1;
471 strncpy (&hwidlist[str_length], secondary_hwid, LINE_LEN);
472 str_length += strlen (&hwidlist[str_length]) + 1;
475 * Locate the inf-file, we need to store it somewhere where the system can
476 * find it. We need to pick the correct driver for win32/win64.
479 GetFullPathNameA (INF_FILE64, MAX_PATH, inf_file_path, &temp_inf_filename);
481 GetFullPathNameA (INF_FILE, MAX_PATH, inf_file_path, &temp_inf_filename);
483 fprintf (stderr, "INFO: Located our driver's .inf file at %s\n", inf_file_path);
485 * Bootstrap our device info using the drivers inf-file
487 if ( ! SetupDiGetINFClassA (inf_file_path,
489 class_name, sizeof (class_name) / sizeof (char),
494 * Collect all the other needed information...
495 * let the system fill our this form
497 DeviceInfo = SetupDiCreateDeviceInfoList (&class_guid, NULL);
498 if (DeviceInfo == INVALID_HANDLE_VALUE)
501 DeviceNode.cbSize = sizeof (SP_DEVINFO_DATA);
502 if ( ! SetupDiCreateDeviceInfoA (DeviceInfo,
511 /* Deploy all the information collected into the registry */
512 if ( ! SetupDiSetDeviceRegistryPropertyA (DeviceInfo,
516 str_length * sizeof (char)))
519 /* Install our new class(=device) into the system */
520 if ( ! SetupDiCallClassInstaller (DIF_REGISTERDEVICE,
525 /* This system call tends to take a while (several seconds!) on
526 "modern" Windoze systems */
527 if ( ! UpdateDriverForPlugAndPlayDevicesA (NULL,
530 INSTALLFLAG_FORCE | INSTALLFLAG_NONINTERACTIVE,
531 NULL)) //reboot required? NEVER!
534 fprintf (stderr, "DEBUG: successfully created a network device\n");
540 * Remove our new virtual interface to use for tunneling.
541 * This function must be called AFTER setup_interface!
543 * @return: TRUE if destruction was successful, else FALSE
548 SP_REMOVEDEVICE_PARAMS remove;
550 if (INVALID_HANDLE_VALUE == DeviceInfo)
553 remove.ClassInstallHeader.cbSize = sizeof (SP_CLASSINSTALL_HEADER);
554 remove.HwProfile = 0;
555 remove.Scope = DI_REMOVEDEVICE_GLOBAL;
556 remove.ClassInstallHeader.InstallFunction = DIF_REMOVE;
558 * 1. Prepare our existing device information set, and place the
559 * uninstall related information into the structure
561 if ( ! SetupDiSetClassInstallParamsA (DeviceInfo,
562 (PSP_DEVINFO_DATA) & DeviceNode,
563 &remove.ClassInstallHeader,
567 * 2. Uninstall the virtual interface using the class installer
569 if ( ! SetupDiCallClassInstaller (DIF_REMOVE,
571 (PSP_DEVINFO_DATA) & DeviceNode))
574 SetupDiDestroyDeviceInfoList (DeviceInfo);
576 fprintf (stderr, "DEBUG: removed interface successfully\n");
583 * Do all the lookup necessary to retrieve the inteface's actual name
586 * @return: TRUE if we were able to lookup the interface's name, else FALSE
589 resolve_interface_name ()
591 SP_DEVINFO_LIST_DETAIL_DATA device_details;
592 char pnp_instance_id [MAX_DEVICE_ID_LEN];
593 HKEY adapter_key_handle;
599 char adapter[] = INTERFACE_REGISTRY_LOCATION;
601 /* We can obtain the PNP instance ID from our setupapi handle */
602 device_details.cbSize = sizeof (device_details);
603 if (CR_SUCCESS != CM_Get_Device_ID_ExA (DeviceNode.DevInst,
604 (PCHAR) pnp_instance_id,
607 NULL)) //hMachine, we are local
610 fprintf (stderr, "DEBUG: Resolving interface name for network device %s\n",pnp_instance_id);
612 /* Registry is incredibly slow, retry for up to 30 seconds to allow registry to refresh */
613 for (retrys = 0; retrys < 120 && !retval; retrys++)
618 /* Now we can use this ID to locate the correct networks interface in registry */
619 if (ERROR_SUCCESS != RegOpenKeyExA (
624 &adapter_key_handle))
627 /* Of course there is a multitude of entries here, with arbitrary names,
628 * thus we need to iterate through there.
632 char instance_key[256];
633 char query_key [256];
634 HKEY instance_key_handle;
635 char pnpinstanceid_name[] = "PnpInstanceID";
636 char pnpinstanceid_value[256];
637 char adaptername_name[] = "Name";
640 len = 256 * sizeof (char);
641 /* optain a subkey of {4D36E972-E325-11CE-BFC1-08002BE10318} */
642 status = RegEnumKeyExA (
652 /* this may fail due to one of two reasons:
653 * we are at the end of the list*/
654 if (ERROR_NO_MORE_ITEMS == status)
656 // * we found a broken registry key, continue with the next key.
657 if (ERROR_SUCCESS != status)
660 /* prepare our new query string: */
661 snprintf (query_key, 256, "%s\\%s\\Connection",
665 /* look inside instance_key\\Connection */
666 if (ERROR_SUCCESS != RegOpenKeyExA (
671 &instance_key_handle))
674 /* now, read our PnpInstanceID */
675 len = sizeof (pnpinstanceid_value);
676 status = RegQueryValueExA (instance_key_handle,
678 NULL, //reserved, always NULL according to MSDN
680 (LPBYTE) pnpinstanceid_value,
683 if (status != ERROR_SUCCESS || data_type != REG_SZ)
686 /* compare the value we got to our devices PNPInstanceID*/
687 if (0 != strncmp (pnpinstanceid_value, pnp_instance_id,
688 sizeof (pnpinstanceid_value) / sizeof (char)))
691 len = sizeof (device_visible_name);
692 status = RegQueryValueExA (
695 NULL, //reserved, always NULL according to MSDN
697 (LPBYTE) device_visible_name,
700 if (status != ERROR_SUCCESS || data_type != REG_SZ)
704 * we have successfully found OUR instance,
705 * save the device GUID before exiting
708 strncpy (device_guid, instance_key, 256);
710 fprintf (stderr, "DEBUG: Interface Name lookup succeeded on retry %d, got \"%s\" %s\n", retrys, device_visible_name, device_guid);
713 RegCloseKey (instance_key_handle);
718 RegCloseKey (adapter_key_handle);
725 * Determines the version of the installed TAP32 driver and checks if it's sufficiently new for GNUNET
727 * @param handle the handle to our tap device
728 * @return TRUE if the version is sufficient, else FALSE
731 check_tapw32_version (HANDLE handle)
735 memset (&(version), 0, sizeof (version));
737 if (DeviceIoControl (handle, TAP_WIN_IOCTL_GET_VERSION,
738 &version, sizeof (version),
739 &version, sizeof (version), &len, NULL))
740 fprintf (stderr, "INFO: TAP-Windows Driver Version %d.%d %s\n",
743 (version[2] ? "(DEBUG)" : ""));
745 if ((version[0] != TAP_WIN_MIN_MAJOR) ||
746 (version[1] < TAP_WIN_MIN_MINOR )){
747 fprintf (stderr, "FATAL: This version of gnunet requires a TAP-Windows driver that is at least version %d.%d\n",
758 * Creates a tun-interface called dev;
760 * @return the fd to the tun or -1 on error
765 char device_path[256];
768 if (! setup_interface ())
771 return INVALID_HANDLE_VALUE;
774 if (! resolve_interface_name ())
777 return INVALID_HANDLE_VALUE;
780 /* Open Windows TAP-Windows adapter */
781 snprintf (device_path, sizeof (device_path), "%s%s%s",
786 handle = CreateFile (
788 GENERIC_READ | GENERIC_WRITE,
789 0, /* was: FILE_SHARE_READ */
792 FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
796 if (INVALID_HANDLE_VALUE == handle)
798 fprintf (stderr, "FATAL: CreateFile failed on TAP device: %s\n", device_path);
802 /* get driver version info */
803 if (! check_tapw32_version (handle))
805 CloseHandle (handle);
806 return INVALID_HANDLE_VALUE;
809 /* TODO (opt?): get MTU-Size */
811 fprintf (stderr, "DEBUG: successfully opened TAP device\n");
817 * Brings a TAP device up and sets it to connected state.
819 * @param handle the handle to our TAP device
820 * @return True if the operation succeeded, else false
823 tun_up (HANDLE handle)
827 if (! DeviceIoControl (handle, TAP_WIN_IOCTL_SET_MEDIA_STATUS,
828 &status, sizeof (status),
829 &status, sizeof (status), &len, NULL))
831 fprintf (stderr, "FATAL: TAP driver ignored request to UP interface (DeviceIoControl call)\n");
835 /* Wait for the device to go UP, might take some time. */
836 Sleep (TAP32_POSTUP_WAITTIME * 1000);
837 fprintf (stderr, "DEBUG: successfully set TAP device to UP\n");
844 * Attempts to read off an input facility (tap or named pipe) in overlapped mode.
847 * If the input facility is in IOSTATE_READY, it will issue a new read operation to the
848 * input handle. Then it goes into IOSTATE_QUEUED state.
849 * In case the read succeeded instantly the input facility enters 3.
852 * If the input facility is in IOSTATE_QUEUED state, it will check if the queued read has finished already.
853 * If it has finished, go to state 3.
854 * If it has failed, set IOSTATE_FAILED
857 * If the output facility is in state IOSTATE_READY, the read-buffer is copied to the output buffer.
858 * The input facility enters state IOSTATE_READY
859 * The output facility enters state IOSTATE_READY
860 * If the output facility is in state IOSTATE_QUEUED, the input facility enters IOSTATE_WAITING
862 * IOSTATE_WAITING is reset by the output facility, once it has completed.
864 * @param input_facility input named pipe or file to work with.
865 * @param output_facility output pipe or file to hand over data to.
866 * @return false if an event reset was impossible (OS error), else true
869 attempt_read_tap (struct io_facility * input_facility,
870 struct io_facility * output_facility)
872 struct GNUNET_MessageHeader * hdr;
875 switch (input_facility->facility_state)
879 if (! ResetEvent (input_facility->overlapped.hEvent))
884 input_facility->buffer_size = 0;
886 /* Check how the task is handled */
887 if (ReadFile (input_facility->handle,
888 input_facility->buffer,
889 sizeof (input_facility->buffer) - sizeof (struct GNUNET_MessageHeader),
890 &input_facility->buffer_size,
891 &input_facility->overlapped))
892 {/* async event processed immediately*/
894 /* reset event manually*/
895 if (! SetEvent (input_facility->overlapped.hEvent))
898 fprintf (stderr, "DEBUG: tap read succeeded immediately\n");
900 /* we successfully read something from the TAP and now need to
901 * send it our via STDOUT. Is that possible at the moment? */
902 if ((IOSTATE_READY == output_facility->facility_state ||
903 IOSTATE_WAITING == output_facility->facility_state)
904 && (0 < input_facility->buffer_size))
905 { /* hand over this buffers content and apply message header for gnunet */
906 hdr = (struct GNUNET_MessageHeader *) output_facility->buffer;
907 size = input_facility->buffer_size + sizeof (struct GNUNET_MessageHeader);
909 memcpy (output_facility->buffer + sizeof (struct GNUNET_MessageHeader),
910 input_facility->buffer,
911 input_facility->buffer_size);
913 output_facility->buffer_size = size;
914 hdr->size = htons (size);
915 hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
916 output_facility->facility_state = IOSTATE_READY;
918 else if (0 < input_facility->buffer_size)
919 /* If we have have read our buffer, wait for our write-partner*/
920 input_facility->facility_state = IOSTATE_WAITING;
922 else /* operation was either queued or failed*/
924 int err = GetLastError ();
925 if (ERROR_IO_PENDING == err)
926 { /* operation queued */
927 input_facility->facility_state = IOSTATE_QUEUED;
930 { /* error occurred, let the rest of the elements finish */
931 input_facility->path_open = FALSE;
932 input_facility->facility_state = IOSTATE_FAILED;
933 if (IOSTATE_WAITING == output_facility->facility_state)
934 output_facility->path_open = FALSE;
936 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish\n");
941 // We are queued and should check if the read has finished
944 // there was an operation going on already, check if that has completed now.
946 if (GetOverlappedResult (input_facility->handle,
947 &input_facility->overlapped,
948 &input_facility->buffer_size,
950 {/* successful return for a queued operation */
951 if (! ResetEvent (input_facility->overlapped.hEvent))
954 fprintf (stderr, "DEBUG: tap read succeeded delayed\n");
956 /* we successfully read something from the TAP and now need to
957 * send it our via STDOUT. Is that possible at the moment? */
958 if ((IOSTATE_READY == output_facility->facility_state ||
959 IOSTATE_WAITING == output_facility->facility_state)
960 && 0 < input_facility->buffer_size)
961 { /* hand over this buffers content and apply message header for gnunet */
962 hdr = (struct GNUNET_MessageHeader *) output_facility->buffer;
963 size = input_facility->buffer_size + sizeof (struct GNUNET_MessageHeader);
965 memcpy (output_facility->buffer + sizeof (struct GNUNET_MessageHeader),
966 input_facility->buffer,
967 input_facility->buffer_size);
969 output_facility->buffer_size = size;
970 hdr->size = htons(size);
971 hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
972 output_facility->facility_state = IOSTATE_READY;
973 input_facility->facility_state = IOSTATE_READY;
975 else if (0 < input_facility->buffer_size)
976 { /* If we have have read our buffer, wait for our write-partner*/
977 input_facility->facility_state = IOSTATE_WAITING;
978 // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish?
982 { /* operation still pending/queued or failed? */
983 int err = GetLastError ();
984 if ((ERROR_IO_INCOMPLETE != err) && (ERROR_IO_PENDING != err))
985 { /* error occurred, let the rest of the elements finish */
986 input_facility->path_open = FALSE;
987 input_facility->facility_state = IOSTATE_FAILED;
988 if (IOSTATE_WAITING == output_facility->facility_state)
989 output_facility->path_open = FALSE;
990 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish\n");
996 hdr = (struct GNUNET_MessageHeader *) output_facility->buffer;
997 size = input_facility->buffer_size + sizeof (struct GNUNET_MessageHeader);
999 memcpy (output_facility->buffer + sizeof (struct GNUNET_MessageHeader),
1000 input_facility->buffer,
1001 input_facility->buffer_size);
1003 output_facility->buffer_size = size;
1004 hdr->size = htons (size);
1005 hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
1006 output_facility->facility_state = IOSTATE_READY;
1007 input_facility->facility_state = IOSTATE_READY;
1016 * Attempts to read off an input facility (tap or named pipe) in overlapped mode.
1019 * If the input facility is in IOSTATE_READY, it will issue a new read operation to the
1020 * input handle. Then it goes into IOSTATE_QUEUED state.
1021 * In case the read succeeded instantly the input facility enters 3.
1024 * If the input facility is in IOSTATE_QUEUED state, it will check if the queued read has finished already.
1025 * If it has finished, go to state 3.
1026 * If it has failed, set IOSTATE_FAILED
1029 * If the facility is finished with ready
1030 * The read-buffer is copied to the output buffer, except for the GNUNET_MessageHeader.
1031 * The input facility enters state IOSTATE_READY
1032 * The output facility enters state IOSTATE_READY
1033 * If the output facility is in state IOSTATE_QUEUED, the input facility enters IOSTATE_WAITING
1035 * IOSTATE_WAITING is reset by the output facility, once it has completed.
1037 * @param input_facility input named pipe or file to work with.
1038 * @param output_facility output pipe or file to hand over data to.
1039 * @return false if an event reset was impossible (OS error), else true
1042 attempt_read_stdin (struct io_facility * input_facility,
1043 struct io_facility * output_facility)
1045 struct GNUNET_MessageHeader * hdr;
1047 switch (input_facility->facility_state)
1051 input_facility->buffer_size = 0;
1053 partial_read_iostate_ready:
1054 if (! ResetEvent (input_facility->overlapped.hEvent))
1057 /* Check how the task is handled */
1058 if (ReadFile (input_facility->handle,
1059 input_facility->buffer + input_facility->buffer_size,
1060 sizeof (input_facility->buffer) - input_facility->buffer_size,
1061 &input_facility->buffer_size_processed,
1062 &input_facility->overlapped))
1063 {/* async event processed immediately*/
1064 hdr = (struct GNUNET_MessageHeader *) input_facility->buffer;
1066 /* reset event manually*/
1067 if (!SetEvent (input_facility->overlapped.hEvent))
1070 fprintf (stderr, "DEBUG: stdin read succeeded immediately\n");
1071 input_facility->buffer_size += input_facility->buffer_size_processed;
1073 if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER ||
1074 ntohs (hdr->size) > sizeof (input_facility->buffer))
1076 fprintf (stderr, "WARNING: Protocol violation, got GNUnet Message type %h, size %h\n", ntohs (hdr->type), ntohs (hdr->size));
1077 input_facility->facility_state = IOSTATE_READY;
1080 /* we got the a part of a packet */
1081 if (ntohs (hdr->size) > input_facility->buffer_size)
1082 goto partial_read_iostate_ready;
1084 /* have we read more than 0 bytes of payload? (sizeread > header)*/
1085 if (input_facility->buffer_size > sizeof (struct GNUNET_MessageHeader) &&
1086 ((IOSTATE_READY == output_facility->facility_state) ||
1087 (IOSTATE_WAITING == output_facility->facility_state)))
1088 {/* we successfully read something from the TAP and now need to
1089 * send it our via STDOUT. Is that possible at the moment? */
1091 /* hand over this buffers content and strip gnunet message header */
1092 memcpy (output_facility->buffer,
1093 input_facility->buffer + sizeof (struct GNUNET_MessageHeader),
1094 input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader));
1095 output_facility->buffer_size = input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader);
1096 output_facility->facility_state = IOSTATE_READY;
1097 input_facility->facility_state = IOSTATE_READY;
1099 else if (input_facility->buffer_size > sizeof (struct GNUNET_MessageHeader))
1100 /* If we have have read our buffer, wait for our write-partner*/
1101 input_facility->facility_state = IOSTATE_WAITING;
1102 else /* we read nothing */
1103 input_facility->facility_state = IOSTATE_READY;
1105 else /* operation was either queued or failed*/
1107 int err = GetLastError ();
1108 if (ERROR_IO_PENDING == err) /* operation queued */
1109 input_facility->facility_state = IOSTATE_QUEUED;
1111 { /* error occurred, let the rest of the elements finish */
1112 input_facility->path_open = FALSE;
1113 input_facility->facility_state = IOSTATE_FAILED;
1114 if (IOSTATE_WAITING == output_facility->facility_state)
1115 output_facility->path_open = FALSE;
1117 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish\n");
1122 // We are queued and should check if the read has finished
1123 case IOSTATE_QUEUED:
1125 // there was an operation going on already, check if that has completed now.
1126 if (GetOverlappedResult (input_facility->handle,
1127 &input_facility->overlapped,
1128 &input_facility->buffer_size_processed,
1130 {/* successful return for a queued operation */
1131 hdr = (struct GNUNET_MessageHeader *) input_facility->buffer;
1133 if (! ResetEvent (input_facility->overlapped.hEvent))
1136 fprintf (stderr, "DEBUG: stdin read succeeded delayed\n");
1137 input_facility->buffer_size += input_facility->buffer_size_processed;
1139 if ((ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER) ||
1140 (ntohs (hdr->size) > sizeof (input_facility->buffer)))
1142 fprintf (stderr, "WARNING: Protocol violation, got GNUnet Message type %h, size %h\n", ntohs (hdr->type), ntohs (hdr->size));
1143 input_facility->facility_state = IOSTATE_READY;
1146 /* we got the a part of a packet */
1147 if (ntohs (hdr->size) > input_facility->buffer_size );
1148 goto partial_read_iostate_ready;
1150 /* we successfully read something from the TAP and now need to
1151 * send it our via STDOUT. Is that possible at the moment? */
1152 if ((IOSTATE_READY == output_facility->facility_state ||
1153 IOSTATE_WAITING == output_facility->facility_state)
1154 && input_facility->buffer_size > sizeof(struct GNUNET_MessageHeader))
1155 { /* hand over this buffers content and strip gnunet message header */
1156 memcpy (output_facility->buffer,
1157 input_facility->buffer + sizeof(struct GNUNET_MessageHeader),
1158 input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader));
1159 output_facility->buffer_size = input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader);
1160 output_facility->facility_state = IOSTATE_READY;
1161 input_facility->facility_state = IOSTATE_READY;
1163 else if (input_facility->buffer_size > sizeof(struct GNUNET_MessageHeader))
1164 input_facility->facility_state = IOSTATE_WAITING;
1166 input_facility->facility_state = IOSTATE_READY;
1169 { /* operation still pending/queued or failed? */
1170 int err = GetLastError ();
1171 if ((ERROR_IO_INCOMPLETE != err) && (ERROR_IO_PENDING != err))
1172 { /* error occurred, let the rest of the elements finish */
1173 input_facility->path_open = FALSE;
1174 input_facility->facility_state = IOSTATE_FAILED;
1175 if (IOSTATE_WAITING == output_facility->facility_state)
1176 output_facility->path_open = FALSE;
1177 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish\n");
1182 case IOSTATE_RESUME: /* Our buffer was filled already but our write facility was busy. */
1183 memcpy (output_facility->buffer,
1184 input_facility->buffer + sizeof (struct GNUNET_MessageHeader),
1185 input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader));
1186 output_facility->buffer_size = input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader);
1187 output_facility->facility_state = IOSTATE_READY;
1188 input_facility->facility_state = IOSTATE_READY;
1197 * Attempts to write to an output facility (tap or named pipe) in overlapped mode.
1199 * TODO: high level description
1201 * @param output_facility output pipe or file to hand over data to.
1202 * @param input_facility input named pipe or file to work with.
1203 * @return false if an event reset was impossible (OS error), else true
1206 attempt_write (struct io_facility * output_facility,
1207 struct io_facility * input_facility)
1209 switch (output_facility->facility_state)
1212 output_facility->buffer_size_written = 0;
1214 continue_partial_write:
1215 if (! ResetEvent (output_facility->overlapped.hEvent))
1218 /* Check how the task was handled */
1219 if (WriteFile (output_facility->handle,
1220 output_facility->buffer + output_facility->buffer_size_written,
1221 output_facility->buffer_size - output_facility->buffer_size_written,
1222 &output_facility->buffer_size_processed,
1223 &output_facility->overlapped))
1224 {/* async event processed immediately*/
1226 fprintf (stderr, "DEBUG: write succeeded immediately\n");
1227 output_facility->buffer_size_written += output_facility->buffer_size_processed;
1229 /* reset event manually*/
1230 if (! SetEvent (output_facility->overlapped.hEvent))
1234 if (output_facility->buffer_size_written < output_facility->buffer_size)
1235 goto continue_partial_write;
1237 /* we are now waiting for our buffer to be filled*/
1238 output_facility->facility_state = IOSTATE_WAITING;
1240 /* we successfully wrote something and now need to reset our reader */
1241 if (IOSTATE_WAITING == input_facility->facility_state)
1242 input_facility->facility_state = IOSTATE_RESUME;
1243 else if (IOSTATE_FAILED == input_facility->facility_state)
1244 output_facility->path_open = FALSE;
1246 else /* operation was either queued or failed*/
1248 int err = GetLastError ();
1249 if (ERROR_IO_PENDING == err)
1250 { /* operation queued */
1251 output_facility->facility_state = IOSTATE_QUEUED;
1254 { /* error occurred, close this path */
1255 output_facility->path_open = FALSE;
1256 output_facility->facility_state = IOSTATE_FAILED;
1257 fprintf (stderr, "FATAL: Write to handle failed, exiting\n");
1261 case IOSTATE_QUEUED:
1262 // there was an operation going on already, check if that has completed now.
1264 if (GetOverlappedResult (output_facility->handle,
1265 &output_facility->overlapped,
1266 &output_facility->buffer_size_processed,
1268 {/* successful return for a queued operation */
1269 if (! ResetEvent (output_facility->overlapped.hEvent))
1272 fprintf (stderr, "DEBUG: write succeeded delayed\n");
1273 output_facility->buffer_size_written += output_facility->buffer_size_processed;
1276 if (output_facility->buffer_size_written < output_facility->buffer_size)
1277 goto continue_partial_write;
1279 /* we are now waiting for our buffer to be filled*/
1280 output_facility->facility_state = IOSTATE_WAITING;
1282 /* we successfully wrote something and now need to reset our reader */
1283 if (IOSTATE_WAITING == input_facility->facility_state)
1284 input_facility->facility_state = IOSTATE_RESUME;
1285 else if (IOSTATE_FAILED == input_facility->facility_state)
1286 output_facility->path_open = FALSE;
1289 { /* operation still pending/queued or failed? */
1290 int err = GetLastError ();
1291 if ((ERROR_IO_INCOMPLETE != err) && (ERROR_IO_PENDING != err))
1292 { /* error occurred, close this path */
1293 output_facility->path_open = FALSE;
1294 output_facility->facility_state = IOSTATE_FAILED;
1295 fprintf (stderr, "FATAL: Write to handle failed, exiting\n");
1305 * Initialize a overlapped structure
1307 * @param elem the element to initilize
1308 * @param initial_state the initial state for this instance
1309 * @param signaled if the hEvent created should default to signaled or not
1310 * @return true on success, else false
1313 initialize_io_facility (struct io_facility * elem,
1317 elem->path_open = TRUE;
1318 elem->handle = INVALID_HANDLE_VALUE;
1319 elem->facility_state = initial_state;
1320 elem->buffer_size = 0;
1321 elem->overlapped.hEvent = CreateEvent (NULL, TRUE, signaled, NULL);
1322 if (NULL == elem->overlapped.hEvent)
1330 * Start forwarding to and from the tunnel.
1332 * @param tap_handle device handle for interacting with the Virtual interface
1335 run (HANDLE tap_handle)
1337 /* IO-Facility for reading from our virtual interface */
1338 struct io_facility tap_read;
1339 /* IO-Facility for writing to our virtual interface */
1340 struct io_facility tap_write;
1341 /* IO-Facility for reading from stdin */
1342 struct io_facility std_in;
1343 /* IO-Facility for writing to stdout */
1344 struct io_facility std_out;
1346 HANDLE parent_std_in_handle = GetStdHandle (STD_INPUT_HANDLE);
1347 HANDLE parent_std_out_handle = GetStdHandle (STD_OUTPUT_HANDLE);
1350 /* we do this HERE and not beforehand (in init_tun()), in contrast to openvpn
1351 * to remove the need to flush the arp cache, handle DHCP and wrong IPs.
1353 * DHCP and such are all features we will never use in gnunet afaik.
1354 * But for openvpn those are essential.
1356 if (! tun_up (tap_handle))
1359 /* Initialize our overlapped IO structures*/
1360 if (! (initialize_io_facility (&tap_read, IOSTATE_READY, FALSE)
1361 && initialize_io_facility (&tap_write, IOSTATE_WAITING, TRUE)
1362 && initialize_io_facility (&std_in, IOSTATE_READY, FALSE)
1363 && initialize_io_facility (&std_out, IOSTATE_WAITING, TRUE)))
1364 goto teardown_final;
1366 /* Handles for STDIN and STDOUT */
1367 tap_read.handle = tap_handle;
1368 tap_write.handle = tap_handle;
1370 #ifdef DEBUG_TO_CONSOLE
1371 /* Debug output to console STDIN/STDOUT*/
1372 std_in.handle = parent_std_in_handle;
1373 std_out.handle = parent_std_out_handle;
1376 fprintf (stderr, "DEBUG: reopening stdin/out for overlapped IO\n");
1378 * Find out the types of our handles.
1379 * This part is a problem, because in windows we need to handle files,
1380 * pipes and the console differently.
1382 if ((FILE_TYPE_PIPE != GetFileType (parent_std_in_handle)) ||
1383 (FILE_TYPE_PIPE != GetFileType (parent_std_out_handle)))
1385 fprintf (stderr, "ERROR: stdin/stdout must be named pipes\n");
1389 std_in.handle = ReOpenFile (parent_std_in_handle,
1391 FILE_SHARE_WRITE | FILE_SHARE_READ,
1392 FILE_FLAG_OVERLAPPED);
1394 if (INVALID_HANDLE_VALUE == std_in.handle)
1396 fprintf (stderr, "FATAL: Could not reopen stdin for in overlapped mode, has to be a named pipe\n");
1400 std_out.handle = ReOpenFile (parent_std_out_handle,
1403 FILE_FLAG_OVERLAPPED);
1405 if (INVALID_HANDLE_VALUE == std_out.handle)
1407 fprintf (stderr, "FATAL: Could not reopen stdout for in overlapped mode, has to be a named pipe\n");
1412 fprintf (stderr, "DEBUG: mainloop has begun\n");
1414 while (std_out.path_open || tap_write.path_open)
1416 /* perform READ from stdin if possible */
1417 if (std_in.path_open && (! attempt_read_stdin (&std_in, &tap_write)))
1420 /* perform READ from tap if possible */
1421 if (tap_read.path_open && (! attempt_read_tap (&tap_read, &std_out)))
1424 /* perform WRITE to tap if possible */
1425 if (tap_write.path_open && (! attempt_write (&tap_write, &std_in)))
1428 /* perform WRITE to STDOUT if possible */
1429 if (std_out.path_open && (! attempt_write (&std_out, &tap_read)))
1435 fprintf (stderr, "DEBUG: teardown initiated\n");
1437 CancelIo (tap_handle);
1438 CancelIo (std_in.handle);
1439 CancelIo (std_out.handle);
1443 CloseHandle (tap_handle);
1448 * Open VPN tunnel interface.
1450 * @param argc must be 6
1451 * @param argv 0: binary name (gnunet-helper-vpn)
1452 * 1: tunnel interface prefix (gnunet-vpn)
1453 * 2: IPv6 address (::1), "-" to disable
1454 * 3: IPv6 netmask length in bits (64), ignored if #2 is "-"
1455 * 4: IPv4 address (1.2.3.4), "-" to disable
1456 * 5: IPv4 netmask (255.255.0.0), ignored if #4 is "-"
1459 main (int argc, char **argv)
1461 char hwid[LINE_LEN];
1464 BOOL have_ip4 = FALSE;
1465 BOOL have_ip6 = FALSE;
1469 fprintf (stderr, "FATAL: must supply 5 arguments\nUsage:\ngnunet-helper-vpn <if name prefix> <address6 or \"-\"> <netbits6> <address4 or \"-\"> <netmask4>\n", argv[0]);
1473 strncpy (hwid, argv[1], LINE_LEN);
1474 hwid[LINE_LEN - 1] = '\0';
1477 * We use our PID for finding/resolving the control-panel name of our virtual
1478 * device. PIDs are (of course) unique at runtime, thus we can safely use it
1479 * as additional hardware-id for our device.
1481 snprintf (secondary_hwid, LINE_LEN / 2, "%s-%d",
1485 if (INVALID_HANDLE_VALUE == (handle = init_tun ()))
1487 fprintf (stderr, "FATAL: could not initialize virtual-interface %s with IPv6 %s/%s and IPv4 %s/%s\n",
1497 fprintf (stderr, "DEBUG: Setting IPs, if needed\n");
1498 if (0 != strcmp (argv[2], "-"))
1500 const char *address = argv[2];
1501 long prefix_len = atol (argv[3]);
1503 if ((prefix_len < 1) || (prefix_len > 127))
1505 fprintf (stderr, "FATAL: ipv6 prefix_len out of range\n");
1510 fprintf (stderr, "DEBUG: Setting IP6 address: %s/%d\n",address,prefix_len);
1511 if (0 != (global_ret = set_address6 (address, prefix_len)))
1517 if (0 != strcmp (argv[4], "-"))
1519 const char *address = argv[4];
1520 const char *mask = argv[5];
1522 fprintf (stderr, "DEBUG: Setting IP4 address: %s/%s\n",address,mask);
1523 if (0 != (global_ret = set_address4 (address, mask)))
1535 const char *address = argv[4];
1536 fprintf (stderr, "DEBUG: Removing IP4 address\n");
1537 remove_address4 (address);
1541 const char *address = argv[2];
1542 fprintf (stderr, "DEBUG: Removing IP6 address\n");
1543 remove_address6 (address);
1546 fprintf (stderr, "DEBUG: removing interface\n");
1547 remove_interface ();
1548 fprintf (stderr, "DEBUG: graceful exit completed\n");