GUACAMOLE-269: Merge support for overriding the character sequence sent for backspace for SSH/telnet.
This commit is contained in:
commit
70b2b8a1bf
@ -29,6 +29,7 @@ libguac_client_ssh_la_SOURCES = \
|
||||
settings.c \
|
||||
sftp.c \
|
||||
ssh.c \
|
||||
ttymode.c \
|
||||
user.c
|
||||
|
||||
noinst_HEADERS = \
|
||||
@ -38,6 +39,7 @@ noinst_HEADERS = \
|
||||
settings.h \
|
||||
sftp.h \
|
||||
ssh.h \
|
||||
ttymode.h \
|
||||
user.h
|
||||
|
||||
# Add agent sources if enabled
|
||||
|
@ -56,6 +56,7 @@ const char* GUAC_SSH_CLIENT_ARGS[] = {
|
||||
"create-recording-path",
|
||||
"read-only",
|
||||
"server-alive-interval",
|
||||
"backspace",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -209,6 +210,13 @@ enum SSH_ARGS_IDX {
|
||||
*/
|
||||
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
|
||||
};
|
||||
|
||||
@ -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,
|
||||
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 */
|
||||
return settings;
|
||||
|
||||
|
@ -223,6 +223,11 @@ typedef struct guac_ssh_settings {
|
||||
*/
|
||||
int server_alive_interval;
|
||||
|
||||
/**
|
||||
* The integer ASCII code of the command to send for backspace.
|
||||
*/
|
||||
int backspace;
|
||||
|
||||
} guac_ssh_settings;
|
||||
|
||||
/**
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "sftp.h"
|
||||
#include "ssh.h"
|
||||
#include "terminal/terminal.h"
|
||||
#include "ttymode.h"
|
||||
|
||||
#ifdef ENABLE_SSH_AGENT
|
||||
#include "ssh_agent.h"
|
||||
@ -191,6 +192,8 @@ void* ssh_client_thread(void* data) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char ssh_ttymodes[GUAC_SSH_TTYMODES_SIZE(1)];
|
||||
|
||||
/* Set up screen recording, if requested */
|
||||
if (settings->recording_path != NULL) {
|
||||
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,
|
||||
settings->font_name, settings->font_size,
|
||||
settings->resolution, settings->width, settings->height,
|
||||
settings->color_scheme);
|
||||
settings->color_scheme, settings->backspace);
|
||||
|
||||
/* Fail if terminal init failed */
|
||||
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 */
|
||||
if (libssh2_channel_request_pty_ex(ssh_client->term_channel, "linux", sizeof("linux")-1, NULL, 0,
|
||||
ssh_client->term->term_width, ssh_client->term->term_height, 0, 0)) {
|
||||
if (libssh2_channel_request_pty_ex(ssh_client->term_channel, "linux", sizeof("linux")-1,
|
||||
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.");
|
||||
return NULL;
|
||||
}
|
||||
|
61
src/protocols/ssh/ttymode.c
Normal file
61
src/protocols/ssh/ttymode.c
Normal 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;
|
||||
|
||||
}
|
89
src/protocols/ssh/ttymode.h
Normal file
89
src/protocols/ssh/ttymode.h
Normal 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
|
@ -50,6 +50,7 @@ const char* GUAC_TELNET_CLIENT_ARGS[] = {
|
||||
"recording-include-keys",
|
||||
"create-recording-path",
|
||||
"read-only",
|
||||
"backspace",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -174,6 +175,12 @@ enum TELNET_ARGS_IDX {
|
||||
*/
|
||||
IDX_READ_ONLY,
|
||||
|
||||
/**
|
||||
* ASCII code, as an integer to use for the backspace key, or 127
|
||||
* if not specified.
|
||||
*/
|
||||
IDX_BACKSPACE,
|
||||
|
||||
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,
|
||||
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 */
|
||||
return settings;
|
||||
|
||||
|
@ -207,6 +207,13 @@ typedef struct guac_telnet_settings {
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
|
@ -480,7 +480,7 @@ void* guac_telnet_client_thread(void* data) {
|
||||
telnet_client->term = guac_terminal_create(client,
|
||||
settings->font_name, settings->font_size,
|
||||
settings->resolution, settings->width, settings->height,
|
||||
settings->color_scheme);
|
||||
settings->color_scheme, settings->backspace);
|
||||
|
||||
/* Fail if terminal init failed */
|
||||
if (telnet_client->term == NULL) {
|
||||
|
@ -257,7 +257,8 @@ void* guac_terminal_thread(void* data) {
|
||||
|
||||
guac_terminal* guac_terminal_create(guac_client* client,
|
||||
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_background;
|
||||
@ -406,6 +407,9 @@ guac_terminal* guac_terminal_create(guac_client* client,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Configure backspace */
|
||||
term->backspace = backspace;
|
||||
|
||||
return term;
|
||||
|
||||
}
|
||||
@ -1594,7 +1598,11 @@ static int __guac_terminal_send_key(guac_terminal* term, int keysym, int pressed
|
||||
/* Non-printable keys */
|
||||
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 == 0xFF0D || keysym == 0xFF8D) return guac_terminal_send_string(term, "\x0D"); /* Enter */
|
||||
if (keysym == 0xFF1B) return guac_terminal_send_string(term, "\x1B"); /* Esc */
|
||||
|
@ -432,6 +432,11 @@ struct guac_terminal {
|
||||
*/
|
||||
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
|
||||
* GUAC_TERMINAL_SCHEME_GRAY_BLACK.
|
||||
*
|
||||
* @param backspace
|
||||
* The integer ASCII code to send when backspace is pressed in
|
||||
* this terminal.
|
||||
*
|
||||
* @return
|
||||
* A new guac_terminal having the given font, dimensions, and attributes
|
||||
* which renders all text to the given client.
|
||||
*/
|
||||
guac_terminal* guac_terminal_create(guac_client* client,
|
||||
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.
|
||||
|
Loading…
Reference in New Issue
Block a user