Replace enum+union of local channel types with a vtable.

There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.

Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.

I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
This commit is contained in:
Simon Tatham 2018-09-12 15:03:47 +01:00
Родитель 8dfb2a1186
Коммит 6a8b9d3813
9 изменённых файлов: 785 добавлений и 569 удалений

2
Recipe
Просмотреть файл

@ -254,7 +254,7 @@ SSH = ssh ssh1bpp ssh2bpp ssh2bpp-bare ssh1censor ssh2censor
+ sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf + sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf
+ sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd + sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd
+ sshaes sshccp sshsh256 sshsh512 sshbn wildcard pinger ssharcf + sshaes sshccp sshsh256 sshsh512 sshbn wildcard pinger ssharcf
+ sshgssc pgssapi sshshare sshecc aqsync marshal nullplug + sshgssc pgssapi sshshare sshecc aqsync marshal nullplug agentf
WINSSH = SSH winnoise wincapi winpgntc wingss winshare winnps winnpc WINSSH = SSH winnoise wincapi winpgntc wingss winshare winnps winnpc
+ winhsock errsock + winhsock errsock
UXSSH = SSH uxnoise uxagentc uxgss uxshare UXSSH = SSH uxnoise uxagentc uxgss uxshare

235
agentf.c Normal file
Просмотреть файл

@ -0,0 +1,235 @@
/*
* SSH agent forwarding.
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "putty.h"
#include "ssh.h"
#include "pageant.h"
#include "sshchan.h"
typedef struct agentf {
struct ssh_channel *c;
bufchain inbuffer;
agent_pending_query *pending;
int input_wanted;
int rcvd_eof;
Channel chan;
} agentf;
static void agentf_got_response(agentf *af, void *reply, int replylen)
{
af->pending = NULL;
if (!reply) {
/* The real agent didn't send any kind of reply at all for
* some reason, so fake an SSH_AGENT_FAILURE. */
reply = "\0\0\0\1\5";
replylen = 5;
}
sshfwd_write(af->c, reply, replylen);
}
static void agentf_callback(void *vctx, void *reply, int replylen);
static void agentf_try_forward(agentf *af)
{
unsigned datalen, length;
strbuf *message;
unsigned char msglen[4];
void *reply;
int replylen;
/*
* Don't try to parallelise agent requests. Wait for each one to
* return before attempting the next.
*/
if (af->pending)
return;
/*
* If the outgoing side of the channel connection is currently
* throttled, don't submit any new forwarded requests to the real
* agent. This causes the input side of the agent forwarding not
* to be emptied, exerting the required back-pressure on the
* remote client, and encouraging it to read our responses before
* sending too many more requests.
*/
if (!af->input_wanted)
return;
while (1) {
/*
* Try to extract a complete message from the input buffer.
*/
datalen = bufchain_size(&af->inbuffer);
if (datalen < 4)
break; /* not even a length field available yet */
bufchain_fetch(&af->inbuffer, msglen, 4);
length = GET_32BIT(msglen);
if (length > AGENT_MAX_MSGLEN-4) {
/*
* If the remote has sent a message that's just _too_
* long, we should reject it in advance of seeing the rest
* of the incoming message, and also close the connection
* for good measure (which avoids us having to faff about
* with carefully ignoring just the right number of bytes
* from the overlong message).
*/
agentf_got_response(af, NULL, 0);
sshfwd_write_eof(af->c);
return;
}
if (length > datalen - 4)
break; /* a whole message is not yet available */
bufchain_consume(&af->inbuffer, 4);
message = strbuf_new_for_agent_query();
bufchain_fetch_consume(
&af->inbuffer, strbuf_append(message, length), length);
af->pending = agent_query(
message, &reply, &replylen, agentf_callback, af);
strbuf_free(message);
if (af->pending)
return; /* agent_query promised to reply in due course */
/*
* If the agent gave us an answer immediately, pass it
* straight on and go round this loop again.
*/
agentf_got_response(af, reply, replylen);
sfree(reply);
}
/*
* If we get here (i.e. we left the above while loop via 'break'
* rather than 'return'), that means we've determined that the
* input buffer for the agent forwarding connection doesn't
* contain a complete request.
*
* So if there's potentially more data to come, we can return now,
* and wait for the remote client to send it. But if the remote
* has sent EOF, it would be a mistake to do that, because we'd be
* waiting a long time. So this is the moment to check for EOF,
* and respond appropriately.
*/
if (af->rcvd_eof)
sshfwd_write_eof(af->c);
}
static void agentf_callback(void *vctx, void *reply, int replylen)
{
agentf *af = (agentf *)vctx;
agentf_got_response(af, reply, replylen);
sfree(reply);
/*
* Now try to extract and send further messages from the channel's
* input-side buffer.
*/
agentf_try_forward(af);
}
static void agentf_free(Channel *chan);
static int agentf_send(Channel *chan, int is_stderr, const void *, int);
static void agentf_send_eof(Channel *chan);
static char *agentf_log_close_msg(Channel *chan);
static void agentf_set_input_wanted(Channel *chan, int wanted);
static const struct ChannelVtable agentf_channelvt = {
agentf_free,
chan_remotely_opened_confirmation,
chan_remotely_opened_failure,
agentf_send,
agentf_send_eof,
agentf_set_input_wanted,
agentf_log_close_msg,
chan_no_eager_close,
};
Channel *agentf_new(struct ssh_channel *c)
{
agentf *af = snew(agentf);
af->c = c;
af->chan.vt = &agentf_channelvt;
af->chan.initial_fixed_window_size = 0;
af->rcvd_eof = TRUE;
bufchain_init(&af->inbuffer);
af->pending = NULL;
af->input_wanted = TRUE;
return &af->chan;
}
static void agentf_free(Channel *chan)
{
assert(chan->vt == &agentf_channelvt);
agentf *af = FROMFIELD(chan, agentf, chan);
if (af->pending)
agent_cancel_query(af->pending);
bufchain_clear(&af->inbuffer);
sfree(af);
}
static int agentf_send(Channel *chan, int is_stderr,
const void *data, int length)
{
assert(chan->vt == &agentf_channelvt);
agentf *af = FROMFIELD(chan, agentf, chan);
bufchain_add(&af->inbuffer, data, length);
agentf_try_forward(af);
/*
* We exert back-pressure on an agent forwarding client if and
* only if we're waiting for the response to an asynchronous agent
* request. This prevents the client running out of window while
* receiving the _first_ message, but means that if any message
* takes time to process, the client will be discouraged from
* sending an endless stream of further ones after it.
*/
return (af->pending ? bufchain_size(&af->inbuffer) : 0);
}
static void agentf_send_eof(Channel *chan)
{
assert(chan->vt == &agentf_channelvt);
agentf *af = FROMFIELD(chan, agentf, chan);
af->rcvd_eof = TRUE;
/* Call try_forward, which will respond to the EOF now if
* appropriate, or wait until the queue of outstanding requests is
* dealt with if not. */
agentf_try_forward(af);
}
static char *agentf_log_close_msg(Channel *chan)
{
return dupstr("Agent-forwarding connection closed");
}
static void agentf_set_input_wanted(Channel *chan, int wanted)
{
assert(chan->vt == &agentf_channelvt);
agentf *af = FROMFIELD(chan, agentf, chan);
af->input_wanted = wanted;
/* Agent forwarding channels are buffer-managed by not asking the
* agent questions if the SSH channel isn't accepting input. So if
* it's started again, we should ask a question if we have one
* pending.. */
if (wanted)
agentf_try_forward(af);
}

