clientloop.c revision 162852
1162852Sdes/* $OpenBSD: clientloop.c,v 1.175 2006/08/03 03:34:42 deraadt Exp $ */
257429Smarkm/*
357429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi>
457429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
557429Smarkm *                    All rights reserved
665668Skris * The main loop for the interactive session (client side).
760573Skris *
865668Skris * As far as I am concerned, the code I have written for this software
965668Skris * can be used freely for any purpose.  Any derived versions of this
1065668Skris * software must be clearly marked as such, and if the derived work is
1165668Skris * incompatible with the protocol description in the RFC file, it must be
1265668Skris * called by a name other than "ssh" or "Secure Shell".
1360573Skris *
1460573Skris *
1565668Skris * Copyright (c) 1999 Theo de Raadt.  All rights reserved.
1660573Skris *
1765668Skris * Redistribution and use in source and binary forms, with or without
1865668Skris * modification, are permitted provided that the following conditions
1965668Skris * are met:
2065668Skris * 1. Redistributions of source code must retain the above copyright
2165668Skris *    notice, this list of conditions and the following disclaimer.
2265668Skris * 2. Redistributions in binary form must reproduce the above copyright
2365668Skris *    notice, this list of conditions and the following disclaimer in the
2465668Skris *    documentation and/or other materials provided with the distribution.
2565668Skris *
2665668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2765668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2865668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2965668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
3065668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
3165668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3265668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3365668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3465668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3565668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3665668Skris *
3765668Skris *
3860573Skris * SSH2 support added by Markus Friedl.
3992555Sdes * Copyright (c) 1999, 2000, 2001 Markus Friedl.  All rights reserved.
4065668Skris *
4165668Skris * Redistribution and use in source and binary forms, with or without
4265668Skris * modification, are permitted provided that the following conditions
4365668Skris * are met:
4465668Skris * 1. Redistributions of source code must retain the above copyright
4565668Skris *    notice, this list of conditions and the following disclaimer.
4665668Skris * 2. Redistributions in binary form must reproduce the above copyright
4765668Skris *    notice, this list of conditions and the following disclaimer in the
4865668Skris *    documentation and/or other materials provided with the distribution.
4965668Skris *
5065668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
5165668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
5265668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
5365668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
5465668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
5565668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
5665668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
5765668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
5865668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
5965668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6057429Smarkm */
6157429Smarkm
6257429Smarkm#include "includes.h"
6357429Smarkm
64162852Sdes#include <sys/types.h>
65162852Sdes#include <sys/ioctl.h>
66162852Sdes#include <sys/param.h>
67162852Sdes#ifdef HAVE_SYS_STAT_H
68162852Sdes# include <sys/stat.h>
69162852Sdes#endif
70162852Sdes#ifdef HAVE_SYS_TIME_H
71162852Sdes# include <sys/time.h>
72162852Sdes#endif
73162852Sdes#include <sys/socket.h>
74162852Sdes
75162852Sdes#include <ctype.h>
76162852Sdes#include <errno.h>
77162852Sdes#ifdef HAVE_PATHS_H
78162852Sdes#include <paths.h>
79162852Sdes#endif
80162852Sdes#include <signal.h>
81162852Sdes#include <stdarg.h>
82162852Sdes#include <stdio.h>
83162852Sdes#include <stdlib.h>
84162852Sdes#include <string.h>
85162852Sdes#include <termios.h>
86162852Sdes#include <pwd.h>
87162852Sdes#include <unistd.h>
88162852Sdes
89162852Sdes#include "xmalloc.h"
9076259Sgreen#include "ssh.h"
9176259Sgreen#include "ssh1.h"
9276259Sgreen#include "ssh2.h"
9357429Smarkm#include "packet.h"
9457429Smarkm#include "buffer.h"
9560573Skris#include "compat.h"
9660573Skris#include "channels.h"
9760573Skris#include "dispatch.h"
9876259Sgreen#include "key.h"
99162852Sdes#include "cipher.h"
10076259Sgreen#include "kex.h"
10176259Sgreen#include "log.h"
10276259Sgreen#include "readconf.h"
10376259Sgreen#include "clientloop.h"
104157016Sdes#include "sshconnect.h"
10576259Sgreen#include "authfd.h"
10676259Sgreen#include "atomicio.h"
107137015Sdes#include "sshpty.h"
10876259Sgreen#include "misc.h"
109137015Sdes#include "monitor_fdpass.h"
110137015Sdes#include "match.h"
111137015Sdes#include "msg.h"
11260573Skris
11369587Sgreen/* import options */
11468700Sgreenextern Options options;
11568700Sgreen
11657429Smarkm/* Flag indicating that stdin should be redirected from /dev/null. */
11757429Smarkmextern int stdin_null_flag;
11857429Smarkm
119126274Sdes/* Flag indicating that no shell has been requested */
120126274Sdesextern int no_shell_flag;
121126274Sdes
122137015Sdes/* Control socket */
123137015Sdesextern int control_fd;
124137015Sdes
12557429Smarkm/*
12657429Smarkm * Name of the host we are connecting to.  This is the name given on the
12757429Smarkm * command line, or the HostName specified for the user-supplied name in a
12857429Smarkm * configuration file.
12957429Smarkm */
13057429Smarkmextern char *host;
13157429Smarkm
13257429Smarkm/*
13357429Smarkm * Flag to indicate that we have received a window change signal which has
13457429Smarkm * not yet been processed.  This will cause a message indicating the new
13557429Smarkm * window size to be sent to the server a little later.  This is volatile
13657429Smarkm * because this is updated in a signal handler.
13757429Smarkm */
13892555Sdesstatic volatile sig_atomic_t received_window_change_signal = 0;
13992555Sdesstatic volatile sig_atomic_t received_signal = 0;
14057429Smarkm
141157016Sdes/* Flag indicating whether the user's terminal is in non-blocking mode. */
14257429Smarkmstatic int in_non_blocking_mode = 0;
14357429Smarkm
14457429Smarkm/* Common data for the client loop code. */
145162852Sdesstatic volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */
14665668Skrisstatic int escape_char;		/* Escape character. */
14757429Smarkmstatic int escape_pending;	/* Last character was the escape character */
14857429Smarkmstatic int last_was_cr;		/* Last character was a newline. */
14957429Smarkmstatic int exit_status;		/* Used to store the exit status of the command. */
15057429Smarkmstatic int stdin_eof;		/* EOF has been encountered on standard error. */
15157429Smarkmstatic Buffer stdin_buffer;	/* Buffer for stdin data. */
15257429Smarkmstatic Buffer stdout_buffer;	/* Buffer for stdout data. */
15357429Smarkmstatic Buffer stderr_buffer;	/* Buffer for stderr data. */
15476259Sgreenstatic u_long stdin_bytes, stdout_bytes, stderr_bytes;
15576259Sgreenstatic u_int buffer_high;/* Soft max buffer size. */
15657429Smarkmstatic int connection_in;	/* Connection to server (input). */
15757429Smarkmstatic int connection_out;	/* Connection to server (output). */
15876259Sgreenstatic int need_rekeying;	/* Set to non-zero if rekeying is requested. */
15976259Sgreenstatic int session_closed = 0;	/* In SSH2: login session closed. */
160126274Sdesstatic int server_alive_timeouts = 0;
16157429Smarkm
16292555Sdesstatic void client_init_dispatch(void);
16360573Skrisint	session_ident = -1;
16460573Skris
165137015Sdesstruct confirm_ctx {
166137015Sdes	int want_tty;
167137015Sdes	int want_subsys;
168149749Sdes	int want_x_fwd;
169149749Sdes	int want_agent_fwd;
170137015Sdes	Buffer cmd;
171137015Sdes	char *term;
172137015Sdes	struct termios tio;
173137015Sdes	char **env;
174137015Sdes};
175137015Sdes
17676259Sgreen/*XXX*/
17776259Sgreenextern Kex *xxx_kex;
17857429Smarkm
179137015Sdesvoid ssh_process_session2_setup(int, int, int, Buffer *);
180137015Sdes
18157429Smarkm/* Restores stdin to blocking mode. */
18257429Smarkm
18392555Sdesstatic void
18476259Sgreenleave_non_blocking(void)
18557429Smarkm{
18657429Smarkm	if (in_non_blocking_mode) {
187137015Sdes		unset_nonblock(fileno(stdin));
18857429Smarkm		in_non_blocking_mode = 0;
18957429Smarkm	}
19057429Smarkm}
19157429Smarkm
19257429Smarkm/* Puts stdin terminal in non-blocking mode. */
19357429Smarkm
19492555Sdesstatic void
19576259Sgreenenter_non_blocking(void)
19657429Smarkm{
19757429Smarkm	in_non_blocking_mode = 1;
198137015Sdes	set_nonblock(fileno(stdin));
19957429Smarkm}
20057429Smarkm
20157429Smarkm/*
20257429Smarkm * Signal handler for the window change signal (SIGWINCH).  This just sets a
20357429Smarkm * flag indicating that the window has changed.
20457429Smarkm */
205162852Sdes/*ARGSUSED */
20692555Sdesstatic void
20757429Smarkmwindow_change_handler(int sig)
20857429Smarkm{
20957429Smarkm	received_window_change_signal = 1;
21057429Smarkm	signal(SIGWINCH, window_change_handler);
21157429Smarkm}
21257429Smarkm
21357429Smarkm/*
21457429Smarkm * Signal handler for signals that cause the program to terminate.  These
21557429Smarkm * signals must be trapped to restore terminal modes.
21657429Smarkm */
217162852Sdes/*ARGSUSED */
21892555Sdesstatic void
21957429Smarkmsignal_handler(int sig)
22057429Smarkm{
22192555Sdes	received_signal = sig;
22292555Sdes	quit_pending = 1;
22357429Smarkm}
22457429Smarkm
22557429Smarkm/*
22657429Smarkm * Returns current time in seconds from Jan 1, 1970 with the maximum
22757429Smarkm * available resolution.
22857429Smarkm */
22957429Smarkm
23092555Sdesstatic double
23176259Sgreenget_current_time(void)
23257429Smarkm{
23357429Smarkm	struct timeval tv;
23457429Smarkm	gettimeofday(&tv, NULL);
23557429Smarkm	return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
23657429Smarkm}
23757429Smarkm
238149749Sdes#define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1"
239149749Sdesvoid
240149749Sdesclient_x11_get_proto(const char *display, const char *xauth_path,
241149749Sdes    u_int trusted, char **_proto, char **_data)
242149749Sdes{
243149749Sdes	char cmd[1024];
244149749Sdes	char line[512];
245149749Sdes	char xdisplay[512];
246149749Sdes	static char proto[512], data[512];
247149749Sdes	FILE *f;
248149749Sdes	int got_data = 0, generated = 0, do_unlink = 0, i;
249149749Sdes	char *xauthdir, *xauthfile;
250149749Sdes	struct stat st;
251149749Sdes
252149749Sdes	xauthdir = xauthfile = NULL;
253149749Sdes	*_proto = proto;
254149749Sdes	*_data = data;
255149749Sdes	proto[0] = data[0] = '\0';
256149749Sdes
257149749Sdes	if (xauth_path == NULL ||(stat(xauth_path, &st) == -1)) {
258149749Sdes		debug("No xauth program.");
259149749Sdes	} else {
260149749Sdes		if (display == NULL) {
261149749Sdes			debug("x11_get_proto: DISPLAY not set");
262149749Sdes			return;
263149749Sdes		}
264149749Sdes		/*
265149749Sdes		 * Handle FamilyLocal case where $DISPLAY does
266149749Sdes		 * not match an authorization entry.  For this we
267149749Sdes		 * just try "xauth list unix:displaynum.screennum".
268149749Sdes		 * XXX: "localhost" match to determine FamilyLocal
269149749Sdes		 *      is not perfect.
270149749Sdes		 */
271149749Sdes		if (strncmp(display, "localhost:", 10) == 0) {
272149749Sdes			snprintf(xdisplay, sizeof(xdisplay), "unix:%s",
273149749Sdes			    display + 10);
274149749Sdes			display = xdisplay;
275149749Sdes		}
276149749Sdes		if (trusted == 0) {
277149749Sdes			xauthdir = xmalloc(MAXPATHLEN);
278149749Sdes			xauthfile = xmalloc(MAXPATHLEN);
279149749Sdes			strlcpy(xauthdir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN);
280149749Sdes			if (mkdtemp(xauthdir) != NULL) {
281149749Sdes				do_unlink = 1;
282149749Sdes				snprintf(xauthfile, MAXPATHLEN, "%s/xauthfile",
283149749Sdes				    xauthdir);
284149749Sdes				snprintf(cmd, sizeof(cmd),
285149749Sdes				    "%s -f %s generate %s " SSH_X11_PROTO
286149749Sdes				    " untrusted timeout 1200 2>" _PATH_DEVNULL,
287149749Sdes				    xauth_path, xauthfile, display);
288149749Sdes				debug2("x11_get_proto: %s", cmd);
289149749Sdes				if (system(cmd) == 0)
290149749Sdes					generated = 1;
291149749Sdes			}
292149749Sdes		}
293149749Sdes		snprintf(cmd, sizeof(cmd),
294157016Sdes		    "%s %s%s list %s 2>" _PATH_DEVNULL,
295149749Sdes		    xauth_path,
296149749Sdes		    generated ? "-f " : "" ,
297149749Sdes		    generated ? xauthfile : "",
298149749Sdes		    display);
299149749Sdes		debug2("x11_get_proto: %s", cmd);
300149749Sdes		f = popen(cmd, "r");
301149749Sdes		if (f && fgets(line, sizeof(line), f) &&
302149749Sdes		    sscanf(line, "%*s %511s %511s", proto, data) == 2)
303149749Sdes			got_data = 1;
304149749Sdes		if (f)
305149749Sdes			pclose(f);
306149749Sdes	}
307149749Sdes
308149749Sdes	if (do_unlink) {
309149749Sdes		unlink(xauthfile);
310149749Sdes		rmdir(xauthdir);
311149749Sdes	}
312149749Sdes	if (xauthdir)
313149749Sdes		xfree(xauthdir);
314149749Sdes	if (xauthfile)
315149749Sdes		xfree(xauthfile);
316149749Sdes
317149749Sdes	/*
318149749Sdes	 * If we didn't get authentication data, just make up some
319149749Sdes	 * data.  The forwarding code will check the validity of the
320149749Sdes	 * response anyway, and substitute this data.  The X11
321149749Sdes	 * server, however, will ignore this fake data and use
322149749Sdes	 * whatever authentication mechanisms it was using otherwise
323149749Sdes	 * for the local connection.
324149749Sdes	 */
325149749Sdes	if (!got_data) {
326149749Sdes		u_int32_t rnd = 0;
327149749Sdes
328149749Sdes		logit("Warning: No xauth data; "
329149749Sdes		    "using fake authentication data for X11 forwarding.");
330149749Sdes		strlcpy(proto, SSH_X11_PROTO, sizeof proto);
331149749Sdes		for (i = 0; i < 16; i++) {
332149749Sdes			if (i % 4 == 0)
333149749Sdes				rnd = arc4random();
334149749Sdes			snprintf(data + 2 * i, sizeof data - 2 * i, "%02x",
335149749Sdes			    rnd & 0xff);
336149749Sdes			rnd >>= 8;
337149749Sdes		}
338149749Sdes	}
339149749Sdes}
340149749Sdes
34157429Smarkm/*
34257429Smarkm * This is called when the interactive is entered.  This checks if there is
34357429Smarkm * an EOF coming on stdin.  We must check this explicitly, as select() does
34457429Smarkm * not appear to wake up when redirecting from /dev/null.
34557429Smarkm */
34657429Smarkm
34792555Sdesstatic void
34876259Sgreenclient_check_initial_eof_on_stdin(void)
34957429Smarkm{
35057429Smarkm	int len;
35157429Smarkm	char buf[1];
35257429Smarkm
35357429Smarkm	/*
35457429Smarkm	 * If standard input is to be "redirected from /dev/null", we simply
35557429Smarkm	 * mark that we have seen an EOF and send an EOF message to the
35657429Smarkm	 * server. Otherwise, we try to read a single character; it appears
35757429Smarkm	 * that for some files, such /dev/null, select() never wakes up for
35857429Smarkm	 * read for this descriptor, which means that we never get EOF.  This
35957429Smarkm	 * way we will get the EOF if stdin comes from /dev/null or similar.
36057429Smarkm	 */
36157429Smarkm	if (stdin_null_flag) {
36257429Smarkm		/* Fake EOF on stdin. */
36357429Smarkm		debug("Sending eof.");
36457429Smarkm		stdin_eof = 1;
36557429Smarkm		packet_start(SSH_CMSG_EOF);
36657429Smarkm		packet_send();
36757429Smarkm	} else {
36857429Smarkm		enter_non_blocking();
36957429Smarkm
37057429Smarkm		/* Check for immediate EOF on stdin. */
37157429Smarkm		len = read(fileno(stdin), buf, 1);
37257429Smarkm		if (len == 0) {
37357429Smarkm			/* EOF.  Record that we have seen it and send EOF to server. */
37457429Smarkm			debug("Sending eof.");
37557429Smarkm			stdin_eof = 1;
37657429Smarkm			packet_start(SSH_CMSG_EOF);
37757429Smarkm			packet_send();
37857429Smarkm		} else if (len > 0) {
37957429Smarkm			/*
38057429Smarkm			 * Got data.  We must store the data in the buffer,
38157429Smarkm			 * and also process it as an escape character if
38257429Smarkm			 * appropriate.
38357429Smarkm			 */
38476259Sgreen			if ((u_char) buf[0] == escape_char)
38557429Smarkm				escape_pending = 1;
38676259Sgreen			else
38757429Smarkm				buffer_append(&stdin_buffer, buf, 1);
38857429Smarkm		}
38957429Smarkm		leave_non_blocking();
39057429Smarkm	}
39157429Smarkm}
39257429Smarkm
39357429Smarkm
39457429Smarkm/*
39557429Smarkm * Make packets from buffered stdin data, and buffer them for sending to the
39657429Smarkm * connection.
39757429Smarkm */
39857429Smarkm
39992555Sdesstatic void
40076259Sgreenclient_make_packets_from_stdin_data(void)
40157429Smarkm{
40276259Sgreen	u_int len;
40357429Smarkm
40457429Smarkm	/* Send buffered stdin data to the server. */
40557429Smarkm	while (buffer_len(&stdin_buffer) > 0 &&
40692555Sdes	    packet_not_very_much_data_to_write()) {
40757429Smarkm		len = buffer_len(&stdin_buffer);
40857429Smarkm		/* Keep the packets at reasonable size. */
40957429Smarkm		if (len > packet_get_maxsize())
41057429Smarkm			len = packet_get_maxsize();
41157429Smarkm		packet_start(SSH_CMSG_STDIN_DATA);
41257429Smarkm		packet_put_string(buffer_ptr(&stdin_buffer), len);
41357429Smarkm		packet_send();
41457429Smarkm		buffer_consume(&stdin_buffer, len);
41576259Sgreen		stdin_bytes += len;
41657429Smarkm		/* If we have a pending EOF, send it now. */
41757429Smarkm		if (stdin_eof && buffer_len(&stdin_buffer) == 0) {
41857429Smarkm			packet_start(SSH_CMSG_EOF);
41957429Smarkm			packet_send();
42057429Smarkm		}
42157429Smarkm	}
42257429Smarkm}
42357429Smarkm
42457429Smarkm/*
42557429Smarkm * Checks if the client window has changed, and sends a packet about it to
42657429Smarkm * the server if so.  The actual change is detected elsewhere (by a software
42757429Smarkm * interrupt on Unix); this just checks the flag and sends a message if
42857429Smarkm * appropriate.
42957429Smarkm */
43057429Smarkm
43192555Sdesstatic void
43276259Sgreenclient_check_window_change(void)
43357429Smarkm{
43460573Skris	struct winsize ws;
43557429Smarkm
43660573Skris	if (! received_window_change_signal)
43760573Skris		return;
43860573Skris	/** XXX race */
43960573Skris	received_window_change_signal = 0;
44057429Smarkm
44169587Sgreen	debug2("client_check_window_change: changed");
44260573Skris
44360573Skris	if (compat20) {
444137015Sdes		channel_send_window_changes();
44560573Skris	} else {
446137015Sdes		if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
447137015Sdes			return;
44860573Skris		packet_start(SSH_CMSG_WINDOW_SIZE);
449162852Sdes		packet_put_int((u_int)ws.ws_row);
450162852Sdes		packet_put_int((u_int)ws.ws_col);
451162852Sdes		packet_put_int((u_int)ws.ws_xpixel);
452162852Sdes		packet_put_int((u_int)ws.ws_ypixel);
45360573Skris		packet_send();
45457429Smarkm	}
45557429Smarkm}
45657429Smarkm
457126274Sdesstatic void
458126274Sdesclient_global_request_reply(int type, u_int32_t seq, void *ctxt)
459126274Sdes{
460126274Sdes	server_alive_timeouts = 0;
461126274Sdes	client_global_request_reply_fwd(type, seq, ctxt);
462126274Sdes}
463126274Sdes
464126274Sdesstatic void
465126274Sdesserver_alive_check(void)
466126274Sdes{
467126274Sdes	if (++server_alive_timeouts > options.server_alive_count_max)
468126274Sdes		packet_disconnect("Timeout, server not responding.");
469126274Sdes	packet_start(SSH2_MSG_GLOBAL_REQUEST);
470126274Sdes	packet_put_cstring("keepalive@openssh.com");
471126274Sdes	packet_put_char(1);     /* boolean: want reply */
472126274Sdes	packet_send();
473126274Sdes}
474126274Sdes
47557429Smarkm/*
47657429Smarkm * Waits until the client can do something (some data becomes available on
47757429Smarkm * one of the file descriptors).
47857429Smarkm */
47992555Sdesstatic void
48076259Sgreenclient_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
481137015Sdes    int *maxfdp, u_int *nallocp, int rekeying)
48257429Smarkm{
483126274Sdes	struct timeval tv, *tvp;
484126274Sdes	int ret;
485126274Sdes
48676259Sgreen	/* Add any selections by the channel mechanism. */
48792555Sdes	channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying);
48857429Smarkm
48960573Skris	if (!compat20) {
49060573Skris		/* Read from the connection, unless our buffers are full. */
49160573Skris		if (buffer_len(&stdout_buffer) < buffer_high &&
49260573Skris		    buffer_len(&stderr_buffer) < buffer_high &&
49360573Skris		    channel_not_very_much_buffered_data())
49476259Sgreen			FD_SET(connection_in, *readsetp);
49560573Skris		/*
49660573Skris		 * Read from stdin, unless we have seen EOF or have very much
49760573Skris		 * buffered data to send to the server.
49860573Skris		 */
49960573Skris		if (!stdin_eof && packet_not_very_much_data_to_write())
50076259Sgreen			FD_SET(fileno(stdin), *readsetp);
50160573Skris
50260573Skris		/* Select stdout/stderr if have data in buffer. */
50360573Skris		if (buffer_len(&stdout_buffer) > 0)
50476259Sgreen			FD_SET(fileno(stdout), *writesetp);
50560573Skris		if (buffer_len(&stderr_buffer) > 0)
50676259Sgreen			FD_SET(fileno(stderr), *writesetp);
50760573Skris	} else {
50892555Sdes		/* channel_prepare_select could have closed the last channel */
50992555Sdes		if (session_closed && !channel_still_open() &&
51092555Sdes		    !packet_have_data_to_write()) {
51192555Sdes			/* clear mask since we did not call select() */
51292555Sdes			memset(*readsetp, 0, *nallocp);
51392555Sdes			memset(*writesetp, 0, *nallocp);
51492555Sdes			return;
51592555Sdes		} else {
51692555Sdes			FD_SET(connection_in, *readsetp);
51792555Sdes		}
51860573Skris	}
51957429Smarkm
52057429Smarkm	/* Select server connection if have data to write to the server. */
52157429Smarkm	if (packet_have_data_to_write())
52276259Sgreen		FD_SET(connection_out, *writesetp);
52357429Smarkm
524137015Sdes	if (control_fd != -1)
525137015Sdes		FD_SET(control_fd, *readsetp);
526137015Sdes
52757429Smarkm	/*
52857429Smarkm	 * Wait for something to happen.  This will suspend the process until
52957429Smarkm	 * some selected descriptor can be read, written, or has some other
530126274Sdes	 * event pending.
53157429Smarkm	 */
53257429Smarkm
533126274Sdes	if (options.server_alive_interval == 0 || !compat20)
534126274Sdes		tvp = NULL;
535137015Sdes	else {
536126274Sdes		tv.tv_sec = options.server_alive_interval;
537126274Sdes		tv.tv_usec = 0;
538126274Sdes		tvp = &tv;
539126274Sdes	}
540126274Sdes	ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
541126274Sdes	if (ret < 0) {
54257429Smarkm		char buf[100];
54376259Sgreen
54476259Sgreen		/*
54576259Sgreen		 * We have to clear the select masks, because we return.
54676259Sgreen		 * We have to return, because the mainloop checks for the flags
54776259Sgreen		 * set by the signal handlers.
54876259Sgreen		 */
54992555Sdes		memset(*readsetp, 0, *nallocp);
55092555Sdes		memset(*writesetp, 0, *nallocp);
55176259Sgreen
55257429Smarkm		if (errno == EINTR)
55357429Smarkm			return;
55457429Smarkm		/* Note: we might still have data in the buffers. */
55557429Smarkm		snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
55657429Smarkm		buffer_append(&stderr_buffer, buf, strlen(buf));
55757429Smarkm		quit_pending = 1;
558126274Sdes	} else if (ret == 0)
559126274Sdes		server_alive_check();
56057429Smarkm}
56157429Smarkm
56292555Sdesstatic void
56365668Skrisclient_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr)
56457429Smarkm{
56557429Smarkm	/* Flush stdout and stderr buffers. */
56665668Skris	if (buffer_len(bout) > 0)
567124208Sdes		atomicio(vwrite, fileno(stdout), buffer_ptr(bout), buffer_len(bout));
56865668Skris	if (buffer_len(berr) > 0)
569124208Sdes		atomicio(vwrite, fileno(stderr), buffer_ptr(berr), buffer_len(berr));
57057429Smarkm
57157429Smarkm	leave_raw_mode();
57257429Smarkm
57357429Smarkm	/*
57457429Smarkm	 * Free (and clear) the buffer to reduce the amount of data that gets
57557429Smarkm	 * written to swap.
57657429Smarkm	 */
57765668Skris	buffer_free(bin);
57865668Skris	buffer_free(bout);
57965668Skris	buffer_free(berr);
58057429Smarkm
58157429Smarkm	/* Send the suspend signal to the program itself. */
58257429Smarkm	kill(getpid(), SIGTSTP);
58357429Smarkm
584146998Sdes	/* Reset window sizes in case they have changed */
585146998Sdes	received_window_change_signal = 1;
58657429Smarkm
58757429Smarkm	/* OK, we have been continued by the user. Reinitialize buffers. */
58865668Skris	buffer_init(bin);
58965668Skris	buffer_init(bout);
59065668Skris	buffer_init(berr);
59157429Smarkm
59257429Smarkm	enter_raw_mode();
59357429Smarkm}
59457429Smarkm
59592555Sdesstatic void
596162852Sdesclient_process_net_input(fd_set *readset)
59757429Smarkm{
59860573Skris	int len;
59960573Skris	char buf[8192];
60057429Smarkm
60157429Smarkm	/*
60257429Smarkm	 * Read input from the server, and add any such data to the buffer of
60357429Smarkm	 * the packet subsystem.
60457429Smarkm	 */
60557429Smarkm	if (FD_ISSET(connection_in, readset)) {
60657429Smarkm		/* Read as much as possible. */
60757429Smarkm		len = read(connection_in, buf, sizeof(buf));
60857429Smarkm		if (len == 0) {
60957429Smarkm			/* Received EOF.  The remote host has closed the connection. */
61057429Smarkm			snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n",
61157429Smarkm				 host);
61257429Smarkm			buffer_append(&stderr_buffer, buf, strlen(buf));
61357429Smarkm			quit_pending = 1;
61457429Smarkm			return;
61557429Smarkm		}
61657429Smarkm		/*
61757429Smarkm		 * There is a kernel bug on Solaris that causes select to
61857429Smarkm		 * sometimes wake up even though there is no data available.
61957429Smarkm		 */
62076259Sgreen		if (len < 0 && (errno == EAGAIN || errno == EINTR))
62157429Smarkm			len = 0;
62257429Smarkm
62357429Smarkm		if (len < 0) {
62457429Smarkm			/* An error has encountered.  Perhaps there is a network problem. */
62557429Smarkm			snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n",
62657429Smarkm				 host, strerror(errno));
62757429Smarkm			buffer_append(&stderr_buffer, buf, strlen(buf));
62857429Smarkm			quit_pending = 1;
62957429Smarkm			return;
63057429Smarkm		}
63157429Smarkm		packet_process_incoming(buf, len);
63257429Smarkm	}
63360573Skris}
63460573Skris
63598675Sdesstatic void
636137015Sdesclient_subsystem_reply(int type, u_int32_t seq, void *ctxt)
637137015Sdes{
638137015Sdes	int id;
639137015Sdes	Channel *c;
640137015Sdes
641137015Sdes	id = packet_get_int();
642137015Sdes	packet_check_eom();
643137015Sdes
644137015Sdes	if ((c = channel_lookup(id)) == NULL) {
645137015Sdes		error("%s: no channel for id %d", __func__, id);
646137015Sdes		return;
647137015Sdes	}
648137015Sdes
649137015Sdes	if (type == SSH2_MSG_CHANNEL_SUCCESS)
650137015Sdes		debug2("Request suceeded on channel %d", id);
651137015Sdes	else if (type == SSH2_MSG_CHANNEL_FAILURE) {
652137015Sdes		error("Request failed on channel %d", id);
653137015Sdes		channel_free(c);
654137015Sdes	}
655137015Sdes}
656137015Sdes
657137015Sdesstatic void
658137015Sdesclient_extra_session2_setup(int id, void *arg)
659137015Sdes{
660137015Sdes	struct confirm_ctx *cctx = arg;
661149749Sdes	const char *display;
662137015Sdes	Channel *c;
663137015Sdes	int i;
664137015Sdes
665137015Sdes	if (cctx == NULL)
666137015Sdes		fatal("%s: cctx == NULL", __func__);
667137015Sdes	if ((c = channel_lookup(id)) == NULL)
668137015Sdes		fatal("%s: no channel for id %d", __func__, id);
669137015Sdes
670149749Sdes	display = getenv("DISPLAY");
671149749Sdes	if (cctx->want_x_fwd && options.forward_x11 && display != NULL) {
672149749Sdes		char *proto, *data;
673149749Sdes		/* Get reasonable local authentication information. */
674149749Sdes		client_x11_get_proto(display, options.xauth_location,
675149749Sdes		    options.forward_x11_trusted, &proto, &data);
676149749Sdes		/* Request forwarding with authentication spoofing. */
677149749Sdes		debug("Requesting X11 forwarding with authentication spoofing.");
678149749Sdes		x11_request_forwarding_with_spoofing(id, display, proto, data);
679149749Sdes		/* XXX wait for reply */
680149749Sdes	}
681149749Sdes
682149749Sdes	if (cctx->want_agent_fwd && options.forward_agent) {
683149749Sdes		debug("Requesting authentication agent forwarding.");
684149749Sdes		channel_request_start(id, "auth-agent-req@openssh.com", 0);
685149749Sdes		packet_send();
686149749Sdes	}
687149749Sdes
688137015Sdes	client_session2_setup(id, cctx->want_tty, cctx->want_subsys,
689137015Sdes	    cctx->term, &cctx->tio, c->rfd, &cctx->cmd, cctx->env,
690137015Sdes	    client_subsystem_reply);
691137015Sdes
692137015Sdes	c->confirm_ctx = NULL;
693137015Sdes	buffer_free(&cctx->cmd);
694137015Sdes	xfree(cctx->term);
695137015Sdes	if (cctx->env != NULL) {
696137015Sdes		for (i = 0; cctx->env[i] != NULL; i++)
697137015Sdes			xfree(cctx->env[i]);
698137015Sdes		xfree(cctx->env);
699137015Sdes	}
700137015Sdes	xfree(cctx);
701137015Sdes}
702137015Sdes
703137015Sdesstatic void
704162852Sdesclient_process_control(fd_set *readset)
705137015Sdes{
706137015Sdes	Buffer m;
707137015Sdes	Channel *c;
708149749Sdes	int client_fd, new_fd[3], ver, allowed;
709137015Sdes	socklen_t addrlen;
710137015Sdes	struct sockaddr_storage addr;
711137015Sdes	struct confirm_ctx *cctx;
712137015Sdes	char *cmd;
713149749Sdes	u_int i, len, env_len, command, flags;
714137015Sdes	uid_t euid;
715137015Sdes	gid_t egid;
716137015Sdes
717137015Sdes	/*
718137015Sdes	 * Accept connection on control socket
719137015Sdes	 */
720137015Sdes	if (control_fd == -1 || !FD_ISSET(control_fd, readset))
721137015Sdes		return;
722137015Sdes
723137015Sdes	memset(&addr, 0, sizeof(addr));
724137015Sdes	addrlen = sizeof(addr);
725137015Sdes	if ((client_fd = accept(control_fd,
726137015Sdes	    (struct sockaddr*)&addr, &addrlen)) == -1) {
727137015Sdes		error("%s accept: %s", __func__, strerror(errno));
728137015Sdes		return;
729137015Sdes	}
730137015Sdes
731137015Sdes	if (getpeereid(client_fd, &euid, &egid) < 0) {
732137015Sdes		error("%s getpeereid failed: %s", __func__, strerror(errno));
733137015Sdes		close(client_fd);
734137015Sdes		return;
735137015Sdes	}
736137015Sdes	if ((euid != 0) && (getuid() != euid)) {
737137015Sdes		error("control mode uid mismatch: peer euid %u != uid %u",
738137015Sdes		    (u_int) euid, (u_int) getuid());
739137015Sdes		close(client_fd);
740137015Sdes		return;
741137015Sdes	}
742137015Sdes
743146998Sdes	unset_nonblock(client_fd);
744146998Sdes
745146998Sdes	/* Read command */
746146998Sdes	buffer_init(&m);
747146998Sdes	if (ssh_msg_recv(client_fd, &m) == -1) {
748146998Sdes		error("%s: client msg_recv failed", __func__);
749146998Sdes		close(client_fd);
750146998Sdes		buffer_free(&m);
751146998Sdes		return;
752146998Sdes	}
753149749Sdes	if ((ver = buffer_get_char(&m)) != SSHMUX_VER) {
754146998Sdes		error("%s: wrong client version %d", __func__, ver);
755146998Sdes		buffer_free(&m);
756146998Sdes		close(client_fd);
757146998Sdes		return;
758146998Sdes	}
759146998Sdes
760137015Sdes	allowed = 1;
761146998Sdes	command = buffer_get_int(&m);
762146998Sdes	flags = buffer_get_int(&m);
763137015Sdes
764146998Sdes	buffer_clear(&m);
765146998Sdes
766146998Sdes	switch (command) {
767146998Sdes	case SSHMUX_COMMAND_OPEN:
768149749Sdes		if (options.control_master == SSHCTL_MASTER_ASK ||
769149749Sdes		    options.control_master == SSHCTL_MASTER_AUTO_ASK)
770146998Sdes			allowed = ask_permission("Allow shared connection "
771146998Sdes			    "to %s? ", host);
772146998Sdes		/* continue below */
773146998Sdes		break;
774146998Sdes	case SSHMUX_COMMAND_TERMINATE:
775149749Sdes		if (options.control_master == SSHCTL_MASTER_ASK ||
776149749Sdes		    options.control_master == SSHCTL_MASTER_AUTO_ASK)
777146998Sdes			allowed = ask_permission("Terminate shared connection "
778146998Sdes			    "to %s? ", host);
779146998Sdes		if (allowed)
780146998Sdes			quit_pending = 1;
781147001Sdes		/* FALLTHROUGH */
782146998Sdes	case SSHMUX_COMMAND_ALIVE_CHECK:
783146998Sdes		/* Reply for SSHMUX_COMMAND_TERMINATE and ALIVE_CHECK */
784146998Sdes		buffer_clear(&m);
785146998Sdes		buffer_put_int(&m, allowed);
786146998Sdes		buffer_put_int(&m, getpid());
787149749Sdes		if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
788146998Sdes			error("%s: client msg_send failed", __func__);
789146998Sdes			close(client_fd);
790146998Sdes			buffer_free(&m);
791146998Sdes			return;
792137015Sdes		}
793146998Sdes		buffer_free(&m);
794146998Sdes		close(client_fd);
795146998Sdes		return;
796146998Sdes	default:
797146998Sdes		error("Unsupported command %d", command);
798146998Sdes		buffer_free(&m);
799146998Sdes		close(client_fd);
800146998Sdes		return;
801137015Sdes	}
802137015Sdes
803146998Sdes	/* Reply for SSHMUX_COMMAND_OPEN */
804146998Sdes	buffer_clear(&m);
805137015Sdes	buffer_put_int(&m, allowed);
806137015Sdes	buffer_put_int(&m, getpid());
807149749Sdes	if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
808137015Sdes		error("%s: client msg_send failed", __func__);
809137015Sdes		close(client_fd);
810137015Sdes		buffer_free(&m);
811137015Sdes		return;
812137015Sdes	}
813137015Sdes
814137015Sdes	if (!allowed) {
815137015Sdes		error("Refused control connection");
816137015Sdes		close(client_fd);
817137015Sdes		buffer_free(&m);
818137015Sdes		return;
819137015Sdes	}
820137015Sdes
821146998Sdes	buffer_clear(&m);
822137015Sdes	if (ssh_msg_recv(client_fd, &m) == -1) {
823137015Sdes		error("%s: client msg_recv failed", __func__);
824137015Sdes		close(client_fd);
825137015Sdes		buffer_free(&m);
826137015Sdes		return;
827137015Sdes	}
828149749Sdes	if ((ver = buffer_get_char(&m)) != SSHMUX_VER) {
829137015Sdes		error("%s: wrong client version %d", __func__, ver);
830137015Sdes		buffer_free(&m);
831137015Sdes		close(client_fd);
832137015Sdes		return;
833137015Sdes	}
834137015Sdes
835162852Sdes	cctx = xcalloc(1, sizeof(*cctx));
836146998Sdes	cctx->want_tty = (flags & SSHMUX_FLAG_TTY) != 0;
837146998Sdes	cctx->want_subsys = (flags & SSHMUX_FLAG_SUBSYS) != 0;
838149749Sdes	cctx->want_x_fwd = (flags & SSHMUX_FLAG_X11_FWD) != 0;
839149749Sdes	cctx->want_agent_fwd = (flags & SSHMUX_FLAG_AGENT_FWD) != 0;
840137015Sdes	cctx->term = buffer_get_string(&m, &len);
841137015Sdes
842137015Sdes	cmd = buffer_get_string(&m, &len);
843137015Sdes	buffer_init(&cctx->cmd);
844137015Sdes	buffer_append(&cctx->cmd, cmd, strlen(cmd));
845137015Sdes
846137015Sdes	env_len = buffer_get_int(&m);
847137015Sdes	env_len = MIN(env_len, 4096);
848137015Sdes	debug3("%s: receiving %d env vars", __func__, env_len);
849137015Sdes	if (env_len != 0) {
850162852Sdes		cctx->env = xcalloc(env_len + 1, sizeof(*cctx->env));
851137015Sdes		for (i = 0; i < env_len; i++)
852137015Sdes			cctx->env[i] = buffer_get_string(&m, &len);
853137015Sdes		cctx->env[i] = NULL;
854137015Sdes	}
855137015Sdes
856137015Sdes	debug2("%s: accepted tty %d, subsys %d, cmd %s", __func__,
857137015Sdes	    cctx->want_tty, cctx->want_subsys, cmd);
858162852Sdes	xfree(cmd);
859137015Sdes
860137015Sdes	/* Gather fds from client */
861137015Sdes	new_fd[0] = mm_receive_fd(client_fd);
862137015Sdes	new_fd[1] = mm_receive_fd(client_fd);
863137015Sdes	new_fd[2] = mm_receive_fd(client_fd);
864137015Sdes
865137015Sdes	debug2("%s: got fds stdin %d, stdout %d, stderr %d", __func__,
866137015Sdes	    new_fd[0], new_fd[1], new_fd[2]);
867137015Sdes
868137015Sdes	/* Try to pick up ttymodes from client before it goes raw */
869137015Sdes	if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1)
870137015Sdes		error("%s: tcgetattr: %s", __func__, strerror(errno));
871137015Sdes
872146998Sdes	/* This roundtrip is just for synchronisation of ttymodes */
873137015Sdes	buffer_clear(&m);
874149749Sdes	if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
875137015Sdes		error("%s: client msg_send failed", __func__);
876137015Sdes		close(client_fd);
877137015Sdes		close(new_fd[0]);
878137015Sdes		close(new_fd[1]);
879137015Sdes		close(new_fd[2]);
880137015Sdes		buffer_free(&m);
881146998Sdes		xfree(cctx->term);
882146998Sdes		if (env_len != 0) {
883146998Sdes			for (i = 0; i < env_len; i++)
884146998Sdes				xfree(cctx->env[i]);
885146998Sdes			xfree(cctx->env);
886146998Sdes		}
887137015Sdes		return;
888137015Sdes	}
889137015Sdes	buffer_free(&m);
890137015Sdes
891137015Sdes	/* enable nonblocking unless tty */
892137015Sdes	if (!isatty(new_fd[0]))
893137015Sdes		set_nonblock(new_fd[0]);
894137015Sdes	if (!isatty(new_fd[1]))
895137015Sdes		set_nonblock(new_fd[1]);
896137015Sdes	if (!isatty(new_fd[2]))
897137015Sdes		set_nonblock(new_fd[2]);
898137015Sdes
899137015Sdes	set_nonblock(client_fd);
900137015Sdes
901137015Sdes	c = channel_new("session", SSH_CHANNEL_OPENING,
902137015Sdes	    new_fd[0], new_fd[1], new_fd[2],
903137015Sdes	    CHAN_SES_WINDOW_DEFAULT, CHAN_SES_PACKET_DEFAULT,
904137015Sdes	    CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0);
905137015Sdes
906137015Sdes	/* XXX */
907137015Sdes	c->ctl_fd = client_fd;
908137015Sdes
909137015Sdes	debug3("%s: channel_new: %d", __func__, c->self);
910137015Sdes
911137015Sdes	channel_send_open(c->self);
912137015Sdes	channel_register_confirm(c->self, client_extra_session2_setup, cctx);
913137015Sdes}
914137015Sdes
915137015Sdesstatic void
91698675Sdesprocess_cmdline(void)
91798675Sdes{
91898675Sdes	void (*handler)(int);
919146998Sdes	char *s, *cmd, *cancel_host;
920137015Sdes	int delete = 0;
92198675Sdes	int local = 0;
922146998Sdes	u_short cancel_port;
923146998Sdes	Forward fwd;
92498675Sdes
92598675Sdes	leave_raw_mode();
92698675Sdes	handler = signal(SIGINT, SIG_IGN);
92798675Sdes	cmd = s = read_passphrase("\r\nssh> ", RP_ECHO);
92898675Sdes	if (s == NULL)
92998675Sdes		goto out;
93098675Sdes	while (*s && isspace(*s))
93198675Sdes		s++;
932137015Sdes	if (*s == '-')
933137015Sdes		s++;	/* Skip cmdline '-', if any */
934137015Sdes	if (*s == '\0')
93598675Sdes		goto out;
936137015Sdes
937137015Sdes	if (*s == 'h' || *s == 'H' || *s == '?') {
938137015Sdes		logit("Commands:");
939162852Sdes		logit("      -L[bind_address:]port:host:hostport    "
940162852Sdes		    "Request local forward");
941162852Sdes		logit("      -R[bind_address:]port:host:hostport    "
942162852Sdes		    "Request remote forward");
943162852Sdes		logit("      -KR[bind_address:]port                 "
944162852Sdes		    "Cancel remote forward");
945157016Sdes		if (!options.permit_local_command)
946157016Sdes			goto out;
947162852Sdes		logit("      !args                                  "
948162852Sdes		    "Execute local command");
949137015Sdes		goto out;
950137015Sdes	}
951137015Sdes
952157016Sdes	if (*s == '!' && options.permit_local_command) {
953157016Sdes		s++;
954157016Sdes		ssh_local_cmd(s);
955157016Sdes		goto out;
956157016Sdes	}
957157016Sdes
958137015Sdes	if (*s == 'K') {
959137015Sdes		delete = 1;
960137015Sdes		s++;
961137015Sdes	}
962137015Sdes	if (*s != 'L' && *s != 'R') {
963124208Sdes		logit("Invalid command.");
96498675Sdes		goto out;
96598675Sdes	}
966137015Sdes	if (*s == 'L')
96798675Sdes		local = 1;
968137015Sdes	if (local && delete) {
969137015Sdes		logit("Not supported.");
970137015Sdes		goto out;
971137015Sdes	}
972137015Sdes	if ((!local || delete) && !compat20) {
973124208Sdes		logit("Not supported for SSH protocol version 1.");
97498675Sdes		goto out;
97598675Sdes	}
976137015Sdes
977137015Sdes	s++;
97898675Sdes	while (*s && isspace(*s))
97998675Sdes		s++;
98098675Sdes
981137015Sdes	if (delete) {
982146998Sdes		cancel_port = 0;
983146998Sdes		cancel_host = hpdelim(&s);	/* may be NULL */
984146998Sdes		if (s != NULL) {
985146998Sdes			cancel_port = a2port(s);
986146998Sdes			cancel_host = cleanhostname(cancel_host);
987146998Sdes		} else {
988146998Sdes			cancel_port = a2port(cancel_host);
989146998Sdes			cancel_host = NULL;
99098675Sdes		}
991146998Sdes		if (cancel_port == 0) {
992146998Sdes			logit("Bad forwarding close port");
993137015Sdes			goto out;
994137015Sdes		}
995146998Sdes		channel_request_rforward_cancel(cancel_host, cancel_port);
996137015Sdes	} else {
997146998Sdes		if (!parse_forward(&fwd, s)) {
998137015Sdes			logit("Bad forwarding specification.");
999137015Sdes			goto out;
1000137015Sdes		}
1001137015Sdes		if (local) {
1002146998Sdes			if (channel_setup_local_fwd_listener(fwd.listen_host,
1003146998Sdes			    fwd.listen_port, fwd.connect_host,
1004146998Sdes			    fwd.connect_port, options.gateway_ports) < 0) {
1005137015Sdes				logit("Port forwarding failed.");
1006137015Sdes				goto out;
1007137015Sdes			}
1008146998Sdes		} else {
1009162852Sdes			if (channel_request_remote_forwarding(fwd.listen_host,
1010146998Sdes			    fwd.listen_port, fwd.connect_host,
1011162852Sdes			    fwd.connect_port) < 0) {
1012162852Sdes				logit("Port forwarding failed.");
1013162852Sdes				goto out;
1014162852Sdes			}
1015146998Sdes		}
1016146998Sdes
1017137015Sdes		logit("Forwarding port.");
1018137015Sdes	}
1019137015Sdes
102098675Sdesout:
102198675Sdes	signal(SIGINT, handler);
102298675Sdes	enter_raw_mode();
102398675Sdes	if (cmd)
102498675Sdes		xfree(cmd);
102598675Sdes}
102698675Sdes
102765668Skris/* process the characters one by one */
102892555Sdesstatic int
102965668Skrisprocess_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len)
103065668Skris{
103165668Skris	char string[1024];
103265668Skris	pid_t pid;
103365668Skris	int bytes = 0;
103476259Sgreen	u_int i;
103576259Sgreen	u_char ch;
103665668Skris	char *s;
103765668Skris
1038149749Sdes	if (len <= 0)
1039149749Sdes		return (0);
1040149749Sdes
1041149749Sdes	for (i = 0; i < (u_int)len; i++) {
104265668Skris		/* Get one character at a time. */
104365668Skris		ch = buf[i];
104465668Skris
104565668Skris		if (escape_pending) {
104665668Skris			/* We have previously seen an escape character. */
104765668Skris			/* Clear the flag now. */
104865668Skris			escape_pending = 0;
104965668Skris
105065668Skris			/* Process the escaped character. */
105165668Skris			switch (ch) {
105265668Skris			case '.':
105365668Skris				/* Terminate the connection. */
105465668Skris				snprintf(string, sizeof string, "%c.\r\n", escape_char);
105565668Skris				buffer_append(berr, string, strlen(string));
105665668Skris
105765668Skris				quit_pending = 1;
105865668Skris				return -1;
105965668Skris
106065668Skris			case 'Z' - 64:
106165668Skris				/* Suspend the program. */
106265668Skris				/* Print a message to that effect to the user. */
106365668Skris				snprintf(string, sizeof string, "%c^Z [suspend ssh]\r\n", escape_char);
106465668Skris				buffer_append(berr, string, strlen(string));
106565668Skris
106665668Skris				/* Restore terminal modes and suspend. */
106765668Skris				client_suspend_self(bin, bout, berr);
106865668Skris
106965668Skris				/* We have been continued. */
107065668Skris				continue;
107165668Skris
1072124208Sdes			case 'B':
1073124208Sdes				if (compat20) {
1074124208Sdes					snprintf(string, sizeof string,
1075124208Sdes					    "%cB\r\n", escape_char);
1076124208Sdes					buffer_append(berr, string,
1077124208Sdes					    strlen(string));
1078124208Sdes					channel_request_start(session_ident,
1079124208Sdes					    "break", 0);
1080124208Sdes					packet_put_int(1000);
1081124208Sdes					packet_send();
1082124208Sdes				}
1083124208Sdes				continue;
1084124208Sdes
108576259Sgreen			case 'R':
108676259Sgreen				if (compat20) {
108776259Sgreen					if (datafellows & SSH_BUG_NOREKEY)
1088124208Sdes						logit("Server does not support re-keying");
108976259Sgreen					else
109076259Sgreen						need_rekeying = 1;
109176259Sgreen				}
109276259Sgreen				continue;
109376259Sgreen
109465668Skris			case '&':
109565668Skris				/*
109665668Skris				 * Detach the program (continue to serve connections,
109765668Skris				 * but put in background and no more new connections).
109865668Skris				 */
109965668Skris				/* Restore tty modes. */
110065668Skris				leave_raw_mode();
110165668Skris
110265668Skris				/* Stop listening for new connections. */
110365668Skris				channel_stop_listening();
110465668Skris
110592555Sdes				snprintf(string, sizeof string,
110692555Sdes				    "%c& [backgrounded]\n", escape_char);
110792555Sdes				buffer_append(berr, string, strlen(string));
110865668Skris
110965668Skris				/* Fork into background. */
111065668Skris				pid = fork();
111165668Skris				if (pid < 0) {
111265668Skris					error("fork: %.100s", strerror(errno));
111365668Skris					continue;
111465668Skris				}
111565668Skris				if (pid != 0) {	/* This is the parent. */
111665668Skris					/* The parent just exits. */
111765668Skris					exit(0);
111865668Skris				}
111965668Skris				/* The child continues serving connections. */
112092555Sdes				if (compat20) {
112192555Sdes					buffer_append(bin, "\004", 1);
112292555Sdes					/* fake EOF on stdin */
112392555Sdes					return -1;
112492555Sdes				} else if (!stdin_eof) {
112592555Sdes					/*
112692555Sdes					 * Sending SSH_CMSG_EOF alone does not always appear
112792555Sdes					 * to be enough.  So we try to send an EOF character
112892555Sdes					 * first.
112992555Sdes					 */
113092555Sdes					packet_start(SSH_CMSG_STDIN_DATA);
113192555Sdes					packet_put_string("\004", 1);
113292555Sdes					packet_send();
113392555Sdes					/* Close stdin. */
113492555Sdes					stdin_eof = 1;
113592555Sdes					if (buffer_len(bin) == 0) {
113692555Sdes						packet_start(SSH_CMSG_EOF);
113792555Sdes						packet_send();
113892555Sdes					}
113992555Sdes				}
114092555Sdes				continue;
114165668Skris
114265668Skris			case '?':
114365668Skris				snprintf(string, sizeof string,
114465668Skris"%c?\r\n\
114565668SkrisSupported escape sequences:\r\n\
1146106121Sdes%c.  - terminate connection\r\n\
1147124208Sdes%cB  - send a BREAK to the remote system\r\n\
1148106121Sdes%cC  - open a command line\r\n\
1149106121Sdes%cR  - Request rekey (SSH protocol 2 only)\r\n\
1150106121Sdes%c^Z - suspend ssh\r\n\
1151106121Sdes%c#  - list forwarded connections\r\n\
1152106121Sdes%c&  - background ssh (when waiting for connections to terminate)\r\n\
1153106121Sdes%c?  - this message\r\n\
1154106121Sdes%c%c  - send the escape character by typing it twice\r\n\
115565668Skris(Note that escapes are only recognized immediately after newline.)\r\n",
1156106121Sdes				    escape_char, escape_char, escape_char, escape_char,
1157106121Sdes				    escape_char, escape_char, escape_char, escape_char,
1158124208Sdes				    escape_char, escape_char, escape_char);
115965668Skris				buffer_append(berr, string, strlen(string));
116065668Skris				continue;
116165668Skris
116265668Skris			case '#':
116365668Skris				snprintf(string, sizeof string, "%c#\r\n", escape_char);
116465668Skris				buffer_append(berr, string, strlen(string));
116565668Skris				s = channel_open_message();
116665668Skris				buffer_append(berr, s, strlen(s));
116765668Skris				xfree(s);
116865668Skris				continue;
116965668Skris
117098675Sdes			case 'C':
117198675Sdes				process_cmdline();
117298675Sdes				continue;
117398675Sdes
117465668Skris			default:
117565668Skris				if (ch != escape_char) {
117665668Skris					buffer_put_char(bin, escape_char);
117765668Skris					bytes++;
117865668Skris				}
117965668Skris				/* Escaped characters fall through here */
118065668Skris				break;
118165668Skris			}
118265668Skris		} else {
118365668Skris			/*
118465668Skris			 * The previous character was not an escape char. Check if this
118565668Skris			 * is an escape.
118665668Skris			 */
118765668Skris			if (last_was_cr && ch == escape_char) {
118865668Skris				/* It is. Set the flag and continue to next character. */
118965668Skris				escape_pending = 1;
119065668Skris				continue;
119165668Skris			}
119265668Skris		}
119365668Skris
119465668Skris		/*
119565668Skris		 * Normal character.  Record whether it was a newline,
119665668Skris		 * and append it to the buffer.
119765668Skris		 */
119865668Skris		last_was_cr = (ch == '\r' || ch == '\n');
119965668Skris		buffer_put_char(bin, ch);
120065668Skris		bytes++;
120165668Skris	}
120265668Skris	return bytes;
120365668Skris}
120465668Skris
120592555Sdesstatic void
1206162852Sdesclient_process_input(fd_set *readset)
120760573Skris{
120860573Skris	int len;
120965668Skris	char buf[8192];
121060573Skris
121157429Smarkm	/* Read input from stdin. */
121257429Smarkm	if (FD_ISSET(fileno(stdin), readset)) {
121357429Smarkm		/* Read as much as possible. */
121457429Smarkm		len = read(fileno(stdin), buf, sizeof(buf));
121576259Sgreen		if (len < 0 && (errno == EAGAIN || errno == EINTR))
121676259Sgreen			return;		/* we'll try again later */
121757429Smarkm		if (len <= 0) {
121857429Smarkm			/*
121957429Smarkm			 * Received EOF or error.  They are treated
122057429Smarkm			 * similarly, except that an error message is printed
122157429Smarkm			 * if it was an error condition.
122257429Smarkm			 */
122357429Smarkm			if (len < 0) {
122457429Smarkm				snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno));
122557429Smarkm				buffer_append(&stderr_buffer, buf, strlen(buf));
122657429Smarkm			}
122757429Smarkm			/* Mark that we have seen EOF. */
122857429Smarkm			stdin_eof = 1;
122957429Smarkm			/*
123057429Smarkm			 * Send an EOF message to the server unless there is
123157429Smarkm			 * data in the buffer.  If there is data in the
123257429Smarkm			 * buffer, no message will be sent now.  Code
123357429Smarkm			 * elsewhere will send the EOF when the buffer
123457429Smarkm			 * becomes empty if stdin_eof is set.
123557429Smarkm			 */
123657429Smarkm			if (buffer_len(&stdin_buffer) == 0) {
123757429Smarkm				packet_start(SSH_CMSG_EOF);
123857429Smarkm				packet_send();
123957429Smarkm			}
124092555Sdes		} else if (escape_char == SSH_ESCAPECHAR_NONE) {
124157429Smarkm			/*
124257429Smarkm			 * Normal successful read, and no escape character.
124357429Smarkm			 * Just append the data to buffer.
124457429Smarkm			 */
124557429Smarkm			buffer_append(&stdin_buffer, buf, len);
124657429Smarkm		} else {
124757429Smarkm			/*
124857429Smarkm			 * Normal, successful read.  But we have an escape character
124957429Smarkm			 * and have to process the characters one by one.
125057429Smarkm			 */
125176259Sgreen			if (process_escapes(&stdin_buffer, &stdout_buffer,
125276259Sgreen			    &stderr_buffer, buf, len) == -1)
125365668Skris				return;
125457429Smarkm		}
125557429Smarkm	}
125657429Smarkm}
125757429Smarkm
125892555Sdesstatic void
1259162852Sdesclient_process_output(fd_set *writeset)
126057429Smarkm{
126157429Smarkm	int len;
126257429Smarkm	char buf[100];
126357429Smarkm
126457429Smarkm	/* Write buffered output to stdout. */
126557429Smarkm	if (FD_ISSET(fileno(stdout), writeset)) {
126657429Smarkm		/* Write as much data as possible. */
126757429Smarkm		len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
126857429Smarkm		    buffer_len(&stdout_buffer));
126957429Smarkm		if (len <= 0) {
127076259Sgreen			if (errno == EINTR || errno == EAGAIN)
127157429Smarkm				len = 0;
127257429Smarkm			else {
127357429Smarkm				/*
127457429Smarkm				 * An error or EOF was encountered.  Put an
127557429Smarkm				 * error message to stderr buffer.
127657429Smarkm				 */
127757429Smarkm				snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno));
127857429Smarkm				buffer_append(&stderr_buffer, buf, strlen(buf));
127957429Smarkm				quit_pending = 1;
128057429Smarkm				return;
128157429Smarkm			}
128257429Smarkm		}
128357429Smarkm		/* Consume printed data from the buffer. */
128457429Smarkm		buffer_consume(&stdout_buffer, len);
128576259Sgreen		stdout_bytes += len;
128657429Smarkm	}
128757429Smarkm	/* Write buffered output to stderr. */
128857429Smarkm	if (FD_ISSET(fileno(stderr), writeset)) {
128957429Smarkm		/* Write as much data as possible. */
129057429Smarkm		len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
129157429Smarkm		    buffer_len(&stderr_buffer));
129257429Smarkm		if (len <= 0) {
129376259Sgreen			if (errno == EINTR || errno == EAGAIN)
129457429Smarkm				len = 0;
129557429Smarkm			else {
129657429Smarkm				/* EOF or error, but can't even print error message. */
129757429Smarkm				quit_pending = 1;
129857429Smarkm				return;
129957429Smarkm			}
130057429Smarkm		}
130157429Smarkm		/* Consume printed characters from the buffer. */
130257429Smarkm		buffer_consume(&stderr_buffer, len);
130376259Sgreen		stderr_bytes += len;
130457429Smarkm	}
130557429Smarkm}
130657429Smarkm
130757429Smarkm/*
130860573Skris * Get packets from the connection input buffer, and process them as long as
130960573Skris * there are packets available.
131060573Skris *
131160573Skris * Any unknown packets received during the actual
131260573Skris * session cause the session to terminate.  This is
131360573Skris * intended to make debugging easier since no
131460573Skris * confirmations are sent.  Any compatible protocol
131560573Skris * extensions must be negotiated during the
131660573Skris * preparatory phase.
131760573Skris */
131860573Skris
131992555Sdesstatic void
132076259Sgreenclient_process_buffered_input_packets(void)
132160573Skris{
132276259Sgreen	dispatch_run(DISPATCH_NONBLOCK, &quit_pending, compat20 ? xxx_kex : NULL);
132360573Skris}
132460573Skris
132565668Skris/* scan buf[] for '~' before sending data to the peer */
132665668Skris
132792555Sdesstatic int
132865668Skrissimple_escape_filter(Channel *c, char *buf, int len)
132965668Skris{
133065668Skris	/* XXX we assume c->extended is writeable */
133165668Skris	return process_escapes(&c->input, &c->output, &c->extended, buf, len);
133265668Skris}
133365668Skris
133492555Sdesstatic void
133576259Sgreenclient_channel_closed(int id, void *arg)
133676259Sgreen{
133792555Sdes	channel_cancel_cleanup(id);
133876259Sgreen	session_closed = 1;
1339126274Sdes	leave_raw_mode();
134076259Sgreen}
134176259Sgreen
134260573Skris/*
134357429Smarkm * Implements the interactive session with the server.  This is called after
134457429Smarkm * the user has been authenticated, and a command has been started on the
134592555Sdes * remote host.  If escape_char != SSH_ESCAPECHAR_NONE, it is the character
134692555Sdes * used as an escape character for terminating or suspending the session.
134757429Smarkm */
134857429Smarkm
134960573Skrisint
135065668Skrisclient_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
135157429Smarkm{
135276259Sgreen	fd_set *readset = NULL, *writeset = NULL;
135357429Smarkm	double start_time, total_time;
1354137015Sdes	int max_fd = 0, max_fd2 = 0, len, rekeying = 0;
1355137015Sdes	u_int nalloc = 0;
135657429Smarkm	char buf[100];
135757429Smarkm
135857429Smarkm	debug("Entering interactive session.");
135957429Smarkm
136057429Smarkm	start_time = get_current_time();
136157429Smarkm
136257429Smarkm	/* Initialize variables. */
136357429Smarkm	escape_pending = 0;
136457429Smarkm	last_was_cr = 1;
136557429Smarkm	exit_status = -1;
136657429Smarkm	stdin_eof = 0;
136757429Smarkm	buffer_high = 64 * 1024;
136857429Smarkm	connection_in = packet_get_connection_in();
136957429Smarkm	connection_out = packet_get_connection_out();
137076259Sgreen	max_fd = MAX(connection_in, connection_out);
1371137015Sdes	if (control_fd != -1)
1372137015Sdes		max_fd = MAX(max_fd, control_fd);
137376259Sgreen
137476259Sgreen	if (!compat20) {
137576259Sgreen		/* enable nonblocking unless tty */
137676259Sgreen		if (!isatty(fileno(stdin)))
137776259Sgreen			set_nonblock(fileno(stdin));
137876259Sgreen		if (!isatty(fileno(stdout)))
137976259Sgreen			set_nonblock(fileno(stdout));
138076259Sgreen		if (!isatty(fileno(stderr)))
138176259Sgreen			set_nonblock(fileno(stderr));
138276259Sgreen		max_fd = MAX(max_fd, fileno(stdin));
138376259Sgreen		max_fd = MAX(max_fd, fileno(stdout));
138476259Sgreen		max_fd = MAX(max_fd, fileno(stderr));
138576259Sgreen	}
138657429Smarkm	stdin_bytes = 0;
138757429Smarkm	stdout_bytes = 0;
138857429Smarkm	stderr_bytes = 0;
138957429Smarkm	quit_pending = 0;
139057429Smarkm	escape_char = escape_char_arg;
139157429Smarkm
139257429Smarkm	/* Initialize buffers. */
139357429Smarkm	buffer_init(&stdin_buffer);
139457429Smarkm	buffer_init(&stdout_buffer);
139557429Smarkm	buffer_init(&stderr_buffer);
139657429Smarkm
139760573Skris	client_init_dispatch();
139860573Skris
1399113908Sdes	/*
1400113908Sdes	 * Set signal handlers, (e.g. to restore non-blocking mode)
1401113908Sdes	 * but don't overwrite SIG_IGN, matches behaviour from rsh(1)
1402113908Sdes	 */
1403146998Sdes	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
1404146998Sdes		signal(SIGHUP, signal_handler);
1405113908Sdes	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1406113908Sdes		signal(SIGINT, signal_handler);
1407113908Sdes	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
1408113908Sdes		signal(SIGQUIT, signal_handler);
1409113908Sdes	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
1410113908Sdes		signal(SIGTERM, signal_handler);
1411146998Sdes	signal(SIGWINCH, window_change_handler);
141257429Smarkm
141357429Smarkm	if (have_pty)
141457429Smarkm		enter_raw_mode();
141557429Smarkm
141676259Sgreen	if (compat20) {
141776259Sgreen		session_ident = ssh2_chan_id;
141892555Sdes		if (escape_char != SSH_ESCAPECHAR_NONE)
141976259Sgreen			channel_register_filter(session_ident,
1420157016Sdes			    simple_escape_filter, NULL);
142176259Sgreen		if (session_ident != -1)
142276259Sgreen			channel_register_cleanup(session_ident,
1423157016Sdes			    client_channel_closed, 0);
142476259Sgreen	} else {
142576259Sgreen		/* Check if we should immediately send eof on stdin. */
142660573Skris		client_check_initial_eof_on_stdin();
142776259Sgreen	}
142857429Smarkm
142957429Smarkm	/* Main loop of the client for the interactive session mode. */
143057429Smarkm	while (!quit_pending) {
143157429Smarkm
143257429Smarkm		/* Process buffered packets sent by the server. */
143357429Smarkm		client_process_buffered_input_packets();
143457429Smarkm
143576259Sgreen		if (compat20 && session_closed && !channel_still_open())
143660573Skris			break;
143760573Skris
143876259Sgreen		rekeying = (xxx_kex != NULL && !xxx_kex->done);
143957429Smarkm
144076259Sgreen		if (rekeying) {
144176259Sgreen			debug("rekeying in progress");
144276259Sgreen		} else {
144376259Sgreen			/*
144476259Sgreen			 * Make packets of buffered stdin data, and buffer
144576259Sgreen			 * them for sending to the server.
144676259Sgreen			 */
144776259Sgreen			if (!compat20)
144876259Sgreen				client_make_packets_from_stdin_data();
144957429Smarkm
145076259Sgreen			/*
145176259Sgreen			 * Make packets from buffered channel data, and
145276259Sgreen			 * enqueue them for sending to the server.
145376259Sgreen			 */
145476259Sgreen			if (packet_not_very_much_data_to_write())
145576259Sgreen				channel_output_poll();
145657429Smarkm
145776259Sgreen			/*
145876259Sgreen			 * Check if the window size has changed, and buffer a
145976259Sgreen			 * message about it to the server if so.
146076259Sgreen			 */
146176259Sgreen			client_check_window_change();
146257429Smarkm
146376259Sgreen			if (quit_pending)
146476259Sgreen				break;
146576259Sgreen		}
146657429Smarkm		/*
146757429Smarkm		 * Wait until we have something to do (something becomes
146857429Smarkm		 * available on one of the descriptors).
146957429Smarkm		 */
147092555Sdes		max_fd2 = max_fd;
147176259Sgreen		client_wait_until_can_do_something(&readset, &writeset,
147292555Sdes		    &max_fd2, &nalloc, rekeying);
147357429Smarkm
147457429Smarkm		if (quit_pending)
147557429Smarkm			break;
147657429Smarkm
147776259Sgreen		/* Do channel operations unless rekeying in progress. */
147876259Sgreen		if (!rekeying) {
147976259Sgreen			channel_after_select(readset, writeset);
1480124208Sdes			if (need_rekeying || packet_need_rekeying()) {
1481124208Sdes				debug("need rekeying");
148276259Sgreen				xxx_kex->done = 0;
148376259Sgreen				kex_send_kexinit(xxx_kex);
148476259Sgreen				need_rekeying = 0;
148576259Sgreen			}
148676259Sgreen		}
148776259Sgreen
148860573Skris		/* Buffer input from the connection.  */
148976259Sgreen		client_process_net_input(readset);
149057429Smarkm
1491137015Sdes		/* Accept control connections.  */
1492137015Sdes		client_process_control(readset);
1493137015Sdes
149460573Skris		if (quit_pending)
149560573Skris			break;
149657429Smarkm
149760573Skris		if (!compat20) {
149860573Skris			/* Buffer data from stdin */
149976259Sgreen			client_process_input(readset);
150060573Skris			/*
150160573Skris			 * Process output to stdout and stderr.  Output to
150260573Skris			 * the connection is processed elsewhere (above).
150360573Skris			 */
150476259Sgreen			client_process_output(writeset);
150560573Skris		}
150660573Skris
150757429Smarkm		/* Send as much buffered packet data as possible to the sender. */
150876259Sgreen		if (FD_ISSET(connection_out, writeset))
150957429Smarkm			packet_write_poll();
151057429Smarkm	}
151176259Sgreen	if (readset)
151276259Sgreen		xfree(readset);
151376259Sgreen	if (writeset)
151476259Sgreen		xfree(writeset);
151557429Smarkm
151657429Smarkm	/* Terminate the session. */
151757429Smarkm
151857429Smarkm	/* Stop watching for window change. */
1519146998Sdes	signal(SIGWINCH, SIG_DFL);
152057429Smarkm
152192555Sdes	channel_free_all();
152257429Smarkm
152392555Sdes	if (have_pty)
152492555Sdes		leave_raw_mode();
152592555Sdes
152692555Sdes	/* restore blocking io */
152792555Sdes	if (!isatty(fileno(stdin)))
152892555Sdes		unset_nonblock(fileno(stdin));
152992555Sdes	if (!isatty(fileno(stdout)))
153092555Sdes		unset_nonblock(fileno(stdout));
153192555Sdes	if (!isatty(fileno(stderr)))
153292555Sdes		unset_nonblock(fileno(stderr));
153392555Sdes
1534126274Sdes	/*
1535126274Sdes	 * If there was no shell or command requested, there will be no remote
1536126274Sdes	 * exit status to be returned.  In that case, clear error code if the
1537126274Sdes	 * connection was deliberately terminated at this end.
1538126274Sdes	 */
1539126274Sdes	if (no_shell_flag && received_signal == SIGTERM) {
1540126274Sdes		received_signal = 0;
1541126274Sdes		exit_status = 0;
154292555Sdes	}
154392555Sdes
1544126274Sdes	if (received_signal)
1545126274Sdes		fatal("Killed by signal %d.", (int) received_signal);
1546126274Sdes
154757429Smarkm	/*
154857429Smarkm	 * In interactive mode (with pseudo tty) display a message indicating
154957429Smarkm	 * that the connection has been closed.
155057429Smarkm	 */
155157429Smarkm	if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) {
155257429Smarkm		snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host);
155357429Smarkm		buffer_append(&stderr_buffer, buf, strlen(buf));
155457429Smarkm	}
155592555Sdes
155657429Smarkm	/* Output any buffered data for stdout. */
155757429Smarkm	while (buffer_len(&stdout_buffer) > 0) {
155857429Smarkm		len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
155957429Smarkm		    buffer_len(&stdout_buffer));
156057429Smarkm		if (len <= 0) {
156157429Smarkm			error("Write failed flushing stdout buffer.");
156257429Smarkm			break;
156357429Smarkm		}
156457429Smarkm		buffer_consume(&stdout_buffer, len);
156576259Sgreen		stdout_bytes += len;
156657429Smarkm	}
156757429Smarkm
156857429Smarkm	/* Output any buffered data for stderr. */
156957429Smarkm	while (buffer_len(&stderr_buffer) > 0) {
157057429Smarkm		len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
157157429Smarkm		    buffer_len(&stderr_buffer));
157257429Smarkm		if (len <= 0) {
157357429Smarkm			error("Write failed flushing stderr buffer.");
157457429Smarkm			break;
157557429Smarkm		}
157657429Smarkm		buffer_consume(&stderr_buffer, len);
157776259Sgreen		stderr_bytes += len;
157857429Smarkm	}
157957429Smarkm
158057429Smarkm	/* Clear and free any buffers. */
158157429Smarkm	memset(buf, 0, sizeof(buf));
158257429Smarkm	buffer_free(&stdin_buffer);
158357429Smarkm	buffer_free(&stdout_buffer);
158457429Smarkm	buffer_free(&stderr_buffer);
158557429Smarkm
158657429Smarkm	/* Report bytes transferred, and transfer rates. */
158757429Smarkm	total_time = get_current_time() - start_time;
158857429Smarkm	debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds",
158992555Sdes	    stdin_bytes, stdout_bytes, stderr_bytes, total_time);
159057429Smarkm	if (total_time > 0)
159157429Smarkm		debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f",
159292555Sdes		    stdin_bytes / total_time, stdout_bytes / total_time,
159392555Sdes		    stderr_bytes / total_time);
159457429Smarkm
159557429Smarkm	/* Return the exit status of the program. */
159657429Smarkm	debug("Exit status %d", exit_status);
159757429Smarkm	return exit_status;
159857429Smarkm}
159960573Skris
160060573Skris/*********/
160160573Skris
160292555Sdesstatic void
160392555Sdesclient_input_stdout_data(int type, u_int32_t seq, void *ctxt)
160460573Skris{
160576259Sgreen	u_int data_len;
160660573Skris	char *data = packet_get_string(&data_len);
160792555Sdes	packet_check_eom();
160860573Skris	buffer_append(&stdout_buffer, data, data_len);
160960573Skris	memset(data, 0, data_len);
161060573Skris	xfree(data);
161160573Skris}
161292555Sdesstatic void
161392555Sdesclient_input_stderr_data(int type, u_int32_t seq, void *ctxt)
161460573Skris{
161576259Sgreen	u_int data_len;
161660573Skris	char *data = packet_get_string(&data_len);
161792555Sdes	packet_check_eom();
161860573Skris	buffer_append(&stderr_buffer, data, data_len);
161960573Skris	memset(data, 0, data_len);
162060573Skris	xfree(data);
162160573Skris}
162292555Sdesstatic void
162392555Sdesclient_input_exit_status(int type, u_int32_t seq, void *ctxt)
162460573Skris{
162560573Skris	exit_status = packet_get_int();
162692555Sdes	packet_check_eom();
162760573Skris	/* Acknowledge the exit. */
162860573Skris	packet_start(SSH_CMSG_EXIT_CONFIRMATION);
162960573Skris	packet_send();
163060573Skris	/*
163160573Skris	 * Must wait for packet to be sent since we are
163260573Skris	 * exiting the loop.
163360573Skris	 */
163460573Skris	packet_write_wait();
163560573Skris	/* Flag that we want to exit. */
163660573Skris	quit_pending = 1;
163760573Skris}
1638126274Sdesstatic void
1639126274Sdesclient_input_agent_open(int type, u_int32_t seq, void *ctxt)
1640126274Sdes{
1641126274Sdes	Channel *c = NULL;
1642126274Sdes	int remote_id, sock;
164360573Skris
1644126274Sdes	/* Read the remote channel number from the message. */
1645126274Sdes	remote_id = packet_get_int();
1646126274Sdes	packet_check_eom();
1647126274Sdes
1648126274Sdes	/*
1649126274Sdes	 * Get a connection to the local authentication agent (this may again
1650126274Sdes	 * get forwarded).
1651126274Sdes	 */
1652126274Sdes	sock = ssh_get_authentication_socket();
1653126274Sdes
1654126274Sdes	/*
1655126274Sdes	 * If we could not connect the agent, send an error message back to
1656126274Sdes	 * the server. This should never happen unless the agent dies,
1657126274Sdes	 * because authentication forwarding is only enabled if we have an
1658126274Sdes	 * agent.
1659126274Sdes	 */
1660126274Sdes	if (sock >= 0) {
1661126274Sdes		c = channel_new("", SSH_CHANNEL_OPEN, sock, sock,
1662126274Sdes		    -1, 0, 0, 0, "authentication agent connection", 1);
1663126274Sdes		c->remote_id = remote_id;
1664126274Sdes		c->force_drain = 1;
1665126274Sdes	}
1666126274Sdes	if (c == NULL) {
1667126274Sdes		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
1668126274Sdes		packet_put_int(remote_id);
1669126274Sdes	} else {
1670126274Sdes		/* Send a confirmation to the remote host. */
1671126274Sdes		debug("Forwarding authentication connection.");
1672126274Sdes		packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
1673126274Sdes		packet_put_int(remote_id);
1674126274Sdes		packet_put_int(c->self);
1675126274Sdes	}
1676126274Sdes	packet_send();
1677126274Sdes}
1678126274Sdes
167992555Sdesstatic Channel *
168076259Sgreenclient_request_forwarded_tcpip(const char *request_type, int rchan)
168176259Sgreen{
1682106121Sdes	Channel *c = NULL;
168376259Sgreen	char *listen_address, *originator_address;
168476259Sgreen	int listen_port, originator_port;
168592555Sdes	int sock;
168676259Sgreen
168776259Sgreen	/* Get rest of the packet */
168876259Sgreen	listen_address = packet_get_string(NULL);
168976259Sgreen	listen_port = packet_get_int();
169076259Sgreen	originator_address = packet_get_string(NULL);
169176259Sgreen	originator_port = packet_get_int();
169292555Sdes	packet_check_eom();
169376259Sgreen
169476259Sgreen	debug("client_request_forwarded_tcpip: listen %s port %d, originator %s port %d",
169576259Sgreen	    listen_address, listen_port, originator_address, originator_port);
169676259Sgreen
169792555Sdes	sock = channel_connect_by_listen_address(listen_port);
169892555Sdes	if (sock < 0) {
169992555Sdes		xfree(originator_address);
170092555Sdes		xfree(listen_address);
170192555Sdes		return NULL;
170276259Sgreen	}
170392555Sdes	c = channel_new("forwarded-tcpip",
170492555Sdes	    SSH_CHANNEL_CONNECTING, sock, sock, -1,
170592555Sdes	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0,
1706124208Sdes	    originator_address, 1);
170776259Sgreen	xfree(originator_address);
170876259Sgreen	xfree(listen_address);
170976259Sgreen	return c;
171076259Sgreen}
171176259Sgreen
1712106121Sdesstatic Channel *
171376259Sgreenclient_request_x11(const char *request_type, int rchan)
171476259Sgreen{
171576259Sgreen	Channel *c = NULL;
171676259Sgreen	char *originator;
171776259Sgreen	int originator_port;
171892555Sdes	int sock;
171976259Sgreen
172076259Sgreen	if (!options.forward_x11) {
172176259Sgreen		error("Warning: ssh server tried X11 forwarding.");
1722157016Sdes		error("Warning: this is probably a break-in attempt by a malicious server.");
172376259Sgreen		return NULL;
172476259Sgreen	}
172576259Sgreen	originator = packet_get_string(NULL);
172676259Sgreen	if (datafellows & SSH_BUG_X11FWD) {
172776259Sgreen		debug2("buggy server: x11 request w/o originator_port");
172876259Sgreen		originator_port = 0;
172976259Sgreen	} else {
173076259Sgreen		originator_port = packet_get_int();
173176259Sgreen	}
173292555Sdes	packet_check_eom();
173376259Sgreen	/* XXX check permission */
173476259Sgreen	debug("client_request_x11: request from %s %d", originator,
173576259Sgreen	    originator_port);
173692555Sdes	xfree(originator);
173776259Sgreen	sock = x11_connect_display();
173892555Sdes	if (sock < 0)
173992555Sdes		return NULL;
174092555Sdes	c = channel_new("x11",
174192555Sdes	    SSH_CHANNEL_X11_OPEN, sock, sock, -1,
1742124208Sdes	    CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1);
174392555Sdes	c->force_drain = 1;
174476259Sgreen	return c;
174576259Sgreen}
174676259Sgreen
1747106121Sdesstatic Channel *
174876259Sgreenclient_request_agent(const char *request_type, int rchan)
174976259Sgreen{
175076259Sgreen	Channel *c = NULL;
175192555Sdes	int sock;
175276259Sgreen
175376259Sgreen	if (!options.forward_agent) {
175476259Sgreen		error("Warning: ssh server tried agent forwarding.");
1755157016Sdes		error("Warning: this is probably a break-in attempt by a malicious server.");
175676259Sgreen		return NULL;
175776259Sgreen	}
175876259Sgreen	sock =  ssh_get_authentication_socket();
175992555Sdes	if (sock < 0)
176092555Sdes		return NULL;
176192555Sdes	c = channel_new("authentication agent connection",
176292555Sdes	    SSH_CHANNEL_OPEN, sock, sock, -1,
176392555Sdes	    CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0,
1764124208Sdes	    "authentication agent connection", 1);
176592555Sdes	c->force_drain = 1;
176676259Sgreen	return c;
176776259Sgreen}
176876259Sgreen
176960573Skris/* XXXX move to generic input handler */
177092555Sdesstatic void
177192555Sdesclient_input_channel_open(int type, u_int32_t seq, void *ctxt)
177260573Skris{
177360573Skris	Channel *c = NULL;
177460573Skris	char *ctype;
177560573Skris	int rchan;
177699060Sdes	u_int rmaxpack, rwindow, len;
177760573Skris
177860573Skris	ctype = packet_get_string(&len);
177960573Skris	rchan = packet_get_int();
178060573Skris	rwindow = packet_get_int();
178160573Skris	rmaxpack = packet_get_int();
178260573Skris
178360573Skris	debug("client_input_channel_open: ctype %s rchan %d win %d max %d",
178460573Skris	    ctype, rchan, rwindow, rmaxpack);
178560573Skris
178676259Sgreen	if (strcmp(ctype, "forwarded-tcpip") == 0) {
178776259Sgreen		c = client_request_forwarded_tcpip(ctype, rchan);
178876259Sgreen	} else if (strcmp(ctype, "x11") == 0) {
178976259Sgreen		c = client_request_x11(ctype, rchan);
179076259Sgreen	} else if (strcmp(ctype, "auth-agent@openssh.com") == 0) {
179176259Sgreen		c = client_request_agent(ctype, rchan);
179260573Skris	}
179360573Skris/* XXX duplicate : */
179460573Skris	if (c != NULL) {
179560573Skris		debug("confirm %s", ctype);
179660573Skris		c->remote_id = rchan;
179760573Skris		c->remote_window = rwindow;
179860573Skris		c->remote_maxpacket = rmaxpack;
179992555Sdes		if (c->type != SSH_CHANNEL_CONNECTING) {
180092555Sdes			packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
180192555Sdes			packet_put_int(c->remote_id);
180292555Sdes			packet_put_int(c->self);
180392555Sdes			packet_put_int(c->local_window);
180492555Sdes			packet_put_int(c->local_maxpacket);
180592555Sdes			packet_send();
180692555Sdes		}
180760573Skris	} else {
180860573Skris		debug("failure %s", ctype);
180960573Skris		packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
181060573Skris		packet_put_int(rchan);
181160573Skris		packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED);
181292555Sdes		if (!(datafellows & SSH_BUG_OPENFAILURE)) {
181392555Sdes			packet_put_cstring("open failed");
181492555Sdes			packet_put_cstring("");
181592555Sdes		}
181660573Skris		packet_send();
181760573Skris	}
181860573Skris	xfree(ctype);
181960573Skris}
182092555Sdesstatic void
182192555Sdesclient_input_channel_req(int type, u_int32_t seq, void *ctxt)
182276259Sgreen{
182376259Sgreen	Channel *c = NULL;
1824137015Sdes	int exitval, id, reply, success = 0;
182576259Sgreen	char *rtype;
182660573Skris
182776259Sgreen	id = packet_get_int();
182876259Sgreen	rtype = packet_get_string(NULL);
182976259Sgreen	reply = packet_get_char();
183076259Sgreen
183176259Sgreen	debug("client_input_channel_req: channel %d rtype %s reply %d",
183276259Sgreen	    id, rtype, reply);
183376259Sgreen
1834137015Sdes	if (id == -1) {
1835137015Sdes		error("client_input_channel_req: request for channel -1");
1836137015Sdes	} else if ((c = channel_lookup(id)) == NULL) {
183776259Sgreen		error("client_input_channel_req: channel %d: unknown channel", id);
183876259Sgreen	} else if (strcmp(rtype, "exit-status") == 0) {
1839137015Sdes		exitval = packet_get_int();
1840137015Sdes		if (id == session_ident) {
1841137015Sdes			success = 1;
1842137015Sdes			exit_status = exitval;
1843137015Sdes		} else if (c->ctl_fd == -1) {
1844137015Sdes			error("client_input_channel_req: unexpected channel %d",
1845137015Sdes			    session_ident);
1846137015Sdes		} else {
1847137015Sdes			atomicio(vwrite, c->ctl_fd, &exitval, sizeof(exitval));
1848137015Sdes			success = 1;
1849137015Sdes		}
185092555Sdes		packet_check_eom();
185176259Sgreen	}
185276259Sgreen	if (reply) {
185376259Sgreen		packet_start(success ?
185476259Sgreen		    SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
1855137015Sdes		packet_put_int(id);
185676259Sgreen		packet_send();
185776259Sgreen	}
185876259Sgreen	xfree(rtype);
185976259Sgreen}
186092555Sdesstatic void
186192555Sdesclient_input_global_request(int type, u_int32_t seq, void *ctxt)
186292555Sdes{
186392555Sdes	char *rtype;
186492555Sdes	int want_reply;
186592555Sdes	int success = 0;
186676259Sgreen
186792555Sdes	rtype = packet_get_string(NULL);
186892555Sdes	want_reply = packet_get_char();
1869126274Sdes	debug("client_input_global_request: rtype %s want_reply %d",
1870126274Sdes	    rtype, want_reply);
187192555Sdes	if (want_reply) {
187292555Sdes		packet_start(success ?
187392555Sdes		    SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE);
187492555Sdes		packet_send();
187592555Sdes		packet_write_wait();
187692555Sdes	}
187792555Sdes	xfree(rtype);
187892555Sdes}
187992555Sdes
1880137015Sdesvoid
1881137015Sdesclient_session2_setup(int id, int want_tty, int want_subsystem,
1882137015Sdes    const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env,
1883137015Sdes    dispatch_fn *subsys_repl)
1884137015Sdes{
1885137015Sdes	int len;
1886146998Sdes	Channel *c = NULL;
1887137015Sdes
1888137015Sdes	debug2("%s: id %d", __func__, id);
1889137015Sdes
1890146998Sdes	if ((c = channel_lookup(id)) == NULL)
1891146998Sdes		fatal("client_session2_setup: channel %d: unknown channel", id);
1892146998Sdes
1893137015Sdes	if (want_tty) {
1894137015Sdes		struct winsize ws;
1895137015Sdes		struct termios tio;
1896137015Sdes
1897137015Sdes		/* Store window size in the packet. */
1898137015Sdes		if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0)
1899137015Sdes			memset(&ws, 0, sizeof(ws));
1900137015Sdes
1901137015Sdes		channel_request_start(id, "pty-req", 0);
1902137015Sdes		packet_put_cstring(term != NULL ? term : "");
1903162852Sdes		packet_put_int((u_int)ws.ws_col);
1904162852Sdes		packet_put_int((u_int)ws.ws_row);
1905162852Sdes		packet_put_int((u_int)ws.ws_xpixel);
1906162852Sdes		packet_put_int((u_int)ws.ws_ypixel);
1907137015Sdes		tio = get_saved_tio();
1908137015Sdes		tty_make_modes(-1, tiop != NULL ? tiop : &tio);
1909137015Sdes		packet_send();
1910137015Sdes		/* XXX wait for reply */
1911146998Sdes		c->client_tty = 1;
1912137015Sdes	}
1913137015Sdes
1914137015Sdes	/* Transfer any environment variables from client to server */
1915137015Sdes	if (options.num_send_env != 0 && env != NULL) {
1916137015Sdes		int i, j, matched;
1917137015Sdes		char *name, *val;
1918137015Sdes
1919137015Sdes		debug("Sending environment.");
1920137015Sdes		for (i = 0; env[i] != NULL; i++) {
1921137015Sdes			/* Split */
1922137015Sdes			name = xstrdup(env[i]);
1923137015Sdes			if ((val = strchr(name, '=')) == NULL) {
1924157016Sdes				xfree(name);
1925137015Sdes				continue;
1926137015Sdes			}
1927137015Sdes			*val++ = '\0';
1928137015Sdes
1929137015Sdes			matched = 0;
1930137015Sdes			for (j = 0; j < options.num_send_env; j++) {
1931137015Sdes				if (match_pattern(name, options.send_env[j])) {
1932137015Sdes					matched = 1;
1933137015Sdes					break;
1934137015Sdes				}
1935137015Sdes			}
1936137015Sdes			if (!matched) {
1937137015Sdes				debug3("Ignored env %s", name);
1938157016Sdes				xfree(name);
1939137015Sdes				continue;
1940137015Sdes			}
1941137015Sdes
1942137015Sdes			debug("Sending env %s = %s", name, val);
1943137015Sdes			channel_request_start(id, "env", 0);
1944137015Sdes			packet_put_cstring(name);
1945137015Sdes			packet_put_cstring(val);
1946137015Sdes			packet_send();
1947157016Sdes			xfree(name);
1948137015Sdes		}
1949137015Sdes	}
1950137015Sdes
1951137015Sdes	len = buffer_len(cmd);
1952137015Sdes	if (len > 0) {
1953137015Sdes		if (len > 900)
1954137015Sdes			len = 900;
1955137015Sdes		if (want_subsystem) {
1956137015Sdes			debug("Sending subsystem: %.*s", len, (u_char*)buffer_ptr(cmd));
1957137015Sdes			channel_request_start(id, "subsystem", subsys_repl != NULL);
1958137015Sdes			if (subsys_repl != NULL) {
1959137015Sdes				/* register callback for reply */
1960137015Sdes				/* XXX we assume that client_loop has already been called */
1961137015Sdes				dispatch_set(SSH2_MSG_CHANNEL_FAILURE, subsys_repl);
1962137015Sdes				dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, subsys_repl);
1963137015Sdes			}
1964137015Sdes		} else {
1965137015Sdes			debug("Sending command: %.*s", len, (u_char*)buffer_ptr(cmd));
1966137015Sdes			channel_request_start(id, "exec", 0);
1967137015Sdes		}
1968137015Sdes		packet_put_string(buffer_ptr(cmd), buffer_len(cmd));
1969137015Sdes		packet_send();
1970137015Sdes	} else {
1971137015Sdes		channel_request_start(id, "shell", 0);
1972137015Sdes		packet_send();
1973137015Sdes	}
1974137015Sdes}
1975137015Sdes
197692555Sdesstatic void
197776259Sgreenclient_init_dispatch_20(void)
197860573Skris{
197960573Skris	dispatch_init(&dispatch_protocol_error);
198098675Sdes
198160573Skris	dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose);
198260573Skris	dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data);
198360573Skris	dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof);
198460573Skris	dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data);
198560573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open);
198660573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
198760573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
198876259Sgreen	dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req);
198960573Skris	dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust);
199092555Sdes	dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request);
199176259Sgreen
199276259Sgreen	/* rekeying */
199376259Sgreen	dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
199498675Sdes
199598675Sdes	/* global request reply messages */
199698675Sdes	dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply);
199798675Sdes	dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply);
199860573Skris}
199992555Sdesstatic void
200076259Sgreenclient_init_dispatch_13(void)
200160573Skris{
200260573Skris	dispatch_init(NULL);
200360573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close);
200460573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation);
200560573Skris	dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data);
200660573Skris	dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
200760573Skris	dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
200860573Skris	dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open);
200960573Skris	dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status);
201060573Skris	dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data);
201160573Skris	dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data);
201268700Sgreen
201368700Sgreen	dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ?
2014126274Sdes	    &client_input_agent_open : &deny_input_open);
201568700Sgreen	dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ?
201669587Sgreen	    &x11_input_open : &deny_input_open);
201760573Skris}
201892555Sdesstatic void
201976259Sgreenclient_init_dispatch_15(void)
202060573Skris{
202160573Skris	client_init_dispatch_13();
202260573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof);
202360573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose);
202460573Skris}
202592555Sdesstatic void
202676259Sgreenclient_init_dispatch(void)
202760573Skris{
202860573Skris	if (compat20)
202960573Skris		client_init_dispatch_20();
203060573Skris	else if (compat13)
203160573Skris		client_init_dispatch_13();
203260573Skris	else
203360573Skris		client_init_dispatch_15();
203460573Skris}
2035126274Sdes
2036126274Sdes/* client specific fatal cleanup */
2037126274Sdesvoid
2038126274Sdescleanup_exit(int i)
2039126274Sdes{
2040126274Sdes	leave_raw_mode();
2041126274Sdes	leave_non_blocking();
2042137015Sdes	if (options.control_path != NULL && control_fd != -1)
2043137015Sdes		unlink(options.control_path);
2044126274Sdes	_exit(i);
2045126274Sdes}
2046