clientloop.c revision 126274
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.
3892555Sdes * Copyright (c) 1999, 2000, 2001 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"
62126274SdesRCSID("$OpenBSD: clientloop.c,v 1.117 2003/12/16 15:49:51 markus Exp $");
6357429Smarkm
6476259Sgreen#include "ssh.h"
6576259Sgreen#include "ssh1.h"
6676259Sgreen#include "ssh2.h"
6757429Smarkm#include "xmalloc.h"
6857429Smarkm#include "packet.h"
6957429Smarkm#include "buffer.h"
7060573Skris#include "compat.h"
7160573Skris#include "channels.h"
7260573Skris#include "dispatch.h"
7365668Skris#include "buffer.h"
7465668Skris#include "bufaux.h"
7576259Sgreen#include "key.h"
7676259Sgreen#include "kex.h"
7776259Sgreen#include "log.h"
7876259Sgreen#include "readconf.h"
7976259Sgreen#include "clientloop.h"
8076259Sgreen#include "authfd.h"
8176259Sgreen#include "atomicio.h"
8276259Sgreen#include "sshtty.h"
8376259Sgreen#include "misc.h"
8498675Sdes#include "readpass.h"
8560573Skris
8669587Sgreen/* import options */
8768700Sgreenextern Options options;
8868700Sgreen
8957429Smarkm/* Flag indicating that stdin should be redirected from /dev/null. */
9057429Smarkmextern int stdin_null_flag;
9157429Smarkm
92126274Sdes/* Flag indicating that no shell has been requested */
93126274Sdesextern int no_shell_flag;
94126274Sdes
9557429Smarkm/*
9657429Smarkm * Name of the host we are connecting to.  This is the name given on the
9757429Smarkm * command line, or the HostName specified for the user-supplied name in a
9857429Smarkm * configuration file.
9957429Smarkm */
10057429Smarkmextern char *host;
10157429Smarkm
10257429Smarkm/*
10357429Smarkm * Flag to indicate that we have received a window change signal which has
10457429Smarkm * not yet been processed.  This will cause a message indicating the new
10557429Smarkm * window size to be sent to the server a little later.  This is volatile
10657429Smarkm * because this is updated in a signal handler.
10757429Smarkm */
10892555Sdesstatic volatile sig_atomic_t received_window_change_signal = 0;
10992555Sdesstatic volatile sig_atomic_t received_signal = 0;
11057429Smarkm
11157429Smarkm/* Flag indicating whether the user\'s terminal is in non-blocking mode. */
11257429Smarkmstatic int in_non_blocking_mode = 0;
11357429Smarkm
11457429Smarkm/* Common data for the client loop code. */
11565668Skrisstatic int quit_pending;	/* Set to non-zero to quit the client loop. */
11665668Skrisstatic int escape_char;		/* Escape character. */
11757429Smarkmstatic int escape_pending;	/* Last character was the escape character */
11857429Smarkmstatic int last_was_cr;		/* Last character was a newline. */
11957429Smarkmstatic int exit_status;		/* Used to store the exit status of the command. */
12057429Smarkmstatic int stdin_eof;		/* EOF has been encountered on standard error. */
12157429Smarkmstatic Buffer stdin_buffer;	/* Buffer for stdin data. */
12257429Smarkmstatic Buffer stdout_buffer;	/* Buffer for stdout data. */
12357429Smarkmstatic Buffer stderr_buffer;	/* Buffer for stderr data. */
12476259Sgreenstatic u_long stdin_bytes, stdout_bytes, stderr_bytes;
12576259Sgreenstatic u_int buffer_high;/* Soft max buffer size. */
12657429Smarkmstatic int connection_in;	/* Connection to server (input). */
12757429Smarkmstatic int connection_out;	/* Connection to server (output). */
12876259Sgreenstatic int need_rekeying;	/* Set to non-zero if rekeying is requested. */
12976259Sgreenstatic int session_closed = 0;	/* In SSH2: login session closed. */
130126274Sdesstatic int server_alive_timeouts = 0;
13157429Smarkm
13292555Sdesstatic void client_init_dispatch(void);
13360573Skrisint	session_ident = -1;
13460573Skris
13576259Sgreen/*XXX*/
13676259Sgreenextern Kex *xxx_kex;
13757429Smarkm
13857429Smarkm/* Restores stdin to blocking mode. */
13957429Smarkm
14092555Sdesstatic void
14176259Sgreenleave_non_blocking(void)
14257429Smarkm{
14357429Smarkm	if (in_non_blocking_mode) {
14457429Smarkm		(void) fcntl(fileno(stdin), F_SETFL, 0);
14557429Smarkm		in_non_blocking_mode = 0;
14657429Smarkm	}
14757429Smarkm}
14857429Smarkm
14957429Smarkm/* Puts stdin terminal in non-blocking mode. */
15057429Smarkm
15192555Sdesstatic void
15276259Sgreenenter_non_blocking(void)
15357429Smarkm{
15457429Smarkm	in_non_blocking_mode = 1;
15557429Smarkm	(void) fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
15657429Smarkm}
15757429Smarkm
15857429Smarkm/*
15957429Smarkm * Signal handler for the window change signal (SIGWINCH).  This just sets a
16057429Smarkm * flag indicating that the window has changed.
16157429Smarkm */
16257429Smarkm
16392555Sdesstatic void
16457429Smarkmwindow_change_handler(int sig)
16557429Smarkm{
16657429Smarkm	received_window_change_signal = 1;
16757429Smarkm	signal(SIGWINCH, window_change_handler);
16857429Smarkm}
16957429Smarkm
17057429Smarkm/*
17157429Smarkm * Signal handler for signals that cause the program to terminate.  These
17257429Smarkm * signals must be trapped to restore terminal modes.
17357429Smarkm */
17457429Smarkm
17592555Sdesstatic void
17657429Smarkmsignal_handler(int sig)
17757429Smarkm{
17892555Sdes	received_signal = sig;
17992555Sdes	quit_pending = 1;
18057429Smarkm}
18157429Smarkm
18257429Smarkm/*
18357429Smarkm * Returns current time in seconds from Jan 1, 1970 with the maximum
18457429Smarkm * available resolution.
18557429Smarkm */
18657429Smarkm
18792555Sdesstatic double
18876259Sgreenget_current_time(void)
18957429Smarkm{
19057429Smarkm	struct timeval tv;
19157429Smarkm	gettimeofday(&tv, NULL);
19257429Smarkm	return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
19357429Smarkm}
19457429Smarkm
19557429Smarkm/*
19657429Smarkm * This is called when the interactive is entered.  This checks if there is
19757429Smarkm * an EOF coming on stdin.  We must check this explicitly, as select() does
19857429Smarkm * not appear to wake up when redirecting from /dev/null.
19957429Smarkm */
20057429Smarkm
20192555Sdesstatic void
20276259Sgreenclient_check_initial_eof_on_stdin(void)
20357429Smarkm{
20457429Smarkm	int len;
20557429Smarkm	char buf[1];
20657429Smarkm
20757429Smarkm	/*
20857429Smarkm	 * If standard input is to be "redirected from /dev/null", we simply
20957429Smarkm	 * mark that we have seen an EOF and send an EOF message to the
21057429Smarkm	 * server. Otherwise, we try to read a single character; it appears
21157429Smarkm	 * that for some files, such /dev/null, select() never wakes up for
21257429Smarkm	 * read for this descriptor, which means that we never get EOF.  This
21357429Smarkm	 * way we will get the EOF if stdin comes from /dev/null or similar.
21457429Smarkm	 */
21557429Smarkm	if (stdin_null_flag) {
21657429Smarkm		/* Fake EOF on stdin. */
21757429Smarkm		debug("Sending eof.");
21857429Smarkm		stdin_eof = 1;
21957429Smarkm		packet_start(SSH_CMSG_EOF);
22057429Smarkm		packet_send();
22157429Smarkm	} else {
22257429Smarkm		enter_non_blocking();
22357429Smarkm
22457429Smarkm		/* Check for immediate EOF on stdin. */
22557429Smarkm		len = read(fileno(stdin), buf, 1);
22657429Smarkm		if (len == 0) {
22757429Smarkm			/* EOF.  Record that we have seen it and send EOF to server. */
22857429Smarkm			debug("Sending eof.");
22957429Smarkm			stdin_eof = 1;
23057429Smarkm			packet_start(SSH_CMSG_EOF);
23157429Smarkm			packet_send();
23257429Smarkm		} else if (len > 0) {
23357429Smarkm			/*
23457429Smarkm			 * Got data.  We must store the data in the buffer,
23557429Smarkm			 * and also process it as an escape character if
23657429Smarkm			 * appropriate.
23757429Smarkm			 */
23876259Sgreen			if ((u_char) buf[0] == escape_char)
23957429Smarkm				escape_pending = 1;
24076259Sgreen			else
24157429Smarkm				buffer_append(&stdin_buffer, buf, 1);
24257429Smarkm		}
24357429Smarkm		leave_non_blocking();
24457429Smarkm	}
24557429Smarkm}
24657429Smarkm
24757429Smarkm
24857429Smarkm/*
24957429Smarkm * Make packets from buffered stdin data, and buffer them for sending to the
25057429Smarkm * connection.
25157429Smarkm */
25257429Smarkm
25392555Sdesstatic void
25476259Sgreenclient_make_packets_from_stdin_data(void)
25557429Smarkm{
25676259Sgreen	u_int len;
25757429Smarkm
25857429Smarkm	/* Send buffered stdin data to the server. */
25957429Smarkm	while (buffer_len(&stdin_buffer) > 0 &&
26092555Sdes	    packet_not_very_much_data_to_write()) {
26157429Smarkm		len = buffer_len(&stdin_buffer);
26257429Smarkm		/* Keep the packets at reasonable size. */
26357429Smarkm		if (len > packet_get_maxsize())
26457429Smarkm			len = packet_get_maxsize();
26557429Smarkm		packet_start(SSH_CMSG_STDIN_DATA);
26657429Smarkm		packet_put_string(buffer_ptr(&stdin_buffer), len);
26757429Smarkm		packet_send();
26857429Smarkm		buffer_consume(&stdin_buffer, len);
26976259Sgreen		stdin_bytes += len;
27057429Smarkm		/* If we have a pending EOF, send it now. */
27157429Smarkm		if (stdin_eof && buffer_len(&stdin_buffer) == 0) {
27257429Smarkm			packet_start(SSH_CMSG_EOF);
27357429Smarkm			packet_send();
27457429Smarkm		}
27557429Smarkm	}
27657429Smarkm}
27757429Smarkm
27857429Smarkm/*
27957429Smarkm * Checks if the client window has changed, and sends a packet about it to
28057429Smarkm * the server if so.  The actual change is detected elsewhere (by a software
28157429Smarkm * interrupt on Unix); this just checks the flag and sends a message if
28257429Smarkm * appropriate.
28357429Smarkm */
28457429Smarkm
28592555Sdesstatic void
28676259Sgreenclient_check_window_change(void)
28757429Smarkm{
28860573Skris	struct winsize ws;
28957429Smarkm
29060573Skris	if (! received_window_change_signal)
29160573Skris		return;
29260573Skris	/** XXX race */
29360573Skris	received_window_change_signal = 0;
29457429Smarkm
29560573Skris	if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
29660573Skris		return;
29760573Skris
29869587Sgreen	debug2("client_check_window_change: changed");
29960573Skris
30060573Skris	if (compat20) {
30160573Skris		channel_request_start(session_ident, "window-change", 0);
30260573Skris		packet_put_int(ws.ws_col);
30360573Skris		packet_put_int(ws.ws_row);
30460573Skris		packet_put_int(ws.ws_xpixel);
30560573Skris		packet_put_int(ws.ws_ypixel);
30660573Skris		packet_send();
30760573Skris	} else {
30860573Skris		packet_start(SSH_CMSG_WINDOW_SIZE);
30960573Skris		packet_put_int(ws.ws_row);
31060573Skris		packet_put_int(ws.ws_col);
31160573Skris		packet_put_int(ws.ws_xpixel);
31260573Skris		packet_put_int(ws.ws_ypixel);
31360573Skris		packet_send();
31457429Smarkm	}
31557429Smarkm}
31657429Smarkm
317126274Sdesstatic void
318126274Sdesclient_global_request_reply(int type, u_int32_t seq, void *ctxt)
319126274Sdes{
320126274Sdes	server_alive_timeouts = 0;
321126274Sdes	client_global_request_reply_fwd(type, seq, ctxt);
322126274Sdes}
323126274Sdes
324126274Sdesstatic void
325126274Sdesserver_alive_check(void)
326126274Sdes{
327126274Sdes	if (++server_alive_timeouts > options.server_alive_count_max)
328126274Sdes		packet_disconnect("Timeout, server not responding.");
329126274Sdes	packet_start(SSH2_MSG_GLOBAL_REQUEST);
330126274Sdes	packet_put_cstring("keepalive@openssh.com");
331126274Sdes	packet_put_char(1);     /* boolean: want reply */
332126274Sdes	packet_send();
333126274Sdes}
334126274Sdes
33557429Smarkm/*
33657429Smarkm * Waits until the client can do something (some data becomes available on
33757429Smarkm * one of the file descriptors).
33857429Smarkm */
33957429Smarkm
34092555Sdesstatic void
34176259Sgreenclient_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
34292555Sdes    int *maxfdp, int *nallocp, int rekeying)
34357429Smarkm{
344126274Sdes	struct timeval tv, *tvp;
345126274Sdes	int ret;
346126274Sdes
34776259Sgreen	/* Add any selections by the channel mechanism. */
34892555Sdes	channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying);
34957429Smarkm
35060573Skris	if (!compat20) {
35160573Skris		/* Read from the connection, unless our buffers are full. */
35260573Skris		if (buffer_len(&stdout_buffer) < buffer_high &&
35360573Skris		    buffer_len(&stderr_buffer) < buffer_high &&
35460573Skris		    channel_not_very_much_buffered_data())
35576259Sgreen			FD_SET(connection_in, *readsetp);
35660573Skris		/*
35760573Skris		 * Read from stdin, unless we have seen EOF or have very much
35860573Skris		 * buffered data to send to the server.
35960573Skris		 */
36060573Skris		if (!stdin_eof && packet_not_very_much_data_to_write())
36176259Sgreen			FD_SET(fileno(stdin), *readsetp);
36260573Skris
36360573Skris		/* Select stdout/stderr if have data in buffer. */
36460573Skris		if (buffer_len(&stdout_buffer) > 0)
36576259Sgreen			FD_SET(fileno(stdout), *writesetp);
36660573Skris		if (buffer_len(&stderr_buffer) > 0)
36776259Sgreen			FD_SET(fileno(stderr), *writesetp);
36860573Skris	} else {
36992555Sdes		/* channel_prepare_select could have closed the last channel */
37092555Sdes		if (session_closed && !channel_still_open() &&
37192555Sdes		    !packet_have_data_to_write()) {
37292555Sdes			/* clear mask since we did not call select() */
37392555Sdes			memset(*readsetp, 0, *nallocp);
37492555Sdes			memset(*writesetp, 0, *nallocp);
37592555Sdes			return;
37692555Sdes		} else {
37792555Sdes			FD_SET(connection_in, *readsetp);
37892555Sdes		}
37960573Skris	}
38057429Smarkm
38157429Smarkm	/* Select server connection if have data to write to the server. */
38257429Smarkm	if (packet_have_data_to_write())
38376259Sgreen		FD_SET(connection_out, *writesetp);
38457429Smarkm
38557429Smarkm	/*
38657429Smarkm	 * Wait for something to happen.  This will suspend the process until
38757429Smarkm	 * some selected descriptor can be read, written, or has some other
388126274Sdes	 * event pending.
38957429Smarkm	 */
39057429Smarkm
391126274Sdes	if (options.server_alive_interval == 0 || !compat20)
392126274Sdes		tvp = NULL;
393126274Sdes	else {
394126274Sdes		tv.tv_sec = options.server_alive_interval;
395126274Sdes		tv.tv_usec = 0;
396126274Sdes		tvp = &tv;
397126274Sdes	}
398126274Sdes	ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
399126274Sdes	if (ret < 0) {
40057429Smarkm		char buf[100];
40176259Sgreen
40276259Sgreen		/*
40376259Sgreen		 * We have to clear the select masks, because we return.
40476259Sgreen		 * We have to return, because the mainloop checks for the flags
40576259Sgreen		 * set by the signal handlers.
40676259Sgreen		 */
40792555Sdes		memset(*readsetp, 0, *nallocp);
40892555Sdes		memset(*writesetp, 0, *nallocp);
40976259Sgreen
41057429Smarkm		if (errno == EINTR)
41157429Smarkm			return;
41257429Smarkm		/* Note: we might still have data in the buffers. */
41357429Smarkm		snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
41457429Smarkm		buffer_append(&stderr_buffer, buf, strlen(buf));
41557429Smarkm		quit_pending = 1;
416126274Sdes	} else if (ret == 0)
417126274Sdes		server_alive_check();
41857429Smarkm}
41957429Smarkm
42092555Sdesstatic void
42165668Skrisclient_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr)
42257429Smarkm{
42357429Smarkm	struct winsize oldws, newws;
42457429Smarkm
42557429Smarkm	/* Flush stdout and stderr buffers. */
42665668Skris	if (buffer_len(bout) > 0)
427124208Sdes		atomicio(vwrite, fileno(stdout), buffer_ptr(bout), buffer_len(bout));
42865668Skris	if (buffer_len(berr) > 0)
429124208Sdes		atomicio(vwrite, fileno(stderr), buffer_ptr(berr), buffer_len(berr));
43057429Smarkm
43157429Smarkm	leave_raw_mode();
43257429Smarkm
43357429Smarkm	/*
43457429Smarkm	 * Free (and clear) the buffer to reduce the amount of data that gets
43557429Smarkm	 * written to swap.
43657429Smarkm	 */
43765668Skris	buffer_free(bin);
43865668Skris	buffer_free(bout);
43965668Skris	buffer_free(berr);
44057429Smarkm
44157429Smarkm	/* Save old window size. */
44257429Smarkm	ioctl(fileno(stdin), TIOCGWINSZ, &oldws);
44357429Smarkm
44457429Smarkm	/* Send the suspend signal to the program itself. */
44557429Smarkm	kill(getpid(), SIGTSTP);
44657429Smarkm
44757429Smarkm	/* Check if the window size has changed. */
44857429Smarkm	if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 &&
44957429Smarkm	    (oldws.ws_row != newws.ws_row ||
45092555Sdes	    oldws.ws_col != newws.ws_col ||
45192555Sdes	    oldws.ws_xpixel != newws.ws_xpixel ||
45292555Sdes	    oldws.ws_ypixel != newws.ws_ypixel))
45357429Smarkm		received_window_change_signal = 1;
45457429Smarkm
45557429Smarkm	/* OK, we have been continued by the user. Reinitialize buffers. */
45665668Skris	buffer_init(bin);
45765668Skris	buffer_init(bout);
45865668Skris	buffer_init(berr);
45957429Smarkm
46057429Smarkm	enter_raw_mode();
46157429Smarkm}
46257429Smarkm
46392555Sdesstatic void
46460573Skrisclient_process_net_input(fd_set * readset)
46557429Smarkm{
46660573Skris	int len;
46760573Skris	char buf[8192];
46857429Smarkm
46957429Smarkm	/*
47057429Smarkm	 * Read input from the server, and add any such data to the buffer of
47157429Smarkm	 * the packet subsystem.
47257429Smarkm	 */
47357429Smarkm	if (FD_ISSET(connection_in, readset)) {
47457429Smarkm		/* Read as much as possible. */
47557429Smarkm		len = read(connection_in, buf, sizeof(buf));
47657429Smarkm		if (len == 0) {
47757429Smarkm			/* Received EOF.  The remote host has closed the connection. */
47857429Smarkm			snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n",
47957429Smarkm				 host);
48057429Smarkm			buffer_append(&stderr_buffer, buf, strlen(buf));
48157429Smarkm			quit_pending = 1;
48257429Smarkm			return;
48357429Smarkm		}
48457429Smarkm		/*
48557429Smarkm		 * There is a kernel bug on Solaris that causes select to
48657429Smarkm		 * sometimes wake up even though there is no data available.
48757429Smarkm		 */
48876259Sgreen		if (len < 0 && (errno == EAGAIN || errno == EINTR))
48957429Smarkm			len = 0;
49057429Smarkm
49157429Smarkm		if (len < 0) {
49257429Smarkm			/* An error has encountered.  Perhaps there is a network problem. */
49357429Smarkm			snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n",
49457429Smarkm				 host, strerror(errno));
49557429Smarkm			buffer_append(&stderr_buffer, buf, strlen(buf));
49657429Smarkm			quit_pending = 1;
49757429Smarkm			return;
49857429Smarkm		}
49957429Smarkm		packet_process_incoming(buf, len);
50057429Smarkm	}
50160573Skris}
50260573Skris
50398675Sdesstatic void
50498675Sdesprocess_cmdline(void)
50598675Sdes{
50698675Sdes	void (*handler)(int);
50798675Sdes	char *s, *cmd;
50898675Sdes	u_short fwd_port, fwd_host_port;
50998675Sdes	char buf[1024], sfwd_port[6], sfwd_host_port[6];
51098675Sdes	int local = 0;
51198675Sdes
51298675Sdes	leave_raw_mode();
51398675Sdes	handler = signal(SIGINT, SIG_IGN);
51498675Sdes	cmd = s = read_passphrase("\r\nssh> ", RP_ECHO);
51598675Sdes	if (s == NULL)
51698675Sdes		goto out;
51798675Sdes	while (*s && isspace(*s))
51898675Sdes		s++;
51998675Sdes	if (*s == 0)
52098675Sdes		goto out;
52198675Sdes	if (strlen(s) < 2 || s[0] != '-' || !(s[1] == 'L' || s[1] == 'R')) {
522124208Sdes		logit("Invalid command.");
52398675Sdes		goto out;
52498675Sdes	}
52598675Sdes	if (s[1] == 'L')
52698675Sdes		local = 1;
52798675Sdes	if (!local && !compat20) {
528124208Sdes		logit("Not supported for SSH protocol version 1.");
52998675Sdes		goto out;
53098675Sdes	}
53198675Sdes	s += 2;
53298675Sdes	while (*s && isspace(*s))
53398675Sdes		s++;
53498675Sdes
53598675Sdes	if (sscanf(s, "%5[0-9]:%255[^:]:%5[0-9]",
53698675Sdes	    sfwd_port, buf, sfwd_host_port) != 3 &&
53798675Sdes	    sscanf(s, "%5[0-9]/%255[^/]/%5[0-9]",
53898675Sdes	    sfwd_port, buf, sfwd_host_port) != 3) {
539124208Sdes		logit("Bad forwarding specification.");
54098675Sdes		goto out;
54198675Sdes	}
54298675Sdes	if ((fwd_port = a2port(sfwd_port)) == 0 ||
54398675Sdes	    (fwd_host_port = a2port(sfwd_host_port)) == 0) {
544124208Sdes		logit("Bad forwarding port(s).");
54598675Sdes		goto out;
54698675Sdes	}
54798675Sdes	if (local) {
54898675Sdes		if (channel_setup_local_fwd_listener(fwd_port, buf,
54998675Sdes		    fwd_host_port, options.gateway_ports) < 0) {
550124208Sdes			logit("Port forwarding failed.");
55198675Sdes			goto out;
55298675Sdes		}
55398675Sdes	} else
55498675Sdes		channel_request_remote_forwarding(fwd_port, buf,
55598675Sdes		    fwd_host_port);
556124208Sdes	logit("Forwarding port.");
55798675Sdesout:
55898675Sdes	signal(SIGINT, handler);
55998675Sdes	enter_raw_mode();
56098675Sdes	if (cmd)
56198675Sdes		xfree(cmd);
56298675Sdes}
56398675Sdes
56465668Skris/* process the characters one by one */
56592555Sdesstatic int
56665668Skrisprocess_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len)
56765668Skris{
56865668Skris	char string[1024];
56965668Skris	pid_t pid;
57065668Skris	int bytes = 0;
57176259Sgreen	u_int i;
57276259Sgreen	u_char ch;
57365668Skris	char *s;
57465668Skris
57565668Skris	for (i = 0; i < len; i++) {
57665668Skris		/* Get one character at a time. */
57765668Skris		ch = buf[i];
57865668Skris
57965668Skris		if (escape_pending) {
58065668Skris			/* We have previously seen an escape character. */
58165668Skris			/* Clear the flag now. */
58265668Skris			escape_pending = 0;
58365668Skris
58465668Skris			/* Process the escaped character. */
58565668Skris			switch (ch) {
58665668Skris			case '.':
58765668Skris				/* Terminate the connection. */
58865668Skris				snprintf(string, sizeof string, "%c.\r\n", escape_char);
58965668Skris				buffer_append(berr, string, strlen(string));
59065668Skris
59165668Skris				quit_pending = 1;
59265668Skris				return -1;
59365668Skris
59465668Skris			case 'Z' - 64:
59565668Skris				/* Suspend the program. */
59665668Skris				/* Print a message to that effect to the user. */
59765668Skris				snprintf(string, sizeof string, "%c^Z [suspend ssh]\r\n", escape_char);
59865668Skris				buffer_append(berr, string, strlen(string));
59965668Skris
60065668Skris				/* Restore terminal modes and suspend. */
60165668Skris				client_suspend_self(bin, bout, berr);
60265668Skris
60365668Skris				/* We have been continued. */
60465668Skris				continue;
60565668Skris
606124208Sdes			case 'B':
607124208Sdes				if (compat20) {
608124208Sdes					snprintf(string, sizeof string,
609124208Sdes					    "%cB\r\n", escape_char);
610124208Sdes					buffer_append(berr, string,
611124208Sdes					    strlen(string));
612124208Sdes					channel_request_start(session_ident,
613124208Sdes					    "break", 0);
614124208Sdes					packet_put_int(1000);
615124208Sdes					packet_send();
616124208Sdes				}
617124208Sdes				continue;
618124208Sdes
61976259Sgreen			case 'R':
62076259Sgreen				if (compat20) {
62176259Sgreen					if (datafellows & SSH_BUG_NOREKEY)
622124208Sdes						logit("Server does not support re-keying");
62376259Sgreen					else
62476259Sgreen						need_rekeying = 1;
62576259Sgreen				}
62676259Sgreen				continue;
62776259Sgreen
62865668Skris			case '&':
62965668Skris				/*
63065668Skris				 * Detach the program (continue to serve connections,
63165668Skris				 * but put in background and no more new connections).
63265668Skris				 */
63365668Skris				/* Restore tty modes. */
63465668Skris				leave_raw_mode();
63565668Skris
63665668Skris				/* Stop listening for new connections. */
63765668Skris				channel_stop_listening();
63865668Skris
63992555Sdes				snprintf(string, sizeof string,
64092555Sdes				    "%c& [backgrounded]\n", escape_char);
64192555Sdes				buffer_append(berr, string, strlen(string));
64265668Skris
64365668Skris				/* Fork into background. */
64465668Skris				pid = fork();
64565668Skris				if (pid < 0) {
64665668Skris					error("fork: %.100s", strerror(errno));
64765668Skris					continue;
64865668Skris				}
64965668Skris				if (pid != 0) {	/* This is the parent. */
65065668Skris					/* The parent just exits. */
65165668Skris					exit(0);
65265668Skris				}
65365668Skris				/* The child continues serving connections. */
65492555Sdes				if (compat20) {
65592555Sdes					buffer_append(bin, "\004", 1);
65692555Sdes					/* fake EOF on stdin */
65792555Sdes					return -1;
65892555Sdes				} else if (!stdin_eof) {
65992555Sdes					/*
66092555Sdes					 * Sending SSH_CMSG_EOF alone does not always appear
66192555Sdes					 * to be enough.  So we try to send an EOF character
66292555Sdes					 * first.
66392555Sdes					 */
66492555Sdes					packet_start(SSH_CMSG_STDIN_DATA);
66592555Sdes					packet_put_string("\004", 1);
66692555Sdes					packet_send();
66792555Sdes					/* Close stdin. */
66892555Sdes					stdin_eof = 1;
66992555Sdes					if (buffer_len(bin) == 0) {
67092555Sdes						packet_start(SSH_CMSG_EOF);
67192555Sdes						packet_send();
67292555Sdes					}
67392555Sdes				}
67492555Sdes				continue;
67565668Skris
67665668Skris			case '?':
67765668Skris				snprintf(string, sizeof string,
67865668Skris"%c?\r\n\
67965668SkrisSupported escape sequences:\r\n\
680106121Sdes%c.  - terminate connection\r\n\
681124208Sdes%cB  - send a BREAK to the remote system\r\n\
682106121Sdes%cC  - open a command line\r\n\
683106121Sdes%cR  - Request rekey (SSH protocol 2 only)\r\n\
684106121Sdes%c^Z - suspend ssh\r\n\
685106121Sdes%c#  - list forwarded connections\r\n\
686106121Sdes%c&  - background ssh (when waiting for connections to terminate)\r\n\
687106121Sdes%c?  - this message\r\n\
688106121Sdes%c%c  - send the escape character by typing it twice\r\n\
68965668Skris(Note that escapes are only recognized immediately after newline.)\r\n",
690106121Sdes				    escape_char, escape_char, escape_char, escape_char,
691106121Sdes				    escape_char, escape_char, escape_char, escape_char,
692124208Sdes				    escape_char, escape_char, escape_char);
69365668Skris				buffer_append(berr, string, strlen(string));
69465668Skris				continue;
69565668Skris
69665668Skris			case '#':
69765668Skris				snprintf(string, sizeof string, "%c#\r\n", escape_char);
69865668Skris				buffer_append(berr, string, strlen(string));
69965668Skris				s = channel_open_message();
70065668Skris				buffer_append(berr, s, strlen(s));
70165668Skris				xfree(s);
70265668Skris				continue;
70365668Skris
70498675Sdes			case 'C':
70598675Sdes				process_cmdline();
70698675Sdes				continue;
70798675Sdes
70865668Skris			default:
70965668Skris				if (ch != escape_char) {
71065668Skris					buffer_put_char(bin, escape_char);
71165668Skris					bytes++;
71265668Skris				}
71365668Skris				/* Escaped characters fall through here */
71465668Skris				break;
71565668Skris			}
71665668Skris		} else {
71765668Skris			/*
71865668Skris			 * The previous character was not an escape char. Check if this
71965668Skris			 * is an escape.
72065668Skris			 */
72165668Skris			if (last_was_cr && ch == escape_char) {
72265668Skris				/* It is. Set the flag and continue to next character. */
72365668Skris				escape_pending = 1;
72465668Skris				continue;
72565668Skris			}
72665668Skris		}
72765668Skris
72865668Skris		/*
72965668Skris		 * Normal character.  Record whether it was a newline,
73065668Skris		 * and append it to the buffer.
73165668Skris		 */
73265668Skris		last_was_cr = (ch == '\r' || ch == '\n');
73365668Skris		buffer_put_char(bin, ch);
73465668Skris		bytes++;
73565668Skris	}
73665668Skris	return bytes;
73765668Skris}
73865668Skris
73992555Sdesstatic void
74060573Skrisclient_process_input(fd_set * readset)
74160573Skris{
74260573Skris	int len;
74365668Skris	char buf[8192];
74460573Skris
74557429Smarkm	/* Read input from stdin. */
74657429Smarkm	if (FD_ISSET(fileno(stdin), readset)) {
74757429Smarkm		/* Read as much as possible. */
74857429Smarkm		len = read(fileno(stdin), buf, sizeof(buf));
74976259Sgreen		if (len < 0 && (errno == EAGAIN || errno == EINTR))
75076259Sgreen			return;		/* we'll try again later */
75157429Smarkm		if (len <= 0) {
75257429Smarkm			/*
75357429Smarkm			 * Received EOF or error.  They are treated
75457429Smarkm			 * similarly, except that an error message is printed
75557429Smarkm			 * if it was an error condition.
75657429Smarkm			 */
75757429Smarkm			if (len < 0) {
75857429Smarkm				snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno));
75957429Smarkm				buffer_append(&stderr_buffer, buf, strlen(buf));
76057429Smarkm			}
76157429Smarkm			/* Mark that we have seen EOF. */
76257429Smarkm			stdin_eof = 1;
76357429Smarkm			/*
76457429Smarkm			 * Send an EOF message to the server unless there is
76557429Smarkm			 * data in the buffer.  If there is data in the
76657429Smarkm			 * buffer, no message will be sent now.  Code
76757429Smarkm			 * elsewhere will send the EOF when the buffer
76857429Smarkm			 * becomes empty if stdin_eof is set.
76957429Smarkm			 */
77057429Smarkm			if (buffer_len(&stdin_buffer) == 0) {
77157429Smarkm				packet_start(SSH_CMSG_EOF);
77257429Smarkm				packet_send();
77357429Smarkm			}
77492555Sdes		} else if (escape_char == SSH_ESCAPECHAR_NONE) {
77557429Smarkm			/*
77657429Smarkm			 * Normal successful read, and no escape character.
77757429Smarkm			 * Just append the data to buffer.
77857429Smarkm			 */
77957429Smarkm			buffer_append(&stdin_buffer, buf, len);
78057429Smarkm		} else {
78157429Smarkm			/*
78257429Smarkm			 * Normal, successful read.  But we have an escape character
78357429Smarkm			 * and have to process the characters one by one.
78457429Smarkm			 */
78576259Sgreen			if (process_escapes(&stdin_buffer, &stdout_buffer,
78676259Sgreen			    &stderr_buffer, buf, len) == -1)
78765668Skris				return;
78857429Smarkm		}
78957429Smarkm	}
79057429Smarkm}
79157429Smarkm
79292555Sdesstatic void
79357429Smarkmclient_process_output(fd_set * writeset)
79457429Smarkm{
79557429Smarkm	int len;
79657429Smarkm	char buf[100];
79757429Smarkm
79857429Smarkm	/* Write buffered output to stdout. */
79957429Smarkm	if (FD_ISSET(fileno(stdout), writeset)) {
80057429Smarkm		/* Write as much data as possible. */
80157429Smarkm		len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
80257429Smarkm		    buffer_len(&stdout_buffer));
80357429Smarkm		if (len <= 0) {
80476259Sgreen			if (errno == EINTR || errno == EAGAIN)
80557429Smarkm				len = 0;
80657429Smarkm			else {
80757429Smarkm				/*
80857429Smarkm				 * An error or EOF was encountered.  Put an
80957429Smarkm				 * error message to stderr buffer.
81057429Smarkm				 */
81157429Smarkm				snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno));
81257429Smarkm				buffer_append(&stderr_buffer, buf, strlen(buf));
81357429Smarkm				quit_pending = 1;
81457429Smarkm				return;
81557429Smarkm			}
81657429Smarkm		}
81757429Smarkm		/* Consume printed data from the buffer. */
81857429Smarkm		buffer_consume(&stdout_buffer, len);
81976259Sgreen		stdout_bytes += len;
82057429Smarkm	}
82157429Smarkm	/* Write buffered output to stderr. */
82257429Smarkm	if (FD_ISSET(fileno(stderr), writeset)) {
82357429Smarkm		/* Write as much data as possible. */
82457429Smarkm		len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
82557429Smarkm		    buffer_len(&stderr_buffer));
82657429Smarkm		if (len <= 0) {
82776259Sgreen			if (errno == EINTR || errno == EAGAIN)
82857429Smarkm				len = 0;
82957429Smarkm			else {
83057429Smarkm				/* EOF or error, but can't even print error message. */
83157429Smarkm				quit_pending = 1;
83257429Smarkm				return;
83357429Smarkm			}
83457429Smarkm		}
83557429Smarkm		/* Consume printed characters from the buffer. */
83657429Smarkm		buffer_consume(&stderr_buffer, len);
83776259Sgreen		stderr_bytes += len;
83857429Smarkm	}
83957429Smarkm}
84057429Smarkm
84157429Smarkm/*
84260573Skris * Get packets from the connection input buffer, and process them as long as
84360573Skris * there are packets available.
84460573Skris *
84560573Skris * Any unknown packets received during the actual
84660573Skris * session cause the session to terminate.  This is
84760573Skris * intended to make debugging easier since no
84860573Skris * confirmations are sent.  Any compatible protocol
84960573Skris * extensions must be negotiated during the
85060573Skris * preparatory phase.
85160573Skris */
85260573Skris
85392555Sdesstatic void
85476259Sgreenclient_process_buffered_input_packets(void)
85560573Skris{
85676259Sgreen	dispatch_run(DISPATCH_NONBLOCK, &quit_pending, compat20 ? xxx_kex : NULL);
85760573Skris}
85860573Skris
85965668Skris/* scan buf[] for '~' before sending data to the peer */
86065668Skris
86192555Sdesstatic int
86265668Skrissimple_escape_filter(Channel *c, char *buf, int len)
86365668Skris{
86465668Skris	/* XXX we assume c->extended is writeable */
86565668Skris	return process_escapes(&c->input, &c->output, &c->extended, buf, len);
86665668Skris}
86765668Skris
86892555Sdesstatic void
86976259Sgreenclient_channel_closed(int id, void *arg)
87076259Sgreen{
87176259Sgreen	if (id != session_ident)
87276259Sgreen		error("client_channel_closed: id %d != session_ident %d",
87376259Sgreen		    id, session_ident);
87492555Sdes	channel_cancel_cleanup(id);
87576259Sgreen	session_closed = 1;
876126274Sdes	leave_raw_mode();
87776259Sgreen}
87876259Sgreen
87960573Skris/*
88057429Smarkm * Implements the interactive session with the server.  This is called after
88157429Smarkm * the user has been authenticated, and a command has been started on the
88292555Sdes * remote host.  If escape_char != SSH_ESCAPECHAR_NONE, it is the character
88392555Sdes * used as an escape character for terminating or suspending the session.
88457429Smarkm */
88557429Smarkm
88660573Skrisint
88765668Skrisclient_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
88857429Smarkm{
88976259Sgreen	fd_set *readset = NULL, *writeset = NULL;
89057429Smarkm	double start_time, total_time;
89192555Sdes	int max_fd = 0, max_fd2 = 0, len, rekeying = 0, nalloc = 0;
89257429Smarkm	char buf[100];
89357429Smarkm
89457429Smarkm	debug("Entering interactive session.");
89557429Smarkm
89657429Smarkm	start_time = get_current_time();
89757429Smarkm
89857429Smarkm	/* Initialize variables. */
89957429Smarkm	escape_pending = 0;
90057429Smarkm	last_was_cr = 1;
90157429Smarkm	exit_status = -1;
90257429Smarkm	stdin_eof = 0;
90357429Smarkm	buffer_high = 64 * 1024;
90457429Smarkm	connection_in = packet_get_connection_in();
90557429Smarkm	connection_out = packet_get_connection_out();
90676259Sgreen	max_fd = MAX(connection_in, connection_out);
90776259Sgreen
90876259Sgreen	if (!compat20) {
90976259Sgreen		/* enable nonblocking unless tty */
91076259Sgreen		if (!isatty(fileno(stdin)))
91176259Sgreen			set_nonblock(fileno(stdin));
91276259Sgreen		if (!isatty(fileno(stdout)))
91376259Sgreen			set_nonblock(fileno(stdout));
91476259Sgreen		if (!isatty(fileno(stderr)))
91576259Sgreen			set_nonblock(fileno(stderr));
91676259Sgreen		max_fd = MAX(max_fd, fileno(stdin));
91776259Sgreen		max_fd = MAX(max_fd, fileno(stdout));
91876259Sgreen		max_fd = MAX(max_fd, fileno(stderr));
91976259Sgreen	}
92057429Smarkm	stdin_bytes = 0;
92157429Smarkm	stdout_bytes = 0;
92257429Smarkm	stderr_bytes = 0;
92357429Smarkm	quit_pending = 0;
92457429Smarkm	escape_char = escape_char_arg;
92557429Smarkm
92657429Smarkm	/* Initialize buffers. */
92757429Smarkm	buffer_init(&stdin_buffer);
92857429Smarkm	buffer_init(&stdout_buffer);
92957429Smarkm	buffer_init(&stderr_buffer);
93057429Smarkm
93160573Skris	client_init_dispatch();
93260573Skris
933113908Sdes	/*
934113908Sdes	 * Set signal handlers, (e.g. to restore non-blocking mode)
935113908Sdes	 * but don't overwrite SIG_IGN, matches behaviour from rsh(1)
936113908Sdes	 */
937113908Sdes	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
938113908Sdes		signal(SIGINT, signal_handler);
939113908Sdes	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
940113908Sdes		signal(SIGQUIT, signal_handler);
941113908Sdes	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
942113908Sdes		signal(SIGTERM, signal_handler);
94357429Smarkm	if (have_pty)
94457429Smarkm		signal(SIGWINCH, window_change_handler);
94557429Smarkm
94657429Smarkm	if (have_pty)
94757429Smarkm		enter_raw_mode();
94857429Smarkm
94976259Sgreen	if (compat20) {
95076259Sgreen		session_ident = ssh2_chan_id;
95192555Sdes		if (escape_char != SSH_ESCAPECHAR_NONE)
95276259Sgreen			channel_register_filter(session_ident,
95376259Sgreen			    simple_escape_filter);
95476259Sgreen		if (session_ident != -1)
95576259Sgreen			channel_register_cleanup(session_ident,
95676259Sgreen			    client_channel_closed);
95776259Sgreen	} else {
95876259Sgreen		/* Check if we should immediately send eof on stdin. */
95960573Skris		client_check_initial_eof_on_stdin();
96076259Sgreen	}
96157429Smarkm
96257429Smarkm	/* Main loop of the client for the interactive session mode. */
96357429Smarkm	while (!quit_pending) {
96457429Smarkm
96557429Smarkm		/* Process buffered packets sent by the server. */
96657429Smarkm		client_process_buffered_input_packets();
96757429Smarkm
96876259Sgreen		if (compat20 && session_closed && !channel_still_open())
96960573Skris			break;
97060573Skris
97176259Sgreen		rekeying = (xxx_kex != NULL && !xxx_kex->done);
97257429Smarkm
97376259Sgreen		if (rekeying) {
97476259Sgreen			debug("rekeying in progress");
97576259Sgreen		} else {
97676259Sgreen			/*
97776259Sgreen			 * Make packets of buffered stdin data, and buffer
97876259Sgreen			 * them for sending to the server.
97976259Sgreen			 */
98076259Sgreen			if (!compat20)
98176259Sgreen				client_make_packets_from_stdin_data();
98257429Smarkm
98376259Sgreen			/*
98476259Sgreen			 * Make packets from buffered channel data, and
98576259Sgreen			 * enqueue them for sending to the server.
98676259Sgreen			 */
98776259Sgreen			if (packet_not_very_much_data_to_write())
98876259Sgreen				channel_output_poll();
98957429Smarkm
99076259Sgreen			/*
99176259Sgreen			 * Check if the window size has changed, and buffer a
99276259Sgreen			 * message about it to the server if so.
99376259Sgreen			 */
99476259Sgreen			client_check_window_change();
99557429Smarkm
99676259Sgreen			if (quit_pending)
99776259Sgreen				break;
99876259Sgreen		}
99957429Smarkm		/*
100057429Smarkm		 * Wait until we have something to do (something becomes
100157429Smarkm		 * available on one of the descriptors).
100257429Smarkm		 */
100392555Sdes		max_fd2 = max_fd;
100476259Sgreen		client_wait_until_can_do_something(&readset, &writeset,
100592555Sdes		    &max_fd2, &nalloc, rekeying);
100657429Smarkm
100757429Smarkm		if (quit_pending)
100857429Smarkm			break;
100957429Smarkm
101076259Sgreen		/* Do channel operations unless rekeying in progress. */
101176259Sgreen		if (!rekeying) {
101276259Sgreen			channel_after_select(readset, writeset);
1013124208Sdes			if (need_rekeying || packet_need_rekeying()) {
1014124208Sdes				debug("need rekeying");
101576259Sgreen				xxx_kex->done = 0;
101676259Sgreen				kex_send_kexinit(xxx_kex);
101776259Sgreen				need_rekeying = 0;
101876259Sgreen			}
101976259Sgreen		}
102076259Sgreen
102160573Skris		/* Buffer input from the connection.  */
102276259Sgreen		client_process_net_input(readset);
102357429Smarkm
102460573Skris		if (quit_pending)
102560573Skris			break;
102657429Smarkm
102760573Skris		if (!compat20) {
102860573Skris			/* Buffer data from stdin */
102976259Sgreen			client_process_input(readset);
103060573Skris			/*
103160573Skris			 * Process output to stdout and stderr.  Output to
103260573Skris			 * the connection is processed elsewhere (above).
103360573Skris			 */
103476259Sgreen			client_process_output(writeset);
103560573Skris		}
103660573Skris
103757429Smarkm		/* Send as much buffered packet data as possible to the sender. */
103876259Sgreen		if (FD_ISSET(connection_out, writeset))
103957429Smarkm			packet_write_poll();
104057429Smarkm	}
104176259Sgreen	if (readset)
104276259Sgreen		xfree(readset);
104376259Sgreen	if (writeset)
104476259Sgreen		xfree(writeset);
104557429Smarkm
104657429Smarkm	/* Terminate the session. */
104757429Smarkm
104857429Smarkm	/* Stop watching for window change. */
104957429Smarkm	if (have_pty)
105057429Smarkm		signal(SIGWINCH, SIG_DFL);
105157429Smarkm
105292555Sdes	channel_free_all();
105357429Smarkm
105492555Sdes	if (have_pty)
105592555Sdes		leave_raw_mode();
105692555Sdes
105792555Sdes	/* restore blocking io */
105892555Sdes	if (!isatty(fileno(stdin)))
105992555Sdes		unset_nonblock(fileno(stdin));
106092555Sdes	if (!isatty(fileno(stdout)))
106192555Sdes		unset_nonblock(fileno(stdout));
106292555Sdes	if (!isatty(fileno(stderr)))
106392555Sdes		unset_nonblock(fileno(stderr));
106492555Sdes
1065126274Sdes	/*
1066126274Sdes	 * If there was no shell or command requested, there will be no remote
1067126274Sdes	 * exit status to be returned.  In that case, clear error code if the
1068126274Sdes	 * connection was deliberately terminated at this end.
1069126274Sdes	 */
1070126274Sdes	if (no_shell_flag && received_signal == SIGTERM) {
1071126274Sdes		received_signal = 0;
1072126274Sdes		exit_status = 0;
107392555Sdes	}
107492555Sdes
1075126274Sdes	if (received_signal)
1076126274Sdes		fatal("Killed by signal %d.", (int) received_signal);
1077126274Sdes
107857429Smarkm	/*
107957429Smarkm	 * In interactive mode (with pseudo tty) display a message indicating
108057429Smarkm	 * that the connection has been closed.
108157429Smarkm	 */
108257429Smarkm	if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) {
108357429Smarkm		snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host);
108457429Smarkm		buffer_append(&stderr_buffer, buf, strlen(buf));
108557429Smarkm	}
108692555Sdes
108757429Smarkm	/* Output any buffered data for stdout. */
108857429Smarkm	while (buffer_len(&stdout_buffer) > 0) {
108957429Smarkm		len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
109057429Smarkm		    buffer_len(&stdout_buffer));
109157429Smarkm		if (len <= 0) {
109257429Smarkm			error("Write failed flushing stdout buffer.");
109357429Smarkm			break;
109457429Smarkm		}
109557429Smarkm		buffer_consume(&stdout_buffer, len);
109676259Sgreen		stdout_bytes += len;
109757429Smarkm	}
109857429Smarkm
109957429Smarkm	/* Output any buffered data for stderr. */
110057429Smarkm	while (buffer_len(&stderr_buffer) > 0) {
110157429Smarkm		len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
110257429Smarkm		    buffer_len(&stderr_buffer));
110357429Smarkm		if (len <= 0) {
110457429Smarkm			error("Write failed flushing stderr buffer.");
110557429Smarkm			break;
110657429Smarkm		}
110757429Smarkm		buffer_consume(&stderr_buffer, len);
110876259Sgreen		stderr_bytes += len;
110957429Smarkm	}
111057429Smarkm
111157429Smarkm	/* Clear and free any buffers. */
111257429Smarkm	memset(buf, 0, sizeof(buf));
111357429Smarkm	buffer_free(&stdin_buffer);
111457429Smarkm	buffer_free(&stdout_buffer);
111557429Smarkm	buffer_free(&stderr_buffer);
111657429Smarkm
111757429Smarkm	/* Report bytes transferred, and transfer rates. */
111857429Smarkm	total_time = get_current_time() - start_time;
111957429Smarkm	debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds",
112092555Sdes	    stdin_bytes, stdout_bytes, stderr_bytes, total_time);
112157429Smarkm	if (total_time > 0)
112257429Smarkm		debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f",
112392555Sdes		    stdin_bytes / total_time, stdout_bytes / total_time,
112492555Sdes		    stderr_bytes / total_time);
112557429Smarkm
112657429Smarkm	/* Return the exit status of the program. */
112757429Smarkm	debug("Exit status %d", exit_status);
112857429Smarkm	return exit_status;
112957429Smarkm}
113060573Skris
113160573Skris/*********/
113260573Skris
113392555Sdesstatic void
113492555Sdesclient_input_stdout_data(int type, u_int32_t seq, void *ctxt)
113560573Skris{
113676259Sgreen	u_int data_len;
113760573Skris	char *data = packet_get_string(&data_len);
113892555Sdes	packet_check_eom();
113960573Skris	buffer_append(&stdout_buffer, data, data_len);
114060573Skris	memset(data, 0, data_len);
114160573Skris	xfree(data);
114260573Skris}
114392555Sdesstatic void
114492555Sdesclient_input_stderr_data(int type, u_int32_t seq, void *ctxt)
114560573Skris{
114676259Sgreen	u_int data_len;
114760573Skris	char *data = packet_get_string(&data_len);
114892555Sdes	packet_check_eom();
114960573Skris	buffer_append(&stderr_buffer, data, data_len);
115060573Skris	memset(data, 0, data_len);
115160573Skris	xfree(data);
115260573Skris}
115392555Sdesstatic void
115492555Sdesclient_input_exit_status(int type, u_int32_t seq, void *ctxt)
115560573Skris{
115660573Skris	exit_status = packet_get_int();
115792555Sdes	packet_check_eom();
115860573Skris	/* Acknowledge the exit. */
115960573Skris	packet_start(SSH_CMSG_EXIT_CONFIRMATION);
116060573Skris	packet_send();
116160573Skris	/*
116260573Skris	 * Must wait for packet to be sent since we are
116360573Skris	 * exiting the loop.
116460573Skris	 */
116560573Skris	packet_write_wait();
116660573Skris	/* Flag that we want to exit. */
116760573Skris	quit_pending = 1;
116860573Skris}
1169126274Sdesstatic void
1170126274Sdesclient_input_agent_open(int type, u_int32_t seq, void *ctxt)
1171126274Sdes{
1172126274Sdes	Channel *c = NULL;
1173126274Sdes	int remote_id, sock;
117460573Skris
1175126274Sdes	/* Read the remote channel number from the message. */
1176126274Sdes	remote_id = packet_get_int();
1177126274Sdes	packet_check_eom();
1178126274Sdes
1179126274Sdes	/*
1180126274Sdes	 * Get a connection to the local authentication agent (this may again
1181126274Sdes	 * get forwarded).
1182126274Sdes	 */
1183126274Sdes	sock = ssh_get_authentication_socket();
1184126274Sdes
1185126274Sdes	/*
1186126274Sdes	 * If we could not connect the agent, send an error message back to
1187126274Sdes	 * the server. This should never happen unless the agent dies,
1188126274Sdes	 * because authentication forwarding is only enabled if we have an
1189126274Sdes	 * agent.
1190126274Sdes	 */
1191126274Sdes	if (sock >= 0) {
1192126274Sdes		c = channel_new("", SSH_CHANNEL_OPEN, sock, sock,
1193126274Sdes		    -1, 0, 0, 0, "authentication agent connection", 1);
1194126274Sdes		c->remote_id = remote_id;
1195126274Sdes		c->force_drain = 1;
1196126274Sdes	}
1197126274Sdes	if (c == NULL) {
1198126274Sdes		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
1199126274Sdes		packet_put_int(remote_id);
1200126274Sdes	} else {
1201126274Sdes		/* Send a confirmation to the remote host. */
1202126274Sdes		debug("Forwarding authentication connection.");
1203126274Sdes		packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
1204126274Sdes		packet_put_int(remote_id);
1205126274Sdes		packet_put_int(c->self);
1206126274Sdes	}
1207126274Sdes	packet_send();
1208126274Sdes}
1209126274Sdes
121092555Sdesstatic Channel *
121176259Sgreenclient_request_forwarded_tcpip(const char *request_type, int rchan)
121276259Sgreen{
1213106121Sdes	Channel *c = NULL;
121476259Sgreen	char *listen_address, *originator_address;
121576259Sgreen	int listen_port, originator_port;
121692555Sdes	int sock;
121776259Sgreen
121876259Sgreen	/* Get rest of the packet */
121976259Sgreen	listen_address = packet_get_string(NULL);
122076259Sgreen	listen_port = packet_get_int();
122176259Sgreen	originator_address = packet_get_string(NULL);
122276259Sgreen	originator_port = packet_get_int();
122392555Sdes	packet_check_eom();
122476259Sgreen
122576259Sgreen	debug("client_request_forwarded_tcpip: listen %s port %d, originator %s port %d",
122676259Sgreen	    listen_address, listen_port, originator_address, originator_port);
122776259Sgreen
122892555Sdes	sock = channel_connect_by_listen_address(listen_port);
122992555Sdes	if (sock < 0) {
123092555Sdes		xfree(originator_address);
123192555Sdes		xfree(listen_address);
123292555Sdes		return NULL;
123376259Sgreen	}
123492555Sdes	c = channel_new("forwarded-tcpip",
123592555Sdes	    SSH_CHANNEL_CONNECTING, sock, sock, -1,
123692555Sdes	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0,
1237124208Sdes	    originator_address, 1);
123876259Sgreen	xfree(originator_address);
123976259Sgreen	xfree(listen_address);
124076259Sgreen	return c;
124176259Sgreen}
124276259Sgreen
1243106121Sdesstatic Channel *
124476259Sgreenclient_request_x11(const char *request_type, int rchan)
124576259Sgreen{
124676259Sgreen	Channel *c = NULL;
124776259Sgreen	char *originator;
124876259Sgreen	int originator_port;
124992555Sdes	int sock;
125076259Sgreen
125176259Sgreen	if (!options.forward_x11) {
125276259Sgreen		error("Warning: ssh server tried X11 forwarding.");
125376259Sgreen		error("Warning: this is probably a break in attempt by a malicious server.");
125476259Sgreen		return NULL;
125576259Sgreen	}
125676259Sgreen	originator = packet_get_string(NULL);
125776259Sgreen	if (datafellows & SSH_BUG_X11FWD) {
125876259Sgreen		debug2("buggy server: x11 request w/o originator_port");
125976259Sgreen		originator_port = 0;
126076259Sgreen	} else {
126176259Sgreen		originator_port = packet_get_int();
126276259Sgreen	}
126392555Sdes	packet_check_eom();
126476259Sgreen	/* XXX check permission */
126576259Sgreen	debug("client_request_x11: request from %s %d", originator,
126676259Sgreen	    originator_port);
126792555Sdes	xfree(originator);
126876259Sgreen	sock = x11_connect_display();
126992555Sdes	if (sock < 0)
127092555Sdes		return NULL;
127192555Sdes	c = channel_new("x11",
127292555Sdes	    SSH_CHANNEL_X11_OPEN, sock, sock, -1,
1273124208Sdes	    CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1);
127492555Sdes	c->force_drain = 1;
127576259Sgreen	return c;
127676259Sgreen}
127776259Sgreen
1278106121Sdesstatic Channel *
127976259Sgreenclient_request_agent(const char *request_type, int rchan)
128076259Sgreen{
128176259Sgreen	Channel *c = NULL;
128292555Sdes	int sock;
128376259Sgreen
128476259Sgreen	if (!options.forward_agent) {
128576259Sgreen		error("Warning: ssh server tried agent forwarding.");
128676259Sgreen		error("Warning: this is probably a break in attempt by a malicious server.");
128776259Sgreen		return NULL;
128876259Sgreen	}
128976259Sgreen	sock =  ssh_get_authentication_socket();
129092555Sdes	if (sock < 0)
129192555Sdes		return NULL;
129292555Sdes	c = channel_new("authentication agent connection",
129392555Sdes	    SSH_CHANNEL_OPEN, sock, sock, -1,
129492555Sdes	    CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0,
1295124208Sdes	    "authentication agent connection", 1);
129692555Sdes	c->force_drain = 1;
129776259Sgreen	return c;
129876259Sgreen}
129976259Sgreen
130060573Skris/* XXXX move to generic input handler */
130192555Sdesstatic void
130292555Sdesclient_input_channel_open(int type, u_int32_t seq, void *ctxt)
130360573Skris{
130460573Skris	Channel *c = NULL;
130560573Skris	char *ctype;
130660573Skris	int rchan;
130799060Sdes	u_int rmaxpack, rwindow, len;
130860573Skris
130960573Skris	ctype = packet_get_string(&len);
131060573Skris	rchan = packet_get_int();
131160573Skris	rwindow = packet_get_int();
131260573Skris	rmaxpack = packet_get_int();
131360573Skris
131460573Skris	debug("client_input_channel_open: ctype %s rchan %d win %d max %d",
131560573Skris	    ctype, rchan, rwindow, rmaxpack);
131660573Skris
131776259Sgreen	if (strcmp(ctype, "forwarded-tcpip") == 0) {
131876259Sgreen		c = client_request_forwarded_tcpip(ctype, rchan);
131976259Sgreen	} else if (strcmp(ctype, "x11") == 0) {
132076259Sgreen		c = client_request_x11(ctype, rchan);
132176259Sgreen	} else if (strcmp(ctype, "auth-agent@openssh.com") == 0) {
132276259Sgreen		c = client_request_agent(ctype, rchan);
132360573Skris	}
132460573Skris/* XXX duplicate : */
132560573Skris	if (c != NULL) {
132660573Skris		debug("confirm %s", ctype);
132760573Skris		c->remote_id = rchan;
132860573Skris		c->remote_window = rwindow;
132960573Skris		c->remote_maxpacket = rmaxpack;
133092555Sdes		if (c->type != SSH_CHANNEL_CONNECTING) {
133192555Sdes			packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
133292555Sdes			packet_put_int(c->remote_id);
133392555Sdes			packet_put_int(c->self);
133492555Sdes			packet_put_int(c->local_window);
133592555Sdes			packet_put_int(c->local_maxpacket);
133692555Sdes			packet_send();
133792555Sdes		}
133860573Skris	} else {
133960573Skris		debug("failure %s", ctype);
134060573Skris		packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
134160573Skris		packet_put_int(rchan);
134260573Skris		packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED);
134392555Sdes		if (!(datafellows & SSH_BUG_OPENFAILURE)) {
134492555Sdes			packet_put_cstring("open failed");
134592555Sdes			packet_put_cstring("");
134692555Sdes		}
134760573Skris		packet_send();
134860573Skris	}
134960573Skris	xfree(ctype);
135060573Skris}
135192555Sdesstatic void
135292555Sdesclient_input_channel_req(int type, u_int32_t seq, void *ctxt)
135376259Sgreen{
135476259Sgreen	Channel *c = NULL;
135576259Sgreen	int id, reply, success = 0;
135676259Sgreen	char *rtype;
135760573Skris
135876259Sgreen	id = packet_get_int();
135976259Sgreen	rtype = packet_get_string(NULL);
136076259Sgreen	reply = packet_get_char();
136176259Sgreen
136276259Sgreen	debug("client_input_channel_req: channel %d rtype %s reply %d",
136376259Sgreen	    id, rtype, reply);
136476259Sgreen
136576259Sgreen	if (session_ident == -1) {
136676259Sgreen		error("client_input_channel_req: no channel %d", session_ident);
136776259Sgreen	} else if (id != session_ident) {
136876259Sgreen		error("client_input_channel_req: channel %d: wrong channel: %d",
136976259Sgreen		    session_ident, id);
137076259Sgreen	}
137176259Sgreen	c = channel_lookup(id);
137276259Sgreen	if (c == NULL) {
137376259Sgreen		error("client_input_channel_req: channel %d: unknown channel", id);
137476259Sgreen	} else if (strcmp(rtype, "exit-status") == 0) {
137576259Sgreen		success = 1;
137676259Sgreen		exit_status = packet_get_int();
137792555Sdes		packet_check_eom();
137876259Sgreen	}
137976259Sgreen	if (reply) {
138076259Sgreen		packet_start(success ?
138176259Sgreen		    SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
138276259Sgreen		packet_put_int(c->remote_id);
138376259Sgreen		packet_send();
138476259Sgreen	}
138576259Sgreen	xfree(rtype);
138676259Sgreen}
138792555Sdesstatic void
138892555Sdesclient_input_global_request(int type, u_int32_t seq, void *ctxt)
138992555Sdes{
139092555Sdes	char *rtype;
139192555Sdes	int want_reply;
139292555Sdes	int success = 0;
139376259Sgreen
139492555Sdes	rtype = packet_get_string(NULL);
139592555Sdes	want_reply = packet_get_char();
1396126274Sdes	debug("client_input_global_request: rtype %s want_reply %d",
1397126274Sdes	    rtype, want_reply);
139892555Sdes	if (want_reply) {
139992555Sdes		packet_start(success ?
140092555Sdes		    SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE);
140192555Sdes		packet_send();
140292555Sdes		packet_write_wait();
140392555Sdes	}
140492555Sdes	xfree(rtype);
140592555Sdes}
140692555Sdes
140792555Sdesstatic void
140876259Sgreenclient_init_dispatch_20(void)
140960573Skris{
141060573Skris	dispatch_init(&dispatch_protocol_error);
141198675Sdes
141260573Skris	dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose);
141360573Skris	dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data);
141460573Skris	dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof);
141560573Skris	dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data);
141660573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open);
141760573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
141860573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
141976259Sgreen	dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req);
142060573Skris	dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust);
142192555Sdes	dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request);
142276259Sgreen
142376259Sgreen	/* rekeying */
142476259Sgreen	dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
142598675Sdes
142698675Sdes	/* global request reply messages */
142798675Sdes	dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply);
142898675Sdes	dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply);
142960573Skris}
143092555Sdesstatic void
143176259Sgreenclient_init_dispatch_13(void)
143260573Skris{
143360573Skris	dispatch_init(NULL);
143460573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close);
143560573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation);
143660573Skris	dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data);
143760573Skris	dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
143860573Skris	dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
143960573Skris	dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open);
144060573Skris	dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status);
144160573Skris	dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data);
144260573Skris	dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data);
144368700Sgreen
144468700Sgreen	dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ?
1445126274Sdes	    &client_input_agent_open : &deny_input_open);
144668700Sgreen	dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ?
144769587Sgreen	    &x11_input_open : &deny_input_open);
144860573Skris}
144992555Sdesstatic void
145076259Sgreenclient_init_dispatch_15(void)
145160573Skris{
145260573Skris	client_init_dispatch_13();
145360573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof);
145460573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose);
145560573Skris}
145692555Sdesstatic void
145776259Sgreenclient_init_dispatch(void)
145860573Skris{
145960573Skris	if (compat20)
146060573Skris		client_init_dispatch_20();
146160573Skris	else if (compat13)
146260573Skris		client_init_dispatch_13();
146360573Skris	else
146460573Skris		client_init_dispatch_15();
146560573Skris}
1466126274Sdes
1467126274Sdes/* client specific fatal cleanup */
1468126274Sdesvoid
1469126274Sdescleanup_exit(int i)
1470126274Sdes{
1471126274Sdes	leave_raw_mode();
1472126274Sdes	leave_non_blocking();
1473126274Sdes	_exit(i);
1474126274Sdes}
1475