version 1.15, 2000/03/28 20:31:26 |
version 1.16, 2000/04/04 15:19:42 |
|
|
* |
* |
* The main loop for the interactive session (client side). |
* The main loop for the interactive session (client side). |
* |
* |
|
* SSH2 support added by Markus Friedl. |
*/ |
*/ |
|
|
#include "includes.h" |
#include "includes.h" |
|
|
#include "authfd.h" |
#include "authfd.h" |
#include "readconf.h" |
#include "readconf.h" |
|
|
|
#include "ssh2.h" |
#include "compat.h" |
#include "compat.h" |
#include "channels.h" |
#include "channels.h" |
#include "dispatch.h" |
#include "dispatch.h" |
|
|
static int quit_pending; /* Set to non-zero to quit the client loop. */ |
static int quit_pending; /* Set to non-zero to quit the client loop. */ |
static int escape_char; /* Escape character. */ |
static int escape_char; /* Escape character. */ |
|
|
|
|
|
void client_init_dispatch(void); |
|
int session_ident = -1; |
|
|
/* Returns the user\'s terminal to normal mode if it had been put in raw mode. */ |
/* Returns the user\'s terminal to normal mode if it had been put in raw mode. */ |
|
|
void |
void |
|
|
void |
void |
client_check_window_change() |
client_check_window_change() |
{ |
{ |
/* Send possible window change message to the server. */ |
struct winsize ws; |
if (received_window_change_signal) { |
|
struct winsize ws; |
|
|
|
/* Clear the window change indicator. */ |
if (! received_window_change_signal) |
received_window_change_signal = 0; |
return; |
|
/** XXX race */ |
|
received_window_change_signal = 0; |
|
|
/* Read new window size. */ |
if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) |
if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) { |
return; |
/* Successful, send the packet now. */ |
|
packet_start(SSH_CMSG_WINDOW_SIZE); |
debug("client_check_window_change: changed"); |
packet_put_int(ws.ws_row); |
|
packet_put_int(ws.ws_col); |
if (compat20) { |
packet_put_int(ws.ws_xpixel); |
channel_request_start(session_ident, "window-change", 0); |
packet_put_int(ws.ws_ypixel); |
packet_put_int(ws.ws_col); |
packet_send(); |
packet_put_int(ws.ws_row); |
} |
packet_put_int(ws.ws_xpixel); |
|
packet_put_int(ws.ws_ypixel); |
|
packet_send(); |
|
} else { |
|
packet_start(SSH_CMSG_WINDOW_SIZE); |
|
packet_put_int(ws.ws_row); |
|
packet_put_int(ws.ws_col); |
|
packet_put_int(ws.ws_xpixel); |
|
packet_put_int(ws.ws_ypixel); |
|
packet_send(); |
} |
} |
} |
} |
|
|
|
|
void |
void |
client_wait_until_can_do_something(fd_set * readset, fd_set * writeset) |
client_wait_until_can_do_something(fd_set * readset, fd_set * writeset) |
{ |
{ |
|
/*debug("client_wait_until_can_do_something"); */ |
|
|
/* Initialize select masks. */ |
/* Initialize select masks. */ |
FD_ZERO(readset); |
FD_ZERO(readset); |
|
|
/* Read from the connection, unless our buffers are full. */ |
/* Read from the connection, unless our buffers are full. */ |
if (buffer_len(&stdout_buffer) < buffer_high && |
if (!compat20) { |
buffer_len(&stderr_buffer) < buffer_high && |
if (buffer_len(&stdout_buffer) < buffer_high && |
channel_not_very_much_buffered_data()) |
buffer_len(&stderr_buffer) < buffer_high && |
|
channel_not_very_much_buffered_data()) |
|
FD_SET(connection_in, readset); |
|
} else { |
FD_SET(connection_in, readset); |
FD_SET(connection_in, readset); |
|
} |
|
|
/* |
/* |
* Read from stdin, unless we have seen EOF or have very much |
* Read from stdin, unless we have seen EOF or have very much |
* buffered data to send to the server. |
* buffered data to send to the server. |
*/ |
*/ |
if (!stdin_eof && packet_not_very_much_data_to_write()) |
if (!compat20) |
FD_SET(fileno(stdin), readset); |
if (!stdin_eof && packet_not_very_much_data_to_write()) |
|
FD_SET(fileno(stdin), readset); |
|
|
FD_ZERO(writeset); |
FD_ZERO(writeset); |
|
|
|
|
if (packet_have_data_to_write()) |
if (packet_have_data_to_write()) |
FD_SET(connection_out, writeset); |
FD_SET(connection_out, writeset); |
|
|
/* Select stdout if have data in buffer. */ |
if (!compat20) { |
if (buffer_len(&stdout_buffer) > 0) |
/* Select stdout if have data in buffer. */ |
FD_SET(fileno(stdout), writeset); |
if (buffer_len(&stdout_buffer) > 0) |
|
FD_SET(fileno(stdout), writeset); |
|
|
/* Select stderr if have data in buffer. */ |
/* Select stderr if have data in buffer. */ |
if (buffer_len(&stderr_buffer) > 0) |
if (buffer_len(&stderr_buffer) > 0) |
FD_SET(fileno(stderr), writeset); |
FD_SET(fileno(stderr), writeset); |
|
} |
|
|
|
/* move UP XXX */ |
/* Update maximum file descriptor number, if appropriate. */ |
/* Update maximum file descriptor number, if appropriate. */ |
if (channel_max_fd() > max_fd) |
if (channel_max_fd() > max_fd) |
max_fd = channel_max_fd(); |
max_fd = channel_max_fd(); |
|
|
if (FD_ISSET(connection_in, readset)) { |
if (FD_ISSET(connection_in, readset)) { |
/* Read as much as possible. */ |
/* Read as much as possible. */ |
len = read(connection_in, buf, sizeof(buf)); |
len = read(connection_in, buf, sizeof(buf)); |
|
/*debug("read connection_in len %d", len); XXX */ |
if (len == 0) { |
if (len == 0) { |
/* Received EOF. The remote host has closed the connection. */ |
/* Received EOF. The remote host has closed the connection. */ |
snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n", |
snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n", |
|
|
} |
} |
packet_process_incoming(buf, len); |
packet_process_incoming(buf, len); |
} |
} |
|
|
|
if (compat20) |
|
return; |
|
|
/* Read input from stdin. */ |
/* Read input from stdin. */ |
if (FD_ISSET(fileno(stdin), readset)) { |
if (FD_ISSET(fileno(stdin), readset)) { |
/* Read as much as possible. */ |
/* Read as much as possible. */ |
|
|
* character for terminating or suspending the session. |
* character for terminating or suspending the session. |
*/ |
*/ |
|
|
void client_init_dispatch(void); |
|
|
|
int |
int |
client_loop(int have_pty, int escape_char_arg) |
client_loop(int have_pty, int escape_char_arg) |
{ |
{ |
|
|
enter_raw_mode(); |
enter_raw_mode(); |
|
|
/* Check if we should immediately send of on stdin. */ |
/* Check if we should immediately send of on stdin. */ |
client_check_initial_eof_on_stdin(); |
if(!compat20) |
|
client_check_initial_eof_on_stdin(); |
|
|
/* Main loop of the client for the interactive session mode. */ |
/* Main loop of the client for the interactive session mode. */ |
while (!quit_pending) { |
while (!quit_pending) { |
|
|
/* Process buffered packets sent by the server. */ |
/* Process buffered packets sent by the server. */ |
client_process_buffered_input_packets(); |
client_process_buffered_input_packets(); |
|
|
|
if (compat20 && !channel_still_open()) { |
|
debug("!channel_still_open."); |
|
break; |
|
} |
|
|
/* |
/* |
* Make packets of buffered stdin data, and buffer them for |
* Make packets of buffered stdin data, and buffer them for |
* sending to the server. |
* sending to the server. |
*/ |
*/ |
client_make_packets_from_stdin_data(); |
if(!compat20) |
|
client_make_packets_from_stdin_data(); |
|
|
/* |
/* |
* Make packets from buffered channel data, and buffer them |
* Make packets from buffered channel data, and buffer them |
|
|
* Process output to stdout and stderr. Output to the |
* Process output to stdout and stderr. Output to the |
* connection is processed elsewhere (above). |
* connection is processed elsewhere (above). |
*/ |
*/ |
client_process_output(&writeset); |
if(!compat20) |
|
client_process_output(&writeset); |
|
|
/* Send as much buffered packet data as possible to the sender. */ |
/* Send as much buffered packet data as possible to the sender. */ |
if (FD_ISSET(connection_out, &writeset)) |
if (FD_ISSET(connection_out, &writeset)) |
|
|
} |
} |
|
|
void |
void |
|
client_init_dispatch_20() |
|
{ |
|
dispatch_init(&dispatch_protocol_error); |
|
dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); |
|
dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); |
|
dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); |
|
dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); |
|
dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); |
|
dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); |
|
dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &channel_input_channel_request); |
|
dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); |
|
} |
|
void |
client_init_dispatch_13() |
client_init_dispatch_13() |
{ |
{ |
dispatch_init(NULL); |
dispatch_init(NULL); |
|
|
void |
void |
client_init_dispatch() |
client_init_dispatch() |
{ |
{ |
if (compat13) |
if (compat20) |
|
client_init_dispatch_20(); |
|
else if (compat13) |
client_init_dispatch_13(); |
client_init_dispatch_13(); |
else |
else |
client_init_dispatch_15(); |
client_init_dispatch_15(); |
|
} |
|
|
|
void |
|
client_input_channel_req(int id, void *arg) |
|
{ |
|
Channel *c = NULL; |
|
unsigned int len; |
|
int success = 0; |
|
int reply; |
|
char *rtype; |
|
|
|
rtype = packet_get_string(&len); |
|
reply = packet_get_char(); |
|
|
|
log("session_input_channel_req: rtype %s reply %d", rtype, reply); |
|
|
|
c = channel_lookup(id); |
|
if (c == NULL) |
|
fatal("session_input_channel_req: channel %d: bad channel", id); |
|
|
|
if (session_ident == -1) { |
|
error("client_input_channel_req: no channel %d", id); |
|
} else if (id != session_ident) { |
|
error("client_input_channel_req: bad channel %d != %d", |
|
id, session_ident); |
|
} else if (strcmp(rtype, "exit-status") == 0) { |
|
success = 1; |
|
exit_status = packet_get_int(); |
|
} |
|
if (reply) { |
|
packet_start(success ? |
|
SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); |
|
packet_put_int(c->remote_id); |
|
packet_send(); |
|
} |
|
xfree(rtype); |
|
} |
|
|
|
void |
|
client_set_session_ident(int id) |
|
{ |
|
debug("client_set_session_ident: id %d", id); |
|
session_ident = id; |
|
channel_register_callback(id, SSH2_MSG_CHANNEL_REQUEST, |
|
client_input_channel_req, (void *)0); |
} |
} |