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;
227 * ReOpenFile is only available as of XP SP2 and 2003 SP1
229 WINBASEAPI HANDLE WINAPI ReOpenFile (HANDLE, DWORD, DWORD, DWORD);
232 * IsWow64Process definition for our is_win64, as this is a kernel function
234 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
237 * Determines if the host OS is win32 or win64
245 //this is a win64 binary,
247 #elif defined(_WIN32)
248 //this is a 32bit binary, and we need to check if we are running in WOW64
249 BOOL success = FALSE;
250 BOOL on_wow64 = FALSE;
251 LPFN_ISWOW64PROCESS IsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandle ("kernel32"), "IsWow64Process");
253 if (NULL != IsWow64Process)
254 success = IsWow64Process (GetCurrentProcess (), &on_wow64);
256 return success && on_wow64;
260 * Wrapper for executing a shellcommand in windows.
262 * @param command - the command + parameters to execute
263 * @return * exitcode of the program executed,
264 * * EINVAL (cmd/file not found)
265 * * EPIPE (could not read STDOUT)
268 execute_shellcommand (const char *command)
272 if ( (NULL == command) ||
273 (NULL == (pipe = _popen (command, "rt"))) )
277 fprintf (stderr, "DEBUG: Command output: \n");
278 char output[LINE_LEN];
279 while (NULL != fgets (output, sizeof (output), pipe))
280 fprintf (stderr, "%s", output);
283 return _pclose (pipe);
288 * @brief Sets the IPv6-Address given in address on the interface dev
290 * @param address the IPv6-Address
291 * @param prefix_len the length of the network-prefix
294 set_address6 (const char *address, unsigned long prefix_len)
297 char command[LINE_LEN];
298 struct sockaddr_in6 sa6;
301 * parse the new address
303 memset (&sa6, 0, sizeof (struct sockaddr_in6));
304 sa6.sin6_family = AF_INET6;
305 if (1 != inet_pton (AF_INET6, address, &sa6.sin6_addr.s6_addr))
307 fprintf (stderr, "ERROR: Failed to parse address `%s': %s\n", address,
313 * prepare the command
315 snprintf (command, LINE_LEN,
316 "netsh interface ipv6 add address \"%s\" %s/%d store=active",
317 device_visible_name, address, prefix_len);
321 ret = execute_shellcommand (command);
325 fprintf (stderr, "FATAL: Setting IPv6 address failed: %s\n", strerror (ret));
331 * @brief Removes the IPv6-Address given in address from the interface dev
333 * @param dev the interface to remove
334 * @param address the IPv4-Address
335 * @param mask the netmask
338 remove_address6 (const char *address)
340 char command[LINE_LEN];
343 // sanity checking was already done in set_address6
345 * prepare the command
347 snprintf (command, LINE_LEN,
348 "netsh interface ipv6 delete address \"%s\" store=persistent",
349 device_visible_name, address);
353 ret = execute_shellcommand (command);
357 fprintf (stderr, "FATAL: removing IPv6 address failed: %s\n", strerror (ret));
362 * @brief Sets the IPv4-Address given in address on the interface dev
364 * @param dev the interface to configure
365 * @param address the IPv4-Address
366 * @param mask the netmask
369 set_address4 (const char *address, const char *mask)
372 char command[LINE_LEN];
374 struct sockaddr_in addr;
375 addr.sin_family = AF_INET;
380 if (1 != inet_pton (AF_INET, address, &addr.sin_addr.s_addr))
382 fprintf (stderr, "ERROR: Failed to parse address `%s': %s\n", address,
386 // Set Device to Subnet-Mode?
387 // do we really need tun.c:2925 ?
390 * prepare the command
392 snprintf (command, LINE_LEN,
393 "netsh interface ipv4 add address \"%s\" %s %s store=active",
394 device_visible_name, address, mask);
398 ret = execute_shellcommand (command);
402 fprintf (stderr, "FATAL: Setting IPv4 address failed: %s\n", strerror (ret));
408 * @brief Removes the IPv4-Address given in address from the interface dev
410 * @param dev the interface to remove
411 * @param address the IPv4-Address
412 * @param mask the netmask
415 remove_address4 (const char *address)
417 char command[LINE_LEN];
420 // sanity checking was already done in set_address4
423 * prepare the command
425 snprintf (command, LINE_LEN,
426 "netsh interface ipv4 delete address \"%s\" gateway=all store=persistent",
427 device_visible_name, address);
431 ret = execute_shellcommand (command);
435 fprintf (stderr, "FATAL: removing IPv4 address failed: %s\n", strerror (ret));
440 * Setup a new virtual interface to use for tunneling.
442 * @return: TRUE if setup was successful, else FALSE
448 * where to find our inf-file. (+ the "full" path, after windows found")
450 * We do not directly input all the props here, because openvpn will update
451 * these details over time.
453 char inf_file_path[MAX_PATH];
454 char * temp_inf_filename;
455 char hwidlist[LINE_LEN + 4];
456 char class_name[128];
461 * Set the device's hardware ID and add it to a list.
462 * This information will later on identify this device in registry.
464 strncpy (hwidlist, HARDWARE_ID, LINE_LEN);
466 * this is kind of over-complicated, but allows keeps things independent of
467 * how the openvpn-hwid is actually stored.
469 * A HWID list is double-\0 terminated and \0 separated
471 str_length = strlen (hwidlist) + 1;
472 strncpy (&hwidlist[str_length], secondary_hwid, LINE_LEN);
473 str_length += strlen (&hwidlist[str_length]) + 1;
476 * Locate the inf-file, we need to store it somewhere where the system can
477 * find it. We need to pick the correct driver for win32/win64.
480 GetFullPathNameA (INF_FILE64, MAX_PATH, inf_file_path, &temp_inf_filename);
482 GetFullPathNameA (INF_FILE, MAX_PATH, inf_file_path, &temp_inf_filename);
484 fprintf (stderr, "INFO: Located our driver's .inf file at %s\n", inf_file_path);
486 * Bootstrap our device info using the drivers inf-file
488 if ( ! SetupDiGetINFClassA (inf_file_path,
490 class_name, sizeof (class_name) / sizeof (char),
495 * Collect all the other needed information...
496 * let the system fill our this form
498 DeviceInfo = SetupDiCreateDeviceInfoList (&class_guid, NULL);
499 if (DeviceInfo == INVALID_HANDLE_VALUE)
502 DeviceNode.cbSize = sizeof (SP_DEVINFO_DATA);
503 if ( ! SetupDiCreateDeviceInfoA (DeviceInfo,
512 /* Deploy all the information collected into the registry */
513 if ( ! SetupDiSetDeviceRegistryPropertyA (DeviceInfo,
517 str_length * sizeof (char)))
520 /* Install our new class(=device) into the system */
521 if ( ! SetupDiCallClassInstaller (DIF_REGISTERDEVICE,
526 /* This system call tends to take a while (several seconds!) on
527 "modern" Windoze systems */
528 if ( ! UpdateDriverForPlugAndPlayDevicesA (NULL,
531 INSTALLFLAG_FORCE | INSTALLFLAG_NONINTERACTIVE,
532 NULL)) //reboot required? NEVER!
535 fprintf (stderr, "DEBUG: successfully created a network device\n");
541 * Remove our new virtual interface to use for tunneling.
542 * This function must be called AFTER setup_interface!
544 * @return: TRUE if destruction was successful, else FALSE
549 SP_REMOVEDEVICE_PARAMS remove;
551 if (INVALID_HANDLE_VALUE == DeviceInfo)
554 remove.ClassInstallHeader.cbSize = sizeof (SP_CLASSINSTALL_HEADER);
555 remove.HwProfile = 0;
556 remove.Scope = DI_REMOVEDEVICE_GLOBAL;
557 remove.ClassInstallHeader.InstallFunction = DIF_REMOVE;
559 * 1. Prepare our existing device information set, and place the
560 * uninstall related information into the structure
562 if ( ! SetupDiSetClassInstallParamsA (DeviceInfo,
563 (PSP_DEVINFO_DATA) & DeviceNode,
564 &remove.ClassInstallHeader,
568 * 2. Uninstall the virtual interface using the class installer
570 if ( ! SetupDiCallClassInstaller (DIF_REMOVE,
572 (PSP_DEVINFO_DATA) & DeviceNode))
575 SetupDiDestroyDeviceInfoList (DeviceInfo);
577 fprintf (stderr, "DEBUG: removed interface successfully\n");
584 * Do all the lookup necessary to retrieve the inteface's actual name
587 * @return: TRUE if we were able to lookup the interface's name, else FALSE
590 resolve_interface_name ()
592 SP_DEVINFO_LIST_DETAIL_DATA device_details;
593 char pnp_instance_id [MAX_DEVICE_ID_LEN];
594 HKEY adapter_key_handle;
600 char adapter[] = INTERFACE_REGISTRY_LOCATION;
602 /* We can obtain the PNP instance ID from our setupapi handle */
603 device_details.cbSize = sizeof (device_details);
604 if (CR_SUCCESS != CM_Get_Device_ID_ExA (DeviceNode.DevInst,
605 (PCHAR) pnp_instance_id,
608 NULL)) //hMachine, we are local
611 fprintf (stderr, "DEBUG: Resolving interface name for network device %s\n",pnp_instance_id);
613 /* Registry is incredibly slow, retry for up to 30 seconds to allow registry to refresh */
614 for (retrys = 0; retrys < 120 && !retval; retrys++)
619 /* Now we can use this ID to locate the correct networks interface in registry */
620 if (ERROR_SUCCESS != RegOpenKeyExA (
625 &adapter_key_handle))
628 /* Of course there is a multitude of entries here, with arbitrary names,
629 * thus we need to iterate through there.
633 char instance_key[256];
634 char query_key [256];
635 HKEY instance_key_handle;
636 char pnpinstanceid_name[] = "PnpInstanceID";
637 char pnpinstanceid_value[256];
638 char adaptername_name[] = "Name";
641 len = 256 * sizeof (char);
642 /* optain a subkey of {4D36E972-E325-11CE-BFC1-08002BE10318} */
643 status = RegEnumKeyExA (
653 /* this may fail due to one of two reasons:
654 * we are at the end of the list*/
655 if (ERROR_NO_MORE_ITEMS == status)
657 // * we found a broken registry key, continue with the next key.
658 if (ERROR_SUCCESS != status)
661 /* prepare our new query string: */
662 snprintf (query_key, 256, "%s\\%s\\Connection",
666 /* look inside instance_key\\Connection */
667 if (ERROR_SUCCESS != RegOpenKeyExA (
672 &instance_key_handle))
675 /* now, read our PnpInstanceID */
676 len = sizeof (pnpinstanceid_value);
677 status = RegQueryValueExA (instance_key_handle,
679 NULL, //reserved, always NULL according to MSDN
681 (LPBYTE) pnpinstanceid_value,
684 if (status != ERROR_SUCCESS || data_type != REG_SZ)
687 /* compare the value we got to our devices PNPInstanceID*/
688 if (0 != strncmp (pnpinstanceid_value, pnp_instance_id,
689 sizeof (pnpinstanceid_value) / sizeof (char)))
692 len = sizeof (device_visible_name);
693 status = RegQueryValueExA (
696 NULL, //reserved, always NULL according to MSDN
698 (LPBYTE) device_visible_name,
701 if (status != ERROR_SUCCESS || data_type != REG_SZ)
705 * we have successfully found OUR instance,
706 * save the device GUID before exiting
709 strncpy (device_guid, instance_key, 256);
711 fprintf (stderr, "DEBUG: Interface Name lookup succeeded on retry %d, got \"%s\" %s\n", retrys, device_visible_name, device_guid);
714 RegCloseKey (instance_key_handle);
719 RegCloseKey (adapter_key_handle);
726 * Determines the version of the installed TAP32 driver and checks if it's sufficiently new for GNUNET
728 * @param handle the handle to our tap device
729 * @return TRUE if the version is sufficient, else FALSE
732 check_tapw32_version (HANDLE handle)
736 memset (&(version), 0, sizeof (version));
738 if (DeviceIoControl (handle, TAP_WIN_IOCTL_GET_VERSION,
739 &version, sizeof (version),
740 &version, sizeof (version), &len, NULL))
741 fprintf (stderr, "INFO: TAP-Windows Driver Version %d.%d %s\n",
744 (version[2] ? "(DEBUG)" : ""));
746 if ((version[0] != TAP_WIN_MIN_MAJOR) ||
747 (version[1] < TAP_WIN_MIN_MINOR )){
748 fprintf (stderr, "FATAL: This version of gnunet requires a TAP-Windows driver that is at least version %d.%d\n",
759 * Creates a tun-interface called dev;
761 * @return the fd to the tun or -1 on error
766 char device_path[256];
769 if (! setup_interface ())
772 return INVALID_HANDLE_VALUE;
775 if (! resolve_interface_name ())
778 return INVALID_HANDLE_VALUE;
781 /* Open Windows TAP-Windows adapter */
782 snprintf (device_path, sizeof (device_path), "%s%s%s",
787 handle = CreateFile (
789 GENERIC_READ | GENERIC_WRITE,
790 0, /* was: FILE_SHARE_READ */
793 FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
797 if (INVALID_HANDLE_VALUE == handle)
799 fprintf (stderr, "FATAL: CreateFile failed on TAP device: %s\n", device_path);
803 /* get driver version info */
804 if (! check_tapw32_version (handle))
806 CloseHandle (handle);
807 return INVALID_HANDLE_VALUE;
810 /* TODO (opt?): get MTU-Size */
812 fprintf (stderr, "DEBUG: successfully opened TAP device\n");
818 * Brings a TAP device up and sets it to connected state.
820 * @param handle the handle to our TAP device
821 * @return True if the operation succeeded, else false
824 tun_up (HANDLE handle)
828 if (! DeviceIoControl (handle, TAP_WIN_IOCTL_SET_MEDIA_STATUS,
829 &status, sizeof (status),
830 &status, sizeof (status), &len, NULL))
832 fprintf (stderr, "FATAL: TAP driver ignored request to UP interface (DeviceIoControl call)\n");
836 /* Wait for the device to go UP, might take some time. */
837 Sleep (TAP32_POSTUP_WAITTIME * 1000);
838 fprintf (stderr, "DEBUG: successfully set TAP device to UP\n");
845 * Attempts to read off an input facility (tap or named pipe) in overlapped mode.
848 * If the input facility is in IOSTATE_READY, it will issue a new read operation to the
849 * input handle. Then it goes into IOSTATE_QUEUED state.
850 * In case the read succeeded instantly the input facility enters 3.
853 * If the input facility is in IOSTATE_QUEUED state, it will check if the queued read has finished already.
854 * If it has finished, go to state 3.
855 * If it has failed, set IOSTATE_FAILED
858 * If the output facility is in state IOSTATE_READY, the read-buffer is copied to the output buffer.
859 * The input facility enters state IOSTATE_READY
860 * The output facility enters state IOSTATE_READY
861 * If the output facility is in state IOSTATE_QUEUED, the input facility enters IOSTATE_WAITING
863 * IOSTATE_WAITING is reset by the output facility, once it has completed.
865 * @param input_facility input named pipe or file to work with.
866 * @param output_facility output pipe or file to hand over data to.
867 * @return false if an event reset was impossible (OS error), else true
870 attempt_read_tap (struct io_facility * input_facility,
871 struct io_facility * output_facility)
873 struct GNUNET_MessageHeader * hdr;
876 switch (input_facility->facility_state)
880 if (! ResetEvent (input_facility->overlapped.hEvent))
885 input_facility->buffer_size = 0;
887 /* Check how the task is handled */
888 if (ReadFile (input_facility->handle,
889 input_facility->buffer,
890 sizeof (input_facility->buffer) - sizeof (struct GNUNET_MessageHeader),
891 &input_facility->buffer_size,
892 &input_facility->overlapped))
893 {/* async event processed immediately*/
895 /* reset event manually*/
896 if (! SetEvent (input_facility->overlapped.hEvent))
899 fprintf (stderr, "DEBUG: tap read succeeded immediately\n");
901 /* we successfully read something from the TAP and now need to
902 * send it our via STDOUT. Is that possible at the moment? */
903 if ((IOSTATE_READY == output_facility->facility_state ||
904 IOSTATE_WAITING == output_facility->facility_state)
905 && (0 < input_facility->buffer_size))
906 { /* hand over this buffers content and apply message header for gnunet */
907 hdr = (struct GNUNET_MessageHeader *) output_facility->buffer;
908 size = input_facility->buffer_size + sizeof (struct GNUNET_MessageHeader);
910 memcpy (output_facility->buffer + sizeof (struct GNUNET_MessageHeader),
911 input_facility->buffer,
912 input_facility->buffer_size);
914 output_facility->buffer_size = size;
915 hdr->size = htons (size);
916 hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
917 output_facility->facility_state = IOSTATE_READY;
919 else if (0 < input_facility->buffer_size)
920 /* If we have have read our buffer, wait for our write-partner*/
921 input_facility->facility_state = IOSTATE_WAITING;
923 else /* operation was either queued or failed*/
925 int err = GetLastError ();
926 if (ERROR_IO_PENDING == err)
927 { /* operation queued */
928 input_facility->facility_state = IOSTATE_QUEUED;
931 { /* error occurred, let the rest of the elements finish */
932 input_facility->path_open = FALSE;
933 input_facility->facility_state = IOSTATE_FAILED;
934 if (IOSTATE_WAITING == output_facility->facility_state)
935 output_facility->path_open = FALSE;
937 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish\n");
942 // We are queued and should check if the read has finished
945 // there was an operation going on already, check if that has completed now.
947 if (GetOverlappedResult (input_facility->handle,
948 &input_facility->overlapped,
949 &input_facility->buffer_size,
951 {/* successful return for a queued operation */
952 if (! ResetEvent (input_facility->overlapped.hEvent))
955 fprintf (stderr, "DEBUG: tap read succeeded delayed\n");
957 /* we successfully read something from the TAP and now need to
958 * send it our via STDOUT. Is that possible at the moment? */
959 if ((IOSTATE_READY == output_facility->facility_state ||
960 IOSTATE_WAITING == output_facility->facility_state)
961 && 0 < input_facility->buffer_size)
962 { /* hand over this buffers content and apply message header for gnunet */
963 hdr = (struct GNUNET_MessageHeader *) output_facility->buffer;
964 size = input_facility->buffer_size + sizeof (struct GNUNET_MessageHeader);
966 memcpy (output_facility->buffer + sizeof (struct GNUNET_MessageHeader),
967 input_facility->buffer,
968 input_facility->buffer_size);
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;
976 else if (0 < input_facility->buffer_size)
977 { /* If we have have read our buffer, wait for our write-partner*/
978 input_facility->facility_state = IOSTATE_WAITING;
979 // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish?
983 { /* operation still pending/queued or failed? */
984 int err = GetLastError ();
985 if ((ERROR_IO_INCOMPLETE != err) && (ERROR_IO_PENDING != err))
986 { /* error occurred, let the rest of the elements finish */
987 input_facility->path_open = FALSE;
988 input_facility->facility_state = IOSTATE_FAILED;
989 if (IOSTATE_WAITING == output_facility->facility_state)
990 output_facility->path_open = FALSE;
991 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish\n");
997 hdr = (struct GNUNET_MessageHeader *) output_facility->buffer;
998 size = input_facility->buffer_size + sizeof (struct GNUNET_MessageHeader);
1000 memcpy (output_facility->buffer + sizeof (struct GNUNET_MessageHeader),
1001 input_facility->buffer,
1002 input_facility->buffer_size);
1004 output_facility->buffer_size = size;
1005 hdr->size = htons (size);
1006 hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
1007 output_facility->facility_state = IOSTATE_READY;
1008 input_facility->facility_state = IOSTATE_READY;
1017 * Attempts to read off an input facility (tap or named pipe) in overlapped mode.
1020 * If the input facility is in IOSTATE_READY, it will issue a new read operation to the
1021 * input handle. Then it goes into IOSTATE_QUEUED state.
1022 * In case the read succeeded instantly the input facility enters 3.
1025 * If the input facility is in IOSTATE_QUEUED state, it will check if the queued read has finished already.
1026 * If it has finished, go to state 3.
1027 * If it has failed, set IOSTATE_FAILED
1030 * If the facility is finished with ready
1031 * The read-buffer is copied to the output buffer, except for the GNUNET_MessageHeader.
1032 * The input facility enters state IOSTATE_READY
1033 * The output facility enters state IOSTATE_READY
1034 * If the output facility is in state IOSTATE_QUEUED, the input facility enters IOSTATE_WAITING
1036 * IOSTATE_WAITING is reset by the output facility, once it has completed.
1038 * @param input_facility input named pipe or file to work with.
1039 * @param output_facility output pipe or file to hand over data to.
1040 * @return false if an event reset was impossible (OS error), else true
1043 attempt_read_stdin (struct io_facility * input_facility,
1044 struct io_facility * output_facility)
1046 struct GNUNET_MessageHeader * hdr;
1048 switch (input_facility->facility_state)
1052 input_facility->buffer_size = 0;
1054 partial_read_iostate_ready:
1055 if (! ResetEvent (input_facility->overlapped.hEvent))
1058 /* Check how the task is handled */
1059 if (ReadFile (input_facility->handle,
1060 input_facility->buffer + input_facility->buffer_size,
1061 sizeof (input_facility->buffer) - input_facility->buffer_size,
1062 &input_facility->buffer_size_processed,
1063 &input_facility->overlapped))
1064 {/* async event processed immediately*/
1065 hdr = (struct GNUNET_MessageHeader *) input_facility->buffer;
1067 /* reset event manually*/
1068 if (!SetEvent (input_facility->overlapped.hEvent))
1071 fprintf (stderr, "DEBUG: stdin read succeeded immediately\n");
1072 input_facility->buffer_size += input_facility->buffer_size_processed;
1074 if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER ||
1075 ntohs (hdr->size) > sizeof (input_facility->buffer))
1077 fprintf (stderr, "WARNING: Protocol violation, got GNUnet Message type %h, size %h\n", ntohs (hdr->type), ntohs (hdr->size));
1078 input_facility->facility_state = IOSTATE_READY;
1081 /* we got the a part of a packet */
1082 if (ntohs (hdr->size) > input_facility->buffer_size)
1083 goto partial_read_iostate_ready;
1085 /* have we read more than 0 bytes of payload? (sizeread > header)*/
1086 if (input_facility->buffer_size > sizeof (struct GNUNET_MessageHeader) &&
1087 ((IOSTATE_READY == output_facility->facility_state) ||
1088 (IOSTATE_WAITING == output_facility->facility_state)))
1089 {/* we successfully read something from the TAP and now need to
1090 * send it our via STDOUT. Is that possible at the moment? */
1092 /* hand over this buffers content and strip gnunet message header */
1093 memcpy (output_facility->buffer,
1094 input_facility->buffer + sizeof (struct GNUNET_MessageHeader),
1095 input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader));
1096 output_facility->buffer_size = input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader);
1097 output_facility->facility_state = IOSTATE_READY;
1098 input_facility->facility_state = IOSTATE_READY;
1100 else if (input_facility->buffer_size > sizeof (struct GNUNET_MessageHeader))
1101 /* If we have have read our buffer, wait for our write-partner*/
1102 input_facility->facility_state = IOSTATE_WAITING;
1103 else /* we read nothing */
1104 input_facility->facility_state = IOSTATE_READY;
1106 else /* operation was either queued or failed*/
1108 int err = GetLastError ();
1109 if (ERROR_IO_PENDING == err) /* operation queued */
1110 input_facility->facility_state = IOSTATE_QUEUED;
1112 { /* error occurred, let the rest of the elements finish */
1113 input_facility->path_open = FALSE;
1114 input_facility->facility_state = IOSTATE_FAILED;
1115 if (IOSTATE_WAITING == output_facility->facility_state)
1116 output_facility->path_open = FALSE;
1118 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish\n");
1123 // We are queued and should check if the read has finished
1124 case IOSTATE_QUEUED:
1126 // there was an operation going on already, check if that has completed now.
1127 if (GetOverlappedResult (input_facility->handle,
1128 &input_facility->overlapped,
1129 &input_facility->buffer_size_processed,
1131 {/* successful return for a queued operation */
1132 hdr = (struct GNUNET_MessageHeader *) input_facility->buffer;
1134 if (! ResetEvent (input_facility->overlapped.hEvent))
1137 fprintf (stderr, "DEBUG: stdin read succeeded delayed\n");
1138 input_facility->buffer_size += input_facility->buffer_size_processed;
1140 if ((ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER) ||
1141 (ntohs (hdr->size) > sizeof (input_facility->buffer)))
1143 fprintf (stderr, "WARNING: Protocol violation, got GNUnet Message type %h, size %h\n", ntohs (hdr->type), ntohs (hdr->size));
1144 input_facility->facility_state = IOSTATE_READY;
1147 /* we got the a part of a packet */
1148 if (ntohs (hdr->size) > input_facility->buffer_size );
1149 goto partial_read_iostate_ready;
1151 /* we successfully read something from the TAP and now need to
1152 * send it our via STDOUT. Is that possible at the moment? */
1153 if ((IOSTATE_READY == output_facility->facility_state ||
1154 IOSTATE_WAITING == output_facility->facility_state)
1155 && input_facility->buffer_size > sizeof(struct GNUNET_MessageHeader))
1156 { /* hand over this buffers content and strip gnunet message header */
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;
1164 else if (input_facility->buffer_size > sizeof(struct GNUNET_MessageHeader))
1165 input_facility->facility_state = IOSTATE_WAITING;
1167 input_facility->facility_state = IOSTATE_READY;
1170 { /* operation still pending/queued or failed? */
1171 int err = GetLastError ();
1172 if ((ERROR_IO_INCOMPLETE != err) && (ERROR_IO_PENDING != err))
1173 { /* error occurred, let the rest of the elements finish */
1174 input_facility->path_open = FALSE;
1175 input_facility->facility_state = IOSTATE_FAILED;
1176 if (IOSTATE_WAITING == output_facility->facility_state)
1177 output_facility->path_open = FALSE;
1178 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish\n");
1183 case IOSTATE_RESUME: /* Our buffer was filled already but our write facility was busy. */
1184 memcpy (output_facility->buffer,
1185 input_facility->buffer + sizeof (struct GNUNET_MessageHeader),
1186 input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader));
1187 output_facility->buffer_size = input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader);
1188 output_facility->facility_state = IOSTATE_READY;
1189 input_facility->facility_state = IOSTATE_READY;
1198 * Attempts to write to an output facility (tap or named pipe) in overlapped mode.
1200 * TODO: high level description
1202 * @param output_facility output pipe or file to hand over data to.
1203 * @param input_facility input named pipe or file to work with.
1204 * @return false if an event reset was impossible (OS error), else true
1207 attempt_write (struct io_facility * output_facility,
1208 struct io_facility * input_facility)
1212 switch (output_facility->facility_state)
1216 if (! ResetEvent (output_facility->overlapped.hEvent))
1219 output_facility->buffer_size_processed = 0;
1220 status = WriteFile (output_facility->handle,
1221 output_facility->buffer,
1222 output_facility->buffer_size,
1223 &output_facility->buffer_size_processed,
1224 &output_facility->overlapped);
1226 /* Check how the task was handled */
1228 output_facility->buffer_size_processed == output_facility->buffer_size)
1229 {/* async event processed immediately*/
1231 fprintf (stderr, "DEBUG: write succeeded immediately\n");
1233 /* reset event manually*/
1234 if (! SetEvent (output_facility->overlapped.hEvent))
1237 /* we are now waiting for our buffer to be filled*/
1238 output_facility->facility_state = IOSTATE_WAITING;
1239 output_facility->buffer_size = 0;
1240 output_facility->buffer_size_processed = 0;
1242 /* we successfully wrote something and now need to reset our reader */
1243 if (IOSTATE_WAITING == input_facility->facility_state)
1244 input_facility->facility_state = IOSTATE_RESUME;
1245 else if (IOSTATE_FAILED == input_facility->facility_state)
1246 output_facility->path_open = FALSE;
1248 else /* operation was either queued or failed*/
1250 int err = GetLastError ();
1251 if (ERROR_IO_PENDING == err)
1252 { /* operation queued */
1253 output_facility->facility_state = IOSTATE_QUEUED;
1256 { /* error occurred, close this path */
1257 output_facility->path_open = FALSE;
1258 output_facility->facility_state = IOSTATE_FAILED;
1259 fprintf (stderr, "FATAL: Write to handle failed, exiting\n");
1263 case IOSTATE_QUEUED:
1264 // there was an operation going on already, check if that has completed now.
1265 status = GetOverlappedResult (output_facility->handle,
1266 &output_facility->overlapped,
1267 &output_facility->buffer_size_processed,
1270 output_facility->buffer_size_processed == output_facility->buffer_size)
1271 {/* successful return for a queued operation */
1272 if (! ResetEvent (output_facility->overlapped.hEvent))
1275 fprintf (stderr, "DEBUG: write succeeded delayed\n");
1277 /* we are now waiting for our buffer to be filled*/
1278 output_facility->facility_state = IOSTATE_WAITING;
1279 output_facility->buffer_size = 0;
1280 output_facility->buffer_size_processed = 0;
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 fd_tun tunnel FD
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");