Skip to content

Commit

Permalink
GUACAMOLE-600: Correct handling of non-blocking socket for timeout.
Browse files Browse the repository at this point in the history
  • Loading branch information
necouchman committed Aug 30, 2024
1 parent ae79094 commit bbb8b12
Showing 1 changed file with 57 additions and 18 deletions.
75 changes: 57 additions & 18 deletions src/libguac/tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ int guac_tcp_connect(const char* hostname, const char* port, const int timeout)
}

/* Attempt connection to each address until success */
current_address = addresses;
while (current_address != NULL) {
for (current_address = addresses; current_address != NULL; current_address = current_address->ai_next) {

/* Resolve hostname */
if ((retval = getnameinfo(current_address->ai_addr,
Expand All @@ -69,40 +68,80 @@ int guac_tcp_connect(const char* hostname, const char* port, const int timeout)
continue;
}

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

/* Set socket to non-blocking */
fcntl(fd, F_SETFL, O_NONBLOCK);
/* Variable to store current socket options. */
int opt;

/* Get current socket options */
if ((opt = fcntl(fd, F_GETFL, NULL)) < 0) {
guac_error = GUAC_STATUS_INVALID_ARGUMENT;
guac_error_message = "Failed to retrieve socket options.";
close(fd);
continue;
}

/* Set up timeout. */
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
/* Set socket to non-blocking */
if (fcntl(fd, F_SETFL, opt | O_NONBLOCK) < 0) {
guac_error = GUAC_STATUS_INVALID_ARGUMENT;
guac_error_message = "Failed to set non-blocking socket.";
close(fd);
continue;
}

/* Structure that stores our timeout setting. */
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;
if ((retval = connect(fd, current_address->ai_addr, current_address->ai_addrlen)) < 0) {
if (errno == EINPROGRESS) {
/* Set up timeout. */
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);

retval = select(fd + 1, NULL, &fdset, NULL, &tv);
}

else {
guac_error = GUAC_STATUS_REFUSED;
guac_error_message = "Unable to connect via socket.";
close(fd);
continue;
}
}

/* Check for the connection and break if successful */
if (select(fd + 1, NULL, &fdset, NULL, &tv) > 0)
/* Successful connection */
if (retval > 0) {
/* Restore previous socket options. */
if (fcntl(fd, F_SETFL, opt) < 0) {
guac_error = GUAC_STATUS_INVALID_ARGUMENT;
guac_error_message = "Failed to set non-blocking socket.";
close(fd);
continue;
}

break;
}

if (retval == 0) {
guac_error = GUAC_STATUS_REFUSED;
guac_error_message = "Timeout connecting via socket.";
}
else {
guac_error = GUAC_STATUS_INVALID_ARGUMENT;
guac_error_message = "Error attempting to connect via socket.";
}

/* Connection not successful - free resources and go to the next address. */
/* Some error has occurred - free resources before next iteration. */
close(fd);
current_address = current_address->ai_next;

}

Expand Down

0 comments on commit bbb8b12

Please sign in to comment.