From b89d17fbca4271a476b5faeaf84bc6f0cac532bc Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Wed, 1 Jan 2020 11:10:22 +0000 Subject: [PATCH] Centralise implementations of Windows do_select(). Windows Plink and PSFTP had very similar implementations, and now they share one that lives in a new file winselcli.c. I've similarly moved GUI PuTTY's implementation out of window.c into winselgui.c, where other GUI programs wanting to do networking will be able to access that too. In the spirit of centralisation, I've also taken the opportunity to make both functions use the reasonably complete winsock_error_string() rather than (for some historical reason) each inlining a minimal version that reports most errors as 'unknown'. --- Recipe | 8 ++--- windows/window.c | 27 +--------------- windows/winplink.c | 26 ++------------- windows/winselcli.c | 78 +++++++++++++++++++++++++++++++++++++++++++++ windows/winselgui.c | 38 ++++++++++++++++++++++ windows/winsftp.c | 52 ++++++------------------------ windows/winstuff.h | 17 ++++++++-- 7 files changed, 147 insertions(+), 99 deletions(-) create mode 100644 windows/winselcli.c create mode 100644 windows/winselgui.c diff --git a/Recipe b/Recipe index 4bad3f33..077ae7cf 100644 --- a/Recipe +++ b/Recipe @@ -244,7 +244,7 @@ TERMINAL = terminal stripctrl wcwidth logging tree234 minibidi # GUI front end and terminal emulator (putty, puttytel). GUITERM = TERMINAL window windlg winctrls sizetip winprint winutils - + wincfg sercfg winhelp winjump sessprep + + wincfg sercfg winhelp winjump sessprep winselgui # Same thing on Unix. UXTERM = TERMINAL uxcfg sercfg uxucs uxprint timing callback miscucs @@ -331,11 +331,11 @@ U_BE_NOSSH = be_nos_s uxser nocproxy putty : [G] GUITERM NONSSH WINSSH W_BE_ALL WINMISC winx11 putty.res LIBS puttytel : [G] GUITERM NONSSH W_BE_NOSSH WINMISC puttytel.res nogss LIBS plink : [C] winplink wincons NONSSH WINSSH W_BE_ALL logging WINMISC - + winx11 plink.res winnojmp sessprep noterm winnohlp LIBS + + winx11 plink.res winnojmp sessprep noterm winnohlp winselcli LIBS pscp : [C] pscp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC - + pscp.res winnojmp winnohlp LIBS + + pscp.res winnojmp winnohlp winselcli LIBS psftp : [C] psftp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC - + psftp.res winnojmp winnohlp LIBS + + psftp.res winnojmp winnohlp winselcli LIBS pageant : [G] winpgnt pageant sshrsa sshpubk sshdes ARITH sshmd5 version + tree234 MISC sshaes sshsha winsecur winpgntc aqsync sshdss sshsh256 diff --git a/windows/window.c b/windows/window.c index a17a9854..6264b6d9 100644 --- a/windows/window.c +++ b/windows/window.c @@ -871,6 +871,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) lp_eventlog(default_logpolicy, "Running with restricted process ACL"); } + winselgui_set_hwnd(hwnd); start_backend(); /* @@ -1023,32 +1024,6 @@ void cleanup_exit(int code) exit(code); } -/* - * Set up, or shut down, an AsyncSelect. Called from winnet.c. - */ -char *do_select(SOCKET skt, bool enable) -{ - int msg, events; - if (enable) { - msg = WM_NETEVENT; - events = (FD_CONNECT | FD_READ | FD_WRITE | - FD_OOB | FD_CLOSE | FD_ACCEPT); - } else { - msg = events = 0; - } - if (!hwnd) - return "do_select(): internal error (hwnd==NULL)"; - if (p_WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) { - switch (p_WSAGetLastError()) { - case WSAENETDOWN: - return "Network is down"; - default: - return "WSAAsyncSelect(): unknown error"; - } - } - return NULL; -} - /* * Refresh the saved-session submenu from `sesslist'. */ diff --git a/windows/winplink.c b/windows/winplink.c index 39ca8fb5..b3054b8a 100644 --- a/windows/winplink.c +++ b/windows/winplink.c @@ -38,8 +38,6 @@ StripCtrlChars *stdout_scc, *stderr_scc; BinarySink *stdout_bs, *stderr_bs; DWORD orig_console_mode; -WSAEVENT netevent; - static Backend *backend; Conf *conf; @@ -190,26 +188,6 @@ static void version(void) exit(0); } -char *do_select(SOCKET skt, bool enable) -{ - int events; - if (enable) { - events = (FD_CONNECT | FD_READ | FD_WRITE | - FD_OOB | FD_CLOSE | FD_ACCEPT); - } else { - events = 0; - } - if (p_WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) { - switch (p_WSAGetLastError()) { - case WSAENETDOWN: - return "Network is down"; - default: - return "WSAEventSelect(): unknown error"; - } - } - return NULL; -} - size_t stdin_gotdata(struct handle *h, const void *data, size_t len, int err) { if (err) { @@ -502,7 +480,7 @@ int main(int argc, char **argv) /* * Start up the connection. */ - netevent = CreateEvent(NULL, false, false, NULL); + winselcli_setup(); /* ensure event object exists */ { const char *error; char *realhost; @@ -558,7 +536,7 @@ int main(int argc, char **argv) handles = handle_get_events(&nhandles); handles = sresize(handles, nhandles+1, HANDLE); - handles[nhandles] = netevent; + handles[nhandles] = winselcli_event; n = MsgWaitForMultipleObjects(nhandles+1, handles, false, ticks, QS_POSTMESSAGE); if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) { diff --git a/windows/winselcli.c b/windows/winselcli.c new file mode 100644 index 00000000..c19706d7 --- /dev/null +++ b/windows/winselcli.c @@ -0,0 +1,78 @@ +/* + * Implementation of do_select() for winnet.c to use, suitable for use + * when there's no GUI window to have network activity reported to. + * + * It uses WSAEventSelect, where available, to convert network + * activity into activity on an event object, for integration into an + * event loop that includes WaitForMultipleObjects. + * + * It also maintains a list of currently active sockets, which can be + * retrieved by a front end that wants to use WinSock's synchronous + * select() function. + */ + +#include "putty.h" + +static tree234 *winselcli_sockets; + +static int socket_cmp(void *av, void *bv) +{ + return memcmp(av, bv, sizeof(SOCKET)); +} + +HANDLE winselcli_event = INVALID_HANDLE_VALUE; + +void winselcli_setup(void) +{ + if (!winselcli_sockets) + winselcli_sockets = newtree234(socket_cmp); + + if (p_WSAEventSelect && winselcli_event == INVALID_HANDLE_VALUE) + winselcli_event = CreateEvent(NULL, false, false, NULL); +} + +SOCKET winselcli_unique_socket(void) +{ + if (!winselcli_sockets) + return INVALID_SOCKET; + + assert(count234(winselcli_sockets) <= 1); + + SOCKET *p = index234(winselcli_sockets, 0); + if (!p) + return INVALID_SOCKET; + + return *p; +} + +char *do_select(SOCKET skt, bool enable) +{ + /* Check everything's been set up, for convenience of callers. */ + winselcli_setup(); + + if (enable) { + SOCKET *ptr = snew(SOCKET); + *ptr = skt; + if (add234(winselcli_sockets, ptr) != ptr) + sfree(ptr); /* already there */ + } else { + SOCKET *ptr = del234(winselcli_sockets, &skt); + if (ptr) + sfree(ptr); + } + + if (p_WSAEventSelect) { + int events; + if (enable) { + events = (FD_CONNECT | FD_READ | FD_WRITE | + FD_OOB | FD_CLOSE | FD_ACCEPT); + } else { + events = 0; + } + + if (p_WSAEventSelect(skt, winselcli_event, events) == SOCKET_ERROR) + return winsock_error_string(p_WSAGetLastError()); + } + + return NULL; +} diff --git a/windows/winselgui.c b/windows/winselgui.c new file mode 100644 index 00000000..1dc376bd --- /dev/null +++ b/windows/winselgui.c @@ -0,0 +1,38 @@ +/* + * Implementation of do_select() for winnet.c to use, that uses + * WSAAsyncSelect to convert network activity into window messages, + * for integration into a GUI event loop. + */ + +#include "putty.h" + +static HWND winsel_hwnd = NULL; + +void winselgui_set_hwnd(HWND hwnd) +{ + winsel_hwnd = hwnd; +} + +void winselgui_clear_hwnd(void) +{ + winsel_hwnd = NULL; +} + +char *do_select(SOCKET skt, bool enable) +{ + int msg, events; + if (enable) { + msg = WM_NETEVENT; + events = (FD_CONNECT | FD_READ | FD_WRITE | + FD_OOB | FD_CLOSE | FD_ACCEPT); + } else { + msg = events = 0; + } + + assert(winsel_hwnd); + + if (p_WSAAsyncSelect(skt, winsel_hwnd, msg, events) == SOCKET_ERROR) + return winsock_error_string(p_WSAGetLastError()); + + return NULL; +} diff --git a/windows/winsftp.c b/windows/winsftp.c index 71c28ac0..c38fd9b6 100644 --- a/windows/winsftp.c +++ b/windows/winsftp.c @@ -462,41 +462,6 @@ char *dir_file_cat(const char *dir, const char *file) * Platform-specific network handling. */ -/* - * Be told what socket we're supposed to be using. - */ -static SOCKET sftp_ssh_socket = INVALID_SOCKET; -static HANDLE netevent = INVALID_HANDLE_VALUE; -char *do_select(SOCKET skt, bool enable) -{ - int events; - if (enable) - sftp_ssh_socket = skt; - else - sftp_ssh_socket = INVALID_SOCKET; - - if (netevent == INVALID_HANDLE_VALUE) - netevent = CreateEvent(NULL, false, false, NULL); - - if (p_WSAEventSelect) { - if (enable) { - events = (FD_CONNECT | FD_READ | FD_WRITE | - FD_OOB | FD_CLOSE | FD_ACCEPT); - } else { - events = 0; - } - if (p_WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) { - switch (p_WSAGetLastError()) { - case WSAENETDOWN: - return "Network is down"; - default: - return "WSAEventSelect(): unknown error"; - } - } - } - return NULL; -} - int do_eventsel_loop(HANDLE other_event) { int n, nhandles, nallhandles, netindex, otherindex; @@ -527,8 +492,8 @@ int do_eventsel_loop(HANDLE other_event) handles = sresize(handles, nhandles+2, HANDLE); nallhandles = nhandles; - if (netevent != INVALID_HANDLE_VALUE) - handles[netindex = nallhandles++] = netevent; + if (winselcli_event != INVALID_HANDLE_VALUE) + handles[netindex = nallhandles++] = winselcli_event; else netindex = -1; if (other_event != INVALID_HANDLE_VALUE) @@ -630,12 +595,13 @@ int ssh_sftp_loop_iteration(void) fd_set readfds; int ret; unsigned long now = GETTICKCOUNT(), then; + SOCKET skt = winselcli_unique_socket(); - if (sftp_ssh_socket == INVALID_SOCKET) + if (skt == INVALID_SOCKET) return -1; /* doom */ - if (socket_writable(sftp_ssh_socket)) - select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_WRITE); + if (socket_writable(skt)) + select_result((WPARAM) skt, (LPARAM) FD_WRITE); do { unsigned long next; @@ -657,7 +623,7 @@ int ssh_sftp_loop_iteration(void) } FD_ZERO(&readfds); - FD_SET(sftp_ssh_socket, &readfds); + FD_SET(skt, &readfds); ret = p_select(1, &readfds, NULL, NULL, ptv); if (ret < 0) @@ -669,7 +635,7 @@ int ssh_sftp_loop_iteration(void) } while (ret == 0); - select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ); + select_result((WPARAM) skt, (LPARAM) FD_READ); return 0; } else { @@ -712,7 +678,7 @@ char *ssh_sftp_get_cmdline(const char *prompt, bool no_fds_ok) fputs(prompt, stdout); fflush(stdout); - if ((sftp_ssh_socket == INVALID_SOCKET && no_fds_ok) || + if ((winselcli_unique_socket() == INVALID_SOCKET && no_fds_ok) || p_WSAEventSelect == NULL) { return fgetline(stdin); /* very simple */ } diff --git a/windows/winstuff.h b/windows/winstuff.h index c189c37a..1870b28e 100644 --- a/windows/winstuff.h +++ b/windows/winstuff.h @@ -319,6 +319,8 @@ bool socket_writable(SOCKET skt); void socket_reselect_all(void); /* Make a SockAddr which just holds a named pipe address. */ SockAddr *sk_namedpipe_addr(const char *pipename); +/* Turn a WinSock error code into a string. */ +const char *winsock_error_string(int error); /* * winnet.c dynamically loads WinSock 2 or WinSock 1 depending on @@ -347,11 +349,22 @@ DECL_WINDOWS_FUNCTION(GLOBAL, int, select, #endif /* - * Provided by each client of winnet.c, and called by winnet.c to turn - * on or off WSA*Select for a given socket. + * Implemented differently depending on the client of winnet.c, and + * called by winnet.c to turn on or off WSA*Select for a given socket. */ char *do_select(SOCKET skt, bool enable); +/* + * Exports from winselgui.c and winselcli.c, each of which provides an + * implementation of do_select. + */ +void winselgui_set_hwnd(HWND hwnd); +void winselgui_clear_hwnd(void); + +void winselcli_setup(void); +SOCKET winselcli_unique_socket(void); +extern HANDLE winselcli_event; + /* * Network-subsystem-related functions provided in other Windows modules. */