FreeRDP
Loading...
Searching...
No Matches
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
43typedef enum
44{
45 AINPUT_INITIAL,
46 AINPUT_OPENED,
47 AINPUT_VERSION_SENT,
48} eAInputChannelState;
49
50typedef 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
72static UINT ainput_server_context_poll(ainput_server_context* context);
73static BOOL ainput_server_context_handle(ainput_server_context* context, HANDLE* handle);
74static UINT ainput_server_context_poll_int(ainput_server_context* context);
75
76static 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
89static UINT ainput_server_open_channel(ainput_server* ainput)
90{
91 DWORD Error = 0;
92 HANDLE hEvent = nullptr;
93 DWORD StartTick = 0;
94 DWORD BytesReturned = 0;
95 PULONG pSessionId = nullptr;
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 == nullptr)
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
158static UINT ainput_server_send_version(ainput_server* ainput)
159{
160 ULONG written = 0;
161
162 WINPR_ASSERT(ainput);
163
164 wStream* s = ainput->buffer;
165 WINPR_ASSERT(s);
166
167 Stream_ResetPosition(s);
168 if (!Stream_EnsureCapacity(s, 10))
169 {
170 WLog_WARN(TAG, "[%s] out of memory", AINPUT_DVC_CHANNEL_NAME);
171 return ERROR_OUTOFMEMORY;
172 }
173
174 Stream_Write_UINT16(s, MSG_AINPUT_VERSION);
175 Stream_Write_UINT32(s, AINPUT_VERSION_MAJOR); /* Version (4 bytes) */
176 Stream_Write_UINT32(s, AINPUT_VERSION_MINOR); /* Version (4 bytes) */
177
178 const ULONG len = WINPR_ASSERTING_INT_CAST(ULONG, Stream_GetPosition(s));
179 if (!WTSVirtualChannelWrite(ainput->ainput_channel, Stream_BufferAs(s, char), len, &written))
180 {
181 WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
182 return ERROR_INTERNAL_ERROR;
183 }
184
185 return CHANNEL_RC_OK;
186}
187
188static UINT ainput_server_recv_mouse_event(ainput_server* ainput, wStream* s)
189{
190 UINT error = CHANNEL_RC_OK;
191 UINT64 flags = 0;
192 UINT64 time = 0;
193 INT32 x = 0;
194 INT32 y = 0;
195 char buffer[128] = WINPR_C_ARRAY_INIT;
196
197 WINPR_ASSERT(ainput);
198 WINPR_ASSERT(s);
199
200 if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
201 return ERROR_NO_DATA;
202
203 Stream_Read_UINT64(s, time);
204 Stream_Read_UINT64(s, flags);
205 Stream_Read_INT32(s, x);
206 Stream_Read_INT32(s, y);
207
208 WLog_VRB(TAG, "received: time=0x%08" PRIx64 ", flags=%s, %" PRId32 "x%" PRId32, time,
209 ainput_flags_to_string(flags, buffer, sizeof(buffer)), x, y);
210 IFCALLRET(ainput->context.MouseEvent, error, &ainput->context, time, flags, x, y);
211
212 return error;
213}
214
215static HANDLE ainput_server_get_channel_handle(ainput_server* ainput)
216{
217 void* buffer = nullptr;
218 DWORD BytesReturned = 0;
219 HANDLE ChannelEvent = nullptr;
220
221 WINPR_ASSERT(ainput);
222
223 if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualEventHandle, &buffer,
224 &BytesReturned) == TRUE)
225 {
226 if (BytesReturned == sizeof(HANDLE))
227 ChannelEvent = *(HANDLE*)buffer;
228
229 WTSFreeMemory(buffer);
230 }
231
232 return ChannelEvent;
233}
234
235static DWORD WINAPI ainput_server_thread_func(LPVOID arg)
236{
237 DWORD nCount = 0;
238 HANDLE events[2] = WINPR_C_ARRAY_INIT;
239 ainput_server* ainput = (ainput_server*)arg;
240 UINT error = CHANNEL_RC_OK;
241 DWORD status = 0;
242
243 WINPR_ASSERT(ainput);
244
245 nCount = 0;
246 events[nCount++] = ainput->stopEvent;
247
248 while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
249 {
250 switch (ainput->state)
251 {
252 case AINPUT_OPENED:
253 events[1] = ainput_server_get_channel_handle(ainput);
254 nCount = 2;
255 status = WaitForMultipleObjects(nCount, events, FALSE, 100);
256 switch (status)
257 {
258 case WAIT_TIMEOUT:
259 case WAIT_OBJECT_0 + 1:
260 case WAIT_OBJECT_0:
261 error = ainput_server_context_poll_int(&ainput->context);
262 break;
263 case WAIT_FAILED:
264 default:
265 WLog_WARN(TAG, "[%s] Wait for open failed", AINPUT_DVC_CHANNEL_NAME);
266 error = ERROR_INTERNAL_ERROR;
267 break;
268 }
269 break;
270 case AINPUT_VERSION_SENT:
271 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
272 switch (status)
273 {
274 case WAIT_TIMEOUT:
275 case WAIT_OBJECT_0 + 1:
276 case WAIT_OBJECT_0:
277 error = ainput_server_context_poll_int(&ainput->context);
278 break;
279
280 case WAIT_FAILED:
281 default:
282 WLog_WARN(TAG, "[%s] Wait for version failed", AINPUT_DVC_CHANNEL_NAME);
283 error = ERROR_INTERNAL_ERROR;
284 break;
285 }
286 break;
287 default:
288 error = ainput_server_context_poll_int(&ainput->context);
289 break;
290 }
291 }
292
293 (void)WTSVirtualChannelClose(ainput->ainput_channel);
294 ainput->ainput_channel = nullptr;
295
296 if (error && ainput->context.rdpcontext)
297 setChannelError(ainput->context.rdpcontext, error,
298 "ainput_server_thread_func reported an error");
299
300 ExitThread(error);
301 return error;
302}
303
309static UINT ainput_server_open(ainput_server_context* context)
310{
311 ainput_server* ainput = (ainput_server*)context;
312
313 WINPR_ASSERT(ainput);
314
315 if (!ainput->externalThread && (ainput->thread == nullptr))
316 {
317 ainput->stopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
318 if (!ainput->stopEvent)
319 {
320 WLog_ERR(TAG, "CreateEvent failed!");
321 return ERROR_INTERNAL_ERROR;
322 }
323
324 ainput->thread = CreateThread(nullptr, 0, ainput_server_thread_func, ainput, 0, nullptr);
325 if (!ainput->thread)
326 {
327 WLog_ERR(TAG, "CreateEvent failed!");
328 (void)CloseHandle(ainput->stopEvent);
329 ainput->stopEvent = nullptr;
330 return ERROR_INTERNAL_ERROR;
331 }
332 }
333 ainput->isOpened = TRUE;
334
335 return CHANNEL_RC_OK;
336}
337
343static UINT ainput_server_close(ainput_server_context* context)
344{
345 UINT error = CHANNEL_RC_OK;
346 ainput_server* ainput = (ainput_server*)context;
347
348 WINPR_ASSERT(ainput);
349
350 if (!ainput->externalThread && ainput->thread)
351 {
352 (void)SetEvent(ainput->stopEvent);
353
354 if (WaitForSingleObject(ainput->thread, INFINITE) == WAIT_FAILED)
355 {
356 error = GetLastError();
357 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
358 return error;
359 }
360
361 (void)CloseHandle(ainput->thread);
362 (void)CloseHandle(ainput->stopEvent);
363 ainput->thread = nullptr;
364 ainput->stopEvent = nullptr;
365 }
366 if (ainput->externalThread)
367 {
368 if (ainput->state != AINPUT_INITIAL)
369 {
370 (void)WTSVirtualChannelClose(ainput->ainput_channel);
371 ainput->ainput_channel = nullptr;
372 ainput->state = AINPUT_INITIAL;
373 }
374 }
375 ainput->isOpened = FALSE;
376
377 return error;
378}
379
380static UINT ainput_server_initialize(ainput_server_context* context, BOOL externalThread)
381{
382 UINT error = CHANNEL_RC_OK;
383 ainput_server* ainput = (ainput_server*)context;
384
385 WINPR_ASSERT(ainput);
386
387 if (ainput->isOpened)
388 {
389 WLog_WARN(TAG, "Application error: AINPUT channel already initialized, calling in this "
390 "state is not possible!");
391 return ERROR_INVALID_STATE;
392 }
393 ainput->externalThread = externalThread;
394 return error;
395}
396
397ainput_server_context* ainput_server_context_new(HANDLE vcm)
398{
399 ainput_server* ainput = (ainput_server*)calloc(1, sizeof(ainput_server));
400
401 if (!ainput)
402 return nullptr;
403
404 ainput->context.vcm = vcm;
405 ainput->context.Open = ainput_server_open;
406 ainput->context.IsOpen = ainput_server_is_open;
407 ainput->context.Close = ainput_server_close;
408 ainput->context.Initialize = ainput_server_initialize;
409 ainput->context.Poll = ainput_server_context_poll;
410 ainput->context.ChannelHandle = ainput_server_context_handle;
411
412 ainput->buffer = Stream_New(nullptr, 4096);
413 if (!ainput->buffer)
414 goto fail;
415 return &ainput->context;
416fail:
417 WINPR_PRAGMA_DIAG_PUSH
418 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
419 ainput_server_context_free(&ainput->context);
420 WINPR_PRAGMA_DIAG_POP
421 return nullptr;
422}
423
424void ainput_server_context_free(ainput_server_context* context)
425{
426 ainput_server* ainput = (ainput_server*)context;
427 if (ainput)
428 {
429 ainput_server_close(context);
430 Stream_Free(ainput->buffer, TRUE);
431 }
432 free(ainput);
433}
434
435static UINT ainput_process_message(ainput_server* ainput)
436{
437 UINT error = ERROR_INTERNAL_ERROR;
438 ULONG BytesReturned = 0;
439 ULONG ActualBytesReturned = 0;
440
441 WINPR_ASSERT(ainput);
442 WINPR_ASSERT(ainput->ainput_channel);
443
444 wStream* s = ainput->buffer;
445 WINPR_ASSERT(s);
446
447 Stream_ResetPosition(s);
448 const BOOL rc = WTSVirtualChannelRead(ainput->ainput_channel, 0, nullptr, 0, &BytesReturned);
449 if (!rc)
450 goto out;
451
452 if (BytesReturned < 2)
453 {
454 error = CHANNEL_RC_OK;
455 goto out;
456 }
457
458 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
459 {
460 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
461 error = CHANNEL_RC_NO_MEMORY;
462 goto out;
463 }
464
465 if (WTSVirtualChannelRead(ainput->ainput_channel, 0, Stream_BufferAs(s, char),
466 (ULONG)Stream_Capacity(s), &ActualBytesReturned) == FALSE)
467 {
468 WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
469 goto out;
470 }
471
472 if (BytesReturned != ActualBytesReturned)
473 {
474 WLog_ERR(TAG, "WTSVirtualChannelRead size mismatch %" PRIu32 ", expected %" PRIu32,
475 ActualBytesReturned, BytesReturned);
476 goto out;
477 }
478
479 if (!Stream_SetLength(s, ActualBytesReturned))
480 goto out;
481
482 {
483 const UINT16 MessageId = Stream_Get_UINT16(s);
484
485 switch (MessageId)
486 {
487 case MSG_AINPUT_MOUSE:
488 error = ainput_server_recv_mouse_event(ainput, s);
489 break;
490
491 default:
492 WLog_ERR(TAG, "audin_server_thread_func: unknown MessageId %" PRIu16 "", MessageId);
493 break;
494 }
495 }
496
497out:
498 if (error)
499 WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
500
501 return error;
502}
503
504BOOL ainput_server_context_handle(ainput_server_context* context, HANDLE* handle)
505{
506 ainput_server* ainput = (ainput_server*)context;
507 WINPR_ASSERT(ainput);
508 WINPR_ASSERT(handle);
509
510 if (!ainput->externalThread)
511 {
512 WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME);
513 return FALSE;
514 }
515 if (ainput->state == AINPUT_INITIAL)
516 {
517 WLog_WARN(TAG, "[%s] state fail!", AINPUT_DVC_CHANNEL_NAME);
518 return FALSE;
519 }
520 *handle = ainput_server_get_channel_handle(ainput);
521 return TRUE;
522}
523
524UINT ainput_server_context_poll_int(ainput_server_context* context)
525{
526 ainput_server* ainput = (ainput_server*)context;
527 UINT error = ERROR_INTERNAL_ERROR;
528
529 WINPR_ASSERT(ainput);
530
531 switch (ainput->state)
532 {
533 case AINPUT_INITIAL:
534 error = ainput_server_open_channel(ainput);
535 if (error)
536 WLog_ERR(TAG, "ainput_server_open_channel failed with error %" PRIu32 "!", error);
537 else
538 ainput->state = AINPUT_OPENED;
539 break;
540 case AINPUT_OPENED:
541 {
542 union
543 {
544 BYTE* pb;
545 void* pv;
546 } buffer;
547 DWORD BytesReturned = 0;
548
549 buffer.pv = nullptr;
550
551 if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualChannelReady, &buffer.pv,
552 &BytesReturned) != TRUE)
553 {
554 WLog_ERR(TAG, "WTSVirtualChannelReady failed,");
555 }
556 else
557 {
558 if (*buffer.pb != 0)
559 {
560 error = ainput_server_send_version(ainput);
561 if (error)
562 WLog_ERR(TAG, "audin_server_send_version failed with error %" PRIu32 "!",
563 error);
564 else
565 ainput->state = AINPUT_VERSION_SENT;
566 }
567 else
568 error = CHANNEL_RC_OK;
569 }
570 WTSFreeMemory(buffer.pv);
571 }
572 break;
573 case AINPUT_VERSION_SENT:
574 error = ainput_process_message(ainput);
575 break;
576
577 default:
578 WLog_ERR(TAG, "AINPUT channel is in invalid state %u", ainput->state);
579 break;
580 }
581
582 return error;
583}
584
585UINT ainput_server_context_poll(ainput_server_context* context)
586{
587 ainput_server* ainput = (ainput_server*)context;
588
589 WINPR_ASSERT(ainput);
590 if (!ainput->externalThread)
591 {
592 WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME);
593 return ERROR_INTERNAL_ERROR;
594 }
595 return ainput_server_context_poll_int(context);
596}