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. */