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 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
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 #deifne 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 driver in Unicode.
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 * Hardware ID used in the inf-file.
80 * This might change over time, as openvpn advances their driver
82 #define HARDWARE_ID "tap0901"
85 * Minimum major-id of the driver version we can work with
87 #define TAP_WIN_MIN_MAJOR 9
90 * Minimum minor-id of the driver version we can work with.
91 * v <= 7 has buggy IPv6.
92 * v == 8 is broken for small IPv4 Packets
94 #define TAP_WIN_MIN_MINOR 9
97 * Time in seconds to wait for our virtual device to go up after telling it to do so.
99 * openvpn doesn't specify a value, 4 seems sane for testing, even for openwrt
100 * (in fact, 4 was chosen by a fair dice roll...)
102 #define TAP32_POSTUP_WAITTIME 4
105 * Location of the network interface list resides in registry.
107 #define INTERFACE_REGISTRY_LOCATION "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
110 * Our local process' PID. Used for creating a sufficiently unique additional
111 * hardware ID for our device.
113 static char secondary_hwid[LINE_LEN / 2];
116 * Device's visible Name, used to identify a network device in netsh.
117 * eg: "Local Area Connection 9"
119 static char device_visible_name[256];
122 * This is our own local instance of a virtual network interface
123 * It is (somewhat) equivalent to using tun/tap in unixoid systems
125 * Upon initialization, we create such an device node.
126 * Upon termination, we remove it again.
128 * If we crash this device might stay around.
130 static HDEVINFO DeviceInfo = INVALID_HANDLE_VALUE;
133 * Registry Key we hand over to windows to spawn a new virtual interface
135 static SP_DEVINFO_DATA DeviceNode;
138 * GUID of our virtual device in the form of
139 * {12345678-1234-1234-1234-123456789abc} - in hex
141 static char device_guid[256];
145 * Possible states of an IO facility.
151 * overlapped I/O is ready for work
156 * overlapped I/O has been queued
161 * overlapped I/O has finished, but is waiting for it's write-partner
166 * there is a full buffer waiting
171 * Operlapped IO states for facility objects
172 * overlapped I/O has failed, stop processing
180 * A IO Object + read/writebuffer + buffer-size for windows asynchronous IO handling
185 * The mode the state machine associated with this object is in.
187 enum IO_State facility_state;
190 * If the path is open or blocked in general (used for quickly checking)
192 BOOL path_open; // BOOL is winbool (int), NOT boolean (unsigned char)!
195 * Windows Object-Handle (used for accessing TAP and STDIN/STDOUT)
200 * Overlaped IO structure used for asynchronous IO in windows.
202 OVERLAPPED overlapped;
205 * Buffer for reading things to and writing from...
207 unsigned char buffer[MAX_SIZE];
210 * How much of this buffer was used when reading or how much data can be written
215 * Amount of data written, is compared to buffer_size.
217 DWORD buffer_size_written;
221 * ReOpenFile is only available as of XP SP2 and 2003 SP1
223 WINBASEAPI HANDLE WINAPI ReOpenFile (HANDLE, DWORD, DWORD, DWORD);
227 * Wrapper for executing a shellcommand in windows.
229 * @param command - the command + parameters to execute
230 * @return * exitcode of the program executed,
231 * * EINVAL (cmd/file not found)
232 * * EPIPE (could not read STDOUT)
235 execute_shellcommand (const char *command)
239 if ( (NULL == command) ||
240 (NULL == (pipe = _popen (command, "rt"))) )
244 fprintf (stderr, "DEBUG: Command output: \n");
245 char output[LINE_LEN];
246 while (NULL != fgets (output, sizeof (output), pipe))
247 fprintf (stderr, "%s", output);
250 return _pclose (pipe);
255 * @brief Sets the IPv6-Address given in address on the interface dev
257 * @param address the IPv6-Address
258 * @param prefix_len the length of the network-prefix
261 set_address6 (const char *address, unsigned long prefix_len)
264 char command[LINE_LEN];
265 struct sockaddr_in6 sa6;
268 * parse the new address
270 memset (&sa6, 0, sizeof (struct sockaddr_in6));
271 sa6.sin6_family = AF_INET6;
272 if (1 != inet_pton (AF_INET6, address, &sa6.sin6_addr.s6_addr))
274 fprintf (stderr, "ERROR: Failed to parse address `%s': %s\n", address,
280 * prepare the command
282 snprintf (command, LINE_LEN,
283 "netsh interface ipv6 add address \"%s\" %s/%d store=active",
284 device_visible_name, address, prefix_len);
288 ret = execute_shellcommand (command);
292 fprintf (stderr, "FATAL: Setting IPv6 address failed: %s\n", strerror (ret));
298 * @brief Removes the IPv6-Address given in address from the interface dev
300 * @param dev the interface to remove
301 * @param address the IPv4-Address
302 * @param mask the netmask
305 remove_address6 (const char *address)
307 char command[LINE_LEN];
310 // sanity checking was already done in set_address6
312 * prepare the command
314 snprintf (command, LINE_LEN,
315 "netsh interface ipv6 delete address \"%s\" store=persistent",
316 device_visible_name, address);
320 ret = execute_shellcommand (command);
324 fprintf (stderr, "FATAL: removing IPv6 address failed: %s\n", strerror (ret));
329 * @brief Sets the IPv4-Address given in address on the interface dev
331 * @param dev the interface to configure
332 * @param address the IPv4-Address
333 * @param mask the netmask
336 set_address4 (const char *address, const char *mask)
339 char command[LINE_LEN];
341 struct sockaddr_in addr;
342 addr.sin_family = AF_INET;
347 if (1 != inet_pton (AF_INET, address, &addr.sin_addr.s_addr))
349 fprintf (stderr, "ERROR: Failed to parse address `%s': %s\n", address,
353 // Set Device to Subnet-Mode?
354 // do we really need tun.c:2925 ?
357 * prepare the command
359 snprintf (command, LINE_LEN,
360 "netsh interface ipv4 add address \"%s\" %s %s store=active",
361 device_visible_name, address, mask);
365 ret = execute_shellcommand (command);
369 fprintf (stderr, "FATAL: Setting IPv4 address failed: %s\n", strerror (ret));
375 * @brief Removes the IPv4-Address given in address from the interface dev
377 * @param dev the interface to remove
378 * @param address the IPv4-Address
379 * @param mask the netmask
382 remove_address4 (const char *address)
384 char command[LINE_LEN];
387 // sanity checking was already done in set_address4
390 * prepare the command
392 snprintf (command, LINE_LEN,
393 "netsh interface ipv4 delete address \"%s\" gateway=all store=persistent",
394 device_visible_name, address);
398 ret = execute_shellcommand (command);
402 fprintf (stderr, "FATAL: removing IPv4 address failed: %s\n", strerror (ret));
407 * Setup a new virtual interface to use for tunneling.
409 * @return: TRUE if setup was successful, else FALSE
415 * where to find our inf-file. (+ the "full" path, after windows found")
417 * We do not directly input all the props here, because openvpn will update
418 * these details over time.
420 char inf_file_path[MAX_PATH];
421 char * temp_inf_filename;
422 char hwidlist[LINE_LEN + 4];
423 char class_name[128];
428 * Set the device's hardware ID and add it to a list.
429 * This information will later on identify this device in registry.
431 strncpy (hwidlist, HARDWARE_ID, LINE_LEN);
433 * this is kind of over-complicated, but allows keeps things independent of
434 * how the openvpn-hwid is actually stored.
436 * A HWID list is double-\0 terminated and \0 separated
438 str_length = strlen (hwidlist) + 1;
439 strncpy (&hwidlist[str_length], secondary_hwid, LINE_LEN);
440 str_length += strlen (&hwidlist[str_length]) + 1;
443 * Locate the inf-file, we need to store it somewhere where the system can
444 * find it. A good choice would be CWD/PDW or %WINDIR$\system32\
446 * TODO: How about win64 in the future?
447 * We need to use a different driver for amd64/i386 !
449 GetFullPathNameA (INF_FILE, MAX_PATH, inf_file_path, &temp_inf_filename);
451 fprintf (stderr, "INFO: Located our driver's .inf file at %s\n", inf_file_path);
453 * Bootstrap our device info using the drivers inf-file
455 if ( ! SetupDiGetINFClassA (inf_file_path,
457 class_name, sizeof (class_name) / sizeof (char),
462 * Collect all the other needed information...
463 * let the system fill our this form
465 DeviceInfo = SetupDiCreateDeviceInfoList (&class_guid, NULL);
466 if (DeviceInfo == INVALID_HANDLE_VALUE)
469 DeviceNode.cbSize = sizeof (SP_DEVINFO_DATA);
470 if ( ! SetupDiCreateDeviceInfoA (DeviceInfo,
479 /* Deploy all the information collected into the registry */
480 if ( ! SetupDiSetDeviceRegistryPropertyA (DeviceInfo,
484 str_length * sizeof (char)))
487 /* Install our new class(=device) into the system */
488 if ( ! SetupDiCallClassInstaller (DIF_REGISTERDEVICE,
493 /* This system call tends to take a while (several seconds!) on
494 "modern" Windoze systems */
495 if ( ! UpdateDriverForPlugAndPlayDevicesA (NULL,
498 INSTALLFLAG_FORCE | INSTALLFLAG_NONINTERACTIVE,
499 NULL)) //reboot required? NEVER!
502 fprintf (stderr, "DEBUG: successfully created a network device\n");
508 * Remove our new virtual interface to use for tunneling.
509 * This function must be called AFTER setup_interface!
511 * @return: TRUE if destruction was successful, else FALSE
516 SP_REMOVEDEVICE_PARAMS remove;
518 if (INVALID_HANDLE_VALUE == DeviceInfo)
521 remove.ClassInstallHeader.cbSize = sizeof (SP_CLASSINSTALL_HEADER);
522 remove.HwProfile = 0;
523 remove.Scope = DI_REMOVEDEVICE_GLOBAL;
524 remove.ClassInstallHeader.InstallFunction = DIF_REMOVE;
526 * 1. Prepare our existing device information set, and place the
527 * uninstall related information into the structure
529 if ( ! SetupDiSetClassInstallParamsA (DeviceInfo,
530 (PSP_DEVINFO_DATA) & DeviceNode,
531 &remove.ClassInstallHeader,
535 * 2. Uninstall the virtual interface using the class installer
537 if ( ! SetupDiCallClassInstaller (DIF_REMOVE,
539 (PSP_DEVINFO_DATA) & DeviceNode))
542 SetupDiDestroyDeviceInfoList (DeviceInfo);
544 fprintf (stderr, "DEBUG: removed interface successfully\n");
551 * Do all the lookup necessary to retrieve the inteface's actual name
554 * @return: TRUE if we were able to lookup the interface's name, else FALSE
557 resolve_interface_name ()
559 SP_DEVINFO_LIST_DETAIL_DATA device_details;
560 char pnp_instance_id [MAX_DEVICE_ID_LEN];
561 HKEY adapter_key_handle;
567 char adapter[] = INTERFACE_REGISTRY_LOCATION;
569 /* We can obtain the PNP instance ID from our setupapi handle */
570 device_details.cbSize = sizeof (device_details);
571 if (CR_SUCCESS != CM_Get_Device_ID_ExA (DeviceNode.DevInst,
572 (PCHAR) pnp_instance_id,
575 NULL)) //hMachine, we are local
578 fprintf (stderr, "DEBUG: Resolving interface name for network device %s\n",pnp_instance_id);
580 /* Registry is incredibly slow, retry for up to 30 seconds to allow registry to refresh */
581 for (retrys = 0; retrys < 120 && !retval; retrys++)
586 /* Now we can use this ID to locate the correct networks interface in registry */
587 if (ERROR_SUCCESS != RegOpenKeyExA (
592 &adapter_key_handle))
595 /* Of course there is a multitude of entries here, with arbitrary names,
596 * thus we need to iterate through there.
600 char instance_key[256];
601 char query_key [256];
602 HKEY instance_key_handle;
603 char pnpinstanceid_name[] = "PnpInstanceID";
604 char pnpinstanceid_value[256];
605 char adaptername_name[] = "Name";
608 len = 256 * sizeof (char);
609 /* optain a subkey of {4D36E972-E325-11CE-BFC1-08002BE10318} */
610 status = RegEnumKeyExA (
620 /* this may fail due to one of two reasons:
621 * we are at the end of the list*/
622 if (ERROR_NO_MORE_ITEMS == status)
624 // * we found a broken registry key, continue with the next key.
625 if (ERROR_SUCCESS != status)
628 /* prepare our new query string: */
629 snprintf (query_key, 256, "%s\\%s\\Connection",
633 /* look inside instance_key\\Connection */
634 if (ERROR_SUCCESS != RegOpenKeyExA (
639 &instance_key_handle))
642 /* now, read our PnpInstanceID */
643 len = sizeof (pnpinstanceid_value);
644 status = RegQueryValueExA (instance_key_handle,
646 NULL, //reserved, always NULL according to MSDN
648 (LPBYTE) pnpinstanceid_value,
651 if (status != ERROR_SUCCESS || data_type != REG_SZ)
654 /* compare the value we got to our devices PNPInstanceID*/
655 if (0 != strncmp (pnpinstanceid_value, pnp_instance_id,
656 sizeof (pnpinstanceid_value) / sizeof (char)))
659 len = sizeof (device_visible_name);
660 status = RegQueryValueExA (
663 NULL, //reserved, always NULL according to MSDN
665 (LPBYTE) device_visible_name,
668 if (status != ERROR_SUCCESS || data_type != REG_SZ)
672 * we have successfully found OUR instance,
673 * save the device GUID before exiting
676 strncpy (device_guid, instance_key, 256);
678 fprintf (stderr, "DEBUG: Interface Name lookup succeeded on retry %d, got \"%s\" %s\n", retrys, device_visible_name, device_guid);
681 RegCloseKey (instance_key_handle);
686 RegCloseKey (adapter_key_handle);
693 * Determines the version of the installed TAP32 driver and checks if it's sufficiently new for GNUNET
695 * @param handle the handle to our tap device
696 * @return TRUE if the version is sufficient, else FALSE
699 check_tapw32_version (HANDLE handle)
703 memset (&(version), 0, sizeof (version));
705 if (DeviceIoControl (handle, TAP_WIN_IOCTL_GET_VERSION,
706 &version, sizeof (version),
707 &version, sizeof (version), &len, NULL))
708 fprintf (stderr, "INFO: TAP-Windows Driver Version %d.%d %s\n",
711 (version[2] ? "(DEBUG)" : ""));
713 if ((version[0] != TAP_WIN_MIN_MAJOR) ||
714 (version[1] < TAP_WIN_MIN_MINOR )){
715 fprintf (stderr, "FATAL: This version of gnunet requires a TAP-Windows driver that is at least version %d.%d\n",
726 * Creates a tun-interface called dev;
728 * @return the fd to the tun or -1 on error
733 char device_path[256];
736 if (! setup_interface ())
739 return INVALID_HANDLE_VALUE;
742 if (! resolve_interface_name ())
745 return INVALID_HANDLE_VALUE;
748 /* Open Windows TAP-Windows adapter */
749 snprintf (device_path, sizeof (device_path), "%s%s%s",
754 handle = CreateFile (
756 GENERIC_READ | GENERIC_WRITE,
757 0, /* was: FILE_SHARE_READ */
760 FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
764 if (INVALID_HANDLE_VALUE == handle)
766 fprintf (stderr, "FATAL: CreateFile failed on TAP device: %s\n", device_path);
770 /* get driver version info */
771 if (! check_tapw32_version (handle))
773 CloseHandle (handle);
774 return INVALID_HANDLE_VALUE;
777 /* TODO (opt?): get MTU-Size */
779 fprintf (stderr, "DEBUG: successfully opened TAP device\n");
785 * Brings a TAP device up and sets it to connected state.
787 * @param handle the handle to our TAP device
788 * @return True if the operation succeeded, else false
791 tun_up (HANDLE handle)
795 if (! DeviceIoControl (handle, TAP_WIN_IOCTL_SET_MEDIA_STATUS,
796 &status, sizeof (status),
797 &status, sizeof (status), &len, NULL))
799 fprintf (stderr, "FATAL: TAP driver ignored request to UP interface (DeviceIoControl call)\n");
803 /* Wait for the device to go UP, might take some time. */
804 Sleep (TAP32_POSTUP_WAITTIME * 1000);
805 fprintf (stderr, "DEBUG: successfully set TAP device to UP\n");
812 * Attempts to read off an input facility (tap or named pipe) in overlapped mode.
815 * If the input facility is in IOSTATE_READY, it will issue a new read operation to the
816 * input handle. Then it goes into IOSTATE_QUEUED state.
817 * In case the read succeeded instantly the input facility enters 3.
820 * If the input facility is in IOSTATE_QUEUED state, it will check if the queued read has finished already.
821 * If it has finished, go to state 3.
822 * If it has failed, set IOSTATE_FAILED
825 * If the output facility is in state IOSTATE_READY, the read-buffer is copied to the output buffer.
826 * The input facility enters state IOSTATE_READY
827 * The output facility enters state IOSTATE_READY
828 * If the output facility is in state IOSTATE_QUEUED, the input facility enters IOSTATE_WAITING
830 * IOSTATE_WAITING is reset by the output facility, once it has completed.
832 * @param input_facility input named pipe or file to work with.
833 * @param output_facility output pipe or file to hand over data to.
834 * @return false if an event reset was impossible (OS error), else true
837 attempt_read_tap (struct io_facility * input_facility,
838 struct io_facility * output_facility)
840 struct GNUNET_MessageHeader * hdr;
843 switch (input_facility->facility_state)
847 if (! ResetEvent (input_facility->overlapped.hEvent))
852 input_facility->buffer_size = 0;
854 /* Check how the task is handled */
855 if (ReadFile (input_facility->handle,
856 input_facility->buffer,
857 sizeof (input_facility->buffer) - sizeof (struct GNUNET_MessageHeader),
858 &input_facility->buffer_size,
859 &input_facility->overlapped))
860 {/* async event processed immediately*/
862 /* reset event manually*/
863 if (! SetEvent (input_facility->overlapped.hEvent))
866 fprintf (stderr, "DEBUG: tap read succeeded immediately\n");
868 /* we successfully read something from the TAP and now need to
869 * send it our via STDOUT. Is that possible at the moment? */
870 if ((IOSTATE_READY == output_facility->facility_state ||
871 IOSTATE_WAITING == output_facility->facility_state)
872 && (0 < input_facility->buffer_size))
873 { /* hand over this buffers content and apply message header for gnunet */
874 hdr = (struct GNUNET_MessageHeader *) output_facility->buffer;
875 size = input_facility->buffer_size + sizeof (struct GNUNET_MessageHeader);
877 memcpy (output_facility->buffer + sizeof (struct GNUNET_MessageHeader),
878 input_facility->buffer,
879 input_facility->buffer_size);
881 output_facility->buffer_size = size;
882 hdr->size = htons (size);
883 hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
884 output_facility->facility_state = IOSTATE_READY;
886 else if (0 < input_facility->buffer_size)
887 { /* If we have have read our buffer, wait for our write-partner*/
888 input_facility->facility_state = IOSTATE_WAITING;
889 // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish?
892 else /* operation was either queued or failed*/
894 int err = GetLastError ();
895 if (ERROR_IO_PENDING == err)
896 { /* operation queued */
897 input_facility->facility_state = IOSTATE_QUEUED;
900 { /* error occurred, let the rest of the elements finish */
901 input_facility->path_open = FALSE;
902 input_facility->facility_state = IOSTATE_FAILED;
903 if (IOSTATE_WAITING == output_facility->facility_state)
904 output_facility->path_open = FALSE;
906 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish\n");
911 // We are queued and should check if the read has finished
914 // there was an operation going on already, check if that has completed now.
916 if (GetOverlappedResult (input_facility->handle,
917 &input_facility->overlapped,
918 &input_facility->buffer_size,
920 {/* successful return for a queued operation */
921 if (! ResetEvent (input_facility->overlapped.hEvent))
924 fprintf (stderr, "DEBUG: tap read succeeded delayed\n");
926 /* we successfully read something from the TAP and now need to
927 * send it our via STDOUT. Is that possible at the moment? */
928 if ((IOSTATE_READY == output_facility->facility_state ||
929 IOSTATE_WAITING == output_facility->facility_state)
930 && 0 < input_facility->buffer_size)
931 { /* hand over this buffers content and apply message header for gnunet */
932 hdr = (struct GNUNET_MessageHeader *) output_facility->buffer;
933 size = input_facility->buffer_size + sizeof (struct GNUNET_MessageHeader);
935 memcpy (output_facility->buffer + sizeof (struct GNUNET_MessageHeader),
936 input_facility->buffer,
937 input_facility->buffer_size);
939 output_facility->buffer_size = size;
940 hdr->size = htons(size);
941 hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
942 output_facility->facility_state = IOSTATE_READY;
943 input_facility->facility_state = IOSTATE_READY;
945 else if (0 < input_facility->buffer_size)
946 { /* If we have have read our buffer, wait for our write-partner*/
947 input_facility->facility_state = IOSTATE_WAITING;
948 // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish?
952 { /* operation still pending/queued or failed? */
953 int err = GetLastError ();
954 if ((ERROR_IO_INCOMPLETE != err) && (ERROR_IO_PENDING != err))
955 { /* error occurred, let the rest of the elements finish */
956 input_facility->path_open = FALSE;
957 input_facility->facility_state = IOSTATE_FAILED;
958 if (IOSTATE_WAITING == output_facility->facility_state)
959 output_facility->path_open = FALSE;
960 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish\n");
966 hdr = (struct GNUNET_MessageHeader *) output_facility->buffer;
967 size = input_facility->buffer_size + sizeof (struct GNUNET_MessageHeader);
969 memcpy (output_facility->buffer + sizeof (struct GNUNET_MessageHeader),
970 input_facility->buffer,
971 input_facility->buffer_size);
973 output_facility->buffer_size = size;
974 hdr->size = htons (size);
975 hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
976 output_facility->facility_state = IOSTATE_READY;
977 input_facility->facility_state = IOSTATE_READY;
986 * Attempts to read off an input facility (tap or named pipe) in overlapped mode.
989 * If the input facility is in IOSTATE_READY, it will issue a new read operation to the
990 * input handle. Then it goes into IOSTATE_QUEUED state.
991 * In case the read succeeded instantly the input facility enters 3.
994 * If the input facility is in IOSTATE_QUEUED state, it will check if the queued read has finished already.
995 * If it has finished, go to state 3.
996 * If it has failed, set IOSTATE_FAILED
999 * If the facility is finished with ready
1000 * The read-buffer is copied to the output buffer, except for the GNUNET_MessageHeader.
1001 * The input facility enters state IOSTATE_READY
1002 * The output facility enters state IOSTATE_READY
1003 * If the output facility is in state IOSTATE_QUEUED, the input facility enters IOSTATE_WAITING
1005 * IOSTATE_WAITING is reset by the output facility, once it has completed.
1007 * @param input_facility input named pipe or file to work with.
1008 * @param output_facility output pipe or file to hand over data to.
1009 * @return false if an event reset was impossible (OS error), else true
1012 attempt_read_stdin (struct io_facility * input_facility,
1013 struct io_facility * output_facility)
1015 struct GNUNET_MessageHeader * hdr;
1017 switch (input_facility->facility_state)
1021 if (! ResetEvent (input_facility->overlapped.hEvent))
1023 input_facility->buffer_size = 0;
1024 status = ReadFile (input_facility->handle,
1025 input_facility->buffer,
1026 sizeof (input_facility->buffer),
1027 &input_facility->buffer_size,
1028 &input_facility->overlapped);
1030 /* Check how the task is handled */
1031 if (status && (sizeof (struct GNUNET_MessageHeader) < input_facility->buffer_size))
1032 {/* async event processed immediately*/
1033 hdr = (struct GNUNET_MessageHeader *) input_facility->buffer;
1035 /* reset event manually*/
1036 if (! SetEvent (input_facility->overlapped.hEvent))
1039 fprintf (stderr, "DEBUG: stdin read succeeded immediately\n");
1041 if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER ||
1042 ntohs (hdr->size) > sizeof (input_facility->buffer))
1044 fprintf (stderr, "WARNING: Protocol violation, got GNUnet Message type %h, size %h\n", ntohs (hdr->type), ntohs (hdr->size));
1045 input_facility->facility_state = IOSTATE_READY;
1048 if (ntohs (hdr->size) > input_facility->buffer_size);
1049 // TODO: add support for partial read
1051 /* we successfully read something from the TAP and now need to
1052 * send it our via STDOUT. Is that possible at the moment? */
1053 if (sizeof (struct GNUNET_MessageHeader) < input_facility->buffer_size)
1055 if (IOSTATE_READY == output_facility->facility_state ||
1056 IOSTATE_WAITING == output_facility->facility_state)
1058 /* hand over this buffers content and strip gnunet message header */
1059 memcpy (output_facility->buffer,
1060 input_facility->buffer + sizeof (struct GNUNET_MessageHeader),
1061 input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader));
1062 output_facility->buffer_size = input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader);
1063 output_facility->facility_state = IOSTATE_READY;
1066 else if (IOSTATE_QUEUED == output_facility->facility_state)
1067 /* If we have have read our buffer, wait for our write-partner*/
1068 input_facility->facility_state = IOSTATE_WAITING;
1069 // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish?
1072 else if (status && 0 >= input_facility->buffer_size)
1074 if (! SetEvent (input_facility->overlapped.hEvent))
1077 input_facility->facility_state = IOSTATE_READY;
1079 else /* operation was either queued or failed*/
1081 int err = GetLastError ();
1082 if (ERROR_IO_PENDING == err)
1083 { /* operation queued */
1084 input_facility->facility_state = IOSTATE_QUEUED;
1087 { /* error occurred, let the rest of the elements finish */
1088 input_facility->path_open = FALSE;
1089 input_facility->facility_state = IOSTATE_FAILED;
1090 if (IOSTATE_WAITING == output_facility->facility_state)
1091 output_facility->path_open = FALSE;
1093 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish\n");
1098 // We are queued and should check if the read has finished
1099 case IOSTATE_QUEUED:
1101 // there was an operation going on already, check if that has completed now.
1102 if (GetOverlappedResult (input_facility->handle,
1103 &input_facility->overlapped,
1104 &input_facility->buffer_size,
1106 {/* successful return for a queued operation */
1107 hdr = (struct GNUNET_MessageHeader *) input_facility->buffer;
1109 if (! ResetEvent (input_facility->overlapped.hEvent))
1112 fprintf (stderr, "DEBUG: stdin read succeeded delayed\n");
1114 if ((ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER) ||
1115 (ntohs (hdr->size) > sizeof (input_facility->buffer)))
1117 fprintf (stderr, "WARNING: Protocol violation, got GNUnet Message type %h, size %h\n", ntohs (hdr->type), ntohs (hdr->size));
1118 input_facility->facility_state = IOSTATE_READY;
1121 if (ntohs (hdr->size) > input_facility->buffer_size );
1122 // TODO: add support for partial read
1124 /* we successfully read something from the TAP and now need to
1125 * send it our via STDOUT. Is that possible at the moment? */
1126 if ((IOSTATE_READY == output_facility->facility_state ||
1127 IOSTATE_WAITING == output_facility->facility_state)
1128 && sizeof(struct GNUNET_MessageHeader) < input_facility->buffer_size)
1129 { /* hand over this buffers content and strip gnunet message header */
1130 memcpy (output_facility->buffer,
1131 input_facility->buffer + sizeof(struct GNUNET_MessageHeader),
1132 input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader));
1133 output_facility->buffer_size = input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader);
1134 output_facility->facility_state = IOSTATE_READY;
1135 input_facility->facility_state = IOSTATE_READY;
1137 else if (sizeof(struct GNUNET_MessageHeader) < input_facility->buffer_size)
1138 { /* If we have have read our buffer, wait for our write-partner*/
1139 input_facility->facility_state = IOSTATE_WAITING;
1140 // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish?
1142 else if (sizeof(struct GNUNET_MessageHeader) >= input_facility->buffer_size)
1143 input_facility->facility_state = IOSTATE_READY;
1146 { /* operation still pending/queued or failed? */
1147 int err = GetLastError ();
1148 if ((ERROR_IO_INCOMPLETE != err) && (ERROR_IO_PENDING != err))
1149 { /* error occurred, let the rest of the elements finish */
1150 input_facility->path_open = FALSE;
1151 input_facility->facility_state = IOSTATE_FAILED;
1152 if (IOSTATE_WAITING == output_facility->facility_state)
1153 output_facility->path_open = FALSE;
1154 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish\n");
1159 case IOSTATE_RESUME: /* Our buffer was filled already but our write facility was busy. */
1160 memcpy (output_facility->buffer,
1161 input_facility->buffer + sizeof (struct GNUNET_MessageHeader),
1162 input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader));
1163 output_facility->buffer_size = input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader);
1164 output_facility->facility_state = IOSTATE_READY;
1165 input_facility->facility_state = IOSTATE_READY;
1174 * Attempts to write to an output facility (tap or named pipe) in overlapped mode.
1176 * TODO: high level description
1178 * @param output_facility output pipe or file to hand over data to.
1179 * @param input_facility input named pipe or file to work with.
1180 * @return false if an event reset was impossible (OS error), else true
1183 attempt_write (struct io_facility * output_facility,
1184 struct io_facility * input_facility)
1188 switch (output_facility->facility_state)
1192 if (! ResetEvent (output_facility->overlapped.hEvent))
1195 output_facility->buffer_size_written = 0;
1196 status = WriteFile (output_facility->handle,
1197 output_facility->buffer,
1198 output_facility->buffer_size,
1199 &output_facility->buffer_size_written,
1200 &output_facility->overlapped);
1202 /* Check how the task was handled */
1204 output_facility->buffer_size_written == output_facility->buffer_size)
1205 {/* async event processed immediately*/
1207 fprintf (stderr, "DEBUG: write succeeded immediately\n");
1209 /* reset event manually*/
1210 if (! SetEvent (output_facility->overlapped.hEvent))
1213 /* we are now waiting for our buffer to be filled*/
1214 output_facility->facility_state = IOSTATE_WAITING;
1215 output_facility->buffer_size = 0;
1216 output_facility->buffer_size_written = 0;
1218 /* we successfully wrote something and now need to reset our reader */
1219 if (IOSTATE_WAITING == input_facility->facility_state)
1220 input_facility->facility_state = IOSTATE_RESUME;
1221 else if (IOSTATE_FAILED == input_facility->facility_state)
1222 output_facility->path_open = FALSE;
1224 else /* operation was either queued or failed*/
1226 int err = GetLastError ();
1227 if (ERROR_IO_PENDING == err)
1228 { /* operation queued */
1229 output_facility->facility_state = IOSTATE_QUEUED;
1232 { /* error occurred, close this path */
1233 output_facility->path_open = FALSE;
1234 output_facility->facility_state = IOSTATE_FAILED;
1235 fprintf (stderr, "FATAL: Write to handle failed, exiting\n");
1239 case IOSTATE_QUEUED:
1240 // there was an operation going on already, check if that has completed now.
1241 status = GetOverlappedResult (output_facility->handle,
1242 &output_facility->overlapped,
1243 &output_facility->buffer_size_written,
1246 output_facility->buffer_size_written == output_facility->buffer_size)
1247 {/* successful return for a queued operation */
1248 if (! ResetEvent (output_facility->overlapped.hEvent))
1251 fprintf (stderr, "DEBUG: write succeeded delayed\n");
1253 /* we are now waiting for our buffer to be filled*/
1254 output_facility->facility_state = IOSTATE_WAITING;
1255 output_facility->buffer_size = 0;
1256 output_facility->buffer_size_written = 0;
1258 /* we successfully wrote something and now need to reset our reader */
1259 if (IOSTATE_WAITING == input_facility->facility_state)
1260 input_facility->facility_state = IOSTATE_RESUME;
1261 else if (IOSTATE_FAILED == input_facility->facility_state)
1262 output_facility->path_open = FALSE;
1265 { /* operation still pending/queued or failed? */
1266 int err = GetLastError ();
1267 if ((ERROR_IO_INCOMPLETE != err) && (ERROR_IO_PENDING != err))
1268 { /* error occurred, close this path */
1269 output_facility->path_open = FALSE;
1270 output_facility->facility_state = IOSTATE_FAILED;
1271 fprintf (stderr, "FATAL: Write to handle failed, exiting\n");
1281 * Initialize a overlapped structure
1283 * @param elem the element to initilize
1284 * @param initial_state the initial state for this instance
1285 * @param signaled if the hEvent created should default to signaled or not
1286 * @return true on success, else false
1289 initialize_io_facility (struct io_facility * elem,
1293 elem->path_open = TRUE;
1294 elem->handle = INVALID_HANDLE_VALUE;
1295 elem->facility_state = initial_state;
1296 elem->buffer_size = 0;
1297 elem->overlapped.hEvent = CreateEvent (NULL, TRUE, signaled, NULL);
1298 if (NULL == elem->overlapped.hEvent)
1306 * Start forwarding to and from the tunnel.
1308 * @param fd_tun tunnel FD
1311 run (HANDLE tap_handle)
1313 /* IO-Facility for reading from our virtual interface */
1314 struct io_facility tap_read;
1315 /* IO-Facility for writing to our virtual interface */
1316 struct io_facility tap_write;
1317 /* IO-Facility for reading from stdin */
1318 struct io_facility std_in;
1319 /* IO-Facility for writing to stdout */
1320 struct io_facility std_out;
1322 HANDLE parent_std_in_handle = GetStdHandle (STD_INPUT_HANDLE);
1323 HANDLE parent_std_out_handle = GetStdHandle (STD_OUTPUT_HANDLE);
1326 /* we do this HERE and not beforehand (in init_tun()), in contrast to openvpn
1327 * to remove the need to flush the arp cache, handle DHCP and wrong IPs.
1329 * DHCP and such are all features we will never use in gnunet afaik.
1330 * But for openvpn those are essential.
1332 if (! tun_up (tap_handle))
1335 /* Initialize our overlapped IO structures*/
1336 if (! (initialize_io_facility (&tap_read, IOSTATE_READY, FALSE)
1337 && initialize_io_facility (&tap_write, IOSTATE_WAITING, TRUE)
1338 && initialize_io_facility (&std_in, IOSTATE_READY, FALSE)
1339 && initialize_io_facility (&std_out, IOSTATE_WAITING, TRUE)))
1340 goto teardown_final;
1342 /* Handles for STDIN and STDOUT */
1343 tap_read.handle = tap_handle;
1344 tap_write.handle = tap_handle;
1346 #ifdef DEBUG_TO_CONSOLE
1347 /* Debug output to console STDIN/STDOUT*/
1348 std_in.handle = parent_std_in_handle;
1349 std_out.handle = parent_std_out_handle;
1352 fprintf (stderr, "DEBUG: reopening stdin/out for overlapped IO\n");
1354 * Find out the types of our handles.
1355 * This part is a problem, because in windows we need to handle files,
1356 * pipes and the console differently.
1358 if ((FILE_TYPE_PIPE != GetFileType (parent_std_in_handle)) ||
1359 (FILE_TYPE_PIPE != GetFileType (parent_std_out_handle)))
1361 fprintf (stderr, "ERROR: stdin/stdout must be named pipes\n");
1365 std_in.handle = ReOpenFile (parent_std_in_handle,
1367 FILE_SHARE_WRITE | FILE_SHARE_READ,
1368 FILE_FLAG_OVERLAPPED);
1370 if (INVALID_HANDLE_VALUE == std_in.handle)
1372 fprintf (stderr, "FATAL: Could not reopen stdin for in overlapped mode, has to be a named pipe\n");
1376 std_out.handle = ReOpenFile (parent_std_out_handle,
1379 FILE_FLAG_OVERLAPPED);
1381 if (INVALID_HANDLE_VALUE == std_out.handle)
1383 fprintf (stderr, "FATAL: Could not reopen stdout for in overlapped mode, has to be a named pipe\n");
1388 fprintf (stderr, "DEBUG: mainloop has begun\n");
1390 while (std_out.path_open || tap_write.path_open)
1392 /* perform READ from stdin if possible */
1393 if (std_in.path_open && (! attempt_read_stdin (&std_in, &tap_write)))
1396 /* perform READ from tap if possible */
1397 if (tap_read.path_open && (! attempt_read_tap (&tap_read, &std_out)))
1400 /* perform WRITE to tap if possible */
1401 if (tap_write.path_open && (! attempt_write (&tap_write, &std_in)))
1404 /* perform WRITE to STDOUT if possible */
1405 if (std_out.path_open && (! attempt_write (&std_out, &tap_read)))
1411 fprintf (stderr, "DEBUG: teardown initiated\n");
1413 CancelIo (tap_handle);
1414 CancelIo (std_in.handle);
1415 CancelIo (std_out.handle);
1419 CloseHandle (tap_handle);
1424 * Open VPN tunnel interface.
1426 * @param argc must be 6
1427 * @param argv 0: binary name (gnunet-helper-vpn)
1428 * 1: tunnel interface prefix (gnunet-vpn)
1429 * 2: IPv6 address (::1), "-" to disable
1430 * 3: IPv6 netmask length in bits (64), ignored if #2 is "-"
1431 * 4: IPv4 address (1.2.3.4), "-" to disable
1432 * 5: IPv4 netmask (255.255.0.0), ignored if #4 is "-"
1435 main (int argc, char **argv)
1437 char hwid[LINE_LEN];
1440 BOOL have_ip4 = FALSE;
1441 BOOL have_ip6 = FALSE;
1445 fprintf (stderr, "FATAL: must supply 5 arguments\nUsage:\ngnunet-helper-vpn <if name prefix> <address6 or \"-\"> <netbits6> <address4 or \"-\"> <netmask4>\n", argv[0]);
1449 strncpy (hwid, argv[1], LINE_LEN);
1450 hwid[LINE_LEN - 1] = '\0';
1453 * We use our PID for finding/resolving the control-panel name of our virtual
1454 * device. PIDs are (of course) unique at runtime, thus we can safely use it
1455 * as additional hardware-id for our device.
1457 snprintf (secondary_hwid, LINE_LEN / 2, "%s-%d",
1461 if (INVALID_HANDLE_VALUE == (handle = init_tun ()))
1463 fprintf (stderr, "FATAL: could not initialize virtual-interface %s with IPv6 %s/%s and IPv4 %s/%s\n",
1473 fprintf (stderr, "DEBUG: Setting IPs, if needed\n");
1474 if (0 != strcmp (argv[2], "-"))
1476 const char *address = argv[2];
1477 long prefix_len = atol (argv[3]);
1479 if ((prefix_len < 1) || (prefix_len > 127))
1481 fprintf (stderr, "FATAL: ipv6 prefix_len out of range\n");
1486 fprintf (stderr, "DEBUG: Setting IP6 address: %s/%d\n",address,prefix_len);
1487 if (0 != (global_ret = set_address6 (address, prefix_len)))
1493 if (0 != strcmp (argv[4], "-"))
1495 const char *address = argv[4];
1496 const char *mask = argv[5];
1498 fprintf (stderr, "DEBUG: Setting IP4 address: %s/%s\n",address,mask);
1499 if (0 != (global_ret = set_address4 (address, mask)))
1511 const char *address = argv[4];
1512 fprintf (stderr, "DEBUG: Removing IP4 address\n");
1513 remove_address4 (address);
1517 const char *address = argv[2];
1518 fprintf (stderr, "DEBUG: Removing IP6 address\n");
1519 remove_address6 (address);
1522 fprintf (stderr, "DEBUG: removing interface\n");
1523 remove_interface ();
1524 fprintf (stderr, "DEBUG: graceful exit completed\n");