From 23455a566b6fbfd7991cb04f7e52b75856c6ad2f Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Fri, 1 Nov 2024 23:18:48 +0000 Subject: [PATCH] GUACAMOLE-1841: Attempt to fix socket issues from rebase. --- src/common-ssh/ssh.c | 22 ++++++++++++ src/libguac/guacamole/tcp.h | 16 +++++++++ src/libguac/tcp.c | 68 +++++++++++++++++++++++++++++++++-- src/libguac/wol.c | 27 +++++++++++++- src/protocols/telnet/telnet.c | 4 +++ 5 files changed, 134 insertions(+), 3 deletions(-) diff --git a/src/common-ssh/ssh.c b/src/common-ssh/ssh.c index c609404d8..003650155 100644 --- a/src/common-ssh/ssh.c +++ b/src/common-ssh/ssh.c @@ -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; @@ -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; } @@ -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; } @@ -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; } @@ -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; } diff --git a/src/libguac/guacamole/tcp.h b/src/libguac/guacamole/tcp.h index d24e45971..ced851547 100644 --- a/src/libguac/guacamole/tcp.h +++ b/src/libguac/guacamole/tcp.h @@ -30,13 +30,25 @@ #include +#ifdef WINDOWS_BUILD +#include +#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. * @@ -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 diff --git a/src/libguac/tcp.c b/src/libguac/tcp.c index 4163121fd..c83d62853 100644 --- a/src/libguac/tcp.c +++ b/src/libguac/tcp.c @@ -23,6 +23,7 @@ #include #include +#include #ifdef WINDOWS_BUILD #include @@ -35,7 +36,11 @@ #include #endif +#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 int retval; @@ -75,6 +80,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) { @@ -124,9 +170,22 @@ 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; @@ -134,6 +193,7 @@ int guac_tcp_connect(const char* hostname, const char* port, const int timeout) close(fd); continue; } +#endif break; } @@ -161,7 +221,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 } diff --git a/src/libguac/wol.c b/src/libguac/wol.c index 85ad9cef8..323fc4d4f 100644 --- a/src/libguac/wol.c +++ b/src/libguac/wol.c @@ -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. */ @@ -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); @@ -235,7 +252,11 @@ 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; } @@ -243,7 +264,11 @@ int guac_wol_wake_and_wait(const char* mac_addr, const char* broadcast_addr, * 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); } diff --git a/src/protocols/telnet/telnet.c b/src/protocols/telnet/telnet.c index 52fc7d885..c95915c42 100644 --- a/src/protocols/telnet/telnet.c +++ b/src/protocols/telnet/telnet.c @@ -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);