FreeRDP
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 
42 typedef struct
43 {
45  GeometryClientContext* context;
46 } GEOMETRY_PLUGIN;
47 
48 static UINT32 mappedGeometryHash(const void* v)
49 {
50  const UINT64* g = (const UINT64*)v;
51  return (UINT32)((*g >> 32) + (*g & 0xffffffff));
52 }
53 
54 static 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 
61 static void freerdp_rgndata_reset(FREERDP_RGNDATA* data)
62 {
63  data->nRectCount = 0;
64 }
65 
66 static UINT32 geometry_read_RGNDATA(wLog* logger, wStream* s, UINT32 len, FREERDP_RGNDATA* rgndata)
67 {
68  UINT32 dwSize = 0;
69  UINT32 iType = 0;
70  INT32 right = 0;
71  INT32 bottom = 0;
72  INT32 x = 0;
73  INT32 y = 0;
74  INT32 w = 0;
75  INT32 h = 0;
76 
77  if (len < 32)
78  {
79  WLog_Print(logger, WLOG_ERROR, "invalid RGNDATA");
80  return ERROR_INVALID_DATA;
81  }
82 
83  Stream_Read_UINT32(s, dwSize);
84 
85  if (dwSize != 32)
86  {
87  WLog_Print(logger, WLOG_ERROR, "invalid RGNDATA dwSize");
88  return ERROR_INVALID_DATA;
89  }
90 
91  Stream_Read_UINT32(s, iType);
92 
93  if (iType != RDH_RECTANGLE)
94  {
95  WLog_Print(logger, WLOG_ERROR, "iType %" PRIu32 " for RGNDATA is not supported", iType);
96  return ERROR_UNSUPPORTED_TYPE;
97  }
98 
99  Stream_Read_UINT32(s, rgndata->nRectCount);
100  Stream_Seek_UINT32(s); /* nRgnSize IGNORED */
101  Stream_Read_INT32(s, x);
102  Stream_Read_INT32(s, y);
103  Stream_Read_INT32(s, right);
104  Stream_Read_INT32(s, bottom);
105  if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX))
106  return ERROR_INVALID_DATA;
107  w = right - x;
108  h = bottom - y;
109  if ((abs(w) > INT16_MAX) || (abs(h) > INT16_MAX))
110  return ERROR_INVALID_DATA;
111  rgndata->boundingRect.x = (INT16)x;
112  rgndata->boundingRect.y = (INT16)y;
113  rgndata->boundingRect.width = (INT16)w;
114  rgndata->boundingRect.height = (INT16)h;
115  len -= 32;
116 
117  if (len / (4 * 4) < rgndata->nRectCount)
118  {
119  WLog_Print(logger, WLOG_ERROR, "not enough data for region rectangles");
120  return ERROR_INVALID_DATA;
121  }
122 
123  if (rgndata->nRectCount)
124  {
125  RDP_RECT* tmp = realloc(rgndata->rects, rgndata->nRectCount * sizeof(RDP_RECT));
126 
127  if (!tmp)
128  {
129  WLog_Print(logger, WLOG_ERROR, "unable to allocate memory for %" PRIu32 " RECTs",
130  rgndata->nRectCount);
131  return CHANNEL_RC_NO_MEMORY;
132  }
133  rgndata->rects = tmp;
134 
135  for (UINT32 i = 0; i < rgndata->nRectCount; i++)
136  {
137  Stream_Read_INT32(s, x);
138  Stream_Read_INT32(s, y);
139  Stream_Read_INT32(s, right);
140  Stream_Read_INT32(s, bottom);
141  if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX))
142  return ERROR_INVALID_DATA;
143  w = right - x;
144  h = bottom - y;
145  if ((abs(w) > INT16_MAX) || (abs(h) > INT16_MAX))
146  return ERROR_INVALID_DATA;
147  rgndata->rects[i].x = (INT16)x;
148  rgndata->rects[i].y = (INT16)y;
149  rgndata->rects[i].width = (INT16)w;
150  rgndata->rects[i].height = (INT16)h;
151  }
152  }
153 
154  return CHANNEL_RC_OK;
155 }
156 
162 static UINT geometry_recv_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
163 {
164  UINT32 length = 0;
165  UINT32 cbGeometryBuffer = 0;
166  MAPPED_GEOMETRY* mappedGeometry = NULL;
167  GEOMETRY_PLUGIN* geometry = NULL;
168  GeometryClientContext* context = NULL;
169  UINT ret = CHANNEL_RC_OK;
170  UINT32 updateType = 0;
171  UINT32 geometryType = 0;
172  UINT64 id = 0;
173  wLog* logger = NULL;
174 
175  geometry = (GEOMETRY_PLUGIN*)callback->plugin;
176  logger = geometry->base.log;
177  context = (GeometryClientContext*)geometry->base.iface.pInterface;
178 
179  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
180  return ERROR_INVALID_DATA;
181 
182  Stream_Read_UINT32(s, length); /* Length (4 bytes) */
183 
184  if (length < 73 || !Stream_CheckAndLogRequiredLength(TAG, s, (length - 4)))
185  {
186  WLog_Print(logger, WLOG_ERROR, "invalid packet length");
187  return ERROR_INVALID_DATA;
188  }
189 
190  Stream_Read_UINT32(s, context->remoteVersion);
191  Stream_Read_UINT64(s, id);
192  Stream_Read_UINT32(s, updateType);
193  Stream_Seek_UINT32(s); /* flags */
194 
195  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  Stream_Read_UINT64(s, mappedGeometry->topLevelId);
245 
246  Stream_Read_INT32(s, mappedGeometry->left);
247  Stream_Read_INT32(s, mappedGeometry->top);
248  Stream_Read_INT32(s, mappedGeometry->right);
249  Stream_Read_INT32(s, mappedGeometry->bottom);
250 
251  Stream_Read_INT32(s, mappedGeometry->topLevelLeft);
252  Stream_Read_INT32(s, mappedGeometry->topLevelTop);
253  Stream_Read_INT32(s, mappedGeometry->topLevelRight);
254  Stream_Read_INT32(s, mappedGeometry->topLevelBottom);
255 
256  Stream_Read_UINT32(s, geometryType);
257  if (geometryType != 0x02)
258  WLog_Print(logger, WLOG_DEBUG, "geometryType should be set to 0x02 and is 0x%" PRIx32,
259  geometryType);
260 
261  Stream_Read_UINT32(s, cbGeometryBuffer);
262  if (!Stream_CheckAndLogRequiredLength(TAG, s, cbGeometryBuffer))
263  {
264  // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert ownership mappedGeometry
265  return ERROR_INVALID_DATA;
266  }
267 
268  if (cbGeometryBuffer)
269  {
270  ret = geometry_read_RGNDATA(logger, s, cbGeometryBuffer, &mappedGeometry->geometry);
271  if (ret != CHANNEL_RC_OK)
272  return ret;
273  }
274  else
275  {
276  freerdp_rgndata_reset(&mappedGeometry->geometry);
277  }
278 
279  if (newOne)
280  {
281  if (context->MappedGeometryAdded &&
282  !context->MappedGeometryAdded(context, mappedGeometry))
283  {
284  WLog_Print(logger, WLOG_ERROR, "geometry added callback failed");
285  ret = ERROR_INTERNAL_ERROR;
286  }
287  }
288  else
289  {
290  if (mappedGeometry->MappedGeometryUpdate &&
291  !mappedGeometry->MappedGeometryUpdate(mappedGeometry))
292  {
293  WLog_Print(logger, WLOG_ERROR, "geometry update callback failed");
294  ret = ERROR_INTERNAL_ERROR;
295  }
296  }
297  }
298  else
299  {
300  WLog_Print(logger, WLOG_ERROR, "unknown updateType=%" PRIu32 "", updateType);
301  ret = CHANNEL_RC_OK;
302  }
303 
304  return ret;
305 }
306 
312 static UINT geometry_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
313 {
314  GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
315  return geometry_recv_pdu(callback, data);
316 }
317 
323 static UINT geometry_on_close(IWTSVirtualChannelCallback* pChannelCallback)
324 {
325  free(pChannelCallback);
326  return CHANNEL_RC_OK;
327 }
328 
329 static void mappedGeometryUnref_void(void* arg)
330 {
331  MAPPED_GEOMETRY* g = (MAPPED_GEOMETRY*)arg;
332  mappedGeometryUnref(g);
333 }
334 
339 static const IWTSVirtualChannelCallback geometry_callbacks = { geometry_on_data_received,
340  NULL, /* Open */
341  geometry_on_close, NULL };
342 
343 static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, rdpContext* rcontext, rdpSettings* settings)
344 {
345  GeometryClientContext* context = NULL;
346  GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)base;
347 
348  WINPR_ASSERT(base);
349  WINPR_UNUSED(settings);
350 
351  context = (GeometryClientContext*)calloc(1, sizeof(GeometryClientContext));
352  if (!context)
353  {
354  WLog_Print(base->log, WLOG_ERROR, "calloc failed!");
355  return CHANNEL_RC_NO_MEMORY;
356  }
357 
358  context->geometries = HashTable_New(FALSE);
359  if (!context->geometries)
360  {
361  WLog_Print(base->log, WLOG_ERROR, "unable to allocate geometries");
362  free(context);
363  return CHANNEL_RC_NO_MEMORY;
364  }
365 
366  HashTable_SetHashFunction(context->geometries, mappedGeometryHash);
367  {
368  wObject* obj = HashTable_KeyObject(context->geometries);
369  obj->fnObjectEquals = mappedGeometryKeyCompare;
370  }
371  {
372  wObject* obj = HashTable_ValueObject(context->geometries);
373  obj->fnObjectFree = mappedGeometryUnref_void;
374  }
375  context->handle = (void*)geometry;
376 
377  geometry->context = context;
378  geometry->base.iface.pInterface = (void*)context;
379 
380  return CHANNEL_RC_OK;
381 }
382 
383 static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base)
384 {
385  GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)base;
386 
387  if (geometry->context)
388  HashTable_Free(geometry->context->geometries);
389  free(geometry->context);
390 }
391 
397 FREERDP_ENTRY_POINT(UINT VCAPITYPE geometry_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
398 {
399  return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, GEOMETRY_DVC_CHANNEL_NAME,
400  sizeof(GEOMETRY_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
401  &geometry_callbacks, init_plugin_cb, terminate_plugin_cb);
402 }
This struct contains function pointer to initialize/free objects.
Definition: collections.h:57