From b01e0672d583d4c516db04a4ec72169dd1630875 Mon Sep 17 00:00:00 2001 From: Ondrej Holy Date: Thu, 2 Jul 2026 08:07:33 +0000 Subject: [PATCH] Add RDP Smart Card Redirection Support Resolves: RHEL-171999 --- ...CHANNEL_OPTION_SHOW_PROTOCOL-server-.patch | 551 + ...ix-off-by-one-scan-length-in-rdpdr_r.patch | 26 + freerdp.spec | 24 +- support-rdpdr-device-control-responses.patch | 14826 ++++++++++++++++ ...er-clean-up-warnings-fix-poll-return.patch | 156 + ...r-increase-capacity-for-fragmented-P.patch | 26 + utils-scard-fix-endianess-issue.patch | 36 + ...validate-cbAtr-against-rgbAtr-size-o.patch | 189 + utils-test-replace-NULL.patch | 46 + 9 files changed, 15879 insertions(+), 1 deletion(-) create mode 100644 channels-handle-CHANNEL_OPTION_SHOW_PROTOCOL-server-.patch create mode 100644 channels-rdpdr-fix-off-by-one-scan-length-in-rdpdr_r.patch create mode 100644 support-rdpdr-device-control-responses.patch create mode 100644 utils-pdu-tracker-clean-up-warnings-fix-poll-return.patch create mode 100644 utils-pdu-tracker-increase-capacity-for-fragmented-P.patch create mode 100644 utils-scard-fix-endianess-issue.patch create mode 100644 utils-smartcard-validate-cbAtr-against-rgbAtr-size-o.patch create mode 100644 utils-test-replace-NULL.patch diff --git a/channels-handle-CHANNEL_OPTION_SHOW_PROTOCOL-server-.patch b/channels-handle-CHANNEL_OPTION_SHOW_PROTOCOL-server-.patch new file mode 100644 index 0000000..956310f --- /dev/null +++ b/channels-handle-CHANNEL_OPTION_SHOW_PROTOCOL-server-.patch @@ -0,0 +1,551 @@ +From 2e1113794d125269f30fc7acf4d0f3d898d74d52 Mon Sep 17 00:00:00 2001 +From: David Fort +Date: Mon, 15 Jun 2026 16:30:40 +0200 +Subject: [PATCH] [channels] handle CHANNEL_OPTION_SHOW_PROTOCOL server-side + +With some static channels like rdpdr or clipdr there's no marking of packet +length so in theory you can't handle packets that you don't know (you don't know +how many bytes to skip) or that are badly encoded (WTSVirtualChannelRead linearizes +the traffic so you can't know which bytes are part of the current PDU and which +bytes are from the next one). To solve that this patch adds server-side the handling +of a CHANNEL_OPTION_SHOW_PROTOCOL with WTSVirtualChannelOpen, when you set that +option you get CHANNEL_PDU_HEADER before the packet payload, and so you can do the +reassembly of a PDU. This allows to skip a PDU when you don't know the type, and +go to the next one correctly if for instance the padding at the end of the PDU is +incorrect (think of some smartcard commands invalidly padded by FreeRDP). + +The patch also provides a ChannelPduTracker component that can do the reassembly +for you. +--- + include/freerdp/utils/channel_pdu_tracker.h | 65 +++++++++++ + libfreerdp/core/peer.c | 60 +++++----- + libfreerdp/core/server.c | 82 ++++++++------ + libfreerdp/utils/CMakeLists.txt | 1 + + libfreerdp/utils/channel_pdu_tracker.c | 119 ++++++++++++++++++++ + 5 files changed, 259 insertions(+), 68 deletions(-) + create mode 100644 include/freerdp/utils/channel_pdu_tracker.h + create mode 100644 libfreerdp/utils/channel_pdu_tracker.c + +diff --git a/include/freerdp/utils/channel_pdu_tracker.h b/include/freerdp/utils/channel_pdu_tracker.h +new file mode 100644 +index 0000000..96c52b5 +--- /dev/null ++++ b/include/freerdp/utils/channel_pdu_tracker.h +@@ -0,0 +1,65 @@ ++/** ++ * FreeRDP: A Remote Desktop Protocol Implementation ++ * static channel utility functions ++ * ++ * Copyright 2026 David Fort ++ * ++ * 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_CHANNEL_TRACKER_H_ ++#define FREERDP_UTILS_CHANNEL_TRACKER_H_ ++ ++#include ++#include ++ ++#include ++ ++#ifdef __cplusplus ++extern "C" ++{ ++#endif ++ ++ typedef struct ChannelPduTracker ChannelPduTracker; ++ ++ /** ++ * frees a ChannelPduTracker ++ * @param tracker the tracker to release ++ * @since version 3.28.0 ++ */ ++ FREERDP_API void ChannelPduTracker_free(ChannelPduTracker* tracker); ++ ++ /** ++ * Creates a ChannelPduTracker ++ * @param vc the HANDLE of the channel ++ * @return the created ChannelPduTracker, nullptr if something failed ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_MALLOC(ChannelPduTracker_free, 1) ++ FREERDP_API ChannelPduTracker* ChannelPduTracker_new(HANDLE vc); ++ ++ /** ++ * Polls the channel and return a PDU if a whole PDU was reassembled ++ * @param tracker the ChannelPduTracker to poll a packet from ++ * @param ok a pointer to a status BOOL ++ * @return the reassembled PDU if any ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_MALLOC(Stream_Release, 1) ++ FREERDP_API wStream* ChannelPduTracker_poll(ChannelPduTracker* tracker, BOOL* ok); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* FREERDP_UTILS_CHANNEL_TRACKER_H_ */ +diff --git a/libfreerdp/core/peer.c b/libfreerdp/core/peer.c +index 3e3f024..355c578 100644 +--- a/libfreerdp/core/peer.c ++++ b/libfreerdp/core/peer.c +@@ -48,17 +48,11 @@ static state_run_t peer_recv_pdu(freerdp_peer* client, wStream* s); + static HANDLE freerdp_peer_virtual_channel_open(freerdp_peer* client, const char* name, + UINT32 flags) + { +- UINT32 index = 0; +- BOOL joined = FALSE; +- rdpMcsChannel* mcsChannel = nullptr; +- rdpPeerChannel* peerChannel = nullptr; +- rdpMcs* mcs = nullptr; +- + WINPR_ASSERT(client); + WINPR_ASSERT(client->context); + WINPR_ASSERT(client->context->rdp); + WINPR_ASSERT(name); +- mcs = client->context->rdp->mcs; ++ rdpMcs* mcs = client->context->rdp->mcs; + WINPR_ASSERT(mcs); + + if (flags & WTS_CHANNEL_OPTION_DYNAMIC) +@@ -69,6 +63,9 @@ static HANDLE freerdp_peer_virtual_channel_open(freerdp_peer* client, const char + if (length > 8) + return nullptr; /* SVC maximum name length is 8 */ + ++ BOOL joined = FALSE; ++ UINT32 index = 0; ++ rdpMcsChannel* mcsChannel = nullptr; + for (; index < mcs->channelCount; index++) + { + mcsChannel = &(mcs->channels[index]); +@@ -86,7 +83,7 @@ static HANDLE freerdp_peer_virtual_channel_open(freerdp_peer* client, const char + if (!joined) + return nullptr; /* channel is not joined */ + +- peerChannel = (rdpPeerChannel*)mcsChannel->handle; ++ rdpPeerChannel* peerChannel = (rdpPeerChannel*)mcsChannel->handle; + + if (peerChannel) + { +@@ -1473,33 +1470,30 @@ freerdp_peer* freerdp_peer_new(int sockfd) + (void)freerdp_tcp_set_nodelay(WLog_Get(TAG), WLOG_DEBUG, sockfd); + } + +- if (client) +- { +- client->sockfd = sockfd; +- client->ContextSize = sizeof(rdpContext); +- client->Initialize = freerdp_peer_initialize; ++ client->sockfd = sockfd; ++ client->ContextSize = sizeof(rdpContext); ++ client->Initialize = freerdp_peer_initialize; + #if defined(WITH_FREERDP_DEPRECATED) +- client->GetFileDescriptor = freerdp_peer_get_fds; ++ client->GetFileDescriptor = freerdp_peer_get_fds; + #endif +- client->GetEventHandle = freerdp_peer_get_event_handle; +- client->GetEventHandles = freerdp_peer_get_event_handles; +- client->CheckFileDescriptor = freerdp_peer_check_fds; +- client->Close = freerdp_peer_close; +- client->Disconnect = freerdp_peer_disconnect; +- client->SendChannelData = freerdp_peer_send_channel_data; +- client->SendChannelPacket = freerdp_peer_send_channel_packet; +- client->SendServerRedirection = freerdp_peer_send_server_redirection_pdu; +- client->IsWriteBlocked = freerdp_peer_is_write_blocked; +- client->DrainOutputBuffer = freerdp_peer_drain_output_buffer; +- client->HasMoreToRead = freerdp_peer_has_more_to_read; +- client->VirtualChannelOpen = freerdp_peer_virtual_channel_open; +- client->VirtualChannelClose = freerdp_peer_virtual_channel_close; +- client->VirtualChannelWrite = freerdp_peer_virtual_channel_write; +- client->VirtualChannelRead = nullptr; /* must be defined by server application */ +- client->VirtualChannelGetData = freerdp_peer_virtual_channel_get_data; +- client->VirtualChannelSetData = freerdp_peer_virtual_channel_set_data; +- client->SetState = freerdp_peer_set_state; +- } ++ client->GetEventHandle = freerdp_peer_get_event_handle; ++ client->GetEventHandles = freerdp_peer_get_event_handles; ++ client->CheckFileDescriptor = freerdp_peer_check_fds; ++ client->Close = freerdp_peer_close; ++ client->Disconnect = freerdp_peer_disconnect; ++ client->SendChannelData = freerdp_peer_send_channel_data; ++ client->SendChannelPacket = freerdp_peer_send_channel_packet; ++ client->SendServerRedirection = freerdp_peer_send_server_redirection_pdu; ++ client->IsWriteBlocked = freerdp_peer_is_write_blocked; ++ client->DrainOutputBuffer = freerdp_peer_drain_output_buffer; ++ client->HasMoreToRead = freerdp_peer_has_more_to_read; ++ client->VirtualChannelOpen = freerdp_peer_virtual_channel_open; ++ client->VirtualChannelClose = freerdp_peer_virtual_channel_close; ++ client->VirtualChannelWrite = freerdp_peer_virtual_channel_write; ++ client->VirtualChannelRead = nullptr; /* must be defined by server application */ ++ client->VirtualChannelGetData = freerdp_peer_virtual_channel_get_data; ++ client->VirtualChannelSetData = freerdp_peer_virtual_channel_set_data; ++ client->SetState = freerdp_peer_set_state; + + return client; + } +diff --git a/libfreerdp/core/server.c b/libfreerdp/core/server.c +index 3c998f0..7afcb62 100644 +--- a/libfreerdp/core/server.c ++++ b/libfreerdp/core/server.c +@@ -76,23 +76,27 @@ static rdpPeerChannel* wts_get_dvc_channel_by_id(WTSVirtualChannelManager* vcm, + return HashTable_GetItemValue(vcm->dynamicVirtualChannels, &ChannelId); + } + +-static BOOL wts_queue_receive_data(rdpPeerChannel* channel, const BYTE* Buffer, UINT32 Length) ++static BOOL wts_queue_receive_data(rdpPeerChannel* channel, const BYTE* Buffer1, UINT32 Length1, ++ const BYTE* Buffer2, UINT32 Length2) + { +- BYTE* buffer = nullptr; +- wtsChannelMessage* messageCtx = nullptr; +- + WINPR_ASSERT(channel); +- messageCtx = (wtsChannelMessage*)malloc(sizeof(wtsChannelMessage) + Length); + ++ wtsChannelMessage* messageCtx = ++ (wtsChannelMessage*)malloc(sizeof(wtsChannelMessage) + Length1 + Length2); + if (!messageCtx) + return FALSE; + + WINPR_ASSERT(channel->channelId <= UINT16_MAX); + messageCtx->channelId = (UINT16)channel->channelId; +- messageCtx->length = Length; ++ messageCtx->length = Length1 + Length2; + messageCtx->offset = 0; +- buffer = (BYTE*)(messageCtx + 1); +- CopyMemory(buffer, Buffer, Length); ++ BYTE* buffer = (BYTE*)(messageCtx + 1); ++ CopyMemory(buffer, Buffer1, Length1); ++ if (Length2 && Buffer2) ++ { ++ buffer += Length1; ++ CopyMemory(buffer, Buffer2, Length2); ++ } + return MessageQueue_Post(channel->queue, messageCtx, 0, nullptr, nullptr); + } + +@@ -252,7 +256,7 @@ static BOOL wts_read_drdynvc_data(rdpPeerChannel* channel, wStream* s, UINT32 le + if (Stream_GetPosition(channel->receiveData) >= channel->dvc_total_length) + { + ret = wts_queue_receive_data(channel, Stream_Buffer(channel->receiveData), +- channel->dvc_total_length); ++ channel->dvc_total_length, nullptr, 0); + channel->dvc_total_length = 0; + } + else +@@ -260,7 +264,7 @@ static BOOL wts_read_drdynvc_data(rdpPeerChannel* channel, wStream* s, UINT32 le + } + else + { +- ret = wts_queue_receive_data(channel, Stream_ConstPointer(s), length); ++ ret = wts_queue_receive_data(channel, Stream_ConstPointer(s), length, nullptr, 0); + } + + return ret; +@@ -454,6 +458,17 @@ static BOOL WTSProcessChannelData(rdpPeerChannel* channel, UINT16 channelId, con + WINPR_ASSERT(channel->vcm); + WINPR_UNUSED(channelId); + ++ if (channel->channelFlags & CHANNEL_OPTION_SHOW_PROTOCOL) ++ { ++ const CHANNEL_PDU_HEADER header = { ++ .length = (UINT32)size, ++ .flags = flags, ++ }; ++ ++ return wts_queue_receive_data(channel, (const BYTE*)&header, sizeof(header), data, ++ (UINT32)size); ++ } ++ + if (flags & CHANNEL_FLAG_FIRST) + { + Stream_ResetPosition(channel->receiveData); +@@ -482,7 +497,7 @@ static BOOL WTSProcessChannelData(rdpPeerChannel* channel, UINT16 channelId, con + ret = FALSE; + else + ret = wts_queue_receive_data(channel, Stream_Buffer(channel->receiveData), +- (UINT32)pos); ++ (UINT32)pos, nullptr, 0); + } + + Stream_ResetPosition(channel->receiveData); +@@ -1353,7 +1368,7 @@ static void peer_channel_queue_free_message(void* obj) + + static rdpPeerChannel* channel_new(WTSVirtualChannelManager* vcm, freerdp_peer* client, + UINT32 ChannelId, UINT16 index, UINT16 type, size_t chunkSize, +- const char* name) ++ const char* name, UINT32 flags) + { + wObject queueCallbacks = WINPR_C_ARRAY_INIT; + queueCallbacks.fnObjectFree = peer_channel_queue_free_message; +@@ -1371,6 +1386,7 @@ static rdpPeerChannel* channel_new(WTSVirtualChannelManager* vcm, freerdp_peer* + channel->channelType = type; + channel->creationStatus = + (type == RDP_PEER_CHANNEL_TYPE_SVC) ? ERROR_SUCCESS : ERROR_OPERATION_IN_PROGRESS; ++ channel->channelFlags = flags; + + return channel; + fail: +@@ -1378,44 +1394,35 @@ fail: + return nullptr; + } + +-HANDLE WINAPI FreeRDP_WTSVirtualChannelOpen(HANDLE hServer, WINPR_ATTR_UNUSED DWORD SessionId, +- LPSTR pVirtualName) ++HANDLE WINAPI FreeRDP_WTSVirtualChannelOpenStatic(HANDLE hServer, WINPR_ATTR_UNUSED DWORD SessionId, ++ LPSTR pVirtualName, UINT32 Flags) + { +- size_t length = 0; +- rdpMcs* mcs = nullptr; +- rdpMcsChannel* joined_channel = nullptr; +- freerdp_peer* client = nullptr; +- rdpPeerChannel* channel = nullptr; +- WTSVirtualChannelManager* vcm = nullptr; +- HANDLE hChannelHandle = nullptr; +- rdpContext* context = nullptr; +- vcm = (WTSVirtualChannelManager*)hServer; +- ++ WTSVirtualChannelManager* vcm = (WTSVirtualChannelManager*)hServer; + if (!vcm) + { + SetLastError(ERROR_INVALID_DATA); + return nullptr; + } + +- client = vcm->client; ++ freerdp_peer* client = vcm->client; + WINPR_ASSERT(client); + +- context = client->context; ++ rdpContext* context = client->context; + WINPR_ASSERT(context); + WINPR_ASSERT(context->rdp); + WINPR_ASSERT(context->settings); + +- mcs = context->rdp->mcs; ++ rdpMcs* mcs = context->rdp->mcs; + WINPR_ASSERT(mcs); + +- length = strnlen(pVirtualName, CHANNEL_NAME_LEN + 1); +- ++ size_t length = strnlen(pVirtualName, CHANNEL_NAME_LEN + 1); + if (length > CHANNEL_NAME_LEN) + { + SetLastError(ERROR_NOT_FOUND); + return nullptr; + } + ++ rdpMcsChannel* joined_channel = nullptr; + UINT32 index = 0; + for (; index < mcs->channelCount; index++) + { +@@ -1433,8 +1440,7 @@ HANDLE WINAPI FreeRDP_WTSVirtualChannelOpen(HANDLE hServer, WINPR_ATTR_UNUSED DW + return nullptr; + } + +- channel = (rdpPeerChannel*)joined_channel->handle; +- ++ rdpPeerChannel* channel = (rdpPeerChannel*)joined_channel->handle; + if (!channel) + { + const UINT32 VCChunkSize = +@@ -1442,7 +1448,7 @@ HANDLE WINAPI FreeRDP_WTSVirtualChannelOpen(HANDLE hServer, WINPR_ATTR_UNUSED DW + + WINPR_ASSERT(index <= UINT16_MAX); + channel = channel_new(vcm, client, joined_channel->ChannelId, (UINT16)index, +- RDP_PEER_CHANNEL_TYPE_SVC, VCChunkSize, pVirtualName); ++ RDP_PEER_CHANNEL_TYPE_SVC, VCChunkSize, pVirtualName, Flags); + + if (!channel) + goto fail; +@@ -1450,7 +1456,7 @@ HANDLE WINAPI FreeRDP_WTSVirtualChannelOpen(HANDLE hServer, WINPR_ATTR_UNUSED DW + joined_channel->handle = channel; + } + +- hChannelHandle = (HANDLE)channel; ++ HANDLE hChannelHandle = (HANDLE)channel; + return hChannelHandle; + fail: + channel_free(channel); +@@ -1458,6 +1464,11 @@ fail: + return nullptr; + } + ++HANDLE WINAPI FreeRDP_WTSVirtualChannelOpen(HANDLE hServer, DWORD SessionId, LPSTR pVirtualName) ++{ ++ return FreeRDP_WTSVirtualChannelOpenStatic(hServer, SessionId, pVirtualName, 0); ++} ++ + HANDLE WINAPI FreeRDP_WTSVirtualChannelOpenEx(DWORD SessionId, LPSTR pVirtualName, DWORD flags) + { + wStream* s = nullptr; +@@ -1481,7 +1492,7 @@ HANDLE WINAPI FreeRDP_WTSVirtualChannelOpenEx(DWORD SessionId, LPSTR pVirtualNam + if (!(flags & WTS_CHANNEL_OPTION_DYNAMIC)) + { + HashTable_Unlock(g_ServerHandles); +- return FreeRDP_WTSVirtualChannelOpen((HANDLE)vcm, SessionId, pVirtualName); ++ return FreeRDP_WTSVirtualChannelOpenStatic((HANDLE)vcm, SessionId, pVirtualName, flags); + } + + freerdp_peer* client = vcm->client; +@@ -1521,7 +1532,8 @@ HANDLE WINAPI FreeRDP_WTSVirtualChannelOpenEx(DWORD SessionId, LPSTR pVirtualNam + + const UINT32 VCChunkSize = + freerdp_settings_get_uint32(client->context->settings, FreeRDP_VCChunkSize); +- channel = channel_new(vcm, client, 0, 0, RDP_PEER_CHANNEL_TYPE_DVC, VCChunkSize, pVirtualName); ++ channel = ++ channel_new(vcm, client, 0, 0, RDP_PEER_CHANNEL_TYPE_DVC, VCChunkSize, pVirtualName, flags); + + if (!channel) + { +diff --git a/libfreerdp/utils/CMakeLists.txt b/libfreerdp/utils/CMakeLists.txt +index 6f821a6..30b7ca9 100644 +--- a/libfreerdp/utils/CMakeLists.txt ++++ b/libfreerdp/utils/CMakeLists.txt +@@ -19,6 +19,7 @@ set(MODULE_NAME "freerdp-utils") + set(MODULE_PREFIX "FREERDP_UTILS") + + set(${MODULE_PREFIX}_SRCS ++ channel_pdu_tracker.c + encoded_types.c + helpers.c + passphrase.c +diff --git a/libfreerdp/utils/channel_pdu_tracker.c b/libfreerdp/utils/channel_pdu_tracker.c +new file mode 100644 +index 0000000..18e9aab +--- /dev/null ++++ b/libfreerdp/utils/channel_pdu_tracker.c +@@ -0,0 +1,119 @@ ++/** ++ * FreeRDP: A Remote Desktop Protocol Implementation ++ * static channel utility functions ++ * ++ * Copyright 2026 David Fort ++ * ++ * 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 ++ ++struct ChannelPduTracker ++{ ++ HANDLE vc; ++ wStream* currentPacket; ++ BYTE buffer[CHANNEL_PDU_LENGTH]; ++ BYTE* writePtr; ++}; ++ ++#define TAG "ChannelPduTracker" ++ ++wStream* ChannelPduTracker_poll(ChannelPduTracker* tracker, BOOL* ok) ++{ ++ WINPR_ASSERT(tracker); ++ WINPR_ASSERT(ok); ++ ++ ULONG sz = 0; ++ *ok = FALSE; ++ const ULONG readSz = ++ WINPR_ASSERTING_INT_CAST(ULONG, CHANNEL_PDU_LENGTH - (tracker->writePtr - tracker->buffer)); ++ if (!WTSVirtualChannelRead(tracker->vc, INFINITE, (char*)tracker->writePtr, readSz, &sz)) ++ return nullptr; ++ ++ tracker->writePtr += sz; ++ const size_t recvSz = (tracker->writePtr - tracker->buffer); ++ if (recvSz < sizeof(CHANNEL_PDU_HEADER)) ++ { ++ *ok = TRUE; ++ return nullptr; ++ } ++ ++ CHANNEL_PDU_HEADER* header = (CHANNEL_PDU_HEADER*)tracker->buffer; ++ if (header->length > CHANNEL_CHUNK_LENGTH) ++ { ++ WLog_ERR(TAG, "chunk size %" PRIu32 " is too big", header->length); ++ return nullptr; ++ } ++ ++ if (recvSz - sizeof(CHANNEL_PDU_HEADER) < header->length) ++ { ++ WLog_ERR(TAG, "chunk size %" PRIu32 " is too big", header->length); ++ return nullptr; ++ } ++ ++ tracker->writePtr = tracker->buffer; ++ if (header->flags & CHANNEL_FLAG_FIRST) ++ Stream_ResetPosition(tracker->currentPacket); ++ ++ if (!Stream_EnsureRemainingCapacity(tracker->currentPacket, header->length)) ++ return nullptr; ++ ++ Stream_Write(tracker->currentPacket, header + 1, header->length); ++ ++ if (header->flags & CHANNEL_FLAG_LAST) ++ { ++ Stream_SealLength(tracker->currentPacket); ++ Stream_ResetPosition(tracker->currentPacket); ++ wStream* ret = tracker->currentPacket; ++ ++ tracker->currentPacket = Stream_New(nullptr, 4096); ++ if (!tracker->currentPacket) ++ { ++ Stream_Release(ret); ++ WLog_ERR(TAG, "error allocating new currentPacket"); ++ return nullptr; ++ } ++ } ++ ++ *ok = TRUE; ++ return nullptr; ++} ++ ++void ChannelPduTracker_free(ChannelPduTracker* tracker) ++{ ++ if (!tracker) ++ return; ++ ++ Stream_Release(tracker->currentPacket); ++ free(tracker); ++} ++ ++ChannelPduTracker* ChannelPduTracker_new(HANDLE vc) ++{ ++ ChannelPduTracker* ret = calloc(1, sizeof(*ret)); ++ if (!ret) ++ return nullptr; ++ ++ ret->currentPacket = Stream_New(nullptr, 4096); ++ if (!ret->currentPacket) ++ { ++ free(ret); ++ return nullptr; ++ } ++ ret->vc = vc; ++ ret->writePtr = ret->buffer; ++ return ret; ++} +-- +2.54.0 + diff --git a/channels-rdpdr-fix-off-by-one-scan-length-in-rdpdr_r.patch b/channels-rdpdr-fix-off-by-one-scan-length-in-rdpdr_r.patch new file mode 100644 index 0000000..3264d6e --- /dev/null +++ b/channels-rdpdr-fix-off-by-one-scan-length-in-rdpdr_r.patch @@ -0,0 +1,26 @@ +From 8223c5a0abdf4d5eae40c66e1329d62766578ce1 Mon Sep 17 00:00:00 2001 +From: Sayed Kaif +Date: Wed, 24 Jun 2026 17:44:53 +0530 +Subject: [PATCH] [channels,rdpdr] fix off-by-one scan length in + rdpdr_read_ustring + +--- + channels/rdpdr/server/rdpdr_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/channels/rdpdr/server/rdpdr_main.c b/channels/rdpdr/server/rdpdr_main.c +index bbb3b7e..62e5b90 100644 +--- a/channels/rdpdr/server/rdpdr_main.c ++++ b/channels/rdpdr/server/rdpdr_main.c +@@ -222,7 +222,7 @@ static UINT32 g_ClientId = 0; + + static const WCHAR* rdpdr_read_ustring(wLog* log, wStream* s, size_t bytelen) + { +- const size_t charlen = (bytelen + 1) / sizeof(WCHAR); ++ const size_t charlen = bytelen / sizeof(WCHAR); + const WCHAR* str = Stream_ConstPointer(s); + if (!Stream_CheckAndLogRequiredLengthWLog(log, s, bytelen)) + return nullptr; +-- +2.54.0 + diff --git a/freerdp.spec b/freerdp.spec index 4fc4879..a5c84cd 100644 --- a/freerdp.spec +++ b/freerdp.spec @@ -30,7 +30,7 @@ Name: freerdp Epoch: 2 Version: 3.27.1 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Free implementation of the Remote Desktop Protocol (RDP) # The effective license is Apache-2.0 but: @@ -47,6 +47,24 @@ URL: http://www.freerdp.com/ Source0: FreeRDP-%{version}-repack.tar.gz Source1: freerdp_download_and_repack.sh +# Add RDP Smart Card Redirection Support +# https://github.com/FreeRDP/FreeRDP/pull/12928 +# https://github.com/FreeRDP/FreeRDP/commit/2a6d532a1d633145ae48e55ae471f2f2d8e56157 +# https://github.com/FreeRDP/FreeRDP/commit/5cdd46594ec792715a00ecb935dcac7aadde0fc3 +# https://github.com/FreeRDP/FreeRDP/commit/2e1113794d125269f30fc7acf4d0f3d898d74d52 +# https://github.com/FreeRDP/FreeRDP/commit/8223c5a0abdf4d5eae40c66e1329d62766578ce1 +# https://github.com/FreeRDP/FreeRDP/commit/1a15128e93705d5680c84b9e6368245d247fe893 +# https://github.com/FreeRDP/FreeRDP/commit/a41855e88ea37b92a4d506430bce4b09e76f7d76 +# https://github.com/FreeRDP/FreeRDP/pull/12756 +Patch: channels-handle-CHANNEL_OPTION_SHOW_PROTOCOL-server-.patch +Patch: utils-smartcard-validate-cbAtr-against-rgbAtr-size-o.patch +Patch: utils-scard-fix-endianess-issue.patch +Patch: utils-test-replace-NULL.patch +Patch: channels-rdpdr-fix-off-by-one-scan-length-in-rdpdr_r.patch +Patch: utils-pdu-tracker-clean-up-warnings-fix-poll-return.patch +Patch: utils-pdu-tracker-increase-capacity-for-fragmented-P.patch +Patch: support-rdpdr-device-control-responses.patch + BuildRequires: gcc BuildRequires: gcc-c++ BuildRequires: alsa-lib-devel @@ -378,6 +396,10 @@ find %{buildroot} -name "*.a" -delete %{_libdir}/pkgconfig/winpr-tools3.pc %changelog +* Wed Jul 01 2026 Ondrej Holy - 2:3.27.1-2 +- Add RDP Smart Card Redirection Support + Resolves: RHEL-171999 + * Wed Jul 01 2026 Ondrej Holy - 2:3.27.1-1 - Update to 3.27.1 Resolves: RHEL-186097, RHEL-186938, RHEL-186960, RHEL-186965, RHEL-186980 diff --git a/support-rdpdr-device-control-responses.patch b/support-rdpdr-device-control-responses.patch new file mode 100644 index 0000000..c3981c2 --- /dev/null +++ b/support-rdpdr-device-control-responses.patch @@ -0,0 +1,14826 @@ +From 9088b82c20844abf72d51368a3b14d3e95128937 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Wed, 6 May 2026 16:32:49 +0200 +Subject: [PATCH 01/31] [channels,rdpdr] fix capability type check + +caps was accumulated using CAP_*_TYPE enum values (sequential: 1-5) +but compared against context->supported which uses RDPDR_DTYP_* +bitmask values. Map CAP_*_TYPE to the corresponding RDPDR_DTYP_* +bitmask before accumulating into caps. + +Also moved the supported-check and deactivation out of the +per-capability loop to avoid prematurely clearing context->supported +bits before all capabilities were accumulated. +--- + channels/rdpdr/server/rdpdr_main.c | 41 ++++++++++++++++++++---------- + 1 file changed, 27 insertions(+), 14 deletions(-) + +diff --git a/channels/rdpdr/server/rdpdr_main.c b/channels/rdpdr/server/rdpdr_main.c +index 62e5b90448a3..1de60b6688df 100644 +--- a/channels/rdpdr/server/rdpdr_main.c ++++ b/channels/rdpdr/server/rdpdr_main.c +@@ -904,6 +904,24 @@ static UINT rdpdr_server_send_core_capability_request(RdpdrServerContext* contex + return error; + } + ++static UINT16 rdpdr_cap_type_to_dtyp(UINT16 capabilityType) ++{ ++ switch (capabilityType) ++ { ++ case CAP_PRINTER_TYPE: ++ return RDPDR_DTYP_PRINT; ++ case CAP_PORT_TYPE: ++ return RDPDR_DTYP_SERIAL | RDPDR_DTYP_PARALLEL; ++ case CAP_DRIVE_TYPE: ++ return RDPDR_DTYP_FILESYSTEM; ++ case CAP_SMARTCARD_TYPE: ++ return RDPDR_DTYP_SMARTCARD; ++ case CAP_GENERAL_TYPE: ++ default: ++ return 0; ++ } ++} ++ + /** + * Function description + * +@@ -943,7 +961,6 @@ static UINT rdpdr_server_receive_core_capability_response(RdpdrServerContext* co + if (status != CHANNEL_RC_OK) + return status; + +- caps |= capabilityHeader.CapabilityType; + switch (capabilityHeader.CapabilityType) + { + case CAP_GENERAL_TYPE: +@@ -1007,21 +1024,14 @@ static UINT rdpdr_server_receive_core_capability_response(RdpdrServerContext* co + return ERROR_INVALID_DATA; + } + +- for (UINT16 x = 0; x < 16; x++) ++ const UINT16 client_dtyp = rdpdr_cap_type_to_dtyp(capabilityHeader.CapabilityType); ++ if ((client_dtyp != 0) && ((client_dtyp & context->supported) == 0)) + { +- const UINT16 mask = (UINT16)(1u << x); +- if (((caps & mask) != 0) && ((context->supported & mask) == 0)) +- { +- WLog_Print(context->priv->log, WLOG_WARN, +- "client sent capability %s we did not announce!", +- freerdp_rdpdr_dtyp_string(mask)); +- } +- +- /* we assume the server supports the capability. so only deactivate what the client did +- * not respond with */ +- if ((caps & mask) == 0) +- context->supported &= ~mask; ++ WLog_Print(context->priv->log, WLOG_WARN, ++ "client sent capability %s we did not announce!", ++ rdpdr_cap_type_string(capabilityHeader.CapabilityType)); + } ++ caps |= client_dtyp; + + const size_t end = Stream_GetPosition(s); + const size_t diff = end - start; +@@ -1035,6 +1045,9 @@ static UINT rdpdr_server_receive_core_capability_response(RdpdrServerContext* co + } + } + ++ /* Only deactivate what the client did not respond with */ ++ context->supported &= caps; ++ + return CHANNEL_RC_OK; + } + + +From 0e5bcdbb78657828098b290579da03c080e40333 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Wed, 6 May 2026 19:02:19 +0200 +Subject: [PATCH 02/31] [channels,smartcard] fix DeviceDataLength in + DEVICE_ANNOUNCE + +Per MS-RDPEFS 2.2.1.3, the DeviceDataLength field in the +DEVICE_ANNOUNCE message for smartcard devices must be 0. +Remove the "SCARD" device data that was incorrectly sent, +which caused the server to reject the announcement. +--- + channels/smartcard/client/smartcard_main.c | 11 ----------- + 1 file changed, 11 deletions(-) + +diff --git a/channels/smartcard/client/smartcard_main.c b/channels/smartcard/client/smartcard_main.c +index d186ddfa7ad8..bf08b8c08c7e 100644 +--- a/channels/smartcard/client/smartcard_main.c ++++ b/channels/smartcard/client/smartcard_main.c +@@ -632,7 +632,6 @@ static void smartcard_free_irp(void* obj) + */ + FREERDP_ENTRY_POINT(UINT VCAPITYPE DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)) + { +- size_t length = 0; + UINT error = CHANNEL_RC_NO_MEMORY; + + SMARTCARD_DEVICE* smartcard = (SMARTCARD_DEVICE*)calloc(1, sizeof(SMARTCARD_DEVICE)); +@@ -649,16 +648,6 @@ FREERDP_ENTRY_POINT(UINT VCAPITYPE DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POIN + smartcard->device.Init = smartcard_init; + smartcard->device.Free = smartcard_free; + smartcard->rdpcontext = pEntryPoints->rdpcontext; +- length = strlen(smartcard->device.name); +- smartcard->device.data = Stream_New(nullptr, length + 1); +- +- if (!smartcard->device.data) +- { +- WLog_ERR(TAG, "Stream_New failed!"); +- goto fail; +- } +- +- Stream_Write(smartcard->device.data, "SCARD", 6); + smartcard->IrpQueue = MessageQueue_New(nullptr); + + if (!smartcard->IrpQueue) + +From 8a9b9d3b0e30f1e5454fb377bd5400c75175ae2e Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Thu, 7 May 2026 19:05:10 +0200 +Subject: [PATCH 03/31] [utils,smartcard] use + RDPDR_DEVICE_IO_CONTROL_REQ_HDR_LENGTH constant + +Replace hardcoded 32 with the named constant for the device control +request header length check. +--- + libfreerdp/utils/smartcard_operations.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libfreerdp/utils/smartcard_operations.c b/libfreerdp/utils/smartcard_operations.c +index b7a754831b30..dfbdfeacb08c 100644 +--- a/libfreerdp/utils/smartcard_operations.c ++++ b/libfreerdp/utils/smartcard_operations.c +@@ -547,7 +547,7 @@ LONG smartcard_irp_device_control_decode(wStream* s, UINT32 CompletionId, UINT32 + + /* Device Control Request */ + +- if (!Stream_CheckAndLogRequiredLength(TAG, s, 32)) ++ if (!Stream_CheckAndLogRequiredLength(TAG, s, RDPDR_DEVICE_IO_CONTROL_REQ_HDR_LENGTH)) + return SCARD_F_INTERNAL_ERROR; + + const UINT32 outputBufferLength = Stream_Get_UINT32(s); /* OutputBufferLength (4 bytes) */ + +From be8358d457f18dd9b6ace7abd9a04f5338676a05 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Thu, 7 May 2026 19:08:06 +0200 +Subject: [PATCH 04/31] [utils,smartcard] rename decode to decode_request + +smartcard_irp_device_control_decode only decodes device control request +types (DR_CONTROL_REQ), not response types. Rename to decode_request to +make this explicit and allow a symmetric decode_response counterpart. +--- + channels/smartcard/client/smartcard_main.c | 4 +- + include/freerdp/utils/smartcard_operations.h | 31 ++++ + libfreerdp/utils/smartcard_operations.c | 184 ++++++++++--------- + server/proxy/channels/pf_channel_smartcard.c | 2 +- + 4 files changed, 130 insertions(+), 91 deletions(-) + +diff --git a/channels/smartcard/client/smartcard_main.c b/channels/smartcard/client/smartcard_main.c +index bf08b8c08c7e..0093a1955d8a 100644 +--- a/channels/smartcard/client/smartcard_main.c ++++ b/channels/smartcard/client/smartcard_main.c +@@ -378,8 +378,8 @@ static UINT smartcard_process_irp(SMARTCARD_DEVICE* smartcard, IRP* irp, BOOL* h + element->irp = irp; + element->operation.completionID = irp->CompletionId; + +- status = smartcard_irp_device_control_decode(irp->input, irp->CompletionId, irp->FileId, +- &element->operation); ++ status = smartcard_irp_device_control_decode_request(irp->input, irp->CompletionId, ++ irp->FileId, &element->operation); + + if (status != SCARD_S_SUCCESS) + { +diff --git a/include/freerdp/utils/smartcard_operations.h b/include/freerdp/utils/smartcard_operations.h +index 90c71baf7dee..41a5fd6b043a 100644 +--- a/include/freerdp/utils/smartcard_operations.h ++++ b/include/freerdp/utils/smartcard_operations.h +@@ -86,11 +86,42 @@ extern "C" + UINT32 outputBufferLength; /** @since version 3.13.0 */ + } SMARTCARD_OPERATION; + ++ /** @brief Alias for \ref smartcard_irp_device_control_decode_request. ++ * ++ * Kept for backward compatibility; new code should use the _request variant. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_irp_device_control_decode(wStream* s, UINT32 CompletionId, + UINT32 FileId, + SMARTCARD_OPERATION* operation); + ++ /** @brief Decode a smartcard IOCTL request received from the client. ++ * ++ * Parses the Device Control Request fields (OutputBufferLength, InputBufferLength, ++ * IoControlCode, Padding), then unpacks the Common Type Header, Private Type Header, ++ * and the IOCTL-specific call payload into \p operation->call. ++ * ++ * @param s Stream positioned at OutputBufferLength, after the DeviceIoRequest header. ++ * @param CompletionId The CompletionId from the DeviceIoRequest header. ++ * @param FileId The FileId from the DeviceIoRequest header. ++ * @param operation [out] The decoded ioControlCode, call union, and context handles. ++ * @return \b SCARD_S_SUCCESS on success, a smartcard error code on failure. ++ * ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_irp_device_control_decode_request(wStream* s, UINT32 CompletionId, ++ UINT32 FileId, ++ SMARTCARD_OPERATION* operation); ++ ++ /** @brief Free resources held by a SMARTCARD_OPERATION. ++ * ++ * Releases any memory allocated inside the call and ret unions (e.g., reader name ++ * strings, reader state arrays, receive buffers). ++ * ++ * @param op The operation to clean up. May be NULL (no-op). ++ * @param allocated If TRUE, also frees the SMARTCARD_OPERATION struct itself. ++ */ + FREERDP_API void smartcard_operation_free(SMARTCARD_OPERATION* op, BOOL allocated); + + #ifdef __cplusplus +diff --git a/libfreerdp/utils/smartcard_operations.c b/libfreerdp/utils/smartcard_operations.c +index dfbdfeacb08c..a56e4e23c3f0 100644 +--- a/libfreerdp/utils/smartcard_operations.c ++++ b/libfreerdp/utils/smartcard_operations.c +@@ -55,7 +55,7 @@ static LONG smartcard_call_to_operation_handle(SMARTCARD_OPERATION* operation) + return SCARD_S_SUCCESS; + } + +-static LONG smartcard_EstablishContext_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_EstablishContext_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -71,7 +71,7 @@ static LONG smartcard_EstablishContext_Decode(wStream* s, SMARTCARD_OPERATION* o + return SCARD_S_SUCCESS; + } + +-static LONG smartcard_ReleaseContext_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_ReleaseContext_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -85,7 +85,7 @@ static LONG smartcard_ReleaseContext_Decode(wStream* s, SMARTCARD_OPERATION* ope + return status; + } + +-static LONG smartcard_IsValidContext_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_IsValidContext_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -97,7 +97,7 @@ static LONG smartcard_IsValidContext_Decode(wStream* s, SMARTCARD_OPERATION* ope + return status; + } + +-static LONG smartcard_ListReaderGroupsA_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_ListReaderGroupsA_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -109,7 +109,7 @@ static LONG smartcard_ListReaderGroupsA_Decode(wStream* s, SMARTCARD_OPERATION* + return status; + } + +-static LONG smartcard_ListReaderGroupsW_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_ListReaderGroupsW_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -121,7 +121,7 @@ static LONG smartcard_ListReaderGroupsW_Decode(wStream* s, SMARTCARD_OPERATION* + return status; + } + +-static LONG smartcard_ListReadersA_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_ListReadersA_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -133,7 +133,7 @@ static LONG smartcard_ListReadersA_Decode(wStream* s, SMARTCARD_OPERATION* opera + return status; + } + +-static LONG smartcard_ListReadersW_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_ListReadersW_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -145,7 +145,8 @@ static LONG smartcard_ListReadersW_Decode(wStream* s, SMARTCARD_OPERATION* opera + return status; + } + +-static LONG smartcard_context_and_two_strings_a_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_ContextAndTwoStringA_decode_request(wStream* s, ++ SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -158,7 +159,8 @@ static LONG smartcard_context_and_two_strings_a_Decode(wStream* s, SMARTCARD_OPE + return status; + } + +-static LONG smartcard_context_and_two_strings_w_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_ContextAndTwoStringW_decode_request(wStream* s, ++ SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -171,7 +173,7 @@ static LONG smartcard_context_and_two_strings_w_Decode(wStream* s, SMARTCARD_OPE + return status; + } + +-static LONG smartcard_context_and_string_a_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_ContextAndStringA_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -183,7 +185,7 @@ static LONG smartcard_context_and_string_a_Decode(wStream* s, SMARTCARD_OPERATIO + return status; + } + +-static LONG smartcard_context_and_string_w_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_ContextAndStringW_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -195,7 +197,7 @@ static LONG smartcard_context_and_string_w_Decode(wStream* s, SMARTCARD_OPERATIO + return status; + } + +-static LONG smartcard_LocateCardsA_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_LocateCardsA_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -207,7 +209,7 @@ static LONG smartcard_LocateCardsA_Decode(wStream* s, SMARTCARD_OPERATION* opera + return status; + } + +-static LONG smartcard_LocateCardsW_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_LocateCardsW_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -219,7 +221,7 @@ static LONG smartcard_LocateCardsW_Decode(wStream* s, SMARTCARD_OPERATION* opera + return status; + } + +-static LONG smartcard_GetStatusChangeA_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_GetStatusChangeA_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + WINPR_ASSERT(s); + WINPR_ASSERT(operation); +@@ -227,7 +229,7 @@ static LONG smartcard_GetStatusChangeA_Decode(wStream* s, SMARTCARD_OPERATION* o + return smartcard_unpack_get_status_change_a_call(s, &operation->call.getStatusChangeA); + } + +-static LONG smartcard_GetStatusChangeW_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_GetStatusChangeW_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + WINPR_ASSERT(s); + WINPR_ASSERT(operation); +@@ -235,7 +237,7 @@ static LONG smartcard_GetStatusChangeW_Decode(wStream* s, SMARTCARD_OPERATION* o + return smartcard_unpack_get_status_change_w_call(s, &operation->call.getStatusChangeW); + } + +-static LONG smartcard_Cancel_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_Cancel_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -247,7 +249,7 @@ static LONG smartcard_Cancel_Decode(wStream* s, SMARTCARD_OPERATION* operation) + return status; + } + +-static LONG smartcard_ConnectA_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_ConnectA_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -259,7 +261,7 @@ static LONG smartcard_ConnectA_Decode(wStream* s, SMARTCARD_OPERATION* operation + return status; + } + +-static LONG smartcard_ConnectW_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_ConnectW_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -271,7 +273,7 @@ static LONG smartcard_ConnectW_Decode(wStream* s, SMARTCARD_OPERATION* operation + return status; + } + +-static LONG smartcard_Reconnect_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_Reconnect_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -283,7 +285,7 @@ static LONG smartcard_Reconnect_Decode(wStream* s, SMARTCARD_OPERATION* operatio + return status; + } + +-static LONG smartcard_Disconnect_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_Disconnect_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -296,7 +298,7 @@ static LONG smartcard_Disconnect_Decode(wStream* s, SMARTCARD_OPERATION* operati + return status; + } + +-static LONG smartcard_BeginTransaction_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_BeginTransaction_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -309,7 +311,7 @@ static LONG smartcard_BeginTransaction_Decode(wStream* s, SMARTCARD_OPERATION* o + return status; + } + +-static LONG smartcard_EndTransaction_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_EndTransaction_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -322,7 +324,7 @@ static LONG smartcard_EndTransaction_Decode(wStream* s, SMARTCARD_OPERATION* ope + return status; + } + +-static LONG smartcard_State_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_State_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -334,7 +336,7 @@ static LONG smartcard_State_Decode(wStream* s, SMARTCARD_OPERATION* operation) + return status; + } + +-static LONG smartcard_StatusA_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_StatusA_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -346,7 +348,7 @@ static LONG smartcard_StatusA_Decode(wStream* s, SMARTCARD_OPERATION* operation) + return status; + } + +-static LONG smartcard_StatusW_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_StatusW_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -358,7 +360,7 @@ static LONG smartcard_StatusW_Decode(wStream* s, SMARTCARD_OPERATION* operation) + return status; + } + +-static LONG smartcard_Transmit_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_Transmit_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -370,7 +372,7 @@ static LONG smartcard_Transmit_Decode(wStream* s, SMARTCARD_OPERATION* operation + return status; + } + +-static LONG smartcard_Control_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_Control_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -382,7 +384,7 @@ static LONG smartcard_Control_Decode(wStream* s, SMARTCARD_OPERATION* operation) + return status; + } + +-static LONG smartcard_GetAttrib_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_GetAttrib_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -394,7 +396,7 @@ static LONG smartcard_GetAttrib_Decode(wStream* s, SMARTCARD_OPERATION* operatio + return status; + } + +-static LONG smartcard_SetAttrib_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_SetAttrib_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -406,7 +408,7 @@ static LONG smartcard_SetAttrib_Decode(wStream* s, SMARTCARD_OPERATION* operatio + return status; + } + +-static LONG smartcard_AccessStartedEvent_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_AccessStartedEvent_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + WINPR_ASSERT(s); + WINPR_ASSERT(operation); +@@ -419,7 +421,7 @@ static LONG smartcard_AccessStartedEvent_Decode(wStream* s, SMARTCARD_OPERATION* + return SCARD_S_SUCCESS; + } + +-static LONG smartcard_LocateCardsByATRA_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_LocateCardsByATRA_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -431,7 +433,7 @@ static LONG smartcard_LocateCardsByATRA_Decode(wStream* s, SMARTCARD_OPERATION* + return status; + } + +-static LONG smartcard_LocateCardsByATRW_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_LocateCardsByATRW_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -443,7 +445,7 @@ static LONG smartcard_LocateCardsByATRW_Decode(wStream* s, SMARTCARD_OPERATION* + return status; + } + +-static LONG smartcard_ReadCacheA_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_ReadCacheA_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -455,7 +457,7 @@ static LONG smartcard_ReadCacheA_Decode(wStream* s, SMARTCARD_OPERATION* operati + return status; + } + +-static LONG smartcard_ReadCacheW_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_ReadCacheW_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -467,7 +469,7 @@ static LONG smartcard_ReadCacheW_Decode(wStream* s, SMARTCARD_OPERATION* operati + return status; + } + +-static LONG smartcard_WriteCacheA_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_WriteCacheA_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -479,7 +481,7 @@ static LONG smartcard_WriteCacheA_Decode(wStream* s, SMARTCARD_OPERATION* operat + return status; + } + +-static LONG smartcard_WriteCacheW_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_WriteCacheW_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -491,7 +493,7 @@ static LONG smartcard_WriteCacheW_Decode(wStream* s, SMARTCARD_OPERATION* operat + return status; + } + +-static LONG smartcard_GetTransmitCount_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_GetTransmitCount_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -503,7 +505,7 @@ static LONG smartcard_GetTransmitCount_Decode(wStream* s, SMARTCARD_OPERATION* o + return status; + } + +-static LONG smartcard_ReleaseStartedEvent_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_ReleaseStartedEvent_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + WINPR_UNUSED(s); + WINPR_UNUSED(operation); +@@ -512,7 +514,7 @@ static LONG smartcard_ReleaseStartedEvent_Decode(wStream* s, SMARTCARD_OPERATION + return SCARD_E_UNSUPPORTED_FEATURE; + } + +-static LONG smartcard_GetReaderIcon_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_GetReaderIcon_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -524,7 +526,7 @@ static LONG smartcard_GetReaderIcon_Decode(wStream* s, SMARTCARD_OPERATION* oper + return status; + } + +-static LONG smartcard_GetDeviceTypeId_Decode(wStream* s, SMARTCARD_OPERATION* operation) ++static LONG smartcard_GetDeviceTypeId_decode_request(wStream* s, SMARTCARD_OPERATION* operation) + { + LONG status = 0; + +@@ -538,6 +540,12 @@ static LONG smartcard_GetDeviceTypeId_Decode(wStream* s, SMARTCARD_OPERATION* op + + LONG smartcard_irp_device_control_decode(wStream* s, UINT32 CompletionId, UINT32 FileId, + SMARTCARD_OPERATION* operation) ++{ ++ return smartcard_irp_device_control_decode_request(s, CompletionId, FileId, operation); ++} ++ ++LONG smartcard_irp_device_control_decode_request(wStream* s, UINT32 CompletionId, UINT32 FileId, ++ SMARTCARD_OPERATION* operation) + { + LONG status = 0; + UINT32 offset = 0; +@@ -584,195 +592,195 @@ LONG smartcard_irp_device_control_decode(wStream* s, UINT32 CompletionId, UINT32 + switch (ioControlCode) + { + case SCARD_IOCTL_ESTABLISHCONTEXT: +- status = smartcard_EstablishContext_Decode(s, operation); ++ status = smartcard_EstablishContext_decode_request(s, operation); + break; + + case SCARD_IOCTL_RELEASECONTEXT: +- status = smartcard_ReleaseContext_Decode(s, operation); ++ status = smartcard_ReleaseContext_decode_request(s, operation); + break; + + case SCARD_IOCTL_ISVALIDCONTEXT: +- status = smartcard_IsValidContext_Decode(s, operation); ++ status = smartcard_IsValidContext_decode_request(s, operation); + break; + + case SCARD_IOCTL_LISTREADERGROUPSA: +- status = smartcard_ListReaderGroupsA_Decode(s, operation); ++ status = smartcard_ListReaderGroupsA_decode_request(s, operation); + break; + + case SCARD_IOCTL_LISTREADERGROUPSW: +- status = smartcard_ListReaderGroupsW_Decode(s, operation); ++ status = smartcard_ListReaderGroupsW_decode_request(s, operation); + break; + + case SCARD_IOCTL_LISTREADERSA: +- status = smartcard_ListReadersA_Decode(s, operation); ++ status = smartcard_ListReadersA_decode_request(s, operation); + break; + + case SCARD_IOCTL_LISTREADERSW: +- status = smartcard_ListReadersW_Decode(s, operation); ++ status = smartcard_ListReadersW_decode_request(s, operation); + break; + + case SCARD_IOCTL_INTRODUCEREADERGROUPA: +- status = smartcard_context_and_string_a_Decode(s, operation); ++ status = smartcard_ContextAndStringA_decode_request(s, operation); + break; + + case SCARD_IOCTL_INTRODUCEREADERGROUPW: +- status = smartcard_context_and_string_w_Decode(s, operation); ++ status = smartcard_ContextAndStringW_decode_request(s, operation); + break; + + case SCARD_IOCTL_FORGETREADERGROUPA: +- status = smartcard_context_and_string_a_Decode(s, operation); ++ status = smartcard_ContextAndStringA_decode_request(s, operation); + break; + + case SCARD_IOCTL_FORGETREADERGROUPW: +- status = smartcard_context_and_string_w_Decode(s, operation); ++ status = smartcard_ContextAndStringW_decode_request(s, operation); + break; + + case SCARD_IOCTL_INTRODUCEREADERA: +- status = smartcard_context_and_two_strings_a_Decode(s, operation); ++ status = smartcard_ContextAndTwoStringA_decode_request(s, operation); + break; + + case SCARD_IOCTL_INTRODUCEREADERW: +- status = smartcard_context_and_two_strings_w_Decode(s, operation); ++ status = smartcard_ContextAndTwoStringW_decode_request(s, operation); + break; + + case SCARD_IOCTL_FORGETREADERA: +- status = smartcard_context_and_string_a_Decode(s, operation); ++ status = smartcard_ContextAndStringA_decode_request(s, operation); + break; + + case SCARD_IOCTL_FORGETREADERW: +- status = smartcard_context_and_string_w_Decode(s, operation); ++ status = smartcard_ContextAndStringW_decode_request(s, operation); + break; + + case SCARD_IOCTL_ADDREADERTOGROUPA: +- status = smartcard_context_and_two_strings_a_Decode(s, operation); ++ status = smartcard_ContextAndTwoStringA_decode_request(s, operation); + break; + + case SCARD_IOCTL_ADDREADERTOGROUPW: +- status = smartcard_context_and_two_strings_w_Decode(s, operation); ++ status = smartcard_ContextAndTwoStringW_decode_request(s, operation); + break; + + case SCARD_IOCTL_REMOVEREADERFROMGROUPA: +- status = smartcard_context_and_two_strings_a_Decode(s, operation); ++ status = smartcard_ContextAndTwoStringA_decode_request(s, operation); + break; + + case SCARD_IOCTL_REMOVEREADERFROMGROUPW: +- status = smartcard_context_and_two_strings_w_Decode(s, operation); ++ status = smartcard_ContextAndTwoStringW_decode_request(s, operation); + break; + + case SCARD_IOCTL_LOCATECARDSA: +- status = smartcard_LocateCardsA_Decode(s, operation); ++ status = smartcard_LocateCardsA_decode_request(s, operation); + break; + + case SCARD_IOCTL_LOCATECARDSW: +- status = smartcard_LocateCardsW_Decode(s, operation); ++ status = smartcard_LocateCardsW_decode_request(s, operation); + break; + + case SCARD_IOCTL_GETSTATUSCHANGEA: +- status = smartcard_GetStatusChangeA_Decode(s, operation); ++ status = smartcard_GetStatusChangeA_decode_request(s, operation); + break; + + case SCARD_IOCTL_GETSTATUSCHANGEW: +- status = smartcard_GetStatusChangeW_Decode(s, operation); ++ status = smartcard_GetStatusChangeW_decode_request(s, operation); + break; + + case SCARD_IOCTL_CANCEL: +- status = smartcard_Cancel_Decode(s, operation); ++ status = smartcard_Cancel_decode_request(s, operation); + break; + + case SCARD_IOCTL_CONNECTA: +- status = smartcard_ConnectA_Decode(s, operation); ++ status = smartcard_ConnectA_decode_request(s, operation); + break; + + case SCARD_IOCTL_CONNECTW: +- status = smartcard_ConnectW_Decode(s, operation); ++ status = smartcard_ConnectW_decode_request(s, operation); + break; + + case SCARD_IOCTL_RECONNECT: +- status = smartcard_Reconnect_Decode(s, operation); ++ status = smartcard_Reconnect_decode_request(s, operation); + break; + + case SCARD_IOCTL_DISCONNECT: +- status = smartcard_Disconnect_Decode(s, operation); ++ status = smartcard_Disconnect_decode_request(s, operation); + break; + + case SCARD_IOCTL_BEGINTRANSACTION: +- status = smartcard_BeginTransaction_Decode(s, operation); ++ status = smartcard_BeginTransaction_decode_request(s, operation); + break; + + case SCARD_IOCTL_ENDTRANSACTION: +- status = smartcard_EndTransaction_Decode(s, operation); ++ status = smartcard_EndTransaction_decode_request(s, operation); + break; + + case SCARD_IOCTL_STATE: +- status = smartcard_State_Decode(s, operation); ++ status = smartcard_State_decode_request(s, operation); + break; + + case SCARD_IOCTL_STATUSA: +- status = smartcard_StatusA_Decode(s, operation); ++ status = smartcard_StatusA_decode_request(s, operation); + break; + + case SCARD_IOCTL_STATUSW: +- status = smartcard_StatusW_Decode(s, operation); ++ status = smartcard_StatusW_decode_request(s, operation); + break; + + case SCARD_IOCTL_TRANSMIT: +- status = smartcard_Transmit_Decode(s, operation); ++ status = smartcard_Transmit_decode_request(s, operation); + break; + + case SCARD_IOCTL_CONTROL: +- status = smartcard_Control_Decode(s, operation); ++ status = smartcard_Control_decode_request(s, operation); + break; + + case SCARD_IOCTL_GETATTRIB: +- status = smartcard_GetAttrib_Decode(s, operation); ++ status = smartcard_GetAttrib_decode_request(s, operation); + break; + + case SCARD_IOCTL_SETATTRIB: +- status = smartcard_SetAttrib_Decode(s, operation); ++ status = smartcard_SetAttrib_decode_request(s, operation); + break; + + case SCARD_IOCTL_ACCESSSTARTEDEVENT: +- status = smartcard_AccessStartedEvent_Decode(s, operation); ++ status = smartcard_AccessStartedEvent_decode_request(s, operation); + break; + + case SCARD_IOCTL_LOCATECARDSBYATRA: +- status = smartcard_LocateCardsByATRA_Decode(s, operation); ++ status = smartcard_LocateCardsByATRA_decode_request(s, operation); + break; + + case SCARD_IOCTL_LOCATECARDSBYATRW: +- status = smartcard_LocateCardsByATRW_Decode(s, operation); ++ status = smartcard_LocateCardsByATRW_decode_request(s, operation); + break; + + case SCARD_IOCTL_READCACHEA: +- status = smartcard_ReadCacheA_Decode(s, operation); ++ status = smartcard_ReadCacheA_decode_request(s, operation); + break; + + case SCARD_IOCTL_READCACHEW: +- status = smartcard_ReadCacheW_Decode(s, operation); ++ status = smartcard_ReadCacheW_decode_request(s, operation); + break; + + case SCARD_IOCTL_WRITECACHEA: +- status = smartcard_WriteCacheA_Decode(s, operation); ++ status = smartcard_WriteCacheA_decode_request(s, operation); + break; + + case SCARD_IOCTL_WRITECACHEW: +- status = smartcard_WriteCacheW_Decode(s, operation); ++ status = smartcard_WriteCacheW_decode_request(s, operation); + break; + + case SCARD_IOCTL_GETTRANSMITCOUNT: +- status = smartcard_GetTransmitCount_Decode(s, operation); ++ status = smartcard_GetTransmitCount_decode_request(s, operation); + break; + + case SCARD_IOCTL_RELEASETARTEDEVENT: +- status = smartcard_ReleaseStartedEvent_Decode(s, operation); ++ status = smartcard_ReleaseStartedEvent_decode_request(s, operation); + break; + + case SCARD_IOCTL_GETREADERICON: +- status = smartcard_GetReaderIcon_Decode(s, operation); ++ status = smartcard_GetReaderIcon_decode_request(s, operation); + break; + + case SCARD_IOCTL_GETDEVICETYPEID: +- status = smartcard_GetDeviceTypeId_Decode(s, operation); ++ status = smartcard_GetDeviceTypeId_decode_request(s, operation); + break; + + default: +diff --git a/server/proxy/channels/pf_channel_smartcard.c b/server/proxy/channels/pf_channel_smartcard.c +index 3bd9360bb0c7..a69dc55b9250 100644 +--- a/server/proxy/channels/pf_channel_smartcard.c ++++ b/server/proxy/channels/pf_channel_smartcard.c +@@ -214,7 +214,7 @@ BOOL pf_channel_smartcard_client_handle(wLog* log, pClientContext* pc, wStream* + return FALSE; + } + +- status = smartcard_irp_device_control_decode(s, CompletionId, FileId, &e.op); ++ status = smartcard_irp_device_control_decode_request(s, CompletionId, FileId, &e.op); + if (status != 0) + goto fail; + + +From 6efff92ae960647bd48afb1c0e913610a1768f81 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Thu, 14 May 2026 13:02:38 +0200 +Subject: [PATCH 05/31] [utils,smartcard] remove extra line breaks in + smartcard_operation_free + +In the switch some entries had line breaks and others didn't. +This ensures there's a consistent style. +--- + libfreerdp/utils/smartcard_operations.c | 9 --------- + 1 file changed, 9 deletions(-) + +diff --git a/libfreerdp/utils/smartcard_operations.c b/libfreerdp/utils/smartcard_operations.c +index a56e4e23c3f0..61c5761a41f6 100644 +--- a/libfreerdp/utils/smartcard_operations.c ++++ b/libfreerdp/utils/smartcard_operations.c +@@ -871,7 +871,6 @@ void smartcard_operation_free(SMARTCARD_OPERATION* op, BOOL allocated) + case SCARD_IOCTL_GETATTRIB: + case SCARD_IOCTL_GETTRANSMITCOUNT: + break; +- + case SCARD_IOCTL_LOCATECARDSA: + { + LocateCardsA_Call* call = &op->call.locateCardsA; +@@ -888,7 +887,6 @@ void smartcard_operation_free(SMARTCARD_OPERATION* op, BOOL allocated) + free_reader_states_w(call->rgReaderStates, call->cReaders); + } + break; +- + case SCARD_IOCTL_LOCATECARDSBYATRA: + { + LocateCardsByATRA_Call* call = &op->call.locateCardsByATRA; +@@ -910,7 +908,6 @@ void smartcard_operation_free(SMARTCARD_OPERATION* op, BOOL allocated) + free(call->sz); + } + break; +- + case SCARD_IOCTL_FORGETREADERW: + case SCARD_IOCTL_INTRODUCEREADERGROUPW: + case SCARD_IOCTL_FORGETREADERGROUPW: +@@ -919,29 +916,24 @@ void smartcard_operation_free(SMARTCARD_OPERATION* op, BOOL allocated) + free(call->sz); + } + break; +- + case SCARD_IOCTL_INTRODUCEREADERA: + case SCARD_IOCTL_REMOVEREADERFROMGROUPA: + case SCARD_IOCTL_ADDREADERTOGROUPA: +- + { + ContextAndTwoStringA_Call* call = &op->call.contextAndTwoStringA; + free(call->sz1); + free(call->sz2); + } + break; +- + case SCARD_IOCTL_INTRODUCEREADERW: + case SCARD_IOCTL_REMOVEREADERFROMGROUPW: + case SCARD_IOCTL_ADDREADERTOGROUPW: +- + { + ContextAndTwoStringW_Call* call = &op->call.contextAndTwoStringW; + free(call->sz1); + free(call->sz2); + } + break; +- + case SCARD_IOCTL_LISTREADERSA: + case SCARD_IOCTL_LISTREADERSW: + { +@@ -955,7 +947,6 @@ void smartcard_operation_free(SMARTCARD_OPERATION* op, BOOL allocated) + free_reader_states_a(call->rgReaderStates, call->cReaders); + } + break; +- + case SCARD_IOCTL_GETSTATUSCHANGEW: + { + GetStatusChangeW_Call* call = &op->call.getStatusChangeW; + +From ed6f3bc1306313f94acc694d60686f2278a3fdb5 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Wed, 13 May 2026 12:34:46 +0200 +Subject: [PATCH 06/31] [utils,smartcard] document smartcard_pack.h public API + +Add comments to all public functions describing parameters, return +values and stream positioning, preparing for additional unpack methods. + +Co-Authored-By: Claude +--- + include/freerdp/utils/smartcard_pack.h | 315 ++++++++++++++++++++++++- + 1 file changed, 313 insertions(+), 2 deletions(-) + +diff --git a/include/freerdp/utils/smartcard_pack.h b/include/freerdp/utils/smartcard_pack.h +index 4dc31b4a1a64..96cae2387abb 100644 +--- a/include/freerdp/utils/smartcard_pack.h ++++ b/include/freerdp/utils/smartcard_pack.h +@@ -39,197 +39,508 @@ extern "C" + { + #endif + ++ /** @brief Write adding bytes to align to \p alignment. ++ * @param s Stream to write padding into. ++ * @param size Number of data bytes written so far. ++ * @param alignment Required alignment boundary. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_write_size_align(wStream* s, size_t size, UINT32 alignment); + ++ /** @brief Read and skip padding bytes to align to \p alignment. ++ * @param s Stream to read padding from. ++ * @param size Number of data bytes read so far. ++ * @param alignment Required alignment boundary. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_read_size_align(wStream* s, size_t size, UINT32 alignment); + ++ /** @brief Convert a REDIR_SCARDCONTEXT to a native SCARDCONTEXT. ++ * @param context The redirected context to convert. ++ * @return The native SCARDCONTEXT value. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API SCARDCONTEXT smartcard_scard_context_native_from_redir(REDIR_SCARDCONTEXT* context); ++ ++ /** @brief Convert a native SCARDCONTEXT to a REDIR_SCARDCONTEXT. ++ * @param context [out] The redirected context to populate. ++ * @param hContext The native SCARDCONTEXT value. ++ */ + FREERDP_API void smartcard_scard_context_native_to_redir(REDIR_SCARDCONTEXT* context, + SCARDCONTEXT hContext); + ++ /** @brief Convert a REDIR_SCARDHANDLE to a native SCARDHANDLE. ++ * @param handle The redirected handle to convert. ++ * @return The native SCARDHANDLE value. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API SCARDHANDLE smartcard_scard_handle_native_from_redir(REDIR_SCARDHANDLE* handle); ++ ++ /** @brief Convert a native SCARDHANDLE to a REDIR_SCARDHANDLE. ++ * @param handle [out] The redirected handle to populate. ++ * @param hCard The native SCARDHANDLE value. ++ */ + FREERDP_API void smartcard_scard_handle_native_to_redir(REDIR_SCARDHANDLE* handle, + SCARDHANDLE hCard); + ++ /** @brief Unpack the Common Type Header [MS-RPCE] from a stream. ++ * @param s Stream to read from. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_common_type_header(wStream* s); ++ ++ /** @brief Pack the Common Type Header [MS-RPCE] into a stream. ++ * @param s Stream to write into. ++ */ + FREERDP_API void smartcard_pack_common_type_header(wStream* s); + ++ /** @brief Unpack the Private Type Header [MS-RPCE] from a stream. ++ * @param s Stream to read from. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_private_type_header(wStream* s); ++ ++ /** @brief Pack the Private Type Header [MS-RPCE] into a stream. ++ * @param s Stream to write into. ++ * @param objectBufferLength Length of the payload following this header. ++ */ + FREERDP_API void smartcard_pack_private_type_header(wStream* s, UINT32 objectBufferLength); + ++ /** @brief Unpack an EstablishContext_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded EstablishContext_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_establish_context_call(wStream* s, + EstablishContext_Call* call); + ++ /** @brief Pack an EstablishContext_Return into a stream. ++ * @param s Stream to write into after the Common/Private Type Headers and the ReturnCode. ++ * @param ret The EstablishContext_Return to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_establish_context_return(wStream* s, + const EstablishContext_Return* ret); + ++ /** @brief Unpack a Context_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded Context_Call. ++ * @param name Operation name used for trace logging. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_context_call(wStream* s, Context_Call* call, + const char* name); + ++ /** @brief Log a Long_Return for tracing purposes. ++ * @param ret The Long_Return to trace. ++ * @param name Operation name used for the log message. ++ */ + FREERDP_API void smartcard_trace_long_return(const Long_Return* ret, const char* name); + ++ /** @brief Unpack a ListReaderGroups_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded ListReaderGroups_Call. ++ * @param unicode TRUE for wide-char (W) variant, FALSE for ANSI (A). ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_list_reader_groups_call(wStream* s, + ListReaderGroups_Call* call, + BOOL unicode); + ++ /** @brief Pack a ListReaderGroups_Return into a stream. ++ * @param s Stream positioned after the the Common/Private Type Headers and the ++ * ReturnCode. ++ * @param ret The ListReaderGroups_Return to encode. ++ * @param unicode TRUE for wide-char (W) variant, FALSE for ANSI (A). ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_list_reader_groups_return(wStream* s, + const ListReaderGroups_Return* ret, + BOOL unicode); + ++ /** @brief Unpack a ListReaders_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded ListReaders_Call. ++ * @param unicode TRUE for wide-char (W) variant, FALSE for ANSI (A). ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_list_readers_call(wStream* s, ListReaders_Call* call, + BOOL unicode); + ++ /** @brief Pack a ListReaders_Return into a stream. ++ * @param s Stream to write into after the Common/Private Type Headers and the ++ * ReturnCode. ++ * @param ret The ListReaders_Return to encode. ++ * @param unicode TRUE for wide-char (W) variant, FALSE for ANSI (A). ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_list_readers_return(wStream* s, const ListReaders_Return* ret, + BOOL unicode); + ++ /** @brief Unpack a ContextAndTwoStringA_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded ContextAndTwoStringA_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG + smartcard_unpack_context_and_two_strings_a_call(wStream* s, ContextAndTwoStringA_Call* call); + ++ /** @brief Unpack a ContextAndTwoStringW_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded ContextAndTwoStringW_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG + smartcard_unpack_context_and_two_strings_w_call(wStream* s, ContextAndTwoStringW_Call* call); + ++ /** @brief Unpack a ContextAndStringA_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded ContextAndStringA_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_context_and_string_a_call(wStream* s, + ContextAndStringA_Call* call); + ++ /** @brief Unpack a ContextAndStringW_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded ContextAndStringW_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_context_and_string_w_call(wStream* s, + ContextAndStringW_Call* call); + ++ /** @brief Unpack a LocateCardsA_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded LocateCardsA_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_locate_cards_a_call(wStream* s, LocateCardsA_Call* call); + ++ /** @brief Pack a LocateCards_Return into a stream. ++ * @param s Stream to write into after the Common/Private Type Headers and the ReturnCode. ++ * @param ret The LocateCards_Return to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_locate_cards_return(wStream* s, const LocateCards_Return* ret); + ++ /** @brief Unpack a LocateCardsW_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded LocateCardsW_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_locate_cards_w_call(wStream* s, LocateCardsW_Call* call); + ++ /** @brief Pack a LocateCardsW return into a stream. ++ * @param s Stream to write into after the Common/Private Type Headers and the ReturnCode. ++ * @param ret The LocateCards_Return containing the return data to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD +- FREERDP_API LONG smartcard_pack_locate_cards_w_return(wStream* s, const LocateCardsW_Call* ret); ++ FREERDP_API LONG smartcard_pack_locate_cards_w_return(wStream* s, ++ const LocateCards_Return* ret); + ++ /** @brief Unpack a ConnectA_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded ConnectA_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_connect_a_call(wStream* s, ConnectA_Call* call); + ++ /** @brief Unpack a ConnectW_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded ConnectW_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_connect_w_call(wStream* s, ConnectW_Call* call); + ++ /** @brief Pack a Connect_Return into a stream. ++ * @param s Stream to write into after the Common/Private Type Headers and the ReturnCode. ++ * @param ret The Connect_Return to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_connect_return(wStream* s, const Connect_Return* ret); + ++ /** @brief Unpack a Reconnect_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded Reconnect_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_reconnect_call(wStream* s, Reconnect_Call* call); + ++ /** @brief Pack a Reconnect_Return into a stream. ++ * @param s Stream to write into after the Common/Private Type Headers and the ReturnCode. ++ * @param ret The Reconnect_Return to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_reconnect_return(wStream* s, const Reconnect_Return* ret); + ++ /** @brief Unpack a HCardAndDisposition_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded HCardAndDisposition_Call. ++ * @param name Operation name used for trace logging. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_hcard_and_disposition_call(wStream* s, + HCardAndDisposition_Call* call, + const char* name); + ++ /** @brief Unpack a GetStatusChangeA_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded GetStatusChangeA_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_get_status_change_a_call(wStream* s, + GetStatusChangeA_Call* call); + ++ /** @brief Unpack a GetStatusChangeW_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded GetStatusChangeW_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_get_status_change_w_call(wStream* s, + GetStatusChangeW_Call* call); + ++ /** @brief Pack a GetStatusChange_Return into a stream. ++ * @param s Stream to write into after the Common/Private Type Headers and the ++ * ReturnCode. ++ * @param ret The GetStatusChange_Return to encode. ++ * @param unicode TRUE for wide-char (W) variant, FALSE for ANSI (A). ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_get_status_change_return(wStream* s, + const GetStatusChange_Return* ret, + BOOL unicode); + ++ /** @brief Unpack a State_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded State_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_state_call(wStream* s, State_Call* call); ++ ++ /** @brief Pack a State_Return into a stream. ++ * @param s Stream to write into after the Common/Private Type Headers and the ReturnCode. ++ * @param ret The State_Return to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_state_return(wStream* s, const State_Return* ret); + ++ /** @brief Unpack a Status_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded Status_Call. ++ * @param unicode TRUE for wide-char (W) variant, FALSE for ANSI (A). ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_status_call(wStream* s, Status_Call* call, BOOL unicode); + ++ /** @brief Pack a Status_Return into a stream. ++ * @param s Stream to write into after the Common/Private Type Headers and the ++ * ReturnCode. ++ * @param ret The Status_Return to encode. ++ * @param unicode TRUE for wide-char (W) variant, FALSE for ANSI (A). ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_status_return(wStream* s, const Status_Return* ret, + BOOL unicode); + ++ /** @brief Unpack a GetAttrib_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded GetAttrib_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_get_attrib_call(wStream* s, GetAttrib_Call* call); + ++ /** @brief Pack a GetAttrib_Return into a stream. ++ * @param s Stream to write into after the Common/Private Type Headers and the ++ * ReturnCode. ++ * @param ret The GetAttrib_Return to encode. ++ * @param dwAttrId The attribute ID from the original request. ++ * @param cbAttrCallLen The cbAttrLen from the original request. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_get_attrib_return(wStream* s, const GetAttrib_Return* ret, + DWORD dwAttrId, DWORD cbAttrCallLen); + ++ /** @brief Unpack a SetAttrib_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded SetAttrib_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_set_attrib_call(wStream* s, SetAttrib_Call* call); + ++ /** @brief Unpack a Control_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded Control_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_control_call(wStream* s, Control_Call* call); + ++ /** @brief Pack a Control_Return into a stream. ++ * @param s Stream to write into after the Common/Private Type Headers and the ReturnCode. ++ * @param ret The Control_Return to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_control_return(wStream* s, const Control_Return* ret); + ++ /** @brief Unpack a Transmit_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded Transmit_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_transmit_call(wStream* s, Transmit_Call* call); + ++ /** @brief Pack a Transmit_Return into a stream. ++ * @param s Stream to write into after the Common/Private Type Headers and the ReturnCode. ++ * @param ret The Transmit_Return to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_transmit_return(wStream* s, const Transmit_Return* ret); + ++ /** @brief Unpack a LocateCardsByATRA_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded LocateCardsByATRA_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_locate_cards_by_atr_a_call(wStream* s, + LocateCardsByATRA_Call* call); + ++ /** @brief Unpack a LocateCardsByATRW_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded LocateCardsByATRW_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_locate_cards_by_atr_w_call(wStream* s, + LocateCardsByATRW_Call* call); + ++ /** @brief Unpack a ReadCacheA_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded ReadCacheA_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_read_cache_a_call(wStream* s, ReadCacheA_Call* call); + ++ /** @brief Unpack a ReadCacheW_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded ReadCacheW_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_read_cache_w_call(wStream* s, ReadCacheW_Call* call); + ++ /** @brief Pack a ReadCache_Return into a stream. ++ * @param s Stream to write into after the Common/Private Type Headers and the ReturnCode. ++ * @param ret The ReadCache_Return to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_read_cache_return(wStream* s, const ReadCache_Return* ret); + ++ /** @brief Unpack a WriteCacheA_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded WriteCacheA_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_write_cache_a_call(wStream* s, WriteCacheA_Call* call); + ++ /** @brief Unpack a WriteCacheW_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded WriteCacheW_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_write_cache_w_call(wStream* s, WriteCacheW_Call* call); + ++ /** @brief Unpack a GetTransmitCount_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded GetTransmitCount_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_get_transmit_count_call(wStream* s, + GetTransmitCount_Call* call); ++ ++ /** @brief Pack a GetTransmitCount_Return into a stream. ++ * @param s Stream to write into after the Common/Private Type Headers and the ReturnCode. ++ * @param ret The GetTransmitCount_Return to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_get_transmit_count_return(wStream* s, +- const GetTransmitCount_Return* call); ++ const GetTransmitCount_Return* ret); + ++ /** @brief Unpack a GetReaderIcon_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded GetReaderIcon_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_get_reader_icon_call(wStream* s, GetReaderIcon_Call* call); ++ ++ /** @brief Pack a GetReaderIcon_Return into a stream. ++ * @param s Stream to write into after the Common/Private Type Headers and the ReturnCode. ++ * @param ret The GetReaderIcon_Return to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_get_reader_icon_return(wStream* s, + const GetReaderIcon_Return* ret); + ++ /** @brief Unpack a GetDeviceTypeId_Call from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call [out] The decoded GetDeviceTypeId_Call. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_get_device_type_id_call(wStream* s, + GetDeviceTypeId_Call* call); + ++ /** @brief Pack a GetDeviceTypeId_Return into a stream. ++ * @param s Stream to write into after the Common/Private Type Headers and the ReturnCode. ++ * @param ret The GetDeviceTypeId_Return to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ */ + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_device_type_id_return(wStream* s, + const GetDeviceTypeId_Return* ret); + +From 4374e3045325e5587ca18e35dca1e6fb14aa7382 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Fri, 12 Jun 2026 21:29:42 +0200 +Subject: [PATCH 07/31] [utils,smartcard] fix NDR write alignment to use byte + count instead of element count + +--- + libfreerdp/utils/smartcard_pack.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libfreerdp/utils/smartcard_pack.c b/libfreerdp/utils/smartcard_pack.c +index 83d1fbd4a384..b978e2fb8209 100644 +--- a/libfreerdp/utils/smartcard_pack.c ++++ b/libfreerdp/utils/smartcard_pack.c +@@ -286,7 +286,7 @@ static LONG smartcard_ndr_write(wStream* s, const BYTE* data, UINT32 size, UINT3 + Stream_Write(s, data, dataLen); + else + Stream_Zero(s, dataLen); +- return smartcard_pack_write_size_align(s, len, 4); ++ return smartcard_pack_write_size_align(s, dataLen, 4); + } + + static LONG smartcard_ndr_write_state(wStream* s, const ReaderState_Return* data, UINT32 size, + +From 24ff58507f09ca7d1c5ca806500433254d94b232 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Wed, 13 May 2026 12:34:46 +0200 +Subject: [PATCH 08/31] [utils,smartcard] add pack and unpack helpers for + MS-RDPESC messages + +Add pack_call and unpack_return functions for smartcard IOCTL messages +defined in the Smart Card Virtual Channel Extension (MS-RDPESC). +These messages are wrapped in device control requests and responses. + +Co-authored-by: David Fort +--- + include/freerdp/utils/smartcard_pack.h | 249 +++++++ + libfreerdp/utils/smartcard_pack.c | 961 ++++++++++++++++++++++++- + 2 files changed, 1206 insertions(+), 4 deletions(-) + +diff --git a/include/freerdp/utils/smartcard_pack.h b/include/freerdp/utils/smartcard_pack.h +index 96cae2387abb..040db784be39 100644 +--- a/include/freerdp/utils/smartcard_pack.h ++++ b/include/freerdp/utils/smartcard_pack.h +@@ -119,6 +119,16 @@ extern "C" + FREERDP_API LONG smartcard_unpack_establish_context_call(wStream* s, + EstablishContext_Call* call); + ++ /** @brief Pack an EstablishContext_Call into a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call The EstablishContext_Call to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_pack_establish_context_call(wStream* s, ++ const EstablishContext_Call* call); ++ + /** @brief Pack an EstablishContext_Return into a stream. + * @param s Stream to write into after the Common/Private Type Headers and the ReturnCode. + * @param ret The EstablishContext_Return to encode. +@@ -128,6 +138,16 @@ extern "C" + FREERDP_API LONG smartcard_pack_establish_context_return(wStream* s, + const EstablishContext_Return* ret); + ++ /** @brief Unpack an EstablishContext_Return from a stream. ++ * @param s Stream positioned after the the Common/Private Type Headers and the ReturnCode. ++ * @param ret [out] The decoded EstablishContext_Return. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_unpack_establish_context_return(wStream* s, ++ EstablishContext_Return* ret); ++ + /** @brief Unpack a Context_Call from a stream. + * @param s Stream positioned after the Common/Private Type Headers. + * @param call [out] The decoded Context_Call. +@@ -155,6 +175,18 @@ extern "C" + ListReaderGroups_Call* call, + BOOL unicode); + ++ /** @brief Pack a ListReaderGroups_Call into a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call The ListReaderGroups_Call to encode. ++ * @param unicode TRUE for wide-char (W) variant, FALSE for ANSI (A). ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_pack_list_reader_groups_call(wStream* s, ++ const ListReaderGroups_Call* call, ++ BOOL unicode); ++ + /** @brief Pack a ListReaderGroups_Return into a stream. + * @param s Stream positioned after the the Common/Private Type Headers and the + * ReturnCode. +@@ -167,6 +199,18 @@ extern "C" + const ListReaderGroups_Return* ret, + BOOL unicode); + ++ /** @brief Unpack a ListReaderGroups_Return from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers and the ReturnCode. ++ * @param ret [out] The decoded ListReaderGroups_Return. ++ * @param unicode TRUE for the W variant, FALSE for the A variant. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_unpack_list_reader_groups_return(wStream* s, ++ ListReaderGroups_Return* ret, ++ BOOL unicode); ++ + /** @brief Unpack a ListReaders_Call from a stream. + * @param s Stream positioned after the Common/Private Type Headers. + * @param call [out] The decoded ListReaders_Call. +@@ -281,6 +325,33 @@ extern "C" + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_connect_return(wStream* s, const Connect_Return* ret); + ++ /** @brief Unpack a Connect_Return from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers and the ReturnCode. ++ * @param ret [out] The decoded Connect_Return. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_unpack_connect_return(wStream* s, Connect_Return* ret); ++ ++ /** @brief Pack a Control_Call into a stream. ++ * @param s Stream to write into after the Common/Private Type Headers and the ReturnCode. ++ * @param call The Control_Call to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_pack_control_call(wStream* s, const Control_Call* call); ++ ++ /** @brief Unpack a Control_return from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param ret [out] the decoded Control_Return. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_unpack_control_return(wStream* s, Control_Return* ret); ++ + /** @brief Unpack a Reconnect_Call from a stream. + * @param s Stream positioned after the Common/Private Type Headers. + * @param call [out] The decoded Reconnect_Call. +@@ -289,6 +360,15 @@ extern "C" + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_reconnect_call(wStream* s, Reconnect_Call* call); + ++ /** @brief Pack a Reconnect_Call into a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call The Reconnect_Call to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_pack_reconnect_call(wStream* s, const Reconnect_Call* call); ++ + /** @brief Pack a Reconnect_Return into a stream. + * @param s Stream to write into after the Common/Private Type Headers and the ReturnCode. + * @param ret The Reconnect_Return to encode. +@@ -297,6 +377,15 @@ extern "C" + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_reconnect_return(wStream* s, const Reconnect_Return* ret); + ++ /** @brief Unpack a Reconnect_Return from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers and the ReturnCode. ++ * @param ret [out] The decoded Reconnect_Return. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_unpack_reconnect_return(wStream* s, Reconnect_Return* ret); ++ + /** @brief Unpack a HCardAndDisposition_Call from a stream. + * @param s Stream positioned after the Common/Private Type Headers. + * @param call [out] The decoded HCardAndDisposition_Call. +@@ -363,6 +452,16 @@ extern "C" + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_status_call(wStream* s, Status_Call* call, BOOL unicode); + ++ /** @brief Pack a Status_Call into a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call The Status_Call to encode. ++ * @param unicode TRUE for wide-char (W) variant, FALSE for ANSI (A). ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_pack_status_call(wStream* s, const Status_Call* call, BOOL unicode); ++ + /** @brief Pack a Status_Return into a stream. + * @param s Stream to write into after the Common/Private Type Headers and the + * ReturnCode. +@@ -374,6 +473,16 @@ extern "C" + FREERDP_API LONG smartcard_pack_status_return(wStream* s, const Status_Return* ret, + BOOL unicode); + ++ /** @brief Unpack a Status_Return from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers and the ReturnCode. ++ * @param ret [out] The decoded Status_Return. ++ * @param unicode TRUE for the W variant, FALSE for the A variant. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_unpack_status_return(wStream* s, Status_Return* ret, BOOL unicode); ++ + /** @brief Unpack a GetAttrib_Call from a stream. + * @param s Stream positioned after the Common/Private Type Headers. + * @param call [out] The decoded GetAttrib_Call. +@@ -382,6 +491,15 @@ extern "C" + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_get_attrib_call(wStream* s, GetAttrib_Call* call); + ++ /** @brief Pack a GetAttrib_Call to a stream. ++ * @param s wStream to write to. ++ * @param call The GetAttrib_Call to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_pack_get_attrib_call(wStream* s, const GetAttrib_Call* call); ++ + /** @brief Pack a GetAttrib_Return into a stream. + * @param s Stream to write into after the Common/Private Type Headers and the + * ReturnCode. +@@ -394,6 +512,15 @@ extern "C" + FREERDP_API LONG smartcard_pack_get_attrib_return(wStream* s, const GetAttrib_Return* ret, + DWORD dwAttrId, DWORD cbAttrCallLen); + ++ /** @brief Unpack a GetAttrib_Return from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers and the ReturnCode. ++ * @param ret [out] The decoded GetAttrib_Return. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_unpack_get_attrib_return(wStream* s, GetAttrib_Return* ret); ++ + /** @brief Unpack a SetAttrib_Call from a stream. + * @param s Stream positioned after the Common/Private Type Headers. + * @param call [out] The decoded SetAttrib_Call. +@@ -402,6 +529,15 @@ extern "C" + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_unpack_set_attrib_call(wStream* s, SetAttrib_Call* call); + ++ /** @brief Pack a SetAttrib_Call into a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call The SetAttrib_Call to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_pack_set_attrib_call(wStream* s, const SetAttrib_Call* call); ++ + /** @brief Unpack a Control_Call from a stream. + * @param s Stream positioned after the Common/Private Type Headers. + * @param call [out] The decoded Control_Call. +@@ -434,6 +570,15 @@ extern "C" + WINPR_ATTR_NODISCARD + FREERDP_API LONG smartcard_pack_transmit_return(wStream* s, const Transmit_Return* ret); + ++ /** @brief Unpack a Transmit_Return from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers and the ReturnCode. ++ * @param ret [out] The decoded Transmit_Return. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_unpack_transmit_return(wStream* s, Transmit_Return* ret); ++ + /** @brief Unpack a LocateCardsByATRA_Call from a stream. + * @param s Stream positioned after the Common/Private Type Headers. + * @param call [out] The decoded LocateCardsByATRA_Call. +@@ -545,6 +690,110 @@ extern "C" + FREERDP_API LONG smartcard_pack_device_type_id_return(wStream* s, + const GetDeviceTypeId_Return* ret); + ++ /** @brief Pack a Context_Call into a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call The Context_Call to encode. ++ * @param name Operation name used for trace logging. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_pack_context_call(wStream* s, const Context_Call* call, ++ const char* name); ++ ++ /** @brief Pack a ListReaders_Call into a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call The ListReaders_Call to encode. ++ * @param unicode TRUE for wide-char (W) variant, FALSE for ANSI (A). ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_pack_list_readers_call(wStream* s, const ListReaders_Call* call, ++ BOOL unicode); ++ ++ /** @brief Pack a GetStatusChangeA_Call into a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call The GetStatusChangeA_Call to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_pack_get_status_change_a_call(wStream* s, ++ const GetStatusChangeA_Call* call); ++ ++ /** @brief Pack a GetStatusChangeW_Call into a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call The GetStatusChangeW_Call to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_pack_get_status_change_w_call(wStream* s, ++ const GetStatusChangeW_Call* call); ++ ++ /** @brief Pack a ConnectA_Call into a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call The ConnectA_Call to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_pack_connect_a_call(wStream* s, const ConnectA_Call* call); ++ ++ /** @brief Pack a ConnectW_Call into a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call The ConnectW_Call to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_pack_connect_w_call(wStream* s, const ConnectW_Call* call); ++ ++ /** @brief Pack a HCardAndDisposition_Call into a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call The HCardAndDisposition_Call to encode. ++ * @param name Operation name used for trace logging. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_pack_hcard_and_disposition_call(wStream* s, ++ const HCardAndDisposition_Call* call, ++ const char* name); ++ ++ /** @brief Pack a Transmit_Call into a stream. ++ * @param s Stream positioned after the Common/Private Type Headers. ++ * @param call The Transmit_Call to encode. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_pack_transmit_call(wStream* s, const Transmit_Call* call); ++ ++ /** @brief Unpack a ListReaders_Return from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers and the ReturnCode. ++ * @param ret [out] The decoded ListReaders_Return. ++ * @param unicode TRUE for the W variant, FALSE for the A variant. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_unpack_list_readers_return(wStream* s, ListReaders_Return* ret, ++ BOOL unicode); ++ ++ /** @brief Unpack a GetStatusChange_Return from a stream. ++ * @param s Stream positioned after the Common/Private Type Headers and the ReturnCode. ++ * @param ret [out] The decoded GetStatusChange_Return. ++ * @param unicode TRUE for the W variant, FALSE for the A variant. ++ * @return \b SCARD_S_SUCCESS on success, an error code on failure. ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_unpack_get_status_change_return(wStream* s, ++ GetStatusChange_Return* ret, ++ BOOL unicode); ++ + #ifdef __cplusplus + } + #endif +diff --git a/libfreerdp/utils/smartcard_pack.c b/libfreerdp/utils/smartcard_pack.c +index b978e2fb8209..5aa54590f4fc 100644 +--- a/libfreerdp/utils/smartcard_pack.c ++++ b/libfreerdp/utils/smartcard_pack.c +@@ -303,6 +303,18 @@ static LONG smartcard_ndr_write_state(wStream* s, const ReaderState_Return* data + return smartcard_ndr_write(s, cnv.data, size, sizeof(ReaderState_Return), type); + } + ++static LONG smartcard_ndr_read_state(wLog* log, wStream* s, ReaderState_Return** data, size_t min, ++ ndr_ptr_t type) ++{ ++ union ++ { ++ ReaderState_Return** ppc; ++ BYTE** ppv; ++ } u; ++ u.ppc = data; ++ return smartcard_ndr_read(log, s, u.ppv, min, sizeof(ReaderState_Return), type); ++} ++ + static LONG smartcard_ndr_read_atrmask(wLog* log, wStream* s, LocateCards_ATRMask** data, + size_t min, ndr_ptr_t type) + { +@@ -382,6 +394,28 @@ static LONG smartcard_ndr_read_w(wLog* log, wStream* s, WCHAR** data, ndr_ptr_t + return smartcard_ndr_read(log, s, u.ppv, 0, sizeof(WCHAR), type); + } + ++static LONG smartcard_ndr_write_w(wStream* s, const WCHAR* data, UINT32 size, ndr_ptr_t type) ++{ ++ union ++ { ++ const WCHAR* wz; ++ const BYTE* bp; ++ } cnv; ++ cnv.wz = data; ++ return smartcard_ndr_write(s, cnv.bp, size, sizeof(WCHAR), type); ++} ++ ++static LONG smartcard_ndr_write_a(wStream* s, const CHAR* data, UINT32 size, ndr_ptr_t type) ++{ ++ union ++ { ++ const CHAR* sz; ++ const BYTE* bp; ++ } cnv; ++ cnv.sz = data; ++ return smartcard_ndr_write(s, cnv.bp, size, sizeof(CHAR), type); ++} ++ + static LONG smartcard_ndr_read_u(wLog* log, wStream* s, UUID** data) + { + union +@@ -546,7 +580,7 @@ static void smartcard_dump_array(wLog* log, DWORD level, WINPR_FORMAT_ARG const + va_start(ap, len); + winpr_vasprintf(&fprefix, &flen, prefix, ap); + va_end(ap); +- WLog_Print(log, level, "%s%s%s", prefix, buffer, postfix); ++ WLog_Print(log, level, "%s%s%s", fprefix, buffer, postfix); + free(buffer); + free(fprefix); + } +@@ -554,7 +588,7 @@ static void smartcard_dump_array(wLog* log, DWORD level, WINPR_FORMAT_ARG const + static void smartcard_log_redir_handle(wLog* log, const REDIR_SCARDHANDLE* pHandle) + { + WINPR_ASSERT(pHandle); +- smartcard_dump_array(log, g_LogLevel, " hContext: ", "", pHandle->pbHandle, pHandle->cbHandle); ++ smartcard_dump_array(log, g_LogLevel, "handle: ", "", pHandle->pbHandle, pHandle->cbHandle); + } + + static void smartcard_log_context(wLog* log, const REDIR_SCARDCONTEXT* phContext) +@@ -2041,6 +2075,21 @@ LONG smartcard_unpack_establish_context_call(wStream* s, EstablishContext_Call* + return SCARD_S_SUCCESS; + } + ++LONG smartcard_pack_establish_context_call(wStream* s, const EstablishContext_Call* call) ++{ ++ WINPR_ASSERT(call); ++ wLog* log = scard_log(); ++ ++ smartcard_trace_establish_context_call(log, call); ++ ++ if (!Stream_EnsureRemainingCapacity(s, 4)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_UINT32(s, call->dwScope); ++ ++ return SCARD_S_SUCCESS; ++} ++ + LONG smartcard_pack_establish_context_return(wStream* s, const EstablishContext_Return* ret) + { + WINPR_ASSERT(ret); +@@ -2059,6 +2108,26 @@ LONG smartcard_pack_establish_context_return(wStream* s, const EstablishContext_ + return smartcard_pack_redir_scard_context_ref(log, s, &(ret->hContext)); + } + ++LONG smartcard_unpack_establish_context_return(wStream* s, EstablishContext_Return* ret) ++{ ++ WINPR_ASSERT(ret); ++ wLog* log = scard_log(); ++ UINT32 index = 0; ++ UINT32 pbContextNdrPtr = 0; ++ ++ LONG status = ++ smartcard_unpack_redir_scard_context(log, s, &(ret->hContext), &index, &pbContextNdrPtr); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ status = smartcard_unpack_redir_scard_context_ref(log, s, pbContextNdrPtr, &(ret->hContext)); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ smartcard_trace_establish_context_return(log, ret); ++ return SCARD_S_SUCCESS; ++} ++ + LONG smartcard_unpack_context_call(wStream* s, Context_Call* call, const char* name) + { + UINT32 index = 0; +@@ -2110,6 +2179,28 @@ LONG smartcard_unpack_list_reader_groups_call(wStream* s, ListReaderGroups_Call* + return SCARD_S_SUCCESS; + } + ++LONG smartcard_pack_list_reader_groups_call(wStream* s, const ListReaderGroups_Call* call, ++ BOOL unicode) ++{ ++ WINPR_ASSERT(call); ++ wLog* log = scard_log(); ++ DWORD index = 0; ++ ++ smartcard_trace_list_reader_groups_call(log, call, unicode); ++ ++ LONG status = smartcard_pack_redir_scard_context(log, s, &call->handles.hContext, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 8)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_INT32(s, call->fmszGroupsIsNULL); ++ Stream_Write_UINT32(s, call->cchGroups); ++ ++ return smartcard_pack_redir_scard_context_ref(log, s, &call->handles.hContext); ++} ++ + LONG smartcard_pack_list_reader_groups_return(wStream* s, const ListReaderGroups_Return* ret, + BOOL unicode) + { +@@ -2138,6 +2229,33 @@ LONG smartcard_pack_list_reader_groups_return(wStream* s, const ListReaderGroups + return ret->ReturnCode; + } + ++LONG smartcard_unpack_list_reader_groups_return(wStream* s, ListReaderGroups_Return* ret, ++ BOOL unicode) ++{ ++ WINPR_ASSERT(ret); ++ wLog* log = scard_log(); ++ UINT32 index = 0; ++ UINT32 mszNdrPtr = 0; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 4)) ++ return STATUS_BUFFER_TOO_SMALL; ++ ++ Stream_Read_UINT32(s, ret->cBytes); ++ ++ if (!smartcard_ndr_pointer_read(log, s, &index, &mszNdrPtr)) ++ return ERROR_INVALID_DATA; ++ ++ if (mszNdrPtr) ++ { ++ LONG status = smartcard_ndr_read(log, s, &ret->msz, ret->cBytes, 1, NDR_PTR_SIMPLE); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ } ++ ++ smartcard_trace_list_reader_groups_return(log, ret, unicode); ++ return SCARD_S_SUCCESS; ++} ++ + LONG smartcard_unpack_list_readers_call(wStream* s, ListReaders_Call* call, BOOL unicode) + { + UINT32 index = 0; +@@ -2368,6 +2486,36 @@ LONG smartcard_unpack_reconnect_call(wStream* s, Reconnect_Call* call) + return status; + } + ++LONG smartcard_pack_reconnect_call(wStream* s, const Reconnect_Call* call) ++{ ++ WINPR_ASSERT(call); ++ wLog* log = scard_log(); ++ DWORD index = 0; ++ ++ smartcard_trace_reconnect_call(log, call); ++ ++ LONG status = smartcard_pack_redir_scard_context(log, s, &call->handles.hContext, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ status = smartcard_pack_redir_scard_handle(log, s, &call->handles.hCard, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 12)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_UINT32(s, call->dwShareMode); ++ Stream_Write_UINT32(s, call->dwPreferredProtocols); ++ Stream_Write_UINT32(s, call->dwInitialization); ++ ++ status = smartcard_pack_redir_scard_context_ref(log, s, &call->handles.hContext); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ return smartcard_pack_redir_scard_handle_ref(log, s, &call->handles.hCard); ++} ++ + LONG smartcard_pack_reconnect_return(wStream* s, const Reconnect_Return* ret) + { + WINPR_ASSERT(ret); +@@ -2380,6 +2528,20 @@ LONG smartcard_pack_reconnect_return(wStream* s, const Reconnect_Return* ret) + return ret->ReturnCode; + } + ++LONG smartcard_unpack_reconnect_return(wStream* s, Reconnect_Return* ret) ++{ ++ WINPR_ASSERT(ret); ++ wLog* log = scard_log(); ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 4)) ++ return STATUS_BUFFER_TOO_SMALL; ++ ++ Stream_Read_UINT32(s, ret->dwActiveProtocol); ++ ++ smartcard_trace_reconnect_return(log, ret); ++ return SCARD_S_SUCCESS; ++} ++ + LONG smartcard_unpack_hcard_and_disposition_call(wStream* s, HCardAndDisposition_Call* call, + const char* name) + { +@@ -2816,6 +2978,36 @@ LONG smartcard_unpack_status_call(wStream* s, Status_Call* call, BOOL unicode) + return status; + } + ++LONG smartcard_pack_status_call(wStream* s, const Status_Call* call, BOOL unicode) ++{ ++ WINPR_ASSERT(call); ++ wLog* log = scard_log(); ++ DWORD index = 0; ++ ++ smartcard_trace_status_call(log, call, unicode); ++ ++ LONG status = smartcard_pack_redir_scard_context(log, s, &call->handles.hContext, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ status = smartcard_pack_redir_scard_handle(log, s, &call->handles.hCard, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 12)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_INT32(s, call->fmszReaderNamesIsNULL); ++ Stream_Write_UINT32(s, call->cchReaderLen); ++ Stream_Write_UINT32(s, call->cbAtrLen); ++ ++ status = smartcard_pack_redir_scard_context_ref(log, s, &call->handles.hContext); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ return smartcard_pack_redir_scard_handle_ref(log, s, &call->handles.hCard); ++} ++ + LONG smartcard_pack_status_return(wStream* s, const Status_Return* ret, BOOL unicode) + { + WINPR_ASSERT(ret); +@@ -2851,6 +3043,41 @@ LONG smartcard_pack_status_return(wStream* s, const Status_Return* ret, BOOL uni + return ret->ReturnCode; + } + ++LONG smartcard_unpack_status_return(wStream* s, Status_Return* ret, BOOL unicode) ++{ ++ WINPR_ASSERT(ret); ++ wLog* log = scard_log(); ++ UINT32 index = 0; ++ UINT32 mszNdrPtr = 0; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 4)) ++ return STATUS_BUFFER_TOO_SMALL; ++ ++ Stream_Read_UINT32(s, ret->cBytes); ++ ++ if (!smartcard_ndr_pointer_read(log, s, &index, &mszNdrPtr)) ++ return ERROR_INVALID_DATA; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 44)) ++ return STATUS_BUFFER_TOO_SMALL; ++ ++ Stream_Read_UINT32(s, ret->dwState); ++ Stream_Read_UINT32(s, ret->dwProtocol); ++ Stream_Read(s, ret->pbAtr, sizeof(ret->pbAtr)); ++ Stream_Read_UINT32(s, ret->cbAtrLen); ++ ++ if (mszNdrPtr) ++ { ++ LONG status = ++ smartcard_ndr_read(log, s, &ret->mszReaderNames, ret->cBytes, 1, NDR_PTR_SIMPLE); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ } ++ ++ smartcard_trace_status_return(log, ret, unicode); ++ return SCARD_S_SUCCESS; ++} ++ + LONG smartcard_unpack_get_attrib_call(wStream* s, GetAttrib_Call* call) + { + WINPR_ASSERT(call); +@@ -2944,8 +3171,8 @@ LONG smartcard_unpack_control_call(wStream* s, Control_Call* call) + if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 20)) + return STATUS_BUFFER_TOO_SMALL; + +- Stream_Read_UINT32(s, call->dwControlCode); /* dwControlCode (4 bytes) */ +- Stream_Read_UINT32(s, call->cbInBufferSize); /* cbInBufferSize (4 bytes) */ ++ Stream_Read_UINT32(s, call->dwControlCode); /* dwControlCode (4 bytes) */ ++ Stream_Read_UINT32(s, call->cbInBufferSize); /* cbInBufferSize (4 bytes) */ + if (!smartcard_ndr_pointer_read(log, s, &index, + &pvInBufferNdrPtr)) /* pvInBufferNdrPtr (4 bytes) */ + return ERROR_INVALID_DATA; +@@ -3557,6 +3784,46 @@ LONG smartcard_unpack_set_attrib_call(wStream* s, SetAttrib_Call* call) + return SCARD_S_SUCCESS; + } + ++LONG smartcard_pack_set_attrib_call(wStream* s, const SetAttrib_Call* call) ++{ ++ WINPR_ASSERT(call); ++ wLog* log = scard_log(); ++ DWORD index = 0; ++ ++ smartcard_trace_set_attrib_call(log, call); ++ ++ LONG status = smartcard_pack_redir_scard_context(log, s, &call->handles.hContext, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ status = smartcard_pack_redir_scard_handle(log, s, &call->handles.hCard, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 8)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_UINT32(s, call->dwAttrId); ++ Stream_Write_UINT32(s, call->cbAttrLen); ++ ++ if (!smartcard_ndr_pointer_write(s, &index, call->cbAttrLen)) ++ return SCARD_E_NO_MEMORY; ++ ++ status = smartcard_pack_redir_scard_context_ref(log, s, &call->handles.hContext); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ status = smartcard_pack_redir_scard_handle_ref(log, s, &call->handles.hCard); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ status = smartcard_ndr_write(s, call->pbAttr, call->cbAttrLen, 1, NDR_PTR_SIMPLE); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ return SCARD_S_SUCCESS; ++} ++ + LONG smartcard_unpack_locate_cards_by_atr_w_call(wStream* s, LocateCardsByATRW_Call* call) + { + UINT32 rgReaderStatesNdrPtr = 0; +@@ -4037,3 +4304,689 @@ LONG smartcard_pack_read_cache_return(wStream* s, const ReadCache_Return* ret) + return status; + return ret->ReturnCode; + } ++ ++LONG smartcard_pack_context_call(wStream* s, const Context_Call* call, const char* name) ++{ ++ WINPR_ASSERT(call); ++ wLog* log = scard_log(); ++ DWORD index = 0; ++ ++ smartcard_trace_context_call(log, call, name); ++ ++ LONG status = smartcard_pack_redir_scard_context(log, s, &call->handles.hContext, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ return smartcard_pack_redir_scard_context_ref(log, s, &call->handles.hContext); ++} ++ ++LONG smartcard_pack_list_readers_call(wStream* s, const ListReaders_Call* call, BOOL unicode) ++{ ++ WINPR_ASSERT(call); ++ wLog* log = scard_log(); ++ DWORD index = 0; ++ ++ smartcard_trace_list_readers_call(log, call, unicode); ++ ++ LONG status = smartcard_pack_redir_scard_context(log, s, &call->handles.hContext, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 4)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_UINT32(s, call->cBytes); ++ if (!smartcard_ndr_pointer_write(s, &index, call->cBytes)) ++ return SCARD_E_NO_MEMORY; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 8)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_INT32(s, call->fmszReadersIsNULL); ++ Stream_Write_UINT32(s, call->cchReaders); ++ ++ status = smartcard_pack_redir_scard_context_ref(log, s, &call->handles.hContext); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (call->cBytes > 0) ++ { ++ status = smartcard_ndr_write(s, call->mszGroups, call->cBytes, 1, NDR_PTR_SIMPLE); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ } ++ ++ return SCARD_S_SUCCESS; ++} ++ ++static LONG smartcard_pack_reader_state_a(wStream* s, const LPSCARD_READERSTATEA rgReaderStates, ++ UINT32 cReaders, UINT32* ptrIndex) ++{ ++ WINPR_ASSERT(rgReaderStates || (cReaders == 0)); ++ ++ if (!Stream_EnsureRemainingCapacity(s, 4)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_UINT32(s, cReaders); ++ ++ for (UINT32 i = 0; i < cReaders; i++) ++ { ++ const SCARD_READERSTATEA* state = &rgReaderStates[i]; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 52)) ++ return SCARD_E_NO_MEMORY; ++ ++ const DWORD nameLen = state->szReader ? (DWORD)(strlen(state->szReader) + 1) : 0; ++ if (!smartcard_ndr_pointer_write(s, ptrIndex, nameLen)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_UINT32(s, state->dwCurrentState); ++ Stream_Write_UINT32(s, state->dwEventState); ++ Stream_Write_UINT32(s, state->cbAtr); ++ Stream_Write(s, state->rgbAtr, 36); ++ } ++ ++ for (UINT32 i = 0; i < cReaders; i++) ++ { ++ const SCARD_READERSTATEA* state = &rgReaderStates[i]; ++ ++ if (state->szReader) ++ { ++ const UINT32 nameLen = (UINT32)(strlen(state->szReader) + 1); ++ LONG status = smartcard_ndr_write_a(s, state->szReader, nameLen, NDR_PTR_FULL); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ } ++ } ++ ++ return SCARD_S_SUCCESS; ++} ++ ++static LONG smartcard_pack_reader_state_w(wStream* s, const LPSCARD_READERSTATEW rgReaderStates, ++ UINT32 cReaders, UINT32* ptrIndex) ++{ ++ WINPR_ASSERT(rgReaderStates || (cReaders == 0)); ++ ++ if (!Stream_EnsureRemainingCapacity(s, 4)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_UINT32(s, cReaders); ++ ++ for (UINT32 i = 0; i < cReaders; i++) ++ { ++ const SCARD_READERSTATEW* state = &rgReaderStates[i]; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 52)) ++ return SCARD_E_NO_MEMORY; ++ ++ const DWORD nameLen = state->szReader ? (DWORD)(_wcslen(state->szReader) + 1) : 0; ++ if (!smartcard_ndr_pointer_write(s, ptrIndex, nameLen)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_UINT32(s, state->dwCurrentState); ++ Stream_Write_UINT32(s, state->dwEventState); ++ Stream_Write_UINT32(s, state->cbAtr); ++ Stream_Write(s, state->rgbAtr, 36); ++ } ++ ++ for (UINT32 i = 0; i < cReaders; i++) ++ { ++ const SCARD_READERSTATEW* state = &rgReaderStates[i]; ++ ++ if (state->szReader) ++ { ++ const UINT32 nameLen = (UINT32)(_wcslen(state->szReader) + 1); ++ LONG status = smartcard_ndr_write_w(s, state->szReader, nameLen, NDR_PTR_FULL); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ } ++ } ++ ++ return SCARD_S_SUCCESS; ++} ++ ++LONG smartcard_pack_get_status_change_a_call(wStream* s, const GetStatusChangeA_Call* call) ++{ ++ WINPR_ASSERT(call); ++ wLog* log = scard_log(); ++ DWORD index = 0; ++ ++ smartcard_trace_get_status_change_a_call(log, call); ++ ++ LONG status = smartcard_pack_redir_scard_context(log, s, &call->handles.hContext, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 8)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_UINT32(s, call->dwTimeOut); ++ Stream_Write_UINT32(s, call->cReaders); ++ ++ if (!smartcard_ndr_pointer_write(s, &index, call->cReaders)) ++ return SCARD_E_NO_MEMORY; ++ ++ status = smartcard_pack_redir_scard_context_ref(log, s, &call->handles.hContext); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (call->cReaders > 0) ++ { ++ status = smartcard_pack_reader_state_a(s, call->rgReaderStates, call->cReaders, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ } ++ ++ return SCARD_S_SUCCESS; ++} ++ ++LONG smartcard_pack_get_status_change_w_call(wStream* s, const GetStatusChangeW_Call* call) ++{ ++ WINPR_ASSERT(call); ++ wLog* log = scard_log(); ++ DWORD index = 0; ++ ++ smartcard_trace_get_status_change_w_call(log, call); ++ ++ LONG status = smartcard_pack_redir_scard_context(log, s, &call->handles.hContext, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 8)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_UINT32(s, call->dwTimeOut); ++ Stream_Write_UINT32(s, call->cReaders); ++ ++ if (!smartcard_ndr_pointer_write(s, &index, call->cReaders)) ++ return SCARD_E_NO_MEMORY; ++ ++ status = smartcard_pack_redir_scard_context_ref(log, s, &call->handles.hContext); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (call->cReaders > 0) ++ { ++ status = smartcard_pack_reader_state_w(s, call->rgReaderStates, call->cReaders, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ } ++ ++ return SCARD_S_SUCCESS; ++} ++ ++LONG smartcard_pack_connect_a_call(wStream* s, const ConnectA_Call* call) ++{ ++ WINPR_ASSERT(call); ++ wLog* log = scard_log(); ++ DWORD index = 0; ++ ++ smartcard_trace_connect_a_call(log, call); ++ ++ const DWORD readerLen = call->szReader ? (DWORD)(strlen(call->szReader) + 1) : 0; ++ if (!smartcard_ndr_pointer_write(s, &index, readerLen)) ++ return SCARD_E_NO_MEMORY; ++ ++ LONG status = ++ smartcard_pack_redir_scard_context(log, s, &call->Common.handles.hContext, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 8)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_UINT32(s, call->Common.dwShareMode); ++ Stream_Write_UINT32(s, call->Common.dwPreferredProtocols); ++ ++ if (call->szReader) ++ { ++ status = smartcard_ndr_write_a(s, call->szReader, readerLen, NDR_PTR_FULL); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ } ++ ++ return smartcard_pack_redir_scard_context_ref(log, s, &call->Common.handles.hContext); ++} ++ ++LONG smartcard_pack_connect_w_call(wStream* s, const ConnectW_Call* call) ++{ ++ WINPR_ASSERT(call); ++ wLog* log = scard_log(); ++ DWORD index = 0; ++ ++ smartcard_trace_connect_w_call(log, call); ++ ++ const DWORD readerLen = call->szReader ? (DWORD)(_wcslen(call->szReader) + 1) : 0; ++ if (!smartcard_ndr_pointer_write(s, &index, readerLen)) ++ return SCARD_E_NO_MEMORY; ++ ++ LONG status = ++ smartcard_pack_redir_scard_context(log, s, &call->Common.handles.hContext, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 8)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_UINT32(s, call->Common.dwShareMode); ++ Stream_Write_UINT32(s, call->Common.dwPreferredProtocols); ++ ++ if (call->szReader) ++ { ++ status = smartcard_ndr_write_w(s, call->szReader, readerLen, NDR_PTR_FULL); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ } ++ ++ return smartcard_pack_redir_scard_context_ref(log, s, &call->Common.handles.hContext); ++} ++ ++LONG smartcard_pack_control_call(wStream* s, const Control_Call* call) ++{ ++ WINPR_ASSERT(call); ++ wLog* log = scard_log(); ++ DWORD index = 0; ++ ++ smartcard_trace_control_call(log, call); ++ ++ LONG status = smartcard_pack_redir_scard_context(log, s, &call->handles.hContext, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ status = smartcard_pack_redir_scard_handle(log, s, &call->handles.hCard, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 8)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_UINT32(s, call->dwControlCode); ++ Stream_Write_UINT32(s, call->cbInBufferSize); ++ if (!smartcard_ndr_pointer_write(s, &index, call->cbInBufferSize)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_INT32(s, call->fpvOutBufferIsNULL); ++ Stream_Write_UINT32(s, call->cbOutBufferSize); ++ ++ status = smartcard_pack_redir_scard_context_ref(log, s, &call->handles.hContext); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ status = smartcard_pack_redir_scard_handle_ref(log, s, &call->handles.hCard); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (call->cbInBufferSize) ++ { ++ status = smartcard_ndr_write(s, call->pvInBuffer, call->cbInBufferSize, 1, NDR_PTR_SIMPLE); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ } ++ ++ return status; ++} ++ ++LONG smartcard_pack_hcard_and_disposition_call(wStream* s, const HCardAndDisposition_Call* call, ++ const char* name) ++{ ++ WINPR_ASSERT(call); ++ wLog* log = scard_log(); ++ DWORD index = 0; ++ ++ smartcard_trace_hcard_and_disposition_call(log, call, name); ++ ++ LONG status = smartcard_pack_redir_scard_context(log, s, &call->handles.hContext, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ status = smartcard_pack_redir_scard_handle(log, s, &call->handles.hCard, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 4)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_UINT32(s, call->dwDisposition); ++ ++ status = smartcard_pack_redir_scard_context_ref(log, s, &call->handles.hContext); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ return smartcard_pack_redir_scard_handle_ref(log, s, &call->handles.hCard); ++} ++ ++LONG smartcard_pack_transmit_call(wStream* s, const Transmit_Call* call) ++{ ++ WINPR_ASSERT(call); ++ wLog* log = scard_log(); ++ DWORD index = 0; ++ ++ smartcard_trace_transmit_call(log, call); ++ ++ LONG status = smartcard_pack_redir_scard_context(log, s, &call->handles.hContext, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ status = smartcard_pack_redir_scard_handle(log, s, &call->handles.hCard, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ UINT32 cbExtraBytes = 0; ++ const BYTE* pbExtraBytes = NULL; ++ if (call->pioSendPci) ++ { ++ cbExtraBytes = (UINT32)(call->pioSendPci->cbPciLength - sizeof(SCARD_IO_REQUEST)); ++ if (cbExtraBytes > 0) ++ pbExtraBytes = &((const BYTE*)call->pioSendPci)[sizeof(SCARD_IO_REQUEST)]; ++ } ++ ++ if (!Stream_EnsureRemainingCapacity(s, 32)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_UINT32(s, call->pioSendPci ? call->pioSendPci->dwProtocol : 0); ++ Stream_Write_UINT32(s, cbExtraBytes); ++ if (!smartcard_ndr_pointer_write(s, &index, cbExtraBytes)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_UINT32(s, call->cbSendLength); ++ if (!smartcard_ndr_pointer_write(s, &index, call->cbSendLength)) ++ return SCARD_E_NO_MEMORY; ++ ++ const UINT32 recvPciLen = call->pioRecvPci ? (UINT32)call->pioRecvPci->cbPciLength : 0; ++ if (!smartcard_ndr_pointer_write(s, &index, recvPciLen)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_INT32(s, call->fpbRecvBufferIsNULL); ++ Stream_Write_UINT32(s, call->cbRecvLength); ++ ++ status = smartcard_pack_redir_scard_context_ref(log, s, &call->handles.hContext); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ status = smartcard_pack_redir_scard_handle_ref(log, s, &call->handles.hCard); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (cbExtraBytes > 0) ++ { ++ status = smartcard_ndr_write(s, pbExtraBytes, cbExtraBytes, 1, NDR_PTR_SIMPLE); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ } ++ ++ if (call->cbSendLength > 0) ++ { ++ status = smartcard_ndr_write(s, call->pbSendBuffer, call->cbSendLength, 1, NDR_PTR_SIMPLE); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ } ++ ++ if (call->pioRecvPci && recvPciLen > 0) ++ { ++ UINT32 cbRecvExtra = (UINT32)(call->pioRecvPci->cbPciLength - sizeof(SCARD_IO_REQUEST)); ++ const BYTE* pbRecvExtra = &((const BYTE*)call->pioRecvPci)[sizeof(SCARD_IO_REQUEST)]; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 12)) ++ return SCARD_E_NO_MEMORY; ++ ++ Stream_Write_UINT32(s, call->pioRecvPci->dwProtocol); ++ Stream_Write_UINT32(s, cbRecvExtra); ++ if (!smartcard_ndr_pointer_write(s, &index, cbRecvExtra)) ++ return SCARD_E_NO_MEMORY; ++ ++ if (cbRecvExtra > 0) ++ { ++ status = smartcard_ndr_write(s, pbRecvExtra, cbRecvExtra, 1, NDR_PTR_SIMPLE); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ } ++ } ++ ++ return SCARD_S_SUCCESS; ++} ++ ++LONG smartcard_pack_get_attrib_call(wStream* s, const GetAttrib_Call* call) ++{ ++ WINPR_ASSERT(call); ++ wLog* log = scard_log(); ++ DWORD index = 0; ++ ++ smartcard_trace_get_attrib_call(log, call); ++ ++ LONG status = smartcard_pack_redir_scard_context(log, s, &call->handles.hContext, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ status = smartcard_pack_redir_scard_handle(log, s, &call->handles.hCard, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 12)) ++ return STATUS_NO_MEMORY; ++ ++ Stream_Write_UINT32(s, call->dwAttrId); ++ Stream_Write_INT32(s, call->fpbAttrIsNULL); ++ Stream_Write_UINT32(s, call->cbAttrLen); ++ ++ status = smartcard_pack_redir_scard_context_ref(log, s, &call->handles.hContext); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ return smartcard_pack_redir_scard_handle_ref(log, s, &call->handles.hCard); ++} ++ ++LONG smartcard_unpack_list_readers_return(wStream* s, ListReaders_Return* ret, BOOL unicode) ++{ ++ WINPR_ASSERT(ret); ++ wLog* log = scard_log(); ++ UINT32 index = 0; ++ UINT32 mszNdrPtr = 0; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 4)) ++ return STATUS_BUFFER_TOO_SMALL; ++ ++ Stream_Read_UINT32(s, ret->cBytes); ++ ++ if (!smartcard_ndr_pointer_read(log, s, &index, &mszNdrPtr)) ++ return ERROR_INVALID_DATA; ++ ++ if (mszNdrPtr) ++ { ++ LONG status = smartcard_ndr_read(log, s, &ret->msz, ret->cBytes, 1, NDR_PTR_SIMPLE); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ } ++ ++ smartcard_trace_list_readers_return(log, ret, unicode); ++ return SCARD_S_SUCCESS; ++} ++ ++LONG smartcard_unpack_get_status_change_return(wStream* s, GetStatusChange_Return* ret, ++ BOOL unicode) ++{ ++ WINPR_ASSERT(ret); ++ wLog* log = scard_log(); ++ UINT32 index = 0; ++ UINT32 ndrPtr = 0; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 4)) ++ return STATUS_BUFFER_TOO_SMALL; ++ ++ Stream_Read_UINT32(s, ret->cReaders); ++ ++ if (!smartcard_ndr_pointer_read(log, s, &index, &ndrPtr)) ++ return ERROR_INVALID_DATA; ++ ++ if (ndrPtr && ret->cReaders > 0) ++ { ++ LONG status = ++ smartcard_ndr_read_state(log, s, &ret->rgReaderStates, ret->cReaders, NDR_PTR_SIMPLE); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ } ++ else ++ ret->rgReaderStates = NULL; ++ ++ smartcard_trace_get_status_change_return(log, ret, unicode); ++ return SCARD_S_SUCCESS; ++} ++ ++LONG smartcard_unpack_connect_return(wStream* s, Connect_Return* ret) ++{ ++ WINPR_ASSERT(ret); ++ wLog* log = scard_log(); ++ UINT32 index = 0; ++ UINT32 pbContextNdrPtr = 0; ++ ++ LONG status = ++ smartcard_unpack_redir_scard_context(log, s, &ret->hContext, &index, &pbContextNdrPtr); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ status = smartcard_unpack_redir_scard_handle(log, s, &ret->hCard, &index); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 4)) ++ return STATUS_BUFFER_TOO_SMALL; ++ ++ Stream_Read_UINT32(s, ret->dwActiveProtocol); ++ ++ status = smartcard_unpack_redir_scard_context_ref(log, s, pbContextNdrPtr, &ret->hContext); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ status = smartcard_unpack_redir_scard_handle_ref(log, s, &ret->hCard); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ smartcard_trace_connect_return(log, ret); ++ return SCARD_S_SUCCESS; ++} ++ ++LONG smartcard_unpack_control_return(wStream* s, Control_Return* ret) ++{ ++ WINPR_ASSERT(ret); ++ wLog* log = scard_log(); ++ UINT32 index = 0; ++ UINT32 pvOutBufferNdrPtr = 0; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 8)) ++ return STATUS_BUFFER_TOO_SMALL; ++ ++ Stream_Read_UINT32(s, ret->cbOutBufferSize); ++ ++ if (!smartcard_ndr_pointer_read(log, s, &index, &pvOutBufferNdrPtr)) ++ return ERROR_INVALID_DATA; ++ ++ if (pvOutBufferNdrPtr && ret->cbOutBufferSize) ++ { ++ LONG status = ++ smartcard_ndr_read(log, s, &ret->pvOutBuffer, ret->cbOutBufferSize, 1, NDR_PTR_SIMPLE); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ } ++ smartcard_trace_control_return(log, ret); ++ return SCARD_S_SUCCESS; ++} ++ ++LONG smartcard_unpack_transmit_return(wStream* s, Transmit_Return* ret) ++{ ++ WINPR_ASSERT(ret); ++ wLog* log = scard_log(); ++ UINT32 index = 0; ++ UINT32 recvPciNdrPtr = 0; ++ UINT32 recvBufferNdrPtr = 0; ++ ++ if (!smartcard_ndr_pointer_read(log, s, &index, &recvPciNdrPtr)) ++ return ERROR_INVALID_DATA; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 4)) ++ return STATUS_BUFFER_TOO_SMALL; ++ ++ Stream_Read_UINT32(s, ret->cbRecvLength); ++ ++ if (!smartcard_ndr_pointer_read(log, s, &index, &recvBufferNdrPtr)) ++ return ERROR_INVALID_DATA; ++ ++ if (recvPciNdrPtr) ++ { ++ if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 8)) ++ return STATUS_BUFFER_TOO_SMALL; ++ ++ UINT32 dwProtocol = 0; ++ UINT32 cbExtraBytes = 0; ++ Stream_Read_UINT32(s, dwProtocol); ++ Stream_Read_UINT32(s, cbExtraBytes); ++ ++ UINT32 pbExtraBytesNdrPtr = 0; ++ if (!smartcard_ndr_pointer_read(log, s, &index, &pbExtraBytesNdrPtr)) ++ return ERROR_INVALID_DATA; ++ ++ if (pbExtraBytesNdrPtr) ++ { ++ if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 4)) ++ return STATUS_BUFFER_TOO_SMALL; ++ ++ UINT32 length = 0; ++ Stream_Read_UINT32(s, length); ++ if (length != cbExtraBytes) ++ { ++ WLog_Print(log, WLOG_WARN, ++ "Transmit_Return unexpected length: Actual: %" PRIu32 ++ ", Expected: %" PRIu32 " (pioRecvPci.cbExtraBytes)", ++ length, cbExtraBytes); ++ return STATUS_INVALID_PARAMETER; ++ } ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(log, s, cbExtraBytes)) ++ return STATUS_BUFFER_TOO_SMALL; ++ ++ ret->pioRecvPci = (LPSCARD_IO_REQUEST)malloc(sizeof(SCARD_IO_REQUEST) + cbExtraBytes); ++ if (!ret->pioRecvPci) ++ return SCARD_E_NO_MEMORY; ++ ++ ret->pioRecvPci->dwProtocol = dwProtocol; ++ ret->pioRecvPci->cbPciLength = (DWORD)(sizeof(SCARD_IO_REQUEST) + cbExtraBytes); ++ ++ BYTE* pbExtraBytes = &((BYTE*)ret->pioRecvPci)[sizeof(SCARD_IO_REQUEST)]; ++ Stream_Read(s, pbExtraBytes, length); ++ if (smartcard_unpack_read_size_align(s, cbExtraBytes, 4) < 0) ++ return STATUS_INVALID_PARAMETER; ++ } ++ } ++ ++ if (recvBufferNdrPtr && ret->cbRecvLength > 0) ++ { ++ LONG status = ++ smartcard_ndr_read(log, s, &ret->pbRecvBuffer, ret->cbRecvLength, 1, NDR_PTR_SIMPLE); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ } ++ ++ smartcard_trace_transmit_return(log, ret); ++ return SCARD_S_SUCCESS; ++} ++ ++LONG smartcard_unpack_get_attrib_return(wStream* s, GetAttrib_Return* ret) ++{ ++ WINPR_ASSERT(ret); ++ wLog* log = scard_log(); ++ UINT32 index = 0; ++ UINT32 pbAttrPtr = 0; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 4)) ++ return STATUS_BUFFER_TOO_SMALL; ++ ++ ret->cbAttrLen = Stream_Get_UINT32(s); ++ ++ if (!smartcard_ndr_pointer_read(log, s, &index, &pbAttrPtr)) ++ return ERROR_INVALID_DATA; ++ ++ LONG status = smartcard_ndr_read(log, s, &ret->pbAttr, ret->cbAttrLen, 1, NDR_PTR_SIMPLE); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ smartcard_trace_get_attrib_return(log, ret, 0); ++ return SCARD_S_SUCCESS; ++} + +From c3cbf5dc622e1f00a3176694829c1e151135b75a Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Fri, 8 May 2026 10:44:30 +0200 +Subject: [PATCH 09/31] [utils,smartcard] add decode_response for server-side + IOCTL parsing + +Add smartcard_irp_device_control_decode_response as the symmetric +counterpart to decode_request. It decodes DR_CONTROL_RSP: reads +OutputBufferLength, CommonTypeHeader, PrivateTypeHeader, then dispatches +to per-IOCTL decode functions. + +Add a ret union to SMARTCARD_OPERATION for decoded response data, and a +returnCode field since all smartcard IOCTL responses share a common +ReturnCode as their first field before the per-operation return data. + +There are some responses that aren't supported for decoding yet. +--- + include/freerdp/utils/smartcard_operations.h | 46 +++ + libfreerdp/utils/smartcard_operations.c | 382 ++++++++++++++++++- + 2 files changed, 427 insertions(+), 1 deletion(-) + +diff --git a/include/freerdp/utils/smartcard_operations.h b/include/freerdp/utils/smartcard_operations.h +index 41a5fd6b043a..a090b64fdfad 100644 +--- a/include/freerdp/utils/smartcard_operations.h ++++ b/include/freerdp/utils/smartcard_operations.h +@@ -84,6 +84,19 @@ extern "C" + SCARDHANDLE hCard; + const char* ioControlCodeName; + UINT32 outputBufferLength; /** @since version 3.13.0 */ ++ union ++ { ++ EstablishContext_Return establishContext; ++ ListReaders_Return listReaders; ++ GetStatusChange_Return getStatusChange; ++ Connect_Return connect; ++ Control_Return control; ++ Transmit_Return transmit; ++ GetAttrib_Return getAttrib; ++ Reconnect_Return reconnect; ++ Status_Return status; ++ } ret; /** @since version 3.28.0 */ ++ LONG returnCode; /** @since version 3.28.0 */ + } SMARTCARD_OPERATION; + + /** @brief Alias for \ref smartcard_irp_device_control_decode_request. +@@ -114,6 +127,39 @@ extern "C" + UINT32 FileId, + SMARTCARD_OPERATION* operation); + ++ /** @brief Decode a smartcard IOCTL response received from the client. ++ * ++ * Parses the OutputBufferLength, Common Type Header, Private Type Header, and the ++ * ReturnCode, then unpacks the IOCTL-specific return payload into \p operation->ret. ++ * ++ * @param s Stream positioned at OutputBufferLength, after the DeviceIoReply ++ * header. ++ * @param ioControlCode The SCARD_IOCTL_* code identifying the response type. ++ * @param operation [out] The decoded returnCode and ret union. ++ * @return \b SCARD_S_SUCCESS on success, a smartcard error code on failure. ++ * ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG smartcard_irp_device_control_decode_response(wStream* s, UINT32 ioControlCode, ++ SMARTCARD_OPERATION* operation); ++ ++ /** @brief Encode a smartcard IOCTL request to send to the client. ++ * ++ * Writes the Common Type Header, Private Type Header, and the IOCTL-specific ++ * call payload from \p operation->call into the stream. The ioControlCode field ++ * in \p operation selects which pack_call function is dispatched. ++ * ++ * @param s Stream to write into, positioned where the type headers should start. ++ * @param operation with the populated call union and ioControlCode to encode. ++ * @return \b SCARD_S_SUCCESS on success, a smartcard error code on failure. ++ * ++ * @since version 3.28.0 ++ */ ++ WINPR_ATTR_NODISCARD ++ FREERDP_API LONG ++ smartcard_irp_device_control_encode_request(wStream* s, const SMARTCARD_OPERATION* operation); ++ + /** @brief Free resources held by a SMARTCARD_OPERATION. + * + * Releases any memory allocated inside the call and ret unions (e.g., reader name +diff --git a/libfreerdp/utils/smartcard_operations.c b/libfreerdp/utils/smartcard_operations.c +index 61c5761a41f6..906923fc7ecc 100644 +--- a/libfreerdp/utils/smartcard_operations.c ++++ b/libfreerdp/utils/smartcard_operations.c +@@ -561,7 +561,7 @@ LONG smartcard_irp_device_control_decode_request(wStream* s, UINT32 CompletionId + const UINT32 outputBufferLength = Stream_Get_UINT32(s); /* OutputBufferLength (4 bytes) */ + const UINT32 inputBufferLength = Stream_Get_UINT32(s); /* InputBufferLength (4 bytes) */ + const UINT32 ioControlCode = Stream_Get_UINT32(s); /* IoControlCode (4 bytes) */ +- Stream_Seek(s, 20); /* Padding (20 bytes) */ ++ Stream_Seek(s, 20); /* Padding (20 bytes) */ + operation->ioControlCode = ioControlCode; + operation->ioControlCodeName = scard_get_ioctl_string(ioControlCode, FALSE); + operation->outputBufferLength = outputBufferLength; +@@ -825,6 +825,347 @@ LONG smartcard_irp_device_control_decode_request(wStream* s, UINT32 CompletionId + return status; + } + ++static LONG smartcard_EstablishContext_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_unpack_establish_context_return(s, &operation->ret.establishContext); ++} ++ ++static LONG smartcard_ReleaseContext_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_UNUSED(s); ++ WINPR_ASSERT(operation); ++ ++ const Long_Return ret = { .ReturnCode = operation->returnCode }; ++ smartcard_trace_long_return(&ret, "ReleaseContext"); ++ return SCARD_S_SUCCESS; ++} ++ ++static LONG smartcard_IsValidContext_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_UNUSED(s); ++ WINPR_ASSERT(operation); ++ ++ const Long_Return ret = { .ReturnCode = operation->returnCode }; ++ smartcard_trace_long_return(&ret, "IsValidContext"); ++ return SCARD_S_SUCCESS; ++} ++ ++static LONG smartcard_ListReaderGroupsA_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_unpack_list_reader_groups_return(s, &operation->ret.listReaders, FALSE); ++} ++ ++static LONG smartcard_ListReaderGroupsW_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_unpack_list_reader_groups_return(s, &operation->ret.listReaders, TRUE); ++} ++ ++static LONG smartcard_ListReadersA_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_unpack_list_readers_return(s, &operation->ret.listReaders, FALSE); ++} ++ ++static LONG smartcard_ListReadersW_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_unpack_list_readers_return(s, &operation->ret.listReaders, TRUE); ++} ++ ++static LONG smartcard_GetStatusChangeA_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_unpack_get_status_change_return(s, &operation->ret.getStatusChange, FALSE); ++} ++ ++static LONG smartcard_GetStatusChangeW_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_unpack_get_status_change_return(s, &operation->ret.getStatusChange, TRUE); ++} ++ ++static LONG smartcard_Cancel_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_UNUSED(s); ++ WINPR_ASSERT(operation); ++ ++ const Long_Return ret = { .ReturnCode = operation->returnCode }; ++ smartcard_trace_long_return(&ret, "Cancel"); ++ return SCARD_S_SUCCESS; ++} ++ ++static LONG smartcard_ConnectA_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_unpack_connect_return(s, &operation->ret.connect); ++} ++ ++static LONG smartcard_ConnectW_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_unpack_connect_return(s, &operation->ret.connect); ++} ++ ++static LONG smartcard_Reconnect_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_unpack_reconnect_return(s, &operation->ret.reconnect); ++} ++ ++static LONG smartcard_Disconnect_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_UNUSED(s); ++ WINPR_ASSERT(operation); ++ ++ const Long_Return ret = { .ReturnCode = operation->returnCode }; ++ smartcard_trace_long_return(&ret, "Disconnect"); ++ return SCARD_S_SUCCESS; ++} ++ ++static LONG smartcard_BeginTransaction_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_UNUSED(s); ++ WINPR_ASSERT(operation); ++ ++ const Long_Return ret = { .ReturnCode = operation->returnCode }; ++ smartcard_trace_long_return(&ret, "BeginTransaction"); ++ return SCARD_S_SUCCESS; ++} ++ ++static LONG smartcard_EndTransaction_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_UNUSED(s); ++ WINPR_ASSERT(operation); ++ ++ const Long_Return ret = { .ReturnCode = operation->returnCode }; ++ smartcard_trace_long_return(&ret, "EndTransaction"); ++ return SCARD_S_SUCCESS; ++} ++ ++static LONG smartcard_StatusA_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_unpack_status_return(s, &operation->ret.status, FALSE); ++} ++ ++static LONG smartcard_StatusW_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_unpack_status_return(s, &operation->ret.status, TRUE); ++} ++ ++static LONG smartcard_Transmit_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_unpack_transmit_return(s, &operation->ret.transmit); ++} ++ ++static LONG smartcard_Control_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_unpack_control_return(s, &operation->ret.control); ++} ++ ++static LONG smartcard_GetAttrib_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_unpack_get_attrib_return(s, &operation->ret.getAttrib); ++} ++ ++static LONG smartcard_SetAttrib_decode_response(wStream* s, SMARTCARD_OPERATION* operation) ++{ ++ WINPR_UNUSED(s); ++ WINPR_ASSERT(operation); ++ ++ const Long_Return ret = { .ReturnCode = operation->returnCode }; ++ smartcard_trace_long_return(&ret, "SetAttrib"); ++ return SCARD_S_SUCCESS; ++} ++ ++LONG smartcard_irp_device_control_decode_response(wStream* s, UINT32 ioControlCode, ++ SMARTCARD_OPERATION* operation) ++{ ++ LONG status = SCARD_S_SUCCESS; ++ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ if (!Stream_CheckAndLogRequiredLength(TAG, s, RDPDR_DEVICE_IO_CONTROL_RSP_HDR_LENGTH)) ++ return SCARD_F_INTERNAL_ERROR; ++ ++ const size_t startPosition = Stream_GetPosition(s); ++ ++ const UINT32 outputBufferLength = Stream_Get_UINT32(s); ++ operation->ioControlCode = ioControlCode; ++ operation->ioControlCodeName = scard_get_ioctl_string(ioControlCode, FALSE); ++ operation->outputBufferLength = outputBufferLength; ++ ++ if (outputBufferLength == 0) ++ return SCARD_S_SUCCESS; ++ ++ if (!Stream_CheckAndLogRequiredLength(TAG, s, outputBufferLength)) ++ return SCARD_F_INTERNAL_ERROR; ++ ++ status = smartcard_unpack_common_type_header(s); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ status = smartcard_unpack_private_type_header(s); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) ++ return SCARD_F_INTERNAL_ERROR; ++ ++ Stream_Read_INT32(s, operation->returnCode); ++ ++ if (operation->returnCode != SCARD_S_SUCCESS) ++ { ++ WLog_WARN(TAG, "%s failed with ReturnCode %s [0x%08" PRIX32 "]", ++ scard_get_ioctl_string(ioControlCode, TRUE), ++ SCardGetErrorString(operation->returnCode), (UINT32)operation->returnCode); ++ return SCARD_S_SUCCESS; ++ } ++ ++ switch (ioControlCode) ++ { ++ case SCARD_IOCTL_ESTABLISHCONTEXT: ++ status = smartcard_EstablishContext_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_RELEASECONTEXT: ++ status = smartcard_ReleaseContext_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_ISVALIDCONTEXT: ++ status = smartcard_IsValidContext_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_LISTREADERGROUPSA: ++ status = smartcard_ListReaderGroupsA_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_LISTREADERGROUPSW: ++ status = smartcard_ListReaderGroupsW_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_LISTREADERSA: ++ status = smartcard_ListReadersA_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_LISTREADERSW: ++ status = smartcard_ListReadersW_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_GETSTATUSCHANGEA: ++ status = smartcard_GetStatusChangeA_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_GETSTATUSCHANGEW: ++ status = smartcard_GetStatusChangeW_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_CANCEL: ++ status = smartcard_Cancel_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_CONNECTA: ++ status = smartcard_ConnectA_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_CONNECTW: ++ status = smartcard_ConnectW_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_RECONNECT: ++ status = smartcard_Reconnect_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_DISCONNECT: ++ status = smartcard_Disconnect_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_BEGINTRANSACTION: ++ status = smartcard_BeginTransaction_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_ENDTRANSACTION: ++ status = smartcard_EndTransaction_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_STATUSA: ++ status = smartcard_StatusA_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_STATUSW: ++ status = smartcard_StatusW_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_TRANSMIT: ++ status = smartcard_Transmit_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_CONTROL: ++ status = smartcard_Control_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_GETATTRIB: ++ status = smartcard_GetAttrib_decode_response(s, operation); ++ break; ++ ++ case SCARD_IOCTL_SETATTRIB: ++ status = smartcard_SetAttrib_decode_response(s, operation); ++ break; ++ ++ default: ++ WLog_WARN(TAG, "unsupported response for %s (0x%08" PRIX32 ")", ++ scard_get_ioctl_string(ioControlCode, TRUE), ioControlCode); ++ break; ++ } ++ ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ const size_t offset = startPosition + RDPDR_DEVICE_IO_CONTROL_RSP_HDR_LENGTH; ++ if (smartcard_unpack_read_size_align(s, Stream_GetPosition(s) - offset, 8) < 0) ++ return STATUS_INVALID_PARAMETER; ++ ++ return status; ++} ++ + static void free_reader_states_a(LPSCARD_READERSTATEA rgReaderStates, UINT32 cReaders) + { + for (UINT32 x = 0; x < cReaders; x++) +@@ -851,6 +1192,8 @@ void smartcard_operation_free(SMARTCARD_OPERATION* op, BOOL allocated) + { + if (!op) + return; ++ ++ /* Free resources in the call union */ + switch (op->ioControlCode) + { + case SCARD_IOCTL_CANCEL: +@@ -1028,6 +1371,43 @@ void smartcard_operation_free(SMARTCARD_OPERATION* op, BOOL allocated) + break; + } + ++ /* Free resources in the ret union */ ++ switch (op->ioControlCode) ++ { ++ case SCARD_IOCTL_LISTREADERSA: ++ case SCARD_IOCTL_LISTREADERSW: ++ case SCARD_IOCTL_LISTREADERGROUPSA: ++ case SCARD_IOCTL_LISTREADERGROUPSW: ++ { ++ ListReaders_Return* ret = &op->ret.listReaders; ++ free(ret->msz); ++ } ++ break; ++ case SCARD_IOCTL_STATUSA: ++ case SCARD_IOCTL_STATUSW: ++ { ++ Status_Return* ret = &op->ret.status; ++ free(ret->mszReaderNames); ++ } ++ break; ++ case SCARD_IOCTL_GETSTATUSCHANGEA: ++ case SCARD_IOCTL_GETSTATUSCHANGEW: ++ { ++ GetStatusChange_Return* ret = &op->ret.getStatusChange; ++ free(ret->rgReaderStates); ++ } ++ break; ++ case SCARD_IOCTL_TRANSMIT: ++ { ++ Transmit_Return* ret = &op->ret.transmit; ++ free(ret->pbRecvBuffer); ++ free(ret->pioRecvPci); ++ } ++ break; ++ default: ++ break; ++ } ++ + { + SMARTCARD_OPERATION empty = WINPR_C_ARRAY_INIT; + *op = empty; + +From 4c83cecd3bb108e0643020c2b2a21514d316b976 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Thu, 14 May 2026 16:44:31 +0200 +Subject: [PATCH 10/31] [utils,smartcard] add encode_request for server-side + IOCTL encoding + +Add smartcard_irp_device_control_encode_request as the symmetric +counterpart to decode_request. It encodes DR_CONTROL_REQ: writes +CommonTypeHeader, PrivateTypeHeader, then dispatches to per-IOCTL +encode functions. + +There are some requests that aren't supported for encoding yet. + +Co-authored-by: David Fort +--- + libfreerdp/utils/smartcard_operations.c | 329 ++++++++++++++++++++++++ + 1 file changed, 329 insertions(+) + +diff --git a/libfreerdp/utils/smartcard_operations.c b/libfreerdp/utils/smartcard_operations.c +index 906923fc7ecc..a38e0d102487 100644 +--- a/libfreerdp/utils/smartcard_operations.c ++++ b/libfreerdp/utils/smartcard_operations.c +@@ -1166,6 +1166,335 @@ LONG smartcard_irp_device_control_decode_response(wStream* s, UINT32 ioControlCo + return status; + } + ++static LONG smartcard_EstablishContext_encode_request(wStream* s, ++ const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_establish_context_call(s, &operation->call.establishContext); ++} ++ ++static LONG smartcard_ReleaseContext_encode_request(wStream* s, ++ const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_context_call(s, &operation->call.context, "ReleaseContext"); ++} ++ ++static LONG smartcard_IsValidContext_encode_request(wStream* s, ++ const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_context_call(s, &operation->call.context, "IsValidContext"); ++} ++ ++static LONG smartcard_ListReaderGroupsA_encode_request(wStream* s, ++ const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_list_reader_groups_call(s, &operation->call.listReaderGroups, FALSE); ++} ++ ++static LONG smartcard_ListReaderGroupsW_encode_request(wStream* s, ++ const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_list_reader_groups_call(s, &operation->call.listReaderGroups, TRUE); ++} ++ ++static LONG smartcard_ListReadersA_encode_request(wStream* s, const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_list_readers_call(s, &operation->call.listReaders, FALSE); ++} ++ ++static LONG smartcard_ListReadersW_encode_request(wStream* s, const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_list_readers_call(s, &operation->call.listReaders, TRUE); ++} ++ ++static LONG smartcard_GetStatusChangeA_encode_request(wStream* s, ++ const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_get_status_change_a_call(s, &operation->call.getStatusChangeA); ++} ++ ++static LONG smartcard_GetStatusChangeW_encode_request(wStream* s, ++ const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_get_status_change_w_call(s, &operation->call.getStatusChangeW); ++} ++ ++static LONG smartcard_Cancel_encode_request(wStream* s, const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_context_call(s, &operation->call.context, "Cancel"); ++} ++ ++static LONG smartcard_ConnectA_encode_request(wStream* s, const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_connect_a_call(s, &operation->call.connectA); ++} ++ ++static LONG smartcard_ConnectW_encode_request(wStream* s, const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_connect_w_call(s, &operation->call.connectW); ++} ++ ++static LONG smartcard_Reconnect_encode_request(wStream* s, const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_reconnect_call(s, &operation->call.reconnect); ++} ++ ++static LONG smartcard_Disconnect_encode_request(wStream* s, const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_hcard_and_disposition_call(s, &operation->call.hCardAndDisposition, ++ "Disconnect"); ++} ++ ++static LONG smartcard_BeginTransaction_encode_request(wStream* s, ++ const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_hcard_and_disposition_call(s, &operation->call.hCardAndDisposition, ++ "BeginTransaction"); ++} ++ ++static LONG smartcard_EndTransaction_encode_request(wStream* s, ++ const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_hcard_and_disposition_call(s, &operation->call.hCardAndDisposition, ++ "EndTransaction"); ++} ++ ++static LONG smartcard_StatusA_encode_request(wStream* s, const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_status_call(s, &operation->call.status, FALSE); ++} ++ ++static LONG smartcard_StatusW_encode_request(wStream* s, const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_status_call(s, &operation->call.status, TRUE); ++} ++ ++static LONG smartcard_Transmit_encode_request(wStream* s, const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_transmit_call(s, &operation->call.transmit); ++} ++ ++static LONG smartcard_Control_encode_request(wStream* s, const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_control_call(s, &operation->call.control); ++} ++ ++static LONG smartcard_GetAttrib_encode_request(wStream* s, const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_get_attrib_call(s, &operation->call.getAttrib); ++} ++ ++static LONG smartcard_SetAttrib_encode_request(wStream* s, const SMARTCARD_OPERATION* operation) ++{ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ return smartcard_pack_set_attrib_call(s, &operation->call.setAttrib); ++} ++ ++LONG smartcard_irp_device_control_encode_request(wStream* s, const SMARTCARD_OPERATION* operation) ++{ ++ LONG status = SCARD_S_SUCCESS; ++ ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(operation); ++ ++ const UINT32 ioControlCode = operation->ioControlCode; ++ ++ const size_t headerPos = Stream_GetPosition(s); ++ Stream_Zero(s, SMARTCARD_COMMON_TYPE_HEADER_LENGTH); /* CommonTypeHeader (8 bytes) */ ++ Stream_Zero(s, SMARTCARD_PRIVATE_TYPE_HEADER_LENGTH); /* PrivateTypeHeader (8 bytes) */ ++ ++ switch (ioControlCode) ++ { ++ case SCARD_IOCTL_ESTABLISHCONTEXT: ++ status = smartcard_EstablishContext_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_RELEASECONTEXT: ++ status = smartcard_ReleaseContext_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_ISVALIDCONTEXT: ++ status = smartcard_IsValidContext_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_LISTREADERGROUPSA: ++ status = smartcard_ListReaderGroupsA_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_LISTREADERGROUPSW: ++ status = smartcard_ListReaderGroupsW_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_LISTREADERSA: ++ status = smartcard_ListReadersA_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_LISTREADERSW: ++ status = smartcard_ListReadersW_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_GETSTATUSCHANGEA: ++ status = smartcard_GetStatusChangeA_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_GETSTATUSCHANGEW: ++ status = smartcard_GetStatusChangeW_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_CANCEL: ++ status = smartcard_Cancel_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_CONNECTA: ++ status = smartcard_ConnectA_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_CONNECTW: ++ status = smartcard_ConnectW_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_RECONNECT: ++ status = smartcard_Reconnect_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_DISCONNECT: ++ status = smartcard_Disconnect_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_BEGINTRANSACTION: ++ status = smartcard_BeginTransaction_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_ENDTRANSACTION: ++ status = smartcard_EndTransaction_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_STATUSA: ++ status = smartcard_StatusA_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_STATUSW: ++ status = smartcard_StatusW_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_TRANSMIT: ++ status = smartcard_Transmit_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_CONTROL: ++ status = smartcard_Control_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_GETATTRIB: ++ status = smartcard_GetAttrib_encode_request(s, operation); ++ break; ++ ++ case SCARD_IOCTL_SETATTRIB: ++ status = smartcard_SetAttrib_encode_request(s, operation); ++ break; ++ ++ default: ++ WLog_WARN(TAG, "unsupported request for %s (0x%08" PRIX32 ")", ++ scard_get_ioctl_string(ioControlCode, TRUE), ioControlCode); ++ return SCARD_E_UNSUPPORTED_FEATURE; ++ } ++ ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ if ((ioControlCode != SCARD_IOCTL_ACCESSSTARTEDEVENT) && ++ (ioControlCode != SCARD_IOCTL_RELEASETARTEDEVENT)) ++ { ++ const LONG rc = smartcard_pack_write_size_align(s, Stream_GetPosition(s), 8); ++ if (rc != SCARD_S_SUCCESS) ++ return rc; ++ } ++ ++ const size_t dataEndPos = Stream_GetPosition(s); ++ const size_t payloadSize = dataEndPos - headerPos; ++ const size_t objectBufferLength = ++ payloadSize - SMARTCARD_COMMON_TYPE_HEADER_LENGTH - SMARTCARD_PRIVATE_TYPE_HEADER_LENGTH; ++ ++ status = smartcard_pack_write_size_align(s, payloadSize, 8); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ const size_t endPos = Stream_GetPosition(s); ++ if (!Stream_SetPosition(s, headerPos)) ++ return SCARD_E_BAD_SEEK; ++ smartcard_pack_common_type_header(s); ++ smartcard_pack_private_type_header(s, (UINT32)objectBufferLength); ++ if (!Stream_SetPosition(s, endPos)) ++ return SCARD_E_BAD_SEEK; ++ Stream_SealLength(s); ++ ++ return status; ++} ++ + static void free_reader_states_a(LPSCARD_READERSTATEA rgReaderStates, UINT32 cReaders) + { + for (UINT32 x = 0; x < cReaders; x++) + +From 41fa3da3f479ea6967c6e37f8935f8e4d143b61d Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Tue, 23 Jun 2026 19:10:08 +0200 +Subject: [PATCH 11/31] [channels,rdpdr] cache context->priv in local variable + +Common subexpression elimination: extract RdpdrServerPrivate* priv +to avoid repeated context->priv pointer dereferences. + +Co-Authored-By: David Fort +--- + channels/rdpdr/server/rdpdr_main.c | 759 +++++++++++++++++------------ + 1 file changed, 438 insertions(+), 321 deletions(-) + +diff --git a/channels/rdpdr/server/rdpdr_main.c b/channels/rdpdr/server/rdpdr_main.c +index 1de60b6688df..5f37a3c7a3d0 100644 +--- a/channels/rdpdr/server/rdpdr_main.c ++++ b/channels/rdpdr/server/rdpdr_main.c +@@ -250,8 +250,10 @@ static BOOL rdpdr_server_enqueue_irp(RdpdrServerContext* context, RDPDR_IRP* irp + { + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); ++ ++ RdpdrServerPrivate* priv = context->priv; + const uintptr_t key = irp->CompletionId + 1ull; +- return ListDictionary_Add(context->priv->IrpList, (void*)key, irp); ++ return ListDictionary_Add(priv->IrpList, (void*)key, irp); + } + + static RDPDR_IRP* rdpdr_server_dequeue_irp(RdpdrServerContext* context, UINT32 completionId) +@@ -260,8 +262,9 @@ static RDPDR_IRP* rdpdr_server_dequeue_irp(RdpdrServerContext* context, UINT32 c + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + ++ RdpdrServerPrivate* priv = context->priv; + const uintptr_t key = completionId + 1ull; +- irp = (RDPDR_IRP*)ListDictionary_Take(context->priv->IrpList, (void*)key); ++ irp = (RDPDR_IRP*)ListDictionary_Take(priv->IrpList, (void*)key); + return irp; + } + +@@ -274,6 +277,7 @@ static UINT rdpdr_seal_send_free_request(RdpdrServerContext* context, wStream* s + WINPR_ASSERT(context->priv); + WINPR_ASSERT(s); + ++ RdpdrServerPrivate* priv = context->priv; + Stream_SealLength(s); + const size_t length = Stream_Length(s); + WINPR_ASSERT(length <= UINT32_MAX); +@@ -285,14 +289,14 @@ static UINT rdpdr_seal_send_free_request(RdpdrServerContext* context, wStream* s + Stream_Read_UINT16(s, header.Component); + Stream_Read_UINT16(s, header.PacketId); + +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ WLog_Print(priv->log, WLOG_DEBUG, + "sending message {Component %s[%04" PRIx16 "], PacketId %s[%04" PRIx16 "]", + rdpdr_component_string(header.Component), header.Component, + rdpdr_packetid_string(header.PacketId), header.PacketId); + } +- winpr_HexLogDump(context->priv->log, WLOG_DEBUG, Stream_Buffer(s), Stream_Length(s)); +- status = WTSVirtualChannelWrite(context->priv->ChannelHandle, Stream_BufferAs(s, char), +- (ULONG)length, &written); ++ winpr_HexLogDump(priv->log, WLOG_DEBUG, Stream_Buffer(s), Stream_Length(s)); ++ status = WTSVirtualChannelWrite(priv->ChannelHandle, Stream_BufferAs(s, char), (ULONG)length, ++ &written); + Stream_Free(s, TRUE); + return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR; + } +@@ -318,19 +322,20 @@ static UINT rdpdr_server_send_announce_request(RdpdrServerContext* context) + if (error != CHANNEL_RC_OK) + return error; + ++ RdpdrServerPrivate* priv = context->priv; + s = Stream_New(nullptr, RDPDR_HEADER_LENGTH + 8); + + if (!s) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "Stream_New failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT16(s, header.Component); /* Component (2 bytes) */ + Stream_Write_UINT16(s, header.PacketId); /* PacketId (2 bytes) */ +- Stream_Write_UINT16(s, context->priv->VersionMajor); /* VersionMajor (2 bytes) */ +- Stream_Write_UINT16(s, context->priv->VersionMinor); /* VersionMinor (2 bytes) */ +- Stream_Write_UINT32(s, context->priv->ClientId); /* ClientId (4 bytes) */ ++ Stream_Write_UINT16(s, priv->VersionMajor); /* VersionMajor (2 bytes) */ ++ Stream_Write_UINT16(s, priv->VersionMinor); /* VersionMinor (2 bytes) */ ++ Stream_Write_UINT32(s, priv->ClientId); /* ClientId (4 bytes) */ + return rdpdr_seal_send_free_request(context, s); + } + +@@ -349,19 +354,21 @@ static UINT rdpdr_server_receive_announce_response(RdpdrServerContext* context, + WINPR_ASSERT(context->priv); + WINPR_ASSERT(header); + ++ RdpdrServerPrivate* priv = context->priv; ++ + WINPR_UNUSED(header); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 8)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 8)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT16(s, VersionMajor); /* VersionMajor (2 bytes) */ + Stream_Read_UINT16(s, VersionMinor); /* VersionMinor (2 bytes) */ + Stream_Read_UINT32(s, ClientId); /* ClientId (4 bytes) */ +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ WLog_Print(priv->log, WLOG_DEBUG, + "Client Announce Response: VersionMajor: 0x%08" PRIX16 " VersionMinor: 0x%04" PRIX16 + " ClientId: 0x%08" PRIX32 "", + VersionMajor, VersionMinor, ClientId); +- context->priv->ClientId = ClientId; ++ priv->ClientId = ClientId; + + return IFCALLRESULT(CHANNEL_RC_OK, context->ReceiveAnnounceResponse, context, VersionMajor, + VersionMinor, ClientId); +@@ -385,7 +392,9 @@ static UINT rdpdr_server_receive_client_name_request(RdpdrServerContext* context + WINPR_ASSERT(header); + WINPR_UNUSED(header); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 12)) ++ RdpdrServerPrivate* priv = context->priv; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 12)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, UnicodeFlag); /* UnicodeFlag (4 bytes) */ +@@ -396,7 +405,7 @@ static UINT rdpdr_server_receive_client_name_request(RdpdrServerContext* context + UnicodeFlag = UnicodeFlag & 0x00000001; + + if (CodePage != 0) +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.2.4 Client Name Request (DR_CORE_CLIENT_NAME_REQ)::CodePage " + "must be 0, but is 0x%08" PRIx32, + CodePage); +@@ -410,8 +419,8 @@ static UINT rdpdr_server_receive_client_name_request(RdpdrServerContext* context + { + if ((ComputerNameLen % 2) || ComputerNameLen > 512 || ComputerNameLen < 2) + { +- WLog_Print(context->priv->log, WLOG_ERROR, +- "invalid unicode computer name length: %" PRIu32 "", ComputerNameLen); ++ WLog_Print(priv->log, WLOG_ERROR, "invalid unicode computer name length: %" PRIu32 "", ++ ComputerNameLen); + return ERROR_INVALID_DATA; + } + } +@@ -419,56 +428,55 @@ static UINT rdpdr_server_receive_client_name_request(RdpdrServerContext* context + { + if (ComputerNameLen > 256 || ComputerNameLen < 1) + { +- WLog_Print(context->priv->log, WLOG_ERROR, +- "invalid ascii computer name length: %" PRIu32 "", ComputerNameLen); ++ WLog_Print(priv->log, WLOG_ERROR, "invalid ascii computer name length: %" PRIu32 "", ++ ComputerNameLen); + return ERROR_INVALID_DATA; + } + } + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, ComputerNameLen)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, ComputerNameLen)) + return ERROR_INVALID_DATA; + + /* ComputerName must be null terminated, check if it really is */ + const char* computerName = Stream_ConstPointer(s); + if (computerName[ComputerNameLen - 1] || (UnicodeFlag && computerName[ComputerNameLen - 2])) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "computer name must be null terminated"); ++ WLog_Print(priv->log, WLOG_ERROR, "computer name must be null terminated"); + return ERROR_INVALID_DATA; + } + +- if (context->priv->ClientComputerName) ++ if (priv->ClientComputerName) + { +- free(context->priv->ClientComputerName); +- context->priv->ClientComputerName = nullptr; ++ free(priv->ClientComputerName); ++ priv->ClientComputerName = nullptr; + } + + if (UnicodeFlag) + { +- context->priv->ClientComputerName = ++ priv->ClientComputerName = + Stream_Read_UTF16_String_As_UTF8(s, ComputerNameLen / sizeof(WCHAR), nullptr); +- if (!context->priv->ClientComputerName) ++ if (!priv->ClientComputerName) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "failed to convert client computer name"); ++ WLog_Print(priv->log, WLOG_ERROR, "failed to convert client computer name"); + return ERROR_INVALID_DATA; + } + } + else + { + const char* name = Stream_ConstPointer(s); +- context->priv->ClientComputerName = _strdup(name); ++ priv->ClientComputerName = _strdup(name); + Stream_Seek(s, ComputerNameLen); + +- if (!context->priv->ClientComputerName) ++ if (!priv->ClientComputerName) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "failed to duplicate client computer name"); ++ WLog_Print(priv->log, WLOG_ERROR, "failed to duplicate client computer name"); + return CHANNEL_RC_NO_MEMORY; + } + } + +- WLog_Print(context->priv->log, WLOG_DEBUG, "ClientComputerName: %s", +- context->priv->ClientComputerName); ++ WLog_Print(priv->log, WLOG_DEBUG, "ClientComputerName: %s", priv->ClientComputerName); + return IFCALLRESULT(CHANNEL_RC_OK, context->ReceiveClientNameRequest, context, ComputerNameLen, +- context->priv->ClientComputerName); ++ priv->ClientComputerName); + } + + static UINT rdpdr_server_write_capability_set_header_cb(RdpdrServerContext* context, wStream* s, +@@ -476,7 +484,9 @@ static UINT rdpdr_server_write_capability_set_header_cb(RdpdrServerContext* cont + { + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); +- UINT error = rdpdr_write_capset_header(context->priv->log, s, header); ++ ++ RdpdrServerPrivate* priv = context->priv; ++ UINT error = rdpdr_write_capset_header(priv->log, s, header); + if (error != CHANNEL_RC_OK) + return error; + +@@ -495,7 +505,9 @@ static UINT rdpdr_server_read_general_capability_set(RdpdrServerContext* context + WINPR_ASSERT(context->priv); + WINPR_ASSERT(header); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 32)) ++ RdpdrServerPrivate* priv = context->priv; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + + const UINT32 OsType = Stream_Get_UINT32(s); /* osType (4 bytes), ignored on receipt */ +@@ -513,7 +525,7 @@ static UINT rdpdr_server_read_general_capability_set(RdpdrServerContext* context + + { + char buffer[1024] = WINPR_C_ARRAY_INIT; +- WLog_Print(context->priv->log, WLOG_TRACE, ++ WLog_Print(priv->log, WLOG_TRACE, + "OsType=%" PRIu32 ", OsVersion=%" PRIu32 ", VersionMajor=%" PRIu32 + ", VersionMinor=%" PRIu32 ", IoCode1=%s, IoCode2=%" PRIu32 + ", ExtendedPdu=%" PRIu32 ", ExtraFlags1=%" PRIu32 ", ExtraFlags2=%" PRIu32, +@@ -524,7 +536,7 @@ static UINT rdpdr_server_read_general_capability_set(RdpdrServerContext* context + + if (VersionMajor != RDPDR_MAJOR_RDP_VERSION) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "unsupported RDPDR version %" PRIu16 ".%" PRIu16, ++ WLog_Print(priv->log, WLOG_ERROR, "unsupported RDPDR version %" PRIu16 ".%" PRIu16, + VersionMajor, VersionMinor); + return ERROR_INVALID_DATA; + } +@@ -538,21 +550,20 @@ static UINT rdpdr_server_read_general_capability_set(RdpdrServerContext* context + case RDPDR_MINOR_RDP_VERSION_5_0: + break; + default: +- WLog_Print(context->priv->log, WLOG_WARN, +- "unsupported RDPDR minor version %" PRIu16 ".%" PRIu16, VersionMajor, +- VersionMinor); ++ WLog_Print(priv->log, WLOG_WARN, "unsupported RDPDR minor version %" PRIu16 ".%" PRIu16, ++ VersionMajor, VersionMinor); + break; + } + + UINT32 SpecialTypeDeviceCap = 0; + if (header->Version == GENERAL_CAPABILITY_VERSION_02) + { +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 4)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + + SpecialTypeDeviceCap = Stream_Get_UINT32(s); /* SpecialTypeDeviceCap (4 bytes) */ + } +- context->priv->SpecialDeviceTypeCap = SpecialTypeDeviceCap; ++ priv->SpecialDeviceTypeCap = SpecialTypeDeviceCap; + + const UINT32 mask = + RDPDR_IRP_MJ_CREATE | RDPDR_IRP_MJ_CLEANUP | RDPDR_IRP_MJ_CLOSE | RDPDR_IRP_MJ_READ | +@@ -564,15 +575,15 @@ static UINT rdpdr_server_read_general_capability_set(RdpdrServerContext* context + if ((IoCode1 & mask) == 0) + { + char buffer[1024] = WINPR_C_ARRAY_INIT; +- WLog_Print(context->priv->log, WLOG_ERROR, "Missing IRP mask values %s", ++ WLog_Print(priv->log, WLOG_ERROR, "Missing IRP mask values %s", + rdpdr_irp_mask2str(IoCode1 & mask, buffer, sizeof(buffer))); + return ERROR_INVALID_DATA; + } +- context->priv->IoCode1 = IoCode1; ++ priv->IoCode1 = IoCode1; + + if (IoCode2 != 0) + { +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.2.7.1 General Capability Set (GENERAL_CAPS_SET) ioCode2 is " + "reserved for future use, expected value 0 got %" PRIu32, + IoCode2); +@@ -580,17 +591,17 @@ static UINT rdpdr_server_read_general_capability_set(RdpdrServerContext* context + + if ((ExtendedPdu & RDPDR_CLIENT_DISPLAY_NAME_PDU) == 0) + { +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.2.7.1 General Capability Set (GENERAL_CAPS_SET) extendedPDU " + "should always set RDPDR_CLIENT_DISPLAY_NAME_PDU[0x00000002]"); + } + +- context->priv->ExtendedPDU = ExtendedPdu; +- context->priv->UserLoggedOnPdu = (ExtendedPdu & RDPDR_USER_LOGGEDON_PDU) != 0; ++ priv->ExtendedPDU = ExtendedPdu; ++ priv->UserLoggedOnPdu = (ExtendedPdu & RDPDR_USER_LOGGEDON_PDU) != 0; + + if (ExtraFlags2 != 0) + { +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.2.7.1 General Capability Set (GENERAL_CAPS_SET) ExtraFlags2 is " + "reserved for future use, expected value 0 got %" PRIu32, + ExtraFlags2); +@@ -637,14 +648,15 @@ static UINT rdpdr_server_write_general_capability_set(RdpdrServerContext* contex + extendedPdu |= RDPDR_CLIENT_DISPLAY_NAME_PDU; /* always set */ + extendedPdu |= RDPDR_DEVICE_REMOVE_PDUS; /* optional */ + +- if (context->priv->UserLoggedOnPdu) ++ RdpdrServerPrivate* priv = context->priv; ++ if (priv->UserLoggedOnPdu) + extendedPdu |= RDPDR_USER_LOGGEDON_PDU; /* optional */ + + extraFlags1 = 0; + extraFlags1 |= ENABLE_ASYNCIO; /* optional */ + SpecialTypeDeviceCap = 0; + +- UINT error = rdpdr_write_capset_header(context->priv->log, s, &header); ++ UINT error = rdpdr_write_capset_header(priv->log, s, &header); + if (error != CHANNEL_RC_OK) + return error; + +@@ -652,8 +664,8 @@ static UINT rdpdr_server_write_general_capability_set(RdpdrServerContext* contex + const size_t start = Stream_GetPosition(s); + Stream_Write_UINT32(s, 0); /* osType (4 bytes), ignored on receipt */ + Stream_Write_UINT32(s, 0); /* osVersion (4 bytes), unused and must be set to zero */ +- Stream_Write_UINT16(s, context->priv->VersionMajor); /* protocolMajorVersion (2 bytes) */ +- Stream_Write_UINT16(s, context->priv->VersionMinor); /* protocolMinorVersion (2 bytes) */ ++ Stream_Write_UINT16(s, priv->VersionMajor); /* protocolMajorVersion (2 bytes) */ ++ Stream_Write_UINT16(s, priv->VersionMinor); /* protocolMinorVersion (2 bytes) */ + Stream_Write_UINT32(s, ioCode1); /* ioCode1 (4 bytes) */ + Stream_Write_UINT32(s, 0); /* ioCode2 (4 bytes), must be set to zero, reserved for future use */ + Stream_Write_UINT32(s, extendedPdu); /* extendedPdu (4 bytes) */ +@@ -832,11 +844,12 @@ static UINT rdpdr_server_send_core_capability_request(RdpdrServerContext* contex + if ((context->supported & RDPDR_DTYP_SMARTCARD) != 0) + numCapabilities++; + ++ RdpdrServerPrivate* priv = context->priv; + s = Stream_New(nullptr, RDPDR_HEADER_LENGTH + 512); + + if (!s) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "Stream_New failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); + return CHANNEL_RC_NO_MEMORY; + } + +@@ -847,7 +860,7 @@ static UINT rdpdr_server_send_core_capability_request(RdpdrServerContext* contex + + if ((error = rdpdr_server_write_general_capability_set(context, s))) + { +- WLog_Print(context->priv->log, WLOG_ERROR, ++ WLog_Print(priv->log, WLOG_ERROR, + "rdpdr_server_write_general_capability_set failed with error %" PRIu32 "!", + error); + goto out; +@@ -857,7 +870,7 @@ static UINT rdpdr_server_send_core_capability_request(RdpdrServerContext* contex + { + if ((error = rdpdr_server_write_drive_capability_set(context, s))) + { +- WLog_Print(context->priv->log, WLOG_ERROR, ++ WLog_Print(priv->log, WLOG_ERROR, + "rdpdr_server_write_drive_capability_set failed with error %" PRIu32 "!", + error); + goto out; +@@ -869,7 +882,7 @@ static UINT rdpdr_server_send_core_capability_request(RdpdrServerContext* contex + { + if ((error = rdpdr_server_write_port_capability_set(context, s))) + { +- WLog_Print(context->priv->log, WLOG_ERROR, ++ WLog_Print(priv->log, WLOG_ERROR, + "rdpdr_server_write_port_capability_set failed with error %" PRIu32 "!", + error); + goto out; +@@ -880,7 +893,7 @@ static UINT rdpdr_server_send_core_capability_request(RdpdrServerContext* contex + { + if ((error = rdpdr_server_write_printer_capability_set(context, s))) + { +- WLog_Print(context->priv->log, WLOG_ERROR, ++ WLog_Print(priv->log, WLOG_ERROR, + "rdpdr_server_write_printer_capability_set failed with error %" PRIu32 "!", + error); + goto out; +@@ -891,7 +904,7 @@ static UINT rdpdr_server_send_core_capability_request(RdpdrServerContext* contex + { + if ((error = rdpdr_server_write_smartcard_capability_set(context, s))) + { +- WLog_Print(context->priv->log, WLOG_ERROR, ++ WLog_Print(priv->log, WLOG_ERROR, + "rdpdr_server_write_printer_capability_set failed with error %" PRIu32 "!", + error); + goto out; +@@ -936,9 +949,11 @@ static UINT rdpdr_server_receive_core_capability_response(RdpdrServerContext* co + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + ++ RdpdrServerPrivate* priv = context->priv; ++ + WINPR_UNUSED(header); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 4)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT16(s, numCapabilities); /* numCapabilities (2 bytes) */ +@@ -950,9 +965,9 @@ static UINT rdpdr_server_receive_core_capability_response(RdpdrServerContext* co + RDPDR_CAPABILITY_HEADER capabilityHeader = WINPR_C_ARRAY_INIT; + const size_t start = Stream_GetPosition(s); + +- if ((status = rdpdr_read_capset_header(context->priv->log, s, &capabilityHeader))) ++ if ((status = rdpdr_read_capset_header(priv->log, s, &capabilityHeader))) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", status); ++ WLog_Print(priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", status); + return status; + } + +@@ -967,8 +982,7 @@ static UINT rdpdr_server_receive_core_capability_response(RdpdrServerContext* co + if ((status = + rdpdr_server_read_general_capability_set(context, s, &capabilityHeader))) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", +- status); ++ WLog_Print(priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", status); + return status; + } + +@@ -978,8 +992,7 @@ static UINT rdpdr_server_receive_core_capability_response(RdpdrServerContext* co + if ((status = + rdpdr_server_read_printer_capability_set(context, s, &capabilityHeader))) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", +- status); ++ WLog_Print(priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", status); + return status; + } + +@@ -988,8 +1001,7 @@ static UINT rdpdr_server_receive_core_capability_response(RdpdrServerContext* co + case CAP_PORT_TYPE: + if ((status = rdpdr_server_read_port_capability_set(context, s, &capabilityHeader))) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", +- status); ++ WLog_Print(priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", status); + return status; + } + +@@ -999,8 +1011,7 @@ static UINT rdpdr_server_receive_core_capability_response(RdpdrServerContext* co + if ((status = + rdpdr_server_read_drive_capability_set(context, s, &capabilityHeader))) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", +- status); ++ WLog_Print(priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", status); + return status; + } + +@@ -1010,15 +1021,14 @@ static UINT rdpdr_server_receive_core_capability_response(RdpdrServerContext* co + if ((status = + rdpdr_server_read_smartcard_capability_set(context, s, &capabilityHeader))) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", +- status); ++ WLog_Print(priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", status); + return status; + } + + break; + + default: +- WLog_Print(context->priv->log, WLOG_WARN, "Unknown capabilityType %" PRIu16 "", ++ WLog_Print(priv->log, WLOG_WARN, "Unknown capabilityType %" PRIu16 "", + capabilityHeader.CapabilityType); + Stream_Seek(s, capabilityHeader.CapabilityLength); + return ERROR_INVALID_DATA; +@@ -1027,8 +1037,7 @@ static UINT rdpdr_server_receive_core_capability_response(RdpdrServerContext* co + const UINT16 client_dtyp = rdpdr_cap_type_to_dtyp(capabilityHeader.CapabilityType); + if ((client_dtyp != 0) && ((client_dtyp & context->supported) == 0)) + { +- WLog_Print(context->priv->log, WLOG_WARN, +- "client sent capability %s we did not announce!", ++ WLog_Print(priv->log, WLOG_WARN, "client sent capability %s we did not announce!", + rdpdr_cap_type_string(capabilityHeader.CapabilityType)); + } + caps |= client_dtyp; +@@ -1037,7 +1046,7 @@ static UINT rdpdr_server_receive_core_capability_response(RdpdrServerContext* co + const size_t diff = end - start; + if (diff != capabilityHeader.CapabilityLength + RDPDR_CAPABILITY_HEADER_LENGTH) + { +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "{capability %s[0x%04" PRIx16 "]} processed %" PRIuz + " bytes, but expected to be %" PRIu16, + rdpdr_cap_type_string(capabilityHeader.CapabilityType), +@@ -1066,19 +1075,21 @@ static UINT rdpdr_server_send_client_id_confirm(RdpdrServerContext* context) + + header.Component = RDPDR_CTYP_CORE; + header.PacketId = PAKID_CORE_CLIENTID_CONFIRM; ++ ++ RdpdrServerPrivate* priv = context->priv; + s = Stream_New(nullptr, RDPDR_HEADER_LENGTH + 8); + + if (!s) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "Stream_New failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT16(s, header.Component); /* Component (2 bytes) */ + Stream_Write_UINT16(s, header.PacketId); /* PacketId (2 bytes) */ +- Stream_Write_UINT16(s, context->priv->VersionMajor); /* VersionMajor (2 bytes) */ +- Stream_Write_UINT16(s, context->priv->VersionMinor); /* VersionMinor (2 bytes) */ +- Stream_Write_UINT32(s, context->priv->ClientId); /* ClientId (4 bytes) */ ++ Stream_Write_UINT16(s, priv->VersionMajor); /* VersionMajor (2 bytes) */ ++ Stream_Write_UINT16(s, priv->VersionMinor); /* VersionMinor (2 bytes) */ ++ Stream_Write_UINT32(s, priv->ClientId); /* ClientId (4 bytes) */ + return rdpdr_seal_send_free_request(context, s); + } + +@@ -1096,20 +1107,22 @@ static UINT rdpdr_server_receive_device_list_announce_request(RdpdrServerContext + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + ++ RdpdrServerPrivate* priv = context->priv; ++ + WINPR_UNUSED(header); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 4)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, DeviceCount); /* DeviceCount (4 bytes) */ +- WLog_Print(context->priv->log, WLOG_DEBUG, "DeviceCount: %" PRIu32 "", DeviceCount); ++ WLog_Print(priv->log, WLOG_DEBUG, "DeviceCount: %" PRIu32 "", DeviceCount); + + for (UINT32 i = 0; i < DeviceCount; i++) + { + UINT error = 0; + RdpdrDevice device = WINPR_C_ARRAY_INIT; + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 20)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 20)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, device.DeviceType); /* DeviceType (4 bytes) */ +@@ -1118,7 +1131,7 @@ static UINT rdpdr_server_receive_device_list_announce_request(RdpdrServerContext + Stream_Read_UINT32(s, device.DeviceDataLength); /* DeviceDataLength (4 bytes) */ + device.DeviceData = Stream_Pointer(s); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, device.DeviceDataLength)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, device.DeviceDataLength)) + return ERROR_INVALID_DATA; + + if (!rdpdr_add_device(context->priv, &device)) +@@ -1143,7 +1156,7 @@ static UINT rdpdr_server_receive_device_list_announce_request(RdpdrServerContext + case RDPDR_DTYP_SERIAL: + if (device.DeviceDataLength != 0) + { +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[rdpdr] RDPDR_DTYP_SERIAL::DeviceDataLength != 0 [%" PRIu32 "]", + device.DeviceDataLength); + error = ERROR_INVALID_DATA; +@@ -1156,7 +1169,7 @@ static UINT rdpdr_server_receive_device_list_announce_request(RdpdrServerContext + case RDPDR_DTYP_PARALLEL: + if (device.DeviceDataLength != 0) + { +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[rdpdr] RDPDR_DTYP_PARALLEL::DeviceDataLength != 0 [%" PRIu32 "]", + device.DeviceDataLength); + error = ERROR_INVALID_DATA; +@@ -1169,7 +1182,7 @@ static UINT rdpdr_server_receive_device_list_announce_request(RdpdrServerContext + case RDPDR_DTYP_SMARTCARD: + if (device.DeviceDataLength != 0) + { +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[rdpdr] RDPDR_DTYP_SMARTCARD::DeviceDataLength != 0 [%" PRIu32 "]", + device.DeviceDataLength); + error = ERROR_INVALID_DATA; +@@ -1180,7 +1193,7 @@ static UINT rdpdr_server_receive_device_list_announce_request(RdpdrServerContext + break; + + default: +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.2.9 Client Device List Announce Request " + "(DR_CORE_DEVICELIST_ANNOUNCE_REQ) unknown device type %04" PRIx16 + " at position %" PRIu32, +@@ -1212,26 +1225,27 @@ static UINT rdpdr_server_receive_device_list_remove_request(RdpdrServerContext* + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + ++ RdpdrServerPrivate* priv = context->priv; ++ + WINPR_UNUSED(header); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 4)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, DeviceCount); /* DeviceCount (4 bytes) */ +- WLog_Print(context->priv->log, WLOG_DEBUG, "DeviceCount: %" PRIu32 "", DeviceCount); ++ WLog_Print(priv->log, WLOG_DEBUG, "DeviceCount: %" PRIu32 "", DeviceCount); + + for (UINT32 i = 0; i < DeviceCount; i++) + { + UINT error = 0; + const RdpdrDevice* device = nullptr; + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 4)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, DeviceId); /* DeviceId (4 bytes) */ + device = rdpdr_get_device_by_id(context->priv, DeviceId); +- WLog_Print(context->priv->log, WLOG_DEBUG, "Device %" PRIu32 " Id: 0x%08" PRIX32 "", i, +- DeviceId); ++ WLog_Print(priv->log, WLOG_DEBUG, "Device %" PRIu32 " Id: 0x%08" PRIX32 "", i, DeviceId); + DeviceType = 0; + if (device) + DeviceType = device->DeviceType; +@@ -1301,7 +1315,9 @@ static UINT rdpdr_server_receive_io_create_request(RdpdrServerContext* context, + UINT32 PathLength = 0; + + WINPR_ASSERT(context); +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 32)) ++ ++ RdpdrServerPrivate* priv = context->priv; ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, DesiredAccess); +@@ -1312,13 +1328,13 @@ static UINT rdpdr_server_receive_io_create_request(RdpdrServerContext* context, + Stream_Read_UINT32(s, CreateOptions); + Stream_Read_UINT32(s, PathLength); + +- path = rdpdr_read_ustring(context->priv->log, s, PathLength); ++ path = rdpdr_read_ustring(priv->log, s, PathLength); + if (!path && (PathLength > 0)) + return ERROR_INVALID_DATA; + +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.1.4.1 Device Create Request (DR_CREATE_REQ) not implemented"); +- WLog_Print(context->priv->log, WLOG_WARN, "TODO"); ++ WLog_Print(priv->log, WLOG_WARN, "TODO"); + + return CHANNEL_RC_OK; + } +@@ -1331,14 +1347,16 @@ static UINT rdpdr_server_receive_io_close_request(RdpdrServerContext* context, w + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 32)) ++ RdpdrServerPrivate* priv = context->priv; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + + Stream_Seek(s, 32); /* Padding */ + +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.1.4.2 Device Close Request (DR_CLOSE_REQ) not implemented"); +- WLog_Print(context->priv->log, WLOG_WARN, "TODO"); ++ WLog_Print(priv->log, WLOG_WARN, "TODO"); + + return CHANNEL_RC_OK; + } +@@ -1353,19 +1371,21 @@ static UINT rdpdr_server_receive_io_read_request(RdpdrServerContext* context, wS + + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 32)) ++ ++ RdpdrServerPrivate* priv = context->priv; ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, Length); + Stream_Read_UINT64(s, Offset); + Stream_Seek(s, 20); /* Padding */ + +- WLog_Print(context->priv->log, WLOG_DEBUG, "Got Offset [0x%016" PRIx64 "], Length %" PRIu32, +- Offset, Length); ++ WLog_Print(priv->log, WLOG_DEBUG, "Got Offset [0x%016" PRIx64 "], Length %" PRIu32, Offset, ++ Length); + +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.1.4.3 Device Read Request (DR_READ_REQ) not implemented"); +- WLog_Print(context->priv->log, WLOG_WARN, "TODO"); ++ WLog_Print(priv->log, WLOG_WARN, "TODO"); + + return CHANNEL_RC_OK; + } +@@ -1381,24 +1401,26 @@ static UINT rdpdr_server_receive_io_write_request(RdpdrServerContext* context, w + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 32)) ++ RdpdrServerPrivate* priv = context->priv; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, Length); + Stream_Read_UINT64(s, Offset); + Stream_Seek(s, 20); /* Padding */ + +- WLog_Print(context->priv->log, WLOG_DEBUG, "Got Offset [0x%016" PRIx64 "], Length %" PRIu32, +- Offset, Length); ++ WLog_Print(priv->log, WLOG_DEBUG, "Got Offset [0x%016" PRIx64 "], Length %" PRIu32, Offset, ++ Length); + + const BYTE* data = Stream_ConstPointer(s); +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, Length)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, Length)) + return ERROR_INVALID_DATA; + Stream_Seek(s, Length); + +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.1.4.4 Device Write Request (DR_WRITE_REQ) not implemented"); +- WLog_Print(context->priv->log, WLOG_WARN, "TODO: parse %p", (const void*)data); ++ WLog_Print(priv->log, WLOG_WARN, "TODO: parse %p", (const void*)data); + + return CHANNEL_RC_OK; + } +@@ -1415,7 +1437,9 @@ static UINT rdpdr_server_receive_io_device_control_request(RdpdrServerContext* c + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 32)) ++ RdpdrServerPrivate* priv = context->priv; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, OutputBufferLength); +@@ -1424,14 +1448,13 @@ static UINT rdpdr_server_receive_io_device_control_request(RdpdrServerContext* c + Stream_Seek(s, 20); /* Padding */ + + const BYTE* InputBuffer = Stream_ConstPointer(s); +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, InputBufferLength)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, InputBufferLength)) + return ERROR_INVALID_DATA; + Stream_Seek(s, InputBufferLength); + +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.1.4.5 Device Control Request (DR_CONTROL_REQ) not implemented"); +- WLog_Print(context->priv->log, WLOG_WARN, +- "TODO: parse %p [%" PRIu32 "], OutputBufferLength=%" PRIu32, ++ WLog_Print(priv->log, WLOG_WARN, "TODO: parse %p [%" PRIu32 "], OutputBufferLength=%" PRIu32, + (const void*)InputBuffer, InputBufferLength, OutputBufferLength); + + return CHANNEL_RC_OK; +@@ -1445,26 +1468,28 @@ static UINT rdpdr_server_receive_io_query_volume_information_request( + UINT32 Length = 0; + + WINPR_ASSERT(context); +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 32)) ++ ++ RdpdrServerPrivate* priv = context->priv; ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, FsInformationClass); + Stream_Read_UINT32(s, Length); + Stream_Seek(s, 24); /* Padding */ + +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ WLog_Print(priv->log, WLOG_DEBUG, + "Got FSInformationClass %s [0x%08" PRIx32 "], Length %" PRIu32, + FSInformationClass2Tag(FsInformationClass), FsInformationClass, Length); + + const BYTE* QueryVolumeBuffer = Stream_ConstPointer(s); +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, Length)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, Length)) + return ERROR_INVALID_DATA; + Stream_Seek(s, Length); + +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.3.3.6 Server Drive Query Volume Information Request " + "(DR_DRIVE_QUERY_VOLUME_INFORMATION_REQ) not implemented"); +- WLog_Print(context->priv->log, WLOG_WARN, "TODO: parse %p", (const void*)QueryVolumeBuffer); ++ WLog_Print(priv->log, WLOG_WARN, "TODO: parse %p", (const void*)QueryVolumeBuffer); + + return CHANNEL_RC_OK; + } +@@ -1479,26 +1504,28 @@ static UINT rdpdr_server_receive_io_set_volume_information_request( + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 32)) ++ RdpdrServerPrivate* priv = context->priv; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, FsInformationClass); + Stream_Read_UINT32(s, Length); + Stream_Seek(s, 24); /* Padding */ + +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ WLog_Print(priv->log, WLOG_DEBUG, + "Got FSInformationClass %s [0x%08" PRIx32 "], Length %" PRIu32, + FSInformationClass2Tag(FsInformationClass), FsInformationClass, Length); + + const BYTE* SetVolumeBuffer = Stream_ConstPointer(s); +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, Length)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, Length)) + return ERROR_INVALID_DATA; + Stream_Seek(s, Length); + +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.3.3.7 Server Drive Set Volume Information Request " + "(DR_DRIVE_SET_VOLUME_INFORMATION_REQ) not implemented"); +- WLog_Print(context->priv->log, WLOG_WARN, "TODO: parse %p", (const void*)SetVolumeBuffer); ++ WLog_Print(priv->log, WLOG_WARN, "TODO: parse %p", (const void*)SetVolumeBuffer); + + return CHANNEL_RC_OK; + } +@@ -1515,26 +1542,28 @@ static UINT rdpdr_server_receive_io_query_information_request(RdpdrServerContext + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 32)) ++ RdpdrServerPrivate* priv = context->priv; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, FsInformationClass); + Stream_Read_UINT32(s, Length); + Stream_Seek(s, 24); /* Padding */ + +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ WLog_Print(priv->log, WLOG_DEBUG, + "Got FSInformationClass %s [0x%08" PRIx32 "], Length %" PRIu32, + FSInformationClass2Tag(FsInformationClass), FsInformationClass, Length); + + const BYTE* QueryBuffer = Stream_ConstPointer(s); +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, Length)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, Length)) + return ERROR_INVALID_DATA; + Stream_Seek(s, Length); + +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.3.3.8 Server Drive Query Information Request " + "(DR_DRIVE_QUERY_INFORMATION_REQ) not implemented"); +- WLog_Print(context->priv->log, WLOG_WARN, "TODO: parse %p", (const void*)QueryBuffer); ++ WLog_Print(priv->log, WLOG_WARN, "TODO: parse %p", (const void*)QueryBuffer); + + return CHANNEL_RC_OK; + } +@@ -1550,26 +1579,28 @@ static UINT rdpdr_server_receive_io_set_information_request(RdpdrServerContext* + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 32)) ++ RdpdrServerPrivate* priv = context->priv; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, FsInformationClass); + Stream_Read_UINT32(s, Length); + Stream_Seek(s, 24); /* Padding */ + +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ WLog_Print(priv->log, WLOG_DEBUG, + "Got FSInformationClass %s [0x%08" PRIx32 "], Length %" PRIu32, + FSInformationClass2Tag(FsInformationClass), FsInformationClass, Length); + + const BYTE* SetBuffer = Stream_ConstPointer(s); +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, Length)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, Length)) + return ERROR_INVALID_DATA; + Stream_Seek(s, Length); + +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.3.3.9 Server Drive Set Information Request " + "(DR_DRIVE_SET_INFORMATION_REQ) not implemented"); +- WLog_Print(context->priv->log, WLOG_WARN, "TODO: parse %p", (const void*)SetBuffer); ++ WLog_Print(priv->log, WLOG_WARN, "TODO: parse %p", (const void*)SetBuffer); + + return CHANNEL_RC_OK; + } +@@ -1586,7 +1617,9 @@ static UINT rdpdr_server_receive_io_query_directory_request(RdpdrServerContext* + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 32)) ++ RdpdrServerPrivate* priv = context->priv; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, FsInformationClass); +@@ -1594,22 +1627,22 @@ static UINT rdpdr_server_receive_io_query_directory_request(RdpdrServerContext* + Stream_Read_UINT32(s, PathLength); + Stream_Seek(s, 23); /* Padding */ + +- const WCHAR* wPath = rdpdr_read_ustring(context->priv->log, s, PathLength); ++ const WCHAR* wPath = rdpdr_read_ustring(priv->log, s, PathLength); + if (!wPath && (PathLength > 0)) + return ERROR_INVALID_DATA; + + char* Path = ConvertWCharNToUtf8Alloc(wPath, PathLength / sizeof(WCHAR), nullptr); +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ WLog_Print(priv->log, WLOG_DEBUG, + "Got FSInformationClass %s [0x%08" PRIx32 "], InitialQuery [%" PRIu8 + "] Path[%" PRIu32 "] %s", + FSInformationClass2Tag(FsInformationClass), FsInformationClass, InitialQuery, + PathLength, Path); + free(Path); + +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.3.3.10 Server Drive Query Directory Request " + "(DR_DRIVE_QUERY_DIRECTORY_REQ) not implemented"); +- WLog_Print(context->priv->log, WLOG_WARN, "TODO"); ++ WLog_Print(priv->log, WLOG_WARN, "TODO"); + + return CHANNEL_RC_OK; + } +@@ -1626,17 +1659,19 @@ static UINT rdpdr_server_receive_io_change_directory_request(RdpdrServerContext* + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 32)) ++ RdpdrServerPrivate* priv = context->priv; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT8(s, WatchTree); + Stream_Read_UINT32(s, CompletionFilter); + Stream_Seek(s, 27); /* Padding */ + +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.3.3.11 Server Drive NotifyChange Directory Request " + "(DR_DRIVE_NOTIFY_CHANGE_DIRECTORY_REQ) not implemented"); +- WLog_Print(context->priv->log, WLOG_WARN, "TODO"); ++ WLog_Print(priv->log, WLOG_WARN, "TODO"); + + return CHANNEL_RC_OK; + } +@@ -1649,6 +1684,7 @@ static UINT rdpdr_server_receive_io_directory_control_request(RdpdrServerContext + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + ++ RdpdrServerPrivate* priv = context->priv; + switch (MinorFunction) + { + case IRP_MN_QUERY_DIRECTORY: +@@ -1658,7 +1694,7 @@ static UINT rdpdr_server_receive_io_directory_control_request(RdpdrServerContext + return rdpdr_server_receive_io_change_directory_request(context, s, DeviceId, FileId, + CompletionId); + default: +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.1.4 Device I/O Request (DR_DEVICE_IOREQUEST) " + "MajorFunction=%s, MinorFunction=%08" PRIx32 " is not supported", + rdpdr_irp_string(IRP_MJ_DIRECTORY_CONTROL), MinorFunction); +@@ -1674,7 +1710,9 @@ static UINT rdpdr_server_receive_io_lock_control_request(RdpdrServerContext* con + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 32)) ++ RdpdrServerPrivate* priv = context->priv; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + + const uint32_t Operation = Stream_Get_UINT32(s); +@@ -1682,7 +1720,7 @@ static UINT rdpdr_server_receive_io_lock_control_request(RdpdrServerContext* con + const uint32_t NumLocks = Stream_Get_UINT32(s); + Stream_Seek(s, 20); /* Padding */ + +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ WLog_Print(priv->log, WLOG_DEBUG, + "IRP_MJ_LOCK_CONTROL, Operation=%s, Lock=0x%08" PRIx32 ", NumLocks=%" PRIu32, + DR_DRIVE_LOCK_REQ2str(Operation), Lock, NumLocks); + +@@ -1693,22 +1731,22 @@ static UINT rdpdr_server_receive_io_lock_control_request(RdpdrServerContext* con + UINT64 Length = 0; + UINT64 Offset = 0; + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 16)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 16)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT64(s, Length); + Stream_Read_UINT64(s, Offset); + +- WLog_Print(context->priv->log, WLOG_DEBUG, +- "Locking at Offset=0x%08" PRIx64 " [Length %" PRIu64 "]", Offset, Length); ++ WLog_Print(priv->log, WLOG_DEBUG, "Locking at Offset=0x%08" PRIx64 " [Length %" PRIu64 "]", ++ Offset, Length); + } + +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.3.3.12 Server Drive Lock Control Request (DR_DRIVE_LOCK_REQ) " + "[Lock=0x%08" PRIx32 "]" + "not implemented", + Lock); +- WLog_Print(context->priv->log, WLOG_WARN, "TODO"); ++ WLog_Print(priv->log, WLOG_WARN, "TODO"); + + return CHANNEL_RC_OK; + } +@@ -1726,7 +1764,9 @@ static UINT rdpdr_server_receive_device_io_request(RdpdrServerContext* context, + WINPR_ASSERT(context->priv); + WINPR_ASSERT(header); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 20)) ++ RdpdrServerPrivate* priv = context->priv; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 20)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, DeviceId); +@@ -1735,7 +1775,7 @@ static UINT rdpdr_server_receive_device_io_request(RdpdrServerContext* context, + Stream_Read_UINT32(s, MajorFunction); + Stream_Read_UINT32(s, MinorFunction); + if ((MinorFunction != 0) && (MajorFunction != IRP_MJ_DIRECTORY_CONTROL)) +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.1.4 Device I/O Request (DR_DEVICE_IOREQUEST) MajorFunction=%s, " + "MinorFunction=0x%08" PRIx32 " != 0", + rdpdr_irp_string(MajorFunction), MinorFunction); +@@ -1777,9 +1817,9 @@ static UINT rdpdr_server_receive_device_io_request(RdpdrServerContext* context, + + default: + WLog_Print( +- context->priv->log, WLOG_WARN, ++ priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.1.4 Device I/O Request (DR_DEVICE_IOREQUEST) not implemented"); +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "got DeviceId=0x%08" PRIx32 ", FileId=0x%08" PRIx32 + ", CompletionId=0x%08" PRIx32 ", MajorFunction=0x%08" PRIx32 + ", MinorFunction=0x%08" PRIx32, +@@ -1806,22 +1846,24 @@ static UINT rdpdr_server_receive_device_io_completion(RdpdrServerContext* contex + WINPR_ASSERT(s); + WINPR_ASSERT(header); + ++ RdpdrServerPrivate* priv = context->priv; ++ + WINPR_UNUSED(header); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 12)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 12)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, deviceId); + Stream_Read_UINT32(s, completionId); + Stream_Read_UINT32(s, ioStatus); +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ WLog_Print(priv->log, WLOG_DEBUG, + "deviceId=%" PRIu32 ", completionId=0x%" PRIx32 ", ioStatus=0x%" PRIx32 "", deviceId, + completionId, ioStatus); + irp = rdpdr_server_dequeue_irp(context, completionId); + + if (!irp) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "IRP not found for completionId=0x%" PRIx32 "", ++ WLog_Print(priv->log, WLOG_ERROR, "IRP not found for completionId=0x%" PRIx32 "", + completionId); + return CHANNEL_RC_OK; + } +@@ -1850,11 +1892,13 @@ static UINT rdpdr_server_send_user_logged_on(RdpdrServerContext* context) + + header.Component = RDPDR_CTYP_CORE; + header.PacketId = PAKID_CORE_USER_LOGGEDON; ++ ++ RdpdrServerPrivate* priv = context->priv; + s = Stream_New(nullptr, RDPDR_HEADER_LENGTH); + + if (!s) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "Stream_New failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); + return CHANNEL_RC_NO_MEMORY; + } + +@@ -1877,7 +1921,9 @@ static UINT rdpdr_server_receive_prn_cache_add_printer(RdpdrServerContext* conte + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 24)) ++ RdpdrServerPrivate* priv = context->priv; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 24)) + return ERROR_INVALID_DATA; + + Stream_Read(s, PortDosName, 8); +@@ -1886,23 +1932,23 @@ static UINT rdpdr_server_receive_prn_cache_add_printer(RdpdrServerContext* conte + Stream_Read_UINT32(s, PrinterNameLen); + Stream_Read_UINT32(s, CachedFieldsLen); + +- PnPName = rdpdr_read_ustring(context->priv->log, s, PnPNameLen); ++ PnPName = rdpdr_read_ustring(priv->log, s, PnPNameLen); + if (!PnPName && (PnPNameLen > 0)) + return ERROR_INVALID_DATA; +- DriverName = rdpdr_read_ustring(context->priv->log, s, DriverNameLen); ++ DriverName = rdpdr_read_ustring(priv->log, s, DriverNameLen); + if (!DriverName && (DriverNameLen > 0)) + return ERROR_INVALID_DATA; +- PrinterName = rdpdr_read_ustring(context->priv->log, s, PrinterNameLen); ++ PrinterName = rdpdr_read_ustring(priv->log, s, PrinterNameLen); + if (!PrinterName && (PrinterNameLen > 0)) + return ERROR_INVALID_DATA; + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, CachedFieldsLen)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, CachedFieldsLen)) + return ERROR_INVALID_DATA; + Stream_Seek(s, CachedFieldsLen); + +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEPC] 2.2.2.3 Add Printer Cachedata (DR_PRN_ADD_CACHEDATA) not implemented"); +- WLog_Print(context->priv->log, WLOG_WARN, "TODO"); ++ WLog_Print(priv->log, WLOG_WARN, "TODO"); + return CHANNEL_RC_OK; + } + +@@ -1914,25 +1960,27 @@ static UINT rdpdr_server_receive_prn_cache_update_printer(RdpdrServerContext* co + + WINPR_ASSERT(context); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 8)) ++ RdpdrServerPrivate* priv = context->priv; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 8)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, PrinterNameLen); + Stream_Read_UINT32(s, CachedFieldsLen); + +- PrinterName = rdpdr_read_ustring(context->priv->log, s, PrinterNameLen); ++ PrinterName = rdpdr_read_ustring(priv->log, s, PrinterNameLen); + if (!PrinterName && (PrinterNameLen > 0)) + return ERROR_INVALID_DATA; + + const BYTE* config = Stream_ConstPointer(s); +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, CachedFieldsLen)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, CachedFieldsLen)) + return ERROR_INVALID_DATA; + Stream_Seek(s, CachedFieldsLen); + + WLog_Print( +- context->priv->log, WLOG_WARN, ++ priv->log, WLOG_WARN, + "[MS-RDPEPC] 2.2.2.4 Update Printer Cachedata (DR_PRN_UPDATE_CACHEDATA) not implemented"); +- WLog_Print(context->priv->log, WLOG_WARN, "TODO: parse %p", (const void*)config); ++ WLog_Print(priv->log, WLOG_WARN, "TODO: parse %p", (const void*)config); + return CHANNEL_RC_OK; + } + +@@ -1944,19 +1992,21 @@ static UINT rdpdr_server_receive_prn_cache_delete_printer(RdpdrServerContext* co + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 4)) ++ RdpdrServerPrivate* priv = context->priv; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, PrinterNameLen); + +- PrinterName = rdpdr_read_ustring(context->priv->log, s, PrinterNameLen); ++ PrinterName = rdpdr_read_ustring(priv->log, s, PrinterNameLen); + if (!PrinterName && (PrinterNameLen > 0)) + return ERROR_INVALID_DATA; + + WLog_Print( +- context->priv->log, WLOG_WARN, ++ priv->log, WLOG_WARN, + "[MS-RDPEPC] 2.2.2.5 Delete Printer Cachedata (DR_PRN_DELETE_CACHEDATA) not implemented"); +- WLog_Print(context->priv->log, WLOG_WARN, "TODO"); ++ WLog_Print(priv->log, WLOG_WARN, "TODO"); + return CHANNEL_RC_OK; + } + +@@ -1970,23 +2020,25 @@ static UINT rdpdr_server_receive_prn_cache_rename_cachedata(RdpdrServerContext* + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 8)) ++ RdpdrServerPrivate* priv = context->priv; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 8)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, OldPrinterNameLen); + Stream_Read_UINT32(s, NewPrinterNameLen); + +- OldPrinterName = rdpdr_read_ustring(context->priv->log, s, OldPrinterNameLen); ++ OldPrinterName = rdpdr_read_ustring(priv->log, s, OldPrinterNameLen); + if (!OldPrinterName && (OldPrinterNameLen > 0)) + return ERROR_INVALID_DATA; +- NewPrinterName = rdpdr_read_ustring(context->priv->log, s, NewPrinterNameLen); ++ NewPrinterName = rdpdr_read_ustring(priv->log, s, NewPrinterNameLen); + if (!NewPrinterName && (NewPrinterNameLen > 0)) + return ERROR_INVALID_DATA; + + WLog_Print( +- context->priv->log, WLOG_WARN, ++ priv->log, WLOG_WARN, + "[MS-RDPEPC] 2.2.2.6 Rename Printer Cachedata (DR_PRN_RENAME_CACHEDATA) not implemented"); +- WLog_Print(context->priv->log, WLOG_WARN, "TODO"); ++ WLog_Print(priv->log, WLOG_WARN, "TODO"); + return CHANNEL_RC_OK; + } + +@@ -2000,7 +2052,8 @@ rdpdr_server_receive_prn_cache_data_request(RdpdrServerContext* context, wStream + WINPR_ASSERT(context->priv); + WINPR_ASSERT(header); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 4)) ++ RdpdrServerPrivate* priv = context->priv; ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, EventId); +@@ -2015,7 +2068,7 @@ rdpdr_server_receive_prn_cache_data_request(RdpdrServerContext* context, wStream + case RDPDR_RENAME_PRINTER_EVENT: + return rdpdr_server_receive_prn_cache_rename_cachedata(context, s); + default: +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEPC] PAKID_PRN_CACHE_DATA unknown EventId=0x%08" PRIx32, EventId); + return ERROR_INVALID_DATA; + } +@@ -2031,17 +2084,19 @@ static UINT rdpdr_server_receive_prn_using_xps_request(RdpdrServerContext* conte + WINPR_ASSERT(context->priv); + WINPR_ASSERT(header); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 8)) ++ RdpdrServerPrivate* priv = context->priv; ++ ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 8)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, PrinterId); + Stream_Read_UINT32(s, Flags); + + WLog_Print( +- context->priv->log, WLOG_WARN, ++ priv->log, WLOG_WARN, + "[MS-RDPEPC] 2.2.2.2 Server Printer Set XPS Mode (DR_PRN_USING_XPS) not implemented"); +- WLog_Print(context->priv->log, WLOG_WARN, "PrinterId=0x%08" PRIx32 ", Flags=0x%08" PRIx32, +- PrinterId, Flags); ++ WLog_Print(priv->log, WLOG_WARN, "PrinterId=0x%08" PRIx32 ", Flags=0x%08" PRIx32, PrinterId, ++ Flags); + return CHANNEL_RC_OK; + } + +@@ -2059,7 +2114,8 @@ static UINT rdpdr_server_receive_pdu(RdpdrServerContext* context, wStream* s, + WINPR_ASSERT(s); + WINPR_ASSERT(header); + +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ RdpdrServerPrivate* priv = context->priv; ++ WLog_Print(priv->log, WLOG_DEBUG, + "receiving message {Component %s[%04" PRIx16 "], PacketId %s[%04" PRIx16 "]", + rdpdr_component_string(header->Component), header->Component, + rdpdr_packetid_string(header->PacketId), header->PacketId); +@@ -2069,7 +2125,7 @@ static UINT rdpdr_server_receive_pdu(RdpdrServerContext* context, wStream* s, + switch (header->PacketId) + { + case PAKID_CORE_SERVER_ANNOUNCE: +- WLog_Print(context->priv->log, WLOG_ERROR, ++ WLog_Print(priv->log, WLOG_ERROR, + "[MS-RDPEFS] 2.2.2.2 Server Announce Request " + "(DR_CORE_SERVER_ANNOUNCE_REQ) must not be sent by a client!"); + break; +@@ -2087,13 +2143,13 @@ static UINT rdpdr_server_receive_pdu(RdpdrServerContext* context, wStream* s, + break; + + case PAKID_CORE_USER_LOGGEDON: +- WLog_Print(context->priv->log, WLOG_ERROR, ++ WLog_Print(priv->log, WLOG_ERROR, + "[MS-RDPEFS] 2.2.2.5 Server User Logged On (DR_CORE_USER_LOGGEDON) " + "must not be sent by a client!"); + break; + + case PAKID_CORE_SERVER_CAPABILITY: +- WLog_Print(context->priv->log, WLOG_ERROR, ++ WLog_Print(priv->log, WLOG_ERROR, + "[MS-RDPEFS] 2.2.2.7 Server Core Capability Request " + "(DR_CORE_CAPABILITY_REQ) must not be sent by a client!"); + break; +@@ -2102,7 +2158,7 @@ static UINT rdpdr_server_receive_pdu(RdpdrServerContext* context, wStream* s, + error = rdpdr_server_receive_core_capability_response(context, s, header); + if (error == CHANNEL_RC_OK) + { +- if (context->priv->UserLoggedOnPdu) ++ if (priv->UserLoggedOnPdu) + error = rdpdr_server_send_user_logged_on(context); + } + +@@ -2117,7 +2173,7 @@ static UINT rdpdr_server_receive_pdu(RdpdrServerContext* context, wStream* s, + break; + + case PAKID_CORE_DEVICE_REPLY: +- WLog_Print(context->priv->log, WLOG_ERROR, ++ WLog_Print(priv->log, WLOG_ERROR, + "[MS-RDPEFS] 2.2.2.1 Server Device Announce Response " + "(DR_CORE_DEVICE_ANNOUNCE_RSP) must not be sent by a client!"); + break; +@@ -2131,7 +2187,7 @@ static UINT rdpdr_server_receive_pdu(RdpdrServerContext* context, wStream* s, + break; + + default: +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "Unknown RDPDR_HEADER.Component: %s [0x%04" PRIx16 "], PacketId: %s", + rdpdr_component_string(header->Component), header->Component, + rdpdr_packetid_string(header->PacketId)); +@@ -2151,7 +2207,7 @@ static UINT rdpdr_server_receive_pdu(RdpdrServerContext* context, wStream* s, + break; + + default: +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "Unknown RDPDR_HEADER.Component: %s [0x%04" PRIx16 "], PacketId: %s", + rdpdr_component_string(header->Component), header->Component, + rdpdr_packetid_string(header->PacketId)); +@@ -2160,7 +2216,7 @@ static UINT rdpdr_server_receive_pdu(RdpdrServerContext* context, wStream* s, + } + else + { +- WLog_Print(context->priv->log, WLOG_WARN, ++ WLog_Print(priv->log, WLOG_WARN, + "Unknown RDPDR_HEADER.Component: %s [0x%04" PRIx16 "], PacketId: %s", + rdpdr_component_string(header->Component), header->Component, + rdpdr_packetid_string(header->PacketId)); +@@ -2184,14 +2240,16 @@ static DWORD WINAPI rdpdr_server_thread(LPVOID arg) + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + ++ RdpdrServerPrivate* priv = context->priv; ++ + if (!s) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "Stream_New failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); + error = CHANNEL_RC_NO_MEMORY; + goto out; + } + +- if (WTSVirtualChannelQuery(context->priv->ChannelHandle, WTSVirtualEventHandle, &buffer, ++ if (WTSVirtualChannelQuery(priv->ChannelHandle, WTSVirtualEventHandle, &buffer, + &BytesReturned) == TRUE) + { + if (BytesReturned == sizeof(HANDLE)) +@@ -2202,11 +2260,11 @@ static DWORD WINAPI rdpdr_server_thread(LPVOID arg) + + nCount = 0; + events[nCount++] = ChannelEvent; +- events[nCount++] = context->priv->StopEvent; ++ events[nCount++] = priv->StopEvent; + + if ((error = rdpdr_server_send_announce_request(context))) + { +- WLog_Print(context->priv->log, WLOG_ERROR, ++ WLog_Print(priv->log, WLOG_ERROR, + "rdpdr_server_send_announce_request failed with error %" PRIu32 "!", error); + goto out_stream; + } +@@ -2220,42 +2278,42 @@ static DWORD WINAPI rdpdr_server_thread(LPVOID arg) + if (status == WAIT_FAILED) + { + error = GetLastError(); +- WLog_Print(context->priv->log, WLOG_ERROR, ++ WLog_Print(priv->log, WLOG_ERROR, + "WaitForMultipleObjects failed with error %" PRIu32 "!", error); + goto out_stream; + } + +- status = WaitForSingleObject(context->priv->StopEvent, 0); ++ status = WaitForSingleObject(priv->StopEvent, 0); + + if (status == WAIT_FAILED) + { + error = GetLastError(); +- WLog_Print(context->priv->log, WLOG_ERROR, +- "WaitForSingleObject failed with error %" PRIu32 "!", error); ++ WLog_Print(priv->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!", ++ error); + goto out_stream; + } + + if (status == WAIT_OBJECT_0) + break; + +- if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0, nullptr, 0, &BytesReturned)) ++ if (!WTSVirtualChannelRead(priv->ChannelHandle, 0, nullptr, 0, &BytesReturned)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "WTSVirtualChannelRead failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "WTSVirtualChannelRead failed!"); + error = ERROR_INTERNAL_ERROR; + break; + } + if (!Stream_EnsureRemainingCapacity(s, BytesReturned)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!"); + error = ERROR_INTERNAL_ERROR; + break; + } + + capacity = MIN(Stream_Capacity(s), UINT32_MAX); +- if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0, Stream_BufferAs(s, char), ++ if (!WTSVirtualChannelRead(priv->ChannelHandle, 0, Stream_BufferAs(s, char), + (ULONG)capacity, &BytesReturned)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "WTSVirtualChannelRead failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "WTSVirtualChannelRead failed!"); + error = ERROR_INTERNAL_ERROR; + break; + } +@@ -2278,7 +2336,7 @@ static DWORD WINAPI rdpdr_server_thread(LPVOID arg) + + if ((error = rdpdr_server_receive_pdu(context, s, &header))) + { +- WLog_Print(context->priv->log, WLOG_ERROR, ++ WLog_Print(priv->log, WLOG_ERROR, + "rdpdr_server_receive_pdu failed with error %" PRIu32 "!", error); + goto out_stream; + } +@@ -2306,27 +2364,28 @@ static UINT rdpdr_server_start(RdpdrServerContext* context) + { + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); +- context->priv->ChannelHandle = ++ ++ RdpdrServerPrivate* priv = context->priv; ++ priv->ChannelHandle = + WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, RDPDR_SVC_CHANNEL_NAME); + +- if (!context->priv->ChannelHandle) ++ if (!priv->ChannelHandle) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "WTSVirtualChannelOpen failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "WTSVirtualChannelOpen failed!"); + return CHANNEL_RC_BAD_CHANNEL; + } + +- if (!(context->priv->StopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr))) ++ if (!(priv->StopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr))) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "CreateEvent failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "CreateEvent failed!"); + return ERROR_INTERNAL_ERROR; + } + +- if (!(context->priv->Thread = +- CreateThread(nullptr, 0, rdpdr_server_thread, (void*)context, 0, nullptr))) ++ if (!(priv->Thread = CreateThread(nullptr, 0, rdpdr_server_thread, (void*)context, 0, nullptr))) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "CreateThread failed!"); +- (void)CloseHandle(context->priv->StopEvent); +- context->priv->StopEvent = nullptr; ++ WLog_Print(priv->log, WLOG_ERROR, "CreateThread failed!"); ++ (void)CloseHandle(priv->StopEvent); ++ priv->StopEvent = nullptr; + return ERROR_INTERNAL_ERROR; + } + +@@ -2344,28 +2403,30 @@ static UINT rdpdr_server_stop(RdpdrServerContext* context) + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- if (context->priv->StopEvent) ++ RdpdrServerPrivate* priv = context->priv; ++ ++ if (priv->StopEvent) + { +- (void)SetEvent(context->priv->StopEvent); ++ (void)SetEvent(priv->StopEvent); + +- if (WaitForSingleObject(context->priv->Thread, INFINITE) == WAIT_FAILED) ++ if (WaitForSingleObject(priv->Thread, INFINITE) == WAIT_FAILED) + { + error = GetLastError(); +- WLog_Print(context->priv->log, WLOG_ERROR, +- "WaitForSingleObject failed with error %" PRIu32 "!", error); ++ WLog_Print(priv->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!", ++ error); + return error; + } + +- (void)CloseHandle(context->priv->Thread); +- context->priv->Thread = nullptr; +- (void)CloseHandle(context->priv->StopEvent); +- context->priv->StopEvent = nullptr; ++ (void)CloseHandle(priv->Thread); ++ priv->Thread = nullptr; ++ (void)CloseHandle(priv->StopEvent); ++ priv->StopEvent = nullptr; + } + +- if (context->priv->ChannelHandle) ++ if (priv->ChannelHandle) + { +- (void)WTSVirtualChannelClose(context->priv->ChannelHandle); +- context->priv->ChannelHandle = nullptr; ++ (void)WTSVirtualChannelClose(priv->ChannelHandle); ++ priv->ChannelHandle = nullptr; + } + return CHANNEL_RC_OK; + } +@@ -2441,7 +2502,9 @@ static UINT rdpdr_server_send_device_create_request(RdpdrServerContext* context, + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ RdpdrServerPrivate* priv = context->priv; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerSendDeviceCreateRequest: deviceId=%" PRIu32 + ", path=%s, desiredAccess=0x%" PRIx32 " createOptions=0x%" PRIx32 + " createDisposition=0x%" PRIx32 "", +@@ -2452,7 +2515,7 @@ static UINT rdpdr_server_send_device_create_request(RdpdrServerContext* context, + + if (!s) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "Stream_New failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); + return CHANNEL_RC_NO_MEMORY; + } + +@@ -2488,14 +2551,16 @@ static UINT rdpdr_server_send_device_close_request(RdpdrServerContext* context, + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ RdpdrServerPrivate* priv = context->priv; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerSendDeviceCloseRequest: deviceId=%" PRIu32 ", fileId=%" PRIu32 "", + deviceId, fileId); + s = Stream_New(nullptr, 128); + + if (!s) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "Stream_New failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); + return CHANNEL_RC_NO_MEMORY; + } + +@@ -2517,7 +2582,9 @@ static UINT rdpdr_server_send_device_read_request(RdpdrServerContext* context, U + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ RdpdrServerPrivate* priv = context->priv; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerSendDeviceReadRequest: deviceId=%" PRIu32 ", fileId=%" PRIu32 + ", length=%" PRIu32 ", offset=%" PRIu32 "", + deviceId, fileId, length, offset); +@@ -2525,7 +2592,7 @@ static UINT rdpdr_server_send_device_read_request(RdpdrServerContext* context, U + + if (!s) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "Stream_New failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); + return CHANNEL_RC_NO_MEMORY; + } + +@@ -2550,7 +2617,9 @@ static UINT rdpdr_server_send_device_write_request(RdpdrServerContext* context, + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ RdpdrServerPrivate* priv = context->priv; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerSendDeviceWriteRequest: deviceId=%" PRIu32 ", fileId=%" PRIu32 + ", length=%" PRIu32 ", offset=%" PRIu32 "", + deviceId, fileId, length, offset); +@@ -2558,7 +2627,7 @@ static UINT rdpdr_server_send_device_write_request(RdpdrServerContext* context, + + if (!s) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "Stream_New failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); + return CHANNEL_RC_NO_MEMORY; + } + +@@ -2585,7 +2654,9 @@ static UINT rdpdr_server_send_device_query_directory_request(RdpdrServerContext* + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ RdpdrServerPrivate* priv = context->priv; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerSendDeviceQueryDirectoryRequest: deviceId=%" PRIu32 ", fileId=%" PRIu32 + ", path=%s", + deviceId, fileId, path); +@@ -2595,7 +2666,7 @@ static UINT rdpdr_server_send_device_query_directory_request(RdpdrServerContext* + + if (!s) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "Stream_New failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); + return CHANNEL_RC_NO_MEMORY; + } + +@@ -2635,7 +2706,9 @@ static UINT rdpdr_server_send_device_file_rename_request(RdpdrServerContext* con + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ RdpdrServerPrivate* priv = context->priv; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerSendDeviceFileNameRequest: deviceId=%" PRIu32 ", fileId=%" PRIu32 + ", path=%s", + deviceId, fileId, path); +@@ -2645,7 +2718,7 @@ static UINT rdpdr_server_send_device_file_rename_request(RdpdrServerContext* con + + if (!s) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "Stream_New failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); + return CHANNEL_RC_NO_MEMORY; + } + +@@ -2704,7 +2777,8 @@ static UINT rdpdr_server_drive_create_directory_callback2(RdpdrServerContext* co + WINPR_ASSERT(irp); + WINPR_UNUSED(s); + +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ RdpdrServerPrivate* priv = context->priv; ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerDriveCreateDirectoryCallback2: deviceId=%" PRIu32 + ", completionId=%" PRIu32 ", ioStatus=0x%" PRIx32 "", + deviceId, completionId, ioStatus); +@@ -2728,7 +2802,9 @@ static UINT rdpdr_server_drive_create_directory_callback1(RdpdrServerContext* co + WINPR_ASSERT(context->priv); + WINPR_ASSERT(s); + WINPR_ASSERT(irp); +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ ++ RdpdrServerPrivate* priv = context->priv; ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerDriveCreateDirectoryCallback1: deviceId=%" PRIu32 + ", completionId=%" PRIu32 ", ioStatus=0x%" PRIx32 "", + deviceId, completionId, ioStatus); +@@ -2742,23 +2818,23 @@ static UINT rdpdr_server_drive_create_directory_callback1(RdpdrServerContext* co + return CHANNEL_RC_OK; + } + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 5)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 5)) + return ERROR_INVALID_DATA; + + const uint32_t fileId = Stream_Get_UINT32(s); /* FileId (4 bytes) */ + const uint8_t information = Stream_Get_UINT8(s); /* Information (1 byte) */ +- WLog_Print(context->priv->log, WLOG_DEBUG, "fileId [0x%08" PRIx32 "], information %s", fileId, ++ WLog_Print(priv->log, WLOG_DEBUG, "fileId [0x%08" PRIx32 "], information %s", fileId, + fileInformation2str(information)); + + /* Setup the IRP. */ +- irp->CompletionId = context->priv->NextCompletionId++; ++ irp->CompletionId = priv->NextCompletionId++; + irp->Callback = rdpdr_server_drive_create_directory_callback2; + irp->DeviceId = deviceId; + irp->FileId = fileId; + + if (!rdpdr_server_enqueue_irp(context, irp)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); + rdpdr_server_irp_free(irp); + return ERROR_INTERNAL_ERROR; + } +@@ -2780,15 +2856,17 @@ static UINT rdpdr_server_drive_create_directory(RdpdrServerContext* context, voi + WINPR_ASSERT(context->priv); + WINPR_ASSERT(callbackData); + WINPR_ASSERT(path); ++ ++ RdpdrServerPrivate* priv = context->priv; + irp = rdpdr_server_irp_new(); + + if (!irp) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + +- irp->CompletionId = context->priv->NextCompletionId++; ++ irp->CompletionId = priv->NextCompletionId++; + irp->Callback = rdpdr_server_drive_create_directory_callback1; + irp->CallbackData = callbackData; + irp->DeviceId = deviceId; +@@ -2797,7 +2875,7 @@ static UINT rdpdr_server_drive_create_directory(RdpdrServerContext* context, voi + + if (!rdpdr_server_enqueue_irp(context, irp)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); + rdpdr_server_irp_free(irp); + return ERROR_INTERNAL_ERROR; + } +@@ -2826,7 +2904,8 @@ static UINT rdpdr_server_drive_delete_directory_callback2(RdpdrServerContext* co + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); + +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ RdpdrServerPrivate* priv = context->priv; ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerDriveDeleteDirectoryCallback2: deviceId=%" PRIu32 + ", completionId=%" PRIu32 ", ioStatus=0x%" PRIx32 "", + deviceId, completionId, ioStatus); +@@ -2849,7 +2928,9 @@ static UINT rdpdr_server_drive_delete_directory_callback1(RdpdrServerContext* co + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ ++ RdpdrServerPrivate* priv = context->priv; ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerDriveDeleteDirectoryCallback1: deviceId=%" PRIu32 + ", completionId=%" PRIu32 ", ioStatus=0x%" PRIx32 "", + deviceId, completionId, ioStatus); +@@ -2863,23 +2944,23 @@ static UINT rdpdr_server_drive_delete_directory_callback1(RdpdrServerContext* co + return CHANNEL_RC_OK; + } + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 5)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 5)) + return ERROR_INVALID_DATA; + + const uint32_t fileId = Stream_Get_UINT32(s); /* FileId (4 bytes) */ + const uint8_t information = Stream_Get_UINT8(s); /* Information (1 byte) */ +- WLog_Print(context->priv->log, WLOG_DEBUG, "fileId [0x%08" PRIx32 "], information %s", fileId, ++ WLog_Print(priv->log, WLOG_DEBUG, "fileId [0x%08" PRIx32 "], information %s", fileId, + fileInformation2str(information)); + + /* Setup the IRP. */ +- irp->CompletionId = context->priv->NextCompletionId++; ++ irp->CompletionId = priv->NextCompletionId++; + irp->Callback = rdpdr_server_drive_delete_directory_callback2; + irp->DeviceId = deviceId; + irp->FileId = fileId; + + if (!rdpdr_server_enqueue_irp(context, irp)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); + rdpdr_server_irp_free(irp); + return ERROR_INTERNAL_ERROR; + } +@@ -2901,13 +2982,15 @@ static UINT rdpdr_server_drive_delete_directory(RdpdrServerContext* context, voi + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); + ++ RdpdrServerPrivate* priv = context->priv; ++ + if (!irp) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + +- irp->CompletionId = context->priv->NextCompletionId++; ++ irp->CompletionId = priv->NextCompletionId++; + irp->Callback = rdpdr_server_drive_delete_directory_callback1; + irp->CallbackData = callbackData; + irp->DeviceId = deviceId; +@@ -2916,7 +2999,7 @@ static UINT rdpdr_server_drive_delete_directory(RdpdrServerContext* context, voi + + if (!rdpdr_server_enqueue_irp(context, irp)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); + rdpdr_server_irp_free(irp); + return ERROR_INTERNAL_ERROR; + } +@@ -2947,21 +3030,23 @@ static UINT rdpdr_server_drive_query_directory_callback2(RdpdrServerContext* con + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ ++ RdpdrServerPrivate* priv = context->priv; ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerDriveQueryDirectoryCallback2: deviceId=%" PRIu32 + ", completionId=%" PRIu32 ", ioStatus=0x%" PRIx32 "", + deviceId, completionId, ioStatus); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 4)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, length); /* Length (4 bytes) */ + + if (length > 0) + { +- if ((error = rdpdr_server_read_file_directory_information(context->priv->log, s, &fdi))) ++ if ((error = rdpdr_server_read_file_directory_information(priv->log, s, &fdi))) + { +- WLog_Print(context->priv->log, WLOG_ERROR, ++ WLog_Print(priv->log, WLOG_ERROR, + "rdpdr_server_read_file_directory_information failed with error %" PRIu32 + "!", + error); +@@ -2970,7 +3055,7 @@ static UINT rdpdr_server_drive_query_directory_callback2(RdpdrServerContext* con + } + else + { +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 1)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 1)) + return ERROR_INVALID_DATA; + + Stream_Seek(s, 1); /* Padding (1 byte) */ +@@ -2982,12 +3067,12 @@ static UINT rdpdr_server_drive_query_directory_callback2(RdpdrServerContext* con + context->OnDriveQueryDirectoryComplete(context, irp->CallbackData, ioStatus, + length > 0 ? &fdi : nullptr); + /* Setup the IRP. */ +- irp->CompletionId = context->priv->NextCompletionId++; ++ irp->CompletionId = priv->NextCompletionId++; + irp->Callback = rdpdr_server_drive_query_directory_callback2; + + if (!rdpdr_server_enqueue_irp(context, irp)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); + rdpdr_server_irp_free(irp); + return ERROR_INTERNAL_ERROR; + } +@@ -3019,7 +3104,9 @@ static UINT rdpdr_server_drive_query_directory_callback1(RdpdrServerContext* con + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ ++ RdpdrServerPrivate* priv = context->priv; ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerDriveQueryDirectoryCallback1: deviceId=%" PRIu32 + ", completionId=%" PRIu32 ", ioStatus=0x%" PRIx32 "", + deviceId, completionId, ioStatus); +@@ -3033,12 +3120,12 @@ static UINT rdpdr_server_drive_query_directory_callback1(RdpdrServerContext* con + return CHANNEL_RC_OK; + } + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 4)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + + const uint32_t fileId = Stream_Get_UINT32(s); + /* Setup the IRP. */ +- irp->CompletionId = context->priv->NextCompletionId++; ++ irp->CompletionId = priv->NextCompletionId++; + irp->Callback = rdpdr_server_drive_query_directory_callback2; + irp->DeviceId = deviceId; + irp->FileId = fileId; +@@ -3046,7 +3133,7 @@ static UINT rdpdr_server_drive_query_directory_callback1(RdpdrServerContext* con + + if (!rdpdr_server_enqueue_irp(context, irp)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); + rdpdr_server_irp_free(irp); + return ERROR_INTERNAL_ERROR; + } +@@ -3069,13 +3156,15 @@ static UINT rdpdr_server_drive_query_directory(RdpdrServerContext* context, void + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); + ++ RdpdrServerPrivate* priv = context->priv; ++ + if (!irp) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + +- irp->CompletionId = context->priv->NextCompletionId++; ++ irp->CompletionId = priv->NextCompletionId++; + irp->Callback = rdpdr_server_drive_query_directory_callback1; + irp->CallbackData = callbackData; + irp->DeviceId = deviceId; +@@ -3084,7 +3173,7 @@ static UINT rdpdr_server_drive_query_directory(RdpdrServerContext* context, void + + if (!rdpdr_server_enqueue_irp(context, irp)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); + rdpdr_server_irp_free(irp); + return ERROR_INTERNAL_ERROR; + } +@@ -3111,17 +3200,19 @@ static UINT rdpdr_server_drive_open_file_callback(RdpdrServerContext* context, w + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ ++ RdpdrServerPrivate* priv = context->priv; ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerDriveOpenFileCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 + ", ioStatus=0x%" PRIx32 "", + deviceId, completionId, ioStatus); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 5)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 5)) + return ERROR_INVALID_DATA; + + const uint32_t fileId = Stream_Get_UINT32(s); /* FileId (4 bytes) */ + const uint8_t information = Stream_Get_UINT8(s); /* Information (1 byte) */ +- WLog_Print(context->priv->log, WLOG_DEBUG, "fileId [0x%08" PRIx32 "], information %s", fileId, ++ WLog_Print(priv->log, WLOG_DEBUG, "fileId [0x%08" PRIx32 "], information %s", fileId, + fileInformation2str(information)); + + /* Invoke the open file completion routine. */ +@@ -3145,13 +3236,15 @@ static UINT rdpdr_server_drive_open_file(RdpdrServerContext* context, void* call + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); + ++ RdpdrServerPrivate* priv = context->priv; ++ + if (!irp) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + +- irp->CompletionId = context->priv->NextCompletionId++; ++ irp->CompletionId = priv->NextCompletionId++; + irp->Callback = rdpdr_server_drive_open_file_callback; + irp->CallbackData = callbackData; + irp->DeviceId = deviceId; +@@ -3160,7 +3253,7 @@ static UINT rdpdr_server_drive_open_file(RdpdrServerContext* context, void* call + + if (!rdpdr_server_enqueue_irp(context, irp)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); + rdpdr_server_irp_free(irp); + return ERROR_INTERNAL_ERROR; + } +@@ -3189,17 +3282,19 @@ static UINT rdpdr_server_drive_read_file_callback(RdpdrServerContext* context, w + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ ++ RdpdrServerPrivate* priv = context->priv; ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerDriveReadFileCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 + ", ioStatus=0x%" PRIx32 "", + deviceId, completionId, ioStatus); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 4)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, length); /* Length (4 bytes) */ + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, length)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, length)) + return ERROR_INVALID_DATA; + + if (length > 0) +@@ -3229,13 +3324,15 @@ static UINT rdpdr_server_drive_read_file(RdpdrServerContext* context, void* call + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); + ++ RdpdrServerPrivate* priv = context->priv; ++ + if (!irp) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + +- irp->CompletionId = context->priv->NextCompletionId++; ++ irp->CompletionId = priv->NextCompletionId++; + irp->Callback = rdpdr_server_drive_read_file_callback; + irp->CallbackData = callbackData; + irp->DeviceId = deviceId; +@@ -3243,7 +3340,7 @@ static UINT rdpdr_server_drive_read_file(RdpdrServerContext* context, void* call + + if (!rdpdr_server_enqueue_irp(context, irp)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); + rdpdr_server_irp_free(irp); + return ERROR_INTERNAL_ERROR; + } +@@ -3271,18 +3368,20 @@ static UINT rdpdr_server_drive_write_file_callback(RdpdrServerContext* context, + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ ++ RdpdrServerPrivate* priv = context->priv; ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerDriveWriteFileCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 + ", ioStatus=0x%" PRIx32 "", + deviceId, completionId, ioStatus); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 5)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 5)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, length); /* Length (4 bytes) */ + Stream_Seek(s, 1); /* Padding (1 byte) */ + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, length)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, length)) + return ERROR_INVALID_DATA; + + /* Invoke the write file completion routine. */ +@@ -3306,13 +3405,15 @@ static UINT rdpdr_server_drive_write_file(RdpdrServerContext* context, void* cal + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); + ++ RdpdrServerPrivate* priv = context->priv; ++ + if (!irp) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + +- irp->CompletionId = context->priv->NextCompletionId++; ++ irp->CompletionId = priv->NextCompletionId++; + irp->Callback = rdpdr_server_drive_write_file_callback; + irp->CallbackData = callbackData; + irp->DeviceId = deviceId; +@@ -3320,7 +3421,7 @@ static UINT rdpdr_server_drive_write_file(RdpdrServerContext* context, void* cal + + if (!rdpdr_server_enqueue_irp(context, irp)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); + rdpdr_server_irp_free(irp); + return ERROR_INTERNAL_ERROR; + } +@@ -3349,13 +3450,15 @@ static UINT rdpdr_server_drive_close_file_callback(RdpdrServerContext* context, + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); + +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ RdpdrServerPrivate* priv = context->priv; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerDriveCloseFileCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 + ", ioStatus=0x%" PRIx32 "", + deviceId, completionId, ioStatus); + + // padding 5 bytes +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 5)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 5)) + return ERROR_INVALID_DATA; + + Stream_Seek(s, 5); +@@ -3380,13 +3483,15 @@ static UINT rdpdr_server_drive_close_file(RdpdrServerContext* context, void* cal + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); + ++ RdpdrServerPrivate* priv = context->priv; ++ + if (!irp) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + +- irp->CompletionId = context->priv->NextCompletionId++; ++ irp->CompletionId = priv->NextCompletionId++; + irp->Callback = rdpdr_server_drive_close_file_callback; + irp->CallbackData = callbackData; + irp->DeviceId = deviceId; +@@ -3394,7 +3499,7 @@ static UINT rdpdr_server_drive_close_file(RdpdrServerContext* context, void* cal + + if (!rdpdr_server_enqueue_irp(context, irp)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); + rdpdr_server_irp_free(irp); + return ERROR_INTERNAL_ERROR; + } +@@ -3422,7 +3527,8 @@ static UINT rdpdr_server_drive_delete_file_callback2(RdpdrServerContext* context + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); + +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ RdpdrServerPrivate* priv = context->priv; ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerDriveDeleteFileCallback2: deviceId=%" PRIu32 ", completionId=%" PRIu32 + ", ioStatus=0x%" PRIx32 "", + deviceId, completionId, ioStatus); +@@ -3445,7 +3551,9 @@ static UINT rdpdr_server_drive_delete_file_callback1(RdpdrServerContext* context + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ ++ RdpdrServerPrivate* priv = context->priv; ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerDriveDeleteFileCallback1: deviceId=%" PRIu32 ", completionId=%" PRIu32 + ", ioStatus=0x%" PRIx32 "", + deviceId, completionId, ioStatus); +@@ -3459,23 +3567,23 @@ static UINT rdpdr_server_drive_delete_file_callback1(RdpdrServerContext* context + return CHANNEL_RC_OK; + } + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 5)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 5)) + return ERROR_INVALID_DATA; + + const uint32_t fileId = Stream_Get_UINT32(s); /* FileId (4 bytes) */ + const uint8_t information = Stream_Get_UINT8(s); /* Information (1 byte) */ + +- WLog_Print(context->priv->log, WLOG_DEBUG, "fileId [0x%08" PRIx32 "], information %s", fileId, ++ WLog_Print(priv->log, WLOG_DEBUG, "fileId [0x%08" PRIx32 "], information %s", fileId, + fileInformation2str(information)); + /* Setup the IRP. */ +- irp->CompletionId = context->priv->NextCompletionId++; ++ irp->CompletionId = priv->NextCompletionId++; + irp->Callback = rdpdr_server_drive_delete_file_callback2; + irp->DeviceId = deviceId; + irp->FileId = fileId; + + if (!rdpdr_server_enqueue_irp(context, irp)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); + rdpdr_server_irp_free(irp); + return ERROR_INTERNAL_ERROR; + } +@@ -3497,13 +3605,15 @@ static UINT rdpdr_server_drive_delete_file(RdpdrServerContext* context, void* ca + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); + ++ RdpdrServerPrivate* priv = context->priv; ++ + if (!irp) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + +- irp->CompletionId = context->priv->NextCompletionId++; ++ irp->CompletionId = priv->NextCompletionId++; + irp->Callback = rdpdr_server_drive_delete_file_callback1; + irp->CallbackData = callbackData; + irp->DeviceId = deviceId; +@@ -3512,7 +3622,7 @@ static UINT rdpdr_server_drive_delete_file(RdpdrServerContext* context, void* ca + + if (!rdpdr_server_enqueue_irp(context, irp)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); + rdpdr_server_irp_free(irp); + return ERROR_INTERNAL_ERROR; + } +@@ -3542,7 +3652,8 @@ static UINT rdpdr_server_drive_rename_file_callback3(RdpdrServerContext* context + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); + +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ RdpdrServerPrivate* priv = context->priv; ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerDriveRenameFileCallback3: deviceId=%" PRIu32 ", completionId=%" PRIu32 + ", ioStatus=0x%" PRIx32 "", + deviceId, completionId, ioStatus); +@@ -3564,12 +3675,14 @@ static UINT rdpdr_server_drive_rename_file_callback2(RdpdrServerContext* context + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ ++ RdpdrServerPrivate* priv = context->priv; ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerDriveRenameFileCallback2: deviceId=%" PRIu32 ", completionId=%" PRIu32 + ", ioStatus=0x%" PRIx32 "", + deviceId, completionId, ioStatus); + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 5)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 5)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, length); /* Length (4 bytes) */ +@@ -3577,13 +3690,13 @@ static UINT rdpdr_server_drive_rename_file_callback2(RdpdrServerContext* context + /* Invoke the rename file completion routine. */ + context->OnDriveRenameFileComplete(context, irp->CallbackData, ioStatus); + /* Setup the IRP. */ +- irp->CompletionId = context->priv->NextCompletionId++; ++ irp->CompletionId = priv->NextCompletionId++; + irp->Callback = rdpdr_server_drive_rename_file_callback3; + irp->DeviceId = deviceId; + + if (!rdpdr_server_enqueue_irp(context, irp)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); + rdpdr_server_irp_free(irp); + return ERROR_INTERNAL_ERROR; + } +@@ -3605,7 +3718,9 @@ static UINT rdpdr_server_drive_rename_file_callback1(RdpdrServerContext* context + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); +- WLog_Print(context->priv->log, WLOG_DEBUG, ++ ++ RdpdrServerPrivate* priv = context->priv; ++ WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerDriveRenameFileCallback1: deviceId=%" PRIu32 ", completionId=%" PRIu32 + ", ioStatus=0x%" PRIx32 "", + deviceId, completionId, ioStatus); +@@ -3619,23 +3734,23 @@ static UINT rdpdr_server_drive_rename_file_callback1(RdpdrServerContext* context + return CHANNEL_RC_OK; + } + +- if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 5)) ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 5)) + return ERROR_INVALID_DATA; + + const uint32_t fileId = Stream_Get_UINT32(s); /* FileId (4 bytes) */ + const uint8_t information = Stream_Get_UINT8(s); /* Information (1 byte) */ +- WLog_Print(context->priv->log, WLOG_DEBUG, "fileId [0x%08" PRIx32 "], information %s", fileId, ++ WLog_Print(priv->log, WLOG_DEBUG, "fileId [0x%08" PRIx32 "], information %s", fileId, + fileInformation2str(information)); + + /* Setup the IRP. */ +- irp->CompletionId = context->priv->NextCompletionId++; ++ irp->CompletionId = priv->NextCompletionId++; + irp->Callback = rdpdr_server_drive_rename_file_callback2; + irp->DeviceId = deviceId; + irp->FileId = fileId; + + if (!rdpdr_server_enqueue_irp(context, irp)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); + rdpdr_server_irp_free(irp); + return ERROR_INTERNAL_ERROR; + } +@@ -3656,15 +3771,17 @@ static UINT rdpdr_server_drive_rename_file(RdpdrServerContext* context, void* ca + { + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); ++ ++ RdpdrServerPrivate* priv = context->priv; + RDPDR_IRP* irp = rdpdr_server_irp_new(); + + if (!irp) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + +- irp->CompletionId = context->priv->NextCompletionId++; ++ irp->CompletionId = priv->NextCompletionId++; + irp->Callback = rdpdr_server_drive_rename_file_callback1; + irp->CallbackData = callbackData; + irp->DeviceId = deviceId; +@@ -3675,7 +3792,7 @@ static UINT rdpdr_server_drive_rename_file(RdpdrServerContext* context, void* ca + + if (!rdpdr_server_enqueue_irp(context, irp)) + { +- WLog_Print(context->priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); + rdpdr_server_irp_free(irp); + return ERROR_INTERNAL_ERROR; + } + +From 65d04a2f4eb13ae313c44c5443ab06b973376049 Mon Sep 17 00:00:00 2001 +From: David Fort +Date: Tue, 23 Jun 2026 19:10:19 +0200 +Subject: [PATCH 12/31] [channels,rdpdr] extract RDPDR_IRP_Callback typedef and + convert RDPDR_IRP to struct + +--- + channels/rdpdr/server/rdpdr_main.h | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +diff --git a/channels/rdpdr/server/rdpdr_main.h b/channels/rdpdr/server/rdpdr_main.h +index dabbae24ad8b..b1d86b148ad7 100644 +--- a/channels/rdpdr/server/rdpdr_main.h ++++ b/channels/rdpdr/server/rdpdr_main.h +@@ -31,7 +31,11 @@ + #include + #include + +-typedef struct S_RDPDR_IRP ++typedef struct S_RDPDR_IRP RDPDR_IRP; ++typedef UINT (*RDPDR_IRP_Callback)(RdpdrServerContext* context, wStream* s, struct S_RDPDR_IRP* irp, ++ UINT32 deviceId, UINT32 completionId, UINT32 ioStatus); ++ ++struct S_RDPDR_IRP + { + UINT32 CompletionId; + UINT32 DeviceId; +@@ -39,9 +43,7 @@ typedef struct S_RDPDR_IRP + char PathName[256]; + char ExtraBuffer[256]; + void* CallbackData; +- UINT(*Callback) +- (RdpdrServerContext* context, wStream* s, struct S_RDPDR_IRP* irp, UINT32 deviceId, +- UINT32 completionId, UINT32 ioStatus); +-} RDPDR_IRP; ++ RDPDR_IRP_Callback Callback; ++}; + + #endif /* FREERDP_CHANNEL_RDPDR_SERVER_MAIN_H */ + +From f29fc22218918fb8b2c8f9f8b04150db2bee7fee Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Tue, 23 Jun 2026 19:10:31 +0200 +Subject: [PATCH 13/31] [channels,rdpdr] declare variables at point of first + use + +Move variable declarations from function tops to just before +their first use. + +Co-Authored-By: David Fort +--- + channels/rdpdr/server/rdpdr_main.c | 378 ++++++++++++----------------- + 1 file changed, 154 insertions(+), 224 deletions(-) + +diff --git a/channels/rdpdr/server/rdpdr_main.c b/channels/rdpdr/server/rdpdr_main.c +index 5f37a3c7a3d0..488efca5bc46 100644 +--- a/channels/rdpdr/server/rdpdr_main.c ++++ b/channels/rdpdr/server/rdpdr_main.c +@@ -191,9 +191,9 @@ static RdpdrDevice* rdpdr_get_device_by_id(RdpdrServerPrivate* priv, UINT32 Devi + + static BOOL rdpdr_remove_device_by_id(RdpdrServerPrivate* priv, UINT32 DeviceId) + { +- const RdpdrDevice* device = rdpdr_get_device_by_id(priv, DeviceId); + WINPR_ASSERT(priv); + ++ const RdpdrDevice* device = rdpdr_get_device_by_id(priv, DeviceId); + if (!device) + { + WLog_Print(priv->log, WLOG_WARN, "[del] Device Id: 0x%08" PRIX32 ": no such device", +@@ -258,21 +258,17 @@ static BOOL rdpdr_server_enqueue_irp(RdpdrServerContext* context, RDPDR_IRP* irp + + static RDPDR_IRP* rdpdr_server_dequeue_irp(RdpdrServerContext* context, UINT32 completionId) + { +- RDPDR_IRP* irp = nullptr; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + + RdpdrServerPrivate* priv = context->priv; + const uintptr_t key = completionId + 1ull; +- irp = (RDPDR_IRP*)ListDictionary_Take(priv->IrpList, (void*)key); ++ RDPDR_IRP* irp = (RDPDR_IRP*)ListDictionary_Take(priv->IrpList, (void*)key); + return irp; + } + + static UINT rdpdr_seal_send_free_request(RdpdrServerContext* context, wStream* s) + { +- BOOL status = 0; +- ULONG written = 0; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(s); +@@ -295,8 +291,9 @@ static UINT rdpdr_seal_send_free_request(RdpdrServerContext* context, wStream* s + rdpdr_packetid_string(header.PacketId), header.PacketId); + } + winpr_HexLogDump(priv->log, WLOG_DEBUG, Stream_Buffer(s), Stream_Length(s)); +- status = WTSVirtualChannelWrite(priv->ChannelHandle, Stream_BufferAs(s, char), (ULONG)length, +- &written); ++ ULONG written = 0; ++ BOOL status = WTSVirtualChannelWrite(priv->ChannelHandle, Stream_BufferAs(s, char), ++ (ULONG)length, &written); + Stream_Free(s, TRUE); + return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR; + } +@@ -308,29 +305,25 @@ static UINT rdpdr_seal_send_free_request(RdpdrServerContext* context, wStream* s + */ + static UINT rdpdr_server_send_announce_request(RdpdrServerContext* context) + { +- UINT error = 0; +- wStream* s = nullptr; +- RDPDR_HEADER header = WINPR_C_ARRAY_INIT; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- header.Component = RDPDR_CTYP_CORE; +- header.PacketId = PAKID_CORE_SERVER_ANNOUNCE; +- +- error = IFCALLRESULT(CHANNEL_RC_OK, context->SendServerAnnounce, context); ++ UINT error = IFCALLRESULT(CHANNEL_RC_OK, context->SendServerAnnounce, context); + if (error != CHANNEL_RC_OK) + return error; + + RdpdrServerPrivate* priv = context->priv; +- s = Stream_New(nullptr, RDPDR_HEADER_LENGTH + 8); +- ++ wStream* s = Stream_New(nullptr, RDPDR_HEADER_LENGTH + 8); + if (!s) + { + WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); + return CHANNEL_RC_NO_MEMORY; + } + ++ const RDPDR_HEADER header = { ++ .Component = RDPDR_CTYP_CORE, ++ .PacketId = PAKID_CORE_SERVER_ANNOUNCE, ++ }; + Stream_Write_UINT16(s, header.Component); /* Component (2 bytes) */ + Stream_Write_UINT16(s, header.PacketId); /* PacketId (2 bytes) */ + Stream_Write_UINT16(s, priv->VersionMajor); /* VersionMajor (2 bytes) */ +@@ -347,9 +340,6 @@ static UINT rdpdr_server_send_announce_request(RdpdrServerContext* context) + static UINT rdpdr_server_receive_announce_response(RdpdrServerContext* context, wStream* s, + const RDPDR_HEADER* header) + { +- UINT32 ClientId = 0; +- UINT16 VersionMajor = 0; +- UINT16 VersionMinor = 0; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(header); +@@ -361,6 +351,9 @@ static UINT rdpdr_server_receive_announce_response(RdpdrServerContext* context, + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 8)) + return ERROR_INVALID_DATA; + ++ UINT16 VersionMajor = 0; ++ UINT16 VersionMinor = 0; ++ UINT32 ClientId = 0; + Stream_Read_UINT16(s, VersionMajor); /* VersionMajor (2 bytes) */ + Stream_Read_UINT16(s, VersionMinor); /* VersionMinor (2 bytes) */ + Stream_Read_UINT32(s, ClientId); /* ClientId (4 bytes) */ +@@ -382,10 +375,6 @@ static UINT rdpdr_server_receive_announce_response(RdpdrServerContext* context, + static UINT rdpdr_server_receive_client_name_request(RdpdrServerContext* context, wStream* s, + const RDPDR_HEADER* header) + { +- UINT32 UnicodeFlag = 0; +- UINT32 CodePage = 0; +- UINT32 ComputerNameLen = 0; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(s); +@@ -397,6 +386,9 @@ static UINT rdpdr_server_receive_client_name_request(RdpdrServerContext* context + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 12)) + return ERROR_INVALID_DATA; + ++ UINT32 UnicodeFlag = 0; ++ UINT32 CodePage = 0; ++ UINT32 ComputerNameLen = 0; + Stream_Read_UINT32(s, UnicodeFlag); /* UnicodeFlag (4 bytes) */ + Stream_Read_UINT32(s, CodePage); /* CodePage (4 bytes), MUST be set to zero */ + Stream_Read_UINT32(s, ComputerNameLen); /* ComputerNameLen (4 bytes) */ +@@ -617,17 +609,13 @@ static UINT rdpdr_server_read_general_capability_set(RdpdrServerContext* context + */ + static UINT rdpdr_server_write_general_capability_set(RdpdrServerContext* context, wStream* s) + { +- UINT32 ioCode1 = 0; +- UINT32 extendedPdu = 0; +- UINT32 extraFlags1 = 0; +- UINT32 SpecialTypeDeviceCap = 0; +- const RDPDR_CAPABILITY_HEADER header = { CAP_GENERAL_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH + 36, +- GENERAL_CAPABILITY_VERSION_02 }; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- ioCode1 = 0; ++ const RDPDR_CAPABILITY_HEADER header = { CAP_GENERAL_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH + 36, ++ GENERAL_CAPABILITY_VERSION_02 }; ++ ++ UINT32 ioCode1 = 0; + ioCode1 |= RDPDR_IRP_MJ_CREATE; /* always set */ + ioCode1 |= RDPDR_IRP_MJ_CLEANUP; /* always set */ + ioCode1 |= RDPDR_IRP_MJ_CLOSE; /* always set */ +@@ -644,7 +632,8 @@ static UINT rdpdr_server_write_general_capability_set(RdpdrServerContext* contex + ioCode1 |= RDPDR_IRP_MJ_LOCK_CONTROL; /* always set */ + ioCode1 |= RDPDR_IRP_MJ_QUERY_SECURITY; /* optional */ + ioCode1 |= RDPDR_IRP_MJ_SET_SECURITY; /* optional */ +- extendedPdu = 0; ++ ++ UINT32 extendedPdu = 0; + extendedPdu |= RDPDR_CLIENT_DISPLAY_NAME_PDU; /* always set */ + extendedPdu |= RDPDR_DEVICE_REMOVE_PDUS; /* optional */ + +@@ -652,9 +641,9 @@ static UINT rdpdr_server_write_general_capability_set(RdpdrServerContext* contex + if (priv->UserLoggedOnPdu) + extendedPdu |= RDPDR_USER_LOGGEDON_PDU; /* optional */ + +- extraFlags1 = 0; ++ UINT32 extraFlags1 = 0; + extraFlags1 |= ENABLE_ASYNCIO; /* optional */ +- SpecialTypeDeviceCap = 0; ++ UINT32 SpecialTypeDeviceCap = 0; + + UINT error = rdpdr_write_capset_header(priv->log, s, &header); + if (error != CHANNEL_RC_OK) +@@ -703,12 +692,13 @@ static UINT rdpdr_server_read_printer_capability_set(RdpdrServerContext* context + */ + static UINT rdpdr_server_write_printer_capability_set(RdpdrServerContext* context, wStream* s) + { +- const RDPDR_CAPABILITY_HEADER header = { CAP_PRINTER_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH, +- PRINT_CAPABILITY_VERSION_01 }; + WINPR_UNUSED(context); + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + ++ const RDPDR_CAPABILITY_HEADER header = { CAP_PRINTER_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH, ++ PRINT_CAPABILITY_VERSION_01 }; ++ + return rdpdr_server_write_capability_set_header_cb(context, s, &header); + } + +@@ -736,12 +726,13 @@ static UINT rdpdr_server_read_port_capability_set(RdpdrServerContext* context, w + */ + static UINT rdpdr_server_write_port_capability_set(RdpdrServerContext* context, wStream* s) + { +- const RDPDR_CAPABILITY_HEADER header = { CAP_PORT_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH, +- PORT_CAPABILITY_VERSION_01 }; + WINPR_UNUSED(context); + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + ++ const RDPDR_CAPABILITY_HEADER header = { CAP_PORT_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH, ++ PORT_CAPABILITY_VERSION_01 }; ++ + return rdpdr_server_write_capability_set_header_cb(context, s, &header); + } + +@@ -769,13 +760,13 @@ static UINT rdpdr_server_read_drive_capability_set(RdpdrServerContext* context, + */ + static UINT rdpdr_server_write_drive_capability_set(RdpdrServerContext* context, wStream* s) + { +- const RDPDR_CAPABILITY_HEADER header = { CAP_DRIVE_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH, +- DRIVE_CAPABILITY_VERSION_02 }; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_UNUSED(context); + ++ const RDPDR_CAPABILITY_HEADER header = { CAP_DRIVE_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH, ++ DRIVE_CAPABILITY_VERSION_02 }; ++ + return rdpdr_server_write_capability_set_header_cb(context, s, &header); + } + +@@ -803,13 +794,13 @@ static UINT rdpdr_server_read_smartcard_capability_set(RdpdrServerContext* conte + */ + static UINT rdpdr_server_write_smartcard_capability_set(RdpdrServerContext* context, wStream* s) + { +- const RDPDR_CAPABILITY_HEADER header = { CAP_SMARTCARD_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH, +- SMARTCARD_CAPABILITY_VERSION_01 }; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); +- + WINPR_UNUSED(context); + ++ const RDPDR_CAPABILITY_HEADER header = { CAP_SMARTCARD_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH, ++ SMARTCARD_CAPABILITY_VERSION_01 }; ++ + return rdpdr_server_write_capability_set_header_cb(context, s, &header); + } + +@@ -820,17 +811,10 @@ static UINT rdpdr_server_write_smartcard_capability_set(RdpdrServerContext* cont + */ + static UINT rdpdr_server_send_core_capability_request(RdpdrServerContext* context) + { +- wStream* s = nullptr; +- RDPDR_HEADER header = WINPR_C_ARRAY_INIT; +- UINT16 numCapabilities = 0; +- UINT error = 0; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- header.Component = RDPDR_CTYP_CORE; +- header.PacketId = PAKID_CORE_SERVER_CAPABILITY; +- numCapabilities = 1; +- ++ UINT16 numCapabilities = 1; + if ((context->supported & RDPDR_DTYP_FILESYSTEM) != 0) + numCapabilities++; + +@@ -845,19 +829,23 @@ static UINT rdpdr_server_send_core_capability_request(RdpdrServerContext* contex + numCapabilities++; + + RdpdrServerPrivate* priv = context->priv; +- s = Stream_New(nullptr, RDPDR_HEADER_LENGTH + 512); +- ++ wStream* s = Stream_New(nullptr, RDPDR_HEADER_LENGTH + 512); + if (!s) + { + WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); + return CHANNEL_RC_NO_MEMORY; + } + ++ const RDPDR_HEADER header = { ++ .Component = RDPDR_CTYP_CORE, ++ .PacketId = PAKID_CORE_SERVER_CAPABILITY, ++ }; + Stream_Write_UINT16(s, header.Component); /* Component (2 bytes) */ + Stream_Write_UINT16(s, header.PacketId); /* PacketId (2 bytes) */ + Stream_Write_UINT16(s, numCapabilities); /* numCapabilities (2 bytes) */ + Stream_Write_UINT16(s, 0); /* Padding (2 bytes) */ + ++ UINT error = 0; + if ((error = rdpdr_server_write_general_capability_set(context, s))) + { + WLog_Print(priv->log, WLOG_ERROR, +@@ -943,9 +931,6 @@ static UINT16 rdpdr_cap_type_to_dtyp(UINT16 capabilityType) + static UINT rdpdr_server_receive_core_capability_response(RdpdrServerContext* context, wStream* s, + const RDPDR_HEADER* header) + { +- UINT status = 0; +- UINT16 numCapabilities = 0; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -956,6 +941,7 @@ static UINT rdpdr_server_receive_core_capability_response(RdpdrServerContext* co + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + ++ UINT16 numCapabilities = 0; + Stream_Read_UINT16(s, numCapabilities); /* numCapabilities (2 bytes) */ + Stream_Seek_UINT16(s); /* Padding (2 bytes) */ + +@@ -965,6 +951,7 @@ static UINT rdpdr_server_receive_core_capability_response(RdpdrServerContext* co + RDPDR_CAPABILITY_HEADER capabilityHeader = WINPR_C_ARRAY_INIT; + const size_t start = Stream_GetPosition(s); + ++ UINT status = 0; + if ((status = rdpdr_read_capset_header(priv->log, s, &capabilityHeader))) + { + WLog_Print(priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", status); +@@ -1067,24 +1054,21 @@ static UINT rdpdr_server_receive_core_capability_response(RdpdrServerContext* co + */ + static UINT rdpdr_server_send_client_id_confirm(RdpdrServerContext* context) + { +- wStream* s = nullptr; +- RDPDR_HEADER header = WINPR_C_ARRAY_INIT; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- header.Component = RDPDR_CTYP_CORE; +- header.PacketId = PAKID_CORE_CLIENTID_CONFIRM; +- + RdpdrServerPrivate* priv = context->priv; +- s = Stream_New(nullptr, RDPDR_HEADER_LENGTH + 8); +- ++ wStream* s = Stream_New(nullptr, RDPDR_HEADER_LENGTH + 8); + if (!s) + { + WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); + return CHANNEL_RC_NO_MEMORY; + } + ++ const RDPDR_HEADER header = { ++ .Component = RDPDR_CTYP_CORE, ++ .PacketId = PAKID_CORE_CLIENTID_CONFIRM, ++ }; + Stream_Write_UINT16(s, header.Component); /* Component (2 bytes) */ + Stream_Write_UINT16(s, header.PacketId); /* PacketId (2 bytes) */ + Stream_Write_UINT16(s, priv->VersionMajor); /* VersionMajor (2 bytes) */ +@@ -1102,8 +1086,6 @@ static UINT rdpdr_server_receive_device_list_announce_request(RdpdrServerContext + wStream* s, + const RDPDR_HEADER* header) + { +- UINT32 DeviceCount = 0; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -1114,17 +1096,16 @@ static UINT rdpdr_server_receive_device_list_announce_request(RdpdrServerContext + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + ++ UINT32 DeviceCount = 0; + Stream_Read_UINT32(s, DeviceCount); /* DeviceCount (4 bytes) */ + WLog_Print(priv->log, WLOG_DEBUG, "DeviceCount: %" PRIu32 "", DeviceCount); + + for (UINT32 i = 0; i < DeviceCount; i++) + { +- UINT error = 0; +- RdpdrDevice device = WINPR_C_ARRAY_INIT; +- + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 20)) + return ERROR_INVALID_DATA; + ++ RdpdrDevice device = WINPR_C_ARRAY_INIT; + Stream_Read_UINT32(s, device.DeviceType); /* DeviceType (4 bytes) */ + Stream_Read_UINT32(s, device.DeviceId); /* DeviceId (4 bytes) */ + Stream_Read(s, device.PreferredDosName, 8); /* PreferredDosName (8 bytes) */ +@@ -1137,7 +1118,7 @@ static UINT rdpdr_server_receive_device_list_announce_request(RdpdrServerContext + if (!rdpdr_add_device(context->priv, &device)) + return ERROR_INTERNAL_ERROR; + +- error = IFCALLRESULT(CHANNEL_RC_OK, context->ReceiveDeviceAnnounce, context, &device); ++ UINT error = IFCALLRESULT(CHANNEL_RC_OK, context->ReceiveDeviceAnnounce, context, &device); + if (error != CHANNEL_RC_OK) + return error; + +@@ -1219,9 +1200,6 @@ static UINT rdpdr_server_receive_device_list_announce_request(RdpdrServerContext + static UINT rdpdr_server_receive_device_list_remove_request(RdpdrServerContext* context, wStream* s, + const RDPDR_HEADER* header) + { +- UINT32 DeviceCount = 0; +- UINT32 DeviceType = 0; +- UINT32 DeviceId = 0; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -1232,6 +1210,7 @@ static UINT rdpdr_server_receive_device_list_remove_request(RdpdrServerContext* + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + ++ UINT32 DeviceCount = 0; + Stream_Read_UINT32(s, DeviceCount); /* DeviceCount (4 bytes) */ + WLog_Print(priv->log, WLOG_DEBUG, "DeviceCount: %" PRIu32 "", DeviceCount); + +@@ -1243,10 +1222,11 @@ static UINT rdpdr_server_receive_device_list_remove_request(RdpdrServerContext* + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + ++ UINT32 DeviceId = 0; + Stream_Read_UINT32(s, DeviceId); /* DeviceId (4 bytes) */ + device = rdpdr_get_device_by_id(context->priv, DeviceId); + WLog_Print(priv->log, WLOG_DEBUG, "Device %" PRIu32 " Id: 0x%08" PRIX32 "", i, DeviceId); +- DeviceType = 0; ++ UINT32 DeviceType = 0; + if (device) + DeviceType = device->DeviceType; + +@@ -1305,7 +1285,12 @@ static UINT rdpdr_server_receive_io_create_request(RdpdrServerContext* context, + WINPR_ATTR_UNUSED UINT32 FileId, + WINPR_ATTR_UNUSED UINT32 CompletionId) + { +- const WCHAR* path = nullptr; ++ WINPR_ASSERT(context); ++ ++ RdpdrServerPrivate* priv = context->priv; ++ if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) ++ return ERROR_INVALID_DATA; ++ + UINT32 DesiredAccess = 0; + UINT32 AllocationSize = 0; + UINT32 FileAttributes = 0; +@@ -1313,13 +1298,6 @@ static UINT rdpdr_server_receive_io_create_request(RdpdrServerContext* context, + UINT32 CreateDisposition = 0; + UINT32 CreateOptions = 0; + UINT32 PathLength = 0; +- +- WINPR_ASSERT(context); +- +- RdpdrServerPrivate* priv = context->priv; +- if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) +- return ERROR_INVALID_DATA; +- + Stream_Read_UINT32(s, DesiredAccess); + Stream_Read_UINT32(s, AllocationSize); + Stream_Read_UINT32(s, FileAttributes); +@@ -1328,7 +1306,7 @@ static UINT rdpdr_server_receive_io_create_request(RdpdrServerContext* context, + Stream_Read_UINT32(s, CreateOptions); + Stream_Read_UINT32(s, PathLength); + +- path = rdpdr_read_ustring(priv->log, s, PathLength); ++ const WCHAR* path = rdpdr_read_ustring(priv->log, s, PathLength); + if (!path && (PathLength > 0)) + return ERROR_INVALID_DATA; + +@@ -1366,9 +1344,6 @@ static UINT rdpdr_server_receive_io_read_request(RdpdrServerContext* context, wS + WINPR_ATTR_UNUSED UINT32 FileId, + WINPR_ATTR_UNUSED UINT32 CompletionId) + { +- UINT32 Length = 0; +- UINT64 Offset = 0; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -1376,6 +1351,8 @@ static UINT rdpdr_server_receive_io_read_request(RdpdrServerContext* context, wS + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + ++ UINT32 Length = 0; ++ UINT64 Offset = 0; + Stream_Read_UINT32(s, Length); + Stream_Read_UINT64(s, Offset); + Stream_Seek(s, 20); /* Padding */ +@@ -1395,9 +1372,6 @@ static UINT rdpdr_server_receive_io_write_request(RdpdrServerContext* context, w + WINPR_ATTR_UNUSED UINT32 FileId, + WINPR_ATTR_UNUSED UINT32 CompletionId) + { +- UINT32 Length = 0; +- UINT64 Offset = 0; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -1406,6 +1380,8 @@ static UINT rdpdr_server_receive_io_write_request(RdpdrServerContext* context, w + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + ++ UINT32 Length = 0; ++ UINT64 Offset = 0; + Stream_Read_UINT32(s, Length); + Stream_Read_UINT64(s, Offset); + Stream_Seek(s, 20); /* Padding */ +@@ -1430,10 +1406,6 @@ static UINT rdpdr_server_receive_io_device_control_request(RdpdrServerContext* c + WINPR_ATTR_UNUSED UINT32 FileId, + WINPR_ATTR_UNUSED UINT32 CompletionId) + { +- UINT32 OutputBufferLength = 0; +- UINT32 InputBufferLength = 0; +- UINT32 IoControlCode = 0; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -1442,6 +1414,9 @@ static UINT rdpdr_server_receive_io_device_control_request(RdpdrServerContext* c + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + ++ UINT32 OutputBufferLength = 0; ++ UINT32 InputBufferLength = 0; ++ UINT32 IoControlCode = 0; + Stream_Read_UINT32(s, OutputBufferLength); + Stream_Read_UINT32(s, InputBufferLength); + Stream_Read_UINT32(s, IoControlCode); +@@ -1464,15 +1439,14 @@ static UINT rdpdr_server_receive_io_query_volume_information_request( + RdpdrServerContext* context, wStream* s, WINPR_ATTR_UNUSED UINT32 DeviceId, + WINPR_ATTR_UNUSED UINT32 FileId, WINPR_ATTR_UNUSED UINT32 CompletionId) + { +- UINT32 FsInformationClass = 0; +- UINT32 Length = 0; +- + WINPR_ASSERT(context); + + RdpdrServerPrivate* priv = context->priv; + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + ++ UINT32 FsInformationClass = 0; ++ UINT32 Length = 0; + Stream_Read_UINT32(s, FsInformationClass); + Stream_Read_UINT32(s, Length); + Stream_Seek(s, 24); /* Padding */ +@@ -1498,9 +1472,6 @@ static UINT rdpdr_server_receive_io_set_volume_information_request( + RdpdrServerContext* context, wStream* s, WINPR_ATTR_UNUSED UINT32 DeviceId, + WINPR_ATTR_UNUSED UINT32 FileId, WINPR_ATTR_UNUSED UINT32 CompletionId) + { +- UINT32 FsInformationClass = 0; +- UINT32 Length = 0; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -1509,6 +1480,8 @@ static UINT rdpdr_server_receive_io_set_volume_information_request( + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + ++ UINT32 FsInformationClass = 0; ++ UINT32 Length = 0; + Stream_Read_UINT32(s, FsInformationClass); + Stream_Read_UINT32(s, Length); + Stream_Seek(s, 24); /* Padding */ +@@ -1536,9 +1509,6 @@ static UINT rdpdr_server_receive_io_query_information_request(RdpdrServerContext + WINPR_ATTR_UNUSED UINT32 FileId, + WINPR_ATTR_UNUSED UINT32 CompletionId) + { +- UINT32 FsInformationClass = 0; +- UINT32 Length = 0; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -1547,6 +1517,8 @@ static UINT rdpdr_server_receive_io_query_information_request(RdpdrServerContext + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + ++ UINT32 FsInformationClass = 0; ++ UINT32 Length = 0; + Stream_Read_UINT32(s, FsInformationClass); + Stream_Read_UINT32(s, Length); + Stream_Seek(s, 24); /* Padding */ +@@ -1573,9 +1545,6 @@ static UINT rdpdr_server_receive_io_set_information_request(RdpdrServerContext* + WINPR_ATTR_UNUSED UINT32 FileId, + WINPR_ATTR_UNUSED UINT32 CompletionId) + { +- UINT32 FsInformationClass = 0; +- UINT32 Length = 0; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -1584,6 +1553,8 @@ static UINT rdpdr_server_receive_io_set_information_request(RdpdrServerContext* + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + ++ UINT32 FsInformationClass = 0; ++ UINT32 Length = 0; + Stream_Read_UINT32(s, FsInformationClass); + Stream_Read_UINT32(s, Length); + Stream_Seek(s, 24); /* Padding */ +@@ -1610,10 +1581,6 @@ static UINT rdpdr_server_receive_io_query_directory_request(RdpdrServerContext* + WINPR_ATTR_UNUSED UINT32 FileId, + WINPR_ATTR_UNUSED UINT32 CompletionId) + { +- BYTE InitialQuery = 0; +- UINT32 FsInformationClass = 0; +- UINT32 PathLength = 0; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -1622,6 +1589,9 @@ static UINT rdpdr_server_receive_io_query_directory_request(RdpdrServerContext* + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + ++ UINT32 FsInformationClass = 0; ++ BYTE InitialQuery = 0; ++ UINT32 PathLength = 0; + Stream_Read_UINT32(s, FsInformationClass); + Stream_Read_UINT8(s, InitialQuery); + Stream_Read_UINT32(s, PathLength); +@@ -1653,9 +1623,6 @@ static UINT rdpdr_server_receive_io_change_directory_request(RdpdrServerContext* + WINPR_ATTR_UNUSED UINT32 FileId, + WINPR_ATTR_UNUSED UINT32 CompletionId) + { +- BYTE WatchTree = 0; +- UINT32 CompletionFilter = 0; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -1664,6 +1631,8 @@ static UINT rdpdr_server_receive_io_change_directory_request(RdpdrServerContext* + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + ++ BYTE WatchTree = 0; ++ UINT32 CompletionFilter = 0; + Stream_Read_UINT8(s, WatchTree); + Stream_Read_UINT32(s, CompletionFilter); + Stream_Seek(s, 27); /* Padding */ +@@ -1754,12 +1723,6 @@ static UINT rdpdr_server_receive_io_lock_control_request(RdpdrServerContext* con + static UINT rdpdr_server_receive_device_io_request(RdpdrServerContext* context, wStream* s, + WINPR_ATTR_UNUSED const RDPDR_HEADER* header) + { +- UINT32 DeviceId = 0; +- UINT32 FileId = 0; +- UINT32 CompletionId = 0; +- UINT32 MajorFunction = 0; +- UINT32 MinorFunction = 0; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(header); +@@ -1769,6 +1732,11 @@ static UINT rdpdr_server_receive_device_io_request(RdpdrServerContext* context, + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 20)) + return ERROR_INVALID_DATA; + ++ UINT32 DeviceId = 0; ++ UINT32 FileId = 0; ++ UINT32 CompletionId = 0; ++ UINT32 MajorFunction = 0; ++ UINT32 MinorFunction = 0; + Stream_Read_UINT32(s, DeviceId); + Stream_Read_UINT32(s, FileId); + Stream_Read_UINT32(s, CompletionId); +@@ -1836,11 +1804,6 @@ static UINT rdpdr_server_receive_device_io_request(RdpdrServerContext* context, + static UINT rdpdr_server_receive_device_io_completion(RdpdrServerContext* context, wStream* s, + const RDPDR_HEADER* header) + { +- UINT32 deviceId = 0; +- UINT32 completionId = 0; +- UINT32 ioStatus = 0; +- RDPDR_IRP* irp = nullptr; +- UINT error = CHANNEL_RC_OK; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(s); +@@ -1853,13 +1816,16 @@ static UINT rdpdr_server_receive_device_io_completion(RdpdrServerContext* contex + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 12)) + return ERROR_INVALID_DATA; + ++ UINT32 deviceId = 0; ++ UINT32 completionId = 0; ++ UINT32 ioStatus = 0; + Stream_Read_UINT32(s, deviceId); + Stream_Read_UINT32(s, completionId); + Stream_Read_UINT32(s, ioStatus); + WLog_Print(priv->log, WLOG_DEBUG, + "deviceId=%" PRIu32 ", completionId=0x%" PRIx32 ", ioStatus=0x%" PRIx32 "", deviceId, + completionId, ioStatus); +- irp = rdpdr_server_dequeue_irp(context, completionId); ++ RDPDR_IRP* irp = rdpdr_server_dequeue_irp(context, completionId); + + if (!irp) + { +@@ -1869,6 +1835,7 @@ static UINT rdpdr_server_receive_device_io_completion(RdpdrServerContext* contex + } + + /* Invoke the callback. */ ++ UINT error = CHANNEL_RC_OK; + if (irp->Callback) + { + error = (*irp->Callback)(context, s, irp, deviceId, completionId, ioStatus); +@@ -1884,24 +1851,21 @@ static UINT rdpdr_server_receive_device_io_completion(RdpdrServerContext* contex + */ + static UINT rdpdr_server_send_user_logged_on(RdpdrServerContext* context) + { +- wStream* s = nullptr; +- RDPDR_HEADER header = WINPR_C_ARRAY_INIT; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- header.Component = RDPDR_CTYP_CORE; +- header.PacketId = PAKID_CORE_USER_LOGGEDON; +- + RdpdrServerPrivate* priv = context->priv; +- s = Stream_New(nullptr, RDPDR_HEADER_LENGTH); +- ++ wStream* s = Stream_New(nullptr, RDPDR_HEADER_LENGTH); + if (!s) + { + WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); + return CHANNEL_RC_NO_MEMORY; + } + ++ const RDPDR_HEADER header = { ++ .Component = RDPDR_CTYP_CORE, ++ .PacketId = PAKID_CORE_USER_LOGGEDON, ++ }; + Stream_Write_UINT16(s, header.Component); /* Component (2 bytes) */ + Stream_Write_UINT16(s, header.PacketId); /* PacketId (2 bytes) */ + return rdpdr_seal_send_free_request(context, s); +@@ -1909,15 +1873,6 @@ static UINT rdpdr_server_send_user_logged_on(RdpdrServerContext* context) + + static UINT rdpdr_server_receive_prn_cache_add_printer(RdpdrServerContext* context, wStream* s) + { +- char PortDosName[9] = WINPR_C_ARRAY_INIT; +- UINT32 PnPNameLen = 0; +- UINT32 DriverNameLen = 0; +- UINT32 PrinterNameLen = 0; +- UINT32 CachedFieldsLen = 0; +- const WCHAR* PnPName = nullptr; +- const WCHAR* DriverName = nullptr; +- const WCHAR* PrinterName = nullptr; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -1926,19 +1881,24 @@ static UINT rdpdr_server_receive_prn_cache_add_printer(RdpdrServerContext* conte + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 24)) + return ERROR_INVALID_DATA; + ++ char PortDosName[9] = WINPR_C_ARRAY_INIT; ++ UINT32 PnPNameLen = 0; ++ UINT32 DriverNameLen = 0; ++ UINT32 PrinterNameLen = 0; ++ UINT32 CachedFieldsLen = 0; + Stream_Read(s, PortDosName, 8); + Stream_Read_UINT32(s, PnPNameLen); + Stream_Read_UINT32(s, DriverNameLen); + Stream_Read_UINT32(s, PrinterNameLen); + Stream_Read_UINT32(s, CachedFieldsLen); + +- PnPName = rdpdr_read_ustring(priv->log, s, PnPNameLen); ++ const WCHAR* PnPName = rdpdr_read_ustring(priv->log, s, PnPNameLen); + if (!PnPName && (PnPNameLen > 0)) + return ERROR_INVALID_DATA; +- DriverName = rdpdr_read_ustring(priv->log, s, DriverNameLen); ++ const WCHAR* DriverName = rdpdr_read_ustring(priv->log, s, DriverNameLen); + if (!DriverName && (DriverNameLen > 0)) + return ERROR_INVALID_DATA; +- PrinterName = rdpdr_read_ustring(priv->log, s, PrinterNameLen); ++ const WCHAR* PrinterName = rdpdr_read_ustring(priv->log, s, PrinterNameLen); + if (!PrinterName && (PrinterNameLen > 0)) + return ERROR_INVALID_DATA; + +@@ -1954,10 +1914,6 @@ static UINT rdpdr_server_receive_prn_cache_add_printer(RdpdrServerContext* conte + + static UINT rdpdr_server_receive_prn_cache_update_printer(RdpdrServerContext* context, wStream* s) + { +- UINT32 PrinterNameLen = 0; +- UINT32 CachedFieldsLen = 0; +- const WCHAR* PrinterName = nullptr; +- + WINPR_ASSERT(context); + + RdpdrServerPrivate* priv = context->priv; +@@ -1965,10 +1921,12 @@ static UINT rdpdr_server_receive_prn_cache_update_printer(RdpdrServerContext* co + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 8)) + return ERROR_INVALID_DATA; + ++ UINT32 PrinterNameLen = 0; ++ UINT32 CachedFieldsLen = 0; + Stream_Read_UINT32(s, PrinterNameLen); + Stream_Read_UINT32(s, CachedFieldsLen); + +- PrinterName = rdpdr_read_ustring(priv->log, s, PrinterNameLen); ++ const WCHAR* PrinterName = rdpdr_read_ustring(priv->log, s, PrinterNameLen); + if (!PrinterName && (PrinterNameLen > 0)) + return ERROR_INVALID_DATA; + +@@ -1986,9 +1944,6 @@ static UINT rdpdr_server_receive_prn_cache_update_printer(RdpdrServerContext* co + + static UINT rdpdr_server_receive_prn_cache_delete_printer(RdpdrServerContext* context, wStream* s) + { +- UINT32 PrinterNameLen = 0; +- const WCHAR* PrinterName = nullptr; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -1997,9 +1952,10 @@ static UINT rdpdr_server_receive_prn_cache_delete_printer(RdpdrServerContext* co + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + ++ UINT32 PrinterNameLen = 0; + Stream_Read_UINT32(s, PrinterNameLen); + +- PrinterName = rdpdr_read_ustring(priv->log, s, PrinterNameLen); ++ const WCHAR* PrinterName = rdpdr_read_ustring(priv->log, s, PrinterNameLen); + if (!PrinterName && (PrinterNameLen > 0)) + return ERROR_INVALID_DATA; + +@@ -2012,11 +1968,6 @@ static UINT rdpdr_server_receive_prn_cache_delete_printer(RdpdrServerContext* co + + static UINT rdpdr_server_receive_prn_cache_rename_cachedata(RdpdrServerContext* context, wStream* s) + { +- UINT32 OldPrinterNameLen = 0; +- UINT32 NewPrinterNameLen = 0; +- const WCHAR* OldPrinterName = nullptr; +- const WCHAR* NewPrinterName = nullptr; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -2025,13 +1976,15 @@ static UINT rdpdr_server_receive_prn_cache_rename_cachedata(RdpdrServerContext* + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 8)) + return ERROR_INVALID_DATA; + ++ UINT32 OldPrinterNameLen = 0; ++ UINT32 NewPrinterNameLen = 0; + Stream_Read_UINT32(s, OldPrinterNameLen); + Stream_Read_UINT32(s, NewPrinterNameLen); + +- OldPrinterName = rdpdr_read_ustring(priv->log, s, OldPrinterNameLen); ++ const WCHAR* OldPrinterName = rdpdr_read_ustring(priv->log, s, OldPrinterNameLen); + if (!OldPrinterName && (OldPrinterNameLen > 0)) + return ERROR_INVALID_DATA; +- NewPrinterName = rdpdr_read_ustring(priv->log, s, NewPrinterNameLen); ++ const WCHAR* NewPrinterName = rdpdr_read_ustring(priv->log, s, NewPrinterNameLen); + if (!NewPrinterName && (NewPrinterNameLen > 0)) + return ERROR_INVALID_DATA; + +@@ -2046,8 +1999,6 @@ static UINT + rdpdr_server_receive_prn_cache_data_request(RdpdrServerContext* context, wStream* s, + WINPR_ATTR_UNUSED const RDPDR_HEADER* header) + { +- UINT32 EventId = 0; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(header); +@@ -2056,6 +2007,7 @@ rdpdr_server_receive_prn_cache_data_request(RdpdrServerContext* context, wStream + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + ++ UINT32 EventId = 0; + Stream_Read_UINT32(s, EventId); + switch (EventId) + { +@@ -2077,9 +2029,6 @@ rdpdr_server_receive_prn_cache_data_request(RdpdrServerContext* context, wStream + static UINT rdpdr_server_receive_prn_using_xps_request(RdpdrServerContext* context, wStream* s, + WINPR_ATTR_UNUSED const RDPDR_HEADER* header) + { +- UINT32 PrinterId = 0; +- UINT32 Flags = 0; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(header); +@@ -2089,6 +2038,8 @@ static UINT rdpdr_server_receive_prn_using_xps_request(RdpdrServerContext* conte + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 8)) + return ERROR_INVALID_DATA; + ++ UINT32 PrinterId = 0; ++ UINT32 Flags = 0; + Stream_Read_UINT32(s, PrinterId); + Stream_Read_UINT32(s, Flags); + +@@ -2227,20 +2178,19 @@ static UINT rdpdr_server_receive_pdu(RdpdrServerContext* context, wStream* s, + + static DWORD WINAPI rdpdr_server_thread(LPVOID arg) + { +- DWORD status = 0; +- DWORD nCount = 0; +- void* buffer = nullptr; +- HANDLE events[8] = WINPR_C_ARRAY_INIT; +- HANDLE ChannelEvent = nullptr; +- DWORD BytesReturned = 0; +- UINT error = 0; + RdpdrServerContext* context = (RdpdrServerContext*)arg; +- wStream* s = Stream_New(nullptr, 4096); + + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + ++ UINT error = 0; + RdpdrServerPrivate* priv = context->priv; ++ DWORD nCount = 0; ++ void* buffer = nullptr; ++ HANDLE events[8] = WINPR_C_ARRAY_INIT; ++ HANDLE ChannelEvent = nullptr; ++ DWORD BytesReturned = 0; ++ wStream* s = Stream_New(nullptr, 4096); + + if (!s) + { +@@ -2273,7 +2223,7 @@ static DWORD WINAPI rdpdr_server_thread(LPVOID arg) + { + size_t capacity = 0; + BytesReturned = 0; +- status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); ++ DWORD status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); + + if (status == WAIT_FAILED) + { +@@ -2399,19 +2349,17 @@ static UINT rdpdr_server_start(RdpdrServerContext* context) + */ + static UINT rdpdr_server_stop(RdpdrServerContext* context) + { +- UINT error = 0; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + + RdpdrServerPrivate* priv = context->priv; +- + if (priv->StopEvent) + { + (void)SetEvent(priv->StopEvent); + + if (WaitForSingleObject(priv->Thread, INFINITE) == WAIT_FAILED) + { +- error = GetLastError(); ++ UINT error = GetLastError(); + WLog_Print(priv->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!", + error); + return error; +@@ -2452,7 +2400,6 @@ static void rdpdr_server_write_device_iorequest(wStream* s, UINT32 deviceId, UIN + static UINT rdpdr_server_read_file_directory_information(wLog* log, wStream* s, + FILE_DIRECTORY_INFORMATION* fdi) + { +- UINT32 fileNameLength = 0; + WINPR_ASSERT(fdi); + ZeroMemory(fdi, sizeof(FILE_DIRECTORY_INFORMATION)); + +@@ -2468,6 +2415,7 @@ static UINT rdpdr_server_read_file_directory_information(wLog* log, wStream* s, + Stream_Read_INT64(s, fdi->EndOfFile.QuadPart); /* EndOfFile (8 bytes) */ + Stream_Read_INT64(s, fdi->AllocationSize.QuadPart); /* AllocationSize (8 bytes) */ + Stream_Read_UINT32(s, fdi->FileAttributes); /* FileAttributes (4 bytes) */ ++ UINT32 fileNameLength = 0; + Stream_Read_UINT32(s, fileNameLength); /* FileNameLength (4 bytes) */ + + if (!Stream_CheckAndLogRequiredLengthWLog(log, s, fileNameLength)) +@@ -2497,8 +2445,6 @@ static UINT rdpdr_server_send_device_create_request(RdpdrServerContext* context, + UINT32 desiredAccess, UINT32 createOptions, + UINT32 createDisposition) + { +- size_t pathLength = 0; +- wStream* s = nullptr; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -2510,8 +2456,8 @@ static UINT rdpdr_server_send_device_create_request(RdpdrServerContext* context, + " createDisposition=0x%" PRIx32 "", + deviceId, path, desiredAccess, createOptions, createDisposition); + /* Compute the required Unicode size. */ +- pathLength = (strlen(path) + 1U) * sizeof(WCHAR); +- s = Stream_New(nullptr, 256U + pathLength); ++ size_t pathLength = (strlen(path) + 1U) * sizeof(WCHAR); ++ wStream* s = Stream_New(nullptr, 256U + pathLength); + + if (!s) + { +@@ -2547,7 +2493,6 @@ static UINT rdpdr_server_send_device_create_request(RdpdrServerContext* context, + static UINT rdpdr_server_send_device_close_request(RdpdrServerContext* context, UINT32 deviceId, + UINT32 fileId, UINT32 completionId) + { +- wStream* s = nullptr; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -2556,7 +2501,7 @@ static UINT rdpdr_server_send_device_close_request(RdpdrServerContext* context, + WLog_Print(priv->log, WLOG_DEBUG, + "RdpdrServerSendDeviceCloseRequest: deviceId=%" PRIu32 ", fileId=%" PRIu32 "", + deviceId, fileId); +- s = Stream_New(nullptr, 128); ++ wStream* s = Stream_New(nullptr, 128); + + if (!s) + { +@@ -2578,7 +2523,6 @@ static UINT rdpdr_server_send_device_read_request(RdpdrServerContext* context, U + UINT32 fileId, UINT32 completionId, UINT32 length, + UINT32 offset) + { +- wStream* s = nullptr; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -2588,7 +2532,7 @@ static UINT rdpdr_server_send_device_read_request(RdpdrServerContext* context, U + "RdpdrServerSendDeviceReadRequest: deviceId=%" PRIu32 ", fileId=%" PRIu32 + ", length=%" PRIu32 ", offset=%" PRIu32 "", + deviceId, fileId, length, offset); +- s = Stream_New(nullptr, 128); ++ wStream* s = Stream_New(nullptr, 128); + + if (!s) + { +@@ -2613,7 +2557,6 @@ static UINT rdpdr_server_send_device_write_request(RdpdrServerContext* context, + UINT32 fileId, UINT32 completionId, + const char* data, UINT32 length, UINT32 offset) + { +- wStream* s = nullptr; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -2623,7 +2566,7 @@ static UINT rdpdr_server_send_device_write_request(RdpdrServerContext* context, + "RdpdrServerSendDeviceWriteRequest: deviceId=%" PRIu32 ", fileId=%" PRIu32 + ", length=%" PRIu32 ", offset=%" PRIu32 "", + deviceId, fileId, length, offset); +- s = Stream_New(nullptr, 64 + length); ++ wStream* s = Stream_New(nullptr, 64 + length); + + if (!s) + { +@@ -2649,8 +2592,6 @@ static UINT rdpdr_server_send_device_query_directory_request(RdpdrServerContext* + UINT32 deviceId, UINT32 fileId, + UINT32 completionId, const char* path) + { +- size_t pathLength = 0; +- wStream* s = nullptr; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -2661,8 +2602,8 @@ static UINT rdpdr_server_send_device_query_directory_request(RdpdrServerContext* + ", path=%s", + deviceId, fileId, path); + /* Compute the required Unicode size. */ +- pathLength = path ? (strlen(path) + 1) * sizeof(WCHAR) : 0; +- s = Stream_New(nullptr, 64 + pathLength); ++ size_t pathLength = path ? (strlen(path) + 1) * sizeof(WCHAR) : 0; ++ wStream* s = Stream_New(nullptr, 64 + pathLength); + + if (!s) + { +@@ -2701,8 +2642,6 @@ static UINT rdpdr_server_send_device_file_rename_request(RdpdrServerContext* con + UINT32 deviceId, UINT32 fileId, + UINT32 completionId, const char* path) + { +- size_t pathLength = 0; +- wStream* s = nullptr; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +@@ -2713,8 +2652,8 @@ static UINT rdpdr_server_send_device_file_rename_request(RdpdrServerContext* con + ", path=%s", + deviceId, fileId, path); + /* Compute the required Unicode size. */ +- pathLength = path ? (strlen(path) + 1) * sizeof(WCHAR) : 0; +- s = Stream_New(nullptr, 64 + pathLength); ++ size_t pathLength = path ? (strlen(path) + 1) * sizeof(WCHAR) : 0; ++ wStream* s = Stream_New(nullptr, 64 + pathLength); + + if (!s) + { +@@ -2851,14 +2790,13 @@ static UINT rdpdr_server_drive_create_directory_callback1(RdpdrServerContext* co + static UINT rdpdr_server_drive_create_directory(RdpdrServerContext* context, void* callbackData, + UINT32 deviceId, const char* path) + { +- RDPDR_IRP* irp = nullptr; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(callbackData); + WINPR_ASSERT(path); + + RdpdrServerPrivate* priv = context->priv; +- irp = rdpdr_server_irp_new(); ++ RDPDR_IRP* irp = rdpdr_server_irp_new(); + + if (!irp) + { +@@ -2977,12 +2915,11 @@ static UINT rdpdr_server_drive_delete_directory_callback1(RdpdrServerContext* co + static UINT rdpdr_server_drive_delete_directory(RdpdrServerContext* context, void* callbackData, + UINT32 deviceId, const char* path) + { +- RDPDR_IRP* irp = rdpdr_server_irp_new(); + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); +- WINPR_ASSERT(irp); + + RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = rdpdr_server_irp_new(); + + if (!irp) + { +@@ -3023,10 +2960,6 @@ static UINT rdpdr_server_drive_query_directory_callback2(RdpdrServerContext* con + RDPDR_IRP* irp, UINT32 deviceId, + UINT32 completionId, UINT32 ioStatus) + { +- UINT error = 0; +- UINT32 length = 0; +- FILE_DIRECTORY_INFORMATION fdi = WINPR_C_ARRAY_INIT; +- + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); +@@ -3040,11 +2973,14 @@ static UINT rdpdr_server_drive_query_directory_callback2(RdpdrServerContext* con + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + ++ UINT32 length = 0; + Stream_Read_UINT32(s, length); /* Length (4 bytes) */ + ++ FILE_DIRECTORY_INFORMATION fdi = WINPR_C_ARRAY_INIT; + if (length > 0) + { +- if ((error = rdpdr_server_read_file_directory_information(priv->log, s, &fdi))) ++ UINT error = rdpdr_server_read_file_directory_information(priv->log, s, &fdi); ++ if (error) + { + WLog_Print(priv->log, WLOG_ERROR, + "rdpdr_server_read_file_directory_information failed with error %" PRIu32 +@@ -3151,12 +3087,11 @@ static UINT rdpdr_server_drive_query_directory_callback1(RdpdrServerContext* con + static UINT rdpdr_server_drive_query_directory(RdpdrServerContext* context, void* callbackData, + UINT32 deviceId, const char* path) + { +- RDPDR_IRP* irp = rdpdr_server_irp_new(); + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); +- WINPR_ASSERT(irp); + + RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = rdpdr_server_irp_new(); + + if (!irp) + { +@@ -3231,12 +3166,11 @@ static UINT rdpdr_server_drive_open_file(RdpdrServerContext* context, void* call + UINT32 deviceId, const char* path, UINT32 desiredAccess, + UINT32 createDisposition) + { +- RDPDR_IRP* irp = rdpdr_server_irp_new(); + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); +- WINPR_ASSERT(irp); + + RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = rdpdr_server_irp_new(); + + if (!irp) + { +@@ -3277,8 +3211,6 @@ static UINT rdpdr_server_drive_read_file_callback(RdpdrServerContext* context, w + RDPDR_IRP* irp, UINT32 deviceId, + UINT32 completionId, UINT32 ioStatus) + { +- UINT32 length = 0; +- char* buffer = nullptr; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); +@@ -3292,11 +3224,13 @@ static UINT rdpdr_server_drive_read_file_callback(RdpdrServerContext* context, w + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + ++ UINT32 length = 0; + Stream_Read_UINT32(s, length); /* Length (4 bytes) */ + + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, length)) + return ERROR_INVALID_DATA; + ++ char* buffer = nullptr; + if (length > 0) + { + buffer = Stream_Pointer(s); +@@ -3319,12 +3253,11 @@ static UINT rdpdr_server_drive_read_file(RdpdrServerContext* context, void* call + UINT32 deviceId, UINT32 fileId, UINT32 length, + UINT32 offset) + { +- RDPDR_IRP* irp = rdpdr_server_irp_new(); + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); +- WINPR_ASSERT(irp); + + RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = rdpdr_server_irp_new(); + + if (!irp) + { +@@ -3364,7 +3297,6 @@ static UINT rdpdr_server_drive_write_file_callback(RdpdrServerContext* context, + RDPDR_IRP* irp, UINT32 deviceId, + UINT32 completionId, UINT32 ioStatus) + { +- UINT32 length = 0; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); +@@ -3378,6 +3310,7 @@ static UINT rdpdr_server_drive_write_file_callback(RdpdrServerContext* context, + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 5)) + return ERROR_INVALID_DATA; + ++ UINT32 length = 0; + Stream_Read_UINT32(s, length); /* Length (4 bytes) */ + Stream_Seek(s, 1); /* Padding (1 byte) */ + +@@ -3400,12 +3333,11 @@ static UINT rdpdr_server_drive_write_file(RdpdrServerContext* context, void* cal + UINT32 deviceId, UINT32 fileId, const char* buffer, + UINT32 length, UINT32 offset) + { +- RDPDR_IRP* irp = rdpdr_server_irp_new(); + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); +- WINPR_ASSERT(irp); + + RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = rdpdr_server_irp_new(); + + if (!irp) + { +@@ -3478,12 +3410,11 @@ static UINT rdpdr_server_drive_close_file_callback(RdpdrServerContext* context, + static UINT rdpdr_server_drive_close_file(RdpdrServerContext* context, void* callbackData, + UINT32 deviceId, UINT32 fileId) + { +- RDPDR_IRP* irp = rdpdr_server_irp_new(); + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); +- WINPR_ASSERT(irp); + + RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = rdpdr_server_irp_new(); + + if (!irp) + { +@@ -3600,12 +3531,11 @@ static UINT rdpdr_server_drive_delete_file_callback1(RdpdrServerContext* context + static UINT rdpdr_server_drive_delete_file(RdpdrServerContext* context, void* callbackData, + UINT32 deviceId, const char* path) + { +- RDPDR_IRP* irp = rdpdr_server_irp_new(); + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); +- WINPR_ASSERT(irp); + + RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = rdpdr_server_irp_new(); + + if (!irp) + { +@@ -3671,7 +3601,6 @@ static UINT rdpdr_server_drive_rename_file_callback2(RdpdrServerContext* context + RDPDR_IRP* irp, UINT32 deviceId, + UINT32 completionId, UINT32 ioStatus) + { +- UINT32 length = 0; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + WINPR_ASSERT(irp); +@@ -3685,6 +3614,7 @@ static UINT rdpdr_server_drive_rename_file_callback2(RdpdrServerContext* context + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 5)) + return ERROR_INVALID_DATA; + ++ UINT32 length = 0; + Stream_Read_UINT32(s, length); /* Length (4 bytes) */ + Stream_Seek(s, 1); /* Padding (1 byte) */ + /* Invoke the rename file completion routine. */ + +From d67800b0d1c7ac10f574a56bcfb8d36e3f17bd3f Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Mon, 22 Jun 2026 21:36:20 +0200 +Subject: [PATCH 14/31] [channels,rdpdr] migrate Stream API usage + +- Stream_Read_UINT32/UINT16 to Stream_Get_UINT32/UINT16 +- Stream_Free to Stream_Release +- Stream_Pointer to Stream_PointerAs + +Co-Authored-By: David Fort +--- + channels/rdpdr/server/rdpdr_main.c | 271 +++++++++++------------------ + 1 file changed, 104 insertions(+), 167 deletions(-) + +diff --git a/channels/rdpdr/server/rdpdr_main.c b/channels/rdpdr/server/rdpdr_main.c +index 488efca5bc46..132f0746a17a 100644 +--- a/channels/rdpdr/server/rdpdr_main.c ++++ b/channels/rdpdr/server/rdpdr_main.c +@@ -281,9 +281,10 @@ static UINT rdpdr_seal_send_free_request(RdpdrServerContext* context, wStream* s + + if (length >= RDPDR_HEADER_LENGTH) + { +- RDPDR_HEADER header = WINPR_C_ARRAY_INIT; +- Stream_Read_UINT16(s, header.Component); +- Stream_Read_UINT16(s, header.PacketId); ++ const RDPDR_HEADER header = { ++ .Component = Stream_Get_UINT16(s), ++ .PacketId = Stream_Get_UINT16(s), ++ }; + + WLog_Print(priv->log, WLOG_DEBUG, + "sending message {Component %s[%04" PRIx16 "], PacketId %s[%04" PRIx16 "]", +@@ -294,7 +295,7 @@ static UINT rdpdr_seal_send_free_request(RdpdrServerContext* context, wStream* s + ULONG written = 0; + BOOL status = WTSVirtualChannelWrite(priv->ChannelHandle, Stream_BufferAs(s, char), + (ULONG)length, &written); +- Stream_Free(s, TRUE); ++ Stream_Release(s); + return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR; + } + +@@ -351,12 +352,9 @@ static UINT rdpdr_server_receive_announce_response(RdpdrServerContext* context, + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 8)) + return ERROR_INVALID_DATA; + +- UINT16 VersionMajor = 0; +- UINT16 VersionMinor = 0; +- UINT32 ClientId = 0; +- Stream_Read_UINT16(s, VersionMajor); /* VersionMajor (2 bytes) */ +- Stream_Read_UINT16(s, VersionMinor); /* VersionMinor (2 bytes) */ +- Stream_Read_UINT32(s, ClientId); /* ClientId (4 bytes) */ ++ const UINT16 VersionMajor = Stream_Get_UINT16(s); /* VersionMajor (2 bytes) */ ++ const UINT16 VersionMinor = Stream_Get_UINT16(s); /* VersionMinor (2 bytes) */ ++ const UINT32 ClientId = Stream_Get_UINT32(s); /* ClientId (4 bytes) */ + WLog_Print(priv->log, WLOG_DEBUG, + "Client Announce Response: VersionMajor: 0x%08" PRIX16 " VersionMinor: 0x%04" PRIX16 + " ClientId: 0x%08" PRIX32 "", +@@ -386,12 +384,9 @@ static UINT rdpdr_server_receive_client_name_request(RdpdrServerContext* context + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 12)) + return ERROR_INVALID_DATA; + +- UINT32 UnicodeFlag = 0; +- UINT32 CodePage = 0; +- UINT32 ComputerNameLen = 0; +- Stream_Read_UINT32(s, UnicodeFlag); /* UnicodeFlag (4 bytes) */ +- Stream_Read_UINT32(s, CodePage); /* CodePage (4 bytes), MUST be set to zero */ +- Stream_Read_UINT32(s, ComputerNameLen); /* ComputerNameLen (4 bytes) */ ++ UINT32 UnicodeFlag = Stream_Get_UINT32(s); /* UnicodeFlag (4 bytes) */ ++ const UINT32 CodePage = Stream_Get_UINT32(s); /* CodePage (4 bytes) */ ++ const UINT32 ComputerNameLen = Stream_Get_UINT32(s); /* ComputerNameLen (4 bytes) */ + /* UnicodeFlag is either 0 or 1, the other 31 bits must be ignored. + */ + UnicodeFlag = UnicodeFlag & 0x00000001; +@@ -502,18 +497,19 @@ static UINT rdpdr_server_read_general_capability_set(RdpdrServerContext* context + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + +- const UINT32 OsType = Stream_Get_UINT32(s); /* osType (4 bytes), ignored on receipt */ +- const UINT32 OsVersion = +- Stream_Get_UINT32(s); /* osVersion (4 bytes), unused and must be set to zero */ ++ const UINT32 OsType = Stream_Get_UINT32(s); /* osType (4 bytes), ++ ignored on receipt */ ++ const UINT32 OsVersion = Stream_Get_UINT32(s); /* osVersion (4 bytes), ++ unused and must be set to zero */ + const UINT32 VersionMajor = Stream_Get_UINT16(s); /* protocolMajorVersion (2 bytes) */ + const UINT32 VersionMinor = Stream_Get_UINT16(s); /* protocolMinorVersion (2 bytes) */ + const UINT32 IoCode1 = Stream_Get_UINT32(s); /* ioCode1 (4 bytes) */ +- const UINT32 IoCode2 = +- Stream_Get_UINT32(s); /* ioCode2 (4 bytes), must be set to zero, reserved for future use */ ++ const UINT32 IoCode2 = Stream_Get_UINT32(s); /* ioCode2 (4 bytes), ++ must be set to zero, reserved for future use */ + const UINT32 ExtendedPdu = Stream_Get_UINT32(s); /* extendedPdu (4 bytes) */ + const UINT32 ExtraFlags1 = Stream_Get_UINT32(s); /* extraFlags1 (4 bytes) */ +- const UINT32 ExtraFlags2 = Stream_Get_UINT32( +- s); /* extraFlags2 (4 bytes), must be set to zero, reserved for future use */ ++ const UINT32 ExtraFlags2 = Stream_Get_UINT32(s); /* extraFlags2 (4 bytes), ++ must be set to zero, reserved for future use */ + + { + char buffer[1024] = WINPR_C_ARRAY_INIT; +@@ -901,7 +897,7 @@ static UINT rdpdr_server_send_core_capability_request(RdpdrServerContext* contex + + return rdpdr_seal_send_free_request(context, s); + out: +- Stream_Free(s, TRUE); ++ Stream_Release(s); + return error; + } + +@@ -941,9 +937,8 @@ static UINT rdpdr_server_receive_core_capability_response(RdpdrServerContext* co + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + +- UINT16 numCapabilities = 0; +- Stream_Read_UINT16(s, numCapabilities); /* numCapabilities (2 bytes) */ +- Stream_Seek_UINT16(s); /* Padding (2 bytes) */ ++ const UINT16 numCapabilities = Stream_Get_UINT16(s); /* numCapabilities (2 bytes) */ ++ Stream_Seek_UINT16(s); /* Padding (2 bytes) */ + + UINT16 caps = 0; + for (UINT16 i = 0; i < numCapabilities; i++) +@@ -1096,8 +1091,7 @@ static UINT rdpdr_server_receive_device_list_announce_request(RdpdrServerContext + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + +- UINT32 DeviceCount = 0; +- Stream_Read_UINT32(s, DeviceCount); /* DeviceCount (4 bytes) */ ++ const UINT32 DeviceCount = Stream_Get_UINT32(s); /* DeviceCount (4 bytes) */ + WLog_Print(priv->log, WLOG_DEBUG, "DeviceCount: %" PRIu32 "", DeviceCount); + + for (UINT32 i = 0; i < DeviceCount; i++) +@@ -1106,11 +1100,11 @@ static UINT rdpdr_server_receive_device_list_announce_request(RdpdrServerContext + return ERROR_INVALID_DATA; + + RdpdrDevice device = WINPR_C_ARRAY_INIT; +- Stream_Read_UINT32(s, device.DeviceType); /* DeviceType (4 bytes) */ +- Stream_Read_UINT32(s, device.DeviceId); /* DeviceId (4 bytes) */ ++ device.DeviceType = Stream_Get_UINT32(s); /* DeviceType (4 bytes) */ ++ device.DeviceId = Stream_Get_UINT32(s); /* DeviceId (4 bytes) */ + Stream_Read(s, device.PreferredDosName, 8); /* PreferredDosName (8 bytes) */ +- Stream_Read_UINT32(s, device.DeviceDataLength); /* DeviceDataLength (4 bytes) */ +- device.DeviceData = Stream_Pointer(s); ++ device.DeviceDataLength = Stream_Get_UINT32(s); /* DeviceDataLength (4 bytes) */ ++ device.DeviceData = Stream_PointerAs(s, BYTE); + + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, device.DeviceDataLength)) + return ERROR_INVALID_DATA; +@@ -1210,8 +1204,7 @@ static UINT rdpdr_server_receive_device_list_remove_request(RdpdrServerContext* + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + +- UINT32 DeviceCount = 0; +- Stream_Read_UINT32(s, DeviceCount); /* DeviceCount (4 bytes) */ ++ const UINT32 DeviceCount = Stream_Get_UINT32(s); /* DeviceCount (4 bytes) */ + WLog_Print(priv->log, WLOG_DEBUG, "DeviceCount: %" PRIu32 "", DeviceCount); + + for (UINT32 i = 0; i < DeviceCount; i++) +@@ -1222,8 +1215,7 @@ static UINT rdpdr_server_receive_device_list_remove_request(RdpdrServerContext* + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + +- UINT32 DeviceId = 0; +- Stream_Read_UINT32(s, DeviceId); /* DeviceId (4 bytes) */ ++ const UINT32 DeviceId = Stream_Get_UINT32(s); /* DeviceId (4 bytes) */ + device = rdpdr_get_device_by_id(context->priv, DeviceId); + WLog_Print(priv->log, WLOG_DEBUG, "Device %" PRIu32 " Id: 0x%08" PRIX32 "", i, DeviceId); + UINT32 DeviceType = 0; +@@ -1291,20 +1283,13 @@ static UINT rdpdr_server_receive_io_create_request(RdpdrServerContext* context, + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + +- UINT32 DesiredAccess = 0; +- UINT32 AllocationSize = 0; +- UINT32 FileAttributes = 0; +- UINT32 SharedAccess = 0; +- UINT32 CreateDisposition = 0; +- UINT32 CreateOptions = 0; +- UINT32 PathLength = 0; +- Stream_Read_UINT32(s, DesiredAccess); +- Stream_Read_UINT32(s, AllocationSize); +- Stream_Read_UINT32(s, FileAttributes); +- Stream_Read_UINT32(s, SharedAccess); +- Stream_Read_UINT32(s, CreateDisposition); +- Stream_Read_UINT32(s, CreateOptions); +- Stream_Read_UINT32(s, PathLength); ++ WINPR_ATTR_UNUSED const UINT32 DesiredAccess = Stream_Get_UINT32(s); ++ WINPR_ATTR_UNUSED const UINT32 AllocationSize = Stream_Get_UINT32(s); ++ WINPR_ATTR_UNUSED const UINT32 FileAttributes = Stream_Get_UINT32(s); ++ WINPR_ATTR_UNUSED const UINT32 SharedAccess = Stream_Get_UINT32(s); ++ WINPR_ATTR_UNUSED const UINT32 CreateDisposition = Stream_Get_UINT32(s); ++ WINPR_ATTR_UNUSED const UINT32 CreateOptions = Stream_Get_UINT32(s); ++ const UINT32 PathLength = Stream_Get_UINT32(s); + + const WCHAR* path = rdpdr_read_ustring(priv->log, s, PathLength); + if (!path && (PathLength > 0)) +@@ -1351,10 +1336,8 @@ static UINT rdpdr_server_receive_io_read_request(RdpdrServerContext* context, wS + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + +- UINT32 Length = 0; +- UINT64 Offset = 0; +- Stream_Read_UINT32(s, Length); +- Stream_Read_UINT64(s, Offset); ++ const UINT32 Length = Stream_Get_UINT32(s); ++ const UINT64 Offset = Stream_Get_UINT64(s); + Stream_Seek(s, 20); /* Padding */ + + WLog_Print(priv->log, WLOG_DEBUG, "Got Offset [0x%016" PRIx64 "], Length %" PRIu32, Offset, +@@ -1380,10 +1363,8 @@ static UINT rdpdr_server_receive_io_write_request(RdpdrServerContext* context, w + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + +- UINT32 Length = 0; +- UINT64 Offset = 0; +- Stream_Read_UINT32(s, Length); +- Stream_Read_UINT64(s, Offset); ++ const UINT32 Length = Stream_Get_UINT32(s); ++ const UINT64 Offset = Stream_Get_UINT64(s); + Stream_Seek(s, 20); /* Padding */ + + WLog_Print(priv->log, WLOG_DEBUG, "Got Offset [0x%016" PRIx64 "], Length %" PRIu32, Offset, +@@ -1414,12 +1395,9 @@ static UINT rdpdr_server_receive_io_device_control_request(RdpdrServerContext* c + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + +- UINT32 OutputBufferLength = 0; +- UINT32 InputBufferLength = 0; +- UINT32 IoControlCode = 0; +- Stream_Read_UINT32(s, OutputBufferLength); +- Stream_Read_UINT32(s, InputBufferLength); +- Stream_Read_UINT32(s, IoControlCode); ++ WINPR_ATTR_UNUSED const UINT32 OutputBufferLength = Stream_Get_UINT32(s); ++ WINPR_ATTR_UNUSED const UINT32 InputBufferLength = Stream_Get_UINT32(s); ++ WINPR_ATTR_UNUSED const UINT32 IoControlCode = Stream_Get_UINT32(s); + Stream_Seek(s, 20); /* Padding */ + + const BYTE* InputBuffer = Stream_ConstPointer(s); +@@ -1445,10 +1423,8 @@ static UINT rdpdr_server_receive_io_query_volume_information_request( + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + +- UINT32 FsInformationClass = 0; +- UINT32 Length = 0; +- Stream_Read_UINT32(s, FsInformationClass); +- Stream_Read_UINT32(s, Length); ++ const UINT32 FsInformationClass = Stream_Get_UINT32(s); ++ const UINT32 Length = Stream_Get_UINT32(s); + Stream_Seek(s, 24); /* Padding */ + + WLog_Print(priv->log, WLOG_DEBUG, +@@ -1480,10 +1456,8 @@ static UINT rdpdr_server_receive_io_set_volume_information_request( + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + +- UINT32 FsInformationClass = 0; +- UINT32 Length = 0; +- Stream_Read_UINT32(s, FsInformationClass); +- Stream_Read_UINT32(s, Length); ++ const UINT32 FsInformationClass = Stream_Get_UINT32(s); ++ const UINT32 Length = Stream_Get_UINT32(s); + Stream_Seek(s, 24); /* Padding */ + + WLog_Print(priv->log, WLOG_DEBUG, +@@ -1517,10 +1491,8 @@ static UINT rdpdr_server_receive_io_query_information_request(RdpdrServerContext + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + +- UINT32 FsInformationClass = 0; +- UINT32 Length = 0; +- Stream_Read_UINT32(s, FsInformationClass); +- Stream_Read_UINT32(s, Length); ++ const UINT32 FsInformationClass = Stream_Get_UINT32(s); ++ const UINT32 Length = Stream_Get_UINT32(s); + Stream_Seek(s, 24); /* Padding */ + + WLog_Print(priv->log, WLOG_DEBUG, +@@ -1553,10 +1525,8 @@ static UINT rdpdr_server_receive_io_set_information_request(RdpdrServerContext* + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + +- UINT32 FsInformationClass = 0; +- UINT32 Length = 0; +- Stream_Read_UINT32(s, FsInformationClass); +- Stream_Read_UINT32(s, Length); ++ const UINT32 FsInformationClass = Stream_Get_UINT32(s); ++ const UINT32 Length = Stream_Get_UINT32(s); + Stream_Seek(s, 24); /* Padding */ + + WLog_Print(priv->log, WLOG_DEBUG, +@@ -1589,12 +1559,9 @@ static UINT rdpdr_server_receive_io_query_directory_request(RdpdrServerContext* + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + +- UINT32 FsInformationClass = 0; +- BYTE InitialQuery = 0; +- UINT32 PathLength = 0; +- Stream_Read_UINT32(s, FsInformationClass); +- Stream_Read_UINT8(s, InitialQuery); +- Stream_Read_UINT32(s, PathLength); ++ const UINT32 FsInformationClass = Stream_Get_UINT32(s); ++ const BYTE InitialQuery = Stream_Get_UINT8(s); ++ const UINT32 PathLength = Stream_Get_UINT32(s); + Stream_Seek(s, 23); /* Padding */ + + const WCHAR* wPath = rdpdr_read_ustring(priv->log, s, PathLength); +@@ -1631,10 +1598,8 @@ static UINT rdpdr_server_receive_io_change_directory_request(RdpdrServerContext* + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32)) + return ERROR_INVALID_DATA; + +- BYTE WatchTree = 0; +- UINT32 CompletionFilter = 0; +- Stream_Read_UINT8(s, WatchTree); +- Stream_Read_UINT32(s, CompletionFilter); ++ WINPR_ATTR_UNUSED const BYTE WatchTree = Stream_Get_UINT8(s); ++ WINPR_ATTR_UNUSED const UINT32 CompletionFilter = Stream_Get_UINT32(s); + Stream_Seek(s, 27); /* Padding */ + + WLog_Print(priv->log, WLOG_WARN, +@@ -1697,14 +1662,11 @@ static UINT rdpdr_server_receive_io_lock_control_request(RdpdrServerContext* con + + for (UINT32 x = 0; x < NumLocks; x++) + { +- UINT64 Length = 0; +- UINT64 Offset = 0; +- + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 16)) + return ERROR_INVALID_DATA; + +- Stream_Read_UINT64(s, Length); +- Stream_Read_UINT64(s, Offset); ++ const UINT64 Length = Stream_Get_UINT64(s); ++ const UINT64 Offset = Stream_Get_UINT64(s); + + WLog_Print(priv->log, WLOG_DEBUG, "Locking at Offset=0x%08" PRIx64 " [Length %" PRIu64 "]", + Offset, Length); +@@ -1732,16 +1694,11 @@ static UINT rdpdr_server_receive_device_io_request(RdpdrServerContext* context, + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 20)) + return ERROR_INVALID_DATA; + +- UINT32 DeviceId = 0; +- UINT32 FileId = 0; +- UINT32 CompletionId = 0; +- UINT32 MajorFunction = 0; +- UINT32 MinorFunction = 0; +- Stream_Read_UINT32(s, DeviceId); +- Stream_Read_UINT32(s, FileId); +- Stream_Read_UINT32(s, CompletionId); +- Stream_Read_UINT32(s, MajorFunction); +- Stream_Read_UINT32(s, MinorFunction); ++ const UINT32 DeviceId = Stream_Get_UINT32(s); ++ const UINT32 FileId = Stream_Get_UINT32(s); ++ const UINT32 CompletionId = Stream_Get_UINT32(s); ++ const UINT32 MajorFunction = Stream_Get_UINT32(s); ++ const UINT32 MinorFunction = Stream_Get_UINT32(s); + if ((MinorFunction != 0) && (MajorFunction != IRP_MJ_DIRECTORY_CONTROL)) + WLog_Print(priv->log, WLOG_WARN, + "[MS-RDPEFS] 2.2.1.4 Device I/O Request (DR_DEVICE_IOREQUEST) MajorFunction=%s, " +@@ -1816,12 +1773,9 @@ static UINT rdpdr_server_receive_device_io_completion(RdpdrServerContext* contex + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 12)) + return ERROR_INVALID_DATA; + +- UINT32 deviceId = 0; +- UINT32 completionId = 0; +- UINT32 ioStatus = 0; +- Stream_Read_UINT32(s, deviceId); +- Stream_Read_UINT32(s, completionId); +- Stream_Read_UINT32(s, ioStatus); ++ const UINT32 deviceId = Stream_Get_UINT32(s); ++ const UINT32 completionId = Stream_Get_UINT32(s); ++ const UINT32 ioStatus = Stream_Get_UINT32(s); + WLog_Print(priv->log, WLOG_DEBUG, + "deviceId=%" PRIu32 ", completionId=0x%" PRIx32 ", ioStatus=0x%" PRIx32 "", deviceId, + completionId, ioStatus); +@@ -1882,15 +1836,11 @@ static UINT rdpdr_server_receive_prn_cache_add_printer(RdpdrServerContext* conte + return ERROR_INVALID_DATA; + + char PortDosName[9] = WINPR_C_ARRAY_INIT; +- UINT32 PnPNameLen = 0; +- UINT32 DriverNameLen = 0; +- UINT32 PrinterNameLen = 0; +- UINT32 CachedFieldsLen = 0; + Stream_Read(s, PortDosName, 8); +- Stream_Read_UINT32(s, PnPNameLen); +- Stream_Read_UINT32(s, DriverNameLen); +- Stream_Read_UINT32(s, PrinterNameLen); +- Stream_Read_UINT32(s, CachedFieldsLen); ++ const UINT32 PnPNameLen = Stream_Get_UINT32(s); ++ const UINT32 DriverNameLen = Stream_Get_UINT32(s); ++ const UINT32 PrinterNameLen = Stream_Get_UINT32(s); ++ const UINT32 CachedFieldsLen = Stream_Get_UINT32(s); + + const WCHAR* PnPName = rdpdr_read_ustring(priv->log, s, PnPNameLen); + if (!PnPName && (PnPNameLen > 0)) +@@ -1921,10 +1871,8 @@ static UINT rdpdr_server_receive_prn_cache_update_printer(RdpdrServerContext* co + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 8)) + return ERROR_INVALID_DATA; + +- UINT32 PrinterNameLen = 0; +- UINT32 CachedFieldsLen = 0; +- Stream_Read_UINT32(s, PrinterNameLen); +- Stream_Read_UINT32(s, CachedFieldsLen); ++ const UINT32 PrinterNameLen = Stream_Get_UINT32(s); ++ const UINT32 CachedFieldsLen = Stream_Get_UINT32(s); + + const WCHAR* PrinterName = rdpdr_read_ustring(priv->log, s, PrinterNameLen); + if (!PrinterName && (PrinterNameLen > 0)) +@@ -1952,8 +1900,7 @@ static UINT rdpdr_server_receive_prn_cache_delete_printer(RdpdrServerContext* co + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + +- UINT32 PrinterNameLen = 0; +- Stream_Read_UINT32(s, PrinterNameLen); ++ const UINT32 PrinterNameLen = Stream_Get_UINT32(s); + + const WCHAR* PrinterName = rdpdr_read_ustring(priv->log, s, PrinterNameLen); + if (!PrinterName && (PrinterNameLen > 0)) +@@ -1976,10 +1923,8 @@ static UINT rdpdr_server_receive_prn_cache_rename_cachedata(RdpdrServerContext* + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 8)) + return ERROR_INVALID_DATA; + +- UINT32 OldPrinterNameLen = 0; +- UINT32 NewPrinterNameLen = 0; +- Stream_Read_UINT32(s, OldPrinterNameLen); +- Stream_Read_UINT32(s, NewPrinterNameLen); ++ const UINT32 OldPrinterNameLen = Stream_Get_UINT32(s); ++ const UINT32 NewPrinterNameLen = Stream_Get_UINT32(s); + + const WCHAR* OldPrinterName = rdpdr_read_ustring(priv->log, s, OldPrinterNameLen); + if (!OldPrinterName && (OldPrinterNameLen > 0)) +@@ -2007,8 +1952,7 @@ rdpdr_server_receive_prn_cache_data_request(RdpdrServerContext* context, wStream + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + +- UINT32 EventId = 0; +- Stream_Read_UINT32(s, EventId); ++ const UINT32 EventId = Stream_Get_UINT32(s); + switch (EventId) + { + case RDPDR_ADD_PRINTER_EVENT: +@@ -2038,10 +1982,8 @@ static UINT rdpdr_server_receive_prn_using_xps_request(RdpdrServerContext* conte + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 8)) + return ERROR_INVALID_DATA; + +- UINT32 PrinterId = 0; +- UINT32 Flags = 0; +- Stream_Read_UINT32(s, PrinterId); +- Stream_Read_UINT32(s, Flags); ++ const UINT32 PrinterId = Stream_Get_UINT32(s); ++ const UINT32 Flags = Stream_Get_UINT32(s); + + WLog_Print( + priv->log, WLOG_WARN, +@@ -2279,10 +2221,10 @@ static DWORD WINAPI rdpdr_server_thread(LPVOID arg) + + while (Stream_GetRemainingLength(s) >= RDPDR_HEADER_LENGTH) + { +- RDPDR_HEADER header = WINPR_C_ARRAY_INIT; +- +- Stream_Read_UINT16(s, header.Component); /* Component (2 bytes) */ +- Stream_Read_UINT16(s, header.PacketId); /* PacketId (2 bytes) */ ++ const RDPDR_HEADER header = { ++ .Component = Stream_Get_UINT16(s), /* Component (2 bytes) */ ++ .PacketId = Stream_Get_UINT16(s), /* PacketId (2 bytes) */ ++ }; + + if ((error = rdpdr_server_receive_pdu(context, s, &header))) + { +@@ -2295,7 +2237,7 @@ static DWORD WINAPI rdpdr_server_thread(LPVOID arg) + } + + out_stream: +- Stream_Free(s, TRUE); ++ Stream_Release(s); + out: + + if (error && context->rdpcontext) +@@ -2406,17 +2348,16 @@ static UINT rdpdr_server_read_file_directory_information(wLog* log, wStream* s, + if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 64)) + return ERROR_INVALID_DATA; + +- Stream_Read_UINT32(s, fdi->NextEntryOffset); /* NextEntryOffset (4 bytes) */ +- Stream_Read_UINT32(s, fdi->FileIndex); /* FileIndex (4 bytes) */ +- Stream_Read_INT64(s, fdi->CreationTime.QuadPart); /* CreationTime (8 bytes) */ +- Stream_Read_INT64(s, fdi->LastAccessTime.QuadPart); /* LastAccessTime (8 bytes) */ +- Stream_Read_INT64(s, fdi->LastWriteTime.QuadPart); /* LastWriteTime (8 bytes) */ +- Stream_Read_INT64(s, fdi->ChangeTime.QuadPart); /* ChangeTime (8 bytes) */ +- Stream_Read_INT64(s, fdi->EndOfFile.QuadPart); /* EndOfFile (8 bytes) */ +- Stream_Read_INT64(s, fdi->AllocationSize.QuadPart); /* AllocationSize (8 bytes) */ +- Stream_Read_UINT32(s, fdi->FileAttributes); /* FileAttributes (4 bytes) */ +- UINT32 fileNameLength = 0; +- Stream_Read_UINT32(s, fileNameLength); /* FileNameLength (4 bytes) */ ++ fdi->NextEntryOffset = Stream_Get_UINT32(s); /* NextEntryOffset (4 bytes) */ ++ fdi->FileIndex = Stream_Get_UINT32(s); /* FileIndex (4 bytes) */ ++ fdi->CreationTime.QuadPart = Stream_Get_INT64(s); /* CreationTime (8 bytes) */ ++ fdi->LastAccessTime.QuadPart = Stream_Get_INT64(s); /* LastAccessTime (8 bytes) */ ++ fdi->LastWriteTime.QuadPart = Stream_Get_INT64(s); /* LastWriteTime (8 bytes) */ ++ fdi->ChangeTime.QuadPart = Stream_Get_INT64(s); /* ChangeTime (8 bytes) */ ++ fdi->EndOfFile.QuadPart = Stream_Get_INT64(s); /* EndOfFile (8 bytes) */ ++ fdi->AllocationSize.QuadPart = Stream_Get_INT64(s); /* AllocationSize (8 bytes) */ ++ fdi->FileAttributes = Stream_Get_UINT32(s); /* FileAttributes (4 bytes) */ ++ const UINT32 fileNameLength = Stream_Get_UINT32(s); /* FileNameLength (4 bytes) */ + + if (!Stream_CheckAndLogRequiredLengthWLog(log, s, fileNameLength)) + return ERROR_INVALID_DATA; +@@ -2479,7 +2420,7 @@ static UINT rdpdr_server_send_device_create_request(RdpdrServerContext* context, + if (Stream_Write_UTF16_String_From_UTF8(s, pathLength / sizeof(WCHAR), path, + pathLength / sizeof(WCHAR), TRUE) < 0) + { +- Stream_Free(s, TRUE); ++ Stream_Release(s); + return ERROR_INTERNAL_ERROR; + } + return rdpdr_seal_send_free_request(context, s); +@@ -2625,7 +2566,7 @@ static UINT rdpdr_server_send_device_query_directory_request(RdpdrServerContext* + if (Stream_Write_UTF16_String_From_UTF8(s, pathLength / sizeof(WCHAR), path, + pathLength / sizeof(WCHAR), TRUE) < 0) + { +- Stream_Free(s, TRUE); ++ Stream_Release(s); + return ERROR_INTERNAL_ERROR; + } + } +@@ -2678,7 +2619,7 @@ static UINT rdpdr_server_send_device_file_rename_request(RdpdrServerContext* con + if (Stream_Write_UTF16_String_From_UTF8(s, pathLength / sizeof(WCHAR), path, + pathLength / sizeof(WCHAR), TRUE) < 0) + { +- Stream_Free(s, TRUE); ++ Stream_Release(s); + return ERROR_INTERNAL_ERROR; + } + } +@@ -2973,8 +2914,7 @@ static UINT rdpdr_server_drive_query_directory_callback2(RdpdrServerContext* con + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + +- UINT32 length = 0; +- Stream_Read_UINT32(s, length); /* Length (4 bytes) */ ++ const UINT32 length = Stream_Get_UINT32(s); /* Length (4 bytes) */ + + FILE_DIRECTORY_INFORMATION fdi = WINPR_C_ARRAY_INIT; + if (length > 0) +@@ -3224,16 +3164,14 @@ static UINT rdpdr_server_drive_read_file_callback(RdpdrServerContext* context, w + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4)) + return ERROR_INVALID_DATA; + +- UINT32 length = 0; +- Stream_Read_UINT32(s, length); /* Length (4 bytes) */ +- ++ const UINT32 length = Stream_Get_UINT32(s); /* Length (4 bytes) */ + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, length)) + return ERROR_INVALID_DATA; + + char* buffer = nullptr; + if (length > 0) + { +- buffer = Stream_Pointer(s); ++ buffer = Stream_PointerAs(s, char); + Stream_Seek(s, length); + } + +@@ -3310,9 +3248,8 @@ static UINT rdpdr_server_drive_write_file_callback(RdpdrServerContext* context, + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 5)) + return ERROR_INVALID_DATA; + +- UINT32 length = 0; +- Stream_Read_UINT32(s, length); /* Length (4 bytes) */ +- Stream_Seek(s, 1); /* Padding (1 byte) */ ++ const UINT32 length = Stream_Get_UINT32(s); /* Length (4 bytes) */ ++ Stream_Seek(s, 1); /* Padding (1 byte) */ + + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, length)) + return ERROR_INVALID_DATA; +@@ -3614,9 +3551,9 @@ static UINT rdpdr_server_drive_rename_file_callback2(RdpdrServerContext* context + if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 5)) + return ERROR_INVALID_DATA; + +- UINT32 length = 0; +- Stream_Read_UINT32(s, length); /* Length (4 bytes) */ +- Stream_Seek(s, 1); /* Padding (1 byte) */ ++ WINPR_ATTR_UNUSED const UINT32 length = Stream_Get_UINT32(s); /* Length (4 bytes) */ ++ Stream_Seek(s, 1); /* Padding (1 byte) */ ++ + /* Invoke the rename file completion routine. */ + context->OnDriveRenameFileComplete(context, irp->CallbackData, ioStatus); + /* Setup the IRP. */ + +From 1c81bb4568234088886378e73d465e3717687271 Mon Sep 17 00:00:00 2001 +From: David Fort +Date: Tue, 23 Jun 2026 20:07:50 +0200 +Subject: [PATCH 15/31] [channels] handle CHANNEL_OPTION_SHOW_PROTOCOL + server-side + +With some static channels like rdpdr or clipdr there's no marking of packet +length so in theory you can't handle packets that you don't know (you don't know +how many bytes to skip) or that are badly encoded (WTSVirtualChannelRead linearizes +the traffic so you can't know which bytes are part of the current PDU and which +bytes are from the next one). To solve that this patch adds server-side the handling +of a CHANNEL_OPTION_SHOW_PROTOCOL with WTSVirtualChannelOpen, when you set that +option you get CHANNEL_PDU_HEADER before the packet payload, and so you can do the +reassembly of a PDU. This allows to skip a PDU when you don't know the type, and +go to the next one correctly if for instance the padding at the end of the PDU is +incorrect (think of some smartcard commands invalidly padded by FreeRDP). + +The patch also provides a ChannelPduTracker component that can do the reassembly +for you. + +Co-Authored-By: Joan Torres Lopez +--- + channels/rdpdr/server/rdpdr_main.c | 131 +++++++++++++---------------- + 1 file changed, 57 insertions(+), 74 deletions(-) + +diff --git a/channels/rdpdr/server/rdpdr_main.c b/channels/rdpdr/server/rdpdr_main.c +index 132f0746a17a..06cd7a025ee4 100644 +--- a/channels/rdpdr/server/rdpdr_main.c ++++ b/channels/rdpdr/server/rdpdr_main.c +@@ -33,6 +33,7 @@ + + #include + #include "rdpdr_main.h" ++#include + + #define RDPDR_ADD_PRINTER_EVENT 0x00000001 + #define RDPDR_UPDATE_PRINTER_EVENT 0x00000002 +@@ -2126,101 +2127,62 @@ static DWORD WINAPI rdpdr_server_thread(LPVOID arg) + WINPR_ASSERT(context->priv); + + UINT error = 0; ++ wStream* s = nullptr; + RdpdrServerPrivate* priv = context->priv; +- DWORD nCount = 0; ++ ChannelPduTracker* tracker = ChannelPduTracker_new(priv->ChannelHandle); ++ if (!tracker) ++ goto out; ++ + void* buffer = nullptr; +- HANDLE events[8] = WINPR_C_ARRAY_INIT; + HANDLE ChannelEvent = nullptr; + DWORD BytesReturned = 0; +- wStream* s = Stream_New(nullptr, 4096); +- +- if (!s) ++ if (!WTSVirtualChannelQuery(priv->ChannelHandle, WTSVirtualEventHandle, &buffer, ++ &BytesReturned)) + { +- WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); +- error = CHANNEL_RC_NO_MEMORY; ++ WLog_Print(priv->log, WLOG_ERROR, "error retrieving WTSVirtualEventHandle"); + goto out; + } + +- if (WTSVirtualChannelQuery(priv->ChannelHandle, WTSVirtualEventHandle, &buffer, +- &BytesReturned) == TRUE) ++ if (BytesReturned != sizeof(HANDLE)) + { +- if (BytesReturned == sizeof(HANDLE)) +- ChannelEvent = *(HANDLE*)buffer; +- ++ WLog_Print(priv->log, WLOG_ERROR, "invalid size for WTSVirtualEventHandle"); + WTSFreeMemory(buffer); ++ goto out; + } + +- nCount = 0; +- events[nCount++] = ChannelEvent; +- events[nCount++] = priv->StopEvent; ++ ChannelEvent = *(HANDLE*)buffer; ++ WTSFreeMemory(buffer); + + if ((error = rdpdr_server_send_announce_request(context))) + { + WLog_Print(priv->log, WLOG_ERROR, + "rdpdr_server_send_announce_request failed with error %" PRIu32 "!", error); +- goto out_stream; ++ goto out; + } + ++ HANDLE events[2] = { priv->StopEvent, ChannelEvent }; ++ + while (1) + { +- size_t capacity = 0; +- BytesReturned = 0; +- DWORD status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); +- +- if (status == WAIT_FAILED) +- { +- error = GetLastError(); +- WLog_Print(priv->log, WLOG_ERROR, +- "WaitForMultipleObjects failed with error %" PRIu32 "!", error); +- goto out_stream; +- } +- +- status = WaitForSingleObject(priv->StopEvent, 0); +- +- if (status == WAIT_FAILED) +- { +- error = GetLastError(); +- WLog_Print(priv->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!", +- error); +- goto out_stream; +- } +- +- if (status == WAIT_OBJECT_0) +- break; +- +- if (!WTSVirtualChannelRead(priv->ChannelHandle, 0, nullptr, 0, &BytesReturned)) +- { +- WLog_Print(priv->log, WLOG_ERROR, "WTSVirtualChannelRead failed!"); +- error = ERROR_INTERNAL_ERROR; +- break; +- } +- if (!Stream_EnsureRemainingCapacity(s, BytesReturned)) +- { +- WLog_Print(priv->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!"); +- error = ERROR_INTERNAL_ERROR; +- break; +- } +- +- capacity = MIN(Stream_Capacity(s), UINT32_MAX); +- if (!WTSVirtualChannelRead(priv->ChannelHandle, 0, Stream_BufferAs(s, char), +- (ULONG)capacity, &BytesReturned)) +- { +- WLog_Print(priv->log, WLOG_ERROR, "WTSVirtualChannelRead failed!"); +- error = ERROR_INTERNAL_ERROR; +- break; +- } ++ DWORD status = WaitForMultipleObjects(2, events, FALSE, INFINITE); + +- if (BytesReturned >= RDPDR_HEADER_LENGTH) ++ switch (status) + { +- Stream_ResetPosition(s); +- if (!Stream_SetLength(s, BytesReturned)) ++ case WAIT_OBJECT_0: ++ /* StopEvent */ ++ goto out; ++ case WAIT_OBJECT_0 + 1: + { +- error = ERROR_INTERNAL_ERROR; +- goto out_stream; +- } ++ s = ChannelPduTracker_poll(tracker); ++ if (!s) ++ break; ++ ++ if (Stream_GetRemainingLength(s) < RDPDR_HEADER_LENGTH) ++ { ++ error = ERROR_INTERNAL_ERROR; ++ goto out; ++ } + +- while (Stream_GetRemainingLength(s) >= RDPDR_HEADER_LENGTH) +- { + const RDPDR_HEADER header = { + .Component = Stream_Get_UINT16(s), /* Component (2 bytes) */ + .PacketId = Stream_Get_UINT16(s), /* PacketId (2 bytes) */ +@@ -2230,15 +2192,26 @@ static DWORD WINAPI rdpdr_server_thread(LPVOID arg) + { + WLog_Print(priv->log, WLOG_ERROR, + "rdpdr_server_receive_pdu failed with error %" PRIu32 "!", error); +- goto out_stream; ++ goto out; + } ++ Stream_Release(s); ++ s = nullptr; ++ break; + } ++ case WAIT_FAILED: ++ default: ++ error = GetLastError(); ++ WLog_Print(priv->log, WLOG_ERROR, ++ "WaitForMultipleObjects failed with error %" PRIu32 "!", error); ++ goto out; + } + } + +-out_stream: +- Stream_Release(s); + out: ++ ChannelPduTracker_free(tracker); ++ ++ if (s) ++ Stream_Release(s); + + if (error && context->rdpcontext) + setChannelError(context->rdpcontext, error, "rdpdr_server_thread reported an error"); +@@ -2257,9 +2230,19 @@ static UINT rdpdr_server_start(RdpdrServerContext* context) + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + ++ PULONG pSessionId = nullptr; ++ DWORD BytesReturned = 0; ++ ++ if (!WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId, ++ (LPSTR*)&pSessionId, &BytesReturned)) ++ return CHANNEL_RC_BAD_CHANNEL; ++ ++ DWORD SessionId = (DWORD)*pSessionId; ++ WTSFreeMemory(pSessionId); ++ + RdpdrServerPrivate* priv = context->priv; + priv->ChannelHandle = +- WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, RDPDR_SVC_CHANNEL_NAME); ++ WTSVirtualChannelOpenEx(SessionId, RDPDR_SVC_CHANNEL_NAME, CHANNEL_OPTION_SHOW_PROTOCOL); + + if (!priv->ChannelHandle) + { + +From 7af13f0b5d3b784e00348ebaa46284aa48c70d44 Mon Sep 17 00:00:00 2001 +From: David Fort +Date: Mon, 22 Jun 2026 21:37:34 +0200 +Subject: [PATCH 16/31] [channels,rdpdr] track smartcard device in device + announce + +- add haveSmartcardDevice and smartcardDeviceId to RdpdrServerPrivate +- store smartcard device ID on device announce +- clear smartcard device state on device remove +--- + channels/rdpdr/server/rdpdr_main.c | 22 ++++++++++++++-------- + 1 file changed, 14 insertions(+), 8 deletions(-) + +diff --git a/channels/rdpdr/server/rdpdr_main.c b/channels/rdpdr/server/rdpdr_main.c +index 06cd7a025ee4..e90c287c8a4e 100644 +--- a/channels/rdpdr/server/rdpdr_main.c ++++ b/channels/rdpdr/server/rdpdr_main.c +@@ -64,6 +64,8 @@ struct s_rdpdr_server_private + UINT32 SpecialDeviceTypeCap; + UINT32 IoCode1; + UINT32 ExtendedPDU; ++ BOOL haveSmartcardDevice; ++ UINT32 smartcardDeviceId; + }; + + static const char* fileInformation2str(uint8_t val) +@@ -1156,16 +1158,13 @@ static UINT rdpdr_server_receive_device_list_announce_request(RdpdrServerContext + break; + + case RDPDR_DTYP_SMARTCARD: +- if (device.DeviceDataLength != 0) ++ if ((context->supported & RDPDR_DTYP_SMARTCARD) != 0) + { +- WLog_Print(priv->log, WLOG_WARN, +- "[rdpdr] RDPDR_DTYP_SMARTCARD::DeviceDataLength != 0 [%" PRIu32 "]", +- device.DeviceDataLength); +- error = ERROR_INVALID_DATA; +- } +- else if ((context->supported & RDPDR_DTYP_SMARTCARD) != 0) ++ priv->smartcardDeviceId = device.DeviceId; ++ priv->haveSmartcardDevice = TRUE; + error = +- IFCALLRESULT(CHANNEL_RC_OK, context->OnSmartcardCreate, context, &device); ++ IFCALLRESULT(STATUS_SUCCESS, context->OnSmartcardCreate, context, &device); ++ } + break; + + default: +@@ -1254,6 +1253,11 @@ static UINT rdpdr_server_receive_device_list_remove_request(RdpdrServerContext* + break; + + case RDPDR_DTYP_SMARTCARD: ++ if (priv->haveSmartcardDevice) ++ { ++ priv->haveSmartcardDevice = FALSE; ++ priv->smartcardDeviceId = 0; ++ } + if ((context->supported & RDPDR_DTYP_SMARTCARD) != 0) + error = + IFCALLRESULT(CHANNEL_RC_OK, context->OnSmartcardDelete, context, DeviceId); +@@ -3678,6 +3682,8 @@ static RdpdrServerPrivate* rdpdr_server_private_new(void) + priv->ClientId = g_ClientId++; + priv->UserLoggedOnPdu = TRUE; + priv->NextCompletionId = 1; ++ priv->haveSmartcardDevice = FALSE; ++ priv->smartcardDeviceId = 0; + priv->IrpList = ListDictionary_New(TRUE); + + if (!priv->IrpList) + +From 5a61669cd5bd16643545ad46bb7c2edebe4004bb Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Tue, 23 Jun 2026 20:27:59 +0200 +Subject: [PATCH 17/31] [channels,rdpdr] send DEVICE_REPLY in device announce + +Send PAKID_CORE_DEVICE_REPLY for each announced device. + +Co-Authored-By: David Fort +--- + channels/rdpdr/server/rdpdr_main.c | 36 ++++++++++++++++++++++++++++-- + 1 file changed, 34 insertions(+), 2 deletions(-) + +diff --git a/channels/rdpdr/server/rdpdr_main.c b/channels/rdpdr/server/rdpdr_main.c +index e90c287c8a4e..fd11ad86e37f 100644 +--- a/channels/rdpdr/server/rdpdr_main.c ++++ b/channels/rdpdr/server/rdpdr_main.c +@@ -1075,6 +1075,36 @@ static UINT rdpdr_server_send_client_id_confirm(RdpdrServerContext* context) + return rdpdr_seal_send_free_request(context, s); + } + ++/** ++ * Function description ++ * ++ * @return 0 on success, otherwise a Win32 error code ++ */ ++static UINT rdpdr_server_send_device_reply(RdpdrServerContext* context, UINT32 deviceId, ++ UINT32 resultCode) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ ++ RdpdrServerPrivate* priv = context->priv; ++ wStream* s = Stream_New(nullptr, RDPDR_HEADER_LENGTH + 8); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ return CHANNEL_RC_NO_MEMORY; ++ } ++ ++ const RDPDR_HEADER header = { ++ .Component = RDPDR_CTYP_CORE, ++ .PacketId = PAKID_CORE_DEVICE_REPLY, ++ }; ++ Stream_Write_UINT16(s, header.Component); /* Component (2 bytes) */ ++ Stream_Write_UINT16(s, header.PacketId); /* PacketId (2 bytes) */ ++ Stream_Write_UINT32(s, deviceId); /* DeviceId (4 bytes) */ ++ Stream_Write_UINT32(s, resultCode); /* ResultCode (4 bytes) */ ++ return rdpdr_seal_send_free_request(context, s); ++} ++ + /** + * Function description + * +@@ -1119,6 +1149,7 @@ static UINT rdpdr_server_receive_device_list_announce_request(RdpdrServerContext + if (error != CHANNEL_RC_OK) + return error; + ++ error = STATUS_NOT_SUPPORTED; + switch (device.DeviceType) + { + case RDPDR_DTYP_FILESYSTEM: +@@ -1177,10 +1208,11 @@ static UINT rdpdr_server_receive_device_list_announce_request(RdpdrServerContext + break; + } + ++ Stream_Seek(s, device.DeviceDataLength); ++ ++ error = rdpdr_server_send_device_reply(context, device.DeviceId, error); + if (error != CHANNEL_RC_OK) + return error; +- +- Stream_Seek(s, device.DeviceDataLength); + } + + return CHANNEL_RC_OK; + +From c8becfea93ae68bde1dc235e2bbc46a18c443dd2 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Fri, 19 Jun 2026 12:19:03 +0200 +Subject: [PATCH 18/31] [channels,rdpdr] fix IRP list leak on shutdown + +Set fnObjectFree on IrpList so that pending IRPs are freed when the +dictionary is destroyed during shutdown. +--- + channels/rdpdr/server/rdpdr_main.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/channels/rdpdr/server/rdpdr_main.c b/channels/rdpdr/server/rdpdr_main.c +index fd11ad86e37f..5c2df122f1ad 100644 +--- a/channels/rdpdr/server/rdpdr_main.c ++++ b/channels/rdpdr/server/rdpdr_main.c +@@ -3721,6 +3721,8 @@ static RdpdrServerPrivate* rdpdr_server_private_new(void) + if (!priv->IrpList) + goto fail; + ++ ListDictionary_ValueObject(priv->IrpList)->fnObjectFree = (OBJECT_FREE_FN)rdpdr_server_irp_free; ++ + priv->devicelist = HashTable_New(FALSE); + if (!priv->devicelist) + goto fail; + +From 97612e10160c38f1d241025d0fe2af87196ae9f9 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Tue, 23 Jun 2026 21:30:37 +0200 +Subject: [PATCH 19/31] [channels,rdpdr] add and use prepare_irp helper + +- add prepare_irp to centralize IRP creation, enqueue, and device check +- replace inline IRP creation in all drive operations +- add prepare_smartcard_irp wrapper with smartcard device check and IoControlCode +- add IoControlCode field to RDPDR_IRP + +Co-Authored-By: David Fort +--- + channels/rdpdr/server/rdpdr_main.c | 310 +++++++++++------------------ + channels/rdpdr/server/rdpdr_main.h | 1 + + 2 files changed, 122 insertions(+), 189 deletions(-) + +diff --git a/channels/rdpdr/server/rdpdr_main.c b/channels/rdpdr/server/rdpdr_main.c +index 5c2df122f1ad..af42beb7520f 100644 +--- a/channels/rdpdr/server/rdpdr_main.c ++++ b/channels/rdpdr/server/rdpdr_main.c +@@ -2395,6 +2395,66 @@ static UINT rdpdr_server_read_file_directory_information(wLog* log, wStream* s, + return CHANNEL_RC_OK; + } + ++static UINT prepare_irp(RdpdrServerContext* context, UINT32 deviceId, RDPDR_IRP_Callback callback, ++ void* callbackData, RDPDR_IRP** outIrp) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(callbackData); ++ WINPR_ASSERT(outIrp); ++ ++ RdpdrServerPrivate* priv = context->priv; ++ ++ RDPDR_IRP* irp = rdpdr_server_irp_new(); ++ if (!irp) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); ++ return CHANNEL_RC_NO_MEMORY; ++ } ++ ++ irp->CompletionId = priv->NextCompletionId++; ++ irp->Callback = callback; ++ irp->CallbackData = callbackData; ++ irp->DeviceId = deviceId; ++ ++ if (!rdpdr_server_enqueue_irp(context, irp)) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); ++ rdpdr_server_irp_free(irp); ++ return ERROR_INTERNAL_ERROR; ++ } ++ ++ *outIrp = irp; ++ return CHANNEL_RC_OK; ++} ++ ++static UINT prepare_smartcard_irp(RdpdrServerContext* context, UINT32 ioControlCode, ++ RDPDR_IRP_Callback callback, void* callbackData, ++ RDPDR_IRP** outIrp) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(callbackData); ++ ++ const char* cmd = scard_get_ioctl_string(ioControlCode, FALSE); ++ ++ RdpdrServerPrivate* priv = context->priv; ++ if (!priv->haveSmartcardDevice) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "%s - no smartcard device registered", cmd); ++ return ERROR_BAD_DEVICE; ++ } ++ ++ RDPDR_IRP* irp = nullptr; ++ UINT error = prepare_irp(context, priv->smartcardDeviceId, callback, callbackData, &irp); ++ if (error != CHANNEL_RC_OK) ++ return error; ++ ++ irp->IoControlCode = ioControlCode; ++ *outIrp = irp; ++ return CHANNEL_RC_OK; ++} ++ + /** + * Function description + * +@@ -2755,32 +2815,18 @@ static UINT rdpdr_server_drive_create_directory(RdpdrServerContext* context, voi + WINPR_ASSERT(callbackData); + WINPR_ASSERT(path); + +- RdpdrServerPrivate* priv = context->priv; +- RDPDR_IRP* irp = rdpdr_server_irp_new(); ++ RDPDR_IRP* irp = nullptr; ++ UINT ret = prepare_irp(context, deviceId, rdpdr_server_drive_create_directory_callback1, ++ callbackData, &irp); ++ if (ret != CHANNEL_RC_OK) ++ return ret; + +- if (!irp) +- { +- WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); +- return CHANNEL_RC_NO_MEMORY; +- } +- +- irp->CompletionId = priv->NextCompletionId++; +- irp->Callback = rdpdr_server_drive_create_directory_callback1; +- irp->CallbackData = callbackData; +- irp->DeviceId = deviceId; + strncpy(irp->PathName, path, sizeof(irp->PathName) - 1); + rdpdr_server_convert_slashes(irp->PathName, sizeof(irp->PathName)); + +- if (!rdpdr_server_enqueue_irp(context, irp)) +- { +- WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); +- rdpdr_server_irp_free(irp); +- return ERROR_INTERNAL_ERROR; +- } +- + /* Send a request to open the file. */ + return rdpdr_server_send_device_create_request( +- context, deviceId, irp->CompletionId, irp->PathName, FILE_READ_DATA | SYNCHRONIZE, ++ context, irp->DeviceId, irp->CompletionId, irp->PathName, FILE_READ_DATA | SYNCHRONIZE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_CREATE); + } + +@@ -2878,32 +2924,18 @@ static UINT rdpdr_server_drive_delete_directory(RdpdrServerContext* context, voi + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- RdpdrServerPrivate* priv = context->priv; +- RDPDR_IRP* irp = rdpdr_server_irp_new(); +- +- if (!irp) +- { +- WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); +- return CHANNEL_RC_NO_MEMORY; +- } ++ RDPDR_IRP* irp = nullptr; ++ UINT ret = prepare_irp(context, deviceId, rdpdr_server_drive_delete_directory_callback1, ++ callbackData, &irp); ++ if (ret != CHANNEL_RC_OK) ++ return ret; + +- irp->CompletionId = priv->NextCompletionId++; +- irp->Callback = rdpdr_server_drive_delete_directory_callback1; +- irp->CallbackData = callbackData; +- irp->DeviceId = deviceId; + strncpy(irp->PathName, path, sizeof(irp->PathName) - 1); + rdpdr_server_convert_slashes(irp->PathName, sizeof(irp->PathName)); + +- if (!rdpdr_server_enqueue_irp(context, irp)) +- { +- WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); +- rdpdr_server_irp_free(irp); +- return ERROR_INTERNAL_ERROR; +- } +- + /* Send a request to open the file. */ + return rdpdr_server_send_device_create_request( +- context, deviceId, irp->CompletionId, irp->PathName, DELETE | SYNCHRONIZE, ++ context, irp->DeviceId, irp->CompletionId, irp->PathName, DELETE | SYNCHRONIZE, + FILE_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN); + } + +@@ -3049,32 +3081,18 @@ static UINT rdpdr_server_drive_query_directory(RdpdrServerContext* context, void + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- RdpdrServerPrivate* priv = context->priv; +- RDPDR_IRP* irp = rdpdr_server_irp_new(); ++ RDPDR_IRP* irp = nullptr; ++ UINT ret = prepare_irp(context, deviceId, rdpdr_server_drive_query_directory_callback1, ++ callbackData, &irp); ++ if (ret != CHANNEL_RC_OK) ++ return ret; + +- if (!irp) +- { +- WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); +- return CHANNEL_RC_NO_MEMORY; +- } +- +- irp->CompletionId = priv->NextCompletionId++; +- irp->Callback = rdpdr_server_drive_query_directory_callback1; +- irp->CallbackData = callbackData; +- irp->DeviceId = deviceId; + strncpy(irp->PathName, path, sizeof(irp->PathName) - 1); + rdpdr_server_convert_slashes(irp->PathName, sizeof(irp->PathName)); + +- if (!rdpdr_server_enqueue_irp(context, irp)) +- { +- WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); +- rdpdr_server_irp_free(irp); +- return ERROR_INTERNAL_ERROR; +- } +- + /* Send a request to open the directory. */ + return rdpdr_server_send_device_create_request( +- context, deviceId, irp->CompletionId, irp->PathName, FILE_READ_DATA | SYNCHRONIZE, ++ context, irp->DeviceId, irp->CompletionId, irp->PathName, FILE_READ_DATA | SYNCHRONIZE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN); + } + +@@ -3128,31 +3146,17 @@ static UINT rdpdr_server_drive_open_file(RdpdrServerContext* context, void* call + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- RdpdrServerPrivate* priv = context->priv; +- RDPDR_IRP* irp = rdpdr_server_irp_new(); +- +- if (!irp) +- { +- WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); +- return CHANNEL_RC_NO_MEMORY; +- } ++ RDPDR_IRP* irp = nullptr; ++ UINT ret = ++ prepare_irp(context, deviceId, rdpdr_server_drive_open_file_callback, callbackData, &irp); ++ if (ret != CHANNEL_RC_OK) ++ return ret; + +- irp->CompletionId = priv->NextCompletionId++; +- irp->Callback = rdpdr_server_drive_open_file_callback; +- irp->CallbackData = callbackData; +- irp->DeviceId = deviceId; + strncpy(irp->PathName, path, sizeof(irp->PathName) - 1); + rdpdr_server_convert_slashes(irp->PathName, sizeof(irp->PathName)); + +- if (!rdpdr_server_enqueue_irp(context, irp)) +- { +- WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); +- rdpdr_server_irp_free(irp); +- return ERROR_INTERNAL_ERROR; +- } +- + /* Send a request to open the file. */ +- return rdpdr_server_send_device_create_request(context, deviceId, irp->CompletionId, ++ return rdpdr_server_send_device_create_request(context, irp->DeviceId, irp->CompletionId, + irp->PathName, desiredAccess | SYNCHRONIZE, + FILE_SYNCHRONOUS_IO_NONALERT, createDisposition); + } +@@ -3213,32 +3217,17 @@ static UINT rdpdr_server_drive_read_file(RdpdrServerContext* context, void* call + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- RdpdrServerPrivate* priv = context->priv; +- RDPDR_IRP* irp = rdpdr_server_irp_new(); ++ RDPDR_IRP* irp = nullptr; ++ UINT ret = ++ prepare_irp(context, deviceId, rdpdr_server_drive_read_file_callback, callbackData, &irp); ++ if (ret != CHANNEL_RC_OK) ++ return ret; + +- if (!irp) +- { +- WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); +- return CHANNEL_RC_NO_MEMORY; +- } +- +- irp->CompletionId = priv->NextCompletionId++; +- irp->Callback = rdpdr_server_drive_read_file_callback; +- irp->CallbackData = callbackData; +- irp->DeviceId = deviceId; + irp->FileId = fileId; + +- if (!rdpdr_server_enqueue_irp(context, irp)) +- { +- WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); +- rdpdr_server_irp_free(irp); +- return ERROR_INTERNAL_ERROR; +- } +- + /* Send a request to open the directory. */ +- // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): rdpdr_server_enqueue_irp owns irp +- return rdpdr_server_send_device_read_request(context, deviceId, fileId, irp->CompletionId, +- length, offset); ++ return rdpdr_server_send_device_read_request(context, irp->DeviceId, irp->FileId, ++ irp->CompletionId, length, offset); + } + + /************************************************* +@@ -3292,32 +3281,17 @@ static UINT rdpdr_server_drive_write_file(RdpdrServerContext* context, void* cal + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- RdpdrServerPrivate* priv = context->priv; +- RDPDR_IRP* irp = rdpdr_server_irp_new(); ++ RDPDR_IRP* irp = nullptr; ++ UINT ret = ++ prepare_irp(context, deviceId, rdpdr_server_drive_write_file_callback, callbackData, &irp); ++ if (ret != CHANNEL_RC_OK) ++ return ret; + +- if (!irp) +- { +- WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); +- return CHANNEL_RC_NO_MEMORY; +- } +- +- irp->CompletionId = priv->NextCompletionId++; +- irp->Callback = rdpdr_server_drive_write_file_callback; +- irp->CallbackData = callbackData; +- irp->DeviceId = deviceId; + irp->FileId = fileId; + +- if (!rdpdr_server_enqueue_irp(context, irp)) +- { +- WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); +- rdpdr_server_irp_free(irp); +- return ERROR_INTERNAL_ERROR; +- } +- + /* Send a request to open the directory. */ +- // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): rdpdr_server_enqueue_irp owns irp +- return rdpdr_server_send_device_write_request(context, deviceId, fileId, irp->CompletionId, +- buffer, length, offset); ++ return rdpdr_server_send_device_write_request(context, irp->DeviceId, irp->FileId, ++ irp->CompletionId, buffer, length, offset); + } + + /************************************************* +@@ -3369,31 +3343,17 @@ static UINT rdpdr_server_drive_close_file(RdpdrServerContext* context, void* cal + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- RdpdrServerPrivate* priv = context->priv; +- RDPDR_IRP* irp = rdpdr_server_irp_new(); ++ RDPDR_IRP* irp = nullptr; ++ UINT ret = ++ prepare_irp(context, deviceId, rdpdr_server_drive_close_file_callback, callbackData, &irp); ++ if (ret != CHANNEL_RC_OK) ++ return ret; + +- if (!irp) +- { +- WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); +- return CHANNEL_RC_NO_MEMORY; +- } +- +- irp->CompletionId = priv->NextCompletionId++; +- irp->Callback = rdpdr_server_drive_close_file_callback; +- irp->CallbackData = callbackData; +- irp->DeviceId = deviceId; + irp->FileId = fileId; + +- if (!rdpdr_server_enqueue_irp(context, irp)) +- { +- WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); +- rdpdr_server_irp_free(irp); +- return ERROR_INTERNAL_ERROR; +- } +- + /* Send a request to open the directory. */ +- // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): rdpdr_server_enqueue_irp owns irp +- return rdpdr_server_send_device_close_request(context, deviceId, fileId, irp->CompletionId); ++ return rdpdr_server_send_device_close_request(context, irp->DeviceId, irp->FileId, ++ irp->CompletionId); + } + + /************************************************* +@@ -3476,7 +3436,8 @@ static UINT rdpdr_server_drive_delete_file_callback1(RdpdrServerContext* context + } + + /* Send a request to close the file */ +- return rdpdr_server_send_device_close_request(context, deviceId, fileId, irp->CompletionId); ++ return rdpdr_server_send_device_close_request(context, irp->DeviceId, irp->FileId, ++ irp->CompletionId); + } + + /** +@@ -3490,32 +3451,18 @@ static UINT rdpdr_server_drive_delete_file(RdpdrServerContext* context, void* ca + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- RdpdrServerPrivate* priv = context->priv; +- RDPDR_IRP* irp = rdpdr_server_irp_new(); ++ RDPDR_IRP* irp = nullptr; ++ UINT ret = prepare_irp(context, deviceId, rdpdr_server_drive_delete_file_callback1, ++ callbackData, &irp); ++ if (ret != CHANNEL_RC_OK) ++ return ret; + +- if (!irp) +- { +- WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); +- return CHANNEL_RC_NO_MEMORY; +- } +- +- irp->CompletionId = priv->NextCompletionId++; +- irp->Callback = rdpdr_server_drive_delete_file_callback1; +- irp->CallbackData = callbackData; +- irp->DeviceId = deviceId; + strncpy(irp->PathName, path, sizeof(irp->PathName) - 1); + rdpdr_server_convert_slashes(irp->PathName, sizeof(irp->PathName)); + +- if (!rdpdr_server_enqueue_irp(context, irp)) +- { +- WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); +- rdpdr_server_irp_free(irp); +- return ERROR_INTERNAL_ERROR; +- } +- + /* Send a request to open the file. */ + return rdpdr_server_send_device_create_request( +- context, deviceId, irp->CompletionId, irp->PathName, FILE_READ_DATA | SYNCHRONIZE, ++ context, irp->DeviceId, irp->CompletionId, irp->PathName, FILE_READ_DATA | SYNCHRONIZE, + FILE_DELETE_ON_CLOSE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN); + } + +@@ -3588,7 +3535,7 @@ static UINT rdpdr_server_drive_rename_file_callback2(RdpdrServerContext* context + } + + /* Send a request to close the file */ +- return rdpdr_server_send_device_close_request(context, deviceId, irp->FileId, ++ return rdpdr_server_send_device_close_request(context, irp->DeviceId, irp->FileId, + irp->CompletionId); + } + +@@ -3642,7 +3589,7 @@ static UINT rdpdr_server_drive_rename_file_callback1(RdpdrServerContext* context + } + + /* Send a request to rename the file */ +- return rdpdr_server_send_device_file_rename_request(context, deviceId, fileId, ++ return rdpdr_server_send_device_file_rename_request(context, irp->DeviceId, irp->FileId, + irp->CompletionId, irp->ExtraBuffer); + } + +@@ -3658,34 +3605,19 @@ static UINT rdpdr_server_drive_rename_file(RdpdrServerContext* context, void* ca + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + +- RdpdrServerPrivate* priv = context->priv; +- RDPDR_IRP* irp = rdpdr_server_irp_new(); +- +- if (!irp) +- { +- WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!"); +- return CHANNEL_RC_NO_MEMORY; +- } ++ RDPDR_IRP* irp = nullptr; ++ UINT ret = prepare_irp(context, deviceId, rdpdr_server_drive_rename_file_callback1, ++ callbackData, &irp); ++ if (ret != CHANNEL_RC_OK) ++ return ret; + +- irp->CompletionId = priv->NextCompletionId++; +- irp->Callback = rdpdr_server_drive_rename_file_callback1; +- irp->CallbackData = callbackData; +- irp->DeviceId = deviceId; + strncpy(irp->PathName, oldPath, sizeof(irp->PathName) - 1); + strncpy(irp->ExtraBuffer, newPath, sizeof(irp->ExtraBuffer) - 1); + rdpdr_server_convert_slashes(irp->PathName, sizeof(irp->PathName)); + rdpdr_server_convert_slashes(irp->ExtraBuffer, sizeof(irp->ExtraBuffer)); + +- if (!rdpdr_server_enqueue_irp(context, irp)) +- { +- WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!"); +- rdpdr_server_irp_free(irp); +- return ERROR_INTERNAL_ERROR; +- } +- + /* Send a request to open the file. */ +- // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): rdpdr_server_enqueue_irp owns irp +- return rdpdr_server_send_device_create_request(context, deviceId, irp->CompletionId, ++ return rdpdr_server_send_device_create_request(context, irp->DeviceId, irp->CompletionId, + irp->PathName, FILE_READ_DATA | SYNCHRONIZE, + FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN); + } +diff --git a/channels/rdpdr/server/rdpdr_main.h b/channels/rdpdr/server/rdpdr_main.h +index b1d86b148ad7..40327a81313b 100644 +--- a/channels/rdpdr/server/rdpdr_main.h ++++ b/channels/rdpdr/server/rdpdr_main.h +@@ -40,6 +40,7 @@ struct S_RDPDR_IRP + UINT32 CompletionId; + UINT32 DeviceId; + UINT32 FileId; ++ UINT32 IoControlCode; + char PathName[256]; + char ExtraBuffer[256]; + void* CallbackData; + +From 26c63ef8e8591b6fec802c2e04a024f04314f418 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Wed, 24 Jun 2026 18:19:58 +0200 +Subject: [PATCH 20/31] [channels,rdpdr] normalize indentation in rdpdr.h + +Indent all declarations inside extern "C" block one level. +--- + include/freerdp/server/rdpdr.h | 359 +++++++++++++++++---------------- + 1 file changed, 185 insertions(+), 174 deletions(-) + +diff --git a/include/freerdp/server/rdpdr.h b/include/freerdp/server/rdpdr.h +index f2c33a14ba97..411b33b6d6d7 100644 +--- a/include/freerdp/server/rdpdr.h ++++ b/include/freerdp/server/rdpdr.h +@@ -49,186 +49,197 @@ extern "C" + } RDPDR_HEADER; + + #ifndef __MINGW32__ +-typedef struct +-{ +- UINT32 NextEntryOffset; +- UINT32 FileIndex; +- LARGE_INTEGER CreationTime; +- LARGE_INTEGER LastAccessTime; +- LARGE_INTEGER LastWriteTime; +- LARGE_INTEGER ChangeTime; +- LARGE_INTEGER EndOfFile; +- LARGE_INTEGER AllocationSize; +- UINT32 FileAttributes; ++ typedef struct ++ { ++ UINT32 NextEntryOffset; ++ UINT32 FileIndex; ++ LARGE_INTEGER CreationTime; ++ LARGE_INTEGER LastAccessTime; ++ LARGE_INTEGER LastWriteTime; ++ LARGE_INTEGER ChangeTime; ++ LARGE_INTEGER EndOfFile; ++ LARGE_INTEGER AllocationSize; ++ UINT32 FileAttributes; + #if defined(WITH_WCHAR_FILE_DIRECTORY_INFORMATION) +- WCHAR FileName[512]; ++ WCHAR FileName[512]; + #else +- char FileName[512]; ++ char FileName[512]; + #endif +-} FILE_DIRECTORY_INFORMATION; ++ } FILE_DIRECTORY_INFORMATION; + #endif + +-typedef UINT (*psRdpdrStart)(RdpdrServerContext* context); +-typedef UINT (*psRdpdrStop)(RdpdrServerContext* context); +- +-typedef UINT (*psRdpdrCapablityPDU)(RdpdrServerContext* context, +- const RDPDR_CAPABILITY_HEADER* header, size_t size, +- const BYTE* data); +-typedef UINT (*psRdpdrReceivePDU)(RdpdrServerContext* context, const RDPDR_HEADER* header, +- UINT error); +-typedef UINT (*psRdpdrReceiveAnnounceResponse)(RdpdrServerContext* context, UINT16 VersionMajor, +- UINT16 VersionMinor, UINT32 ClientId); +-typedef UINT (*psRdpdrSendServerAnnounce)(RdpdrServerContext* context); +-typedef UINT (*psRdpdrReceiveDeviceAnnounce)(RdpdrServerContext* context, +- const RdpdrDevice* device); +-typedef UINT (*psRdpdrReceiveDeviceRemove)(RdpdrServerContext* context, UINT32 deviceId, +- const RdpdrDevice* device); +-typedef UINT (*psRdpdrReceiveClientNameRequest)(RdpdrServerContext* context, size_t ComputerNameLen, +- const char* name); +- +-typedef UINT (*psRdpdrDriveCreateDirectory)(RdpdrServerContext* context, void* callbackData, +- UINT32 deviceId, const char* path); +-typedef UINT (*psRdpdrDriveDeleteDirectory)(RdpdrServerContext* context, void* callbackData, +- UINT32 deviceId, const char* path); +-typedef UINT (*psRdpdrDriveQueryDirectory)(RdpdrServerContext* context, void* callbackData, ++ typedef UINT (*psRdpdrStart)(RdpdrServerContext* context); ++ typedef UINT (*psRdpdrStop)(RdpdrServerContext* context); ++ ++ typedef UINT (*psRdpdrCapablityPDU)(RdpdrServerContext* context, ++ const RDPDR_CAPABILITY_HEADER* header, size_t size, ++ const BYTE* data); ++ typedef UINT (*psRdpdrReceivePDU)(RdpdrServerContext* context, const RDPDR_HEADER* header, ++ UINT error); ++ typedef UINT (*psRdpdrReceiveAnnounceResponse)(RdpdrServerContext* context, UINT16 VersionMajor, ++ UINT16 VersionMinor, UINT32 ClientId); ++ typedef UINT (*psRdpdrSendServerAnnounce)(RdpdrServerContext* context); ++ typedef UINT (*psRdpdrReceiveDeviceAnnounce)(RdpdrServerContext* context, ++ const RdpdrDevice* device); ++ typedef UINT (*psRdpdrReceiveDeviceRemove)(RdpdrServerContext* context, UINT32 deviceId, ++ const RdpdrDevice* device); ++ typedef UINT (*psRdpdrReceiveClientNameRequest)(RdpdrServerContext* context, ++ size_t ComputerNameLen, const char* name); ++ ++ typedef UINT (*psRdpdrDriveCreateDirectory)(RdpdrServerContext* context, void* callbackData, ++ UINT32 deviceId, const char* path); ++ typedef UINT (*psRdpdrDriveDeleteDirectory)(RdpdrServerContext* context, void* callbackData, ++ UINT32 deviceId, const char* path); ++ typedef UINT (*psRdpdrDriveQueryDirectory)(RdpdrServerContext* context, void* callbackData, ++ UINT32 deviceId, const char* path); ++ typedef UINT (*psRdpdrDriveOpenFile)(RdpdrServerContext* context, void* callbackData, ++ UINT32 deviceId, const char* path, UINT32 desiredAccess, ++ UINT32 createDisposition); ++ typedef UINT (*psRdpdrDriveReadFile)(RdpdrServerContext* context, void* callbackData, ++ UINT32 deviceId, UINT32 fileId, UINT32 length, ++ UINT32 offset); ++ typedef UINT (*psRdpdrDriveWriteFile)(RdpdrServerContext* context, void* callbackData, ++ UINT32 deviceId, UINT32 fileId, const char* buffer, ++ UINT32 length, UINT32 offset); ++ typedef UINT (*psRdpdrDriveCloseFile)(RdpdrServerContext* context, void* callbackData, ++ UINT32 deviceId, UINT32 fileId); ++ typedef UINT (*psRdpdrDriveDeleteFile)(RdpdrServerContext* context, void* callbackData, + UINT32 deviceId, const char* path); +-typedef UINT (*psRdpdrDriveOpenFile)(RdpdrServerContext* context, void* callbackData, +- UINT32 deviceId, const char* path, UINT32 desiredAccess, +- UINT32 createDisposition); +-typedef UINT (*psRdpdrDriveReadFile)(RdpdrServerContext* context, void* callbackData, +- UINT32 deviceId, UINT32 fileId, UINT32 length, UINT32 offset); +-typedef UINT (*psRdpdrDriveWriteFile)(RdpdrServerContext* context, void* callbackData, +- UINT32 deviceId, UINT32 fileId, const char* buffer, +- UINT32 length, UINT32 offset); +-typedef UINT (*psRdpdrDriveCloseFile)(RdpdrServerContext* context, void* callbackData, +- UINT32 deviceId, UINT32 fileId); +-typedef UINT (*psRdpdrDriveDeleteFile)(RdpdrServerContext* context, void* callbackData, +- UINT32 deviceId, const char* path); +-typedef UINT (*psRdpdrDriveRenameFile)(RdpdrServerContext* context, void* callbackData, +- UINT32 deviceId, const char* oldPath, const char* newPath); +- +-typedef void (*psRdpdrOnDriveCreateDirectoryComplete)(RdpdrServerContext* context, +- void* callbackData, UINT32 ioStatus); +-typedef void (*psRdpdrOnDriveDeleteDirectoryComplete)(RdpdrServerContext* context, +- void* callbackData, UINT32 ioStatus); +-typedef void (*psRdpdrOnDriveQueryDirectoryComplete)(RdpdrServerContext* context, +- void* callbackData, UINT32 ioStatus, +- FILE_DIRECTORY_INFORMATION* fdi); +-typedef void (*psRdpdrOnDriveOpenFileComplete)(RdpdrServerContext* context, void* callbackData, +- UINT32 ioStatus, UINT32 deviceId, UINT32 fileId); +-typedef void (*psRdpdrOnDriveReadFileComplete)(RdpdrServerContext* context, void* callbackData, +- UINT32 ioStatus, const char* buffer, UINT32 length); +-typedef void (*psRdpdrOnDriveWriteFileComplete)(RdpdrServerContext* context, void* callbackData, +- UINT32 ioStatus, UINT32 bytesWritten); +-typedef void (*psRdpdrOnDriveCloseFileComplete)(RdpdrServerContext* context, void* callbackData, +- UINT32 ioStatus); +-typedef void (*psRdpdrOnDriveDeleteFileComplete)(RdpdrServerContext* context, void* callbackData, +- UINT32 ioStatus); +-typedef void (*psRdpdrOnDriveRenameFileComplete)(RdpdrServerContext* context, void* callbackData, +- UINT32 ioStatus); +- +-typedef UINT (*psRdpdrOnDeviceCreate)(RdpdrServerContext* context, const RdpdrDevice* device); +-typedef UINT (*psRdpdrOnDeviceDelete)(RdpdrServerContext* context, UINT32 deviceId); +- +-struct s_rdpdr_server_context +-{ +- HANDLE vcm; +- +- WINPR_ATTR_NODISCARD psRdpdrStart Start; +- psRdpdrStop Stop; +- +- RdpdrServerPrivate* priv; +- +- /* Server self-defined pointer. */ +- void* data; +- +- /**< Server supported redirections. +- * initially used to determine which redirections are supported by the +- * server in the server capability, later on updated with what the client +- * actually wants to have supported. +- * +- * Use the \b RDPDR_DTYP_* defines as a mask to check. +- */ +- UINT16 supported; +- +- /*** RDPDR message intercept callbacks */ +- WINPR_ATTR_NODISCARD psRdpdrCapablityPDU +- ReceiveCaps; /**< Called for each received capability */ +- WINPR_ATTR_NODISCARD psRdpdrCapablityPDU SendCaps; /**< Called for each capability to be sent */ +- WINPR_ATTR_NODISCARD psRdpdrReceivePDU +- ReceivePDU; /**< Called after a RDPDR pdu was received and parsed */ +- WINPR_ATTR_NODISCARD psRdpdrSendServerAnnounce +- SendServerAnnounce; /**< Called before the server sends the announce message */ +- WINPR_ATTR_NODISCARD psRdpdrReceiveAnnounceResponse +- ReceiveAnnounceResponse; /**< Called after the client announce response is received */ +- WINPR_ATTR_NODISCARD psRdpdrReceiveClientNameRequest +- ReceiveClientNameRequest; /**< Called after a client name request is received */ +- WINPR_ATTR_NODISCARD psRdpdrReceiveDeviceAnnounce +- ReceiveDeviceAnnounce; /** < Called after a new device request was received but before the +- device is added */ +- WINPR_ATTR_NODISCARD psRdpdrReceiveDeviceRemove +- ReceiveDeviceRemove; /**< Called after a new device request was +- received, but before it is removed */ +- +- /*** Drive APIs called by the server. ***/ +- WINPR_ATTR_NODISCARD psRdpdrDriveCreateDirectory DriveCreateDirectory; +- WINPR_ATTR_NODISCARD psRdpdrDriveDeleteDirectory DriveDeleteDirectory; +- WINPR_ATTR_NODISCARD psRdpdrDriveQueryDirectory DriveQueryDirectory; +- WINPR_ATTR_NODISCARD psRdpdrDriveOpenFile DriveOpenFile; +- WINPR_ATTR_NODISCARD psRdpdrDriveReadFile DriveReadFile; +- WINPR_ATTR_NODISCARD psRdpdrDriveWriteFile DriveWriteFile; +- WINPR_ATTR_NODISCARD psRdpdrDriveCloseFile DriveCloseFile; +- WINPR_ATTR_NODISCARD psRdpdrDriveDeleteFile DriveDeleteFile; +- WINPR_ATTR_NODISCARD psRdpdrDriveRenameFile DriveRenameFile; +- +- /*** Drive callbacks registered by the server. ***/ +- WINPR_ATTR_NODISCARD psRdpdrOnDeviceCreate OnDriveCreate; /**< Called for devices of type \b +- RDPDR_DTYP_FILESYSTEM after \b ReceiveDeviceAnnounce */ +- WINPR_ATTR_NODISCARD psRdpdrOnDeviceDelete OnDriveDelete; /**< Called for devices of type \b +- RDPDR_DTYP_FILESYSTEM after \b ReceiveDeviceRemove */ +- psRdpdrOnDriveCreateDirectoryComplete OnDriveCreateDirectoryComplete; +- psRdpdrOnDriveDeleteDirectoryComplete OnDriveDeleteDirectoryComplete; +- psRdpdrOnDriveQueryDirectoryComplete OnDriveQueryDirectoryComplete; +- psRdpdrOnDriveOpenFileComplete OnDriveOpenFileComplete; +- psRdpdrOnDriveReadFileComplete OnDriveReadFileComplete; +- psRdpdrOnDriveWriteFileComplete OnDriveWriteFileComplete; +- psRdpdrOnDriveCloseFileComplete OnDriveCloseFileComplete; +- psRdpdrOnDriveDeleteFileComplete OnDriveDeleteFileComplete; +- psRdpdrOnDriveRenameFileComplete OnDriveRenameFileComplete; +- +- /*** Serial Port callbacks registered by the server. ***/ +- WINPR_ATTR_NODISCARD psRdpdrOnDeviceCreate OnSerialPortCreate; /**< Called for devices of type +- \b RDPDR_DTYP_SERIAL after \b ReceiveDeviceAnnounce */ +- WINPR_ATTR_NODISCARD psRdpdrOnDeviceDelete OnSerialPortDelete; /**< Called for devices of type +- \b RDPDR_DTYP_SERIAL after \b ReceiveDeviceRemove */ +- +- /*** Parallel Port callbacks registered by the server. ***/ +- WINPR_ATTR_NODISCARD psRdpdrOnDeviceCreate OnParallelPortCreate; /**< Called for devices of type +- \b RDPDR_DTYP_PARALLEL after \b ReceiveDeviceAnnounce */ +- WINPR_ATTR_NODISCARD psRdpdrOnDeviceDelete OnParallelPortDelete; /**< Called for devices of type +- \b RDPDR_DTYP_PARALLEL after \b ReceiveDeviceRemove */ +- +- /*** Printer callbacks registered by the server. ***/ +- WINPR_ATTR_NODISCARD psRdpdrOnDeviceCreate OnPrinterCreate; /**< Called for devices of type +- RDPDR_DTYP_PRINT after \b ReceiveDeviceAnnounce */ +- WINPR_ATTR_NODISCARD psRdpdrOnDeviceDelete OnPrinterDelete; /**< Called for devices of type +- RDPDR_DTYP_PRINT after \b ReceiveDeviceRemove */ +- +- /*** Smartcard callbacks registered by the server. ***/ +- WINPR_ATTR_NODISCARD psRdpdrOnDeviceCreate +- OnSmartcardCreate; /**< Called for devices of type RDPDR_DTYP_SMARTCARD +- after \b ReceiveDeviceAnnounce */ +- WINPR_ATTR_NODISCARD psRdpdrOnDeviceDelete OnSmartcardDelete; /**< Called for devices of type +- RDPDR_DTYP_SMARTCARD after \b ReceiveDeviceRemove */ +- +- rdpContext* rdpcontext; +-}; +- +-FREERDP_API void rdpdr_server_context_free(RdpdrServerContext* context); +- +-WINPR_ATTR_MALLOC(rdpdr_server_context_free, 1) +-WINPR_ATTR_NODISCARD +-FREERDP_API RdpdrServerContext* rdpdr_server_context_new(HANDLE vcm); ++ typedef UINT (*psRdpdrDriveRenameFile)(RdpdrServerContext* context, void* callbackData, ++ UINT32 deviceId, const char* oldPath, ++ const char* newPath); ++ ++ typedef void (*psRdpdrOnDriveCreateDirectoryComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus); ++ typedef void (*psRdpdrOnDriveDeleteDirectoryComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus); ++ typedef void (*psRdpdrOnDriveQueryDirectoryComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus, ++ FILE_DIRECTORY_INFORMATION* fdi); ++ typedef void (*psRdpdrOnDriveOpenFileComplete)(RdpdrServerContext* context, void* callbackData, ++ UINT32 ioStatus, UINT32 deviceId, UINT32 fileId); ++ typedef void (*psRdpdrOnDriveReadFileComplete)(RdpdrServerContext* context, void* callbackData, ++ UINT32 ioStatus, const char* buffer, ++ UINT32 length); ++ typedef void (*psRdpdrOnDriveWriteFileComplete)(RdpdrServerContext* context, void* callbackData, ++ UINT32 ioStatus, UINT32 bytesWritten); ++ typedef void (*psRdpdrOnDriveCloseFileComplete)(RdpdrServerContext* context, void* callbackData, ++ UINT32 ioStatus); ++ typedef void (*psRdpdrOnDriveDeleteFileComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus); ++ typedef void (*psRdpdrOnDriveRenameFileComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus); ++ ++ typedef UINT (*psRdpdrOnDeviceCreate)(RdpdrServerContext* context, const RdpdrDevice* device); ++ typedef UINT (*psRdpdrOnDeviceDelete)(RdpdrServerContext* context, UINT32 deviceId); ++ ++ struct s_rdpdr_server_context ++ { ++ HANDLE vcm; ++ ++ WINPR_ATTR_NODISCARD psRdpdrStart Start; ++ psRdpdrStop Stop; ++ ++ RdpdrServerPrivate* priv; ++ ++ /* Server self-defined pointer. */ ++ void* data; ++ ++ /**< Server supported redirections. ++ * initially used to determine which redirections are supported by the ++ * server in the server capability, later on updated with what the client ++ * actually wants to have supported. ++ * ++ * Use the \b RDPDR_DTYP_* defines as a mask to check. ++ */ ++ UINT16 supported; ++ ++ /*** RDPDR message intercept callbacks */ ++ WINPR_ATTR_NODISCARD psRdpdrCapablityPDU ++ ReceiveCaps; /**< Called for each received capability */ ++ WINPR_ATTR_NODISCARD psRdpdrCapablityPDU ++ SendCaps; /**< Called for each capability to be sent */ ++ WINPR_ATTR_NODISCARD psRdpdrReceivePDU ++ ReceivePDU; /**< Called after a RDPDR pdu was received and parsed */ ++ WINPR_ATTR_NODISCARD psRdpdrSendServerAnnounce ++ SendServerAnnounce; /**< Called before the server sends the announce message */ ++ WINPR_ATTR_NODISCARD psRdpdrReceiveAnnounceResponse ++ ReceiveAnnounceResponse; /**< Called after the client announce response is received */ ++ WINPR_ATTR_NODISCARD psRdpdrReceiveClientNameRequest ++ ReceiveClientNameRequest; /**< Called after a client name request is received */ ++ WINPR_ATTR_NODISCARD psRdpdrReceiveDeviceAnnounce ++ ReceiveDeviceAnnounce; /** < Called after a new device request was received but before ++ the device is added */ ++ WINPR_ATTR_NODISCARD psRdpdrReceiveDeviceRemove ++ ReceiveDeviceRemove; /**< Called after a new device request was ++ received, but before it is removed */ ++ ++ /*** Drive APIs called by the server. ***/ ++ WINPR_ATTR_NODISCARD psRdpdrDriveCreateDirectory DriveCreateDirectory; ++ WINPR_ATTR_NODISCARD psRdpdrDriveDeleteDirectory DriveDeleteDirectory; ++ WINPR_ATTR_NODISCARD psRdpdrDriveQueryDirectory DriveQueryDirectory; ++ WINPR_ATTR_NODISCARD psRdpdrDriveOpenFile DriveOpenFile; ++ WINPR_ATTR_NODISCARD psRdpdrDriveReadFile DriveReadFile; ++ WINPR_ATTR_NODISCARD psRdpdrDriveWriteFile DriveWriteFile; ++ WINPR_ATTR_NODISCARD psRdpdrDriveCloseFile DriveCloseFile; ++ WINPR_ATTR_NODISCARD psRdpdrDriveDeleteFile DriveDeleteFile; ++ WINPR_ATTR_NODISCARD psRdpdrDriveRenameFile DriveRenameFile; ++ ++ /*** Drive callbacks registered by the server. ***/ ++ WINPR_ATTR_NODISCARD psRdpdrOnDeviceCreate ++ OnDriveCreate; /**< Called for devices of type \b ++ RDPDR_DTYP_FILESYSTEM after \b ReceiveDeviceAnnounce */ ++ WINPR_ATTR_NODISCARD psRdpdrOnDeviceDelete ++ OnDriveDelete; /**< Called for devices of type \b ++ RDPDR_DTYP_FILESYSTEM after \b ReceiveDeviceRemove */ ++ psRdpdrOnDriveCreateDirectoryComplete OnDriveCreateDirectoryComplete; ++ psRdpdrOnDriveDeleteDirectoryComplete OnDriveDeleteDirectoryComplete; ++ psRdpdrOnDriveQueryDirectoryComplete OnDriveQueryDirectoryComplete; ++ psRdpdrOnDriveOpenFileComplete OnDriveOpenFileComplete; ++ psRdpdrOnDriveReadFileComplete OnDriveReadFileComplete; ++ psRdpdrOnDriveWriteFileComplete OnDriveWriteFileComplete; ++ psRdpdrOnDriveCloseFileComplete OnDriveCloseFileComplete; ++ psRdpdrOnDriveDeleteFileComplete OnDriveDeleteFileComplete; ++ psRdpdrOnDriveRenameFileComplete OnDriveRenameFileComplete; ++ ++ /*** Serial Port callbacks registered by the server. ***/ ++ WINPR_ATTR_NODISCARD psRdpdrOnDeviceCreate ++ OnSerialPortCreate; /**< Called for devices of type ++ \b RDPDR_DTYP_SERIAL after \b ReceiveDeviceAnnounce */ ++ WINPR_ATTR_NODISCARD psRdpdrOnDeviceDelete ++ OnSerialPortDelete; /**< Called for devices of type ++ \b RDPDR_DTYP_SERIAL after \b ReceiveDeviceRemove */ ++ ++ /*** Parallel Port callbacks registered by the server. ***/ ++ WINPR_ATTR_NODISCARD psRdpdrOnDeviceCreate ++ OnParallelPortCreate; /**< Called for devices of type ++ \b RDPDR_DTYP_PARALLEL after \b ReceiveDeviceAnnounce */ ++ WINPR_ATTR_NODISCARD psRdpdrOnDeviceDelete ++ OnParallelPortDelete; /**< Called for devices of type ++ \b RDPDR_DTYP_PARALLEL after \b ReceiveDeviceRemove */ ++ ++ /*** Printer callbacks registered by the server. ***/ ++ WINPR_ATTR_NODISCARD psRdpdrOnDeviceCreate OnPrinterCreate; /**< Called for devices of type ++ RDPDR_DTYP_PRINT after \b ReceiveDeviceAnnounce */ ++ WINPR_ATTR_NODISCARD psRdpdrOnDeviceDelete OnPrinterDelete; /**< Called for devices of type ++ RDPDR_DTYP_PRINT after \b ReceiveDeviceRemove */ ++ ++ /*** Smartcard callbacks registered by the server. ***/ ++ WINPR_ATTR_NODISCARD psRdpdrOnDeviceCreate ++ OnSmartcardCreate; /**< Called for devices of type RDPDR_DTYP_SMARTCARD ++ after \b ReceiveDeviceAnnounce */ ++ WINPR_ATTR_NODISCARD psRdpdrOnDeviceDelete ++ OnSmartcardDelete; /**< Called for devices of type ++ RDPDR_DTYP_SMARTCARD after \b ReceiveDeviceRemove */ ++ ++ rdpContext* rdpcontext; ++ }; ++ ++ FREERDP_API void rdpdr_server_context_free(RdpdrServerContext* context); ++ ++ WINPR_ATTR_MALLOC(rdpdr_server_context_free, 1) ++ WINPR_ATTR_NODISCARD ++ FREERDP_API RdpdrServerContext* rdpdr_server_context_new(HANDLE vcm); + + #ifdef __cplusplus + } + +From 8064bf9ed77211dea30c1114b5286c5303e1af78 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Mon, 22 Jun 2026 13:58:54 +0200 +Subject: [PATCH 21/31] [channels,rdpdr] add smartcard server operations + +Add smartcard IOCTL operations to the RDPDR server. Each operation +sends an IRP_MJ_DEVICE_CONTROL request to the client and parses +the response via a completion callback. + +Add rdpdr_server_send_device_control_request for sending +IRP_MJ_DEVICE_CONTROL requests, following the existing drive pattern. + +Add request and completion function pointers to the public +RdpdrServerContext API for all smartcard operations. + +Co-authored-by: David Fort +--- + channels/rdpdr/server/rdpdr_main.c | 1979 ++++++++++++++++- + include/freerdp/server/rdpdr.h | 220 ++ + libfreerdp/utils/test/CMakeLists.txt | 4 +- + .../utils/test/TestSmartcardOperations.c | 1445 ++++++++++++ + 4 files changed, 3614 insertions(+), 34 deletions(-) + create mode 100644 libfreerdp/utils/test/TestSmartcardOperations.c + +diff --git a/channels/rdpdr/server/rdpdr_main.c b/channels/rdpdr/server/rdpdr_main.c +index af42beb7520f..7b9c1d33ff10 100644 +--- a/channels/rdpdr/server/rdpdr_main.c ++++ b/channels/rdpdr/server/rdpdr_main.c +@@ -32,6 +32,8 @@ + #include + + #include ++#include ++#include + #include "rdpdr_main.h" + #include + +@@ -43,6 +45,9 @@ + #define RDPDR_HEADER_LENGTH 4 + #define RDPDR_CAPABILITY_HEADER_LENGTH 8 + ++/* [MS-RDPESC] 3.2.5.1: the output buffer length SHOULD be set to 2048 */ ++#define SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH 2048 ++ + struct s_rdpdr_server_private + { + HANDLE Thread; +@@ -2706,6 +2711,55 @@ static UINT rdpdr_server_send_device_file_rename_request(RdpdrServerContext* con + return rdpdr_seal_send_free_request(context, s); + } + ++static UINT rdpdr_server_send_device_control_request(RdpdrServerContext* context, UINT32 deviceId, ++ UINT32 completionId, UINT32 ioControlCode, ++ UINT32 outputBufferLength, ++ const void* inputBuffer, ++ size_t inputBufferLength) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(inputBuffer || (inputBufferLength == 0)); ++ ++ if (inputBufferLength > UINT32_MAX) ++ { ++ WLog_Print(context->priv->log, WLOG_ERROR, ++ "inputBufferLength %" PRIuz " exceeds UINT32_MAX", inputBufferLength); ++ return ERROR_INVALID_PARAMETER; ++ } ++ ++ RdpdrServerPrivate* priv = context->priv; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, ++ "RdpdrServerSendDeviceControlRequest: deviceId=%" PRIu32 "," ++ " ioControlCode=0x%" PRIx32 ", inputBufferLength=%" PRIuz, ++ deviceId, ioControlCode, inputBufferLength); ++ ++ if (inputBufferLength > 0) ++ winpr_HexLogDump(priv->log, WLOG_DEBUG, inputBuffer, inputBufferLength); ++ ++ wStream* s = ++ Stream_New(nullptr, RDPDR_HEADER_LENGTH + RDPDR_DEVICE_IO_REQUEST_LENGTH + ++ RDPDR_DEVICE_IO_CONTROL_REQ_HDR_LENGTH + inputBufferLength); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ return CHANNEL_RC_NO_MEMORY; ++ } ++ ++ UINT32 inputLength = WINPR_ASSERTING_INT_CAST(UINT32, inputBufferLength); ++ rdpdr_server_write_device_iorequest(s, deviceId, 0, completionId, IRP_MJ_DEVICE_CONTROL, 0); ++ Stream_Write_UINT32(s, outputBufferLength); /* OutputBufferLength (4 bytes) */ ++ Stream_Write_UINT32(s, inputLength); /* InputBufferLength (4 bytes) */ ++ Stream_Write_UINT32(s, ioControlCode); /* IoControlCode (4 bytes) */ ++ Stream_Zero(s, 20); /* Padding (20 bytes) */ ++ ++ if (inputBufferLength > 0) ++ Stream_Write(s, inputBuffer, inputBufferLength); ++ ++ return rdpdr_seal_send_free_request(context, s); ++} ++ + static void rdpdr_server_convert_slashes(char* path, int size) + { + WINPR_ASSERT(path || (size <= 0)); +@@ -3682,46 +3736,1905 @@ static RdpdrServerPrivate* rdpdr_server_private_new(void) + return nullptr; + } + +-RdpdrServerContext* rdpdr_server_context_new(HANDLE vcm) ++static UINT rdpdr_server_smartcard_establish_context_callback(RdpdrServerContext* context, ++ wStream* s, RDPDR_IRP* irp, ++ UINT32 deviceId, UINT32 completionId, ++ UINT32 ioStatus) + { +- RdpdrServerContext* context = (RdpdrServerContext*)calloc(1, sizeof(RdpdrServerContext)); ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(irp); + +- if (!context) +- goto fail; ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; + +- context->vcm = vcm; +- context->Start = rdpdr_server_start; +- context->Stop = rdpdr_server_stop; +- context->DriveCreateDirectory = rdpdr_server_drive_create_directory; +- context->DriveDeleteDirectory = rdpdr_server_drive_delete_directory; +- context->DriveQueryDirectory = rdpdr_server_drive_query_directory; +- context->DriveOpenFile = rdpdr_server_drive_open_file; +- context->DriveReadFile = rdpdr_server_drive_read_file; +- context->DriveWriteFile = rdpdr_server_drive_write_file; +- context->DriveCloseFile = rdpdr_server_drive_close_file; +- context->DriveDeleteFile = rdpdr_server_drive_delete_file; +- context->DriveRenameFile = rdpdr_server_drive_rename_file; +- context->priv = rdpdr_server_private_new(); +- if (!context->priv) +- goto fail; ++ WLog_Print(priv->log, WLOG_DEBUG, ++ "RdpdrServerSmartcardEstablishContextCallback: deviceId=%" PRIu32 ++ ", completionId=%" PRIu32 ", ioStatus=0x%" PRIx32 "", ++ deviceId, completionId, ioStatus); + +- /* By default announce everything, the server application can deactivate that later on */ +- context->supported = UINT16_MAX; ++ if (ioStatus != STATUS_SUCCESS) ++ { ++ if (context->OnSmartcardEstablishContextComplete) ++ context->OnSmartcardEstablishContextComplete(context, irp->CallbackData, ioStatus, ++ SCARD_F_INTERNAL_ERROR, nullptr); ++ goto out; ++ } + +- return context; +-fail: +- WINPR_PRAGMA_DIAG_PUSH +- WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC +- rdpdr_server_context_free(context); +- WINPR_PRAGMA_DIAG_POP +- return nullptr; ++ result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INVALID_DATA; ++ goto out; ++ } ++ ++ if (context->OnSmartcardEstablishContextComplete) ++ context->OnSmartcardEstablishContextComplete(context, irp->CallbackData, ioStatus, ++ op.returnCode, &op.ret.establishContext); ++ ++out: ++ smartcard_operation_free(&op, FALSE); ++ rdpdr_server_irp_free(irp); ++ return result; + } + +-void rdpdr_server_context_free(RdpdrServerContext* context) ++static UINT rdpdr_server_smartcard_establish_context(RdpdrServerContext* context, ++ void* callbackData, UINT32 dwScope, ++ UINT32* completionId) + { +- if (!context) +- return; ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(completionId); + +- rdpdr_server_private_free(context->priv); +- free(context); ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = nullptr; ++ wStream* s = nullptr; ++ ++ switch (dwScope) ++ { ++ case SCARD_SCOPE_USER: ++ case SCARD_SCOPE_TERMINAL: ++ case SCARD_SCOPE_SYSTEM: ++ break; ++ default: ++ WLog_Print(priv->log, WLOG_ERROR, "invalid dwScope=0x%08" PRIx32, dwScope); ++ return ERROR_INVALID_PARAMETER; ++ } ++ ++ result = prepare_smartcard_irp(context, SCARD_IOCTL_ESTABLISHCONTEXT, ++ rdpdr_server_smartcard_establish_context_callback, callbackData, ++ &irp); ++ if (result != CHANNEL_RC_OK) ++ return result; ++ ++ *completionId = irp->CompletionId; ++ ++ const SMARTCARD_OPERATION op = { ++ .ioControlCode = SCARD_IOCTL_ESTABLISHCONTEXT, ++ .call.establishContext.dwScope = dwScope, ++ }; ++ ++ s = Stream_New(nullptr, 64); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ result = CHANNEL_RC_NO_MEMORY; ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_encode_request(s, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INTERNAL_ERROR; ++ goto out; ++ } ++ ++ result = rdpdr_server_send_device_control_request( ++ context, irp->DeviceId, irp->CompletionId, irp->IoControlCode, ++ SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); ++ ++out: ++ if (result != CHANNEL_RC_OK && irp) ++ rdpdr_server_discard_request(context, irp->CompletionId); ++ if (s) ++ Stream_Release(s); ++ return result; ++} ++ ++/* ReleaseContext */ ++static UINT rdpdr_server_smartcard_release_context_callback(RdpdrServerContext* context, wStream* s, ++ RDPDR_IRP* irp, UINT32 deviceId, ++ UINT32 completionId, UINT32 ioStatus) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(irp); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, ++ "SmartcardReleaseContextCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 ++ ", ioStatus=0x%" PRIx32, ++ deviceId, completionId, ioStatus); ++ ++ if (ioStatus != STATUS_SUCCESS) ++ { ++ if (context->OnSmartcardReleaseContextComplete) ++ context->OnSmartcardReleaseContextComplete(context, irp->CallbackData, ioStatus, ++ SCARD_F_INTERNAL_ERROR); ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INVALID_DATA; ++ goto out; ++ } ++ ++ if (context->OnSmartcardReleaseContextComplete) ++ context->OnSmartcardReleaseContextComplete(context, irp->CallbackData, ioStatus, ++ op.returnCode); ++ ++out: ++ smartcard_operation_free(&op, FALSE); ++ rdpdr_server_irp_free(irp); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_release_context(RdpdrServerContext* context, void* callbackData, ++ const REDIR_SCARDCONTEXT* hContext, ++ UINT32* completionId) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(hContext); ++ WINPR_ASSERT(completionId); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = nullptr; ++ wStream* s = nullptr; ++ ++ result = ++ prepare_smartcard_irp(context, SCARD_IOCTL_RELEASECONTEXT, ++ rdpdr_server_smartcard_release_context_callback, callbackData, &irp); ++ if (result != CHANNEL_RC_OK) ++ return result; ++ ++ *completionId = irp->CompletionId; ++ ++ const SMARTCARD_OPERATION op = { ++ .ioControlCode = SCARD_IOCTL_RELEASECONTEXT, ++ .call.context.handles.hContext = *hContext, ++ }; ++ ++ s = Stream_New(nullptr, 64); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ result = CHANNEL_RC_NO_MEMORY; ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_encode_request(s, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INTERNAL_ERROR; ++ goto out; ++ } ++ ++ result = rdpdr_server_send_device_control_request( ++ context, irp->DeviceId, irp->CompletionId, irp->IoControlCode, ++ SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); ++ ++out: ++ if (result != CHANNEL_RC_OK && irp) ++ rdpdr_server_discard_request(context, irp->CompletionId); ++ if (s) ++ Stream_Release(s); ++ return result; ++} ++ ++/* IsValidContext */ ++static UINT rdpdr_server_smartcard_is_valid_context_callback(RdpdrServerContext* context, ++ wStream* s, RDPDR_IRP* irp, ++ UINT32 deviceId, UINT32 completionId, ++ UINT32 ioStatus) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(irp); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, ++ "SmartcardIsValidContextCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 ++ ", ioStatus=0x%" PRIx32, ++ deviceId, completionId, ioStatus); ++ ++ if (ioStatus != STATUS_SUCCESS) ++ { ++ if (context->OnSmartcardIsValidContextComplete) ++ context->OnSmartcardIsValidContextComplete(context, irp->CallbackData, ioStatus, ++ SCARD_F_INTERNAL_ERROR); ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INVALID_DATA; ++ goto out; ++ } ++ ++ if (context->OnSmartcardIsValidContextComplete) ++ context->OnSmartcardIsValidContextComplete(context, irp->CallbackData, ioStatus, ++ op.returnCode); ++ ++out: ++ smartcard_operation_free(&op, FALSE); ++ rdpdr_server_irp_free(irp); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_is_valid_context(RdpdrServerContext* context, void* callbackData, ++ const REDIR_SCARDCONTEXT* hContext, ++ UINT32* completionId) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(hContext); ++ WINPR_ASSERT(completionId); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = nullptr; ++ wStream* s = nullptr; ++ ++ result = ++ prepare_smartcard_irp(context, SCARD_IOCTL_ISVALIDCONTEXT, ++ rdpdr_server_smartcard_is_valid_context_callback, callbackData, &irp); ++ if (result != CHANNEL_RC_OK) ++ return result; ++ ++ *completionId = irp->CompletionId; ++ ++ const SMARTCARD_OPERATION op = { ++ .ioControlCode = SCARD_IOCTL_ISVALIDCONTEXT, ++ .call.context.handles.hContext = *hContext, ++ }; ++ ++ s = Stream_New(nullptr, 64); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ result = CHANNEL_RC_NO_MEMORY; ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_encode_request(s, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INTERNAL_ERROR; ++ goto out; ++ } ++ ++ result = rdpdr_server_send_device_control_request( ++ context, irp->DeviceId, irp->CompletionId, irp->IoControlCode, ++ SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); ++ ++out: ++ if (result != CHANNEL_RC_OK && irp) ++ rdpdr_server_discard_request(context, irp->CompletionId); ++ if (s) ++ Stream_Release(s); ++ return result; ++} ++ ++/* ListReaderGroups */ ++static UINT rdpdr_server_smartcard_list_reader_groups_callback(RdpdrServerContext* context, ++ wStream* s, RDPDR_IRP* irp, ++ UINT32 deviceId, UINT32 completionId, ++ UINT32 ioStatus) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(irp); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, ++ "SmartcardListReaderGroupsCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 ++ ", ioStatus=0x%" PRIx32, ++ deviceId, completionId, ioStatus); ++ ++ if (ioStatus != STATUS_SUCCESS) ++ { ++ if (context->OnSmartcardListReaderGroupsComplete) ++ context->OnSmartcardListReaderGroupsComplete(context, irp->CallbackData, ioStatus, ++ SCARD_F_INTERNAL_ERROR, nullptr); ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INVALID_DATA; ++ goto out; ++ } ++ ++ if (context->OnSmartcardListReaderGroupsComplete) ++ context->OnSmartcardListReaderGroupsComplete(context, irp->CallbackData, ioStatus, ++ op.returnCode, &op.ret.listReaders); ++ ++out: ++ smartcard_operation_free(&op, FALSE); ++ rdpdr_server_irp_free(irp); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_list_reader_groups(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioControlCode, ++ const ListReaderGroups_Call* call, ++ UINT32* completionId) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(call); ++ WINPR_ASSERT(completionId); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = nullptr; ++ wStream* s = nullptr; ++ ++ result = prepare_smartcard_irp(context, ioControlCode, ++ rdpdr_server_smartcard_list_reader_groups_callback, callbackData, ++ &irp); ++ if (result != CHANNEL_RC_OK) ++ return result; ++ ++ *completionId = irp->CompletionId; ++ ++ /* clang-format off */ ++ const SMARTCARD_OPERATION op = { ++ .ioControlCode = ioControlCode, ++ .call.listReaderGroups = { ++ .handles.hContext = call->handles.hContext, ++ .fmszGroupsIsNULL = call->fmszGroupsIsNULL, ++ .cchGroups = call->cchGroups, ++ } ++ }; ++ /* clang-format on */ ++ ++ s = Stream_New(nullptr, 32); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ result = CHANNEL_RC_NO_MEMORY; ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_encode_request(s, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INTERNAL_ERROR; ++ goto out; ++ } ++ ++ result = rdpdr_server_send_device_control_request( ++ context, irp->DeviceId, irp->CompletionId, irp->IoControlCode, ++ SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); ++ ++out: ++ if (result != CHANNEL_RC_OK && irp) ++ rdpdr_server_discard_request(context, irp->CompletionId); ++ if (s) ++ Stream_Release(s); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_list_reader_groupsA(RdpdrServerContext* context, ++ void* callbackData, ++ const ListReaderGroups_Call* call, ++ UINT32* completionId) ++{ ++ return rdpdr_server_smartcard_list_reader_groups( ++ context, callbackData, SCARD_IOCTL_LISTREADERGROUPSA, call, completionId); ++} ++ ++static UINT rdpdr_server_smartcard_list_reader_groupsW(RdpdrServerContext* context, ++ void* callbackData, ++ const ListReaderGroups_Call* call, ++ UINT32* completionId) ++{ ++ return rdpdr_server_smartcard_list_reader_groups( ++ context, callbackData, SCARD_IOCTL_LISTREADERGROUPSW, call, completionId); ++} ++ ++/* ListReaders */ ++static UINT rdpdr_server_smartcard_list_readers_callback(RdpdrServerContext* context, wStream* s, ++ RDPDR_IRP* irp, UINT32 deviceId, ++ UINT32 completionId, UINT32 ioStatus) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(irp); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, ++ "SmartcardListReadersCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 ++ ", ioStatus=0x%" PRIx32, ++ deviceId, completionId, ioStatus); ++ ++ if (ioStatus != STATUS_SUCCESS) ++ { ++ if (context->OnSmartcardListReadersComplete) ++ context->OnSmartcardListReadersComplete(context, irp->CallbackData, ioStatus, ++ SCARD_F_INTERNAL_ERROR, nullptr); ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INVALID_DATA; ++ goto out; ++ } ++ ++ if (context->OnSmartcardListReadersComplete) ++ context->OnSmartcardListReadersComplete(context, irp->CallbackData, ioStatus, op.returnCode, ++ &op.ret.listReaders); ++ ++out: ++ smartcard_operation_free(&op, FALSE); ++ rdpdr_server_irp_free(irp); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_list_readers(RdpdrServerContext* context, void* callbackData, ++ UINT32 ioControlCode, const ListReaders_Call* call, ++ UINT32* completionId) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(call); ++ WINPR_ASSERT(completionId); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = nullptr; ++ wStream* s = nullptr; ++ ++ result = prepare_smartcard_irp( ++ context, ioControlCode, rdpdr_server_smartcard_list_readers_callback, callbackData, &irp); ++ if (result != CHANNEL_RC_OK) ++ return result; ++ ++ *completionId = irp->CompletionId; ++ ++ /* clang-format off */ ++ const SMARTCARD_OPERATION op = { ++ .ioControlCode = ioControlCode, ++ .call.listReaders = { ++ .handles.hContext = call->handles.hContext, ++ .cBytes = call->cBytes, ++ .mszGroups = call->mszGroups, ++ .fmszReadersIsNULL = call->fmszReadersIsNULL, ++ .cchReaders = call->cchReaders, ++ } ++ }; ++ /* clang-format on */ ++ ++ s = Stream_New(nullptr, 256); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ result = CHANNEL_RC_NO_MEMORY; ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_encode_request(s, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INTERNAL_ERROR; ++ goto out; ++ } ++ ++ result = rdpdr_server_send_device_control_request( ++ context, irp->DeviceId, irp->CompletionId, irp->IoControlCode, ++ SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); ++ ++out: ++ if (result != CHANNEL_RC_OK && irp) ++ rdpdr_server_discard_request(context, irp->CompletionId); ++ if (s) ++ Stream_Release(s); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_list_readersA(RdpdrServerContext* context, void* callbackData, ++ const ListReaders_Call* call, UINT32* completionId) ++{ ++ return rdpdr_server_smartcard_list_readers(context, callbackData, SCARD_IOCTL_LISTREADERSA, ++ call, completionId); ++} ++ ++static UINT rdpdr_server_smartcard_list_readersW(RdpdrServerContext* context, void* callbackData, ++ const ListReaders_Call* call, UINT32* completionId) ++{ ++ return rdpdr_server_smartcard_list_readers(context, callbackData, SCARD_IOCTL_LISTREADERSW, ++ call, completionId); ++} ++ ++/* GetStatusChange */ ++static UINT rdpdr_server_smartcard_get_status_change_callback(RdpdrServerContext* context, ++ wStream* s, RDPDR_IRP* irp, ++ UINT32 deviceId, UINT32 completionId, ++ UINT32 ioStatus) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(irp); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, ++ "SmartcardGetStatusChangeCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 ++ ", ioStatus=0x%" PRIx32, ++ deviceId, completionId, ioStatus); ++ ++ if (ioStatus != STATUS_SUCCESS) ++ { ++ if (context->OnSmartcardGetStatusChangeComplete) ++ context->OnSmartcardGetStatusChangeComplete(context, irp->CallbackData, ioStatus, ++ SCARD_F_INTERNAL_ERROR, nullptr); ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INVALID_DATA; ++ goto out; ++ } ++ ++ if (context->OnSmartcardGetStatusChangeComplete) ++ context->OnSmartcardGetStatusChangeComplete(context, irp->CallbackData, ioStatus, ++ op.returnCode, &op.ret.getStatusChange); ++ ++out: ++ smartcard_operation_free(&op, FALSE); ++ rdpdr_server_irp_free(irp); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_get_status_change(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioControlCode, ++ const void* call, DWORD cReaders, ++ UINT32* completionId) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(call); ++ WINPR_ASSERT(completionId); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = nullptr; ++ wStream* s = nullptr; ++ ++ result = prepare_smartcard_irp(context, ioControlCode, ++ rdpdr_server_smartcard_get_status_change_callback, callbackData, ++ &irp); ++ if (result != CHANNEL_RC_OK) ++ return result; ++ ++ *completionId = irp->CompletionId; ++ ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; ++ op.ioControlCode = ioControlCode; ++ ++ switch (ioControlCode) ++ { ++ case SCARD_IOCTL_GETSTATUSCHANGEA: ++ op.call.getStatusChangeA = *(const GetStatusChangeA_Call*)call; ++ break; ++ case SCARD_IOCTL_GETSTATUSCHANGEW: ++ op.call.getStatusChangeW = *(const GetStatusChangeW_Call*)call; ++ break; ++ default: ++ WINPR_ASSERT(FALSE); ++ return ERROR_INVALID_PARAMETER; ++ } ++ ++ s = Stream_New(nullptr, 256 + cReaders * 256); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ result = CHANNEL_RC_NO_MEMORY; ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_encode_request(s, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INTERNAL_ERROR; ++ goto out; ++ } ++ ++ result = rdpdr_server_send_device_control_request( ++ context, irp->DeviceId, irp->CompletionId, irp->IoControlCode, ++ SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); ++ ++out: ++ if (result != CHANNEL_RC_OK && irp) ++ rdpdr_server_discard_request(context, irp->CompletionId); ++ if (s) ++ Stream_Release(s); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_get_status_changeA(RdpdrServerContext* context, ++ void* callbackData, ++ const GetStatusChangeA_Call* call, ++ UINT32* completionId) ++{ ++ return rdpdr_server_smartcard_get_status_change( ++ context, callbackData, SCARD_IOCTL_GETSTATUSCHANGEA, call, call->cReaders, completionId); ++} ++ ++static UINT rdpdr_server_smartcard_get_status_changeW(RdpdrServerContext* context, ++ void* callbackData, ++ const GetStatusChangeW_Call* call, ++ UINT32* completionId) ++{ ++ return rdpdr_server_smartcard_get_status_change( ++ context, callbackData, SCARD_IOCTL_GETSTATUSCHANGEW, call, call->cReaders, completionId); ++} ++ ++/* Cancel */ ++static UINT rdpdr_server_smartcard_cancel_callback(RdpdrServerContext* context, wStream* s, ++ RDPDR_IRP* irp, UINT32 deviceId, ++ UINT32 completionId, UINT32 ioStatus) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(irp); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, ++ "SmartcardCancelCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 ++ ", ioStatus=0x%" PRIx32, ++ deviceId, completionId, ioStatus); ++ ++ if (ioStatus != STATUS_SUCCESS) ++ { ++ if (context->OnSmartcardCancelComplete) ++ context->OnSmartcardCancelComplete(context, irp->CallbackData, ioStatus, ++ SCARD_F_INTERNAL_ERROR); ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INVALID_DATA; ++ goto out; ++ } ++ ++ if (context->OnSmartcardCancelComplete) ++ context->OnSmartcardCancelComplete(context, irp->CallbackData, ioStatus, op.returnCode); ++ ++out: ++ smartcard_operation_free(&op, FALSE); ++ rdpdr_server_irp_free(irp); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_cancel(RdpdrServerContext* context, void* callbackData, ++ const REDIR_SCARDCONTEXT* hContext, UINT32* completionId) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(hContext); ++ WINPR_ASSERT(completionId); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = nullptr; ++ wStream* s = nullptr; ++ ++ result = prepare_smartcard_irp(context, SCARD_IOCTL_CANCEL, ++ rdpdr_server_smartcard_cancel_callback, callbackData, &irp); ++ if (result != CHANNEL_RC_OK) ++ return result; ++ ++ *completionId = irp->CompletionId; ++ ++ const SMARTCARD_OPERATION op = { ++ .ioControlCode = SCARD_IOCTL_CANCEL, ++ .call.context.handles.hContext = *hContext, ++ }; ++ ++ s = Stream_New(nullptr, 64); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ result = CHANNEL_RC_NO_MEMORY; ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_encode_request(s, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INTERNAL_ERROR; ++ goto out; ++ } ++ ++ result = rdpdr_server_send_device_control_request( ++ context, irp->DeviceId, irp->CompletionId, irp->IoControlCode, ++ SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); ++ ++out: ++ if (result != CHANNEL_RC_OK && irp) ++ rdpdr_server_discard_request(context, irp->CompletionId); ++ if (s) ++ Stream_Release(s); ++ return result; ++} ++ ++/* Connect */ ++static UINT rdpdr_server_smartcard_connect_callback(RdpdrServerContext* context, wStream* s, ++ RDPDR_IRP* irp, UINT32 deviceId, ++ UINT32 completionId, UINT32 ioStatus) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(irp); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, ++ "SmartcardConnectCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 ++ ", ioStatus=0x%" PRIx32, ++ deviceId, completionId, ioStatus); ++ ++ if (ioStatus != STATUS_SUCCESS) ++ { ++ if (context->OnSmartcardConnectComplete) ++ context->OnSmartcardConnectComplete(context, irp->CallbackData, ioStatus, ++ SCARD_F_INTERNAL_ERROR, nullptr); ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INVALID_DATA; ++ goto out; ++ } ++ ++ if (context->OnSmartcardConnectComplete) ++ context->OnSmartcardConnectComplete(context, irp->CallbackData, ioStatus, op.returnCode, ++ &op.ret.connect); ++ ++out: ++ smartcard_operation_free(&op, FALSE); ++ rdpdr_server_irp_free(irp); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_connect(RdpdrServerContext* context, void* callbackData, ++ UINT32 ioControlCode, const void* call, ++ UINT32* completionId) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(call); ++ WINPR_ASSERT(completionId); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = nullptr; ++ wStream* s = nullptr; ++ ++ result = prepare_smartcard_irp(context, ioControlCode, rdpdr_server_smartcard_connect_callback, ++ callbackData, &irp); ++ if (result != CHANNEL_RC_OK) ++ return result; ++ ++ *completionId = irp->CompletionId; ++ ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; ++ op.ioControlCode = ioControlCode; ++ ++ switch (ioControlCode) ++ { ++ case SCARD_IOCTL_CONNECTA: ++ op.call.connectA = *(const ConnectA_Call*)call; ++ break; ++ case SCARD_IOCTL_CONNECTW: ++ op.call.connectW = *(const ConnectW_Call*)call; ++ break; ++ default: ++ WINPR_ASSERT(FALSE); ++ return ERROR_INVALID_PARAMETER; ++ } ++ ++ s = Stream_New(nullptr, 512); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ result = CHANNEL_RC_NO_MEMORY; ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_encode_request(s, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INTERNAL_ERROR; ++ goto out; ++ } ++ ++ result = rdpdr_server_send_device_control_request( ++ context, irp->DeviceId, irp->CompletionId, irp->IoControlCode, ++ SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); ++ ++out: ++ if (result != CHANNEL_RC_OK && irp) ++ rdpdr_server_discard_request(context, irp->CompletionId); ++ if (s) ++ Stream_Release(s); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_connectA(RdpdrServerContext* context, void* callbackData, ++ const ConnectA_Call* call, UINT32* completionId) ++{ ++ return rdpdr_server_smartcard_connect(context, callbackData, SCARD_IOCTL_CONNECTA, call, ++ completionId); ++} ++ ++static UINT rdpdr_server_smartcard_connectW(RdpdrServerContext* context, void* callbackData, ++ const ConnectW_Call* call, UINT32* completionId) ++{ ++ return rdpdr_server_smartcard_connect(context, callbackData, SCARD_IOCTL_CONNECTW, call, ++ completionId); ++} ++ ++/* Reconnect */ ++static UINT rdpdr_server_smartcard_reconnect_callback(RdpdrServerContext* context, wStream* s, ++ RDPDR_IRP* irp, UINT32 deviceId, ++ UINT32 completionId, UINT32 ioStatus) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(irp); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, ++ "SmartcardReconnectCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 ++ ", ioStatus=0x%" PRIx32, ++ deviceId, completionId, ioStatus); ++ ++ if (ioStatus != STATUS_SUCCESS) ++ { ++ if (context->OnSmartcardReconnectComplete) ++ context->OnSmartcardReconnectComplete(context, irp->CallbackData, ioStatus, ++ SCARD_F_INTERNAL_ERROR, nullptr); ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INVALID_DATA; ++ goto out; ++ } ++ ++ if (context->OnSmartcardReconnectComplete) ++ context->OnSmartcardReconnectComplete(context, irp->CallbackData, ioStatus, op.returnCode, ++ &op.ret.reconnect); ++ ++out: ++ smartcard_operation_free(&op, FALSE); ++ rdpdr_server_irp_free(irp); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_reconnect(RdpdrServerContext* context, void* callbackData, ++ const Reconnect_Call* call, UINT32* completionId) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(call); ++ WINPR_ASSERT(completionId); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = nullptr; ++ wStream* s = nullptr; ++ ++ result = prepare_smartcard_irp(context, SCARD_IOCTL_RECONNECT, ++ rdpdr_server_smartcard_reconnect_callback, callbackData, &irp); ++ if (result != CHANNEL_RC_OK) ++ return result; ++ ++ *completionId = irp->CompletionId; ++ ++ /* clang-format off */ ++ const SMARTCARD_OPERATION op = { ++ .ioControlCode = SCARD_IOCTL_RECONNECT, ++ .call.reconnect = { ++ .handles.hContext = call->handles.hContext, ++ .handles.hCard = call->handles.hCard, ++ .dwShareMode = call->dwShareMode, ++ .dwPreferredProtocols = call->dwPreferredProtocols, ++ .dwInitialization = call->dwInitialization, ++ } ++ }; ++ /* clang-format on */ ++ ++ s = Stream_New(nullptr, 128); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ result = CHANNEL_RC_NO_MEMORY; ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_encode_request(s, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INTERNAL_ERROR; ++ goto out; ++ } ++ ++ result = rdpdr_server_send_device_control_request( ++ context, irp->DeviceId, irp->CompletionId, irp->IoControlCode, ++ SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); ++ ++out: ++ if (result != CHANNEL_RC_OK && irp) ++ rdpdr_server_discard_request(context, irp->CompletionId); ++ if (s) ++ Stream_Release(s); ++ return result; ++} ++ ++/* Disconnect */ ++static UINT rdpdr_server_smartcard_disconnect_callback(RdpdrServerContext* context, wStream* s, ++ RDPDR_IRP* irp, UINT32 deviceId, ++ UINT32 completionId, UINT32 ioStatus) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(irp); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, ++ "SmartcardDisconnectCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 ++ ", ioStatus=0x%" PRIx32, ++ deviceId, completionId, ioStatus); ++ ++ if (ioStatus != STATUS_SUCCESS) ++ { ++ if (context->OnSmartcardDisconnectComplete) ++ context->OnSmartcardDisconnectComplete(context, irp->CallbackData, ioStatus, ++ SCARD_F_INTERNAL_ERROR); ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INVALID_DATA; ++ goto out; ++ } ++ ++ if (context->OnSmartcardDisconnectComplete) ++ context->OnSmartcardDisconnectComplete(context, irp->CallbackData, ioStatus, op.returnCode); ++ ++out: ++ smartcard_operation_free(&op, FALSE); ++ rdpdr_server_irp_free(irp); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_disconnect(RdpdrServerContext* context, void* callbackData, ++ const HCardAndDisposition_Call* call, ++ UINT32* completionId) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(call); ++ WINPR_ASSERT(completionId); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = nullptr; ++ wStream* s = nullptr; ++ ++ result = prepare_smartcard_irp(context, SCARD_IOCTL_DISCONNECT, ++ rdpdr_server_smartcard_disconnect_callback, callbackData, &irp); ++ if (result != CHANNEL_RC_OK) ++ return result; ++ ++ *completionId = irp->CompletionId; ++ ++ /* clang-format off */ ++ const SMARTCARD_OPERATION op = { ++ .ioControlCode = SCARD_IOCTL_DISCONNECT, ++ .call.hCardAndDisposition = { ++ .handles.hContext = call->handles.hContext, ++ .handles.hCard = call->handles.hCard, ++ .dwDisposition = call->dwDisposition, ++ } ++ }; ++ /* clang-format on */ ++ ++ s = Stream_New(nullptr, 128); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ result = CHANNEL_RC_NO_MEMORY; ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_encode_request(s, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INTERNAL_ERROR; ++ goto out; ++ } ++ ++ result = rdpdr_server_send_device_control_request( ++ context, irp->DeviceId, irp->CompletionId, irp->IoControlCode, ++ SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); ++ ++out: ++ if (result != CHANNEL_RC_OK && irp) ++ rdpdr_server_discard_request(context, irp->CompletionId); ++ if (s) ++ Stream_Release(s); ++ return result; ++} ++ ++/* BeginTransaction */ ++static UINT rdpdr_server_smartcard_begin_transaction_callback(RdpdrServerContext* context, ++ wStream* s, RDPDR_IRP* irp, ++ UINT32 deviceId, UINT32 completionId, ++ UINT32 ioStatus) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(irp); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, ++ "SmartcardBeginTransactionCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 ++ ", ioStatus=0x%" PRIx32, ++ deviceId, completionId, ioStatus); ++ ++ if (ioStatus != STATUS_SUCCESS) ++ { ++ if (context->OnSmartcardBeginTransactionComplete) ++ context->OnSmartcardBeginTransactionComplete(context, irp->CallbackData, ioStatus, ++ SCARD_F_INTERNAL_ERROR); ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INVALID_DATA; ++ goto out; ++ } ++ ++ if (context->OnSmartcardBeginTransactionComplete) ++ context->OnSmartcardBeginTransactionComplete(context, irp->CallbackData, ioStatus, ++ op.returnCode); ++ ++out: ++ smartcard_operation_free(&op, FALSE); ++ rdpdr_server_irp_free(irp); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_begin_transaction(RdpdrServerContext* context, ++ void* callbackData, ++ const HCardAndDisposition_Call* call, ++ UINT32* completionId) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(call); ++ WINPR_ASSERT(completionId); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = nullptr; ++ wStream* s = nullptr; ++ ++ result = prepare_smartcard_irp(context, SCARD_IOCTL_BEGINTRANSACTION, ++ rdpdr_server_smartcard_begin_transaction_callback, callbackData, ++ &irp); ++ if (result != CHANNEL_RC_OK) ++ return result; ++ ++ *completionId = irp->CompletionId; ++ ++ /* clang-format off */ ++ const SMARTCARD_OPERATION op = { ++ .ioControlCode = SCARD_IOCTL_BEGINTRANSACTION, ++ .call.hCardAndDisposition = { ++ .handles.hContext = call->handles.hContext, ++ .handles.hCard = call->handles.hCard, ++ .dwDisposition = SCARD_LEAVE_CARD, ++ } ++ }; ++ /* clang-format on */ ++ ++ s = Stream_New(nullptr, 128); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ result = CHANNEL_RC_NO_MEMORY; ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_encode_request(s, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INTERNAL_ERROR; ++ goto out; ++ } ++ ++ result = rdpdr_server_send_device_control_request( ++ context, irp->DeviceId, irp->CompletionId, irp->IoControlCode, ++ SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); ++ ++out: ++ if (result != CHANNEL_RC_OK && irp) ++ rdpdr_server_discard_request(context, irp->CompletionId); ++ if (s) ++ Stream_Release(s); ++ return result; ++} ++ ++/* EndTransaction */ ++static UINT rdpdr_server_smartcard_end_transaction_callback(RdpdrServerContext* context, wStream* s, ++ RDPDR_IRP* irp, UINT32 deviceId, ++ UINT32 completionId, UINT32 ioStatus) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(irp); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, ++ "SmartcardEndTransactionCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 ++ ", ioStatus=0x%" PRIx32, ++ deviceId, completionId, ioStatus); ++ ++ if (ioStatus != STATUS_SUCCESS) ++ { ++ if (context->OnSmartcardEndTransactionComplete) ++ context->OnSmartcardEndTransactionComplete(context, irp->CallbackData, ioStatus, ++ SCARD_F_INTERNAL_ERROR); ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INVALID_DATA; ++ goto out; ++ } ++ ++ if (context->OnSmartcardEndTransactionComplete) ++ context->OnSmartcardEndTransactionComplete(context, irp->CallbackData, ioStatus, ++ op.returnCode); ++ ++out: ++ smartcard_operation_free(&op, FALSE); ++ rdpdr_server_irp_free(irp); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_end_transaction(RdpdrServerContext* context, void* callbackData, ++ const HCardAndDisposition_Call* call, ++ UINT32* completionId) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(call); ++ WINPR_ASSERT(completionId); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = nullptr; ++ wStream* s = nullptr; ++ ++ result = ++ prepare_smartcard_irp(context, SCARD_IOCTL_ENDTRANSACTION, ++ rdpdr_server_smartcard_end_transaction_callback, callbackData, &irp); ++ if (result != CHANNEL_RC_OK) ++ return result; ++ ++ *completionId = irp->CompletionId; ++ ++ /* clang-format off */ ++ const SMARTCARD_OPERATION op = { ++ .ioControlCode = SCARD_IOCTL_ENDTRANSACTION, ++ .call.hCardAndDisposition = { ++ .handles.hContext = call->handles.hContext, ++ .handles.hCard = call->handles.hCard, ++ .dwDisposition = call->dwDisposition, ++ } ++ }; ++ /* clang-format on */ ++ ++ s = Stream_New(nullptr, 128); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ result = CHANNEL_RC_NO_MEMORY; ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_encode_request(s, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INTERNAL_ERROR; ++ goto out; ++ } ++ ++ result = rdpdr_server_send_device_control_request( ++ context, irp->DeviceId, irp->CompletionId, irp->IoControlCode, ++ SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); ++ ++out: ++ if (result != CHANNEL_RC_OK && irp) ++ rdpdr_server_discard_request(context, irp->CompletionId); ++ if (s) ++ Stream_Release(s); ++ return result; ++} ++ ++/* Status */ ++static UINT rdpdr_server_smartcard_status_callback(RdpdrServerContext* context, wStream* s, ++ RDPDR_IRP* irp, UINT32 deviceId, ++ UINT32 completionId, UINT32 ioStatus) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(irp); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, ++ "SmartcardStatusCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 ++ ", ioStatus=0x%" PRIx32, ++ deviceId, completionId, ioStatus); ++ ++ if (ioStatus != STATUS_SUCCESS) ++ { ++ if (context->OnSmartcardStatusComplete) ++ context->OnSmartcardStatusComplete(context, irp->CallbackData, ioStatus, ++ SCARD_F_INTERNAL_ERROR, nullptr); ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INVALID_DATA; ++ goto out; ++ } ++ ++ if (context->OnSmartcardStatusComplete) ++ context->OnSmartcardStatusComplete(context, irp->CallbackData, ioStatus, op.returnCode, ++ &op.ret.status); ++ ++out: ++ smartcard_operation_free(&op, FALSE); ++ rdpdr_server_irp_free(irp); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_status(RdpdrServerContext* context, void* callbackData, ++ UINT32 ioControlCode, const Status_Call* call, ++ UINT32* completionId) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(call); ++ WINPR_ASSERT(completionId); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = nullptr; ++ wStream* s = nullptr; ++ ++ result = prepare_smartcard_irp(context, ioControlCode, rdpdr_server_smartcard_status_callback, ++ callbackData, &irp); ++ if (result != CHANNEL_RC_OK) ++ return result; ++ ++ *completionId = irp->CompletionId; ++ ++ /* clang-format off */ ++ const SMARTCARD_OPERATION op = { ++ .ioControlCode = ioControlCode, ++ .call.status = { ++ .handles.hContext = call->handles.hContext, ++ .handles.hCard = call->handles.hCard, ++ .fmszReaderNamesIsNULL = call->fmszReaderNamesIsNULL, ++ .cchReaderLen = call->cchReaderLen, ++ .cbAtrLen = call->cbAtrLen, ++ } ++ }; ++ /* clang-format on */ ++ ++ s = Stream_New(nullptr, 128); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ result = CHANNEL_RC_NO_MEMORY; ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_encode_request(s, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INTERNAL_ERROR; ++ goto out; ++ } ++ ++ result = rdpdr_server_send_device_control_request( ++ context, irp->DeviceId, irp->CompletionId, irp->IoControlCode, ++ SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); ++ ++out: ++ if (result != CHANNEL_RC_OK && irp) ++ rdpdr_server_discard_request(context, irp->CompletionId); ++ if (s) ++ Stream_Release(s); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_statusA(RdpdrServerContext* context, void* callbackData, ++ const Status_Call* call, UINT32* completionId) ++{ ++ return rdpdr_server_smartcard_status(context, callbackData, SCARD_IOCTL_STATUSA, call, ++ completionId); ++} ++ ++static UINT rdpdr_server_smartcard_statusW(RdpdrServerContext* context, void* callbackData, ++ const Status_Call* call, UINT32* completionId) ++{ ++ return rdpdr_server_smartcard_status(context, callbackData, SCARD_IOCTL_STATUSW, call, ++ completionId); ++} ++ ++/* Transmit */ ++static UINT rdpdr_server_smartcard_transmit_callback(RdpdrServerContext* context, wStream* s, ++ RDPDR_IRP* irp, UINT32 deviceId, ++ UINT32 completionId, UINT32 ioStatus) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(irp); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, ++ "SmartcardTransmitCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 ++ ", ioStatus=0x%" PRIx32, ++ deviceId, completionId, ioStatus); ++ ++ if (ioStatus != STATUS_SUCCESS) ++ { ++ if (context->OnSmartcardTransmitComplete) ++ context->OnSmartcardTransmitComplete(context, irp->CallbackData, ioStatus, ++ SCARD_F_INTERNAL_ERROR, nullptr); ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INVALID_DATA; ++ goto out; ++ } ++ ++ if (context->OnSmartcardTransmitComplete) ++ context->OnSmartcardTransmitComplete(context, irp->CallbackData, ioStatus, op.returnCode, ++ &op.ret.transmit); ++ ++out: ++ smartcard_operation_free(&op, FALSE); ++ rdpdr_server_irp_free(irp); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_transmit(RdpdrServerContext* context, void* callbackData, ++ const Transmit_Call* call, UINT32* completionId) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(call); ++ WINPR_ASSERT(completionId); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = nullptr; ++ wStream* s = nullptr; ++ ++ result = prepare_smartcard_irp(context, SCARD_IOCTL_TRANSMIT, ++ rdpdr_server_smartcard_transmit_callback, callbackData, &irp); ++ if (result != CHANNEL_RC_OK) ++ return result; ++ ++ *completionId = irp->CompletionId; ++ ++ /* clang-format off */ ++ const SMARTCARD_OPERATION op = { ++ .ioControlCode = SCARD_IOCTL_TRANSMIT, ++ .call.transmit = *call ++ }; ++ /* clang-format on */ ++ ++ s = Stream_New(nullptr, 512 + call->cbSendLength); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ result = CHANNEL_RC_NO_MEMORY; ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_encode_request(s, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INTERNAL_ERROR; ++ goto out; ++ } ++ ++ result = rdpdr_server_send_device_control_request( ++ context, irp->DeviceId, irp->CompletionId, irp->IoControlCode, ++ SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); ++ ++out: ++ if (result != CHANNEL_RC_OK && irp) ++ rdpdr_server_discard_request(context, irp->CompletionId); ++ if (s) ++ Stream_Release(s); ++ return result; ++} ++ ++/* Control */ ++static UINT rdpdr_server_smartcard_control_callback(RdpdrServerContext* context, wStream* s, ++ RDPDR_IRP* irp, UINT32 deviceId, ++ UINT32 completionId, UINT32 ioStatus) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(irp); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, ++ "SmartcardControlCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 ++ ", ioStatus=0x%" PRIx32, ++ deviceId, completionId, ioStatus); ++ ++ if (ioStatus != STATUS_SUCCESS) ++ { ++ if (context->OnSmartcardControlComplete) ++ context->OnSmartcardControlComplete(context, irp->CallbackData, ioStatus, ++ SCARD_F_INTERNAL_ERROR, nullptr); ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INVALID_DATA; ++ goto out; ++ } ++ ++ if (context->OnSmartcardControlComplete) ++ context->OnSmartcardControlComplete(context, irp->CallbackData, ioStatus, op.returnCode, ++ &op.ret.control); ++ ++out: ++ smartcard_operation_free(&op, FALSE); ++ rdpdr_server_irp_free(irp); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_control(RdpdrServerContext* context, void* callbackData, ++ const Control_Call* call, UINT32* completionId) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(call); ++ WINPR_ASSERT(completionId); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = nullptr; ++ wStream* s = nullptr; ++ ++ result = prepare_smartcard_irp(context, SCARD_IOCTL_CONTROL, ++ rdpdr_server_smartcard_control_callback, callbackData, &irp); ++ if (result != CHANNEL_RC_OK) ++ return result; ++ ++ *completionId = irp->CompletionId; ++ ++ /* clang-format off */ ++ const SMARTCARD_OPERATION op = { ++ .ioControlCode = SCARD_IOCTL_CONTROL, ++ .call.control = { ++ .handles.hContext = call->handles.hContext, ++ .handles.hCard = call->handles.hCard, ++ .dwControlCode = call->dwControlCode, ++ .cbInBufferSize = call->cbInBufferSize, ++ .pvInBuffer = call->pvInBuffer, ++ .fpvOutBufferIsNULL = call->fpvOutBufferIsNULL, ++ .cbOutBufferSize = call->cbOutBufferSize, ++ } ++ }; ++ /* clang-format on */ ++ ++ s = Stream_New(nullptr, 512 + call->cbOutBufferSize); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ result = CHANNEL_RC_NO_MEMORY; ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_encode_request(s, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INTERNAL_ERROR; ++ goto out; ++ } ++ ++ result = rdpdr_server_send_device_control_request( ++ context, irp->DeviceId, irp->CompletionId, irp->IoControlCode, ++ SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); ++ ++out: ++ if (result != CHANNEL_RC_OK && irp) ++ rdpdr_server_discard_request(context, irp->CompletionId); ++ if (s) ++ Stream_Release(s); ++ return result; ++} ++ ++/* getAttrib */ ++static UINT rdpdr_server_smartcard_get_attrib_callback(RdpdrServerContext* context, wStream* s, ++ RDPDR_IRP* irp, UINT32 deviceId, ++ UINT32 completionId, UINT32 ioStatus) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(irp); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, ++ "SmartcardGetAttribCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 ++ ", ioStatus=0x%" PRIx32, ++ deviceId, completionId, ioStatus); ++ ++ if (ioStatus != STATUS_SUCCESS) ++ { ++ if (context->OnSmartcardGetAttribComplete) ++ context->OnSmartcardGetAttribComplete(context, irp->CallbackData, ioStatus, ++ SCARD_F_INTERNAL_ERROR, nullptr); ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INVALID_DATA; ++ goto out; ++ } ++ ++ if (context->OnSmartcardGetAttribComplete) ++ context->OnSmartcardGetAttribComplete(context, irp->CallbackData, ioStatus, op.returnCode, ++ &op.ret.getAttrib); ++ ++out: ++ smartcard_operation_free(&op, FALSE); ++ rdpdr_server_irp_free(irp); ++ return result; ++} ++ ++UINT rdpdr_server_smartcard_get_attrib(RdpdrServerContext* context, void* callbackData, ++ const GetAttrib_Call* call, UINT32* completionId) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(call); ++ WINPR_ASSERT(completionId); ++ ++ RdpdrServerPrivate* priv = context->priv; ++ ++ RDPDR_IRP* irp = nullptr; ++ UINT ret = ++ prepare_smartcard_irp(context, SCARD_IOCTL_GETATTRIB, ++ rdpdr_server_smartcard_get_attrib_callback, callbackData, &irp); ++ if (ret != CHANNEL_RC_OK) ++ return ret; ++ ++ *completionId = irp->CompletionId; ++ ++ /* clang-format off */ ++ const SMARTCARD_OPERATION op = { ++ .ioControlCode = SCARD_IOCTL_GETATTRIB, ++ .call.getAttrib = *call ++ }; ++ /* clang-format on */ ++ ++ wStream* s = Stream_New(nullptr, 64); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ return CHANNEL_RC_NO_MEMORY; ++ } ++ ++ LONG status = smartcard_irp_device_control_encode_request(s, &op); ++ if (status != SCARD_S_SUCCESS) ++ { ++ Stream_Release(s); ++ return ERROR_INTERNAL_ERROR; ++ } ++ ++ const UINT error = rdpdr_server_send_device_control_request( ++ context, irp->DeviceId, irp->CompletionId, irp->IoControlCode, ++ SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); ++ ++ Stream_Release(s); ++ return error; ++} ++ ++/* SetAttrib */ ++static UINT rdpdr_server_smartcard_set_attrib_callback(RdpdrServerContext* context, wStream* s, ++ RDPDR_IRP* irp, UINT32 deviceId, ++ UINT32 completionId, UINT32 ioStatus) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(s); ++ WINPR_ASSERT(irp); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT; ++ ++ WLog_Print(priv->log, WLOG_DEBUG, ++ "SmartcardSetAttribCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32 ++ ", ioStatus=0x%" PRIx32, ++ deviceId, completionId, ioStatus); ++ ++ if (ioStatus != STATUS_SUCCESS) ++ { ++ if (context->OnSmartcardSetAttribComplete) ++ context->OnSmartcardSetAttribComplete(context, irp->CallbackData, ioStatus, ++ SCARD_F_INTERNAL_ERROR); ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INVALID_DATA; ++ goto out; ++ } ++ ++ if (context->OnSmartcardSetAttribComplete) ++ context->OnSmartcardSetAttribComplete(context, irp->CallbackData, ioStatus, op.returnCode); ++ ++out: ++ smartcard_operation_free(&op, FALSE); ++ rdpdr_server_irp_free(irp); ++ return result; ++} ++ ++static UINT rdpdr_server_smartcard_set_attrib(RdpdrServerContext* context, void* callbackData, ++ const SetAttrib_Call* call, UINT32* completionId) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(context->priv); ++ WINPR_ASSERT(call); ++ WINPR_ASSERT(completionId); ++ ++ UINT result = CHANNEL_RC_OK; ++ RdpdrServerPrivate* priv = context->priv; ++ RDPDR_IRP* irp = nullptr; ++ wStream* s = nullptr; ++ ++ result = prepare_smartcard_irp(context, SCARD_IOCTL_SETATTRIB, ++ rdpdr_server_smartcard_set_attrib_callback, callbackData, &irp); ++ if (result != CHANNEL_RC_OK) ++ return result; ++ ++ *completionId = irp->CompletionId; ++ ++ /* clang-format off */ ++ const SMARTCARD_OPERATION op = { ++ .ioControlCode = SCARD_IOCTL_SETATTRIB, ++ .call.setAttrib = { ++ .handles.hContext = call->handles.hContext, ++ .handles.hCard = call->handles.hCard, ++ .dwAttrId = call->dwAttrId, ++ .cbAttrLen = call->cbAttrLen, ++ .pbAttr = call->pbAttr, ++ } ++ }; ++ /* clang-format on */ ++ ++ s = Stream_New(nullptr, 64 + call->cbAttrLen); ++ if (!s) ++ { ++ WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); ++ result = CHANNEL_RC_NO_MEMORY; ++ goto out; ++ } ++ ++ result = smartcard_irp_device_control_encode_request(s, &op); ++ if (result != SCARD_S_SUCCESS) ++ { ++ result = ERROR_INTERNAL_ERROR; ++ goto out; ++ } ++ ++ result = rdpdr_server_send_device_control_request( ++ context, irp->DeviceId, irp->CompletionId, irp->IoControlCode, ++ SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); ++ ++out: ++ if (result != CHANNEL_RC_OK && irp) ++ rdpdr_server_discard_request(context, irp->CompletionId); ++ if (s) ++ Stream_Release(s); ++ return result; ++} ++ ++RdpdrServerContext* rdpdr_server_context_new(HANDLE vcm) ++{ ++ RdpdrServerContext* context = (RdpdrServerContext*)calloc(1, sizeof(RdpdrServerContext)); ++ ++ if (!context) ++ goto fail; ++ ++ context->vcm = vcm; ++ context->Start = rdpdr_server_start; ++ context->Stop = rdpdr_server_stop; ++ context->DriveCreateDirectory = rdpdr_server_drive_create_directory; ++ context->DriveDeleteDirectory = rdpdr_server_drive_delete_directory; ++ context->DriveQueryDirectory = rdpdr_server_drive_query_directory; ++ context->DriveOpenFile = rdpdr_server_drive_open_file; ++ context->DriveReadFile = rdpdr_server_drive_read_file; ++ context->DriveWriteFile = rdpdr_server_drive_write_file; ++ context->DriveCloseFile = rdpdr_server_drive_close_file; ++ context->DriveDeleteFile = rdpdr_server_drive_delete_file; ++ context->DriveRenameFile = rdpdr_server_drive_rename_file; ++ context->SmartcardEstablishContext = rdpdr_server_smartcard_establish_context; ++ context->SmartcardReleaseContext = rdpdr_server_smartcard_release_context; ++ context->SmartcardIsValidContext = rdpdr_server_smartcard_is_valid_context; ++ context->SmartcardListReaderGroupsA = rdpdr_server_smartcard_list_reader_groupsA; ++ context->SmartcardListReaderGroupsW = rdpdr_server_smartcard_list_reader_groupsW; ++ context->SmartcardListReadersA = rdpdr_server_smartcard_list_readersA; ++ context->SmartcardListReadersW = rdpdr_server_smartcard_list_readersW; ++ context->SmartcardGetStatusChangeA = rdpdr_server_smartcard_get_status_changeA; ++ context->SmartcardGetStatusChangeW = rdpdr_server_smartcard_get_status_changeW; ++ context->SmartcardCancel = rdpdr_server_smartcard_cancel; ++ context->SmartcardConnectA = rdpdr_server_smartcard_connectA; ++ context->SmartcardConnectW = rdpdr_server_smartcard_connectW; ++ context->SmartcardReconnect = rdpdr_server_smartcard_reconnect; ++ context->SmartcardDisconnect = rdpdr_server_smartcard_disconnect; ++ context->SmartcardBeginTransaction = rdpdr_server_smartcard_begin_transaction; ++ context->SmartcardEndTransaction = rdpdr_server_smartcard_end_transaction; ++ context->SmartcardStatusA = rdpdr_server_smartcard_statusA; ++ context->SmartcardStatusW = rdpdr_server_smartcard_statusW; ++ context->SmartcardTransmit = rdpdr_server_smartcard_transmit; ++ context->SmartcardControl = rdpdr_server_smartcard_control; ++ context->SmartcardGetAttrib = rdpdr_server_smartcard_get_attrib; ++ context->SmartcardSetAttrib = rdpdr_server_smartcard_set_attrib; ++ context->priv = rdpdr_server_private_new(); ++ if (!context->priv) ++ goto fail; ++ ++ /* By default announce everything, the server application can deactivate that later on */ ++ context->supported = UINT16_MAX; ++ ++ return context; ++fail: ++ WINPR_PRAGMA_DIAG_PUSH ++ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC ++ rdpdr_server_context_free(context); ++ WINPR_PRAGMA_DIAG_POP ++ return nullptr; ++} ++ ++void rdpdr_server_context_free(RdpdrServerContext* context) ++{ ++ if (!context) ++ return; ++ ++ rdpdr_server_private_free(context->priv); ++ free(context); ++} ++ ++void rdpdr_server_discard_request(RdpdrServerContext* context, UINT32 completionId) ++{ ++ WINPR_ASSERT(context); ++ ++ RdpdrServerPrivate* priv = context->priv; ++ const uintptr_t key = completionId + 1ull; ++ ListDictionary_Remove(priv->IrpList, (void*)key); + } +diff --git a/include/freerdp/server/rdpdr.h b/include/freerdp/server/rdpdr.h +index 411b33b6d6d7..a44a7e1cef87 100644 +--- a/include/freerdp/server/rdpdr.h ++++ b/include/freerdp/server/rdpdr.h +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + #include + + #ifdef __cplusplus +@@ -133,6 +134,117 @@ extern "C" + typedef UINT (*psRdpdrOnDeviceCreate)(RdpdrServerContext* context, const RdpdrDevice* device); + typedef UINT (*psRdpdrOnDeviceDelete)(RdpdrServerContext* context, UINT32 deviceId); + ++ typedef UINT (*psRdpdrSmartcardEstablishContext)(RdpdrServerContext* context, ++ void* callbackData, UINT32 dwScope, ++ UINT32* completionId); ++ typedef UINT (*psRdpdrSmartcardReleaseContext)(RdpdrServerContext* context, void* callbackData, ++ const REDIR_SCARDCONTEXT* hContext, ++ UINT32* completionId); ++ typedef UINT (*psRdpdrSmartcardIsValidContext)(RdpdrServerContext* context, void* callbackData, ++ const REDIR_SCARDCONTEXT* hContext, ++ UINT32* completionId); ++ typedef UINT (*psRdpdrSmartcardListReaderGroups)(RdpdrServerContext* context, ++ void* callbackData, ++ const ListReaderGroups_Call* call, ++ UINT32* completionId); ++ typedef UINT (*psRdpdrSmartcardListReaders)(RdpdrServerContext* context, void* callbackData, ++ const ListReaders_Call* call, UINT32* completionId); ++ typedef UINT (*psRdpdrSmartcardGetStatusChangeA)(RdpdrServerContext* context, ++ void* callbackData, ++ const GetStatusChangeA_Call* call, ++ UINT32* completionId); ++ typedef UINT (*psRdpdrSmartcardGetStatusChangeW)(RdpdrServerContext* context, ++ void* callbackData, ++ const GetStatusChangeW_Call* call, ++ UINT32* completionId); ++ typedef UINT (*psRdpdrSmartcardCancel)(RdpdrServerContext* context, void* callbackData, ++ const REDIR_SCARDCONTEXT* hContext, ++ UINT32* completionId); ++ typedef UINT (*psRdpdrSmartcardConnectA)(RdpdrServerContext* context, void* callbackData, ++ const ConnectA_Call* call, UINT32* completionId); ++ typedef UINT (*psRdpdrSmartcardConnectW)(RdpdrServerContext* context, void* callbackData, ++ const ConnectW_Call* call, UINT32* completionId); ++ typedef UINT (*psRdpdrSmartcardReconnect)(RdpdrServerContext* context, void* callbackData, ++ const Reconnect_Call* call, UINT32* completionId); ++ typedef UINT (*psRdpdrSmartcardDisconnect)(RdpdrServerContext* context, void* callbackData, ++ const HCardAndDisposition_Call* call, ++ UINT32* completionId); ++ typedef UINT (*psRdpdrSmartcardBeginTransaction)(RdpdrServerContext* context, ++ void* callbackData, ++ const HCardAndDisposition_Call* call, ++ UINT32* completionId); ++ typedef UINT (*psRdpdrSmartcardEndTransaction)(RdpdrServerContext* context, void* callbackData, ++ const HCardAndDisposition_Call* call, ++ UINT32* completionId); ++ typedef UINT (*psRdpdrSmartcardStatus)(RdpdrServerContext* context, void* callbackData, ++ const Status_Call* call, UINT32* completionId); ++ typedef UINT (*psRdpdrSmartcardTransmit)(RdpdrServerContext* context, void* callbackData, ++ const Transmit_Call* call, UINT32* completionId); ++ typedef UINT (*psRdpdrSmartcardControl)(RdpdrServerContext* context, void* callbackData, ++ const Control_Call* call, UINT32* completionId); ++ typedef UINT (*psRdpdrSmartcardGetAttrib)(RdpdrServerContext* context, void* callbackData, ++ const GetAttrib_Call* call, UINT32* completionId); ++ typedef UINT (*psRdpdrSmartcardSetAttrib)(RdpdrServerContext* context, void* callbackData, ++ const SetAttrib_Call* call, UINT32* completionId); ++ ++ typedef void (*psRdpdrOnSmartcardEstablishContextComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus, ++ LONG returnCode, ++ const EstablishContext_Return* ret); ++ typedef void (*psRdpdrOnSmartcardReleaseContextComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus, ++ LONG returnCode); ++ typedef void (*psRdpdrOnSmartcardIsValidContextComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus, ++ LONG returnCode); ++ typedef void (*psRdpdrOnSmartcardListReaderGroupsComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus, ++ LONG returnCode, ++ const ListReaderGroups_Return* ret); ++ typedef void (*psRdpdrOnSmartcardListReadersComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus, ++ LONG returnCode, ++ const ListReaders_Return* ret); ++ typedef void (*psRdpdrOnSmartcardGetStatusChangeComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus, ++ LONG returnCode, ++ const GetStatusChange_Return* ret); ++ typedef void (*psRdpdrOnSmartcardCancelComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus, ++ LONG returnCode); ++ typedef void (*psRdpdrOnSmartcardConnectComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus, ++ LONG returnCode, const Connect_Return* ret); ++ typedef void (*psRdpdrOnSmartcardReconnectComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus, ++ LONG returnCode, ++ const Reconnect_Return* ret); ++ typedef void (*psRdpdrOnSmartcardDisconnectComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus, ++ LONG returnCode); ++ typedef void (*psRdpdrOnSmartcardBeginTransactionComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus, ++ LONG returnCode); ++ typedef void (*psRdpdrOnSmartcardEndTransactionComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus, ++ LONG returnCode); ++ typedef void (*psRdpdrOnSmartcardStatusComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus, ++ LONG returnCode, const Status_Return* ret); ++ typedef void (*psRdpdrOnSmartcardTransmitComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus, ++ LONG returnCode, const Transmit_Return* ret); ++ typedef void (*psRdpdrOnSmartcardControlComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus, ++ LONG returnCode, const Control_Return* ret); ++ typedef void (*psRdpdrOnSmartcardGetAttribComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus, ++ LONG returnCode, ++ const GetAttrib_Return* ret); ++ typedef void (*psRdpdrOnSmartcardSetAttribComplete)(RdpdrServerContext* context, ++ void* callbackData, UINT32 ioStatus, ++ LONG returnCode); ++ + struct s_rdpdr_server_context + { + HANDLE vcm; +@@ -233,6 +345,107 @@ extern "C" + RDPDR_DTYP_SMARTCARD after \b ReceiveDeviceRemove */ + + rdpContext* rdpcontext; ++ ++ /*** New Smartcard APIs called by the server. ***/ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardEstablishContext ++ SmartcardEstablishContext; /**< Send SCardEstablishContext to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardReleaseContext ++ SmartcardReleaseContext; /**< Send SCardReleaseContext to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardIsValidContext ++ SmartcardIsValidContext; /**< Send SCardIsValidContext to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardListReaderGroups ++ SmartcardListReaderGroupsA; /**< Send SCardListReaderGroupsA to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardListReaderGroups ++ SmartcardListReaderGroupsW; /**< Send SCardListReaderGroupsW to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardListReaders ++ SmartcardListReadersA; /**< Send SCardListReadersA to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardListReaders ++ SmartcardListReadersW; /**< Send SCardListReadersW to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardGetStatusChangeA ++ SmartcardGetStatusChangeA; /**< Send SCardGetStatusChangeA to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardGetStatusChangeW ++ SmartcardGetStatusChangeW; /**< Send SCardGetStatusChangeW to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardCancel ++ SmartcardCancel; /**< Send SCardCancel to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardConnectA ++ SmartcardConnectA; /**< Send SCardConnectA to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardConnectW ++ SmartcardConnectW; /**< Send SCardConnectW to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardReconnect ++ SmartcardReconnect; /**< Send SCardReconnect to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardDisconnect ++ SmartcardDisconnect; /**< Send SCardDisconnect to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardBeginTransaction ++ SmartcardBeginTransaction; /**< Send SCardBeginTransaction to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardEndTransaction ++ SmartcardEndTransaction; /**< Send SCardEndTransaction to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardStatus ++ SmartcardStatusA; /**< Send SCardStatusA to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardStatus ++ SmartcardStatusW; /**< Send SCardStatusW to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardTransmit ++ SmartcardTransmit; /**< Send SCardTransmit to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardControl ++ SmartcardControl; /**< Send SCardControl to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardGetAttrib ++ SmartcardGetAttrib; /**< Send SCardGetAttrib to the client. */ ++ WINPR_ATTR_NODISCARD psRdpdrSmartcardSetAttrib ++ SmartcardSetAttrib; /**< Send SCardSetAttrib to the client. */ ++ ++ /*** New Smartcard callbacks registered by the server. ***/ ++ /* clang-format off */ ++ psRdpdrOnSmartcardEstablishContextComplete ++ OnSmartcardEstablishContextComplete; /**< Completion callback for ++ SmartcardEstablishContext. */ ++ psRdpdrOnSmartcardReleaseContextComplete ++ OnSmartcardReleaseContextComplete; /**< Completion callback for ++ SmartcardReleaseContext. */ ++ psRdpdrOnSmartcardIsValidContextComplete ++ OnSmartcardIsValidContextComplete; /**< Completion callback for ++ SmartcardIsValidContext. */ ++ psRdpdrOnSmartcardListReaderGroupsComplete ++ OnSmartcardListReaderGroupsComplete; /**< Completion callback for ++ SmartcardListReaderGroups. */ ++ psRdpdrOnSmartcardListReadersComplete ++ OnSmartcardListReadersComplete; /**< Completion callback for ++ SmartcardListReaders. */ ++ psRdpdrOnSmartcardGetStatusChangeComplete ++ OnSmartcardGetStatusChangeComplete; /**< Completion callback for ++ SmartcardGetStatusChange. */ ++ psRdpdrOnSmartcardCancelComplete ++ OnSmartcardCancelComplete; /**< Completion callback for ++ SmartcardCancel. */ ++ psRdpdrOnSmartcardConnectComplete ++ OnSmartcardConnectComplete; /**< Completion callback for ++ SmartcardConnect. */ ++ psRdpdrOnSmartcardReconnectComplete ++ OnSmartcardReconnectComplete; /**< Completion callback for ++ SmartcardReconnect. */ ++ psRdpdrOnSmartcardDisconnectComplete ++ OnSmartcardDisconnectComplete; /**< Completion callback for ++ SmartcardDisconnect. */ ++ psRdpdrOnSmartcardBeginTransactionComplete ++ OnSmartcardBeginTransactionComplete; /**< Completion callback for ++ SmartcardBeginTransaction. */ ++ psRdpdrOnSmartcardEndTransactionComplete ++ OnSmartcardEndTransactionComplete; /**< Completion callback for ++ SmartcardEndTransaction. */ ++ psRdpdrOnSmartcardStatusComplete ++ OnSmartcardStatusComplete; /**< Completion callback for ++ SmartcardStatus. */ ++ psRdpdrOnSmartcardTransmitComplete ++ OnSmartcardTransmitComplete; /**< Completion callback for ++ SmartcardTransmit. */ ++ psRdpdrOnSmartcardControlComplete ++ OnSmartcardControlComplete; /**< Completion callback for ++ SmartcardControl. */ ++ psRdpdrOnSmartcardGetAttribComplete ++ OnSmartcardGetAttribComplete; /**< Completion callback for ++ SmartcardGetAttrib. */ ++ psRdpdrOnSmartcardSetAttribComplete ++ OnSmartcardSetAttribComplete; /**< Completion callback for ++ SmartcardSetAttrib. */ ++ /* clang-format on */ + }; + + FREERDP_API void rdpdr_server_context_free(RdpdrServerContext* context); +@@ -241,6 +454,13 @@ extern "C" + WINPR_ATTR_NODISCARD + FREERDP_API RdpdrServerContext* rdpdr_server_context_new(HANDLE vcm); + ++ /** ++ * @brief Remove and free a pending IRP request from the context. ++ * @param context The RDPDR server context. ++ * @param completionId The completion ID of the IRP to discard. ++ * @since version 3.28 ++ */ ++ FREERDP_API void rdpdr_server_discard_request(RdpdrServerContext* context, UINT32 completionId); + #ifdef __cplusplus + } + #endif +diff --git a/libfreerdp/utils/test/CMakeLists.txt b/libfreerdp/utils/test/CMakeLists.txt +index 0c2bf8491d7f..eedd7b2599f6 100644 +--- a/libfreerdp/utils/test/CMakeLists.txt ++++ b/libfreerdp/utils/test/CMakeLists.txt +@@ -5,7 +5,9 @@ disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR}) + + set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +-set(${MODULE_PREFIX}_TESTS TestRingBuffer.c TestPodArrays.c TestEncodedTypes.c TestSmartcardPack.c) ++set(${MODULE_PREFIX}_TESTS TestRingBuffer.c TestPodArrays.c TestEncodedTypes.c TestSmartcardPack.c ++ TestSmartcardOperations.c ++) + + create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} ${${MODULE_PREFIX}_TESTS}) + +diff --git a/libfreerdp/utils/test/TestSmartcardOperations.c b/libfreerdp/utils/test/TestSmartcardOperations.c +new file mode 100644 +index 000000000000..4c885b13359c +--- /dev/null ++++ b/libfreerdp/utils/test/TestSmartcardOperations.c +@@ -0,0 +1,1445 @@ ++/** ++ * FreeRDP: A Remote Desktop Protocol Implementation ++ * ++ * Copyright 2026 Joan Torres Lopez ++ * ++ * 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 ++ ++static inline void fill_redir_context(REDIR_SCARDCONTEXT* ctx, SCARDCONTEXT val) ++{ ++ smartcard_scard_context_native_to_redir(ctx, val); ++} ++ ++static inline void fill_redir_handle(REDIR_SCARDHANDLE* handle, SCARDHANDLE val) ++{ ++ smartcard_scard_handle_native_to_redir(handle, val); ++} ++ ++static wStream* build_device_control_request(UINT32 ioControlCode, const void* inputBuffer, ++ size_t inputBufferLength) ++{ ++ const char* name = scard_get_ioctl_string(ioControlCode, TRUE); ++ const size_t totalLength = RDPDR_DEVICE_IO_CONTROL_REQ_HDR_LENGTH + inputBufferLength; ++ ++ wStream* s = Stream_New(NULL, totalLength); ++ if (!s) ++ { ++ (void)fprintf(stderr, "build_device_control_request(%s): Stream_New(%" PRIuz ") failed\n", ++ name, totalLength); ++ return NULL; ++ } ++ ++ UINT32 inputLength = WINPR_ASSERTING_INT_CAST(UINT32, inputBufferLength); ++ Stream_Write_UINT32(s, 2048); /* OutputBufferLength */ ++ Stream_Write_UINT32(s, inputLength); /* InputBufferLength */ ++ Stream_Write_UINT32(s, ioControlCode); /* IoControlCode */ ++ Stream_Zero(s, 20); /* Padding */ ++ ++ if (inputBufferLength > 0) ++ Stream_Write(s, inputBuffer, inputBufferLength); ++ ++ Stream_SealLength(s); ++ if (!Stream_SetPosition(s, 0)) ++ { ++ (void)fprintf(stderr, "build_device_control_request(%s): Stream_SetPosition failed\n", ++ name); ++ Stream_Release(s); ++ return NULL; ++ } ++ ++ return s; ++} ++ ++static BOOL test_request_roundtrip(const SMARTCARD_OPERATION* opIn, SMARTCARD_OPERATION* opOut) ++{ ++ const UINT32 ioControlCode = opIn->ioControlCode; ++ const char* name = scard_get_ioctl_string(ioControlCode, TRUE); ++ ++ wStream* sEnc = Stream_New(NULL, 4096); ++ if (!sEnc) ++ { ++ (void)fprintf(stderr, "%s: Stream_New for encode failed\n", name); ++ return FALSE; ++ } ++ ++ LONG status = smartcard_irp_device_control_encode_request(sEnc, opIn); ++ if (status != SCARD_S_SUCCESS) ++ { ++ (void)fprintf(stderr, "%s: encode_request failed with 0x%08" PRIX32 "\n", name, ++ (UINT32)status); ++ Stream_Release(sEnc); ++ return FALSE; ++ } ++ ++ wStream* sDec = ++ build_device_control_request(ioControlCode, Stream_Buffer(sEnc), Stream_Length(sEnc)); ++ Stream_Release(sEnc); ++ if (!sDec) ++ return FALSE; ++ ++ *opOut = (SMARTCARD_OPERATION)WINPR_C_ARRAY_INIT; ++ status = smartcard_irp_device_control_decode_request(sDec, 1, 0, opOut); ++ Stream_Release(sDec); ++ if (status != SCARD_S_SUCCESS) ++ { ++ (void)fprintf(stderr, "%s: decode_request failed with 0x%08" PRIX32 "\n", name, ++ (UINT32)status); ++ return FALSE; ++ } ++ ++ if (opOut->ioControlCode != ioControlCode) ++ { ++ (void)fprintf(stderr, "%s: ioControlCode mismatch: 0x%08" PRIX32 " != 0x%08" PRIX32 "\n", ++ name, opOut->ioControlCode, ioControlCode); ++ smartcard_operation_free(opOut, FALSE); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static inline BOOL check_field(const char* name, const char* field, UINT32 a, UINT32 b) ++{ ++ if (a != b) ++ { ++ (void)fprintf(stderr, "%s: %s mismatch: 0x%" PRIX32 " != 0x%" PRIX32 "\n", name, field, a, ++ b); ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++static inline BOOL check_bytes(const char* name, const char* field, const void* a, const void* b, ++ size_t len) ++{ ++ if (!a || !b || memcmp(a, b, len) != 0) ++ { ++ (void)fprintf(stderr, "%s: %s mismatch (%" PRIuz " bytes)\n", name, field, len); ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++static inline BOOL check_redir_context(const char* name, const REDIR_SCARDCONTEXT* a, ++ const REDIR_SCARDCONTEXT* b) ++{ ++ return check_field(name, "hContext.cbContext", a->cbContext, b->cbContext) && ++ check_bytes(name, "hContext.pbContext", a->pbContext, b->pbContext, a->cbContext); ++} ++ ++static inline BOOL check_redir_handle(const char* name, const REDIR_SCARDHANDLE* a, ++ const REDIR_SCARDHANDLE* b) ++{ ++ return check_field(name, "hCard.cbHandle", a->cbHandle, b->cbHandle) && ++ check_bytes(name, "hCard.pbHandle", a->pbHandle, b->pbHandle, a->cbHandle); ++} ++ ++static BOOL test_establish_context_encode_decode_request(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_ESTABLISHCONTEXT; ++ opIn.call.establishContext.dwScope = SCARD_SCOPE_SYSTEM; ++ ++ if (!test_request_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_field("EstablishContext", "dwScope", opIn.call.establishContext.dwScope, ++ opOut.call.establishContext.dwScope)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_release_context_encode_decode_request(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_RELEASECONTEXT; ++ fill_redir_context(&opIn.call.context.handles.hContext, 0x1234); ++ ++ if (!test_request_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_redir_context("ReleaseContext", &opIn.call.context.handles.hContext, ++ &opOut.call.context.handles.hContext)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_is_valid_context_encode_decode_request(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_ISVALIDCONTEXT; ++ fill_redir_context(&opIn.call.context.handles.hContext, 0x5678); ++ ++ if (!test_request_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_redir_context("IsValidContext", &opIn.call.context.handles.hContext, ++ &opOut.call.context.handles.hContext)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_list_reader_groups_encode_decode_request_impl(UINT32 ioControlCode) ++{ ++ BOOL success = FALSE; ++ const char* n = scard_get_ioctl_string(ioControlCode, TRUE); ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = ioControlCode; ++ fill_redir_context(&opIn.call.listReaderGroups.handles.hContext, 0x1234); ++ opIn.call.listReaderGroups.fmszGroupsIsNULL = 0; ++ opIn.call.listReaderGroups.cchGroups = SCARD_AUTOALLOCATE; ++ ++ if (!test_request_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_redir_context(n, &opIn.call.listReaderGroups.handles.hContext, ++ &opOut.call.listReaderGroups.handles.hContext)) ++ goto out; ++ if (!check_field(n, "fmszGroupsIsNULL", opIn.call.listReaderGroups.fmszGroupsIsNULL, ++ opOut.call.listReaderGroups.fmszGroupsIsNULL)) ++ goto out; ++ if (!check_field(n, "cchGroups", opIn.call.listReaderGroups.cchGroups, ++ opOut.call.listReaderGroups.cchGroups)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_list_reader_groups_a_encode_decode_request(void) ++{ ++ return test_list_reader_groups_encode_decode_request_impl(SCARD_IOCTL_LISTREADERGROUPSA); ++} ++ ++static BOOL test_list_reader_groups_w_encode_decode_request(void) ++{ ++ return test_list_reader_groups_encode_decode_request_impl(SCARD_IOCTL_LISTREADERGROUPSW); ++} ++ ++static BOOL test_list_readers_encode_decode_request_impl(UINT32 ioControlCode) ++{ ++ BOOL success = FALSE; ++ const char* n = scard_get_ioctl_string(ioControlCode, TRUE); ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = ioControlCode; ++ fill_redir_context(&opIn.call.listReaders.handles.hContext, 0x1234); ++ opIn.call.listReaders.fmszReadersIsNULL = 0; ++ opIn.call.listReaders.cchReaders = SCARD_AUTOALLOCATE; ++ ++ if (!test_request_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_redir_context(n, &opIn.call.listReaders.handles.hContext, ++ &opOut.call.listReaders.handles.hContext)) ++ goto out; ++ if (!check_field(n, "cchReaders", opIn.call.listReaders.cchReaders, ++ opOut.call.listReaders.cchReaders)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_list_readers_a_encode_decode_request(void) ++{ ++ return test_list_readers_encode_decode_request_impl(SCARD_IOCTL_LISTREADERSA); ++} ++ ++static BOOL test_list_readers_w_encode_decode_request(void) ++{ ++ return test_list_readers_encode_decode_request_impl(SCARD_IOCTL_LISTREADERSW); ++} ++ ++static BOOL test_cancel_encode_decode_request(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_CANCEL; ++ fill_redir_context(&opIn.call.context.handles.hContext, 0xABCD); ++ ++ if (!test_request_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_redir_context("Cancel", &opIn.call.context.handles.hContext, ++ &opOut.call.context.handles.hContext)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_connect_a_encode_decode_request(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_CONNECTA; ++ fill_redir_context(&opIn.call.connectA.Common.handles.hContext, 0x1234); ++ opIn.call.connectA.Common.dwShareMode = SCARD_SHARE_SHARED; ++ opIn.call.connectA.Common.dwPreferredProtocols = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; ++ opIn.call.connectA.szReader = _strdup("TestReader"); ++ ++ if (!test_request_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_redir_context("ConnectA", &opIn.call.connectA.Common.handles.hContext, ++ &opOut.call.connectA.Common.handles.hContext)) ++ goto out; ++ if (!check_field("ConnectA", "dwShareMode", opIn.call.connectA.Common.dwShareMode, ++ opOut.call.connectA.Common.dwShareMode)) ++ goto out; ++ if (!check_field("ConnectA", "dwPreferredProtocols", ++ opIn.call.connectA.Common.dwPreferredProtocols, ++ opOut.call.connectA.Common.dwPreferredProtocols)) ++ goto out; ++ if (!check_bytes("ConnectA", "szReader", opIn.call.connectA.szReader, ++ opOut.call.connectA.szReader, strlen(opIn.call.connectA.szReader) + 1)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_connect_w_encode_decode_request(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_CONNECTW; ++ fill_redir_context(&opIn.call.connectW.Common.handles.hContext, 0x1234); ++ opIn.call.connectW.Common.dwShareMode = SCARD_SHARE_SHARED; ++ opIn.call.connectW.Common.dwPreferredProtocols = SCARD_PROTOCOL_T0; ++ opIn.call.connectW.szReader = ConvertUtf8ToWCharAlloc("TestReader", NULL); ++ ++ if (!test_request_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_redir_context("ConnectW", &opIn.call.connectW.Common.handles.hContext, ++ &opOut.call.connectW.Common.handles.hContext)) ++ goto out; ++ if (!check_field("ConnectW", "dwShareMode", opIn.call.connectW.Common.dwShareMode, ++ opOut.call.connectW.Common.dwShareMode)) ++ goto out; ++ if (!check_bytes("ConnectW", "szReader", opIn.call.connectW.szReader, ++ opOut.call.connectW.szReader, ++ (_wcslen(opIn.call.connectW.szReader) + 1) * sizeof(WCHAR))) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_reconnect_encode_decode_request(void) ++{ ++ BOOL success = FALSE; ++ const char* n = "Reconnect"; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_RECONNECT; ++ fill_redir_context(&opIn.call.reconnect.handles.hContext, 0x1234); ++ fill_redir_handle(&opIn.call.reconnect.handles.hCard, 0x5678); ++ opIn.call.reconnect.dwShareMode = SCARD_SHARE_EXCLUSIVE; ++ opIn.call.reconnect.dwPreferredProtocols = SCARD_PROTOCOL_T1; ++ opIn.call.reconnect.dwInitialization = SCARD_LEAVE_CARD; ++ ++ if (!test_request_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_redir_context(n, &opIn.call.reconnect.handles.hContext, ++ &opOut.call.reconnect.handles.hContext)) ++ goto out; ++ if (!check_redir_handle(n, &opIn.call.reconnect.handles.hCard, ++ &opOut.call.reconnect.handles.hCard)) ++ goto out; ++ if (!check_field(n, "dwShareMode", opIn.call.reconnect.dwShareMode, ++ opOut.call.reconnect.dwShareMode)) ++ goto out; ++ if (!check_field(n, "dwPreferredProtocols", opIn.call.reconnect.dwPreferredProtocols, ++ opOut.call.reconnect.dwPreferredProtocols)) ++ goto out; ++ if (!check_field(n, "dwInitialization", opIn.call.reconnect.dwInitialization, ++ opOut.call.reconnect.dwInitialization)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_hcard_and_disposition_encode_decode_request_impl(UINT32 ioControlCode, ++ DWORD dwDisposition) ++{ ++ BOOL success = FALSE; ++ const char* n = scard_get_ioctl_string(ioControlCode, TRUE); ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = ioControlCode; ++ fill_redir_context(&opIn.call.hCardAndDisposition.handles.hContext, 0x1234); ++ fill_redir_handle(&opIn.call.hCardAndDisposition.handles.hCard, 0x5678); ++ opIn.call.hCardAndDisposition.dwDisposition = dwDisposition; ++ ++ if (!test_request_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_redir_context(n, &opIn.call.hCardAndDisposition.handles.hContext, ++ &opOut.call.hCardAndDisposition.handles.hContext)) ++ goto out; ++ if (!check_redir_handle(n, &opIn.call.hCardAndDisposition.handles.hCard, ++ &opOut.call.hCardAndDisposition.handles.hCard)) ++ goto out; ++ if (!check_field(n, "dwDisposition", opIn.call.hCardAndDisposition.dwDisposition, ++ opOut.call.hCardAndDisposition.dwDisposition)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_disconnect_encode_decode_request(void) ++{ ++ return test_hcard_and_disposition_encode_decode_request_impl(SCARD_IOCTL_DISCONNECT, ++ SCARD_LEAVE_CARD); ++} ++ ++static BOOL test_begin_transaction_encode_decode_request(void) ++{ ++ return test_hcard_and_disposition_encode_decode_request_impl(SCARD_IOCTL_BEGINTRANSACTION, ++ SCARD_LEAVE_CARD); ++} ++ ++static BOOL test_end_transaction_encode_decode_request(void) ++{ ++ return test_hcard_and_disposition_encode_decode_request_impl(SCARD_IOCTL_ENDTRANSACTION, ++ SCARD_RESET_CARD); ++} ++ ++static BOOL test_status_encode_decode_request_impl(UINT32 ioControlCode) ++{ ++ BOOL success = FALSE; ++ const char* n = scard_get_ioctl_string(ioControlCode, TRUE); ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = ioControlCode; ++ fill_redir_context(&opIn.call.status.handles.hContext, 0x1234); ++ fill_redir_handle(&opIn.call.status.handles.hCard, 0x5678); ++ opIn.call.status.fmszReaderNamesIsNULL = 0; ++ opIn.call.status.cchReaderLen = SCARD_AUTOALLOCATE; ++ opIn.call.status.cbAtrLen = 36; ++ ++ if (!test_request_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_redir_context(n, &opIn.call.status.handles.hContext, ++ &opOut.call.status.handles.hContext)) ++ goto out; ++ if (!check_redir_handle(n, &opIn.call.status.handles.hCard, &opOut.call.status.handles.hCard)) ++ goto out; ++ if (!check_field(n, "fmszReaderNamesIsNULL", opIn.call.status.fmszReaderNamesIsNULL, ++ opOut.call.status.fmszReaderNamesIsNULL)) ++ goto out; ++ if (!check_field(n, "cchReaderLen", opIn.call.status.cchReaderLen, ++ opOut.call.status.cchReaderLen)) ++ goto out; ++ if (!check_field(n, "cbAtrLen", opIn.call.status.cbAtrLen, opOut.call.status.cbAtrLen)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_status_a_encode_decode_request(void) ++{ ++ return test_status_encode_decode_request_impl(SCARD_IOCTL_STATUSA); ++} ++ ++static BOOL test_status_w_encode_decode_request(void) ++{ ++ return test_status_encode_decode_request_impl(SCARD_IOCTL_STATUSW); ++} ++ ++static BOOL test_transmit_encode_decode_request(void) ++{ ++ BOOL success = FALSE; ++ BYTE sendBuf[] = { 0x00, 0xA4, 0x04, 0x00, 0x07 }; ++ SCARD_IO_REQUEST pioSendPci = { .dwProtocol = SCARD_PROTOCOL_T0, .cbPciLength = 8 }; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_TRANSMIT; ++ fill_redir_context(&opIn.call.transmit.handles.hContext, 0x1234); ++ fill_redir_handle(&opIn.call.transmit.handles.hCard, 0x5678); ++ opIn.call.transmit.pioSendPci = &pioSendPci; ++ opIn.call.transmit.cbSendLength = sizeof(sendBuf); ++ opIn.call.transmit.pbSendBuffer = sendBuf; ++ opIn.call.transmit.fpbRecvBufferIsNULL = 0; ++ opIn.call.transmit.cbRecvLength = 256; ++ ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ if (!test_request_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_redir_context("Transmit", &opIn.call.transmit.handles.hContext, ++ &opOut.call.transmit.handles.hContext)) ++ goto out; ++ if (!check_redir_handle("Transmit", &opIn.call.transmit.handles.hCard, ++ &opOut.call.transmit.handles.hCard)) ++ goto out; ++ if (!check_field("Transmit", "cbSendLength", opIn.call.transmit.cbSendLength, ++ opOut.call.transmit.cbSendLength)) ++ goto out; ++ if (!check_bytes("Transmit", "pbSendBuffer", opIn.call.transmit.pbSendBuffer, ++ opOut.call.transmit.pbSendBuffer, opIn.call.transmit.cbSendLength)) ++ goto out; ++ if (!check_field("Transmit", "fpbRecvBufferIsNULL", ++ (UINT32)opIn.call.transmit.fpbRecvBufferIsNULL, ++ (UINT32)opOut.call.transmit.fpbRecvBufferIsNULL)) ++ goto out; ++ if (!check_field("Transmit", "cbRecvLength", opIn.call.transmit.cbRecvLength, ++ opOut.call.transmit.cbRecvLength)) ++ goto out; ++ if (!check_field("Transmit", "pioSendPci.dwProtocol", opIn.call.transmit.pioSendPci->dwProtocol, ++ opOut.call.transmit.pioSendPci->dwProtocol)) ++ goto out; ++ ++ success = TRUE; ++out: ++ opIn.call.transmit.pioSendPci = NULL; ++ opIn.call.transmit.pbSendBuffer = NULL; ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_control_encode_decode_request(void) ++{ ++ BOOL success = FALSE; ++ BYTE inBuf[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_CONTROL; ++ fill_redir_context(&opIn.call.control.handles.hContext, 0x1234); ++ fill_redir_handle(&opIn.call.control.handles.hCard, 0x5678); ++ opIn.call.control.dwControlCode = 0x42000001; ++ opIn.call.control.cbInBufferSize = sizeof(inBuf); ++ opIn.call.control.pvInBuffer = inBuf; ++ opIn.call.control.fpvOutBufferIsNULL = 0; ++ opIn.call.control.cbOutBufferSize = 256; ++ ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ if (!test_request_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_redir_context("Control", &opIn.call.control.handles.hContext, ++ &opOut.call.control.handles.hContext)) ++ goto out; ++ if (!check_redir_handle("Control", &opIn.call.control.handles.hCard, ++ &opOut.call.control.handles.hCard)) ++ goto out; ++ if (!check_field("Control", "dwControlCode", opIn.call.control.dwControlCode, ++ opOut.call.control.dwControlCode)) ++ goto out; ++ if (!check_field("Control", "cbInBufferSize", opIn.call.control.cbInBufferSize, ++ opOut.call.control.cbInBufferSize)) ++ goto out; ++ if (!check_bytes("Control", "pvInBuffer", opIn.call.control.pvInBuffer, ++ opOut.call.control.pvInBuffer, opIn.call.control.cbInBufferSize)) ++ goto out; ++ if (!check_field("Control", "cbOutBufferSize", opIn.call.control.cbOutBufferSize, ++ opOut.call.control.cbOutBufferSize)) ++ goto out; ++ ++ success = TRUE; ++out: ++ opIn.call.control.pvInBuffer = NULL; ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_get_attrib_encode_decode_request(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_GETATTRIB; ++ fill_redir_context(&opIn.call.getAttrib.handles.hContext, 0x1234); ++ fill_redir_handle(&opIn.call.getAttrib.handles.hCard, 0x5678); ++ opIn.call.getAttrib.dwAttrId = SCARD_ATTR_ATR_STRING; ++ opIn.call.getAttrib.fpbAttrIsNULL = 0; ++ opIn.call.getAttrib.cbAttrLen = 36; ++ ++ if (!test_request_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_redir_context("GetAttrib", &opIn.call.getAttrib.handles.hContext, ++ &opOut.call.getAttrib.handles.hContext)) ++ goto out; ++ if (!check_redir_handle("GetAttrib", &opIn.call.getAttrib.handles.hCard, ++ &opOut.call.getAttrib.handles.hCard)) ++ goto out; ++ if (!check_field("GetAttrib", "dwAttrId", opIn.call.getAttrib.dwAttrId, ++ opOut.call.getAttrib.dwAttrId)) ++ goto out; ++ if (!check_field("GetAttrib", "cbAttrLen", opIn.call.getAttrib.cbAttrLen, ++ opOut.call.getAttrib.cbAttrLen)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_set_attrib_encode_decode_request(void) ++{ ++ BOOL success = FALSE; ++ BYTE attrBuf[] = { 0x3B, 0x00 }; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_SETATTRIB; ++ fill_redir_context(&opIn.call.setAttrib.handles.hContext, 0x1234); ++ fill_redir_handle(&opIn.call.setAttrib.handles.hCard, 0x5678); ++ opIn.call.setAttrib.dwAttrId = SCARD_ATTR_ATR_STRING; ++ opIn.call.setAttrib.cbAttrLen = sizeof(attrBuf); ++ opIn.call.setAttrib.pbAttr = attrBuf; ++ ++ if (!test_request_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_redir_context("SetAttrib", &opIn.call.setAttrib.handles.hContext, ++ &opOut.call.setAttrib.handles.hContext)) ++ goto out; ++ if (!check_redir_handle("SetAttrib", &opIn.call.setAttrib.handles.hCard, ++ &opOut.call.setAttrib.handles.hCard)) ++ goto out; ++ if (!check_field("SetAttrib", "dwAttrId", opIn.call.setAttrib.dwAttrId, ++ opOut.call.setAttrib.dwAttrId)) ++ goto out; ++ if (!check_field("SetAttrib", "cbAttrLen", opIn.call.setAttrib.cbAttrLen, ++ opOut.call.setAttrib.cbAttrLen)) ++ goto out; ++ if (!check_bytes("SetAttrib", "pbAttr", opIn.call.setAttrib.pbAttr, opOut.call.setAttrib.pbAttr, ++ opIn.call.setAttrib.cbAttrLen)) ++ goto out; ++ ++ success = TRUE; ++out: ++ opIn.call.setAttrib.pbAttr = NULL; ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_encode_decode_requests(void) ++{ ++ BOOL success = TRUE; ++ ++ if (!test_establish_context_encode_decode_request()) ++ success = FALSE; ++ if (!test_release_context_encode_decode_request()) ++ success = FALSE; ++ if (!test_is_valid_context_encode_decode_request()) ++ success = FALSE; ++ if (!test_list_reader_groups_a_encode_decode_request()) ++ success = FALSE; ++ if (!test_list_reader_groups_w_encode_decode_request()) ++ success = FALSE; ++ if (!test_list_readers_a_encode_decode_request()) ++ success = FALSE; ++ if (!test_list_readers_w_encode_decode_request()) ++ success = FALSE; ++ if (!test_cancel_encode_decode_request()) ++ success = FALSE; ++ if (!test_connect_a_encode_decode_request()) ++ success = FALSE; ++ if (!test_connect_w_encode_decode_request()) ++ success = FALSE; ++ if (!test_reconnect_encode_decode_request()) ++ success = FALSE; ++ if (!test_disconnect_encode_decode_request()) ++ success = FALSE; ++ if (!test_begin_transaction_encode_decode_request()) ++ success = FALSE; ++ if (!test_end_transaction_encode_decode_request()) ++ success = FALSE; ++ if (!test_status_a_encode_decode_request()) ++ success = FALSE; ++ if (!test_status_w_encode_decode_request()) ++ success = FALSE; ++ if (!test_transmit_encode_decode_request()) ++ success = FALSE; ++ if (!test_control_encode_decode_request()) ++ success = FALSE; ++ if (!test_get_attrib_encode_decode_request()) ++ success = FALSE; ++ if (!test_set_attrib_encode_decode_request()) ++ success = FALSE; ++ ++ return success; ++} ++ ++static LONG encode_response_payload(wStream* s, UINT32 ioControlCode, ++ const SMARTCARD_OPERATION* opIn) ++{ ++ switch (ioControlCode) ++ { ++ case SCARD_IOCTL_ESTABLISHCONTEXT: ++ return smartcard_pack_establish_context_return(s, &opIn->ret.establishContext); ++ case SCARD_IOCTL_RECONNECT: ++ return smartcard_pack_reconnect_return(s, &opIn->ret.reconnect); ++ case SCARD_IOCTL_CONNECTA: ++ case SCARD_IOCTL_CONNECTW: ++ return smartcard_pack_connect_return(s, &opIn->ret.connect); ++ case SCARD_IOCTL_CONTROL: ++ return smartcard_pack_control_return(s, &opIn->ret.control); ++ case SCARD_IOCTL_TRANSMIT: ++ return smartcard_pack_transmit_return(s, &opIn->ret.transmit); ++ case SCARD_IOCTL_GETATTRIB: ++ return smartcard_pack_get_attrib_return(s, &opIn->ret.getAttrib, 0, ++ opIn->ret.getAttrib.cbAttrLen); ++ case SCARD_IOCTL_GETSTATUSCHANGEA: ++ return smartcard_pack_get_status_change_return(s, &opIn->ret.getStatusChange, FALSE); ++ case SCARD_IOCTL_GETSTATUSCHANGEW: ++ return smartcard_pack_get_status_change_return(s, &opIn->ret.getStatusChange, TRUE); ++ case SCARD_IOCTL_LISTREADERGROUPSA: ++ return smartcard_pack_list_reader_groups_return(s, &opIn->ret.listReaders, FALSE); ++ case SCARD_IOCTL_LISTREADERGROUPSW: ++ return smartcard_pack_list_reader_groups_return(s, &opIn->ret.listReaders, TRUE); ++ case SCARD_IOCTL_LISTREADERSA: ++ return smartcard_pack_list_readers_return(s, &opIn->ret.listReaders, FALSE); ++ case SCARD_IOCTL_LISTREADERSW: ++ return smartcard_pack_list_readers_return(s, &opIn->ret.listReaders, TRUE); ++ case SCARD_IOCTL_STATUSA: ++ return smartcard_pack_status_return(s, &opIn->ret.status, FALSE); ++ case SCARD_IOCTL_STATUSW: ++ return smartcard_pack_status_return(s, &opIn->ret.status, TRUE); ++ case SCARD_IOCTL_RELEASECONTEXT: ++ case SCARD_IOCTL_ISVALIDCONTEXT: ++ case SCARD_IOCTL_CANCEL: ++ case SCARD_IOCTL_DISCONNECT: ++ case SCARD_IOCTL_BEGINTRANSACTION: ++ case SCARD_IOCTL_ENDTRANSACTION: ++ case SCARD_IOCTL_SETATTRIB: ++ return SCARD_S_SUCCESS; ++ default: ++ return SCARD_F_INTERNAL_ERROR; ++ } ++} ++ ++static wStream* build_device_control_response(UINT32 ioControlCode, const SMARTCARD_OPERATION* opIn) ++{ ++ const char* name = scard_get_ioctl_string(ioControlCode, TRUE); ++ ++ wStream* payload = Stream_New(NULL, 4096); ++ if (!payload) ++ { ++ (void)fprintf(stderr, "%s: Stream_New for payload failed\n", name); ++ return NULL; ++ } ++ ++ LONG rc = encode_response_payload(payload, ioControlCode, opIn); ++ if (rc != SCARD_S_SUCCESS) ++ { ++ (void)fprintf(stderr, "%s: encode_response_payload failed with 0x%08" PRIX32 "\n", name, ++ (UINT32)rc); ++ Stream_Release(payload); ++ return NULL; ++ } ++ Stream_SealLength(payload); ++ ++ wStream* s = Stream_New(NULL, 256 + Stream_Length(payload)); ++ if (!s) ++ { ++ (void)fprintf(stderr, "%s: Stream_New for response failed\n", name); ++ Stream_Release(payload); ++ return NULL; ++ } ++ ++ Stream_Seek(s, 4); /* Placeholder for outputBufferLength */ ++ const size_t headerPos = Stream_GetPosition(s); ++ Stream_Seek(s, SMARTCARD_COMMON_TYPE_HEADER_LENGTH + ++ SMARTCARD_PRIVATE_TYPE_HEADER_LENGTH); /* Placeholder for headers */ ++ Stream_Write_INT32(s, opIn->returnCode); ++ Stream_Write(s, Stream_Buffer(payload), Stream_Length(payload)); ++ Stream_Release(payload); ++ ++ const size_t payloadSize = Stream_GetPosition(s) - headerPos; ++ const size_t objectBufferLength = ++ payloadSize - SMARTCARD_COMMON_TYPE_HEADER_LENGTH - SMARTCARD_PRIVATE_TYPE_HEADER_LENGTH; ++ ++ WINPR_UNUSED(smartcard_pack_write_size_align(s, payloadSize, 8)); ++ ++ const size_t endPos = Stream_GetPosition(s); ++ const size_t outputBufferLength = endPos - headerPos; ++ ++ Stream_SetPosition(s, 0); ++ UINT32 outputLength = WINPR_ASSERTING_INT_CAST(UINT32, outputBufferLength); ++ UINT32 objectLength = WINPR_ASSERTING_INT_CAST(UINT32, objectBufferLength); ++ Stream_Write_UINT32(s, outputLength); ++ smartcard_pack_common_type_header(s); ++ smartcard_pack_private_type_header(s, objectLength); ++ ++ Stream_SetPosition(s, endPos); ++ Stream_SealLength(s); ++ if (!Stream_SetPosition(s, 0)) ++ { ++ (void)fprintf(stderr, "%s: Stream_SetPosition failed\n", name); ++ Stream_Release(s); ++ return NULL; ++ } ++ return s; ++} ++ ++static BOOL test_response_roundtrip(const SMARTCARD_OPERATION* opIn, SMARTCARD_OPERATION* opOut) ++{ ++ const UINT32 ioControlCode = opIn->ioControlCode; ++ const char* name = scard_get_ioctl_string(ioControlCode, TRUE); ++ ++ wStream* s = build_device_control_response(ioControlCode, opIn); ++ if (!s) ++ return FALSE; ++ ++ *opOut = (SMARTCARD_OPERATION)WINPR_C_ARRAY_INIT; ++ LONG status = smartcard_irp_device_control_decode_response(s, ioControlCode, opOut); ++ Stream_Release(s); ++ ++ if (status != SCARD_S_SUCCESS) ++ { ++ (void)fprintf(stderr, "%s: decode_response failed with 0x%08" PRIX32 "\n", name, ++ (UINT32)status); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static BOOL test_establish_context_decode_response(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_ESTABLISHCONTEXT; ++ fill_redir_context(&opIn.ret.establishContext.hContext, 0xABCD); ++ opIn.returnCode = SCARD_S_SUCCESS; ++ ++ if (!test_response_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_redir_context("EstablishContext", &opIn.ret.establishContext.hContext, ++ &opOut.ret.establishContext.hContext)) ++ goto out; ++ if (!check_field("EstablishContext", "returnCode", (UINT32)opIn.returnCode, ++ (UINT32)opOut.returnCode)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_release_context_decode_response(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_RELEASECONTEXT; ++ opIn.returnCode = SCARD_S_SUCCESS; ++ ++ if (!test_response_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_field("ReleaseContext", "returnCode", (UINT32)opIn.returnCode, ++ (UINT32)opOut.returnCode)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_is_valid_context_decode_response(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_ISVALIDCONTEXT; ++ opIn.returnCode = SCARD_S_SUCCESS; ++ ++ if (!test_response_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_field("IsValidContext", "returnCode", (UINT32)opIn.returnCode, ++ (UINT32)opOut.returnCode)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_list_reader_groups_decode_response_impl(UINT32 ioControlCode) ++{ ++ BOOL success = FALSE; ++ const char* n = scard_get_ioctl_string(ioControlCode, TRUE); ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = ioControlCode; ++ opIn.returnCode = SCARD_S_SUCCESS; ++ BYTE groups[] = { 'G', 'r', 'p', '\0', '\0' }; ++ opIn.ret.listReaders.cBytes = sizeof(groups); ++ opIn.ret.listReaders.msz = groups; ++ ++ if (!test_response_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_field(n, "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode)) ++ goto out; ++ if (!check_field(n, "cBytes", opIn.ret.listReaders.cBytes, opOut.ret.listReaders.cBytes)) ++ goto out; ++ if (!check_bytes(n, "msz", opIn.ret.listReaders.msz, opOut.ret.listReaders.msz, ++ opIn.ret.listReaders.cBytes)) ++ goto out; ++ ++ success = TRUE; ++out: ++ opIn.ret.listReaders.msz = NULL; ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_list_reader_groups_a_decode_response(void) ++{ ++ return test_list_reader_groups_decode_response_impl(SCARD_IOCTL_LISTREADERGROUPSA); ++} ++ ++static BOOL test_list_reader_groups_w_decode_response(void) ++{ ++ return test_list_reader_groups_decode_response_impl(SCARD_IOCTL_LISTREADERGROUPSW); ++} ++ ++static BOOL test_list_readers_decode_response_impl(UINT32 ioControlCode) ++{ ++ BOOL success = FALSE; ++ const char* n = scard_get_ioctl_string(ioControlCode, TRUE); ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = ioControlCode; ++ opIn.returnCode = SCARD_S_SUCCESS; ++ BYTE readers[] = { 'R', 'd', 'r', '\0', '\0' }; ++ opIn.ret.listReaders.cBytes = sizeof(readers); ++ opIn.ret.listReaders.msz = readers; ++ ++ if (!test_response_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_field(n, "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode)) ++ goto out; ++ if (!check_field(n, "cBytes", opIn.ret.listReaders.cBytes, opOut.ret.listReaders.cBytes)) ++ goto out; ++ if (!check_bytes(n, "msz", opIn.ret.listReaders.msz, opOut.ret.listReaders.msz, ++ opIn.ret.listReaders.cBytes)) ++ goto out; ++ ++ success = TRUE; ++out: ++ opIn.ret.listReaders.msz = NULL; ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_list_readers_a_decode_response(void) ++{ ++ return test_list_readers_decode_response_impl(SCARD_IOCTL_LISTREADERSA); ++} ++ ++static BOOL test_list_readers_w_decode_response(void) ++{ ++ return test_list_readers_decode_response_impl(SCARD_IOCTL_LISTREADERSW); ++} ++ ++static BOOL test_get_status_change_decode_response_impl(UINT32 ioControlCode) ++{ ++ BOOL success = FALSE; ++ const char* n = scard_get_ioctl_string(ioControlCode, TRUE); ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = ioControlCode; ++ opIn.returnCode = SCARD_S_SUCCESS; ++ ReaderState_Return state = WINPR_C_ARRAY_INIT; ++ state.dwCurrentState = SCARD_STATE_PRESENT; ++ state.dwEventState = SCARD_STATE_CHANGED; ++ state.cbAtr = 2; ++ state.rgbAtr[0] = 0x3B; ++ state.rgbAtr[1] = 0x00; ++ opIn.ret.getStatusChange.cReaders = 1; ++ opIn.ret.getStatusChange.rgReaderStates = &state; ++ ++ if (!test_response_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_field(n, "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode)) ++ goto out; ++ if (!check_field(n, "cReaders", opIn.ret.getStatusChange.cReaders, ++ opOut.ret.getStatusChange.cReaders)) ++ goto out; ++ if (!check_field(n, "dwCurrentState", state.dwCurrentState, ++ opOut.ret.getStatusChange.rgReaderStates[0].dwCurrentState)) ++ goto out; ++ if (!check_field(n, "dwEventState", state.dwEventState, ++ opOut.ret.getStatusChange.rgReaderStates[0].dwEventState)) ++ goto out; ++ if (!check_field(n, "cbAtr", state.cbAtr, opOut.ret.getStatusChange.rgReaderStates[0].cbAtr)) ++ goto out; ++ if (!check_bytes(n, "rgbAtr", state.rgbAtr, opOut.ret.getStatusChange.rgReaderStates[0].rgbAtr, ++ sizeof(state.rgbAtr))) ++ goto out; ++ ++ success = TRUE; ++out: ++ opIn.ret.getStatusChange.rgReaderStates = NULL; ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_get_status_change_a_decode_response(void) ++{ ++ return test_get_status_change_decode_response_impl(SCARD_IOCTL_GETSTATUSCHANGEA); ++} ++ ++static BOOL test_get_status_change_w_decode_response(void) ++{ ++ return test_get_status_change_decode_response_impl(SCARD_IOCTL_GETSTATUSCHANGEW); ++} ++ ++static BOOL test_cancel_decode_response(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_CANCEL; ++ opIn.returnCode = SCARD_S_SUCCESS; ++ ++ if (!test_response_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_field("Cancel", "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_connect_decode_response_impl(UINT32 ioControlCode) ++{ ++ BOOL success = FALSE; ++ const char* n = scard_get_ioctl_string(ioControlCode, TRUE); ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = ioControlCode; ++ opIn.returnCode = SCARD_S_SUCCESS; ++ fill_redir_context(&opIn.ret.connect.hContext, 0x1234); ++ fill_redir_handle(&opIn.ret.connect.hCard, 0x5678); ++ opIn.ret.connect.dwActiveProtocol = SCARD_PROTOCOL_T0; ++ ++ if (!test_response_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_field(n, "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode)) ++ goto out; ++ if (!check_redir_context(n, &opIn.ret.connect.hContext, &opOut.ret.connect.hContext)) ++ goto out; ++ if (!check_redir_handle(n, &opIn.ret.connect.hCard, &opOut.ret.connect.hCard)) ++ goto out; ++ if (!check_field(n, "dwActiveProtocol", opIn.ret.connect.dwActiveProtocol, ++ opOut.ret.connect.dwActiveProtocol)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_connect_a_decode_response(void) ++{ ++ return test_connect_decode_response_impl(SCARD_IOCTL_CONNECTA); ++} ++ ++static BOOL test_connect_w_decode_response(void) ++{ ++ return test_connect_decode_response_impl(SCARD_IOCTL_CONNECTW); ++} ++ ++static BOOL test_reconnect_decode_response(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_RECONNECT; ++ opIn.returnCode = SCARD_S_SUCCESS; ++ opIn.ret.reconnect.dwActiveProtocol = SCARD_PROTOCOL_T1; ++ ++ if (!test_response_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_field("Reconnect", "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode)) ++ goto out; ++ if (!check_field("Reconnect", "dwActiveProtocol", opIn.ret.reconnect.dwActiveProtocol, ++ opOut.ret.reconnect.dwActiveProtocol)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_disconnect_decode_response(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_DISCONNECT; ++ opIn.returnCode = SCARD_S_SUCCESS; ++ ++ if (!test_response_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_field("Disconnect", "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_begin_transaction_decode_response(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_BEGINTRANSACTION; ++ opIn.returnCode = SCARD_S_SUCCESS; ++ ++ if (!test_response_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_field("BeginTransaction", "returnCode", (UINT32)opIn.returnCode, ++ (UINT32)opOut.returnCode)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_end_transaction_decode_response(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_ENDTRANSACTION; ++ opIn.returnCode = SCARD_S_SUCCESS; ++ ++ if (!test_response_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_field("EndTransaction", "returnCode", (UINT32)opIn.returnCode, ++ (UINT32)opOut.returnCode)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_status_decode_response_impl(UINT32 ioControlCode) ++{ ++ BOOL success = FALSE; ++ const char* n = scard_get_ioctl_string(ioControlCode, TRUE); ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = ioControlCode; ++ opIn.returnCode = SCARD_S_SUCCESS; ++ BYTE readerName[] = { 'R', 'd', 'r', '\0' }; ++ opIn.ret.status.cBytes = sizeof(readerName); ++ opIn.ret.status.mszReaderNames = readerName; ++ opIn.ret.status.dwState = SCARD_SPECIFIC; ++ opIn.ret.status.dwProtocol = SCARD_PROTOCOL_T1; ++ memset(opIn.ret.status.pbAtr, 0x3B, sizeof(opIn.ret.status.pbAtr)); ++ opIn.ret.status.cbAtrLen = 2; ++ ++ if (!test_response_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_field(n, "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode)) ++ goto out; ++ if (!check_field(n, "dwState", opIn.ret.status.dwState, opOut.ret.status.dwState)) ++ goto out; ++ if (!check_field(n, "dwProtocol", opIn.ret.status.dwProtocol, opOut.ret.status.dwProtocol)) ++ goto out; ++ if (!check_field(n, "cbAtrLen", opIn.ret.status.cbAtrLen, opOut.ret.status.cbAtrLen)) ++ goto out; ++ if (!check_bytes(n, "pbAtr", opIn.ret.status.pbAtr, opOut.ret.status.pbAtr, ++ sizeof(opIn.ret.status.pbAtr))) ++ goto out; ++ if (!check_field(n, "cBytes", opIn.ret.status.cBytes, opOut.ret.status.cBytes)) ++ goto out; ++ if (!check_bytes(n, "mszReaderNames", opIn.ret.status.mszReaderNames, ++ opOut.ret.status.mszReaderNames, opIn.ret.status.cBytes)) ++ goto out; ++ ++ success = TRUE; ++out: ++ opIn.ret.status.mszReaderNames = NULL; ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_status_a_decode_response(void) ++{ ++ return test_status_decode_response_impl(SCARD_IOCTL_STATUSA); ++} ++ ++static BOOL test_status_w_decode_response(void) ++{ ++ return test_status_decode_response_impl(SCARD_IOCTL_STATUSW); ++} ++ ++static BOOL test_transmit_decode_response(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_TRANSMIT; ++ opIn.returnCode = SCARD_S_SUCCESS; ++ BYTE recvBuf[] = { 0x90, 0x00 }; ++ opIn.ret.transmit.cbRecvLength = sizeof(recvBuf); ++ opIn.ret.transmit.pbRecvBuffer = recvBuf; ++ opIn.ret.transmit.pioRecvPci = NULL; ++ ++ if (!test_response_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_field("Transmit", "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode)) ++ goto out; ++ if (!check_field("Transmit", "cbRecvLength", opIn.ret.transmit.cbRecvLength, ++ opOut.ret.transmit.cbRecvLength)) ++ goto out; ++ if (!check_bytes("Transmit", "pbRecvBuffer", opIn.ret.transmit.pbRecvBuffer, ++ opOut.ret.transmit.pbRecvBuffer, opIn.ret.transmit.cbRecvLength)) ++ goto out; ++ ++ success = TRUE; ++out: ++ opIn.ret.transmit.pbRecvBuffer = NULL; ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_control_decode_response(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_CONTROL; ++ opIn.returnCode = SCARD_S_SUCCESS; ++ BYTE outBuf[] = { 0xAA, 0xBB, 0xCC }; ++ opIn.ret.control.cbOutBufferSize = sizeof(outBuf); ++ opIn.ret.control.pvOutBuffer = outBuf; ++ ++ if (!test_response_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_field("Control", "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode)) ++ goto out; ++ if (!check_field("Control", "cbOutBufferSize", opIn.ret.control.cbOutBufferSize, ++ opOut.ret.control.cbOutBufferSize)) ++ goto out; ++ if (!check_bytes("Control", "pvOutBuffer", opIn.ret.control.pvOutBuffer, ++ opOut.ret.control.pvOutBuffer, opIn.ret.control.cbOutBufferSize)) ++ goto out; ++ ++ success = TRUE; ++out: ++ opIn.ret.control.pvOutBuffer = NULL; ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_get_attrib_decode_response(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_GETATTRIB; ++ opIn.returnCode = SCARD_S_SUCCESS; ++ BYTE attr[] = { 0x3B, 0x90, 0x00 }; ++ opIn.ret.getAttrib.cbAttrLen = sizeof(attr); ++ opIn.ret.getAttrib.pbAttr = attr; ++ ++ if (!test_response_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_field("GetAttrib", "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode)) ++ goto out; ++ if (!check_field("GetAttrib", "cbAttrLen", opIn.ret.getAttrib.cbAttrLen, ++ opOut.ret.getAttrib.cbAttrLen)) ++ goto out; ++ if (!check_bytes("GetAttrib", "pbAttr", opIn.ret.getAttrib.pbAttr, opOut.ret.getAttrib.pbAttr, ++ opIn.ret.getAttrib.cbAttrLen)) ++ goto out; ++ ++ success = TRUE; ++out: ++ opIn.ret.getAttrib.pbAttr = NULL; ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_set_attrib_decode_response(void) ++{ ++ BOOL success = FALSE; ++ SMARTCARD_OPERATION opOut = WINPR_C_ARRAY_INIT; ++ SMARTCARD_OPERATION opIn = WINPR_C_ARRAY_INIT; ++ opIn.ioControlCode = SCARD_IOCTL_SETATTRIB; ++ opIn.returnCode = SCARD_S_SUCCESS; ++ ++ if (!test_response_roundtrip(&opIn, &opOut)) ++ goto out; ++ if (!check_field("SetAttrib", "returnCode", (UINT32)opIn.returnCode, (UINT32)opOut.returnCode)) ++ goto out; ++ ++ success = TRUE; ++out: ++ smartcard_operation_free(&opIn, FALSE); ++ smartcard_operation_free(&opOut, FALSE); ++ return success; ++} ++ ++static BOOL test_decode_responses(void) ++{ ++ BOOL success = TRUE; ++ ++ if (!test_establish_context_decode_response()) ++ success = FALSE; ++ if (!test_release_context_decode_response()) ++ success = FALSE; ++ if (!test_is_valid_context_decode_response()) ++ success = FALSE; ++ if (!test_list_reader_groups_a_decode_response()) ++ success = FALSE; ++ if (!test_list_reader_groups_w_decode_response()) ++ success = FALSE; ++ if (!test_list_readers_a_decode_response()) ++ success = FALSE; ++ if (!test_list_readers_w_decode_response()) ++ success = FALSE; ++ if (!test_get_status_change_a_decode_response()) ++ success = FALSE; ++ if (!test_get_status_change_w_decode_response()) ++ success = FALSE; ++ if (!test_cancel_decode_response()) ++ success = FALSE; ++ if (!test_connect_a_decode_response()) ++ success = FALSE; ++ if (!test_connect_w_decode_response()) ++ success = FALSE; ++ if (!test_reconnect_decode_response()) ++ success = FALSE; ++ if (!test_disconnect_decode_response()) ++ success = FALSE; ++ if (!test_begin_transaction_decode_response()) ++ success = FALSE; ++ if (!test_end_transaction_decode_response()) ++ success = FALSE; ++ if (!test_status_a_decode_response()) ++ success = FALSE; ++ if (!test_status_w_decode_response()) ++ success = FALSE; ++ if (!test_transmit_decode_response()) ++ success = FALSE; ++ if (!test_control_decode_response()) ++ success = FALSE; ++ if (!test_get_attrib_decode_response()) ++ success = FALSE; ++ if (!test_set_attrib_decode_response()) ++ success = FALSE; ++ ++ return success; ++} ++ ++int TestSmartcardOperations(int argc, char* argv[]) ++{ ++ WINPR_UNUSED(argc); ++ WINPR_UNUSED(argv); ++ ++ if (!test_encode_decode_requests()) ++ return -1; ++ ++ if (!test_decode_responses()) ++ return -1; ++ ++ return 0; ++} + +From 13dd1b439790515be4f6df013b4fb4fc4cbbae69 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Sat, 27 Jun 2026 01:28:31 +0200 +Subject: [PATCH 22/31] [channels,rdpdr] add doxygen documentation to rdpdr.h + +--- + channels/rdpdr/server/rdpdr_main.h | 17 +++--- + include/freerdp/server/rdpdr.h | 97 ++++++++++++++++++------------ + 2 files changed, 68 insertions(+), 46 deletions(-) + +diff --git a/channels/rdpdr/server/rdpdr_main.h b/channels/rdpdr/server/rdpdr_main.h +index 40327a81313b..bbcb09aea173 100644 +--- a/channels/rdpdr/server/rdpdr_main.h ++++ b/channels/rdpdr/server/rdpdr_main.h +@@ -35,16 +35,17 @@ typedef struct S_RDPDR_IRP RDPDR_IRP; + typedef UINT (*RDPDR_IRP_Callback)(RdpdrServerContext* context, wStream* s, struct S_RDPDR_IRP* irp, + UINT32 deviceId, UINT32 completionId, UINT32 ioStatus); + ++/** IRP request structure */ + struct S_RDPDR_IRP + { +- UINT32 CompletionId; +- UINT32 DeviceId; +- UINT32 FileId; +- UINT32 IoControlCode; +- char PathName[256]; +- char ExtraBuffer[256]; +- void* CallbackData; +- RDPDR_IRP_Callback Callback; ++ UINT32 CompletionId; /**< IRP completion identifier */ ++ UINT32 DeviceId; /**< Target device identifier */ ++ UINT32 FileId; /**< File handle identifier */ ++ UINT32 IoControlCode; /**< I/O control code */ ++ char PathName[256]; /**< File path name */ ++ char ExtraBuffer[256]; /**< Data buffer */ ++ void* CallbackData; /**< User callback data */ ++ RDPDR_IRP_Callback Callback; /**< Completion callback function */ + }; + + #endif /* FREERDP_CHANNEL_RDPDR_SERVER_MAIN_H */ +diff --git a/include/freerdp/server/rdpdr.h b/include/freerdp/server/rdpdr.h +index a44a7e1cef87..9fb1515e1a4e 100644 +--- a/include/freerdp/server/rdpdr.h ++++ b/include/freerdp/server/rdpdr.h +@@ -43,28 +43,30 @@ extern "C" + typedef struct s_rdpdr_server_context RdpdrServerContext; + typedef struct s_rdpdr_server_private RdpdrServerPrivate; + ++ /** RDPDR protocol header */ + typedef struct + { +- UINT16 Component; +- UINT16 PacketId; ++ UINT16 Component; /**< Component identifier */ ++ UINT16 PacketId; /**< Packet identifier */ + } RDPDR_HEADER; + + #ifndef __MINGW32__ ++ /** File directory information structure */ + typedef struct + { +- UINT32 NextEntryOffset; +- UINT32 FileIndex; +- LARGE_INTEGER CreationTime; +- LARGE_INTEGER LastAccessTime; +- LARGE_INTEGER LastWriteTime; +- LARGE_INTEGER ChangeTime; +- LARGE_INTEGER EndOfFile; +- LARGE_INTEGER AllocationSize; +- UINT32 FileAttributes; ++ UINT32 NextEntryOffset; /**< Offset to next entry */ ++ UINT32 FileIndex; /**< File index */ ++ LARGE_INTEGER CreationTime; /**< File creation time */ ++ LARGE_INTEGER LastAccessTime; /**< Last access time */ ++ LARGE_INTEGER LastWriteTime; /**< Last write time */ ++ LARGE_INTEGER ChangeTime; /**< Last change time */ ++ LARGE_INTEGER EndOfFile; /**< End of file position */ ++ LARGE_INTEGER AllocationSize; /**< Allocated size */ ++ UINT32 FileAttributes; /**< File attributes */ + #if defined(WITH_WCHAR_FILE_DIRECTORY_INFORMATION) +- WCHAR FileName[512]; ++ WCHAR FileName[512]; /**< File name (wide char) */ + #else +- char FileName[512]; ++ char FileName[512]; /**< File name */ + #endif + } FILE_DIRECTORY_INFORMATION; + #endif +@@ -247,12 +249,12 @@ extern "C" + + struct s_rdpdr_server_context + { +- HANDLE vcm; ++ HANDLE vcm; /**< Virtual channel manager handle */ + +- WINPR_ATTR_NODISCARD psRdpdrStart Start; +- psRdpdrStop Stop; ++ WINPR_ATTR_NODISCARD psRdpdrStart Start; /**< Start the RDPDR server */ ++ psRdpdrStop Stop; /**< Stop the RDPDR server */ + +- RdpdrServerPrivate* priv; ++ RdpdrServerPrivate* priv; /**< Private server context data */ + + /* Server self-defined pointer. */ + void* data; +@@ -280,22 +282,24 @@ extern "C" + WINPR_ATTR_NODISCARD psRdpdrReceiveClientNameRequest + ReceiveClientNameRequest; /**< Called after a client name request is received */ + WINPR_ATTR_NODISCARD psRdpdrReceiveDeviceAnnounce +- ReceiveDeviceAnnounce; /** < Called after a new device request was received but before ++ ReceiveDeviceAnnounce; /**< Called after a new device request was received but before + the device is added */ + WINPR_ATTR_NODISCARD psRdpdrReceiveDeviceRemove + ReceiveDeviceRemove; /**< Called after a new device request was + received, but before it is removed */ + + /*** Drive APIs called by the server. ***/ +- WINPR_ATTR_NODISCARD psRdpdrDriveCreateDirectory DriveCreateDirectory; +- WINPR_ATTR_NODISCARD psRdpdrDriveDeleteDirectory DriveDeleteDirectory; +- WINPR_ATTR_NODISCARD psRdpdrDriveQueryDirectory DriveQueryDirectory; +- WINPR_ATTR_NODISCARD psRdpdrDriveOpenFile DriveOpenFile; +- WINPR_ATTR_NODISCARD psRdpdrDriveReadFile DriveReadFile; +- WINPR_ATTR_NODISCARD psRdpdrDriveWriteFile DriveWriteFile; +- WINPR_ATTR_NODISCARD psRdpdrDriveCloseFile DriveCloseFile; +- WINPR_ATTR_NODISCARD psRdpdrDriveDeleteFile DriveDeleteFile; +- WINPR_ATTR_NODISCARD psRdpdrDriveRenameFile DriveRenameFile; ++ WINPR_ATTR_NODISCARD psRdpdrDriveCreateDirectory ++ DriveCreateDirectory; /**< Create directory */ ++ WINPR_ATTR_NODISCARD psRdpdrDriveDeleteDirectory ++ DriveDeleteDirectory; /**< Delete directory */ ++ WINPR_ATTR_NODISCARD psRdpdrDriveQueryDirectory DriveQueryDirectory; /**< Query directory */ ++ WINPR_ATTR_NODISCARD psRdpdrDriveOpenFile DriveOpenFile; /**< Open file */ ++ WINPR_ATTR_NODISCARD psRdpdrDriveReadFile DriveReadFile; /**< Read file */ ++ WINPR_ATTR_NODISCARD psRdpdrDriveWriteFile DriveWriteFile; /**< Write file */ ++ WINPR_ATTR_NODISCARD psRdpdrDriveCloseFile DriveCloseFile; /**< Close file */ ++ WINPR_ATTR_NODISCARD psRdpdrDriveDeleteFile DriveDeleteFile; /**< Delete file */ ++ WINPR_ATTR_NODISCARD psRdpdrDriveRenameFile DriveRenameFile; /**< Rename file */ + + /*** Drive callbacks registered by the server. ***/ + WINPR_ATTR_NODISCARD psRdpdrOnDeviceCreate +@@ -304,15 +308,24 @@ extern "C" + WINPR_ATTR_NODISCARD psRdpdrOnDeviceDelete + OnDriveDelete; /**< Called for devices of type \b + RDPDR_DTYP_FILESYSTEM after \b ReceiveDeviceRemove */ +- psRdpdrOnDriveCreateDirectoryComplete OnDriveCreateDirectoryComplete; +- psRdpdrOnDriveDeleteDirectoryComplete OnDriveDeleteDirectoryComplete; +- psRdpdrOnDriveQueryDirectoryComplete OnDriveQueryDirectoryComplete; +- psRdpdrOnDriveOpenFileComplete OnDriveOpenFileComplete; +- psRdpdrOnDriveReadFileComplete OnDriveReadFileComplete; +- psRdpdrOnDriveWriteFileComplete OnDriveWriteFileComplete; +- psRdpdrOnDriveCloseFileComplete OnDriveCloseFileComplete; +- psRdpdrOnDriveDeleteFileComplete OnDriveDeleteFileComplete; +- psRdpdrOnDriveRenameFileComplete OnDriveRenameFileComplete; ++ psRdpdrOnDriveCreateDirectoryComplete ++ OnDriveCreateDirectoryComplete; /**< DriveCreateDirectory completion callback */ ++ psRdpdrOnDriveDeleteDirectoryComplete ++ OnDriveDeleteDirectoryComplete; /**< DriveDeleteDirectory completion callback */ ++ psRdpdrOnDriveQueryDirectoryComplete ++ OnDriveQueryDirectoryComplete; /**< DriveQueryDirectory completion callback */ ++ psRdpdrOnDriveOpenFileComplete ++ OnDriveOpenFileComplete; /**< DriveOpenFile completion callback */ ++ psRdpdrOnDriveReadFileComplete ++ OnDriveReadFileComplete; /**< DriveReadFile completion callback */ ++ psRdpdrOnDriveWriteFileComplete ++ OnDriveWriteFileComplete; /**< DriveWriteFile completion callback */ ++ psRdpdrOnDriveCloseFileComplete ++ OnDriveCloseFileComplete; /**< DriveCloseFile completion callback */ ++ psRdpdrOnDriveDeleteFileComplete ++ OnDriveDeleteFileComplete; /**< DriveDeleteFile completion callback */ ++ psRdpdrOnDriveRenameFileComplete ++ OnDriveRenameFileComplete; /**< DriveRenameFile completion callback */ + + /*** Serial Port callbacks registered by the server. ***/ + WINPR_ATTR_NODISCARD psRdpdrOnDeviceCreate +@@ -344,7 +357,7 @@ extern "C" + OnSmartcardDelete; /**< Called for devices of type + RDPDR_DTYP_SMARTCARD after \b ReceiveDeviceRemove */ + +- rdpContext* rdpcontext; ++ rdpContext* rdpcontext; /**< RDP context */ + + /*** New Smartcard APIs called by the server. ***/ + WINPR_ATTR_NODISCARD psRdpdrSmartcardEstablishContext +@@ -448,10 +461,18 @@ extern "C" + /* clang-format on */ + }; + ++ /** ++ * @brief Free an RDPDR server context. ++ * @param context The RDPDR server context to free. ++ */ + FREERDP_API void rdpdr_server_context_free(RdpdrServerContext* context); + ++ /** ++ * @brief Create a new RDPDR server context. ++ * @param vcm Virtual channel manager handle. ++ * @return Newly allocated RDPDR server context, or NULL on failure. ++ */ + WINPR_ATTR_MALLOC(rdpdr_server_context_free, 1) +- WINPR_ATTR_NODISCARD + FREERDP_API RdpdrServerContext* rdpdr_server_context_new(HANDLE vcm); + + /** + +From 3f911fca8307754edfbc60c3b0994f1cabd52432 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Mon, 29 Jun 2026 19:20:46 +0200 +Subject: [PATCH 23/31] [channels,rdpdr] fix compile warnings + +--- + channels/rdpdr/server/rdpdr_main.c | 150 +++++++++++++++-------------- + 1 file changed, 79 insertions(+), 71 deletions(-) + +diff --git a/channels/rdpdr/server/rdpdr_main.c b/channels/rdpdr/server/rdpdr_main.c +index 7b9c1d33ff10..f5565dcdeb2c 100644 +--- a/channels/rdpdr/server/rdpdr_main.c ++++ b/channels/rdpdr/server/rdpdr_main.c +@@ -254,6 +254,11 @@ static void rdpdr_server_irp_free(RDPDR_IRP* irp) + free(irp); + } + ++static void rdpdr_server_list_free(void* ptr) ++{ ++ RDPDR_IRP* irp = ptr; ++ rdpdr_server_irp_free(irp); ++} + static BOOL rdpdr_server_enqueue_irp(RdpdrServerContext* context, RDPDR_IRP* irp) + { + WINPR_ASSERT(context); +@@ -1154,7 +1159,7 @@ static UINT rdpdr_server_receive_device_list_announce_request(RdpdrServerContext + if (error != CHANNEL_RC_OK) + return error; + +- error = STATUS_NOT_SUPPORTED; ++ error = ERROR_NOT_SUPPORTED; + switch (device.DeviceType) + { + case RDPDR_DTYP_FILESYSTEM: +@@ -2214,7 +2219,10 @@ static DWORD WINAPI rdpdr_server_thread(LPVOID arg) + goto out; + case WAIT_OBJECT_0 + 1: + { +- s = ChannelPduTracker_poll(tracker); ++ BOOL ok = FALSE; ++ s = ChannelPduTracker_poll(tracker, &ok); ++ if (!s && ok) ++ continue; + if (!s) + break; + +@@ -3707,7 +3715,7 @@ static RdpdrServerPrivate* rdpdr_server_private_new(void) + if (!priv->IrpList) + goto fail; + +- ListDictionary_ValueObject(priv->IrpList)->fnObjectFree = (OBJECT_FREE_FN)rdpdr_server_irp_free; ++ ListDictionary_ValueObject(priv->IrpList)->fnObjectFree = rdpdr_server_list_free; + + priv->devicelist = HashTable_New(FALSE); + if (!priv->devicelist) +@@ -3763,8 +3771,8 @@ static UINT rdpdr_server_smartcard_establish_context_callback(RdpdrServerContext + goto out; + } + +- result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INVALID_DATA; + goto out; +@@ -3825,8 +3833,8 @@ static UINT rdpdr_server_smartcard_establish_context(RdpdrServerContext* context + goto out; + } + +- result = smartcard_irp_device_control_encode_request(s, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_encode_request(s, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INTERNAL_ERROR; + goto out; +@@ -3871,8 +3879,8 @@ static UINT rdpdr_server_smartcard_release_context_callback(RdpdrServerContext* + goto out; + } + +- result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INVALID_DATA; + goto out; +@@ -3923,8 +3931,8 @@ static UINT rdpdr_server_smartcard_release_context(RdpdrServerContext* context, + goto out; + } + +- result = smartcard_irp_device_control_encode_request(s, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_encode_request(s, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INTERNAL_ERROR; + goto out; +@@ -3970,8 +3978,8 @@ static UINT rdpdr_server_smartcard_is_valid_context_callback(RdpdrServerContext* + goto out; + } + +- result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INVALID_DATA; + goto out; +@@ -4022,8 +4030,8 @@ static UINT rdpdr_server_smartcard_is_valid_context(RdpdrServerContext* context, + goto out; + } + +- result = smartcard_irp_device_control_encode_request(s, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_encode_request(s, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INTERNAL_ERROR; + goto out; +@@ -4069,8 +4077,8 @@ static UINT rdpdr_server_smartcard_list_reader_groups_callback(RdpdrServerContex + goto out; + } + +- result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INVALID_DATA; + goto out; +@@ -4128,8 +4136,8 @@ static UINT rdpdr_server_smartcard_list_reader_groups(RdpdrServerContext* contex + goto out; + } + +- result = smartcard_irp_device_control_encode_request(s, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_encode_request(s, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INTERNAL_ERROR; + goto out; +@@ -4192,8 +4200,8 @@ static UINT rdpdr_server_smartcard_list_readers_callback(RdpdrServerContext* con + goto out; + } + +- result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INVALID_DATA; + goto out; +@@ -4251,8 +4259,8 @@ static UINT rdpdr_server_smartcard_list_readers(RdpdrServerContext* context, voi + goto out; + } + +- result = smartcard_irp_device_control_encode_request(s, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_encode_request(s, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INTERNAL_ERROR; + goto out; +@@ -4312,8 +4320,8 @@ static UINT rdpdr_server_smartcard_get_status_change_callback(RdpdrServerContext + goto out; + } + +- result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INVALID_DATA; + goto out; +@@ -4376,8 +4384,8 @@ static UINT rdpdr_server_smartcard_get_status_change(RdpdrServerContext* context + goto out; + } + +- result = smartcard_irp_device_control_encode_request(s, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_encode_request(s, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INTERNAL_ERROR; + goto out; +@@ -4440,8 +4448,8 @@ static UINT rdpdr_server_smartcard_cancel_callback(RdpdrServerContext* context, + goto out; + } + +- result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INVALID_DATA; + goto out; +@@ -4489,8 +4497,8 @@ static UINT rdpdr_server_smartcard_cancel(RdpdrServerContext* context, void* cal + goto out; + } + +- result = smartcard_irp_device_control_encode_request(s, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_encode_request(s, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INTERNAL_ERROR; + goto out; +@@ -4535,8 +4543,8 @@ static UINT rdpdr_server_smartcard_connect_callback(RdpdrServerContext* context, + goto out; + } + +- result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INVALID_DATA; + goto out; +@@ -4597,8 +4605,8 @@ static UINT rdpdr_server_smartcard_connect(RdpdrServerContext* context, void* ca + goto out; + } + +- result = smartcard_irp_device_control_encode_request(s, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_encode_request(s, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INTERNAL_ERROR; + goto out; +@@ -4657,8 +4665,8 @@ static UINT rdpdr_server_smartcard_reconnect_callback(RdpdrServerContext* contex + goto out; + } + +- result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INVALID_DATA; + goto out; +@@ -4715,8 +4723,8 @@ static UINT rdpdr_server_smartcard_reconnect(RdpdrServerContext* context, void* + goto out; + } + +- result = smartcard_irp_device_control_encode_request(s, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_encode_request(s, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INTERNAL_ERROR; + goto out; +@@ -4761,8 +4769,8 @@ static UINT rdpdr_server_smartcard_disconnect_callback(RdpdrServerContext* conte + goto out; + } + +- result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INVALID_DATA; + goto out; +@@ -4817,8 +4825,8 @@ static UINT rdpdr_server_smartcard_disconnect(RdpdrServerContext* context, void* + goto out; + } + +- result = smartcard_irp_device_control_encode_request(s, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_encode_request(s, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INTERNAL_ERROR; + goto out; +@@ -4864,8 +4872,8 @@ static UINT rdpdr_server_smartcard_begin_transaction_callback(RdpdrServerContext + goto out; + } + +- result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INVALID_DATA; + goto out; +@@ -4923,8 +4931,8 @@ static UINT rdpdr_server_smartcard_begin_transaction(RdpdrServerContext* context + goto out; + } + +- result = smartcard_irp_device_control_encode_request(s, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_encode_request(s, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INTERNAL_ERROR; + goto out; +@@ -4969,8 +4977,8 @@ static UINT rdpdr_server_smartcard_end_transaction_callback(RdpdrServerContext* + goto out; + } + +- result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INVALID_DATA; + goto out; +@@ -5027,8 +5035,8 @@ static UINT rdpdr_server_smartcard_end_transaction(RdpdrServerContext* context, + goto out; + } + +- result = smartcard_irp_device_control_encode_request(s, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_encode_request(s, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INTERNAL_ERROR; + goto out; +@@ -5073,8 +5081,8 @@ static UINT rdpdr_server_smartcard_status_callback(RdpdrServerContext* context, + goto out; + } + +- result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INVALID_DATA; + goto out; +@@ -5132,8 +5140,8 @@ static UINT rdpdr_server_smartcard_status(RdpdrServerContext* context, void* cal + goto out; + } + +- result = smartcard_irp_device_control_encode_request(s, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_encode_request(s, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INTERNAL_ERROR; + goto out; +@@ -5192,8 +5200,8 @@ static UINT rdpdr_server_smartcard_transmit_callback(RdpdrServerContext* context + goto out; + } + +- result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INVALID_DATA; + goto out; +@@ -5244,8 +5252,8 @@ static UINT rdpdr_server_smartcard_transmit(RdpdrServerContext* context, void* c + goto out; + } + +- result = smartcard_irp_device_control_encode_request(s, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_encode_request(s, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INTERNAL_ERROR; + goto out; +@@ -5290,8 +5298,8 @@ static UINT rdpdr_server_smartcard_control_callback(RdpdrServerContext* context, + goto out; + } + +- result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INVALID_DATA; + goto out; +@@ -5350,8 +5358,8 @@ static UINT rdpdr_server_smartcard_control(RdpdrServerContext* context, void* ca + goto out; + } + +- result = smartcard_irp_device_control_encode_request(s, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_encode_request(s, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INTERNAL_ERROR; + goto out; +@@ -5396,8 +5404,8 @@ static UINT rdpdr_server_smartcard_get_attrib_callback(RdpdrServerContext* conte + goto out; + } + +- result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INVALID_DATA; + goto out; +@@ -5413,8 +5421,8 @@ static UINT rdpdr_server_smartcard_get_attrib_callback(RdpdrServerContext* conte + return result; + } + +-UINT rdpdr_server_smartcard_get_attrib(RdpdrServerContext* context, void* callbackData, +- const GetAttrib_Call* call, UINT32* completionId) ++static UINT rdpdr_server_smartcard_get_attrib(RdpdrServerContext* context, void* callbackData, ++ const GetAttrib_Call* call, UINT32* completionId) + { + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); +@@ -5488,8 +5496,8 @@ static UINT rdpdr_server_smartcard_set_attrib_callback(RdpdrServerContext* conte + goto out; + } + +- result = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INVALID_DATA; + goto out; +@@ -5545,8 +5553,8 @@ static UINT rdpdr_server_smartcard_set_attrib(RdpdrServerContext* context, void* + goto out; + } + +- result = smartcard_irp_device_control_encode_request(s, &op); +- if (result != SCARD_S_SUCCESS) ++ const LONG rc = smartcard_irp_device_control_encode_request(s, &op); ++ if (rc != SCARD_S_SUCCESS) + { + result = ERROR_INTERNAL_ERROR; + goto out; + +From 0cf276e1716ba1febd60761f0ab54b02b610e143 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Tue, 30 Jun 2026 08:49:08 +0200 +Subject: [PATCH 24/31] [utils,smartcard] add missing entries in + smartcard_operation_free + +--- + libfreerdp/utils/smartcard_operations.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/libfreerdp/utils/smartcard_operations.c b/libfreerdp/utils/smartcard_operations.c +index a38e0d102487..a92f7222dd7b 100644 +--- a/libfreerdp/utils/smartcard_operations.c ++++ b/libfreerdp/utils/smartcard_operations.c +@@ -1733,6 +1733,18 @@ void smartcard_operation_free(SMARTCARD_OPERATION* op, BOOL allocated) + free(ret->pioRecvPci); + } + break; ++ case SCARD_IOCTL_CONTROL: ++ { ++ Control_Return* call = &op->ret.control; ++ free(call->pvOutBuffer); ++ } ++ break; ++ case SCARD_IOCTL_GETATTRIB: ++ { ++ GetAttrib_Return* ret = &op->ret.getAttrib; ++ free(ret->pbAttr); ++ } ++ break; + default: + break; + } + +From 5793486ae671b43d5b99fc23dfb82a121b7eb9e6 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Tue, 30 Jun 2026 09:04:21 +0200 +Subject: [PATCH 25/31] [server,rdpdr] update doxygen + +--- + include/freerdp/server/rdpdr.h | 207 ++++++++++++++++++++++----------- + 1 file changed, 140 insertions(+), 67 deletions(-) + +diff --git a/include/freerdp/server/rdpdr.h b/include/freerdp/server/rdpdr.h +index 9fb1515e1a4e..a9b38a0dd9ba 100644 +--- a/include/freerdp/server/rdpdr.h ++++ b/include/freerdp/server/rdpdr.h +@@ -359,106 +359,179 @@ extern "C" + + rdpContext* rdpcontext; /**< RDP context */ + +- /*** New Smartcard APIs called by the server. ***/ ++ /*** New Smartcard APIs called by the server. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardEstablishContext +- SmartcardEstablishContext; /**< Send SCardEstablishContext to the client. */ ++ SmartcardEstablishContext; /**< Send SCardEstablishContext to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardReleaseContext +- SmartcardReleaseContext; /**< Send SCardReleaseContext to the client. */ ++ SmartcardReleaseContext; /**< Send SCardReleaseContext to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardIsValidContext +- SmartcardIsValidContext; /**< Send SCardIsValidContext to the client. */ ++ SmartcardIsValidContext; /**< Send SCardIsValidContext to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardListReaderGroups +- SmartcardListReaderGroupsA; /**< Send SCardListReaderGroupsA to the client. */ ++ SmartcardListReaderGroupsA; /**< Send SCardListReaderGroupsA to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardListReaderGroups +- SmartcardListReaderGroupsW; /**< Send SCardListReaderGroupsW to the client. */ ++ SmartcardListReaderGroupsW; /**< Send SCardListReaderGroupsW to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardListReaders +- SmartcardListReadersA; /**< Send SCardListReadersA to the client. */ ++ SmartcardListReadersA; /**< Send SCardListReadersA to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardListReaders +- SmartcardListReadersW; /**< Send SCardListReadersW to the client. */ ++ SmartcardListReadersW; /**< Send SCardListReadersW to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardGetStatusChangeA +- SmartcardGetStatusChangeA; /**< Send SCardGetStatusChangeA to the client. */ ++ SmartcardGetStatusChangeA; /**< Send SCardGetStatusChangeA to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardGetStatusChangeW +- SmartcardGetStatusChangeW; /**< Send SCardGetStatusChangeW to the client. */ ++ SmartcardGetStatusChangeW; /**< Send SCardGetStatusChangeW to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardCancel +- SmartcardCancel; /**< Send SCardCancel to the client. */ ++ SmartcardCancel; /**< Send SCardCancel to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardConnectA +- SmartcardConnectA; /**< Send SCardConnectA to the client. */ ++ SmartcardConnectA; /**< Send SCardConnectA to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardConnectW +- SmartcardConnectW; /**< Send SCardConnectW to the client. */ ++ SmartcardConnectW; /**< Send SCardConnectW to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardReconnect +- SmartcardReconnect; /**< Send SCardReconnect to the client. */ ++ SmartcardReconnect; /**< Send SCardReconnect to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardDisconnect +- SmartcardDisconnect; /**< Send SCardDisconnect to the client. */ ++ SmartcardDisconnect; /**< Send SCardDisconnect to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardBeginTransaction +- SmartcardBeginTransaction; /**< Send SCardBeginTransaction to the client. */ ++ SmartcardBeginTransaction; /**< Send SCardBeginTransaction to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardEndTransaction +- SmartcardEndTransaction; /**< Send SCardEndTransaction to the client. */ ++ SmartcardEndTransaction; /**< Send SCardEndTransaction to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardStatus +- SmartcardStatusA; /**< Send SCardStatusA to the client. */ ++ SmartcardStatusA; /**< Send SCardStatusA to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardStatus +- SmartcardStatusW; /**< Send SCardStatusW to the client. */ ++ SmartcardStatusW; /**< Send SCardStatusW to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardTransmit +- SmartcardTransmit; /**< Send SCardTransmit to the client. */ ++ SmartcardTransmit; /**< Send SCardTransmit to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardControl +- SmartcardControl; /**< Send SCardControl to the client. */ ++ SmartcardControl; /**< Send SCardControl to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardGetAttrib +- SmartcardGetAttrib; /**< Send SCardGetAttrib to the client. */ ++ SmartcardGetAttrib; /**< Send SCardGetAttrib to the client. ++ * @since version 3.28.0 ++ */ + WINPR_ATTR_NODISCARD psRdpdrSmartcardSetAttrib +- SmartcardSetAttrib; /**< Send SCardSetAttrib to the client. */ ++ SmartcardSetAttrib; /**< Send SCardSetAttrib to the client. ++ * @since version 3.28.0 ++ */ + + /*** New Smartcard callbacks registered by the server. ***/ +- /* clang-format off */ + psRdpdrOnSmartcardEstablishContextComplete +- OnSmartcardEstablishContextComplete; /**< Completion callback for +- SmartcardEstablishContext. */ ++ OnSmartcardEstablishContextComplete; /**< ++ * Completion callback for ++ * SmartcardEstablishContext. ++ * @since version 3.28.0 ++ */ + psRdpdrOnSmartcardReleaseContextComplete +- OnSmartcardReleaseContextComplete; /**< Completion callback for +- SmartcardReleaseContext. */ ++ OnSmartcardReleaseContextComplete; /**< Completion ++ * callback for SmartcardReleaseContext. ++ * @since version 3.28.0 ++ */ + psRdpdrOnSmartcardIsValidContextComplete +- OnSmartcardIsValidContextComplete; /**< Completion callback for +- SmartcardIsValidContext. */ ++ OnSmartcardIsValidContextComplete; /**< Completion ++ * callback for SmartcardIsValidContext. ++ * @since version 3.28.0 ++ */ + psRdpdrOnSmartcardListReaderGroupsComplete +- OnSmartcardListReaderGroupsComplete; /**< Completion callback for +- SmartcardListReaderGroups. */ ++ OnSmartcardListReaderGroupsComplete; /**< ++ * Completion callback for ++ * SmartcardListReaderGroups. ++ * @since version 3.28.0 ++ */ + psRdpdrOnSmartcardListReadersComplete +- OnSmartcardListReadersComplete; /**< Completion callback for +- SmartcardListReaders. */ ++ OnSmartcardListReadersComplete; /**< Completion ++ * callback for SmartcardListReaders. ++ * @since version 3.28.0 ++ */ + psRdpdrOnSmartcardGetStatusChangeComplete +- OnSmartcardGetStatusChangeComplete; /**< Completion callback for +- SmartcardGetStatusChange. */ +- psRdpdrOnSmartcardCancelComplete +- OnSmartcardCancelComplete; /**< Completion callback for +- SmartcardCancel. */ +- psRdpdrOnSmartcardConnectComplete +- OnSmartcardConnectComplete; /**< Completion callback for +- SmartcardConnect. */ +- psRdpdrOnSmartcardReconnectComplete +- OnSmartcardReconnectComplete; /**< Completion callback for +- SmartcardReconnect. */ ++ OnSmartcardGetStatusChangeComplete; /**< ++ * Completion callback for SmartcardGetStatusChange. ++ * @since version 3.28.0 ++ */ ++ psRdpdrOnSmartcardCancelComplete OnSmartcardCancelComplete; /**< Completion callback for ++ * SmartcardCancel. ++ * @since version 3.28.0 ++ */ ++ psRdpdrOnSmartcardConnectComplete OnSmartcardConnectComplete; /**< Completion callback for ++ * SmartcardConnect. ++ * @since version 3.28.0 ++ */ ++ psRdpdrOnSmartcardReconnectComplete OnSmartcardReconnectComplete; /**< Completion callback ++ * for SmartcardReconnect. ++ * @since version 3.28.0 ++ */ + psRdpdrOnSmartcardDisconnectComplete +- OnSmartcardDisconnectComplete; /**< Completion callback for +- SmartcardDisconnect. */ ++ OnSmartcardDisconnectComplete; /**< Completion callback ++ * for SmartcardDisconnect. ++ * @since version 3.28.0 ++ */ + psRdpdrOnSmartcardBeginTransactionComplete +- OnSmartcardBeginTransactionComplete; /**< Completion callback for +- SmartcardBeginTransaction. */ ++ OnSmartcardBeginTransactionComplete; /**< ++ * Completion callback for ++ * SmartcardBeginTransaction. ++ * @since version 3.28.0 ++ */ + psRdpdrOnSmartcardEndTransactionComplete +- OnSmartcardEndTransactionComplete; /**< Completion callback for +- SmartcardEndTransaction. */ +- psRdpdrOnSmartcardStatusComplete +- OnSmartcardStatusComplete; /**< Completion callback for +- SmartcardStatus. */ +- psRdpdrOnSmartcardTransmitComplete +- OnSmartcardTransmitComplete; /**< Completion callback for +- SmartcardTransmit. */ +- psRdpdrOnSmartcardControlComplete +- OnSmartcardControlComplete; /**< Completion callback for +- SmartcardControl. */ +- psRdpdrOnSmartcardGetAttribComplete +- OnSmartcardGetAttribComplete; /**< Completion callback for +- SmartcardGetAttrib. */ +- psRdpdrOnSmartcardSetAttribComplete +- OnSmartcardSetAttribComplete; /**< Completion callback for +- SmartcardSetAttrib. */ +- /* clang-format on */ ++ OnSmartcardEndTransactionComplete; /**< Completion ++ * callback for SmartcardEndTransaction. ++ * @since version 3.28.0 ++ */ ++ psRdpdrOnSmartcardStatusComplete OnSmartcardStatusComplete; /**< Completion callback for ++ * SmartcardStatus. ++ * @since version 3.28.0 ++ */ ++ psRdpdrOnSmartcardTransmitComplete OnSmartcardTransmitComplete; /**< Completion callback for ++ * SmartcardTransmit. ++ * @since version 3.28.0 ++ */ ++ psRdpdrOnSmartcardControlComplete OnSmartcardControlComplete; /**< Completion callback for ++ * SmartcardControl. ++ * @since version 3.28.0 ++ */ ++ psRdpdrOnSmartcardGetAttribComplete OnSmartcardGetAttribComplete; /**< Completion callback ++ * for SmartcardGetAttrib. ++ * @since version 3.28.0 ++ */ ++ psRdpdrOnSmartcardSetAttribComplete OnSmartcardSetAttribComplete; /**< Completion callback ++ * for SmartcardSetAttrib. ++ * @since version 3.28.0 ++ */ + }; + + /** + +From 8cb4b9ca8e80c3a8d50a9f5ef9ef3248bccc37fc Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Tue, 30 Jun 2026 09:06:32 +0200 +Subject: [PATCH 26/31] [utils,test] replace NULL with nullptr + +--- + libfreerdp/utils/smartcard_pack.c | 4 +- + .../utils/test/TestSmartcardOperations.c | 42 +++++++++---------- + 2 files changed, 23 insertions(+), 23 deletions(-) + +diff --git a/libfreerdp/utils/smartcard_pack.c b/libfreerdp/utils/smartcard_pack.c +index 5aa54590f4fc..a10974f75bfb 100644 +--- a/libfreerdp/utils/smartcard_pack.c ++++ b/libfreerdp/utils/smartcard_pack.c +@@ -4672,7 +4672,7 @@ LONG smartcard_pack_transmit_call(wStream* s, const Transmit_Call* call) + return status; + + UINT32 cbExtraBytes = 0; +- const BYTE* pbExtraBytes = NULL; ++ const BYTE* pbExtraBytes = nullptr; + if (call->pioSendPci) + { + cbExtraBytes = (UINT32)(call->pioSendPci->cbPciLength - sizeof(SCARD_IO_REQUEST)); +@@ -4825,7 +4825,7 @@ LONG smartcard_unpack_get_status_change_return(wStream* s, GetStatusChange_Retur + return status; + } + else +- ret->rgReaderStates = NULL; ++ ret->rgReaderStates = nullptr; + + smartcard_trace_get_status_change_return(log, ret, unicode); + return SCARD_S_SUCCESS; +diff --git a/libfreerdp/utils/test/TestSmartcardOperations.c b/libfreerdp/utils/test/TestSmartcardOperations.c +index 4c885b13359c..590d9cc6fb93 100644 +--- a/libfreerdp/utils/test/TestSmartcardOperations.c ++++ b/libfreerdp/utils/test/TestSmartcardOperations.c +@@ -44,12 +44,12 @@ static wStream* build_device_control_request(UINT32 ioControlCode, const void* i + const char* name = scard_get_ioctl_string(ioControlCode, TRUE); + const size_t totalLength = RDPDR_DEVICE_IO_CONTROL_REQ_HDR_LENGTH + inputBufferLength; + +- wStream* s = Stream_New(NULL, totalLength); ++ wStream* s = Stream_New(nullptr, totalLength); + if (!s) + { + (void)fprintf(stderr, "build_device_control_request(%s): Stream_New(%" PRIuz ") failed\n", + name, totalLength); +- return NULL; ++ return nullptr; + } + + UINT32 inputLength = WINPR_ASSERTING_INT_CAST(UINT32, inputBufferLength); +@@ -67,7 +67,7 @@ static wStream* build_device_control_request(UINT32 ioControlCode, const void* i + (void)fprintf(stderr, "build_device_control_request(%s): Stream_SetPosition failed\n", + name); + Stream_Release(s); +- return NULL; ++ return nullptr; + } + + return s; +@@ -78,7 +78,7 @@ static BOOL test_request_roundtrip(const SMARTCARD_OPERATION* opIn, SMARTCARD_OP + const UINT32 ioControlCode = opIn->ioControlCode; + const char* name = scard_get_ioctl_string(ioControlCode, TRUE); + +- wStream* sEnc = Stream_New(NULL, 4096); ++ wStream* sEnc = Stream_New(nullptr, 4096); + if (!sEnc) + { + (void)fprintf(stderr, "%s: Stream_New for encode failed\n", name); +@@ -361,7 +361,7 @@ static BOOL test_connect_w_encode_decode_request(void) + fill_redir_context(&opIn.call.connectW.Common.handles.hContext, 0x1234); + opIn.call.connectW.Common.dwShareMode = SCARD_SHARE_SHARED; + opIn.call.connectW.Common.dwPreferredProtocols = SCARD_PROTOCOL_T0; +- opIn.call.connectW.szReader = ConvertUtf8ToWCharAlloc("TestReader", NULL); ++ opIn.call.connectW.szReader = ConvertUtf8ToWCharAlloc("TestReader", nullptr); + + if (!test_request_roundtrip(&opIn, &opOut)) + goto out; +@@ -559,8 +559,8 @@ static BOOL test_transmit_encode_decode_request(void) + + success = TRUE; + out: +- opIn.call.transmit.pioSendPci = NULL; +- opIn.call.transmit.pbSendBuffer = NULL; ++ opIn.call.transmit.pioSendPci = nullptr; ++ opIn.call.transmit.pbSendBuffer = nullptr; + smartcard_operation_free(&opIn, FALSE); + smartcard_operation_free(&opOut, FALSE); + return success; +@@ -604,7 +604,7 @@ static BOOL test_control_encode_decode_request(void) + + success = TRUE; + out: +- opIn.call.control.pvInBuffer = NULL; ++ opIn.call.control.pvInBuffer = nullptr; + smartcard_operation_free(&opIn, FALSE); + smartcard_operation_free(&opOut, FALSE); + return success; +@@ -677,7 +677,7 @@ static BOOL test_set_attrib_encode_decode_request(void) + + success = TRUE; + out: +- opIn.call.setAttrib.pbAttr = NULL; ++ opIn.call.setAttrib.pbAttr = nullptr; + smartcard_operation_free(&opIn, FALSE); + smartcard_operation_free(&opOut, FALSE); + return success; +@@ -787,7 +787,7 @@ static wStream* build_device_control_response(UINT32 ioControlCode, const SMARTC + if (!payload) + { + (void)fprintf(stderr, "%s: Stream_New for payload failed\n", name); +- return NULL; ++ return nullptr; + } + + LONG rc = encode_response_payload(payload, ioControlCode, opIn); +@@ -796,7 +796,7 @@ static wStream* build_device_control_response(UINT32 ioControlCode, const SMARTC + (void)fprintf(stderr, "%s: encode_response_payload failed with 0x%08" PRIX32 "\n", name, + (UINT32)rc); + Stream_Release(payload); +- return NULL; ++ return nullptr; + } + Stream_SealLength(payload); + +@@ -805,7 +805,7 @@ static wStream* build_device_control_response(UINT32 ioControlCode, const SMARTC + { + (void)fprintf(stderr, "%s: Stream_New for response failed\n", name); + Stream_Release(payload); +- return NULL; ++ return nullptr; + } + + Stream_Seek(s, 4); /* Placeholder for outputBufferLength */ +@@ -838,7 +838,7 @@ static wStream* build_device_control_response(UINT32 ioControlCode, const SMARTC + { + (void)fprintf(stderr, "%s: Stream_SetPosition failed\n", name); + Stream_Release(s); +- return NULL; ++ return nullptr; + } + return s; + } +@@ -957,7 +957,7 @@ static BOOL test_list_reader_groups_decode_response_impl(UINT32 ioControlCode) + + success = TRUE; + out: +- opIn.ret.listReaders.msz = NULL; ++ opIn.ret.listReaders.msz = nullptr; + smartcard_operation_free(&opIn, FALSE); + smartcard_operation_free(&opOut, FALSE); + return success; +@@ -997,7 +997,7 @@ static BOOL test_list_readers_decode_response_impl(UINT32 ioControlCode) + + success = TRUE; + out: +- opIn.ret.listReaders.msz = NULL; ++ opIn.ret.listReaders.msz = nullptr; + smartcard_operation_free(&opIn, FALSE); + smartcard_operation_free(&opOut, FALSE); + return success; +@@ -1051,7 +1051,7 @@ static BOOL test_get_status_change_decode_response_impl(UINT32 ioControlCode) + + success = TRUE; + out: +- opIn.ret.getStatusChange.rgReaderStates = NULL; ++ opIn.ret.getStatusChange.rgReaderStates = nullptr; + smartcard_operation_free(&opIn, FALSE); + smartcard_operation_free(&opOut, FALSE); + return success; +@@ -1251,7 +1251,7 @@ static BOOL test_status_decode_response_impl(UINT32 ioControlCode) + + success = TRUE; + out: +- opIn.ret.status.mszReaderNames = NULL; ++ opIn.ret.status.mszReaderNames = nullptr; + smartcard_operation_free(&opIn, FALSE); + smartcard_operation_free(&opOut, FALSE); + return success; +@@ -1277,7 +1277,7 @@ static BOOL test_transmit_decode_response(void) + BYTE recvBuf[] = { 0x90, 0x00 }; + opIn.ret.transmit.cbRecvLength = sizeof(recvBuf); + opIn.ret.transmit.pbRecvBuffer = recvBuf; +- opIn.ret.transmit.pioRecvPci = NULL; ++ opIn.ret.transmit.pioRecvPci = nullptr; + + if (!test_response_roundtrip(&opIn, &opOut)) + goto out; +@@ -1292,7 +1292,7 @@ static BOOL test_transmit_decode_response(void) + + success = TRUE; + out: +- opIn.ret.transmit.pbRecvBuffer = NULL; ++ opIn.ret.transmit.pbRecvBuffer = nullptr; + smartcard_operation_free(&opIn, FALSE); + smartcard_operation_free(&opOut, FALSE); + return success; +@@ -1322,7 +1322,7 @@ static BOOL test_control_decode_response(void) + + success = TRUE; + out: +- opIn.ret.control.pvOutBuffer = NULL; ++ opIn.ret.control.pvOutBuffer = nullptr; + smartcard_operation_free(&opIn, FALSE); + smartcard_operation_free(&opOut, FALSE); + return success; +@@ -1352,7 +1352,7 @@ static BOOL test_get_attrib_decode_response(void) + + success = TRUE; + out: +- opIn.ret.getAttrib.pbAttr = NULL; ++ opIn.ret.getAttrib.pbAttr = nullptr; + smartcard_operation_free(&opIn, FALSE); + smartcard_operation_free(&opOut, FALSE); + return success; + +From 5562dec703fc1873ee23e272691872087aeb3158 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Tue, 30 Jun 2026 09:08:01 +0200 +Subject: [PATCH 27/31] [utils,smartcard] make declaration parameters + consistent + +--- + libfreerdp/utils/smartcard_pack.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/libfreerdp/utils/smartcard_pack.c b/libfreerdp/utils/smartcard_pack.c +index a10974f75bfb..a035fcab94d7 100644 +--- a/libfreerdp/utils/smartcard_pack.c ++++ b/libfreerdp/utils/smartcard_pack.c +@@ -4256,12 +4256,12 @@ LONG smartcard_pack_get_reader_icon_return(wStream* s, const GetReaderIcon_Retur + return ret->ReturnCode; + } + +-LONG smartcard_pack_get_transmit_count_return(wStream* s, const GetTransmitCount_Return* call) ++LONG smartcard_pack_get_transmit_count_return(wStream* s, const GetTransmitCount_Return* ret) + { +- WINPR_ASSERT(call); ++ WINPR_ASSERT(ret); + wLog* log = scard_log(); + +- smartcard_trace_get_transmit_count_return(log, call); ++ smartcard_trace_get_transmit_count_return(log, ret); + + if (!Stream_EnsureRemainingCapacity(s, 4)) + { +@@ -4269,9 +4269,9 @@ LONG smartcard_pack_get_transmit_count_return(wStream* s, const GetTransmitCount + return SCARD_F_INTERNAL_ERROR; + } + +- Stream_Write_UINT32(s, call->cTransmitCount); /* cBytes (4 cbDataLen) */ ++ Stream_Write_UINT32(s, ret->cTransmitCount); /* cBytes (4 cbDataLen) */ + +- return call->ReturnCode; ++ return ret->ReturnCode; + } + + LONG smartcard_pack_read_cache_return(wStream* s, const ReadCache_Return* ret) + +From c974edcac03ae90b82e6b177cf0875ff10b97a81 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Tue, 30 Jun 2026 09:10:28 +0200 +Subject: [PATCH 28/31] [channel,rdpdr] remove manual code formatting + +--- + channels/rdpdr/server/rdpdr_main.c | 184 +++++++++++------------------ + 1 file changed, 70 insertions(+), 114 deletions(-) + +diff --git a/channels/rdpdr/server/rdpdr_main.c b/channels/rdpdr/server/rdpdr_main.c +index f5565dcdeb2c..f04acffdaafd 100644 +--- a/channels/rdpdr/server/rdpdr_main.c ++++ b/channels/rdpdr/server/rdpdr_main.c +@@ -4117,16 +4117,12 @@ static UINT rdpdr_server_smartcard_list_reader_groups(RdpdrServerContext* contex + + *completionId = irp->CompletionId; + +- /* clang-format off */ +- const SMARTCARD_OPERATION op = { +- .ioControlCode = ioControlCode, +- .call.listReaderGroups = { +- .handles.hContext = call->handles.hContext, +- .fmszGroupsIsNULL = call->fmszGroupsIsNULL, +- .cchGroups = call->cchGroups, +- } +- }; +- /* clang-format on */ ++ const SMARTCARD_OPERATION op = { .ioControlCode = ioControlCode, ++ .call.listReaderGroups = { ++ .handles.hContext = call->handles.hContext, ++ .fmszGroupsIsNULL = call->fmszGroupsIsNULL, ++ .cchGroups = call->cchGroups, ++ } }; + + s = Stream_New(nullptr, 32); + if (!s) +@@ -4238,18 +4234,14 @@ static UINT rdpdr_server_smartcard_list_readers(RdpdrServerContext* context, voi + + *completionId = irp->CompletionId; + +- /* clang-format off */ +- const SMARTCARD_OPERATION op = { +- .ioControlCode = ioControlCode, +- .call.listReaders = { +- .handles.hContext = call->handles.hContext, +- .cBytes = call->cBytes, +- .mszGroups = call->mszGroups, +- .fmszReadersIsNULL = call->fmszReadersIsNULL, +- .cchReaders = call->cchReaders, +- } +- }; +- /* clang-format on */ ++ const SMARTCARD_OPERATION op = { .ioControlCode = ioControlCode, ++ .call.listReaders = { ++ .handles.hContext = call->handles.hContext, ++ .cBytes = call->cBytes, ++ .mszGroups = call->mszGroups, ++ .fmszReadersIsNULL = call->fmszReadersIsNULL, ++ .cchReaders = call->cchReaders, ++ } }; + + s = Stream_New(nullptr, 256); + if (!s) +@@ -4702,18 +4694,14 @@ static UINT rdpdr_server_smartcard_reconnect(RdpdrServerContext* context, void* + + *completionId = irp->CompletionId; + +- /* clang-format off */ +- const SMARTCARD_OPERATION op = { +- .ioControlCode = SCARD_IOCTL_RECONNECT, +- .call.reconnect = { +- .handles.hContext = call->handles.hContext, +- .handles.hCard = call->handles.hCard, +- .dwShareMode = call->dwShareMode, +- .dwPreferredProtocols = call->dwPreferredProtocols, +- .dwInitialization = call->dwInitialization, +- } +- }; +- /* clang-format on */ ++ const SMARTCARD_OPERATION op = { .ioControlCode = SCARD_IOCTL_RECONNECT, ++ .call.reconnect = { ++ .handles.hContext = call->handles.hContext, ++ .handles.hCard = call->handles.hCard, ++ .dwShareMode = call->dwShareMode, ++ .dwPreferredProtocols = call->dwPreferredProtocols, ++ .dwInitialization = call->dwInitialization, ++ } }; + + s = Stream_New(nullptr, 128); + if (!s) +@@ -4806,16 +4794,12 @@ static UINT rdpdr_server_smartcard_disconnect(RdpdrServerContext* context, void* + + *completionId = irp->CompletionId; + +- /* clang-format off */ +- const SMARTCARD_OPERATION op = { +- .ioControlCode = SCARD_IOCTL_DISCONNECT, +- .call.hCardAndDisposition = { +- .handles.hContext = call->handles.hContext, +- .handles.hCard = call->handles.hCard, +- .dwDisposition = call->dwDisposition, +- } +- }; +- /* clang-format on */ ++ const SMARTCARD_OPERATION op = { .ioControlCode = SCARD_IOCTL_DISCONNECT, ++ .call.hCardAndDisposition = { ++ .handles.hContext = call->handles.hContext, ++ .handles.hCard = call->handles.hCard, ++ .dwDisposition = call->dwDisposition, ++ } }; + + s = Stream_New(nullptr, 128); + if (!s) +@@ -4912,16 +4896,12 @@ static UINT rdpdr_server_smartcard_begin_transaction(RdpdrServerContext* context + + *completionId = irp->CompletionId; + +- /* clang-format off */ +- const SMARTCARD_OPERATION op = { +- .ioControlCode = SCARD_IOCTL_BEGINTRANSACTION, +- .call.hCardAndDisposition = { +- .handles.hContext = call->handles.hContext, +- .handles.hCard = call->handles.hCard, +- .dwDisposition = SCARD_LEAVE_CARD, +- } +- }; +- /* clang-format on */ ++ const SMARTCARD_OPERATION op = { .ioControlCode = SCARD_IOCTL_BEGINTRANSACTION, ++ .call.hCardAndDisposition = { ++ .handles.hContext = call->handles.hContext, ++ .handles.hCard = call->handles.hCard, ++ .dwDisposition = SCARD_LEAVE_CARD, ++ } }; + + s = Stream_New(nullptr, 128); + if (!s) +@@ -5016,16 +4996,12 @@ static UINT rdpdr_server_smartcard_end_transaction(RdpdrServerContext* context, + + *completionId = irp->CompletionId; + +- /* clang-format off */ +- const SMARTCARD_OPERATION op = { +- .ioControlCode = SCARD_IOCTL_ENDTRANSACTION, +- .call.hCardAndDisposition = { +- .handles.hContext = call->handles.hContext, +- .handles.hCard = call->handles.hCard, +- .dwDisposition = call->dwDisposition, +- } +- }; +- /* clang-format on */ ++ const SMARTCARD_OPERATION op = { .ioControlCode = SCARD_IOCTL_ENDTRANSACTION, ++ .call.hCardAndDisposition = { ++ .handles.hContext = call->handles.hContext, ++ .handles.hCard = call->handles.hCard, ++ .dwDisposition = call->dwDisposition, ++ } }; + + s = Stream_New(nullptr, 128); + if (!s) +@@ -5119,18 +5095,14 @@ static UINT rdpdr_server_smartcard_status(RdpdrServerContext* context, void* cal + + *completionId = irp->CompletionId; + +- /* clang-format off */ +- const SMARTCARD_OPERATION op = { +- .ioControlCode = ioControlCode, +- .call.status = { +- .handles.hContext = call->handles.hContext, +- .handles.hCard = call->handles.hCard, +- .fmszReaderNamesIsNULL = call->fmszReaderNamesIsNULL, +- .cchReaderLen = call->cchReaderLen, +- .cbAtrLen = call->cbAtrLen, +- } +- }; +- /* clang-format on */ ++ const SMARTCARD_OPERATION op = { .ioControlCode = ioControlCode, ++ .call.status = { ++ .handles.hContext = call->handles.hContext, ++ .handles.hCard = call->handles.hCard, ++ .fmszReaderNamesIsNULL = call->fmszReaderNamesIsNULL, ++ .cchReaderLen = call->cchReaderLen, ++ .cbAtrLen = call->cbAtrLen, ++ } }; + + s = Stream_New(nullptr, 128); + if (!s) +@@ -5237,12 +5209,8 @@ static UINT rdpdr_server_smartcard_transmit(RdpdrServerContext* context, void* c + + *completionId = irp->CompletionId; + +- /* clang-format off */ +- const SMARTCARD_OPERATION op = { +- .ioControlCode = SCARD_IOCTL_TRANSMIT, +- .call.transmit = *call +- }; +- /* clang-format on */ ++ const SMARTCARD_OPERATION op = { .ioControlCode = SCARD_IOCTL_TRANSMIT, ++ .call.transmit = *call }; + + s = Stream_New(nullptr, 512 + call->cbSendLength); + if (!s) +@@ -5335,20 +5303,16 @@ static UINT rdpdr_server_smartcard_control(RdpdrServerContext* context, void* ca + + *completionId = irp->CompletionId; + +- /* clang-format off */ +- const SMARTCARD_OPERATION op = { +- .ioControlCode = SCARD_IOCTL_CONTROL, +- .call.control = { +- .handles.hContext = call->handles.hContext, +- .handles.hCard = call->handles.hCard, +- .dwControlCode = call->dwControlCode, +- .cbInBufferSize = call->cbInBufferSize, +- .pvInBuffer = call->pvInBuffer, +- .fpvOutBufferIsNULL = call->fpvOutBufferIsNULL, +- .cbOutBufferSize = call->cbOutBufferSize, +- } +- }; +- /* clang-format on */ ++ const SMARTCARD_OPERATION op = { .ioControlCode = SCARD_IOCTL_CONTROL, ++ .call.control = { ++ .handles.hContext = call->handles.hContext, ++ .handles.hCard = call->handles.hCard, ++ .dwControlCode = call->dwControlCode, ++ .cbInBufferSize = call->cbInBufferSize, ++ .pvInBuffer = call->pvInBuffer, ++ .fpvOutBufferIsNULL = call->fpvOutBufferIsNULL, ++ .cbOutBufferSize = call->cbOutBufferSize, ++ } }; + + s = Stream_New(nullptr, 512 + call->cbOutBufferSize); + if (!s) +@@ -5440,12 +5404,8 @@ static UINT rdpdr_server_smartcard_get_attrib(RdpdrServerContext* context, void* + + *completionId = irp->CompletionId; + +- /* clang-format off */ +- const SMARTCARD_OPERATION op = { +- .ioControlCode = SCARD_IOCTL_GETATTRIB, +- .call.getAttrib = *call +- }; +- /* clang-format on */ ++ const SMARTCARD_OPERATION op = { .ioControlCode = SCARD_IOCTL_GETATTRIB, ++ .call.getAttrib = *call }; + + wStream* s = Stream_New(nullptr, 64); + if (!s) +@@ -5532,18 +5492,14 @@ static UINT rdpdr_server_smartcard_set_attrib(RdpdrServerContext* context, void* + + *completionId = irp->CompletionId; + +- /* clang-format off */ +- const SMARTCARD_OPERATION op = { +- .ioControlCode = SCARD_IOCTL_SETATTRIB, +- .call.setAttrib = { +- .handles.hContext = call->handles.hContext, +- .handles.hCard = call->handles.hCard, +- .dwAttrId = call->dwAttrId, +- .cbAttrLen = call->cbAttrLen, +- .pbAttr = call->pbAttr, +- } +- }; +- /* clang-format on */ ++ const SMARTCARD_OPERATION op = { .ioControlCode = SCARD_IOCTL_SETATTRIB, ++ .call.setAttrib = { ++ .handles.hContext = call->handles.hContext, ++ .handles.hCard = call->handles.hCard, ++ .dwAttrId = call->dwAttrId, ++ .cbAttrLen = call->cbAttrLen, ++ .pbAttr = call->pbAttr, ++ } }; + + s = Stream_New(nullptr, 64 + call->cbAttrLen); + if (!s) + +From 05fd708bec929581bea630a9d2cb877bdbadb86a Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Tue, 30 Jun 2026 09:19:58 +0200 +Subject: [PATCH 29/31] [channels,rdpdr] silence false clang-analyzer warning + +rdpdr_server_discard_request does free up IRP so the +clang-analyzer-unix.Malloc is a false positive and misleading. +--- + channels/rdpdr/server/rdpdr_main.c | 35 ++++++++++++++++++++++++++++-- + 1 file changed, 33 insertions(+), 2 deletions(-) + +diff --git a/channels/rdpdr/server/rdpdr_main.c b/channels/rdpdr/server/rdpdr_main.c +index f04acffdaafd..7588e15d1fc1 100644 +--- a/channels/rdpdr/server/rdpdr_main.c ++++ b/channels/rdpdr/server/rdpdr_main.c +@@ -3845,8 +3845,10 @@ static UINT rdpdr_server_smartcard_establish_context(RdpdrServerContext* context + SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); + + out: ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (result != CHANNEL_RC_OK && irp) + rdpdr_server_discard_request(context, irp->CompletionId); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (s) + Stream_Release(s); + return result; +@@ -3943,8 +3945,10 @@ static UINT rdpdr_server_smartcard_release_context(RdpdrServerContext* context, + SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); + + out: ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (result != CHANNEL_RC_OK && irp) + rdpdr_server_discard_request(context, irp->CompletionId); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (s) + Stream_Release(s); + return result; +@@ -4042,8 +4046,10 @@ static UINT rdpdr_server_smartcard_is_valid_context(RdpdrServerContext* context, + SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); + + out: ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (result != CHANNEL_RC_OK && irp) + rdpdr_server_discard_request(context, irp->CompletionId); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (s) + Stream_Release(s); + return result; +@@ -4144,8 +4150,10 @@ static UINT rdpdr_server_smartcard_list_reader_groups(RdpdrServerContext* contex + SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); + + out: ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (result != CHANNEL_RC_OK && irp) + rdpdr_server_discard_request(context, irp->CompletionId); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (s) + Stream_Release(s); + return result; +@@ -4263,8 +4271,10 @@ static UINT rdpdr_server_smartcard_list_readers(RdpdrServerContext* context, voi + SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); + + out: ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (result != CHANNEL_RC_OK && irp) + rdpdr_server_discard_request(context, irp->CompletionId); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (s) + Stream_Release(s); + return result; +@@ -4388,8 +4398,10 @@ static UINT rdpdr_server_smartcard_get_status_change(RdpdrServerContext* context + SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); + + out: ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (result != CHANNEL_RC_OK && irp) + rdpdr_server_discard_request(context, irp->CompletionId); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (s) + Stream_Release(s); + return result; +@@ -4501,8 +4513,10 @@ static UINT rdpdr_server_smartcard_cancel(RdpdrServerContext* context, void* cal + SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); + + out: ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (result != CHANNEL_RC_OK && irp) + rdpdr_server_discard_request(context, irp->CompletionId); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (s) + Stream_Release(s); + return result; +@@ -4609,8 +4623,10 @@ static UINT rdpdr_server_smartcard_connect(RdpdrServerContext* context, void* ca + SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); + + out: ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (result != CHANNEL_RC_OK && irp) + rdpdr_server_discard_request(context, irp->CompletionId); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (s) + Stream_Release(s); + return result; +@@ -4723,8 +4739,10 @@ static UINT rdpdr_server_smartcard_reconnect(RdpdrServerContext* context, void* + SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); + + out: ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (result != CHANNEL_RC_OK && irp) + rdpdr_server_discard_request(context, irp->CompletionId); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (s) + Stream_Release(s); + return result; +@@ -4821,8 +4839,10 @@ static UINT rdpdr_server_smartcard_disconnect(RdpdrServerContext* context, void* + SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); + + out: ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (result != CHANNEL_RC_OK && irp) + rdpdr_server_discard_request(context, irp->CompletionId); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (s) + Stream_Release(s); + return result; +@@ -4923,8 +4943,10 @@ static UINT rdpdr_server_smartcard_begin_transaction(RdpdrServerContext* context + SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); + + out: ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (result != CHANNEL_RC_OK && irp) + rdpdr_server_discard_request(context, irp->CompletionId); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (s) + Stream_Release(s); + return result; +@@ -5023,8 +5045,10 @@ static UINT rdpdr_server_smartcard_end_transaction(RdpdrServerContext* context, + SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); + + out: ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (result != CHANNEL_RC_OK && irp) + rdpdr_server_discard_request(context, irp->CompletionId); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (s) + Stream_Release(s); + return result; +@@ -5124,8 +5148,10 @@ static UINT rdpdr_server_smartcard_status(RdpdrServerContext* context, void* cal + SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); + + out: ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (result != CHANNEL_RC_OK && irp) + rdpdr_server_discard_request(context, irp->CompletionId); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (s) + Stream_Release(s); + return result; +@@ -5232,8 +5258,10 @@ static UINT rdpdr_server_smartcard_transmit(RdpdrServerContext* context, void* c + SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); + + out: ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (result != CHANNEL_RC_OK && irp) + rdpdr_server_discard_request(context, irp->CompletionId); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (s) + Stream_Release(s); + return result; +@@ -5334,8 +5362,10 @@ static UINT rdpdr_server_smartcard_control(RdpdrServerContext* context, void* ca + SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); + + out: ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (result != CHANNEL_RC_OK && irp) + rdpdr_server_discard_request(context, irp->CompletionId); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (s) + Stream_Release(s); + return result; +@@ -5483,7 +5513,6 @@ static UINT rdpdr_server_smartcard_set_attrib(RdpdrServerContext* context, void* + UINT result = CHANNEL_RC_OK; + RdpdrServerPrivate* priv = context->priv; + RDPDR_IRP* irp = nullptr; +- wStream* s = nullptr; + + result = prepare_smartcard_irp(context, SCARD_IOCTL_SETATTRIB, + rdpdr_server_smartcard_set_attrib_callback, callbackData, &irp); +@@ -5501,7 +5530,7 @@ static UINT rdpdr_server_smartcard_set_attrib(RdpdrServerContext* context, void* + .pbAttr = call->pbAttr, + } }; + +- s = Stream_New(nullptr, 64 + call->cbAttrLen); ++ wStream* s = Stream_New(nullptr, 64 + call->cbAttrLen); + if (!s) + { + WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); +@@ -5521,8 +5550,10 @@ static UINT rdpdr_server_smartcard_set_attrib(RdpdrServerContext* context, void* + SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); + + out: ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (result != CHANNEL_RC_OK && irp) + rdpdr_server_discard_request(context, irp->CompletionId); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + if (s) + Stream_Release(s); + return result; + +From e8704c1e11605d902ba077c187fab768e250c04b Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Tue, 30 Jun 2026 09:34:30 +0200 +Subject: [PATCH 30/31] [channels,rdpdr] fix clang-analyzer-unix.Malloc + +* Fix false positives with annotations +* Fix actual leaks +--- + channels/rdpdr/server/rdpdr_main.c | 23 +++++++++++++++++------ + 1 file changed, 17 insertions(+), 6 deletions(-) + +diff --git a/channels/rdpdr/server/rdpdr_main.c b/channels/rdpdr/server/rdpdr_main.c +index 7588e15d1fc1..ae230dc1a3d5 100644 +--- a/channels/rdpdr/server/rdpdr_main.c ++++ b/channels/rdpdr/server/rdpdr_main.c +@@ -3352,8 +3352,10 @@ static UINT rdpdr_server_drive_write_file(RdpdrServerContext* context, void* cal + irp->FileId = fileId; + + /* Send a request to open the directory. */ ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + return rdpdr_server_send_device_write_request(context, irp->DeviceId, irp->FileId, + irp->CompletionId, buffer, length, offset); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + } + + /************************************************* +@@ -3414,8 +3416,10 @@ static UINT rdpdr_server_drive_close_file(RdpdrServerContext* context, void* cal + irp->FileId = fileId; + + /* Send a request to open the directory. */ ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + return rdpdr_server_send_device_close_request(context, irp->DeviceId, irp->FileId, + irp->CompletionId); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there + } + + /************************************************* +@@ -5426,7 +5430,7 @@ static UINT rdpdr_server_smartcard_get_attrib(RdpdrServerContext* context, void* + RdpdrServerPrivate* priv = context->priv; + + RDPDR_IRP* irp = nullptr; +- UINT ret = ++ const UINT ret = + prepare_smartcard_irp(context, SCARD_IOCTL_GETATTRIB, + rdpdr_server_smartcard_get_attrib_callback, callbackData, &irp); + if (ret != CHANNEL_RC_OK) +@@ -5437,25 +5441,32 @@ static UINT rdpdr_server_smartcard_get_attrib(RdpdrServerContext* context, void* + const SMARTCARD_OPERATION op = { .ioControlCode = SCARD_IOCTL_GETATTRIB, + .call.getAttrib = *call }; + ++ UINT error = CHANNEL_RC_NO_MEMORY; + wStream* s = Stream_New(nullptr, 64); + if (!s) + { + WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!"); +- return CHANNEL_RC_NO_MEMORY; ++ goto out; + } + + LONG status = smartcard_irp_device_control_encode_request(s, &op); + if (status != SCARD_S_SUCCESS) + { +- Stream_Release(s); +- return ERROR_INTERNAL_ERROR; ++ error = ERROR_INTERNAL_ERROR; ++ goto out; + } + +- const UINT error = rdpdr_server_send_device_control_request( ++ error = rdpdr_server_send_device_control_request( + context, irp->DeviceId, irp->CompletionId, irp->IoControlCode, + SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s)); + +- Stream_Release(s); ++out: ++ // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there ++ if (error != CHANNEL_RC_OK && irp) ++ rdpdr_server_discard_request(context, irp->CompletionId); ++ // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there ++ if (s) ++ Stream_Release(s); + return error; + } + + +From 07bee6f35bc236cbe99e9240aa4c8ac2d923b68a Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Tue, 30 Jun 2026 09:35:36 +0200 +Subject: [PATCH 31/31] [utils,test] replace NULL with nullptr + +--- + libfreerdp/utils/test/TestSmartcardOperations.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/libfreerdp/utils/test/TestSmartcardOperations.c b/libfreerdp/utils/test/TestSmartcardOperations.c +index 590d9cc6fb93..19969df9794e 100644 +--- a/libfreerdp/utils/test/TestSmartcardOperations.c ++++ b/libfreerdp/utils/test/TestSmartcardOperations.c +@@ -783,7 +783,7 @@ static wStream* build_device_control_response(UINT32 ioControlCode, const SMARTC + { + const char* name = scard_get_ioctl_string(ioControlCode, TRUE); + +- wStream* payload = Stream_New(NULL, 4096); ++ wStream* payload = Stream_New(nullptr, 4096); + if (!payload) + { + (void)fprintf(stderr, "%s: Stream_New for payload failed\n", name); +@@ -800,7 +800,7 @@ static wStream* build_device_control_response(UINT32 ioControlCode, const SMARTC + } + Stream_SealLength(payload); + +- wStream* s = Stream_New(NULL, 256 + Stream_Length(payload)); ++ wStream* s = Stream_New(nullptr, 256 + Stream_Length(payload)); + if (!s) + { + (void)fprintf(stderr, "%s: Stream_New for response failed\n", name); diff --git a/utils-pdu-tracker-clean-up-warnings-fix-poll-return.patch b/utils-pdu-tracker-clean-up-warnings-fix-poll-return.patch new file mode 100644 index 0000000..e8f9e2e --- /dev/null +++ b/utils-pdu-tracker-clean-up-warnings-fix-poll-return.patch @@ -0,0 +1,156 @@ +From 1a15128e93705d5680c84b9e6368245d247fe893 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Mon, 29 Jun 2026 10:23:37 +0200 +Subject: [PATCH] [utils,pdu-tracker] clean up warnings, fix poll return + +--- + libfreerdp/utils/channel_pdu_tracker.c | 87 +++++++++++++++----------- + 1 file changed, 52 insertions(+), 35 deletions(-) + +diff --git a/libfreerdp/utils/channel_pdu_tracker.c b/libfreerdp/utils/channel_pdu_tracker.c +index 18e9aab..2b46a98 100644 +--- a/libfreerdp/utils/channel_pdu_tracker.c ++++ b/libfreerdp/utils/channel_pdu_tracker.c +@@ -21,16 +21,18 @@ + + #include + ++#include ++#define TTAG FREERDP_TAG("utils.ChannelPduTracker") ++ + struct ChannelPduTracker + { + HANDLE vc; + wStream* currentPacket; +- BYTE buffer[CHANNEL_PDU_LENGTH]; +- BYTE* writePtr; ++ char buffer[CHANNEL_PDU_LENGTH]; ++ size_t offset; ++ wLog* log; + }; + +-#define TAG "ChannelPduTracker" +- + wStream* ChannelPduTracker_poll(ChannelPduTracker* tracker, BOOL* ok) + { + WINPR_ASSERT(tracker); +@@ -38,58 +40,70 @@ wStream* ChannelPduTracker_poll(ChannelPduTracker* tracker, BOOL* ok) + + ULONG sz = 0; + *ok = FALSE; +- const ULONG readSz = +- WINPR_ASSERTING_INT_CAST(ULONG, CHANNEL_PDU_LENGTH - (tracker->writePtr - tracker->buffer)); +- if (!WTSVirtualChannelRead(tracker->vc, INFINITE, (char*)tracker->writePtr, readSz, &sz)) ++ ++ WINPR_ASSERT(tracker->offset <= CHANNEL_PDU_LENGTH); ++ const ULONG readSz = WINPR_ASSERTING_INT_CAST(ULONG, CHANNEL_PDU_LENGTH - tracker->offset); ++ if (!WTSVirtualChannelRead(tracker->vc, INFINITE, &tracker->buffer[tracker->offset], readSz, ++ &sz)) + return nullptr; + +- tracker->writePtr += sz; +- const size_t recvSz = (tracker->writePtr - tracker->buffer); ++ tracker->offset += sz; ++ WINPR_ASSERT(tracker->offset <= CHANNEL_PDU_LENGTH); ++ ++ const size_t recvSz = tracker->offset; + if (recvSz < sizeof(CHANNEL_PDU_HEADER)) + { + *ok = TRUE; + return nullptr; + } + +- CHANNEL_PDU_HEADER* header = (CHANNEL_PDU_HEADER*)tracker->buffer; ++ const CHANNEL_PDU_HEADER* header = (const CHANNEL_PDU_HEADER*)tracker->buffer; + if (header->length > CHANNEL_CHUNK_LENGTH) + { +- WLog_ERR(TAG, "chunk size %" PRIu32 " is too big", header->length); ++ WLog_Print(tracker->log, WLOG_ERROR, "chunk size %" PRIu32 " is too big", header->length); + return nullptr; + } + +- if (recvSz - sizeof(CHANNEL_PDU_HEADER) < header->length) ++ const size_t actual = recvSz - sizeof(CHANNEL_PDU_HEADER); ++ if (actual != header->length) + { +- WLog_ERR(TAG, "chunk size %" PRIu32 " is too big", header->length); ++ WLog_Print(tracker->log, WLOG_ERROR, ++ "Expected chunk size %" PRIu32 " does not match received data size %" PRIuz, ++ header->length, actual); + return nullptr; + } + +- tracker->writePtr = tracker->buffer; +- if (header->flags & CHANNEL_FLAG_FIRST) ++ tracker->offset = 0; ++ if ((header->flags & CHANNEL_FLAG_FIRST) != 0) + Stream_ResetPosition(tracker->currentPacket); + +- if (!Stream_EnsureRemainingCapacity(tracker->currentPacket, header->length)) ++ if (!Stream_CheckAndLogRequiredCapacityWLog(tracker->log, tracker->currentPacket, ++ header->length)) + return nullptr; + +- Stream_Write(tracker->currentPacket, header + 1, header->length); ++ Stream_Write(tracker->currentPacket, &tracker->buffer[sizeof(CHANNEL_PDU_HEADER)], ++ header->length); + +- if (header->flags & CHANNEL_FLAG_LAST) ++ if ((header->flags & CHANNEL_FLAG_LAST) == 0) + { +- Stream_SealLength(tracker->currentPacket); +- Stream_ResetPosition(tracker->currentPacket); +- wStream* ret = tracker->currentPacket; +- +- tracker->currentPacket = Stream_New(nullptr, 4096); +- if (!tracker->currentPacket) +- { +- Stream_Release(ret); +- WLog_ERR(TAG, "error allocating new currentPacket"); +- return nullptr; +- } ++ *ok = TRUE; ++ return nullptr; ++ } ++ ++ Stream_SealLength(tracker->currentPacket); ++ Stream_ResetPosition(tracker->currentPacket); ++ wStream* ret = tracker->currentPacket; ++ ++ tracker->currentPacket = Stream_New(nullptr, 4096); ++ if (!tracker->currentPacket) ++ { ++ Stream_Release(ret); ++ WLog_Print(tracker->log, WLOG_ERROR, "error allocating new currentPacket"); ++ return nullptr; + } + + *ok = TRUE; +- return nullptr; ++ return ret; + } + + void ChannelPduTracker_free(ChannelPduTracker* tracker) +@@ -109,11 +123,14 @@ ChannelPduTracker* ChannelPduTracker_new(HANDLE vc) + + ret->currentPacket = Stream_New(nullptr, 4096); + if (!ret->currentPacket) +- { +- free(ret); +- return nullptr; +- } ++ goto fail; ++ + ret->vc = vc; +- ret->writePtr = ret->buffer; ++ ret->log = WLog_Get(TTAG); ++ if (!ret->log) ++ goto fail; + return ret; ++fail: ++ ChannelPduTracker_free(ret); ++ return nullptr; + } +-- +2.54.0 + diff --git a/utils-pdu-tracker-increase-capacity-for-fragmented-P.patch b/utils-pdu-tracker-increase-capacity-for-fragmented-P.patch new file mode 100644 index 0000000..cc20885 --- /dev/null +++ b/utils-pdu-tracker-increase-capacity-for-fragmented-P.patch @@ -0,0 +1,26 @@ +From a41855e88ea37b92a4d506430bce4b09e76f7d76 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Mon, 29 Jun 2026 15:50:18 +0200 +Subject: [PATCH] [utils,pdu-tracker] increase capacity for fragmented PDU + +--- + libfreerdp/utils/channel_pdu_tracker.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/libfreerdp/utils/channel_pdu_tracker.c b/libfreerdp/utils/channel_pdu_tracker.c +index 2b46a98..63c112d 100644 +--- a/libfreerdp/utils/channel_pdu_tracker.c ++++ b/libfreerdp/utils/channel_pdu_tracker.c +@@ -77,8 +77,7 @@ wStream* ChannelPduTracker_poll(ChannelPduTracker* tracker, BOOL* ok) + if ((header->flags & CHANNEL_FLAG_FIRST) != 0) + Stream_ResetPosition(tracker->currentPacket); + +- if (!Stream_CheckAndLogRequiredCapacityWLog(tracker->log, tracker->currentPacket, +- header->length)) ++ if (!Stream_EnsureRemainingCapacity(tracker->currentPacket, header->length)) + return nullptr; + + Stream_Write(tracker->currentPacket, &tracker->buffer[sizeof(CHANNEL_PDU_HEADER)], +-- +2.54.0 + diff --git a/utils-scard-fix-endianess-issue.patch b/utils-scard-fix-endianess-issue.patch new file mode 100644 index 0000000..8534a50 --- /dev/null +++ b/utils-scard-fix-endianess-issue.patch @@ -0,0 +1,36 @@ +From 2a6d532a1d633145ae48e55ae471f2f2d8e56157 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Fri, 19 Jun 2026 20:15:15 +0200 +Subject: [PATCH] [utils,scard] fix endianess issue + +--- + libfreerdp/utils/smartcard_pack.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/libfreerdp/utils/smartcard_pack.c b/libfreerdp/utils/smartcard_pack.c +index bb977c4..83d1fbd 100644 +--- a/libfreerdp/utils/smartcard_pack.c ++++ b/libfreerdp/utils/smartcard_pack.c +@@ -319,14 +319,15 @@ static LONG smartcard_ndr_read_atrmask(wLog* log, wStream* s, LocateCards_ATRMas + /* [MS-RDPESC] 2.2.1.5: cbAtr is range(0..36), the number of valid bytes in the fixed + * rgbAtr/rgbMask arrays. A larger value walks past them when the mask is compared byte + * by byte, so reject it here rather than trusting the wire value. */ +- const LocateCards_ATRMask* masks = *data; ++ LocateCards_ATRMask* masks = *data; + for (size_t x = 0; x < min; x++) + { +- if (masks[x].cbAtr > ARRAYSIZE(masks[x].rgbAtr)) ++ const UINT32 val = winpr_Data_Get_UINT32(&masks[x].cbAtr); ++ if (val > ARRAYSIZE(masks[x].rgbAtr)) + { + WLog_Print(log, WLOG_ERROR, +- "LocateCards_ATRMask[%" PRIuz "]::cbAtr %" PRIu32 " exceeds %" PRIuz, x, +- masks[x].cbAtr, (size_t)ARRAYSIZE(masks[x].rgbAtr)); ++ "LocateCards_ATRMask[%" PRIuz "]::cbAtr %" PRIu32 " exceeds %" PRIuz, x, val, ++ (size_t)ARRAYSIZE(masks[x].rgbAtr)); + free(*data); + *data = nullptr; + return STATUS_DATA_ERROR; +-- +2.54.0 + diff --git a/utils-smartcard-validate-cbAtr-against-rgbAtr-size-o.patch b/utils-smartcard-validate-cbAtr-against-rgbAtr-size-o.patch new file mode 100644 index 0000000..4c1f40a --- /dev/null +++ b/utils-smartcard-validate-cbAtr-against-rgbAtr-size-o.patch @@ -0,0 +1,189 @@ +From ececc5cbd0646ef012ca178322e6ef922a11f0c1 Mon Sep 17 00:00:00 2001 +From: metsw24-max +Date: Fri, 19 Jun 2026 12:32:01 +0530 +Subject: [PATCH] [utils,smartcard] validate cbAtr against rgbAtr size on + unpack + +--- + libfreerdp/utils/smartcard_pack.c | 36 ++++++++- + libfreerdp/utils/test/CMakeLists.txt | 2 +- + libfreerdp/utils/test/TestSmartcardPack.c | 93 +++++++++++++++++++++++ + 3 files changed, 129 insertions(+), 2 deletions(-) + create mode 100644 libfreerdp/utils/test/TestSmartcardPack.c + +diff --git a/libfreerdp/utils/smartcard_pack.c b/libfreerdp/utils/smartcard_pack.c +index d8f9cab..bb977c4 100644 +--- a/libfreerdp/utils/smartcard_pack.c ++++ b/libfreerdp/utils/smartcard_pack.c +@@ -312,7 +312,27 @@ static LONG smartcard_ndr_read_atrmask(wLog* log, wStream* s, LocateCards_ATRMas + BYTE** ppv; + } u; + u.ppc = data; +- return smartcard_ndr_read(log, s, u.ppv, min, sizeof(LocateCards_ATRMask), type); ++ const LONG status = smartcard_ndr_read(log, s, u.ppv, min, sizeof(LocateCards_ATRMask), type); ++ if (status != SCARD_S_SUCCESS) ++ return status; ++ ++ /* [MS-RDPESC] 2.2.1.5: cbAtr is range(0..36), the number of valid bytes in the fixed ++ * rgbAtr/rgbMask arrays. A larger value walks past them when the mask is compared byte ++ * by byte, so reject it here rather than trusting the wire value. */ ++ const LocateCards_ATRMask* masks = *data; ++ for (size_t x = 0; x < min; x++) ++ { ++ if (masks[x].cbAtr > ARRAYSIZE(masks[x].rgbAtr)) ++ { ++ WLog_Print(log, WLOG_ERROR, ++ "LocateCards_ATRMask[%" PRIuz "]::cbAtr %" PRIu32 " exceeds %" PRIuz, x, ++ masks[x].cbAtr, (size_t)ARRAYSIZE(masks[x].rgbAtr)); ++ free(*data); ++ *data = nullptr; ++ return STATUS_DATA_ERROR; ++ } ++ } ++ return SCARD_S_SUCCESS; + } + + static LONG smartcard_ndr_read_fixed_string_a(wLog* log, wStream* s, CHAR** data, size_t min, +@@ -2454,6 +2474,13 @@ static LONG smartcard_unpack_reader_state_a(wLog* log, wStream* s, LPSCARD_READE + Stream_Read_UINT32(s, readerState->dwCurrentState); /* dwCurrentState (4 bytes) */ + Stream_Read_UINT32(s, readerState->dwEventState); /* dwEventState (4 bytes) */ + Stream_Read_UINT32(s, readerState->cbAtr); /* cbAtr (4 bytes) */ ++ if (readerState->cbAtr > ARRAYSIZE(readerState->rgbAtr)) ++ { ++ WLog_Print(log, WLOG_ERROR, ++ "SCARD_READERSTATEA[%" PRIu32 "]::cbAtr %" PRIu32 " exceeds %" PRIuz, index, ++ readerState->cbAtr, (size_t)ARRAYSIZE(readerState->rgbAtr)); ++ goto fail; ++ } + Stream_Read(s, readerState->rgbAtr, 36); /* rgbAtr [0..36] (36 bytes) */ + } + +@@ -2528,6 +2555,13 @@ static LONG smartcard_unpack_reader_state_w(wLog* log, wStream* s, LPSCARD_READE + Stream_Read_UINT32(s, readerState->dwCurrentState); /* dwCurrentState (4 bytes) */ + Stream_Read_UINT32(s, readerState->dwEventState); /* dwEventState (4 bytes) */ + Stream_Read_UINT32(s, readerState->cbAtr); /* cbAtr (4 bytes) */ ++ if (readerState->cbAtr > ARRAYSIZE(readerState->rgbAtr)) ++ { ++ WLog_Print(log, WLOG_ERROR, ++ "SCARD_READERSTATEW[%" PRIu32 "]::cbAtr %" PRIu32 " exceeds %" PRIuz, index, ++ readerState->cbAtr, (size_t)ARRAYSIZE(readerState->rgbAtr)); ++ goto fail; ++ } + Stream_Read(s, readerState->rgbAtr, 36); /* rgbAtr [0..36] (36 bytes) */ + } + +diff --git a/libfreerdp/utils/test/CMakeLists.txt b/libfreerdp/utils/test/CMakeLists.txt +index b90c5f4..0c2bf84 100644 +--- a/libfreerdp/utils/test/CMakeLists.txt ++++ b/libfreerdp/utils/test/CMakeLists.txt +@@ -5,7 +5,7 @@ disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR}) + + set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +-set(${MODULE_PREFIX}_TESTS TestRingBuffer.c TestPodArrays.c TestEncodedTypes.c) ++set(${MODULE_PREFIX}_TESTS TestRingBuffer.c TestPodArrays.c TestEncodedTypes.c TestSmartcardPack.c) + + create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} ${${MODULE_PREFIX}_TESTS}) + +diff --git a/libfreerdp/utils/test/TestSmartcardPack.c b/libfreerdp/utils/test/TestSmartcardPack.c +new file mode 100644 +index 0000000..8c04600 +--- /dev/null ++++ b/libfreerdp/utils/test/TestSmartcardPack.c +@@ -0,0 +1,93 @@ ++/** ++ * FreeRDP: A Remote Desktop Protocol Implementation ++ * ++ * 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 ++ ++/* Build a LocateCardsByATRA call (cbContext=0, cAtrs=1, cReaders=0) holding a single ++ * LocateCards_ATRMask whose cbAtr is set to the requested value. */ ++static wStream* build_locate_cards_by_atr_a(UINT32 cbAtr) ++{ ++ wStream* s = Stream_New(NULL, 24 + 8 + sizeof(LocateCards_ATRMask)); ++ if (!s) ++ return NULL; ++ ++ Stream_Write_UINT32(s, 0); /* cbContext = 0 */ ++ Stream_Write_UINT32(s, 0); /* pbContextNdrPtr = 0 */ ++ Stream_Write_UINT32(s, 1); /* cAtrs = 1 */ ++ Stream_Write_UINT32(s, 0x00020000); /* rgAtrMasksNdrPtr */ ++ Stream_Write_UINT32(s, 0); /* cReaders = 0 */ ++ Stream_Write_UINT32(s, 0); /* rgReaderStatesNdrPtr = 0 */ ++ Stream_Write_UINT32(s, 1); /* conformant count = cAtrs */ ++ Stream_Write_UINT32(s, cbAtr); /* LocateCards_ATRMask::cbAtr */ ++ Stream_Zero(s, 36); /* rgbAtr[36] */ ++ Stream_Zero(s, 36); /* rgbMask[36] */ ++ ++ Stream_SealLength(s); ++ if (!Stream_SetPosition(s, 0)) ++ { ++ Stream_Free(s, TRUE); ++ return NULL; ++ } ++ return s; ++} ++ ++static BOOL run_case(UINT32 cbAtr, BOOL expectAccept) ++{ ++ wStream* s = build_locate_cards_by_atr_a(cbAtr); ++ if (!s) ++ return FALSE; ++ ++ LocateCardsByATRA_Call call = { 0 }; ++ const LONG status = smartcard_unpack_locate_cards_by_atr_a_call(s, &call); ++ Stream_Free(s, TRUE); ++ ++ const BOOL accepted = (status == SCARD_S_SUCCESS) ? TRUE : FALSE; ++ if (accepted != expectAccept) ++ { ++ printf("cbAtr=%" PRIu32 ": expected %s, unpack returned 0x%08" PRIX32 "\n", cbAtr, ++ expectAccept ? "accept" : "reject", (UINT32)status); ++ free(call.rgAtrMasks); ++ return FALSE; ++ } ++ ++ free(call.rgAtrMasks); ++ return TRUE; ++} ++ ++int TestSmartcardPack(int argc, char* argv[]) ++{ ++ WINPR_UNUSED(argc); ++ WINPR_UNUSED(argv); ++ ++ /* Valid: cbAtr within the fixed 36-byte rgbAtr/rgbMask arrays. */ ++ if (!run_case(0, TRUE)) ++ return -1; ++ if (!run_case(36, TRUE)) ++ return -1; ++ ++ /* Out of range: cbAtr larger than the buffers would later overrun them. */ ++ if (!run_case(37, FALSE)) ++ return -1; ++ if (!run_case(0xFFFFFFFF, FALSE)) ++ return -1; ++ ++ return 0; ++} +-- +2.54.0 + diff --git a/utils-test-replace-NULL.patch b/utils-test-replace-NULL.patch new file mode 100644 index 0000000..0b1ea16 --- /dev/null +++ b/utils-test-replace-NULL.patch @@ -0,0 +1,46 @@ +From 5cdd46594ec792715a00ecb935dcac7aadde0fc3 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Fri, 19 Jun 2026 20:58:44 +0200 +Subject: [PATCH] [utils,test] replace NULL + +--- + libfreerdp/utils/test/TestSmartcardPack.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/libfreerdp/utils/test/TestSmartcardPack.c b/libfreerdp/utils/test/TestSmartcardPack.c +index 8c04600..4c465f1 100644 +--- a/libfreerdp/utils/test/TestSmartcardPack.c ++++ b/libfreerdp/utils/test/TestSmartcardPack.c +@@ -25,9 +25,9 @@ + * LocateCards_ATRMask whose cbAtr is set to the requested value. */ + static wStream* build_locate_cards_by_atr_a(UINT32 cbAtr) + { +- wStream* s = Stream_New(NULL, 24 + 8 + sizeof(LocateCards_ATRMask)); ++ wStream* s = Stream_New(nullptr, 24 + 8 + sizeof(LocateCards_ATRMask)); + if (!s) +- return NULL; ++ return nullptr; + + Stream_Write_UINT32(s, 0); /* cbContext = 0 */ + Stream_Write_UINT32(s, 0); /* pbContextNdrPtr = 0 */ +@@ -44,7 +44,7 @@ static wStream* build_locate_cards_by_atr_a(UINT32 cbAtr) + if (!Stream_SetPosition(s, 0)) + { + Stream_Free(s, TRUE); +- return NULL; ++ return nullptr; + } + return s; + } +@@ -59,7 +59,7 @@ static BOOL run_case(UINT32 cbAtr, BOOL expectAccept) + const LONG status = smartcard_unpack_locate_cards_by_atr_a_call(s, &call); + Stream_Free(s, TRUE); + +- const BOOL accepted = (status == SCARD_S_SUCCESS) ? TRUE : FALSE; ++ const BOOL accepted = status == SCARD_S_SUCCESS; + if (accepted != expectAccept) + { + printf("cbAtr=%" PRIu32 ": expected %s, unpack returned 0x%08" PRIX32 "\n", cbAtr, +-- +2.54.0 +