clientloop.c revision 57429
157429Smarkm/*
257429Smarkm *
357429Smarkm * clientloop.c
457429Smarkm *
557429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi>
657429Smarkm *
757429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
857429Smarkm *                    All rights reserved
957429Smarkm *
1057429Smarkm *
1157429Smarkm * Created: Sat Sep 23 12:23:57 1995 ylo
1257429Smarkm *
1357429Smarkm * The main loop for the interactive session (client side).
1457429Smarkm *
1557429Smarkm */
1657429Smarkm
1757429Smarkm#include "includes.h"
1857429SmarkmRCSID("$Id: clientloop.c,v 1.14 1999/12/06 20:15:26 deraadt Exp $");
1957429Smarkm
2057429Smarkm#include "xmalloc.h"
2157429Smarkm#include "ssh.h"
2257429Smarkm#include "packet.h"
2357429Smarkm#include "buffer.h"
2457429Smarkm#include "authfd.h"
2557429Smarkm#include "readconf.h"
2657429Smarkm
2757429Smarkm/* Flag indicating that stdin should be redirected from /dev/null. */
2857429Smarkmextern int stdin_null_flag;
2957429Smarkm
3057429Smarkm/*
3157429Smarkm * Name of the host we are connecting to.  This is the name given on the
3257429Smarkm * command line, or the HostName specified for the user-supplied name in a
3357429Smarkm * configuration file.
3457429Smarkm */
3557429Smarkmextern char *host;
3657429Smarkm
3757429Smarkm/*
3857429Smarkm * Flag to indicate that we have received a window change signal which has
3957429Smarkm * not yet been processed.  This will cause a message indicating the new
4057429Smarkm * window size to be sent to the server a little later.  This is volatile
4157429Smarkm * because this is updated in a signal handler.
4257429Smarkm */
4357429Smarkmstatic volatile int received_window_change_signal = 0;
4457429Smarkm
4557429Smarkm/* Terminal modes, as saved by enter_raw_mode. */
4657429Smarkmstatic struct termios saved_tio;
4757429Smarkm
4857429Smarkm/*
4957429Smarkm * Flag indicating whether we are in raw mode.  This is used by
5057429Smarkm * enter_raw_mode and leave_raw_mode.
5157429Smarkm */
5257429Smarkmstatic int in_raw_mode = 0;
5357429Smarkm
5457429Smarkm/* Flag indicating whether the user\'s terminal is in non-blocking mode. */
5557429Smarkmstatic int in_non_blocking_mode = 0;
5657429Smarkm
5757429Smarkm/* Common data for the client loop code. */
5857429Smarkmstatic int escape_pending;	/* Last character was the escape character */
5957429Smarkmstatic int last_was_cr;		/* Last character was a newline. */
6057429Smarkmstatic int exit_status;		/* Used to store the exit status of the command. */
6157429Smarkmstatic int stdin_eof;		/* EOF has been encountered on standard error. */
6257429Smarkmstatic Buffer stdin_buffer;	/* Buffer for stdin data. */
6357429Smarkmstatic Buffer stdout_buffer;	/* Buffer for stdout data. */
6457429Smarkmstatic Buffer stderr_buffer;	/* Buffer for stderr data. */
6557429Smarkmstatic unsigned int buffer_high;/* Soft max buffer size. */
6657429Smarkmstatic int max_fd;		/* Maximum file descriptor number in select(). */
6757429Smarkmstatic int connection_in;	/* Connection to server (input). */
6857429Smarkmstatic int connection_out;	/* Connection to server (output). */
6957429Smarkmstatic unsigned long stdin_bytes, stdout_bytes, stderr_bytes;
7057429Smarkmstatic int quit_pending;	/* Set to non-zero to quit the client loop. */
7157429Smarkmstatic int escape_char;		/* Escape character. */
7257429Smarkm
7357429Smarkm/* Returns the user\'s terminal to normal mode if it had been put in raw mode. */
7457429Smarkm
7557429Smarkmvoid
7657429Smarkmleave_raw_mode()
7757429Smarkm{
7857429Smarkm	if (!in_raw_mode)
7957429Smarkm		return;
8057429Smarkm	in_raw_mode = 0;
8157429Smarkm	if (tcsetattr(fileno(stdin), TCSADRAIN, &saved_tio) < 0)
8257429Smarkm		perror("tcsetattr");
8357429Smarkm
8457429Smarkm	fatal_remove_cleanup((void (*) (void *)) leave_raw_mode, NULL);
8557429Smarkm}
8657429Smarkm
8757429Smarkm/* Puts the user\'s terminal in raw mode. */
8857429Smarkm
8957429Smarkmvoid
9057429Smarkmenter_raw_mode()
9157429Smarkm{
9257429Smarkm	struct termios tio;
9357429Smarkm
9457429Smarkm	if (tcgetattr(fileno(stdin), &tio) < 0)
9557429Smarkm		perror("tcgetattr");
9657429Smarkm	saved_tio = tio;
9757429Smarkm	tio.c_iflag |= IGNPAR;
9857429Smarkm	tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
9957429Smarkm	tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
10057429Smarkm#ifdef IEXTEN
10157429Smarkm	tio.c_lflag &= ~IEXTEN;
10257429Smarkm#endif				/* IEXTEN */
10357429Smarkm	tio.c_oflag &= ~OPOST;
10457429Smarkm	tio.c_cc[VMIN] = 1;
10557429Smarkm	tio.c_cc[VTIME] = 0;
10657429Smarkm	if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0)
10757429Smarkm		perror("tcsetattr");
10857429Smarkm	in_raw_mode = 1;
10957429Smarkm
11057429Smarkm	fatal_add_cleanup((void (*) (void *)) leave_raw_mode, NULL);
11157429Smarkm}
11257429Smarkm
11357429Smarkm/* Restores stdin to blocking mode. */
11457429Smarkm
11557429Smarkmvoid
11657429Smarkmleave_non_blocking()
11757429Smarkm{
11857429Smarkm	if (in_non_blocking_mode) {
11957429Smarkm		(void) fcntl(fileno(stdin), F_SETFL, 0);
12057429Smarkm		in_non_blocking_mode = 0;
12157429Smarkm		fatal_remove_cleanup((void (*) (void *)) leave_non_blocking, NULL);
12257429Smarkm	}
12357429Smarkm}
12457429Smarkm
12557429Smarkm/* Puts stdin terminal in non-blocking mode. */
12657429Smarkm
12757429Smarkmvoid
12857429Smarkmenter_non_blocking()
12957429Smarkm{
13057429Smarkm	in_non_blocking_mode = 1;
13157429Smarkm	(void) fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
13257429Smarkm	fatal_add_cleanup((void (*) (void *)) leave_non_blocking, NULL);
13357429Smarkm}
13457429Smarkm
13557429Smarkm/*
13657429Smarkm * Signal handler for the window change signal (SIGWINCH).  This just sets a
13757429Smarkm * flag indicating that the window has changed.
13857429Smarkm */
13957429Smarkm
14057429Smarkmvoid
14157429Smarkmwindow_change_handler(int sig)
14257429Smarkm{
14357429Smarkm	received_window_change_signal = 1;
14457429Smarkm	signal(SIGWINCH, window_change_handler);
14557429Smarkm}
14657429Smarkm
14757429Smarkm/*
14857429Smarkm * Signal handler for signals that cause the program to terminate.  These
14957429Smarkm * signals must be trapped to restore terminal modes.
15057429Smarkm */
15157429Smarkm
15257429Smarkmvoid
15357429Smarkmsignal_handler(int sig)
15457429Smarkm{
15557429Smarkm	if (in_raw_mode)
15657429Smarkm		leave_raw_mode();
15757429Smarkm	if (in_non_blocking_mode)
15857429Smarkm		leave_non_blocking();
15957429Smarkm	channel_stop_listening();
16057429Smarkm	packet_close();
16157429Smarkm	fatal("Killed by signal %d.", sig);
16257429Smarkm}
16357429Smarkm
16457429Smarkm/*
16557429Smarkm * Returns current time in seconds from Jan 1, 1970 with the maximum
16657429Smarkm * available resolution.
16757429Smarkm */
16857429Smarkm
16957429Smarkmdouble
17057429Smarkmget_current_time()
17157429Smarkm{
17257429Smarkm	struct timeval tv;
17357429Smarkm	gettimeofday(&tv, NULL);
17457429Smarkm	return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
17557429Smarkm}
17657429Smarkm
17757429Smarkm/*
17857429Smarkm * This is called when the interactive is entered.  This checks if there is
17957429Smarkm * an EOF coming on stdin.  We must check this explicitly, as select() does
18057429Smarkm * not appear to wake up when redirecting from /dev/null.
18157429Smarkm */
18257429Smarkm
18357429Smarkmvoid
18457429Smarkmclient_check_initial_eof_on_stdin()
18557429Smarkm{
18657429Smarkm	int len;
18757429Smarkm	char buf[1];
18857429Smarkm
18957429Smarkm	/*
19057429Smarkm	 * If standard input is to be "redirected from /dev/null", we simply
19157429Smarkm	 * mark that we have seen an EOF and send an EOF message to the
19257429Smarkm	 * server. Otherwise, we try to read a single character; it appears
19357429Smarkm	 * that for some files, such /dev/null, select() never wakes up for
19457429Smarkm	 * read for this descriptor, which means that we never get EOF.  This
19557429Smarkm	 * way we will get the EOF if stdin comes from /dev/null or similar.
19657429Smarkm	 */
19757429Smarkm	if (stdin_null_flag) {
19857429Smarkm		/* Fake EOF on stdin. */
19957429Smarkm		debug("Sending eof.");
20057429Smarkm		stdin_eof = 1;
20157429Smarkm		packet_start(SSH_CMSG_EOF);
20257429Smarkm		packet_send();
20357429Smarkm	} else {
20457429Smarkm		enter_non_blocking();
20557429Smarkm
20657429Smarkm		/* Check for immediate EOF on stdin. */
20757429Smarkm		len = read(fileno(stdin), buf, 1);
20857429Smarkm		if (len == 0) {
20957429Smarkm			/* EOF.  Record that we have seen it and send EOF to server. */
21057429Smarkm			debug("Sending eof.");
21157429Smarkm			stdin_eof = 1;
21257429Smarkm			packet_start(SSH_CMSG_EOF);
21357429Smarkm			packet_send();
21457429Smarkm		} else if (len > 0) {
21557429Smarkm			/*
21657429Smarkm			 * Got data.  We must store the data in the buffer,
21757429Smarkm			 * and also process it as an escape character if
21857429Smarkm			 * appropriate.
21957429Smarkm			 */
22057429Smarkm			if ((unsigned char) buf[0] == escape_char)
22157429Smarkm				escape_pending = 1;
22257429Smarkm			else {
22357429Smarkm				buffer_append(&stdin_buffer, buf, 1);
22457429Smarkm				stdin_bytes += 1;
22557429Smarkm			}
22657429Smarkm		}
22757429Smarkm		leave_non_blocking();
22857429Smarkm	}
22957429Smarkm}
23057429Smarkm
23157429Smarkm/*
23257429Smarkm * Get packets from the connection input buffer, and process them as long as
23357429Smarkm * there are packets available.
23457429Smarkm */
23557429Smarkm
23657429Smarkmvoid
23757429Smarkmclient_process_buffered_input_packets()
23857429Smarkm{
23957429Smarkm	int type;
24057429Smarkm	char *data;
24157429Smarkm	unsigned int data_len;
24257429Smarkm	int payload_len;
24357429Smarkm
24457429Smarkm	/* Process any buffered packets from the server. */
24557429Smarkm	while (!quit_pending &&
24657429Smarkm	       (type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) {
24757429Smarkm		switch (type) {
24857429Smarkm
24957429Smarkm		case SSH_SMSG_STDOUT_DATA:
25057429Smarkm			data = packet_get_string(&data_len);
25157429Smarkm			packet_integrity_check(payload_len, 4 + data_len, type);
25257429Smarkm			buffer_append(&stdout_buffer, data, data_len);
25357429Smarkm			stdout_bytes += data_len;
25457429Smarkm			memset(data, 0, data_len);
25557429Smarkm			xfree(data);
25657429Smarkm			break;
25757429Smarkm
25857429Smarkm		case SSH_SMSG_STDERR_DATA:
25957429Smarkm			data = packet_get_string(&data_len);
26057429Smarkm			packet_integrity_check(payload_len, 4 + data_len, type);
26157429Smarkm			buffer_append(&stderr_buffer, data, data_len);
26257429Smarkm			stdout_bytes += data_len;
26357429Smarkm			memset(data, 0, data_len);
26457429Smarkm			xfree(data);
26557429Smarkm			break;
26657429Smarkm
26757429Smarkm		case SSH_SMSG_EXITSTATUS:
26857429Smarkm			packet_integrity_check(payload_len, 4, type);
26957429Smarkm			exit_status = packet_get_int();
27057429Smarkm			/* Acknowledge the exit. */
27157429Smarkm			packet_start(SSH_CMSG_EXIT_CONFIRMATION);
27257429Smarkm			packet_send();
27357429Smarkm			/*
27457429Smarkm			 * Must wait for packet to be sent since we are
27557429Smarkm			 * exiting the loop.
27657429Smarkm			 */
27757429Smarkm			packet_write_wait();
27857429Smarkm			/* Flag that we want to exit. */
27957429Smarkm			quit_pending = 1;
28057429Smarkm			break;
28157429Smarkm
28257429Smarkm		case SSH_SMSG_X11_OPEN:
28357429Smarkm			x11_input_open(payload_len);
28457429Smarkm			break;
28557429Smarkm
28657429Smarkm		case SSH_MSG_PORT_OPEN:
28757429Smarkm			channel_input_port_open(payload_len);
28857429Smarkm			break;
28957429Smarkm
29057429Smarkm		case SSH_SMSG_AGENT_OPEN:
29157429Smarkm			packet_integrity_check(payload_len, 4, type);
29257429Smarkm			auth_input_open_request();
29357429Smarkm			break;
29457429Smarkm
29557429Smarkm		case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
29657429Smarkm			packet_integrity_check(payload_len, 4 + 4, type);
29757429Smarkm			channel_input_open_confirmation();
29857429Smarkm			break;
29957429Smarkm
30057429Smarkm		case SSH_MSG_CHANNEL_OPEN_FAILURE:
30157429Smarkm			packet_integrity_check(payload_len, 4, type);
30257429Smarkm			channel_input_open_failure();
30357429Smarkm			break;
30457429Smarkm
30557429Smarkm		case SSH_MSG_CHANNEL_DATA:
30657429Smarkm			channel_input_data(payload_len);
30757429Smarkm			break;
30857429Smarkm
30957429Smarkm		case SSH_MSG_CHANNEL_CLOSE:
31057429Smarkm			packet_integrity_check(payload_len, 4, type);
31157429Smarkm			channel_input_close();
31257429Smarkm			break;
31357429Smarkm
31457429Smarkm		case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION:
31557429Smarkm			packet_integrity_check(payload_len, 4, type);
31657429Smarkm			channel_input_close_confirmation();
31757429Smarkm			break;
31857429Smarkm
31957429Smarkm		default:
32057429Smarkm			/*
32157429Smarkm			 * Any unknown packets received during the actual
32257429Smarkm			 * session cause the session to terminate.  This is
32357429Smarkm			 * intended to make debugging easier since no
32457429Smarkm			 * confirmations are sent.  Any compatible protocol
32557429Smarkm			 * extensions must be negotiated during the
32657429Smarkm			 * preparatory phase.
32757429Smarkm			 */
32857429Smarkm			packet_disconnect("Protocol error during session: type %d",
32957429Smarkm					  type);
33057429Smarkm		}
33157429Smarkm	}
33257429Smarkm}
33357429Smarkm
33457429Smarkm/*
33557429Smarkm * Make packets from buffered stdin data, and buffer them for sending to the
33657429Smarkm * connection.
33757429Smarkm */
33857429Smarkm
33957429Smarkmvoid
34057429Smarkmclient_make_packets_from_stdin_data()
34157429Smarkm{
34257429Smarkm	unsigned int len;
34357429Smarkm
34457429Smarkm	/* Send buffered stdin data to the server. */
34557429Smarkm	while (buffer_len(&stdin_buffer) > 0 &&
34657429Smarkm	       packet_not_very_much_data_to_write()) {
34757429Smarkm		len = buffer_len(&stdin_buffer);
34857429Smarkm		/* Keep the packets at reasonable size. */
34957429Smarkm		if (len > packet_get_maxsize())
35057429Smarkm			len = packet_get_maxsize();
35157429Smarkm		packet_start(SSH_CMSG_STDIN_DATA);
35257429Smarkm		packet_put_string(buffer_ptr(&stdin_buffer), len);
35357429Smarkm		packet_send();
35457429Smarkm		buffer_consume(&stdin_buffer, len);
35557429Smarkm		/* If we have a pending EOF, send it now. */
35657429Smarkm		if (stdin_eof && buffer_len(&stdin_buffer) == 0) {
35757429Smarkm			packet_start(SSH_CMSG_EOF);
35857429Smarkm			packet_send();
35957429Smarkm		}
36057429Smarkm	}
36157429Smarkm}
36257429Smarkm
36357429Smarkm/*
36457429Smarkm * Checks if the client window has changed, and sends a packet about it to
36557429Smarkm * the server if so.  The actual change is detected elsewhere (by a software
36657429Smarkm * interrupt on Unix); this just checks the flag and sends a message if
36757429Smarkm * appropriate.
36857429Smarkm */
36957429Smarkm
37057429Smarkmvoid
37157429Smarkmclient_check_window_change()
37257429Smarkm{
37357429Smarkm	/* Send possible window change message to the server. */
37457429Smarkm	if (received_window_change_signal) {
37557429Smarkm		struct winsize ws;
37657429Smarkm
37757429Smarkm		/* Clear the window change indicator. */
37857429Smarkm		received_window_change_signal = 0;
37957429Smarkm
38057429Smarkm		/* Read new window size. */
38157429Smarkm		if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) {
38257429Smarkm			/* Successful, send the packet now. */
38357429Smarkm			packet_start(SSH_CMSG_WINDOW_SIZE);
38457429Smarkm			packet_put_int(ws.ws_row);
38557429Smarkm			packet_put_int(ws.ws_col);
38657429Smarkm			packet_put_int(ws.ws_xpixel);
38757429Smarkm			packet_put_int(ws.ws_ypixel);
38857429Smarkm			packet_send();
38957429Smarkm		}
39057429Smarkm	}
39157429Smarkm}
39257429Smarkm
39357429Smarkm/*
39457429Smarkm * Waits until the client can do something (some data becomes available on
39557429Smarkm * one of the file descriptors).
39657429Smarkm */
39757429Smarkm
39857429Smarkmvoid
39957429Smarkmclient_wait_until_can_do_something(fd_set * readset, fd_set * writeset)
40057429Smarkm{
40157429Smarkm	/* Initialize select masks. */
40257429Smarkm	FD_ZERO(readset);
40357429Smarkm
40457429Smarkm	/* Read from the connection, unless our buffers are full. */
40557429Smarkm	if (buffer_len(&stdout_buffer) < buffer_high &&
40657429Smarkm	    buffer_len(&stderr_buffer) < buffer_high &&
40757429Smarkm	    channel_not_very_much_buffered_data())
40857429Smarkm		FD_SET(connection_in, readset);
40957429Smarkm
41057429Smarkm	/*
41157429Smarkm	 * Read from stdin, unless we have seen EOF or have very much
41257429Smarkm	 * buffered data to send to the server.
41357429Smarkm	 */
41457429Smarkm	if (!stdin_eof && packet_not_very_much_data_to_write())
41557429Smarkm		FD_SET(fileno(stdin), readset);
41657429Smarkm
41757429Smarkm	FD_ZERO(writeset);
41857429Smarkm
41957429Smarkm	/* Add any selections by the channel mechanism. */
42057429Smarkm	channel_prepare_select(readset, writeset);
42157429Smarkm
42257429Smarkm	/* Select server connection if have data to write to the server. */
42357429Smarkm	if (packet_have_data_to_write())
42457429Smarkm		FD_SET(connection_out, writeset);
42557429Smarkm
42657429Smarkm	/* Select stdout if have data in buffer. */
42757429Smarkm	if (buffer_len(&stdout_buffer) > 0)
42857429Smarkm		FD_SET(fileno(stdout), writeset);
42957429Smarkm
43057429Smarkm	/* Select stderr if have data in buffer. */
43157429Smarkm	if (buffer_len(&stderr_buffer) > 0)
43257429Smarkm		FD_SET(fileno(stderr), writeset);
43357429Smarkm
43457429Smarkm	/* Update maximum file descriptor number, if appropriate. */
43557429Smarkm	if (channel_max_fd() > max_fd)
43657429Smarkm		max_fd = channel_max_fd();
43757429Smarkm
43857429Smarkm	/*
43957429Smarkm	 * Wait for something to happen.  This will suspend the process until
44057429Smarkm	 * some selected descriptor can be read, written, or has some other
44157429Smarkm	 * event pending. Note: if you want to implement SSH_MSG_IGNORE
44257429Smarkm	 * messages to fool traffic analysis, this might be the place to do
44357429Smarkm	 * it: just have a random timeout for the select, and send a random
44457429Smarkm	 * SSH_MSG_IGNORE packet when the timeout expires.
44557429Smarkm	 */
44657429Smarkm
44757429Smarkm	if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0) {
44857429Smarkm		char buf[100];
44957429Smarkm		/* Some systems fail to clear these automatically. */
45057429Smarkm		FD_ZERO(readset);
45157429Smarkm		FD_ZERO(writeset);
45257429Smarkm		if (errno == EINTR)
45357429Smarkm			return;
45457429Smarkm		/* Note: we might still have data in the buffers. */
45557429Smarkm		snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
45657429Smarkm		buffer_append(&stderr_buffer, buf, strlen(buf));
45757429Smarkm		stderr_bytes += strlen(buf);
45857429Smarkm		quit_pending = 1;
45957429Smarkm	}
46057429Smarkm}
46157429Smarkm
46257429Smarkmvoid
46357429Smarkmclient_suspend_self()
46457429Smarkm{
46557429Smarkm	struct winsize oldws, newws;
46657429Smarkm
46757429Smarkm	/* Flush stdout and stderr buffers. */
46857429Smarkm	if (buffer_len(&stdout_buffer) > 0)
46957429Smarkm		atomicio(write, fileno(stdout), buffer_ptr(&stdout_buffer),
47057429Smarkm		    buffer_len(&stdout_buffer));
47157429Smarkm	if (buffer_len(&stderr_buffer) > 0)
47257429Smarkm		atomicio(write, fileno(stderr), buffer_ptr(&stderr_buffer),
47357429Smarkm		    buffer_len(&stderr_buffer));
47457429Smarkm
47557429Smarkm	leave_raw_mode();
47657429Smarkm
47757429Smarkm	/*
47857429Smarkm	 * Free (and clear) the buffer to reduce the amount of data that gets
47957429Smarkm	 * written to swap.
48057429Smarkm	 */
48157429Smarkm	buffer_free(&stdin_buffer);
48257429Smarkm	buffer_free(&stdout_buffer);
48357429Smarkm	buffer_free(&stderr_buffer);
48457429Smarkm
48557429Smarkm	/* Save old window size. */
48657429Smarkm	ioctl(fileno(stdin), TIOCGWINSZ, &oldws);
48757429Smarkm
48857429Smarkm	/* Send the suspend signal to the program itself. */
48957429Smarkm	kill(getpid(), SIGTSTP);
49057429Smarkm
49157429Smarkm	/* Check if the window size has changed. */
49257429Smarkm	if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 &&
49357429Smarkm	    (oldws.ws_row != newws.ws_row ||
49457429Smarkm	     oldws.ws_col != newws.ws_col ||
49557429Smarkm	     oldws.ws_xpixel != newws.ws_xpixel ||
49657429Smarkm	     oldws.ws_ypixel != newws.ws_ypixel))
49757429Smarkm		received_window_change_signal = 1;
49857429Smarkm
49957429Smarkm	/* OK, we have been continued by the user. Reinitialize buffers. */
50057429Smarkm	buffer_init(&stdin_buffer);
50157429Smarkm	buffer_init(&stdout_buffer);
50257429Smarkm	buffer_init(&stderr_buffer);
50357429Smarkm
50457429Smarkm	enter_raw_mode();
50557429Smarkm}
50657429Smarkm
50757429Smarkmvoid
50857429Smarkmclient_process_input(fd_set * readset)
50957429Smarkm{
51057429Smarkm	int len, pid;
51157429Smarkm	char buf[8192], *s;
51257429Smarkm
51357429Smarkm	/*
51457429Smarkm	 * Read input from the server, and add any such data to the buffer of
51557429Smarkm	 * the packet subsystem.
51657429Smarkm	 */
51757429Smarkm	if (FD_ISSET(connection_in, readset)) {
51857429Smarkm		/* Read as much as possible. */
51957429Smarkm		len = read(connection_in, buf, sizeof(buf));
52057429Smarkm		if (len == 0) {
52157429Smarkm			/* Received EOF.  The remote host has closed the connection. */
52257429Smarkm			snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n",
52357429Smarkm				 host);
52457429Smarkm			buffer_append(&stderr_buffer, buf, strlen(buf));
52557429Smarkm			stderr_bytes += strlen(buf);
52657429Smarkm			quit_pending = 1;
52757429Smarkm			return;
52857429Smarkm		}
52957429Smarkm		/*
53057429Smarkm		 * There is a kernel bug on Solaris that causes select to
53157429Smarkm		 * sometimes wake up even though there is no data available.
53257429Smarkm		 */
53357429Smarkm		if (len < 0 && errno == EAGAIN)
53457429Smarkm			len = 0;
53557429Smarkm
53657429Smarkm		if (len < 0) {
53757429Smarkm			/* An error has encountered.  Perhaps there is a network problem. */
53857429Smarkm			snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n",
53957429Smarkm				 host, strerror(errno));
54057429Smarkm			buffer_append(&stderr_buffer, buf, strlen(buf));
54157429Smarkm			stderr_bytes += strlen(buf);
54257429Smarkm			quit_pending = 1;
54357429Smarkm			return;
54457429Smarkm		}
54557429Smarkm		packet_process_incoming(buf, len);
54657429Smarkm	}
54757429Smarkm	/* Read input from stdin. */
54857429Smarkm	if (FD_ISSET(fileno(stdin), readset)) {
54957429Smarkm		/* Read as much as possible. */
55057429Smarkm		len = read(fileno(stdin), buf, sizeof(buf));
55157429Smarkm		if (len <= 0) {
55257429Smarkm			/*
55357429Smarkm			 * Received EOF or error.  They are treated
55457429Smarkm			 * similarly, except that an error message is printed
55557429Smarkm			 * if it was an error condition.
55657429Smarkm			 */
55757429Smarkm			if (len < 0) {
55857429Smarkm				snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno));
55957429Smarkm				buffer_append(&stderr_buffer, buf, strlen(buf));
56057429Smarkm				stderr_bytes += strlen(buf);
56157429Smarkm			}
56257429Smarkm			/* Mark that we have seen EOF. */
56357429Smarkm			stdin_eof = 1;
56457429Smarkm			/*
56557429Smarkm			 * Send an EOF message to the server unless there is
56657429Smarkm			 * data in the buffer.  If there is data in the
56757429Smarkm			 * buffer, no message will be sent now.  Code
56857429Smarkm			 * elsewhere will send the EOF when the buffer
56957429Smarkm			 * becomes empty if stdin_eof is set.
57057429Smarkm			 */
57157429Smarkm			if (buffer_len(&stdin_buffer) == 0) {
57257429Smarkm				packet_start(SSH_CMSG_EOF);
57357429Smarkm				packet_send();
57457429Smarkm			}
57557429Smarkm		} else if (escape_char == -1) {
57657429Smarkm			/*
57757429Smarkm			 * Normal successful read, and no escape character.
57857429Smarkm			 * Just append the data to buffer.
57957429Smarkm			 */
58057429Smarkm			buffer_append(&stdin_buffer, buf, len);
58157429Smarkm			stdin_bytes += len;
58257429Smarkm		} else {
58357429Smarkm			/*
58457429Smarkm			 * Normal, successful read.  But we have an escape character
58557429Smarkm			 * and have to process the characters one by one.
58657429Smarkm			 */
58757429Smarkm			unsigned int i;
58857429Smarkm			for (i = 0; i < len; i++) {
58957429Smarkm				unsigned char ch;
59057429Smarkm				/* Get one character at a time. */
59157429Smarkm				ch = buf[i];
59257429Smarkm
59357429Smarkm				if (escape_pending) {
59457429Smarkm					/* We have previously seen an escape character. */
59557429Smarkm					/* Clear the flag now. */
59657429Smarkm					escape_pending = 0;
59757429Smarkm					/* Process the escaped character. */
59857429Smarkm					switch (ch) {
59957429Smarkm					case '.':
60057429Smarkm						/* Terminate the connection. */
60157429Smarkm						snprintf(buf, sizeof buf, "%c.\r\n", escape_char);
60257429Smarkm						buffer_append(&stderr_buffer, buf, strlen(buf));
60357429Smarkm						stderr_bytes += strlen(buf);
60457429Smarkm						quit_pending = 1;
60557429Smarkm						return;
60657429Smarkm
60757429Smarkm					case 'Z' - 64:
60857429Smarkm						/* Suspend the program. */
60957429Smarkm						/* Print a message to that effect to the user. */
61057429Smarkm						snprintf(buf, sizeof buf, "%c^Z\r\n", escape_char);
61157429Smarkm						buffer_append(&stderr_buffer, buf, strlen(buf));
61257429Smarkm						stderr_bytes += strlen(buf);
61357429Smarkm
61457429Smarkm						/* Restore terminal modes and suspend. */
61557429Smarkm						client_suspend_self();
61657429Smarkm
61757429Smarkm						/* We have been continued. */
61857429Smarkm						continue;
61957429Smarkm
62057429Smarkm					case '&':
62157429Smarkm						/*
62257429Smarkm						 * Detach the program (continue to serve connections,
62357429Smarkm						 * but put in background and no more new connections).
62457429Smarkm						 */
62557429Smarkm						if (!stdin_eof) {
62657429Smarkm							/*
62757429Smarkm							 * Sending SSH_CMSG_EOF alone does not always appear
62857429Smarkm							 * to be enough.  So we try to send an EOF character
62957429Smarkm							 * first.
63057429Smarkm							 */
63157429Smarkm							packet_start(SSH_CMSG_STDIN_DATA);
63257429Smarkm							packet_put_string("\004", 1);
63357429Smarkm							packet_send();
63457429Smarkm							/* Close stdin. */
63557429Smarkm							stdin_eof = 1;
63657429Smarkm							if (buffer_len(&stdin_buffer) == 0) {
63757429Smarkm								packet_start(SSH_CMSG_EOF);
63857429Smarkm								packet_send();
63957429Smarkm							}
64057429Smarkm						}
64157429Smarkm						/* Restore tty modes. */
64257429Smarkm						leave_raw_mode();
64357429Smarkm
64457429Smarkm						/* Stop listening for new connections. */
64557429Smarkm						channel_stop_listening();
64657429Smarkm
64757429Smarkm						printf("%c& [backgrounded]\n", escape_char);
64857429Smarkm
64957429Smarkm						/* Fork into background. */
65057429Smarkm						pid = fork();
65157429Smarkm						if (pid < 0) {
65257429Smarkm							error("fork: %.100s", strerror(errno));
65357429Smarkm							continue;
65457429Smarkm						}
65557429Smarkm						if (pid != 0) {	/* This is the parent. */
65657429Smarkm							/* The parent just exits. */
65757429Smarkm							exit(0);
65857429Smarkm						}
65957429Smarkm						/* The child continues serving connections. */
66057429Smarkm						continue;
66157429Smarkm
66257429Smarkm					case '?':
66357429Smarkm						snprintf(buf, sizeof buf,
66457429Smarkm"%c?\r\n\
66557429SmarkmSupported escape sequences:\r\n\
66657429Smarkm~.  - terminate connection\r\n\
66757429Smarkm~^Z - suspend ssh\r\n\
66857429Smarkm~#  - list forwarded connections\r\n\
66957429Smarkm~&  - background ssh (when waiting for connections to terminate)\r\n\
67057429Smarkm~?  - this message\r\n\
67157429Smarkm~~  - send the escape character by typing it twice\r\n\
67257429Smarkm(Note that escapes are only recognized immediately after newline.)\r\n",
67357429Smarkm							 escape_char);
67457429Smarkm						buffer_append(&stderr_buffer, buf, strlen(buf));
67557429Smarkm						continue;
67657429Smarkm
67757429Smarkm					case '#':
67857429Smarkm						snprintf(buf, sizeof buf, "%c#\r\n", escape_char);
67957429Smarkm						buffer_append(&stderr_buffer, buf, strlen(buf));
68057429Smarkm						s = channel_open_message();
68157429Smarkm						buffer_append(&stderr_buffer, s, strlen(s));
68257429Smarkm						xfree(s);
68357429Smarkm						continue;
68457429Smarkm
68557429Smarkm					default:
68657429Smarkm						if (ch != escape_char) {
68757429Smarkm							/*
68857429Smarkm							 * Escape character followed by non-special character.
68957429Smarkm							 * Append both to the input buffer.
69057429Smarkm							 */
69157429Smarkm							buf[0] = escape_char;
69257429Smarkm							buf[1] = ch;
69357429Smarkm							buffer_append(&stdin_buffer, buf, 2);
69457429Smarkm							stdin_bytes += 2;
69557429Smarkm							continue;
69657429Smarkm						}
69757429Smarkm						/*
69857429Smarkm						 * Note that escape character typed twice
69957429Smarkm						 * falls through here; the latter gets processed
70057429Smarkm						 * as a normal character below.
70157429Smarkm						 */
70257429Smarkm						break;
70357429Smarkm					}
70457429Smarkm				} else {
70557429Smarkm					/*
70657429Smarkm					 * The previous character was not an escape char. Check if this
70757429Smarkm					 * is an escape.
70857429Smarkm					 */
70957429Smarkm					if (last_was_cr && ch == escape_char) {
71057429Smarkm						/* It is. Set the flag and continue to next character. */
71157429Smarkm						escape_pending = 1;
71257429Smarkm						continue;
71357429Smarkm					}
71457429Smarkm				}
71557429Smarkm
71657429Smarkm				/*
71757429Smarkm				 * Normal character.  Record whether it was a newline,
71857429Smarkm				 * and append it to the buffer.
71957429Smarkm				 */
72057429Smarkm				last_was_cr = (ch == '\r' || ch == '\n');
72157429Smarkm				buf[0] = ch;
72257429Smarkm				buffer_append(&stdin_buffer, buf, 1);
72357429Smarkm				stdin_bytes += 1;
72457429Smarkm				continue;
72557429Smarkm			}
72657429Smarkm		}
72757429Smarkm	}
72857429Smarkm}
72957429Smarkm
73057429Smarkmvoid
73157429Smarkmclient_process_output(fd_set * writeset)
73257429Smarkm{
73357429Smarkm	int len;
73457429Smarkm	char buf[100];
73557429Smarkm
73657429Smarkm	/* Write buffered output to stdout. */
73757429Smarkm	if (FD_ISSET(fileno(stdout), writeset)) {
73857429Smarkm		/* Write as much data as possible. */
73957429Smarkm		len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
74057429Smarkm		    buffer_len(&stdout_buffer));
74157429Smarkm		if (len <= 0) {
74257429Smarkm			if (errno == EAGAIN)
74357429Smarkm				len = 0;
74457429Smarkm			else {
74557429Smarkm				/*
74657429Smarkm				 * An error or EOF was encountered.  Put an
74757429Smarkm				 * error message to stderr buffer.
74857429Smarkm				 */
74957429Smarkm				snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno));
75057429Smarkm				buffer_append(&stderr_buffer, buf, strlen(buf));
75157429Smarkm				stderr_bytes += strlen(buf);
75257429Smarkm				quit_pending = 1;
75357429Smarkm				return;
75457429Smarkm			}
75557429Smarkm		}
75657429Smarkm		/* Consume printed data from the buffer. */
75757429Smarkm		buffer_consume(&stdout_buffer, len);
75857429Smarkm	}
75957429Smarkm	/* Write buffered output to stderr. */
76057429Smarkm	if (FD_ISSET(fileno(stderr), writeset)) {
76157429Smarkm		/* Write as much data as possible. */
76257429Smarkm		len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
76357429Smarkm		    buffer_len(&stderr_buffer));
76457429Smarkm		if (len <= 0) {
76557429Smarkm			if (errno == EAGAIN)
76657429Smarkm				len = 0;
76757429Smarkm			else {
76857429Smarkm				/* EOF or error, but can't even print error message. */
76957429Smarkm				quit_pending = 1;
77057429Smarkm				return;
77157429Smarkm			}
77257429Smarkm		}
77357429Smarkm		/* Consume printed characters from the buffer. */
77457429Smarkm		buffer_consume(&stderr_buffer, len);
77557429Smarkm	}
77657429Smarkm}
77757429Smarkm
77857429Smarkm/*
77957429Smarkm * Implements the interactive session with the server.  This is called after
78057429Smarkm * the user has been authenticated, and a command has been started on the
78157429Smarkm * remote host.  If escape_char != -1, it is the character used as an escape
78257429Smarkm * character for terminating or suspending the session.
78357429Smarkm */
78457429Smarkm
78557429Smarkmint
78657429Smarkmclient_loop(int have_pty, int escape_char_arg)
78757429Smarkm{
78857429Smarkm	extern Options options;
78957429Smarkm	double start_time, total_time;
79057429Smarkm	int len;
79157429Smarkm	char buf[100];
79257429Smarkm
79357429Smarkm	debug("Entering interactive session.");
79457429Smarkm
79557429Smarkm	start_time = get_current_time();
79657429Smarkm
79757429Smarkm	/* Initialize variables. */
79857429Smarkm	escape_pending = 0;
79957429Smarkm	last_was_cr = 1;
80057429Smarkm	exit_status = -1;
80157429Smarkm	stdin_eof = 0;
80257429Smarkm	buffer_high = 64 * 1024;
80357429Smarkm	connection_in = packet_get_connection_in();
80457429Smarkm	connection_out = packet_get_connection_out();
80557429Smarkm	max_fd = connection_in;
80657429Smarkm	if (connection_out > max_fd)
80757429Smarkm		max_fd = connection_out;
80857429Smarkm	stdin_bytes = 0;
80957429Smarkm	stdout_bytes = 0;
81057429Smarkm	stderr_bytes = 0;
81157429Smarkm	quit_pending = 0;
81257429Smarkm	escape_char = escape_char_arg;
81357429Smarkm
81457429Smarkm	/* Initialize buffers. */
81557429Smarkm	buffer_init(&stdin_buffer);
81657429Smarkm	buffer_init(&stdout_buffer);
81757429Smarkm	buffer_init(&stderr_buffer);
81857429Smarkm
81957429Smarkm	/* Set signal handlers to restore non-blocking mode.  */
82057429Smarkm	signal(SIGINT, signal_handler);
82157429Smarkm	signal(SIGQUIT, signal_handler);
82257429Smarkm	signal(SIGTERM, signal_handler);
82357429Smarkm	signal(SIGPIPE, SIG_IGN);
82457429Smarkm	if (have_pty)
82557429Smarkm		signal(SIGWINCH, window_change_handler);
82657429Smarkm
82757429Smarkm	if (have_pty)
82857429Smarkm		enter_raw_mode();
82957429Smarkm
83057429Smarkm	/* Check if we should immediately send of on stdin. */
83157429Smarkm	client_check_initial_eof_on_stdin();
83257429Smarkm
83357429Smarkm	/* Main loop of the client for the interactive session mode. */
83457429Smarkm	while (!quit_pending) {
83557429Smarkm		fd_set readset, writeset;
83657429Smarkm
83757429Smarkm		/* Process buffered packets sent by the server. */
83857429Smarkm		client_process_buffered_input_packets();
83957429Smarkm
84057429Smarkm		/*
84157429Smarkm		 * Make packets of buffered stdin data, and buffer them for
84257429Smarkm		 * sending to the server.
84357429Smarkm		 */
84457429Smarkm		client_make_packets_from_stdin_data();
84557429Smarkm
84657429Smarkm		/*
84757429Smarkm		 * Make packets from buffered channel data, and buffer them
84857429Smarkm		 * for sending to the server.
84957429Smarkm		 */
85057429Smarkm		if (packet_not_very_much_data_to_write())
85157429Smarkm			channel_output_poll();
85257429Smarkm
85357429Smarkm		/*
85457429Smarkm		 * Check if the window size has changed, and buffer a message
85557429Smarkm		 * about it to the server if so.
85657429Smarkm		 */
85757429Smarkm		client_check_window_change();
85857429Smarkm
85957429Smarkm		if (quit_pending)
86057429Smarkm			break;
86157429Smarkm
86257429Smarkm		/*
86357429Smarkm		 * Wait until we have something to do (something becomes
86457429Smarkm		 * available on one of the descriptors).
86557429Smarkm		 */
86657429Smarkm		client_wait_until_can_do_something(&readset, &writeset);
86757429Smarkm
86857429Smarkm		if (quit_pending)
86957429Smarkm			break;
87057429Smarkm
87157429Smarkm		/* Do channel operations. */
87257429Smarkm		channel_after_select(&readset, &writeset);
87357429Smarkm
87457429Smarkm		/*
87557429Smarkm		 * Process input from the connection and from stdin. Buffer
87657429Smarkm		 * any data that is available.
87757429Smarkm		 */
87857429Smarkm		client_process_input(&readset);
87957429Smarkm
88057429Smarkm		/*
88157429Smarkm		 * Process output to stdout and stderr.   Output to the
88257429Smarkm		 * connection is processed elsewhere (above).
88357429Smarkm		 */
88457429Smarkm		client_process_output(&writeset);
88557429Smarkm
88657429Smarkm		/* Send as much buffered packet data as possible to the sender. */
88757429Smarkm		if (FD_ISSET(connection_out, &writeset))
88857429Smarkm			packet_write_poll();
88957429Smarkm	}
89057429Smarkm
89157429Smarkm	/* Terminate the session. */
89257429Smarkm
89357429Smarkm	/* Stop watching for window change. */
89457429Smarkm	if (have_pty)
89557429Smarkm		signal(SIGWINCH, SIG_DFL);
89657429Smarkm
89757429Smarkm	/* Stop listening for connections. */
89857429Smarkm	channel_stop_listening();
89957429Smarkm
90057429Smarkm	/*
90157429Smarkm	 * In interactive mode (with pseudo tty) display a message indicating
90257429Smarkm	 * that the connection has been closed.
90357429Smarkm	 */
90457429Smarkm	if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) {
90557429Smarkm		snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host);
90657429Smarkm		buffer_append(&stderr_buffer, buf, strlen(buf));
90757429Smarkm		stderr_bytes += strlen(buf);
90857429Smarkm	}
90957429Smarkm	/* Output any buffered data for stdout. */
91057429Smarkm	while (buffer_len(&stdout_buffer) > 0) {
91157429Smarkm		len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
91257429Smarkm		    buffer_len(&stdout_buffer));
91357429Smarkm		if (len <= 0) {
91457429Smarkm			error("Write failed flushing stdout buffer.");
91557429Smarkm			break;
91657429Smarkm		}
91757429Smarkm		buffer_consume(&stdout_buffer, len);
91857429Smarkm	}
91957429Smarkm
92057429Smarkm	/* Output any buffered data for stderr. */
92157429Smarkm	while (buffer_len(&stderr_buffer) > 0) {
92257429Smarkm		len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
92357429Smarkm		    buffer_len(&stderr_buffer));
92457429Smarkm		if (len <= 0) {
92557429Smarkm			error("Write failed flushing stderr buffer.");
92657429Smarkm			break;
92757429Smarkm		}
92857429Smarkm		buffer_consume(&stderr_buffer, len);
92957429Smarkm	}
93057429Smarkm
93157429Smarkm	if (have_pty)
93257429Smarkm		leave_raw_mode();
93357429Smarkm
93457429Smarkm	/* Clear and free any buffers. */
93557429Smarkm	memset(buf, 0, sizeof(buf));
93657429Smarkm	buffer_free(&stdin_buffer);
93757429Smarkm	buffer_free(&stdout_buffer);
93857429Smarkm	buffer_free(&stderr_buffer);
93957429Smarkm
94057429Smarkm	/* Report bytes transferred, and transfer rates. */
94157429Smarkm	total_time = get_current_time() - start_time;
94257429Smarkm	debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds",
94357429Smarkm	      stdin_bytes, stdout_bytes, stderr_bytes, total_time);
94457429Smarkm	if (total_time > 0)
94557429Smarkm		debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f",
94657429Smarkm		      stdin_bytes / total_time, stdout_bytes / total_time,
94757429Smarkm		      stderr_bytes / total_time);
94857429Smarkm
94957429Smarkm	/* Return the exit status of the program. */
95057429Smarkm	debug("Exit status %d", exit_status);
95157429Smarkm	return exit_status;
95257429Smarkm}
953