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 * Maximum size of a GNUnet message (GNUNET_SERVER_MAX_MESSAGE_SIZE)
62 #define MAX_SIZE 65536
65 * Name or Path+Name of our driver in Unicode.
66 * The .sys and .cat files HAVE to be in the same location as this file!
68 #define INF_FILE "OemWin2k.inf"
71 * Hardware ID used in the inf-file.
72 * This might change over time, as openvpn advances their driver
74 #define HARDWARE_ID "tap0901"
77 * Minimum major-id of the driver version we can work with
79 #define TAP_WIN_MIN_MAJOR 9
82 * Minimum minor-id of the driver version we can work with.
83 * v <= 7 has buggy IPv6.
84 * v == 8 is broken for small IPv4 Packets
86 #define TAP_WIN_MIN_MINOR 9
89 * Time in seconds to wait for our virtual device to go up after telling it to do so.
91 * openvpn doesn't specify a value, 4 seems sane for testing, even for openwrt
92 * (in fact, 4 was chosen by a fair dice roll...)
94 #define TAP32_POSTUP_WAITTIME 4
97 * Location of the network interface list resides in registry.
99 #define INTERFACE_REGISTRY_LOCATION "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
102 * Our local process' PID. Used for creating a sufficiently unique additional
103 * hardware ID for our device.
105 static char secondary_hwid[LINE_LEN / 2];
108 * Device's visible Name, used to identify a network device in netsh.
109 * eg: "Local Area Connection 9"
111 static char device_visible_name[256];
114 * This is our own local instance of a virtual network interface
115 * It is (somewhat) equivalent to using tun/tap in unixoid systems
117 * Upon initialization, we create such an device node.
118 * Upon termination, we remove it again.
120 * If we crash this device might stay around.
122 static HDEVINFO DeviceInfo = INVALID_HANDLE_VALUE;
125 * Registry Key we hand over to windows to spawn a new virtual interface
127 static SP_DEVINFO_DATA DeviceNode;
130 * GUID of our virtual device in the form of
131 * {12345678-1234-1234-1234-123456789abc} - in hex
133 static char device_guid[256];
136 * A IO Object + read/writebuffer + buffer-size for windows asynchronous IO handling
141 * The mode the state machine associated with this object is in. IOSTATE_*
146 * If the path is open or blocked in general (used for quickly checking)
148 BOOL path_open; // BOOL is winbool (int), NOT boolean (unsigned char)!
151 * Windows Object-Handle (used for accessing TAP and STDIN/STDOUT)
156 * Overlaped IO structure used for asynchronous IO in windows.
158 OVERLAPPED overlapped;
161 * Buffer for reading things to and writing from...
163 unsigned char buffer[MAX_SIZE];
166 * How much of this buffer was used when reading or how much data can be written
171 * Amount of data written, is compared to buffer_size.
173 DWORD buffer_size_written;
177 * Operlapped IO states for facility objects
178 * overlapped I/O has failed, stop processing
180 #define IOSTATE_FAILED -1
182 * overlapped I/O is ready for work
184 #define IOSTATE_READY 0
186 * overlapped I/O has been queued
188 #define IOSTATE_QUEUED 1
190 * overlapped I/O has finished, but is waiting for it's write-partner
192 #define IOSTATE_WAITING 3
195 * ReOpenFile is only available as of XP SP2 and 2003 SP1
197 WINBASEAPI HANDLE WINAPI ReOpenFile (HANDLE, DWORD, DWORD, DWORD);
201 * Wrapper for executing a shellcommand in windows.
203 * @param command - the command + parameters to execute
204 * @return * exitcode of the program executed,
205 * * EINVAL (cmd/file not found)
206 * * EPIPE (could not read STDOUT)
209 execute_shellcommand (const char *command)
213 if (NULL == command ||
214 NULL == (pipe = _popen (command, "rt")))
218 fprintf (stderr, "DEBUG: Command output: \n");
219 char output[LINE_LEN];
220 while (NULL != fgets (output, sizeof (output), pipe))
221 fprintf (stderr, "%s", output);
224 return _pclose (pipe);
229 * @brief Sets the IPv6-Address given in address on the interface dev
231 * @param address the IPv6-Address
232 * @param prefix_len the length of the network-prefix
235 set_address6 (const char *address, unsigned long prefix_len)
238 char command[LINE_LEN];
239 struct sockaddr_in6 sa6;
242 * parse the new address
244 memset (&sa6, 0, sizeof (struct sockaddr_in6));
245 sa6.sin6_family = AF_INET6;
246 if (1 != inet_pton (AF_INET6, address, &sa6.sin6_addr.s6_addr))
248 fprintf (stderr, "ERROR: Failed to parse address `%s': %s\n", address,
254 * prepare the command
256 snprintf (command, LINE_LEN,
257 "netsh interface ipv6 add address \"%s\" %s/%d store=active",
258 device_visible_name, address, prefix_len);
262 ret = execute_shellcommand (command);
266 fprintf (stderr, "FATAL: Setting IPv6 address failed: %s\n", strerror (ret));
272 * @brief Removes the IPv6-Address given in address from the interface dev
274 * @param dev the interface to remove
275 * @param address the IPv4-Address
276 * @param mask the netmask
279 remove_address6 (const char *address)
281 char command[LINE_LEN];
284 // sanity checking was already done in set_address6
286 * prepare the command
288 snprintf (command, LINE_LEN,
289 "netsh interface ipv6 delete address \"%s\" store=persistent",
290 device_visible_name, address);
294 ret = execute_shellcommand (command);
298 fprintf (stderr, "FATAL: removing IPv6 address failed: %s\n", strerror (ret));
303 * @brief Sets the IPv4-Address given in address on the interface dev
305 * @param dev the interface to configure
306 * @param address the IPv4-Address
307 * @param mask the netmask
310 set_address4 (const char *address, const char *mask)
313 char command[LINE_LEN];
315 struct sockaddr_in addr;
316 addr.sin_family = AF_INET;
321 if (1 != inet_pton (AF_INET, address, &addr.sin_addr.s_addr))
323 fprintf (stderr, "ERROR: Failed to parse address `%s': %s\n", address,
327 // Set Device to Subnet-Mode?
328 // do we really need tun.c:2925 ?
331 * prepare the command
333 snprintf (command, LINE_LEN,
334 "netsh interface ipv4 add address \"%s\" %s %s store=active",
335 device_visible_name, address, mask);
339 ret = execute_shellcommand (command);
343 fprintf (stderr, "FATAL: Setting IPv4 address failed: %s\n", strerror (ret));
349 * @brief Removes the IPv4-Address given in address from the interface dev
351 * @param dev the interface to remove
352 * @param address the IPv4-Address
353 * @param mask the netmask
356 remove_address4 (const char *address)
358 char command[LINE_LEN];
361 // sanity checking was already done in set_address4
364 * prepare the command
366 snprintf (command, LINE_LEN,
367 "netsh interface ipv4 delete address \"%s\" gateway=all store=persistent",
368 device_visible_name, address);
372 ret = execute_shellcommand (command);
376 fprintf (stderr, "FATAL: removing IPv4 address failed: %s\n", strerror (ret));
381 * Setup a new virtual interface to use for tunneling.
383 * @return: TRUE if setup was successful, else FALSE
389 * where to find our inf-file. (+ the "full" path, after windows found")
391 * We do not directly input all the props here, because openvpn will update
392 * these details over time.
394 char inf_file_path[MAX_PATH];
395 char * temp_inf_filename;
396 char hwidlist[LINE_LEN + 4];
397 char class_name[128];
402 * Set the device's hardware ID and add it to a list.
403 * This information will later on identify this device in registry.
405 strncpy (hwidlist, HARDWARE_ID, LINE_LEN);
407 * this is kind of over-complicated, but allows keeps things independent of
408 * how the openvpn-hwid is actually stored.
410 * A HWID list is double-\0 terminated and \0 separated
412 str_length = strlen (hwidlist) + 1;
413 strncpy (&hwidlist[str_length], secondary_hwid, LINE_LEN);
414 str_length += strlen (&hwidlist[str_length]) + 1;
417 * Locate the inf-file, we need to store it somewhere where the system can
418 * find it. A good choice would be CWD/PDW or %WINDIR$\system32\
420 * TODO: How about win64 in the future?
421 * We need to use a different driver for amd64/i386 !
423 GetFullPathNameA (INF_FILE, MAX_PATH, inf_file_path, &temp_inf_filename);
426 * Bootstrap our device info using the drivers inf-file
428 if (!SetupDiGetINFClassA (inf_file_path,
430 class_name, sizeof (class_name) / sizeof (char),
435 * Collect all the other needed information...
436 * let the system fill our this form
438 DeviceInfo = SetupDiCreateDeviceInfoList (&class_guid, NULL);
439 if (DeviceInfo == INVALID_HANDLE_VALUE)
442 DeviceNode.cbSize = sizeof (SP_DEVINFO_DATA);
443 if (!SetupDiCreateDeviceInfoA (DeviceInfo,
452 /* Deploy all the information collected into the registry */
453 if (!SetupDiSetDeviceRegistryPropertyA (DeviceInfo,
457 str_length * sizeof (char)))
460 /* Install our new class(=device) into the system */
461 if (!SetupDiCallClassInstaller (DIF_REGISTERDEVICE,
466 /* This system call tends to take a while (several seconds!) on
467 "modern" Windoze systems */
468 if (!UpdateDriverForPlugAndPlayDevicesA (NULL,
471 INSTALLFLAG_FORCE | INSTALLFLAG_NONINTERACTIVE,
472 NULL)) //reboot required? NEVER!
480 * Remove our new virtual interface to use for tunneling.
481 * This function must be called AFTER setup_interface!
483 * @return: TRUE if destruction was successful, else FALSE
488 SP_REMOVEDEVICE_PARAMS remove;
490 if (INVALID_HANDLE_VALUE == DeviceInfo)
493 remove.ClassInstallHeader.cbSize = sizeof (SP_CLASSINSTALL_HEADER);
494 remove.HwProfile = 0;
495 remove.Scope = DI_REMOVEDEVICE_GLOBAL;
496 remove.ClassInstallHeader.InstallFunction = DIF_REMOVE;
498 * 1. Prepare our existing device information set, and place the
499 * uninstall related information into the structure
501 if (!SetupDiSetClassInstallParamsA (DeviceInfo,
502 (PSP_DEVINFO_DATA) & DeviceNode,
503 &remove.ClassInstallHeader,
507 * 2. Uninstall the virtual interface using the class installer
509 if (!SetupDiCallClassInstaller (DIF_REMOVE,
511 (PSP_DEVINFO_DATA) & DeviceNode))
514 SetupDiDestroyDeviceInfoList (DeviceInfo);
521 * Do all the lookup necessary to retrieve the inteface's actual name
524 * @return: TRUE if we were able to lookup the interface's name, else FALSE
527 resolve_interface_name ()
529 SP_DEVINFO_LIST_DETAIL_DATA device_details;
530 char pnp_instance_id [MAX_DEVICE_ID_LEN];
531 HKEY adapter_key_handle;
536 boolean retval = FALSE;
537 char adapter[] = INTERFACE_REGISTRY_LOCATION;
539 /* We can obtain the PNP instance ID from our setupapi handle */
540 device_details.cbSize = sizeof (device_details);
541 if (CR_SUCCESS != CM_Get_Device_ID_ExA (DeviceNode.DevInst,
542 (PCHAR) pnp_instance_id,
545 NULL)) //hMachine, we are local
548 /* Registry is incredibly slow, retry for up to 30 seconds to allow registry to refresh */
549 for (retrys = 0; retrys < 120 && !retval; retrys++)
554 /* Now we can use this ID to locate the correct networks interface in registry */
555 if (ERROR_SUCCESS != RegOpenKeyExA (
560 &adapter_key_handle))
563 /* Of course there is a multitude of entries here, with arbitrary names,
564 * thus we need to iterate through there.
568 char instance_key[256];
569 char query_key [256];
570 HKEY instance_key_handle;
571 char pnpinstanceid_name[] = "PnpInstanceID";
572 char pnpinstanceid_value[256];
573 char adaptername_name[] = "Name";
576 len = 256 * sizeof (char);
577 /* optain a subkey of {4D36E972-E325-11CE-BFC1-08002BE10318} */
578 status = RegEnumKeyExA (
588 /* this may fail due to one of two reasons:
589 * we are at the end of the list*/
590 if (ERROR_NO_MORE_ITEMS == status)
592 // * we found a broken registry key, continue with the next key.
593 if (ERROR_SUCCESS != status)
596 /* prepare our new query string: */
597 snprintf (query_key, 256, "%s\\%s\\Connection",
601 /* look inside instance_key\\Connection */
602 status = RegOpenKeyExA (
607 &instance_key_handle);
609 if (status != ERROR_SUCCESS)
612 /* now, read our PnpInstanceID */
613 len = sizeof (pnpinstanceid_value);
614 status = RegQueryValueExA (instance_key_handle,
616 NULL, //reserved, always NULL according to MSDN
618 (LPBYTE) pnpinstanceid_value,
621 if (status != ERROR_SUCCESS || data_type != REG_SZ)
624 /* compare the value we got to our devices PNPInstanceID*/
625 if (0 != strncmp (pnpinstanceid_value, pnp_instance_id,
626 sizeof (pnpinstanceid_value) / sizeof (char)))
629 len = sizeof (device_visible_name);
630 status = RegQueryValueExA (
633 NULL, //reserved, always NULL according to MSDN
635 (LPBYTE) device_visible_name,
638 if (status != ERROR_SUCCESS || data_type != REG_SZ)
642 * we have successfully found OUR instance,
643 * save the device GUID before exiting
646 strncpy (device_guid, instance_key, 256);
648 fprintf (stderr, "DEBUG: Interface Name lookup succeeded on retry %d\n", retrys);
651 RegCloseKey (instance_key_handle);
656 RegCloseKey (adapter_key_handle);
663 check_tapw32_version (HANDLE handle)
667 memset (&(version), 0, sizeof (version));
670 if (DeviceIoControl (handle, TAP_WIN_IOCTL_GET_VERSION,
671 &version, sizeof (version),
672 &version, sizeof (version), &len, NULL))
674 fprintf (stderr, "INFO: TAP-Windows Driver Version %d.%d %s\n",
677 (version[2] ? "(DEBUG)" : ""));
680 if (version[0] != TAP_WIN_MIN_MAJOR || version[1] < TAP_WIN_MIN_MINOR)
682 fprintf (stderr, "FATAL: This version of gnunet requires a TAP-Windows driver that is at least version %d.%d!\n",
692 * Creates a tun-interface called dev;
694 * @return the fd to the tun or -1 on error
699 char device_path[256];
702 if (!setup_interface ())
705 return INVALID_HANDLE_VALUE;
708 if (!resolve_interface_name ())
711 return INVALID_HANDLE_VALUE;
714 /* Open Windows TAP-Windows adapter */
715 snprintf (device_path, sizeof (device_path), "%s%s%s",
720 handle = CreateFile (
722 GENERIC_READ | GENERIC_WRITE,
723 0, /* was: FILE_SHARE_READ */
726 FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
730 if (handle == INVALID_HANDLE_VALUE)
732 fprintf (stderr, "FATAL: CreateFile failed on TAP device: %s\n", device_path);
736 /* get driver version info */
737 if (!check_tapw32_version (handle))
739 CloseHandle (handle);
740 return INVALID_HANDLE_VALUE;
743 /* TODO (opt?): get MTU-Size */
750 * Brings a TAP device up and sets it to connected state.
752 * @param handle the handle to our TAP device
753 * @return True if the operation succeeded, else false
756 tun_up (HANDLE handle)
760 if (!DeviceIoControl (handle, TAP_WIN_IOCTL_SET_MEDIA_STATUS,
761 &status, sizeof (status),
762 &status, sizeof (status), &len, NULL))
764 fprintf (stderr, "FATAL: TAP driver ignored request to UP interface (DeviceIoControl call)!\n");
768 /* Wait for the device to go UP, might take some time. */
769 Sleep (TAP32_POSTUP_WAITTIME * 1000);
777 * Attempts to read off an input facility (tap or named pipe) in overlapped mode.
780 * If the input facility is in IOSTATE_READY, it will issue a new read operation to the
781 * input handle. Then it goes into IOSTATE_QUEUED state.
782 * In case the read succeeded instantly the input facility enters 3.
785 * If the input facility is in IOSTATE_QUEUED state, it will check if the queued read has finished already.
786 * If it has finished, go to state 3.
787 * If it has failed, set IOSTATE_FAILED
790 * If the output facility is in state IOSTATE_READY, the read-buffer is copied to the output buffer.
791 * The input facility enters state IOSTATE_READY
792 * The output facility enters state IOSTATE_READY
793 * If the output facility is in state IOSTATE_QUEUED, the input facility enters IOSTATE_WAITING
795 * IOSTATE_WAITING is reset by the output facility, once it has completed.
797 * @param input_facility input named pipe or file to work with.
798 * @param output_facility output pipe or file to hand over data to.
799 * @return false if an event reset was impossible (OS error), else true
802 attempt_read_tap (struct io_facility * input_facility,
803 struct io_facility * output_facility)
805 switch (input_facility->facility_state)
809 BOOL status; // BOOL is winbool, NOT boolean!
810 if (!ResetEvent (input_facility->overlapped.hEvent))
814 status = ReadFile (input_facility->handle,
815 input_facility->buffer + sizeof (struct GNUNET_MessageHeader),
816 sizeof (input_facility->buffer) - sizeof (struct GNUNET_MessageHeader),
817 &input_facility->buffer_size,
818 &input_facility->overlapped);
820 /* Check how the task is handled */
822 {/* async event processed immediately*/
824 /* reset event manually*/
825 if (!SetEvent (input_facility->overlapped.hEvent))
828 /* we successfully read something from the TAP and now need to
829 * send it our via STDOUT. Is that possible at the moment? */
830 if ((IOSTATE_READY == output_facility->facility_state ||
831 IOSTATE_WAITING == output_facility->facility_state)
832 && 0 < input_facility->buffer_size)
833 { /* hand over this buffers content */
834 memcpy (output_facility->buffer,
835 input_facility->buffer,
836 sizeof (input_facility->buffer));
837 output_facility->buffer_size = input_facility->buffer_size;
838 output_facility->facility_state = IOSTATE_READY;
840 else if (0 < input_facility->buffer_size)
841 { /* If we have have read our buffer, wait for our write-partner*/
842 input_facility->facility_state = IOSTATE_WAITING;
843 // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish?
846 else /* operation was either queued or failed*/
848 int err = GetLastError ();
849 if (ERROR_IO_PENDING == err)
850 { /* operation queued */
851 input_facility->facility_state = IOSTATE_QUEUED;
854 { /* error occurred, let the rest of the elements finish */
855 input_facility->path_open = FALSE;
856 input_facility->facility_state = IOSTATE_FAILED;
857 if (IOSTATE_WAITING == output_facility->facility_state)
858 output_facility->path_open = FALSE;
860 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish!\n");
865 // We are queued and should check if the read has finished
868 BOOL status; // BOOL is winbool, NOT boolean!
870 // there was an operation going on already, check if that has completed now.
871 status = GetOverlappedResult (input_facility->handle,
872 &input_facility->overlapped,
873 &input_facility->buffer_size,
876 {/* successful return for a queued operation */
877 if (!ResetEvent (input_facility->overlapped.hEvent))
880 /* we successfully read something from the TAP and now need to
881 * send it our via STDOUT. Is that possible at the moment? */
882 if ((IOSTATE_READY == output_facility->facility_state ||
883 IOSTATE_WAITING == output_facility->facility_state)
884 && 0 < input_facility->buffer_size)
885 { /* hand over this buffers content */
886 memcpy (output_facility->buffer,
887 input_facility->buffer,
888 input_facility->buffer_size);
889 output_facility->buffer_size = input_facility->buffer_size;
890 output_facility->facility_state = IOSTATE_READY;
891 input_facility->facility_state = IOSTATE_READY;
893 else if (0 < input_facility->buffer_size)
894 { /* If we have have read our buffer, wait for our write-partner*/
895 input_facility->facility_state = IOSTATE_WAITING;
896 // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish?
900 { /* operation still pending/queued or failed? */
901 int err = GetLastError ();
902 if (ERROR_IO_INCOMPLETE != err && ERROR_IO_PENDING != err)
903 { /* error occurred, let the rest of the elements finish */
904 input_facility->path_open = FALSE;
905 input_facility->facility_state = IOSTATE_FAILED;
906 if (IOSTATE_WAITING == output_facility->facility_state)
907 output_facility->path_open = FALSE;
908 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish!\n");
919 * Attempts to read off an input facility (tap or named pipe) in overlapped mode.
922 * If the input facility is in IOSTATE_READY, it will issue a new read operation to the
923 * input handle. Then it goes into IOSTATE_QUEUED state.
924 * In case the read succeeded instantly the input facility enters 3.
927 * If the input facility is in IOSTATE_QUEUED state, it will check if the queued read has finished already.
928 * If it has finished, go to state 3.
929 * If it has failed, set IOSTATE_FAILED
932 * If the facility is finished with ready
933 * The read-buffer is copied to the output buffer, except for the GNUNET_MessageHeader.
934 * The input facility enters state IOSTATE_READY
935 * The output facility enters state IOSTATE_READY
936 * If the output facility is in state IOSTATE_QUEUED, the input facility enters IOSTATE_WAITING
938 * IOSTATE_WAITING is reset by the output facility, once it has completed.
940 * @param input_facility input named pipe or file to work with.
941 * @param output_facility output pipe or file to hand over data to.
942 * @return false if an event reset was impossible (OS error), else true
945 attempt_read_stdin (struct io_facility * input_facility,
946 struct io_facility * output_facility)
948 switch (input_facility->facility_state)
952 BOOL status; // BOOL is winbool, NOT boolean!
953 if (!ResetEvent (input_facility->overlapped.hEvent))
957 status = ReadFile (input_facility->handle,
958 input_facility->buffer,
959 sizeof (input_facility->buffer),
960 &input_facility->buffer_size,
961 &input_facility->overlapped);
963 /* Check how the task is handled */
964 if (status && (0 < input_facility->buffer_size))
965 {/* async event processed immediately*/
966 struct GNUNET_MessageHeader *hdr;
968 /* reset event manually*/
969 if (!SetEvent (input_facility->overlapped.hEvent))
972 hdr = (struct GNUNET_MessageHeader *) input_facility->buffer;
973 if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER ||
974 ntohs (hdr->size) > sizeof (input_facility->buffer))
976 fprintf (stderr, "WARNING: Protocol violation, got GNUnet Message type %h, size %h!\n", ntohs (hdr->type), ntohs (hdr->size));
977 input_facility->facility_state = IOSTATE_READY;
980 //if (ntohs (hdr->size) > input_facility->buffer_size );
981 // TODO: add support for partial read
983 /* we successfully read something from the TAP and now need to
984 * send it our via STDOUT. Is that possible at the moment? */
985 if (IOSTATE_READY == output_facility->facility_state ||
986 IOSTATE_WAITING == output_facility->facility_state)
987 { /* hand over this buffers content */
988 memcpy (output_facility->buffer,
989 input_facility->buffer,
990 sizeof (input_facility->buffer));
991 output_facility->buffer_size = input_facility->buffer_size;
992 output_facility->facility_state = IOSTATE_READY;
994 else if (0 < input_facility->buffer_size)
995 { /* If we have have read our buffer, wait for our write-partner*/
996 input_facility->facility_state = IOSTATE_WAITING;
997 // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish?
1000 input_facility->facility_state = IOSTATE_READY;
1002 else if (status && 0 >= input_facility->buffer_size)
1004 if (!SetEvent (input_facility->overlapped.hEvent))
1007 input_facility->facility_state = IOSTATE_READY;
1009 else /* operation was either queued or failed*/
1011 int err = GetLastError ();
1012 if (ERROR_IO_PENDING == err)
1013 { /* operation queued */
1014 input_facility->facility_state = IOSTATE_QUEUED;
1017 { /* error occurred, let the rest of the elements finish */
1018 input_facility->path_open = FALSE;
1019 input_facility->facility_state = IOSTATE_FAILED;
1020 if (IOSTATE_WAITING == output_facility->facility_state)
1021 output_facility->path_open = FALSE;
1023 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish!\n");
1028 // We are queued and should check if the read has finished
1029 case IOSTATE_QUEUED:
1031 BOOL status; // BOOL is winbool, NOT boolean!
1033 // there was an operation going on already, check if that has completed now.
1034 status = GetOverlappedResult (input_facility->handle,
1035 &input_facility->overlapped,
1036 &input_facility->buffer_size,
1039 {/* successful return for a queued operation */
1040 if (!ResetEvent (input_facility->overlapped.hEvent))
1043 /* we successfully read something from the TAP and now need to
1044 * send it our via STDOUT. Is that possible at the moment? */
1045 if ((IOSTATE_READY == output_facility->facility_state ||
1046 IOSTATE_WAITING == output_facility->facility_state)
1047 && 0 < input_facility->buffer_size)
1048 { /* hand over this buffers content */
1049 memcpy (output_facility->buffer,
1050 input_facility->buffer,
1051 input_facility->buffer_size);
1052 output_facility->buffer_size = input_facility->buffer_size;
1053 output_facility->facility_state = IOSTATE_READY;
1054 input_facility->facility_state = IOSTATE_READY;
1056 else if (0 < input_facility->buffer_size)
1057 { /* If we have have read our buffer, wait for our write-partner*/
1058 input_facility->facility_state = IOSTATE_WAITING;
1059 // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish?
1063 { /* operation still pending/queued or failed? */
1064 int err = GetLastError ();
1065 if (ERROR_IO_INCOMPLETE != err && ERROR_IO_PENDING != err)
1066 { /* error occurred, let the rest of the elements finish */
1067 input_facility->path_open = FALSE;
1068 input_facility->facility_state = IOSTATE_FAILED;
1069 if (IOSTATE_WAITING == output_facility->facility_state)
1070 output_facility->path_open = FALSE;
1071 fprintf (stderr, "FATAL: Read from handle failed, allowing write to finish!\n");
1083 * Attempts to write to an output facility (tap or named pipe) in overlapped mode.
1085 * TODO: high level description
1087 * @param output_facility output pipe or file to hand over data to.
1088 * @param input_facility input named pipe or file to work with.
1089 * @return false if an event reset was impossible (OS error), else true
1092 attempt_write (struct io_facility * output_facility,
1093 struct io_facility * input_facility)
1095 if (IOSTATE_READY == output_facility->facility_state
1096 && output_facility->buffer_size > 0)
1098 BOOL status; // BOOL is winbool, NOT boolean!
1100 if (!ResetEvent (output_facility->overlapped.hEvent))
1105 status = WriteFile (output_facility->handle,
1106 output_facility->buffer,
1107 output_facility->buffer_size,
1108 &output_facility->buffer_size_written,
1109 &output_facility->overlapped);
1111 /* Check how the task is handled */
1113 output_facility->buffer_size_written == output_facility->buffer_size)
1114 {/* async event processed immediately*/
1116 /* reset event manually*/
1117 if (!SetEvent (output_facility->overlapped.hEvent))
1120 /* we are now waiting for our buffer to be filled*/
1121 output_facility->facility_state = IOSTATE_WAITING;
1122 output_facility->buffer_size = 0;
1123 output_facility->buffer_size_written = 0;
1125 /* we successfully wrote something and now need to reset our reader */
1126 if (IOSTATE_WAITING == input_facility->facility_state)
1127 input_facility->facility_state = IOSTATE_READY;
1128 else if (IOSTATE_FAILED == input_facility->facility_state)
1129 output_facility->path_open = FALSE;
1131 else /* operation was either queued or failed*/
1133 int err = GetLastError ();
1134 if (ERROR_IO_PENDING == err)
1135 { /* operation queued */
1136 output_facility->facility_state = IOSTATE_QUEUED;
1139 { /* error occurred, close this path */
1140 output_facility->path_open = FALSE;
1141 output_facility->facility_state = IOSTATE_FAILED;
1142 fprintf (stderr, "FATAL: Write to handle failed, exiting!\n");
1147 else if (IOSTATE_QUEUED == output_facility->facility_state)
1149 BOOL status; // BOOL is winbool, NOT boolean!
1150 // there was an operation going on already, check if that has completed now.
1151 status = GetOverlappedResult (output_facility->handle,
1152 &output_facility->overlapped,
1153 &output_facility->buffer_size_written,
1156 output_facility->buffer_size_written == output_facility->buffer_size)
1157 {/* successful return for a queued operation */
1158 if (!ResetEvent (output_facility->overlapped.hEvent))
1161 /* we are now waiting for our buffer to be filled*/
1162 output_facility->facility_state = IOSTATE_WAITING;
1163 output_facility->buffer_size = 0;
1164 output_facility->buffer_size_written = 0;
1166 /* we successfully wrote something and now need to reset our reader */
1167 if (IOSTATE_WAITING == input_facility->facility_state)
1168 input_facility->facility_state = IOSTATE_READY;
1169 else if (IOSTATE_FAILED == input_facility->facility_state)
1170 output_facility->path_open = FALSE;
1173 { /* operation still pending/queued or failed? */
1174 int err = GetLastError ();
1175 if (ERROR_IO_INCOMPLETE != err && ERROR_IO_PENDING != err)
1176 { /* error occurred, close this path */
1177 output_facility->path_open = FALSE;
1178 output_facility->facility_state = IOSTATE_FAILED;
1179 fprintf (stderr, "FATAL: Write to handle failed, exiting!\n");
1189 * Initialize a overlapped structure
1191 * @param elem the element to initilize
1192 * @param initial_state the initial state for this instance
1193 * @param signaled if the hEvent created should default to signaled or not
1194 * @return true on success, else false
1197 initialize_io_facility (struct io_facility * elem,
1202 elem->path_open = TRUE;
1203 elem->handle = INVALID_HANDLE_VALUE;
1204 elem->facility_state = initial_state;
1205 elem->buffer_size = 0;
1206 elem->overlapped.hEvent = CreateEvent (NULL, TRUE, signaled, NULL);
1207 if (NULL == elem->overlapped.hEvent)
1215 * Start forwarding to and from the tunnel.
1217 * @param fd_tun tunnel FD
1220 run (HANDLE tap_handle)
1222 /* IO-Facility for reading from our virtual interface */
1223 struct io_facility tap_read;
1224 /* IO-Facility for writing to our virtual interface */
1225 struct io_facility tap_write;
1226 /* IO-Facility for reading from stdin */
1227 struct io_facility std_in;
1228 /* IO-Facility for writing to stdout */
1229 struct io_facility std_out;
1231 HANDLE parent_std_in_handle = GetStdHandle (STD_INPUT_HANDLE);
1232 HANDLE parent_std_out_handle = GetStdHandle (STD_OUTPUT_HANDLE);
1235 /* we do this HERE and not beforehand (in init_tun()), in contrast to openvpn
1236 * to remove the need to flush the arp cache, handle DHCP and wrong IPs.
1238 * DHCP and such are all features we will never use in gnunet afaik.
1239 * But for openvpn those are essential.
1241 if (!tun_up (tap_handle))
1244 /* Initialize our overlapped IO structures*/
1245 if (!(initialize_io_facility (&tap_read, IOSTATE_READY, FALSE)
1246 && initialize_io_facility (&tap_write, IOSTATE_WAITING, TRUE)
1247 && initialize_io_facility (&std_in, IOSTATE_READY, FALSE)
1248 && initialize_io_facility (&std_out, IOSTATE_WAITING, TRUE)))
1249 goto teardown_final;
1251 /* Handles for STDIN and STDOUT */
1252 tap_read.handle = tap_handle;
1253 tap_write.handle = tap_handle;
1256 * Find out the types of our handles.
1257 * This part is a problem, because in windows we need to handle files,
1258 * pipes and the console differently.
1260 if (FILE_TYPE_PIPE != GetFileType (parent_std_in_handle) ||
1261 FILE_TYPE_PIPE != GetFileType (parent_std_out_handle))
1263 fprintf (stderr, "ERROR: stdin/stdout must be named pipes!\n");
1267 std_in.handle = ReOpenFile (parent_std_in_handle,
1269 FILE_SHARE_WRITE | FILE_SHARE_READ,
1270 FILE_FLAG_OVERLAPPED);
1272 if (INVALID_HANDLE_VALUE == std_in.handle)
1274 fprintf (stderr, "FATAL: Could not reopen stdin for in overlapped mode, has to be a named pipe!\n");
1278 std_out.handle = ReOpenFile (parent_std_out_handle,
1281 FILE_FLAG_OVERLAPPED);
1283 if (INVALID_HANDLE_VALUE == std_out.handle)
1285 fprintf (stderr, "FATAL: Could not reopen stdout for in overlapped mode, has to be a named pipe!\n");
1289 while (std_out.path_open || tap_write.path_open)
1291 /* perform READ from stdin if possible */
1292 if (std_in.path_open && tap_write.path_open && (!attempt_read_stdin (&std_in, &tap_write)))
1295 /* perform READ from tap if possible */
1296 if (tap_read.path_open && std_out.path_open && (!attempt_read_tap (&tap_read, &std_out)))
1299 /* perform WRITE to tap if possible */
1300 if (tap_write.path_open && (!attempt_write (&tap_write, &std_in)))
1303 /* perform WRITE to STDOUT if possible */
1304 if (std_out.path_open && (!attempt_write (&std_out, &tap_read)))
1310 CancelIo (tap_handle);
1311 CancelIo (std_in.handle);
1312 CancelIo (std_out.handle);
1316 CloseHandle (tap_handle);
1321 * Open VPN tunnel interface.
1323 * @param argc must be 6
1324 * @param argv 0: binary name (gnunet-helper-vpn)
1325 * 1: tunnel interface name (gnunet-vpn)
1326 * 2: IPv6 address (::1), "-" to disable
1327 * 3: IPv6 netmask length in bits (64), ignored if #2 is "-"
1328 * 4: IPv4 address (1.2.3.4), "-" to disable
1329 * 5: IPv4 netmask (255.255.0.0), ignored if #4 is "-"
1332 main (int argc, char **argv)
1334 char hwid[LINE_LEN];
1337 boolean have_ip4 = FALSE;
1338 boolean have_ip6 = FALSE;
1342 fprintf (stderr, "FATAL: must supply 5 arguments!\n");
1346 strncpy (hwid, argv[1], LINE_LEN);
1347 hwid[LINE_LEN - 1] = '\0';
1350 * We use our PID for finding/resolving the control-panel name of our virtual
1351 * device. PIDs are (of course) unique at runtime, thus we can safely use it
1352 * as additional hardware-id for our device.
1354 snprintf (secondary_hwid, LINE_LEN / 2, "%s-%d",
1358 if (INVALID_HANDLE_VALUE == (handle = init_tun ()))
1360 fprintf (stderr, "FATAL: could not initialize virtual-interface %s with IPv6 %s/%s and IPv4 %s/%s\n",
1370 if (0 != strcmp (argv[2], "-"))
1372 const char *address = argv[2];
1373 long prefix_len = atol (argv[3]);
1375 if ((prefix_len < 1) || (prefix_len > 127))
1377 fprintf (stderr, "FATAL: prefix_len out of range\n");
1382 if (0 != (global_ret = set_address6 (address, prefix_len)))
1388 if (0 != strcmp (argv[4], "-"))
1390 const char *address = argv[4];
1391 const char *mask = argv[5];
1393 if (0 != (global_ret = set_address4 (address, mask)))
1405 const char *address = argv[4];
1406 remove_address4 (address);
1410 const char *address = argv[2];
1411 remove_address6 (address);
1413 remove_interface ();