GUACAMOLE-269: Merge support for overriding the character sequence sent for backspace for SSH/telnet.

This commit is contained in:
Michael Jumper 2018-04-02 12:19:02 -07:00
commit 70b2b8a1bf
11 changed files with 225 additions and 7 deletions

View File

@ -29,6 +29,7 @@ libguac_client_ssh_la_SOURCES = \
settings.c \ settings.c \
sftp.c \ sftp.c \
ssh.c \ ssh.c \
ttymode.c \
user.c user.c
noinst_HEADERS = \ noinst_HEADERS = \
@ -38,6 +39,7 @@ noinst_HEADERS = \
settings.h \ settings.h \
sftp.h \ sftp.h \
ssh.h \ ssh.h \
ttymode.h \
user.h user.h
# Add agent sources if enabled # Add agent sources if enabled

View File

@ -56,6 +56,7 @@ const char* GUAC_SSH_CLIENT_ARGS[] = {
"create-recording-path", "create-recording-path",
"read-only", "read-only",
"server-alive-interval", "server-alive-interval",
"backspace",
NULL NULL
}; };
@ -209,6 +210,13 @@ enum SSH_ARGS_IDX {
*/ */
IDX_SERVER_ALIVE_INTERVAL, IDX_SERVER_ALIVE_INTERVAL,
/**
* The ASCII code, as an integer, to send for the backspace key, as configured
* by the SSH connection from the client. By default this will be 127,
* the ASCII DELETE code.
*/
IDX_BACKSPACE,
SSH_ARGS_COUNT SSH_ARGS_COUNT
}; };
@ -348,6 +356,11 @@ guac_ssh_settings* guac_ssh_parse_args(guac_user* user,
guac_user_parse_args_int(user, GUAC_SSH_CLIENT_ARGS, argv, guac_user_parse_args_int(user, GUAC_SSH_CLIENT_ARGS, argv,
IDX_SERVER_ALIVE_INTERVAL, 0); IDX_SERVER_ALIVE_INTERVAL, 0);
/* Parse backspace key setting */
settings->backspace =
guac_user_parse_args_int(user, GUAC_SSH_CLIENT_ARGS, argv,
IDX_BACKSPACE, 127);
/* Parsing was successful */ /* Parsing was successful */
return settings; return settings;

View File

@ -223,6 +223,11 @@ typedef struct guac_ssh_settings {
*/ */
int server_alive_interval; int server_alive_interval;
/**
* The integer ASCII code of the command to send for backspace.
*/
int backspace;
} guac_ssh_settings; } guac_ssh_settings;
/** /**

View File

@ -26,6 +26,7 @@
#include "sftp.h" #include "sftp.h"
#include "ssh.h" #include "ssh.h"
#include "terminal/terminal.h" #include "terminal/terminal.h"
#include "ttymode.h"
#ifdef ENABLE_SSH_AGENT #ifdef ENABLE_SSH_AGENT
#include "ssh_agent.h" #include "ssh_agent.h"
@ -191,6 +192,8 @@ void* ssh_client_thread(void* data) {
return NULL; return NULL;
} }
char ssh_ttymodes[GUAC_SSH_TTYMODES_SIZE(1)];
/* Set up screen recording, if requested */ /* Set up screen recording, if requested */
if (settings->recording_path != NULL) { if (settings->recording_path != NULL) {
ssh_client->recording = guac_common_recording_create(client, ssh_client->recording = guac_common_recording_create(client,
@ -206,7 +209,7 @@ void* ssh_client_thread(void* data) {
ssh_client->term = guac_terminal_create(client, ssh_client->term = guac_terminal_create(client,
settings->font_name, settings->font_size, settings->font_name, settings->font_size,
settings->resolution, settings->width, settings->height, settings->resolution, settings->width, settings->height,
settings->color_scheme); settings->color_scheme, settings->backspace);
/* Fail if terminal init failed */ /* Fail if terminal init failed */
if (ssh_client->term == NULL) { if (ssh_client->term == NULL) {
@ -296,9 +299,17 @@ void* ssh_client_thread(void* data) {
} }
/* Set up the ttymode array prior to requesting the PTY */
int ttymodeBytes = guac_ssh_ttymodes_init(ssh_ttymodes,
GUAC_SSH_TTY_OP_VERASE, settings->backspace, GUAC_SSH_TTY_OP_END);
if (ttymodeBytes < 1)
guac_client_log(client, GUAC_LOG_WARNING, "Unable to set TTY modes."
" Backspace may not work as expected.");
/* Request PTY */ /* Request PTY */
if (libssh2_channel_request_pty_ex(ssh_client->term_channel, "linux", sizeof("linux")-1, NULL, 0, if (libssh2_channel_request_pty_ex(ssh_client->term_channel, "linux", sizeof("linux")-1,
ssh_client->term->term_width, ssh_client->term->term_height, 0, 0)) { ssh_ttymodes, ttymodeBytes, ssh_client->term->term_width,
ssh_client->term->term_height, 0, 0)) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to allocate PTY."); guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to allocate PTY.");
return NULL; return NULL;
} }

View File

@ -0,0 +1,61 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "config.h"
#include "ttymode.h"
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
int guac_ssh_ttymodes_init(char opcode_array[], ...) {
/* Initialize the variable argument list. */
va_list args;
va_start(args, opcode_array);
/* Initialize array pointer and byte counter. */
char *current = opcode_array;
/* Loop through variable argument list. */
while (true) {
/* Next argument should be an opcode. */
char opcode = (char)va_arg(args, int);
*(current++) = opcode;
/* If it's the end opcode, we're done. */
if (opcode == GUAC_SSH_TTY_OP_END)
break;
/* Next argument should be 4-byte value. */
uint32_t value = va_arg(args, uint32_t);
*(current++) = (value >> 24) & 0xFF;
*(current++) = (value >> 16) & 0xFF;
*(current++) = (value >> 8) & 0xFF;
*(current++) = value & 0xFF;
}
/* We're done processing arguments. */
va_end(args);
return current - opcode_array;
}

View File

@ -0,0 +1,89 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef GUAC_SSH_TTYMODE_H
#define GUAC_SSH_TTYMODE_H
#include "config.h"
#include <stdint.h>
/**
* The size of a TTY mode encoding opcode and
* value pair. As defined in the SSH RFC, this
* is 5 bytes - a single byte for the opcode, and
* 4 bytes for the value.
*/
#define GUAC_SSH_TTY_OPCODE_SIZE 5
/**
* The SSH TTY mode encoding opcode that terminates
* the list of TTY modes.
*/
#define GUAC_SSH_TTY_OP_END 0
/**
* The SSH TTY mode encoding opcode that configures
* the TTY erase code to configure the server
* backspace key.
*/
#define GUAC_SSH_TTY_OP_VERASE 3
/**
* Macro for calculating the number of bytes required
* to pass a given number of opcodes, which calculates
* the size of the number of opcodes plus the single byte
* end opcode.
*
* @param num_opcodes
* The number of opcodes for which a size in bytes
* should be calculated.
*
* @returns
* The number of bytes that the given number of
* opcodes will require.
*/
#define GUAC_SSH_TTYMODES_SIZE(num_opcodes) ((GUAC_SSH_TTY_OPCODE_SIZE * num_opcodes) + 1)
/**
* Opcodes and value pairs are passed to the SSH connection
* in a single array, beginning with the opcode and followed
* by a four byte value, repeating until the end opcode is
* encountered. This function takes the array that will be
* sent and a variable number of opcode and value pair
* arguments and places the opcode and values in the array
* as expected by the SSH connection.
*
* @param opcode_array
* Pointer to the opcode array that will ultimately
* be passed to the SSH connection. The array must
* be size to handle 5 bytes for each opcode and value
* pair, plus one additional byte for the end opcode.
*
* @params ...
* A variable number of opcode and value pairs
* to place in the array.
*
* @return
* Number of bytes written to the array, or zero
* if a failure occurs.
*/
int guac_ssh_ttymodes_init(char opcode_array[], ...);
#endif

View File

@ -50,6 +50,7 @@ const char* GUAC_TELNET_CLIENT_ARGS[] = {
"recording-include-keys", "recording-include-keys",
"create-recording-path", "create-recording-path",
"read-only", "read-only",
"backspace",
NULL NULL
}; };
@ -174,6 +175,12 @@ enum TELNET_ARGS_IDX {
*/ */
IDX_READ_ONLY, IDX_READ_ONLY,
/**
* ASCII code, as an integer to use for the backspace key, or 127
* if not specified.
*/
IDX_BACKSPACE,
TELNET_ARGS_COUNT TELNET_ARGS_COUNT
}; };
@ -328,6 +335,11 @@ guac_telnet_settings* guac_telnet_parse_args(guac_user* user,
guac_user_parse_args_boolean(user, GUAC_TELNET_CLIENT_ARGS, argv, guac_user_parse_args_boolean(user, GUAC_TELNET_CLIENT_ARGS, argv,
IDX_CREATE_RECORDING_PATH, false); IDX_CREATE_RECORDING_PATH, false);
/* Parse backspace key code */
settings->backspace =
guac_user_parse_args_int(user, GUAC_TELNET_CLIENT_ARGS, argv,
IDX_BACKSPACE, 127);
/* Parsing was successful */ /* Parsing was successful */
return settings; return settings;

View File

@ -207,6 +207,13 @@ typedef struct guac_telnet_settings {
*/ */
bool recording_include_keys; bool recording_include_keys;
/**
* The ASCII code, as an integer, that the telnet client will use when the
* backspace key is pressed. By default, this is 127, ASCII delete, if
* not specified in the client settings.
*/
int backspace;
} guac_telnet_settings; } guac_telnet_settings;
/** /**

View File

@ -480,7 +480,7 @@ void* guac_telnet_client_thread(void* data) {
telnet_client->term = guac_terminal_create(client, telnet_client->term = guac_terminal_create(client,
settings->font_name, settings->font_size, settings->font_name, settings->font_size,
settings->resolution, settings->width, settings->height, settings->resolution, settings->width, settings->height,
settings->color_scheme); settings->color_scheme, settings->backspace);
/* Fail if terminal init failed */ /* Fail if terminal init failed */
if (telnet_client->term == NULL) { if (telnet_client->term == NULL) {

View File

@ -257,7 +257,8 @@ void* guac_terminal_thread(void* data) {
guac_terminal* guac_terminal_create(guac_client* client, guac_terminal* guac_terminal_create(guac_client* client,
const char* font_name, int font_size, int dpi, const char* font_name, int font_size, int dpi,
int width, int height, const char* color_scheme) { int width, int height, const char* color_scheme,
const int backspace) {
int default_foreground; int default_foreground;
int default_background; int default_background;
@ -406,6 +407,9 @@ guac_terminal* guac_terminal_create(guac_client* client,
return NULL; return NULL;
} }
/* Configure backspace */
term->backspace = backspace;
return term; return term;
} }
@ -1594,7 +1598,11 @@ static int __guac_terminal_send_key(guac_terminal* term, int keysym, int pressed
/* Non-printable keys */ /* Non-printable keys */
else { else {
if (keysym == 0xFF08) return guac_terminal_send_string(term, "\x7F"); /* Backspace */ /* Backspace can vary based on configuration of terminal by client. */
if (keysym == 0xFF08) {
char backspace_str[] = { term->backspace, '\0' };
return guac_terminal_send_string(term, backspace_str);
}
if (keysym == 0xFF09 || keysym == 0xFF89) return guac_terminal_send_string(term, "\x09"); /* Tab */ if (keysym == 0xFF09 || keysym == 0xFF89) return guac_terminal_send_string(term, "\x09"); /* Tab */
if (keysym == 0xFF0D || keysym == 0xFF8D) return guac_terminal_send_string(term, "\x0D"); /* Enter */ if (keysym == 0xFF0D || keysym == 0xFF8D) return guac_terminal_send_string(term, "\x0D"); /* Enter */
if (keysym == 0xFF1B) return guac_terminal_send_string(term, "\x1B"); /* Esc */ if (keysym == 0xFF1B) return guac_terminal_send_string(term, "\x1B"); /* Esc */

View File

@ -432,6 +432,11 @@ struct guac_terminal {
*/ */
guac_common_clipboard* clipboard; guac_common_clipboard* clipboard;
/**
* ASCII character to send when backspace is pressed.
*/
char backspace;
}; };
/** /**
@ -464,13 +469,18 @@ struct guac_terminal {
* invalid, a warning will be logged, and the terminal will fall back on * invalid, a warning will be logged, and the terminal will fall back on
* GUAC_TERMINAL_SCHEME_GRAY_BLACK. * GUAC_TERMINAL_SCHEME_GRAY_BLACK.
* *
* @param backspace
* The integer ASCII code to send when backspace is pressed in
* this terminal.
*
* @return * @return
* A new guac_terminal having the given font, dimensions, and attributes * A new guac_terminal having the given font, dimensions, and attributes
* which renders all text to the given client. * which renders all text to the given client.
*/ */
guac_terminal* guac_terminal_create(guac_client* client, guac_terminal* guac_terminal_create(guac_client* client,
const char* font_name, int font_size, int dpi, const char* font_name, int font_size, int dpi,
int width, int height, const char* color_scheme); int width, int height, const char* color_scheme,
const int backspace);
/** /**
* Frees all resources associated with the given terminal. * Frees all resources associated with the given terminal.