-/* Signal handler for the window change signal (SIGWINCH). This just
- sets a flag indicating that the window has changed. */
-
-void window_change_handler(int sig)
-{
- received_window_change_signal = 1;
- signal(SIGWINCH, window_change_handler);
-}
-
-/* Signal handler for signals that cause the program to terminate. These
- signals must be trapped to restore terminal modes. */
-
-void signal_handler(int sig)
-{
- if (in_raw_mode)
- leave_raw_mode();
- if (in_non_blocking_mode)
- leave_non_blocking();
- channel_stop_listening();
- packet_close();
- fatal("Killed by signal %d.", sig);
-}
-
-/* Returns current time in seconds from Jan 1, 1970 with the maximum available
- resolution. */
-
-double get_current_time()
-{
- struct timeval tv;
- gettimeofday(&tv, NULL);
- return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
-}
-
-/* This is called when the interactive is entered. This checks if there
- is an EOF coming on stdin. We must check this explicitly, as select()
- does not appear to wake up when redirecting from /dev/null. */
-
-void client_check_initial_eof_on_stdin()
-{
- int len;
- char buf[1];
-
- /* If standard input is to be "redirected from /dev/null", we simply
- mark that we have seen an EOF and send an EOF message to the server.
- Otherwise, we try to read a single character; it appears that for some
- files, such /dev/null, select() never wakes up for read for this
- descriptor, which means that we never get EOF. This way we will get
- the EOF if stdin comes from /dev/null or similar. */
- if (stdin_null_flag)
- {
- /* Fake EOF on stdin. */
- debug("Sending eof.");
- stdin_eof = 1;
- packet_start(SSH_CMSG_EOF);
- packet_send();
- }
- else
- {
- /* Enter non-blocking mode for stdin. */
- enter_non_blocking();
-
- /* Check for immediate EOF on stdin. */
- len = read(fileno(stdin), buf, 1);
- if (len == 0)
- {
- /* EOF. Record that we have seen it and send EOF to server. */
- debug("Sending eof.");
- stdin_eof = 1;
- packet_start(SSH_CMSG_EOF);
- packet_send();
- }
- else
- if (len > 0)
- {
- /* Got data. We must store the data in the buffer, and also
- process it as an escape character if appropriate. */
- if ((unsigned char)buf[0] == escape_char)
- escape_pending = 1;
- else
- {
- buffer_append(&stdin_buffer, buf, 1);
- stdin_bytes += 1;
- }
- }
-
- /* Leave non-blocking mode. */
- leave_non_blocking();
- }
-}
-
-/* Get packets from the connection input buffer, and process them as long
- as there are packets available. */
-
-void client_process_buffered_input_packets()
-{
- int type;
- char *data;
- unsigned int data_len;
- int payload_len;
-
- /* Process any buffered packets from the server. */
- while (!quit_pending && (type = packet_read_poll(&payload_len)) != SSH_MSG_NONE)
- {
- switch (type)
- {
-
- case SSH_SMSG_STDOUT_DATA:
- data = packet_get_string(&data_len);
- packet_integrity_check(payload_len, 4 + data_len, type);
- buffer_append(&stdout_buffer, data, data_len);
- stdout_bytes += data_len;
- memset(data, 0, data_len);
- xfree(data);
- break;
-
- case SSH_SMSG_STDERR_DATA:
- data = packet_get_string(&data_len);
- packet_integrity_check(payload_len, 4 + data_len, type);
- buffer_append(&stderr_buffer, data, data_len);
- stdout_bytes += data_len;
- memset(data, 0, data_len);
- xfree(data);
- break;
-
- case SSH_SMSG_EXITSTATUS:
- packet_integrity_check(payload_len, 4, type);
- exit_status = packet_get_int();
- /* 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;
- break;
-
- case SSH_SMSG_X11_OPEN:
- x11_input_open(payload_len);
- break;
-
- case SSH_MSG_PORT_OPEN:
- channel_input_port_open(payload_len);
- break;
-
- case SSH_SMSG_AGENT_OPEN:
- packet_integrity_check(payload_len, 4, type);
- auth_input_open_request();
- break;
-
- case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
- packet_integrity_check(payload_len, 4 + 4, type);
- channel_input_open_confirmation();
- break;
-
- case SSH_MSG_CHANNEL_OPEN_FAILURE:
- packet_integrity_check(payload_len, 4, type);
- channel_input_open_failure();
- break;
-
- case SSH_MSG_CHANNEL_DATA:
- channel_input_data(payload_len);
- break;
-
- case SSH_MSG_CHANNEL_CLOSE:
- packet_integrity_check(payload_len, 4, type);
- channel_input_close();
- break;
-
- case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION:
- packet_integrity_check(payload_len, 4, type);
- channel_input_close_confirmation();
- break;
-
- default:
- /* Any unknown packets received during the actual session
- cause the session to terminate. This is intended to make
- debugging easier since no confirmations are sent. Any
- compatible protocol extensions must be negotiated during
- the preparatory phase. */
- packet_disconnect("Protocol error during session: type %d",
- type);
- }
- }
-}
-
-/* Make packets from buffered stdin data, and buffer them for sending to
- the connection. */
-
-void client_make_packets_from_stdin_data()
-{
- unsigned int len;
-
- /* Send buffered stdin data to the server. */
- while (buffer_len(&stdin_buffer) > 0 &&
- packet_not_very_much_data_to_write())
- {
- len = buffer_len(&stdin_buffer);
- if (len > 32768)
- len = 32768; /* Keep the packets at reasonable size. */
- packet_start(SSH_CMSG_STDIN_DATA);
- packet_put_string(buffer_ptr(&stdin_buffer), len);
- packet_send();
- buffer_consume(&stdin_buffer, len);
- /* If we have a pending EOF, send it now. */
- if (stdin_eof && buffer_len(&stdin_buffer) == 0)
- {
- packet_start(SSH_CMSG_EOF);
- packet_send();
- }
- }
-}
-
-/* Checks if the client window has changed, and sends a packet about it to
- the server if so. The actual change is detected elsewhere (by a software
- interrupt on Unix); this just checks the flag and sends a message if
- appropriate. */
-
-void client_check_window_change()
-{
- /* Send possible window change message to the server. */
- if (received_window_change_signal)
- {
- struct winsize ws;
-
- /* Clear the window change indicator. */
- received_window_change_signal = 0;
-
- /* Read new window size. */
- if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0)
- {
- /* Successful, send the packet now. */
- packet_start(SSH_CMSG_WINDOW_SIZE);
- packet_put_int(ws.ws_row);
- packet_put_int(ws.ws_col);
- packet_put_int(ws.ws_xpixel);
- packet_put_int(ws.ws_ypixel);
- packet_send();
- }
- }
-}
-
-/* Waits until the client can do something (some data becomes available on
- one of the file descriptors). */
-
-void client_wait_until_can_do_something(fd_set *readset, fd_set *writeset)
-{
- /* Initialize select masks. */
- FD_ZERO(readset);
-
- /* Read from the connection, unless our buffers are full. */
- if (buffer_len(&stdout_buffer) < buffer_high &&
- buffer_len(&stderr_buffer) < buffer_high &&
- channel_not_very_much_buffered_data())
- FD_SET(connection_in, readset);
-
- /* Read from stdin, unless we have seen EOF or have very much buffered
- data to send to the server. */
- if (!stdin_eof && packet_not_very_much_data_to_write())
- FD_SET(fileno(stdin), readset);
-
- FD_ZERO(writeset);
-
- /* Add any selections by the channel mechanism. */
- channel_prepare_select(readset, writeset);
-
- /* Select server connection if have data to write to the server. */
- if (packet_have_data_to_write())
- FD_SET(connection_out, writeset);
-
- /* Select stdout if have data in buffer. */
- if (buffer_len(&stdout_buffer) > 0)
- FD_SET(fileno(stdout), writeset);
-
- /* Select stderr if have data in buffer. */
- if (buffer_len(&stderr_buffer) > 0)
- FD_SET(fileno(stderr), writeset);
-
- /* Update maximum file descriptor number, if appropriate. */
- if (channel_max_fd() > max_fd)
- max_fd = channel_max_fd();
-
- /* Wait for something to happen. This will suspend the process until
- some selected descriptor can be read, written, or has some other
- event pending. Note: if you want to implement SSH_MSG_IGNORE
- messages to fool traffic analysis, this might be the place to do
- it: just have a random timeout for the select, and send a random
- SSH_MSG_IGNORE packet when the timeout expires. */
- if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0)
- {
- char buf[100];
- /* Some systems fail to clear these automatically. */
- FD_ZERO(readset);
- FD_ZERO(writeset);
- if (errno == EINTR)
- return;
- /* Note: we might still have data in the buffers. */
- snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
- buffer_append(&stderr_buffer, buf, strlen(buf));
- stderr_bytes += strlen(buf);
- quit_pending = 1;
- }
-}
-
-void client_suspend_self()
-{
- struct winsize oldws, newws;
-
- /* Flush stdout and stderr buffers. */
- if (buffer_len(&stdout_buffer) > 0)
- write(fileno(stdout),
- buffer_ptr(&stdout_buffer),
- buffer_len(&stdout_buffer));
- if (buffer_len(&stderr_buffer) > 0)
- write(fileno(stderr),
- buffer_ptr(&stderr_buffer),
- buffer_len(&stderr_buffer));
-
- /* Leave raw mode. */
- leave_raw_mode();
-
- /* Free (and clear) the buffer to reduce the
- amount of data that gets written to swap. */
- buffer_free(&stdin_buffer);
- buffer_free(&stdout_buffer);
- buffer_free(&stderr_buffer);
-
- /* Save old window size. */
- ioctl(fileno(stdin), TIOCGWINSZ, &oldws);
-
- /* Send the suspend signal to the program
- itself. */
- kill(getpid(), SIGTSTP);
-
- /* Check if the window size has changed. */
- if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 &&
- (oldws.ws_row != newws.ws_row || oldws.ws_col != newws.ws_col ||
- oldws.ws_xpixel != newws.ws_xpixel ||
- oldws.ws_ypixel != newws.ws_ypixel))
- received_window_change_signal = 1;
-
- /* OK, we have been continued by the user.
- Reinitialize buffers. */
- buffer_init(&stdin_buffer);
- buffer_init(&stdout_buffer);
- buffer_init(&stderr_buffer);
-
- /* Re-enter raw mode. */
- enter_raw_mode();
-}
-
-void client_process_input(fd_set *readset)
-{
- int len, pid;
- char buf[8192], *s;
-
- /* Read input from the server, and add any such data to the buffer of the
- packet subsystem. */
- if (FD_ISSET(connection_in, readset))
- {
- /* Read as much as possible. */
- len = read(connection_in, buf, sizeof(buf));
- if (len == 0)
- {
- /* Received EOF. The remote host has closed the connection. */
- snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n",
- host);
- buffer_append(&stderr_buffer, buf, strlen(buf));
- stderr_bytes += strlen(buf);
- quit_pending = 1;
- return;
- }
-
- /* 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)
- len = 0;
-
- if (len < 0)
- {
- /* An error has encountered. Perhaps there is a network
- problem. */
- snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n",
- host, strerror(errno));
- buffer_append(&stderr_buffer, buf, strlen(buf));
- stderr_bytes += strlen(buf);
- quit_pending = 1;
- return;
- }
- packet_process_incoming(buf, len);
- }
-
- /* 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)
- {
- /* Received EOF or error. They are treated similarly,
- except that an error message is printed if it was
- an error condition. */
- if (len < 0)
- {
- snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno));
- buffer_append(&stderr_buffer, buf, strlen(buf));
- stderr_bytes += strlen(buf);
- }
- /* Mark that we have seen EOF. */
- stdin_eof = 1;
- /* Send an EOF message to the server unless there is data
- in the buffer. If there is data in the buffer, no message
- will be sent now. Code elsewhere will send the EOF
- when the buffer becomes empty if stdin_eof is set. */
- if (buffer_len(&stdin_buffer) == 0)
- {
- packet_start(SSH_CMSG_EOF);
- packet_send();
- }
- }
- else
- if (escape_char == -1)
- {
- /* Normal successful read, and no escape character. Just
- append the data to buffer. */
- buffer_append(&stdin_buffer, buf, len);
- stdin_bytes += len;
- }
- else
- {
- /* Normal, successful read. But we have an escape character
- and have to process the characters one by one. */
- unsigned int i;
- for (i = 0; i < len; i++)
- {
- unsigned char ch;
- /* Get one character at a time. */
- ch = buf[i];
-
- /* Check if we have a pending escape character. */
- if (escape_pending)
- {
- /* We have previously seen an escape character. */
- /* Clear the flag now. */
- escape_pending = 0;
- /* Process the escaped character. */
- switch (ch)
- {
- case '.':
- /* Terminate the connection. */
- snprintf(buf, sizeof buf, "%c.\r\n", escape_char);
+/*
+ * Signal handler for signals that cause the program to terminate. These
+ * signals must be trapped to restore terminal modes.
+ */
+
+static void
+signal_handler(int sig)
+{
+ received_signal = sig;
+ quit_pending = 1;
+}
+
+/*
+ * Returns current time in seconds from Jan 1, 1970 with the maximum
+ * available resolution.
+ */
+
+static double
+get_current_time(void)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
+}
+
+/*
+ * This is called when the interactive is entered. This checks if there is
+ * an EOF coming on stdin. We must check this explicitly, as select() does
+ * not appear to wake up when redirecting from /dev/null.
+ */
+
+static void
+client_check_initial_eof_on_stdin(void)
+{
+ int len;
+ char buf[1];
+
+ /*
+ * If standard input is to be "redirected from /dev/null", we simply
+ * mark that we have seen an EOF and send an EOF message to the
+ * server. Otherwise, we try to read a single character; it appears
+ * that for some files, such /dev/null, select() never wakes up for
+ * read for this descriptor, which means that we never get EOF. This
+ * way we will get the EOF if stdin comes from /dev/null or similar.
+ */
+ if (stdin_null_flag) {
+ /* Fake EOF on stdin. */
+ debug("Sending eof.");
+ stdin_eof = 1;
+ packet_start(SSH_CMSG_EOF);
+ packet_send();
+ } else {
+ enter_non_blocking();
+
+ /* Check for immediate EOF on stdin. */
+ len = read(fileno(stdin), buf, 1);
+ if (len == 0) {
+ /* EOF. Record that we have seen it and send EOF to server. */
+ debug("Sending eof.");
+ stdin_eof = 1;
+ packet_start(SSH_CMSG_EOF);
+ packet_send();
+ } else if (len > 0) {
+ /*
+ * Got data. We must store the data in the buffer,
+ * and also process it as an escape character if
+ * appropriate.
+ */
+ if ((u_char) buf[0] == escape_char)
+ escape_pending = 1;
+ else
+ buffer_append(&stdin_buffer, buf, 1);
+ }
+ leave_non_blocking();
+ }
+}
+
+
+/*
+ * Make packets from buffered stdin data, and buffer them for sending to the
+ * connection.
+ */
+
+static void
+client_make_packets_from_stdin_data(void)
+{
+ u_int len;
+
+ /* Send buffered stdin data to the server. */
+ while (buffer_len(&stdin_buffer) > 0 &&
+ packet_not_very_much_data_to_write()) {
+ len = buffer_len(&stdin_buffer);
+ /* Keep the packets at reasonable size. */
+ if (len > packet_get_maxsize())
+ len = packet_get_maxsize();
+ packet_start(SSH_CMSG_STDIN_DATA);
+ 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);
+ packet_send();
+ }
+ }
+}
+
+/*
+ * Checks if the client window has changed, and sends a packet about it to
+ * the server if so. The actual change is detected elsewhere (by a software
+ * interrupt on Unix); this just checks the flag and sends a message if
+ * appropriate.
+ */
+
+static void
+client_check_window_change(void)
+{
+ struct winsize ws;
+
+ if (! received_window_change_signal)
+ return;
+ /** XXX race */
+ received_window_change_signal = 0;
+
+ if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
+ return;
+
+ debug2("client_check_window_change: changed");
+
+ if (compat20) {
+ channel_request_start(session_ident, "window-change", 0);
+ packet_put_int(ws.ws_col);
+ packet_put_int(ws.ws_row);
+ packet_put_int(ws.ws_xpixel);
+ packet_put_int(ws.ws_ypixel);
+ packet_send();
+ } else {
+ packet_start(SSH_CMSG_WINDOW_SIZE);
+ packet_put_int(ws.ws_row);
+ packet_put_int(ws.ws_col);
+ packet_put_int(ws.ws_xpixel);
+ packet_put_int(ws.ws_ypixel);
+ packet_send();
+ }
+}
+
+/*
+ * Waits until the client can do something (some data becomes available on
+ * one of the file descriptors).
+ */
+
+static void
+client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
+ int *maxfdp, int *nallocp, int rekeying)
+{
+ /* Add any selections by the channel mechanism. */
+ channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying);
+
+ if (!compat20) {
+ /* Read from the connection, unless our buffers are full. */
+ if (buffer_len(&stdout_buffer) < buffer_high &&
+ buffer_len(&stderr_buffer) < buffer_high &&
+ channel_not_very_much_buffered_data())
+ FD_SET(connection_in, *readsetp);
+ /*
+ * Read from stdin, unless we have seen EOF or have very much
+ * buffered data to send to the server.
+ */
+ if (!stdin_eof && packet_not_very_much_data_to_write())
+ FD_SET(fileno(stdin), *readsetp);
+
+ /* Select stdout/stderr if have data in buffer. */
+ if (buffer_len(&stdout_buffer) > 0)
+ FD_SET(fileno(stdout), *writesetp);
+ if (buffer_len(&stderr_buffer) > 0)
+ FD_SET(fileno(stderr), *writesetp);
+ } else {
+ /* channel_prepare_select could have closed the last channel */
+ if (session_closed && !channel_still_open() &&
+ !packet_have_data_to_write()) {
+ /* clear mask since we did not call select() */
+ memset(*readsetp, 0, *nallocp);
+ memset(*writesetp, 0, *nallocp);
+ return;
+ } else {
+ FD_SET(connection_in, *readsetp);
+ }
+ }
+
+ /* Select server connection if have data to write to the server. */
+ if (packet_have_data_to_write())
+ FD_SET(connection_out, *writesetp);
+
+ /*
+ * Wait for something to happen. This will suspend the process until
+ * some selected descriptor can be read, written, or has some other
+ * event pending. Note: if you want to implement SSH_MSG_IGNORE
+ * messages to fool traffic analysis, this might be the place to do
+ * it: just have a random timeout for the select, and send a random
+ * SSH_MSG_IGNORE packet when the timeout expires.
+ */
+
+ if (select((*maxfdp)+1, *readsetp, *writesetp, NULL, NULL) < 0) {
+ char buf[100];
+
+ /*
+ * We have to clear the select masks, because we return.
+ * We have to return, because the mainloop checks for the flags
+ * set by the signal handlers.
+ */
+ memset(*readsetp, 0, *nallocp);
+ memset(*writesetp, 0, *nallocp);
+
+ if (errno == EINTR)
+ return;
+ /* Note: we might still have data in the buffers. */
+ snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
+ buffer_append(&stderr_buffer, buf, strlen(buf));
+ quit_pending = 1;
+ }
+}
+
+static void
+client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr)
+{
+ struct winsize oldws, newws;
+
+ /* Flush stdout and stderr buffers. */
+ if (buffer_len(bout) > 0)
+ atomicio(write, fileno(stdout), buffer_ptr(bout), buffer_len(bout));
+ if (buffer_len(berr) > 0)
+ atomicio(write, fileno(stderr), buffer_ptr(berr), buffer_len(berr));
+
+ leave_raw_mode();
+
+ /*
+ * Free (and clear) the buffer to reduce the amount of data that gets
+ * written to swap.
+ */
+ buffer_free(bin);
+ buffer_free(bout);
+ buffer_free(berr);
+
+ /* Save old window size. */
+ ioctl(fileno(stdin), TIOCGWINSZ, &oldws);
+
+ /* Send the suspend signal to the program itself. */
+ kill(getpid(), SIGTSTP);
+
+ /* Check if the window size has changed. */
+ if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 &&
+ (oldws.ws_row != newws.ws_row ||
+ oldws.ws_col != newws.ws_col ||
+ oldws.ws_xpixel != newws.ws_xpixel ||
+ oldws.ws_ypixel != newws.ws_ypixel))
+ received_window_change_signal = 1;
+
+ /* OK, we have been continued by the user. Reinitialize buffers. */
+ buffer_init(bin);
+ buffer_init(bout);
+ buffer_init(berr);
+
+ enter_raw_mode();
+}
+
+static void
+client_process_net_input(fd_set * readset)
+{
+ int len;
+ char buf[8192];
+
+ /*
+ * Read input from the server, and add any such data to the buffer of
+ * the packet subsystem.
+ */
+ if (FD_ISSET(connection_in, readset)) {
+ /* Read as much as possible. */
+ len = read(connection_in, buf, sizeof(buf));
+ if (len == 0) {
+ /* Received EOF. The remote host has closed the connection. */
+ snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n",
+ host);