clientloop.c revision 57429
157429Smarkm/* 257429Smarkm * 357429Smarkm * clientloop.c 457429Smarkm * 557429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi> 657429Smarkm * 757429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 857429Smarkm * All rights reserved 957429Smarkm * 1057429Smarkm * 1157429Smarkm * Created: Sat Sep 23 12:23:57 1995 ylo 1257429Smarkm * 1357429Smarkm * The main loop for the interactive session (client side). 1457429Smarkm * 1557429Smarkm */ 1657429Smarkm 1757429Smarkm#include "includes.h" 1857429SmarkmRCSID("$Id: clientloop.c,v 1.14 1999/12/06 20:15:26 deraadt Exp $"); 1957429Smarkm 2057429Smarkm#include "xmalloc.h" 2157429Smarkm#include "ssh.h" 2257429Smarkm#include "packet.h" 2357429Smarkm#include "buffer.h" 2457429Smarkm#include "authfd.h" 2557429Smarkm#include "readconf.h" 2657429Smarkm 2757429Smarkm/* Flag indicating that stdin should be redirected from /dev/null. */ 2857429Smarkmextern int stdin_null_flag; 2957429Smarkm 3057429Smarkm/* 3157429Smarkm * Name of the host we are connecting to. This is the name given on the 3257429Smarkm * command line, or the HostName specified for the user-supplied name in a 3357429Smarkm * configuration file. 3457429Smarkm */ 3557429Smarkmextern char *host; 3657429Smarkm 3757429Smarkm/* 3857429Smarkm * Flag to indicate that we have received a window change signal which has 3957429Smarkm * not yet been processed. This will cause a message indicating the new 4057429Smarkm * window size to be sent to the server a little later. This is volatile 4157429Smarkm * because this is updated in a signal handler. 4257429Smarkm */ 4357429Smarkmstatic volatile int received_window_change_signal = 0; 4457429Smarkm 4557429Smarkm/* Terminal modes, as saved by enter_raw_mode. */ 4657429Smarkmstatic struct termios saved_tio; 4757429Smarkm 4857429Smarkm/* 4957429Smarkm * Flag indicating whether we are in raw mode. This is used by 5057429Smarkm * enter_raw_mode and leave_raw_mode. 5157429Smarkm */ 5257429Smarkmstatic int in_raw_mode = 0; 5357429Smarkm 5457429Smarkm/* Flag indicating whether the user\'s terminal is in non-blocking mode. */ 5557429Smarkmstatic int in_non_blocking_mode = 0; 5657429Smarkm 5757429Smarkm/* Common data for the client loop code. */ 5857429Smarkmstatic int escape_pending; /* Last character was the escape character */ 5957429Smarkmstatic int last_was_cr; /* Last character was a newline. */ 6057429Smarkmstatic int exit_status; /* Used to store the exit status of the command. */ 6157429Smarkmstatic int stdin_eof; /* EOF has been encountered on standard error. */ 6257429Smarkmstatic Buffer stdin_buffer; /* Buffer for stdin data. */ 6357429Smarkmstatic Buffer stdout_buffer; /* Buffer for stdout data. */ 6457429Smarkmstatic Buffer stderr_buffer; /* Buffer for stderr data. */ 6557429Smarkmstatic unsigned int buffer_high;/* Soft max buffer size. */ 6657429Smarkmstatic int max_fd; /* Maximum file descriptor number in select(). */ 6757429Smarkmstatic int connection_in; /* Connection to server (input). */ 6857429Smarkmstatic int connection_out; /* Connection to server (output). */ 6957429Smarkmstatic unsigned long stdin_bytes, stdout_bytes, stderr_bytes; 7057429Smarkmstatic int quit_pending; /* Set to non-zero to quit the client loop. */ 7157429Smarkmstatic int escape_char; /* Escape character. */ 7257429Smarkm 7357429Smarkm/* Returns the user\'s terminal to normal mode if it had been put in raw mode. */ 7457429Smarkm 7557429Smarkmvoid 7657429Smarkmleave_raw_mode() 7757429Smarkm{ 7857429Smarkm if (!in_raw_mode) 7957429Smarkm return; 8057429Smarkm in_raw_mode = 0; 8157429Smarkm if (tcsetattr(fileno(stdin), TCSADRAIN, &saved_tio) < 0) 8257429Smarkm perror("tcsetattr"); 8357429Smarkm 8457429Smarkm fatal_remove_cleanup((void (*) (void *)) leave_raw_mode, NULL); 8557429Smarkm} 8657429Smarkm 8757429Smarkm/* Puts the user\'s terminal in raw mode. */ 8857429Smarkm 8957429Smarkmvoid 9057429Smarkmenter_raw_mode() 9157429Smarkm{ 9257429Smarkm struct termios tio; 9357429Smarkm 9457429Smarkm if (tcgetattr(fileno(stdin), &tio) < 0) 9557429Smarkm perror("tcgetattr"); 9657429Smarkm saved_tio = tio; 9757429Smarkm tio.c_iflag |= IGNPAR; 9857429Smarkm tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); 9957429Smarkm tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); 10057429Smarkm#ifdef IEXTEN 10157429Smarkm tio.c_lflag &= ~IEXTEN; 10257429Smarkm#endif /* IEXTEN */ 10357429Smarkm tio.c_oflag &= ~OPOST; 10457429Smarkm tio.c_cc[VMIN] = 1; 10557429Smarkm tio.c_cc[VTIME] = 0; 10657429Smarkm if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0) 10757429Smarkm perror("tcsetattr"); 10857429Smarkm in_raw_mode = 1; 10957429Smarkm 11057429Smarkm fatal_add_cleanup((void (*) (void *)) leave_raw_mode, NULL); 11157429Smarkm} 11257429Smarkm 11357429Smarkm/* Restores stdin to blocking mode. */ 11457429Smarkm 11557429Smarkmvoid 11657429Smarkmleave_non_blocking() 11757429Smarkm{ 11857429Smarkm if (in_non_blocking_mode) { 11957429Smarkm (void) fcntl(fileno(stdin), F_SETFL, 0); 12057429Smarkm in_non_blocking_mode = 0; 12157429Smarkm fatal_remove_cleanup((void (*) (void *)) leave_non_blocking, NULL); 12257429Smarkm } 12357429Smarkm} 12457429Smarkm 12557429Smarkm/* Puts stdin terminal in non-blocking mode. */ 12657429Smarkm 12757429Smarkmvoid 12857429Smarkmenter_non_blocking() 12957429Smarkm{ 13057429Smarkm in_non_blocking_mode = 1; 13157429Smarkm (void) fcntl(fileno(stdin), F_SETFL, O_NONBLOCK); 13257429Smarkm fatal_add_cleanup((void (*) (void *)) leave_non_blocking, NULL); 13357429Smarkm} 13457429Smarkm 13557429Smarkm/* 13657429Smarkm * Signal handler for the window change signal (SIGWINCH). This just sets a 13757429Smarkm * flag indicating that the window has changed. 13857429Smarkm */ 13957429Smarkm 14057429Smarkmvoid 14157429Smarkmwindow_change_handler(int sig) 14257429Smarkm{ 14357429Smarkm received_window_change_signal = 1; 14457429Smarkm signal(SIGWINCH, window_change_handler); 14557429Smarkm} 14657429Smarkm 14757429Smarkm/* 14857429Smarkm * Signal handler for signals that cause the program to terminate. These 14957429Smarkm * signals must be trapped to restore terminal modes. 15057429Smarkm */ 15157429Smarkm 15257429Smarkmvoid 15357429Smarkmsignal_handler(int sig) 15457429Smarkm{ 15557429Smarkm if (in_raw_mode) 15657429Smarkm leave_raw_mode(); 15757429Smarkm if (in_non_blocking_mode) 15857429Smarkm leave_non_blocking(); 15957429Smarkm channel_stop_listening(); 16057429Smarkm packet_close(); 16157429Smarkm fatal("Killed by signal %d.", sig); 16257429Smarkm} 16357429Smarkm 16457429Smarkm/* 16557429Smarkm * Returns current time in seconds from Jan 1, 1970 with the maximum 16657429Smarkm * available resolution. 16757429Smarkm */ 16857429Smarkm 16957429Smarkmdouble 17057429Smarkmget_current_time() 17157429Smarkm{ 17257429Smarkm struct timeval tv; 17357429Smarkm gettimeofday(&tv, NULL); 17457429Smarkm return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; 17557429Smarkm} 17657429Smarkm 17757429Smarkm/* 17857429Smarkm * This is called when the interactive is entered. This checks if there is 17957429Smarkm * an EOF coming on stdin. We must check this explicitly, as select() does 18057429Smarkm * not appear to wake up when redirecting from /dev/null. 18157429Smarkm */ 18257429Smarkm 18357429Smarkmvoid 18457429Smarkmclient_check_initial_eof_on_stdin() 18557429Smarkm{ 18657429Smarkm int len; 18757429Smarkm char buf[1]; 18857429Smarkm 18957429Smarkm /* 19057429Smarkm * If standard input is to be "redirected from /dev/null", we simply 19157429Smarkm * mark that we have seen an EOF and send an EOF message to the 19257429Smarkm * server. Otherwise, we try to read a single character; it appears 19357429Smarkm * that for some files, such /dev/null, select() never wakes up for 19457429Smarkm * read for this descriptor, which means that we never get EOF. This 19557429Smarkm * way we will get the EOF if stdin comes from /dev/null or similar. 19657429Smarkm */ 19757429Smarkm if (stdin_null_flag) { 19857429Smarkm /* Fake EOF on stdin. */ 19957429Smarkm debug("Sending eof."); 20057429Smarkm stdin_eof = 1; 20157429Smarkm packet_start(SSH_CMSG_EOF); 20257429Smarkm packet_send(); 20357429Smarkm } else { 20457429Smarkm enter_non_blocking(); 20557429Smarkm 20657429Smarkm /* Check for immediate EOF on stdin. */ 20757429Smarkm len = read(fileno(stdin), buf, 1); 20857429Smarkm if (len == 0) { 20957429Smarkm /* EOF. Record that we have seen it and send EOF to server. */ 21057429Smarkm debug("Sending eof."); 21157429Smarkm stdin_eof = 1; 21257429Smarkm packet_start(SSH_CMSG_EOF); 21357429Smarkm packet_send(); 21457429Smarkm } else if (len > 0) { 21557429Smarkm /* 21657429Smarkm * Got data. We must store the data in the buffer, 21757429Smarkm * and also process it as an escape character if 21857429Smarkm * appropriate. 21957429Smarkm */ 22057429Smarkm if ((unsigned char) buf[0] == escape_char) 22157429Smarkm escape_pending = 1; 22257429Smarkm else { 22357429Smarkm buffer_append(&stdin_buffer, buf, 1); 22457429Smarkm stdin_bytes += 1; 22557429Smarkm } 22657429Smarkm } 22757429Smarkm leave_non_blocking(); 22857429Smarkm } 22957429Smarkm} 23057429Smarkm 23157429Smarkm/* 23257429Smarkm * Get packets from the connection input buffer, and process them as long as 23357429Smarkm * there are packets available. 23457429Smarkm */ 23557429Smarkm 23657429Smarkmvoid 23757429Smarkmclient_process_buffered_input_packets() 23857429Smarkm{ 23957429Smarkm int type; 24057429Smarkm char *data; 24157429Smarkm unsigned int data_len; 24257429Smarkm int payload_len; 24357429Smarkm 24457429Smarkm /* Process any buffered packets from the server. */ 24557429Smarkm while (!quit_pending && 24657429Smarkm (type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) { 24757429Smarkm switch (type) { 24857429Smarkm 24957429Smarkm case SSH_SMSG_STDOUT_DATA: 25057429Smarkm data = packet_get_string(&data_len); 25157429Smarkm packet_integrity_check(payload_len, 4 + data_len, type); 25257429Smarkm buffer_append(&stdout_buffer, data, data_len); 25357429Smarkm stdout_bytes += data_len; 25457429Smarkm memset(data, 0, data_len); 25557429Smarkm xfree(data); 25657429Smarkm break; 25757429Smarkm 25857429Smarkm case SSH_SMSG_STDERR_DATA: 25957429Smarkm data = packet_get_string(&data_len); 26057429Smarkm packet_integrity_check(payload_len, 4 + data_len, type); 26157429Smarkm buffer_append(&stderr_buffer, data, data_len); 26257429Smarkm stdout_bytes += data_len; 26357429Smarkm memset(data, 0, data_len); 26457429Smarkm xfree(data); 26557429Smarkm break; 26657429Smarkm 26757429Smarkm case SSH_SMSG_EXITSTATUS: 26857429Smarkm packet_integrity_check(payload_len, 4, type); 26957429Smarkm exit_status = packet_get_int(); 27057429Smarkm /* Acknowledge the exit. */ 27157429Smarkm packet_start(SSH_CMSG_EXIT_CONFIRMATION); 27257429Smarkm packet_send(); 27357429Smarkm /* 27457429Smarkm * Must wait for packet to be sent since we are 27557429Smarkm * exiting the loop. 27657429Smarkm */ 27757429Smarkm packet_write_wait(); 27857429Smarkm /* Flag that we want to exit. */ 27957429Smarkm quit_pending = 1; 28057429Smarkm break; 28157429Smarkm 28257429Smarkm case SSH_SMSG_X11_OPEN: 28357429Smarkm x11_input_open(payload_len); 28457429Smarkm break; 28557429Smarkm 28657429Smarkm case SSH_MSG_PORT_OPEN: 28757429Smarkm channel_input_port_open(payload_len); 28857429Smarkm break; 28957429Smarkm 29057429Smarkm case SSH_SMSG_AGENT_OPEN: 29157429Smarkm packet_integrity_check(payload_len, 4, type); 29257429Smarkm auth_input_open_request(); 29357429Smarkm break; 29457429Smarkm 29557429Smarkm case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: 29657429Smarkm packet_integrity_check(payload_len, 4 + 4, type); 29757429Smarkm channel_input_open_confirmation(); 29857429Smarkm break; 29957429Smarkm 30057429Smarkm case SSH_MSG_CHANNEL_OPEN_FAILURE: 30157429Smarkm packet_integrity_check(payload_len, 4, type); 30257429Smarkm channel_input_open_failure(); 30357429Smarkm break; 30457429Smarkm 30557429Smarkm case SSH_MSG_CHANNEL_DATA: 30657429Smarkm channel_input_data(payload_len); 30757429Smarkm break; 30857429Smarkm 30957429Smarkm case SSH_MSG_CHANNEL_CLOSE: 31057429Smarkm packet_integrity_check(payload_len, 4, type); 31157429Smarkm channel_input_close(); 31257429Smarkm break; 31357429Smarkm 31457429Smarkm case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION: 31557429Smarkm packet_integrity_check(payload_len, 4, type); 31657429Smarkm channel_input_close_confirmation(); 31757429Smarkm break; 31857429Smarkm 31957429Smarkm default: 32057429Smarkm /* 32157429Smarkm * Any unknown packets received during the actual 32257429Smarkm * session cause the session to terminate. This is 32357429Smarkm * intended to make debugging easier since no 32457429Smarkm * confirmations are sent. Any compatible protocol 32557429Smarkm * extensions must be negotiated during the 32657429Smarkm * preparatory phase. 32757429Smarkm */ 32857429Smarkm packet_disconnect("Protocol error during session: type %d", 32957429Smarkm type); 33057429Smarkm } 33157429Smarkm } 33257429Smarkm} 33357429Smarkm 33457429Smarkm/* 33557429Smarkm * Make packets from buffered stdin data, and buffer them for sending to the 33657429Smarkm * connection. 33757429Smarkm */ 33857429Smarkm 33957429Smarkmvoid 34057429Smarkmclient_make_packets_from_stdin_data() 34157429Smarkm{ 34257429Smarkm unsigned int len; 34357429Smarkm 34457429Smarkm /* Send buffered stdin data to the server. */ 34557429Smarkm while (buffer_len(&stdin_buffer) > 0 && 34657429Smarkm packet_not_very_much_data_to_write()) { 34757429Smarkm len = buffer_len(&stdin_buffer); 34857429Smarkm /* Keep the packets at reasonable size. */ 34957429Smarkm if (len > packet_get_maxsize()) 35057429Smarkm len = packet_get_maxsize(); 35157429Smarkm packet_start(SSH_CMSG_STDIN_DATA); 35257429Smarkm packet_put_string(buffer_ptr(&stdin_buffer), len); 35357429Smarkm packet_send(); 35457429Smarkm buffer_consume(&stdin_buffer, len); 35557429Smarkm /* If we have a pending EOF, send it now. */ 35657429Smarkm if (stdin_eof && buffer_len(&stdin_buffer) == 0) { 35757429Smarkm packet_start(SSH_CMSG_EOF); 35857429Smarkm packet_send(); 35957429Smarkm } 36057429Smarkm } 36157429Smarkm} 36257429Smarkm 36357429Smarkm/* 36457429Smarkm * Checks if the client window has changed, and sends a packet about it to 36557429Smarkm * the server if so. The actual change is detected elsewhere (by a software 36657429Smarkm * interrupt on Unix); this just checks the flag and sends a message if 36757429Smarkm * appropriate. 36857429Smarkm */ 36957429Smarkm 37057429Smarkmvoid 37157429Smarkmclient_check_window_change() 37257429Smarkm{ 37357429Smarkm /* Send possible window change message to the server. */ 37457429Smarkm if (received_window_change_signal) { 37557429Smarkm struct winsize ws; 37657429Smarkm 37757429Smarkm /* Clear the window change indicator. */ 37857429Smarkm received_window_change_signal = 0; 37957429Smarkm 38057429Smarkm /* Read new window size. */ 38157429Smarkm if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) { 38257429Smarkm /* Successful, send the packet now. */ 38357429Smarkm packet_start(SSH_CMSG_WINDOW_SIZE); 38457429Smarkm packet_put_int(ws.ws_row); 38557429Smarkm packet_put_int(ws.ws_col); 38657429Smarkm packet_put_int(ws.ws_xpixel); 38757429Smarkm packet_put_int(ws.ws_ypixel); 38857429Smarkm packet_send(); 38957429Smarkm } 39057429Smarkm } 39157429Smarkm} 39257429Smarkm 39357429Smarkm/* 39457429Smarkm * Waits until the client can do something (some data becomes available on 39557429Smarkm * one of the file descriptors). 39657429Smarkm */ 39757429Smarkm 39857429Smarkmvoid 39957429Smarkmclient_wait_until_can_do_something(fd_set * readset, fd_set * writeset) 40057429Smarkm{ 40157429Smarkm /* Initialize select masks. */ 40257429Smarkm FD_ZERO(readset); 40357429Smarkm 40457429Smarkm /* Read from the connection, unless our buffers are full. */ 40557429Smarkm if (buffer_len(&stdout_buffer) < buffer_high && 40657429Smarkm buffer_len(&stderr_buffer) < buffer_high && 40757429Smarkm channel_not_very_much_buffered_data()) 40857429Smarkm FD_SET(connection_in, readset); 40957429Smarkm 41057429Smarkm /* 41157429Smarkm * Read from stdin, unless we have seen EOF or have very much 41257429Smarkm * buffered data to send to the server. 41357429Smarkm */ 41457429Smarkm if (!stdin_eof && packet_not_very_much_data_to_write()) 41557429Smarkm FD_SET(fileno(stdin), readset); 41657429Smarkm 41757429Smarkm FD_ZERO(writeset); 41857429Smarkm 41957429Smarkm /* Add any selections by the channel mechanism. */ 42057429Smarkm channel_prepare_select(readset, writeset); 42157429Smarkm 42257429Smarkm /* Select server connection if have data to write to the server. */ 42357429Smarkm if (packet_have_data_to_write()) 42457429Smarkm FD_SET(connection_out, writeset); 42557429Smarkm 42657429Smarkm /* Select stdout if have data in buffer. */ 42757429Smarkm if (buffer_len(&stdout_buffer) > 0) 42857429Smarkm FD_SET(fileno(stdout), writeset); 42957429Smarkm 43057429Smarkm /* Select stderr if have data in buffer. */ 43157429Smarkm if (buffer_len(&stderr_buffer) > 0) 43257429Smarkm FD_SET(fileno(stderr), writeset); 43357429Smarkm 43457429Smarkm /* Update maximum file descriptor number, if appropriate. */ 43557429Smarkm if (channel_max_fd() > max_fd) 43657429Smarkm max_fd = channel_max_fd(); 43757429Smarkm 43857429Smarkm /* 43957429Smarkm * Wait for something to happen. This will suspend the process until 44057429Smarkm * some selected descriptor can be read, written, or has some other 44157429Smarkm * event pending. Note: if you want to implement SSH_MSG_IGNORE 44257429Smarkm * messages to fool traffic analysis, this might be the place to do 44357429Smarkm * it: just have a random timeout for the select, and send a random 44457429Smarkm * SSH_MSG_IGNORE packet when the timeout expires. 44557429Smarkm */ 44657429Smarkm 44757429Smarkm if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0) { 44857429Smarkm char buf[100]; 44957429Smarkm /* Some systems fail to clear these automatically. */ 45057429Smarkm FD_ZERO(readset); 45157429Smarkm FD_ZERO(writeset); 45257429Smarkm if (errno == EINTR) 45357429Smarkm return; 45457429Smarkm /* Note: we might still have data in the buffers. */ 45557429Smarkm snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); 45657429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 45757429Smarkm stderr_bytes += strlen(buf); 45857429Smarkm quit_pending = 1; 45957429Smarkm } 46057429Smarkm} 46157429Smarkm 46257429Smarkmvoid 46357429Smarkmclient_suspend_self() 46457429Smarkm{ 46557429Smarkm struct winsize oldws, newws; 46657429Smarkm 46757429Smarkm /* Flush stdout and stderr buffers. */ 46857429Smarkm if (buffer_len(&stdout_buffer) > 0) 46957429Smarkm atomicio(write, fileno(stdout), buffer_ptr(&stdout_buffer), 47057429Smarkm buffer_len(&stdout_buffer)); 47157429Smarkm if (buffer_len(&stderr_buffer) > 0) 47257429Smarkm atomicio(write, fileno(stderr), buffer_ptr(&stderr_buffer), 47357429Smarkm buffer_len(&stderr_buffer)); 47457429Smarkm 47557429Smarkm leave_raw_mode(); 47657429Smarkm 47757429Smarkm /* 47857429Smarkm * Free (and clear) the buffer to reduce the amount of data that gets 47957429Smarkm * written to swap. 48057429Smarkm */ 48157429Smarkm buffer_free(&stdin_buffer); 48257429Smarkm buffer_free(&stdout_buffer); 48357429Smarkm buffer_free(&stderr_buffer); 48457429Smarkm 48557429Smarkm /* Save old window size. */ 48657429Smarkm ioctl(fileno(stdin), TIOCGWINSZ, &oldws); 48757429Smarkm 48857429Smarkm /* Send the suspend signal to the program itself. */ 48957429Smarkm kill(getpid(), SIGTSTP); 49057429Smarkm 49157429Smarkm /* Check if the window size has changed. */ 49257429Smarkm if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 && 49357429Smarkm (oldws.ws_row != newws.ws_row || 49457429Smarkm oldws.ws_col != newws.ws_col || 49557429Smarkm oldws.ws_xpixel != newws.ws_xpixel || 49657429Smarkm oldws.ws_ypixel != newws.ws_ypixel)) 49757429Smarkm received_window_change_signal = 1; 49857429Smarkm 49957429Smarkm /* OK, we have been continued by the user. Reinitialize buffers. */ 50057429Smarkm buffer_init(&stdin_buffer); 50157429Smarkm buffer_init(&stdout_buffer); 50257429Smarkm buffer_init(&stderr_buffer); 50357429Smarkm 50457429Smarkm enter_raw_mode(); 50557429Smarkm} 50657429Smarkm 50757429Smarkmvoid 50857429Smarkmclient_process_input(fd_set * readset) 50957429Smarkm{ 51057429Smarkm int len, pid; 51157429Smarkm char buf[8192], *s; 51257429Smarkm 51357429Smarkm /* 51457429Smarkm * Read input from the server, and add any such data to the buffer of 51557429Smarkm * the packet subsystem. 51657429Smarkm */ 51757429Smarkm if (FD_ISSET(connection_in, readset)) { 51857429Smarkm /* Read as much as possible. */ 51957429Smarkm len = read(connection_in, buf, sizeof(buf)); 52057429Smarkm if (len == 0) { 52157429Smarkm /* Received EOF. The remote host has closed the connection. */ 52257429Smarkm snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n", 52357429Smarkm host); 52457429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 52557429Smarkm stderr_bytes += strlen(buf); 52657429Smarkm quit_pending = 1; 52757429Smarkm return; 52857429Smarkm } 52957429Smarkm /* 53057429Smarkm * There is a kernel bug on Solaris that causes select to 53157429Smarkm * sometimes wake up even though there is no data available. 53257429Smarkm */ 53357429Smarkm if (len < 0 && errno == EAGAIN) 53457429Smarkm len = 0; 53557429Smarkm 53657429Smarkm if (len < 0) { 53757429Smarkm /* An error has encountered. Perhaps there is a network problem. */ 53857429Smarkm snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n", 53957429Smarkm host, strerror(errno)); 54057429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 54157429Smarkm stderr_bytes += strlen(buf); 54257429Smarkm quit_pending = 1; 54357429Smarkm return; 54457429Smarkm } 54557429Smarkm packet_process_incoming(buf, len); 54657429Smarkm } 54757429Smarkm /* Read input from stdin. */ 54857429Smarkm if (FD_ISSET(fileno(stdin), readset)) { 54957429Smarkm /* Read as much as possible. */ 55057429Smarkm len = read(fileno(stdin), buf, sizeof(buf)); 55157429Smarkm if (len <= 0) { 55257429Smarkm /* 55357429Smarkm * Received EOF or error. They are treated 55457429Smarkm * similarly, except that an error message is printed 55557429Smarkm * if it was an error condition. 55657429Smarkm */ 55757429Smarkm if (len < 0) { 55857429Smarkm snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno)); 55957429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 56057429Smarkm stderr_bytes += strlen(buf); 56157429Smarkm } 56257429Smarkm /* Mark that we have seen EOF. */ 56357429Smarkm stdin_eof = 1; 56457429Smarkm /* 56557429Smarkm * Send an EOF message to the server unless there is 56657429Smarkm * data in the buffer. If there is data in the 56757429Smarkm * buffer, no message will be sent now. Code 56857429Smarkm * elsewhere will send the EOF when the buffer 56957429Smarkm * becomes empty if stdin_eof is set. 57057429Smarkm */ 57157429Smarkm if (buffer_len(&stdin_buffer) == 0) { 57257429Smarkm packet_start(SSH_CMSG_EOF); 57357429Smarkm packet_send(); 57457429Smarkm } 57557429Smarkm } else if (escape_char == -1) { 57657429Smarkm /* 57757429Smarkm * Normal successful read, and no escape character. 57857429Smarkm * Just append the data to buffer. 57957429Smarkm */ 58057429Smarkm buffer_append(&stdin_buffer, buf, len); 58157429Smarkm stdin_bytes += len; 58257429Smarkm } else { 58357429Smarkm /* 58457429Smarkm * Normal, successful read. But we have an escape character 58557429Smarkm * and have to process the characters one by one. 58657429Smarkm */ 58757429Smarkm unsigned int i; 58857429Smarkm for (i = 0; i < len; i++) { 58957429Smarkm unsigned char ch; 59057429Smarkm /* Get one character at a time. */ 59157429Smarkm ch = buf[i]; 59257429Smarkm 59357429Smarkm if (escape_pending) { 59457429Smarkm /* We have previously seen an escape character. */ 59557429Smarkm /* Clear the flag now. */ 59657429Smarkm escape_pending = 0; 59757429Smarkm /* Process the escaped character. */ 59857429Smarkm switch (ch) { 59957429Smarkm case '.': 60057429Smarkm /* Terminate the connection. */ 60157429Smarkm snprintf(buf, sizeof buf, "%c.\r\n", escape_char); 60257429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 60357429Smarkm stderr_bytes += strlen(buf); 60457429Smarkm quit_pending = 1; 60557429Smarkm return; 60657429Smarkm 60757429Smarkm case 'Z' - 64: 60857429Smarkm /* Suspend the program. */ 60957429Smarkm /* Print a message to that effect to the user. */ 61057429Smarkm snprintf(buf, sizeof buf, "%c^Z\r\n", escape_char); 61157429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 61257429Smarkm stderr_bytes += strlen(buf); 61357429Smarkm 61457429Smarkm /* Restore terminal modes and suspend. */ 61557429Smarkm client_suspend_self(); 61657429Smarkm 61757429Smarkm /* We have been continued. */ 61857429Smarkm continue; 61957429Smarkm 62057429Smarkm case '&': 62157429Smarkm /* 62257429Smarkm * Detach the program (continue to serve connections, 62357429Smarkm * but put in background and no more new connections). 62457429Smarkm */ 62557429Smarkm if (!stdin_eof) { 62657429Smarkm /* 62757429Smarkm * Sending SSH_CMSG_EOF alone does not always appear 62857429Smarkm * to be enough. So we try to send an EOF character 62957429Smarkm * first. 63057429Smarkm */ 63157429Smarkm packet_start(SSH_CMSG_STDIN_DATA); 63257429Smarkm packet_put_string("\004", 1); 63357429Smarkm packet_send(); 63457429Smarkm /* Close stdin. */ 63557429Smarkm stdin_eof = 1; 63657429Smarkm if (buffer_len(&stdin_buffer) == 0) { 63757429Smarkm packet_start(SSH_CMSG_EOF); 63857429Smarkm packet_send(); 63957429Smarkm } 64057429Smarkm } 64157429Smarkm /* Restore tty modes. */ 64257429Smarkm leave_raw_mode(); 64357429Smarkm 64457429Smarkm /* Stop listening for new connections. */ 64557429Smarkm channel_stop_listening(); 64657429Smarkm 64757429Smarkm printf("%c& [backgrounded]\n", escape_char); 64857429Smarkm 64957429Smarkm /* Fork into background. */ 65057429Smarkm pid = fork(); 65157429Smarkm if (pid < 0) { 65257429Smarkm error("fork: %.100s", strerror(errno)); 65357429Smarkm continue; 65457429Smarkm } 65557429Smarkm if (pid != 0) { /* This is the parent. */ 65657429Smarkm /* The parent just exits. */ 65757429Smarkm exit(0); 65857429Smarkm } 65957429Smarkm /* The child continues serving connections. */ 66057429Smarkm continue; 66157429Smarkm 66257429Smarkm case '?': 66357429Smarkm snprintf(buf, sizeof buf, 66457429Smarkm"%c?\r\n\ 66557429SmarkmSupported escape sequences:\r\n\ 66657429Smarkm~. - terminate connection\r\n\ 66757429Smarkm~^Z - suspend ssh\r\n\ 66857429Smarkm~# - list forwarded connections\r\n\ 66957429Smarkm~& - background ssh (when waiting for connections to terminate)\r\n\ 67057429Smarkm~? - this message\r\n\ 67157429Smarkm~~ - send the escape character by typing it twice\r\n\ 67257429Smarkm(Note that escapes are only recognized immediately after newline.)\r\n", 67357429Smarkm escape_char); 67457429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 67557429Smarkm continue; 67657429Smarkm 67757429Smarkm case '#': 67857429Smarkm snprintf(buf, sizeof buf, "%c#\r\n", escape_char); 67957429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 68057429Smarkm s = channel_open_message(); 68157429Smarkm buffer_append(&stderr_buffer, s, strlen(s)); 68257429Smarkm xfree(s); 68357429Smarkm continue; 68457429Smarkm 68557429Smarkm default: 68657429Smarkm if (ch != escape_char) { 68757429Smarkm /* 68857429Smarkm * Escape character followed by non-special character. 68957429Smarkm * Append both to the input buffer. 69057429Smarkm */ 69157429Smarkm buf[0] = escape_char; 69257429Smarkm buf[1] = ch; 69357429Smarkm buffer_append(&stdin_buffer, buf, 2); 69457429Smarkm stdin_bytes += 2; 69557429Smarkm continue; 69657429Smarkm } 69757429Smarkm /* 69857429Smarkm * Note that escape character typed twice 69957429Smarkm * falls through here; the latter gets processed 70057429Smarkm * as a normal character below. 70157429Smarkm */ 70257429Smarkm break; 70357429Smarkm } 70457429Smarkm } else { 70557429Smarkm /* 70657429Smarkm * The previous character was not an escape char. Check if this 70757429Smarkm * is an escape. 70857429Smarkm */ 70957429Smarkm if (last_was_cr && ch == escape_char) { 71057429Smarkm /* It is. Set the flag and continue to next character. */ 71157429Smarkm escape_pending = 1; 71257429Smarkm continue; 71357429Smarkm } 71457429Smarkm } 71557429Smarkm 71657429Smarkm /* 71757429Smarkm * Normal character. Record whether it was a newline, 71857429Smarkm * and append it to the buffer. 71957429Smarkm */ 72057429Smarkm last_was_cr = (ch == '\r' || ch == '\n'); 72157429Smarkm buf[0] = ch; 72257429Smarkm buffer_append(&stdin_buffer, buf, 1); 72357429Smarkm stdin_bytes += 1; 72457429Smarkm continue; 72557429Smarkm } 72657429Smarkm } 72757429Smarkm } 72857429Smarkm} 72957429Smarkm 73057429Smarkmvoid 73157429Smarkmclient_process_output(fd_set * writeset) 73257429Smarkm{ 73357429Smarkm int len; 73457429Smarkm char buf[100]; 73557429Smarkm 73657429Smarkm /* Write buffered output to stdout. */ 73757429Smarkm if (FD_ISSET(fileno(stdout), writeset)) { 73857429Smarkm /* Write as much data as possible. */ 73957429Smarkm len = write(fileno(stdout), buffer_ptr(&stdout_buffer), 74057429Smarkm buffer_len(&stdout_buffer)); 74157429Smarkm if (len <= 0) { 74257429Smarkm if (errno == EAGAIN) 74357429Smarkm len = 0; 74457429Smarkm else { 74557429Smarkm /* 74657429Smarkm * An error or EOF was encountered. Put an 74757429Smarkm * error message to stderr buffer. 74857429Smarkm */ 74957429Smarkm snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno)); 75057429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 75157429Smarkm stderr_bytes += strlen(buf); 75257429Smarkm quit_pending = 1; 75357429Smarkm return; 75457429Smarkm } 75557429Smarkm } 75657429Smarkm /* Consume printed data from the buffer. */ 75757429Smarkm buffer_consume(&stdout_buffer, len); 75857429Smarkm } 75957429Smarkm /* Write buffered output to stderr. */ 76057429Smarkm if (FD_ISSET(fileno(stderr), writeset)) { 76157429Smarkm /* Write as much data as possible. */ 76257429Smarkm len = write(fileno(stderr), buffer_ptr(&stderr_buffer), 76357429Smarkm buffer_len(&stderr_buffer)); 76457429Smarkm if (len <= 0) { 76557429Smarkm if (errno == EAGAIN) 76657429Smarkm len = 0; 76757429Smarkm else { 76857429Smarkm /* EOF or error, but can't even print error message. */ 76957429Smarkm quit_pending = 1; 77057429Smarkm return; 77157429Smarkm } 77257429Smarkm } 77357429Smarkm /* Consume printed characters from the buffer. */ 77457429Smarkm buffer_consume(&stderr_buffer, len); 77557429Smarkm } 77657429Smarkm} 77757429Smarkm 77857429Smarkm/* 77957429Smarkm * Implements the interactive session with the server. This is called after 78057429Smarkm * the user has been authenticated, and a command has been started on the 78157429Smarkm * remote host. If escape_char != -1, it is the character used as an escape 78257429Smarkm * character for terminating or suspending the session. 78357429Smarkm */ 78457429Smarkm 78557429Smarkmint 78657429Smarkmclient_loop(int have_pty, int escape_char_arg) 78757429Smarkm{ 78857429Smarkm extern Options options; 78957429Smarkm double start_time, total_time; 79057429Smarkm int len; 79157429Smarkm char buf[100]; 79257429Smarkm 79357429Smarkm debug("Entering interactive session."); 79457429Smarkm 79557429Smarkm start_time = get_current_time(); 79657429Smarkm 79757429Smarkm /* Initialize variables. */ 79857429Smarkm escape_pending = 0; 79957429Smarkm last_was_cr = 1; 80057429Smarkm exit_status = -1; 80157429Smarkm stdin_eof = 0; 80257429Smarkm buffer_high = 64 * 1024; 80357429Smarkm connection_in = packet_get_connection_in(); 80457429Smarkm connection_out = packet_get_connection_out(); 80557429Smarkm max_fd = connection_in; 80657429Smarkm if (connection_out > max_fd) 80757429Smarkm max_fd = connection_out; 80857429Smarkm stdin_bytes = 0; 80957429Smarkm stdout_bytes = 0; 81057429Smarkm stderr_bytes = 0; 81157429Smarkm quit_pending = 0; 81257429Smarkm escape_char = escape_char_arg; 81357429Smarkm 81457429Smarkm /* Initialize buffers. */ 81557429Smarkm buffer_init(&stdin_buffer); 81657429Smarkm buffer_init(&stdout_buffer); 81757429Smarkm buffer_init(&stderr_buffer); 81857429Smarkm 81957429Smarkm /* Set signal handlers to restore non-blocking mode. */ 82057429Smarkm signal(SIGINT, signal_handler); 82157429Smarkm signal(SIGQUIT, signal_handler); 82257429Smarkm signal(SIGTERM, signal_handler); 82357429Smarkm signal(SIGPIPE, SIG_IGN); 82457429Smarkm if (have_pty) 82557429Smarkm signal(SIGWINCH, window_change_handler); 82657429Smarkm 82757429Smarkm if (have_pty) 82857429Smarkm enter_raw_mode(); 82957429Smarkm 83057429Smarkm /* Check if we should immediately send of on stdin. */ 83157429Smarkm client_check_initial_eof_on_stdin(); 83257429Smarkm 83357429Smarkm /* Main loop of the client for the interactive session mode. */ 83457429Smarkm while (!quit_pending) { 83557429Smarkm fd_set readset, writeset; 83657429Smarkm 83757429Smarkm /* Process buffered packets sent by the server. */ 83857429Smarkm client_process_buffered_input_packets(); 83957429Smarkm 84057429Smarkm /* 84157429Smarkm * Make packets of buffered stdin data, and buffer them for 84257429Smarkm * sending to the server. 84357429Smarkm */ 84457429Smarkm client_make_packets_from_stdin_data(); 84557429Smarkm 84657429Smarkm /* 84757429Smarkm * Make packets from buffered channel data, and buffer them 84857429Smarkm * for sending to the server. 84957429Smarkm */ 85057429Smarkm if (packet_not_very_much_data_to_write()) 85157429Smarkm channel_output_poll(); 85257429Smarkm 85357429Smarkm /* 85457429Smarkm * Check if the window size has changed, and buffer a message 85557429Smarkm * about it to the server if so. 85657429Smarkm */ 85757429Smarkm client_check_window_change(); 85857429Smarkm 85957429Smarkm if (quit_pending) 86057429Smarkm break; 86157429Smarkm 86257429Smarkm /* 86357429Smarkm * Wait until we have something to do (something becomes 86457429Smarkm * available on one of the descriptors). 86557429Smarkm */ 86657429Smarkm client_wait_until_can_do_something(&readset, &writeset); 86757429Smarkm 86857429Smarkm if (quit_pending) 86957429Smarkm break; 87057429Smarkm 87157429Smarkm /* Do channel operations. */ 87257429Smarkm channel_after_select(&readset, &writeset); 87357429Smarkm 87457429Smarkm /* 87557429Smarkm * Process input from the connection and from stdin. Buffer 87657429Smarkm * any data that is available. 87757429Smarkm */ 87857429Smarkm client_process_input(&readset); 87957429Smarkm 88057429Smarkm /* 88157429Smarkm * Process output to stdout and stderr. Output to the 88257429Smarkm * connection is processed elsewhere (above). 88357429Smarkm */ 88457429Smarkm client_process_output(&writeset); 88557429Smarkm 88657429Smarkm /* Send as much buffered packet data as possible to the sender. */ 88757429Smarkm if (FD_ISSET(connection_out, &writeset)) 88857429Smarkm packet_write_poll(); 88957429Smarkm } 89057429Smarkm 89157429Smarkm /* Terminate the session. */ 89257429Smarkm 89357429Smarkm /* Stop watching for window change. */ 89457429Smarkm if (have_pty) 89557429Smarkm signal(SIGWINCH, SIG_DFL); 89657429Smarkm 89757429Smarkm /* Stop listening for connections. */ 89857429Smarkm channel_stop_listening(); 89957429Smarkm 90057429Smarkm /* 90157429Smarkm * In interactive mode (with pseudo tty) display a message indicating 90257429Smarkm * that the connection has been closed. 90357429Smarkm */ 90457429Smarkm if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { 90557429Smarkm snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host); 90657429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 90757429Smarkm stderr_bytes += strlen(buf); 90857429Smarkm } 90957429Smarkm /* Output any buffered data for stdout. */ 91057429Smarkm while (buffer_len(&stdout_buffer) > 0) { 91157429Smarkm len = write(fileno(stdout), buffer_ptr(&stdout_buffer), 91257429Smarkm buffer_len(&stdout_buffer)); 91357429Smarkm if (len <= 0) { 91457429Smarkm error("Write failed flushing stdout buffer."); 91557429Smarkm break; 91657429Smarkm } 91757429Smarkm buffer_consume(&stdout_buffer, len); 91857429Smarkm } 91957429Smarkm 92057429Smarkm /* Output any buffered data for stderr. */ 92157429Smarkm while (buffer_len(&stderr_buffer) > 0) { 92257429Smarkm len = write(fileno(stderr), buffer_ptr(&stderr_buffer), 92357429Smarkm buffer_len(&stderr_buffer)); 92457429Smarkm if (len <= 0) { 92557429Smarkm error("Write failed flushing stderr buffer."); 92657429Smarkm break; 92757429Smarkm } 92857429Smarkm buffer_consume(&stderr_buffer, len); 92957429Smarkm } 93057429Smarkm 93157429Smarkm if (have_pty) 93257429Smarkm leave_raw_mode(); 93357429Smarkm 93457429Smarkm /* Clear and free any buffers. */ 93557429Smarkm memset(buf, 0, sizeof(buf)); 93657429Smarkm buffer_free(&stdin_buffer); 93757429Smarkm buffer_free(&stdout_buffer); 93857429Smarkm buffer_free(&stderr_buffer); 93957429Smarkm 94057429Smarkm /* Report bytes transferred, and transfer rates. */ 94157429Smarkm total_time = get_current_time() - start_time; 94257429Smarkm debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds", 94357429Smarkm stdin_bytes, stdout_bytes, stderr_bytes, total_time); 94457429Smarkm if (total_time > 0) 94557429Smarkm debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f", 94657429Smarkm stdin_bytes / total_time, stdout_bytes / total_time, 94757429Smarkm stderr_bytes / total_time); 94857429Smarkm 94957429Smarkm /* Return the exit status of the program. */ 95057429Smarkm debug("Exit status %d", exit_status); 95157429Smarkm return exit_status; 95257429Smarkm} 953