FreeRDP
camera_device_enumerator_main.c
1 
20 #include <winpr/cast.h>
21 
22 #include <freerdp/config.h>
23 
24 #include <freerdp/freerdp.h>
25 #include <freerdp/channels/log.h>
26 #include <freerdp/server/rdpecam-enumerator.h>
27 
28 #define TAG CHANNELS_TAG("rdpecam-enumerator.server")
29 
30 typedef enum
31 {
32  ENUMERATOR_INITIAL,
33  ENUMERATOR_OPENED,
34 } eEnumeratorChannelState;
35 
36 typedef struct
37 {
38  CamDevEnumServerContext context;
39 
40  HANDLE stopEvent;
41 
42  HANDLE thread;
43  void* enumerator_channel;
44 
45  DWORD SessionId;
46 
47  BOOL isOpened;
48  BOOL externalThread;
49 
50  /* Channel state */
51  eEnumeratorChannelState state;
52 
53  wStream* buffer;
54 } enumerator_server;
55 
56 static UINT enumerator_server_initialize(CamDevEnumServerContext* context, BOOL externalThread)
57 {
58  UINT error = CHANNEL_RC_OK;
59  enumerator_server* enumerator = (enumerator_server*)context;
60 
61  WINPR_ASSERT(enumerator);
62 
63  if (enumerator->isOpened)
64  {
65  WLog_WARN(TAG, "Application error: Camera Device Enumerator channel already initialized, "
66  "calling in this state is not possible!");
67  return ERROR_INVALID_STATE;
68  }
69 
70  enumerator->externalThread = externalThread;
71 
72  return error;
73 }
74 
75 static UINT enumerator_server_open_channel(enumerator_server* enumerator)
76 {
77  CamDevEnumServerContext* context = &enumerator->context;
78  DWORD Error = ERROR_SUCCESS;
79  HANDLE hEvent = NULL;
80  DWORD BytesReturned = 0;
81  PULONG pSessionId = NULL;
82  UINT32 channelId = 0;
83  BOOL status = TRUE;
84 
85  WINPR_ASSERT(enumerator);
86 
87  if (WTSQuerySessionInformationA(enumerator->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
88  (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
89  {
90  WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
91  return ERROR_INTERNAL_ERROR;
92  }
93 
94  enumerator->SessionId = (DWORD)*pSessionId;
95  WTSFreeMemory(pSessionId);
96  hEvent = WTSVirtualChannelManagerGetEventHandle(enumerator->context.vcm);
97 
98  if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
99  {
100  Error = GetLastError();
101  WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
102  return Error;
103  }
104 
105  enumerator->enumerator_channel = WTSVirtualChannelOpenEx(
106  enumerator->SessionId, RDPECAM_CONTROL_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
107  if (!enumerator->enumerator_channel)
108  {
109  Error = GetLastError();
110  WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error);
111  return Error;
112  }
113 
114  channelId = WTSChannelGetIdByHandle(enumerator->enumerator_channel);
115 
116  IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
117  if (!status)
118  {
119  WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
120  return ERROR_INTERNAL_ERROR;
121  }
122 
123  return Error;
124 }
125 
126 static UINT enumerator_server_handle_select_version_request(CamDevEnumServerContext* context,
127  wStream* s,
128  const CAM_SHARED_MSG_HEADER* header)
129 {
130  CAM_SELECT_VERSION_REQUEST pdu = { 0 };
131  UINT error = CHANNEL_RC_OK;
132 
133  WINPR_ASSERT(context);
134  WINPR_ASSERT(header);
135 
136  pdu.Header = *header;
137 
138  IFCALLRET(context->SelectVersionRequest, error, context, &pdu);
139  if (error)
140  WLog_ERR(TAG, "context->SelectVersionRequest failed with error %" PRIu32 "", error);
141 
142  return error;
143 }
144 
145 static UINT enumerator_server_recv_device_added_notification(CamDevEnumServerContext* context,
146  wStream* s,
147  const CAM_SHARED_MSG_HEADER* header)
148 {
150  UINT error = CHANNEL_RC_OK;
151  size_t remaining_length = 0;
152  WCHAR* channel_name_start = 0;
153 
154  WINPR_ASSERT(context);
155  WINPR_ASSERT(header);
156 
157  pdu.Header = *header;
158 
159  /*
160  * RequiredLength 4:
161  *
162  * Nullterminator DeviceName (2),
163  * VirtualChannelName (>= 1),
164  * Nullterminator VirtualChannelName (1)
165  */
166  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
167  return ERROR_NO_DATA;
168 
169  pdu.DeviceName = Stream_Pointer(s);
170 
171  remaining_length = Stream_GetRemainingLength(s);
172  channel_name_start = Stream_Pointer(s);
173 
174  /* Search for null terminator of DeviceName */
175  size_t i = 0;
176  for (; i < remaining_length; i += sizeof(WCHAR), ++channel_name_start)
177  {
178  if (*channel_name_start == L'\0')
179  break;
180  }
181 
182  if (*channel_name_start != L'\0')
183  {
184  WLog_ERR(TAG, "enumerator_server_recv_device_added_notification: Invalid DeviceName!");
185  return ERROR_INVALID_DATA;
186  }
187 
188  pdu.VirtualChannelName = (char*)++channel_name_start;
189  ++i;
190 
191  if (i >= remaining_length || *pdu.VirtualChannelName == '\0')
192  {
193  WLog_ERR(TAG,
194  "enumerator_server_recv_device_added_notification: Invalid VirtualChannelName!");
195  return ERROR_INVALID_DATA;
196  }
197 
198  char* tmp = pdu.VirtualChannelName;
199  for (; i < remaining_length; ++i, ++tmp)
200  {
201  if (*tmp == '\0')
202  break;
203  }
204 
205  if (*tmp != '\0')
206  {
207  WLog_ERR(TAG,
208  "enumerator_server_recv_device_added_notification: Invalid VirtualChannelName!");
209  return ERROR_INVALID_DATA;
210  }
211 
212  IFCALLRET(context->DeviceAddedNotification, error, context, &pdu);
213  if (error)
214  WLog_ERR(TAG, "context->DeviceAddedNotification failed with error %" PRIu32 "", error);
215 
216  return error;
217 }
218 
219 static UINT enumerator_server_recv_device_removed_notification(CamDevEnumServerContext* context,
220  wStream* s,
221  const CAM_SHARED_MSG_HEADER* header)
222 {
224  UINT error = CHANNEL_RC_OK;
225  size_t remaining_length = 0;
226 
227  WINPR_ASSERT(context);
228  WINPR_ASSERT(header);
229 
230  pdu.Header = *header;
231 
232  if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
233  return ERROR_NO_DATA;
234 
235  pdu.VirtualChannelName = Stream_Pointer(s);
236 
237  remaining_length = Stream_GetRemainingLength(s);
238  char* tmp = pdu.VirtualChannelName + 1;
239 
240  for (size_t i = 1; i < remaining_length; ++i, ++tmp)
241  {
242  if (*tmp == '\0')
243  break;
244  }
245 
246  if (*tmp != '\0')
247  {
248  WLog_ERR(TAG,
249  "enumerator_server_recv_device_removed_notification: Invalid VirtualChannelName!");
250  return ERROR_INVALID_DATA;
251  }
252 
253  IFCALLRET(context->DeviceRemovedNotification, error, context, &pdu);
254  if (error)
255  WLog_ERR(TAG, "context->DeviceRemovedNotification failed with error %" PRIu32 "", error);
256 
257  return error;
258 }
259 
260 static UINT enumerator_process_message(enumerator_server* enumerator)
261 {
262  BOOL rc = 0;
263  UINT error = ERROR_INTERNAL_ERROR;
264  ULONG BytesReturned = 0;
265  CAM_SHARED_MSG_HEADER header = { 0 };
266  wStream* s = NULL;
267 
268  WINPR_ASSERT(enumerator);
269  WINPR_ASSERT(enumerator->enumerator_channel);
270 
271  s = enumerator->buffer;
272  WINPR_ASSERT(s);
273 
274  Stream_SetPosition(s, 0);
275  rc = WTSVirtualChannelRead(enumerator->enumerator_channel, 0, NULL, 0, &BytesReturned);
276  if (!rc)
277  goto out;
278 
279  if (BytesReturned < 1)
280  {
281  error = CHANNEL_RC_OK;
282  goto out;
283  }
284 
285  if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
286  {
287  WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
288  error = CHANNEL_RC_NO_MEMORY;
289  goto out;
290  }
291 
292  if (WTSVirtualChannelRead(enumerator->enumerator_channel, 0, Stream_BufferAs(s, char),
293  (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
294  {
295  WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
296  goto out;
297  }
298 
299  Stream_SetLength(s, BytesReturned);
300  if (!Stream_CheckAndLogRequiredLength(TAG, s, CAM_HEADER_SIZE))
301  return ERROR_NO_DATA;
302 
303  Stream_Read_UINT8(s, header.Version);
304  Stream_Read_UINT8(s, header.MessageId);
305 
306  switch (header.MessageId)
307  {
308  case CAM_MSG_ID_SelectVersionRequest:
309  error =
310  enumerator_server_handle_select_version_request(&enumerator->context, s, &header);
311  break;
312  case CAM_MSG_ID_DeviceAddedNotification:
313  error =
314  enumerator_server_recv_device_added_notification(&enumerator->context, s, &header);
315  break;
316  case CAM_MSG_ID_DeviceRemovedNotification:
317  error = enumerator_server_recv_device_removed_notification(&enumerator->context, s,
318  &header);
319  break;
320  default:
321  WLog_ERR(TAG, "enumerator_process_message: unknown or invalid MessageId %" PRIu8 "",
322  header.MessageId);
323  break;
324  }
325 
326 out:
327  if (error)
328  WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
329 
330  return error;
331 }
332 
333 static UINT enumerator_server_context_poll_int(CamDevEnumServerContext* context)
334 {
335  enumerator_server* enumerator = (enumerator_server*)context;
336  UINT error = ERROR_INTERNAL_ERROR;
337 
338  WINPR_ASSERT(enumerator);
339 
340  switch (enumerator->state)
341  {
342  case ENUMERATOR_INITIAL:
343  error = enumerator_server_open_channel(enumerator);
344  if (error)
345  WLog_ERR(TAG, "enumerator_server_open_channel failed with error %" PRIu32 "!",
346  error);
347  else
348  enumerator->state = ENUMERATOR_OPENED;
349  break;
350  case ENUMERATOR_OPENED:
351  error = enumerator_process_message(enumerator);
352  break;
353  default:
354  break;
355  }
356 
357  return error;
358 }
359 
360 static HANDLE enumerator_server_get_channel_handle(enumerator_server* enumerator)
361 {
362  void* buffer = NULL;
363  DWORD BytesReturned = 0;
364  HANDLE ChannelEvent = NULL;
365 
366  WINPR_ASSERT(enumerator);
367 
368  if (WTSVirtualChannelQuery(enumerator->enumerator_channel, WTSVirtualEventHandle, &buffer,
369  &BytesReturned) == TRUE)
370  {
371  if (BytesReturned == sizeof(HANDLE))
372  ChannelEvent = *(HANDLE*)buffer;
373 
374  WTSFreeMemory(buffer);
375  }
376 
377  return ChannelEvent;
378 }
379 
380 static DWORD WINAPI enumerator_server_thread_func(LPVOID arg)
381 {
382  DWORD nCount = 0;
383  HANDLE events[2] = { 0 };
384  enumerator_server* enumerator = (enumerator_server*)arg;
385  UINT error = CHANNEL_RC_OK;
386  DWORD status = 0;
387 
388  WINPR_ASSERT(enumerator);
389 
390  nCount = 0;
391  events[nCount++] = enumerator->stopEvent;
392 
393  while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
394  {
395  switch (enumerator->state)
396  {
397  case ENUMERATOR_INITIAL:
398  error = enumerator_server_context_poll_int(&enumerator->context);
399  if (error == CHANNEL_RC_OK)
400  {
401  events[1] = enumerator_server_get_channel_handle(enumerator);
402  nCount = 2;
403  }
404  break;
405  case ENUMERATOR_OPENED:
406  status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
407  switch (status)
408  {
409  case WAIT_OBJECT_0:
410  break;
411  case WAIT_OBJECT_0 + 1:
412  case WAIT_TIMEOUT:
413  error = enumerator_server_context_poll_int(&enumerator->context);
414  break;
415 
416  case WAIT_FAILED:
417  default:
418  error = ERROR_INTERNAL_ERROR;
419  break;
420  }
421  break;
422  default:
423  break;
424  }
425  }
426 
427  (void)WTSVirtualChannelClose(enumerator->enumerator_channel);
428  enumerator->enumerator_channel = NULL;
429 
430  if (error && enumerator->context.rdpcontext)
431  setChannelError(enumerator->context.rdpcontext, error,
432  "enumerator_server_thread_func reported an error");
433 
434  ExitThread(error);
435  return error;
436 }
437 
438 static UINT enumerator_server_open(CamDevEnumServerContext* context)
439 {
440  enumerator_server* enumerator = (enumerator_server*)context;
441 
442  WINPR_ASSERT(enumerator);
443 
444  if (!enumerator->externalThread && (enumerator->thread == NULL))
445  {
446  enumerator->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
447  if (!enumerator->stopEvent)
448  {
449  WLog_ERR(TAG, "CreateEvent failed!");
450  return ERROR_INTERNAL_ERROR;
451  }
452 
453  enumerator->thread =
454  CreateThread(NULL, 0, enumerator_server_thread_func, enumerator, 0, NULL);
455  if (!enumerator->thread)
456  {
457  WLog_ERR(TAG, "CreateThread failed!");
458  (void)CloseHandle(enumerator->stopEvent);
459  enumerator->stopEvent = NULL;
460  return ERROR_INTERNAL_ERROR;
461  }
462  }
463  enumerator->isOpened = TRUE;
464 
465  return CHANNEL_RC_OK;
466 }
467 
468 static UINT enumerator_server_close(CamDevEnumServerContext* context)
469 {
470  UINT error = CHANNEL_RC_OK;
471  enumerator_server* enumerator = (enumerator_server*)context;
472 
473  WINPR_ASSERT(enumerator);
474 
475  if (!enumerator->externalThread && enumerator->thread)
476  {
477  (void)SetEvent(enumerator->stopEvent);
478 
479  if (WaitForSingleObject(enumerator->thread, INFINITE) == WAIT_FAILED)
480  {
481  error = GetLastError();
482  WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
483  return error;
484  }
485 
486  (void)CloseHandle(enumerator->thread);
487  (void)CloseHandle(enumerator->stopEvent);
488  enumerator->thread = NULL;
489  enumerator->stopEvent = NULL;
490  }
491  if (enumerator->externalThread)
492  {
493  if (enumerator->state != ENUMERATOR_INITIAL)
494  {
495  (void)WTSVirtualChannelClose(enumerator->enumerator_channel);
496  enumerator->enumerator_channel = NULL;
497  enumerator->state = ENUMERATOR_INITIAL;
498  }
499  }
500  enumerator->isOpened = FALSE;
501 
502  return error;
503 }
504 
505 static UINT enumerator_server_context_poll(CamDevEnumServerContext* context)
506 {
507  enumerator_server* enumerator = (enumerator_server*)context;
508 
509  WINPR_ASSERT(enumerator);
510 
511  if (!enumerator->externalThread)
512  return ERROR_INTERNAL_ERROR;
513 
514  return enumerator_server_context_poll_int(context);
515 }
516 
517 static BOOL enumerator_server_context_handle(CamDevEnumServerContext* context, HANDLE* handle)
518 {
519  enumerator_server* enumerator = (enumerator_server*)context;
520 
521  WINPR_ASSERT(enumerator);
522  WINPR_ASSERT(handle);
523 
524  if (!enumerator->externalThread)
525  return FALSE;
526  if (enumerator->state == ENUMERATOR_INITIAL)
527  return FALSE;
528 
529  *handle = enumerator_server_get_channel_handle(enumerator);
530 
531  return TRUE;
532 }
533 
534 static UINT enumerator_server_packet_send(CamDevEnumServerContext* context, wStream* s)
535 {
536  enumerator_server* enumerator = (enumerator_server*)context;
537  UINT error = CHANNEL_RC_OK;
538  ULONG written = 0;
539 
540  const size_t len = Stream_GetPosition(s);
541  WINPR_ASSERT(len <= UINT32_MAX);
542  if (!WTSVirtualChannelWrite(enumerator->enumerator_channel, Stream_BufferAs(s, char),
543  (UINT32)len, &written))
544  {
545  WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
546  error = ERROR_INTERNAL_ERROR;
547  goto out;
548  }
549 
550  if (written < Stream_GetPosition(s))
551  {
552  WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
553  Stream_GetPosition(s));
554  }
555 
556 out:
557  Stream_Free(s, TRUE);
558  return error;
559 }
560 
561 static UINT enumerator_send_select_version_response_pdu(
562  CamDevEnumServerContext* context, const CAM_SELECT_VERSION_RESPONSE* selectVersionResponse)
563 {
564  wStream* s = NULL;
565 
566  s = Stream_New(NULL, CAM_HEADER_SIZE);
567  if (!s)
568  {
569  WLog_ERR(TAG, "Stream_New failed!");
570  return ERROR_NOT_ENOUGH_MEMORY;
571  }
572 
573  Stream_Write_UINT8(s, selectVersionResponse->Header.Version);
574  Stream_Write_UINT8(s,
575  WINPR_ASSERTING_INT_CAST(uint8_t, selectVersionResponse->Header.MessageId));
576 
577  return enumerator_server_packet_send(context, s);
578 }
579 
580 CamDevEnumServerContext* cam_dev_enum_server_context_new(HANDLE vcm)
581 {
582  enumerator_server* enumerator = (enumerator_server*)calloc(1, sizeof(enumerator_server));
583 
584  if (!enumerator)
585  return NULL;
586 
587  enumerator->context.vcm = vcm;
588  enumerator->context.Initialize = enumerator_server_initialize;
589  enumerator->context.Open = enumerator_server_open;
590  enumerator->context.Close = enumerator_server_close;
591  enumerator->context.Poll = enumerator_server_context_poll;
592  enumerator->context.ChannelHandle = enumerator_server_context_handle;
593 
594  enumerator->context.SelectVersionResponse = enumerator_send_select_version_response_pdu;
595 
596  enumerator->buffer = Stream_New(NULL, 4096);
597  if (!enumerator->buffer)
598  goto fail;
599 
600  return &enumerator->context;
601 fail:
602  WINPR_PRAGMA_DIAG_PUSH
603  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
604  cam_dev_enum_server_context_free(&enumerator->context);
605  WINPR_PRAGMA_DIAG_POP
606  return NULL;
607 }
608 
609 void cam_dev_enum_server_context_free(CamDevEnumServerContext* context)
610 {
611  enumerator_server* enumerator = (enumerator_server*)context;
612 
613  if (enumerator)
614  {
615  enumerator_server_close(context);
616  Stream_Free(enumerator->buffer, TRUE);
617  }
618 
619  free(enumerator);
620 }