clientloop.c revision 69587
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. 3865668Skris * Copyright (c) 1999,2000 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" 6269587SgreenRCSID("$OpenBSD: clientloop.c,v 1.39 2000/10/27 07:48:22 markus Exp $"); 6357429Smarkm 6457429Smarkm#include "xmalloc.h" 6557429Smarkm#include "ssh.h" 6657429Smarkm#include "packet.h" 6757429Smarkm#include "buffer.h" 6857429Smarkm#include "readconf.h" 6957429Smarkm 7060573Skris#include "ssh2.h" 7160573Skris#include "compat.h" 7260573Skris#include "channels.h" 7360573Skris#include "dispatch.h" 7460573Skris 7565668Skris#include "buffer.h" 7665668Skris#include "bufaux.h" 7760573Skris 7869587Sgreen 7969587Sgreen/* import options */ 8068700Sgreenextern Options options; 8168700Sgreen 8257429Smarkm/* Flag indicating that stdin should be redirected from /dev/null. */ 8357429Smarkmextern int stdin_null_flag; 8457429Smarkm 8557429Smarkm/* 8657429Smarkm * Name of the host we are connecting to. This is the name given on the 8757429Smarkm * command line, or the HostName specified for the user-supplied name in a 8857429Smarkm * configuration file. 8957429Smarkm */ 9057429Smarkmextern char *host; 9157429Smarkm 9257429Smarkm/* 9357429Smarkm * Flag to indicate that we have received a window change signal which has 9457429Smarkm * not yet been processed. This will cause a message indicating the new 9557429Smarkm * window size to be sent to the server a little later. This is volatile 9657429Smarkm * because this is updated in a signal handler. 9757429Smarkm */ 9857429Smarkmstatic volatile int received_window_change_signal = 0; 9957429Smarkm 10057429Smarkm/* Terminal modes, as saved by enter_raw_mode. */ 10157429Smarkmstatic struct termios saved_tio; 10257429Smarkm 10357429Smarkm/* 10457429Smarkm * Flag indicating whether we are in raw mode. This is used by 10557429Smarkm * enter_raw_mode and leave_raw_mode. 10657429Smarkm */ 10757429Smarkmstatic int in_raw_mode = 0; 10857429Smarkm 10957429Smarkm/* Flag indicating whether the user\'s terminal is in non-blocking mode. */ 11057429Smarkmstatic int in_non_blocking_mode = 0; 11157429Smarkm 11257429Smarkm/* Common data for the client loop code. */ 11365668Skrisstatic int quit_pending; /* Set to non-zero to quit the client loop. */ 11465668Skrisstatic int escape_char; /* Escape character. */ 11557429Smarkmstatic int escape_pending; /* Last character was the escape character */ 11657429Smarkmstatic int last_was_cr; /* Last character was a newline. */ 11757429Smarkmstatic int exit_status; /* Used to store the exit status of the command. */ 11857429Smarkmstatic int stdin_eof; /* EOF has been encountered on standard error. */ 11957429Smarkmstatic Buffer stdin_buffer; /* Buffer for stdin data. */ 12057429Smarkmstatic Buffer stdout_buffer; /* Buffer for stdout data. */ 12157429Smarkmstatic Buffer stderr_buffer; /* Buffer for stderr data. */ 12265668Skrisstatic unsigned long stdin_bytes, stdout_bytes, stderr_bytes; 12357429Smarkmstatic unsigned int buffer_high;/* Soft max buffer size. */ 12457429Smarkmstatic int max_fd; /* Maximum file descriptor number in select(). */ 12557429Smarkmstatic int connection_in; /* Connection to server (input). */ 12657429Smarkmstatic int connection_out; /* Connection to server (output). */ 12757429Smarkm 12860573Skris 12960573Skrisvoid client_init_dispatch(void); 13060573Skrisint session_ident = -1; 13160573Skris 13257429Smarkm/* Returns the user\'s terminal to normal mode if it had been put in raw mode. */ 13357429Smarkm 13460573Skrisvoid 13557429Smarkmleave_raw_mode() 13657429Smarkm{ 13757429Smarkm if (!in_raw_mode) 13857429Smarkm return; 13957429Smarkm in_raw_mode = 0; 14057429Smarkm if (tcsetattr(fileno(stdin), TCSADRAIN, &saved_tio) < 0) 14157429Smarkm perror("tcsetattr"); 14257429Smarkm 14357429Smarkm fatal_remove_cleanup((void (*) (void *)) leave_raw_mode, NULL); 14457429Smarkm} 14557429Smarkm 14657429Smarkm/* Puts the user\'s terminal in raw mode. */ 14757429Smarkm 14860573Skrisvoid 14957429Smarkmenter_raw_mode() 15057429Smarkm{ 15157429Smarkm struct termios tio; 15257429Smarkm 15357429Smarkm if (tcgetattr(fileno(stdin), &tio) < 0) 15457429Smarkm perror("tcgetattr"); 15557429Smarkm saved_tio = tio; 15657429Smarkm tio.c_iflag |= IGNPAR; 15757429Smarkm tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); 15857429Smarkm tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); 15957429Smarkm#ifdef IEXTEN 16057429Smarkm tio.c_lflag &= ~IEXTEN; 16157429Smarkm#endif /* IEXTEN */ 16257429Smarkm tio.c_oflag &= ~OPOST; 16357429Smarkm tio.c_cc[VMIN] = 1; 16457429Smarkm tio.c_cc[VTIME] = 0; 16557429Smarkm if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0) 16657429Smarkm perror("tcsetattr"); 16757429Smarkm in_raw_mode = 1; 16857429Smarkm 16957429Smarkm fatal_add_cleanup((void (*) (void *)) leave_raw_mode, NULL); 17057429Smarkm} 17157429Smarkm 17257429Smarkm/* Restores stdin to blocking mode. */ 17357429Smarkm 17460573Skrisvoid 17557429Smarkmleave_non_blocking() 17657429Smarkm{ 17757429Smarkm if (in_non_blocking_mode) { 17857429Smarkm (void) fcntl(fileno(stdin), F_SETFL, 0); 17957429Smarkm in_non_blocking_mode = 0; 18057429Smarkm fatal_remove_cleanup((void (*) (void *)) leave_non_blocking, NULL); 18157429Smarkm } 18257429Smarkm} 18357429Smarkm 18457429Smarkm/* Puts stdin terminal in non-blocking mode. */ 18557429Smarkm 18660573Skrisvoid 18757429Smarkmenter_non_blocking() 18857429Smarkm{ 18957429Smarkm in_non_blocking_mode = 1; 19057429Smarkm (void) fcntl(fileno(stdin), F_SETFL, O_NONBLOCK); 19157429Smarkm fatal_add_cleanup((void (*) (void *)) leave_non_blocking, NULL); 19257429Smarkm} 19357429Smarkm 19457429Smarkm/* 19557429Smarkm * Signal handler for the window change signal (SIGWINCH). This just sets a 19657429Smarkm * flag indicating that the window has changed. 19757429Smarkm */ 19857429Smarkm 19960573Skrisvoid 20057429Smarkmwindow_change_handler(int sig) 20157429Smarkm{ 20257429Smarkm received_window_change_signal = 1; 20357429Smarkm signal(SIGWINCH, window_change_handler); 20457429Smarkm} 20557429Smarkm 20657429Smarkm/* 20757429Smarkm * Signal handler for signals that cause the program to terminate. These 20857429Smarkm * signals must be trapped to restore terminal modes. 20957429Smarkm */ 21057429Smarkm 21160573Skrisvoid 21257429Smarkmsignal_handler(int sig) 21357429Smarkm{ 21457429Smarkm if (in_raw_mode) 21557429Smarkm leave_raw_mode(); 21657429Smarkm if (in_non_blocking_mode) 21757429Smarkm leave_non_blocking(); 21857429Smarkm channel_stop_listening(); 21957429Smarkm packet_close(); 22057429Smarkm fatal("Killed by signal %d.", sig); 22157429Smarkm} 22257429Smarkm 22357429Smarkm/* 22457429Smarkm * Returns current time in seconds from Jan 1, 1970 with the maximum 22557429Smarkm * available resolution. 22657429Smarkm */ 22757429Smarkm 22860573Skrisdouble 22957429Smarkmget_current_time() 23057429Smarkm{ 23157429Smarkm struct timeval tv; 23257429Smarkm gettimeofday(&tv, NULL); 23357429Smarkm return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; 23457429Smarkm} 23557429Smarkm 23657429Smarkm/* 23757429Smarkm * This is called when the interactive is entered. This checks if there is 23857429Smarkm * an EOF coming on stdin. We must check this explicitly, as select() does 23957429Smarkm * not appear to wake up when redirecting from /dev/null. 24057429Smarkm */ 24157429Smarkm 24260573Skrisvoid 24357429Smarkmclient_check_initial_eof_on_stdin() 24457429Smarkm{ 24557429Smarkm int len; 24657429Smarkm char buf[1]; 24757429Smarkm 24857429Smarkm /* 24957429Smarkm * If standard input is to be "redirected from /dev/null", we simply 25057429Smarkm * mark that we have seen an EOF and send an EOF message to the 25157429Smarkm * server. Otherwise, we try to read a single character; it appears 25257429Smarkm * that for some files, such /dev/null, select() never wakes up for 25357429Smarkm * read for this descriptor, which means that we never get EOF. This 25457429Smarkm * way we will get the EOF if stdin comes from /dev/null or similar. 25557429Smarkm */ 25657429Smarkm if (stdin_null_flag) { 25757429Smarkm /* Fake EOF on stdin. */ 25857429Smarkm debug("Sending eof."); 25957429Smarkm stdin_eof = 1; 26057429Smarkm packet_start(SSH_CMSG_EOF); 26157429Smarkm packet_send(); 26257429Smarkm } else { 26357429Smarkm enter_non_blocking(); 26457429Smarkm 26557429Smarkm /* Check for immediate EOF on stdin. */ 26657429Smarkm len = read(fileno(stdin), buf, 1); 26757429Smarkm if (len == 0) { 26857429Smarkm /* EOF. Record that we have seen it and send EOF to server. */ 26957429Smarkm debug("Sending eof."); 27057429Smarkm stdin_eof = 1; 27157429Smarkm packet_start(SSH_CMSG_EOF); 27257429Smarkm packet_send(); 27357429Smarkm } else if (len > 0) { 27457429Smarkm /* 27557429Smarkm * Got data. We must store the data in the buffer, 27657429Smarkm * and also process it as an escape character if 27757429Smarkm * appropriate. 27857429Smarkm */ 27957429Smarkm if ((unsigned char) buf[0] == escape_char) 28057429Smarkm escape_pending = 1; 28157429Smarkm else { 28257429Smarkm buffer_append(&stdin_buffer, buf, 1); 28357429Smarkm stdin_bytes += 1; 28457429Smarkm } 28557429Smarkm } 28657429Smarkm leave_non_blocking(); 28757429Smarkm } 28857429Smarkm} 28957429Smarkm 29057429Smarkm 29157429Smarkm/* 29257429Smarkm * Make packets from buffered stdin data, and buffer them for sending to the 29357429Smarkm * connection. 29457429Smarkm */ 29557429Smarkm 29660573Skrisvoid 29757429Smarkmclient_make_packets_from_stdin_data() 29857429Smarkm{ 29957429Smarkm unsigned int len; 30057429Smarkm 30157429Smarkm /* Send buffered stdin data to the server. */ 30257429Smarkm while (buffer_len(&stdin_buffer) > 0 && 30357429Smarkm packet_not_very_much_data_to_write()) { 30457429Smarkm len = buffer_len(&stdin_buffer); 30557429Smarkm /* Keep the packets at reasonable size. */ 30657429Smarkm if (len > packet_get_maxsize()) 30757429Smarkm len = packet_get_maxsize(); 30857429Smarkm packet_start(SSH_CMSG_STDIN_DATA); 30957429Smarkm packet_put_string(buffer_ptr(&stdin_buffer), len); 31057429Smarkm packet_send(); 31157429Smarkm buffer_consume(&stdin_buffer, len); 31257429Smarkm /* If we have a pending EOF, send it now. */ 31357429Smarkm if (stdin_eof && buffer_len(&stdin_buffer) == 0) { 31457429Smarkm packet_start(SSH_CMSG_EOF); 31557429Smarkm packet_send(); 31657429Smarkm } 31757429Smarkm } 31857429Smarkm} 31957429Smarkm 32057429Smarkm/* 32157429Smarkm * Checks if the client window has changed, and sends a packet about it to 32257429Smarkm * the server if so. The actual change is detected elsewhere (by a software 32357429Smarkm * interrupt on Unix); this just checks the flag and sends a message if 32457429Smarkm * appropriate. 32557429Smarkm */ 32657429Smarkm 32760573Skrisvoid 32857429Smarkmclient_check_window_change() 32957429Smarkm{ 33060573Skris struct winsize ws; 33157429Smarkm 33260573Skris if (! received_window_change_signal) 33360573Skris return; 33460573Skris /** XXX race */ 33560573Skris received_window_change_signal = 0; 33657429Smarkm 33760573Skris if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) 33860573Skris return; 33960573Skris 34069587Sgreen debug2("client_check_window_change: changed"); 34160573Skris 34260573Skris if (compat20) { 34360573Skris channel_request_start(session_ident, "window-change", 0); 34460573Skris packet_put_int(ws.ws_col); 34560573Skris packet_put_int(ws.ws_row); 34660573Skris packet_put_int(ws.ws_xpixel); 34760573Skris packet_put_int(ws.ws_ypixel); 34860573Skris packet_send(); 34960573Skris } else { 35060573Skris packet_start(SSH_CMSG_WINDOW_SIZE); 35160573Skris packet_put_int(ws.ws_row); 35260573Skris packet_put_int(ws.ws_col); 35360573Skris packet_put_int(ws.ws_xpixel); 35460573Skris packet_put_int(ws.ws_ypixel); 35560573Skris packet_send(); 35657429Smarkm } 35757429Smarkm} 35857429Smarkm 35957429Smarkm/* 36057429Smarkm * Waits until the client can do something (some data becomes available on 36157429Smarkm * one of the file descriptors). 36257429Smarkm */ 36357429Smarkm 36460573Skrisvoid 36557429Smarkmclient_wait_until_can_do_something(fd_set * readset, fd_set * writeset) 36657429Smarkm{ 36757429Smarkm /* Initialize select masks. */ 36857429Smarkm FD_ZERO(readset); 36960573Skris FD_ZERO(writeset); 37057429Smarkm 37160573Skris if (!compat20) { 37260573Skris /* Read from the connection, unless our buffers are full. */ 37360573Skris if (buffer_len(&stdout_buffer) < buffer_high && 37460573Skris buffer_len(&stderr_buffer) < buffer_high && 37560573Skris channel_not_very_much_buffered_data()) 37660573Skris FD_SET(connection_in, readset); 37760573Skris /* 37860573Skris * Read from stdin, unless we have seen EOF or have very much 37960573Skris * buffered data to send to the server. 38060573Skris */ 38160573Skris if (!stdin_eof && packet_not_very_much_data_to_write()) 38260573Skris FD_SET(fileno(stdin), readset); 38360573Skris 38460573Skris /* Select stdout/stderr if have data in buffer. */ 38560573Skris if (buffer_len(&stdout_buffer) > 0) 38660573Skris FD_SET(fileno(stdout), writeset); 38760573Skris if (buffer_len(&stderr_buffer) > 0) 38860573Skris FD_SET(fileno(stderr), writeset); 38960573Skris } else { 39057429Smarkm FD_SET(connection_in, readset); 39160573Skris } 39257429Smarkm 39357429Smarkm /* Add any selections by the channel mechanism. */ 39457429Smarkm channel_prepare_select(readset, writeset); 39557429Smarkm 39657429Smarkm /* Select server connection if have data to write to the server. */ 39757429Smarkm if (packet_have_data_to_write()) 39857429Smarkm FD_SET(connection_out, writeset); 39957429Smarkm 40060573Skris/* move UP XXX */ 40157429Smarkm /* Update maximum file descriptor number, if appropriate. */ 40257429Smarkm if (channel_max_fd() > max_fd) 40357429Smarkm max_fd = channel_max_fd(); 40457429Smarkm 40557429Smarkm /* 40657429Smarkm * Wait for something to happen. This will suspend the process until 40757429Smarkm * some selected descriptor can be read, written, or has some other 40857429Smarkm * event pending. Note: if you want to implement SSH_MSG_IGNORE 40957429Smarkm * messages to fool traffic analysis, this might be the place to do 41057429Smarkm * it: just have a random timeout for the select, and send a random 41157429Smarkm * SSH_MSG_IGNORE packet when the timeout expires. 41257429Smarkm */ 41357429Smarkm 41457429Smarkm if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0) { 41557429Smarkm char buf[100]; 41657429Smarkm /* Some systems fail to clear these automatically. */ 41757429Smarkm FD_ZERO(readset); 41857429Smarkm FD_ZERO(writeset); 41957429Smarkm if (errno == EINTR) 42057429Smarkm return; 42157429Smarkm /* Note: we might still have data in the buffers. */ 42257429Smarkm snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); 42357429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 42457429Smarkm stderr_bytes += strlen(buf); 42557429Smarkm quit_pending = 1; 42657429Smarkm } 42757429Smarkm} 42857429Smarkm 42960573Skrisvoid 43065668Skrisclient_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) 43157429Smarkm{ 43257429Smarkm struct winsize oldws, newws; 43357429Smarkm 43457429Smarkm /* Flush stdout and stderr buffers. */ 43565668Skris if (buffer_len(bout) > 0) 43665668Skris atomicio(write, fileno(stdout), buffer_ptr(bout), buffer_len(bout)); 43765668Skris if (buffer_len(berr) > 0) 43865668Skris atomicio(write, fileno(stderr), buffer_ptr(berr), buffer_len(berr)); 43957429Smarkm 44057429Smarkm leave_raw_mode(); 44157429Smarkm 44257429Smarkm /* 44357429Smarkm * Free (and clear) the buffer to reduce the amount of data that gets 44457429Smarkm * written to swap. 44557429Smarkm */ 44665668Skris buffer_free(bin); 44765668Skris buffer_free(bout); 44865668Skris buffer_free(berr); 44957429Smarkm 45057429Smarkm /* Save old window size. */ 45157429Smarkm ioctl(fileno(stdin), TIOCGWINSZ, &oldws); 45257429Smarkm 45357429Smarkm /* Send the suspend signal to the program itself. */ 45457429Smarkm kill(getpid(), SIGTSTP); 45557429Smarkm 45657429Smarkm /* Check if the window size has changed. */ 45757429Smarkm if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 && 45857429Smarkm (oldws.ws_row != newws.ws_row || 45957429Smarkm oldws.ws_col != newws.ws_col || 46057429Smarkm oldws.ws_xpixel != newws.ws_xpixel || 46157429Smarkm oldws.ws_ypixel != newws.ws_ypixel)) 46257429Smarkm received_window_change_signal = 1; 46357429Smarkm 46457429Smarkm /* OK, we have been continued by the user. Reinitialize buffers. */ 46565668Skris buffer_init(bin); 46665668Skris buffer_init(bout); 46765668Skris buffer_init(berr); 46857429Smarkm 46957429Smarkm enter_raw_mode(); 47057429Smarkm} 47157429Smarkm 47260573Skrisvoid 47360573Skrisclient_process_net_input(fd_set * readset) 47457429Smarkm{ 47560573Skris int len; 47660573Skris char buf[8192]; 47757429Smarkm 47857429Smarkm /* 47957429Smarkm * Read input from the server, and add any such data to the buffer of 48057429Smarkm * the packet subsystem. 48157429Smarkm */ 48257429Smarkm if (FD_ISSET(connection_in, readset)) { 48357429Smarkm /* Read as much as possible. */ 48457429Smarkm len = read(connection_in, buf, sizeof(buf)); 48557429Smarkm if (len == 0) { 48657429Smarkm /* Received EOF. The remote host has closed the connection. */ 48757429Smarkm snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n", 48857429Smarkm host); 48957429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 49057429Smarkm stderr_bytes += strlen(buf); 49157429Smarkm quit_pending = 1; 49257429Smarkm return; 49357429Smarkm } 49457429Smarkm /* 49557429Smarkm * There is a kernel bug on Solaris that causes select to 49657429Smarkm * sometimes wake up even though there is no data available. 49757429Smarkm */ 49857429Smarkm if (len < 0 && errno == EAGAIN) 49957429Smarkm len = 0; 50057429Smarkm 50157429Smarkm if (len < 0) { 50257429Smarkm /* An error has encountered. Perhaps there is a network problem. */ 50357429Smarkm snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n", 50457429Smarkm host, strerror(errno)); 50557429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 50657429Smarkm stderr_bytes += strlen(buf); 50757429Smarkm quit_pending = 1; 50857429Smarkm return; 50957429Smarkm } 51057429Smarkm packet_process_incoming(buf, len); 51157429Smarkm } 51260573Skris} 51360573Skris 51465668Skris/* process the characters one by one */ 51565668Skrisint 51665668Skrisprocess_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len) 51765668Skris{ 51865668Skris char string[1024]; 51965668Skris pid_t pid; 52065668Skris int bytes = 0; 52165668Skris unsigned int i; 52265668Skris unsigned char ch; 52365668Skris char *s; 52465668Skris 52565668Skris for (i = 0; i < len; i++) { 52665668Skris /* Get one character at a time. */ 52765668Skris ch = buf[i]; 52865668Skris 52965668Skris if (escape_pending) { 53065668Skris /* We have previously seen an escape character. */ 53165668Skris /* Clear the flag now. */ 53265668Skris escape_pending = 0; 53365668Skris 53465668Skris /* Process the escaped character. */ 53565668Skris switch (ch) { 53665668Skris case '.': 53765668Skris /* Terminate the connection. */ 53865668Skris snprintf(string, sizeof string, "%c.\r\n", escape_char); 53965668Skris buffer_append(berr, string, strlen(string)); 54065668Skris /*stderr_bytes += strlen(string); XXX*/ 54165668Skris 54265668Skris quit_pending = 1; 54365668Skris return -1; 54465668Skris 54565668Skris case 'Z' - 64: 54665668Skris /* Suspend the program. */ 54765668Skris /* Print a message to that effect to the user. */ 54865668Skris snprintf(string, sizeof string, "%c^Z [suspend ssh]\r\n", escape_char); 54965668Skris buffer_append(berr, string, strlen(string)); 55065668Skris /*stderr_bytes += strlen(string); XXX*/ 55165668Skris 55265668Skris /* Restore terminal modes and suspend. */ 55365668Skris client_suspend_self(bin, bout, berr); 55465668Skris 55565668Skris /* We have been continued. */ 55665668Skris continue; 55765668Skris 55865668Skris case '&': 55965668Skris /* XXX does not work yet with proto 2 */ 56065668Skris if (compat20) 56165668Skris continue; 56265668Skris /* 56365668Skris * Detach the program (continue to serve connections, 56465668Skris * but put in background and no more new connections). 56565668Skris */ 56665668Skris if (!stdin_eof) { 56765668Skris /* 56865668Skris * Sending SSH_CMSG_EOF alone does not always appear 56965668Skris * to be enough. So we try to send an EOF character 57065668Skris * first. 57165668Skris */ 57265668Skris packet_start(SSH_CMSG_STDIN_DATA); 57365668Skris packet_put_string("\004", 1); 57465668Skris packet_send(); 57565668Skris /* Close stdin. */ 57665668Skris stdin_eof = 1; 57765668Skris if (buffer_len(bin) == 0) { 57865668Skris packet_start(SSH_CMSG_EOF); 57965668Skris packet_send(); 58065668Skris } 58165668Skris } 58265668Skris /* Restore tty modes. */ 58365668Skris leave_raw_mode(); 58465668Skris 58565668Skris /* Stop listening for new connections. */ 58665668Skris channel_stop_listening(); 58765668Skris 58865668Skris printf("%c& [backgrounded]\n", escape_char); 58965668Skris 59065668Skris /* Fork into background. */ 59165668Skris pid = fork(); 59265668Skris if (pid < 0) { 59365668Skris error("fork: %.100s", strerror(errno)); 59465668Skris continue; 59565668Skris } 59665668Skris if (pid != 0) { /* This is the parent. */ 59765668Skris /* The parent just exits. */ 59865668Skris exit(0); 59965668Skris } 60065668Skris /* The child continues serving connections. */ 60165668Skris continue; /*XXX ? */ 60265668Skris 60365668Skris case '?': 60465668Skris snprintf(string, sizeof string, 60565668Skris"%c?\r\n\ 60665668SkrisSupported escape sequences:\r\n\ 60765668Skris~. - terminate connection\r\n\ 60865668Skris~^Z - suspend ssh\r\n\ 60965668Skris~# - list forwarded connections\r\n\ 61065668Skris~& - background ssh (when waiting for connections to terminate)\r\n\ 61165668Skris~? - this message\r\n\ 61265668Skris~~ - send the escape character by typing it twice\r\n\ 61365668Skris(Note that escapes are only recognized immediately after newline.)\r\n", 61465668Skris escape_char); 61565668Skris buffer_append(berr, string, strlen(string)); 61665668Skris continue; 61765668Skris 61865668Skris case '#': 61965668Skris snprintf(string, sizeof string, "%c#\r\n", escape_char); 62065668Skris buffer_append(berr, string, strlen(string)); 62165668Skris s = channel_open_message(); 62265668Skris buffer_append(berr, s, strlen(s)); 62365668Skris xfree(s); 62465668Skris continue; 62565668Skris 62665668Skris default: 62765668Skris if (ch != escape_char) { 62865668Skris buffer_put_char(bin, escape_char); 62965668Skris bytes++; 63065668Skris } 63165668Skris /* Escaped characters fall through here */ 63265668Skris break; 63365668Skris } 63465668Skris } else { 63565668Skris /* 63665668Skris * The previous character was not an escape char. Check if this 63765668Skris * is an escape. 63865668Skris */ 63965668Skris if (last_was_cr && ch == escape_char) { 64065668Skris /* It is. Set the flag and continue to next character. */ 64165668Skris escape_pending = 1; 64265668Skris continue; 64365668Skris } 64465668Skris } 64565668Skris 64665668Skris /* 64765668Skris * Normal character. Record whether it was a newline, 64865668Skris * and append it to the buffer. 64965668Skris */ 65065668Skris last_was_cr = (ch == '\r' || ch == '\n'); 65165668Skris buffer_put_char(bin, ch); 65265668Skris bytes++; 65365668Skris } 65465668Skris return bytes; 65565668Skris} 65665668Skris 65760573Skrisvoid 65860573Skrisclient_process_input(fd_set * readset) 65960573Skris{ 66065668Skris int ret; 66160573Skris int len; 66265668Skris char buf[8192]; 66360573Skris 66457429Smarkm /* Read input from stdin. */ 66557429Smarkm if (FD_ISSET(fileno(stdin), readset)) { 66657429Smarkm /* Read as much as possible. */ 66757429Smarkm len = read(fileno(stdin), buf, sizeof(buf)); 66857429Smarkm if (len <= 0) { 66957429Smarkm /* 67057429Smarkm * Received EOF or error. They are treated 67157429Smarkm * similarly, except that an error message is printed 67257429Smarkm * if it was an error condition. 67357429Smarkm */ 67457429Smarkm if (len < 0) { 67557429Smarkm snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno)); 67657429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 67757429Smarkm stderr_bytes += strlen(buf); 67857429Smarkm } 67957429Smarkm /* Mark that we have seen EOF. */ 68057429Smarkm stdin_eof = 1; 68157429Smarkm /* 68257429Smarkm * Send an EOF message to the server unless there is 68357429Smarkm * data in the buffer. If there is data in the 68457429Smarkm * buffer, no message will be sent now. Code 68557429Smarkm * elsewhere will send the EOF when the buffer 68657429Smarkm * becomes empty if stdin_eof is set. 68757429Smarkm */ 68857429Smarkm if (buffer_len(&stdin_buffer) == 0) { 68957429Smarkm packet_start(SSH_CMSG_EOF); 69057429Smarkm packet_send(); 69157429Smarkm } 69257429Smarkm } else if (escape_char == -1) { 69357429Smarkm /* 69457429Smarkm * Normal successful read, and no escape character. 69557429Smarkm * Just append the data to buffer. 69657429Smarkm */ 69757429Smarkm buffer_append(&stdin_buffer, buf, len); 69857429Smarkm stdin_bytes += len; 69957429Smarkm } else { 70057429Smarkm /* 70157429Smarkm * Normal, successful read. But we have an escape character 70257429Smarkm * and have to process the characters one by one. 70357429Smarkm */ 70465668Skris ret = process_escapes(&stdin_buffer, &stdout_buffer, &stderr_buffer, buf, len); 70565668Skris if (ret == -1) 70665668Skris return; 70765668Skris stdout_bytes += ret; 70857429Smarkm } 70957429Smarkm } 71057429Smarkm} 71157429Smarkm 71260573Skrisvoid 71357429Smarkmclient_process_output(fd_set * writeset) 71457429Smarkm{ 71557429Smarkm int len; 71657429Smarkm char buf[100]; 71757429Smarkm 71857429Smarkm /* Write buffered output to stdout. */ 71957429Smarkm if (FD_ISSET(fileno(stdout), writeset)) { 72057429Smarkm /* Write as much data as possible. */ 72157429Smarkm len = write(fileno(stdout), buffer_ptr(&stdout_buffer), 72257429Smarkm buffer_len(&stdout_buffer)); 72357429Smarkm if (len <= 0) { 72457429Smarkm if (errno == EAGAIN) 72557429Smarkm len = 0; 72657429Smarkm else { 72757429Smarkm /* 72857429Smarkm * An error or EOF was encountered. Put an 72957429Smarkm * error message to stderr buffer. 73057429Smarkm */ 73157429Smarkm snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno)); 73257429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 73357429Smarkm stderr_bytes += strlen(buf); 73457429Smarkm quit_pending = 1; 73557429Smarkm return; 73657429Smarkm } 73757429Smarkm } 73857429Smarkm /* Consume printed data from the buffer. */ 73957429Smarkm buffer_consume(&stdout_buffer, len); 74057429Smarkm } 74157429Smarkm /* Write buffered output to stderr. */ 74257429Smarkm if (FD_ISSET(fileno(stderr), writeset)) { 74357429Smarkm /* Write as much data as possible. */ 74457429Smarkm len = write(fileno(stderr), buffer_ptr(&stderr_buffer), 74557429Smarkm buffer_len(&stderr_buffer)); 74657429Smarkm if (len <= 0) { 74757429Smarkm if (errno == EAGAIN) 74857429Smarkm len = 0; 74957429Smarkm else { 75057429Smarkm /* EOF or error, but can't even print error message. */ 75157429Smarkm quit_pending = 1; 75257429Smarkm return; 75357429Smarkm } 75457429Smarkm } 75557429Smarkm /* Consume printed characters from the buffer. */ 75657429Smarkm buffer_consume(&stderr_buffer, len); 75757429Smarkm } 75857429Smarkm} 75957429Smarkm 76057429Smarkm/* 76160573Skris * Get packets from the connection input buffer, and process them as long as 76260573Skris * there are packets available. 76360573Skris * 76460573Skris * Any unknown packets received during the actual 76560573Skris * session cause the session to terminate. This is 76660573Skris * intended to make debugging easier since no 76760573Skris * confirmations are sent. Any compatible protocol 76860573Skris * extensions must be negotiated during the 76960573Skris * preparatory phase. 77060573Skris */ 77160573Skris 77260573Skrisvoid 77360573Skrisclient_process_buffered_input_packets() 77460573Skris{ 77569587Sgreen dispatch_run(DISPATCH_NONBLOCK, &quit_pending, NULL); 77660573Skris} 77760573Skris 77865668Skris/* scan buf[] for '~' before sending data to the peer */ 77965668Skris 78065668Skrisint 78165668Skrissimple_escape_filter(Channel *c, char *buf, int len) 78265668Skris{ 78365668Skris /* XXX we assume c->extended is writeable */ 78465668Skris return process_escapes(&c->input, &c->output, &c->extended, buf, len); 78565668Skris} 78665668Skris 78760573Skris/* 78857429Smarkm * Implements the interactive session with the server. This is called after 78957429Smarkm * the user has been authenticated, and a command has been started on the 79057429Smarkm * remote host. If escape_char != -1, it is the character used as an escape 79157429Smarkm * character for terminating or suspending the session. 79257429Smarkm */ 79357429Smarkm 79460573Skrisint 79565668Skrisclient_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) 79657429Smarkm{ 79757429Smarkm double start_time, total_time; 79857429Smarkm int len; 79957429Smarkm char buf[100]; 80057429Smarkm 80157429Smarkm debug("Entering interactive session."); 80257429Smarkm 80357429Smarkm start_time = get_current_time(); 80457429Smarkm 80557429Smarkm /* Initialize variables. */ 80657429Smarkm escape_pending = 0; 80757429Smarkm last_was_cr = 1; 80857429Smarkm exit_status = -1; 80957429Smarkm stdin_eof = 0; 81057429Smarkm buffer_high = 64 * 1024; 81157429Smarkm connection_in = packet_get_connection_in(); 81257429Smarkm connection_out = packet_get_connection_out(); 81357429Smarkm max_fd = connection_in; 81457429Smarkm if (connection_out > max_fd) 81557429Smarkm max_fd = connection_out; 81657429Smarkm stdin_bytes = 0; 81757429Smarkm stdout_bytes = 0; 81857429Smarkm stderr_bytes = 0; 81957429Smarkm quit_pending = 0; 82057429Smarkm escape_char = escape_char_arg; 82157429Smarkm 82257429Smarkm /* Initialize buffers. */ 82357429Smarkm buffer_init(&stdin_buffer); 82457429Smarkm buffer_init(&stdout_buffer); 82557429Smarkm buffer_init(&stderr_buffer); 82657429Smarkm 82760573Skris client_init_dispatch(); 82860573Skris 82957429Smarkm /* Set signal handlers to restore non-blocking mode. */ 83057429Smarkm signal(SIGINT, signal_handler); 83157429Smarkm signal(SIGQUIT, signal_handler); 83257429Smarkm signal(SIGTERM, signal_handler); 83357429Smarkm signal(SIGPIPE, SIG_IGN); 83457429Smarkm if (have_pty) 83557429Smarkm signal(SIGWINCH, window_change_handler); 83657429Smarkm 83757429Smarkm if (have_pty) 83857429Smarkm enter_raw_mode(); 83957429Smarkm 84065668Skris /* Check if we should immediately send eof on stdin. */ 84160573Skris if (!compat20) 84260573Skris client_check_initial_eof_on_stdin(); 84357429Smarkm 84465668Skris if (compat20 && escape_char != -1) 84565668Skris channel_register_filter(ssh2_chan_id, simple_escape_filter); 84665668Skris 84757429Smarkm /* Main loop of the client for the interactive session mode. */ 84857429Smarkm while (!quit_pending) { 84957429Smarkm fd_set readset, writeset; 85057429Smarkm 85157429Smarkm /* Process buffered packets sent by the server. */ 85257429Smarkm client_process_buffered_input_packets(); 85357429Smarkm 85460573Skris if (compat20 && !channel_still_open()) { 85569587Sgreen debug2("!channel_still_open."); 85660573Skris break; 85760573Skris } 85860573Skris 85957429Smarkm /* 86057429Smarkm * Make packets of buffered stdin data, and buffer them for 86157429Smarkm * sending to the server. 86257429Smarkm */ 86360573Skris if (!compat20) 86460573Skris client_make_packets_from_stdin_data(); 86557429Smarkm 86657429Smarkm /* 86757429Smarkm * Make packets from buffered channel data, and buffer them 86857429Smarkm * for sending to the server. 86957429Smarkm */ 87057429Smarkm if (packet_not_very_much_data_to_write()) 87157429Smarkm channel_output_poll(); 87257429Smarkm 87357429Smarkm /* 87457429Smarkm * Check if the window size has changed, and buffer a message 87557429Smarkm * about it to the server if so. 87657429Smarkm */ 87757429Smarkm client_check_window_change(); 87857429Smarkm 87957429Smarkm if (quit_pending) 88057429Smarkm break; 88157429Smarkm 88257429Smarkm /* 88357429Smarkm * Wait until we have something to do (something becomes 88457429Smarkm * available on one of the descriptors). 88557429Smarkm */ 88657429Smarkm client_wait_until_can_do_something(&readset, &writeset); 88757429Smarkm 88857429Smarkm if (quit_pending) 88957429Smarkm break; 89057429Smarkm 89157429Smarkm /* Do channel operations. */ 89257429Smarkm channel_after_select(&readset, &writeset); 89357429Smarkm 89460573Skris /* Buffer input from the connection. */ 89560573Skris client_process_net_input(&readset); 89657429Smarkm 89760573Skris if (quit_pending) 89860573Skris break; 89957429Smarkm 90060573Skris if (!compat20) { 90160573Skris /* Buffer data from stdin */ 90260573Skris client_process_input(&readset); 90360573Skris /* 90460573Skris * Process output to stdout and stderr. Output to 90560573Skris * the connection is processed elsewhere (above). 90660573Skris */ 90760573Skris client_process_output(&writeset); 90860573Skris } 90960573Skris 91057429Smarkm /* Send as much buffered packet data as possible to the sender. */ 91157429Smarkm if (FD_ISSET(connection_out, &writeset)) 91257429Smarkm packet_write_poll(); 91357429Smarkm } 91457429Smarkm 91557429Smarkm /* Terminate the session. */ 91657429Smarkm 91757429Smarkm /* Stop watching for window change. */ 91857429Smarkm if (have_pty) 91957429Smarkm signal(SIGWINCH, SIG_DFL); 92057429Smarkm 92157429Smarkm /* Stop listening for connections. */ 92257429Smarkm channel_stop_listening(); 92357429Smarkm 92457429Smarkm /* 92557429Smarkm * In interactive mode (with pseudo tty) display a message indicating 92657429Smarkm * that the connection has been closed. 92757429Smarkm */ 92857429Smarkm if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { 92957429Smarkm snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host); 93057429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 93157429Smarkm stderr_bytes += strlen(buf); 93257429Smarkm } 93357429Smarkm /* Output any buffered data for stdout. */ 93457429Smarkm while (buffer_len(&stdout_buffer) > 0) { 93557429Smarkm len = write(fileno(stdout), buffer_ptr(&stdout_buffer), 93657429Smarkm buffer_len(&stdout_buffer)); 93757429Smarkm if (len <= 0) { 93857429Smarkm error("Write failed flushing stdout buffer."); 93957429Smarkm break; 94057429Smarkm } 94157429Smarkm buffer_consume(&stdout_buffer, len); 94257429Smarkm } 94357429Smarkm 94457429Smarkm /* Output any buffered data for stderr. */ 94557429Smarkm while (buffer_len(&stderr_buffer) > 0) { 94657429Smarkm len = write(fileno(stderr), buffer_ptr(&stderr_buffer), 94757429Smarkm buffer_len(&stderr_buffer)); 94857429Smarkm if (len <= 0) { 94957429Smarkm error("Write failed flushing stderr buffer."); 95057429Smarkm break; 95157429Smarkm } 95257429Smarkm buffer_consume(&stderr_buffer, len); 95357429Smarkm } 95457429Smarkm 95557429Smarkm if (have_pty) 95657429Smarkm leave_raw_mode(); 95757429Smarkm 95857429Smarkm /* Clear and free any buffers. */ 95957429Smarkm memset(buf, 0, sizeof(buf)); 96057429Smarkm buffer_free(&stdin_buffer); 96157429Smarkm buffer_free(&stdout_buffer); 96257429Smarkm buffer_free(&stderr_buffer); 96357429Smarkm 96457429Smarkm /* Report bytes transferred, and transfer rates. */ 96557429Smarkm total_time = get_current_time() - start_time; 96657429Smarkm debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds", 96757429Smarkm stdin_bytes, stdout_bytes, stderr_bytes, total_time); 96857429Smarkm if (total_time > 0) 96957429Smarkm debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f", 97057429Smarkm stdin_bytes / total_time, stdout_bytes / total_time, 97157429Smarkm stderr_bytes / total_time); 97257429Smarkm 97357429Smarkm /* Return the exit status of the program. */ 97457429Smarkm debug("Exit status %d", exit_status); 97557429Smarkm return exit_status; 97657429Smarkm} 97760573Skris 97860573Skris/*********/ 97960573Skris 98060573Skrisvoid 98169587Sgreenclient_input_stdout_data(int type, int plen, void *ctxt) 98260573Skris{ 98360573Skris unsigned int data_len; 98460573Skris char *data = packet_get_string(&data_len); 98560573Skris packet_integrity_check(plen, 4 + data_len, type); 98660573Skris buffer_append(&stdout_buffer, data, data_len); 98760573Skris stdout_bytes += data_len; 98860573Skris memset(data, 0, data_len); 98960573Skris xfree(data); 99060573Skris} 99160573Skrisvoid 99269587Sgreenclient_input_stderr_data(int type, int plen, void *ctxt) 99360573Skris{ 99460573Skris unsigned int data_len; 99560573Skris char *data = packet_get_string(&data_len); 99660573Skris packet_integrity_check(plen, 4 + data_len, type); 99760573Skris buffer_append(&stderr_buffer, data, data_len); 99860573Skris stdout_bytes += data_len; 99960573Skris memset(data, 0, data_len); 100060573Skris xfree(data); 100160573Skris} 100260573Skrisvoid 100369587Sgreenclient_input_exit_status(int type, int plen, void *ctxt) 100460573Skris{ 100560573Skris packet_integrity_check(plen, 4, type); 100660573Skris exit_status = packet_get_int(); 100760573Skris /* Acknowledge the exit. */ 100860573Skris packet_start(SSH_CMSG_EXIT_CONFIRMATION); 100960573Skris packet_send(); 101060573Skris /* 101160573Skris * Must wait for packet to be sent since we are 101260573Skris * exiting the loop. 101360573Skris */ 101460573Skris packet_write_wait(); 101560573Skris /* Flag that we want to exit. */ 101660573Skris quit_pending = 1; 101760573Skris} 101860573Skris 101960573Skris/* XXXX move to generic input handler */ 102060573Skrisvoid 102169587Sgreenclient_input_channel_open(int type, int plen, void *ctxt) 102260573Skris{ 102360573Skris Channel *c = NULL; 102460573Skris char *ctype; 102560573Skris int id; 102660573Skris unsigned int len; 102760573Skris int rchan; 102860573Skris int rmaxpack; 102960573Skris int rwindow; 103060573Skris 103160573Skris ctype = packet_get_string(&len); 103260573Skris rchan = packet_get_int(); 103360573Skris rwindow = packet_get_int(); 103460573Skris rmaxpack = packet_get_int(); 103560573Skris 103660573Skris debug("client_input_channel_open: ctype %s rchan %d win %d max %d", 103760573Skris ctype, rchan, rwindow, rmaxpack); 103860573Skris 103968700Sgreen if (strcmp(ctype, "x11") == 0 && options.forward_x11) { 104060573Skris int sock; 104160573Skris char *originator; 104260573Skris int originator_port; 104360573Skris originator = packet_get_string(NULL); 104460573Skris if (datafellows & SSH_BUG_X11FWD) { 104569587Sgreen debug2("buggy server: x11 request w/o originator_port"); 104660573Skris originator_port = 0; 104760573Skris } else { 104860573Skris originator_port = packet_get_int(); 104960573Skris } 105060573Skris packet_done(); 105160573Skris /* XXX check permission */ 105260573Skris xfree(originator); 105360573Skris /* XXX move to channels.c */ 105460573Skris sock = x11_connect_display(); 105560573Skris if (sock >= 0) { 105660573Skris id = channel_new("x11", SSH_CHANNEL_X11_OPEN, 105765668Skris sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, 105869587Sgreen CHAN_X11_PACKET_DEFAULT, 0, xstrdup("x11"), 1); 105960573Skris c = channel_lookup(id); 106060573Skris } 106160573Skris } 106260573Skris/* XXX duplicate : */ 106360573Skris if (c != NULL) { 106460573Skris debug("confirm %s", ctype); 106560573Skris c->remote_id = rchan; 106660573Skris c->remote_window = rwindow; 106760573Skris c->remote_maxpacket = rmaxpack; 106860573Skris 106960573Skris packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 107060573Skris packet_put_int(c->remote_id); 107160573Skris packet_put_int(c->self); 107260573Skris packet_put_int(c->local_window); 107360573Skris packet_put_int(c->local_maxpacket); 107460573Skris packet_send(); 107560573Skris } else { 107660573Skris debug("failure %s", ctype); 107760573Skris packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 107860573Skris packet_put_int(rchan); 107960573Skris packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); 108060573Skris packet_put_cstring("bla bla"); 108160573Skris packet_put_cstring(""); 108260573Skris packet_send(); 108360573Skris } 108460573Skris xfree(ctype); 108560573Skris} 108660573Skris 108760573Skrisvoid 108860573Skrisclient_init_dispatch_20() 108960573Skris{ 109060573Skris dispatch_init(&dispatch_protocol_error); 109160573Skris dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); 109260573Skris dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); 109360573Skris dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); 109460573Skris dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); 109560573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); 109660573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 109760573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 109860573Skris dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &channel_input_channel_request); 109960573Skris dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); 110060573Skris} 110160573Skrisvoid 110260573Skrisclient_init_dispatch_13() 110360573Skris{ 110460573Skris dispatch_init(NULL); 110560573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); 110660573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); 110760573Skris dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); 110860573Skris dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 110960573Skris dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 111060573Skris dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); 111160573Skris dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status); 111260573Skris dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data); 111360573Skris dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data); 111468700Sgreen 111568700Sgreen dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ? 111669587Sgreen &auth_input_open_request : &deny_input_open); 111768700Sgreen dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ? 111869587Sgreen &x11_input_open : &deny_input_open); 111960573Skris} 112060573Skrisvoid 112160573Skrisclient_init_dispatch_15() 112260573Skris{ 112360573Skris client_init_dispatch_13(); 112460573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); 112560573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose); 112660573Skris} 112760573Skrisvoid 112860573Skrisclient_init_dispatch() 112960573Skris{ 113060573Skris if (compat20) 113160573Skris client_init_dispatch_20(); 113260573Skris else if (compat13) 113360573Skris client_init_dispatch_13(); 113460573Skris else 113560573Skris client_init_dispatch_15(); 113660573Skris} 113760573Skris 113860573Skrisvoid 113960573Skrisclient_input_channel_req(int id, void *arg) 114060573Skris{ 114160573Skris Channel *c = NULL; 114260573Skris unsigned int len; 114360573Skris int success = 0; 114460573Skris int reply; 114560573Skris char *rtype; 114660573Skris 114760573Skris rtype = packet_get_string(&len); 114860573Skris reply = packet_get_char(); 114960573Skris 115060573Skris debug("client_input_channel_req: rtype %s reply %d", rtype, reply); 115160573Skris 115260573Skris c = channel_lookup(id); 115360573Skris if (c == NULL) 115469587Sgreen fatal("client_input_channel_req: channel %d: bad channel", id); 115560573Skris 115660573Skris if (session_ident == -1) { 115760573Skris error("client_input_channel_req: no channel %d", id); 115860573Skris } else if (id != session_ident) { 115960573Skris error("client_input_channel_req: bad channel %d != %d", 116060573Skris id, session_ident); 116160573Skris } else if (strcmp(rtype, "exit-status") == 0) { 116260573Skris success = 1; 116360573Skris exit_status = packet_get_int(); 116460573Skris packet_done(); 116560573Skris } 116660573Skris if (reply) { 116760573Skris packet_start(success ? 116860573Skris SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); 116960573Skris packet_put_int(c->remote_id); 117060573Skris packet_send(); 117160573Skris } 117260573Skris xfree(rtype); 117360573Skris} 117460573Skris 117560573Skrisvoid 117660573Skrisclient_set_session_ident(int id) 117760573Skris{ 117869587Sgreen debug2("client_set_session_ident: id %d", id); 117960573Skris session_ident = id; 118060573Skris channel_register_callback(id, SSH2_MSG_CHANNEL_REQUEST, 118160573Skris client_input_channel_req, (void *)0); 118260573Skris} 1183