diff options
Diffstat (limited to '')
-rw-r--r-- | channels/geometry/client/CMakeLists.txt | 31 | ||||
-rw-r--r-- | channels/geometry/client/geometry_main.c | 402 | ||||
-rw-r--r-- | channels/geometry/client/geometry_main.h | 30 |
3 files changed, 463 insertions, 0 deletions
diff --git a/channels/geometry/client/CMakeLists.txt b/channels/geometry/client/CMakeLists.txt new file mode 100644 index 0000000..b069bb1 --- /dev/null +++ b/channels/geometry/client/CMakeLists.txt @@ -0,0 +1,31 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2017 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. + +define_channel_client("geometry") + +set(${MODULE_PREFIX}_SRCS + geometry_main.c + geometry_main.h +) + +set(${MODULE_PREFIX}_LIBS + winpr +) + +include_directories(..) + +add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry") diff --git a/channels/geometry/client/geometry_main.c b/channels/geometry/client/geometry_main.c new file mode 100644 index 0000000..5465fc9 --- /dev/null +++ b/channels/geometry/client/geometry_main.c @@ -0,0 +1,402 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Geometry tracking Virtual Channel Extension + * + * Copyright 2017 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/config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <winpr/crt.h> +#include <winpr/synch.h> +#include <winpr/print.h> +#include <winpr/stream.h> +#include <winpr/cmdline.h> +#include <winpr/collections.h> + +#include <freerdp/addin.h> +#include <freerdp/client/channels.h> +#include <freerdp/client/geometry.h> +#include <freerdp/channels/log.h> + +#define TAG CHANNELS_TAG("geometry.client") + +#include "geometry_main.h" + +typedef struct +{ + GENERIC_DYNVC_PLUGIN base; + GeometryClientContext* context; +} GEOMETRY_PLUGIN; + +static UINT32 mappedGeometryHash(const void* v) +{ + const UINT64* g = (const UINT64*)v; + return (UINT32)((*g >> 32) + (*g & 0xffffffff)); +} + +static BOOL mappedGeometryKeyCompare(const void* v1, const void* v2) +{ + const UINT64* g1 = (const UINT64*)v1; + const UINT64* g2 = (const UINT64*)v2; + return *g1 == *g2; +} + +static void freerdp_rgndata_reset(FREERDP_RGNDATA* data) +{ + data->nRectCount = 0; +} + +static UINT32 geometry_read_RGNDATA(wLog* logger, wStream* s, UINT32 len, FREERDP_RGNDATA* rgndata) +{ + UINT32 dwSize = 0; + UINT32 iType = 0; + INT32 right = 0; + INT32 bottom = 0; + INT32 x = 0; + INT32 y = 0; + INT32 w = 0; + INT32 h = 0; + + if (len < 32) + { + WLog_Print(logger, WLOG_ERROR, "invalid RGNDATA"); + return ERROR_INVALID_DATA; + } + + Stream_Read_UINT32(s, dwSize); + + if (dwSize != 32) + { + WLog_Print(logger, WLOG_ERROR, "invalid RGNDATA dwSize"); + return ERROR_INVALID_DATA; + } + + Stream_Read_UINT32(s, iType); + + if (iType != RDH_RECTANGLE) + { + WLog_Print(logger, WLOG_ERROR, "iType %" PRIu32 " for RGNDATA is not supported", iType); + return ERROR_UNSUPPORTED_TYPE; + } + + Stream_Read_UINT32(s, rgndata->nRectCount); + Stream_Seek_UINT32(s); /* nRgnSize IGNORED */ + Stream_Read_INT32(s, x); + Stream_Read_INT32(s, y); + Stream_Read_INT32(s, right); + Stream_Read_INT32(s, bottom); + if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX)) + return ERROR_INVALID_DATA; + w = right - x; + h = bottom - y; + if ((abs(w) > INT16_MAX) || (abs(h) > INT16_MAX)) + return ERROR_INVALID_DATA; + rgndata->boundingRect.x = (INT16)x; + rgndata->boundingRect.y = (INT16)y; + rgndata->boundingRect.width = (INT16)w; + rgndata->boundingRect.height = (INT16)h; + len -= 32; + + if (len / (4 * 4) < rgndata->nRectCount) + { + WLog_Print(logger, WLOG_ERROR, "not enough data for region rectangles"); + return ERROR_INVALID_DATA; + } + + if (rgndata->nRectCount) + { + RDP_RECT* tmp = realloc(rgndata->rects, rgndata->nRectCount * sizeof(RDP_RECT)); + + if (!tmp) + { + WLog_Print(logger, WLOG_ERROR, "unable to allocate memory for %" PRIu32 " RECTs", + rgndata->nRectCount); + return CHANNEL_RC_NO_MEMORY; + } + rgndata->rects = tmp; + + for (UINT32 i = 0; i < rgndata->nRectCount; i++) + { + Stream_Read_INT32(s, x); + Stream_Read_INT32(s, y); + Stream_Read_INT32(s, right); + Stream_Read_INT32(s, bottom); + if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX)) + return ERROR_INVALID_DATA; + w = right - x; + h = bottom - y; + if ((abs(w) > INT16_MAX) || (abs(h) > INT16_MAX)) + return ERROR_INVALID_DATA; + rgndata->rects[i].x = (INT16)x; + rgndata->rects[i].y = (INT16)y; + rgndata->rects[i].width = (INT16)w; + rgndata->rects[i].height = (INT16)h; + } + } + + return CHANNEL_RC_OK; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT geometry_recv_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s) +{ + UINT32 length = 0; + UINT32 cbGeometryBuffer = 0; + MAPPED_GEOMETRY* mappedGeometry = NULL; + GEOMETRY_PLUGIN* geometry = NULL; + GeometryClientContext* context = NULL; + UINT ret = CHANNEL_RC_OK; + UINT32 updateType = 0; + UINT32 geometryType = 0; + UINT64 id = 0; + wLog* logger = NULL; + + geometry = (GEOMETRY_PLUGIN*)callback->plugin; + logger = geometry->base.log; + context = (GeometryClientContext*)geometry->base.iface.pInterface; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, length); /* Length (4 bytes) */ + + if (length < 73 || !Stream_CheckAndLogRequiredLength(TAG, s, (length - 4))) + { + WLog_Print(logger, WLOG_ERROR, "invalid packet length"); + return ERROR_INVALID_DATA; + } + + Stream_Read_UINT32(s, context->remoteVersion); + Stream_Read_UINT64(s, id); + Stream_Read_UINT32(s, updateType); + Stream_Seek_UINT32(s); /* flags */ + + mappedGeometry = HashTable_GetItemValue(context->geometries, &id); + + if (updateType == GEOMETRY_CLEAR) + { + if (!mappedGeometry) + { + WLog_Print(logger, WLOG_ERROR, + "geometry 0x%" PRIx64 " not found here, ignoring clear command", id); + return CHANNEL_RC_OK; + } + + WLog_Print(logger, WLOG_DEBUG, "clearing geometry 0x%" PRIx64 "", id); + + if (mappedGeometry->MappedGeometryClear && + !mappedGeometry->MappedGeometryClear(mappedGeometry)) + return ERROR_INTERNAL_ERROR; + + if (!HashTable_Remove(context->geometries, &id)) + WLog_Print(logger, WLOG_ERROR, "geometry not removed from geometries"); + } + else if (updateType == GEOMETRY_UPDATE) + { + BOOL newOne = FALSE; + + if (!mappedGeometry) + { + newOne = TRUE; + WLog_Print(logger, WLOG_DEBUG, "creating geometry 0x%" PRIx64 "", id); + mappedGeometry = calloc(1, sizeof(MAPPED_GEOMETRY)); + if (!mappedGeometry) + return CHANNEL_RC_NO_MEMORY; + + mappedGeometry->refCounter = 1; + mappedGeometry->mappingId = id; + + if (!HashTable_Insert(context->geometries, &(mappedGeometry->mappingId), + mappedGeometry)) + { + WLog_Print(logger, WLOG_ERROR, + "unable to register geometry 0x%" PRIx64 " in the table", id); + free(mappedGeometry); + return CHANNEL_RC_NO_MEMORY; + } + } + else + { + WLog_Print(logger, WLOG_DEBUG, "updating geometry 0x%" PRIx64 "", id); + } + + Stream_Read_UINT64(s, mappedGeometry->topLevelId); + + Stream_Read_INT32(s, mappedGeometry->left); + Stream_Read_INT32(s, mappedGeometry->top); + Stream_Read_INT32(s, mappedGeometry->right); + Stream_Read_INT32(s, mappedGeometry->bottom); + + Stream_Read_INT32(s, mappedGeometry->topLevelLeft); + Stream_Read_INT32(s, mappedGeometry->topLevelTop); + Stream_Read_INT32(s, mappedGeometry->topLevelRight); + Stream_Read_INT32(s, mappedGeometry->topLevelBottom); + + Stream_Read_UINT32(s, geometryType); + if (geometryType != 0x02) + WLog_Print(logger, WLOG_DEBUG, "geometryType should be set to 0x02 and is 0x%" PRIx32, + geometryType); + + Stream_Read_UINT32(s, cbGeometryBuffer); + if (!Stream_CheckAndLogRequiredLength(TAG, s, cbGeometryBuffer)) + { + // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert ownership mappedGeometry + return ERROR_INVALID_DATA; + } + + if (cbGeometryBuffer) + { + ret = geometry_read_RGNDATA(logger, s, cbGeometryBuffer, &mappedGeometry->geometry); + if (ret != CHANNEL_RC_OK) + return ret; + } + else + { + freerdp_rgndata_reset(&mappedGeometry->geometry); + } + + if (newOne) + { + if (context->MappedGeometryAdded && + !context->MappedGeometryAdded(context, mappedGeometry)) + { + WLog_Print(logger, WLOG_ERROR, "geometry added callback failed"); + ret = ERROR_INTERNAL_ERROR; + } + } + else + { + if (mappedGeometry->MappedGeometryUpdate && + !mappedGeometry->MappedGeometryUpdate(mappedGeometry)) + { + WLog_Print(logger, WLOG_ERROR, "geometry update callback failed"); + ret = ERROR_INTERNAL_ERROR; + } + } + } + else + { + WLog_Print(logger, WLOG_ERROR, "unknown updateType=%" PRIu32 "", updateType); + ret = CHANNEL_RC_OK; + } + + return ret; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT geometry_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data) +{ + GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback; + return geometry_recv_pdu(callback, data); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT geometry_on_close(IWTSVirtualChannelCallback* pChannelCallback) +{ + free(pChannelCallback); + return CHANNEL_RC_OK; +} + +static void mappedGeometryUnref_void(void* arg) +{ + MAPPED_GEOMETRY* g = (MAPPED_GEOMETRY*)arg; + mappedGeometryUnref(g); +} + +/** + * Channel Client Interface + */ + +static const IWTSVirtualChannelCallback geometry_callbacks = { geometry_on_data_received, + NULL, /* Open */ + geometry_on_close, NULL }; + +static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, rdpContext* rcontext, rdpSettings* settings) +{ + GeometryClientContext* context = NULL; + GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)base; + + WINPR_ASSERT(base); + WINPR_UNUSED(settings); + + context = (GeometryClientContext*)calloc(1, sizeof(GeometryClientContext)); + if (!context) + { + WLog_Print(base->log, WLOG_ERROR, "calloc failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + context->geometries = HashTable_New(FALSE); + if (!context->geometries) + { + WLog_Print(base->log, WLOG_ERROR, "unable to allocate geometries"); + free(context); + return CHANNEL_RC_NO_MEMORY; + } + + HashTable_SetHashFunction(context->geometries, mappedGeometryHash); + { + wObject* obj = HashTable_KeyObject(context->geometries); + obj->fnObjectEquals = mappedGeometryKeyCompare; + } + { + wObject* obj = HashTable_ValueObject(context->geometries); + obj->fnObjectFree = mappedGeometryUnref_void; + } + context->handle = (void*)geometry; + + geometry->context = context; + geometry->base.iface.pInterface = (void*)context; + + return CHANNEL_RC_OK; +} + +static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base) +{ + GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)base; + + if (geometry->context) + HashTable_Free(geometry->context->geometries); + free(geometry->context); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +FREERDP_ENTRY_POINT(UINT geometry_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)) +{ + return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, GEOMETRY_DVC_CHANNEL_NAME, + sizeof(GEOMETRY_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK), + &geometry_callbacks, init_plugin_cb, terminate_plugin_cb); +} diff --git a/channels/geometry/client/geometry_main.h b/channels/geometry/client/geometry_main.h new file mode 100644 index 0000000..dc914b6 --- /dev/null +++ b/channels/geometry/client/geometry_main.h @@ -0,0 +1,30 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Geometry tracking virtual channel extension + * + * Copyright 2017 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. + */ + +#ifndef FREERDP_CHANNEL_GEOMETRY_CLIENT_MAIN_H +#define FREERDP_CHANNEL_GEOMETRY_CLIENT_MAIN_H + +#include <freerdp/config.h> + +#include <freerdp/dvc.h> +#include <freerdp/types.h> +#include <freerdp/addin.h> +#include <freerdp/client/geometry.h> + +#endif /* FREERDP_CHANNEL_GEOMETRY_CLIENT_MAIN_H */ |