FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
server/remdesk_main.c
1
22#include <freerdp/config.h>
23
24#include <winpr/crt.h>
25#include <winpr/print.h>
26#include <winpr/stream.h>
27
28#include <freerdp/freerdp.h>
29
30#include "remdesk_main.h"
31#include "remdesk_common.h"
32
38static UINT remdesk_virtual_channel_write(RemdeskServerContext* context, wStream* s)
39{
40 const size_t len = Stream_Length(s);
41 WINPR_ASSERT(len <= UINT32_MAX);
42 ULONG BytesWritten = 0;
43 BOOL status = WTSVirtualChannelWrite(context->priv->ChannelHandle, Stream_BufferAs(s, char),
44 (UINT32)len, &BytesWritten);
45 return (status) ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
46}
47
53static UINT remdesk_send_ctl_result_pdu(RemdeskServerContext* context, UINT32 result)
54{
55 wStream* s = NULL;
57 UINT error = 0;
58 pdu.result = result;
59
60 if ((error = remdesk_prepare_ctl_header(&(pdu.ctlHeader), REMDESK_CTL_RESULT, 4)))
61 {
62 WLog_ERR(TAG, "remdesk_prepare_ctl_header failed with error %" PRIu32 "!", error);
63 return error;
64 }
65
66 s = Stream_New(NULL, REMDESK_CHANNEL_CTL_SIZE + pdu.ctlHeader.ch.DataLength);
67
68 if (!s)
69 {
70 WLog_ERR(TAG, "Stream_New failed!");
71 return CHANNEL_RC_NO_MEMORY;
72 }
73
74 if ((error = remdesk_write_ctl_header(s, &(pdu.ctlHeader))))
75 {
76 WLog_ERR(TAG, "remdesk_write_ctl_header failed with error %" PRIu32 "!", error);
77 goto out;
78 }
79
80 Stream_Write_UINT32(s, pdu.result); /* result (4 bytes) */
81 Stream_SealLength(s);
82
83 if ((error = remdesk_virtual_channel_write(context, s)))
84 WLog_ERR(TAG, "remdesk_virtual_channel_write failed with error %" PRIu32 "!", error);
85
86out:
87 Stream_Free(s, TRUE);
88 return error;
89}
90
96static UINT remdesk_send_ctl_version_info_pdu(RemdeskServerContext* context)
97{
98 wStream* s = NULL;
100 UINT error = 0;
101
102 if ((error = remdesk_prepare_ctl_header(&(pdu.ctlHeader), REMDESK_CTL_VERSIONINFO, 8)))
103 {
104 WLog_ERR(TAG, "remdesk_prepare_ctl_header failed with error %" PRIu32 "!", error);
105 return error;
106 }
107
108 pdu.versionMajor = 1;
109 pdu.versionMinor = 2;
110 s = Stream_New(NULL, REMDESK_CHANNEL_CTL_SIZE + pdu.ctlHeader.ch.DataLength);
111
112 if (!s)
113 {
114 WLog_ERR(TAG, "Stream_New failed!");
115 return CHANNEL_RC_NO_MEMORY;
116 }
117
118 if ((error = remdesk_write_ctl_header(s, &(pdu.ctlHeader))))
119 {
120 WLog_ERR(TAG, "remdesk_write_ctl_header failed with error %" PRIu32 "!", error);
121 goto out;
122 }
123
124 Stream_Write_UINT32(s, pdu.versionMajor); /* versionMajor (4 bytes) */
125 Stream_Write_UINT32(s, pdu.versionMinor); /* versionMinor (4 bytes) */
126 Stream_SealLength(s);
127
128 if ((error = remdesk_virtual_channel_write(context, s)))
129 WLog_ERR(TAG, "remdesk_virtual_channel_write failed with error %" PRIu32 "!", error);
130
131out:
132 Stream_Free(s, TRUE);
133 return error;
134}
135
141static UINT remdesk_recv_ctl_version_info_pdu(WINPR_ATTR_UNUSED RemdeskServerContext* context,
142 wStream* s,
143 WINPR_ATTR_UNUSED REMDESK_CHANNEL_HEADER* header)
144{
145 UINT32 versionMajor = 0;
146 UINT32 versionMinor = 0;
147
148 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
149 return ERROR_INVALID_DATA;
150
151 Stream_Read_UINT32(s, versionMajor); /* versionMajor (4 bytes) */
152 Stream_Read_UINT32(s, versionMinor); /* versionMinor (4 bytes) */
153 if ((versionMajor != 1) || (versionMinor != 2))
154 {
155 WLog_ERR(TAG, "REMOTEDESKTOP_CTL_VERSIONINFO_PACKET invalid version %" PRIu32 ".%" PRIu32,
156 versionMajor, versionMinor);
157 return ERROR_INVALID_DATA;
158 }
159
160 return CHANNEL_RC_OK;
161}
162
168static UINT remdesk_recv_ctl_remote_control_desktop_pdu(RemdeskServerContext* context, wStream* s,
170{
171 size_t cchStringW = 0;
173 UINT error = 0;
174 UINT32 msgLength = header->DataLength - 4;
175 const WCHAR* pStringW = Stream_ConstPointer(s);
176 const WCHAR* raConnectionStringW = pStringW;
177
178 while ((msgLength > 0) && pStringW[cchStringW])
179 {
180 msgLength -= 2;
181 cchStringW++;
182 }
183
184 if (pStringW[cchStringW] || !cchStringW)
185 return ERROR_INVALID_DATA;
186
187 cchStringW++;
188 const size_t cbRaConnectionStringW = cchStringW * 2;
189 pdu.raConnectionString =
190 ConvertWCharNToUtf8Alloc(raConnectionStringW, cbRaConnectionStringW / sizeof(WCHAR), NULL);
191 if (!pdu.raConnectionString)
192 return ERROR_INTERNAL_ERROR;
193
194 WLog_INFO(TAG, "RaConnectionString: %s", pdu.raConnectionString);
195 free(pdu.raConnectionString);
196
197 if ((error = remdesk_send_ctl_result_pdu(context, 0)))
198 WLog_ERR(TAG, "remdesk_send_ctl_result_pdu failed with error %" PRIu32 "!", error);
199
200 return error;
201}
202
208static UINT remdesk_recv_ctl_authenticate_pdu(WINPR_ATTR_UNUSED RemdeskServerContext* context,
210{
211 size_t cchTmpStringW = 0;
212 const WCHAR* expertBlobW = NULL;
214 UINT32 msgLength = header->DataLength - 4;
215 const WCHAR* pStringW = Stream_ConstPointer(s);
216 const WCHAR* raConnectionStringW = pStringW;
217
218 while ((msgLength > 0) && pStringW[cchTmpStringW])
219 {
220 msgLength -= 2;
221 cchTmpStringW++;
222 }
223
224 if (pStringW[cchTmpStringW] || !cchTmpStringW)
225 return ERROR_INVALID_DATA;
226
227 cchTmpStringW++;
228 const size_t cbRaConnectionStringW = cchTmpStringW * sizeof(WCHAR);
229 pStringW += cchTmpStringW;
230 expertBlobW = pStringW;
231
232 size_t cchStringW = 0;
233 while ((msgLength > 0) && pStringW[cchStringW])
234 {
235 msgLength -= 2;
236 cchStringW++;
237 }
238
239 if (pStringW[cchStringW] || !cchStringW)
240 return ERROR_INVALID_DATA;
241
242 cchStringW++;
243 const size_t cbExpertBlobW = cchStringW * 2;
244 pdu.raConnectionString =
245 ConvertWCharNToUtf8Alloc(raConnectionStringW, cbRaConnectionStringW / sizeof(WCHAR), NULL);
246 if (!pdu.raConnectionString)
247 return ERROR_INTERNAL_ERROR;
248
249 pdu.expertBlob = ConvertWCharNToUtf8Alloc(expertBlobW, cbExpertBlobW / sizeof(WCHAR), NULL);
250 if (!pdu.expertBlob)
251 {
252 free(pdu.raConnectionString);
253 return ERROR_INTERNAL_ERROR;
254 }
255
256 WLog_INFO(TAG, "RaConnectionString: %s ExpertBlob: %s", pdu.raConnectionString, pdu.expertBlob);
257 free(pdu.raConnectionString);
258 free(pdu.expertBlob);
259 return CHANNEL_RC_OK;
260}
261
267static UINT remdesk_recv_ctl_verify_password_pdu(RemdeskServerContext* context, wStream* s,
269{
271
272 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
273 return ERROR_INVALID_DATA;
274
275 const WCHAR* expertBlobW = Stream_ConstPointer(s);
276 if (header->DataLength < 4)
277 return ERROR_INVALID_PARAMETER;
278
279 const size_t cbExpertBlobW = header->DataLength - 4;
280
281 pdu.expertBlob = ConvertWCharNToUtf8Alloc(expertBlobW, cbExpertBlobW / sizeof(WCHAR), NULL);
282 if (!pdu.expertBlob)
283 return ERROR_INTERNAL_ERROR;
284
285 WLog_INFO(TAG, "ExpertBlob: %s", pdu.expertBlob);
286
287 // TODO: Callback?
288
289 free(pdu.expertBlob);
290 return remdesk_send_ctl_result_pdu(context, 0);
291}
292
298static UINT remdesk_recv_ctl_pdu(RemdeskServerContext* context, wStream* s,
300{
301 UINT error = CHANNEL_RC_OK;
302 UINT32 msgType = 0;
303
304 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
305 return ERROR_INVALID_DATA;
306
307 Stream_Read_UINT32(s, msgType); /* msgType (4 bytes) */
308 WLog_INFO(TAG, "msgType: %" PRIu32 "", msgType);
309
310 switch (msgType)
311 {
312 case REMDESK_CTL_REMOTE_CONTROL_DESKTOP:
313 if ((error = remdesk_recv_ctl_remote_control_desktop_pdu(context, s, header)))
314 {
315 WLog_ERR(TAG,
316 "remdesk_recv_ctl_remote_control_desktop_pdu failed with error %" PRIu32
317 "!",
318 error);
319 return error;
320 }
321
322 break;
323
324 case REMDESK_CTL_AUTHENTICATE:
325 if ((error = remdesk_recv_ctl_authenticate_pdu(context, s, header)))
326 {
327 WLog_ERR(TAG, "remdesk_recv_ctl_authenticate_pdu failed with error %" PRIu32 "!",
328 error);
329 return error;
330 }
331
332 break;
333
334 case REMDESK_CTL_DISCONNECT:
335 break;
336
337 case REMDESK_CTL_VERSIONINFO:
338 if ((error = remdesk_recv_ctl_version_info_pdu(context, s, header)))
339 {
340 WLog_ERR(TAG, "remdesk_recv_ctl_version_info_pdu failed with error %" PRIu32 "!",
341 error);
342 return error;
343 }
344
345 break;
346
347 case REMDESK_CTL_ISCONNECTED:
348 break;
349
350 case REMDESK_CTL_VERIFY_PASSWORD:
351 if ((error = remdesk_recv_ctl_verify_password_pdu(context, s, header)))
352 {
353 WLog_ERR(TAG, "remdesk_recv_ctl_verify_password_pdu failed with error %" PRIu32 "!",
354 error);
355 return error;
356 }
357
358 break;
359
360 case REMDESK_CTL_EXPERT_ON_VISTA:
361 break;
362
363 case REMDESK_CTL_RANOVICE_NAME:
364 break;
365
366 case REMDESK_CTL_RAEXPERT_NAME:
367 break;
368
369 case REMDESK_CTL_TOKEN:
370 break;
371
372 default:
373 WLog_ERR(TAG, "remdesk_recv_control_pdu: unknown msgType: %" PRIu32 "", msgType);
374 error = ERROR_INVALID_DATA;
375 break;
376 }
377
378 return error;
379}
380
386static UINT remdesk_server_receive_pdu(RemdeskServerContext* context, wStream* s)
387{
388 UINT error = CHANNEL_RC_OK;
390
391 if ((error = remdesk_read_channel_header(s, &header)))
392 {
393 WLog_ERR(TAG, "remdesk_read_channel_header failed with error %" PRIu32 "!", error);
394 return error;
395 }
396
397 if (strcmp(header.ChannelName, "RC_CTL") == 0)
398 {
399 if ((error = remdesk_recv_ctl_pdu(context, s, &header)))
400 {
401 WLog_ERR(TAG, "remdesk_recv_ctl_pdu failed with error %" PRIu32 "!", error);
402 return error;
403 }
404 }
405 else if (strcmp(header.ChannelName, "70") == 0)
406 {
407 }
408 else if (strcmp(header.ChannelName, "71") == 0)
409 {
410 }
411 else if (strcmp(header.ChannelName, ".") == 0)
412 {
413 }
414 else if (strcmp(header.ChannelName, "1000.") == 0)
415 {
416 }
417 else if (strcmp(header.ChannelName, "RA_FX") == 0)
418 {
419 }
420 else
421 {
422 }
423
424 return error;
425}
426
427static DWORD WINAPI remdesk_server_thread(LPVOID arg)
428{
429 wStream* s = NULL;
430 DWORD status = 0;
431 DWORD nCount = 0;
432 void* buffer = NULL;
433 UINT32* pHeader = NULL;
434 UINT32 PduLength = 0;
435 HANDLE events[8];
436 HANDLE ChannelEvent = NULL;
437 DWORD BytesReturned = 0;
438 RemdeskServerContext* context = NULL;
439 UINT error = 0;
440 context = (RemdeskServerContext*)arg;
441 buffer = NULL;
442 BytesReturned = 0;
443 ChannelEvent = NULL;
444 s = Stream_New(NULL, 4096);
445
446 if (!s)
447 {
448 WLog_ERR(TAG, "Stream_New failed!");
449 error = CHANNEL_RC_NO_MEMORY;
450 goto out;
451 }
452
453 if (WTSVirtualChannelQuery(context->priv->ChannelHandle, WTSVirtualEventHandle, &buffer,
454 &BytesReturned) == TRUE)
455 {
456 if (BytesReturned == sizeof(HANDLE))
457 ChannelEvent = *(HANDLE*)buffer;
458
459 WTSFreeMemory(buffer);
460 }
461 else
462 {
463 WLog_ERR(TAG, "WTSVirtualChannelQuery failed!");
464 error = ERROR_INTERNAL_ERROR;
465 goto out;
466 }
467
468 nCount = 0;
469 events[nCount++] = ChannelEvent;
470 events[nCount++] = context->priv->StopEvent;
471
472 if ((error = remdesk_send_ctl_version_info_pdu(context)))
473 {
474 WLog_ERR(TAG, "remdesk_send_ctl_version_info_pdu failed with error %" PRIu32 "!", error);
475 goto out;
476 }
477
478 while (1)
479 {
480 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
481
482 if (status == WAIT_FAILED)
483 {
484 error = GetLastError();
485 WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
486 break;
487 }
488
489 status = WaitForSingleObject(context->priv->StopEvent, 0);
490
491 if (status == WAIT_FAILED)
492 {
493 error = GetLastError();
494 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
495 break;
496 }
497
498 if (status == WAIT_OBJECT_0)
499 {
500 break;
501 }
502
503 const size_t len = Stream_Capacity(s);
504 if (len > UINT32_MAX)
505 {
506 error = ERROR_INTERNAL_ERROR;
507 break;
508 }
509 if (WTSVirtualChannelRead(context->priv->ChannelHandle, 0, Stream_BufferAs(s, char),
510 (UINT32)len, &BytesReturned))
511 {
512 if (BytesReturned)
513 Stream_Seek(s, BytesReturned);
514 }
515 else
516 {
517 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
518 {
519 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
520 error = CHANNEL_RC_NO_MEMORY;
521 break;
522 }
523 }
524
525 if (Stream_GetPosition(s) >= 8)
526 {
527 pHeader = Stream_BufferAs(s, UINT32);
528 PduLength = pHeader[0] + pHeader[1] + 8;
529
530 if (PduLength >= Stream_GetPosition(s))
531 {
532 Stream_SealLength(s);
533 Stream_SetPosition(s, 0);
534
535 if ((error = remdesk_server_receive_pdu(context, s)))
536 {
537 WLog_ERR(TAG, "remdesk_server_receive_pdu failed with error %" PRIu32 "!",
538 error);
539 break;
540 }
541
542 Stream_SetPosition(s, 0);
543 }
544 }
545 }
546
547 Stream_Free(s, TRUE);
548out:
549
550 if (error && context->rdpcontext)
551 setChannelError(context->rdpcontext, error, "remdesk_server_thread reported an error");
552
553 ExitThread(error);
554 return error;
555}
556
562static UINT remdesk_server_start(RemdeskServerContext* context)
563{
564 context->priv->ChannelHandle =
565 WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, REMDESK_SVC_CHANNEL_NAME);
566
567 if (!context->priv->ChannelHandle)
568 {
569 WLog_ERR(TAG, "WTSVirtualChannelOpen failed!");
570 return ERROR_INTERNAL_ERROR;
571 }
572
573 if (!(context->priv->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
574 {
575 WLog_ERR(TAG, "CreateEvent failed!");
576 return ERROR_INTERNAL_ERROR;
577 }
578
579 if (!(context->priv->Thread =
580 CreateThread(NULL, 0, remdesk_server_thread, (void*)context, 0, NULL)))
581 {
582 WLog_ERR(TAG, "CreateThread failed!");
583 (void)CloseHandle(context->priv->StopEvent);
584 context->priv->StopEvent = NULL;
585 return ERROR_INTERNAL_ERROR;
586 }
587
588 return CHANNEL_RC_OK;
589}
590
596static UINT remdesk_server_stop(RemdeskServerContext* context)
597{
598 UINT error = 0;
599 (void)SetEvent(context->priv->StopEvent);
600
601 if (WaitForSingleObject(context->priv->Thread, INFINITE) == WAIT_FAILED)
602 {
603 error = GetLastError();
604 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
605 return error;
606 }
607
608 (void)CloseHandle(context->priv->Thread);
609 (void)CloseHandle(context->priv->StopEvent);
610 return CHANNEL_RC_OK;
611}
612
613RemdeskServerContext* remdesk_server_context_new(HANDLE vcm)
614{
615 RemdeskServerContext* context = NULL;
616 context = (RemdeskServerContext*)calloc(1, sizeof(RemdeskServerContext));
617
618 if (context)
619 {
620 context->vcm = vcm;
621 context->Start = remdesk_server_start;
622 context->Stop = remdesk_server_stop;
623 context->priv = (RemdeskServerPrivate*)calloc(1, sizeof(RemdeskServerPrivate));
624
625 if (!context->priv)
626 {
627 free(context);
628 return NULL;
629 }
630
631 context->priv->Version = 1;
632 }
633
634 return context;
635}
636
637void remdesk_server_context_free(RemdeskServerContext* context)
638{
639 if (context)
640 {
641 if (context->priv->ChannelHandle != INVALID_HANDLE_VALUE)
642 (void)WTSVirtualChannelClose(context->priv->ChannelHandle);
643
644 free(context->priv);
645 free(context);
646 }
647}