1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-06-30 19:12:48 -05:00

Revamp of EOF handling in all network connections, pipes and other

data channels. Should comprehensively fix 'half-closed', in principle,
though it's a big and complicated change and so there's a good chance
I've made at least one mistake somewhere.

All connections should now be rigorous about propagating end-of-file
(or end-of-data-stream, or socket shutdown, or whatever) independently
in both directions, except in frontends with no mechanism for sending
explicit EOF (e.g. interactive terminal windows) or backends which are
basically always used for interactive sessions so it's unlikely that
an application would be depending on independent EOF (telnet, rlogin).

EOF should now never accidentally be sent while there's still buffered
data to go out before it. (May help fix 'portfwd-corrupt', and also I
noticed recently that the ssh main session channel can accidentally
have MSG_EOF sent before the output bufchain is clear, leading to
embarrassment when it subsequently does send the output).

[originally from svn r9279]
This commit is contained in:
Simon Tatham
2011-09-13 11:44:03 +00:00
parent c68a646c64
commit 947962e0b9
23 changed files with 616 additions and 237 deletions

View File

@ -5612,6 +5612,11 @@ int from_backend_untrusted(void *frontend, const char *data, int len)
return term_data_untrusted(term, data, len);
}
int from_backend_eof(void *frontend)
{
return TRUE; /* do respond to incoming EOF with outgoing */
}
int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
{
int ret;

View File

@ -250,6 +250,7 @@ struct handle_output {
* Data only ever read or written by the main thread.
*/
bufchain queued_data; /* data still waiting to be written */
enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof;
/*
* Callback function called when the backlog in the bufchain
@ -320,6 +321,11 @@ static void handle_try_output(struct handle_output *ctx)
ctx->len = sendlen;
SetEvent(ctx->ev_from_main);
ctx->busy = TRUE;
} else if (!ctx->busy && bufchain_size(&ctx->queued_data) == 0 &&
ctx->outgoingeof == EOF_PENDING) {
CloseHandle(ctx->h);
ctx->h = INVALID_HANDLE_VALUE;
ctx->outgoingeof = EOF_SENT;
}
}
@ -408,6 +414,7 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata,
h->u.o.done = FALSE;
h->u.o.privdata = privdata;
bufchain_init(&h->u.o.queued_data);
h->u.o.outgoingeof = EOF_NO;
h->u.o.sentdata = sentdata;
h->u.o.flags = flags;
@ -424,11 +431,28 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata,
int handle_write(struct handle *h, const void *data, int len)
{
assert(h->output);
assert(h->u.o.outgoingeof == EOF_NO);
bufchain_add(&h->u.o.queued_data, data, len);
handle_try_output(&h->u.o);
return bufchain_size(&h->u.o.queued_data);
}
void handle_write_eof(struct handle *h)
{
/*
* This function is called when we want to proactively send an
* end-of-file notification on the handle. We can only do this by
* actually closing the handle - so never call this on a
* bidirectional handle if we're still interested in its incoming
* direction!
*/
assert(h->output);
if (!h->u.o.outgoingeof == EOF_NO) {
h->u.o.outgoingeof = EOF_PENDING;
handle_try_output(&h->u.o);
}
}
HANDLE *handle_get_events(int *nevents)
{
HANDLE *ret;

View File

@ -64,6 +64,7 @@ struct Socket_tag {
char oobdata[1];
int sending_oob;
int oobinline, nodelay, keepalive, privport;
enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof;
SockAddr addr;
SockAddrStep step;
int port;
@ -167,6 +168,7 @@ DECL_WINDOWS_FUNCTION(static, int, setsockopt,
DECL_WINDOWS_FUNCTION(static, SOCKET, socket, (int, int, int));
DECL_WINDOWS_FUNCTION(static, int, listen, (SOCKET, int));
DECL_WINDOWS_FUNCTION(static, int, send, (SOCKET, const char FAR *, int, int));
DECL_WINDOWS_FUNCTION(static, int, shutdown, (SOCKET, int));
DECL_WINDOWS_FUNCTION(static, int, ioctlsocket,
(SOCKET, long, u_long FAR *));
DECL_WINDOWS_FUNCTION(static, SOCKET, accept,
@ -291,6 +293,7 @@ void sk_init(void)
GET_WINDOWS_FUNCTION(winsock_module, socket);
GET_WINDOWS_FUNCTION(winsock_module, listen);
GET_WINDOWS_FUNCTION(winsock_module, send);
GET_WINDOWS_FUNCTION(winsock_module, shutdown);
GET_WINDOWS_FUNCTION(winsock_module, ioctlsocket);
GET_WINDOWS_FUNCTION(winsock_module, accept);
GET_WINDOWS_FUNCTION(winsock_module, recv);
@ -745,6 +748,7 @@ static void sk_tcp_flush(Socket s)
static void sk_tcp_close(Socket s);
static int sk_tcp_write(Socket s, const char *data, int len);
static int sk_tcp_write_oob(Socket s, const char *data, int len);
static void sk_tcp_write_eof(Socket s);
static void sk_tcp_set_private_ptr(Socket s, void *ptr);
static void *sk_tcp_get_private_ptr(Socket s);
static void sk_tcp_set_frozen(Socket s, int is_frozen);
@ -759,6 +763,7 @@ Socket sk_register(void *sock, Plug plug)
sk_tcp_close,
sk_tcp_write,
sk_tcp_write_oob,
sk_tcp_write_eof,
sk_tcp_flush,
sk_tcp_set_private_ptr,
sk_tcp_get_private_ptr,
@ -780,6 +785,7 @@ Socket sk_register(void *sock, Plug plug)
bufchain_init(&ret->output_data);
ret->writable = 1; /* to start with */
ret->sending_oob = 0;
ret->outgoingeof = EOF_NO;
ret->frozen = 1;
ret->frozen_readable = 0;
ret->localhost_only = 0; /* unused, but best init anyway */
@ -1007,6 +1013,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
sk_tcp_close,
sk_tcp_write,
sk_tcp_write_oob,
sk_tcp_write_eof,
sk_tcp_flush,
sk_tcp_set_private_ptr,
sk_tcp_get_private_ptr,
@ -1028,6 +1035,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
ret->connected = 0; /* to start with */
ret->writable = 0; /* to start with */
ret->sending_oob = 0;
ret->outgoingeof = EOF_NO;
ret->frozen = 0;
ret->frozen_readable = 0;
ret->localhost_only = 0; /* unused, but best init anyway */
@ -1058,6 +1066,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
sk_tcp_close,
sk_tcp_write,
sk_tcp_write_oob,
sk_tcp_write_eof,
sk_tcp_flush,
sk_tcp_set_private_ptr,
sk_tcp_get_private_ptr,
@ -1089,6 +1098,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
bufchain_init(&ret->output_data);
ret->writable = 0; /* to start with */
ret->sending_oob = 0;
ret->outgoingeof = EOF_NO;
ret->frozen = 0;
ret->frozen_readable = 0;
ret->localhost_only = local_host_only;
@ -1325,12 +1335,23 @@ void try_send(Actual_Socket s)
}
}
}
/*
* If we reach here, we've finished sending everything we might
* have needed to send. Send EOF, if we need to.
*/
if (s->outgoingeof == EOF_PENDING) {
p_shutdown(s->s, SD_SEND);
s->outgoingeof = EOF_SENT;
}
}
static int sk_tcp_write(Socket sock, const char *buf, int len)
{
Actual_Socket s = (Actual_Socket) sock;
assert(s->outgoingeof == EOF_NO);
/*
* Add the data to the buffer list on the socket.
*/
@ -1349,6 +1370,8 @@ static int sk_tcp_write_oob(Socket sock, const char *buf, int len)
{
Actual_Socket s = (Actual_Socket) sock;
assert(s->outgoingeof == EOF_NO);
/*
* Replace the buffer list on the socket with the data.
*/
@ -1366,6 +1389,24 @@ static int sk_tcp_write_oob(Socket sock, const char *buf, int len)
return s->sending_oob;
}
static void sk_tcp_write_eof(Socket sock)
{
Actual_Socket s = (Actual_Socket) sock;
assert(s->outgoingeof == EOF_NO);
/*
* Mark the socket as pending outgoing EOF.
*/
s->outgoingeof = EOF_PENDING;
/*
* Now try sending from the start of the buffer list.
*/
if (s->writable)
try_send(s);
}
int select_result(WPARAM wParam, LPARAM lParam)
{
int ret, open;

View File

@ -130,6 +130,12 @@ int from_backend_untrusted(void *frontend_handle, const char *data, int len)
return 0; /* not reached */
}
int from_backend_eof(void *frontend_handle)
{
handle_write_eof(stdout_handle);
return FALSE; /* do not respond to incoming EOF with outgoing */
}
int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
{
int ret;

View File

@ -87,6 +87,13 @@ static int sk_localproxy_write_oob(Socket s, const char *data, int len)
return sk_localproxy_write(s, data, len);
}
static void sk_localproxy_write_eof(Socket s)
{
Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
handle_write_eof(ps->to_cmd_h);
}
static void sk_localproxy_flush(Socket s)
{
/* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */
@ -132,6 +139,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname,
sk_localproxy_close,
sk_localproxy_write,
sk_localproxy_write_oob,
sk_localproxy_write_eof,
sk_localproxy_flush,
sk_localproxy_set_private_ptr,
sk_localproxy_get_private_ptr,

View File

@ -489,6 +489,7 @@ struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata,
struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata,
void *privdata, int flags);
int handle_write(struct handle *h, const void *data, int len);
void handle_write_eof(struct handle *h);
HANDLE *handle_get_events(int *nevents);
void handle_free(struct handle *h);
void handle_got_event(HANDLE event);