Remove core SSH client code. Refactor message handler to handle pipe for STDOUT. Refactor key and clipboard handlers to handle pipe for STDIN.
This commit is contained in:
parent
791da3dc81
commit
ec845a812a
@ -48,21 +48,44 @@
|
|||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
#include "cursor.h"
|
#include "cursor.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSH-specific client data.
|
||||||
|
*/
|
||||||
typedef struct ssh_guac_client_data {
|
typedef struct ssh_guac_client_data {
|
||||||
|
|
||||||
ssh_session session;
|
ssh_session session;
|
||||||
ssh_channel term_channel;
|
ssh_channel term_channel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The terminal which will render all output from the SSH client.
|
||||||
|
*/
|
||||||
guac_terminal* term;
|
guac_terminal* term;
|
||||||
|
|
||||||
char * clipboard_data;
|
/**
|
||||||
|
* The current contents of the clipboard.
|
||||||
char password[1024];
|
*/
|
||||||
int password_length;
|
char* clipboard_data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the control key is currently being held down.
|
||||||
|
*/
|
||||||
int mod_ctrl;
|
int mod_ctrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current mouse button state.
|
||||||
|
*/
|
||||||
int mouse_mask;
|
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.
|
* The cached I-bar cursor.
|
||||||
*/
|
*/
|
||||||
@ -80,7 +103,5 @@ typedef struct ssh_guac_client_data {
|
|||||||
|
|
||||||
} ssh_guac_client_data;
|
} ssh_guac_client_data;
|
||||||
|
|
||||||
int ssh_guac_client_auth(guac_client* client, const char* password);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -38,12 +38,12 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <libssh/libssh.h>
|
|
||||||
|
|
||||||
#include <guacamole/socket.h>
|
#include <guacamole/socket.h>
|
||||||
#include <guacamole/protocol.h>
|
#include <guacamole/protocol.h>
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
|
#include <guacamole/error.h>
|
||||||
|
|
||||||
#include "ssh_client.h"
|
#include "ssh_client.h"
|
||||||
#include "ssh_handlers.h"
|
#include "ssh_handlers.h"
|
||||||
@ -59,53 +59,6 @@ const char* GUAC_CLIENT_ARGS[] = {
|
|||||||
NULL
|
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) {
|
int guac_client_init(guac_client* client, int argc, char** argv) {
|
||||||
|
|
||||||
guac_socket* socket = client->socket;
|
guac_socket* socket = client->socket;
|
||||||
@ -135,111 +88,44 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
|
|||||||
|
|
||||||
guac_socket_flush(socket);
|
guac_socket_flush(socket);
|
||||||
|
|
||||||
/* Open SSH session */
|
/* Open STDOUT pipe */
|
||||||
client_data->session = ssh_new();
|
if (pipe(client_data->stdout_pipe_fd)) {
|
||||||
if (client_data->session == NULL) {
|
guac_error = GUAC_STATUS_SEE_ERRNO;
|
||||||
guac_protocol_send_error(socket, "Unable to create SSH session.");
|
guac_error_message = "Unable to open pipe for STDOUT";
|
||||||
guac_socket_flush(socket);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set session options */
|
/* Redirect STDOUT to pipe */
|
||||||
ssh_options_set(client_data->session, SSH_OPTIONS_HOST, argv[0]);
|
if (dup2(client_data->stdout_pipe_fd[1], STDOUT_FILENO) < 0) {
|
||||||
ssh_options_set(client_data->session, SSH_OPTIONS_USER, argv[1]);
|
guac_error = GUAC_STATUS_SEE_ERRNO;
|
||||||
|
guac_error_message = "Unable redirect STDOUT";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Connect */
|
/* Open STDIN pipe */
|
||||||
if (ssh_connect(client_data->session) != SSH_OK) {
|
if (pipe(client_data->stdin_pipe_fd)) {
|
||||||
guac_protocol_send_error(socket, "Unable to connect via SSH.");
|
guac_error = GUAC_STATUS_SEE_ERRNO;
|
||||||
guac_socket_flush(socket);
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set basic handlers */
|
/* 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->handle_messages = ssh_guac_client_handle_messages;
|
||||||
client->mouse_handler = ssh_guac_client_mouse_handler;
|
|
||||||
client->key_handler = ssh_guac_client_key_handler;
|
|
||||||
client->clipboard_handler = ssh_guac_client_clipboard_handler;
|
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 */
|
/* Success */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,14 +41,13 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <libssh/libssh.h>
|
|
||||||
|
|
||||||
#include <cairo/cairo.h>
|
#include <cairo/cairo.h>
|
||||||
#include <pango/pangocairo.h>
|
#include <pango/pangocairo.h>
|
||||||
|
|
||||||
#include <guacamole/socket.h>
|
#include <guacamole/socket.h>
|
||||||
#include <guacamole/protocol.h>
|
#include <guacamole/protocol.h>
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
|
#include <guacamole/error.h>
|
||||||
|
|
||||||
#include "ssh_handlers.h"
|
#include "ssh_handlers.h"
|
||||||
#include "ssh_client.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;
|
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
||||||
char buffer[8192];
|
char buffer[8192];
|
||||||
|
|
||||||
ssh_channel channels[2], out_channels[2];
|
int ret_val;
|
||||||
|
int fd = client_data->stdout_pipe_fd[0];
|
||||||
struct timeval timeout;
|
struct timeval timeout;
|
||||||
fd_set fds;
|
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 */
|
/* Build fd_set */
|
||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
FD_SET(ssh_fd, &fds);
|
FD_SET(fd, &fds);
|
||||||
|
|
||||||
/* Time to wait */
|
/* Time to wait */
|
||||||
timeout.tv_sec = 1;
|
timeout.tv_sec = 1;
|
||||||
timeout.tv_usec = 0;
|
timeout.tv_usec = 0;
|
||||||
|
|
||||||
/* Wait for data to be available */
|
/* Wait for data to be available */
|
||||||
if (ssh_select(channels, out_channels, ssh_fd+1, &fds, &timeout)
|
ret_val = select(fd+1, &fds, NULL, NULL, &timeout);
|
||||||
== SSH_OK) {
|
if (ret_val > 0) {
|
||||||
|
|
||||||
int bytes_read = 0;
|
int bytes_read = 0;
|
||||||
|
|
||||||
/* Lock terminal access */
|
/* Lock terminal access */
|
||||||
pthread_mutex_lock(&(client_data->term->lock));
|
pthread_mutex_lock(&(client_data->term->lock));
|
||||||
|
|
||||||
/* While data available, write to terminal */
|
/* Read data, write to terminal */
|
||||||
while (channel_is_open(client_data->term_channel)
|
if ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) {
|
||||||
&& !channel_is_eof(client_data->term_channel)
|
|
||||||
&& (bytes_read = channel_read_nonblocking(client_data->term_channel, buffer, sizeof(buffer), 0)) > 0) {
|
|
||||||
|
|
||||||
if (guac_terminal_write(client_data->term, buffer, bytes_read)
|
if (guac_terminal_write(client_data->term, buffer, bytes_read))
|
||||||
|| guac_socket_flush(socket))
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -118,6 +107,11 @@ int ssh_guac_client_handle_messages(guac_client* client) {
|
|||||||
pthread_mutex_unlock(&(client_data->term->lock));
|
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;
|
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);
|
int length = strlen(client_data->clipboard_data);
|
||||||
if (length)
|
if (length)
|
||||||
return channel_write(client_data->term_channel,
|
return write(client_data->stdin_pipe_fd[1],
|
||||||
client_data->clipboard_data, length);
|
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;
|
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
||||||
guac_terminal* term = client_data->term;
|
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 */
|
/* Hide mouse cursor if not already hidden */
|
||||||
if (client_data->current_cursor != client_data->blank_cursor) {
|
if (client_data->current_cursor != client_data->blank_cursor) {
|
||||||
pthread_mutex_lock(&(term->lock));
|
pthread_mutex_lock(&(term->lock));
|
||||||
@ -275,7 +272,7 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) {
|
|||||||
else
|
else
|
||||||
return 0;
|
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];
|
char data[5];
|
||||||
|
|
||||||
length = guac_terminal_encode_utf8(keysym & 0xFFFF, data);
|
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 */
|
/* Ignore other keys */
|
||||||
else return 0;
|
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 */
|
/* Resize terminal */
|
||||||
guac_terminal_resize(terminal, columns, rows);
|
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 */
|
/* Reset scroll region */
|
||||||
terminal->scroll_end = rows - 1;
|
terminal->scroll_end = rows - 1;
|
||||||
|
Loading…
Reference in New Issue
Block a user