diff --git a/openvpn-gui-res.h b/openvpn-gui-res.h index e46dd98..378d229 100644 --- a/openvpn-gui-res.h +++ b/openvpn-gui-res.h @@ -296,4 +296,7 @@ /* Save password related messages */ #define IDS_NFO_DELETE_PASS 2001 +/* Timer IDs */ +#define IDT_STOP_TIMER 2500 /* Timer used to trigger force termination */ + #endif diff --git a/openvpn.c b/openvpn.c index 30b3c22..3356876 100644 --- a/openvpn.c +++ b/openvpn.c @@ -54,6 +54,9 @@ extern options_t o; +static BOOL +TerminateOpenVPN(connection_t *c); + const TCHAR *cfgProp = _T("conn"); typedef struct { @@ -471,7 +474,8 @@ OnStop(connection_t *c, UNUSED char *msg) SetForegroundWindow(c->hwndStatus); ShowWindow(c->hwndStatus, SW_SHOW); } - ShowLocalizedMsg(IDS_NFO_CONN_TERMINATED, c->config_name); + MessageBox(c->hwndStatus, LoadLocalizedString(IDS_NFO_CONN_TERMINATED, c->config_file), + _T(PACKAGE_NAME), MB_OK); SendMessage(c->hwndStatus, WM_CLOSE, 0, 0); break; @@ -498,7 +502,7 @@ OnStop(connection_t *c, UNUSED char *msg) SetForegroundWindow(c->hwndStatus); ShowWindow(c->hwndStatus, SW_SHOW); } - ShowLocalizedMsg(msg_id, msg_xtra); + MessageBox(c->hwndStatus, LoadLocalizedString(msg_id, msg_xtra), _T(PACKAGE_NAME), MB_OK); SendMessage(c->hwndStatus, WM_CLOSE, 0, 0); break; @@ -658,10 +662,15 @@ HandleServiceIO (DWORD err, DWORD bytes, LPOVERLAPPED lpo) int len, capacity; len = _countof(s->readbuf); - capacity = len*sizeof(*(s->readbuf)); + capacity = (len-1)*sizeof(*(s->readbuf)); if (bytes > 0) + { + /* messages from the service are not nul terminated */ + int nchars = bytes/sizeof(s->readbuf[0]); + s->readbuf[nchars] = L'\0'; SetEvent (s->hEvent); + } if (err) { _snwprintf(s->readbuf, len, L"0x%08x\nInteractive Service disconnected\n", err); @@ -714,6 +723,7 @@ static void OnService(connection_t *c, UNUSED char *msg) { DWORD err = 0; + DWORD pid = 0; WCHAR *p, *buf, *next; DWORD len; const WCHAR *prefix = L"IService> "; @@ -730,6 +740,17 @@ OnService(connection_t *c, UNUSED char *msg) } p = buf + 11; + if (!err && swscanf (p, L"0x%08x\nProcess ID", &pid) == 1 && pid != 0) + { + PrintDebug (L"Process ID of openvpn started by IService: %d", pid); + c->hProcess = OpenProcess (PROCESS_TERMINATE|PROCESS_QUERY_INFORMATION, FALSE, pid); + if (!c->hProcess) + PrintDebug (L"Failed to get process handle from pid of openvpn: error = %lu", + GetLastError()); + free (buf); + return; + } + while (iswspace(*p)) ++p; while (p && *p) @@ -791,10 +812,11 @@ Cleanup (connection_t *c) if (c->hProcess) CloseHandle (c->hProcess); - else - CloseServiceIO (&c->iserv); c->hProcess = NULL; + if (c->iserv.hEvent) + CloseServiceIO (&c->iserv); + if (c->exit_event) CloseHandle (c->exit_event); c->exit_event = NULL; @@ -926,6 +948,7 @@ StatusDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) SetMenuStatus(c, disconnecting); SetDlgItemText(c->hwndStatus, ID_TXT_STATUS, LoadLocalizedString(IDS_NFO_STATE_WAIT_TERM)); SetEvent(c->exit_event); + SetTimer(hwndDlg, IDT_STOP_TIMER, 3000, NULL); break; case WM_OVPN_SUSPEND: @@ -936,6 +959,18 @@ StatusDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) SetMenuStatus(c, disconnecting); SetDlgItemText(c->hwndStatus, ID_TXT_STATUS, LoadLocalizedString(IDS_NFO_STATE_WAIT_TERM)); SetEvent(c->exit_event); + SetTimer(hwndDlg, IDT_STOP_TIMER, 3000, NULL); + break; + + case WM_TIMER: + PrintDebug(L"WM_TIMER message with wParam = %lu", wParam); + c = (connection_t *) GetProp(hwndDlg, cfgProp); + if (wParam == IDT_STOP_TIMER) + { + /* openvpn failed to respond to stop signal -- terminate */ + TerminateOpenVPN(c); + KillTimer (hwndDlg, IDT_STOP_TIMER); + } break; } return FALSE; @@ -974,7 +1009,7 @@ ThreadOpenVPNStatus(void *p) PostMessage(c->hwndStatus, WM_CLOSE, 0, 0); /* Start the async read loop for service and set it as the wait event */ - if (!c->hProcess) + if (c->iserv.hEvent) { HandleServiceIO (0, 0, (LPOVERLAPPED) &c->iserv); wait_event = c->iserv.hEvent; @@ -994,9 +1029,9 @@ ThreadOpenVPNStatus(void *p) if ((res = MsgWaitForMultipleObjectsEx (1, &wait_event, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE)) == WAIT_OBJECT_0) { - if (c->hProcess) + if (wait_event == c->hProcess) OnProcess (c, NULL); - else + else if (wait_event == c->iserv.hEvent) OnService (c, NULL); } continue; @@ -1011,6 +1046,7 @@ ThreadOpenVPNStatus(void *p) /* release handles etc.*/ Cleanup (c); + c->hwndStatus = NULL; return 0; } @@ -1055,6 +1091,15 @@ StartOpenVPN(connection_t *c) CLEAR(c->ip); + if (c->hwndStatus) + { + PrintDebug(L"Connection request when previous status window is still open -- ignored"); + WriteStatusLog(c, L"OpenVPN GUI> ", + L"Complete the pending dialog before starting a new connection", false); + SetForegroundWindow(c->hwndStatus); + return FALSE; + } + RunPreconnectScript(c); /* Create thread to show the connection's status dialog */ @@ -1219,6 +1264,33 @@ StopOpenVPN(connection_t *c) PostMessage(c->hwndStatus, WM_OVPN_STOP, 0, 0); } +/* force-kill as a last resort */ +static BOOL +TerminateOpenVPN (connection_t *c) +{ + DWORD exit_code = 0; + BOOL retval = TRUE; + + if (!c->hProcess) + return retval; + if (!GetExitCodeProcess (c->hProcess, &exit_code)) + { + PrintDebug (L"In TerminateOpenVPN: failed to get process status: error = %lu", GetLastError()); + return FALSE; + } + if (exit_code == STILL_ACTIVE) + { + retval = TerminateProcess (c->hProcess, 1); + if (retval) + PrintDebug (L"Openvpn Process for config '%s' terminated", c->config_name); + else + PrintDebug (L"Failed to terminate openvpn Process for config '%s'", c->config_name); + } + else + PrintDebug(L"In TerminateOpenVPN: Process is not active"); + + return retval; +} void SuspendOpenVPN(int config)