Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,68 @@
# 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
helpers.c
passphrase.c
cliprdr_utils.c
rdpdr_utils.c
pcap.c
profiler.c
ringbuffer.c
platform_signal.h
signal.c
string.c
gfx.c
drdynvc.c
smartcard_operations.c
smartcard_pack.h
smartcard_pack.c
smartcard_call.c
stopwatch.c
http.c
)
if(WIN32)
list(APPEND ${MODULE_PREFIX}_SRCS signal_win32.c)
else()
list(APPEND ${MODULE_PREFIX}_SRCS signal_posix.c)
endif()
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()
check_library_exists(m pow "" HAVE_LIB_M)
if(HAVE_LIB_M)
freerdp_library_add(m)
freerdp_pc_add_library_private("m")
endif()
if(BUILD_TESTING_INTERNAL OR BUILD_TESTING)
add_subdirectory(test)
endif()

View File

@@ -0,0 +1,259 @@
/**
* 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 = nullptr;
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* descriptor)
{
UINT64 tmp = 0;
WINPR_ASSERT(descriptor);
if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(FILEDESCRIPTORW)))
return FALSE;
Stream_Read_UINT32(s, descriptor->dwFlags); /* flags (4 bytes) */
Stream_Read_UINT32(s, descriptor->clsid.Data1);
Stream_Read_UINT16(s, descriptor->clsid.Data2);
Stream_Read_UINT16(s, descriptor->clsid.Data3);
Stream_Read(s, &descriptor->clsid.Data4, sizeof(descriptor->clsid.Data4));
Stream_Read_INT32(s, descriptor->sizel.cx);
Stream_Read_INT32(s, descriptor->sizel.cy);
Stream_Read_INT32(s, descriptor->pointl.x);
Stream_Read_INT32(s, descriptor->pointl.y);
Stream_Read_UINT32(s, descriptor->dwFileAttributes); /* fileAttributes (4 bytes) */
Stream_Read_UINT64(s, tmp); /* ftCreationTime (8 bytes) */
descriptor->ftCreationTime = uint64_to_filetime(tmp);
Stream_Read_UINT64(s, tmp); /* ftLastAccessTime (8 bytes) */
descriptor->ftLastAccessTime = uint64_to_filetime(tmp);
Stream_Read_UINT64(s, tmp); /* lastWriteTime (8 bytes) */
descriptor->ftLastWriteTime = uint64_to_filetime(tmp);
Stream_Read_UINT32(s, descriptor->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
Stream_Read_UINT32(s, descriptor->nFileSizeLow); /* fileSizeLow (4 bytes) */
Stream_Read_UTF16_String(s, descriptor->cFileName,
ARRAYSIZE(descriptor->cFileName)); /* cFileName (520 bytes) */
return TRUE;
}
BOOL cliprdr_write_filedescriptor(wStream* s, const FILEDESCRIPTORW* descriptor)
{
WINPR_ASSERT(descriptor);
if (!Stream_EnsureRemainingCapacity(s, sizeof(FILEDESCRIPTORW)))
return FALSE;
Stream_Write_UINT32(s, descriptor->dwFlags); /* flags (4 bytes) */
Stream_Write_UINT32(s, descriptor->clsid.Data1);
Stream_Write_UINT16(s, descriptor->clsid.Data2);
Stream_Write_UINT16(s, descriptor->clsid.Data3);
Stream_Write(s, &descriptor->clsid.Data4, sizeof(descriptor->clsid.Data4));
Stream_Write_INT32(s, descriptor->sizel.cx);
Stream_Write_INT32(s, descriptor->sizel.cy);
Stream_Write_INT32(s, descriptor->pointl.x);
Stream_Write_INT32(s, descriptor->pointl.y);
Stream_Write_UINT32(s, descriptor->dwFileAttributes); /* fileAttributes (4 bytes) */
Stream_Write_UINT64(s, filetime_to_uint64(descriptor->ftCreationTime));
Stream_Write_UINT64(s, filetime_to_uint64(descriptor->ftLastAccessTime));
Stream_Write_UINT64(
s, filetime_to_uint64(descriptor->ftLastWriteTime)); /* lastWriteTime (8 bytes) */
Stream_Write_UINT32(s, descriptor->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
Stream_Write_UINT32(s, descriptor->nFileSizeLow); /* fileSizeLow (4 bytes) */
Stream_Write_UTF16_String(s, descriptor->cFileName,
ARRAYSIZE(descriptor->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 = nullptr;
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(nullptr, 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;
}

View File

@@ -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";
}
}

View File

@@ -0,0 +1,417 @@
/**
* 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;
static inline FOUR_BYTE_SIGNED_INTEGER FOUR_BYTE_SIGNED_INTEGER_init(void)
{
const FOUR_BYTE_SIGNED_INTEGER empty = {
.c = ONE_BYTE_VAL, .s = POSITIVE_VAL, .val1 = 0, .val2 = 0, .val3 = 0, .val4 = 0
};
return empty;
}
static inline FOUR_BYTE_FLOAT FOUR_BYTE_FLOAT_init(void)
{
const FOUR_BYTE_FLOAT empty = {
.c = ONE_BYTE_VAL, .s = POSITIVE_VAL, .e = 0, .val1 = 0, .val2 = 0, .val3 = 0, .val4 = 0
};
return empty;
}
BOOL freerdp_read_four_byte_signed_integer(wStream* s, INT32* value)
{
FOUR_BYTE_SIGNED_INTEGER si = FOUR_BYTE_SIGNED_INTEGER_init();
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 = (EncodedTypeByteCount)((byte & 0xC0) >> 6);
si.s = (EncodedTypeSign)((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_write_four_byte_signed_integer(wStream* s, INT32 value)
{
FOUR_BYTE_SIGNED_INTEGER si = FOUR_BYTE_SIGNED_INTEGER_init();
WINPR_ASSERT(s);
if (value > FREERDP_FOUR_BYTE_SIGNED_INT_MAX)
return FALSE;
if (value < FREERDP_FOUR_BYTE_SIGNED_INT_MIN)
return FALSE;
if (value < 0)
{
si.s = NEGATIVE_VAL;
value = -value;
}
if (value <= 0x1F)
{
si.c = ONE_BYTE_VAL;
si.val1 = value & 0x1f;
}
else if (value <= 0x1FFF)
{
si.c = TWO_BYTE_VAL;
si.val1 = (value >> 8) & 0x1f;
si.val2 = value & 0xff;
}
else if (value <= 0x1FFFFF)
{
si.c = THREE_BYTE_VAL;
si.val1 = (value >> 16) & 0x1f;
si.val2 = (value >> 8) & 0xff;
si.val3 = value & 0xff;
}
else if (value <= 0x1FFFFFFF)
{
si.c = FOUR_BYTE_VAL;
si.val1 = (value >> 24) & 0x1f;
si.val2 = (value >> 16) & 0xff;
si.val3 = (value >> 8) & 0xff;
si.val4 = value & 0xff;
}
else
{
WLog_ERR(TAG, "Invalid byte count for value %" PRId32, value);
return FALSE;
}
if (!Stream_EnsureRemainingCapacity(s, si.c + 1))
return FALSE;
const BYTE byte = ((si.c << 6) & 0xC0) | ((si.s << 5) & 0x20) | (si.val1 & 0x1F);
Stream_Write_UINT8(s, byte);
switch (si.c)
{
case ONE_BYTE_VAL:
break;
case TWO_BYTE_VAL:
Stream_Write_UINT8(s, si.val2);
break;
case THREE_BYTE_VAL:
Stream_Write_UINT8(s, si.val2);
Stream_Write_UINT8(s, si.val3);
break;
case FOUR_BYTE_VAL:
Stream_Write_UINT8(s, si.val2);
Stream_Write_UINT8(s, si.val3);
Stream_Write_UINT8(s, 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;
}
return TRUE;
}
BOOL freerdp_read_four_byte_float(wStream* s, double* value)
{
return freerdp_read_four_byte_float_exp(s, value, nullptr);
}
BOOL freerdp_read_four_byte_float_exp(wStream* s, double* value, BYTE* exp)
{
FOUR_BYTE_FLOAT f = FOUR_BYTE_FLOAT_init();
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 = (EncodedTypeByteCount)((byte & 0xC0) >> 6);
f.s = (EncodedTypeSign)((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;
if (exp)
*exp = f.e;
return TRUE;
}
BOOL freerdp_write_four_byte_float(wStream* s, double value)
{
FOUR_BYTE_FLOAT si = FOUR_BYTE_FLOAT_init();
WINPR_ASSERT(s);
if (value > FREERDP_FOUR_BYTE_FLOAT_MAX)
return FALSE;
if (value < FREERDP_FOUR_BYTE_FLOAT_MIN)
return FALSE;
if (value < 0)
{
si.s = NEGATIVE_VAL;
value = -value;
}
int exp = 0;
double ival = FP_NAN;
const double aval = fabs(value);
const double frac = modf(aval, &ival);
if (frac != 0.0)
{
const double maxfrac = frac * 10000000.0;
if (maxfrac <= 1.0)
exp = 0;
else if (maxfrac <= 10.0)
exp = 1;
else if (maxfrac <= 100.0)
exp = 2;
else if (maxfrac <= 1000.0)
exp = 3;
else if (maxfrac <= 10000.0)
exp = 4;
else if (maxfrac <= 100000.0)
exp = 5;
else if (maxfrac <= 1000000.0)
exp = 6;
else
exp = 7;
}
UINT64 base = (UINT64)llround(aval);
while (exp >= 0)
{
const double div = pow(10.0, exp);
const double dval = (aval * div);
base = (UINT64)dval;
if (base <= 0x03ffffff)
break;
exp--;
}
if (exp < 0)
return FALSE;
si.e = (BYTE)exp;
if (base <= 0x03)
{
si.c = ONE_BYTE_VAL;
si.val1 = base & 0x03;
}
else if (base <= 0x03ff)
{
si.c = TWO_BYTE_VAL;
si.val1 = (base >> 8) & 0x03;
si.val2 = base & 0xff;
}
else if (base <= 0x03ffff)
{
si.c = THREE_BYTE_VAL;
si.val1 = (base >> 16) & 0x03;
si.val2 = (base >> 8) & 0xff;
si.val3 = base & 0xff;
}
else if (base <= 0x03ffffff)
{
si.c = FOUR_BYTE_VAL;
si.val1 = (base >> 24) & 0x03;
si.val2 = (base >> 16) & 0xff;
si.val3 = (base >> 8) & 0xff;
si.val4 = base & 0xff;
}
else
{
WLog_ERR(TAG, "Invalid byte count for value %lf", value);
return FALSE;
}
if (!Stream_EnsureRemainingCapacity(s, si.c + 1))
return FALSE;
const BYTE byte =
((si.c << 6) & 0xC0) | ((si.s << 5) & 0x20) | ((si.e << 2) & 0x1c) | (si.val1 & 0x03);
Stream_Write_UINT8(s, byte);
switch (si.c)
{
case ONE_BYTE_VAL:
break;
case TWO_BYTE_VAL:
Stream_Write_UINT8(s, si.val2);
break;
case THREE_BYTE_VAL:
Stream_Write_UINT8(s, si.val2);
Stream_Write_UINT8(s, si.val3);
break;
case FOUR_BYTE_VAL:
Stream_Write_UINT8(s, si.val2);
Stream_Write_UINT8(s, si.val3);
Stream_Write_UINT8(s, 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;
}
return TRUE;
}

View File

@@ -0,0 +1,97 @@
/**
* 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";
default:
break;
}
return "RDPGFX_CODECID_UNKNOWN";
}

View File

@@ -0,0 +1,308 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* common helper utilities
*
* Copyright 2024 Armin Novak <anovak@thincast.com>
* Copyright 2024 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 <ctype.h>
#include <freerdp/utils/helpers.h>
#include <winpr/path.h>
#include <winpr/file.h>
#include <winpr/build-config.h>
#include <freerdp/version.h>
#include <freerdp/build-config.h>
#include "../core/utils.h"
static INIT_ONCE s_freerdp_app_details_once = INIT_ONCE_STATIC_INIT;
static char s_freerdp_vendor_string[MAX_PATH] = WINPR_C_ARRAY_INIT;
static char s_freerdp_product_string[MAX_PATH] = WINPR_C_ARRAY_INIT;
static char s_freerdp_details_string[3ull * MAX_PATH] = WINPR_C_ARRAY_INIT;
static WCHAR s_freerdp_details_string_w[3ull * MAX_PATH] = WINPR_C_ARRAY_INIT;
static SSIZE_T s_freerdp_version = -1;
static BOOL s_freerdp_app_details_are_custom = FALSE;
static void updateDetailsString(void)
{
const char* vendor = s_freerdp_vendor_string;
const char* product = s_freerdp_product_string;
const SSIZE_T version = s_freerdp_version;
WINPR_ASSERT(vendor);
WINPR_ASSERT(product);
if (s_freerdp_app_details_are_custom)
{
if (version < 0)
(void)_snprintf(s_freerdp_details_string, sizeof(s_freerdp_details_string) - 1, "%s-%s",
vendor, product);
else
(void)_snprintf(s_freerdp_details_string, sizeof(s_freerdp_details_string) - 1,
"%s-%s%" PRIdz, vendor, product, version);
}
else if (version < 0)
{
(void)_snprintf(s_freerdp_details_string, sizeof(s_freerdp_details_string) - 1, "%s",
product);
}
else
(void)_snprintf(s_freerdp_details_string, sizeof(s_freerdp_details_string) - 1, "%s%" PRIdz,
product, version);
(void)ConvertUtf8NToWChar(s_freerdp_details_string, sizeof(s_freerdp_details_string),
s_freerdp_details_string_w, sizeof(s_freerdp_details_string_w) - 1);
}
static BOOL CALLBACK init_app_details(WINPR_ATTR_UNUSED PINIT_ONCE once,
WINPR_ATTR_UNUSED PVOID param,
WINPR_ATTR_UNUSED PVOID* context)
{
const size_t vlen = sizeof(FREERDP_VENDOR_STRING);
const size_t plen = sizeof(FREERDP_PRODUCT_STRING);
const char* rvlen = strncpy(s_freerdp_vendor_string, FREERDP_VENDOR_STRING, vlen);
const char* rplen = strncpy(s_freerdp_product_string, FREERDP_PRODUCT_STRING, plen);
if (!rvlen || !rplen)
return FALSE;
#if defined(WITH_RESOURCE_VERSIONING)
s_freerdp_version = FREERDP_VERSION_MAJOR;
#else
s_freerdp_version = -1;
#endif
updateDetailsString();
return TRUE;
}
WINPR_ATTR_NODISCARD
static BOOL initializeApplicationDetails(void)
{
return InitOnceExecuteOnce(&s_freerdp_app_details_once, init_app_details, nullptr, nullptr);
}
BOOL freerdp_setApplicationDetails(const char* vendor, const char* product, SSIZE_T version)
{
if (!initializeApplicationDetails())
return -1;
if (!vendor || !product)
return FALSE;
const size_t vlen = strnlen(vendor, MAX_PATH);
const size_t plen = strnlen(product, MAX_PATH);
if ((vlen == MAX_PATH) || (plen == MAX_PATH))
return FALSE;
if (!strncpy(s_freerdp_vendor_string, vendor, vlen + 1))
return FALSE;
if (!strncpy(s_freerdp_product_string, product, plen + 1))
return FALSE;
s_freerdp_version = version;
s_freerdp_app_details_are_custom = TRUE;
const char separator = PathGetSeparatorA(PATH_STYLE_NATIVE);
char* str = freerdp_getApplicatonDetailsCombined(separator);
if (!str)
return FALSE;
const BOOL rc = winpr_setApplicationDetails(str, "WinPR", -1);
free(str);
updateDetailsString();
return rc;
}
const char* freerdp_getApplicationDetailsVendor(void)
{
if (!initializeApplicationDetails())
return nullptr;
return s_freerdp_vendor_string;
}
const char* freerdp_getApplicationDetailsProduct(void)
{
if (!initializeApplicationDetails())
return nullptr;
return s_freerdp_product_string;
}
char* freerdp_getApplicatonDetailsRegKey(const char* fmt)
{
char* val = freerdp_getApplicatonDetailsCombined('\\');
if (!val)
return nullptr;
char* str = nullptr;
size_t slen = 0;
(void)winpr_asprintf(&str, &slen, fmt, val);
free(val);
return str;
}
char* freerdp_getApplicatonDetailsCombined(char separator)
{
const SSIZE_T version = freerdp_getApplicationDetailsVersion();
const char* vendor = freerdp_getApplicationDetailsVendor();
const char* product = freerdp_getApplicationDetailsProduct();
size_t slen = 0;
char* str = nullptr;
if (version < 0)
{
(void)winpr_asprintf(&str, &slen, "%s%c%s", vendor, separator, product);
}
else
{
(void)winpr_asprintf(&str, &slen, "%s%c%s%" PRIdz, vendor, separator, product, version);
}
return str;
}
SSIZE_T freerdp_getApplicationDetailsVersion(void)
{
if (!initializeApplicationDetails())
return -1;
return s_freerdp_version;
}
const char* freerdp_getApplicationDetailsString(void)
{
return s_freerdp_details_string;
}
const WCHAR* freerdp_getApplicationDetailsStringW(void)
{
return s_freerdp_details_string_w;
}
BOOL freerdp_areApplicationDetailsCustomized(void)
{
return s_freerdp_app_details_are_custom;
}
#if !defined(WITH_FULL_CONFIG_PATH)
WINPR_ATTR_MALLOC(free, 1)
WINPR_ATTR_NODISCARD
static char* freerdp_settings_get_legacy_config_path(const char* filename)
{
char product[sizeof(FREERDP_PRODUCT_STRING)] = WINPR_C_ARRAY_INIT;
for (size_t i = 0; i < sizeof(product); i++)
product[i] = (char)tolower(FREERDP_PRODUCT_STRING[i]);
char* path = GetKnownSubPath(KNOWN_PATH_XDG_CONFIG_HOME, product);
if (!path)
return nullptr;
char* filepath = GetCombinedPath(path, filename);
free(path);
return filepath;
}
#endif
WINPR_ATTR_NODISCARD
WINPR_ATTR_MALLOC(free, 1) static char* getCustomConfigPath(BOOL system, const char* filename)
{
eKnownPathTypes id = system ? KNOWN_PATH_SYSTEM_CONFIG_HOME : KNOWN_PATH_XDG_CONFIG_HOME;
const char* vendor = freerdp_getApplicationDetailsVendor();
const char* product = freerdp_getApplicationDetailsProduct();
const SSIZE_T version = freerdp_getApplicationDetailsVersion();
if (!vendor || !product)
return nullptr;
char* config = GetKnownSubPathV(id, "%s", vendor);
if (!config)
return nullptr;
char* base = nullptr;
if (version < 0)
base = GetCombinedPathV(config, "%s", product);
else
base = GetCombinedPathV(config, "%s%" PRIdz, product, version);
free(config);
if (!base)
return nullptr;
if (!filename)
return base;
char* path = GetCombinedPathV(base, "%s", filename);
free(base);
return path;
}
char* freerdp_GetConfigFilePath(BOOL system, const char* filename)
{
#if defined(FREERDP_USE_VENDOR_PRODUCT_CONFIG_DIR)
const BOOL customized = TRUE;
#else
const BOOL customized = freerdp_areApplicationDetailsCustomized();
#endif
if (customized)
return getCustomConfigPath(system, filename);
eKnownPathTypes id = system ? KNOWN_PATH_SYSTEM_CONFIG_HOME : KNOWN_PATH_XDG_CONFIG_HOME;
const char* vendor = freerdp_getApplicationDetailsVendor();
const char* product = freerdp_getApplicationDetailsProduct();
const SSIZE_T version = freerdp_getApplicationDetailsVersion();
if (!vendor || !product)
return nullptr;
#if !defined(WITH_FULL_CONFIG_PATH)
if (!system && (_stricmp(vendor, product) == 0))
return freerdp_settings_get_legacy_config_path(filename);
#endif
char* config = GetKnownPath(id);
if (!config)
return nullptr;
char* base = nullptr;
if (version < 0)
base = GetCombinedPathV(config, "%s", product);
else
base = GetCombinedPathV(config, "%s%" PRIdz, product, version);
free(config);
if (!base)
return nullptr;
if (!filename)
return base;
char* path = GetCombinedPathV(base, "%s", filename);
free(base);
return path;
}
WINPR_JSON* freerdp_GetJSONConfigFile(BOOL system, const char* filename)
{
char* path = freerdp_GetConfigFilePath(system, filename);
if (!path)
return nullptr;
WINPR_JSON* json = WINPR_JSON_ParseFromFile(path);
free(path);
return json;
}

View File

@@ -0,0 +1,420 @@
/**
* 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_PrintTextMessage(log, level, line, file, fkt, "%s: %s", msg,
ERR_error_string(ec, nullptr));
}
if (!error_logged)
WLog_PrintTextMessage(log, 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
if (size > INT32_MAX)
return -1;
return BIO_get_line(bio, buffer, (int)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 = nullptr;
const char* path = nullptr;
char* headers = nullptr;
size_t size = 0;
int status = 0;
char buffer[1024] = WINPR_C_ARRAY_INIT;
BIO* bio = nullptr;
SSL_CTX* ssl_ctx = nullptr;
SSL* ssl = nullptr;
WINPR_ASSERT(status_code);
WINPR_ASSERT(response);
WINPR_ASSERT(response_length);
wLog* log = WLog_Get(TAG);
WINPR_ASSERT(log);
*response = nullptr;
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 = WINPR_ASSERTING_INT_CAST(size_t, 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)
{
free(hostname);
return FALSE;
}
}
else
{
if (winpr_asprintf(&headers, &size, get_header_fmt, path, hostname) < 0)
{
free(hostname);
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();
{
const size_t hlen = strnlen(headers, size);
if (hlen > INT32_MAX)
goto out;
if (BIO_write(bio, headers, (int)hlen) < 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, (int)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;
}
const char header[9] = { 'H', 'T', 'T', 'P', '/', '1', '.', '1', ' ' };
if ((status < (INT64)sizeof(header)) || (strncmp(header, buffer, sizeof(header)) != 0))
{
WLog_Print(log, WLOG_ERROR, "invalid HTTP status header");
goto out;
}
errno = 0;
*status_code = strtol(&buffer[sizeof(header)], nullptr, 0);
if (errno != 0)
{
char ebuffer[256] = WINPR_C_ARRAY_INIT;
WLog_Print(log, WLOG_ERROR, "invalid HTTP status line: %s [%d]",
winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
goto out;
}
do
{
status = get_line(bio, buffer, sizeof(buffer));
if (status <= 0)
{
log_errors(log, "could not read response");
goto out;
}
char* val = nullptr;
char* name = strtok_s(buffer, ":", &val);
if (name && (_stricmp(name, "content-length") == 0))
{
errno = 0;
*response_length = strtoul(val, nullptr, 10);
if (errno)
{
char ebuffer[256] = WINPR_C_ARRAY_INIT;
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;
size_t left = *response_length;
while (left > 0)
{
const int rd = (left < INT32_MAX) ? (int)left : INT32_MAX;
status = BIO_read(bio, p, rd);
if (status <= 0)
{
log_errors(log, "could not read response");
goto out;
}
p += status;
if ((size_t)status > left)
break;
left -= (size_t)status;
}
}
WLog_Print(log, WLOG_DEBUG, "response[%" PRIuz "]:\n%s", *response_length,
(const char*)(*response));
ret = TRUE;
out:
if (!ret)
{
free(*response);
*response = nullptr;
*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";
}
}
const char* freerdp_http_status_string_format(long status, char* buffer, size_t size)
{
const char* code = freerdp_http_status_string(status);
(void)_snprintf(buffer, size, "%s [%ld]", code, status);
return buffer;
}

View File

@@ -0,0 +1,493 @@
/**
* 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 <winpr/environment.h>
#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);
}
const 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] = WINPR_C_ARRAY_INIT;
BOOL fSave = FALSE;
DWORD dwFlags = 0;
WCHAR* promptW = ConvertUtf8ToWCharAlloc(prompt, nullptr);
const DWORD status =
CredUICmdLinePromptForCredentialsW(promptW, nullptr, 0, UserNameW, ARRAYSIZE(UserNameW),
PasswordW, ARRAYSIZE(PasswordW), &fSave, dwFlags);
free(promptW);
if (ConvertWCharNToUtf8(PasswordW, ARRAYSIZE(PasswordW), buf, bufsiz) < 0)
return nullptr;
return buf;
}
#elif !defined(ANDROID)
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
#include <freerdp/utils/signal.h>
#include <freerdp/log.h>
#if defined(WINPR_HAVE_POLL_H) && !defined(__APPLE__)
#include <poll.h>
#else
#include <time.h>
#include <sys/select.h>
#endif
#define TAG FREERDP_TAG("utils.passphrase")
static int wait_for_fd(int fd, int timeout)
{
int status = 0;
#if defined(WINPR_HAVE_POLL_H) && !defined(__APPLE__)
struct pollfd pollset = WINPR_C_ARRAY_INIT;
pollset.fd = fd;
pollset.events = POLLIN;
pollset.revents = 0;
do
{
status = poll(&pollset, 1, timeout);
} while ((status < 0) && (errno == EINTR));
#else
fd_set rset = WINPR_C_ARRAY_INIT;
struct timeval tv = WINPR_C_ARRAY_INIT;
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, nullptr, nullptr, timeout ? &tv : nullptr);
} while ((status < 0) && (errno == EINTR));
#endif
return status;
}
static void replace_char(char* buffer, WINPR_ATTR_UNUSED size_t buffer_len, const char* toreplace)
{
while (*toreplace != '\0')
{
char* ptr = nullptr;
while ((ptr = strrchr(buffer, *toreplace)) != nullptr)
*ptr = '\0';
toreplace++;
}
}
static const char* freerdp_passphrase_read_tty(rdpContext* context, const char* prompt, char* buf,
size_t bufsiz, int from_stdin)
{
BOOL terminal_needs_reset = FALSE;
char term_name[L_ctermid] = WINPR_C_ARRAY_INIT;
FILE* fout = nullptr;
if (bufsiz == 0)
{
errno = EINVAL;
return nullptr;
}
ctermid(term_name);
int terminal_fildes = 0;
if (from_stdin || (strcmp(term_name, "") == 0))
{
fout = stdout;
terminal_fildes = STDIN_FILENO;
}
else
{
const int term_file = open(term_name, O_RDWR);
if (term_file < 0)
{
fout = stdout;
terminal_fildes = STDIN_FILENO;
}
else
{
fout = fdopen(term_file, "w");
if (!fout)
{
close(term_file);
return nullptr;
}
terminal_fildes = term_file;
}
}
struct termios orig_flags = WINPR_C_ARRAY_INIT;
if (tcgetattr(terminal_fildes, &orig_flags) != -1)
{
struct termios new_flags = WINPR_C_ARRAY_INIT;
new_flags = orig_flags;
new_flags.c_lflag &= (uint32_t)~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;
(void)fprintf(fout, "%s", prompt);
(void)fflush(fout);
{
char* ptr = nullptr;
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)
(void)fclose(fp);
return buf;
error:
{
// NOLINTNEXTLINE(clang-analyzer-unix.Stream)
int saved_errno = errno;
if (terminal_needs_reset)
(void)tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags);
if (terminal_fildes != STDIN_FILENO)
{
if (fp)
(void)fclose(fp);
}
// NOLINTNEXTLINE(clang-analyzer-unix.Stream)
errno = saved_errno;
}
return nullptr;
}
static const char* freerdp_passphrase_read_askpass(const char* prompt, char* buf, size_t bufsiz,
char const* askpass_env)
{
char command[4096] = WINPR_C_ARRAY_INIT;
(void)sprintf_s(command, sizeof(command), "%s 'FreeRDP authentication\n%s'", askpass_env,
prompt);
// NOLINTNEXTLINE(clang-analyzer-optin.taint.GenericTaint)
FILE* askproc = popen(command, "r");
if (!askproc)
return nullptr;
WINPR_ASSERT(bufsiz <= INT32_MAX);
if (fgets(buf, (int)bufsiz, askproc) != nullptr)
buf[strcspn(buf, "\r\n")] = '\0';
else
buf = nullptr;
const int status = pclose(askproc);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
buf = nullptr;
return buf;
}
const char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf,
size_t bufsiz, int from_stdin)
{
// NOLINTNEXTLINE(concurrency-mt-unsafe)
const char* askpass_env = getenv("FREERDP_ASKPASS");
if (askpass_env)
return freerdp_passphrase_read_askpass(prompt, buf, bufsiz, askpass_env);
else
return freerdp_passphrase_read_tty(context, prompt, buf, bufsiz, from_stdin);
}
static BOOL set_termianl_nonblock(int ifd, BOOL nonblock);
static void restore_terminal(void)
{
(void)set_termianl_nonblock(-1, FALSE);
}
BOOL set_termianl_nonblock(int ifd, BOOL nonblock)
{
static int fd = -1;
static bool registered = false;
static int orig = 0;
static struct termios termios = WINPR_C_ARRAY_INIT;
if (ifd >= 0)
fd = ifd;
if (fd < 0)
return FALSE;
if (nonblock)
{
if (!registered)
{
(void)atexit(restore_terminal);
registered = true;
}
const int rc1 = fcntl(fd, F_SETFL, orig | O_NONBLOCK);
if (rc1 != 0)
{
char buffer[128] = WINPR_C_ARRAY_INIT;
WLog_ERR(TAG, "fcntl(F_SETFL) failed with %s",
winpr_strerror(errno, buffer, sizeof(buffer)));
return FALSE;
}
const int rc2 = tcgetattr(fd, &termios);
if (rc2 != 0)
{
char buffer[128] = WINPR_C_ARRAY_INIT;
WLog_ERR(TAG, "tcgetattr() failed with %s",
winpr_strerror(errno, buffer, sizeof(buffer)));
return FALSE;
}
struct termios now = termios;
cfmakeraw(&now);
const int rc3 = tcsetattr(fd, TCSANOW, &now);
if (rc3 != 0)
{
char buffer[128] = WINPR_C_ARRAY_INIT;
WLog_ERR(TAG, "tcsetattr(TCSANOW) failed with %s",
winpr_strerror(errno, buffer, sizeof(buffer)));
return FALSE;
}
}
else
{
const int rc1 = tcsetattr(fd, TCSANOW, &termios);
if (rc1 != 0)
{
char buffer[128] = WINPR_C_ARRAY_INIT;
WLog_ERR(TAG, "tcsetattr(TCSANOW) failed with %s",
winpr_strerror(errno, buffer, sizeof(buffer)));
return FALSE;
}
const int rc2 = fcntl(fd, F_SETFL, orig);
if (rc2 != 0)
{
char buffer[128] = WINPR_C_ARRAY_INIT;
WLog_ERR(TAG, "fcntl(F_SETFL) failed with %s",
winpr_strerror(errno, buffer, sizeof(buffer)));
return FALSE;
}
fd = -1;
}
return TRUE;
}
int freerdp_interruptible_getc(rdpContext* context, FILE* stream)
{
int rc = EOF;
const int fd = fileno(stream);
(void)set_termianl_nonblock(fd, TRUE);
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)
{
if (c == 3) /* ctrl + c */
return EOF;
if (c == 4) /* ctrl + d */
return EOF;
if (c == 26) /* ctrl + z */
return EOF;
rc = (int)c;
}
break;
}
} while (!freerdp_shall_disconnect_context(context));
(void)set_termianl_nonblock(fd, FALSE);
return rc;
}
#else
const char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf,
size_t bufsiz, int from_stdin)
{
return nullptr;
}
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 = nullptr;
size_t step = 32;
size_t used = 0;
char* ptr = nullptr;
size_t len = 0;
if (!plineptr || !psize)
{
errno = EINVAL;
return -1;
}
bool echo = true;
#if !defined(_WIN32) && !defined(ANDROID)
{
const int fd = fileno(stream);
struct termios termios = WINPR_C_ARRAY_INIT;
/* This might fail if /from-stdin is used. */
if (tcgetattr(fd, &termios) == 0)
echo = (termios.c_lflag & ECHO) != 0;
else
echo = false;
}
#endif
if (*plineptr && (*psize > 0))
{
ptr = *plineptr;
used = *psize;
if (echo)
{
printf("%s", ptr);
(void)fflush(stdout);
}
}
do
{
if (used + 2 >= len)
{
len += step;
n = realloc(ptr, len);
if (!n)
{
free(ptr);
*plineptr = nullptr;
return -1;
}
ptr = n;
}
c = freerdp_interruptible_getc(context, stream);
if (c == 127)
{
if (used > 0)
{
ptr[used--] = '\0';
if (echo)
{
printf("\b");
printf(" ");
printf("\b");
(void)fflush(stdout);
}
}
continue;
}
if (echo)
{
printf("%c", c);
(void)fflush(stdout);
}
if (c != EOF)
ptr[used++] = (char)c;
} while ((c != '\n') && (c != '\r') && (c != EOF));
printf("\n");
ptr[used] = '\0';
if (c == EOF)
{
free(ptr);
*plineptr = nullptr;
return EOF;
}
*plineptr = ptr;
*psize = used;
return WINPR_ASSERTING_INT_CAST(SSIZE_T, used);
}

