| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561 | 
							- /*
 
-  * SSH main session channel handling.
 
-  */
 
- #include <assert.h>
 
- #include <stdio.h>
 
- #include <stdlib.h>
 
- #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 size_t mainchan_send(
 
-     Channel *chan, bool is_stderr, const void *, size_t);
 
- static void mainchan_send_eof(Channel *chan);
 
- static void mainchan_set_input_wanted(Channel *chan, bool wanted);
 
- static char *mainchan_log_close_msg(Channel *chan);
 
- static bool mainchan_rcvd_exit_status(Channel *chan, int status);
 
- static bool mainchan_rcvd_exit_signal(
 
-     Channel *chan, ptrlen signame, bool core_dumped, ptrlen msg);
 
- static bool mainchan_rcvd_exit_signal_numeric(
 
-     Channel *chan, int signum, bool core_dumped, ptrlen msg);
 
- static void mainchan_request_response(Channel *chan, bool 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_default_want_close,
 
-     mainchan_rcvd_exit_status,
 
-     mainchan_rcvd_exit_signal,
 
-     mainchan_rcvd_exit_signal_numeric,
 
-     chan_no_run_shell,
 
-     chan_no_run_command,
 
-     chan_no_run_subsystem,
 
-     chan_no_enable_x11_forwarding,
 
-     chan_no_enable_agent_forwarding,
 
-     chan_no_allocate_pty,
 
-     chan_no_set_env,
 
-     chan_no_send_break,
 
-     chan_no_send_signal,
 
-     chan_no_change_window_size,
 
-     mainchan_request_response,
 
- };
 
- typedef enum MainChanType {
 
-     MAINCHAN_SESSION, MAINCHAN_DIRECT_TCPIP
 
- } MainChanType;
 
- struct mainchan {
 
-     SshChannel *sc;
 
-     Conf *conf;
 
-     PacketProtocolLayer *ppl;
 
-     ConnectionLayer *cl;
 
-     MainChanType type;
 
-     bool is_simple;
 
-     bool req_x11, req_agent, req_pty, req_cmd_primary, req_cmd_fallback;
 
-     int n_req_env, n_env_replies, n_env_fails;
 
-     bool eof_pending, eof_sent, got_pty, ready;
 
-     int term_width, term_height;
 
-     Channel chan;
 
- };
 
- mainchan *mainchan_new(
 
-     PacketProtocolLayer *ppl, ConnectionLayer *cl, Conf *conf,
 
-     int term_width, int term_height, bool is_simple, SshChannel **sc_out)
 
- {
 
-     mainchan *mc;
 
-     if (conf_get_bool(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",
 
-                                    NULL, &mc->chan);
 
-         mc->type = MAINCHAN_DIRECT_TCPIP;
 
-     } else {
 
-         mc->sc = ssh_session_open(cl, &mc->chan);
 
-         mc->type = MAINCHAN_SESSION;
 
-     }
 
-     if (sc_out) *sc_out = mc->sc;
 
-     return mc;
 
- }
 
- static void mainchan_free(Channel *chan)
 
- {
 
-     pinitassert(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;
 
-         bool retry_cmd_now = false;
 
- 	if (conf_get_bool(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_bool(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_bool(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_bool(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, bool success)
 
- {
 
-     pinitassert(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) || conf_get_bool(mc->conf, CONF_force_remote_cmd2)) { // WINSCP
 
-             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)
 
- {
 
-     pinitassert(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(get_log_callback_set(mc->cl->logctx), mainchan_open_failure_abort, ctx);
 
- }
 
- static size_t mainchan_send(Channel *chan, bool is_stderr,
 
-                          const void *data, size_t length)
 
- {
 
-     pinitassert(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)
 
- {
 
-     pinitassert(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); /* stop reading from stdin */
 
-     }
 
- }
 
- static void mainchan_set_input_wanted(Channel *chan, bool wanted)
 
- {
 
-     pinitassert(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 bool mainchan_rcvd_exit_status(Channel *chan, int status)
 
- {
 
-     pinitassert(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,
 
-     bool 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 bool mainchan_rcvd_exit_signal(
 
-     Channel *chan, ptrlen signame, bool core_dumped, ptrlen msg)
 
- {
 
-     pinitassert(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, or 128 if the string didn't match any we recognise.
 
-      */
 
-     exitcode = 128;
 
-     #define SIGNAL_SUB(s) \
 
-         if (ptrlen_eq_string(signame, #s))      \
 
-             exitcode = 128 + SIG ## s;
 
-     #define SIGNAL_MAIN(s, text) SIGNAL_SUB(s)
 
-     #define SIGNALS_LOCAL_ONLY
 
-     #include "sshsignals.h"
 
-     #undef SIGNAL_SUB
 
-     #undef SIGNAL_MAIN
 
-     #undef SIGNALS_LOCAL_ONLY
 
-     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 bool mainchan_rcvd_exit_signal_numeric(
 
-     Channel *chan, int signum, bool core_dumped, ptrlen msg)
 
- {
 
-     pinitassert(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;
 
- }
 
- void mainchan_get_specials(
 
-     mainchan *mc, add_special_fn_t add_special, void *ctx)
 
- {
 
-     /* FIXME: this _does_ depend on whether these services are supported */
 
-     /* WINSCP (causes linker warning and we do not use these)
 
-     add_special(ctx, "Break", SS_BRK, 0);
 
-     #define SIGNAL_MAIN(name, desc) \
 
-     add_special(ctx, "SIG" #name " (" desc ")", SS_SIG ## name, 0);
 
-     #define SIGNAL_SUB(name)
 
-     #include "sshsignals.h"
 
-     #undef SIGNAL_MAIN
 
-     #undef SIGNAL_SUB
 
-     add_special(ctx, "More signals", SS_SUBMENU, 0);
 
-     #define SIGNAL_MAIN(name, desc)
 
-     #define SIGNAL_SUB(name) \
 
-     add_special(ctx, "SIG" #name, SS_SIG ## name, 0);
 
-     #include "sshsignals.h"
 
-     #undef SIGNAL_MAIN
 
-     #undef SIGNAL_SUB
 
-     add_special(ctx, NULL, SS_EXITMENU, 0);
 
-     */
 
- }
 
- static const char *ssh_signal_lookup(SessionSpecialCode code)
 
- {
 
-     #define SIGNAL_SUB(name) \
 
-     if (code == SS_SIG ## name) return #name;
 
-     #define SIGNAL_MAIN(name, desc) SIGNAL_SUB(name)
 
-     #include "sshsignals.h"
 
-     #undef SIGNAL_MAIN
 
-     #undef SIGNAL_SUB
 
-     /* If none of those clauses matched, fail lookup. */
 
-     return NULL;
 
- }
 
- 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);
 
- }
 
 
  |