From cfbb8ebcbd7e82bc380c814f92cdf91e7d130eb2 Mon Sep 17 00:00:00 2001 From: Mike Gabriel Date: Tue, 16 Aug 2022 23:18:01 +0200 Subject: [PATCH] New upstream version 2.8.0+dfsg1 --- CMakeLists.txt | 18 +- ChangeLog | 36 +- channels/ainput/common/ainput_common.h | 44 +- channels/ainput/server/ainput_main.c | 59 +- channels/audin/client/mac/audin_mac.m | 54 +- .../audin/client/opensles/audin_opensl_es.c | 2 +- channels/audin/server/audin.c | 14 +- channels/cliprdr/client/cliprdr_format.c | 195 ---- channels/disp/server/disp_main.c | 12 + channels/echo/server/echo_main.c | 14 + channels/rail/client/rail_orders.c | 192 +++- channels/rail/rail_common.c | 91 +- channels/rail/rail_common.h | 1 + channels/rail/server/rail_main.c | 10 +- channels/rdpdr/server/rdpdr_main.c | 2 +- channels/rdpecam/CMakeLists.txt | 22 + channels/rdpecam/ChannelOptions.cmake | 12 + channels/rdpecam/server/CMakeLists.txt | 27 + .../server/camera_device_enumerator_main.c | 612 +++++++++++ channels/rdpecam/server/camera_device_main.c | 971 ++++++++++++++++++ channels/rdpei/server/rdpei_main.c | 11 + channels/rdpgfx/client/rdpgfx_main.c | 34 +- channels/rdpgfx/server/rdpgfx_main.c | 11 + channels/rdpsnd/client/alsa/rdpsnd_alsa.c | 4 - channels/rdpsnd/client/mac/rdpsnd_mac.m | 319 +++--- channels/rdpsnd/client/oss/rdpsnd_oss.c | 4 - channels/rdpsnd/client/pulse/rdpsnd_pulse.c | 10 +- channels/rdpsnd/client/rdpsnd_main.c | 19 +- channels/rdpsnd/server/rdpsnd_main.c | 483 ++++++--- channels/server/channels.c | 21 + channels/telemetry/CMakeLists.txt | 22 + channels/telemetry/ChannelOptions.cmake | 12 + channels/telemetry/server/CMakeLists.txt | 26 + channels/telemetry/server/telemetry_main.c | 443 ++++++++ .../urbdrc/client/libusb/libusb_udevice.c | 4 +- .../urbdrc/client/libusb/libusb_udevman.c | 9 +- client/Wayland/wlfreerdp.c | 2 + client/X11/xf_client.c | 6 +- client/X11/xf_cliprdr.c | 29 +- client/X11/xf_cliprdr.h | 2 +- client/X11/xf_event.c | 124 ++- client/X11/xf_graphics.c | 89 +- client/X11/xf_graphics.h | 2 + client/X11/xf_keyboard.c | 11 +- client/X11/xf_rail.c | 30 +- client/X11/xf_window.c | 10 + client/X11/xf_window.h | 8 + client/common/client.c | 2 +- client/common/cmdline.c | 15 +- client/common/cmdline.h | 3 + config.h.in | 6 + include/freerdp/channels/cliprdr.h | 12 +- include/freerdp/channels/rdpecam.h | 336 ++++++ include/freerdp/channels/rdpgfx.h | 12 +- include/freerdp/channels/telemetry.h | 38 + include/freerdp/channels/wtsvc.h | 10 + include/freerdp/client/rail.h | 4 + include/freerdp/constants.h | 8 +- include/freerdp/peer.h | 3 + include/freerdp/rail.h | 88 +- include/freerdp/server/ainput.h | 8 + include/freerdp/server/audin.h | 7 + include/freerdp/server/disp.h | 7 + include/freerdp/server/echo.h | 7 + include/freerdp/server/rdpecam-enumerator.h | 134 +++ include/freerdp/server/rdpecam.h | 278 +++++ include/freerdp/server/rdpei.h | 5 + include/freerdp/server/rdpgfx.h | 7 + include/freerdp/server/rdpsnd.h | 47 + include/freerdp/server/telemetry.h | 108 ++ include/freerdp/settings.h | 9 +- include/freerdp/utils/cliprdr_utils.h | 48 + libfreerdp/CMakeLists.txt | 17 +- libfreerdp/codec/progressive.c | 2 +- libfreerdp/codec/rfx_neon.c | 4 +- libfreerdp/codec/zgfx.c | 15 +- libfreerdp/common/settings.c | 23 + libfreerdp/common/settings_getters.c | 14 + libfreerdp/common/settings_str.c | 219 ++++ libfreerdp/core/capabilities.c | 31 +- libfreerdp/core/gateway/rdg.c | 17 +- libfreerdp/core/info.c | 23 +- libfreerdp/core/nego.c | 23 + libfreerdp/core/nego.h | 1 + libfreerdp/core/peer.c | 88 ++ libfreerdp/core/server.c | 65 +- libfreerdp/core/server.h | 3 + libfreerdp/core/settings.c | 3 + .../core/test/settings_property_lists.h | 2 + libfreerdp/crypto/opensslcompat.c | 3 +- libfreerdp/crypto/opensslcompat.h | 3 +- libfreerdp/crypto/tls.c | 37 +- libfreerdp/utils/CMakeLists.txt | 1 + libfreerdp/utils/cliprdr_utils.c | 235 +++++ server/shadow/shadow_client.c | 20 +- uwac/include/uwac/uwac.h | 14 +- uwac/libuwac/uwac-window.c | 6 + winpr/CMakeLists.txt | 8 +- winpr/include/winpr/clipboard.h | 2 + winpr/include/winpr/crt.h | 6 + winpr/include/winpr/stream.h | 14 + winpr/include/winpr/string.h | 3 + winpr/include/winpr/thread.h | 2 +- winpr/include/winpr/windows.h | 32 +- winpr/libwinpr/CMakeLists.txt | 16 +- winpr/libwinpr/clipboard/posix.c | 59 +- winpr/libwinpr/clipboard/synthetic.c | 102 +- winpr/libwinpr/crt/string.c | 17 + winpr/libwinpr/crypto/hash.c | 21 +- winpr/libwinpr/file/generic.c | 13 +- winpr/libwinpr/path/shell_ios.m | 2 +- .../test/TestPipeCreateNamedPipeOverlapped.c | 28 +- winpr/libwinpr/smartcard/smartcard.c | 90 +- winpr/libwinpr/sspi/NTLM/ntlm.c | 12 +- winpr/libwinpr/sspicli/sspicli.c | 7 +- .../sysinfo/cpufeatures/cpu-features.c | 1 + winpr/libwinpr/thread/thread.c | 431 +++++--- winpr/libwinpr/thread/thread.h | 58 +- winpr/libwinpr/utils/CMakeLists.txt | 52 +- winpr/libwinpr/utils/corkscrew/debug.c | 262 +++++ winpr/libwinpr/utils/corkscrew/debug.h | 40 + winpr/libwinpr/utils/debug.c | 456 +------- winpr/libwinpr/utils/execinfo/debug.c | 88 ++ winpr/libwinpr/utils/execinfo/debug.h | 40 + winpr/libwinpr/utils/stream.c | 65 ++ winpr/libwinpr/utils/unwind/debug.c | 128 +++ winpr/libwinpr/utils/unwind/debug.h | 41 + winpr/libwinpr/utils/windows/debug.c | 169 +++ winpr/libwinpr/utils/windows/debug.h | 40 + 129 files changed, 6897 insertions(+), 1612 deletions(-) create mode 100644 channels/rdpecam/CMakeLists.txt create mode 100644 channels/rdpecam/ChannelOptions.cmake create mode 100644 channels/rdpecam/server/CMakeLists.txt create mode 100644 channels/rdpecam/server/camera_device_enumerator_main.c create mode 100644 channels/rdpecam/server/camera_device_main.c create mode 100644 channels/telemetry/CMakeLists.txt create mode 100644 channels/telemetry/ChannelOptions.cmake create mode 100644 channels/telemetry/server/CMakeLists.txt create mode 100644 channels/telemetry/server/telemetry_main.c create mode 100644 include/freerdp/channels/rdpecam.h create mode 100644 include/freerdp/channels/telemetry.h create mode 100644 include/freerdp/server/rdpecam-enumerator.h create mode 100644 include/freerdp/server/rdpecam.h create mode 100644 include/freerdp/server/telemetry.h create mode 100644 include/freerdp/utils/cliprdr_utils.h create mode 100644 libfreerdp/utils/cliprdr_utils.c create mode 100644 winpr/libwinpr/utils/corkscrew/debug.c create mode 100644 winpr/libwinpr/utils/corkscrew/debug.h create mode 100644 winpr/libwinpr/utils/execinfo/debug.c create mode 100644 winpr/libwinpr/utils/execinfo/debug.h create mode 100644 winpr/libwinpr/utils/unwind/debug.c create mode 100644 winpr/libwinpr/utils/unwind/debug.h create mode 100644 winpr/libwinpr/utils/windows/debug.c create mode 100644 winpr/libwinpr/utils/windows/debug.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 172832f..b1ad102 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,7 +85,7 @@ if ($ENV{BUILD_NUMBER}) endif() set(WITH_LIBRARY_VERSIONING "ON") -set(RAW_VERSION_STRING "2.7.0") +set(RAW_VERSION_STRING "2.8.0") if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag") file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING) elseif(USE_VERSION_FROM_GIT_TAG) @@ -355,7 +355,7 @@ if(NOT IOS) find_package(Threads REQUIRED) endif() -if(NOT WIN32) +if(NOT WIN32 AND NOT IOS) CHECK_SYMBOL_EXISTS(pthread_mutex_timedlock pthread.h HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL) if (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL) CHECK_LIBRARY_EXISTS(pthread pthread_mutex_timedlock "" HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB) @@ -598,6 +598,11 @@ if(ANDROID) set (WITH_NEON OFF) endif() + if(ANDROID_ABI STREQUAL arm64-v8a) + # https://github.com/android/ndk/issues/910 + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfloat-abi=softfp") + endif() + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") add_definitions(-DNDK_DEBUG=1) @@ -1032,15 +1037,6 @@ add_subdirectory(include) add_subdirectory(libfreerdp) -if (IOS) - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.0") - if (IOS_PLATFORM MATCHES "SIMULATOR") - set(CMAKE_OSX_SYSROOT "iphonesimulator") - else() - set(CMAKE_OSX_SYSROOT "iphoneos") - endif() -endif() - # RdTk include_directories("${CMAKE_SOURCE_DIR}/rdtk/include") include_directories("${CMAKE_BINARY_DIR}/rdtk/include") diff --git a/ChangeLog b/ChangeLog index ad92d8b..c2e7a3f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,38 @@ +# 2022-07-28 Version 2.8.0 + +Noteworthy changes: + +* Backported API to get peer accepted channel option flags +* Backported API to get peer accepted channel names +* Backported Stream_CheckAndLogRequiredLength +* Backported #7954: Add server side handling for [MS-RDPET] +* Backported #8010: Add server side handling for [MS-RDPECAM] +* Backported #8041: Remove ALAW/ULAW codecs from linux backends (unreliable) +* Backported #8051: Relieve CLIPRDR filename restriction when connecting to non-MS Windows servers +* Backported #8048: TLS version control +* Backported #7987: Add a new command line arg to enforce tls1.2 + +Fixed issues: + +* Fixed #7837: Prevent out of bound reads for FFMPEG +* Backported #7859 and #7861: Unwind support for backtrace generation +* Backported #7440: wlfreerdp appid +* Backported #7832: RAIL window restore +* Backported #7833: Refactored WinPR thread locking +* Backported #7893: Mac rdpsnd memory leak fixes +* Backported #7895: Mac audin memory leak fixes +* Backported #7898: Automatic android versioning +* Backported #7916: GFX 10.7 capability support +* Backported #7949: Server RDPSND API improvements +* Backported #7957: Server DVC API improvements +* Backported #7760: Fixed osMinorType values +* Backported #8013: Add missing osMajorType values +* Backported #8076: Fix wrong usage of subband diffing flag (tile artifact fix) + +For a complete and detailed change log since the last release run: +git log 2.7.0..2.8.0 + + # 2022-04-25 Version 2.7.0 Noteworthy changes: @@ -583,4 +618,3 @@ Virtual Channels: * rdpsnd (Sound Redirection) * alsa support * pulse support - diff --git a/channels/ainput/common/ainput_common.h b/channels/ainput/common/ainput_common.h index bc1be04..34442f7 100644 --- a/channels/ainput/common/ainput_common.h +++ b/channels/ainput/common/ainput_common.h @@ -21,59 +21,37 @@ #ifndef FREERDP_INT_AINPUT_COMMON_H #define FREERDP_INT_AINPUT_COMMON_H -#include #include #include -static INLINE void ainput_append(char* buffer, size_t size, const char* what, BOOL separator) -{ - size_t have; - size_t toadd; - - assert(buffer || (size == 0)); - assert(what); - - have = strnlen(buffer, size); - toadd = strlen(what); - if (have > 0) - toadd += 1; - - if (size - have < toadd + 1) - return; - - if (have > 0) - strcat(buffer, separator ? "|" : " "); - strcat(buffer, what); -} - static INLINE const char* ainput_flags_to_string(UINT64 flags, char* buffer, size_t size) { char number[32] = { 0 }; if (flags & AINPUT_FLAGS_HAVE_REL) - ainput_append(buffer, size, "AINPUT_FLAGS_HAVE_REL", TRUE); + winpr_str_append("AINPUT_FLAGS_HAVE_REL", buffer, size, "|"); if (flags & AINPUT_FLAGS_WHEEL) - ainput_append(buffer, size, "AINPUT_FLAGS_WHEEL", TRUE); + winpr_str_append("AINPUT_FLAGS_WHEEL", buffer, size, "|"); if (flags & AINPUT_FLAGS_MOVE) - ainput_append(buffer, size, "AINPUT_FLAGS_MOVE", TRUE); + winpr_str_append("AINPUT_FLAGS_MOVE", buffer, size, "|"); if (flags & AINPUT_FLAGS_DOWN) - ainput_append(buffer, size, "AINPUT_FLAGS_DOWN", TRUE); + winpr_str_append("AINPUT_FLAGS_DOWN", buffer, size, "|"); if (flags & AINPUT_FLAGS_REL) - ainput_append(buffer, size, "AINPUT_FLAGS_REL", TRUE); + winpr_str_append("AINPUT_FLAGS_REL", buffer, size, "|"); if (flags & AINPUT_FLAGS_BUTTON1) - ainput_append(buffer, size, "AINPUT_FLAGS_BUTTON1", TRUE); + winpr_str_append("AINPUT_FLAGS_BUTTON1", buffer, size, "|"); if (flags & AINPUT_FLAGS_BUTTON2) - ainput_append(buffer, size, "AINPUT_FLAGS_BUTTON2", TRUE); + winpr_str_append("AINPUT_FLAGS_BUTTON2", buffer, size, "|"); if (flags & AINPUT_FLAGS_BUTTON3) - ainput_append(buffer, size, "AINPUT_FLAGS_BUTTON3", TRUE); + winpr_str_append("AINPUT_FLAGS_BUTTON3", buffer, size, "|"); if (flags & AINPUT_XFLAGS_BUTTON1) - ainput_append(buffer, size, "AINPUT_XFLAGS_BUTTON1", TRUE); + winpr_str_append("AINPUT_XFLAGS_BUTTON1", buffer, size, "|"); if (flags & AINPUT_XFLAGS_BUTTON2) - ainput_append(buffer, size, "AINPUT_XFLAGS_BUTTON2", TRUE); + winpr_str_append("AINPUT_XFLAGS_BUTTON2", buffer, size, "|"); _snprintf(number, sizeof(number), "[0x%08" PRIx64 "]", flags); - ainput_append(buffer, size, number, FALSE); + winpr_str_append(number, buffer, size, " "); return buffer; } diff --git a/channels/ainput/server/ainput_main.c b/channels/ainput/server/ainput_main.c index bc1737e..943d0fa 100644 --- a/channels/ainput/server/ainput_main.c +++ b/channels/ainput/server/ainput_main.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -121,16 +122,36 @@ static UINT ainput_server_open_channel(ainput_server* ainput) ainput->ainput_channel = WTSVirtualChannelOpenEx(ainput->SessionId, AINPUT_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC); - if (ainput->ainput_channel) - break; - Error = GetLastError(); if (Error == ERROR_NOT_FOUND) + { + WLog_DBG(TAG, "Channel %s not found", AINPUT_DVC_CHANNEL_NAME); break; + } + + if (ainput->ainput_channel) + { + UINT32 channelId; + BOOL status = TRUE; + + channelId = WTSChannelGetIdByHandle(ainput->ainput_channel); + + IFCALLRET(ainput->context.ChannelIdAssigned, status, &ainput->context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + return ERROR_INTERNAL_ERROR; + } + + break; + } if (GetTickCount() - StartTick > 5000) + { + WLog_WARN(TAG, "Timeout opening channel %s", AINPUT_DVC_CHANNEL_NAME); break; + } } return ainput->ainput_channel ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR; @@ -148,7 +169,10 @@ static UINT ainput_server_send_version(ainput_server* ainput) Stream_SetPosition(s, 0); if (!Stream_EnsureCapacity(s, 10)) + { + WLog_WARN(TAG, "[%s] out of memory", AINPUT_DVC_CHANNEL_NAME); return ERROR_OUTOFMEMORY; + } Stream_Write_UINT16(s, MSG_AINPUT_VERSION); Stream_Write_UINT32(s, AINPUT_VERSION_MAJOR); /* Version (4 bytes) */ @@ -175,7 +199,7 @@ static UINT ainput_server_recv_mouse_event(ainput_server* ainput, wStream* s) WINPR_ASSERT(ainput); WINPR_ASSERT(s); - if (Stream_GetRemainingLength(s) < 24) + if (!Stream_CheckAndLogRequiredLength(TAG, s, 24)) return ERROR_NO_DATA; Stream_Read_UINT64(s, time); @@ -237,9 +261,11 @@ static DWORD WINAPI ainput_server_thread_func(LPVOID arg) case WAIT_OBJECT_0 + 1: case WAIT_OBJECT_0: error = ainput_server_context_poll_int(&ainput->context); + break; case WAIT_FAILED: default: + WLog_WARN(TAG, "[%s] Wait for open failed", AINPUT_DVC_CHANNEL_NAME); error = ERROR_INTERNAL_ERROR; break; } @@ -252,9 +278,10 @@ static DWORD WINAPI ainput_server_thread_func(LPVOID arg) case WAIT_OBJECT_0 + 1: case WAIT_OBJECT_0: error = ainput_server_context_poll_int(&ainput->context); - + break; case WAIT_FAILED: default: + WLog_WARN(TAG, "[%s] Wait for version failed", AINPUT_DVC_CHANNEL_NAME); error = ERROR_INTERNAL_ERROR; break; } @@ -408,7 +435,7 @@ static UINT ainput_process_message(ainput_server* ainput) { BOOL rc; UINT error = ERROR_INTERNAL_ERROR; - ULONG BytesReturned; + ULONG BytesReturned, ActualBytesReturned; UINT16 MessageId; wStream* s; @@ -437,13 +464,20 @@ static UINT ainput_process_message(ainput_server* ainput) } if (WTSVirtualChannelRead(ainput->ainput_channel, 0, (PCHAR)Stream_Buffer(s), - (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE) + (ULONG)Stream_Capacity(s), &ActualBytesReturned) == FALSE) { WLog_ERR(TAG, "WTSVirtualChannelRead failed!"); goto out; } - Stream_SetLength(s, BytesReturned); + if (BytesReturned != ActualBytesReturned) + { + WLog_ERR(TAG, "WTSVirtualChannelRead size mismatch %" PRId32 ", expected %" PRId32, + ActualBytesReturned, BytesReturned); + goto out; + } + + Stream_SetLength(s, ActualBytesReturned); Stream_Read_UINT16(s, MessageId); switch (MessageId) @@ -471,9 +505,15 @@ BOOL ainput_server_context_handle(ainput_server_context* context, HANDLE* handle WINPR_ASSERT(handle); if (!ainput->externalThread) + { + WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME); return FALSE; + } if (ainput->state == AINPUT_INITIAL) + { + WLog_WARN(TAG, "[%s] state fail!", AINPUT_DVC_CHANNEL_NAME); return FALSE; + } *handle = ainput_server_get_channel_handle(ainput); return TRUE; } @@ -539,6 +579,9 @@ UINT ainput_server_context_poll(ainput_server_context* context) WINPR_ASSERT(ainput); if (!ainput->externalThread) + { + WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME); return ERROR_INTERNAL_ERROR; + } return ainput_server_context_poll_int(context); } diff --git a/channels/audin/client/mac/audin_mac.m b/channels/audin/client/mac/audin_mac.m index ecf3c8c..56b7ea9 100644 --- a/channels/audin/client/mac/audin_mac.m +++ b/channels/audin/client/mac/audin_mac.m @@ -423,32 +423,34 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn #if defined(MAC_OS_X_VERSION_10_14) if (@available(macOS 10.14, *)) { - AVAuthorizationStatus status = - [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]; - switch (status) - { - case AVAuthorizationStatusAuthorized: - mac->isAuthorized = TRUE; - break; - case AVAuthorizationStatusNotDetermined: - [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio - completionHandler:^(BOOL granted) { - if (granted == YES) - { - mac->isAuthorized = TRUE; - } - else - WLog_WARN(TAG, "Microphone access denied by user"); - }]; - break; - case AVAuthorizationStatusRestricted: - WLog_WARN(TAG, "Microphone access restricted by policy"); - break; - case AVAuthorizationStatusDenied: - WLog_WARN(TAG, "Microphone access denied by policy"); - break; - default: - break; + @autoreleasepool { + AVAuthorizationStatus status = + [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]; + switch (status) + { + case AVAuthorizationStatusAuthorized: + mac->isAuthorized = TRUE; + break; + case AVAuthorizationStatusNotDetermined: + [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio + completionHandler:^(BOOL granted) { + if (granted == YES) + { + mac->isAuthorized = TRUE; + } + else + WLog_WARN(TAG, "Microphone access denied by user"); + }]; + break; + case AVAuthorizationStatusRestricted: + WLog_WARN(TAG, "Microphone access restricted by policy"); + break; + case AVAuthorizationStatusDenied: + WLog_WARN(TAG, "Microphone access denied by policy"); + break; + default: + break; + } } } #endif diff --git a/channels/audin/client/opensles/audin_opensl_es.c b/channels/audin/client/opensles/audin_opensl_es.c index 87393ad..a3b7c3c 100644 --- a/channels/audin/client/opensles/audin_opensl_es.c +++ b/channels/audin/client/opensles/audin_opensl_es.c @@ -215,7 +215,7 @@ static UINT audin_opensles_open(IAudinDevice* device, AudinReceive receive, void opensles->user_data = user_data; return CHANNEL_RC_OK; error_out: - audin_opensles_close(opensles); + audin_opensles_close(device); return ERROR_INTERNAL_ERROR; } diff --git a/channels/audin/server/audin.c b/channels/audin/server/audin.c index e5c51bb..8252236 100644 --- a/channels/audin/server/audin.c +++ b/channels/audin/server/audin.c @@ -162,9 +162,6 @@ static UINT audin_server_send_formats(audin_server* audin, wStream* s) for (i = 0; i < audin->context.num_server_formats; i++) { AUDIO_FORMAT format = audin->context.server_formats[i]; - // TODO: Eliminate this - format.nAvgBytesPerSec = - format.nSamplesPerSec * format.nChannels * format.wBitsPerSample / 8; if (!audio_format_write(s, &format)) { @@ -560,6 +557,8 @@ static BOOL audin_server_open(audin_server_context* context) PULONG pSessionId = NULL; DWORD BytesReturned = 0; audin->SessionId = WTS_CURRENT_SESSION; + UINT32 channelId; + BOOL status = TRUE; if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId, (LPSTR*)&pSessionId, &BytesReturned)) @@ -577,6 +576,15 @@ static BOOL audin_server_open(audin_server_context* context) return FALSE; } + channelId = WTSChannelGetIdByHandle(audin->audin_channel); + + IFCALLRET(context->ChannelIdAssigned, status, context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + return ERROR_INTERNAL_ERROR; + } + if (!(audin->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) { WLog_ERR(TAG, "CreateEvent failed!"); diff --git a/channels/cliprdr/client/cliprdr_format.c b/channels/cliprdr/client/cliprdr_format.c index 4c31a1b..0b6111b 100644 --- a/channels/cliprdr/client/cliprdr_format.c +++ b/channels/cliprdr/client/cliprdr_format.c @@ -173,198 +173,3 @@ UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UI return error; } - -static UINT64 filetime_to_uint64(FILETIME value) -{ - UINT64 converted = 0; - converted |= (UINT32)value.dwHighDateTime; - converted <<= 32; - converted |= (UINT32)value.dwLowDateTime; - return converted; -} - -static FILETIME uint64_to_filetime(UINT64 value) -{ - FILETIME converted; - converted.dwLowDateTime = (UINT32)(value >> 0); - converted.dwHighDateTime = (UINT32)(value >> 32); - return converted; -} - -#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520) - -/** - * Parse a packed file list. - * - * The resulting array must be freed with the `free()` function. - * - * @param [in] format_data packed `CLIPRDR_FILELIST` to parse. - * @param [in] format_data_length length of `format_data` in bytes. - * @param [out] file_descriptor_array parsed array of `FILEDESCRIPTOR` structs. - * @param [out] file_descriptor_count number of elements in `file_descriptor_array`. - * - * @returns 0 on success, otherwise a Win32 error code. - */ -UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length, - FILEDESCRIPTORW** file_descriptor_array, UINT32* file_descriptor_count) -{ - UINT result = NO_ERROR; - UINT32 i; - UINT32 count = 0; - wStream* s = NULL; - - if (!format_data || !file_descriptor_array || !file_descriptor_count) - return ERROR_BAD_ARGUMENTS; - - s = Stream_New((BYTE*)format_data, format_data_length); - if (!s) - return ERROR_NOT_ENOUGH_MEMORY; - - if (Stream_GetRemainingLength(s) < 4) - { - WLog_ERR(TAG, "invalid packed file list"); - - result = ERROR_INCORRECT_SIZE; - goto out; - } - - Stream_Read_UINT32(s, count); /* cItems (4 bytes) */ - - if (Stream_GetRemainingLength(s) / CLIPRDR_FILEDESCRIPTOR_SIZE < count) - { - WLog_ERR(TAG, "packed file list is too short: expected %" PRIuz ", have %" PRIuz, - ((size_t)count) * CLIPRDR_FILEDESCRIPTOR_SIZE, Stream_GetRemainingLength(s)); - - result = ERROR_INCORRECT_SIZE; - goto out; - } - - *file_descriptor_count = count; - *file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTORW)); - if (!*file_descriptor_array) - { - result = ERROR_NOT_ENOUGH_MEMORY; - goto out; - } - - for (i = 0; i < count; i++) - { - int c; - UINT64 lastWriteTime; - FILEDESCRIPTORW* file = &((*file_descriptor_array)[i]); - - Stream_Read_UINT32(s, file->dwFlags); /* flags (4 bytes) */ - Stream_Seek(s, 32); /* reserved1 (32 bytes) */ - Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */ - Stream_Seek(s, 16); /* reserved2 (16 bytes) */ - Stream_Read_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */ - file->ftLastWriteTime = uint64_to_filetime(lastWriteTime); - Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */ - Stream_Read_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */ - for (c = 0; c < 260; c++) /* cFileName (520 bytes) */ - Stream_Read_UINT16(s, file->cFileName[c]); - } - - if (Stream_GetRemainingLength(s) > 0) - WLog_WARN(TAG, "packed file list has %" PRIuz " excess bytes", - Stream_GetRemainingLength(s)); -out: - Stream_Free(s, FALSE); - - return result; -} - -#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024) - -/** - * Serialize a packed file list. - * - * The resulting format data must be freed with the `free()` function. - * - * @param [in] file_descriptor_array array of `FILEDESCRIPTOR` structs to serialize. - * @param [in] file_descriptor_count number of elements in `file_descriptor_array`. - * @param [out] format_data serialized CLIPRDR_FILELIST. - * @param [out] format_data_length length of `format_data` in bytes. - * - * @returns 0 on success, otherwise a Win32 error code. - */ -UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array, - UINT32 file_descriptor_count, BYTE** format_data, - UINT32* format_data_length) -{ - return cliprdr_serialize_file_list_ex(CB_STREAM_FILECLIP_ENABLED, file_descriptor_array, - file_descriptor_count, format_data, format_data_length); -} - -UINT cliprdr_serialize_file_list_ex(UINT32 flags, const FILEDESCRIPTORW* file_descriptor_array, - UINT32 file_descriptor_count, BYTE** format_data, - UINT32* format_data_length) -{ - UINT result = NO_ERROR; - UINT32 i; - wStream* s = NULL; - - if (!file_descriptor_array || !format_data || !format_data_length) - return ERROR_BAD_ARGUMENTS; - - if ((flags & CB_STREAM_FILECLIP_ENABLED) == 0) - { - WLog_WARN(TAG, "No file clipboard support annouonced!"); - return ERROR_BAD_ARGUMENTS; - } - - s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE); - if (!s) - return ERROR_NOT_ENOUGH_MEMORY; - - Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */ - - for (i = 0; i < file_descriptor_count; i++) - { - int c; - UINT64 lastWriteTime; - const FILEDESCRIPTORW* file = &file_descriptor_array[i]; - - /* - * There is a known issue with Windows server getting stuck in - * an infinite loop when downloading files that are larger than - * 2 gigabytes. Do not allow clients to send such file lists. - * - * https://support.microsoft.com/en-us/help/2258090 - */ - if ((flags & CB_HUGE_FILE_SUPPORT_ENABLED) == 0) - { - if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE)) - { - WLog_ERR(TAG, "cliprdr does not support files over 2 GB"); - result = ERROR_FILE_TOO_LARGE; - goto error; - } - } - - Stream_Write_UINT32(s, file->dwFlags); /* flags (4 bytes) */ - Stream_Zero(s, 32); /* reserved1 (32 bytes) */ - Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */ - Stream_Zero(s, 16); /* reserved2 (16 bytes) */ - lastWriteTime = filetime_to_uint64(file->ftLastWriteTime); - Stream_Write_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */ - Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */ - Stream_Write_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */ - for (c = 0; c < 260; c++) /* cFileName (520 bytes) */ - Stream_Write_UINT16(s, file->cFileName[c]); - } - - Stream_SealLength(s); - - Stream_GetBuffer(s, *format_data); - Stream_GetLength(s, *format_data_length); - - Stream_Free(s, FALSE); - - return result; - -error: - Stream_Free(s, TRUE); - - return result; -} diff --git a/channels/disp/server/disp_main.c b/channels/disp/server/disp_main.c index 64e96c3..9caec45 100644 --- a/channels/disp/server/disp_main.c +++ b/channels/disp/server/disp_main.c @@ -378,6 +378,8 @@ static UINT disp_server_open(DispServerContext* context) void* buffer; buffer = NULL; priv->SessionId = WTS_CURRENT_SESSION; + UINT32 channelId; + BOOL status = TRUE; if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId, (LPSTR*)&pSessionId, &BytesReturned) == FALSE) @@ -399,6 +401,16 @@ static UINT disp_server_open(DispServerContext* context) goto out_close; } + channelId = WTSChannelGetIdByHandle(priv->disp_channel); + + IFCALLRET(context->ChannelIdAssigned, status, context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + rc = ERROR_INTERNAL_ERROR; + goto out_close; + } + /* Query for channel event handle */ if (!WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualEventHandle, &buffer, &BytesReturned) || diff --git a/channels/echo/server/echo_main.c b/channels/echo/server/echo_main.c index 55e2fa2..1da2894 100644 --- a/channels/echo/server/echo_main.c +++ b/channels/echo/server/echo_main.c @@ -91,7 +91,21 @@ static UINT echo_server_open_channel(echo_server* echo) WTSVirtualChannelOpenEx(echo->SessionId, "ECHO", WTS_CHANNEL_OPTION_DYNAMIC); if (echo->echo_channel) + { + UINT32 channelId; + BOOL status = TRUE; + + channelId = WTSChannelGetIdByHandle(echo->echo_channel); + + IFCALLRET(echo->context.ChannelIdAssigned, status, &echo->context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + return ERROR_INTERNAL_ERROR; + } + break; + } Error = GetLastError(); diff --git a/channels/rail/client/rail_orders.c b/channels/rail/client/rail_orders.c index c394a59..4070b0f 100644 --- a/channels/rail/client/rail_orders.c +++ b/channels/rail/client/rail_orders.c @@ -32,6 +32,8 @@ #include "rail_orders.h" +static BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureMask); + /** * Function description * @@ -39,6 +41,7 @@ */ UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType) { + char buffer[128] = { 0 }; UINT16 orderLength; if (!rail || !s) @@ -49,7 +52,7 @@ UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType) rail_write_pdu_header(s, orderType, orderLength); Stream_SetPosition(s, orderLength); WLog_Print(rail->log, WLOG_DEBUG, "Sending %s PDU, length: %" PRIu16 "", - rail_get_order_type_string(orderType), orderLength); + rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength); return rail_send_channel_data(rail, s); } @@ -388,7 +391,46 @@ static UINT rail_recv_handshake_order(railPlugin* rail, wStream* s) return error; } -static BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureMask) +static UINT rail_read_compartment_info_order(wStream* s, + RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo) +{ + if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_COMPARTMENT_INFO_ORDER_LENGTH)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, compartmentInfo->ImeState); /* ImeState (4 bytes) */ + Stream_Read_UINT32(s, compartmentInfo->ImeConvMode); /* ImeConvMode (4 bytes) */ + Stream_Read_UINT32(s, compartmentInfo->ImeSentenceMode); /* ImeSentenceMode (4 bytes) */ + Stream_Read_UINT32(s, compartmentInfo->KanaMode); /* KANAMode (4 bytes) */ + return CHANNEL_RC_OK; +} + +static UINT rail_recv_compartmentinfo_order(railPlugin* rail, wStream* s) +{ + RailClientContext* context = rail_get_client_interface(rail); + RAIL_COMPARTMENT_INFO_ORDER pdu = { 0 }; + UINT error; + + if (!context || !s) + return ERROR_INVALID_PARAMETER; + + if (!rail_is_feature_supported(rail->rdpcontext, RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED)) + return ERROR_BAD_CONFIGURATION; + + if ((error = rail_read_compartment_info_order(s, &pdu))) + return error; + + if (context->custom) + { + IFCALLRET(context->ClientCompartmentInfo, error, context, &pdu); + + if (error) + WLog_ERR(TAG, "context.ClientCompartmentInfo failed with error %" PRIu32 "", error); + } + + return error; +} + +BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureMask) { UINT32 supported, masked; @@ -400,7 +442,15 @@ static BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureM masked = (supported & featureMask); if (masked != featureMask) + { + char mask[256] = { 0 }; + char actual[256] = { 0 }; + + WLog_WARN(TAG, "[%s] have %s, require %s", __func__, + freerdp_rail_support_flags_to_string(supported, actual, sizeof(actual)), + freerdp_rail_support_flags_to_string(featureMask, mask, sizeof(mask))); return FALSE; + } return TRUE; } @@ -904,6 +954,73 @@ static UINT rail_recv_get_application_id_extended_response_order(railPlugin* rai return error; } +static UINT rail_read_textscaleinfo_order(wStream* s, UINT32* pTextScaleFactor) +{ + WINPR_ASSERT(pTextScaleFactor); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, *pTextScaleFactor); + return CHANNEL_RC_OK; +} + +static UINT rail_recv_textscaleinfo_order(railPlugin* rail, wStream* s) +{ + RailClientContext* context = rail_get_client_interface(rail); + UINT32 TextScaleFactor = 0; + UINT error; + + if (!context) + return ERROR_INVALID_PARAMETER; + + if ((error = rail_read_textscaleinfo_order(s, &TextScaleFactor))) + return error; + + if (context->custom) + { + IFCALLRET(context->ClientTextScale, error, context, TextScaleFactor); + + if (error) + WLog_ERR(TAG, "context.ClientTextScale failed with error %" PRIu32 "", error); + } + + return error; +} + +static UINT rail_read_caretblinkinfo_order(wStream* s, UINT32* pCaretBlinkRate) +{ + WINPR_ASSERT(pCaretBlinkRate); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, *pCaretBlinkRate); + return CHANNEL_RC_OK; +} + +static UINT rail_recv_caretblinkinfo_order(railPlugin* rail, wStream* s) +{ + RailClientContext* context = rail_get_client_interface(rail); + UINT32 CaretBlinkRate = 0; + UINT error; + + if (!context) + return ERROR_INVALID_PARAMETER; + if ((error = rail_read_caretblinkinfo_order(s, &CaretBlinkRate))) + return error; + + if (context->custom) + { + IFCALLRET(context->ClientCaretBlinkRate, error, context, CaretBlinkRate); + + if (error) + WLog_ERR(TAG, "context.ClientCaretBlinkRate failed with error %" PRIu32 "", error); + } + + return error; +} + /** * Function description * @@ -911,9 +1028,10 @@ static UINT rail_recv_get_application_id_extended_response_order(railPlugin* rai */ UINT rail_order_recv(railPlugin* rail, wStream* s) { + char buffer[128] = { 0 }; UINT16 orderType; UINT16 orderLength; - UINT error; + UINT error = CHANNEL_RC_OK; if (!rail || !s) return ERROR_INVALID_PARAMETER; @@ -925,55 +1043,88 @@ UINT rail_order_recv(railPlugin* rail, wStream* s) } WLog_Print(rail->log, WLOG_DEBUG, "Received %s PDU, length:%" PRIu16 "", - rail_get_order_type_string(orderType), orderLength); + rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength); switch (orderType) { case TS_RAIL_ORDER_HANDSHAKE: - return rail_recv_handshake_order(rail, s); + error = rail_recv_handshake_order(rail, s); + break; + + case TS_RAIL_ORDER_COMPARTMENTINFO: + error = rail_recv_compartmentinfo_order(rail, s); + break; case TS_RAIL_ORDER_HANDSHAKE_EX: - return rail_recv_handshake_ex_order(rail, s); + error = rail_recv_handshake_ex_order(rail, s); + break; case TS_RAIL_ORDER_EXEC_RESULT: - return rail_recv_exec_result_order(rail, s); + error = rail_recv_exec_result_order(rail, s); + break; case TS_RAIL_ORDER_SYSPARAM: - return rail_recv_server_sysparam_order(rail, s); + error = rail_recv_server_sysparam_order(rail, s); + break; case TS_RAIL_ORDER_MINMAXINFO: - return rail_recv_server_minmaxinfo_order(rail, s); + error = rail_recv_server_minmaxinfo_order(rail, s); + break; case TS_RAIL_ORDER_LOCALMOVESIZE: - return rail_recv_server_localmovesize_order(rail, s); + error = rail_recv_server_localmovesize_order(rail, s); + break; case TS_RAIL_ORDER_GET_APPID_RESP: - return rail_recv_server_get_appid_resp_order(rail, s); + error = rail_recv_server_get_appid_resp_order(rail, s); + break; case TS_RAIL_ORDER_LANGBARINFO: - return rail_recv_langbar_info_order(rail, s); + error = rail_recv_langbar_info_order(rail, s); + break; case TS_RAIL_ORDER_TASKBARINFO: - return rail_recv_taskbar_info_order(rail, s); + error = rail_recv_taskbar_info_order(rail, s); + break; case TS_RAIL_ORDER_ZORDER_SYNC: - return rail_recv_zorder_sync_order(rail, s); + error = rail_recv_zorder_sync_order(rail, s); + break; case TS_RAIL_ORDER_CLOAK: - return rail_recv_cloak_order(rail, s); + error = rail_recv_cloak_order(rail, s); + break; case TS_RAIL_ORDER_POWER_DISPLAY_REQUEST: - return rail_recv_power_display_request_order(rail, s); + error = rail_recv_power_display_request_order(rail, s); + break; case TS_RAIL_ORDER_GET_APPID_RESP_EX: - return rail_recv_get_application_id_extended_response_order(rail, s); + error = rail_recv_get_application_id_extended_response_order(rail, s); + break; + + case TS_RAIL_ORDER_TEXTSCALEINFO: + error = rail_recv_textscaleinfo_order(rail, s); + break; + + case TS_RAIL_ORDER_CARETBLINKINFO: + error = rail_recv_caretblinkinfo_order(rail, s); + break; default: - WLog_ERR(TAG, "Unknown RAIL PDU order reveived."); + WLog_ERR(TAG, "Unknown RAIL PDU %s received.", + rail_get_order_type_string_full(orderType, buffer, sizeof(buffer))); return ERROR_INVALID_DATA; } - return CHANNEL_RC_OK; + if (error != CHANNEL_RC_OK) + { + char buffer[128] = { 0 }; + WLog_Print(rail->log, WLOG_ERROR, "Failed to process rail %s PDU, length:%" PRIu16 "", + rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength); + } + + return error; } /** @@ -1358,6 +1509,9 @@ UINT rail_send_client_compartment_info_order(railPlugin* rail, if (!rail || !compartmentInfo) return ERROR_INVALID_PARAMETER; + if (!rail_is_feature_supported(rail->rdpcontext, RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED)) + return ERROR_BAD_CONFIGURATION; + s = rail_pdu_init(RAIL_COMPARTMENT_INFO_ORDER_LENGTH); if (!s) diff --git a/channels/rail/rail_common.c b/channels/rail/rail_common.c index b6d69b7..e77616d 100644 --- a/channels/rail/rail_common.c +++ b/channels/rail/rail_common.c @@ -27,37 +27,72 @@ #define TAG CHANNELS_TAG("rail.common") -static const char* const RAIL_ORDER_TYPE_STRINGS[] = { "", - "Execute", - "Activate", - "System Parameters Update", - "System Command", - "Handshake", - "Notify Event", - "", - "Window Move", - "Local Move/Size", - "Min Max Info", - "Client Status", - "System Menu", - "Language Bar Info", - "Get Application ID Request", - "Get Application ID Response", - "Execute Result", - "", - "", - "", - "", - "", - "" }; - const char* rail_get_order_type_string(UINT16 orderType) { - UINT32 index = ((orderType & 0xF0) >> 3) + (orderType & 0x0F); - if (index >= ARRAYSIZE(RAIL_ORDER_TYPE_STRINGS)) - return "UNKNOWN"; + switch (orderType) + { + case TS_RAIL_ORDER_EXEC: + return "TS_RAIL_ORDER_EXEC"; + case TS_RAIL_ORDER_ACTIVATE: + return "TS_RAIL_ORDER_ACTIVATE"; + case TS_RAIL_ORDER_SYSPARAM: + return "TS_RAIL_ORDER_SYSPARAM"; + case TS_RAIL_ORDER_SYSCOMMAND: + return "TS_RAIL_ORDER_SYSCOMMAND"; + case TS_RAIL_ORDER_HANDSHAKE: + return "TS_RAIL_ORDER_HANDSHAKE"; + case TS_RAIL_ORDER_NOTIFY_EVENT: + return "TS_RAIL_ORDER_NOTIFY_EVENT"; + case TS_RAIL_ORDER_WINDOWMOVE: + return "TS_RAIL_ORDER_WINDOWMOVE"; + case TS_RAIL_ORDER_LOCALMOVESIZE: + return "TS_RAIL_ORDER_LOCALMOVESIZE"; + case TS_RAIL_ORDER_MINMAXINFO: + return "TS_RAIL_ORDER_MINMAXINFO"; + case TS_RAIL_ORDER_CLIENTSTATUS: + return "TS_RAIL_ORDER_CLIENTSTATUS"; + case TS_RAIL_ORDER_SYSMENU: + return "TS_RAIL_ORDER_SYSMENU"; + case TS_RAIL_ORDER_LANGBARINFO: + return "TS_RAIL_ORDER_LANGBARINFO"; + case TS_RAIL_ORDER_GET_APPID_REQ: + return "TS_RAIL_ORDER_GET_APPID_REQ"; + case TS_RAIL_ORDER_GET_APPID_RESP: + return "TS_RAIL_ORDER_GET_APPID_RESP"; + case TS_RAIL_ORDER_TASKBARINFO: + return "TS_RAIL_ORDER_TASKBARINFO"; + case TS_RAIL_ORDER_LANGUAGEIMEINFO: + return "TS_RAIL_ORDER_LANGUAGEIMEINFO"; + case TS_RAIL_ORDER_COMPARTMENTINFO: + return "TS_RAIL_ORDER_COMPARTMENTINFO"; + case TS_RAIL_ORDER_HANDSHAKE_EX: + return "TS_RAIL_ORDER_HANDSHAKE_EX"; + case TS_RAIL_ORDER_ZORDER_SYNC: + return "TS_RAIL_ORDER_ZORDER_SYNC"; + case TS_RAIL_ORDER_CLOAK: + return "TS_RAIL_ORDER_CLOAK"; + case TS_RAIL_ORDER_POWER_DISPLAY_REQUEST: + return "TS_RAIL_ORDER_POWER_DISPLAY_REQUEST"; + case TS_RAIL_ORDER_SNAP_ARRANGE: + return "TS_RAIL_ORDER_SNAP_ARRANGE"; + case TS_RAIL_ORDER_GET_APPID_RESP_EX: + return "TS_RAIL_ORDER_GET_APPID_RESP_EX"; + case TS_RAIL_ORDER_EXEC_RESULT: + return "TS_RAIL_ORDER_EXEC_RESULT"; + case TS_RAIL_ORDER_TEXTSCALEINFO: + return "TS_RAIL_ORDER_TEXTSCALEINFO"; + case TS_RAIL_ORDER_CARETBLINKINFO: + return "TS_RAIL_ORDER_CARETBLINKINFO"; + default: + return "TS_RAIL_ORDER_UNKNOWN"; + } +} - return RAIL_ORDER_TYPE_STRINGS[index]; +const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length) +{ + _snprintf(buffer, length, "%s[0x%04" PRIx16 "]", rail_get_order_type_string(orderType), + orderType); + return buffer; } /** diff --git a/channels/rail/rail_common.h b/channels/rail/rail_common.h index e13412d..34b6fa0 100644 --- a/channels/rail/rail_common.h +++ b/channels/rail/rail_common.h @@ -71,5 +71,6 @@ UINT rail_write_sysparam_order(wStream* s, const RAIL_SYSPARAM_ORDER* sysparam, BOOL extendedSpiSupported); BOOL rail_is_extended_spi_supported(UINT32 channelsFlags); const char* rail_get_order_type_string(UINT16 orderType); +const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length); #endif /* FREERDP_CHANNEL_RAIL_COMMON_H */ diff --git a/channels/rail/server/rail_main.c b/channels/rail/server/rail_main.c index db38928..4949fb7 100644 --- a/channels/rail/server/rail_main.c +++ b/channels/rail/server/rail_main.c @@ -62,6 +62,7 @@ static UINT rail_send(RailServerContext* context, wStream* s, ULONG length) */ static UINT rail_server_send_pdu(RailServerContext* context, wStream* s, UINT16 orderType) { + char buffer[128] = { 0 }; UINT16 orderLength; if (!context || !s) @@ -71,8 +72,8 @@ static UINT rail_server_send_pdu(RailServerContext* context, wStream* s, UINT16 Stream_SetPosition(s, 0); rail_write_pdu_header(s, orderType, orderLength); Stream_SetPosition(s, orderLength); - WLog_DBG(TAG, "Sending %s PDU, length: %" PRIu16 "", rail_get_order_type_string(orderType), - orderLength); + WLog_DBG(TAG, "Sending %s PDU, length: %" PRIu16 "", + rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength); return rail_send(context, s, orderLength); } @@ -1535,6 +1536,7 @@ void rail_server_set_handshake_ex_flags(RailServerContext* context, DWORD flags) UINT rail_server_handle_messages(RailServerContext* context) { + char buffer[128] = { 0 }; UINT status = CHANNEL_RC_OK; DWORD bytesReturned; UINT16 orderType; @@ -1584,8 +1586,8 @@ UINT rail_server_handle_messages(RailServerContext* context) return ERROR_INTERNAL_ERROR; } - WLog_DBG(TAG, "Received %s PDU, length:%" PRIu16 "", rail_get_order_type_string(orderType), - orderLength); + WLog_DBG(TAG, "Received %s PDU, length:%" PRIu16 "", + rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength); switch (orderType) { diff --git a/channels/rdpdr/server/rdpdr_main.c b/channels/rdpdr/server/rdpdr_main.c index 3e005da..2dcb2a0 100644 --- a/channels/rdpdr/server/rdpdr_main.c +++ b/channels/rdpdr/server/rdpdr_main.c @@ -1952,7 +1952,7 @@ static UINT rdpdr_server_drive_query_directory_callback1(RdpdrServerContext* con irp->Callback = rdpdr_server_drive_query_directory_callback2; irp->DeviceId = deviceId; irp->FileId = fileId; - strcat(irp->PathName, "\\*.*"); + winpr_str_append("\\*.*", irp->PathName, ARRAYSIZE(irp->PathName), NULL); if (!rdpdr_server_enqueue_irp(context, irp)) { diff --git a/channels/rdpecam/CMakeLists.txt b/channels/rdpecam/CMakeLists.txt new file mode 100644 index 0000000..63ed410 --- /dev/null +++ b/channels/rdpecam/CMakeLists.txt @@ -0,0 +1,22 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2022 Pascal Nowack +# +# 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. + +define_channel("rdpecam") + +if(WITH_SERVER_CHANNELS) + add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME}) +endif() diff --git a/channels/rdpecam/ChannelOptions.cmake b/channels/rdpecam/ChannelOptions.cmake new file mode 100644 index 0000000..7528d11 --- /dev/null +++ b/channels/rdpecam/ChannelOptions.cmake @@ -0,0 +1,12 @@ + +set(OPTION_DEFAULT ON) +set(OPTION_CLIENT_DEFAULT OFF) +set(OPTION_SERVER_DEFAULT ON) + +define_channel_options(NAME "rdpecam" TYPE "dynamic" + DESCRIPTION "Video Capture Virtual Channel Extension" + SPECIFICATIONS "[MS-RDPECAM]" + DEFAULT ${OPTION_DEFAULT}) + +define_channel_server_options(${OPTION_SERVER_DEFAULT}) + diff --git a/channels/rdpecam/server/CMakeLists.txt b/channels/rdpecam/server/CMakeLists.txt new file mode 100644 index 0000000..2a65ba7 --- /dev/null +++ b/channels/rdpecam/server/CMakeLists.txt @@ -0,0 +1,27 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2022 Pascal Nowack +# +# 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. + +define_channel_server("rdpecam") + +set(${MODULE_PREFIX}_SRCS + camera_device_enumerator_main.c + camera_device_main.c) + +add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry") + +target_link_libraries(${MODULE_NAME} freerdp) +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server") diff --git a/channels/rdpecam/server/camera_device_enumerator_main.c b/channels/rdpecam/server/camera_device_enumerator_main.c new file mode 100644 index 0000000..a17eeee --- /dev/null +++ b/channels/rdpecam/server/camera_device_enumerator_main.c @@ -0,0 +1,612 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Video Capture Virtual Channel Extension + * + * Copyright 2022 Pascal Nowack + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#define TAG CHANNELS_TAG("rdpecam-enumerator.server") + +typedef enum +{ + ENUMERATOR_INITIAL, + ENUMERATOR_OPENED, +} eEnumeratorChannelState; + +typedef struct +{ + CamDevEnumServerContext context; + + HANDLE stopEvent; + + HANDLE thread; + void* enumerator_channel; + + DWORD SessionId; + + BOOL isOpened; + BOOL externalThread; + + /* Channel state */ + eEnumeratorChannelState state; + + wStream* buffer; +} enumerator_server; + +static UINT enumerator_server_initialize(CamDevEnumServerContext* context, BOOL externalThread) +{ + UINT error = CHANNEL_RC_OK; + enumerator_server* enumerator = (enumerator_server*)context; + + WINPR_ASSERT(enumerator); + + if (enumerator->isOpened) + { + WLog_WARN(TAG, "Application error: Camera Device Enumerator channel already initialized, " + "calling in this state is not possible!"); + return ERROR_INVALID_STATE; + } + + enumerator->externalThread = externalThread; + + return error; +} + +static UINT enumerator_server_open_channel(enumerator_server* enumerator) +{ + CamDevEnumServerContext* context = &enumerator->context; + DWORD Error = ERROR_SUCCESS; + HANDLE hEvent; + DWORD BytesReturned = 0; + PULONG pSessionId = NULL; + UINT32 channelId; + BOOL status = TRUE; + + WINPR_ASSERT(enumerator); + + if (WTSQuerySessionInformationA(enumerator->context.vcm, WTS_CURRENT_SESSION, WTSSessionId, + (LPSTR*)&pSessionId, &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSQuerySessionInformationA failed!"); + return ERROR_INTERNAL_ERROR; + } + + enumerator->SessionId = (DWORD)*pSessionId; + WTSFreeMemory(pSessionId); + hEvent = WTSVirtualChannelManagerGetEventHandle(enumerator->context.vcm); + + if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED) + { + Error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error); + return Error; + } + + enumerator->enumerator_channel = WTSVirtualChannelOpenEx( + enumerator->SessionId, RDPECAM_CONTROL_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC); + if (!enumerator->enumerator_channel) + { + Error = GetLastError(); + WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error); + return Error; + } + + channelId = WTSChannelGetIdByHandle(enumerator->enumerator_channel); + + IFCALLRET(context->ChannelIdAssigned, status, context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + return ERROR_INTERNAL_ERROR; + } + + return Error; +} + +static UINT enumerator_server_handle_select_version_request(CamDevEnumServerContext* context, + wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_SELECT_VERSION_REQUEST pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + IFCALLRET(context->SelectVersionRequest, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->SelectVersionRequest failed with error %" PRIu32 "", error); + + return error; +} + +static UINT enumerator_server_recv_device_added_notification(CamDevEnumServerContext* context, + wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_DEVICE_ADDED_NOTIFICATION pdu; + UINT error = CHANNEL_RC_OK; + size_t remaining_length; + WCHAR* channel_name_start; + char* tmp; + size_t i; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + /* + * RequiredLength 4: + * + * Nullterminator DeviceName (2), + * VirtualChannelName (>= 1), + * Nullterminator VirtualChannelName (1) + */ + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_NO_DATA; + + pdu.DeviceName = (WCHAR*)Stream_Pointer(s); + + remaining_length = Stream_GetRemainingLength(s); + channel_name_start = (WCHAR*)Stream_Pointer(s); + + /* Search for null terminator of DeviceName */ + for (i = 0; i < remaining_length; i += sizeof(WCHAR), ++channel_name_start) + { + if (*channel_name_start == L'\0') + break; + } + + if (*channel_name_start != L'\0') + { + WLog_ERR(TAG, "enumerator_server_recv_device_added_notification: Invalid DeviceName!"); + return ERROR_INVALID_DATA; + } + + pdu.VirtualChannelName = (char*)++channel_name_start; + ++i; + + if (i >= remaining_length || *pdu.VirtualChannelName == '\0') + { + WLog_ERR(TAG, + "enumerator_server_recv_device_added_notification: Invalid VirtualChannelName!"); + return ERROR_INVALID_DATA; + } + + tmp = pdu.VirtualChannelName; + for (; i < remaining_length; ++i, ++tmp) + { + if (*tmp == '\0') + break; + } + + if (*tmp != '\0') + { + WLog_ERR(TAG, + "enumerator_server_recv_device_added_notification: Invalid VirtualChannelName!"); + return ERROR_INVALID_DATA; + } + + IFCALLRET(context->DeviceAddedNotification, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->DeviceAddedNotification failed with error %" PRIu32 "", error); + + return error; +} + +static UINT enumerator_server_recv_device_removed_notification(CamDevEnumServerContext* context, + wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_DEVICE_REMOVED_NOTIFICATION pdu; + UINT error = CHANNEL_RC_OK; + size_t remaining_length; + char* tmp; + size_t i; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 2)) + return ERROR_NO_DATA; + + pdu.VirtualChannelName = (char*)Stream_Pointer(s); + + remaining_length = Stream_GetRemainingLength(s); + tmp = (char*)(Stream_Pointer(s) + 1); + + for (i = 1; i < remaining_length; ++i, ++tmp) + { + if (*tmp == '\0') + break; + } + + if (*tmp != '\0') + { + WLog_ERR(TAG, + "enumerator_server_recv_device_removed_notification: Invalid VirtualChannelName!"); + return ERROR_INVALID_DATA; + } + + IFCALLRET(context->DeviceRemovedNotification, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->DeviceRemovedNotification failed with error %" PRIu32 "", error); + + return error; +} + +static UINT enumerator_process_message(enumerator_server* enumerator) +{ + BOOL rc; + UINT error = ERROR_INTERNAL_ERROR; + ULONG BytesReturned; + CAM_SHARED_MSG_HEADER header = { 0 }; + wStream* s; + + WINPR_ASSERT(enumerator); + WINPR_ASSERT(enumerator->enumerator_channel); + + s = enumerator->buffer; + WINPR_ASSERT(s); + + Stream_SetPosition(s, 0); + rc = WTSVirtualChannelRead(enumerator->enumerator_channel, 0, NULL, 0, &BytesReturned); + if (!rc) + goto out; + + if (BytesReturned < 1) + { + error = CHANNEL_RC_OK; + goto out; + } + + if (!Stream_EnsureRemainingCapacity(s, BytesReturned)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + error = CHANNEL_RC_NO_MEMORY; + goto out; + } + + if (WTSVirtualChannelRead(enumerator->enumerator_channel, 0, (PCHAR)Stream_Buffer(s), + (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSVirtualChannelRead failed!"); + goto out; + } + + Stream_SetLength(s, BytesReturned); + if (!Stream_CheckAndLogRequiredLength(TAG, s, CAM_HEADER_SIZE)) + return ERROR_NO_DATA; + + Stream_Read_UINT8(s, header.Version); + Stream_Read_UINT8(s, header.MessageId); + + switch (header.MessageId) + { + case CAM_MSG_ID_SelectVersionRequest: + error = + enumerator_server_handle_select_version_request(&enumerator->context, s, &header); + break; + case CAM_MSG_ID_DeviceAddedNotification: + error = + enumerator_server_recv_device_added_notification(&enumerator->context, s, &header); + break; + case CAM_MSG_ID_DeviceRemovedNotification: + error = enumerator_server_recv_device_removed_notification(&enumerator->context, s, + &header); + break; + default: + WLog_ERR(TAG, "enumerator_process_message: unknown or invalid MessageId %" PRIu8 "", + header.MessageId); + break; + } + +out: + if (error) + WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error); + + return error; +} + +static UINT enumerator_server_context_poll_int(CamDevEnumServerContext* context) +{ + enumerator_server* enumerator = (enumerator_server*)context; + UINT error = ERROR_INTERNAL_ERROR; + + WINPR_ASSERT(enumerator); + + switch (enumerator->state) + { + case ENUMERATOR_INITIAL: + error = enumerator_server_open_channel(enumerator); + if (error) + WLog_ERR(TAG, "enumerator_server_open_channel failed with error %" PRIu32 "!", + error); + else + enumerator->state = ENUMERATOR_OPENED; + break; + case ENUMERATOR_OPENED: + error = enumerator_process_message(enumerator); + break; + } + + return error; +} + +static HANDLE enumerator_server_get_channel_handle(enumerator_server* enumerator) +{ + void* buffer = NULL; + DWORD BytesReturned = 0; + HANDLE ChannelEvent = NULL; + + WINPR_ASSERT(enumerator); + + if (WTSVirtualChannelQuery(enumerator->enumerator_channel, WTSVirtualEventHandle, &buffer, + &BytesReturned) == TRUE) + { + if (BytesReturned == sizeof(HANDLE)) + CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE)); + + WTSFreeMemory(buffer); + } + + return ChannelEvent; +} + +static DWORD WINAPI enumerator_server_thread_func(LPVOID arg) +{ + DWORD nCount; + HANDLE events[2] = { 0 }; + enumerator_server* enumerator = (enumerator_server*)arg; + UINT error = CHANNEL_RC_OK; + DWORD status; + + WINPR_ASSERT(enumerator); + + nCount = 0; + events[nCount++] = enumerator->stopEvent; + + while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0)) + { + switch (enumerator->state) + { + case ENUMERATOR_INITIAL: + error = enumerator_server_context_poll_int(&enumerator->context); + if (error == CHANNEL_RC_OK) + { + events[1] = enumerator_server_get_channel_handle(enumerator); + nCount = 2; + } + break; + case ENUMERATOR_OPENED: + status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); + switch (status) + { + case WAIT_OBJECT_0: + break; + case WAIT_OBJECT_0 + 1: + case WAIT_TIMEOUT: + error = enumerator_server_context_poll_int(&enumerator->context); + break; + + case WAIT_FAILED: + default: + error = ERROR_INTERNAL_ERROR; + break; + } + break; + } + } + + WTSVirtualChannelClose(enumerator->enumerator_channel); + enumerator->enumerator_channel = NULL; + + if (error && enumerator->context.rdpcontext) + setChannelError(enumerator->context.rdpcontext, error, + "enumerator_server_thread_func reported an error"); + + ExitThread(error); + return error; +} + +static UINT enumerator_server_open(CamDevEnumServerContext* context) +{ + enumerator_server* enumerator = (enumerator_server*)context; + + WINPR_ASSERT(enumerator); + + if (!enumerator->externalThread && (enumerator->thread == NULL)) + { + enumerator->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!enumerator->stopEvent) + { + WLog_ERR(TAG, "CreateEvent failed!"); + return ERROR_INTERNAL_ERROR; + } + + enumerator->thread = + CreateThread(NULL, 0, enumerator_server_thread_func, enumerator, 0, NULL); + if (!enumerator->thread) + { + WLog_ERR(TAG, "CreateThread failed!"); + CloseHandle(enumerator->stopEvent); + enumerator->stopEvent = NULL; + return ERROR_INTERNAL_ERROR; + } + } + enumerator->isOpened = TRUE; + + return CHANNEL_RC_OK; +} + +static UINT enumerator_server_close(CamDevEnumServerContext* context) +{ + UINT error = CHANNEL_RC_OK; + enumerator_server* enumerator = (enumerator_server*)context; + + WINPR_ASSERT(enumerator); + + if (!enumerator->externalThread && enumerator->thread) + { + SetEvent(enumerator->stopEvent); + + if (WaitForSingleObject(enumerator->thread, INFINITE) == WAIT_FAILED) + { + error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error); + return error; + } + + CloseHandle(enumerator->thread); + CloseHandle(enumerator->stopEvent); + enumerator->thread = NULL; + enumerator->stopEvent = NULL; + } + if (enumerator->externalThread) + { + if (enumerator->state != ENUMERATOR_INITIAL) + { + WTSVirtualChannelClose(enumerator->enumerator_channel); + enumerator->enumerator_channel = NULL; + enumerator->state = ENUMERATOR_INITIAL; + } + } + enumerator->isOpened = FALSE; + + return error; +} + +static UINT enumerator_server_context_poll(CamDevEnumServerContext* context) +{ + enumerator_server* enumerator = (enumerator_server*)context; + + WINPR_ASSERT(enumerator); + + if (!enumerator->externalThread) + return ERROR_INTERNAL_ERROR; + + return enumerator_server_context_poll_int(context); +} + +static BOOL enumerator_server_context_handle(CamDevEnumServerContext* context, HANDLE* handle) +{ + enumerator_server* enumerator = (enumerator_server*)context; + + WINPR_ASSERT(enumerator); + WINPR_ASSERT(handle); + + if (!enumerator->externalThread) + return FALSE; + if (enumerator->state == ENUMERATOR_INITIAL) + return FALSE; + + *handle = enumerator_server_get_channel_handle(enumerator); + + return TRUE; +} + +static UINT enumerator_server_packet_send(CamDevEnumServerContext* context, wStream* s) +{ + enumerator_server* enumerator = (enumerator_server*)context; + UINT error = CHANNEL_RC_OK; + ULONG written; + + if (!WTSVirtualChannelWrite(enumerator->enumerator_channel, (PCHAR)Stream_Buffer(s), + Stream_GetPosition(s), &written)) + { + WLog_ERR(TAG, "WTSVirtualChannelWrite failed!"); + error = ERROR_INTERNAL_ERROR; + goto out; + } + + if (written < Stream_GetPosition(s)) + { + WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written, + Stream_GetPosition(s)); + } + +out: + Stream_Free(s, TRUE); + return error; +} + +static UINT enumerator_send_select_version_response_pdu( + CamDevEnumServerContext* context, const CAM_SELECT_VERSION_RESPONSE* selectVersionResponse) +{ + wStream* s; + + s = Stream_New(NULL, CAM_HEADER_SIZE); + if (!s) + { + WLog_ERR(TAG, "Stream_New failed!"); + return ERROR_NOT_ENOUGH_MEMORY; + } + + Stream_Write_UINT8(s, selectVersionResponse->Header.Version); + Stream_Write_UINT8(s, selectVersionResponse->Header.MessageId); + + return enumerator_server_packet_send(context, s); +} + +CamDevEnumServerContext* cam_dev_enum_server_context_new(HANDLE vcm) +{ + enumerator_server* enumerator = (enumerator_server*)calloc(1, sizeof(enumerator_server)); + + if (!enumerator) + return NULL; + + enumerator->context.vcm = vcm; + enumerator->context.Initialize = enumerator_server_initialize; + enumerator->context.Open = enumerator_server_open; + enumerator->context.Close = enumerator_server_close; + enumerator->context.Poll = enumerator_server_context_poll; + enumerator->context.ChannelHandle = enumerator_server_context_handle; + + enumerator->context.SelectVersionResponse = enumerator_send_select_version_response_pdu; + + enumerator->buffer = Stream_New(NULL, 4096); + if (!enumerator->buffer) + goto fail; + + return &enumerator->context; +fail: + cam_dev_enum_server_context_free(&enumerator->context); + return NULL; +} + +void cam_dev_enum_server_context_free(CamDevEnumServerContext* context) +{ + enumerator_server* enumerator = (enumerator_server*)context; + + if (enumerator) + { + enumerator_server_close(context); + Stream_Free(enumerator->buffer, TRUE); + } + + free(enumerator); +} diff --git a/channels/rdpecam/server/camera_device_main.c b/channels/rdpecam/server/camera_device_main.c new file mode 100644 index 0000000..00bea94 --- /dev/null +++ b/channels/rdpecam/server/camera_device_main.c @@ -0,0 +1,971 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Video Capture Virtual Channel Extension + * + * Copyright 2022 Pascal Nowack + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#define TAG CHANNELS_TAG("rdpecam.server") + +typedef enum +{ + CAMERA_DEVICE_INITIAL, + CAMERA_DEVICE_OPENED, +} eCameraDeviceChannelState; + +typedef struct +{ + CameraDeviceServerContext context; + + HANDLE stopEvent; + + HANDLE thread; + void* device_channel; + + DWORD SessionId; + + BOOL isOpened; + BOOL externalThread; + + /* Channel state */ + eCameraDeviceChannelState state; + + wStream* buffer; +} device_server; + +static UINT device_server_initialize(CameraDeviceServerContext* context, BOOL externalThread) +{ + UINT error = CHANNEL_RC_OK; + device_server* device = (device_server*)context; + + WINPR_ASSERT(device); + + if (device->isOpened) + { + WLog_WARN(TAG, "Application error: Camera channel already initialized, " + "calling in this state is not possible!"); + return ERROR_INVALID_STATE; + } + + device->externalThread = externalThread; + + return error; +} + +static UINT device_server_open_channel(device_server* device) +{ + CameraDeviceServerContext* context = &device->context; + DWORD Error = ERROR_SUCCESS; + HANDLE hEvent; + DWORD BytesReturned = 0; + PULONG pSessionId = NULL; + UINT32 channelId; + BOOL status = TRUE; + + WINPR_ASSERT(device); + + if (WTSQuerySessionInformationA(device->context.vcm, WTS_CURRENT_SESSION, WTSSessionId, + (LPSTR*)&pSessionId, &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSQuerySessionInformationA failed!"); + return ERROR_INTERNAL_ERROR; + } + + device->SessionId = (DWORD)*pSessionId; + WTSFreeMemory(pSessionId); + hEvent = WTSVirtualChannelManagerGetEventHandle(device->context.vcm); + + if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED) + { + Error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error); + return Error; + } + + device->device_channel = WTSVirtualChannelOpenEx(device->SessionId, context->virtualChannelName, + WTS_CHANNEL_OPTION_DYNAMIC); + if (!device->device_channel) + { + Error = GetLastError(); + WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error); + return Error; + } + + channelId = WTSChannelGetIdByHandle(device->device_channel); + + IFCALLRET(context->ChannelIdAssigned, status, context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + return ERROR_INTERNAL_ERROR; + } + + return Error; +} + +static UINT device_server_handle_success_response(CameraDeviceServerContext* context, wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_SUCCESS_RESPONSE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + IFCALLRET(context->SuccessResponse, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->SuccessResponse failed with error %" PRIu32 "", error); + + return error; +} + +static UINT device_server_recv_error_response(CameraDeviceServerContext* context, wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_ERROR_RESPONSE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_NO_DATA; + + Stream_Read_UINT32(s, pdu.ErrorCode); + + IFCALLRET(context->ErrorResponse, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->ErrorResponse failed with error %" PRIu32 "", error); + + return error; +} + +static UINT device_server_recv_stream_list_response(CameraDeviceServerContext* context, wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_STREAM_LIST_RESPONSE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + BYTE i; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 5)) + return ERROR_NO_DATA; + + pdu.N_Descriptions = MIN(Stream_GetRemainingLength(s) / 5, 255); + + for (i = 0; i < pdu.N_Descriptions; ++i) + { + CAM_STREAM_DESCRIPTION* StreamDescription = &pdu.StreamDescriptions[i]; + + Stream_Read_UINT16(s, StreamDescription->FrameSourceTypes); + Stream_Read_UINT8(s, StreamDescription->StreamCategory); + Stream_Read_UINT8(s, StreamDescription->Selected); + Stream_Read_UINT8(s, StreamDescription->CanBeShared); + } + + IFCALLRET(context->StreamListResponse, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->StreamListResponse failed with error %" PRIu32 "", error); + + return error; +} + +static UINT device_server_recv_media_type_list_response(CameraDeviceServerContext* context, + wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_MEDIA_TYPE_LIST_RESPONSE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + BYTE i; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 26)) + return ERROR_NO_DATA; + + pdu.N_Descriptions = Stream_GetRemainingLength(s) / 26; + + pdu.MediaTypeDescriptions = calloc(pdu.N_Descriptions, sizeof(CAM_MEDIA_TYPE_DESCRIPTION)); + if (!pdu.MediaTypeDescriptions) + { + WLog_ERR(TAG, "Failed to allocate %zu CAM_MEDIA_TYPE_DESCRIPTION structs", + pdu.N_Descriptions); + return ERROR_NOT_ENOUGH_MEMORY; + } + + for (i = 0; i < pdu.N_Descriptions; ++i) + { + CAM_MEDIA_TYPE_DESCRIPTION* MediaTypeDescriptions = &pdu.MediaTypeDescriptions[i]; + + Stream_Read_UINT8(s, MediaTypeDescriptions->Format); + Stream_Read_UINT32(s, MediaTypeDescriptions->Width); + Stream_Read_UINT32(s, MediaTypeDescriptions->Height); + Stream_Read_UINT32(s, MediaTypeDescriptions->FrameRateNumerator); + Stream_Read_UINT32(s, MediaTypeDescriptions->FrameRateDenominator); + Stream_Read_UINT32(s, MediaTypeDescriptions->PixelAspectRatioNumerator); + Stream_Read_UINT32(s, MediaTypeDescriptions->PixelAspectRatioDenominator); + Stream_Read_UINT8(s, MediaTypeDescriptions->Flags); + } + + IFCALLRET(context->MediaTypeListResponse, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->MediaTypeListResponse failed with error %" PRIu32 "", error); + + free(pdu.MediaTypeDescriptions); + + return error; +} + +static UINT device_server_recv_current_media_type_response(CameraDeviceServerContext* context, + wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_CURRENT_MEDIA_TYPE_RESPONSE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 26)) + return ERROR_NO_DATA; + + Stream_Read_UINT8(s, pdu.MediaTypeDescription.Format); + Stream_Read_UINT32(s, pdu.MediaTypeDescription.Width); + Stream_Read_UINT32(s, pdu.MediaTypeDescription.Height); + Stream_Read_UINT32(s, pdu.MediaTypeDescription.FrameRateNumerator); + Stream_Read_UINT32(s, pdu.MediaTypeDescription.FrameRateDenominator); + Stream_Read_UINT32(s, pdu.MediaTypeDescription.PixelAspectRatioNumerator); + Stream_Read_UINT32(s, pdu.MediaTypeDescription.PixelAspectRatioDenominator); + Stream_Read_UINT8(s, pdu.MediaTypeDescription.Flags); + + IFCALLRET(context->CurrentMediaTypeResponse, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->CurrentMediaTypeResponse failed with error %" PRIu32 "", error); + + return error; +} + +static UINT device_server_recv_sample_response(CameraDeviceServerContext* context, wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_SAMPLE_RESPONSE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 1)) + return ERROR_NO_DATA; + + Stream_Read_UINT8(s, pdu.StreamIndex); + + pdu.SampleSize = Stream_GetRemainingLength(s); + pdu.Sample = Stream_Pointer(s); + + IFCALLRET(context->SampleResponse, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->SampleResponse failed with error %" PRIu32 "", error); + + return error; +} + +static UINT device_server_recv_sample_error_response(CameraDeviceServerContext* context, wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_SAMPLE_ERROR_RESPONSE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 5)) + return ERROR_NO_DATA; + + Stream_Read_UINT8(s, pdu.StreamIndex); + Stream_Read_UINT32(s, pdu.ErrorCode); + + IFCALLRET(context->SampleErrorResponse, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->SampleErrorResponse failed with error %" PRIu32 "", error); + + return error; +} + +static UINT device_server_recv_property_list_response(CameraDeviceServerContext* context, + wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_PROPERTY_LIST_RESPONSE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + pdu.N_Properties = Stream_GetRemainingLength(s) / 19; + + if (pdu.N_Properties > 0) + { + size_t i; + + pdu.Properties = calloc(pdu.N_Properties, sizeof(CAM_PROPERTY_DESCRIPTION)); + if (!pdu.Properties) + { + WLog_ERR(TAG, "Failed to allocate %zu CAM_PROPERTY_DESCRIPTION structs", + pdu.N_Properties); + return ERROR_NOT_ENOUGH_MEMORY; + } + + for (i = 0; i < pdu.N_Properties; ++i) + { + Stream_Read_UINT8(s, pdu.Properties[i].PropertySet); + Stream_Read_UINT8(s, pdu.Properties[i].PropertyId); + Stream_Read_UINT8(s, pdu.Properties[i].Capabilities); + Stream_Read_INT32(s, pdu.Properties[i].MinValue); + Stream_Read_INT32(s, pdu.Properties[i].MaxValue); + Stream_Read_INT32(s, pdu.Properties[i].Step); + Stream_Read_INT32(s, pdu.Properties[i].DefaultValue); + } + } + + IFCALLRET(context->PropertyListResponse, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->PropertyListResponse failed with error %" PRIu32 "", error); + + free(pdu.Properties); + + return error; +} + +static UINT device_server_recv_property_value_response(CameraDeviceServerContext* context, + wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_PROPERTY_VALUE_RESPONSE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 5)) + return ERROR_NO_DATA; + + Stream_Read_UINT8(s, pdu.PropertyValue.Mode); + Stream_Read_INT32(s, pdu.PropertyValue.Value); + + IFCALLRET(context->PropertyValueResponse, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->PropertyValueResponse failed with error %" PRIu32 "", error); + + return error; +} + +static UINT device_process_message(device_server* device) +{ + BOOL rc; + UINT error = ERROR_INTERNAL_ERROR; + ULONG BytesReturned; + CAM_SHARED_MSG_HEADER header = { 0 }; + wStream* s; + + WINPR_ASSERT(device); + WINPR_ASSERT(device->device_channel); + + s = device->buffer; + WINPR_ASSERT(s); + + Stream_SetPosition(s, 0); + rc = WTSVirtualChannelRead(device->device_channel, 0, NULL, 0, &BytesReturned); + if (!rc) + goto out; + + if (BytesReturned < 1) + { + error = CHANNEL_RC_OK; + goto out; + } + + if (!Stream_EnsureRemainingCapacity(s, BytesReturned)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + error = CHANNEL_RC_NO_MEMORY; + goto out; + } + + if (WTSVirtualChannelRead(device->device_channel, 0, (PCHAR)Stream_Buffer(s), + (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSVirtualChannelRead failed!"); + goto out; + } + + Stream_SetLength(s, BytesReturned); + if (!Stream_CheckAndLogRequiredLength(TAG, s, CAM_HEADER_SIZE)) + return ERROR_NO_DATA; + + Stream_Read_UINT8(s, header.Version); + Stream_Read_UINT8(s, header.MessageId); + + switch (header.MessageId) + { + case CAM_MSG_ID_SuccessResponse: + error = device_server_handle_success_response(&device->context, s, &header); + break; + case CAM_MSG_ID_ErrorResponse: + error = device_server_recv_error_response(&device->context, s, &header); + break; + case CAM_MSG_ID_StreamListResponse: + error = device_server_recv_stream_list_response(&device->context, s, &header); + break; + case CAM_MSG_ID_MediaTypeListResponse: + error = device_server_recv_media_type_list_response(&device->context, s, &header); + break; + case CAM_MSG_ID_CurrentMediaTypeResponse: + error = device_server_recv_current_media_type_response(&device->context, s, &header); + break; + case CAM_MSG_ID_SampleResponse: + error = device_server_recv_sample_response(&device->context, s, &header); + break; + case CAM_MSG_ID_SampleErrorResponse: + error = device_server_recv_sample_error_response(&device->context, s, &header); + break; + case CAM_MSG_ID_PropertyListResponse: + error = device_server_recv_property_list_response(&device->context, s, &header); + break; + case CAM_MSG_ID_PropertyValueResponse: + error = device_server_recv_property_value_response(&device->context, s, &header); + break; + default: + WLog_ERR(TAG, "device_process_message: unknown or invalid MessageId %" PRIu8 "", + header.MessageId); + break; + } + +out: + if (error) + WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error); + + return error; +} + +static UINT device_server_context_poll_int(CameraDeviceServerContext* context) +{ + device_server* device = (device_server*)context; + UINT error = ERROR_INTERNAL_ERROR; + + WINPR_ASSERT(device); + + switch (device->state) + { + case CAMERA_DEVICE_INITIAL: + error = device_server_open_channel(device); + if (error) + WLog_ERR(TAG, "device_server_open_channel failed with error %" PRIu32 "!", error); + else + device->state = CAMERA_DEVICE_OPENED; + break; + case CAMERA_DEVICE_OPENED: + error = device_process_message(device); + break; + } + + return error; +} + +static HANDLE device_server_get_channel_handle(device_server* device) +{ + void* buffer = NULL; + DWORD BytesReturned = 0; + HANDLE ChannelEvent = NULL; + + WINPR_ASSERT(device); + + if (WTSVirtualChannelQuery(device->device_channel, WTSVirtualEventHandle, &buffer, + &BytesReturned) == TRUE) + { + if (BytesReturned == sizeof(HANDLE)) + CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE)); + + WTSFreeMemory(buffer); + } + + return ChannelEvent; +} + +static DWORD WINAPI device_server_thread_func(LPVOID arg) +{ + DWORD nCount; + HANDLE events[2] = { 0 }; + device_server* device = (device_server*)arg; + UINT error = CHANNEL_RC_OK; + DWORD status; + + WINPR_ASSERT(device); + + nCount = 0; + events[nCount++] = device->stopEvent; + + while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0)) + { + switch (device->state) + { + case CAMERA_DEVICE_INITIAL: + error = device_server_context_poll_int(&device->context); + if (error == CHANNEL_RC_OK) + { + events[1] = device_server_get_channel_handle(device); + nCount = 2; + } + break; + case CAMERA_DEVICE_OPENED: + status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); + switch (status) + { + case WAIT_OBJECT_0: + break; + case WAIT_OBJECT_0 + 1: + case WAIT_TIMEOUT: + error = device_server_context_poll_int(&device->context); + break; + + case WAIT_FAILED: + default: + error = ERROR_INTERNAL_ERROR; + break; + } + break; + } + } + + WTSVirtualChannelClose(device->device_channel); + device->device_channel = NULL; + + if (error && device->context.rdpcontext) + setChannelError(device->context.rdpcontext, error, + "device_server_thread_func reported an error"); + + ExitThread(error); + return error; +} + +static UINT device_server_open(CameraDeviceServerContext* context) +{ + device_server* device = (device_server*)context; + + WINPR_ASSERT(device); + + if (!device->externalThread && (device->thread == NULL)) + { + device->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!device->stopEvent) + { + WLog_ERR(TAG, "CreateEvent failed!"); + return ERROR_INTERNAL_ERROR; + } + + device->thread = CreateThread(NULL, 0, device_server_thread_func, device, 0, NULL); + if (!device->thread) + { + WLog_ERR(TAG, "CreateThread failed!"); + CloseHandle(device->stopEvent); + device->stopEvent = NULL; + return ERROR_INTERNAL_ERROR; + } + } + device->isOpened = TRUE; + + return CHANNEL_RC_OK; +} + +static UINT device_server_close(CameraDeviceServerContext* context) +{ + UINT error = CHANNEL_RC_OK; + device_server* device = (device_server*)context; + + WINPR_ASSERT(device); + + if (!device->externalThread && device->thread) + { + SetEvent(device->stopEvent); + + if (WaitForSingleObject(device->thread, INFINITE) == WAIT_FAILED) + { + error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error); + return error; + } + + CloseHandle(device->thread); + CloseHandle(device->stopEvent); + device->thread = NULL; + device->stopEvent = NULL; + } + if (device->externalThread) + { + if (device->state != CAMERA_DEVICE_INITIAL) + { + WTSVirtualChannelClose(device->device_channel); + device->device_channel = NULL; + device->state = CAMERA_DEVICE_INITIAL; + } + } + device->isOpened = FALSE; + + return error; +} + +static UINT device_server_context_poll(CameraDeviceServerContext* context) +{ + device_server* device = (device_server*)context; + + WINPR_ASSERT(device); + + if (!device->externalThread) + return ERROR_INTERNAL_ERROR; + + return device_server_context_poll_int(context); +} + +static BOOL device_server_context_handle(CameraDeviceServerContext* context, HANDLE* handle) +{ + device_server* device = (device_server*)context; + + WINPR_ASSERT(device); + WINPR_ASSERT(handle); + + if (!device->externalThread) + return FALSE; + if (device->state == CAMERA_DEVICE_INITIAL) + return FALSE; + + *handle = device_server_get_channel_handle(device); + + return TRUE; +} + +static wStream* device_server_packet_new(size_t size, BYTE version, BYTE messageId) +{ + wStream* s; + + WINPR_ASSERT(size > 0); + + /* Allocate what we need plus header bytes */ + s = Stream_New(NULL, size + CAM_HEADER_SIZE); + if (!s) + { + WLog_ERR(TAG, "Stream_New failed!"); + return NULL; + } + + Stream_Write_UINT8(s, version); + Stream_Write_UINT8(s, messageId); + + return s; +} + +static UINT device_server_packet_send(CameraDeviceServerContext* context, wStream* s) +{ + device_server* device = (device_server*)context; + UINT error = CHANNEL_RC_OK; + ULONG written; + + WINPR_ASSERT(context); + WINPR_ASSERT(s); + + if (!WTSVirtualChannelWrite(device->device_channel, (PCHAR)Stream_Buffer(s), + Stream_GetPosition(s), &written)) + { + WLog_ERR(TAG, "WTSVirtualChannelWrite failed!"); + error = ERROR_INTERNAL_ERROR; + goto out; + } + + if (written < Stream_GetPosition(s)) + { + WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written, + Stream_GetPosition(s)); + } + +out: + Stream_Free(s, TRUE); + return error; +} + +static UINT device_server_write_and_send_header(CameraDeviceServerContext* context, BYTE messageId) +{ + wStream* s; + + WINPR_ASSERT(context); + + s = device_server_packet_new(0, context->protocolVersion, messageId); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + return device_server_packet_send(context, s); +} + +static UINT +device_send_activate_device_request_pdu(CameraDeviceServerContext* context, + const CAM_ACTIVATE_DEVICE_REQUEST* activateDeviceRequest) +{ + WINPR_ASSERT(context); + + return device_server_write_and_send_header(context, CAM_MSG_ID_ActivateDeviceRequest); +} + +static UINT device_send_deactivate_device_request_pdu( + CameraDeviceServerContext* context, + const CAM_DEACTIVATE_DEVICE_REQUEST* deactivateDeviceRequest) +{ + WINPR_ASSERT(context); + + return device_server_write_and_send_header(context, CAM_MSG_ID_DeactivateDeviceRequest); +} + +static UINT device_send_stream_list_request_pdu(CameraDeviceServerContext* context, + const CAM_STREAM_LIST_REQUEST* streamListRequest) +{ + WINPR_ASSERT(context); + + return device_server_write_and_send_header(context, CAM_MSG_ID_StreamListRequest); +} + +static UINT +device_send_media_type_list_request_pdu(CameraDeviceServerContext* context, + const CAM_MEDIA_TYPE_LIST_REQUEST* mediaTypeListRequest) +{ + wStream* s; + + WINPR_ASSERT(context); + WINPR_ASSERT(mediaTypeListRequest); + + s = device_server_packet_new(1, context->protocolVersion, CAM_MSG_ID_MediaTypeListRequest); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + Stream_Write_UINT8(s, mediaTypeListRequest->StreamIndex); + + return device_server_packet_send(context, s); +} + +static UINT device_send_current_media_type_request_pdu( + CameraDeviceServerContext* context, + const CAM_CURRENT_MEDIA_TYPE_REQUEST* currentMediaTypeRequest) +{ + wStream* s; + + WINPR_ASSERT(context); + WINPR_ASSERT(currentMediaTypeRequest); + + s = device_server_packet_new(1, context->protocolVersion, CAM_MSG_ID_CurrentMediaTypeRequest); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + Stream_Write_UINT8(s, currentMediaTypeRequest->StreamIndex); + + return device_server_packet_send(context, s); +} + +static UINT +device_send_start_streams_request_pdu(CameraDeviceServerContext* context, + const CAM_START_STREAMS_REQUEST* startStreamsRequest) +{ + wStream* s; + size_t i; + + WINPR_ASSERT(context); + WINPR_ASSERT(startStreamsRequest); + + s = device_server_packet_new(startStreamsRequest->N_Infos * 27ul, context->protocolVersion, + CAM_MSG_ID_StartStreamsRequest); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + for (i = 0; i < startStreamsRequest->N_Infos; ++i) + { + const CAM_START_STREAM_INFO* info = &startStreamsRequest->StartStreamsInfo[i]; + const CAM_MEDIA_TYPE_DESCRIPTION* description = &info->MediaTypeDescription; + + Stream_Write_UINT8(s, info->StreamIndex); + + Stream_Write_UINT8(s, description->Format); + Stream_Write_UINT32(s, description->Width); + Stream_Write_UINT32(s, description->Height); + Stream_Write_UINT32(s, description->FrameRateNumerator); + Stream_Write_UINT32(s, description->FrameRateDenominator); + Stream_Write_UINT32(s, description->PixelAspectRatioNumerator); + Stream_Write_UINT32(s, description->PixelAspectRatioDenominator); + Stream_Write_UINT8(s, description->Flags); + } + + return device_server_packet_send(context, s); +} + +static UINT device_send_stop_streams_request_pdu(CameraDeviceServerContext* context, + const CAM_STOP_STREAMS_REQUEST* stopStreamsRequest) +{ + WINPR_ASSERT(context); + + return device_server_write_and_send_header(context, CAM_MSG_ID_StopStreamsRequest); +} + +static UINT device_send_sample_request_pdu(CameraDeviceServerContext* context, + const CAM_SAMPLE_REQUEST* sampleRequest) +{ + wStream* s; + + WINPR_ASSERT(context); + WINPR_ASSERT(sampleRequest); + + s = device_server_packet_new(1, context->protocolVersion, CAM_MSG_ID_SampleRequest); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + Stream_Write_UINT8(s, sampleRequest->StreamIndex); + + return device_server_packet_send(context, s); +} + +static UINT +device_send_property_list_request_pdu(CameraDeviceServerContext* context, + const CAM_PROPERTY_LIST_REQUEST* propertyListRequest) +{ + WINPR_ASSERT(context); + + return device_server_write_and_send_header(context, CAM_MSG_ID_PropertyListRequest); +} + +static UINT +device_send_property_value_request_pdu(CameraDeviceServerContext* context, + const CAM_PROPERTY_VALUE_REQUEST* propertyValueRequest) +{ + wStream* s; + + WINPR_ASSERT(context); + WINPR_ASSERT(propertyValueRequest); + + s = device_server_packet_new(2, context->protocolVersion, CAM_MSG_ID_PropertyValueRequest); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + Stream_Write_UINT8(s, propertyValueRequest->PropertySet); + Stream_Write_UINT8(s, propertyValueRequest->PropertyId); + + return device_server_packet_send(context, s); +} + +static UINT device_send_set_property_value_request_pdu( + CameraDeviceServerContext* context, + const CAM_SET_PROPERTY_VALUE_REQUEST* setPropertyValueRequest) +{ + wStream* s; + + WINPR_ASSERT(context); + WINPR_ASSERT(setPropertyValueRequest); + + s = device_server_packet_new(2 + 5, context->protocolVersion, + CAM_MSG_ID_SetPropertyValueRequest); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + Stream_Write_UINT8(s, setPropertyValueRequest->PropertySet); + Stream_Write_UINT8(s, setPropertyValueRequest->PropertyId); + + Stream_Write_UINT8(s, setPropertyValueRequest->PropertyValue.Mode); + Stream_Write_INT32(s, setPropertyValueRequest->PropertyValue.Value); + + return device_server_packet_send(context, s); +} + +CameraDeviceServerContext* camera_device_server_context_new(HANDLE vcm) +{ + device_server* device = (device_server*)calloc(1, sizeof(device_server)); + + if (!device) + return NULL; + + device->context.vcm = vcm; + device->context.Initialize = device_server_initialize; + device->context.Open = device_server_open; + device->context.Close = device_server_close; + device->context.Poll = device_server_context_poll; + device->context.ChannelHandle = device_server_context_handle; + + device->context.ActivateDeviceRequest = device_send_activate_device_request_pdu; + device->context.DeactivateDeviceRequest = device_send_deactivate_device_request_pdu; + + device->context.StreamListRequest = device_send_stream_list_request_pdu; + device->context.MediaTypeListRequest = device_send_media_type_list_request_pdu; + device->context.CurrentMediaTypeRequest = device_send_current_media_type_request_pdu; + + device->context.StartStreamsRequest = device_send_start_streams_request_pdu; + device->context.StopStreamsRequest = device_send_stop_streams_request_pdu; + device->context.SampleRequest = device_send_sample_request_pdu; + + device->context.PropertyListRequest = device_send_property_list_request_pdu; + device->context.PropertyValueRequest = device_send_property_value_request_pdu; + device->context.SetPropertyValueRequest = device_send_set_property_value_request_pdu; + + device->buffer = Stream_New(NULL, 4096); + if (!device->buffer) + goto fail; + + return &device->context; +fail: + camera_device_server_context_free(&device->context); + return NULL; +} + +void camera_device_server_context_free(CameraDeviceServerContext* context) +{ + device_server* device = (device_server*)context; + + if (device) + { + device_server_close(context); + Stream_Free(device->buffer, TRUE); + } + + free(context->virtualChannelName); + + free(device); +} diff --git a/channels/rdpei/server/rdpei_main.c b/channels/rdpei/server/rdpei_main.c index 1e2dfe2..433b2cf 100644 --- a/channels/rdpei/server/rdpei_main.c +++ b/channels/rdpei/server/rdpei_main.c @@ -99,6 +99,8 @@ UINT rdpei_server_init(RdpeiServerContext* context) void* buffer = NULL; DWORD bytesReturned; RdpeiServerPrivate* priv = context->priv; + UINT32 channelId; + BOOL status = TRUE; priv->channelHandle = WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, RDPEI_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC); @@ -108,6 +110,15 @@ UINT rdpei_server_init(RdpeiServerContext* context) return CHANNEL_RC_INITIALIZATION_ERROR; } + channelId = WTSChannelGetIdByHandle(priv->channelHandle); + + IFCALLRET(context->onChannelIdAssigned, status, context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->onChannelIdAssigned failed!"); + goto out_close; + } + if (!WTSVirtualChannelQuery(priv->channelHandle, WTSVirtualEventHandle, &buffer, &bytesReturned) || (bytesReturned != sizeof(HANDLE))) diff --git a/channels/rdpgfx/client/rdpgfx_main.c b/channels/rdpgfx/client/rdpgfx_main.c index 67b3a7d..ceb4c18 100644 --- a/channels/rdpgfx/client/rdpgfx_main.c +++ b/channels/rdpgfx/client/rdpgfx_main.c @@ -163,11 +163,12 @@ fail: static BOOL rdpgfx_is_capability_filtered(RDPGFX_PLUGIN* gfx, UINT32 caps) { const UINT32 filter = gfx->capsFilter; - const UINT32 capList[] = { - RDPGFX_CAPVERSION_8, RDPGFX_CAPVERSION_81, RDPGFX_CAPVERSION_10, - RDPGFX_CAPVERSION_101, RDPGFX_CAPVERSION_102, RDPGFX_CAPVERSION_103, - RDPGFX_CAPVERSION_104, RDPGFX_CAPVERSION_105, RDPGFX_CAPVERSION_106 - }; + const UINT32 capList[] = { RDPGFX_CAPVERSION_8, RDPGFX_CAPVERSION_81, + RDPGFX_CAPVERSION_10, RDPGFX_CAPVERSION_101, + RDPGFX_CAPVERSION_102, RDPGFX_CAPVERSION_103, + RDPGFX_CAPVERSION_104, RDPGFX_CAPVERSION_105, + RDPGFX_CAPVERSION_106, RDPGFX_CAPVERSION_106_ERR, + RDPGFX_CAPVERSION_107 }; UINT32 x; for (x = 0; x < ARRAYSIZE(capList); x++) @@ -190,7 +191,7 @@ static UINT rdpgfx_send_supported_caps(RDPGFX_CHANNEL_CALLBACK* callback) RdpgfxClientContext* context; RDPGFX_CAPSET* capsSet; RDPGFX_CAPSET capsSets[RDPGFX_NUMBER_CAPSETS] = { 0 }; - RDPGFX_CAPS_ADVERTISE_PDU pdu; + RDPGFX_CAPS_ADVERTISE_PDU pdu = { 0 }; if (!callback) return ERROR_BAD_ARGUMENTS; @@ -323,6 +324,25 @@ static UINT rdpgfx_send_supported_caps(RDPGFX_CHANNEL_CALLBACK* callback) capsSet->length = 0x4; capsSet->flags = caps10Flags; } + + if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_106_ERR)) + { + capsSet = &capsSets[pdu.capsSetCount++]; + capsSet->version = RDPGFX_CAPVERSION_106_ERR; + capsSet->length = 0x4; + capsSet->flags = caps10Flags; + } + + if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_107)) + { + capsSet = &capsSets[pdu.capsSetCount++]; + capsSet->version = RDPGFX_CAPVERSION_107; + capsSet->length = 0x4; + capsSet->flags = caps10Flags; +#if !defined(CAIRO_FOUND) && !defined(SWSCALE_FOUND) + capsSet->flags |= RDPGFX_CAPS_FLAG_SCALEDMAP_DISABLE; +#endif + } } return IFCALLRESULT(ERROR_BAD_CONFIGURATION, context->CapsAdvertise, context, &pdu); @@ -895,6 +915,8 @@ static UINT rdpgfx_recv_end_frame_pdu(RDPGFX_CHANNEL_CALLBACK* callback, wStream case RDPGFX_CAPVERSION_104: case RDPGFX_CAPVERSION_105: case RDPGFX_CAPVERSION_106: + case RDPGFX_CAPVERSION_106_ERR: + case RDPGFX_CAPVERSION_107: if (gfx->SendQoeAck) { RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU qoe; diff --git a/channels/rdpgfx/server/rdpgfx_main.c b/channels/rdpgfx/server/rdpgfx_main.c index 306cc1c..283bb88 100644 --- a/channels/rdpgfx/server/rdpgfx_main.c +++ b/channels/rdpgfx/server/rdpgfx_main.c @@ -1428,6 +1428,8 @@ static BOOL rdpgfx_server_open(RdpgfxServerContext* context) PULONG pSessionId = NULL; DWORD BytesReturned = 0; priv->SessionId = WTS_CURRENT_SESSION; + UINT32 channelId; + BOOL status = TRUE; if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId, (LPSTR*)&pSessionId, &BytesReturned) == FALSE) @@ -1447,6 +1449,15 @@ static BOOL rdpgfx_server_open(RdpgfxServerContext* context) return FALSE; } + channelId = WTSChannelGetIdByHandle(priv->rdpgfx_channel); + + IFCALLRET(context->ChannelIdAssigned, status, context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + goto out_close; + } + /* Query for channel event handle */ if (!WTSVirtualChannelQuery(priv->rdpgfx_channel, WTSVirtualEventHandle, &buffer, &BytesReturned) || diff --git a/channels/rdpsnd/client/alsa/rdpsnd_alsa.c b/channels/rdpsnd/client/alsa/rdpsnd_alsa.c index 95e7a79..a903596 100644 --- a/channels/rdpsnd/client/alsa/rdpsnd_alsa.c +++ b/channels/rdpsnd/client/alsa/rdpsnd_alsa.c @@ -213,10 +213,6 @@ static BOOL rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, const AUDIO_FORMA break; - case WAVE_FORMAT_ALAW: - case WAVE_FORMAT_MULAW: - break; - default: return FALSE; } diff --git a/channels/rdpsnd/client/mac/rdpsnd_mac.m b/channels/rdpsnd/client/mac/rdpsnd_mac.m index 8a01d9b..94ef150 100644 --- a/channels/rdpsnd/client/mac/rdpsnd_mac.m +++ b/channels/rdpsnd/client/mac/rdpsnd_mac.m @@ -139,86 +139,99 @@ static void rdpsnd_mac_release(rdpsndMacPlugin *mac) static BOOL rdpsnd_mac_open(rdpsndDevicePlugin *device, const AUDIO_FORMAT *format, UINT32 latency) { - AudioDeviceID outputDeviceID; - UInt32 propertySize; - OSStatus err; - NSError *error; - rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; - AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDefaultOutputDevice, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; + @autoreleasepool + { + AudioDeviceID outputDeviceID; + UInt32 propertySize; + OSStatus err; + NSError *error; + rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; + AudioObjectPropertyAddress propertyAddress = { + kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyScopeGlobal, +#if defined(MAC_OS_VERSION_12_0) + kAudioObjectPropertyElementMain +#else + kAudioObjectPropertyElementMaster +#endif + }; - if (mac->isOpen) + if (mac->isOpen) + return TRUE; + + if (!rdpsnd_mac_set_format(device, format, latency)) + return FALSE; + + propertySize = sizeof(outputDeviceID); + err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, + &propertySize, &outputDeviceID); + if (err) + { + WLog_ERR(TAG, "AudioHardwareGetProperty: %s", FormatError(err)); + return FALSE; + } + + mac->engine = [[AVAudioEngine alloc] init]; + if (!mac->engine) + return FALSE; + + err = AudioUnitSetProperty(mac->engine.outputNode.audioUnit, + kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, + 0, &outputDeviceID, sizeof(outputDeviceID)); + if (err) + { + rdpsnd_mac_release(mac); + WLog_ERR(TAG, "AudioUnitSetProperty: %s", FormatError(err)); + return FALSE; + } + + mac->player = [[AVAudioPlayerNode alloc] init]; + if (!mac->player) + { + rdpsnd_mac_release(mac); + WLog_ERR(TAG, "AVAudioPlayerNode::init() failed"); + return FALSE; + } + + [mac->engine attachNode:mac->player]; + + [mac->engine connect:mac->player to:mac->engine.mainMixerNode format:nil]; + + [mac->engine prepare]; + + if (![mac->engine startAndReturnError:&error]) + { + device->Close(device); + WLog_ERR(TAG, "Failed to start audio player %s", + [error.localizedDescription UTF8String]); + return FALSE; + } + + mac->isOpen = TRUE; return TRUE; - - if (!rdpsnd_mac_set_format(device, format, latency)) - return FALSE; - - propertySize = sizeof(outputDeviceID); - err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, - &propertySize, &outputDeviceID); - if (err) - { - WLog_ERR(TAG, "AudioHardwareGetProperty: %s", FormatError(err)); - return FALSE; } - - mac->engine = [[AVAudioEngine alloc] init]; - if (!mac->engine) - return FALSE; - - err = AudioUnitSetProperty(mac->engine.outputNode.audioUnit, - kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, - &outputDeviceID, sizeof(outputDeviceID)); - if (err) - { - rdpsnd_mac_release(mac); - WLog_ERR(TAG, "AudioUnitSetProperty: %s", FormatError(err)); - return FALSE; - } - - mac->player = [[AVAudioPlayerNode alloc] init]; - if (!mac->player) - { - rdpsnd_mac_release(mac); - WLog_ERR(TAG, "AVAudioPlayerNode::init() failed"); - return FALSE; - } - - [mac->engine attachNode:mac->player]; - - [mac->engine connect:mac->player to:mac->engine.mainMixerNode format:nil]; - - [mac->engine prepare]; - - if (![mac->engine startAndReturnError:&error]) - { - device->Close(device); - WLog_ERR(TAG, "Failed to start audio player %s", [error.localizedDescription UTF8String]); - return FALSE; - } - - mac->isOpen = TRUE; - return TRUE; } static void rdpsnd_mac_close(rdpsndDevicePlugin *device) { - rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; - - if (mac->isPlaying) + @autoreleasepool { - [mac->player stop]; - mac->isPlaying = FALSE; - } + rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; - if (mac->isOpen) - { - [mac->engine stop]; - mac->isOpen = FALSE; - } + if (mac->isPlaying) + { + [mac->player stop]; + mac->isPlaying = FALSE; + } - rdpsnd_mac_release(mac); + if (mac->isOpen) + { + [mac->engine stop]; + mac->isOpen = FALSE; + } + + rdpsnd_mac_release(mac); + } } static void rdpsnd_mac_free(rdpsndDevicePlugin *device) @@ -246,108 +259,118 @@ static BOOL rdpsnd_mac_format_supported(rdpsndDevicePlugin *device, const AUDIO_ static BOOL rdpsnd_mac_set_volume(rdpsndDevicePlugin *device, UINT32 value) { - Float32 fVolume; - UINT16 volumeLeft; - UINT16 volumeRight; - rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; + @autoreleasepool + { + Float32 fVolume; + UINT16 volumeLeft; + UINT16 volumeRight; + rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; - if (!mac->player) - return FALSE; + if (!mac->player) + return FALSE; - volumeLeft = (value & 0xFFFF); - volumeRight = ((value >> 16) & 0xFFFF); - fVolume = ((float)volumeLeft) / 65535.0f; + volumeLeft = (value & 0xFFFF); + volumeRight = ((value >> 16) & 0xFFFF); + fVolume = ((float)volumeLeft) / 65535.0f; - mac->player.volume = fVolume; + mac->player.volume = fVolume; - return TRUE; + return TRUE; + } } static void rdpsnd_mac_start(rdpsndDevicePlugin *device) { - rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; - - if (!mac->isPlaying) + @autoreleasepool { - if (!mac->engine.isRunning) + rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; + + if (!mac->isPlaying) { - NSError *error; - - if (![mac->engine startAndReturnError:&error]) + if (!mac->engine.isRunning) { - device->Close(device); - WLog_ERR(TAG, "Failed to start audio player %s", - [error.localizedDescription UTF8String]); - return; + NSError *error; + + if (![mac->engine startAndReturnError:&error]) + { + device->Close(device); + WLog_ERR(TAG, "Failed to start audio player %s", + [error.localizedDescription UTF8String]); + return; + } } + + [mac->player play]; + + mac->isPlaying = TRUE; + mac->diff = 100; /* Initial latency, corrected after first sample is played. */ } - - [mac->player play]; - - mac->isPlaying = TRUE; - mac->diff = 100; /* Initial latency, corrected after first sample is played. */ } } static UINT rdpsnd_mac_play(rdpsndDevicePlugin *device, const BYTE *data, size_t size) { - rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; - AVAudioPCMBuffer *buffer; - AVAudioFormat *format; - float *const *db; - size_t pos, step, x; - AVAudioFrameCount count; - UINT64 start = GetTickCount64(); - - if (!mac->isOpen) - return 0; - - step = 2 * mac->format.nChannels; - - count = size / step; - format = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32 - sampleRate:mac->format.nSamplesPerSec - channels:mac->format.nChannels - interleaved:NO]; - if (!format) + @autoreleasepool { - WLog_WARN(TAG, "AVAudioFormat::init() failed"); - return 0; - } - buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:count]; - if (!buffer) - { - [format release]; - WLog_WARN(TAG, "AVAudioPCMBuffer::init() failed"); - return 0; - } + rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; + AVAudioPCMBuffer *buffer; + AVAudioFormat *format; + float *const *db; + size_t pos, step, x; + AVAudioFrameCount count; + UINT64 start = GetTickCount64(); - buffer.frameLength = buffer.frameCapacity; - db = buffer.floatChannelData; + if (!mac->isOpen) + return 0; - for (pos = 0; pos < count; pos++) - { - const BYTE *d = &data[pos * step]; - for (x = 0; x < mac->format.nChannels; x++) + step = 2 * mac->format.nChannels; + + count = size / step; + format = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32 + sampleRate:mac->format.nSamplesPerSec + channels:mac->format.nChannels + interleaved:NO]; + if (!format) { - const float val = (int16_t)((uint16_t)d[0] | ((uint16_t)d[1] << 8)) / 32768.0f; - db[x][pos] = val; - d += sizeof(int16_t); + WLog_WARN(TAG, "AVAudioFormat::init() failed"); + return 0; } + buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:count]; + [format release]; + if (!buffer) + { + WLog_WARN(TAG, "AVAudioPCMBuffer::init() failed"); + return 0; + } + + buffer.frameLength = buffer.frameCapacity; + db = buffer.floatChannelData; + + for (pos = 0; pos < count; pos++) + { + const BYTE *d = &data[pos * step]; + for (x = 0; x < mac->format.nChannels; x++) + { + const float val = (int16_t)((uint16_t)d[0] | ((uint16_t)d[1] << 8)) / 32768.0f; + db[x][pos] = val; + d += sizeof(int16_t); + } + } + + rdpsnd_mac_start(device); + + [mac->player scheduleBuffer:buffer + completionHandler:^{ + UINT64 stop = GetTickCount64(); + if (start > stop) + mac->diff = 0; + else + mac->diff = stop - start; + }]; + [buffer release]; + + return mac->diff > UINT_MAX ? UINT_MAX : mac->diff; } - - rdpsnd_mac_start(device); - - [mac->player scheduleBuffer:buffer - completionHandler:^{ - UINT64 stop = GetTickCount64(); - if (start > stop) - mac->diff = 0; - else - mac->diff = stop - start; - }]; - - return mac->diff > UINT_MAX ? UINT_MAX : mac->diff; } #ifdef BUILTIN_CHANNELS diff --git a/channels/rdpsnd/client/oss/rdpsnd_oss.c b/channels/rdpsnd/client/oss/rdpsnd_oss.c index 7a7937d..f8e15d2 100644 --- a/channels/rdpsnd/client/oss/rdpsnd_oss.c +++ b/channels/rdpsnd/client/oss/rdpsnd_oss.c @@ -117,10 +117,6 @@ static BOOL rdpsnd_oss_format_supported(rdpsndDevicePlugin* device, const AUDIO_ break; - case WAVE_FORMAT_MULAW: - case WAVE_FORMAT_ALAW: - break; - default: return FALSE; } diff --git a/channels/rdpsnd/client/pulse/rdpsnd_pulse.c b/channels/rdpsnd/client/pulse/rdpsnd_pulse.c index 7f3a6da..7a95470 100644 --- a/channels/rdpsnd/client/pulse/rdpsnd_pulse.c +++ b/channels/rdpsnd/client/pulse/rdpsnd_pulse.c @@ -427,15 +427,7 @@ BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMA break; - case WAVE_FORMAT_ALAW: - case WAVE_FORMAT_MULAW: - if (format->cbSize == 0 && (format->nSamplesPerSec <= PA_RATE_MAX) && - (format->wBitsPerSample == 8) && - (format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX)) - { - return TRUE; - } - + default: break; } diff --git a/channels/rdpsnd/client/rdpsnd_main.c b/channels/rdpsnd/client/rdpsnd_main.c index ebdf931..f624058 100644 --- a/channels/rdpsnd/client/rdpsnd_main.c +++ b/channels/rdpsnd/client/rdpsnd_main.c @@ -582,6 +582,7 @@ static UINT rdpsnd_treat_wave(rdpsndPlugin* rdpsnd, wStream* s, size_t size) UINT64 end; UINT64 diffMS, ts; UINT latency = 0; + UINT error; if (Stream_GetRemainingLength(s) < size) return ERROR_BAD_LENGTH; @@ -589,6 +590,15 @@ static UINT rdpsnd_treat_wave(rdpsndPlugin* rdpsnd, wStream* s, size_t size) if (rdpsnd->wCurrentFormatNo >= rdpsnd->NumberOfClientFormats) return ERROR_INTERNAL_ERROR; + /* + * Send the first WaveConfirm PDU. The server side uses this to determine the + * network latency. + * See also [MS-RDPEA] 2.2.3.8 Wave Confirm PDU + */ + error = rdpsnd_send_wave_confirm_pdu(rdpsnd, rdpsnd->wTimeStamp, rdpsnd->cBlockNo); + if (error) + return error; + data = Stream_Pointer(s); format = &rdpsnd->ClientFormats[rdpsnd->wCurrentFormatNo]; WLog_Print(rdpsnd->log, WLOG_DEBUG, @@ -621,10 +631,11 @@ static UINT rdpsnd_treat_wave(rdpsndPlugin* rdpsnd, wStream* s, size_t size) diffMS = end - rdpsnd->wArrivalTime + latency; ts = (rdpsnd->wTimeStamp + diffMS) % UINT16_MAX; - /* Don't send wave confirm PDU if on dynamic channel */ - if (rdpsnd->dynamic) - return CHANNEL_RC_OK; - + /* + * Send the second WaveConfirm PDU. With the first WaveConfirm PDU, + * the server side uses this second WaveConfirm PDU to determine the actual + * render latency. + */ return rdpsnd_send_wave_confirm_pdu(rdpsnd, (UINT16)ts, rdpsnd->cBlockNo); } diff --git a/channels/rdpsnd/server/rdpsnd_main.c b/channels/rdpsnd/server/rdpsnd_main.c index 537aef6..363fac7 100644 --- a/channels/rdpsnd/server/rdpsnd_main.c +++ b/channels/rdpsnd/server/rdpsnd_main.c @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -36,17 +37,33 @@ #include "rdpsnd_common.h" #include "rdpsnd_main.h" +static wStream* rdpsnd_server_get_buffer(RdpsndServerContext* context) +{ + wStream* s; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + + s = context->priv->rdpsnd_pdu; + Stream_SetPosition(s, 0); + return s; +} + /** - * Function description + * Send Server Audio Formats and Version PDU (2.2.2.1) * * @return 0 on success, otherwise a Win32 error code */ -static UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s) +static UINT rdpsnd_server_send_formats(RdpsndServerContext* context) { + wStream* s = rdpsnd_server_get_buffer(context); size_t pos; UINT16 i; BOOL status = FALSE; ULONG written; + + if (!Stream_EnsureRemainingCapacity(s, 24)) + return ERROR_OUTOFMEMORY; + Stream_Write_UINT8(s, SNDC_FORMATS); Stream_Write_UINT8(s, 0); Stream_Seek_UINT16(s); @@ -61,12 +78,9 @@ static UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s) for (i = 0; i < context->num_server_formats; i++) { - AUDIO_FORMAT format = context->server_formats[i]; - // TODO: Eliminate this!!! - format.nAvgBytesPerSec = - format.nSamplesPerSec * format.nChannels * format.wBitsPerSample / 8; + const AUDIO_FORMAT* format = &context->server_formats[i]; - if (!audio_format_write(s, &format)) + if (!audio_format_write(s, format)) goto fail; } @@ -74,6 +88,8 @@ static UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s) Stream_SetPosition(s, 2); Stream_Write_UINT16(s, pos - 4); Stream_SetPosition(s, pos); + + WINPR_ASSERT(context->priv); status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), Stream_GetPosition(s), &written); Stream_SetPosition(s, 0); @@ -82,7 +98,7 @@ fail: } /** - * Function description + * Read Wave Confirm PDU (2.2.3.8) and handle callback * * @return 0 on success, otherwise a Win32 error code */ @@ -92,11 +108,10 @@ static UINT rdpsnd_server_recv_waveconfirm(RdpsndServerContext* context, wStream BYTE confirmBlockNum; UINT error = CHANNEL_RC_OK; - if (Stream_GetRemainingLength(s) < 4) - { - WLog_ERR(TAG, "not enough data in stream!"); + WINPR_ASSERT(context); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) return ERROR_INVALID_DATA; - } Stream_Read_UINT16(s, timestamp); Stream_Read_UINT8(s, confirmBlockNum); @@ -110,13 +125,39 @@ static UINT rdpsnd_server_recv_waveconfirm(RdpsndServerContext* context, wStream } /** - * Function description + * Read Training Confirm PDU (2.2.3.2) and handle callback + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpsnd_server_recv_trainingconfirm(RdpsndServerContext* context, wStream* s) +{ + UINT16 timestamp; + UINT16 packsize; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT16(s, timestamp); + Stream_Read_UINT16(s, packsize); + + IFCALLRET(context->TrainingConfirm, error, context, timestamp, packsize); + if (error) + WLog_ERR(TAG, "context->TrainingConfirm failed with error %" PRIu32 "", error); + + return error; +} + +/** + * Read Quality Mode PDU (2.2.2.3) * * @return 0 on success, otherwise a Win32 error code */ static UINT rdpsnd_server_recv_quality_mode(RdpsndServerContext* context, wStream* s) { - UINT16 quality; + WINPR_ASSERT(context); if (Stream_GetRemainingLength(s) < 4) { @@ -124,34 +165,34 @@ static UINT rdpsnd_server_recv_quality_mode(RdpsndServerContext* context, wStrea return ERROR_INVALID_DATA; } - Stream_Read_UINT16(s, quality); - Stream_Seek_UINT16(s); // reserved - WLog_DBG(TAG, "Client requested sound quality: 0x%04" PRIX16 "", quality); + Stream_Read_UINT16(s, context->qualityMode); /* wQualityMode */ + Stream_Seek_UINT16(s); /* Reserved */ + + WLog_DBG(TAG, "Client requested sound quality: 0x%04" PRIX16 "", context->qualityMode); + return CHANNEL_RC_OK; } /** - * Function description + * Read Client Audio Formats and Version PDU (2.2.2.2) * * @return 0 on success, otherwise a Win32 error code */ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s) { UINT16 i, num_known_format = 0; - UINT32 flags, vol, pitch; UINT16 udpPort; BYTE lastblock; UINT error = CHANNEL_RC_OK; - if (Stream_GetRemainingLength(s) < 20) - { - WLog_ERR(TAG, "not enough data in stream!"); - return ERROR_INVALID_DATA; - } + WINPR_ASSERT(context); - Stream_Read_UINT32(s, flags); /* dwFlags */ - Stream_Read_UINT32(s, vol); /* dwVolume */ - Stream_Read_UINT32(s, pitch); /* dwPitch */ + if (!Stream_CheckAndLogRequiredLength(TAG, s, 20)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, context->capsFlags); /* dwFlags */ + Stream_Read_UINT32(s, context->initialVolume); /* dwVolume */ + Stream_Read_UINT32(s, context->initialPitch); /* dwPitch */ Stream_Read_UINT16(s, udpPort); /* wDGramPort */ Stream_Read_UINT16(s, context->num_client_formats); /* wNumberOfFormats */ Stream_Read_UINT8(s, lastblock); /* cLastBlockConfirmed */ @@ -159,11 +200,8 @@ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s) Stream_Seek_UINT8(s); /* bPad */ /* this check is only a guess as cbSize can influence the size of a format record */ - if (Stream_GetRemainingLength(s) < context->num_client_formats * 18) - { - WLog_ERR(TAG, "not enough data in stream!"); + if (!Stream_CheckAndLogRequiredLength(TAG, s, 18ull * context->num_client_formats)) return ERROR_INVALID_DATA; - } if (!context->num_client_formats) { @@ -181,24 +219,26 @@ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s) for (i = 0; i < context->num_client_formats; i++) { - if (Stream_GetRemainingLength(s) < 18) + AUDIO_FORMAT* format = &context->client_formats[i]; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 18)) { WLog_ERR(TAG, "not enough data in stream!"); error = ERROR_INVALID_DATA; goto out_free; } - Stream_Read_UINT16(s, context->client_formats[i].wFormatTag); - Stream_Read_UINT16(s, context->client_formats[i].nChannels); - Stream_Read_UINT32(s, context->client_formats[i].nSamplesPerSec); - Stream_Read_UINT32(s, context->client_formats[i].nAvgBytesPerSec); - Stream_Read_UINT16(s, context->client_formats[i].nBlockAlign); - Stream_Read_UINT16(s, context->client_formats[i].wBitsPerSample); - Stream_Read_UINT16(s, context->client_formats[i].cbSize); + Stream_Read_UINT16(s, format->wFormatTag); + Stream_Read_UINT16(s, format->nChannels); + Stream_Read_UINT32(s, format->nSamplesPerSec); + Stream_Read_UINT32(s, format->nAvgBytesPerSec); + Stream_Read_UINT16(s, format->nBlockAlign); + Stream_Read_UINT16(s, format->wBitsPerSample); + Stream_Read_UINT16(s, format->cbSize); - if (context->client_formats[i].cbSize > 0) + if (format->cbSize > 0) { - if (!Stream_SafeSeek(s, context->client_formats[i].cbSize)) + if (!Stream_SafeSeek(s, format->cbSize)) { WLog_ERR(TAG, "Stream_SafeSeek failed!"); error = ERROR_INTERNAL_ERROR; @@ -206,7 +246,7 @@ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s) } } - if (context->client_formats[i].wFormatTag != 0) + if (format->wFormatTag != 0) { // lets call this a known format // TODO: actually look through our own list of known formats @@ -228,15 +268,19 @@ out_free: static DWORD WINAPI rdpsnd_server_thread(LPVOID arg) { - DWORD nCount, status; - HANDLE events[8]; - RdpsndServerContext* context; + DWORD nCount = 0, status; + HANDLE events[2] = { 0 }; + RdpsndServerContext* context = (RdpsndServerContext*)arg; UINT error = CHANNEL_RC_OK; - context = (RdpsndServerContext*)arg; - nCount = 0; + + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + events[nCount++] = context->priv->channelEvent; events[nCount++] = context->priv->StopEvent; + WINPR_ASSERT(nCount <= ARRAYSIZE(events)); + while (TRUE) { status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); @@ -281,6 +325,9 @@ static DWORD WINAPI rdpsnd_server_thread(LPVOID arg) */ static UINT rdpsnd_server_initialize(RdpsndServerContext* context, BOOL ownThread) { + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + context->priv->ownThread = ownThread; return context->Start(context); } @@ -297,6 +344,9 @@ static UINT rdpsnd_server_select_format(RdpsndServerContext* context, UINT16 cli AUDIO_FORMAT* format; UINT error = CHANNEL_RC_OK; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + if ((client_format_index >= context->num_client_formats) || (!context->src_format)) { WLog_ERR(TAG, "index %d is not correct.", client_format_index); @@ -371,6 +421,51 @@ out: return error; } +/** + * Send Training PDU (2.2.3.1) + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpsnd_server_training(RdpsndServerContext* context, UINT16 timestamp, UINT16 packsize, + BYTE* data) +{ + size_t end = 0; + ULONG written; + BOOL status; + wStream* s = rdpsnd_server_get_buffer(context); + + if (!Stream_EnsureRemainingCapacity(s, 8)) + return ERROR_INTERNAL_ERROR; + + Stream_Write_UINT8(s, SNDC_TRAINING); + Stream_Write_UINT8(s, 0); + Stream_Seek_UINT16(s); + Stream_Write_UINT16(s, timestamp); + Stream_Write_UINT16(s, packsize); + + if (packsize > 0) + { + if (!Stream_EnsureRemainingCapacity(s, packsize)) + { + Stream_SetPosition(s, 0); + return ERROR_INTERNAL_ERROR; + } + + Stream_Write(s, data, packsize); + } + + end = Stream_GetPosition(s); + Stream_SetPosition(s, 2); + Stream_Write_UINT16(s, end - 4); + + status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), end, + &written); + + Stream_SetPosition(s, 0); + + return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR; +} + static BOOL rdpsnd_server_align_wave_pdu(wStream* s, UINT32 alignment) { size_t size; @@ -404,15 +499,21 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi const BYTE* src; AUDIO_FORMAT* format; ULONG written; - wStream* s = context->priv->rdpsnd_pdu; UINT error = CHANNEL_RC_OK; + wStream* s = rdpsnd_server_get_buffer(context); - if (context->selected_client_format >= context->num_client_formats) + if (context->selected_client_format > context->num_client_formats) return ERROR_INTERNAL_ERROR; + WINPR_ASSERT(context->client_formats); + format = &context->client_formats[context->selected_client_format]; /* WaveInfo PDU */ Stream_SetPosition(s, 0); + + if (!Stream_EnsureRemainingCapacity(s, 16)) + return ERROR_OUTOFMEMORY; + Stream_Write_UINT8(s, SNDC_WAVE); /* msgType */ Stream_Write_UINT8(s, 0); /* bPad */ Stream_Write_UINT16(s, 0); /* BodySize */ @@ -436,7 +537,6 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi Stream_SetPosition(s, 2); Stream_Write_UINT16(s, end - start + 8); Stream_SetPosition(s, end); - context->block_no = (context->block_no + 1) % 256; if (!WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), start + 4, &written)) @@ -464,6 +564,8 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi error = ERROR_INTERNAL_ERROR; } + context->block_no = (context->block_no + 1) % 256; + out: Stream_SetPosition(s, 0); context->priv->out_pending_frames = 0; @@ -476,59 +578,79 @@ out: * * @return 0 on success, otherwise a Win32 error code */ -static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, UINT16 wTimestamp) +static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, UINT16 formatNo, + const BYTE* data, size_t size, BOOL encoded, + UINT16 timestamp, UINT32 audioTimeStamp) { - size_t length; size_t end = 0; - const BYTE* src; - AUDIO_FORMAT* format; ULONG written; - wStream* s = context->priv->rdpsnd_pdu; UINT error = CHANNEL_RC_OK; + BOOL status; + wStream* s = rdpsnd_server_get_buffer(context); - if (context->selected_client_format >= context->num_client_formats) - return ERROR_INTERNAL_ERROR; - - format = &context->client_formats[context->selected_client_format]; - /* WaveInfo PDU */ - Stream_SetPosition(s, 0); - Stream_Write_UINT8(s, SNDC_WAVE2); /* msgType */ - Stream_Write_UINT8(s, 0); /* bPad */ - Stream_Write_UINT16(s, 0); /* BodySize */ - Stream_Write_UINT16(s, wTimestamp); /* wTimeStamp */ - Stream_Write_UINT16(s, context->selected_client_format); /* wFormatNo */ - Stream_Write_UINT8(s, context->block_no); /* cBlockNo */ - Stream_Seek(s, 3); /* bPad */ - Stream_Write_UINT32(s, wTimestamp); /* dwAudioTimeStamp */ - src = context->priv->out_buffer; - length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame; - - if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, src, length, s)) + if (!Stream_EnsureRemainingCapacity(s, 16)) + { error = ERROR_INTERNAL_ERROR; + goto out; + } + + /* Wave2 PDU */ + Stream_Write_UINT8(s, SNDC_WAVE2); /* msgType */ + Stream_Write_UINT8(s, 0); /* bPad */ + Stream_Write_UINT16(s, 0); /* BodySize */ + Stream_Write_UINT16(s, timestamp); /* wTimeStamp */ + Stream_Write_UINT16(s, formatNo); /* wFormatNo */ + Stream_Write_UINT8(s, context->block_no); /* cBlockNo */ + Stream_Write_UINT8(s, 0); /* bPad */ + Stream_Write_UINT8(s, 0); /* bPad */ + Stream_Write_UINT8(s, 0); /* bPad */ + Stream_Write_UINT32(s, audioTimeStamp); /* dwAudioTimeStamp */ + + if (encoded) + { + if (!Stream_EnsureRemainingCapacity(s, size)) + { + error = ERROR_INTERNAL_ERROR; + goto out; + } + + Stream_Write(s, data, size); + } else { - BOOL rc; + AUDIO_FORMAT* format; - /* Set stream size */ - if (!rdpsnd_server_align_wave_pdu(s, format->nBlockAlign)) - return ERROR_INTERNAL_ERROR; - - end = Stream_GetPosition(s); - Stream_SetPosition(s, 2); - Stream_Write_UINT16(s, end - 4); - context->block_no = (context->block_no + 1) % 256; - rc = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), end, - &written); - - if (!rc || (end != written)) + if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, data, size, s)) { - WLog_ERR(TAG, - "WTSVirtualChannelWrite failed! [stream length=%" PRIdz " - written=%" PRIu32, - end, written); error = ERROR_INTERNAL_ERROR; + goto out; + } + + format = &context->client_formats[formatNo]; + if (!rdpsnd_server_align_wave_pdu(s, format->nBlockAlign)) + { + error = ERROR_INTERNAL_ERROR; + goto out; } } + end = Stream_GetPosition(s); + Stream_SetPosition(s, 2); + Stream_Write_UINT16(s, end - 4); + + status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), end, + &written); + + if (!status || (end != written)) + { + WLog_ERR(TAG, "WTSVirtualChannelWrite failed! [stream length=%" PRIdz " - written=%" PRIu32, + end, written); + error = ERROR_INTERNAL_ERROR; + } + + context->block_no = (context->block_no + 1) % 256; + +out: Stream_SetPosition(s, 0); context->priv->out_pending_frames = 0; return error; @@ -537,8 +659,21 @@ static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, UINT16 wT /* Wrapper function to send WAVE or WAVE2 PDU depending on client connected */ static UINT rdpsnd_server_send_audio_pdu(RdpsndServerContext* context, UINT16 wTimestamp) { + const BYTE* src; + size_t length; + + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + + if (context->selected_client_format >= context->num_client_formats) + return ERROR_INTERNAL_ERROR; + + src = context->priv->out_buffer; + length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame; + if (context->clientVersion >= CHANNEL_VERSION_WIN_8) - return rdpsnd_server_send_wave2_pdu(context, wTimestamp); + return rdpsnd_server_send_wave2_pdu(context, context->selected_client_format, src, length, + FALSE, wTimestamp, wTimestamp); else return rdpsnd_server_send_wave_pdu(context, wTimestamp); } @@ -551,9 +686,11 @@ static UINT rdpsnd_server_send_audio_pdu(RdpsndServerContext* context, UINT16 wT static UINT rdpsnd_server_send_samples(RdpsndServerContext* context, const void* buf, int nframes, UINT16 wTimestamp) { - int cframes; - int cframesize; UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + EnterCriticalSection(&context->priv->lock); if (context->selected_client_format >= context->num_client_formats) @@ -566,12 +703,13 @@ static UINT rdpsnd_server_send_samples(RdpsndServerContext* context, const void* while (nframes > 0) { - cframes = MIN(nframes, context->priv->out_frames - context->priv->out_pending_frames); - cframesize = cframes * context->priv->src_bytes_per_frame; + const size_t cframes = + MIN(nframes, context->priv->out_frames - context->priv->out_pending_frames); + size_t cframesize = cframes * context->priv->src_bytes_per_frame; CopyMemory(context->priv->out_buffer + (context->priv->out_pending_frames * context->priv->src_bytes_per_frame), buf, cframesize); - buf = (BYTE*)buf + cframesize; + buf = (const BYTE*)buf + cframesize; nframes -= cframes; context->priv->out_pending_frames += cframes; @@ -590,6 +728,33 @@ out: return error; } +/** + * Send encoded audio samples using a Wave2 PDU. + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpsnd_server_send_samples2(RdpsndServerContext* context, UINT16 formatNo, + const void* buf, size_t size, UINT16 timestamp, + UINT32 audioTimeStamp) +{ + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + + if (context->clientVersion < CHANNEL_VERSION_WIN_8) + return ERROR_INTERNAL_ERROR; + + EnterCriticalSection(&context->priv->lock); + + error = + rdpsnd_server_send_wave2_pdu(context, formatNo, buf, size, TRUE, timestamp, audioTimeStamp); + + LeaveCriticalSection(&context->priv->lock); + + return error; +} + /** * Function description * @@ -597,21 +762,23 @@ out: */ static UINT rdpsnd_server_set_volume(RdpsndServerContext* context, int left, int right) { - size_t pos; + size_t len; BOOL status; ULONG written; - wStream* s = context->priv->rdpsnd_pdu; + wStream* s = rdpsnd_server_get_buffer(context); + + if (!Stream_EnsureRemainingCapacity(s, 8)) + return ERROR_NOT_ENOUGH_MEMORY; + Stream_Write_UINT8(s, SNDC_SETVOLUME); Stream_Write_UINT8(s, 0); - Stream_Seek_UINT16(s); + Stream_Write_UINT16(s, 4); /* Payload length */ Stream_Write_UINT16(s, left); Stream_Write_UINT16(s, right); - pos = Stream_GetPosition(s); - Stream_SetPosition(s, 2); - Stream_Write_UINT16(s, pos - 4); - Stream_SetPosition(s, pos); + len = Stream_GetPosition(s); + status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), - Stream_GetPosition(s), &written); + (ULONG)len, &written); Stream_SetPosition(s, 0); return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR; } @@ -626,8 +793,9 @@ static UINT rdpsnd_server_close(RdpsndServerContext* context) size_t pos; BOOL status; ULONG written; - wStream* s = context->priv->rdpsnd_pdu; UINT error = CHANNEL_RC_OK; + wStream* s = rdpsnd_server_get_buffer(context); + EnterCriticalSection(&context->priv->lock); if (context->priv->out_pending_frames > 0) @@ -649,6 +817,10 @@ static UINT rdpsnd_server_close(RdpsndServerContext* context) return error; context->selected_client_format = 0xFFFF; + + if (!Stream_EnsureRemainingCapacity(s, 4)) + return ERROR_OUTOFMEMORY; + Stream_Write_UINT8(s, SNDC_CLOSE); Stream_Write_UINT8(s, 0); Stream_Seek_UINT16(s); @@ -671,14 +843,21 @@ static UINT rdpsnd_server_start(RdpsndServerContext* context) { void* buffer = NULL; DWORD bytesReturned; - RdpsndServerPrivate* priv = context->priv; + RdpsndServerPrivate* priv; UINT error = ERROR_INTERNAL_ERROR; PULONG pSessionId = NULL; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + + priv = context->priv; priv->SessionId = WTS_CURRENT_SESSION; if (context->use_dynamic_virtual_channel) { + UINT32 channelId; + BOOL status = TRUE; + if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId, (LPSTR*)&pSessionId, &bytesReturned)) { @@ -691,6 +870,15 @@ static UINT rdpsnd_server_start(RdpsndServerContext* context) WLog_ERR(TAG, "Open audio dynamic virtual channel (AUDIO_PLAYBACK_DVC) failed!"); return ERROR_INTERNAL_ERROR; } + + channelId = WTSChannelGetIdByHandle(priv->ChannelHandle); + + IFCALLRET(context->ChannelIdAssigned, status, context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + goto out_close; + } } else { @@ -741,7 +929,7 @@ static UINT rdpsnd_server_start(RdpsndServerContext* context) goto out_pdu; } - if ((error = rdpsnd_server_send_formats(context, context->priv->rdpsnd_pdu))) + if ((error = rdpsnd_server_send_formats(context))) { WLog_ERR(TAG, "rdpsnd_server_send_formats failed with error %" PRIu32 "", error); goto out_lock; @@ -791,6 +979,12 @@ static UINT rdpsnd_server_stop(RdpsndServerContext* context) { UINT error = CHANNEL_RC_OK; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + + if (!context->priv->StopEvent) + return error; + if (context->priv->ownThread) { if (context->priv->StopEvent) @@ -806,36 +1000,46 @@ static UINT rdpsnd_server_stop(RdpsndServerContext* context) CloseHandle(context->priv->Thread); CloseHandle(context->priv->StopEvent); + context->priv->Thread = NULL; + context->priv->StopEvent = NULL; } } DeleteCriticalSection(&context->priv->lock); if (context->priv->rdpsnd_pdu) + { Stream_Free(context->priv->rdpsnd_pdu, TRUE); + context->priv->rdpsnd_pdu = NULL; + } + + if (context->priv->ChannelHandle) + { + WTSVirtualChannelClose(context->priv->ChannelHandle); + context->priv->ChannelHandle = NULL; + } return error; } RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm) { - RdpsndServerContext* context; RdpsndServerPrivate* priv; - context = (RdpsndServerContext*)calloc(1, sizeof(RdpsndServerContext)); + RdpsndServerContext* context = (RdpsndServerContext*)calloc(1, sizeof(RdpsndServerContext)); if (!context) - { - WLog_ERR(TAG, "calloc failed!"); - return NULL; - } + goto fail; context->vcm = vcm; context->Start = rdpsnd_server_start; context->Stop = rdpsnd_server_stop; context->selected_client_format = 0xFFFF; context->Initialize = rdpsnd_server_initialize; + context->SendFormats = rdpsnd_server_send_formats; context->SelectFormat = rdpsnd_server_select_format; + context->Training = rdpsnd_server_training; context->SendSamples = rdpsnd_server_send_samples; + context->SendSamples2 = rdpsnd_server_send_samples2; context->SetVolume = rdpsnd_server_set_volume; context->Close = rdpsnd_server_close; context->priv = priv = (RdpsndServerPrivate*)calloc(1, sizeof(RdpsndServerPrivate)); @@ -843,7 +1047,7 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm) if (!priv) { WLog_ERR(TAG, "calloc failed!"); - goto out_free; + goto fail; } priv->dsp_context = freerdp_dsp_context_new(TRUE); @@ -851,7 +1055,7 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm) if (!priv->dsp_context) { WLog_ERR(TAG, "freerdp_dsp_context_new failed!"); - goto out_free_priv; + goto fail; } priv->input_stream = Stream_New(NULL, 4); @@ -859,24 +1063,23 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm) if (!priv->input_stream) { WLog_ERR(TAG, "Stream_New failed!"); - goto out_free_dsp; + goto fail; } priv->expectedBytes = 4; priv->waitingHeader = TRUE; priv->ownThread = TRUE; return context; -out_free_dsp: - freerdp_dsp_context_free(priv->dsp_context); -out_free_priv: - free(context->priv); -out_free: - free(context); +fail: + rdpsnd_server_context_free(context); return NULL; } void rdpsnd_server_context_reset(RdpsndServerContext* context) { + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + context->priv->expectedBytes = 4; context->priv->waitingHeader = TRUE; Stream_SetPosition(context->priv->input_stream, 0); @@ -884,16 +1087,21 @@ void rdpsnd_server_context_reset(RdpsndServerContext* context) void rdpsnd_server_context_free(RdpsndServerContext* context) { - if (context->priv->ChannelHandle) - WTSVirtualChannelClose(context->priv->ChannelHandle); + if (!context) + return; - free(context->priv->out_buffer); + if (context->priv) + { + rdpsnd_server_stop(context); - if (context->priv->dsp_context) - freerdp_dsp_context_free(context->priv->dsp_context); + free(context->priv->out_buffer); - if (context->priv->input_stream) - Stream_Free(context->priv->input_stream, TRUE); + if (context->priv->dsp_context) + freerdp_dsp_context_free(context->priv->dsp_context); + + if (context->priv->input_stream) + Stream_Free(context->priv->input_stream, TRUE); + } free(context->server_formats); free(context->client_formats); @@ -903,6 +1111,9 @@ void rdpsnd_server_context_free(RdpsndServerContext* context) HANDLE rdpsnd_server_get_event_handle(RdpsndServerContext* context) { + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + return context->priv->channelEvent; } @@ -924,8 +1135,14 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context) { DWORD bytesReturned; UINT ret = CHANNEL_RC_OK; - RdpsndServerPrivate* priv = context->priv; - wStream* s = priv->input_stream; + RdpsndServerPrivate* priv; + wStream* s; + + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + + priv = context->priv; + s = priv->input_stream; if (!WTSVirtualChannelRead(priv->ChannelHandle, 0, (PCHAR)Stream_Pointer(s), priv->expectedBytes, &bytesReturned)) @@ -980,6 +1197,10 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context) ret = rdpsnd_server_recv_waveconfirm(context, s); break; + case SNDC_TRAINING: + ret = rdpsnd_server_recv_trainingconfirm(context, s); + break; + case SNDC_FORMATS: ret = rdpsnd_server_recv_formats(context, s); @@ -990,8 +1211,6 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context) case SNDC_QUALITYMODE: ret = rdpsnd_server_recv_quality_mode(context, s); - Stream_SetPosition(s, - 0); /* in case the Activated callback tries to treat some messages */ if ((ret == CHANNEL_RC_OK) && (context->clientVersion >= CHANNEL_VERSION_WIN_7)) IFCALL(context->Activated, context); diff --git a/channels/server/channels.c b/channels/server/channels.c index 3714c5e..2223ef8 100644 --- a/channels/server/channels.c +++ b/channels/server/channels.c @@ -50,9 +50,15 @@ #include #include #include +#include #include #include +#if defined(CHANNEL_RDPECAM_SERVER) +#include +#include +#endif + #if defined(CHANNEL_AINPUT_SERVER) #include #endif @@ -71,8 +77,13 @@ void freerdp_channels_dummy(void) RemdeskServerContext* remdesk; EncomspServerContext* encomsp; RailServerContext* rail; + TelemetryServerContext* telemetry; RdpgfxServerContext* rdpgfx; DispServerContext* disp; +#if defined (CHANNEL_RDPECAM_SERVER) + CamDevEnumServerContext* camera_enumerator; + CameraDeviceServerContext* camera_device; +#endif audin = audin_server_context_new(NULL); audin_server_context_free(audin); rdpsnd = rdpsnd_server_context_new(NULL); @@ -93,10 +104,20 @@ void freerdp_channels_dummy(void) encomsp_server_context_free(encomsp); rail = rail_server_context_new(NULL); rail_server_context_free(rail); + telemetry = telemetry_server_context_new(NULL); + telemetry_server_context_free(telemetry); rdpgfx = rdpgfx_server_context_new(NULL); rdpgfx_server_context_free(rdpgfx); disp = disp_server_context_new(NULL); disp_server_context_free(disp); + +#if defined (CHANNEL_RDPECAM_SERVER) + camera_enumerator = cam_dev_enum_server_context_new(NULL); + cam_dev_enum_server_context_free(camera_enumerator); + camera_device = camera_device_server_context_new(NULL); + camera_device_server_context_free(camera_device); +#endif + #if defined(CHANNEL_AINPUT_SERVER) { ainput_server_context* ainput = ainput_server_context_new(NULL); diff --git a/channels/telemetry/CMakeLists.txt b/channels/telemetry/CMakeLists.txt new file mode 100644 index 0000000..d2b4f24 --- /dev/null +++ b/channels/telemetry/CMakeLists.txt @@ -0,0 +1,22 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2022 Pascal Nowack +# +# 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. + +define_channel("telemetry") + +if(WITH_SERVER_CHANNELS) + add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME}) +endif() diff --git a/channels/telemetry/ChannelOptions.cmake b/channels/telemetry/ChannelOptions.cmake new file mode 100644 index 0000000..1b9e391 --- /dev/null +++ b/channels/telemetry/ChannelOptions.cmake @@ -0,0 +1,12 @@ + +set(OPTION_DEFAULT OFF) +set(OPTION_CLIENT_DEFAULT OFF) +set(OPTION_SERVER_DEFAULT ON) + +define_channel_options(NAME "telemetry" TYPE "dynamic" + DESCRIPTION "Telemetry Virtual Channel Extension" + SPECIFICATIONS "[MS-RDPET]" + DEFAULT ${OPTION_DEFAULT}) + +define_channel_server_options(${OPTION_SERVER_DEFAULT}) + diff --git a/channels/telemetry/server/CMakeLists.txt b/channels/telemetry/server/CMakeLists.txt new file mode 100644 index 0000000..75be8ac --- /dev/null +++ b/channels/telemetry/server/CMakeLists.txt @@ -0,0 +1,26 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2022 Pascal Nowack +# +# 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. + +define_channel_server("telemetry") + +set(${MODULE_PREFIX}_SRCS + telemetry_main.c) + +add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry") + +target_link_libraries(${MODULE_NAME} freerdp) +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server") diff --git a/channels/telemetry/server/telemetry_main.c b/channels/telemetry/server/telemetry_main.c new file mode 100644 index 0000000..7173603 --- /dev/null +++ b/channels/telemetry/server/telemetry_main.c @@ -0,0 +1,443 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Telemetry Virtual Channel Extension + * + * Copyright 2022 Pascal Nowack + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#define TAG CHANNELS_TAG("telemetry.server") + +typedef enum +{ + TELEMETRY_INITIAL, + TELEMETRY_OPENED, +} eTelemetryChannelState; + +typedef struct +{ + TelemetryServerContext context; + + HANDLE stopEvent; + + HANDLE thread; + void* telemetry_channel; + + DWORD SessionId; + + BOOL isOpened; + BOOL externalThread; + + /* Channel state */ + eTelemetryChannelState state; + + wStream* buffer; +} telemetry_server; + +static UINT telemetry_server_initialize(TelemetryServerContext* context, BOOL externalThread) +{ + UINT error = CHANNEL_RC_OK; + telemetry_server* telemetry = (telemetry_server*)context; + + WINPR_ASSERT(telemetry); + + if (telemetry->isOpened) + { + WLog_WARN(TAG, "Application error: TELEMETRY channel already initialized, " + "calling in this state is not possible!"); + return ERROR_INVALID_STATE; + } + + telemetry->externalThread = externalThread; + + return error; +} + +static UINT telemetry_server_open_channel(telemetry_server* telemetry) +{ + TelemetryServerContext* context = &telemetry->context; + DWORD Error = ERROR_SUCCESS; + HANDLE hEvent; + DWORD BytesReturned = 0; + PULONG pSessionId = NULL; + UINT32 channelId; + BOOL status = TRUE; + + WINPR_ASSERT(telemetry); + + if (WTSQuerySessionInformationA(telemetry->context.vcm, WTS_CURRENT_SESSION, WTSSessionId, + (LPSTR*)&pSessionId, &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSQuerySessionInformationA failed!"); + return ERROR_INTERNAL_ERROR; + } + + telemetry->SessionId = (DWORD)*pSessionId; + WTSFreeMemory(pSessionId); + hEvent = WTSVirtualChannelManagerGetEventHandle(telemetry->context.vcm); + + if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED) + { + Error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error); + return Error; + } + + telemetry->telemetry_channel = WTSVirtualChannelOpenEx( + telemetry->SessionId, TELEMETRY_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC); + if (!telemetry->telemetry_channel) + { + Error = GetLastError(); + WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error); + return Error; + } + + channelId = WTSChannelGetIdByHandle(telemetry->telemetry_channel); + + IFCALLRET(context->ChannelIdAssigned, status, context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + return ERROR_INTERNAL_ERROR; + } + + return Error; +} + +static UINT telemetry_server_recv_rdp_telemetry_pdu(TelemetryServerContext* context, wStream* s) +{ + TELEMETRY_RDP_TELEMETRY_PDU pdu; + UINT error = CHANNEL_RC_OK; + + if (Stream_GetRemainingLength(s) < 16) + { + WLog_ERR(TAG, "telemetry_server_recv_rdp_telemetry_pdu: Not enough data!"); + return ERROR_NO_DATA; + } + + Stream_Read_UINT32(s, pdu.PromptForCredentialsMillis); + Stream_Read_UINT32(s, pdu.PromptForCredentialsDoneMillis); + Stream_Read_UINT32(s, pdu.GraphicsChannelOpenedMillis); + Stream_Read_UINT32(s, pdu.FirstGraphicsReceivedMillis); + + IFCALLRET(context->RdpTelemetry, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->RdpTelemetry failed with error %" PRIu32 "", error); + + return error; +} + +static UINT telemetry_process_message(telemetry_server* telemetry) +{ + BOOL rc; + UINT error = ERROR_INTERNAL_ERROR; + ULONG BytesReturned; + BYTE MessageId; + BYTE Length; + wStream* s; + + WINPR_ASSERT(telemetry); + WINPR_ASSERT(telemetry->telemetry_channel); + + s = telemetry->buffer; + WINPR_ASSERT(s); + + Stream_SetPosition(s, 0); + rc = WTSVirtualChannelRead(telemetry->telemetry_channel, 0, NULL, 0, &BytesReturned); + if (!rc) + goto out; + + if (BytesReturned < 1) + { + error = CHANNEL_RC_OK; + goto out; + } + + if (!Stream_EnsureRemainingCapacity(s, BytesReturned)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + error = CHANNEL_RC_NO_MEMORY; + goto out; + } + + if (WTSVirtualChannelRead(telemetry->telemetry_channel, 0, (PCHAR)Stream_Buffer(s), + (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSVirtualChannelRead failed!"); + goto out; + } + + Stream_SetLength(s, BytesReturned); + if (!Stream_CheckAndLogRequiredLength(TAG, s, 2)) + return ERROR_NO_DATA; + + Stream_Read_UINT8(s, MessageId); + Stream_Read_UINT8(s, Length); + + switch (MessageId) + { + case 0x01: + error = telemetry_server_recv_rdp_telemetry_pdu(&telemetry->context, s); + break; + default: + WLog_ERR(TAG, "telemetry_process_message: unknown MessageId %" PRIu8 "", MessageId); + break; + } + +out: + if (error) + WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error); + + return error; +} + +static UINT telemetry_server_context_poll_int(TelemetryServerContext* context) +{ + telemetry_server* telemetry = (telemetry_server*)context; + UINT error = ERROR_INTERNAL_ERROR; + + WINPR_ASSERT(telemetry); + + switch (telemetry->state) + { + case TELEMETRY_INITIAL: + error = telemetry_server_open_channel(telemetry); + if (error) + WLog_ERR(TAG, "telemetry_server_open_channel failed with error %" PRIu32 "!", + error); + else + telemetry->state = TELEMETRY_OPENED; + break; + case TELEMETRY_OPENED: + error = telemetry_process_message(telemetry); + break; + } + + return error; +} + +static HANDLE telemetry_server_get_channel_handle(telemetry_server* telemetry) +{ + void* buffer = NULL; + DWORD BytesReturned = 0; + HANDLE ChannelEvent = NULL; + + WINPR_ASSERT(telemetry); + + if (WTSVirtualChannelQuery(telemetry->telemetry_channel, WTSVirtualEventHandle, &buffer, + &BytesReturned) == TRUE) + { + if (BytesReturned == sizeof(HANDLE)) + CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE)); + + WTSFreeMemory(buffer); + } + + return ChannelEvent; +} + +static DWORD WINAPI telemetry_server_thread_func(LPVOID arg) +{ + DWORD nCount; + HANDLE events[2] = { 0 }; + telemetry_server* telemetry = (telemetry_server*)arg; + UINT error = CHANNEL_RC_OK; + DWORD status; + + WINPR_ASSERT(telemetry); + + nCount = 0; + events[nCount++] = telemetry->stopEvent; + + while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0)) + { + switch (telemetry->state) + { + case TELEMETRY_INITIAL: + error = telemetry_server_context_poll_int(&telemetry->context); + if (error == CHANNEL_RC_OK) + { + events[1] = telemetry_server_get_channel_handle(telemetry); + nCount = 2; + } + break; + case TELEMETRY_OPENED: + status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); + switch (status) + { + case WAIT_OBJECT_0: + break; + case WAIT_OBJECT_0 + 1: + case WAIT_TIMEOUT: + error = telemetry_server_context_poll_int(&telemetry->context); + break; + + case WAIT_FAILED: + default: + error = ERROR_INTERNAL_ERROR; + break; + } + break; + } + } + + WTSVirtualChannelClose(telemetry->telemetry_channel); + telemetry->telemetry_channel = NULL; + + if (error && telemetry->context.rdpcontext) + setChannelError(telemetry->context.rdpcontext, error, + "telemetry_server_thread_func reported an error"); + + ExitThread(error); + return error; +} + +static UINT telemetry_server_open(TelemetryServerContext* context) +{ + telemetry_server* telemetry = (telemetry_server*)context; + + WINPR_ASSERT(telemetry); + + if (!telemetry->externalThread && (telemetry->thread == NULL)) + { + telemetry->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!telemetry->stopEvent) + { + WLog_ERR(TAG, "CreateEvent failed!"); + return ERROR_INTERNAL_ERROR; + } + + telemetry->thread = CreateThread(NULL, 0, telemetry_server_thread_func, telemetry, 0, NULL); + if (!telemetry->thread) + { + WLog_ERR(TAG, "CreateThread failed!"); + CloseHandle(telemetry->stopEvent); + telemetry->stopEvent = NULL; + return ERROR_INTERNAL_ERROR; + } + } + telemetry->isOpened = TRUE; + + return CHANNEL_RC_OK; +} + +static UINT telemetry_server_close(TelemetryServerContext* context) +{ + UINT error = CHANNEL_RC_OK; + telemetry_server* telemetry = (telemetry_server*)context; + + WINPR_ASSERT(telemetry); + + if (!telemetry->externalThread && telemetry->thread) + { + SetEvent(telemetry->stopEvent); + + if (WaitForSingleObject(telemetry->thread, INFINITE) == WAIT_FAILED) + { + error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error); + return error; + } + + CloseHandle(telemetry->thread); + CloseHandle(telemetry->stopEvent); + telemetry->thread = NULL; + telemetry->stopEvent = NULL; + } + if (telemetry->externalThread) + { + if (telemetry->state != TELEMETRY_INITIAL) + { + WTSVirtualChannelClose(telemetry->telemetry_channel); + telemetry->telemetry_channel = NULL; + telemetry->state = TELEMETRY_INITIAL; + } + } + telemetry->isOpened = FALSE; + + return error; +} + +static UINT telemetry_server_context_poll(TelemetryServerContext* context) +{ + telemetry_server* telemetry = (telemetry_server*)context; + + WINPR_ASSERT(telemetry); + + if (!telemetry->externalThread) + return ERROR_INTERNAL_ERROR; + + return telemetry_server_context_poll_int(context); +} + +static BOOL telemetry_server_context_handle(TelemetryServerContext* context, HANDLE* handle) +{ + telemetry_server* telemetry = (telemetry_server*)context; + + WINPR_ASSERT(telemetry); + WINPR_ASSERT(handle); + + if (!telemetry->externalThread) + return FALSE; + if (telemetry->state == TELEMETRY_INITIAL) + return FALSE; + + *handle = telemetry_server_get_channel_handle(telemetry); + + return TRUE; +} + +TelemetryServerContext* telemetry_server_context_new(HANDLE vcm) +{ + telemetry_server* telemetry = (telemetry_server*)calloc(1, sizeof(telemetry_server)); + + if (!telemetry) + return NULL; + + telemetry->context.vcm = vcm; + telemetry->context.Initialize = telemetry_server_initialize; + telemetry->context.Open = telemetry_server_open; + telemetry->context.Close = telemetry_server_close; + telemetry->context.Poll = telemetry_server_context_poll; + telemetry->context.ChannelHandle = telemetry_server_context_handle; + + telemetry->buffer = Stream_New(NULL, 4096); + if (!telemetry->buffer) + goto fail; + + return &telemetry->context; +fail: + telemetry_server_context_free(&telemetry->context); + return NULL; +} + +void telemetry_server_context_free(TelemetryServerContext* context) +{ + telemetry_server* telemetry = (telemetry_server*)context; + + if (telemetry) + { + telemetry_server_close(context); + Stream_Free(telemetry->buffer, TRUE); + } + + free(telemetry); +} diff --git a/channels/urbdrc/client/libusb/libusb_udevice.c b/channels/urbdrc/client/libusb/libusb_udevice.c index 1322a4f..505c31d 100644 --- a/channels/urbdrc/client/libusb/libusb_udevice.c +++ b/channels/urbdrc/client/libusb/libusb_udevice.c @@ -237,7 +237,7 @@ static void async_transfer_user_data_free(ASYNC_TRANSFER_USER_DATA* user_data) } } -static void func_iso_callback(struct libusb_transfer* transfer) +static void LIBUSB_CALL func_iso_callback(struct libusb_transfer* transfer) { ASYNC_TRANSFER_USER_DATA* user_data = (ASYNC_TRANSFER_USER_DATA*)transfer->user_data; const UINT32 streamID = stream_id_from_buffer(transfer); @@ -336,7 +336,7 @@ static const LIBUSB_ENDPOINT_DESCEIPTOR* func_get_ep_desc(LIBUSB_CONFIG_DESCRIPT return NULL; } -static void func_bulk_transfer_cb(struct libusb_transfer* transfer) +static void LIBUSB_CALL func_bulk_transfer_cb(struct libusb_transfer* transfer) { ASYNC_TRANSFER_USER_DATA* user_data; uint32_t streamID; diff --git a/channels/urbdrc/client/libusb/libusb_udevman.c b/channels/urbdrc/client/libusb/libusb_udevman.c index edfe74f..5f1e9e0 100644 --- a/channels/urbdrc/client/libusb/libusb_udevman.c +++ b/channels/urbdrc/client/libusb/libusb_udevman.c @@ -511,12 +511,7 @@ static BOOL filter_by_class(uint8_t bDeviceClass, uint8_t bDeviceSubClass) static BOOL append(char* dst, size_t length, const char* src) { - size_t slen = strlen(src); - size_t dlen = strnlen(dst, length); - if (dlen + slen >= length) - return FALSE; - strcat(dst, src); - return TRUE; + return winpr_str_append(src, dst, length, NULL); } static BOOL device_is_filtered(struct libusb_device* dev, @@ -865,7 +860,7 @@ static BOOL poll_libusb_events(UDEVMAN* udevman) return rc > 0; } -static DWORD poll_thread(LPVOID lpThreadParameter) +static DWORD WINAPI poll_thread(LPVOID lpThreadParameter) { libusb_hotplug_callback_handle handle; UDEVMAN* udevman = (UDEVMAN*)lpThreadParameter; diff --git a/client/Wayland/wlfreerdp.c b/client/Wayland/wlfreerdp.c index ac8e772..c7a9fca 100644 --- a/client/Wayland/wlfreerdp.c +++ b/client/Wayland/wlfreerdp.c @@ -225,6 +225,7 @@ static BOOL wl_post_connect(freerdp* instance) wlfContext* context; rdpSettings* settings; char* title = "FreeRDP"; + char* app_id = "wlfreerdp"; UINT32 w, h; if (!instance || !instance->context) @@ -266,6 +267,7 @@ static BOOL wl_post_connect(freerdp* instance) UwacWindowSetFullscreenState(window, NULL, instance->context->settings->Fullscreen); UwacWindowSetTitle(window, title); + UwacWindowSetAppId(window, app_id); UwacWindowSetOpaqueRegion(context->window, 0, 0, w, h); instance->update->BeginPaint = wl_begin_paint; instance->update->EndPaint = wl_end_paint; diff --git a/client/X11/xf_client.c b/client/X11/xf_client.c index c2704a4..bd3eb0d 100644 --- a/client/X11/xf_client.c +++ b/client/X11/xf_client.c @@ -815,8 +815,6 @@ void xf_lock_x11_(xfContext* xfc, const char* fkt) else XLockDisplay(xfc->display); - if (xfc->locked) - WLog_WARN(TAG, "%s:\t[%" PRIu32 "] recursive lock from %s", __FUNCTION__, xfc->locked, fkt); xfc->locked++; WLog_VRB(TAG, "%s:\t[%" PRIu32 "] from %s", __FUNCTION__, xfc->locked, fkt); } @@ -1256,6 +1254,7 @@ static BOOL xf_post_connect(freerdp* instance) context = instance->context; settings = instance->settings; update = context->update; + BOOL serverIsWindowsPlatform; if (!gdi_init(instance, xf_get_local_color_format(xfc, TRUE))) return FALSE; @@ -1325,7 +1324,8 @@ static BOOL xf_post_connect(freerdp* instance) update->SetKeyboardIndicators = xf_keyboard_set_indicators; update->SetKeyboardImeStatus = xf_keyboard_set_ime_status; - if (!(xfc->clipboard = xf_clipboard_new(xfc))) + serverIsWindowsPlatform = (settings->OsMajorType == OSMAJORTYPE_WINDOWS); + if (!(xfc->clipboard = xf_clipboard_new(xfc, !serverIsWindowsPlatform))) return FALSE; if (!(xfc->xfDisp = xf_disp_new(xfc))) diff --git a/client/X11/xf_cliprdr.c b/client/X11/xf_cliprdr.c index 85a1736..37ed538 100644 --- a/client/X11/xf_cliprdr.c +++ b/client/X11/xf_cliprdr.c @@ -1756,7 +1756,27 @@ static UINT xf_cliprdr_clipboard_file_range_failure(wClipboardDelegate* delegate return clipboard->context->ClientFileContentsResponse(clipboard->context, &response); } -xfClipboard* xf_clipboard_new(xfContext* xfc) +static BOOL xf_cliprdr_clipboard_is_valid_unix_filename(LPCWSTR filename) +{ + LPCWSTR c; + + if (!filename) + return FALSE; + + if (filename[0] == L'\0') + return FALSE; + + /* Reserved characters */ + for (c = filename; *c; ++c) + { + if (*c == L'/') + return FALSE; + } + + return TRUE; +} + +xfClipboard* xf_clipboard_new(xfContext* xfc, BOOL relieveFilenameRestriction) { int i, n = 0; rdpChannels* channels; @@ -1885,6 +1905,13 @@ xfClipboard* xf_clipboard_new(xfContext* xfc) clipboard->delegate->ClipboardFileSizeFailure = xf_cliprdr_clipboard_file_size_failure; clipboard->delegate->ClipboardFileRangeSuccess = xf_cliprdr_clipboard_file_range_success; clipboard->delegate->ClipboardFileRangeFailure = xf_cliprdr_clipboard_file_range_failure; + + if (relieveFilenameRestriction) + { + WLog_DBG(TAG, "Relieving CLIPRDR filename restriction"); + clipboard->delegate->IsFileNameComponentValid = xf_cliprdr_clipboard_is_valid_unix_filename; + } + return clipboard; error: diff --git a/client/X11/xf_cliprdr.h b/client/X11/xf_cliprdr.h index f2262f7..7f571c4 100644 --- a/client/X11/xf_cliprdr.h +++ b/client/X11/xf_cliprdr.h @@ -25,7 +25,7 @@ #include -xfClipboard* xf_clipboard_new(xfContext* xfc); +xfClipboard* xf_clipboard_new(xfContext* xfc, BOOL relieveFilenameRestriction); void xf_clipboard_free(xfClipboard* clipboard); void xf_cliprdr_init(xfContext* xfc, CliprdrClientContext* cliprdr); diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c index 99577b1..bdcdcb5 100644 --- a/client/X11/xf_event.c +++ b/client/X11/xf_event.c @@ -24,6 +24,8 @@ #include #include +#include + #include #include @@ -33,6 +35,7 @@ #include "xf_disp.h" #include "xf_input.h" #include "xf_gfx.h" +#include "xf_graphics.h" #include "xf_event.h" #include "xf_input.h" @@ -537,6 +540,7 @@ static BOOL xf_event_FocusIn(xfContext* xfc, const XFocusInEvent* event, BOOL ap /* Release all keys, should already be done at FocusOut but might be missed * if the WM decided to use an alternate event order */ xf_keyboard_release_all_keypress(xfc); + xf_pointer_update_scale(xfc); if (app) { @@ -668,6 +672,9 @@ static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* even rdpSettings* settings; settings = xfc->context.settings; + WLog_DBG(TAG, "%s: x=%" PRId32 ", y=%" PRId32 ", w=%" PRId32 ", h=%" PRId32, __func__, event->x, + event->y, event->width, event->height); + if (!app) { if (!xfc->window) @@ -710,43 +717,42 @@ static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* even /* ask the server to resize using the display channel */ xf_disp_handle_configureNotify(xfc, alignedWidth, alignedHeight); } - - return TRUE; } - - appWindow = xf_AppWindowFromX11Window(xfc, event->window); - - if (appWindow) + else { - /* - * ConfigureNotify coordinates are expressed relative to the window parent. - * Translate these to root window coordinates. - */ - XTranslateCoordinates(xfc->display, appWindow->handle, RootWindowOfScreen(xfc->screen), 0, - 0, &appWindow->x, &appWindow->y, &childWindow); - appWindow->width = event->width; - appWindow->height = event->height; + appWindow = xf_AppWindowFromX11Window(xfc, event->window); - /* - * Additional checks for not in a local move and not ignoring configure to send - * position update to server, also should the window not be focused then do not - * send to server yet (i.e. resizing using window decoration). - * The server will be updated when the window gets refocused. - */ - if (appWindow->decorations) + if (appWindow) { - /* moving resizing using window decoration */ - xf_rail_adjust_position(xfc, appWindow); - } - else - { - if ((!event->send_event || appWindow->local_move.state == LMS_NOT_ACTIVE) && - !appWindow->rail_ignore_configure && xfc->focused) + /* + * ConfigureNotify coordinates are expressed relative to the window parent. + * Translate these to root window coordinates. + */ + XTranslateCoordinates(xfc->display, appWindow->handle, RootWindowOfScreen(xfc->screen), + 0, 0, &appWindow->x, &appWindow->y, &childWindow); + appWindow->width = event->width; + appWindow->height = event->height; + + /* + * Additional checks for not in a local move and not ignoring configure to send + * position update to server, also should the window not be focused then do not + * send to server yet (i.e. resizing using window decoration). + * The server will be updated when the window gets refocused. + */ + if (appWindow->decorations) + { + /* moving resizing using window decoration */ xf_rail_adjust_position(xfc, appWindow); + } + else + { + if ((!event->send_event || appWindow->local_move.state == LMS_NOT_ACTIVE) && + !appWindow->rail_ignore_configure && xfc->focused) + xf_rail_adjust_position(xfc, appWindow); + } } } - - return TRUE; + return xf_pointer_update_scale(xfc); } static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app) @@ -759,14 +765,14 @@ static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app) { appWindow = xf_AppWindowFromX11Window(xfc, event->window); - if (appWindow) + if (appWindow && (appWindow->rail_state == WINDOW_SHOW)) { /* local restore event */ /* This is now handled as part of the PropertyNotify * Doing this here would inhibit the ability to restore a maximized window * that is minimized back to the maximized state */ - xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); + // xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); appWindow->is_mapped = TRUE; } } @@ -777,6 +783,10 @@ static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app) static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL app) { xfAppWindow* appWindow; + + WINPR_ASSERT(xfc); + WINPR_ASSERT(event); + xf_keyboard_release_all_keypress(xfc); if (!app) @@ -796,6 +806,9 @@ static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, BOOL app) { + WINPR_ASSERT(xfc); + WINPR_ASSERT(event); + /* * This section handles sending the appropriate commands to the rail server * when the window has been minimized, maximized, restored locally @@ -830,18 +843,27 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, if (status) { + if (appWindow) + { + appWindow->maxVert = FALSE; + appWindow->maxHorz = FALSE; + } for (i = 0; i < nitems; i++) { if ((Atom)((UINT16**)prop)[i] == XInternAtom(xfc->display, "_NET_WM_STATE_MAXIMIZED_VERT", False)) { maxVert = TRUE; + if (appWindow) + appWindow->maxVert = TRUE; } if ((Atom)((UINT16**)prop)[i] == XInternAtom(xfc->display, "_NET_WM_STATE_MAXIMIZED_HORZ", False)) { maxHorz = TRUE; + if (appWindow) + appWindow->maxHorz = TRUE; } } @@ -858,9 +880,17 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, { /* If the window is in the iconic state */ if (((UINT32)*prop == 3)) + { minimized = TRUE; + if (appWindow) + appWindow->minimized = TRUE; + } else + { minimized = FALSE; + if (appWindow) + appWindow->minimized = FALSE; + } minimizedChanged = TRUE; XFree(prop); @@ -869,22 +899,30 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, if (app) { - if (maxVert && maxHorz && !minimized && - (appWindow->rail_state != WINDOW_SHOW_MAXIMIZED)) + WINPR_ASSERT(appWindow); + if (appWindow->maxVert && appWindow->maxHorz && !appWindow->minimized) { - appWindow->rail_state = WINDOW_SHOW_MAXIMIZED; - xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MAXIMIZE); + if (appWindow->rail_state != WINDOW_SHOW_MAXIMIZED) + { + appWindow->rail_state = WINDOW_SHOW_MAXIMIZED; + xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MAXIMIZE); + } } - else if (minimized && (appWindow->rail_state != WINDOW_SHOW_MINIMIZED)) + else if (appWindow->minimized) { - appWindow->rail_state = WINDOW_SHOW_MINIMIZED; - xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MINIMIZE); + if (appWindow->rail_state != WINDOW_SHOW_MINIMIZED) + { + appWindow->rail_state = WINDOW_SHOW_MINIMIZED; + xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MINIMIZE); + } } - else if (!minimized && !maxVert && !maxHorz && (appWindow->rail_state != WINDOW_SHOW) && - (appWindow->rail_state != WINDOW_HIDE)) + else { - appWindow->rail_state = WINDOW_SHOW; - xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); + if (appWindow->rail_state != WINDOW_SHOW && appWindow->rail_state != WINDOW_HIDE) + { + appWindow->rail_state = WINDOW_SHOW; + xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); + } } } else if (minimizedChanged) diff --git a/client/X11/xf_graphics.c b/client/X11/xf_graphics.c index 11c6d59..7050569 100644 --- a/client/X11/xf_graphics.c +++ b/client/X11/xf_graphics.c @@ -30,6 +30,9 @@ #include #endif +#include +#include + #include #include @@ -42,6 +45,8 @@ #include #define TAG CLIENT_TAG("x11") +static BOOL xf_Pointer_Set(rdpContext* context, const rdpPointer* pointer); + BOOL xf_decode_color(xfContext* xfc, const UINT32 srcColor, XColor* color) { rdpGdi* gdi; @@ -227,14 +232,14 @@ static BOOL xf_Bitmap_SetSurface(rdpContext* context, rdpBitmap* bitmap, BOOL pr return TRUE; } -static BOOL _xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpPointer* pointer, - Cursor* cursor) +static BOOL xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpPointer* pointer, + Cursor* cursor) { #ifdef WITH_XCURSOR UINT32 CursorFormat; xfContext* xfc = (xfContext*)context; xfPointer* xpointer = (xfPointer*)pointer; - XcursorImage ci; + XcursorImage ci = { 0 }; rdpSettings* settings; UINT32 xTargetSize; UINT32 yTargetSize; @@ -256,6 +261,9 @@ static BOOL _xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpP xTargetSize = pointer->width * xscale; yTargetSize = pointer->height * yscale; + WLog_DBG(TAG, "%s: scaled: %" PRIu32 "x%" PRIu32 ", desktop: %" PRIu32 "x%" PRIu32, __func__, + xfc->scaledWidth, xfc->savedHeight, settings->DesktopWidth, settings->DesktopHeight); + for (int i = 0; i < xpointer->nCursors; i++) { if (xpointer->cursorWidths[i] == xTargetSize && xpointer->cursorHeights[i] == yTargetSize) @@ -297,7 +305,6 @@ static BOOL _xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpP } } - ZeroMemory(&ci, sizeof(ci)); ci.version = XCURSOR_IMAGE_VERSION; ci.size = sizeof(ci); ci.width = xTargetSize; @@ -306,14 +313,21 @@ static BOOL _xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpP ci.yhot = pointer->yPos * yscale; size = ci.height * ci.width * GetBytesPerPixel(CursorFormat) * 1ULL; - if (xscale != 1 || yscale != 1) + if (!(ci.pixels = (XcursorPixel*)_aligned_malloc(size, 16))) { - if (!(ci.pixels = (XcursorPixel*)_aligned_malloc(size, 16))) - { - xf_unlock_x11(xfc); - return FALSE; - } + xf_unlock_x11(xfc); + return FALSE; + } + const double xs = fabs(fabs(xscale) - 1.0); + const double ys = fabs(fabs(yscale) - 1.0); + WLog_DBG(TAG, + "%s: cursorIndex %" PRId32 " scaling pointer %" PRIu32 "x%" PRIu32 " --> %" PRIu32 + "x%" PRIu32 " [%lfx%lf]", + __func__, cursorIndex, pointer->width, pointer->height, ci.width, ci.height, + xscale, yscale); + if ((xs > DBL_EPSILON) || (ys > DBL_EPSILON)) + { if (!freerdp_image_scale((BYTE*)ci.pixels, CursorFormat, 0, 0, 0, ci.width, ci.height, (BYTE*)xpointer->cursorPixels, CursorFormat, 0, 0, 0, pointer->width, pointer->height)) @@ -325,7 +339,7 @@ static BOOL _xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpP } else { - ci.pixels = xpointer->cursorPixels; + memcpy(ci.pixels, xpointer->cursorPixels, size); } cursorIndex = xpointer->nCursors; @@ -333,11 +347,14 @@ static BOOL _xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpP xpointer->cursorHeights[cursorIndex] = ci.height; xpointer->cursors[cursorIndex] = XcursorImageLoadCursor(xfc->display, &ci); xpointer->nCursors += 1; - if (xscale != 1 || yscale != 1) - _aligned_free(ci.pixels); + _aligned_free(ci.pixels); xf_unlock_x11(xfc); } + else + { + WLog_DBG(TAG, "%s: using cached cursor %" PRId32, __func__, cursorIndex); + } cursor[0] = xpointer->cursors[cursorIndex]; #endif @@ -372,8 +389,21 @@ static Window xf_Pointer_get_window(xfContext* xfc) } } +BOOL xf_pointer_update_scale(xfContext* xfc) +{ + xfPointer* pointer; + WINPR_ASSERT(xfc); + + pointer = xfc->pointer; + if (!pointer) + return TRUE; + + return xf_Pointer_Set(&xfc->context, &xfc->pointer->pointer); +} + static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer) { + BOOL rc = FALSE; #ifdef WITH_XCURSOR UINT32 CursorFormat; size_t size; @@ -381,7 +411,7 @@ static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer) xfPointer* xpointer = (xfPointer*)pointer; if (!context || !pointer || !context->gdi) - return FALSE; + goto fail; if (!xfc->invert) CursorFormat = (!xfc->big_endian) ? PIXEL_FORMAT_RGBA32 : PIXEL_FORMAT_ABGR32; @@ -394,7 +424,7 @@ static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer) size = pointer->height * pointer->width * GetBytesPerPixel(CursorFormat) * 1ULL; if (!(xpointer->cursorPixels = (XcursorPixel*)_aligned_malloc(size, 16))) - return FALSE; + goto fail; if (!freerdp_image_copy_from_pointer_data( (BYTE*)xpointer->cursorPixels, CursorFormat, 0, 0, 0, pointer->width, pointer->height, @@ -404,15 +434,18 @@ static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer) _aligned_free(xpointer->cursorPixels); return FALSE; } + rc = TRUE; - if (!_xf_Pointer_GetCursorForCurrentScale(context, pointer, &(xpointer->cursor))) - return FALSE; #endif - return TRUE; +fail: + WLog_DBG(TAG, "%s: %ld", __func__, rc ? pointer : -1); + return rc; } static void xf_Pointer_Free(rdpContext* context, rdpPointer* pointer) { + WLog_DBG(TAG, "%s: %p", __func__, pointer); + #ifdef WITH_XCURSOR xfContext* xfc = (xfContext*)context; xfPointer* xpointer = (xfPointer*)pointer; @@ -438,6 +471,8 @@ static void xf_Pointer_Free(rdpContext* context, rdpPointer* pointer) static BOOL xf_Pointer_Set(rdpContext* context, const rdpPointer* pointer) { + WLog_DBG(TAG, "%s: %p", __func__, pointer); + #ifdef WITH_XCURSOR xfContext* xfc = (xfContext*)context; Window handle = xf_Pointer_get_window(xfc); @@ -447,18 +482,23 @@ static BOOL xf_Pointer_Set(rdpContext* context, const rdpPointer* pointer) if (handle) { - if (!_xf_Pointer_GetCursorForCurrentScale(context, pointer, &(xfc->pointer->cursor))) + if (!xf_Pointer_GetCursorForCurrentScale(context, pointer, &(xfc->pointer->cursor))) return FALSE; xf_lock_x11(xfc); XDefineCursor(xfc->display, handle, xfc->pointer->cursor); xf_unlock_x11(xfc); } + else + { + WLog_WARN(TAG, "%s: handle=%ld", __func__, handle); + } #endif return TRUE; } static BOOL xf_Pointer_SetNull(rdpContext* context) { + WLog_DBG(TAG, "%s", __func__); #ifdef WITH_XCURSOR xfContext* xfc = (xfContext*)context; static Cursor nullcursor = None; @@ -515,10 +555,11 @@ static BOOL xf_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y) if (!handle) { - WLog_WARN(TAG, "xf_Pointer_SetPosition: focus %d, handle%lu", xfc->focused, handle); + WLog_WARN(TAG, "%s: focus %d, handle%lu", __func__, xfc->focused, handle); return TRUE; } + WLog_DBG(TAG, "%s: %" PRIu32 "x%" PRIu32, __func__, x, y); if (xfc->remote_app && !xfc->focused) return TRUE; @@ -529,7 +570,7 @@ static BOOL xf_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y) rc = XGetWindowAttributes(xfc->display, handle, ¤t); if (rc == 0) { - WLog_WARN(TAG, "xf_Pointer_SetPosition: XGetWindowAttributes==%d", rc); + WLog_WARN(TAG, "%s: XGetWindowAttributes==%d", __func__, rc); goto out; } @@ -538,17 +579,17 @@ static BOOL xf_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y) rc = XChangeWindowAttributes(xfc->display, handle, CWEventMask, &tmp); if (rc == 0) { - WLog_WARN(TAG, "xf_Pointer_SetPosition: XChangeWindowAttributes==%d", rc); + WLog_WARN(TAG, "%s: XChangeWindowAttributes==%d", __func__, rc); goto out; } rc = XWarpPointer(xfc->display, None, handle, 0, 0, 0, 0, x, y); if (rc == 0) - WLog_WARN(TAG, "xf_Pointer_SetPosition: XWarpPointer==%d", rc); + WLog_WARN(TAG, "%s: XWarpPointer==%d", __func__, rc); tmp.event_mask = current.your_event_mask; rc = XChangeWindowAttributes(xfc->display, handle, CWEventMask, &tmp); if (rc == 0) - WLog_WARN(TAG, "xf_Pointer_SetPosition: 2.try XChangeWindowAttributes==%d", rc); + WLog_WARN(TAG, "%s: 2.try XChangeWindowAttributes==%d", __func__, rc); ret = TRUE; out: xf_unlock_x11(xfc); diff --git a/client/X11/xf_graphics.h b/client/X11/xf_graphics.h index 303e116..96e1d98 100644 --- a/client/X11/xf_graphics.h +++ b/client/X11/xf_graphics.h @@ -29,4 +29,6 @@ BOOL xf_register_graphics(rdpGraphics* graphics); BOOL xf_decode_color(xfContext* xfc, const UINT32 srcColor, XColor* color); UINT32 xf_get_local_color_format(xfContext* xfc, BOOL aligned); +BOOL xf_pointer_update_scale(xfContext* xfc); + #endif /* FREERDP_CLIENT_X11_GRAPHICS_H */ diff --git a/client/X11/xf_keyboard.c b/client/X11/xf_keyboard.c index 26a1fd2..377e9bd 100644 --- a/client/X11/xf_keyboard.c +++ b/client/X11/xf_keyboard.c @@ -400,18 +400,19 @@ static int xf_keyboard_execute_action_script(xfContext* xfc, XF_MODIFIER_KEYS* m } if (mod->Shift) - strcat(combination, "Shift+"); + winpr_str_append("Shift", combination, sizeof(combination), "+"); if (mod->Ctrl) - strcat(combination, "Ctrl+"); + winpr_str_append("Ctrl", combination, sizeof(combination), "+"); if (mod->Alt) - strcat(combination, "Alt+"); + winpr_str_append("Alt", combination, sizeof(combination), "+"); if (mod->Super) - strcat(combination, "Super+"); + winpr_str_append("Super", combination, sizeof(combination), "+"); + + winpr_str_append(keyStr, combination, sizeof(combination), NULL); - strcat(combination, keyStr); count = ArrayList_Count(xfc->keyCombinations); for (index = 0; index < count; index++) diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c index 770234f..090f599 100644 --- a/client/X11/xf_rail.c +++ b/client/X11/xf_rail.c @@ -136,10 +136,10 @@ void xf_rail_adjust_position(xfContext* xfc, xfAppWindow* appWindow) * Calculate new size/position for the rail window(new values for * windowOffsetX/windowOffsetY/windowWidth/windowHeight) on the server */ - windowMove.left = appWindow->x; - windowMove.top = appWindow->y; - windowMove.right = windowMove.left + appWindow->width; - windowMove.bottom = windowMove.top + appWindow->height; + windowMove.left = appWindow->x - appWindow->resizeMarginLeft; + windowMove.top = appWindow->y - appWindow->resizeMarginTop; + windowMove.right = appWindow->x + appWindow->width + appWindow->resizeMarginRight; + windowMove.bottom = appWindow->y + appWindow->height + appWindow->resizeMarginBottom; xfc->rail->ClientWindowMove(xfc->rail, &windowMove); } } @@ -163,12 +163,12 @@ void xf_rail_end_local_move(xfContext* xfc, xfAppWindow* appWindow) * windowOffsetX/windowOffsetY/windowWidth/windowHeight) on the server * */ - windowMove.left = appWindow->x; - windowMove.top = appWindow->y; + windowMove.left = appWindow->x - appWindow->resizeMarginLeft; + windowMove.top = appWindow->y - appWindow->resizeMarginTop; windowMove.right = - windowMove.left + - appWindow->width; /* In the update to RDP the position is one past the window */ - windowMove.bottom = windowMove.top + appWindow->height; + appWindow->x + + appWindow->width + appWindow->resizeMarginRight; /* In the update to RDP the position is one past the window */ + windowMove.bottom = appWindow->y + appWindow->height + appWindow->resizeMarginBottom; xfc->rail->ClientWindowMove(xfc->rail, &windowMove); /* * Simulate button up at new position to end the local move (per RDP spec) @@ -344,6 +344,18 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO* appWindow->windowWidth = windowState->windowWidth; appWindow->windowHeight = windowState->windowHeight; } + + if (fieldFlags & WINDOW_ORDER_FIELD_RESIZE_MARGIN_X) + { + appWindow->resizeMarginLeft = windowState->resizeMarginLeft; + appWindow->resizeMarginRight = windowState->resizeMarginRight; + } + + if (fieldFlags & WINDOW_ORDER_FIELD_RESIZE_MARGIN_Y) + { + appWindow->resizeMarginTop = windowState->resizeMarginTop; + appWindow->resizeMarginBottom = windowState->resizeMarginBottom; + } if (fieldFlags & WINDOW_ORDER_FIELD_OWNER) { diff --git a/client/X11/xf_window.c b/client/X11/xf_window.c index 5c50a07..9b5b1c4 100644 --- a/client/X11/xf_window.c +++ b/client/X11/xf_window.c @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -775,6 +776,9 @@ int xf_AppWindowCreate(xfContext* xfc, xfAppWindow* appWindow) appWindow->is_mapped = FALSE; appWindow->is_transient = FALSE; appWindow->rail_state = 0; + appWindow->maxVert = FALSE; + appWindow->maxHorz = FALSE; + appWindow->minimized = FALSE; appWindow->rail_ignore_configure = FALSE; appWindow->handle = XCreateWindow(xfc->display, RootWindowOfScreen(xfc->screen), appWindow->x, appWindow->y, appWindow->width, appWindow->height, 0, @@ -930,6 +934,9 @@ void xf_MoveWindow(xfContext* xfc, xfAppWindow* appWindow, int x, int y, int wid void xf_ShowWindow(xfContext* xfc, xfAppWindow* appWindow, BYTE state) { + WINPR_ASSERT(xfc); + WINPR_ASSERT(appWindow); + switch (state) { case WINDOW_HIDE: @@ -937,11 +944,14 @@ void xf_ShowWindow(xfContext* xfc, xfAppWindow* appWindow, BYTE state) break; case WINDOW_SHOW_MINIMIZED: + appWindow->minimized = TRUE; XIconifyWindow(xfc->display, appWindow->handle, xfc->screen_number); break; case WINDOW_SHOW_MAXIMIZED: /* Set the window as maximized */ + appWindow->maxHorz = TRUE; + appWindow->maxVert = TRUE; xf_SendClientEvent(xfc, appWindow->handle, xfc->_NET_WM_STATE, 4, _NET_WM_STATE_ADD, xfc->_NET_WM_STATE_MAXIMIZED_VERT, xfc->_NET_WM_STATE_MAXIMIZED_HORZ, 0); diff --git a/client/X11/xf_window.h b/client/X11/xf_window.h index 3adaa3c..0f85af1 100644 --- a/client/X11/xf_window.h +++ b/client/X11/xf_window.h @@ -126,6 +126,11 @@ struct xf_app_window UINT32 localWindowOffsetCorrX; UINT32 localWindowOffsetCorrY; + + UINT32 resizeMarginLeft; + UINT32 resizeMarginTop; + UINT32 resizeMarginRight; + UINT32 resizeMarginBottom; GC gc; int shmid; @@ -137,6 +142,9 @@ struct xf_app_window BOOL is_transient; xfLocalMove local_move; BYTE rail_state; + BOOL maxVert; + BOOL maxHorz; + BOOL minimized; BOOL rail_ignore_configure; }; diff --git a/client/common/client.c b/client/common/client.c index 12317dc..6862deb 100644 --- a/client/common/client.c +++ b/client/common/client.c @@ -782,7 +782,7 @@ BOOL client_auto_reconnect_ex(freerdp* instance, BOOL (*window_events)(freerdp* if (freerdp_reconnect(instance)) return TRUE; - switch (freerdp_get_last_error(instance)) + switch (freerdp_get_last_error(instance->context)) { case FREERDP_ERROR_CONNECT_CANCELLED: WLog_WARN(TAG, "Autoreconnect aborted by user"); diff --git a/client/common/cmdline.c b/client/common/cmdline.c index 77fcf81..d2d949b 100644 --- a/client/common/cmdline.c +++ b/client/common/cmdline.c @@ -211,6 +211,7 @@ static BOOL copy_value(const char* value, char** dst) static BOOL append_value(const char* value, char** dst) { size_t x = 0, y; + size_t size; char* tmp; if (!dst || !value) return FALSE; @@ -218,14 +219,16 @@ static BOOL append_value(const char* value, char** dst) if (*dst) x = strlen(*dst); y = strlen(value); - tmp = realloc(*dst, x + y + 2); + + size = x + y + 2; + tmp = realloc(*dst, size); if (!tmp) return FALSE; if (x == 0) tmp[0] = '\0'; else - strcat(tmp, ","); - strcat(tmp, value); + winpr_str_append(",", tmp, size, NULL); + winpr_str_append(value, tmp, size, NULL); *dst = tmp; return TRUE; } @@ -2915,6 +2918,12 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, settings->TlsSecLevel = (UINT32)val; } + CommandLineSwitchCase(arg, "enforce-tlsv1_2") + { + if (!(freerdp_settings_set_uint16(settings, FreeRDP_TLSMinVersion, TLS1_2_VERSION) && + freerdp_settings_set_uint16(settings, FreeRDP_TLSMaxVersion, TLS1_2_VERSION))) + return COMMAND_LINE_ERROR_UNEXPECTED_VALUE; + } CommandLineSwitchCase(arg, "cert") { int rc = 0; diff --git a/client/common/cmdline.h b/client/common/cmdline.h index 9a60e3d..92a86f9 100644 --- a/client/common/cmdline.h +++ b/client/common/cmdline.h @@ -353,6 +353,9 @@ static const COMMAND_LINE_ARGUMENT_A args[] = { "Allowed TLS ciphers" }, { "tls-seclevel", COMMAND_LINE_VALUE_REQUIRED, "", "1", NULL, -1, NULL, "TLS security level - defaults to 1" }, + { "enforce-tlsv1_2", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, + "Force use of TLS1.2 for connection. Some servers have a buggy TLS version negotiation and " + "might fail without this" }, { "toggle-fullscreen", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Alt+Ctrl+Enter to toggle fullscreen" }, { "tune", COMMAND_LINE_VALUE_REQUIRED, ",", "", NULL, -1, NULL, diff --git a/config.h.in b/config.h.in index 1f35d68..801b085 100644 --- a/config.h.in +++ b/config.h.in @@ -111,6 +111,9 @@ #cmakedefine CHANNEL_RDPDR #cmakedefine CHANNEL_RDPDR_CLIENT #cmakedefine CHANNEL_RDPDR_SERVER +#cmakedefine CHANNEL_RDPECAM +#cmakedefine CHANNEL_RDPECAM_CLIENT +#cmakedefine CHANNEL_RDPECAM_SERVER #cmakedefine CHANNEL_RDPEI #cmakedefine CHANNEL_RDPEI_CLIENT #cmakedefine CHANNEL_RDPEI_SERVER @@ -132,6 +135,9 @@ #cmakedefine CHANNEL_SSHAGENT #cmakedefine CHANNEL_SSHAGENT_CLIENT #cmakedefine CHANNEL_SSHAGENT_SERVER +#cmakedefine CHANNEL_TELEMETRY +#cmakedefine CHANNEL_TELEMETRY_CLIENT +#cmakedefine CHANNEL_TELEMETRY_SERVER #cmakedefine CHANNEL_TSMF #cmakedefine CHANNEL_TSMF_CLIENT #cmakedefine CHANNEL_TSMF_SERVER diff --git a/include/freerdp/channels/cliprdr.h b/include/freerdp/channels/cliprdr.h index 86fc658..fbf23f6 100644 --- a/include/freerdp/channels/cliprdr.h +++ b/include/freerdp/channels/cliprdr.h @@ -22,6 +22,7 @@ #include #include +#include #include @@ -93,17 +94,6 @@ extern "C" { #endif - FREERDP_API UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length, - FILEDESCRIPTORW** file_descriptor_array, - UINT32* file_descriptor_count); - FREERDP_API UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array, - UINT32 file_descriptor_count, BYTE** format_data, - UINT32* format_data_length); - FREERDP_API UINT cliprdr_serialize_file_list_ex(UINT32 flags, - const FILEDESCRIPTORW* file_descriptor_array, - UINT32 file_descriptor_count, - BYTE** format_data, UINT32* format_data_length); - #ifdef __cplusplus } #endif diff --git a/include/freerdp/channels/rdpecam.h b/include/freerdp/channels/rdpecam.h new file mode 100644 index 0000000..2d5ae40 --- /dev/null +++ b/include/freerdp/channels/rdpecam.h @@ -0,0 +1,336 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Video Capture Virtual Channel Extension + * + * Copyright 2022 Pascal Nowack + * + * 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. + */ + +#ifndef FREERDP_CHANNEL_RDPECAM_H +#define FREERDP_CHANNEL_RDPECAM_H + +#include +#include +#include + +#define RDPECAM_DVC_CHANNEL_NAME "rdpecam" +#define RDPECAM_CONTROL_DVC_CHANNEL_NAME "RDCamera_Device_Enumerator" + +typedef enum +{ + CAM_MSG_ID_SuccessResponse = 0x01, + CAM_MSG_ID_ErrorResponse = 0x02, + CAM_MSG_ID_SelectVersionRequest = 0x03, + CAM_MSG_ID_SelectVersionResponse = 0x04, + CAM_MSG_ID_DeviceAddedNotification = 0x05, + CAM_MSG_ID_DeviceRemovedNotification = 0x06, + CAM_MSG_ID_ActivateDeviceRequest = 0x07, + CAM_MSG_ID_DeactivateDeviceRequest = 0x08, + CAM_MSG_ID_StreamListRequest = 0x09, + CAM_MSG_ID_StreamListResponse = 0x0A, + CAM_MSG_ID_MediaTypeListRequest = 0x0B, + CAM_MSG_ID_MediaTypeListResponse = 0x0C, + CAM_MSG_ID_CurrentMediaTypeRequest = 0x0D, + CAM_MSG_ID_CurrentMediaTypeResponse = 0x0E, + CAM_MSG_ID_StartStreamsRequest = 0x0F, + CAM_MSG_ID_StopStreamsRequest = 0x10, + CAM_MSG_ID_SampleRequest = 0x11, + CAM_MSG_ID_SampleResponse = 0x12, + CAM_MSG_ID_SampleErrorResponse = 0x13, + CAM_MSG_ID_PropertyListRequest = 0x14, + CAM_MSG_ID_PropertyListResponse = 0x15, + CAM_MSG_ID_PropertyValueRequest = 0x16, + CAM_MSG_ID_PropertyValueResponse = 0x17, + CAM_MSG_ID_SetPropertyValueRequest = 0x18, +} CAM_MSG_ID; + +#define CAM_HEADER_SIZE 2 + +typedef struct +{ + BYTE Version; + CAM_MSG_ID MessageId; +} CAM_SHARED_MSG_HEADER; + +/* Messages Exchanged on the Device Enumeration Channel (2.2.2) */ + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; +} CAM_SELECT_VERSION_REQUEST; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; +} CAM_SELECT_VERSION_RESPONSE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + WCHAR* DeviceName; + char* VirtualChannelName; +} CAM_DEVICE_ADDED_NOTIFICATION; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + char* VirtualChannelName; +} CAM_DEVICE_REMOVED_NOTIFICATION; + +/* Messages Exchanged on Device Channels (2.2.3) */ + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; +} CAM_SUCCESS_RESPONSE; + +typedef enum +{ + CAM_ERROR_CODE_UnexpectedError = 0x00000001, + CAM_ERROR_CODE_InvalidMessage = 0x00000002, + CAM_ERROR_CODE_NotInitialized = 0x00000003, + CAM_ERROR_CODE_InvalidRequest = 0x00000004, + CAM_ERROR_CODE_InvalidStreamNumber = 0x00000005, + CAM_ERROR_CODE_InvalidMediaType = 0x00000006, + CAM_ERROR_CODE_OutOfMemory = 0x00000007, + CAM_ERROR_CODE_ItemNotFound = 0x00000008, + CAM_ERROR_CODE_SetNotFound = 0x00000009, + CAM_ERROR_CODE_OperationNotSupported = 0x0000000A, +} CAM_ERROR_CODE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + CAM_ERROR_CODE ErrorCode; +} CAM_ERROR_RESPONSE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; +} CAM_ACTIVATE_DEVICE_REQUEST; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; +} CAM_DEACTIVATE_DEVICE_REQUEST; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; +} CAM_STREAM_LIST_REQUEST; + +typedef enum +{ + CAM_STREAM_FRAME_SOURCE_TYPE_Color = 0x0001, + CAM_STREAM_FRAME_SOURCE_TYPE_Infrared = 0x0002, + CAM_STREAM_FRAME_SOURCE_TYPE_Custom = 0x0008, +} CAM_STREAM_FRAME_SOURCE_TYPES; + +typedef enum +{ + CAM_STREAM_CATEGORY_Capture = 0x01, +} CAM_STREAM_CATEGORY; + +typedef struct +{ + CAM_STREAM_FRAME_SOURCE_TYPES FrameSourceTypes; + CAM_STREAM_CATEGORY StreamCategory; + BYTE Selected; + BYTE CanBeShared; +} CAM_STREAM_DESCRIPTION; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + BYTE N_Descriptions; + CAM_STREAM_DESCRIPTION StreamDescriptions[255]; +} CAM_STREAM_LIST_RESPONSE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + BYTE StreamIndex; +} CAM_MEDIA_TYPE_LIST_REQUEST; + +typedef enum +{ + CAM_MEDIA_FORMAT_H264 = 0x01, + CAM_MEDIA_FORMAT_MJPG = 0x02, + CAM_MEDIA_FORMAT_YUY2 = 0x03, + CAM_MEDIA_FORMAT_NV12 = 0x04, + CAM_MEDIA_FORMAT_I420 = 0x05, + CAM_MEDIA_FORMAT_RGB24 = 0x06, + CAM_MEDIA_FORMAT_RGB32 = 0x07, +} CAM_MEDIA_FORMAT; + +typedef enum +{ + CAM_MEDIA_TYPE_DESCRIPTION_FLAG_DecodingRequired = 0x01, + CAM_MEDIA_TYPE_DESCRIPTION_FLAG_BottomUpImage = 0x02, +} CAM_MEDIA_TYPE_DESCRIPTION_FLAGS; + +typedef struct +{ + CAM_MEDIA_FORMAT Format; + UINT32 Width; + UINT32 Height; + UINT32 FrameRateNumerator; + UINT32 FrameRateDenominator; + UINT32 PixelAspectRatioNumerator; + UINT32 PixelAspectRatioDenominator; + CAM_MEDIA_TYPE_DESCRIPTION_FLAGS Flags; +} CAM_MEDIA_TYPE_DESCRIPTION; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + size_t N_Descriptions; + CAM_MEDIA_TYPE_DESCRIPTION* MediaTypeDescriptions; +} CAM_MEDIA_TYPE_LIST_RESPONSE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + BYTE StreamIndex; +} CAM_CURRENT_MEDIA_TYPE_REQUEST; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + CAM_MEDIA_TYPE_DESCRIPTION MediaTypeDescription; +} CAM_CURRENT_MEDIA_TYPE_RESPONSE; + +typedef struct +{ + BYTE StreamIndex; + CAM_MEDIA_TYPE_DESCRIPTION MediaTypeDescription; +} CAM_START_STREAM_INFO; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + BYTE N_Infos; + CAM_START_STREAM_INFO StartStreamsInfo[255]; +} CAM_START_STREAMS_REQUEST; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; +} CAM_STOP_STREAMS_REQUEST; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + BYTE StreamIndex; +} CAM_SAMPLE_REQUEST; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + BYTE StreamIndex; + size_t SampleSize; + BYTE* Sample; +} CAM_SAMPLE_RESPONSE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + BYTE StreamIndex; + CAM_ERROR_CODE ErrorCode; +} CAM_SAMPLE_ERROR_RESPONSE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; +} CAM_PROPERTY_LIST_REQUEST; + +typedef enum +{ + CAM_PROPERTY_SET_CameraControl = 0x01, + CAM_PROPERTY_SET_VideoProcAmp = 0x02, +} CAM_PROPERTY_SET; + +/* CameraControl properties */ +#define CAM_PROPERTY_ID_CAMERA_CONTROL_Exposure 0x01 +#define CAM_PROPERTY_ID_CAMERA_CONTROL_Focus 0x02 +#define CAM_PROPERTY_ID_CAMERA_CONTROL_Pan 0x03 +#define CAM_PROPERTY_ID_CAMERA_CONTROL_Roll 0x04 +#define CAM_PROPERTY_ID_CAMERA_CONTROL_Tilt 0x05 +#define CAM_PROPERTY_ID_CAMERA_CONTROL_Zoom 0x06 + +/* VideoProcAmp properties */ +#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_BacklightCompensation 0x01 +#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_Brightness 0x02 +#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_Contrast 0x03 +#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_Hue 0x04 +#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_WhiteBalance 0x05 + +typedef enum +{ + CAM_PROPERTY_CAPABILITY_Manual = 0x01, + CAM_PROPERTY_CAPABILITY_Auto = 0x02, +} CAM_PROPERTY_CAPABILITIES; + +typedef struct +{ + CAM_PROPERTY_SET PropertySet; + BYTE PropertyId; + CAM_PROPERTY_CAPABILITIES Capabilities; + INT32 MinValue; + INT32 MaxValue; + INT32 Step; + INT32 DefaultValue; +} CAM_PROPERTY_DESCRIPTION; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + size_t N_Properties; + CAM_PROPERTY_DESCRIPTION* Properties; +} CAM_PROPERTY_LIST_RESPONSE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + CAM_PROPERTY_SET PropertySet; + BYTE PropertyId; +} CAM_PROPERTY_VALUE_REQUEST; + +typedef enum +{ + CAM_PROPERTY_MODE_Manual = 0x01, + CAM_PROPERTY_MODE_Auto = 0x02, +} CAM_PROPERTY_MODE; + +typedef struct +{ + CAM_PROPERTY_MODE Mode; + INT32 Value; +} CAM_PROPERTY_VALUE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + CAM_PROPERTY_VALUE PropertyValue; +} CAM_PROPERTY_VALUE_RESPONSE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + CAM_PROPERTY_SET PropertySet; + BYTE PropertyId; + CAM_PROPERTY_VALUE PropertyValue; +} CAM_SET_PROPERTY_VALUE_REQUEST; + +#endif /* FREERDP_CHANNEL_RDPECAM_H */ diff --git a/include/freerdp/channels/rdpgfx.h b/include/freerdp/channels/rdpgfx.h index 095022d..b7511f8 100644 --- a/include/freerdp/channels/rdpgfx.h +++ b/include/freerdp/channels/rdpgfx.h @@ -99,11 +99,16 @@ typedef struct _RDPGFX_HEADER RDPGFX_HEADER; #define RDPGFX_CAPVERSION_103 0x000A0301 /** [MS-RDPEGFX] 2.2.3.6 */ #define RDPGFX_CAPVERSION_104 0x000A0400 /** [MS-RDPEGFX] 2.2.3.7 */ #define RDPGFX_CAPVERSION_105 0x000A0502 /** [MS-RDPEGFX] 2.2.3.8 */ -#define RDPGFX_CAPVERSION_106 \ - 0x000A0600 /** [MS-RDPEGFX] 2.2.3.9, [MS-RDPEGFX-errata] 2018-12-10 \ +#define RDPGFX_CAPVERSION_106 \ + 0x000A0600 /** [MS-RDPEGFX] 2.2.3.9 (the value in the doc is wrong, see \ + * [MS-RDPEGFX]-180912-errata] \ + * Since this is/was documented for a long time, also define \ + * the incorrect value in case some server actually uses it. \ */ +#define RDPGFX_CAPVERSION_106_ERR 0x000A0601 +#define RDPGFX_CAPVERSION_107 0x000A0701 /** [MS-RDPEGFX] 2.2.3.10 */ -#define RDPGFX_NUMBER_CAPSETS 9 +#define RDPGFX_NUMBER_CAPSETS 11 #define RDPGFX_CAPSET_BASE_SIZE 8 struct _RDPGFX_CAPSET @@ -119,6 +124,7 @@ typedef struct _RDPGFX_CAPSET RDPGFX_CAPSET; #define RDPGFX_CAPS_FLAG_AVC420_ENABLED 0x00000010U /* 8.1+ */ #define RDPGFX_CAPS_FLAG_AVC_DISABLED 0x00000020U /* 10.0+ */ #define RDPGFX_CAPS_FLAG_AVC_THINCLIENT 0x00000040U /* 10.3+ */ +#define RDPGFX_CAPS_FLAG_SCALEDMAP_DISABLE 0x00000080U /* 10.7+ */ struct _RDPGFX_CAPSET_VERSION8 { diff --git a/include/freerdp/channels/telemetry.h b/include/freerdp/channels/telemetry.h new file mode 100644 index 0000000..85ad1ff --- /dev/null +++ b/include/freerdp/channels/telemetry.h @@ -0,0 +1,38 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Telemetry Virtual Channel Extension + * + * Copyright 2022 Pascal Nowack + * + * 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. + */ + +#ifndef FREERDP_CHANNEL_TELEMETRY_H +#define FREERDP_CHANNEL_TELEMETRY_H + +#include +#include +#include + +#define TELEMETRY_DVC_CHANNEL_NAME "Microsoft::Windows::RDS::Telemetry" + +struct _TELEMETRY_RDP_TELEMETRY_PDU +{ + UINT32 PromptForCredentialsMillis; + UINT32 PromptForCredentialsDoneMillis; + UINT32 GraphicsChannelOpenedMillis; + UINT32 FirstGraphicsReceivedMillis; +}; +typedef struct _TELEMETRY_RDP_TELEMETRY_PDU TELEMETRY_RDP_TELEMETRY_PDU; + +#endif /* FREERDP_CHANNEL_TELEMETRY_H */ diff --git a/include/freerdp/channels/wtsvc.h b/include/freerdp/channels/wtsvc.h index fe93787..286c752 100644 --- a/include/freerdp/channels/wtsvc.h +++ b/include/freerdp/channels/wtsvc.h @@ -52,6 +52,9 @@ extern "C" DRDYNVC_STATE_FAILED = 3 }; + typedef BOOL (*psDVCCreationStatusCallback)(void* userdata, UINT32 channelId, + INT32 creationStatus); + /** * WTSVirtualChannelManager functions are FreeRDP extensions to the API. */ @@ -62,6 +65,9 @@ extern "C" FREERDP_API HANDLE WTSVirtualChannelManagerGetEventHandle(HANDLE hServer); FREERDP_API BOOL WTSVirtualChannelManagerIsChannelJoined(HANDLE hServer, const char* name); FREERDP_API BYTE WTSVirtualChannelManagerGetDrdynvcState(HANDLE hServer); + FREERDP_API void WTSVirtualChannelManagerSetDVCCreationCallback(HANDLE hServer, + psDVCCreationStatusCallback cb, + void* userdata); /** * Extended FreeRDP WTS functions for channel handling @@ -76,6 +82,10 @@ extern "C" FREERDP_API void* WTSChannelGetHandleByName(freerdp_peer* client, const char* channel_name); FREERDP_API void* WTSChannelGetHandleById(freerdp_peer* client, const UINT16 channel_id); FREERDP_API const char* WTSChannelGetName(freerdp_peer* client, UINT16 channel_id); + FREERDP_API INT64 WTSChannelGetOptions(freerdp_peer* client, UINT16 channel_id); + FREERDP_API char** WTSGetAcceptedChannelNames(freerdp_peer* client, size_t* count); + + FREERDP_API UINT32 WTSChannelGetIdByHandle(HANDLE hChannelHandle); #ifdef __cplusplus } diff --git a/include/freerdp/client/rail.h b/include/freerdp/client/rail.h index 0ce7dc3..ef5aefd 100644 --- a/include/freerdp/client/rail.h +++ b/include/freerdp/client/rail.h @@ -86,6 +86,8 @@ typedef UINT (*pcRailServerGetAppidResponseExtended)(RailClientContext* context, const RAIL_GET_APPID_RESP_EX* id); typedef UINT (*pcRailClientCompartmentInfo)(RailClientContext* context, const RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo); +typedef UINT (*pcRailClientTextScale)(RailClientContext* context, UINT32 TextScale); +typedef UINT (*pcRailClientCaretBlinkRate)(RailClientContext* context, UINT32 CaretBlinkRate); struct _rail_client_context { @@ -121,6 +123,8 @@ struct _rail_client_context pcRailServerGetAppidResponseExtended ServerGetAppidResponseExtended; pcRailClientCompartmentInfo ClientCompartmentInfo; pcRailOnOpen OnOpen; + pcRailClientTextScale ClientTextScale; + pcRailClientCaretBlinkRate ClientCaretBlinkRate; }; #endif /* FREERDP_CHANNEL_RAIL_CLIENT_RAIL_H */ diff --git a/include/freerdp/constants.h b/include/freerdp/constants.h index d7949ef..44fb1cb 100644 --- a/include/freerdp/constants.h +++ b/include/freerdp/constants.h @@ -45,6 +45,10 @@ enum RDP_CODEC_ID #define OSMAJORTYPE_OS2 0x0002 #define OSMAJORTYPE_MACINTOSH 0x0003 #define OSMAJORTYPE_UNIX 0x0004 +#define OSMAJORTYPE_IOS 0x0005 +#define OSMAJORTYPE_OSX 0x0006 +#define OSMAJORTYPE_ANDROID 0x0007 +#define OSMAJORTYPE_CHROME_OS 0x0008 /** * OSMinorType @@ -58,6 +62,8 @@ enum RDP_CODEC_ID #define OSMINORTYPE_MACINTOSH 0x0006 #define OSMINORTYPE_NATIVE_XSERVER 0x0007 #define OSMINORTYPE_PSEUDO_XSERVER 0x0008 -#define OSMINORTYPE_NATIVE_WAYLAND 0x0009 +#define OSMINORTYPE_WINDOWS_RT 0x0009 +/* As of 2022-03-29 the following does not exist officially in [MS-RDPBCGR] */ +#define OSMINORTYPE_NATIVE_WAYLAND (0xFFFF - 1) #endif /* FREERDP_CONSTANTS_H */ diff --git a/include/freerdp/peer.h b/include/freerdp/peer.h index a640444..4176f25 100644 --- a/include/freerdp/peer.h +++ b/include/freerdp/peer.h @@ -143,6 +143,9 @@ extern "C" FREERDP_API BOOL freerdp_peer_context_new(freerdp_peer* client); FREERDP_API void freerdp_peer_context_free(freerdp_peer* client); + FREERDP_API const char* freerdp_peer_os_major_type_string(freerdp_peer* client); + FREERDP_API const char* freerdp_peer_os_minor_type_string(freerdp_peer* client); + FREERDP_API freerdp_peer* freerdp_peer_new(int sockfd); FREERDP_API void freerdp_peer_free(freerdp_peer* client); diff --git a/include/freerdp/rail.h b/include/freerdp/rail.h index e532082..fefffc1 100644 --- a/include/freerdp/rail.h +++ b/include/freerdp/rail.h @@ -135,15 +135,19 @@ enum SPI_MASK #endif /* Client Information PDU */ -#define TS_RAIL_CLIENTSTATUS_ALLOWLOCALMOVESIZE 0x00000001 -#define TS_RAIL_CLIENTSTATUS_AUTORECONNECT 0x00000002 -#define TS_RAIL_CLIENTSTATUS_ZORDER_SYNC 0x00000004 -#define TS_RAIL_CLIENTSTATUS_WINDOW_RESIZE_MARGIN_SUPPORTED 0x00000010 -#define TS_RAIL_CLIENTSTATUS_HIGH_DPI_ICONS_SUPPORTED 0x00000020 -#define TS_RAIL_CLIENTSTATUS_APPBAR_REMOTING_SUPPORTED 0x00000040 -#define TS_RAIL_CLIENTSTATUS_POWER_DISPLAY_REQUEST_SUPPORTED 0x00000080 -#define TS_RAIL_CLIENTSTATUS_GET_APPID_RESPONSE_EX_SUPPORTED 0x00000100 -#define TS_RAIL_CLIENTSTATUS_BIDIRECTIONAL_CLOAK_SUPPORTED 0x00000200 +typedef enum +{ + TS_RAIL_CLIENTSTATUS_ALLOWLOCALMOVESIZE = 0x00000001, + TS_RAIL_CLIENTSTATUS_AUTORECONNECT = 0x00000002, + TS_RAIL_CLIENTSTATUS_ZORDER_SYNC = 0x00000004, + TS_RAIL_CLIENTSTATUS_WINDOW_RESIZE_MARGIN_SUPPORTED = 0x00000010, + TS_RAIL_CLIENTSTATUS_HIGH_DPI_ICONS_SUPPORTED = 0x00000020, + TS_RAIL_CLIENTSTATUS_APPBAR_REMOTING_SUPPORTED = 0x00000040, + TS_RAIL_CLIENTSTATUS_POWER_DISPLAY_REQUEST_SUPPORTED = 0x00000080, + TS_RAIL_CLIENTSTATUS_GET_APPID_RESPONSE_EX_SUPPORTED = 0x00000100, + TS_RAIL_CLIENTSTATUS_BIDIRECTIONAL_CLOAK_SUPPORTED = 0x00000200, + TS_RAIL_CLIENTSTATUS_SUPPRESS_ICON_ORDERS = 0x00000400 +} CLIENT_INFO_PDU; /* Server Move/Size Start PDU */ #define RAIL_WMSZ_LEFT 0x0001 @@ -182,10 +186,15 @@ enum SPI_MASK #endif /* Extended Handshake Flags */ -#define TS_RAIL_ORDER_HANDSHAKEEX_FLAGS_HIDEF 0x00000001 -#define TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_EXTENDED_SPI_SUPPORTED 0x00000002 -#define TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_SNAP_ARRANGE_SUPPORTED 0x00000004 - +typedef enum +{ + TS_RAIL_ORDER_HANDSHAKEEX_FLAGS_HIDEF = 0x00000001, + TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_EXTENDED_SPI_SUPPORTED = 0x00000002, + TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_SNAP_ARRANGE_SUPPORTED = 0x00000004, + TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_TEXT_SCALE_SUPPORTED = 0x00000008, + TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_CARET_BLINK_SUPPORTED = 0x00000010, + TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_EXTENDED_SPI_2_SUPPORTED = 0x00000020 +} EXTENDED_HANDSHAKE_FLAGS; /* Language Profile Information Flags */ #define TF_PROFILETYPE_INPUTPROCESSOR 0x00000001 #define TF_PROFILETYPE_KEYBOARDLAYOUT 0x00000002 @@ -560,30 +569,35 @@ typedef struct _RAIL_GET_APPID_RESP_EX RAIL_GET_APPID_RESP_EX; /* RAIL Constants */ -#define TS_RAIL_ORDER_EXEC 0x0001 -#define TS_RAIL_ORDER_ACTIVATE 0x0002 -#define TS_RAIL_ORDER_SYSPARAM 0x0003 -#define TS_RAIL_ORDER_SYSCOMMAND 0x0004 -#define TS_RAIL_ORDER_HANDSHAKE 0x0005 -#define TS_RAIL_ORDER_NOTIFY_EVENT 0x0006 -#define TS_RAIL_ORDER_WINDOWMOVE 0x0008 -#define TS_RAIL_ORDER_LOCALMOVESIZE 0x0009 -#define TS_RAIL_ORDER_MINMAXINFO 0x000A -#define TS_RAIL_ORDER_CLIENTSTATUS 0x000B -#define TS_RAIL_ORDER_SYSMENU 0x000C -#define TS_RAIL_ORDER_LANGBARINFO 0x000D -#define TS_RAIL_ORDER_GET_APPID_REQ 0x000E -#define TS_RAIL_ORDER_GET_APPID_RESP 0x000F -#define TS_RAIL_ORDER_TASKBARINFO 0x0010 -#define TS_RAIL_ORDER_LANGUAGEIMEINFO 0x0011 -#define TS_RAIL_ORDER_COMPARTMENTINFO 0x0012 -#define TS_RAIL_ORDER_HANDSHAKE_EX 0x0013 -#define TS_RAIL_ORDER_ZORDER_SYNC 0x0014 -#define TS_RAIL_ORDER_CLOAK 0x0015 -#define TS_RAIL_ORDER_POWER_DISPLAY_REQUEST 0x0016 -#define TS_RAIL_ORDER_SNAP_ARRANGE 0x0017 -#define TS_RAIL_ORDER_GET_APPID_RESP_EX 0x0018 -#define TS_RAIL_ORDER_EXEC_RESULT 0x0080 +typedef enum +{ + TS_RAIL_ORDER_EXEC = 0x0001, + TS_RAIL_ORDER_ACTIVATE = 0x0002, + TS_RAIL_ORDER_SYSPARAM = 0x0003, + TS_RAIL_ORDER_SYSCOMMAND = 0x0004, + TS_RAIL_ORDER_HANDSHAKE = 0x0005, + TS_RAIL_ORDER_NOTIFY_EVENT = 0x0006, + TS_RAIL_ORDER_WINDOWMOVE = 0x0008, + TS_RAIL_ORDER_LOCALMOVESIZE = 0x0009, + TS_RAIL_ORDER_MINMAXINFO = 0x000A, + TS_RAIL_ORDER_CLIENTSTATUS = 0x000B, + TS_RAIL_ORDER_SYSMENU = 0x000C, + TS_RAIL_ORDER_LANGBARINFO = 0x000D, + TS_RAIL_ORDER_GET_APPID_REQ = 0x000E, + TS_RAIL_ORDER_GET_APPID_RESP = 0x000F, + TS_RAIL_ORDER_TASKBARINFO = 0x0010, + TS_RAIL_ORDER_LANGUAGEIMEINFO = 0x0011, + TS_RAIL_ORDER_COMPARTMENTINFO = 0x0012, + TS_RAIL_ORDER_HANDSHAKE_EX = 0x0013, + TS_RAIL_ORDER_ZORDER_SYNC = 0x0014, + TS_RAIL_ORDER_CLOAK = 0x0015, + TS_RAIL_ORDER_POWER_DISPLAY_REQUEST = 0x0016, + TS_RAIL_ORDER_SNAP_ARRANGE = 0x0017, + TS_RAIL_ORDER_GET_APPID_RESP_EX = 0x0018, + TS_RAIL_ORDER_TEXTSCALEINFO = 0x0019, + TS_RAIL_ORDER_CARETBLINKINFO = 0x001A, + TS_RAIL_ORDER_EXEC_RESULT = 0x0080 +} ORDER_TYPE; #ifdef __cplusplus extern "C" diff --git a/include/freerdp/server/ainput.h b/include/freerdp/server/ainput.h index 39acad1..200de56 100644 --- a/include/freerdp/server/ainput.h +++ b/include/freerdp/server/ainput.h @@ -22,6 +22,7 @@ #define FREERDP_CHANNEL_AINPUT_SERVER_H #include +#include typedef enum AINPUT_SERVER_OPEN_RESULT { @@ -33,6 +34,8 @@ typedef enum AINPUT_SERVER_OPEN_RESULT typedef struct _ainput_server_context ainput_server_context; +typedef BOOL (*psAInputChannelIdAssigned)(ainput_server_context* context, UINT32 channelId); + typedef UINT (*psAInputServerInitialize)(ainput_server_context* context, BOOL externalThread); typedef UINT (*psAInputServerPoll)(ainput_server_context* context); typedef BOOL (*psAInputServerChannelHandle)(ainput_server_context* context, HANDLE* handle); @@ -97,6 +100,11 @@ struct _ainput_server_context psAInputServerMouseEvent MouseEvent; rdpContext* rdpcontext; + + /** + * Callback, when the channel got its id assigned. + */ + psAInputChannelIdAssigned ChannelIdAssigned; }; #ifdef __cplusplus diff --git a/include/freerdp/server/audin.h b/include/freerdp/server/audin.h index a8da17d..f9c581f 100644 --- a/include/freerdp/server/audin.h +++ b/include/freerdp/server/audin.h @@ -28,6 +28,8 @@ typedef struct _audin_server_context audin_server_context; +typedef BOOL (*psAudinServerChannelIdAssigned)(audin_server_context* context, UINT32 channelId); + typedef UINT (*psAudinServerSelectFormat)(audin_server_context* context, size_t client_format_index); typedef BOOL (*psAudinServerOpen)(audin_server_context* context); @@ -99,6 +101,11 @@ struct _audin_server_context psAudinServerReceiveSamples ReceiveSamples; rdpContext* rdpcontext; + + /** + * Callback, when the channel got its id assigned. + */ + psAudinServerChannelIdAssigned ChannelIdAssigned; }; #ifdef __cplusplus diff --git a/include/freerdp/server/disp.h b/include/freerdp/server/disp.h index 43aea64..ab7e09b 100644 --- a/include/freerdp/server/disp.h +++ b/include/freerdp/server/disp.h @@ -29,6 +29,8 @@ typedef struct _disp_server_private DispServerPrivate; typedef struct _disp_server_context DispServerContext; +typedef BOOL (*psDispChannelIdAssigned)(DispServerContext* context, UINT32 channelId); + typedef UINT (*psDispMonitorLayout)(DispServerContext* context, const DISPLAY_CONTROL_MONITOR_LAYOUT_PDU* pdu); typedef UINT (*psDispCaps)(DispServerContext* context); @@ -53,6 +55,11 @@ struct _disp_server_context DispServerPrivate* priv; rdpContext* rdpcontext; + + /** + * Callback, when the channel got its id assigned. + */ + psDispChannelIdAssigned ChannelIdAssigned; }; #ifdef __cplusplus diff --git a/include/freerdp/server/echo.h b/include/freerdp/server/echo.h index 0643a59..bbaeb58 100644 --- a/include/freerdp/server/echo.h +++ b/include/freerdp/server/echo.h @@ -34,6 +34,8 @@ typedef enum ECHO_SERVER_OPEN_RESULT typedef struct _echo_server_context echo_server_context; +typedef BOOL (*psEchoServerChannelIdAssigned)(echo_server_context* context, UINT32 channelId); + typedef UINT (*psEchoServerOpen)(echo_server_context* context); typedef UINT (*psEchoServerClose)(echo_server_context* context); typedef BOOL (*psEchoServerRequest)(echo_server_context* context, const BYTE* buffer, @@ -76,6 +78,11 @@ struct _echo_server_context psEchoServerResponse Response; rdpContext* rdpcontext; + + /** + * Callback, when the channel got its id assigned. + */ + psEchoServerChannelIdAssigned ChannelIdAssigned; }; #ifdef __cplusplus diff --git a/include/freerdp/server/rdpecam-enumerator.h b/include/freerdp/server/rdpecam-enumerator.h new file mode 100644 index 0000000..3f1b770 --- /dev/null +++ b/include/freerdp/server/rdpecam-enumerator.h @@ -0,0 +1,134 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Video Capture Virtual Channel Extension + * + * Copyright 2022 Pascal Nowack + * + * 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. + */ + +#ifndef FREERDP_CHANNEL_CAM_DEV_ENUM_SERVER_CAM_DEV_ENUM_H +#define FREERDP_CHANNEL_CAM_DEV_ENUM_SERVER_CAM_DEV_ENUM_H + +#include +#include + +typedef struct _cam_dev_enum_server_context CamDevEnumServerContext; + +typedef UINT (*psCamDevEnumServerServerOpen)(CamDevEnumServerContext* context); +typedef UINT (*psCamDevEnumServerServerClose)(CamDevEnumServerContext* context); + +typedef BOOL (*psCamDevEnumServerServerChannelIdAssigned)(CamDevEnumServerContext* context, + UINT32 channelId); + +typedef UINT (*psCamDevEnumServerServerInitialize)(CamDevEnumServerContext* context, + BOOL externalThread); +typedef UINT (*psCamDevEnumServerServerPoll)(CamDevEnumServerContext* context); +typedef BOOL (*psCamDevEnumServerServerChannelHandle)(CamDevEnumServerContext* context, + HANDLE* handle); + +typedef UINT (*psCamDevEnumServerServerSelectVersionRequest)( + CamDevEnumServerContext* context, const CAM_SELECT_VERSION_REQUEST* selectVersionRequest); +typedef UINT (*psCamDevEnumServerServerSelectVersionResponse)( + CamDevEnumServerContext* context, const CAM_SELECT_VERSION_RESPONSE* selectVersionResponse); + +typedef UINT (*psCamDevEnumServerServerDeviceAddedNotification)( + CamDevEnumServerContext* context, const CAM_DEVICE_ADDED_NOTIFICATION* deviceAddedNotification); +typedef UINT (*psCamDevEnumServerServerDeviceRemovedNotification)( + CamDevEnumServerContext* context, + const CAM_DEVICE_REMOVED_NOTIFICATION* deviceRemovedNotification); + +struct _cam_dev_enum_server_context +{ + HANDLE vcm; + + /* Server self-defined pointer. */ + void* userdata; + + /*** APIs called by the server. ***/ + + /** + * Optional: Set thread handling. + * When externalThread=TRUE, the application is responsible to call + * Poll() periodically to process channel events. + * + * Defaults to externalThread=FALSE + */ + psCamDevEnumServerServerInitialize Initialize; + + /** + * Open the camera device enumerator channel. + */ + psCamDevEnumServerServerOpen Open; + + /** + * Close the camera device enumerator channel. + */ + psCamDevEnumServerServerClose Close; + + /** + * Poll + * When externalThread=TRUE, call Poll() periodically from your main loop. + * If externalThread=FALSE do not call. + */ + psCamDevEnumServerServerPoll Poll; + + /** + * Retrieve the channel handle for use in conjunction with Poll(). + * If externalThread=FALSE do not call. + */ + psCamDevEnumServerServerChannelHandle ChannelHandle; + + /* + * Send a Select Version Response PDU. + */ + psCamDevEnumServerServerSelectVersionResponse SelectVersionResponse; + + /*** Callbacks registered by the server. ***/ + + /** + * Callback, when the channel got its id assigned. + */ + psCamDevEnumServerServerChannelIdAssigned ChannelIdAssigned; + + /** + * Callback for the Select Version Request PDU. + */ + psCamDevEnumServerServerSelectVersionRequest SelectVersionRequest; + + /** + * Callback for the Device Added Notification PDU. + */ + psCamDevEnumServerServerDeviceAddedNotification DeviceAddedNotification; + + /** + * Callback for the Device Removed Notification PDU. + */ + psCamDevEnumServerServerDeviceRemovedNotification DeviceRemovedNotification; + + rdpContext* rdpcontext; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + + FREERDP_API CamDevEnumServerContext* cam_dev_enum_server_context_new(HANDLE vcm); + FREERDP_API void cam_dev_enum_server_context_free(CamDevEnumServerContext* context); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_CHANNEL_CAM_DEV_ENUM_SERVER_CAM_DEV_ENUM_H */ diff --git a/include/freerdp/server/rdpecam.h b/include/freerdp/server/rdpecam.h new file mode 100644 index 0000000..a28ff87 --- /dev/null +++ b/include/freerdp/server/rdpecam.h @@ -0,0 +1,278 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Video Capture Virtual Channel Extension + * + * Copyright 2022 Pascal Nowack + * + * 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. + */ + +#ifndef FREERDP_CHANNEL_CAMERA_DEVICE_SERVER_CAMERA_DEVICE_H +#define FREERDP_CHANNEL_CAMERA_DEVICE_SERVER_CAMERA_DEVICE_H + +#include +#include + +typedef struct camera_device_server_context CameraDeviceServerContext; + +typedef UINT (*psCameraDeviceServerOpen)(CameraDeviceServerContext* context); +typedef UINT (*psCameraDeviceServerClose)(CameraDeviceServerContext* context); + +typedef BOOL (*psCameraDeviceServerChannelIdAssigned)(CameraDeviceServerContext* context, + UINT32 channelId); + +typedef UINT (*psCameraDeviceServerInitialize)(CameraDeviceServerContext* context, + BOOL externalThread); +typedef UINT (*psCameraDeviceServerPoll)(CameraDeviceServerContext* context); +typedef BOOL (*psCameraDeviceServerChannelHandle)(CameraDeviceServerContext* context, + HANDLE* handle); + +typedef UINT (*psCameraDeviceServerSuccessResponse)(CameraDeviceServerContext* context, + const CAM_SUCCESS_RESPONSE* successResponse); +typedef UINT (*psCameraDeviceServerErrorResponse)(CameraDeviceServerContext* context, + const CAM_ERROR_RESPONSE* errorResponse); + +typedef UINT (*psCameraDeviceServerActivateDeviceRequest)( + CameraDeviceServerContext* context, const CAM_ACTIVATE_DEVICE_REQUEST* activateDeviceRequest); +typedef UINT (*psCameraDeviceServerDeactivateDeviceRequest)( + CameraDeviceServerContext* context, + const CAM_DEACTIVATE_DEVICE_REQUEST* deactivateDeviceRequest); + +typedef UINT (*psCameraDeviceServerStreamListRequest)( + CameraDeviceServerContext* context, const CAM_STREAM_LIST_REQUEST* streamListRequest); +typedef UINT (*psCameraDeviceServerStreamListResponse)( + CameraDeviceServerContext* context, const CAM_STREAM_LIST_RESPONSE* streamListResponse); + +typedef UINT (*psCameraDeviceServerMediaTypeListRequest)( + CameraDeviceServerContext* context, const CAM_MEDIA_TYPE_LIST_REQUEST* mediaTypeListRequest); +typedef UINT (*psCameraDeviceServerMediaTypeListResponse)( + CameraDeviceServerContext* context, const CAM_MEDIA_TYPE_LIST_RESPONSE* mediaTypeListResponse); + +typedef UINT (*psCameraDeviceServerCurrentMediaTypeRequest)( + CameraDeviceServerContext* context, + const CAM_CURRENT_MEDIA_TYPE_REQUEST* currentMediaTypeRequest); +typedef UINT (*psCameraDeviceServerCurrentMediaTypeResponse)( + CameraDeviceServerContext* context, + const CAM_CURRENT_MEDIA_TYPE_RESPONSE* currentMediaTypeResponse); + +typedef UINT (*psCameraDeviceServerStartStreamsRequest)( + CameraDeviceServerContext* context, const CAM_START_STREAMS_REQUEST* startStreamsRequest); +typedef UINT (*psCameraDeviceServerStopStreamsRequest)( + CameraDeviceServerContext* context, const CAM_STOP_STREAMS_REQUEST* stopStreamsRequest); + +typedef UINT (*psCameraDeviceServerSampleRequest)(CameraDeviceServerContext* context, + const CAM_SAMPLE_REQUEST* sampleRequest); +typedef UINT (*psCameraDeviceServerSampleResponse)(CameraDeviceServerContext* context, + const CAM_SAMPLE_RESPONSE* sampleResponse); +typedef UINT (*psCameraDeviceServerSampleErrorResponse)( + CameraDeviceServerContext* context, const CAM_SAMPLE_ERROR_RESPONSE* sampleErrorResponse); + +typedef UINT (*psCameraDeviceServerPropertyListRequest)( + CameraDeviceServerContext* context, const CAM_PROPERTY_LIST_REQUEST* propertyListRequest); +typedef UINT (*psCameraDeviceServerPropertyListResponse)( + CameraDeviceServerContext* context, const CAM_PROPERTY_LIST_RESPONSE* propertyListResponse); + +typedef UINT (*psCameraDeviceServerPropertyValueRequest)( + CameraDeviceServerContext* context, const CAM_PROPERTY_VALUE_REQUEST* propertyValueRequest); +typedef UINT (*psCameraDeviceServerPropertyValueResponse)( + CameraDeviceServerContext* context, const CAM_PROPERTY_VALUE_RESPONSE* propertyValueResponse); + +typedef UINT (*psCameraDeviceServerSetPropertyValueRequest)( + CameraDeviceServerContext* context, + const CAM_SET_PROPERTY_VALUE_REQUEST* setPropertyValueRequest); + +struct camera_device_server_context +{ + HANDLE vcm; + + /* Server self-defined pointer. */ + void* userdata; + + /** + * Name of the virtual channel. Pointer owned by the CameraDeviceServerContext, + * meaning camera_device_server_context_free() takes care of freeing the pointer. + * + * Server implementations should sanitize the virtual channel name for invalid + * names, like names for other known channels + * ("ECHO", "AUDIO_PLAYBACK_DVC", etc.) + */ + char* virtualChannelName; + + /** + * Protocol version to be used. Every sent server to client PDU has the + * version value in the Header set to the following value. + */ + BYTE protocolVersion; + + /*** APIs called by the server. ***/ + + /** + * Optional: Set thread handling. + * When externalThread=TRUE, the application is responsible to call + * Poll() periodically to process channel events. + * + * Defaults to externalThread=FALSE + */ + psCameraDeviceServerInitialize Initialize; + + /** + * Open the camera device channel. + */ + psCameraDeviceServerOpen Open; + + /** + * Close the camera device channel. + */ + psCameraDeviceServerClose Close; + + /** + * Poll + * When externalThread=TRUE, call Poll() periodically from your main loop. + * If externalThread=FALSE do not call. + */ + psCameraDeviceServerPoll Poll; + + /** + * Retrieve the channel handle for use in conjunction with Poll(). + * If externalThread=FALSE do not call. + */ + psCameraDeviceServerChannelHandle ChannelHandle; + + /** + * For the following server to client PDUs, + * the message header does not have to be set. + */ + + /** + * Send a Activate Device Request PDU. + */ + psCameraDeviceServerActivateDeviceRequest ActivateDeviceRequest; + + /** + * Send a Deactivate Device Request PDU. + */ + psCameraDeviceServerDeactivateDeviceRequest DeactivateDeviceRequest; + + /** + * Send a Stream List Request PDU. + */ + psCameraDeviceServerStreamListRequest StreamListRequest; + + /** + * Send a Media Type List Request PDU. + */ + psCameraDeviceServerMediaTypeListRequest MediaTypeListRequest; + + /** + * Send a Current Media Type Request PDU. + */ + psCameraDeviceServerCurrentMediaTypeRequest CurrentMediaTypeRequest; + + /** + * Send a Start Streams Request PDU. + */ + psCameraDeviceServerStartStreamsRequest StartStreamsRequest; + + /** + * Send a Stop Streams Request PDU. + */ + psCameraDeviceServerStopStreamsRequest StopStreamsRequest; + + /** + * Send a Sample Request PDU. + */ + psCameraDeviceServerSampleRequest SampleRequest; + + /** + * Send a Property List Request PDU. + */ + psCameraDeviceServerPropertyListRequest PropertyListRequest; + + /** + * Send a Property Value Request PDU. + */ + psCameraDeviceServerPropertyValueRequest PropertyValueRequest; + + /** + * Send a Set Property Value Request PDU. + */ + psCameraDeviceServerSetPropertyValueRequest SetPropertyValueRequest; + + /*** Callbacks registered by the server. ***/ + + /** + * Callback, when the channel got its id assigned. + */ + psCameraDeviceServerChannelIdAssigned ChannelIdAssigned; + + /** + * Callback for the Success Response PDU. + */ + psCameraDeviceServerSuccessResponse SuccessResponse; + + /** + * Callback for the Error Response PDU. + */ + psCameraDeviceServerErrorResponse ErrorResponse; + + /** + * Callback for the Stream List Response PDU. + */ + psCameraDeviceServerStreamListResponse StreamListResponse; + + /** + * Callback for the Media Type List Response PDU. + */ + psCameraDeviceServerMediaTypeListResponse MediaTypeListResponse; + + /** + * Callback for the Current Media Type Response PDU. + */ + psCameraDeviceServerCurrentMediaTypeResponse CurrentMediaTypeResponse; + + /** + * Callback for the Sample Response PDU. + */ + psCameraDeviceServerSampleResponse SampleResponse; + + /** + * Callback for the Sample Error Response PDU. + */ + psCameraDeviceServerSampleErrorResponse SampleErrorResponse; + + /** + * Callback for the Property List Response PDU. + */ + psCameraDeviceServerPropertyListResponse PropertyListResponse; + + /** + * Callback for the Property Value Response PDU. + */ + psCameraDeviceServerPropertyValueResponse PropertyValueResponse; + + rdpContext* rdpcontext; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + + FREERDP_API CameraDeviceServerContext* camera_device_server_context_new(HANDLE vcm); + FREERDP_API void camera_device_server_context_free(CameraDeviceServerContext* context); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_CHANNEL_CAMERA_DEVICE_SERVER_CAMERA_DEVICE_H */ diff --git a/include/freerdp/server/rdpei.h b/include/freerdp/server/rdpei.h index b784e78..01b1393 100644 --- a/include/freerdp/server/rdpei.h +++ b/include/freerdp/server/rdpei.h @@ -47,6 +47,11 @@ struct _rdpei_server_context void* user_data; /* user data, useful for callbacks */ UINT (*onPenEvent)(RdpeiServerContext* context, const RDPINPUT_PEN_EVENT* penEvent); + + /** + * Callback, when the channel got its id assigned. + */ + BOOL (*onChannelIdAssigned)(RdpeiServerContext* context, UINT32 channelId); }; #ifdef __cplusplus diff --git a/include/freerdp/server/rdpgfx.h b/include/freerdp/server/rdpgfx.h index 9e52ca1..aee9de9 100644 --- a/include/freerdp/server/rdpgfx.h +++ b/include/freerdp/server/rdpgfx.h @@ -29,6 +29,8 @@ typedef struct _rdpgfx_server_private RdpgfxServerPrivate; typedef BOOL (*psRdpgfxServerOpen)(RdpgfxServerContext* context); typedef BOOL (*psRdpgfxServerClose)(RdpgfxServerContext* context); +typedef BOOL (*psRdpgfxServerChannelIdAssigned)(RdpgfxServerContext* context, UINT32 channelId); + typedef UINT (*psRdpgfxResetGraphics)(RdpgfxServerContext* context, const RDPGFX_RESET_GRAPHICS_PDU* resetGraphics); typedef UINT (*psRdpgfxStartFrame)(RdpgfxServerContext* context, @@ -112,6 +114,11 @@ struct _rdpgfx_server_context RdpgfxServerPrivate* priv; rdpContext* rdpcontext; + + /** + * Callback, when the channel got its id assigned. + */ + psRdpgfxServerChannelIdAssigned ChannelIdAssigned; }; #ifdef __cplusplus diff --git a/include/freerdp/server/rdpsnd.h b/include/freerdp/server/rdpsnd.h index b83a636..a8cb510 100644 --- a/include/freerdp/server/rdpsnd.h +++ b/include/freerdp/server/rdpsnd.h @@ -32,11 +32,21 @@ typedef struct _rdpsnd_server_private RdpsndServerPrivate; typedef UINT (*psRdpsndStart)(RdpsndServerContext* context); typedef UINT (*psRdpsndStop)(RdpsndServerContext* context); +typedef BOOL (*psRdpsndChannelIdAssigned)(RdpsndServerContext* context, UINT32 channelId); + typedef UINT (*psRdpsndServerInitialize)(RdpsndServerContext* context, BOOL ownThread); +typedef UINT (*psRdpsndServerSendFormats)(RdpsndServerContext* context); typedef UINT (*psRdpsndServerSelectFormat)(RdpsndServerContext* context, UINT16 client_format_index); +typedef UINT (*psRdpsndServerTraining)(RdpsndServerContext* context, UINT16 timestamp, + UINT16 packsize, BYTE* data); +typedef UINT (*psRdpsndServerTrainingConfirm)(RdpsndServerContext* context, UINT16 timestamp, + UINT16 packsize); typedef UINT (*psRdpsndServerSendSamples)(RdpsndServerContext* context, const void* buf, int nframes, UINT16 wTimestamp); +typedef UINT (*psRdpsndServerSendSamples2)(RdpsndServerContext* context, UINT16 formatNo, + const void* buf, size_t size, UINT16 timestamp, + UINT32 audioTimeStamp); typedef UINT (*psRdpsndServerConfirmBlock)(RdpsndServerContext* context, BYTE confirmBlockNum, UINT16 wtimestamp); typedef UINT (*psRdpsndServerSetVolume)(RdpsndServerContext* context, int left, int right); @@ -122,6 +132,43 @@ struct _rdpsnd_server_context /* Server to request to use dynamic virtual channel. */ BOOL use_dynamic_virtual_channel; + + /* dwFlags in CLIENT_AUDIO_VERSION_AND_FORMATS */ + UINT32 capsFlags; + /* dwVolume in CLIENT_AUDIO_VERSION_AND_FORMATS */ + UINT32 initialVolume; + /* dwPitch in CLIENT_AUDIO_VERSION_AND_FORMATS */ + UINT32 initialPitch; + + UINT16 qualityMode; + + /** + * Send server formats and version to the client. Automatically sent, when + * opening the channel. + * Also used to restart the protocol after sending the Close PDU. + */ + psRdpsndServerSendFormats SendFormats; + /** + * Send Training PDU. + */ + psRdpsndServerTraining Training; + + /** + * Send encoded audio samples using a Wave2 PDU. + * When successful, the block_no member is incremented. + */ + psRdpsndServerSendSamples2 SendSamples2; + + /** + * Called when a TrainingConfirm PDU is received from the client. + */ + psRdpsndServerTrainingConfirm TrainingConfirm; + + /** + * Callback, when the channel got its id assigned. + * Only called, when use_dynamic_virtual_channel=TRUE. + */ + psRdpsndChannelIdAssigned ChannelIdAssigned; }; #ifdef __cplusplus diff --git a/include/freerdp/server/telemetry.h b/include/freerdp/server/telemetry.h new file mode 100644 index 0000000..04f72ad --- /dev/null +++ b/include/freerdp/server/telemetry.h @@ -0,0 +1,108 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Telemetry Virtual Channel Extension + * + * Copyright 2022 Pascal Nowack + * + * 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. + */ + +#ifndef FREERDP_CHANNEL_TELEMETRY_SERVER_TELEMETRY_H +#define FREERDP_CHANNEL_TELEMETRY_SERVER_TELEMETRY_H + +#include +#include + +typedef struct _telemetry_server_context TelemetryServerContext; + +typedef UINT (*psTelemetryServerOpen)(TelemetryServerContext* context); +typedef UINT (*psTelemetryServerClose)(TelemetryServerContext* context); + +typedef BOOL (*psTelemetryServerChannelIdAssigned)(TelemetryServerContext* context, + UINT32 channelId); + +typedef UINT (*psTelemetryServerInitialize)(TelemetryServerContext* context, BOOL externalThread); +typedef UINT (*psTelemetryServerPoll)(TelemetryServerContext* context); +typedef BOOL (*psTelemetryServerChannelHandle)(TelemetryServerContext* context, HANDLE* handle); + +typedef UINT (*psTelemetryServerRdpTelemetry)(TelemetryServerContext* context, + const TELEMETRY_RDP_TELEMETRY_PDU* rdpTelemetry); + +struct _telemetry_server_context +{ + HANDLE vcm; + + /* Server self-defined pointer. */ + void* userdata; + + /*** APIs called by the server. ***/ + + /** + * Optional: Set thread handling. + * When externalThread=TRUE, the application is responsible to call + * Poll() periodically to process channel events. + * + * Defaults to externalThread=FALSE + */ + psTelemetryServerInitialize Initialize; + + /** + * Open the telemetry channel. + */ + psTelemetryServerOpen Open; + + /** + * Close the telemetry channel. + */ + psTelemetryServerClose Close; + + /** + * Poll + * When externalThread=TRUE, call Poll() periodically from your main loop. + * If externalThread=FALSE do not call. + */ + psTelemetryServerPoll Poll; + + /** + * Retrieve the channel handle for use in conjunction with Poll(). + * If externalThread=FALSE do not call. + */ + psTelemetryServerChannelHandle ChannelHandle; + + /*** Callbacks registered by the server. ***/ + + /** + * Callback, when the channel got its id assigned + */ + psTelemetryServerChannelIdAssigned ChannelIdAssigned; + /** + * Callback for the RDP Telemetry PDU. + */ + psTelemetryServerRdpTelemetry RdpTelemetry; + + rdpContext* rdpcontext; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + + FREERDP_API TelemetryServerContext* telemetry_server_context_new(HANDLE vcm); + FREERDP_API void telemetry_server_context_free(TelemetryServerContext* context); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_CHANNEL_TELEMETRY_SERVER_TELEMETRY_H */ diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 3b7a138..46d8536 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -656,6 +656,8 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_NtlmSamFile (1103) #define FreeRDP_FIPSMode (1104) #define FreeRDP_TlsSecLevel (1105) +#define FreeRDP_TLSMinVersion (1106) +#define FreeRDP_TLSMaxVersion (1107) #define FreeRDP_MstscCookieMode (1152) #define FreeRDP_CookieMaxLength (1153) #define FreeRDP_PreconnectionId (1154) @@ -1108,7 +1110,9 @@ struct rdp_settings ALIGN64 char* NtlmSamFile; /* 1103 */ ALIGN64 BOOL FIPSMode; /* 1104 */ ALIGN64 UINT32 TlsSecLevel; /* 1105 */ - UINT64 padding1152[1152 - 1106]; /* 1106 */ + ALIGN64 UINT16 TLSMinVersion; /* 1106 */ + ALIGN64 UINT16 TLSMaxVersion; /* 1107 */ + UINT64 padding1152[1152 - 1108]; /* 1108 */ /* Connection Cookie */ ALIGN64 BOOL MstscCookieMode; /* 1152 */ @@ -1705,6 +1709,9 @@ extern "C" FREERDP_API SSIZE_T freerdp_settings_get_type_for_key(size_t key); FREERDP_API const char* freerdp_settings_get_name_for_key(size_t key); + FREERDP_API char* freerdp_rail_support_flags_to_string(UINT32 flags, char* buffer, + size_t length); + #ifdef __cplusplus } #endif diff --git a/include/freerdp/utils/cliprdr_utils.h b/include/freerdp/utils/cliprdr_utils.h new file mode 100644 index 0000000..59ecf84 --- /dev/null +++ b/include/freerdp/utils/cliprdr_utils.h @@ -0,0 +1,48 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPDR utility functions + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * 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. + */ + +#ifndef FREERDP_UTILS_CLIPRDR_H +#define FREERDP_UTILS_CLIPRDR_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + FREERDP_API UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length, + FILEDESCRIPTORW** file_descriptor_array, + UINT32* file_descriptor_count); + FREERDP_API UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array, + UINT32 file_descriptor_count, BYTE** format_data, + UINT32* format_data_length); + FREERDP_API UINT cliprdr_serialize_file_list_ex(UINT32 flags, + const FILEDESCRIPTORW* file_descriptor_array, + UINT32 file_descriptor_count, + BYTE** format_data, UINT32* format_data_length); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libfreerdp/CMakeLists.txt b/libfreerdp/CMakeLists.txt index 5b74614..a259b68 100644 --- a/libfreerdp/CMakeLists.txt +++ b/libfreerdp/CMakeLists.txt @@ -211,7 +211,12 @@ if(FAAC_FOUND) endif() if(WITH_NEON) - set_source_files_properties(${CODEC_NEON_SRCS} PROPERTIES COMPILE_FLAGS "-mfpu=neon -Wno-unused-variable" ) + check_symbol_exists("_M_AMD64" "" MSVC_ARM64) + check_symbol_exists("__aarch64__" "" ARCH_ARM64) + + if (NOT MSVC_ARM64 AND NOT ARCH_ARM64) + set_source_files_properties(${CODEC_NEON_SRCS} PROPERTIES COMPILE_FLAGS "-mfpu=neon" ) + endif() set(CODEC_SRCS ${CODEC_SRCS} ${CODEC_NEON_SRCS}) endif() @@ -308,7 +313,7 @@ freerdp_definition_add(-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}) ### IPP Variable debugging if(WITH_IPP) - if(CMAKE_COMPILER_IS_GNUCC) + if(CMAKE_COMPILER_IS_GNUCC OR ${CMAKE_C_COMPILER_ID} STREQUAL "Clang") foreach(INCLDIR ${IPP_INCLUDE_DIRS}) set(OPTIMIZATION "${OPTIMIZATION} -I${INCLDIR}") endforeach(INCLDIR) @@ -330,9 +335,11 @@ if(WITH_SSE2) PROPERTIES COMPILE_FLAGS "${OPTIMIZATION} /arch:SSE2") endif() elseif(WITH_NEON) - if(CMAKE_COMPILER_IS_GNUCC) - set_source_files_properties(${PRIMITIVES_OPT_SRCS} - PROPERTIES COMPILE_FLAGS "${OPTIMIZATION} -mfpu=neon") + if(CMAKE_COMPILER_IS_GNUCC OR ${CMAKE_C_COMPILER_ID} STREQUAL "Clang") + if (NOT MSVC_ARM64 AND NOT ARCH_ARM64) + set_source_files_properties(${PRIMITIVES_OPT_SRCS} + PROPERTIES COMPILE_FLAGS "${OPTIMIZATION} -mfpu=neon") + endif() endif() # TODO: Add MSVC equivalent endif() diff --git a/libfreerdp/codec/progressive.c b/libfreerdp/codec/progressive.c index a9e39d2..e95db2d 100644 --- a/libfreerdp/codec/progressive.c +++ b/libfreerdp/codec/progressive.c @@ -896,7 +896,7 @@ static INLINE int progressive_rfx_decode_component(PROGRESSIVE_CONTEXT* progress return status; CopyMemory(sign, buffer, 4096 * 2); - if (!subbandDiff) + if (!extrapolate) { rfx_differential_decode(buffer + 4032, 64); progressive_rfx_decode_block(prims, &buffer[0], 1024, shift->HL1); /* HL1 */ diff --git a/libfreerdp/codec/rfx_neon.c b/libfreerdp/codec/rfx_neon.c index c98955a..c41ebb4 100644 --- a/libfreerdp/codec/rfx_neon.c +++ b/libfreerdp/codec/rfx_neon.c @@ -21,7 +21,7 @@ #include "config.h" #endif -#if defined(__ARM_NEON__) +#if defined(WITH_NEON) #include #include @@ -241,4 +241,4 @@ void rfx_init_neon(RFX_CONTEXT* context) } } -#endif // __ARM_NEON__ +#endif // WITH_NEON diff --git a/libfreerdp/codec/zgfx.c b/libfreerdp/codec/zgfx.c index 1a2878b..20fbd35 100644 --- a/libfreerdp/codec/zgfx.c +++ b/libfreerdp/codec/zgfx.c @@ -363,6 +363,17 @@ static BOOL zgfx_decompress_segment(ZGFX_CONTEXT* zgfx, wStream* stream, size_t return TRUE; } +/* Allocate the buffers a bit larger. + * + * Due to optimizations some h264 decoders will read data beyond + * the actual available data, so ensure that it will never be a + * out of bounds read. + */ +static BYTE* aligned_zgfx_malloc(size_t size) +{ + return malloc(size + 64); +} + int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData, UINT32* pDstSize, UINT32 flags) { @@ -386,7 +397,7 @@ int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BY *ppDstData = NULL; if (zgfx->OutputCount > 0) - *ppDstData = (BYTE*)malloc(zgfx->OutputCount); + *ppDstData = aligned_zgfx_malloc(zgfx->OutputCount); if (!*ppDstData) goto fail; @@ -412,7 +423,7 @@ int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BY if (Stream_GetRemainingLength(stream) < segmentCount * sizeof(UINT32)) goto fail; - pConcatenated = (BYTE*)malloc(uncompressedSize); + pConcatenated = aligned_zgfx_malloc(uncompressedSize); if (!pConcatenated) goto fail; diff --git a/libfreerdp/common/settings.c b/libfreerdp/common/settings.c index b639778..f32f32a 100644 --- a/libfreerdp/common/settings.c +++ b/libfreerdp/common/settings.c @@ -925,3 +925,26 @@ BOOL freerdp_settings_set_value_for_name(rdpSettings* settings, const char* name } return FALSE; } + +char* freerdp_rail_support_flags_to_string(UINT32 flags, char* buffer, size_t length) +{ + if (flags & RAIL_LEVEL_SUPPORTED) + winpr_str_append("RAIL_LEVEL_SUPPORTED", buffer, length, "|"); + if (flags & RAIL_LEVEL_DOCKED_LANGBAR_SUPPORTED) + winpr_str_append("RAIL_LEVEL_DOCKED_LANGBAR_SUPPORTED", buffer, length, "|"); + if (flags & RAIL_LEVEL_SHELL_INTEGRATION_SUPPORTED) + winpr_str_append("RAIL_LEVEL_SHELL_INTEGRATION_SUPPORTED", buffer, length, "|"); + if (flags & RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED) + winpr_str_append("RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED", buffer, length, "|"); + if (flags & RAIL_LEVEL_SERVER_TO_CLIENT_IME_SYNC_SUPPORTED) + winpr_str_append("RAIL_LEVEL_SERVER_TO_CLIENT_IME_SYNC_SUPPORTED", buffer, length, "|"); + if (flags & RAIL_LEVEL_HIDE_MINIMIZED_APPS_SUPPORTED) + winpr_str_append("RAIL_LEVEL_HIDE_MINIMIZED_APPS_SUPPORTED", buffer, length, "|"); + if (flags & RAIL_LEVEL_WINDOW_CLOAKING_SUPPORTED) + winpr_str_append("RAIL_LEVEL_WINDOW_CLOAKING_SUPPORTED", buffer, length, "|"); + if (flags & RAIL_LEVEL_HANDSHAKE_EX_SUPPORTED) + winpr_str_append("RAIL_LEVEL_HANDSHAKE_EX_SUPPORTED", buffer, length, "|"); + if (flags & RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED) + winpr_str_append("RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED", buffer, length, "|"); + return buffer; +} diff --git a/libfreerdp/common/settings_getters.c b/libfreerdp/common/settings_getters.c index a3ce8e9..9224524 100644 --- a/libfreerdp/common/settings_getters.c +++ b/libfreerdp/common/settings_getters.c @@ -1193,6 +1193,12 @@ UINT16 freerdp_settings_get_uint16(const rdpSettings* settings, size_t id) case FreeRDP_ProxyPort: return settings->ProxyPort; + case FreeRDP_TLSMaxVersion: + return settings->TLSMaxVersion; + + case FreeRDP_TLSMinVersion: + return settings->TLSMinVersion; + default: WLog_ERR(TAG, "[%s] Invalid key index %" PRIuz, __FUNCTION__, id); return FALSE; @@ -1214,6 +1220,14 @@ BOOL freerdp_settings_set_uint16(rdpSettings* settings, size_t id, UINT16 val) settings->ProxyPort = val; break; + case FreeRDP_TLSMaxVersion: + settings->TLSMaxVersion = val; + break; + + case FreeRDP_TLSMinVersion: + settings->TLSMinVersion = val; + break; + default: WLog_ERR(TAG, "[%s] Invalid key index %" PRIuz, __FUNCTION__, id); return FALSE; diff --git a/libfreerdp/common/settings_str.c b/libfreerdp/common/settings_str.c index 9a545f2..9a0bf53 100644 --- a/libfreerdp/common/settings_str.c +++ b/libfreerdp/common/settings_str.c @@ -180,6 +180,8 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_Workarea, 0, "FreeRDP_Workarea" }, { FreeRDP_DesktopOrientation, 1, "FreeRDP_DesktopOrientation" }, { FreeRDP_ProxyPort, 1, "FreeRDP_ProxyPort" }, + { FreeRDP_TLSMaxVersion, 1, "FreeRDP_TLSMaxVersion" }, + { FreeRDP_TLSMinVersion, 1, "FreeRDP_TLSMinVersion" }, { FreeRDP_AcceptedCertLength, 3, "FreeRDP_AcceptedCertLength" }, { FreeRDP_AuthenticationLevel, 3, "FreeRDP_AuthenticationLevel" }, { FreeRDP_AutoReconnectMaxRetries, 3, "FreeRDP_AutoReconnectMaxRetries" }, @@ -393,6 +395,10 @@ static const struct settings_str_entry settings_map[] = { BOOL freerdp_settings_clone_keys(rdpSettings* dst, const rdpSettings* src) { size_t x; + + WINPR_ASSERT(dst); + WINPR_ASSERT(src); + for (x = 0; x < ARRAYSIZE(settings_map); x++) { const struct settings_str_entry* cur = &settings_map[x]; @@ -466,9 +472,214 @@ BOOL freerdp_settings_clone_keys(rdpSettings* dst, const rdpSettings* src) return TRUE; } +BOOL freerdp_settings_print_diff(wLog* log, DWORD level, const rdpSettings* settings, + const rdpSettings* other) +{ + BOOL rc = FALSE; + size_t x; + + WINPR_ASSERT(log); + WINPR_ASSERT(settings); + WINPR_ASSERT(other); + + for (x = 0; x < ARRAYSIZE(settings_map); x++) + { + const struct settings_str_entry* cur = &settings_map[x]; + switch (cur->type) + { + case 0: /* bool */ + { + BOOL sval = freerdp_settings_get_bool(settings, cur->id); + BOOL cval = freerdp_settings_get_bool(other, cur->id); + if (sval != cval) + { + rc = TRUE; + WLog_Print(log, level, "%s [BOOL]: %s -> %s", cur->str, sval ? "TRUE" : "FALSE", + cval ? "TRUE" : "FALSE"); + } + } + break; + case 1: /* UINT16 */ + { + UINT16 sval = freerdp_settings_get_uint16(settings, cur->id); + UINT16 cval = freerdp_settings_get_uint16(other, cur->id); + if (sval != cval) + { + rc = TRUE; + WLog_Print(log, level, "%s [UINT16]: %" PRIu16 " -> %" PRIu16, cur->str, sval, + cval); + } + } + break; + case 2: /* INT16 */ + { + INT16 sval = freerdp_settings_get_int16(settings, cur->id); + INT16 cval = freerdp_settings_get_int16(other, cur->id); + if (sval != cval) + { + rc = TRUE; + WLog_Print(log, level, "%s [INT16]: %" PRId16 " -> %" PRId16, cur->str, sval, + cval); + } + } + break; + case 3: /* UINT32 */ + { + UINT32 sval = freerdp_settings_get_uint32(settings, cur->id); + UINT32 cval = freerdp_settings_get_uint32(other, cur->id); + if (sval != cval) + { + rc = TRUE; + WLog_Print(log, level, "%s [UINT32]: %" PRIu32 " -> %" PRIu32, cur->str, sval, + cval); + } + } + break; + case 4: /* INT32 */ + { + INT32 sval = freerdp_settings_get_int32(settings, cur->id); + INT32 cval = freerdp_settings_get_int32(other, cur->id); + if (sval != cval) + { + rc = TRUE; + WLog_Print(log, level, "%s [INT32]: %" PRId32 " -> %" PRId32, cur->str, sval, + cval); + } + } + break; + case 5: /* UINT64 */ + { + UINT64 sval = freerdp_settings_get_uint64(settings, cur->id); + UINT64 cval = freerdp_settings_get_uint64(other, cur->id); + if (sval != cval) + { + rc = TRUE; + WLog_Print(log, level, "%s [UINT64]: %" PRIu64 " -> %" PRIu64, cur->str, sval, + cval); + } + } + break; + case 6: /* INT64 */ + { + INT64 sval = freerdp_settings_get_int64(settings, cur->id); + INT64 cval = freerdp_settings_get_int64(other, cur->id); + if (sval != cval) + { + rc = TRUE; + WLog_Print(log, level, "%s [INT64]: %" PRId64 " -> %" PRId64, cur->str, sval, + cval); + } + } + break; + case 7: /* strings */ + { + const char* sval = freerdp_settings_get_string(settings, cur->id); + const char* cval = freerdp_settings_get_string(other, cur->id); + if (sval != cval) + { + if (!sval || !cval || (strcmp(sval, cval) != 0)) + { + rc = TRUE; + WLog_Print(log, level, "%s [STRING]: '%s' -> '%s'", cur->str, sval, cval); + } + } + } + break; + case 8: /* pointer */ + { + const void* sval = freerdp_settings_get_pointer(settings, cur->id); + const void* cval = freerdp_settings_get_pointer(other, cur->id); + if (sval != cval) + { + if ((sval && !cval) || (!sval && cval)) + { + rc = TRUE; + WLog_Print(log, level, "%s [POINTER]: '%p' -> '%p'", cur->str, sval, cval); + } + } + } + break; + } + } + return rc; +} + +void freerdp_settings_dump(wLog* log, DWORD level, const rdpSettings* settings) +{ + size_t x; + + WINPR_ASSERT(log); + WINPR_ASSERT(settings); + + for (x = 0; x < ARRAYSIZE(settings_map); x++) + { + const struct settings_str_entry* cur = &settings_map[x]; + switch (cur->type) + { + case 0: /* bool */ + { + BOOL sval = freerdp_settings_get_bool(settings, cur->id); + WLog_Print(log, level, "%s [BOOL]: %s", cur->str, sval ? "TRUE" : "FALSE"); + } + break; + case 1: /* UINT16 */ + { + UINT16 sval = freerdp_settings_get_uint16(settings, cur->id); + WLog_Print(log, level, "%s [UINT16]: %" PRIu16, cur->str, sval); + } + break; + case 2: /* INT16 */ + { + INT16 sval = freerdp_settings_get_int16(settings, cur->id); + WLog_Print(log, level, "%s [INT16]: %" PRId16, cur->str, sval); + } + break; + case 3: /* UINT32 */ + { + UINT32 sval = freerdp_settings_get_uint32(settings, cur->id); + WLog_Print(log, level, "%s [UINT32]: %" PRIu32, cur->str, sval); + } + break; + case 4: /* INT32 */ + { + INT32 sval = freerdp_settings_get_int32(settings, cur->id); + WLog_Print(log, level, "%s [INT32]: %" PRId32, cur->str, sval); + } + break; + case 5: /* UINT64 */ + { + UINT64 sval = freerdp_settings_get_uint64(settings, cur->id); + WLog_Print(log, level, "%s [UINT64]: %" PRIu64, cur->str, sval); + } + break; + case 6: /* INT64 */ + { + INT64 sval = freerdp_settings_get_int64(settings, cur->id); + WLog_Print(log, level, "%s [INT64]: %" PRId64, cur->str, sval); + } + break; + case 7: /* strings */ + { + const char* sval = freerdp_settings_get_string(settings, cur->id); + WLog_Print(log, level, "%s [STRING]: '%s'", cur->str, sval); + } + break; + case 8: /* pointer */ + { + const void* sval = freerdp_settings_get_pointer(settings, cur->id); + WLog_Print(log, level, "%s [POINTER]: '%p'", cur->str, sval); + } + break; + } + } +} + void freerdp_settings_free_keys(rdpSettings* dst, BOOL cleanup) { size_t x; + + WINPR_ASSERT(dst); + for (x = 0; x < ARRAYSIZE(settings_map); x++) { const struct settings_str_entry* cur = &settings_map[x]; @@ -487,6 +698,9 @@ void freerdp_settings_free_keys(rdpSettings* dst, BOOL cleanup) SSIZE_T freerdp_settings_get_key_for_name(const char* value) { size_t x; + + WINPR_ASSERT(value); + for (x = 0; x < ARRAYSIZE(settings_map); x++) { const struct settings_str_entry* cur = &settings_map[x]; @@ -499,6 +713,9 @@ SSIZE_T freerdp_settings_get_key_for_name(const char* value) SSIZE_T freerdp_settings_get_type_for_name(const char* value) { size_t x; + + WINPR_ASSERT(value); + for (x = 0; x < ARRAYSIZE(settings_map); x++) { const struct settings_str_entry* cur = &settings_map[x]; @@ -511,6 +728,7 @@ SSIZE_T freerdp_settings_get_type_for_name(const char* value) SSIZE_T freerdp_settings_get_type_for_key(size_t key) { size_t x; + for (x = 0; x < ARRAYSIZE(settings_map); x++) { const struct settings_str_entry* cur = &settings_map[x]; @@ -523,6 +741,7 @@ SSIZE_T freerdp_settings_get_type_for_key(size_t key) const char* freerdp_settings_get_name_for_key(size_t key) { size_t x; + for (x = 0; x < ARRAYSIZE(settings_map); x++) { const struct settings_str_entry* cur = &settings_map[x]; diff --git a/libfreerdp/core/capabilities.c b/libfreerdp/core/capabilities.c index 30edcbb..433f4ce 100644 --- a/libfreerdp/core/capabilities.c +++ b/libfreerdp/core/capabilities.c @@ -162,26 +162,17 @@ static BOOL rdp_read_general_capability_set(wStream* s, rdpSettings* settings) if (Stream_GetRemainingLength(s) < 20) return FALSE; - if (settings->ServerMode) - { - Stream_Read_UINT16(s, settings->OsMajorType); /* osMajorType (2 bytes) */ - Stream_Read_UINT16(s, settings->OsMinorType); /* osMinorType (2 bytes) */ - } - else - { - Stream_Seek_UINT16(s); /* osMajorType (2 bytes) */ - Stream_Seek_UINT16(s); /* osMinorType (2 bytes) */ - } - - Stream_Seek_UINT16(s); /* protocolVersion (2 bytes) */ - Stream_Seek_UINT16(s); /* pad2OctetsA (2 bytes) */ - Stream_Seek_UINT16(s); /* generalCompressionTypes (2 bytes) */ - Stream_Read_UINT16(s, extraFlags); /* extraFlags (2 bytes) */ - Stream_Seek_UINT16(s); /* updateCapabilityFlag (2 bytes) */ - Stream_Seek_UINT16(s); /* remoteUnshareFlag (2 bytes) */ - Stream_Seek_UINT16(s); /* generalCompressionLevel (2 bytes) */ - Stream_Read_UINT8(s, refreshRectSupport); /* refreshRectSupport (1 byte) */ - Stream_Read_UINT8(s, suppressOutputSupport); /* suppressOutputSupport (1 byte) */ + Stream_Read_UINT16(s, settings->OsMajorType); /* osMajorType (2 bytes) */ + Stream_Read_UINT16(s, settings->OsMinorType); /* osMinorType (2 bytes) */ + Stream_Seek_UINT16(s); /* protocolVersion (2 bytes) */ + Stream_Seek_UINT16(s); /* pad2OctetsA (2 bytes) */ + Stream_Seek_UINT16(s); /* generalCompressionTypes (2 bytes) */ + Stream_Read_UINT16(s, extraFlags); /* extraFlags (2 bytes) */ + Stream_Seek_UINT16(s); /* updateCapabilityFlag (2 bytes) */ + Stream_Seek_UINT16(s); /* remoteUnshareFlag (2 bytes) */ + Stream_Seek_UINT16(s); /* generalCompressionLevel (2 bytes) */ + Stream_Read_UINT8(s, refreshRectSupport); /* refreshRectSupport (1 byte) */ + Stream_Read_UINT8(s, suppressOutputSupport); /* suppressOutputSupport (1 byte) */ settings->NoBitmapCompressionHeader = (extraFlags & NO_BITMAP_COMPRESSION_HDR) ? TRUE : FALSE; settings->LongCredentialsSupported = (extraFlags & LONG_CREDENTIALS_SUPPORTED) ? TRUE : FALSE; diff --git a/libfreerdp/core/gateway/rdg.c b/libfreerdp/core/gateway/rdg.c index 6b998c8..ffcd3d4 100644 --- a/libfreerdp/core/gateway/rdg.c +++ b/libfreerdp/core/gateway/rdg.c @@ -257,20 +257,18 @@ static const char* flags_to_string(UINT32 flags, const t_flag_mapping* map, size { size_t x = 0; static char buffer[1024] = { 0 }; - char fields[12]; - memset(buffer, 0, sizeof(buffer)); + char fields[12] = { 0 }; for (x = 0; x < elements; x++) { - if (buffer[0] != '\0') - strcat(buffer, "|"); + const t_flag_mapping* cur = &map[x]; - if ((map[x].code & flags) != 0) - strcat(buffer, map[x].name); + if ((cur->code & flags) != 0) + winpr_str_append(cur->name, buffer, sizeof(buffer), "|"); } sprintf_s(fields, ARRAYSIZE(fields), " [%04" PRIx32 "]", flags); - strcat(buffer, fields); + winpr_str_append(fields, buffer, sizeof(buffer), NULL); return buffer; } @@ -314,7 +312,8 @@ static BOOL rdg_read_http_unicode_string(wStream* s, const WCHAR** string, UINT1 /* Read length of the string */ if (rem < 4) { - WLog_ERR(TAG, "[%s]: Could not read stream length, only have % " PRIuz " bytes", rem); + WLog_ERR(TAG, "[%s]: Could not read stream length, only have % " PRIuz " bytes", + __FUNCTION__, rem); return FALSE; } Stream_Read_UINT16(s, strLenBytes); @@ -327,7 +326,7 @@ static BOOL rdg_read_http_unicode_string(wStream* s, const WCHAR** string, UINT1 { WLog_ERR(TAG, "[%s]: Could not read stream data, only have % " PRIuz " bytes, expected %" PRIu16, - rem - 4, strLenBytes); + __FUNCTION__, rem - 4, strLenBytes); return FALSE; } diff --git a/libfreerdp/core/info.c b/libfreerdp/core/info.c index f31b736..1603adc 100644 --- a/libfreerdp/core/info.c +++ b/libfreerdp/core/info.c @@ -41,11 +41,13 @@ static const char* const INFO_TYPE_LOGON_STRINGS[4] = { "Logon Info V1", "Logon /* This define limits the length of the strings in the label field. */ #define MAX_LABEL_LENGTH 40 -static struct +struct info_flags_t { UINT32 flag; const char* label; -} const info_flags[] = { +}; + +static const struct info_flags_t info_flags[] = { { INFO_MOUSE, "INFO_MOUSE" }, { INFO_DISABLECTRLALTDEL, "INFO_DISABLECTRLALTDEL" }, { INFO_AUTOLOGON, "INFO_AUTOLOGON" }, @@ -118,12 +120,8 @@ static BOOL rdp_read_info_null_string(UINT32 flags, wStream* s, size_t cbLen, CH static char* rdp_info_package_flags_description(UINT32 flags) { char* result; - size_t maximum_size = 1; /* Reserve space for the terminating '\0' by strcat if all flags set */ + size_t maximum_size = 1 + MAX_LABEL_LENGTH * ARRAYSIZE(info_flags); size_t i; - size_t size; - - for (i = 0; i < ARRAYSIZE(info_flags); i++) - maximum_size += strnlen(info_flags[i].label, MAX_LABEL_LENGTH) + 1; result = calloc(maximum_size, sizeof(char)); @@ -132,18 +130,13 @@ static char* rdp_info_package_flags_description(UINT32 flags) for (i = 0; i < ARRAYSIZE(info_flags); i++) { - if (info_flags[i].flag & flags) + const struct info_flags_t* cur = &info_flags[i]; + if (cur->flag & flags) { - strcat(result, info_flags[i].label); - strcat(result, "|"); + winpr_str_append(cur->label, result, maximum_size, "|"); } } - size = strnlen(result, maximum_size); - - if (size > 0) - result[size - 1] = '\0'; /* remove last "|" */ - return result; } diff --git a/libfreerdp/core/nego.c b/libfreerdp/core/nego.c index ac4e93f..48739a7 100644 --- a/libfreerdp/core/nego.c +++ b/libfreerdp/core/nego.c @@ -945,6 +945,28 @@ BOOL nego_process_negotiation_request(rdpNego* nego, wStream* s) * @param nego * @param s */ +static const char* nego_rdp_neg_rsp_flags_str(UINT32 flags) +{ + static char buffer[1024] = { 0 }; + + _snprintf(buffer, ARRAYSIZE(buffer), "[0x%02" PRIx32 "] ", flags); + if (flags & EXTENDED_CLIENT_DATA_SUPPORTED) + winpr_str_append("EXTENDED_CLIENT_DATA_SUPPORTED", buffer, sizeof(buffer), "|"); + if (flags & DYNVC_GFX_PROTOCOL_SUPPORTED) + winpr_str_append("DYNVC_GFX_PROTOCOL_SUPPORTED", buffer, sizeof(buffer), "|"); + if (flags & RDP_NEGRSP_RESERVED) + winpr_str_append("RDP_NEGRSP_RESERVED", buffer, sizeof(buffer), "|"); + if (flags & RESTRICTED_ADMIN_MODE_SUPPORTED) + winpr_str_append("RESTRICTED_ADMIN_MODE_SUPPORTED", buffer, sizeof(buffer), "|"); + if (flags & REDIRECTED_AUTHENTICATION_MODE_SUPPORTED) + winpr_str_append("REDIRECTED_AUTHENTICATION_MODE_SUPPORTED", buffer, sizeof(buffer), "|"); + if ((flags & + ~(EXTENDED_CLIENT_DATA_SUPPORTED | DYNVC_GFX_PROTOCOL_SUPPORTED | RDP_NEGRSP_RESERVED | + RESTRICTED_ADMIN_MODE_SUPPORTED | REDIRECTED_AUTHENTICATION_MODE_SUPPORTED))) + winpr_str_append("UNKNOWN", buffer, sizeof(buffer), "|"); + + return buffer; +} BOOL nego_process_negotiation_response(rdpNego* nego, wStream* s) { @@ -959,6 +981,7 @@ BOOL nego_process_negotiation_response(rdpNego* nego, wStream* s) } Stream_Read_UINT8(s, nego->flags); + WLog_DBG(TAG, "RDP_NEG_RSP::flags = { %s }", nego_rdp_neg_rsp_flags_str(nego->flags)); Stream_Read_UINT16(s, length); Stream_Read_UINT32(s, nego->SelectedProtocol); nego->state = NEGO_STATE_FINAL; diff --git a/libfreerdp/core/nego.h b/libfreerdp/core/nego.h index f67e4fa..ce7a47e 100644 --- a/libfreerdp/core/nego.h +++ b/libfreerdp/core/nego.h @@ -81,6 +81,7 @@ enum RDP_NEG_MSG #define DYNVC_GFX_PROTOCOL_SUPPORTED 0x02 #define RDP_NEGRSP_RESERVED 0x04 #define RESTRICTED_ADMIN_MODE_SUPPORTED 0x08 +#define REDIRECTED_AUTHENTICATION_MODE_SUPPORTED 0x10 #define PRECONNECTION_PDU_V1_SIZE 16 #define PRECONNECTION_PDU_V2_MIN_SIZE (PRECONNECTION_PDU_V1_SIZE + 2) diff --git a/libfreerdp/core/peer.c b/libfreerdp/core/peer.c index ce8628f..aff32c2 100644 --- a/libfreerdp/core/peer.c +++ b/libfreerdp/core/peer.c @@ -866,6 +866,94 @@ void freerdp_peer_context_free(freerdp_peer* client) } } +static const char* os_major_type_to_string(UINT16 osMajorType) +{ + switch (osMajorType) + { + case OSMAJORTYPE_UNSPECIFIED: + return "Unspecified platform"; + case OSMAJORTYPE_WINDOWS: + return "Windows platform"; + case OSMAJORTYPE_OS2: + return "OS/2 platform"; + case OSMAJORTYPE_MACINTOSH: + return "Macintosh platform"; + case OSMAJORTYPE_UNIX: + return "UNIX platform"; + case OSMAJORTYPE_IOS: + return "iOS platform"; + case OSMAJORTYPE_OSX: + return "OS X platform"; + case OSMAJORTYPE_ANDROID: + return "Android platform"; + case OSMAJORTYPE_CHROME_OS: + return "Chrome OS platform"; + } + + return "Unknown platform"; +} + +const char* freerdp_peer_os_major_type_string(freerdp_peer* client) +{ + rdpContext* context; + UINT16 osMajorType; + + WINPR_ASSERT(client); + + context = client->context; + WINPR_ASSERT(context); + WINPR_ASSERT(context->settings); + + osMajorType = freerdp_settings_get_uint32(context->settings, FreeRDP_OsMajorType); + + return os_major_type_to_string(osMajorType); +} + +static const char* os_minor_type_to_string(UINT16 osMinorType) +{ + switch (osMinorType) + { + case OSMINORTYPE_UNSPECIFIED: + return "Unspecified version"; + case OSMINORTYPE_WINDOWS_31X: + return "Windows 3.1x"; + case OSMINORTYPE_WINDOWS_95: + return "Windows 95"; + case OSMINORTYPE_WINDOWS_NT: + return "Windows NT"; + case OSMINORTYPE_OS2_V21: + return "OS/2 2.1"; + case OSMINORTYPE_POWER_PC: + return "PowerPC"; + case OSMINORTYPE_MACINTOSH: + return "Macintosh"; + case OSMINORTYPE_NATIVE_XSERVER: + return "Native X Server"; + case OSMINORTYPE_PSEUDO_XSERVER: + return "Pseudo X Server"; + case OSMINORTYPE_WINDOWS_RT: + return "Windows RT"; + } + + return "Unknown version"; +} + +const char* freerdp_peer_os_minor_type_string(freerdp_peer* client) +{ + rdpContext* context; + UINT16 osMinorType; + + WINPR_ASSERT(client); + + context = client->context; + WINPR_ASSERT(context); + WINPR_ASSERT(context->settings); + + osMinorType = freerdp_settings_get_uint32(context->settings, FreeRDP_OsMinorType); + + return os_minor_type_to_string(osMinorType); +} + freerdp_peer* freerdp_peer_new(int sockfd) { UINT32 option_value; diff --git a/libfreerdp/core/server.c b/libfreerdp/core/server.c index 484ed8b..8e36972 100644 --- a/libfreerdp/core/server.c +++ b/libfreerdp/core/server.c @@ -158,6 +158,7 @@ static BOOL wts_read_drdynvc_capabilities_response(rdpPeerChannel* channel, UINT static BOOL wts_read_drdynvc_create_response(rdpPeerChannel* channel, wStream* s, UINT32 length) { UINT32 CreationStatus; + BOOL status = TRUE; if (length < 4) return FALSE; @@ -176,7 +177,12 @@ static BOOL wts_read_drdynvc_create_response(rdpPeerChannel* channel, wStream* s channel->dvc_open_state = DVC_OPEN_STATE_SUCCEEDED; } - return TRUE; + IFCALLRET(channel->vcm->dvc_creation_status, status, channel->vcm->dvc_creation_status_userdata, + channel->channelId, (INT32)CreationStatus); + if (!status) + WLog_ERR(TAG, "vcm->dvc_creation_status failed!"); + + return status; } static BOOL wts_read_drdynvc_data_first(rdpPeerChannel* channel, wStream* s, int cbLen, @@ -578,6 +584,17 @@ BYTE WTSVirtualChannelManagerGetDrdynvcState(HANDLE hServer) return vcm->drdynvc_state; } +void WTSVirtualChannelManagerSetDVCCreationCallback(HANDLE hServer, psDVCCreationStatusCallback cb, + void* userdata) +{ + WTSVirtualChannelManager* vcm = hServer; + + WINPR_ASSERT(vcm); + + vcm->dvc_creation_status = cb; + vcm->dvc_creation_status_userdata = userdata; +} + UINT16 WTSChannelGetId(freerdp_peer* client, const char* channel_name) { rdpMcsChannel* channel; @@ -593,6 +610,15 @@ UINT16 WTSChannelGetId(freerdp_peer* client, const char* channel_name) return channel->ChannelId; } +UINT32 WTSChannelGetIdByHandle(HANDLE hChannelHandle) +{ + rdpPeerChannel* channel = hChannelHandle; + + WINPR_ASSERT(channel); + + return channel->channelId; +} + BOOL WTSChannelSetHandleByName(freerdp_peer* client, const char* channel_name, void* handle) { rdpMcsChannel* channel; @@ -670,6 +696,43 @@ const char* WTSChannelGetName(freerdp_peer* client, UINT16 channel_id) return (const char*)channel->Name; } +INT64 WTSChannelGetOptions(freerdp_peer* client, UINT16 channel_id) +{ + rdpMcsChannel* channel; + + if (!client || !client->context || !client->context->rdp) + return -1; + + channel = wts_get_joined_channel_by_id(client->context->rdp->mcs, channel_id); + + if (!channel) + return -1; + + return (INT64)channel->options; +} + +char** WTSGetAcceptedChannelNames(freerdp_peer* client, size_t* count) +{ + rdpMcs* mcs; + char** names; + UINT32 index; + + if (!client || !client->context || !count) + return NULL; + + mcs = client->context->rdp->mcs; + *count = mcs->channelCount; + + names = (char**)calloc(mcs->channelCount, sizeof(char*)); + if (!names) + return NULL; + + for (index = 0; index < mcs->channelCount; index++) + names[index] = mcs->channels[index].Name; + + return names; +} + BOOL WINAPI FreeRDP_WTSStartRemoteControlSessionW(LPWSTR pTargetServerName, ULONG TargetLogonId, BYTE HotkeyVk, USHORT HotkeyModifiers) { diff --git a/libfreerdp/core/server.h b/libfreerdp/core/server.h index 3e2552e..ea1415c 100644 --- a/libfreerdp/core/server.h +++ b/libfreerdp/core/server.h @@ -86,6 +86,9 @@ struct WTSVirtualChannelManager BYTE drdynvc_state; LONG dvc_channel_id_seq; + psDVCCreationStatusCallback dvc_creation_status; + void* dvc_creation_status_userdata; + wArrayList* dynamicVirtualChannels; }; diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index 6a7b990..76b0e79 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -599,6 +599,9 @@ rdpSettings* freerdp_settings_new(DWORD flags) settings->TlsSecLevel = 1; settings->OrderSupport = calloc(1, 32); + freerdp_settings_set_uint16(settings, FreeRDP_TLSMinVersion, TLS1_VERSION); + freerdp_settings_set_uint16(settings, FreeRDP_TLSMaxVersion, 0); + if (!settings->OrderSupport) goto out_fail; diff --git a/libfreerdp/core/test/settings_property_lists.h b/libfreerdp/core/test/settings_property_lists.h index db8e5ed..dcf1e49 100644 --- a/libfreerdp/core/test/settings_property_lists.h +++ b/libfreerdp/core/test/settings_property_lists.h @@ -173,6 +173,8 @@ static const size_t bool_list_indices[] = { static const size_t uint16_list_indices[] = { FreeRDP_DesktopOrientation, FreeRDP_ProxyPort, + FreeRDP_TLSMaxVersion, + FreeRDP_TLSMinVersion, }; #define have_uint32_list_indices diff --git a/libfreerdp/crypto/opensslcompat.c b/libfreerdp/crypto/opensslcompat.c index 701902d..314d6b5 100644 --- a/libfreerdp/crypto/opensslcompat.c +++ b/libfreerdp/crypto/opensslcompat.c @@ -19,7 +19,8 @@ #include "opensslcompat.h" -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) BIO_METHOD* BIO_meth_new(int type, const char* name) { diff --git a/libfreerdp/crypto/opensslcompat.h b/libfreerdp/crypto/opensslcompat.h index 169e8e4..3ad5b6b 100644 --- a/libfreerdp/crypto/opensslcompat.h +++ b/libfreerdp/crypto/opensslcompat.h @@ -30,7 +30,8 @@ #include -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) #include #include diff --git a/libfreerdp/crypto/tls.c b/libfreerdp/crypto/tls.c index 1990302..99b0792 100644 --- a/libfreerdp/crypto/tls.c +++ b/libfreerdp/crypto/tls.c @@ -77,6 +77,7 @@ struct _BIO_RDP_TLS }; typedef struct _BIO_RDP_TLS BIO_RDP_TLS; +static BOOL tls_prep(rdpTls* tls, BIO* underlying, int options, BOOL clientMode); static int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, UINT16 port); static void tls_print_certificate_name_mismatch_error(const char* hostname, UINT16 port, const char* common_name, char** alt_names, @@ -667,8 +668,18 @@ static BOOL tls_prepare(rdpTls* tls, BIO* underlying, SSL_METHOD* method, int op SSL_CTX_set_options(tls->ctx, options); SSL_CTX_set_read_ahead(tls->ctx, 1); #if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) - SSL_CTX_set_min_proto_version(tls->ctx, TLS1_VERSION); /* min version */ - SSL_CTX_set_max_proto_version(tls->ctx, 0); /* highest supported version by library */ + UINT16 version = freerdp_settings_get_uint16(settings, FreeRDP_TLSMinVersion); + if (!SSL_CTX_set_min_proto_version(tls->ctx, version)) + { + WLog_ERR(TAG, "SSL_CTX_set_min_proto_version %s failed", version); + return FALSE; + } + version = freerdp_settings_get_uint16(settings, FreeRDP_TLSMaxVersion); + if (!SSL_CTX_set_max_proto_version(tls->ctx, version)) + { + WLog_ERR(TAG, "SSL_CTX_set_max_proto_version %s failed", version); + return FALSE; + } #endif #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) SSL_CTX_set_security_level(tls->ctx, settings->TlsSecLevel); @@ -855,6 +866,18 @@ int tls_connect(rdpTls* tls, BIO* underlying) * support empty fragments. This needs to be disabled. */ options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; + + if (!tls_prep(tls, underlying, options, TRUE)) + return 0; + +#if !defined(OPENSSL_NO_TLSEXT) && !defined(LIBRESSL_VERSION_NUMBER) + SSL_set_tlsext_host_name(tls->ssl, tls->hostname); +#endif + return tls_do_handshake(tls, TRUE); +} + +BOOL tls_prep(rdpTls* tls, BIO* underlying, int options, BOOL clientMode) +{ #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) /** * disable SSLv2 and SSLv3 @@ -862,16 +885,10 @@ int tls_connect(rdpTls* tls, BIO* underlying) options |= SSL_OP_NO_SSLv2; options |= SSL_OP_NO_SSLv3; - if (!tls_prepare(tls, underlying, SSLv23_client_method(), options, TRUE)) + return tls_prepare(tls, underlying, SSLv23_client_method(), options, clientMode); #else - if (!tls_prepare(tls, underlying, TLS_client_method(), options, TRUE)) + return tls_prepare(tls, underlying, TLS_client_method(), options, clientMode); #endif - return 0; - -#if !defined(OPENSSL_NO_TLSEXT) && !defined(LIBRESSL_VERSION_NUMBER) - SSL_set_tlsext_host_name(tls->ssl, tls->hostname); -#endif - return tls_do_handshake(tls, TRUE); } #if defined(MICROSOFT_IOS_SNI_BUG) && !defined(OPENSSL_NO_TLSEXT) && \ diff --git a/libfreerdp/utils/CMakeLists.txt b/libfreerdp/utils/CMakeLists.txt index e3a8981..d09ca8b 100644 --- a/libfreerdp/utils/CMakeLists.txt +++ b/libfreerdp/utils/CMakeLists.txt @@ -20,6 +20,7 @@ set(MODULE_PREFIX "FREERDP_UTILS") set(${MODULE_PREFIX}_SRCS passphrase.c + cliprdr_utils.c pcap.c profiler.c ringbuffer.c diff --git a/libfreerdp/utils/cliprdr_utils.c b/libfreerdp/utils/cliprdr_utils.c new file mode 100644 index 0000000..7fb99d6 --- /dev/null +++ b/libfreerdp/utils/cliprdr_utils.c @@ -0,0 +1,235 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Clipboard Virtual Channel Extension + * + * Copyright 2013 Marc-Andre Moreau + * Copyright 2022 Armin Novak +#include +#include + +#include +#define TAG FREERDP_TAG("utils." CLIPRDR_SVC_CHANNEL_NAME) + +#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520) +#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024) + +static UINT64 filetime_to_uint64(FILETIME value) +{ + UINT64 converted = 0; + converted |= (UINT32)value.dwHighDateTime; + converted <<= 32; + converted |= (UINT32)value.dwLowDateTime; + return converted; +} + +static FILETIME uint64_to_filetime(UINT64 value) +{ + FILETIME converted; + converted.dwLowDateTime = (UINT32)(value >> 0); + converted.dwHighDateTime = (UINT32)(value >> 32); + return converted; +} + +/** + * Parse a packed file list. + * + * The resulting array must be freed with the `free()` function. + * + * @param [in] format_data packed `CLIPRDR_FILELIST` to parse. + * @param [in] format_data_length length of `format_data` in bytes. + * @param [out] file_descriptor_array parsed array of `FILEDESCRIPTOR` structs. + * @param [out] file_descriptor_count number of elements in `file_descriptor_array`. + * + * @returns 0 on success, otherwise a Win32 error code. + */ +UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length, + FILEDESCRIPTORW** file_descriptor_array, UINT32* file_descriptor_count) +{ + UINT result = NO_ERROR; + UINT32 i; + UINT32 count = 0; + wStream sbuffer; + wStream* s = &sbuffer; + + if (!format_data || !file_descriptor_array || !file_descriptor_count) + return ERROR_BAD_ARGUMENTS; + + Stream_StaticInit(&sbuffer, format_data, format_data_length); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + if (Stream_GetRemainingLength(s) < 4) + { + WLog_ERR(TAG, "invalid packed file list"); + + result = ERROR_INCORRECT_SIZE; + goto out; + } + + Stream_Read_UINT32(s, count); /* cItems (4 bytes) */ + + if (Stream_GetRemainingLength(s) / CLIPRDR_FILEDESCRIPTOR_SIZE < count) + { + WLog_ERR(TAG, "packed file list is too short: expected %" PRIuz ", have %" PRIuz, + ((size_t)count) * CLIPRDR_FILEDESCRIPTOR_SIZE, Stream_GetRemainingLength(s)); + + result = ERROR_INCORRECT_SIZE; + goto out; + } + + *file_descriptor_count = count; + *file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTORW)); + if (!*file_descriptor_array) + { + result = ERROR_NOT_ENOUGH_MEMORY; + goto out; + } + + for (i = 0; i < count; i++) + { + UINT64 tmp; + FILEDESCRIPTORW* file = &((*file_descriptor_array)[i]); + + Stream_Read_UINT32(s, file->dwFlags); /* flags (4 bytes) */ + Stream_Read_UINT32(s, file->clsid.Data1); + Stream_Read_UINT16(s, file->clsid.Data2); + Stream_Read_UINT16(s, file->clsid.Data3); + Stream_Read(s, &file->clsid.Data4, sizeof(file->clsid.Data4)); + Stream_Read_INT32(s, file->sizel.cx); + Stream_Read_INT32(s, file->sizel.cy); + Stream_Read_INT32(s, file->pointl.x); + Stream_Read_INT32(s, file->pointl.y); + Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */ + Stream_Read_UINT64(s, tmp); /* ftCreationTime (8 bytes) */ + file->ftCreationTime = uint64_to_filetime(tmp); + Stream_Read_UINT64(s, tmp); /* ftLastAccessTime (8 bytes) */ + file->ftLastAccessTime = uint64_to_filetime(tmp); + Stream_Read_UINT64(s, tmp); /* lastWriteTime (8 bytes) */ + file->ftLastWriteTime = uint64_to_filetime(tmp); + Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */ + Stream_Read_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */ + Stream_Read_UTF16_String(s, file->cFileName, + ARRAYSIZE(file->cFileName)); /* cFileName (520 bytes) */ + } + + if (Stream_GetRemainingLength(s) > 0) + WLog_WARN(TAG, "packed file list has %" PRIuz " excess bytes", + Stream_GetRemainingLength(s)); +out: + + return result; +} + +/** + * Serialize a packed file list. + * + * The resulting format data must be freed with the `free()` function. + * + * @param [in] file_descriptor_array array of `FILEDESCRIPTOR` structs to serialize. + * @param [in] file_descriptor_count number of elements in `file_descriptor_array`. + * @param [out] format_data serialized CLIPRDR_FILELIST. + * @param [out] format_data_length length of `format_data` in bytes. + * + * @returns 0 on success, otherwise a Win32 error code. + */ +UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array, + UINT32 file_descriptor_count, BYTE** format_data, + UINT32* format_data_length) +{ + return cliprdr_serialize_file_list_ex(CB_STREAM_FILECLIP_ENABLED, file_descriptor_array, + file_descriptor_count, format_data, format_data_length); +} + +UINT cliprdr_serialize_file_list_ex(UINT32 flags, const FILEDESCRIPTORW* file_descriptor_array, + UINT32 file_descriptor_count, BYTE** format_data, + UINT32* format_data_length) +{ + UINT result = NO_ERROR; + UINT32 i; + size_t len; + wStream* s = NULL; + + if (!file_descriptor_array || !format_data || !format_data_length) + return ERROR_BAD_ARGUMENTS; + + if ((flags & CB_STREAM_FILECLIP_ENABLED) == 0) + { + WLog_WARN(TAG, "No file clipboard support annouonced!"); + return ERROR_BAD_ARGUMENTS; + } + + s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */ + + for (i = 0; i < file_descriptor_count; i++) + { + int c; + UINT64 lastWriteTime; + const FILEDESCRIPTORW* file = &file_descriptor_array[i]; + + /* + * There is a known issue with Windows server getting stuck in + * an infinite loop when downloading files that are larger than + * 2 gigabytes. Do not allow clients to send such file lists. + * + * https://support.microsoft.com/en-us/help/2258090 + */ + if ((flags & CB_HUGE_FILE_SUPPORT_ENABLED) == 0) + { + if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE)) + { + WLog_ERR(TAG, "cliprdr does not support files over 2 GB"); + result = ERROR_FILE_TOO_LARGE; + goto error; + } + } + + Stream_Write_UINT32(s, file->dwFlags); /* flags (4 bytes) */ + Stream_Zero(s, 32); /* reserved1 (32 bytes) */ + Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */ + Stream_Zero(s, 16); /* reserved2 (16 bytes) */ + lastWriteTime = filetime_to_uint64(file->ftLastWriteTime); + Stream_Write_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */ + Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */ + Stream_Write_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */ + for (c = 0; c < 260; c++) /* cFileName (520 bytes) */ + Stream_Write_UINT16(s, file->cFileName[c]); + } + + Stream_SealLength(s); + + Stream_GetBuffer(s, *format_data); + Stream_GetLength(s, len); + if (len > UINT32_MAX) + goto error; + + *format_data_length = (UINT32)len; + + Stream_Free(s, FALSE); + + return result; + +error: + Stream_Free(s, TRUE); + + return result; +} diff --git a/server/shadow/shadow_client.c b/server/shadow/shadow_client.c index e6bc67b..0a1519b 100644 --- a/server/shadow/shadow_client.c +++ b/server/shadow/shadow_client.c @@ -596,11 +596,12 @@ shadow_client_rdpgfx_frame_acknowledge(RdpgfxServerContext* context, static BOOL shadow_are_caps_filtered(const rdpSettings* settings, UINT32 caps) { const UINT32 filter = settings->GfxCapsFilter; - const UINT32 capList[] = { - RDPGFX_CAPVERSION_8, RDPGFX_CAPVERSION_81, RDPGFX_CAPVERSION_10, - RDPGFX_CAPVERSION_101, RDPGFX_CAPVERSION_102, RDPGFX_CAPVERSION_103, - RDPGFX_CAPVERSION_104, RDPGFX_CAPVERSION_105, RDPGFX_CAPVERSION_106 - }; + const UINT32 capList[] = { RDPGFX_CAPVERSION_8, RDPGFX_CAPVERSION_81, + RDPGFX_CAPVERSION_10, RDPGFX_CAPVERSION_101, + RDPGFX_CAPVERSION_102, RDPGFX_CAPVERSION_103, + RDPGFX_CAPVERSION_104, RDPGFX_CAPVERSION_105, + RDPGFX_CAPVERSION_106, RDPGFX_CAPVERSION_106_ERR, + RDPGFX_CAPVERSION_107 }; UINT32 x; for (x = 0; x < ARRAYSIZE(capList); x++) @@ -682,10 +683,19 @@ static UINT shadow_client_rdpgfx_caps_advertise(RdpgfxServerContext* context, if (!shadow_client_refresh_rect(&client->context, 0, NULL)) return rc; + if (shadow_client_caps_test_version(context, h264, capsAdvertise->capsSets, + capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_107, &rc)) + return rc; + if (shadow_client_caps_test_version(context, h264, capsAdvertise->capsSets, capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_106, &rc)) return rc; + if (shadow_client_caps_test_version(context, h264, capsAdvertise->capsSets, + capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_106_ERR, + &rc)) + return rc; + if (shadow_client_caps_test_version(context, h264, capsAdvertise->capsSets, capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_105, &rc)) return rc; diff --git a/uwac/include/uwac/uwac.h b/uwac/include/uwac/uwac.h index 8bd37d4..c3ad12c 100644 --- a/uwac/include/uwac/uwac.h +++ b/uwac/include/uwac/uwac.h @@ -551,10 +551,18 @@ extern "C" UWAC_API void UwacWindowSetTitle(UwacWindow* window, const char* name); /** + * Sets the app id of the UwacWindow * - * @param display - * @param timeout - * @return + * @param window the UwacWindow + * @param app_id app id + */ + UWAC_API void UwacWindowSetAppId(UwacWindow* window, const char* app_id); + + /** Dispatch the display + * + * @param display The display to dispatch + * @param timeout The maximum time to wait in milliseconds (-1 == infinite). + * @return 1 for success, 0 if display not running, -1 on failure */ UWAC_API int UwacDisplayDispatch(UwacDisplay* display, int timeout); diff --git a/uwac/libuwac/uwac-window.c b/uwac/libuwac/uwac-window.c index 8e6f08d..4600ad7 100644 --- a/uwac/libuwac/uwac-window.c +++ b/uwac/libuwac/uwac-window.c @@ -817,3 +817,9 @@ void UwacWindowSetTitle(UwacWindow* window, const char* name) else if (window->shell_surface) wl_shell_surface_set_title(window->shell_surface, name); } + +void UwacWindowSetAppId(UwacWindow* window, const char* app_id) +{ + if (window->xdg_toplevel) + xdg_toplevel_set_app_id(window->xdg_toplevel, app_id); +} diff --git a/winpr/CMakeLists.txt b/winpr/CMakeLists.txt index d8a40da..cf500ed 100644 --- a/winpr/CMakeLists.txt +++ b/winpr/CMakeLists.txt @@ -57,7 +57,7 @@ if (NOT WIN32) endif() # Soname versioning -set(RAW_VERSION_STRING "2.7.0") +set(RAW_VERSION_STRING "2.8.0") if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag") file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING) elseif(USE_VERSION_FROM_GIT_TAG) @@ -114,6 +114,10 @@ else() check_include_files(sys/sockio.h HAVE_SYS_SOCKIO_H) check_include_files(sys/strtio.h HAVE_SYS_STRTIO_H) check_include_files(sys/select.h HAVE_SYS_SELECT_H) + check_include_files(unwind.h HAVE_UNWIND_H) + if (HAVE_UNWIND_H) + add_definitions(-DHAVE_UNWIND_H) + endif() else() set(HAVE_FCNTL_H 1) set(HAVE_UNISTD_H 1) @@ -127,7 +131,7 @@ else() set(HAVE_TM_GMTOFF 1) endif() - if(NOT WIN32) + if(NOT WIN32 AND NOT IOS) CHECK_SYMBOL_EXISTS(pthread_mutex_timedlock pthread.h HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL) if (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL) CHECK_LIBRARY_EXISTS(pthread pthread_mutex_timedlock "" HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB) diff --git a/winpr/include/winpr/clipboard.h b/winpr/include/winpr/clipboard.h index bbf2f5f..95fb913 100644 --- a/winpr/include/winpr/clipboard.h +++ b/winpr/include/winpr/clipboard.h @@ -64,6 +64,8 @@ struct _wClipboardDelegate (wClipboardDelegate*, const wClipboardFileRangeRequest*, const BYTE* data, UINT32 size); UINT(*ClipboardFileRangeFailure) (wClipboardDelegate*, const wClipboardFileRangeRequest*, UINT errorCode); + + BOOL (*IsFileNameComponentValid)(LPCWSTR lpFileName); }; #ifdef __cplusplus diff --git a/winpr/include/winpr/crt.h b/winpr/include/winpr/crt.h index 181ac14..3f6b5a1 100644 --- a/winpr/include/winpr/crt.h +++ b/winpr/include/winpr/crt.h @@ -32,6 +32,12 @@ #ifndef _WIN32 +#include + +#ifndef _write +#define _write write +#endif + #ifndef _strtoui64 #define _strtoui64 strtoull #endif diff --git a/winpr/include/winpr/stream.h b/winpr/include/winpr/stream.h index f351eaa..ed637f0 100644 --- a/winpr/include/winpr/stream.h +++ b/winpr/include/winpr/stream.h @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" @@ -56,6 +57,19 @@ extern "C" WINPR_API void Stream_StaticInit(wStream* s, BYTE* buffer, size_t size); WINPR_API void Stream_Free(wStream* s, BOOL bFreeBuffer); +#define Stream_CheckAndLogRequiredLength(tag, s, len) \ + Stream_CheckAndLogRequiredLengthEx(tag, WLOG_WARN, s, len, "%s(%s:%d)", __FUNCTION__, \ + __FILE__, __LINE__) + WINPR_API BOOL Stream_CheckAndLogRequiredLengthEx(const char* tag, DWORD level, wStream* s, + UINT64 len, const char* fmt, ...); + WINPR_API BOOL Stream_CheckAndLogRequiredLengthExVa(const char* tag, DWORD level, wStream* s, + UINT64 len, const char* fmt, va_list args); + WINPR_API BOOL Stream_CheckAndLogRequiredLengthWLogEx(wLog* log, DWORD level, wStream* s, + UINT64 len, const char* fmt, ...); + WINPR_API BOOL Stream_CheckAndLogRequiredLengthWLogExVa(wLog* log, DWORD level, wStream* s, + UINT64 len, const char* fmt, + va_list args); + static INLINE void Stream_Seek(wStream* s, size_t _offset) { s->pointer += (_offset); diff --git a/winpr/include/winpr/string.h b/winpr/include/winpr/string.h index 8ce83bc..69af1a0 100644 --- a/winpr/include/winpr/string.h +++ b/winpr/include/winpr/string.h @@ -31,6 +31,9 @@ extern "C" { #endif + WINPR_API BOOL winpr_str_append(const char* what, char* buffer, size_t size, + const char* separator); + #ifndef _WIN32 #define CSTR_LESS_THAN 1 diff --git a/winpr/include/winpr/thread.h b/winpr/include/winpr/thread.h index 4e603b3..c7961e9 100644 --- a/winpr/include/winpr/thread.h +++ b/winpr/include/winpr/thread.h @@ -207,7 +207,7 @@ extern "C" LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId); - WINPR_API DECLSPEC_NORETURN VOID ExitThread(DWORD dwExitCode); + WINPR_API VOID ExitThread(DWORD dwExitCode); WINPR_API BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode); WINPR_API HANDLE _GetCurrentThread(void); diff --git a/winpr/include/winpr/windows.h b/winpr/include/winpr/windows.h index b5313f0..5bc6722 100644 --- a/winpr/include/winpr/windows.h +++ b/winpr/include/winpr/windows.h @@ -37,10 +37,34 @@ /* Client System Parameters Update PDU * defined in winuser.h */ -#define SPI_SETCARETWIDTH 0x00002007 -#define SPI_SETSTICKYKEYS 0x0000003B -#define SPI_SETTOGGLEKEYS 0x00000035 -#define SPI_SETFILTERKEYS 0x00000033 +typedef enum +{ + SPI_SETDRAGFULLWINDOWS = 0x00000025, + SPI_SETKEYBOARDCUES = 0x0000100B, + SPI_SETKEYBOARDPREF = 0x00000045, + SPI_SETWORKAREA = 0x0000002f, + RAIL_SPI_DISPLAYCHANGE = 0x0000F001, + SPI_SETMOUSEBUTTONSWAP = 0x00000021, + RAIL_SPI_TASKBARPOS = 0x0000F000, + SPI_SETHIGHCONTRAST = 0x00000043, + SPI_SETCARETWIDTH = 0x00002007, + SPI_SETSTICKYKEYS = 0x0000003B, + SPI_SETTOGGLEKEYS = 0x00000035, + SPI_SETFILTERKEYS = 0x00000033, + RAIL_SPI_DISPLAY_ANIMATIONS_ENABLED = 0x0000F002, + RAIL_SPI_DISPLAY_ADVANCED_EFFECTS_ENABLED = 0x0000F003, + RAIL_SPI_DISPLAY_AUTO_HIDE_SCROLLBARS = 0x0000F004, + RAIL_SPI_DISPLAY_MESSAGE_DURATION = 0x0000F005, + RAIL_SPI_CLOSED_CAPTION_FONT_COLOR = 0x0000F006, + RAIL_SPI_CLOSED_CAPTION_FONT_OPACITY = 0x0000F007, + RAIL_SPI_CLOSED_CAPTION_FONT_SIZE = 0x0000F008, + RAIL_SPI_CLOSED_CAPTION_FONT_STYLE = 0x0000F009, + RAIL_SPI_CLOSED_CAPTION_FONT_EDGE_EFFECT = 0x0000F00A, + RAIL_SPI_CLOSED_CAPTION_BACKGROUND_COLOR = 0x0000F00B, + RAIL_SPI_CLOSED_CAPTION_BACKGROUND_OPACITY = 0x0000F00C, + RAIL_SPI_CLOSED_CAPTION_REGION_COLOR = 0x0000F00D, + RAIL_SPI_CLOSED_CAPTION_REGION_OPACITY = 0x0000F00E +} SystemParam; /* Server System Parameters Update PDU */ #define SPI_SETSCREENSAVEACTIVE 0x00000011 diff --git a/winpr/libwinpr/CMakeLists.txt b/winpr/libwinpr/CMakeLists.txt index d9673f2..2df8d89 100644 --- a/winpr/libwinpr/CMakeLists.txt +++ b/winpr/libwinpr/CMakeLists.txt @@ -78,13 +78,15 @@ macro (winpr_definition_add) endmacro() set(CMAKE_REQUIRED_LIBRARIES rt) -check_function_exists(timer_create TIMER_CREATE) -check_function_exists(timer_delete TIMER_DELETE) -check_function_exists(timer_settime TIMER_SETTIME) -check_function_exists(timer_gettime TIMER_GETTIME) -if (TIMER_CREATE AND TIMER_DELETE AND TIMER_SETTIME AND TIMER_GETTIME) - add_definitions(-DWITH_POSIX_TIMER) - winpr_library_add_private(rt) +if (NOT IOS) + check_function_exists(timer_create TIMER_CREATE) + check_function_exists(timer_delete TIMER_DELETE) + check_function_exists(timer_settime TIMER_SETTIME) + check_function_exists(timer_gettime TIMER_GETTIME) + if (TIMER_CREATE AND TIMER_DELETE AND TIMER_SETTIME AND TIMER_GETTIME) + add_definitions(-DWITH_POSIX_TIMER) + winpr_library_add_private(rt) + endif() endif() if (ANDROID) diff --git a/winpr/libwinpr/clipboard/posix.c b/winpr/libwinpr/clipboard/posix.c index 3fe1d70..867fddf 100644 --- a/winpr/libwinpr/clipboard/posix.c +++ b/winpr/libwinpr/clipboard/posix.c @@ -182,10 +182,13 @@ error: * Note that the function converts a single file name component, * it does not take care of component separators. */ -static WCHAR* convert_local_name_component_to_remote(const char* local_name) +static WCHAR* convert_local_name_component_to_remote(wClipboard* clipboard, const char* local_name) { + wClipboardDelegate* delegate = ClipboardGetDelegate(clipboard); WCHAR* remote_name = NULL; + WINPR_ASSERT(delegate); + /* * Note that local file names are not actually guaranteed to be * encoded in UTF-8. Filesystems and users can use whatever they @@ -203,8 +206,12 @@ static WCHAR* convert_local_name_component_to_remote(const char* local_name) * Some file names are not valid on Windows. Check for these now * so that we won't get ourselves into a trouble later as such names * are known to crash some Windows shells when pasted via clipboard. + * + * The IsFileNameComponentValid callback can be overridden by the API + * user, if it is known, that the connected peer is not on the + * Windows platform. */ - if (!ValidFileNameComponent(remote_name)) + if (!delegate->IsFileNameComponentValid(remote_name)) { WLog_ERR(TAG, "invalid file name component: %s", local_name); goto error; @@ -252,10 +259,12 @@ static WCHAR* concat_remote_name(const WCHAR* dir, const WCHAR* file) return buffer; } -static BOOL add_file_to_list(const char* local_name, const WCHAR* remote_name, wArrayList* files); +static BOOL add_file_to_list(wClipboard* clipboard, const char* local_name, + const WCHAR* remote_name, wArrayList* files); -static BOOL add_directory_entry_to_list(const char* local_dir_name, const WCHAR* remote_dir_name, - const struct dirent* entry, wArrayList* files) +static BOOL add_directory_entry_to_list(wClipboard* clipboard, const char* local_dir_name, + const WCHAR* remote_dir_name, const struct dirent* entry, + wArrayList* files) { BOOL result = FALSE; char* local_name = NULL; @@ -266,7 +275,7 @@ static BOOL add_directory_entry_to_list(const char* local_dir_name, const WCHAR* if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) return TRUE; - remote_base_name = convert_local_name_component_to_remote(entry->d_name); + remote_base_name = convert_local_name_component_to_remote(clipboard, entry->d_name); if (!remote_base_name) return FALSE; @@ -275,7 +284,7 @@ static BOOL add_directory_entry_to_list(const char* local_dir_name, const WCHAR* remote_name = concat_remote_name(remote_dir_name, remote_base_name); if (local_name && remote_name) - result = add_file_to_list(local_name, remote_name, files); + result = add_file_to_list(clipboard, local_name, remote_name, files); free(remote_base_name); free(remote_name); @@ -283,8 +292,9 @@ static BOOL add_directory_entry_to_list(const char* local_dir_name, const WCHAR* return result; } -static BOOL do_add_directory_contents_to_list(const char* local_name, const WCHAR* remote_name, - DIR* dirp, wArrayList* files) +static BOOL do_add_directory_contents_to_list(wClipboard* clipboard, const char* local_name, + const WCHAR* remote_name, DIR* dirp, + wArrayList* files) { /* * For some reason POSIX does not require readdir() to be thread-safe. @@ -316,15 +326,15 @@ static BOOL do_add_directory_contents_to_list(const char* local_name, const WCHA return FALSE; } - if (!add_directory_entry_to_list(local_name, remote_name, entry, files)) + if (!add_directory_entry_to_list(clipboard, local_name, remote_name, entry, files)) return FALSE; } return TRUE; } -static BOOL add_directory_contents_to_list(const char* local_name, const WCHAR* remote_name, - wArrayList* files) +static BOOL add_directory_contents_to_list(wClipboard* clipboard, const char* local_name, + const WCHAR* remote_name, wArrayList* files) { BOOL result = FALSE; DIR* dirp = NULL; @@ -338,7 +348,7 @@ static BOOL add_directory_contents_to_list(const char* local_name, const WCHAR* goto out; } - result = do_add_directory_contents_to_list(local_name, remote_name, dirp, files); + result = do_add_directory_contents_to_list(clipboard, local_name, remote_name, dirp, files); if (closedir(dirp)) { @@ -350,7 +360,8 @@ out: return result; } -static BOOL add_file_to_list(const char* local_name, const WCHAR* remote_name, wArrayList* files) +static BOOL add_file_to_list(wClipboard* clipboard, const char* local_name, + const WCHAR* remote_name, wArrayList* files) { struct posix_file* file = NULL; WLog_VRB(TAG, "adding file: %s", local_name); @@ -371,7 +382,7 @@ static BOOL add_file_to_list(const char* local_name, const WCHAR* remote_name, w * This is effectively a recursive call, but we do not track * recursion depth, thus filesystem loops can cause a crash. */ - if (!add_directory_contents_to_list(local_name, remote_name, files)) + if (!add_directory_contents_to_list(clipboard, local_name, remote_name, files)) return FALSE; } @@ -392,7 +403,7 @@ static const char* get_basename(const char* name) return last_name; } -static BOOL process_file_name(const char* local_name, wArrayList* files) +static BOOL process_file_name(wClipboard* clipboard, const char* local_name, wArrayList* files) { BOOL result = FALSE; const char* base_name = NULL; @@ -403,17 +414,17 @@ static BOOL process_file_name(const char* local_name, wArrayList* files) * to have names relative to that selection. */ base_name = get_basename(local_name); - remote_name = convert_local_name_component_to_remote(base_name); + remote_name = convert_local_name_component_to_remote(clipboard, base_name); if (!remote_name) return FALSE; - result = add_file_to_list(local_name, remote_name, files); + result = add_file_to_list(clipboard, local_name, remote_name, files); free(remote_name); return result; } -static BOOL process_uri(const char* uri, size_t uri_len, wArrayList* files) +static BOOL process_uri(wClipboard* clipboard, const char* uri, size_t uri_len, wArrayList* files) { const char prefix[] = "file://"; BOOL result = FALSE; @@ -432,13 +443,14 @@ static BOOL process_uri(const char* uri, size_t uri_len, wArrayList* files) if (!name) goto out; - result = process_file_name(name, files); + result = process_file_name(clipboard, name, files); out: free(name); return result; } -static BOOL process_uri_list(const char* data, size_t length, wArrayList* files) +static BOOL process_uri_list(wClipboard* clipboard, const char* data, size_t length, + wArrayList* files) { const char* cur = data; const char* lim = data + length; @@ -485,7 +497,7 @@ static BOOL process_uri_list(const char* data, size_t length, wArrayList* files) if (comment) continue; - if (!process_uri(start, stop - start, files)) + if (!process_uri(clipboard, start, stop - start, files)) return FALSE; } @@ -561,7 +573,7 @@ static void* convert_uri_list_to_filedescriptors(wClipboard* clipboard, UINT32 f if (formatId != ClipboardGetFormatId(clipboard, "text/uri-list")) return NULL; - if (!process_uri_list((const char*)data, *pSize, clipboard->localFiles)) + if (!process_uri_list(clipboard, (const char*)data, *pSize, clipboard->localFiles)) return NULL; descriptors = convert_local_file_list_to_filedescriptors(clipboard->localFiles); @@ -946,6 +958,7 @@ static void setup_delegate(wClipboardDelegate* delegate) delegate->ClientRequestFileRange = posix_file_request_range; delegate->ClipboardFileRangeSuccess = dummy_file_range_success; delegate->ClipboardFileRangeFailure = dummy_file_range_failure; + delegate->IsFileNameComponentValid = ValidFileNameComponent; } BOOL ClipboardInitPosixFileSubsystem(wClipboard* clipboard) diff --git a/winpr/libwinpr/clipboard/synthetic.c b/winpr/libwinpr/clipboard/synthetic.c index 63b5153..fcd66dc 100644 --- a/winpr/libwinpr/clipboard/synthetic.c +++ b/winpr/libwinpr/clipboard/synthetic.c @@ -321,91 +321,107 @@ static void* clipboard_synthesize_image_bmp(wClipboard* clipboard, UINT32 format */ static void* clipboard_synthesize_html_format(wClipboard* clipboard, UINT32 formatId, - const void* data, UINT32* pSize) + const void* pData, UINT32* pSize) { - char* pSrcData = NULL; + union + { + const void* cpv; + const char* cpc; + const BYTE* cpb; + WCHAR* pv; + } pSrcData; char* pDstData = NULL; - INT64 SrcSize = (INT64)*pSize; + pSrcData.cpv = NULL; + + WINPR_ASSERT(clipboard); + WINPR_ASSERT(pSize); if (formatId == ClipboardGetFormatId(clipboard, "text/html")) { + const INT64 SrcSize = (INT64)*pSize; + const size_t DstSize = SrcSize + 200; char* body; - BYTE bom[2]; - char num[20]; - WCHAR* wstr; + char num[20] = { 0 }; + + /* Create a copy, we modify the input data */ + pSrcData.pv = calloc(1, SrcSize + 1); + if (!pSrcData.pv) + goto fail; + memcpy(pSrcData.pv, pData, SrcSize); if (SrcSize > 2) { - CopyMemory(bom, data, 2); - - if ((bom[0] == 0xFE) && (bom[1] == 0xFF)) - { - ByteSwapUnicode((WCHAR*)data, SrcSize / 2); - } - - if ((bom[0] == 0xFF) && (bom[1] == 0xFE)) - { - wstr = (WCHAR*)&((BYTE*)data)[2]; - ConvertFromUnicode(CP_UTF8, 0, wstr, (SrcSize - 2) / 2, &pSrcData, 0, NULL, NULL); - } - } - - if (!pSrcData) - { - pSrcData = (char*)calloc(1, SrcSize + 1); - - if (!pSrcData) + if (SrcSize > INT_MAX) return NULL; - CopyMemory(pSrcData, data, SrcSize); + /* Check the BOM (Byte Order Mark) */ + if ((pSrcData.cpb[0] == 0xFE) && (pSrcData.cpb[1] == 0xFF)) + ByteSwapUnicode(pSrcData.pv, (int)(SrcSize / 2)); + + /* Check if we have WCHAR, convert to UTF-8 */ + if ((pSrcData.cpb[0] == 0xFF) && (pSrcData.cpb[1] == 0xFE)) + { + char* utfString = NULL; + ConvertFromUnicode(CP_UTF8, 0, &pSrcData.pv[1], (int)(SrcSize - 2) / 2, &utfString, + 0, NULL, NULL); + free(pSrcData.pv); + pSrcData.cpc = utfString; + } } - pDstData = (char*)calloc(1, SrcSize + 200); + pDstData = (char*)calloc(1, DstSize); if (!pDstData) - { - free(pSrcData); - return NULL; - } + goto fail; - sprintf_s(pDstData, SrcSize + 200, + sprintf_s(pDstData, DstSize, "Version:0.9\r\n" "StartHTML:0000000000\r\n" "EndHTML:0000000000\r\n" "StartFragment:0000000000\r\n" "EndFragment:0000000000\r\n"); - body = strstr(pSrcData, ""); + { + if (!winpr_str_append("", pDstData, DstSize, NULL)) + goto fail; + } - strcat(pDstData, ""); + if (!winpr_str_append("", pDstData, DstSize, NULL)) + goto fail; /* StartFragment */ sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, SrcSize + 200)); CopyMemory(&pDstData[69], num, 10); - strcat(pDstData, pSrcData); + if (!winpr_str_append(pSrcData.cpc, pDstData, DstSize, NULL)) + goto fail; /* EndFragment */ sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, SrcSize + 200)); CopyMemory(&pDstData[93], num, 10); - strcat(pDstData, ""); + if (!winpr_str_append("", pDstData, DstSize, NULL)) + goto fail; if (!body) - strcat(pDstData, ""); + { + if (!winpr_str_append("", pDstData, DstSize, NULL)) + goto fail; + } /* EndHTML */ - sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, SrcSize + 200)); + sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, DstSize)); CopyMemory(&pDstData[43], num, 10); - *pSize = (UINT32)strlen(pDstData) + 1; - free(pSrcData); + *pSize = (UINT32)strnlen(pDstData, DstSize) + 1; } +fail: + free(pSrcData.pv); return pDstData; } diff --git a/winpr/libwinpr/crt/string.c b/winpr/libwinpr/crt/string.c index 37fcb4b..748210e 100644 --- a/winpr/libwinpr/crt/string.c +++ b/winpr/libwinpr/crt/string.c @@ -33,6 +33,23 @@ #include "../log.h" #define TAG WINPR_TAG("crt") +BOOL winpr_str_append(const char* what, char* buffer, size_t size, const char* separator) +{ + const size_t used = strnlen(buffer, size); + const size_t add = strnlen(what, size); + const size_t sep_len = separator ? strnlen(separator, size) : 0; + const size_t sep = (used > 0) ? sep_len : 0; + + if (used + add + sep >= size) + return FALSE; + + if ((used > 0) && (sep_len > 0)) + strncat(buffer, separator, sep_len); + + strncat(buffer, what, add); + return TRUE; +} + #ifndef _WIN32 char* _strdup(const char* strSource) diff --git a/winpr/libwinpr/crypto/hash.c b/winpr/libwinpr/crypto/hash.c index 8265b2e..5e94039 100644 --- a/winpr/libwinpr/crypto/hash.c +++ b/winpr/libwinpr/crypto/hash.c @@ -151,7 +151,8 @@ WINPR_HMAC_CTX* winpr_HMAC_New(void) WINPR_HMAC_CTX* ctx = NULL; #if defined(WITH_OPENSSL) HMAC_CTX* hmac = NULL; -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) if (!(hmac = (HMAC_CTX*)calloc(1, sizeof(HMAC_CTX)))) return NULL; @@ -185,7 +186,8 @@ BOOL winpr_HMAC_Init(WINPR_HMAC_CTX* ctx, WINPR_MD_TYPE md, const BYTE* key, siz if (!evp || !hmac) return FALSE; -#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER) +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) HMAC_Init_ex(hmac, key, keylen, evp, NULL); /* no return value on OpenSSL 0.9.x */ return TRUE; #else @@ -221,7 +223,8 @@ BOOL winpr_HMAC_Update(WINPR_HMAC_CTX* ctx, const BYTE* input, size_t ilen) { #if defined(WITH_OPENSSL) HMAC_CTX* hmac = (HMAC_CTX*)ctx; -#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER) +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) HMAC_Update(hmac, input, ilen); /* no return value on OpenSSL 0.9.x */ return TRUE; #else @@ -253,7 +256,8 @@ BOOL winpr_HMAC_Final(WINPR_HMAC_CTX* ctx, BYTE* output, size_t olen) #if defined(WITH_OPENSSL) hmac = (HMAC_CTX*)ctx; -#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER) +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) HMAC_Final(hmac, output, NULL); /* no return value on OpenSSL 0.9.x */ return TRUE; #else @@ -279,7 +283,8 @@ void winpr_HMAC_Free(WINPR_HMAC_CTX* ctx) if (hmac) { -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) HMAC_CTX_cleanup(hmac); free(hmac); #else @@ -332,7 +337,8 @@ WINPR_DIGEST_CTX* winpr_Digest_New(void) WINPR_DIGEST_CTX* ctx = NULL; #if defined(WITH_OPENSSL) EVP_MD_CTX* mdctx; -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) mdctx = EVP_MD_CTX_create(); #else mdctx = EVP_MD_CTX_new(); @@ -464,7 +470,8 @@ void winpr_Digest_Free(WINPR_DIGEST_CTX* ctx) if (mdctx) { -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) EVP_MD_CTX_destroy(mdctx); #else EVP_MD_CTX_free(mdctx); diff --git a/winpr/libwinpr/file/generic.c b/winpr/libwinpr/file/generic.c index aa53f68..9668bb5 100644 --- a/winpr/libwinpr/file/generic.c +++ b/winpr/libwinpr/file/generic.c @@ -24,6 +24,7 @@ #endif #include +#include #include #include @@ -541,15 +542,7 @@ DWORD WINAPI GetFileAttributesW(LPCWSTR lpFileName) static char* append(char* buffer, size_t size, const char* append) { - const size_t len = strnlen(buffer, size); - if (len == 0) - _snprintf(buffer, size, "%s", append); - else - { - strcat(buffer, "|"); - strcat(buffer, append); - } - + winpr_str_append(append, buffer, size, "|"); return buffer; } @@ -588,7 +581,7 @@ static const char* flagsToStr(char* buffer, size_t size, DWORD flags) append(buffer, size, "FILE_ATTRIBUTE_VIRTUAL"); _snprintf(strflags, sizeof(strflags), " [0x%08" PRIx32 "]", flags); - strcat(buffer, strflags); + winpr_str_append(strflags, buffer, size, NULL); return buffer; } diff --git a/winpr/libwinpr/path/shell_ios.m b/winpr/libwinpr/path/shell_ios.m index 7a6f78b..b2b9e31 100644 --- a/winpr/libwinpr/path/shell_ios.m +++ b/winpr/libwinpr/path/shell_ios.m @@ -26,7 +26,7 @@ #include "shell_ios.h" -NSString *ios_get_directory_for_search_path(NSString *searchPath) +NSString *ios_get_directory_for_search_path(NSSearchPathDirectory searchPath) { return [NSSearchPathForDirectoriesInDomains(searchPath, NSUserDomainMask, YES) lastObject]; } diff --git a/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c b/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c index 6c8d3be..ba2fce0 100644 --- a/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c +++ b/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c @@ -19,7 +19,7 @@ static BYTE CLIENT_MESSAGE[PIPE_BUFFER_SIZE]; static BOOL bClientSuccess = FALSE; static BOOL bServerSuccess = FALSE; -static HANDLE serverReadyEvent; +static HANDLE serverReadyEvent = NULL; static LPTSTR lpszPipeName = _T("\\\\.\\pipe\\winpr_test_pipe_overlapped"); @@ -30,11 +30,13 @@ static DWORD WINAPI named_pipe_client_thread(LPVOID arg) HANDLE hNamedPipe = NULL; BYTE* lpReadBuffer = NULL; BOOL fSuccess = FALSE; - OVERLAPPED overlapped; + OVERLAPPED overlapped = { 0 }; DWORD nNumberOfBytesToRead; DWORD nNumberOfBytesToWrite; DWORD NumberOfBytesTransferred; + WINPR_UNUSED(arg); + status = WaitForSingleObject(serverReadyEvent, PIPE_TIMEOUT_MS); if (status != WAIT_OBJECT_0) { @@ -43,8 +45,6 @@ static DWORD WINAPI named_pipe_client_thread(LPVOID arg) } /* 1: initialize overlapped structure */ - - ZeroMemory(&overlapped, sizeof(OVERLAPPED)); if (!(hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) { printf("client: CreateEvent failure: %" PRIu32 "\n", GetLastError()); @@ -159,16 +159,16 @@ static DWORD WINAPI named_pipe_server_thread(LPVOID arg) HANDLE hEvent = NULL; HANDLE hNamedPipe = NULL; BYTE* lpReadBuffer = NULL; - OVERLAPPED overlapped; + OVERLAPPED overlapped = { 0 }; BOOL fSuccess = FALSE; BOOL fConnected = FALSE; DWORD nNumberOfBytesToRead; DWORD nNumberOfBytesToWrite; DWORD NumberOfBytesTransferred; - /* 1: initialize overlapped structure */ + WINPR_UNUSED(arg); - ZeroMemory(&overlapped, sizeof(OVERLAPPED)); + /* 1: initialize overlapped structure */ if (!(hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) { printf("server: CreateEvent failure: %" PRIu32 "\n", GetLastError()); @@ -332,8 +332,8 @@ finish: int TestPipeCreateNamedPipeOverlapped(int argc, char* argv[]) { - HANDLE ClientThread; - HANDLE ServerThread; + HANDLE ClientThread = NULL; + HANDLE ServerThread = NULL; int result = -1; FillMemory(SERVER_MESSAGE, PIPE_BUFFER_SIZE, 0xAA); @@ -349,8 +349,7 @@ int TestPipeCreateNamedPipeOverlapped(int argc, char* argv[]) printf("CreateThread (client) failed: %" PRIu32 "\n", GetLastError()); goto out; } - if (!(ServerThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)named_pipe_server_thread, - NULL, 0, NULL))) + if (!(ServerThread = CreateThread(NULL, 0, named_pipe_server_thread, NULL, 0, NULL))) { printf("CreateThread (server) failed: %" PRIu32 "\n", GetLastError()); goto out; @@ -372,6 +371,13 @@ int TestPipeCreateNamedPipeOverlapped(int argc, char* argv[]) out: + if (ClientThread) + CloseHandle(ClientThread); + if (ServerThread) + CloseHandle(ServerThread); + if (serverReadyEvent) + CloseHandle(serverReadyEvent); + #ifndef _WIN32 if (result == 0) { diff --git a/winpr/libwinpr/smartcard/smartcard.c b/winpr/libwinpr/smartcard/smartcard.c index 22d6b03..30e472c 100644 --- a/winpr/libwinpr/smartcard/smartcard.c +++ b/winpr/libwinpr/smartcard/smartcard.c @@ -1047,103 +1047,47 @@ WINSCARDAPI const char* WINAPI SCardGetCardStateString(DWORD dwCardState) WINSCARDAPI char* WINAPI SCardGetReaderStateString(DWORD dwReaderState) { - char* szReaderState = malloc(512); + const size_t size = 512; + char* buffer = calloc(size, sizeof(char)); - if (!szReaderState) + if (!buffer) return NULL; - szReaderState[0] = '\0'; - if (dwReaderState & SCARD_STATE_IGNORE) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_IGNORE"); - } + winpr_str_append("SCARD_STATE_IGNORE", buffer, size, "|"); if (dwReaderState & SCARD_STATE_CHANGED) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_CHANGED"); - } + winpr_str_append("SCARD_STATE_CHANGED", buffer, size, "|"); if (dwReaderState & SCARD_STATE_UNKNOWN) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_UNKNOWN"); - } + winpr_str_append("SCARD_STATE_UNKNOWN", buffer, size, "|"); if (dwReaderState & SCARD_STATE_UNAVAILABLE) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_UNAVAILABLE"); - } + winpr_str_append("SCARD_STATE_UNAVAILABLE", buffer, size, "|"); if (dwReaderState & SCARD_STATE_EMPTY) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_EMPTY"); - } + winpr_str_append("SCARD_STATE_EMPTY", buffer, size, "|"); if (dwReaderState & SCARD_STATE_PRESENT) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_PRESENT"); - } + winpr_str_append("SCARD_STATE_PRESENT", buffer, size, "|"); if (dwReaderState & SCARD_STATE_ATRMATCH) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_ATRMATCH"); - } + winpr_str_append("SCARD_STATE_ATRMATCH", buffer, size, "|"); if (dwReaderState & SCARD_STATE_EXCLUSIVE) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_EXCLUSIVE"); - } + winpr_str_append("SCARD_STATE_EXCLUSIVE", buffer, size, "|"); if (dwReaderState & SCARD_STATE_INUSE) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_INUSE"); - } + winpr_str_append("SCARD_STATE_INUSE", buffer, size, "|"); if (dwReaderState & SCARD_STATE_MUTE) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_MUTE"); - } + winpr_str_append("SCARD_STATE_MUTE", buffer, size, "|"); if (dwReaderState & SCARD_STATE_UNPOWERED) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); + winpr_str_append("SCARD_STATE_UNPOWERED", buffer, size, "|"); - strcat(szReaderState, "SCARD_STATE_UNPOWERED"); - } + if (!buffer[0]) + winpr_str_append("SCARD_STATE_UNAWARE", buffer, size, "|"); - if (!szReaderState[0]) - strcat(szReaderState, "SCARD_STATE_UNAWARE"); - - return szReaderState; + return buffer; } diff --git a/winpr/libwinpr/sspi/NTLM/ntlm.c b/winpr/libwinpr/sspi/NTLM/ntlm.c index 372e6fb..0e12a34 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm.c +++ b/winpr/libwinpr/sspi/NTLM/ntlm.c @@ -1309,15 +1309,15 @@ char* ntlm_negotiate_flags_string(char* buffer, size_t size, UINT32 flags) { if (size - len < 1) break; - strcat(buffer, "|"); - len++; - } + winpr_str_append("|", buffer, size, NULL); + len++; + } if (size - len < flen) break; - strcat(buffer, str); - } - } + winpr_str_append(str, buffer, size, NULL); + } + } return buffer; } diff --git a/winpr/libwinpr/sspicli/sspicli.c b/winpr/libwinpr/sspicli/sspicli.c index 1d491b8..75585dd 100644 --- a/winpr/libwinpr/sspicli/sspicli.c +++ b/winpr/libwinpr/sspicli/sspicli.c @@ -230,7 +230,12 @@ BOOL GetUserNameExA(EXTENDED_NAME_FORMAT NameFormat, LPSTR lpNameBuffer, PULONG if (getlogin_r(lpNameBuffer, *nSize) != 0) return FALSE; #else - strncpy(lpNameBuffer, getlogin(), *nSize); + { + const char* name = getlogin(); + if (!name) + return FALSE; + strncpy(lpNameBuffer, name, strnlen(name, *nSize)); + } #endif *nSize = strnlen(lpNameBuffer, *nSize); return TRUE; diff --git a/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.c b/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.c index a5a0bf1..d8d59d0 100644 --- a/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.c +++ b/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.c @@ -69,6 +69,7 @@ #include #include #include +#include #include #include #include diff --git a/winpr/libwinpr/thread/thread.c b/winpr/libwinpr/thread/thread.c index e671b64..3b95d1d 100644 --- a/winpr/libwinpr/thread/thread.c +++ b/winpr/libwinpr/thread/thread.c @@ -24,7 +24,7 @@ #include "config.h" #endif -#include +#include #include @@ -98,7 +98,10 @@ #define TAG WINPR_TAG("thread") static WINPR_THREAD mainThread; + +#if defined(WITH_THREAD_LIST) static wListDictionary* thread_list = NULL; +#endif static BOOL ThreadCloseHandle(HANDLE handle); static void cleanup_handle(void* obj); @@ -126,14 +129,190 @@ static int ThreadGetFd(HANDLE handle) return pThread->event.fds[0]; } +#define run_mutex_init(fkt, mux, arg) run_mutex_init_(fkt, #fkt, mux, arg) +static BOOL run_mutex_init_(int (*fkt)(pthread_mutex_t*, const pthread_mutexattr_t*), + const char* name, pthread_mutex_t* mutex, + const pthread_mutexattr_t* mutexattr) +{ + int rc; + + WINPR_ASSERT(fkt); + WINPR_ASSERT(mutex); + + rc = fkt(mutex, mutexattr); + if (rc != 0) + { + WLog_WARN(TAG, "[%s] failed with [%s]", name, strerror(rc)); + } + return rc == 0; +} + +#define run_mutex_fkt(fkt, mux) run_mutex_fkt_(fkt, #fkt, mux) +static BOOL run_mutex_fkt_(int (*fkt)(pthread_mutex_t* mux), const char* name, + pthread_mutex_t* mutex) +{ + int rc; + + WINPR_ASSERT(fkt); + WINPR_ASSERT(mutex); + + rc = fkt(mutex); + if (rc != 0) + { + WLog_WARN(TAG, "[%s] failed with [%s]", name, strerror(rc)); + } + return rc == 0; +} + +#define run_cond_init(fkt, cond, arg) run_cond_init_(fkt, #fkt, cond, arg) +static BOOL run_cond_init_(int (*fkt)(pthread_cond_t*, const pthread_condattr_t*), const char* name, + pthread_cond_t* condition, const pthread_condattr_t* conditionattr) +{ + int rc; + + WINPR_ASSERT(fkt); + WINPR_ASSERT(condition); + + rc = fkt(condition, conditionattr); + if (rc != 0) + { + WLog_WARN(TAG, "[%s] failed with [%s]", name, strerror(rc)); + } + return rc == 0; +} + +#define run_cond_fkt(fkt, cond) run_cond_fkt_(fkt, #fkt, cond) +static BOOL run_cond_fkt_(int (*fkt)(pthread_cond_t* mux), const char* name, + pthread_cond_t* condition) +{ + int rc; + + WINPR_ASSERT(fkt); + WINPR_ASSERT(condition); + + rc = fkt(condition); + if (rc != 0) + { + WLog_WARN(TAG, "[%s] failed with [%s]", name, strerror(rc)); + } + return rc == 0; +} + +static int pthread_mutex_checked_unlock(pthread_mutex_t* mutex) +{ + WINPR_ASSERT(mutex); + WINPR_ASSERT(pthread_mutex_trylock(mutex) == EBUSY); + return pthread_mutex_unlock(mutex); +} + +static BOOL mux_condition_bundle_init(mux_condition_bundle* bundle) +{ + WINPR_ASSERT(bundle); + + bundle->val = FALSE; + if (!run_mutex_init(pthread_mutex_init, &bundle->mux, NULL)) + return FALSE; + + if (!run_cond_init(pthread_cond_init, &bundle->cond, NULL)) + return FALSE; + return TRUE; +} + +static void mux_condition_bundle_uninit(mux_condition_bundle* bundle) +{ + mux_condition_bundle empty = { 0 }; + + WINPR_ASSERT(bundle); + + run_cond_fkt(pthread_cond_destroy, &bundle->cond); + run_mutex_fkt(pthread_mutex_destroy, &bundle->mux); + *bundle = empty; +} + +static BOOL mux_condition_bundle_signal(mux_condition_bundle* bundle) +{ + BOOL rc = TRUE; + WINPR_ASSERT(bundle); + + if (!run_mutex_fkt(pthread_mutex_lock, &bundle->mux)) + return FALSE; + bundle->val = TRUE; + if (!run_cond_fkt(pthread_cond_signal, &bundle->cond)) + rc = FALSE; + if (!run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux)) + rc = FALSE; + return rc; +} + +static BOOL mux_condition_bundle_lock(mux_condition_bundle* bundle) +{ + WINPR_ASSERT(bundle); + return run_mutex_fkt(pthread_mutex_lock, &bundle->mux); +} + +static BOOL mux_condition_bundle_unlock(mux_condition_bundle* bundle) +{ + WINPR_ASSERT(bundle); + return run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux); +} + +static BOOL mux_condition_bundle_wait(mux_condition_bundle* bundle, const char* name) +{ + BOOL rc = FALSE; + + WINPR_ASSERT(bundle); + WINPR_ASSERT(name); + WINPR_ASSERT(pthread_mutex_trylock(&bundle->mux) == EBUSY); + + while (!bundle->val) + { + int r = pthread_cond_wait(&bundle->cond, &bundle->mux); + if (r != 0) + { + WLog_ERR(TAG, "failed to wait for %s [%s]", name, strerror(r)); + switch (r) + { + case ENOTRECOVERABLE: + case EPERM: + case ETIMEDOUT: + case EINVAL: + goto fail; + + default: + break; + } + } + } + + rc = bundle->val; + +fail: + return rc; +} + +static BOOL signal_thread_ready(WINPR_THREAD* thread) +{ + WINPR_ASSERT(thread); + + return mux_condition_bundle_signal(&thread->isCreated); +} + +static BOOL signal_thread_is_running(WINPR_THREAD* thread) +{ + WINPR_ASSERT(thread); + + return mux_condition_bundle_signal(&thread->isRunning); +} + static DWORD ThreadCleanupHandle(HANDLE handle) { + DWORD status = WAIT_FAILED; WINPR_THREAD* thread = (WINPR_THREAD*)handle; if (!ThreadIsHandled(handle)) return WAIT_FAILED; - if (pthread_mutex_lock(&thread->mutex)) + if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex)) return WAIT_FAILED; if (!thread->joined) @@ -144,17 +323,19 @@ static DWORD ThreadCleanupHandle(HANDLE handle) if (status != 0) { WLog_ERR(TAG, "pthread_join failure: [%d] %s", status, strerror(status)); - pthread_mutex_unlock(&thread->mutex); - return WAIT_FAILED; + goto fail; } else thread->joined = TRUE; } - if (pthread_mutex_unlock(&thread->mutex)) + status = WAIT_OBJECT_0; + +fail: + if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex)) return WAIT_FAILED; - return WAIT_OBJECT_0; + return status; } static HANDLE_OPS ops = { ThreadIsHandled, @@ -218,7 +399,8 @@ static void dump_thread(WINPR_THREAD* thread) free(msg); } - +#else + WINPR_UNUSED(thread); #endif } @@ -236,6 +418,7 @@ static BOOL reset_event(WINPR_THREAD* thread) return winpr_event_reset(&thread->event); } +#if defined(WITH_THREAD_LIST) static BOOL thread_compare(const void* a, const void* b) { const pthread_t* p1 = a; @@ -243,12 +426,13 @@ static BOOL thread_compare(const void* a, const void* b) BOOL rc = pthread_equal(*p1, *p2); return rc; } +#endif static INIT_ONCE threads_InitOnce = INIT_ONCE_STATIC_INIT; static pthread_t mainThreadId; static DWORD currentThreadTlsIndex = TLS_OUT_OF_INDEXES; -BOOL initializeThreads(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context) +static BOOL initializeThreads(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context) { if (!apc_init(&mainThread.apc)) { @@ -265,10 +449,54 @@ BOOL initializeThreads(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context) WLog_ERR(TAG, "Major bug, unable to allocate a TLS value for currentThread"); } +#if defined(WITH_THREAD_LIST) + thread_list = ListDictionary_New(TRUE); + + if (!thread_list) + { + WLog_ERR(TAG, "Couldn't create global thread list"); + goto error_thread_list; + } + + thread_list->objectKey.fnObjectEquals = thread_compare; +#endif + out: return TRUE; } +static BOOL signal_and_wait_for_ready(WINPR_THREAD* thread) +{ + BOOL res = FALSE; + + WINPR_ASSERT(thread); + + if (!mux_condition_bundle_lock(&thread->isRunning)) + return FALSE; + + if (!signal_thread_ready(thread)) + goto fail; + + if (!mux_condition_bundle_wait(&thread->isRunning, "threadIsRunning")) + goto fail; + +#if defined(WITH_THREAD_LIST) + if (!ListDictionary_Contains(thread_list, &thread->thread)) + { + WLog_ERR(TAG, "Thread not in thread_list, startup failed!"); + goto fail; + } +#endif + + res = TRUE; + +fail: + if (!mux_condition_bundle_unlock(&thread->isRunning)) + return FALSE; + + return res; +} + /* Thread launcher function responsible for registering * cleanup handlers and calling pthread_exit, if not done * in thread function. */ @@ -296,23 +524,9 @@ static void* thread_launcher(void* arg) goto exit; } - if (pthread_mutex_lock(&thread->threadIsReadyMutex)) + if (!signal_and_wait_for_ready(thread)) goto exit; - if (!ListDictionary_Contains(thread_list, &thread->thread)) - { - if (pthread_cond_wait(&thread->threadIsReady, &thread->threadIsReadyMutex) != 0) - { - WLog_ERR(TAG, "The thread could not be made ready"); - pthread_mutex_unlock(&thread->threadIsReadyMutex); - goto exit; - } - } - - if (pthread_mutex_unlock(&thread->threadIsReadyMutex)) - goto exit; - - assert(ListDictionary_Contains(thread_list, &thread->thread)); rc = fkt(thread->lpParameter); exit: @@ -325,6 +539,8 @@ exit: set_event(thread); + signal_thread_ready(thread); + if (thread->detached || !thread->started) cleanup_handle(thread); } @@ -334,7 +550,14 @@ exit: static BOOL winpr_StartThread(WINPR_THREAD* thread) { - pthread_attr_t attr; + BOOL rc = FALSE; + BOOL locked = FALSE; + pthread_attr_t attr = { 0 }; + + if (!mux_condition_bundle_lock(&thread->isCreated)) + return FALSE; + locked = TRUE; + pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); @@ -344,35 +567,44 @@ static BOOL winpr_StartThread(WINPR_THREAD* thread) thread->started = TRUE; reset_event(thread); - if (pthread_create(&thread->thread, &attr, thread_launcher, thread)) - goto error; - - if (pthread_mutex_lock(&thread->threadIsReadyMutex)) - goto error; - +#if defined(WITH_THREAD_LIST) if (!ListDictionary_Add(thread_list, &thread->thread, thread)) { WLog_ERR(TAG, "failed to add the thread to the thread list"); - pthread_mutex_unlock(&thread->threadIsReadyMutex); goto error; } +#endif - if (pthread_cond_signal(&thread->threadIsReady) != 0) + if (pthread_create(&thread->thread, &attr, thread_launcher, thread)) + goto error; + + if (!mux_condition_bundle_wait(&thread->isCreated, "threadIsCreated")) + goto error; + + locked = FALSE; + if (!mux_condition_bundle_unlock(&thread->isCreated)) + goto error; + + if (!signal_thread_is_running(thread)) { WLog_ERR(TAG, "failed to signal the thread was ready"); - pthread_mutex_unlock(&thread->threadIsReadyMutex); goto error; } - if (pthread_mutex_unlock(&thread->threadIsReadyMutex)) - goto error; + rc = TRUE; +error: + if (locked) + { + if (!mux_condition_bundle_unlock(&thread->isCreated)) + rc = FALSE; + } pthread_attr_destroy(&attr); - dump_thread(thread); - return TRUE; -error: - pthread_attr_destroy(&attr); - return FALSE; + + if (rc) + dump_thread(thread); + + return rc; } HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, @@ -380,8 +612,7 @@ HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize DWORD dwCreationFlags, LPDWORD lpThreadId) { HANDLE handle; - WINPR_THREAD* thread; - thread = (WINPR_THREAD*)calloc(1, sizeof(WINPR_THREAD)); + WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1, sizeof(WINPR_THREAD)); if (!thread) return NULL; @@ -399,104 +630,66 @@ HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize if (!winpr_event_init(&thread->event)) { WLog_ERR(TAG, "failed to create event"); - goto error_event; + goto fail; } - if (pthread_mutex_init(&thread->mutex, NULL) != 0) + if (!run_mutex_init(pthread_mutex_init, &thread->mutex, NULL)) { WLog_ERR(TAG, "failed to initialize thread mutex"); - goto error_mutex; + goto fail; } if (!apc_init(&thread->apc)) { WLog_ERR(TAG, "failed to initialize APC"); - goto error_APC; + goto fail; } - if (pthread_mutex_init(&thread->threadIsReadyMutex, NULL) != 0) - { - WLog_ERR(TAG, "failed to initialize a mutex for a condition variable"); - goto error_thread_ready_mutex; - } - - if (pthread_cond_init(&thread->threadIsReady, NULL) != 0) - { - WLog_ERR(TAG, "failed to initialize a condition variable"); - goto error_thread_ready; - } + if (!mux_condition_bundle_init(&thread->isCreated)) + goto fail; + if (!mux_condition_bundle_init(&thread->isRunning)) + goto fail; WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ); handle = (HANDLE)thread; - if (!thread_list) - { - InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL); - thread_list = ListDictionary_New(TRUE); - - if (!thread_list) - { - WLog_ERR(TAG, "Couldn't create global thread list"); - goto error_thread_list; - } - - thread_list->objectKey.fnObjectEquals = thread_compare; - } + InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL); if (!(dwCreationFlags & CREATE_SUSPENDED)) { if (!winpr_StartThread(thread)) - goto error_thread_list; + goto fail; } else { if (!set_event(thread)) - goto error_thread_list; + goto fail; } return handle; -error_thread_list: - pthread_cond_destroy(&thread->threadIsReady); -error_thread_ready: - pthread_mutex_destroy(&thread->threadIsReadyMutex); -error_thread_ready_mutex: - apc_uninit(&thread->apc); -error_APC: - pthread_mutex_destroy(&thread->mutex); -error_mutex: - winpr_event_uninit(&thread->event); -error_event: - free(thread); +fail: + cleanup_handle(thread); return NULL; } void cleanup_handle(void* obj) { - int rc; WINPR_THREAD* thread = (WINPR_THREAD*)obj; + if (!thread) + return; if (!apc_uninit(&thread->apc)) WLog_ERR(TAG, "failed to destroy APC"); - rc = pthread_cond_destroy(&thread->threadIsReady); - if (rc) - WLog_ERR(TAG, "failed to destroy thread->threadIsReady [%d] %s (%d)", rc, strerror(errno), - errno); - - rc = pthread_mutex_destroy(&thread->threadIsReadyMutex); - if (rc) - WLog_ERR(TAG, "failed to destroy thread->threadIsReadyMutex [%d] %s (%d)", rc, - strerror(errno), errno); - - rc = pthread_mutex_destroy(&thread->mutex); - if (rc) - WLog_ERR(TAG, "failed to destroy thread->mutex [%d] %s (%d)", rc, strerror(errno), errno); + mux_condition_bundle_uninit(&thread->isCreated); + mux_condition_bundle_uninit(&thread->isRunning); + run_mutex_fkt(pthread_mutex_destroy, &thread->mutex); winpr_event_uninit(&thread->event); - if (thread_list && ListDictionary_Contains(thread_list, &thread->thread)) - ListDictionary_Remove(thread_list, &thread->thread); - +#if defined(WITH_THREAD_LIST) + ListDictionary_Remove(thread_list, &thread->thread); +#endif #if defined(WITH_DEBUG_THREADS) if (thread->create_stack) @@ -513,6 +706,7 @@ BOOL ThreadCloseHandle(HANDLE handle) { WINPR_THREAD* thread = (WINPR_THREAD*)handle; +#if defined(WITH_THREAD_LIST) if (!thread_list) { WLog_ERR(TAG, "Thread list does not exist, check call!"); @@ -526,6 +720,7 @@ BOOL ThreadCloseHandle(HANDLE handle) else { ListDictionary_Lock(thread_list); +#endif dump_thread(thread); if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0)) @@ -539,14 +734,10 @@ BOOL ThreadCloseHandle(HANDLE handle) cleanup_handle(thread); } +#if defined(WITH_THREAD_LIST) ListDictionary_Unlock(thread_list); - - if (ListDictionary_Count(thread_list) < 1) - { - ListDictionary_Free(thread_list); - thread_list = NULL; - } } +#endif return TRUE; } @@ -562,6 +753,7 @@ HANDLE CreateRemoteThread(HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttribu VOID ExitThread(DWORD dwExitCode) { +#if defined(WITH_THREAD_LIST) DWORD rc; pthread_t tid = pthread_self(); @@ -586,7 +778,7 @@ VOID ExitThread(DWORD dwExitCode) WINPR_THREAD* thread; ListDictionary_Lock(thread_list); thread = ListDictionary_GetItemValue(thread_list, &tid); - assert(thread); + WINPR_ASSERT(thread); thread->exited = TRUE; thread->dwExitCode = dwExitCode; #if defined(WITH_DEBUG_THREADS) @@ -601,6 +793,9 @@ VOID ExitThread(DWORD dwExitCode) pthread_exit((void*)(size_t)rc); } +#else + WINPR_UNUSED(dwExitCode); +#endif } BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode) @@ -657,7 +852,7 @@ typedef struct ULONG_PTR completionArg; } UserApcItem; -void userAPC(LPVOID arg) +static void userAPC(LPVOID arg) { UserApcItem* userApc = (UserApcItem*)arg; @@ -670,7 +865,6 @@ DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData) { ULONG Type; WINPR_HANDLE* Object; - WINPR_THREAD* thread; WINPR_APC_ITEM* apc; UserApcItem* apcItem; @@ -683,7 +877,6 @@ DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData) SetLastError(ERROR_INVALID_PARAMETER); return (DWORD)0; } - thread = (WINPR_THREAD*)Object; apcItem = calloc(1, sizeof(*apcItem)); if (!apcItem) @@ -715,21 +908,21 @@ DWORD ResumeThread(HANDLE hThread) thread = (WINPR_THREAD*)Object; - if (pthread_mutex_lock(&thread->mutex)) + if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex)) return (DWORD)-1; if (!thread->started) { if (!winpr_StartThread(thread)) { - pthread_mutex_unlock(&thread->mutex); + run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex); return (DWORD)-1; } } else WLog_WARN(TAG, "Thread already started!"); - if (pthread_mutex_unlock(&thread->mutex)) + if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex)) return (DWORD)-1; return 0; @@ -767,7 +960,7 @@ BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode) thread->exited = TRUE; thread->dwExitCode = dwExitCode; - if (pthread_mutex_lock(&thread->mutex)) + if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex)) return FALSE; #ifndef ANDROID @@ -776,16 +969,16 @@ BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode) WLog_ERR(TAG, "Function not supported on this platform!"); #endif - if (pthread_mutex_unlock(&thread->mutex)) + if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex)) return FALSE; set_event(thread); return TRUE; } -#if defined(WITH_DEBUG_THREADS) VOID DumpThreadHandles(void) { +#if defined(WITH_DEBUG_THREADS) char** msg; size_t used, i; void* stack = winpr_backtrace(20); @@ -801,6 +994,7 @@ VOID DumpThreadHandles(void) winpr_backtrace_free(stack); WLog_DBG(TAG, "---------------- Start Dumping thread handles -----------"); +#if defined(WITH_THREAD_LIST) if (!thread_list) { WLog_DBG(TAG, "All threads properly shut down and disposed of."); @@ -844,8 +1038,9 @@ VOID DumpThreadHandles(void) free(keys); ListDictionary_Unlock(thread_list); } +#endif WLog_DBG(TAG, "---------------- End Dumping thread handles -------------"); +#endif } #endif -#endif diff --git a/winpr/libwinpr/thread/thread.h b/winpr/libwinpr/thread/thread.h index f187e26..a533685 100644 --- a/winpr/libwinpr/thread/thread.h +++ b/winpr/libwinpr/thread/thread.h @@ -32,47 +32,63 @@ #include "../synch/event.h" #include "apc.h" +#ifdef __GNUC__ +#define ALIGN64 __attribute__((aligned(8))) +#else +#ifdef _WIN32 +#define ALIGN64 __declspec(align(8)) +#else +#define ALIGN64 +#endif +#endif + typedef void* (*pthread_start_routine)(void*); typedef struct winpr_APC_item WINPR_APC_ITEM; +typedef struct +{ + ALIGN64 pthread_mutex_t mux; + ALIGN64 pthread_cond_t cond; + ALIGN64 BOOL val; +} mux_condition_bundle; + struct winpr_thread { WINPR_HANDLE_DEF(); - BOOL started; - WINPR_EVENT_IMPL event; - BOOL mainProcess; - BOOL detached; - BOOL joined; - BOOL exited; - DWORD dwExitCode; - pthread_t thread; - SIZE_T dwStackSize; - LPVOID lpParameter; - pthread_mutex_t mutex; - pthread_mutex_t threadIsReadyMutex; - pthread_cond_t threadIsReady; - LPTHREAD_START_ROUTINE lpStartAddress; - LPSECURITY_ATTRIBUTES lpThreadAttributes; - APC_QUEUE apc; + ALIGN64 BOOL started; + ALIGN64 WINPR_EVENT_IMPL event; + ALIGN64 BOOL mainProcess; + ALIGN64 BOOL detached; + ALIGN64 BOOL joined; + ALIGN64 BOOL exited; + ALIGN64 DWORD dwExitCode; + ALIGN64 pthread_t thread; + ALIGN64 SIZE_T dwStackSize; + ALIGN64 LPVOID lpParameter; + ALIGN64 pthread_mutex_t mutex; + mux_condition_bundle isRunning; + mux_condition_bundle isCreated; + ALIGN64 LPTHREAD_START_ROUTINE lpStartAddress; + ALIGN64 LPSECURITY_ATTRIBUTES lpThreadAttributes; + ALIGN64 APC_QUEUE apc; #if defined(WITH_DEBUG_THREADS) - void* create_stack; - void* exit_stack; + ALIGN64 void* create_stack; + ALIGN64 void* exit_stack; #endif }; typedef struct winpr_thread WINPR_THREAD; WINPR_THREAD* winpr_GetCurrentThread(VOID); -struct winpr_process +typedef struct { WINPR_HANDLE_DEF(); pid_t pid; int status; DWORD dwExitCode; -}; -typedef struct winpr_process WINPR_PROCESS; +} WINPR_PROCESS; #endif diff --git a/winpr/libwinpr/utils/CMakeLists.txt b/winpr/libwinpr/utils/CMakeLists.txt index 2027a60..b1acf9a 100644 --- a/winpr/libwinpr/utils/CMakeLists.txt +++ b/winpr/libwinpr/utils/CMakeLists.txt @@ -17,7 +17,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) -set(${MODULE_PREFIX}_COLLECTIONS_SRCS +set(COLLECTIONS_SRCS collections/Queue.c collections/Stack.c collections/PubSub.c @@ -35,11 +35,11 @@ set(${MODULE_PREFIX}_COLLECTIONS_SRCS collections/MessageQueue.c collections/MessagePipe.c) -set(${MODULE_PREFIX}_LODEPNG_SRCS +set(LODEPNG_SRCS lodepng/lodepng.c lodepng/lodepng.h) -set(${MODULE_PREFIX}_TRIO_SRCS +set(TRIO_SRCS trio/strio.h trio/trio.c trio/trio.h @@ -67,7 +67,7 @@ if (LIBSYSTEMD_FOUND) winpr_library_add_private(${LIBSYSTEMD_LIBRARY}) endif() -set(${MODULE_PREFIX}_WLOG_SRCS +set(WLOG_SRCS wlog/wlog.c wlog/wlog.h wlog/Layout.c @@ -97,7 +97,7 @@ set(${MODULE_PREFIX}_WLOG_SRCS ) -set(${MODULE_PREFIX}_SRCS +set(SRCS ini.c sam.c ntlm.c @@ -112,13 +112,45 @@ set(${MODULE_PREFIX}_SRCS if (ANDROID) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + if (NOT HAVE_UNWIND_H) + message("[backtrace] android NDK without unwind.h, falling back to corkscrew") + set(HAVE_CORKSCREW 1) + endif() endif() -winpr_module_add(${${MODULE_PREFIX}_SRCS} - ${${MODULE_PREFIX}_COLLECTIONS_SRCS} - ${${MODULE_PREFIX}_LODEPNG_SRCS} - ${${MODULE_PREFIX}_TRIO_SRCS} - ${${MODULE_PREFIX}_WLOG_SRCS}) +if (HAVE_CORKSCREW) + list(APPEND SRCS + corkscrew/debug.c + corkscrew/debug.h) +endif() + +if (WIN32) + list(APPEND SRCS + windows/debug.c + windows/debug.h) +endif() + +if (HAVE_EXECINFO_H) + list(APPEND SRCS + execinfo/debug.c + execinfo/debug.h) +endif() + +if (HAVE_UNWIND_H) + option(USE_UNWIND "Use unwind.h to generate backtraces" ON) + if (USE_UNWIND) + add_definitions(-DUSE_UNWIND) + list(APPEND SRCS + unwind/debug.c + unwind/debug.h) + endif() +endif() + +winpr_module_add(${SRCS} + ${COLLECTIONS_SRCS} + ${LODEPNG_SRCS} + ${TRIO_SRCS} + ${WLOG_SRCS}) winpr_include_directory_add( "lodepng" diff --git a/winpr/libwinpr/utils/corkscrew/debug.c b/winpr/libwinpr/utils/corkscrew/debug.c new file mode 100644 index 0000000..0034fc9 --- /dev/null +++ b/winpr/libwinpr/utils/corkscrew/debug.c @@ -0,0 +1,262 @@ +/** + * WinPR: Windows Portable Runtime + * Debugging Utils + * + * Copyright 2014 Armin Novak + * Copyright 2014 Thincast Technologies GmbH + * + * 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. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include "debug.h" + +#define TAG "com.winpr.utils.debug" +#define LOGT(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_TRACE, __VA_ARGS__); \ + } while (0) +#define LOGD(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_DEBUG, __VA_ARGS__); \ + } while (0) +#define LOGI(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_INFO, __VA_ARGS__); \ + } while (0) +#define LOGW(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_WARN, __VA_ARGS__); \ + } while (0) +#define LOGE(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_ERROR, __VA_ARGS__); \ + } while (0) +#define LOGF(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_FATAL, __VA_ARGS__); \ + } while (0) + +static const char* support_msg = "Invalid stacktrace buffer! check if platform is supported!"; + +typedef struct +{ + backtrace_frame_t* buffer; + size_t max; + size_t used; +} t_corkscrew_data; + +typedef struct +{ + void* hdl; + ssize_t (*unwind_backtrace)(backtrace_frame_t* backtrace, size_t ignore_depth, + size_t max_depth); + ssize_t (*unwind_backtrace_thread)(pid_t tid, backtrace_frame_t* backtrace, size_t ignore_depth, + size_t max_depth); + ssize_t (*unwind_backtrace_ptrace)(pid_t tid, const ptrace_context_t* context, + backtrace_frame_t* backtrace, size_t ignore_depth, + size_t max_depth); + void (*get_backtrace_symbols)(const backtrace_frame_t* backtrace, size_t frames, + backtrace_symbol_t* backtrace_symbols); + void (*get_backtrace_symbols_ptrace)(const ptrace_context_t* context, + const backtrace_frame_t* backtrace, size_t frames, + backtrace_symbol_t* backtrace_symbols); + void (*free_backtrace_symbols)(backtrace_symbol_t* backtrace_symbols, size_t frames); + void (*format_backtrace_line)(unsigned frameNumber, const backtrace_frame_t* frame, + const backtrace_symbol_t* symbol, char* buffer, + size_t bufferSize); +} t_corkscrew; + +static pthread_once_t initialized = PTHREAD_ONCE_INIT; +static t_corkscrew* fkt = NULL; + +void load_library(void) +{ + static t_corkscrew lib; + { + lib.hdl = dlopen("libcorkscrew.so", RTLD_LAZY); + + if (!lib.hdl) + { + LOGF("dlopen error %s", dlerror()); + goto fail; + } + + lib.unwind_backtrace = dlsym(lib.hdl, "unwind_backtrace"); + + if (!lib.unwind_backtrace) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + lib.unwind_backtrace_thread = dlsym(lib.hdl, "unwind_backtrace_thread"); + + if (!lib.unwind_backtrace_thread) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + lib.unwind_backtrace_ptrace = dlsym(lib.hdl, "unwind_backtrace_ptrace"); + + if (!lib.unwind_backtrace_ptrace) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + lib.get_backtrace_symbols = dlsym(lib.hdl, "get_backtrace_symbols"); + + if (!lib.get_backtrace_symbols) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + lib.get_backtrace_symbols_ptrace = dlsym(lib.hdl, "get_backtrace_symbols_ptrace"); + + if (!lib.get_backtrace_symbols_ptrace) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + lib.free_backtrace_symbols = dlsym(lib.hdl, "free_backtrace_symbols"); + + if (!lib.free_backtrace_symbols) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + lib.format_backtrace_line = dlsym(lib.hdl, "format_backtrace_line"); + + if (!lib.format_backtrace_line) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + fkt = &lib; + return; + } +fail: +{ + if (lib.hdl) + dlclose(lib.hdl); + + fkt = NULL; +} +} + +void winpr_corkscrew_backtrace_free(void* buffer) +{ + t_corkscrew_data* data = (t_corkscrew_data*)buffer; + if (!data) + return; + + free(data->buffer); + free(data); +} + +void* winpr_corkscrew_backtrace(DWORD size) +{ + t_corkscrew_data* data = calloc(1, sizeof(t_corkscrew_data)); + + if (!data) + return NULL; + + data->buffer = calloc(size, sizeof(backtrace_frame_t)); + + if (!data->buffer) + { + free(data); + return NULL; + } + + pthread_once(&initialized, load_library); + data->max = size; + data->used = fkt->unwind_backtrace(data->buffer, 0, size); + return data; +} + +char** winpr_corkscrew_backtrace_symbols(void* buffer, size_t* used) +{ + t_corkscrew_data* data = (t_corkscrew_data*)buffer; + if (used) + *used = 0; + + if (!data) + return NULL; + + pthread_once(&initialized, load_library); + + if (!fkt) + { + LOGF(support_msg); + return NULL; + } + else + { + size_t line_len = (data->max > 1024) ? data->max : 1024; + size_t i; + size_t array_size = data->used * sizeof(char*); + size_t lines_size = data->used * line_len; + char** vlines = calloc(1, array_size + lines_size); + backtrace_symbol_t* symbols = calloc(data->used, sizeof(backtrace_symbol_t)); + + if (!vlines || !symbols) + { + free(vlines); + free(symbols); + return NULL; + } + + /* Set the pointers in the allocated buffer's initial array section */ + for (i = 0; i < data->used; i++) + vlines[i] = (char*)vlines + array_size + i * line_len; + + fkt->get_backtrace_symbols(data->buffer, data->used, symbols); + + for (i = 0; i < data->used; i++) + fkt->format_backtrace_line(i, &data->buffer[i], &symbols[i], vlines[i], line_len); + + fkt->free_backtrace_symbols(symbols, data->used); + free(symbols); + + if (used) + *used = data->used; + + return vlines; + } +} + diff --git a/winpr/libwinpr/utils/corkscrew/debug.h b/winpr/libwinpr/utils/corkscrew/debug.h new file mode 100644 index 0000000..e98b9bd --- /dev/null +++ b/winpr/libwinpr/utils/corkscrew/debug.h @@ -0,0 +1,40 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Debugging helpers + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * 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. + */ + +#ifndef WINPR_DEBUG_CORKSCREW_H +#define WINPR_DEBUG_CORKSCREW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + + void* winpr_corkscrew_backtrace(DWORD size); + void winpr_corkscrew_backtrace_free(void* buffer); + char** winpr_corkscrew_backtrace_symbols(void* buffer, size_t* used); + void winpr_corkscrew_backtrace_symbols_fd(void* buffer, int fd); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_DEBUG_CORKSCREW_H */ diff --git a/winpr/libwinpr/utils/debug.c b/winpr/libwinpr/utils/debug.c index 503f4af..460b674 100644 --- a/winpr/libwinpr/utils/debug.c +++ b/winpr/libwinpr/utils/debug.c @@ -24,21 +24,24 @@ #include #include + +#include #include #if defined(HAVE_EXECINFO_H) -#include +#include #endif -#if defined(ANDROID) -#include +#if defined(USE_UNWIND) +#include +#endif + +#if defined(HAVE_CORKSCREW) +#include #endif #if defined(_WIN32) || defined(_WIN64) -#include -#include -#include -#define write _write +#include #endif #include @@ -79,205 +82,19 @@ static const char* support_msg = "Invalid stacktrace buffer! check if platform is supported!"; -#if defined(HAVE_EXECINFO_H) -typedef struct -{ - void** buffer; - size_t max; - size_t used; -} t_execinfo; -#endif - -#if defined(_WIN32) || defined(_WIN64) -typedef struct -{ - PVOID* stack; - ULONG used; - ULONG max; -} t_win_stack; -#endif - -#if defined(ANDROID) -#include -#include -#include - -typedef struct -{ - backtrace_frame_t* buffer; - size_t max; - size_t used; -} t_corkscrew_data; - -typedef struct -{ - void* hdl; - ssize_t (*unwind_backtrace)(backtrace_frame_t* backtrace, size_t ignore_depth, - size_t max_depth); - ssize_t (*unwind_backtrace_thread)(pid_t tid, backtrace_frame_t* backtrace, size_t ignore_depth, - size_t max_depth); - ssize_t (*unwind_backtrace_ptrace)(pid_t tid, const ptrace_context_t* context, - backtrace_frame_t* backtrace, size_t ignore_depth, - size_t max_depth); - void (*get_backtrace_symbols)(const backtrace_frame_t* backtrace, size_t frames, - backtrace_symbol_t* backtrace_symbols); - void (*get_backtrace_symbols_ptrace)(const ptrace_context_t* context, - const backtrace_frame_t* backtrace, size_t frames, - backtrace_symbol_t* backtrace_symbols); - void (*free_backtrace_symbols)(backtrace_symbol_t* backtrace_symbols, size_t frames); - void (*format_backtrace_line)(unsigned frameNumber, const backtrace_frame_t* frame, - const backtrace_symbol_t* symbol, char* buffer, - size_t bufferSize); -} t_corkscrew; - -static pthread_once_t initialized = PTHREAD_ONCE_INIT; -static t_corkscrew* fkt = NULL; - -void load_library(void) -{ - static t_corkscrew lib; - { - lib.hdl = dlopen("libcorkscrew.so", RTLD_LAZY); - - if (!lib.hdl) - { - LOGF("dlopen error %s", dlerror()); - goto fail; - } - - lib.unwind_backtrace = dlsym(lib.hdl, "unwind_backtrace"); - - if (!lib.unwind_backtrace) - { - LOGF("dlsym error %s", dlerror()); - goto fail; - } - - lib.unwind_backtrace_thread = dlsym(lib.hdl, "unwind_backtrace_thread"); - - if (!lib.unwind_backtrace_thread) - { - LOGF("dlsym error %s", dlerror()); - goto fail; - } - - lib.unwind_backtrace_ptrace = dlsym(lib.hdl, "unwind_backtrace_ptrace"); - - if (!lib.unwind_backtrace_ptrace) - { - LOGF("dlsym error %s", dlerror()); - goto fail; - } - - lib.get_backtrace_symbols = dlsym(lib.hdl, "get_backtrace_symbols"); - - if (!lib.get_backtrace_symbols) - { - LOGF("dlsym error %s", dlerror()); - goto fail; - } - - lib.get_backtrace_symbols_ptrace = dlsym(lib.hdl, "get_backtrace_symbols_ptrace"); - - if (!lib.get_backtrace_symbols_ptrace) - { - LOGF("dlsym error %s", dlerror()); - goto fail; - } - - lib.free_backtrace_symbols = dlsym(lib.hdl, "free_backtrace_symbols"); - - if (!lib.free_backtrace_symbols) - { - LOGF("dlsym error %s", dlerror()); - goto fail; - } - - lib.format_backtrace_line = dlsym(lib.hdl, "format_backtrace_line"); - - if (!lib.format_backtrace_line) - { - LOGF("dlsym error %s", dlerror()); - goto fail; - } - - fkt = &lib; - return; - } -fail: -{ - if (lib.hdl) - dlclose(lib.hdl); - - fkt = NULL; -} -} -#endif - -#if defined(_WIN32) && (NTDDI_VERSION <= NTDDI_WINXP) - -typedef USHORT(WINAPI* PRTL_CAPTURE_STACK_BACK_TRACE_FN)(ULONG FramesToSkip, ULONG FramesToCapture, - PVOID* BackTrace, PULONG BackTraceHash); - -static HMODULE g_NTDLL_Library = NULL; -static BOOL g_RtlCaptureStackBackTrace_Detected = FALSE; -static BOOL g_RtlCaptureStackBackTrace_Available = FALSE; -static PRTL_CAPTURE_STACK_BACK_TRACE_FN g_pRtlCaptureStackBackTrace = NULL; - -USHORT RtlCaptureStackBackTrace(ULONG FramesToSkip, ULONG FramesToCapture, PVOID* BackTrace, - PULONG BackTraceHash) -{ - if (!g_RtlCaptureStackBackTrace_Detected) - { - g_NTDLL_Library = LoadLibraryA("kernel32.dll"); - - if (g_NTDLL_Library) - { - g_pRtlCaptureStackBackTrace = (PRTL_CAPTURE_STACK_BACK_TRACE_FN)GetProcAddress( - g_NTDLL_Library, "RtlCaptureStackBackTrace"); - g_RtlCaptureStackBackTrace_Available = (g_pRtlCaptureStackBackTrace) ? TRUE : FALSE; - } - else - { - g_RtlCaptureStackBackTrace_Available = FALSE; - } - - g_RtlCaptureStackBackTrace_Detected = TRUE; - } - - if (g_RtlCaptureStackBackTrace_Available) - { - return (*g_pRtlCaptureStackBackTrace)(FramesToSkip, FramesToCapture, BackTrace, - BackTraceHash); - } - - return 0; -} - -#endif - void winpr_backtrace_free(void* buffer) { if (!buffer) - { - LOGF(support_msg); return; - } -#if defined(HAVE_EXECINFO_H) - t_execinfo* data = (t_execinfo*)buffer; - free(data->buffer); - free(data); -#elif defined(ANDROID) - t_corkscrew_data* data = (t_corkscrew_data*)buffer; - free(data->buffer); - free(data); +#if defined(USE_UNWIND) + winpr_unwind_backtrace_free(buffer); +#elif defined(HAVE_EXECINFO_H) + winpr_execinfo_backtrace_free(buffer); +#elif defined(HAVE_CORKSCREW) + winpr_corkscrew_backtrace_free(buffer); #elif defined(_WIN32) || defined(_WIN64) - { - t_win_stack* data = (t_win_stack*)buffer; - free(data->stack); - free(data); - } + winpr_win_backtrace_free(buffer); #else LOGF(support_msg); #endif @@ -285,60 +102,14 @@ void winpr_backtrace_free(void* buffer) void* winpr_backtrace(DWORD size) { -#if defined(HAVE_EXECINFO_H) - t_execinfo* data = calloc(1, sizeof(t_execinfo)); - - if (!data) - return NULL; - - data->buffer = calloc(size, sizeof(void*)); - - if (!data->buffer) - { - free(data); - return NULL; - } - - data->max = size; - data->used = backtrace(data->buffer, size); - return data; -#elif defined(ANDROID) - t_corkscrew_data* data = calloc(1, sizeof(t_corkscrew_data)); - - if (!data) - return NULL; - - data->buffer = calloc(size, sizeof(backtrace_frame_t)); - - if (!data->buffer) - { - free(data); - return NULL; - } - - pthread_once(&initialized, load_library); - data->max = size; - data->used = fkt->unwind_backtrace(data->buffer, 0, size); - return data; +#if defined(USE_UNWIND) + return winpr_unwind_backtrace(size); +#elif defined(HAVE_EXECINFO_H) + return winpr_execinfo_backtrace(size); +#elif defined(HAVE_CORKSCREW) + return winpr_corkscrew_backtrace(size); #elif (defined(_WIN32) || defined(_WIN64)) && !defined(_UWP) - HANDLE process = GetCurrentProcess(); - t_win_stack* data = calloc(1, sizeof(t_win_stack)); - - if (!data) - return NULL; - - data->max = size; - data->stack = calloc(data->max, sizeof(PVOID)); - - if (!data->stack) - { - free(data); - return NULL; - } - - SymInitialize(process, NULL, TRUE); - data->used = RtlCaptureStackBackTrace(2, size, data->stack, NULL); - return data; + return winpr_win_backtrace(size); #else LOGF(support_msg); return NULL; @@ -356,113 +127,15 @@ char** winpr_backtrace_symbols(void* buffer, size_t* used) return NULL; } -#if defined(HAVE_EXECINFO_H) - t_execinfo* data = (t_execinfo*)buffer; - - if (!data) - return NULL; - - if (used) - *used = data->used; - - return backtrace_symbols(data->buffer, data->used); -#elif defined(ANDROID) - t_corkscrew_data* data = (t_corkscrew_data*)buffer; - - if (!data) - return NULL; - - pthread_once(&initialized, load_library); - - if (!fkt) - { - LOGF(support_msg); - return NULL; - } - else - { - size_t line_len = (data->max > 1024) ? data->max : 1024; - size_t i; - size_t array_size = data->used * sizeof(char*); - size_t lines_size = data->used * line_len; - char** vlines = calloc(1, array_size + lines_size); - backtrace_symbol_t* symbols = calloc(data->used, sizeof(backtrace_symbol_t)); - - if (!vlines || !symbols) - { - free(vlines); - free(symbols); - return NULL; - } - - /* Set the pointers in the allocated buffer's initial array section */ - for (i = 0; i < data->used; i++) - vlines[i] = (char*)vlines + array_size + i * line_len; - - fkt->get_backtrace_symbols(data->buffer, data->used, symbols); - - for (i = 0; i < data->used; i++) - fkt->format_backtrace_line(i, &data->buffer[i], &symbols[i], vlines[i], line_len); - - fkt->free_backtrace_symbols(symbols, data->used); - free(symbols); - - if (used) - *used = data->used; - - return vlines; - } +#if defined(USE_UNWIND) + return winpr_unwind_backtrace_symbols(buffer, used); +#elif defined(HAVE_EXECINFO_H) + return winpr_execinfo_backtrace_symbols(buffer, used); +#elif defined(HAVE_CORKSCREW) + return winpr_corkscrew_backtrace_symbols(buffer, used); #elif (defined(_WIN32) || defined(_WIN64)) && !defined(_UWP) - { - size_t i; - size_t line_len = 1024; - HANDLE process = GetCurrentProcess(); - t_win_stack* data = (t_win_stack*)buffer; - size_t array_size = data->used * sizeof(char*); - size_t lines_size = data->used * line_len; - char** vlines = calloc(1, array_size + lines_size); - SYMBOL_INFO* symbol = calloc(1, sizeof(SYMBOL_INFO) + line_len * sizeof(char)); - IMAGEHLP_LINE64* line = (IMAGEHLP_LINE64*)calloc(1, sizeof(IMAGEHLP_LINE64)); - - if (!vlines || !symbol || !line) - { - free(vlines); - free(symbol); - free(line); - return NULL; - } - - line->SizeOfStruct = sizeof(IMAGEHLP_LINE64); - symbol->MaxNameLen = line_len; - symbol->SizeOfStruct = sizeof(SYMBOL_INFO); - - /* Set the pointers in the allocated buffer's initial array section */ - for (i = 0; i < data->used; i++) - vlines[i] = (char*)vlines + array_size + i * line_len; - - for (i = 0; i < data->used; i++) - { - DWORD64 address = (DWORD64)(data->stack[i]); - DWORD displacement; - SymFromAddr(process, address, 0, symbol); - - if (SymGetLineFromAddr64(process, address, &displacement, line)) - { - sprintf_s(vlines[i], line_len, "%016" PRIx64 ": %s in %s:%" PRIu32, symbol->Address, - symbol->Name, line->FileName, line->LineNumber); - } - else - sprintf_s(vlines[i], line_len, "%016" PRIx64 ": %s", symbol->Address, symbol->Name); - } - - if (used) - *used = data->used; - - free(symbol); - free(line); - return vlines; - } + return winpr_win_backtrace_symbols(buffer, used); #else LOGF(support_msg); return NULL; @@ -477,25 +150,19 @@ void winpr_backtrace_symbols_fd(void* buffer, int fd) return; } -#if defined(HAVE_EXECINFO_H) - t_execinfo* data = (t_execinfo*)buffer; - - if (!data) - return; - - backtrace_symbols_fd(data->buffer, data->used, fd); -#elif defined(_WIN32) || defined(_WIN64) || defined(ANDROID) +#if defined(HAVE_EXECINFO_H) && !defined(USE_UNWIND) + winpr_execinfo_backtrace_symbols_fd(buffer, fd); +#elif !defined(ANDROID) { - DWORD i; - size_t used; - char** lines; - lines = winpr_backtrace_symbols(buffer, &used); + size_t i; + size_t used = 0; + char** lines = winpr_backtrace_symbols(buffer, &used); - if (lines) - { - for (i = 0; i < used; i++) - write(fd, lines[i], strlen(lines[i])); - } + if (!lines) + return; + + for (i = 0; i < used; i++) + _write(fd, lines[i], (unsigned)strnlen(lines[i], UINT32_MAX)); } #else LOGF(support_msg); @@ -527,47 +194,16 @@ void winpr_log_backtrace_ex(wLog* log, DWORD level, DWORD size) for (x = 0; x < used; x++) WLog_Print(log, level, "%" PRIuz ": %s\n", x, msg[x]); } - + free(msg); + +fail: winpr_backtrace_free(stack); } char* winpr_strerror(DWORD dw, char* dmsg, size_t size) { #if defined(_WIN32) - DWORD rc; - DWORD nSize = 0; - DWORD dwFlags = 0; - LPTSTR msg = NULL; - BOOL alloc = FALSE; - dwFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; -#ifdef FORMAT_MESSAGE_ALLOCATE_BUFFER - alloc = TRUE; - dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; -#else - nSize = (DWORD)(size * 2); - msg = (LPTSTR)calloc(nSize, sizeof(TCHAR)); -#endif - rc = FormatMessage(dwFlags, NULL, dw, 0, alloc ? (LPTSTR)&msg : msg, nSize, NULL); - - if (rc) - { -#if defined(UNICODE) - WideCharToMultiByte(CP_ACP, 0, msg, rc, dmsg, size - 1, NULL, NULL); -#else /* defined(UNICODE) */ - memcpy(dmsg, msg, min(rc, size - 1)); -#endif /* defined(UNICODE) */ - dmsg[min(rc, size - 1)] = 0; -#ifdef FORMAT_MESSAGE_ALLOCATE_BUFFER - LocalFree(msg); -#else - free(msg); -#endif - } - else - { - _snprintf(dmsg, size, "FAILURE: 0x%08" PRIX32 "", GetLastError()); - } - + return winpr_win_strerror(dw, dmsg, size); #else /* defined(_WIN32) */ _snprintf(dmsg, size, "%s", strerror(dw)); #endif /* defined(_WIN32) */ diff --git a/winpr/libwinpr/utils/execinfo/debug.c b/winpr/libwinpr/utils/execinfo/debug.c new file mode 100644 index 0000000..509d1f7 --- /dev/null +++ b/winpr/libwinpr/utils/execinfo/debug.c @@ -0,0 +1,88 @@ +/** + * WinPR: Windows Portable Runtime + * Debugging Utils + * + * Copyright 2014 Armin Novak + * Copyright 2014 Thincast Technologies GmbH + * + * 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. + */ + +#include +#include + +#include + +#include "debug.h" + +typedef struct +{ + void** buffer; + size_t max; + size_t used; +} t_execinfo; + +void winpr_execinfo_backtrace_free(void* buffer) +{ + t_execinfo* data = (t_execinfo*)buffer; + if (!data) + return; + + free(data->buffer); + free(data); +} + +void* winpr_execinfo_backtrace(DWORD size) +{ + t_execinfo* data = calloc(1, sizeof(t_execinfo)); + + if (!data) + return NULL; + + data->buffer = calloc(size, sizeof(void*)); + + if (!data->buffer) + { + free(data); + return NULL; + } + + data->max = size; + data->used = backtrace(data->buffer, size); + return data; +} + +char** winpr_execinfo_backtrace_symbols(void* buffer, size_t* used) +{ + t_execinfo* data = (t_execinfo*)buffer; + if (used) + *used = 0; + + if (!data) + return NULL; + + if (used) + *used = data->used; + + return backtrace_symbols(data->buffer, data->used); +} + +void winpr_execinfo_backtrace_symbols_fd(void* buffer, int fd) +{ + t_execinfo* data = (t_execinfo*)buffer; + + if (!data) + return; + + backtrace_symbols_fd(data->buffer, data->used, fd); +} diff --git a/winpr/libwinpr/utils/execinfo/debug.h b/winpr/libwinpr/utils/execinfo/debug.h new file mode 100644 index 0000000..aaf7748 --- /dev/null +++ b/winpr/libwinpr/utils/execinfo/debug.h @@ -0,0 +1,40 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Debugging helpers + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * 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. + */ + +#ifndef WINPR_DEBUG_EXECINFO_H +#define WINPR_DEBUG_EXECINFO_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + + void* winpr_execinfo_backtrace(DWORD size); + void winpr_execinfo_backtrace_free(void* buffer); + char** winpr_execinfo_backtrace_symbols(void* buffer, size_t* used); + void winpr_execinfo_backtrace_symbols_fd(void* buffer, int fd); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_DEBUG_EXECINFO_H */ diff --git a/winpr/libwinpr/utils/stream.c b/winpr/libwinpr/utils/stream.c index 1271981..cc119c7 100644 --- a/winpr/libwinpr/utils/stream.c +++ b/winpr/libwinpr/utils/stream.c @@ -132,3 +132,68 @@ void Stream_Free(wStream* s, BOOL bFreeBuffer) free(s); } } + +BOOL Stream_CheckAndLogRequiredLengthEx(const char* tag, DWORD level, wStream* s, UINT64 len, + const char* fmt, ...) +{ + const size_t actual = Stream_GetRemainingLength(s); + + if (actual < len) + { + va_list args; + + va_start(args, fmt); + Stream_CheckAndLogRequiredLengthExVa(tag, level, s, len, fmt, args); + va_end(args); + + return FALSE; + } + return TRUE; +} + +BOOL Stream_CheckAndLogRequiredLengthExVa(const char* tag, DWORD level, wStream* s, UINT64 len, + const char* fmt, va_list args) +{ + const size_t actual = Stream_GetRemainingLength(s); + + if (actual < len) + return Stream_CheckAndLogRequiredLengthWLogExVa(WLog_Get(tag), level, s, len, fmt, args); + return TRUE; +} + +BOOL Stream_CheckAndLogRequiredLengthWLogEx(wLog* log, DWORD level, wStream* s, UINT64 len, + const char* fmt, ...) +{ + const size_t actual = Stream_GetRemainingLength(s); + + if (actual < len) + { + va_list args; + + va_start(args, fmt); + Stream_CheckAndLogRequiredLengthWLogExVa(log, level, s, len, fmt, args); + va_end(args); + + return FALSE; + } + return TRUE; +} + +BOOL Stream_CheckAndLogRequiredLengthWLogExVa(wLog* log, DWORD level, wStream* s, UINT64 len, + const char* fmt, va_list args) +{ + const size_t actual = Stream_GetRemainingLength(s); + + if (actual < len) + { + char prefix[1024] = { 0 }; + + vsnprintf(prefix, sizeof(prefix), fmt, args); + + WLog_Print(log, level, "[%s] invalid length, got %" PRIuz ", require at least %" PRIu64, + prefix, actual, len); + winpr_log_backtrace_ex(log, level, 20); + return FALSE; + } + return TRUE; +} diff --git a/winpr/libwinpr/utils/unwind/debug.c b/winpr/libwinpr/utils/unwind/debug.c new file mode 100644 index 0000000..4e86f7b --- /dev/null +++ b/winpr/libwinpr/utils/unwind/debug.c @@ -0,0 +1,128 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Debugging helpers + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * 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. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include + +#include +#include "debug.h" + +#include + +typedef struct +{ + uintptr_t pc; + void* langSpecificData; +} unwind_info_t; + +typedef struct +{ + size_t pos; + size_t size; + unwind_info_t* info; +} unwind_context_t; + +static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* context, void* arg) +{ + unwind_context_t* ctx = arg; + + assert(ctx); + + if (ctx->pos < ctx->size) + { + unwind_info_t* info = &ctx->info[ctx->pos++]; + info->pc = _Unwind_GetIP(context); + info->langSpecificData = _Unwind_GetLanguageSpecificData(context); + } + + return _URC_NO_REASON; +} + +void* winpr_unwind_backtrace(DWORD size) +{ + _Unwind_Reason_Code rc; + unwind_context_t* ctx = calloc(1, sizeof(unwind_context_t)); + if (!ctx) + goto fail; + ctx->size = size; + ctx->info = calloc(size, sizeof(unwind_info_t)); + if (!ctx->info) + goto fail; + + rc = _Unwind_Backtrace(unwind_backtrace_callback, ctx); + if (rc != _URC_END_OF_STACK) + goto fail; + + return ctx; +fail: + winpr_unwind_backtrace_free(ctx); + return NULL; +} + +void winpr_unwind_backtrace_free(void* buffer) +{ + unwind_context_t* ctx = buffer; + if (!ctx) + return; + free(ctx->info); + free(ctx); +} + +#define UNWIND_MAX_LINE_SIZE 1024 + +char** winpr_unwind_backtrace_symbols(void* buffer, size_t* used) +{ + size_t x; + char** str = NULL; + unwind_context_t* ctx = buffer; + + if (!ctx) + return NULL; + + str = calloc(ctx->pos * (sizeof(char*) + UNWIND_MAX_LINE_SIZE), sizeof(char*)); + if (!str) + return NULL; + + if (used) + *used = ctx->pos; + + for (x = 0; x < ctx->pos; x++) + { + char* msg = str + ctx->pos * sizeof(char*) + x * UNWIND_MAX_LINE_SIZE; + const unwind_info_t* info = &ctx->info[x]; + Dl_info dlinfo = { 0 }; + int rc = dladdr(info->pc, &dlinfo); + + str[x] = msg; + + if (rc == 0) + _snprintf(msg, UNWIND_MAX_LINE_SIZE, "unresolvable, address=%p", info->pc); + else + _snprintf(msg, UNWIND_MAX_LINE_SIZE, "dli_fname=%s [%p], dli_sname=%s [%p]", + dlinfo.dli_fname, dlinfo.dli_fbase, dlinfo.dli_sname, dlinfo.dli_saddr); + } + + return str; +} diff --git a/winpr/libwinpr/utils/unwind/debug.h b/winpr/libwinpr/utils/unwind/debug.h new file mode 100644 index 0000000..5cd1290 --- /dev/null +++ b/winpr/libwinpr/utils/unwind/debug.h @@ -0,0 +1,41 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Debugging helpers + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * 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. + */ + +#ifndef WINPR_DEBUG_UNWIND_H +#define WINPR_DEBUG_UNWIND_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include + + void* winpr_unwind_backtrace(DWORD size); + void winpr_unwind_backtrace_free(void* buffer); + char** winpr_unwind_backtrace_symbols(void* buffer, size_t* used); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_DEBUG_UNWIND_H */ diff --git a/winpr/libwinpr/utils/windows/debug.c b/winpr/libwinpr/utils/windows/debug.c new file mode 100644 index 0000000..41545c7 --- /dev/null +++ b/winpr/libwinpr/utils/windows/debug.c @@ -0,0 +1,169 @@ +/** + * WinPR: Windows Portable Runtime + * Debugging Utils + * + * Copyright 2014 Armin Novak + * Copyright 2014 Thincast Technologies GmbH + * + * 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. + */ + +#include +#include + +#include +#include +#include + +#include "debug.h" + +#ifndef MIN +#define MIN(a, b) (a) < (b) ? (a) : (b) +#endif + +typedef struct +{ + PVOID* stack; + ULONG used; + ULONG max; +} t_win_stack; + +void winpr_win_backtrace_free(void* buffer) +{ + t_win_stack* data = (t_win_stack*)buffer; + if (!data) + return; + + free(data->stack); + free(data); +} + +void* winpr_win_backtrace(DWORD size) +{ + HANDLE process = GetCurrentProcess(); + t_win_stack* data = calloc(1, sizeof(t_win_stack)); + + if (!data) + return NULL; + + data->max = size; + data->stack = calloc(data->max, sizeof(PVOID)); + + if (!data->stack) + { + free(data); + return NULL; + } + + SymInitialize(process, NULL, TRUE); + data->used = RtlCaptureStackBackTrace(2, size, data->stack, NULL); + return data; +} + +char** winpr_win_backtrace_symbols(void* buffer, size_t* used) +{ + if (used) + *used = 0; + + if (!buffer) + return NULL; + + { + size_t i; + size_t line_len = 1024; + HANDLE process = GetCurrentProcess(); + t_win_stack* data = (t_win_stack*)buffer; + size_t array_size = data->used * sizeof(char*); + size_t lines_size = data->used * line_len; + char** vlines = calloc(1, array_size + lines_size); + SYMBOL_INFO* symbol = calloc(1, sizeof(SYMBOL_INFO) + line_len * sizeof(char)); + IMAGEHLP_LINE64* line = (IMAGEHLP_LINE64*)calloc(1, sizeof(IMAGEHLP_LINE64)); + + if (!vlines || !symbol || !line) + { + free(vlines); + free(symbol); + free(line); + return NULL; + } + + line->SizeOfStruct = sizeof(IMAGEHLP_LINE64); + symbol->MaxNameLen = (ULONG)line_len; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + + /* Set the pointers in the allocated buffer's initial array section */ + for (i = 0; i < data->used; i++) + vlines[i] = (char*)vlines + array_size + i * line_len; + + for (i = 0; i < data->used; i++) + { + DWORD64 address = (DWORD64)(data->stack[i]); + DWORD displacement; + SymFromAddr(process, address, 0, symbol); + + if (SymGetLineFromAddr64(process, address, &displacement, line)) + { + sprintf_s(vlines[i], line_len, "%016" PRIx64 ": %s in %s:%" PRIu32, symbol->Address, + symbol->Name, line->FileName, line->LineNumber); + } + else + sprintf_s(vlines[i], line_len, "%016" PRIx64 ": %s", symbol->Address, symbol->Name); + } + + if (used) + *used = data->used; + + free(symbol); + free(line); + return vlines; + } +} + +char* winpr_win_strerror(DWORD dw, char* dmsg, size_t size) +{ + DWORD rc; + DWORD nSize = 0; + DWORD dwFlags = 0; + LPTSTR msg = NULL; + BOOL alloc = FALSE; + dwFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; +#ifdef FORMAT_MESSAGE_ALLOCATE_BUFFER + alloc = TRUE; + dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; +#else + nSize = (DWORD)(size * 2); + msg = (LPTSTR)calloc(nSize, sizeof(TCHAR)); +#endif + rc = FormatMessage(dwFlags, NULL, dw, 0, alloc ? (LPTSTR)&msg : msg, nSize, NULL); + + if (rc) + { +#if defined(UNICODE) + WideCharToMultiByte(CP_ACP, 0, msg, rc, dmsg, (int)MIN(size - 1, INT_MAX), NULL, NULL); +#else /* defined(UNICODE) */ + memcpy(dmsg, msg, MIN(rc, size - 1)); +#endif /* defined(UNICODE) */ + dmsg[MIN(rc, size - 1)] = 0; +#ifdef FORMAT_MESSAGE_ALLOCATE_BUFFER + LocalFree(msg); +#else + free(msg); +#endif + } + else + { + _snprintf(dmsg, size, "FAILURE: 0x%08" PRIX32 "", GetLastError()); + } + + return dmsg; +} diff --git a/winpr/libwinpr/utils/windows/debug.h b/winpr/libwinpr/utils/windows/debug.h new file mode 100644 index 0000000..c36b87a --- /dev/null +++ b/winpr/libwinpr/utils/windows/debug.h @@ -0,0 +1,40 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Debugging helpers + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * 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. + */ + +#ifndef WINPR_DEBUG_WIN_H +#define WINPR_DEBUG_WIN_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + + void* winpr_win_backtrace(DWORD size); + void winpr_win_backtrace_free(void* buffer); + char** winpr_win_backtrace_symbols(void* buffer, size_t* used); + char* winpr_win_strerror(DWORD dw, char* dmsg, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_DEBUG_WIN_H */