From fa9e79323b2c212759cfc7c5e127800e5113626f Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Mon, 19 Dec 2022 13:11:07 -0500 Subject: [PATCH] Save active connections on exit and auto-start in next session New feature: any connection that is not disconnected on exit will auto-connect when the GUi is started the next time. There is no option to toggle auto-connect of any profile. Instead, just connect normally and leave the connection open while closing the GUI directly or indirectly (on logout, for example). Such a connection will auto-connect when the GUI is started the next time. If auto-connect is not desired for a particular connection, stop it before exit. Or, the whole feature may be disabled in the setings menu (implemented in next commit). Signed-off-by: Selva Nair --- main.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++ options.h | 1 + registry.c | 1 + 3 files changed, 127 insertions(+) diff --git a/main.c b/main.c index 8848043..385b414 100644 --- a/main.c +++ b/main.c @@ -66,6 +66,8 @@ static void ShowSettingsDialog(); void CloseApplication(HWND hwnd); void ImportConfigFileFromDisk(); void ImportConfigFromAS(); +static void SaveAutoRestartList(); +static void LoadAutoRestartList(); /* Class name and window title */ TCHAR szClassName[ ] = _T("OpenVPN-GUI"); @@ -585,6 +587,11 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM ImportConfigFile(o.action_arg, true); /* prompt user */ } + if (o.enable_auto_restart) + { + LoadAutoRestartList(); + } + if (!AutoStartConnections()) { SendMessage(hwnd, WM_CLOSE, 0, 0); break; @@ -675,6 +682,7 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM return(TRUE); case WM_ENDSESSION: + SaveAutoRestartList(); StopAllOpenVPN(); OnDestroyTray(); break; @@ -836,6 +844,8 @@ CloseApplication(HWND hwnd) break; /* show the above message box only once */ } + SaveAutoRestartList(); /* active connection names saved in registry */ + DestroyWindow(hwnd); } @@ -949,3 +959,118 @@ ErrorExit(int exit_code, const wchar_t *msg) exit(exit_code); } } + +/** + * Save a list of active connections in registry. If enable_auto_restart + * is false, any previously saved list is deleted and no new list is saved. + * Do not show any messages here as this may be called on receiving + * WM_ENDSESSION (user logout). + */ +static void +SaveAutoRestartList() +{ + size_t len = 0; + int nactive = 0; + int max_active = o.num_configs - CountConnState(disconnected); + + if (!o.enable_auto_restart || max_active <= 0) + { + /* delete the list -- on load this gets treated as an empty list */ + RegDeleteKeyValueW(HKEY_CURRENT_USER, GUI_REGKEY_HKCU, L"auto_restart_list"); + return; + } + + int *active_conns = malloc((size_t) max_active*sizeof(int)); + + if (!active_conns) + { + MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Out of memory while persisting state in registry"); + return; + } + + for (int i = 0; i < o.num_configs; i++) + { + if (o.conn[i].state == disconnected + || o.conn[i].flags & FLAG_DAEMON_PERSISTENT) + { + continue; + } + /* accumulate space needed for list of active connections */ + len += wcslen(o.conn[i].config_name) + 1; + active_conns[nactive++] = i; + } + len++; /* for double nul termination */ + + if (len == 1) len++; /* two nuls for empty string */ + + /* Make a double nul terminated list of active connections */ + wchar_t *list = calloc(len, sizeof(wchar_t)); + if (!list) + { + free(active_conns); + MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Out of memory while persisting state in registry"); + return; + } + + wchar_t *p = list; + for (int i = 0; i < nactive; i++) + { + connection_t *c = &o.conn[active_conns[i]]; + wcscpy(p, c->config_name); /* wcscpy is safe here */ + p += wcslen(c->config_name) + 1; + } + + /* Save the list in registry for auto-connect on restart */ + LSTATUS status = RegSetKeyValueW(HKEY_CURRENT_USER, GUI_REGKEY_HKCU, L"auto_restart_list", + REG_MULTI_SZ, list, (DWORD) len*sizeof(wchar_t)); + if (status != ERROR_SUCCESS) + { + MsgToEventLog(EVENTLOG_ERROR_TYPE, L"RegSetKeyValue returned error: status = %lu", status); + } + + free(active_conns); + free(list); +} + +/* + * Read list of active connections in last session and set them to auto-start + */ +static void +LoadAutoRestartList() +{ + wchar_t *list; + DWORD len = 0; + + LSTATUS status = RegGetValueW(HKEY_CURRENT_USER, GUI_REGKEY_HKCU, L"auto_restart_list", + RRF_RT_REG_MULTI_SZ, NULL, NULL, &len); + if (status != ERROR_SUCCESS || len == 0) + { + return; + } + + list = malloc(len); + if (!list) + { + MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Out of memory while reading state from registry"); + return; + } + + status = RegGetValueW(HKEY_CURRENT_USER, GUI_REGKEY_HKCU, L"auto_restart_list", + RRF_RT_REG_MULTI_SZ, NULL, list, &len); + if (status != ERROR_SUCCESS) + { + MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Error reading state from registry"); + free(list); + return; + } + + for (wchar_t *p = list; *p; p += wcslen(p) + 1) + { + connection_t *c = GetConnByName(p); + if (!c || c->flags & FLAG_DAEMON_PERSISTENT) + { + continue; + } + c->auto_connect = true; + } +} diff --git a/options.h b/options.h index ab4bcbd..ac2dc54 100644 --- a/options.h +++ b/options.h @@ -224,6 +224,7 @@ typedef struct { DWORD ovpn_engine; /* 0 - openvpn2, 1 - openvpn3 */ DWORD enable_persistent; /* 0 - disabled, 1 - enabled, 2 - enabled & auto attach */ + DWORD enable_auto_restart; /* 0 - disabled, >0 enabled */ #ifdef DEBUG FILE *debug_fp; #endif diff --git a/registry.c b/registry.c index 8ec96e5..88cb17b 100644 --- a/registry.c +++ b/registry.c @@ -66,6 +66,7 @@ struct regkey_int { {L"disable_popup_messages", &o.disable_popup_messages, 0}, {L"management_port_offset", &o.mgmt_port_offset, 25340}, {L"enable_peristent_connections", &o.enable_persistent, 2}, + {L"enable_auto_restart", &o.enable_auto_restart, 1}, {L"ovpn_engine", &o.ovpn_engine, OPENVPN_ENGINE_OVPN2} };