+
+ rekeying = (xxx_kex != NULL && !xxx_kex->done);
+
+ if (rekeying) {
+ debug("rekeying in progress");
+ } else {
+ /*
+ * Make packets of buffered stdin data, and buffer
+ * them for sending to the server.
+ */
+ if (!compat20)
+ client_make_packets_from_stdin_data();
+
+ /*
+ * Make packets from buffered channel data, and
+ * enqueue them for sending to the server.
+ */
+ if (packet_not_very_much_data_to_write())
+ channel_output_poll();
+
+ /*
+ * Check if the window size has changed, and buffer a
+ * message about it to the server if so.
+ */
+ client_check_window_change();
+
+ if (quit_pending)
+ break;
+ }
+ /*
+ * Wait until we have something to do (something becomes
+ * available on one of the descriptors).
+ */
+ max_fd2 = max_fd;
+ client_wait_until_can_do_something(&readset, &writeset,
+ &max_fd2, &nalloc, rekeying);
+
+ if (quit_pending)
+ break;
+
+ /* Do channel operations unless rekeying in progress. */
+ if (!rekeying) {
+ channel_after_select(readset, writeset);
+ if (need_rekeying || packet_need_rekeying()) {
+ debug("need rekeying");
+ xxx_kex->done = 0;
+ kex_send_kexinit(xxx_kex);
+ need_rekeying = 0;
+ }
+ }
+
+ /* Buffer input from the connection. */
+ client_process_net_input(readset);
+
+ if (quit_pending)
+ break;
+
+ if (!compat20) {
+ /* Buffer data from stdin */
+ client_process_input(readset);
+ /*
+ * Process output to stdout and stderr. Output to
+ * the connection is processed elsewhere (above).
+ */
+ client_process_output(writeset);
+ }
+
+ /* Send as much buffered packet data as possible to the sender. */
+ if (FD_ISSET(connection_out, writeset))
+ packet_write_poll();
+ }
+ if (readset)
+ xfree(readset);
+ if (writeset)
+ xfree(writeset);
+
+ /* Terminate the session. */
+
+ /* Stop watching for window change. */
+ if (have_pty)
+ signal(SIGWINCH, SIG_DFL);
+
+ channel_free_all();
+
+ if (have_pty)
+ leave_raw_mode();
+
+ /* restore blocking io */
+ if (!isatty(fileno(stdin)))
+ unset_nonblock(fileno(stdin));
+ if (!isatty(fileno(stdout)))
+ unset_nonblock(fileno(stdout));
+ if (!isatty(fileno(stderr)))
+ unset_nonblock(fileno(stderr));
+
+ /*
+ * If there was no shell or command requested, there will be no remote
+ * exit status to be returned. In that case, clear error code if the
+ * connection was deliberately terminated at this end.
+ */
+ if (no_shell_flag && received_signal == SIGTERM) {
+ received_signal = 0;
+ exit_status = 0;
+ }
+
+ if (received_signal)
+ fatal("Killed by signal %d.", (int) received_signal);
+
+ /*
+ * In interactive mode (with pseudo tty) display a message indicating
+ * that the connection has been closed.
+ */
+ if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) {
+ snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host);
+ buffer_append(&stderr_buffer, buf, strlen(buf));
+ }
+
+ /* Output any buffered data for stdout. */
+ while (buffer_len(&stdout_buffer) > 0) {
+ len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
+ buffer_len(&stdout_buffer));
+ if (len <= 0) {
+ error("Write failed flushing stdout buffer.");
+ break;
+ }
+ buffer_consume(&stdout_buffer, len);
+ stdout_bytes += len;
+ }
+
+ /* Output any buffered data for stderr. */
+ while (buffer_len(&stderr_buffer) > 0) {
+ len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
+ buffer_len(&stderr_buffer));
+ if (len <= 0) {
+ error("Write failed flushing stderr buffer.");
+ break;
+ }
+ buffer_consume(&stderr_buffer, len);
+ stderr_bytes += len;
+ }
+
+ /* Clear and free any buffers. */
+ memset(buf, 0, sizeof(buf));
+ buffer_free(&stdin_buffer);
+ buffer_free(&stdout_buffer);
+ buffer_free(&stderr_buffer);
+
+ /* 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);
+ 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);
+
+ /* Return the exit status of the program. */
+ debug("Exit status %d", exit_status);
+ return exit_status;
+}
+
+/*********/
+
+static void
+client_input_stdout_data(int type, u_int32_t seq, void *ctxt)
+{
+ u_int data_len;
+ char *data = packet_get_string(&data_len);
+ packet_check_eom();
+ buffer_append(&stdout_buffer, data, data_len);
+ memset(data, 0, data_len);
+ xfree(data);
+}
+static void
+client_input_stderr_data(int type, u_int32_t seq, void *ctxt)
+{
+ u_int data_len;
+ char *data = packet_get_string(&data_len);
+ packet_check_eom();
+ buffer_append(&stderr_buffer, data, data_len);
+ memset(data, 0, data_len);
+ xfree(data);
+}
+static void
+client_input_exit_status(int type, u_int32_t seq, void *ctxt)
+{
+ exit_status = packet_get_int();
+ packet_check_eom();
+ /* Acknowledge the exit. */
+ packet_start(SSH_CMSG_EXIT_CONFIRMATION);
+ packet_send();
+ /*
+ * Must wait for packet to be sent since we are
+ * exiting the loop.
+ */
+ packet_write_wait();
+ /* Flag that we want to exit. */
+ quit_pending = 1;
+}
+static void
+client_input_agent_open(int type, u_int32_t seq, void *ctxt)
+{
+ Channel *c = NULL;
+ int remote_id, sock;
+
+ /* Read the remote channel number from the message. */
+ remote_id = packet_get_int();
+ packet_check_eom();
+
+ /*
+ * Get a connection to the local authentication agent (this may again
+ * get forwarded).
+ */
+ sock = ssh_get_authentication_socket();
+
+ /*
+ * If we could not connect the agent, send an error message back to
+ * the server. This should never happen unless the agent dies,
+ * because authentication forwarding is only enabled if we have an
+ * agent.
+ */
+ if (sock >= 0) {
+ c = channel_new("", SSH_CHANNEL_OPEN, sock, sock,
+ -1, 0, 0, 0, "authentication agent connection", 1);
+ c->remote_id = remote_id;
+ c->force_drain = 1;
+ }
+ if (c == NULL) {
+ packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
+ packet_put_int(remote_id);
+ } else {
+ /* Send a confirmation to the remote host. */
+ debug("Forwarding authentication connection.");
+ packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
+ packet_put_int(remote_id);
+ packet_put_int(c->self);
+ }
+ packet_send();
+}
+
+static Channel *
+client_request_forwarded_tcpip(const char *request_type, int rchan)
+{
+ Channel *c = NULL;
+ char *listen_address, *originator_address;
+ int listen_port, originator_port;
+ int sock;
+
+ /* Get rest of the packet */
+ listen_address = packet_get_string(NULL);
+ listen_port = packet_get_int();
+ originator_address = packet_get_string(NULL);
+ originator_port = packet_get_int();
+ packet_check_eom();
+
+ debug("client_request_forwarded_tcpip: listen %s port %d, originator %s port %d",
+ listen_address, listen_port, originator_address, originator_port);
+
+ sock = channel_connect_by_listen_address(listen_port);
+ if (sock < 0) {
+ xfree(originator_address);
+ xfree(listen_address);
+ return NULL;
+ }
+ c = channel_new("forwarded-tcpip",
+ SSH_CHANNEL_CONNECTING, sock, sock, -1,
+ CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0,
+ originator_address, 1);
+ xfree(originator_address);
+ xfree(listen_address);
+ return c;
+}
+
+static Channel *
+client_request_x11(const char *request_type, int rchan)
+{
+ Channel *c = NULL;
+ char *originator;
+ int originator_port;
+ int sock;
+
+ if (!options.forward_x11) {
+ error("Warning: ssh server tried X11 forwarding.");
+ error("Warning: this is probably a break in attempt by a malicious server.");
+ return NULL;
+ }
+ originator = packet_get_string(NULL);
+ if (datafellows & SSH_BUG_X11FWD) {
+ debug2("buggy server: x11 request w/o originator_port");
+ originator_port = 0;
+ } else {
+ originator_port = packet_get_int();
+ }
+ packet_check_eom();
+ /* XXX check permission */
+ debug("client_request_x11: request from %s %d", originator,
+ originator_port);
+ xfree(originator);
+ sock = x11_connect_display();
+ if (sock < 0)
+ return NULL;
+ c = channel_new("x11",
+ SSH_CHANNEL_X11_OPEN, sock, sock, -1,
+ CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1);
+ c->force_drain = 1;
+ return c;
+}
+
+static Channel *
+client_request_agent(const char *request_type, int rchan)
+{
+ Channel *c = NULL;
+ int sock;
+
+ if (!options.forward_agent) {
+ error("Warning: ssh server tried agent forwarding.");
+ error("Warning: this is probably a break in attempt by a malicious server.");
+ return NULL;
+ }
+ sock = ssh_get_authentication_socket();
+ if (sock < 0)
+ return NULL;
+ c = channel_new("authentication agent connection",
+ SSH_CHANNEL_OPEN, sock, sock, -1,
+ CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0,
+ "authentication agent connection", 1);
+ c->force_drain = 1;
+ return c;
+}
+
+/* XXXX move to generic input handler */
+static void
+client_input_channel_open(int type, u_int32_t seq, void *ctxt)
+{
+ Channel *c = NULL;
+ char *ctype;
+ int rchan;
+ u_int rmaxpack, rwindow, len;
+
+ ctype = packet_get_string(&len);
+ rchan = packet_get_int();
+ rwindow = packet_get_int();
+ rmaxpack = packet_get_int();
+
+ debug("client_input_channel_open: ctype %s rchan %d win %d max %d",
+ ctype, rchan, rwindow, rmaxpack);
+
+ if (strcmp(ctype, "forwarded-tcpip") == 0) {
+ c = client_request_forwarded_tcpip(ctype, rchan);
+ } else if (strcmp(ctype, "x11") == 0) {
+ c = client_request_x11(ctype, rchan);
+ } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) {
+ c = client_request_agent(ctype, rchan);
+ }
+/* XXX duplicate : */
+ if (c != NULL) {
+ debug("confirm %s", ctype);
+ c->remote_id = rchan;
+ c->remote_window = rwindow;
+ c->remote_maxpacket = rmaxpack;
+ if (c->type != SSH_CHANNEL_CONNECTING) {
+ packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
+ packet_put_int(c->remote_id);
+ packet_put_int(c->self);
+ packet_put_int(c->local_window);
+ packet_put_int(c->local_maxpacket);
+ packet_send();
+ }
+ } else {
+ debug("failure %s", ctype);
+ packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
+ packet_put_int(rchan);
+ packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED);
+ if (!(datafellows & SSH_BUG_OPENFAILURE)) {
+ packet_put_cstring("open failed");
+ packet_put_cstring("");
+ }
+ packet_send();
+ }
+ xfree(ctype);
+}
+static void
+client_input_channel_req(int type, u_int32_t seq, void *ctxt)
+{
+ Channel *c = NULL;
+ int id, reply, success = 0;
+ char *rtype;
+
+ id = packet_get_int();
+ rtype = packet_get_string(NULL);
+ reply = packet_get_char();
+
+ debug("client_input_channel_req: channel %d rtype %s reply %d",
+ id, rtype, reply);
+
+ if (session_ident == -1) {
+ error("client_input_channel_req: no channel %d", session_ident);
+ } else if (id != session_ident) {
+ error("client_input_channel_req: channel %d: wrong channel: %d",
+ session_ident, id);
+ }
+ c = channel_lookup(id);
+ if (c == NULL) {
+ error("client_input_channel_req: channel %d: unknown channel", id);
+ } else if (strcmp(rtype, "exit-status") == 0) {
+ success = 1;
+ exit_status = packet_get_int();
+ packet_check_eom();
+ }
+ if (reply) {
+ packet_start(success ?
+ SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
+ packet_put_int(c->remote_id);
+ packet_send();
+ }
+ xfree(rtype);
+}
+static void
+client_input_global_request(int type, u_int32_t seq, void *ctxt)
+{
+ char *rtype;
+ int want_reply;
+ int success = 0;
+
+ rtype = packet_get_string(NULL);
+ want_reply = packet_get_char();
+ debug("client_input_global_request: rtype %s want_reply %d",
+ rtype, want_reply);
+ if (want_reply) {
+ packet_start(success ?
+ SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE);
+ packet_send();
+ packet_write_wait();
+ }
+ xfree(rtype);
+}
+
+static void
+client_init_dispatch_20(void)
+{
+ dispatch_init(&dispatch_protocol_error);
+
+ dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose);
+ dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data);
+ dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof);
+ dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data);
+ dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open);
+ dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
+ dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
+ dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req);
+ dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust);
+ dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request);
+
+ /* rekeying */
+ dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
+
+ /* global request reply messages */
+ dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply);
+ dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply);
+}
+static void
+client_init_dispatch_13(void)
+{
+ dispatch_init(NULL);
+ dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close);
+ dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation);
+ dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data);
+ dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
+ dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
+ dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open);
+ dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status);
+ dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data);
+ dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data);
+
+ dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ?
+ &client_input_agent_open : &deny_input_open);
+ dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ?
+ &x11_input_open : &deny_input_open);
+}
+static void
+client_init_dispatch_15(void)
+{
+ client_init_dispatch_13();
+ dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof);
+ dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose);
+}
+static void
+client_init_dispatch(void)
+{
+ if (compat20)
+ client_init_dispatch_20();
+ else if (compat13)
+ client_init_dispatch_13();