Skip to content

Commit

Permalink
GUACAMOLE-1841: Attempt to fix socket issues from rebase.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmuehlner committed Nov 1, 2024
1 parent bf74915 commit 4e1fb80
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 3 deletions.
22 changes: 22 additions & 0 deletions src/common-ssh/ssh.c
Original file line number Diff line number Diff line change
Expand Up @@ -426,8 +426,14 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
int timeout, int keepalive, const char* host_key,
guac_ssh_credential_handler* credential_handler) {

#ifdef WINDOWS_BUILD
SOCKET fd = guac_tcp_connect(hostname, port, timeout);
if (fd == INVALID_SOCKET) {
#else
int fd = guac_tcp_connect(hostname, port, timeout);
if (fd < 0) {
#endif

guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"Failed to open TCP connection to %s on %s.", hostname, port);
return NULL;
Expand Down Expand Up @@ -464,7 +470,11 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
"SSH handshake failed.");
guac_mem_free(common_session);
#ifdef WINDOWS_BUILD
closesocket(fd);
#else
close(fd);
#endif
return NULL;
}

Expand All @@ -477,7 +487,11 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"Failed to get host key for %s", hostname);
guac_mem_free(common_session);
#ifdef WINDOWS_BUILD
closesocket(fd);
#else
close(fd);
#endif
return NULL;
}

Expand All @@ -500,7 +514,11 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
"Host key did not match any provided known host keys. %s", err_msg);

guac_mem_free(common_session);
#ifdef WINDOWS_BUILD
closesocket(fd);
#else
close(fd);
#endif
return NULL;
}

Expand All @@ -514,7 +532,11 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
/* Attempt authentication */
if (guac_common_ssh_authenticate(common_session)) {
guac_mem_free(common_session);
#ifdef WINDOWS_BUILD
closesocket(fd);
#else
close(fd);
#endif
return NULL;
}

Expand Down
16 changes: 16 additions & 0 deletions src/libguac/guacamole/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,25 @@

#include <stddef.h>

#ifdef WINDOWS_BUILD
#include <winsock2.h>
#endif

/**
* Linux:
* Given a hostname or IP address and port, attempt to connect to that system,
* returning the file descriptor of an open socket if the connection succeeds,
* or a negative value if it fails. The returned file descriptor must
* eventually be freed with a call to close(). If this function fails,
* guac_error will be set appropriately.
*
* Windows:
* Given a hostname or IP address and port, attempt to connect to that system,
* returning A SOCKET representing an open socket if the connection succeeds,
* or INVALID_SOCKET if it fails. The returned socket must eventually be freed
* with a call to closesocket(). If this function fails, guac_error will be set
* appropriately.
*
* @param hostname
* The hostname or IP address to which to attempt connections.
*
Expand All @@ -50,6 +62,10 @@
* A valid socket if the connection succeeds, or a negative integer if it
* fails.
*/
#ifdef WINDOWS_BUILD
SOCKET guac_tcp_connect(const char* hostname, const char* port, const int timeout);
#else
int guac_tcp_connect(const char* hostname, const char* port, const int timeout);
#endif

#endif // GUAC_TCP_H
69 changes: 67 additions & 2 deletions src/libguac/tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>

#ifdef WINDOWS_BUILD
#include <winsock2.h>
Expand All @@ -35,7 +36,12 @@
#include <unistd.h>
#endif

#ifdef WINDOWS_BUILD
SOCKET guac_tcp_connect(const char* hostname, const char* port, const int timeout) {
SOCKET sock = INVALID_SOCKET;
#else
int guac_tcp_connect(const char* hostname, const char* port, const int timeout) {
#endif

int retval;

Expand Down Expand Up @@ -75,6 +81,47 @@ int guac_tcp_connect(const char* hostname, const char* port, const int timeout)
continue;
}

#ifdef WINDOWS_BUILD

/* Get socket or return the error. */
SOCKET sock = socket(current_address->ai_family, SOCK_STREAM, 0);
if (fd < 0) {
freeaddrinfo(addresses);
return fd;
}

/* Sockets are always created in blocking mode in WinAPI. See
https://learn.microsoft.com/en-us/windows/win32/winsock/winsock-ioctls#fionbio
*/
u_long blocking_mode = 0;
if(ioctlsocket(sock, FIONBIO, &blocking_mode) != NO_ERROR) {
guac_error = GUAC_STATUS_INVALID_ARGUMENT;
guac_error_message = "Failed to set non-blocking socket.";
close(fd);
continue;
}

/* Get socket or return the error. WSAGetLastError() is expected
to return WSAEWOULDBLOCK for non-blocking sockets. */
sock = socket(current_address->ai_family, SOCK_STREAM, 0);
if (sock == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) {
freeaddrinfo(addresses);
return fd;
}

/* Structure that stores our timeout setting. */
struct timeval tv;
tv.tv_sec = timeout;
tv.tv_usec = 0;

/* Set up timeout. */
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(sock, &fdset);

int retval = select(sock, NULL, &fdset, NULL, &tv);
#else

/* Get socket or return the error. */
fd = socket(current_address->ai_family, SOCK_STREAM, 0);
if (fd < 0) {
Expand Down Expand Up @@ -124,16 +171,30 @@ int guac_tcp_connect(const char* hostname, const char* port, const int timeout)
continue;
}
}