View File

@@ -0,0 +1,260 @@
/**
* 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 <winpr/sysinfo.h>
#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 = nullptr;
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)
{
WINPR_ASSERT(pcap);
WINPR_ASSERT(data || (length == 0));
WINPR_ASSERT(length <= UINT32_MAX);
pcap_record* record = (pcap_record*)calloc(1, sizeof(pcap_record));
if (!record)
return FALSE;
record->cdata = data;
record->length = (UINT32)length;
record->header.incl_len = (UINT32)length;
record->header.orig_len = (UINT32)length;
const UINT64 ns = winpr_GetUnixTimeNS();
record->header.ts_sec = (UINT32)WINPR_TIME_NS_TO_S(ns);
record->header.ts_usec = (UINT32)WINPR_TIME_NS_REM_US(ns);
if (pcap->tail == nullptr)
{
pcap->tail = record;
if (!pcap->tail)
return FALSE;
pcap->head = pcap->tail;
}
else
{
record->next = pcap->tail;
pcap->tail = record;
}
if (pcap->record == nullptr)
pcap->record = record;
return TRUE;
}
BOOL pcap_has_next_record(const rdpPcap* pcap)
{
WINPR_ASSERT(pcap);
return (pcap->file_size - (_ftelli64(pcap->fp)) > 16);
}
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)
{
WINPR_ASSERT(name);
rdpPcap* 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 == nullptr)
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
{
(void)_fseeki64(pcap->fp, 0, SEEK_END);
pcap->file_size = _ftelli64(pcap->fp);
(void)_fseeki64(pcap->fp, 0, SEEK_SET);
if (!pcap_read_header(pcap, &pcap->header))
goto fail;
}
return pcap;
fail:
pcap_close(pcap);
return nullptr;
}
void pcap_flush(rdpPcap* pcap)
{
WINPR_ASSERT(pcap);
while (pcap->record != nullptr)
{
(void)pcap_write_record(pcap, pcap->record);
pcap->record = pcap->record->next;
}
if (pcap->fp != nullptr)
(void)fflush(pcap->fp);
}
void pcap_close(rdpPcap* pcap)
{
if (!pcap)
return;
pcap_flush(pcap);
if (pcap->fp != nullptr)
(void)fclose(pcap->fp);
free(pcap->name);
free(pcap);
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include <winpr/wtypes.h>
extern BOOL fsig_handlers_registered;
void fsig_lock(void);
void fsig_unlock(void);
void fsig_term_handler(int signum);

View File

@@ -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 nullptr;
profiler->name = _strdup(name);
profiler->stopwatch = stopwatch_create();
if (!profiler->name || !profiler->stopwatch)
goto fail;
return profiler;
fail:
profiler_free(profiler);
return nullptr;
}
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,
"-------------------------------+------------+-------------+-----------+-------");
}

View File

@@ -0,0 +1,689 @@
/**
* 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>
LONG scard_log_status_error(const char* tag, const char* what, LONG status)
{
wLog* log = WLog_Get(tag);
return scard_log_status_error_wlog(log, what, status);
}
LONG scard_log_status_error_wlog(wLog* log, 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(log, 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,
NTSTATUS ioStatus)
{
WINPR_ASSERT(out);
Stream_ResetPosition(out);
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_INT32(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_ResetPosition(s);
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] = WINPR_C_ARRAY_INIT;
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
(void)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 = WINPR_C_ARRAY_INIT;
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 = WINPR_C_ARRAY_INIT;
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* out, const char* custom)
{
rdpdr_dump_packet(log, lvl, out, custom, FALSE);
}
void rdpdr_dump_send_packet(wLog* log, DWORD lvl, wStream* out, const char* custom)
{
rdpdr_dump_packet(log, lvl, out, 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;
}
const char* rdpdr_irp_val2str(UINT32 ioCode1)
{
switch (ioCode1)
{
case RDPDR_IRP_MJ_CREATE:
return "MJ_CREATE";
case RDPDR_IRP_MJ_CLEANUP:
return "MJ_CLEANUP";
case RDPDR_IRP_MJ_CLOSE:
return "MJ_CLOSE";
case RDPDR_IRP_MJ_READ:
return "MJ_READ";
case RDPDR_IRP_MJ_WRITE:
return "MJ_WRITE";
case RDPDR_IRP_MJ_FLUSH_BUFFERS:
return "MJ_FLUSH_BUFFERS";
case RDPDR_IRP_MJ_SHUTDOWN:
return "MJ_SHUTDOWN";
case RDPDR_IRP_MJ_DEVICE_CONTROL:
return "MJ_DEVICE_CONTROL";
case RDPDR_IRP_MJ_QUERY_VOLUME_INFORMATION:
return "MJ_QUERY_VOLUME_INFORMATION";
case RDPDR_IRP_MJ_SET_VOLUME_INFORMATION:
return "MJ_SET_VOLUME_INFORMATION";
case RDPDR_IRP_MJ_QUERY_INFORMATION:
return "MJ_QUERY_INFORMATION";
case RDPDR_IRP_MJ_SET_INFORMATION:
return "MJ_SET_INFORMATION";
case RDPDR_IRP_MJ_DIRECTORY_CONTROL:
return "MJ_DIRECTORY_CONTROL";
case RDPDR_IRP_MJ_LOCK_CONTROL:
return "MJ_LOCK_CONTROL";
case RDPDR_IRP_MJ_QUERY_SECURITY:
return "MJ_QUERY_SECURITY";
case RDPDR_IRP_MJ_SET_SECURITY:
return "MJ_SET_SECURITY";
default:
return "IRP_MJ_UNKNOWN";
}
}
const char* rdpdr_irp_mask2str(UINT32 ioCode1Mask, char* buffer, size_t len)
{
if (len < 1)
return nullptr;
if (!winpr_str_append("{", buffer, len, nullptr))
return nullptr;
for (size_t x = 0; x < 32; x++)
{
const UINT32 mask = (1u << x);
if (ioCode1Mask & mask)
{
if (!winpr_str_append(rdpdr_irp_val2str(mask), &buffer[1], len - 1, "|"))
return nullptr;
}
}
char number[16] = WINPR_C_ARRAY_INIT;
(void)_snprintf(number, sizeof(number), "}[0x%08" PRIx32 "]", ioCode1Mask);
if (!winpr_str_append(number, buffer, len, nullptr))
return nullptr;
return buffer;
}
const char* rdpdr_device_type_string(UINT32 type)
{
switch (type)
{
case RDPDR_DTYP_SERIAL:
return "serial";
case RDPDR_DTYP_PRINT:
return "printer";
case RDPDR_DTYP_FILESYSTEM:
return "drive";
case RDPDR_DTYP_SMARTCARD:
return "smartcard";
case RDPDR_DTYP_PARALLEL:
return "parallel";
default:
return "UNKNOWN";
}
}

View File

@@ -0,0 +1,311 @@
/**
* 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>
#ifdef WITH_DEBUG_RINGBUFFER
#define TAG FREERDP_TAG("utils.ringbuffer")
#define DEBUG_RINGBUFFER(...) WLog_DBG(TAG, __VA_ARGS__)
#else
#define DEBUG_RINGBUFFER(...) \
do \
{ \
} while (0)
#endif
BOOL ringbuffer_init(RingBuffer* ringbuffer, size_t initialSize)
{
WINPR_ASSERT(ringbuffer);
ringbuffer->buffer = malloc(initialSize);
if (!ringbuffer->buffer)
return FALSE;
ringbuffer->readPtr = ringbuffer->writePtr = 0;
ringbuffer->initialSize = ringbuffer->size = ringbuffer->freeSize = initialSize;
DEBUG_RINGBUFFER("ringbuffer_init(%p)", (void*)rb);
return TRUE;
}
size_t ringbuffer_used(const RingBuffer* ringbuffer)
{
WINPR_ASSERT(ringbuffer);
return ringbuffer->size - ringbuffer->freeSize;
}
size_t ringbuffer_capacity(const RingBuffer* ringbuffer)
{
WINPR_ASSERT(ringbuffer);
return ringbuffer->size;
}
void ringbuffer_destroy(RingBuffer* ringbuffer)
{
DEBUG_RINGBUFFER("ringbuffer_destroy(%p)", (void*)ringbuffer);
if (!ringbuffer)
return;
free(ringbuffer->buffer);
ringbuffer->buffer = nullptr;
}
static BOOL ringbuffer_realloc(RingBuffer* ringbuffer, size_t targetSize)
{
WINPR_ASSERT(ringbuffer);
BYTE* newData = nullptr;
DEBUG_RINGBUFFER("ringbuffer_realloc(%p): targetSize: %" PRIdz "", (void*)rb, targetSize);
if (ringbuffer->writePtr == ringbuffer->readPtr)
{
/* when no size is used we can realloc() and set the heads at the
* beginning of the buffer
*/
newData = (BYTE*)realloc(ringbuffer->buffer, targetSize);
if (!newData)
return FALSE;
ringbuffer->readPtr = ringbuffer->writePtr = 0;
ringbuffer->buffer = newData;
}
else if ((ringbuffer->writePtr >= ringbuffer->readPtr) && (ringbuffer->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(ringbuffer->buffer, targetSize);
if (!newData)
return FALSE;
ringbuffer->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 (ringbuffer->readPtr < ringbuffer->writePtr)
{
/* readPtr writePtr
* | |
* v v
* [............|XXXXXXXXXXXXXX|..........]
*/
memcpy(newData, ringbuffer->buffer + ringbuffer->readPtr, ringbuffer_used(ringbuffer));
}
else
{
/* writePtr readPtr
* | |
* v v
* [XXXXXXXXXXXX|..............|XXXXXXXXXX]
*/
BYTE* dst = newData;
memcpy(dst, ringbuffer->buffer + ringbuffer->readPtr,
ringbuffer->size - ringbuffer->readPtr);
dst += (ringbuffer->size - ringbuffer->readPtr);
if (ringbuffer->writePtr)
memcpy(dst, ringbuffer->buffer, ringbuffer->writePtr);
}
ringbuffer->writePtr = ringbuffer->size - ringbuffer->freeSize;
ringbuffer->readPtr = 0;
free(ringbuffer->buffer);
ringbuffer->buffer = newData;
}
ringbuffer->freeSize += (targetSize - ringbuffer->size);
ringbuffer->size = targetSize;
return TRUE;
}
/**
* Write to a ringbuffer
*
* @param ringbuffer 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* ringbuffer, const BYTE* ptr, size_t sz)
{
size_t toWrite = 0;
size_t remaining = 0;
WINPR_ASSERT(ringbuffer);
DEBUG_RINGBUFFER("ringbuffer_write(%p): sz: %" PRIdz "", (void*)rb, sz);
if ((ringbuffer->freeSize <= sz) && !ringbuffer_realloc(ringbuffer, ringbuffer->size + sz))
return FALSE;
/* the write could be split in two
* readHead writeHead
* | |
* v v
* [ ################ ]
*/
toWrite = sz;
remaining = sz;
if (ringbuffer->size - ringbuffer->writePtr < sz)
toWrite = ringbuffer->size - ringbuffer->writePtr;
if (toWrite)
{
memcpy(ringbuffer->buffer + ringbuffer->writePtr, ptr, toWrite);
remaining -= toWrite;
ptr += toWrite;
}
if (remaining)
memcpy(ringbuffer->buffer, ptr, remaining);
ringbuffer->writePtr = (ringbuffer->writePtr + sz) % ringbuffer->size;
ringbuffer->freeSize -= sz;
return TRUE;
}
BYTE* ringbuffer_ensure_linear_write(RingBuffer* ringbuffer, size_t sz)
{
DEBUG_RINGBUFFER("ringbuffer_ensure_linear_write(%p): sz: %" PRIdz "", (void*)rb, sz);
WINPR_ASSERT(ringbuffer);
if (ringbuffer->freeSize < sz)
{
if (!ringbuffer_realloc(ringbuffer, ringbuffer->size + sz - ringbuffer->freeSize + 32))
return nullptr;
}
if (ringbuffer->writePtr == ringbuffer->readPtr)
{
ringbuffer->writePtr = ringbuffer->readPtr = 0;
}
if (ringbuffer->writePtr + sz < ringbuffer->size)
return ringbuffer->buffer + ringbuffer->writePtr;
/*
* to add: .......
* [ XXXXXXXXX ]
*
* result:
* [XXXXXXXXX....... ]
*/
memmove(ringbuffer->buffer, ringbuffer->buffer + ringbuffer->readPtr,
ringbuffer->writePtr - ringbuffer->readPtr);
ringbuffer->readPtr = 0;
ringbuffer->writePtr = ringbuffer->size - ringbuffer->freeSize;
return ringbuffer->buffer + ringbuffer->writePtr;
}
BOOL ringbuffer_commit_written_bytes(RingBuffer* ringbuffer, size_t sz)
{
DEBUG_RINGBUFFER("ringbuffer_commit_written_bytes(%p): sz: %" PRIdz "", (void*)rb, sz);
WINPR_ASSERT(ringbuffer);
if (sz < 1)
return TRUE;
if (ringbuffer->writePtr + sz > ringbuffer->size)
return FALSE;
ringbuffer->writePtr = (ringbuffer->writePtr + sz) % ringbuffer->size;
ringbuffer->freeSize -= sz;
return TRUE;
}
int ringbuffer_peek(const RingBuffer* ringbuffer, 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 "", (const void*)rb, sz);
WINPR_ASSERT(ringbuffer);
if (sz < 1)
return 0;
if ((ringbuffer->size - ringbuffer->freeSize) < sz)
remaining = ringbuffer->size - ringbuffer->freeSize;
toRead = remaining;
if ((ringbuffer->readPtr + remaining) > ringbuffer->size)
toRead = ringbuffer->size - ringbuffer->readPtr;
if (toRead)
{
chunks[0].data = ringbuffer->buffer + ringbuffer->readPtr;
chunks[0].size = toRead;
remaining -= toRead;
chunkIndex++;
status++;
}
if (remaining)
{
chunks[chunkIndex].data = ringbuffer->buffer;
chunks[chunkIndex].size = remaining;
status++;
}
return status;
}
void ringbuffer_commit_read_bytes(RingBuffer* ringbuffer, size_t sz)
{
DEBUG_RINGBUFFER("ringbuffer_commit_read_bytes(%p): sz: %" PRIdz "", (void*)ringbuffer, sz);
WINPR_ASSERT(ringbuffer);
if (sz < 1)
return;
WINPR_ASSERT(ringbuffer->size - ringbuffer->freeSize >= sz);
ringbuffer->readPtr = (ringbuffer->readPtr + sz) % ringbuffer->size;
ringbuffer->freeSize += sz;
/* when we reach a reasonable free size, we can go back to the original size */
if ((ringbuffer->size != ringbuffer->initialSize) &&
(ringbuffer_used(ringbuffer) < ringbuffer->initialSize / 2))
ringbuffer_realloc(ringbuffer, ringbuffer->initialSize);
}

