diff options
Diffstat (limited to 'sesschan.c')
-rw-r--r-- | sesschan.c | 787 |
1 files changed, 0 insertions, 787 deletions
diff --git a/sesschan.c b/sesschan.c deleted file mode 100644 index 0cf85b3b..00000000 --- a/sesschan.c +++ /dev/null @@ -1,787 +0,0 @@ -/* - * Implement the "session" channel type for the SSH server. - */ - -#include <assert.h> -#include <stdio.h> -#include <stdlib.h> -#include <ctype.h> - -#include "putty.h" -#include "ssh.h" -#include "sshchan.h" -#include "sshserver.h" -#include "sftp.h" - -struct agentfwd { - ConnectionLayer *cl; - Socket *socket; - Plug plug; -}; - -typedef struct sesschan { - SshChannel *c; - - LogContext *parent_logctx, *child_logctx; - Conf *conf; - const SftpServerVtable *sftpserver_vt; - - LogPolicy logpolicy; - Seat seat; - - bool want_pty; - struct ssh_ttymodes ttymodes; - int wc, hc, wp, hp; - strbuf *termtype; - - bool ignoring_input; - bool seen_eof, seen_exit; - - Plug xfwd_plug; - int n_x11_sockets; - Socket *x11_sockets[MAX_X11_SOCKETS]; - - agentfwd *agent; - - Backend *backend; - - bufchain subsys_input; - SftpServer *sftpsrv; - ScpServer *scpsrv; - const SshServerConfig *ssc; - - Channel chan; -} sesschan; - -static void sesschan_free(Channel *chan); -static size_t sesschan_send( - Channel *chan, bool is_stderr, const void *, size_t); -static void sesschan_send_eof(Channel *chan); -static char *sesschan_log_close_msg(Channel *chan); -static bool sesschan_want_close(Channel *, bool, bool); -static void sesschan_set_input_wanted(Channel *chan, bool wanted); -static bool sesschan_run_shell(Channel *chan); -static bool sesschan_run_command(Channel *chan, ptrlen command); -static bool sesschan_run_subsystem(Channel *chan, ptrlen subsys); -static bool sesschan_enable_x11_forwarding( - Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata, - unsigned screen_number); -static bool sesschan_enable_agent_forwarding(Channel *chan); -static bool sesschan_allocate_pty( - Channel *chan, ptrlen termtype, unsigned width, unsigned height, - unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes); -static bool sesschan_set_env(Channel *chan, ptrlen var, ptrlen value); -static bool sesschan_send_break(Channel *chan, unsigned length); -static bool sesschan_send_signal(Channel *chan, ptrlen signame); -static bool sesschan_change_window_size( - Channel *chan, unsigned width, unsigned height, - unsigned pixwidth, unsigned pixheight); - -static const ChannelVtable sesschan_channelvt = { - .free = sesschan_free, - .open_confirmation = chan_remotely_opened_confirmation, - .open_failed = chan_remotely_opened_failure, - .send = sesschan_send, - .send_eof = sesschan_send_eof, - .set_input_wanted = sesschan_set_input_wanted, - .log_close_msg = sesschan_log_close_msg, - .want_close = sesschan_want_close, - .rcvd_exit_status = chan_no_exit_status, - .rcvd_exit_signal = chan_no_exit_signal, - .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, - .run_shell = sesschan_run_shell, - .run_command = sesschan_run_command, - .run_subsystem = sesschan_run_subsystem, - .enable_x11_forwarding = sesschan_enable_x11_forwarding, - .enable_agent_forwarding = sesschan_enable_agent_forwarding, - .allocate_pty = sesschan_allocate_pty, - .set_env = sesschan_set_env, - .send_break = sesschan_send_break, - .send_signal = sesschan_send_signal, - .change_window_size = sesschan_change_window_size, - .request_response = chan_no_request_response, -}; - -static size_t sftp_chan_send( - Channel *chan, bool is_stderr, const void *, size_t); -static void sftp_chan_send_eof(Channel *chan); -static char *sftp_log_close_msg(Channel *chan); - -static const ChannelVtable sftp_channelvt = { - .free = sesschan_free, - .open_confirmation = chan_remotely_opened_confirmation, - .open_failed = chan_remotely_opened_failure, - .send = sftp_chan_send, - .send_eof = sftp_chan_send_eof, - .set_input_wanted = sesschan_set_input_wanted, - .log_close_msg = sftp_log_close_msg, - .want_close = chan_default_want_close, - .rcvd_exit_status = chan_no_exit_status, - .rcvd_exit_signal = chan_no_exit_signal, - .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, - .run_shell = chan_no_run_shell, - .run_command = chan_no_run_command, - .run_subsystem = chan_no_run_subsystem, - .enable_x11_forwarding = chan_no_enable_x11_forwarding, - .enable_agent_forwarding = chan_no_enable_agent_forwarding, - .allocate_pty = chan_no_allocate_pty, - .set_env = chan_no_set_env, - .send_break = chan_no_send_break, - .send_signal = chan_no_send_signal, - .change_window_size = chan_no_change_window_size, - .request_response = chan_no_request_response, -}; - -static size_t scp_chan_send( - Channel *chan, bool is_stderr, const void *, size_t); -static void scp_chan_send_eof(Channel *chan); -static void scp_set_input_wanted(Channel *chan, bool wanted); -static char *scp_log_close_msg(Channel *chan); - -static const ChannelVtable scp_channelvt = { - .free = sesschan_free, - .open_confirmation = chan_remotely_opened_confirmation, - .open_failed = chan_remotely_opened_failure, - .send = scp_chan_send, - .send_eof = scp_chan_send_eof, - .set_input_wanted = scp_set_input_wanted, - .log_close_msg = scp_log_close_msg, - .want_close = chan_default_want_close, - .rcvd_exit_status = chan_no_exit_status, - .rcvd_exit_signal = chan_no_exit_signal, - .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, - .run_shell = chan_no_run_shell, - .run_command = chan_no_run_command, - .run_subsystem = chan_no_run_subsystem, - .enable_x11_forwarding = chan_no_enable_x11_forwarding, - .enable_agent_forwarding = chan_no_enable_agent_forwarding, - .allocate_pty = chan_no_allocate_pty, - .set_env = chan_no_set_env, - .send_break = chan_no_send_break, - .send_signal = chan_no_send_signal, - .change_window_size = chan_no_change_window_size, - .request_response = chan_no_request_response, -}; - -static void sesschan_eventlog(LogPolicy *lp, const char *event) {} -static void sesschan_logging_error(LogPolicy *lp, const char *event) {} -static int sesschan_askappend( - LogPolicy *lp, Filename *filename, - void (*callback)(void *ctx, int result), void *ctx) { return 2; } - -static const LogPolicyVtable sesschan_logpolicy_vt = { - .eventlog = sesschan_eventlog, - .askappend = sesschan_askappend, - .logging_error = sesschan_logging_error, - .verbose = null_lp_verbose_no, -}; - -static size_t sesschan_seat_output( - Seat *, bool is_stderr, const void *, size_t); -static bool sesschan_seat_eof(Seat *); -static void sesschan_notify_remote_exit(Seat *seat); -static void sesschan_connection_fatal(Seat *seat, const char *message); -static bool sesschan_get_window_pixel_size(Seat *seat, int *w, int *h); - -static const SeatVtable sesschan_seat_vt = { - .output = sesschan_seat_output, - .eof = sesschan_seat_eof, - .get_userpass_input = nullseat_get_userpass_input, - .notify_remote_exit = sesschan_notify_remote_exit, - .connection_fatal = sesschan_connection_fatal, - .update_specials_menu = nullseat_update_specials_menu, - .get_ttymode = nullseat_get_ttymode, - .set_busy_status = nullseat_set_busy_status, - .verify_ssh_host_key = nullseat_verify_ssh_host_key, - .confirm_weak_crypto_primitive = nullseat_confirm_weak_crypto_primitive, - .confirm_weak_cached_hostkey = nullseat_confirm_weak_cached_hostkey, - .is_utf8 = nullseat_is_never_utf8, - .echoedit_update = nullseat_echoedit_update, - .get_x_display = nullseat_get_x_display, - .get_windowid = nullseat_get_windowid, - .get_window_pixel_size = sesschan_get_window_pixel_size, - .stripctrl_new = nullseat_stripctrl_new, - .set_trust_status = nullseat_set_trust_status, - .verbose = nullseat_verbose_no, - .interactive = nullseat_interactive_no, - .get_cursor_position = nullseat_get_cursor_position, -}; - -Channel *sesschan_new(SshChannel *c, LogContext *logctx, - const SftpServerVtable *sftpserver_vt, - const SshServerConfig *ssc) -{ - sesschan *sess = snew(sesschan); - memset(sess, 0, sizeof(sesschan)); - - sess->c = c; - sess->chan.vt = &sesschan_channelvt; - sess->chan.initial_fixed_window_size = 0; - sess->parent_logctx = logctx; - sess->ssc = ssc; - - /* Start with a completely default Conf */ - sess->conf = conf_new(); - load_open_settings(NULL, sess->conf); - - /* Set close-on-exit = true to suppress uxpty.c's "[pterm: process - * terminated with status x]" message */ - conf_set_int(sess->conf, CONF_close_on_exit, FORCE_ON); - - sess->seat.vt = &sesschan_seat_vt; - sess->logpolicy.vt = &sesschan_logpolicy_vt; - sess->child_logctx = log_init(&sess->logpolicy, sess->conf); - - sess->sftpserver_vt = sftpserver_vt; - - bufchain_init(&sess->subsys_input); - - return &sess->chan; -} - -static void sesschan_free(Channel *chan) -{ - sesschan *sess = container_of(chan, sesschan, chan); - int i; - - delete_callbacks_for_context(sess); - conf_free(sess->conf); - if (sess->backend) - backend_free(sess->backend); - bufchain_clear(&sess->subsys_input); - if (sess->sftpsrv) - sftpsrv_free(sess->sftpsrv); - for (i = 0; i < sess->n_x11_sockets; i++) - sk_close(sess->x11_sockets[i]); - if (sess->agent) - agentfwd_free(sess->agent); - - sfree(sess); -} - -static size_t sesschan_send(Channel *chan, bool is_stderr, - const void *data, size_t length) -{ - sesschan *sess = container_of(chan, sesschan, chan); - - if (!sess->backend || sess->ignoring_input) - return 0; - - return backend_send(sess->backend, data, length); -} - -static void sesschan_send_eof(Channel *chan) -{ - sesschan *sess = container_of(chan, sesschan, chan); - if (sess->backend) - backend_special(sess->backend, SS_EOF, 0); -} - -static char *sesschan_log_close_msg(Channel *chan) -{ - return dupstr("Session channel closed"); -} - -static void sesschan_set_input_wanted(Channel *chan, bool wanted) -{ - /* I don't think we need to do anything here */ -} - -static void sesschan_start_backend(sesschan *sess, const char *cmd) -{ - /* - * List of environment variables that we should not pass through - * from the login session Uppity was run in (which, it being a - * test server, there will usually be one of). These variables - * will be set as part of X or agent forwarding, and shouldn't be - * confusingly set in the absence of that. - * - * (DISPLAY must also be cleared, but uxpty.c will do that anyway - * when our get_x_display method returns NULL.) - */ - static const char *const env_to_unset[] = { - "XAUTHORITY", "SSH_AUTH_SOCK", "SSH_AGENT_PID", - NULL /* terminator */ - }; - - sess->backend = pty_backend_create( - &sess->seat, sess->child_logctx, sess->conf, NULL, cmd, - sess->ttymodes, !sess->want_pty, sess->ssc->session_starting_dir, - env_to_unset); - backend_size(sess->backend, sess->wc, sess->hc); -} - -bool sesschan_run_shell(Channel *chan) -{ - sesschan *sess = container_of(chan, sesschan, chan); - - if (sess->backend) - return false; - - sesschan_start_backend(sess, NULL); - return true; -} - -bool sesschan_run_command(Channel *chan, ptrlen command) -{ - sesschan *sess = container_of(chan, sesschan, chan); - - if (sess->backend) - return false; - - /* FIXME: make this possible to configure off */ - if ((sess->scpsrv = scp_recognise_exec(sess->c, sess->sftpserver_vt, - command)) != NULL) { - sess->chan.vt = &scp_channelvt; - logevent(sess->parent_logctx, "Starting built-in SCP server"); - return true; - } - - char *command_str = mkstr(command); - sesschan_start_backend(sess, command_str); - sfree(command_str); - - return true; -} - -bool sesschan_run_subsystem(Channel *chan, ptrlen subsys) -{ - sesschan *sess = container_of(chan, sesschan, chan); - - if (ptrlen_eq_string(subsys, "sftp") && sess->sftpserver_vt) { - sess->sftpsrv = sftpsrv_new(sess->sftpserver_vt); - sess->chan.vt = &sftp_channelvt; - logevent(sess->parent_logctx, "Starting built-in SFTP subsystem"); - return true; - } - - return false; -} - -static void fwd_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, - const char *error_msg, int error_code) -{ /* don't expect any weirdnesses from a listening socket */ } -static void fwd_closing(Plug *plug, const char *error_msg, int error_code, - bool calling_back) -{ /* not here, either */ } - -static int xfwd_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx) -{ - sesschan *sess = container_of(p, sesschan, xfwd_plug); - Plug *plug; - Channel *chan; - Socket *s; - SocketPeerInfo *pi; - const char *err; - - chan = portfwd_raw_new(sess->c->cl, &plug, false); - s = constructor(ctx, plug); - if ((err = sk_socket_error(s)) != NULL) { - portfwd_raw_free(chan); - return 1; - } - pi = sk_peer_info(s); - portfwd_raw_setup(chan, s, ssh_serverside_x11_open(sess->c->cl, chan, pi)); - sk_free_peer_info(pi); - - return 0; -} - -static const PlugVtable xfwd_plugvt = { - .log = fwd_log, - .closing = fwd_closing, - .accepting = xfwd_accepting, -}; - -bool sesschan_enable_x11_forwarding( - Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata_hex, - unsigned screen_number) -{ - sesschan *sess = container_of(chan, sesschan, chan); - strbuf *authdata_bin; - size_t i; - char screensuffix[32]; - - if (oneshot) - return false; /* not supported */ - - snprintf(screensuffix, sizeof(screensuffix), ".%u", screen_number); - - /* - * Decode the authorisation data from ASCII hex into binary. - */ - if (authdata_hex.len % 2) - return false; /* expected an even number of digits */ - authdata_bin = strbuf_new_nm(); - for (i = 0; i < authdata_hex.len; i += 2) { - const unsigned char *hex = authdata_hex.ptr; - char hexbuf[3]; - - if (!isxdigit(hex[i]) || !isxdigit(hex[i+1])) { - strbuf_free(authdata_bin); - return false; /* not hex */ - } - - hexbuf[0] = hex[i]; - hexbuf[1] = hex[i+1]; - hexbuf[2] = '\0'; - put_byte(authdata_bin, strtoul(hexbuf, NULL, 16)); - } - - sess->xfwd_plug.vt = &xfwd_plugvt; - - sess->n_x11_sockets = platform_make_x11_server( - &sess->xfwd_plug, appname, 10, screensuffix, - authproto, ptrlen_from_strbuf(authdata_bin), - sess->x11_sockets, sess->conf); - - strbuf_free(authdata_bin); - return sess->n_x11_sockets != 0; -} - -static int agentfwd_accepting( - Plug *p, accept_fn_t constructor, accept_ctx_t ctx) -{ - agentfwd *agent = container_of(p, agentfwd, plug); - Plug *plug; - Channel *chan; - Socket *s; - const char *err; - - chan = portfwd_raw_new(agent->cl, &plug, false); - s = constructor(ctx, plug); - if ((err = sk_socket_error(s)) != NULL) { - portfwd_raw_free(chan); - return 1; - } - portfwd_raw_setup(chan, s, ssh_serverside_agent_open(agent->cl, chan)); - - return 0; -} - -static const PlugVtable agentfwd_plugvt = { - .log = fwd_log, - .closing = fwd_closing, - .accepting = agentfwd_accepting, -}; - -agentfwd *agentfwd_new(ConnectionLayer *cl, char **socketname_out) -{ - agentfwd *agent = snew(agentfwd); - agent->cl = cl; - agent->plug.vt = &agentfwd_plugvt; - - char *dir_prefix = dupprintf("/tmp/%s-agentfwd", appname); - char *error = NULL, *socketname = NULL; - agent->socket = platform_make_agent_socket( - &agent->plug, dir_prefix, &error, &socketname); - sfree(dir_prefix); - sfree(error); - - if (!agent->socket) { - sfree(agent); - sfree(socketname); - return NULL; - } - - *socketname_out = socketname; - return agent; -} - -void agentfwd_free(agentfwd *agent) -{ - if (agent->socket) - sk_close(agent->socket); - sfree(agent); -} - -bool sesschan_enable_agent_forwarding(Channel *chan) -{ - sesschan *sess = container_of(chan, sesschan, chan); - char *socketname; - - assert(!sess->agent); - - sess->agent = agentfwd_new(sess->c->cl, &socketname); - - if (!sess->agent) - return false; - - conf_set_str_str(sess->conf, CONF_environmt, "SSH_AUTH_SOCK", socketname); - sfree(socketname); - return true; -} - -bool sesschan_allocate_pty( - Channel *chan, ptrlen termtype, unsigned width, unsigned height, - unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes) -{ - sesschan *sess = container_of(chan, sesschan, chan); - char *s; - - if (sess->want_pty) - return false; - - s = mkstr(termtype); - conf_set_str(sess->conf, CONF_termtype, s); - sfree(s); - - sess->want_pty = true; - sess->ttymodes = modes; - sess->wc = width; - sess->hc = height; - sess->wp = pixwidth; - sess->hp = pixheight; - - return true; -} - -bool sesschan_set_env(Channel *chan, ptrlen var, ptrlen value) -{ - sesschan *sess = container_of(chan, sesschan, chan); - - char *svar = mkstr(var), *svalue = mkstr(value); - conf_set_str_str(sess->conf, CONF_environmt, svar, svalue); - sfree(svar); - sfree(svalue); - - return true; -} - -bool sesschan_send_break(Channel *chan, unsigned length) -{ - sesschan *sess = container_of(chan, sesschan, chan); - - if (sess->backend) { - /* We ignore the break length. We could pass it through as the - * 'arg' parameter, and have uxpty.c collect it and pass it on - * to tcsendbreak, but since tcsendbreak in turn assigns - * implementation-defined semantics to _its_ duration - * parameter, this all just sounds too difficult. */ - backend_special(sess->backend, SS_BRK, 0); - return true; - } - return false; -} - -bool sesschan_send_signal(Channel *chan, ptrlen signame) -{ - sesschan *sess = container_of(chan, sesschan, chan); - - /* Start with a code that definitely isn't a signal (or indeed a - * special command at all), to indicate 'nothing matched'. */ - SessionSpecialCode code = SS_EXITMENU; - - #define SIGNAL_SUB(name) \ - if (ptrlen_eq_string(signame, #name)) code = SS_SIG ## name; - #define SIGNAL_MAIN(name, text) SIGNAL_SUB(name) - #include "sshsignals.h" - #undef SIGNAL_MAIN - #undef SIGNAL_SUB - - if (code == SS_EXITMENU) - return false; - - backend_special(sess->backend, code, 0); - return true; -} - -bool sesschan_change_window_size( - Channel *chan, unsigned width, unsigned height, - unsigned pixwidth, unsigned pixheight) -{ - sesschan *sess = container_of(chan, sesschan, chan); - - if (!sess->want_pty) - return false; - - sess->wc = width; - sess->hc = height; - sess->wp = pixwidth; - sess->hp = pixheight; - - if (sess->backend) - backend_size(sess->backend, sess->wc, sess->hc); - - return true; -} - -static size_t sesschan_seat_output( - Seat *seat, bool is_stderr, const void *data, size_t len) -{ - sesschan *sess = container_of(seat, sesschan, seat); - return sshfwd_write_ext(sess->c, is_stderr, data, len); -} - -static void sesschan_check_close_callback(void *vctx) -{ - sesschan *sess = (sesschan *)vctx; - - /* - * Once we've seen incoming EOF from the backend (aka EIO from the - * pty master) and also passed on the process's exit status, we - * should proactively initiate closure of the session channel. - */ - if (sess->seen_eof && sess->seen_exit) - sshfwd_initiate_close(sess->c, NULL); -} - -static bool sesschan_want_close(Channel *chan, bool seen_eof, bool rcvd_eof) -{ - sesschan *sess = container_of(chan, sesschan, chan); - - /* - * Similarly to above, we don't want to initiate channel closure - * until we've sent the process's exit status, _even_ if EOF of - * the actual data stream has happened in both directions. - */ - return (sess->seen_eof && sess->seen_exit); -} - -static bool sesschan_seat_eof(Seat *seat) -{ - sesschan *sess = container_of(seat, sesschan, seat); - - sshfwd_write_eof(sess->c); - sess->seen_eof = true; - - queue_toplevel_callback(sesschan_check_close_callback, sess); - return true; -} - -static void sesschan_notify_remote_exit(Seat *seat) -{ - sesschan *sess = container_of(seat, sesschan, seat); - - if (!sess->backend) - return; - - bool got_signal = false; - if (!sess->ssc->exit_signal_numeric) { - char *sigmsg; - ptrlen signame = pty_backend_exit_signame(sess->backend, &sigmsg); - - if (signame.len) { - if (!sigmsg) - sigmsg = dupstr(""); - - sshfwd_send_exit_signal( - sess->c, signame, false, ptrlen_from_asciz(sigmsg)); - - got_signal = true; - } - - sfree(sigmsg); - } else { - int signum = pty_backend_exit_signum(sess->backend); - - if (signum >= 0) { - sshfwd_send_exit_signal_numeric(sess->c, signum, false, - PTRLEN_LITERAL("")); - got_signal = true; - } - } - - if (!got_signal) - sshfwd_send_exit_status(sess->c, backend_exitcode(sess->backend)); - - sess->seen_exit = true; - queue_toplevel_callback(sesschan_check_close_callback, sess); -} - -static void sesschan_connection_fatal(Seat *seat, const char *message) -{ - sesschan *sess = container_of(seat, sesschan, seat); - - /* Closest translation I can think of */ - sshfwd_send_exit_signal( - sess->c, PTRLEN_LITERAL("HUP"), false, ptrlen_from_asciz(message)); - - sess->ignoring_input = true; -} - -static bool sesschan_get_window_pixel_size(Seat *seat, int *width, int *height) -{ - sesschan *sess = container_of(seat, sesschan, seat); - - *width = sess->wp; - *height = sess->hp; - - return true; -} - -/* ---------------------------------------------------------------------- - * Built-in SFTP subsystem. - */ - -static size_t sftp_chan_send(Channel *chan, bool is_stderr, - const void *data, size_t length) -{ - sesschan *sess = container_of(chan, sesschan, chan); - - bufchain_add(&sess->subsys_input, data, length); - - while (bufchain_size(&sess->subsys_input) >= 4) { - char lenbuf[4]; - unsigned pktlen; - struct sftp_packet *pkt, *reply; - - bufchain_fetch(&sess->subsys_input, lenbuf, 4); - pktlen = GET_32BIT_MSB_FIRST(lenbuf); - - if (bufchain_size(&sess->subsys_input) - 4 < pktlen) - break; /* wait for more data */ - - bufchain_consume(&sess->subsys_input, 4); - pkt = sftp_recv_prepare(pktlen); - bufchain_fetch_consume(&sess->subsys_input, pkt->data, pktlen); - sftp_recv_finish(pkt); - reply = sftp_handle_request(sess->sftpsrv, pkt); - sftp_pkt_free(pkt); - - sftp_send_prepare(reply); - sshfwd_write(sess->c, reply->data, reply->length); - sftp_pkt_free(reply); - } - - return 0; -} - -static void sftp_chan_send_eof(Channel *chan) -{ - sesschan *sess = container_of(chan, sesschan, chan); - sshfwd_write_eof(sess->c); -} - -static char *sftp_log_close_msg(Channel *chan) -{ - return dupstr("Session channel (SFTP) closed"); -} - -/* ---------------------------------------------------------------------- - * Built-in SCP subsystem. - */ - -static size_t scp_chan_send(Channel *chan, bool is_stderr, - const void *data, size_t length) -{ - sesschan *sess = container_of(chan, sesschan, chan); - return scp_send(sess->scpsrv, data, length); -} - -static void scp_chan_send_eof(Channel *chan) -{ - sesschan *sess = container_of(chan, sesschan, chan); - scp_eof(sess->scpsrv); -} - -static char *scp_log_close_msg(Channel *chan) -{ - return dupstr("Session channel (SCP) closed"); -} - -static void scp_set_input_wanted(Channel *chan, bool wanted) -{ - sesschan *sess = container_of(chan, sesschan, chan); - scp_throttle(sess->scpsrv, !wanted); -} |