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 return ERROR_INVALID_DATA;
101 const INT32 w = right - x;
102 const INT32 h = bottom - y;
103 if ((abs(w) > INT16_MAX) || (abs(h) > INT16_MAX))
104 return ERROR_INVALID_DATA;
105 rgndata->boundingRect.x = (INT16)x;
106 rgndata->boundingRect.y = (INT16)y;
107 rgndata->boundingRect.width = (INT16)w;
108 rgndata->boundingRect.height = (INT16)h;
109 }
110 len -= 32;
111
112 if (len / (4 * 4) < rgndata->nRectCount)
113 {
114 WLog_Print(logger, WLOG_ERROR, "not enough data for region rectangles");
115 return ERROR_INVALID_DATA;
116 }
117
118 if (rgndata->nRectCount)
119 {
120 RDP_RECT* tmp = realloc(rgndata->rects, rgndata->nRectCount * sizeof(RDP_RECT));
121
122 if (!tmp)
123 {
124 WLog_Print(logger, WLOG_ERROR, "unable to allocate memory for %" PRIu32 " RECTs",
125 rgndata->nRectCount);
126 return CHANNEL_RC_NO_MEMORY;
127 }
128 rgndata->rects = tmp;
129
130 for (UINT32 i = 0; i < rgndata->nRectCount; i++)
131 {
132 RDP_RECT* rect = &rgndata->rects[i];
133
134 if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, 16))
135 return CHANNEL_RC_NULL_DATA;
136
137 const INT32 x = Stream_Get_INT32(s);
138 const INT32 y = Stream_Get_INT32(s);
139 const INT32 right = Stream_Get_INT32(s);
140 const INT32 bottom = Stream_Get_INT32(s);
141 if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX))
142 return ERROR_INVALID_DATA;
143
144 const INT32 w = right - x;
145 const INT32 h = bottom - y;
146 if ((abs(w) > INT16_MAX) || (abs(h) > INT16_MAX))
147 return ERROR_INVALID_DATA;
148
149 rect->x = (INT16)x;
150 rect->y = (INT16)y;
151 rect->width = (INT16)w;
152 rect->height = (INT16)h;
153 }
154 }
155
156 return CHANNEL_RC_OK;
157}
158
164static UINT geometry_recv_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
165{
166 UINT ret = CHANNEL_RC_OK;
167
168 WINPR_ASSERT(callback);
169 GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)callback->plugin;
170 WINPR_ASSERT(geometry);
171
172 wLog* logger = geometry->base.log;
173 GeometryClientContext* context = (GeometryClientContext*)geometry->base.iface.pInterface;
174 WINPR_ASSERT(context);
175
176 if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, 4))
177 return ERROR_INVALID_DATA;
178
179 const UINT32 length = Stream_Get_UINT32(s); /* Length (4 bytes) */
180
181 if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, (length - 4)))
182 {
183 WLog_Print(logger, WLOG_ERROR, "invalid packet length");
184 return ERROR_INVALID_DATA;
185 }
186
187 if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, 20))
188 return ERROR_INVALID_DATA;
189
190 context->remoteVersion = Stream_Get_UINT32(s);
191 const UINT64 id = Stream_Get_UINT64(s);
192 const UINT32 updateType = Stream_Get_UINT32(s);
193 Stream_Seek_UINT32(s); /* flags */
194
195 MAPPED_GEOMETRY* mappedGeometry = HashTable_GetItemValue(context->geometries, &id);
196
197 if (updateType == GEOMETRY_CLEAR)
198 {
199 if (!mappedGeometry)
200 {
201 WLog_Print(logger, WLOG_ERROR,
202 "geometry 0x%" PRIx64 " not found here, ignoring clear command", id);
203 return CHANNEL_RC_OK;
204 }
205
206 WLog_Print(logger, WLOG_DEBUG, "clearing geometry 0x%" PRIx64 "", id);
207
208 if (mappedGeometry->MappedGeometryClear &&
209 !mappedGeometry->MappedGeometryClear(mappedGeometry))
210 return ERROR_INTERNAL_ERROR;
211
212 if (!HashTable_Remove(context->geometries, &id))
213 WLog_Print(logger, WLOG_ERROR, "geometry not removed from geometries");
214 }
215 else if (updateType == GEOMETRY_UPDATE)
216 {
217 BOOL newOne = FALSE;
218
219 if (!mappedGeometry)
220 {
221 newOne = TRUE;
222 WLog_Print(logger, WLOG_DEBUG, "creating geometry 0x%" PRIx64 "", id);
223 mappedGeometry = calloc(1, sizeof(MAPPED_GEOMETRY));
224 if (!mappedGeometry)
225 return CHANNEL_RC_NO_MEMORY;
226
227 mappedGeometry->refCounter = 1;
228 mappedGeometry->mappingId = id;
229
230 if (!HashTable_Insert(context->geometries, &(mappedGeometry->mappingId),
231 mappedGeometry))
232 {
233 WLog_Print(logger, WLOG_ERROR,
234 "unable to register geometry 0x%" PRIx64 " in the table", id);
235 free(mappedGeometry);
236 return CHANNEL_RC_NO_MEMORY;
237 }
238 }
239 else
240 {
241 WLog_Print(logger, WLOG_DEBUG, "updating geometry 0x%" PRIx64 "", id);
242 }
243
244 if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, 48))
245 {
246 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert ownership mappedGeometry
247 return ERROR_INVALID_DATA;
248 }
249
250 mappedGeometry->topLevelId = Stream_Get_UINT64(s);
251
252 mappedGeometry->left = Stream_Get_INT32(s);
253 mappedGeometry->top = Stream_Get_INT32(s);
254 mappedGeometry->right = Stream_Get_INT32(s);
255 mappedGeometry->bottom = Stream_Get_INT32(s);
256
257 mappedGeometry->topLevelLeft = Stream_Get_INT32(s);
258 mappedGeometry->topLevelTop = Stream_Get_INT32(s);
259 mappedGeometry->topLevelRight = Stream_Get_INT32(s);
260 mappedGeometry->topLevelBottom = Stream_Get_INT32(s);
261
262 const UINT32 geometryType = Stream_Get_UINT32(s);
263 if (geometryType != 0x02)
264 WLog_Print(logger, WLOG_DEBUG, "geometryType should be set to 0x02 and is 0x%" PRIx32,
265 geometryType);
266
267 const UINT32 cbGeometryBuffer = Stream_Get_UINT32(s);
268 if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, cbGeometryBuffer))
269 {
270 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert ownership mappedGeometry
271 return ERROR_INVALID_DATA;
272 }
273
274 if (cbGeometryBuffer > 0)
275 {
276 ret = geometry_read_RGNDATA(logger, s, cbGeometryBuffer, &mappedGeometry->geometry);
277 if (ret != CHANNEL_RC_OK)
278 return ret;
279 }
280 else
281 {
282 freerdp_rgndata_reset(&mappedGeometry->geometry);
283 }
284
285 if (newOne)
286 {
287 if (context->MappedGeometryAdded &&
288 !context->MappedGeometryAdded(context, mappedGeometry))
289 {
290 WLog_Print(logger, WLOG_ERROR, "geometry added callback failed");
291 ret = ERROR_INTERNAL_ERROR;
292 }
293 }
294 else
295 {
296 if (mappedGeometry->MappedGeometryUpdate &&
297 !mappedGeometry->MappedGeometryUpdate(mappedGeometry))
298 {
299 WLog_Print(logger, WLOG_ERROR, "geometry update callback failed");
300 ret = ERROR_INTERNAL_ERROR;
301 }
302 }
303 }
304 else
305 {
306 WLog_Print(logger, WLOG_ERROR, "unknown updateType=%" PRIu32 "", updateType);
307 ret = CHANNEL_RC_OK;
308 }
309
310 return ret;
311}
312
318static UINT geometry_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
319{
320 GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
321 return geometry_recv_pdu(callback, data);
322}
323
329static UINT geometry_on_close(IWTSVirtualChannelCallback* pChannelCallback)
330{
331 free(pChannelCallback);
332 return CHANNEL_RC_OK;
333}
334
335static void mappedGeometryUnref_void(void* arg)
336{
337 MAPPED_GEOMETRY* g = (MAPPED_GEOMETRY*)arg;
338 mappedGeometryUnref(g);
339}
340
345static const IWTSVirtualChannelCallback geometry_callbacks = { geometry_on_data_received,
346 NULL, /* Open */
347 geometry_on_close, NULL };
348
349static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, WINPR_ATTR_UNUSED rdpContext* rcontext,
350 rdpSettings* settings)
351{
352 GeometryClientContext* context = NULL;
353 GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)base;
354
355 WINPR_ASSERT(base);
356 WINPR_UNUSED(settings);
357
358 context = (GeometryClientContext*)calloc(1, sizeof(GeometryClientContext));
359 if (!context)
360 {
361 WLog_Print(base->log, WLOG_ERROR, "calloc failed!");
362 return CHANNEL_RC_NO_MEMORY;
363 }
364
365 context->geometries = HashTable_New(FALSE);
366 if (!context->geometries)
367 {
368 WLog_Print(base->log, WLOG_ERROR, "unable to allocate geometries");
369 free(context);
370 return CHANNEL_RC_NO_MEMORY;
371 }
372
373 HashTable_SetHashFunction(context->geometries, mappedGeometryHash);
374 {
375 wObject* obj = HashTable_KeyObject(context->geometries);
376 obj->fnObjectEquals = mappedGeometryKeyCompare;
377 }
378 {
379 wObject* obj = HashTable_ValueObject(context->geometries);
380 obj->fnObjectFree = mappedGeometryUnref_void;
381 }
382 context->handle = (void*)geometry;
383
384 geometry->context = context;
385 geometry->base.iface.pInterface = (void*)context;
386
387 return CHANNEL_RC_OK;
388}
389
390static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base)
391{
392 GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)base;
393
394 if (geometry->context)
395 HashTable_Free(geometry->context->geometries);
396 free(geometry->context);
397}
398
404FREERDP_ENTRY_POINT(UINT VCAPITYPE geometry_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
405{
406 return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, GEOMETRY_DVC_CHANNEL_NAME,
407 sizeof(GEOMETRY_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
408 &geometry_callbacks, init_plugin_cb, terminate_plugin_cb);
409}
This struct contains function pointer to initialize/free objects.
Definition collections.h:57