FreeRDP
server/ainput_main.c
1 
21 #include <freerdp/config.h>
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include <winpr/crt.h>
28 #include <winpr/assert.h>
29 #include <winpr/synch.h>
30 #include <winpr/thread.h>
31 #include <winpr/stream.h>
32 #include <winpr/sysinfo.h>
33 
34 #include <freerdp/freerdp.h>
35 #include <freerdp/channels/ainput.h>
36 #include <freerdp/server/ainput.h>
37 #include <freerdp/channels/log.h>
38 
39 #include "../common/ainput_common.h"
40 
41 #define TAG CHANNELS_TAG("ainput.server")
42 
43 typedef enum
44 {
45  AINPUT_INITIAL,
46  AINPUT_OPENED,
47  AINPUT_VERSION_SENT,
48 } eAInputChannelState;
49 
50 typedef struct
51 {
52  ainput_server_context context;
53 
54  BOOL opened;
55 
56  HANDLE stopEvent;
57 
58  HANDLE thread;
59  void* ainput_channel;
60 
61  DWORD SessionId;
62 
63  BOOL isOpened;
64  BOOL externalThread;
65 
66  /* Channel state */
67  eAInputChannelState state;
68 
69  wStream* buffer;
70 } ainput_server;
71 
72 static UINT ainput_server_context_poll(ainput_server_context* context);
73 static BOOL ainput_server_context_handle(ainput_server_context* context, HANDLE* handle);
74 static UINT ainput_server_context_poll_int(ainput_server_context* context);
75 
76 static BOOL ainput_server_is_open(ainput_server_context* context)
77 {
78  ainput_server* ainput = (ainput_server*)context;
79 
80  WINPR_ASSERT(ainput);
81  return ainput->isOpened;
82 }
83 
89 static UINT ainput_server_open_channel(ainput_server* ainput)
90 {
91  DWORD Error = 0;
92  HANDLE hEvent = NULL;
93  DWORD StartTick = 0;
94  DWORD BytesReturned = 0;
95  PULONG pSessionId = NULL;
96 
97  WINPR_ASSERT(ainput);
98 
99  if (WTSQuerySessionInformationA(ainput->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
100  (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
101  {
102  WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
103  return ERROR_INTERNAL_ERROR;
104  }
105 
106  ainput->SessionId = (DWORD)*pSessionId;
107  WTSFreeMemory(pSessionId);
108  hEvent = WTSVirtualChannelManagerGetEventHandle(ainput->context.vcm);
109  StartTick = GetTickCount();
110 
111  while (ainput->ainput_channel == NULL)
112  {
113  if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
114  {
115  Error = GetLastError();
116  WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
117  return Error;
118  }
119 
120  ainput->ainput_channel = WTSVirtualChannelOpenEx(ainput->SessionId, AINPUT_DVC_CHANNEL_NAME,
121  WTS_CHANNEL_OPTION_DYNAMIC);
122 
123  Error = GetLastError();
124 
125  if (Error == ERROR_NOT_FOUND)
126  {
127  WLog_DBG(TAG, "Channel %s not found", AINPUT_DVC_CHANNEL_NAME);
128  break;
129  }
130 
131  if (ainput->ainput_channel)
132  {
133  UINT32 channelId = 0;
134  BOOL status = TRUE;
135 
136  channelId = WTSChannelGetIdByHandle(ainput->ainput_channel);
137 
138  IFCALLRET(ainput->context.ChannelIdAssigned, status, &ainput->context, channelId);
139  if (!status)
140  {
141  WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
142  return ERROR_INTERNAL_ERROR;
143  }
144 
145  break;
146  }
147 
148  if (GetTickCount() - StartTick > 5000)
149  {
150  WLog_WARN(TAG, "Timeout opening channel %s", AINPUT_DVC_CHANNEL_NAME);
151  break;
152  }
153  }
154 
155  return ainput->ainput_channel ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
156 }
157 
158 static UINT ainput_server_send_version(ainput_server* ainput)
159 {
160  ULONG written = 0;
161  wStream* s = NULL;
162 
163  WINPR_ASSERT(ainput);
164 
165  s = ainput->buffer;
166  WINPR_ASSERT(s);
167 
168  Stream_SetPosition(s, 0);
169  if (!Stream_EnsureCapacity(s, 10))
170  {
171  WLog_WARN(TAG, "[%s] out of memory", AINPUT_DVC_CHANNEL_NAME);
172  return ERROR_OUTOFMEMORY;
173  }
174 
175  Stream_Write_UINT16(s, MSG_AINPUT_VERSION);
176  Stream_Write_UINT32(s, AINPUT_VERSION_MAJOR); /* Version (4 bytes) */
177  Stream_Write_UINT32(s, AINPUT_VERSION_MINOR); /* Version (4 bytes) */
178 
179  WINPR_ASSERT(Stream_GetPosition(s) <= UINT32_MAX);
180  if (!WTSVirtualChannelWrite(ainput->ainput_channel, Stream_BufferAs(s, char),
181  (ULONG)Stream_GetPosition(s), &written))
182  {
183  WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
184  return ERROR_INTERNAL_ERROR;
185  }
186 
187  return CHANNEL_RC_OK;
188 }
189 
190 static UINT ainput_server_recv_mouse_event(ainput_server* ainput, wStream* s)
191 {
192  UINT error = CHANNEL_RC_OK;
193  UINT64 flags = 0;
194  UINT64 time = 0;
195  INT32 x = 0;
196  INT32 y = 0;
197  char buffer[128] = { 0 };
198 
199  WINPR_ASSERT(ainput);
200  WINPR_ASSERT(s);
201 
202  if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
203  return ERROR_NO_DATA;
204 
205  Stream_Read_UINT64(s, time);
206  Stream_Read_UINT64(s, flags);
207  Stream_Read_INT32(s, x);
208  Stream_Read_INT32(s, y);
209 
210  WLog_VRB(TAG, "received: time=0x%08" PRIx64 ", flags=%s, %" PRId32 "x%" PRId32, time,
211  ainput_flags_to_string(flags, buffer, sizeof(buffer)), x, y);
212  IFCALLRET(ainput->context.MouseEvent, error, &ainput->context, time, flags, x, y);
213 
214  return error;
215 }
216 
217 static HANDLE ainput_server_get_channel_handle(ainput_server* ainput)
218 {
219  void* buffer = NULL;
220  DWORD BytesReturned = 0;
221  HANDLE ChannelEvent = NULL;
222 
223  WINPR_ASSERT(ainput);
224 
225  if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualEventHandle, &buffer,
226  &BytesReturned) == TRUE)
227  {
228  if (BytesReturned == sizeof(HANDLE))
229  CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
230 
231  WTSFreeMemory(buffer);
232  }
233 
234  return ChannelEvent;
235 }
236 
237 static DWORD WINAPI ainput_server_thread_func(LPVOID arg)
238 {
239  DWORD nCount = 0;
240  HANDLE events[2] = { 0 };
241  ainput_server* ainput = (ainput_server*)arg;
242  UINT error = CHANNEL_RC_OK;
243  DWORD status = 0;
244 
245  WINPR_ASSERT(ainput);
246 
247  nCount = 0;
248  events[nCount++] = ainput->stopEvent;
249 
250  while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
251  {
252  switch (ainput->state)
253  {
254  case AINPUT_OPENED:
255  events[1] = ainput_server_get_channel_handle(ainput);
256  nCount = 2;
257  status = WaitForMultipleObjects(nCount, events, FALSE, 100);
258  switch (status)
259  {
260  case WAIT_TIMEOUT:
261  case WAIT_OBJECT_0 + 1:
262  case WAIT_OBJECT_0:
263  error = ainput_server_context_poll_int(&ainput->context);
264  break;
265  case WAIT_FAILED:
266  default:
267  WLog_WARN(TAG, "[%s] Wait for open failed", AINPUT_DVC_CHANNEL_NAME);
268  error = ERROR_INTERNAL_ERROR;
269  break;
270  }
271  break;
272  case AINPUT_VERSION_SENT:
273  status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
274  switch (status)
275  {
276  case WAIT_TIMEOUT:
277  case WAIT_OBJECT_0 + 1:
278  case WAIT_OBJECT_0:
279  error = ainput_server_context_poll_int(&ainput->context);
280  break;
281 
282  case WAIT_FAILED:
283  default:
284  WLog_WARN(TAG, "[%s] Wait for version failed", AINPUT_DVC_CHANNEL_NAME);
285  error = ERROR_INTERNAL_ERROR;
286  break;
287  }
288  break;
289  default:
290  error = ainput_server_context_poll_int(&ainput->context);
291  break;
292  }
293  }
294 
295  (void)WTSVirtualChannelClose(ainput->ainput_channel);
296  ainput->ainput_channel = NULL;
297 
298  if (error && ainput->context.rdpcontext)
299  setChannelError(ainput->context.rdpcontext, error,
300  "ainput_server_thread_func reported an error");
301 
302  ExitThread(error);
303  return error;
304 }
305 
311 static UINT ainput_server_open(ainput_server_context* context)
312 {
313  ainput_server* ainput = (ainput_server*)context;
314 
315  WINPR_ASSERT(ainput);
316 
317  if (!ainput->externalThread && (ainput->thread == NULL))
318  {
319  ainput->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
320  if (!ainput->stopEvent)
321  {
322  WLog_ERR(TAG, "CreateEvent failed!");
323  return ERROR_INTERNAL_ERROR;
324  }
325 
326  ainput->thread = CreateThread(NULL, 0, ainput_server_thread_func, ainput, 0, NULL);
327  if (!ainput->thread)
328  {
329  WLog_ERR(TAG, "CreateEvent failed!");
330  (void)CloseHandle(ainput->stopEvent);
331  ainput->stopEvent = NULL;
332  return ERROR_INTERNAL_ERROR;
333  }
334  }
335  ainput->isOpened = TRUE;
336 
337  return CHANNEL_RC_OK;
338 }
339 
345 static UINT ainput_server_close(ainput_server_context* context)
346 {
347  UINT error = CHANNEL_RC_OK;
348  ainput_server* ainput = (ainput_server*)context;
349 
350  WINPR_ASSERT(ainput);
351 
352  if (!ainput->externalThread && ainput->thread)
353  {
354  (void)SetEvent(ainput->stopEvent);
355 
356  if (WaitForSingleObject(ainput->thread, INFINITE) == WAIT_FAILED)
357  {
358  error = GetLastError();
359  WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
360  return error;
361  }
362 
363  (void)CloseHandle(ainput->thread);
364  (void)CloseHandle(ainput->stopEvent);
365  ainput->thread = NULL;
366  ainput->stopEvent = NULL;
367  }
368  if (ainput->externalThread)
369  {
370  if (ainput->state != AINPUT_INITIAL)
371  {
372  (void)WTSVirtualChannelClose(ainput->ainput_channel);
373  ainput->ainput_channel = NULL;
374  ainput->state = AINPUT_INITIAL;
375  }
376  }
377  ainput->isOpened = FALSE;
378 
379  return error;
380 }
381 
382 static UINT ainput_server_initialize(ainput_server_context* context, BOOL externalThread)
383 {
384  UINT error = CHANNEL_RC_OK;
385  ainput_server* ainput = (ainput_server*)context;
386 
387  WINPR_ASSERT(ainput);
388 
389  if (ainput->isOpened)
390  {
391  WLog_WARN(TAG, "Application error: AINPUT channel already initialized, calling in this "
392  "state is not possible!");
393  return ERROR_INVALID_STATE;
394  }
395  ainput->externalThread = externalThread;
396  return error;
397 }
398 
399 ainput_server_context* ainput_server_context_new(HANDLE vcm)
400 {
401  ainput_server* ainput = (ainput_server*)calloc(1, sizeof(ainput_server));
402 
403  if (!ainput)
404  return NULL;
405 
406  ainput->context.vcm = vcm;
407  ainput->context.Open = ainput_server_open;
408  ainput->context.IsOpen = ainput_server_is_open;
409  ainput->context.Close = ainput_server_close;
410  ainput->context.Initialize = ainput_server_initialize;
411  ainput->context.Poll = ainput_server_context_poll;
412  ainput->context.ChannelHandle = ainput_server_context_handle;
413 
414  ainput->buffer = Stream_New(NULL, 4096);
415  if (!ainput->buffer)
416  goto fail;
417  return &ainput->context;
418 fail:
419  WINPR_PRAGMA_DIAG_PUSH
420  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
421  ainput_server_context_free(&ainput->context);
422  WINPR_PRAGMA_DIAG_POP
423  return NULL;
424 }
425 
426 void ainput_server_context_free(ainput_server_context* context)
427 {
428  ainput_server* ainput = (ainput_server*)context;
429  if (ainput)
430  {
431  ainput_server_close(context);
432  Stream_Free(ainput->buffer, TRUE);
433  }
434  free(ainput);
435 }
436 
437 static UINT ainput_process_message(ainput_server* ainput)
438 {
439  BOOL rc = 0;
440  UINT error = ERROR_INTERNAL_ERROR;
441  ULONG BytesReturned = 0;
442  ULONG ActualBytesReturned = 0;
443  UINT16 MessageId = 0;
444  wStream* s = NULL;
445 
446  WINPR_ASSERT(ainput);
447  WINPR_ASSERT(ainput->ainput_channel);
448 
449  s = ainput->buffer;
450  WINPR_ASSERT(s);
451 
452  Stream_SetPosition(s, 0);
453  rc = WTSVirtualChannelRead(ainput->ainput_channel, 0, NULL, 0, &BytesReturned);
454  if (!rc)
455  goto out;
456 
457  if (BytesReturned < 2)
458  {
459  error = CHANNEL_RC_OK;
460  goto out;
461  }
462 
463  if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
464  {
465  WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
466  error = CHANNEL_RC_NO_MEMORY;
467  goto out;
468  }
469 
470  if (WTSVirtualChannelRead(ainput->ainput_channel, 0, Stream_BufferAs(s, char),
471  (ULONG)Stream_Capacity(s), &ActualBytesReturned) == FALSE)
472  {
473  WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
474  goto out;
475  }
476 
477  if (BytesReturned != ActualBytesReturned)
478  {
479  WLog_ERR(TAG, "WTSVirtualChannelRead size mismatch %" PRId32 ", expected %" PRId32,
480  ActualBytesReturned, BytesReturned);
481  goto out;
482  }
483 
484  Stream_SetLength(s, ActualBytesReturned);
485  Stream_Read_UINT16(s, MessageId);
486 
487  switch (MessageId)
488  {
489  case MSG_AINPUT_MOUSE:
490  error = ainput_server_recv_mouse_event(ainput, s);
491  break;
492 
493  default:
494  WLog_ERR(TAG, "audin_server_thread_func: unknown MessageId %" PRIu8 "", MessageId);
495  break;
496  }
497 
498 out:
499  if (error)
500  WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
501 
502  return error;
503 }
504 
505 BOOL ainput_server_context_handle(ainput_server_context* context, HANDLE* handle)
506 {
507  ainput_server* ainput = (ainput_server*)context;
508  WINPR_ASSERT(ainput);
509  WINPR_ASSERT(handle);
510 
511  if (!ainput->externalThread)
512  {
513  WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME);
514  return FALSE;
515  }
516  if (ainput->state == AINPUT_INITIAL)
517  {
518  WLog_WARN(TAG, "[%s] state fail!", AINPUT_DVC_CHANNEL_NAME);
519  return FALSE;
520  }
521  *handle = ainput_server_get_channel_handle(ainput);
522  return TRUE;
523 }
524 
525 UINT ainput_server_context_poll_int(ainput_server_context* context)
526 {
527  ainput_server* ainput = (ainput_server*)context;
528  UINT error = ERROR_INTERNAL_ERROR;
529 
530  WINPR_ASSERT(ainput);
531 
532  switch (ainput->state)
533  {
534  case AINPUT_INITIAL:
535  error = ainput_server_open_channel(ainput);
536  if (error)
537  WLog_ERR(TAG, "ainput_server_open_channel failed with error %" PRIu32 "!", error);
538  else
539  ainput->state = AINPUT_OPENED;
540  break;
541  case AINPUT_OPENED:
542  {
543  union
544  {
545  BYTE* pb;
546  void* pv;
547  } buffer;
548  DWORD BytesReturned = 0;
549 
550  buffer.pv = NULL;
551 
552  if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualChannelReady, &buffer.pv,
553  &BytesReturned) != TRUE)
554  {
555  WLog_ERR(TAG, "WTSVirtualChannelReady failed,");
556  }
557  else
558  {
559  if (*buffer.pb != 0)
560  {
561  error = ainput_server_send_version(ainput);
562  if (error)
563  WLog_ERR(TAG, "audin_server_send_version failed with error %" PRIu32 "!",
564  error);
565  else
566  ainput->state = AINPUT_VERSION_SENT;
567  }
568  else
569  error = CHANNEL_RC_OK;
570  }
571  WTSFreeMemory(buffer.pv);
572  }
573  break;
574  case AINPUT_VERSION_SENT:
575  error = ainput_process_message(ainput);
576  break;
577 
578  default:
579  WLog_ERR(TAG, "AINPUT chanel is in invalid state %d", ainput->state);
580  break;
581  }
582 
583  return error;
584 }
585 
586 UINT ainput_server_context_poll(ainput_server_context* context)
587 {
588  ainput_server* ainput = (ainput_server*)context;
589 
590  WINPR_ASSERT(ainput);
591  if (!ainput->externalThread)
592  {
593  WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME);
594  return ERROR_INTERNAL_ERROR;
595  }
596  return ainput_server_context_poll_int(context);
597 }