View File

@@ -0,0 +1,123 @@
/**
* 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 <string.h>
#include <winpr/crt.h>
#include <winpr/debug.h>
#include <freerdp/utils/signal.h>
#include <freerdp/log.h>
#include "platform_signal.h"
#define TAG FREERDP_TAG("utils.signal")
#if defined(_WIN32)
const char* strsignal(int signum);
#endif
BOOL fsig_handlers_registered = FALSE;
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] = WINPR_C_ARRAY_INIT;
void fsig_term_handler(int signum)
{
static BOOL recursive = FALSE;
if (!recursive)
{
recursive = TRUE;
// NOLINTNEXTLINE(concurrency-mt-unsafe)
WLog_ERR(TAG, "Caught signal '%s' [%d]", strsignal(signum), signum);
}
fsig_lock();
for (size_t x = 0; x < cleanup_handler_count; x++)
{
const cleanup_handler_t empty = WINPR_C_ARRAY_INIT;
cleanup_handler_t* cur = &cleanup_handlers[x];
if (cur->handler)
{
// NOLINTNEXTLINE(concurrency-mt-unsafe)
cur->handler(signum, strsignal(signum), cur->context);
}
*cur = empty;
}
cleanup_handler_count = 0;
fsig_unlock();
}
BOOL freerdp_add_signal_cleanup_handler(void* context, freerdp_signal_handler_t handler)
{
BOOL rc = FALSE;
fsig_lock();
if (fsig_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;
fsig_unlock();
return rc;
}
BOOL freerdp_del_signal_cleanup_handler(void* context, freerdp_signal_handler_t handler)
{
BOOL rc = FALSE;
fsig_lock();
if (fsig_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 = WINPR_C_ARRAY_INIT;
for (size_t y = x + 1; y < cleanup_handler_count - 1; y++)
{
*cur++ = cleanup_handlers[y];
}
*cur = empty;
cleanup_handler_count--;
break;
}
}
}
rc = TRUE;
fsig_unlock();
return rc;
}

View File

@@ -0,0 +1,168 @@
#include <pthread.h>
#include <stddef.h>
#include <errno.h>
#include <signal.h>
#include <termios.h>
#include <winpr/wlog.h>
#include <winpr/debug.h>
#include <freerdp/log.h>
#include <freerdp/utils/signal.h>
#include "platform_signal.h"
#define TAG FREERDP_TAG("utils.signal.posix")
static pthread_mutex_t signal_handler_lock = PTHREAD_MUTEX_INITIALIZER;
void fsig_lock(void)
{
const int rc = pthread_mutex_lock(&signal_handler_lock);
if (rc != 0)
{
char ebuffer[256] = WINPR_C_ARRAY_INIT;
WLog_ERR(TAG, "[pthread_mutex_lock] failed with %s [%d]",
winpr_strerror(rc, ebuffer, sizeof(ebuffer)), rc);
}
}
void fsig_unlock(void)
{
const int rc = pthread_mutex_unlock(&signal_handler_lock);
if (rc != 0)
{
char ebuffer[256] = WINPR_C_ARRAY_INIT;
WLog_ERR(TAG, "[pthread_mutex_lock] failed with %s [%d]",
winpr_strerror(rc, ebuffer, sizeof(ebuffer)), rc);
}
}
static void fatal_handler(int signum)
{
struct sigaction default_sigaction;
sigset_t this_mask;
static BOOL recursive = FALSE;
if (!recursive)
{
recursive = TRUE;
// NOLINTNEXTLINE(concurrency-mt-unsafe)
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, nullptr);
sigemptyset(&this_mask);
sigaddset(&this_mask, signum);
pthread_sigmask(SIG_UNBLOCK, &this_mask, nullptr);
(void)raise(signum);
}
static const int term_signals[] = { SIGINT, SIGKILL, SIGQUIT, SIGSTOP, SIGTERM };
static const int fatal_signals[] = { SIGABRT, SIGALRM, SIGBUS, SIGFPE, SIGHUP,
SIGILL, SIGSEGV, SIGTTIN, SIGTTOU,
#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 = WINPR_C_ARRAY_INIT;
struct sigaction saction = WINPR_C_ARRAY_INIT;
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 = WINPR_C_ARRAY_INIT;
if (sigaction(signals[x], nullptr, &orig_sigaction) == 0)
{
if (orig_sigaction.sa_handler != SIG_IGN)
{
sigaction(signals[x], &saction, nullptr);
}
}
}
pthread_sigmask(SIG_SETMASK, &orig_set, nullptr);
return TRUE;
}
static void unregister_handlers(const int* signals, size_t count)
{
WINPR_ASSERT(signals || (count == 0));
sigset_t orig_set = WINPR_C_ARRAY_INIT;
struct sigaction saction = WINPR_C_ARRAY_INIT;
pthread_sigmask(SIG_BLOCK, &(saction.sa_mask), &orig_set);
sigfillset(&(saction.sa_mask));
sigdelset(&(saction.sa_mask), SIGCONT);
saction.sa_handler = SIG_IGN;
saction.sa_flags = 0;
for (size_t x = 0; x < count; x++)
{
sigaction(signals[x], &saction, nullptr);
}
pthread_sigmask(SIG_SETMASK, &orig_set, nullptr);
}
static void unregister_all_handlers(void)
{
unregister_handlers(fatal_signals, ARRAYSIZE(fatal_signals));
unregister_handlers(term_signals, ARRAYSIZE(term_signals));
}
int freerdp_handle_signals(void)
{
int rc = -1;
fsig_lock();
WLog_DBG(TAG, "Registering signal hook...");
(void)atexit(unregister_all_handlers);
if (!register_handlers(fatal_signals, ARRAYSIZE(fatal_signals), fatal_handler))
goto fail;
if (!register_handlers(term_signals, ARRAYSIZE(term_signals), fsig_term_handler))
goto fail;
/* Ignore SIGPIPE signal. */
(void)signal(SIGPIPE, SIG_IGN);
fsig_handlers_registered = TRUE;
rc = 0;
fail:
fsig_unlock();
return rc;
}

