GUACAMOLE-860: [WIP] More tn5250 work.
This commit is contained in:
parent
30ec3be924
commit
19899756a0
@ -39,6 +39,7 @@ DIST_SUBDIRS = \
|
||||
src/protocols/rdp \
|
||||
src/protocols/ssh \
|
||||
src/protocols/telnet \
|
||||
src/protocols/tn5250 \
|
||||
src/protocols/vnc
|
||||
|
||||
SUBDIRS = \
|
||||
@ -71,6 +72,7 @@ endif
|
||||
|
||||
if ENABLE_TELNET
|
||||
SUBDIRS += src/protocols/telnet
|
||||
SUBDIRS += src/protocols/tn5250
|
||||
endif
|
||||
|
||||
if ENABLE_VNC
|
||||
|
@ -1382,6 +1382,7 @@ AC_CONFIG_FILES([Makefile
|
||||
src/protocols/rdp/tests/Makefile
|
||||
src/protocols/ssh/Makefile
|
||||
src/protocols/telnet/Makefile
|
||||
src/protocols/tn5250/Makefile
|
||||
src/protocols/vnc/Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
@ -1447,6 +1448,7 @@ $PACKAGE_NAME version $PACKAGE_VERSION
|
||||
RDP ........... ${build_rdp}
|
||||
SSH ........... ${build_ssh}
|
||||
Telnet ........ ${build_telnet}
|
||||
TN5250 ........ ${build_telnet}
|
||||
VNC ........... ${build_vnc}
|
||||
|
||||
Services / tools:
|
||||
|
@ -72,30 +72,6 @@ int guac_tn5250_user_key_handler(guac_user* user, int keysym, int pressed) {
|
||||
if (term == NULL)
|
||||
return 0;
|
||||
|
||||
/* Stop searching for password */
|
||||
if (settings->password_regex != NULL) {
|
||||
|
||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||
"Stopping password prompt search due to user input.");
|
||||
|
||||
regfree(settings->password_regex);
|
||||
free(settings->password_regex);
|
||||
settings->password_regex = NULL;
|
||||
|
||||
}
|
||||
|
||||
/* Stop searching for username */
|
||||
if (settings->username_regex != NULL) {
|
||||
|
||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||
"Stopping username prompt search due to user input.");
|
||||
|
||||
regfree(settings->username_regex);
|
||||
free(settings->username_regex);
|
||||
settings->username_regex = NULL;
|
||||
|
||||
}
|
||||
|
||||
/* Intercept and handle Pause / Break / Ctrl+0 as "IAC BRK" */
|
||||
if (pressed && (
|
||||
keysym == 0xFF13 /* Pause */
|
||||
|
@ -34,10 +34,7 @@ const char* GUAC_TN5250_CLIENT_ARGS[] = {
|
||||
"hostname",
|
||||
"port",
|
||||
"ssl",
|
||||
"username",
|
||||
"username-regex",
|
||||
"password",
|
||||
"password-regex",
|
||||
"enhanced",
|
||||
"font-name",
|
||||
"font-size",
|
||||
"color-scheme",
|
||||
@ -54,8 +51,6 @@ const char* GUAC_TN5250_CLIENT_ARGS[] = {
|
||||
"backspace",
|
||||
"terminal-type",
|
||||
"scrollback",
|
||||
"login-success-regex",
|
||||
"login-failure-regex",
|
||||
"disable-copy",
|
||||
"disable-paste",
|
||||
NULL
|
||||
@ -77,29 +72,12 @@ enum TN5250_ARGS_IDX {
|
||||
* Whether or not to use SSL. Optional.
|
||||
*/
|
||||
IDX_SSL,
|
||||
|
||||
|
||||
/**
|
||||
* The name of the user to login as. Optional.
|
||||
* Whether or not to use RFC2877 enhanced TN5250. Optional.
|
||||
*/
|
||||
IDX_USERNAME,
|
||||
|
||||
/**
|
||||
* The regular expression to use when searching for the username/login
|
||||
* prompt. Optional.
|
||||
*/
|
||||
IDX_USERNAME_REGEX,
|
||||
|
||||
/**
|
||||
* The password to use when logging in. Optional.
|
||||
*/
|
||||
IDX_PASSWORD,
|
||||
|
||||
/**
|
||||
* The regular expression to use when searching for the password prompt.
|
||||
* Optional.
|
||||
*/
|
||||
IDX_PASSWORD_REGEX,
|
||||
|
||||
IDX_ENHANCED,
|
||||
|
||||
/**
|
||||
* The name of the font to use within the terminal.
|
||||
*/
|
||||
@ -206,24 +184,6 @@ enum TN5250_ARGS_IDX {
|
||||
*/
|
||||
IDX_SCROLLBACK,
|
||||
|
||||
/**
|
||||
* The regular expression to use when searching for whether login was
|
||||
* successful. This parameter is optional. If given, the
|
||||
* "login-failure-regex" parameter must also be specified, and the first
|
||||
* frame of the Guacamole connection will be withheld until login
|
||||
* success/failure has been determined.
|
||||
*/
|
||||
IDX_LOGIN_SUCCESS_REGEX,
|
||||
|
||||
/**
|
||||
* The regular expression to use when searching for whether login was
|
||||
* unsuccessful. This parameter is optional. If given, the
|
||||
* "login-success-regex" parameter must also be specified, and the first
|
||||
* frame of the Guacamole connection will be withheld until login
|
||||
* success/failure has been determined.
|
||||
*/
|
||||
IDX_LOGIN_FAILURE_REGEX,
|
||||
|
||||
/**
|
||||
* Whether outbound clipboard access should be blocked. If set to "true",
|
||||
* it will not be possible to copy data from the terminal to the client
|
||||
@ -241,54 +201,6 @@ enum TN5250_ARGS_IDX {
|
||||
TN5250_ARGS_COUNT
|
||||
};
|
||||
|
||||
/**
|
||||
* Compiles the given regular expression, returning NULL if compilation fails
|
||||
* or of the given regular expression is NULL. The returned regex_t must be
|
||||
* freed with regfree() AND free(), or with guac_tn5250_regex_free().
|
||||
*
|
||||
* @param user
|
||||
* The user who provided the setting associated with the given regex
|
||||
* pattern. Error messages will be logged on behalf of this user.
|
||||
*
|
||||
* @param pattern
|
||||
* The regular expression pattern to compile.
|
||||
*
|
||||
* @return
|
||||
* The compiled regular expression, or NULL if compilation fails or NULL
|
||||
* was originally provided for the pattern.
|
||||
*/
|
||||
static regex_t* guac_tn5250_compile_regex(guac_user* user, char* pattern) {
|
||||
|
||||
/* Nothing to compile if no pattern provided */
|
||||
if (pattern == NULL)
|
||||
return NULL;
|
||||
|
||||
int compile_result;
|
||||
regex_t* regex = malloc(sizeof(regex_t));
|
||||
|
||||
/* Compile regular expression */
|
||||
compile_result = regcomp(regex, pattern,
|
||||
REG_EXTENDED | REG_NOSUB | REG_ICASE | REG_NEWLINE);
|
||||
|
||||
/* Notify of failure to parse/compile */
|
||||
if (compile_result != 0) {
|
||||
guac_user_log(user, GUAC_LOG_ERROR, "Regular expression '%s' "
|
||||
"could not be compiled.", pattern);
|
||||
free(regex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return regex;
|
||||
}
|
||||
|
||||
void guac_tn5250_regex_free(regex_t** regex) {
|
||||
if (*regex != NULL) {
|
||||
regfree(*regex);
|
||||
free(*regex);
|
||||
*regex = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
guac_tn5250_settings* guac_tn5250_parse_args(guac_user* user,
|
||||
int argc, const char** argv) {
|
||||
|
||||
@ -307,59 +219,6 @@ guac_tn5250_settings* guac_tn5250_parse_args(guac_user* user,
|
||||
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||
IDX_HOSTNAME, "");
|
||||
|
||||
/* Read username */
|
||||
settings->username =
|
||||
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||
IDX_USERNAME, NULL);
|
||||
|
||||
/* Read username regex only if username is specified */
|
||||
if (settings->username != NULL) {
|
||||
settings->username_regex = guac_tn5250_compile_regex(user,
|
||||
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||
IDX_USERNAME_REGEX, GUAC_TN5250_DEFAULT_USERNAME_REGEX));
|
||||
}
|
||||
|
||||
/* Read password */
|
||||
settings->password =
|
||||
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||
IDX_PASSWORD, NULL);
|
||||
|
||||
/* Read password regex only if password is specified */
|
||||
if (settings->password != NULL) {
|
||||
settings->password_regex = guac_tn5250_compile_regex(user,
|
||||
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||
IDX_PASSWORD_REGEX, GUAC_TN5250_DEFAULT_PASSWORD_REGEX));
|
||||
}
|
||||
|
||||
/* Read optional login success detection regex */
|
||||
settings->login_success_regex = guac_tn5250_compile_regex(user,
|
||||
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||
IDX_LOGIN_SUCCESS_REGEX, NULL));
|
||||
|
||||
/* Read optional login failure detection regex */
|
||||
settings->login_failure_regex = guac_tn5250_compile_regex(user,
|
||||
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||
IDX_LOGIN_FAILURE_REGEX, NULL));
|
||||
|
||||
/* Both login success and login failure regexes must be provided if either
|
||||
* is present at all */
|
||||
if (settings->login_success_regex != NULL
|
||||
&& settings->login_failure_regex == NULL) {
|
||||
guac_tn5250_regex_free(&settings->login_success_regex);
|
||||
guac_user_log(user, GUAC_LOG_WARNING, "Ignoring provided value for "
|
||||
"\"%s\" as \"%s\" must also be provided.",
|
||||
GUAC_TN5250_CLIENT_ARGS[IDX_LOGIN_SUCCESS_REGEX],
|
||||
GUAC_TN5250_CLIENT_ARGS[IDX_LOGIN_FAILURE_REGEX]);
|
||||
}
|
||||
else if (settings->login_failure_regex != NULL
|
||||
&& settings->login_success_regex == NULL) {
|
||||
guac_tn5250_regex_free(&settings->login_failure_regex);
|
||||
guac_user_log(user, GUAC_LOG_WARNING, "Ignoring provided value for "
|
||||
"\"%s\" as \"%s\" must also be provided.",
|
||||
GUAC_TN5250_CLIENT_ARGS[IDX_LOGIN_FAILURE_REGEX],
|
||||
GUAC_TN5250_CLIENT_ARGS[IDX_LOGIN_SUCCESS_REGEX]);
|
||||
}
|
||||
|
||||
/* Read-only mode */
|
||||
settings->read_only =
|
||||
guac_user_parse_args_boolean(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||
@ -404,6 +263,11 @@ guac_tn5250_settings* guac_tn5250_parse_args(guac_user* user,
|
||||
settings->port =
|
||||
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||
IDX_PORT, defualt_port);
|
||||
|
||||
/* Enhanced mode */
|
||||
settings->enhanced =
|
||||
guac_user_parse_args_boolean(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||
IDX_ENHANCED, false);
|
||||
|
||||
/* Read typescript path */
|
||||
settings->typescript_path =
|
||||
@ -456,9 +320,37 @@ guac_tn5250_settings* guac_tn5250_parse_args(guac_user* user,
|
||||
IDX_BACKSPACE, 127);
|
||||
|
||||
/* Read terminal emulator type. */
|
||||
settings->terminal_type =
|
||||
guac_user_parse_args_string(user, GUAC_TN5250_CLIENT_ARGS, argv,
|
||||
IDX_TERMINAL_TYPE, "linux");
|
||||
char* terminal_type = guac_user_parse_args_string(user,
|
||||
GUAC_TN5250_CLIENT_ARGS, argv, IDX_TERMINAL_TYPE, "5251-11");
|
||||
|
||||
if (strcmp(terminal_type, "3179-2") == 0)
|
||||
settings->terminal_type = IBM_3179_2;
|
||||
else if (strcmp(terminal_type, "3180-2") == 0)
|
||||
settings->terminal_type = IBM_3180_2;
|
||||
else if (strcmp(terminal_type, "3196-a1") == 0)
|
||||
settings->terminal_type = IBM_3196_A1;
|
||||
else if (strcmp(terminal_type, "3477-fc") == 0)
|
||||
settings->terminal_type = IBM_3477_FC;
|
||||
else if (strcmp(terminal_type, "3477-fg") == 0)
|
||||
settings->terminal_type = IBM_3477_FG;
|
||||
else if (strcmp(terminal_type, "5251-11") == 0)
|
||||
settings->terminal_type = IBM_5251_11;
|
||||
else if (strcmp(terminal_type, "5291-1") == 0)
|
||||
settings->terminal_type = IBM_5291_1;
|
||||
else if (strcmp(terminal_type, "5292-2") == 0)
|
||||
settings->terminal_type = IBM_5292_2;
|
||||
else if (strcmp(terminal_type, "5555-b01") == 0)
|
||||
settings->terminal_type = IBM_5555_B01;
|
||||
else if (strcmp(terminal_type, "5555-c01") == 0)
|
||||
settings->terminal_type = IBM_5555_C01;
|
||||
else {
|
||||
guac_user_log(user, GUAC_LOG_WARNING,
|
||||
"Invalid terminal type %s, defaulting to 5251-11",
|
||||
terminal_type);
|
||||
settings->terminal_type = IBM_5251_11;
|
||||
}
|
||||
free(terminal_type);
|
||||
|
||||
|
||||
/* Parse clipboard copy disable flag */
|
||||
settings->disable_copy =
|
||||
@ -481,16 +373,6 @@ void guac_tn5250_settings_free(guac_tn5250_settings* settings) {
|
||||
free(settings->hostname);
|
||||
free(settings->port);
|
||||
|
||||
/* Free credentials */
|
||||
free(settings->username);
|
||||
free(settings->password);
|
||||
|
||||
/* Free various regexes */
|
||||
guac_tn5250_regex_free(&settings->username_regex);
|
||||
guac_tn5250_regex_free(&settings->password_regex);
|
||||
guac_tn5250_regex_free(&settings->login_success_regex);
|
||||
guac_tn5250_regex_free(&settings->login_failure_regex);
|
||||
|
||||
/* Free display preferences */
|
||||
free(settings->font_name);
|
||||
free(settings->color_scheme);
|
||||
|
@ -61,18 +61,6 @@
|
||||
*/
|
||||
#define GUAC_TN5250_DEFAULT_RECORDING_NAME "recording"
|
||||
|
||||
/**
|
||||
* The regular expression to use when searching for the username/login prompt
|
||||
* if no other regular expression is specified.
|
||||
*/
|
||||
#define GUAC_TN5250_DEFAULT_USERNAME_REGEX "[Ll]ogin:"
|
||||
|
||||
/**
|
||||
* The regular expression to use when searching for the password prompt if no
|
||||
* other regular expression is specified.
|
||||
*/
|
||||
#define GUAC_TN5250_DEFAULT_PASSWORD_REGEX "[Pp]assword:"
|
||||
|
||||
/**
|
||||
* The default maximum scrollback size in rows.
|
||||
*/
|
||||
@ -136,12 +124,16 @@ typedef enum guac_tn5250_terminal_types {
|
||||
|
||||
} guac_tn5250_terminal_type;
|
||||
|
||||
/**
|
||||
* Data structure to store all of the characteristics of various types of
|
||||
* 5250-compatible terminals.
|
||||
*/
|
||||
typedef struct __guac_tn5250_terminal_params {
|
||||
|
||||
/**
|
||||
* The type of terminal, as defined in RFC-1205
|
||||
*/
|
||||
guac_tn5250_terminal_type terminal;
|
||||
char* terminal;
|
||||
|
||||
/**
|
||||
* The number of rows in the terminal
|
||||
@ -160,19 +152,24 @@ typedef struct __guac_tn5250_terminal_params {
|
||||
|
||||
} __guac_tn5250_terminal_params;
|
||||
|
||||
/**
|
||||
* An array of all of the possible terminal types, including the ENUM value,
|
||||
* the height (in rows) and width (in columns), and whether or not the terminal
|
||||
* supports color (true if color, false if monochrome).
|
||||
*/
|
||||
__guac_tn5250_terminal_params __guac_tn5250_terminals[] = {
|
||||
{IBM_3179_2, 24, 80, true },
|
||||
{IBM_3180_2, 27, 132, false},
|
||||
{IBM_3196_A1, 24, 80, false},
|
||||
{IBM_3477_FC, 27, 132, true },
|
||||
{IBM_3477_FG, 27, 132, false},
|
||||
{IBM_5251_11, 24, 80, false},
|
||||
{IBM_5291_1, 24, 80, false},
|
||||
{IBM_5292_2, 24, 80, true },
|
||||
{IBM_5555_B01, 24, 80, false},
|
||||
{IBM_5555_C01, 24, 80, true },
|
||||
{"IBM_3179_2", 24, 80, true },
|
||||
{"IBM_3180_2", 27, 132, false},
|
||||
{"IBM_3196_A1", 24, 80, false},
|
||||
{"IBM_3477_FC", 27, 132, true },
|
||||
{"IBM_3477_FG", 27, 132, false},
|
||||
{"IBM_5251_11", 24, 80, false},
|
||||
{"IBM_5291_1", 24, 80, false},
|
||||
{"IBM_5292_2", 24, 80, true },
|
||||
{"IBM_5555_B01", 24, 80, false},
|
||||
{"IBM_5555_C01", 24, 80, true },
|
||||
{NULL, -1, -1, false}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Settings for the TN5250 connection. The values for this structure are parsed
|
||||
@ -195,48 +192,11 @@ typedef struct guac_tn5250_settings {
|
||||
* Whether or not to use SSL.
|
||||
*/
|
||||
bool ssl;
|
||||
|
||||
|
||||
/**
|
||||
* The name of the user to login as, if any. If no username is specified,
|
||||
* this will be NULL.
|
||||
* Whether or not to use enhanced TN5250 mode (RFC2877)
|
||||
*/
|
||||
char* username;
|
||||
|
||||
/**
|
||||
* The regular expression to use when searching for the username/login
|
||||
* prompt. If no username is specified, this will be NULL. If a username
|
||||
* is specified, this will either be the specified username regex, or the
|
||||
* default username regex.
|
||||
*/
|
||||
regex_t* username_regex;
|
||||
|
||||
/**
|
||||
* The password to give when authenticating, if any. If no password is
|
||||
* specified, this will be NULL.
|
||||
*/
|
||||
char* password;
|
||||
|
||||
/**
|
||||
* The regular expression to use when searching for the password prompt. If
|
||||
* no password is specified, this will be NULL. If a password is specified,
|
||||
* this will either be the specified password regex, or the default
|
||||
* password regex.
|
||||
*/
|
||||
regex_t* password_regex;
|
||||
|
||||
/**
|
||||
* The regular expression to use when searching for whether login was
|
||||
* successful. If no such regex is specified, or if no login failure regex
|
||||
* was specified, this will be NULL.
|
||||
*/
|
||||
regex_t* login_success_regex;
|
||||
|
||||
/**
|
||||
* The regular expression to use when searching for whether login failed.
|
||||
* If no such regex is specified, or if no login success regex was
|
||||
* specified, this will be NULL.
|
||||
*/
|
||||
regex_t* login_failure_regex;
|
||||
bool enhanced;
|
||||
|
||||
/**
|
||||
* Whether this connection is read-only, and user input should be dropped.
|
||||
|
174
src/protocols/tn5250/sna.h
Normal file
174
src/protocols/tn5250/sna.h
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
/*
|
||||
* File: sna.h
|
||||
* Author: nick_couchman
|
||||
*
|
||||
* Created on August 4, 2019, 1:43 PM
|
||||
*/
|
||||
|
||||
#ifndef GUAC_SNA_H
|
||||
#define GUAC_SNA_H
|
||||
|
||||
/**
|
||||
* This is the packet header that defines traffic on the TN5250 link as
|
||||
* SNA traffic. It will be present on traffic coming from the mainframe,
|
||||
* and should also be sent for any SNA-specific traffic to the mainframe.
|
||||
*/
|
||||
#define SNA_PACKET_HEADER 0x12a0
|
||||
|
||||
/**
|
||||
* This is a constant header on all SNA packets.
|
||||
*/
|
||||
#define SNA_VAR_HEADER 0x04
|
||||
|
||||
/**
|
||||
* Operational codes as defined in RFC1207.
|
||||
*
|
||||
* No operation.
|
||||
*/
|
||||
#define OPCODE_NOOP 0x00
|
||||
|
||||
/**
|
||||
* Invite operation, sent by server to allow client to talk.
|
||||
*/
|
||||
#define OPCODE_INVITE 0x01
|
||||
|
||||
/**
|
||||
* Output only
|
||||
*/
|
||||
#define OPCODE_OUTPUT 0x02
|
||||
|
||||
/**
|
||||
* Put/get operation
|
||||
*/
|
||||
#define OPCODE_PUT_GET 0x03
|
||||
|
||||
/**
|
||||
* Save screen
|
||||
*/
|
||||
#define OPCODE_SAVE_SCREEN 0x04
|
||||
|
||||
/**
|
||||
* Restore screen
|
||||
*/
|
||||
#define OPCODE_RESTORE_SCREEN 0x05
|
||||
|
||||
/**
|
||||
* Read immediate
|
||||
*/
|
||||
#define OPCODE_READ_IMMEIDATE 0x06
|
||||
|
||||
/**
|
||||
* Read screen
|
||||
*/
|
||||
#define OPCODE_READ_SCREEN 0x08
|
||||
|
||||
/**
|
||||
* Cancel invite
|
||||
*/
|
||||
#define OPCODE_CANCEL_INVITE 0x0a
|
||||
|
||||
/**
|
||||
* Message light on
|
||||
*/
|
||||
#define OPCODE_MSG_ON 0x0b
|
||||
|
||||
/**
|
||||
* Message light off
|
||||
*/
|
||||
#define OPCODE_MSG_OFF 0x0c
|
||||
|
||||
/**
|
||||
* A 16-bit set of flags, as defined in RFC1205, Section 3, for TN5250-specific
|
||||
* data transfer.
|
||||
*/
|
||||
struct sna_flags {
|
||||
|
||||
/**
|
||||
* Indicates data stream output error.
|
||||
*/
|
||||
unsigned int ERR : 1;
|
||||
|
||||
/**
|
||||
* 5250 Attention Key has been pressed.
|
||||
*/
|
||||
unsigned int ATN : 1;
|
||||
|
||||
/**
|
||||
* Reserved/unused.
|
||||
*/
|
||||
unsigned int : 3;
|
||||
|
||||
/**
|
||||
* System Request key was pressed.
|
||||
*/
|
||||
unsigned int SRQ : 1;
|
||||
|
||||
/**
|
||||
* Test Request key was pressed.
|
||||
*/
|
||||
unsigned int TRQ : 1;
|
||||
|
||||
/**
|
||||
* Help in Error State function - error code will follow header in data.
|
||||
*/
|
||||
unsigned int HLP : 1;
|
||||
|
||||
/**
|
||||
* Final 8 bits are reserved/unused.
|
||||
*/
|
||||
unsigned int : 8;
|
||||
|
||||
} sna_flags;
|
||||
|
||||
struct sna_packet {
|
||||
|
||||
/**
|
||||
* The length of the packet.
|
||||
*/
|
||||
unsigned int len : 16;
|
||||
|
||||
/**
|
||||
* The SNA header marker, 0x12a0
|
||||
*/
|
||||
unsigned int sna_header : 16;
|
||||
|
||||
/**
|
||||
* Reserved portion, should be zeroes.
|
||||
*/
|
||||
unsigned int reserved : 16;
|
||||
|
||||
/**
|
||||
* The variable header length, always 0x04.
|
||||
*/
|
||||
unsigned int varlen : 8;
|
||||
|
||||
/**
|
||||
* SNA flags.
|
||||
*/
|
||||
unsigned int sna_flags : 16;
|
||||
|
||||
/**
|
||||
* The opcode.
|
||||
*/
|
||||
unsigned int opcode : 8;
|
||||
|
||||
/**
|
||||
* Any data sent in the packet.
|
||||
*/
|
||||
void* sna_data;
|
||||
|
||||
/**
|
||||
* IAC and EOR marker.
|
||||
*/
|
||||
unsigned int sna_end : 16;
|
||||
|
||||
} sna_packet;
|
||||
|
||||
#endif /* SNA_H */
|
||||
|
@ -44,11 +44,13 @@
|
||||
*/
|
||||
static const telnet_telopt_t __telnet_options[] = {
|
||||
{ TELNET_TELOPT_ECHO, TELNET_WONT, TELNET_DO },
|
||||
{ TELNET_TELOPT_TTYPE, TELNET_WILL, TELNET_DONT },
|
||||
{ TELNET_TELOPT_TTYPE, TELNET_WILL, TELNET_DO },
|
||||
{ TELNET_TELOPT_COMPRESS2, TELNET_WONT, TELNET_DO },
|
||||
{ TELNET_TELOPT_MSSP, TELNET_WONT, TELNET_DO },
|
||||
{ TELNET_TELOPT_NAWS, TELNET_WILL, TELNET_DONT },
|
||||
{ TELNET_TELOPT_NAWS, TELNET_WONT, TELNET_DONT },
|
||||
{ TELNET_TELOPT_NEW_ENVIRON, TELNET_WILL, TELNET_DONT },
|
||||
{ TELNET_TELOPT_EOR, TELNET_WILL, TELNET_DO },
|
||||
{ TELNET_TELOPT_BINARY, TELNET_WILL, TELNET_DO },
|
||||
{ -1, 0, 0 }
|
||||
};
|
||||
|
||||
@ -82,181 +84,6 @@ static int __guac_telnet_write_all(int fd, const char* buffer, int size) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the given line against the given regex, returning true and sending
|
||||
* the given value if a match is found. An enter keypress is automatically
|
||||
* sent after the value is sent.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the tn5250 session.
|
||||
*
|
||||
* @param regex
|
||||
* The regex to search for within the given line buffer.
|
||||
*
|
||||
* @param value
|
||||
* The string value to send through STDIN of the tn5250 session if a
|
||||
* match is found, or NULL if no value should be sent.
|
||||
*
|
||||
* @param line_buffer
|
||||
* The line of character data to test.
|
||||
*
|
||||
* @return
|
||||
* true if a match is found, false otherwise.
|
||||
*/
|
||||
static bool guac_tn5250_regex_exec(guac_client* client, regex_t* regex,
|
||||
const char* value, const char* line_buffer) {
|
||||
|
||||
guac_tn5250_client* tn5250_client = (guac_tn5250_client*) client->data;
|
||||
|
||||
/* Send value upon match */
|
||||
if (regexec(regex, line_buffer, 0, NULL, 0) == 0) {
|
||||
|
||||
/* Send value */
|
||||
if (value != NULL) {
|
||||
guac_terminal_send_string(tn5250_client->term, value);
|
||||
guac_terminal_send_string(tn5250_client->term, "\x0D");
|
||||
}
|
||||
|
||||
/* Stop searching for prompt */
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the given line against the various stored regexes, automatically
|
||||
* sending the configured username, password, or reporting login
|
||||
* success/failure depending on context. If no search is in progress, either
|
||||
* because no regexes have been defined or because all applicable searches have
|
||||
* completed, this function has no effect.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the tn5250 session.
|
||||
*
|
||||
* @param line_buffer
|
||||
* The line of character data to test.
|
||||
*/
|
||||
static void guac_tn5250_search_line(guac_client* client, const char* line_buffer) {
|
||||
|
||||
guac_tn5250_client* tn5250_client = (guac_tn5250_client*) client->data;
|
||||
guac_tn5250_settings* settings = tn5250_client->settings;
|
||||
|
||||
/* Continue search for username prompt */
|
||||
if (settings->username_regex != NULL) {
|
||||
if (guac_tn5250_regex_exec(client, settings->username_regex,
|
||||
settings->username, line_buffer)) {
|
||||
guac_client_log(client, GUAC_LOG_DEBUG, "Username sent");
|
||||
guac_tn5250_regex_free(&settings->username_regex);
|
||||
}
|
||||
}
|
||||
|
||||
/* Continue search for password prompt */
|
||||
if (settings->password_regex != NULL) {
|
||||
if (guac_tn5250_regex_exec(client, settings->password_regex,
|
||||
settings->password, line_buffer)) {
|
||||
|
||||
guac_client_log(client, GUAC_LOG_DEBUG, "Password sent");
|
||||
|
||||
/* Do not continue searching for username/password once password is sent */
|
||||
guac_tn5250_regex_free(&settings->username_regex);
|
||||
guac_tn5250_regex_free(&settings->password_regex);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Continue search for login success */
|
||||
if (settings->login_success_regex != NULL) {
|
||||
if (guac_tn5250_regex_exec(client, settings->login_success_regex,
|
||||
NULL, line_buffer)) {
|
||||
|
||||
/* Allow terminal to render now that login has been deemed successful */
|
||||
guac_client_log(client, GUAC_LOG_DEBUG, "Login successful");
|
||||
guac_terminal_start(tn5250_client->term);
|
||||
|
||||
/* Stop all searches */
|
||||
guac_tn5250_regex_free(&settings->username_regex);
|
||||
guac_tn5250_regex_free(&settings->password_regex);
|
||||
guac_tn5250_regex_free(&settings->login_success_regex);
|
||||
guac_tn5250_regex_free(&settings->login_failure_regex);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Continue search for login failure */
|
||||
if (settings->login_failure_regex != NULL) {
|
||||
if (guac_tn5250_regex_exec(client, settings->login_failure_regex,
|
||||
NULL, line_buffer)) {
|
||||
|
||||
/* Advise that login has failed and connection should be closed */
|
||||
guac_client_abort(client,
|
||||
GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
|
||||
"Login failed");
|
||||
|
||||
/* Stop all searches */
|
||||
guac_tn5250_regex_free(&settings->username_regex);
|
||||
guac_tn5250_regex_free(&settings->password_regex);
|
||||
guac_tn5250_regex_free(&settings->login_success_regex);
|
||||
guac_tn5250_regex_free(&settings->login_failure_regex);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a line matching the various stored regexes, automatically
|
||||
* sending the configured username, password, or reporting login
|
||||
* success/failure depending on context. If no search is in progress, either
|
||||
* because no regexes have been defined or because all applicable searches
|
||||
* have completed, this function has no effect.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the tn5250 session.
|
||||
*
|
||||
* @param buffer
|
||||
* The buffer of received data to search through.
|
||||
*
|
||||
* @param size
|
||||
* The size of the given buffer, in bytes.
|
||||
*/
|
||||
static void guac_tn5250_search(guac_client* client, const char* buffer, int size) {
|
||||
|
||||
static char line_buffer[1024] = {0};
|
||||
static int length = 0;
|
||||
|
||||
/* Append all characters in buffer to current line */
|
||||
const char* current = buffer;
|
||||
for (int i = 0; i < size; i++) {
|
||||
|
||||
char c = *(current++);
|
||||
|
||||
/* Attempt pattern match and clear buffer upon reading newline */
|
||||
if (c == '\n') {
|
||||
if (length > 0) {
|
||||
line_buffer[length] = '\0';
|
||||
guac_tn5250_search_line(client, line_buffer);
|
||||
length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Append all non-newline characters to line buffer as long as space
|
||||
* remains */
|
||||
else if (length < sizeof(line_buffer) - 1)
|
||||
line_buffer[length++] = c;
|
||||
|
||||
}
|
||||
|
||||
/* Attempt pattern match if an unfinished line remains (may be a prompt) */
|
||||
if (length > 0) {
|
||||
line_buffer[length] = '\0';
|
||||
guac_tn5250_search_line(client, line_buffer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler, as defined by libtelnet. This function is passed to
|
||||
* telnet_init() and will be called for every event fired by libtelnet,
|
||||
@ -272,46 +99,52 @@ static void __guac_tn5250_event_handler(telnet_t* telnet, telnet_event_t* event,
|
||||
|
||||
/* Terminal output received */
|
||||
case TELNET_EV_DATA:
|
||||
guac_terminal_write(tn5250_client->term, event->data.buffer, event->data.size);
|
||||
guac_tn5250_search(client, event->data.buffer, event->data.size);
|
||||
__guac_tn5250_recv_sna_packet(client, event);
|
||||
// guac_terminal_write(tn5250_client->term, event->data.buffer, event->data.size);
|
||||
break;
|
||||
|
||||
/* Data destined for remote end */
|
||||
case TELNET_EV_SEND:
|
||||
if (__guac_tn5250_write_all(tn5250_client->socket_fd, event->data.buffer, event->data.size)
|
||||
!= event->data.size)
|
||||
if (__guac_tn5250_send_sna_packet(client, event))
|
||||
guac_client_stop(client);
|
||||
break;
|
||||
|
||||
/* Remote feature enabled */
|
||||
case TELNET_EV_WILL:
|
||||
guac_client_log(client, GUAC_LOG_DEBUG, "Received TELNET_EV_WILL "
|
||||
"for option %d", event->neg.telopt);
|
||||
if (event->neg.telopt == TELNET_TELOPT_ECHO)
|
||||
tn5250_client->echo_enabled = 0; /* Disable local echo, as remote will echo */
|
||||
break;
|
||||
|
||||
/* Remote feature disabled */
|
||||
case TELNET_EV_WONT:
|
||||
guac_client_log(client, GUAC_LOG_DEBUG, "Received TELNET_EV_WONT "
|
||||
"for option %d", event->neg.telopt);
|
||||
if (event->neg.telopt == TELNET_TELOPT_ECHO)
|
||||
tn5250_client->echo_enabled = 1; /* Enable local echo, as remote won't echo */
|
||||
break;
|
||||
|
||||
/* Local feature enable */
|
||||
case TELNET_EV_DO:
|
||||
if (event->neg.telopt == TELNET_TELOPT_NAWS) {
|
||||
tn5250_client->naws_enabled = 1;
|
||||
guac_tn5250_send_naws(telnet, tn5250_client->term->term_width, tn5250_client->term->term_height);
|
||||
}
|
||||
guac_client_log(client, GUAC_LOG_DEBUG, "Received TELNET_EV_DO "
|
||||
"for option %d", event->neg.telopt);
|
||||
break;
|
||||
|
||||
/* Terminal type request */
|
||||
case TELNET_EV_TTYPE:
|
||||
guac_client_log(client, GUAC_LOG_DEBUG, "Received TELNET_EV_TTYPE "
|
||||
"for command %d", event->ttype.cmd);
|
||||
if (event->ttype.cmd == TELNET_TTYPE_SEND)
|
||||
tn5250_ttype_is(tn5250_client->telnet, settings->terminal_type);
|
||||
tn5250_ttype_is(tn5250_client->telnet, __guac_tn5250_terminals[settings->terminal_type].terminal);
|
||||
break;
|
||||
|
||||
/* Environment request */
|
||||
case TELNET_EV_ENVIRON:
|
||||
|
||||
guac_client_log(client, GUAC_LOG_DEBUG, "Received TELNET_EV_ENVIRON "
|
||||
"for size %d", event->environ.size);
|
||||
|
||||
/* Only send USER if entire environment was requested */
|
||||
if (event->environ.size == 0)
|
||||
guac_tn5250_send_user(telnet, settings->username);
|
||||
@ -485,6 +318,7 @@ static void __guac_tn5250_send_uint16(telnet_t* telnet, uint16_t value) {
|
||||
* Sends an 8-bit value over the given telnet connection.
|
||||
*
|
||||
* @param telnet The telnet connection to use.
|
||||
*
|
||||
* @param value The value to send.
|
||||
*/
|
||||
static void __guac_tn5250_send_uint8(telnet_t* telnet, uint8_t value) {
|
||||
@ -528,6 +362,7 @@ void guac_tn5250_send_user(telnet_t* telnet, const char* username) {
|
||||
* error, and > 0 on success.
|
||||
*
|
||||
* @param socket_fd The file descriptor to wait for.
|
||||
*
|
||||
* @return A value greater than zero on success, zero on timeout, and
|
||||
* less than zero on error.
|
||||
*/
|
||||
@ -579,6 +414,10 @@ void* guac_tn5250_client_thread(void* data) {
|
||||
"Terminal initialization failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Fix terminal width/height */
|
||||
tn5250_client->term.term_width = __guac_tn5250_terminals[settings->terminal_type].cols;
|
||||
tn5250_client->term.term_height = __guac_tn5250_terminals[settings->terminal_type].rows;
|
||||
|
||||
/* Set up typescript, if requested */
|
||||
if (settings->typescript_path != NULL) {
|
||||
@ -598,11 +437,8 @@ void* guac_tn5250_client_thread(void* data) {
|
||||
/* Logged in */
|
||||
guac_client_log(client, GUAC_LOG_INFO, "TN5250 connection successful.");
|
||||
|
||||
/* Allow terminal to render if login success/failure detection is not
|
||||
* enabled */
|
||||
if (settings->login_success_regex == NULL
|
||||
&& settings->login_failure_regex == NULL)
|
||||
guac_terminal_start(tn5250_client->term);
|
||||
/* Allow terminal to render */
|
||||
guac_terminal_start(tn5250_client->term);
|
||||
|
||||
/* Start input thread */
|
||||
if (pthread_create(&(input_thread), NULL, __guac_tn5250_input_thread, (void*) client)) {
|
||||
@ -634,3 +470,13 @@ void* guac_tn5250_client_thread(void* data) {
|
||||
|
||||
}
|
||||
|
||||
void __guac_tn5250_send_sna_packet(void* data, tn5250_flags flags,
|
||||
unsigned char opcode, char* data) {
|
||||
|
||||
}
|
||||
|
||||
void __guac_tn5250_recv_sna_packet(guac_client* client, telnet_event_t* event) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "common/clipboard.h"
|
||||
#include "common/recording.h"
|
||||
#include "settings.h"
|
||||
#include "sna.h"
|
||||
#include "terminal/terminal.h"
|
||||
|
||||
#include <libtelnet.h>
|
||||
@ -85,22 +86,73 @@ typedef struct guac_tn5250_client {
|
||||
|
||||
} guac_tn5250_client;
|
||||
|
||||
unsigned char tn5250_opcodes[] = {
|
||||
0x00, /* No operation */
|
||||
0x01, /* Invite */
|
||||
0x02, /* Output only */
|
||||
0x03, /* Put/Get */
|
||||
0x04, /* Save screen */
|
||||
0x05, /* Restore screen */
|
||||
0x06, /* Read immediate */
|
||||
0x07, /* Reserved */
|
||||
0x08, /* Read screen */
|
||||
0x09, /* Reserved */
|
||||
0x0a, /* Cancel invite */
|
||||
0x0b, /* Turn on message light */
|
||||
0x0c, /* Turn off message light */
|
||||
NULL
|
||||
}
|
||||
|
||||
/**
|
||||
* Main tn5250 client thread, handling transfer of tn5250 output to STDOUT.
|
||||
*
|
||||
* @param data
|
||||
* The client data associated with this thread.
|
||||
*/
|
||||
void* guac_tn5250_client_thread(void* data);
|
||||
|
||||
/**
|
||||
* Send a telnet NAWS message indicating the given terminal window dimensions
|
||||
* in characters.
|
||||
*/
|
||||
void guac_tn5250_send_naws(telnet_t* telnet, uint16_t width, uint16_t height);
|
||||
|
||||
/**
|
||||
* Sends the given username by setting the remote USER environment variable
|
||||
* using the tn5250 NEW-ENVIRON option.
|
||||
*
|
||||
* @param telnet
|
||||
* The telnet connection to send the USER variable to.
|
||||
*
|
||||
* @param username
|
||||
* The username to send.
|
||||
*/
|
||||
void guac_tn5250_send_user(telnet_t* telnet, const char* username);
|
||||
|
||||
/**
|
||||
* Sends the given data in TN5250 mode, creating the necessary packet
|
||||
* structure over the telnet connection to talk to the system.
|
||||
*
|
||||
* @param telnet
|
||||
* The telnet client sending the packet.
|
||||
*
|
||||
* @param sna_flags
|
||||
* Any flags that should be set in the packet.
|
||||
*
|
||||
* @param opcode
|
||||
* Any opcode that should be sent to the mainframe.
|
||||
*
|
||||
* @param data
|
||||
* Data that should be sent to the mainframe.
|
||||
*/
|
||||
void __guac_tn5250_send_sna_packet(telnet_t* telnet, sna_flags flags,
|
||||
unsigned char opcode, char* data);
|
||||
|
||||
/**
|
||||
* Handles a received SNA packet, processing flags and opcodes, and then writing
|
||||
* any output to the terminal.
|
||||
*
|
||||
* @param client
|
||||
* The Guacamole Client that is receiving the packet.
|
||||
*
|
||||
* @param event
|
||||
* The Telnet event that is being processed.
|
||||
*/
|
||||
void __guac_tn5250_recv_sna_packet(guac_client* client, telnet_event_t* event);
|
||||
|
||||
#endif
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user