X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/e74caf1e467a3d98b0219d46bd14614ea54efa1e..279c74eb3359d6afd5a7b7a77d6b386d7fb55eb6:/clientloop.c diff --git a/clientloop.c b/clientloop.c index d2407ed7..05f4720a 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.198 2008/06/12 15:19:17 djm Exp $ */ +/* $OpenBSD: clientloop.c,v 1.217 2010/01/26 01:28:35 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -107,9 +107,9 @@ #include "atomicio.h" #include "sshpty.h" #include "misc.h" -#include "monitor_fdpass.h" #include "match.h" #include "msg.h" +#include "roaming.h" /* import options */ extern Options options; @@ -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 @@ -130,6 +130,9 @@ extern int muxserver_sock; */ extern char *host; +/* Force TTY allocation */ +extern int force_tty_flag; + /* * Flag to indicate that we have received a window change signal which has * not yet been processed. This will cause a message indicating the new @@ -143,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. */ @@ -152,7 +155,6 @@ static int stdin_eof; /* EOF has been encountered on stderr. */ static Buffer stdin_buffer; /* Buffer for stdin data. */ static Buffer stdout_buffer; /* Buffer for stdout data. */ static Buffer stderr_buffer; /* Buffer for stderr data. */ -static u_long stdin_bytes, stdout_bytes, stderr_bytes; static u_int buffer_high;/* Soft max buffer size. */ static int connection_in; /* Connection to server (input). */ static int connection_out; /* Connection to server (output). */ @@ -162,6 +164,8 @@ static int session_closed = 0; /* In SSH2: login session closed. */ static void client_init_dispatch(void); int session_ident = -1; +int session_resumed = 0; + /* Track escape per proto2 channel */ struct escape_filter_ctx { int escape_pending; @@ -437,7 +441,6 @@ client_make_packets_from_stdin_data(void) packet_put_string(buffer_ptr(&stdin_buffer), len); packet_send(); buffer_consume(&stdin_buffer, len); - stdin_bytes += len; /* If we have a pending EOF, send it now. */ if (stdin_eof && buffer_len(&stdin_buffer) == 0) { packet_start(SSH_CMSG_EOF); @@ -494,13 +497,13 @@ client_global_request_reply(int type, u_int32_t seq, void *ctxt) xfree(gc); } - keep_alive_timeouts = 0; + packet_set_alive_timeouts(0); } static void server_alive_check(void) { - if (++keep_alive_timeouts > options.server_alive_count_max) { + if (packet_inc_alive_timeouts() > options.server_alive_count_max) { logit("Timeout, server not responding."); cleanup_exit(255); } @@ -561,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 @@ -610,7 +610,7 @@ client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) atomicio(vwrite, fileno(stderr), buffer_ptr(berr), buffer_len(berr)); - leave_raw_mode(); + leave_raw_mode(force_tty_flag); /* * Free (and clear) the buffer to reduce the amount of data that gets @@ -631,14 +631,14 @@ client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) buffer_init(bout); buffer_init(berr); - enter_raw_mode(); + enter_raw_mode(force_tty_flag); } static void client_process_net_input(fd_set *readset) { - int len; - char buf[8192]; + int len, cont = 0; + char buf[SSH_IOBUFSZ]; /* * Read input from the server, and add any such data to the buffer of @@ -646,8 +646,8 @@ client_process_net_input(fd_set *readset) */ if (FD_ISSET(connection_in, readset)) { /* Read as much as possible. */ - len = read(connection_in, buf, sizeof(buf)); - if (len == 0) { + len = roaming_read(connection_in, buf, sizeof(buf), &cont); + if (len == 0 && cont == 0) { /* * Received EOF. The remote host has closed the * connection. @@ -663,7 +663,8 @@ client_process_net_input(fd_set *readset) * There is a kernel bug on Solaris that causes select to * sometimes wake up even though there is no data available. */ - if (len < 0 && (errno == EAGAIN || errno == EINTR)) + if (len < 0 && + (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) len = 0; if (len < 0) { @@ -691,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", @@ -742,14 +743,14 @@ client_expect_confirm(int id, const char *request, int do_close) void client_register_global_confirm(global_confirm_cb *cb, void *ctx) { - struct global_confirm *gc, *first_gc; + struct global_confirm *gc, *last_gc; /* Coalesce identical callbacks */ - first_gc = TAILQ_FIRST(&global_confirms); - if (first_gc && first_gc->cb == cb && first_gc->ctx == ctx) { - if (++first_gc->ref_count >= INT_MAX) - fatal("%s: first_gc->ref_count = %d", - __func__, first_gc->ref_count); + last_gc = TAILQ_LAST(&global_confirms, global_confirms); + if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) { + if (++last_gc->ref_count >= INT_MAX) + fatal("%s: last_gc->ref_count = %d", + __func__, last_gc->ref_count); return; } @@ -766,14 +767,14 @@ process_cmdline(void) void (*handler)(int); char *s, *cmd, *cancel_host; int delete = 0; - int local = 0; - u_short cancel_port; + int local = 0, remote = 0, dynamic = 0; + int cancel_port; Forward fwd; bzero(&fwd, sizeof(fwd)); fwd.listen_host = fwd.connect_host = NULL; - leave_raw_mode(); + leave_raw_mode(force_tty_flag); handler = signal(SIGINT, SIG_IGN); cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); if (s == NULL) @@ -791,6 +792,8 @@ process_cmdline(void) "Request local forward"); logit(" -R[bind_address:]port:host:hostport " "Request remote forward"); + logit(" -D[bind_address:]port " + "Request dynamic forward"); logit(" -KR[bind_address:]port " "Cancel remote forward"); if (!options.permit_local_command) @@ -810,17 +813,22 @@ process_cmdline(void) delete = 1; s++; } - if (*s != 'L' && *s != 'R') { + if (*s == 'L') + local = 1; + else if (*s == 'R') + remote = 1; + else if (*s == 'D') + dynamic = 1; + else { logit("Invalid command."); goto out; } - if (*s == 'L') - local = 1; - if (local && delete) { + + if ((local || dynamic) && delete) { logit("Not supported."); goto out; } - if ((!local || delete) && !compat20) { + if (remote && delete && !compat20) { logit("Not supported for SSH protocol version 1."); goto out; } @@ -828,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 */ @@ -838,17 +847,17 @@ process_cmdline(void) cancel_port = a2port(cancel_host); cancel_host = NULL; } - if (cancel_port == 0) { + if (cancel_port <= 0) { logit("Bad forwarding close port"); goto out; } channel_request_rforward_cancel(cancel_host, cancel_port); } else { - if (!parse_forward(&fwd, s)) { + if (!parse_forward(&fwd, s, dynamic, remote)) { logit("Bad forwarding specification."); goto out; } - if (local) { + if (local || dynamic) { if (channel_setup_local_fwd_listener(fwd.listen_host, fwd.listen_port, fwd.connect_host, fwd.connect_port, options.gateway_ports) < 0) { @@ -869,7 +878,7 @@ process_cmdline(void) out: signal(SIGINT, handler); - enter_raw_mode(); + enter_raw_mode(force_tty_flag); if (cmd) xfree(cmd); if (fwd.listen_host != NULL) @@ -925,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; @@ -935,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 " @@ -980,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 @@ -988,7 +997,7 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, * more new connections). */ /* Restore tty modes. */ - leave_raw_mode(); + leave_raw_mode(force_tty_flag); /* Stop listening for new connections. */ channel_stop_listening(); @@ -1031,13 +1040,12 @@ 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\ %c. - terminate session\r\n\ %cB - send a BREAK to the remote system\r\n\ - %cC - open a command line\r\n\ %cR - Request rekey (SSH protocol 2 only)\r\n\ %c# - list forwarded connections\r\n\ %c? - this message\r\n\ @@ -1046,8 +1054,7 @@ Supported escape sequences:\r\n\ escape_char, escape_char, escape_char, escape_char, escape_char, escape_char, - escape_char, escape_char, - escape_char); + escape_char, escape_char); } else { snprintf(string, sizeof string, "%c?\r\n\ @@ -1082,6 +1089,8 @@ Supported escape sequences:\r\n\ continue; case 'C': + if (c && c->ctl_chan != -1) + goto noescape; process_cmdline(); continue; @@ -1123,13 +1132,14 @@ static void client_process_input(fd_set *readset) { int len; - char buf[8192]; + char buf[SSH_IOBUFSZ]; /* Read input from stdin. */ if (FD_ISSET(fileno(stdin), readset)) { /* Read as much as possible. */ len = read(fileno(stdin), buf, sizeof(buf)); - if (len < 0 && (errno == EAGAIN || errno == EINTR)) + if (len < 0 && + (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) return; /* we'll try again later */ if (len <= 0) { /* @@ -1186,7 +1196,8 @@ client_process_output(fd_set *writeset) len = write(fileno(stdout), buffer_ptr(&stdout_buffer), buffer_len(&stdout_buffer)); if (len <= 0) { - if (errno == EINTR || errno == EAGAIN) + if (errno == EINTR || errno == EAGAIN || + errno == EWOULDBLOCK) len = 0; else { /* @@ -1202,7 +1213,6 @@ client_process_output(fd_set *writeset) } /* Consume printed data from the buffer. */ buffer_consume(&stdout_buffer, len); - stdout_bytes += len; } /* Write buffered output to stderr. */ if (FD_ISSET(fileno(stderr), writeset)) { @@ -1210,7 +1220,8 @@ client_process_output(fd_set *writeset) len = write(fileno(stderr), buffer_ptr(&stderr_buffer), buffer_len(&stderr_buffer)); if (len <= 0) { - if (errno == EINTR || errno == EAGAIN) + if (errno == EINTR || errno == EAGAIN || + errno == EWOULDBLOCK) len = 0; else { /* @@ -1223,7 +1234,6 @@ client_process_output(fd_set *writeset) } /* Consume printed characters from the buffer. */ buffer_consume(&stderr_buffer, len); - stderr_bytes += len; } } @@ -1282,7 +1292,7 @@ client_channel_closed(int id, void *arg) { channel_cancel_cleanup(id); session_closed = 1; - leave_raw_mode(); + leave_raw_mode(force_tty_flag); } /* @@ -1298,6 +1308,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) fd_set *readset = NULL, *writeset = NULL; double start_time, total_time; int max_fd = 0, max_fd2 = 0, len, rekeying = 0; + u_int64_t ibytes, obytes; u_int nalloc = 0; char buf[100]; @@ -1314,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 */ @@ -1329,9 +1338,6 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) max_fd = MAX(max_fd, fileno(stdout)); max_fd = MAX(max_fd, fileno(stderr)); } - stdin_bytes = 0; - stdout_bytes = 0; - stderr_bytes = 0; quit_pending = 0; escape_char1 = escape_char_arg; @@ -1357,7 +1363,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) signal(SIGWINCH, window_change_handler); if (have_pty) - enter_raw_mode(); + enter_raw_mode(force_tty_flag); if (compat20) { session_ident = ssh2_chan_id; @@ -1436,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; @@ -1455,6 +1455,14 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) client_process_output(writeset); } + if (session_resumed) { + connection_in = packet_get_connection_in(); + connection_out = packet_get_connection_out(); + max_fd = MAX(max_fd, connection_out); + max_fd = MAX(max_fd, connection_in); + session_resumed = 0; + } + /* * Send as much buffered packet data as possible to the * sender. @@ -1472,10 +1480,18 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) /* Stop watching for window change. */ signal(SIGWINCH, SIG_DFL); + if (compat20) { + packet_start(SSH2_MSG_DISCONNECT); + packet_put_int(SSH2_DISCONNECT_BY_APPLICATION); + packet_put_cstring("disconnected by user"); + packet_send(); + packet_write_wait(); + } + channel_free_all(); if (have_pty) - leave_raw_mode(); + leave_raw_mode(force_tty_flag); /* restore blocking io */ if (!isatty(fileno(stdin))) @@ -1517,7 +1533,6 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) break; } buffer_consume(&stdout_buffer, len); - stdout_bytes += len; } /* Output any buffered data for stderr. */ @@ -1529,7 +1544,6 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) break; } buffer_consume(&stderr_buffer, len); - stderr_bytes += len; } /* Clear and free any buffers. */ @@ -1540,13 +1554,13 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) /* Report bytes transferred, and transfer rates. */ total_time = get_current_time() - start_time; - debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f " - "seconds", stdin_bytes, stdout_bytes, stderr_bytes, total_time); + packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes); + packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes); + verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", + obytes, ibytes, total_time); if (total_time > 0) - debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f", - stdin_bytes / total_time, stdout_bytes / total_time, - stderr_bytes / total_time); - + verbose("Bytes per second: sent %.1f, received %.1f", + obytes / total_time, ibytes / total_time); /* Return the exit status of the program. */ debug("Exit status %d", exit_status); return exit_status; @@ -1636,7 +1650,7 @@ client_request_forwarded_tcpip(const char *request_type, int rchan) { Channel *c = NULL; char *listen_address, *originator_address; - int listen_port, originator_port; + u_short listen_port, originator_port; /* Get rest of the packet */ listen_address = packet_get_string(NULL); @@ -1662,7 +1676,7 @@ client_request_x11(const char *request_type, int rchan) { Channel *c = NULL; char *originator; - int originator_port; + u_short originator_port; int sock; if (!options.forward_x11) { @@ -1726,7 +1740,7 @@ client_request_tun_fwd(int tun_mode, int local_tun, int remote_tun) return 0; if (!compat20) { - error("Tunnel forwarding is not support for protocol 1"); + error("Tunnel forwarding is not supported for protocol 1"); return -1; } @@ -1745,7 +1759,7 @@ client_request_tun_fwd(int tun_mode, int local_tun, int remote_tun) #if defined(SSH_TUN_FILTER) if (options.tun_open == SSH_TUNMODE_POINTOPOINT) channel_register_filter(c->self, sys_tun_infilter, - sys_tun_outfilter); + sys_tun_outfilter, NULL, NULL); #endif packet_start(SSH2_MSG_CHANNEL_OPEN); @@ -1835,22 +1849,23 @@ 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 (id == session_ident) { + if (c->ctl_chan != -1) { + mux_exit_message(c, exitval); + success = 1; + } else if (id == session_ident) { + /* Record exit value of local session */ success = 1; exit_status = exitval; - } else if (c->ctl_fd == -1) { + } else { error("client_input_channel_req: unexpected channel %d", session_ident); - } else { - atomicio(vwrite, c->ctl_fd, &exitval, sizeof(exitval)); - success = 1; } packet_check_eom(); } if (reply) { packet_start(success ? SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); - packet_put_int(id); + packet_put_int(c->remote_id); packet_send(); } xfree(rtype); @@ -2039,7 +2054,7 @@ client_init_dispatch(void) void cleanup_exit(int i) { - leave_raw_mode(); + leave_raw_mode(force_tty_flag); leave_non_blocking(); if (options.control_path != NULL && muxserver_sock != -1) unlink(options.control_path);