View File

@@ -0,0 +1,117 @@
#include <stddef.h>
#include <errno.h>
#include <signal.h>
#include <winpr/wlog.h>
#include <winpr/debug.h>
#include <winpr/assert.h>
#include <freerdp/log.h>
#include <freerdp/utils/signal.h>
#include "platform_signal.h"
#define TAG FREERDP_TAG("utils.signal.posix")
static CRITICAL_SECTION signal_lock;
void fsig_lock(void)
{
EnterCriticalSection(&signal_lock);
}
void fsig_unlock(void)
{
LeaveCriticalSection(&signal_lock);
}
const char* strsignal(int signum)
{
#define CASE_STR(x) \
case x: \
return #x
switch (signum)
{
CASE_STR(SIGINT);
CASE_STR(SIGILL);
CASE_STR(SIGFPE);
CASE_STR(SIGSEGV);
CASE_STR(SIGTERM);
CASE_STR(SIGBREAK);
CASE_STR(SIGABRT);
CASE_STR(SIGABRT_COMPAT);
default:
return "SIG_UNKNOWN";
}
}
static void fatal_handler(int signum)
{
static BOOL recursive = FALSE;
if (!recursive)
{
recursive = TRUE;
// NOLINTNEXTLINE(concuSWSrrency-mt-unsafe)
WLog_ERR(TAG, "Caught signal '%s' [%d]", strsignal(signum), signum);
winpr_log_backtrace(TAG, WLOG_ERROR, 20);
}
(void)raise(signum);
}
static const int term_signals[] = { SIGINT, SIGTERM };
static const int fatal_signals[] = { SIGABRT, SIGFPE, SIGILL, SIGSEGV };
static BOOL register_handlers(const int* signals, size_t count, void (*handler)(int))
{
WINPR_ASSERT(signals || (count == 0));
WINPR_ASSERT(handler);
for (size_t x = 0; x < count; x++)
{
(void)signal(signals[x], handler);
}
return TRUE;
}
static void unregister_handlers(const int* signals, size_t count)
{
WINPR_ASSERT(signals || (count == 0));
for (size_t x = 0; x < count; x++)
{
(void)signal(signals[x], SIG_IGN);
}
}
static void unregister_all_handlers(void)
{
unregister_handlers(fatal_signals, ARRAYSIZE(fatal_signals));
unregister_handlers(term_signals, ARRAYSIZE(term_signals));
DeleteCriticalSection(&signal_lock);
}
int freerdp_handle_signals(void)
{
int rc = -1;
InitializeCriticalSection(&signal_lock);
fsig_lock();
WLog_DBG(TAG, "Registering signal hook...");
(void)atexit(unregister_all_handlers);
if (!register_handlers(fatal_signals, ARRAYSIZE(fatal_signals), fatal_handler))
goto fail;
if (!register_handlers(term_signals, ARRAYSIZE(term_signals), fsig_term_handler))
goto fail;
fsig_handlers_registered = true;
rc = 0;
fail:
fsig_unlock();
return rc;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Smart Card Structure Packing
*
* Copyright 2025 Armin Novak <armin.novak@thincast.com>
* Copyright 2025 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.
*/
#pragma once
#include <winpr/wlog.h>
#include <freerdp/api.h>
#include <freerdp/channels/scard.h>
FREERDP_LOCAL void smartcard_trace_long_return_int(wLog* log, const Long_Return* ret,
const char* name);

View File

@@ -0,0 +1,79 @@
/**
* 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 <winpr/sysinfo.h>
#include <freerdp/utils/stopwatch.h>
static void stopwatch_set_time(UINT64* usecs)
{
const UINT64 ns = winpr_GetTickCount64NS();
*usecs = WINPR_TIME_NS_TO_US(ns);
}
STOPWATCH* stopwatch_create(void)
{
STOPWATCH* sw = (STOPWATCH*)calloc(1, sizeof(STOPWATCH));
if (!sw)
return nullptr;
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)
{
const long double ld = stopwatch->elapsed / 1000000.0L;
return (double)ld;
}
void stopwatch_get_elapsed_time_in_useconds(STOPWATCH* stopwatch, UINT32* sec, UINT32* usec)
{
*sec = (UINT32)stopwatch->elapsed / 1000000;
*usec = (UINT32)stopwatch->elapsed % 1000000;
}

View File

@@ -0,0 +1,218 @@
/**
* 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 <errno.h>
#include <freerdp/utils/string.h>
#include <freerdp/settings.h>
#if defined(CHANNEL_RDPEI)
#include <freerdp/channels/rdpei.h>
#endif
const 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 nullptr;
}
}
return buffer;
}
const 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 = nullptr;
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] = WINPR_C_ARRAY_INIT;
(void)_snprintf(msg, sizeof(msg), "[0x%08" PRIx32 "]", flags);
winpr_str_append(msg, buffer, size, "");
}
return buffer;
}
BOOL freerdp_extract_key_value(const char* str, UINT32* pkey, UINT32* pvalue)
{
if (!str || !pkey || !pvalue)
return FALSE;
char* end1 = nullptr;
errno = 0;
unsigned long key = strtoul(str, &end1, 0);
if ((errno != 0) || !end1 || (*end1 != '=') || (key > UINT32_MAX))
return FALSE;
errno = 0;
unsigned long val = strtoul(&end1[1], nullptr, 0);
if ((errno != 0) || (val > UINT32_MAX))
return FALSE;
*pkey = (UINT32)key;
*pvalue = (UINT32)val;
return TRUE;
}
const char* freerdp_desktop_rotation_flags_to_string(UINT32 flags)
{
#define ENTRY(x) \
case x: \
return #x
switch (flags)
{
ENTRY(ORIENTATION_LANDSCAPE);
ENTRY(ORIENTATION_PORTRAIT);
ENTRY(ORIENTATION_LANDSCAPE_FLIPPED);
ENTRY(ORIENTATION_PORTRAIT_FLIPPED);
default:
return "ORIENTATION_UNKNOWN";
}
#undef ENTRY
}
const char* freerdp_input_touch_state_string(DWORD flags)
{
#if defined(CHANNEL_RDPEI)
if (flags & RDPINPUT_CONTACT_FLAG_DOWN)
return "RDPINPUT_CONTACT_FLAG_DOWN";
else if (flags & RDPINPUT_CONTACT_FLAG_UPDATE)
return "RDPINPUT_CONTACT_FLAG_UPDATE";
else if (flags & RDPINPUT_CONTACT_FLAG_UP)
return "RDPINPUT_CONTACT_FLAG_UP";
else if (flags & RDPINPUT_CONTACT_FLAG_INRANGE)
return "RDPINPUT_CONTACT_FLAG_INRANGE";
else if (flags & RDPINPUT_CONTACT_FLAG_INCONTACT)
return "RDPINPUT_CONTACT_FLAG_INCONTACT";
else if (flags & RDPINPUT_CONTACT_FLAG_CANCELED)
return "RDPINPUT_CONTACT_FLAG_CANCELED";
else
return "RDPINPUT_CONTACT_FLAG_UNKNOWN";
#else
return "CHANNEL_RDPEI not supported";
#endif
}
const char* freerdp_order_support_flags_string(UINT8 type)
{
#define ENTRY(x) \
case x: \
return #x
switch (type)
{
ENTRY(NEG_DSTBLT_INDEX);
ENTRY(NEG_PATBLT_INDEX);
ENTRY(NEG_SCRBLT_INDEX);
ENTRY(NEG_MEMBLT_INDEX);
ENTRY(NEG_MEM3BLT_INDEX);
ENTRY(NEG_ATEXTOUT_INDEX);
ENTRY(NEG_AEXTTEXTOUT_INDEX);
ENTRY(NEG_DRAWNINEGRID_INDEX);
ENTRY(NEG_LINETO_INDEX);
ENTRY(NEG_MULTI_DRAWNINEGRID_INDEX);
ENTRY(NEG_OPAQUE_RECT_INDEX);
ENTRY(NEG_SAVEBITMAP_INDEX);
ENTRY(NEG_WTEXTOUT_INDEX);
ENTRY(NEG_MEMBLT_V2_INDEX);
ENTRY(NEG_MEM3BLT_V2_INDEX);
ENTRY(NEG_MULTIDSTBLT_INDEX);
ENTRY(NEG_MULTIPATBLT_INDEX);
ENTRY(NEG_MULTISCRBLT_INDEX);
ENTRY(NEG_MULTIOPAQUERECT_INDEX);
ENTRY(NEG_FAST_INDEX_INDEX);
ENTRY(NEG_POLYGON_SC_INDEX);
ENTRY(NEG_POLYGON_CB_INDEX);
ENTRY(NEG_POLYLINE_INDEX);
ENTRY(NEG_UNUSED23_INDEX);
ENTRY(NEG_FAST_GLYPH_INDEX);
ENTRY(NEG_ELLIPSE_SC_INDEX);
ENTRY(NEG_ELLIPSE_CB_INDEX);
ENTRY(NEG_GLYPH_INDEX_INDEX);
ENTRY(NEG_GLYPH_WEXTTEXTOUT_INDEX);
ENTRY(NEG_GLYPH_WLONGTEXTOUT_INDEX);
ENTRY(NEG_GLYPH_WLONGEXTTEXTOUT_INDEX);
ENTRY(NEG_UNUSED31_INDEX);
default:
return "UNKNOWN";
}
#undef ENTRY
}

View File

@@ -0,0 +1,26 @@
set(MODULE_NAME "TestFreeRDPUtils")
set(MODULE_PREFIX "TEST_FREERDP_UTILS")
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
set(${MODULE_PREFIX}_TESTS TestRingBuffer.c TestPodArrays.c TestEncodedTypes.c)
create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} ${${MODULE_PREFIX}_TESTS})
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
target_link_libraries(${MODULE_NAME} PRIVATE freerdp winpr)
if(NOT WIN32)
target_link_libraries(${MODULE_NAME} PRIVATE m)
endif()
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")

View File

@@ -0,0 +1,218 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
*
* Copyright 2024 Thincast Technologies GmbH
* Copyright 2024 Armin Novak <anovak@thincast.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 <float.h>
#include <limits.h>
#include <math.h>
#include <winpr/crypto.h>
#include <freerdp/utils/encoded_types.h>
#ifndef MIN
#define MIN(x, y) ((x) < (y)) ? (x) : (y)
#endif
#ifndef MAX
#define MAX(x, y) ((x) > (y)) ? (x) : (y)
#endif
static BOOL test_signed_integer_read_write_equal(INT32 value)
{
INT32 rvalue = 0;
BYTE buffer[32] = WINPR_C_ARRAY_INIT;
wStream sbuffer = WINPR_C_ARRAY_INIT;
wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
WINPR_ASSERT(s);
if (!freerdp_write_four_byte_signed_integer(s, value))
{
(void)fprintf(stderr, "[%s(%" PRId32 ")] failed to write to stream\n", __func__, value);
return FALSE;
}
Stream_ResetPosition(s);
if (!freerdp_read_four_byte_signed_integer(s, &rvalue))
{
(void)fprintf(stderr, "[%s(%" PRId32 ")] failed to read from stream\n", __func__, value);
return FALSE;
}
if (value != rvalue)
{
(void)fprintf(stderr, "[%s(%" PRId32 ")] read invalid value %" PRId32 " from stream\n",
__func__, value, rvalue);
return FALSE;
}
return TRUE;
}
static BOOL test_signed_integer_write_oor(INT32 value)
{
BYTE buffer[32] = WINPR_C_ARRAY_INIT;
wStream sbuffer = WINPR_C_ARRAY_INIT;
wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
WINPR_ASSERT(s);
if (freerdp_write_four_byte_signed_integer(s, value))
{
(void)fprintf(stderr,
"[%s(%" PRId32 ")] out of range value not detected and written to stream\n",
__func__, value);
return FALSE;
}
return TRUE;
}
static BOOL test_signed_integers(void)
{
const INT32 outofrange[] = { FREERDP_FOUR_BYTE_SIGNED_INT_MAX + 1,
FREERDP_FOUR_BYTE_SIGNED_INT_MIN - 1, INT32_MAX, INT32_MIN };
const INT32 limits[] = { 1, 0, -1, FREERDP_FOUR_BYTE_SIGNED_INT_MAX,
FREERDP_FOUR_BYTE_SIGNED_INT_MIN };
for (size_t x = 0; x < ARRAYSIZE(limits); x++)
{
if (!test_signed_integer_read_write_equal(limits[x]))
return FALSE;
}
for (size_t x = 0; x < ARRAYSIZE(outofrange); x++)
{
if (!test_signed_integer_write_oor(outofrange[x]))
return FALSE;
}
for (size_t x = 0; x < 100000; x++)
{
INT32 val = 0;
if (winpr_RAND(&val, sizeof(val)) < 0)
return FALSE;
val = MAX(val, 0);
val = MIN(val, FREERDP_FOUR_BYTE_SIGNED_INT_MAX);
const INT32 nval = -val;
if (!test_signed_integer_read_write_equal(val))
return FALSE;
if (!test_signed_integer_read_write_equal(nval))
return FALSE;
}
return TRUE;
}
static BOOL test_float_read_write_equal(double value)
{
BYTE exp = 0;
double rvalue = FP_NAN;
BYTE buffer[32] = WINPR_C_ARRAY_INIT;
wStream sbuffer = WINPR_C_ARRAY_INIT;
wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
WINPR_ASSERT(s);
if (!freerdp_write_four_byte_float(s, value))
{
(void)fprintf(stderr, "[%s(%lf)] failed to write to stream\n", __func__, value);
return FALSE;
}
Stream_ResetPosition(s);
if (!freerdp_read_four_byte_float_exp(s, &rvalue, &exp))
{
(void)fprintf(stderr, "[%s(%lf)] failed to read from stream\n", __func__, value);
return FALSE;
}
const double diff = fabs(value - rvalue);
const double expdiff = diff * pow(10, exp);
if (expdiff >= 1.0)
{
(void)fprintf(stderr, "[%s(%lf)] read invalid value %lf from stream\n", __func__, value,
rvalue);
return FALSE;
}
return TRUE;
}
static BOOL test_floag_write_oor(double value)
{
BYTE buffer[32] = WINPR_C_ARRAY_INIT;
wStream sbuffer = WINPR_C_ARRAY_INIT;
wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
WINPR_ASSERT(s);
if (freerdp_write_four_byte_float(s, value))
{
(void)fprintf(stderr, "[%s(%lf)] out of range value not detected and written to stream\n",
__func__, value);
return FALSE;
}
return TRUE;
}
static double get(void)
{
double val = NAN;
do
{
if (winpr_RAND(&val, sizeof(val)) < 0)
{
// NOLINTNEXTLINE(concurrency-mt-unsafe)
exit(-1);
}
} while ((val < 0.0) || (val > FREERDP_FOUR_BYTE_FLOAT_MAX) || isnan(val) || isinf(val));
return val;
}
static BOOL test_floats(void)
{
const double outofrange[] = { FREERDP_FOUR_BYTE_FLOAT_MAX + 1, FREERDP_FOUR_BYTE_FLOAT_MIN - 1,
DBL_MAX, -DBL_MAX };
const double limits[] = { 100045.26129238126, 1, 0, -1, FREERDP_FOUR_BYTE_FLOAT_MAX,
FREERDP_FOUR_BYTE_FLOAT_MIN };
for (size_t x = 0; x < ARRAYSIZE(limits); x++)
{
if (!test_float_read_write_equal(limits[x]))
return FALSE;
}
for (size_t x = 0; x < ARRAYSIZE(outofrange); x++)
{
if (!test_floag_write_oor(outofrange[x]))
return FALSE;
}
for (size_t x = 0; x < 100000; x++)
{
double val = get();
const double nval = -val;
if (!test_float_read_write_equal(val))
return FALSE;
if (!test_float_read_write_equal(nval))
return FALSE;
}
return TRUE;
}
int TestEncodedTypes(int argc, char* argv[])
{
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
if (!test_signed_integers())
return -1;
if (!test_floats())
return -1;
return 0;
}

View File

@@ -0,0 +1,132 @@
/**
* 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>
// NOLINTNEXTLINE(readability-non-const-parameter)
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);
}
// NOLINTNEXTLINE(bugprone-suspicious-memory-comparison,cert-exp42-c,cert-flp37-c)
POD_ARRAYS_IMPL(BasicStruct, basicstruct)
int TestPodArrays(int argc, char* argv[])
{
int rc = -1;
UINT32 sum = 0;
UINT32 foreach_index = 0;
ArrayUINT32 uint32s = WINPR_C_ARRAY_INIT;
UINT32* ptr = nullptr;
const UINT32* cptr = nullptr;
ArrayBasicStruct basicStructs = WINPR_C_ARRAY_INIT;
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, nullptr) || 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, nullptr))
goto fail;
rc = 0;
fail:
array_uint32_uninit(&uint32s);
array_basicstruct_uninit(&basicStructs);
return rc;
}

View File

@@ -0,0 +1,227 @@
/**
* 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 = nullptr;
BYTE* rb_ptr = nullptr;
DataChunk chunks[2];
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
if (!ringbuffer_init(&ringBuffer, 10))
{
(void)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;
(void)fprintf(stderr, "%d: basic tests...", ++testNo);
if (!ringbuffer_write(&ringBuffer, tmpBuf, 5) || !ringbuffer_write(&ringBuffer, tmpBuf, 5) ||
!ringbuffer_write(&ringBuffer, tmpBuf, 5))
{
(void)fprintf(stderr, "error when writing bytes\n");
return -1;
}
if (ringbuffer_used(&ringBuffer) != 15)
{
(void)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)
{
(void)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)
{
(void)fprintf(stderr,
"invalid byte at %" PRIuz ", got %" PRIu8 " instead of %" PRIuz "\n", i,
chunks[0].data[i], i % 5U);
return -1;
}
}
if (ringbuffer_used(&ringBuffer) != 5)
{
(void)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 split in 2 chunks */
if (!ringbuffer_write(&ringBuffer, tmpBuf, 6) ||
ringbuffer_peek(&ringBuffer, chunks, 11) != 2 || chunks[0].size != 10 ||
chunks[1].size != 1)
{
(void)fprintf(stderr, "invalid read of split data\n");
return -1;
}
ringbuffer_commit_read_bytes(&ringBuffer, 11);
(void)fprintf(stderr, "ok\n");
(void)fprintf(stderr, "%d: peek with nothing to read...", ++testNo);
if (ringbuffer_peek(&ringBuffer, chunks, 10))
{
(void)fprintf(stderr, "peek returns some chunks\n");
return -1;
}
(void)fprintf(stderr, "ok\n");
(void)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)
{
(void)fprintf(stderr, "ringbuffer_ensure_linear_write() error\n");
return -1;
}
memcpy(rb_ptr, tmpBuf, 50);
if (!ringbuffer_commit_written_bytes(&ringBuffer, 50))
{
(void)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)
{
(void)fprintf(stderr, "not the expected capacity, have %" PRIuz " and expects 10\n",
ringbuffer_capacity(&ringBuffer));
return -1;
}
(void)fprintf(stderr, "ok\n");
(void)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))
{
(void)fprintf(stderr, "ringbuffer_commit_written_bytes() error, i=%d\n", i);
return -1;
}
}
ringbuffer_commit_read_bytes(&ringBuffer, 50ULL * 1000ULL);
(void)fprintf(stderr, "ok\n");
ringbuffer_destroy(&ringBuffer);
(void)fprintf(stderr, "%d: specific overlaps test...", ++testNo);
if (!test_overlaps())
{
(void)fprintf(stderr, "ko\n");
return -1;
}
(void)fprintf(stderr, "ok\n");
ringbuffer_destroy(&ringBuffer);
free(tmpBuf);
return 0;
}