clientloop.c revision 126274
157429Smarkm/* 257429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi> 357429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 457429Smarkm * All rights reserved 565668Skris * The main loop for the interactive session (client side). 660573Skris * 765668Skris * As far as I am concerned, the code I have written for this software 865668Skris * can be used freely for any purpose. Any derived versions of this 965668Skris * software must be clearly marked as such, and if the derived work is 1065668Skris * incompatible with the protocol description in the RFC file, it must be 1165668Skris * called by a name other than "ssh" or "Secure Shell". 1260573Skris * 1360573Skris * 1465668Skris * Copyright (c) 1999 Theo de Raadt. All rights reserved. 1560573Skris * 1665668Skris * Redistribution and use in source and binary forms, with or without 1765668Skris * modification, are permitted provided that the following conditions 1865668Skris * are met: 1965668Skris * 1. Redistributions of source code must retain the above copyright 2065668Skris * notice, this list of conditions and the following disclaimer. 2165668Skris * 2. Redistributions in binary form must reproduce the above copyright 2265668Skris * notice, this list of conditions and the following disclaimer in the 2365668Skris * documentation and/or other materials provided with the distribution. 2465668Skris * 2565668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2665668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2765668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2865668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2965668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 3065668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3165668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3265668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3365668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3465668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3565668Skris * 3665668Skris * 3760573Skris * SSH2 support added by Markus Friedl. 3892555Sdes * Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved. 3965668Skris * 4065668Skris * Redistribution and use in source and binary forms, with or without 4165668Skris * modification, are permitted provided that the following conditions 4265668Skris * are met: 4365668Skris * 1. Redistributions of source code must retain the above copyright 4465668Skris * notice, this list of conditions and the following disclaimer. 4565668Skris * 2. Redistributions in binary form must reproduce the above copyright 4665668Skris * notice, this list of conditions and the following disclaimer in the 4765668Skris * documentation and/or other materials provided with the distribution. 4865668Skris * 4965668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 5065668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 5165668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 5265668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 5365668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 5465668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 5565668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 5665668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 5765668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 5865668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 5957429Smarkm */ 6057429Smarkm 6157429Smarkm#include "includes.h" 62126274SdesRCSID("$OpenBSD: clientloop.c,v 1.117 2003/12/16 15:49:51 markus Exp $"); 6357429Smarkm 6476259Sgreen#include "ssh.h" 6576259Sgreen#include "ssh1.h" 6676259Sgreen#include "ssh2.h" 6757429Smarkm#include "xmalloc.h" 6857429Smarkm#include "packet.h" 6957429Smarkm#include "buffer.h" 7060573Skris#include "compat.h" 7160573Skris#include "channels.h" 7260573Skris#include "dispatch.h" 7365668Skris#include "buffer.h" 7465668Skris#include "bufaux.h" 7576259Sgreen#include "key.h" 7676259Sgreen#include "kex.h" 7776259Sgreen#include "log.h" 7876259Sgreen#include "readconf.h" 7976259Sgreen#include "clientloop.h" 8076259Sgreen#include "authfd.h" 8176259Sgreen#include "atomicio.h" 8276259Sgreen#include "sshtty.h" 8376259Sgreen#include "misc.h" 8498675Sdes#include "readpass.h" 8560573Skris 8669587Sgreen/* import options */ 8768700Sgreenextern Options options; 8868700Sgreen 8957429Smarkm/* Flag indicating that stdin should be redirected from /dev/null. */ 9057429Smarkmextern int stdin_null_flag; 9157429Smarkm 92126274Sdes/* Flag indicating that no shell has been requested */ 93126274Sdesextern int no_shell_flag; 94126274Sdes 9557429Smarkm/* 9657429Smarkm * Name of the host we are connecting to. This is the name given on the 9757429Smarkm * command line, or the HostName specified for the user-supplied name in a 9857429Smarkm * configuration file. 9957429Smarkm */ 10057429Smarkmextern char *host; 10157429Smarkm 10257429Smarkm/* 10357429Smarkm * Flag to indicate that we have received a window change signal which has 10457429Smarkm * not yet been processed. This will cause a message indicating the new 10557429Smarkm * window size to be sent to the server a little later. This is volatile 10657429Smarkm * because this is updated in a signal handler. 10757429Smarkm */ 10892555Sdesstatic volatile sig_atomic_t received_window_change_signal = 0; 10992555Sdesstatic volatile sig_atomic_t received_signal = 0; 11057429Smarkm 11157429Smarkm/* Flag indicating whether the user\'s terminal is in non-blocking mode. */ 11257429Smarkmstatic int in_non_blocking_mode = 0; 11357429Smarkm 11457429Smarkm/* Common data for the client loop code. */ 11565668Skrisstatic int quit_pending; /* Set to non-zero to quit the client loop. */ 11665668Skrisstatic int escape_char; /* Escape character. */ 11757429Smarkmstatic int escape_pending; /* Last character was the escape character */ 11857429Smarkmstatic int last_was_cr; /* Last character was a newline. */ 11957429Smarkmstatic int exit_status; /* Used to store the exit status of the command. */ 12057429Smarkmstatic int stdin_eof; /* EOF has been encountered on standard error. */ 12157429Smarkmstatic Buffer stdin_buffer; /* Buffer for stdin data. */ 12257429Smarkmstatic Buffer stdout_buffer; /* Buffer for stdout data. */ 12357429Smarkmstatic Buffer stderr_buffer; /* Buffer for stderr data. */ 12476259Sgreenstatic u_long stdin_bytes, stdout_bytes, stderr_bytes; 12576259Sgreenstatic u_int buffer_high;/* Soft max buffer size. */ 12657429Smarkmstatic int connection_in; /* Connection to server (input). */ 12757429Smarkmstatic int connection_out; /* Connection to server (output). */ 12876259Sgreenstatic int need_rekeying; /* Set to non-zero if rekeying is requested. */ 12976259Sgreenstatic int session_closed = 0; /* In SSH2: login session closed. */ 130126274Sdesstatic int server_alive_timeouts = 0; 13157429Smarkm 13292555Sdesstatic void client_init_dispatch(void); 13360573Skrisint session_ident = -1; 13460573Skris 13576259Sgreen/*XXX*/ 13676259Sgreenextern Kex *xxx_kex; 13757429Smarkm 13857429Smarkm/* Restores stdin to blocking mode. */ 13957429Smarkm 14092555Sdesstatic void 14176259Sgreenleave_non_blocking(void) 14257429Smarkm{ 14357429Smarkm if (in_non_blocking_mode) { 14457429Smarkm (void) fcntl(fileno(stdin), F_SETFL, 0); 14557429Smarkm in_non_blocking_mode = 0; 14657429Smarkm } 14757429Smarkm} 14857429Smarkm 14957429Smarkm/* Puts stdin terminal in non-blocking mode. */ 15057429Smarkm 15192555Sdesstatic void 15276259Sgreenenter_non_blocking(void) 15357429Smarkm{ 15457429Smarkm in_non_blocking_mode = 1; 15557429Smarkm (void) fcntl(fileno(stdin), F_SETFL, O_NONBLOCK); 15657429Smarkm} 15757429Smarkm 15857429Smarkm/* 15957429Smarkm * Signal handler for the window change signal (SIGWINCH). This just sets a 16057429Smarkm * flag indicating that the window has changed. 16157429Smarkm */ 16257429Smarkm 16392555Sdesstatic void 16457429Smarkmwindow_change_handler(int sig) 16557429Smarkm{ 16657429Smarkm received_window_change_signal = 1; 16757429Smarkm signal(SIGWINCH, window_change_handler); 16857429Smarkm} 16957429Smarkm 17057429Smarkm/* 17157429Smarkm * Signal handler for signals that cause the program to terminate. These 17257429Smarkm * signals must be trapped to restore terminal modes. 17357429Smarkm */ 17457429Smarkm 17592555Sdesstatic void 17657429Smarkmsignal_handler(int sig) 17757429Smarkm{ 17892555Sdes received_signal = sig; 17992555Sdes quit_pending = 1; 18057429Smarkm} 18157429Smarkm 18257429Smarkm/* 18357429Smarkm * Returns current time in seconds from Jan 1, 1970 with the maximum 18457429Smarkm * available resolution. 18557429Smarkm */ 18657429Smarkm 18792555Sdesstatic double 18876259Sgreenget_current_time(void) 18957429Smarkm{ 19057429Smarkm struct timeval tv; 19157429Smarkm gettimeofday(&tv, NULL); 19257429Smarkm return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; 19357429Smarkm} 19457429Smarkm 19557429Smarkm/* 19657429Smarkm * This is called when the interactive is entered. This checks if there is 19757429Smarkm * an EOF coming on stdin. We must check this explicitly, as select() does 19857429Smarkm * not appear to wake up when redirecting from /dev/null. 19957429Smarkm */ 20057429Smarkm 20192555Sdesstatic void 20276259Sgreenclient_check_initial_eof_on_stdin(void) 20357429Smarkm{ 20457429Smarkm int len; 20557429Smarkm char buf[1]; 20657429Smarkm 20757429Smarkm /* 20857429Smarkm * If standard input is to be "redirected from /dev/null", we simply 20957429Smarkm * mark that we have seen an EOF and send an EOF message to the 21057429Smarkm * server. Otherwise, we try to read a single character; it appears 21157429Smarkm * that for some files, such /dev/null, select() never wakes up for 21257429Smarkm * read for this descriptor, which means that we never get EOF. This 21357429Smarkm * way we will get the EOF if stdin comes from /dev/null or similar. 21457429Smarkm */ 21557429Smarkm if (stdin_null_flag) { 21657429Smarkm /* Fake EOF on stdin. */ 21757429Smarkm debug("Sending eof."); 21857429Smarkm stdin_eof = 1; 21957429Smarkm packet_start(SSH_CMSG_EOF); 22057429Smarkm packet_send(); 22157429Smarkm } else { 22257429Smarkm enter_non_blocking(); 22357429Smarkm 22457429Smarkm /* Check for immediate EOF on stdin. */ 22557429Smarkm len = read(fileno(stdin), buf, 1); 22657429Smarkm if (len == 0) { 22757429Smarkm /* EOF. Record that we have seen it and send EOF to server. */ 22857429Smarkm debug("Sending eof."); 22957429Smarkm stdin_eof = 1; 23057429Smarkm packet_start(SSH_CMSG_EOF); 23157429Smarkm packet_send(); 23257429Smarkm } else if (len > 0) { 23357429Smarkm /* 23457429Smarkm * Got data. We must store the data in the buffer, 23557429Smarkm * and also process it as an escape character if 23657429Smarkm * appropriate. 23757429Smarkm */ 23876259Sgreen if ((u_char) buf[0] == escape_char) 23957429Smarkm escape_pending = 1; 24076259Sgreen else 24157429Smarkm buffer_append(&stdin_buffer, buf, 1); 24257429Smarkm } 24357429Smarkm leave_non_blocking(); 24457429Smarkm } 24557429Smarkm} 24657429Smarkm 24757429Smarkm 24857429Smarkm/* 24957429Smarkm * Make packets from buffered stdin data, and buffer them for sending to the 25057429Smarkm * connection. 25157429Smarkm */ 25257429Smarkm 25392555Sdesstatic void 25476259Sgreenclient_make_packets_from_stdin_data(void) 25557429Smarkm{ 25676259Sgreen u_int len; 25757429Smarkm 25857429Smarkm /* Send buffered stdin data to the server. */ 25957429Smarkm while (buffer_len(&stdin_buffer) > 0 && 26092555Sdes packet_not_very_much_data_to_write()) { 26157429Smarkm len = buffer_len(&stdin_buffer); 26257429Smarkm /* Keep the packets at reasonable size. */ 26357429Smarkm if (len > packet_get_maxsize()) 26457429Smarkm len = packet_get_maxsize(); 26557429Smarkm packet_start(SSH_CMSG_STDIN_DATA); 26657429Smarkm packet_put_string(buffer_ptr(&stdin_buffer), len); 26757429Smarkm packet_send(); 26857429Smarkm buffer_consume(&stdin_buffer, len); 26976259Sgreen stdin_bytes += len; 27057429Smarkm /* If we have a pending EOF, send it now. */ 27157429Smarkm if (stdin_eof && buffer_len(&stdin_buffer) == 0) { 27257429Smarkm packet_start(SSH_CMSG_EOF); 27357429Smarkm packet_send(); 27457429Smarkm } 27557429Smarkm } 27657429Smarkm} 27757429Smarkm 27857429Smarkm/* 27957429Smarkm * Checks if the client window has changed, and sends a packet about it to 28057429Smarkm * the server if so. The actual change is detected elsewhere (by a software 28157429Smarkm * interrupt on Unix); this just checks the flag and sends a message if 28257429Smarkm * appropriate. 28357429Smarkm */ 28457429Smarkm 28592555Sdesstatic void 28676259Sgreenclient_check_window_change(void) 28757429Smarkm{ 28860573Skris struct winsize ws; 28957429Smarkm 29060573Skris if (! received_window_change_signal) 29160573Skris return; 29260573Skris /** XXX race */ 29360573Skris received_window_change_signal = 0; 29457429Smarkm 29560573Skris if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) 29660573Skris return; 29760573Skris 29869587Sgreen debug2("client_check_window_change: changed"); 29960573Skris 30060573Skris if (compat20) { 30160573Skris channel_request_start(session_ident, "window-change", 0); 30260573Skris packet_put_int(ws.ws_col); 30360573Skris packet_put_int(ws.ws_row); 30460573Skris packet_put_int(ws.ws_xpixel); 30560573Skris packet_put_int(ws.ws_ypixel); 30660573Skris packet_send(); 30760573Skris } else { 30860573Skris packet_start(SSH_CMSG_WINDOW_SIZE); 30960573Skris packet_put_int(ws.ws_row); 31060573Skris packet_put_int(ws.ws_col); 31160573Skris packet_put_int(ws.ws_xpixel); 31260573Skris packet_put_int(ws.ws_ypixel); 31360573Skris packet_send(); 31457429Smarkm } 31557429Smarkm} 31657429Smarkm 317126274Sdesstatic void 318126274Sdesclient_global_request_reply(int type, u_int32_t seq, void *ctxt) 319126274Sdes{ 320126274Sdes server_alive_timeouts = 0; 321126274Sdes client_global_request_reply_fwd(type, seq, ctxt); 322126274Sdes} 323126274Sdes 324126274Sdesstatic void 325126274Sdesserver_alive_check(void) 326126274Sdes{ 327126274Sdes if (++server_alive_timeouts > options.server_alive_count_max) 328126274Sdes packet_disconnect("Timeout, server not responding."); 329126274Sdes packet_start(SSH2_MSG_GLOBAL_REQUEST); 330126274Sdes packet_put_cstring("keepalive@openssh.com"); 331126274Sdes packet_put_char(1); /* boolean: want reply */ 332126274Sdes packet_send(); 333126274Sdes} 334126274Sdes 33557429Smarkm/* 33657429Smarkm * Waits until the client can do something (some data becomes available on 33757429Smarkm * one of the file descriptors). 33857429Smarkm */ 33957429Smarkm 34092555Sdesstatic void 34176259Sgreenclient_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, 34292555Sdes int *maxfdp, int *nallocp, int rekeying) 34357429Smarkm{ 344126274Sdes struct timeval tv, *tvp; 345126274Sdes int ret; 346126274Sdes 34776259Sgreen /* Add any selections by the channel mechanism. */ 34892555Sdes channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying); 34957429Smarkm 35060573Skris if (!compat20) { 35160573Skris /* Read from the connection, unless our buffers are full. */ 35260573Skris if (buffer_len(&stdout_buffer) < buffer_high && 35360573Skris buffer_len(&stderr_buffer) < buffer_high && 35460573Skris channel_not_very_much_buffered_data()) 35576259Sgreen FD_SET(connection_in, *readsetp); 35660573Skris /* 35760573Skris * Read from stdin, unless we have seen EOF or have very much 35860573Skris * buffered data to send to the server. 35960573Skris */ 36060573Skris if (!stdin_eof && packet_not_very_much_data_to_write()) 36176259Sgreen FD_SET(fileno(stdin), *readsetp); 36260573Skris 36360573Skris /* Select stdout/stderr if have data in buffer. */ 36460573Skris if (buffer_len(&stdout_buffer) > 0) 36576259Sgreen FD_SET(fileno(stdout), *writesetp); 36660573Skris if (buffer_len(&stderr_buffer) > 0) 36776259Sgreen FD_SET(fileno(stderr), *writesetp); 36860573Skris } else { 36992555Sdes /* channel_prepare_select could have closed the last channel */ 37092555Sdes if (session_closed && !channel_still_open() && 37192555Sdes !packet_have_data_to_write()) { 37292555Sdes /* clear mask since we did not call select() */ 37392555Sdes memset(*readsetp, 0, *nallocp); 37492555Sdes memset(*writesetp, 0, *nallocp); 37592555Sdes return; 37692555Sdes } else { 37792555Sdes FD_SET(connection_in, *readsetp); 37892555Sdes } 37960573Skris } 38057429Smarkm 38157429Smarkm /* Select server connection if have data to write to the server. */ 38257429Smarkm if (packet_have_data_to_write()) 38376259Sgreen FD_SET(connection_out, *writesetp); 38457429Smarkm 38557429Smarkm /* 38657429Smarkm * Wait for something to happen. This will suspend the process until 38757429Smarkm * some selected descriptor can be read, written, or has some other 388126274Sdes * event pending. 38957429Smarkm */ 39057429Smarkm 391126274Sdes if (options.server_alive_interval == 0 || !compat20) 392126274Sdes tvp = NULL; 393126274Sdes else { 394126274Sdes tv.tv_sec = options.server_alive_interval; 395126274Sdes tv.tv_usec = 0; 396126274Sdes tvp = &tv; 397126274Sdes } 398126274Sdes ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); 399126274Sdes if (ret < 0) { 40057429Smarkm char buf[100]; 40176259Sgreen 40276259Sgreen /* 40376259Sgreen * We have to clear the select masks, because we return. 40476259Sgreen * We have to return, because the mainloop checks for the flags 40576259Sgreen * set by the signal handlers. 40676259Sgreen */ 40792555Sdes memset(*readsetp, 0, *nallocp); 40892555Sdes memset(*writesetp, 0, *nallocp); 40976259Sgreen 41057429Smarkm if (errno == EINTR) 41157429Smarkm return; 41257429Smarkm /* Note: we might still have data in the buffers. */ 41357429Smarkm snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); 41457429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 41557429Smarkm quit_pending = 1; 416126274Sdes } else if (ret == 0) 417126274Sdes server_alive_check(); 41857429Smarkm} 41957429Smarkm 42092555Sdesstatic void 42165668Skrisclient_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) 42257429Smarkm{ 42357429Smarkm struct winsize oldws, newws; 42457429Smarkm 42557429Smarkm /* Flush stdout and stderr buffers. */ 42665668Skris if (buffer_len(bout) > 0) 427124208Sdes atomicio(vwrite, fileno(stdout), buffer_ptr(bout), buffer_len(bout)); 42865668Skris if (buffer_len(berr) > 0) 429124208Sdes atomicio(vwrite, fileno(stderr), buffer_ptr(berr), buffer_len(berr)); 43057429Smarkm 43157429Smarkm leave_raw_mode(); 43257429Smarkm 43357429Smarkm /* 43457429Smarkm * Free (and clear) the buffer to reduce the amount of data that gets 43557429Smarkm * written to swap. 43657429Smarkm */ 43765668Skris buffer_free(bin); 43865668Skris buffer_free(bout); 43965668Skris buffer_free(berr); 44057429Smarkm 44157429Smarkm /* Save old window size. */ 44257429Smarkm ioctl(fileno(stdin), TIOCGWINSZ, &oldws); 44357429Smarkm 44457429Smarkm /* Send the suspend signal to the program itself. */ 44557429Smarkm kill(getpid(), SIGTSTP); 44657429Smarkm 44757429Smarkm /* Check if the window size has changed. */ 44857429Smarkm if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 && 44957429Smarkm (oldws.ws_row != newws.ws_row || 45092555Sdes oldws.ws_col != newws.ws_col || 45192555Sdes oldws.ws_xpixel != newws.ws_xpixel || 45292555Sdes oldws.ws_ypixel != newws.ws_ypixel)) 45357429Smarkm received_window_change_signal = 1; 45457429Smarkm 45557429Smarkm /* OK, we have been continued by the user. Reinitialize buffers. */ 45665668Skris buffer_init(bin); 45765668Skris buffer_init(bout); 45865668Skris buffer_init(berr); 45957429Smarkm 46057429Smarkm enter_raw_mode(); 46157429Smarkm} 46257429Smarkm 46392555Sdesstatic void 46460573Skrisclient_process_net_input(fd_set * readset) 46557429Smarkm{ 46660573Skris int len; 46760573Skris char buf[8192]; 46857429Smarkm 46957429Smarkm /* 47057429Smarkm * Read input from the server, and add any such data to the buffer of 47157429Smarkm * the packet subsystem. 47257429Smarkm */ 47357429Smarkm if (FD_ISSET(connection_in, readset)) { 47457429Smarkm /* Read as much as possible. */ 47557429Smarkm len = read(connection_in, buf, sizeof(buf)); 47657429Smarkm if (len == 0) { 47757429Smarkm /* Received EOF. The remote host has closed the connection. */ 47857429Smarkm snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n", 47957429Smarkm host); 48057429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 48157429Smarkm quit_pending = 1; 48257429Smarkm return; 48357429Smarkm } 48457429Smarkm /* 48557429Smarkm * There is a kernel bug on Solaris that causes select to 48657429Smarkm * sometimes wake up even though there is no data available. 48757429Smarkm */ 48876259Sgreen if (len < 0 && (errno == EAGAIN || errno == EINTR)) 48957429Smarkm len = 0; 49057429Smarkm 49157429Smarkm if (len < 0) { 49257429Smarkm /* An error has encountered. Perhaps there is a network problem. */ 49357429Smarkm snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n", 49457429Smarkm host, strerror(errno)); 49557429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 49657429Smarkm quit_pending = 1; 49757429Smarkm return; 49857429Smarkm } 49957429Smarkm packet_process_incoming(buf, len); 50057429Smarkm } 50160573Skris} 50260573Skris 50398675Sdesstatic void 50498675Sdesprocess_cmdline(void) 50598675Sdes{ 50698675Sdes void (*handler)(int); 50798675Sdes char *s, *cmd; 50898675Sdes u_short fwd_port, fwd_host_port; 50998675Sdes char buf[1024], sfwd_port[6], sfwd_host_port[6]; 51098675Sdes int local = 0; 51198675Sdes 51298675Sdes leave_raw_mode(); 51398675Sdes handler = signal(SIGINT, SIG_IGN); 51498675Sdes cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); 51598675Sdes if (s == NULL) 51698675Sdes goto out; 51798675Sdes while (*s && isspace(*s)) 51898675Sdes s++; 51998675Sdes if (*s == 0) 52098675Sdes goto out; 52198675Sdes if (strlen(s) < 2 || s[0] != '-' || !(s[1] == 'L' || s[1] == 'R')) { 522124208Sdes logit("Invalid command."); 52398675Sdes goto out; 52498675Sdes } 52598675Sdes if (s[1] == 'L') 52698675Sdes local = 1; 52798675Sdes if (!local && !compat20) { 528124208Sdes logit("Not supported for SSH protocol version 1."); 52998675Sdes goto out; 53098675Sdes } 53198675Sdes s += 2; 53298675Sdes while (*s && isspace(*s)) 53398675Sdes s++; 53498675Sdes 53598675Sdes if (sscanf(s, "%5[0-9]:%255[^:]:%5[0-9]", 53698675Sdes sfwd_port, buf, sfwd_host_port) != 3 && 53798675Sdes sscanf(s, "%5[0-9]/%255[^/]/%5[0-9]", 53898675Sdes sfwd_port, buf, sfwd_host_port) != 3) { 539124208Sdes logit("Bad forwarding specification."); 54098675Sdes goto out; 54198675Sdes } 54298675Sdes if ((fwd_port = a2port(sfwd_port)) == 0 || 54398675Sdes (fwd_host_port = a2port(sfwd_host_port)) == 0) { 544124208Sdes logit("Bad forwarding port(s)."); 54598675Sdes goto out; 54698675Sdes } 54798675Sdes if (local) { 54898675Sdes if (channel_setup_local_fwd_listener(fwd_port, buf, 54998675Sdes fwd_host_port, options.gateway_ports) < 0) { 550124208Sdes logit("Port forwarding failed."); 55198675Sdes goto out; 55298675Sdes } 55398675Sdes } else 55498675Sdes channel_request_remote_forwarding(fwd_port, buf, 55598675Sdes fwd_host_port); 556124208Sdes logit("Forwarding port."); 55798675Sdesout: 55898675Sdes signal(SIGINT, handler); 55998675Sdes enter_raw_mode(); 56098675Sdes if (cmd) 56198675Sdes xfree(cmd); 56298675Sdes} 56398675Sdes 56465668Skris/* process the characters one by one */ 56592555Sdesstatic int 56665668Skrisprocess_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len) 56765668Skris{ 56865668Skris char string[1024]; 56965668Skris pid_t pid; 57065668Skris int bytes = 0; 57176259Sgreen u_int i; 57276259Sgreen u_char ch; 57365668Skris char *s; 57465668Skris 57565668Skris for (i = 0; i < len; i++) { 57665668Skris /* Get one character at a time. */ 57765668Skris ch = buf[i]; 57865668Skris 57965668Skris if (escape_pending) { 58065668Skris /* We have previously seen an escape character. */ 58165668Skris /* Clear the flag now. */ 58265668Skris escape_pending = 0; 58365668Skris 58465668Skris /* Process the escaped character. */ 58565668Skris switch (ch) { 58665668Skris case '.': 58765668Skris /* Terminate the connection. */ 58865668Skris snprintf(string, sizeof string, "%c.\r\n", escape_char); 58965668Skris buffer_append(berr, string, strlen(string)); 59065668Skris 59165668Skris quit_pending = 1; 59265668Skris return -1; 59365668Skris 59465668Skris case 'Z' - 64: 59565668Skris /* Suspend the program. */ 59665668Skris /* Print a message to that effect to the user. */ 59765668Skris snprintf(string, sizeof string, "%c^Z [suspend ssh]\r\n", escape_char); 59865668Skris buffer_append(berr, string, strlen(string)); 59965668Skris 60065668Skris /* Restore terminal modes and suspend. */ 60165668Skris client_suspend_self(bin, bout, berr); 60265668Skris 60365668Skris /* We have been continued. */ 60465668Skris continue; 60565668Skris 606124208Sdes case 'B': 607124208Sdes if (compat20) { 608124208Sdes snprintf(string, sizeof string, 609124208Sdes "%cB\r\n", escape_char); 610124208Sdes buffer_append(berr, string, 611124208Sdes strlen(string)); 612124208Sdes channel_request_start(session_ident, 613124208Sdes "break", 0); 614124208Sdes packet_put_int(1000); 615124208Sdes packet_send(); 616124208Sdes } 617124208Sdes continue; 618124208Sdes 61976259Sgreen case 'R': 62076259Sgreen if (compat20) { 62176259Sgreen if (datafellows & SSH_BUG_NOREKEY) 622124208Sdes logit("Server does not support re-keying"); 62376259Sgreen else 62476259Sgreen need_rekeying = 1; 62576259Sgreen } 62676259Sgreen continue; 62776259Sgreen 62865668Skris case '&': 62965668Skris /* 63065668Skris * Detach the program (continue to serve connections, 63165668Skris * but put in background and no more new connections). 63265668Skris */ 63365668Skris /* Restore tty modes. */ 63465668Skris leave_raw_mode(); 63565668Skris 63665668Skris /* Stop listening for new connections. */ 63765668Skris channel_stop_listening(); 63865668Skris 63992555Sdes snprintf(string, sizeof string, 64092555Sdes "%c& [backgrounded]\n", escape_char); 64192555Sdes buffer_append(berr, string, strlen(string)); 64265668Skris 64365668Skris /* Fork into background. */ 64465668Skris pid = fork(); 64565668Skris if (pid < 0) { 64665668Skris error("fork: %.100s", strerror(errno)); 64765668Skris continue; 64865668Skris } 64965668Skris if (pid != 0) { /* This is the parent. */ 65065668Skris /* The parent just exits. */ 65165668Skris exit(0); 65265668Skris } 65365668Skris /* The child continues serving connections. */ 65492555Sdes if (compat20) { 65592555Sdes buffer_append(bin, "\004", 1); 65692555Sdes /* fake EOF on stdin */ 65792555Sdes return -1; 65892555Sdes } else if (!stdin_eof) { 65992555Sdes /* 66092555Sdes * Sending SSH_CMSG_EOF alone does not always appear 66192555Sdes * to be enough. So we try to send an EOF character 66292555Sdes * first. 66392555Sdes */ 66492555Sdes packet_start(SSH_CMSG_STDIN_DATA); 66592555Sdes packet_put_string("\004", 1); 66692555Sdes packet_send(); 66792555Sdes /* Close stdin. */ 66892555Sdes stdin_eof = 1; 66992555Sdes if (buffer_len(bin) == 0) { 67092555Sdes packet_start(SSH_CMSG_EOF); 67192555Sdes packet_send(); 67292555Sdes } 67392555Sdes } 67492555Sdes continue; 67565668Skris 67665668Skris case '?': 67765668Skris snprintf(string, sizeof string, 67865668Skris"%c?\r\n\ 67965668SkrisSupported escape sequences:\r\n\ 680106121Sdes%c. - terminate connection\r\n\ 681124208Sdes%cB - send a BREAK to the remote system\r\n\ 682106121Sdes%cC - open a command line\r\n\ 683106121Sdes%cR - Request rekey (SSH protocol 2 only)\r\n\ 684106121Sdes%c^Z - suspend ssh\r\n\ 685106121Sdes%c# - list forwarded connections\r\n\ 686106121Sdes%c& - background ssh (when waiting for connections to terminate)\r\n\ 687106121Sdes%c? - this message\r\n\ 688106121Sdes%c%c - send the escape character by typing it twice\r\n\ 68965668Skris(Note that escapes are only recognized immediately after newline.)\r\n", 690106121Sdes escape_char, escape_char, escape_char, escape_char, 691106121Sdes escape_char, escape_char, escape_char, escape_char, 692124208Sdes escape_char, escape_char, escape_char); 69365668Skris buffer_append(berr, string, strlen(string)); 69465668Skris continue; 69565668Skris 69665668Skris case '#': 69765668Skris snprintf(string, sizeof string, "%c#\r\n", escape_char); 69865668Skris buffer_append(berr, string, strlen(string)); 69965668Skris s = channel_open_message(); 70065668Skris buffer_append(berr, s, strlen(s)); 70165668Skris xfree(s); 70265668Skris continue; 70365668Skris 70498675Sdes case 'C': 70598675Sdes process_cmdline(); 70698675Sdes continue; 70798675Sdes 70865668Skris default: 70965668Skris if (ch != escape_char) { 71065668Skris buffer_put_char(bin, escape_char); 71165668Skris bytes++; 71265668Skris } 71365668Skris /* Escaped characters fall through here */ 71465668Skris break; 71565668Skris } 71665668Skris } else { 71765668Skris /* 71865668Skris * The previous character was not an escape char. Check if this 71965668Skris * is an escape. 72065668Skris */ 72165668Skris if (last_was_cr && ch == escape_char) { 72265668Skris /* It is. Set the flag and continue to next character. */ 72365668Skris escape_pending = 1; 72465668Skris continue; 72565668Skris } 72665668Skris } 72765668Skris 72865668Skris /* 72965668Skris * Normal character. Record whether it was a newline, 73065668Skris * and append it to the buffer. 73165668Skris */ 73265668Skris last_was_cr = (ch == '\r' || ch == '\n'); 73365668Skris buffer_put_char(bin, ch); 73465668Skris bytes++; 73565668Skris } 73665668Skris return bytes; 73765668Skris} 73865668Skris 73992555Sdesstatic void 74060573Skrisclient_process_input(fd_set * readset) 74160573Skris{ 74260573Skris int len; 74365668Skris char buf[8192]; 74460573Skris 74557429Smarkm /* Read input from stdin. */ 74657429Smarkm if (FD_ISSET(fileno(stdin), readset)) { 74757429Smarkm /* Read as much as possible. */ 74857429Smarkm len = read(fileno(stdin), buf, sizeof(buf)); 74976259Sgreen if (len < 0 && (errno == EAGAIN || errno == EINTR)) 75076259Sgreen return; /* we'll try again later */ 75157429Smarkm if (len <= 0) { 75257429Smarkm /* 75357429Smarkm * Received EOF or error. They are treated 75457429Smarkm * similarly, except that an error message is printed 75557429Smarkm * if it was an error condition. 75657429Smarkm */ 75757429Smarkm if (len < 0) { 75857429Smarkm snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno)); 75957429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 76057429Smarkm } 76157429Smarkm /* Mark that we have seen EOF. */ 76257429Smarkm stdin_eof = 1; 76357429Smarkm /* 76457429Smarkm * Send an EOF message to the server unless there is 76557429Smarkm * data in the buffer. If there is data in the 76657429Smarkm * buffer, no message will be sent now. Code 76757429Smarkm * elsewhere will send the EOF when the buffer 76857429Smarkm * becomes empty if stdin_eof is set. 76957429Smarkm */ 77057429Smarkm if (buffer_len(&stdin_buffer) == 0) { 77157429Smarkm packet_start(SSH_CMSG_EOF); 77257429Smarkm packet_send(); 77357429Smarkm } 77492555Sdes } else if (escape_char == SSH_ESCAPECHAR_NONE) { 77557429Smarkm /* 77657429Smarkm * Normal successful read, and no escape character. 77757429Smarkm * Just append the data to buffer. 77857429Smarkm */ 77957429Smarkm buffer_append(&stdin_buffer, buf, len); 78057429Smarkm } else { 78157429Smarkm /* 78257429Smarkm * Normal, successful read. But we have an escape character 78357429Smarkm * and have to process the characters one by one. 78457429Smarkm */ 78576259Sgreen if (process_escapes(&stdin_buffer, &stdout_buffer, 78676259Sgreen &stderr_buffer, buf, len) == -1) 78765668Skris return; 78857429Smarkm } 78957429Smarkm } 79057429Smarkm} 79157429Smarkm 79292555Sdesstatic void 79357429Smarkmclient_process_output(fd_set * writeset) 79457429Smarkm{ 79557429Smarkm int len; 79657429Smarkm char buf[100]; 79757429Smarkm 79857429Smarkm /* Write buffered output to stdout. */ 79957429Smarkm if (FD_ISSET(fileno(stdout), writeset)) { 80057429Smarkm /* Write as much data as possible. */ 80157429Smarkm len = write(fileno(stdout), buffer_ptr(&stdout_buffer), 80257429Smarkm buffer_len(&stdout_buffer)); 80357429Smarkm if (len <= 0) { 80476259Sgreen if (errno == EINTR || errno == EAGAIN) 80557429Smarkm len = 0; 80657429Smarkm else { 80757429Smarkm /* 80857429Smarkm * An error or EOF was encountered. Put an 80957429Smarkm * error message to stderr buffer. 81057429Smarkm */ 81157429Smarkm snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno)); 81257429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 81357429Smarkm quit_pending = 1; 81457429Smarkm return; 81557429Smarkm } 81657429Smarkm } 81757429Smarkm /* Consume printed data from the buffer. */ 81857429Smarkm buffer_consume(&stdout_buffer, len); 81976259Sgreen stdout_bytes += len; 82057429Smarkm } 82157429Smarkm /* Write buffered output to stderr. */ 82257429Smarkm if (FD_ISSET(fileno(stderr), writeset)) { 82357429Smarkm /* Write as much data as possible. */ 82457429Smarkm len = write(fileno(stderr), buffer_ptr(&stderr_buffer), 82557429Smarkm buffer_len(&stderr_buffer)); 82657429Smarkm if (len <= 0) { 82776259Sgreen if (errno == EINTR || errno == EAGAIN) 82857429Smarkm len = 0; 82957429Smarkm else { 83057429Smarkm /* EOF or error, but can't even print error message. */ 83157429Smarkm quit_pending = 1; 83257429Smarkm return; 83357429Smarkm } 83457429Smarkm } 83557429Smarkm /* Consume printed characters from the buffer. */ 83657429Smarkm buffer_consume(&stderr_buffer, len); 83776259Sgreen stderr_bytes += len; 83857429Smarkm } 83957429Smarkm} 84057429Smarkm 84157429Smarkm/* 84260573Skris * Get packets from the connection input buffer, and process them as long as 84360573Skris * there are packets available. 84460573Skris * 84560573Skris * Any unknown packets received during the actual 84660573Skris * session cause the session to terminate. This is 84760573Skris * intended to make debugging easier since no 84860573Skris * confirmations are sent. Any compatible protocol 84960573Skris * extensions must be negotiated during the 85060573Skris * preparatory phase. 85160573Skris */ 85260573Skris 85392555Sdesstatic void 85476259Sgreenclient_process_buffered_input_packets(void) 85560573Skris{ 85676259Sgreen dispatch_run(DISPATCH_NONBLOCK, &quit_pending, compat20 ? xxx_kex : NULL); 85760573Skris} 85860573Skris 85965668Skris/* scan buf[] for '~' before sending data to the peer */ 86065668Skris 86192555Sdesstatic int 86265668Skrissimple_escape_filter(Channel *c, char *buf, int len) 86365668Skris{ 86465668Skris /* XXX we assume c->extended is writeable */ 86565668Skris return process_escapes(&c->input, &c->output, &c->extended, buf, len); 86665668Skris} 86765668Skris 86892555Sdesstatic void 86976259Sgreenclient_channel_closed(int id, void *arg) 87076259Sgreen{ 87176259Sgreen if (id != session_ident) 87276259Sgreen error("client_channel_closed: id %d != session_ident %d", 87376259Sgreen id, session_ident); 87492555Sdes channel_cancel_cleanup(id); 87576259Sgreen session_closed = 1; 876126274Sdes leave_raw_mode(); 87776259Sgreen} 87876259Sgreen 87960573Skris/* 88057429Smarkm * Implements the interactive session with the server. This is called after 88157429Smarkm * the user has been authenticated, and a command has been started on the 88292555Sdes * remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character 88392555Sdes * used as an escape character for terminating or suspending the session. 88457429Smarkm */ 88557429Smarkm 88660573Skrisint 88765668Skrisclient_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) 88857429Smarkm{ 88976259Sgreen fd_set *readset = NULL, *writeset = NULL; 89057429Smarkm double start_time, total_time; 89192555Sdes int max_fd = 0, max_fd2 = 0, len, rekeying = 0, nalloc = 0; 89257429Smarkm char buf[100]; 89357429Smarkm 89457429Smarkm debug("Entering interactive session."); 89557429Smarkm 89657429Smarkm start_time = get_current_time(); 89757429Smarkm 89857429Smarkm /* Initialize variables. */ 89957429Smarkm escape_pending = 0; 90057429Smarkm last_was_cr = 1; 90157429Smarkm exit_status = -1; 90257429Smarkm stdin_eof = 0; 90357429Smarkm buffer_high = 64 * 1024; 90457429Smarkm connection_in = packet_get_connection_in(); 90557429Smarkm connection_out = packet_get_connection_out(); 90676259Sgreen max_fd = MAX(connection_in, connection_out); 90776259Sgreen 90876259Sgreen if (!compat20) { 90976259Sgreen /* enable nonblocking unless tty */ 91076259Sgreen if (!isatty(fileno(stdin))) 91176259Sgreen set_nonblock(fileno(stdin)); 91276259Sgreen if (!isatty(fileno(stdout))) 91376259Sgreen set_nonblock(fileno(stdout)); 91476259Sgreen if (!isatty(fileno(stderr))) 91576259Sgreen set_nonblock(fileno(stderr)); 91676259Sgreen max_fd = MAX(max_fd, fileno(stdin)); 91776259Sgreen max_fd = MAX(max_fd, fileno(stdout)); 91876259Sgreen max_fd = MAX(max_fd, fileno(stderr)); 91976259Sgreen } 92057429Smarkm stdin_bytes = 0; 92157429Smarkm stdout_bytes = 0; 92257429Smarkm stderr_bytes = 0; 92357429Smarkm quit_pending = 0; 92457429Smarkm escape_char = escape_char_arg; 92557429Smarkm 92657429Smarkm /* Initialize buffers. */ 92757429Smarkm buffer_init(&stdin_buffer); 92857429Smarkm buffer_init(&stdout_buffer); 92957429Smarkm buffer_init(&stderr_buffer); 93057429Smarkm 93160573Skris client_init_dispatch(); 93260573Skris 933113908Sdes /* 934113908Sdes * Set signal handlers, (e.g. to restore non-blocking mode) 935113908Sdes * but don't overwrite SIG_IGN, matches behaviour from rsh(1) 936113908Sdes */ 937113908Sdes if (signal(SIGINT, SIG_IGN) != SIG_IGN) 938113908Sdes signal(SIGINT, signal_handler); 939113908Sdes if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) 940113908Sdes signal(SIGQUIT, signal_handler); 941113908Sdes if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 942113908Sdes signal(SIGTERM, signal_handler); 94357429Smarkm if (have_pty) 94457429Smarkm signal(SIGWINCH, window_change_handler); 94557429Smarkm 94657429Smarkm if (have_pty) 94757429Smarkm enter_raw_mode(); 94857429Smarkm 94976259Sgreen if (compat20) { 95076259Sgreen session_ident = ssh2_chan_id; 95192555Sdes if (escape_char != SSH_ESCAPECHAR_NONE) 95276259Sgreen channel_register_filter(session_ident, 95376259Sgreen simple_escape_filter); 95476259Sgreen if (session_ident != -1) 95576259Sgreen channel_register_cleanup(session_ident, 95676259Sgreen client_channel_closed); 95776259Sgreen } else { 95876259Sgreen /* Check if we should immediately send eof on stdin. */ 95960573Skris client_check_initial_eof_on_stdin(); 96076259Sgreen } 96157429Smarkm 96257429Smarkm /* Main loop of the client for the interactive session mode. */ 96357429Smarkm while (!quit_pending) { 96457429Smarkm 96557429Smarkm /* Process buffered packets sent by the server. */ 96657429Smarkm client_process_buffered_input_packets(); 96757429Smarkm 96876259Sgreen if (compat20 && session_closed && !channel_still_open()) 96960573Skris break; 97060573Skris 97176259Sgreen rekeying = (xxx_kex != NULL && !xxx_kex->done); 97257429Smarkm 97376259Sgreen if (rekeying) { 97476259Sgreen debug("rekeying in progress"); 97576259Sgreen } else { 97676259Sgreen /* 97776259Sgreen * Make packets of buffered stdin data, and buffer 97876259Sgreen * them for sending to the server. 97976259Sgreen */ 98076259Sgreen if (!compat20) 98176259Sgreen client_make_packets_from_stdin_data(); 98257429Smarkm 98376259Sgreen /* 98476259Sgreen * Make packets from buffered channel data, and 98576259Sgreen * enqueue them for sending to the server. 98676259Sgreen */ 98776259Sgreen if (packet_not_very_much_data_to_write()) 98876259Sgreen channel_output_poll(); 98957429Smarkm 99076259Sgreen /* 99176259Sgreen * Check if the window size has changed, and buffer a 99276259Sgreen * message about it to the server if so. 99376259Sgreen */ 99476259Sgreen client_check_window_change(); 99557429Smarkm 99676259Sgreen if (quit_pending) 99776259Sgreen break; 99876259Sgreen } 99957429Smarkm /* 100057429Smarkm * Wait until we have something to do (something becomes 100157429Smarkm * available on one of the descriptors). 100257429Smarkm */ 100392555Sdes max_fd2 = max_fd; 100476259Sgreen client_wait_until_can_do_something(&readset, &writeset, 100592555Sdes &max_fd2, &nalloc, rekeying); 100657429Smarkm 100757429Smarkm if (quit_pending) 100857429Smarkm break; 100957429Smarkm 101076259Sgreen /* Do channel operations unless rekeying in progress. */ 101176259Sgreen if (!rekeying) { 101276259Sgreen channel_after_select(readset, writeset); 1013124208Sdes if (need_rekeying || packet_need_rekeying()) { 1014124208Sdes debug("need rekeying"); 101576259Sgreen xxx_kex->done = 0; 101676259Sgreen kex_send_kexinit(xxx_kex); 101776259Sgreen need_rekeying = 0; 101876259Sgreen } 101976259Sgreen } 102076259Sgreen 102160573Skris /* Buffer input from the connection. */ 102276259Sgreen client_process_net_input(readset); 102357429Smarkm 102460573Skris if (quit_pending) 102560573Skris break; 102657429Smarkm 102760573Skris if (!compat20) { 102860573Skris /* Buffer data from stdin */ 102976259Sgreen client_process_input(readset); 103060573Skris /* 103160573Skris * Process output to stdout and stderr. Output to 103260573Skris * the connection is processed elsewhere (above). 103360573Skris */ 103476259Sgreen client_process_output(writeset); 103560573Skris } 103660573Skris 103757429Smarkm /* Send as much buffered packet data as possible to the sender. */ 103876259Sgreen if (FD_ISSET(connection_out, writeset)) 103957429Smarkm packet_write_poll(); 104057429Smarkm } 104176259Sgreen if (readset) 104276259Sgreen xfree(readset); 104376259Sgreen if (writeset) 104476259Sgreen xfree(writeset); 104557429Smarkm 104657429Smarkm /* Terminate the session. */ 104757429Smarkm 104857429Smarkm /* Stop watching for window change. */ 104957429Smarkm if (have_pty) 105057429Smarkm signal(SIGWINCH, SIG_DFL); 105157429Smarkm 105292555Sdes channel_free_all(); 105357429Smarkm 105492555Sdes if (have_pty) 105592555Sdes leave_raw_mode(); 105692555Sdes 105792555Sdes /* restore blocking io */ 105892555Sdes if (!isatty(fileno(stdin))) 105992555Sdes unset_nonblock(fileno(stdin)); 106092555Sdes if (!isatty(fileno(stdout))) 106192555Sdes unset_nonblock(fileno(stdout)); 106292555Sdes if (!isatty(fileno(stderr))) 106392555Sdes unset_nonblock(fileno(stderr)); 106492555Sdes 1065126274Sdes /* 1066126274Sdes * If there was no shell or command requested, there will be no remote 1067126274Sdes * exit status to be returned. In that case, clear error code if the 1068126274Sdes * connection was deliberately terminated at this end. 1069126274Sdes */ 1070126274Sdes if (no_shell_flag && received_signal == SIGTERM) { 1071126274Sdes received_signal = 0; 1072126274Sdes exit_status = 0; 107392555Sdes } 107492555Sdes 1075126274Sdes if (received_signal) 1076126274Sdes fatal("Killed by signal %d.", (int) received_signal); 1077126274Sdes 107857429Smarkm /* 107957429Smarkm * In interactive mode (with pseudo tty) display a message indicating 108057429Smarkm * that the connection has been closed. 108157429Smarkm */ 108257429Smarkm if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { 108357429Smarkm snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host); 108457429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 108557429Smarkm } 108692555Sdes 108757429Smarkm /* Output any buffered data for stdout. */ 108857429Smarkm while (buffer_len(&stdout_buffer) > 0) { 108957429Smarkm len = write(fileno(stdout), buffer_ptr(&stdout_buffer), 109057429Smarkm buffer_len(&stdout_buffer)); 109157429Smarkm if (len <= 0) { 109257429Smarkm error("Write failed flushing stdout buffer."); 109357429Smarkm break; 109457429Smarkm } 109557429Smarkm buffer_consume(&stdout_buffer, len); 109676259Sgreen stdout_bytes += len; 109757429Smarkm } 109857429Smarkm 109957429Smarkm /* Output any buffered data for stderr. */ 110057429Smarkm while (buffer_len(&stderr_buffer) > 0) { 110157429Smarkm len = write(fileno(stderr), buffer_ptr(&stderr_buffer), 110257429Smarkm buffer_len(&stderr_buffer)); 110357429Smarkm if (len <= 0) { 110457429Smarkm error("Write failed flushing stderr buffer."); 110557429Smarkm break; 110657429Smarkm } 110757429Smarkm buffer_consume(&stderr_buffer, len); 110876259Sgreen stderr_bytes += len; 110957429Smarkm } 111057429Smarkm 111157429Smarkm /* Clear and free any buffers. */ 111257429Smarkm memset(buf, 0, sizeof(buf)); 111357429Smarkm buffer_free(&stdin_buffer); 111457429Smarkm buffer_free(&stdout_buffer); 111557429Smarkm buffer_free(&stderr_buffer); 111657429Smarkm 111757429Smarkm /* Report bytes transferred, and transfer rates. */ 111857429Smarkm total_time = get_current_time() - start_time; 111957429Smarkm debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds", 112092555Sdes stdin_bytes, stdout_bytes, stderr_bytes, total_time); 112157429Smarkm if (total_time > 0) 112257429Smarkm debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f", 112392555Sdes stdin_bytes / total_time, stdout_bytes / total_time, 112492555Sdes stderr_bytes / total_time); 112557429Smarkm 112657429Smarkm /* Return the exit status of the program. */ 112757429Smarkm debug("Exit status %d", exit_status); 112857429Smarkm return exit_status; 112957429Smarkm} 113060573Skris 113160573Skris/*********/ 113260573Skris 113392555Sdesstatic void 113492555Sdesclient_input_stdout_data(int type, u_int32_t seq, void *ctxt) 113560573Skris{ 113676259Sgreen u_int data_len; 113760573Skris char *data = packet_get_string(&data_len); 113892555Sdes packet_check_eom(); 113960573Skris buffer_append(&stdout_buffer, data, data_len); 114060573Skris memset(data, 0, data_len); 114160573Skris xfree(data); 114260573Skris} 114392555Sdesstatic void 114492555Sdesclient_input_stderr_data(int type, u_int32_t seq, void *ctxt) 114560573Skris{ 114676259Sgreen u_int data_len; 114760573Skris char *data = packet_get_string(&data_len); 114892555Sdes packet_check_eom(); 114960573Skris buffer_append(&stderr_buffer, data, data_len); 115060573Skris memset(data, 0, data_len); 115160573Skris xfree(data); 115260573Skris} 115392555Sdesstatic void 115492555Sdesclient_input_exit_status(int type, u_int32_t seq, void *ctxt) 115560573Skris{ 115660573Skris exit_status = packet_get_int(); 115792555Sdes packet_check_eom(); 115860573Skris /* Acknowledge the exit. */ 115960573Skris packet_start(SSH_CMSG_EXIT_CONFIRMATION); 116060573Skris packet_send(); 116160573Skris /* 116260573Skris * Must wait for packet to be sent since we are 116360573Skris * exiting the loop. 116460573Skris */ 116560573Skris packet_write_wait(); 116660573Skris /* Flag that we want to exit. */ 116760573Skris quit_pending = 1; 116860573Skris} 1169126274Sdesstatic void 1170126274Sdesclient_input_agent_open(int type, u_int32_t seq, void *ctxt) 1171126274Sdes{ 1172126274Sdes Channel *c = NULL; 1173126274Sdes int remote_id, sock; 117460573Skris 1175126274Sdes /* Read the remote channel number from the message. */ 1176126274Sdes remote_id = packet_get_int(); 1177126274Sdes packet_check_eom(); 1178126274Sdes 1179126274Sdes /* 1180126274Sdes * Get a connection to the local authentication agent (this may again 1181126274Sdes * get forwarded). 1182126274Sdes */ 1183126274Sdes sock = ssh_get_authentication_socket(); 1184126274Sdes 1185126274Sdes /* 1186126274Sdes * If we could not connect the agent, send an error message back to 1187126274Sdes * the server. This should never happen unless the agent dies, 1188126274Sdes * because authentication forwarding is only enabled if we have an 1189126274Sdes * agent. 1190126274Sdes */ 1191126274Sdes if (sock >= 0) { 1192126274Sdes c = channel_new("", SSH_CHANNEL_OPEN, sock, sock, 1193126274Sdes -1, 0, 0, 0, "authentication agent connection", 1); 1194126274Sdes c->remote_id = remote_id; 1195126274Sdes c->force_drain = 1; 1196126274Sdes } 1197126274Sdes if (c == NULL) { 1198126274Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 1199126274Sdes packet_put_int(remote_id); 1200126274Sdes } else { 1201126274Sdes /* Send a confirmation to the remote host. */ 1202126274Sdes debug("Forwarding authentication connection."); 1203126274Sdes packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 1204126274Sdes packet_put_int(remote_id); 1205126274Sdes packet_put_int(c->self); 1206126274Sdes } 1207126274Sdes packet_send(); 1208126274Sdes} 1209126274Sdes 121092555Sdesstatic Channel * 121176259Sgreenclient_request_forwarded_tcpip(const char *request_type, int rchan) 121276259Sgreen{ 1213106121Sdes Channel *c = NULL; 121476259Sgreen char *listen_address, *originator_address; 121576259Sgreen int listen_port, originator_port; 121692555Sdes int sock; 121776259Sgreen 121876259Sgreen /* Get rest of the packet */ 121976259Sgreen listen_address = packet_get_string(NULL); 122076259Sgreen listen_port = packet_get_int(); 122176259Sgreen originator_address = packet_get_string(NULL); 122276259Sgreen originator_port = packet_get_int(); 122392555Sdes packet_check_eom(); 122476259Sgreen 122576259Sgreen debug("client_request_forwarded_tcpip: listen %s port %d, originator %s port %d", 122676259Sgreen listen_address, listen_port, originator_address, originator_port); 122776259Sgreen 122892555Sdes sock = channel_connect_by_listen_address(listen_port); 122992555Sdes if (sock < 0) { 123092555Sdes xfree(originator_address); 123192555Sdes xfree(listen_address); 123292555Sdes return NULL; 123376259Sgreen } 123492555Sdes c = channel_new("forwarded-tcpip", 123592555Sdes SSH_CHANNEL_CONNECTING, sock, sock, -1, 123692555Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0, 1237124208Sdes originator_address, 1); 123876259Sgreen xfree(originator_address); 123976259Sgreen xfree(listen_address); 124076259Sgreen return c; 124176259Sgreen} 124276259Sgreen 1243106121Sdesstatic Channel * 124476259Sgreenclient_request_x11(const char *request_type, int rchan) 124576259Sgreen{ 124676259Sgreen Channel *c = NULL; 124776259Sgreen char *originator; 124876259Sgreen int originator_port; 124992555Sdes int sock; 125076259Sgreen 125176259Sgreen if (!options.forward_x11) { 125276259Sgreen error("Warning: ssh server tried X11 forwarding."); 125376259Sgreen error("Warning: this is probably a break in attempt by a malicious server."); 125476259Sgreen return NULL; 125576259Sgreen } 125676259Sgreen originator = packet_get_string(NULL); 125776259Sgreen if (datafellows & SSH_BUG_X11FWD) { 125876259Sgreen debug2("buggy server: x11 request w/o originator_port"); 125976259Sgreen originator_port = 0; 126076259Sgreen } else { 126176259Sgreen originator_port = packet_get_int(); 126276259Sgreen } 126392555Sdes packet_check_eom(); 126476259Sgreen /* XXX check permission */ 126576259Sgreen debug("client_request_x11: request from %s %d", originator, 126676259Sgreen originator_port); 126792555Sdes xfree(originator); 126876259Sgreen sock = x11_connect_display(); 126992555Sdes if (sock < 0) 127092555Sdes return NULL; 127192555Sdes c = channel_new("x11", 127292555Sdes SSH_CHANNEL_X11_OPEN, sock, sock, -1, 1273124208Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); 127492555Sdes c->force_drain = 1; 127576259Sgreen return c; 127676259Sgreen} 127776259Sgreen 1278106121Sdesstatic Channel * 127976259Sgreenclient_request_agent(const char *request_type, int rchan) 128076259Sgreen{ 128176259Sgreen Channel *c = NULL; 128292555Sdes int sock; 128376259Sgreen 128476259Sgreen if (!options.forward_agent) { 128576259Sgreen error("Warning: ssh server tried agent forwarding."); 128676259Sgreen error("Warning: this is probably a break in attempt by a malicious server."); 128776259Sgreen return NULL; 128876259Sgreen } 128976259Sgreen sock = ssh_get_authentication_socket(); 129092555Sdes if (sock < 0) 129192555Sdes return NULL; 129292555Sdes c = channel_new("authentication agent connection", 129392555Sdes SSH_CHANNEL_OPEN, sock, sock, -1, 129492555Sdes CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0, 1295124208Sdes "authentication agent connection", 1); 129692555Sdes c->force_drain = 1; 129776259Sgreen return c; 129876259Sgreen} 129976259Sgreen 130060573Skris/* XXXX move to generic input handler */ 130192555Sdesstatic void 130292555Sdesclient_input_channel_open(int type, u_int32_t seq, void *ctxt) 130360573Skris{ 130460573Skris Channel *c = NULL; 130560573Skris char *ctype; 130660573Skris int rchan; 130799060Sdes u_int rmaxpack, rwindow, len; 130860573Skris 130960573Skris ctype = packet_get_string(&len); 131060573Skris rchan = packet_get_int(); 131160573Skris rwindow = packet_get_int(); 131260573Skris rmaxpack = packet_get_int(); 131360573Skris 131460573Skris debug("client_input_channel_open: ctype %s rchan %d win %d max %d", 131560573Skris ctype, rchan, rwindow, rmaxpack); 131660573Skris 131776259Sgreen if (strcmp(ctype, "forwarded-tcpip") == 0) { 131876259Sgreen c = client_request_forwarded_tcpip(ctype, rchan); 131976259Sgreen } else if (strcmp(ctype, "x11") == 0) { 132076259Sgreen c = client_request_x11(ctype, rchan); 132176259Sgreen } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { 132276259Sgreen c = client_request_agent(ctype, rchan); 132360573Skris } 132460573Skris/* XXX duplicate : */ 132560573Skris if (c != NULL) { 132660573Skris debug("confirm %s", ctype); 132760573Skris c->remote_id = rchan; 132860573Skris c->remote_window = rwindow; 132960573Skris c->remote_maxpacket = rmaxpack; 133092555Sdes if (c->type != SSH_CHANNEL_CONNECTING) { 133192555Sdes packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 133292555Sdes packet_put_int(c->remote_id); 133392555Sdes packet_put_int(c->self); 133492555Sdes packet_put_int(c->local_window); 133592555Sdes packet_put_int(c->local_maxpacket); 133692555Sdes packet_send(); 133792555Sdes } 133860573Skris } else { 133960573Skris debug("failure %s", ctype); 134060573Skris packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 134160573Skris packet_put_int(rchan); 134260573Skris packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); 134392555Sdes if (!(datafellows & SSH_BUG_OPENFAILURE)) { 134492555Sdes packet_put_cstring("open failed"); 134592555Sdes packet_put_cstring(""); 134692555Sdes } 134760573Skris packet_send(); 134860573Skris } 134960573Skris xfree(ctype); 135060573Skris} 135192555Sdesstatic void 135292555Sdesclient_input_channel_req(int type, u_int32_t seq, void *ctxt) 135376259Sgreen{ 135476259Sgreen Channel *c = NULL; 135576259Sgreen int id, reply, success = 0; 135676259Sgreen char *rtype; 135760573Skris 135876259Sgreen id = packet_get_int(); 135976259Sgreen rtype = packet_get_string(NULL); 136076259Sgreen reply = packet_get_char(); 136176259Sgreen 136276259Sgreen debug("client_input_channel_req: channel %d rtype %s reply %d", 136376259Sgreen id, rtype, reply); 136476259Sgreen 136576259Sgreen if (session_ident == -1) { 136676259Sgreen error("client_input_channel_req: no channel %d", session_ident); 136776259Sgreen } else if (id != session_ident) { 136876259Sgreen error("client_input_channel_req: channel %d: wrong channel: %d", 136976259Sgreen session_ident, id); 137076259Sgreen } 137176259Sgreen c = channel_lookup(id); 137276259Sgreen if (c == NULL) { 137376259Sgreen error("client_input_channel_req: channel %d: unknown channel", id); 137476259Sgreen } else if (strcmp(rtype, "exit-status") == 0) { 137576259Sgreen success = 1; 137676259Sgreen exit_status = packet_get_int(); 137792555Sdes packet_check_eom(); 137876259Sgreen } 137976259Sgreen if (reply) { 138076259Sgreen packet_start(success ? 138176259Sgreen SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); 138276259Sgreen packet_put_int(c->remote_id); 138376259Sgreen packet_send(); 138476259Sgreen } 138576259Sgreen xfree(rtype); 138676259Sgreen} 138792555Sdesstatic void 138892555Sdesclient_input_global_request(int type, u_int32_t seq, void *ctxt) 138992555Sdes{ 139092555Sdes char *rtype; 139192555Sdes int want_reply; 139292555Sdes int success = 0; 139376259Sgreen 139492555Sdes rtype = packet_get_string(NULL); 139592555Sdes want_reply = packet_get_char(); 1396126274Sdes debug("client_input_global_request: rtype %s want_reply %d", 1397126274Sdes rtype, want_reply); 139892555Sdes if (want_reply) { 139992555Sdes packet_start(success ? 140092555Sdes SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); 140192555Sdes packet_send(); 140292555Sdes packet_write_wait(); 140392555Sdes } 140492555Sdes xfree(rtype); 140592555Sdes} 140692555Sdes 140792555Sdesstatic void 140876259Sgreenclient_init_dispatch_20(void) 140960573Skris{ 141060573Skris dispatch_init(&dispatch_protocol_error); 141198675Sdes 141260573Skris dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); 141360573Skris dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); 141460573Skris dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); 141560573Skris dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); 141660573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); 141760573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 141860573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 141976259Sgreen dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); 142060573Skris dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); 142192555Sdes dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); 142276259Sgreen 142376259Sgreen /* rekeying */ 142476259Sgreen dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); 142598675Sdes 142698675Sdes /* global request reply messages */ 142798675Sdes dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); 142898675Sdes dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); 142960573Skris} 143092555Sdesstatic void 143176259Sgreenclient_init_dispatch_13(void) 143260573Skris{ 143360573Skris dispatch_init(NULL); 143460573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); 143560573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); 143660573Skris dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); 143760573Skris dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 143860573Skris dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 143960573Skris dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); 144060573Skris dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status); 144160573Skris dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data); 144260573Skris dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data); 144368700Sgreen 144468700Sgreen dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ? 1445126274Sdes &client_input_agent_open : &deny_input_open); 144668700Sgreen dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ? 144769587Sgreen &x11_input_open : &deny_input_open); 144860573Skris} 144992555Sdesstatic void 145076259Sgreenclient_init_dispatch_15(void) 145160573Skris{ 145260573Skris client_init_dispatch_13(); 145360573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); 145460573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose); 145560573Skris} 145692555Sdesstatic void 145776259Sgreenclient_init_dispatch(void) 145860573Skris{ 145960573Skris if (compat20) 146060573Skris client_init_dispatch_20(); 146160573Skris else if (compat13) 146260573Skris client_init_dispatch_13(); 146360573Skris else 146460573Skris client_init_dispatch_15(); 146560573Skris} 1466126274Sdes 1467126274Sdes/* client specific fatal cleanup */ 1468126274Sdesvoid 1469126274Sdescleanup_exit(int i) 1470126274Sdes{ 1471126274Sdes leave_raw_mode(); 1472126274Sdes leave_non_blocking(); 1473126274Sdes _exit(i); 1474126274Sdes} 1475