in src/libguac/tcp.c [32:160]
int guac_tcp_connect(const char* hostname, const char* port, const int timeout) {
int retval;
int fd = EBADFD;
struct addrinfo* addresses;
struct addrinfo* current_address;
char connected_address[1024];
char connected_port[64];
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP
};
/* Get addresses for requested hostname and port. */
if ((retval = getaddrinfo(hostname, port, &hints, &addresses))) {
guac_error = GUAC_STATUS_INVALID_ARGUMENT;
guac_error_message = "Error parsing address or port.";
return retval;
}
/* Attempt connection to each address until success */
for (current_address = addresses; current_address != NULL; current_address = current_address->ai_next) {
/* Resolve hostname */
if ((retval = getnameinfo(current_address->ai_addr,
current_address->ai_addrlen,
connected_address, sizeof(connected_address),
connected_port, sizeof(connected_port),
NI_NUMERICHOST | NI_NUMERICSERV))) {
guac_error = GUAC_STATUS_INVALID_ARGUMENT;
guac_error_message = "Error resolving host.";
continue;
}
/* Get socket or return the error. */
fd = socket(current_address->ai_family, SOCK_STREAM, 0);
if (fd < 0) {
freeaddrinfo(addresses);
return fd;
}
/* 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 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 ((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;
}
}
/* 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 reset socket options.";
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.";
}
/* Some error has occurred - free resources before next iteration. */
close(fd);
}
/* Free addrinfo */
freeaddrinfo(addresses);
/* If unable to connect to anything, set error status. */
if (current_address == NULL) {
guac_error = GUAC_STATUS_REFUSED;
guac_error_message = "Unable to connect to remote host.";
}
/* Return the fd, or the error message if the socket connection failed. */
return fd;
}