diff --git a/Recipe b/Recipe index 581d030c..4aff0ab8 100644 --- a/Recipe +++ b/Recipe @@ -256,7 +256,7 @@ SSH = ssh sshcommon ssh1bpp ssh2bpp ssh2bpp-bare ssh1censor ssh2censor + sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd + sshaes sshccp sshsh256 sshsh512 sshbn wildcard pinger ssharcf + sshgssc pgssapi sshshare sshecc aqsync marshal nullplug agentf - + sshmac + + sshmac mainchan WINSSH = SSH winnoise wincapi winpgntc wingss winshare winnps winnpc + winhsock errsock UXSSH = SSH uxnoise uxagentc uxgss uxshare diff --git a/defs.h b/defs.h index ec31c246..84df209a 100644 --- a/defs.h +++ b/defs.h @@ -62,6 +62,7 @@ typedef struct Ssh Ssh; typedef struct Channel Channel; typedef struct SshChannel SshChannel; +typedef struct mainchan mainchan; typedef struct ssh_sharing_state ssh_sharing_state; typedef struct ssh_sharing_connstate ssh_sharing_connstate; diff --git a/mainchan.c b/mainchan.c new file mode 100644 index 00000000..e1b75eb6 --- /dev/null +++ b/mainchan.c @@ -0,0 +1,609 @@ +/* + * SSH main session channel handling. + */ + +#include +#include +#include + +#include "putty.h" +#include "ssh.h" +#include "sshppl.h" +#include "sshchan.h" + +static void mainchan_free(Channel *chan); +static void mainchan_open_confirmation(Channel *chan); +static void mainchan_open_failure(Channel *chan, const char *errtext); +static int mainchan_send(Channel *chan, int is_stderr, const void *, int); +static void mainchan_send_eof(Channel *chan); +static void mainchan_set_input_wanted(Channel *chan, int wanted); +static char *mainchan_log_close_msg(Channel *chan); +static int mainchan_rcvd_exit_status(Channel *chan, int status); +static int mainchan_rcvd_exit_signal( + Channel *chan, ptrlen signame, int core_dumped, ptrlen msg); +static int mainchan_rcvd_exit_signal_numeric( + Channel *chan, int signum, int core_dumped, ptrlen msg); +static void mainchan_request_response(Channel *chan, int success); + +static const struct ChannelVtable mainchan_channelvt = { + mainchan_free, + mainchan_open_confirmation, + mainchan_open_failure, + mainchan_send, + mainchan_send_eof, + mainchan_set_input_wanted, + mainchan_log_close_msg, + chan_no_eager_close, + mainchan_rcvd_exit_status, + mainchan_rcvd_exit_signal, + mainchan_rcvd_exit_signal_numeric, + mainchan_request_response, +}; + +typedef enum MainChanType { + MAINCHAN_SESSION, MAINCHAN_DIRECT_TCPIP +} MainChanType; + +typedef struct mainchan { + SshChannel *sc; + Conf *conf; + PacketProtocolLayer *ppl; + ConnectionLayer *cl; + + MainChanType type; + int is_simple; + + int req_x11, req_agent, req_pty, req_cmd_primary, req_cmd_fallback; + int n_req_env, n_env_replies, n_env_fails; + int eof_pending, eof_sent, got_pty, ready; + + int term_width, term_height; + + Channel chan; +} mainchan; + +mainchan *mainchan_new( + PacketProtocolLayer *ppl, ConnectionLayer *cl, Conf *conf, + int term_width, int term_height, int is_simple, SshChannel **sc_out) +{ + mainchan *mc; + + if (conf_get_int(conf, CONF_ssh_no_shell)) + return NULL; /* no main channel at all */ + + mc = snew(mainchan); + memset(mc, 0, sizeof(mainchan)); + mc->ppl = ppl; + mc->cl = cl; + mc->conf = conf_copy(conf); + mc->term_width = term_width; + mc->term_height = term_height; + mc->is_simple = is_simple; + + mc->sc = NULL; + mc->chan.vt = &mainchan_channelvt; + mc->chan.initial_fixed_window_size = 0; + + if (*conf_get_str(mc->conf, CONF_ssh_nc_host)) { + const char *host = conf_get_str(mc->conf, CONF_ssh_nc_host); + int port = conf_get_int(mc->conf, CONF_ssh_nc_port); + + mc->sc = ssh_lportfwd_open(cl, host, port, "main channel", &mc->chan); + mc->type = MAINCHAN_DIRECT_TCPIP; + } else { + mc->sc = ssh_session_open(cl, &mc->chan); + mc->type = MAINCHAN_SESSION; + } + + *sc_out = mc->sc; + return mc; +} + +static void mainchan_free(Channel *chan) +{ + assert(chan->vt == &mainchan_channelvt); + mainchan *mc = container_of(chan, mainchan, chan); + conf_free(mc->conf); + sfree(mc); +} + +static void mainchan_try_fallback_command(mainchan *mc); +static void mainchan_ready(mainchan *mc); + +static void mainchan_open_confirmation(Channel *chan) +{ + mainchan *mc = container_of(chan, mainchan, chan); + PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */ + + seat_update_specials_menu(mc->ppl->seat); + ppl_logevent(("Opened main channel")); + + if (mc->is_simple) + sshfwd_hint_channel_is_simple(mc->sc); + + if (mc->type == MAINCHAN_SESSION) { + /* + * Send the CHANNEL_REQUESTS for the main session channel. + */ + char *key, *val, *cmd; + struct X11Display *x11disp; + struct X11FakeAuth *x11auth; + int retry_cmd_now = FALSE; + + if (conf_get_int(mc->conf, CONF_x11_forward)) {; + char *x11_setup_err; + if ((x11disp = x11_setup_display( + conf_get_str(mc->conf, CONF_x11_display), + mc->conf, &x11_setup_err)) == NULL) { + ppl_logevent(("X11 forwarding not enabled: unable to" + " initialise X display: %s", x11_setup_err)); + sfree(x11_setup_err); + } else { + x11auth = ssh_add_x11_display( + mc->cl, conf_get_int(mc->conf, CONF_x11_auth), x11disp); + + sshfwd_request_x11_forwarding( + mc->sc, TRUE, x11auth->protoname, x11auth->datastring, + x11disp->screennum, FALSE); + mc->req_x11 = TRUE; + } + } + + if (ssh_agent_forwarding_permitted(mc->cl)) { + sshfwd_request_agent_forwarding(mc->sc, TRUE); + mc->req_agent = TRUE; + } + + if (!conf_get_int(mc->conf, CONF_nopty)) { + sshfwd_request_pty( + mc->sc, TRUE, mc->conf, mc->term_width, mc->term_height); + mc->req_pty = TRUE; + } + + for (val = conf_get_str_strs(mc->conf, CONF_environmt, NULL, &key); + val != NULL; + val = conf_get_str_strs(mc->conf, CONF_environmt, key, &key)) { + sshfwd_send_env_var(mc->sc, TRUE, key, val); + mc->n_req_env++; + } + if (mc->n_req_env) + ppl_logevent(("Sent %d environment variables", mc->n_req_env)); + + cmd = conf_get_str(mc->conf, CONF_remote_cmd); + if (conf_get_int(mc->conf, CONF_ssh_subsys)) { + retry_cmd_now = !sshfwd_start_subsystem(mc->sc, TRUE, cmd); + } else if (*cmd) { + sshfwd_start_command(mc->sc, TRUE, cmd); + } else { + sshfwd_start_shell(mc->sc, TRUE); + } + + if (retry_cmd_now) + mainchan_try_fallback_command(mc); + else + mc->req_cmd_primary = TRUE; + + } else { + ssh_set_ldisc_option(mc->cl, LD_ECHO, TRUE); + ssh_set_ldisc_option(mc->cl, LD_EDIT, TRUE); + mainchan_ready(mc); + } +} + +static void mainchan_try_fallback_command(mainchan *mc) +{ + const char *cmd = conf_get_str(mc->conf, CONF_remote_cmd2); + if (conf_get_int(mc->conf, CONF_ssh_subsys2)) { + sshfwd_start_subsystem(mc->sc, TRUE, cmd); + } else { + sshfwd_start_command(mc->sc, TRUE, cmd); + } + mc->req_cmd_fallback = TRUE; +} + +static void mainchan_request_response(Channel *chan, int success) +{ + assert(chan->vt == &mainchan_channelvt); + mainchan *mc = container_of(chan, mainchan, chan); + PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */ + + if (mc->req_x11) { + mc->req_x11 = FALSE; + + if (success) { + ppl_logevent(("X11 forwarding enabled")); + ssh_enable_x_fwd(mc->cl); + } else { + ppl_logevent(("X11 forwarding refused")); + } + return; + } + + if (mc->req_agent) { + mc->req_agent = FALSE; + + if (success) { + ppl_logevent(("Agent forwarding enabled")); + ssh_enable_agent_fwd(mc->cl); + } else { + ppl_logevent(("Agent forwarding refused")); + } + return; + } + + if (mc->req_pty) { + mc->req_pty = FALSE; + + if (success) { + ppl_logevent(("Allocated pty")); + mc->got_pty = TRUE; + } else { + ppl_logevent(("Server refused to allocate pty")); + ppl_printf(("Server refused to allocate pty\r\n")); + ssh_set_ldisc_option(mc->cl, LD_ECHO, TRUE); + ssh_set_ldisc_option(mc->cl, LD_EDIT, TRUE); + } + return; + } + + if (mc->n_env_replies < mc->n_req_env) { + int j = mc->n_env_replies++; + if (!success) { + ppl_logevent(("Server refused to set environment variable %s", + conf_get_str_nthstrkey(mc->conf, + CONF_environmt, j))); + mc->n_env_fails++; + } + + if (mc->n_env_replies == mc->n_req_env) { + if (mc->n_env_fails == 0) { + ppl_logevent(("All environment variables successfully set")); + } else if (mc->n_env_fails == mc->n_req_env) { + ppl_logevent(("All environment variables refused")); + ppl_printf(("Server refused to set environment " + "variables\r\n")); + } else { + ppl_printf(("Server refused to set all environment " + "variables\r\n")); + } + } + return; + } + + if (mc->req_cmd_primary) { + mc->req_cmd_primary = FALSE; + + if (success) { + ppl_logevent(("Started a shell/command")); + mainchan_ready(mc); + } else if (*conf_get_str(mc->conf, CONF_remote_cmd2)) { + ppl_logevent(("Primary command failed; attempting fallback")); + mainchan_try_fallback_command(mc); + } else { + /* + * If there's no remote_cmd2 configured, then we have no + * fallback command, so we've run out of options. + */ + ssh_sw_abort(mc->ppl->ssh, + "Server refused to start a shell/command"); + } + return; + } + + if (mc->req_cmd_fallback) { + mc->req_cmd_fallback = FALSE; + + if (success) { + ppl_logevent(("Started a shell/command")); + ssh_got_fallback_cmd(mc->ppl->ssh); + mainchan_ready(mc); + } else { + ssh_sw_abort(mc->ppl->ssh, + "Server refused to start a shell/command"); + } + return; + } +} + +static void mainchan_ready(mainchan *mc) +{ + mc->ready = TRUE; + + ssh_set_wants_user_input(mc->cl, TRUE); + ssh_ppl_got_user_input(mc->ppl); /* in case any is already queued */ + + /* If an EOF arrived before we were ready, handle it now. */ + if (mc->eof_pending) { + mc->eof_pending = FALSE; + mainchan_special_cmd(mc, SS_EOF, 0); + } + + ssh_ldisc_update(mc->ppl->ssh); + queue_idempotent_callback(&mc->ppl->ic_process_queue); +} + +struct mainchan_open_failure_abort_ctx { + Ssh *ssh; + char *abort_message; +}; + +static void mainchan_open_failure_abort(void *vctx) +{ + struct mainchan_open_failure_abort_ctx *ctx = + (struct mainchan_open_failure_abort_ctx *)vctx; + ssh_sw_abort( + ctx->ssh, "Server refused to open main channel: %s", + ctx->abort_message); + sfree(ctx->abort_message); + sfree(ctx); +} + +static void mainchan_open_failure(Channel *chan, const char *errtext) +{ + assert(chan->vt == &mainchan_channelvt); + mainchan *mc = container_of(chan, mainchan, chan); + + struct mainchan_open_failure_abort_ctx *ctx = + snew(struct mainchan_open_failure_abort_ctx); + + ctx->ssh = mc->ppl->ssh; + ctx->abort_message = dupstr(errtext); + queue_toplevel_callback(mainchan_open_failure_abort, ctx); +} + +static int mainchan_send(Channel *chan, int is_stderr, + const void *data, int length) +{ + assert(chan->vt == &mainchan_channelvt); + mainchan *mc = container_of(chan, mainchan, chan); + return seat_output(mc->ppl->seat, is_stderr, data, length); +} + +static void mainchan_send_eof(Channel *chan) +{ + assert(chan->vt == &mainchan_channelvt); + mainchan *mc = container_of(chan, mainchan, chan); + PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */ + + if (!mc->eof_sent & (seat_eof(mc->ppl->seat) || mc->got_pty)) { + /* + * Either seat_eof told us that the front end wants us to + * close the outgoing side of the connection as soon as we see + * EOF from the far end, or else we've unilaterally decided to + * do that because we've allocated a remote pty and hence EOF + * isn't a particularly meaningful concept. + */ + sshfwd_write_eof(mc->sc); + ppl_logevent(("Sent EOF message")); + } + mc->eof_sent = TRUE; + ssh_set_wants_user_input(mc->cl, FALSE); /* now stop reading from stdin */ +} + +static void mainchan_set_input_wanted(Channel *chan, int wanted) +{ + assert(chan->vt == &mainchan_channelvt); + mainchan *mc = container_of(chan, mainchan, chan); + + /* + * This is the main channel of the SSH session, i.e. the one tied + * to the standard input (or GUI) of the primary SSH client user + * interface. So ssh->send_ok is how we control whether we're + * reading from that input. + */ + ssh_set_wants_user_input(mc->cl, wanted); +} + +static char *mainchan_log_close_msg(Channel *chan) +{ + return dupstr("Main session channel closed"); +} + +static int mainchan_rcvd_exit_status(Channel *chan, int status) +{ + assert(chan->vt == &mainchan_channelvt); + mainchan *mc = container_of(chan, mainchan, chan); + PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */ + + ssh_got_exitcode(mc->ppl->ssh, status); + ppl_logevent(("Session sent command exit status %d", status)); + return TRUE; +} + +static void mainchan_log_exit_signal_common( + mainchan *mc, const char *sigdesc, + int core_dumped, ptrlen msg) +{ + PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */ + + const char *core_msg = core_dumped ? " (core dumped)" : ""; + const char *msg_pre = (msg.len ? " (" : ""); + const char *msg_post = (msg.len ? ")" : ""); + ppl_logevent(("Session exited on %s%s%s%.*s%s", + sigdesc, core_msg, msg_pre, PTRLEN_PRINTF(msg), msg_post)); +} + +static int mainchan_rcvd_exit_signal( + Channel *chan, ptrlen signame, int core_dumped, ptrlen msg) +{ + assert(chan->vt == &mainchan_channelvt); + mainchan *mc = container_of(chan, mainchan, chan); + int exitcode; + char *signame_str; + + /* + * Translate the signal description back into a locally + * meaningful number. + */ + + if (0) + ; +#define TRANSLATE_SIGNAL(s) \ + else if (ptrlen_eq_string(signame, #s)) \ + exitcode = 128 + SIG ## s +#ifdef SIGABRT + TRANSLATE_SIGNAL(ABRT); +#endif +#ifdef SIGALRM + TRANSLATE_SIGNAL(ALRM); +#endif +#ifdef SIGFPE + TRANSLATE_SIGNAL(FPE); +#endif +#ifdef SIGHUP + TRANSLATE_SIGNAL(HUP); +#endif +#ifdef SIGILL + TRANSLATE_SIGNAL(ILL); +#endif +#ifdef SIGINT + TRANSLATE_SIGNAL(INT); +#endif +#ifdef SIGKILL + TRANSLATE_SIGNAL(KILL); +#endif +#ifdef SIGPIPE + TRANSLATE_SIGNAL(PIPE); +#endif +#ifdef SIGQUIT + TRANSLATE_SIGNAL(QUIT); +#endif +#ifdef SIGSEGV + TRANSLATE_SIGNAL(SEGV); +#endif +#ifdef SIGTERM + TRANSLATE_SIGNAL(TERM); +#endif +#ifdef SIGUSR1 + TRANSLATE_SIGNAL(USR1); +#endif +#ifdef SIGUSR2 + TRANSLATE_SIGNAL(USR2); +#endif +#undef TRANSLATE_SIGNAL + else + exitcode = 128; + + ssh_got_exitcode(mc->ppl->ssh, exitcode); + if (exitcode == 128) + signame_str = dupprintf("unrecognised signal \"%.*s\"", + PTRLEN_PRINTF(signame)); + else + signame_str = dupprintf("signal SIG%.*s", PTRLEN_PRINTF(signame)); + mainchan_log_exit_signal_common(mc, signame_str, core_dumped, msg); + sfree(signame_str); + return TRUE; +} + +static int mainchan_rcvd_exit_signal_numeric( + Channel *chan, int signum, int core_dumped, ptrlen msg) +{ + assert(chan->vt == &mainchan_channelvt); + mainchan *mc = container_of(chan, mainchan, chan); + char *signum_str; + + ssh_got_exitcode(mc->ppl->ssh, 128 + signum); + signum_str = dupprintf("signal %d", signum); + mainchan_log_exit_signal_common(mc, signum_str, core_dumped, msg); + sfree(signum_str); + return TRUE; +} + +/* + * List of signal names defined by RFC 4254. These include all the ISO + * C signals, but are a subset of the POSIX required signals. + * + * The list macro takes parameters MAIN and SUB, which is an arbitrary + * UI decision to expose the signals we think users are most likely to + * want, with extra descriptive text, and relegate the less probable + * ones to a submenu for people who know what they're doing. + */ +#define SIGNAL_LIST(MAIN, SUB) \ + MAIN(INT, "Interrupt") \ + MAIN(TERM, "Terminate") \ + MAIN(KILL, "Kill") \ + MAIN(QUIT, "Quit") \ + MAIN(HUP, "Hangup") \ + SUB(ABRT) \ + SUB(ALRM) \ + SUB(FPE) \ + SUB(ILL) \ + SUB(PIPE) \ + SUB(SEGV) \ + SUB(USR1) \ + SUB(USR2) \ + /* end of list */ + +void mainchan_get_specials( + mainchan *mc, add_special_fn_t add_special, void *ctx) +{ + /* FIXME: this _does_ depend on whether these services are supported */ + + add_special(ctx, "Break", SS_BRK, 0); + + #define ADD_MAIN(name, desc) \ + add_special(ctx, "SIG" #name " (" desc ")", SS_SIG ## name, 0); + #define ADD_SUB(name) \ + add_special(ctx, "SIG" #name, SS_SIG ## name, 0); + + #define NO_ADD_SUB(name) + #define NO_ADD_MAIN(name, desc) + + SIGNAL_LIST(ADD_MAIN, NO_ADD_SUB); + add_special(ctx, "More signals", SS_SUBMENU, 0); + SIGNAL_LIST(NO_ADD_MAIN, ADD_SUB); + add_special(ctx, NULL, SS_EXITMENU, 0); + + #undef ADD_MAIN + #undef ADD_SUB + #undef NO_ADD_MAIN + #undef NO_ADD_SUB +} + +static const char *ssh_signal_lookup(SessionSpecialCode code) +{ + #define CHECK_SUB(name) \ + if (code == SS_SIG ## name) return #name; + #define CHECK_MAIN(name, desc) CHECK_SUB(name) + + SIGNAL_LIST(CHECK_MAIN, CHECK_SUB); + return NULL; + + #undef CHECK_MAIN + #undef CHECK_SUB +} + +void mainchan_special_cmd(mainchan *mc, SessionSpecialCode code, int arg) +{ + PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */ + const char *signame; + + if (code == SS_EOF) { + if (!mc->ready) { + /* + * Buffer the EOF to send as soon as the main channel is + * fully set up. + */ + mc->eof_pending = TRUE; + } else if (!mc->eof_sent) { + sshfwd_write_eof(mc->sc); + mc->eof_sent = TRUE; + } + } else if (code == SS_BRK) { + sshfwd_send_serial_break( + mc->sc, FALSE, 0 /* default break length */); + } else if ((signame = ssh_signal_lookup(code)) != NULL) { + /* It's a signal. */ + sshfwd_send_signal(mc->sc, FALSE, signame); + ppl_logevent(("Sent signal SIG%s", signame)); + } +} + +void mainchan_terminal_size(mainchan *mc, int width, int height) +{ + mc->term_width = width; + mc->term_height = height; + + if (mc->req_pty || mc->got_pty) + sshfwd_send_terminal_size_change(mc->sc, width, height); +} diff --git a/putty.h b/putty.h index 46b8be54..dcf37286 100644 --- a/putty.h +++ b/putty.h @@ -216,6 +216,7 @@ typedef enum { SS_SIGINT, SS_SIGKILL, SS_SIGPIPE, SS_SIGQUIT, SS_SIGSEGV, SS_SIGTERM, SS_SIGUSR1, SS_SIGUSR2, + /* * These aren't really special commands, but they appear in the * enumeration because the list returned from @@ -236,6 +237,10 @@ struct SessionSpecial { int arg; }; +/* Needed by both sshchan.h and sshppl.h */ +typedef void (*add_special_fn_t)( + void *ctx, const char *text, SessionSpecialCode code, int arg); + typedef enum { MBT_NOTHING, MBT_LEFT, MBT_MIDDLE, MBT_RIGHT, /* `raw' button designations */ @@ -357,7 +362,8 @@ enum { * Line discipline options which the backend might try to control. */ LD_EDIT, /* local line editing */ - LD_ECHO /* local echo */ + LD_ECHO, /* local echo */ + LD_N_OPTIONS }; enum { diff --git a/ssh.h b/ssh.h index 0ed25b2a..9c198109 100644 --- a/ssh.h +++ b/ssh.h @@ -186,6 +186,9 @@ void share_setup_x11_channel(ssh_sharing_connstate *cs, share_channel *chan, int protomajor, int protominor, const void *initial_data, int initial_len); +struct X11Display; +struct X11FakeAuth; + /* Structure definition centralised here because the SSH-1 and SSH-2 * connection layers both use it. But the client module (portfwd.c) * should not try to look inside here. */ @@ -215,6 +218,13 @@ struct ConnectionLayerVtable { ConnectionLayer *cl, const char *hostname, int port, const char *org, Channel *chan); + /* Initiate opening of a 'session'-type channel */ + SshChannel *(*session_open)(ConnectionLayer *cl, Channel *chan); + + /* Add an X11 display for ordinary X forwarding */ + struct X11FakeAuth *(*add_x11_display)( + ConnectionLayer *cl, int authtype, struct X11Display *x11disp); + /* Add and remove X11 displays for connection sharing downstreams */ struct X11FakeAuth *(*add_sharing_x11_display)( ConnectionLayer *cl, int authtype, ssh_sharing_connstate *share_cs, @@ -263,6 +273,20 @@ struct ConnectionLayerVtable { /* Ask the connection layer about its current preference for * line-discipline options. */ int (*ldisc_option)(ConnectionLayer *cl, int option); + + /* Communicate _to_ the connection layer (from the main session + * channel) what its preference for line-discipline options is. */ + void (*set_ldisc_option)(ConnectionLayer *cl, int option, int value); + + /* Communicate to the connection layer whether X and agent + * forwarding were successfully enabled (for purposes of + * knowing whether to accept subsequent channel-opens). */ + void (*enable_x_fwd)(ConnectionLayer *cl); + void (*enable_agent_fwd)(ConnectionLayer *cl); + + /* Communicate to the connection layer whether the main session + * channel currently wants user input. */ + void (*set_wants_user_input)(ConnectionLayer *cl, int wanted); }; struct ConnectionLayer { @@ -275,6 +299,10 @@ struct ConnectionLayer { #define ssh_rportfwd_remove(cl, rpf) ((cl)->vt->rportfwd_remove(cl, rpf)) #define ssh_lportfwd_open(cl, h, p, org, chan) \ ((cl)->vt->lportfwd_open(cl, h, p, org, chan)) +#define ssh_session_open(cl, chan) \ + ((cl)->vt->session_open(cl, chan)) +#define ssh_add_x11_display(cl, auth, disp) \ + ((cl)->vt->add_x11_display(cl, auth, disp)) #define ssh_add_sharing_x11_display(cl, auth, cs, ch) \ ((cl)->vt->add_sharing_x11_display(cl, auth, cs, ch)) #define ssh_remove_sharing_x11_display(cl, fa) \ @@ -298,6 +326,12 @@ struct ConnectionLayer { #define ssh_throttle_all_channels(cl, throttled) \ ((cl)->vt->throttle_all_channels(cl, throttled)) #define ssh_ldisc_option(cl, option) ((cl)->vt->ldisc_option(cl, option)) +#define ssh_set_ldisc_option(cl, opt, val) \ + ((cl)->vt->set_ldisc_option(cl, opt, val)) +#define ssh_enable_x_fwd(cl) ((cl)->vt->enable_x_fwd(cl)) +#define ssh_enable_agent_fwd(cl) ((cl)->vt->enable_agent_fwd(cl)) +#define ssh_set_wants_user_input(cl, wanted) \ + ((cl)->vt->set_wants_user_input(cl, wanted)) /* Exports from portfwd.c */ PortFwdManager *portfwdmgr_new(ConnectionLayer *cl); diff --git a/ssh1connection.c b/ssh1connection.c index 51e2a262..34ba5bff 100644 --- a/ssh1connection.c +++ b/ssh1connection.c @@ -113,6 +113,8 @@ static const struct ConnectionLayerVtable ssh1_connlayer_vtable = { ssh1_rportfwd_alloc, ssh1_rportfwd_remove, ssh1_lportfwd_open, + NULL /* session_open */, + NULL /* add_x11_display */, NULL /* add_sharing_x11_display */, NULL /* remove_sharing_x11_display */, NULL /* send_packet_from_downstream */, @@ -126,6 +128,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 */, }; struct ssh1_channel { diff --git a/ssh2connection.c b/ssh2connection.c index 790f4dd3..1307a6b1 100644 --- a/ssh2connection.c +++ b/ssh2connection.c @@ -23,13 +23,11 @@ struct ssh2_connection_state { ssh_sharing_state *connshare; char *peer_verstring; - struct ssh2_channel *mainchan; /* primary session channel */ - char *mainchan_open_error; - int mainchan_ready; - int echoedit; - int mainchan_eof_pending, mainchan_eof_sent; + mainchan *mainchan; + SshChannel *mainchan_sc; + int ldisc_opts[LD_N_OPTIONS]; int session_attempt, session_status; - int term_width, term_height, term_width_orig, term_height_orig; + int term_width, term_height; int want_user_input; int ssh_is_simple; @@ -107,6 +105,9 @@ static void ssh2_rportfwd_remove( static SshChannel *ssh2_lportfwd_open( ConnectionLayer *cl, const char *hostname, int port, const char *org, Channel *chan); +static SshChannel *ssh2_session_open(ConnectionLayer *cl, Channel *chan); +static struct X11FakeAuth *ssh2_add_x11_display( + ConnectionLayer *cl, int authtype, struct X11Display *x11disp); static struct X11FakeAuth *ssh2_add_sharing_x11_display( ConnectionLayer *cl, int authtype, ssh_sharing_connstate *share_cs, share_channel *share_chan); @@ -128,11 +129,17 @@ static void ssh2_stdout_unthrottle(ConnectionLayer *cl, int bufsize); static int ssh2_stdin_backlog(ConnectionLayer *cl); static void ssh2_throttle_all_channels(ConnectionLayer *cl, int throttled); static int ssh2_ldisc_option(ConnectionLayer *cl, int option); +static void ssh2_set_ldisc_option(ConnectionLayer *cl, int option, int value); +static void ssh2_enable_x_fwd(ConnectionLayer *cl); +static void ssh2_enable_agent_fwd(ConnectionLayer *cl); +static void ssh2_set_wants_user_input(ConnectionLayer *cl, int wanted); static const struct ConnectionLayerVtable ssh2_connlayer_vtable = { ssh2_rportfwd_alloc, ssh2_rportfwd_remove, ssh2_lportfwd_open, + ssh2_session_open, + ssh2_add_x11_display, ssh2_add_sharing_x11_display, ssh2_remove_sharing_x11_display, ssh2_send_packet_from_downstream, @@ -146,6 +153,10 @@ static const struct ConnectionLayerVtable ssh2_connlayer_vtable = { ssh2_stdin_backlog, ssh2_throttle_all_channels, ssh2_ldisc_option, + ssh2_set_ldisc_option, + ssh2_enable_x_fwd, + ssh2_enable_agent_fwd, + ssh2_set_wants_user_input, }; static char *ssh2_channel_open_failure_error_text(PktIn *pktin) @@ -340,8 +351,6 @@ static void ssh2_queue_global_request_handler( s->globreq_tail = ogr; } -static void create_mainchan(struct ssh2_connection_state *s, Conf *conf); - static int ssh2_channelcmp(void *av, void *bv) { const struct ssh2_channel *a = (const struct ssh2_channel *) av; @@ -382,8 +391,14 @@ static void ssh2_channel_free(struct ssh2_channel *c) c->chanreq_head = c->chanreq_head->next; sfree(chanreq); } - if (c->chan) + if (c->chan) { + struct ssh2_connection_state *s = c->connlayer; + if (s->mainchan_sc == &c->sc) { + s->mainchan = NULL; + s->mainchan_sc = NULL; + } chan_free(c->chan); + } sfree(c); } @@ -442,8 +457,6 @@ static void ssh2_connection_free(PacketProtocolLayer *ppl) conf_free(s->conf); - sfree(s->mainchan_open_error); - while ((c = delpos234(s->channels, 0)) != NULL) ssh2_channel_free(c); freetree234(s->channels); @@ -574,6 +587,7 @@ static int ssh2_connection_filter_queue(struct ssh2_connection_state *s) error = chan_open_auth_agent(s, &c->chan, &c->sc); } else { error = dupstr("Unsupported channel type requested"); + c->chan = NULL; } if (share_ctx) { @@ -706,7 +720,6 @@ static int ssh2_connection_filter_queue(struct ssh2_connection_state *s) chan_open_failed(c->chan, err); sfree(err); } - chan_free(c->chan); del234(s->channels, c); ssh2_channel_free(c); @@ -1096,7 +1109,9 @@ static void ssh2_connection_process_queue(PacketProtocolLayer *ppl) /* * Create the main session channel, if any. */ - create_mainchan(s, s->conf); + s->mainchan = mainchan_new( + &s->ppl, &s->cl, s->conf, s->term_width, s->term_height, + s->ssh_is_simple, &s->mainchan_sc); /* * Transfer data! @@ -1705,6 +1720,27 @@ static SshChannel *ssh2_lportfwd_open( return &c->sc; } +static SshChannel *ssh2_session_open(ConnectionLayer *cl, Channel *chan) +{ + struct ssh2_connection_state *s = + container_of(cl, struct ssh2_connection_state, cl); + PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ + struct ssh2_channel *c = snew(struct ssh2_channel); + PktOut *pktout; + + c->connlayer = s; + ssh2_channel_init(c); + c->halfopen = TRUE; + c->chan = chan; + + ppl_logevent(("Opening main session channel")); + + pktout = ssh2_chanopen_init(c, "session"); + pq_push(s->ppl.out_pq, pktout); + + return &c->sc; +} + static void ssh2_rportfwd_globreq_response(struct ssh2_connection_state *s, PktIn *pktin, void *ctx) { @@ -1816,6 +1852,16 @@ static void ssh2_sharing_no_more_downstreams(ConnectionLayer *cl) queue_toplevel_callback(ssh2_check_termination_callback, s); } +static struct X11FakeAuth *ssh2_add_x11_display( + ConnectionLayer *cl, int authtype, struct X11Display *disp) +{ + struct ssh2_connection_state *s = + container_of(cl, struct ssh2_connection_state, cl); + struct X11FakeAuth *auth = x11_invent_fake_auth(s->x11authtree, authtype); + auth->disp = disp; + return auth; +} + static struct X11FakeAuth *ssh2_add_sharing_x11_display( ConnectionLayer *cl, int authtype, ssh_sharing_connstate *share_cs, share_channel *share_chan) @@ -1888,497 +1934,6 @@ static int ssh2_agent_forwarding_permitted(ConnectionLayer *cl) return conf_get_int(s->conf, CONF_agentfwd) && agent_exists(); } -static void mainchan_free(Channel *chan); -static void mainchan_open_confirmation(Channel *chan); -static void mainchan_open_failure(Channel *chan, const char *errtext); -static int mainchan_send(Channel *chan, int is_stderr, const void *, int); -static void mainchan_send_eof(Channel *chan); -static void mainchan_set_input_wanted(Channel *chan, int wanted); -static char *mainchan_log_close_msg(Channel *chan); -static int mainchan_rcvd_exit_status(Channel *chan, int status); -static int mainchan_rcvd_exit_signal( - Channel *chan, ptrlen signame, int core_dumped, ptrlen msg); -static int mainchan_rcvd_exit_signal_numeric( - Channel *chan, int signum, int core_dumped, ptrlen msg); -static void mainchan_request_response(Channel *chan, int success); - -static const struct ChannelVtable mainchan_channelvt = { - mainchan_free, - mainchan_open_confirmation, - mainchan_open_failure, - mainchan_send, - mainchan_send_eof, - mainchan_set_input_wanted, - mainchan_log_close_msg, - chan_no_eager_close, - mainchan_rcvd_exit_status, - mainchan_rcvd_exit_signal, - mainchan_rcvd_exit_signal_numeric, - mainchan_request_response, -}; - -typedef enum MainChanType { - MAINCHAN_SESSION, MAINCHAN_DIRECT_TCPIP -} MainChanType; - -typedef struct mainchan { - struct ssh2_connection_state *connlayer; - SshChannel *sc; - MainChanType type; - Conf *conf; - int req_x11, req_agent, req_pty, req_cmd_primary, req_cmd_fallback; - int n_req_env, n_env_replies, n_env_fails; - - Channel chan; -} mainchan; - -static void create_mainchan(struct ssh2_connection_state *s, Conf *conf) -{ - PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ - mainchan *mc; - PktOut *pktout; - - if (conf_get_int(s->conf, CONF_ssh_no_shell)) - return; /* do nothing */ - - mc = snew(mainchan); - memset(mc, 0, sizeof(mainchan)); - mc->connlayer = s; - mc->sc = NULL; - mc->chan.vt = &mainchan_channelvt; - mc->chan.initial_fixed_window_size = 0; - mc->conf = conf_copy(conf); - - if (*conf_get_str(mc->conf, CONF_ssh_nc_host)) { - mc->sc = ssh_lportfwd_open( - &s->cl, conf_get_str(mc->conf, CONF_ssh_nc_host), - conf_get_int(mc->conf, CONF_ssh_nc_port), - "main channel", &mc->chan); - s->mainchan = container_of(mc->sc, struct ssh2_channel, sc); - mc->type = MAINCHAN_DIRECT_TCPIP; - } else { - s->mainchan = snew(struct ssh2_channel); - mc->sc = &s->mainchan->sc; - s->mainchan->connlayer = s; - ssh2_channel_init(s->mainchan); - s->mainchan->chan = &mc->chan; - s->mainchan->halfopen = TRUE; - pktout = ssh2_chanopen_init(s->mainchan, "session"); - ppl_logevent(("Opening session as main channel")); - pq_push(s->ppl.out_pq, pktout); - mc->type = MAINCHAN_SESSION; - } -} - -static void mainchan_free(Channel *chan) -{ - assert(chan->vt == &mainchan_channelvt); - mainchan *mc = container_of(chan, mainchan, chan); - struct ssh2_connection_state *s = mc->connlayer; - s->mainchan = NULL; - conf_free(mc->conf); - sfree(mc); -} - -static void mainchan_try_fallback_command(mainchan *mc); -static void mainchan_ready(mainchan *mc); - -static void mainchan_open_confirmation(Channel *chan) -{ - mainchan *mc = container_of(chan, mainchan, chan); - struct ssh2_connection_state *s = mc->connlayer; - PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ - - seat_update_specials_menu(s->ppl.seat); - ppl_logevent(("Opened main channel")); - - if (s->ssh_is_simple) - sshfwd_hint_channel_is_simple(mc->sc); - - if (mc->type == MAINCHAN_SESSION) { - /* - * Send the CHANNEL_REQUESTS for the main session channel. - */ - char *key, *val, *cmd; - struct X11Display *x11disp; - struct X11FakeAuth *x11auth; - int retry_cmd_now = FALSE; - - if (conf_get_int(mc->conf, CONF_x11_forward)) {; - char *x11_setup_err; - if ((x11disp = x11_setup_display( - conf_get_str(mc->conf, CONF_x11_display), - mc->conf, &x11_setup_err)) == NULL) { - ppl_logevent(("X11 forwarding not enabled: unable to" - " initialise X display: %s", x11_setup_err)); - sfree(x11_setup_err); - } else { - x11auth = x11_invent_fake_auth( - s->x11authtree, conf_get_int(mc->conf, CONF_x11_auth)); - x11auth->disp = x11disp; - - sshfwd_request_x11_forwarding( - mc->sc, TRUE, x11auth->protoname, x11auth->datastring, - x11disp->screennum, FALSE); - mc->req_x11 = TRUE; - } - } - - if (ssh_agent_forwarding_permitted(&s->cl)) { - sshfwd_request_agent_forwarding(mc->sc, TRUE); - mc->req_agent = TRUE; - } - - if (!conf_get_int(mc->conf, CONF_nopty)) { - sshfwd_request_pty( - mc->sc, TRUE, mc->conf, s->term_width, s->term_height); - /* Record the initial width/height we requested, so we - * know whether we need to send a change later once - * everything is set up (if the window is resized in - * between) */ - s->term_width_orig = s->term_width; - s->term_height_orig = s->term_height; - mc->req_pty = TRUE; - } - - for (val = conf_get_str_strs(mc->conf, CONF_environmt, NULL, &key); - val != NULL; - val = conf_get_str_strs(mc->conf, CONF_environmt, key, &key)) { - sshfwd_send_env_var(mc->sc, TRUE, key, val); - mc->n_req_env++; - } - if (mc->n_req_env) - ppl_logevent(("Sent %d environment variables", mc->n_req_env)); - - cmd = conf_get_str(s->conf, CONF_remote_cmd); - if (conf_get_int(s->conf, CONF_ssh_subsys)) { - retry_cmd_now = !sshfwd_start_subsystem(mc->sc, TRUE, cmd); - } else if (*cmd) { - sshfwd_start_command(mc->sc, TRUE, cmd); - } else { - sshfwd_start_shell(mc->sc, TRUE); - } - - if (retry_cmd_now) - mainchan_try_fallback_command(mc); - else - mc->req_cmd_primary = TRUE; - - } else { - s->echoedit = TRUE; - mainchan_ready(mc); - } -} - -static void mainchan_try_fallback_command(mainchan *mc) -{ - const char *cmd = conf_get_str(mc->conf, CONF_remote_cmd2); - if (conf_get_int(mc->conf, CONF_ssh_subsys2)) { - sshfwd_start_subsystem(mc->sc, TRUE, cmd); - } else { - sshfwd_start_command(mc->sc, TRUE, cmd); - } - mc->req_cmd_fallback = TRUE; -} - -static void mainchan_request_response(Channel *chan, int success) -{ - assert(chan->vt == &mainchan_channelvt); - mainchan *mc = container_of(chan, mainchan, chan); - struct ssh2_connection_state *s = mc->connlayer; - PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ - - if (mc->req_x11) { - mc->req_x11 = FALSE; - - if (success) { - ppl_logevent(("X11 forwarding enabled")); - s->X11_fwd_enabled = TRUE; - } else { - ppl_logevent(("X11 forwarding refused")); - } - return; - } - - if (mc->req_agent) { - mc->req_agent = FALSE; - - if (success) { - ppl_logevent(("Agent forwarding enabled")); - } else { - ppl_logevent(("Agent forwarding refused")); - } - return; - } - - if (mc->req_pty) { - mc->req_pty = FALSE; - - if (success) { - ppl_logevent(("Allocated pty")); - s->agent_fwd_enabled = TRUE; - } else { - ppl_logevent(("Server refused to allocate pty")); - ppl_printf(("Server refused to allocate pty\r\n")); - s->echoedit = TRUE; - } - return; - } - - if (mc->n_env_replies < mc->n_req_env) { - int j = mc->n_env_replies++; - if (!success) { - ppl_logevent(("Server refused to set environment variable %s", - conf_get_str_nthstrkey(mc->conf, - CONF_environmt, j))); - mc->n_env_fails++; - } - - if (mc->n_env_replies == mc->n_req_env) { - if (mc->n_env_fails == 0) { - ppl_logevent(("All environment variables successfully set")); - } else if (mc->n_env_fails == mc->n_req_env) { - ppl_logevent(("All environment variables refused")); - ppl_printf(("Server refused to set environment " - "variables\r\n")); - } else { - ppl_printf(("Server refused to set all environment " - "variables\r\n")); - } - } - return; - } - - if (mc->req_cmd_primary) { - mc->req_cmd_primary = FALSE; - - if (success) { - ppl_logevent(("Started a shell/command")); - mainchan_ready(mc); - queue_idempotent_callback(&s->ppl.ic_process_queue); - } else if (*conf_get_str(mc->conf, CONF_remote_cmd2)) { - ppl_logevent(("Primary command failed; attempting fallback")); - mainchan_try_fallback_command(mc); - } else { - /* - * If there's no remote_cmd2 configured, then we have no - * fallback command, so we've run out of options. - */ - ssh_sw_abort(s->ppl.ssh, - "Server refused to start a shell/command"); - } - return; - } - - if (mc->req_cmd_fallback) { - mc->req_cmd_fallback = FALSE; - - if (success) { - ppl_logevent(("Started a shell/command")); - ssh_got_fallback_cmd(s->ppl.ssh); - mainchan_ready(mc); - queue_idempotent_callback(&s->ppl.ic_process_queue); - } else { - ssh_sw_abort(s->ppl.ssh, - "Server refused to start a shell/command"); - } - return; - } -} - -static void mainchan_ready(mainchan *mc) -{ - struct ssh2_connection_state *s = mc->connlayer; - - s->mainchan_ready = TRUE; - s->want_user_input = TRUE; - ssh_ppl_got_user_input(&s->ppl); /* in case any 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->mainchan_eof_pending) - ssh_ppl_special_cmd(&s->ppl, SS_EOF, 0); - 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); -} - -static void mainchan_open_failure(Channel *chan, const char *errtext) -{ - assert(chan->vt == &mainchan_channelvt); - mainchan *mc = container_of(chan, mainchan, chan); - struct ssh2_connection_state *s = mc->connlayer; - - ssh_sw_abort( - s->ppl.ssh, "Server refused to open main channel: %s", errtext); -} - -static int mainchan_send(Channel *chan, int is_stderr, - const void *data, int length) -{ - assert(chan->vt == &mainchan_channelvt); - mainchan *mc = container_of(chan, mainchan, chan); - struct ssh2_connection_state *s = mc->connlayer; - return seat_output(s->ppl.seat, is_stderr, data, length); -} - -static void mainchan_send_eof(Channel *chan) -{ - assert(chan->vt == &mainchan_channelvt); - mainchan *mc = container_of(chan, mainchan, chan); - struct ssh2_connection_state *s = mc->connlayer; - PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ - - if (!s->mainchan_eof_sent && (seat_eof(s->ppl.seat) || s->got_pty)) { - /* - * Either seat_eof told us that the front end wants us to - * close the outgoing side of the connection as soon as we see - * EOF from the far end, or else we've unilaterally decided to - * do that because we've allocated a remote pty and hence EOF - * isn't a particularly meaningful concept. - */ - sshfwd_write_eof(mc->sc); - ppl_logevent(("Sent EOF message")); - s->mainchan_eof_sent = TRUE; - s->want_user_input = FALSE; /* now stop reading from stdin */ - } -} - -static void mainchan_set_input_wanted(Channel *chan, int wanted) -{ - assert(chan->vt == &mainchan_channelvt); - mainchan *mc = container_of(chan, mainchan, chan); - struct ssh2_connection_state *s = mc->connlayer; - - /* - * This is the main channel of the SSH session, i.e. the one tied - * to the standard input (or GUI) of the primary SSH client user - * interface. So ssh->send_ok is how we control whether we're - * reading from that input. - */ - s->want_user_input = wanted; -} - -static char *mainchan_log_close_msg(Channel *chan) -{ - return dupstr("Main session channel closed"); -} - -static int mainchan_rcvd_exit_status(Channel *chan, int status) -{ - assert(chan->vt == &mainchan_channelvt); - mainchan *mc = container_of(chan, mainchan, chan); - struct ssh2_connection_state *s = mc->connlayer; - PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ - - ssh_got_exitcode(s->ppl.ssh, status); - ppl_logevent(("Session sent command exit status %d", status)); - return TRUE; -} - -static void ssh2_log_exit_signal_common( - struct ssh2_connection_state *s, const char *sigdesc, - int core_dumped, ptrlen msg) -{ - PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ - - const char *core_msg = core_dumped ? " (core dumped)" : ""; - const char *msg_pre = (msg.len ? " (" : ""); - const char *msg_post = (msg.len ? ")" : ""); - ppl_logevent(("Session exited on %s%s%s%.*s%s", - sigdesc, core_msg, msg_pre, PTRLEN_PRINTF(msg), msg_post)); -} - -static int mainchan_rcvd_exit_signal( - Channel *chan, ptrlen signame, int core_dumped, ptrlen msg) -{ - assert(chan->vt == &mainchan_channelvt); - mainchan *mc = container_of(chan, mainchan, chan); - struct ssh2_connection_state *s = mc->connlayer; - int exitcode; - char *signame_str; - - /* - * Translate the signal description back into a locally - * meaningful number. - */ - - if (0) - ; -#define TRANSLATE_SIGNAL(s) \ - else if (ptrlen_eq_string(signame, #s)) \ - exitcode = 128 + SIG ## s -#ifdef SIGABRT - TRANSLATE_SIGNAL(ABRT); -#endif -#ifdef SIGALRM - TRANSLATE_SIGNAL(ALRM); -#endif -#ifdef SIGFPE - TRANSLATE_SIGNAL(FPE); -#endif -#ifdef SIGHUP - TRANSLATE_SIGNAL(HUP); -#endif -#ifdef SIGILL - TRANSLATE_SIGNAL(ILL); -#endif -#ifdef SIGINT - TRANSLATE_SIGNAL(INT); -#endif -#ifdef SIGKILL - TRANSLATE_SIGNAL(KILL); -#endif -#ifdef SIGPIPE - TRANSLATE_SIGNAL(PIPE); -#endif -#ifdef SIGQUIT - TRANSLATE_SIGNAL(QUIT); -#endif -#ifdef SIGSEGV - TRANSLATE_SIGNAL(SEGV); -#endif -#ifdef SIGTERM - TRANSLATE_SIGNAL(TERM); -#endif -#ifdef SIGUSR1 - TRANSLATE_SIGNAL(USR1); -#endif -#ifdef SIGUSR2 - TRANSLATE_SIGNAL(USR2); -#endif -#undef TRANSLATE_SIGNAL - else - exitcode = 128; - - ssh_got_exitcode(s->ppl.ssh, exitcode); - if (exitcode == 128) - signame_str = dupprintf("unrecognised signal \"%.*s\"", - PTRLEN_PRINTF(signame)); - else - signame_str = dupprintf("signal SIG%.*s", PTRLEN_PRINTF(signame)); - ssh2_log_exit_signal_common(s, signame_str, core_dumped, msg); - sfree(signame_str); - return TRUE; -} - -static int mainchan_rcvd_exit_signal_numeric( - Channel *chan, int signum, int core_dumped, ptrlen msg) -{ - assert(chan->vt == &mainchan_channelvt); - mainchan *mc = container_of(chan, mainchan, chan); - struct ssh2_connection_state *s = mc->connlayer; - char *signum_str; - - ssh_got_exitcode(s->ppl.ssh, 128 + signum); - signum_str = dupprintf("signal %d", signum); - ssh2_log_exit_signal_common(s, signum_str, core_dumped, msg); - sfree(signum_str); - return TRUE; -} - static char *chan_open_x11( struct ssh2_connection_state *s, Channel **ch, SshChannel *sc, ptrlen peeraddr, int peerport) @@ -2456,31 +2011,6 @@ static char *chan_open_auth_agent( return NULL; } -/* - * List of signal names defined by RFC 4254. These include all the ISO - * C signals, but are a subset of the POSIX required signals. - * - * The list macro takes parameters MAIN and SUB, which is an arbitrary - * UI decision to expose the signals we think users are most likely to - * want, with extra descriptive text, and relegate the less probable - * ones to a submenu for people who know what they're doing. - */ -#define SIGNAL_LIST(MAIN, SUB) \ - MAIN(INT, "Interrupt") \ - MAIN(TERM, "Terminate") \ - MAIN(KILL, "Kill") \ - MAIN(QUIT, "Quit") \ - MAIN(HUP, "Hangup") \ - SUB(ABRT) \ - SUB(ALRM) \ - SUB(FPE) \ - SUB(ILL) \ - SUB(PIPE) \ - SUB(SEGV) \ - SUB(USR1) \ - SUB(USR2) \ - /* end of list */ - static int ssh2_connection_get_specials( PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx) { @@ -2489,26 +2019,7 @@ static int ssh2_connection_get_specials( int toret = FALSE; if (s->mainchan) { - add_special(ctx, "Break", SS_BRK, 0); - - #define ADD_MAIN(name, desc) \ - add_special(ctx, "SIG" #name " (" desc ")", SS_SIG ## name, 0); - #define ADD_SUB(name) \ - add_special(ctx, "SIG" #name, SS_SIG ## name, 0); - - #define NO_ADD_SUB(name) - #define NO_ADD_MAIN(name, desc) - - SIGNAL_LIST(ADD_MAIN, NO_ADD_SUB); - add_special(ctx, "More signals", SS_SUBMENU, 0); - SIGNAL_LIST(NO_ADD_MAIN, ADD_SUB); - add_special(ctx, NULL, SS_EXITMENU, 0); - - #undef ADD_MAIN - #undef ADD_SUB - #undef NO_ADD_MAIN - #undef NO_ADD_SUB - + mainchan_get_specials(s->mainchan, add_special, ctx); toret = TRUE; } @@ -2528,26 +2039,12 @@ static int ssh2_connection_get_specials( return toret; } -static const char *ssh_signal_lookup(SessionSpecialCode code) -{ - #define CHECK_SUB(name) \ - if (code == SS_SIG ## name) return #name; - #define CHECK_MAIN(name, desc) CHECK_SUB(name) - - SIGNAL_LIST(CHECK_MAIN, CHECK_SUB); - return NULL; - - #undef CHECK_MAIN - #undef CHECK_SUB -} - static void ssh2_connection_special_cmd(PacketProtocolLayer *ppl, SessionSpecialCode code, int arg) { struct ssh2_connection_state *s = container_of(ppl, struct ssh2_connection_state, ppl); PktOut *pktout; - const char *signame; if (code == SS_PING || code == SS_NOP) { if (!(s->ppl.remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) { @@ -2555,30 +2052,8 @@ static void ssh2_connection_special_cmd(PacketProtocolLayer *ppl, put_stringz(pktout, ""); pq_push(s->ppl.out_pq, pktout); } - } else if (code == SS_EOF) { - if (!s->mainchan_ready) { - /* - * Buffer the EOF to send as soon as the main channel is - * fully set up. - */ - s->mainchan_eof_pending = TRUE; - } else if (s->mainchan && !s->mainchan_eof_sent) { - sshfwd_write_eof(&s->mainchan->sc); - } - } else if (code == SS_BRK) { - if (s->mainchan) { - pktout = ssh2_chanreq_init(s->mainchan, "break", NULL, NULL); - put_uint32(pktout, 0); /* default break length */ - pq_push(s->ppl.out_pq, pktout); - } - } else if ((signame = ssh_signal_lookup(code)) != NULL) { - /* It's a signal. */ - if (s->mainchan) { - pktout = ssh2_chanreq_init(s->mainchan, "signal", NULL, NULL); - put_stringz(pktout, signame); - pq_push(s->ppl.out_pq, pktout); - ppl_logevent(("Sent signal SIG%s", signame)); - } + } else if (s->mainchan) { + mainchan_special_cmd(s->mainchan, code, arg); } } @@ -2589,16 +2064,8 @@ static void ssh2_terminal_size(ConnectionLayer *cl, int width, int height) s->term_width = width; s->term_height = height; - - if (s->mainchan_ready) { - PktOut *pktout = ssh2_chanreq_init( - s->mainchan, "window-change", NULL, NULL); - put_uint32(pktout, s->term_width); - put_uint32(pktout, s->term_height); - 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 ssh2_stdout_unthrottle(ConnectionLayer *cl, int bufsize) @@ -2607,15 +2074,19 @@ static void ssh2_stdout_unthrottle(ConnectionLayer *cl, int bufsize) container_of(cl, struct ssh2_connection_state, cl); if (s->mainchan) - ssh2channel_unthrottle(&s->mainchan->sc, bufsize); + sshfwd_unthrottle(s->mainchan_sc, bufsize); } static int ssh2_stdin_backlog(ConnectionLayer *cl) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); + struct ssh2_channel *c; - return s->mainchan ? bufchain_size(&s->mainchan->outbuffer) : 0; + if (!s->mainchan) + return 0; + c = container_of(s->mainchan_sc, struct ssh2_channel, sc); + return s->mainchan ? bufchain_size(&c->outbuffer) : 0; } static void ssh2_throttle_all_channels(ConnectionLayer *cl, int throttled) @@ -2636,15 +2107,46 @@ static int ssh2_ldisc_option(ConnectionLayer *cl, int option) struct ssh2_connection_state *s = container_of(cl, struct ssh2_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 ssh2_set_ldisc_option(ConnectionLayer *cl, int option, int value) +{ + struct ssh2_connection_state *s = + container_of(cl, struct ssh2_connection_state, cl); + + s->ldisc_opts[option] = value; +} + +static void ssh2_enable_x_fwd(ConnectionLayer *cl) +{ + struct ssh2_connection_state *s = + container_of(cl, struct ssh2_connection_state, cl); + + s->X11_fwd_enabled = TRUE; +} + +static void ssh2_enable_agent_fwd(ConnectionLayer *cl) +{ + struct ssh2_connection_state *s = + container_of(cl, struct ssh2_connection_state, cl); + + s->agent_fwd_enabled = TRUE; +} + +static void ssh2_set_wants_user_input(ConnectionLayer *cl, int wanted) +{ + struct ssh2_connection_state *s = + container_of(cl, struct ssh2_connection_state, cl); + + s->want_user_input = wanted; } static int ssh2_connection_want_user_input(PacketProtocolLayer *ppl) { struct ssh2_connection_state *s = container_of(ppl, struct ssh2_connection_state, ppl); - return s->mainchan_ready && s->want_user_input; + return s->want_user_input; } static void ssh2_connection_got_user_input(PacketProtocolLayer *ppl) @@ -2659,7 +2161,7 @@ static void ssh2_connection_got_user_input(PacketProtocolLayer *ppl) void *data; int len; bufchain_prefix(s->ppl.user_input, &data, &len); - sshfwd_write(&s->mainchan->sc, data, len); + sshfwd_write(s->mainchan_sc, data, len); bufchain_consume(s->ppl.user_input, len); } } diff --git a/sshchan.h b/sshchan.h index e29e00d0..7f1be259 100644 --- a/sshchan.h +++ b/sshchan.h @@ -191,4 +191,19 @@ struct SshChannel { #define sshfwd_hint_channel_is_simple(c) \ ((c)->vt->hint_channel_is_simple(c)) +/* ---------------------------------------------------------------------- + * The 'main' or primary channel of the SSH connection is special, + * because it's the one that's connected directly to parts of the + * frontend such as the terminal and the specials menu. So it exposes + * a richer API. + */ + +mainchan *mainchan_new( + PacketProtocolLayer *ppl, ConnectionLayer *cl, Conf *conf, + int term_width, int term_height, int is_simple, SshChannel **sc_out); +void mainchan_get_specials( + mainchan *mc, add_special_fn_t add_special, void *ctx); +void mainchan_special_cmd(mainchan *mc, SessionSpecialCode code, int arg); +void mainchan_terminal_size(mainchan *mc, int width, int height); + #endif /* PUTTY_SSHCHAN_H */ diff --git a/sshppl.h b/sshppl.h index 2536c0ec..e1d4c7c3 100644 --- a/sshppl.h +++ b/sshppl.h @@ -8,8 +8,6 @@ #define PUTTY_SSHPPL_H typedef void (*packet_handler_fn_t)(PacketProtocolLayer *ppl, PktIn *pktin); -typedef void (*add_special_fn_t)( - void *ctx, const char *text, SessionSpecialCode code, int arg); struct PacketProtocolLayerVtable { void (*free)(PacketProtocolLayer *);