mirror of https://github.com/OpenVPN/openvpn-gui
Merge pull request #28 from selvanair/service-io
Read errors from the service pipe and handle fatal onespull/45/head
commit
2dd468d23c
27
manage.c
27
manage.c
|
@ -65,10 +65,13 @@ OpenManagement(connection_t *c)
|
|||
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
|
||||
return FALSE;
|
||||
|
||||
c->manage.connected = FALSE;
|
||||
c->manage.sk = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (c->manage.sk == INVALID_SOCKET)
|
||||
{
|
||||
WSACleanup ();
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
if (WSAAsyncSelect(c->manage.sk, c->hwndStatus, WM_MANAGEMENT,
|
||||
FD_CONNECT|FD_READ|FD_WRITE|FD_CLOSE) != 0)
|
||||
return FALSE;
|
||||
|
@ -206,10 +209,14 @@ OnManagement(SOCKET sk, LPARAM lParam)
|
|||
else
|
||||
{
|
||||
/* Connection to MI timed out. */
|
||||
c->state = timedout;
|
||||
if (c->state != disconnected)
|
||||
c->state = timedout;
|
||||
CloseManagement (c);
|
||||
rtmsg_handler[stop](c, "");
|
||||
}
|
||||
}
|
||||
else
|
||||
c->manage.connected = TRUE;
|
||||
break;
|
||||
|
||||
case FD_READ:
|
||||
|
@ -340,6 +347,18 @@ OnManagement(SOCKET sk, LPARAM lParam)
|
|||
break;
|
||||
|
||||
case FD_CLOSE:
|
||||
CloseManagement (c);
|
||||
if (rtmsg_handler[stop])
|
||||
rtmsg_handler[stop](c, "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CloseManagement(connection_t *c)
|
||||
{
|
||||
if (c->manage.sk != INVALID_SOCKET)
|
||||
{
|
||||
if (c->manage.saved_size)
|
||||
{
|
||||
free(c->manage.saved_data);
|
||||
|
@ -348,11 +367,9 @@ OnManagement(SOCKET sk, LPARAM lParam)
|
|||
}
|
||||
closesocket(c->manage.sk);
|
||||
c->manage.sk = INVALID_SOCKET;
|
||||
c->manage.connected = FALSE;
|
||||
while (UnqueueCommand(c))
|
||||
;
|
||||
WSACleanup();
|
||||
if (rtmsg_handler[stop])
|
||||
rtmsg_handler[stop](c, "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
1
manage.h
1
manage.h
|
@ -68,5 +68,6 @@ BOOL OpenManagement(connection_t *);
|
|||
BOOL ManagementCommand(connection_t *, char *, mgmt_msg_func, mgmt_cmd_type);
|
||||
|
||||
void OnManagement(SOCKET, LPARAM);
|
||||
void CloseManagement(connection_t *);
|
||||
|
||||
#endif
|
||||
|
|
325
openvpn.c
325
openvpn.c
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 2004 Mathias Sundman <mathias@nilings.se>
|
||||
* 2010 Heiko Hund <heikoh@users.sf.net>
|
||||
* 2016 Selva Nair <selva.nair@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -31,6 +32,7 @@
|
|||
#include <stdio.h>
|
||||
#include <process.h>
|
||||
#include <richedit.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "tray.h"
|
||||
#include "main.h"
|
||||
|
@ -466,6 +468,269 @@ OnStop(connection_t *c, UNUSED char *msg)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Break a long line into shorter segments
|
||||
*/
|
||||
static WCHAR *
|
||||
WrapLine (WCHAR *line)
|
||||
{
|
||||
int i = 0;
|
||||
WCHAR *next = NULL;
|
||||
int len = 80;
|
||||
|
||||
for (i = 0; *line; i++, ++line)
|
||||
{
|
||||
if ((*line == L'\r') || (*line == L'\n'))
|
||||
*line = L' ';
|
||||
if (next && i > len) break;
|
||||
if (iswspace(*line)) next = line;
|
||||
}
|
||||
if (!*line) next = NULL;
|
||||
if (next)
|
||||
{
|
||||
*next = L'\0';
|
||||
++next;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a line to the status log window and optionally to the log file
|
||||
*/
|
||||
static void
|
||||
WriteStatusLog (connection_t *c, const WCHAR *prefix, const WCHAR *line, BOOL fileio)
|
||||
{
|
||||
HWND logWnd = GetDlgItem(c->hwndStatus, ID_EDT_LOG);
|
||||
FILE *log_fd;
|
||||
time_t now;
|
||||
WCHAR datetime[26];
|
||||
|
||||
time (&now);
|
||||
/* TODO: change this to use _wctime_s when mingw supports it */
|
||||
wcsncpy (datetime, _wctime(&now), _countof(datetime));
|
||||
datetime[24] = L' ';
|
||||
|
||||
/* Remove lines from log window if it is getting full */
|
||||
if (SendMessage(logWnd, EM_GETLINECOUNT, 0, 0) > MAX_LOG_LINES)
|
||||
{
|
||||
int pos = SendMessage(logWnd, EM_LINEINDEX, DEL_LOG_LINES, 0);
|
||||
SendMessage(logWnd, EM_SETSEL, 0, pos);
|
||||
SendMessage(logWnd, EM_REPLACESEL, FALSE, (LPARAM) _T(""));
|
||||
}
|
||||
/* Append line to log window */
|
||||
SendMessage(logWnd, EM_SETSEL, (WPARAM) -1, (LPARAM) -1);
|
||||
SendMessage(logWnd, EM_REPLACESEL, FALSE, (LPARAM) datetime);
|
||||
SendMessage(logWnd, EM_REPLACESEL, FALSE, (LPARAM) prefix);
|
||||
SendMessage(logWnd, EM_REPLACESEL, FALSE, (LPARAM) line);
|
||||
SendMessage(logWnd, EM_REPLACESEL, FALSE, (LPARAM) L"\n");
|
||||
|
||||
if (!fileio) return;
|
||||
|
||||
log_fd = _tfopen (c->log_path, TEXT("at+,ccs=UTF-8"));
|
||||
if (log_fd)
|
||||
{
|
||||
fwprintf (log_fd, L"%s%s%s\n", datetime, prefix, line);
|
||||
fclose (log_fd);
|
||||
}
|
||||
}
|
||||
|
||||
#define IO_TIMEOUT 5000 /* milliseconds */
|
||||
|
||||
static void
|
||||
CloseServiceIO (service_io_t *s)
|
||||
{
|
||||
if (s->hEvent)
|
||||
CloseHandle(s->hEvent);
|
||||
s->hEvent = NULL;
|
||||
if (s->pipe && s->pipe != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(s->pipe);
|
||||
s->pipe = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open the service pipe and initialize service I/O.
|
||||
* Failure is not fatal.
|
||||
*/
|
||||
static BOOL
|
||||
InitServiceIO (service_io_t *s)
|
||||
{
|
||||
DWORD dwMode = PIPE_READMODE_MESSAGE;
|
||||
CLEAR(*s);
|
||||
|
||||
/* auto-reset event used for signalling i/o completion*/
|
||||
s->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
|
||||
if (!s->hEvent)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
s->pipe = CreateFile(_T("\\\\.\\pipe\\openvpn\\service"),
|
||||
GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
||||
|
||||
if ( !s->pipe ||
|
||||
s->pipe == INVALID_HANDLE_VALUE ||
|
||||
!SetNamedPipeHandleState(s->pipe, &dwMode, NULL, NULL)
|
||||
)
|
||||
{
|
||||
CloseServiceIO (s);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read-completion routine for interactive service pipe. Call with
|
||||
* err = 0, bytes = 0 to queue the first read request.
|
||||
*/
|
||||
static void
|
||||
HandleServiceIO (DWORD err, DWORD bytes, LPOVERLAPPED lpo)
|
||||
{
|
||||
service_io_t *s = (service_io_t *) lpo;
|
||||
int len, capacity;
|
||||
|
||||
len = _countof(s->readbuf);
|
||||
capacity = len*sizeof(*(s->readbuf));
|
||||
|
||||
if (bytes > 0)
|
||||
SetEvent (s->hEvent);
|
||||
if (err)
|
||||
{
|
||||
_snwprintf(s->readbuf, len, L"0x%08x\nInteractive Service disconnected\n", err);
|
||||
s->readbuf[len-1] = L'\0';
|
||||
SetEvent (s->hEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
/* queue next read request */
|
||||
ReadFileEx (s->pipe, s->readbuf, capacity, lpo, (LPOVERLAPPED_COMPLETION_ROUTINE) HandleServiceIO);
|
||||
/* Any error in the above call will get checked in next round */
|
||||
}
|
||||
|
||||
/*
|
||||
* Write size bytes in buf to the pipe with a timeout.
|
||||
* Retun value: TRUE on success FLASE on error
|
||||
*/
|
||||
static BOOL
|
||||
WritePipe (HANDLE pipe, LPVOID buf, DWORD size)
|
||||
{
|
||||
OVERLAPPED o;
|
||||
BOOL retval = FALSE;
|
||||
|
||||
CLEAR(o);
|
||||
o.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
|
||||
|
||||
if (!o.hEvent)
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (WriteFile (pipe, buf, size, NULL, &o) ||
|
||||
GetLastError() == ERROR_IO_PENDING )
|
||||
{
|
||||
if (WaitForSingleObject(o.hEvent, IO_TIMEOUT) == WAIT_OBJECT_0)
|
||||
retval = TRUE;
|
||||
else
|
||||
CancelIo (pipe);
|
||||
// TODO report error -- timeout
|
||||
}
|
||||
|
||||
CloseHandle(o.hEvent);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when read from service pipe signals
|
||||
*/
|
||||
static void
|
||||
OnService(connection_t *c, UNUSED char *msg)
|
||||
{
|
||||
DWORD err = 0;
|
||||
WCHAR *p, *buf, *next;
|
||||
DWORD len;
|
||||
const WCHAR *prefix = L"IService> ";
|
||||
|
||||
len = wcslen (c->iserv.readbuf);
|
||||
if (!len || (buf = wcsdup (c->iserv.readbuf)) == NULL)
|
||||
return;
|
||||
|
||||
/* messages from the service are in the format "0x08x\n%s\n%s" */
|
||||
if (swscanf (buf, L"0x%08x\n", &err) != 1)
|
||||
{
|
||||
free (buf);
|
||||
return;
|
||||
}
|
||||
|
||||
p = buf + 11;
|
||||
while (iswspace(*p)) ++p;
|
||||
|
||||
while (p && *p)
|
||||
{
|
||||
next = WrapLine (p);
|
||||
WriteStatusLog (c, prefix, p, c->manage.connected ? FALSE : TRUE);
|
||||
p = next;
|
||||
}
|
||||
free (buf);
|
||||
|
||||
/* Error from iservice before management interface is connected */
|
||||
switch (err)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case ERROR_STARTUP_DATA:
|
||||
WriteStatusLog (c, prefix, L"OpenVPN not started due to previous errors", true);
|
||||
c->state = timedout; /* Force the popup message to include the log file name */
|
||||
OnStop (c, NULL);
|
||||
break;
|
||||
case ERROR_OPENVPN_STARTUP:
|
||||
WriteStatusLog (c, prefix, L"Check the log file for details", false);
|
||||
c->state = timedout; /* Force the popup message to include the log file name */
|
||||
OnStop(c, NULL);
|
||||
break;
|
||||
default:
|
||||
/* Unknown failure: let management connection timeout */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the directly started openvpn process exits
|
||||
*/
|
||||
static void
|
||||
OnProcess (connection_t *c, UNUSED char *msg)
|
||||
{
|
||||
DWORD err;
|
||||
WCHAR tmp[256];
|
||||
|
||||
if (!GetExitCodeProcess(c->hProcess, &err) || err == STILL_ACTIVE)
|
||||
return;
|
||||
|
||||
_snwprintf(tmp, _countof(tmp), L"OpenVPN terminated with exit code %lu. "
|
||||
L"See the log file for details", err);
|
||||
tmp[_countof(tmp)-1] = L'\0';
|
||||
WriteStatusLog(c, L"OpenVPN GUI> ", tmp, false);
|
||||
|
||||
OnStop (c, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Close open handles
|
||||
*/
|
||||
static void
|
||||
Cleanup (connection_t *c)
|
||||
{
|
||||
CloseManagement (c);
|
||||
|
||||
if (c->hProcess)
|
||||
CloseHandle (c->hProcess);
|
||||
else
|
||||
CloseServiceIO (&c->iserv);
|
||||
c->hProcess = NULL;
|
||||
|
||||
if (c->exit_event)
|
||||
CloseHandle (c->exit_event);
|
||||
c->exit_event = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* DialogProc for OpenVPN status dialog windows
|
||||
|
@ -587,7 +852,6 @@ StatusDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ThreadProc for OpenVPN status dialog windows
|
||||
*/
|
||||
|
@ -597,6 +861,9 @@ ThreadOpenVPNStatus(void *p)
|
|||
connection_t *c = p;
|
||||
TCHAR conn_name[200];
|
||||
MSG msg;
|
||||
HANDLE wait_event;
|
||||
|
||||
CLEAR (msg);
|
||||
|
||||
/* Cut of extention from config filename. */
|
||||
_tcsncpy(conn_name, c->config_file, _countof(conn_name));
|
||||
|
@ -617,12 +884,35 @@ ThreadOpenVPNStatus(void *p)
|
|||
if (!OpenManagement(c))
|
||||
PostMessage(c->hwndStatus, WM_CLOSE, 0, 0);
|
||||
|
||||
/* Start the async read loop for service and set it as the wait event */
|
||||
if (!c->hProcess)
|
||||
{
|
||||
HandleServiceIO (0, 0, (LPOVERLAPPED) &c->iserv);
|
||||
wait_event = c->iserv.hEvent;
|
||||
}
|
||||
else
|
||||
wait_event = c->hProcess;
|
||||
|
||||
if (o.silent_connection[0] == '0')
|
||||
ShowWindow(c->hwndStatus, SW_SHOW);
|
||||
|
||||
/* Run the message loop for the status window */
|
||||
while (GetMessage(&msg, NULL, 0, 0))
|
||||
while (WM_QUIT != msg.message)
|
||||
{
|
||||
DWORD res;
|
||||
if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
||||
{
|
||||
if ((res = MsgWaitForMultipleObjectsEx (1, &wait_event, INFINITE, QS_ALLINPUT,
|
||||
MWMO_ALERTABLE)) == WAIT_OBJECT_0)
|
||||
{
|
||||
if (c->hProcess)
|
||||
OnProcess (c, NULL);
|
||||
else
|
||||
OnService (c, NULL);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (msg.hwnd == NULL)
|
||||
{
|
||||
switch (msg.message)
|
||||
|
@ -653,10 +943,12 @@ ThreadOpenVPNStatus(void *p)
|
|||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
/* release handles etc.*/
|
||||
Cleanup (c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set priority based on the registry or cmd-line value
|
||||
*/
|
||||
|
@ -682,7 +974,6 @@ SetProcessPriority(DWORD *priority)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Launch an OpenVPN process and the accompanying thread to monitor it
|
||||
*/
|
||||
|
@ -693,7 +984,7 @@ StartOpenVPN(connection_t *c)
|
|||
TCHAR *options = cmdline + 8;
|
||||
TCHAR exit_event_name[17];
|
||||
HANDLE hStdInRead = NULL, hStdInWrite = NULL;
|
||||
HANDLE hNul = NULL, hThread = NULL, service = NULL;
|
||||
HANDLE hNul = NULL, hThread = NULL;
|
||||
DWORD written;
|
||||
BOOL retval = FALSE;
|
||||
|
||||
|
@ -739,15 +1030,10 @@ StartOpenVPN(connection_t *c)
|
|||
(o.proxy_source != config ? _T("--management-query-proxy ") : _T("")));
|
||||
|
||||
/* Try to open the service pipe */
|
||||
if (!IsUserAdmin())
|
||||
service = CreateFile(_T("\\\\.\\pipe\\openvpn\\service"),
|
||||
GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
|
||||
if (service && service != INVALID_HANDLE_VALUE)
|
||||
if (!IsUserAdmin() && InitServiceIO (&c->iserv))
|
||||
{
|
||||
DWORD size = _tcslen(c->config_dir) + _tcslen(options) + sizeof(c->manage.password) + 3;
|
||||
TCHAR startup_info[1024];
|
||||
DWORD dwMode = PIPE_READMODE_MESSAGE;
|
||||
|
||||
if ( !AuthorizeConfig(c))
|
||||
{
|
||||
|
@ -755,22 +1041,17 @@ StartOpenVPN(connection_t *c)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (!SetNamedPipeHandleState(service, &dwMode, NULL, NULL))
|
||||
{
|
||||
ShowLocalizedMsg (IDS_ERR_ACCESS_SERVICE_PIPE);
|
||||
CloseHandle(c->exit_event);
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->hProcess = NULL;
|
||||
c->manage.password[sizeof(c->manage.password) - 1] = '\n';
|
||||
_sntprintf_0(startup_info, _T("%s%c%s%c%.*S"), c->config_dir, _T('\0'),
|
||||
options, _T('\0'), sizeof(c->manage.password), c->manage.password);
|
||||
c->manage.password[sizeof(c->manage.password) - 1] = '\0';
|
||||
|
||||
if (!WriteFile(service, startup_info, size * sizeof (TCHAR), &written, NULL))
|
||||
if (!WritePipe(c->iserv.pipe, startup_info, size * sizeof (TCHAR)))
|
||||
{
|
||||
ShowLocalizedMsg (IDS_ERR_WRITE_SERVICE_PIPE);
|
||||
CloseHandle(c->exit_event);
|
||||
CloseServiceIO(&c->iserv);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
@ -788,6 +1069,7 @@ StartOpenVPN(connection_t *c)
|
|||
.lpSecurityDescriptor = &sd,
|
||||
.bInheritHandle = TRUE
|
||||
};
|
||||
|
||||
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
|
||||
{
|
||||
ShowLocalizedMsg(IDS_ERR_INIT_SEC_DESC);
|
||||
|
@ -852,7 +1134,7 @@ StartOpenVPN(connection_t *c)
|
|||
WriteFile(hStdInWrite, c->manage.password, sizeof(c->manage.password), &written, NULL);
|
||||
c->manage.password[sizeof(c->manage.password) - 1] = '\0';
|
||||
|
||||
CloseHandle(pi.hProcess);
|
||||
c->hProcess = pi.hProcess; /* Will be closed in the event loop on exit */
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
|
||||
|
@ -861,8 +1143,6 @@ StartOpenVPN(connection_t *c)
|
|||
retval = TRUE;
|
||||
|
||||
out:
|
||||
if (service && service != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(service);
|
||||
if (hThread && hThread != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(hThread);
|
||||
if (hStdInWrite && hStdInWrite != INVALID_HANDLE_VALUE)
|
||||
|
@ -1030,4 +1310,3 @@ out:
|
|||
CloseHandle(hStdOutWrite);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,4 +38,10 @@ void OnStop(connection_t *, char *);
|
|||
|
||||
extern const TCHAR *cfgProp;
|
||||
|
||||
/* These error codes are from openvpn service sources */
|
||||
#define ERROR_OPENVPN_STARTUP 0x20000000
|
||||
#define ERROR_STARTUP_DATA 0x20000001
|
||||
#define ERROR_MESSAGE_DATA 0x20000002
|
||||
#define ERROR_MESSAGE_TYPE 0x20000003
|
||||
|
||||
#endif
|
||||
|
|
12
options.h
12
options.h
|
@ -74,6 +74,14 @@ typedef enum {
|
|||
timedout
|
||||
} conn_state_t;
|
||||
|
||||
/* Interactive Service IO parameters */
|
||||
typedef struct {
|
||||
OVERLAPPED o; /* This has to be the first element */
|
||||
HANDLE pipe;
|
||||
HANDLE hEvent;
|
||||
WCHAR readbuf[512];
|
||||
} service_io_t;
|
||||
|
||||
/* Connections parameters */
|
||||
struct connection {
|
||||
TCHAR config_file[MAX_PATH]; /* Name of the config file */
|
||||
|
@ -95,8 +103,12 @@ struct connection {
|
|||
char *saved_data;
|
||||
size_t saved_size;
|
||||
mgmt_cmd_t *cmd_queue;
|
||||
BOOL connected; /* True, if management interface has connected */
|
||||
} manage;
|
||||
|
||||
HANDLE hProcess; /* Handle of openvpn process if directly started */
|
||||
service_io_t iserv;
|
||||
|
||||
HANDLE exit_event;
|
||||
DWORD threadId;
|
||||
HWND hwndStatus;
|
||||
|
|
Loading…
Reference in New Issue