FreeRDP
Loading...
Searching...
No Matches
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 void* buffer = NULL;
430 HANDLE events[8] = { 0 };
431 HANDLE ChannelEvent = NULL;
432 DWORD BytesReturned = 0;
433 UINT error = 0;
434 RemdeskServerContext* context = (RemdeskServerContext*)arg;
435 WINPR_ASSERT(context);
436 wStream* s = Stream_New(NULL, 4096);
437
438 if (!s)
439 {
440 WLog_ERR(TAG, "Stream_New failed!");
441 error = CHANNEL_RC_NO_MEMORY;
442 goto out;
443 }
444
445 if (WTSVirtualChannelQuery(context->priv->ChannelHandle, WTSVirtualEventHandle, &buffer,
446 &BytesReturned) == TRUE)
447 {
448 if (BytesReturned == sizeof(HANDLE))
449 ChannelEvent = *(HANDLE*)buffer;
450
451 WTSFreeMemory(buffer);
452 }
453 else
454 {
455 WLog_ERR(TAG, "WTSVirtualChannelQuery failed!");
456 error = ERROR_INTERNAL_ERROR;
457 goto out;
458 }
459
460 DWORD nCount = 0;
461 events[nCount++] = ChannelEvent;
462 events[nCount++] = context->priv->StopEvent;
463
464 if ((error = remdesk_send_ctl_version_info_pdu(context)))
465 {
466 WLog_ERR(TAG, "remdesk_send_ctl_version_info_pdu failed with error %" PRIu32 "!", error);
467 goto out;
468 }
469
470 while (1)
471 {
472 DWORD status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
473
474 if (status == WAIT_FAILED)
475 {
476 error = GetLastError();
477 WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
478 break;
479 }
480
481 status = WaitForSingleObject(context->priv->StopEvent, 0);
482
483 if (status == WAIT_FAILED)
484 {
485 error = GetLastError();
486 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
487 break;
488 }
489
490 if (status == WAIT_OBJECT_0)
491 {
492 break;
493 }
494
495 const size_t len = Stream_Capacity(s);
496 if (len > UINT32_MAX)
497 {
498 error = ERROR_INTERNAL_ERROR;
499 break;
500 }
501 if (WTSVirtualChannelRead(context->priv->ChannelHandle, 0, Stream_BufferAs(s, char),
502 (UINT32)len, &BytesReturned))
503 {
504 if (BytesReturned)
505 Stream_Seek(s, BytesReturned);
506 }
507 else
508 {
509 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
510 {
511 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
512 error = CHANNEL_RC_NO_MEMORY;
513 break;
514 }
515 }
516
517 if (Stream_GetPosition(s) >= 8)
518 {
519 const UINT32* pHeader = Stream_BufferAs(s, UINT32);
520 const UINT32 PduLength = pHeader[0] + pHeader[1] + 8;
521
522 if (PduLength >= Stream_GetPosition(s))
523 {
524 Stream_SealLength(s);
525 Stream_SetPosition(s, 0);
526
527 if ((error = remdesk_server_receive_pdu(context, s)))
528 {
529 WLog_ERR(TAG, "remdesk_server_receive_pdu failed with error %" PRIu32 "!",
530 error);
531 break;
532 }
533
534 Stream_SetPosition(s, 0);
535 }
536 }
537 }
538
539out:
540 Stream_Free(s, TRUE);
541
542 if (error && context->rdpcontext)
543 setChannelError(context->rdpcontext, error, "remdesk_server_thread reported an error");
544
545 ExitThread(error);
546 return error;
547}
548
554static UINT remdesk_server_start(RemdeskServerContext* context)
555{
556 context->priv->ChannelHandle =
557 WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, REMDESK_SVC_CHANNEL_NAME);
558
559 if (!context->priv->ChannelHandle)
560 {
561 WLog_ERR(TAG, "WTSVirtualChannelOpen failed!");
562 return ERROR_INTERNAL_ERROR;
563 }
564
565 if (!(context->priv->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
566 {
567 WLog_ERR(TAG, "CreateEvent failed!");
568 return ERROR_INTERNAL_ERROR;
569 }
570
571 if (!(context->priv->Thread =
572 CreateThread(NULL, 0, remdesk_server_thread, (void*)context, 0, NULL)))
573 {
574 WLog_ERR(TAG, "CreateThread failed!");
575 (void)CloseHandle(context->priv->StopEvent);
576 context->priv->StopEvent = NULL;
577 return ERROR_INTERNAL_ERROR;
578 }
579
580 return CHANNEL_RC_OK;
581}
582
588static UINT remdesk_server_stop(RemdeskServerContext* context)
589{
590 UINT error = 0;
591 (void)SetEvent(context->priv->StopEvent);
592
593 if (WaitForSingleObject(context->priv->Thread, INFINITE) == WAIT_FAILED)
594 {
595 error = GetLastError();
596 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
597 return error;
598 }
599
600 (void)CloseHandle(context->priv->Thread);
601 (void)CloseHandle(context->priv->StopEvent);
602 return CHANNEL_RC_OK;
603}
604
605RemdeskServerContext* remdesk_server_context_new(HANDLE vcm)
606{
607 RemdeskServerContext* context = NULL;
608 context = (RemdeskServerContext*)calloc(1, sizeof(RemdeskServerContext));
609
610 if (context)
611 {
612 context->vcm = vcm;
613 context->Start = remdesk_server_start;
614 context->Stop = remdesk_server_stop;
615 context->priv = (RemdeskServerPrivate*)calloc(1, sizeof(RemdeskServerPrivate));
616
617 if (!context->priv)
618 {
619 free(context);
620 return NULL;
621 }
622
623 context->priv->Version = 1;
624 }
625
626 return context;
627}
628
629void remdesk_server_context_free(RemdeskServerContext* context)
630{
631 if (context)
632 {
633 if (context->priv->ChannelHandle != INVALID_HANDLE_VALUE)
634 (void)WTSVirtualChannelClose(context->priv->ChannelHandle);
635
636 free(context->priv);
637 free(context);
638 }
639}