From ec845a812a7fd5d5320fa7cdbbadf3f2c4ebea69 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 17 May 2013 20:20:51 -0700 Subject: [PATCH] Remove core SSH client code. Refactor message handler to handle pipe for STDOUT. Refactor key and clipboard handlers to handle pipe for STDIN. --- protocols/ssh/include/ssh_client.h | 35 ++++-- protocols/ssh/src/ssh_client.c | 170 +++++------------------------ protocols/ssh/src/ssh_handlers.c | 49 ++++----- 3 files changed, 79 insertions(+), 175 deletions(-) diff --git a/protocols/ssh/include/ssh_client.h b/protocols/ssh/include/ssh_client.h index 3ccbd376..03813836 100644 --- a/protocols/ssh/include/ssh_client.h +++ b/protocols/ssh/include/ssh_client.h @@ -48,21 +48,44 @@ #include "terminal.h" #include "cursor.h" +/** + * SSH-specific client data. + */ typedef struct ssh_guac_client_data { ssh_session session; ssh_channel term_channel; + /** + * The terminal which will render all output from the SSH client. + */ guac_terminal* term; - - char * clipboard_data; - - char password[1024]; - int password_length; + + /** + * The current contents of the clipboard. + */ + char* clipboard_data; + /** + * Whether the control key is currently being held down. + */ int mod_ctrl; + + /** + * The current mouse button state. + */ int mouse_mask; + /** + * Pipe which will be used to provide STDOUT to the SSH client. + */ + int stdout_pipe_fd[2]; + + /** + * Pipe which will be used to provide STDIN to the SSH client. + */ + int stdin_pipe_fd[2]; + /** * The cached I-bar cursor. */ @@ -80,7 +103,5 @@ typedef struct ssh_guac_client_data { } ssh_guac_client_data; -int ssh_guac_client_auth(guac_client* client, const char* password); - #endif diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 0ceed6df..9d10806e 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -38,12 +38,12 @@ #include #include - -#include +#include #include #include #include +#include #include "ssh_client.h" #include "ssh_handlers.h" @@ -59,53 +59,6 @@ const char* GUAC_CLIENT_ARGS[] = { NULL }; -int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pressed) { - - ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; - - /* If key pressed */ - if (pressed) { - - /* If simple ASCII key */ - if (keysym >= 0x00 && keysym <= 0xFF) { - /* Add to password */ - client_data->password[client_data->password_length++] = keysym; - guac_terminal_write(client_data->term, "*", 1); - guac_terminal_display_flush(client_data->term->display); - guac_socket_flush(client->socket); - } - else if (keysym == 0xFF08) { - - if (client_data->password_length > 0) { - client_data->password_length--; - - /* Backspace */ - guac_terminal_write(client_data->term, "\x08\x1B[K", 4); - guac_terminal_display_flush(client_data->term->display); - guac_socket_flush(client->socket); - } - - } - else if (keysym == 0xFF0D) { - - /* Finish password */ - client_data->password[client_data->password_length] = '\0'; - - /* Clear screen */ - guac_terminal_write(client_data->term, "\x1B[2J\x1B[1;1H", 10); - guac_terminal_display_flush(client_data->term->display); - guac_socket_flush(client->socket); - - return ssh_guac_client_auth(client, client_data->password); - - } - - } - - return 0; - -} - int guac_client_init(guac_client* client, int argc, char** argv) { guac_socket* socket = client->socket; @@ -135,111 +88,44 @@ int guac_client_init(guac_client* client, int argc, char** argv) { guac_socket_flush(socket); - /* Open SSH session */ - client_data->session = ssh_new(); - if (client_data->session == NULL) { - guac_protocol_send_error(socket, "Unable to create SSH session."); - guac_socket_flush(socket); + /* Open STDOUT pipe */ + if (pipe(client_data->stdout_pipe_fd)) { + guac_error = GUAC_STATUS_SEE_ERRNO; + guac_error_message = "Unable to open pipe for STDOUT"; return 1; } - /* Set session options */ - ssh_options_set(client_data->session, SSH_OPTIONS_HOST, argv[0]); - ssh_options_set(client_data->session, SSH_OPTIONS_USER, argv[1]); + /* Redirect STDOUT to pipe */ + if (dup2(client_data->stdout_pipe_fd[1], STDOUT_FILENO) < 0) { + guac_error = GUAC_STATUS_SEE_ERRNO; + guac_error_message = "Unable redirect STDOUT"; + return 1; + } - /* Connect */ - if (ssh_connect(client_data->session) != SSH_OK) { - guac_protocol_send_error(socket, "Unable to connect via SSH."); - guac_socket_flush(socket); + /* Open STDIN pipe */ + if (pipe(client_data->stdin_pipe_fd)) { + guac_error = GUAC_STATUS_SEE_ERRNO; + guac_error_message = "Unable to open pipe for STDIN"; + return 1; + } + + /* Redirect STDIN to pipe */ + if (dup2(client_data->stdin_pipe_fd[0], STDIN_FILENO) < 0) { + guac_error = GUAC_STATUS_SEE_ERRNO; + guac_error_message = "Unable redirect STDIN"; return 1; } /* Set basic handlers */ - client->free_handler = ssh_guac_client_free_handler; - client->size_handler = ssh_guac_client_size_handler; - - /* If password provided, authenticate now */ - if (argv[2][0] != '\0') - return ssh_guac_client_auth(client, argv[2]); - - /* Otherwise, prompt for password */ - else { - - client_data->password_length = 0; - guac_terminal_write(client_data->term, "Password: ", 10); - guac_terminal_display_flush(client_data->term->display); - guac_socket_flush(client->socket); - - client->key_handler = ssh_guac_client_password_key_handler; - - } - - /* Success */ - return 0; - -} - -int ssh_guac_client_auth(guac_client* client, const char* password) { - - guac_socket* socket = client->socket; - ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; - guac_terminal* term = client_data->term; - - /* Authenticate */ - if (ssh_userauth_password(client_data->session, NULL, password) != SSH_AUTH_SUCCESS) { - guac_protocol_send_error(socket, "SSH auth failed."); - guac_socket_flush(socket); - return 1; - } - - /* Open channel for terminal */ - client_data->term_channel = channel_new(client_data->session); - if (client_data->term_channel == NULL) { - guac_protocol_send_error(socket, "Unable to open channel."); - guac_socket_flush(socket); - return 1; - } - - /* Open session for channel */ - if (channel_open_session(client_data->term_channel) != SSH_OK) { - guac_protocol_send_error(socket, "Unable to open channel session."); - guac_socket_flush(socket); - return 1; - } - - /* Request PTY */ - if (channel_request_pty(client_data->term_channel) != SSH_OK) { - guac_protocol_send_error(socket, "Unable to allocate PTY for channel."); - guac_socket_flush(socket); - return 1; - } - - /* Request PTY size */ - if (channel_change_pty_size(client_data->term_channel, term->term_width, term->term_height) != SSH_OK) { - guac_protocol_send_error(socket, "Unable to change PTY size."); - guac_socket_flush(socket); - return 1; - } - - /* Request shell */ - if (channel_request_shell(client_data->term_channel) != SSH_OK) { - guac_protocol_send_error(socket, "Unable to associate shell with PTY."); - guac_socket_flush(socket); - return 1; - } - - guac_client_log_info(client, "SSH connection successful."); - - /* Set handlers */ - client->handle_messages = ssh_guac_client_handle_messages; - client->mouse_handler = ssh_guac_client_mouse_handler; - client->key_handler = ssh_guac_client_key_handler; + client->handle_messages = ssh_guac_client_handle_messages; client->clipboard_handler = ssh_guac_client_clipboard_handler; + client->key_handler = ssh_guac_client_key_handler; + client->mouse_handler = ssh_guac_client_mouse_handler; + client->size_handler = ssh_guac_client_size_handler; + client->free_handler = ssh_guac_client_free_handler; /* Success */ return 0; } - - diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 5439af69..023e66b4 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -41,14 +41,13 @@ #include #include -#include - #include #include #include #include #include +#include #include "ssh_handlers.h" #include "ssh_client.h" @@ -61,42 +60,32 @@ int ssh_guac_client_handle_messages(guac_client* client) { ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; char buffer[8192]; - ssh_channel channels[2], out_channels[2]; + int ret_val; + int fd = client_data->stdout_pipe_fd[0]; struct timeval timeout; fd_set fds; - int ssh_fd; - - /* Channels to read */ - channels[0] = client_data->term_channel; - channels[1] = NULL; - - /* Get SSH file descriptor */ - ssh_fd = ssh_get_fd(client_data->session); /* Build fd_set */ FD_ZERO(&fds); - FD_SET(ssh_fd, &fds); + FD_SET(fd, &fds); /* Time to wait */ timeout.tv_sec = 1; timeout.tv_usec = 0; /* Wait for data to be available */ - if (ssh_select(channels, out_channels, ssh_fd+1, &fds, &timeout) - == SSH_OK) { + ret_val = select(fd+1, &fds, NULL, NULL, &timeout); + if (ret_val > 0) { int bytes_read = 0; /* Lock terminal access */ pthread_mutex_lock(&(client_data->term->lock)); - /* While data available, write to terminal */ - while (channel_is_open(client_data->term_channel) - && !channel_is_eof(client_data->term_channel) - && (bytes_read = channel_read_nonblocking(client_data->term_channel, buffer, sizeof(buffer), 0)) > 0) { + /* Read data, write to terminal */ + if ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) { - if (guac_terminal_write(client_data->term, buffer, bytes_read) - || guac_socket_flush(socket)) + if (guac_terminal_write(client_data->term, buffer, bytes_read)) return 1; } @@ -118,6 +107,11 @@ int ssh_guac_client_handle_messages(guac_client* client) { pthread_mutex_unlock(&(client_data->term->lock)); } + else if (ret_val < 0) { + guac_error_message = "Error waiting for pipe"; + guac_error = GUAC_STATUS_SEE_ERRNO; + return 1; + } return 0; @@ -164,7 +158,7 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { int length = strlen(client_data->clipboard_data); if (length) - return channel_write(client_data->term_channel, + return write(client_data->stdin_pipe_fd[1], client_data->clipboard_data, length); } @@ -234,6 +228,9 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; guac_terminal* term = client_data->term; + /* Get write end of STDIN pipe */ + int fd = client_data->stdin_pipe_fd[1]; + /* Hide mouse cursor if not already hidden */ if (client_data->current_cursor != client_data->blank_cursor) { pthread_mutex_lock(&(term->lock)); @@ -275,7 +272,7 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { else return 0; - return channel_write(client_data->term_channel, &data, 1); + return write(fd, &data, 1); } @@ -286,7 +283,7 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { char data[5]; length = guac_terminal_encode_utf8(keysym & 0xFFFF, data); - return channel_write(client_data->term_channel, data, length); + return write(fd, data, length); } @@ -309,7 +306,7 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { /* Ignore other keys */ else return 0; - return channel_write(client_data->term_channel, data, length); + return write(fd, data, length); } } @@ -335,8 +332,8 @@ int ssh_guac_client_size_handler(guac_client* client, int width, int height) { /* Resize terminal */ guac_terminal_resize(terminal, columns, rows); - channel_change_pty_size(guac_client_data->term_channel, - terminal->term_width, terminal->term_height); + + /* FIXME: Make resize call to SSH thread */ /* Reset scroll region */ terminal->scroll_end = rows - 1;