From 15659a2590db52bdee6b31230900737aa6e1cd3e Mon Sep 17 00:00:00 2001 From: David Fort Date: Sat, 29 Nov 2025 23:50:40 +0100 Subject: [PATCH] drdynvc: implement compressed packet The DRDYNVC compression is the zgfx compression, this patch adds the support for DYNVC_DATA_COMPRESSED and DYNVC_DATA_FIRST_COMPRESSED packets. --- channels/drdynvc/client/drdynvc_main.c | 106 ++++++++++++++++++++----- channels/drdynvc/client/drdynvc_main.h | 2 + 2 files changed, 86 insertions(+), 22 deletions(-) diff --git a/channels/drdynvc/client/drdynvc_main.c b/channels/drdynvc/client/drdynvc_main.c index ab1a65b3d..3a0839440 100644 --- a/channels/drdynvc/client/drdynvc_main.c +++ b/channels/drdynvc/client/drdynvc_main.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "drdynvc_main.h" @@ -405,6 +406,7 @@ static void dvcman_channel_free(DVCMAN_CHANNEL* channel) if (channel->dvc_data) Stream_Release(channel->dvc_data); + zgfx_context_free(channel->decompressor); DeleteCriticalSection(&(channel->lock)); free(channel->channel_name); free(channel); @@ -552,10 +554,13 @@ static DVCMAN_CHANNEL* dvcman_channel_new(WINPR_ATTR_UNUSED drdynvcPlugin* drdyn channel->refCounter = 1; channel->state = DVC_CHANNEL_INIT; channel->channel_name = _strdup(ChannelName); - if (!channel->channel_name) goto fail; + channel->decompressor = zgfx_context_new(FALSE); + if (!channel->decompressor) + goto fail; + if (!InitializeCriticalSectionEx(&(channel->lock), 0, 0)) goto fail; @@ -1294,25 +1299,20 @@ static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, UINT8 Sp, UIN * @return 0 on success, otherwise a Win32 error code */ static UINT drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s, - UINT32 ThreadingFlags) + BOOL compressed, UINT32 ThreadingFlags) { - UINT status = CHANNEL_RC_OK; - UINT32 Length = 0; - UINT32 ChannelId = 0; - DVCMAN_CHANNEL* channel = NULL; - WINPR_ASSERT(drdynvc); if (!Stream_CheckAndLogRequiredLength( TAG, s, drdynvc_cblen_to_bytes(cbChId) + drdynvc_cblen_to_bytes(Sp))) return ERROR_INVALID_DATA; - ChannelId = drdynvc_read_variable_uint(s, cbChId); - Length = drdynvc_read_variable_uint(s, Sp); + UINT32 ChannelId = drdynvc_read_variable_uint(s, cbChId); + UINT32 Length = drdynvc_read_variable_uint(s, Sp); WLog_Print(drdynvc->log, WLOG_TRACE, "process_data_first: Sp=%d cbChId=%d, ChannelId=%" PRIu32 " Length=%" PRIu32 "", Sp, cbChId, ChannelId, Length); - channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE); + DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE); if (!channel) { /** @@ -1324,9 +1324,36 @@ static UINT drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp, int cbChI return CHANNEL_RC_OK; } + UINT status = CHANNEL_RC_OK; + BOOL shouldFree = FALSE; if (channel->state != DVC_CHANNEL_RUNNING) goto out; + if (compressed) + { + BYTE* data = NULL; + UINT32 dataSize = 0; + if (zgfx_decompress(channel->decompressor, Stream_Pointer(s), + WINPR_ASSERTING_INT_CAST(UINT32, Stream_GetRemainingLength(s)), &data, + &dataSize, 0) < 0) + { + status = ERROR_INVALID_DATA; + WLog_Print(drdynvc->log, WLOG_ERROR, "error de-compressing first packet"); + goto out; + } + + s = Stream_New(data, dataSize); + if (!s) + { + status = CHANNEL_RC_NO_MEMORY; + WLog_Print(drdynvc->log, WLOG_ERROR, "error allocating new Stream(len=%" PRIu32 ")", + dataSize); + free(data); + goto out; + } + shouldFree = TRUE; + } + status = dvcman_receive_channel_data_first(channel, Length); if (status == CHANNEL_RC_OK) @@ -1336,6 +1363,8 @@ static UINT drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp, int cbChI status = dvcman_channel_close(channel, FALSE, FALSE); out: + if (shouldFree) + Stream_Free(s, TRUE); dvcman_channel_unref(channel); return status; } @@ -1346,21 +1375,17 @@ out: * @return 0 on success, otherwise a Win32 error code */ static UINT drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s, - UINT32 ThreadingFlags) + BOOL compressed, UINT32 ThreadingFlags) { - UINT32 ChannelId = 0; - DVCMAN_CHANNEL* channel = NULL; - UINT status = CHANNEL_RC_OK; - WINPR_ASSERT(drdynvc); if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId))) return ERROR_INVALID_DATA; - ChannelId = drdynvc_read_variable_uint(s, cbChId); + UINT32 ChannelId = drdynvc_read_variable_uint(s, cbChId); WLog_Print(drdynvc->log, WLOG_TRACE, "process_data: Sp=%d cbChId=%d, ChannelId=%" PRIu32 "", Sp, cbChId, ChannelId); - channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE); + DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE); if (!channel) { /** @@ -1372,14 +1397,44 @@ static UINT drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId, wSt return CHANNEL_RC_OK; } + BOOL shouldFree = FALSE; + UINT status = CHANNEL_RC_OK; if (channel->state != DVC_CHANNEL_RUNNING) goto out; + if (compressed) + { + BYTE* data = NULL; + UINT32 dataSize = 0; + + if (zgfx_decompress(channel->decompressor, Stream_Pointer(s), + WINPR_ASSERTING_INT_CAST(UINT32, Stream_GetRemainingLength(s)), &data, + &dataSize, 0) < 0) + { + status = ERROR_INVALID_DATA; + WLog_Print(drdynvc->log, WLOG_ERROR, "error de-compressing data packet"); + goto out; + } + + s = Stream_New(data, dataSize); + if (!s) + { + status = CHANNEL_RC_NO_MEMORY; + WLog_Print(drdynvc->log, WLOG_ERROR, "error allocating new Stream(len=%" PRIu32 ")", + dataSize); + free(data); + goto out; + } + shouldFree = TRUE; + } + status = dvcman_receive_channel_data(channel, s, ThreadingFlags); if (status != CHANNEL_RC_OK) status = dvcman_channel_close(channel, FALSE, FALSE); out: + if (shouldFree) + Stream_Free(s, TRUE); dvcman_channel_unref(channel); return status; } @@ -1423,13 +1478,11 @@ static UINT drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp, int cb */ static UINT drdynvc_order_recv(drdynvcPlugin* drdynvc, wStream* s, UINT32 ThreadingFlags) { - UINT8 value = 0; - WINPR_ASSERT(drdynvc); if (!Stream_CheckAndLogRequiredLength(TAG, s, 1)) return ERROR_INVALID_DATA; - Stream_Read_UINT8(s, value); + UINT8 value = Stream_Get_UINT8(s); const UINT8 Cmd = (value & 0xf0) >> 4; const UINT8 Sp = (value & 0x0c) >> 2; const UINT8 cbChId = (value & 0x03) >> 0; @@ -1445,14 +1498,23 @@ static UINT drdynvc_order_recv(drdynvcPlugin* drdynvc, wStream* s, UINT32 Thread return drdynvc_process_create_request(drdynvc, Sp, cbChId, s); case DATA_FIRST_PDU: - return drdynvc_process_data_first(drdynvc, Sp, cbChId, s, ThreadingFlags); + case DATA_FIRST_COMPRESSED_PDU: + return drdynvc_process_data_first(drdynvc, Sp, cbChId, s, + (Cmd == DATA_FIRST_COMPRESSED_PDU), ThreadingFlags); case DATA_PDU: - return drdynvc_process_data(drdynvc, Sp, cbChId, s, ThreadingFlags); + case DATA_COMPRESSED_PDU: + return drdynvc_process_data(drdynvc, Sp, cbChId, s, (Cmd == DATA_COMPRESSED_PDU), + ThreadingFlags); case CLOSE_REQUEST_PDU: return drdynvc_process_close_request(drdynvc, Sp, cbChId, s); + case SOFT_SYNC_RESPONSE_PDU: + WLog_Print(drdynvc->log, WLOG_ERROR, + "not expecting a SOFT_SYNC_RESPONSE_PDU as a client"); + return ERROR_INTERNAL_ERROR; + default: WLog_Print(drdynvc->log, WLOG_ERROR, "unknown drdynvc cmd 0x%x", Cmd); return ERROR_INTERNAL_ERROR; diff --git a/channels/drdynvc/client/drdynvc_main.h b/channels/drdynvc/client/drdynvc_main.h index c282cdd61..5b1f73368 100644 --- a/channels/drdynvc/client/drdynvc_main.h +++ b/channels/drdynvc/client/drdynvc_main.h @@ -33,6 +33,7 @@ #include #include #include +#include #include typedef struct drdynvc_plugin drdynvcPlugin; @@ -91,6 +92,7 @@ typedef struct wStream* dvc_data; UINT32 dvc_data_length; + ZGFX_CONTEXT* decompressor; CRITICAL_SECTION lock; } DVCMAN_CHANNEL;