blob: aade8606b6340dfb960a88d39caf1c6c4143d181 [file] [log] [blame]
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001/*
Damien Miller95def091999-11-25 00:26:21 +11002 * Author: Tatu Ylonen <ylo@cs.hut.fi>
Damien Miller95def091999-11-25 00:26:21 +11003 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4 * All rights reserved
Damien Miller95def091999-11-25 00:26:21 +11005 * The main loop for the interactive session (client side).
Damien Miller4af51302000-04-16 11:18:38 +10006 *
Damien Millere4340be2000-09-16 13:29:08 +11007 * As far as I am concerned, the code I have written for this software
8 * can be used freely for any purpose. Any derived versions of this
9 * software must be clearly marked as such, and if the derived work is
10 * incompatible with the protocol description in the RFC file, it must be
11 * called by a name other than "ssh" or "Secure Shell".
12 *
13 *
14 * Copyright (c) 1999 Theo de Raadt. All rights reserved.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 *
36 *
Damien Miller1383bd82000-04-06 12:32:37 +100037 * SSH2 support added by Markus Friedl.
Damien Millere4340be2000-09-16 13:29:08 +110038 * Copyright (c) 1999,2000 Markus Friedl. All rights reserved.
39 *
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
42 * are met:
43 * 1. Redistributions of source code must retain the above copyright
44 * notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 * notice, this list of conditions and the following disclaimer in the
47 * documentation and/or other materials provided with the distribution.
48 *
49 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
50 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
51 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
52 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
53 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
54 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
55 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
56 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
57 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
58 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Damien Miller95def091999-11-25 00:26:21 +110059 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100060
61#include "includes.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000062RCSID("$OpenBSD: clientloop.c,v 1.45 2001/01/21 19:05:47 markus Exp $");
Damien Millerd4a8b7e1999-10-27 13:42:43 +100063
Damien Millerd4a8b7e1999-10-27 13:42:43 +100064#include "ssh.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000065#include "ssh1.h"
66#include "ssh2.h"
67#include "xmalloc.h"
Damien Millerd4a8b7e1999-10-27 13:42:43 +100068#include "packet.h"
69#include "buffer.h"
Damien Millerb38eff82000-04-01 11:09:21 +100070#include "compat.h"
71#include "channels.h"
72#include "dispatch.h"
Damien Millerad833b32000-08-23 10:46:23 +100073#include "buffer.h"
74#include "bufaux.h"
Damien Miller0bc1bd82000-11-13 22:57:25 +110075#include "key.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000076#include "log.h"
77#include "readconf.h"
Ben Lindstrombf555ba2001-01-18 02:04:35 +000078#include "clientloop.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000079#include "authfd.h"
80#include "atomicio.h"
Damien Miller69b69aa2000-10-28 14:19:58 +110081
82/* import options */
83extern Options options;
84
Damien Millerd4a8b7e1999-10-27 13:42:43 +100085/* Flag indicating that stdin should be redirected from /dev/null. */
86extern int stdin_null_flag;
87
Damien Miller5428f641999-11-25 11:54:57 +110088/*
89 * Name of the host we are connecting to. This is the name given on the
90 * command line, or the HostName specified for the user-supplied name in a
91 * configuration file.
92 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100093extern char *host;
94
Damien Miller5428f641999-11-25 11:54:57 +110095/*
96 * Flag to indicate that we have received a window change signal which has
97 * not yet been processed. This will cause a message indicating the new
98 * window size to be sent to the server a little later. This is volatile
99 * because this is updated in a signal handler.
100 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000101static volatile int received_window_change_signal = 0;
102
103/* Terminal modes, as saved by enter_raw_mode. */
104static struct termios saved_tio;
105
Damien Miller5428f641999-11-25 11:54:57 +1100106/*
107 * Flag indicating whether we are in raw mode. This is used by
108 * enter_raw_mode and leave_raw_mode.
109 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000110static int in_raw_mode = 0;
111
112/* Flag indicating whether the user\'s terminal is in non-blocking mode. */
113static int in_non_blocking_mode = 0;
114
115/* Common data for the client loop code. */
Damien Millerad833b32000-08-23 10:46:23 +1000116static int quit_pending; /* Set to non-zero to quit the client loop. */
117static int escape_char; /* Escape character. */
Damien Miller95def091999-11-25 00:26:21 +1100118static int escape_pending; /* Last character was the escape character */
119static int last_was_cr; /* Last character was a newline. */
120static int exit_status; /* Used to store the exit status of the command. */
121static int stdin_eof; /* EOF has been encountered on standard error. */
122static Buffer stdin_buffer; /* Buffer for stdin data. */
123static Buffer stdout_buffer; /* Buffer for stdout data. */
124static Buffer stderr_buffer; /* Buffer for stderr data. */
Ben Lindstrom46c16222000-12-22 01:43:59 +0000125static u_long stdin_bytes, stdout_bytes, stderr_bytes;
126static u_int buffer_high;/* Soft max buffer size. */
Damien Miller95def091999-11-25 00:26:21 +1100127static int max_fd; /* Maximum file descriptor number in select(). */
128static int connection_in; /* Connection to server (input). */
129static int connection_out; /* Connection to server (output). */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000130
Damien Miller1383bd82000-04-06 12:32:37 +1000131
132void client_init_dispatch(void);
133int session_ident = -1;
134
Damien Miller5428f641999-11-25 11:54:57 +1100135/* Returns the user\'s terminal to normal mode if it had been put in raw mode. */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000136
Damien Miller4af51302000-04-16 11:18:38 +1000137void
Damien Miller95def091999-11-25 00:26:21 +1100138leave_raw_mode()
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000139{
Damien Miller95def091999-11-25 00:26:21 +1100140 if (!in_raw_mode)
141 return;
142 in_raw_mode = 0;
143 if (tcsetattr(fileno(stdin), TCSADRAIN, &saved_tio) < 0)
144 perror("tcsetattr");
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000145
Damien Miller95def091999-11-25 00:26:21 +1100146 fatal_remove_cleanup((void (*) (void *)) leave_raw_mode, NULL);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000147}
148
149/* Puts the user\'s terminal in raw mode. */
150
Damien Miller4af51302000-04-16 11:18:38 +1000151void
Damien Miller95def091999-11-25 00:26:21 +1100152enter_raw_mode()
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000153{
Damien Miller95def091999-11-25 00:26:21 +1100154 struct termios tio;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000155
Damien Miller95def091999-11-25 00:26:21 +1100156 if (tcgetattr(fileno(stdin), &tio) < 0)
157 perror("tcgetattr");
158 saved_tio = tio;
159 tio.c_iflag |= IGNPAR;
160 tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
161 tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000162#ifdef IEXTEN
Damien Miller95def091999-11-25 00:26:21 +1100163 tio.c_lflag &= ~IEXTEN;
164#endif /* IEXTEN */
165 tio.c_oflag &= ~OPOST;
166 tio.c_cc[VMIN] = 1;
167 tio.c_cc[VTIME] = 0;
168 if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0)
169 perror("tcsetattr");
170 in_raw_mode = 1;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000171
Damien Miller95def091999-11-25 00:26:21 +1100172 fatal_add_cleanup((void (*) (void *)) leave_raw_mode, NULL);
173}
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000174
175/* Restores stdin to blocking mode. */
176
Damien Miller4af51302000-04-16 11:18:38 +1000177void
Damien Miller95def091999-11-25 00:26:21 +1100178leave_non_blocking()
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000179{
Damien Miller95def091999-11-25 00:26:21 +1100180 if (in_non_blocking_mode) {
181 (void) fcntl(fileno(stdin), F_SETFL, 0);
182 in_non_blocking_mode = 0;
183 fatal_remove_cleanup((void (*) (void *)) leave_non_blocking, NULL);
184 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000185}
186
Damien Miller95def091999-11-25 00:26:21 +1100187/* Puts stdin terminal in non-blocking mode. */
188
Damien Miller4af51302000-04-16 11:18:38 +1000189void
Damien Miller95def091999-11-25 00:26:21 +1100190enter_non_blocking()
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000191{
Damien Miller95def091999-11-25 00:26:21 +1100192 in_non_blocking_mode = 1;
193 (void) fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
194 fatal_add_cleanup((void (*) (void *)) leave_non_blocking, NULL);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000195}
196
Damien Miller5428f641999-11-25 11:54:57 +1100197/*
198 * Signal handler for the window change signal (SIGWINCH). This just sets a
199 * flag indicating that the window has changed.
200 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000201
Damien Miller4af51302000-04-16 11:18:38 +1000202void
Damien Miller95def091999-11-25 00:26:21 +1100203window_change_handler(int sig)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000204{
Damien Miller95def091999-11-25 00:26:21 +1100205 received_window_change_signal = 1;
206 signal(SIGWINCH, window_change_handler);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000207}
208
Damien Miller5428f641999-11-25 11:54:57 +1100209/*
210 * Signal handler for signals that cause the program to terminate. These
211 * signals must be trapped to restore terminal modes.
212 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000213
Damien Miller4af51302000-04-16 11:18:38 +1000214void
Damien Miller95def091999-11-25 00:26:21 +1100215signal_handler(int sig)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000216{
Damien Miller95def091999-11-25 00:26:21 +1100217 if (in_raw_mode)
218 leave_raw_mode();
219 if (in_non_blocking_mode)
220 leave_non_blocking();
221 channel_stop_listening();
222 packet_close();
223 fatal("Killed by signal %d.", sig);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000224}
225
Damien Miller5428f641999-11-25 11:54:57 +1100226/*
227 * Returns current time in seconds from Jan 1, 1970 with the maximum
228 * available resolution.
229 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000230
Damien Miller4af51302000-04-16 11:18:38 +1000231double
Damien Miller95def091999-11-25 00:26:21 +1100232get_current_time()
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000233{
Damien Miller95def091999-11-25 00:26:21 +1100234 struct timeval tv;
235 gettimeofday(&tv, NULL);
236 return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000237}
238
Damien Miller5428f641999-11-25 11:54:57 +1100239/*
240 * This is called when the interactive is entered. This checks if there is
241 * an EOF coming on stdin. We must check this explicitly, as select() does
242 * not appear to wake up when redirecting from /dev/null.
243 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000244
Damien Miller4af51302000-04-16 11:18:38 +1000245void
Damien Miller95def091999-11-25 00:26:21 +1100246client_check_initial_eof_on_stdin()
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000247{
Damien Miller95def091999-11-25 00:26:21 +1100248 int len;
249 char buf[1];
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000250
Damien Miller5428f641999-11-25 11:54:57 +1100251 /*
252 * If standard input is to be "redirected from /dev/null", we simply
253 * mark that we have seen an EOF and send an EOF message to the
254 * server. Otherwise, we try to read a single character; it appears
255 * that for some files, such /dev/null, select() never wakes up for
256 * read for this descriptor, which means that we never get EOF. This
257 * way we will get the EOF if stdin comes from /dev/null or similar.
258 */
Damien Miller95def091999-11-25 00:26:21 +1100259 if (stdin_null_flag) {
260 /* Fake EOF on stdin. */
261 debug("Sending eof.");
262 stdin_eof = 1;
263 packet_start(SSH_CMSG_EOF);
264 packet_send();
265 } else {
Damien Miller95def091999-11-25 00:26:21 +1100266 enter_non_blocking();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000267
Damien Miller95def091999-11-25 00:26:21 +1100268 /* Check for immediate EOF on stdin. */
269 len = read(fileno(stdin), buf, 1);
270 if (len == 0) {
Damien Miller5428f641999-11-25 11:54:57 +1100271 /* EOF. Record that we have seen it and send EOF to server. */
Damien Miller95def091999-11-25 00:26:21 +1100272 debug("Sending eof.");
273 stdin_eof = 1;
274 packet_start(SSH_CMSG_EOF);
275 packet_send();
276 } else if (len > 0) {
Damien Miller5428f641999-11-25 11:54:57 +1100277 /*
278 * Got data. We must store the data in the buffer,
279 * and also process it as an escape character if
280 * appropriate.
281 */
Ben Lindstrom46c16222000-12-22 01:43:59 +0000282 if ((u_char) buf[0] == escape_char)
Damien Miller95def091999-11-25 00:26:21 +1100283 escape_pending = 1;
284 else {
285 buffer_append(&stdin_buffer, buf, 1);
286 stdin_bytes += 1;
287 }
288 }
Damien Miller95def091999-11-25 00:26:21 +1100289 leave_non_blocking();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000290 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000291}
292
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000293
Damien Miller5428f641999-11-25 11:54:57 +1100294/*
295 * Make packets from buffered stdin data, and buffer them for sending to the
296 * connection.
297 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000298
Damien Miller4af51302000-04-16 11:18:38 +1000299void
Damien Miller95def091999-11-25 00:26:21 +1100300client_make_packets_from_stdin_data()
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000301{
Ben Lindstrom46c16222000-12-22 01:43:59 +0000302 u_int len;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000303
Damien Miller95def091999-11-25 00:26:21 +1100304 /* Send buffered stdin data to the server. */
305 while (buffer_len(&stdin_buffer) > 0 &&
306 packet_not_very_much_data_to_write()) {
307 len = buffer_len(&stdin_buffer);
308 /* Keep the packets at reasonable size. */
309 if (len > packet_get_maxsize())
310 len = packet_get_maxsize();
311 packet_start(SSH_CMSG_STDIN_DATA);
312 packet_put_string(buffer_ptr(&stdin_buffer), len);
313 packet_send();
314 buffer_consume(&stdin_buffer, len);
315 /* If we have a pending EOF, send it now. */
316 if (stdin_eof && buffer_len(&stdin_buffer) == 0) {
317 packet_start(SSH_CMSG_EOF);
318 packet_send();
319 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000320 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000321}
322
Damien Miller5428f641999-11-25 11:54:57 +1100323/*
324 * Checks if the client window has changed, and sends a packet about it to
325 * the server if so. The actual change is detected elsewhere (by a software
326 * interrupt on Unix); this just checks the flag and sends a message if
327 * appropriate.
328 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000329
Damien Miller4af51302000-04-16 11:18:38 +1000330void
Damien Miller95def091999-11-25 00:26:21 +1100331client_check_window_change()
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000332{
Damien Miller1383bd82000-04-06 12:32:37 +1000333 struct winsize ws;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000334
Damien Miller1383bd82000-04-06 12:32:37 +1000335 if (! received_window_change_signal)
336 return;
337 /** XXX race */
338 received_window_change_signal = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000339
Damien Miller1383bd82000-04-06 12:32:37 +1000340 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
341 return;
342
Damien Millerd3444942000-09-30 14:20:03 +1100343 debug2("client_check_window_change: changed");
Damien Miller1383bd82000-04-06 12:32:37 +1000344
345 if (compat20) {
346 channel_request_start(session_ident, "window-change", 0);
347 packet_put_int(ws.ws_col);
348 packet_put_int(ws.ws_row);
349 packet_put_int(ws.ws_xpixel);
350 packet_put_int(ws.ws_ypixel);
351 packet_send();
352 } else {
353 packet_start(SSH_CMSG_WINDOW_SIZE);
354 packet_put_int(ws.ws_row);
355 packet_put_int(ws.ws_col);
356 packet_put_int(ws.ws_xpixel);
357 packet_put_int(ws.ws_ypixel);
358 packet_send();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000359 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000360}
361
Damien Miller5428f641999-11-25 11:54:57 +1100362/*
363 * Waits until the client can do something (some data becomes available on
364 * one of the file descriptors).
365 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000366
Damien Miller4af51302000-04-16 11:18:38 +1000367void
Damien Miller95def091999-11-25 00:26:21 +1100368client_wait_until_can_do_something(fd_set * readset, fd_set * writeset)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000369{
Damien Miller95def091999-11-25 00:26:21 +1100370 /* Initialize select masks. */
371 FD_ZERO(readset);
Damien Miller95def091999-11-25 00:26:21 +1100372 FD_ZERO(writeset);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000373
Damien Miller1383bd82000-04-06 12:32:37 +1000374 if (!compat20) {
375 /* Read from the connection, unless our buffers are full. */
376 if (buffer_len(&stdout_buffer) < buffer_high &&
377 buffer_len(&stderr_buffer) < buffer_high &&
378 channel_not_very_much_buffered_data())
379 FD_SET(connection_in, readset);
380 /*
381 * Read from stdin, unless we have seen EOF or have very much
382 * buffered data to send to the server.
383 */
384 if (!stdin_eof && packet_not_very_much_data_to_write())
385 FD_SET(fileno(stdin), readset);
386
387 /* Select stdout/stderr if have data in buffer. */
388 if (buffer_len(&stdout_buffer) > 0)
389 FD_SET(fileno(stdout), writeset);
390 if (buffer_len(&stderr_buffer) > 0)
391 FD_SET(fileno(stderr), writeset);
392 } else {
393 FD_SET(connection_in, readset);
394 }
395
Damien Miller95def091999-11-25 00:26:21 +1100396 /* Add any selections by the channel mechanism. */
397 channel_prepare_select(readset, writeset);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000398
Damien Miller95def091999-11-25 00:26:21 +1100399 /* Select server connection if have data to write to the server. */
400 if (packet_have_data_to_write())
401 FD_SET(connection_out, writeset);
402
Damien Miller1383bd82000-04-06 12:32:37 +1000403/* move UP XXX */
Damien Miller95def091999-11-25 00:26:21 +1100404 /* Update maximum file descriptor number, if appropriate. */
405 if (channel_max_fd() > max_fd)
406 max_fd = channel_max_fd();
407
Damien Miller5428f641999-11-25 11:54:57 +1100408 /*
409 * Wait for something to happen. This will suspend the process until
410 * some selected descriptor can be read, written, or has some other
411 * event pending. Note: if you want to implement SSH_MSG_IGNORE
412 * messages to fool traffic analysis, this might be the place to do
413 * it: just have a random timeout for the select, and send a random
414 * SSH_MSG_IGNORE packet when the timeout expires.
415 */
Damien Miller95def091999-11-25 00:26:21 +1100416
417 if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0) {
418 char buf[100];
419 /* Some systems fail to clear these automatically. */
420 FD_ZERO(readset);
421 FD_ZERO(writeset);
422 if (errno == EINTR)
423 return;
424 /* Note: we might still have data in the buffers. */
425 snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
426 buffer_append(&stderr_buffer, buf, strlen(buf));
427 stderr_bytes += strlen(buf);
428 quit_pending = 1;
429 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000430}
431
Damien Miller4af51302000-04-16 11:18:38 +1000432void
Damien Millerad833b32000-08-23 10:46:23 +1000433client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000434{
Damien Miller95def091999-11-25 00:26:21 +1100435 struct winsize oldws, newws;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000436
Damien Miller95def091999-11-25 00:26:21 +1100437 /* Flush stdout and stderr buffers. */
Damien Millerad833b32000-08-23 10:46:23 +1000438 if (buffer_len(bout) > 0)
439 atomicio(write, fileno(stdout), buffer_ptr(bout), buffer_len(bout));
440 if (buffer_len(berr) > 0)
441 atomicio(write, fileno(stderr), buffer_ptr(berr), buffer_len(berr));
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000442
Damien Miller95def091999-11-25 00:26:21 +1100443 leave_raw_mode();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000444
Damien Miller5428f641999-11-25 11:54:57 +1100445 /*
446 * Free (and clear) the buffer to reduce the amount of data that gets
447 * written to swap.
448 */
Damien Millerad833b32000-08-23 10:46:23 +1000449 buffer_free(bin);
450 buffer_free(bout);
451 buffer_free(berr);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000452
Damien Miller95def091999-11-25 00:26:21 +1100453 /* Save old window size. */
454 ioctl(fileno(stdin), TIOCGWINSZ, &oldws);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000455
Damien Miller95def091999-11-25 00:26:21 +1100456 /* Send the suspend signal to the program itself. */
457 kill(getpid(), SIGTSTP);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000458
Damien Miller95def091999-11-25 00:26:21 +1100459 /* Check if the window size has changed. */
460 if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 &&
461 (oldws.ws_row != newws.ws_row ||
462 oldws.ws_col != newws.ws_col ||
463 oldws.ws_xpixel != newws.ws_xpixel ||
464 oldws.ws_ypixel != newws.ws_ypixel))
465 received_window_change_signal = 1;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000466
Damien Miller95def091999-11-25 00:26:21 +1100467 /* OK, we have been continued by the user. Reinitialize buffers. */
Damien Millerad833b32000-08-23 10:46:23 +1000468 buffer_init(bin);
469 buffer_init(bout);
470 buffer_init(berr);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000471
Damien Miller95def091999-11-25 00:26:21 +1100472 enter_raw_mode();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000473}
474
Damien Miller4af51302000-04-16 11:18:38 +1000475void
Damien Miller1383bd82000-04-06 12:32:37 +1000476client_process_net_input(fd_set * readset)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000477{
Damien Miller1383bd82000-04-06 12:32:37 +1000478 int len;
479 char buf[8192];
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000480
Damien Miller5428f641999-11-25 11:54:57 +1100481 /*
482 * Read input from the server, and add any such data to the buffer of
483 * the packet subsystem.
484 */
Damien Miller95def091999-11-25 00:26:21 +1100485 if (FD_ISSET(connection_in, readset)) {
486 /* Read as much as possible. */
487 len = read(connection_in, buf, sizeof(buf));
488 if (len == 0) {
489 /* Received EOF. The remote host has closed the connection. */
490 snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n",
491 host);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000492 buffer_append(&stderr_buffer, buf, strlen(buf));
493 stderr_bytes += strlen(buf);
494 quit_pending = 1;
495 return;
Damien Miller95def091999-11-25 00:26:21 +1100496 }
Damien Miller5428f641999-11-25 11:54:57 +1100497 /*
498 * There is a kernel bug on Solaris that causes select to
499 * sometimes wake up even though there is no data available.
500 */
Damien Miller95def091999-11-25 00:26:21 +1100501 if (len < 0 && errno == EAGAIN)
502 len = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000503
Damien Miller95def091999-11-25 00:26:21 +1100504 if (len < 0) {
505 /* An error has encountered. Perhaps there is a network problem. */
506 snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n",
507 host, strerror(errno));
508 buffer_append(&stderr_buffer, buf, strlen(buf));
509 stderr_bytes += strlen(buf);
510 quit_pending = 1;
511 return;
512 }
513 packet_process_incoming(buf, len);
514 }
Damien Miller1383bd82000-04-06 12:32:37 +1000515}
516
Damien Millerad833b32000-08-23 10:46:23 +1000517/* process the characters one by one */
518int
519process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len)
520{
521 char string[1024];
522 pid_t pid;
523 int bytes = 0;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000524 u_int i;
525 u_char ch;
Damien Millerad833b32000-08-23 10:46:23 +1000526 char *s;
527
528 for (i = 0; i < len; i++) {
529 /* Get one character at a time. */
530 ch = buf[i];
531
532 if (escape_pending) {
533 /* We have previously seen an escape character. */
534 /* Clear the flag now. */
535 escape_pending = 0;
536
537 /* Process the escaped character. */
538 switch (ch) {
539 case '.':
540 /* Terminate the connection. */
541 snprintf(string, sizeof string, "%c.\r\n", escape_char);
542 buffer_append(berr, string, strlen(string));
543 /*stderr_bytes += strlen(string); XXX*/
544
545 quit_pending = 1;
546 return -1;
547
548 case 'Z' - 64:
549 /* Suspend the program. */
550 /* Print a message to that effect to the user. */
551 snprintf(string, sizeof string, "%c^Z [suspend ssh]\r\n", escape_char);
552 buffer_append(berr, string, strlen(string));
553 /*stderr_bytes += strlen(string); XXX*/
554
555 /* Restore terminal modes and suspend. */
556 client_suspend_self(bin, bout, berr);
557
558 /* We have been continued. */
559 continue;
560
561 case '&':
562 /* XXX does not work yet with proto 2 */
563 if (compat20)
564 continue;
565 /*
566 * Detach the program (continue to serve connections,
567 * but put in background and no more new connections).
568 */
569 if (!stdin_eof) {
570 /*
571 * Sending SSH_CMSG_EOF alone does not always appear
572 * to be enough. So we try to send an EOF character
573 * first.
574 */
575 packet_start(SSH_CMSG_STDIN_DATA);
576 packet_put_string("\004", 1);
577 packet_send();
578 /* Close stdin. */
579 stdin_eof = 1;
580 if (buffer_len(bin) == 0) {
581 packet_start(SSH_CMSG_EOF);
582 packet_send();
583 }
584 }
585 /* Restore tty modes. */
586 leave_raw_mode();
587
588 /* Stop listening for new connections. */
589 channel_stop_listening();
590
591 printf("%c& [backgrounded]\n", escape_char);
592
593 /* Fork into background. */
594 pid = fork();
595 if (pid < 0) {
596 error("fork: %.100s", strerror(errno));
597 continue;
598 }
599 if (pid != 0) { /* This is the parent. */
600 /* The parent just exits. */
601 exit(0);
602 }
603 /* The child continues serving connections. */
604 continue; /*XXX ? */
605
606 case '?':
607 snprintf(string, sizeof string,
608"%c?\r\n\
609Supported escape sequences:\r\n\
610~. - terminate connection\r\n\
611~^Z - suspend ssh\r\n\
612~# - list forwarded connections\r\n\
613~& - background ssh (when waiting for connections to terminate)\r\n\
614~? - this message\r\n\
615~~ - send the escape character by typing it twice\r\n\
616(Note that escapes are only recognized immediately after newline.)\r\n",
617 escape_char);
618 buffer_append(berr, string, strlen(string));
619 continue;
620
621 case '#':
622 snprintf(string, sizeof string, "%c#\r\n", escape_char);
623 buffer_append(berr, string, strlen(string));
624 s = channel_open_message();
625 buffer_append(berr, s, strlen(s));
626 xfree(s);
627 continue;
628
629 default:
630 if (ch != escape_char) {
631 buffer_put_char(bin, escape_char);
632 bytes++;
633 }
634 /* Escaped characters fall through here */
635 break;
636 }
637 } else {
638 /*
639 * The previous character was not an escape char. Check if this
640 * is an escape.
641 */
642 if (last_was_cr && ch == escape_char) {
643 /* It is. Set the flag and continue to next character. */
644 escape_pending = 1;
645 continue;
646 }
647 }
648
649 /*
650 * Normal character. Record whether it was a newline,
651 * and append it to the buffer.
652 */
653 last_was_cr = (ch == '\r' || ch == '\n');
654 buffer_put_char(bin, ch);
655 bytes++;
656 }
657 return bytes;
658}
659
Damien Miller4af51302000-04-16 11:18:38 +1000660void
Damien Miller1383bd82000-04-06 12:32:37 +1000661client_process_input(fd_set * readset)
662{
Damien Millerad833b32000-08-23 10:46:23 +1000663 int ret;
Damien Miller166fca82000-04-20 07:42:21 +1000664 int len;
Damien Millerad833b32000-08-23 10:46:23 +1000665 char buf[8192];
Damien Miller1383bd82000-04-06 12:32:37 +1000666
Damien Miller95def091999-11-25 00:26:21 +1100667 /* Read input from stdin. */
668 if (FD_ISSET(fileno(stdin), readset)) {
669 /* Read as much as possible. */
670 len = read(fileno(stdin), buf, sizeof(buf));
671 if (len <= 0) {
Damien Miller5428f641999-11-25 11:54:57 +1100672 /*
673 * Received EOF or error. They are treated
674 * similarly, except that an error message is printed
675 * if it was an error condition.
676 */
Damien Miller95def091999-11-25 00:26:21 +1100677 if (len < 0) {
678 snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno));
679 buffer_append(&stderr_buffer, buf, strlen(buf));
680 stderr_bytes += strlen(buf);
681 }
682 /* Mark that we have seen EOF. */
683 stdin_eof = 1;
Damien Miller5428f641999-11-25 11:54:57 +1100684 /*
685 * Send an EOF message to the server unless there is
686 * data in the buffer. If there is data in the
687 * buffer, no message will be sent now. Code
688 * elsewhere will send the EOF when the buffer
689 * becomes empty if stdin_eof is set.
690 */
Damien Miller95def091999-11-25 00:26:21 +1100691 if (buffer_len(&stdin_buffer) == 0) {
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000692 packet_start(SSH_CMSG_EOF);
693 packet_send();
Damien Miller95def091999-11-25 00:26:21 +1100694 }
695 } else if (escape_char == -1) {
Damien Miller5428f641999-11-25 11:54:57 +1100696 /*
697 * Normal successful read, and no escape character.
698 * Just append the data to buffer.
699 */
Damien Miller95def091999-11-25 00:26:21 +1100700 buffer_append(&stdin_buffer, buf, len);
701 stdin_bytes += len;
702 } else {
Damien Miller5428f641999-11-25 11:54:57 +1100703 /*
704 * Normal, successful read. But we have an escape character
705 * and have to process the characters one by one.
706 */
Damien Millerad833b32000-08-23 10:46:23 +1000707 ret = process_escapes(&stdin_buffer, &stdout_buffer, &stderr_buffer, buf, len);
708 if (ret == -1)
709 return;
710 stdout_bytes += ret;
Damien Miller95def091999-11-25 00:26:21 +1100711 }
712 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000713}
714
Damien Miller4af51302000-04-16 11:18:38 +1000715void
Damien Miller95def091999-11-25 00:26:21 +1100716client_process_output(fd_set * writeset)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000717{
Damien Miller95def091999-11-25 00:26:21 +1100718 int len;
719 char buf[100];
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000720
Damien Miller95def091999-11-25 00:26:21 +1100721 /* Write buffered output to stdout. */
722 if (FD_ISSET(fileno(stdout), writeset)) {
723 /* Write as much data as possible. */
724 len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
Damien Miller037a0dc1999-12-07 15:38:31 +1100725 buffer_len(&stdout_buffer));
Damien Miller95def091999-11-25 00:26:21 +1100726 if (len <= 0) {
727 if (errno == EAGAIN)
728 len = 0;
729 else {
Damien Miller5428f641999-11-25 11:54:57 +1100730 /*
731 * An error or EOF was encountered. Put an
732 * error message to stderr buffer.
733 */
Damien Miller95def091999-11-25 00:26:21 +1100734 snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno));
735 buffer_append(&stderr_buffer, buf, strlen(buf));
736 stderr_bytes += strlen(buf);
737 quit_pending = 1;
738 return;
739 }
740 }
741 /* Consume printed data from the buffer. */
742 buffer_consume(&stdout_buffer, len);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000743 }
Damien Miller95def091999-11-25 00:26:21 +1100744 /* Write buffered output to stderr. */
745 if (FD_ISSET(fileno(stderr), writeset)) {
746 /* Write as much data as possible. */
747 len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
Damien Miller037a0dc1999-12-07 15:38:31 +1100748 buffer_len(&stderr_buffer));
Damien Miller95def091999-11-25 00:26:21 +1100749 if (len <= 0) {
750 if (errno == EAGAIN)
751 len = 0;
752 else {
Damien Miller5428f641999-11-25 11:54:57 +1100753 /* EOF or error, but can't even print error message. */
Damien Miller95def091999-11-25 00:26:21 +1100754 quit_pending = 1;
755 return;
756 }
757 }
758 /* Consume printed characters from the buffer. */
759 buffer_consume(&stderr_buffer, len);
760 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000761}
762
Damien Miller5428f641999-11-25 11:54:57 +1100763/*
Damien Millerb38eff82000-04-01 11:09:21 +1000764 * Get packets from the connection input buffer, and process them as long as
765 * there are packets available.
766 *
767 * Any unknown packets received during the actual
768 * session cause the session to terminate. This is
769 * intended to make debugging easier since no
770 * confirmations are sent. Any compatible protocol
771 * extensions must be negotiated during the
772 * preparatory phase.
773 */
774
Damien Miller4af51302000-04-16 11:18:38 +1000775void
Damien Millerb38eff82000-04-01 11:09:21 +1000776client_process_buffered_input_packets()
777{
Damien Miller62cee002000-09-23 17:15:56 +1100778 dispatch_run(DISPATCH_NONBLOCK, &quit_pending, NULL);
Damien Millerb38eff82000-04-01 11:09:21 +1000779}
780
Damien Millerad833b32000-08-23 10:46:23 +1000781/* scan buf[] for '~' before sending data to the peer */
782
783int
784simple_escape_filter(Channel *c, char *buf, int len)
785{
786 /* XXX we assume c->extended is writeable */
787 return process_escapes(&c->input, &c->output, &c->extended, buf, len);
788}
789
Damien Millerb38eff82000-04-01 11:09:21 +1000790/*
Damien Miller5428f641999-11-25 11:54:57 +1100791 * Implements the interactive session with the server. This is called after
792 * the user has been authenticated, and a command has been started on the
793 * remote host. If escape_char != -1, it is the character used as an escape
794 * character for terminating or suspending the session.
795 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000796
Damien Miller4af51302000-04-16 11:18:38 +1000797int
Damien Millerad833b32000-08-23 10:46:23 +1000798client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000799{
Damien Miller95def091999-11-25 00:26:21 +1100800 double start_time, total_time;
801 int len;
802 char buf[100];
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000803
Damien Miller95def091999-11-25 00:26:21 +1100804 debug("Entering interactive session.");
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000805
Damien Miller95def091999-11-25 00:26:21 +1100806 start_time = get_current_time();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000807
Damien Miller95def091999-11-25 00:26:21 +1100808 /* Initialize variables. */
809 escape_pending = 0;
810 last_was_cr = 1;
811 exit_status = -1;
812 stdin_eof = 0;
813 buffer_high = 64 * 1024;
814 connection_in = packet_get_connection_in();
815 connection_out = packet_get_connection_out();
816 max_fd = connection_in;
817 if (connection_out > max_fd)
818 max_fd = connection_out;
819 stdin_bytes = 0;
820 stdout_bytes = 0;
821 stderr_bytes = 0;
822 quit_pending = 0;
823 escape_char = escape_char_arg;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000824
Damien Miller95def091999-11-25 00:26:21 +1100825 /* Initialize buffers. */
826 buffer_init(&stdin_buffer);
827 buffer_init(&stdout_buffer);
828 buffer_init(&stderr_buffer);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000829
Damien Millerb38eff82000-04-01 11:09:21 +1000830 client_init_dispatch();
831
Damien Miller95def091999-11-25 00:26:21 +1100832 /* Set signal handlers to restore non-blocking mode. */
833 signal(SIGINT, signal_handler);
834 signal(SIGQUIT, signal_handler);
835 signal(SIGTERM, signal_handler);
836 signal(SIGPIPE, SIG_IGN);
837 if (have_pty)
838 signal(SIGWINCH, window_change_handler);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000839
Damien Miller95def091999-11-25 00:26:21 +1100840 if (have_pty)
841 enter_raw_mode();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000842
Damien Millerbe484b52000-07-15 14:14:16 +1000843 /* Check if we should immediately send eof on stdin. */
Damien Miller1383bd82000-04-06 12:32:37 +1000844 if (!compat20)
845 client_check_initial_eof_on_stdin();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000846
Damien Millerad833b32000-08-23 10:46:23 +1000847 if (compat20 && escape_char != -1)
848 channel_register_filter(ssh2_chan_id, simple_escape_filter);
849
Damien Miller95def091999-11-25 00:26:21 +1100850 /* Main loop of the client for the interactive session mode. */
851 while (!quit_pending) {
852 fd_set readset, writeset;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000853
Damien Miller5428f641999-11-25 11:54:57 +1100854 /* Process buffered packets sent by the server. */
Damien Miller95def091999-11-25 00:26:21 +1100855 client_process_buffered_input_packets();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000856
Damien Miller1383bd82000-04-06 12:32:37 +1000857 if (compat20 && !channel_still_open()) {
Damien Millerd3444942000-09-30 14:20:03 +1100858 debug2("!channel_still_open.");
Damien Miller1383bd82000-04-06 12:32:37 +1000859 break;
860 }
861
Damien Miller5428f641999-11-25 11:54:57 +1100862 /*
863 * Make packets of buffered stdin data, and buffer them for
864 * sending to the server.
865 */
Damien Miller1383bd82000-04-06 12:32:37 +1000866 if (!compat20)
867 client_make_packets_from_stdin_data();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000868
Damien Miller5428f641999-11-25 11:54:57 +1100869 /*
870 * Make packets from buffered channel data, and buffer them
871 * for sending to the server.
872 */
Damien Miller95def091999-11-25 00:26:21 +1100873 if (packet_not_very_much_data_to_write())
874 channel_output_poll();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000875
Damien Miller5428f641999-11-25 11:54:57 +1100876 /*
877 * Check if the window size has changed, and buffer a message
878 * about it to the server if so.
879 */
Damien Miller95def091999-11-25 00:26:21 +1100880 client_check_window_change();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000881
Damien Miller95def091999-11-25 00:26:21 +1100882 if (quit_pending)
883 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000884
Damien Miller5428f641999-11-25 11:54:57 +1100885 /*
886 * Wait until we have something to do (something becomes
887 * available on one of the descriptors).
888 */
Damien Miller95def091999-11-25 00:26:21 +1100889 client_wait_until_can_do_something(&readset, &writeset);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000890
Damien Miller95def091999-11-25 00:26:21 +1100891 if (quit_pending)
892 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000893
Damien Miller95def091999-11-25 00:26:21 +1100894 /* Do channel operations. */
895 channel_after_select(&readset, &writeset);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000896
Damien Miller1383bd82000-04-06 12:32:37 +1000897 /* Buffer input from the connection. */
898 client_process_net_input(&readset);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000899
Damien Miller1383bd82000-04-06 12:32:37 +1000900 if (quit_pending)
901 break;
902
903 if (!compat20) {
904 /* Buffer data from stdin */
905 client_process_input(&readset);
906 /*
907 * Process output to stdout and stderr. Output to
908 * the connection is processed elsewhere (above).
909 */
910 client_process_output(&writeset);
911 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000912
Damien Miller5428f641999-11-25 11:54:57 +1100913 /* Send as much buffered packet data as possible to the sender. */
Damien Miller95def091999-11-25 00:26:21 +1100914 if (FD_ISSET(connection_out, &writeset))
915 packet_write_poll();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000916 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000917
Damien Miller95def091999-11-25 00:26:21 +1100918 /* Terminate the session. */
919
920 /* Stop watching for window change. */
921 if (have_pty)
922 signal(SIGWINCH, SIG_DFL);
923
924 /* Stop listening for connections. */
925 channel_stop_listening();
926
Damien Miller5428f641999-11-25 11:54:57 +1100927 /*
928 * In interactive mode (with pseudo tty) display a message indicating
929 * that the connection has been closed.
930 */
Damien Miller95def091999-11-25 00:26:21 +1100931 if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) {
932 snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host);
933 buffer_append(&stderr_buffer, buf, strlen(buf));
934 stderr_bytes += strlen(buf);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000935 }
Damien Miller95def091999-11-25 00:26:21 +1100936 /* Output any buffered data for stdout. */
937 while (buffer_len(&stdout_buffer) > 0) {
938 len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
Damien Miller037a0dc1999-12-07 15:38:31 +1100939 buffer_len(&stdout_buffer));
Damien Miller95def091999-11-25 00:26:21 +1100940 if (len <= 0) {
941 error("Write failed flushing stdout buffer.");
942 break;
943 }
944 buffer_consume(&stdout_buffer, len);
945 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000946
Damien Miller95def091999-11-25 00:26:21 +1100947 /* Output any buffered data for stderr. */
948 while (buffer_len(&stderr_buffer) > 0) {
949 len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
Damien Miller037a0dc1999-12-07 15:38:31 +1100950 buffer_len(&stderr_buffer));
Damien Miller95def091999-11-25 00:26:21 +1100951 if (len <= 0) {
952 error("Write failed flushing stderr buffer.");
953 break;
954 }
955 buffer_consume(&stderr_buffer, len);
956 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000957
Damien Miller95def091999-11-25 00:26:21 +1100958 if (have_pty)
959 leave_raw_mode();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000960
Damien Miller95def091999-11-25 00:26:21 +1100961 /* Clear and free any buffers. */
962 memset(buf, 0, sizeof(buf));
963 buffer_free(&stdin_buffer);
964 buffer_free(&stdout_buffer);
965 buffer_free(&stderr_buffer);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000966
Damien Miller95def091999-11-25 00:26:21 +1100967 /* Report bytes transferred, and transfer rates. */
968 total_time = get_current_time() - start_time;
969 debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds",
970 stdin_bytes, stdout_bytes, stderr_bytes, total_time);
971 if (total_time > 0)
972 debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f",
973 stdin_bytes / total_time, stdout_bytes / total_time,
974 stderr_bytes / total_time);
975
976 /* Return the exit status of the program. */
977 debug("Exit status %d", exit_status);
978 return exit_status;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000979}
Damien Millerb38eff82000-04-01 11:09:21 +1000980
981/*********/
982
983void
Damien Miller62cee002000-09-23 17:15:56 +1100984client_input_stdout_data(int type, int plen, void *ctxt)
Damien Millerb38eff82000-04-01 11:09:21 +1000985{
Ben Lindstrom46c16222000-12-22 01:43:59 +0000986 u_int data_len;
Damien Millerb38eff82000-04-01 11:09:21 +1000987 char *data = packet_get_string(&data_len);
988 packet_integrity_check(plen, 4 + data_len, type);
989 buffer_append(&stdout_buffer, data, data_len);
990 stdout_bytes += data_len;
991 memset(data, 0, data_len);
992 xfree(data);
993}
994void
Damien Miller62cee002000-09-23 17:15:56 +1100995client_input_stderr_data(int type, int plen, void *ctxt)
Damien Millerb38eff82000-04-01 11:09:21 +1000996{
Ben Lindstrom46c16222000-12-22 01:43:59 +0000997 u_int data_len;
Damien Millerb38eff82000-04-01 11:09:21 +1000998 char *data = packet_get_string(&data_len);
999 packet_integrity_check(plen, 4 + data_len, type);
1000 buffer_append(&stderr_buffer, data, data_len);
1001 stdout_bytes += data_len;
1002 memset(data, 0, data_len);
1003 xfree(data);
1004}
1005void
Damien Miller62cee002000-09-23 17:15:56 +11001006client_input_exit_status(int type, int plen, void *ctxt)
Damien Millerb38eff82000-04-01 11:09:21 +10001007{
1008 packet_integrity_check(plen, 4, type);
1009 exit_status = packet_get_int();
1010 /* Acknowledge the exit. */
1011 packet_start(SSH_CMSG_EXIT_CONFIRMATION);
1012 packet_send();
1013 /*
1014 * Must wait for packet to be sent since we are
1015 * exiting the loop.
1016 */
1017 packet_write_wait();
1018 /* Flag that we want to exit. */
1019 quit_pending = 1;
1020}
1021
Damien Miller0bc1bd82000-11-13 22:57:25 +11001022Channel *
1023client_request_forwarded_tcpip(const char *request_type, int rchan)
1024{
1025 Channel* c = NULL;
1026 char *listen_address, *originator_address;
1027 int listen_port, originator_port;
1028 int sock, newch;
1029
1030 /* Get rest of the packet */
1031 listen_address = packet_get_string(NULL);
1032 listen_port = packet_get_int();
1033 originator_address = packet_get_string(NULL);
1034 originator_port = packet_get_int();
1035 packet_done();
1036
1037 debug("client_request_forwarded_tcpip: listen %s port %d, originator %s port %d",
1038 listen_address, listen_port, originator_address, originator_port);
1039
1040 sock = channel_connect_by_listen_adress(listen_port);
1041 if (sock >= 0) {
1042 newch = channel_new("forwarded-tcpip",
Ben Lindstrom7ad97102000-12-06 01:42:49 +00001043 SSH_CHANNEL_CONNECTING, sock, sock, -1,
Damien Miller0bc1bd82000-11-13 22:57:25 +11001044 CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0,
1045 xstrdup(originator_address), 1);
1046 c = channel_lookup(newch);
1047 }
1048 xfree(originator_address);
1049 xfree(listen_address);
1050 return c;
1051}
1052
1053Channel*
1054client_request_x11(const char *request_type, int rchan)
1055{
1056 Channel *c = NULL;
1057 char *originator;
1058 int originator_port;
1059 int sock, newch;
1060
1061 if (!options.forward_x11) {
1062 error("Warning: ssh server tried X11 forwarding.");
1063 error("Warning: this is probably a break in attempt by a malicious server.");
1064 return NULL;
1065 }
1066 originator = packet_get_string(NULL);
1067 if (datafellows & SSH_BUG_X11FWD) {
1068 debug2("buggy server: x11 request w/o originator_port");
1069 originator_port = 0;
1070 } else {
1071 originator_port = packet_get_int();
1072 }
1073 packet_done();
1074 /* XXX check permission */
1075 sock = x11_connect_display();
1076 if (sock >= 0) {
1077 newch = channel_new("x11",
1078 SSH_CHANNEL_X11_OPEN, sock, sock, -1,
1079 CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0,
1080 xstrdup("x11"), 1);
1081 c = channel_lookup(newch);
1082 }
1083 xfree(originator);
1084 return c;
1085}
1086
1087Channel*
1088client_request_agent(const char *request_type, int rchan)
1089{
1090 Channel *c = NULL;
1091 int sock, newch;
1092
1093 if (!options.forward_agent) {
1094 error("Warning: ssh server tried agent forwarding.");
1095 error("Warning: this is probably a break in attempt by a malicious server.");
1096 return NULL;
1097 }
1098 sock = ssh_get_authentication_socket();
1099 if (sock >= 0) {
1100 newch = channel_new("authentication agent connection",
1101 SSH_CHANNEL_OPEN, sock, sock, -1,
1102 CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0,
1103 xstrdup("authentication agent connection"), 1);
1104 c = channel_lookup(newch);
1105 }
1106 return c;
1107}
1108
Damien Millerbd483e72000-04-30 10:00:53 +10001109/* XXXX move to generic input handler */
1110void
Damien Miller62cee002000-09-23 17:15:56 +11001111client_input_channel_open(int type, int plen, void *ctxt)
Damien Millerbd483e72000-04-30 10:00:53 +10001112{
1113 Channel *c = NULL;
1114 char *ctype;
Ben Lindstrom46c16222000-12-22 01:43:59 +00001115 u_int len;
Damien Millerbd483e72000-04-30 10:00:53 +10001116 int rchan;
1117 int rmaxpack;
1118 int rwindow;
1119
1120 ctype = packet_get_string(&len);
1121 rchan = packet_get_int();
1122 rwindow = packet_get_int();
1123 rmaxpack = packet_get_int();
1124
Damien Millere247cc42000-05-07 12:03:14 +10001125 debug("client_input_channel_open: ctype %s rchan %d win %d max %d",
Damien Millerbd483e72000-04-30 10:00:53 +10001126 ctype, rchan, rwindow, rmaxpack);
1127
Damien Miller0bc1bd82000-11-13 22:57:25 +11001128 if (strcmp(ctype, "forwarded-tcpip") == 0) {
1129 c = client_request_forwarded_tcpip(ctype, rchan);
1130 } else if (strcmp(ctype, "x11") == 0) {
1131 c = client_request_x11(ctype, rchan);
1132 } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) {
1133 c = client_request_agent(ctype, rchan);
Damien Millerbd483e72000-04-30 10:00:53 +10001134 }
1135/* XXX duplicate : */
1136 if (c != NULL) {
1137 debug("confirm %s", ctype);
1138 c->remote_id = rchan;
1139 c->remote_window = rwindow;
1140 c->remote_maxpacket = rmaxpack;
1141
1142 packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
1143 packet_put_int(c->remote_id);
1144 packet_put_int(c->self);
1145 packet_put_int(c->local_window);
1146 packet_put_int(c->local_maxpacket);
1147 packet_send();
1148 } else {
1149 debug("failure %s", ctype);
1150 packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
1151 packet_put_int(rchan);
1152 packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED);
1153 packet_put_cstring("bla bla");
1154 packet_put_cstring("");
1155 packet_send();
1156 }
1157 xfree(ctype);
1158}
1159
Damien Miller4af51302000-04-16 11:18:38 +10001160void
Damien Miller1383bd82000-04-06 12:32:37 +10001161client_init_dispatch_20()
1162{
1163 dispatch_init(&dispatch_protocol_error);
1164 dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose);
1165 dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data);
1166 dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof);
1167 dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data);
Damien Millerbd483e72000-04-30 10:00:53 +10001168 dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open);
Damien Miller1383bd82000-04-06 12:32:37 +10001169 dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
1170 dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
1171 dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &channel_input_channel_request);
1172 dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust);
1173}
Damien Miller4af51302000-04-16 11:18:38 +10001174void
Damien Millerb38eff82000-04-01 11:09:21 +10001175client_init_dispatch_13()
1176{
1177 dispatch_init(NULL);
1178 dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close);
1179 dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation);
1180 dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data);
Damien Millerb38eff82000-04-01 11:09:21 +10001181 dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
1182 dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
1183 dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open);
Damien Millerb38eff82000-04-01 11:09:21 +10001184 dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status);
1185 dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data);
1186 dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data);
Damien Miller69b69aa2000-10-28 14:19:58 +11001187
1188 dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ?
1189 &auth_input_open_request : &deny_input_open);
1190 dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ?
1191 &x11_input_open : &deny_input_open);
Damien Millerb38eff82000-04-01 11:09:21 +10001192}
Damien Miller4af51302000-04-16 11:18:38 +10001193void
Damien Millerb38eff82000-04-01 11:09:21 +10001194client_init_dispatch_15()
1195{
1196 client_init_dispatch_13();
1197 dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof);
1198 dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose);
1199}
Damien Miller4af51302000-04-16 11:18:38 +10001200void
Damien Millerb38eff82000-04-01 11:09:21 +10001201client_init_dispatch()
1202{
Damien Miller1383bd82000-04-06 12:32:37 +10001203 if (compat20)
1204 client_init_dispatch_20();
1205 else if (compat13)
Damien Millerb38eff82000-04-01 11:09:21 +10001206 client_init_dispatch_13();
1207 else
1208 client_init_dispatch_15();
1209}
Damien Miller1383bd82000-04-06 12:32:37 +10001210
1211void
1212client_input_channel_req(int id, void *arg)
1213{
1214 Channel *c = NULL;
Ben Lindstrom46c16222000-12-22 01:43:59 +00001215 u_int len;
Damien Miller1383bd82000-04-06 12:32:37 +10001216 int success = 0;
1217 int reply;
1218 char *rtype;
1219
1220 rtype = packet_get_string(&len);
1221 reply = packet_get_char();
1222
Damien Millere247cc42000-05-07 12:03:14 +10001223 debug("client_input_channel_req: rtype %s reply %d", rtype, reply);
Damien Miller1383bd82000-04-06 12:32:37 +10001224
1225 c = channel_lookup(id);
1226 if (c == NULL)
Damien Millere4340be2000-09-16 13:29:08 +11001227 fatal("client_input_channel_req: channel %d: bad channel", id);
Damien Miller1383bd82000-04-06 12:32:37 +10001228
1229 if (session_ident == -1) {
1230 error("client_input_channel_req: no channel %d", id);
1231 } else if (id != session_ident) {
1232 error("client_input_channel_req: bad channel %d != %d",
1233 id, session_ident);
1234 } else if (strcmp(rtype, "exit-status") == 0) {
1235 success = 1;
1236 exit_status = packet_get_int();
Damien Miller4af51302000-04-16 11:18:38 +10001237 packet_done();
Damien Miller1383bd82000-04-06 12:32:37 +10001238 }
1239 if (reply) {
1240 packet_start(success ?
1241 SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
1242 packet_put_int(c->remote_id);
1243 packet_send();
1244 }
1245 xfree(rtype);
1246}
1247
1248void
Ben Lindstrombf555ba2001-01-18 02:04:35 +00001249clientloop_set_session_ident(int id)
Damien Miller1383bd82000-04-06 12:32:37 +10001250{
Ben Lindstrombf555ba2001-01-18 02:04:35 +00001251 debug2("clientloop_set_session_ident: id %d", id);
Damien Miller1383bd82000-04-06 12:32:37 +10001252 session_ident = id;
1253 channel_register_callback(id, SSH2_MSG_CHANNEL_REQUEST,
1254 client_input_channel_req, (void *)0);
1255}