From 1cdda6dd2c8d2fcc48b138298b651241d06a9865 Mon Sep 17 00:00:00 2001 From: Virtually Nick Date: Thu, 16 Feb 2023 16:52:23 -0500 Subject: [PATCH 1/2] GUACAMOLE-600: Add support for setting SSH and SFTP timeouts. --- src/common-ssh/common-ssh/ssh.h | 6 +++++- src/common-ssh/ssh.c | 10 +++++++-- src/libguac/guacamole/socket-tcp.h | 5 ++++- src/libguac/guacamole/wol-constants.h | 6 ++++++ src/libguac/guacamole/wol.h | 6 +++++- src/libguac/socket-tcp.c | 30 +++++++++++++++++++++------ src/libguac/wol.c | 6 +++--- src/protocols/rdp/rdp.c | 7 ++++--- src/protocols/rdp/settings.c | 12 +++++++++++ src/protocols/rdp/settings.h | 11 ++++++++++ src/protocols/ssh/settings.c | 11 ++++++++++ src/protocols/ssh/settings.h | 12 +++++++++++ src/protocols/ssh/ssh.c | 10 +++++---- src/protocols/vnc/settings.c | 12 +++++++++++ src/protocols/vnc/settings.h | 10 +++++++++ src/protocols/vnc/vnc.c | 7 ++++--- 16 files changed, 137 insertions(+), 24 deletions(-) diff --git a/src/common-ssh/common-ssh/ssh.h b/src/common-ssh/common-ssh/ssh.h index 986c63b29..d5805b4a9 100644 --- a/src/common-ssh/common-ssh/ssh.h +++ b/src/common-ssh/common-ssh/ssh.h @@ -114,6 +114,10 @@ void guac_common_ssh_uninit(); * * @param user * The user to authenticate as, once connected. + * + * @param timeout + * The number of seconds to attempt to connect to the SSH server before + * timing out. * * @param keepalive * How frequently the connection should send keepalive packets, in @@ -138,7 +142,7 @@ void guac_common_ssh_uninit(); */ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client, const char* hostname, const char* port, guac_common_ssh_user* user, - int keepalive, const char* host_key, + int timeout, int keepalive, const char* host_key, guac_ssh_credential_handler* credential_handler); /** diff --git a/src/common-ssh/ssh.c b/src/common-ssh/ssh.c index ac7867006..edd2240cf 100644 --- a/src/common-ssh/ssh.c +++ b/src/common-ssh/ssh.c @@ -35,11 +35,17 @@ #include #include +#include +#include +#include +#include #include #include #include #include #include +#include +#include #include #ifdef LIBSSH2_USES_GCRYPT @@ -408,10 +414,10 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session) guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client, const char* hostname, const char* port, guac_common_ssh_user* user, - int keepalive, const char* host_key, + int timeout, int keepalive, const char* host_key, guac_ssh_credential_handler* credential_handler) { - int fd = guac_socket_tcp_connect(hostname, port); + int fd = guac_socket_tcp_connect(hostname, port, timeout); if (fd < 0) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Failed to open TCP connection to %s on %s.", hostname, port); diff --git a/src/libguac/guacamole/socket-tcp.h b/src/libguac/guacamole/socket-tcp.h index 48c0743ab..4d13e20bf 100644 --- a/src/libguac/guacamole/socket-tcp.h +++ b/src/libguac/guacamole/socket-tcp.h @@ -35,10 +35,13 @@ * @param port * The TCP port to which to attempt to connect. * + * @param timeout + * The number of seconds to try the TCP connection before timing out. + * * @return * A valid socket if the connection succeeds, or a negative integer if it * fails. */ -int guac_socket_tcp_connect(const char* hostname, const char* port); +int guac_socket_tcp_connect(const char* hostname, const char* port, const int timeout); #endif // __GUAC_SOCKET_TCP_H \ No newline at end of file diff --git a/src/libguac/guacamole/wol-constants.h b/src/libguac/guacamole/wol-constants.h index 8b43fd181..831a475e3 100644 --- a/src/libguac/guacamole/wol-constants.h +++ b/src/libguac/guacamole/wol-constants.h @@ -33,6 +33,12 @@ */ #define GUAC_WOL_DEFAULT_CONNECT_RETRIES 5 +/** + * The default number of seconds for the connection timeout when attempting + * to connect to the remote system to see if it is awake. + */ +#define GUAC_WOL_DEFAULT_CONNECTION_TIMEOUT 10 + /** * The value for the local IPv4 broadcast address. */ diff --git a/src/libguac/guacamole/wol.h b/src/libguac/guacamole/wol.h index f4c7d66ea..21036ed66 100644 --- a/src/libguac/guacamole/wol.h +++ b/src/libguac/guacamole/wol.h @@ -83,6 +83,10 @@ int guac_wol_wake(const char* mac_addr, const char* broadcast_addr, * @param port * The TCP port of the remote system on which the connection will be * attempted after the system has been woken. + * + * @param timeout + * The number of seconds to wait when attempting the connection to the + * remote system when checking to see if it is awake. * * @return * Zero if the packet is successfully sent to the destination; non-zero @@ -90,7 +94,7 @@ int guac_wol_wake(const char* mac_addr, const char* broadcast_addr, */ int guac_wol_wake_and_wait(const char* mac_addr, const char* broadcast_addr, const unsigned short udp_port, int wait_time, int retries, - const char* hostname, const char* port); + const char* hostname, const char* port, const int timeout); #endif /* GUAC_WOL_H */ diff --git a/src/libguac/socket-tcp.c b/src/libguac/socket-tcp.c index 7e41d0218..4464ce925 100644 --- a/src/libguac/socket-tcp.c +++ b/src/libguac/socket-tcp.c @@ -22,11 +22,13 @@ #include "guacamole/socket.h" #include +#include #include #include +#include #include -int guac_socket_tcp_connect(const char* hostname, const char* port) { +int guac_socket_tcp_connect(const char* hostname, const char* port, const int timeout) { int retval; @@ -73,15 +75,31 @@ int guac_socket_tcp_connect(const char* hostname, const char* port) { return fd; } - /* Connect */ - if (connect(fd, current_address->ai_addr, - current_address->ai_addrlen) == 0) { + /* Set socket to non-blocking */ + fcntl(fd, F_SETFL, O_NONBLOCK); - /* Done if successful connect */ - break; + /* Set up timeout. */ + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(fd, &fdset); + + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + /* Connect and wait for timeout */ + if (connect(fd, current_address->ai_addr, current_address->ai_addrlen) < 0) { + guac_error = GUAC_STATUS_REFUSED; + guac_error_message = "Unable to connect via socket."; + close(fd); + break; } + /* Check for the connection and break if successful */ + if (select(fd + 1, NULL, &fdset, NULL, &tv) > 0) + break; + + /* Connection not successful - free resources and go to the next address. */ close(fd); current_address = current_address->ai_next; diff --git a/src/libguac/wol.c b/src/libguac/wol.c index c669dc47a..5629c0bf5 100644 --- a/src/libguac/wol.c +++ b/src/libguac/wol.c @@ -201,10 +201,10 @@ int guac_wol_wake(const char* mac_addr, const char* broadcast_addr, int guac_wol_wake_and_wait(const char* mac_addr, const char* broadcast_addr, const unsigned short udp_port, int wait_time, int retries, - const char* hostname, const char* port) { + const char* hostname, const char* port, const int timeout) { /* Attempt to connect, first. */ - int sockfd = guac_socket_tcp_connect(hostname, port); + int sockfd = guac_socket_tcp_connect(hostname, port, timeout); /* If connection succeeds, no need to wake the system. */ if (sockfd > 0) { @@ -222,7 +222,7 @@ int guac_wol_wake_and_wait(const char* mac_addr, const char* broadcast_addr, /* Try to connect on the specified TCP port and hostname or IP. */ for (int i = 0; i < retries; i++) { - sockfd = guac_socket_tcp_connect(hostname, port); + sockfd = guac_socket_tcp_connect(hostname, port, timeout); /* Connection succeeded - close socket and exit. */ if (sockfd > 0) { diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c index d2e5bf960..b2c3c3a1a 100644 --- a/src/protocols/rdp/rdp.c +++ b/src/protocols/rdp/rdp.c @@ -725,7 +725,8 @@ void* guac_rdp_client_thread(void* data) { settings->wol_wait_time, GUAC_WOL_DEFAULT_CONNECT_RETRIES, settings->hostname, - (const char *) str_port)) { + (const char *) str_port, + GUAC_WOL_DEFAULT_CONNECTION_TIMEOUT)) { guac_client_log(client, GUAC_LOG_ERROR, "Failed to send WOL packet, or server failed to wake up."); guac_mem_free(str_port); return NULL; @@ -823,8 +824,8 @@ void* guac_rdp_client_thread(void* data) { /* Attempt SSH connection */ rdp_client->sftp_session = guac_common_ssh_create_session(client, settings->sftp_hostname, - settings->sftp_port, rdp_client->sftp_user, settings->sftp_server_alive_interval, - settings->sftp_host_key, NULL); + settings->sftp_port, rdp_client->sftp_user, settings->sftp_timeout, + settings->sftp_server_alive_interval, settings->sftp_host_key, NULL); /* Fail if SSH connection does not succeed */ if (rdp_client->sftp_session == NULL) { diff --git a/src/protocols/rdp/settings.c b/src/protocols/rdp/settings.c index 4b02b3eb1..6e12fb309 100644 --- a/src/protocols/rdp/settings.c +++ b/src/protocols/rdp/settings.c @@ -104,6 +104,7 @@ const char* GUAC_RDP_CLIENT_ARGS[] = { "sftp-hostname", "sftp-host-key", "sftp-port", + "sftp-timeout", "sftp-username", "sftp-password", "sftp-private-key", @@ -454,6 +455,12 @@ enum RDP_ARGS_IDX { */ IDX_SFTP_PORT, + /** + * The number of seconds to attempt to connect to the SSH server before + * timing out. + */ + IDX_SFTP_TIMEOUT, + /** * The username to provide when authenticating with the SSH server for * SFTP. If blank, the username provided for the RDP user will be used. @@ -1086,6 +1093,11 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user, guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv, IDX_SFTP_PORT, "22"); + /* SFTP timeout */ + settings->sftp_timeout = + guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv, + IDX_SFTP_TIMEOUT, RDP_DEFAULT_SFTP_TIMEOUT); + /* Username for SSH/SFTP authentication */ settings->sftp_username = guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv, diff --git a/src/protocols/rdp/settings.h b/src/protocols/rdp/settings.h index 7837e6aeb..de8395446 100644 --- a/src/protocols/rdp/settings.h +++ b/src/protocols/rdp/settings.h @@ -38,6 +38,11 @@ */ #define RDP_DEFAULT_PORT 3389 +/** + * The default SFTP connection timeout, in seconds. + */ +#define RDP_DEFAULT_SFTP_TIMEOUT 10 + /** * The default RDP port used by Hyper-V "VMConnect". */ @@ -452,6 +457,12 @@ typedef struct guac_rdp_settings { */ char* sftp_port; + /** + * The number of seconds to attempt to connect to the SSH server before + * timing out. + */ + int sftp_timeout; + /** * The username to provide when authenticating with the SSH server for * SFTP. diff --git a/src/protocols/ssh/settings.c b/src/protocols/ssh/settings.c index a297963bc..48e7383bd 100644 --- a/src/protocols/ssh/settings.c +++ b/src/protocols/ssh/settings.c @@ -38,6 +38,7 @@ const char* GUAC_SSH_CLIENT_ARGS[] = { "hostname", "host-key", "port", + "timeout", "username", "password", GUAC_SSH_ARGV_FONT_NAME, @@ -99,6 +100,11 @@ enum SSH_ARGS_IDX { */ IDX_PORT, + /** + * The timeout of the connection attempt, in seconds. Optional. + */ + IDX_TIMEOUT, + /** * The name of the user to login as. Optional. */ @@ -454,6 +460,11 @@ guac_ssh_settings* guac_ssh_parse_args(guac_user* user, guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv, IDX_PORT, GUAC_SSH_DEFAULT_PORT); + /* Parse the timeout value. */ + settings->timeout = + guac_user_parse_args_int(user, GUAC_SSH_CLIENT_ARGS, argv, + IDX_TIMEOUT, GUAC_SSH_DEFAULT_TIMEOUT); + /* Read-only mode */ settings->read_only = guac_user_parse_args_boolean(user, GUAC_SSH_CLIENT_ARGS, argv, diff --git a/src/protocols/ssh/settings.h b/src/protocols/ssh/settings.h index 0f9668327..654046183 100644 --- a/src/protocols/ssh/settings.h +++ b/src/protocols/ssh/settings.h @@ -32,6 +32,12 @@ */ #define GUAC_SSH_DEFAULT_PORT "22" +/** + * The default number of seconds to attempt a connection to the SSH/SFTP + * server before giving up. + */ +#define GUAC_SSH_DEFAULT_TIMEOUT 10 + /** * The filename to use for the typescript, if not specified. */ @@ -69,6 +75,12 @@ typedef struct guac_ssh_settings { */ char* port; + /** + * The number of seconds to attempt to connect to the SSH server before + * timing out. + */ + int timeout; + /** * The name of the user to login as, if any. If no username is specified, * this will be NULL. diff --git a/src/protocols/ssh/ssh.c b/src/protocols/ssh/ssh.c index 8db094af6..5a0a7e742 100644 --- a/src/protocols/ssh/ssh.c +++ b/src/protocols/ssh/ssh.c @@ -250,7 +250,8 @@ void* ssh_client_thread(void* data) { settings->wol_wait_time, GUAC_WOL_DEFAULT_CONNECT_RETRIES, settings->hostname, - settings->port)) { + settings->port, + settings->timeout)) { guac_client_log(client, GUAC_LOG_ERROR, "Failed to send WOL packet or connect to remote server."); return NULL; } @@ -336,7 +337,8 @@ void* ssh_client_thread(void* data) { /* Open SSH session */ ssh_client->session = guac_common_ssh_create_session(client, - settings->hostname, settings->port, ssh_client->user, settings->server_alive_interval, + settings->hostname, settings->port, ssh_client->user, + settings->timeout, settings->server_alive_interval, settings->host_key, guac_ssh_get_credential); if (ssh_client->session == NULL) { /* Already aborted within guac_common_ssh_create_session() */ @@ -387,8 +389,8 @@ void* ssh_client_thread(void* data) { guac_client_log(client, GUAC_LOG_DEBUG, "Reconnecting for SFTP..."); ssh_client->sftp_session = guac_common_ssh_create_session(client, settings->hostname, - settings->port, ssh_client->user, settings->server_alive_interval, - settings->host_key, NULL); + settings->port, ssh_client->user, settings->timeout, + settings->server_alive_interval, settings->host_key, NULL); if (ssh_client->sftp_session == NULL) { /* Already aborted within guac_common_ssh_create_session() */ return NULL; diff --git a/src/protocols/vnc/settings.c b/src/protocols/vnc/settings.c index 09ee8a7a4..a441e5d2f 100644 --- a/src/protocols/vnc/settings.c +++ b/src/protocols/vnc/settings.c @@ -67,6 +67,7 @@ const char* GUAC_VNC_CLIENT_ARGS[] = { "sftp-hostname", "sftp-host-key", "sftp-port", + "sftp-timeout", "sftp-username", "sftp-password", "sftp-private-key", @@ -233,6 +234,12 @@ enum VNC_ARGS_IDX { */ IDX_SFTP_PORT, + /** + * The number of seconds to attempt to connect to the SFTP server before + * timing out. + */ + IDX_SFTP_TIMEOUT, + /** * The username to provide when authenticating with the SSH server for * SFTP. @@ -563,6 +570,11 @@ guac_vnc_settings* guac_vnc_parse_args(guac_user* user, guac_user_parse_args_string(user, GUAC_VNC_CLIENT_ARGS, argv, IDX_SFTP_PORT, "22"); + /* SFTP connection timeout */ + settings->sftp_timeout = + guac_user_parse_args_int(user, GUAC_VNC_CLIENT_ARGS, argv, + IDX_SFTP_TIMEOUT, GUAC_VNC_DEFAULT_SFTP_TIMEOUT); + /* Username for SSH/SFTP authentication */ settings->sftp_username = guac_user_parse_args_string(user, GUAC_VNC_CLIENT_ARGS, argv, diff --git a/src/protocols/vnc/settings.h b/src/protocols/vnc/settings.h index 3b885816d..ef8550932 100644 --- a/src/protocols/vnc/settings.h +++ b/src/protocols/vnc/settings.h @@ -29,6 +29,11 @@ */ #define GUAC_VNC_DEFAULT_RECORDING_NAME "recording" +/** + * The default number of seconds to attempt to connect to the SFTP server. + */ +#define GUAC_VNC_DEFAULT_SFTP_TIMEOUT 10 + /** * VNC-specific client data. */ @@ -182,6 +187,11 @@ typedef struct guac_vnc_settings { */ char* sftp_port; + /** + * The number of seconds to attempt to connect to the SFTP server. + */ + int sftp_timeout; + /** * The username to provide when authenticating with the SSH server for * SFTP. diff --git a/src/protocols/vnc/vnc.c b/src/protocols/vnc/vnc.c index ec2c505d4..6076e5919 100644 --- a/src/protocols/vnc/vnc.c +++ b/src/protocols/vnc/vnc.c @@ -301,7 +301,8 @@ void* guac_vnc_client_thread(void* data) { settings->wol_wait_time, GUAC_WOL_DEFAULT_CONNECT_RETRIES, settings->hostname, - (const char *) str_port)) { + (const char *) str_port, + GUAC_WOL_DEFAULT_CONNECTION_TIMEOUT)) { guac_client_log(client, GUAC_LOG_ERROR, "Failed to send WOL packet or connect to remote system."); guac_mem_free(str_port); return NULL; @@ -409,8 +410,8 @@ void* guac_vnc_client_thread(void* data) { /* Attempt SSH connection */ vnc_client->sftp_session = guac_common_ssh_create_session(client, settings->sftp_hostname, - settings->sftp_port, vnc_client->sftp_user, settings->sftp_server_alive_interval, - settings->sftp_host_key, NULL); + settings->sftp_port, vnc_client->sftp_user, settings->sftp_timeout, + settings->sftp_server_alive_interval, settings->sftp_host_key, NULL); /* Fail if SSH connection does not succeed */ if (vnc_client->sftp_session == NULL) { From 7d0b76be639bec30a0d17e11f01086597f577b20 Mon Sep 17 00:00:00 2001 From: Virtually Nick Date: Fri, 24 May 2024 21:33:04 -0400 Subject: [PATCH 2/2] GUACAMOLE-600: Update telnet protocol to send timeout to common socket code. --- src/protocols/telnet/telnet.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/protocols/telnet/telnet.c b/src/protocols/telnet/telnet.c index 80ce6c878..a524e6933 100644 --- a/src/protocols/telnet/telnet.c +++ b/src/protocols/telnet/telnet.c @@ -386,7 +386,7 @@ static telnet_t* __guac_telnet_create_session(guac_client* client) { guac_telnet_client* telnet_client = (guac_telnet_client*) client->data; guac_telnet_settings* settings = telnet_client->settings; - int fd = guac_socket_tcp_connect(settings->hostname, settings->port); + int fd = guac_socket_tcp_connect(settings->hostname, settings->port, settings->timeout); /* Open telnet session */ telnet_t* telnet = telnet_init(__telnet_options, __guac_telnet_event_handler, 0, client); @@ -511,7 +511,8 @@ void* guac_telnet_client_thread(void* data) { settings->wol_wait_time, GUAC_WOL_DEFAULT_CONNECT_RETRIES, settings->hostname, - settings->port)) { + settings->port, + settings->timeout)) { guac_client_log(client, GUAC_LOG_ERROR, "Failed to send WOL packet or connect to remote server."); return NULL; }