2
defs.h
Просмотреть файл

@ -53,6 +53,8 @@ typedef struct Frontend Frontend;
typedef struct ssh_tag *Ssh; typedef struct ssh_tag *Ssh;
typedef struct Channel Channel;
/* Note indirection: for historical reasons (it used to be closer to /* Note indirection: for historical reasons (it used to be closer to
* the OS socket type), the type that most code uses for a socket is * the OS socket type), the type that most code uses for a socket is
* 'Socket', not 'Socket *'. So an implementation of Socket or Plug * 'Socket', not 'Socket *'. So an implementation of Socket or Plug

143
portfwd.c
Просмотреть файл

@ -8,6 +8,7 @@
#include "putty.h" #include "putty.h"
#include "ssh.h" #include "ssh.h"
#include "sshchan.h"
/* /*
* Enumeration of values that live in the 'socks_state' field of * Enumeration of values that live in the 'socks_state' field of
@ -21,12 +22,12 @@ typedef enum {
SOCKS_5_CONNECT /* expect a SOCKS 5 connection message */ SOCKS_5_CONNECT /* expect a SOCKS 5 connection message */
} SocksState; } SocksState;
struct PortForwarding { typedef struct PortForwarding {
struct ssh_channel *c; /* channel structure held by ssh.c */ struct ssh_channel *c; /* channel structure held by ssh.c */
Ssh ssh; /* instance of SSH backend itself */ Ssh ssh; /* instance of SSH backend itself */
/* Note that ssh need not be filled in if c is non-NULL */ /* Note that ssh need not be filled in if c is non-NULL */
Socket s; Socket s;
int throttled, throttle_override; int input_wanted;
int ready; int ready;
SocksState socks_state; SocksState socks_state;
/* /*
@ -44,7 +45,8 @@ struct PortForwarding {
size_t socksbuf_consumed; size_t socksbuf_consumed;
const Plug_vtable *plugvt; const Plug_vtable *plugvt;
}; Channel chan;
} PortForwarding;
struct PortListener { struct PortListener {
Ssh ssh; /* instance of SSH backend itself */ Ssh ssh; /* instance of SSH backend itself */
@ -105,6 +107,8 @@ static void pfl_log(Plug plug, int type, SockAddr addr, int port,
/* we have to dump these since we have no interface to logging.c */ /* we have to dump these since we have no interface to logging.c */
} }
static void pfd_close(struct PortForwarding *pf);
static void pfd_closing(Plug plug, const char *error_msg, int error_code, static void pfd_closing(Plug plug, const char *error_msg, int error_code,
int calling_back) int calling_back)
{ {
@ -142,10 +146,12 @@ static void pfl_closing(Plug plug, const char *error_msg, int error_code,
pfl_terminate(pl); pfl_terminate(pl);
} }
static void wrap_send_port_open(void *channel, const char *hostname, int port, static struct ssh_channel *wrap_send_port_open(
Socket s) Ssh ssh, const char *hostname, int port, Socket s, Channel *chan)
{ {
char *peerinfo, *description; char *peerinfo, *description;
struct ssh_channel *toret;
peerinfo = sk_peer_info(s); peerinfo = sk_peer_info(s);
if (peerinfo) { if (peerinfo) {
description = dupprintf("forwarding from %s", peerinfo); description = dupprintf("forwarding from %s", peerinfo);
@ -153,8 +159,11 @@ static void wrap_send_port_open(void *channel, const char *hostname, int port,
} else { } else {
description = dupstr("forwarding"); description = dupstr("forwarding");
} }
ssh_send_port_open(channel, hostname, port, description);
toret = ssh_send_port_open(ssh, hostname, port, description, chan);
sfree(description); sfree(description);
return toret;
} }
static char *ipv4_to_string(unsigned ipv4) static char *ipv4_to_string(unsigned ipv4)
@ -396,21 +405,11 @@ static void pfd_receive(Plug plug, int urgent, char *data, int len)
*/ */
sk_set_frozen(pf->s, 1); sk_set_frozen(pf->s, 1);
pf->c = new_sock_channel(pf->ssh, pf); pf->c = wrap_send_port_open(pf->ssh, pf->hostname, pf->port, pf->s,
if (pf->c == NULL) { &pf->chan);
pfd_close(pf);
return;
} else {
/* asks to forward to the specified host/port for this */
wrap_send_port_open(pf->c, pf->hostname, pf->port, pf->s);
}
}
if (pf->ready) {
if (sshfwd_write(pf->c, data, len) > 0) {
pf->throttled = 1;
sk_set_frozen(pf->s, 1);
}
} }
if (pf->ready)
sshfwd_write(pf->c, data, len);
} }
static void pfd_sent(Plug plug, int bufsize) static void pfd_sent(Plug plug, int bufsize)
@ -429,6 +428,25 @@ static const Plug_vtable PortForwarding_plugvt = {
NULL NULL
}; };
static void pfd_chan_free(Channel *chan);
static void pfd_open_confirmation(Channel *chan);
static void pfd_open_failure(Channel *chan, const char *errtext);
static int pfd_send(Channel *chan, int is_stderr, const void *data, int len);
static void pfd_send_eof(Channel *chan);
static void pfd_set_input_wanted(Channel *chan, int wanted);
static char *pfd_log_close_msg(Channel *chan);
static const struct ChannelVtable PortForwarding_channelvt = {
pfd_chan_free,
pfd_open_confirmation,
pfd_open_failure,
pfd_send,
pfd_send_eof,
pfd_set_input_wanted,
pfd_log_close_msg,
chan_no_eager_close,
};
/* /*
* Called when receiving a PORT OPEN from the server to make a * Called when receiving a PORT OPEN from the server to make a
* connection to a destination host. * connection to a destination host.
@ -436,8 +454,8 @@ static const Plug_vtable PortForwarding_plugvt = {
* On success, returns NULL and fills in *pf_ret. On error, returns a * On success, returns NULL and fills in *pf_ret. On error, returns a
* dynamically allocated error message string. * dynamically allocated error message string.
*/ */
char *pfd_connect(struct PortForwarding **pf_ret, char *hostname,int port, char *pfd_connect(Channel **chan_ret, char *hostname,int port,
void *c, Conf *conf, int addressfamily) struct ssh_channel *c, Conf *conf, int addressfamily)
{ {
SockAddr addr; SockAddr addr;
const char *err; const char *err;
@ -459,9 +477,12 @@ char *pfd_connect(struct PortForwarding **pf_ret, char *hostname,int port,
/* /*
* Open socket. * Open socket.
*/ */
pf = *pf_ret = new_portfwd_state(); pf = new_portfwd_state();
*chan_ret = &pf->chan;
pf->plugvt = &PortForwarding_plugvt; pf->plugvt = &PortForwarding_plugvt;
pf->throttled = pf->throttle_override = 0; pf->chan.initial_fixed_window_size = 0;
pf->chan.vt = &PortForwarding_channelvt;
pf->input_wanted = TRUE;
pf->ready = 1; pf->ready = 1;
pf->c = c; pf->c = c;
pf->ssh = NULL; /* we shouldn't need this */ pf->ssh = NULL; /* we shouldn't need this */
@ -474,7 +495,7 @@ char *pfd_connect(struct PortForwarding **pf_ret, char *hostname,int port,
char *err_ret = dupstr(err); char *err_ret = dupstr(err);
sk_close(pf->s); sk_close(pf->s);
free_portfwd_state(pf); free_portfwd_state(pf);
*pf_ret = NULL; *chan_ret = NULL;
return err_ret; return err_ret;
} }
@ -495,6 +516,9 @@ static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
pl = FROMFIELD(p, struct PortListener, plugvt); pl = FROMFIELD(p, struct PortListener, plugvt);
pf = new_portfwd_state(); pf = new_portfwd_state();
pf->plugvt = &PortForwarding_plugvt; pf->plugvt = &PortForwarding_plugvt;
pf->chan.initial_fixed_window_size = 0;
pf->chan.vt = &PortForwarding_channelvt;
pf->input_wanted = TRUE;
pf->c = NULL; pf->c = NULL;
pf->ssh = pl->ssh; pf->ssh = pl->ssh;
@ -505,7 +529,7 @@ static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
return err != NULL; return err != NULL;
} }
pf->throttled = pf->throttle_override = 0; pf->input_wanted = TRUE;
pf->ready = 0; pf->ready = 0;
if (pl->is_dynamic) { if (pl->is_dynamic) {
@ -518,15 +542,8 @@ static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
pf->socks_state = SOCKS_NONE; pf->socks_state = SOCKS_NONE;
pf->hostname = dupstr(pl->hostname); pf->hostname = dupstr(pl->hostname);
pf->port = pl->port; pf->port = pl->port;
pf->c = new_sock_channel(pl->ssh, pf); pf->c = wrap_send_port_open(pl->ssh, pf->hostname, pf->port,
s, &pf->chan);
if (pf->c == NULL) {
free_portfwd_state(pf);
return 1;
} else {
/* asks to forward to the specified host/port for this */
wrap_send_port_open(pf->c, pf->hostname, pf->port, s);
}
} }
return 0; return 0;
@ -580,7 +597,12 @@ char *pfl_listen(char *desthost, int destport, char *srcaddr,
return NULL; return NULL;
} }
void pfd_close(struct PortForwarding *pf) static char *pfd_log_close_msg(Channel *chan)
{
return dupstr("Forwarded port closed");
}
static void pfd_close(struct PortForwarding *pf)
{ {
if (!pf) if (!pf)
return; return;
@ -601,43 +623,42 @@ void pfl_terminate(struct PortListener *pl)
free_portlistener_state(pl); free_portlistener_state(pl);
} }
void pfd_unthrottle(struct PortForwarding *pf) static void pfd_set_input_wanted(Channel *chan, int wanted)
{ {
if (!pf) assert(chan->vt == &PortForwarding_channelvt);
return; PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
pf->input_wanted = wanted;
pf->throttled = 0; sk_set_frozen(pf->s, !pf->input_wanted);
sk_set_frozen(pf->s, pf->throttled || pf->throttle_override);
} }
void pfd_override_throttle(struct PortForwarding *pf, int enable) static void pfd_chan_free(Channel *chan)
{ {
if (!pf) assert(chan->vt == &PortForwarding_channelvt);
return; PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
pfd_close(pf);
pf->throttle_override = enable;
sk_set_frozen(pf->s, pf->throttled || pf->throttle_override);
} }
/* /*
* Called to send data down the raw connection. * Called to send data down the raw connection.
*/ */
int pfd_send(struct PortForwarding *pf, const void *data, int len) static int pfd_send(Channel *chan, int is_stderr, const void *data, int len)
{ {
if (pf == NULL) assert(chan->vt == &PortForwarding_channelvt);
return 0; PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
return sk_write(pf->s, data, len); return sk_write(pf->s, data, len);
} }
void pfd_send_eof(struct PortForwarding *pf) static void pfd_send_eof(Channel *chan)
{ {
assert(chan->vt == &PortForwarding_channelvt);
PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
sk_write_eof(pf->s); sk_write_eof(pf->s);
} }
void pfd_confirm(struct PortForwarding *pf) static void pfd_open_confirmation(Channel *chan)
{ {
if (pf == NULL) assert(chan->vt == &PortForwarding_channelvt);
return; PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
pf->ready = 1; pf->ready = 1;
sk_set_frozen(pf->s, 0); sk_set_frozen(pf->s, 0);
@ -649,3 +670,15 @@ void pfd_confirm(struct PortForwarding *pf)
pf->socksbuf = NULL; pf->socksbuf = NULL;
} }
} }
static void pfd_open_failure(Channel *chan, const char *errtext)
{
assert(chan->vt == &PortForwarding_channelvt);
PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
char *msg = dupprintf(
"Forwarded connection refused by server%s%s",
errtext ? ": " : "", errtext ? errtext : "");
logevent(ssh_get_frontend(pf->ssh), msg);
sfree(msg);
}

