xref: /freebsd/crypto/openssh/clientloop.c (revision 021d409f5beb1827f72d24f171e3c3ed233ed62a)
1511b41d2SMark Murray /*
2511b41d2SMark Murray  * Author: Tatu Ylonen <ylo@cs.hut.fi>
3511b41d2SMark Murray  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4511b41d2SMark Murray  *                    All rights reserved
5511b41d2SMark Murray  * The main loop for the interactive session (client side).
6511b41d2SMark Murray  *
7b66f2d16SKris Kennaway  * As far as I am concerned, the code I have written for this software
8b66f2d16SKris Kennaway  * can be used freely for any purpose.  Any derived versions of this
9b66f2d16SKris Kennaway  * software must be clearly marked as such, and if the derived work is
10b66f2d16SKris Kennaway  * incompatible with the protocol description in the RFC file, it must be
11b66f2d16SKris Kennaway  * called by a name other than "ssh" or "Secure Shell".
12b66f2d16SKris Kennaway  *
13b66f2d16SKris Kennaway  *
14b66f2d16SKris Kennaway  * Copyright (c) 1999 Theo de Raadt.  All rights reserved.
15b66f2d16SKris Kennaway  *
16b66f2d16SKris Kennaway  * Redistribution and use in source and binary forms, with or without
17b66f2d16SKris Kennaway  * modification, are permitted provided that the following conditions
18b66f2d16SKris Kennaway  * are met:
19b66f2d16SKris Kennaway  * 1. Redistributions of source code must retain the above copyright
20b66f2d16SKris Kennaway  *    notice, this list of conditions and the following disclaimer.
21b66f2d16SKris Kennaway  * 2. Redistributions in binary form must reproduce the above copyright
22b66f2d16SKris Kennaway  *    notice, this list of conditions and the following disclaimer in the
23b66f2d16SKris Kennaway  *    documentation and/or other materials provided with the distribution.
24b66f2d16SKris Kennaway  *
25b66f2d16SKris Kennaway  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26b66f2d16SKris Kennaway  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27b66f2d16SKris Kennaway  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28b66f2d16SKris Kennaway  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29b66f2d16SKris Kennaway  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30b66f2d16SKris Kennaway  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31b66f2d16SKris Kennaway  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32b66f2d16SKris Kennaway  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33b66f2d16SKris Kennaway  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34b66f2d16SKris Kennaway  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35b66f2d16SKris Kennaway  *
36b66f2d16SKris Kennaway  *
37a04a10f8SKris Kennaway  * SSH2 support added by Markus Friedl.
38ae1f160dSDag-Erling Smørgrav  * Copyright (c) 1999, 2000, 2001 Markus Friedl.  All rights reserved.
39b66f2d16SKris Kennaway  *
40b66f2d16SKris Kennaway  * Redistribution and use in source and binary forms, with or without
41b66f2d16SKris Kennaway  * modification, are permitted provided that the following conditions
42b66f2d16SKris Kennaway  * are met:
43b66f2d16SKris Kennaway  * 1. Redistributions of source code must retain the above copyright
44b66f2d16SKris Kennaway  *    notice, this list of conditions and the following disclaimer.
45b66f2d16SKris Kennaway  * 2. Redistributions in binary form must reproduce the above copyright
46b66f2d16SKris Kennaway  *    notice, this list of conditions and the following disclaimer in the
47b66f2d16SKris Kennaway  *    documentation and/or other materials provided with the distribution.
48b66f2d16SKris Kennaway  *
49b66f2d16SKris Kennaway  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
50b66f2d16SKris Kennaway  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
51b66f2d16SKris Kennaway  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
52b66f2d16SKris Kennaway  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
53b66f2d16SKris Kennaway  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
54b66f2d16SKris Kennaway  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
55b66f2d16SKris Kennaway  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
56b66f2d16SKris Kennaway  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
57b66f2d16SKris Kennaway  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
58b66f2d16SKris Kennaway  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59511b41d2SMark Murray  */
60511b41d2SMark Murray 
61511b41d2SMark Murray #include "includes.h"
62021d409fSDag-Erling Smørgrav RCSID("$OpenBSD: clientloop.c,v 1.149 2005/12/30 15:56:37 reyk Exp $");
63511b41d2SMark Murray 
64511b41d2SMark Murray #include "ssh.h"
651e8db6e2SBrian Feldman #include "ssh1.h"
661e8db6e2SBrian Feldman #include "ssh2.h"
671e8db6e2SBrian Feldman #include "xmalloc.h"
68511b41d2SMark Murray #include "packet.h"
69511b41d2SMark Murray #include "buffer.h"
70a04a10f8SKris Kennaway #include "compat.h"
71a04a10f8SKris Kennaway #include "channels.h"
72a04a10f8SKris Kennaway #include "dispatch.h"
73b66f2d16SKris Kennaway #include "buffer.h"
74b66f2d16SKris Kennaway #include "bufaux.h"
751e8db6e2SBrian Feldman #include "key.h"
761e8db6e2SBrian Feldman #include "kex.h"
771e8db6e2SBrian Feldman #include "log.h"
781e8db6e2SBrian Feldman #include "readconf.h"
791e8db6e2SBrian Feldman #include "clientloop.h"
80021d409fSDag-Erling Smørgrav #include "sshconnect.h"
811e8db6e2SBrian Feldman #include "authfd.h"
821e8db6e2SBrian Feldman #include "atomicio.h"
83d74d50a8SDag-Erling Smørgrav #include "sshpty.h"
841e8db6e2SBrian Feldman #include "misc.h"
85d74d50a8SDag-Erling Smørgrav #include "monitor_fdpass.h"
86d74d50a8SDag-Erling Smørgrav #include "match.h"
87d74d50a8SDag-Erling Smørgrav #include "msg.h"
885b9b2fafSBrian Feldman 
895b9b2fafSBrian Feldman /* import options */
904899dde7SBrian Feldman extern Options options;
914899dde7SBrian Feldman 
92511b41d2SMark Murray /* Flag indicating that stdin should be redirected from /dev/null. */
93511b41d2SMark Murray extern int stdin_null_flag;
94511b41d2SMark Murray 
95efcad6b7SDag-Erling Smørgrav /* Flag indicating that no shell has been requested */
96efcad6b7SDag-Erling Smørgrav extern int no_shell_flag;
97efcad6b7SDag-Erling Smørgrav 
98d74d50a8SDag-Erling Smørgrav /* Control socket */
99d74d50a8SDag-Erling Smørgrav extern int control_fd;
100d74d50a8SDag-Erling Smørgrav 
101511b41d2SMark Murray /*
102511b41d2SMark Murray  * Name of the host we are connecting to.  This is the name given on the
103511b41d2SMark Murray  * command line, or the HostName specified for the user-supplied name in a
104511b41d2SMark Murray  * configuration file.
105511b41d2SMark Murray  */
106511b41d2SMark Murray extern char *host;
107511b41d2SMark Murray 
108511b41d2SMark Murray /*
109511b41d2SMark Murray  * Flag to indicate that we have received a window change signal which has
110511b41d2SMark Murray  * not yet been processed.  This will cause a message indicating the new
111511b41d2SMark Murray  * window size to be sent to the server a little later.  This is volatile
112511b41d2SMark Murray  * because this is updated in a signal handler.
113511b41d2SMark Murray  */
114ae1f160dSDag-Erling Smørgrav static volatile sig_atomic_t received_window_change_signal = 0;
115ae1f160dSDag-Erling Smørgrav static volatile sig_atomic_t received_signal = 0;
116511b41d2SMark Murray 
117021d409fSDag-Erling Smørgrav /* Flag indicating whether the user's terminal is in non-blocking mode. */
118511b41d2SMark Murray static int in_non_blocking_mode = 0;
119511b41d2SMark Murray 
120511b41d2SMark Murray /* Common data for the client loop code. */
121b66f2d16SKris Kennaway static int quit_pending;	/* Set to non-zero to quit the client loop. */
122b66f2d16SKris Kennaway static int escape_char;		/* Escape character. */
123511b41d2SMark Murray static int escape_pending;	/* Last character was the escape character */
124511b41d2SMark Murray static int last_was_cr;		/* Last character was a newline. */
125511b41d2SMark Murray static int exit_status;		/* Used to store the exit status of the command. */
126511b41d2SMark Murray static int stdin_eof;		/* EOF has been encountered on standard error. */
127511b41d2SMark Murray static Buffer stdin_buffer;	/* Buffer for stdin data. */
128511b41d2SMark Murray static Buffer stdout_buffer;	/* Buffer for stdout data. */
129511b41d2SMark Murray static Buffer stderr_buffer;	/* Buffer for stderr data. */
1301e8db6e2SBrian Feldman static u_long stdin_bytes, stdout_bytes, stderr_bytes;
1311e8db6e2SBrian Feldman static u_int buffer_high;/* Soft max buffer size. */
132511b41d2SMark Murray static int connection_in;	/* Connection to server (input). */
133511b41d2SMark Murray static int connection_out;	/* Connection to server (output). */
1341e8db6e2SBrian Feldman static int need_rekeying;	/* Set to non-zero if rekeying is requested. */
1351e8db6e2SBrian Feldman static int session_closed = 0;	/* In SSH2: login session closed. */
136efcad6b7SDag-Erling Smørgrav static int server_alive_timeouts = 0;
137a04a10f8SKris Kennaway 
138ae1f160dSDag-Erling Smørgrav static void client_init_dispatch(void);
139a04a10f8SKris Kennaway int	session_ident = -1;
140a04a10f8SKris Kennaway 
141d74d50a8SDag-Erling Smørgrav struct confirm_ctx {
142d74d50a8SDag-Erling Smørgrav 	int want_tty;
143d74d50a8SDag-Erling Smørgrav 	int want_subsys;
144043840dfSDag-Erling Smørgrav 	int want_x_fwd;
145043840dfSDag-Erling Smørgrav 	int want_agent_fwd;
146d74d50a8SDag-Erling Smørgrav 	Buffer cmd;
147d74d50a8SDag-Erling Smørgrav 	char *term;
148d74d50a8SDag-Erling Smørgrav 	struct termios tio;
149d74d50a8SDag-Erling Smørgrav 	char **env;
150d74d50a8SDag-Erling Smørgrav };
151d74d50a8SDag-Erling Smørgrav 
1521e8db6e2SBrian Feldman /*XXX*/
1531e8db6e2SBrian Feldman extern Kex *xxx_kex;
154511b41d2SMark Murray 
155d74d50a8SDag-Erling Smørgrav void ssh_process_session2_setup(int, int, int, Buffer *);
156d74d50a8SDag-Erling Smørgrav 
157511b41d2SMark Murray /* Restores stdin to blocking mode. */
158511b41d2SMark Murray 
159ae1f160dSDag-Erling Smørgrav static void
1601e8db6e2SBrian Feldman leave_non_blocking(void)
161511b41d2SMark Murray {
162511b41d2SMark Murray 	if (in_non_blocking_mode) {
163d74d50a8SDag-Erling Smørgrav 		unset_nonblock(fileno(stdin));
164511b41d2SMark Murray 		in_non_blocking_mode = 0;
165511b41d2SMark Murray 	}
166511b41d2SMark Murray }
167511b41d2SMark Murray 
168511b41d2SMark Murray /* Puts stdin terminal in non-blocking mode. */
169511b41d2SMark Murray 
170ae1f160dSDag-Erling Smørgrav static void
1711e8db6e2SBrian Feldman enter_non_blocking(void)
172511b41d2SMark Murray {
173511b41d2SMark Murray 	in_non_blocking_mode = 1;
174d74d50a8SDag-Erling Smørgrav 	set_nonblock(fileno(stdin));
175511b41d2SMark Murray }
176511b41d2SMark Murray 
177511b41d2SMark Murray /*
178511b41d2SMark Murray  * Signal handler for the window change signal (SIGWINCH).  This just sets a
179511b41d2SMark Murray  * flag indicating that the window has changed.
180511b41d2SMark Murray  */
181511b41d2SMark Murray 
182ae1f160dSDag-Erling Smørgrav static void
183511b41d2SMark Murray window_change_handler(int sig)
184511b41d2SMark Murray {
185511b41d2SMark Murray 	received_window_change_signal = 1;
186511b41d2SMark Murray 	signal(SIGWINCH, window_change_handler);
187511b41d2SMark Murray }
188511b41d2SMark Murray 
189511b41d2SMark Murray /*
190511b41d2SMark Murray  * Signal handler for signals that cause the program to terminate.  These
191511b41d2SMark Murray  * signals must be trapped to restore terminal modes.
192511b41d2SMark Murray  */
193511b41d2SMark Murray 
194ae1f160dSDag-Erling Smørgrav static void
195511b41d2SMark Murray signal_handler(int sig)
196511b41d2SMark Murray {
197ae1f160dSDag-Erling Smørgrav 	received_signal = sig;
198ae1f160dSDag-Erling Smørgrav 	quit_pending = 1;
199511b41d2SMark Murray }
200511b41d2SMark Murray 
201511b41d2SMark Murray /*
202511b41d2SMark Murray  * Returns current time in seconds from Jan 1, 1970 with the maximum
203511b41d2SMark Murray  * available resolution.
204511b41d2SMark Murray  */
205511b41d2SMark Murray 
206ae1f160dSDag-Erling Smørgrav static double
2071e8db6e2SBrian Feldman get_current_time(void)
208511b41d2SMark Murray {
209511b41d2SMark Murray 	struct timeval tv;
210511b41d2SMark Murray 	gettimeofday(&tv, NULL);
211511b41d2SMark Murray 	return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
212511b41d2SMark Murray }
213511b41d2SMark Murray 
214043840dfSDag-Erling Smørgrav #define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1"
215043840dfSDag-Erling Smørgrav void
216043840dfSDag-Erling Smørgrav client_x11_get_proto(const char *display, const char *xauth_path,
217043840dfSDag-Erling Smørgrav     u_int trusted, char **_proto, char **_data)
218043840dfSDag-Erling Smørgrav {
219043840dfSDag-Erling Smørgrav 	char cmd[1024];
220043840dfSDag-Erling Smørgrav 	char line[512];
221043840dfSDag-Erling Smørgrav 	char xdisplay[512];
222043840dfSDag-Erling Smørgrav 	static char proto[512], data[512];
223043840dfSDag-Erling Smørgrav 	FILE *f;
224043840dfSDag-Erling Smørgrav 	int got_data = 0, generated = 0, do_unlink = 0, i;
225043840dfSDag-Erling Smørgrav 	char *xauthdir, *xauthfile;
226043840dfSDag-Erling Smørgrav 	struct stat st;
227043840dfSDag-Erling Smørgrav 
228043840dfSDag-Erling Smørgrav 	xauthdir = xauthfile = NULL;
229043840dfSDag-Erling Smørgrav 	*_proto = proto;
230043840dfSDag-Erling Smørgrav 	*_data = data;
231043840dfSDag-Erling Smørgrav 	proto[0] = data[0] = '\0';
232043840dfSDag-Erling Smørgrav 
233043840dfSDag-Erling Smørgrav 	if (xauth_path == NULL ||(stat(xauth_path, &st) == -1)) {
234043840dfSDag-Erling Smørgrav 		debug("No xauth program.");
235043840dfSDag-Erling Smørgrav 	} else {
236043840dfSDag-Erling Smørgrav 		if (display == NULL) {
237043840dfSDag-Erling Smørgrav 			debug("x11_get_proto: DISPLAY not set");
238043840dfSDag-Erling Smørgrav 			return;
239043840dfSDag-Erling Smørgrav 		}
240043840dfSDag-Erling Smørgrav 		/*
241043840dfSDag-Erling Smørgrav 		 * Handle FamilyLocal case where $DISPLAY does
242043840dfSDag-Erling Smørgrav 		 * not match an authorization entry.  For this we
243043840dfSDag-Erling Smørgrav 		 * just try "xauth list unix:displaynum.screennum".
244043840dfSDag-Erling Smørgrav 		 * XXX: "localhost" match to determine FamilyLocal
245043840dfSDag-Erling Smørgrav 		 *      is not perfect.
246043840dfSDag-Erling Smørgrav 		 */
247043840dfSDag-Erling Smørgrav 		if (strncmp(display, "localhost:", 10) == 0) {
248043840dfSDag-Erling Smørgrav 			snprintf(xdisplay, sizeof(xdisplay), "unix:%s",
249043840dfSDag-Erling Smørgrav 			    display + 10);
250043840dfSDag-Erling Smørgrav 			display = xdisplay;
251043840dfSDag-Erling Smørgrav 		}
252043840dfSDag-Erling Smørgrav 		if (trusted == 0) {
253043840dfSDag-Erling Smørgrav 			xauthdir = xmalloc(MAXPATHLEN);
254043840dfSDag-Erling Smørgrav 			xauthfile = xmalloc(MAXPATHLEN);
255043840dfSDag-Erling Smørgrav 			strlcpy(xauthdir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN);
256043840dfSDag-Erling Smørgrav 			if (mkdtemp(xauthdir) != NULL) {
257043840dfSDag-Erling Smørgrav 				do_unlink = 1;
258043840dfSDag-Erling Smørgrav 				snprintf(xauthfile, MAXPATHLEN, "%s/xauthfile",
259043840dfSDag-Erling Smørgrav 				    xauthdir);
260043840dfSDag-Erling Smørgrav 				snprintf(cmd, sizeof(cmd),
261043840dfSDag-Erling Smørgrav 				    "%s -f %s generate %s " SSH_X11_PROTO
262043840dfSDag-Erling Smørgrav 				    " untrusted timeout 1200 2>" _PATH_DEVNULL,
263043840dfSDag-Erling Smørgrav 				    xauth_path, xauthfile, display);
264043840dfSDag-Erling Smørgrav 				debug2("x11_get_proto: %s", cmd);
265043840dfSDag-Erling Smørgrav 				if (system(cmd) == 0)
266043840dfSDag-Erling Smørgrav 					generated = 1;
267043840dfSDag-Erling Smørgrav 			}
268043840dfSDag-Erling Smørgrav 		}
269043840dfSDag-Erling Smørgrav 		snprintf(cmd, sizeof(cmd),
270021d409fSDag-Erling Smørgrav 		    "%s %s%s list %s 2>" _PATH_DEVNULL,
271043840dfSDag-Erling Smørgrav 		    xauth_path,
272043840dfSDag-Erling Smørgrav 		    generated ? "-f " : "" ,
273043840dfSDag-Erling Smørgrav 		    generated ? xauthfile : "",
274043840dfSDag-Erling Smørgrav 		    display);
275043840dfSDag-Erling Smørgrav 		debug2("x11_get_proto: %s", cmd);
276043840dfSDag-Erling Smørgrav 		f = popen(cmd, "r");
277043840dfSDag-Erling Smørgrav 		if (f && fgets(line, sizeof(line), f) &&
278043840dfSDag-Erling Smørgrav 		    sscanf(line, "%*s %511s %511s", proto, data) == 2)
279043840dfSDag-Erling Smørgrav 			got_data = 1;
280043840dfSDag-Erling Smørgrav 		if (f)
281043840dfSDag-Erling Smørgrav 			pclose(f);
282043840dfSDag-Erling Smørgrav 	}
283043840dfSDag-Erling Smørgrav 
284043840dfSDag-Erling Smørgrav 	if (do_unlink) {
285043840dfSDag-Erling Smørgrav 		unlink(xauthfile);
286043840dfSDag-Erling Smørgrav 		rmdir(xauthdir);
287043840dfSDag-Erling Smørgrav 	}
288043840dfSDag-Erling Smørgrav 	if (xauthdir)
289043840dfSDag-Erling Smørgrav 		xfree(xauthdir);
290043840dfSDag-Erling Smørgrav 	if (xauthfile)
291043840dfSDag-Erling Smørgrav 		xfree(xauthfile);
292043840dfSDag-Erling Smørgrav 
293043840dfSDag-Erling Smørgrav 	/*
294043840dfSDag-Erling Smørgrav 	 * If we didn't get authentication data, just make up some
295043840dfSDag-Erling Smørgrav 	 * data.  The forwarding code will check the validity of the
296043840dfSDag-Erling Smørgrav 	 * response anyway, and substitute this data.  The X11
297043840dfSDag-Erling Smørgrav 	 * server, however, will ignore this fake data and use
298043840dfSDag-Erling Smørgrav 	 * whatever authentication mechanisms it was using otherwise
299043840dfSDag-Erling Smørgrav 	 * for the local connection.
300043840dfSDag-Erling Smørgrav 	 */
301043840dfSDag-Erling Smørgrav 	if (!got_data) {
302043840dfSDag-Erling Smørgrav 		u_int32_t rnd = 0;
303043840dfSDag-Erling Smørgrav 
304043840dfSDag-Erling Smørgrav 		logit("Warning: No xauth data; "
305043840dfSDag-Erling Smørgrav 		    "using fake authentication data for X11 forwarding.");
306043840dfSDag-Erling Smørgrav 		strlcpy(proto, SSH_X11_PROTO, sizeof proto);
307043840dfSDag-Erling Smørgrav 		for (i = 0; i < 16; i++) {
308043840dfSDag-Erling Smørgrav 			if (i % 4 == 0)
309043840dfSDag-Erling Smørgrav 				rnd = arc4random();
310043840dfSDag-Erling Smørgrav 			snprintf(data + 2 * i, sizeof data - 2 * i, "%02x",
311043840dfSDag-Erling Smørgrav 			    rnd & 0xff);
312043840dfSDag-Erling Smørgrav 			rnd >>= 8;
313043840dfSDag-Erling Smørgrav 		}
314043840dfSDag-Erling Smørgrav 	}
315043840dfSDag-Erling Smørgrav }
316043840dfSDag-Erling Smørgrav 
317511b41d2SMark Murray /*
318511b41d2SMark Murray  * This is called when the interactive is entered.  This checks if there is
319511b41d2SMark Murray  * an EOF coming on stdin.  We must check this explicitly, as select() does
320511b41d2SMark Murray  * not appear to wake up when redirecting from /dev/null.
321511b41d2SMark Murray  */
322511b41d2SMark Murray 
323ae1f160dSDag-Erling Smørgrav static void
3241e8db6e2SBrian Feldman client_check_initial_eof_on_stdin(void)
325511b41d2SMark Murray {
326511b41d2SMark Murray 	int len;
327511b41d2SMark Murray 	char buf[1];
328511b41d2SMark Murray 
329511b41d2SMark Murray 	/*
330511b41d2SMark Murray 	 * If standard input is to be "redirected from /dev/null", we simply
331511b41d2SMark Murray 	 * mark that we have seen an EOF and send an EOF message to the
332511b41d2SMark Murray 	 * server. Otherwise, we try to read a single character; it appears
333511b41d2SMark Murray 	 * that for some files, such /dev/null, select() never wakes up for
334511b41d2SMark Murray 	 * read for this descriptor, which means that we never get EOF.  This
335511b41d2SMark Murray 	 * way we will get the EOF if stdin comes from /dev/null or similar.
336511b41d2SMark Murray 	 */
337511b41d2SMark Murray 	if (stdin_null_flag) {
338511b41d2SMark Murray 		/* Fake EOF on stdin. */
339511b41d2SMark Murray 		debug("Sending eof.");
340511b41d2SMark Murray 		stdin_eof = 1;
341511b41d2SMark Murray 		packet_start(SSH_CMSG_EOF);
342511b41d2SMark Murray 		packet_send();
343511b41d2SMark Murray 	} else {
344511b41d2SMark Murray 		enter_non_blocking();
345511b41d2SMark Murray 
346511b41d2SMark Murray 		/* Check for immediate EOF on stdin. */
347511b41d2SMark Murray 		len = read(fileno(stdin), buf, 1);
348511b41d2SMark Murray 		if (len == 0) {
349511b41d2SMark Murray 			/* EOF.  Record that we have seen it and send EOF to server. */
350511b41d2SMark Murray 			debug("Sending eof.");
351511b41d2SMark Murray 			stdin_eof = 1;
352511b41d2SMark Murray 			packet_start(SSH_CMSG_EOF);
353511b41d2SMark Murray 			packet_send();
354511b41d2SMark Murray 		} else if (len > 0) {
355511b41d2SMark Murray 			/*
356511b41d2SMark Murray 			 * Got data.  We must store the data in the buffer,
357511b41d2SMark Murray 			 * and also process it as an escape character if
358511b41d2SMark Murray 			 * appropriate.
359511b41d2SMark Murray 			 */
3601e8db6e2SBrian Feldman 			if ((u_char) buf[0] == escape_char)
361511b41d2SMark Murray 				escape_pending = 1;
3621e8db6e2SBrian Feldman 			else
363511b41d2SMark Murray 				buffer_append(&stdin_buffer, buf, 1);
364511b41d2SMark Murray 		}
365511b41d2SMark Murray 		leave_non_blocking();
366511b41d2SMark Murray 	}
367511b41d2SMark Murray }
368511b41d2SMark Murray 
369511b41d2SMark Murray 
370511b41d2SMark Murray /*
371511b41d2SMark Murray  * Make packets from buffered stdin data, and buffer them for sending to the
372511b41d2SMark Murray  * connection.
373511b41d2SMark Murray  */
374511b41d2SMark Murray 
375ae1f160dSDag-Erling Smørgrav static void
3761e8db6e2SBrian Feldman client_make_packets_from_stdin_data(void)
377511b41d2SMark Murray {
3781e8db6e2SBrian Feldman 	u_int len;
379511b41d2SMark Murray 
380511b41d2SMark Murray 	/* Send buffered stdin data to the server. */
381511b41d2SMark Murray 	while (buffer_len(&stdin_buffer) > 0 &&
382511b41d2SMark Murray 	    packet_not_very_much_data_to_write()) {
383511b41d2SMark Murray 		len = buffer_len(&stdin_buffer);
384511b41d2SMark Murray 		/* Keep the packets at reasonable size. */
385511b41d2SMark Murray 		if (len > packet_get_maxsize())
386511b41d2SMark Murray 			len = packet_get_maxsize();
387511b41d2SMark Murray 		packet_start(SSH_CMSG_STDIN_DATA);
388511b41d2SMark Murray 		packet_put_string(buffer_ptr(&stdin_buffer), len);
389511b41d2SMark Murray 		packet_send();
390511b41d2SMark Murray 		buffer_consume(&stdin_buffer, len);
3911e8db6e2SBrian Feldman 		stdin_bytes += len;
392511b41d2SMark Murray 		/* If we have a pending EOF, send it now. */
393511b41d2SMark Murray 		if (stdin_eof && buffer_len(&stdin_buffer) == 0) {
394511b41d2SMark Murray 			packet_start(SSH_CMSG_EOF);
395511b41d2SMark Murray 			packet_send();
396511b41d2SMark Murray 		}
397511b41d2SMark Murray 	}
398511b41d2SMark Murray }
399511b41d2SMark Murray 
400511b41d2SMark Murray /*
401511b41d2SMark Murray  * Checks if the client window has changed, and sends a packet about it to
402511b41d2SMark Murray  * the server if so.  The actual change is detected elsewhere (by a software
403511b41d2SMark Murray  * interrupt on Unix); this just checks the flag and sends a message if
404511b41d2SMark Murray  * appropriate.
405511b41d2SMark Murray  */
406511b41d2SMark Murray 
407ae1f160dSDag-Erling Smørgrav static void
4081e8db6e2SBrian Feldman client_check_window_change(void)
409511b41d2SMark Murray {
410511b41d2SMark Murray 	struct winsize ws;
411511b41d2SMark Murray 
412a04a10f8SKris Kennaway 	if (! received_window_change_signal)
413a04a10f8SKris Kennaway 		return;
414a04a10f8SKris Kennaway 	/** XXX race */
415511b41d2SMark Murray 	received_window_change_signal = 0;
416511b41d2SMark Murray 
4175b9b2fafSBrian Feldman 	debug2("client_check_window_change: changed");
418a04a10f8SKris Kennaway 
419a04a10f8SKris Kennaway 	if (compat20) {
420d74d50a8SDag-Erling Smørgrav 		channel_send_window_changes();
421a04a10f8SKris Kennaway 	} else {
422d74d50a8SDag-Erling Smørgrav 		if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
423d74d50a8SDag-Erling Smørgrav 			return;
424511b41d2SMark Murray 		packet_start(SSH_CMSG_WINDOW_SIZE);
425511b41d2SMark Murray 		packet_put_int(ws.ws_row);
426511b41d2SMark Murray 		packet_put_int(ws.ws_col);
427511b41d2SMark Murray 		packet_put_int(ws.ws_xpixel);
428511b41d2SMark Murray 		packet_put_int(ws.ws_ypixel);
429511b41d2SMark Murray 		packet_send();
430511b41d2SMark Murray 	}
431511b41d2SMark Murray }
432511b41d2SMark Murray 
433efcad6b7SDag-Erling Smørgrav static void
434efcad6b7SDag-Erling Smørgrav client_global_request_reply(int type, u_int32_t seq, void *ctxt)
435efcad6b7SDag-Erling Smørgrav {
436efcad6b7SDag-Erling Smørgrav 	server_alive_timeouts = 0;
437efcad6b7SDag-Erling Smørgrav 	client_global_request_reply_fwd(type, seq, ctxt);
438efcad6b7SDag-Erling Smørgrav }
439efcad6b7SDag-Erling Smørgrav 
440efcad6b7SDag-Erling Smørgrav static void
441efcad6b7SDag-Erling Smørgrav server_alive_check(void)
442efcad6b7SDag-Erling Smørgrav {
443efcad6b7SDag-Erling Smørgrav 	if (++server_alive_timeouts > options.server_alive_count_max)
444efcad6b7SDag-Erling Smørgrav 		packet_disconnect("Timeout, server not responding.");
445efcad6b7SDag-Erling Smørgrav 	packet_start(SSH2_MSG_GLOBAL_REQUEST);
446efcad6b7SDag-Erling Smørgrav 	packet_put_cstring("keepalive@openssh.com");
447efcad6b7SDag-Erling Smørgrav 	packet_put_char(1);     /* boolean: want reply */
448efcad6b7SDag-Erling Smørgrav 	packet_send();
449efcad6b7SDag-Erling Smørgrav }
450efcad6b7SDag-Erling Smørgrav 
451511b41d2SMark Murray /*
452511b41d2SMark Murray  * Waits until the client can do something (some data becomes available on
453511b41d2SMark Murray  * one of the file descriptors).
454511b41d2SMark Murray  */
455ae1f160dSDag-Erling Smørgrav static void
4561e8db6e2SBrian Feldman client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
457d74d50a8SDag-Erling Smørgrav     int *maxfdp, u_int *nallocp, int rekeying)
458511b41d2SMark Murray {
459efcad6b7SDag-Erling Smørgrav 	struct timeval tv, *tvp;
460efcad6b7SDag-Erling Smørgrav 	int ret;
461efcad6b7SDag-Erling Smørgrav 
4621e8db6e2SBrian Feldman 	/* Add any selections by the channel mechanism. */
463ae1f160dSDag-Erling Smørgrav 	channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying);
464511b41d2SMark Murray 
465a04a10f8SKris Kennaway 	if (!compat20) {
466511b41d2SMark Murray 		/* Read from the connection, unless our buffers are full. */
467511b41d2SMark Murray 		if (buffer_len(&stdout_buffer) < buffer_high &&
468511b41d2SMark Murray 		    buffer_len(&stderr_buffer) < buffer_high &&
469511b41d2SMark Murray 		    channel_not_very_much_buffered_data())
4701e8db6e2SBrian Feldman 			FD_SET(connection_in, *readsetp);
471511b41d2SMark Murray 		/*
472511b41d2SMark Murray 		 * Read from stdin, unless we have seen EOF or have very much
473511b41d2SMark Murray 		 * buffered data to send to the server.
474511b41d2SMark Murray 		 */
475511b41d2SMark Murray 		if (!stdin_eof && packet_not_very_much_data_to_write())
4761e8db6e2SBrian Feldman 			FD_SET(fileno(stdin), *readsetp);
477511b41d2SMark Murray 
478a04a10f8SKris Kennaway 		/* Select stdout/stderr if have data in buffer. */
479a04a10f8SKris Kennaway 		if (buffer_len(&stdout_buffer) > 0)
4801e8db6e2SBrian Feldman 			FD_SET(fileno(stdout), *writesetp);
481a04a10f8SKris Kennaway 		if (buffer_len(&stderr_buffer) > 0)
4821e8db6e2SBrian Feldman 			FD_SET(fileno(stderr), *writesetp);
483a04a10f8SKris Kennaway 	} else {
484ae1f160dSDag-Erling Smørgrav 		/* channel_prepare_select could have closed the last channel */
485ae1f160dSDag-Erling Smørgrav 		if (session_closed && !channel_still_open() &&
486ae1f160dSDag-Erling Smørgrav 		    !packet_have_data_to_write()) {
487ae1f160dSDag-Erling Smørgrav 			/* clear mask since we did not call select() */
488ae1f160dSDag-Erling Smørgrav 			memset(*readsetp, 0, *nallocp);
489ae1f160dSDag-Erling Smørgrav 			memset(*writesetp, 0, *nallocp);
490ae1f160dSDag-Erling Smørgrav 			return;
491ae1f160dSDag-Erling Smørgrav 		} else {
4921e8db6e2SBrian Feldman 			FD_SET(connection_in, *readsetp);
493a04a10f8SKris Kennaway 		}
494ae1f160dSDag-Erling Smørgrav 	}
495511b41d2SMark Murray 
496511b41d2SMark Murray 	/* Select server connection if have data to write to the server. */
497511b41d2SMark Murray 	if (packet_have_data_to_write())
4981e8db6e2SBrian Feldman 		FD_SET(connection_out, *writesetp);
499511b41d2SMark Murray 
500d74d50a8SDag-Erling Smørgrav 	if (control_fd != -1)
501d74d50a8SDag-Erling Smørgrav 		FD_SET(control_fd, *readsetp);
502d74d50a8SDag-Erling Smørgrav 
503511b41d2SMark Murray 	/*
504511b41d2SMark Murray 	 * Wait for something to happen.  This will suspend the process until
505511b41d2SMark Murray 	 * some selected descriptor can be read, written, or has some other
506efcad6b7SDag-Erling Smørgrav 	 * event pending.
507511b41d2SMark Murray 	 */
508511b41d2SMark Murray 
509efcad6b7SDag-Erling Smørgrav 	if (options.server_alive_interval == 0 || !compat20)
510efcad6b7SDag-Erling Smørgrav 		tvp = NULL;
511efcad6b7SDag-Erling Smørgrav 	else {
512efcad6b7SDag-Erling Smørgrav 		tv.tv_sec = options.server_alive_interval;
513efcad6b7SDag-Erling Smørgrav 		tv.tv_usec = 0;
514efcad6b7SDag-Erling Smørgrav 		tvp = &tv;
515efcad6b7SDag-Erling Smørgrav 	}
516efcad6b7SDag-Erling Smørgrav 	ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
517efcad6b7SDag-Erling Smørgrav 	if (ret < 0) {
518511b41d2SMark Murray 		char buf[100];
5191e8db6e2SBrian Feldman 
5201e8db6e2SBrian Feldman 		/*
5211e8db6e2SBrian Feldman 		 * We have to clear the select masks, because we return.
5221e8db6e2SBrian Feldman 		 * We have to return, because the mainloop checks for the flags
5231e8db6e2SBrian Feldman 		 * set by the signal handlers.
5241e8db6e2SBrian Feldman 		 */
525ae1f160dSDag-Erling Smørgrav 		memset(*readsetp, 0, *nallocp);
526ae1f160dSDag-Erling Smørgrav 		memset(*writesetp, 0, *nallocp);
5271e8db6e2SBrian Feldman 
528511b41d2SMark Murray 		if (errno == EINTR)
529511b41d2SMark Murray 			return;
530511b41d2SMark Murray 		/* Note: we might still have data in the buffers. */
531511b41d2SMark Murray 		snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
532511b41d2SMark Murray 		buffer_append(&stderr_buffer, buf, strlen(buf));
533511b41d2SMark Murray 		quit_pending = 1;
534efcad6b7SDag-Erling Smørgrav 	} else if (ret == 0)
535efcad6b7SDag-Erling Smørgrav 		server_alive_check();
536511b41d2SMark Murray }
537511b41d2SMark Murray 
538ae1f160dSDag-Erling Smørgrav static void
539b66f2d16SKris Kennaway client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr)
540511b41d2SMark Murray {
541511b41d2SMark Murray 	/* Flush stdout and stderr buffers. */
542b66f2d16SKris Kennaway 	if (buffer_len(bout) > 0)
543d95e11bfSDag-Erling Smørgrav 		atomicio(vwrite, fileno(stdout), buffer_ptr(bout), buffer_len(bout));
544b66f2d16SKris Kennaway 	if (buffer_len(berr) > 0)
545d95e11bfSDag-Erling Smørgrav 		atomicio(vwrite, fileno(stderr), buffer_ptr(berr), buffer_len(berr));
546511b41d2SMark Murray 
547511b41d2SMark Murray 	leave_raw_mode();
548511b41d2SMark Murray 
549511b41d2SMark Murray 	/*
550511b41d2SMark Murray 	 * Free (and clear) the buffer to reduce the amount of data that gets
551511b41d2SMark Murray 	 * written to swap.
552511b41d2SMark Murray 	 */
553b66f2d16SKris Kennaway 	buffer_free(bin);
554b66f2d16SKris Kennaway 	buffer_free(bout);
555b66f2d16SKris Kennaway 	buffer_free(berr);
556511b41d2SMark Murray 
557511b41d2SMark Murray 	/* Send the suspend signal to the program itself. */
558511b41d2SMark Murray 	kill(getpid(), SIGTSTP);
559511b41d2SMark Murray 
5605e8dbd04SDag-Erling Smørgrav 	/* Reset window sizes in case they have changed */
561511b41d2SMark Murray 	received_window_change_signal = 1;
562511b41d2SMark Murray 
563511b41d2SMark Murray 	/* OK, we have been continued by the user. Reinitialize buffers. */
564b66f2d16SKris Kennaway 	buffer_init(bin);
565b66f2d16SKris Kennaway 	buffer_init(bout);
566b66f2d16SKris Kennaway 	buffer_init(berr);
567511b41d2SMark Murray 
568511b41d2SMark Murray 	enter_raw_mode();
569511b41d2SMark Murray }
570511b41d2SMark Murray 
571ae1f160dSDag-Erling Smørgrav static void
572a04a10f8SKris Kennaway client_process_net_input(fd_set * readset)
573511b41d2SMark Murray {
574a04a10f8SKris Kennaway 	int len;
575a04a10f8SKris Kennaway 	char buf[8192];
576511b41d2SMark Murray 
577511b41d2SMark Murray 	/*
578511b41d2SMark Murray 	 * Read input from the server, and add any such data to the buffer of
579511b41d2SMark Murray 	 * the packet subsystem.
580511b41d2SMark Murray 	 */
581511b41d2SMark Murray 	if (FD_ISSET(connection_in, readset)) {
582511b41d2SMark Murray 		/* Read as much as possible. */
583511b41d2SMark Murray 		len = read(connection_in, buf, sizeof(buf));
584511b41d2SMark Murray 		if (len == 0) {
585511b41d2SMark Murray 			/* Received EOF.  The remote host has closed the connection. */
586511b41d2SMark Murray 			snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n",
587511b41d2SMark Murray 				 host);
588511b41d2SMark Murray 			buffer_append(&stderr_buffer, buf, strlen(buf));
589511b41d2SMark Murray 			quit_pending = 1;
590511b41d2SMark Murray 			return;
591511b41d2SMark Murray 		}
592511b41d2SMark Murray 		/*
593511b41d2SMark Murray 		 * There is a kernel bug on Solaris that causes select to
594511b41d2SMark Murray 		 * sometimes wake up even though there is no data available.
595511b41d2SMark Murray 		 */
5961e8db6e2SBrian Feldman 		if (len < 0 && (errno == EAGAIN || errno == EINTR))
597511b41d2SMark Murray 			len = 0;
598511b41d2SMark Murray 
599511b41d2SMark Murray 		if (len < 0) {
600511b41d2SMark Murray 			/* An error has encountered.  Perhaps there is a network problem. */
601511b41d2SMark Murray 			snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n",
602511b41d2SMark Murray 				 host, strerror(errno));
603511b41d2SMark Murray 			buffer_append(&stderr_buffer, buf, strlen(buf));
604511b41d2SMark Murray 			quit_pending = 1;
605511b41d2SMark Murray 			return;
606511b41d2SMark Murray 		}
607511b41d2SMark Murray 		packet_process_incoming(buf, len);
608511b41d2SMark Murray 	}
609a04a10f8SKris Kennaway }
610a04a10f8SKris Kennaway 
611545d5ecaSDag-Erling Smørgrav static void
612d74d50a8SDag-Erling Smørgrav client_subsystem_reply(int type, u_int32_t seq, void *ctxt)
613d74d50a8SDag-Erling Smørgrav {
614d74d50a8SDag-Erling Smørgrav 	int id;
615d74d50a8SDag-Erling Smørgrav 	Channel *c;
616d74d50a8SDag-Erling Smørgrav 
617d74d50a8SDag-Erling Smørgrav 	id = packet_get_int();
618d74d50a8SDag-Erling Smørgrav 	packet_check_eom();
619d74d50a8SDag-Erling Smørgrav 
620d74d50a8SDag-Erling Smørgrav 	if ((c = channel_lookup(id)) == NULL) {
621d74d50a8SDag-Erling Smørgrav 		error("%s: no channel for id %d", __func__, id);
622d74d50a8SDag-Erling Smørgrav 		return;
623d74d50a8SDag-Erling Smørgrav 	}
624d74d50a8SDag-Erling Smørgrav 
625d74d50a8SDag-Erling Smørgrav 	if (type == SSH2_MSG_CHANNEL_SUCCESS)
626d74d50a8SDag-Erling Smørgrav 		debug2("Request suceeded on channel %d", id);
627d74d50a8SDag-Erling Smørgrav 	else if (type == SSH2_MSG_CHANNEL_FAILURE) {
628d74d50a8SDag-Erling Smørgrav 		error("Request failed on channel %d", id);
629d74d50a8SDag-Erling Smørgrav 		channel_free(c);
630d74d50a8SDag-Erling Smørgrav 	}
631d74d50a8SDag-Erling Smørgrav }
632d74d50a8SDag-Erling Smørgrav 
633d74d50a8SDag-Erling Smørgrav static void
634d74d50a8SDag-Erling Smørgrav client_extra_session2_setup(int id, void *arg)
635d74d50a8SDag-Erling Smørgrav {
636d74d50a8SDag-Erling Smørgrav 	struct confirm_ctx *cctx = arg;
637043840dfSDag-Erling Smørgrav 	const char *display;
638d74d50a8SDag-Erling Smørgrav 	Channel *c;
639d74d50a8SDag-Erling Smørgrav 	int i;
640d74d50a8SDag-Erling Smørgrav 
641d74d50a8SDag-Erling Smørgrav 	if (cctx == NULL)
642d74d50a8SDag-Erling Smørgrav 		fatal("%s: cctx == NULL", __func__);
643d74d50a8SDag-Erling Smørgrav 	if ((c = channel_lookup(id)) == NULL)
644d74d50a8SDag-Erling Smørgrav 		fatal("%s: no channel for id %d", __func__, id);
645d74d50a8SDag-Erling Smørgrav 
646043840dfSDag-Erling Smørgrav 	display = getenv("DISPLAY");
647043840dfSDag-Erling Smørgrav 	if (cctx->want_x_fwd && options.forward_x11 && display != NULL) {
648043840dfSDag-Erling Smørgrav 		char *proto, *data;
649043840dfSDag-Erling Smørgrav 		/* Get reasonable local authentication information. */
650043840dfSDag-Erling Smørgrav 		client_x11_get_proto(display, options.xauth_location,
651043840dfSDag-Erling Smørgrav 		    options.forward_x11_trusted, &proto, &data);
652043840dfSDag-Erling Smørgrav 		/* Request forwarding with authentication spoofing. */
653043840dfSDag-Erling Smørgrav 		debug("Requesting X11 forwarding with authentication spoofing.");
654043840dfSDag-Erling Smørgrav 		x11_request_forwarding_with_spoofing(id, display, proto, data);
655043840dfSDag-Erling Smørgrav 		/* XXX wait for reply */
656043840dfSDag-Erling Smørgrav 	}
657043840dfSDag-Erling Smørgrav 
658043840dfSDag-Erling Smørgrav 	if (cctx->want_agent_fwd && options.forward_agent) {
659043840dfSDag-Erling Smørgrav 		debug("Requesting authentication agent forwarding.");
660043840dfSDag-Erling Smørgrav 		channel_request_start(id, "auth-agent-req@openssh.com", 0);
661043840dfSDag-Erling Smørgrav 		packet_send();
662043840dfSDag-Erling Smørgrav 	}
663043840dfSDag-Erling Smørgrav 
664d74d50a8SDag-Erling Smørgrav 	client_session2_setup(id, cctx->want_tty, cctx->want_subsys,
665d74d50a8SDag-Erling Smørgrav 	    cctx->term, &cctx->tio, c->rfd, &cctx->cmd, cctx->env,
666d74d50a8SDag-Erling Smørgrav 	    client_subsystem_reply);
667d74d50a8SDag-Erling Smørgrav 
668d74d50a8SDag-Erling Smørgrav 	c->confirm_ctx = NULL;
669d74d50a8SDag-Erling Smørgrav 	buffer_free(&cctx->cmd);
670d74d50a8SDag-Erling Smørgrav 	xfree(cctx->term);
671d74d50a8SDag-Erling Smørgrav 	if (cctx->env != NULL) {
672d74d50a8SDag-Erling Smørgrav 		for (i = 0; cctx->env[i] != NULL; i++)
673d74d50a8SDag-Erling Smørgrav 			xfree(cctx->env[i]);
674d74d50a8SDag-Erling Smørgrav 		xfree(cctx->env);
675d74d50a8SDag-Erling Smørgrav 	}
676d74d50a8SDag-Erling Smørgrav 	xfree(cctx);
677d74d50a8SDag-Erling Smørgrav }
678d74d50a8SDag-Erling Smørgrav 
679d74d50a8SDag-Erling Smørgrav static void
680d74d50a8SDag-Erling Smørgrav client_process_control(fd_set * readset)
681d74d50a8SDag-Erling Smørgrav {
682d74d50a8SDag-Erling Smørgrav 	Buffer m;
683d74d50a8SDag-Erling Smørgrav 	Channel *c;
684043840dfSDag-Erling Smørgrav 	int client_fd, new_fd[3], ver, allowed;
685d74d50a8SDag-Erling Smørgrav 	socklen_t addrlen;
686d74d50a8SDag-Erling Smørgrav 	struct sockaddr_storage addr;
687d74d50a8SDag-Erling Smørgrav 	struct confirm_ctx *cctx;
688d74d50a8SDag-Erling Smørgrav 	char *cmd;
689043840dfSDag-Erling Smørgrav 	u_int i, len, env_len, command, flags;
690d74d50a8SDag-Erling Smørgrav 	uid_t euid;
691d74d50a8SDag-Erling Smørgrav 	gid_t egid;
692d74d50a8SDag-Erling Smørgrav 
693d74d50a8SDag-Erling Smørgrav 	/*
694d74d50a8SDag-Erling Smørgrav 	 * Accept connection on control socket
695d74d50a8SDag-Erling Smørgrav 	 */
696d74d50a8SDag-Erling Smørgrav 	if (control_fd == -1 || !FD_ISSET(control_fd, readset))
697d74d50a8SDag-Erling Smørgrav 		return;
698d74d50a8SDag-Erling Smørgrav 
699d74d50a8SDag-Erling Smørgrav 	memset(&addr, 0, sizeof(addr));
700d74d50a8SDag-Erling Smørgrav 	addrlen = sizeof(addr);
701d74d50a8SDag-Erling Smørgrav 	if ((client_fd = accept(control_fd,
702d74d50a8SDag-Erling Smørgrav 	    (struct sockaddr*)&addr, &addrlen)) == -1) {
703d74d50a8SDag-Erling Smørgrav 		error("%s accept: %s", __func__, strerror(errno));
704d74d50a8SDag-Erling Smørgrav 		return;
705d74d50a8SDag-Erling Smørgrav 	}
706d74d50a8SDag-Erling Smørgrav 
707d74d50a8SDag-Erling Smørgrav 	if (getpeereid(client_fd, &euid, &egid) < 0) {
708d74d50a8SDag-Erling Smørgrav 		error("%s getpeereid failed: %s", __func__, strerror(errno));
709d74d50a8SDag-Erling Smørgrav 		close(client_fd);
710d74d50a8SDag-Erling Smørgrav 		return;
711d74d50a8SDag-Erling Smørgrav 	}
712d74d50a8SDag-Erling Smørgrav 	if ((euid != 0) && (getuid() != euid)) {
713d74d50a8SDag-Erling Smørgrav 		error("control mode uid mismatch: peer euid %u != uid %u",
714d74d50a8SDag-Erling Smørgrav 		    (u_int) euid, (u_int) getuid());
715d74d50a8SDag-Erling Smørgrav 		close(client_fd);
716d74d50a8SDag-Erling Smørgrav 		return;
717d74d50a8SDag-Erling Smørgrav 	}
718d74d50a8SDag-Erling Smørgrav 
719d74d50a8SDag-Erling Smørgrav 	unset_nonblock(client_fd);
720d74d50a8SDag-Erling Smørgrav 
7215e8dbd04SDag-Erling Smørgrav 	/* Read command */
722d74d50a8SDag-Erling Smørgrav 	buffer_init(&m);
7235e8dbd04SDag-Erling Smørgrav 	if (ssh_msg_recv(client_fd, &m) == -1) {
7245e8dbd04SDag-Erling Smørgrav 		error("%s: client msg_recv failed", __func__);
7255e8dbd04SDag-Erling Smørgrav 		close(client_fd);
7265e8dbd04SDag-Erling Smørgrav 		buffer_free(&m);
7275e8dbd04SDag-Erling Smørgrav 		return;
7285e8dbd04SDag-Erling Smørgrav 	}
729043840dfSDag-Erling Smørgrav 	if ((ver = buffer_get_char(&m)) != SSHMUX_VER) {
7305e8dbd04SDag-Erling Smørgrav 		error("%s: wrong client version %d", __func__, ver);
7315e8dbd04SDag-Erling Smørgrav 		buffer_free(&m);
7325e8dbd04SDag-Erling Smørgrav 		close(client_fd);
7335e8dbd04SDag-Erling Smørgrav 		return;
7345e8dbd04SDag-Erling Smørgrav 	}
735d74d50a8SDag-Erling Smørgrav 
7365e8dbd04SDag-Erling Smørgrav 	allowed = 1;
7375e8dbd04SDag-Erling Smørgrav 	command = buffer_get_int(&m);
7385e8dbd04SDag-Erling Smørgrav 	flags = buffer_get_int(&m);
7395e8dbd04SDag-Erling Smørgrav 
7405e8dbd04SDag-Erling Smørgrav 	buffer_clear(&m);
7415e8dbd04SDag-Erling Smørgrav 
7425e8dbd04SDag-Erling Smørgrav 	switch (command) {
7435e8dbd04SDag-Erling Smørgrav 	case SSHMUX_COMMAND_OPEN:
744043840dfSDag-Erling Smørgrav 		if (options.control_master == SSHCTL_MASTER_ASK ||
745043840dfSDag-Erling Smørgrav 		    options.control_master == SSHCTL_MASTER_AUTO_ASK)
7465e8dbd04SDag-Erling Smørgrav 			allowed = ask_permission("Allow shared connection "
7475e8dbd04SDag-Erling Smørgrav 			    "to %s? ", host);
7485e8dbd04SDag-Erling Smørgrav 		/* continue below */
7495e8dbd04SDag-Erling Smørgrav 		break;
7505e8dbd04SDag-Erling Smørgrav 	case SSHMUX_COMMAND_TERMINATE:
751043840dfSDag-Erling Smørgrav 		if (options.control_master == SSHCTL_MASTER_ASK ||
752043840dfSDag-Erling Smørgrav 		    options.control_master == SSHCTL_MASTER_AUTO_ASK)
7535e8dbd04SDag-Erling Smørgrav 			allowed = ask_permission("Terminate shared connection "
7545e8dbd04SDag-Erling Smørgrav 			    "to %s? ", host);
7555e8dbd04SDag-Erling Smørgrav 		if (allowed)
7565e8dbd04SDag-Erling Smørgrav 			quit_pending = 1;
7575e8dbd04SDag-Erling Smørgrav 		/* FALLTHROUGH */
7585e8dbd04SDag-Erling Smørgrav 	case SSHMUX_COMMAND_ALIVE_CHECK:
7595e8dbd04SDag-Erling Smørgrav 		/* Reply for SSHMUX_COMMAND_TERMINATE and ALIVE_CHECK */
7605e8dbd04SDag-Erling Smørgrav 		buffer_clear(&m);
761d74d50a8SDag-Erling Smørgrav 		buffer_put_int(&m, allowed);
762d74d50a8SDag-Erling Smørgrav 		buffer_put_int(&m, getpid());
763043840dfSDag-Erling Smørgrav 		if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
764d74d50a8SDag-Erling Smørgrav 			error("%s: client msg_send failed", __func__);
765d74d50a8SDag-Erling Smørgrav 			close(client_fd);
766d74d50a8SDag-Erling Smørgrav 			buffer_free(&m);
767d74d50a8SDag-Erling Smørgrav 			return;
768d74d50a8SDag-Erling Smørgrav 		}
7695e8dbd04SDag-Erling Smørgrav 		buffer_free(&m);
7705e8dbd04SDag-Erling Smørgrav 		close(client_fd);
7715e8dbd04SDag-Erling Smørgrav 		return;
7725e8dbd04SDag-Erling Smørgrav 	default:
7735e8dbd04SDag-Erling Smørgrav 		error("Unsupported command %d", command);
7745e8dbd04SDag-Erling Smørgrav 		buffer_free(&m);
7755e8dbd04SDag-Erling Smørgrav 		close(client_fd);
7765e8dbd04SDag-Erling Smørgrav 		return;
7775e8dbd04SDag-Erling Smørgrav 	}
7785e8dbd04SDag-Erling Smørgrav 
7795e8dbd04SDag-Erling Smørgrav 	/* Reply for SSHMUX_COMMAND_OPEN */
780d74d50a8SDag-Erling Smørgrav 	buffer_clear(&m);
7815e8dbd04SDag-Erling Smørgrav 	buffer_put_int(&m, allowed);
7825e8dbd04SDag-Erling Smørgrav 	buffer_put_int(&m, getpid());
783043840dfSDag-Erling Smørgrav 	if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
7845e8dbd04SDag-Erling Smørgrav 		error("%s: client msg_send failed", __func__);
7855e8dbd04SDag-Erling Smørgrav 		close(client_fd);
7865e8dbd04SDag-Erling Smørgrav 		buffer_free(&m);
7875e8dbd04SDag-Erling Smørgrav 		return;
7885e8dbd04SDag-Erling Smørgrav 	}
789d74d50a8SDag-Erling Smørgrav 
790d74d50a8SDag-Erling Smørgrav 	if (!allowed) {
791d74d50a8SDag-Erling Smørgrav 		error("Refused control connection");
792d74d50a8SDag-Erling Smørgrav 		close(client_fd);
793d74d50a8SDag-Erling Smørgrav 		buffer_free(&m);
794d74d50a8SDag-Erling Smørgrav 		return;
795d74d50a8SDag-Erling Smørgrav 	}
796d74d50a8SDag-Erling Smørgrav 
7975e8dbd04SDag-Erling Smørgrav 	buffer_clear(&m);
798d74d50a8SDag-Erling Smørgrav 	if (ssh_msg_recv(client_fd, &m) == -1) {
799d74d50a8SDag-Erling Smørgrav 		error("%s: client msg_recv failed", __func__);
800d74d50a8SDag-Erling Smørgrav 		close(client_fd);
801d74d50a8SDag-Erling Smørgrav 		buffer_free(&m);
802d74d50a8SDag-Erling Smørgrav 		return;
803d74d50a8SDag-Erling Smørgrav 	}
804043840dfSDag-Erling Smørgrav 	if ((ver = buffer_get_char(&m)) != SSHMUX_VER) {
805d74d50a8SDag-Erling Smørgrav 		error("%s: wrong client version %d", __func__, ver);
806d74d50a8SDag-Erling Smørgrav 		buffer_free(&m);
807d74d50a8SDag-Erling Smørgrav 		close(client_fd);
808d74d50a8SDag-Erling Smørgrav 		return;
809d74d50a8SDag-Erling Smørgrav 	}
810d74d50a8SDag-Erling Smørgrav 
811d74d50a8SDag-Erling Smørgrav 	cctx = xmalloc(sizeof(*cctx));
812d74d50a8SDag-Erling Smørgrav 	memset(cctx, 0, sizeof(*cctx));
8135e8dbd04SDag-Erling Smørgrav 	cctx->want_tty = (flags & SSHMUX_FLAG_TTY) != 0;
8145e8dbd04SDag-Erling Smørgrav 	cctx->want_subsys = (flags & SSHMUX_FLAG_SUBSYS) != 0;
815043840dfSDag-Erling Smørgrav 	cctx->want_x_fwd = (flags & SSHMUX_FLAG_X11_FWD) != 0;
816043840dfSDag-Erling Smørgrav 	cctx->want_agent_fwd = (flags & SSHMUX_FLAG_AGENT_FWD) != 0;
817d74d50a8SDag-Erling Smørgrav 	cctx->term = buffer_get_string(&m, &len);
818d74d50a8SDag-Erling Smørgrav 
819d74d50a8SDag-Erling Smørgrav 	cmd = buffer_get_string(&m, &len);
820d74d50a8SDag-Erling Smørgrav 	buffer_init(&cctx->cmd);
821d74d50a8SDag-Erling Smørgrav 	buffer_append(&cctx->cmd, cmd, strlen(cmd));
822d74d50a8SDag-Erling Smørgrav 
823d74d50a8SDag-Erling Smørgrav 	env_len = buffer_get_int(&m);
824d74d50a8SDag-Erling Smørgrav 	env_len = MIN(env_len, 4096);
825d74d50a8SDag-Erling Smørgrav 	debug3("%s: receiving %d env vars", __func__, env_len);
826d74d50a8SDag-Erling Smørgrav 	if (env_len != 0) {
827d74d50a8SDag-Erling Smørgrav 		cctx->env = xmalloc(sizeof(*cctx->env) * (env_len + 1));
828d74d50a8SDag-Erling Smørgrav 		for (i = 0; i < env_len; i++)
829d74d50a8SDag-Erling Smørgrav 			cctx->env[i] = buffer_get_string(&m, &len);
830d74d50a8SDag-Erling Smørgrav 		cctx->env[i] = NULL;
831d74d50a8SDag-Erling Smørgrav 	}
832d74d50a8SDag-Erling Smørgrav 
833d74d50a8SDag-Erling Smørgrav 	debug2("%s: accepted tty %d, subsys %d, cmd %s", __func__,
834d74d50a8SDag-Erling Smørgrav 	    cctx->want_tty, cctx->want_subsys, cmd);
835d74d50a8SDag-Erling Smørgrav 
836d74d50a8SDag-Erling Smørgrav 	/* Gather fds from client */
837d74d50a8SDag-Erling Smørgrav 	new_fd[0] = mm_receive_fd(client_fd);
838d74d50a8SDag-Erling Smørgrav 	new_fd[1] = mm_receive_fd(client_fd);
839d74d50a8SDag-Erling Smørgrav 	new_fd[2] = mm_receive_fd(client_fd);
840d74d50a8SDag-Erling Smørgrav 
841d74d50a8SDag-Erling Smørgrav 	debug2("%s: got fds stdin %d, stdout %d, stderr %d", __func__,
842d74d50a8SDag-Erling Smørgrav 	    new_fd[0], new_fd[1], new_fd[2]);
843d74d50a8SDag-Erling Smørgrav 
844d74d50a8SDag-Erling Smørgrav 	/* Try to pick up ttymodes from client before it goes raw */
845d74d50a8SDag-Erling Smørgrav 	if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1)
846d74d50a8SDag-Erling Smørgrav 		error("%s: tcgetattr: %s", __func__, strerror(errno));
847d74d50a8SDag-Erling Smørgrav 
8485e8dbd04SDag-Erling Smørgrav 	/* This roundtrip is just for synchronisation of ttymodes */
849d74d50a8SDag-Erling Smørgrav 	buffer_clear(&m);
850043840dfSDag-Erling Smørgrav 	if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
851d74d50a8SDag-Erling Smørgrav 		error("%s: client msg_send failed", __func__);
852d74d50a8SDag-Erling Smørgrav 		close(client_fd);
853d74d50a8SDag-Erling Smørgrav 		close(new_fd[0]);
854d74d50a8SDag-Erling Smørgrav 		close(new_fd[1]);
855d74d50a8SDag-Erling Smørgrav 		close(new_fd[2]);
856d74d50a8SDag-Erling Smørgrav 		buffer_free(&m);
8575e8dbd04SDag-Erling Smørgrav 		xfree(cctx->term);
8585e8dbd04SDag-Erling Smørgrav 		if (env_len != 0) {
8595e8dbd04SDag-Erling Smørgrav 			for (i = 0; i < env_len; i++)
8605e8dbd04SDag-Erling Smørgrav 				xfree(cctx->env[i]);
8615e8dbd04SDag-Erling Smørgrav 			xfree(cctx->env);
8625e8dbd04SDag-Erling Smørgrav 		}
863d74d50a8SDag-Erling Smørgrav 		return;
864d74d50a8SDag-Erling Smørgrav 	}
865d74d50a8SDag-Erling Smørgrav 	buffer_free(&m);
866d74d50a8SDag-Erling Smørgrav 
867d74d50a8SDag-Erling Smørgrav 	/* enable nonblocking unless tty */
868d74d50a8SDag-Erling Smørgrav 	if (!isatty(new_fd[0]))
869d74d50a8SDag-Erling Smørgrav 		set_nonblock(new_fd[0]);
870d74d50a8SDag-Erling Smørgrav 	if (!isatty(new_fd[1]))
871d74d50a8SDag-Erling Smørgrav 		set_nonblock(new_fd[1]);
872d74d50a8SDag-Erling Smørgrav 	if (!isatty(new_fd[2]))
873d74d50a8SDag-Erling Smørgrav 		set_nonblock(new_fd[2]);
874d74d50a8SDag-Erling Smørgrav 
875d74d50a8SDag-Erling Smørgrav 	set_nonblock(client_fd);
876d74d50a8SDag-Erling Smørgrav 
877d74d50a8SDag-Erling Smørgrav 	c = channel_new("session", SSH_CHANNEL_OPENING,
878d74d50a8SDag-Erling Smørgrav 	    new_fd[0], new_fd[1], new_fd[2],
879d74d50a8SDag-Erling Smørgrav 	    CHAN_SES_WINDOW_DEFAULT, CHAN_SES_PACKET_DEFAULT,
880d74d50a8SDag-Erling Smørgrav 	    CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0);
881d74d50a8SDag-Erling Smørgrav 
882d74d50a8SDag-Erling Smørgrav 	/* XXX */
883d74d50a8SDag-Erling Smørgrav 	c->ctl_fd = client_fd;
884d74d50a8SDag-Erling Smørgrav 
885d74d50a8SDag-Erling Smørgrav 	debug3("%s: channel_new: %d", __func__, c->self);
886d74d50a8SDag-Erling Smørgrav 
887d74d50a8SDag-Erling Smørgrav 	channel_send_open(c->self);
888d74d50a8SDag-Erling Smørgrav 	channel_register_confirm(c->self, client_extra_session2_setup, cctx);
889d74d50a8SDag-Erling Smørgrav }
890d74d50a8SDag-Erling Smørgrav 
891d74d50a8SDag-Erling Smørgrav static void
892545d5ecaSDag-Erling Smørgrav process_cmdline(void)
893545d5ecaSDag-Erling Smørgrav {
894545d5ecaSDag-Erling Smørgrav 	void (*handler)(int);
8955e8dbd04SDag-Erling Smørgrav 	char *s, *cmd, *cancel_host;
896d74d50a8SDag-Erling Smørgrav 	int delete = 0;
897545d5ecaSDag-Erling Smørgrav 	int local = 0;
8985e8dbd04SDag-Erling Smørgrav 	u_short cancel_port;
8995e8dbd04SDag-Erling Smørgrav 	Forward fwd;
900545d5ecaSDag-Erling Smørgrav 
901545d5ecaSDag-Erling Smørgrav 	leave_raw_mode();
902545d5ecaSDag-Erling Smørgrav 	handler = signal(SIGINT, SIG_IGN);
903545d5ecaSDag-Erling Smørgrav 	cmd = s = read_passphrase("\r\nssh> ", RP_ECHO);
904545d5ecaSDag-Erling Smørgrav 	if (s == NULL)
905545d5ecaSDag-Erling Smørgrav 		goto out;
906545d5ecaSDag-Erling Smørgrav 	while (*s && isspace(*s))
907545d5ecaSDag-Erling Smørgrav 		s++;
908d74d50a8SDag-Erling Smørgrav 	if (*s == '-')
909d74d50a8SDag-Erling Smørgrav 		s++;	/* Skip cmdline '-', if any */
910d74d50a8SDag-Erling Smørgrav 	if (*s == '\0')
911545d5ecaSDag-Erling Smørgrav 		goto out;
912d74d50a8SDag-Erling Smørgrav 
913d74d50a8SDag-Erling Smørgrav 	if (*s == 'h' || *s == 'H' || *s == '?') {
914d74d50a8SDag-Erling Smørgrav 		logit("Commands:");
915d74d50a8SDag-Erling Smørgrav 		logit("      -Lport:host:hostport    Request local forward");
916d74d50a8SDag-Erling Smørgrav 		logit("      -Rport:host:hostport    Request remote forward");
917d74d50a8SDag-Erling Smørgrav 		logit("      -KRhostport             Cancel remote forward");
918021d409fSDag-Erling Smørgrav 		if (!options.permit_local_command)
919021d409fSDag-Erling Smørgrav 			goto out;
920021d409fSDag-Erling Smørgrav 		logit("      !args                   Execute local command");
921021d409fSDag-Erling Smørgrav 		goto out;
922021d409fSDag-Erling Smørgrav 	}
923021d409fSDag-Erling Smørgrav 
924021d409fSDag-Erling Smørgrav 	if (*s == '!' && options.permit_local_command) {
925021d409fSDag-Erling Smørgrav 		s++;
926021d409fSDag-Erling Smørgrav 		ssh_local_cmd(s);
927d74d50a8SDag-Erling Smørgrav 		goto out;
928d74d50a8SDag-Erling Smørgrav 	}
929d74d50a8SDag-Erling Smørgrav 
930d74d50a8SDag-Erling Smørgrav 	if (*s == 'K') {
931d74d50a8SDag-Erling Smørgrav 		delete = 1;
932d74d50a8SDag-Erling Smørgrav 		s++;
933d74d50a8SDag-Erling Smørgrav 	}
934d74d50a8SDag-Erling Smørgrav 	if (*s != 'L' && *s != 'R') {
935d95e11bfSDag-Erling Smørgrav 		logit("Invalid command.");
936545d5ecaSDag-Erling Smørgrav 		goto out;
937545d5ecaSDag-Erling Smørgrav 	}
938d74d50a8SDag-Erling Smørgrav 	if (*s == 'L')
939545d5ecaSDag-Erling Smørgrav 		local = 1;
940d74d50a8SDag-Erling Smørgrav 	if (local && delete) {
941d74d50a8SDag-Erling Smørgrav 		logit("Not supported.");
942d74d50a8SDag-Erling Smørgrav 		goto out;
943d74d50a8SDag-Erling Smørgrav 	}
944d74d50a8SDag-Erling Smørgrav 	if ((!local || delete) && !compat20) {
945d95e11bfSDag-Erling Smørgrav 		logit("Not supported for SSH protocol version 1.");
946545d5ecaSDag-Erling Smørgrav 		goto out;
947545d5ecaSDag-Erling Smørgrav 	}
948d74d50a8SDag-Erling Smørgrav 
949d74d50a8SDag-Erling Smørgrav 	s++;
950545d5ecaSDag-Erling Smørgrav 	while (*s && isspace(*s))
951545d5ecaSDag-Erling Smørgrav 		s++;
952545d5ecaSDag-Erling Smørgrav 
953d74d50a8SDag-Erling Smørgrav 	if (delete) {
9545e8dbd04SDag-Erling Smørgrav 		cancel_port = 0;
9555e8dbd04SDag-Erling Smørgrav 		cancel_host = hpdelim(&s);	/* may be NULL */
9565e8dbd04SDag-Erling Smørgrav 		if (s != NULL) {
9575e8dbd04SDag-Erling Smørgrav 			cancel_port = a2port(s);
9585e8dbd04SDag-Erling Smørgrav 			cancel_host = cleanhostname(cancel_host);
959d74d50a8SDag-Erling Smørgrav 		} else {
9605e8dbd04SDag-Erling Smørgrav 			cancel_port = a2port(cancel_host);
9615e8dbd04SDag-Erling Smørgrav 			cancel_host = NULL;
9625e8dbd04SDag-Erling Smørgrav 		}
9635e8dbd04SDag-Erling Smørgrav 		if (cancel_port == 0) {
9645e8dbd04SDag-Erling Smørgrav 			logit("Bad forwarding close port");
965545d5ecaSDag-Erling Smørgrav 			goto out;
966545d5ecaSDag-Erling Smørgrav 		}
9675e8dbd04SDag-Erling Smørgrav 		channel_request_rforward_cancel(cancel_host, cancel_port);
9685e8dbd04SDag-Erling Smørgrav 	} else {
9695e8dbd04SDag-Erling Smørgrav 		if (!parse_forward(&fwd, s)) {
9705e8dbd04SDag-Erling Smørgrav 			logit("Bad forwarding specification.");
971545d5ecaSDag-Erling Smørgrav 			goto out;
972545d5ecaSDag-Erling Smørgrav 		}
973545d5ecaSDag-Erling Smørgrav 		if (local) {
9745e8dbd04SDag-Erling Smørgrav 			if (channel_setup_local_fwd_listener(fwd.listen_host,
9755e8dbd04SDag-Erling Smørgrav 			    fwd.listen_port, fwd.connect_host,
9765e8dbd04SDag-Erling Smørgrav 			    fwd.connect_port, options.gateway_ports) < 0) {
977d95e11bfSDag-Erling Smørgrav 				logit("Port forwarding failed.");
978545d5ecaSDag-Erling Smørgrav 				goto out;
979545d5ecaSDag-Erling Smørgrav 			}
9805e8dbd04SDag-Erling Smørgrav 		} else {
9815e8dbd04SDag-Erling Smørgrav 			channel_request_remote_forwarding(fwd.listen_host,
9825e8dbd04SDag-Erling Smørgrav 			    fwd.listen_port, fwd.connect_host,
9835e8dbd04SDag-Erling Smørgrav 			    fwd.connect_port);
9845e8dbd04SDag-Erling Smørgrav 		}
9855e8dbd04SDag-Erling Smørgrav 
986d95e11bfSDag-Erling Smørgrav 		logit("Forwarding port.");
987d74d50a8SDag-Erling Smørgrav 	}
988d74d50a8SDag-Erling Smørgrav 
989545d5ecaSDag-Erling Smørgrav out:
990545d5ecaSDag-Erling Smørgrav 	signal(SIGINT, handler);
991545d5ecaSDag-Erling Smørgrav 	enter_raw_mode();
992545d5ecaSDag-Erling Smørgrav 	if (cmd)
993545d5ecaSDag-Erling Smørgrav 		xfree(cmd);
994545d5ecaSDag-Erling Smørgrav }
995545d5ecaSDag-Erling Smørgrav 
996b66f2d16SKris Kennaway /* process the characters one by one */
997ae1f160dSDag-Erling Smørgrav static int
998b66f2d16SKris Kennaway process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len)
999b66f2d16SKris Kennaway {
1000b66f2d16SKris Kennaway 	char string[1024];
1001b66f2d16SKris Kennaway 	pid_t pid;
1002b66f2d16SKris Kennaway 	int bytes = 0;
10031e8db6e2SBrian Feldman 	u_int i;
10041e8db6e2SBrian Feldman 	u_char ch;
1005b66f2d16SKris Kennaway 	char *s;
1006b66f2d16SKris Kennaway 
1007043840dfSDag-Erling Smørgrav 	if (len <= 0)
1008043840dfSDag-Erling Smørgrav 		return (0);
1009043840dfSDag-Erling Smørgrav 
1010043840dfSDag-Erling Smørgrav 	for (i = 0; i < (u_int)len; i++) {
1011b66f2d16SKris Kennaway 		/* Get one character at a time. */
1012b66f2d16SKris Kennaway 		ch = buf[i];
1013b66f2d16SKris Kennaway 
1014b66f2d16SKris Kennaway 		if (escape_pending) {
1015b66f2d16SKris Kennaway 			/* We have previously seen an escape character. */
1016b66f2d16SKris Kennaway 			/* Clear the flag now. */
1017b66f2d16SKris Kennaway 			escape_pending = 0;
1018b66f2d16SKris Kennaway 
1019b66f2d16SKris Kennaway 			/* Process the escaped character. */
1020b66f2d16SKris Kennaway 			switch (ch) {
1021b66f2d16SKris Kennaway 			case '.':
1022b66f2d16SKris Kennaway 				/* Terminate the connection. */
1023b66f2d16SKris Kennaway 				snprintf(string, sizeof string, "%c.\r\n", escape_char);
1024b66f2d16SKris Kennaway 				buffer_append(berr, string, strlen(string));
1025b66f2d16SKris Kennaway 
1026b66f2d16SKris Kennaway 				quit_pending = 1;
1027b66f2d16SKris Kennaway 				return -1;
1028b66f2d16SKris Kennaway 
1029b66f2d16SKris Kennaway 			case 'Z' - 64:
1030b66f2d16SKris Kennaway 				/* Suspend the program. */
1031b66f2d16SKris Kennaway 				/* Print a message to that effect to the user. */
1032b66f2d16SKris Kennaway 				snprintf(string, sizeof string, "%c^Z [suspend ssh]\r\n", escape_char);
1033b66f2d16SKris Kennaway 				buffer_append(berr, string, strlen(string));
1034b66f2d16SKris Kennaway 
1035b66f2d16SKris Kennaway 				/* Restore terminal modes and suspend. */
1036b66f2d16SKris Kennaway 				client_suspend_self(bin, bout, berr);
1037b66f2d16SKris Kennaway 
1038b66f2d16SKris Kennaway 				/* We have been continued. */
1039b66f2d16SKris Kennaway 				continue;
1040b66f2d16SKris Kennaway 
1041d95e11bfSDag-Erling Smørgrav 			case 'B':
1042d95e11bfSDag-Erling Smørgrav 				if (compat20) {
1043d95e11bfSDag-Erling Smørgrav 					snprintf(string, sizeof string,
1044d95e11bfSDag-Erling Smørgrav 					    "%cB\r\n", escape_char);
1045d95e11bfSDag-Erling Smørgrav 					buffer_append(berr, string,
1046d95e11bfSDag-Erling Smørgrav 					    strlen(string));
1047d95e11bfSDag-Erling Smørgrav 					channel_request_start(session_ident,
1048d95e11bfSDag-Erling Smørgrav 					    "break", 0);
1049d95e11bfSDag-Erling Smørgrav 					packet_put_int(1000);
1050d95e11bfSDag-Erling Smørgrav 					packet_send();
1051d95e11bfSDag-Erling Smørgrav 				}
1052d95e11bfSDag-Erling Smørgrav 				continue;
1053d95e11bfSDag-Erling Smørgrav 
10541e8db6e2SBrian Feldman 			case 'R':
10551e8db6e2SBrian Feldman 				if (compat20) {
10561e8db6e2SBrian Feldman 					if (datafellows & SSH_BUG_NOREKEY)
1057d95e11bfSDag-Erling Smørgrav 						logit("Server does not support re-keying");
10581e8db6e2SBrian Feldman 					else
10591e8db6e2SBrian Feldman 						need_rekeying = 1;
10601e8db6e2SBrian Feldman 				}
10611e8db6e2SBrian Feldman 				continue;
10621e8db6e2SBrian Feldman 
1063b66f2d16SKris Kennaway 			case '&':
1064b66f2d16SKris Kennaway 				/*
1065b66f2d16SKris Kennaway 				 * Detach the program (continue to serve connections,
1066b66f2d16SKris Kennaway 				 * but put in background and no more new connections).
1067b66f2d16SKris Kennaway 				 */
1068ae1f160dSDag-Erling Smørgrav 				/* Restore tty modes. */
1069ae1f160dSDag-Erling Smørgrav 				leave_raw_mode();
1070ae1f160dSDag-Erling Smørgrav 
1071ae1f160dSDag-Erling Smørgrav 				/* Stop listening for new connections. */
1072ae1f160dSDag-Erling Smørgrav 				channel_stop_listening();
1073ae1f160dSDag-Erling Smørgrav 
1074ae1f160dSDag-Erling Smørgrav 				snprintf(string, sizeof string,
1075ae1f160dSDag-Erling Smørgrav 				    "%c& [backgrounded]\n", escape_char);
1076ae1f160dSDag-Erling Smørgrav 				buffer_append(berr, string, strlen(string));
1077ae1f160dSDag-Erling Smørgrav 
1078ae1f160dSDag-Erling Smørgrav 				/* Fork into background. */
1079ae1f160dSDag-Erling Smørgrav 				pid = fork();
1080ae1f160dSDag-Erling Smørgrav 				if (pid < 0) {
1081ae1f160dSDag-Erling Smørgrav 					error("fork: %.100s", strerror(errno));
1082ae1f160dSDag-Erling Smørgrav 					continue;
1083ae1f160dSDag-Erling Smørgrav 				}
1084ae1f160dSDag-Erling Smørgrav 				if (pid != 0) {	/* This is the parent. */
1085ae1f160dSDag-Erling Smørgrav 					/* The parent just exits. */
1086ae1f160dSDag-Erling Smørgrav 					exit(0);
1087ae1f160dSDag-Erling Smørgrav 				}
1088ae1f160dSDag-Erling Smørgrav 				/* The child continues serving connections. */
1089ae1f160dSDag-Erling Smørgrav 				if (compat20) {
1090ae1f160dSDag-Erling Smørgrav 					buffer_append(bin, "\004", 1);
1091ae1f160dSDag-Erling Smørgrav 					/* fake EOF on stdin */
1092ae1f160dSDag-Erling Smørgrav 					return -1;
1093ae1f160dSDag-Erling Smørgrav 				} else if (!stdin_eof) {
1094b66f2d16SKris Kennaway 					/*
1095b66f2d16SKris Kennaway 					 * Sending SSH_CMSG_EOF alone does not always appear
1096b66f2d16SKris Kennaway 					 * to be enough.  So we try to send an EOF character
1097b66f2d16SKris Kennaway 					 * first.
1098b66f2d16SKris Kennaway 					 */
1099b66f2d16SKris Kennaway 					packet_start(SSH_CMSG_STDIN_DATA);
1100b66f2d16SKris Kennaway 					packet_put_string("\004", 1);
1101b66f2d16SKris Kennaway 					packet_send();
1102b66f2d16SKris Kennaway 					/* Close stdin. */
1103b66f2d16SKris Kennaway 					stdin_eof = 1;
1104b66f2d16SKris Kennaway 					if (buffer_len(bin) == 0) {
1105b66f2d16SKris Kennaway 						packet_start(SSH_CMSG_EOF);
1106b66f2d16SKris Kennaway 						packet_send();
1107b66f2d16SKris Kennaway 					}
1108b66f2d16SKris Kennaway 				}
1109b66f2d16SKris Kennaway 				continue;
1110b66f2d16SKris Kennaway 
1111b66f2d16SKris Kennaway 			case '?':
1112b66f2d16SKris Kennaway 				snprintf(string, sizeof string,
1113b66f2d16SKris Kennaway "%c?\r\n\
1114b66f2d16SKris Kennaway Supported escape sequences:\r\n\
11154b17dab0SDag-Erling Smørgrav %c.  - terminate connection\r\n\
1116d95e11bfSDag-Erling Smørgrav %cB  - send a BREAK to the remote system\r\n\
11174b17dab0SDag-Erling Smørgrav %cC  - open a command line\r\n\
11184b17dab0SDag-Erling Smørgrav %cR  - Request rekey (SSH protocol 2 only)\r\n\
11194b17dab0SDag-Erling Smørgrav %c^Z - suspend ssh\r\n\
11204b17dab0SDag-Erling Smørgrav %c#  - list forwarded connections\r\n\
11214b17dab0SDag-Erling Smørgrav %c&  - background ssh (when waiting for connections to terminate)\r\n\
11224b17dab0SDag-Erling Smørgrav %c?  - this message\r\n\
11234b17dab0SDag-Erling Smørgrav %c%c  - send the escape character by typing it twice\r\n\
1124b66f2d16SKris Kennaway (Note that escapes are only recognized immediately after newline.)\r\n",
11254b17dab0SDag-Erling Smørgrav 				    escape_char, escape_char, escape_char, escape_char,
11264b17dab0SDag-Erling Smørgrav 				    escape_char, escape_char, escape_char, escape_char,
1127d95e11bfSDag-Erling Smørgrav 				    escape_char, escape_char, escape_char);
1128b66f2d16SKris Kennaway 				buffer_append(berr, string, strlen(string));
1129b66f2d16SKris Kennaway 				continue;
1130b66f2d16SKris Kennaway 
1131b66f2d16SKris Kennaway 			case '#':
1132b66f2d16SKris Kennaway 				snprintf(string, sizeof string, "%c#\r\n", escape_char);
1133b66f2d16SKris Kennaway 				buffer_append(berr, string, strlen(string));
1134b66f2d16SKris Kennaway 				s = channel_open_message();
1135b66f2d16SKris Kennaway 				buffer_append(berr, s, strlen(s));
1136b66f2d16SKris Kennaway 				xfree(s);
1137b66f2d16SKris Kennaway 				continue;
1138b66f2d16SKris Kennaway 
1139545d5ecaSDag-Erling Smørgrav 			case 'C':
1140545d5ecaSDag-Erling Smørgrav 				process_cmdline();
1141545d5ecaSDag-Erling Smørgrav 				continue;
1142545d5ecaSDag-Erling Smørgrav 
1143b66f2d16SKris Kennaway 			default:
1144b66f2d16SKris Kennaway 				if (ch != escape_char) {
1145b66f2d16SKris Kennaway 					buffer_put_char(bin, escape_char);
1146b66f2d16SKris Kennaway 					bytes++;
1147b66f2d16SKris Kennaway 				}
1148b66f2d16SKris Kennaway 				/* Escaped characters fall through here */
1149b66f2d16SKris Kennaway 				break;
1150b66f2d16SKris Kennaway 			}
1151b66f2d16SKris Kennaway 		} else {
1152b66f2d16SKris Kennaway 			/*
1153b66f2d16SKris Kennaway 			 * The previous character was not an escape char. Check if this
1154b66f2d16SKris Kennaway 			 * is an escape.
1155b66f2d16SKris Kennaway 			 */
1156b66f2d16SKris Kennaway 			if (last_was_cr && ch == escape_char) {
1157b66f2d16SKris Kennaway 				/* It is. Set the flag and continue to next character. */
1158b66f2d16SKris Kennaway 				escape_pending = 1;
1159b66f2d16SKris Kennaway 				continue;
1160b66f2d16SKris Kennaway 			}
1161b66f2d16SKris Kennaway 		}
1162b66f2d16SKris Kennaway 
1163b66f2d16SKris Kennaway 		/*
1164b66f2d16SKris Kennaway 		 * Normal character.  Record whether it was a newline,
1165b66f2d16SKris Kennaway 		 * and append it to the buffer.
1166b66f2d16SKris Kennaway 		 */
1167b66f2d16SKris Kennaway 		last_was_cr = (ch == '\r' || ch == '\n');
1168b66f2d16SKris Kennaway 		buffer_put_char(bin, ch);
1169b66f2d16SKris Kennaway 		bytes++;
1170b66f2d16SKris Kennaway 	}
1171b66f2d16SKris Kennaway 	return bytes;
1172b66f2d16SKris Kennaway }
1173b66f2d16SKris Kennaway 
1174ae1f160dSDag-Erling Smørgrav static void
1175a04a10f8SKris Kennaway client_process_input(fd_set * readset)
1176a04a10f8SKris Kennaway {
1177a04a10f8SKris Kennaway 	int len;
1178b66f2d16SKris Kennaway 	char buf[8192];
1179a04a10f8SKris Kennaway 
1180511b41d2SMark Murray 	/* Read input from stdin. */
1181511b41d2SMark Murray 	if (FD_ISSET(fileno(stdin), readset)) {
1182511b41d2SMark Murray 		/* Read as much as possible. */
1183511b41d2SMark Murray 		len = read(fileno(stdin), buf, sizeof(buf));
11841e8db6e2SBrian Feldman 		if (len < 0 && (errno == EAGAIN || errno == EINTR))
11851e8db6e2SBrian Feldman 			return;		/* we'll try again later */
1186511b41d2SMark Murray 		if (len <= 0) {
1187511b41d2SMark Murray 			/*
1188511b41d2SMark Murray 			 * Received EOF or error.  They are treated
1189511b41d2SMark Murray 			 * similarly, except that an error message is printed
1190511b41d2SMark Murray 			 * if it was an error condition.
1191511b41d2SMark Murray 			 */
1192511b41d2SMark Murray 			if (len < 0) {
1193511b41d2SMark Murray 				snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno));
1194511b41d2SMark Murray 				buffer_append(&stderr_buffer, buf, strlen(buf));
1195511b41d2SMark Murray 			}
1196511b41d2SMark Murray 			/* Mark that we have seen EOF. */
1197511b41d2SMark Murray 			stdin_eof = 1;
1198511b41d2SMark Murray 			/*
1199511b41d2SMark Murray 			 * Send an EOF message to the server unless there is
1200511b41d2SMark Murray 			 * data in the buffer.  If there is data in the
1201511b41d2SMark Murray 			 * buffer, no message will be sent now.  Code
1202511b41d2SMark Murray 			 * elsewhere will send the EOF when the buffer
1203511b41d2SMark Murray 			 * becomes empty if stdin_eof is set.
1204511b41d2SMark Murray 			 */
1205511b41d2SMark Murray 			if (buffer_len(&stdin_buffer) == 0) {
1206511b41d2SMark Murray 				packet_start(SSH_CMSG_EOF);
1207511b41d2SMark Murray 				packet_send();
1208511b41d2SMark Murray 			}
1209ae1f160dSDag-Erling Smørgrav 		} else if (escape_char == SSH_ESCAPECHAR_NONE) {
1210511b41d2SMark Murray 			/*
1211511b41d2SMark Murray 			 * Normal successful read, and no escape character.
1212511b41d2SMark Murray 			 * Just append the data to buffer.
1213511b41d2SMark Murray 			 */
1214511b41d2SMark Murray 			buffer_append(&stdin_buffer, buf, len);
1215511b41d2SMark Murray 		} else {
1216511b41d2SMark Murray 			/*
1217511b41d2SMark Murray 			 * Normal, successful read.  But we have an escape character
1218511b41d2SMark Murray 			 * and have to process the characters one by one.
1219511b41d2SMark Murray 			 */
12201e8db6e2SBrian Feldman 			if (process_escapes(&stdin_buffer, &stdout_buffer,
12211e8db6e2SBrian Feldman 			    &stderr_buffer, buf, len) == -1)
1222511b41d2SMark Murray 				return;
1223511b41d2SMark Murray 		}
1224511b41d2SMark Murray 	}
1225511b41d2SMark Murray }
1226511b41d2SMark Murray 
1227ae1f160dSDag-Erling Smørgrav static void
1228511b41d2SMark Murray client_process_output(fd_set * writeset)
1229511b41d2SMark Murray {
1230511b41d2SMark Murray 	int len;
1231511b41d2SMark Murray 	char buf[100];
1232511b41d2SMark Murray 
1233511b41d2SMark Murray 	/* Write buffered output to stdout. */
1234511b41d2SMark Murray 	if (FD_ISSET(fileno(stdout), writeset)) {
1235511b41d2SMark Murray 		/* Write as much data as possible. */
1236511b41d2SMark Murray 		len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
1237511b41d2SMark Murray 		    buffer_len(&stdout_buffer));
1238511b41d2SMark Murray 		if (len <= 0) {
12391e8db6e2SBrian Feldman 			if (errno == EINTR || errno == EAGAIN)
1240511b41d2SMark Murray 				len = 0;
1241511b41d2SMark Murray 			else {
1242511b41d2SMark Murray 				/*
1243511b41d2SMark Murray 				 * An error or EOF was encountered.  Put an
1244511b41d2SMark Murray 				 * error message to stderr buffer.
1245511b41d2SMark Murray 				 */
1246511b41d2SMark Murray 				snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno));
1247511b41d2SMark Murray 				buffer_append(&stderr_buffer, buf, strlen(buf));
1248511b41d2SMark Murray 				quit_pending = 1;
1249511b41d2SMark Murray 				return;
1250511b41d2SMark Murray 			}
1251511b41d2SMark Murray 		}
1252511b41d2SMark Murray 		/* Consume printed data from the buffer. */
1253511b41d2SMark Murray 		buffer_consume(&stdout_buffer, len);
12541e8db6e2SBrian Feldman 		stdout_bytes += len;
1255511b41d2SMark Murray 	}
1256511b41d2SMark Murray 	/* Write buffered output to stderr. */
1257511b41d2SMark Murray 	if (FD_ISSET(fileno(stderr), writeset)) {
1258511b41d2SMark Murray 		/* Write as much data as possible. */
1259511b41d2SMark Murray 		len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
1260511b41d2SMark Murray 		    buffer_len(&stderr_buffer));
1261511b41d2SMark Murray 		if (len <= 0) {
12621e8db6e2SBrian Feldman 			if (errno == EINTR || errno == EAGAIN)
1263511b41d2SMark Murray 				len = 0;
1264511b41d2SMark Murray 			else {
1265511b41d2SMark Murray 				/* EOF or error, but can't even print error message. */
1266511b41d2SMark Murray 				quit_pending = 1;
1267511b41d2SMark Murray 				return;
1268511b41d2SMark Murray 			}
1269511b41d2SMark Murray 		}
1270511b41d2SMark Murray 		/* Consume printed characters from the buffer. */
1271511b41d2SMark Murray 		buffer_consume(&stderr_buffer, len);
12721e8db6e2SBrian Feldman 		stderr_bytes += len;
1273511b41d2SMark Murray 	}
1274511b41d2SMark Murray }
1275511b41d2SMark Murray 
1276511b41d2SMark Murray /*
1277a04a10f8SKris Kennaway  * Get packets from the connection input buffer, and process them as long as
1278a04a10f8SKris Kennaway  * there are packets available.
1279a04a10f8SKris Kennaway  *
1280a04a10f8SKris Kennaway  * Any unknown packets received during the actual
1281a04a10f8SKris Kennaway  * session cause the session to terminate.  This is
1282a04a10f8SKris Kennaway  * intended to make debugging easier since no
1283a04a10f8SKris Kennaway  * confirmations are sent.  Any compatible protocol
1284a04a10f8SKris Kennaway  * extensions must be negotiated during the
1285a04a10f8SKris Kennaway  * preparatory phase.
1286a04a10f8SKris Kennaway  */
1287a04a10f8SKris Kennaway 
1288ae1f160dSDag-Erling Smørgrav static void
12891e8db6e2SBrian Feldman client_process_buffered_input_packets(void)
1290a04a10f8SKris Kennaway {
12911e8db6e2SBrian Feldman 	dispatch_run(DISPATCH_NONBLOCK, &quit_pending, compat20 ? xxx_kex : NULL);
1292a04a10f8SKris Kennaway }
1293a04a10f8SKris Kennaway 
1294b66f2d16SKris Kennaway /* scan buf[] for '~' before sending data to the peer */
1295b66f2d16SKris Kennaway 
1296ae1f160dSDag-Erling Smørgrav static int
1297b66f2d16SKris Kennaway simple_escape_filter(Channel *c, char *buf, int len)
1298b66f2d16SKris Kennaway {
1299b66f2d16SKris Kennaway 	/* XXX we assume c->extended is writeable */
1300b66f2d16SKris Kennaway 	return process_escapes(&c->input, &c->output, &c->extended, buf, len);
1301b66f2d16SKris Kennaway }
1302b66f2d16SKris Kennaway 
1303ae1f160dSDag-Erling Smørgrav static void
13041e8db6e2SBrian Feldman client_channel_closed(int id, void *arg)
13051e8db6e2SBrian Feldman {
1306ae1f160dSDag-Erling Smørgrav 	channel_cancel_cleanup(id);
13071e8db6e2SBrian Feldman 	session_closed = 1;
13081e8db6e2SBrian Feldman 	leave_raw_mode();
13091e8db6e2SBrian Feldman }
13101e8db6e2SBrian Feldman 
1311a04a10f8SKris Kennaway /*
1312511b41d2SMark Murray  * Implements the interactive session with the server.  This is called after
1313511b41d2SMark Murray  * the user has been authenticated, and a command has been started on the
1314ae1f160dSDag-Erling Smørgrav  * remote host.  If escape_char != SSH_ESCAPECHAR_NONE, it is the character
1315ae1f160dSDag-Erling Smørgrav  * used as an escape character for terminating or suspending the session.
1316511b41d2SMark Murray  */
1317511b41d2SMark Murray 
1318511b41d2SMark Murray int
1319b66f2d16SKris Kennaway client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
1320511b41d2SMark Murray {
13211e8db6e2SBrian Feldman 	fd_set *readset = NULL, *writeset = NULL;
1322511b41d2SMark Murray 	double start_time, total_time;
1323d74d50a8SDag-Erling Smørgrav 	int max_fd = 0, max_fd2 = 0, len, rekeying = 0;
1324d74d50a8SDag-Erling Smørgrav 	u_int nalloc = 0;
1325511b41d2SMark Murray 	char buf[100];
1326511b41d2SMark Murray 
1327511b41d2SMark Murray 	debug("Entering interactive session.");
1328511b41d2SMark Murray 
1329511b41d2SMark Murray 	start_time = get_current_time();
1330511b41d2SMark Murray 
1331511b41d2SMark Murray 	/* Initialize variables. */
1332511b41d2SMark Murray 	escape_pending = 0;
1333511b41d2SMark Murray 	last_was_cr = 1;
1334511b41d2SMark Murray 	exit_status = -1;
1335511b41d2SMark Murray 	stdin_eof = 0;
1336511b41d2SMark Murray 	buffer_high = 64 * 1024;
1337511b41d2SMark Murray 	connection_in = packet_get_connection_in();
1338511b41d2SMark Murray 	connection_out = packet_get_connection_out();
13391e8db6e2SBrian Feldman 	max_fd = MAX(connection_in, connection_out);
1340d74d50a8SDag-Erling Smørgrav 	if (control_fd != -1)
1341d74d50a8SDag-Erling Smørgrav 		max_fd = MAX(max_fd, control_fd);
13421e8db6e2SBrian Feldman 
13431e8db6e2SBrian Feldman 	if (!compat20) {
13441e8db6e2SBrian Feldman 		/* enable nonblocking unless tty */
13451e8db6e2SBrian Feldman 		if (!isatty(fileno(stdin)))
13461e8db6e2SBrian Feldman 			set_nonblock(fileno(stdin));
13471e8db6e2SBrian Feldman 		if (!isatty(fileno(stdout)))
13481e8db6e2SBrian Feldman 			set_nonblock(fileno(stdout));
13491e8db6e2SBrian Feldman 		if (!isatty(fileno(stderr)))
13501e8db6e2SBrian Feldman 			set_nonblock(fileno(stderr));
13511e8db6e2SBrian Feldman 		max_fd = MAX(max_fd, fileno(stdin));
13521e8db6e2SBrian Feldman 		max_fd = MAX(max_fd, fileno(stdout));
13531e8db6e2SBrian Feldman 		max_fd = MAX(max_fd, fileno(stderr));
13541e8db6e2SBrian Feldman 	}
1355511b41d2SMark Murray 	stdin_bytes = 0;
1356511b41d2SMark Murray 	stdout_bytes = 0;
1357511b41d2SMark Murray 	stderr_bytes = 0;
1358511b41d2SMark Murray 	quit_pending = 0;
1359511b41d2SMark Murray 	escape_char = escape_char_arg;
1360511b41d2SMark Murray 
1361511b41d2SMark Murray 	/* Initialize buffers. */
1362511b41d2SMark Murray 	buffer_init(&stdin_buffer);
1363511b41d2SMark Murray 	buffer_init(&stdout_buffer);
1364511b41d2SMark Murray 	buffer_init(&stderr_buffer);
1365511b41d2SMark Murray 
1366a04a10f8SKris Kennaway 	client_init_dispatch();
1367a04a10f8SKris Kennaway 
1368d0c8c0bcSDag-Erling Smørgrav 	/*
1369d0c8c0bcSDag-Erling Smørgrav 	 * Set signal handlers, (e.g. to restore non-blocking mode)
1370d0c8c0bcSDag-Erling Smørgrav 	 * but don't overwrite SIG_IGN, matches behaviour from rsh(1)
1371d0c8c0bcSDag-Erling Smørgrav 	 */
13725e8dbd04SDag-Erling Smørgrav 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
13735e8dbd04SDag-Erling Smørgrav 		signal(SIGHUP, signal_handler);
1374d0c8c0bcSDag-Erling Smørgrav 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1375511b41d2SMark Murray 		signal(SIGINT, signal_handler);
1376d0c8c0bcSDag-Erling Smørgrav 	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
1377511b41d2SMark Murray 		signal(SIGQUIT, signal_handler);
1378d0c8c0bcSDag-Erling Smørgrav 	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
1379511b41d2SMark Murray 		signal(SIGTERM, signal_handler);
1380511b41d2SMark Murray 	signal(SIGWINCH, window_change_handler);
1381511b41d2SMark Murray 
1382511b41d2SMark Murray 	if (have_pty)
1383511b41d2SMark Murray 		enter_raw_mode();
1384511b41d2SMark Murray 
13851e8db6e2SBrian Feldman 	if (compat20) {
13861e8db6e2SBrian Feldman 		session_ident = ssh2_chan_id;
1387ae1f160dSDag-Erling Smørgrav 		if (escape_char != SSH_ESCAPECHAR_NONE)
13881e8db6e2SBrian Feldman 			channel_register_filter(session_ident,
1389021d409fSDag-Erling Smørgrav 			    simple_escape_filter, NULL);
13901e8db6e2SBrian Feldman 		if (session_ident != -1)
13911e8db6e2SBrian Feldman 			channel_register_cleanup(session_ident,
1392021d409fSDag-Erling Smørgrav 			    client_channel_closed, 0);
13931e8db6e2SBrian Feldman 	} else {
1394b66f2d16SKris Kennaway 		/* Check if we should immediately send eof on stdin. */
1395511b41d2SMark Murray 		client_check_initial_eof_on_stdin();
13961e8db6e2SBrian Feldman 	}
1397b66f2d16SKris Kennaway 
1398511b41d2SMark Murray 	/* Main loop of the client for the interactive session mode. */
1399511b41d2SMark Murray 	while (!quit_pending) {
1400511b41d2SMark Murray 
1401511b41d2SMark Murray 		/* Process buffered packets sent by the server. */
1402511b41d2SMark Murray 		client_process_buffered_input_packets();
1403511b41d2SMark Murray 
14041e8db6e2SBrian Feldman 		if (compat20 && session_closed && !channel_still_open())
1405a04a10f8SKris Kennaway 			break;
1406a04a10f8SKris Kennaway 
14071e8db6e2SBrian Feldman 		rekeying = (xxx_kex != NULL && !xxx_kex->done);
14081e8db6e2SBrian Feldman 
14091e8db6e2SBrian Feldman 		if (rekeying) {
14101e8db6e2SBrian Feldman 			debug("rekeying in progress");
14111e8db6e2SBrian Feldman 		} else {
1412511b41d2SMark Murray 			/*
14131e8db6e2SBrian Feldman 			 * Make packets of buffered stdin data, and buffer
14141e8db6e2SBrian Feldman 			 * them for sending to the server.
1415511b41d2SMark Murray 			 */
1416a04a10f8SKris Kennaway 			if (!compat20)
1417511b41d2SMark Murray 				client_make_packets_from_stdin_data();
1418511b41d2SMark Murray 
1419511b41d2SMark Murray 			/*
14201e8db6e2SBrian Feldman 			 * Make packets from buffered channel data, and
14211e8db6e2SBrian Feldman 			 * enqueue them for sending to the server.
1422511b41d2SMark Murray 			 */
1423511b41d2SMark Murray 			if (packet_not_very_much_data_to_write())
1424511b41d2SMark Murray 				channel_output_poll();
1425511b41d2SMark Murray 
1426511b41d2SMark Murray 			/*
14271e8db6e2SBrian Feldman 			 * Check if the window size has changed, and buffer a
14281e8db6e2SBrian Feldman 			 * message about it to the server if so.
1429511b41d2SMark Murray 			 */
1430511b41d2SMark Murray 			client_check_window_change();
1431511b41d2SMark Murray 
1432511b41d2SMark Murray 			if (quit_pending)
1433511b41d2SMark Murray 				break;
14341e8db6e2SBrian Feldman 		}
1435511b41d2SMark Murray 		/*
1436511b41d2SMark Murray 		 * Wait until we have something to do (something becomes
1437511b41d2SMark Murray 		 * available on one of the descriptors).
1438511b41d2SMark Murray 		 */
1439ae1f160dSDag-Erling Smørgrav 		max_fd2 = max_fd;
14401e8db6e2SBrian Feldman 		client_wait_until_can_do_something(&readset, &writeset,
1441ae1f160dSDag-Erling Smørgrav 		    &max_fd2, &nalloc, rekeying);
1442511b41d2SMark Murray 
1443511b41d2SMark Murray 		if (quit_pending)
1444511b41d2SMark Murray 			break;
1445511b41d2SMark Murray 
14461e8db6e2SBrian Feldman 		/* Do channel operations unless rekeying in progress. */
14471e8db6e2SBrian Feldman 		if (!rekeying) {
14481e8db6e2SBrian Feldman 			channel_after_select(readset, writeset);
1449d95e11bfSDag-Erling Smørgrav 			if (need_rekeying || packet_need_rekeying()) {
1450d95e11bfSDag-Erling Smørgrav 				debug("need rekeying");
14511e8db6e2SBrian Feldman 				xxx_kex->done = 0;
14521e8db6e2SBrian Feldman 				kex_send_kexinit(xxx_kex);
14531e8db6e2SBrian Feldman 				need_rekeying = 0;
14541e8db6e2SBrian Feldman 			}
14551e8db6e2SBrian Feldman 		}
1456511b41d2SMark Murray 
1457a04a10f8SKris Kennaway 		/* Buffer input from the connection.  */
14581e8db6e2SBrian Feldman 		client_process_net_input(readset);
1459511b41d2SMark Murray 
1460d74d50a8SDag-Erling Smørgrav 		/* Accept control connections.  */
1461d74d50a8SDag-Erling Smørgrav 		client_process_control(readset);
1462d74d50a8SDag-Erling Smørgrav 
1463a04a10f8SKris Kennaway 		if (quit_pending)
1464a04a10f8SKris Kennaway 			break;
1465a04a10f8SKris Kennaway 
1466a04a10f8SKris Kennaway 		if (!compat20) {
1467a04a10f8SKris Kennaway 			/* Buffer data from stdin */
14681e8db6e2SBrian Feldman 			client_process_input(readset);
1469511b41d2SMark Murray 			/*
1470a04a10f8SKris Kennaway 			 * Process output to stdout and stderr.  Output to
1471a04a10f8SKris Kennaway 			 * the connection is processed elsewhere (above).
1472511b41d2SMark Murray 			 */
14731e8db6e2SBrian Feldman 			client_process_output(writeset);
1474a04a10f8SKris Kennaway 		}
1475511b41d2SMark Murray 
1476511b41d2SMark Murray 		/* Send as much buffered packet data as possible to the sender. */
14771e8db6e2SBrian Feldman 		if (FD_ISSET(connection_out, writeset))
1478511b41d2SMark Murray 			packet_write_poll();
1479511b41d2SMark Murray 	}
14801e8db6e2SBrian Feldman 	if (readset)
14811e8db6e2SBrian Feldman 		xfree(readset);
14821e8db6e2SBrian Feldman 	if (writeset)
14831e8db6e2SBrian Feldman 		xfree(writeset);
1484511b41d2SMark Murray 
1485511b41d2SMark Murray 	/* Terminate the session. */
1486511b41d2SMark Murray 
1487511b41d2SMark Murray 	/* Stop watching for window change. */
1488511b41d2SMark Murray 	signal(SIGWINCH, SIG_DFL);
1489511b41d2SMark Murray 
1490ae1f160dSDag-Erling Smørgrav 	channel_free_all();
1491ae1f160dSDag-Erling Smørgrav 
1492ae1f160dSDag-Erling Smørgrav 	if (have_pty)
1493ae1f160dSDag-Erling Smørgrav 		leave_raw_mode();
1494ae1f160dSDag-Erling Smørgrav 
1495ae1f160dSDag-Erling Smørgrav 	/* restore blocking io */
1496ae1f160dSDag-Erling Smørgrav 	if (!isatty(fileno(stdin)))
1497ae1f160dSDag-Erling Smørgrav 		unset_nonblock(fileno(stdin));
1498ae1f160dSDag-Erling Smørgrav 	if (!isatty(fileno(stdout)))
1499ae1f160dSDag-Erling Smørgrav 		unset_nonblock(fileno(stdout));
1500ae1f160dSDag-Erling Smørgrav 	if (!isatty(fileno(stderr)))
1501ae1f160dSDag-Erling Smørgrav 		unset_nonblock(fileno(stderr));
1502ae1f160dSDag-Erling Smørgrav 
1503efcad6b7SDag-Erling Smørgrav 	/*
1504efcad6b7SDag-Erling Smørgrav 	 * If there was no shell or command requested, there will be no remote
1505efcad6b7SDag-Erling Smørgrav 	 * exit status to be returned.  In that case, clear error code if the
1506efcad6b7SDag-Erling Smørgrav 	 * connection was deliberately terminated at this end.
1507efcad6b7SDag-Erling Smørgrav 	 */
1508efcad6b7SDag-Erling Smørgrav 	if (no_shell_flag && received_signal == SIGTERM) {
1509efcad6b7SDag-Erling Smørgrav 		received_signal = 0;
1510efcad6b7SDag-Erling Smørgrav 		exit_status = 0;
1511ae1f160dSDag-Erling Smørgrav 	}
1512511b41d2SMark Murray 
1513efcad6b7SDag-Erling Smørgrav 	if (received_signal)
1514efcad6b7SDag-Erling Smørgrav 		fatal("Killed by signal %d.", (int) received_signal);
1515efcad6b7SDag-Erling Smørgrav 
1516511b41d2SMark Murray 	/*
1517511b41d2SMark Murray 	 * In interactive mode (with pseudo tty) display a message indicating
1518511b41d2SMark Murray 	 * that the connection has been closed.
1519511b41d2SMark Murray 	 */
1520511b41d2SMark Murray 	if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) {
1521511b41d2SMark Murray 		snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host);
1522511b41d2SMark Murray 		buffer_append(&stderr_buffer, buf, strlen(buf));
1523511b41d2SMark Murray 	}
1524ae1f160dSDag-Erling Smørgrav 
1525511b41d2SMark Murray 	/* Output any buffered data for stdout. */
1526511b41d2SMark Murray 	while (buffer_len(&stdout_buffer) > 0) {
1527511b41d2SMark Murray 		len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
1528511b41d2SMark Murray 		    buffer_len(&stdout_buffer));
1529511b41d2SMark Murray 		if (len <= 0) {
1530511b41d2SMark Murray 			error("Write failed flushing stdout buffer.");
1531511b41d2SMark Murray 			break;
1532511b41d2SMark Murray 		}
1533511b41d2SMark Murray 		buffer_consume(&stdout_buffer, len);
15341e8db6e2SBrian Feldman 		stdout_bytes += len;
1535511b41d2SMark Murray 	}
1536511b41d2SMark Murray 
1537511b41d2SMark Murray 	/* Output any buffered data for stderr. */
1538511b41d2SMark Murray 	while (buffer_len(&stderr_buffer) > 0) {
1539511b41d2SMark Murray 		len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
1540511b41d2SMark Murray 		    buffer_len(&stderr_buffer));
1541511b41d2SMark Murray 		if (len <= 0) {
1542511b41d2SMark Murray 			error("Write failed flushing stderr buffer.");
1543511b41d2SMark Murray 			break;
1544511b41d2SMark Murray 		}
1545511b41d2SMark Murray 		buffer_consume(&stderr_buffer, len);
15461e8db6e2SBrian Feldman 		stderr_bytes += len;
1547511b41d2SMark Murray 	}
1548511b41d2SMark Murray 
1549511b41d2SMark Murray 	/* Clear and free any buffers. */
1550511b41d2SMark Murray 	memset(buf, 0, sizeof(buf));
1551511b41d2SMark Murray 	buffer_free(&stdin_buffer);
1552511b41d2SMark Murray 	buffer_free(&stdout_buffer);
1553511b41d2SMark Murray 	buffer_free(&stderr_buffer);
1554511b41d2SMark Murray 
1555511b41d2SMark Murray 	/* Report bytes transferred, and transfer rates. */
1556511b41d2SMark Murray 	total_time = get_current_time() - start_time;
1557511b41d2SMark Murray 	debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds",
1558511b41d2SMark Murray 	    stdin_bytes, stdout_bytes, stderr_bytes, total_time);
1559511b41d2SMark Murray 	if (total_time > 0)
1560511b41d2SMark Murray 		debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f",
1561511b41d2SMark Murray 		    stdin_bytes / total_time, stdout_bytes / total_time,
1562511b41d2SMark Murray 		    stderr_bytes / total_time);
1563511b41d2SMark Murray 
1564511b41d2SMark Murray 	/* Return the exit status of the program. */
1565511b41d2SMark Murray 	debug("Exit status %d", exit_status);
1566511b41d2SMark Murray 	return exit_status;
1567511b41d2SMark Murray }
1568a04a10f8SKris Kennaway 
1569a04a10f8SKris Kennaway /*********/
1570a04a10f8SKris Kennaway 
1571ae1f160dSDag-Erling Smørgrav static void
1572ae1f160dSDag-Erling Smørgrav client_input_stdout_data(int type, u_int32_t seq, void *ctxt)
1573a04a10f8SKris Kennaway {
15741e8db6e2SBrian Feldman 	u_int data_len;
1575a04a10f8SKris Kennaway 	char *data = packet_get_string(&data_len);
1576ae1f160dSDag-Erling Smørgrav 	packet_check_eom();
1577a04a10f8SKris Kennaway 	buffer_append(&stdout_buffer, data, data_len);
1578a04a10f8SKris Kennaway 	memset(data, 0, data_len);
1579a04a10f8SKris Kennaway 	xfree(data);
1580a04a10f8SKris Kennaway }
1581ae1f160dSDag-Erling Smørgrav static void
1582ae1f160dSDag-Erling Smørgrav client_input_stderr_data(int type, u_int32_t seq, void *ctxt)
1583a04a10f8SKris Kennaway {
15841e8db6e2SBrian Feldman 	u_int data_len;
1585a04a10f8SKris Kennaway 	char *data = packet_get_string(&data_len);
1586ae1f160dSDag-Erling Smørgrav 	packet_check_eom();
1587a04a10f8SKris Kennaway 	buffer_append(&stderr_buffer, data, data_len);
1588a04a10f8SKris Kennaway 	memset(data, 0, data_len);
1589a04a10f8SKris Kennaway 	xfree(data);
1590a04a10f8SKris Kennaway }
1591ae1f160dSDag-Erling Smørgrav static void
1592ae1f160dSDag-Erling Smørgrav client_input_exit_status(int type, u_int32_t seq, void *ctxt)
1593a04a10f8SKris Kennaway {
1594a04a10f8SKris Kennaway 	exit_status = packet_get_int();
1595ae1f160dSDag-Erling Smørgrav 	packet_check_eom();
1596a04a10f8SKris Kennaway 	/* Acknowledge the exit. */
1597a04a10f8SKris Kennaway 	packet_start(SSH_CMSG_EXIT_CONFIRMATION);
1598a04a10f8SKris Kennaway 	packet_send();
1599a04a10f8SKris Kennaway 	/*
1600a04a10f8SKris Kennaway 	 * Must wait for packet to be sent since we are
1601a04a10f8SKris Kennaway 	 * exiting the loop.
1602a04a10f8SKris Kennaway 	 */
1603a04a10f8SKris Kennaway 	packet_write_wait();
1604a04a10f8SKris Kennaway 	/* Flag that we want to exit. */
1605a04a10f8SKris Kennaway 	quit_pending = 1;
1606a04a10f8SKris Kennaway }
1607efcad6b7SDag-Erling Smørgrav static void
1608efcad6b7SDag-Erling Smørgrav client_input_agent_open(int type, u_int32_t seq, void *ctxt)
1609efcad6b7SDag-Erling Smørgrav {
1610efcad6b7SDag-Erling Smørgrav 	Channel *c = NULL;
1611efcad6b7SDag-Erling Smørgrav 	int remote_id, sock;
1612efcad6b7SDag-Erling Smørgrav 
1613efcad6b7SDag-Erling Smørgrav 	/* Read the remote channel number from the message. */
1614efcad6b7SDag-Erling Smørgrav 	remote_id = packet_get_int();
1615efcad6b7SDag-Erling Smørgrav 	packet_check_eom();
1616efcad6b7SDag-Erling Smørgrav 
1617efcad6b7SDag-Erling Smørgrav 	/*
1618efcad6b7SDag-Erling Smørgrav 	 * Get a connection to the local authentication agent (this may again
1619efcad6b7SDag-Erling Smørgrav 	 * get forwarded).
1620efcad6b7SDag-Erling Smørgrav 	 */
1621efcad6b7SDag-Erling Smørgrav 	sock = ssh_get_authentication_socket();
1622efcad6b7SDag-Erling Smørgrav 
1623efcad6b7SDag-Erling Smørgrav 	/*
1624efcad6b7SDag-Erling Smørgrav 	 * If we could not connect the agent, send an error message back to
1625efcad6b7SDag-Erling Smørgrav 	 * the server. This should never happen unless the agent dies,
1626efcad6b7SDag-Erling Smørgrav 	 * because authentication forwarding is only enabled if we have an
1627efcad6b7SDag-Erling Smørgrav 	 * agent.
1628efcad6b7SDag-Erling Smørgrav 	 */
1629efcad6b7SDag-Erling Smørgrav 	if (sock >= 0) {
1630efcad6b7SDag-Erling Smørgrav 		c = channel_new("", SSH_CHANNEL_OPEN, sock, sock,
1631efcad6b7SDag-Erling Smørgrav 		    -1, 0, 0, 0, "authentication agent connection", 1);
1632efcad6b7SDag-Erling Smørgrav 		c->remote_id = remote_id;
1633efcad6b7SDag-Erling Smørgrav 		c->force_drain = 1;
1634efcad6b7SDag-Erling Smørgrav 	}
1635efcad6b7SDag-Erling Smørgrav 	if (c == NULL) {
1636efcad6b7SDag-Erling Smørgrav 		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
1637efcad6b7SDag-Erling Smørgrav 		packet_put_int(remote_id);
1638efcad6b7SDag-Erling Smørgrav 	} else {
1639efcad6b7SDag-Erling Smørgrav 		/* Send a confirmation to the remote host. */
1640efcad6b7SDag-Erling Smørgrav 		debug("Forwarding authentication connection.");
1641efcad6b7SDag-Erling Smørgrav 		packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
1642efcad6b7SDag-Erling Smørgrav 		packet_put_int(remote_id);
1643efcad6b7SDag-Erling Smørgrav 		packet_put_int(c->self);
1644efcad6b7SDag-Erling Smørgrav 	}
1645efcad6b7SDag-Erling Smørgrav 	packet_send();
1646efcad6b7SDag-Erling Smørgrav }
1647a04a10f8SKris Kennaway 
1648ae1f160dSDag-Erling Smørgrav static Channel *
16491e8db6e2SBrian Feldman client_request_forwarded_tcpip(const char *request_type, int rchan)
16501e8db6e2SBrian Feldman {
16511e8db6e2SBrian Feldman 	Channel *c = NULL;
16521e8db6e2SBrian Feldman 	char *listen_address, *originator_address;
16531e8db6e2SBrian Feldman 	int listen_port, originator_port;
1654ae1f160dSDag-Erling Smørgrav 	int sock;
16551e8db6e2SBrian Feldman 
16561e8db6e2SBrian Feldman 	/* Get rest of the packet */
16571e8db6e2SBrian Feldman 	listen_address = packet_get_string(NULL);
16581e8db6e2SBrian Feldman 	listen_port = packet_get_int();
16591e8db6e2SBrian Feldman 	originator_address = packet_get_string(NULL);
16601e8db6e2SBrian Feldman 	originator_port = packet_get_int();
1661ae1f160dSDag-Erling Smørgrav 	packet_check_eom();
16621e8db6e2SBrian Feldman 
16631e8db6e2SBrian Feldman 	debug("client_request_forwarded_tcpip: listen %s port %d, originator %s port %d",
16641e8db6e2SBrian Feldman 	    listen_address, listen_port, originator_address, originator_port);
16651e8db6e2SBrian Feldman 
1666ae1f160dSDag-Erling Smørgrav 	sock = channel_connect_by_listen_address(listen_port);
1667ae1f160dSDag-Erling Smørgrav 	if (sock < 0) {
1668ae1f160dSDag-Erling Smørgrav 		xfree(originator_address);
1669ae1f160dSDag-Erling Smørgrav 		xfree(listen_address);
1670ae1f160dSDag-Erling Smørgrav 		return NULL;
1671ae1f160dSDag-Erling Smørgrav 	}
1672ae1f160dSDag-Erling Smørgrav 	c = channel_new("forwarded-tcpip",
16731e8db6e2SBrian Feldman 	    SSH_CHANNEL_CONNECTING, sock, sock, -1,
16741e8db6e2SBrian Feldman 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0,
1675d95e11bfSDag-Erling Smørgrav 	    originator_address, 1);
16761e8db6e2SBrian Feldman 	xfree(originator_address);
16771e8db6e2SBrian Feldman 	xfree(listen_address);
16781e8db6e2SBrian Feldman 	return c;
16791e8db6e2SBrian Feldman }
16801e8db6e2SBrian Feldman 
1681ae1f160dSDag-Erling Smørgrav static Channel *
16821e8db6e2SBrian Feldman client_request_x11(const char *request_type, int rchan)
16831e8db6e2SBrian Feldman {
16841e8db6e2SBrian Feldman 	Channel *c = NULL;
16851e8db6e2SBrian Feldman 	char *originator;
16861e8db6e2SBrian Feldman 	int originator_port;
1687ae1f160dSDag-Erling Smørgrav 	int sock;
16881e8db6e2SBrian Feldman 
16891e8db6e2SBrian Feldman 	if (!options.forward_x11) {
16901e8db6e2SBrian Feldman 		error("Warning: ssh server tried X11 forwarding.");
1691021d409fSDag-Erling Smørgrav 		error("Warning: this is probably a break-in attempt by a malicious server.");
16921e8db6e2SBrian Feldman 		return NULL;
16931e8db6e2SBrian Feldman 	}
16941e8db6e2SBrian Feldman 	originator = packet_get_string(NULL);
16951e8db6e2SBrian Feldman 	if (datafellows & SSH_BUG_X11FWD) {
16961e8db6e2SBrian Feldman 		debug2("buggy server: x11 request w/o originator_port");
16971e8db6e2SBrian Feldman 		originator_port = 0;
16981e8db6e2SBrian Feldman 	} else {
16991e8db6e2SBrian Feldman 		originator_port = packet_get_int();
17001e8db6e2SBrian Feldman 	}
1701ae1f160dSDag-Erling Smørgrav 	packet_check_eom();
17021e8db6e2SBrian Feldman 	/* XXX check permission */
17031e8db6e2SBrian Feldman 	debug("client_request_x11: request from %s %d", originator,
17041e8db6e2SBrian Feldman 	    originator_port);
1705ae1f160dSDag-Erling Smørgrav 	xfree(originator);
17061e8db6e2SBrian Feldman 	sock = x11_connect_display();
1707ae1f160dSDag-Erling Smørgrav 	if (sock < 0)
1708ae1f160dSDag-Erling Smørgrav 		return NULL;
1709ae1f160dSDag-Erling Smørgrav 	c = channel_new("x11",
17101e8db6e2SBrian Feldman 	    SSH_CHANNEL_X11_OPEN, sock, sock, -1,
1711d95e11bfSDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1);
1712ae1f160dSDag-Erling Smørgrav 	c->force_drain = 1;
17131e8db6e2SBrian Feldman 	return c;
17141e8db6e2SBrian Feldman }
17151e8db6e2SBrian Feldman 
1716ae1f160dSDag-Erling Smørgrav static Channel *
17171e8db6e2SBrian Feldman client_request_agent(const char *request_type, int rchan)
17181e8db6e2SBrian Feldman {
17191e8db6e2SBrian Feldman 	Channel *c = NULL;
1720ae1f160dSDag-Erling Smørgrav 	int sock;
17211e8db6e2SBrian Feldman 
17221e8db6e2SBrian Feldman 	if (!options.forward_agent) {
17231e8db6e2SBrian Feldman 		error("Warning: ssh server tried agent forwarding.");
1724021d409fSDag-Erling Smørgrav 		error("Warning: this is probably a break-in attempt by a malicious server.");
17251e8db6e2SBrian Feldman 		return NULL;
17261e8db6e2SBrian Feldman 	}
17271e8db6e2SBrian Feldman 	sock =  ssh_get_authentication_socket();
1728ae1f160dSDag-Erling Smørgrav 	if (sock < 0)
1729ae1f160dSDag-Erling Smørgrav 		return NULL;
1730ae1f160dSDag-Erling Smørgrav 	c = channel_new("authentication agent connection",
17311e8db6e2SBrian Feldman 	    SSH_CHANNEL_OPEN, sock, sock, -1,
17321e8db6e2SBrian Feldman 	    CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0,
1733d95e11bfSDag-Erling Smørgrav 	    "authentication agent connection", 1);
1734ae1f160dSDag-Erling Smørgrav 	c->force_drain = 1;
17351e8db6e2SBrian Feldman 	return c;
17361e8db6e2SBrian Feldman }
17371e8db6e2SBrian Feldman 
1738a04a10f8SKris Kennaway /* XXXX move to generic input handler */
1739ae1f160dSDag-Erling Smørgrav static void
1740ae1f160dSDag-Erling Smørgrav client_input_channel_open(int type, u_int32_t seq, void *ctxt)
1741a04a10f8SKris Kennaway {
1742a04a10f8SKris Kennaway 	Channel *c = NULL;
1743a04a10f8SKris Kennaway 	char *ctype;
1744a04a10f8SKris Kennaway 	int rchan;
1745ee21a45fSDag-Erling Smørgrav 	u_int rmaxpack, rwindow, len;
1746a04a10f8SKris Kennaway 
1747a04a10f8SKris Kennaway 	ctype = packet_get_string(&len);
1748a04a10f8SKris Kennaway 	rchan = packet_get_int();
1749a04a10f8SKris Kennaway 	rwindow = packet_get_int();
1750a04a10f8SKris Kennaway 	rmaxpack = packet_get_int();
1751a04a10f8SKris Kennaway 
1752a04a10f8SKris Kennaway 	debug("client_input_channel_open: ctype %s rchan %d win %d max %d",
1753a04a10f8SKris Kennaway 	    ctype, rchan, rwindow, rmaxpack);
1754a04a10f8SKris Kennaway 
17551e8db6e2SBrian Feldman 	if (strcmp(ctype, "forwarded-tcpip") == 0) {
17561e8db6e2SBrian Feldman 		c = client_request_forwarded_tcpip(ctype, rchan);
17571e8db6e2SBrian Feldman 	} else if (strcmp(ctype, "x11") == 0) {
17581e8db6e2SBrian Feldman 		c = client_request_x11(ctype, rchan);
17591e8db6e2SBrian Feldman 	} else if (strcmp(ctype, "auth-agent@openssh.com") == 0) {
17601e8db6e2SBrian Feldman 		c = client_request_agent(ctype, rchan);
1761a04a10f8SKris Kennaway 	}
1762a04a10f8SKris Kennaway /* XXX duplicate : */
1763a04a10f8SKris Kennaway 	if (c != NULL) {
1764a04a10f8SKris Kennaway 		debug("confirm %s", ctype);
1765a04a10f8SKris Kennaway 		c->remote_id = rchan;
1766a04a10f8SKris Kennaway 		c->remote_window = rwindow;
1767a04a10f8SKris Kennaway 		c->remote_maxpacket = rmaxpack;
1768ae1f160dSDag-Erling Smørgrav 		if (c->type != SSH_CHANNEL_CONNECTING) {
1769a04a10f8SKris Kennaway 			packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
1770a04a10f8SKris Kennaway 			packet_put_int(c->remote_id);
1771a04a10f8SKris Kennaway 			packet_put_int(c->self);
1772a04a10f8SKris Kennaway 			packet_put_int(c->local_window);
1773a04a10f8SKris Kennaway 			packet_put_int(c->local_maxpacket);
1774a04a10f8SKris Kennaway 			packet_send();
1775ae1f160dSDag-Erling Smørgrav 		}
1776a04a10f8SKris Kennaway 	} else {
1777a04a10f8SKris Kennaway 		debug("failure %s", ctype);
1778a04a10f8SKris Kennaway 		packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
1779a04a10f8SKris Kennaway 		packet_put_int(rchan);
1780a04a10f8SKris Kennaway 		packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED);
1781ae1f160dSDag-Erling Smørgrav 		if (!(datafellows & SSH_BUG_OPENFAILURE)) {
1782ae1f160dSDag-Erling Smørgrav 			packet_put_cstring("open failed");
1783a04a10f8SKris Kennaway 			packet_put_cstring("");
1784ae1f160dSDag-Erling Smørgrav 		}
1785a04a10f8SKris Kennaway 		packet_send();
1786a04a10f8SKris Kennaway 	}
1787a04a10f8SKris Kennaway 	xfree(ctype);
1788a04a10f8SKris Kennaway }
1789ae1f160dSDag-Erling Smørgrav static void
1790ae1f160dSDag-Erling Smørgrav client_input_channel_req(int type, u_int32_t seq, void *ctxt)
17911e8db6e2SBrian Feldman {
17921e8db6e2SBrian Feldman 	Channel *c = NULL;
1793d74d50a8SDag-Erling Smørgrav 	int exitval, id, reply, success = 0;
17941e8db6e2SBrian Feldman 	char *rtype;
17951e8db6e2SBrian Feldman 
17961e8db6e2SBrian Feldman 	id = packet_get_int();
17971e8db6e2SBrian Feldman 	rtype = packet_get_string(NULL);
17981e8db6e2SBrian Feldman 	reply = packet_get_char();
17991e8db6e2SBrian Feldman 
18001e8db6e2SBrian Feldman 	debug("client_input_channel_req: channel %d rtype %s reply %d",
18011e8db6e2SBrian Feldman 	    id, rtype, reply);
18021e8db6e2SBrian Feldman 
1803d74d50a8SDag-Erling Smørgrav 	if (id == -1) {
1804d74d50a8SDag-Erling Smørgrav 		error("client_input_channel_req: request for channel -1");
1805d74d50a8SDag-Erling Smørgrav 	} else if ((c = channel_lookup(id)) == NULL) {
18061e8db6e2SBrian Feldman 		error("client_input_channel_req: channel %d: unknown channel", id);
18071e8db6e2SBrian Feldman 	} else if (strcmp(rtype, "exit-status") == 0) {
1808d74d50a8SDag-Erling Smørgrav 		exitval = packet_get_int();
1809d74d50a8SDag-Erling Smørgrav 		if (id == session_ident) {
18101e8db6e2SBrian Feldman 			success = 1;
1811d74d50a8SDag-Erling Smørgrav 			exit_status = exitval;
1812d74d50a8SDag-Erling Smørgrav 		} else if (c->ctl_fd == -1) {
1813d74d50a8SDag-Erling Smørgrav 			error("client_input_channel_req: unexpected channel %d",
1814d74d50a8SDag-Erling Smørgrav 			    session_ident);
1815d74d50a8SDag-Erling Smørgrav 		} else {
1816d74d50a8SDag-Erling Smørgrav 			atomicio(vwrite, c->ctl_fd, &exitval, sizeof(exitval));
1817d74d50a8SDag-Erling Smørgrav 			success = 1;
1818d74d50a8SDag-Erling Smørgrav 		}
1819ae1f160dSDag-Erling Smørgrav 		packet_check_eom();
18201e8db6e2SBrian Feldman 	}
18211e8db6e2SBrian Feldman 	if (reply) {
18221e8db6e2SBrian Feldman 		packet_start(success ?
18231e8db6e2SBrian Feldman 		    SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
1824d74d50a8SDag-Erling Smørgrav 		packet_put_int(id);
18251e8db6e2SBrian Feldman 		packet_send();
18261e8db6e2SBrian Feldman 	}
18271e8db6e2SBrian Feldman 	xfree(rtype);
18281e8db6e2SBrian Feldman }
1829ae1f160dSDag-Erling Smørgrav static void
1830ae1f160dSDag-Erling Smørgrav client_input_global_request(int type, u_int32_t seq, void *ctxt)
1831ae1f160dSDag-Erling Smørgrav {
1832ae1f160dSDag-Erling Smørgrav 	char *rtype;
1833ae1f160dSDag-Erling Smørgrav 	int want_reply;
1834ae1f160dSDag-Erling Smørgrav 	int success = 0;
1835a04a10f8SKris Kennaway 
1836ae1f160dSDag-Erling Smørgrav 	rtype = packet_get_string(NULL);
1837ae1f160dSDag-Erling Smørgrav 	want_reply = packet_get_char();
1838efcad6b7SDag-Erling Smørgrav 	debug("client_input_global_request: rtype %s want_reply %d",
1839efcad6b7SDag-Erling Smørgrav 	    rtype, want_reply);
1840ae1f160dSDag-Erling Smørgrav 	if (want_reply) {
1841ae1f160dSDag-Erling Smørgrav 		packet_start(success ?
1842ae1f160dSDag-Erling Smørgrav 		    SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE);
1843ae1f160dSDag-Erling Smørgrav 		packet_send();
1844ae1f160dSDag-Erling Smørgrav 		packet_write_wait();
1845ae1f160dSDag-Erling Smørgrav 	}
1846ae1f160dSDag-Erling Smørgrav 	xfree(rtype);
1847ae1f160dSDag-Erling Smørgrav }
1848ae1f160dSDag-Erling Smørgrav 
1849d74d50a8SDag-Erling Smørgrav void
1850d74d50a8SDag-Erling Smørgrav client_session2_setup(int id, int want_tty, int want_subsystem,
1851d74d50a8SDag-Erling Smørgrav     const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env,
1852d74d50a8SDag-Erling Smørgrav     dispatch_fn *subsys_repl)
1853d74d50a8SDag-Erling Smørgrav {
1854d74d50a8SDag-Erling Smørgrav 	int len;
18555e8dbd04SDag-Erling Smørgrav 	Channel *c = NULL;
1856d74d50a8SDag-Erling Smørgrav 
1857d74d50a8SDag-Erling Smørgrav 	debug2("%s: id %d", __func__, id);
1858d74d50a8SDag-Erling Smørgrav 
18595e8dbd04SDag-Erling Smørgrav 	if ((c = channel_lookup(id)) == NULL)
18605e8dbd04SDag-Erling Smørgrav 		fatal("client_session2_setup: channel %d: unknown channel", id);
18615e8dbd04SDag-Erling Smørgrav 
1862d74d50a8SDag-Erling Smørgrav 	if (want_tty) {
1863d74d50a8SDag-Erling Smørgrav 		struct winsize ws;
1864d74d50a8SDag-Erling Smørgrav 		struct termios tio;
1865d74d50a8SDag-Erling Smørgrav 
1866d74d50a8SDag-Erling Smørgrav 		/* Store window size in the packet. */
1867d74d50a8SDag-Erling Smørgrav 		if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0)
1868d74d50a8SDag-Erling Smørgrav 			memset(&ws, 0, sizeof(ws));
1869d74d50a8SDag-Erling Smørgrav 
1870d74d50a8SDag-Erling Smørgrav 		channel_request_start(id, "pty-req", 0);
1871d74d50a8SDag-Erling Smørgrav 		packet_put_cstring(term != NULL ? term : "");
1872d74d50a8SDag-Erling Smørgrav 		packet_put_int(ws.ws_col);
1873d74d50a8SDag-Erling Smørgrav 		packet_put_int(ws.ws_row);
1874d74d50a8SDag-Erling Smørgrav 		packet_put_int(ws.ws_xpixel);
1875d74d50a8SDag-Erling Smørgrav 		packet_put_int(ws.ws_ypixel);
1876d74d50a8SDag-Erling Smørgrav 		tio = get_saved_tio();
1877d74d50a8SDag-Erling Smørgrav 		tty_make_modes(-1, tiop != NULL ? tiop : &tio);
1878d74d50a8SDag-Erling Smørgrav 		packet_send();
1879d74d50a8SDag-Erling Smørgrav 		/* XXX wait for reply */
18805e8dbd04SDag-Erling Smørgrav 		c->client_tty = 1;
1881d74d50a8SDag-Erling Smørgrav 	}
1882d74d50a8SDag-Erling Smørgrav 
1883d74d50a8SDag-Erling Smørgrav 	/* Transfer any environment variables from client to server */
1884d74d50a8SDag-Erling Smørgrav 	if (options.num_send_env != 0 && env != NULL) {
1885d74d50a8SDag-Erling Smørgrav 		int i, j, matched;
1886d74d50a8SDag-Erling Smørgrav 		char *name, *val;
1887d74d50a8SDag-Erling Smørgrav 
1888d74d50a8SDag-Erling Smørgrav 		debug("Sending environment.");
1889d74d50a8SDag-Erling Smørgrav 		for (i = 0; env[i] != NULL; i++) {
1890d74d50a8SDag-Erling Smørgrav 			/* Split */
1891d74d50a8SDag-Erling Smørgrav 			name = xstrdup(env[i]);
1892d74d50a8SDag-Erling Smørgrav 			if ((val = strchr(name, '=')) == NULL) {
1893021d409fSDag-Erling Smørgrav 				xfree(name);
1894d74d50a8SDag-Erling Smørgrav 				continue;
1895d74d50a8SDag-Erling Smørgrav 			}
1896d74d50a8SDag-Erling Smørgrav 			*val++ = '\0';
1897d74d50a8SDag-Erling Smørgrav 
1898d74d50a8SDag-Erling Smørgrav 			matched = 0;
1899d74d50a8SDag-Erling Smørgrav 			for (j = 0; j < options.num_send_env; j++) {
1900d74d50a8SDag-Erling Smørgrav 				if (match_pattern(name, options.send_env[j])) {
1901d74d50a8SDag-Erling Smørgrav 					matched = 1;
1902d74d50a8SDag-Erling Smørgrav 					break;
1903d74d50a8SDag-Erling Smørgrav 				}
1904d74d50a8SDag-Erling Smørgrav 			}
1905d74d50a8SDag-Erling Smørgrav 			if (!matched) {
1906d74d50a8SDag-Erling Smørgrav 				debug3("Ignored env %s", name);
1907021d409fSDag-Erling Smørgrav 				xfree(name);
1908d74d50a8SDag-Erling Smørgrav 				continue;
1909d74d50a8SDag-Erling Smørgrav 			}
1910d74d50a8SDag-Erling Smørgrav 
1911d74d50a8SDag-Erling Smørgrav 			debug("Sending env %s = %s", name, val);
1912d74d50a8SDag-Erling Smørgrav 			channel_request_start(id, "env", 0);
1913d74d50a8SDag-Erling Smørgrav 			packet_put_cstring(name);
1914d74d50a8SDag-Erling Smørgrav 			packet_put_cstring(val);
1915d74d50a8SDag-Erling Smørgrav 			packet_send();
1916021d409fSDag-Erling Smørgrav 			xfree(name);
1917d74d50a8SDag-Erling Smørgrav 		}
1918d74d50a8SDag-Erling Smørgrav 	}
1919d74d50a8SDag-Erling Smørgrav 
1920d74d50a8SDag-Erling Smørgrav 	len = buffer_len(cmd);
1921d74d50a8SDag-Erling Smørgrav 	if (len > 0) {
1922d74d50a8SDag-Erling Smørgrav 		if (len > 900)
1923d74d50a8SDag-Erling Smørgrav 			len = 900;
1924d74d50a8SDag-Erling Smørgrav 		if (want_subsystem) {
1925d74d50a8SDag-Erling Smørgrav 			debug("Sending subsystem: %.*s", len, (u_char*)buffer_ptr(cmd));
1926d74d50a8SDag-Erling Smørgrav 			channel_request_start(id, "subsystem", subsys_repl != NULL);
1927d74d50a8SDag-Erling Smørgrav 			if (subsys_repl != NULL) {
1928d74d50a8SDag-Erling Smørgrav 				/* register callback for reply */
1929d74d50a8SDag-Erling Smørgrav 				/* XXX we assume that client_loop has already been called */
1930d74d50a8SDag-Erling Smørgrav 				dispatch_set(SSH2_MSG_CHANNEL_FAILURE, subsys_repl);
1931d74d50a8SDag-Erling Smørgrav 				dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, subsys_repl);
1932d74d50a8SDag-Erling Smørgrav 			}
1933d74d50a8SDag-Erling Smørgrav 		} else {
1934d74d50a8SDag-Erling Smørgrav 			debug("Sending command: %.*s", len, (u_char*)buffer_ptr(cmd));
1935d74d50a8SDag-Erling Smørgrav 			channel_request_start(id, "exec", 0);
1936d74d50a8SDag-Erling Smørgrav 		}
1937d74d50a8SDag-Erling Smørgrav 		packet_put_string(buffer_ptr(cmd), buffer_len(cmd));
1938d74d50a8SDag-Erling Smørgrav 		packet_send();
1939d74d50a8SDag-Erling Smørgrav 	} else {
1940d74d50a8SDag-Erling Smørgrav 		channel_request_start(id, "shell", 0);
1941d74d50a8SDag-Erling Smørgrav 		packet_send();
1942d74d50a8SDag-Erling Smørgrav 	}
1943d74d50a8SDag-Erling Smørgrav }
1944d74d50a8SDag-Erling Smørgrav 
1945ae1f160dSDag-Erling Smørgrav static void
19461e8db6e2SBrian Feldman client_init_dispatch_20(void)
1947a04a10f8SKris Kennaway {
1948a04a10f8SKris Kennaway 	dispatch_init(&dispatch_protocol_error);
1949545d5ecaSDag-Erling Smørgrav 
1950a04a10f8SKris Kennaway 	dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose);
1951a04a10f8SKris Kennaway 	dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data);
1952a04a10f8SKris Kennaway 	dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof);
1953a04a10f8SKris Kennaway 	dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data);
1954a04a10f8SKris Kennaway 	dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open);
1955a04a10f8SKris Kennaway 	dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
1956a04a10f8SKris Kennaway 	dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
19571e8db6e2SBrian Feldman 	dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req);
1958a04a10f8SKris Kennaway 	dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust);
1959ae1f160dSDag-Erling Smørgrav 	dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request);
19601e8db6e2SBrian Feldman 
19611e8db6e2SBrian Feldman 	/* rekeying */
19621e8db6e2SBrian Feldman 	dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
1963545d5ecaSDag-Erling Smørgrav 
1964545d5ecaSDag-Erling Smørgrav 	/* global request reply messages */
1965545d5ecaSDag-Erling Smørgrav 	dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply);
1966545d5ecaSDag-Erling Smørgrav 	dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply);
1967a04a10f8SKris Kennaway }
1968ae1f160dSDag-Erling Smørgrav static void
19691e8db6e2SBrian Feldman client_init_dispatch_13(void)
1970a04a10f8SKris Kennaway {
1971a04a10f8SKris Kennaway 	dispatch_init(NULL);
1972a04a10f8SKris Kennaway 	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close);
1973a04a10f8SKris Kennaway 	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation);
1974a04a10f8SKris Kennaway 	dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data);
1975a04a10f8SKris Kennaway 	dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
1976a04a10f8SKris Kennaway 	dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
1977a04a10f8SKris Kennaway 	dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open);
1978a04a10f8SKris Kennaway 	dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status);
1979a04a10f8SKris Kennaway 	dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data);
1980a04a10f8SKris Kennaway 	dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data);
19814899dde7SBrian Feldman 
19824899dde7SBrian Feldman 	dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ?
1983efcad6b7SDag-Erling Smørgrav 	    &client_input_agent_open : &deny_input_open);
19844899dde7SBrian Feldman 	dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ?
19855b9b2fafSBrian Feldman 	    &x11_input_open : &deny_input_open);
1986a04a10f8SKris Kennaway }
1987ae1f160dSDag-Erling Smørgrav static void
19881e8db6e2SBrian Feldman client_init_dispatch_15(void)
1989a04a10f8SKris Kennaway {
1990a04a10f8SKris Kennaway 	client_init_dispatch_13();
1991a04a10f8SKris Kennaway 	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof);
1992a04a10f8SKris Kennaway 	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose);
1993a04a10f8SKris Kennaway }
1994ae1f160dSDag-Erling Smørgrav static void
19951e8db6e2SBrian Feldman client_init_dispatch(void)
1996a04a10f8SKris Kennaway {
1997a04a10f8SKris Kennaway 	if (compat20)
1998a04a10f8SKris Kennaway 		client_init_dispatch_20();
1999a04a10f8SKris Kennaway 	else if (compat13)
2000a04a10f8SKris Kennaway 		client_init_dispatch_13();
2001a04a10f8SKris Kennaway 	else
2002a04a10f8SKris Kennaway 		client_init_dispatch_15();
2003a04a10f8SKris Kennaway }
2004efcad6b7SDag-Erling Smørgrav 
2005efcad6b7SDag-Erling Smørgrav /* client specific fatal cleanup */
2006efcad6b7SDag-Erling Smørgrav void
2007efcad6b7SDag-Erling Smørgrav cleanup_exit(int i)
2008efcad6b7SDag-Erling Smørgrav {
2009efcad6b7SDag-Erling Smørgrav 	leave_raw_mode();
2010efcad6b7SDag-Erling Smørgrav 	leave_non_blocking();
2011d74d50a8SDag-Erling Smørgrav 	if (options.control_path != NULL && control_fd != -1)
2012d74d50a8SDag-Erling Smørgrav 		unlink(options.control_path);
2013efcad6b7SDag-Erling Smørgrav 	_exit(i);
2014efcad6b7SDag-Erling Smørgrav }
2015