#endif
/* Successful connection */
if (retval > 0) {

#ifdef WINDOWS_BUILD

// Set the socket back to blocking mode
blocking_mode = 1;
if(ioctlsocket(sock, FIONBIO, &blocking_mode) != NO_ERROR) {
guac_error = GUAC_STATUS_INVALID_ARGUMENT;
guac_error_message = "Failed to reset socket options.";
close(fd);
continue;
}
#else

/* Restore previous socket options. */
if (fcntl(fd, F_SETFL, opt) < 0) {
guac_error = GUAC_STATUS_INVALID_ARGUMENT;
guac_error_message = "Failed to reset socket options.";
close(fd);
continue;
}
#endif

break;
}
Expand Down Expand Up @@ -161,7 +222,11 @@ int guac_tcp_connect(const char* hostname, const char* port, const int timeout)
guac_error_message = "Unable to connect to remote host.";
}

/* Return the fd, or the error message if the socket connection failed. */
/* Return the socket, or the error message if the socket connection failed. */
#ifdef WINDOWS_BUILD
return sock;
#else
return fd;
#endif

}
27 changes: 26 additions & 1 deletion src/libguac/wol.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,21 @@ int guac_wol_wake_and_wait(const char* mac_addr, const char* broadcast_addr,
const char* hostname, const char* port, const int timeout) {

/* Attempt to connect, first. */
#ifdef WINDOWS_BUILD

SOCKET sockfd = guac_tcp_connect(hostname, port, timeout);

/* If connection succeeds, no need to wake the system. */
if (sockfd != INVALID_SOCKET) {
closesocket(sockfd);
return 0;
}

/* Close the fd to avoid resource leak. */
closesocket(sockfd);

#else

int sockfd = guac_tcp_connect(hostname, port, timeout);

/* If connection succeeds, no need to wake the system. */
Expand All @@ -221,6 +236,8 @@ int guac_wol_wake_and_wait(const char* mac_addr, const char* broadcast_addr,
/* Close the fd to avoid resource leak. */
close(sockfd);

#endif

/* Send the magic WOL packet and store return value. */
int retval = guac_wol_wake(mac_addr, broadcast_addr, udp_port);

Expand All @@ -235,15 +252,23 @@ int guac_wol_wake_and_wait(const char* mac_addr, const char* broadcast_addr,

/* Connection succeeded - close socket and exit. */
if (sockfd > 0) {
close(sockfd);
#ifdef WINDOWS_BUILD
closesocket(sockfd);
#else
close(sockfd);
#endif
return 0;
}

/**
* Connection did not succed - close the socket and sleep for the
* specified amount of time before retrying.
*/
#ifdef WINDOWS_BUILD
closesocket(sockfd);
#else
close(sockfd);
#endif
guac_timestamp_msleep(wait_time * 1000);
}

Expand Down
4 changes: 4 additions & 0 deletions src/protocols/telnet/telnet.c
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,11 @@ 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;

#ifdef WINDOWS_BUILD
SOCKET fd = guac_tcp_connect(settings->hostname, settings->port, settings->timeout);
#else
int fd = guac_tcp_connect(settings->hostname, settings->port, settings->timeout);
#endif

/* Open telnet session */
telnet_t* telnet = telnet_init(__telnet_options, __guac_telnet_event_handler, 0, client);
Expand Down

0 comments on commit 4e1fb80

Please sign in to comment.