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 c4af066b8..cd1c588cc 100644 --- a/src/common-ssh/ssh.c +++ b/src/common-ssh/ssh.c @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -42,6 +43,7 @@ #include #include #include +#include #include #include @@ -411,7 +413,7 @@ 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 retval; @@ -459,17 +461,43 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client, return NULL; } - /* Connect */ - if (connect(fd, current_address->ai_addr, - current_address->ai_addrlen) == 0) { + /* Set socket to non-blocking */ + fcntl(fd, F_SETFL, O_NONBLOCK); + + /* 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_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, + "Unable to connect to socket: %s", strerror(errno)); + return NULL; + } + + retval = select(fd + 1, NULL, &fdset, NULL, &tv); + + /* Timeout has occured - log the failure and move to the next address. */ + if (retval == 0) { + guac_client_log(client, GUAC_LOG_DEBUG, + "Timeout connecting to host %s, port %s", + connected_address, connected_port); + continue; + } + /* Connection is successful - log it and break the loop. */ + else if (retval > 0) { guac_client_log(client, GUAC_LOG_DEBUG, "Successfully connected to host %s, port %s", connected_address, connected_port); /* Done if successful connect */ break; - } /* Otherwise log information regarding bind failure */ diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c index dff536eff..2427a21a0 100644 --- a/src/protocols/rdp/rdp.c +++ b/src/protocols/rdp/rdp.c @@ -792,8 +792,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 96d9d15d3..90321896c 100644 --- a/src/protocols/ssh/ssh.c +++ b/src/protocols/ssh/ssh.c @@ -317,7 +317,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() */ @@ -368,8 +369,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 aa411047d..5e22114d5 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", @@ -232,6 +233,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. @@ -551,6 +558,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 7ecd3700b..c0a735555 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 85e97a13b..9f4923ac7 100644 --- a/src/protocols/vnc/vnc.c +++ b/src/protocols/vnc/vnc.c @@ -376,8 +376,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) {