clientloop.c revision 207319
1207319Sdes/* $OpenBSD: clientloop.c,v 1.219 2010/03/13 21:10:38 djm 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
89181111Sdes#include "openbsd-compat/sys-queue.h"
90162852Sdes#include "xmalloc.h"
9176259Sgreen#include "ssh.h"
9276259Sgreen#include "ssh1.h"
9376259Sgreen#include "ssh2.h"
9457429Smarkm#include "packet.h"
9557429Smarkm#include "buffer.h"
9660573Skris#include "compat.h"
9760573Skris#include "channels.h"
9860573Skris#include "dispatch.h"
9976259Sgreen#include "key.h"
100162852Sdes#include "cipher.h"
10176259Sgreen#include "kex.h"
10276259Sgreen#include "log.h"
10376259Sgreen#include "readconf.h"
10476259Sgreen#include "clientloop.h"
105157016Sdes#include "sshconnect.h"
10676259Sgreen#include "authfd.h"
10776259Sgreen#include "atomicio.h"
108137015Sdes#include "sshpty.h"
10976259Sgreen#include "misc.h"
110137015Sdes#include "match.h"
111137015Sdes#include "msg.h"
112197679Sdes#include "roaming.h"
11360573Skris
11469587Sgreen/* import options */
11568700Sgreenextern Options options;
11668700Sgreen
11757429Smarkm/* Flag indicating that stdin should be redirected from /dev/null. */
11857429Smarkmextern int stdin_null_flag;
11957429Smarkm
120126274Sdes/* Flag indicating that no shell has been requested */
121126274Sdesextern int no_shell_flag;
122126274Sdes
123137015Sdes/* Control socket */
124204917Sdesextern int muxserver_sock; /* XXX use mux_client_cleanup() instead */
125137015Sdes
12657429Smarkm/*
12757429Smarkm * Name of the host we are connecting to.  This is the name given on the
12857429Smarkm * command line, or the HostName specified for the user-supplied name in a
12957429Smarkm * configuration file.
13057429Smarkm */
13157429Smarkmextern char *host;
13257429Smarkm
133204917Sdes/* Force TTY allocation */
134204917Sdesextern int force_tty_flag;
135204917Sdes
13657429Smarkm/*
13757429Smarkm * Flag to indicate that we have received a window change signal which has
13857429Smarkm * not yet been processed.  This will cause a message indicating the new
13957429Smarkm * window size to be sent to the server a little later.  This is volatile
14057429Smarkm * because this is updated in a signal handler.
14157429Smarkm */
14292555Sdesstatic volatile sig_atomic_t received_window_change_signal = 0;
14392555Sdesstatic volatile sig_atomic_t received_signal = 0;
14457429Smarkm
145157016Sdes/* Flag indicating whether the user's terminal is in non-blocking mode. */
14657429Smarkmstatic int in_non_blocking_mode = 0;
14757429Smarkm
14857429Smarkm/* Common data for the client loop code. */
149204917Sdesvolatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */
150181111Sdesstatic int escape_char1;	/* Escape character. (proto1 only) */
151181111Sdesstatic int escape_pending1;	/* Last character was an escape (proto1 only) */
15257429Smarkmstatic int last_was_cr;		/* Last character was a newline. */
153181111Sdesstatic int exit_status;		/* Used to store the command exit status. */
154181111Sdesstatic int stdin_eof;		/* EOF has been encountered on stderr. */
15557429Smarkmstatic Buffer stdin_buffer;	/* Buffer for stdin data. */
15657429Smarkmstatic Buffer stdout_buffer;	/* Buffer for stdout data. */
15757429Smarkmstatic Buffer stderr_buffer;	/* Buffer for stderr data. */
15876259Sgreenstatic u_int buffer_high;/* Soft max buffer size. */
15957429Smarkmstatic int connection_in;	/* Connection to server (input). */
16057429Smarkmstatic int connection_out;	/* Connection to server (output). */
16176259Sgreenstatic int need_rekeying;	/* Set to non-zero if rekeying is requested. */
16276259Sgreenstatic int session_closed = 0;	/* In SSH2: login session closed. */
16357429Smarkm
16492555Sdesstatic void client_init_dispatch(void);
16560573Skrisint	session_ident = -1;
16660573Skris
167204917Sdesint	session_resumed = 0;
168204917Sdes
169181111Sdes/* Track escape per proto2 channel */
170181111Sdesstruct escape_filter_ctx {
171181111Sdes	int escape_pending;
172181111Sdes	int escape_char;
173137015Sdes};
174137015Sdes
175181111Sdes/* Context for channel confirmation replies */
176181111Sdesstruct channel_reply_ctx {
177181111Sdes	const char *request_type;
178181111Sdes	int id, do_close;
179181111Sdes};
180181111Sdes
181181111Sdes/* Global request success/failure callbacks */
182181111Sdesstruct global_confirm {
183181111Sdes	TAILQ_ENTRY(global_confirm) entry;
184181111Sdes	global_confirm_cb *cb;
185181111Sdes	void *ctx;
186181111Sdes	int ref_count;
187181111Sdes};
188181111SdesTAILQ_HEAD(global_confirms, global_confirm);
189181111Sdesstatic struct global_confirms global_confirms =
190181111Sdes    TAILQ_HEAD_INITIALIZER(global_confirms);
191181111Sdes
19276259Sgreen/*XXX*/
19376259Sgreenextern Kex *xxx_kex;
19457429Smarkm
195137015Sdesvoid ssh_process_session2_setup(int, int, int, Buffer *);
196137015Sdes
19757429Smarkm/* Restores stdin to blocking mode. */
19857429Smarkm
19992555Sdesstatic void
20076259Sgreenleave_non_blocking(void)
20157429Smarkm{
20257429Smarkm	if (in_non_blocking_mode) {
203137015Sdes		unset_nonblock(fileno(stdin));
20457429Smarkm		in_non_blocking_mode = 0;
20557429Smarkm	}
20657429Smarkm}
20757429Smarkm
20857429Smarkm/* Puts stdin terminal in non-blocking mode. */
20957429Smarkm
21092555Sdesstatic void
21176259Sgreenenter_non_blocking(void)
21257429Smarkm{
21357429Smarkm	in_non_blocking_mode = 1;
214137015Sdes	set_nonblock(fileno(stdin));
21557429Smarkm}
21657429Smarkm
21757429Smarkm/*
21857429Smarkm * Signal handler for the window change signal (SIGWINCH).  This just sets a
21957429Smarkm * flag indicating that the window has changed.
22057429Smarkm */
221162852Sdes/*ARGSUSED */
22292555Sdesstatic void
22357429Smarkmwindow_change_handler(int sig)
22457429Smarkm{
22557429Smarkm	received_window_change_signal = 1;
22657429Smarkm	signal(SIGWINCH, window_change_handler);
22757429Smarkm}
22857429Smarkm
22957429Smarkm/*
23057429Smarkm * Signal handler for signals that cause the program to terminate.  These
23157429Smarkm * signals must be trapped to restore terminal modes.
23257429Smarkm */
233162852Sdes/*ARGSUSED */
23492555Sdesstatic void
23557429Smarkmsignal_handler(int sig)
23657429Smarkm{
23792555Sdes	received_signal = sig;
23892555Sdes	quit_pending = 1;
23957429Smarkm}
24057429Smarkm
24157429Smarkm/*
24257429Smarkm * Returns current time in seconds from Jan 1, 1970 with the maximum
24357429Smarkm * available resolution.
24457429Smarkm */
24557429Smarkm
24692555Sdesstatic double
24776259Sgreenget_current_time(void)
24857429Smarkm{
24957429Smarkm	struct timeval tv;
25057429Smarkm	gettimeofday(&tv, NULL);
25157429Smarkm	return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
25257429Smarkm}
25357429Smarkm
254149749Sdes#define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1"
255149749Sdesvoid
256149749Sdesclient_x11_get_proto(const char *display, const char *xauth_path,
257149749Sdes    u_int trusted, char **_proto, char **_data)
258149749Sdes{
259149749Sdes	char cmd[1024];
260149749Sdes	char line[512];
261149749Sdes	char xdisplay[512];
262149749Sdes	static char proto[512], data[512];
263149749Sdes	FILE *f;
264149749Sdes	int got_data = 0, generated = 0, do_unlink = 0, i;
265149749Sdes	char *xauthdir, *xauthfile;
266149749Sdes	struct stat st;
267149749Sdes
268149749Sdes	xauthdir = xauthfile = NULL;
269149749Sdes	*_proto = proto;
270149749Sdes	*_data = data;
271149749Sdes	proto[0] = data[0] = '\0';
272149749Sdes
273149749Sdes	if (xauth_path == NULL ||(stat(xauth_path, &st) == -1)) {
274149749Sdes		debug("No xauth program.");
275149749Sdes	} else {
276149749Sdes		if (display == NULL) {
277149749Sdes			debug("x11_get_proto: DISPLAY not set");
278149749Sdes			return;
279149749Sdes		}
280149749Sdes		/*
281149749Sdes		 * Handle FamilyLocal case where $DISPLAY does
282149749Sdes		 * not match an authorization entry.  For this we
283149749Sdes		 * just try "xauth list unix:displaynum.screennum".
284149749Sdes		 * XXX: "localhost" match to determine FamilyLocal
285149749Sdes		 *      is not perfect.
286149749Sdes		 */
287149749Sdes		if (strncmp(display, "localhost:", 10) == 0) {
288149749Sdes			snprintf(xdisplay, sizeof(xdisplay), "unix:%s",
289149749Sdes			    display + 10);
290149749Sdes			display = xdisplay;
291149749Sdes		}
292149749Sdes		if (trusted == 0) {
293149749Sdes			xauthdir = xmalloc(MAXPATHLEN);
294149749Sdes			xauthfile = xmalloc(MAXPATHLEN);
295149749Sdes			strlcpy(xauthdir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN);
296149749Sdes			if (mkdtemp(xauthdir) != NULL) {
297149749Sdes				do_unlink = 1;
298149749Sdes				snprintf(xauthfile, MAXPATHLEN, "%s/xauthfile",
299149749Sdes				    xauthdir);
300149749Sdes				snprintf(cmd, sizeof(cmd),
301149749Sdes				    "%s -f %s generate %s " SSH_X11_PROTO
302149749Sdes				    " untrusted timeout 1200 2>" _PATH_DEVNULL,
303149749Sdes				    xauth_path, xauthfile, display);
304149749Sdes				debug2("x11_get_proto: %s", cmd);
305149749Sdes				if (system(cmd) == 0)
306149749Sdes					generated = 1;
307149749Sdes			}
308149749Sdes		}
309181111Sdes
310181111Sdes		/*
311181111Sdes		 * When in untrusted mode, we read the cookie only if it was
312181111Sdes		 * successfully generated as an untrusted one in the step
313181111Sdes		 * above.
314181111Sdes		 */
315181111Sdes		if (trusted || generated) {
316181111Sdes			snprintf(cmd, sizeof(cmd),
317181111Sdes			    "%s %s%s list %s 2>" _PATH_DEVNULL,
318181111Sdes			    xauth_path,
319181111Sdes			    generated ? "-f " : "" ,
320181111Sdes			    generated ? xauthfile : "",
321181111Sdes			    display);
322181111Sdes			debug2("x11_get_proto: %s", cmd);
323181111Sdes			f = popen(cmd, "r");
324181111Sdes			if (f && fgets(line, sizeof(line), f) &&
325181111Sdes			    sscanf(line, "%*s %511s %511s", proto, data) == 2)
326181111Sdes				got_data = 1;
327181111Sdes			if (f)
328181111Sdes				pclose(f);
329181111Sdes		} else
330181111Sdes			error("Warning: untrusted X11 forwarding setup failed: "
331181111Sdes			    "xauth key data not generated");
332149749Sdes	}
333149749Sdes
334149749Sdes	if (do_unlink) {
335149749Sdes		unlink(xauthfile);
336149749Sdes		rmdir(xauthdir);
337149749Sdes	}
338149749Sdes	if (xauthdir)
339149749Sdes		xfree(xauthdir);
340149749Sdes	if (xauthfile)
341149749Sdes		xfree(xauthfile);
342149749Sdes
343149749Sdes	/*
344149749Sdes	 * If we didn't get authentication data, just make up some
345149749Sdes	 * data.  The forwarding code will check the validity of the
346149749Sdes	 * response anyway, and substitute this data.  The X11
347149749Sdes	 * server, however, will ignore this fake data and use
348149749Sdes	 * whatever authentication mechanisms it was using otherwise
349149749Sdes	 * for the local connection.
350149749Sdes	 */
351149749Sdes	if (!got_data) {
352149749Sdes		u_int32_t rnd = 0;
353149749Sdes
354149749Sdes		logit("Warning: No xauth data; "
355149749Sdes		    "using fake authentication data for X11 forwarding.");
356149749Sdes		strlcpy(proto, SSH_X11_PROTO, sizeof proto);
357149749Sdes		for (i = 0; i < 16; i++) {
358149749Sdes			if (i % 4 == 0)
359149749Sdes				rnd = arc4random();
360149749Sdes			snprintf(data + 2 * i, sizeof data - 2 * i, "%02x",
361149749Sdes			    rnd & 0xff);
362149749Sdes			rnd >>= 8;
363149749Sdes		}
364149749Sdes	}
365149749Sdes}
366149749Sdes
36757429Smarkm/*
36857429Smarkm * This is called when the interactive is entered.  This checks if there is
36957429Smarkm * an EOF coming on stdin.  We must check this explicitly, as select() does
37057429Smarkm * not appear to wake up when redirecting from /dev/null.
37157429Smarkm */
37257429Smarkm
37392555Sdesstatic void
37476259Sgreenclient_check_initial_eof_on_stdin(void)
37557429Smarkm{
37657429Smarkm	int len;
37757429Smarkm	char buf[1];
37857429Smarkm
37957429Smarkm	/*
38057429Smarkm	 * If standard input is to be "redirected from /dev/null", we simply
38157429Smarkm	 * mark that we have seen an EOF and send an EOF message to the
38257429Smarkm	 * server. Otherwise, we try to read a single character; it appears
38357429Smarkm	 * that for some files, such /dev/null, select() never wakes up for
38457429Smarkm	 * read for this descriptor, which means that we never get EOF.  This
38557429Smarkm	 * way we will get the EOF if stdin comes from /dev/null or similar.
38657429Smarkm	 */
38757429Smarkm	if (stdin_null_flag) {
38857429Smarkm		/* Fake EOF on stdin. */
38957429Smarkm		debug("Sending eof.");
39057429Smarkm		stdin_eof = 1;
39157429Smarkm		packet_start(SSH_CMSG_EOF);
39257429Smarkm		packet_send();
39357429Smarkm	} else {
39457429Smarkm		enter_non_blocking();
39557429Smarkm
39657429Smarkm		/* Check for immediate EOF on stdin. */
39757429Smarkm		len = read(fileno(stdin), buf, 1);
39857429Smarkm		if (len == 0) {
399181111Sdes			/*
400181111Sdes			 * EOF.  Record that we have seen it and send
401181111Sdes			 * EOF to server.
402181111Sdes			 */
40357429Smarkm			debug("Sending eof.");
40457429Smarkm			stdin_eof = 1;
40557429Smarkm			packet_start(SSH_CMSG_EOF);
40657429Smarkm			packet_send();
40757429Smarkm		} else if (len > 0) {
40857429Smarkm			/*
40957429Smarkm			 * Got data.  We must store the data in the buffer,
41057429Smarkm			 * and also process it as an escape character if
41157429Smarkm			 * appropriate.
41257429Smarkm			 */
413181111Sdes			if ((u_char) buf[0] == escape_char1)
414181111Sdes				escape_pending1 = 1;
41576259Sgreen			else
41657429Smarkm				buffer_append(&stdin_buffer, buf, 1);
41757429Smarkm		}
41857429Smarkm		leave_non_blocking();
41957429Smarkm	}
42057429Smarkm}
42157429Smarkm
42257429Smarkm
42357429Smarkm/*
42457429Smarkm * Make packets from buffered stdin data, and buffer them for sending to the
42557429Smarkm * connection.
42657429Smarkm */
42757429Smarkm
42892555Sdesstatic void
42976259Sgreenclient_make_packets_from_stdin_data(void)
43057429Smarkm{
43176259Sgreen	u_int len;
43257429Smarkm
43357429Smarkm	/* Send buffered stdin data to the server. */
43457429Smarkm	while (buffer_len(&stdin_buffer) > 0 &&
43592555Sdes	    packet_not_very_much_data_to_write()) {
43657429Smarkm		len = buffer_len(&stdin_buffer);
43757429Smarkm		/* Keep the packets at reasonable size. */
43857429Smarkm		if (len > packet_get_maxsize())
43957429Smarkm			len = packet_get_maxsize();
44057429Smarkm		packet_start(SSH_CMSG_STDIN_DATA);
44157429Smarkm		packet_put_string(buffer_ptr(&stdin_buffer), len);
44257429Smarkm		packet_send();
44357429Smarkm		buffer_consume(&stdin_buffer, len);
44457429Smarkm		/* If we have a pending EOF, send it now. */
44557429Smarkm		if (stdin_eof && buffer_len(&stdin_buffer) == 0) {
44657429Smarkm			packet_start(SSH_CMSG_EOF);
44757429Smarkm			packet_send();
44857429Smarkm		}
44957429Smarkm	}
45057429Smarkm}
45157429Smarkm
45257429Smarkm/*
45357429Smarkm * Checks if the client window has changed, and sends a packet about it to
45457429Smarkm * the server if so.  The actual change is detected elsewhere (by a software
45557429Smarkm * interrupt on Unix); this just checks the flag and sends a message if
45657429Smarkm * appropriate.
45757429Smarkm */
45857429Smarkm
45992555Sdesstatic void
46076259Sgreenclient_check_window_change(void)
46157429Smarkm{
46260573Skris	struct winsize ws;
46357429Smarkm
46460573Skris	if (! received_window_change_signal)
46560573Skris		return;
46660573Skris	/** XXX race */
46760573Skris	received_window_change_signal = 0;
46857429Smarkm
46969587Sgreen	debug2("client_check_window_change: changed");
47060573Skris
47160573Skris	if (compat20) {
472137015Sdes		channel_send_window_changes();
47360573Skris	} else {
474137015Sdes		if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
475137015Sdes			return;
47660573Skris		packet_start(SSH_CMSG_WINDOW_SIZE);
477162852Sdes		packet_put_int((u_int)ws.ws_row);
478162852Sdes		packet_put_int((u_int)ws.ws_col);
479162852Sdes		packet_put_int((u_int)ws.ws_xpixel);
480162852Sdes		packet_put_int((u_int)ws.ws_ypixel);
48160573Skris		packet_send();
48257429Smarkm	}
48357429Smarkm}
48457429Smarkm
485126274Sdesstatic void
486126274Sdesclient_global_request_reply(int type, u_int32_t seq, void *ctxt)
487126274Sdes{
488181111Sdes	struct global_confirm *gc;
489181111Sdes
490181111Sdes	if ((gc = TAILQ_FIRST(&global_confirms)) == NULL)
491181111Sdes		return;
492181111Sdes	if (gc->cb != NULL)
493181111Sdes		gc->cb(type, seq, gc->ctx);
494181111Sdes	if (--gc->ref_count <= 0) {
495181111Sdes		TAILQ_REMOVE(&global_confirms, gc, entry);
496181111Sdes		bzero(gc, sizeof(*gc));
497181111Sdes		xfree(gc);
498181111Sdes	}
499181111Sdes
500197679Sdes	packet_set_alive_timeouts(0);
501126274Sdes}
502126274Sdes
503126274Sdesstatic void
504126274Sdesserver_alive_check(void)
505126274Sdes{
506197679Sdes	if (packet_inc_alive_timeouts() > options.server_alive_count_max) {
507164146Sdes		logit("Timeout, server not responding.");
508164146Sdes		cleanup_exit(255);
509164146Sdes	}
510126274Sdes	packet_start(SSH2_MSG_GLOBAL_REQUEST);
511126274Sdes	packet_put_cstring("keepalive@openssh.com");
512126274Sdes	packet_put_char(1);     /* boolean: want reply */
513126274Sdes	packet_send();
514181111Sdes	/* Insert an empty placeholder to maintain ordering */
515181111Sdes	client_register_global_confirm(NULL, NULL);
516126274Sdes}
517126274Sdes
51857429Smarkm/*
51957429Smarkm * Waits until the client can do something (some data becomes available on
52057429Smarkm * one of the file descriptors).
52157429Smarkm */
52292555Sdesstatic void
52376259Sgreenclient_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
524137015Sdes    int *maxfdp, u_int *nallocp, int rekeying)
52557429Smarkm{
526126274Sdes	struct timeval tv, *tvp;
527126274Sdes	int ret;
528126274Sdes
52976259Sgreen	/* Add any selections by the channel mechanism. */
53092555Sdes	channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying);
53157429Smarkm
53260573Skris	if (!compat20) {
53360573Skris		/* Read from the connection, unless our buffers are full. */
53460573Skris		if (buffer_len(&stdout_buffer) < buffer_high &&
53560573Skris		    buffer_len(&stderr_buffer) < buffer_high &&
53660573Skris		    channel_not_very_much_buffered_data())
53776259Sgreen			FD_SET(connection_in, *readsetp);
53860573Skris		/*
53960573Skris		 * Read from stdin, unless we have seen EOF or have very much
54060573Skris		 * buffered data to send to the server.
54160573Skris		 */
54260573Skris		if (!stdin_eof && packet_not_very_much_data_to_write())
54376259Sgreen			FD_SET(fileno(stdin), *readsetp);
54460573Skris
54560573Skris		/* Select stdout/stderr if have data in buffer. */
54660573Skris		if (buffer_len(&stdout_buffer) > 0)
54776259Sgreen			FD_SET(fileno(stdout), *writesetp);
54860573Skris		if (buffer_len(&stderr_buffer) > 0)
54976259Sgreen			FD_SET(fileno(stderr), *writesetp);
55060573Skris	} else {
55192555Sdes		/* channel_prepare_select could have closed the last channel */
55292555Sdes		if (session_closed && !channel_still_open() &&
55392555Sdes		    !packet_have_data_to_write()) {
55492555Sdes			/* clear mask since we did not call select() */
55592555Sdes			memset(*readsetp, 0, *nallocp);
55692555Sdes			memset(*writesetp, 0, *nallocp);
55792555Sdes			return;
55892555Sdes		} else {
55992555Sdes			FD_SET(connection_in, *readsetp);
56092555Sdes		}
56160573Skris	}
56257429Smarkm
56357429Smarkm	/* Select server connection if have data to write to the server. */
56457429Smarkm	if (packet_have_data_to_write())
56576259Sgreen		FD_SET(connection_out, *writesetp);
56657429Smarkm
56757429Smarkm	/*
56857429Smarkm	 * Wait for something to happen.  This will suspend the process until
56957429Smarkm	 * some selected descriptor can be read, written, or has some other
570126274Sdes	 * event pending.
57157429Smarkm	 */
57257429Smarkm
573126274Sdes	if (options.server_alive_interval == 0 || !compat20)
574126274Sdes		tvp = NULL;
575137015Sdes	else {
576126274Sdes		tv.tv_sec = options.server_alive_interval;
577126274Sdes		tv.tv_usec = 0;
578126274Sdes		tvp = &tv;
579126274Sdes	}
580126274Sdes	ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
581126274Sdes	if (ret < 0) {
58257429Smarkm		char buf[100];
58376259Sgreen
58476259Sgreen		/*
58576259Sgreen		 * We have to clear the select masks, because we return.
58676259Sgreen		 * We have to return, because the mainloop checks for the flags
58776259Sgreen		 * set by the signal handlers.
58876259Sgreen		 */
58992555Sdes		memset(*readsetp, 0, *nallocp);
59092555Sdes		memset(*writesetp, 0, *nallocp);
59176259Sgreen
59257429Smarkm		if (errno == EINTR)
59357429Smarkm			return;
59457429Smarkm		/* Note: we might still have data in the buffers. */
59557429Smarkm		snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
59657429Smarkm		buffer_append(&stderr_buffer, buf, strlen(buf));
59757429Smarkm		quit_pending = 1;
598126274Sdes	} else if (ret == 0)
599126274Sdes		server_alive_check();
60057429Smarkm}
60157429Smarkm
60292555Sdesstatic void
60365668Skrisclient_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr)
60457429Smarkm{
60557429Smarkm	/* Flush stdout and stderr buffers. */
60665668Skris	if (buffer_len(bout) > 0)
607181111Sdes		atomicio(vwrite, fileno(stdout), buffer_ptr(bout),
608181111Sdes		    buffer_len(bout));
60965668Skris	if (buffer_len(berr) > 0)
610181111Sdes		atomicio(vwrite, fileno(stderr), buffer_ptr(berr),
611181111Sdes		    buffer_len(berr));
61257429Smarkm
613204917Sdes	leave_raw_mode(force_tty_flag);
61457429Smarkm
61557429Smarkm	/*
61657429Smarkm	 * Free (and clear) the buffer to reduce the amount of data that gets
61757429Smarkm	 * written to swap.
61857429Smarkm	 */
61965668Skris	buffer_free(bin);
62065668Skris	buffer_free(bout);
62165668Skris	buffer_free(berr);
62257429Smarkm
62357429Smarkm	/* Send the suspend signal to the program itself. */
62457429Smarkm	kill(getpid(), SIGTSTP);
62557429Smarkm
626146998Sdes	/* Reset window sizes in case they have changed */
627146998Sdes	received_window_change_signal = 1;
62857429Smarkm
62957429Smarkm	/* OK, we have been continued by the user. Reinitialize buffers. */
63065668Skris	buffer_init(bin);
63165668Skris	buffer_init(bout);
63265668Skris	buffer_init(berr);
63357429Smarkm
634204917Sdes	enter_raw_mode(force_tty_flag);
63557429Smarkm}
63657429Smarkm
63792555Sdesstatic void
638162852Sdesclient_process_net_input(fd_set *readset)
63957429Smarkm{
640197679Sdes	int len, cont = 0;
641197679Sdes	char buf[SSH_IOBUFSZ];
64257429Smarkm
64357429Smarkm	/*
64457429Smarkm	 * Read input from the server, and add any such data to the buffer of
64557429Smarkm	 * the packet subsystem.
64657429Smarkm	 */
64757429Smarkm	if (FD_ISSET(connection_in, readset)) {
64857429Smarkm		/* Read as much as possible. */
649197679Sdes		len = roaming_read(connection_in, buf, sizeof(buf), &cont);
650197679Sdes		if (len == 0 && cont == 0) {
651181111Sdes			/*
652181111Sdes			 * Received EOF.  The remote host has closed the
653181111Sdes			 * connection.
654181111Sdes			 */
655181111Sdes			snprintf(buf, sizeof buf,
656181111Sdes			    "Connection to %.300s closed by remote host.\r\n",
657181111Sdes			    host);
65857429Smarkm			buffer_append(&stderr_buffer, buf, strlen(buf));
65957429Smarkm			quit_pending = 1;
66057429Smarkm			return;
66157429Smarkm		}
66257429Smarkm		/*
66357429Smarkm		 * There is a kernel bug on Solaris that causes select to
66457429Smarkm		 * sometimes wake up even though there is no data available.
66557429Smarkm		 */
666181111Sdes		if (len < 0 &&
667181111Sdes		    (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK))
66857429Smarkm			len = 0;
66957429Smarkm
67057429Smarkm		if (len < 0) {
671181111Sdes			/*
672181111Sdes			 * An error has encountered.  Perhaps there is a
673181111Sdes			 * network problem.
674181111Sdes			 */
675181111Sdes			snprintf(buf, sizeof buf,
676181111Sdes			    "Read from remote host %.300s: %.100s\r\n",
677181111Sdes			    host, strerror(errno));
67857429Smarkm			buffer_append(&stderr_buffer, buf, strlen(buf));
67957429Smarkm			quit_pending = 1;
68057429Smarkm			return;
68157429Smarkm		}
68257429Smarkm		packet_process_incoming(buf, len);
68357429Smarkm	}
68460573Skris}
68560573Skris
68698675Sdesstatic void
687181111Sdesclient_status_confirm(int type, Channel *c, void *ctx)
688137015Sdes{
689181111Sdes	struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx;
690181111Sdes	char errmsg[256];
691181111Sdes	int tochan;
692137015Sdes
693181111Sdes	/* XXX supress on mux _client_ quietmode */
694181111Sdes	tochan = options.log_level >= SYSLOG_LEVEL_ERROR &&
695204917Sdes	    c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE;
696137015Sdes
697181111Sdes	if (type == SSH2_MSG_CHANNEL_SUCCESS) {
698181111Sdes		debug2("%s request accepted on channel %d",
699181111Sdes		    cr->request_type, c->self);
700181111Sdes	} else if (type == SSH2_MSG_CHANNEL_FAILURE) {
701181111Sdes		if (tochan) {
702181111Sdes			snprintf(errmsg, sizeof(errmsg),
703181111Sdes			    "%s request failed\r\n", cr->request_type);
704181111Sdes		} else {
705181111Sdes			snprintf(errmsg, sizeof(errmsg),
706181111Sdes			    "%s request failed on channel %d",
707181111Sdes			    cr->request_type, c->self);
708181111Sdes		}
709181111Sdes		/* If error occurred on primary session channel, then exit */
710181111Sdes		if (cr->do_close && c->self == session_ident)
711181111Sdes			fatal("%s", errmsg);
712181111Sdes		/* If error occurred on mux client, append to their stderr */
713181111Sdes		if (tochan)
714181111Sdes			buffer_append(&c->extended, errmsg, strlen(errmsg));
715181111Sdes		else
716181111Sdes			error("%s", errmsg);
717181111Sdes		if (cr->do_close) {
718181111Sdes			chan_read_failed(c);
719181111Sdes			chan_write_failed(c);
720181111Sdes		}
721137015Sdes	}
722181111Sdes	xfree(cr);
723137015Sdes}
724137015Sdes
725137015Sdesstatic void
726181111Sdesclient_abandon_status_confirm(Channel *c, void *ctx)
727137015Sdes{
728181111Sdes	xfree(ctx);
729137015Sdes}
730137015Sdes
731137015Sdesstatic void
732181111Sdesclient_expect_confirm(int id, const char *request, int do_close)
733137015Sdes{
734181111Sdes	struct channel_reply_ctx *cr = xmalloc(sizeof(*cr));
735137015Sdes
736181111Sdes	cr->request_type = request;
737181111Sdes	cr->do_close = do_close;
738137015Sdes
739181111Sdes	channel_register_status_confirm(id, client_status_confirm,
740181111Sdes	    client_abandon_status_confirm, cr);
741181111Sdes}
742137015Sdes
743181111Sdesvoid
744181111Sdesclient_register_global_confirm(global_confirm_cb *cb, void *ctx)
745181111Sdes{
746181111Sdes	struct global_confirm *gc, *last_gc;
747137015Sdes
748181111Sdes	/* Coalesce identical callbacks */
749181111Sdes	last_gc = TAILQ_LAST(&global_confirms, global_confirms);
750181111Sdes	if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) {
751181111Sdes		if (++last_gc->ref_count >= INT_MAX)
752181111Sdes			fatal("%s: last_gc->ref_count = %d",
753181111Sdes			    __func__, last_gc->ref_count);
754146998Sdes		return;
755146998Sdes	}
756146998Sdes
757181111Sdes	gc = xmalloc(sizeof(*gc));
758181111Sdes	gc->cb = cb;
759181111Sdes	gc->ctx = ctx;
760181111Sdes	gc->ref_count = 1;
761181111Sdes	TAILQ_INSERT_TAIL(&global_confirms, gc, entry);
762137015Sdes}
763137015Sdes
764137015Sdesstatic void
76598675Sdesprocess_cmdline(void)
76698675Sdes{
76798675Sdes	void (*handler)(int);
768146998Sdes	char *s, *cmd, *cancel_host;
769137015Sdes	int delete = 0;
770192595Sdes	int local = 0, remote = 0, dynamic = 0;
771192595Sdes	int cancel_port;
772146998Sdes	Forward fwd;
77398675Sdes
774181111Sdes	bzero(&fwd, sizeof(fwd));
775181111Sdes	fwd.listen_host = fwd.connect_host = NULL;
776181111Sdes
777204917Sdes	leave_raw_mode(force_tty_flag);
77898675Sdes	handler = signal(SIGINT, SIG_IGN);
77998675Sdes	cmd = s = read_passphrase("\r\nssh> ", RP_ECHO);
78098675Sdes	if (s == NULL)
78198675Sdes		goto out;
782181111Sdes	while (isspace(*s))
78398675Sdes		s++;
784137015Sdes	if (*s == '-')
785137015Sdes		s++;	/* Skip cmdline '-', if any */
786137015Sdes	if (*s == '\0')
78798675Sdes		goto out;
788137015Sdes
789137015Sdes	if (*s == 'h' || *s == 'H' || *s == '?') {
790137015Sdes		logit("Commands:");
791162852Sdes		logit("      -L[bind_address:]port:host:hostport    "
792162852Sdes		    "Request local forward");
793162852Sdes		logit("      -R[bind_address:]port:host:hostport    "
794162852Sdes		    "Request remote forward");
795192595Sdes		logit("      -D[bind_address:]port                  "
796192595Sdes		    "Request dynamic forward");
797162852Sdes		logit("      -KR[bind_address:]port                 "
798162852Sdes		    "Cancel remote forward");
799157016Sdes		if (!options.permit_local_command)
800157016Sdes			goto out;
801162852Sdes		logit("      !args                                  "
802162852Sdes		    "Execute local command");
803137015Sdes		goto out;
804137015Sdes	}
805137015Sdes
806157016Sdes	if (*s == '!' && options.permit_local_command) {
807157016Sdes		s++;
808157016Sdes		ssh_local_cmd(s);
809157016Sdes		goto out;
810157016Sdes	}
811157016Sdes
812137015Sdes	if (*s == 'K') {
813137015Sdes		delete = 1;
814137015Sdes		s++;
815137015Sdes	}
816192595Sdes	if (*s == 'L')
817192595Sdes		local = 1;
818192595Sdes	else if (*s == 'R')
819192595Sdes		remote = 1;
820192595Sdes	else if (*s == 'D')
821192595Sdes		dynamic = 1;
822192595Sdes	else {
823124208Sdes		logit("Invalid command.");
82498675Sdes		goto out;
82598675Sdes	}
826192595Sdes
827192595Sdes	if ((local || dynamic) && delete) {
828137015Sdes		logit("Not supported.");
829137015Sdes		goto out;
830137015Sdes	}
831192595Sdes	if (remote && delete && !compat20) {
832124208Sdes		logit("Not supported for SSH protocol version 1.");
83398675Sdes		goto out;
83498675Sdes	}
835137015Sdes
836181111Sdes	while (isspace(*++s))
837181111Sdes		;
83898675Sdes
839204917Sdes	/* XXX update list of forwards in options */
840137015Sdes	if (delete) {
841146998Sdes		cancel_port = 0;
842146998Sdes		cancel_host = hpdelim(&s);	/* may be NULL */
843146998Sdes		if (s != NULL) {
844146998Sdes			cancel_port = a2port(s);
845146998Sdes			cancel_host = cleanhostname(cancel_host);
846146998Sdes		} else {
847146998Sdes			cancel_port = a2port(cancel_host);
848146998Sdes			cancel_host = NULL;
84998675Sdes		}
850192595Sdes		if (cancel_port <= 0) {
851146998Sdes			logit("Bad forwarding close port");
852137015Sdes			goto out;
853137015Sdes		}
854146998Sdes		channel_request_rforward_cancel(cancel_host, cancel_port);
855137015Sdes	} else {
856192595Sdes		if (!parse_forward(&fwd, s, dynamic, remote)) {
857137015Sdes			logit("Bad forwarding specification.");
858137015Sdes			goto out;
859137015Sdes		}
860192595Sdes		if (local || dynamic) {
861146998Sdes			if (channel_setup_local_fwd_listener(fwd.listen_host,
862146998Sdes			    fwd.listen_port, fwd.connect_host,
863146998Sdes			    fwd.connect_port, options.gateway_ports) < 0) {
864137015Sdes				logit("Port forwarding failed.");
865137015Sdes				goto out;
866137015Sdes			}
867146998Sdes		} else {
868162852Sdes			if (channel_request_remote_forwarding(fwd.listen_host,
869146998Sdes			    fwd.listen_port, fwd.connect_host,
870162852Sdes			    fwd.connect_port) < 0) {
871162852Sdes				logit("Port forwarding failed.");
872162852Sdes				goto out;
873162852Sdes			}
874146998Sdes		}
875146998Sdes
876137015Sdes		logit("Forwarding port.");
877137015Sdes	}
878137015Sdes
87998675Sdesout:
88098675Sdes	signal(SIGINT, handler);
881204917Sdes	enter_raw_mode(force_tty_flag);
88298675Sdes	if (cmd)
88398675Sdes		xfree(cmd);
884181111Sdes	if (fwd.listen_host != NULL)
885181111Sdes		xfree(fwd.listen_host);
886181111Sdes	if (fwd.connect_host != NULL)
887181111Sdes		xfree(fwd.connect_host);
88898675Sdes}
88998675Sdes
890181111Sdes/*
891181111Sdes * Process the characters one by one, call with c==NULL for proto1 case.
892181111Sdes */
89392555Sdesstatic int
894181111Sdesprocess_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr,
895181111Sdes    char *buf, int len)
89665668Skris{
89765668Skris	char string[1024];
89865668Skris	pid_t pid;
89965668Skris	int bytes = 0;
90076259Sgreen	u_int i;
90176259Sgreen	u_char ch;
90265668Skris	char *s;
903181111Sdes	int *escape_pendingp, escape_char;
904181111Sdes	struct escape_filter_ctx *efc;
90565668Skris
906181111Sdes	if (c == NULL) {
907181111Sdes		escape_pendingp = &escape_pending1;
908181111Sdes		escape_char = escape_char1;
909181111Sdes	} else {
910181111Sdes		if (c->filter_ctx == NULL)
911181111Sdes			return 0;
912181111Sdes		efc = (struct escape_filter_ctx *)c->filter_ctx;
913181111Sdes		escape_pendingp = &efc->escape_pending;
914181111Sdes		escape_char = efc->escape_char;
915181111Sdes	}
916181111Sdes
917149749Sdes	if (len <= 0)
918149749Sdes		return (0);
919149749Sdes
920149749Sdes	for (i = 0; i < (u_int)len; i++) {
92165668Skris		/* Get one character at a time. */
92265668Skris		ch = buf[i];
92365668Skris
924181111Sdes		if (*escape_pendingp) {
92565668Skris			/* We have previously seen an escape character. */
92665668Skris			/* Clear the flag now. */
927181111Sdes			*escape_pendingp = 0;
92865668Skris
92965668Skris			/* Process the escaped character. */
93065668Skris			switch (ch) {
93165668Skris			case '.':
93265668Skris				/* Terminate the connection. */
933181111Sdes				snprintf(string, sizeof string, "%c.\r\n",
934181111Sdes				    escape_char);
93565668Skris				buffer_append(berr, string, strlen(string));
93665668Skris
937204917Sdes				if (c && c->ctl_chan != -1) {
938181111Sdes					chan_read_failed(c);
939181111Sdes					chan_write_failed(c);
940181111Sdes					return 0;
941181111Sdes				} else
942181111Sdes					quit_pending = 1;
94365668Skris				return -1;
94465668Skris
94565668Skris			case 'Z' - 64:
946181111Sdes				/* XXX support this for mux clients */
947204917Sdes				if (c && c->ctl_chan != -1) {
948181111Sdes noescape:
949181111Sdes					snprintf(string, sizeof string,
950181111Sdes					    "%c%c escape not available to "
951181111Sdes					    "multiplexed sessions\r\n",
952181111Sdes					    escape_char, ch);
953181111Sdes					buffer_append(berr, string,
954181111Sdes					    strlen(string));
955181111Sdes					continue;
956181111Sdes				}
957181111Sdes				/* Suspend the program. Inform the user */
958181111Sdes				snprintf(string, sizeof string,
959181111Sdes				    "%c^Z [suspend ssh]\r\n", escape_char);
96065668Skris				buffer_append(berr, string, strlen(string));
96165668Skris
96265668Skris				/* Restore terminal modes and suspend. */
96365668Skris				client_suspend_self(bin, bout, berr);
96465668Skris
96565668Skris				/* We have been continued. */
96665668Skris				continue;
96765668Skris
968124208Sdes			case 'B':
969124208Sdes				if (compat20) {
970124208Sdes					snprintf(string, sizeof string,
971124208Sdes					    "%cB\r\n", escape_char);
972124208Sdes					buffer_append(berr, string,
973124208Sdes					    strlen(string));
974124208Sdes					channel_request_start(session_ident,
975124208Sdes					    "break", 0);
976124208Sdes					packet_put_int(1000);
977124208Sdes					packet_send();
978124208Sdes				}
979124208Sdes				continue;
980124208Sdes
98176259Sgreen			case 'R':
98276259Sgreen				if (compat20) {
98376259Sgreen					if (datafellows & SSH_BUG_NOREKEY)
984181111Sdes						logit("Server does not "
985181111Sdes						    "support re-keying");
98676259Sgreen					else
98776259Sgreen						need_rekeying = 1;
98876259Sgreen				}
98976259Sgreen				continue;
99076259Sgreen
99165668Skris			case '&':
992204917Sdes				if (c && c->ctl_chan != -1)
993181111Sdes					goto noescape;
99465668Skris				/*
995181111Sdes				 * Detach the program (continue to serve
996181111Sdes				 * connections, but put in background and no
997181111Sdes				 * more new connections).
99865668Skris				 */
99965668Skris				/* Restore tty modes. */
1000204917Sdes				leave_raw_mode(force_tty_flag);
100165668Skris
100265668Skris				/* Stop listening for new connections. */
100365668Skris				channel_stop_listening();
100465668Skris
100592555Sdes				snprintf(string, sizeof string,
100692555Sdes				    "%c& [backgrounded]\n", escape_char);
100792555Sdes				buffer_append(berr, string, strlen(string));
100865668Skris
100965668Skris				/* Fork into background. */
101065668Skris				pid = fork();
101165668Skris				if (pid < 0) {
101265668Skris					error("fork: %.100s", strerror(errno));
101365668Skris					continue;
101465668Skris				}
101565668Skris				if (pid != 0) {	/* This is the parent. */
101665668Skris					/* The parent just exits. */
101765668Skris					exit(0);
101865668Skris				}
101965668Skris				/* The child continues serving connections. */
102092555Sdes				if (compat20) {
102192555Sdes					buffer_append(bin, "\004", 1);
102292555Sdes					/* fake EOF on stdin */
102392555Sdes					return -1;
102492555Sdes				} else if (!stdin_eof) {
102592555Sdes					/*
1026181111Sdes					 * Sending SSH_CMSG_EOF alone does not
1027181111Sdes					 * always appear to be enough.  So we
1028181111Sdes					 * try to send an EOF character first.
102992555Sdes					 */
103092555Sdes					packet_start(SSH_CMSG_STDIN_DATA);
103192555Sdes					packet_put_string("\004", 1);
103292555Sdes					packet_send();
103392555Sdes					/* Close stdin. */
103492555Sdes					stdin_eof = 1;
103592555Sdes					if (buffer_len(bin) == 0) {
103692555Sdes						packet_start(SSH_CMSG_EOF);
103792555Sdes						packet_send();
103892555Sdes					}
103992555Sdes				}
104092555Sdes				continue;
104165668Skris
104265668Skris			case '?':
1043204917Sdes				if (c && c->ctl_chan != -1) {
1044181111Sdes					snprintf(string, sizeof string,
104565668Skris"%c?\r\n\
104665668SkrisSupported escape sequences:\r\n\
1047181111Sdes  %c.  - terminate session\r\n\
1048181111Sdes  %cB  - send a BREAK to the remote system\r\n\
1049181111Sdes  %cR  - Request rekey (SSH protocol 2 only)\r\n\
1050181111Sdes  %c#  - list forwarded connections\r\n\
1051181111Sdes  %c?  - this message\r\n\
1052181111Sdes  %c%c  - send the escape character by typing it twice\r\n\
105365668Skris(Note that escapes are only recognized immediately after newline.)\r\n",
1054181111Sdes					    escape_char, escape_char,
1055181111Sdes					    escape_char, escape_char,
1056181111Sdes					    escape_char, escape_char,
1057192595Sdes					    escape_char, escape_char);
1058181111Sdes				} else {
1059181111Sdes					snprintf(string, sizeof string,
1060181111Sdes"%c?\r\n\
1061181111SdesSupported escape sequences:\r\n\
1062181111Sdes  %c.  - terminate connection (and any multiplexed sessions)\r\n\
1063181111Sdes  %cB  - send a BREAK to the remote system\r\n\
1064181111Sdes  %cC  - open a command line\r\n\
1065181111Sdes  %cR  - Request rekey (SSH protocol 2 only)\r\n\
1066181111Sdes  %c^Z - suspend ssh\r\n\
1067181111Sdes  %c#  - list forwarded connections\r\n\
1068181111Sdes  %c&  - background ssh (when waiting for connections to terminate)\r\n\
1069181111Sdes  %c?  - this message\r\n\
1070181111Sdes  %c%c  - send the escape character by typing it twice\r\n\
1071181111Sdes(Note that escapes are only recognized immediately after newline.)\r\n",
1072181111Sdes					    escape_char, escape_char,
1073181111Sdes					    escape_char, escape_char,
1074181111Sdes					    escape_char, escape_char,
1075181111Sdes					    escape_char, escape_char,
1076181111Sdes					    escape_char, escape_char,
1077181111Sdes					    escape_char);
1078181111Sdes				}
107965668Skris				buffer_append(berr, string, strlen(string));
108065668Skris				continue;
108165668Skris
108265668Skris			case '#':
1083181111Sdes				snprintf(string, sizeof string, "%c#\r\n",
1084181111Sdes				    escape_char);
108565668Skris				buffer_append(berr, string, strlen(string));
108665668Skris				s = channel_open_message();
108765668Skris				buffer_append(berr, s, strlen(s));
108865668Skris				xfree(s);
108965668Skris				continue;
109065668Skris
109198675Sdes			case 'C':
1092204917Sdes				if (c && c->ctl_chan != -1)
1093192595Sdes					goto noescape;
109498675Sdes				process_cmdline();
109598675Sdes				continue;
109698675Sdes
109765668Skris			default:
109865668Skris				if (ch != escape_char) {
109965668Skris					buffer_put_char(bin, escape_char);
110065668Skris					bytes++;
110165668Skris				}
110265668Skris				/* Escaped characters fall through here */
110365668Skris				break;
110465668Skris			}
110565668Skris		} else {
110665668Skris			/*
1107181111Sdes			 * The previous character was not an escape char.
1108181111Sdes			 * Check if this is an escape.
110965668Skris			 */
111065668Skris			if (last_was_cr && ch == escape_char) {
1111181111Sdes				/*
1112181111Sdes				 * It is. Set the flag and continue to
1113181111Sdes				 * next character.
1114181111Sdes				 */
1115181111Sdes				*escape_pendingp = 1;
111665668Skris				continue;
111765668Skris			}
111865668Skris		}
111965668Skris
112065668Skris		/*
112165668Skris		 * Normal character.  Record whether it was a newline,
112265668Skris		 * and append it to the buffer.
112365668Skris		 */
112465668Skris		last_was_cr = (ch == '\r' || ch == '\n');
112565668Skris		buffer_put_char(bin, ch);
112665668Skris		bytes++;
112765668Skris	}
112865668Skris	return bytes;
112965668Skris}
113065668Skris
113192555Sdesstatic void
1132162852Sdesclient_process_input(fd_set *readset)
113360573Skris{
113460573Skris	int len;
1135197679Sdes	char buf[SSH_IOBUFSZ];
113660573Skris
113757429Smarkm	/* Read input from stdin. */
113857429Smarkm	if (FD_ISSET(fileno(stdin), readset)) {
113957429Smarkm		/* Read as much as possible. */
114057429Smarkm		len = read(fileno(stdin), buf, sizeof(buf));
1141181111Sdes		if (len < 0 &&
1142181111Sdes		    (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK))
114376259Sgreen			return;		/* we'll try again later */
114457429Smarkm		if (len <= 0) {
114557429Smarkm			/*
114657429Smarkm			 * Received EOF or error.  They are treated
114757429Smarkm			 * similarly, except that an error message is printed
114857429Smarkm			 * if it was an error condition.
114957429Smarkm			 */
115057429Smarkm			if (len < 0) {
1151181111Sdes				snprintf(buf, sizeof buf, "read: %.100s\r\n",
1152181111Sdes				    strerror(errno));
115357429Smarkm				buffer_append(&stderr_buffer, buf, strlen(buf));
115457429Smarkm			}
115557429Smarkm			/* Mark that we have seen EOF. */
115657429Smarkm			stdin_eof = 1;
115757429Smarkm			/*
115857429Smarkm			 * Send an EOF message to the server unless there is
115957429Smarkm			 * data in the buffer.  If there is data in the
116057429Smarkm			 * buffer, no message will be sent now.  Code
116157429Smarkm			 * elsewhere will send the EOF when the buffer
116257429Smarkm			 * becomes empty if stdin_eof is set.
116357429Smarkm			 */
116457429Smarkm			if (buffer_len(&stdin_buffer) == 0) {
116557429Smarkm				packet_start(SSH_CMSG_EOF);
116657429Smarkm				packet_send();
116757429Smarkm			}
1168181111Sdes		} else if (escape_char1 == SSH_ESCAPECHAR_NONE) {
116957429Smarkm			/*
117057429Smarkm			 * Normal successful read, and no escape character.
117157429Smarkm			 * Just append the data to buffer.
117257429Smarkm			 */
117357429Smarkm			buffer_append(&stdin_buffer, buf, len);
117457429Smarkm		} else {
117557429Smarkm			/*
1176181111Sdes			 * Normal, successful read.  But we have an escape
1177181111Sdes			 * character and have to process the characters one
1178181111Sdes			 * by one.
117957429Smarkm			 */
1180181111Sdes			if (process_escapes(NULL, &stdin_buffer,
1181181111Sdes			    &stdout_buffer, &stderr_buffer, buf, len) == -1)
118265668Skris				return;
118357429Smarkm		}
118457429Smarkm	}
118557429Smarkm}
118657429Smarkm
118792555Sdesstatic void
1188162852Sdesclient_process_output(fd_set *writeset)
118957429Smarkm{
119057429Smarkm	int len;
119157429Smarkm	char buf[100];
119257429Smarkm
119357429Smarkm	/* Write buffered output to stdout. */
119457429Smarkm	if (FD_ISSET(fileno(stdout), writeset)) {
119557429Smarkm		/* Write as much data as possible. */
119657429Smarkm		len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
119757429Smarkm		    buffer_len(&stdout_buffer));
119857429Smarkm		if (len <= 0) {
1199181111Sdes			if (errno == EINTR || errno == EAGAIN ||
1200181111Sdes			    errno == EWOULDBLOCK)
120157429Smarkm				len = 0;
120257429Smarkm			else {
120357429Smarkm				/*
120457429Smarkm				 * An error or EOF was encountered.  Put an
120557429Smarkm				 * error message to stderr buffer.
120657429Smarkm				 */
1207181111Sdes				snprintf(buf, sizeof buf,
1208181111Sdes				    "write stdout: %.50s\r\n", strerror(errno));
120957429Smarkm				buffer_append(&stderr_buffer, buf, strlen(buf));
121057429Smarkm				quit_pending = 1;
121157429Smarkm				return;
121257429Smarkm			}
121357429Smarkm		}
121457429Smarkm		/* Consume printed data from the buffer. */
121557429Smarkm		buffer_consume(&stdout_buffer, len);
121657429Smarkm	}
121757429Smarkm	/* Write buffered output to stderr. */
121857429Smarkm	if (FD_ISSET(fileno(stderr), writeset)) {
121957429Smarkm		/* Write as much data as possible. */
122057429Smarkm		len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
122157429Smarkm		    buffer_len(&stderr_buffer));
122257429Smarkm		if (len <= 0) {
1223181111Sdes			if (errno == EINTR || errno == EAGAIN ||
1224181111Sdes			    errno == EWOULDBLOCK)
122557429Smarkm				len = 0;
122657429Smarkm			else {
1227181111Sdes				/*
1228181111Sdes				 * EOF or error, but can't even print
1229181111Sdes				 * error message.
1230181111Sdes				 */
123157429Smarkm				quit_pending = 1;
123257429Smarkm				return;
123357429Smarkm			}
123457429Smarkm		}
123557429Smarkm		/* Consume printed characters from the buffer. */
123657429Smarkm		buffer_consume(&stderr_buffer, len);
123757429Smarkm	}
123857429Smarkm}
123957429Smarkm
124057429Smarkm/*
124160573Skris * Get packets from the connection input buffer, and process them as long as
124260573Skris * there are packets available.
124360573Skris *
124460573Skris * Any unknown packets received during the actual
124560573Skris * session cause the session to terminate.  This is
124660573Skris * intended to make debugging easier since no
124760573Skris * confirmations are sent.  Any compatible protocol
124860573Skris * extensions must be negotiated during the
124960573Skris * preparatory phase.
125060573Skris */
125160573Skris
125292555Sdesstatic void
125376259Sgreenclient_process_buffered_input_packets(void)
125460573Skris{
1255181111Sdes	dispatch_run(DISPATCH_NONBLOCK, &quit_pending,
1256181111Sdes	    compat20 ? xxx_kex : NULL);
125760573Skris}
125860573Skris
125965668Skris/* scan buf[] for '~' before sending data to the peer */
126065668Skris
1261181111Sdes/* Helper: allocate a new escape_filter_ctx and fill in its escape char */
1262181111Sdesvoid *
1263181111Sdesclient_new_escape_filter_ctx(int escape_char)
126465668Skris{
1265181111Sdes	struct escape_filter_ctx *ret;
1266181111Sdes
1267181111Sdes	ret = xmalloc(sizeof(*ret));
1268181111Sdes	ret->escape_pending = 0;
1269181111Sdes	ret->escape_char = escape_char;
1270181111Sdes	return (void *)ret;
127165668Skris}
127265668Skris
1273181111Sdes/* Free the escape filter context on channel free */
1274181111Sdesvoid
1275181111Sdesclient_filter_cleanup(int cid, void *ctx)
1276181111Sdes{
1277181111Sdes	xfree(ctx);
1278181111Sdes}
1279181111Sdes
1280181111Sdesint
1281181111Sdesclient_simple_escape_filter(Channel *c, char *buf, int len)
1282181111Sdes{
1283181111Sdes	if (c->extended_usage != CHAN_EXTENDED_WRITE)
1284181111Sdes		return 0;
1285181111Sdes
1286181111Sdes	return process_escapes(c, &c->input, &c->output, &c->extended,
1287181111Sdes	    buf, len);
1288181111Sdes}
1289181111Sdes
129092555Sdesstatic void
129176259Sgreenclient_channel_closed(int id, void *arg)
129276259Sgreen{
129392555Sdes	channel_cancel_cleanup(id);
129476259Sgreen	session_closed = 1;
1295204917Sdes	leave_raw_mode(force_tty_flag);
129676259Sgreen}
129776259Sgreen
129860573Skris/*
129957429Smarkm * Implements the interactive session with the server.  This is called after
130057429Smarkm * the user has been authenticated, and a command has been started on the
130192555Sdes * remote host.  If escape_char != SSH_ESCAPECHAR_NONE, it is the character
130292555Sdes * used as an escape character for terminating or suspending the session.
130357429Smarkm */
130457429Smarkm
130560573Skrisint
130665668Skrisclient_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
130757429Smarkm{
130876259Sgreen	fd_set *readset = NULL, *writeset = NULL;
130957429Smarkm	double start_time, total_time;
1310137015Sdes	int max_fd = 0, max_fd2 = 0, len, rekeying = 0;
1311181111Sdes	u_int64_t ibytes, obytes;
1312137015Sdes	u_int nalloc = 0;
131357429Smarkm	char buf[100];
131457429Smarkm
131557429Smarkm	debug("Entering interactive session.");
131657429Smarkm
131757429Smarkm	start_time = get_current_time();
131857429Smarkm
131957429Smarkm	/* Initialize variables. */
1320181111Sdes	escape_pending1 = 0;
132157429Smarkm	last_was_cr = 1;
132257429Smarkm	exit_status = -1;
132357429Smarkm	stdin_eof = 0;
132457429Smarkm	buffer_high = 64 * 1024;
132557429Smarkm	connection_in = packet_get_connection_in();
132657429Smarkm	connection_out = packet_get_connection_out();
132776259Sgreen	max_fd = MAX(connection_in, connection_out);
132876259Sgreen
132976259Sgreen	if (!compat20) {
133076259Sgreen		/* enable nonblocking unless tty */
133176259Sgreen		if (!isatty(fileno(stdin)))
133276259Sgreen			set_nonblock(fileno(stdin));
133376259Sgreen		if (!isatty(fileno(stdout)))
133476259Sgreen			set_nonblock(fileno(stdout));
133576259Sgreen		if (!isatty(fileno(stderr)))
133676259Sgreen			set_nonblock(fileno(stderr));
133776259Sgreen		max_fd = MAX(max_fd, fileno(stdin));
133876259Sgreen		max_fd = MAX(max_fd, fileno(stdout));
133976259Sgreen		max_fd = MAX(max_fd, fileno(stderr));
134076259Sgreen	}
134157429Smarkm	quit_pending = 0;
1342181111Sdes	escape_char1 = escape_char_arg;
134357429Smarkm
134457429Smarkm	/* Initialize buffers. */
134557429Smarkm	buffer_init(&stdin_buffer);
134657429Smarkm	buffer_init(&stdout_buffer);
134757429Smarkm	buffer_init(&stderr_buffer);
134857429Smarkm
134960573Skris	client_init_dispatch();
135060573Skris
1351113908Sdes	/*
1352113908Sdes	 * Set signal handlers, (e.g. to restore non-blocking mode)
1353113908Sdes	 * but don't overwrite SIG_IGN, matches behaviour from rsh(1)
1354113908Sdes	 */
1355146998Sdes	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
1356146998Sdes		signal(SIGHUP, signal_handler);
1357113908Sdes	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1358113908Sdes		signal(SIGINT, signal_handler);
1359113908Sdes	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
1360113908Sdes		signal(SIGQUIT, signal_handler);
1361113908Sdes	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
1362113908Sdes		signal(SIGTERM, signal_handler);
1363146998Sdes	signal(SIGWINCH, window_change_handler);
136457429Smarkm
136557429Smarkm	if (have_pty)
1366204917Sdes		enter_raw_mode(force_tty_flag);
136757429Smarkm
136876259Sgreen	if (compat20) {
136976259Sgreen		session_ident = ssh2_chan_id;
1370181111Sdes		if (escape_char_arg != SSH_ESCAPECHAR_NONE)
137176259Sgreen			channel_register_filter(session_ident,
1372181111Sdes			    client_simple_escape_filter, NULL,
1373181111Sdes			    client_filter_cleanup,
1374181111Sdes			    client_new_escape_filter_ctx(escape_char_arg));
137576259Sgreen		if (session_ident != -1)
137676259Sgreen			channel_register_cleanup(session_ident,
1377157016Sdes			    client_channel_closed, 0);
137876259Sgreen	} else {
137976259Sgreen		/* Check if we should immediately send eof on stdin. */
138060573Skris		client_check_initial_eof_on_stdin();
138176259Sgreen	}
138257429Smarkm
138357429Smarkm	/* Main loop of the client for the interactive session mode. */
138457429Smarkm	while (!quit_pending) {
138557429Smarkm
138657429Smarkm		/* Process buffered packets sent by the server. */
138757429Smarkm		client_process_buffered_input_packets();
138857429Smarkm
138976259Sgreen		if (compat20 && session_closed && !channel_still_open())
139060573Skris			break;
139160573Skris
139276259Sgreen		rekeying = (xxx_kex != NULL && !xxx_kex->done);
139357429Smarkm
139476259Sgreen		if (rekeying) {
139576259Sgreen			debug("rekeying in progress");
139676259Sgreen		} else {
139776259Sgreen			/*
139876259Sgreen			 * Make packets of buffered stdin data, and buffer
139976259Sgreen			 * them for sending to the server.
140076259Sgreen			 */
140176259Sgreen			if (!compat20)
140276259Sgreen				client_make_packets_from_stdin_data();
140357429Smarkm
140476259Sgreen			/*
140576259Sgreen			 * Make packets from buffered channel data, and
140676259Sgreen			 * enqueue them for sending to the server.
140776259Sgreen			 */
140876259Sgreen			if (packet_not_very_much_data_to_write())
140976259Sgreen				channel_output_poll();
141057429Smarkm
141176259Sgreen			/*
141276259Sgreen			 * Check if the window size has changed, and buffer a
141376259Sgreen			 * message about it to the server if so.
141476259Sgreen			 */
141576259Sgreen			client_check_window_change();
141657429Smarkm
141776259Sgreen			if (quit_pending)
141876259Sgreen				break;
141976259Sgreen		}
142057429Smarkm		/*
142157429Smarkm		 * Wait until we have something to do (something becomes
142257429Smarkm		 * available on one of the descriptors).
142357429Smarkm		 */
142492555Sdes		max_fd2 = max_fd;
142576259Sgreen		client_wait_until_can_do_something(&readset, &writeset,
142692555Sdes		    &max_fd2, &nalloc, rekeying);
142757429Smarkm
142857429Smarkm		if (quit_pending)
142957429Smarkm			break;
143057429Smarkm
143176259Sgreen		/* Do channel operations unless rekeying in progress. */
143276259Sgreen		if (!rekeying) {
143376259Sgreen			channel_after_select(readset, writeset);
1434124208Sdes			if (need_rekeying || packet_need_rekeying()) {
1435124208Sdes				debug("need rekeying");
143676259Sgreen				xxx_kex->done = 0;
143776259Sgreen				kex_send_kexinit(xxx_kex);
143876259Sgreen				need_rekeying = 0;
143976259Sgreen			}
144076259Sgreen		}
144176259Sgreen
144260573Skris		/* Buffer input from the connection.  */
144376259Sgreen		client_process_net_input(readset);
144457429Smarkm
144560573Skris		if (quit_pending)
144660573Skris			break;
144757429Smarkm
144860573Skris		if (!compat20) {
144960573Skris			/* Buffer data from stdin */
145076259Sgreen			client_process_input(readset);
145160573Skris			/*
145260573Skris			 * Process output to stdout and stderr.  Output to
145360573Skris			 * the connection is processed elsewhere (above).
145460573Skris			 */
145576259Sgreen			client_process_output(writeset);
145660573Skris		}
145760573Skris
1458204917Sdes		if (session_resumed) {
1459204917Sdes			connection_in = packet_get_connection_in();
1460204917Sdes			connection_out = packet_get_connection_out();
1461204917Sdes			max_fd = MAX(max_fd, connection_out);
1462204917Sdes			max_fd = MAX(max_fd, connection_in);
1463204917Sdes			session_resumed = 0;
1464204917Sdes		}
1465204917Sdes
1466181111Sdes		/*
1467181111Sdes		 * Send as much buffered packet data as possible to the
1468181111Sdes		 * sender.
1469181111Sdes		 */
147076259Sgreen		if (FD_ISSET(connection_out, writeset))
147157429Smarkm			packet_write_poll();
147257429Smarkm	}
147376259Sgreen	if (readset)
147476259Sgreen		xfree(readset);
147576259Sgreen	if (writeset)
147676259Sgreen		xfree(writeset);
147757429Smarkm
147857429Smarkm	/* Terminate the session. */
147957429Smarkm
148057429Smarkm	/* Stop watching for window change. */
1481146998Sdes	signal(SIGWINCH, SIG_DFL);
148257429Smarkm
1483197679Sdes	if (compat20) {
1484197679Sdes		packet_start(SSH2_MSG_DISCONNECT);
1485197679Sdes		packet_put_int(SSH2_DISCONNECT_BY_APPLICATION);
1486197679Sdes		packet_put_cstring("disconnected by user");
1487207319Sdes		packet_put_cstring(""); /* language tag */
1488197679Sdes		packet_send();
1489197679Sdes		packet_write_wait();
1490197679Sdes	}
1491197679Sdes
149292555Sdes	channel_free_all();
149357429Smarkm
149492555Sdes	if (have_pty)
1495204917Sdes		leave_raw_mode(force_tty_flag);
149692555Sdes
149792555Sdes	/* restore blocking io */
149892555Sdes	if (!isatty(fileno(stdin)))
149992555Sdes		unset_nonblock(fileno(stdin));
150092555Sdes	if (!isatty(fileno(stdout)))
150192555Sdes		unset_nonblock(fileno(stdout));
150292555Sdes	if (!isatty(fileno(stderr)))
150392555Sdes		unset_nonblock(fileno(stderr));
150492555Sdes
1505126274Sdes	/*
1506126274Sdes	 * If there was no shell or command requested, there will be no remote
1507126274Sdes	 * exit status to be returned.  In that case, clear error code if the
1508126274Sdes	 * connection was deliberately terminated at this end.
1509126274Sdes	 */
1510126274Sdes	if (no_shell_flag && received_signal == SIGTERM) {
1511126274Sdes		received_signal = 0;
1512126274Sdes		exit_status = 0;
151392555Sdes	}
151492555Sdes
1515126274Sdes	if (received_signal)
1516126274Sdes		fatal("Killed by signal %d.", (int) received_signal);
1517126274Sdes
151857429Smarkm	/*
151957429Smarkm	 * In interactive mode (with pseudo tty) display a message indicating
152057429Smarkm	 * that the connection has been closed.
152157429Smarkm	 */
152257429Smarkm	if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) {
1523181111Sdes		snprintf(buf, sizeof buf,
1524181111Sdes		    "Connection to %.64s closed.\r\n", host);
152557429Smarkm		buffer_append(&stderr_buffer, buf, strlen(buf));
152657429Smarkm	}
152792555Sdes
152857429Smarkm	/* Output any buffered data for stdout. */
152957429Smarkm	while (buffer_len(&stdout_buffer) > 0) {
153057429Smarkm		len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
153157429Smarkm		    buffer_len(&stdout_buffer));
153257429Smarkm		if (len <= 0) {
153357429Smarkm			error("Write failed flushing stdout buffer.");
153457429Smarkm			break;
153557429Smarkm		}
153657429Smarkm		buffer_consume(&stdout_buffer, len);
153757429Smarkm	}
153857429Smarkm
153957429Smarkm	/* Output any buffered data for stderr. */
154057429Smarkm	while (buffer_len(&stderr_buffer) > 0) {
154157429Smarkm		len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
154257429Smarkm		    buffer_len(&stderr_buffer));
154357429Smarkm		if (len <= 0) {
154457429Smarkm			error("Write failed flushing stderr buffer.");
154557429Smarkm			break;
154657429Smarkm		}
154757429Smarkm		buffer_consume(&stderr_buffer, len);
154857429Smarkm	}
154957429Smarkm
155057429Smarkm	/* Clear and free any buffers. */
155157429Smarkm	memset(buf, 0, sizeof(buf));
155257429Smarkm	buffer_free(&stdin_buffer);
155357429Smarkm	buffer_free(&stdout_buffer);
155457429Smarkm	buffer_free(&stderr_buffer);
155557429Smarkm
155657429Smarkm	/* Report bytes transferred, and transfer rates. */
155757429Smarkm	total_time = get_current_time() - start_time;
1558181111Sdes	packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes);
1559181111Sdes	packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes);
1560181111Sdes	verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds",
1561181111Sdes	    obytes, ibytes, total_time);
156257429Smarkm	if (total_time > 0)
1563181111Sdes		verbose("Bytes per second: sent %.1f, received %.1f",
1564181111Sdes		    obytes / total_time, ibytes / total_time);
156557429Smarkm	/* Return the exit status of the program. */
156657429Smarkm	debug("Exit status %d", exit_status);
156757429Smarkm	return exit_status;
156857429Smarkm}
156960573Skris
157060573Skris/*********/
157160573Skris
157292555Sdesstatic void
157392555Sdesclient_input_stdout_data(int type, u_int32_t seq, void *ctxt)
157460573Skris{
157576259Sgreen	u_int data_len;
157660573Skris	char *data = packet_get_string(&data_len);
157792555Sdes	packet_check_eom();
157860573Skris	buffer_append(&stdout_buffer, data, data_len);
157960573Skris	memset(data, 0, data_len);
158060573Skris	xfree(data);
158160573Skris}
158292555Sdesstatic void
158392555Sdesclient_input_stderr_data(int type, u_int32_t seq, void *ctxt)
158460573Skris{
158576259Sgreen	u_int data_len;
158660573Skris	char *data = packet_get_string(&data_len);
158792555Sdes	packet_check_eom();
158860573Skris	buffer_append(&stderr_buffer, data, data_len);
158960573Skris	memset(data, 0, data_len);
159060573Skris	xfree(data);
159160573Skris}
159292555Sdesstatic void
159392555Sdesclient_input_exit_status(int type, u_int32_t seq, void *ctxt)
159460573Skris{
159560573Skris	exit_status = packet_get_int();
159692555Sdes	packet_check_eom();
159760573Skris	/* Acknowledge the exit. */
159860573Skris	packet_start(SSH_CMSG_EXIT_CONFIRMATION);
159960573Skris	packet_send();
160060573Skris	/*
160160573Skris	 * Must wait for packet to be sent since we are
160260573Skris	 * exiting the loop.
160360573Skris	 */
160460573Skris	packet_write_wait();
160560573Skris	/* Flag that we want to exit. */
160660573Skris	quit_pending = 1;
160760573Skris}
1608126274Sdesstatic void
1609126274Sdesclient_input_agent_open(int type, u_int32_t seq, void *ctxt)
1610126274Sdes{
1611126274Sdes	Channel *c = NULL;
1612126274Sdes	int remote_id, sock;
161360573Skris
1614126274Sdes	/* Read the remote channel number from the message. */
1615126274Sdes	remote_id = packet_get_int();
1616126274Sdes	packet_check_eom();
1617126274Sdes
1618126274Sdes	/*
1619126274Sdes	 * Get a connection to the local authentication agent (this may again
1620126274Sdes	 * get forwarded).
1621126274Sdes	 */
1622126274Sdes	sock = ssh_get_authentication_socket();
1623126274Sdes
1624126274Sdes	/*
1625126274Sdes	 * If we could not connect the agent, send an error message back to
1626126274Sdes	 * the server. This should never happen unless the agent dies,
1627126274Sdes	 * because authentication forwarding is only enabled if we have an
1628126274Sdes	 * agent.
1629126274Sdes	 */
1630126274Sdes	if (sock >= 0) {
1631126274Sdes		c = channel_new("", SSH_CHANNEL_OPEN, sock, sock,
1632126274Sdes		    -1, 0, 0, 0, "authentication agent connection", 1);
1633126274Sdes		c->remote_id = remote_id;
1634126274Sdes		c->force_drain = 1;
1635126274Sdes	}
1636126274Sdes	if (c == NULL) {
1637126274Sdes		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
1638126274Sdes		packet_put_int(remote_id);
1639126274Sdes	} else {
1640126274Sdes		/* Send a confirmation to the remote host. */
1641126274Sdes		debug("Forwarding authentication connection.");
1642126274Sdes		packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
1643126274Sdes		packet_put_int(remote_id);
1644126274Sdes		packet_put_int(c->self);
1645126274Sdes	}
1646126274Sdes	packet_send();
1647126274Sdes}
1648126274Sdes
164992555Sdesstatic Channel *
165076259Sgreenclient_request_forwarded_tcpip(const char *request_type, int rchan)
165176259Sgreen{
1652106121Sdes	Channel *c = NULL;
165376259Sgreen	char *listen_address, *originator_address;
1654192595Sdes	u_short listen_port, originator_port;
165576259Sgreen
165676259Sgreen	/* Get rest of the packet */
165776259Sgreen	listen_address = packet_get_string(NULL);
165876259Sgreen	listen_port = packet_get_int();
165976259Sgreen	originator_address = packet_get_string(NULL);
166076259Sgreen	originator_port = packet_get_int();
166192555Sdes	packet_check_eom();
166276259Sgreen
1663181111Sdes	debug("client_request_forwarded_tcpip: listen %s port %d, "
1664181111Sdes	    "originator %s port %d", listen_address, listen_port,
1665181111Sdes	    originator_address, originator_port);
166676259Sgreen
1667181111Sdes	c = channel_connect_by_listen_address(listen_port,
1668181111Sdes	    "forwarded-tcpip", originator_address);
1669181111Sdes
167076259Sgreen	xfree(originator_address);
167176259Sgreen	xfree(listen_address);
167276259Sgreen	return c;
167376259Sgreen}
167476259Sgreen
1675106121Sdesstatic Channel *
167676259Sgreenclient_request_x11(const char *request_type, int rchan)
167776259Sgreen{
167876259Sgreen	Channel *c = NULL;
167976259Sgreen	char *originator;
1680192595Sdes	u_short originator_port;
168192555Sdes	int sock;
168276259Sgreen
168376259Sgreen	if (!options.forward_x11) {
168476259Sgreen		error("Warning: ssh server tried X11 forwarding.");
1685181111Sdes		error("Warning: this is probably a break-in attempt by a "
1686181111Sdes		    "malicious server.");
168776259Sgreen		return NULL;
168876259Sgreen	}
168976259Sgreen	originator = packet_get_string(NULL);
169076259Sgreen	if (datafellows & SSH_BUG_X11FWD) {
169176259Sgreen		debug2("buggy server: x11 request w/o originator_port");
169276259Sgreen		originator_port = 0;
169376259Sgreen	} else {
169476259Sgreen		originator_port = packet_get_int();
169576259Sgreen	}
169692555Sdes	packet_check_eom();
169776259Sgreen	/* XXX check permission */
169876259Sgreen	debug("client_request_x11: request from %s %d", originator,
169976259Sgreen	    originator_port);
170092555Sdes	xfree(originator);
170176259Sgreen	sock = x11_connect_display();
170292555Sdes	if (sock < 0)
170392555Sdes		return NULL;
170492555Sdes	c = channel_new("x11",
170592555Sdes	    SSH_CHANNEL_X11_OPEN, sock, sock, -1,
1706124208Sdes	    CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1);
170792555Sdes	c->force_drain = 1;
170876259Sgreen	return c;
170976259Sgreen}
171076259Sgreen
1711106121Sdesstatic Channel *
171276259Sgreenclient_request_agent(const char *request_type, int rchan)
171376259Sgreen{
171476259Sgreen	Channel *c = NULL;
171592555Sdes	int sock;
171676259Sgreen
171776259Sgreen	if (!options.forward_agent) {
171876259Sgreen		error("Warning: ssh server tried agent forwarding.");
1719181111Sdes		error("Warning: this is probably a break-in attempt by a "
1720181111Sdes		    "malicious server.");
172176259Sgreen		return NULL;
172276259Sgreen	}
1723181111Sdes	sock = ssh_get_authentication_socket();
172492555Sdes	if (sock < 0)
172592555Sdes		return NULL;
172692555Sdes	c = channel_new("authentication agent connection",
172792555Sdes	    SSH_CHANNEL_OPEN, sock, sock, -1,
1728181111Sdes	    CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0,
1729124208Sdes	    "authentication agent connection", 1);
173092555Sdes	c->force_drain = 1;
173176259Sgreen	return c;
173276259Sgreen}
173376259Sgreen
1734181111Sdesint
1735181111Sdesclient_request_tun_fwd(int tun_mode, int local_tun, int remote_tun)
1736181111Sdes{
1737181111Sdes	Channel *c;
1738181111Sdes	int fd;
1739181111Sdes
1740181111Sdes	if (tun_mode == SSH_TUNMODE_NO)
1741181111Sdes		return 0;
1742181111Sdes
1743181111Sdes	if (!compat20) {
1744192595Sdes		error("Tunnel forwarding is not supported for protocol 1");
1745181111Sdes		return -1;
1746181111Sdes	}
1747181111Sdes
1748181111Sdes	debug("Requesting tun unit %d in mode %d", local_tun, tun_mode);
1749181111Sdes
1750181111Sdes	/* Open local tunnel device */
1751181111Sdes	if ((fd = tun_open(local_tun, tun_mode)) == -1) {
1752181111Sdes		error("Tunnel device open failed.");
1753181111Sdes		return -1;
1754181111Sdes	}
1755181111Sdes
1756181111Sdes	c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1,
1757181111Sdes	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1);
1758181111Sdes	c->datagram = 1;
1759181111Sdes
1760181111Sdes#if defined(SSH_TUN_FILTER)
1761181111Sdes	if (options.tun_open == SSH_TUNMODE_POINTOPOINT)
1762181111Sdes		channel_register_filter(c->self, sys_tun_infilter,
1763181111Sdes		    sys_tun_outfilter, NULL, NULL);
1764181111Sdes#endif
1765181111Sdes
1766181111Sdes	packet_start(SSH2_MSG_CHANNEL_OPEN);
1767181111Sdes	packet_put_cstring("tun@openssh.com");
1768181111Sdes	packet_put_int(c->self);
1769181111Sdes	packet_put_int(c->local_window_max);
1770181111Sdes	packet_put_int(c->local_maxpacket);
1771181111Sdes	packet_put_int(tun_mode);
1772181111Sdes	packet_put_int(remote_tun);
1773181111Sdes	packet_send();
1774181111Sdes
1775181111Sdes	return 0;
1776181111Sdes}
1777181111Sdes
177860573Skris/* XXXX move to generic input handler */
177992555Sdesstatic void
178092555Sdesclient_input_channel_open(int type, u_int32_t seq, void *ctxt)
178160573Skris{
178260573Skris	Channel *c = NULL;
178360573Skris	char *ctype;
178460573Skris	int rchan;
178599060Sdes	u_int rmaxpack, rwindow, len;
178660573Skris
178760573Skris	ctype = packet_get_string(&len);
178860573Skris	rchan = packet_get_int();
178960573Skris	rwindow = packet_get_int();
179060573Skris	rmaxpack = packet_get_int();
179160573Skris
179260573Skris	debug("client_input_channel_open: ctype %s rchan %d win %d max %d",
179360573Skris	    ctype, rchan, rwindow, rmaxpack);
179460573Skris
179576259Sgreen	if (strcmp(ctype, "forwarded-tcpip") == 0) {
179676259Sgreen		c = client_request_forwarded_tcpip(ctype, rchan);
179776259Sgreen	} else if (strcmp(ctype, "x11") == 0) {
179876259Sgreen		c = client_request_x11(ctype, rchan);
179976259Sgreen	} else if (strcmp(ctype, "auth-agent@openssh.com") == 0) {
180076259Sgreen		c = client_request_agent(ctype, rchan);
180160573Skris	}
180260573Skris/* XXX duplicate : */
180360573Skris	if (c != NULL) {
180460573Skris		debug("confirm %s", ctype);
180560573Skris		c->remote_id = rchan;
180660573Skris		c->remote_window = rwindow;
180760573Skris		c->remote_maxpacket = rmaxpack;
180892555Sdes		if (c->type != SSH_CHANNEL_CONNECTING) {
180992555Sdes			packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
181092555Sdes			packet_put_int(c->remote_id);
181192555Sdes			packet_put_int(c->self);
181292555Sdes			packet_put_int(c->local_window);
181392555Sdes			packet_put_int(c->local_maxpacket);
181492555Sdes			packet_send();
181592555Sdes		}
181660573Skris	} else {
181760573Skris		debug("failure %s", ctype);
181860573Skris		packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
181960573Skris		packet_put_int(rchan);
182060573Skris		packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED);
182192555Sdes		if (!(datafellows & SSH_BUG_OPENFAILURE)) {
182292555Sdes			packet_put_cstring("open failed");
182392555Sdes			packet_put_cstring("");
182492555Sdes		}
182560573Skris		packet_send();
182660573Skris	}
182760573Skris	xfree(ctype);
182860573Skris}
182992555Sdesstatic void
183092555Sdesclient_input_channel_req(int type, u_int32_t seq, void *ctxt)
183176259Sgreen{
183276259Sgreen	Channel *c = NULL;
1833137015Sdes	int exitval, id, reply, success = 0;
183476259Sgreen	char *rtype;
183560573Skris
183676259Sgreen	id = packet_get_int();
183776259Sgreen	rtype = packet_get_string(NULL);
183876259Sgreen	reply = packet_get_char();
183976259Sgreen
184076259Sgreen	debug("client_input_channel_req: channel %d rtype %s reply %d",
184176259Sgreen	    id, rtype, reply);
184276259Sgreen
1843137015Sdes	if (id == -1) {
1844137015Sdes		error("client_input_channel_req: request for channel -1");
1845137015Sdes	} else if ((c = channel_lookup(id)) == NULL) {
1846181111Sdes		error("client_input_channel_req: channel %d: "
1847181111Sdes		    "unknown channel", id);
1848181111Sdes	} else if (strcmp(rtype, "eow@openssh.com") == 0) {
1849181111Sdes		packet_check_eom();
1850181111Sdes		chan_rcvd_eow(c);
185176259Sgreen	} else if (strcmp(rtype, "exit-status") == 0) {
1852137015Sdes		exitval = packet_get_int();
1853204917Sdes		if (c->ctl_chan != -1) {
1854204917Sdes			mux_exit_message(c, exitval);
1855137015Sdes			success = 1;
1856204917Sdes		} else if (id == session_ident) {
1857204917Sdes			/* Record exit value of local session */
1858204917Sdes			success = 1;
1859137015Sdes			exit_status = exitval;
1860137015Sdes		} else {
1861204917Sdes			/* Probably for a mux channel that has already closed */
1862204917Sdes			debug("%s: no sink for exit-status on channel %d",
1863204917Sdes			    __func__, id);
1864137015Sdes		}
186592555Sdes		packet_check_eom();
186676259Sgreen	}
186776259Sgreen	if (reply) {
186876259Sgreen		packet_start(success ?
186976259Sgreen		    SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
1870192595Sdes		packet_put_int(c->remote_id);
187176259Sgreen		packet_send();
187276259Sgreen	}
187376259Sgreen	xfree(rtype);
187476259Sgreen}
187592555Sdesstatic void
187692555Sdesclient_input_global_request(int type, u_int32_t seq, void *ctxt)
187792555Sdes{
187892555Sdes	char *rtype;
187992555Sdes	int want_reply;
188092555Sdes	int success = 0;
188176259Sgreen
188292555Sdes	rtype = packet_get_string(NULL);
188392555Sdes	want_reply = packet_get_char();
1884126274Sdes	debug("client_input_global_request: rtype %s want_reply %d",
1885126274Sdes	    rtype, want_reply);
188692555Sdes	if (want_reply) {
188792555Sdes		packet_start(success ?
188892555Sdes		    SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE);
188992555Sdes		packet_send();
189092555Sdes		packet_write_wait();
189192555Sdes	}
189292555Sdes	xfree(rtype);
189392555Sdes}
189492555Sdes
1895137015Sdesvoid
1896137015Sdesclient_session2_setup(int id, int want_tty, int want_subsystem,
1897181111Sdes    const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env)
1898137015Sdes{
1899137015Sdes	int len;
1900146998Sdes	Channel *c = NULL;
1901137015Sdes
1902137015Sdes	debug2("%s: id %d", __func__, id);
1903137015Sdes
1904146998Sdes	if ((c = channel_lookup(id)) == NULL)
1905146998Sdes		fatal("client_session2_setup: channel %d: unknown channel", id);
1906146998Sdes
1907137015Sdes	if (want_tty) {
1908137015Sdes		struct winsize ws;
1909137015Sdes
1910137015Sdes		/* Store window size in the packet. */
1911137015Sdes		if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0)
1912137015Sdes			memset(&ws, 0, sizeof(ws));
1913137015Sdes
1914181111Sdes		channel_request_start(id, "pty-req", 1);
1915181111Sdes		client_expect_confirm(id, "PTY allocation", 0);
1916137015Sdes		packet_put_cstring(term != NULL ? term : "");
1917162852Sdes		packet_put_int((u_int)ws.ws_col);
1918162852Sdes		packet_put_int((u_int)ws.ws_row);
1919162852Sdes		packet_put_int((u_int)ws.ws_xpixel);
1920162852Sdes		packet_put_int((u_int)ws.ws_ypixel);
1921181111Sdes		if (tiop == NULL)
1922181111Sdes			tiop = get_saved_tio();
1923181111Sdes		tty_make_modes(-1, tiop);
1924137015Sdes		packet_send();
1925137015Sdes		/* XXX wait for reply */
1926146998Sdes		c->client_tty = 1;
1927137015Sdes	}
1928137015Sdes
1929137015Sdes	/* Transfer any environment variables from client to server */
1930137015Sdes	if (options.num_send_env != 0 && env != NULL) {
1931137015Sdes		int i, j, matched;
1932137015Sdes		char *name, *val;
1933137015Sdes
1934137015Sdes		debug("Sending environment.");
1935137015Sdes		for (i = 0; env[i] != NULL; i++) {
1936137015Sdes			/* Split */
1937137015Sdes			name = xstrdup(env[i]);
1938137015Sdes			if ((val = strchr(name, '=')) == NULL) {
1939157016Sdes				xfree(name);
1940137015Sdes				continue;
1941137015Sdes			}
1942137015Sdes			*val++ = '\0';
1943137015Sdes
1944137015Sdes			matched = 0;
1945137015Sdes			for (j = 0; j < options.num_send_env; j++) {
1946137015Sdes				if (match_pattern(name, options.send_env[j])) {
1947137015Sdes					matched = 1;
1948137015Sdes					break;
1949137015Sdes				}
1950137015Sdes			}
1951137015Sdes			if (!matched) {
1952137015Sdes				debug3("Ignored env %s", name);
1953157016Sdes				xfree(name);
1954137015Sdes				continue;
1955137015Sdes			}
1956137015Sdes
1957137015Sdes			debug("Sending env %s = %s", name, val);
1958137015Sdes			channel_request_start(id, "env", 0);
1959137015Sdes			packet_put_cstring(name);
1960137015Sdes			packet_put_cstring(val);
1961137015Sdes			packet_send();
1962157016Sdes			xfree(name);
1963137015Sdes		}
1964137015Sdes	}
1965137015Sdes
1966137015Sdes	len = buffer_len(cmd);
1967137015Sdes	if (len > 0) {
1968137015Sdes		if (len > 900)
1969137015Sdes			len = 900;
1970137015Sdes		if (want_subsystem) {
1971181111Sdes			debug("Sending subsystem: %.*s",
1972181111Sdes			    len, (u_char*)buffer_ptr(cmd));
1973181111Sdes			channel_request_start(id, "subsystem", 1);
1974181111Sdes			client_expect_confirm(id, "subsystem", 1);
1975137015Sdes		} else {
1976181111Sdes			debug("Sending command: %.*s",
1977181111Sdes			    len, (u_char*)buffer_ptr(cmd));
1978181111Sdes			channel_request_start(id, "exec", 1);
1979181111Sdes			client_expect_confirm(id, "exec", 1);
1980137015Sdes		}
1981137015Sdes		packet_put_string(buffer_ptr(cmd), buffer_len(cmd));
1982137015Sdes		packet_send();
1983137015Sdes	} else {
1984181111Sdes		channel_request_start(id, "shell", 1);
1985181111Sdes		client_expect_confirm(id, "shell", 1);
1986137015Sdes		packet_send();
1987137015Sdes	}
1988137015Sdes}
1989137015Sdes
199092555Sdesstatic void
199176259Sgreenclient_init_dispatch_20(void)
199260573Skris{
199360573Skris	dispatch_init(&dispatch_protocol_error);
199498675Sdes
199560573Skris	dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose);
199660573Skris	dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data);
199760573Skris	dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof);
199860573Skris	dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data);
199960573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open);
200060573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
200160573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
200276259Sgreen	dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req);
200360573Skris	dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust);
2004181111Sdes	dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm);
2005181111Sdes	dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm);
200692555Sdes	dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request);
200776259Sgreen
200876259Sgreen	/* rekeying */
200976259Sgreen	dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
201098675Sdes
201198675Sdes	/* global request reply messages */
201298675Sdes	dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply);
201398675Sdes	dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply);
201460573Skris}
2015181111Sdes
201692555Sdesstatic void
201776259Sgreenclient_init_dispatch_13(void)
201860573Skris{
201960573Skris	dispatch_init(NULL);
202060573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close);
202160573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation);
202260573Skris	dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data);
202360573Skris	dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
202460573Skris	dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
202560573Skris	dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open);
202660573Skris	dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status);
202760573Skris	dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data);
202860573Skris	dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data);
202968700Sgreen
203068700Sgreen	dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ?
2031126274Sdes	    &client_input_agent_open : &deny_input_open);
203268700Sgreen	dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ?
203369587Sgreen	    &x11_input_open : &deny_input_open);
203460573Skris}
2035181111Sdes
203692555Sdesstatic void
203776259Sgreenclient_init_dispatch_15(void)
203860573Skris{
203960573Skris	client_init_dispatch_13();
204060573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof);
204160573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose);
204260573Skris}
2043181111Sdes
204492555Sdesstatic void
204576259Sgreenclient_init_dispatch(void)
204660573Skris{
204760573Skris	if (compat20)
204860573Skris		client_init_dispatch_20();
204960573Skris	else if (compat13)
205060573Skris		client_init_dispatch_13();
205160573Skris	else
205260573Skris		client_init_dispatch_15();
205360573Skris}
2054126274Sdes
2055126274Sdes/* client specific fatal cleanup */
2056126274Sdesvoid
2057126274Sdescleanup_exit(int i)
2058126274Sdes{
2059204917Sdes	leave_raw_mode(force_tty_flag);
2060126274Sdes	leave_non_blocking();
2061181111Sdes	if (options.control_path != NULL && muxserver_sock != -1)
2062137015Sdes		unlink(options.control_path);
2063126274Sdes	_exit(i);
2064126274Sdes}
2065