]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
Install a custom signal handler on Windows using SetConsoleCtrlHandler()
authorTim De Baets <10608063-tdebaets@users.noreply.gitlab.com>
Tue, 11 Jan 2022 17:40:28 +0000 (18:40 +0100)
committerDaniel Lenski <dlenski@gmail.com>
Thu, 13 Jan 2022 18:20:44 +0000 (10:20 -0800)
This fixes the longstanding bug
https://gitlab.com/openconnect/openconnect/-/issues/362, wherein the
vpnc-script never gets called to do any routing cleanup on Windows.

Also added checking for the number of characters returned by ReadConsole() so
that we still exit when receiving a control signal while waiting for user
input.

Signed-off-by: Tim De Baets <10608063-tdebaets@users.noreply.gitlab.com>
main.c

diff --git a/main.c b/main.c
index 67225b501dd46e555d0525db66179c49fd527fd0..1dcf1f1f3c9ed61dccda843b30d88ee667280988 100644 (file)
--- a/main.c
+++ b/main.c
@@ -420,7 +420,7 @@ static void read_stdin(char **string, int hidden, int allow_fail)
 {
        CONSOLE_READCONSOLE_CONTROL rcc = { sizeof(rcc), 0, 13, 0 };
        HANDLE stdinh = GetStdHandle(STD_INPUT_HANDLE);
-       DWORD cmode, nr_read;
+       DWORD cmode, nr_read, last_error;
        wchar_t wbuf[1024];
        char *buf;
 
@@ -428,6 +428,8 @@ static void read_stdin(char **string, int hidden, int allow_fail)
                if (hidden)
                        SetConsoleMode(stdinh, cmode & (~ENABLE_ECHO_INPUT));
 
+               SetLastError(0);
+
                if (!ReadConsoleW(stdinh, wbuf, sizeof(wbuf)/2, &nr_read, &rcc)) {
                        char *errstr = openconnect__win32_strerror(GetLastError());
                        fprintf(stderr, _("ReadConsole() failed: %s\n"), errstr);
@@ -437,8 +439,26 @@ static void read_stdin(char **string, int hidden, int allow_fail)
                                SetConsoleMode(stdinh, cmode);
                        return;
                }
+
+               last_error = GetLastError();
+
                if (hidden)
                        SetConsoleMode(stdinh, cmode);
+
+               if (!nr_read) {
+                       if (allow_fail) {
+                               *string = NULL;
+                               return;
+                       } else {
+                               if (last_error == ERROR_OPERATION_ABORTED) {
+                                       fprintf(stderr, _("Operation aborted by user\n"));
+                               } else {
+                                       /* Should never happen */
+                                       fprintf(stderr, _("ReadConsole() didn't read any input\n"));
+                               }
+                               exit(1);
+                       }
+               }
        } else {
                /* Not a console; maybe reading from a piped stdin? */
                if (!fgetws(wbuf, sizeof(wbuf)/2, stdin)) {
@@ -825,6 +845,33 @@ static void set_default_vpncscript(void)
                default_vpncscript = "cscript " DEFAULT_VPNCSCRIPT;
        }
 }
+
+static BOOL WINAPI console_ctrl_handler(DWORD dwCtrlType)
+{
+       char cmd;
+
+       /* Note: this function always runs in a separate thread */
+
+       switch (dwCtrlType) {
+       case CTRL_C_EVENT:
+       case CTRL_BREAK_EVENT:
+       case CTRL_CLOSE_EVENT:
+       case CTRL_LOGOFF_EVENT:
+       case CTRL_SHUTDOWN_EVENT:
+               cmd = OC_CMD_CANCEL;
+               break;
+       default:
+               return FALSE;
+       }
+
+       /* Use send() here since, on Windows, sig_cmd_fd is a socket descriptor */
+       send(sig_cmd_fd, &cmd, 1, 0);
+
+       if (sig_vpninfo)
+               sig_vpninfo->need_poll_cmd_fd = 1;
+
+       return TRUE;
+}
 #endif
 
 static struct oc_vpn_option *gai_overrides;
@@ -2121,7 +2168,9 @@ int main(int argc, char **argv)
        sigaction(SIGHUP, &sa, NULL);
        sigaction(SIGUSR1, &sa, NULL);
        sigaction(SIGUSR2, &sa, NULL);
-#endif /* !_WIN32 */
+#else /* _WIN32 */
+       SetConsoleCtrlHandler(console_ctrl_handler, TRUE /* Add */);
+#endif
 
        sig_vpninfo = vpninfo;
        sig_cmd_fd = openconnect_setup_cmd_pipe(vpninfo);