Commit e1537f95 authored by Damien Miller's avatar Damien Miller

- djm@cvs.openbsd.org 2010/01/26 01:28:35

     [channels.c channels.h clientloop.c clientloop.h mux.c nchan.c ssh.c]
     rewrite ssh(1) multiplexing code to a more sensible protocol.

     The new multiplexing code uses channels for the listener and
     accepted control sockets to make the mux master non-blocking, so
     no stalls when processing messages from a slave.

     avoid use of fatal() in mux master protocol parsing so an errant slave
     process cannot take down a running master.

     implement requesting of port-forwards over multiplexed sessions. Any
     port forwards requested by the slave are added to those the master has
     established.

     add support for stdio forwarding ("ssh -W host:port ...") in mux slaves.

     document master/slave mux protocol so that other tools can use it to
     control a running ssh(1). Note: there are no guarantees that this
     protocol won't be incompatibly changed (though it is versioned).

     feedback Salvador Fandino, dtucker@
     channel changes ok markus@
parent f589fd1e
......@@ -8,6 +8,29 @@
[roaming_client.c]
s/long long unsigned/unsigned long long/, from tim via portable
(Id sync only, change already in portable)
- djm@cvs.openbsd.org 2010/01/26 01:28:35
[channels.c channels.h clientloop.c clientloop.h mux.c nchan.c ssh.c]
rewrite ssh(1) multiplexing code to a more sensible protocol.
The new multiplexing code uses channels for the listener and
accepted control sockets to make the mux master non-blocking, so
no stalls when processing messages from a slave.
avoid use of fatal() in mux master protocol parsing so an errant slave
process cannot take down a running master.
implement requesting of port-forwards over multiplexed sessions. Any
port forwards requested by the slave are added to those the master has
established.
add support for stdio forwarding ("ssh -W host:port ...") in mux slaves.
document master/slave mux protocol so that other tools can use it to
control a running ssh(1). Note: there are no guarantees that this
protocol won't be incompatibly changed (though it is versioned).
feedback Salvador Fandino, dtucker@
channel changes ok markus@
20100122
- (tim) [configure.ac] Due to constraints in Windows Sockets in terms of
......
This diff is collapsed.
/* $OpenBSD: channels.h,v 1.102 2010/01/11 01:39:46 dtucker Exp $ */
/* $OpenBSD: channels.h,v 1.103 2010/01/26 01:28:35 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
......@@ -53,7 +53,9 @@
#define SSH_CHANNEL_CONNECTING 12
#define SSH_CHANNEL_DYNAMIC 13
#define SSH_CHANNEL_ZOMBIE 14 /* Almost dead. */
#define SSH_CHANNEL_MAX_TYPE 15
#define SSH_CHANNEL_MUX_LISTENER 15 /* Listener for mux conn. */
#define SSH_CHANNEL_MUX_CLIENT 16 /* Conn. to mux slave */
#define SSH_CHANNEL_MAX_TYPE 17
struct Channel;
typedef struct Channel Channel;
......@@ -81,6 +83,9 @@ struct channel_connect {
struct addrinfo *ai, *aitop;
};
/* Callbacks for mux channels back into client-specific code */
typedef int mux_callback_fn(struct Channel *);
struct Channel {
int type; /* channel type/state */
int self; /* my own channel identifier */
......@@ -92,7 +97,7 @@ struct Channel {
int wfd; /* write fd */
int efd; /* extended fd */
int sock; /* sock fd */
int ctl_fd; /* control fd (client sharing) */
int ctl_chan; /* control channel (multiplexed connections) */
int isatty; /* rfd is a tty */
int wfd_isatty; /* wfd is a tty */
int client_tty; /* (client) TTY has been requested */
......@@ -142,6 +147,10 @@ struct Channel {
/* non-blocking connect */
struct channel_connect connect_ctx;
/* multiplexing protocol hook, called for each packet received */
mux_callback_fn *mux_rcb;
void *mux_ctx;
};
#define CHAN_EXTENDED_IGNORE 0
......@@ -172,6 +181,7 @@ struct Channel {
#define CHAN_CLOSE_RCVD 0x02
#define CHAN_EOF_SENT 0x04
#define CHAN_EOF_RCVD 0x08
#define CHAN_LOCAL 0x10
#define CHAN_RBUF 16*1024
......@@ -243,7 +253,7 @@ void channel_clear_adm_permitted_opens(void);
void channel_print_adm_permitted_opens(void);
int channel_input_port_forward_request(int, int);
Channel *channel_connect_to(const char *, u_short, char *, char *);
Channel *channel_connect_stdio_fwd(const char*, u_short);
Channel *channel_connect_stdio_fwd(const char*, u_short, int, int);
Channel *channel_connect_by_listen_address(u_short, char *, char *);
int channel_request_remote_forwarding(const char *, u_short,
const char *, u_short);
......
/* $OpenBSD: clientloop.c,v 1.216 2010/01/09 05:04:24 djm Exp $ */
/* $OpenBSD: clientloop.c,v 1.217 2010/01/26 01:28:35 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
......@@ -121,7 +121,7 @@ extern int stdin_null_flag;
extern int no_shell_flag;
/* Control socket */
extern int muxserver_sock;
extern int muxserver_sock; /* XXX use mux_client_cleanup() instead */
/*
* Name of the host we are connecting to. This is the name given on the
......@@ -146,7 +146,7 @@ static volatile sig_atomic_t received_signal = 0;
static int in_non_blocking_mode = 0;
/* Common data for the client loop code. */
static volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */
volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */
static int escape_char1; /* Escape character. (proto1 only) */
static int escape_pending1; /* Last character was an escape (proto1 only) */
static int last_was_cr; /* Last character was a newline. */
......@@ -564,9 +564,6 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
if (packet_have_data_to_write())
FD_SET(connection_out, *writesetp);
if (muxserver_sock != -1)
FD_SET(muxserver_sock, *readsetp);
/*
* Wait for something to happen. This will suspend the process until
* some selected descriptor can be read, written, or has some other
......@@ -695,7 +692,7 @@ client_status_confirm(int type, Channel *c, void *ctx)
/* XXX supress on mux _client_ quietmode */
tochan = options.log_level >= SYSLOG_LEVEL_ERROR &&
c->ctl_fd != -1 && c->extended_usage == CHAN_EXTENDED_WRITE;
c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE;
if (type == SSH2_MSG_CHANNEL_SUCCESS) {
debug2("%s request accepted on channel %d",
......@@ -839,6 +836,7 @@ process_cmdline(void)
while (isspace(*++s))
;
/* XXX update list of forwards in options */
if (delete) {
cancel_port = 0;
cancel_host = hpdelim(&s); /* may be NULL */
......@@ -936,7 +934,7 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr,
escape_char);
buffer_append(berr, string, strlen(string));
if (c && c->ctl_fd != -1) {
if (c && c->ctl_chan != -1) {
chan_read_failed(c);
chan_write_failed(c);
return 0;
......@@ -946,7 +944,7 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr,
case 'Z' - 64:
/* XXX support this for mux clients */
if (c && c->ctl_fd != -1) {
if (c && c->ctl_chan != -1) {
noescape:
snprintf(string, sizeof string,
"%c%c escape not available to "
......@@ -991,7 +989,7 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr,
continue;
case '&':
if (c && c->ctl_fd != -1)
if (c && c->ctl_chan != -1)
goto noescape;
/*
* Detach the program (continue to serve
......@@ -1042,7 +1040,7 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr,
continue;
case '?':
if (c && c->ctl_fd != -1) {
if (c && c->ctl_chan != -1) {
snprintf(string, sizeof string,
"%c?\r\n\
Supported escape sequences:\r\n\
......@@ -1091,7 +1089,7 @@ Supported escape sequences:\r\n\
continue;
case 'C':
if (c && c->ctl_fd != -1)
if (c && c->ctl_chan != -1)
goto noescape;
process_cmdline();
continue;
......@@ -1327,8 +1325,6 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
connection_in = packet_get_connection_in();
connection_out = packet_get_connection_out();
max_fd = MAX(connection_in, connection_out);
if (muxserver_sock != -1)
max_fd = MAX(max_fd, muxserver_sock);
if (!compat20) {
/* enable nonblocking unless tty */
......@@ -1446,12 +1442,6 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
/* Buffer input from the connection. */
client_process_net_input(readset);
/* Accept control connections. */
if (muxserver_sock != -1 &&FD_ISSET(muxserver_sock, readset)) {
if (muxserver_accept_control())
quit_pending = 1;
}
if (quit_pending)
break;
......@@ -1859,9 +1849,8 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt)
chan_rcvd_eow(c);
} else if (strcmp(rtype, "exit-status") == 0) {
exitval = packet_get_int();
if (c->ctl_fd != -1) {
/* Dispatch to mux client */
atomicio(vwrite, c->ctl_fd, &exitval, sizeof(exitval));
if (c->ctl_chan != -1) {
mux_exit_message(c, exitval);
success = 1;
} else if (id == session_ident) {
/* Record exit value of local session */
......
/* $OpenBSD: clientloop.h,v 1.22 2008/06/12 15:19:17 djm Exp $ */
/* $OpenBSD: clientloop.h,v 1.23 2010/01/26 01:28:35 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
......@@ -56,18 +56,14 @@ typedef void global_confirm_cb(int, u_int32_t seq, void *);
void client_register_global_confirm(global_confirm_cb *, void *);
/* Multiplexing protocol version */
#define SSHMUX_VER 2
#define SSHMUX_VER 4
/* Multiplexing control protocol flags */
#define SSHMUX_COMMAND_OPEN 1 /* Open new connection */
#define SSHMUX_COMMAND_ALIVE_CHECK 2 /* Check master is alive */
#define SSHMUX_COMMAND_TERMINATE 3 /* Ask master to exit */
#define SSHMUX_FLAG_TTY (1) /* Request tty on open */
#define SSHMUX_FLAG_SUBSYS (1<<1) /* Subsystem request on open */
#define SSHMUX_FLAG_X11_FWD (1<<2) /* Request X11 forwarding */
#define SSHMUX_FLAG_AGENT_FWD (1<<3) /* Request agent forwarding */
#define SSHMUX_COMMAND_STDIO_FWD 4 /* Open stdio fwd (ssh -W) */
void muxserver_listen(void);
int muxserver_accept_control(void);
void muxclient(const char *);
void mux_exit_message(Channel *, int);
This diff is collapsed.
/* $OpenBSD: nchan.c,v 1.62 2008/11/07 18:50:18 stevesk Exp $ */
/* $OpenBSD: nchan.c,v 1.63 2010/01/26 01:28:35 djm Exp $ */
/*
* Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved.
*
......@@ -161,7 +161,7 @@ chan_ibuf_empty(Channel *c)
switch (c->istate) {
case CHAN_INPUT_WAIT_DRAIN:
if (compat20) {
if (!(c->flags & CHAN_CLOSE_SENT))
if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_LOCAL)))
chan_send_eof2(c);
chan_set_istate(c, CHAN_INPUT_CLOSED);
} else {
......@@ -278,9 +278,12 @@ static void
chan_rcvd_close2(Channel *c)
{
debug2("channel %d: rcvd close", c->self);
if (c->flags & CHAN_CLOSE_RCVD)
error("channel %d: protocol error: close rcvd twice", c->self);
c->flags |= CHAN_CLOSE_RCVD;
if (!(c->flags & CHAN_LOCAL)) {
if (c->flags & CHAN_CLOSE_RCVD)
error("channel %d: protocol error: close rcvd twice",
c->self);
c->flags |= CHAN_CLOSE_RCVD;
}
if (c->type == SSH_CHANNEL_LARVAL) {
/* tear down larval channels immediately */
chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
......@@ -302,11 +305,13 @@ chan_rcvd_close2(Channel *c)
chan_set_istate(c, CHAN_INPUT_CLOSED);
break;
case CHAN_INPUT_WAIT_DRAIN:
chan_send_eof2(c);
if (!(c->flags & CHAN_LOCAL))
chan_send_eof2(c);
chan_set_istate(c, CHAN_INPUT_CLOSED);
break;
}
}
void
chan_rcvd_eow(Channel *c)
{
......@@ -454,6 +459,10 @@ chan_is_dead(Channel *c, int do_send)
c->self, c->efd, buffer_len(&c->extended));
return 0;
}
if (c->flags & CHAN_LOCAL) {
debug2("channel %d: is dead (local)", c->self);
return 1;
}
if (!(c->flags & CHAN_CLOSE_SENT)) {
if (do_send) {
chan_send_close2(c);
......
/* $OpenBSD: ssh.c,v 1.331 2010/01/11 01:39:46 dtucker Exp $ */
/* $OpenBSD: ssh.c,v 1.332 2010/01/26 01:28:35 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
......@@ -319,6 +319,11 @@ main(int ac, char **av)
options.gateway_ports = 1;
break;
case 'O':
if (stdio_forward_host != NULL)
fatal("Cannot specify multiplexing "
"command with -W");
else if (muxclient_command != 0)
fatal("Multiplexing command already specified");
if (strcmp(optarg, "check") == 0)
muxclient_command = SSHMUX_COMMAND_ALIVE_CHECK;
else if (strcmp(optarg, "exit") == 0)
......@@ -395,6 +400,10 @@ main(int ac, char **av)
}
break;
case 'W':
if (stdio_forward_host != NULL)
fatal("stdio forward already specified");
if (muxclient_command != 0)
fatal("Cannot specify stdio forward with -O");
if (parse_forward(&fwd, optarg, 1, 0)) {
stdio_forward_host = fwd.listen_host;
stdio_forward_port = fwd.listen_port;
......@@ -902,11 +911,18 @@ static int
client_setup_stdio_fwd(const char *host_to_connect, u_short port_to_connect)
{
Channel *c;
int in, out;
debug3("client_setup_stdio_fwd %s:%d", host_to_connect,
port_to_connect);
if ((c = channel_connect_stdio_fwd(host_to_connect, port_to_connect))
== NULL)
in = dup(STDIN_FILENO);
out = dup(STDOUT_FILENO);
if (in < 0 || out < 0)
fatal("channel_connect_stdio_fwd: dup() in/out failed");
if ((c = channel_connect_stdio_fwd(host_to_connect, port_to_connect,
in, out)) == NULL)
return 0;
channel_register_cleanup(c->self, client_cleanup_stdio_fwd, 0);
return 1;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment