libfreerdp-core: fix TCP connection timeout (non-Windows)

This commit is contained in:
Marc-André Moreau 2014-12-27 16:48:32 -05:00
parent ee5a354328
commit 772a0aaf0c
2 changed files with 90 additions and 24 deletions

View File

@ -10,11 +10,6 @@
fun:_nss_dns_gethostbyname4_r fun:_nss_dns_gethostbyname4_r
fun:gaih_inet fun:gaih_inet
fun:getaddrinfo fun:getaddrinfo
fun:freerdp_tcp_connect
fun:tcp_connect
fun:transport_connect
fun:nego_tcp_connect
fun:nego_transport_connect
} }
{ {

View File

@ -550,6 +550,76 @@ BOOL freerdp_tcp_resolve_hostname(const char* hostname)
return TRUE; return TRUE;
} }
BOOL freerdp_tcp_connect_timeout(int sockfd, struct sockaddr* addr, socklen_t addrlen, int timeout)
{
int status;
#ifndef _WIN32
int flags;
fd_set cfds;
socklen_t optlen;
struct timeval tv;
/* set socket in non-blocking mode */
flags = fcntl(sockfd, F_GETFL);
if (flags < 0)
return FALSE;
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
/* non-blocking tcp connect */
status = connect(sockfd, addr, addrlen);
if (status >= 0)
return TRUE; /* connection success */
if (errno != EINPROGRESS)
return FALSE;
FD_ZERO(&cfds);
FD_SET(sockfd, &cfds);
tv.tv_sec = timeout;
tv.tv_usec = 0;
status = _select(sockfd + 1, NULL, &cfds, NULL, &tv);
if (status != 1)
return FALSE; /* connection timeout or error */
status = 0;
optlen = sizeof(status);
if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*) &status, &optlen) < 0)
return FALSE;
if (status != 0)
return FALSE;
/* set socket in blocking mode */
flags = fcntl(sockfd, F_GETFL);
if (flags < 0)
return FALSE;
fcntl(sockfd, F_SETFL, flags & ~O_NONBLOCK);
#else
status = connect(sockfd, addr, addrlen);
if (status >= 0)
return TRUE;
return FALSE;
#endif
return TRUE;
}
BOOL freerdp_tcp_connect(rdpTcp* tcp, const char* hostname, int port, int timeout) BOOL freerdp_tcp_connect(rdpTcp* tcp, const char* hostname, int port, int timeout)
{ {
int status; int status;
@ -607,10 +677,10 @@ BOOL freerdp_tcp_connect(rdpTcp* tcp, const char* hostname, int port, int timeou
if (tcp->sockfd < 0) if (tcp->sockfd < 0)
return FALSE; return FALSE;
#else /* NO_IPV6 */ #else /* NO_IPV6 */
char port_str[16];
struct addrinfo hints; struct addrinfo hints;
struct addrinfo *result; struct addrinfo* result;
struct addrinfo *tmp; struct addrinfo* addr;
char port_str[11];
if (!settings->GatewayEnabled) if (!settings->GatewayEnabled)
{ {
@ -624,46 +694,47 @@ BOOL freerdp_tcp_connect(rdpTcp* tcp, const char* hostname, int port, int timeou
} }
ZeroMemory(&hints, sizeof(hints)); ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
/* sprintf_s(port_str, sizeof(port_str) - 1, "%u", port);
* FIXME: the following is a nasty workaround. Find a cleaner way:
* Either set port manually afterwards or get it passed as string?
*/
sprintf_s(port_str, 11, "%u", port);
status = getaddrinfo(hostname, port_str, &hints, &result); status = getaddrinfo(hostname, port_str, &hints, &result);
if (status) { if (status) {
WLog_ERR(TAG, "getaddrinfo: %s", gai_strerror(status)); WLog_ERR(TAG, "getaddrinfo: %s", gai_strerror(status));
return FALSE; return FALSE;
} }
/* For now prefer IPv4 over IPv6. */ /* Prefer IPv4 over IPv6 */
tmp = result; addr = result;
if (tmp->ai_family == AF_INET6 && tmp->ai_next != 0) if ((addr->ai_family == AF_INET6) && (addr->ai_next != 0))
{ {
while ((tmp = tmp->ai_next)) while ((addr = addr->ai_next))
{ {
if (tmp->ai_family == AF_INET) if (addr->ai_family == AF_INET)
break; break;
} }
if (!tmp) if (!addr)
tmp = result; addr = result;
} }
tcp->sockfd = socket(tmp->ai_family, tmp->ai_socktype, tmp->ai_protocol);
tcp->sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (tcp->sockfd < 0) { if (tcp->sockfd < 0) {
freeaddrinfo(result); freeaddrinfo(result);
return FALSE; return FALSE;
} }
if (connect(tcp->sockfd, tmp->ai_addr, tmp->ai_addrlen) < 0) { if (!freerdp_tcp_connect_timeout(tcp->sockfd, addr->ai_addr, addr->ai_addrlen, timeout))
WLog_ERR(TAG, "connect: %s", strerror(errno)); {
fprintf(stderr, "failed to connect to %s\n", hostname);
freeaddrinfo(result); freeaddrinfo(result);
return FALSE; return FALSE;
} }
freeaddrinfo(result); freeaddrinfo(result);
tcp->socketBio = BIO_new_socket(tcp->sockfd, BIO_NOCLOSE); tcp->socketBio = BIO_new_socket(tcp->sockfd, BIO_NOCLOSE);
/* TODO: make sure the handshake is done by querying the bio */ /* TODO: make sure the handshake is done by querying the bio */