From 52606929fb6965c369dd54f6c34a0579302cde2c Mon Sep 17 00:00:00 2001 From: Stefan Heinzel Date: Tue, 14 Nov 2023 15:33:53 +0000 Subject: [PATCH] 5726 add vsock support for client and server --- client/common/cmdline.c | 8 +++- include/config/config.h.in | 2 + libfreerdp/core/CMakeLists.txt | 2 + libfreerdp/core/listener.c | 81 +++++++++++++++++++++++++++++++++- libfreerdp/core/tcp.c | 61 +++++++++++++++++++++---- libfreerdp/core/utils.c | 11 +++++ libfreerdp/core/utils.h | 2 + 7 files changed, 156 insertions(+), 11 deletions(-) diff --git a/client/common/cmdline.c b/client/common/cmdline.c index 0903c773c..07e3a3ee5 100644 --- a/client/common/cmdline.c +++ b/client/common/cmdline.c @@ -2315,7 +2315,13 @@ static int parse_host_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT /* ipv4 */ if (!p) { - p = strchr(arg->Value, ':'); + const char scheme[] = "://"; + const char* val = strstr(arg->Value, scheme); + if (val) + val += strnlen(scheme, sizeof(scheme)); + else + val = arg->Value; + p = strchr(val, ':'); if (p) { diff --git a/include/config/config.h.in b/include/config/config.h.in index 6957b9ae0..0c6ec7b8e 100644 --- a/include/config/config.h.in +++ b/include/config/config.h.in @@ -187,4 +187,6 @@ #cmakedefine WITH_PROXY_MODULES #cmakedefine WITH_PROXY_EMULATE_SMARTCARD +#cmakedefine HAVE_AF_VSOCK_H + #endif /* FREERDP_CONFIG_H */ diff --git a/libfreerdp/core/CMakeLists.txt b/libfreerdp/core/CMakeLists.txt index 08b2e9a5f..a576e5e36 100644 --- a/libfreerdp/core/CMakeLists.txt +++ b/libfreerdp/core/CMakeLists.txt @@ -18,6 +18,8 @@ set(MODULE_NAME "freerdp-core") set(MODULE_PREFIX "FREERDP_CORE") +CHECK_INCLUDE_FILES("ctype.h;linux/vm_sockets.h" HAVE_AF_VSOCK_H) + freerdp_definition_add(-DEXT_PATH="${FREERDP_EXTENSION_PATH}") freerdp_include_directory_add(${OPENSSL_INCLUDE_DIR}) diff --git a/libfreerdp/core/listener.c b/libfreerdp/core/listener.c index 3d2c3480b..a7ff3fbfc 100644 --- a/libfreerdp/core/listener.c +++ b/libfreerdp/core/listener.c @@ -40,12 +40,85 @@ #include #endif +#if defined(HAVE_AF_VSOCK_H) +#include +#include +#endif + #include #include "listener.h" +#include "utils.h" #define TAG FREERDP_TAG("core.listener") +static BOOL freerdp_listener_open_from_vsock(freerdp_listener* instance, const char* bind_address, + UINT16 port) +{ +#if defined(HAVE_AF_VSOCK_H) + rdpListener* listener = (rdpListener*)instance->listener; + const int sockfd = socket(AF_VSOCK, SOCK_STREAM, 0); + if (sockfd == -1) + { + WLog_ERR(TAG, "Error creating socket: %s", strerror(errno)); + return FALSE; + } + const int flags = fcntl(sockfd, F_GETFL, 0); + if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1) + { + WLog_ERR(TAG, "Error making socket nonblocking: %s", strerror(errno)); + closesocket((SOCKET)sockfd); + return FALSE; + } + struct sockaddr_vm addr = { 0 }; + + addr.svm_family = AF_VSOCK; + addr.svm_port = port; + + errno = 0; + char* ptr = NULL; + unsigned long val = strtoul(bind_address, &ptr, 10); + if (errno || (val > UINT32_MAX)) + { + WLog_ERR(TAG, "could not extract port from '%s', value=%ul, error=%s", bind_address, val, + strerror(errno)); + return FALSE; + } + addr.svm_cid = val; + if (bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_vm)) == -1) + { + WLog_ERR(TAG, "Error binding vsock at cid %d port %d: %s", addr.svm_cid, port, + strerror(errno)); + closesocket((SOCKET)sockfd); + return FALSE; + } + + if (listen(sockfd, 10) == -1) + { + WLog_ERR(TAG, "Error listening to socket at cid %d port %d: %s", addr.svm_cid, port, + strerror(errno)); + closesocket((SOCKET)sockfd); + return FALSE; + } + listener->sockfds[listener->num_sockfds] = sockfd; + listener->events[listener->num_sockfds] = WSACreateEvent(); + + if (!listener->events[listener->num_sockfds]) + { + listener->num_sockfds = 0; + } + + WSAEventSelect(sockfd, listener->events[listener->num_sockfds], FD_READ | FD_ACCEPT | FD_CLOSE); + listener->num_sockfds++; + + WLog_INFO(TAG, "Listening on %s:%d", bind_address, port); + return TRUE; +#else + WLog_ERR(TAG, "compiled without AF_VSOCK, '%s' not supported", bind_address); + return FALSE; +#endif +} + static BOOL freerdp_listener_open(freerdp_listener* instance, const char* bind_address, UINT16 port) { int ai_flags = 0; @@ -64,6 +137,12 @@ static BOOL freerdp_listener_open(freerdp_listener* instance, const char* bind_a if (!bind_address) ai_flags = AI_PASSIVE; + if (utils_is_vsock(bind_address)) + { + bind_address = utils_is_vsock(bind_address); + return freerdp_listener_open_from_vsock(instance, bind_address, port); + } + res = freerdp_tcp_resolve_host(bind_address, port, ai_flags); if (!res) @@ -322,7 +401,7 @@ BOOL freerdp_peer_set_local_and_hostname(freerdp_peer* client, } #ifndef _WIN32 -#ifdef AF_VSOCK +#if defined(HAVE_AF_VSOCK_H) else if (peer_addr->ss_family == AF_UNIX || peer_addr->ss_family == AF_VSOCK) #else else if (peer_addr->ss_family == AF_UNIX) diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c index ead6303b3..906f60f47 100644 --- a/libfreerdp/core/tcp.c +++ b/libfreerdp/core/tcp.c @@ -85,6 +85,11 @@ #include "tcp.h" #include "../crypto/opensslcompat.h" +#if defined(HAVE_AF_VSOCK_H) +#include +#include +#endif + #define TAG FREERDP_TAG("core") /* Simple Socket BIO */ @@ -1090,6 +1095,7 @@ int freerdp_tcp_default_connect(rdpContext* context, rdpSettings* settings, cons if (hostname[0] == '|') useExternalDefinedSocket = TRUE; + const char* vsock = utils_is_vsock(hostname); if (ipcSocket) { sockfd = freerdp_uds_connect(hostname); @@ -1103,6 +1109,40 @@ int freerdp_tcp_default_connect(rdpContext* context, rdpSettings* settings, cons } else if (useExternalDefinedSocket) sockfd = port; + else if (vsock) + { +#if defined(HAVE_AF_VSOCK_H) + hostname = vsock; + sockfd = socket(AF_VSOCK, SOCK_STREAM, 0); + struct sockaddr_vm addr = { 0 }; + + addr.svm_family = AF_VSOCK; + addr.svm_port = port; + + errno = 0; + char* ptr = NULL; + unsigned long val = strtoul(hostname, &ptr, 10); + if (errno || (val > UINT32_MAX)) + { + WLog_ERR(TAG, "could not extract port from '%s', value=%ul, error=%s", hostname, val, + strerror(errno)); + return -1; + } + 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); + return -1; + } +#else + WLog_ERR(TAG, "Compiled without AF_VSOCK, '%s' not supported", hostname); + return -1; +#endif + } else { sockfd = -1; @@ -1185,18 +1225,21 @@ int freerdp_tcp_default_connect(rdpContext* context, rdpSettings* settings, cons } } - free(settings->ClientAddress); - settings->ClientAddress = freerdp_tcp_get_ip_address(sockfd, &settings->IPv6Enabled); - - if (!settings->ClientAddress) + if (!vsock) { - if (!useExternalDefinedSocket) - close(sockfd); + free(settings->ClientAddress); + settings->ClientAddress = freerdp_tcp_get_ip_address(sockfd, &settings->IPv6Enabled); - freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED); + if (!settings->ClientAddress) + { + if (!useExternalDefinedSocket) + close(sockfd); - WLog_ERR(TAG, "Couldn't get socket ip address"); - return -1; + freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED); + + WLog_ERR(TAG, "Couldn't get socket ip address"); + return -1; + } } optval = 1; diff --git a/libfreerdp/core/utils.c b/libfreerdp/core/utils.c index 72e532f2b..4a71d83a9 100644 --- a/libfreerdp/core/utils.c +++ b/libfreerdp/core/utils.c @@ -288,3 +288,14 @@ BOOL utils_abort_event_is_set(rdpRdp* rdp) status = WaitForSingleObject(rdp->abortEvent, 0); return status == WAIT_OBJECT_0; } + +const char* utils_is_vsock(const char* hostname) +{ + if (!hostname) + return NULL; + + const char vsock[8] = "vsock://"; + if (strncmp(hostname, vsock, sizeof(vsock)) == 0) + return &hostname[sizeof(vsock)]; + return NULL; +} diff --git a/libfreerdp/core/utils.h b/libfreerdp/core/utils.h index 2d9123de1..87fa2727b 100644 --- a/libfreerdp/core/utils.h +++ b/libfreerdp/core/utils.h @@ -45,4 +45,6 @@ BOOL utils_sync_credentials(rdpSettings* settings, BOOL toGateway); BOOL utils_str_is_empty(const char* str); BOOL utils_str_copy(const char* value, char** dst); +const char* utils_is_vsock(const char* hostname); + #endif /* FREERDP_LIB_CORE_UTILS_H */