summaryrefslogtreecommitdiffstats
path: root/win32.c
diff options
context:
space:
mode:
authorjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>2005-09-26 05:28:27 +0000
committerjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>2005-09-26 05:28:27 +0000
commit6fbf66fad3367b24fd6743bcd50254902fd9c8d5 (patch)
tree9802876e3771744eead18917bb47ff6e90ac39f5 /win32.c
downloadopenvpn-6fbf66fad3367b24fd6743bcd50254902fd9c8d5.tar.gz
openvpn-6fbf66fad3367b24fd6743bcd50254902fd9c8d5.tar.xz
openvpn-6fbf66fad3367b24fd6743bcd50254902fd9c8d5.zip
This is the start of the BETA21 branch.
It includes the --topology feature, and TAP-Win32 driver changes to allow non-admin access. git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@580 e7ae566f-a301-0410-adde-c780ea21d3b5
Diffstat (limited to 'win32.c')
-rwxr-xr-xwin32.c758
1 files changed, 758 insertions, 0 deletions
diff --git a/win32.c b/win32.c
new file mode 100755
index 0000000..f9a80fc
--- /dev/null
+++ b/win32.c
@@ -0,0 +1,758 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 this program (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Win32-specific OpenVPN code, targetted at the mingw
+ * development environment.
+ */
+
+#ifdef WIN32
+
+#include "config-win32.h"
+
+#include "syshead.h"
+#include "buffer.h"
+#include "error.h"
+#include "mtu.h"
+#include "sig.h"
+#include "win32.h"
+
+#include "memdbg.h"
+
+/*
+ * Windows internal socket API state (opaque).
+ */
+static struct WSAData wsa_state; /* GLOBAL */
+
+/*
+ * Should we call win32_pause() on program exit?
+ */
+static bool pause_exit_enabled = false; /* GLOBAL */
+
+/*
+ * win32_signal is used to get input from the keyboard
+ * if we are running in a console, or get input from an
+ * event object if we are running as a service.
+ */
+
+struct win32_signal win32_signal; /* GLOBAL */
+
+/*
+ * Save our old window title so we can restore
+ * it on exit.
+ */
+struct window_title window_title; /* GLOBAL*/
+
+/*
+ * Special global semaphore used to protect network
+ * shell commands from simultaneous instantiation.
+ */
+
+struct semaphore netcmd_semaphore; /* GLOBAL */
+
+void
+init_win32 (void)
+{
+ if (WSAStartup(0x0101, &wsa_state))
+ {
+ msg (M_ERR, "WSAStartup failed");
+ }
+ window_title_clear (&window_title);
+ win32_signal_clear (&win32_signal);
+ netcmd_semaphore_init ();
+}
+
+void
+uninit_win32 (void)
+{
+ netcmd_semaphore_close ();
+ if (pause_exit_enabled)
+ {
+ if (win32_signal.mode == WSO_MODE_UNDEF)
+ {
+ struct win32_signal w;
+ win32_signal_open (&w, WSO_FORCE_CONSOLE, NULL, false);
+ win32_pause (&w);
+ win32_signal_close (&w);
+ }
+ else
+ win32_pause (&win32_signal);
+ }
+ window_title_restore (&window_title);
+ win32_signal_close (&win32_signal);
+ WSACleanup ();
+}
+
+void
+set_pause_exit_win32 (void)
+{
+ pause_exit_enabled = true;
+}
+
+bool
+init_security_attributes_allow_all (struct security_attributes *obj)
+{
+ CLEAR (*obj);
+
+ obj->sa.nLength = sizeof (SECURITY_ATTRIBUTES);
+ obj->sa.lpSecurityDescriptor = &obj->sd;
+ obj->sa.bInheritHandle = FALSE;
+ if (!InitializeSecurityDescriptor (&obj->sd, SECURITY_DESCRIPTOR_REVISION))
+ return false;
+ if (!SetSecurityDescriptorDacl (&obj->sd, TRUE, NULL, FALSE))
+ return false;
+ return true;
+}
+
+void
+overlapped_io_init (struct overlapped_io *o,
+ const struct frame *frame,
+ BOOL event_state,
+ bool tuntap_buffer) /* if true: tuntap buffer, if false: socket buffer */
+{
+ CLEAR (*o);
+
+ /* manual reset event, initially set according to event_state */
+ o->overlapped.hEvent = CreateEvent (NULL, TRUE, event_state, NULL);
+ if (o->overlapped.hEvent == NULL)
+ msg (M_ERR, "Error: overlapped_io_init: CreateEvent failed");
+
+ /* allocate buffer for overlapped I/O */
+ alloc_buf_sock_tun (&o->buf_init, frame, tuntap_buffer, 0);
+}
+
+void
+overlapped_io_close (struct overlapped_io *o)
+{
+ if (o->overlapped.hEvent)
+ {
+ if (!CloseHandle (o->overlapped.hEvent))
+ msg (M_WARN | M_ERRNO, "Warning: CloseHandle failed on overlapped I/O event object");
+ }
+ free_buf (&o->buf_init);
+}
+
+char *
+overlapped_io_state_ascii (const struct overlapped_io *o)
+{
+ switch (o->iostate)
+ {
+ case IOSTATE_INITIAL:
+ return "0";
+ case IOSTATE_QUEUED:
+ return "Q";
+ case IOSTATE_IMMEDIATE_RETURN:
+ return "1";
+ }
+ return "?";
+}
+
+/*
+ * Event-based notification of network events
+ */
+
+void
+init_net_event_win32 (struct rw_handle *event, long network_events, socket_descriptor_t sd, unsigned int flags)
+{
+ /* manual reset events, initially set to unsignaled */
+
+ /* initialize write event */
+ if (!(flags & NE32_PERSIST_EVENT) || !event->write)
+ {
+ if (flags & NE32_WRITE_EVENT)
+ {
+ event->write = CreateEvent (NULL, TRUE, FALSE, NULL);
+ if (event->write == NULL)
+ msg (M_ERR, "Error: init_net_event_win32: CreateEvent (write) failed");
+ }
+ else
+ event->write = NULL;
+ }
+
+ /* initialize read event */
+ if (!(flags & NE32_PERSIST_EVENT) || !event->read)
+ {
+ event->read = CreateEvent (NULL, TRUE, FALSE, NULL);
+ if (event->read == NULL)
+ msg (M_ERR, "Error: init_net_event_win32: CreateEvent (read) failed");
+ }
+
+ /* setup network events to change read event state */
+ if (WSAEventSelect (sd, event->read, network_events) != 0)
+ msg (M_FATAL | M_ERRNO_SOCK, "Error: init_net_event_win32: WSAEventSelect call failed");
+}
+
+long
+reset_net_event_win32 (struct rw_handle *event, socket_descriptor_t sd)
+{
+ WSANETWORKEVENTS wne;
+ if (WSAEnumNetworkEvents (sd, event->read, &wne) != 0)
+ {
+ msg (M_FATAL | M_ERRNO_SOCK, "Error: reset_net_event_win32: WSAEnumNetworkEvents call failed");
+ return 0; /* NOTREACHED */
+ }
+ else
+ return wne.lNetworkEvents;
+}
+
+void
+close_net_event_win32 (struct rw_handle *event, socket_descriptor_t sd, unsigned int flags)
+{
+ if (event->read)
+ {
+ if (socket_defined (sd))
+ {
+ if (WSAEventSelect (sd, event->read, 0) != 0)
+ msg (M_WARN | M_ERRNO_SOCK, "Warning: close_net_event_win32: WSAEventSelect call failed");
+ }
+ if (!ResetEvent (event->read))
+ msg (M_WARN | M_ERRNO, "Warning: ResetEvent (read) failed in close_net_event_win32");
+ if (!(flags & NE32_PERSIST_EVENT))
+ {
+ if (!CloseHandle (event->read))
+ msg (M_WARN | M_ERRNO, "Warning: CloseHandle (read) failed in close_net_event_win32");
+ event->read = NULL;
+ }
+ }
+
+ if (event->write)
+ {
+ if (!ResetEvent (event->write))
+ msg (M_WARN | M_ERRNO, "Warning: ResetEvent (write) failed in close_net_event_win32");
+ if (!(flags & NE32_PERSIST_EVENT))
+ {
+ if (!CloseHandle (event->write))
+ msg (M_WARN | M_ERRNO, "Warning: CloseHandle (write) failed in close_net_event_win32");
+ event->write = NULL;
+ }
+ }
+}
+
+/*
+ * struct net_event_win32
+ */
+
+void
+net_event_win32_init (struct net_event_win32 *ne)
+{
+ CLEAR (*ne);
+ ne->sd = SOCKET_UNDEFINED;
+}
+
+void
+net_event_win32_start (struct net_event_win32 *ne, long network_events, socket_descriptor_t sd)
+{
+ ASSERT (!socket_defined (ne->sd));
+ ne->sd = sd;
+ ne->event_mask = 0;
+ init_net_event_win32 (&ne->handle, network_events, sd, NE32_PERSIST_EVENT|NE32_WRITE_EVENT);
+}
+
+void
+net_event_win32_reset_write (struct net_event_win32 *ne)
+{
+ BOOL status;
+ if (ne->event_mask & FD_WRITE)
+ status = SetEvent (ne->handle.write);
+ else
+ status = ResetEvent (ne->handle.write);
+ if (!status)
+ msg (M_WARN | M_ERRNO, "Warning: SetEvent/ResetEvent failed in net_event_win32_reset_write");
+}
+
+void
+net_event_win32_reset (struct net_event_win32 *ne)
+{
+ ne->event_mask |= reset_net_event_win32 (&ne->handle, ne->sd);
+}
+
+void
+net_event_win32_stop (struct net_event_win32 *ne)
+{
+ if (net_event_win32_defined (ne))
+ close_net_event_win32 (&ne->handle, ne->sd, NE32_PERSIST_EVENT);
+ ne->sd = SOCKET_UNDEFINED;
+ ne->event_mask = 0;
+}
+
+void
+net_event_win32_close (struct net_event_win32 *ne)
+{
+ if (net_event_win32_defined (ne))
+ close_net_event_win32 (&ne->handle, ne->sd, 0);
+ net_event_win32_init (ne);
+}
+
+/*
+ * Simulate *nix signals on Windows.
+ *
+ * Two modes:
+ * (1) Console mode -- map keyboard function keys to signals
+ * (2) Service mode -- map Windows event object to SIGTERM
+ */
+
+void
+win32_signal_clear (struct win32_signal *ws)
+{
+ CLEAR (*ws);
+}
+
+void
+win32_signal_open (struct win32_signal *ws,
+ int force,
+ const char *exit_event_name,
+ bool exit_event_initial_state)
+{
+ CLEAR (*ws);
+
+ ws->mode = WSO_MODE_UNDEF;
+ ws->in.read = INVALID_HANDLE_VALUE;
+ ws->in.write = INVALID_HANDLE_VALUE;
+ ws->console_mode_save = 0;
+ ws->console_mode_save_defined = false;
+
+ if (force == WSO_NOFORCE || force == WSO_FORCE_CONSOLE)
+ {
+ /*
+ * Try to open console.
+ */
+ ws->in.read = GetStdHandle (STD_INPUT_HANDLE);
+ if (ws->in.read != INVALID_HANDLE_VALUE)
+ {
+ if (GetConsoleMode (ws->in.read, &ws->console_mode_save))
+ {
+ /* running on a console */
+ const DWORD new_console_mode = ws->console_mode_save
+ & ~(ENABLE_WINDOW_INPUT
+ | ENABLE_PROCESSED_INPUT
+ | ENABLE_LINE_INPUT
+ | ENABLE_ECHO_INPUT
+ | ENABLE_MOUSE_INPUT);
+
+ if (new_console_mode != ws->console_mode_save)
+ {
+ if (!SetConsoleMode (ws->in.read, new_console_mode))
+ msg (M_ERR, "Error: win32_signal_open: SetConsoleMode failed");
+ ws->console_mode_save_defined = true;
+ }
+ ws->mode = WSO_MODE_CONSOLE;
+ }
+ else
+ ws->in.read = INVALID_HANDLE_VALUE; /* probably running as a service */
+ }
+ }
+
+ /*
+ * If console open failed, assume we are running
+ * as a service.
+ */
+ if ((force == WSO_NOFORCE || force == WSO_FORCE_SERVICE)
+ && !HANDLE_DEFINED (ws->in.read) && exit_event_name)
+ {
+ struct security_attributes sa;
+
+ if (!init_security_attributes_allow_all (&sa))
+ msg (M_ERR, "Error: win32_signal_open: init SA failed");
+
+ ws->in.read = CreateEvent (&sa.sa,
+ TRUE,
+ exit_event_initial_state ? TRUE : FALSE,
+ exit_event_name);
+ if (ws->in.read == NULL)
+ {
+ msg (M_WARN|M_ERRNO, "NOTE: CreateEvent '%s' failed", exit_event_name);
+ }
+ else
+ {
+ if (WaitForSingleObject (ws->in.read, 0) != WAIT_TIMEOUT)
+ msg (M_FATAL, "ERROR: Exit Event ('%s') is signaled", exit_event_name);
+ else
+ ws->mode = WSO_MODE_SERVICE;
+ }
+ }
+}
+
+static bool
+keyboard_input_available (struct win32_signal *ws)
+{
+ ASSERT (ws->mode == WSO_MODE_CONSOLE);
+ if (HANDLE_DEFINED (ws->in.read))
+ {
+ DWORD n;
+ if (GetNumberOfConsoleInputEvents (ws->in.read, &n))
+ return n > 0;
+ }
+ return false;
+}
+
+static unsigned int
+keyboard_ir_to_key (INPUT_RECORD *ir)
+{
+ if (ir->Event.KeyEvent.uChar.AsciiChar == 0)
+ return ir->Event.KeyEvent.wVirtualScanCode;
+
+ if ((ir->Event.KeyEvent.dwControlKeyState
+ & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
+ && (ir->Event.KeyEvent.wVirtualKeyCode != 18))
+ return ir->Event.KeyEvent.wVirtualScanCode * 256;
+
+ return ir->Event.KeyEvent.uChar.AsciiChar;
+}
+
+static unsigned int
+win32_keyboard_get (struct win32_signal *ws)
+{
+ ASSERT (ws->mode == WSO_MODE_CONSOLE);
+ if (HANDLE_DEFINED (ws->in.read))
+ {
+ INPUT_RECORD ir;
+ do {
+ DWORD n;
+ if (!keyboard_input_available (ws))
+ return 0;
+ if (!ReadConsoleInput (ws->in.read, &ir, 1, &n))
+ return 0;
+ } while (ir.EventType != KEY_EVENT || ir.Event.KeyEvent.bKeyDown != TRUE);
+
+ return keyboard_ir_to_key (&ir);
+ }
+ else
+ return 0;
+}
+
+void
+win32_signal_close (struct win32_signal *ws)
+{
+ if (ws->mode == WSO_MODE_SERVICE && HANDLE_DEFINED (ws->in.read))
+ CloseHandle (ws->in.read);
+ if (ws->console_mode_save_defined)
+ {
+ if (!SetConsoleMode (ws->in.read, ws->console_mode_save))
+ msg (M_ERR, "Error: win32_signal_close: SetConsoleMode failed");
+ }
+ CLEAR (*ws);
+}
+
+/*
+ * Return true if interrupt occurs in service mode.
+ */
+static bool
+win32_service_interrupt (struct win32_signal *ws)
+{
+ if (ws->mode == WSO_MODE_SERVICE)
+ {
+ if (HANDLE_DEFINED (ws->in.read)
+ && WaitForSingleObject (ws->in.read, 0) == WAIT_OBJECT_0)
+ return true;
+ }
+ return false;
+}
+
+int
+win32_signal_get (struct win32_signal *ws)
+{
+ int ret = 0;
+ if (siginfo_static.signal_received)
+ {
+ ret = siginfo_static.signal_received;
+ }
+ else
+ {
+ if (ws->mode == WSO_MODE_SERVICE)
+ {
+ if (win32_service_interrupt (ws))
+ ret = SIGTERM;
+ }
+ else if (ws->mode == WSO_MODE_CONSOLE)
+ {
+ switch (win32_keyboard_get (ws))
+ {
+ case 0x3B: /* F1 -> USR1 */
+ ret = SIGUSR1;
+ break;
+ case 0x3C: /* F2 -> USR2 */
+ ret = SIGUSR2;
+ break;
+ case 0x3D: /* F3 -> HUP */
+ ret = SIGHUP;
+ break;
+ case 0x3E: /* F4 -> TERM */
+ ret = SIGTERM;
+ break;
+ }
+ }
+ if (ret)
+ {
+ siginfo_static.signal_received = ret;
+ siginfo_static.hard = true;
+ }
+ }
+ return ret;
+}
+
+void
+win32_pause (struct win32_signal *ws)
+{
+ if (ws->mode == WSO_MODE_CONSOLE && HANDLE_DEFINED (ws->in.read))
+ {
+ int status;
+ msg (M_INFO|M_NOPREFIX, "Press any key to continue...");
+ do {
+ status = WaitForSingleObject (ws->in.read, INFINITE);
+ } while (!win32_keyboard_get (ws));
+ }
+}
+
+/* window functions */
+
+void
+window_title_clear (struct window_title *wt)
+{
+ CLEAR (*wt);
+}
+
+void
+window_title_save (struct window_title *wt)
+{
+ if (!wt->saved)
+ {
+ if (!GetConsoleTitle (wt->old_window_title, sizeof (wt->old_window_title)))
+ {
+ wt->old_window_title[0] = 0;
+ wt->saved = false;
+ }
+ else
+ wt->saved = true;
+ }
+}
+
+void
+window_title_restore (const struct window_title *wt)
+{
+ if (wt->saved)
+ SetConsoleTitle (wt->old_window_title);
+}
+
+void
+window_title_generate (const char *title)
+{
+ struct gc_arena gc = gc_new ();
+ struct buffer out = alloc_buf_gc (256, &gc);
+ if (!title)
+ title = "";
+ buf_printf (&out, "[%s] " PACKAGE_NAME " " VERSION " F4:EXIT F1:USR1 F2:USR2 F3:HUP", title);
+ SetConsoleTitle (BSTR (&out));
+ gc_free (&gc);
+}
+
+/* semaphore functions */
+
+void
+semaphore_clear (struct semaphore *s)
+{
+ CLEAR (*s);
+}
+
+void
+semaphore_open (struct semaphore *s, const char *name)
+{
+ struct security_attributes sa;
+
+ s->locked = false;
+ s->name = name;
+ s->hand = NULL;
+
+ if (init_security_attributes_allow_all (&sa))
+ s->hand = CreateSemaphore(&sa.sa, 1, 1, name);
+
+ if (s->hand == NULL)
+ msg (M_WARN|M_ERRNO, "WARNING: Cannot create Win32 semaphore '%s'", name);
+ else
+ dmsg (D_SEMAPHORE, "Created Win32 semaphore '%s'", s->name);
+}
+
+bool
+semaphore_lock (struct semaphore *s, int timeout_milliseconds)
+{
+ bool ret = true;
+
+ if (s->hand)
+ {
+ DWORD status;
+ ASSERT (!s->locked);
+
+ dmsg (D_SEMAPHORE_LOW, "Attempting to lock Win32 semaphore '%s' prior to net shell command (timeout = %d sec)",
+ s->name,
+ timeout_milliseconds / 1000);
+ status = WaitForSingleObject (s->hand, timeout_milliseconds);
+ if (status == WAIT_FAILED)
+ msg (M_ERR, "Wait failed on Win32 semaphore '%s'", s->name);
+ ret = (status == WAIT_TIMEOUT) ? false : true;
+ if (ret)
+ {
+ dmsg (D_SEMAPHORE, "Locked Win32 semaphore '%s'", s->name);
+ s->locked = true;
+ }
+ else
+ {
+ dmsg (D_SEMAPHORE, "Wait on Win32 semaphore '%s' timed out after %d milliseconds",
+ s->name,
+ timeout_milliseconds);
+ }
+ }
+ return ret;
+}
+
+void
+semaphore_release (struct semaphore *s)
+{
+ if (s->hand)
+ {
+ ASSERT (s->locked);
+ dmsg (D_SEMAPHORE, "Releasing Win32 semaphore '%s'", s->name);
+ if (!ReleaseSemaphore(s->hand, 1, NULL))
+ msg (M_WARN | M_ERRNO, "ReleaseSemaphore failed on Win32 semaphore '%s'",
+ s->name);
+ s->locked = false;
+ }
+}
+
+void
+semaphore_close (struct semaphore *s)
+{
+ if (s->hand)
+ {
+ if (s->locked)
+ semaphore_release (s);
+ dmsg (D_SEMAPHORE, "Closing Win32 semaphore '%s'", s->name);
+ CloseHandle (s->hand);
+ s->hand = NULL;
+ }
+}
+
+/*
+ * Special global semaphore used to protect network
+ * shell commands from simultaneous instantiation.
+ */
+
+void
+netcmd_semaphore_init (void)
+{
+ semaphore_open (&netcmd_semaphore, PACKAGE "_netcmd");
+}
+
+void
+netcmd_semaphore_close (void)
+{
+ semaphore_close (&netcmd_semaphore);
+}
+
+void
+netcmd_semaphore_lock (void)
+{
+ const int timeout_seconds = 600;
+ if (!semaphore_lock (&netcmd_semaphore, timeout_seconds * 1000))
+ msg (M_FATAL, "Cannot lock net command semaphore");
+}
+
+void
+netcmd_semaphore_release (void)
+{
+ semaphore_release (&netcmd_semaphore);
+}
+
+/*
+ * Get input from console.
+ *
+ * Return false on input error, or if service
+ * exit event is signaled.
+ */
+
+bool
+get_console_input_win32 (const char *prompt, const bool echo, char *input, const int capacity)
+{
+ HANDLE in = INVALID_HANDLE_VALUE;
+ HANDLE err = INVALID_HANDLE_VALUE;
+ DWORD len = 0;
+
+ ASSERT (prompt);
+ ASSERT (input);
+ ASSERT (capacity > 0);
+
+ input[0] = '\0';
+
+ in = GetStdHandle (STD_INPUT_HANDLE);
+ err = get_orig_stderr ();
+
+ if (in != INVALID_HANDLE_VALUE
+ && err != INVALID_HANDLE_VALUE
+ && !win32_service_interrupt (&win32_signal)
+ && WriteFile (err, prompt, strlen (prompt), &len, NULL))
+ {
+ bool is_console = (GetFileType (in) == FILE_TYPE_CHAR);
+ DWORD flags_save = 0;
+ int status = 0;
+
+ if (is_console)
+ {
+ if (GetConsoleMode (in, &flags_save))
+ {
+ DWORD flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
+ if (echo)
+ flags |= ENABLE_ECHO_INPUT;
+ SetConsoleMode (in, flags);
+ }
+ else
+ is_console = 0;
+ }
+
+ status = ReadFile (in, input, capacity, &len, NULL);
+
+ string_null_terminate (input, (int)len, capacity);
+ chomp (input);
+
+ if (!echo)
+ WriteFile (err, "\r\n", 2, &len, NULL);
+ if (is_console)
+ SetConsoleMode (in, flags_save);
+ if (status && !win32_service_interrupt (&win32_signal))
+ return true;
+ }
+
+ return false;
+}
+
+/* get password from console */
+
+char *
+getpass (const char *prompt)
+{
+ static char line[256];
+ if (get_console_input_win32 (prompt, false, line, sizeof (line)))
+ return line;
+ else
+ return NULL;
+}
+
+#endif