Files

1614 lines
36 KiB
C
Raw Permalink Normal View History

/**
2012-10-08 23:02:04 -04:00
* FreeRDP: A Remote Desktop Protocol Implementation
* Transmission Control Protocol (TCP)
*
* Copyright 2011 Vic Lee
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
2022-02-16 11:20:38 +01:00
#include <freerdp/config.h>
#include "settings.h"
#include <time.h>
#include <errno.h>
#include <fcntl.h>
2011-08-16 15:35:29 -04:00
#include <winpr/crt.h>
#include <winpr/platform.h>
#include <winpr/winsock.h>
#include "rdp.h"
#include "utils.h"
2014-11-12 14:06:34 -05:00
#if !defined(_WIN32)
2011-08-16 15:35:29 -04:00
#include <netdb.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
2011-09-12 03:45:38 -07:00
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <net/if.h>
2014-10-17 12:08:39 +02:00
#include <sys/types.h>
#include <arpa/inet.h>
#ifdef WINPR_HAVE_POLL_H
#include <poll.h>
#else
#include <time.h>
#include <sys/select.h>
#endif
#if defined(__FreeBSD__) || defined(__OpenBSD__)
2014-05-01 15:09:35 +02:00
#ifndef SOL_TCP
2019-11-06 15:24:51 +01:00
#define SOL_TCP IPPROTO_TCP
2014-05-01 15:09:35 +02:00
#endif
#endif
#ifdef __APPLE__
#ifndef SOL_TCP
2019-11-06 15:24:51 +01:00
#define SOL_TCP IPPROTO_TCP
#endif
#ifndef TCP_KEEPIDLE
#define TCP_KEEPIDLE TCP_KEEPALIVE
#endif
#endif
2014-11-12 14:06:34 -05:00
#else
#include <winpr/windows.h>
#include <winpr/crt.h>
#define SHUT_RDWR SD_BOTH
#define close(_fd) closesocket(_fd)
#endif
#include <freerdp/log.h>
2014-11-12 14:06:34 -05:00
#include <winpr/stream.h>
#include "tcp.h"
2016-11-21 17:28:54 +01:00
#include "../crypto/opensslcompat.h"
#if defined(HAVE_AF_VSOCK_H)
#include <ctype.h>
#include <linux/vm_sockets.h>
#endif
#define TAG FREERDP_TAG("core")
/* Simple Socket BIO */
typedef struct
{
SOCKET socket;
HANDLE hEvent;
} WINPR_BIO_SIMPLE_SOCKET;
static int transport_bio_simple_init(BIO* bio, SOCKET socket, int shutdown);
static int transport_bio_simple_uninit(BIO* bio);
static int transport_bio_simple_write(BIO* bio, const char* buf, int size)
{
int error = 0;
int status = 0;
2019-11-06 15:24:51 +01:00
WINPR_BIO_SIMPLE_SOCKET* ptr = (WINPR_BIO_SIMPLE_SOCKET*)BIO_get_data(bio);
if (!buf)
return 0;
BIO_clear_flags(bio, BIO_FLAGS_WRITE);
status = _send(ptr->socket, buf, size, 0);
if (status <= 0)
{
error = WSAGetLastError();
2019-11-06 15:24:51 +01:00
if ((error == WSAEWOULDBLOCK) || (error == WSAEINTR) || (error == WSAEINPROGRESS) ||
(error == WSAEALREADY))
{
BIO_set_flags(bio, (BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY));
}
else
{
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
}
}
return status;
}
static int transport_bio_simple_read(BIO* bio, char* buf, int size)
{
int error = 0;
int status = 0;
2019-11-06 15:24:51 +01:00
WINPR_BIO_SIMPLE_SOCKET* ptr = (WINPR_BIO_SIMPLE_SOCKET*)BIO_get_data(bio);
if (!buf)
return 0;
BIO_clear_flags(bio, BIO_FLAGS_READ);
2024-09-16 04:53:18 +02:00
(void)WSAResetEvent(ptr->hEvent);
status = _recv(ptr->socket, buf, size, 0);
if (status > 0)
{
return status;
}
if (status == 0)
{
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
return 0;
}
error = WSAGetLastError();
2019-11-06 15:24:51 +01:00
if ((error == WSAEWOULDBLOCK) || (error == WSAEINTR) || (error == WSAEINPROGRESS) ||
(error == WSAEALREADY))
{
BIO_set_flags(bio, (BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY));
}
else
{
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
}
return -1;
}
static int transport_bio_simple_puts(WINPR_ATTR_UNUSED BIO* bio, WINPR_ATTR_UNUSED const char* str)
{
return -2;
}
static int transport_bio_simple_gets(WINPR_ATTR_UNUSED BIO* bio, WINPR_ATTR_UNUSED char* str,
WINPR_ATTR_UNUSED int size)
{
return 1;
}
static long transport_bio_simple_ctrl(BIO* bio, int cmd, long arg1, void* arg2)
{
int status = -1;
2019-11-06 15:24:51 +01:00
WINPR_BIO_SIMPLE_SOCKET* ptr = (WINPR_BIO_SIMPLE_SOCKET*)BIO_get_data(bio);
2022-04-25 09:47:14 +02:00
switch (cmd)
{
2022-04-25 09:47:14 +02:00
case BIO_C_SET_SOCKET:
transport_bio_simple_uninit(bio);
transport_bio_simple_init(bio, (SOCKET)arg2, (int)arg1);
return 1;
case BIO_C_GET_SOCKET:
if (!BIO_get_init(bio) || !arg2)
return 0;
*((SOCKET*)arg2) = ptr->socket;
return 1;
case BIO_C_GET_EVENT:
if (!BIO_get_init(bio) || !arg2)
return 0;
*((HANDLE*)arg2) = ptr->hEvent;
return 1;
case BIO_C_SET_NONBLOCK:
{
#ifndef _WIN32
int flags = 0;
2022-06-23 07:57:38 +02:00
flags = fcntl((int)ptr->socket, F_GETFL);
2022-06-23 07:57:38 +02:00
if (flags == -1)
return 0;
2022-06-23 07:57:38 +02:00
if (arg1)
(void)fcntl((int)ptr->socket, F_SETFL, flags | O_NONBLOCK);
2022-06-23 07:57:38 +02:00
else
(void)fcntl((int)ptr->socket, F_SETFL, flags & ~(O_NONBLOCK));
2016-12-19 13:49:40 +01:00
#else
2022-06-23 07:57:38 +02:00
/* the internal socket is always non-blocking */
#endif
2022-06-23 07:57:38 +02:00
return 1;
2022-04-25 09:47:14 +02:00
}
case BIO_C_WAIT_READ:
{
int timeout = (int)arg1;
int sockfd = (int)ptr->socket;
#ifdef WINPR_HAVE_POLL_H
2022-06-23 07:57:38 +02:00
struct pollfd pollset;
pollset.fd = sockfd;
pollset.events = POLLIN;
pollset.revents = 0;
2022-06-23 07:57:38 +02:00
do
{
status = poll(&pollset, 1, timeout);
} while ((status < 0) && (errno == EINTR));
2016-12-19 13:49:40 +01:00
#else
fd_set rset = WINPR_C_ARRAY_INIT;
struct timeval tv = WINPR_C_ARRAY_INIT;
2022-06-23 07:57:38 +02:00
FD_ZERO(&rset);
FD_SET(sockfd, &rset);
2022-06-23 07:57:38 +02:00
if (timeout)
{
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
}
2022-06-23 07:57:38 +02:00
do
{
status = select(sockfd + 1, &rset, nullptr, nullptr, timeout ? &tv : nullptr);
2022-06-23 07:57:38 +02:00
} while ((status < 0) && (errno == EINTR));
2016-12-19 13:49:40 +01:00
#endif
/* Convert timeout to error return */
if (status == 0)
errno = ETIMEDOUT;
2022-04-25 09:47:14 +02:00
}
break;
2022-04-25 09:47:14 +02:00
case BIO_C_WAIT_WRITE:
{
int timeout = (int)arg1;
int sockfd = (int)ptr->socket;
#ifdef WINPR_HAVE_POLL_H
2022-06-23 07:57:38 +02:00
struct pollfd pollset;
pollset.fd = sockfd;
pollset.events = POLLOUT;
pollset.revents = 0;
2022-06-23 07:57:38 +02:00
do
{
status = poll(&pollset, 1, timeout);
} while ((status < 0) && (errno == EINTR));
2016-12-19 13:49:40 +01:00
#else
fd_set rset = WINPR_C_ARRAY_INIT;
struct timeval tv = WINPR_C_ARRAY_INIT;
2022-06-23 07:57:38 +02:00
FD_ZERO(&rset);
FD_SET(sockfd, &rset);
2022-06-23 07:57:38 +02:00
if (timeout)
{
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
}
2022-06-23 07:57:38 +02:00
do
{
status = select(sockfd + 1, nullptr, &rset, nullptr, timeout ? &tv : nullptr);
2022-06-23 07:57:38 +02:00
} while ((status < 0) && (errno == EINTR));
2016-12-19 13:49:40 +01:00
#endif
/* Convert timeout to error return */
if (status == 0)
errno = ETIMEDOUT;
2022-04-25 09:47:14 +02:00
}
break;
case BIO_C_SET_FD:
if (arg2)
{
transport_bio_simple_uninit(bio);
2019-11-06 15:24:51 +01:00
transport_bio_simple_init(bio, (SOCKET) * ((int*)arg2), (int)arg1);
status = 1;
}
2016-12-19 13:49:40 +01:00
break;
case BIO_C_GET_FD:
2016-11-21 17:28:54 +01:00
if (BIO_get_init(bio))
{
if (arg2)
2019-11-06 15:24:51 +01:00
*((int*)arg2) = (int)ptr->socket;
2016-12-19 13:49:40 +01:00
2019-11-06 15:24:51 +01:00
status = (int)ptr->socket;
}
2016-12-19 13:49:40 +01:00
break;
case BIO_CTRL_GET_CLOSE:
2016-11-21 17:28:54 +01:00
status = BIO_get_shutdown(bio);
break;
case BIO_CTRL_SET_CLOSE:
2019-11-06 15:24:51 +01:00
BIO_set_shutdown(bio, (int)arg1);
status = 1;
break;
case BIO_CTRL_FLUSH:
case BIO_CTRL_DUP:
status = 1;
break;
default:
status = 0;
break;
}
return status;
}
static int transport_bio_simple_init(BIO* bio, SOCKET socket, int shutdown)
{
2019-11-06 15:24:51 +01:00
WINPR_BIO_SIMPLE_SOCKET* ptr = (WINPR_BIO_SIMPLE_SOCKET*)BIO_get_data(bio);
ptr->socket = socket;
2016-11-21 17:28:54 +01:00
BIO_set_shutdown(bio, shutdown);
BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
BIO_set_init(bio, 1);
ptr->hEvent = WSACreateEvent();
if (!ptr->hEvent)
return 0;
/* WSAEventSelect automatically sets the socket in non-blocking mode */
if (WSAEventSelect(ptr->socket, ptr->hEvent, FD_READ | FD_ACCEPT | FD_CLOSE))
2015-07-14 11:58:01 +02:00
{
WLog_ERR(TAG, "WSAEventSelect returned 0x%08x",
WINPR_CXX_COMPAT_CAST(unsigned, WSAGetLastError()));
2015-07-14 11:58:01 +02:00
return 0;
}
return 1;
}
static int transport_bio_simple_uninit(BIO* bio)
{
2019-11-06 15:24:51 +01:00
WINPR_BIO_SIMPLE_SOCKET* ptr = (WINPR_BIO_SIMPLE_SOCKET*)BIO_get_data(bio);
2016-11-21 17:28:54 +01:00
if (BIO_get_shutdown(bio))
{
if (BIO_get_init(bio) && ptr)
{
_shutdown(ptr->socket, SD_BOTH);
closesocket(ptr->socket);
ptr->socket = 0;
}
}
if (ptr && ptr->hEvent)
{
2024-09-16 04:58:36 +02:00
(void)CloseHandle(ptr->hEvent);
ptr->hEvent = nullptr;
}
2016-11-21 17:28:54 +01:00
BIO_set_init(bio, 0);
BIO_set_flags(bio, 0);
return 1;
}
static int transport_bio_simple_new(BIO* bio)
{
WINPR_BIO_SIMPLE_SOCKET* ptr = nullptr;
2016-11-21 17:28:54 +01:00
BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
2019-11-06 15:24:51 +01:00
ptr = (WINPR_BIO_SIMPLE_SOCKET*)calloc(1, sizeof(WINPR_BIO_SIMPLE_SOCKET));
if (!ptr)
return 0;
2016-11-21 17:28:54 +01:00
BIO_set_data(bio, ptr);
return 1;
}
static int transport_bio_simple_free(BIO* bio)
{
WINPR_BIO_SIMPLE_SOCKET* ptr = nullptr;
2016-11-21 17:28:54 +01:00
if (!bio)
return 0;
transport_bio_simple_uninit(bio);
2019-11-06 15:24:51 +01:00
ptr = (WINPR_BIO_SIMPLE_SOCKET*)BIO_get_data(bio);
2016-11-21 17:28:54 +01:00
if (ptr)
{
BIO_set_data(bio, nullptr);
2016-11-21 17:28:54 +01:00
free(ptr);
}
return 1;
}
BIO_METHOD* BIO_s_simple_socket(void)
{
static BIO_METHOD* bio_methods = nullptr;
2016-11-21 17:28:54 +01:00
if (bio_methods == nullptr)
2016-11-21 17:28:54 +01:00
{
if (!(bio_methods = BIO_meth_new(BIO_TYPE_SIMPLE, "SimpleSocket")))
return nullptr;
2016-11-21 17:28:54 +01:00
BIO_meth_set_write(bio_methods, transport_bio_simple_write);
BIO_meth_set_read(bio_methods, transport_bio_simple_read);
BIO_meth_set_puts(bio_methods, transport_bio_simple_puts);
BIO_meth_set_gets(bio_methods, transport_bio_simple_gets);
BIO_meth_set_ctrl(bio_methods, transport_bio_simple_ctrl);
BIO_meth_set_create(bio_methods, transport_bio_simple_new);
BIO_meth_set_destroy(bio_methods, transport_bio_simple_free);
}
return bio_methods;
}
/* Buffered Socket BIO */
typedef struct
{
BIO* bufferedBio;
BOOL readBlocked;
BOOL writeBlocked;
RingBuffer xmitBuffer;
} WINPR_BIO_BUFFERED_SOCKET;
static int transport_bio_buffered_write(BIO* bio, const char* buf, int num)
{
int ret = num;
int nchunks = 0;
size_t committedBytes = 0;
DataChunk chunks[2] = WINPR_C_ARRAY_INIT;
2019-11-06 15:24:51 +01:00
WINPR_BIO_BUFFERED_SOCKET* ptr = (WINPR_BIO_BUFFERED_SOCKET*)BIO_get_data(bio);
BIO* next_bio = nullptr;
WINPR_ASSERT(bio);
WINPR_ASSERT(ptr);
if (num < 0)
return num;
ptr->writeBlocked = FALSE;
BIO_clear_flags(bio, BIO_FLAGS_WRITE);
/* we directly append extra bytes in the xmit buffer, this could be prevented
* but for now it makes the code more simple.
*/
if (buf && (num > 0) && !ringbuffer_write(&ptr->xmitBuffer, (const BYTE*)buf, (size_t)num))
{
WLog_ERR(TAG, "an error occurred when writing (num: %d)", num);
return -1;
}
nchunks = ringbuffer_peek(&ptr->xmitBuffer, chunks, ringbuffer_used(&ptr->xmitBuffer));
2016-11-21 17:28:54 +01:00
next_bio = BIO_next(bio);
for (int i = 0; i < nchunks; i++)
{
while (chunks[i].size)
{
ERR_clear_error();
2024-10-14 15:50:38 +02:00
const size_t wr = MIN(INT32_MAX, chunks[i].size);
const int status = BIO_write(next_bio, chunks[i].data, (int)wr);
if (status <= 0)
{
2016-11-21 17:28:54 +01:00
if (!BIO_should_retry(next_bio))
{
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
ret = -1; /* fatal error */
goto out;
}
2016-11-21 17:28:54 +01:00
if (BIO_should_write(next_bio))
{
BIO_set_flags(bio, BIO_FLAGS_WRITE);
ptr->writeBlocked = TRUE;
goto out; /* EWOULDBLOCK */
}
}
else
{
committedBytes += (size_t)status;
chunks[i].size -= (size_t)status;
chunks[i].data += status;
}
}
}
out:
ringbuffer_commit_read_bytes(&ptr->xmitBuffer, committedBytes);
return ret;
}
static int transport_bio_buffered_read(BIO* bio, char* buf, int size)
{
int status = 0;
2019-11-06 15:24:51 +01:00
WINPR_BIO_BUFFERED_SOCKET* ptr = (WINPR_BIO_BUFFERED_SOCKET*)BIO_get_data(bio);
2016-11-21 17:28:54 +01:00
BIO* next_bio = BIO_next(bio);
ptr->readBlocked = FALSE;
BIO_clear_flags(bio, BIO_FLAGS_READ);
ERR_clear_error();
2016-11-21 17:28:54 +01:00
status = BIO_read(next_bio, buf, size);
if (status <= 0)
{
2016-11-21 17:28:54 +01:00
if (!BIO_should_retry(next_bio))
{
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
goto out;
}
BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
2016-11-21 17:28:54 +01:00
if (BIO_should_read(next_bio))
{
BIO_set_flags(bio, BIO_FLAGS_READ);
ptr->readBlocked = TRUE;
goto out;
}
}
out:
return status;
}
static int transport_bio_buffered_puts(WINPR_ATTR_UNUSED BIO* bio,
WINPR_ATTR_UNUSED const char* str)
{
return 1;
}
static int transport_bio_buffered_gets(WINPR_ATTR_UNUSED BIO* bio, WINPR_ATTR_UNUSED char* str,
WINPR_ATTR_UNUSED int size)
{
return 1;
}
static long transport_bio_buffered_ctrl(BIO* bio, int cmd, long arg1, void* arg2)
{
2022-04-25 09:47:14 +02:00
long status = -1;
2019-11-06 15:24:51 +01:00
WINPR_BIO_BUFFERED_SOCKET* ptr = (WINPR_BIO_BUFFERED_SOCKET*)BIO_get_data(bio);
switch (cmd)
{
case BIO_CTRL_FLUSH:
if (!ringbuffer_used(&ptr->xmitBuffer))
status = 1;
else
status = (transport_bio_buffered_write(bio, nullptr, 0) >= 0) ? 1 : -1;
2016-12-19 13:49:40 +01:00
break;
case BIO_CTRL_WPENDING:
status = WINPR_ASSERTING_INT_CAST(long, ringbuffer_used(&ptr->xmitBuffer));
break;
case BIO_CTRL_PENDING:
status = 0;
break;
case BIO_C_READ_BLOCKED:
2019-11-06 15:24:51 +01:00
status = (int)ptr->readBlocked;
break;
case BIO_C_WRITE_BLOCKED:
2019-11-06 15:24:51 +01:00
status = (int)ptr->writeBlocked;
break;
default:
2016-11-21 17:28:54 +01:00
status = BIO_ctrl(BIO_next(bio), cmd, arg1, arg2);
break;
}
return status;
}
static int transport_bio_buffered_new(BIO* bio)
{
WINPR_BIO_BUFFERED_SOCKET* ptr = nullptr;
2016-11-21 17:28:54 +01:00
BIO_set_init(bio, 1);
BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
2019-11-06 15:24:51 +01:00
ptr = (WINPR_BIO_BUFFERED_SOCKET*)calloc(1, sizeof(WINPR_BIO_BUFFERED_SOCKET));
if (!ptr)
return -1;
2019-11-06 15:24:51 +01:00
BIO_set_data(bio, (void*)ptr);
if (!ringbuffer_init(&ptr->xmitBuffer, 0x10000))
return -1;
return 1;
}
/* Free the buffered BIO.
* Do not free other elements in the BIO stack,
* let BIO_free_all handle that. */
static int transport_bio_buffered_free(BIO* bio)
{
2019-11-06 15:24:51 +01:00
WINPR_BIO_BUFFERED_SOCKET* ptr = (WINPR_BIO_BUFFERED_SOCKET*)BIO_get_data(bio);
if (!ptr)
return 0;
ringbuffer_destroy(&ptr->xmitBuffer);
free(ptr);
return 1;
}
BIO_METHOD* BIO_s_buffered_socket(void)
{
static BIO_METHOD* bio_methods = nullptr;
2016-11-21 17:28:54 +01:00
if (bio_methods == nullptr)
2016-11-21 17:28:54 +01:00
{
if (!(bio_methods = BIO_meth_new(BIO_TYPE_BUFFERED, "BufferedSocket")))
return nullptr;
2016-11-21 17:28:54 +01:00
BIO_meth_set_write(bio_methods, transport_bio_buffered_write);
BIO_meth_set_read(bio_methods, transport_bio_buffered_read);
BIO_meth_set_puts(bio_methods, transport_bio_buffered_puts);
BIO_meth_set_gets(bio_methods, transport_bio_buffered_gets);
BIO_meth_set_ctrl(bio_methods, transport_bio_buffered_ctrl);
BIO_meth_set_create(bio_methods, transport_bio_buffered_new);
BIO_meth_set_destroy(bio_methods, transport_bio_buffered_free);
}
return bio_methods;
}
2018-09-27 16:08:28 +02:00
char* freerdp_tcp_address_to_string(const struct sockaddr_storage* addr, BOOL* pIPv6)
{
char ipAddress[INET6_ADDRSTRLEN + 1] = WINPR_C_ARRAY_INIT;
2021-08-02 12:13:34 +02:00
const struct sockaddr_in6* sockaddr_ipv6 = (const struct sockaddr_in6*)addr;
const struct sockaddr_in* sockaddr_ipv4 = (const struct sockaddr_in*)addr;
if (addr == nullptr)
{
return nullptr;
}
2018-02-08 16:31:26 +01:00
switch (sockaddr_ipv4->sin_family)
{
case AF_INET:
2019-11-06 15:24:51 +01:00
if (!inet_ntop(sockaddr_ipv4->sin_family, &sockaddr_ipv4->sin_addr, ipAddress,
sizeof(ipAddress)))
return nullptr;
2018-02-08 16:31:26 +01:00
break;
case AF_INET6:
2019-11-06 15:24:51 +01:00
if (!inet_ntop(sockaddr_ipv6->sin6_family, &sockaddr_ipv6->sin6_addr, ipAddress,
sizeof(ipAddress)))
return nullptr;
break;
case AF_UNIX:
(void)sprintf_s(ipAddress, ARRAYSIZE(ipAddress), "127.0.0.1");
break;
default:
return nullptr;
}
if (pIPv6 != nullptr)
{
2018-02-08 16:31:26 +01:00
*pIPv6 = (sockaddr_ipv4->sin_family == AF_INET6);
}
return _strdup(ipAddress);
}
static bool freerdp_tcp_get_ip_address(rdpSettings* settings, int sockfd)
{
WINPR_ASSERT(settings);
struct sockaddr_storage saddr = WINPR_C_ARRAY_INIT;
socklen_t length = sizeof(struct sockaddr_storage);
if (!freerdp_settings_set_string(settings, FreeRDP_ClientAddress, nullptr))
return false;
if (sockfd < 0)
return false;
if (getsockname(sockfd, (struct sockaddr*)&saddr, &length) != 0)
return false;
settings->ClientAddress = freerdp_tcp_address_to_string(&saddr, &settings->IPv6Enabled);
return settings->ClientAddress != nullptr;
}
char* freerdp_tcp_get_peer_address(SOCKET sockfd)
{
struct sockaddr_storage saddr = WINPR_C_ARRAY_INIT;
socklen_t length = sizeof(struct sockaddr_storage);
2024-10-14 15:50:38 +02:00
if (getpeername((int)sockfd, (struct sockaddr*)&saddr, &length) != 0)
{
return nullptr;
}
return freerdp_tcp_address_to_string(&saddr, nullptr);
}
static int freerdp_uds_connect(const char* path)
2014-11-12 14:06:34 -05:00
{
#ifndef _WIN32
int status = 0;
int sockfd = 0;
struct sockaddr_un addr = WINPR_C_ARRAY_INIT;
2014-11-12 14:06:34 -05:00
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd == -1)
{
WLog_ERR(TAG, "socket");
return -1;
}
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
2019-11-06 15:24:51 +01:00
status = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
2014-11-12 14:06:34 -05:00
if (status < 0)
{
WLog_ERR(TAG, "connect");
close(sockfd);
return -1;
}
return sockfd;
#else /* ifndef _WIN32 */
return -1;
#endif
}
2018-09-27 16:08:28 +02:00
struct addrinfo* freerdp_tcp_resolve_host(const char* hostname, int port, int ai_flags)
{
char* service = nullptr;
2018-09-27 16:08:28 +02:00
char port_str[16];
int status = 0;
struct addrinfo hints = WINPR_C_ARRAY_INIT;
struct addrinfo* result = nullptr;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
2018-09-27 16:08:28 +02:00
hints.ai_flags = ai_flags;
if (port >= 0)
{
(void)sprintf_s(port_str, sizeof(port_str) - 1, "%d", port);
2018-09-27 16:08:28 +02:00
service = port_str;
}
status = getaddrinfo(hostname, service, &hints, &result);
if (status)
return nullptr;
2018-09-27 16:08:28 +02:00
return result;
}
static BOOL freerdp_tcp_is_hostname_resolvable(rdpContext* context, const char* hostname)
{
struct addrinfo* result = freerdp_tcp_resolve_host(hostname, -1, 0);
if (!result)
2016-12-19 13:49:40 +01:00
{
freerdp_set_last_error_if_not(context, FREERDP_ERROR_DNS_NAME_NOT_FOUND);
2016-12-19 13:49:40 +01:00
return FALSE;
2016-12-19 13:49:40 +01:00
}
freerdp_set_last_error_log(context, 0);
freeaddrinfo(result);
return TRUE;
}
2019-11-06 15:24:51 +01:00
static BOOL freerdp_tcp_connect_timeout(rdpContext* context, int sockfd, struct sockaddr* addr,
size_t addrlen, UINT32 timeout)
{
BOOL rc = FALSE;
HANDLE handles[2] = WINPR_C_ARRAY_INIT;
DWORD count = 0;
2015-07-14 11:58:01 +02:00
u_long arg = 0;
2021-06-02 16:51:49 +02:00
DWORD tout = (timeout > 0) ? timeout : INFINITE;
2019-02-07 14:22:28 +01:00
handles[count] = CreateEvent(nullptr, TRUE, FALSE, nullptr);
2016-12-19 13:49:40 +01:00
if (!handles[count])
return FALSE;
const int wsastatus = WSAEventSelect((SOCKET)sockfd, handles[count++],
FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE);
2016-12-19 13:49:40 +01:00
if (wsastatus < 0)
{
WLog_ERR(TAG, "WSAEventSelect failed with %d", WSAGetLastError());
goto fail;
2015-07-14 11:58:01 +02:00
}
handles[count++] = utils_get_abort_event(context->rdp);
2016-12-19 13:49:40 +01:00
{
2026-01-08 10:32:30 +01:00
const int constatus =
_connect((SOCKET)sockfd, addr, WINPR_ASSERTING_INT_CAST(int, addrlen));
if (constatus < 0)
{
2026-01-08 10:32:30 +01:00
const int estatus = WSAGetLastError();
2016-12-19 13:49:40 +01:00
2026-01-08 10:32:30 +01:00
switch (estatus)
{
case WSAEINPROGRESS:
case WSAEWOULDBLOCK:
break;
default:
goto fail;
}
}
}
{
2026-01-08 10:32:30 +01:00
const DWORD wstatus = WaitForMultipleObjects(count, handles, FALSE, tout);
if (WAIT_OBJECT_0 != wstatus)
goto fail;
}
2026-01-08 10:32:30 +01:00
{
INT32 optval = 0;
socklen_t optlen = sizeof(optval);
if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)
goto fail;
if (optval != 0)
2026-01-08 10:32:30 +01:00
{
char ebuffer[256] = WINPR_C_ARRAY_INIT;
WLog_DBG(TAG, "connect failed with error: %s [%" PRId32 "]",
winpr_strerror(optval, ebuffer, sizeof(ebuffer)), optval);
goto fail;
2026-01-08 10:32:30 +01:00
}
}
{
2026-01-08 10:32:30 +01:00
const int status = WSAEventSelect((SOCKET)sockfd, handles[0], 0);
if (status < 0)
{
WLog_ERR(TAG, "WSAEventSelect failed with %d", WSAGetLastError());
goto fail;
}
}
if (_ioctlsocket((SOCKET)sockfd, FIONBIO, &arg) != 0)
goto fail;
2015-07-14 11:58:01 +02:00
rc = TRUE;
fail:
2024-09-16 04:58:36 +02:00
(void)CloseHandle(handles[0]);
return rc;
}
typedef struct
{
SOCKET s;
struct addrinfo* addr;
struct addrinfo* result;
} t_peer;
static void peer_free(t_peer* peer)
{
if (peer->s != INVALID_SOCKET)
closesocket(peer->s);
freeaddrinfo(peer->addr);
memset(peer, 0, sizeof(t_peer));
peer->s = INVALID_SOCKET;
}
static int freerdp_tcp_connect_multi(rdpContext* context, char** hostnames, const UINT32* ports,
UINT32 count, UINT16 port, WINPR_ATTR_UNUSED UINT32 timeout)
{
UINT32 sindex = count;
SOCKET sockfd = INVALID_SOCKET;
struct addrinfo* addr = nullptr;
struct addrinfo* result = nullptr;
2024-10-14 15:50:38 +02:00
HANDLE* events = (HANDLE*)calloc(count + 1, sizeof(HANDLE));
t_peer* peers = (t_peer*)calloc(count, sizeof(t_peer));
if (!peers || !events || (count < 1))
{
free(peers);
free((void*)events);
return -1;
}
for (UINT32 index = 0; index < count; index++)
{
int curPort = port;
if (ports)
curPort = WINPR_ASSERTING_INT_CAST(int, ports[index]);
result = freerdp_tcp_resolve_host(hostnames[index], curPort, 0);
2018-09-27 16:08:28 +02:00
if (!result)
continue;
addr = result;
if ((addr->ai_family == AF_INET6) && (addr->ai_next != nullptr))
{
while ((addr = addr->ai_next))
{
if (addr->ai_family == AF_INET)
break;
}
if (!addr)
addr = result;
}
peers[index].s = _socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (peers[index].s == INVALID_SOCKET)
{
freeaddrinfo(result);
continue;
}
peers[index].addr = addr;
peers[index].result = result;
}
for (UINT32 index = 0; index < count; index++)
{
sockfd = peers[index].s;
addr = peers[index].addr;
2018-11-23 10:32:52 +01:00
if ((sockfd == INVALID_SOCKET) || (!addr))
continue;
/* blocking tcp connect */
const int rc =
_connect(sockfd, addr->ai_addr, WINPR_ASSERTING_INT_CAST(int, addr->ai_addrlen));
if (rc >= 0)
{
/* connection success */
sindex = index;
break;
}
}
if (sindex < count)
{
sockfd = peers[sindex].s;
peers[sindex].s = INVALID_SOCKET;
}
else
freerdp_set_last_error_log(context, FREERDP_ERROR_CONNECT_CANCELLED);
for (UINT32 index = 0; index < count; index++)
peer_free(&peers[index]);
free(peers);
free((void*)events);
2024-10-14 15:50:38 +02:00
return (int)sockfd;
}
BOOL freerdp_tcp_set_keep_alive_mode(const rdpSettings* settings, int sockfd)
{
const BOOL keepalive = (freerdp_settings_get_bool(settings, FreeRDP_TcpKeepAlive));
UINT32 optval = 0;
socklen_t optlen = 0;
optval = keepalive ? 1 : 0;
optlen = sizeof(optval);
2019-11-06 15:24:51 +01:00
if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (void*)&optval, optlen) < 0)
{
WLog_WARN(TAG, "setsockopt() SOL_SOCKET, SO_KEEPALIVE");
}
#ifndef _WIN32
#ifdef TCP_KEEPIDLE
optval = keepalive ? freerdp_settings_get_uint32(settings, FreeRDP_TcpKeepAliveDelay) : 0;
optlen = sizeof(optval);
2019-11-06 15:24:51 +01:00
if (setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, (void*)&optval, optlen) < 0)
{
WLog_WARN(TAG, "setsockopt() IPPROTO_TCP, TCP_KEEPIDLE");
}
2017-05-17 09:30:13 -07:00
#endif
#ifndef SOL_TCP
/* "tcp" from /etc/protocols as getprotobyname(3C) */
2017-05-17 09:30:13 -07:00
#define SOL_TCP 6
2016-12-19 13:49:40 +01:00
#endif
#ifdef TCP_KEEPCNT
optval = keepalive ? freerdp_settings_get_uint32(settings, FreeRDP_TcpKeepAliveRetries) : 0;
optlen = sizeof(optval);
2019-11-06 15:24:51 +01:00
if (setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, (void*)&optval, optlen) < 0)
{
WLog_WARN(TAG, "setsockopt() SOL_TCP, TCP_KEEPCNT");
}
2016-12-19 13:49:40 +01:00
#endif
#ifdef TCP_KEEPINTVL
optval = keepalive ? freerdp_settings_get_uint32(settings, FreeRDP_TcpKeepAliveInterval) : 0;
optlen = sizeof(optval);
2019-11-06 15:24:51 +01:00
if (setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, (void*)&optval, optlen) < 0)
{
WLog_WARN(TAG, "setsockopt() SOL_TCP, TCP_KEEPINTVL");
}
2016-12-19 13:49:40 +01:00
#endif
#endif
#if defined(__MACOSX__) || defined(__IOS__)
optval = 1;
optlen = sizeof(optval);
2016-12-19 13:49:40 +01:00
2019-11-06 15:24:51 +01:00
if (setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&optval, optlen) < 0)
{
WLog_WARN(TAG, "setsockopt() SOL_SOCKET, SO_NOSIGPIPE");
}
2016-12-19 13:49:40 +01:00
#endif
#ifdef TCP_USER_TIMEOUT
optval = freerdp_settings_get_uint32(settings, FreeRDP_TcpAckTimeout);
optlen = sizeof(optval);
2019-11-06 15:24:51 +01:00
if (setsockopt(sockfd, SOL_TCP, TCP_USER_TIMEOUT, (void*)&optval, optlen) < 0)
{
WLog_WARN(TAG, "setsockopt() SOL_TCP, TCP_USER_TIMEOUT");
}
2016-12-19 13:49:40 +01:00
#endif
return TRUE;
}
2021-09-06 11:01:36 +02:00
int freerdp_tcp_connect(rdpContext* context, const char* hostname, int port, DWORD timeout)
{
rdpTransport* transport = nullptr;
if (!context || !context->rdp)
return -1;
transport = context->rdp->transport;
if (!transport)
return -1;
2021-09-06 11:01:36 +02:00
return transport_tcp_connect(context->rdp->transport, hostname, port, timeout);
}
static struct addrinfo* reorder_addrinfo_by_preference(rdpContext* context, struct addrinfo* addr)
{
WINPR_ASSERT(context);
WINPR_ASSERT(addr);
const BOOL preferIPv6 =
freerdp_settings_get_bool(context->settings, FreeRDP_PreferIPv6OverIPv4);
if (!preferIPv6)
return addr;
struct addrinfo* ipv6Head = nullptr;
struct addrinfo* ipv6Tail = nullptr;
struct addrinfo* otherHead = nullptr;
struct addrinfo* otherTail = nullptr;
/* Partition the list into IPv6 and other addresses */
while (addr)
{
struct addrinfo* next = addr->ai_next;
addr->ai_next = nullptr;
if (addr->ai_family == AF_INET6)
{
if (!ipv6Head)
ipv6Head = addr;
else
ipv6Tail->ai_next = addr;
ipv6Tail = addr;
}
else
{
if (!otherHead)
otherHead = addr;
else
otherTail->ai_next = addr;
otherTail = addr;
}
addr = next;
}
/* Concatenate the lists */
if (ipv6Tail)
ipv6Tail->ai_next = otherHead;
return ipv6Head ? ipv6Head : otherHead;
}
static int get_next_addrinfo(rdpContext* context, struct addrinfo* input, struct addrinfo** result,
UINT32 errorCode)
{
WINPR_ASSERT(context);
WINPR_ASSERT(result);
struct addrinfo* addr = input;
if (!addr)
goto fail;
/* We want to force IPvX, abort if not detected */
{
2026-01-08 10:32:30 +01:00
const UINT32 IPvX = freerdp_settings_get_uint32(context->settings, FreeRDP_ForceIPvX);
switch (IPvX)
{
2026-01-08 10:32:30 +01:00
case 4:
case 6:
{
const int family = (IPvX == 4) ? AF_INET : AF_INET6;
while (addr && (addr->ai_family != family))
addr = addr->ai_next;
}
break;
2026-01-08 10:32:30 +01:00
default:
break;
}
}
if (!addr)
goto fail;
*result = addr;
return 0;
fail:
freerdp_set_last_error_if_not(context, errorCode);
*result = nullptr;
return -1;
}
static int freerdp_vsock_connect(rdpContext* context, const char* hostname, int port)
{
#if defined(HAVE_AF_VSOCK_H)
int sockfd = socket(AF_VSOCK, SOCK_STREAM, 0);
if (sockfd < 0)
{
char buffer[256] = WINPR_C_ARRAY_INIT;
WLog_WARN(TAG, "socket(AF_VSOCK, SOCK_STREAM, 0) failed with %s",
winpr_strerror(errno, buffer, sizeof(buffer)));
freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED);
return -1;
}
struct sockaddr_vm addr = WINPR_C_ARRAY_INIT;
addr.svm_family = AF_VSOCK;
addr.svm_port = WINPR_ASSERTING_INT_CAST(typeof(addr.svm_port), port);
errno = 0;
char* ptr = nullptr;
unsigned long val = strtoul(hostname, &ptr, 10);
if (errno || (val > UINT32_MAX))
{
char ebuffer[256] = WINPR_C_ARRAY_INIT;
WLog_ERR(TAG, "could not extract port from '%s', value=%lu, error=%s", hostname, val,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
close(sockfd);
return -1;
}
addr.svm_cid = WINPR_ASSERTING_INT_CAST(typeof(addr.svm_cid), val);
if (addr.svm_cid == 2)
{
addr.svm_flags = VMADDR_FLAG_TO_HOST;
}
if ((connect(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_vm))) == -1)
{
WLog_ERR(TAG, "failed to connect to %s", hostname);
close(sockfd);
return -1;
}
return sockfd;
#else
WLog_ERR(TAG, "Compiled without AF_VSOCK, '%s' not supported", hostname);
return -1;
#endif
}
static void log_connection_address(const char* hostname, struct addrinfo* addr)
{
WINPR_ASSERT(addr);
char* peerAddress =
freerdp_tcp_address_to_string((const struct sockaddr_storage*)addr->ai_addr, nullptr);
if (peerAddress)
WLog_DBG(TAG, "resolved %s: try to connect to %s", hostname, peerAddress);
free(peerAddress);
}
static int freerdp_host_connect(rdpContext* context, const char* hostname, int port, DWORD timeout)
{
int sockfd = -1;
struct addrinfo* addr = nullptr;
struct addrinfo* result = freerdp_tcp_resolve_host(hostname, port, 0);
if (!result)
{
freerdp_set_last_error_if_not(context, FREERDP_ERROR_DNS_NAME_NOT_FOUND);
return -1;
}
freerdp_set_last_error_log(context, 0);
/* By default we take the first returned entry.
* If PreferIPv6OverIPv4 = TRUE we reorder addresses by preference:
* IPv6 addresses come first, then other addresses.
*/
result = reorder_addrinfo_by_preference(context, result);
const int rc = get_next_addrinfo(context, result, &addr, FREERDP_ERROR_DNS_NAME_NOT_FOUND);
if (rc < 0)
goto fail;
do
{
sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (sockfd >= 0)
{
log_connection_address(hostname, addr);
if (!freerdp_tcp_connect_timeout(context, sockfd, addr->ai_addr, addr->ai_addrlen,
timeout))
{
close(sockfd);
sockfd = -1;
}
}
if (sockfd < 0)
{
const int lrc =
get_next_addrinfo(context, addr->ai_next, &addr, FREERDP_ERROR_CONNECT_FAILED);
if (lrc < 0)
goto fail;
}
} while (sockfd < 0);
fail:
freeaddrinfo(result);
return sockfd;
}
int freerdp_tcp_default_connect(rdpContext* context, rdpSettings* settings, const char* hostname,
int port, DWORD timeout)
{
int sockfd = -1;
BOOL ipcSocket = FALSE;
BOOL useExternalDefinedSocket = FALSE;
if (!hostname)
{
freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED);
return -1;
}
if (hostname[0] == '/')
ipcSocket = TRUE;
if (hostname[0] == '|')
useExternalDefinedSocket = TRUE;
const char* vsock = utils_is_vsock(hostname);
if (ipcSocket)
{
sockfd = freerdp_uds_connect(hostname);
if (sockfd < 0)
{
freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED);
return -1;
}
}
2016-12-19 13:49:40 +01:00
else if (useExternalDefinedSocket)
sockfd = port;
else if (vsock)
sockfd = freerdp_vsock_connect(context, vsock, port);
else
{
if (!settings->GatewayEnabled)
{
2019-11-06 15:24:51 +01:00
if (!freerdp_tcp_is_hostname_resolvable(context, hostname) ||
settings->RemoteAssistanceMode)
{
if (settings->TargetNetAddressCount > 0)
{
WINPR_ASSERT(port <= UINT16_MAX);
sockfd = freerdp_tcp_connect_multi(
2019-11-06 15:24:51 +01:00
context, settings->TargetNetAddresses, settings->TargetNetPorts,
settings->TargetNetAddressCount, (UINT16)port, timeout);
}
}
}
if (sockfd <= 0)
sockfd = freerdp_host_connect(context, hostname, port, timeout);
}
if (!vsock)
{
if (!freerdp_tcp_get_ip_address(settings, sockfd))
{
if (!useExternalDefinedSocket)
close(sockfd);
freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED);
WLog_ERR(TAG, "Couldn't get socket ip address");
return -1;
}
}
if (!ipcSocket && !useExternalDefinedSocket)
{
(void)freerdp_tcp_set_nodelay(WLog_Get(TAG), WLOG_ERROR, sockfd);
}
/* receive buffer must be a least 32 K */
UINT32 optval = 1;
socklen_t optlen = sizeof(optval);
2019-11-06 15:24:51 +01:00
if (getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (void*)&optval, &optlen) == 0)
{
if (optval < (1024 * 32))
{
optval = 1024 * 32;
optlen = sizeof(optval);
2019-11-06 15:24:51 +01:00
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (void*)&optval, optlen) < 0)
{
close(sockfd);
freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED);
WLog_ERR(TAG, "unable to set receive buffer len");
return -1;
}
}
}
if (!ipcSocket && !useExternalDefinedSocket)
{
if (!freerdp_tcp_set_keep_alive_mode(settings, sockfd))
{
close(sockfd);
freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED);
WLog_ERR(TAG, "Couldn't set keep alive mode.");
return -1;
}
}
if (WaitForSingleObject(utils_get_abort_event(context->rdp), 0) == WAIT_OBJECT_0)
{
close(sockfd);
return -1;
}
return sockfd;
}
struct rdp_tcp_layer
{
int sockfd;
HANDLE hEvent;
};
typedef struct rdp_tcp_layer rdpTcpLayer;
static int freerdp_tcp_layer_read(void* userContext, void* data, int bytes)
{
if (!userContext)
return -1;
if (!data || !bytes)
return 0;
rdpTcpLayer* tcpLayer = (rdpTcpLayer*)userContext;
2024-09-16 04:53:18 +02:00
(void)WSAResetEvent(tcpLayer->hEvent);
const int status = _recv((SOCKET)tcpLayer->sockfd, data, bytes, 0);
if (status > 0)
return status;
const int error = WSAGetLastError();
if ((error == WSAEWOULDBLOCK) || (error == WSAEINTR) || (error == WSAEINPROGRESS) ||
(error == WSAEALREADY))
errno = EAGAIN;
return status;
}
static int freerdp_tcp_layer_write(void* userContext, const void* data, int bytes)
{
if (!userContext)
return -1;
if (!data || !bytes)
return 0;
rdpTcpLayer* tcpLayer = (rdpTcpLayer*)userContext;
const int status = _send((SOCKET)tcpLayer->sockfd, data, bytes, 0);
if (status > 0)
return status;
const int error = WSAGetLastError();
if ((error == WSAEWOULDBLOCK) || (error == WSAEINTR) || (error == WSAEINPROGRESS) ||
(error == WSAEALREADY))
errno = EAGAIN;
return status;
}
static BOOL freerdp_tcp_layer_close(void* userContext)
{
if (!userContext)
return FALSE;
rdpTcpLayer* tcpLayer = (rdpTcpLayer*)userContext;
if (tcpLayer->sockfd >= 0)
closesocket((SOCKET)tcpLayer->sockfd);
if (tcpLayer->hEvent)
2024-09-16 04:58:36 +02:00
(void)CloseHandle(tcpLayer->hEvent);
return TRUE;
}
static BOOL freerdp_tcp_layer_wait(void* userContext, BOOL waitWrite, DWORD timeout)
{
if (!userContext)
return FALSE;
rdpTcpLayer* tcpLayer = (rdpTcpLayer*)userContext;
int status = -1;
int sockfd = tcpLayer->sockfd;
#ifdef WINPR_HAVE_POLL_H
struct pollfd pollset = WINPR_C_ARRAY_INIT;
pollset.fd = sockfd;
pollset.events = waitWrite ? POLLOUT : POLLIN;
do
{
status = poll(&pollset, 1, (int)timeout);
} while ((status < 0) && (errno == EINTR));
#else
fd_set rset = WINPR_C_ARRAY_INIT;
struct timeval tv = WINPR_C_ARRAY_INIT;
FD_ZERO(&rset);
FD_SET(sockfd, &rset);
if (timeout)
{
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
}
do
{
if (waitWrite)
status = select(sockfd + 1, nullptr, &rset, nullptr, timeout ? &tv : nullptr);
else
status = select(sockfd + 1, &rset, nullptr, nullptr, timeout ? &tv : nullptr);
} while ((status < 0) && (errno == EINTR));
#endif
return status != 0;
}
static HANDLE freerdp_tcp_layer_get_event(void* userContext)
{
if (!userContext)
return nullptr;
rdpTcpLayer* tcpLayer = (rdpTcpLayer*)userContext;
return tcpLayer->hEvent;
}
rdpTransportLayer* freerdp_tcp_connect_layer(rdpContext* context, const char* hostname, int port,
DWORD timeout)
{
WINPR_ASSERT(context);
const rdpSettings* settings = context->settings;
WINPR_ASSERT(settings);
rdpTransportLayer* layer = nullptr;
rdpTcpLayer* tcpLayer = nullptr;
int sockfd = freerdp_tcp_connect(context, hostname, port, timeout);
if (sockfd < 0)
goto fail;
if (!freerdp_tcp_set_keep_alive_mode(settings, sockfd))
goto fail;
layer = transport_layer_new(freerdp_get_transport(context), sizeof(rdpTcpLayer));
if (!layer)
goto fail;
layer->Read = freerdp_tcp_layer_read;
layer->Write = freerdp_tcp_layer_write;
layer->Close = freerdp_tcp_layer_close;
layer->Wait = freerdp_tcp_layer_wait;
layer->GetEvent = freerdp_tcp_layer_get_event;
tcpLayer = (rdpTcpLayer*)layer->userContext;
WINPR_ASSERT(tcpLayer);
tcpLayer->sockfd = -1;
tcpLayer->hEvent = WSACreateEvent();
if (!tcpLayer->hEvent)
goto fail;
/* WSAEventSelect automatically sets the socket in non-blocking mode */
if (WSAEventSelect((SOCKET)sockfd, tcpLayer->hEvent, FD_READ | FD_ACCEPT | FD_CLOSE))
{
WLog_ERR(TAG, "WSAEventSelect returned 0x%08x", (unsigned)WSAGetLastError());
goto fail;
}
tcpLayer->sockfd = sockfd;
return layer;
fail:
if (sockfd >= 0)
closesocket((SOCKET)sockfd);
transport_layer_free(layer);
return nullptr;
}
BOOL freerdp_tcp_set_nodelay(wLog* log, DWORD level, int sockfd)
{
WINPR_ASSERT(log);
int type = -1;
socklen_t typelen = sizeof(type);
char* ptype = (char*)&type;
const int rc = getsockopt(sockfd, SOL_SOCKET, SO_TYPE, ptype, &typelen);
if (rc < 0)
{
char buffer[128] = WINPR_C_ARRAY_INIT;
WLog_Print(log, level, "can't get SOL_SOCKET|SO_TYPE (%s)",
winpr_strerror(errno, buffer, sizeof(buffer)));
return FALSE;
}
else if (type == SOCK_STREAM)
{
int option_value = -1;
const socklen_t option_len = sizeof(option_value);
const int sr =
setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (void*)&option_value, option_len);
if (sr < 0)
{
/* local unix sockets don't have the TCP_NODELAY implemented, so don't make this
* error fatal */
char buffer[128] = WINPR_C_ARRAY_INIT;
WLog_Print(log, level, "can't set TCP_NODELAY (%s)",
winpr_strerror(errno, buffer, sizeof(buffer)));
return FALSE;
}
}
else
{
WLog_Print(log, level, "Socket SOL_SOCKET|SO_TYPE %d unsupported", type);
return FALSE;
}
return TRUE;
}