summaryrefslogtreecommitdiffstats
path: root/libfreerdp/utils
diff options
context:
space:
mode:
Diffstat (limited to 'libfreerdp/utils')
-rw-r--r--libfreerdp/utils/CMakeLists.txt56
-rw-r--r--libfreerdp/utils/cliprdr_utils.c258
-rw-r--r--libfreerdp/utils/drdynvc.c50
-rw-r--r--libfreerdp/utils/encoded_types.c187
-rw-r--r--libfreerdp/utils/gfx.c95
-rw-r--r--libfreerdp/utils/http.c386
-rw-r--r--libfreerdp/utils/passphrase.c299
-rw-r--r--libfreerdp/utils/pcap.c286
-rw-r--r--libfreerdp/utils/profiler.c98
-rw-r--r--libfreerdp/utils/rdpdr_utils.c598
-rw-r--r--libfreerdp/utils/ringbuffer.c295
-rw-r--r--libfreerdp/utils/signal.c264
-rw-r--r--libfreerdp/utils/smartcard_call.c2019
-rw-r--r--libfreerdp/utils/smartcard_operations.c1048
-rw-r--r--libfreerdp/utils/smartcard_pack.c3649
-rw-r--r--libfreerdp/utils/stopwatch.c98
-rw-r--r--libfreerdp/utils/string.c105
-rw-r--r--libfreerdp/utils/test/CMakeLists.txt28
-rw-r--r--libfreerdp/utils/test/TestPodArrays.c131
-rw-r--r--libfreerdp/utils/test/TestRingBuffer.c226
20 files changed, 10176 insertions, 0 deletions
diff --git a/libfreerdp/utils/CMakeLists.txt b/libfreerdp/utils/CMakeLists.txt
new file mode 100644
index 0000000..52690bf
--- /dev/null
+++ b/libfreerdp/utils/CMakeLists.txt
@@ -0,0 +1,56 @@
+# FreeRDP: A Remote Desktop Protocol Implementation
+# libfreerdp-utils cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(MODULE_NAME "freerdp-utils")
+set(MODULE_PREFIX "FREERDP_UTILS")
+
+set(${MODULE_PREFIX}_SRCS
+ encoded_types.c
+ passphrase.c
+ cliprdr_utils.c
+ rdpdr_utils.c
+ pcap.c
+ profiler.c
+ ringbuffer.c
+ signal.c
+ string.c
+ gfx.c
+ drdynvc.c
+ smartcard_operations.c
+ smartcard_pack.c
+ smartcard_call.c
+ stopwatch.c
+ http.c
+)
+
+freerdp_module_add(${${MODULE_PREFIX}_SRCS})
+
+freerdp_library_add(${CMAKE_THREAD_LIBS_INIT})
+
+if(WIN32)
+ freerdp_library_add(ws2_32)
+ freerdp_library_add(credui)
+ freerdp_library_add(cfgmgr32)
+endif()
+
+if(${CMAKE_SYSTEM_NAME} MATCHES SunOS)
+ freerdp_library_add(rt)
+endif()
+
+if(BUILD_TESTING)
+ add_subdirectory(test)
+endif()
diff --git a/libfreerdp/utils/cliprdr_utils.c b/libfreerdp/utils/cliprdr_utils.c
new file mode 100644
index 0000000..5835ab6
--- /dev/null
+++ b/libfreerdp/utils/cliprdr_utils.c
@@ -0,0 +1,258 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Clipboard Virtual Channel Extension
+ *
+ * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2022 Armin Novak <anovak@thincast.com
+ * Copyright 2022 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/stream.h>
+#include <freerdp/utils/cliprdr_utils.h>
+#include <freerdp/channels/cliprdr.h>
+
+#include <freerdp/log.h>
+#define TAG FREERDP_TAG("utils." CLIPRDR_SVC_CHANNEL_NAME)
+
+#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520)
+#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024)
+
+static UINT64 filetime_to_uint64(FILETIME value)
+{
+ UINT64 converted = 0;
+ converted |= (UINT32)value.dwHighDateTime;
+ converted <<= 32;
+ converted |= (UINT32)value.dwLowDateTime;
+ return converted;
+}
+
+static FILETIME uint64_to_filetime(UINT64 value)
+{
+ FILETIME converted;
+ converted.dwLowDateTime = (UINT32)(value >> 0);
+ converted.dwHighDateTime = (UINT32)(value >> 32);
+ return converted;
+}
+
+/**
+ * Parse a packed file list.
+ *
+ * The resulting array must be freed with the `free()` function.
+ *
+ * @param [in] format_data packed `CLIPRDR_FILELIST` to parse.
+ * @param [in] format_data_length length of `format_data` in bytes.
+ * @param [out] file_descriptor_array parsed array of `FILEDESCRIPTOR` structs.
+ * @param [out] file_descriptor_count number of elements in `file_descriptor_array`.
+ *
+ * @returns 0 on success, otherwise a Win32 error code.
+ */
+UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
+ FILEDESCRIPTORW** file_descriptor_array, UINT32* file_descriptor_count)
+{
+ UINT result = NO_ERROR;
+ UINT32 count = 0;
+ wStream sbuffer;
+ wStream* s = NULL;
+
+ if (!format_data || !file_descriptor_array || !file_descriptor_count)
+ return ERROR_BAD_ARGUMENTS;
+
+ s = Stream_StaticConstInit(&sbuffer, format_data, format_data_length);
+ if (!s)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ {
+ result = ERROR_INCORRECT_SIZE;
+ goto out;
+ }
+
+ Stream_Read_UINT32(s, count); /* cItems (4 bytes) */
+
+ if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, count, CLIPRDR_FILEDESCRIPTOR_SIZE))
+ {
+ result = ERROR_INCORRECT_SIZE;
+ goto out;
+ }
+
+ *file_descriptor_count = count;
+ *file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTORW));
+ if (!*file_descriptor_array)
+ {
+ result = ERROR_NOT_ENOUGH_MEMORY;
+ goto out;
+ }
+
+ for (UINT32 i = 0; i < count; i++)
+ {
+ FILEDESCRIPTORW* file = &((*file_descriptor_array)[i]);
+
+ if (!cliprdr_read_filedescriptor(s, file))
+ goto out;
+ }
+
+ if (Stream_GetRemainingLength(s) > 0)
+ WLog_WARN(TAG, "packed file list has %" PRIuz " excess bytes",
+ Stream_GetRemainingLength(s));
+out:
+
+ return result;
+}
+
+BOOL cliprdr_read_filedescriptor(wStream* s, FILEDESCRIPTORW* file)
+{
+ UINT64 tmp = 0;
+ WINPR_ASSERT(file);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(FILEDESCRIPTORW)))
+ return FALSE;
+
+ Stream_Read_UINT32(s, file->dwFlags); /* flags (4 bytes) */
+ Stream_Read_UINT32(s, file->clsid.Data1);
+ Stream_Read_UINT16(s, file->clsid.Data2);
+ Stream_Read_UINT16(s, file->clsid.Data3);
+ Stream_Read(s, &file->clsid.Data4, sizeof(file->clsid.Data4));
+ Stream_Read_INT32(s, file->sizel.cx);
+ Stream_Read_INT32(s, file->sizel.cy);
+ Stream_Read_INT32(s, file->pointl.x);
+ Stream_Read_INT32(s, file->pointl.y);
+ Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
+ Stream_Read_UINT64(s, tmp); /* ftCreationTime (8 bytes) */
+ file->ftCreationTime = uint64_to_filetime(tmp);
+ Stream_Read_UINT64(s, tmp); /* ftLastAccessTime (8 bytes) */
+ file->ftLastAccessTime = uint64_to_filetime(tmp);
+ Stream_Read_UINT64(s, tmp); /* lastWriteTime (8 bytes) */
+ file->ftLastWriteTime = uint64_to_filetime(tmp);
+ Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
+ Stream_Read_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
+ Stream_Read_UTF16_String(s, file->cFileName,
+ ARRAYSIZE(file->cFileName)); /* cFileName (520 bytes) */
+ return TRUE;
+}
+
+BOOL cliprdr_write_filedescriptor(wStream* s, const FILEDESCRIPTORW* file)
+{
+ WINPR_ASSERT(file);
+
+ if (!Stream_EnsureRemainingCapacity(s, sizeof(FILEDESCRIPTORW)))
+ return FALSE;
+
+ Stream_Write_UINT32(s, file->dwFlags); /* flags (4 bytes) */
+
+ Stream_Write_UINT32(s, file->clsid.Data1);
+ Stream_Write_UINT16(s, file->clsid.Data2);
+ Stream_Write_UINT16(s, file->clsid.Data3);
+ Stream_Write(s, &file->clsid.Data4, sizeof(file->clsid.Data4));
+ Stream_Write_INT32(s, file->sizel.cx);
+ Stream_Write_INT32(s, file->sizel.cy);
+ Stream_Write_INT32(s, file->pointl.x);
+ Stream_Write_INT32(s, file->pointl.y);
+ Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
+ Stream_Write_UINT64(s, filetime_to_uint64(file->ftCreationTime));
+ Stream_Write_UINT64(s, filetime_to_uint64(file->ftLastAccessTime));
+ Stream_Write_UINT64(s, filetime_to_uint64(file->ftLastWriteTime)); /* lastWriteTime (8 bytes) */
+ Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
+ Stream_Write_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
+ Stream_Write_UTF16_String(s, file->cFileName,
+ ARRAYSIZE(file->cFileName)); /* cFileName (520 bytes) */
+ return TRUE;
+}
+
+/**
+ * Serialize a packed file list.
+ *
+ * The resulting format data must be freed with the `free()` function.
+ *
+ * @param [in] file_descriptor_array array of `FILEDESCRIPTOR` structs to serialize.
+ * @param [in] file_descriptor_count number of elements in `file_descriptor_array`.
+ * @param [out] format_data serialized CLIPRDR_FILELIST.
+ * @param [out] format_data_length length of `format_data` in bytes.
+ *
+ * @returns 0 on success, otherwise a Win32 error code.
+ */
+UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array,
+ UINT32 file_descriptor_count, BYTE** format_data,
+ UINT32* format_data_length)
+{
+ return cliprdr_serialize_file_list_ex(CB_STREAM_FILECLIP_ENABLED, file_descriptor_array,
+ file_descriptor_count, format_data, format_data_length);
+}
+
+UINT cliprdr_serialize_file_list_ex(UINT32 flags, const FILEDESCRIPTORW* file_descriptor_array,
+ UINT32 file_descriptor_count, BYTE** format_data,
+ UINT32* format_data_length)
+{
+ UINT result = NO_ERROR;
+ size_t len = 0;
+ wStream* s = NULL;
+
+ if (!file_descriptor_array || !format_data || !format_data_length)
+ return ERROR_BAD_ARGUMENTS;
+
+ if ((flags & CB_STREAM_FILECLIP_ENABLED) == 0)
+ {
+ WLog_WARN(TAG, "No file clipboard support annouonced!");
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE);
+ if (!s)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */
+
+ for (UINT32 i = 0; i < file_descriptor_count; i++)
+ {
+ const FILEDESCRIPTORW* file = &file_descriptor_array[i];
+
+ /*
+ * There is a known issue with Windows server getting stuck in
+ * an infinite loop when downloading files that are larger than
+ * 2 gigabytes. Do not allow clients to send such file lists.
+ *
+ * https://support.microsoft.com/en-us/help/2258090
+ */
+ if ((flags & CB_HUGE_FILE_SUPPORT_ENABLED) == 0)
+ {
+ if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE))
+ {
+ WLog_ERR(TAG, "cliprdr does not support files over 2 GB");
+ result = ERROR_FILE_TOO_LARGE;
+ goto error;
+ }
+ }
+
+ if (!cliprdr_write_filedescriptor(s, file))
+ goto error;
+ }
+
+ Stream_SealLength(s);
+
+ Stream_GetBuffer(s, *format_data);
+ Stream_GetLength(s, len);
+ if (len > UINT32_MAX)
+ goto error;
+
+ *format_data_length = (UINT32)len;
+
+ Stream_Free(s, FALSE);
+
+ return result;
+
+error:
+ Stream_Free(s, TRUE);
+
+ return result;
+}
diff --git a/libfreerdp/utils/drdynvc.c b/libfreerdp/utils/drdynvc.c
new file mode 100644
index 0000000..ba9bf5a
--- /dev/null
+++ b/libfreerdp/utils/drdynvc.c
@@ -0,0 +1,50 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * drdynvc Utils - Helper functions converting something to string
+ *
+ * Copyright 2023 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2023 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/utils/drdynvc.h>
+#include <freerdp/channels/drdynvc.h>
+
+const char* drdynvc_get_packet_type(BYTE cmd)
+{
+ switch (cmd)
+ {
+ case CREATE_REQUEST_PDU:
+ return "CREATE_REQUEST_PDU";
+ case DATA_FIRST_PDU:
+ return "DATA_FIRST_PDU";
+ case DATA_PDU:
+ return "DATA_PDU";
+ case CLOSE_REQUEST_PDU:
+ return "CLOSE_REQUEST_PDU";
+ case CAPABILITY_REQUEST_PDU:
+ return "CAPABILITY_REQUEST_PDU";
+ case DATA_FIRST_COMPRESSED_PDU:
+ return "DATA_FIRST_COMPRESSED_PDU";
+ case DATA_COMPRESSED_PDU:
+ return "DATA_COMPRESSED_PDU";
+ case SOFT_SYNC_REQUEST_PDU:
+ return "SOFT_SYNC_REQUEST_PDU";
+ case SOFT_SYNC_RESPONSE_PDU:
+ return "SOFT_SYNC_RESPONSE_PDU";
+ default:
+ return "UNKNOWN";
+ }
+}
diff --git a/libfreerdp/utils/encoded_types.c b/libfreerdp/utils/encoded_types.c
new file mode 100644
index 0000000..68e6e4d
--- /dev/null
+++ b/libfreerdp/utils/encoded_types.c
@@ -0,0 +1,187 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Helper functions to parse encoded types into regular ones
+ *
+ * Copyright 2023 Pascal Nowack <Pascal.Nowack@gmx.de>
+ *
+ * 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 <freerdp/config.h>
+
+#include <freerdp/channels/log.h>
+#include <freerdp/utils/encoded_types.h>
+#include <math.h>
+
+#define TAG CHANNELS_TAG("encoded_types")
+
+typedef enum
+{
+ ONE_BYTE_VAL,
+ TWO_BYTE_VAL,
+ THREE_BYTE_VAL,
+ FOUR_BYTE_VAL,
+ FIVE_BYTE_VAL,
+ SIX_BYTE_VAL,
+ SEVEN_BYTE_VAL,
+ EIGHT_BYTE_VAL,
+} EncodedTypeByteCount;
+
+typedef enum
+{
+ POSITIVE_VAL,
+ NEGATIVE_VAL,
+} EncodedTypeSign;
+
+typedef struct
+{
+ EncodedTypeByteCount c;
+ EncodedTypeSign s;
+ BYTE val1;
+ BYTE val2;
+ BYTE val3;
+ BYTE val4;
+} FOUR_BYTE_SIGNED_INTEGER;
+
+typedef struct
+{
+ EncodedTypeByteCount c;
+ EncodedTypeSign s;
+ BYTE e;
+ BYTE val1;
+ BYTE val2;
+ BYTE val3;
+ BYTE val4;
+} FOUR_BYTE_FLOAT;
+
+BOOL freerdp_read_four_byte_signed_integer(wStream* s, INT32* value)
+{
+ FOUR_BYTE_SIGNED_INTEGER si = { 0 };
+ BYTE byte = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(value);
+
+ *value = 0;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
+ return FALSE;
+
+ Stream_Read_UINT8(s, byte);
+
+ si.c = (byte & 0xC0) >> 6;
+ si.s = (byte & 0x20) >> 5;
+ si.val1 = (byte & 0x1F);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, si.c))
+ return FALSE;
+
+ switch (si.c)
+ {
+ case ONE_BYTE_VAL:
+ *value = si.val1;
+ break;
+ case TWO_BYTE_VAL:
+ Stream_Read_UINT8(s, si.val2);
+ *value = (((INT32)si.val1) << 8) | ((INT32)si.val2);
+ break;
+ case THREE_BYTE_VAL:
+ Stream_Read_UINT8(s, si.val2);
+ Stream_Read_UINT8(s, si.val3);
+ *value = (((INT32)si.val1) << 16) | (((INT32)si.val2) << 8) | ((INT32)si.val3);
+ break;
+ case FOUR_BYTE_VAL:
+ Stream_Read_UINT8(s, si.val2);
+ Stream_Read_UINT8(s, si.val3);
+ Stream_Read_UINT8(s, si.val4);
+ *value = (((INT32)si.val1) << 24) | (((INT32)si.val2) << 16) | (((INT32)si.val3) << 8) |
+ ((INT32)si.val4);
+ break;
+ case FIVE_BYTE_VAL:
+ case SIX_BYTE_VAL:
+ case SEVEN_BYTE_VAL:
+ case EIGHT_BYTE_VAL:
+ default:
+ WLog_ERR(TAG, "Invalid byte count value in si.c: %u", si.c);
+ return FALSE;
+ }
+
+ if (si.s == NEGATIVE_VAL)
+ *value *= -1;
+
+ return TRUE;
+}
+
+BOOL freerdp_read_four_byte_float(wStream* s, double* value)
+{
+ FOUR_BYTE_FLOAT f = { 0 };
+ UINT32 base = 0;
+ BYTE byte = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(value);
+
+ *value = 0.0;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
+ return FALSE;
+
+ Stream_Read_UINT8(s, byte);
+
+ f.c = (byte & 0xC0) >> 6;
+ f.s = (byte & 0x20) >> 5;
+ f.e = (byte & 0x1C) >> 2;
+ f.val1 = (byte & 0x03);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, f.c))
+ return FALSE;
+
+ switch (f.c)
+ {
+ case ONE_BYTE_VAL:
+ base = f.val1;
+ break;
+ case TWO_BYTE_VAL:
+ Stream_Read_UINT8(s, f.val2);
+ base = (((UINT32)f.val1) << 8) | ((UINT32)f.val2);
+ break;
+ case THREE_BYTE_VAL:
+ Stream_Read_UINT8(s, f.val2);
+ Stream_Read_UINT8(s, f.val3);
+ base = (((UINT32)f.val1) << 16) | (((UINT32)f.val2) << 8) | ((UINT32)f.val3);
+ break;
+ case FOUR_BYTE_VAL:
+ Stream_Read_UINT8(s, f.val2);
+ Stream_Read_UINT8(s, f.val3);
+ Stream_Read_UINT8(s, f.val4);
+ base = (((UINT32)f.val1) << 24) | (((UINT32)f.val2) << 16) | (((UINT32)f.val3) << 8) |
+ ((UINT32)f.val4);
+ break;
+ case FIVE_BYTE_VAL:
+ case SIX_BYTE_VAL:
+ case SEVEN_BYTE_VAL:
+ case EIGHT_BYTE_VAL:
+ default:
+ WLog_ERR(TAG, "Invalid byte count value in f.c: %u", f.c);
+ return FALSE;
+ }
+
+ *value = base;
+ *value /= pow(10, f.e);
+
+ if (f.s == NEGATIVE_VAL)
+ *value *= -1.0;
+
+ return TRUE;
+}
diff --git a/libfreerdp/utils/gfx.c b/libfreerdp/utils/gfx.c
new file mode 100644
index 0000000..cf459ae
--- /dev/null
+++ b/libfreerdp/utils/gfx.c
@@ -0,0 +1,95 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * GFX Utils - Helper functions converting something to string
+ *
+ * Copyright 2022 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2022 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/utils/gfx.h>
+#include <freerdp/channels/rdpgfx.h>
+
+static const char* RDPGFX_CMDID_STRINGS[] = { "RDPGFX_CMDID_UNUSED_0000",
+ "RDPGFX_CMDID_WIRETOSURFACE_1",
+ "RDPGFX_CMDID_WIRETOSURFACE_2",
+ "RDPGFX_CMDID_DELETEENCODINGCONTEXT",
+ "RDPGFX_CMDID_SOLIDFILL",
+ "RDPGFX_CMDID_SURFACETOSURFACE",
+ "RDPGFX_CMDID_SURFACETOCACHE",
+ "RDPGFX_CMDID_CACHETOSURFACE",
+ "RDPGFX_CMDID_EVICTCACHEENTRY",
+ "RDPGFX_CMDID_CREATESURFACE",
+ "RDPGFX_CMDID_DELETESURFACE",
+ "RDPGFX_CMDID_STARTFRAME",
+ "RDPGFX_CMDID_ENDFRAME",
+ "RDPGFX_CMDID_FRAMEACKNOWLEDGE",
+ "RDPGFX_CMDID_RESETGRAPHICS",
+ "RDPGFX_CMDID_MAPSURFACETOOUTPUT",
+ "RDPGFX_CMDID_CACHEIMPORTOFFER",
+ "RDPGFX_CMDID_CACHEIMPORTREPLY",
+ "RDPGFX_CMDID_CAPSADVERTISE",
+ "RDPGFX_CMDID_CAPSCONFIRM",
+ "RDPGFX_CMDID_UNUSED_0014",
+ "RDPGFX_CMDID_MAPSURFACETOWINDOW",
+ "RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE",
+ "RDPGFX_CMDID_MAPSURFACETOSCALEDOUTPUT",
+ "RDPGFX_CMDID_MAPSURFACETOSCALEDWINDOW" };
+
+const char* rdpgfx_get_cmd_id_string(UINT16 cmdId)
+{
+ if (cmdId <= RDPGFX_CMDID_MAPSURFACETOSCALEDWINDOW)
+ return RDPGFX_CMDID_STRINGS[cmdId];
+ else
+ return "RDPGFX_CMDID_UNKNOWN";
+}
+
+const char* rdpgfx_get_codec_id_string(UINT16 codecId)
+{
+ switch (codecId)
+ {
+ case RDPGFX_CODECID_UNCOMPRESSED:
+ return "RDPGFX_CODECID_UNCOMPRESSED";
+
+ case RDPGFX_CODECID_CAVIDEO:
+ return "RDPGFX_CODECID_CAVIDEO";
+
+ case RDPGFX_CODECID_CLEARCODEC:
+ return "RDPGFX_CODECID_CLEARCODEC";
+
+ case RDPGFX_CODECID_PLANAR:
+ return "RDPGFX_CODECID_PLANAR";
+
+ case RDPGFX_CODECID_AVC420:
+ return "RDPGFX_CODECID_AVC420";
+
+ case RDPGFX_CODECID_AVC444:
+ return "RDPGFX_CODECID_AVC444";
+
+ case RDPGFX_CODECID_AVC444v2:
+ return "RDPGFX_CODECID_AVC444v2";
+
+ case RDPGFX_CODECID_ALPHA:
+ return "RDPGFX_CODECID_ALPHA";
+
+ case RDPGFX_CODECID_CAPROGRESSIVE:
+ return "RDPGFX_CODECID_CAPROGRESSIVE";
+
+ case RDPGFX_CODECID_CAPROGRESSIVE_V2:
+ return "RDPGFX_CODECID_CAPROGRESSIVE_V2";
+ }
+
+ return "RDPGFX_CODECID_UNKNOWN";
+}
diff --git a/libfreerdp/utils/http.c b/libfreerdp/utils/http.c
new file mode 100644
index 0000000..70963f0
--- /dev/null
+++ b/libfreerdp/utils/http.c
@@ -0,0 +1,386 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Simple HTTP client request utility
+ *
+ * Copyright 2023 Isaac Klein <fifthdegree@protonmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+#include <freerdp/utils/http.h>
+
+#include <winpr/assert.h>
+#include <winpr/string.h>
+
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+#include <freerdp/log.h>
+#define TAG FREERDP_TAG("utils.http")
+
+static const char get_header_fmt[] = "GET %s HTTP/1.1\r\n"
+ "Host: %s\r\n"
+ "\r\n";
+
+static const char post_header_fmt[] = "POST %s HTTP/1.1\r\n"
+ "Host: %s\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Content-Length: %lu\r\n"
+ "\r\n";
+
+#define log_errors(log, msg) log_errors_(log, msg, __FILE__, __func__, __LINE__)
+static void log_errors_(wLog* log, const char* msg, const char* file, const char* fkt, size_t line)
+{
+ const DWORD level = WLOG_ERROR;
+ unsigned long ec = 0;
+
+ if (!WLog_IsLevelActive(log, level))
+ return;
+
+ BOOL error_logged = FALSE;
+ while ((ec = ERR_get_error()))
+ {
+ error_logged = TRUE;
+ WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, level, line, file, fkt, "%s: %s", msg,
+ ERR_error_string(ec, NULL));
+ }
+ if (!error_logged)
+ WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, level, line, file, fkt,
+ "%s (no details available)", msg);
+}
+
+static int get_line(BIO* bio, char* buffer, size_t size)
+{
+#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
+ if (size <= 1)
+ return -1;
+
+ size_t pos = 0;
+ do
+ {
+ int rc = BIO_read(bio, &buffer[pos], 1);
+ if (rc <= 0)
+ return rc;
+ char cur = buffer[pos];
+ pos += rc;
+ if ((cur == '\n') || (pos >= size - 1))
+ {
+ buffer[pos] = '\0';
+ return (int)pos;
+ }
+ } while (1);
+#else
+ return BIO_get_line(bio, buffer, size);
+#endif
+}
+
+BOOL freerdp_http_request(const char* url, const char* body, long* status_code, BYTE** response,
+ size_t* response_length)
+{
+ BOOL ret = FALSE;
+ char* hostname = NULL;
+ const char* path = NULL;
+ char* headers = NULL;
+ size_t size = 0;
+ int status = 0;
+ char buffer[1024] = { 0 };
+ BIO* bio = NULL;
+ SSL_CTX* ssl_ctx = NULL;
+ SSL* ssl = NULL;
+
+ WINPR_ASSERT(status_code);
+ WINPR_ASSERT(response);
+ WINPR_ASSERT(response_length);
+
+ wLog* log = WLog_Get(TAG);
+ WINPR_ASSERT(log);
+
+ *response = NULL;
+
+ if (!url || strnlen(url, 8) < 8 || strncmp(url, "https://", 8) != 0 ||
+ !(path = strchr(url + 8, '/')))
+ {
+ WLog_Print(log, WLOG_ERROR, "invalid url provided");
+ goto out;
+ }
+
+ const size_t len = path - (url + 8);
+ hostname = strndup(&url[8], len);
+ if (!hostname)
+ return FALSE;
+
+ size_t blen = 0;
+ if (body)
+ {
+ blen = strlen(body);
+ if (winpr_asprintf(&headers, &size, post_header_fmt, path, hostname, blen) < 0)
+ return FALSE;
+ }
+ else
+ {
+ if (winpr_asprintf(&headers, &size, get_header_fmt, path, hostname) < 0)
+ return FALSE;
+ }
+
+ ssl_ctx = SSL_CTX_new(TLS_client_method());
+
+ if (!ssl_ctx)
+ {
+ log_errors(log, "could not set up ssl context");
+ goto out;
+ }
+
+ if (!SSL_CTX_set_default_verify_paths(ssl_ctx))
+ {
+ log_errors(log, "could not set ssl context verify paths");
+ goto out;
+ }
+
+ SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
+
+ bio = BIO_new_ssl_connect(ssl_ctx);
+ if (!bio)
+ {
+ log_errors(log, "could not set up connection");
+ goto out;
+ }
+
+ if (BIO_set_conn_port(bio, "https") <= 0)
+ {
+ log_errors(log, "could not set port");
+ goto out;
+ }
+
+ if (!BIO_set_conn_hostname(bio, hostname))
+ {
+ log_errors(log, "could not set hostname");
+ goto out;
+ }
+
+ BIO_get_ssl(bio, &ssl);
+ if (!ssl)
+ {
+ log_errors(log, "could not get ssl");
+ goto out;
+ }
+
+ if (!SSL_set_tlsext_host_name(ssl, hostname))
+ {
+ log_errors(log, "could not set sni hostname");
+ goto out;
+ }
+
+ WLog_Print(log, WLOG_DEBUG, "headers:\n%s", headers);
+ ERR_clear_error();
+ if (BIO_write(bio, headers, strnlen(headers, size)) < 0)
+ {
+ log_errors(log, "could not write headers");
+ goto out;
+ }
+
+ if (body)
+ {
+ WLog_Print(log, WLOG_DEBUG, "body:\n%s", body);
+
+ if (blen > INT_MAX)
+ {
+ WLog_Print(log, WLOG_ERROR, "body too long!");
+ goto out;
+ }
+
+ ERR_clear_error();
+ if (BIO_write(bio, body, blen) < 0)
+ {
+ log_errors(log, "could not write body");
+ goto out;
+ }
+ }
+
+ status = get_line(bio, buffer, sizeof(buffer));
+ if (status <= 0)
+ {
+ log_errors(log, "could not read response");
+ goto out;
+ }
+
+ if (sscanf(buffer, "HTTP/1.1 %li %*[^\r\n]\r\n", status_code) < 1)
+ {
+ WLog_Print(log, WLOG_ERROR, "invalid HTTP status line");
+ goto out;
+ }
+
+ do
+ {
+ status = get_line(bio, buffer, sizeof(buffer));
+ if (status <= 0)
+ {
+ log_errors(log, "could not read response");
+ goto out;
+ }
+
+ char* val = NULL;
+ char* name = strtok_s(buffer, ":", &val);
+ if (name && (_stricmp(name, "content-length") == 0))
+ {
+ errno = 0;
+ *response_length = strtoul(val, NULL, 10);
+ if (errno)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_Print(log, WLOG_ERROR, "could not parse content length (%s): %s [%d]", val,
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
+ goto out;
+ }
+ }
+ } while (strcmp(buffer, "\r\n") != 0);
+
+ if (*response_length > 0)
+ {
+ if (*response_length > INT_MAX)
+ {
+ WLog_Print(log, WLOG_ERROR, "response too long!");
+ goto out;
+ }
+
+ *response = calloc(1, *response_length + 1);
+ if (!*response)
+ goto out;
+
+ BYTE* p = *response;
+ int left = *response_length;
+ while (left > 0)
+ {
+ status = BIO_read(bio, p, left);
+ if (status <= 0)
+ {
+ log_errors(log, "could not read response");
+ goto out;
+ }
+ p += status;
+ left -= status;
+ }
+ }
+
+ ret = TRUE;
+
+out:
+ if (!ret)
+ {
+ free(*response);
+ *response = NULL;
+ *response_length = 0;
+ }
+ free(hostname);
+ free(headers);
+ BIO_free_all(bio);
+ SSL_CTX_free(ssl_ctx);
+ return ret;
+}
+
+const char* freerdp_http_status_string(long status)
+{
+ switch (status)
+ {
+ case HTTP_STATUS_CONTINUE:
+ return "HTTP_STATUS_CONTINUE";
+ case HTTP_STATUS_SWITCH_PROTOCOLS:
+ return "HTTP_STATUS_SWITCH_PROTOCOLS";
+ case HTTP_STATUS_OK:
+ return "HTTP_STATUS_OK";
+ case HTTP_STATUS_CREATED:
+ return "HTTP_STATUS_CREATED";
+ case HTTP_STATUS_ACCEPTED:
+ return "HTTP_STATUS_ACCEPTED";
+ case HTTP_STATUS_PARTIAL:
+ return "HTTP_STATUS_PARTIAL";
+ case HTTP_STATUS_NO_CONTENT:
+ return "HTTP_STATUS_NO_CONTENT";
+ case HTTP_STATUS_RESET_CONTENT:
+ return "HTTP_STATUS_RESET_CONTENT";
+ case HTTP_STATUS_PARTIAL_CONTENT:
+ return "HTTP_STATUS_PARTIAL_CONTENT";
+ case HTTP_STATUS_WEBDAV_MULTI_STATUS:
+ return "HTTP_STATUS_WEBDAV_MULTI_STATUS";
+ case HTTP_STATUS_AMBIGUOUS:
+ return "HTTP_STATUS_AMBIGUOUS";
+ case HTTP_STATUS_MOVED:
+ return "HTTP_STATUS_MOVED";
+ case HTTP_STATUS_REDIRECT:
+ return "HTTP_STATUS_REDIRECT";
+ case HTTP_STATUS_REDIRECT_METHOD:
+ return "HTTP_STATUS_REDIRECT_METHOD";
+ case HTTP_STATUS_NOT_MODIFIED:
+ return "HTTP_STATUS_NOT_MODIFIED";
+ case HTTP_STATUS_USE_PROXY:
+ return "HTTP_STATUS_USE_PROXY";
+ case HTTP_STATUS_REDIRECT_KEEP_VERB:
+ return "HTTP_STATUS_REDIRECT_KEEP_VERB";
+ case HTTP_STATUS_BAD_REQUEST:
+ return "HTTP_STATUS_BAD_REQUEST";
+ case HTTP_STATUS_DENIED:
+ return "HTTP_STATUS_DENIED";
+ case HTTP_STATUS_PAYMENT_REQ:
+ return "HTTP_STATUS_PAYMENT_REQ";
+ case HTTP_STATUS_FORBIDDEN:
+ return "HTTP_STATUS_FORBIDDEN";
+ case HTTP_STATUS_NOT_FOUND:
+ return "HTTP_STATUS_NOT_FOUND";
+ case HTTP_STATUS_BAD_METHOD:
+ return "HTTP_STATUS_BAD_METHOD";
+ case HTTP_STATUS_NONE_ACCEPTABLE:
+ return "HTTP_STATUS_NONE_ACCEPTABLE";
+ case HTTP_STATUS_PROXY_AUTH_REQ:
+ return "HTTP_STATUS_PROXY_AUTH_REQ";
+ case HTTP_STATUS_REQUEST_TIMEOUT:
+ return "HTTP_STATUS_REQUEST_TIMEOUT";
+ case HTTP_STATUS_CONFLICT:
+ return "HTTP_STATUS_CONFLICT";
+ case HTTP_STATUS_GONE:
+ return "HTTP_STATUS_GONE";
+ case HTTP_STATUS_LENGTH_REQUIRED:
+ return "HTTP_STATUS_LENGTH_REQUIRED";
+ case HTTP_STATUS_PRECOND_FAILED:
+ return "HTTP_STATUS_PRECOND_FAILED";
+ case HTTP_STATUS_REQUEST_TOO_LARGE:
+ return "HTTP_STATUS_REQUEST_TOO_LARGE";
+ case HTTP_STATUS_URI_TOO_LONG:
+ return "HTTP_STATUS_URI_TOO_LONG";
+ case HTTP_STATUS_UNSUPPORTED_MEDIA:
+ return "HTTP_STATUS_UNSUPPORTED_MEDIA";
+ case HTTP_STATUS_RETRY_WITH:
+ return "HTTP_STATUS_RETRY_WITH";
+ case HTTP_STATUS_SERVER_ERROR:
+ return "HTTP_STATUS_SERVER_ERROR";
+ case HTTP_STATUS_NOT_SUPPORTED:
+ return "HTTP_STATUS_NOT_SUPPORTED";
+ case HTTP_STATUS_BAD_GATEWAY:
+ return "HTTP_STATUS_BAD_GATEWAY";
+ case HTTP_STATUS_SERVICE_UNAVAIL:
+ return "HTTP_STATUS_SERVICE_UNAVAIL";
+ case HTTP_STATUS_GATEWAY_TIMEOUT:
+ return "HTTP_STATUS_GATEWAY_TIMEOUT";
+ case HTTP_STATUS_VERSION_NOT_SUP:
+ return "HTTP_STATUS_VERSION_NOT_SUP";
+ default:
+ return "HTTP_STATUS_UNKNOWN";
+ }
+}
+
+char* freerdp_http_status_string_format(long status, char* buffer, size_t size)
+{
+ const char* code = freerdp_http_status_string(status);
+ _snprintf(buffer, size, "%s [%ld]", code, status);
+ return buffer;
+}
diff --git a/libfreerdp/utils/passphrase.c b/libfreerdp/utils/passphrase.c
new file mode 100644
index 0000000..016da79
--- /dev/null
+++ b/libfreerdp/utils/passphrase.c
@@ -0,0 +1,299 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Passphrase Handling Utils
+ *
+ * Copyright 2011 Shea Levy <shea@shealevy.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+#include <freerdp/freerdp.h>
+
+#include <errno.h>
+#include <freerdp/utils/passphrase.h>
+
+#ifdef _WIN32
+
+#include <stdio.h>
+#include <io.h>
+#include <conio.h>
+#include <wincred.h>
+
+static char read_chr(FILE* f)
+{
+ char chr;
+ const BOOL isTty = _isatty(_fileno(f));
+ if (isTty)
+ return fgetc(f);
+ if (fscanf_s(f, "%c", &chr, (UINT32)sizeof(char)) && !feof(f))
+ return chr;
+ return 0;
+}
+
+int freerdp_interruptible_getc(rdpContext* context, FILE* f)
+{
+ return read_chr(f);
+}
+
+char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf, size_t bufsiz,
+ int from_stdin)
+{
+ WCHAR UserNameW[CREDUI_MAX_USERNAME_LENGTH + 1] = { 'p', 'r', 'e', 'f', 'i',
+ 'l', 'l', 'e', 'd', '\0' };
+ WCHAR PasswordW[CREDUI_MAX_PASSWORD_LENGTH + 1] = { 0 };
+ BOOL fSave = FALSE;
+ DWORD dwFlags = 0;
+ WCHAR* promptW = ConvertUtf8ToWCharAlloc(prompt, NULL);
+ const DWORD status =
+ CredUICmdLinePromptForCredentialsW(promptW, NULL, 0, UserNameW, ARRAYSIZE(UserNameW),
+ PasswordW, ARRAYSIZE(PasswordW), &fSave, dwFlags);
+ free(promptW);
+ if (ConvertWCharNToUtf8(PasswordW, ARRAYSIZE(PasswordW), buf, bufsiz) < 0)
+ return NULL;
+ return buf;
+}
+
+#elif !defined(ANDROID)
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.h>
+#include <freerdp/utils/signal.h>
+
+#if defined(WINPR_HAVE_POLL_H) && !defined(__APPLE__)
+#include <poll.h>
+#else
+#include <time.h>
+#include <sys/select.h>
+#endif
+
+static int wait_for_fd(int fd, int timeout)
+{
+ int status = 0;
+#if defined(WINPR_HAVE_POLL_H) && !defined(__APPLE__)
+ struct pollfd pollset = { 0 };
+ pollset.fd = fd;
+ pollset.events = POLLIN;
+ pollset.revents = 0;
+
+ do
+ {
+ status = poll(&pollset, 1, timeout);
+ } while ((status < 0) && (errno == EINTR));
+
+#else
+ fd_set rset = { 0 };
+ struct timeval tv = { 0 };
+ FD_ZERO(&rset);
+ FD_SET(fd, &rset);
+
+ if (timeout)
+ {
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+ }
+
+ do
+ {
+ status = select(fd + 1, &rset, NULL, NULL, timeout ? &tv : NULL);
+ } while ((status < 0) && (errno == EINTR));
+
+#endif
+ return status;
+}
+
+static void replace_char(char* buffer, size_t buffer_len, const char* toreplace)
+{
+ while (*toreplace != '\0')
+ {
+ char* ptr = NULL;
+ while ((ptr = strrchr(buffer, *toreplace)) != NULL)
+ *ptr = '\0';
+ toreplace++;
+ }
+}
+
+char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf, size_t bufsiz,
+ int from_stdin)
+{
+ BOOL terminal_needs_reset = FALSE;
+ char term_name[L_ctermid] = { 0 };
+ int term_file = 0;
+
+ FILE* fout = NULL;
+
+ if (bufsiz == 0)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ ctermid(term_name);
+ int terminal_fildes = 0;
+ if (from_stdin || strcmp(term_name, "") == 0 || (term_file = open(term_name, O_RDWR)) == -1)
+ {
+ fout = stdout;
+ terminal_fildes = STDIN_FILENO;
+ }
+ else
+ {
+ fout = fdopen(term_file, "w");
+ terminal_fildes = term_file;
+ }
+
+ struct termios orig_flags = { 0 };
+ if (tcgetattr(terminal_fildes, &orig_flags) != -1)
+ {
+ struct termios new_flags = { 0 };
+ new_flags = orig_flags;
+ new_flags.c_lflag &= ~ECHO;
+ new_flags.c_lflag |= ECHONL;
+ terminal_needs_reset = TRUE;
+ if (tcsetattr(terminal_fildes, TCSAFLUSH, &new_flags) == -1)
+ terminal_needs_reset = FALSE;
+ }
+
+ FILE* fp = fdopen(terminal_fildes, "r");
+ if (!fp)
+ goto error;
+
+ fprintf(fout, "%s", prompt);
+ fflush(fout);
+
+ char* ptr = NULL;
+ size_t ptr_len = 0;
+
+ const SSIZE_T res = freerdp_interruptible_get_line(context, &ptr, &ptr_len, fp);
+ if (res < 0)
+ goto error;
+ replace_char(ptr, ptr_len, "\r\n");
+
+ strncpy(buf, ptr, MIN(bufsiz, ptr_len));
+ free(ptr);
+ if (terminal_needs_reset)
+ {
+ if (tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags) == -1)
+ goto error;
+ }
+
+ if (terminal_fildes != STDIN_FILENO)
+ {
+ if (fclose(fp) == -1)
+ goto error;
+ }
+
+ return buf;
+
+error:
+{
+ int saved_errno = errno;
+ if (terminal_needs_reset)
+ tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags);
+ if (terminal_fildes != STDIN_FILENO)
+ {
+ if (fp)
+ fclose(fp);
+ }
+ errno = saved_errno;
+ return NULL;
+}
+}
+
+int freerdp_interruptible_getc(rdpContext* context, FILE* f)
+{
+ int rc = EOF;
+ const int fd = fileno(f);
+
+ const int orig = fcntl(fd, F_GETFL);
+ fcntl(fd, F_SETFL, orig | O_NONBLOCK);
+ do
+ {
+ const int res = wait_for_fd(fd, 10);
+ if (res != 0)
+ {
+ char c = 0;
+ const ssize_t rd = read(fd, &c, 1);
+ if (rd == 1)
+ rc = c;
+ break;
+ }
+ } while (!freerdp_shall_disconnect_context(context));
+
+ fcntl(fd, F_SETFL, orig);
+ return rc;
+}
+
+#else
+
+char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf, size_t bufsiz,
+ int from_stdin)
+{
+ return NULL;
+}
+
+int freerdp_interruptible_getc(rdpContext* context, FILE* f)
+{
+ return EOF;
+}
+#endif
+
+SSIZE_T freerdp_interruptible_get_line(rdpContext* context, char** plineptr, size_t* psize,
+ FILE* stream)
+{
+ int c = 0;
+ char* n = NULL;
+ size_t step = 32;
+ size_t used = 0;
+ char* ptr = NULL;
+ size_t len = 0;
+
+ if (!plineptr || !psize)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ do
+ {
+ if (used + 2 >= len)
+ {
+ len += step;
+ n = realloc(ptr, len);
+
+ if (!n)
+ {
+ return -1;
+ }
+
+ ptr = n;
+ }
+
+ c = freerdp_interruptible_getc(context, stream);
+ if (c != EOF)
+ ptr[used++] = (char)c;
+ } while ((c != '\n') && (c != '\r') && (c != EOF));
+
+ ptr[used] = '\0';
+ if (c == EOF)
+ {
+ free(ptr);
+ return EOF;
+ }
+ *plineptr = ptr;
+ *psize = used;
+ return used;
+}
diff --git a/libfreerdp/utils/pcap.c b/libfreerdp/utils/pcap.c
new file mode 100644
index 0000000..db50909
--- /dev/null
+++ b/libfreerdp/utils/pcap.c
@@ -0,0 +1,286 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * pcap File Format Utils
+ *
+ * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <winpr/wtypes.h>
+#include <winpr/assert.h>
+#include <winpr/file.h>
+#include <winpr/crt.h>
+#include <freerdp/log.h>
+
+#define TAG FREERDP_TAG("utils")
+
+#ifndef _WIN32
+#include <sys/time.h>
+#else
+#include <time.h>
+#include <sys/timeb.h>
+#include <winpr/windows.h>
+
+int gettimeofday(struct timeval* tp, void* tz)
+{
+ struct _timeb timebuffer;
+ _ftime(&timebuffer);
+ tp->tv_sec = (long)timebuffer.time;
+ tp->tv_usec = timebuffer.millitm * 1000;
+ return 0;
+}
+#endif
+
+#include <freerdp/types.h>
+#include <freerdp/utils/pcap.h>
+
+#define PCAP_MAGIC 0xA1B2C3D4
+
+struct rdp_pcap
+{
+ FILE* fp;
+ char* name;
+ BOOL write;
+ INT64 file_size;
+ size_t record_count;
+ pcap_header header;
+ pcap_record* head;
+ pcap_record* tail;
+ pcap_record* record;
+};
+
+static BOOL pcap_read_header(rdpPcap* pcap, pcap_header* header)
+{
+ WINPR_ASSERT(pcap);
+ WINPR_ASSERT(header);
+
+ return fread(header, sizeof(pcap_header), 1, pcap->fp) == 1;
+}
+
+static BOOL pcap_write_header(rdpPcap* pcap, const pcap_header* header)
+{
+ WINPR_ASSERT(pcap);
+ WINPR_ASSERT(header);
+
+ return fwrite(header, sizeof(pcap_header), 1, pcap->fp) == 1;
+}
+
+static BOOL pcap_read_record_header(rdpPcap* pcap, pcap_record_header* record)
+{
+ WINPR_ASSERT(pcap);
+ WINPR_ASSERT(record);
+
+ return fread(record, sizeof(pcap_record_header), 1, pcap->fp) == 1;
+}
+
+static BOOL pcap_write_record_header(rdpPcap* pcap, const pcap_record_header* record)
+{
+ WINPR_ASSERT(pcap);
+ WINPR_ASSERT(record);
+
+ return fwrite(record, sizeof(pcap_record_header), 1, pcap->fp) == 1;
+}
+
+static BOOL pcap_read_record(rdpPcap* pcap, pcap_record* record)
+{
+ WINPR_ASSERT(pcap);
+ WINPR_ASSERT(record);
+
+ if (!pcap_read_record_header(pcap, &record->header))
+ return FALSE;
+
+ record->length = record->header.incl_len;
+ record->data = malloc(record->length);
+ if (!record->data)
+ return FALSE;
+
+ if (fread(record->data, record->length, 1, pcap->fp) != 1)
+ {
+ free(record->data);
+ record->data = NULL;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static BOOL pcap_write_record(rdpPcap* pcap, const pcap_record* record)
+{
+ WINPR_ASSERT(pcap);
+ WINPR_ASSERT(record);
+
+ return pcap_write_record_header(pcap, &record->header) &&
+ (fwrite(record->cdata, record->length, 1, pcap->fp) == 1);
+}
+
+BOOL pcap_add_record(rdpPcap* pcap, const void* data, size_t length)
+{
+ pcap_record* record = NULL;
+ struct timeval tp;
+
+ WINPR_ASSERT(pcap);
+ WINPR_ASSERT(data || (length == 0));
+ WINPR_ASSERT(length <= UINT32_MAX);
+
+ record = (pcap_record*)calloc(1, sizeof(pcap_record));
+ if (!record)
+ return FALSE;
+
+ record->cdata = data;
+ record->length = length;
+ record->header.incl_len = (UINT32)length;
+ record->header.orig_len = (UINT32)length;
+
+ gettimeofday(&tp, 0);
+ record->header.ts_sec = (UINT32)tp.tv_sec;
+ record->header.ts_usec = (UINT32)tp.tv_usec;
+
+ if (pcap->tail == NULL)
+ {
+ pcap->tail = record;
+ if (!pcap->tail)
+ return FALSE;
+
+ pcap->head = pcap->tail;
+ }
+ else
+ {
+ record->next = pcap->tail;
+ pcap->tail = record;
+ }
+
+ if (pcap->record == NULL)
+ pcap->record = record;
+
+ return TRUE;
+}
+
+BOOL pcap_has_next_record(const rdpPcap* pcap)
+{
+ WINPR_ASSERT(pcap);
+
+ if (pcap->file_size - (_ftelli64(pcap->fp)) <= 16)
+ return FALSE;
+
+ return TRUE;
+}
+
+BOOL pcap_get_next_record_header(rdpPcap* pcap, pcap_record* record)
+{
+ WINPR_ASSERT(pcap);
+ WINPR_ASSERT(record);
+
+ if (pcap_has_next_record(pcap) != TRUE)
+ return FALSE;
+
+ pcap_read_record_header(pcap, &record->header);
+ record->length = record->header.incl_len;
+
+ return TRUE;
+}
+
+BOOL pcap_get_next_record_content(rdpPcap* pcap, pcap_record* record)
+{
+ WINPR_ASSERT(pcap);
+ WINPR_ASSERT(record);
+
+ return fread(record->data, record->length, 1, pcap->fp) == 1;
+}
+
+BOOL pcap_get_next_record(rdpPcap* pcap, pcap_record* record)
+{
+ WINPR_ASSERT(pcap);
+ WINPR_ASSERT(record);
+
+ return pcap_has_next_record(pcap) && pcap_read_record(pcap, record);
+}
+
+rdpPcap* pcap_open(const char* name, BOOL write)
+{
+ rdpPcap* pcap = NULL;
+
+ WINPR_ASSERT(name);
+
+ pcap = (rdpPcap*)calloc(1, sizeof(rdpPcap));
+ if (!pcap)
+ goto fail;
+
+ pcap->name = _strdup(name);
+ pcap->write = write;
+ pcap->record_count = 0;
+ pcap->fp = winpr_fopen(name, write ? "w+b" : "rb");
+
+ if (pcap->fp == NULL)
+ goto fail;
+
+ if (write)
+ {
+ pcap->header.magic_number = PCAP_MAGIC;
+ pcap->header.version_major = 2;
+ pcap->header.version_minor = 4;
+ pcap->header.thiszone = 0;
+ pcap->header.sigfigs = 0;
+ pcap->header.snaplen = UINT32_MAX;
+ pcap->header.network = 0;
+ if (!pcap_write_header(pcap, &pcap->header))
+ goto fail;
+ }
+ else
+ {
+ _fseeki64(pcap->fp, 0, SEEK_END);
+ pcap->file_size = _ftelli64(pcap->fp);
+ _fseeki64(pcap->fp, 0, SEEK_SET);
+ if (!pcap_read_header(pcap, &pcap->header))
+ goto fail;
+ }
+
+ return pcap;
+
+fail:
+ pcap_close(pcap);
+ return NULL;
+}
+
+void pcap_flush(rdpPcap* pcap)
+{
+ WINPR_ASSERT(pcap);
+
+ while (pcap->record != NULL)
+ {
+ pcap_write_record(pcap, pcap->record);
+ pcap->record = pcap->record->next;
+ }
+
+ if (pcap->fp != NULL)
+ fflush(pcap->fp);
+}
+
+void pcap_close(rdpPcap* pcap)
+{
+ if (!pcap)
+ return;
+
+ pcap_flush(pcap);
+
+ if (pcap->fp != NULL)
+ fclose(pcap->fp);
+
+ free(pcap->name);
+ free(pcap);
+}
diff --git a/libfreerdp/utils/profiler.c b/libfreerdp/utils/profiler.c
new file mode 100644
index 0000000..b1c058d
--- /dev/null
+++ b/libfreerdp/utils/profiler.c
@@ -0,0 +1,98 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Profiler Utils
+ *
+ * Copyright 2011 Stephen Erisman
+ *
+ * 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 <freerdp/config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <freerdp/utils/profiler.h>
+#include <freerdp/log.h>
+
+#define TAG FREERDP_TAG("utils")
+
+struct S_PROFILER
+{
+ char* name;
+ STOPWATCH* stopwatch;
+};
+
+PROFILER* profiler_create(const char* name)
+{
+ PROFILER* profiler = (PROFILER*)calloc(1, sizeof(PROFILER));
+
+ if (!profiler)
+ return NULL;
+
+ profiler->name = _strdup(name);
+ profiler->stopwatch = stopwatch_create();
+
+ if (!profiler->name || !profiler->stopwatch)
+ goto fail;
+
+ return profiler;
+fail:
+ profiler_free(profiler);
+ return NULL;
+}
+
+void profiler_free(PROFILER* profiler)
+{
+ if (profiler)
+ {
+ free(profiler->name);
+ stopwatch_free(profiler->stopwatch);
+ }
+
+ free(profiler);
+}
+
+void profiler_enter(PROFILER* profiler)
+{
+ stopwatch_start(profiler->stopwatch);
+}
+
+void profiler_exit(PROFILER* profiler)
+{
+ stopwatch_stop(profiler->stopwatch);
+}
+
+void profiler_print_header(void)
+{
+ WLog_INFO(TAG,
+ "-------------------------------+------------+-------------+-----------+-------");
+ WLog_INFO(TAG,
+ "PROFILER NAME | COUNT | TOTAL | AVG | IPS");
+ WLog_INFO(TAG,
+ "-------------------------------+------------+-------------+-----------+-------");
+}
+
+void profiler_print(PROFILER* profiler)
+{
+ double s = stopwatch_get_elapsed_time_in_seconds(profiler->stopwatch);
+ double avg = profiler->stopwatch->count == 0 ? 0 : s / profiler->stopwatch->count;
+ WLog_INFO(TAG, "%-30s | %10u | %10.4fs | %8.6fs | %6.0f", profiler->name,
+ profiler->stopwatch->count, s, avg, profiler->stopwatch->count / s);
+}
+
+void profiler_print_footer(void)
+{
+ WLog_INFO(TAG,
+ "-------------------------------+------------+-------------+-----------+-------");
+}
diff --git a/libfreerdp/utils/rdpdr_utils.c b/libfreerdp/utils/rdpdr_utils.c
new file mode 100644
index 0000000..28cc97f
--- /dev/null
+++ b/libfreerdp/utils/rdpdr_utils.c
@@ -0,0 +1,598 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * SCard utility functions
+ *
+ * Copyright 2021 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2021 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/wlog.h>
+#include <winpr/print.h>
+#include <winpr/smartcard.h>
+
+#include <freerdp/utils/rdpdr_utils.h>
+#include <freerdp/channels/scard.h>
+#include <freerdp/channels/rdpdr.h>
+
+#include <freerdp/log.h>
+#define TAG FREERDP_TAG("utils.scard")
+
+LONG scard_log_status_error(const char* tag, const char* what, LONG status)
+{
+ if (status != SCARD_S_SUCCESS)
+ {
+ DWORD level = WLOG_ERROR;
+ switch (status)
+ {
+ case SCARD_E_TIMEOUT:
+ level = WLOG_DEBUG;
+ break;
+ case SCARD_E_NO_READERS_AVAILABLE:
+ level = WLOG_INFO;
+ break;
+ default:
+ break;
+ }
+ WLog_Print(WLog_Get(tag), level, "%s failed with error %s [%" PRId32 "]", what,
+ SCardGetErrorString(status), status);
+ }
+ return status;
+}
+
+const char* scard_get_ioctl_string(UINT32 ioControlCode, BOOL funcName)
+{
+ switch (ioControlCode)
+ {
+ case SCARD_IOCTL_ESTABLISHCONTEXT:
+ return funcName ? "SCardEstablishContext" : "SCARD_IOCTL_ESTABLISHCONTEXT";
+
+ case SCARD_IOCTL_RELEASECONTEXT:
+ return funcName ? "SCardReleaseContext" : "SCARD_IOCTL_RELEASECONTEXT";
+
+ case SCARD_IOCTL_ISVALIDCONTEXT:
+ return funcName ? "SCardIsValidContext" : "SCARD_IOCTL_ISVALIDCONTEXT";
+
+ case SCARD_IOCTL_LISTREADERGROUPSA:
+ return funcName ? "SCardListReaderGroupsA" : "SCARD_IOCTL_LISTREADERGROUPSA";
+
+ case SCARD_IOCTL_LISTREADERGROUPSW:
+ return funcName ? "SCardListReaderGroupsW" : "SCARD_IOCTL_LISTREADERGROUPSW";
+
+ case SCARD_IOCTL_LISTREADERSA:
+ return funcName ? "SCardListReadersA" : "SCARD_IOCTL_LISTREADERSA";
+
+ case SCARD_IOCTL_LISTREADERSW:
+ return funcName ? "SCardListReadersW" : "SCARD_IOCTL_LISTREADERSW";
+
+ case SCARD_IOCTL_INTRODUCEREADERGROUPA:
+ return funcName ? "SCardIntroduceReaderGroupA" : "SCARD_IOCTL_INTRODUCEREADERGROUPA";
+
+ case SCARD_IOCTL_INTRODUCEREADERGROUPW:
+ return funcName ? "SCardIntroduceReaderGroupW" : "SCARD_IOCTL_INTRODUCEREADERGROUPW";
+
+ case SCARD_IOCTL_FORGETREADERGROUPA:
+ return funcName ? "SCardForgetReaderGroupA" : "SCARD_IOCTL_FORGETREADERGROUPA";
+
+ case SCARD_IOCTL_FORGETREADERGROUPW:
+ return funcName ? "SCardForgetReaderGroupW" : "SCARD_IOCTL_FORGETREADERGROUPW";
+
+ case SCARD_IOCTL_INTRODUCEREADERA:
+ return funcName ? "SCardIntroduceReaderA" : "SCARD_IOCTL_INTRODUCEREADERA";
+
+ case SCARD_IOCTL_INTRODUCEREADERW:
+ return funcName ? "SCardIntroduceReaderW" : "SCARD_IOCTL_INTRODUCEREADERW";
+
+ case SCARD_IOCTL_FORGETREADERA:
+ return funcName ? "SCardForgetReaderA" : "SCARD_IOCTL_FORGETREADERA";
+
+ case SCARD_IOCTL_FORGETREADERW:
+ return funcName ? "SCardForgetReaderW" : "SCARD_IOCTL_FORGETREADERW";
+
+ case SCARD_IOCTL_ADDREADERTOGROUPA:
+ return funcName ? "SCardAddReaderToGroupA" : "SCARD_IOCTL_ADDREADERTOGROUPA";
+
+ case SCARD_IOCTL_ADDREADERTOGROUPW:
+ return funcName ? "SCardAddReaderToGroupW" : "SCARD_IOCTL_ADDREADERTOGROUPW";
+
+ case SCARD_IOCTL_REMOVEREADERFROMGROUPA:
+ return funcName ? "SCardRemoveReaderFromGroupA" : "SCARD_IOCTL_REMOVEREADERFROMGROUPA";
+
+ case SCARD_IOCTL_REMOVEREADERFROMGROUPW:
+ return funcName ? "SCardRemoveReaderFromGroupW" : "SCARD_IOCTL_REMOVEREADERFROMGROUPW";
+
+ case SCARD_IOCTL_LOCATECARDSA:
+ return funcName ? "SCardLocateCardsA" : "SCARD_IOCTL_LOCATECARDSA";
+
+ case SCARD_IOCTL_LOCATECARDSW:
+ return funcName ? "SCardLocateCardsW" : "SCARD_IOCTL_LOCATECARDSW";
+
+ case SCARD_IOCTL_GETSTATUSCHANGEA:
+ return funcName ? "SCardGetStatusChangeA" : "SCARD_IOCTL_GETSTATUSCHANGEA";
+
+ case SCARD_IOCTL_GETSTATUSCHANGEW:
+ return funcName ? "SCardGetStatusChangeW" : "SCARD_IOCTL_GETSTATUSCHANGEW";
+
+ case SCARD_IOCTL_CANCEL:
+ return funcName ? "SCardCancel" : "SCARD_IOCTL_CANCEL";
+
+ case SCARD_IOCTL_CONNECTA:
+ return funcName ? "SCardConnectA" : "SCARD_IOCTL_CONNECTA";
+
+ case SCARD_IOCTL_CONNECTW:
+ return funcName ? "SCardConnectW" : "SCARD_IOCTL_CONNECTW";
+
+ case SCARD_IOCTL_RECONNECT:
+ return funcName ? "SCardReconnect" : "SCARD_IOCTL_RECONNECT";
+
+ case SCARD_IOCTL_DISCONNECT:
+ return funcName ? "SCardDisconnect" : "SCARD_IOCTL_DISCONNECT";
+
+ case SCARD_IOCTL_BEGINTRANSACTION:
+ return funcName ? "SCardBeginTransaction" : "SCARD_IOCTL_BEGINTRANSACTION";
+
+ case SCARD_IOCTL_ENDTRANSACTION:
+ return funcName ? "SCardEndTransaction" : "SCARD_IOCTL_ENDTRANSACTION";
+
+ case SCARD_IOCTL_STATE:
+ return funcName ? "SCardState" : "SCARD_IOCTL_STATE";
+
+ case SCARD_IOCTL_STATUSA:
+ return funcName ? "SCardStatusA" : "SCARD_IOCTL_STATUSA";
+
+ case SCARD_IOCTL_STATUSW:
+ return funcName ? "SCardStatusW" : "SCARD_IOCTL_STATUSW";
+
+ case SCARD_IOCTL_TRANSMIT:
+ return funcName ? "SCardTransmit" : "SCARD_IOCTL_TRANSMIT";
+
+ case SCARD_IOCTL_CONTROL:
+ return funcName ? "SCardControl" : "SCARD_IOCTL_CONTROL";
+
+ case SCARD_IOCTL_GETATTRIB:
+ return funcName ? "SCardGetAttrib" : "SCARD_IOCTL_GETATTRIB";
+
+ case SCARD_IOCTL_SETATTRIB:
+ return funcName ? "SCardSetAttrib" : "SCARD_IOCTL_SETATTRIB";
+
+ case SCARD_IOCTL_ACCESSSTARTEDEVENT:
+ return funcName ? "SCardAccessStartedEvent" : "SCARD_IOCTL_ACCESSSTARTEDEVENT";
+
+ case SCARD_IOCTL_LOCATECARDSBYATRA:
+ return funcName ? "SCardLocateCardsByATRA" : "SCARD_IOCTL_LOCATECARDSBYATRA";
+
+ case SCARD_IOCTL_LOCATECARDSBYATRW:
+ return funcName ? "SCardLocateCardsByATRB" : "SCARD_IOCTL_LOCATECARDSBYATRW";
+
+ case SCARD_IOCTL_READCACHEA:
+ return funcName ? "SCardReadCacheA" : "SCARD_IOCTL_READCACHEA";
+
+ case SCARD_IOCTL_READCACHEW:
+ return funcName ? "SCardReadCacheW" : "SCARD_IOCTL_READCACHEW";
+
+ case SCARD_IOCTL_WRITECACHEA:
+ return funcName ? "SCardWriteCacheA" : "SCARD_IOCTL_WRITECACHEA";
+
+ case SCARD_IOCTL_WRITECACHEW:
+ return funcName ? "SCardWriteCacheW" : "SCARD_IOCTL_WRITECACHEW";
+
+ case SCARD_IOCTL_GETTRANSMITCOUNT:
+ return funcName ? "SCardGetTransmitCount" : "SCARD_IOCTL_GETTRANSMITCOUNT";
+
+ case SCARD_IOCTL_RELEASETARTEDEVENT:
+ return funcName ? "SCardReleaseStartedEvent" : "SCARD_IOCTL_RELEASETARTEDEVENT";
+
+ case SCARD_IOCTL_GETREADERICON:
+ return funcName ? "SCardGetReaderIcon" : "SCARD_IOCTL_GETREADERICON";
+
+ case SCARD_IOCTL_GETDEVICETYPEID:
+ return funcName ? "SCardGetDeviceTypeId" : "SCARD_IOCTL_GETDEVICETYPEID";
+
+ default:
+ return funcName ? "SCardUnknown" : "SCARD_IOCTL_UNKNOWN";
+ }
+}
+
+const char* rdpdr_component_string(UINT16 component)
+{
+ switch (component)
+ {
+ case RDPDR_CTYP_PRN:
+ return "RDPDR_CTYP_PRN";
+ case RDPDR_CTYP_CORE:
+ return "RDPDR_CTYP_CORE";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char* rdpdr_packetid_string(UINT16 packetid)
+{
+ switch (packetid)
+ {
+ case PAKID_CORE_SERVER_ANNOUNCE:
+ return "PAKID_CORE_SERVER_ANNOUNCE";
+ case PAKID_CORE_CLIENTID_CONFIRM:
+ return "PAKID_CORE_CLIENTID_CONFIRM";
+ case PAKID_CORE_CLIENT_NAME:
+ return "PAKID_CORE_CLIENT_NAME";
+ case PAKID_CORE_DEVICELIST_ANNOUNCE:
+ return "PAKID_CORE_DEVICELIST_ANNOUNCE";
+ case PAKID_CORE_DEVICE_REPLY:
+ return "PAKID_CORE_DEVICE_REPLY";
+ case PAKID_CORE_DEVICE_IOREQUEST:
+ return "PAKID_CORE_DEVICE_IOREQUEST";
+ case PAKID_CORE_DEVICE_IOCOMPLETION:
+ return "PAKID_CORE_DEVICE_IOCOMPLETION";
+ case PAKID_CORE_SERVER_CAPABILITY:
+ return "PAKID_CORE_SERVER_CAPABILITY";
+ case PAKID_CORE_CLIENT_CAPABILITY:
+ return "PAKID_CORE_CLIENT_CAPABILITY";
+ case PAKID_CORE_DEVICELIST_REMOVE:
+ return "PAKID_CORE_DEVICELIST_REMOVE";
+ case PAKID_CORE_USER_LOGGEDON:
+ return "PAKID_CORE_USER_LOGGEDON";
+ case PAKID_PRN_CACHE_DATA:
+ return "PAKID_PRN_CACHE_DATA";
+ case PAKID_PRN_USING_XPS:
+ return "PAKID_PRN_USING_XPS";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+BOOL rdpdr_write_iocompletion_header(wStream* out, UINT32 DeviceId, UINT32 CompletionId,
+ UINT32 ioStatus)
+{
+ WINPR_ASSERT(out);
+ Stream_SetPosition(out, 0);
+ if (!Stream_EnsureRemainingCapacity(out, 16))
+ return FALSE;
+ Stream_Write_UINT16(out, RDPDR_CTYP_CORE); /* Component (2 bytes) */
+ Stream_Write_UINT16(out, PAKID_CORE_DEVICE_IOCOMPLETION); /* PacketId (2 bytes) */
+ Stream_Write_UINT32(out, DeviceId); /* DeviceId (4 bytes) */
+ Stream_Write_UINT32(out, CompletionId); /* CompletionId (4 bytes) */
+ Stream_Write_UINT32(out, ioStatus); /* IoStatus (4 bytes) */
+
+ return TRUE;
+}
+
+static void rdpdr_dump_packet(wLog* log, DWORD lvl, wStream* s, const char* custom, BOOL send)
+{
+ if (!WLog_IsLevelActive(log, lvl))
+ return;
+
+ const size_t gpos = Stream_GetPosition(s);
+ const size_t pos = send ? Stream_GetPosition(s) : Stream_Length(s);
+
+ UINT16 component = 0;
+ UINT16 packetid = 0;
+
+ Stream_SetPosition(s, 0);
+
+ if (pos >= 2)
+ Stream_Read_UINT16(s, component);
+ if (pos >= 4)
+ Stream_Read_UINT16(s, packetid);
+
+ switch (packetid)
+ {
+ case PAKID_CORE_SERVER_ANNOUNCE:
+ case PAKID_CORE_CLIENTID_CONFIRM:
+ {
+ UINT16 versionMajor = 0;
+ UINT16 versionMinor = 0;
+ UINT32 clientID = 0;
+
+ if (pos >= 6)
+ Stream_Read_UINT16(s, versionMajor);
+ if (pos >= 8)
+ Stream_Read_UINT16(s, versionMinor);
+ if (pos >= 12)
+ Stream_Read_UINT32(s, clientID);
+ WLog_Print(log, lvl,
+ "%s [%s | %s] [version:%" PRIu16 ".%" PRIu16 "][id:0x%08" PRIx32
+ "] -> %" PRIuz,
+ custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid),
+ versionMajor, versionMinor, clientID, pos);
+ }
+ break;
+ case PAKID_CORE_CLIENT_NAME:
+ {
+ char name[256] = { 0 };
+ UINT32 unicodeFlag = 0;
+ UINT32 codePage = 0;
+ UINT32 computerNameLen = 0;
+ if (pos >= 8)
+ Stream_Read_UINT32(s, unicodeFlag);
+ if (pos >= 12)
+ Stream_Read_UINT32(s, codePage);
+ if (pos >= 16)
+ Stream_Read_UINT32(s, computerNameLen);
+ if (pos >= 16 + computerNameLen)
+ {
+ if (unicodeFlag == 0)
+ Stream_Read(s, name, MIN(sizeof(name), computerNameLen));
+ else
+ ConvertWCharNToUtf8(Stream_ConstPointer(s), computerNameLen / sizeof(WCHAR),
+ name, sizeof(name));
+ }
+ WLog_Print(log, lvl,
+ "%s [%s | %s] [ucs:%" PRIu32 "|cp:%" PRIu32 "][len:0x%08" PRIx32
+ "] '%s' -> %" PRIuz,
+ custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid),
+ unicodeFlag, codePage, computerNameLen, name, pos);
+ }
+ break;
+
+ case PAKID_CORE_DEVICE_IOREQUEST:
+ {
+ UINT32 CompletionId = 0;
+ UINT32 deviceID = 0;
+ UINT32 FileId = 0;
+ UINT32 MajorFunction = 0;
+ UINT32 MinorFunction = 0;
+
+ if (pos >= 8)
+ Stream_Read_UINT32(s, deviceID);
+ if (pos >= 12)
+ Stream_Read_UINT32(s, FileId);
+ if (pos >= 16)
+ Stream_Read_UINT32(s, CompletionId);
+ if (pos >= 20)
+ Stream_Read_UINT32(s, MajorFunction);
+ if (pos >= 24)
+ Stream_Read_UINT32(s, MinorFunction);
+ WLog_Print(log, lvl,
+ "%s [%s | %s] [0x%08" PRIx32 "] FileId=0x%08" PRIx32
+ ", CompletionId=0x%08" PRIx32 ", MajorFunction=0x%08" PRIx32
+ ", MinorFunction=0x%08" PRIx32 " -> %" PRIuz,
+ custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid),
+ deviceID, FileId, CompletionId, MajorFunction, MinorFunction, pos);
+ }
+ break;
+ case PAKID_CORE_DEVICE_IOCOMPLETION:
+ {
+ UINT32 completionID = 0;
+ UINT32 ioStatus = 0;
+ UINT32 deviceID = 0;
+ if (pos >= 8)
+ Stream_Read_UINT32(s, deviceID);
+ if (pos >= 12)
+ Stream_Read_UINT32(s, completionID);
+ if (pos >= 16)
+ Stream_Read_UINT32(s, ioStatus);
+
+ WLog_Print(log, lvl,
+ "%s [%s | %s] [0x%08" PRIx32 "] completionID=0x%08" PRIx32
+ ", ioStatus=0x%08" PRIx32 " -> %" PRIuz,
+ custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid),
+ deviceID, completionID, ioStatus, pos);
+ }
+ break;
+ case PAKID_CORE_DEVICE_REPLY:
+ {
+ UINT32 deviceID = 0;
+ UINT32 status = 0;
+
+ if (pos >= 8)
+ Stream_Read_UINT32(s, deviceID);
+ if (pos >= 12)
+ Stream_Read_UINT32(s, status);
+ WLog_Print(log, lvl,
+ "%s [%s | %s] [id:0x%08" PRIx32 ",status=0x%08" PRIx32 "] -> %" PRIuz,
+ custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid),
+ deviceID, status, pos);
+ }
+ break;
+ case PAKID_CORE_CLIENT_CAPABILITY:
+ case PAKID_CORE_SERVER_CAPABILITY:
+ {
+ UINT16 numCapabilities = 0;
+ if (pos >= 6)
+ Stream_Read_UINT16(s, numCapabilities);
+ if (pos >= 8)
+ Stream_Seek_UINT16(s); /* padding */
+ WLog_Print(log, lvl, "%s [%s | %s] [caps:%" PRIu16 "] -> %" PRIuz, custom,
+ rdpdr_component_string(component), rdpdr_packetid_string(packetid),
+ numCapabilities, pos);
+ for (UINT16 x = 0; x < numCapabilities; x++)
+ {
+ RDPDR_CAPABILITY_HEADER header = { 0 };
+ const UINT error = rdpdr_read_capset_header(log, s, &header);
+ if (error == CHANNEL_RC_OK)
+ Stream_Seek(s, header.CapabilityLength);
+ }
+ }
+ break;
+ case PAKID_CORE_DEVICELIST_ANNOUNCE:
+ {
+ size_t offset = 8;
+ UINT32 count = 0;
+
+ if (pos >= offset)
+ Stream_Read_UINT32(s, count);
+
+ WLog_Print(log, lvl, "%s [%s | %s] [%" PRIu32 "] -> %" PRIuz, custom,
+ rdpdr_component_string(component), rdpdr_packetid_string(packetid), count,
+ pos);
+
+ for (UINT32 x = 0; x < count; x++)
+ {
+ RdpdrDevice device = { 0 };
+
+ offset += 20;
+ if (pos >= offset)
+ {
+ 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) */
+ Stream_Read_UINT32(s, device.DeviceDataLength); /* DeviceDataLength (4 bytes) */
+ device.DeviceData = Stream_Pointer(s);
+ }
+ offset += device.DeviceDataLength;
+
+ WLog_Print(log, lvl,
+ "%s [announce][%" PRIu32 "] %s [0x%08" PRIx32
+ "] '%s' [DeviceDataLength=%" PRIu32 "]",
+ custom, x, freerdp_rdpdr_dtyp_string(device.DeviceType), device.DeviceId,
+ device.PreferredDosName, device.DeviceDataLength);
+ }
+ }
+ break;
+ case PAKID_CORE_DEVICELIST_REMOVE:
+ {
+ size_t offset = 8;
+ UINT32 count = 0;
+
+ if (pos >= offset)
+ Stream_Read_UINT32(s, count);
+
+ WLog_Print(log, lvl, "%s [%s | %s] [%" PRIu32 "] -> %" PRIuz, custom,
+ rdpdr_component_string(component), rdpdr_packetid_string(packetid), count,
+ pos);
+
+ for (UINT32 x = 0; x < count; x++)
+ {
+ UINT32 id = 0;
+
+ offset += 4;
+ if (pos >= offset)
+ Stream_Read_UINT32(s, id);
+
+ WLog_Print(log, lvl, "%s [remove][%" PRIu32 "] id=%" PRIu32, custom, x, id);
+ }
+ }
+ break;
+ case PAKID_CORE_USER_LOGGEDON:
+ WLog_Print(log, lvl, "%s [%s | %s] -> %" PRIuz, custom,
+ rdpdr_component_string(component), rdpdr_packetid_string(packetid), pos);
+ break;
+ default:
+ {
+ WLog_Print(log, lvl, "%s [%s | %s] -> %" PRIuz, custom,
+ rdpdr_component_string(component), rdpdr_packetid_string(packetid), pos);
+ }
+ break;
+ }
+
+ // winpr_HexLogDump(log, lvl, Stream_Buffer(s), pos);
+ Stream_SetPosition(s, gpos);
+}
+
+void rdpdr_dump_received_packet(wLog* log, DWORD lvl, wStream* s, const char* custom)
+{
+ rdpdr_dump_packet(log, lvl, s, custom, FALSE);
+}
+
+void rdpdr_dump_send_packet(wLog* log, DWORD lvl, wStream* s, const char* custom)
+{
+ rdpdr_dump_packet(log, lvl, s, custom, TRUE);
+}
+
+const char* rdpdr_irp_string(UINT32 major)
+{
+ switch (major)
+ {
+ case IRP_MJ_CREATE:
+ return "IRP_MJ_CREATE";
+ case IRP_MJ_CLOSE:
+ return "IRP_MJ_CLOSE";
+ case IRP_MJ_READ:
+ return "IRP_MJ_READ";
+ case IRP_MJ_WRITE:
+ return "IRP_MJ_WRITE";
+ case IRP_MJ_DEVICE_CONTROL:
+ return "IRP_MJ_DEVICE_CONTROL";
+ case IRP_MJ_QUERY_VOLUME_INFORMATION:
+ return "IRP_MJ_QUERY_VOLUME_INFORMATION";
+ case IRP_MJ_SET_VOLUME_INFORMATION:
+ return "IRP_MJ_SET_VOLUME_INFORMATION";
+ case IRP_MJ_QUERY_INFORMATION:
+ return "IRP_MJ_QUERY_INFORMATION";
+ case IRP_MJ_SET_INFORMATION:
+ return "IRP_MJ_SET_INFORMATION";
+ case IRP_MJ_DIRECTORY_CONTROL:
+ return "IRP_MJ_DIRECTORY_CONTROL";
+ case IRP_MJ_LOCK_CONTROL:
+ return "IRP_MJ_LOCK_CONTROL";
+ default:
+ return "IRP_UNKNOWN";
+ }
+}
+
+const char* rdpdr_cap_type_string(UINT16 capability)
+{
+ switch (capability)
+ {
+ case CAP_GENERAL_TYPE:
+ return "CAP_GENERAL_TYPE";
+ case CAP_PRINTER_TYPE:
+ return "CAP_PRINTER_TYPE";
+ case CAP_PORT_TYPE:
+ return "CAP_PORT_TYPE";
+ case CAP_DRIVE_TYPE:
+ return "CAP_DRIVE_TYPE";
+ case CAP_SMARTCARD_TYPE:
+ return "CAP_SMARTCARD_TYPE";
+ default:
+ return "CAP_UNKNOWN";
+ }
+}
+
+UINT rdpdr_read_capset_header(wLog* log, wStream* s, RDPDR_CAPABILITY_HEADER* header)
+{
+ WINPR_ASSERT(header);
+ if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 8))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT16(s, header->CapabilityType); /* CapabilityType (2 bytes) */
+ Stream_Read_UINT16(s, header->CapabilityLength); /* CapabilityLength (2 bytes) */
+ Stream_Read_UINT32(s, header->Version); /* Version (4 bytes) */
+
+ WLog_Print(log, WLOG_TRACE,
+ "capability %s [0x%04" PRIx16 "] got version %" PRIu32 ", length %" PRIu16,
+ rdpdr_cap_type_string(header->CapabilityType), header->CapabilityType,
+ header->Version, header->CapabilityLength);
+ if (header->CapabilityLength < 8)
+ {
+ WLog_Print(log, WLOG_ERROR, "capability %s got short length %" PRIu32,
+ rdpdr_cap_type_string(header->CapabilityType), header->CapabilityLength);
+ return ERROR_INVALID_DATA;
+ }
+ header->CapabilityLength -= 8;
+ if (!Stream_CheckAndLogRequiredLengthWLog(log, s, header->CapabilityLength))
+ return ERROR_INVALID_DATA;
+ return CHANNEL_RC_OK;
+}
+
+UINT rdpdr_write_capset_header(wLog* log, wStream* s, const RDPDR_CAPABILITY_HEADER* header)
+{
+ WINPR_ASSERT(header);
+ WINPR_ASSERT(header->CapabilityLength >= 8);
+
+ if (!Stream_EnsureRemainingCapacity(s, header->CapabilityLength))
+ {
+ WLog_Print(log, WLOG_ERROR, "not enough data in stream!");
+ return ERROR_INVALID_DATA;
+ }
+
+ WLog_Print(log, WLOG_TRACE, "writing capability %s version %" PRIu32 ", length %" PRIu16,
+ rdpdr_cap_type_string(header->CapabilityType), header->Version,
+ header->CapabilityLength);
+ Stream_Write_UINT16(s, header->CapabilityType); /* CapabilityType (2 bytes) */
+ Stream_Write_UINT16(s, header->CapabilityLength); /* CapabilityLength (2 bytes) */
+ Stream_Write_UINT32(s, header->Version); /* Version (4 bytes) */
+ return CHANNEL_RC_OK;
+}
diff --git a/libfreerdp/utils/ringbuffer.c b/libfreerdp/utils/ringbuffer.c
new file mode 100644
index 0000000..d8390de
--- /dev/null
+++ b/libfreerdp/utils/ringbuffer.c
@@ -0,0 +1,295 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 Thincast Technologies GmbH
+ * Copyright 2014 Hardening <contact@hardening-consulting.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <freerdp/utils/ringbuffer.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <winpr/assert.h>
+
+#include <winpr/crt.h>
+#include <freerdp/log.h>
+
+#define TAG FREERDP_TAG("utils.ringbuffer")
+
+#ifdef WITH_DEBUG_RINGBUFFER
+#define DEBUG_RINGBUFFER(...) WLog_DBG(TAG, __VA_ARGS__)
+#else
+#define DEBUG_RINGBUFFER(...) \
+ do \
+ { \
+ } while (0)
+#endif
+
+BOOL ringbuffer_init(RingBuffer* rb, size_t initialSize)
+{
+ rb->buffer = malloc(initialSize);
+
+ if (!rb->buffer)
+ return FALSE;
+
+ rb->readPtr = rb->writePtr = 0;
+ rb->initialSize = rb->size = rb->freeSize = initialSize;
+ DEBUG_RINGBUFFER("ringbuffer_init(%p)", (void*)rb);
+ return TRUE;
+}
+
+size_t ringbuffer_used(const RingBuffer* rb)
+{
+ return rb->size - rb->freeSize;
+}
+
+size_t ringbuffer_capacity(const RingBuffer* rb)
+{
+ return rb->size;
+}
+
+void ringbuffer_destroy(RingBuffer* rb)
+{
+ DEBUG_RINGBUFFER("ringbuffer_destroy(%p)", (void*)rb);
+ free(rb->buffer);
+ rb->buffer = NULL;
+}
+
+static BOOL ringbuffer_realloc(RingBuffer* rb, size_t targetSize)
+{
+ BYTE* newData = NULL;
+ DEBUG_RINGBUFFER("ringbuffer_realloc(%p): targetSize: %" PRIdz "", (void*)rb, targetSize);
+
+ if (rb->writePtr == rb->readPtr)
+ {
+ /* when no size is used we can realloc() and set the heads at the
+ * beginning of the buffer
+ */
+ newData = (BYTE*)realloc(rb->buffer, targetSize);
+
+ if (!newData)
+ return FALSE;
+
+ rb->readPtr = rb->writePtr = 0;
+ rb->buffer = newData;
+ }
+ else if ((rb->writePtr >= rb->readPtr) && (rb->writePtr < targetSize))
+ {
+ /* we reallocate only if we're in that case, realloc don't touch read
+ * and write heads
+ *
+ * readPtr writePtr
+ * | |
+ * v v
+ * [............|XXXXXXXXXXXXXX|..........]
+ */
+ newData = (BYTE*)realloc(rb->buffer, targetSize);
+
+ if (!newData)
+ return FALSE;
+
+ rb->buffer = newData;
+ }
+ else
+ {
+ /* in case of malloc the read head is moved at the beginning of the new buffer
+ * and the write head is set accordingly
+ */
+ newData = (BYTE*)malloc(targetSize);
+
+ if (!newData)
+ return FALSE;
+
+ if (rb->readPtr < rb->writePtr)
+ {
+ /* readPtr writePtr
+ * | |
+ * v v
+ * [............|XXXXXXXXXXXXXX|..........]
+ */
+ memcpy(newData, rb->buffer + rb->readPtr, ringbuffer_used(rb));
+ }
+ else
+ {
+ /* writePtr readPtr
+ * | |
+ * v v
+ * [XXXXXXXXXXXX|..............|XXXXXXXXXX]
+ */
+ BYTE* dst = newData;
+ memcpy(dst, rb->buffer + rb->readPtr, rb->size - rb->readPtr);
+ dst += (rb->size - rb->readPtr);
+
+ if (rb->writePtr)
+ memcpy(dst, rb->buffer, rb->writePtr);
+ }
+
+ rb->writePtr = rb->size - rb->freeSize;
+ rb->readPtr = 0;
+ free(rb->buffer);
+ rb->buffer = newData;
+ }
+
+ rb->freeSize += (targetSize - rb->size);
+ rb->size = targetSize;
+ return TRUE;
+}
+
+/**
+ * Write to a ringbuffer
+ *
+ * @param rb A pointer to the ringbuffer
+ * @param ptr A pointer to the data to write
+ * @param sz The number of bytes to write
+ *
+ * @return \b TRUE for success, \b FALSE for failure
+ */
+BOOL ringbuffer_write(RingBuffer* rb, const BYTE* ptr, size_t sz)
+{
+ size_t toWrite = 0;
+ size_t remaining = 0;
+ DEBUG_RINGBUFFER("ringbuffer_write(%p): sz: %" PRIdz "", (void*)rb, sz);
+
+ if ((rb->freeSize <= sz) && !ringbuffer_realloc(rb, rb->size + sz))
+ return FALSE;
+
+ /* the write could be split in two
+ * readHead writeHead
+ * | |
+ * v v
+ * [ ################ ]
+ */
+ toWrite = sz;
+ remaining = sz;
+
+ if (rb->size - rb->writePtr < sz)
+ toWrite = rb->size - rb->writePtr;
+
+ if (toWrite)
+ {
+ memcpy(rb->buffer + rb->writePtr, ptr, toWrite);
+ remaining -= toWrite;
+ ptr += toWrite;
+ }
+
+ if (remaining)
+ memcpy(rb->buffer, ptr, remaining);
+
+ rb->writePtr = (rb->writePtr + sz) % rb->size;
+ rb->freeSize -= sz;
+ return TRUE;
+}
+
+BYTE* ringbuffer_ensure_linear_write(RingBuffer* rb, size_t sz)
+{
+ DEBUG_RINGBUFFER("ringbuffer_ensure_linear_write(%p): sz: %" PRIdz "", (void*)rb, sz);
+
+ if (rb->freeSize < sz)
+ {
+ if (!ringbuffer_realloc(rb, rb->size + sz - rb->freeSize + 32))
+ return NULL;
+ }
+
+ if (rb->writePtr == rb->readPtr)
+ {
+ rb->writePtr = rb->readPtr = 0;
+ }
+
+ if (rb->writePtr + sz < rb->size)
+ return rb->buffer + rb->writePtr;
+
+ /*
+ * to add: .......
+ * [ XXXXXXXXX ]
+ *
+ * result:
+ * [XXXXXXXXX....... ]
+ */
+ memmove(rb->buffer, rb->buffer + rb->readPtr, rb->writePtr - rb->readPtr);
+ rb->readPtr = 0;
+ rb->writePtr = rb->size - rb->freeSize;
+ return rb->buffer + rb->writePtr;
+}
+
+BOOL ringbuffer_commit_written_bytes(RingBuffer* rb, size_t sz)
+{
+ DEBUG_RINGBUFFER("ringbuffer_commit_written_bytes(%p): sz: %" PRIdz "", (void*)rb, sz);
+
+ if (sz < 1)
+ return TRUE;
+
+ if (rb->writePtr + sz > rb->size)
+ return FALSE;
+
+ rb->writePtr = (rb->writePtr + sz) % rb->size;
+ rb->freeSize -= sz;
+ return TRUE;
+}
+
+int ringbuffer_peek(const RingBuffer* rb, DataChunk chunks[2], size_t sz)
+{
+ size_t remaining = sz;
+ size_t toRead = 0;
+ int chunkIndex = 0;
+ int status = 0;
+ DEBUG_RINGBUFFER("ringbuffer_peek(%p): sz: %" PRIdz "", (void*)rb, sz);
+
+ if (sz < 1)
+ return 0;
+
+ if ((rb->size - rb->freeSize) < sz)
+ remaining = rb->size - rb->freeSize;
+
+ toRead = remaining;
+
+ if ((rb->readPtr + remaining) > rb->size)
+ toRead = rb->size - rb->readPtr;
+
+ if (toRead)
+ {
+ chunks[0].data = rb->buffer + rb->readPtr;
+ chunks[0].size = toRead;
+ remaining -= toRead;
+ chunkIndex++;
+ status++;
+ }
+
+ if (remaining)
+ {
+ chunks[chunkIndex].data = rb->buffer;
+ chunks[chunkIndex].size = remaining;
+ status++;
+ }
+
+ return status;
+}
+
+void ringbuffer_commit_read_bytes(RingBuffer* rb, size_t sz)
+{
+ DEBUG_RINGBUFFER("ringbuffer_commit_read_bytes(%p): sz: %" PRIdz "", (void*)rb, sz);
+
+ if (sz < 1)
+ return;
+
+ WINPR_ASSERT(rb->size - rb->freeSize >= sz);
+ rb->readPtr = (rb->readPtr + sz) % rb->size;
+ rb->freeSize += sz;
+
+ /* when we reach a reasonable free size, we can go back to the original size */
+ if ((rb->size != rb->initialSize) && (ringbuffer_used(rb) < rb->initialSize / 2))
+ ringbuffer_realloc(rb, rb->initialSize);
+}
diff --git a/libfreerdp/utils/signal.c b/libfreerdp/utils/signal.c
new file mode 100644
index 0000000..487b9b0
--- /dev/null
+++ b/libfreerdp/utils/signal.c
@@ -0,0 +1,264 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Signal handling
+ *
+ * Copyright 2011 Shea Levy <shea@shealevy.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <stddef.h>
+#include <errno.h>
+#include <string.h>
+
+#include <winpr/crt.h>
+
+#include <freerdp/utils/signal.h>
+#include <freerdp/log.h>
+
+#ifndef _WIN32
+#include <signal.h>
+#include <termios.h>
+#endif
+
+#define TAG FREERDP_TAG("utils.signal")
+
+#ifdef _WIN32
+
+int freerdp_handle_signals(void)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+BOOL freerdp_add_signal_cleanup_handler(void* context, freerdp_signal_handler_t fkt)
+{
+ return FALSE;
+}
+
+BOOL freerdp_del_signal_cleanup_handler(void* context, freerdp_signal_handler_t fkt)
+{
+ return FALSE;
+}
+#else
+
+#include <pthread.h>
+#include <winpr/debug.h>
+
+static BOOL handlers_registered = FALSE;
+static pthread_mutex_t signal_handler_lock = PTHREAD_MUTEX_INITIALIZER;
+
+typedef struct
+{
+ void* context;
+ freerdp_signal_handler_t handler;
+} cleanup_handler_t;
+
+static size_t cleanup_handler_count = 0;
+static cleanup_handler_t cleanup_handlers[20] = { 0 };
+
+static void lock(void)
+{
+ const int rc = pthread_mutex_lock(&signal_handler_lock);
+ if (rc != 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "[pthread_mutex_lock] failed with %s [%d]",
+ winpr_strerror(rc, ebuffer, sizeof(ebuffer)), rc);
+ }
+}
+
+static void unlock(void)
+{
+ const int rc = pthread_mutex_unlock(&signal_handler_lock);
+ if (rc != 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "[pthread_mutex_lock] failed with %s [%d]",
+ winpr_strerror(rc, ebuffer, sizeof(ebuffer)), rc);
+ }
+}
+
+static void term_handler(int signum)
+{
+ static BOOL recursive = FALSE;
+
+ if (!recursive)
+ {
+ recursive = TRUE;
+ WLog_ERR(TAG, "Caught signal '%s' [%d]", strsignal(signum), signum);
+ }
+
+ lock();
+ for (size_t x = 0; x < cleanup_handler_count; x++)
+ {
+ const cleanup_handler_t empty = { 0 };
+ cleanup_handler_t* cur = &cleanup_handlers[x];
+ if (cur->handler)
+ cur->handler(signum, strsignal(signum), cur->context);
+ *cur = empty;
+ }
+ cleanup_handler_count = 0;
+ unlock();
+}
+
+static void fatal_handler(int signum)
+{
+ struct sigaction default_sigaction;
+ sigset_t this_mask;
+ static BOOL recursive = FALSE;
+
+ if (!recursive)
+ {
+ recursive = TRUE;
+ WLog_ERR(TAG, "Caught signal '%s' [%d]", strsignal(signum), signum);
+
+ winpr_log_backtrace(TAG, WLOG_ERROR, 20);
+ }
+
+ default_sigaction.sa_handler = SIG_DFL;
+ sigfillset(&(default_sigaction.sa_mask));
+ default_sigaction.sa_flags = 0;
+ sigaction(signum, &default_sigaction, NULL);
+ sigemptyset(&this_mask);
+ sigaddset(&this_mask, signum);
+ pthread_sigmask(SIG_UNBLOCK, &this_mask, NULL);
+ raise(signum);
+}
+
+static const int term_signals[] = { SIGINT, SIGKILL, SIGQUIT, SIGSTOP, SIGTERM };
+
+static const int fatal_signals[] = { SIGABRT, SIGALRM, SIGBUS, SIGFPE, SIGHUP, SIGILL,
+ SIGSEGV, SIGTSTP, SIGTTIN, SIGTTOU, SIGUSR1, SIGUSR2,
+#ifdef SIGPOLL
+ SIGPOLL,
+#endif
+#ifdef SIGPROF
+ SIGPROF,
+#endif
+#ifdef SIGSYS
+ SIGSYS,
+#endif
+ SIGTRAP,
+#ifdef SIGVTALRM
+ SIGVTALRM,
+#endif
+ SIGXCPU, SIGXFSZ };
+
+static BOOL register_handlers(const int* signals, size_t count, void (*handler)(int))
+{
+ WINPR_ASSERT(signals || (count == 0));
+ WINPR_ASSERT(handler);
+
+ sigset_t orig_set = { 0 };
+ struct sigaction saction = { 0 };
+
+ pthread_sigmask(SIG_BLOCK, &(saction.sa_mask), &orig_set);
+
+ sigfillset(&(saction.sa_mask));
+ sigdelset(&(saction.sa_mask), SIGCONT);
+
+ saction.sa_handler = handler;
+ saction.sa_flags = 0;
+
+ for (size_t x = 0; x < count; x++)
+ {
+ struct sigaction orig_sigaction = { 0 };
+ if (sigaction(signals[x], NULL, &orig_sigaction) == 0)
+ {
+ if (orig_sigaction.sa_handler != SIG_IGN)
+ {
+ sigaction(signals[x], &saction, NULL);
+ }
+ }
+ }
+
+ pthread_sigmask(SIG_SETMASK, &orig_set, NULL);
+
+ return TRUE;
+}
+
+int freerdp_handle_signals(void)
+{
+ int rc = -1;
+
+ lock();
+
+ WLog_DBG(TAG, "Registering signal hook...");
+
+ if (!register_handlers(fatal_signals, ARRAYSIZE(fatal_signals), fatal_handler))
+ goto fail;
+ if (!register_handlers(term_signals, ARRAYSIZE(term_signals), term_handler))
+ goto fail;
+
+ /* Ignore SIGPIPE signal. */
+ signal(SIGPIPE, SIG_IGN);
+ handlers_registered = TRUE;
+ rc = 0;
+fail:
+ unlock();
+ return rc;
+}
+
+BOOL freerdp_add_signal_cleanup_handler(void* context, freerdp_signal_handler_t handler)
+{
+ BOOL rc = FALSE;
+ lock();
+ if (handlers_registered)
+ {
+ if (cleanup_handler_count < ARRAYSIZE(cleanup_handlers))
+ {
+ cleanup_handler_t* cur = &cleanup_handlers[cleanup_handler_count++];
+ cur->context = context;
+ cur->handler = handler;
+ }
+ else
+ WLog_WARN(TAG, "Failed to register cleanup handler, only %" PRIuz " handlers supported",
+ ARRAYSIZE(cleanup_handlers));
+ }
+ rc = TRUE;
+ unlock();
+ return rc;
+}
+
+BOOL freerdp_del_signal_cleanup_handler(void* context, freerdp_signal_handler_t handler)
+{
+ BOOL rc = FALSE;
+ lock();
+ if (handlers_registered)
+ {
+ for (size_t x = 0; x < cleanup_handler_count; x++)
+ {
+ cleanup_handler_t* cur = &cleanup_handlers[x];
+ if ((cur->context == context) && (cur->handler == handler))
+ {
+ const cleanup_handler_t empty = { 0 };
+ for (size_t y = x + 1; y < cleanup_handler_count - 1; y++)
+ {
+ *cur++ = cleanup_handlers[y];
+ }
+
+ *cur = empty;
+ cleanup_handler_count--;
+ break;
+ }
+ }
+ }
+ rc = TRUE;
+ unlock();
+ return rc;
+}
+
+#endif
diff --git a/libfreerdp/utils/smartcard_call.c b/libfreerdp/utils/smartcard_call.c
new file mode 100644
index 0000000..a957e08
--- /dev/null
+++ b/libfreerdp/utils/smartcard_call.c
@@ -0,0 +1,2019 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Smartcard Device Service Virtual Channel
+ *
+ * Copyright (C) Alexi Volkov <alexi@myrealbox.com> 2006
+ * Copyright 2011 O.S. Systems Software Ltda.
+ * Copyright 2011 Anthony Tong <atong@trustedcs.com>
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
+ * Copyright 2017 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2017 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <winpr/assert.h>
+
+#include <winpr/crt.h>
+#include <winpr/print.h>
+#include <winpr/stream.h>
+#include <winpr/library.h>
+#include <winpr/smartcard.h>
+
+#include <freerdp/freerdp.h>
+#include <freerdp/channels/rdpdr.h>
+#include <freerdp/channels/scard.h>
+
+#include <freerdp/utils/rdpdr_utils.h>
+#include <freerdp/utils/smartcard_pack.h>
+#include <freerdp/utils/smartcard_call.h>
+
+#include <freerdp/log.h>
+#define TAG FREERDP_TAG("utils.smartcard.call")
+
+#if defined(WITH_SMARTCARD_EMULATE)
+#include <freerdp/emulate/scard/smartcard_emulate.h>
+
+#define str(x) #x
+#define wrap(ctx, fkt, ...) \
+ ctx->useEmulatedCard ? Emulate_##fkt(ctx->emulation, ##__VA_ARGS__) \
+ : ctx->pWinSCardApi->pfn##fkt(__VA_ARGS__)
+#define wrap_ptr(ctx, fkt, ...) wrap(ctx, fkt, ##__VA_ARGS__)
+#else
+#define wrap(ctx, fkt, ...) \
+ ctx->useEmulatedCard ? SCARD_F_INTERNAL_ERROR : ctx->pWinSCardApi->pfn##fkt(__VA_ARGS__)
+#define wrap_ptr(ctx, fkt, ...) \
+ ctx->useEmulatedCard ? NULL : ctx->pWinSCardApi->pfn##fkt(__VA_ARGS__)
+#endif
+
+struct s_scard_call_context
+{
+ BOOL useEmulatedCard;
+ HANDLE StartedEvent;
+ wLinkedList* names;
+ wHashTable* rgSCardContextList;
+#if defined(WITH_SMARTCARD_EMULATE)
+ SmartcardEmulationContext* emulation;
+#endif
+ HANDLE hWinSCardLibrary;
+ SCardApiFunctionTable WinSCardApi;
+ const SCardApiFunctionTable* pWinSCardApi;
+ HANDLE stopEvent;
+ void* userdata;
+
+ void* (*fn_new)(void*, SCARDCONTEXT);
+ void (*fn_free)(void*);
+};
+
+struct s_scard_context_element
+{
+ void* context;
+ void (*fn_free)(void*);
+};
+
+static void context_free(void* arg);
+
+static LONG smartcard_EstablishContext_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ SCARDCONTEXT hContext = { 0 };
+ EstablishContext_Return ret = { 0 };
+ EstablishContext_Call* call = &operation->call.establishContext;
+ status = ret.ReturnCode =
+ wrap(smartcard, SCardEstablishContext, call->dwScope, NULL, NULL, &hContext);
+
+ if (ret.ReturnCode == SCARD_S_SUCCESS)
+ {
+ const void* key = (void*)(size_t)hContext;
+ struct s_scard_context_element* pContext =
+ calloc(1, sizeof(struct s_scard_context_element));
+ if (!pContext)
+ return STATUS_NO_MEMORY;
+
+ pContext->fn_free = smartcard->fn_free;
+
+ if (smartcard->fn_new)
+ {
+ pContext->context = smartcard->fn_new(smartcard->userdata, hContext);
+ if (!pContext->context)
+ {
+ free(pContext);
+ return STATUS_NO_MEMORY;
+ }
+ }
+
+ if (!HashTable_Insert(smartcard->rgSCardContextList, key, (void*)pContext))
+ {
+ WLog_ERR(TAG, "ListDictionary_Add failed!");
+ context_free(pContext);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+ else
+ {
+ return scard_log_status_error(TAG, "SCardEstablishContext", status);
+ }
+
+ // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert takes ownership of pContext
+ smartcard_scard_context_native_to_redir(&(ret.hContext), hContext);
+
+ status = smartcard_pack_establish_context_return(out, &ret);
+ if (status != SCARD_S_SUCCESS)
+ {
+ return scard_log_status_error(TAG, "smartcard_pack_establish_context_return", status);
+ }
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ReleaseContext_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ ret.ReturnCode = wrap(smartcard, SCardReleaseContext, operation->hContext);
+
+ if (ret.ReturnCode == SCARD_S_SUCCESS)
+ HashTable_Remove(smartcard->rgSCardContextList, (void*)operation->hContext);
+ else
+ {
+ return scard_log_status_error(TAG, "SCardReleaseContext", ret.ReturnCode);
+ }
+
+ smartcard_trace_long_return(&ret, "ReleaseContext");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_IsValidContext_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ ret.ReturnCode = wrap(smartcard, SCardIsValidContext, operation->hContext);
+ smartcard_trace_long_return(&ret, "IsValidContext");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ListReaderGroupsA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ ListReaderGroups_Return ret = { 0 };
+ LPSTR mszGroups = NULL;
+ DWORD cchGroups = 0;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ cchGroups = SCARD_AUTOALLOCATE;
+ ret.ReturnCode =
+ wrap(smartcard, SCardListReaderGroupsA, operation->hContext, (LPSTR)&mszGroups, &cchGroups);
+ ret.msz = (BYTE*)mszGroups;
+ ret.cBytes = cchGroups;
+
+ status = smartcard_pack_list_reader_groups_return(out, &ret, FALSE);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (mszGroups)
+ wrap(smartcard, SCardFreeMemory, operation->hContext, mszGroups);
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ListReaderGroupsW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ ListReaderGroups_Return ret = { 0 };
+ LPWSTR mszGroups = NULL;
+ DWORD cchGroups = 0;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ cchGroups = SCARD_AUTOALLOCATE;
+ status = ret.ReturnCode = wrap(smartcard, SCardListReaderGroupsW, operation->hContext,
+ (LPWSTR)&mszGroups, &cchGroups);
+ ret.msz = (BYTE*)mszGroups;
+ ret.cBytes = cchGroups * sizeof(WCHAR);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ status = smartcard_pack_list_reader_groups_return(out, &ret, TRUE);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (mszGroups)
+ wrap(smartcard, SCardFreeMemory, operation->hContext, mszGroups);
+
+ return ret.ReturnCode;
+}
+
+static BOOL filter_match(wLinkedList* list, LPCSTR reader, size_t readerLen)
+{
+ if (readerLen < 1)
+ return FALSE;
+
+ LinkedList_Enumerator_Reset(list);
+
+ while (LinkedList_Enumerator_MoveNext(list))
+ {
+ const char* filter = LinkedList_Enumerator_Current(list);
+
+ if (filter)
+ {
+ if (strstr(reader, filter) != NULL)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static DWORD filter_device_by_name_a(wLinkedList* list, LPSTR* mszReaders, DWORD cchReaders)
+{
+ size_t rpos = 0;
+ size_t wpos = 0;
+
+ if (*mszReaders == NULL || LinkedList_Count(list) < 1)
+ return cchReaders;
+
+ do
+ {
+ LPCSTR rreader = &(*mszReaders)[rpos];
+ LPSTR wreader = &(*mszReaders)[wpos];
+ size_t readerLen = strnlen(rreader, cchReaders - rpos);
+
+ rpos += readerLen + 1;
+
+ if (filter_match(list, rreader, readerLen))
+ {
+ if (rreader != wreader)
+ memmove(wreader, rreader, readerLen + 1);
+
+ wpos += readerLen + 1;
+ }
+ } while (rpos < cchReaders);
+
+ /* this string must be double 0 terminated */
+ if (rpos != wpos)
+ {
+ if (wpos >= cchReaders)
+ return 0;
+
+ (*mszReaders)[wpos++] = '\0';
+ }
+
+ return (DWORD)wpos;
+}
+
+static DWORD filter_device_by_name_w(wLinkedList* list, LPWSTR* mszReaders, DWORD cchReaders)
+{
+ DWORD rc = 0;
+ LPSTR readers = NULL;
+
+ if (LinkedList_Count(list) < 1)
+ return cchReaders;
+
+ readers = ConvertMszWCharNToUtf8Alloc(*mszReaders, cchReaders, NULL);
+
+ if (!readers)
+ {
+ free(readers);
+ return 0;
+ }
+
+ free(*mszReaders);
+ *mszReaders = NULL;
+ rc = filter_device_by_name_a(list, &readers, cchReaders);
+
+ *mszReaders = ConvertMszUtf8NToWCharAlloc(readers, rc, NULL);
+ if (!*mszReaders)
+ rc = 0;
+
+ free(readers);
+ return rc;
+}
+
+static LONG smartcard_ListReadersA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ ListReaders_Return ret = { 0 };
+ LPSTR mszReaders = NULL;
+ DWORD cchReaders = 0;
+ ListReaders_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.listReaders;
+ cchReaders = SCARD_AUTOALLOCATE;
+ status = ret.ReturnCode = wrap(smartcard, SCardListReadersA, operation->hContext,
+ (LPCSTR)call->mszGroups, (LPSTR)&mszReaders, &cchReaders);
+
+ if (status != SCARD_S_SUCCESS)
+ {
+ return scard_log_status_error(TAG, "SCardListReadersA", status);
+ }
+
+ cchReaders = filter_device_by_name_a(smartcard->names, &mszReaders, cchReaders);
+ ret.msz = (BYTE*)mszReaders;
+ ret.cBytes = cchReaders;
+
+ status = smartcard_pack_list_readers_return(out, &ret, FALSE);
+ if (status != SCARD_S_SUCCESS)
+ {
+ return scard_log_status_error(TAG, "smartcard_pack_list_readers_return", status);
+ }
+
+ if (mszReaders)
+ wrap(smartcard, SCardFreeMemory, operation->hContext, mszReaders);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ListReadersW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ ListReaders_Return ret = { 0 };
+ DWORD cchReaders = 0;
+ ListReaders_Call* call = NULL;
+ union
+ {
+ const BYTE* bp;
+ const char* sz;
+ const WCHAR* wz;
+ } string;
+ union
+ {
+ WCHAR** ppw;
+ WCHAR* pw;
+ CHAR* pc;
+ BYTE* pb;
+ } mszReaders;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.listReaders;
+
+ string.bp = call->mszGroups;
+ cchReaders = SCARD_AUTOALLOCATE;
+ status = ret.ReturnCode = wrap(smartcard, SCardListReadersW, operation->hContext, string.wz,
+ (LPWSTR)&mszReaders.pw, &cchReaders);
+
+ if (status != SCARD_S_SUCCESS)
+ return scard_log_status_error(TAG, "SCardListReadersW", status);
+
+ cchReaders = filter_device_by_name_w(smartcard->names, &mszReaders.pw, cchReaders);
+ ret.msz = mszReaders.pb;
+ ret.cBytes = cchReaders * sizeof(WCHAR);
+ status = smartcard_pack_list_readers_return(out, &ret, TRUE);
+
+ if (mszReaders.pb)
+ wrap(smartcard, SCardFreeMemory, operation->hContext, mszReaders.pb);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_IntroduceReaderGroupA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndStringA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndStringA;
+ ret.ReturnCode = wrap(smartcard, SCardIntroduceReaderGroupA, operation->hContext, call->sz);
+ scard_log_status_error(TAG, "SCardIntroduceReaderGroupA", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "IntroduceReaderGroupA");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_IntroduceReaderGroupW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndStringW_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndStringW;
+ ret.ReturnCode = wrap(smartcard, SCardIntroduceReaderGroupW, operation->hContext, call->sz);
+ scard_log_status_error(TAG, "SCardIntroduceReaderGroupW", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "IntroduceReaderGroupW");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_IntroduceReaderA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndTwoStringA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndTwoStringA;
+ ret.ReturnCode =
+ wrap(smartcard, SCardIntroduceReaderA, operation->hContext, call->sz1, call->sz2);
+ scard_log_status_error(TAG, "SCardIntroduceReaderA", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "IntroduceReaderA");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_IntroduceReaderW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndTwoStringW_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndTwoStringW;
+ ret.ReturnCode =
+ wrap(smartcard, SCardIntroduceReaderW, operation->hContext, call->sz1, call->sz2);
+ scard_log_status_error(TAG, "SCardIntroduceReaderW", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "IntroduceReaderW");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ForgetReaderA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndStringA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndStringA;
+ ret.ReturnCode = wrap(smartcard, SCardForgetReaderA, operation->hContext, call->sz);
+ scard_log_status_error(TAG, "SCardForgetReaderA", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "SCardForgetReaderA");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ForgetReaderW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndStringW_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndStringW;
+ ret.ReturnCode = wrap(smartcard, SCardForgetReaderW, operation->hContext, call->sz);
+ scard_log_status_error(TAG, "SCardForgetReaderW", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "SCardForgetReaderW");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_AddReaderToGroupA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndTwoStringA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndTwoStringA;
+ ret.ReturnCode =
+ wrap(smartcard, SCardAddReaderToGroupA, operation->hContext, call->sz1, call->sz2);
+ scard_log_status_error(TAG, "SCardAddReaderToGroupA", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "SCardAddReaderToGroupA");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_AddReaderToGroupW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndTwoStringW_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndTwoStringW;
+ ret.ReturnCode =
+ wrap(smartcard, SCardAddReaderToGroupW, operation->hContext, call->sz1, call->sz2);
+ scard_log_status_error(TAG, "SCardAddReaderToGroupW", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "SCardAddReaderToGroupA");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_RemoveReaderFromGroupA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndTwoStringA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndTwoStringA;
+ ret.ReturnCode =
+ wrap(smartcard, SCardRemoveReaderFromGroupA, operation->hContext, call->sz1, call->sz2);
+ scard_log_status_error(TAG, "SCardRemoveReaderFromGroupA", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "SCardRemoveReaderFromGroupA");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_RemoveReaderFromGroupW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndTwoStringW_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndTwoStringW;
+ ret.ReturnCode =
+ wrap(smartcard, SCardRemoveReaderFromGroupW, operation->hContext, call->sz1, call->sz2);
+ scard_log_status_error(TAG, "SCardRemoveReaderFromGroupW", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "SCardRemoveReaderFromGroupW");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_LocateCardsA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ LocateCards_Return ret = { 0 };
+ LocateCardsA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.locateCardsA;
+
+ ret.ReturnCode = wrap(smartcard, SCardLocateCardsA, operation->hContext, call->mszCards,
+ call->rgReaderStates, call->cReaders);
+ scard_log_status_error(TAG, "SCardLocateCardsA", ret.ReturnCode);
+ ret.cReaders = call->cReaders;
+ ret.rgReaderStates = NULL;
+
+ if (ret.cReaders > 0)
+ {
+ ret.rgReaderStates = (ReaderState_Return*)calloc(ret.cReaders, sizeof(ReaderState_Return));
+
+ if (!ret.rgReaderStates)
+ return STATUS_NO_MEMORY;
+ }
+
+ for (UINT32 x = 0; x < ret.cReaders; x++)
+ {
+ ret.rgReaderStates[x].dwCurrentState = call->rgReaderStates[x].dwCurrentState;
+ ret.rgReaderStates[x].dwEventState = call->rgReaderStates[x].dwEventState;
+ ret.rgReaderStates[x].cbAtr = call->rgReaderStates[x].cbAtr;
+ CopyMemory(&(ret.rgReaderStates[x].rgbAtr), &(call->rgReaderStates[x].rgbAtr),
+ sizeof(ret.rgReaderStates[x].rgbAtr));
+ }
+
+ status = smartcard_pack_locate_cards_return(out, &ret);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_LocateCardsW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ LocateCards_Return ret = { 0 };
+ LocateCardsW_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.locateCardsW;
+
+ ret.ReturnCode = wrap(smartcard, SCardLocateCardsW, operation->hContext, call->mszCards,
+ call->rgReaderStates, call->cReaders);
+ scard_log_status_error(TAG, "SCardLocateCardsW", ret.ReturnCode);
+ ret.cReaders = call->cReaders;
+ ret.rgReaderStates = NULL;
+
+ if (ret.cReaders > 0)
+ {
+ ret.rgReaderStates = (ReaderState_Return*)calloc(ret.cReaders, sizeof(ReaderState_Return));
+
+ if (!ret.rgReaderStates)
+ return STATUS_NO_MEMORY;
+ }
+
+ for (UINT32 x = 0; x < ret.cReaders; x++)
+ {
+ ret.rgReaderStates[x].dwCurrentState = call->rgReaderStates[x].dwCurrentState;
+ ret.rgReaderStates[x].dwEventState = call->rgReaderStates[x].dwEventState;
+ ret.rgReaderStates[x].cbAtr = call->rgReaderStates[x].cbAtr;
+ CopyMemory(&(ret.rgReaderStates[x].rgbAtr), &(call->rgReaderStates[x].rgbAtr),
+ sizeof(ret.rgReaderStates[x].rgbAtr));
+ }
+
+ status = smartcard_pack_locate_cards_return(out, &ret);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ReadCacheA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ BOOL autoalloc = 0;
+ ReadCache_Return ret = { 0 };
+ ReadCacheA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.readCacheA;
+ autoalloc = (call->Common.cbDataLen == SCARD_AUTOALLOCATE);
+
+ if (!call->Common.fPbDataIsNULL)
+ {
+ ret.cbDataLen = call->Common.cbDataLen;
+ if (!autoalloc)
+ {
+ ret.pbData = malloc(ret.cbDataLen);
+ if (!ret.pbData)
+ return SCARD_F_INTERNAL_ERROR;
+ }
+ }
+
+ if (autoalloc)
+ ret.ReturnCode = wrap(smartcard, SCardReadCacheA, operation->hContext,
+ call->Common.CardIdentifier, call->Common.FreshnessCounter,
+ call->szLookupName, (BYTE*)&ret.pbData, &ret.cbDataLen);
+ else
+ ret.ReturnCode =
+ wrap(smartcard, SCardReadCacheA, operation->hContext, call->Common.CardIdentifier,
+ call->Common.FreshnessCounter, call->szLookupName, ret.pbData, &ret.cbDataLen);
+ if ((ret.ReturnCode != SCARD_W_CACHE_ITEM_NOT_FOUND) &&
+ (ret.ReturnCode != SCARD_W_CACHE_ITEM_STALE))
+ {
+ scard_log_status_error(TAG, "SCardReadCacheA", ret.ReturnCode);
+ }
+
+ status = smartcard_pack_read_cache_return(out, &ret);
+ if (autoalloc)
+ wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbData);
+ else
+ free(ret.pbData);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ReadCacheW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ ReadCache_Return ret = { 0 };
+ ReadCacheW_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.readCacheW;
+
+ if (!call->Common.fPbDataIsNULL)
+ ret.cbDataLen = SCARD_AUTOALLOCATE;
+
+ ret.ReturnCode =
+ wrap(smartcard, SCardReadCacheW, operation->hContext, call->Common.CardIdentifier,
+ call->Common.FreshnessCounter, call->szLookupName, (BYTE*)&ret.pbData, &ret.cbDataLen);
+
+ if ((ret.ReturnCode != SCARD_W_CACHE_ITEM_NOT_FOUND) &&
+ (ret.ReturnCode != SCARD_W_CACHE_ITEM_STALE))
+ {
+ scard_log_status_error(TAG, "SCardReadCacheA", ret.ReturnCode);
+ }
+
+ status = smartcard_pack_read_cache_return(out, &ret);
+
+ wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbData);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_WriteCacheA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ WriteCacheA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.writeCacheA;
+
+ ret.ReturnCode = wrap(smartcard, SCardWriteCacheA, operation->hContext,
+ call->Common.CardIdentifier, call->Common.FreshnessCounter,
+ call->szLookupName, call->Common.pbData, call->Common.cbDataLen);
+ scard_log_status_error(TAG, "SCardWriteCacheA", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "SCardWriteCacheA");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_WriteCacheW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ WriteCacheW_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.writeCacheW;
+
+ ret.ReturnCode = wrap(smartcard, SCardWriteCacheW, operation->hContext,
+ call->Common.CardIdentifier, call->Common.FreshnessCounter,
+ call->szLookupName, call->Common.pbData, call->Common.cbDataLen);
+ scard_log_status_error(TAG, "SCardWriteCacheW", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "SCardWriteCacheW");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_GetTransmitCount_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ GetTransmitCount_Return ret = { 0 };
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ ret.ReturnCode = wrap(smartcard, SCardGetTransmitCount, operation->hCard, &ret.cTransmitCount);
+ scard_log_status_error(TAG, "SCardGetTransmitCount", ret.ReturnCode);
+ status = smartcard_pack_get_transmit_count_return(out, &ret);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ReleaseStartedEvent_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ WINPR_UNUSED(smartcard);
+ WINPR_UNUSED(out);
+ WINPR_UNUSED(operation);
+
+ WLog_WARN(TAG, "According to [MS-RDPESC] 3.1.4 Message Processing Events and Sequencing Rules "
+ "this is not supported?!?");
+ return SCARD_E_UNSUPPORTED_FEATURE;
+}
+
+static LONG smartcard_GetReaderIcon_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ GetReaderIcon_Return ret = { 0 };
+ GetReaderIcon_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.getReaderIcon;
+
+ ret.cbDataLen = SCARD_AUTOALLOCATE;
+ ret.ReturnCode = wrap(smartcard, SCardGetReaderIconW, operation->hContext, call->szReaderName,
+ (LPBYTE)&ret.pbData, &ret.cbDataLen);
+ scard_log_status_error(TAG, "SCardGetReaderIconW", ret.ReturnCode);
+
+ status = smartcard_pack_get_reader_icon_return(out, &ret);
+ wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbData);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_GetDeviceTypeId_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ GetDeviceTypeId_Return ret = { 0 };
+ GetDeviceTypeId_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.getDeviceTypeId;
+
+ ret.ReturnCode = wrap(smartcard, SCardGetDeviceTypeIdW, operation->hContext, call->szReaderName,
+ &ret.dwDeviceId);
+ scard_log_status_error(TAG, "SCardGetDeviceTypeIdW", ret.ReturnCode);
+
+ status = smartcard_pack_device_type_id_return(out, &ret);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_GetStatusChangeA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = STATUS_NO_MEMORY;
+ DWORD dwTimeOut = 0;
+ const DWORD dwTimeStep = 100;
+ GetStatusChange_Return ret = { 0 };
+ GetStatusChangeA_Call* call = NULL;
+ LPSCARD_READERSTATEA rgReaderStates = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.getStatusChangeA;
+ dwTimeOut = call->dwTimeOut;
+
+ if (call->cReaders > 0)
+ {
+ ret.cReaders = call->cReaders;
+ rgReaderStates = calloc(ret.cReaders, sizeof(SCARD_READERSTATEA));
+ ret.rgReaderStates = (ReaderState_Return*)calloc(ret.cReaders, sizeof(ReaderState_Return));
+ if (!rgReaderStates || !ret.rgReaderStates)
+ goto fail;
+ }
+
+ for (UINT32 x = 0; x < MAX(1, dwTimeOut);)
+ {
+ if (call->cReaders > 0)
+ memcpy(rgReaderStates, call->rgReaderStates,
+ call->cReaders * sizeof(SCARD_READERSTATEA));
+ ret.ReturnCode = wrap(smartcard, SCardGetStatusChangeA, operation->hContext,
+ MIN(dwTimeOut, dwTimeStep), rgReaderStates, call->cReaders);
+ if (ret.ReturnCode != SCARD_E_TIMEOUT)
+ break;
+ if (WaitForSingleObject(smartcard->stopEvent, 0) == WAIT_OBJECT_0)
+ break;
+ if (dwTimeOut != INFINITE)
+ x += dwTimeStep;
+ }
+ scard_log_status_error(TAG, "SCardGetStatusChangeA", ret.ReturnCode);
+
+ for (UINT32 index = 0; index < ret.cReaders; index++)
+ {
+ const SCARD_READERSTATEA* cur = &rgReaderStates[index];
+ ReaderState_Return* rout = &ret.rgReaderStates[index];
+
+ rout->dwCurrentState = cur->dwCurrentState;
+ rout->dwEventState = cur->dwEventState;
+ rout->cbAtr = cur->cbAtr;
+ CopyMemory(&(rout->rgbAtr), cur->rgbAtr, sizeof(rout->rgbAtr));
+ }
+
+ status = smartcard_pack_get_status_change_return(out, &ret, TRUE);
+fail:
+ free(ret.rgReaderStates);
+ free(rgReaderStates);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_GetStatusChangeW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = STATUS_NO_MEMORY;
+ DWORD dwTimeOut = 0;
+ const DWORD dwTimeStep = 100;
+ GetStatusChange_Return ret = { 0 };
+ GetStatusChangeW_Call* call = NULL;
+ LPSCARD_READERSTATEW rgReaderStates = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.getStatusChangeW;
+ dwTimeOut = call->dwTimeOut;
+
+ if (call->cReaders > 0)
+ {
+ ret.cReaders = call->cReaders;
+ rgReaderStates = calloc(ret.cReaders, sizeof(SCARD_READERSTATEW));
+ ret.rgReaderStates = (ReaderState_Return*)calloc(ret.cReaders, sizeof(ReaderState_Return));
+ if (!rgReaderStates || !ret.rgReaderStates)
+ goto fail;
+ }
+
+ for (UINT32 x = 0; x < MAX(1, dwTimeOut);)
+ {
+ if (call->cReaders > 0)
+ memcpy(rgReaderStates, call->rgReaderStates,
+ call->cReaders * sizeof(SCARD_READERSTATEW));
+ {
+ ret.ReturnCode = wrap(smartcard, SCardGetStatusChangeW, operation->hContext,
+ MIN(dwTimeOut, dwTimeStep), rgReaderStates, call->cReaders);
+ }
+ if (ret.ReturnCode != SCARD_E_TIMEOUT)
+ break;
+ if (WaitForSingleObject(smartcard->stopEvent, 0) == WAIT_OBJECT_0)
+ break;
+ if (dwTimeOut != INFINITE)
+ x += dwTimeStep;
+ }
+ scard_log_status_error(TAG, "SCardGetStatusChangeW", ret.ReturnCode);
+
+ for (UINT32 index = 0; index < ret.cReaders; index++)
+ {
+ const SCARD_READERSTATEW* cur = &rgReaderStates[index];
+ ReaderState_Return* rout = &ret.rgReaderStates[index];
+
+ rout->dwCurrentState = cur->dwCurrentState;
+ rout->dwEventState = cur->dwEventState;
+ rout->cbAtr = cur->cbAtr;
+ CopyMemory(&(rout->rgbAtr), cur->rgbAtr, sizeof(rout->rgbAtr));
+ }
+
+ status = smartcard_pack_get_status_change_return(out, &ret, TRUE);
+fail:
+ free(ret.rgReaderStates);
+ free(rgReaderStates);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_Cancel_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ ret.ReturnCode = wrap(smartcard, SCardCancel, operation->hContext);
+ scard_log_status_error(TAG, "SCardCancel", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "Cancel");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ConnectA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ SCARDHANDLE hCard = 0;
+ Connect_Return ret = { 0 };
+ ConnectA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.connectA;
+
+ if ((call->Common.dwPreferredProtocols == SCARD_PROTOCOL_UNDEFINED) &&
+ (call->Common.dwShareMode != SCARD_SHARE_DIRECT))
+ {
+ call->Common.dwPreferredProtocols = SCARD_PROTOCOL_Tx;
+ }
+
+ ret.ReturnCode = wrap(smartcard, SCardConnectA, operation->hContext, (char*)call->szReader,
+ call->Common.dwShareMode, call->Common.dwPreferredProtocols, &hCard,
+ &ret.dwActiveProtocol);
+ smartcard_scard_context_native_to_redir(&(ret.hContext), operation->hContext);
+ smartcard_scard_handle_native_to_redir(&(ret.hCard), hCard);
+
+ status = smartcard_pack_connect_return(out, &ret);
+ if (status != SCARD_S_SUCCESS)
+ goto out_fail;
+
+ status = ret.ReturnCode;
+out_fail:
+
+ return status;
+}
+
+static LONG smartcard_ConnectW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ SCARDHANDLE hCard = 0;
+ Connect_Return ret = { 0 };
+ ConnectW_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.connectW;
+
+ if ((call->Common.dwPreferredProtocols == SCARD_PROTOCOL_UNDEFINED) &&
+ (call->Common.dwShareMode != SCARD_SHARE_DIRECT))
+ {
+ call->Common.dwPreferredProtocols = SCARD_PROTOCOL_Tx;
+ }
+
+ ret.ReturnCode = wrap(smartcard, SCardConnectW, operation->hContext, (WCHAR*)call->szReader,
+ call->Common.dwShareMode, call->Common.dwPreferredProtocols, &hCard,
+ &ret.dwActiveProtocol);
+ smartcard_scard_context_native_to_redir(&(ret.hContext), operation->hContext);
+ smartcard_scard_handle_native_to_redir(&(ret.hCard), hCard);
+
+ status = smartcard_pack_connect_return(out, &ret);
+ if (status != SCARD_S_SUCCESS)
+ goto out_fail;
+
+ status = ret.ReturnCode;
+out_fail:
+
+ return status;
+}
+
+static LONG smartcard_Reconnect_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ Reconnect_Return ret = { 0 };
+ Reconnect_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.reconnect;
+ ret.ReturnCode =
+ wrap(smartcard, SCardReconnect, operation->hCard, call->dwShareMode,
+ call->dwPreferredProtocols, call->dwInitialization, &ret.dwActiveProtocol);
+ scard_log_status_error(TAG, "SCardReconnect", ret.ReturnCode);
+ status = smartcard_pack_reconnect_return(out, &ret);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_Disconnect_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ HCardAndDisposition_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.hCardAndDisposition;
+
+ ret.ReturnCode = wrap(smartcard, SCardDisconnect, operation->hCard, call->dwDisposition);
+ scard_log_status_error(TAG, "SCardDisconnect", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "Disconnect");
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_BeginTransaction_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ ret.ReturnCode = wrap(smartcard, SCardBeginTransaction, operation->hCard);
+ scard_log_status_error(TAG, "SCardBeginTransaction", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "BeginTransaction");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_EndTransaction_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ HCardAndDisposition_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.hCardAndDisposition;
+
+ ret.ReturnCode = wrap(smartcard, SCardEndTransaction, operation->hCard, call->dwDisposition);
+ scard_log_status_error(TAG, "SCardEndTransaction", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "EndTransaction");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_State_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ State_Return ret = { 0 };
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ ret.cbAtrLen = SCARD_ATR_LENGTH;
+ ret.ReturnCode = wrap(smartcard, SCardState, operation->hCard, &ret.dwState, &ret.dwProtocol,
+ (BYTE*)&ret.rgAtr, &ret.cbAtrLen);
+
+ scard_log_status_error(TAG, "SCardState", ret.ReturnCode);
+ status = smartcard_pack_state_return(out, &ret);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_StatusA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ Status_Return ret = { 0 };
+ DWORD cchReaderLen = 0;
+ DWORD cbAtrLen = 0;
+ LPSTR mszReaderNames = NULL;
+ Status_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.status;
+
+ call->cbAtrLen = 32;
+ cbAtrLen = call->cbAtrLen;
+
+ if (call->fmszReaderNamesIsNULL)
+ cchReaderLen = 0;
+ else
+ cchReaderLen = SCARD_AUTOALLOCATE;
+
+ status = ret.ReturnCode =
+ wrap(smartcard, SCardStatusA, operation->hCard,
+ call->fmszReaderNamesIsNULL ? NULL : (LPSTR)&mszReaderNames, &cchReaderLen,
+ &ret.dwState, &ret.dwProtocol, cbAtrLen ? (BYTE*)&ret.pbAtr : NULL, &cbAtrLen);
+
+ scard_log_status_error(TAG, "SCardStatusA", status);
+ if (status == SCARD_S_SUCCESS)
+ {
+ if (!call->fmszReaderNamesIsNULL)
+ ret.mszReaderNames = (BYTE*)mszReaderNames;
+
+ ret.cBytes = cchReaderLen;
+
+ if (call->cbAtrLen)
+ ret.cbAtrLen = cbAtrLen;
+ }
+
+ status = smartcard_pack_status_return(out, &ret, FALSE);
+
+ if (mszReaderNames)
+ wrap(smartcard, SCardFreeMemory, operation->hContext, mszReaderNames);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_StatusW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ Status_Return ret = { 0 };
+ LPWSTR mszReaderNames = NULL;
+ Status_Call* call = NULL;
+ DWORD cbAtrLen = 0;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.status;
+
+ /**
+ * [MS-RDPESC]
+ * According to 2.2.2.18 Status_Call cbAtrLen is unused an must be ignored upon receipt.
+ */
+ cbAtrLen = call->cbAtrLen = 32;
+
+ if (call->fmszReaderNamesIsNULL)
+ ret.cBytes = 0;
+ else
+ ret.cBytes = SCARD_AUTOALLOCATE;
+
+ status = ret.ReturnCode =
+ wrap(smartcard, SCardStatusW, operation->hCard,
+ call->fmszReaderNamesIsNULL ? NULL : (LPWSTR)&mszReaderNames, &ret.cBytes,
+ &ret.dwState, &ret.dwProtocol, (BYTE*)&ret.pbAtr, &cbAtrLen);
+ scard_log_status_error(TAG, "SCardStatusW", status);
+ if (status == SCARD_S_SUCCESS)
+ {
+ if (!call->fmszReaderNamesIsNULL)
+ ret.mszReaderNames = (BYTE*)mszReaderNames;
+
+ ret.cbAtrLen = cbAtrLen;
+ }
+
+ /* SCardStatusW returns number of characters, we need number of bytes */
+ ret.cBytes *= sizeof(WCHAR);
+
+ status = smartcard_pack_status_return(out, &ret, TRUE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (mszReaderNames)
+ wrap(smartcard, SCardFreeMemory, operation->hContext, mszReaderNames);
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_Transmit_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ Transmit_Return ret = { 0 };
+ Transmit_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.transmit;
+ ret.cbRecvLength = 0;
+ ret.pbRecvBuffer = NULL;
+
+ if (call->cbRecvLength && !call->fpbRecvBufferIsNULL)
+ {
+ if (call->cbRecvLength >= 66560)
+ call->cbRecvLength = 66560;
+
+ ret.cbRecvLength = call->cbRecvLength;
+ ret.pbRecvBuffer = (BYTE*)malloc(ret.cbRecvLength);
+
+ if (!ret.pbRecvBuffer)
+ return STATUS_NO_MEMORY;
+ }
+
+ ret.pioRecvPci = call->pioRecvPci;
+ ret.ReturnCode =
+ wrap(smartcard, SCardTransmit, operation->hCard, call->pioSendPci, call->pbSendBuffer,
+ call->cbSendLength, ret.pioRecvPci, ret.pbRecvBuffer, &(ret.cbRecvLength));
+
+ scard_log_status_error(TAG, "SCardTransmit", ret.ReturnCode);
+
+ status = smartcard_pack_transmit_return(out, &ret);
+ free(ret.pbRecvBuffer);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_Control_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ Control_Return ret = { 0 };
+ Control_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.control;
+ ret.cbOutBufferSize = call->cbOutBufferSize;
+ ret.pvOutBuffer = (BYTE*)malloc(call->cbOutBufferSize);
+
+ if (!ret.pvOutBuffer)
+ return SCARD_E_NO_MEMORY;
+
+ ret.ReturnCode =
+ wrap(smartcard, SCardControl, operation->hCard, call->dwControlCode, call->pvInBuffer,
+ call->cbInBufferSize, ret.pvOutBuffer, call->cbOutBufferSize, &ret.cbOutBufferSize);
+ scard_log_status_error(TAG, "SCardControl", ret.ReturnCode);
+ status = smartcard_pack_control_return(out, &ret);
+
+ free(ret.pvOutBuffer);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_GetAttrib_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ BOOL autoAllocate = FALSE;
+ LONG status = 0;
+ DWORD cbAttrLen = 0;
+ LPBYTE pbAttr = NULL;
+ GetAttrib_Return ret = { 0 };
+ const GetAttrib_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.getAttrib;
+
+ if (!call->fpbAttrIsNULL)
+ {
+ autoAllocate = (call->cbAttrLen == SCARD_AUTOALLOCATE) ? TRUE : FALSE;
+ cbAttrLen = call->cbAttrLen;
+ if (cbAttrLen && !autoAllocate)
+ {
+ ret.pbAttr = (BYTE*)malloc(cbAttrLen);
+
+ if (!ret.pbAttr)
+ return SCARD_E_NO_MEMORY;
+ }
+
+ pbAttr = autoAllocate ? (LPBYTE) & (ret.pbAttr) : ret.pbAttr;
+ }
+
+ ret.ReturnCode =
+ wrap(smartcard, SCardGetAttrib, operation->hCard, call->dwAttrId, pbAttr, &cbAttrLen);
+ scard_log_status_error(TAG, "SCardGetAttrib", ret.ReturnCode);
+ ret.cbAttrLen = cbAttrLen;
+
+ status = smartcard_pack_get_attrib_return(out, &ret, call->dwAttrId, call->cbAttrLen);
+
+ if (autoAllocate)
+ wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbAttr);
+ else
+ free(ret.pbAttr);
+ return status;
+}
+
+static LONG smartcard_SetAttrib_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ SetAttrib_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.setAttrib;
+
+ ret.ReturnCode = wrap(smartcard, SCardSetAttrib, operation->hCard, call->dwAttrId, call->pbAttr,
+ call->cbAttrLen);
+ scard_log_status_error(TAG, "SCardSetAttrib", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "SetAttrib");
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_AccessStartedEvent_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = SCARD_S_SUCCESS;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_UNUSED(operation);
+
+ if (!smartcard->StartedEvent)
+ smartcard->StartedEvent = wrap_ptr(smartcard, SCardAccessStartedEvent);
+
+ if (!smartcard->StartedEvent)
+ status = SCARD_E_NO_SERVICE;
+
+ return status;
+}
+
+static LONG smartcard_LocateCardsByATRA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ GetStatusChange_Return ret = { 0 };
+ LPSCARD_READERSTATEA state = NULL;
+ LPSCARD_READERSTATEA states = NULL;
+ LocateCardsByATRA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.locateCardsByATRA;
+ states = (LPSCARD_READERSTATEA)calloc(call->cReaders, sizeof(SCARD_READERSTATEA));
+
+ if (!states)
+ return STATUS_NO_MEMORY;
+
+ for (UINT32 i = 0; i < call->cReaders; i++)
+ {
+ states[i].szReader = (LPSTR)call->rgReaderStates[i].szReader;
+ states[i].dwCurrentState = call->rgReaderStates[i].dwCurrentState;
+ states[i].dwEventState = call->rgReaderStates[i].dwEventState;
+ states[i].cbAtr = call->rgReaderStates[i].cbAtr;
+ CopyMemory(&(states[i].rgbAtr), &(call->rgReaderStates[i].rgbAtr), 36);
+ }
+
+ status = ret.ReturnCode = wrap(smartcard, SCardGetStatusChangeA, operation->hContext,
+ 0x000001F4, states, call->cReaders);
+
+ scard_log_status_error(TAG, "SCardGetStatusChangeA", status);
+ for (UINT32 i = 0; i < call->cAtrs; i++)
+ {
+ for (UINT32 j = 0; j < call->cReaders; j++)
+ {
+ for (UINT32 k = 0; k < call->rgAtrMasks[i].cbAtr; k++)
+ {
+ if ((call->rgAtrMasks[i].rgbAtr[k] & call->rgAtrMasks[i].rgbMask[k]) !=
+ (states[j].rgbAtr[k] & call->rgAtrMasks[i].rgbMask[k]))
+ {
+ break;
+ }
+
+ states[j].dwEventState |= SCARD_STATE_ATRMATCH;
+ }
+ }
+ }
+
+ ret.cReaders = call->cReaders;
+ ret.rgReaderStates = NULL;
+
+ if (ret.cReaders > 0)
+ ret.rgReaderStates = (ReaderState_Return*)calloc(ret.cReaders, sizeof(ReaderState_Return));
+
+ if (!ret.rgReaderStates)
+ {
+ free(states);
+ return STATUS_NO_MEMORY;
+ }
+
+ for (UINT32 i = 0; i < ret.cReaders; i++)
+ {
+ state = &states[i];
+ ret.rgReaderStates[i].dwCurrentState = state->dwCurrentState;
+ ret.rgReaderStates[i].dwEventState = state->dwEventState;
+ ret.rgReaderStates[i].cbAtr = state->cbAtr;
+ CopyMemory(&(ret.rgReaderStates[i].rgbAtr), &(state->rgbAtr),
+ sizeof(ret.rgReaderStates[i].rgbAtr));
+ }
+
+ free(states);
+
+ status = smartcard_pack_get_status_change_return(out, &ret, FALSE);
+
+ free(ret.rgReaderStates);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret.ReturnCode;
+}
+
+LONG smartcard_irp_device_control_call(scard_call_context* smartcard, wStream* out,
+ UINT32* pIoStatus, SMARTCARD_OPERATION* operation)
+{
+ LONG result = 0;
+ UINT32 offset = 0;
+ UINT32 ioControlCode = 0;
+ size_t outputBufferLength = 0;
+ size_t objectBufferLength = 0;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(pIoStatus);
+ WINPR_ASSERT(operation);
+
+ ioControlCode = operation->ioControlCode;
+ /**
+ * [MS-RDPESC] 3.2.5.1: Sending Outgoing Messages:
+ * the output buffer length SHOULD be set to 2048
+ *
+ * Since it's a SHOULD and not a MUST, we don't care
+ * about it, but we still reserve at least 2048 bytes.
+ */
+ if (!Stream_EnsureRemainingCapacity(out, 2048))
+ return SCARD_E_NO_MEMORY;
+
+ /* Device Control Response */
+ Stream_Write_UINT32(out, 0); /* OutputBufferLength (4 bytes) */
+ Stream_Zero(out, SMARTCARD_COMMON_TYPE_HEADER_LENGTH); /* CommonTypeHeader (8 bytes) */
+ Stream_Zero(out, SMARTCARD_PRIVATE_TYPE_HEADER_LENGTH); /* PrivateTypeHeader (8 bytes) */
+ Stream_Write_UINT32(out, 0); /* Result (4 bytes) */
+
+ /* Call */
+ switch (ioControlCode)
+ {
+ case SCARD_IOCTL_ESTABLISHCONTEXT:
+ result = smartcard_EstablishContext_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_RELEASECONTEXT:
+ result = smartcard_ReleaseContext_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_ISVALIDCONTEXT:
+ result = smartcard_IsValidContext_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_LISTREADERGROUPSA:
+ result = smartcard_ListReaderGroupsA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_LISTREADERGROUPSW:
+ result = smartcard_ListReaderGroupsW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_LISTREADERSA:
+ result = smartcard_ListReadersA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_LISTREADERSW:
+ result = smartcard_ListReadersW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_INTRODUCEREADERGROUPA:
+ result = smartcard_IntroduceReaderGroupA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_INTRODUCEREADERGROUPW:
+ result = smartcard_IntroduceReaderGroupW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_FORGETREADERGROUPA:
+ result = smartcard_ForgetReaderA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_FORGETREADERGROUPW:
+ result = smartcard_ForgetReaderW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_INTRODUCEREADERA:
+ result = smartcard_IntroduceReaderA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_INTRODUCEREADERW:
+ result = smartcard_IntroduceReaderW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_FORGETREADERA:
+ result = smartcard_ForgetReaderA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_FORGETREADERW:
+ result = smartcard_ForgetReaderW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_ADDREADERTOGROUPA:
+ result = smartcard_AddReaderToGroupA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_ADDREADERTOGROUPW:
+ result = smartcard_AddReaderToGroupW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_REMOVEREADERFROMGROUPA:
+ result = smartcard_RemoveReaderFromGroupA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_REMOVEREADERFROMGROUPW:
+ result = smartcard_RemoveReaderFromGroupW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_LOCATECARDSA:
+ result = smartcard_LocateCardsA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_LOCATECARDSW:
+ result = smartcard_LocateCardsW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_GETSTATUSCHANGEA:
+ result = smartcard_GetStatusChangeA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_GETSTATUSCHANGEW:
+ result = smartcard_GetStatusChangeW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_CANCEL:
+ result = smartcard_Cancel_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_CONNECTA:
+ result = smartcard_ConnectA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_CONNECTW:
+ result = smartcard_ConnectW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_RECONNECT:
+ result = smartcard_Reconnect_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_DISCONNECT:
+ result = smartcard_Disconnect_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_BEGINTRANSACTION:
+ result = smartcard_BeginTransaction_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_ENDTRANSACTION:
+ result = smartcard_EndTransaction_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_STATE:
+ result = smartcard_State_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_STATUSA:
+ result = smartcard_StatusA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_STATUSW:
+ result = smartcard_StatusW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_TRANSMIT:
+ result = smartcard_Transmit_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_CONTROL:
+ result = smartcard_Control_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_GETATTRIB:
+ result = smartcard_GetAttrib_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_SETATTRIB:
+ result = smartcard_SetAttrib_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_ACCESSSTARTEDEVENT:
+ result = smartcard_AccessStartedEvent_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_LOCATECARDSBYATRA:
+ result = smartcard_LocateCardsByATRA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_LOCATECARDSBYATRW:
+ result = smartcard_LocateCardsW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_READCACHEA:
+ result = smartcard_ReadCacheA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_READCACHEW:
+ result = smartcard_ReadCacheW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_WRITECACHEA:
+ result = smartcard_WriteCacheA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_WRITECACHEW:
+ result = smartcard_WriteCacheW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_GETTRANSMITCOUNT:
+ result = smartcard_GetTransmitCount_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_RELEASETARTEDEVENT:
+ result = smartcard_ReleaseStartedEvent_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_GETREADERICON:
+ result = smartcard_GetReaderIcon_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_GETDEVICETYPEID:
+ result = smartcard_GetDeviceTypeId_Call(smartcard, out, operation);
+ break;
+
+ default:
+ result = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ /**
+ * [MS-RPCE] 2.2.6.3 Primitive Type Serialization
+ * The type MUST be aligned on an 8-byte boundary. If the size of the
+ * primitive type is not a multiple of 8 bytes, the data MUST be padded.
+ */
+
+ if ((ioControlCode != SCARD_IOCTL_ACCESSSTARTEDEVENT) &&
+ (ioControlCode != SCARD_IOCTL_RELEASETARTEDEVENT))
+ {
+ offset = (RDPDR_DEVICE_IO_RESPONSE_LENGTH + RDPDR_DEVICE_IO_CONTROL_RSP_HDR_LENGTH);
+ smartcard_pack_write_size_align(out, Stream_GetPosition(out) - offset, 8);
+ }
+
+ if ((result != SCARD_S_SUCCESS) && (result != SCARD_E_TIMEOUT) &&
+ (result != SCARD_E_NO_READERS_AVAILABLE) && (result != SCARD_E_NO_SERVICE) &&
+ (result != SCARD_W_CACHE_ITEM_NOT_FOUND) && (result != SCARD_W_CACHE_ITEM_STALE))
+ {
+ WLog_WARN(TAG, "IRP failure: %s (0x%08" PRIX32 "), status: %s (0x%08" PRIX32 ")",
+ scard_get_ioctl_string(ioControlCode, TRUE), ioControlCode,
+ SCardGetErrorString(result), result);
+ }
+
+ *pIoStatus = STATUS_SUCCESS;
+
+ if ((result & 0xC0000000L) == 0xC0000000L)
+ {
+ /* NTSTATUS error */
+ *pIoStatus = (UINT32)result;
+ WLog_WARN(TAG, "IRP failure: %s (0x%08" PRIX32 "), ntstatus: 0x%08" PRIX32 "",
+ scard_get_ioctl_string(ioControlCode, TRUE), ioControlCode, result);
+ }
+
+ Stream_SealLength(out);
+ outputBufferLength = Stream_Length(out);
+ WINPR_ASSERT(outputBufferLength >= RDPDR_DEVICE_IO_RESPONSE_LENGTH - 4U);
+ outputBufferLength -= (RDPDR_DEVICE_IO_RESPONSE_LENGTH + 4U);
+ WINPR_ASSERT(outputBufferLength >= RDPDR_DEVICE_IO_RESPONSE_LENGTH);
+ objectBufferLength = outputBufferLength - RDPDR_DEVICE_IO_RESPONSE_LENGTH;
+ WINPR_ASSERT(outputBufferLength <= UINT32_MAX);
+ WINPR_ASSERT(objectBufferLength <= UINT32_MAX);
+ Stream_SetPosition(out, RDPDR_DEVICE_IO_RESPONSE_LENGTH);
+ /* Device Control Response */
+ Stream_Write_UINT32(out, (UINT32)outputBufferLength); /* OutputBufferLength (4 bytes) */
+ smartcard_pack_common_type_header(out); /* CommonTypeHeader (8 bytes) */
+ smartcard_pack_private_type_header(
+ out, (UINT32)objectBufferLength); /* PrivateTypeHeader (8 bytes) */
+ Stream_Write_INT32(out, result); /* Result (4 bytes) */
+ Stream_SetPosition(out, Stream_Length(out));
+ return SCARD_S_SUCCESS;
+}
+
+void context_free(void* arg)
+{
+ struct s_scard_context_element* element = arg;
+ if (!arg)
+ return;
+
+ if (element->fn_free)
+ element->fn_free(element->context);
+ free(element);
+}
+
+scard_call_context* smartcard_call_context_new(const rdpSettings* settings)
+{
+ wObject* obj = NULL;
+ scard_call_context* ctx = NULL;
+
+ WINPR_ASSERT(settings);
+ ctx = calloc(1, sizeof(scard_call_context));
+ if (!ctx)
+ goto fail;
+
+ ctx->stopEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
+ if (!ctx->stopEvent)
+ goto fail;
+
+ ctx->names = LinkedList_New();
+ if (!ctx->names)
+ goto fail;
+
+#if defined(WITH_SMARTCARD_EMULATE)
+ ctx->useEmulatedCard = freerdp_settings_get_bool(settings, FreeRDP_SmartcardEmulation);
+#endif
+
+ if (ctx->useEmulatedCard)
+ {
+#if defined(WITH_SMARTCARD_EMULATE)
+ ctx->emulation = Emulate_New(settings);
+ if (!ctx->emulation)
+ goto fail;
+#else
+ WLog_ERR(TAG, "Smartcard emulation requested, but not supported!");
+ goto fail;
+#endif
+ }
+ else
+ {
+ const char* WinSCardModule = freerdp_settings_get_string(settings, FreeRDP_WinSCardModule);
+ if (WinSCardModule)
+ {
+ ctx->hWinSCardLibrary = LoadLibraryX(WinSCardModule);
+
+ if (!ctx->hWinSCardLibrary)
+ {
+ WLog_ERR(TAG, "Failed to load WinSCard library: '%s'", WinSCardModule);
+ goto fail;
+ }
+
+ if (!WinSCard_LoadApiTableFunctions(&ctx->WinSCardApi, ctx->hWinSCardLibrary))
+ goto fail;
+ ctx->pWinSCardApi = &ctx->WinSCardApi;
+ }
+ else
+ {
+ ctx->pWinSCardApi = WinPR_GetSCardApiFunctionTable();
+ }
+
+ if (!ctx->pWinSCardApi)
+ {
+ WLog_ERR(TAG, "Failed to load WinSCard API!");
+ goto fail;
+ }
+ }
+
+ ctx->rgSCardContextList = HashTable_New(FALSE);
+ if (!ctx->rgSCardContextList)
+ goto fail;
+
+ obj = HashTable_ValueObject(ctx->rgSCardContextList);
+ WINPR_ASSERT(obj);
+ obj->fnObjectFree = context_free;
+
+ return ctx;
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ smartcard_call_context_free(ctx);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void smartcard_call_context_free(scard_call_context* ctx)
+{
+ if (!ctx)
+ return;
+
+ smartcard_call_context_signal_stop(ctx, FALSE);
+
+ LinkedList_Free(ctx->names);
+ if (ctx->StartedEvent)
+ {
+ WINPR_ASSERT(ctx->useEmulatedCard || ctx->pWinSCardApi);
+ wrap(ctx, SCardReleaseStartedEvent);
+ }
+
+ if (ctx->useEmulatedCard)
+ {
+#ifdef WITH_SMARTCARD_EMULATE
+ if (ctx->emulation)
+ {
+ Emulate_Free(ctx->emulation);
+ ctx->emulation = NULL;
+ }
+#endif
+ }
+
+ if (ctx->hWinSCardLibrary)
+ {
+ ZeroMemory(&ctx->WinSCardApi, sizeof(SCardApiFunctionTable));
+ FreeLibrary(ctx->hWinSCardLibrary);
+ ctx->hWinSCardLibrary = NULL;
+ }
+
+ ctx->pWinSCardApi = NULL;
+
+ HashTable_Free(ctx->rgSCardContextList);
+ CloseHandle(ctx->stopEvent);
+ free(ctx);
+}
+
+BOOL smartcard_call_context_add(scard_call_context* ctx, const char* name)
+{
+ WINPR_ASSERT(ctx);
+ WINPR_ASSERT(name);
+ return LinkedList_AddLast(ctx->names, name);
+}
+
+BOOL smartcard_call_cancel_context(scard_call_context* ctx, SCARDCONTEXT hContext)
+{
+ WINPR_ASSERT(ctx);
+ if (wrap(ctx, SCardIsValidContext, hContext) == SCARD_S_SUCCESS)
+ {
+ wrap(ctx, SCardCancel, hContext);
+ }
+ return TRUE;
+}
+
+BOOL smartcard_call_release_context(scard_call_context* ctx, SCARDCONTEXT hContext)
+{
+ WINPR_ASSERT(ctx);
+ wrap(ctx, SCardReleaseContext, hContext);
+ return TRUE;
+}
+
+BOOL smartcard_call_cancel_all_context(scard_call_context* ctx)
+{
+ WINPR_ASSERT(ctx);
+
+ HashTable_Clear(ctx->rgSCardContextList);
+ return TRUE;
+}
+
+BOOL smarcard_call_set_callbacks(scard_call_context* ctx, void* userdata,
+ void* (*fn_new)(void*, SCARDCONTEXT), void (*fn_free)(void*))
+{
+ WINPR_ASSERT(ctx);
+ ctx->userdata = userdata;
+ ctx->fn_new = fn_new;
+ ctx->fn_free = fn_free;
+ return TRUE;
+}
+
+void* smartcard_call_get_context(scard_call_context* ctx, SCARDCONTEXT hContext)
+{
+ struct s_scard_context_element* element = NULL;
+
+ WINPR_ASSERT(ctx);
+ element = HashTable_GetItemValue(ctx->rgSCardContextList, (void*)hContext);
+ if (!element)
+ return NULL;
+ return element->context;
+}
+
+BOOL smartcard_call_is_configured(scard_call_context* ctx)
+{
+ WINPR_ASSERT(ctx);
+
+#if defined(WITH_SMARTCARD_EMULATE)
+ if (ctx->useEmulatedCard)
+ return Emulate_IsConfigured(ctx->emulation);
+#endif
+
+ return FALSE;
+}
+
+BOOL smartcard_call_context_signal_stop(scard_call_context* ctx, BOOL reset)
+{
+ WINPR_ASSERT(ctx);
+ if (!ctx->stopEvent)
+ return TRUE;
+
+ if (reset)
+ return ResetEvent(ctx->stopEvent);
+ else
+ return SetEvent(ctx->stopEvent);
+}
diff --git a/libfreerdp/utils/smartcard_operations.c b/libfreerdp/utils/smartcard_operations.c
new file mode 100644
index 0000000..fd7be82
--- /dev/null
+++ b/libfreerdp/utils/smartcard_operations.c
@@ -0,0 +1,1048 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Smartcard Device Service Virtual Channel
+ *
+ * Copyright (C) Alexi Volkov <alexi@myrealbox.com> 2006
+ * Copyright 2011 O.S. Systems Software Ltda.
+ * Copyright 2011 Anthony Tong <atong@trustedcs.com>
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
+ * Copyright 2017 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2017 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <winpr/assert.h>
+
+#include <winpr/crt.h>
+#include <winpr/print.h>
+#include <winpr/stream.h>
+#include <winpr/smartcard.h>
+
+#include <freerdp/freerdp.h>
+#include <freerdp/channels/rdpdr.h>
+#include <freerdp/channels/scard.h>
+
+#include <freerdp/utils/rdpdr_utils.h>
+
+#include <freerdp/utils/smartcard_operations.h>
+#include <freerdp/utils/smartcard_pack.h>
+
+#include <freerdp/log.h>
+#define TAG FREERDP_TAG("utils.smartcard.ops")
+
+static LONG smartcard_call_to_operation_handle(SMARTCARD_OPERATION* operation)
+{
+ WINPR_ASSERT(operation);
+
+ operation->hContext =
+ smartcard_scard_context_native_from_redir(&(operation->call.handles.hContext));
+ operation->hCard = smartcard_scard_handle_native_from_redir(&(operation->call.handles.hCard));
+
+ return SCARD_S_SUCCESS;
+}
+
+static LONG smartcard_EstablishContext_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_establish_context_call(s, &operation->call.establishContext);
+ if (status != SCARD_S_SUCCESS)
+ {
+ return scard_log_status_error(TAG, "smartcard_unpack_establish_context_call", status);
+ }
+
+ return SCARD_S_SUCCESS;
+}
+
+static LONG smartcard_ReleaseContext_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_context_call(s, &operation->call.context, "ReleaseContext");
+ if (status != SCARD_S_SUCCESS)
+ scard_log_status_error(TAG, "smartcard_unpack_context_call", status);
+
+ return status;
+}
+
+static LONG smartcard_IsValidContext_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_context_call(s, &operation->call.context, "IsValidContext");
+
+ return status;
+}
+
+static LONG smartcard_ListReaderGroupsA_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_list_reader_groups_call(s, &operation->call.listReaderGroups, FALSE);
+
+ return status;
+}
+
+static LONG smartcard_ListReaderGroupsW_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_list_reader_groups_call(s, &operation->call.listReaderGroups, TRUE);
+
+ return status;
+}
+
+static LONG smartcard_ListReadersA_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_list_readers_call(s, &operation->call.listReaders, FALSE);
+
+ return status;
+}
+
+static LONG smartcard_ListReadersW_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_list_readers_call(s, &operation->call.listReaders, TRUE);
+
+ return status;
+}
+
+static LONG smartcard_context_and_two_strings_a_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status =
+ smartcard_unpack_context_and_two_strings_a_call(s, &operation->call.contextAndTwoStringA);
+
+ return status;
+}
+
+static LONG smartcard_context_and_two_strings_w_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status =
+ smartcard_unpack_context_and_two_strings_w_call(s, &operation->call.contextAndTwoStringW);
+
+ return status;
+}
+
+static LONG smartcard_context_and_string_a_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_context_and_string_a_call(s, &operation->call.contextAndStringA);
+
+ return status;
+}
+
+static LONG smartcard_context_and_string_w_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_context_and_string_w_call(s, &operation->call.contextAndStringW);
+
+ return status;
+}
+
+static LONG smartcard_LocateCardsA_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_locate_cards_a_call(s, &operation->call.locateCardsA);
+
+ return status;
+}
+
+static LONG smartcard_LocateCardsW_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_locate_cards_w_call(s, &operation->call.locateCardsW);
+
+ return status;
+}
+
+static LONG smartcard_GetStatusChangeA_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_get_status_change_a_call(s, &operation->call.getStatusChangeA);
+
+ return status;
+}
+
+static LONG smartcard_GetStatusChangeW_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_get_status_change_w_call(s, &operation->call.getStatusChangeW);
+
+ return status;
+}
+
+static LONG smartcard_Cancel_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_context_call(s, &operation->call.context, "Cancel");
+
+ return status;
+}
+
+static LONG smartcard_ConnectA_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_connect_a_call(s, &operation->call.connectA);
+
+ return status;
+}
+
+static LONG smartcard_ConnectW_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_connect_w_call(s, &operation->call.connectW);
+
+ return status;
+}
+
+static LONG smartcard_Reconnect_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_reconnect_call(s, &operation->call.reconnect);
+
+ return status;
+}
+
+static LONG smartcard_Disconnect_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_hcard_and_disposition_call(s, &operation->call.hCardAndDisposition,
+ "Disconnect");
+
+ return status;
+}
+
+static LONG smartcard_BeginTransaction_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_hcard_and_disposition_call(s, &operation->call.hCardAndDisposition,
+ "BeginTransaction");
+
+ return status;
+}
+
+static LONG smartcard_EndTransaction_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_hcard_and_disposition_call(s, &operation->call.hCardAndDisposition,
+ "EndTransaction");
+
+ return status;
+}
+
+static LONG smartcard_State_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_state_call(s, &operation->call.state);
+
+ return status;
+}
+
+static LONG smartcard_StatusA_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_status_call(s, &operation->call.status, FALSE);
+
+ return status;
+}
+
+static LONG smartcard_StatusW_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_status_call(s, &operation->call.status, TRUE);
+
+ return status;
+}
+
+static LONG smartcard_Transmit_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_transmit_call(s, &operation->call.transmit);
+
+ return status;
+}
+
+static LONG smartcard_Control_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_control_call(s, &operation->call.control);
+
+ return status;
+}
+
+static LONG smartcard_GetAttrib_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_get_attrib_call(s, &operation->call.getAttrib);
+
+ return status;
+}
+
+static LONG smartcard_SetAttrib_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_set_attrib_call(s, &operation->call.setAttrib);
+
+ return status;
+}
+
+static LONG smartcard_AccessStartedEvent_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return SCARD_F_INTERNAL_ERROR;
+
+ Stream_Read_INT32(s, operation->call.lng.LongValue); /* Unused (4 bytes) */
+
+ return SCARD_S_SUCCESS;
+}
+
+static LONG smartcard_LocateCardsByATRA_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_locate_cards_by_atr_a_call(s, &operation->call.locateCardsByATRA);
+
+ return status;
+}
+
+static LONG smartcard_LocateCardsByATRW_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_locate_cards_by_atr_w_call(s, &operation->call.locateCardsByATRW);
+
+ return status;
+}
+
+static LONG smartcard_ReadCacheA_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_read_cache_a_call(s, &operation->call.readCacheA);
+
+ return status;
+}
+
+static LONG smartcard_ReadCacheW_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_read_cache_w_call(s, &operation->call.readCacheW);
+
+ return status;
+}
+
+static LONG smartcard_WriteCacheA_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_write_cache_a_call(s, &operation->call.writeCacheA);
+
+ return status;
+}
+
+static LONG smartcard_WriteCacheW_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_write_cache_w_call(s, &operation->call.writeCacheW);
+
+ return status;
+}
+
+static LONG smartcard_GetTransmitCount_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_get_transmit_count_call(s, &operation->call.getTransmitCount);
+
+ return status;
+}
+
+static LONG smartcard_ReleaseStartedEvent_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ WINPR_UNUSED(s);
+ WINPR_UNUSED(operation);
+ WLog_WARN(TAG, "According to [MS-RDPESC] 3.1.4 Message Processing Events and Sequencing Rules "
+ "SCARD_IOCTL_RELEASETARTEDEVENT is not supported");
+ return SCARD_E_UNSUPPORTED_FEATURE;
+}
+
+static LONG smartcard_GetReaderIcon_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_get_reader_icon_call(s, &operation->call.getReaderIcon);
+
+ return status;
+}
+
+static LONG smartcard_GetDeviceTypeId_Decode(wStream* s, SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ status = smartcard_unpack_get_device_type_id_call(s, &operation->call.getDeviceTypeId);
+
+ return status;
+}
+
+LONG smartcard_irp_device_control_decode(wStream* s, UINT32 CompletionId, UINT32 FileId,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ UINT32 offset = 0;
+ UINT32 ioControlCode = 0;
+ UINT32 outputBufferLength = 0;
+ UINT32 inputBufferLength = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(operation);
+
+ /* Device Control Request */
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 32))
+ return SCARD_F_INTERNAL_ERROR;
+
+ Stream_Read_UINT32(s, outputBufferLength); /* OutputBufferLength (4 bytes) */
+ Stream_Read_UINT32(s, inputBufferLength); /* InputBufferLength (4 bytes) */
+ Stream_Read_UINT32(s, ioControlCode); /* IoControlCode (4 bytes) */
+ Stream_Seek(s, 20); /* Padding (20 bytes) */
+ operation->ioControlCode = ioControlCode;
+ operation->ioControlCodeName = scard_get_ioctl_string(ioControlCode, FALSE);
+
+ if (Stream_Length(s) != (Stream_GetPosition(s) + inputBufferLength))
+ {
+ WLog_WARN(TAG, "InputBufferLength mismatch: Actual: %" PRIuz " Expected: %" PRIuz "",
+ Stream_Length(s), Stream_GetPosition(s) + inputBufferLength);
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ WLog_DBG(TAG, "%s (0x%08" PRIX32 ") FileId: %" PRIu32 " CompletionId: %" PRIu32 "",
+ scard_get_ioctl_string(ioControlCode, TRUE), ioControlCode, FileId, CompletionId);
+
+ if ((ioControlCode != SCARD_IOCTL_ACCESSSTARTEDEVENT) &&
+ (ioControlCode != SCARD_IOCTL_RELEASETARTEDEVENT))
+ {
+ 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;
+ }
+
+ /* Decode */
+ switch (ioControlCode)
+ {
+ case SCARD_IOCTL_ESTABLISHCONTEXT:
+ status = smartcard_EstablishContext_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_RELEASECONTEXT:
+ status = smartcard_ReleaseContext_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_ISVALIDCONTEXT:
+ status = smartcard_IsValidContext_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_LISTREADERGROUPSA:
+ status = smartcard_ListReaderGroupsA_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_LISTREADERGROUPSW:
+ status = smartcard_ListReaderGroupsW_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_LISTREADERSA:
+ status = smartcard_ListReadersA_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_LISTREADERSW:
+ status = smartcard_ListReadersW_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_INTRODUCEREADERGROUPA:
+ status = smartcard_context_and_string_a_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_INTRODUCEREADERGROUPW:
+ status = smartcard_context_and_string_w_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_FORGETREADERGROUPA:
+ status = smartcard_context_and_string_a_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_FORGETREADERGROUPW:
+ status = smartcard_context_and_string_w_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_INTRODUCEREADERA:
+ status = smartcard_context_and_two_strings_a_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_INTRODUCEREADERW:
+ status = smartcard_context_and_two_strings_w_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_FORGETREADERA:
+ status = smartcard_context_and_string_a_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_FORGETREADERW:
+ status = smartcard_context_and_string_w_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_ADDREADERTOGROUPA:
+ status = smartcard_context_and_two_strings_a_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_ADDREADERTOGROUPW:
+ status = smartcard_context_and_two_strings_w_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_REMOVEREADERFROMGROUPA:
+ status = smartcard_context_and_two_strings_a_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_REMOVEREADERFROMGROUPW:
+ status = smartcard_context_and_two_strings_w_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_LOCATECARDSA:
+ status = smartcard_LocateCardsA_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_LOCATECARDSW:
+ status = smartcard_LocateCardsW_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_GETSTATUSCHANGEA:
+ status = smartcard_GetStatusChangeA_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_GETSTATUSCHANGEW:
+ status = smartcard_GetStatusChangeW_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_CANCEL:
+ status = smartcard_Cancel_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_CONNECTA:
+ status = smartcard_ConnectA_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_CONNECTW:
+ status = smartcard_ConnectW_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_RECONNECT:
+ status = smartcard_Reconnect_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_DISCONNECT:
+ status = smartcard_Disconnect_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_BEGINTRANSACTION:
+ status = smartcard_BeginTransaction_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_ENDTRANSACTION:
+ status = smartcard_EndTransaction_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_STATE:
+ status = smartcard_State_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_STATUSA:
+ status = smartcard_StatusA_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_STATUSW:
+ status = smartcard_StatusW_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_TRANSMIT:
+ status = smartcard_Transmit_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_CONTROL:
+ status = smartcard_Control_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_GETATTRIB:
+ status = smartcard_GetAttrib_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_SETATTRIB:
+ status = smartcard_SetAttrib_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_ACCESSSTARTEDEVENT:
+ status = smartcard_AccessStartedEvent_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_LOCATECARDSBYATRA:
+ status = smartcard_LocateCardsByATRA_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_LOCATECARDSBYATRW:
+ status = smartcard_LocateCardsByATRW_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_READCACHEA:
+ status = smartcard_ReadCacheA_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_READCACHEW:
+ status = smartcard_ReadCacheW_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_WRITECACHEA:
+ status = smartcard_WriteCacheA_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_WRITECACHEW:
+ status = smartcard_WriteCacheW_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_GETTRANSMITCOUNT:
+ status = smartcard_GetTransmitCount_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_RELEASETARTEDEVENT:
+ status = smartcard_ReleaseStartedEvent_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_GETREADERICON:
+ status = smartcard_GetReaderIcon_Decode(s, operation);
+ break;
+
+ case SCARD_IOCTL_GETDEVICETYPEID:
+ status = smartcard_GetDeviceTypeId_Decode(s, operation);
+ break;
+
+ default:
+ status = SCARD_F_INTERNAL_ERROR;
+ break;
+ }
+
+ smartcard_call_to_operation_handle(operation);
+
+ if ((ioControlCode != SCARD_IOCTL_ACCESSSTARTEDEVENT) &&
+ (ioControlCode != SCARD_IOCTL_RELEASETARTEDEVENT))
+ {
+ offset = (RDPDR_DEVICE_IO_REQUEST_LENGTH + RDPDR_DEVICE_IO_CONTROL_REQ_HDR_LENGTH);
+ smartcard_unpack_read_size_align(s, Stream_GetPosition(s) - offset, 8);
+ }
+
+ if (Stream_GetPosition(s) < Stream_Length(s))
+ {
+ SIZE_T difference = 0;
+ difference = Stream_Length(s) - Stream_GetPosition(s);
+ WLog_WARN(TAG,
+ "IRP was not fully parsed %s (%s [0x%08" PRIX32 "]): Actual: %" PRIuz
+ ", Expected: %" PRIuz ", Difference: %" PRIuz "",
+ scard_get_ioctl_string(ioControlCode, TRUE),
+ scard_get_ioctl_string(ioControlCode, FALSE), ioControlCode,
+ Stream_GetPosition(s), Stream_Length(s), difference);
+ winpr_HexDump(TAG, WLOG_WARN, Stream_ConstPointer(s), difference);
+ }
+
+ if (Stream_GetPosition(s) > Stream_Length(s))
+ {
+ SIZE_T difference = 0;
+ difference = Stream_GetPosition(s) - Stream_Length(s);
+ WLog_WARN(TAG,
+ "IRP was parsed beyond its end %s (0x%08" PRIX32 "): Actual: %" PRIuz
+ ", Expected: %" PRIuz ", Difference: %" PRIuz "",
+ scard_get_ioctl_string(ioControlCode, TRUE), ioControlCode, Stream_GetPosition(s),
+ Stream_Length(s), difference);
+ }
+
+ return status;
+}
+
+static void free_reader_states_a(LPSCARD_READERSTATEA rgReaderStates, UINT32 cReaders)
+{
+ for (UINT32 x = 0; x < cReaders; x++)
+ {
+ SCARD_READERSTATEA* state = &rgReaderStates[x];
+ free(state->szReader);
+ }
+
+ free(rgReaderStates);
+}
+
+static void free_reader_states_w(LPSCARD_READERSTATEW rgReaderStates, UINT32 cReaders)
+{
+ for (UINT32 x = 0; x < cReaders; x++)
+ {
+ SCARD_READERSTATEW* state = &rgReaderStates[x];
+ free(state->szReader);
+ }
+
+ free(rgReaderStates);
+}
+
+void smartcard_operation_free(SMARTCARD_OPERATION* op, BOOL allocated)
+{
+ if (!op)
+ return;
+ switch (op->ioControlCode)
+ {
+ case SCARD_IOCTL_CANCEL:
+ case SCARD_IOCTL_ACCESSSTARTEDEVENT:
+ case SCARD_IOCTL_RELEASETARTEDEVENT:
+ case SCARD_IOCTL_LISTREADERGROUPSA:
+ case SCARD_IOCTL_LISTREADERGROUPSW:
+ case SCARD_IOCTL_RECONNECT:
+ case SCARD_IOCTL_DISCONNECT:
+ case SCARD_IOCTL_BEGINTRANSACTION:
+ case SCARD_IOCTL_ENDTRANSACTION:
+ case SCARD_IOCTL_STATE:
+ case SCARD_IOCTL_STATUSA:
+ case SCARD_IOCTL_STATUSW:
+ case SCARD_IOCTL_ESTABLISHCONTEXT:
+ case SCARD_IOCTL_RELEASECONTEXT:
+ case SCARD_IOCTL_ISVALIDCONTEXT:
+ case SCARD_IOCTL_GETATTRIB:
+ case SCARD_IOCTL_GETTRANSMITCOUNT:
+ break;
+
+ case SCARD_IOCTL_LOCATECARDSA:
+ {
+ LocateCardsA_Call* call = &op->call.locateCardsA;
+ free(call->mszCards);
+
+ free_reader_states_a(call->rgReaderStates, call->cReaders);
+ }
+ break;
+ case SCARD_IOCTL_LOCATECARDSW:
+ {
+ LocateCardsW_Call* call = &op->call.locateCardsW;
+ free(call->mszCards);
+
+ free_reader_states_w(call->rgReaderStates, call->cReaders);
+ }
+ break;
+
+ case SCARD_IOCTL_LOCATECARDSBYATRA:
+ {
+ LocateCardsByATRA_Call* call = &op->call.locateCardsByATRA;
+
+ free_reader_states_a(call->rgReaderStates, call->cReaders);
+ }
+ break;
+ case SCARD_IOCTL_LOCATECARDSBYATRW:
+ {
+ LocateCardsByATRW_Call* call = &op->call.locateCardsByATRW;
+ free_reader_states_w(call->rgReaderStates, call->cReaders);
+ }
+ break;
+ case SCARD_IOCTL_FORGETREADERA:
+ case SCARD_IOCTL_INTRODUCEREADERGROUPA:
+ case SCARD_IOCTL_FORGETREADERGROUPA:
+ {
+ ContextAndStringA_Call* call = &op->call.contextAndStringA;
+ free(call->sz);
+ }
+ break;
+
+ case SCARD_IOCTL_FORGETREADERW:
+ case SCARD_IOCTL_INTRODUCEREADERGROUPW:
+ case SCARD_IOCTL_FORGETREADERGROUPW:
+ {
+ ContextAndStringW_Call* call = &op->call.contextAndStringW;
+ 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:
+ {
+ ListReaders_Call* call = &op->call.listReaders;
+ free(call->mszGroups);
+ }
+ break;
+ case SCARD_IOCTL_GETSTATUSCHANGEA:
+ {
+ GetStatusChangeA_Call* call = &op->call.getStatusChangeA;
+ free_reader_states_a(call->rgReaderStates, call->cReaders);
+ }
+ break;
+
+ case SCARD_IOCTL_GETSTATUSCHANGEW:
+ {
+ GetStatusChangeW_Call* call = &op->call.getStatusChangeW;
+ free_reader_states_w(call->rgReaderStates, call->cReaders);
+ }
+ break;
+ case SCARD_IOCTL_GETREADERICON:
+ {
+ GetReaderIcon_Call* call = &op->call.getReaderIcon;
+ free(call->szReaderName);
+ }
+ break;
+ case SCARD_IOCTL_GETDEVICETYPEID:
+ {
+ GetDeviceTypeId_Call* call = &op->call.getDeviceTypeId;
+ free(call->szReaderName);
+ }
+ break;
+ case SCARD_IOCTL_CONNECTA:
+ {
+ ConnectA_Call* call = &op->call.connectA;
+ free(call->szReader);
+ }
+ break;
+ case SCARD_IOCTL_CONNECTW:
+ {
+ ConnectW_Call* call = &op->call.connectW;
+ free(call->szReader);
+ }
+ break;
+ case SCARD_IOCTL_SETATTRIB:
+ free(op->call.setAttrib.pbAttr);
+ break;
+ case SCARD_IOCTL_TRANSMIT:
+ {
+ Transmit_Call* call = &op->call.transmit;
+ free(call->pbSendBuffer);
+ free(call->pioSendPci);
+ free(call->pioRecvPci);
+ }
+ break;
+ case SCARD_IOCTL_CONTROL:
+ {
+ Control_Call* call = &op->call.control;
+ free(call->pvInBuffer);
+ }
+ break;
+ case SCARD_IOCTL_READCACHEA:
+ {
+ ReadCacheA_Call* call = &op->call.readCacheA;
+ free(call->szLookupName);
+ free(call->Common.CardIdentifier);
+ }
+ break;
+ case SCARD_IOCTL_READCACHEW:
+ {
+ ReadCacheW_Call* call = &op->call.readCacheW;
+ free(call->szLookupName);
+ free(call->Common.CardIdentifier);
+ }
+ break;
+ case SCARD_IOCTL_WRITECACHEA:
+ {
+ WriteCacheA_Call* call = &op->call.writeCacheA;
+ free(call->szLookupName);
+ free(call->Common.CardIdentifier);
+ free(call->Common.pbData);
+ }
+ break;
+ case SCARD_IOCTL_WRITECACHEW:
+ {
+ WriteCacheW_Call* call = &op->call.writeCacheW;
+ free(call->szLookupName);
+ free(call->Common.CardIdentifier);
+ free(call->Common.pbData);
+ }
+ break;
+ default:
+ break;
+ }
+
+ {
+ SMARTCARD_OPERATION empty = { 0 };
+ *op = empty;
+ }
+
+ if (allocated)
+ free(op);
+}
diff --git a/libfreerdp/utils/smartcard_pack.c b/libfreerdp/utils/smartcard_pack.c
new file mode 100644
index 0000000..58536ca
--- /dev/null
+++ b/libfreerdp/utils/smartcard_pack.c
@@ -0,0 +1,3649 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Smart Card Structure Packing
+ *
+ * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
+ * Copyright 2020 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2020 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/print.h>
+
+#include <freerdp/channels/scard.h>
+#include <freerdp/utils/smartcard_pack.h>
+
+#include <freerdp/log.h>
+#define TAG FREERDP_TAG("scard.pack")
+
+static const DWORD g_LogLevel = WLOG_DEBUG;
+
+#define smartcard_unpack_redir_scard_context(s, context, index, ndr) \
+ smartcard_unpack_redir_scard_context_((s), (context), (index), (ndr), __FILE__, __func__, \
+ __LINE__)
+#define smartcard_unpack_redir_scard_handle(s, context, index) \
+ smartcard_unpack_redir_scard_handle_((s), (context), (index), __FILE__, __func__, __LINE__)
+
+static LONG smartcard_unpack_redir_scard_context_(wStream* s, REDIR_SCARDCONTEXT* context,
+ UINT32* index, UINT32* ppbContextNdrPtr,
+ const char* file, const char* function, int line);
+static LONG smartcard_pack_redir_scard_context(wStream* s, const REDIR_SCARDCONTEXT* context,
+ DWORD* index);
+static LONG smartcard_unpack_redir_scard_handle_(wStream* s, REDIR_SCARDHANDLE* handle,
+ UINT32* index, const char* file,
+ const char* function, int line);
+static LONG smartcard_pack_redir_scard_handle(wStream* s, const REDIR_SCARDHANDLE* handle,
+ DWORD* index);
+static LONG smartcard_unpack_redir_scard_context_ref(wStream* s, UINT32 pbContextNdrPtr,
+ REDIR_SCARDCONTEXT* context);
+static LONG smartcard_pack_redir_scard_context_ref(wStream* s, const REDIR_SCARDCONTEXT* context);
+
+static LONG smartcard_unpack_redir_scard_handle_ref(wStream* s, REDIR_SCARDHANDLE* handle);
+static LONG smartcard_pack_redir_scard_handle_ref(wStream* s, const REDIR_SCARDHANDLE* handle);
+
+typedef enum
+{
+ NDR_PTR_FULL,
+ NDR_PTR_SIMPLE,
+ NDR_PTR_FIXED
+} ndr_ptr_t;
+
+/* Reads a NDR pointer and checks if the value read has the expected relative
+ * addressing */
+#define smartcard_ndr_pointer_read(s, index, ptr) \
+ smartcard_ndr_pointer_read_((s), (index), (ptr), __FILE__, __func__, __LINE__)
+static BOOL smartcard_ndr_pointer_read_(wStream* s, UINT32* index, UINT32* ptr, const char* file,
+ const char* fkt, size_t line)
+{
+ const UINT32 expect = 0x20000 + (*index) * 4;
+ UINT32 ndrPtr = 0;
+ WINPR_UNUSED(file);
+ if (!s)
+ return FALSE;
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT32(s, ndrPtr); /* mszGroupsNdrPtr (4 bytes) */
+ if (ptr)
+ *ptr = ndrPtr;
+ if (expect != ndrPtr)
+ {
+ /* Allow NULL pointer if we read the result */
+ if (ptr && (ndrPtr == 0))
+ return TRUE;
+ WLog_WARN(TAG,
+ "[%s:%" PRIuz "] Read context pointer 0x%08" PRIx32 ", expected 0x%08" PRIx32,
+ fkt, line, ndrPtr, expect);
+ return FALSE;
+ }
+
+ (*index) = (*index) + 1;
+ return TRUE;
+}
+
+static LONG smartcard_ndr_read(wStream* s, BYTE** data, size_t min, size_t elementSize,
+ ndr_ptr_t type)
+{
+ size_t len = 0;
+ size_t offset = 0;
+ size_t len2 = 0;
+ void* r = NULL;
+ size_t required = 0;
+
+ switch (type)
+ {
+ case NDR_PTR_FULL:
+ required = 12;
+ break;
+ case NDR_PTR_SIMPLE:
+ required = 4;
+ break;
+ case NDR_PTR_FIXED:
+ required = min;
+ break;
+ }
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, required))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ switch (type)
+ {
+ case NDR_PTR_FULL:
+ Stream_Read_UINT32(s, len);
+ Stream_Read_UINT32(s, offset);
+ Stream_Read_UINT32(s, len2);
+ if (len != offset + len2)
+ {
+ WLog_ERR(TAG,
+ "Invalid data when reading full NDR pointer: total=%" PRIu32
+ ", offset=%" PRIu32 ", remaining=%" PRIu32,
+ len, offset, len2);
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+ case NDR_PTR_SIMPLE:
+ Stream_Read_UINT32(s, len);
+
+ if ((len != min) && (min > 0))
+ {
+ WLog_ERR(TAG,
+ "Invalid data when reading simple NDR pointer: total=%" PRIu32
+ ", expected=%" PRIu32,
+ len, min);
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+ case NDR_PTR_FIXED:
+ len = (UINT32)min;
+ break;
+ }
+
+ if (min > len)
+ {
+ WLog_ERR(TAG, "Invalid length read from NDR pointer, minimum %" PRIu32 ", got %" PRIu32,
+ min, len);
+ return STATUS_DATA_ERROR;
+ }
+
+ if (len > SIZE_MAX / 2)
+ return STATUS_BUFFER_TOO_SMALL;
+
+ if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, len, elementSize))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ len *= elementSize;
+
+ r = calloc(len + 1, sizeof(CHAR));
+ if (!r)
+ return SCARD_E_NO_MEMORY;
+ Stream_Read(s, r, len);
+ smartcard_unpack_read_size_align(s, len, 4);
+ *data = r;
+ return STATUS_SUCCESS;
+}
+
+static BOOL smartcard_ndr_pointer_write(wStream* s, UINT32* index, DWORD length)
+{
+ const UINT32 ndrPtr = 0x20000 + (*index) * 4;
+
+ if (!s)
+ return FALSE;
+ if (!Stream_EnsureRemainingCapacity(s, 4))
+ return FALSE;
+
+ if (length > 0)
+ {
+ Stream_Write_UINT32(s, ndrPtr); /* mszGroupsNdrPtr (4 bytes) */
+ (*index) = (*index) + 1;
+ }
+ else
+ Stream_Write_UINT32(s, 0);
+ return TRUE;
+}
+
+static LONG smartcard_ndr_write(wStream* s, const BYTE* data, UINT32 size, UINT32 elementSize,
+ ndr_ptr_t type)
+{
+ const UINT32 offset = 0;
+ const UINT32 len = size;
+ const UINT32 dataLen = size * elementSize;
+ size_t required = 0;
+
+ if (size == 0)
+ return SCARD_S_SUCCESS;
+
+ switch (type)
+ {
+ case NDR_PTR_FULL:
+ required = 12;
+ break;
+ case NDR_PTR_SIMPLE:
+ required = 4;
+ break;
+ case NDR_PTR_FIXED:
+ required = 0;
+ break;
+ }
+
+ if (!Stream_EnsureRemainingCapacity(s, required + dataLen + 4))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ switch (type)
+ {
+ case NDR_PTR_FULL:
+ Stream_Write_UINT32(s, len);
+ Stream_Write_UINT32(s, offset);
+ Stream_Write_UINT32(s, len);
+ break;
+ case NDR_PTR_SIMPLE:
+ Stream_Write_UINT32(s, len);
+ break;
+ case NDR_PTR_FIXED:
+ break;
+ }
+
+ if (data)
+ Stream_Write(s, data, dataLen);
+ else
+ Stream_Zero(s, dataLen);
+ return smartcard_pack_write_size_align(s, len, 4);
+}
+
+static LONG smartcard_ndr_write_state(wStream* s, const ReaderState_Return* data, UINT32 size,
+ ndr_ptr_t type)
+{
+ union
+ {
+ const ReaderState_Return* reader;
+ const BYTE* data;
+ } cnv;
+
+ cnv.reader = data;
+ return smartcard_ndr_write(s, cnv.data, size, sizeof(ReaderState_Return), type);
+}
+
+static LONG smartcard_ndr_read_atrmask(wStream* s, LocateCards_ATRMask** data, size_t min,
+ ndr_ptr_t type)
+{
+ union
+ {
+ LocateCards_ATRMask** ppc;
+ BYTE** ppv;
+ } u;
+ u.ppc = data;
+ return smartcard_ndr_read(s, u.ppv, min, sizeof(LocateCards_ATRMask), type);
+}
+
+static LONG smartcard_ndr_read_fixed_string_a(wStream* s, CHAR** data, size_t min, ndr_ptr_t type)
+{
+ union
+ {
+ CHAR** ppc;
+ BYTE** ppv;
+ } u;
+ u.ppc = data;
+ return smartcard_ndr_read(s, u.ppv, min, sizeof(CHAR), type);
+}
+
+static LONG smartcard_ndr_read_fixed_string_w(wStream* s, WCHAR** data, size_t min, ndr_ptr_t type)
+{
+ union
+ {
+ WCHAR** ppc;
+ BYTE** ppv;
+ } u;
+ u.ppc = data;
+ return smartcard_ndr_read(s, u.ppv, min, sizeof(WCHAR), type);
+}
+
+static LONG smartcard_ndr_read_a(wStream* s, CHAR** data, ndr_ptr_t type)
+{
+ union
+ {
+ CHAR** ppc;
+ BYTE** ppv;
+ } u;
+ u.ppc = data;
+ return smartcard_ndr_read(s, u.ppv, 0, sizeof(CHAR), type);
+}
+
+static LONG smartcard_ndr_read_w(wStream* s, WCHAR** data, ndr_ptr_t type)
+{
+ union
+ {
+ WCHAR** ppc;
+ BYTE** ppv;
+ } u;
+ u.ppc = data;
+ return smartcard_ndr_read(s, u.ppv, 0, sizeof(WCHAR), type);
+}
+
+static LONG smartcard_ndr_read_u(wStream* s, UUID** data)
+{
+ union
+ {
+ UUID** ppc;
+ BYTE** ppv;
+ } u;
+ u.ppc = data;
+ return smartcard_ndr_read(s, u.ppv, 1, sizeof(UUID), NDR_PTR_FIXED);
+}
+
+static char* smartcard_convert_string_list(const void* in, size_t bytes, BOOL unicode)
+{
+ size_t length = 0;
+ union
+ {
+ const void* pv;
+ const char* sz;
+ const WCHAR* wz;
+ } string;
+ char* mszA = NULL;
+
+ string.pv = in;
+
+ if (bytes < 1)
+ return NULL;
+
+ if (in == NULL)
+ return NULL;
+
+ if (unicode)
+ {
+ mszA = ConvertMszWCharNToUtf8Alloc(string.wz, bytes / sizeof(WCHAR), &length);
+ if (!mszA)
+ return NULL;
+ }
+ else
+ {
+ mszA = (char*)calloc(bytes, sizeof(char));
+ if (!mszA)
+ return NULL;
+ CopyMemory(mszA, string.sz, bytes - 1);
+ length = bytes;
+ }
+
+ if (length < 1)
+ {
+ free(mszA);
+ return NULL;
+ }
+ for (size_t index = 0; index < length - 1; index++)
+ {
+ if (mszA[index] == '\0')
+ mszA[index] = ',';
+ }
+
+ return mszA;
+}
+
+static char* smartcard_msz_dump_a(const char* msz, size_t len, char* buffer, size_t bufferLen)
+{
+ char* buf = buffer;
+ const char* cur = msz;
+
+ while ((len > 0) && cur && cur[0] != '\0' && (bufferLen > 0))
+ {
+ size_t clen = strnlen(cur, len);
+ int rc = _snprintf(buf, bufferLen, "%s", cur);
+ bufferLen -= (size_t)rc;
+ buf += rc;
+
+ cur += clen;
+ }
+
+ return buffer;
+}
+
+static char* smartcard_msz_dump_w(const WCHAR* msz, size_t len, char* buffer, size_t bufferLen)
+{
+ size_t szlen = 0;
+ if (!msz)
+ return NULL;
+ char* sz = ConvertMszWCharNToUtf8Alloc(msz, len, &szlen);
+ if (!sz)
+ return NULL;
+
+ smartcard_msz_dump_a(sz, szlen, buffer, bufferLen);
+ free(sz);
+ return buffer;
+}
+
+static char* smartcard_array_dump(const void* pd, size_t len, char* buffer, size_t bufferLen)
+{
+ const BYTE* data = pd;
+ int rc = 0;
+ char* start = buffer;
+
+ /* Ensure '\0' termination */
+ if (bufferLen > 0)
+ {
+ buffer[bufferLen - 1] = '\0';
+ bufferLen--;
+ }
+
+ rc = _snprintf(buffer, bufferLen, "{ ");
+ if ((rc < 0) || ((size_t)rc > bufferLen))
+ goto fail;
+ buffer += rc;
+ bufferLen -= (size_t)rc;
+
+ for (size_t x = 0; x < len; x++)
+ {
+ rc = _snprintf(buffer, bufferLen, "%02X", data[x]);
+ if ((rc < 0) || ((size_t)rc > bufferLen))
+ goto fail;
+ buffer += rc;
+ bufferLen -= (size_t)rc;
+ }
+
+ rc = _snprintf(buffer, bufferLen, " }");
+ if ((rc < 0) || ((size_t)rc > bufferLen))
+ goto fail;
+
+fail:
+ return start;
+}
+static void smartcard_log_redir_handle(const char* tag, const REDIR_SCARDHANDLE* pHandle)
+{
+ char buffer[128];
+
+ WLog_LVL(tag, g_LogLevel, " hContext: %s",
+ smartcard_array_dump(pHandle->pbHandle, pHandle->cbHandle, buffer, sizeof(buffer)));
+}
+
+static void smartcard_log_context(const char* tag, const REDIR_SCARDCONTEXT* phContext)
+{
+ char buffer[128];
+ WLog_DBG(
+ tag, "hContext: %s",
+ smartcard_array_dump(phContext->pbContext, phContext->cbContext, buffer, sizeof(buffer)));
+}
+
+static void smartcard_trace_context_and_string_call_a(const char* name,
+ const REDIR_SCARDCONTEXT* phContext,
+ const CHAR* sz)
+{
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "%s {", name);
+ smartcard_log_context(TAG, phContext);
+ WLog_LVL(TAG, g_LogLevel, " sz=%s", sz);
+
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_context_and_string_call_w(const char* name,
+ const REDIR_SCARDCONTEXT* phContext,
+ const WCHAR* sz)
+{
+ char tmp[1024] = { 0 };
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ if (sz)
+ ConvertWCharToUtf8(sz, tmp, ARRAYSIZE(tmp));
+
+ WLog_LVL(TAG, g_LogLevel, "%s {", name);
+ smartcard_log_context(TAG, phContext);
+ WLog_LVL(TAG, g_LogLevel, " sz=%s", tmp);
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_context_call(const Context_Call* call, const char* name)
+{
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "%s_Call {", name);
+ smartcard_log_context(TAG, &call->handles.hContext);
+
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_list_reader_groups_call(const ListReaderGroups_Call* call, BOOL unicode)
+{
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "ListReaderGroups%S_Call {", unicode ? "W" : "A");
+ smartcard_log_context(TAG, &call->handles.hContext);
+
+ WLog_LVL(TAG, g_LogLevel, "fmszGroupsIsNULL: %" PRId32 " cchGroups: 0x%08" PRIx32,
+ call->fmszGroupsIsNULL, call->cchGroups);
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_get_status_change_w_call(const GetStatusChangeW_Call* call)
+{
+ char* szEventState = NULL;
+ char* szCurrentState = NULL;
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "GetStatusChangeW_Call {");
+ smartcard_log_context(TAG, &call->handles.hContext);
+
+ WLog_LVL(TAG, g_LogLevel, "dwTimeOut: 0x%08" PRIX32 " cReaders: %" PRIu32 "", call->dwTimeOut,
+ call->cReaders);
+
+ for (UINT32 index = 0; index < call->cReaders; index++)
+ {
+ const LPSCARD_READERSTATEW readerState = &call->rgReaderStates[index];
+ char szReaderA[1024] = { 0 };
+
+ ConvertWCharToUtf8(readerState->szReader, szReaderA, ARRAYSIZE(szReaderA));
+
+ WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: szReader: %s cbAtr: %" PRIu32 "", index,
+ szReaderA, readerState->cbAtr);
+ szCurrentState = SCardGetReaderStateString(readerState->dwCurrentState);
+ szEventState = SCardGetReaderStateString(readerState->dwEventState);
+ WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: dwCurrentState: %s (0x%08" PRIX32 ")", index,
+ szCurrentState, readerState->dwCurrentState);
+ WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: dwEventState: %s (0x%08" PRIX32 ")", index,
+ szEventState, readerState->dwEventState);
+ free(szCurrentState);
+ free(szEventState);
+ }
+
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_list_reader_groups_return(const ListReaderGroups_Return* ret,
+ BOOL unicode)
+{
+ char* mszA = NULL;
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ mszA = smartcard_convert_string_list(ret->msz, ret->cBytes, unicode);
+
+ WLog_LVL(TAG, g_LogLevel, "ListReaderGroups%s_Return {", unicode ? "W" : "A");
+ WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIx32 ")",
+ SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
+ WLog_LVL(TAG, g_LogLevel, " cBytes: %" PRIu32 " msz: %s", ret->cBytes, mszA);
+ WLog_LVL(TAG, g_LogLevel, "}");
+ free(mszA);
+}
+
+static void smartcard_trace_list_readers_call(const ListReaders_Call* call, BOOL unicode)
+{
+ char* mszGroupsA = NULL;
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ mszGroupsA = smartcard_convert_string_list(call->mszGroups, call->cBytes, unicode);
+
+ WLog_LVL(TAG, g_LogLevel, "ListReaders%s_Call {", unicode ? "W" : "A");
+ smartcard_log_context(TAG, &call->handles.hContext);
+
+ WLog_LVL(TAG, g_LogLevel,
+ "cBytes: %" PRIu32 " mszGroups: %s fmszReadersIsNULL: %" PRId32
+ " cchReaders: 0x%08" PRIX32 "",
+ call->cBytes, mszGroupsA, call->fmszReadersIsNULL, call->cchReaders);
+ WLog_LVL(TAG, g_LogLevel, "}");
+
+ free(mszGroupsA);
+}
+
+static void smartcard_trace_locate_cards_by_atr_a_call(const LocateCardsByATRA_Call* call)
+{
+ char* szEventState = NULL;
+ char* szCurrentState = NULL;
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "LocateCardsByATRA_Call {");
+ smartcard_log_context(TAG, &call->handles.hContext);
+
+ for (UINT32 index = 0; index < call->cReaders; index++)
+ {
+ char buffer[1024];
+ const LPSCARD_READERSTATEA readerState = &call->rgReaderStates[index];
+
+ WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: szReader: %s cbAtr: %" PRIu32 "", index,
+ readerState->szReader, readerState->cbAtr);
+ szCurrentState = SCardGetReaderStateString(readerState->dwCurrentState);
+ szEventState = SCardGetReaderStateString(readerState->dwEventState);
+ WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: dwCurrentState: %s (0x%08" PRIX32 ")", index,
+ szCurrentState, readerState->dwCurrentState);
+ WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: dwEventState: %s (0x%08" PRIX32 ")", index,
+ szEventState, readerState->dwEventState);
+
+ WLog_DBG(
+ TAG, "\t[%" PRIu32 "]: cbAtr: %" PRIu32 " rgbAtr: %s", index, readerState->cbAtr,
+ smartcard_array_dump(readerState->rgbAtr, readerState->cbAtr, buffer, sizeof(buffer)));
+
+ free(szCurrentState);
+ free(szEventState);
+ }
+
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_locate_cards_a_call(const LocateCardsA_Call* call)
+{
+ char buffer[8192];
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "LocateCardsA_Call {");
+ smartcard_log_context(TAG, &call->handles.hContext);
+ WLog_LVL(TAG, g_LogLevel, " cBytes=%" PRId32, call->cBytes);
+ WLog_LVL(TAG, g_LogLevel, " mszCards=%s",
+ smartcard_msz_dump_a(call->mszCards, call->cBytes, buffer, sizeof(buffer)));
+ WLog_LVL(TAG, g_LogLevel, " cReaders=%" PRId32, call->cReaders);
+ // WLog_LVL(TAG, g_LogLevel, " cReaders=%" PRId32, call->rgReaderStates);
+
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_locate_cards_return(const LocateCards_Return* ret)
+{
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "LocateCards_Return {");
+ WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
+ SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
+
+ if (ret->ReturnCode == SCARD_S_SUCCESS)
+ {
+ WLog_LVL(TAG, g_LogLevel, " cReaders=%" PRId32, ret->cReaders);
+ }
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_get_reader_icon_return(const GetReaderIcon_Return* ret)
+{
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "GetReaderIcon_Return {");
+ WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
+ SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
+
+ if (ret->ReturnCode == SCARD_S_SUCCESS)
+ {
+ WLog_LVL(TAG, g_LogLevel, " cbDataLen=%" PRId32, ret->cbDataLen);
+ }
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_get_transmit_count_return(const GetTransmitCount_Return* ret)
+{
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "GetTransmitCount_Return {");
+ WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
+ SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
+
+ WLog_LVL(TAG, g_LogLevel, " cTransmitCount=%" PRIu32, ret->cTransmitCount);
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_read_cache_return(const ReadCache_Return* ret)
+{
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "ReadCache_Return {");
+ WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
+ SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
+
+ if (ret->ReturnCode == SCARD_S_SUCCESS)
+ {
+ char buffer[1024];
+ WLog_LVL(TAG, g_LogLevel, " cbDataLen=%" PRId32, ret->cbDataLen);
+ WLog_LVL(TAG, g_LogLevel, " cbData: %s",
+ smartcard_array_dump(ret->pbData, ret->cbDataLen, buffer, sizeof(buffer)));
+ }
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_locate_cards_w_call(const LocateCardsW_Call* call)
+{
+ char buffer[8192];
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "LocateCardsW_Call {");
+ smartcard_log_context(TAG, &call->handles.hContext);
+ WLog_LVL(TAG, g_LogLevel, " cBytes=%" PRId32, call->cBytes);
+ WLog_LVL(TAG, g_LogLevel, " sz2=%s",
+ smartcard_msz_dump_w(call->mszCards, call->cBytes, buffer, sizeof(buffer)));
+ WLog_LVL(TAG, g_LogLevel, " cReaders=%" PRId32, call->cReaders);
+ // WLog_LVL(TAG, g_LogLevel, " sz2=%s", call->rgReaderStates);
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_list_readers_return(const ListReaders_Return* ret, BOOL unicode)
+{
+ char* mszA = NULL;
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "ListReaders%s_Return {", unicode ? "W" : "A");
+ WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
+ SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
+
+ if (ret->ReturnCode != SCARD_S_SUCCESS)
+ {
+ WLog_LVL(TAG, g_LogLevel, "}");
+ return;
+ }
+
+ mszA = smartcard_convert_string_list(ret->msz, ret->cBytes, unicode);
+
+ WLog_LVL(TAG, g_LogLevel, " cBytes: %" PRIu32 " msz: %s", ret->cBytes, mszA);
+ WLog_LVL(TAG, g_LogLevel, "}");
+ free(mszA);
+}
+
+static void smartcard_trace_get_status_change_return(const GetStatusChange_Return* ret,
+ BOOL unicode)
+{
+ char* szEventState = NULL;
+ char* szCurrentState = NULL;
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "GetStatusChange%s_Return {", unicode ? "W" : "A");
+ WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
+ SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
+ WLog_LVL(TAG, g_LogLevel, " cReaders: %" PRIu32 "", ret->cReaders);
+
+ for (UINT32 index = 0; index < ret->cReaders; index++)
+ {
+ char buffer[1024];
+ const ReaderState_Return* rgReaderState = &(ret->rgReaderStates[index]);
+ szCurrentState = SCardGetReaderStateString(rgReaderState->dwCurrentState);
+ szEventState = SCardGetReaderStateString(rgReaderState->dwEventState);
+ WLog_LVL(TAG, g_LogLevel, " [%" PRIu32 "]: dwCurrentState: %s (0x%08" PRIX32 ")", index,
+ szCurrentState, rgReaderState->dwCurrentState);
+ WLog_LVL(TAG, g_LogLevel, " [%" PRIu32 "]: dwEventState: %s (0x%08" PRIX32 ")", index,
+ szEventState, rgReaderState->dwEventState);
+ WLog_LVL(TAG, g_LogLevel, " [%" PRIu32 "]: cbAtr: %" PRIu32 " rgbAtr: %s", index,
+ rgReaderState->cbAtr,
+ smartcard_array_dump(rgReaderState->rgbAtr, rgReaderState->cbAtr, buffer,
+ sizeof(buffer)));
+ free(szCurrentState);
+ free(szEventState);
+ }
+
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_context_and_two_strings_a_call(const ContextAndTwoStringA_Call* call)
+{
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "ContextAndTwoStringW_Call {");
+ smartcard_log_context(TAG, &call->handles.hContext);
+ WLog_LVL(TAG, g_LogLevel, " sz1=%s", call->sz1);
+ WLog_LVL(TAG, g_LogLevel, " sz2=%s", call->sz2);
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_context_and_two_strings_w_call(const ContextAndTwoStringW_Call* call)
+{
+ char sz1[1024] = { 0 };
+ char sz2[1024] = { 0 };
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+ if (call->sz1)
+ ConvertWCharToUtf8(call->sz1, sz1, ARRAYSIZE(sz1));
+ if (call->sz2)
+ ConvertWCharToUtf8(call->sz2, sz2, ARRAYSIZE(sz2));
+
+ WLog_LVL(TAG, g_LogLevel, "ContextAndTwoStringW_Call {");
+ smartcard_log_context(TAG, &call->handles.hContext);
+ WLog_LVL(TAG, g_LogLevel, " sz1=%s", sz1);
+ WLog_LVL(TAG, g_LogLevel, " sz2=%s", sz2);
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_get_transmit_count_call(const GetTransmitCount_Call* call)
+{
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "GetTransmitCount_Call {");
+ smartcard_log_context(TAG, &call->handles.hContext);
+ smartcard_log_redir_handle(TAG, &call->handles.hCard);
+
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_write_cache_a_call(const WriteCacheA_Call* call)
+{
+ char buffer[1024];
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "GetTransmitCount_Call {");
+
+ WLog_LVL(TAG, g_LogLevel, " szLookupName=%s", call->szLookupName);
+
+ smartcard_log_context(TAG, &call->Common.handles.hContext);
+ WLog_DBG(
+ TAG, "..CardIdentifier=%s",
+ smartcard_array_dump(call->Common.CardIdentifier, sizeof(UUID), buffer, sizeof(buffer)));
+ WLog_LVL(TAG, g_LogLevel, " FreshnessCounter=%" PRIu32, call->Common.FreshnessCounter);
+ WLog_LVL(TAG, g_LogLevel, " cbDataLen=%" PRIu32, call->Common.cbDataLen);
+ WLog_DBG(
+ TAG, " pbData=%s",
+ smartcard_array_dump(call->Common.pbData, call->Common.cbDataLen, buffer, sizeof(buffer)));
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_write_cache_w_call(const WriteCacheW_Call* call)
+{
+ char tmp[1024] = { 0 };
+ char buffer[1024] = { 0 };
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "GetTransmitCount_Call {");
+
+ if (call->szLookupName)
+ ConvertWCharToUtf8(call->szLookupName, tmp, ARRAYSIZE(tmp));
+ WLog_LVL(TAG, g_LogLevel, " szLookupName=%s", tmp);
+
+ smartcard_log_context(TAG, &call->Common.handles.hContext);
+ WLog_DBG(
+ TAG, "..CardIdentifier=%s",
+ smartcard_array_dump(call->Common.CardIdentifier, sizeof(UUID), buffer, sizeof(buffer)));
+ WLog_LVL(TAG, g_LogLevel, " FreshnessCounter=%" PRIu32, call->Common.FreshnessCounter);
+ WLog_LVL(TAG, g_LogLevel, " cbDataLen=%" PRIu32, call->Common.cbDataLen);
+ WLog_DBG(
+ TAG, " pbData=%s",
+ smartcard_array_dump(call->Common.pbData, call->Common.cbDataLen, buffer, sizeof(buffer)));
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_read_cache_a_call(const ReadCacheA_Call* call)
+{
+ char buffer[1024];
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "GetTransmitCount_Call {");
+
+ WLog_LVL(TAG, g_LogLevel, " szLookupName=%s", call->szLookupName);
+ smartcard_log_context(TAG, &call->Common.handles.hContext);
+ WLog_DBG(
+ TAG, "..CardIdentifier=%s",
+ smartcard_array_dump(call->Common.CardIdentifier, sizeof(UUID), buffer, sizeof(buffer)));
+ WLog_LVL(TAG, g_LogLevel, " FreshnessCounter=%" PRIu32, call->Common.FreshnessCounter);
+ WLog_LVL(TAG, g_LogLevel, " fPbDataIsNULL=%" PRId32, call->Common.fPbDataIsNULL);
+ WLog_LVL(TAG, g_LogLevel, " cbDataLen=%" PRIu32, call->Common.cbDataLen);
+
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_read_cache_w_call(const ReadCacheW_Call* call)
+{
+ char tmp[1024] = { 0 };
+ char buffer[1024] = { 0 };
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "GetTransmitCount_Call {");
+ if (call->szLookupName)
+ ConvertWCharToUtf8(call->szLookupName, tmp, ARRAYSIZE(tmp));
+ WLog_LVL(TAG, g_LogLevel, " szLookupName=%s", tmp);
+
+ smartcard_log_context(TAG, &call->Common.handles.hContext);
+ WLog_DBG(
+ TAG, "..CardIdentifier=%s",
+ smartcard_array_dump(call->Common.CardIdentifier, sizeof(UUID), buffer, sizeof(buffer)));
+ WLog_LVL(TAG, g_LogLevel, " FreshnessCounter=%" PRIu32, call->Common.FreshnessCounter);
+ WLog_LVL(TAG, g_LogLevel, " fPbDataIsNULL=%" PRId32, call->Common.fPbDataIsNULL);
+ WLog_LVL(TAG, g_LogLevel, " cbDataLen=%" PRIu32, call->Common.cbDataLen);
+
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_transmit_call(const Transmit_Call* call)
+{
+ UINT32 cbExtraBytes = 0;
+ BYTE* pbExtraBytes = NULL;
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "Transmit_Call {");
+ smartcard_log_context(TAG, &call->handles.hContext);
+ smartcard_log_redir_handle(TAG, &call->handles.hCard);
+
+ if (call->pioSendPci)
+ {
+ cbExtraBytes = (UINT32)(call->pioSendPci->cbPciLength - sizeof(SCARD_IO_REQUEST));
+ pbExtraBytes = &((BYTE*)call->pioSendPci)[sizeof(SCARD_IO_REQUEST)];
+ WLog_LVL(TAG, g_LogLevel, "pioSendPci: dwProtocol: %" PRIu32 " cbExtraBytes: %" PRIu32 "",
+ call->pioSendPci->dwProtocol, cbExtraBytes);
+
+ if (cbExtraBytes)
+ {
+ char buffer[1024];
+ WLog_LVL(TAG, g_LogLevel, "pbExtraBytes: %s",
+ smartcard_array_dump(pbExtraBytes, cbExtraBytes, buffer, sizeof(buffer)));
+ }
+ }
+ else
+ {
+ WLog_LVL(TAG, g_LogLevel, "pioSendPci: null");
+ }
+
+ WLog_LVL(TAG, g_LogLevel, "cbSendLength: %" PRIu32 "", call->cbSendLength);
+
+ if (call->pbSendBuffer)
+ {
+ char buffer[1024];
+ WLog_DBG(
+ TAG, "pbSendBuffer: %s",
+ smartcard_array_dump(call->pbSendBuffer, call->cbSendLength, buffer, sizeof(buffer)));
+ }
+ else
+ {
+ WLog_LVL(TAG, g_LogLevel, "pbSendBuffer: null");
+ }
+
+ if (call->pioRecvPci)
+ {
+ cbExtraBytes = (UINT32)(call->pioRecvPci->cbPciLength - sizeof(SCARD_IO_REQUEST));
+ pbExtraBytes = &((BYTE*)call->pioRecvPci)[sizeof(SCARD_IO_REQUEST)];
+ WLog_LVL(TAG, g_LogLevel, "pioRecvPci: dwProtocol: %" PRIu32 " cbExtraBytes: %" PRIu32 "",
+ call->pioRecvPci->dwProtocol, cbExtraBytes);
+
+ if (cbExtraBytes)
+ {
+ char buffer[1024];
+ WLog_LVL(TAG, g_LogLevel, "pbExtraBytes: %s",
+ smartcard_array_dump(pbExtraBytes, cbExtraBytes, buffer, sizeof(buffer)));
+ }
+ }
+ else
+ {
+ WLog_LVL(TAG, g_LogLevel, "pioRecvPci: null");
+ }
+
+ WLog_LVL(TAG, g_LogLevel, "fpbRecvBufferIsNULL: %" PRId32 " cbRecvLength: %" PRIu32 "",
+ call->fpbRecvBufferIsNULL, call->cbRecvLength);
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_locate_cards_by_atr_w_call(const LocateCardsByATRW_Call* call)
+{
+ char* szEventState = NULL;
+ char* szCurrentState = NULL;
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "LocateCardsByATRW_Call {");
+ smartcard_log_context(TAG, &call->handles.hContext);
+
+ for (UINT32 index = 0; index < call->cReaders; index++)
+ {
+ char buffer[1024] = { 0 };
+ char tmp[1024] = { 0 };
+ const LPSCARD_READERSTATEW readerState =
+ (const LPSCARD_READERSTATEW)&call->rgReaderStates[index];
+
+ if (readerState->szReader)
+ ConvertWCharToUtf8(readerState->szReader, tmp, ARRAYSIZE(tmp));
+ WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: szReader: %s cbAtr: %" PRIu32 "", index, tmp,
+ readerState->cbAtr);
+ szCurrentState = SCardGetReaderStateString(readerState->dwCurrentState);
+ szEventState = SCardGetReaderStateString(readerState->dwEventState);
+ WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: dwCurrentState: %s (0x%08" PRIX32 ")", index,
+ szCurrentState, readerState->dwCurrentState);
+ WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: dwEventState: %s (0x%08" PRIX32 ")", index,
+ szEventState, readerState->dwEventState);
+
+ WLog_DBG(
+ TAG, "\t[%" PRIu32 "]: cbAtr: %" PRIu32 " rgbAtr: %s", index, readerState->cbAtr,
+ smartcard_array_dump(readerState->rgbAtr, readerState->cbAtr, buffer, sizeof(buffer)));
+
+ free(szCurrentState);
+ free(szEventState);
+ }
+
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_transmit_return(const Transmit_Return* ret)
+{
+ UINT32 cbExtraBytes = 0;
+ BYTE* pbExtraBytes = NULL;
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "Transmit_Return {");
+ WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
+ SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
+
+ if (ret->pioRecvPci)
+ {
+ cbExtraBytes = (UINT32)(ret->pioRecvPci->cbPciLength - sizeof(SCARD_IO_REQUEST));
+ pbExtraBytes = &((BYTE*)ret->pioRecvPci)[sizeof(SCARD_IO_REQUEST)];
+ WLog_LVL(TAG, g_LogLevel, " pioRecvPci: dwProtocol: %" PRIu32 " cbExtraBytes: %" PRIu32 "",
+ ret->pioRecvPci->dwProtocol, cbExtraBytes);
+
+ if (cbExtraBytes)
+ {
+ char buffer[1024];
+ WLog_LVL(TAG, g_LogLevel, " pbExtraBytes: %s",
+ smartcard_array_dump(pbExtraBytes, cbExtraBytes, buffer, sizeof(buffer)));
+ }
+ }
+ else
+ {
+ WLog_LVL(TAG, g_LogLevel, " pioRecvPci: null");
+ }
+
+ WLog_LVL(TAG, g_LogLevel, " cbRecvLength: %" PRIu32 "", ret->cbRecvLength);
+
+ if (ret->pbRecvBuffer)
+ {
+ char buffer[1024];
+ WLog_DBG(
+ TAG, " pbRecvBuffer: %s",
+ smartcard_array_dump(ret->pbRecvBuffer, ret->cbRecvLength, buffer, sizeof(buffer)));
+ }
+ else
+ {
+ WLog_LVL(TAG, g_LogLevel, " pbRecvBuffer: null");
+ }
+
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_control_return(const Control_Return* ret)
+{
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "Control_Return {");
+ WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
+ SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
+ WLog_LVL(TAG, g_LogLevel, " cbOutBufferSize: %" PRIu32 "", ret->cbOutBufferSize);
+
+ if (ret->pvOutBuffer)
+ {
+ char buffer[1024];
+ WLog_DBG(
+ TAG, "pvOutBuffer: %s",
+ smartcard_array_dump(ret->pvOutBuffer, ret->cbOutBufferSize, buffer, sizeof(buffer)));
+ }
+ else
+ {
+ WLog_LVL(TAG, g_LogLevel, "pvOutBuffer: null");
+ }
+
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_control_call(const Control_Call* call)
+{
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "Control_Call {");
+ smartcard_log_context(TAG, &call->handles.hContext);
+ smartcard_log_redir_handle(TAG, &call->handles.hCard);
+
+ WLog_LVL(TAG, g_LogLevel,
+ "dwControlCode: 0x%08" PRIX32 " cbInBufferSize: %" PRIu32
+ " fpvOutBufferIsNULL: %" PRId32 " cbOutBufferSize: %" PRIu32 "",
+ call->dwControlCode, call->cbInBufferSize, call->fpvOutBufferIsNULL,
+ call->cbOutBufferSize);
+
+ if (call->pvInBuffer)
+ {
+ char buffer[1024];
+ WLog_DBG(
+ TAG, "pbInBuffer: %s",
+ smartcard_array_dump(call->pvInBuffer, call->cbInBufferSize, buffer, sizeof(buffer)));
+ }
+ else
+ {
+ WLog_LVL(TAG, g_LogLevel, "pvInBuffer: null");
+ }
+
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_set_attrib_call(const SetAttrib_Call* call)
+{
+ char buffer[8192];
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "GetAttrib_Call {");
+ smartcard_log_context(TAG, &call->handles.hContext);
+ smartcard_log_redir_handle(TAG, &call->handles.hCard);
+ WLog_LVL(TAG, g_LogLevel, "dwAttrId: 0x%08" PRIX32, call->dwAttrId);
+ WLog_LVL(TAG, g_LogLevel, "cbAttrLen: 0x%08" PRId32, call->cbAttrLen);
+ WLog_LVL(TAG, g_LogLevel, "pbAttr: %s",
+ smartcard_array_dump(call->pbAttr, call->cbAttrLen, buffer, sizeof(buffer)));
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_get_attrib_return(const GetAttrib_Return* ret, DWORD dwAttrId)
+{
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "GetAttrib_Return {");
+ WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
+ SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
+ WLog_LVL(TAG, g_LogLevel, " dwAttrId: %s (0x%08" PRIX32 ") cbAttrLen: 0x%08" PRIX32 "",
+ SCardGetAttributeString(dwAttrId), dwAttrId, ret->cbAttrLen);
+
+ if (dwAttrId == SCARD_ATTR_VENDOR_NAME)
+ {
+ WLog_LVL(TAG, g_LogLevel, " pbAttr: %.*s", ret->cbAttrLen, (char*)ret->pbAttr);
+ }
+ else if (dwAttrId == SCARD_ATTR_CURRENT_PROTOCOL_TYPE)
+ {
+ union
+ {
+ BYTE* pb;
+ DWORD* pd;
+ } attr;
+ attr.pb = ret->pbAttr;
+ WLog_LVL(TAG, g_LogLevel, " dwProtocolType: %s (0x%08" PRIX32 ")",
+ SCardGetProtocolString(*attr.pd), *attr.pd);
+ }
+
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_get_attrib_call(const GetAttrib_Call* call)
+{
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "GetAttrib_Call {");
+ smartcard_log_context(TAG, &call->handles.hContext);
+ smartcard_log_redir_handle(TAG, &call->handles.hCard);
+
+ WLog_LVL(TAG, g_LogLevel,
+ "dwAttrId: %s (0x%08" PRIX32 ") fpbAttrIsNULL: %" PRId32 " cbAttrLen: 0x%08" PRIX32 "",
+ SCardGetAttributeString(call->dwAttrId), call->dwAttrId, call->fpbAttrIsNULL,
+ call->cbAttrLen);
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_status_call(const Status_Call* call, BOOL unicode)
+{
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "Status%s_Call {", unicode ? "W" : "A");
+ smartcard_log_context(TAG, &call->handles.hContext);
+ smartcard_log_redir_handle(TAG, &call->handles.hCard);
+
+ WLog_LVL(TAG, g_LogLevel,
+ "fmszReaderNamesIsNULL: %" PRId32 " cchReaderLen: %" PRIu32 " cbAtrLen: %" PRIu32 "",
+ call->fmszReaderNamesIsNULL, call->cchReaderLen, call->cbAtrLen);
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_status_return(const Status_Return* ret, BOOL unicode)
+{
+ char* mszReaderNamesA = NULL;
+ char buffer[1024];
+ DWORD cBytes = 0;
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+ cBytes = ret->cBytes;
+ if (ret->ReturnCode != SCARD_S_SUCCESS)
+ cBytes = 0;
+ if (cBytes == SCARD_AUTOALLOCATE)
+ cBytes = 0;
+ mszReaderNamesA = smartcard_convert_string_list(ret->mszReaderNames, cBytes, unicode);
+
+ WLog_LVL(TAG, g_LogLevel, "Status%s_Return {", unicode ? "W" : "A");
+ WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
+ SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
+ WLog_LVL(TAG, g_LogLevel, " dwState: %s (0x%08" PRIX32 ") dwProtocol: %s (0x%08" PRIX32 ")",
+ SCardGetCardStateString(ret->dwState), ret->dwState,
+ SCardGetProtocolString(ret->dwProtocol), ret->dwProtocol);
+
+ WLog_LVL(TAG, g_LogLevel, " cBytes: %" PRIu32 " mszReaderNames: %s", ret->cBytes,
+ mszReaderNamesA);
+
+ WLog_LVL(TAG, g_LogLevel, " cbAtrLen: %" PRIu32 " pbAtr: %s", ret->cbAtrLen,
+ smartcard_array_dump(ret->pbAtr, ret->cbAtrLen, buffer, sizeof(buffer)));
+ WLog_LVL(TAG, g_LogLevel, "}");
+ free(mszReaderNamesA);
+}
+
+static void smartcard_trace_state_return(const State_Return* ret)
+{
+ char buffer[1024];
+ char* state = NULL;
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ state = SCardGetReaderStateString(ret->dwState);
+ WLog_LVL(TAG, g_LogLevel, "Reconnect_Return {");
+ WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
+ SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
+ WLog_LVL(TAG, g_LogLevel, " dwState: %s (0x%08" PRIX32 ")", state, ret->dwState);
+ WLog_LVL(TAG, g_LogLevel, " dwProtocol: %s (0x%08" PRIX32 ")",
+ SCardGetProtocolString(ret->dwProtocol), ret->dwProtocol);
+ WLog_LVL(TAG, g_LogLevel, " cbAtrLen: (0x%08" PRIX32 ")", ret->cbAtrLen);
+ WLog_LVL(TAG, g_LogLevel, " rgAtr: %s",
+ smartcard_array_dump(ret->rgAtr, sizeof(ret->rgAtr), buffer, sizeof(buffer)));
+ WLog_LVL(TAG, g_LogLevel, "}");
+ free(state);
+}
+
+static void smartcard_trace_reconnect_return(const Reconnect_Return* ret)
+{
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "Reconnect_Return {");
+ WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
+ SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
+ WLog_LVL(TAG, g_LogLevel, " dwActiveProtocol: %s (0x%08" PRIX32 ")",
+ SCardGetProtocolString(ret->dwActiveProtocol), ret->dwActiveProtocol);
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_connect_a_call(const ConnectA_Call* call)
+{
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "ConnectA_Call {");
+ smartcard_log_context(TAG, &call->Common.handles.hContext);
+
+ WLog_LVL(TAG, g_LogLevel,
+ "szReader: %s dwShareMode: %s (0x%08" PRIX32 ") dwPreferredProtocols: %s (0x%08" PRIX32
+ ")",
+ call->szReader, SCardGetShareModeString(call->Common.dwShareMode),
+ call->Common.dwShareMode, SCardGetProtocolString(call->Common.dwPreferredProtocols),
+ call->Common.dwPreferredProtocols);
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_connect_w_call(const ConnectW_Call* call)
+{
+ char szReaderA[1024] = { 0 };
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ if (call->szReader)
+ ConvertWCharToUtf8(call->szReader, szReaderA, ARRAYSIZE(szReaderA));
+ WLog_LVL(TAG, g_LogLevel, "ConnectW_Call {");
+ smartcard_log_context(TAG, &call->Common.handles.hContext);
+
+ WLog_LVL(TAG, g_LogLevel,
+ "szReader: %s dwShareMode: %s (0x%08" PRIX32 ") dwPreferredProtocols: %s (0x%08" PRIX32
+ ")",
+ szReaderA, SCardGetShareModeString(call->Common.dwShareMode), call->Common.dwShareMode,
+ SCardGetProtocolString(call->Common.dwPreferredProtocols),
+ call->Common.dwPreferredProtocols);
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_hcard_and_disposition_call(const HCardAndDisposition_Call* call,
+ const char* name)
+{
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "%s_Call {", name);
+ smartcard_log_context(TAG, &call->handles.hContext);
+ smartcard_log_redir_handle(TAG, &call->handles.hCard);
+
+ WLog_LVL(TAG, g_LogLevel, "dwDisposition: %s (0x%08" PRIX32 ")",
+ SCardGetDispositionString(call->dwDisposition), call->dwDisposition);
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_establish_context_call(const EstablishContext_Call* call)
+{
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "EstablishContext_Call {");
+ WLog_LVL(TAG, g_LogLevel, "dwScope: %s (0x%08" PRIX32 ")", SCardGetScopeString(call->dwScope),
+ call->dwScope);
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_establish_context_return(const EstablishContext_Return* ret)
+{
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "EstablishContext_Return {");
+ WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
+ SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
+ smartcard_log_context(TAG, &ret->hContext);
+
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+void smartcard_trace_long_return(const Long_Return* ret, const char* name)
+{
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "%s_Return {", name);
+ WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
+ SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_connect_return(const Connect_Return* ret)
+{
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "Connect_Return {");
+ WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
+ SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
+ smartcard_log_context(TAG, &ret->hContext);
+ smartcard_log_redir_handle(TAG, &ret->hCard);
+
+ WLog_LVL(TAG, g_LogLevel, " dwActiveProtocol: %s (0x%08" PRIX32 ")",
+ SCardGetProtocolString(ret->dwActiveProtocol), ret->dwActiveProtocol);
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_reconnect_call(const Reconnect_Call* call)
+{
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "Reconnect_Call {");
+ smartcard_log_context(TAG, &call->handles.hContext);
+ smartcard_log_redir_handle(TAG, &call->handles.hCard);
+
+ WLog_LVL(TAG, g_LogLevel,
+ "dwShareMode: %s (0x%08" PRIX32 ") dwPreferredProtocols: %s (0x%08" PRIX32
+ ") dwInitialization: %s (0x%08" PRIX32 ")",
+ SCardGetShareModeString(call->dwShareMode), call->dwShareMode,
+ SCardGetProtocolString(call->dwPreferredProtocols), call->dwPreferredProtocols,
+ SCardGetDispositionString(call->dwInitialization), call->dwInitialization);
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static void smartcard_trace_device_type_id_return(const GetDeviceTypeId_Return* ret)
+{
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "GetDeviceTypeId_Return {");
+ WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
+ SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
+ WLog_LVL(TAG, g_LogLevel, " dwDeviceId=%08" PRIx32, ret->dwDeviceId);
+
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static LONG smartcard_unpack_common_context_and_string_a(wStream* s, REDIR_SCARDCONTEXT* phContext,
+ CHAR** pszReaderName)
+{
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+ LONG status = smartcard_unpack_redir_scard_context(s, phContext, &index, &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!smartcard_ndr_pointer_read(s, &index, NULL))
+ return ERROR_INVALID_DATA;
+
+ status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, phContext);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ status = smartcard_ndr_read_a(s, pszReaderName, NDR_PTR_FULL);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ smartcard_trace_context_and_string_call_a(__func__, phContext, *pszReaderName);
+ return SCARD_S_SUCCESS;
+}
+
+static LONG smartcard_unpack_common_context_and_string_w(wStream* s, REDIR_SCARDCONTEXT* phContext,
+ WCHAR** pszReaderName)
+{
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, phContext, &index, &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!smartcard_ndr_pointer_read(s, &index, NULL))
+ return ERROR_INVALID_DATA;
+
+ status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, phContext);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ status = smartcard_ndr_read_w(s, pszReaderName, NDR_PTR_FULL);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ smartcard_trace_context_and_string_call_w(__func__, phContext, *pszReaderName);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_unpack_common_type_header(wStream* s)
+{
+ UINT8 version = 0;
+ UINT32 filler = 0;
+ UINT8 endianness = 0;
+ UINT16 commonHeaderLength = 0;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ /* Process CommonTypeHeader */
+ Stream_Read_UINT8(s, version); /* Version (1 byte) */
+ Stream_Read_UINT8(s, endianness); /* Endianness (1 byte) */
+ Stream_Read_UINT16(s, commonHeaderLength); /* CommonHeaderLength (2 bytes) */
+ Stream_Read_UINT32(s, filler); /* Filler (4 bytes), should be 0xCCCCCCCC */
+
+ if (version != 1)
+ {
+ WLog_WARN(TAG, "Unsupported CommonTypeHeader Version %" PRIu8 "", version);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (endianness != 0x10)
+ {
+ WLog_WARN(TAG, "Unsupported CommonTypeHeader Endianness %" PRIu8 "", endianness);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (commonHeaderLength != 8)
+ {
+ WLog_WARN(TAG, "Unsupported CommonTypeHeader CommonHeaderLength %" PRIu16 "",
+ commonHeaderLength);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (filler != 0xCCCCCCCC)
+ {
+ WLog_WARN(TAG, "Unexpected CommonTypeHeader Filler 0x%08" PRIX32 "", filler);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ return SCARD_S_SUCCESS;
+}
+
+void smartcard_pack_common_type_header(wStream* s)
+{
+ Stream_Write_UINT8(s, 1); /* Version (1 byte) */
+ Stream_Write_UINT8(s, 0x10); /* Endianness (1 byte) */
+ Stream_Write_UINT16(s, 8); /* CommonHeaderLength (2 bytes) */
+ Stream_Write_UINT32(s, 0xCCCCCCCC); /* Filler (4 bytes), should be 0xCCCCCCCC */
+}
+
+LONG smartcard_unpack_private_type_header(wStream* s)
+{
+ UINT32 filler = 0;
+ UINT32 objectBufferLength = 0;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, objectBufferLength); /* ObjectBufferLength (4 bytes) */
+ Stream_Read_UINT32(s, filler); /* Filler (4 bytes), should be 0x00000000 */
+
+ if (filler != 0x00000000)
+ {
+ WLog_WARN(TAG, "Unexpected PrivateTypeHeader Filler 0x%08" PRIX32 "", filler);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, objectBufferLength))
+ return STATUS_INVALID_PARAMETER;
+
+ return SCARD_S_SUCCESS;
+}
+
+void smartcard_pack_private_type_header(wStream* s, UINT32 objectBufferLength)
+{
+ Stream_Write_UINT32(s, objectBufferLength); /* ObjectBufferLength (4 bytes) */
+ Stream_Write_UINT32(s, 0x00000000); /* Filler (4 bytes), should be 0x00000000 */
+}
+
+LONG smartcard_unpack_read_size_align(wStream* s, size_t size, UINT32 alignment)
+{
+ size_t pad = 0;
+
+ pad = size;
+ size = (size + alignment - 1) & ~(alignment - 1);
+ pad = size - pad;
+
+ if (pad)
+ Stream_Seek(s, pad);
+
+ return (LONG)pad;
+}
+
+LONG smartcard_pack_write_size_align(wStream* s, size_t size, UINT32 alignment)
+{
+ size_t pad = 0;
+
+ pad = size;
+ size = (size + alignment - 1) & ~(alignment - 1);
+ pad = size - pad;
+
+ if (pad)
+ {
+ if (!Stream_EnsureRemainingCapacity(s, pad))
+ {
+ WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ Stream_Zero(s, pad);
+ }
+
+ return SCARD_S_SUCCESS;
+}
+
+SCARDCONTEXT smartcard_scard_context_native_from_redir(REDIR_SCARDCONTEXT* context)
+{
+ SCARDCONTEXT hContext = { 0 };
+
+ if ((context->cbContext != sizeof(ULONG_PTR)) && (context->cbContext != 0))
+ {
+ WLog_WARN(TAG,
+ "REDIR_SCARDCONTEXT does not match native size: Actual: %" PRIu32
+ ", Expected: %" PRIuz "",
+ context->cbContext, sizeof(ULONG_PTR));
+ return 0;
+ }
+
+ if (context->cbContext)
+ CopyMemory(&hContext, &(context->pbContext), context->cbContext);
+
+ return hContext;
+}
+
+void smartcard_scard_context_native_to_redir(REDIR_SCARDCONTEXT* context, SCARDCONTEXT hContext)
+{
+ WINPR_ASSERT(context);
+ ZeroMemory(context, sizeof(REDIR_SCARDCONTEXT));
+ context->cbContext = sizeof(ULONG_PTR);
+ CopyMemory(&(context->pbContext), &hContext, context->cbContext);
+}
+
+SCARDHANDLE smartcard_scard_handle_native_from_redir(REDIR_SCARDHANDLE* handle)
+{
+ SCARDHANDLE hCard = 0;
+
+ if (handle->cbHandle == 0)
+ return hCard;
+
+ if (handle->cbHandle != sizeof(ULONG_PTR))
+ {
+ WLog_WARN(TAG,
+ "REDIR_SCARDHANDLE does not match native size: Actual: %" PRIu32
+ ", Expected: %" PRIuz "",
+ handle->cbHandle, sizeof(ULONG_PTR));
+ return 0;
+ }
+
+ if (handle->cbHandle)
+ CopyMemory(&hCard, &(handle->pbHandle), handle->cbHandle);
+
+ return hCard;
+}
+
+void smartcard_scard_handle_native_to_redir(REDIR_SCARDHANDLE* handle, SCARDHANDLE hCard)
+{
+ WINPR_ASSERT(handle);
+ ZeroMemory(handle, sizeof(REDIR_SCARDHANDLE));
+ handle->cbHandle = sizeof(ULONG_PTR);
+ CopyMemory(&(handle->pbHandle), &hCard, handle->cbHandle);
+}
+
+LONG smartcard_unpack_redir_scard_context_(wStream* s, REDIR_SCARDCONTEXT* context, UINT32* index,
+ UINT32* ppbContextNdrPtr, const char* file,
+ const char* function, int line)
+{
+ UINT32 pbContextNdrPtr = 0;
+
+ WINPR_UNUSED(file);
+ WINPR_ASSERT(context);
+
+ ZeroMemory(context, sizeof(REDIR_SCARDCONTEXT));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, context->cbContext); /* cbContext (4 bytes) */
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, context->cbContext))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ if ((context->cbContext != 0) && (context->cbContext != 4) && (context->cbContext != 8))
+ {
+ WLog_WARN(TAG, "REDIR_SCARDCONTEXT length is not 0, 4 or 8: %" PRIu32 "",
+ context->cbContext);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (!smartcard_ndr_pointer_read_(s, index, &pbContextNdrPtr, file, function, line))
+ return ERROR_INVALID_DATA;
+
+ if (((context->cbContext == 0) && pbContextNdrPtr) ||
+ ((context->cbContext != 0) && !pbContextNdrPtr))
+ {
+ WLog_WARN(TAG,
+ "REDIR_SCARDCONTEXT cbContext (%" PRIu32 ") pbContextNdrPtr (%" PRIu32
+ ") inconsistency",
+ context->cbContext, pbContextNdrPtr);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, context->cbContext))
+ return STATUS_INVALID_PARAMETER;
+
+ *ppbContextNdrPtr = pbContextNdrPtr;
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_pack_redir_scard_context(wStream* s, const REDIR_SCARDCONTEXT* context, DWORD* index)
+{
+ const UINT32 pbContextNdrPtr = 0x00020000 + *index * 4;
+
+ if (context->cbContext != 0)
+ {
+ Stream_Write_UINT32(s, context->cbContext); /* cbContext (4 bytes) */
+ Stream_Write_UINT32(s, pbContextNdrPtr); /* pbContextNdrPtr (4 bytes) */
+ *index = *index + 1;
+ }
+ else
+ Stream_Zero(s, 8);
+
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_unpack_redir_scard_context_ref(wStream* s, UINT32 pbContextNdrPtr,
+ REDIR_SCARDCONTEXT* context)
+{
+ UINT32 length = 0;
+
+ WINPR_ASSERT(context);
+ if (context->cbContext == 0)
+ return SCARD_S_SUCCESS;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, length); /* Length (4 bytes) */
+
+ if (length != context->cbContext)
+ {
+ WLog_WARN(TAG, "REDIR_SCARDCONTEXT length (%" PRIu32 ") cbContext (%" PRIu32 ") mismatch",
+ length, context->cbContext);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if ((context->cbContext != 0) && (context->cbContext != 4) && (context->cbContext != 8))
+ {
+ WLog_WARN(TAG, "REDIR_SCARDCONTEXT length is not 4 or 8: %" PRIu32 "", context->cbContext);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, context->cbContext))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ if (context->cbContext)
+ Stream_Read(s, &(context->pbContext), context->cbContext);
+ else
+ ZeroMemory(&(context->pbContext), sizeof(context->pbContext));
+
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_pack_redir_scard_context_ref(wStream* s, const REDIR_SCARDCONTEXT* context)
+{
+
+ Stream_Write_UINT32(s, context->cbContext); /* Length (4 bytes) */
+
+ if (context->cbContext)
+ {
+ Stream_Write(s, &(context->pbContext), context->cbContext);
+ }
+
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_unpack_redir_scard_handle_(wStream* s, REDIR_SCARDHANDLE* handle, UINT32* index,
+ const char* file, const char* function, int line)
+{
+ WINPR_ASSERT(handle);
+ ZeroMemory(handle, sizeof(REDIR_SCARDHANDLE));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, handle->cbHandle); /* Length (4 bytes) */
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, handle->cbHandle))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ if (!smartcard_ndr_pointer_read_(s, index, NULL, file, function, line))
+ return ERROR_INVALID_DATA;
+
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_pack_redir_scard_handle(wStream* s, const REDIR_SCARDHANDLE* handle, DWORD* index)
+{
+ const UINT32 pbContextNdrPtr = 0x00020000 + *index * 4;
+
+ if (handle->cbHandle != 0)
+ {
+ Stream_Write_UINT32(s, handle->cbHandle); /* cbContext (4 bytes) */
+ Stream_Write_UINT32(s, pbContextNdrPtr); /* pbContextNdrPtr (4 bytes) */
+ *index = *index + 1;
+ }
+ else
+ Stream_Zero(s, 8);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_unpack_redir_scard_handle_ref(wStream* s, REDIR_SCARDHANDLE* handle)
+{
+ UINT32 length = 0;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, length); /* Length (4 bytes) */
+
+ if (length != handle->cbHandle)
+ {
+ WLog_WARN(TAG, "REDIR_SCARDHANDLE length (%" PRIu32 ") cbHandle (%" PRIu32 ") mismatch",
+ length, handle->cbHandle);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if ((handle->cbHandle != 4) && (handle->cbHandle != 8))
+ {
+ WLog_WARN(TAG, "REDIR_SCARDHANDLE length is not 4 or 8: %" PRIu32 "", handle->cbHandle);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, handle->cbHandle))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ if (handle->cbHandle)
+ Stream_Read(s, &(handle->pbHandle), handle->cbHandle);
+
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_pack_redir_scard_handle_ref(wStream* s, const REDIR_SCARDHANDLE* handle)
+{
+
+ Stream_Write_UINT32(s, handle->cbHandle); /* Length (4 bytes) */
+
+ if (handle->cbHandle)
+ Stream_Write(s, &(handle->pbHandle), handle->cbHandle);
+
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_unpack_establish_context_call(wStream* s, EstablishContext_Call* call)
+{
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, call->dwScope); /* dwScope (4 bytes) */
+ smartcard_trace_establish_context_call(call);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_pack_establish_context_return(wStream* s, const EstablishContext_Return* ret)
+{
+ LONG status = 0;
+ DWORD index = 0;
+
+ smartcard_trace_establish_context_return(ret);
+ if (ret->ReturnCode != SCARD_S_SUCCESS)
+ return ret->ReturnCode;
+
+ if ((status = smartcard_pack_redir_scard_context(s, &(ret->hContext), &index)))
+ return status;
+
+ return smartcard_pack_redir_scard_context_ref(s, &(ret->hContext));
+}
+
+LONG smartcard_unpack_context_call(wStream* s, Context_Call* call, const char* name)
+{
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->handles.hContext))))
+ WLog_ERR(TAG, "smartcard_unpack_redir_scard_context_ref failed with error %" PRId32 "",
+ status);
+
+ smartcard_trace_context_call(call, name);
+ return status;
+}
+
+LONG smartcard_unpack_list_reader_groups_call(wStream* s, ListReaderGroups_Call* call, BOOL unicode)
+{
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_INT32(s, call->fmszGroupsIsNULL); /* fmszGroupsIsNULL (4 bytes) */
+ Stream_Read_UINT32(s, call->cchGroups); /* cchGroups (4 bytes) */
+ status =
+ smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, &(call->handles.hContext));
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ smartcard_trace_list_reader_groups_call(call, unicode);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_pack_list_reader_groups_return(wStream* s, const ListReaderGroups_Return* ret,
+ BOOL unicode)
+{
+ LONG status = 0;
+ DWORD cBytes = ret->cBytes;
+ UINT32 index = 0;
+
+ smartcard_trace_list_reader_groups_return(ret, unicode);
+ if (ret->ReturnCode != SCARD_S_SUCCESS)
+ cBytes = 0;
+ if (cBytes == SCARD_AUTOALLOCATE)
+ cBytes = 0;
+
+ if (!Stream_EnsureRemainingCapacity(s, 4))
+ return SCARD_E_NO_MEMORY;
+
+ Stream_Write_UINT32(s, cBytes); /* cBytes (4 bytes) */
+ if (!smartcard_ndr_pointer_write(s, &index, cBytes))
+ return SCARD_E_NO_MEMORY;
+
+ status = smartcard_ndr_write(s, ret->msz, cBytes, 1, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret->ReturnCode;
+}
+
+LONG smartcard_unpack_list_readers_call(wStream* s, ListReaders_Call* call, BOOL unicode)
+{
+ UINT32 index = 0;
+ UINT32 mszGroupsNdrPtr = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ call->mszGroups = NULL;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, call->cBytes); /* cBytes (4 bytes) */
+ if (!smartcard_ndr_pointer_read(s, &index, &mszGroupsNdrPtr))
+ return ERROR_INVALID_DATA;
+ Stream_Read_INT32(s, call->fmszReadersIsNULL); /* fmszReadersIsNULL (4 bytes) */
+ Stream_Read_UINT32(s, call->cchReaders); /* cchReaders (4 bytes) */
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->handles.hContext))))
+ return status;
+
+ if (mszGroupsNdrPtr)
+ {
+ status = smartcard_ndr_read(s, &call->mszGroups, call->cBytes, 1, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+
+ smartcard_trace_list_readers_call(call, unicode);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_pack_list_readers_return(wStream* s, const ListReaders_Return* ret, BOOL unicode)
+{
+ LONG status = 0;
+ UINT32 index = 0;
+ UINT32 size = ret->cBytes;
+
+ smartcard_trace_list_readers_return(ret, unicode);
+ if (ret->ReturnCode != SCARD_S_SUCCESS)
+ size = 0;
+
+ if (!Stream_EnsureRemainingCapacity(s, 4))
+ {
+ WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ Stream_Write_UINT32(s, size); /* cBytes (4 bytes) */
+ if (!smartcard_ndr_pointer_write(s, &index, size))
+ return SCARD_E_NO_MEMORY;
+
+ status = smartcard_ndr_write(s, ret->msz, size, 1, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret->ReturnCode;
+}
+
+static LONG smartcard_unpack_connect_common(wStream* s, Connect_Common_Call* common, UINT32* index,
+ UINT32* ppbContextNdrPtr)
+{
+ LONG status = smartcard_unpack_redir_scard_context(s, &(common->handles.hContext), index,
+ ppbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, common->dwShareMode); /* dwShareMode (4 bytes) */
+ Stream_Read_UINT32(s, common->dwPreferredProtocols); /* dwPreferredProtocols (4 bytes) */
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_unpack_connect_a_call(wStream* s, ConnectA_Call* call)
+{
+ LONG status = 0;
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+ call->szReader = NULL;
+
+ if (!smartcard_ndr_pointer_read(s, &index, NULL))
+ return ERROR_INVALID_DATA;
+
+ if ((status = smartcard_unpack_connect_common(s, &(call->Common), &index, &pbContextNdrPtr)))
+ {
+ WLog_ERR(TAG, "smartcard_unpack_connect_common failed with error %" PRId32 "", status);
+ return status;
+ }
+
+ status = smartcard_ndr_read_a(s, &call->szReader, NDR_PTR_FULL);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->Common.handles.hContext))))
+ WLog_ERR(TAG, "smartcard_unpack_redir_scard_context_ref failed with error %" PRId32 "",
+ status);
+
+ smartcard_trace_connect_a_call(call);
+ return status;
+}
+
+LONG smartcard_unpack_connect_w_call(wStream* s, ConnectW_Call* call)
+{
+ LONG status = 0;
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ call->szReader = NULL;
+
+ if (!smartcard_ndr_pointer_read(s, &index, NULL))
+ return ERROR_INVALID_DATA;
+
+ if ((status = smartcard_unpack_connect_common(s, &(call->Common), &index, &pbContextNdrPtr)))
+ {
+ WLog_ERR(TAG, "smartcard_unpack_connect_common failed with error %" PRId32 "", status);
+ return status;
+ }
+
+ status = smartcard_ndr_read_w(s, &call->szReader, NDR_PTR_FULL);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->Common.handles.hContext))))
+ WLog_ERR(TAG, "smartcard_unpack_redir_scard_context_ref failed with error %" PRId32 "",
+ status);
+
+ smartcard_trace_connect_w_call(call);
+ return status;
+}
+
+LONG smartcard_pack_connect_return(wStream* s, const Connect_Return* ret)
+{
+ LONG status = 0;
+ DWORD index = 0;
+
+ smartcard_trace_connect_return(ret);
+
+ status = smartcard_pack_redir_scard_context(s, &ret->hContext, &index);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ status = smartcard_pack_redir_scard_handle(s, &ret->hCard, &index);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!Stream_EnsureRemainingCapacity(s, 4))
+ return SCARD_E_NO_MEMORY;
+
+ Stream_Write_UINT32(s, ret->dwActiveProtocol); /* dwActiveProtocol (4 bytes) */
+ status = smartcard_pack_redir_scard_context_ref(s, &ret->hContext);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return smartcard_pack_redir_scard_handle_ref(s, &(ret->hCard));
+}
+
+LONG smartcard_unpack_reconnect_call(wStream* s, Reconnect_Call* call)
+{
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ status = smartcard_unpack_redir_scard_handle(s, &(call->handles.hCard), &index);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, call->dwShareMode); /* dwShareMode (4 bytes) */
+ Stream_Read_UINT32(s, call->dwPreferredProtocols); /* dwPreferredProtocols (4 bytes) */
+ Stream_Read_UINT32(s, call->dwInitialization); /* dwInitialization (4 bytes) */
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->handles.hContext))))
+ {
+ WLog_ERR(TAG, "smartcard_unpack_redir_scard_context_ref failed with error %" PRId32 "",
+ status);
+ return status;
+ }
+
+ if ((status = smartcard_unpack_redir_scard_handle_ref(s, &(call->handles.hCard))))
+ WLog_ERR(TAG, "smartcard_unpack_redir_scard_handle_ref failed with error %" PRId32 "",
+ status);
+
+ smartcard_trace_reconnect_call(call);
+ return status;
+}
+
+LONG smartcard_pack_reconnect_return(wStream* s, const Reconnect_Return* ret)
+{
+ smartcard_trace_reconnect_return(ret);
+
+ if (!Stream_EnsureRemainingCapacity(s, 4))
+ return SCARD_E_NO_MEMORY;
+ Stream_Write_UINT32(s, ret->dwActiveProtocol); /* dwActiveProtocol (4 bytes) */
+ return ret->ReturnCode;
+}
+
+LONG smartcard_unpack_hcard_and_disposition_call(wStream* s, HCardAndDisposition_Call* call,
+ const char* name)
+{
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ status = smartcard_unpack_redir_scard_handle(s, &(call->handles.hCard), &index);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, call->dwDisposition); /* dwDisposition (4 bytes) */
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->handles.hContext))))
+ return status;
+
+ if ((status = smartcard_unpack_redir_scard_handle_ref(s, &(call->handles.hCard))))
+ return status;
+
+ smartcard_trace_hcard_and_disposition_call(call, name);
+ return status;
+}
+
+static void smartcard_trace_get_status_change_a_call(const GetStatusChangeA_Call* call)
+{
+ char* szEventState = NULL;
+ char* szCurrentState = NULL;
+ LPSCARD_READERSTATEA readerState = NULL;
+
+ if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
+ return;
+
+ WLog_LVL(TAG, g_LogLevel, "GetStatusChangeA_Call {");
+ smartcard_log_context(TAG, &call->handles.hContext);
+
+ WLog_LVL(TAG, g_LogLevel, "dwTimeOut: 0x%08" PRIX32 " cReaders: %" PRIu32 "", call->dwTimeOut,
+ call->cReaders);
+
+ for (UINT32 index = 0; index < call->cReaders; index++)
+ {
+ readerState = &call->rgReaderStates[index];
+ WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: szReader: %s cbAtr: %" PRIu32 "", index,
+ readerState->szReader, readerState->cbAtr);
+ szCurrentState = SCardGetReaderStateString(readerState->dwCurrentState);
+ szEventState = SCardGetReaderStateString(readerState->dwEventState);
+ WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: dwCurrentState: %s (0x%08" PRIX32 ")", index,
+ szCurrentState, readerState->dwCurrentState);
+ WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: dwEventState: %s (0x%08" PRIX32 ")", index,
+ szEventState, readerState->dwEventState);
+ free(szCurrentState);
+ free(szEventState);
+ }
+
+ WLog_LVL(TAG, g_LogLevel, "}");
+}
+
+static LONG smartcard_unpack_reader_state_a(wStream* s, LPSCARD_READERSTATEA* ppcReaders,
+ UINT32 cReaders, UINT32* ptrIndex)
+{
+ UINT32 len = 0;
+ LONG status = SCARD_E_NO_MEMORY;
+ LPSCARD_READERSTATEA rgReaderStates = NULL;
+ BOOL* states = NULL;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return status;
+
+ Stream_Read_UINT32(s, len);
+ if (len != cReaders)
+ {
+ WLog_ERR(TAG, "Count mismatch when reading LPSCARD_READERSTATEA");
+ return status;
+ }
+ rgReaderStates = (LPSCARD_READERSTATEA)calloc(cReaders, sizeof(SCARD_READERSTATEA));
+ states = calloc(cReaders, sizeof(BOOL));
+ if (!rgReaderStates || !states)
+ goto fail;
+ status = ERROR_INVALID_DATA;
+
+ for (UINT32 index = 0; index < cReaders; index++)
+ {
+ UINT32 ptr = UINT32_MAX;
+ LPSCARD_READERSTATEA readerState = &rgReaderStates[index];
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 52))
+ goto fail;
+
+ if (!smartcard_ndr_pointer_read(s, ptrIndex, &ptr))
+ {
+ if (ptr != 0)
+ goto fail;
+ }
+ /* Ignore NULL length strings */
+ states[index] = ptr != 0;
+ 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) */
+ Stream_Read(s, readerState->rgbAtr, 36); /* rgbAtr [0..36] (36 bytes) */
+ }
+
+ for (UINT32 index = 0; index < cReaders; index++)
+ {
+ LPSCARD_READERSTATEA readerState = &rgReaderStates[index];
+
+ /* Ignore empty strings */
+ if (!states[index])
+ continue;
+ status = smartcard_ndr_read_a(s, &readerState->szReader, NDR_PTR_FULL);
+ if (status != SCARD_S_SUCCESS)
+ goto fail;
+ }
+
+ *ppcReaders = rgReaderStates;
+ free(states);
+ return SCARD_S_SUCCESS;
+fail:
+ if (rgReaderStates)
+ {
+ for (UINT32 index = 0; index < cReaders; index++)
+ {
+ LPSCARD_READERSTATEA readerState = &rgReaderStates[index];
+ free(readerState->szReader);
+ }
+ }
+ free(rgReaderStates);
+ free(states);
+ return status;
+}
+
+static LONG smartcard_unpack_reader_state_w(wStream* s, LPSCARD_READERSTATEW* ppcReaders,
+ UINT32 cReaders, UINT32* ptrIndex)
+{
+ UINT32 len = 0;
+ LONG status = SCARD_E_NO_MEMORY;
+ LPSCARD_READERSTATEW rgReaderStates = NULL;
+ BOOL* states = NULL;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return status;
+
+ Stream_Read_UINT32(s, len);
+ if (len != cReaders)
+ {
+ WLog_ERR(TAG, "Count mismatch when reading LPSCARD_READERSTATEW");
+ return status;
+ }
+
+ rgReaderStates = (LPSCARD_READERSTATEW)calloc(cReaders, sizeof(SCARD_READERSTATEW));
+ states = calloc(cReaders, sizeof(BOOL));
+
+ if (!rgReaderStates || !states)
+ goto fail;
+
+ status = ERROR_INVALID_DATA;
+ for (UINT32 index = 0; index < cReaders; index++)
+ {
+ UINT32 ptr = UINT32_MAX;
+ LPSCARD_READERSTATEW readerState = &rgReaderStates[index];
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 52))
+ goto fail;
+
+ if (!smartcard_ndr_pointer_read(s, ptrIndex, &ptr))
+ {
+ if (ptr != 0)
+ goto fail;
+ }
+ /* Ignore NULL length strings */
+ states[index] = ptr != 0;
+ 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) */
+ Stream_Read(s, readerState->rgbAtr, 36); /* rgbAtr [0..36] (36 bytes) */
+ }
+
+ for (UINT32 index = 0; index < cReaders; index++)
+ {
+ LPSCARD_READERSTATEW readerState = &rgReaderStates[index];
+
+ /* Skip NULL pointers */
+ if (!states[index])
+ continue;
+
+ status = smartcard_ndr_read_w(s, &readerState->szReader, NDR_PTR_FULL);
+ if (status != SCARD_S_SUCCESS)
+ goto fail;
+ }
+
+ *ppcReaders = rgReaderStates;
+ free(states);
+ return SCARD_S_SUCCESS;
+fail:
+ if (rgReaderStates)
+ {
+ for (UINT32 index = 0; index < cReaders; index++)
+ {
+ LPSCARD_READERSTATEW readerState = &rgReaderStates[index];
+ free(readerState->szReader);
+ }
+ }
+ free(rgReaderStates);
+ free(states);
+ return status;
+}
+
+/******************************************************************************/
+/************************************* End Trace Functions ********************/
+/******************************************************************************/
+
+LONG smartcard_unpack_get_status_change_a_call(wStream* s, GetStatusChangeA_Call* call)
+{
+ UINT32 ndrPtr = 0;
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ call->rgReaderStates = NULL;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, call->dwTimeOut); /* dwTimeOut (4 bytes) */
+ Stream_Read_UINT32(s, call->cReaders); /* cReaders (4 bytes) */
+ if (!smartcard_ndr_pointer_read(s, &index, &ndrPtr))
+ return ERROR_INVALID_DATA;
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->handles.hContext))))
+ return status;
+
+ if (ndrPtr)
+ {
+ status = smartcard_unpack_reader_state_a(s, &call->rgReaderStates, call->cReaders, &index);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+
+ smartcard_trace_get_status_change_a_call(call);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_unpack_get_status_change_w_call(wStream* s, GetStatusChangeW_Call* call)
+{
+ UINT32 ndrPtr = 0;
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ call->rgReaderStates = NULL;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, call->dwTimeOut); /* dwTimeOut (4 bytes) */
+ Stream_Read_UINT32(s, call->cReaders); /* cReaders (4 bytes) */
+ if (!smartcard_ndr_pointer_read(s, &index, &ndrPtr))
+ return ERROR_INVALID_DATA;
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->handles.hContext))))
+ return status;
+
+ if (ndrPtr)
+ {
+ status = smartcard_unpack_reader_state_w(s, &call->rgReaderStates, call->cReaders, &index);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+
+ smartcard_trace_get_status_change_w_call(call);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_pack_get_status_change_return(wStream* s, const GetStatusChange_Return* ret,
+ BOOL unicode)
+{
+ LONG status = 0;
+ DWORD cReaders = ret->cReaders;
+ UINT32 index = 0;
+
+ smartcard_trace_get_status_change_return(ret, unicode);
+ if (ret->ReturnCode != SCARD_S_SUCCESS)
+ cReaders = 0;
+ if (cReaders == SCARD_AUTOALLOCATE)
+ cReaders = 0;
+
+ if (!Stream_EnsureRemainingCapacity(s, 4))
+ return SCARD_E_NO_MEMORY;
+
+ Stream_Write_UINT32(s, cReaders); /* cReaders (4 bytes) */
+ if (!smartcard_ndr_pointer_write(s, &index, cReaders))
+ return SCARD_E_NO_MEMORY;
+ status = smartcard_ndr_write_state(s, ret->rgReaderStates, cReaders, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret->ReturnCode;
+}
+
+LONG smartcard_unpack_state_call(wStream* s, State_Call* call)
+{
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ status = smartcard_unpack_redir_scard_handle(s, &(call->handles.hCard), &index);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_INT32(s, call->fpbAtrIsNULL); /* fpbAtrIsNULL (4 bytes) */
+ Stream_Read_UINT32(s, call->cbAtrLen); /* cbAtrLen (4 bytes) */
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->handles.hContext))))
+ return status;
+
+ if ((status = smartcard_unpack_redir_scard_handle_ref(s, &(call->handles.hCard))))
+ return status;
+
+ return status;
+}
+
+LONG smartcard_pack_state_return(wStream* s, const State_Return* ret)
+{
+ LONG status = 0;
+ DWORD cbAtrLen = ret->cbAtrLen;
+ UINT32 index = 0;
+
+ smartcard_trace_state_return(ret);
+ if (ret->ReturnCode != SCARD_S_SUCCESS)
+ cbAtrLen = 0;
+ if (cbAtrLen == SCARD_AUTOALLOCATE)
+ cbAtrLen = 0;
+
+ Stream_Write_UINT32(s, ret->dwState); /* dwState (4 bytes) */
+ Stream_Write_UINT32(s, ret->dwProtocol); /* dwProtocol (4 bytes) */
+ Stream_Write_UINT32(s, cbAtrLen); /* cbAtrLen (4 bytes) */
+ if (!smartcard_ndr_pointer_write(s, &index, cbAtrLen))
+ return SCARD_E_NO_MEMORY;
+ status = smartcard_ndr_write(s, ret->rgAtr, cbAtrLen, 1, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret->ReturnCode;
+}
+
+LONG smartcard_unpack_status_call(wStream* s, Status_Call* call, BOOL unicode)
+{
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ status = smartcard_unpack_redir_scard_handle(s, &(call->handles.hCard), &index);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_INT32(s, call->fmszReaderNamesIsNULL); /* fmszReaderNamesIsNULL (4 bytes) */
+ Stream_Read_UINT32(s, call->cchReaderLen); /* cchReaderLen (4 bytes) */
+ Stream_Read_UINT32(s, call->cbAtrLen); /* cbAtrLen (4 bytes) */
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->handles.hContext))))
+ return status;
+
+ if ((status = smartcard_unpack_redir_scard_handle_ref(s, &(call->handles.hCard))))
+ return status;
+
+ smartcard_trace_status_call(call, unicode);
+ return status;
+}
+
+LONG smartcard_pack_status_return(wStream* s, const Status_Return* ret, BOOL unicode)
+{
+ LONG status = 0;
+ UINT32 index = 0;
+ DWORD cBytes = ret->cBytes;
+
+ smartcard_trace_status_return(ret, unicode);
+ if (ret->ReturnCode != SCARD_S_SUCCESS)
+ cBytes = 0;
+ if (cBytes == SCARD_AUTOALLOCATE)
+ cBytes = 0;
+
+ if (!Stream_EnsureRemainingCapacity(s, 4))
+ return SCARD_F_INTERNAL_ERROR;
+
+ Stream_Write_UINT32(s, cBytes); /* cBytes (4 bytes) */
+ if (!smartcard_ndr_pointer_write(s, &index, cBytes))
+ return SCARD_E_NO_MEMORY;
+
+ if (!Stream_EnsureRemainingCapacity(s, 44))
+ return SCARD_F_INTERNAL_ERROR;
+
+ Stream_Write_UINT32(s, ret->dwState); /* dwState (4 bytes) */
+ Stream_Write_UINT32(s, ret->dwProtocol); /* dwProtocol (4 bytes) */
+ Stream_Write(s, ret->pbAtr, sizeof(ret->pbAtr)); /* pbAtr (32 bytes) */
+ Stream_Write_UINT32(s, ret->cbAtrLen); /* cbAtrLen (4 bytes) */
+ status = smartcard_ndr_write(s, ret->mszReaderNames, cBytes, 1, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret->ReturnCode;
+}
+
+LONG smartcard_unpack_get_attrib_call(wStream* s, GetAttrib_Call* call)
+{
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ status = smartcard_unpack_redir_scard_handle(s, &(call->handles.hCard), &index);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, call->dwAttrId); /* dwAttrId (4 bytes) */
+ Stream_Read_INT32(s, call->fpbAttrIsNULL); /* fpbAttrIsNULL (4 bytes) */
+ Stream_Read_UINT32(s, call->cbAttrLen); /* cbAttrLen (4 bytes) */
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->handles.hContext))))
+ return status;
+
+ if ((status = smartcard_unpack_redir_scard_handle_ref(s, &(call->handles.hCard))))
+ return status;
+
+ smartcard_trace_get_attrib_call(call);
+ return status;
+}
+
+LONG smartcard_pack_get_attrib_return(wStream* s, const GetAttrib_Return* ret, DWORD dwAttrId,
+ DWORD cbAttrCallLen)
+{
+ LONG status = 0;
+ DWORD cbAttrLen = 0;
+ UINT32 index = 0;
+ smartcard_trace_get_attrib_return(ret, dwAttrId);
+
+ if (!Stream_EnsureRemainingCapacity(s, 4))
+ return SCARD_F_INTERNAL_ERROR;
+
+ cbAttrLen = ret->cbAttrLen;
+ if (ret->ReturnCode != SCARD_S_SUCCESS)
+ cbAttrLen = 0;
+ if (cbAttrLen == SCARD_AUTOALLOCATE)
+ cbAttrLen = 0;
+
+ if (ret->pbAttr)
+ {
+ if (cbAttrCallLen < cbAttrLen)
+ cbAttrLen = cbAttrCallLen;
+ }
+ Stream_Write_UINT32(s, cbAttrLen); /* cbAttrLen (4 bytes) */
+ if (!smartcard_ndr_pointer_write(s, &index, cbAttrLen))
+ return SCARD_E_NO_MEMORY;
+
+ status = smartcard_ndr_write(s, ret->pbAttr, cbAttrLen, 1, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret->ReturnCode;
+}
+
+LONG smartcard_unpack_control_call(wStream* s, Control_Call* call)
+{
+ UINT32 index = 0;
+ UINT32 pvInBufferNdrPtr = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ call->pvInBuffer = NULL;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ status = smartcard_unpack_redir_scard_handle(s, &(call->handles.hCard), &index);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, 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) */
+ if (!smartcard_ndr_pointer_read(s, &index, &pvInBufferNdrPtr)) /* pvInBufferNdrPtr (4 bytes) */
+ return ERROR_INVALID_DATA;
+ Stream_Read_INT32(s, call->fpvOutBufferIsNULL); /* fpvOutBufferIsNULL (4 bytes) */
+ Stream_Read_UINT32(s, call->cbOutBufferSize); /* cbOutBufferSize (4 bytes) */
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->handles.hContext))))
+ return status;
+
+ if ((status = smartcard_unpack_redir_scard_handle_ref(s, &(call->handles.hCard))))
+ return status;
+
+ if (pvInBufferNdrPtr)
+ {
+ status = smartcard_ndr_read(s, &call->pvInBuffer, call->cbInBufferSize, 1, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+
+ smartcard_trace_control_call(call);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_pack_control_return(wStream* s, const Control_Return* ret)
+{
+ LONG status = 0;
+ DWORD cbDataLen = ret->cbOutBufferSize;
+ UINT32 index = 0;
+
+ smartcard_trace_control_return(ret);
+ if (ret->ReturnCode != SCARD_S_SUCCESS)
+ cbDataLen = 0;
+ if (cbDataLen == SCARD_AUTOALLOCATE)
+ cbDataLen = 0;
+
+ if (!Stream_EnsureRemainingCapacity(s, 4))
+ return SCARD_F_INTERNAL_ERROR;
+
+ Stream_Write_UINT32(s, cbDataLen); /* cbOutBufferSize (4 bytes) */
+ if (!smartcard_ndr_pointer_write(s, &index, cbDataLen))
+ return SCARD_E_NO_MEMORY;
+
+ status = smartcard_ndr_write(s, ret->pvOutBuffer, cbDataLen, 1, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret->ReturnCode;
+}
+
+LONG smartcard_unpack_transmit_call(wStream* s, Transmit_Call* call)
+{
+ UINT32 length = 0;
+ BYTE* pbExtraBytes = NULL;
+ UINT32 pbExtraBytesNdrPtr = 0;
+ UINT32 pbSendBufferNdrPtr = 0;
+ UINT32 pioRecvPciNdrPtr = 0;
+ SCardIO_Request ioSendPci;
+ SCardIO_Request ioRecvPci;
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ call->pioSendPci = NULL;
+ call->pioRecvPci = NULL;
+ call->pbSendBuffer = NULL;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ status = smartcard_unpack_redir_scard_handle(s, &(call->handles.hCard), &index);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 32))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, ioSendPci.dwProtocol); /* dwProtocol (4 bytes) */
+ Stream_Read_UINT32(s, ioSendPci.cbExtraBytes); /* cbExtraBytes (4 bytes) */
+ if (!smartcard_ndr_pointer_read(s, &index,
+ &pbExtraBytesNdrPtr)) /* pbExtraBytesNdrPtr (4 bytes) */
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, call->cbSendLength); /* cbSendLength (4 bytes) */
+ if (!smartcard_ndr_pointer_read(s, &index,
+ &pbSendBufferNdrPtr)) /* pbSendBufferNdrPtr (4 bytes) */
+ return ERROR_INVALID_DATA;
+
+ if (!smartcard_ndr_pointer_read(s, &index, &pioRecvPciNdrPtr)) /* pioRecvPciNdrPtr (4 bytes) */
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_INT32(s, call->fpbRecvBufferIsNULL); /* fpbRecvBufferIsNULL (4 bytes) */
+ Stream_Read_UINT32(s, call->cbRecvLength); /* cbRecvLength (4 bytes) */
+
+ if (ioSendPci.cbExtraBytes > 1024)
+ {
+ WLog_WARN(TAG,
+ "Transmit_Call ioSendPci.cbExtraBytes is out of bounds: %" PRIu32 " (max: 1024)",
+ ioSendPci.cbExtraBytes);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (call->cbSendLength > 66560)
+ {
+ WLog_WARN(TAG, "Transmit_Call cbSendLength is out of bounds: %" PRIu32 " (max: 66560)",
+ ioSendPci.cbExtraBytes);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->handles.hContext))))
+ return status;
+
+ if ((status = smartcard_unpack_redir_scard_handle_ref(s, &(call->handles.hCard))))
+ return status;
+
+ if (ioSendPci.cbExtraBytes && !pbExtraBytesNdrPtr)
+ {
+ WLog_WARN(
+ TAG, "Transmit_Call ioSendPci.cbExtraBytes is non-zero but pbExtraBytesNdrPtr is null");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (pbExtraBytesNdrPtr)
+ {
+ // TODO: Use unified pointer reading
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, length); /* Length (4 bytes) */
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, ioSendPci.cbExtraBytes))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ ioSendPci.pbExtraBytes = Stream_Pointer(s);
+ call->pioSendPci =
+ (LPSCARD_IO_REQUEST)malloc(sizeof(SCARD_IO_REQUEST) + ioSendPci.cbExtraBytes);
+
+ if (!call->pioSendPci)
+ {
+ WLog_WARN(TAG, "Transmit_Call out of memory error (pioSendPci)");
+ return STATUS_NO_MEMORY;
+ }
+
+ call->pioSendPci->dwProtocol = ioSendPci.dwProtocol;
+ call->pioSendPci->cbPciLength = (DWORD)(ioSendPci.cbExtraBytes + sizeof(SCARD_IO_REQUEST));
+ pbExtraBytes = &((BYTE*)call->pioSendPci)[sizeof(SCARD_IO_REQUEST)];
+ Stream_Read(s, pbExtraBytes, ioSendPci.cbExtraBytes);
+ smartcard_unpack_read_size_align(s, ioSendPci.cbExtraBytes, 4);
+ }
+ else
+ {
+ call->pioSendPci = (LPSCARD_IO_REQUEST)calloc(1, sizeof(SCARD_IO_REQUEST));
+
+ if (!call->pioSendPci)
+ {
+ WLog_WARN(TAG, "Transmit_Call out of memory error (pioSendPci)");
+ return STATUS_NO_MEMORY;
+ }
+
+ call->pioSendPci->dwProtocol = ioSendPci.dwProtocol;
+ call->pioSendPci->cbPciLength = sizeof(SCARD_IO_REQUEST);
+ }
+
+ if (pbSendBufferNdrPtr)
+ {
+ status = smartcard_ndr_read(s, &call->pbSendBuffer, call->cbSendLength, 1, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+
+ if (pioRecvPciNdrPtr)
+ {
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, ioRecvPci.dwProtocol); /* dwProtocol (4 bytes) */
+ Stream_Read_UINT32(s, ioRecvPci.cbExtraBytes); /* cbExtraBytes (4 bytes) */
+ if (!smartcard_ndr_pointer_read(s, &index,
+ &pbExtraBytesNdrPtr)) /* pbExtraBytesNdrPtr (4 bytes) */
+ return ERROR_INVALID_DATA;
+
+ if (ioRecvPci.cbExtraBytes && !pbExtraBytesNdrPtr)
+ {
+ WLog_WARN(
+ TAG,
+ "Transmit_Call ioRecvPci.cbExtraBytes is non-zero but pbExtraBytesNdrPtr is null");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (pbExtraBytesNdrPtr)
+ {
+ // TODO: Unify ndr pointer reading
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, length); /* Length (4 bytes) */
+
+ if (ioRecvPci.cbExtraBytes > 1024)
+ {
+ WLog_WARN(TAG,
+ "Transmit_Call ioRecvPci.cbExtraBytes is out of bounds: %" PRIu32
+ " (max: 1024)",
+ ioRecvPci.cbExtraBytes);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (length != ioRecvPci.cbExtraBytes)
+ {
+ WLog_WARN(TAG,
+ "Transmit_Call unexpected length: Actual: %" PRIu32 ", Expected: %" PRIu32
+ " (ioRecvPci.cbExtraBytes)",
+ length, ioRecvPci.cbExtraBytes);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, ioRecvPci.cbExtraBytes))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ ioRecvPci.pbExtraBytes = Stream_Pointer(s);
+ call->pioRecvPci =
+ (LPSCARD_IO_REQUEST)malloc(sizeof(SCARD_IO_REQUEST) + ioRecvPci.cbExtraBytes);
+
+ if (!call->pioRecvPci)
+ {
+ WLog_WARN(TAG, "Transmit_Call out of memory error (pioRecvPci)");
+ return STATUS_NO_MEMORY;
+ }
+
+ call->pioRecvPci->dwProtocol = ioRecvPci.dwProtocol;
+ call->pioRecvPci->cbPciLength =
+ (DWORD)(ioRecvPci.cbExtraBytes + sizeof(SCARD_IO_REQUEST));
+ pbExtraBytes = &((BYTE*)call->pioRecvPci)[sizeof(SCARD_IO_REQUEST)];
+ Stream_Read(s, pbExtraBytes, ioRecvPci.cbExtraBytes);
+ smartcard_unpack_read_size_align(s, ioRecvPci.cbExtraBytes, 4);
+ }
+ else
+ {
+ call->pioRecvPci = (LPSCARD_IO_REQUEST)calloc(1, sizeof(SCARD_IO_REQUEST));
+
+ if (!call->pioRecvPci)
+ {
+ WLog_WARN(TAG, "Transmit_Call out of memory error (pioRecvPci)");
+ return STATUS_NO_MEMORY;
+ }
+
+ call->pioRecvPci->dwProtocol = ioRecvPci.dwProtocol;
+ call->pioRecvPci->cbPciLength = sizeof(SCARD_IO_REQUEST);
+ }
+ }
+
+ smartcard_trace_transmit_call(call);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_pack_transmit_return(wStream* s, const Transmit_Return* ret)
+{
+ LONG status = 0;
+ UINT32 index = 0;
+ LONG error = 0;
+ UINT32 cbRecvLength = ret->cbRecvLength;
+ UINT32 cbRecvPci = ret->pioRecvPci ? ret->pioRecvPci->cbPciLength : 0;
+
+ smartcard_trace_transmit_return(ret);
+
+ if (!ret->pbRecvBuffer)
+ cbRecvLength = 0;
+
+ if (!smartcard_ndr_pointer_write(s, &index, cbRecvPci))
+ return SCARD_E_NO_MEMORY;
+ if (!Stream_EnsureRemainingCapacity(s, 4))
+ return SCARD_E_NO_MEMORY;
+ Stream_Write_UINT32(s, cbRecvLength); /* cbRecvLength (4 bytes) */
+ if (!smartcard_ndr_pointer_write(s, &index, cbRecvLength))
+ return SCARD_E_NO_MEMORY;
+
+ if (ret->pioRecvPci)
+ {
+ UINT32 cbExtraBytes = (UINT32)(ret->pioRecvPci->cbPciLength - sizeof(SCARD_IO_REQUEST));
+ BYTE* pbExtraBytes = &((BYTE*)ret->pioRecvPci)[sizeof(SCARD_IO_REQUEST)];
+
+ if (!Stream_EnsureRemainingCapacity(s, cbExtraBytes + 16))
+ {
+ WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ Stream_Write_UINT32(s, ret->pioRecvPci->dwProtocol); /* dwProtocol (4 bytes) */
+ Stream_Write_UINT32(s, cbExtraBytes); /* cbExtraBytes (4 bytes) */
+ if (!smartcard_ndr_pointer_write(s, &index, cbExtraBytes))
+ return SCARD_E_NO_MEMORY;
+ error = smartcard_ndr_write(s, pbExtraBytes, cbExtraBytes, 1, NDR_PTR_SIMPLE);
+ if (error)
+ return error;
+ }
+
+ status = smartcard_ndr_write(s, ret->pbRecvBuffer, ret->cbRecvLength, 1, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret->ReturnCode;
+}
+
+LONG smartcard_unpack_locate_cards_by_atr_a_call(wStream* s, LocateCardsByATRA_Call* call)
+{
+ UINT32 rgReaderStatesNdrPtr = 0;
+ UINT32 rgAtrMasksNdrPtr = 0;
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ call->rgReaderStates = NULL;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, call->cAtrs);
+ if (!smartcard_ndr_pointer_read(s, &index, &rgAtrMasksNdrPtr))
+ return ERROR_INVALID_DATA;
+ Stream_Read_UINT32(s, call->cReaders); /* cReaders (4 bytes) */
+ if (!smartcard_ndr_pointer_read(s, &index, &rgReaderStatesNdrPtr))
+ return ERROR_INVALID_DATA;
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->handles.hContext))))
+ return status;
+
+ if ((rgAtrMasksNdrPtr && !call->cAtrs) || (!rgAtrMasksNdrPtr && call->cAtrs))
+ {
+ WLog_WARN(TAG,
+ "LocateCardsByATRA_Call rgAtrMasksNdrPtr (0x%08" PRIX32
+ ") and cAtrs (0x%08" PRIX32 ") inconsistency",
+ rgAtrMasksNdrPtr, call->cAtrs);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (rgAtrMasksNdrPtr)
+ {
+ status = smartcard_ndr_read_atrmask(s, &call->rgAtrMasks, call->cAtrs, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+
+ if (rgReaderStatesNdrPtr)
+ {
+ status = smartcard_unpack_reader_state_a(s, &call->rgReaderStates, call->cReaders, &index);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+
+ smartcard_trace_locate_cards_by_atr_a_call(call);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_unpack_context_and_two_strings_a_call(wStream* s, ContextAndTwoStringA_Call* call)
+{
+ UINT32 sz1NdrPtr = 0;
+ UINT32 sz2NdrPtr = 0;
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!smartcard_ndr_pointer_read(s, &index, &sz1NdrPtr))
+ return ERROR_INVALID_DATA;
+ if (!smartcard_ndr_pointer_read(s, &index, &sz2NdrPtr))
+ return ERROR_INVALID_DATA;
+
+ status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, &call->handles.hContext);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (sz1NdrPtr)
+ {
+ status = smartcard_ndr_read_a(s, &call->sz1, NDR_PTR_FULL);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+ if (sz2NdrPtr)
+ {
+ status = smartcard_ndr_read_a(s, &call->sz2, NDR_PTR_FULL);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+ smartcard_trace_context_and_two_strings_a_call(call);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_unpack_context_and_two_strings_w_call(wStream* s, ContextAndTwoStringW_Call* call)
+{
+ UINT32 sz1NdrPtr = 0;
+ UINT32 sz2NdrPtr = 0;
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!smartcard_ndr_pointer_read(s, &index, &sz1NdrPtr))
+ return ERROR_INVALID_DATA;
+ if (!smartcard_ndr_pointer_read(s, &index, &sz2NdrPtr))
+ return ERROR_INVALID_DATA;
+
+ status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, &call->handles.hContext);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (sz1NdrPtr)
+ {
+ status = smartcard_ndr_read_w(s, &call->sz1, NDR_PTR_FULL);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+ if (sz2NdrPtr)
+ {
+ status = smartcard_ndr_read_w(s, &call->sz2, NDR_PTR_FULL);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+ smartcard_trace_context_and_two_strings_w_call(call);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_unpack_locate_cards_a_call(wStream* s, LocateCardsA_Call* call)
+{
+ UINT32 sz1NdrPtr = 0;
+ UINT32 sz2NdrPtr = 0;
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, call->cBytes);
+ if (!smartcard_ndr_pointer_read(s, &index, &sz1NdrPtr))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, call->cReaders);
+ if (!smartcard_ndr_pointer_read(s, &index, &sz2NdrPtr))
+ return ERROR_INVALID_DATA;
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->handles.hContext))))
+ return status;
+
+ if (sz1NdrPtr)
+ {
+ status =
+ smartcard_ndr_read_fixed_string_a(s, &call->mszCards, call->cBytes, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+ if (sz2NdrPtr)
+ {
+ status = smartcard_unpack_reader_state_a(s, &call->rgReaderStates, call->cReaders, &index);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+ smartcard_trace_locate_cards_a_call(call);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_unpack_locate_cards_w_call(wStream* s, LocateCardsW_Call* call)
+{
+ UINT32 sz1NdrPtr = 0;
+ UINT32 sz2NdrPtr = 0;
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, call->cBytes);
+ if (!smartcard_ndr_pointer_read(s, &index, &sz1NdrPtr))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, call->cReaders);
+ if (!smartcard_ndr_pointer_read(s, &index, &sz2NdrPtr))
+ return ERROR_INVALID_DATA;
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->handles.hContext))))
+ return status;
+
+ if (sz1NdrPtr)
+ {
+ status =
+ smartcard_ndr_read_fixed_string_w(s, &call->mszCards, call->cBytes, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+ if (sz2NdrPtr)
+ {
+ status = smartcard_unpack_reader_state_w(s, &call->rgReaderStates, call->cReaders, &index);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+ smartcard_trace_locate_cards_w_call(call);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_unpack_set_attrib_call(wStream* s, SetAttrib_Call* call)
+{
+ UINT32 index = 0;
+ UINT32 ndrPtr = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ status = smartcard_unpack_redir_scard_handle(s, &(call->handles.hCard), &index);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
+ return STATUS_BUFFER_TOO_SMALL;
+ Stream_Read_UINT32(s, call->dwAttrId);
+ Stream_Read_UINT32(s, call->cbAttrLen);
+
+ if (!smartcard_ndr_pointer_read(s, &index, &ndrPtr))
+ return ERROR_INVALID_DATA;
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->handles.hContext))))
+ return status;
+
+ if ((status = smartcard_unpack_redir_scard_handle_ref(s, &(call->handles.hCard))))
+ return status;
+
+ if (ndrPtr)
+ {
+ // TODO: call->cbAttrLen was larger than the pointer value.
+ // TODO: Maybe need to refine the checks?
+ status = smartcard_ndr_read(s, &call->pbAttr, 0, 1, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+ smartcard_trace_set_attrib_call(call);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_unpack_locate_cards_by_atr_w_call(wStream* s, LocateCardsByATRW_Call* call)
+{
+ UINT32 rgReaderStatesNdrPtr = 0;
+ UINT32 rgAtrMasksNdrPtr = 0;
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ call->rgReaderStates = NULL;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, call->cAtrs);
+ if (!smartcard_ndr_pointer_read(s, &index, &rgAtrMasksNdrPtr))
+ return ERROR_INVALID_DATA;
+
+ Stream_Read_UINT32(s, call->cReaders); /* cReaders (4 bytes) */
+ if (!smartcard_ndr_pointer_read(s, &index, &rgReaderStatesNdrPtr))
+ return ERROR_INVALID_DATA;
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->handles.hContext))))
+ return status;
+
+ if ((rgAtrMasksNdrPtr && !call->cAtrs) || (!rgAtrMasksNdrPtr && call->cAtrs))
+ {
+ WLog_WARN(TAG,
+ "LocateCardsByATRW_Call rgAtrMasksNdrPtr (0x%08" PRIX32
+ ") and cAtrs (0x%08" PRIX32 ") inconsistency",
+ rgAtrMasksNdrPtr, call->cAtrs);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (rgAtrMasksNdrPtr)
+ {
+ status = smartcard_ndr_read_atrmask(s, &call->rgAtrMasks, call->cAtrs, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+
+ if (rgReaderStatesNdrPtr)
+ {
+ status = smartcard_unpack_reader_state_w(s, &call->rgReaderStates, call->cReaders, &index);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+
+ smartcard_trace_locate_cards_by_atr_w_call(call);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_unpack_read_cache_a_call(wStream* s, ReadCacheA_Call* call)
+{
+ UINT32 mszNdrPtr = 0;
+ UINT32 contextNdrPtr = 0;
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ if (!smartcard_ndr_pointer_read(s, &index, &mszNdrPtr))
+ return ERROR_INVALID_DATA;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->Common.handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!smartcard_ndr_pointer_read(s, &index, &contextNdrPtr))
+ return ERROR_INVALID_DATA;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
+ return STATUS_BUFFER_TOO_SMALL;
+ Stream_Read_UINT32(s, call->Common.FreshnessCounter);
+ Stream_Read_INT32(s, call->Common.fPbDataIsNULL);
+ Stream_Read_UINT32(s, call->Common.cbDataLen);
+
+ call->szLookupName = NULL;
+ if (mszNdrPtr)
+ {
+ status = smartcard_ndr_read_a(s, &call->szLookupName, NDR_PTR_FULL);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+
+ status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &call->Common.handles.hContext);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (contextNdrPtr)
+ {
+ status = smartcard_ndr_read_u(s, &call->Common.CardIdentifier);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+ smartcard_trace_read_cache_a_call(call);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_unpack_read_cache_w_call(wStream* s, ReadCacheW_Call* call)
+{
+ UINT32 mszNdrPtr = 0;
+ UINT32 contextNdrPtr = 0;
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ if (!smartcard_ndr_pointer_read(s, &index, &mszNdrPtr))
+ return ERROR_INVALID_DATA;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->Common.handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!smartcard_ndr_pointer_read(s, &index, &contextNdrPtr))
+ return ERROR_INVALID_DATA;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
+ return STATUS_BUFFER_TOO_SMALL;
+ Stream_Read_UINT32(s, call->Common.FreshnessCounter);
+ Stream_Read_INT32(s, call->Common.fPbDataIsNULL);
+ Stream_Read_UINT32(s, call->Common.cbDataLen);
+
+ call->szLookupName = NULL;
+ if (mszNdrPtr)
+ {
+ status = smartcard_ndr_read_w(s, &call->szLookupName, NDR_PTR_FULL);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+
+ status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &call->Common.handles.hContext);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (contextNdrPtr)
+ {
+ status = smartcard_ndr_read_u(s, &call->Common.CardIdentifier);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+ smartcard_trace_read_cache_w_call(call);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_unpack_write_cache_a_call(wStream* s, WriteCacheA_Call* call)
+{
+ UINT32 mszNdrPtr = 0;
+ UINT32 contextNdrPtr = 0;
+ UINT32 pbDataNdrPtr = 0;
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ if (!smartcard_ndr_pointer_read(s, &index, &mszNdrPtr))
+ return ERROR_INVALID_DATA;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->Common.handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!smartcard_ndr_pointer_read(s, &index, &contextNdrPtr))
+ return ERROR_INVALID_DATA;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ Stream_Read_UINT32(s, call->Common.FreshnessCounter);
+ Stream_Read_UINT32(s, call->Common.cbDataLen);
+
+ if (!smartcard_ndr_pointer_read(s, &index, &pbDataNdrPtr))
+ return ERROR_INVALID_DATA;
+
+ call->szLookupName = NULL;
+ if (mszNdrPtr)
+ {
+ status = smartcard_ndr_read_a(s, &call->szLookupName, NDR_PTR_FULL);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+
+ status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &call->Common.handles.hContext);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ call->Common.CardIdentifier = NULL;
+ if (contextNdrPtr)
+ {
+ status = smartcard_ndr_read_u(s, &call->Common.CardIdentifier);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+
+ call->Common.pbData = NULL;
+ if (pbDataNdrPtr)
+ {
+ status =
+ smartcard_ndr_read(s, &call->Common.pbData, call->Common.cbDataLen, 1, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+ smartcard_trace_write_cache_a_call(call);
+ return SCARD_S_SUCCESS;
+}
+
+LONG smartcard_unpack_write_cache_w_call(wStream* s, WriteCacheW_Call* call)
+{
+ UINT32 mszNdrPtr = 0;
+ UINT32 contextNdrPtr = 0;
+ UINT32 pbDataNdrPtr = 0;
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ if (!smartcard_ndr_pointer_read(s, &index, &mszNdrPtr))
+ return ERROR_INVALID_DATA;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->Common.handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (!smartcard_ndr_pointer_read(s, &index, &contextNdrPtr))
+ return ERROR_INVALID_DATA;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return STATUS_BUFFER_TOO_SMALL;
+ Stream_Read_UINT32(s, call->Common.FreshnessCounter);
+ Stream_Read_UINT32(s, call->Common.cbDataLen);
+
+ if (!smartcard_ndr_pointer_read(s, &index, &pbDataNdrPtr))
+ return ERROR_INVALID_DATA;
+
+ call->szLookupName = NULL;
+ if (mszNdrPtr)
+ {
+ status = smartcard_ndr_read_w(s, &call->szLookupName, NDR_PTR_FULL);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+
+ status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &call->Common.handles.hContext);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ call->Common.CardIdentifier = NULL;
+ if (contextNdrPtr)
+ {
+ status = smartcard_ndr_read_u(s, &call->Common.CardIdentifier);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+
+ call->Common.pbData = NULL;
+ if (pbDataNdrPtr)
+ {
+ status =
+ smartcard_ndr_read(s, &call->Common.pbData, call->Common.cbDataLen, 1, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ }
+ smartcard_trace_write_cache_w_call(call);
+ return status;
+}
+
+LONG smartcard_unpack_get_transmit_count_call(wStream* s, GetTransmitCount_Call* call)
+{
+ UINT32 index = 0;
+ UINT32 pbContextNdrPtr = 0;
+
+ LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index,
+ &pbContextNdrPtr);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ status = smartcard_unpack_redir_scard_handle(s, &(call->handles.hCard), &index);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr,
+ &(call->handles.hContext))))
+ {
+ WLog_ERR(TAG, "smartcard_unpack_redir_scard_context_ref failed with error %" PRId32 "",
+ status);
+ return status;
+ }
+
+ if ((status = smartcard_unpack_redir_scard_handle_ref(s, &(call->handles.hCard))))
+ WLog_ERR(TAG, "smartcard_unpack_redir_scard_handle_ref failed with error %" PRId32 "",
+ status);
+
+ smartcard_trace_get_transmit_count_call(call);
+ return status;
+}
+
+LONG smartcard_unpack_get_reader_icon_call(wStream* s, GetReaderIcon_Call* call)
+{
+ return smartcard_unpack_common_context_and_string_w(s, &call->handles.hContext,
+ &call->szReaderName);
+}
+
+LONG smartcard_unpack_context_and_string_a_call(wStream* s, ContextAndStringA_Call* call)
+{
+ return smartcard_unpack_common_context_and_string_a(s, &call->handles.hContext, &call->sz);
+}
+
+LONG smartcard_unpack_context_and_string_w_call(wStream* s, ContextAndStringW_Call* call)
+{
+ return smartcard_unpack_common_context_and_string_w(s, &call->handles.hContext, &call->sz);
+}
+
+LONG smartcard_unpack_get_device_type_id_call(wStream* s, GetDeviceTypeId_Call* call)
+{
+ return smartcard_unpack_common_context_and_string_w(s, &call->handles.hContext,
+ &call->szReaderName);
+}
+
+LONG smartcard_pack_device_type_id_return(wStream* s, const GetDeviceTypeId_Return* ret)
+{
+ smartcard_trace_device_type_id_return(ret);
+
+ if (!Stream_EnsureRemainingCapacity(s, 4))
+ {
+ WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ Stream_Write_UINT32(s, ret->dwDeviceId); /* cBytes (4 bytes) */
+
+ return ret->ReturnCode;
+}
+
+LONG smartcard_pack_locate_cards_return(wStream* s, const LocateCards_Return* ret)
+{
+ LONG status = 0;
+ DWORD cbDataLen = ret->cReaders;
+ UINT32 index = 0;
+
+ smartcard_trace_locate_cards_return(ret);
+ if (ret->ReturnCode != SCARD_S_SUCCESS)
+ cbDataLen = 0;
+ if (cbDataLen == SCARD_AUTOALLOCATE)
+ cbDataLen = 0;
+
+ if (!Stream_EnsureRemainingCapacity(s, 4))
+ {
+ WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ Stream_Write_UINT32(s, cbDataLen); /* cBytes (4 cbDataLen) */
+ if (!smartcard_ndr_pointer_write(s, &index, cbDataLen))
+ return SCARD_E_NO_MEMORY;
+
+ status = smartcard_ndr_write_state(s, ret->rgReaderStates, cbDataLen, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret->ReturnCode;
+}
+
+LONG smartcard_pack_get_reader_icon_return(wStream* s, const GetReaderIcon_Return* ret)
+{
+ LONG status = 0;
+ UINT32 index = 0;
+ DWORD cbDataLen = ret->cbDataLen;
+ smartcard_trace_get_reader_icon_return(ret);
+ if (ret->ReturnCode != SCARD_S_SUCCESS)
+ cbDataLen = 0;
+ if (cbDataLen == SCARD_AUTOALLOCATE)
+ cbDataLen = 0;
+
+ if (!Stream_EnsureRemainingCapacity(s, 4))
+ {
+ WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ Stream_Write_UINT32(s, cbDataLen); /* cBytes (4 cbDataLen) */
+ if (!smartcard_ndr_pointer_write(s, &index, cbDataLen))
+ return SCARD_E_NO_MEMORY;
+
+ status = smartcard_ndr_write(s, ret->pbData, cbDataLen, 1, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret->ReturnCode;
+}
+
+LONG smartcard_pack_get_transmit_count_return(wStream* s, const GetTransmitCount_Return* ret)
+{
+ smartcard_trace_get_transmit_count_return(ret);
+
+ if (!Stream_EnsureRemainingCapacity(s, 4))
+ {
+ WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ Stream_Write_UINT32(s, ret->cTransmitCount); /* cBytes (4 cbDataLen) */
+
+ return ret->ReturnCode;
+}
+
+LONG smartcard_pack_read_cache_return(wStream* s, const ReadCache_Return* ret)
+{
+ LONG status = 0;
+ UINT32 index = 0;
+ DWORD cbDataLen = ret->cbDataLen;
+ smartcard_trace_read_cache_return(ret);
+ if (ret->ReturnCode != SCARD_S_SUCCESS)
+ cbDataLen = 0;
+
+ if (cbDataLen == SCARD_AUTOALLOCATE)
+ cbDataLen = 0;
+
+ if (!Stream_EnsureRemainingCapacity(s, 4))
+ {
+ WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ Stream_Write_UINT32(s, cbDataLen); /* cBytes (4 cbDataLen) */
+ if (!smartcard_ndr_pointer_write(s, &index, cbDataLen))
+ return SCARD_E_NO_MEMORY;
+
+ status = smartcard_ndr_write(s, ret->pbData, cbDataLen, 1, NDR_PTR_SIMPLE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret->ReturnCode;
+}
diff --git a/libfreerdp/utils/stopwatch.c b/libfreerdp/utils/stopwatch.c
new file mode 100644
index 0000000..3989638
--- /dev/null
+++ b/libfreerdp/utils/stopwatch.c
@@ -0,0 +1,98 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Stopwatch Utils
+ *
+ * Copyright 2011 Stephen Erisman
+ *
+ * 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 <freerdp/config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <freerdp/utils/stopwatch.h>
+
+#ifdef _WIN32
+LARGE_INTEGER stopwatch_freq = { 0 };
+#else
+#include <sys/time.h>
+#endif
+
+static void stopwatch_set_time(UINT64* usecs)
+{
+#ifdef _WIN32
+ LARGE_INTEGER perfcount;
+ QueryPerformanceCounter(&perfcount);
+ *usecs = (perfcount.QuadPart * 1000000) / stopwatch_freq.QuadPart;
+#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ *usecs = tv.tv_sec * 1000000 + tv.tv_usec;
+#endif
+}
+
+STOPWATCH* stopwatch_create(void)
+{
+ STOPWATCH* sw = NULL;
+#ifdef _WIN32
+ if (stopwatch_freq.QuadPart == 0)
+ {
+ QueryPerformanceFrequency(&stopwatch_freq);
+ }
+#endif
+
+ sw = (STOPWATCH*)malloc(sizeof(STOPWATCH));
+ if (!sw)
+ return NULL;
+ stopwatch_reset(sw);
+
+ return sw;
+}
+
+void stopwatch_free(STOPWATCH* stopwatch)
+{
+ free(stopwatch);
+}
+
+void stopwatch_start(STOPWATCH* stopwatch)
+{
+ stopwatch_set_time(&stopwatch->start);
+ stopwatch->count++;
+}
+
+void stopwatch_stop(STOPWATCH* stopwatch)
+{
+ stopwatch_set_time(&stopwatch->end);
+ stopwatch->elapsed += (stopwatch->end - stopwatch->start);
+}
+
+void stopwatch_reset(STOPWATCH* stopwatch)
+{
+ stopwatch->start = 0;
+ stopwatch->end = 0;
+ stopwatch->elapsed = 0;
+ stopwatch->count = 0;
+}
+
+double stopwatch_get_elapsed_time_in_seconds(STOPWATCH* stopwatch)
+{
+ return (stopwatch->elapsed / 1000000.0);
+}
+
+void stopwatch_get_elapsed_time_in_useconds(STOPWATCH* stopwatch, UINT32* sec, UINT32* usec)
+{
+ *sec = (UINT32)stopwatch->elapsed / 1000000;
+ *usec = (UINT32)stopwatch->elapsed % 1000000;
+}
diff --git a/libfreerdp/utils/string.c b/libfreerdp/utils/string.c
new file mode 100644
index 0000000..3ee73f1
--- /dev/null
+++ b/libfreerdp/utils/string.c
@@ -0,0 +1,105 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * String Utils - Helper functions converting something to string
+ *
+ * Copyright 2022 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2022 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/utils/string.h>
+#include <freerdp/settings.h>
+
+char* rdp_redirection_flags_to_string(UINT32 flags, char* buffer, size_t size)
+{
+ struct map_t
+ {
+ UINT32 flag;
+ const char* name;
+ };
+ const struct map_t map[] = {
+ { LB_TARGET_NET_ADDRESS, "LB_TARGET_NET_ADDRESS" },
+ { LB_LOAD_BALANCE_INFO, "LB_LOAD_BALANCE_INFO" },
+ { LB_USERNAME, "LB_USERNAME" },
+ { LB_DOMAIN, "LB_DOMAIN" },
+ { LB_PASSWORD, "LB_PASSWORD" },
+ { LB_DONTSTOREUSERNAME, "LB_DONTSTOREUSERNAME" },
+ { LB_SMARTCARD_LOGON, "LB_SMARTCARD_LOGON" },
+ { LB_NOREDIRECT, "LB_NOREDIRECT" },
+ { LB_TARGET_FQDN, "LB_TARGET_FQDN" },
+ { LB_TARGET_NETBIOS_NAME, "LB_TARGET_NETBIOS_NAME" },
+ { LB_TARGET_NET_ADDRESSES, "LB_TARGET_NET_ADDRESSES" },
+ { LB_CLIENT_TSV_URL, "LB_CLIENT_TSV_URL" },
+ { LB_SERVER_TSV_CAPABLE, "LB_SERVER_TSV_CAPABLE" },
+ { LB_PASSWORD_IS_PK_ENCRYPTED, "LB_PASSWORD_IS_PK_ENCRYPTED" },
+ { LB_REDIRECTION_GUID, "LB_REDIRECTION_GUID" },
+ { LB_TARGET_CERTIFICATE, "LB_TARGET_CERTIFICATE" },
+ };
+
+ for (size_t x = 0; x < ARRAYSIZE(map); x++)
+ {
+ const struct map_t* cur = &map[x];
+ if (flags & cur->flag)
+ {
+ if (!winpr_str_append(cur->name, buffer, size, "|"))
+ return NULL;
+ }
+ }
+ return buffer;
+}
+
+char* rdp_cluster_info_flags_to_string(UINT32 flags, char* buffer, size_t size)
+{
+ const UINT32 version = (flags & ServerSessionRedirectionVersionMask) >> 2;
+ if (flags & REDIRECTION_SUPPORTED)
+ winpr_str_append("REDIRECTION_SUPPORTED", buffer, size, "|");
+ if (flags & REDIRECTED_SESSIONID_FIELD_VALID)
+ winpr_str_append("REDIRECTED_SESSIONID_FIELD_VALID", buffer, size, "|");
+ if (flags & REDIRECTED_SMARTCARD)
+ winpr_str_append("REDIRECTED_SMARTCARD", buffer, size, "|");
+
+ const char* str = NULL;
+ switch (version)
+ {
+ case REDIRECTION_VERSION1:
+ str = "REDIRECTION_VERSION1";
+ break;
+ case REDIRECTION_VERSION2:
+ str = "REDIRECTION_VERSION2";
+ break;
+ case REDIRECTION_VERSION3:
+ str = "REDIRECTION_VERSION3";
+ break;
+ case REDIRECTION_VERSION4:
+ str = "REDIRECTION_VERSION4";
+ break;
+ case REDIRECTION_VERSION5:
+ str = "REDIRECTION_VERSION5";
+ break;
+ case REDIRECTION_VERSION6:
+ str = "REDIRECTION_VERSION6";
+ break;
+ default:
+ str = "REDIRECTION_VERSION_UNKNOWN";
+ break;
+ }
+ winpr_str_append(str, buffer, size, "|");
+ {
+ char msg[32] = { 0 };
+ _snprintf(msg, sizeof(msg), "[0x%08" PRIx32 "]", flags);
+ winpr_str_append(msg, buffer, size, "");
+ }
+ return buffer;
+}
diff --git a/libfreerdp/utils/test/CMakeLists.txt b/libfreerdp/utils/test/CMakeLists.txt
new file mode 100644
index 0000000..f2b6977
--- /dev/null
+++ b/libfreerdp/utils/test/CMakeLists.txt
@@ -0,0 +1,28 @@
+
+set(MODULE_NAME "TestFreeRDPUtils")
+set(MODULE_PREFIX "TEST_FREERDP_UTILS")
+
+set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
+
+set(${MODULE_PREFIX}_TESTS
+ TestRingBuffer.c
+ TestPodArrays.c
+)
+
+create_test_sourcelist(${MODULE_PREFIX}_SRCS
+ ${${MODULE_PREFIX}_DRIVER}
+ ${${MODULE_PREFIX}_TESTS})
+
+add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
+
+target_link_libraries(${MODULE_NAME} freerdp winpr)
+
+set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
+
+foreach(test ${${MODULE_PREFIX}_TESTS})
+ get_filename_component(TestName ${test} NAME_WE)
+ add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
+endforeach()
+
+set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP/Test")
+
diff --git a/libfreerdp/utils/test/TestPodArrays.c b/libfreerdp/utils/test/TestPodArrays.c
new file mode 100644
index 0000000..6c88b53
--- /dev/null
+++ b/libfreerdp/utils/test/TestPodArrays.c
@@ -0,0 +1,131 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * TestPodArrays
+ *
+ * Copyright 2022 David Fort <contact@hardening-consulting.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <freerdp/utils/pod_arrays.h>
+
+static BOOL cb_compute_sum(UINT32* v, void* target)
+{
+ UINT32* ret = (UINT32*)target;
+ *ret += *v;
+ return TRUE;
+}
+
+static BOOL cb_stop_at_5(UINT32* v, void* target)
+{
+ UINT32* ret = (UINT32*)target;
+ *ret += 1;
+ return (*ret != 5);
+}
+
+static BOOL cb_set_to_1(UINT32* v, void* target)
+{
+ *v = 1;
+ return TRUE;
+}
+
+static BOOL cb_reset_after_1(UINT32* v, void* target)
+{
+ ArrayUINT32* a = (ArrayUINT32*)target;
+ array_uint32_reset(a);
+ return TRUE;
+}
+
+typedef struct
+{
+ UINT32 v1;
+ UINT16 v2;
+} BasicStruct;
+
+static BOOL cb_basic_struct(BasicStruct* v, void* target)
+{
+ return (v->v1 == 1) && (v->v2 == 2);
+}
+
+POD_ARRAYS_IMPL(BasicStruct, basicstruct)
+
+int TestPodArrays(int argc, char* argv[])
+{
+ int rc = -1;
+ UINT32 sum = 0;
+ UINT32 foreach_index = 0;
+ ArrayUINT32 uint32s = { 0 };
+ UINT32* ptr = NULL;
+ const UINT32* cptr = NULL;
+ ArrayBasicStruct basicStructs = { 0 };
+ BasicStruct basicStruct = { 1, 2 };
+
+ array_uint32_init(&uint32s);
+ array_basicstruct_init(&basicStructs);
+
+ for (UINT32 i = 0; i < 10; i++)
+ if (!array_uint32_append(&uint32s, i))
+ goto fail;
+
+ sum = 0;
+ if (!array_uint32_foreach(&uint32s, cb_compute_sum, &sum))
+ goto fail;
+
+ if (sum != 45)
+ goto fail;
+
+ foreach_index = 0;
+ if (array_uint32_foreach(&uint32s, cb_stop_at_5, &foreach_index))
+ goto fail;
+
+ if (foreach_index != 5)
+ goto fail;
+
+ if (array_uint32_get(&uint32s, 4) != 4)
+ goto fail;
+
+ array_uint32_set(&uint32s, 4, 5);
+ if (array_uint32_get(&uint32s, 4) != 5)
+ goto fail;
+
+ ptr = array_uint32_data(&uint32s);
+ if (*ptr != 0)
+ goto fail;
+
+ cptr = array_uint32_cdata(&uint32s);
+ if (*cptr != 0)
+ goto fail;
+
+ /* test modifying values of the array during the foreach */
+ if (!array_uint32_foreach(&uint32s, cb_set_to_1, NULL) || array_uint32_get(&uint32s, 5) != 1)
+ goto fail;
+
+ /* this one is to test that we can modify the array itself during the foreach and that things
+ * go nicely */
+ if (!array_uint32_foreach(&uint32s, cb_reset_after_1, &uint32s) || array_uint32_size(&uint32s))
+ goto fail;
+
+ /* give a try with an array of BasicStructs */
+ if (!array_basicstruct_append(&basicStructs, basicStruct))
+ goto fail;
+
+ if (!array_basicstruct_foreach(&basicStructs, cb_basic_struct, NULL))
+ goto fail;
+
+ rc = 0;
+
+fail:
+ array_uint32_uninit(&uint32s);
+ array_basicstruct_uninit(&basicStructs);
+
+ return rc;
+}
diff --git a/libfreerdp/utils/test/TestRingBuffer.c b/libfreerdp/utils/test/TestRingBuffer.c
new file mode 100644
index 0000000..8e88a65
--- /dev/null
+++ b/libfreerdp/utils/test/TestRingBuffer.c
@@ -0,0 +1,226 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 Thincast Technologies GmbH
+ * Copyright 2014 Hardening <contact@hardening-consulting.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <freerdp/utils/ringbuffer.h>
+
+static BOOL test_overlaps(void)
+{
+ RingBuffer rb;
+ DataChunk chunks[2];
+ BYTE bytes[200];
+ int nchunks = 0;
+ int counter = 0;
+
+ for (size_t i = 0; i < sizeof(bytes); i++)
+ bytes[i] = (BYTE)i;
+
+ ringbuffer_init(&rb, 5);
+ if (!ringbuffer_write(&rb, bytes, 4)) /* [0123.] */
+ goto error;
+ counter += 4;
+ ringbuffer_commit_read_bytes(&rb, 2); /* [..23.] */
+
+ if (!ringbuffer_write(&rb, &bytes[counter], 2)) /* [5.234] */
+ goto error;
+ counter += 2;
+
+ nchunks = ringbuffer_peek(&rb, chunks, 4);
+ if (nchunks != 2 || chunks[0].size != 3 || chunks[1].size != 1)
+ goto error;
+
+ for (int x = 0, j = 2; x < nchunks; x++)
+ {
+ for (size_t k = 0; k < chunks[x].size; k++, j++)
+ {
+ if (chunks[x].data[k] != (BYTE)j)
+ goto error;
+ }
+ }
+
+ ringbuffer_commit_read_bytes(&rb, 3); /* [5....] */
+ if (ringbuffer_used(&rb) != 1)
+ goto error;
+
+ if (!ringbuffer_write(&rb, &bytes[counter], 6)) /* [56789ab....] */
+ goto error;
+
+ ringbuffer_commit_read_bytes(&rb, 6); /* [......b....] */
+ nchunks = ringbuffer_peek(&rb, chunks, 10);
+ if (nchunks != 1 || chunks[0].size != 1 || (*chunks[0].data != 0xb))
+ goto error;
+
+ if (ringbuffer_capacity(&rb) != 5)
+ goto error;
+
+ ringbuffer_destroy(&rb);
+ return TRUE;
+error:
+ ringbuffer_destroy(&rb);
+ return FALSE;
+}
+
+int TestRingBuffer(int argc, char* argv[])
+{
+ RingBuffer ringBuffer;
+ int testNo = 0;
+ BYTE* tmpBuf = NULL;
+ BYTE* rb_ptr = NULL;
+ DataChunk chunks[2];
+
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+ if (!ringbuffer_init(&ringBuffer, 10))
+ {
+ fprintf(stderr, "unable to initialize ringbuffer\n");
+ return -1;
+ }
+
+ tmpBuf = (BYTE*)malloc(50);
+ if (!tmpBuf)
+ return -1;
+
+ for (int i = 0; i < 50; i++)
+ tmpBuf[i] = (char)i;
+
+ fprintf(stderr, "%d: basic tests...", ++testNo);
+ if (!ringbuffer_write(&ringBuffer, tmpBuf, 5) || !ringbuffer_write(&ringBuffer, tmpBuf, 5) ||
+ !ringbuffer_write(&ringBuffer, tmpBuf, 5))
+ {
+ fprintf(stderr, "error when writing bytes\n");
+ return -1;
+ }
+
+ if (ringbuffer_used(&ringBuffer) != 15)
+ {
+ fprintf(stderr, "invalid used size got %" PRIuz " when I would expect 15\n",
+ ringbuffer_used(&ringBuffer));
+ return -1;
+ }
+
+ if (ringbuffer_peek(&ringBuffer, chunks, 10) != 1 || chunks[0].size != 10)
+ {
+ fprintf(stderr, "error when reading bytes\n");
+ return -1;
+ }
+ ringbuffer_commit_read_bytes(&ringBuffer, chunks[0].size);
+
+ /* check retrieved bytes */
+ for (size_t i = 0; i < chunks[0].size; i++)
+ {
+ if (chunks[0].data[i] != i % 5)
+ {
+ fprintf(stderr, "invalid byte at %d, got %" PRIu8 " instead of %d\n", i,
+ chunks[0].data[i], i % 5);
+ return -1;
+ }
+ }
+
+ if (ringbuffer_used(&ringBuffer) != 5)
+ {
+ fprintf(stderr, "invalid used size after read got %" PRIuz " when I would expect 5\n",
+ ringbuffer_used(&ringBuffer));
+ return -1;
+ }
+
+ /* write some more bytes to have writePtr < readPtr and data splitted in 2 chunks */
+ if (!ringbuffer_write(&ringBuffer, tmpBuf, 6) ||
+ ringbuffer_peek(&ringBuffer, chunks, 11) != 2 || chunks[0].size != 10 ||
+ chunks[1].size != 1)
+ {
+ fprintf(stderr, "invalid read of splitted data\n");
+ return -1;
+ }
+
+ ringbuffer_commit_read_bytes(&ringBuffer, 11);
+ fprintf(stderr, "ok\n");
+
+ fprintf(stderr, "%d: peek with nothing to read...", ++testNo);
+ if (ringbuffer_peek(&ringBuffer, chunks, 10))
+ {
+ fprintf(stderr, "peek returns some chunks\n");
+ return -1;
+ }
+ fprintf(stderr, "ok\n");
+
+ fprintf(stderr, "%d: ensure_linear_write / read() shouldn't grow...", ++testNo);
+ for (int i = 0; i < 1000; i++)
+ {
+ rb_ptr = ringbuffer_ensure_linear_write(&ringBuffer, 50);
+ if (!rb_ptr)
+ {
+ fprintf(stderr, "ringbuffer_ensure_linear_write() error\n");
+ return -1;
+ }
+
+ memcpy(rb_ptr, tmpBuf, 50);
+
+ if (!ringbuffer_commit_written_bytes(&ringBuffer, 50))
+ {
+ fprintf(stderr, "ringbuffer_commit_written_bytes() error, i=%d\n", i);
+ return -1;
+ }
+
+ // ringbuffer_commit_read_bytes(&ringBuffer, 25);
+ }
+
+ for (int i = 0; i < 1000; i++)
+ ringbuffer_commit_read_bytes(&ringBuffer, 25);
+
+ for (int i = 0; i < 1000; i++)
+ ringbuffer_commit_read_bytes(&ringBuffer, 25);
+
+ if (ringbuffer_capacity(&ringBuffer) != 10)
+ {
+ fprintf(stderr, "not the expected capacity, have %" PRIuz " and expects 10\n",
+ ringbuffer_capacity(&ringBuffer));
+ return -1;
+ }
+ fprintf(stderr, "ok\n");
+
+ fprintf(stderr, "%d: free size is correctly computed...", ++testNo);
+ for (int i = 0; i < 1000; i++)
+ {
+ ringbuffer_ensure_linear_write(&ringBuffer, 50);
+ if (!ringbuffer_commit_written_bytes(&ringBuffer, 50))
+ {
+ fprintf(stderr, "ringbuffer_commit_written_bytes() error, i=%d\n", i);
+ return -1;
+ }
+ }
+ ringbuffer_commit_read_bytes(&ringBuffer, 50 * 1000);
+ fprintf(stderr, "ok\n");
+
+ ringbuffer_destroy(&ringBuffer);
+
+ fprintf(stderr, "%d: specific overlaps test...", ++testNo);
+ if (!test_overlaps())
+ {
+ fprintf(stderr, "ko\n");
+ return -1;
+ }
+ fprintf(stderr, "ok\n");
+
+ ringbuffer_destroy(&ringBuffer);
+ free(tmpBuf);
+ return 0;
+}