783
ssh.c

Разница между файлами не показана из-за своего большого размера Загрузить разницу

31
ssh.h
Просмотреть файл

@ -14,12 +14,12 @@ extern void sshfwd_write_eof(struct ssh_channel *c);
extern void sshfwd_unclean_close(struct ssh_channel *c, const char *err); extern void sshfwd_unclean_close(struct ssh_channel *c, const char *err);
extern void sshfwd_unthrottle(struct ssh_channel *c, int bufsize); extern void sshfwd_unthrottle(struct ssh_channel *c, int bufsize);
Conf *sshfwd_get_conf(struct ssh_channel *c); Conf *sshfwd_get_conf(struct ssh_channel *c);
void sshfwd_window_override_removed(struct ssh_channel *c);
void sshfwd_x11_sharing_handover(struct ssh_channel *c, void sshfwd_x11_sharing_handover(struct ssh_channel *c,
void *share_cs, void *share_chan, void *share_cs, void *share_chan,
const char *peer_addr, int peer_port, const char *peer_addr, int peer_port,
int endian, int protomajor, int protominor, int endian, int protomajor, int protominor,
const void *initial_data, int initial_len); const void *initial_data, int initial_len);
void sshfwd_x11_is_local(struct ssh_channel *c);
/* /*
* Buffer management constants. There are several of these for * Buffer management constants. There are several of these for
@ -185,6 +185,8 @@ void share_setup_x11_channel(void *csv, void *chanv,
int protomajor, int protominor, int protomajor, int protominor,
const void *initial_data, int initial_len); const void *initial_data, int initial_len);
Frontend *ssh_get_frontend(Ssh ssh);
/* /*
* Useful thing. * Useful thing.
*/ */
@ -665,19 +667,12 @@ void logevent(Frontend *, const char *);
struct PortForwarding; struct PortForwarding;
/* Allocate and register a new channel for port forwarding */ /* Allocate and register a new channel for port forwarding */
void *new_sock_channel(Ssh ssh, struct PortForwarding *pf); struct ssh_channel *ssh_send_port_open(Ssh ssh, const char *hostname, int port,
void ssh_send_port_open(void *channel, const char *hostname, int port, const char *org, Channel *chan);
const char *org);
/* Exports from portfwd.c */ /* Exports from portfwd.c */
extern char *pfd_connect(struct PortForwarding **pf, char *hostname, int port, extern char *pfd_connect(Channel **chan_ret, char *hostname, int port,
void *c, Conf *conf, int addressfamily); struct ssh_channel *c, Conf *conf, int addressfamily);
extern void pfd_close(struct PortForwarding *);
extern int pfd_send(struct PortForwarding *, const void *data, int len);
extern void pfd_send_eof(struct PortForwarding *);
extern void pfd_confirm(struct PortForwarding *);
extern void pfd_unthrottle(struct PortForwarding *);
extern void pfd_override_throttle(struct PortForwarding *, int enable);
struct PortListener; struct PortListener;
/* desthost == NULL indicates dynamic (SOCKS) port forwarding */ /* desthost == NULL indicates dynamic (SOCKS) port forwarding */
extern char *pfl_listen(char *desthost, int destport, char *srcaddr, extern char *pfl_listen(char *desthost, int destport, char *srcaddr,
@ -750,13 +745,9 @@ extern struct X11Display *x11_setup_display(const char *display, Conf *);
void x11_free_display(struct X11Display *disp); void x11_free_display(struct X11Display *disp);
struct X11FakeAuth *x11_invent_fake_auth(tree234 *t, int authtype); struct X11FakeAuth *x11_invent_fake_auth(tree234 *t, int authtype);
void x11_free_fake_auth(struct X11FakeAuth *auth); void x11_free_fake_auth(struct X11FakeAuth *auth);
struct X11Connection; /* opaque outside x11fwd.c */ Channel *x11_new_channel(tree234 *authtree, struct ssh_channel *c,
struct X11Connection *x11_init(tree234 *authtree, void *, const char *, int); const char *peeraddr, int peerport,
extern void x11_close(struct X11Connection *); int connection_sharing_possible);
extern int x11_send(struct X11Connection *, const void *, int);
extern void x11_send_eof(struct X11Connection *s);
extern void x11_unthrottle(struct X11Connection *s);
extern void x11_override_throttle(struct X11Connection *s, int enable);
char *x11_display(const char *display); char *x11_display(const char *display);
/* Platform-dependent X11 functions */ /* Platform-dependent X11 functions */
extern void platform_get_x11_auth(struct X11Display *display, Conf *); extern void platform_get_x11_auth(struct X11Display *display, Conf *);
@ -786,6 +777,8 @@ void x11_get_auth_from_authfile(struct X11Display *display,
int x11_identify_auth_proto(ptrlen protoname); int x11_identify_auth_proto(ptrlen protoname);
void *x11_dehexify(ptrlen hex, int *outlen); void *x11_dehexify(ptrlen hex, int *outlen);
Channel *agentf_new(struct ssh_channel *c);
Bignum copybn(Bignum b); Bignum copybn(Bignum b);
Bignum bn_power_2(int n); Bignum bn_power_2(int n);
void bn_restore_invariant(Bignum b); void bn_restore_invariant(Bignum b);

59
sshchan.h Normal file
Просмотреть файл

@ -0,0 +1,59 @@
/*
* Abstraction of the various ways to handle the local end of an SSH
* connection-layer channel.
*/
#ifndef PUTTY_SSHCHAN_H
#define PUTTY_SSHCHAN_H
struct ChannelVtable {
void (*free)(Channel *);
/* Called for channel types that were created at the same time as
* we sent an outgoing CHANNEL_OPEN, when the confirmation comes
* back from the server indicating that the channel has been
* opened, or the failure message indicating that it hasn't,
* respectively. In the latter case, this must _not_ free the
* Channel structure - the client will call the free method
* separately. But it might do logging or other local cleanup. */
void (*open_confirmation)(Channel *);
void (*open_failed)(Channel *, const char *error_text);
int (*send)(Channel *, int is_stderr, const void *buf, int len);
void (*send_eof)(Channel *);
void (*set_input_wanted)(Channel *, int wanted);
char *(*log_close_msg)(Channel *);
int (*want_close)(Channel *, int sent_local_eof, int rcvd_remote_eof);
};
struct Channel {
const struct ChannelVtable *vt;
unsigned initial_fixed_window_size;
};
#define chan_free(ch) ((ch)->vt->free(ch))
#define chan_open_confirmation(ch) ((ch)->vt->open_confirmation(ch))
#define chan_open_failed(ch, err) ((ch)->vt->open_failed(ch, err))
#define chan_send(ch, err, buf, len) ((ch)->vt->send(ch, err, buf, len))
#define chan_send_eof(ch) ((ch)->vt->send_eof(ch))
#define chan_set_input_wanted(ch, wanted) \
((ch)->vt->set_input_wanted(ch, wanted))
#define chan_log_close_msg(ch) ((ch)->vt->send_eof(ch))
#define chan_want_close(ch, leof, reof) ((ch)->vt->want_close(ch, leof, reof))
/*
* Reusable methods you can put in vtables to give default handling of
* some of those functions.
*/
/* open_confirmation / open_failed for any channel it doesn't apply to */
void chan_remotely_opened_confirmation(Channel *chan);
void chan_remotely_opened_failure(Channel *chan, const char *errtext);
/* want_close for any channel that wants the default behaviour of not
* closing until both directions have had an EOF */
int chan_no_eager_close(Channel *, int, int);
#endif /* PUTTY_SSHCHAN_H */

Просмотреть файл

@ -161,13 +161,17 @@ int sshfwd_write(struct ssh_channel *c, const void *data, int len)
void sshfwd_write_eof(struct ssh_channel *c) { } void sshfwd_write_eof(struct ssh_channel *c) { }
void sshfwd_unclean_close(struct ssh_channel *c, const char *err) { } void sshfwd_unclean_close(struct ssh_channel *c, const char *err) { }
void sshfwd_unthrottle(struct ssh_channel *c, int bufsize) {} void sshfwd_unthrottle(struct ssh_channel *c, int bufsize) {}
void sshfwd_window_override_removed(struct ssh_channel *c) { }
void chan_remotely_opened_confirmation(Channel *chan) { }
void chan_remotely_opened_failure(Channel *chan, const char *err) { }
int chan_no_eager_close(Channel *chan, int s, int r) { return FALSE; }
Conf *sshfwd_get_conf(struct ssh_channel *c) { return NULL; } Conf *sshfwd_get_conf(struct ssh_channel *c) { return NULL; }
void sshfwd_x11_sharing_handover(struct ssh_channel *c, void sshfwd_x11_sharing_handover(struct ssh_channel *c,
void *share_cs, void *share_chan, void *share_cs, void *share_chan,
const char *peer_addr, int peer_port, const char *peer_addr, int peer_port,
int endian, int protomajor, int protominor, int endian, int protomajor, int protominor,
const void *initial_data, int initial_len) {} const void *initial_data, int initial_len) {}
void sshfwd_x11_is_local(struct ssh_channel *c) {}
/* /*
* These functions are part of the plug for our connection to the X * These functions are part of the plug for our connection to the X

Просмотреть файл

@ -9,6 +9,7 @@
#include "putty.h" #include "putty.h"
#include "ssh.h" #include "ssh.h"
#include "sshchan.h"
#include "tree234.h" #include "tree234.h"
#define GET_16BIT(endian, cp) \ #define GET_16BIT(endian, cp) \
@ -26,7 +27,7 @@ struct XDMSeen {
unsigned char clientid[6]; unsigned char clientid[6];
}; };
struct X11Connection { typedef struct X11Connection {
unsigned char firstpkt[12]; /* first X data packet */ unsigned char firstpkt[12]; /* first X data packet */
tree234 *authtree; tree234 *authtree;
struct X11Display *disp; struct X11Display *disp;
@ -34,7 +35,7 @@ struct X11Connection {
unsigned char *auth_data; unsigned char *auth_data;
int data_read, auth_plen, auth_psize, auth_dlen, auth_dsize; int data_read, auth_plen, auth_psize, auth_dlen, auth_dsize;
int verified; int verified;
int throttled, throttle_override; int input_wanted;
int no_data_sent_to_x_client; int no_data_sent_to_x_client;
char *peer_addr; char *peer_addr;
int peer_port; int peer_port;
@ -42,7 +43,8 @@ struct X11Connection {
Socket s; Socket s;
const Plug_vtable *plugvt; const Plug_vtable *plugvt;
}; Channel chan;
} X11Connection;
static int xdmseen_cmp(void *a, void *b) static int xdmseen_cmp(void *a, void *b)
{ {
@ -666,11 +668,8 @@ static void x11_receive(Plug plug, int urgent, char *data, int len)
struct X11Connection *xconn = FROMFIELD( struct X11Connection *xconn = FROMFIELD(
plug, struct X11Connection, plugvt); plug, struct X11Connection, plugvt);
if (sshfwd_write(xconn->c, data, len) > 0) { xconn->no_data_sent_to_x_client = FALSE;
xconn->throttled = 1; sshfwd_write(xconn->c, data, len);
xconn->no_data_sent_to_x_client = FALSE;
sk_set_frozen(xconn->s, 1);
}
} }
static void x11_sent(Plug plug, int bufsize) static void x11_sent(Plug plug, int bufsize)
@ -707,12 +706,30 @@ static const Plug_vtable X11Connection_plugvt = {
NULL NULL
}; };
static void x11_chan_free(Channel *chan);
static int x11_send(Channel *chan, int is_stderr, const void *vdata, int len);
static void x11_send_eof(Channel *chan);
static void x11_set_input_wanted(Channel *chan, int wanted);
static char *x11_log_close_msg(Channel *chan);
static const struct ChannelVtable X11Connection_channelvt = {
x11_chan_free,
chan_remotely_opened_confirmation,
chan_remotely_opened_failure,
x11_send,
x11_send_eof,
x11_set_input_wanted,
x11_log_close_msg,
chan_no_eager_close,
};
/* /*
* Called to set up the X11Connection structure, though this does not * Called to set up the X11Connection structure, though this does not
* yet connect to an actual server. * yet connect to an actual server.
*/ */
struct X11Connection *x11_init(tree234 *authtree, void *c, Channel *x11_new_channel(tree234 *authtree, struct ssh_channel *c,
const char *peeraddr, int peerport) const char *peeraddr, int peerport,
int connection_sharing_possible)
{ {
struct X11Connection *xconn; struct X11Connection *xconn;
@ -721,11 +738,14 @@ struct X11Connection *x11_init(tree234 *authtree, void *c,
*/ */
xconn = snew(struct X11Connection); xconn = snew(struct X11Connection);
xconn->plugvt = &X11Connection_plugvt; xconn->plugvt = &X11Connection_plugvt;
xconn->chan.vt = &X11Connection_channelvt;
xconn->chan.initial_fixed_window_size =
(connection_sharing_possible ? 128 : 0);
xconn->auth_protocol = NULL; xconn->auth_protocol = NULL;
xconn->authtree = authtree; xconn->authtree = authtree;
xconn->verified = 0; xconn->verified = 0;
xconn->data_read = 0; xconn->data_read = 0;
xconn->throttled = xconn->throttle_override = 0; xconn->input_wanted = TRUE;
xconn->no_data_sent_to_x_client = TRUE; xconn->no_data_sent_to_x_client = TRUE;
xconn->c = c; xconn->c = c;
@ -746,13 +766,13 @@ struct X11Connection *x11_init(tree234 *authtree, void *c,
xconn->peer_addr = peeraddr ? dupstr(peeraddr) : NULL; xconn->peer_addr = peeraddr ? dupstr(peeraddr) : NULL;
xconn->peer_port = peerport; xconn->peer_port = peerport;
return xconn; return &xconn->chan;
} }
void x11_close(struct X11Connection *xconn) static void x11_chan_free(Channel *chan)
{ {
if (!xconn) assert(chan->vt == &X11Connection_channelvt);
return; X11Connection *xconn = FROMFIELD(chan, X11Connection, chan);
if (xconn->auth_protocol) { if (xconn->auth_protocol) {
sfree(xconn->auth_protocol); sfree(xconn->auth_protocol);
@ -766,24 +786,14 @@ void x11_close(struct X11Connection *xconn)
sfree(xconn); sfree(xconn);
} }
void x11_unthrottle(struct X11Connection *xconn) static void x11_set_input_wanted(Channel *chan, int wanted)
{ {
if (!xconn) assert(chan->vt == &X11Connection_channelvt);
return; X11Connection *xconn = FROMFIELD(chan, X11Connection, chan);
xconn->throttled = 0; xconn->input_wanted = wanted;
if (xconn->s) if (xconn->s)
sk_set_frozen(xconn->s, xconn->throttled || xconn->throttle_override); sk_set_frozen(xconn->s, !xconn->input_wanted);
}
void x11_override_throttle(struct X11Connection *xconn, int enable)
{
if (!xconn)
return;
xconn->throttle_override = enable;
if (xconn->s)
sk_set_frozen(xconn->s, xconn->throttled || xconn->throttle_override);
} }
static void x11_send_init_error(struct X11Connection *xconn, static void x11_send_init_error(struct X11Connection *xconn,
@ -831,13 +841,12 @@ static int x11_parse_ip(const char *addr_string, unsigned long *ip)
/* /*
* Called to send data down the raw connection. * Called to send data down the raw connection.
*/ */
int x11_send(struct X11Connection *xconn, const void *vdata, int len) static int x11_send(Channel *chan, int is_stderr, const void *vdata, int len)
{ {
assert(chan->vt == &X11Connection_channelvt);
X11Connection *xconn = FROMFIELD(chan, X11Connection, chan);
const char *data = (const char *)vdata; const char *data = (const char *)vdata;
if (!xconn)
return 0;
/* /*
* Read the first packet. * Read the first packet.
*/ */
@ -914,7 +923,8 @@ int x11_send(struct X11Connection *xconn, const void *vdata, int len)
/* /*
* If this auth points to a connection-sharing downstream * If this auth points to a connection-sharing downstream
* rather than an X display we know how to connect to * rather than an X display we know how to connect to
* directly, pass it off to the sharing module now. * directly, pass it off to the sharing module now. (This will
* have the side effect of freeing xconn.)
*/ */
if (auth_matched->share_cs) { if (auth_matched->share_cs) {
sshfwd_x11_sharing_handover(xconn->c, auth_matched->share_cs, sshfwd_x11_sharing_handover(xconn->c, auth_matched->share_cs,
@ -929,7 +939,8 @@ int x11_send(struct X11Connection *xconn, const void *vdata, int len)
* Now we know we're going to accept the connection, and what * Now we know we're going to accept the connection, and what
* X display to connect to. Actually connect to it. * X display to connect to. Actually connect to it.
*/ */
sshfwd_x11_is_local(xconn->c); xconn->chan.initial_fixed_window_size = 0;
sshfwd_window_override_removed(xconn->c);
xconn->disp = auth_matched->disp; xconn->disp = auth_matched->disp;
xconn->s = new_connection(sk_addr_dup(xconn->disp->addr), xconn->s = new_connection(sk_addr_dup(xconn->disp->addr),
xconn->disp->realhost, xconn->disp->port, xconn->disp->realhost, xconn->disp->port,
@ -984,8 +995,11 @@ int x11_send(struct X11Connection *xconn, const void *vdata, int len)
return sk_write(xconn->s, data, len); return sk_write(xconn->s, data, len);
} }
void x11_send_eof(struct X11Connection *xconn) static void x11_send_eof(Channel *chan)
{ {
assert(chan->vt == &X11Connection_channelvt);
X11Connection *xconn = FROMFIELD(chan, X11Connection, chan);
if (xconn->s) { if (xconn->s) {
sk_write_eof(xconn->s); sk_write_eof(xconn->s);
} else { } else {
@ -1000,6 +1014,11 @@ void x11_send_eof(struct X11Connection *xconn)
} }
} }
static char *x11_log_close_msg(Channel *chan)
{
return dupstr("Forwarded X11 connection terminated");
}
/* /*
* Utility functions used by connection sharing to convert textual * Utility functions used by connection sharing to convert textual
* representations of an X11 auth protocol name + hex cookie into our * representations of an X11 auth protocol name + hex cookie into our