clientloop.c revision 69587
157429Smarkm/*
257429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi>
357429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
457429Smarkm *                    All rights reserved
565668Skris * The main loop for the interactive session (client side).
660573Skris *
765668Skris * As far as I am concerned, the code I have written for this software
865668Skris * can be used freely for any purpose.  Any derived versions of this
965668Skris * software must be clearly marked as such, and if the derived work is
1065668Skris * incompatible with the protocol description in the RFC file, it must be
1165668Skris * called by a name other than "ssh" or "Secure Shell".
1260573Skris *
1360573Skris *
1465668Skris * Copyright (c) 1999 Theo de Raadt.  All rights reserved.
1560573Skris *
1665668Skris * Redistribution and use in source and binary forms, with or without
1765668Skris * modification, are permitted provided that the following conditions
1865668Skris * are met:
1965668Skris * 1. Redistributions of source code must retain the above copyright
2065668Skris *    notice, this list of conditions and the following disclaimer.
2165668Skris * 2. Redistributions in binary form must reproduce the above copyright
2265668Skris *    notice, this list of conditions and the following disclaimer in the
2365668Skris *    documentation and/or other materials provided with the distribution.
2465668Skris *
2565668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2665668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2765668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2865668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2965668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
3065668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3165668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3265668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3365668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3465668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3565668Skris *
3665668Skris *
3760573Skris * SSH2 support added by Markus Friedl.
3865668Skris * Copyright (c) 1999,2000 Markus Friedl.  All rights reserved.
3965668Skris *
4065668Skris * Redistribution and use in source and binary forms, with or without
4165668Skris * modification, are permitted provided that the following conditions
4265668Skris * are met:
4365668Skris * 1. Redistributions of source code must retain the above copyright
4465668Skris *    notice, this list of conditions and the following disclaimer.
4565668Skris * 2. Redistributions in binary form must reproduce the above copyright
4665668Skris *    notice, this list of conditions and the following disclaimer in the
4765668Skris *    documentation and/or other materials provided with the distribution.
4865668Skris *
4965668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
5065668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
5165668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
5265668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
5365668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
5465668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
5565668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
5665668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
5765668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
5865668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5957429Smarkm */
6057429Smarkm
6157429Smarkm#include "includes.h"
6269587SgreenRCSID("$OpenBSD: clientloop.c,v 1.39 2000/10/27 07:48:22 markus Exp $");
6357429Smarkm
6457429Smarkm#include "xmalloc.h"
6557429Smarkm#include "ssh.h"
6657429Smarkm#include "packet.h"
6757429Smarkm#include "buffer.h"
6857429Smarkm#include "readconf.h"
6957429Smarkm
7060573Skris#include "ssh2.h"
7160573Skris#include "compat.h"
7260573Skris#include "channels.h"
7360573Skris#include "dispatch.h"
7460573Skris
7565668Skris#include "buffer.h"
7665668Skris#include "bufaux.h"
7760573Skris
7869587Sgreen
7969587Sgreen/* import options */
8068700Sgreenextern Options options;
8168700Sgreen
8257429Smarkm/* Flag indicating that stdin should be redirected from /dev/null. */
8357429Smarkmextern int stdin_null_flag;
8457429Smarkm
8557429Smarkm/*
8657429Smarkm * Name of the host we are connecting to.  This is the name given on the
8757429Smarkm * command line, or the HostName specified for the user-supplied name in a
8857429Smarkm * configuration file.
8957429Smarkm */
9057429Smarkmextern char *host;
9157429Smarkm
9257429Smarkm/*
9357429Smarkm * Flag to indicate that we have received a window change signal which has
9457429Smarkm * not yet been processed.  This will cause a message indicating the new
9557429Smarkm * window size to be sent to the server a little later.  This is volatile
9657429Smarkm * because this is updated in a signal handler.
9757429Smarkm */
9857429Smarkmstatic volatile int received_window_change_signal = 0;
9957429Smarkm
10057429Smarkm/* Terminal modes, as saved by enter_raw_mode. */
10157429Smarkmstatic struct termios saved_tio;
10257429Smarkm
10357429Smarkm/*
10457429Smarkm * Flag indicating whether we are in raw mode.  This is used by
10557429Smarkm * enter_raw_mode and leave_raw_mode.
10657429Smarkm */
10757429Smarkmstatic int in_raw_mode = 0;
10857429Smarkm
10957429Smarkm/* Flag indicating whether the user\'s terminal is in non-blocking mode. */
11057429Smarkmstatic int in_non_blocking_mode = 0;
11157429Smarkm
11257429Smarkm/* Common data for the client loop code. */
11365668Skrisstatic int quit_pending;	/* Set to non-zero to quit the client loop. */
11465668Skrisstatic int escape_char;		/* Escape character. */
11557429Smarkmstatic int escape_pending;	/* Last character was the escape character */
11657429Smarkmstatic int last_was_cr;		/* Last character was a newline. */
11757429Smarkmstatic int exit_status;		/* Used to store the exit status of the command. */
11857429Smarkmstatic int stdin_eof;		/* EOF has been encountered on standard error. */
11957429Smarkmstatic Buffer stdin_buffer;	/* Buffer for stdin data. */
12057429Smarkmstatic Buffer stdout_buffer;	/* Buffer for stdout data. */
12157429Smarkmstatic Buffer stderr_buffer;	/* Buffer for stderr data. */
12265668Skrisstatic unsigned long stdin_bytes, stdout_bytes, stderr_bytes;
12357429Smarkmstatic unsigned int buffer_high;/* Soft max buffer size. */
12457429Smarkmstatic int max_fd;		/* Maximum file descriptor number in select(). */
12557429Smarkmstatic int connection_in;	/* Connection to server (input). */
12657429Smarkmstatic int connection_out;	/* Connection to server (output). */
12757429Smarkm
12860573Skris
12960573Skrisvoid	client_init_dispatch(void);
13060573Skrisint	session_ident = -1;
13160573Skris
13257429Smarkm/* Returns the user\'s terminal to normal mode if it had been put in raw mode. */
13357429Smarkm
13460573Skrisvoid
13557429Smarkmleave_raw_mode()
13657429Smarkm{
13757429Smarkm	if (!in_raw_mode)
13857429Smarkm		return;
13957429Smarkm	in_raw_mode = 0;
14057429Smarkm	if (tcsetattr(fileno(stdin), TCSADRAIN, &saved_tio) < 0)
14157429Smarkm		perror("tcsetattr");
14257429Smarkm
14357429Smarkm	fatal_remove_cleanup((void (*) (void *)) leave_raw_mode, NULL);
14457429Smarkm}
14557429Smarkm
14657429Smarkm/* Puts the user\'s terminal in raw mode. */
14757429Smarkm
14860573Skrisvoid
14957429Smarkmenter_raw_mode()
15057429Smarkm{
15157429Smarkm	struct termios tio;
15257429Smarkm
15357429Smarkm	if (tcgetattr(fileno(stdin), &tio) < 0)
15457429Smarkm		perror("tcgetattr");
15557429Smarkm	saved_tio = tio;
15657429Smarkm	tio.c_iflag |= IGNPAR;
15757429Smarkm	tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
15857429Smarkm	tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
15957429Smarkm#ifdef IEXTEN
16057429Smarkm	tio.c_lflag &= ~IEXTEN;
16157429Smarkm#endif				/* IEXTEN */
16257429Smarkm	tio.c_oflag &= ~OPOST;
16357429Smarkm	tio.c_cc[VMIN] = 1;
16457429Smarkm	tio.c_cc[VTIME] = 0;
16557429Smarkm	if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0)
16657429Smarkm		perror("tcsetattr");
16757429Smarkm	in_raw_mode = 1;
16857429Smarkm
16957429Smarkm	fatal_add_cleanup((void (*) (void *)) leave_raw_mode, NULL);
17057429Smarkm}
17157429Smarkm
17257429Smarkm/* Restores stdin to blocking mode. */
17357429Smarkm
17460573Skrisvoid
17557429Smarkmleave_non_blocking()
17657429Smarkm{
17757429Smarkm	if (in_non_blocking_mode) {
17857429Smarkm		(void) fcntl(fileno(stdin), F_SETFL, 0);
17957429Smarkm		in_non_blocking_mode = 0;
18057429Smarkm		fatal_remove_cleanup((void (*) (void *)) leave_non_blocking, NULL);
18157429Smarkm	}
18257429Smarkm}
18357429Smarkm
18457429Smarkm/* Puts stdin terminal in non-blocking mode. */
18557429Smarkm
18660573Skrisvoid
18757429Smarkmenter_non_blocking()
18857429Smarkm{
18957429Smarkm	in_non_blocking_mode = 1;
19057429Smarkm	(void) fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
19157429Smarkm	fatal_add_cleanup((void (*) (void *)) leave_non_blocking, NULL);
19257429Smarkm}
19357429Smarkm
19457429Smarkm/*
19557429Smarkm * Signal handler for the window change signal (SIGWINCH).  This just sets a
19657429Smarkm * flag indicating that the window has changed.
19757429Smarkm */
19857429Smarkm
19960573Skrisvoid
20057429Smarkmwindow_change_handler(int sig)
20157429Smarkm{
20257429Smarkm	received_window_change_signal = 1;
20357429Smarkm	signal(SIGWINCH, window_change_handler);
20457429Smarkm}
20557429Smarkm
20657429Smarkm/*
20757429Smarkm * Signal handler for signals that cause the program to terminate.  These
20857429Smarkm * signals must be trapped to restore terminal modes.
20957429Smarkm */
21057429Smarkm
21160573Skrisvoid
21257429Smarkmsignal_handler(int sig)
21357429Smarkm{
21457429Smarkm	if (in_raw_mode)
21557429Smarkm		leave_raw_mode();
21657429Smarkm	if (in_non_blocking_mode)
21757429Smarkm		leave_non_blocking();
21857429Smarkm	channel_stop_listening();
21957429Smarkm	packet_close();
22057429Smarkm	fatal("Killed by signal %d.", sig);
22157429Smarkm}
22257429Smarkm
22357429Smarkm/*
22457429Smarkm * Returns current time in seconds from Jan 1, 1970 with the maximum
22557429Smarkm * available resolution.
22657429Smarkm */
22757429Smarkm
22860573Skrisdouble
22957429Smarkmget_current_time()
23057429Smarkm{
23157429Smarkm	struct timeval tv;
23257429Smarkm	gettimeofday(&tv, NULL);
23357429Smarkm	return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
23457429Smarkm}
23557429Smarkm
23657429Smarkm/*
23757429Smarkm * This is called when the interactive is entered.  This checks if there is
23857429Smarkm * an EOF coming on stdin.  We must check this explicitly, as select() does
23957429Smarkm * not appear to wake up when redirecting from /dev/null.
24057429Smarkm */
24157429Smarkm
24260573Skrisvoid
24357429Smarkmclient_check_initial_eof_on_stdin()
24457429Smarkm{
24557429Smarkm	int len;
24657429Smarkm	char buf[1];
24757429Smarkm
24857429Smarkm	/*
24957429Smarkm	 * If standard input is to be "redirected from /dev/null", we simply
25057429Smarkm	 * mark that we have seen an EOF and send an EOF message to the
25157429Smarkm	 * server. Otherwise, we try to read a single character; it appears
25257429Smarkm	 * that for some files, such /dev/null, select() never wakes up for
25357429Smarkm	 * read for this descriptor, which means that we never get EOF.  This
25457429Smarkm	 * way we will get the EOF if stdin comes from /dev/null or similar.
25557429Smarkm	 */
25657429Smarkm	if (stdin_null_flag) {
25757429Smarkm		/* Fake EOF on stdin. */
25857429Smarkm		debug("Sending eof.");
25957429Smarkm		stdin_eof = 1;
26057429Smarkm		packet_start(SSH_CMSG_EOF);
26157429Smarkm		packet_send();
26257429Smarkm	} else {
26357429Smarkm		enter_non_blocking();
26457429Smarkm
26557429Smarkm		/* Check for immediate EOF on stdin. */
26657429Smarkm		len = read(fileno(stdin), buf, 1);
26757429Smarkm		if (len == 0) {
26857429Smarkm			/* EOF.  Record that we have seen it and send EOF to server. */
26957429Smarkm			debug("Sending eof.");
27057429Smarkm			stdin_eof = 1;
27157429Smarkm			packet_start(SSH_CMSG_EOF);
27257429Smarkm			packet_send();
27357429Smarkm		} else if (len > 0) {
27457429Smarkm			/*
27557429Smarkm			 * Got data.  We must store the data in the buffer,
27657429Smarkm			 * and also process it as an escape character if
27757429Smarkm			 * appropriate.
27857429Smarkm			 */
27957429Smarkm			if ((unsigned char) buf[0] == escape_char)
28057429Smarkm				escape_pending = 1;
28157429Smarkm			else {
28257429Smarkm				buffer_append(&stdin_buffer, buf, 1);
28357429Smarkm				stdin_bytes += 1;
28457429Smarkm			}
28557429Smarkm		}
28657429Smarkm		leave_non_blocking();
28757429Smarkm	}
28857429Smarkm}
28957429Smarkm
29057429Smarkm
29157429Smarkm/*
29257429Smarkm * Make packets from buffered stdin data, and buffer them for sending to the
29357429Smarkm * connection.
29457429Smarkm */
29557429Smarkm
29660573Skrisvoid
29757429Smarkmclient_make_packets_from_stdin_data()
29857429Smarkm{
29957429Smarkm	unsigned int len;
30057429Smarkm
30157429Smarkm	/* Send buffered stdin data to the server. */
30257429Smarkm	while (buffer_len(&stdin_buffer) > 0 &&
30357429Smarkm	       packet_not_very_much_data_to_write()) {
30457429Smarkm		len = buffer_len(&stdin_buffer);
30557429Smarkm		/* Keep the packets at reasonable size. */
30657429Smarkm		if (len > packet_get_maxsize())
30757429Smarkm			len = packet_get_maxsize();
30857429Smarkm		packet_start(SSH_CMSG_STDIN_DATA);
30957429Smarkm		packet_put_string(buffer_ptr(&stdin_buffer), len);
31057429Smarkm		packet_send();
31157429Smarkm		buffer_consume(&stdin_buffer, len);
31257429Smarkm		/* If we have a pending EOF, send it now. */
31357429Smarkm		if (stdin_eof && buffer_len(&stdin_buffer) == 0) {
31457429Smarkm			packet_start(SSH_CMSG_EOF);
31557429Smarkm			packet_send();
31657429Smarkm		}
31757429Smarkm	}
31857429Smarkm}
31957429Smarkm
32057429Smarkm/*
32157429Smarkm * Checks if the client window has changed, and sends a packet about it to
32257429Smarkm * the server if so.  The actual change is detected elsewhere (by a software
32357429Smarkm * interrupt on Unix); this just checks the flag and sends a message if
32457429Smarkm * appropriate.
32557429Smarkm */
32657429Smarkm
32760573Skrisvoid
32857429Smarkmclient_check_window_change()
32957429Smarkm{
33060573Skris	struct winsize ws;
33157429Smarkm
33260573Skris	if (! received_window_change_signal)
33360573Skris		return;
33460573Skris	/** XXX race */
33560573Skris	received_window_change_signal = 0;
33657429Smarkm
33760573Skris	if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
33860573Skris		return;
33960573Skris
34069587Sgreen	debug2("client_check_window_change: changed");
34160573Skris
34260573Skris	if (compat20) {
34360573Skris		channel_request_start(session_ident, "window-change", 0);
34460573Skris		packet_put_int(ws.ws_col);
34560573Skris		packet_put_int(ws.ws_row);
34660573Skris		packet_put_int(ws.ws_xpixel);
34760573Skris		packet_put_int(ws.ws_ypixel);
34860573Skris		packet_send();
34960573Skris	} else {
35060573Skris		packet_start(SSH_CMSG_WINDOW_SIZE);
35160573Skris		packet_put_int(ws.ws_row);
35260573Skris		packet_put_int(ws.ws_col);
35360573Skris		packet_put_int(ws.ws_xpixel);
35460573Skris		packet_put_int(ws.ws_ypixel);
35560573Skris		packet_send();
35657429Smarkm	}
35757429Smarkm}
35857429Smarkm
35957429Smarkm/*
36057429Smarkm * Waits until the client can do something (some data becomes available on
36157429Smarkm * one of the file descriptors).
36257429Smarkm */
36357429Smarkm
36460573Skrisvoid
36557429Smarkmclient_wait_until_can_do_something(fd_set * readset, fd_set * writeset)
36657429Smarkm{
36757429Smarkm	/* Initialize select masks. */
36857429Smarkm	FD_ZERO(readset);
36960573Skris	FD_ZERO(writeset);
37057429Smarkm
37160573Skris	if (!compat20) {
37260573Skris		/* Read from the connection, unless our buffers are full. */
37360573Skris		if (buffer_len(&stdout_buffer) < buffer_high &&
37460573Skris		    buffer_len(&stderr_buffer) < buffer_high &&
37560573Skris		    channel_not_very_much_buffered_data())
37660573Skris			FD_SET(connection_in, readset);
37760573Skris		/*
37860573Skris		 * Read from stdin, unless we have seen EOF or have very much
37960573Skris		 * buffered data to send to the server.
38060573Skris		 */
38160573Skris		if (!stdin_eof && packet_not_very_much_data_to_write())
38260573Skris			FD_SET(fileno(stdin), readset);
38360573Skris
38460573Skris		/* Select stdout/stderr if have data in buffer. */
38560573Skris		if (buffer_len(&stdout_buffer) > 0)
38660573Skris			FD_SET(fileno(stdout), writeset);
38760573Skris		if (buffer_len(&stderr_buffer) > 0)
38860573Skris			FD_SET(fileno(stderr), writeset);
38960573Skris	} else {
39057429Smarkm		FD_SET(connection_in, readset);
39160573Skris	}
39257429Smarkm
39357429Smarkm	/* Add any selections by the channel mechanism. */
39457429Smarkm	channel_prepare_select(readset, writeset);
39557429Smarkm
39657429Smarkm	/* Select server connection if have data to write to the server. */
39757429Smarkm	if (packet_have_data_to_write())
39857429Smarkm		FD_SET(connection_out, writeset);
39957429Smarkm
40060573Skris/* move UP XXX */
40157429Smarkm	/* Update maximum file descriptor number, if appropriate. */
40257429Smarkm	if (channel_max_fd() > max_fd)
40357429Smarkm		max_fd = channel_max_fd();
40457429Smarkm
40557429Smarkm	/*
40657429Smarkm	 * Wait for something to happen.  This will suspend the process until
40757429Smarkm	 * some selected descriptor can be read, written, or has some other
40857429Smarkm	 * event pending. Note: if you want to implement SSH_MSG_IGNORE
40957429Smarkm	 * messages to fool traffic analysis, this might be the place to do
41057429Smarkm	 * it: just have a random timeout for the select, and send a random
41157429Smarkm	 * SSH_MSG_IGNORE packet when the timeout expires.
41257429Smarkm	 */
41357429Smarkm
41457429Smarkm	if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0) {
41557429Smarkm		char buf[100];
41657429Smarkm		/* Some systems fail to clear these automatically. */
41757429Smarkm		FD_ZERO(readset);
41857429Smarkm		FD_ZERO(writeset);
41957429Smarkm		if (errno == EINTR)
42057429Smarkm			return;
42157429Smarkm		/* Note: we might still have data in the buffers. */
42257429Smarkm		snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
42357429Smarkm		buffer_append(&stderr_buffer, buf, strlen(buf));
42457429Smarkm		stderr_bytes += strlen(buf);
42557429Smarkm		quit_pending = 1;
42657429Smarkm	}
42757429Smarkm}
42857429Smarkm
42960573Skrisvoid
43065668Skrisclient_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr)
43157429Smarkm{
43257429Smarkm	struct winsize oldws, newws;
43357429Smarkm
43457429Smarkm	/* Flush stdout and stderr buffers. */
43565668Skris	if (buffer_len(bout) > 0)
43665668Skris		atomicio(write, fileno(stdout), buffer_ptr(bout), buffer_len(bout));
43765668Skris	if (buffer_len(berr) > 0)
43865668Skris		atomicio(write, fileno(stderr), buffer_ptr(berr), buffer_len(berr));
43957429Smarkm
44057429Smarkm	leave_raw_mode();
44157429Smarkm
44257429Smarkm	/*
44357429Smarkm	 * Free (and clear) the buffer to reduce the amount of data that gets
44457429Smarkm	 * written to swap.
44557429Smarkm	 */
44665668Skris	buffer_free(bin);
44765668Skris	buffer_free(bout);
44865668Skris	buffer_free(berr);
44957429Smarkm
45057429Smarkm	/* Save old window size. */
45157429Smarkm	ioctl(fileno(stdin), TIOCGWINSZ, &oldws);
45257429Smarkm
45357429Smarkm	/* Send the suspend signal to the program itself. */
45457429Smarkm	kill(getpid(), SIGTSTP);
45557429Smarkm
45657429Smarkm	/* Check if the window size has changed. */
45757429Smarkm	if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 &&
45857429Smarkm	    (oldws.ws_row != newws.ws_row ||
45957429Smarkm	     oldws.ws_col != newws.ws_col ||
46057429Smarkm	     oldws.ws_xpixel != newws.ws_xpixel ||
46157429Smarkm	     oldws.ws_ypixel != newws.ws_ypixel))
46257429Smarkm		received_window_change_signal = 1;
46357429Smarkm
46457429Smarkm	/* OK, we have been continued by the user. Reinitialize buffers. */
46565668Skris	buffer_init(bin);
46665668Skris	buffer_init(bout);
46765668Skris	buffer_init(berr);
46857429Smarkm
46957429Smarkm	enter_raw_mode();
47057429Smarkm}
47157429Smarkm
47260573Skrisvoid
47360573Skrisclient_process_net_input(fd_set * readset)
47457429Smarkm{
47560573Skris	int len;
47660573Skris	char buf[8192];
47757429Smarkm
47857429Smarkm	/*
47957429Smarkm	 * Read input from the server, and add any such data to the buffer of
48057429Smarkm	 * the packet subsystem.
48157429Smarkm	 */
48257429Smarkm	if (FD_ISSET(connection_in, readset)) {
48357429Smarkm		/* Read as much as possible. */
48457429Smarkm		len = read(connection_in, buf, sizeof(buf));
48557429Smarkm		if (len == 0) {
48657429Smarkm			/* Received EOF.  The remote host has closed the connection. */
48757429Smarkm			snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n",
48857429Smarkm				 host);
48957429Smarkm			buffer_append(&stderr_buffer, buf, strlen(buf));
49057429Smarkm			stderr_bytes += strlen(buf);
49157429Smarkm			quit_pending = 1;
49257429Smarkm			return;
49357429Smarkm		}
49457429Smarkm		/*
49557429Smarkm		 * There is a kernel bug on Solaris that causes select to
49657429Smarkm		 * sometimes wake up even though there is no data available.
49757429Smarkm		 */
49857429Smarkm		if (len < 0 && errno == EAGAIN)
49957429Smarkm			len = 0;
50057429Smarkm
50157429Smarkm		if (len < 0) {
50257429Smarkm			/* An error has encountered.  Perhaps there is a network problem. */
50357429Smarkm			snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n",
50457429Smarkm				 host, strerror(errno));
50557429Smarkm			buffer_append(&stderr_buffer, buf, strlen(buf));
50657429Smarkm			stderr_bytes += strlen(buf);
50757429Smarkm			quit_pending = 1;
50857429Smarkm			return;
50957429Smarkm		}
51057429Smarkm		packet_process_incoming(buf, len);
51157429Smarkm	}
51260573Skris}
51360573Skris
51465668Skris/* process the characters one by one */
51565668Skrisint
51665668Skrisprocess_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len)
51765668Skris{
51865668Skris	char string[1024];
51965668Skris	pid_t pid;
52065668Skris	int bytes = 0;
52165668Skris	unsigned int i;
52265668Skris	unsigned char ch;
52365668Skris	char *s;
52465668Skris
52565668Skris	for (i = 0; i < len; i++) {
52665668Skris		/* Get one character at a time. */
52765668Skris		ch = buf[i];
52865668Skris
52965668Skris		if (escape_pending) {
53065668Skris			/* We have previously seen an escape character. */
53165668Skris			/* Clear the flag now. */
53265668Skris			escape_pending = 0;
53365668Skris
53465668Skris			/* Process the escaped character. */
53565668Skris			switch (ch) {
53665668Skris			case '.':
53765668Skris				/* Terminate the connection. */
53865668Skris				snprintf(string, sizeof string, "%c.\r\n", escape_char);
53965668Skris				buffer_append(berr, string, strlen(string));
54065668Skris				/*stderr_bytes += strlen(string); XXX*/
54165668Skris
54265668Skris				quit_pending = 1;
54365668Skris				return -1;
54465668Skris
54565668Skris			case 'Z' - 64:
54665668Skris				/* Suspend the program. */
54765668Skris				/* Print a message to that effect to the user. */
54865668Skris				snprintf(string, sizeof string, "%c^Z [suspend ssh]\r\n", escape_char);
54965668Skris				buffer_append(berr, string, strlen(string));
55065668Skris				/*stderr_bytes += strlen(string); XXX*/
55165668Skris
55265668Skris				/* Restore terminal modes and suspend. */
55365668Skris				client_suspend_self(bin, bout, berr);
55465668Skris
55565668Skris				/* We have been continued. */
55665668Skris				continue;
55765668Skris
55865668Skris			case '&':
55965668Skris				/* XXX does not work yet with proto 2 */
56065668Skris				if (compat20)
56165668Skris					continue;
56265668Skris				/*
56365668Skris				 * Detach the program (continue to serve connections,
56465668Skris				 * but put in background and no more new connections).
56565668Skris				 */
56665668Skris				if (!stdin_eof) {
56765668Skris					/*
56865668Skris					 * Sending SSH_CMSG_EOF alone does not always appear
56965668Skris					 * to be enough.  So we try to send an EOF character
57065668Skris					 * first.
57165668Skris					 */
57265668Skris					packet_start(SSH_CMSG_STDIN_DATA);
57365668Skris					packet_put_string("\004", 1);
57465668Skris					packet_send();
57565668Skris					/* Close stdin. */
57665668Skris					stdin_eof = 1;
57765668Skris					if (buffer_len(bin) == 0) {
57865668Skris						packet_start(SSH_CMSG_EOF);
57965668Skris						packet_send();
58065668Skris					}
58165668Skris				}
58265668Skris				/* Restore tty modes. */
58365668Skris				leave_raw_mode();
58465668Skris
58565668Skris				/* Stop listening for new connections. */
58665668Skris				channel_stop_listening();
58765668Skris
58865668Skris				printf("%c& [backgrounded]\n", escape_char);
58965668Skris
59065668Skris				/* Fork into background. */
59165668Skris				pid = fork();
59265668Skris				if (pid < 0) {
59365668Skris					error("fork: %.100s", strerror(errno));
59465668Skris					continue;
59565668Skris				}
59665668Skris				if (pid != 0) {	/* This is the parent. */
59765668Skris					/* The parent just exits. */
59865668Skris					exit(0);
59965668Skris				}
60065668Skris				/* The child continues serving connections. */
60165668Skris				continue; /*XXX ? */
60265668Skris
60365668Skris			case '?':
60465668Skris				snprintf(string, sizeof string,
60565668Skris"%c?\r\n\
60665668SkrisSupported escape sequences:\r\n\
60765668Skris~.  - terminate connection\r\n\
60865668Skris~^Z - suspend ssh\r\n\
60965668Skris~#  - list forwarded connections\r\n\
61065668Skris~&  - background ssh (when waiting for connections to terminate)\r\n\
61165668Skris~?  - this message\r\n\
61265668Skris~~  - send the escape character by typing it twice\r\n\
61365668Skris(Note that escapes are only recognized immediately after newline.)\r\n",
61465668Skris					 escape_char);
61565668Skris				buffer_append(berr, string, strlen(string));
61665668Skris				continue;
61765668Skris
61865668Skris			case '#':
61965668Skris				snprintf(string, sizeof string, "%c#\r\n", escape_char);
62065668Skris				buffer_append(berr, string, strlen(string));
62165668Skris				s = channel_open_message();
62265668Skris				buffer_append(berr, s, strlen(s));
62365668Skris				xfree(s);
62465668Skris				continue;
62565668Skris
62665668Skris			default:
62765668Skris				if (ch != escape_char) {
62865668Skris					buffer_put_char(bin, escape_char);
62965668Skris					bytes++;
63065668Skris				}
63165668Skris				/* Escaped characters fall through here */
63265668Skris				break;
63365668Skris			}
63465668Skris		} else {
63565668Skris			/*
63665668Skris			 * The previous character was not an escape char. Check if this
63765668Skris			 * is an escape.
63865668Skris			 */
63965668Skris			if (last_was_cr && ch == escape_char) {
64065668Skris				/* It is. Set the flag and continue to next character. */
64165668Skris				escape_pending = 1;
64265668Skris				continue;
64365668Skris			}
64465668Skris		}
64565668Skris
64665668Skris		/*
64765668Skris		 * Normal character.  Record whether it was a newline,
64865668Skris		 * and append it to the buffer.
64965668Skris		 */
65065668Skris		last_was_cr = (ch == '\r' || ch == '\n');
65165668Skris		buffer_put_char(bin, ch);
65265668Skris		bytes++;
65365668Skris	}
65465668Skris	return bytes;
65565668Skris}
65665668Skris
65760573Skrisvoid
65860573Skrisclient_process_input(fd_set * readset)
65960573Skris{
66065668Skris	int ret;
66160573Skris	int len;
66265668Skris	char buf[8192];
66360573Skris
66457429Smarkm	/* Read input from stdin. */
66557429Smarkm	if (FD_ISSET(fileno(stdin), readset)) {
66657429Smarkm		/* Read as much as possible. */
66757429Smarkm		len = read(fileno(stdin), buf, sizeof(buf));
66857429Smarkm		if (len <= 0) {
66957429Smarkm			/*
67057429Smarkm			 * Received EOF or error.  They are treated
67157429Smarkm			 * similarly, except that an error message is printed
67257429Smarkm			 * if it was an error condition.
67357429Smarkm			 */
67457429Smarkm			if (len < 0) {
67557429Smarkm				snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno));
67657429Smarkm				buffer_append(&stderr_buffer, buf, strlen(buf));
67757429Smarkm				stderr_bytes += strlen(buf);
67857429Smarkm			}
67957429Smarkm			/* Mark that we have seen EOF. */
68057429Smarkm			stdin_eof = 1;
68157429Smarkm			/*
68257429Smarkm			 * Send an EOF message to the server unless there is
68357429Smarkm			 * data in the buffer.  If there is data in the
68457429Smarkm			 * buffer, no message will be sent now.  Code
68557429Smarkm			 * elsewhere will send the EOF when the buffer
68657429Smarkm			 * becomes empty if stdin_eof is set.
68757429Smarkm			 */
68857429Smarkm			if (buffer_len(&stdin_buffer) == 0) {
68957429Smarkm				packet_start(SSH_CMSG_EOF);
69057429Smarkm				packet_send();
69157429Smarkm			}
69257429Smarkm		} else if (escape_char == -1) {
69357429Smarkm			/*
69457429Smarkm			 * Normal successful read, and no escape character.
69557429Smarkm			 * Just append the data to buffer.
69657429Smarkm			 */
69757429Smarkm			buffer_append(&stdin_buffer, buf, len);
69857429Smarkm			stdin_bytes += len;
69957429Smarkm		} else {
70057429Smarkm			/*
70157429Smarkm			 * Normal, successful read.  But we have an escape character
70257429Smarkm			 * and have to process the characters one by one.
70357429Smarkm			 */
70465668Skris			ret = process_escapes(&stdin_buffer, &stdout_buffer, &stderr_buffer, buf, len);
70565668Skris			if (ret == -1)
70665668Skris				return;
70765668Skris			stdout_bytes += ret;
70857429Smarkm		}
70957429Smarkm	}
71057429Smarkm}
71157429Smarkm
71260573Skrisvoid
71357429Smarkmclient_process_output(fd_set * writeset)
71457429Smarkm{
71557429Smarkm	int len;
71657429Smarkm	char buf[100];
71757429Smarkm
71857429Smarkm	/* Write buffered output to stdout. */
71957429Smarkm	if (FD_ISSET(fileno(stdout), writeset)) {
72057429Smarkm		/* Write as much data as possible. */
72157429Smarkm		len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
72257429Smarkm		    buffer_len(&stdout_buffer));
72357429Smarkm		if (len <= 0) {
72457429Smarkm			if (errno == EAGAIN)
72557429Smarkm				len = 0;
72657429Smarkm			else {
72757429Smarkm				/*
72857429Smarkm				 * An error or EOF was encountered.  Put an
72957429Smarkm				 * error message to stderr buffer.
73057429Smarkm				 */
73157429Smarkm				snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno));
73257429Smarkm				buffer_append(&stderr_buffer, buf, strlen(buf));
73357429Smarkm				stderr_bytes += strlen(buf);
73457429Smarkm				quit_pending = 1;
73557429Smarkm				return;
73657429Smarkm			}
73757429Smarkm		}
73857429Smarkm		/* Consume printed data from the buffer. */
73957429Smarkm		buffer_consume(&stdout_buffer, len);
74057429Smarkm	}
74157429Smarkm	/* Write buffered output to stderr. */
74257429Smarkm	if (FD_ISSET(fileno(stderr), writeset)) {
74357429Smarkm		/* Write as much data as possible. */
74457429Smarkm		len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
74557429Smarkm		    buffer_len(&stderr_buffer));
74657429Smarkm		if (len <= 0) {
74757429Smarkm			if (errno == EAGAIN)
74857429Smarkm				len = 0;
74957429Smarkm			else {
75057429Smarkm				/* EOF or error, but can't even print error message. */
75157429Smarkm				quit_pending = 1;
75257429Smarkm				return;
75357429Smarkm			}
75457429Smarkm		}
75557429Smarkm		/* Consume printed characters from the buffer. */
75657429Smarkm		buffer_consume(&stderr_buffer, len);
75757429Smarkm	}
75857429Smarkm}
75957429Smarkm
76057429Smarkm/*
76160573Skris * Get packets from the connection input buffer, and process them as long as
76260573Skris * there are packets available.
76360573Skris *
76460573Skris * Any unknown packets received during the actual
76560573Skris * session cause the session to terminate.  This is
76660573Skris * intended to make debugging easier since no
76760573Skris * confirmations are sent.  Any compatible protocol
76860573Skris * extensions must be negotiated during the
76960573Skris * preparatory phase.
77060573Skris */
77160573Skris
77260573Skrisvoid
77360573Skrisclient_process_buffered_input_packets()
77460573Skris{
77569587Sgreen	dispatch_run(DISPATCH_NONBLOCK, &quit_pending, NULL);
77660573Skris}
77760573Skris
77865668Skris/* scan buf[] for '~' before sending data to the peer */
77965668Skris
78065668Skrisint
78165668Skrissimple_escape_filter(Channel *c, char *buf, int len)
78265668Skris{
78365668Skris	/* XXX we assume c->extended is writeable */
78465668Skris	return process_escapes(&c->input, &c->output, &c->extended, buf, len);
78565668Skris}
78665668Skris
78760573Skris/*
78857429Smarkm * Implements the interactive session with the server.  This is called after
78957429Smarkm * the user has been authenticated, and a command has been started on the
79057429Smarkm * remote host.  If escape_char != -1, it is the character used as an escape
79157429Smarkm * character for terminating or suspending the session.
79257429Smarkm */
79357429Smarkm
79460573Skrisint
79565668Skrisclient_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
79657429Smarkm{
79757429Smarkm	double start_time, total_time;
79857429Smarkm	int len;
79957429Smarkm	char buf[100];
80057429Smarkm
80157429Smarkm	debug("Entering interactive session.");
80257429Smarkm
80357429Smarkm	start_time = get_current_time();
80457429Smarkm
80557429Smarkm	/* Initialize variables. */
80657429Smarkm	escape_pending = 0;
80757429Smarkm	last_was_cr = 1;
80857429Smarkm	exit_status = -1;
80957429Smarkm	stdin_eof = 0;
81057429Smarkm	buffer_high = 64 * 1024;
81157429Smarkm	connection_in = packet_get_connection_in();
81257429Smarkm	connection_out = packet_get_connection_out();
81357429Smarkm	max_fd = connection_in;
81457429Smarkm	if (connection_out > max_fd)
81557429Smarkm		max_fd = connection_out;
81657429Smarkm	stdin_bytes = 0;
81757429Smarkm	stdout_bytes = 0;
81857429Smarkm	stderr_bytes = 0;
81957429Smarkm	quit_pending = 0;
82057429Smarkm	escape_char = escape_char_arg;
82157429Smarkm
82257429Smarkm	/* Initialize buffers. */
82357429Smarkm	buffer_init(&stdin_buffer);
82457429Smarkm	buffer_init(&stdout_buffer);
82557429Smarkm	buffer_init(&stderr_buffer);
82657429Smarkm
82760573Skris	client_init_dispatch();
82860573Skris
82957429Smarkm	/* Set signal handlers to restore non-blocking mode.  */
83057429Smarkm	signal(SIGINT, signal_handler);
83157429Smarkm	signal(SIGQUIT, signal_handler);
83257429Smarkm	signal(SIGTERM, signal_handler);
83357429Smarkm	signal(SIGPIPE, SIG_IGN);
83457429Smarkm	if (have_pty)
83557429Smarkm		signal(SIGWINCH, window_change_handler);
83657429Smarkm
83757429Smarkm	if (have_pty)
83857429Smarkm		enter_raw_mode();
83957429Smarkm
84065668Skris	/* Check if we should immediately send eof on stdin. */
84160573Skris	if (!compat20)
84260573Skris		client_check_initial_eof_on_stdin();
84357429Smarkm
84465668Skris	if (compat20 && escape_char != -1)
84565668Skris		channel_register_filter(ssh2_chan_id, simple_escape_filter);
84665668Skris
84757429Smarkm	/* Main loop of the client for the interactive session mode. */
84857429Smarkm	while (!quit_pending) {
84957429Smarkm		fd_set readset, writeset;
85057429Smarkm
85157429Smarkm		/* Process buffered packets sent by the server. */
85257429Smarkm		client_process_buffered_input_packets();
85357429Smarkm
85460573Skris		if (compat20 && !channel_still_open()) {
85569587Sgreen			debug2("!channel_still_open.");
85660573Skris			break;
85760573Skris		}
85860573Skris
85957429Smarkm		/*
86057429Smarkm		 * Make packets of buffered stdin data, and buffer them for
86157429Smarkm		 * sending to the server.
86257429Smarkm		 */
86360573Skris		if (!compat20)
86460573Skris			client_make_packets_from_stdin_data();
86557429Smarkm
86657429Smarkm		/*
86757429Smarkm		 * Make packets from buffered channel data, and buffer them
86857429Smarkm		 * for sending to the server.
86957429Smarkm		 */
87057429Smarkm		if (packet_not_very_much_data_to_write())
87157429Smarkm			channel_output_poll();
87257429Smarkm
87357429Smarkm		/*
87457429Smarkm		 * Check if the window size has changed, and buffer a message
87557429Smarkm		 * about it to the server if so.
87657429Smarkm		 */
87757429Smarkm		client_check_window_change();
87857429Smarkm
87957429Smarkm		if (quit_pending)
88057429Smarkm			break;
88157429Smarkm
88257429Smarkm		/*
88357429Smarkm		 * Wait until we have something to do (something becomes
88457429Smarkm		 * available on one of the descriptors).
88557429Smarkm		 */
88657429Smarkm		client_wait_until_can_do_something(&readset, &writeset);
88757429Smarkm
88857429Smarkm		if (quit_pending)
88957429Smarkm			break;
89057429Smarkm
89157429Smarkm		/* Do channel operations. */
89257429Smarkm		channel_after_select(&readset, &writeset);
89357429Smarkm
89460573Skris		/* Buffer input from the connection.  */
89560573Skris		client_process_net_input(&readset);
89657429Smarkm
89760573Skris		if (quit_pending)
89860573Skris			break;
89957429Smarkm
90060573Skris		if (!compat20) {
90160573Skris			/* Buffer data from stdin */
90260573Skris			client_process_input(&readset);
90360573Skris			/*
90460573Skris			 * Process output to stdout and stderr.  Output to
90560573Skris			 * the connection is processed elsewhere (above).
90660573Skris			 */
90760573Skris			client_process_output(&writeset);
90860573Skris		}
90960573Skris
91057429Smarkm		/* Send as much buffered packet data as possible to the sender. */
91157429Smarkm		if (FD_ISSET(connection_out, &writeset))
91257429Smarkm			packet_write_poll();
91357429Smarkm	}
91457429Smarkm
91557429Smarkm	/* Terminate the session. */
91657429Smarkm
91757429Smarkm	/* Stop watching for window change. */
91857429Smarkm	if (have_pty)
91957429Smarkm		signal(SIGWINCH, SIG_DFL);
92057429Smarkm
92157429Smarkm	/* Stop listening for connections. */
92257429Smarkm	channel_stop_listening();
92357429Smarkm
92457429Smarkm	/*
92557429Smarkm	 * In interactive mode (with pseudo tty) display a message indicating
92657429Smarkm	 * that the connection has been closed.
92757429Smarkm	 */
92857429Smarkm	if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) {
92957429Smarkm		snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host);
93057429Smarkm		buffer_append(&stderr_buffer, buf, strlen(buf));
93157429Smarkm		stderr_bytes += strlen(buf);
93257429Smarkm	}
93357429Smarkm	/* Output any buffered data for stdout. */
93457429Smarkm	while (buffer_len(&stdout_buffer) > 0) {
93557429Smarkm		len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
93657429Smarkm		    buffer_len(&stdout_buffer));
93757429Smarkm		if (len <= 0) {
93857429Smarkm			error("Write failed flushing stdout buffer.");
93957429Smarkm			break;
94057429Smarkm		}
94157429Smarkm		buffer_consume(&stdout_buffer, len);
94257429Smarkm	}
94357429Smarkm
94457429Smarkm	/* Output any buffered data for stderr. */
94557429Smarkm	while (buffer_len(&stderr_buffer) > 0) {
94657429Smarkm		len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
94757429Smarkm		    buffer_len(&stderr_buffer));
94857429Smarkm		if (len <= 0) {
94957429Smarkm			error("Write failed flushing stderr buffer.");
95057429Smarkm			break;
95157429Smarkm		}
95257429Smarkm		buffer_consume(&stderr_buffer, len);
95357429Smarkm	}
95457429Smarkm
95557429Smarkm	if (have_pty)
95657429Smarkm		leave_raw_mode();
95757429Smarkm
95857429Smarkm	/* Clear and free any buffers. */
95957429Smarkm	memset(buf, 0, sizeof(buf));
96057429Smarkm	buffer_free(&stdin_buffer);
96157429Smarkm	buffer_free(&stdout_buffer);
96257429Smarkm	buffer_free(&stderr_buffer);
96357429Smarkm
96457429Smarkm	/* Report bytes transferred, and transfer rates. */
96557429Smarkm	total_time = get_current_time() - start_time;
96657429Smarkm	debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds",
96757429Smarkm	      stdin_bytes, stdout_bytes, stderr_bytes, total_time);
96857429Smarkm	if (total_time > 0)
96957429Smarkm		debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f",
97057429Smarkm		      stdin_bytes / total_time, stdout_bytes / total_time,
97157429Smarkm		      stderr_bytes / total_time);
97257429Smarkm
97357429Smarkm	/* Return the exit status of the program. */
97457429Smarkm	debug("Exit status %d", exit_status);
97557429Smarkm	return exit_status;
97657429Smarkm}
97760573Skris
97860573Skris/*********/
97960573Skris
98060573Skrisvoid
98169587Sgreenclient_input_stdout_data(int type, int plen, void *ctxt)
98260573Skris{
98360573Skris	unsigned int data_len;
98460573Skris	char *data = packet_get_string(&data_len);
98560573Skris	packet_integrity_check(plen, 4 + data_len, type);
98660573Skris	buffer_append(&stdout_buffer, data, data_len);
98760573Skris	stdout_bytes += data_len;
98860573Skris	memset(data, 0, data_len);
98960573Skris	xfree(data);
99060573Skris}
99160573Skrisvoid
99269587Sgreenclient_input_stderr_data(int type, int plen, void *ctxt)
99360573Skris{
99460573Skris	unsigned int data_len;
99560573Skris	char *data = packet_get_string(&data_len);
99660573Skris	packet_integrity_check(plen, 4 + data_len, type);
99760573Skris	buffer_append(&stderr_buffer, data, data_len);
99860573Skris	stdout_bytes += data_len;
99960573Skris	memset(data, 0, data_len);
100060573Skris	xfree(data);
100160573Skris}
100260573Skrisvoid
100369587Sgreenclient_input_exit_status(int type, int plen, void *ctxt)
100460573Skris{
100560573Skris	packet_integrity_check(plen, 4, type);
100660573Skris	exit_status = packet_get_int();
100760573Skris	/* Acknowledge the exit. */
100860573Skris	packet_start(SSH_CMSG_EXIT_CONFIRMATION);
100960573Skris	packet_send();
101060573Skris	/*
101160573Skris	 * Must wait for packet to be sent since we are
101260573Skris	 * exiting the loop.
101360573Skris	 */
101460573Skris	packet_write_wait();
101560573Skris	/* Flag that we want to exit. */
101660573Skris	quit_pending = 1;
101760573Skris}
101860573Skris
101960573Skris/* XXXX move to generic input handler */
102060573Skrisvoid
102169587Sgreenclient_input_channel_open(int type, int plen, void *ctxt)
102260573Skris{
102360573Skris	Channel *c = NULL;
102460573Skris	char *ctype;
102560573Skris	int id;
102660573Skris	unsigned int len;
102760573Skris	int rchan;
102860573Skris	int rmaxpack;
102960573Skris	int rwindow;
103060573Skris
103160573Skris	ctype = packet_get_string(&len);
103260573Skris	rchan = packet_get_int();
103360573Skris	rwindow = packet_get_int();
103460573Skris	rmaxpack = packet_get_int();
103560573Skris
103660573Skris	debug("client_input_channel_open: ctype %s rchan %d win %d max %d",
103760573Skris	    ctype, rchan, rwindow, rmaxpack);
103860573Skris
103968700Sgreen	if (strcmp(ctype, "x11") == 0 && options.forward_x11) {
104060573Skris		int sock;
104160573Skris		char *originator;
104260573Skris		int originator_port;
104360573Skris		originator = packet_get_string(NULL);
104460573Skris		if (datafellows & SSH_BUG_X11FWD) {
104569587Sgreen			debug2("buggy server: x11 request w/o originator_port");
104660573Skris			originator_port = 0;
104760573Skris		} else {
104860573Skris			originator_port = packet_get_int();
104960573Skris		}
105060573Skris		packet_done();
105160573Skris		/* XXX check permission */
105260573Skris		xfree(originator);
105360573Skris		/* XXX move to channels.c */
105460573Skris		sock = x11_connect_display();
105560573Skris		if (sock >= 0) {
105660573Skris			id = channel_new("x11", SSH_CHANNEL_X11_OPEN,
105765668Skris			    sock, sock, -1, CHAN_X11_WINDOW_DEFAULT,
105869587Sgreen			    CHAN_X11_PACKET_DEFAULT, 0, xstrdup("x11"), 1);
105960573Skris			c = channel_lookup(id);
106060573Skris		}
106160573Skris	}
106260573Skris/* XXX duplicate : */
106360573Skris	if (c != NULL) {
106460573Skris		debug("confirm %s", ctype);
106560573Skris		c->remote_id = rchan;
106660573Skris		c->remote_window = rwindow;
106760573Skris		c->remote_maxpacket = rmaxpack;
106860573Skris
106960573Skris		packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
107060573Skris		packet_put_int(c->remote_id);
107160573Skris		packet_put_int(c->self);
107260573Skris		packet_put_int(c->local_window);
107360573Skris		packet_put_int(c->local_maxpacket);
107460573Skris		packet_send();
107560573Skris	} else {
107660573Skris		debug("failure %s", ctype);
107760573Skris		packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
107860573Skris		packet_put_int(rchan);
107960573Skris		packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED);
108060573Skris		packet_put_cstring("bla bla");
108160573Skris		packet_put_cstring("");
108260573Skris		packet_send();
108360573Skris	}
108460573Skris	xfree(ctype);
108560573Skris}
108660573Skris
108760573Skrisvoid
108860573Skrisclient_init_dispatch_20()
108960573Skris{
109060573Skris	dispatch_init(&dispatch_protocol_error);
109160573Skris	dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose);
109260573Skris	dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data);
109360573Skris	dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof);
109460573Skris	dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data);
109560573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open);
109660573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
109760573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
109860573Skris	dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &channel_input_channel_request);
109960573Skris	dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust);
110060573Skris}
110160573Skrisvoid
110260573Skrisclient_init_dispatch_13()
110360573Skris{
110460573Skris	dispatch_init(NULL);
110560573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close);
110660573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation);
110760573Skris	dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data);
110860573Skris	dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
110960573Skris	dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
111060573Skris	dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open);
111160573Skris	dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status);
111260573Skris	dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data);
111360573Skris	dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data);
111468700Sgreen
111568700Sgreen	dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ?
111669587Sgreen	    &auth_input_open_request : &deny_input_open);
111768700Sgreen	dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ?
111869587Sgreen	    &x11_input_open : &deny_input_open);
111960573Skris}
112060573Skrisvoid
112160573Skrisclient_init_dispatch_15()
112260573Skris{
112360573Skris	client_init_dispatch_13();
112460573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof);
112560573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose);
112660573Skris}
112760573Skrisvoid
112860573Skrisclient_init_dispatch()
112960573Skris{
113060573Skris	if (compat20)
113160573Skris		client_init_dispatch_20();
113260573Skris	else if (compat13)
113360573Skris		client_init_dispatch_13();
113460573Skris	else
113560573Skris		client_init_dispatch_15();
113660573Skris}
113760573Skris
113860573Skrisvoid
113960573Skrisclient_input_channel_req(int id, void *arg)
114060573Skris{
114160573Skris	Channel *c = NULL;
114260573Skris	unsigned int len;
114360573Skris	int success = 0;
114460573Skris	int reply;
114560573Skris	char *rtype;
114660573Skris
114760573Skris	rtype = packet_get_string(&len);
114860573Skris	reply = packet_get_char();
114960573Skris
115060573Skris	debug("client_input_channel_req: rtype %s reply %d", rtype, reply);
115160573Skris
115260573Skris	c = channel_lookup(id);
115360573Skris	if (c == NULL)
115469587Sgreen		fatal("client_input_channel_req: channel %d: bad channel", id);
115560573Skris
115660573Skris	if (session_ident == -1) {
115760573Skris		error("client_input_channel_req: no channel %d", id);
115860573Skris	} else if (id != session_ident) {
115960573Skris		error("client_input_channel_req: bad channel %d != %d",
116060573Skris		    id, session_ident);
116160573Skris	} else if (strcmp(rtype, "exit-status") == 0) {
116260573Skris		success = 1;
116360573Skris		exit_status = packet_get_int();
116460573Skris		packet_done();
116560573Skris	}
116660573Skris	if (reply) {
116760573Skris		packet_start(success ?
116860573Skris		    SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
116960573Skris		packet_put_int(c->remote_id);
117060573Skris		packet_send();
117160573Skris	}
117260573Skris	xfree(rtype);
117360573Skris}
117460573Skris
117560573Skrisvoid
117660573Skrisclient_set_session_ident(int id)
117760573Skris{
117869587Sgreen	debug2("client_set_session_ident: id %d", id);
117960573Skris	session_ident = id;
118060573Skris	channel_register_callback(id, SSH2_MSG_CHANNEL_REQUEST,
118160573Skris	    client_input_channel_req, (void *)0);
118260573Skris}
1183