1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

Rewrite the SSH-1 main shell session using mainchan.

In SSH-1, the channel system isn't rich enough to represent the
complicated main shell session, so it's all done with a separate set
of custom message types. But PuTTY now abstracts away that difference,
by representing both as different implementations of the SshChannel
class: ssh1channel is for things that the protocol thinks are 'really'
channels, and ssh1mainchan is for the shell session. All the same
methods are implemented, but generate different wire messages.

This means that the logic to decide _when_ to enable X forwarding,
agent forwarding etc is all centralised into mainchan.c, where it
doesn't have to be repeated for both protocol versions.

It also simplifies the final loop in the connection protocol, which no
longer has to contain the code to move data from the user input
bufchain to the channel's output; that's now done by the mainchan
write method, the same as it is in SSH-2 where mainchan is just like
other channels.
This commit is contained in:
Simon Tatham 2018-09-30 11:22:01 +01:00
parent 72eca76d20
commit 79c4d3f3ee
2 changed files with 371 additions and 189 deletions

View File

@ -95,7 +95,7 @@ mainchan *mainchan_new(
mc->type = MAINCHAN_SESSION;
}
*sc_out = mc->sc;
if (sc_out) *sc_out = mc->sc;
return mc;
}

View File

@ -26,11 +26,20 @@ struct ssh1_connection_state {
tree234 *channels; /* indexed by local id */
/* In SSH-1, the main session doesn't take the form of a 'channel'
* according to the wire protocol. But we want to use the same API
* for it, so we define an SshChannel here - but one that uses a
* separate vtable from the usual one, so it doesn't map to a
* struct ssh1_channel as all the others do. */
SshChannel mainchan_sc;
Channel *mainchan_chan; /* the other end of mainchan_sc */
mainchan *mainchan; /* and its subtype */
int got_pty;
int echoedit;
int ldisc_opts[LD_N_OPTIONS];
int stdout_throttling;
int session_ready;
int session_eof_pending, session_eof_sent, session_terminated;
int want_user_input;
int session_terminated;
int term_width, term_height, term_width_orig, term_height_orig;
int X11_fwd_enabled;
@ -101,19 +110,26 @@ static void ssh1_rportfwd_remove(
static SshChannel *ssh1_lportfwd_open(
ConnectionLayer *cl, const char *hostname, int port,
const char *org, Channel *chan);
static SshChannel *ssh1_session_open(ConnectionLayer *cl, Channel *chan);
static struct X11FakeAuth *ssh1_add_x11_display(
ConnectionLayer *cl, int authtype, struct X11Display *disp);
static int ssh1_agent_forwarding_permitted(ConnectionLayer *cl);
static void ssh1_terminal_size(ConnectionLayer *cl, int width, int height);
static void ssh1_stdout_unthrottle(ConnectionLayer *cl, int bufsize);
static int ssh1_stdin_backlog(ConnectionLayer *cl);
static void ssh1_throttle_all_channels(ConnectionLayer *cl, int throttled);
static int ssh1_ldisc_option(ConnectionLayer *cl, int option);
static void ssh1_set_ldisc_option(ConnectionLayer *cl, int option, int value);
static void ssh1_enable_x_fwd(ConnectionLayer *cl);
static void ssh1_enable_agent_fwd(ConnectionLayer *cl);
static void ssh1_set_wants_user_input(ConnectionLayer *cl, int wanted);
static const struct ConnectionLayerVtable ssh1_connlayer_vtable = {
ssh1_rportfwd_alloc,
ssh1_rportfwd_remove,
ssh1_lportfwd_open,
NULL /* session_open */,
NULL /* add_x11_display */,
ssh1_session_open,
ssh1_add_x11_display,
NULL /* add_sharing_x11_display */,
NULL /* remove_sharing_x11_display */,
NULL /* send_packet_from_downstream */,
@ -127,10 +143,10 @@ static const struct ConnectionLayerVtable ssh1_connlayer_vtable = {
ssh1_stdin_backlog,
ssh1_throttle_all_channels,
ssh1_ldisc_option,
NULL /* set_ldisc_option */,
NULL /* enable_x_fwd */,
NULL /* enable_agent_fwd */,
NULL /* set_wants_user_input */,
ssh1_set_ldisc_option,
ssh1_enable_x_fwd,
ssh1_enable_agent_fwd,
ssh1_set_wants_user_input,
};
struct ssh1_channel {
@ -205,6 +221,54 @@ static const struct SshChannelVtable ssh1channel_vtable = {
NULL /* hint_channel_is_simple */,
};
static void ssh1mainchan_request_x11_forwarding(
SshChannel *c, int want_reply, const char *authproto,
const char *authdata, int screen_number, int oneshot);
static void ssh1mainchan_request_agent_forwarding(
SshChannel *c, int want_reply);
static void ssh1mainchan_request_pty(
SshChannel *c, int want_reply, Conf *conf, int w, int h);
static int ssh1mainchan_send_env_var(
SshChannel *c, int want_reply, const char *var, const char *value);
static void ssh1mainchan_start_shell(
SshChannel *c, int want_reply);
static void ssh1mainchan_start_command(
SshChannel *c, int want_reply, const char *command);
static int ssh1mainchan_start_subsystem(
SshChannel *c, int want_reply, const char *subsystem);
static int ssh1mainchan_send_env_var(
SshChannel *c, int want_reply, const char *var, const char *value);
static int ssh1mainchan_send_serial_break(
SshChannel *c, int want_reply, int length);
static int ssh1mainchan_send_signal(
SshChannel *c, int want_reply, const char *signame);
static void ssh1mainchan_send_terminal_size_change(
SshChannel *c, int w, int h);
static void ssh1mainchan_hint_channel_is_simple(SshChannel *c);
static int ssh1mainchan_write(SshChannel *sc, const void *data, int len);
static void ssh1mainchan_write_eof(SshChannel *sc);
static const struct SshChannelVtable ssh1mainchan_vtable = {
ssh1mainchan_write,
ssh1mainchan_write_eof,
NULL /* unclean_close */,
NULL /* unthrottle */,
NULL /* get_conf */,
NULL /* window_override_removed is only used by SSH-2 sharing */,
NULL /* x11_sharing_handover, likewise */,
ssh1mainchan_request_x11_forwarding,
ssh1mainchan_request_agent_forwarding,
ssh1mainchan_request_pty,
ssh1mainchan_send_env_var,
ssh1mainchan_start_shell,
ssh1mainchan_start_command,
ssh1mainchan_start_subsystem,
ssh1mainchan_send_serial_break,
ssh1mainchan_send_signal,
ssh1mainchan_send_terminal_size_change,
ssh1mainchan_hint_channel_is_simple,
};
static void ssh1_channel_init(struct ssh1_channel *c);
static void ssh1_channel_try_eof(struct ssh1_channel *c);
static void ssh1_channel_close_local(struct ssh1_channel *c,
@ -215,24 +279,60 @@ static void ssh1_channel_check_close(struct ssh1_channel *c);
static int ssh1_check_termination(struct ssh1_connection_state *s);
typedef void (*sf_handler_fn_t)(struct ssh1_connection_state *s,
PktIn *pktin, void *ctx);
int success, void *ctx);
struct outstanding_succfail {
sf_handler_fn_t handler;
void *ctx;
struct outstanding_succfail *next;
/*
* The 'trivial' flag is set if this handler is in response to a
* request for which the SSH-1 protocol doesn't actually specify a
* response packet. The client of this system (mainchan.c) will
* expect to get an acknowledgment regardless, so we arrange to
* send that ack immediately after the rest of the queue empties.
*/
int trivial;
};
static void ssh1_connection_process_trivial_succfails(void *vs);
static void ssh1_queue_succfail_handler(
struct ssh1_connection_state *s, sf_handler_fn_t handler, void *ctx)
struct ssh1_connection_state *s, sf_handler_fn_t handler, void *ctx,
int trivial)
{
struct outstanding_succfail *osf =
snew(struct outstanding_succfail);
osf->handler = handler;
osf->ctx = ctx;
osf->trivial = trivial;
if (s->succfail_tail)
s->succfail_tail->next = osf;
else
s->succfail_head = osf;
s->succfail_tail = osf;
/* In case this one was trivial and the queue was already empty,
* we should make sure we run the handler promptly, and the
* easiest way is to queue it anyway and then run a trivials pass
* by callback. */
queue_toplevel_callback(ssh1_connection_process_trivial_succfails, s);
}
static void ssh1_connection_process_succfail(
struct ssh1_connection_state *s, int success)
{
struct outstanding_succfail *prevhead = s->succfail_head;
s->succfail_head = s->succfail_head->next;
prevhead->handler(s, success, prevhead->ctx);
sfree(prevhead);
}
static void ssh1_connection_process_trivial_succfails(void *vs)
{
struct ssh1_connection_state *s = (struct ssh1_connection_state *)vs;
while (s->succfail_head && s->succfail_head->trivial)
ssh1_connection_process_succfail(s, TRUE);
}
static int ssh1_channelcmp(void *av, void *bv)
@ -315,6 +415,8 @@ static void ssh1_connection_free(PacketProtocolLayer *ppl)
freetree234(s->rportfwds);
portfwdmgr_free(s->portfwdmgr);
delete_callbacks_for_context(s);
sfree(s);
}
@ -363,12 +465,10 @@ static int ssh1_connection_filter_queue(struct ssh1_connection_state *s)
return TRUE;
}
s->succfail_head->handler(s, pktin, s->succfail_head->ctx);
{
struct outstanding_succfail *tmp = s->succfail_head;
s->succfail_head = s->succfail_head->next;
sfree(tmp);
}
ssh1_connection_process_succfail(
s, pktin->type == SSH1_SMSG_SUCCESS);
queue_toplevel_callback(
ssh1_connection_process_trivial_succfails, s);
pq_pop(s->ppl.in_pq);
break;
@ -638,147 +738,23 @@ static void ssh1_connection_process_queue(PacketProtocolLayer *ppl)
struct ssh1_connection_state *s =
container_of(ppl, struct ssh1_connection_state, ppl);
PktIn *pktin;
PktOut *pktout;
if (ssh1_connection_filter_queue(s)) /* no matter why we were called */
return;
crBegin(s->crState);
if (ssh_agent_forwarding_permitted(&s->cl)) {
ppl_logevent(("Requesting agent forwarding"));
pktout = ssh_bpp_new_pktout(s->ppl.bpp,
SSH1_CMSG_AGENT_REQUEST_FORWARDING);
pq_push(s->ppl.out_pq, pktout);
crMaybeWaitUntilV((pktin = ssh1_connection_pop(s)) != NULL);
if (pktin->type == SSH1_SMSG_SUCCESS) {
ppl_logevent(("Agent forwarding enabled"));
s->agent_fwd_enabled = TRUE;
} else if (pktin->type == SSH1_SMSG_FAILURE) {
ppl_logevent(("Agent forwarding refused"));
} else {
ssh_proto_error(s->ppl.ssh, "Unexpected packet received"
" in response to agent forwarding request, "
"type %d (%s)", pktin->type,
ssh1_pkt_type(pktin->type));
return;
}
}
if (conf_get_int(s->conf, CONF_x11_forward)) {
char *x11_setup_err;
s->x11disp =
x11_setup_display(conf_get_str(s->conf, CONF_x11_display),
s->conf, &x11_setup_err);
if (!s->x11disp) {
ppl_logevent(("X11 forwarding not enabled: unable to"
" initialise X display: %s", x11_setup_err));
sfree(x11_setup_err);
} else {
s->x11auth = x11_invent_fake_auth
(s->x11authtree, conf_get_int(s->conf, CONF_x11_auth));
s->x11auth->disp = s->x11disp;
ppl_logevent(("Requesting X11 forwarding"));
pktout = ssh_bpp_new_pktout(
s->ppl.bpp, SSH1_CMSG_X11_REQUEST_FORWARDING);
put_stringz(pktout, s->x11auth->protoname);
put_stringz(pktout, s->x11auth->datastring);
if (s->local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER)
put_uint32(pktout, s->x11disp->screennum);
pq_push(s->ppl.out_pq, pktout);
crMaybeWaitUntilV((pktin = ssh1_connection_pop(s)) != NULL);
if (pktin->type == SSH1_SMSG_SUCCESS) {
ppl_logevent(("X11 forwarding enabled"));
s->X11_fwd_enabled = TRUE;
} else if (pktin->type == SSH1_SMSG_FAILURE) {
ppl_logevent(("X11 forwarding refused"));
} else {
ssh_proto_error(s->ppl.ssh, "Unexpected packet received"
" in response to X11 forwarding request, "
"type %d (%s)", pktin->type,
ssh1_pkt_type(pktin->type));
return;
}
}
}
portfwdmgr_config(s->portfwdmgr, s->conf);
s->portfwdmgr_configured = TRUE;
if (!conf_get_int(s->conf, CONF_nopty)) {
/* Send the pty request. */
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_REQUEST_PTY);
put_stringz(pktout, conf_get_str(s->conf, CONF_termtype));
put_uint32(pktout, s->term_height);
put_uint32(pktout, s->term_width);
s->term_width_orig = s->term_width;
s->term_height_orig = s->term_height;
put_uint32(pktout, 0); /* width in pixels */
put_uint32(pktout, 0); /* height in pixels */
write_ttymodes_to_packet(
BinarySink_UPCAST(pktout), 1,
get_ttymodes_from_conf(s->ppl.seat, s->conf));
pq_push(s->ppl.out_pq, pktout);
crMaybeWaitUntilV((pktin = ssh1_connection_pop(s)) != NULL);
if (pktin->type == SSH1_SMSG_SUCCESS) {
ppl_logevent(("Allocated pty"));
s->got_pty = TRUE;
} else if (pktin->type == SSH1_SMSG_FAILURE) {
ppl_printf(("Server refused to allocate pty\r\n"));
s->echoedit = TRUE;
} else {
ssh_proto_error(s->ppl.ssh, "Unexpected packet received"
" in response to pty request, "
"type %d (%s)", pktin->type,
ssh1_pkt_type(pktin->type));
crStopV;
}
} else {
s->echoedit = TRUE;
}
/*
* Start the shell or command.
*
* Special case: if the first-choice command is an SSH-2
* subsystem (hence not usable here) and the second choice
* exists, we fall straight back to that.
* Start up the main session, by telling mainchan.c to do it all
* just as it would in SSH-2, and translating those concepts to
* SSH-1's non-channel-shaped idea of the main session.
*/
{
char *cmd = conf_get_str(s->conf, CONF_remote_cmd);
if (conf_get_int(s->conf, CONF_ssh_subsys) &&
conf_get_str(s->conf, CONF_remote_cmd2)) {
cmd = conf_get_str(s->conf, CONF_remote_cmd2);
ssh_got_fallback_cmd(s->ppl.ssh);
}
if (*cmd) {
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_EXEC_CMD);
put_stringz(pktout, cmd);
} else {
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_EXEC_SHELL);
}
pq_push(s->ppl.out_pq, pktout);
ppl_logevent(("Started session"));
}
s->session_ready = TRUE;
ssh_ppl_got_user_input(&s->ppl); /* in case any input is already queued */
/* If an EOF or a window-size change arrived before we were ready
* to handle either one, handle them now. */
if (s->session_eof_pending) {
ssh_ppl_special_cmd(&s->ppl, SS_EOF, 0);
s->session_eof_pending = FALSE;
}
if (s->term_width_orig != s->term_width ||
s->term_height_orig != s->term_height)
ssh_terminal_size(&s->cl, s->term_width, s->term_height);
ssh_ldisc_update(s->ppl.ssh);
s->finished_setup = TRUE;
s->mainchan = mainchan_new(
&s->ppl, &s->cl, s->conf, s->term_width, s->term_height,
FALSE /* is_simple */, NULL);
while (1) {
@ -794,17 +770,6 @@ static void ssh1_connection_process_queue(PacketProtocolLayer *ppl)
ssh1_pkt_type(pktin->type));
return;
}
while (bufchain_size(s->ppl.user_input) > 0) {
void *data;
int len;
bufchain_prefix(s->ppl.user_input, &data, &len);
if (len > 512)
len = 512;
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_STDIN_DATA);
put_string(pktout, data, len);
pq_push(s->ppl.out_pq, pktout);
bufchain_consume(s->ppl.user_input, len);
}
crReturnV;
}
@ -1018,6 +983,197 @@ static int ssh1channel_write(SshChannel *sc, const void *buf, int len)
return 0;
}
static struct X11FakeAuth *ssh1_add_x11_display(
ConnectionLayer *cl, int authtype, struct X11Display *disp)
{
struct ssh1_connection_state *s =
container_of(cl, struct ssh1_connection_state, cl);
struct X11FakeAuth *auth = x11_invent_fake_auth(s->x11authtree, authtype);
auth->disp = disp;
return auth;
}
static void ssh1mainchan_succfail_wantreply(struct ssh1_connection_state *s,
int success, void *ctx)
{
chan_request_response(s->mainchan_chan, success);
}
static void ssh1mainchan_succfail_nowantreply(struct ssh1_connection_state *s,
int success, void *ctx)
{
}
static void ssh1mainchan_queue_response(struct ssh1_connection_state *s,
int want_reply, int trivial)
{
sf_handler_fn_t handler = (want_reply ? ssh1mainchan_succfail_wantreply :
ssh1mainchan_succfail_nowantreply);
ssh1_queue_succfail_handler(s, handler, NULL, trivial);
}
static void ssh1mainchan_request_x11_forwarding(
SshChannel *sc, int want_reply, const char *authproto,
const char *authdata, int screen_number, int oneshot)
{
struct ssh1_connection_state *s =
container_of(sc, struct ssh1_connection_state, mainchan_sc);
PktOut *pktout;
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_X11_REQUEST_FORWARDING);
put_stringz(pktout, authproto);
put_stringz(pktout, authdata);
if (s->local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER)
put_uint32(pktout, screen_number);
pq_push(s->ppl.out_pq, pktout);
ssh1mainchan_queue_response(s, want_reply, FALSE);
}
static void ssh1mainchan_request_agent_forwarding(
SshChannel *sc, int want_reply)
{
struct ssh1_connection_state *s =
container_of(sc, struct ssh1_connection_state, mainchan_sc);
PktOut *pktout;
pktout = ssh_bpp_new_pktout(
s->ppl.bpp, SSH1_CMSG_AGENT_REQUEST_FORWARDING);
pq_push(s->ppl.out_pq, pktout);
ssh1mainchan_queue_response(s, want_reply, FALSE);
}
static void ssh1mainchan_request_pty(
SshChannel *sc, int want_reply, Conf *conf, int w, int h)
{
struct ssh1_connection_state *s =
container_of(sc, struct ssh1_connection_state, mainchan_sc);
PktOut *pktout;
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_REQUEST_PTY);
put_stringz(pktout, conf_get_str(s->conf, CONF_termtype));
put_uint32(pktout, h);
put_uint32(pktout, w);
put_uint32(pktout, 0); /* width in pixels */
put_uint32(pktout, 0); /* height in pixels */
write_ttymodes_to_packet(
BinarySink_UPCAST(pktout), 1,
get_ttymodes_from_conf(s->ppl.seat, conf));
pq_push(s->ppl.out_pq, pktout);
ssh1mainchan_queue_response(s, want_reply, FALSE);
}
static int ssh1mainchan_send_env_var(
SshChannel *sc, int want_reply, const char *var, const char *value)
{
return FALSE; /* SSH-1 doesn't support this at all */
}
static void ssh1mainchan_start_shell(
SshChannel *sc, int want_reply)
{
struct ssh1_connection_state *s =
container_of(sc, struct ssh1_connection_state, mainchan_sc);
PktOut *pktout;
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_EXEC_SHELL);
pq_push(s->ppl.out_pq, pktout);
ssh1mainchan_queue_response(s, want_reply, TRUE);
}
static void ssh1mainchan_start_command(
SshChannel *sc, int want_reply, const char *command)
{
struct ssh1_connection_state *s =
container_of(sc, struct ssh1_connection_state, mainchan_sc);
PktOut *pktout;
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_EXEC_CMD);
put_stringz(pktout, command);
pq_push(s->ppl.out_pq, pktout);
ssh1mainchan_queue_response(s, want_reply, TRUE);
}
static int ssh1mainchan_start_subsystem(
SshChannel *sc, int want_reply, const char *subsystem)
{
return FALSE; /* SSH-1 doesn't support this at all */
}
static int ssh1mainchan_send_serial_break(
SshChannel *sc, int want_reply, int length)
{
return FALSE; /* SSH-1 doesn't support this at all */
}
static int ssh1mainchan_send_signal(
SshChannel *sc, int want_reply, const char *signame)
{
return FALSE; /* SSH-1 doesn't support this at all */
}
static void ssh1mainchan_send_terminal_size_change(SshChannel *sc, int w, int h)
{
struct ssh1_connection_state *s =
container_of(sc, struct ssh1_connection_state, mainchan_sc);
PktOut *pktout;
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_WINDOW_SIZE);
put_uint32(pktout, h);
put_uint32(pktout, w);
put_uint32(pktout, 0); /* width in pixels */
put_uint32(pktout, 0); /* height in pixels */
pq_push(s->ppl.out_pq, pktout);
}
static void ssh1mainchan_hint_channel_is_simple(SshChannel *c)
{
}
static int ssh1mainchan_write(SshChannel *sc, const void *data, int len)
{
struct ssh1_connection_state *s =
container_of(sc, struct ssh1_connection_state, mainchan_sc);
PktOut *pktout;
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_STDIN_DATA);
put_string(pktout, data, len);
pq_push(s->ppl.out_pq, pktout);
return 0;
}
static void ssh1mainchan_write_eof(SshChannel *sc)
{
struct ssh1_connection_state *s =
container_of(sc, struct ssh1_connection_state, mainchan_sc);
PktOut *pktout;
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_EOF);
pq_push(s->ppl.out_pq, pktout);
}
static void ssh1_session_confirm_callback(void *vctx)
{
struct ssh1_connection_state *s = (struct ssh1_connection_state *)vctx;
chan_open_confirmation(s->mainchan_chan);
}
static SshChannel *ssh1_session_open(ConnectionLayer *cl, Channel *chan)
{
struct ssh1_connection_state *s =
container_of(cl, struct ssh1_connection_state, cl);
s->mainchan_sc.cl = &s->cl;
s->mainchan_sc.vt = &ssh1mainchan_vtable;
s->mainchan_chan = chan;
queue_toplevel_callback(ssh1_session_confirm_callback, s);
return &s->mainchan_sc;
}
static SshChannel *ssh1_lportfwd_open(
ConnectionLayer *cl, const char *hostname, int port,
const char *org, Channel *chan)
@ -1047,12 +1203,12 @@ static SshChannel *ssh1_lportfwd_open(
}
static void ssh1_rportfwd_response(struct ssh1_connection_state *s,
PktIn *pktin, void *ctx)
int success, void *ctx)
{
PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
struct ssh_rportfwd *rpf = (struct ssh_rportfwd *)ctx;
if (pktin->type == SSH1_SMSG_SUCCESS) {
if (success) {
ppl_logevent(("Remote port forwarding from %s enabled",
rpf->log_description));
} else {
@ -1096,7 +1252,7 @@ static struct ssh_rportfwd *ssh1_rportfwd_alloc(
put_uint32(pktout, rpf->dport);
pq_push(s->ppl.out_pq, pktout);
ssh1_queue_succfail_handler(s, ssh1_rportfwd_response, rpf);
ssh1_queue_succfail_handler(s, ssh1_rportfwd_response, rpf, FALSE);
return rpf;
}
@ -1129,19 +1285,8 @@ static void ssh1_connection_special_cmd(PacketProtocolLayer *ppl,
put_stringz(pktout, "");
pq_push(s->ppl.out_pq, pktout);
}
} else if (code == SS_EOF) {
if (!s->session_ready) {
/*
* Buffer the EOF to send as soon as the main session is
* fully set up.
*/
s->session_eof_pending = TRUE;
} else if (!s->session_eof_sent) {
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_EOF);
pq_push(s->ppl.out_pq, pktout);
ppl_logevent(("Sent EOF message"));
s->session_eof_sent = TRUE;
}
} else if (s->mainchan) {
mainchan_special_cmd(s->mainchan, code, arg);
}
}
@ -1152,15 +1297,8 @@ static void ssh1_terminal_size(ConnectionLayer *cl, int width, int height)
s->term_width = width;
s->term_height = height;
if (s->session_ready) {
PktOut *pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_WINDOW_SIZE);
put_uint32(pktout, s->term_height);
put_uint32(pktout, s->term_width);
put_uint32(pktout, 0);
put_uint32(pktout, 0);
pq_push(s->ppl.out_pq, pktout);
}
if (s->mainchan)
mainchan_terminal_size(s->mainchan, width, height);
}
static void ssh1_stdout_unthrottle(ConnectionLayer *cl, int bufsize)
@ -1195,23 +1333,67 @@ static int ssh1_ldisc_option(ConnectionLayer *cl, int option)
struct ssh1_connection_state *s =
container_of(cl, struct ssh1_connection_state, cl);
/* We always return the same value for LD_ECHO and LD_EDIT */
return s->echoedit;
return s->ldisc_opts[option];
}
static void ssh1_set_ldisc_option(ConnectionLayer *cl, int option, int value)
{
struct ssh1_connection_state *s =
container_of(cl, struct ssh1_connection_state, cl);
s->ldisc_opts[option] = value;
}
static void ssh1_enable_x_fwd(ConnectionLayer *cl)
{
struct ssh1_connection_state *s =
container_of(cl, struct ssh1_connection_state, cl);
s->X11_fwd_enabled = TRUE;
}
static void ssh1_enable_agent_fwd(ConnectionLayer *cl)
{
struct ssh1_connection_state *s =
container_of(cl, struct ssh1_connection_state, cl);
s->agent_fwd_enabled = TRUE;
}
static void ssh1_set_wants_user_input(ConnectionLayer *cl, int wanted)
{
struct ssh1_connection_state *s =
container_of(cl, struct ssh1_connection_state, cl);
s->want_user_input = wanted;
s->finished_setup = TRUE;
}
static int ssh1_connection_want_user_input(PacketProtocolLayer *ppl)
{
struct ssh1_connection_state *s =
container_of(ppl, struct ssh1_connection_state, ppl);
return s->session_ready && !s->session_eof_sent;
return s->want_user_input;
}
static void ssh1_connection_got_user_input(PacketProtocolLayer *ppl)
{
struct ssh1_connection_state *s =
container_of(ppl, struct ssh1_connection_state, ppl);
if (s->session_ready && !s->session_eof_sent)
queue_idempotent_callback(&s->ppl.ic_process_queue);
while (s->mainchan && bufchain_size(s->ppl.user_input) > 0) {
/*
* Add user input to the main channel's buffer.
*/
void *data;
int len;
bufchain_prefix(s->ppl.user_input, &data, &len);
if (len > 512)
len = 512;
sshfwd_write(&s->mainchan_sc, data, len);
bufchain_consume(s->ppl.user_input, len);
}
}
static void ssh1_connection_reconfigure(PacketProtocolLayer *ppl, Conf *conf)