X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fexit%2Fgnunet-helper-exit-windows.c;h=b8ea198afc9d839afc473b67fd2dd371b0cd59d0;hb=ec50a665dc884f7997419d0351ae8ade9c1aeabe;hp=2b99134cc2cf113f7155346ca584981b56fb155b;hpb=d76ff8424ecaa8a42939adf2975a6429870902f1;p=oweals%2Fgnunet.git diff --git a/src/exit/gnunet-helper-exit-windows.c b/src/exit/gnunet-helper-exit-windows.c index 2b99134cc..b8ea198af 100644 --- a/src/exit/gnunet-helper-exit-windows.c +++ b/src/exit/gnunet-helper-exit-windows.c @@ -1,32 +1,51 @@ /* This file is part of GNUnet. - (C) 2010, 2012 Christian Grothoff + Copyright (C) 2010, 2012 Christian Grothoff - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ +/** + * @file exit/gnunet-helper-exit-windows.c + * @brief the helper for the EXIT service in win32 builds. + * Opens a virtual network-interface, sends data received on the if to stdout, + * sends data received on stdin to the interface + * @author Christian M. Fuchs + * + * The following list of people have reviewed this code and considered + * it safe since the last modification (if you reviewed it, please + * have your name added to the list): + * */ #include +#include #include #include +#ifndef __MINGW64_VERSION_MAJOR #include #include -#include +#else +#include +#include +#endif #include #include "platform.h" #include "tap-windows.h" +/** + * Need 'struct GNUNET_HashCode' and 'struct GNUNET_PeerIdentity'. + */ +#include "gnunet_crypto_lib.h" /** * Need 'struct GNUNET_MessageHeader'. */ @@ -50,9 +69,13 @@ #define LOG_DEBUG(msg) do {} while (0) #endif +/** + * Will this binary be run in permissions testing mode? + */ +static boolean privilege_testing = FALSE; /** - * Maximum size of a GNUnet message (GNUNET_SERVER_MAX_MESSAGE_SIZE) + * Maximum size of a GNUnet message (GNUNET_MAX_MESSAGE_SIZE) */ #define MAX_SIZE 65536 @@ -60,16 +83,16 @@ * Name or Path+Name of our win32 driver. * The .sys and .cat files HAVE to be in the same location as this file! */ -#define INF_FILE "share/gnunet/tapw32/OemWin2k.inf" +#define INF_FILE "share/gnunet/openvpn-tap32/tapw32/OemWin2k.inf" /** * Name or Path+Name of our win64 driver. * The .sys and .cat files HAVE to be in the same location as this file! */ -#define INF_FILE64 "share/gnunet/tapw64/OemWin2k.inf" +#define INF_FILE64 "share/gnunet/openvpn-tap32/tapw64/OemWin2k.inf" /** - * Hardware ID used in the inf-file. + * Hardware ID used in the inf-file. * This might change over time, as openvpn advances their driver */ #define HARDWARE_ID "tap0901" @@ -80,7 +103,7 @@ #define TAP_WIN_MIN_MAJOR 9 /** - * Minimum minor-id of the driver version we can work with. + * Minimum minor-id of the driver version we can work with. * v <= 7 has buggy IPv6. * v == 8 is broken for small IPv4 Packets */ @@ -88,7 +111,7 @@ /** * Time in seconds to wait for our virtual device to go up after telling it to do so. - * + * * openvpn doesn't specify a value, 4 seems sane for testing, even for openwrt * (in fact, 4 was chosen by a fair dice roll...) */ @@ -100,7 +123,7 @@ #define INTERFACE_REGISTRY_LOCATION "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}" /** - * Our local process' PID. Used for creating a sufficiently unique additional + * Our local process' PID. Used for creating a sufficiently unique additional * hardware ID for our device. */ static char secondary_hwid[LINE_LEN / 2]; @@ -111,13 +134,13 @@ static char secondary_hwid[LINE_LEN / 2]; */ static char device_visible_name[256]; -/** +/** * This is our own local instance of a virtual network interface * It is (somewhat) equivalent to using tun/tap in unixoid systems - * + * * Upon initialization, we create such an device node. * Upon termination, we remove it again. - * + * * If we crash this device might stay around. */ static HDEVINFO DeviceInfo = INVALID_HANDLE_VALUE; @@ -128,7 +151,7 @@ static HDEVINFO DeviceInfo = INVALID_HANDLE_VALUE; static SP_DEVINFO_DATA DeviceNode; /** - * GUID of our virtual device in the form of + * GUID of our virtual device in the form of * {12345678-1234-1234-1234-123456789abc} - in hex */ static char device_guid[256]; @@ -140,36 +163,36 @@ static char device_guid[256]; enum IO_State { - /** - * overlapped I/O is ready for work + /** + * overlapped I/O is ready for work */ IOSTATE_READY = 0, - /** - * overlapped I/O has been queued + /** + * overlapped I/O has been queued */ IOSTATE_QUEUED, - /** - * overlapped I/O has finished, but is waiting for it's write-partner + /** + * overlapped I/O has finished, but is waiting for it's write-partner */ - IOSTATE_WAITING, - - /** + IOSTATE_WAITING, + + /** * there is a full buffer waiting */ IOSTATE_RESUME, - /** + /** * Operlapped IO states for facility objects - * overlapped I/O has failed, stop processing + * overlapped I/O has failed, stop processing */ - IOSTATE_FAILED + IOSTATE_FAILED }; -/** +/** * A IO Object + read/writebuffer + buffer-size for windows asynchronous IO handling */ struct io_facility @@ -205,7 +228,12 @@ struct io_facility DWORD buffer_size; /** - * Amount of data written, is compared to buffer_size. + * Amount of data actually written or read by readfile/writefile. + */ + DWORD buffer_size_processed; + + /** + * How much of this buffer we have written in total */ DWORD buffer_size_written; }; @@ -222,32 +250,32 @@ typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); /** * Determines if the host OS is win32 or win64 - * - * @return true if + * + * @return true if */ BOOL is_win64 () { #if defined(_WIN64) - //this is a win64 binary, - return TRUE; + //this is a win64 binary, + return TRUE; #elif defined(_WIN32) //this is a 32bit binary, and we need to check if we are running in WOW64 BOOL success = FALSE; BOOL on_wow64 = FALSE; LPFN_ISWOW64PROCESS IsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandle ("kernel32"), "IsWow64Process"); - + if (NULL != IsWow64Process) success = IsWow64Process (GetCurrentProcess (), &on_wow64); - + return success && on_wow64; #endif } /** * Wrapper for executing a shellcommand in windows. - * + * * @param command - the command + parameters to execute - * @return * exitcode of the program executed, + * @return * exitcode of the program executed, * * EINVAL (cmd/file not found) * * EPIPE (could not read STDOUT) */ @@ -317,9 +345,7 @@ set_address6 (const char *address, unsigned long prefix_len) /** * @brief Removes the IPv6-Address given in address from the interface dev * - * @param dev the interface to remove * @param address the IPv4-Address - * @param mask the netmask */ static void remove_address6 (const char *address) @@ -333,7 +359,7 @@ remove_address6 (const char *address) */ snprintf (command, LINE_LEN, "netsh interface ipv6 delete address \"%s\" store=persistent", - device_visible_name, address); + device_visible_name); /* * Set the address */ @@ -348,7 +374,6 @@ remove_address6 (const char *address) /** * @brief Sets the IPv4-Address given in address on the interface dev * - * @param dev the interface to configure * @param address the IPv4-Address * @param mask the netmask */ @@ -370,7 +395,7 @@ set_address4 (const char *address, const char *mask) strerror (errno)); return -1; } - // Set Device to Subnet-Mode? + // Set Device to Subnet-Mode? // do we really need tun.c:2925 ? /* @@ -394,9 +419,7 @@ set_address4 (const char *address, const char *mask) /** * @brief Removes the IPv4-Address given in address from the interface dev * - * @param dev the interface to remove * @param address the IPv4-Address - * @param mask the netmask */ static void remove_address4 (const char *address) @@ -411,7 +434,7 @@ remove_address4 (const char *address) */ snprintf (command, LINE_LEN, "netsh interface ipv4 delete address \"%s\" gateway=all store=persistent", - device_visible_name, address); + device_visible_name); /* * Set the address */ @@ -424,8 +447,8 @@ remove_address4 (const char *address) /** - * Setup a new virtual interface to use for tunneling. - * + * Setup a new virtual interface to use for tunneling. + * * @return: TRUE if setup was successful, else FALSE */ static BOOL @@ -433,7 +456,7 @@ setup_interface () { /* * where to find our inf-file. (+ the "full" path, after windows found") - * + * * We do not directly input all the props here, because openvpn will update * these details over time. */ @@ -444,22 +467,22 @@ setup_interface () GUID class_guid; int str_length = 0; - /** + /** * Set the device's hardware ID and add it to a list. - * This information will later on identify this device in registry. + * This information will later on identify this device in registry. */ strncpy (hwidlist, HARDWARE_ID, LINE_LEN); /** - * this is kind of over-complicated, but allows keeps things independent of - * how the openvpn-hwid is actually stored. - * + * this is kind of over-complicated, but allows keeps things independent of + * how the openvpn-hwid is actually stored. + * * A HWID list is double-\0 terminated and \0 separated */ str_length = strlen (hwidlist) + 1; strncpy (&hwidlist[str_length], secondary_hwid, LINE_LEN); str_length += strlen (&hwidlist[str_length]) + 1; - - /** + + /** * Locate the inf-file, we need to store it somewhere where the system can * find it. We need to pick the correct driver for win32/win64. */ @@ -469,7 +492,7 @@ setup_interface () GetFullPathNameA (INF_FILE, MAX_PATH, inf_file_path, &temp_inf_filename); fprintf (stderr, "INFO: Located our driver's .inf file at %s\n", inf_file_path); - /** + /** * Bootstrap our device info using the drivers inf-file */ if ( ! SetupDiGetINFClassA (inf_file_path, @@ -478,9 +501,9 @@ setup_interface () NULL)) return FALSE; - /** - * Collect all the other needed information... - * let the system fill our this form + /** + * Collect all the other needed information... + * let the system fill our this form */ DeviceInfo = SetupDiCreateDeviceInfoList (&class_guid, NULL); if (DeviceInfo == INVALID_HANDLE_VALUE) @@ -525,9 +548,9 @@ setup_interface () /** - * Remove our new virtual interface to use for tunneling. + * Remove our new virtual interface to use for tunneling. * This function must be called AFTER setup_interface! - * + * * @return: TRUE if destruction was successful, else FALSE */ static BOOL @@ -543,7 +566,7 @@ remove_interface () remove.Scope = DI_REMOVEDEVICE_GLOBAL; remove.ClassInstallHeader.InstallFunction = DIF_REMOVE; /* - * 1. Prepare our existing device information set, and place the + * 1. Prepare our existing device information set, and place the * uninstall related information into the structure */ if ( ! SetupDiSetClassInstallParamsA (DeviceInfo, @@ -560,7 +583,7 @@ remove_interface () return FALSE; SetupDiDestroyDeviceInfoList (DeviceInfo); - + fprintf (stderr, "DEBUG: removed interface successfully\n"); return TRUE; @@ -569,8 +592,8 @@ remove_interface () /** * Do all the lookup necessary to retrieve the inteface's actual name - * off the registry. - * + * off the registry. + * * @return: TRUE if we were able to lookup the interface's name, else FALSE */ static BOOL @@ -594,7 +617,7 @@ resolve_interface_name () 0, //must be 0 NULL)) //hMachine, we are local return FALSE; - + fprintf (stderr, "DEBUG: Resolving interface name for network device %s\n",pnp_instance_id); /* Registry is incredibly slow, retry for up to 30 seconds to allow registry to refresh */ @@ -612,7 +635,7 @@ resolve_interface_name () &adapter_key_handle)) return FALSE; - /* Of course there is a multitude of entries here, with arbitrary names, + /* Of course there is a multitude of entries here, with arbitrary names, * thus we need to iterate through there. */ while (!retval) @@ -637,7 +660,7 @@ resolve_interface_name () NULL, NULL); - /* this may fail due to one of two reasons: + /* this may fail due to one of two reasons: * we are at the end of the list*/ if (ERROR_NO_MORE_ITEMS == status) break; @@ -688,8 +711,8 @@ resolve_interface_name () if (status != ERROR_SUCCESS || data_type != REG_SZ) goto cleanup; - /* - * we have successfully found OUR instance, + /* + * we have successfully found OUR instance, * save the device GUID before exiting */ @@ -711,7 +734,7 @@ cleanup: /** * Determines the version of the installed TAP32 driver and checks if it's sufficiently new for GNUNET - * + * * @param handle the handle to our tap device * @return TRUE if the version is sufficient, else FALSE */ @@ -737,7 +760,7 @@ check_tapw32_version (HANDLE handle) TAP_WIN_MIN_MINOR); return FALSE; } - + return TRUE; } @@ -803,8 +826,8 @@ init_tun () /** * Brings a TAP device up and sets it to connected state. - * - * @param handle the handle to our TAP device + * + * @param handle the handle to our TAP device * @return True if the operation succeeded, else false */ static BOOL @@ -830,25 +853,25 @@ tun_up (HANDLE handle) /** * Attempts to read off an input facility (tap or named pipe) in overlapped mode. - * - * 1. + * + * 1. * If the input facility is in IOSTATE_READY, it will issue a new read operation to the - * input handle. Then it goes into IOSTATE_QUEUED state. + * input handle. Then it goes into IOSTATE_QUEUED state. * In case the read succeeded instantly the input facility enters 3. - * - * 2. + * + * 2. * If the input facility is in IOSTATE_QUEUED state, it will check if the queued read has finished already. * If it has finished, go to state 3. * If it has failed, set IOSTATE_FAILED - * + * * 3. * If the output facility is in state IOSTATE_READY, the read-buffer is copied to the output buffer. * The input facility enters state IOSTATE_READY * The output facility enters state IOSTATE_READY * If the output facility is in state IOSTATE_QUEUED, the input facility enters IOSTATE_WAITING - * + * * IOSTATE_WAITING is reset by the output facility, once it has completed. - * + * * @param input_facility input named pipe or file to work with. * @param output_facility output pipe or file to hand over data to. * @return false if an event reset was impossible (OS error), else true @@ -859,11 +882,11 @@ attempt_read_tap (struct io_facility * input_facility, { struct GNUNET_MessageHeader * hdr; unsigned short size; - + switch (input_facility->facility_state) { case IOSTATE_READY: - { + { if (! ResetEvent (input_facility->overlapped.hEvent)) { return FALSE; @@ -882,9 +905,9 @@ attempt_read_tap (struct io_facility * input_facility, /* reset event manually*/ if (! SetEvent (input_facility->overlapped.hEvent)) return FALSE; - + fprintf (stderr, "DEBUG: tap read succeeded immediately\n"); - + /* we successfully read something from the TAP and now need to * send it our via STDOUT. Is that possible at the moment? */ if ((IOSTATE_READY == output_facility->facility_state || @@ -893,8 +916,8 @@ attempt_read_tap (struct io_facility * input_facility, { /* hand over this buffers content and apply message header for gnunet */ hdr = (struct GNUNET_MessageHeader *) output_facility->buffer; size = input_facility->buffer_size + sizeof (struct GNUNET_MessageHeader); - - memcpy (output_facility->buffer + sizeof (struct GNUNET_MessageHeader), + + GNUNET_memcpy (output_facility->buffer + sizeof (struct GNUNET_MessageHeader), input_facility->buffer, input_facility->buffer_size); @@ -904,10 +927,8 @@ attempt_read_tap (struct io_facility * input_facility, output_facility->facility_state = IOSTATE_READY; } else if (0 < input_facility->buffer_size) - { /* If we have have read our buffer, wait for our write-partner*/ + /* If we have have read our buffer, wait for our write-partner*/ input_facility->facility_state = IOSTATE_WAITING; - // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish? - } } else /* operation was either queued or failed*/ { @@ -942,7 +963,7 @@ attempt_read_tap (struct io_facility * input_facility, return FALSE; fprintf (stderr, "DEBUG: tap read succeeded delayed\n"); - + /* we successfully read something from the TAP and now need to * send it our via STDOUT. Is that possible at the moment? */ if ((IOSTATE_READY == output_facility->facility_state || @@ -951,8 +972,8 @@ attempt_read_tap (struct io_facility * input_facility, { /* hand over this buffers content and apply message header for gnunet */ hdr = (struct GNUNET_MessageHeader *) output_facility->buffer; size = input_facility->buffer_size + sizeof (struct GNUNET_MessageHeader); - - memcpy (output_facility->buffer + sizeof (struct GNUNET_MessageHeader), + + GNUNET_memcpy (output_facility->buffer + sizeof (struct GNUNET_MessageHeader), input_facility->buffer, input_facility->buffer_size); @@ -986,7 +1007,7 @@ attempt_read_tap (struct io_facility * input_facility, hdr = (struct GNUNET_MessageHeader *) output_facility->buffer; size = input_facility->buffer_size + sizeof (struct GNUNET_MessageHeader); - memcpy (output_facility->buffer + sizeof (struct GNUNET_MessageHeader), + GNUNET_memcpy (output_facility->buffer + sizeof (struct GNUNET_MessageHeader), input_facility->buffer, input_facility->buffer_size); @@ -1004,26 +1025,26 @@ attempt_read_tap (struct io_facility * input_facility, /** * Attempts to read off an input facility (tap or named pipe) in overlapped mode. - * - * 1. + * + * 1. * If the input facility is in IOSTATE_READY, it will issue a new read operation to the - * input handle. Then it goes into IOSTATE_QUEUED state. + * input handle. Then it goes into IOSTATE_QUEUED state. * In case the read succeeded instantly the input facility enters 3. - * - * 2. + * + * 2. * If the input facility is in IOSTATE_QUEUED state, it will check if the queued read has finished already. * If it has finished, go to state 3. * If it has failed, set IOSTATE_FAILED - * + * * 3. * If the facility is finished with ready * The read-buffer is copied to the output buffer, except for the GNUNET_MessageHeader. * The input facility enters state IOSTATE_READY * The output facility enters state IOSTATE_READY * If the output facility is in state IOSTATE_QUEUED, the input facility enters IOSTATE_WAITING - * + * * IOSTATE_WAITING is reset by the output facility, once it has completed. - * + * * @param input_facility input named pipe or file to work with. * @param output_facility output pipe or file to hand over data to. * @return false if an event reset was impossible (OS error), else true @@ -1033,31 +1054,33 @@ attempt_read_stdin (struct io_facility * input_facility, struct io_facility * output_facility) { struct GNUNET_MessageHeader * hdr; - BOOL status; + switch (input_facility->facility_state) { case IOSTATE_READY: { + input_facility->buffer_size = 0; + +partial_read_iostate_ready: if (! ResetEvent (input_facility->overlapped.hEvent)) return FALSE; - input_facility->buffer_size = 0; - status = ReadFile (input_facility->handle, - input_facility->buffer, - sizeof (input_facility->buffer), - &input_facility->buffer_size, - &input_facility->overlapped); /* Check how the task is handled */ - if (status && (sizeof (struct GNUNET_MessageHeader) < input_facility->buffer_size)) + if (ReadFile (input_facility->handle, + input_facility->buffer + input_facility->buffer_size, + sizeof (input_facility->buffer) - input_facility->buffer_size, + &input_facility->buffer_size_processed, + &input_facility->overlapped)) {/* async event processed immediately*/ hdr = (struct GNUNET_MessageHeader *) input_facility->buffer; /* reset event manually*/ - if (! SetEvent (input_facility->overlapped.hEvent)) + if (!SetEvent (input_facility->overlapped.hEvent)) return FALSE; fprintf (stderr, "DEBUG: stdin read succeeded immediately\n"); - + input_facility->buffer_size += input_facility->buffer_size_processed; + if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER || ntohs (hdr->size) > sizeof (input_facility->buffer)) { @@ -1065,44 +1088,36 @@ attempt_read_stdin (struct io_facility * input_facility, input_facility->facility_state = IOSTATE_READY; return TRUE; } - if (ntohs (hdr->size) > input_facility->buffer_size); - // TODO: add support for partial read - - /* we successfully read something from the TAP and now need to + /* we got the a part of a packet */ + if (ntohs (hdr->size) > input_facility->buffer_size) + goto partial_read_iostate_ready; + + /* have we read more than 0 bytes of payload? (sizeread > header)*/ + if (input_facility->buffer_size > sizeof (struct GNUNET_MessageHeader) && + ((IOSTATE_READY == output_facility->facility_state) || + (IOSTATE_WAITING == output_facility->facility_state))) + {/* we successfully read something from the TAP and now need to * send it our via STDOUT. Is that possible at the moment? */ - if (sizeof (struct GNUNET_MessageHeader) < input_facility->buffer_size) - { - if (IOSTATE_READY == output_facility->facility_state || - IOSTATE_WAITING == output_facility->facility_state) - { - /* hand over this buffers content and strip gnunet message header */ - memcpy (output_facility->buffer, - input_facility->buffer + sizeof (struct GNUNET_MessageHeader), - input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader)); - output_facility->buffer_size = input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader); - output_facility->facility_state = IOSTATE_READY; - - } - else if (IOSTATE_QUEUED == output_facility->facility_state) - /* If we have have read our buffer, wait for our write-partner*/ - input_facility->facility_state = IOSTATE_WAITING; - // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish? - } - } - else if (status && 0 >= input_facility->buffer_size) - { - if (! SetEvent (input_facility->overlapped.hEvent)) - return FALSE; - input_facility->facility_state = IOSTATE_READY; + /* hand over this buffers content and strip gnunet message header */ + GNUNET_memcpy (output_facility->buffer, + input_facility->buffer + sizeof (struct GNUNET_MessageHeader), + input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader)); + output_facility->buffer_size = input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader); + output_facility->facility_state = IOSTATE_READY; + input_facility->facility_state = IOSTATE_READY; + } + else if (input_facility->buffer_size > sizeof (struct GNUNET_MessageHeader)) + /* If we have have read our buffer, wait for our write-partner*/ + input_facility->facility_state = IOSTATE_WAITING; + else /* we read nothing */ + input_facility->facility_state = IOSTATE_READY; } else /* operation was either queued or failed*/ { int err = GetLastError (); - if (ERROR_IO_PENDING == err) - { /* operation queued */ + if (ERROR_IO_PENDING == err) /* operation queued */ input_facility->facility_state = IOSTATE_QUEUED; - } else { /* error occurred, let the rest of the elements finish */ input_facility->path_open = FALSE; @@ -1121,16 +1136,17 @@ attempt_read_stdin (struct io_facility * input_facility, // there was an operation going on already, check if that has completed now. if (GetOverlappedResult (input_facility->handle, &input_facility->overlapped, - &input_facility->buffer_size, + &input_facility->buffer_size_processed, FALSE)) {/* successful return for a queued operation */ hdr = (struct GNUNET_MessageHeader *) input_facility->buffer; - + if (! ResetEvent (input_facility->overlapped.hEvent)) return FALSE; - + fprintf (stderr, "DEBUG: stdin read succeeded delayed\n"); - + input_facility->buffer_size += input_facility->buffer_size_processed; + if ((ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER) || (ntohs (hdr->size) > sizeof (input_facility->buffer))) { @@ -1138,28 +1154,26 @@ attempt_read_stdin (struct io_facility * input_facility, input_facility->facility_state = IOSTATE_READY; return TRUE; } + /* we got the a part of a packet */ if (ntohs (hdr->size) > input_facility->buffer_size ); - // TODO: add support for partial read + goto partial_read_iostate_ready; /* we successfully read something from the TAP and now need to * send it our via STDOUT. Is that possible at the moment? */ if ((IOSTATE_READY == output_facility->facility_state || IOSTATE_WAITING == output_facility->facility_state) - && sizeof(struct GNUNET_MessageHeader) < input_facility->buffer_size) + && input_facility->buffer_size > sizeof(struct GNUNET_MessageHeader)) { /* hand over this buffers content and strip gnunet message header */ - memcpy (output_facility->buffer, + GNUNET_memcpy (output_facility->buffer, input_facility->buffer + sizeof(struct GNUNET_MessageHeader), input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader)); output_facility->buffer_size = input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader); output_facility->facility_state = IOSTATE_READY; input_facility->facility_state = IOSTATE_READY; } - else if (sizeof(struct GNUNET_MessageHeader) < input_facility->buffer_size) - { /* If we have have read our buffer, wait for our write-partner*/ - input_facility->facility_state = IOSTATE_WAITING; - // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish? - } - else if (sizeof(struct GNUNET_MessageHeader) >= input_facility->buffer_size) + else if (input_facility->buffer_size > sizeof(struct GNUNET_MessageHeader)) + input_facility->facility_state = IOSTATE_WAITING; + else input_facility->facility_state = IOSTATE_READY; } else @@ -1177,7 +1191,7 @@ attempt_read_stdin (struct io_facility * input_facility, } return TRUE; case IOSTATE_RESUME: /* Our buffer was filled already but our write facility was busy. */ - memcpy (output_facility->buffer, + GNUNET_memcpy (output_facility->buffer, input_facility->buffer + sizeof (struct GNUNET_MessageHeader), input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader)); output_facility->buffer_size = input_facility->buffer_size - sizeof (struct GNUNET_MessageHeader); @@ -1194,7 +1208,7 @@ attempt_read_stdin (struct io_facility * input_facility, * Attempts to write to an output facility (tap or named pipe) in overlapped mode. * * TODO: high level description - * + * * @param output_facility output pipe or file to hand over data to. * @param input_facility input named pipe or file to work with. * @return false if an event reset was impossible (OS error), else true @@ -1203,37 +1217,36 @@ static BOOL attempt_write (struct io_facility * output_facility, struct io_facility * input_facility) { - BOOL status; - switch (output_facility->facility_state) { case IOSTATE_READY: + output_facility->buffer_size_written = 0; +continue_partial_write: if (! ResetEvent (output_facility->overlapped.hEvent)) return FALSE; - output_facility->buffer_size_written = 0; - status = WriteFile (output_facility->handle, - output_facility->buffer, - output_facility->buffer_size, - &output_facility->buffer_size_written, - &output_facility->overlapped); - /* Check how the task was handled */ - if (status && - output_facility->buffer_size_written == output_facility->buffer_size) + if (WriteFile (output_facility->handle, + output_facility->buffer + output_facility->buffer_size_written, + output_facility->buffer_size - output_facility->buffer_size_written, + &output_facility->buffer_size_processed, + &output_facility->overlapped)) {/* async event processed immediately*/ fprintf (stderr, "DEBUG: write succeeded immediately\n"); - + output_facility->buffer_size_written += output_facility->buffer_size_processed; + /* reset event manually*/ if (! SetEvent (output_facility->overlapped.hEvent)) return FALSE; + /* partial write */ + if (output_facility->buffer_size_written < output_facility->buffer_size) + goto continue_partial_write; + /* we are now waiting for our buffer to be filled*/ output_facility->facility_state = IOSTATE_WAITING; - output_facility->buffer_size = 0; - output_facility->buffer_size_written = 0; /* we successfully wrote something and now need to reset our reader */ if (IOSTATE_WAITING == input_facility->facility_state) @@ -1258,23 +1271,25 @@ attempt_write (struct io_facility * output_facility, return TRUE; case IOSTATE_QUEUED: // there was an operation going on already, check if that has completed now. - status = GetOverlappedResult (output_facility->handle, + + if (GetOverlappedResult (output_facility->handle, &output_facility->overlapped, - &output_facility->buffer_size_written, - FALSE); - if (status && - output_facility->buffer_size_written == output_facility->buffer_size) + &output_facility->buffer_size_processed, + FALSE)) {/* successful return for a queued operation */ if (! ResetEvent (output_facility->overlapped.hEvent)) return FALSE; - + fprintf (stderr, "DEBUG: write succeeded delayed\n"); + output_facility->buffer_size_written += output_facility->buffer_size_processed; + + /* partial write */ + if (output_facility->buffer_size_written < output_facility->buffer_size) + goto continue_partial_write; /* we are now waiting for our buffer to be filled*/ output_facility->facility_state = IOSTATE_WAITING; - output_facility->buffer_size = 0; - output_facility->buffer_size_written = 0; - + /* we successfully wrote something and now need to reset our reader */ if (IOSTATE_WAITING == input_facility->facility_state) input_facility->facility_state = IOSTATE_RESUME; @@ -1291,7 +1306,7 @@ attempt_write (struct io_facility * output_facility, fprintf (stderr, "FATAL: Write to handle failed, exiting\n"); } } - default: + default: return TRUE; } } @@ -1299,7 +1314,7 @@ attempt_write (struct io_facility * output_facility, /** * Initialize a overlapped structure - * + * * @param elem the element to initilize * @param initial_state the initial state for this instance * @param signaled if the hEvent created should default to signaled or not @@ -1325,7 +1340,7 @@ initialize_io_facility (struct io_facility * elem, /** * Start forwarding to and from the tunnel. * - * @param fd_tun tunnel FD + * @param tap_handle device handle for interacting with the Virtual interface */ static void run (HANDLE tap_handle) @@ -1345,12 +1360,12 @@ run (HANDLE tap_handle) /* tun up: */ /* we do this HERE and not beforehand (in init_tun()), in contrast to openvpn * to remove the need to flush the arp cache, handle DHCP and wrong IPs. - * + * * DHCP and such are all features we will never use in gnunet afaik. * But for openvpn those are essential. */ - if (! tun_up (tap_handle)) - return; + if ((privilege_testing) || (! tun_up (tap_handle) )) + goto teardown_final; /* Initialize our overlapped IO structures*/ if (! (initialize_io_facility (&tap_read, IOSTATE_READY, FALSE) @@ -1367,12 +1382,12 @@ run (HANDLE tap_handle) /* Debug output to console STDIN/STDOUT*/ std_in.handle = parent_std_in_handle; std_out.handle = parent_std_out_handle; - + #else fprintf (stderr, "DEBUG: reopening stdin/out for overlapped IO\n"); - /* - * Find out the types of our handles. - * This part is a problem, because in windows we need to handle files, + /* + * Find out the types of our handles. + * This part is a problem, because in windows we need to handle files, * pipes and the console differently. */ if ((FILE_TYPE_PIPE != GetFileType (parent_std_in_handle)) || @@ -1404,9 +1419,9 @@ run (HANDLE tap_handle) goto teardown; } #endif - + fprintf (stderr, "DEBUG: mainloop has begun\n"); - + while (std_out.path_open || tap_write.path_open) { /* perform READ from stdin if possible */ @@ -1425,17 +1440,16 @@ run (HANDLE tap_handle) if (std_out.path_open && (! attempt_write (&std_out, &tap_read))) break; } + fprintf (stderr, "DEBUG: teardown initiated\n"); teardown: - fprintf (stderr, "DEBUG: teardown initiated\n"); - CancelIo (tap_handle); CancelIo (std_in.handle); CancelIo (std_out.handle); teardown_final: - + CloseHandle (tap_handle); } @@ -1444,34 +1458,48 @@ teardown_final: * Open VPN tunnel interface. * * @param argc must be 6 - * @param argv 0: binary name (gnunet-helper-vpn) - * 1: tunnel interface prefix (gnunet-vpn) - * 2: IPv6 address (::1), "-" to disable - * 3: IPv6 netmask length in bits (64), ignored if #2 is "-" - * 4: IPv4 address (1.2.3.4), "-" to disable - * 5: IPv4 netmask (255.255.0.0), ignored if #4 is "-" + * @param argv 0: binary name ("gnunet-helper-exit") + * 1: tunnel interface name ("gnunet-exit") + * 2: IPv4 "physical" interface name ("eth0"), or "-" to not do IPv4 NAT + * 3: IPv6 address ("::1"), or "-" to skip IPv6 + * 4: IPv6 netmask length in bits ("64") [ignored if #4 is "-"] + * 5: IPv4 address ("1.2.3.4"), or "-" to skip IPv4 + * 6: IPv4 netmask ("255.255.0.0") [ignored if #4 is "-"] */ int main (int argc, char **argv) { char hwid[LINE_LEN]; HANDLE handle; - int global_ret = 0; + int global_ret = 1; + int local_ret = EINVAL; BOOL have_ip4 = FALSE; BOOL have_ip6 = FALSE; + BOOL have_nat44 = FALSE; + + if ( (1 < argc) && (0 != strcmp (argv[1], "-d"))){ + privilege_testing = TRUE; + fprintf (stderr, + "%s", + "DEBUG: Running binary in privilege testing mode."); + argv++; + argc--; + } if (6 != argc) { - fprintf (stderr, "FATAL: must supply 5 arguments\nUsage:\ngnunet-helper-vpn \n", argv[0]); + fprintf (stderr, + "%s", + "FATAL: must supply 6 arguments\nUsage:\ngnunet-helper-exit [-d] \n"); return 1; } strncpy (hwid, argv[1], LINE_LEN); hwid[LINE_LEN - 1] = '\0'; - /* - * We use our PID for finding/resolving the control-panel name of our virtual - * device. PIDs are (of course) unique at runtime, thus we can safely use it + /* + * We use our PID for finding/resolving the control-panel name of our virtual + * device. PIDs are (of course) unique at runtime, thus we can safely use it * as additional hardware-id for our device. */ snprintf (secondary_hwid, LINE_LEN / 2, "%s-%d", @@ -1482,19 +1510,20 @@ main (int argc, char **argv) { fprintf (stderr, "FATAL: could not initialize virtual-interface %s with IPv6 %s/%s and IPv4 %s/%s\n", hwid, - argv[2], argv[3], argv[4], - argv[5]); + argv[5], + argv[6]); global_ret = -1; goto cleanup; } fprintf (stderr, "DEBUG: Setting IPs, if needed\n"); - if (0 != strcmp (argv[2], "-")) + if (0 != strcmp (argv[3], "-")) { - const char *address = argv[2]; - long prefix_len = atol (argv[3]); + char command[LINE_LEN]; + const char *address = argv[3]; + long prefix_len = atol (argv[4]); if ((prefix_len < 1) || (prefix_len > 127)) { @@ -1503,38 +1532,120 @@ main (int argc, char **argv) goto cleanup; } - fprintf (stderr, "DEBUG: Setting IP6 address: %s/%d\n",address,prefix_len); + fprintf (stderr, "DEBUG: Setting IP6 address: %s/%d\n", address, prefix_len); if (0 != (global_ret = set_address6 (address, prefix_len))) goto cleanup; have_ip6 = TRUE; + + /* install our the windows NAT module*/ + fprintf (stderr, "DEBUG: Setting IPv6 Forwarding for internal and external interface.\n"); + /* outside interface (maybe that's already set) */ + snprintf (command, LINE_LEN, + "netsh interface ipv6 set interface interface=\"%s\" metric=1 forwarding=enabled store=active", + argv[2]); + local_ret = execute_shellcommand (command); + if (0 != local_ret) + { + fprintf (stderr, "FATAL: Could not enable forwarding via netsh: %s\n", strerror (local_ret)); + goto cleanup; + } + /* internal interface */ + snprintf (command, LINE_LEN, + "netsh interface ipv6 set interface interface=\"%s\" metric=1 forwarding=enabled advertise=enabled store=active", + device_visible_name); + local_ret = execute_shellcommand (command); + if (0 != local_ret) + { + fprintf (stderr, "FATAL: Could not enable forwarding via netsh: %s\n", strerror (local_ret)); + goto cleanup; + } + /* we can keep IPv6 forwarding around, as all interfaces have + * their forwarding mode reset to false at bootup. */ } - if (0 != strcmp (argv[4], "-")) + if (0 != strcmp (argv[5], "-")) { - const char *address = argv[4]; - const char *mask = argv[5]; + const char *address = argv[5]; + const char *mask = argv[6]; - fprintf (stderr, "DEBUG: Setting IP4 address: %s/%s\n",address,mask); + fprintf (stderr, "DEBUG: Setting IP4 address: %s/%s\n", address, mask); if (0 != (global_ret = set_address4 (address, mask))) goto cleanup; + // setup NAPT, if possible + /* MS has REMOVED the routing/nat capabilities from Vista+, thus + * we can not setup NAT like in XP or on the server. Actually the + * the only feasible solution seems to be to use + * Internet Connection Sharing, which introduces a horde of problems + * such as sending out rogue-RAs on the external interface in an ipv6 + * network. + * Thus, below stuff ONLY works on + * WinXP SP3 + * Win Server 2003 SP1+ + * Win Server 2008 + * ... + */ have_ip4 = TRUE; + if (0 != strcmp (argv[2], "-")) + { + char command[LINE_LEN]; + + /* install our the windows NAT module*/ + fprintf (stderr, "DEBUG: Adding NAPT/Masquerading between external IF %s and mine.\n", argv[2]); + local_ret = execute_shellcommand ("netsh routing ip nat install"); + if (0 != local_ret) + { + fprintf (stderr, "FATAL: Could not install NAPT support via Netsh: %s\n", strerror (local_ret)); + goto cleanup; + } + /* external IF */ + snprintf (command, LINE_LEN, + "netsh routing ip nat add interface \"%s\" full", /*full = NAPT (addr+port)*/ + argv[2]); + local_ret = execute_shellcommand (command); + if (0 != local_ret) + { + fprintf (stderr, "FATAL: IPv4-NAPT on external interface failed: %s\n", strerror (local_ret)); + goto cleanup; + } + /* private/internal/virtual IF */ + snprintf (command, LINE_LEN, + "netsh routing ip nat add interface \"%s\" private", + device_visible_name); + local_ret = execute_shellcommand (command); + if (0 != local_ret) + { + fprintf (stderr, "FATAL: IPv4-NAPT on internal interface failed: %s\n", strerror (local_ret)); + goto cleanup; + + have_nat44 = TRUE; + } + } } run (handle); - global_ret = 0; cleanup: - if (have_ip4) - { - const char *address = argv[4]; - fprintf (stderr, "DEBUG: Removing IP4 address\n"); + if (have_ip4) { + const char *address = argv[5]; + if (have_nat44) { + char command[LINE_LEN]; + fprintf(stderr, "DEBUG: removing IP4 NAPT from virtual interface \n"); + snprintf(command, LINE_LEN, + "netsh routing ip nat del interface \"%s\"", + device_visible_name); + local_ret = execute_shellcommand(command); + if (0 != local_ret) + fprintf(stderr, "WARNING: Could not remove IPv4-NAPT from internal interface, hopefully this will have no effect in future runs: %s\n", strerror(local_ret)); + } + + fprintf(stderr, "DEBUG: Removing IP4 address\n"); remove_address4 (address); - } + } if (have_ip6) { - const char *address = argv[2]; + const char *address = argv[3]; fprintf (stderr, "DEBUG: Removing IP6 address\n"); remove_address6 (address); }