1263970Sdes/* $OpenBSD: clientloop.c,v 1.258 2014/02/02 03:44:31 djm Exp $ */
2224638Sbrooks/* $FreeBSD$ */
357429Smarkm/*
457429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi>
557429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
657429Smarkm *                    All rights reserved
765668Skris * The main loop for the interactive session (client side).
860573Skris *
965668Skris * As far as I am concerned, the code I have written for this software
1065668Skris * can be used freely for any purpose.  Any derived versions of this
1165668Skris * software must be clearly marked as such, and if the derived work is
1265668Skris * incompatible with the protocol description in the RFC file, it must be
1365668Skris * called by a name other than "ssh" or "Secure Shell".
1460573Skris *
1560573Skris *
1665668Skris * Copyright (c) 1999 Theo de Raadt.  All rights reserved.
1760573Skris *
1865668Skris * Redistribution and use in source and binary forms, with or without
1965668Skris * modification, are permitted provided that the following conditions
2065668Skris * are met:
2165668Skris * 1. Redistributions of source code must retain the above copyright
2265668Skris *    notice, this list of conditions and the following disclaimer.
2365668Skris * 2. Redistributions in binary form must reproduce the above copyright
2465668Skris *    notice, this list of conditions and the following disclaimer in the
2565668Skris *    documentation and/or other materials provided with the distribution.
2665668Skris *
2765668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2865668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2965668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
3065668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
3165668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
3265668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3365668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3465668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3565668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3665668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3765668Skris *
3865668Skris *
3960573Skris * SSH2 support added by Markus Friedl.
4092555Sdes * Copyright (c) 1999, 2000, 2001 Markus Friedl.  All rights reserved.
4165668Skris *
4265668Skris * Redistribution and use in source and binary forms, with or without
4365668Skris * modification, are permitted provided that the following conditions
4465668Skris * are met:
4565668Skris * 1. Redistributions of source code must retain the above copyright
4665668Skris *    notice, this list of conditions and the following disclaimer.
4765668Skris * 2. Redistributions in binary form must reproduce the above copyright
4865668Skris *    notice, this list of conditions and the following disclaimer in the
4965668Skris *    documentation and/or other materials provided with the distribution.
5065668Skris *
5165668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
5265668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
5365668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
5465668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
5565668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
5665668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
5765668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
5865668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
5965668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
6065668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6157429Smarkm */
6257429Smarkm
6357429Smarkm#include "includes.h"
64263970Sdes__RCSID("$FreeBSD$");
6557429Smarkm
66162852Sdes#include <sys/types.h>
67162852Sdes#include <sys/ioctl.h>
68162852Sdes#include <sys/param.h>
69162852Sdes#ifdef HAVE_SYS_STAT_H
70162852Sdes# include <sys/stat.h>
71162852Sdes#endif
72162852Sdes#ifdef HAVE_SYS_TIME_H
73162852Sdes# include <sys/time.h>
74162852Sdes#endif
75162852Sdes#include <sys/socket.h>
76162852Sdes
77162852Sdes#include <ctype.h>
78162852Sdes#include <errno.h>
79162852Sdes#ifdef HAVE_PATHS_H
80162852Sdes#include <paths.h>
81162852Sdes#endif
82162852Sdes#include <signal.h>
83162852Sdes#include <stdarg.h>
84162852Sdes#include <stdio.h>
85162852Sdes#include <stdlib.h>
86162852Sdes#include <string.h>
87162852Sdes#include <termios.h>
88162852Sdes#include <pwd.h>
89162852Sdes#include <unistd.h>
90162852Sdes
91181111Sdes#include "openbsd-compat/sys-queue.h"
92162852Sdes#include "xmalloc.h"
9376259Sgreen#include "ssh.h"
9476259Sgreen#include "ssh1.h"
9576259Sgreen#include "ssh2.h"
9657429Smarkm#include "packet.h"
9757429Smarkm#include "buffer.h"
9860573Skris#include "compat.h"
9960573Skris#include "channels.h"
10060573Skris#include "dispatch.h"
10176259Sgreen#include "key.h"
102162852Sdes#include "cipher.h"
10376259Sgreen#include "kex.h"
10476259Sgreen#include "log.h"
10576259Sgreen#include "readconf.h"
10676259Sgreen#include "clientloop.h"
107157016Sdes#include "sshconnect.h"
10876259Sgreen#include "authfd.h"
10976259Sgreen#include "atomicio.h"
110137015Sdes#include "sshpty.h"
11176259Sgreen#include "misc.h"
112137015Sdes#include "match.h"
113137015Sdes#include "msg.h"
114197679Sdes#include "roaming.h"
11560573Skris
11669587Sgreen/* import options */
11768700Sgreenextern Options options;
11868700Sgreen
11957429Smarkm/* Flag indicating that stdin should be redirected from /dev/null. */
12057429Smarkmextern int stdin_null_flag;
12157429Smarkm
122126274Sdes/* Flag indicating that no shell has been requested */
123126274Sdesextern int no_shell_flag;
124126274Sdes
125137015Sdes/* Control socket */
126204917Sdesextern int muxserver_sock; /* XXX use mux_client_cleanup() instead */
127137015Sdes
12857429Smarkm/*
12957429Smarkm * Name of the host we are connecting to.  This is the name given on the
13057429Smarkm * command line, or the HostName specified for the user-supplied name in a
13157429Smarkm * configuration file.
13257429Smarkm */
13357429Smarkmextern char *host;
13457429Smarkm
13557429Smarkm/*
13657429Smarkm * Flag to indicate that we have received a window change signal which has
13757429Smarkm * not yet been processed.  This will cause a message indicating the new
13857429Smarkm * window size to be sent to the server a little later.  This is volatile
13957429Smarkm * because this is updated in a signal handler.
14057429Smarkm */
14192555Sdesstatic volatile sig_atomic_t received_window_change_signal = 0;
14292555Sdesstatic volatile sig_atomic_t received_signal = 0;
14357429Smarkm
144157016Sdes/* Flag indicating whether the user's terminal is in non-blocking mode. */
14557429Smarkmstatic int in_non_blocking_mode = 0;
14657429Smarkm
147215116Sdes/* Time when backgrounded control master using ControlPersist should exit */
148215116Sdesstatic time_t control_persist_exit_time = 0;
149215116Sdes
15057429Smarkm/* Common data for the client loop code. */
151204917Sdesvolatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */
152181111Sdesstatic int escape_char1;	/* Escape character. (proto1 only) */
153181111Sdesstatic int escape_pending1;	/* Last character was an escape (proto1 only) */
15457429Smarkmstatic int last_was_cr;		/* Last character was a newline. */
155181111Sdesstatic int exit_status;		/* Used to store the command exit status. */
156181111Sdesstatic int stdin_eof;		/* EOF has been encountered on stderr. */
15757429Smarkmstatic Buffer stdin_buffer;	/* Buffer for stdin data. */
15857429Smarkmstatic Buffer stdout_buffer;	/* Buffer for stdout data. */
15957429Smarkmstatic Buffer stderr_buffer;	/* Buffer for stderr data. */
160215116Sdesstatic u_int buffer_high;	/* Soft max buffer size. */
16157429Smarkmstatic int connection_in;	/* Connection to server (input). */
16257429Smarkmstatic int connection_out;	/* Connection to server (output). */
16376259Sgreenstatic int need_rekeying;	/* Set to non-zero if rekeying is requested. */
164215116Sdesstatic int session_closed;	/* In SSH2: login session closed. */
165215116Sdesstatic int x11_refuse_time;	/* If >0, refuse x11 opens after this time. */
16657429Smarkm
16792555Sdesstatic void client_init_dispatch(void);
16860573Skrisint	session_ident = -1;
16960573Skris
170204917Sdesint	session_resumed = 0;
171204917Sdes
172181111Sdes/* Track escape per proto2 channel */
173181111Sdesstruct escape_filter_ctx {
174181111Sdes	int escape_pending;
175181111Sdes	int escape_char;
176137015Sdes};
177137015Sdes
178181111Sdes/* Context for channel confirmation replies */
179181111Sdesstruct channel_reply_ctx {
180181111Sdes	const char *request_type;
181247485Sdes	int id;
182247485Sdes	enum confirm_action action;
183181111Sdes};
184181111Sdes
185181111Sdes/* Global request success/failure callbacks */
186181111Sdesstruct global_confirm {
187181111Sdes	TAILQ_ENTRY(global_confirm) entry;
188181111Sdes	global_confirm_cb *cb;
189181111Sdes	void *ctx;
190181111Sdes	int ref_count;
191181111Sdes};
192181111SdesTAILQ_HEAD(global_confirms, global_confirm);
193181111Sdesstatic struct global_confirms global_confirms =
194181111Sdes    TAILQ_HEAD_INITIALIZER(global_confirms);
195181111Sdes
19676259Sgreen/*XXX*/
19776259Sgreenextern Kex *xxx_kex;
19857429Smarkm
199137015Sdesvoid ssh_process_session2_setup(int, int, int, Buffer *);
200137015Sdes
20157429Smarkm/* Restores stdin to blocking mode. */
20257429Smarkm
20392555Sdesstatic void
20476259Sgreenleave_non_blocking(void)
20557429Smarkm{
20657429Smarkm	if (in_non_blocking_mode) {
207137015Sdes		unset_nonblock(fileno(stdin));
20857429Smarkm		in_non_blocking_mode = 0;
20957429Smarkm	}
21057429Smarkm}
21157429Smarkm
21257429Smarkm/* Puts stdin terminal in non-blocking mode. */
21357429Smarkm
21492555Sdesstatic void
21576259Sgreenenter_non_blocking(void)
21657429Smarkm{
21757429Smarkm	in_non_blocking_mode = 1;
218137015Sdes	set_nonblock(fileno(stdin));
21957429Smarkm}
22057429Smarkm
22157429Smarkm/*
22257429Smarkm * Signal handler for the window change signal (SIGWINCH).  This just sets a
22357429Smarkm * flag indicating that the window has changed.
22457429Smarkm */
225162852Sdes/*ARGSUSED */
22692555Sdesstatic void
22757429Smarkmwindow_change_handler(int sig)
22857429Smarkm{
22957429Smarkm	received_window_change_signal = 1;
23057429Smarkm	signal(SIGWINCH, window_change_handler);
23157429Smarkm}
23257429Smarkm
23357429Smarkm/*
23457429Smarkm * Signal handler for signals that cause the program to terminate.  These
23557429Smarkm * signals must be trapped to restore terminal modes.
23657429Smarkm */
237162852Sdes/*ARGSUSED */
23892555Sdesstatic void
23957429Smarkmsignal_handler(int sig)
24057429Smarkm{
24192555Sdes	received_signal = sig;
24292555Sdes	quit_pending = 1;
24357429Smarkm}
24457429Smarkm
24557429Smarkm/*
24657429Smarkm * Returns current time in seconds from Jan 1, 1970 with the maximum
24757429Smarkm * available resolution.
24857429Smarkm */
24957429Smarkm
25092555Sdesstatic double
25176259Sgreenget_current_time(void)
25257429Smarkm{
25357429Smarkm	struct timeval tv;
25457429Smarkm	gettimeofday(&tv, NULL);
25557429Smarkm	return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
25657429Smarkm}
25757429Smarkm
258215116Sdes/*
259215116Sdes * Sets control_persist_exit_time to the absolute time when the
260215116Sdes * backgrounded control master should exit due to expiry of the
261215116Sdes * ControlPersist timeout.  Sets it to 0 if we are not a backgrounded
262215116Sdes * control master process, or if there is no ControlPersist timeout.
263215116Sdes */
264215116Sdesstatic void
265215116Sdesset_control_persist_exit_time(void)
266215116Sdes{
267215116Sdes	if (muxserver_sock == -1 || !options.control_persist
268247485Sdes	    || options.control_persist_timeout == 0) {
269215116Sdes		/* not using a ControlPersist timeout */
270215116Sdes		control_persist_exit_time = 0;
271247485Sdes	} else if (channel_still_open()) {
272215116Sdes		/* some client connections are still open */
273215116Sdes		if (control_persist_exit_time > 0)
274215116Sdes			debug2("%s: cancel scheduled exit", __func__);
275215116Sdes		control_persist_exit_time = 0;
276215116Sdes	} else if (control_persist_exit_time <= 0) {
277215116Sdes		/* a client connection has recently closed */
278263970Sdes		control_persist_exit_time = monotime() +
279215116Sdes			(time_t)options.control_persist_timeout;
280215116Sdes		debug2("%s: schedule exit in %d seconds", __func__,
281215116Sdes		    options.control_persist_timeout);
282215116Sdes	}
283215116Sdes	/* else we are already counting down to the timeout */
284215116Sdes}
285215116Sdes
286247485Sdes#define SSH_X11_VALID_DISPLAY_CHARS ":/.-_"
287247485Sdesstatic int
288247485Sdesclient_x11_display_valid(const char *display)
289247485Sdes{
290247485Sdes	size_t i, dlen;
291247485Sdes
292247485Sdes	dlen = strlen(display);
293247485Sdes	for (i = 0; i < dlen; i++) {
294263970Sdes		if (!isalnum((u_char)display[i]) &&
295247485Sdes		    strchr(SSH_X11_VALID_DISPLAY_CHARS, display[i]) == NULL) {
296247485Sdes			debug("Invalid character '%c' in DISPLAY", display[i]);
297247485Sdes			return 0;
298247485Sdes		}
299247485Sdes	}
300247485Sdes	return 1;
301247485Sdes}
302247485Sdes
303149749Sdes#define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1"
304149749Sdesvoid
305149749Sdesclient_x11_get_proto(const char *display, const char *xauth_path,
306215116Sdes    u_int trusted, u_int timeout, char **_proto, char **_data)
307149749Sdes{
308149749Sdes	char cmd[1024];
309149749Sdes	char line[512];
310149749Sdes	char xdisplay[512];
311149749Sdes	static char proto[512], data[512];
312149749Sdes	FILE *f;
313149749Sdes	int got_data = 0, generated = 0, do_unlink = 0, i;
314149749Sdes	char *xauthdir, *xauthfile;
315149749Sdes	struct stat st;
316215116Sdes	u_int now;
317149749Sdes
318149749Sdes	xauthdir = xauthfile = NULL;
319149749Sdes	*_proto = proto;
320149749Sdes	*_data = data;
321149749Sdes	proto[0] = data[0] = '\0';
322149749Sdes
323149749Sdes	if (xauth_path == NULL ||(stat(xauth_path, &st) == -1)) {
324149749Sdes		debug("No xauth program.");
325247485Sdes	} else if (!client_x11_display_valid(display)) {
326247485Sdes		logit("DISPLAY '%s' invalid, falling back to fake xauth data",
327247485Sdes		    display);
328149749Sdes	} else {
329149749Sdes		if (display == NULL) {
330149749Sdes			debug("x11_get_proto: DISPLAY not set");
331149749Sdes			return;
332149749Sdes		}
333149749Sdes		/*
334149749Sdes		 * Handle FamilyLocal case where $DISPLAY does
335149749Sdes		 * not match an authorization entry.  For this we
336149749Sdes		 * just try "xauth list unix:displaynum.screennum".
337149749Sdes		 * XXX: "localhost" match to determine FamilyLocal
338149749Sdes		 *      is not perfect.
339149749Sdes		 */
340149749Sdes		if (strncmp(display, "localhost:", 10) == 0) {
341149749Sdes			snprintf(xdisplay, sizeof(xdisplay), "unix:%s",
342149749Sdes			    display + 10);
343149749Sdes			display = xdisplay;
344149749Sdes		}
345149749Sdes		if (trusted == 0) {
346149749Sdes			xauthdir = xmalloc(MAXPATHLEN);
347149749Sdes			xauthfile = xmalloc(MAXPATHLEN);
348221420Sdes			mktemp_proto(xauthdir, MAXPATHLEN);
349149749Sdes			if (mkdtemp(xauthdir) != NULL) {
350149749Sdes				do_unlink = 1;
351149749Sdes				snprintf(xauthfile, MAXPATHLEN, "%s/xauthfile",
352149749Sdes				    xauthdir);
353149749Sdes				snprintf(cmd, sizeof(cmd),
354149749Sdes				    "%s -f %s generate %s " SSH_X11_PROTO
355215116Sdes				    " untrusted timeout %u 2>" _PATH_DEVNULL,
356215116Sdes				    xauth_path, xauthfile, display, timeout);
357149749Sdes				debug2("x11_get_proto: %s", cmd);
358149749Sdes				if (system(cmd) == 0)
359149749Sdes					generated = 1;
360215116Sdes				if (x11_refuse_time == 0) {
361263970Sdes					now = monotime() + 1;
362215116Sdes					if (UINT_MAX - timeout < now)
363215116Sdes						x11_refuse_time = UINT_MAX;
364215116Sdes					else
365215116Sdes						x11_refuse_time = now + timeout;
366215116Sdes				}
367149749Sdes			}
368149749Sdes		}
369181111Sdes
370181111Sdes		/*
371181111Sdes		 * When in untrusted mode, we read the cookie only if it was
372181111Sdes		 * successfully generated as an untrusted one in the step
373181111Sdes		 * above.
374181111Sdes		 */
375181111Sdes		if (trusted || generated) {
376181111Sdes			snprintf(cmd, sizeof(cmd),
377181111Sdes			    "%s %s%s list %s 2>" _PATH_DEVNULL,
378181111Sdes			    xauth_path,
379181111Sdes			    generated ? "-f " : "" ,
380181111Sdes			    generated ? xauthfile : "",
381181111Sdes			    display);
382181111Sdes			debug2("x11_get_proto: %s", cmd);
383181111Sdes			f = popen(cmd, "r");
384181111Sdes			if (f && fgets(line, sizeof(line), f) &&
385181111Sdes			    sscanf(line, "%*s %511s %511s", proto, data) == 2)
386181111Sdes				got_data = 1;
387181111Sdes			if (f)
388181111Sdes				pclose(f);
389181111Sdes		} else
390181111Sdes			error("Warning: untrusted X11 forwarding setup failed: "
391181111Sdes			    "xauth key data not generated");
392149749Sdes	}
393149749Sdes
394149749Sdes	if (do_unlink) {
395149749Sdes		unlink(xauthfile);
396149749Sdes		rmdir(xauthdir);
397149749Sdes	}
398263970Sdes	free(xauthdir);
399263970Sdes	free(xauthfile);
400149749Sdes
401149749Sdes	/*
402149749Sdes	 * If we didn't get authentication data, just make up some
403149749Sdes	 * data.  The forwarding code will check the validity of the
404149749Sdes	 * response anyway, and substitute this data.  The X11
405149749Sdes	 * server, however, will ignore this fake data and use
406149749Sdes	 * whatever authentication mechanisms it was using otherwise
407149749Sdes	 * for the local connection.
408149749Sdes	 */
409149749Sdes	if (!got_data) {
410149749Sdes		u_int32_t rnd = 0;
411149749Sdes
412149749Sdes		logit("Warning: No xauth data; "
413149749Sdes		    "using fake authentication data for X11 forwarding.");
414149749Sdes		strlcpy(proto, SSH_X11_PROTO, sizeof proto);
415149749Sdes		for (i = 0; i < 16; i++) {
416149749Sdes			if (i % 4 == 0)
417149749Sdes				rnd = arc4random();
418149749Sdes			snprintf(data + 2 * i, sizeof data - 2 * i, "%02x",
419149749Sdes			    rnd & 0xff);
420149749Sdes			rnd >>= 8;
421149749Sdes		}
422149749Sdes	}
423149749Sdes}
424149749Sdes
42557429Smarkm/*
42657429Smarkm * This is called when the interactive is entered.  This checks if there is
42757429Smarkm * an EOF coming on stdin.  We must check this explicitly, as select() does
42857429Smarkm * not appear to wake up when redirecting from /dev/null.
42957429Smarkm */
43057429Smarkm
43192555Sdesstatic void
43276259Sgreenclient_check_initial_eof_on_stdin(void)
43357429Smarkm{
43457429Smarkm	int len;
43557429Smarkm	char buf[1];
43657429Smarkm
43757429Smarkm	/*
43857429Smarkm	 * If standard input is to be "redirected from /dev/null", we simply
43957429Smarkm	 * mark that we have seen an EOF and send an EOF message to the
44057429Smarkm	 * server. Otherwise, we try to read a single character; it appears
44157429Smarkm	 * that for some files, such /dev/null, select() never wakes up for
44257429Smarkm	 * read for this descriptor, which means that we never get EOF.  This
44357429Smarkm	 * way we will get the EOF if stdin comes from /dev/null or similar.
44457429Smarkm	 */
44557429Smarkm	if (stdin_null_flag) {
44657429Smarkm		/* Fake EOF on stdin. */
44757429Smarkm		debug("Sending eof.");
44857429Smarkm		stdin_eof = 1;
44957429Smarkm		packet_start(SSH_CMSG_EOF);
45057429Smarkm		packet_send();
45157429Smarkm	} else {
45257429Smarkm		enter_non_blocking();
45357429Smarkm
45457429Smarkm		/* Check for immediate EOF on stdin. */
45557429Smarkm		len = read(fileno(stdin), buf, 1);
45657429Smarkm		if (len == 0) {
457181111Sdes			/*
458181111Sdes			 * EOF.  Record that we have seen it and send
459181111Sdes			 * EOF to server.
460181111Sdes			 */
46157429Smarkm			debug("Sending eof.");
46257429Smarkm			stdin_eof = 1;
46357429Smarkm			packet_start(SSH_CMSG_EOF);
46457429Smarkm			packet_send();
46557429Smarkm		} else if (len > 0) {
46657429Smarkm			/*
46757429Smarkm			 * Got data.  We must store the data in the buffer,
46857429Smarkm			 * and also process it as an escape character if
46957429Smarkm			 * appropriate.
47057429Smarkm			 */
471181111Sdes			if ((u_char) buf[0] == escape_char1)
472181111Sdes				escape_pending1 = 1;
47376259Sgreen			else
47457429Smarkm				buffer_append(&stdin_buffer, buf, 1);
47557429Smarkm		}
47657429Smarkm		leave_non_blocking();
47757429Smarkm	}
47857429Smarkm}
47957429Smarkm
48057429Smarkm
48157429Smarkm/*
48257429Smarkm * Make packets from buffered stdin data, and buffer them for sending to the
48357429Smarkm * connection.
48457429Smarkm */
48557429Smarkm
48692555Sdesstatic void
48776259Sgreenclient_make_packets_from_stdin_data(void)
48857429Smarkm{
48976259Sgreen	u_int len;
49057429Smarkm
49157429Smarkm	/* Send buffered stdin data to the server. */
49257429Smarkm	while (buffer_len(&stdin_buffer) > 0 &&
49392555Sdes	    packet_not_very_much_data_to_write()) {
49457429Smarkm		len = buffer_len(&stdin_buffer);
49557429Smarkm		/* Keep the packets at reasonable size. */
49657429Smarkm		if (len > packet_get_maxsize())
49757429Smarkm			len = packet_get_maxsize();
49857429Smarkm		packet_start(SSH_CMSG_STDIN_DATA);
49957429Smarkm		packet_put_string(buffer_ptr(&stdin_buffer), len);
50057429Smarkm		packet_send();
50157429Smarkm		buffer_consume(&stdin_buffer, len);
50257429Smarkm		/* If we have a pending EOF, send it now. */
50357429Smarkm		if (stdin_eof && buffer_len(&stdin_buffer) == 0) {
50457429Smarkm			packet_start(SSH_CMSG_EOF);
50557429Smarkm			packet_send();
50657429Smarkm		}
50757429Smarkm	}
50857429Smarkm}
50957429Smarkm
51057429Smarkm/*
51157429Smarkm * Checks if the client window has changed, and sends a packet about it to
51257429Smarkm * the server if so.  The actual change is detected elsewhere (by a software
51357429Smarkm * interrupt on Unix); this just checks the flag and sends a message if
51457429Smarkm * appropriate.
51557429Smarkm */
51657429Smarkm
51792555Sdesstatic void
51876259Sgreenclient_check_window_change(void)
51957429Smarkm{
52060573Skris	struct winsize ws;
52157429Smarkm
52260573Skris	if (! received_window_change_signal)
52360573Skris		return;
52460573Skris	/** XXX race */
52560573Skris	received_window_change_signal = 0;
52657429Smarkm
52769587Sgreen	debug2("client_check_window_change: changed");
52860573Skris
52960573Skris	if (compat20) {
530137015Sdes		channel_send_window_changes();
53160573Skris	} else {
532137015Sdes		if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
533137015Sdes			return;
53460573Skris		packet_start(SSH_CMSG_WINDOW_SIZE);
535162852Sdes		packet_put_int((u_int)ws.ws_row);
536162852Sdes		packet_put_int((u_int)ws.ws_col);
537162852Sdes		packet_put_int((u_int)ws.ws_xpixel);
538162852Sdes		packet_put_int((u_int)ws.ws_ypixel);
53960573Skris		packet_send();
54057429Smarkm	}
54157429Smarkm}
54257429Smarkm
543126274Sdesstatic void
544126274Sdesclient_global_request_reply(int type, u_int32_t seq, void *ctxt)
545126274Sdes{
546181111Sdes	struct global_confirm *gc;
547181111Sdes
548181111Sdes	if ((gc = TAILQ_FIRST(&global_confirms)) == NULL)
549181111Sdes		return;
550181111Sdes	if (gc->cb != NULL)
551181111Sdes		gc->cb(type, seq, gc->ctx);
552181111Sdes	if (--gc->ref_count <= 0) {
553181111Sdes		TAILQ_REMOVE(&global_confirms, gc, entry);
554263970Sdes		explicit_bzero(gc, sizeof(*gc));
555263970Sdes		free(gc);
556181111Sdes	}
557181111Sdes
558197679Sdes	packet_set_alive_timeouts(0);
559126274Sdes}
560126274Sdes
561126274Sdesstatic void
562126274Sdesserver_alive_check(void)
563126274Sdes{
564197679Sdes	if (packet_inc_alive_timeouts() > options.server_alive_count_max) {
565221420Sdes		logit("Timeout, server %s not responding.", host);
566164146Sdes		cleanup_exit(255);
567164146Sdes	}
568126274Sdes	packet_start(SSH2_MSG_GLOBAL_REQUEST);
569126274Sdes	packet_put_cstring("keepalive@openssh.com");
570126274Sdes	packet_put_char(1);     /* boolean: want reply */
571126274Sdes	packet_send();
572181111Sdes	/* Insert an empty placeholder to maintain ordering */
573181111Sdes	client_register_global_confirm(NULL, NULL);
574126274Sdes}
575126274Sdes
57657429Smarkm/*
57757429Smarkm * Waits until the client can do something (some data becomes available on
57857429Smarkm * one of the file descriptors).
57957429Smarkm */
58092555Sdesstatic void
58176259Sgreenclient_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
582137015Sdes    int *maxfdp, u_int *nallocp, int rekeying)
58357429Smarkm{
584126274Sdes	struct timeval tv, *tvp;
585215116Sdes	int timeout_secs;
586263970Sdes	time_t minwait_secs = 0, server_alive_time = 0, now = monotime();
587126274Sdes	int ret;
588126274Sdes
58976259Sgreen	/* Add any selections by the channel mechanism. */
590247485Sdes	channel_prepare_select(readsetp, writesetp, maxfdp, nallocp,
591247485Sdes	    &minwait_secs, rekeying);
59257429Smarkm
59360573Skris	if (!compat20) {
59460573Skris		/* Read from the connection, unless our buffers are full. */
59560573Skris		if (buffer_len(&stdout_buffer) < buffer_high &&
59660573Skris		    buffer_len(&stderr_buffer) < buffer_high &&
59760573Skris		    channel_not_very_much_buffered_data())
59876259Sgreen			FD_SET(connection_in, *readsetp);
59960573Skris		/*
60060573Skris		 * Read from stdin, unless we have seen EOF or have very much
60160573Skris		 * buffered data to send to the server.
60260573Skris		 */
60360573Skris		if (!stdin_eof && packet_not_very_much_data_to_write())
60476259Sgreen			FD_SET(fileno(stdin), *readsetp);
60560573Skris
60660573Skris		/* Select stdout/stderr if have data in buffer. */
60760573Skris		if (buffer_len(&stdout_buffer) > 0)
60876259Sgreen			FD_SET(fileno(stdout), *writesetp);
60960573Skris		if (buffer_len(&stderr_buffer) > 0)
61076259Sgreen			FD_SET(fileno(stderr), *writesetp);
61160573Skris	} else {
61292555Sdes		/* channel_prepare_select could have closed the last channel */
61392555Sdes		if (session_closed && !channel_still_open() &&
61492555Sdes		    !packet_have_data_to_write()) {
61592555Sdes			/* clear mask since we did not call select() */
61692555Sdes			memset(*readsetp, 0, *nallocp);
61792555Sdes			memset(*writesetp, 0, *nallocp);
61892555Sdes			return;
61992555Sdes		} else {
62092555Sdes			FD_SET(connection_in, *readsetp);
62192555Sdes		}
62260573Skris	}
62357429Smarkm
62457429Smarkm	/* Select server connection if have data to write to the server. */
62557429Smarkm	if (packet_have_data_to_write())
62676259Sgreen		FD_SET(connection_out, *writesetp);
62757429Smarkm
62857429Smarkm	/*
62957429Smarkm	 * Wait for something to happen.  This will suspend the process until
63057429Smarkm	 * some selected descriptor can be read, written, or has some other
631215116Sdes	 * event pending, or a timeout expires.
63257429Smarkm	 */
63357429Smarkm
634215116Sdes	timeout_secs = INT_MAX; /* we use INT_MAX to mean no timeout */
635263970Sdes	if (options.server_alive_interval > 0 && compat20) {
636215116Sdes		timeout_secs = options.server_alive_interval;
637263970Sdes		server_alive_time = now + options.server_alive_interval;
638263970Sdes	}
639263970Sdes	if (options.rekey_interval > 0 && compat20 && !rekeying)
640263970Sdes		timeout_secs = MIN(timeout_secs, packet_get_rekey_timeout());
641215116Sdes	set_control_persist_exit_time();
642215116Sdes	if (control_persist_exit_time > 0) {
643215116Sdes		timeout_secs = MIN(timeout_secs,
644263970Sdes			control_persist_exit_time - now);
645215116Sdes		if (timeout_secs < 0)
646215116Sdes			timeout_secs = 0;
647215116Sdes	}
648247485Sdes	if (minwait_secs != 0)
649247485Sdes		timeout_secs = MIN(timeout_secs, (int)minwait_secs);
650215116Sdes	if (timeout_secs == INT_MAX)
651126274Sdes		tvp = NULL;
652137015Sdes	else {
653215116Sdes		tv.tv_sec = timeout_secs;
654126274Sdes		tv.tv_usec = 0;
655126274Sdes		tvp = &tv;
656126274Sdes	}
657215116Sdes
658126274Sdes	ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
659126274Sdes	if (ret < 0) {
66057429Smarkm		char buf[100];
66176259Sgreen
66276259Sgreen		/*
66376259Sgreen		 * We have to clear the select masks, because we return.
66476259Sgreen		 * We have to return, because the mainloop checks for the flags
66576259Sgreen		 * set by the signal handlers.
66676259Sgreen		 */
66792555Sdes		memset(*readsetp, 0, *nallocp);
66892555Sdes		memset(*writesetp, 0, *nallocp);
66976259Sgreen
67057429Smarkm		if (errno == EINTR)
67157429Smarkm			return;
67257429Smarkm		/* Note: we might still have data in the buffers. */
67357429Smarkm		snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
67457429Smarkm		buffer_append(&stderr_buffer, buf, strlen(buf));
67557429Smarkm		quit_pending = 1;
676263970Sdes	} else if (ret == 0) {
677263970Sdes		/*
678263970Sdes		 * Timeout.  Could have been either keepalive or rekeying.
679263970Sdes		 * Keepalive we check here, rekeying is checked in clientloop.
680263970Sdes		 */
681263970Sdes		if (server_alive_time != 0 && server_alive_time <= monotime())
682263970Sdes			server_alive_check();
683263970Sdes	}
684263970Sdes
68557429Smarkm}
68657429Smarkm
68792555Sdesstatic void
68865668Skrisclient_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr)
68957429Smarkm{
69057429Smarkm	/* Flush stdout and stderr buffers. */
69165668Skris	if (buffer_len(bout) > 0)
692181111Sdes		atomicio(vwrite, fileno(stdout), buffer_ptr(bout),
693181111Sdes		    buffer_len(bout));
69465668Skris	if (buffer_len(berr) > 0)
695181111Sdes		atomicio(vwrite, fileno(stderr), buffer_ptr(berr),
696181111Sdes		    buffer_len(berr));
69757429Smarkm
698247485Sdes	leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
69957429Smarkm
70057429Smarkm	/*
70157429Smarkm	 * Free (and clear) the buffer to reduce the amount of data that gets
70257429Smarkm	 * written to swap.
70357429Smarkm	 */
70465668Skris	buffer_free(bin);
70565668Skris	buffer_free(bout);
70665668Skris	buffer_free(berr);
70757429Smarkm
70857429Smarkm	/* Send the suspend signal to the program itself. */
70957429Smarkm	kill(getpid(), SIGTSTP);
71057429Smarkm
711146998Sdes	/* Reset window sizes in case they have changed */
712146998Sdes	received_window_change_signal = 1;
71357429Smarkm
71457429Smarkm	/* OK, we have been continued by the user. Reinitialize buffers. */
71565668Skris	buffer_init(bin);
71665668Skris	buffer_init(bout);
71765668Skris	buffer_init(berr);
71857429Smarkm
719247485Sdes	enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
72057429Smarkm}
72157429Smarkm
72292555Sdesstatic void
723162852Sdesclient_process_net_input(fd_set *readset)
72457429Smarkm{
725197679Sdes	int len, cont = 0;
726197679Sdes	char buf[SSH_IOBUFSZ];
72757429Smarkm
72857429Smarkm	/*
72957429Smarkm	 * Read input from the server, and add any such data to the buffer of
73057429Smarkm	 * the packet subsystem.
73157429Smarkm	 */
73257429Smarkm	if (FD_ISSET(connection_in, readset)) {
73357429Smarkm		/* Read as much as possible. */
734197679Sdes		len = roaming_read(connection_in, buf, sizeof(buf), &cont);
735197679Sdes		if (len == 0 && cont == 0) {
736181111Sdes			/*
737181111Sdes			 * Received EOF.  The remote host has closed the
738181111Sdes			 * connection.
739181111Sdes			 */
740181111Sdes			snprintf(buf, sizeof buf,
741181111Sdes			    "Connection to %.300s closed by remote host.\r\n",
742181111Sdes			    host);
74357429Smarkm			buffer_append(&stderr_buffer, buf, strlen(buf));
74457429Smarkm			quit_pending = 1;
74557429Smarkm			return;
74657429Smarkm		}
74757429Smarkm		/*
74857429Smarkm		 * There is a kernel bug on Solaris that causes select to
74957429Smarkm		 * sometimes wake up even though there is no data available.
75057429Smarkm		 */
751181111Sdes		if (len < 0 &&
752181111Sdes		    (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK))
75357429Smarkm			len = 0;
75457429Smarkm
75557429Smarkm		if (len < 0) {
756181111Sdes			/*
757181111Sdes			 * An error has encountered.  Perhaps there is a
758181111Sdes			 * network problem.
759181111Sdes			 */
760181111Sdes			snprintf(buf, sizeof buf,
761181111Sdes			    "Read from remote host %.300s: %.100s\r\n",
762181111Sdes			    host, strerror(errno));
76357429Smarkm			buffer_append(&stderr_buffer, buf, strlen(buf));
76457429Smarkm			quit_pending = 1;
76557429Smarkm			return;
76657429Smarkm		}
76757429Smarkm		packet_process_incoming(buf, len);
76857429Smarkm	}
76960573Skris}
77060573Skris
77198675Sdesstatic void
772181111Sdesclient_status_confirm(int type, Channel *c, void *ctx)
773137015Sdes{
774181111Sdes	struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx;
775181111Sdes	char errmsg[256];
776181111Sdes	int tochan;
777137015Sdes
778247485Sdes	/*
779247485Sdes	 * If a TTY was explicitly requested, then a failure to allocate
780247485Sdes	 * one is fatal.
781247485Sdes	 */
782247485Sdes	if (cr->action == CONFIRM_TTY &&
783247485Sdes	    (options.request_tty == REQUEST_TTY_FORCE ||
784247485Sdes	    options.request_tty == REQUEST_TTY_YES))
785247485Sdes		cr->action = CONFIRM_CLOSE;
786247485Sdes
787181111Sdes	/* XXX supress on mux _client_ quietmode */
788181111Sdes	tochan = options.log_level >= SYSLOG_LEVEL_ERROR &&
789204917Sdes	    c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE;
790137015Sdes
791181111Sdes	if (type == SSH2_MSG_CHANNEL_SUCCESS) {
792181111Sdes		debug2("%s request accepted on channel %d",
793181111Sdes		    cr->request_type, c->self);
794181111Sdes	} else if (type == SSH2_MSG_CHANNEL_FAILURE) {
795181111Sdes		if (tochan) {
796181111Sdes			snprintf(errmsg, sizeof(errmsg),
797181111Sdes			    "%s request failed\r\n", cr->request_type);
798181111Sdes		} else {
799181111Sdes			snprintf(errmsg, sizeof(errmsg),
800181111Sdes			    "%s request failed on channel %d",
801181111Sdes			    cr->request_type, c->self);
802181111Sdes		}
803181111Sdes		/* If error occurred on primary session channel, then exit */
804247485Sdes		if (cr->action == CONFIRM_CLOSE && c->self == session_ident)
805181111Sdes			fatal("%s", errmsg);
806247485Sdes		/*
807247485Sdes		 * If error occurred on mux client, append to
808247485Sdes		 * their stderr.
809247485Sdes		 */
810247485Sdes		if (tochan) {
811247485Sdes			buffer_append(&c->extended, errmsg,
812247485Sdes			    strlen(errmsg));
813247485Sdes		} else
814181111Sdes			error("%s", errmsg);
815247485Sdes		if (cr->action == CONFIRM_TTY) {
816247485Sdes			/*
817247485Sdes			 * If a TTY allocation error occurred, then arrange
818247485Sdes			 * for the correct TTY to leave raw mode.
819247485Sdes			 */
820247485Sdes			if (c->self == session_ident)
821247485Sdes				leave_raw_mode(0);
822247485Sdes			else
823247485Sdes				mux_tty_alloc_failed(c);
824247485Sdes		} else if (cr->action == CONFIRM_CLOSE) {
825181111Sdes			chan_read_failed(c);
826181111Sdes			chan_write_failed(c);
827181111Sdes		}
828137015Sdes	}
829263970Sdes	free(cr);
830137015Sdes}
831137015Sdes
832137015Sdesstatic void
833181111Sdesclient_abandon_status_confirm(Channel *c, void *ctx)
834137015Sdes{
835263970Sdes	free(ctx);
836137015Sdes}
837137015Sdes
838247485Sdesvoid
839247485Sdesclient_expect_confirm(int id, const char *request,
840247485Sdes    enum confirm_action action)
841137015Sdes{
842263970Sdes	struct channel_reply_ctx *cr = xcalloc(1, sizeof(*cr));
843137015Sdes
844181111Sdes	cr->request_type = request;
845247485Sdes	cr->action = action;
846137015Sdes
847181111Sdes	channel_register_status_confirm(id, client_status_confirm,
848181111Sdes	    client_abandon_status_confirm, cr);
849181111Sdes}
850137015Sdes
851181111Sdesvoid
852181111Sdesclient_register_global_confirm(global_confirm_cb *cb, void *ctx)
853181111Sdes{
854181111Sdes	struct global_confirm *gc, *last_gc;
855137015Sdes
856181111Sdes	/* Coalesce identical callbacks */
857181111Sdes	last_gc = TAILQ_LAST(&global_confirms, global_confirms);
858181111Sdes	if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) {
859181111Sdes		if (++last_gc->ref_count >= INT_MAX)
860181111Sdes			fatal("%s: last_gc->ref_count = %d",
861181111Sdes			    __func__, last_gc->ref_count);
862146998Sdes		return;
863146998Sdes	}
864146998Sdes
865263970Sdes	gc = xcalloc(1, sizeof(*gc));
866181111Sdes	gc->cb = cb;
867181111Sdes	gc->ctx = ctx;
868181111Sdes	gc->ref_count = 1;
869181111Sdes	TAILQ_INSERT_TAIL(&global_confirms, gc, entry);
870137015Sdes}
871137015Sdes
872137015Sdesstatic void
87398675Sdesprocess_cmdline(void)
87498675Sdes{
87598675Sdes	void (*handler)(int);
876146998Sdes	char *s, *cmd, *cancel_host;
877247485Sdes	int delete = 0, local = 0, remote = 0, dynamic = 0;
878247485Sdes	int cancel_port, ok;
879146998Sdes	Forward fwd;
88098675Sdes
881263970Sdes	memset(&fwd, 0, sizeof(fwd));
882181111Sdes	fwd.listen_host = fwd.connect_host = NULL;
883181111Sdes
884247485Sdes	leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
88598675Sdes	handler = signal(SIGINT, SIG_IGN);
88698675Sdes	cmd = s = read_passphrase("\r\nssh> ", RP_ECHO);
88798675Sdes	if (s == NULL)
88898675Sdes		goto out;
889263970Sdes	while (isspace((u_char)*s))
89098675Sdes		s++;
891137015Sdes	if (*s == '-')
892137015Sdes		s++;	/* Skip cmdline '-', if any */
893137015Sdes	if (*s == '\0')
89498675Sdes		goto out;
895137015Sdes
896137015Sdes	if (*s == 'h' || *s == 'H' || *s == '?') {
897137015Sdes		logit("Commands:");
898162852Sdes		logit("      -L[bind_address:]port:host:hostport    "
899162852Sdes		    "Request local forward");
900162852Sdes		logit("      -R[bind_address:]port:host:hostport    "
901162852Sdes		    "Request remote forward");
902192595Sdes		logit("      -D[bind_address:]port                  "
903192595Sdes		    "Request dynamic forward");
904247485Sdes		logit("      -KL[bind_address:]port                 "
905247485Sdes		    "Cancel local forward");
906162852Sdes		logit("      -KR[bind_address:]port                 "
907162852Sdes		    "Cancel remote forward");
908247485Sdes		logit("      -KD[bind_address:]port                 "
909247485Sdes		    "Cancel dynamic forward");
910157016Sdes		if (!options.permit_local_command)
911157016Sdes			goto out;
912162852Sdes		logit("      !args                                  "
913162852Sdes		    "Execute local command");
914137015Sdes		goto out;
915137015Sdes	}
916137015Sdes
917157016Sdes	if (*s == '!' && options.permit_local_command) {
918157016Sdes		s++;
919157016Sdes		ssh_local_cmd(s);
920157016Sdes		goto out;
921157016Sdes	}
922157016Sdes
923137015Sdes	if (*s == 'K') {
924137015Sdes		delete = 1;
925137015Sdes		s++;
926137015Sdes	}
927192595Sdes	if (*s == 'L')
928192595Sdes		local = 1;
929192595Sdes	else if (*s == 'R')
930192595Sdes		remote = 1;
931192595Sdes	else if (*s == 'D')
932192595Sdes		dynamic = 1;
933192595Sdes	else {
934124208Sdes		logit("Invalid command.");
93598675Sdes		goto out;
93698675Sdes	}
937192595Sdes
938247485Sdes	if (delete && !compat20) {
939124208Sdes		logit("Not supported for SSH protocol version 1.");
94098675Sdes		goto out;
94198675Sdes	}
942137015Sdes
943263970Sdes	while (isspace((u_char)*++s))
944181111Sdes		;
94598675Sdes
946204917Sdes	/* XXX update list of forwards in options */
947137015Sdes	if (delete) {
948146998Sdes		cancel_port = 0;
949146998Sdes		cancel_host = hpdelim(&s);	/* may be NULL */
950146998Sdes		if (s != NULL) {
951146998Sdes			cancel_port = a2port(s);
952146998Sdes			cancel_host = cleanhostname(cancel_host);
953146998Sdes		} else {
954146998Sdes			cancel_port = a2port(cancel_host);
955146998Sdes			cancel_host = NULL;
95698675Sdes		}
957192595Sdes		if (cancel_port <= 0) {
958146998Sdes			logit("Bad forwarding close port");
959137015Sdes			goto out;
960137015Sdes		}
961247485Sdes		if (remote)
962247485Sdes			ok = channel_request_rforward_cancel(cancel_host,
963247485Sdes			    cancel_port) == 0;
964247485Sdes		else if (dynamic)
965247485Sdes                	ok = channel_cancel_lport_listener(cancel_host,
966247485Sdes			    cancel_port, 0, options.gateway_ports) > 0;
967247485Sdes		else
968247485Sdes                	ok = channel_cancel_lport_listener(cancel_host,
969247485Sdes			    cancel_port, CHANNEL_CANCEL_PORT_STATIC,
970247485Sdes			    options.gateway_ports) > 0;
971247485Sdes		if (!ok) {
972247485Sdes			logit("Unkown port forwarding.");
973247485Sdes			goto out;
974247485Sdes		}
975247485Sdes		logit("Canceled forwarding.");
976137015Sdes	} else {
977192595Sdes		if (!parse_forward(&fwd, s, dynamic, remote)) {
978137015Sdes			logit("Bad forwarding specification.");
979137015Sdes			goto out;
980137015Sdes		}
981192595Sdes		if (local || dynamic) {
982251135Sdes			if (!channel_setup_local_fwd_listener(fwd.listen_host,
983146998Sdes			    fwd.listen_port, fwd.connect_host,
984251135Sdes			    fwd.connect_port, options.gateway_ports)) {
985137015Sdes				logit("Port forwarding failed.");
986137015Sdes				goto out;
987137015Sdes			}
988146998Sdes		} else {
989162852Sdes			if (channel_request_remote_forwarding(fwd.listen_host,
990146998Sdes			    fwd.listen_port, fwd.connect_host,
991162852Sdes			    fwd.connect_port) < 0) {
992162852Sdes				logit("Port forwarding failed.");
993162852Sdes				goto out;
994162852Sdes			}
995146998Sdes		}
996137015Sdes		logit("Forwarding port.");
997137015Sdes	}
998137015Sdes
99998675Sdesout:
100098675Sdes	signal(SIGINT, handler);
1001247485Sdes	enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
1002263970Sdes	free(cmd);
1003263970Sdes	free(fwd.listen_host);
1004263970Sdes	free(fwd.connect_host);
100598675Sdes}
100698675Sdes
1007251135Sdes/* reasons to suppress output of an escape command in help output */
1008251135Sdes#define SUPPRESS_NEVER		0	/* never suppress, always show */
1009251135Sdes#define SUPPRESS_PROTO1		1	/* don't show in protocol 1 sessions */
1010251135Sdes#define SUPPRESS_MUXCLIENT	2	/* don't show in mux client sessions */
1011251135Sdes#define SUPPRESS_MUXMASTER	4	/* don't show in mux master sessions */
1012251135Sdes#define SUPPRESS_SYSLOG		8	/* don't show when logging to syslog */
1013251135Sdesstruct escape_help_text {
1014251135Sdes	const char *cmd;
1015251135Sdes	const char *text;
1016251135Sdes	unsigned int flags;
1017251135Sdes};
1018251135Sdesstatic struct escape_help_text esc_txt[] = {
1019251135Sdes    {".",  "terminate session", SUPPRESS_MUXMASTER},
1020251135Sdes    {".",  "terminate connection (and any multiplexed sessions)",
1021251135Sdes	SUPPRESS_MUXCLIENT},
1022251135Sdes    {"B",  "send a BREAK to the remote system", SUPPRESS_PROTO1},
1023251135Sdes    {"C",  "open a command line", SUPPRESS_MUXCLIENT},
1024251135Sdes    {"R",  "request rekey", SUPPRESS_PROTO1},
1025251135Sdes    {"V/v",  "decrease/increase verbosity (LogLevel)", SUPPRESS_MUXCLIENT},
1026251135Sdes    {"^Z", "suspend ssh", SUPPRESS_MUXCLIENT},
1027251135Sdes    {"#",  "list forwarded connections", SUPPRESS_NEVER},
1028251135Sdes    {"&",  "background ssh (when waiting for connections to terminate)",
1029251135Sdes	SUPPRESS_MUXCLIENT},
1030251135Sdes    {"?", "this message", SUPPRESS_NEVER},
1031251135Sdes};
1032251135Sdes
1033251135Sdesstatic void
1034251135Sdesprint_escape_help(Buffer *b, int escape_char, int protocol2, int mux_client,
1035251135Sdes    int using_stderr)
1036251135Sdes{
1037251135Sdes	unsigned int i, suppress_flags;
1038251135Sdes	char string[1024];
1039251135Sdes
1040251135Sdes	snprintf(string, sizeof string, "%c?\r\n"
1041251135Sdes	    "Supported escape sequences:\r\n", escape_char);
1042251135Sdes	buffer_append(b, string, strlen(string));
1043251135Sdes
1044251135Sdes	suppress_flags = (protocol2 ? 0 : SUPPRESS_PROTO1) |
1045251135Sdes	    (mux_client ? SUPPRESS_MUXCLIENT : 0) |
1046251135Sdes	    (mux_client ? 0 : SUPPRESS_MUXMASTER) |
1047251135Sdes	    (using_stderr ? 0 : SUPPRESS_SYSLOG);
1048251135Sdes
1049251135Sdes	for (i = 0; i < sizeof(esc_txt)/sizeof(esc_txt[0]); i++) {
1050251135Sdes		if (esc_txt[i].flags & suppress_flags)
1051251135Sdes			continue;
1052251135Sdes		snprintf(string, sizeof string, " %c%-3s - %s\r\n",
1053251135Sdes		    escape_char, esc_txt[i].cmd, esc_txt[i].text);
1054251135Sdes		buffer_append(b, string, strlen(string));
1055251135Sdes	}
1056251135Sdes
1057251135Sdes	snprintf(string, sizeof string,
1058251135Sdes	    " %c%c   - send the escape character by typing it twice\r\n"
1059251135Sdes	    "(Note that escapes are only recognized immediately after "
1060251135Sdes	    "newline.)\r\n", escape_char, escape_char);
1061251135Sdes	buffer_append(b, string, strlen(string));
1062251135Sdes}
1063251135Sdes
1064181111Sdes/*
1065181111Sdes * Process the characters one by one, call with c==NULL for proto1 case.
1066181111Sdes */
106792555Sdesstatic int
1068181111Sdesprocess_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr,
1069181111Sdes    char *buf, int len)
107065668Skris{
107165668Skris	char string[1024];
107265668Skris	pid_t pid;
107365668Skris	int bytes = 0;
107476259Sgreen	u_int i;
107576259Sgreen	u_char ch;
107665668Skris	char *s;
1077181111Sdes	int *escape_pendingp, escape_char;
1078181111Sdes	struct escape_filter_ctx *efc;
107965668Skris
1080181111Sdes	if (c == NULL) {
1081181111Sdes		escape_pendingp = &escape_pending1;
1082181111Sdes		escape_char = escape_char1;
1083181111Sdes	} else {
1084181111Sdes		if (c->filter_ctx == NULL)
1085181111Sdes			return 0;
1086181111Sdes		efc = (struct escape_filter_ctx *)c->filter_ctx;
1087181111Sdes		escape_pendingp = &efc->escape_pending;
1088181111Sdes		escape_char = efc->escape_char;
1089181111Sdes	}
1090181111Sdes
1091149749Sdes	if (len <= 0)
1092149749Sdes		return (0);
1093149749Sdes
1094149749Sdes	for (i = 0; i < (u_int)len; i++) {
109565668Skris		/* Get one character at a time. */
109665668Skris		ch = buf[i];
109765668Skris
1098181111Sdes		if (*escape_pendingp) {
109965668Skris			/* We have previously seen an escape character. */
110065668Skris			/* Clear the flag now. */
1101181111Sdes			*escape_pendingp = 0;
110265668Skris
110365668Skris			/* Process the escaped character. */
110465668Skris			switch (ch) {
110565668Skris			case '.':
110665668Skris				/* Terminate the connection. */
1107181111Sdes				snprintf(string, sizeof string, "%c.\r\n",
1108181111Sdes				    escape_char);
110965668Skris				buffer_append(berr, string, strlen(string));
111065668Skris
1111204917Sdes				if (c && c->ctl_chan != -1) {
1112181111Sdes					chan_read_failed(c);
1113181111Sdes					chan_write_failed(c);
1114263970Sdes					if (c->detach_user)
1115263970Sdes						c->detach_user(c->self, NULL);
1116263970Sdes					c->type = SSH_CHANNEL_ABANDONED;
1117263970Sdes					buffer_clear(&c->input);
1118263970Sdes					chan_ibuf_empty(c);
1119181111Sdes					return 0;
1120181111Sdes				} else
1121181111Sdes					quit_pending = 1;
112265668Skris				return -1;
112365668Skris
112465668Skris			case 'Z' - 64:
1125181111Sdes				/* XXX support this for mux clients */
1126204917Sdes				if (c && c->ctl_chan != -1) {
1127251135Sdes					char b[16];
1128181111Sdes noescape:
1129251135Sdes					if (ch == 'Z' - 64)
1130251135Sdes						snprintf(b, sizeof b, "^Z");
1131251135Sdes					else
1132251135Sdes						snprintf(b, sizeof b, "%c", ch);
1133181111Sdes					snprintf(string, sizeof string,
1134251135Sdes					    "%c%s escape not available to "
1135181111Sdes					    "multiplexed sessions\r\n",
1136251135Sdes					    escape_char, b);
1137181111Sdes					buffer_append(berr, string,
1138181111Sdes					    strlen(string));
1139181111Sdes					continue;
1140181111Sdes				}
1141181111Sdes				/* Suspend the program. Inform the user */
1142181111Sdes				snprintf(string, sizeof string,
1143181111Sdes				    "%c^Z [suspend ssh]\r\n", escape_char);
114465668Skris				buffer_append(berr, string, strlen(string));
114565668Skris
114665668Skris				/* Restore terminal modes and suspend. */
114765668Skris				client_suspend_self(bin, bout, berr);
114865668Skris
114965668Skris				/* We have been continued. */
115065668Skris				continue;
115165668Skris
1152124208Sdes			case 'B':
1153124208Sdes				if (compat20) {
1154124208Sdes					snprintf(string, sizeof string,
1155124208Sdes					    "%cB\r\n", escape_char);
1156124208Sdes					buffer_append(berr, string,
1157124208Sdes					    strlen(string));
1158263970Sdes					channel_request_start(c->self,
1159124208Sdes					    "break", 0);
1160124208Sdes					packet_put_int(1000);
1161124208Sdes					packet_send();
1162124208Sdes				}
1163124208Sdes				continue;
1164124208Sdes
116576259Sgreen			case 'R':
116676259Sgreen				if (compat20) {
116776259Sgreen					if (datafellows & SSH_BUG_NOREKEY)
1168181111Sdes						logit("Server does not "
1169181111Sdes						    "support re-keying");
117076259Sgreen					else
117176259Sgreen						need_rekeying = 1;
117276259Sgreen				}
117376259Sgreen				continue;
117476259Sgreen
1175251135Sdes			case 'V':
1176251135Sdes				/* FALLTHROUGH */
1177251135Sdes			case 'v':
1178251135Sdes				if (c && c->ctl_chan != -1)
1179251135Sdes					goto noescape;
1180251135Sdes				if (!log_is_on_stderr()) {
1181251135Sdes					snprintf(string, sizeof string,
1182251135Sdes					    "%c%c [Logging to syslog]\r\n",
1183251135Sdes					     escape_char, ch);
1184251135Sdes					buffer_append(berr, string,
1185251135Sdes					    strlen(string));
1186251135Sdes					continue;
1187251135Sdes				}
1188251135Sdes				if (ch == 'V' && options.log_level >
1189251135Sdes				    SYSLOG_LEVEL_QUIET)
1190251135Sdes					log_change_level(--options.log_level);
1191251135Sdes				if (ch == 'v' && options.log_level <
1192251135Sdes				    SYSLOG_LEVEL_DEBUG3)
1193251135Sdes					log_change_level(++options.log_level);
1194251135Sdes				snprintf(string, sizeof string,
1195251135Sdes				    "%c%c [LogLevel %s]\r\n", escape_char, ch,
1196251135Sdes				    log_level_name(options.log_level));
1197251135Sdes				buffer_append(berr, string, strlen(string));
1198251135Sdes				continue;
1199251135Sdes
120065668Skris			case '&':
1201204917Sdes				if (c && c->ctl_chan != -1)
1202181111Sdes					goto noescape;
120365668Skris				/*
1204181111Sdes				 * Detach the program (continue to serve
1205181111Sdes				 * connections, but put in background and no
1206181111Sdes				 * more new connections).
120765668Skris				 */
120865668Skris				/* Restore tty modes. */
1209247485Sdes				leave_raw_mode(
1210247485Sdes				    options.request_tty == REQUEST_TTY_FORCE);
121165668Skris
121265668Skris				/* Stop listening for new connections. */
121365668Skris				channel_stop_listening();
121465668Skris
121592555Sdes				snprintf(string, sizeof string,
121692555Sdes				    "%c& [backgrounded]\n", escape_char);
121792555Sdes				buffer_append(berr, string, strlen(string));
121865668Skris
121965668Skris				/* Fork into background. */
122065668Skris				pid = fork();
122165668Skris				if (pid < 0) {
122265668Skris					error("fork: %.100s", strerror(errno));
122365668Skris					continue;
122465668Skris				}
122565668Skris				if (pid != 0) {	/* This is the parent. */
122665668Skris					/* The parent just exits. */
122765668Skris					exit(0);
122865668Skris				}
122965668Skris				/* The child continues serving connections. */
123092555Sdes				if (compat20) {
123192555Sdes					buffer_append(bin, "\004", 1);
123292555Sdes					/* fake EOF on stdin */
123392555Sdes					return -1;
123492555Sdes				} else if (!stdin_eof) {
123592555Sdes					/*
1236181111Sdes					 * Sending SSH_CMSG_EOF alone does not
1237181111Sdes					 * always appear to be enough.  So we
1238181111Sdes					 * try to send an EOF character first.
123992555Sdes					 */
124092555Sdes					packet_start(SSH_CMSG_STDIN_DATA);
124192555Sdes					packet_put_string("\004", 1);
124292555Sdes					packet_send();
124392555Sdes					/* Close stdin. */
124492555Sdes					stdin_eof = 1;
124592555Sdes					if (buffer_len(bin) == 0) {
124692555Sdes						packet_start(SSH_CMSG_EOF);
124792555Sdes						packet_send();
124892555Sdes					}
124992555Sdes				}
125092555Sdes				continue;
125165668Skris
125265668Skris			case '?':
1253251135Sdes				print_escape_help(berr, escape_char, compat20,
1254251135Sdes				    (c && c->ctl_chan != -1),
1255251135Sdes				    log_is_on_stderr());
125665668Skris				continue;
125765668Skris
125865668Skris			case '#':
1259181111Sdes				snprintf(string, sizeof string, "%c#\r\n",
1260181111Sdes				    escape_char);
126165668Skris				buffer_append(berr, string, strlen(string));
126265668Skris				s = channel_open_message();
126365668Skris				buffer_append(berr, s, strlen(s));
1264263970Sdes				free(s);
126565668Skris				continue;
126665668Skris
126798675Sdes			case 'C':
1268204917Sdes				if (c && c->ctl_chan != -1)
1269192595Sdes					goto noescape;
127098675Sdes				process_cmdline();
127198675Sdes				continue;
127298675Sdes
127365668Skris			default:
127465668Skris				if (ch != escape_char) {
127565668Skris					buffer_put_char(bin, escape_char);
127665668Skris					bytes++;
127765668Skris				}
127865668Skris				/* Escaped characters fall through here */
127965668Skris				break;
128065668Skris			}
128165668Skris		} else {
128265668Skris			/*
1283181111Sdes			 * The previous character was not an escape char.
1284181111Sdes			 * Check if this is an escape.
128565668Skris			 */
128665668Skris			if (last_was_cr && ch == escape_char) {
1287181111Sdes				/*
1288181111Sdes				 * It is. Set the flag and continue to
1289181111Sdes				 * next character.
1290181111Sdes				 */
1291181111Sdes				*escape_pendingp = 1;
129265668Skris				continue;
129365668Skris			}
129465668Skris		}
129565668Skris
129665668Skris		/*
129765668Skris		 * Normal character.  Record whether it was a newline,
129865668Skris		 * and append it to the buffer.
129965668Skris		 */
130065668Skris		last_was_cr = (ch == '\r' || ch == '\n');
130165668Skris		buffer_put_char(bin, ch);
130265668Skris		bytes++;
130365668Skris	}
130465668Skris	return bytes;
130565668Skris}
130665668Skris
130792555Sdesstatic void
1308162852Sdesclient_process_input(fd_set *readset)
130960573Skris{
131060573Skris	int len;
1311197679Sdes	char buf[SSH_IOBUFSZ];
131260573Skris
131357429Smarkm	/* Read input from stdin. */
131457429Smarkm	if (FD_ISSET(fileno(stdin), readset)) {
131557429Smarkm		/* Read as much as possible. */
131657429Smarkm		len = read(fileno(stdin), buf, sizeof(buf));
1317181111Sdes		if (len < 0 &&
1318181111Sdes		    (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK))
131976259Sgreen			return;		/* we'll try again later */
132057429Smarkm		if (len <= 0) {
132157429Smarkm			/*
132257429Smarkm			 * Received EOF or error.  They are treated
132357429Smarkm			 * similarly, except that an error message is printed
132457429Smarkm			 * if it was an error condition.
132557429Smarkm			 */
132657429Smarkm			if (len < 0) {
1327181111Sdes				snprintf(buf, sizeof buf, "read: %.100s\r\n",
1328181111Sdes				    strerror(errno));
132957429Smarkm				buffer_append(&stderr_buffer, buf, strlen(buf));
133057429Smarkm			}
133157429Smarkm			/* Mark that we have seen EOF. */
133257429Smarkm			stdin_eof = 1;
133357429Smarkm			/*
133457429Smarkm			 * Send an EOF message to the server unless there is
133557429Smarkm			 * data in the buffer.  If there is data in the
133657429Smarkm			 * buffer, no message will be sent now.  Code
133757429Smarkm			 * elsewhere will send the EOF when the buffer
133857429Smarkm			 * becomes empty if stdin_eof is set.
133957429Smarkm			 */
134057429Smarkm			if (buffer_len(&stdin_buffer) == 0) {
134157429Smarkm				packet_start(SSH_CMSG_EOF);
134257429Smarkm				packet_send();
134357429Smarkm			}
1344181111Sdes		} else if (escape_char1 == SSH_ESCAPECHAR_NONE) {
134557429Smarkm			/*
134657429Smarkm			 * Normal successful read, and no escape character.
134757429Smarkm			 * Just append the data to buffer.
134857429Smarkm			 */
134957429Smarkm			buffer_append(&stdin_buffer, buf, len);
135057429Smarkm		} else {
135157429Smarkm			/*
1352181111Sdes			 * Normal, successful read.  But we have an escape
1353181111Sdes			 * character and have to process the characters one
1354181111Sdes			 * by one.
135557429Smarkm			 */
1356181111Sdes			if (process_escapes(NULL, &stdin_buffer,
1357181111Sdes			    &stdout_buffer, &stderr_buffer, buf, len) == -1)
135865668Skris				return;
135957429Smarkm		}
136057429Smarkm	}
136157429Smarkm}
136257429Smarkm
136392555Sdesstatic void
1364162852Sdesclient_process_output(fd_set *writeset)
136557429Smarkm{
136657429Smarkm	int len;
136757429Smarkm	char buf[100];
136857429Smarkm
136957429Smarkm	/* Write buffered output to stdout. */
137057429Smarkm	if (FD_ISSET(fileno(stdout), writeset)) {
137157429Smarkm		/* Write as much data as possible. */
137257429Smarkm		len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
137357429Smarkm		    buffer_len(&stdout_buffer));
137457429Smarkm		if (len <= 0) {
1375181111Sdes			if (errno == EINTR || errno == EAGAIN ||
1376181111Sdes			    errno == EWOULDBLOCK)
137757429Smarkm				len = 0;
137857429Smarkm			else {
137957429Smarkm				/*
138057429Smarkm				 * An error or EOF was encountered.  Put an
138157429Smarkm				 * error message to stderr buffer.
138257429Smarkm				 */
1383181111Sdes				snprintf(buf, sizeof buf,
1384181111Sdes				    "write stdout: %.50s\r\n", strerror(errno));
138557429Smarkm				buffer_append(&stderr_buffer, buf, strlen(buf));
138657429Smarkm				quit_pending = 1;
138757429Smarkm				return;
138857429Smarkm			}
138957429Smarkm		}
139057429Smarkm		/* Consume printed data from the buffer. */
139157429Smarkm		buffer_consume(&stdout_buffer, len);
139257429Smarkm	}
139357429Smarkm	/* Write buffered output to stderr. */
139457429Smarkm	if (FD_ISSET(fileno(stderr), writeset)) {
139557429Smarkm		/* Write as much data as possible. */
139657429Smarkm		len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
139757429Smarkm		    buffer_len(&stderr_buffer));
139857429Smarkm		if (len <= 0) {
1399181111Sdes			if (errno == EINTR || errno == EAGAIN ||
1400181111Sdes			    errno == EWOULDBLOCK)
140157429Smarkm				len = 0;
140257429Smarkm			else {
1403181111Sdes				/*
1404181111Sdes				 * EOF or error, but can't even print
1405181111Sdes				 * error message.
1406181111Sdes				 */
140757429Smarkm				quit_pending = 1;
140857429Smarkm				return;
140957429Smarkm			}
141057429Smarkm		}
141157429Smarkm		/* Consume printed characters from the buffer. */
141257429Smarkm		buffer_consume(&stderr_buffer, len);
141357429Smarkm	}
141457429Smarkm}
141557429Smarkm
141657429Smarkm/*
141760573Skris * Get packets from the connection input buffer, and process them as long as
141860573Skris * there are packets available.
141960573Skris *
142060573Skris * Any unknown packets received during the actual
142160573Skris * session cause the session to terminate.  This is
142260573Skris * intended to make debugging easier since no
142360573Skris * confirmations are sent.  Any compatible protocol
142460573Skris * extensions must be negotiated during the
142560573Skris * preparatory phase.
142660573Skris */
142760573Skris
142892555Sdesstatic void
142976259Sgreenclient_process_buffered_input_packets(void)
143060573Skris{
1431181111Sdes	dispatch_run(DISPATCH_NONBLOCK, &quit_pending,
1432181111Sdes	    compat20 ? xxx_kex : NULL);
143360573Skris}
143460573Skris
143565668Skris/* scan buf[] for '~' before sending data to the peer */
143665668Skris
1437181111Sdes/* Helper: allocate a new escape_filter_ctx and fill in its escape char */
1438181111Sdesvoid *
1439181111Sdesclient_new_escape_filter_ctx(int escape_char)
144065668Skris{
1441181111Sdes	struct escape_filter_ctx *ret;
1442181111Sdes
1443263970Sdes	ret = xcalloc(1, sizeof(*ret));
1444181111Sdes	ret->escape_pending = 0;
1445181111Sdes	ret->escape_char = escape_char;
1446181111Sdes	return (void *)ret;
144765668Skris}
144865668Skris
1449181111Sdes/* Free the escape filter context on channel free */
1450181111Sdesvoid
1451181111Sdesclient_filter_cleanup(int cid, void *ctx)
1452181111Sdes{
1453263970Sdes	free(ctx);
1454181111Sdes}
1455181111Sdes
1456181111Sdesint
1457181111Sdesclient_simple_escape_filter(Channel *c, char *buf, int len)
1458181111Sdes{
1459181111Sdes	if (c->extended_usage != CHAN_EXTENDED_WRITE)
1460181111Sdes		return 0;
1461181111Sdes
1462181111Sdes	return process_escapes(c, &c->input, &c->output, &c->extended,
1463181111Sdes	    buf, len);
1464181111Sdes}
1465181111Sdes
146692555Sdesstatic void
146776259Sgreenclient_channel_closed(int id, void *arg)
146876259Sgreen{
146992555Sdes	channel_cancel_cleanup(id);
147076259Sgreen	session_closed = 1;
1471247485Sdes	leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
147276259Sgreen}
147376259Sgreen
147460573Skris/*
147557429Smarkm * Implements the interactive session with the server.  This is called after
147657429Smarkm * the user has been authenticated, and a command has been started on the
147792555Sdes * remote host.  If escape_char != SSH_ESCAPECHAR_NONE, it is the character
147892555Sdes * used as an escape character for terminating or suspending the session.
147957429Smarkm */
148057429Smarkm
148160573Skrisint
148265668Skrisclient_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
148357429Smarkm{
148476259Sgreen	fd_set *readset = NULL, *writeset = NULL;
148557429Smarkm	double start_time, total_time;
1486137015Sdes	int max_fd = 0, max_fd2 = 0, len, rekeying = 0;
1487181111Sdes	u_int64_t ibytes, obytes;
1488137015Sdes	u_int nalloc = 0;
148957429Smarkm	char buf[100];
149057429Smarkm
149157429Smarkm	debug("Entering interactive session.");
149257429Smarkm
149357429Smarkm	start_time = get_current_time();
149457429Smarkm
149557429Smarkm	/* Initialize variables. */
1496181111Sdes	escape_pending1 = 0;
149757429Smarkm	last_was_cr = 1;
149857429Smarkm	exit_status = -1;
149957429Smarkm	stdin_eof = 0;
150057429Smarkm	buffer_high = 64 * 1024;
150157429Smarkm	connection_in = packet_get_connection_in();
150257429Smarkm	connection_out = packet_get_connection_out();
150376259Sgreen	max_fd = MAX(connection_in, connection_out);
150476259Sgreen
150576259Sgreen	if (!compat20) {
150676259Sgreen		/* enable nonblocking unless tty */
150776259Sgreen		if (!isatty(fileno(stdin)))
150876259Sgreen			set_nonblock(fileno(stdin));
150976259Sgreen		if (!isatty(fileno(stdout)))
151076259Sgreen			set_nonblock(fileno(stdout));
151176259Sgreen		if (!isatty(fileno(stderr)))
151276259Sgreen			set_nonblock(fileno(stderr));
151376259Sgreen		max_fd = MAX(max_fd, fileno(stdin));
151476259Sgreen		max_fd = MAX(max_fd, fileno(stdout));
151576259Sgreen		max_fd = MAX(max_fd, fileno(stderr));
151676259Sgreen	}
151757429Smarkm	quit_pending = 0;
1518181111Sdes	escape_char1 = escape_char_arg;
151957429Smarkm
152057429Smarkm	/* Initialize buffers. */
152157429Smarkm	buffer_init(&stdin_buffer);
152257429Smarkm	buffer_init(&stdout_buffer);
152357429Smarkm	buffer_init(&stderr_buffer);
152457429Smarkm
152560573Skris	client_init_dispatch();
152660573Skris
1527113908Sdes	/*
1528113908Sdes	 * Set signal handlers, (e.g. to restore non-blocking mode)
1529113908Sdes	 * but don't overwrite SIG_IGN, matches behaviour from rsh(1)
1530113908Sdes	 */
1531146998Sdes	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
1532146998Sdes		signal(SIGHUP, signal_handler);
1533113908Sdes	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1534113908Sdes		signal(SIGINT, signal_handler);
1535113908Sdes	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
1536113908Sdes		signal(SIGQUIT, signal_handler);
1537113908Sdes	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
1538113908Sdes		signal(SIGTERM, signal_handler);
1539146998Sdes	signal(SIGWINCH, window_change_handler);
154057429Smarkm
154157429Smarkm	if (have_pty)
1542247485Sdes		enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
154357429Smarkm
154476259Sgreen	if (compat20) {
154576259Sgreen		session_ident = ssh2_chan_id;
1546247485Sdes		if (session_ident != -1) {
1547247485Sdes			if (escape_char_arg != SSH_ESCAPECHAR_NONE) {
1548247485Sdes				channel_register_filter(session_ident,
1549247485Sdes				    client_simple_escape_filter, NULL,
1550247485Sdes				    client_filter_cleanup,
1551247485Sdes				    client_new_escape_filter_ctx(
1552247485Sdes				    escape_char_arg));
1553247485Sdes			}
155476259Sgreen			channel_register_cleanup(session_ident,
1555157016Sdes			    client_channel_closed, 0);
1556247485Sdes		}
155776259Sgreen	} else {
155876259Sgreen		/* Check if we should immediately send eof on stdin. */
155960573Skris		client_check_initial_eof_on_stdin();
156076259Sgreen	}
156157429Smarkm
156257429Smarkm	/* Main loop of the client for the interactive session mode. */
156357429Smarkm	while (!quit_pending) {
156457429Smarkm
156557429Smarkm		/* Process buffered packets sent by the server. */
156657429Smarkm		client_process_buffered_input_packets();
156757429Smarkm
156876259Sgreen		if (compat20 && session_closed && !channel_still_open())
156960573Skris			break;
157060573Skris
157176259Sgreen		rekeying = (xxx_kex != NULL && !xxx_kex->done);
157257429Smarkm
157376259Sgreen		if (rekeying) {
157476259Sgreen			debug("rekeying in progress");
157576259Sgreen		} else {
157676259Sgreen			/*
157776259Sgreen			 * Make packets of buffered stdin data, and buffer
157876259Sgreen			 * them for sending to the server.
157976259Sgreen			 */
158076259Sgreen			if (!compat20)
158176259Sgreen				client_make_packets_from_stdin_data();
158257429Smarkm
158376259Sgreen			/*
158476259Sgreen			 * Make packets from buffered channel data, and
158576259Sgreen			 * enqueue them for sending to the server.
158676259Sgreen			 */
158776259Sgreen			if (packet_not_very_much_data_to_write())
158876259Sgreen				channel_output_poll();
158957429Smarkm
159076259Sgreen			/*
159176259Sgreen			 * Check if the window size has changed, and buffer a
159276259Sgreen			 * message about it to the server if so.
159376259Sgreen			 */
159476259Sgreen			client_check_window_change();
159557429Smarkm
159676259Sgreen			if (quit_pending)
159776259Sgreen				break;
159876259Sgreen		}
159957429Smarkm		/*
160057429Smarkm		 * Wait until we have something to do (something becomes
160157429Smarkm		 * available on one of the descriptors).
160257429Smarkm		 */
160392555Sdes		max_fd2 = max_fd;
160476259Sgreen		client_wait_until_can_do_something(&readset, &writeset,
160592555Sdes		    &max_fd2, &nalloc, rekeying);
160657429Smarkm
160757429Smarkm		if (quit_pending)
160857429Smarkm			break;
160957429Smarkm
161076259Sgreen		/* Do channel operations unless rekeying in progress. */
161176259Sgreen		if (!rekeying) {
161276259Sgreen			channel_after_select(readset, writeset);
1613124208Sdes			if (need_rekeying || packet_need_rekeying()) {
1614124208Sdes				debug("need rekeying");
161576259Sgreen				xxx_kex->done = 0;
161676259Sgreen				kex_send_kexinit(xxx_kex);
161776259Sgreen				need_rekeying = 0;
161876259Sgreen			}
161976259Sgreen		}
162076259Sgreen
162160573Skris		/* Buffer input from the connection.  */
162276259Sgreen		client_process_net_input(readset);
162357429Smarkm
162460573Skris		if (quit_pending)
162560573Skris			break;
162657429Smarkm
162760573Skris		if (!compat20) {
162860573Skris			/* Buffer data from stdin */
162976259Sgreen			client_process_input(readset);
163060573Skris			/*
163160573Skris			 * Process output to stdout and stderr.  Output to
163260573Skris			 * the connection is processed elsewhere (above).
163360573Skris			 */
163476259Sgreen			client_process_output(writeset);
163560573Skris		}
163660573Skris
1637204917Sdes		if (session_resumed) {
1638204917Sdes			connection_in = packet_get_connection_in();
1639204917Sdes			connection_out = packet_get_connection_out();
1640204917Sdes			max_fd = MAX(max_fd, connection_out);
1641204917Sdes			max_fd = MAX(max_fd, connection_in);
1642204917Sdes			session_resumed = 0;
1643204917Sdes		}
1644204917Sdes
1645181111Sdes		/*
1646181111Sdes		 * Send as much buffered packet data as possible to the
1647181111Sdes		 * sender.
1648181111Sdes		 */
164976259Sgreen		if (FD_ISSET(connection_out, writeset))
165057429Smarkm			packet_write_poll();
1651215116Sdes
1652215116Sdes		/*
1653215116Sdes		 * If we are a backgrounded control master, and the
1654215116Sdes		 * timeout has expired without any active client
1655215116Sdes		 * connections, then quit.
1656215116Sdes		 */
1657215116Sdes		if (control_persist_exit_time > 0) {
1658263970Sdes			if (monotime() >= control_persist_exit_time) {
1659215116Sdes				debug("ControlPersist timeout expired");
1660215116Sdes				break;
1661215116Sdes			}
1662215116Sdes		}
166357429Smarkm	}
1664263970Sdes	free(readset);
1665263970Sdes	free(writeset);
166657429Smarkm
166757429Smarkm	/* Terminate the session. */
166857429Smarkm
166957429Smarkm	/* Stop watching for window change. */
1670146998Sdes	signal(SIGWINCH, SIG_DFL);
167157429Smarkm
1672197679Sdes	if (compat20) {
1673197679Sdes		packet_start(SSH2_MSG_DISCONNECT);
1674197679Sdes		packet_put_int(SSH2_DISCONNECT_BY_APPLICATION);
1675197679Sdes		packet_put_cstring("disconnected by user");
1676207319Sdes		packet_put_cstring(""); /* language tag */
1677197679Sdes		packet_send();
1678197679Sdes		packet_write_wait();
1679197679Sdes	}
1680197679Sdes
168192555Sdes	channel_free_all();
168257429Smarkm
168392555Sdes	if (have_pty)
1684247485Sdes		leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
168592555Sdes
168692555Sdes	/* restore blocking io */
168792555Sdes	if (!isatty(fileno(stdin)))
168892555Sdes		unset_nonblock(fileno(stdin));
168992555Sdes	if (!isatty(fileno(stdout)))
169092555Sdes		unset_nonblock(fileno(stdout));
169192555Sdes	if (!isatty(fileno(stderr)))
169292555Sdes		unset_nonblock(fileno(stderr));
169392555Sdes
1694126274Sdes	/*
1695126274Sdes	 * If there was no shell or command requested, there will be no remote
1696126274Sdes	 * exit status to be returned.  In that case, clear error code if the
1697126274Sdes	 * connection was deliberately terminated at this end.
1698126274Sdes	 */
1699126274Sdes	if (no_shell_flag && received_signal == SIGTERM) {
1700126274Sdes		received_signal = 0;
1701126274Sdes		exit_status = 0;
170292555Sdes	}
170392555Sdes
1704126274Sdes	if (received_signal)
1705126274Sdes		fatal("Killed by signal %d.", (int) received_signal);
1706126274Sdes
170757429Smarkm	/*
170857429Smarkm	 * In interactive mode (with pseudo tty) display a message indicating
170957429Smarkm	 * that the connection has been closed.
171057429Smarkm	 */
171157429Smarkm	if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) {
1712181111Sdes		snprintf(buf, sizeof buf,
1713181111Sdes		    "Connection to %.64s closed.\r\n", host);
171457429Smarkm		buffer_append(&stderr_buffer, buf, strlen(buf));
171557429Smarkm	}
171692555Sdes
171757429Smarkm	/* Output any buffered data for stdout. */
1718221420Sdes	if (buffer_len(&stdout_buffer) > 0) {
1719221420Sdes		len = atomicio(vwrite, fileno(stdout),
1720221420Sdes		    buffer_ptr(&stdout_buffer), buffer_len(&stdout_buffer));
1721221420Sdes		if (len < 0 || (u_int)len != buffer_len(&stdout_buffer))
172257429Smarkm			error("Write failed flushing stdout buffer.");
1723221420Sdes		else
1724221420Sdes			buffer_consume(&stdout_buffer, len);
172557429Smarkm	}
172657429Smarkm
172757429Smarkm	/* Output any buffered data for stderr. */
1728221420Sdes	if (buffer_len(&stderr_buffer) > 0) {
1729221420Sdes		len = atomicio(vwrite, fileno(stderr),
1730221420Sdes		    buffer_ptr(&stderr_buffer), buffer_len(&stderr_buffer));
1731221420Sdes		if (len < 0 || (u_int)len != buffer_len(&stderr_buffer))
173257429Smarkm			error("Write failed flushing stderr buffer.");
1733221420Sdes		else
1734221420Sdes			buffer_consume(&stderr_buffer, len);
173557429Smarkm	}
173657429Smarkm
173757429Smarkm	/* Clear and free any buffers. */
173857429Smarkm	memset(buf, 0, sizeof(buf));
173957429Smarkm	buffer_free(&stdin_buffer);
174057429Smarkm	buffer_free(&stdout_buffer);
174157429Smarkm	buffer_free(&stderr_buffer);
174257429Smarkm
174357429Smarkm	/* Report bytes transferred, and transfer rates. */
174457429Smarkm	total_time = get_current_time() - start_time;
1745181111Sdes	packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes);
1746181111Sdes	packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes);
1747181111Sdes	verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds",
1748221420Sdes	    (unsigned long long)obytes, (unsigned long long)ibytes, total_time);
174957429Smarkm	if (total_time > 0)
1750181111Sdes		verbose("Bytes per second: sent %.1f, received %.1f",
1751181111Sdes		    obytes / total_time, ibytes / total_time);
175257429Smarkm	/* Return the exit status of the program. */
175357429Smarkm	debug("Exit status %d", exit_status);
175457429Smarkm	return exit_status;
175557429Smarkm}
175660573Skris
175760573Skris/*********/
175860573Skris
175992555Sdesstatic void
176092555Sdesclient_input_stdout_data(int type, u_int32_t seq, void *ctxt)
176160573Skris{
176276259Sgreen	u_int data_len;
176360573Skris	char *data = packet_get_string(&data_len);
176492555Sdes	packet_check_eom();
176560573Skris	buffer_append(&stdout_buffer, data, data_len);
1766263970Sdes	explicit_bzero(data, data_len);
1767263970Sdes	free(data);
176860573Skris}
176992555Sdesstatic void
177092555Sdesclient_input_stderr_data(int type, u_int32_t seq, void *ctxt)
177160573Skris{
177276259Sgreen	u_int data_len;
177360573Skris	char *data = packet_get_string(&data_len);
177492555Sdes	packet_check_eom();
177560573Skris	buffer_append(&stderr_buffer, data, data_len);
1776263970Sdes	explicit_bzero(data, data_len);
1777263970Sdes	free(data);
177860573Skris}
177992555Sdesstatic void
178092555Sdesclient_input_exit_status(int type, u_int32_t seq, void *ctxt)
178160573Skris{
178260573Skris	exit_status = packet_get_int();
178392555Sdes	packet_check_eom();
178460573Skris	/* Acknowledge the exit. */
178560573Skris	packet_start(SSH_CMSG_EXIT_CONFIRMATION);
178660573Skris	packet_send();
178760573Skris	/*
178860573Skris	 * Must wait for packet to be sent since we are
178960573Skris	 * exiting the loop.
179060573Skris	 */
179160573Skris	packet_write_wait();
179260573Skris	/* Flag that we want to exit. */
179360573Skris	quit_pending = 1;
179460573Skris}
1795126274Sdesstatic void
1796126274Sdesclient_input_agent_open(int type, u_int32_t seq, void *ctxt)
1797126274Sdes{
1798126274Sdes	Channel *c = NULL;
1799126274Sdes	int remote_id, sock;
180060573Skris
1801126274Sdes	/* Read the remote channel number from the message. */
1802126274Sdes	remote_id = packet_get_int();
1803126274Sdes	packet_check_eom();
1804126274Sdes
1805126274Sdes	/*
1806126274Sdes	 * Get a connection to the local authentication agent (this may again
1807126274Sdes	 * get forwarded).
1808126274Sdes	 */
1809126274Sdes	sock = ssh_get_authentication_socket();
1810126274Sdes
1811126274Sdes	/*
1812126274Sdes	 * If we could not connect the agent, send an error message back to
1813126274Sdes	 * the server. This should never happen unless the agent dies,
1814126274Sdes	 * because authentication forwarding is only enabled if we have an
1815126274Sdes	 * agent.
1816126274Sdes	 */
1817126274Sdes	if (sock >= 0) {
1818126274Sdes		c = channel_new("", SSH_CHANNEL_OPEN, sock, sock,
1819126274Sdes		    -1, 0, 0, 0, "authentication agent connection", 1);
1820126274Sdes		c->remote_id = remote_id;
1821126274Sdes		c->force_drain = 1;
1822126274Sdes	}
1823126274Sdes	if (c == NULL) {
1824126274Sdes		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
1825126274Sdes		packet_put_int(remote_id);
1826126274Sdes	} else {
1827126274Sdes		/* Send a confirmation to the remote host. */
1828126274Sdes		debug("Forwarding authentication connection.");
1829126274Sdes		packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
1830126274Sdes		packet_put_int(remote_id);
1831126274Sdes		packet_put_int(c->self);
1832126274Sdes	}
1833126274Sdes	packet_send();
1834126274Sdes}
1835126274Sdes
183692555Sdesstatic Channel *
183776259Sgreenclient_request_forwarded_tcpip(const char *request_type, int rchan)
183876259Sgreen{
1839106121Sdes	Channel *c = NULL;
184076259Sgreen	char *listen_address, *originator_address;
1841192595Sdes	u_short listen_port, originator_port;
184276259Sgreen
184376259Sgreen	/* Get rest of the packet */
184476259Sgreen	listen_address = packet_get_string(NULL);
184576259Sgreen	listen_port = packet_get_int();
184676259Sgreen	originator_address = packet_get_string(NULL);
184776259Sgreen	originator_port = packet_get_int();
184892555Sdes	packet_check_eom();
184976259Sgreen
1850181111Sdes	debug("client_request_forwarded_tcpip: listen %s port %d, "
1851181111Sdes	    "originator %s port %d", listen_address, listen_port,
1852181111Sdes	    originator_address, originator_port);
185376259Sgreen
1854181111Sdes	c = channel_connect_by_listen_address(listen_port,
1855181111Sdes	    "forwarded-tcpip", originator_address);
1856181111Sdes
1857263970Sdes	free(originator_address);
1858263970Sdes	free(listen_address);
185976259Sgreen	return c;
186076259Sgreen}
186176259Sgreen
1862106121Sdesstatic Channel *
186376259Sgreenclient_request_x11(const char *request_type, int rchan)
186476259Sgreen{
186576259Sgreen	Channel *c = NULL;
186676259Sgreen	char *originator;
1867192595Sdes	u_short originator_port;
186892555Sdes	int sock;
186976259Sgreen
187076259Sgreen	if (!options.forward_x11) {
187176259Sgreen		error("Warning: ssh server tried X11 forwarding.");
1872181111Sdes		error("Warning: this is probably a break-in attempt by a "
1873181111Sdes		    "malicious server.");
187476259Sgreen		return NULL;
187576259Sgreen	}
1876263970Sdes	if (x11_refuse_time != 0 && monotime() >= x11_refuse_time) {
1877215116Sdes		verbose("Rejected X11 connection after ForwardX11Timeout "
1878215116Sdes		    "expired");
1879215116Sdes		return NULL;
1880215116Sdes	}
188176259Sgreen	originator = packet_get_string(NULL);
188276259Sgreen	if (datafellows & SSH_BUG_X11FWD) {
188376259Sgreen		debug2("buggy server: x11 request w/o originator_port");
188476259Sgreen		originator_port = 0;
188576259Sgreen	} else {
188676259Sgreen		originator_port = packet_get_int();
188776259Sgreen	}
188892555Sdes	packet_check_eom();
188976259Sgreen	/* XXX check permission */
189076259Sgreen	debug("client_request_x11: request from %s %d", originator,
189176259Sgreen	    originator_port);
1892263970Sdes	free(originator);
189376259Sgreen	sock = x11_connect_display();
189492555Sdes	if (sock < 0)
189592555Sdes		return NULL;
1896224638Sbrooks	if (options.hpn_disabled)
1897224638Sbrooks		c = channel_new("x11", SSH_CHANNEL_X11_OPEN, sock, sock, -1,
1898224638Sbrooks		    CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
1899224638Sbrooks		    0, "x11", 1);
1900224638Sbrooks	else
1901224638Sbrooks		c = channel_new("x11", SSH_CHANNEL_X11_OPEN, sock, sock, -1,
1902224638Sbrooks		    options.hpn_buffer_size, CHAN_X11_PACKET_DEFAULT,
1903224638Sbrooks		    0, "x11", 1);
190492555Sdes	c->force_drain = 1;
190576259Sgreen	return c;
190676259Sgreen}
190776259Sgreen
1908106121Sdesstatic Channel *
190976259Sgreenclient_request_agent(const char *request_type, int rchan)
191076259Sgreen{
191176259Sgreen	Channel *c = NULL;
191292555Sdes	int sock;
191376259Sgreen
191476259Sgreen	if (!options.forward_agent) {
191576259Sgreen		error("Warning: ssh server tried agent forwarding.");
1916181111Sdes		error("Warning: this is probably a break-in attempt by a "
1917181111Sdes		    "malicious server.");
191876259Sgreen		return NULL;
191976259Sgreen	}
1920181111Sdes	sock = ssh_get_authentication_socket();
192192555Sdes	if (sock < 0)
192292555Sdes		return NULL;
1923224638Sbrooks	if (options.hpn_disabled)
1924224638Sbrooks		c = channel_new("authentication agent connection",
1925224638Sbrooks		    SSH_CHANNEL_OPEN, sock, sock, -1,
1926224638Sbrooks		    CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0,
1927224638Sbrooks		    "authentication agent connection", 1);
1928224638Sbrooks	else
1929224638Sbrooks		c = channel_new("authentication agent connection",
1930224638Sbrooks		    SSH_CHANNEL_OPEN, sock, sock, -1,
1931224638Sbrooks		    options.hpn_buffer_size, options.hpn_buffer_size, 0,
1932224638Sbrooks		    "authentication agent connection", 1);
193392555Sdes	c->force_drain = 1;
193476259Sgreen	return c;
193576259Sgreen}
193676259Sgreen
1937181111Sdesint
1938181111Sdesclient_request_tun_fwd(int tun_mode, int local_tun, int remote_tun)
1939181111Sdes{
1940181111Sdes	Channel *c;
1941181111Sdes	int fd;
1942181111Sdes
1943181111Sdes	if (tun_mode == SSH_TUNMODE_NO)
1944181111Sdes		return 0;
1945181111Sdes
1946181111Sdes	if (!compat20) {
1947192595Sdes		error("Tunnel forwarding is not supported for protocol 1");
1948181111Sdes		return -1;
1949181111Sdes	}
1950181111Sdes
1951181111Sdes	debug("Requesting tun unit %d in mode %d", local_tun, tun_mode);
1952181111Sdes
1953181111Sdes	/* Open local tunnel device */
1954181111Sdes	if ((fd = tun_open(local_tun, tun_mode)) == -1) {
1955181111Sdes		error("Tunnel device open failed.");
1956181111Sdes		return -1;
1957181111Sdes	}
1958181111Sdes
1959224638Sbrooks	if (options.hpn_disabled)
1960224638Sbrooks		c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1,
1961224638Sbrooks		    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
1962224638Sbrooks		    0, "tun", 1);
1963224638Sbrooks	else
1964224638Sbrooks		c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1,
1965224638Sbrooks		    options.hpn_buffer_size, CHAN_TCP_PACKET_DEFAULT,
1966224638Sbrooks		    0, "tun", 1);
1967181111Sdes	c->datagram = 1;
1968181111Sdes
1969181111Sdes#if defined(SSH_TUN_FILTER)
1970181111Sdes	if (options.tun_open == SSH_TUNMODE_POINTOPOINT)
1971181111Sdes		channel_register_filter(c->self, sys_tun_infilter,
1972181111Sdes		    sys_tun_outfilter, NULL, NULL);
1973181111Sdes#endif
1974181111Sdes
1975181111Sdes	packet_start(SSH2_MSG_CHANNEL_OPEN);
1976181111Sdes	packet_put_cstring("tun@openssh.com");
1977181111Sdes	packet_put_int(c->self);
1978181111Sdes	packet_put_int(c->local_window_max);
1979181111Sdes	packet_put_int(c->local_maxpacket);
1980181111Sdes	packet_put_int(tun_mode);
1981181111Sdes	packet_put_int(remote_tun);
1982181111Sdes	packet_send();
1983181111Sdes
1984181111Sdes	return 0;
1985181111Sdes}
1986181111Sdes
198760573Skris/* XXXX move to generic input handler */
198892555Sdesstatic void
198992555Sdesclient_input_channel_open(int type, u_int32_t seq, void *ctxt)
199060573Skris{
199160573Skris	Channel *c = NULL;
199260573Skris	char *ctype;
199360573Skris	int rchan;
199499060Sdes	u_int rmaxpack, rwindow, len;
199560573Skris
199660573Skris	ctype = packet_get_string(&len);
199760573Skris	rchan = packet_get_int();
199860573Skris	rwindow = packet_get_int();
199960573Skris	rmaxpack = packet_get_int();
200060573Skris
200160573Skris	debug("client_input_channel_open: ctype %s rchan %d win %d max %d",
200260573Skris	    ctype, rchan, rwindow, rmaxpack);
200360573Skris
200476259Sgreen	if (strcmp(ctype, "forwarded-tcpip") == 0) {
200576259Sgreen		c = client_request_forwarded_tcpip(ctype, rchan);
200676259Sgreen	} else if (strcmp(ctype, "x11") == 0) {
200776259Sgreen		c = client_request_x11(ctype, rchan);
200876259Sgreen	} else if (strcmp(ctype, "auth-agent@openssh.com") == 0) {
200976259Sgreen		c = client_request_agent(ctype, rchan);
201060573Skris	}
201160573Skris/* XXX duplicate : */
201260573Skris	if (c != NULL) {
201360573Skris		debug("confirm %s", ctype);
201460573Skris		c->remote_id = rchan;
201560573Skris		c->remote_window = rwindow;
201660573Skris		c->remote_maxpacket = rmaxpack;
201792555Sdes		if (c->type != SSH_CHANNEL_CONNECTING) {
201892555Sdes			packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
201992555Sdes			packet_put_int(c->remote_id);
202092555Sdes			packet_put_int(c->self);
202192555Sdes			packet_put_int(c->local_window);
202292555Sdes			packet_put_int(c->local_maxpacket);
202392555Sdes			packet_send();
202492555Sdes		}
202560573Skris	} else {
202660573Skris		debug("failure %s", ctype);
202760573Skris		packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
202860573Skris		packet_put_int(rchan);
202960573Skris		packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED);
203092555Sdes		if (!(datafellows & SSH_BUG_OPENFAILURE)) {
203192555Sdes			packet_put_cstring("open failed");
203292555Sdes			packet_put_cstring("");
203392555Sdes		}
203460573Skris		packet_send();
203560573Skris	}
2036263970Sdes	free(ctype);
203760573Skris}
203892555Sdesstatic void
203992555Sdesclient_input_channel_req(int type, u_int32_t seq, void *ctxt)
204076259Sgreen{
204176259Sgreen	Channel *c = NULL;
2042137015Sdes	int exitval, id, reply, success = 0;
204376259Sgreen	char *rtype;
204460573Skris
204576259Sgreen	id = packet_get_int();
204676259Sgreen	rtype = packet_get_string(NULL);
204776259Sgreen	reply = packet_get_char();
204876259Sgreen
204976259Sgreen	debug("client_input_channel_req: channel %d rtype %s reply %d",
205076259Sgreen	    id, rtype, reply);
205176259Sgreen
2052137015Sdes	if (id == -1) {
2053137015Sdes		error("client_input_channel_req: request for channel -1");
2054137015Sdes	} else if ((c = channel_lookup(id)) == NULL) {
2055181111Sdes		error("client_input_channel_req: channel %d: "
2056181111Sdes		    "unknown channel", id);
2057181111Sdes	} else if (strcmp(rtype, "eow@openssh.com") == 0) {
2058181111Sdes		packet_check_eom();
2059181111Sdes		chan_rcvd_eow(c);
206076259Sgreen	} else if (strcmp(rtype, "exit-status") == 0) {
2061137015Sdes		exitval = packet_get_int();
2062204917Sdes		if (c->ctl_chan != -1) {
2063204917Sdes			mux_exit_message(c, exitval);
2064137015Sdes			success = 1;
2065204917Sdes		} else if (id == session_ident) {
2066204917Sdes			/* Record exit value of local session */
2067204917Sdes			success = 1;
2068137015Sdes			exit_status = exitval;
2069137015Sdes		} else {
2070204917Sdes			/* Probably for a mux channel that has already closed */
2071204917Sdes			debug("%s: no sink for exit-status on channel %d",
2072204917Sdes			    __func__, id);
2073137015Sdes		}
207492555Sdes		packet_check_eom();
207576259Sgreen	}
2076221420Sdes	if (reply && c != NULL) {
207776259Sgreen		packet_start(success ?
207876259Sgreen		    SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
2079192595Sdes		packet_put_int(c->remote_id);
208076259Sgreen		packet_send();
208176259Sgreen	}
2082263970Sdes	free(rtype);
208376259Sgreen}
208492555Sdesstatic void
208592555Sdesclient_input_global_request(int type, u_int32_t seq, void *ctxt)
208692555Sdes{
208792555Sdes	char *rtype;
208892555Sdes	int want_reply;
208992555Sdes	int success = 0;
209076259Sgreen
209192555Sdes	rtype = packet_get_string(NULL);
209292555Sdes	want_reply = packet_get_char();
2093126274Sdes	debug("client_input_global_request: rtype %s want_reply %d",
2094126274Sdes	    rtype, want_reply);
209592555Sdes	if (want_reply) {
209692555Sdes		packet_start(success ?
209792555Sdes		    SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE);
209892555Sdes		packet_send();
209992555Sdes		packet_write_wait();
210092555Sdes	}
2101263970Sdes	free(rtype);
210292555Sdes}
210392555Sdes
2104137015Sdesvoid
2105137015Sdesclient_session2_setup(int id, int want_tty, int want_subsystem,
2106181111Sdes    const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env)
2107137015Sdes{
2108137015Sdes	int len;
2109146998Sdes	Channel *c = NULL;
2110137015Sdes
2111137015Sdes	debug2("%s: id %d", __func__, id);
2112137015Sdes
2113146998Sdes	if ((c = channel_lookup(id)) == NULL)
2114146998Sdes		fatal("client_session2_setup: channel %d: unknown channel", id);
2115146998Sdes
2116221420Sdes	packet_set_interactive(want_tty,
2117221420Sdes	    options.ip_qos_interactive, options.ip_qos_bulk);
2118221420Sdes
2119137015Sdes	if (want_tty) {
2120137015Sdes		struct winsize ws;
2121137015Sdes
2122137015Sdes		/* Store window size in the packet. */
2123137015Sdes		if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0)
2124137015Sdes			memset(&ws, 0, sizeof(ws));
2125137015Sdes
2126181111Sdes		channel_request_start(id, "pty-req", 1);
2127247485Sdes		client_expect_confirm(id, "PTY allocation", CONFIRM_TTY);
2128137015Sdes		packet_put_cstring(term != NULL ? term : "");
2129162852Sdes		packet_put_int((u_int)ws.ws_col);
2130162852Sdes		packet_put_int((u_int)ws.ws_row);
2131162852Sdes		packet_put_int((u_int)ws.ws_xpixel);
2132162852Sdes		packet_put_int((u_int)ws.ws_ypixel);
2133181111Sdes		if (tiop == NULL)
2134181111Sdes			tiop = get_saved_tio();
2135181111Sdes		tty_make_modes(-1, tiop);
2136137015Sdes		packet_send();
2137137015Sdes		/* XXX wait for reply */
2138146998Sdes		c->client_tty = 1;
2139137015Sdes	}
2140137015Sdes
2141137015Sdes	/* Transfer any environment variables from client to server */
2142137015Sdes	if (options.num_send_env != 0 && env != NULL) {
2143137015Sdes		int i, j, matched;
2144137015Sdes		char *name, *val;
2145137015Sdes
2146137015Sdes		debug("Sending environment.");
2147137015Sdes		for (i = 0; env[i] != NULL; i++) {
2148137015Sdes			/* Split */
2149137015Sdes			name = xstrdup(env[i]);
2150137015Sdes			if ((val = strchr(name, '=')) == NULL) {
2151263970Sdes				free(name);
2152137015Sdes				continue;
2153137015Sdes			}
2154137015Sdes			*val++ = '\0';
2155137015Sdes
2156137015Sdes			matched = 0;
2157137015Sdes			for (j = 0; j < options.num_send_env; j++) {
2158137015Sdes				if (match_pattern(name, options.send_env[j])) {
2159137015Sdes					matched = 1;
2160137015Sdes					break;
2161137015Sdes				}
2162137015Sdes			}
2163137015Sdes			if (!matched) {
2164137015Sdes				debug3("Ignored env %s", name);
2165263970Sdes				free(name);
2166137015Sdes				continue;
2167137015Sdes			}
2168137015Sdes
2169137015Sdes			debug("Sending env %s = %s", name, val);
2170137015Sdes			channel_request_start(id, "env", 0);
2171137015Sdes			packet_put_cstring(name);
2172137015Sdes			packet_put_cstring(val);
2173137015Sdes			packet_send();
2174263970Sdes			free(name);
2175137015Sdes		}
2176137015Sdes	}
2177137015Sdes
2178137015Sdes	len = buffer_len(cmd);
2179137015Sdes	if (len > 0) {
2180137015Sdes		if (len > 900)
2181137015Sdes			len = 900;
2182137015Sdes		if (want_subsystem) {
2183181111Sdes			debug("Sending subsystem: %.*s",
2184181111Sdes			    len, (u_char*)buffer_ptr(cmd));
2185181111Sdes			channel_request_start(id, "subsystem", 1);
2186247485Sdes			client_expect_confirm(id, "subsystem", CONFIRM_CLOSE);
2187137015Sdes		} else {
2188181111Sdes			debug("Sending command: %.*s",
2189181111Sdes			    len, (u_char*)buffer_ptr(cmd));
2190181111Sdes			channel_request_start(id, "exec", 1);
2191247485Sdes			client_expect_confirm(id, "exec", CONFIRM_CLOSE);
2192137015Sdes		}
2193137015Sdes		packet_put_string(buffer_ptr(cmd), buffer_len(cmd));
2194137015Sdes		packet_send();
2195137015Sdes	} else {
2196181111Sdes		channel_request_start(id, "shell", 1);
2197247485Sdes		client_expect_confirm(id, "shell", CONFIRM_CLOSE);
2198137015Sdes		packet_send();
2199137015Sdes	}
2200137015Sdes}
2201137015Sdes
220292555Sdesstatic void
220376259Sgreenclient_init_dispatch_20(void)
220460573Skris{
220560573Skris	dispatch_init(&dispatch_protocol_error);
220698675Sdes
220760573Skris	dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose);
220860573Skris	dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data);
220960573Skris	dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof);
221060573Skris	dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data);
221160573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open);
221260573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
221360573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
221476259Sgreen	dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req);
221560573Skris	dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust);
2216181111Sdes	dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm);
2217181111Sdes	dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm);
221892555Sdes	dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request);
221976259Sgreen
222076259Sgreen	/* rekeying */
222176259Sgreen	dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
222298675Sdes
222398675Sdes	/* global request reply messages */
222498675Sdes	dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply);
222598675Sdes	dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply);
222660573Skris}
2227181111Sdes
222892555Sdesstatic void
222976259Sgreenclient_init_dispatch_13(void)
223060573Skris{
223160573Skris	dispatch_init(NULL);
223260573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close);
223360573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation);
223460573Skris	dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data);
223560573Skris	dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
223660573Skris	dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
223760573Skris	dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open);
223860573Skris	dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status);
223960573Skris	dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data);
224060573Skris	dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data);
224168700Sgreen
224268700Sgreen	dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ?
2243126274Sdes	    &client_input_agent_open : &deny_input_open);
224468700Sgreen	dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ?
224569587Sgreen	    &x11_input_open : &deny_input_open);
224660573Skris}
2247181111Sdes
224892555Sdesstatic void
224976259Sgreenclient_init_dispatch_15(void)
225060573Skris{
225160573Skris	client_init_dispatch_13();
225260573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof);
225360573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose);
225460573Skris}
2255181111Sdes
225692555Sdesstatic void
225776259Sgreenclient_init_dispatch(void)
225860573Skris{
225960573Skris	if (compat20)
226060573Skris		client_init_dispatch_20();
226160573Skris	else if (compat13)
226260573Skris		client_init_dispatch_13();
226360573Skris	else
226460573Skris		client_init_dispatch_15();
226560573Skris}
2266126274Sdes
2267247485Sdesvoid
2268247485Sdesclient_stop_mux(void)
2269247485Sdes{
2270247485Sdes	if (options.control_path != NULL && muxserver_sock != -1)
2271247485Sdes		unlink(options.control_path);
2272247485Sdes	/*
2273251135Sdes	 * If we are in persist mode, or don't have a shell, signal that we
2274251135Sdes	 * should close when all active channels are closed.
2275247485Sdes	 */
2276251135Sdes	if (options.control_persist || no_shell_flag) {
2277247485Sdes		session_closed = 1;
2278247485Sdes		setproctitle("[stopped mux]");
2279247485Sdes	}
2280247485Sdes}
2281247485Sdes
2282126274Sdes/* client specific fatal cleanup */
2283126274Sdesvoid
2284126274Sdescleanup_exit(int i)
2285126274Sdes{
2286247485Sdes	leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
2287126274Sdes	leave_non_blocking();
2288181111Sdes	if (options.control_path != NULL && muxserver_sock != -1)
2289137015Sdes		unlink(options.control_path);
2290221420Sdes	ssh_kill_proxy_command();
2291126274Sdes	_exit(i);
2292126274Sdes}
2293