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 \
|
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
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
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",
|
"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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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) {
|
||||||
|
@ -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 */
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user