mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 01:48:00 +00:00
c6ff548ae0
I found recently that if I ran Windows PSCP as a connection-sharing downstream, it would send the SSH greeting down the named pipe, but never receive anything back, though the upstream PuTTY was sending it. PuTTY and Plink from the same build of the code would act happily as downstreams. It turned out that this was because the WaitForMultipleObjects call in cli_main_loop() in wincliloop.c was failing with ERROR_ACCESS_DENIED. That happened because it had an INVALID_HANDLE_VALUE in its list of objects to wait for. That in turn happened because winselcli_event was set to INVALID_HANDLE_VALUE. Why was winselcli_event not set up? Because it's set up lazily by do_select(), so if the program isn't handling any network sockets at all (which is the case when PSCP is speaking over a named pipe instead), then it never gets made into a valid event object. So the problem wasn't that winselcli_event was in a bad state; it was quite legitimately invalid. The problem was that wincliloop ought to have _coped_ with it being invalid, by not inserting it in its list of objects to wait for. So now we check that case, and only insert winselcli_event in the list if it's valid. And PSCP works again over connection sharing.
134 lines
4.5 KiB
C
134 lines
4.5 KiB
C
#include "putty.h"
|
|
|
|
void cli_main_loop(cliloop_pre_t pre, cliloop_post_t post, void *ctx)
|
|
{
|
|
SOCKET *sklist = NULL;
|
|
size_t skcount = 0, sksize = 0;
|
|
unsigned long now, next, then;
|
|
now = GETTICKCOUNT();
|
|
|
|
while (true) {
|
|
int nhandles;
|
|
HANDLE *handles;
|
|
DWORD n;
|
|
DWORD ticks;
|
|
|
|
const HANDLE *extra_handles = NULL;
|
|
size_t n_extra_handles = 0;
|
|
if (!pre(ctx, &extra_handles, &n_extra_handles))
|
|
break;
|
|
|
|
if (toplevel_callback_pending()) {
|
|
ticks = 0;
|
|
next = now;
|
|
} else if (run_timers(now, &next)) {
|
|
then = now;
|
|
now = GETTICKCOUNT();
|
|
if (now - then > next - then)
|
|
ticks = 0;
|
|
else
|
|
ticks = next - now;
|
|
} else {
|
|
ticks = INFINITE;
|
|
/* no need to initialise next here because we can never
|
|
* get WAIT_TIMEOUT */
|
|
}
|
|
|
|
handles = handle_get_events(&nhandles);
|
|
size_t winselcli_index = -(size_t)1;
|
|
size_t extra_base = nhandles;
|
|
if (winselcli_event != INVALID_HANDLE_VALUE) {
|
|
winselcli_index = extra_base++;
|
|
handles[winselcli_index] = winselcli_event;
|
|
}
|
|
size_t total_handles = extra_base + n_extra_handles;
|
|
handles = sresize(handles, total_handles, HANDLE);
|
|
for (size_t i = 0; i < n_extra_handles; i++)
|
|
handles[extra_base + i] = extra_handles[i];
|
|
|
|
n = WaitForMultipleObjects(total_handles, handles, false, ticks);
|
|
|
|
size_t extra_handle_index = n_extra_handles;
|
|
|
|
if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) {
|
|
handle_got_event(handles[n - WAIT_OBJECT_0]);
|
|
} else if (winselcli_event != INVALID_HANDLE_VALUE &&
|
|
n == WAIT_OBJECT_0 + winselcli_index) {
|
|
WSANETWORKEVENTS things;
|
|
SOCKET socket;
|
|
int i, socketstate;
|
|
|
|
/*
|
|
* We must not call select_result() for any socket
|
|
* until we have finished enumerating within the tree.
|
|
* This is because select_result() may close the socket
|
|
* and modify the tree.
|
|
*/
|
|
/* Count the active sockets. */
|
|
i = 0;
|
|
for (socket = first_socket(&socketstate);
|
|
socket != INVALID_SOCKET;
|
|
socket = next_socket(&socketstate)) i++;
|
|
|
|
/* Expand the buffer if necessary. */
|
|
sgrowarray(sklist, sksize, i);
|
|
|
|
/* Retrieve the sockets into sklist. */
|
|
skcount = 0;
|
|
for (socket = first_socket(&socketstate);
|
|
socket != INVALID_SOCKET;
|
|
socket = next_socket(&socketstate)) {
|
|
sklist[skcount++] = socket;
|
|
}
|
|
|
|
/* Now we're done enumerating; go through the list. */
|
|
for (i = 0; i < skcount; i++) {
|
|
WPARAM wp;
|
|
socket = sklist[i];
|
|
wp = (WPARAM) socket;
|
|
if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) {
|
|
static const struct { int bit, mask; } eventtypes[] = {
|
|
{FD_CONNECT_BIT, FD_CONNECT},
|
|
{FD_READ_BIT, FD_READ},
|
|
{FD_CLOSE_BIT, FD_CLOSE},
|
|
{FD_OOB_BIT, FD_OOB},
|
|
{FD_WRITE_BIT, FD_WRITE},
|
|
{FD_ACCEPT_BIT, FD_ACCEPT},
|
|
};
|
|
int e;
|
|
|
|
noise_ultralight(NOISE_SOURCE_IOID, socket);
|
|
|
|
for (e = 0; e < lenof(eventtypes); e++)
|
|
if (things.lNetworkEvents & eventtypes[e].mask) {
|
|
LPARAM lp;
|
|
int err = things.iErrorCode[eventtypes[e].bit];
|
|
lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err);
|
|
select_result(wp, lp);
|
|
}
|
|
}
|
|
}
|
|
} else if (n >= WAIT_OBJECT_0 + extra_base &&
|
|
n < WAIT_OBJECT_0 + extra_base + n_extra_handles) {
|
|
extra_handle_index = n - (WAIT_OBJECT_0 + extra_base);
|
|
}
|
|
|
|
run_toplevel_callbacks();
|
|
|
|
if (n == WAIT_TIMEOUT) {
|
|
now = next;
|
|
} else {
|
|
now = GETTICKCOUNT();
|
|
}
|
|
|
|
sfree(handles);
|
|
|
|
if (!post(ctx, extra_handle_index))
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool cliloop_null_pre(void *vctx, const HANDLE **eh, size_t *neh)
|
|
{ return true; }
|
|
bool cliloop_null_post(void *vctx, size_t ehi) { return true; }
|