1 /**
2 * FreeRDP: A Remote Desktop Protocol Implementation
3 * Geometry tracking Virtual Channel Extension
4 *
5 * Copyright 2017 David Fort <contact@hardening-consulting.com>
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include <winpr/crt.h>
29 #include <winpr/synch.h>
30 #include <winpr/print.h>
31 #include <winpr/stream.h>
32 #include <winpr/cmdline.h>
33 #include <winpr/collections.h>
34
35 #include <freerdp/addin.h>
36 #include <freerdp/client/geometry.h>
37 #include <freerdp/channels/log.h>
38
39 #define TAG CHANNELS_TAG("geometry.client")
40
41 #include "geometry_main.h"
42
43 struct _GEOMETRY_CHANNEL_CALLBACK
44 {
45 IWTSVirtualChannelCallback iface;
46
47 IWTSPlugin* plugin;
48 IWTSVirtualChannelManager* channel_mgr;
49 IWTSVirtualChannel* channel;
50 };
51 typedef struct _GEOMETRY_CHANNEL_CALLBACK GEOMETRY_CHANNEL_CALLBACK;
52
53 struct _GEOMETRY_LISTENER_CALLBACK
54 {
55 IWTSListenerCallback iface;
56
57 IWTSPlugin* plugin;
58 IWTSVirtualChannelManager* channel_mgr;
59 GEOMETRY_CHANNEL_CALLBACK* channel_callback;
60 };
61 typedef struct _GEOMETRY_LISTENER_CALLBACK GEOMETRY_LISTENER_CALLBACK;
62
63 struct _GEOMETRY_PLUGIN
64 {
65 IWTSPlugin iface;
66
67 IWTSListener* listener;
68 GEOMETRY_LISTENER_CALLBACK* listener_callback;
69
70 GeometryClientContext* context;
71 BOOL initialized;
72 };
73 typedef struct _GEOMETRY_PLUGIN GEOMETRY_PLUGIN;
74
mappedGeometryHash(UINT64 * g)75 static UINT32 mappedGeometryHash(UINT64* g)
76 {
77 return (UINT32)((*g >> 32) + (*g & 0xffffffff));
78 }
79
mappedGeometryKeyCompare(UINT64 * g1,UINT64 * g2)80 static BOOL mappedGeometryKeyCompare(UINT64* g1, UINT64* g2)
81 {
82 return *g1 == *g2;
83 }
84
freerdp_rgndata_reset(FREERDP_RGNDATA * data)85 static void freerdp_rgndata_reset(FREERDP_RGNDATA* data)
86 {
87 data->nRectCount = 0;
88 }
89
geometry_read_RGNDATA(wStream * s,UINT32 len,FREERDP_RGNDATA * rgndata)90 static UINT32 geometry_read_RGNDATA(wStream* s, UINT32 len, FREERDP_RGNDATA* rgndata)
91 {
92 UINT32 dwSize, iType;
93 INT32 right, bottom;
94 INT32 x, y, w, h;
95
96 if (len < 32)
97 {
98 WLog_ERR(TAG, "invalid RGNDATA");
99 return ERROR_INVALID_DATA;
100 }
101
102 Stream_Read_UINT32(s, dwSize);
103
104 if (dwSize != 32)
105 {
106 WLog_ERR(TAG, "invalid RGNDATA dwSize");
107 return ERROR_INVALID_DATA;
108 }
109
110 Stream_Read_UINT32(s, iType);
111
112 if (iType != RDH_RECTANGLE)
113 {
114 WLog_ERR(TAG, "iType %" PRIu32 " for RGNDATA is not supported", iType);
115 return ERROR_UNSUPPORTED_TYPE;
116 }
117
118 Stream_Read_UINT32(s, rgndata->nRectCount);
119 Stream_Seek_UINT32(s); /* nRgnSize IGNORED */
120 Stream_Read_INT32(s, x);
121 Stream_Read_INT32(s, y);
122 Stream_Read_INT32(s, right);
123 Stream_Read_INT32(s, bottom);
124 if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX))
125 return ERROR_INVALID_DATA;
126 w = right - x;
127 h = bottom - y;
128 if ((abs(w) > INT16_MAX) || (abs(h) > INT16_MAX))
129 return ERROR_INVALID_DATA;
130 rgndata->boundingRect.x = (INT16)x;
131 rgndata->boundingRect.y = (INT16)y;
132 rgndata->boundingRect.width = (INT16)w;
133 rgndata->boundingRect.height = (INT16)h;
134 len -= 32;
135
136 if (len / (4 * 4) < rgndata->nRectCount)
137 {
138 WLog_ERR(TAG, "not enough data for region rectangles");
139 }
140
141 if (rgndata->nRectCount)
142 {
143 UINT32 i;
144 RDP_RECT* tmp = realloc(rgndata->rects, rgndata->nRectCount * sizeof(RDP_RECT));
145
146 if (!tmp)
147 {
148 WLog_ERR(TAG, "unable to allocate memory for %" PRIu32 " RECTs", rgndata->nRectCount);
149 return CHANNEL_RC_NO_MEMORY;
150 }
151 rgndata->rects = tmp;
152
153 for (i = 0; i < rgndata->nRectCount; i++)
154 {
155 Stream_Read_INT32(s, x);
156 Stream_Read_INT32(s, y);
157 Stream_Read_INT32(s, right);
158 Stream_Read_INT32(s, bottom);
159 if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX))
160 return ERROR_INVALID_DATA;
161 w = right - x;
162 h = bottom - y;
163 if ((abs(w) > INT16_MAX) || (abs(h) > INT16_MAX))
164 return ERROR_INVALID_DATA;
165 rgndata->rects[i].x = (INT16)x;
166 rgndata->rects[i].y = (INT16)y;
167 rgndata->rects[i].width = (INT16)w;
168 rgndata->rects[i].height = (INT16)h;
169 }
170 }
171
172 return CHANNEL_RC_OK;
173 }
174
175 /**
176 * Function description
177 *
178 * @return 0 on success, otherwise a Win32 error code
179 */
geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK * callback,wStream * s)180 static UINT geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK* callback, wStream* s)
181 {
182 UINT32 length, cbGeometryBuffer;
183 MAPPED_GEOMETRY* mappedGeometry;
184 GEOMETRY_PLUGIN* geometry;
185 GeometryClientContext* context;
186 UINT ret = CHANNEL_RC_OK;
187 UINT32 version, updateType, geometryType;
188 UINT64 id;
189
190 geometry = (GEOMETRY_PLUGIN*)callback->plugin;
191 context = (GeometryClientContext*)geometry->iface.pInterface;
192
193 if (Stream_GetRemainingLength(s) < 4)
194 {
195 WLog_ERR(TAG, "not enough remaining data");
196 return ERROR_INVALID_DATA;
197 }
198
199 Stream_Read_UINT32(s, length); /* Length (4 bytes) */
200
201 if (length < 73 || Stream_GetRemainingLength(s) < (length - 4))
202 {
203 WLog_ERR(TAG, "invalid packet length");
204 return ERROR_INVALID_DATA;
205 }
206
207 Stream_Read_UINT32(s, version);
208 Stream_Read_UINT64(s, id);
209 Stream_Read_UINT32(s, updateType);
210 Stream_Seek_UINT32(s); /* flags */
211
212 mappedGeometry = HashTable_GetItemValue(context->geometries, &id);
213
214 if (updateType == GEOMETRY_CLEAR)
215 {
216 if (!mappedGeometry)
217 {
218 WLog_ERR(TAG, "geometry 0x%" PRIx64 " not found here, ignoring clear command", id);
219 return CHANNEL_RC_OK;
220 }
221
222 WLog_DBG(TAG, "clearing geometry 0x%" PRIx64 "", id);
223
224 if (mappedGeometry->MappedGeometryClear &&
225 !mappedGeometry->MappedGeometryClear(mappedGeometry))
226 return ERROR_INTERNAL_ERROR;
227
228 if (!HashTable_Remove(context->geometries, &id))
229 WLog_ERR(TAG, "geometry not removed from geometries");
230 }
231 else if (updateType == GEOMETRY_UPDATE)
232 {
233 BOOL newOne = FALSE;
234
235 if (!mappedGeometry)
236 {
237 newOne = TRUE;
238 WLog_DBG(TAG, "creating geometry 0x%" PRIx64 "", id);
239 mappedGeometry = calloc(1, sizeof(MAPPED_GEOMETRY));
240 if (!mappedGeometry)
241 return CHANNEL_RC_NO_MEMORY;
242
243 mappedGeometry->refCounter = 1;
244 mappedGeometry->mappingId = id;
245
246 if (HashTable_Add(context->geometries, &(mappedGeometry->mappingId), mappedGeometry) <
247 0)
248 {
249 WLog_ERR(TAG, "unable to register geometry 0x%" PRIx64 " in the table", id);
250 free(mappedGeometry);
251 return CHANNEL_RC_NO_MEMORY;
252 }
253 }
254 else
255 {
256 WLog_DBG(TAG, "updating geometry 0x%" PRIx64 "", id);
257 }
258
259 Stream_Read_UINT64(s, mappedGeometry->topLevelId);
260
261 Stream_Read_INT32(s, mappedGeometry->left);
262 Stream_Read_INT32(s, mappedGeometry->top);
263 Stream_Read_INT32(s, mappedGeometry->right);
264 Stream_Read_INT32(s, mappedGeometry->bottom);
265
266 Stream_Read_INT32(s, mappedGeometry->topLevelLeft);
267 Stream_Read_INT32(s, mappedGeometry->topLevelTop);
268 Stream_Read_INT32(s, mappedGeometry->topLevelRight);
269 Stream_Read_INT32(s, mappedGeometry->topLevelBottom);
270
271 Stream_Read_UINT32(s, geometryType);
272
273 Stream_Read_UINT32(s, cbGeometryBuffer);
274 if (Stream_GetRemainingLength(s) < cbGeometryBuffer)
275 {
276 WLog_ERR(TAG, "invalid packet length");
277 return ERROR_INVALID_DATA;
278 }
279
280 if (cbGeometryBuffer)
281 {
282 ret = geometry_read_RGNDATA(s, cbGeometryBuffer, &mappedGeometry->geometry);
283 if (ret != CHANNEL_RC_OK)
284 return ret;
285 }
286 else
287 {
288 freerdp_rgndata_reset(&mappedGeometry->geometry);
289 }
290
291 if (newOne)
292 {
293 if (context->MappedGeometryAdded &&
294 !context->MappedGeometryAdded(context, mappedGeometry))
295 {
296 WLog_ERR(TAG, "geometry added callback failed");
297 ret = ERROR_INTERNAL_ERROR;
298 }
299 }
300 else
301 {
302 if (mappedGeometry->MappedGeometryUpdate &&
303 !mappedGeometry->MappedGeometryUpdate(mappedGeometry))
304 {
305 WLog_ERR(TAG, "geometry update callback failed");
306 ret = ERROR_INTERNAL_ERROR;
307 }
308 }
309 }
310 else
311 {
312 WLog_ERR(TAG, "unknown updateType=%" PRIu32 "", updateType);
313 ret = CHANNEL_RC_OK;
314 }
315
316 return ret;
317 }
318
319 /**
320 * Function description
321 *
322 * @return 0 on success, otherwise a Win32 error code
323 */
geometry_on_data_received(IWTSVirtualChannelCallback * pChannelCallback,wStream * data)324 static UINT geometry_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
325 {
326 GEOMETRY_CHANNEL_CALLBACK* callback = (GEOMETRY_CHANNEL_CALLBACK*)pChannelCallback;
327 return geometry_recv_pdu(callback, data);
328 }
329
330 /**
331 * Function description
332 *
333 * @return 0 on success, otherwise a Win32 error code
334 */
geometry_on_close(IWTSVirtualChannelCallback * pChannelCallback)335 static UINT geometry_on_close(IWTSVirtualChannelCallback* pChannelCallback)
336 {
337 free(pChannelCallback);
338 return CHANNEL_RC_OK;
339 }
340
341 /**
342 * Function description
343 *
344 * @return 0 on success, otherwise a Win32 error code
345 */
geometry_on_new_channel_connection(IWTSListenerCallback * pListenerCallback,IWTSVirtualChannel * pChannel,BYTE * Data,BOOL * pbAccept,IWTSVirtualChannelCallback ** ppCallback)346 static UINT geometry_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
347 IWTSVirtualChannel* pChannel, BYTE* Data,
348 BOOL* pbAccept,
349 IWTSVirtualChannelCallback** ppCallback)
350 {
351 GEOMETRY_CHANNEL_CALLBACK* callback;
352 GEOMETRY_LISTENER_CALLBACK* listener_callback = (GEOMETRY_LISTENER_CALLBACK*)pListenerCallback;
353
354 WINPR_UNUSED(Data);
355 WINPR_UNUSED(pbAccept);
356
357 callback = (GEOMETRY_CHANNEL_CALLBACK*)calloc(1, sizeof(GEOMETRY_CHANNEL_CALLBACK));
358
359 if (!callback)
360 {
361 WLog_ERR(TAG, "calloc failed!");
362 return CHANNEL_RC_NO_MEMORY;
363 }
364
365 callback->iface.OnDataReceived = geometry_on_data_received;
366 callback->iface.OnClose = geometry_on_close;
367 callback->plugin = listener_callback->plugin;
368 callback->channel_mgr = listener_callback->channel_mgr;
369 callback->channel = pChannel;
370 listener_callback->channel_callback = callback;
371 *ppCallback = (IWTSVirtualChannelCallback*)callback;
372 return CHANNEL_RC_OK;
373 }
374
375 /**
376 * Function description
377 *
378 * @return 0 on success, otherwise a Win32 error code
379 */
geometry_plugin_initialize(IWTSPlugin * pPlugin,IWTSVirtualChannelManager * pChannelMgr)380 static UINT geometry_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
381 {
382 UINT status;
383 GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)pPlugin;
384 if (geometry->initialized)
385 {
386 WLog_ERR(TAG, "[%s] channel initialized twice, aborting", GEOMETRY_DVC_CHANNEL_NAME);
387 return ERROR_INVALID_DATA;
388 }
389 geometry->listener_callback =
390 (GEOMETRY_LISTENER_CALLBACK*)calloc(1, sizeof(GEOMETRY_LISTENER_CALLBACK));
391
392 if (!geometry->listener_callback)
393 {
394 WLog_ERR(TAG, "calloc failed!");
395 return CHANNEL_RC_NO_MEMORY;
396 }
397
398 geometry->listener_callback->iface.OnNewChannelConnection = geometry_on_new_channel_connection;
399 geometry->listener_callback->plugin = pPlugin;
400 geometry->listener_callback->channel_mgr = pChannelMgr;
401 status =
402 pChannelMgr->CreateListener(pChannelMgr, GEOMETRY_DVC_CHANNEL_NAME, 0,
403 &geometry->listener_callback->iface, &(geometry->listener));
404 geometry->listener->pInterface = geometry->iface.pInterface;
405
406 geometry->initialized = status == CHANNEL_RC_OK;
407 return status;
408 }
409
410 /**
411 * Function description
412 *
413 * @return 0 on success, otherwise a Win32 error code
414 */
geometry_plugin_terminated(IWTSPlugin * pPlugin)415 static UINT geometry_plugin_terminated(IWTSPlugin* pPlugin)
416 {
417 GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)pPlugin;
418 GeometryClientContext* context = (GeometryClientContext*)geometry->iface.pInterface;
419
420 if (geometry && geometry->listener_callback)
421 {
422 IWTSVirtualChannelManager* mgr = geometry->listener_callback->channel_mgr;
423 if (mgr)
424 IFCALL(mgr->DestroyListener, mgr, geometry->listener);
425 }
426
427 if (context)
428 HashTable_Free(context->geometries);
429
430 free(geometry->listener_callback);
431 free(geometry->iface.pInterface);
432 free(pPlugin);
433 return CHANNEL_RC_OK;
434 }
435
436 /**
437 * Channel Client Interface
438 */
439
440 #ifdef BUILTIN_CHANNELS
441 #define DVCPluginEntry geometry_DVCPluginEntry
442 #else
443 #define DVCPluginEntry FREERDP_API DVCPluginEntry
444 #endif
445
446 /**
447 * Function description
448 *
449 * @return 0 on success, otherwise a Win32 error code
450 */
DVCPluginEntry(IDRDYNVC_ENTRY_POINTS * pEntryPoints)451 UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
452 {
453 UINT error = CHANNEL_RC_OK;
454 GEOMETRY_PLUGIN* geometry;
455 GeometryClientContext* context;
456 geometry = (GEOMETRY_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, "geometry");
457
458 if (!geometry)
459 {
460 geometry = (GEOMETRY_PLUGIN*)calloc(1, sizeof(GEOMETRY_PLUGIN));
461
462 if (!geometry)
463 {
464 WLog_ERR(TAG, "calloc failed!");
465 return CHANNEL_RC_NO_MEMORY;
466 }
467
468 geometry->iface.Initialize = geometry_plugin_initialize;
469 geometry->iface.Connected = NULL;
470 geometry->iface.Disconnected = NULL;
471 geometry->iface.Terminated = geometry_plugin_terminated;
472 context = (GeometryClientContext*)calloc(1, sizeof(GeometryClientContext));
473
474 if (!context)
475 {
476 WLog_ERR(TAG, "calloc failed!");
477 goto error_context;
478 }
479
480 context->geometries = HashTable_New(FALSE);
481 context->geometries->hash = (HASH_TABLE_HASH_FN)mappedGeometryHash;
482 context->geometries->keyCompare = (HASH_TABLE_KEY_COMPARE_FN)mappedGeometryKeyCompare;
483 context->geometries->valueFree = (HASH_TABLE_VALUE_FREE_FN)mappedGeometryUnref;
484
485 context->handle = (void*)geometry;
486 geometry->iface.pInterface = (void*)context;
487 geometry->context = context;
488 error = pEntryPoints->RegisterPlugin(pEntryPoints, "geometry", (IWTSPlugin*)geometry);
489 }
490 else
491 {
492 WLog_ERR(TAG, "could not get geometry Plugin.");
493 return CHANNEL_RC_BAD_CHANNEL;
494 }
495
496 return error;
497
498 error_context:
499 free(geometry);
500 return CHANNEL_RC_NO_MEMORY;
501 }
502