FreeRDP
Loading...
Searching...
No Matches
geometry_main.c
1
20#include <freerdp/config.h>
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include <winpr/crt.h>
27#include <winpr/synch.h>
28#include <winpr/print.h>
29#include <winpr/stream.h>
30#include <winpr/cmdline.h>
31#include <winpr/collections.h>
32
33#include <freerdp/addin.h>
34#include <freerdp/client/channels.h>
35#include <freerdp/client/geometry.h>
36#include <freerdp/channels/log.h>
37
38#define TAG CHANNELS_TAG("geometry.client")
39
40#include "geometry_main.h"
41
42typedef struct
43{
45 GeometryClientContext* context;
46} GEOMETRY_PLUGIN;
47
48static UINT32 mappedGeometryHash(const void* v)
49{
50 const UINT64* g = (const UINT64*)v;
51 return (UINT32)((*g >> 32) + (*g & 0xffffffff));
52}
53
54static BOOL mappedGeometryKeyCompare(const void* v1, const void* v2)
55{
56 const UINT64* g1 = (const UINT64*)v1;
57 const UINT64* g2 = (const UINT64*)v2;
58 return *g1 == *g2;
59}
60
61static void freerdp_rgndata_reset(FREERDP_RGNDATA* data)
62{
63 data->nRectCount = 0;
64}
65
66static UINT32 geometry_read_RGNDATA(wLog* logger, wStream* s, UINT32 len, FREERDP_RGNDATA* rgndata)
67{
68 WINPR_ASSERT(rgndata);
69
70 if (len < 32)
71 {
72 WLog_Print(logger, WLOG_ERROR, "invalid RGNDATA");
73 return ERROR_INVALID_DATA;
74 }
75
76 const UINT32 dwSize = Stream_Get_UINT32(s);
77
78 if (dwSize != 32)
79 {
80 WLog_Print(logger, WLOG_ERROR, "invalid RGNDATA dwSize");
81 return ERROR_INVALID_DATA;
82 }
83
84 const UINT32 iType = Stream_Get_UINT32(s);
85
86 if (iType != RDH_RECTANGLE)
87 {
88 WLog_Print(logger, WLOG_ERROR, "iType %" PRIu32 " for RGNDATA is not supported", iType);
89 return ERROR_UNSUPPORTED_TYPE;
90 }
91
92 rgndata->nRectCount = Stream_Get_UINT32(s);
93 Stream_Seek_UINT32(s); /* nRgnSize IGNORED */
94 {
95 const INT32 x = Stream_Get_INT32(s);
96 const INT32 y = Stream_Get_INT32(s);
97 const INT32 right = Stream_Get_INT32(s);
98 const INT32 bottom = Stream_Get_INT32(s);
99 if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX))
100 {
101 WLog_Print(logger, WLOG_ERROR, "x=%" PRId32 " or y=%" PRId32 " does not fit into INT16",
102 x, y);
103 return ERROR_INVALID_DATA;
104 }
105 const INT32 w = right - x;
106 const INT32 h = bottom - y;
107 if ((abs(w) > INT16_MAX) || (abs(h) > INT16_MAX))
108 {
109 WLog_Print(logger, WLOG_ERROR, "w=%" PRId32 " or h=%" PRId32 " does not fit into INT16",
110 w, h);
111 return ERROR_INVALID_DATA;
112 }
113 if ((x > right) || (y > bottom))
114 {
115 WLog_Print(logger, WLOG_ERROR,
116 "One of x=%" PRId32 " > right=%" PRId32 " or y=%" PRId32
117 " > bottom=%" PRId32,
118 x, right, y, bottom);
119 return ERROR_INVALID_DATA;
120 }
121 rgndata->boundingRect.x = (INT16)x;
122 rgndata->boundingRect.y = (INT16)y;
123 rgndata->boundingRect.width = (INT16)w;
124 rgndata->boundingRect.height = (INT16)h;
125 }
126 len -= 32;
127
128 if (len / (4 * 4) < rgndata->nRectCount)
129 {
130 WLog_Print(logger, WLOG_ERROR, "not enough data for region rectangles");
131 return ERROR_INVALID_DATA;
132 }
133
134 if (rgndata->nRectCount)
135 {
136 RDP_RECT* tmp = realloc(rgndata->rects, rgndata->nRectCount * sizeof(RDP_RECT));
137
138 if (!tmp)
139 {
140 WLog_Print(logger, WLOG_ERROR, "unable to allocate memory for %" PRIu32 " RECTs",
141 rgndata->nRectCount);
142 return CHANNEL_RC_NO_MEMORY;
143 }
144 rgndata->rects = tmp;
145
146 for (UINT32 i = 0; i < rgndata->nRectCount; i++)
147 {
148 RDP_RECT* rect = &rgndata->rects[i];
149
150 if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, 16))
151 return CHANNEL_RC_NULL_DATA;
152
153 const INT32 x = Stream_Get_INT32(s);
154 const INT32 y = Stream_Get_INT32(s);
155 const INT32 right = Stream_Get_INT32(s);
156 const INT32 bottom = Stream_Get_INT32(s);
157 if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX))
158 {
159 WLog_Print(logger, WLOG_ERROR, "x=%" PRId32 " or y=%" PRId32 " does not fit INT16",
160 x, y);
161 return ERROR_INVALID_DATA;
162 }
163 const INT32 w = right - x;
164 const INT32 h = bottom - y;
165 if ((abs(w) > INT16_MAX) || (abs(h) > INT16_MAX))
166 return ERROR_INVALID_DATA;
167
168 if ((x > right) || (y > bottom))
169 {
170 WLog_Print(logger, WLOG_ERROR,
171 "One of x=%" PRId32 " > right=%" PRId32 ", y=%" PRId32
172 " > bottom=%" PRId32,
173 x, right, y, bottom);
174 return ERROR_INVALID_DATA;
175 }
176 rect->x = (INT16)x;
177 rect->y = (INT16)y;
178 rect->width = (INT16)w;
179 rect->height = (INT16)h;
180 }
181 }
182
183 return CHANNEL_RC_OK;
184}
185
191static UINT geometry_recv_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
192{
193 UINT ret = CHANNEL_RC_OK;
194
195 WINPR_ASSERT(callback);
196 GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)callback->plugin;
197 WINPR_ASSERT(geometry);
198
199 wLog* logger = geometry->base.log;
200 GeometryClientContext* context = (GeometryClientContext*)geometry->base.iface.pInterface;
201 WINPR_ASSERT(context);
202
203 if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, 4))
204 return ERROR_INVALID_DATA;
205
206 const UINT32 length = Stream_Get_UINT32(s); /* Length (4 bytes) */
207
208 if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, (length - 4)))
209 {
210 WLog_Print(logger, WLOG_ERROR, "invalid packet length");
211 return ERROR_INVALID_DATA;
212 }
213
214 if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, 20))
215 return ERROR_INVALID_DATA;
216
217 context->remoteVersion = Stream_Get_UINT32(s);
218 const UINT64 id = Stream_Get_UINT64(s);
219 const UINT32 updateType = Stream_Get_UINT32(s);
220 Stream_Seek_UINT32(s); /* flags */
221
222 MAPPED_GEOMETRY* mappedGeometry = HashTable_GetItemValue(context->geometries, &id);
223
224 if (updateType == GEOMETRY_CLEAR)
225 {
226 if (!mappedGeometry)
227 {
228 WLog_Print(logger, WLOG_ERROR,
229 "geometry 0x%" PRIx64 " not found here, ignoring clear command", id);
230 return CHANNEL_RC_OK;
231 }
232
233 WLog_Print(logger, WLOG_DEBUG, "clearing geometry 0x%" PRIx64 "", id);
234
235 if (mappedGeometry->MappedGeometryClear &&
236 !mappedGeometry->MappedGeometryClear(mappedGeometry))
237 return ERROR_INTERNAL_ERROR;
238
239 if (!HashTable_Remove(context->geometries, &id))
240 WLog_Print(logger, WLOG_ERROR, "geometry not removed from geometries");
241 }
242 else if (updateType == GEOMETRY_UPDATE)
243 {
244 BOOL newOne = FALSE;
245
246 if (!mappedGeometry)
247 {
248 newOne = TRUE;
249 WLog_Print(logger, WLOG_DEBUG, "creating geometry 0x%" PRIx64 "", id);
250 mappedGeometry = calloc(1, sizeof(MAPPED_GEOMETRY));
251 if (!mappedGeometry)
252 return CHANNEL_RC_NO_MEMORY;
253
254 mappedGeometry->refCounter = 1;
255 mappedGeometry->mappingId = id;
256
257 if (!HashTable_Insert(context->geometries, &(mappedGeometry->mappingId),
258 mappedGeometry))
259 {
260 WLog_Print(logger, WLOG_ERROR,
261 "unable to register geometry 0x%" PRIx64 " in the table", id);
262 free(mappedGeometry);
263 return CHANNEL_RC_NO_MEMORY;
264 }
265 }
266 else
267 {
268 WLog_Print(logger, WLOG_DEBUG, "updating geometry 0x%" PRIx64 "", id);
269 }
270
271 if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, 48))
272 {
273 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert ownership mappedGeometry
274 return ERROR_INVALID_DATA;
275 }
276
277 mappedGeometry->topLevelId = Stream_Get_UINT64(s);
278
279 mappedGeometry->left = Stream_Get_INT32(s);
280 mappedGeometry->top = Stream_Get_INT32(s);
281 mappedGeometry->right = Stream_Get_INT32(s);
282 mappedGeometry->bottom = Stream_Get_INT32(s);
283
284 if (mappedGeometry->left > mappedGeometry->right)
285 {
286 WLog_Print(logger, WLOG_ERROR, "invalid left=%" PRId32 " > right=%" PRId32,
287 mappedGeometry->left, mappedGeometry->right);
288 return ERROR_INVALID_DATA;
289 }
290 if (mappedGeometry->top > mappedGeometry->bottom)
291 {
292 WLog_Print(logger, WLOG_ERROR, "invalid top=%" PRId32 " > bottom=%" PRId32,
293 mappedGeometry->top, mappedGeometry->bottom);
294 return ERROR_INVALID_DATA;
295 }
296
297 mappedGeometry->topLevelLeft = Stream_Get_INT32(s);
298 mappedGeometry->topLevelTop = Stream_Get_INT32(s);
299 mappedGeometry->topLevelRight = Stream_Get_INT32(s);
300 mappedGeometry->topLevelBottom = Stream_Get_INT32(s);
301
302 if (mappedGeometry->topLevelLeft > mappedGeometry->topLevelRight)
303 {
304 WLog_Print(logger, WLOG_ERROR,
305 "invalid topLevelLeft=%" PRId32 " > topLevelRight=%" PRId32,
306 mappedGeometry->topLevelLeft, mappedGeometry->topLevelRight);
307 return ERROR_INVALID_DATA;
308 }
309 if (mappedGeometry->topLevelTop > mappedGeometry->topLevelBottom)
310 {
311 WLog_Print(logger, WLOG_ERROR,
312 "invalid topLevelTop=%" PRId32 " > topLevelBottom=%" PRId32,
313 mappedGeometry->topLevelTop, mappedGeometry->topLevelBottom);
314 return ERROR_INVALID_DATA;
315 }
316
317 const UINT32 geometryType = Stream_Get_UINT32(s);
318 if (geometryType != 0x02)
319 {
320 WLog_Print(logger, WLOG_DEBUG, "geometryType should be set to 0x02 and is 0x%" PRIx32,
321 geometryType);
322 return ERROR_INVALID_DATA;
323 }
324
325 const UINT32 cbGeometryBuffer = Stream_Get_UINT32(s);
326 if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, cbGeometryBuffer))
327 {
328 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert ownership mappedGeometry
329 return ERROR_INVALID_DATA;
330 }
331
332 if (cbGeometryBuffer > 0)
333 {
334 ret = geometry_read_RGNDATA(logger, s, cbGeometryBuffer, &mappedGeometry->geometry);
335 if (ret != CHANNEL_RC_OK)
336 return ret;
337 }
338 else
339 {
340 freerdp_rgndata_reset(&mappedGeometry->geometry);
341 }
342
343 if (newOne)
344 {
345 if (context->MappedGeometryAdded &&
346 !context->MappedGeometryAdded(context, mappedGeometry))
347 {
348 WLog_Print(logger, WLOG_ERROR, "geometry added callback failed");
349 ret = ERROR_INTERNAL_ERROR;
350 }
351 }
352 else
353 {
354 if (mappedGeometry->MappedGeometryUpdate &&
355 !mappedGeometry->MappedGeometryUpdate(mappedGeometry))
356 {
357 WLog_Print(logger, WLOG_ERROR, "geometry update callback failed");
358 ret = ERROR_INTERNAL_ERROR;
359 }
360 }
361 }
362 else
363 {
364 WLog_Print(logger, WLOG_ERROR, "unknown updateType=%" PRIu32 "", updateType);
365 ret = CHANNEL_RC_OK;
366 }
367
368 return ret;
369}
370
376static UINT geometry_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
377{
378 GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
379 return geometry_recv_pdu(callback, data);
380}
381
387static UINT geometry_on_close(IWTSVirtualChannelCallback* pChannelCallback)
388{
389 free(pChannelCallback);
390 return CHANNEL_RC_OK;
391}
392
393static void mappedGeometryUnref_void(void* arg)
394{
395 MAPPED_GEOMETRY* g = (MAPPED_GEOMETRY*)arg;
396 mappedGeometryUnref(g);
397}
398
403static const IWTSVirtualChannelCallback geometry_callbacks = { geometry_on_data_received,
404 nullptr, /* Open */
405 geometry_on_close, nullptr };
406
407static void geometry_plugin_free(GeometryClientContext* context)
408{
409 if (!context)
410 return;
411 HashTable_Free(context->geometries);
412 free(context);
413}
414static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base)
415{
416 GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)base;
417
418 if (!geometry)
419 return;
420
421 geometry_plugin_free(geometry->context);
422}
423
424static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, WINPR_ATTR_UNUSED rdpContext* rcontext,
425 rdpSettings* settings)
426{
427 GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)base;
428
429 WINPR_ASSERT(base);
430 WINPR_UNUSED(settings);
431
432 GeometryClientContext* context =
433 (GeometryClientContext*)calloc(1, sizeof(GeometryClientContext));
434 if (!context)
435 goto fail;
436
437 context->geometries = HashTable_New(FALSE);
438 if (!context->geometries)
439 goto fail;
440
441 if (!HashTable_SetHashFunction(context->geometries, mappedGeometryHash))
442 goto fail;
443
444 {
445 wObject* obj = HashTable_KeyObject(context->geometries);
446 obj->fnObjectEquals = mappedGeometryKeyCompare;
447 }
448 {
449 wObject* obj = HashTable_ValueObject(context->geometries);
450 obj->fnObjectFree = mappedGeometryUnref_void;
451 }
452 context->handle = (void*)geometry;
453
454 geometry->context = context;
455 geometry->base.iface.pInterface = (void*)context;
456
457 return CHANNEL_RC_OK;
458
459fail:
460 geometry_plugin_free(context);
461 return CHANNEL_RC_NO_MEMORY;
462}
463
469FREERDP_ENTRY_POINT(UINT VCAPITYPE geometry_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
470{
471 return freerdp_generic_DVCPluginEntryWithOptions(
472 pEntryPoints, TAG, GEOMETRY_DVC_CHANNEL_NAME, sizeof(GEOMETRY_PLUGIN),
473 sizeof(GENERIC_CHANNEL_CALLBACK), &geometry_callbacks, init_plugin_cb, terminate_plugin_cb,
474 GENERIC_CHANNEL_ENTRY_OPTION_MULTI_INSTANCE);
475}
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59
WINPR_ATTR_NODISCARD OBJECT_EQUALS_FN fnObjectEquals
Definition collections.h:61