FreeRDP
Loading...
Searching...
No Matches
shadow_server.c
1
21#include <freerdp/config.h>
22
23#include <errno.h>
24#include <stdint.h>
25
26#include <winpr/assert.h>
27#include <winpr/crt.h>
28#include <winpr/ssl.h>
29#include <winpr/path.h>
30#include <winpr/cmdline.h>
31#include <winpr/winsock.h>
32
33#include <freerdp/log.h>
34#include <freerdp/version.h>
35
36#include <winpr/tools/makecert.h>
37
38#ifndef _WIN32
39#include <sys/select.h>
40#include <signal.h>
41#endif
42
43#include "shadow.h"
44
45#define TAG SERVER_TAG("shadow")
46
47static const char bind_address[] = "bind-address,";
48
49#define fail_at(arg, rc) fail_at_((arg), (rc), __FILE__, __func__, __LINE__)
50static int fail_at_(const COMMAND_LINE_ARGUMENT_A* arg, int rc, const char* file, const char* fkt,
51 size_t line)
52{
53 const DWORD level = WLOG_ERROR;
54 wLog* log = WLog_Get(TAG);
55 if (WLog_IsLevelActive(log, level))
56 WLog_PrintTextMessage(log, level, line, file, fkt,
57 "Command line parsing failed at '%s' value '%s' [%d]", arg->Name,
58 arg->Value, rc);
59 return rc;
60}
61
62static int command_line_compare(const void* pa, const void* pb)
63{
64 const COMMAND_LINE_ARGUMENT_A* a = pa;
65 const COMMAND_LINE_ARGUMENT_A* b = pb;
66
67 if (!a && !b)
68 return 0;
69 if (!a)
70 return -1;
71 if (!b)
72 return 1;
73
74 return strcmp(a->Name, b->Name);
75}
76
77static int shadow_server_print_command_line_help(int argc, char** argv,
78 const COMMAND_LINE_ARGUMENT_A* largs)
79{
80 if ((argc < 1) || !largs || !argv)
81 return -1;
82
83 {
84 char* path = winpr_GetConfigFilePath(TRUE, "SAM");
85 printf("Usage: %s [options]\n", argv[0]);
86 printf("\n");
87 printf("Notes: By default NLA security is active.\n");
88 printf("\tIn this mode a SAM database is required.\n");
89 printf("\tProvide one with /sam-file:<file with path>\n");
90 printf("\telse the default path %s is used.\n", path);
91 printf("\tIf there is no existing SAM file authentication for all users will fail.\n");
92 printf("\n\tIf authentication against PAM is desired, start with -sec-nla (requires "
93 "compiled in "
94 "support for PAM)\n\n");
95 printf("Syntax:\n");
96 printf(" /flag (enables flag)\n");
97 printf(" /option:<value> (specifies option with value)\n");
98 printf(" +toggle -toggle (enables or disables toggle, where '/' is a synonym of '+')\n");
99 printf("\n");
100 free(path);
101 }
102
103 // TODO: Sort arguments
104 size_t nrArgs = 0;
105 {
106 const COMMAND_LINE_ARGUMENT_A* arg = largs;
107 while (arg->Name != NULL)
108 {
109 nrArgs++;
110 arg++;
111 }
112 nrArgs++;
113 }
114 COMMAND_LINE_ARGUMENT_A* args_copy = calloc(nrArgs, sizeof(COMMAND_LINE_ARGUMENT_A));
115 if (!args_copy)
116 return -1;
117 memcpy(args_copy, largs, nrArgs * sizeof(COMMAND_LINE_ARGUMENT_A));
118 qsort(args_copy, nrArgs - 1, sizeof(COMMAND_LINE_ARGUMENT_A), command_line_compare);
119
120 const COMMAND_LINE_ARGUMENT_A* arg = args_copy;
121
122 int rc = -1;
123 do
124 {
125 if (arg->Flags & COMMAND_LINE_VALUE_FLAG)
126 {
127 printf(" %s", "/");
128 printf("%-20s\n", arg->Name);
129 printf("\t%s\n", arg->Text);
130 }
131 else if ((arg->Flags & COMMAND_LINE_VALUE_REQUIRED) ||
132 (arg->Flags & COMMAND_LINE_VALUE_OPTIONAL))
133 {
134 printf(" %s", "/");
135
136 if (arg->Format)
137 {
138 const size_t length = (strlen(arg->Name) + strlen(arg->Format) + 2);
139 char* str = (char*)calloc(length + 1, sizeof(char));
140
141 if (!str)
142 goto fail;
143
144 (void)sprintf_s(str, length + 1, "%s:%s", arg->Name, arg->Format);
145 (void)printf("%-20s\n", str);
146 free(str);
147 }
148 else
149 {
150 printf("%-20s\n", arg->Name);
151 }
152
153 printf("\t%s\n", arg->Text);
154 }
155 else if (arg->Flags & COMMAND_LINE_VALUE_BOOL)
156 {
157 const size_t length = strlen(arg->Name) + 32;
158 char* str = calloc(length + 1, sizeof(char));
159
160 if (!str)
161 goto fail;
162
163 (void)sprintf_s(str, length + 1, "%s (default:%s)", arg->Name,
164 arg->Default ? "on" : "off");
165 (void)printf(" %s", arg->Default ? "-" : "+");
166 (void)printf("%-20s\n", str);
167 (void)printf("\t%s\n", arg->Text);
168
169 free(str);
170 }
171 } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
172
173 rc = 1;
174fail:
175 free(args_copy);
176 return rc;
177}
178
179int shadow_server_command_line_status_print(rdpShadowServer* server, int argc, char** argv,
180 int status, const COMMAND_LINE_ARGUMENT_A* cargs)
181{
182 WINPR_UNUSED(server);
183
184 if (status == COMMAND_LINE_STATUS_PRINT_VERSION)
185 {
186 printf("FreeRDP version %s (git %s)\n", FREERDP_VERSION_FULL, FREERDP_GIT_REVISION);
187 return COMMAND_LINE_STATUS_PRINT_VERSION;
188 }
189 else if (status == COMMAND_LINE_STATUS_PRINT_BUILDCONFIG)
190 {
191 printf("%s\n", freerdp_get_build_config());
192 return COMMAND_LINE_STATUS_PRINT_BUILDCONFIG;
193 }
194 else if (status == COMMAND_LINE_STATUS_PRINT)
195 {
196 return COMMAND_LINE_STATUS_PRINT;
197 }
198 else if (status < 0)
199 {
200 if (shadow_server_print_command_line_help(argc, argv, cargs) < 0)
201 return -1;
202
203 return COMMAND_LINE_STATUS_PRINT_HELP;
204 }
205
206 return 1;
207}
208
209int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** argv,
211{
212 int status = 0;
213 DWORD flags = 0;
214 const COMMAND_LINE_ARGUMENT_A* arg = NULL;
215 rdpSettings* settings = server->settings;
216
217 if ((argc < 2) || !argv || !cargs)
218 return 1;
219
220 CommandLineClearArgumentsA(cargs);
221 flags = COMMAND_LINE_SEPARATOR_COLON;
222 flags |= COMMAND_LINE_SIGIL_SLASH | COMMAND_LINE_SIGIL_PLUS_MINUS;
223 status = CommandLineParseArgumentsA(argc, argv, cargs, flags, server, NULL, NULL);
224
225 if (status < 0)
226 return status;
227
228 arg = cargs;
229 errno = 0;
230
231 do
232 {
233 if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
234 continue;
235
236 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "port")
237 {
238 long val = strtol(arg->Value, NULL, 0);
239
240 if ((errno != 0) || (val <= 0) || (val > UINT16_MAX))
241 return fail_at(arg, COMMAND_LINE_ERROR);
242
243 server->port = (DWORD)val;
244 }
245 CommandLineSwitchCase(arg, "ipc-socket")
246 {
247 /* /bind-address is incompatible */
248 if (server->ipcSocket)
249 return fail_at(arg, COMMAND_LINE_ERROR);
250 server->ipcSocket = _strdup(arg->Value);
251
252 if (!server->ipcSocket)
253 return fail_at(arg, COMMAND_LINE_ERROR);
254 }
255 CommandLineSwitchCase(arg, "bind-address")
256 {
257 int rc = 0;
258 size_t len = strlen(arg->Value) + sizeof(bind_address);
259 /* /ipc-socket is incompatible */
260 if (server->ipcSocket)
261 return fail_at(arg, COMMAND_LINE_ERROR);
262 server->ipcSocket = calloc(len, sizeof(CHAR));
263
264 if (!server->ipcSocket)
265 return fail_at(arg, COMMAND_LINE_ERROR);
266
267 rc = _snprintf(server->ipcSocket, len, "%s%s", bind_address, arg->Value);
268 if ((rc < 0) || ((size_t)rc != len - 1))
269 return fail_at(arg, COMMAND_LINE_ERROR);
270 }
271 CommandLineSwitchCase(arg, "may-view")
272 {
273 server->mayView = arg->Value ? TRUE : FALSE;
274 }
275 CommandLineSwitchCase(arg, "bitmap-compat")
276 {
277 server->SupportMultiRectBitmapUpdates = arg->Value ? FALSE : TRUE;
278 }
279 CommandLineSwitchCase(arg, "may-interact")
280 {
281 server->mayInteract = arg->Value ? TRUE : FALSE;
282 }
283 CommandLineSwitchCase(arg, "server-side-cursor")
284 {
285 server->ShowMouseCursor = arg->Value ? TRUE : FALSE;
286 }
287 CommandLineSwitchCase(arg, "mouse-relative")
288 {
289 const BOOL val = arg->Value ? TRUE : FALSE;
290 if (!freerdp_settings_set_bool(settings, FreeRDP_MouseUseRelativeMove, val) ||
291 !freerdp_settings_set_bool(settings, FreeRDP_HasRelativeMouseEvent, val))
292 return fail_at(arg, COMMAND_LINE_ERROR);
293 }
294 CommandLineSwitchCase(arg, "max-connections")
295 {
296 errno = 0;
297 unsigned long val = strtoul(arg->Value, NULL, 0);
298
299 if ((errno != 0) || (val > UINT32_MAX))
300 return fail_at(arg, COMMAND_LINE_ERROR);
301 server->maxClientsConnected = val;
302 }
303 CommandLineSwitchCase(arg, "rect")
304 {
305 char* p = NULL;
306 char* tok[4];
307 long x = -1;
308 long y = -1;
309 long w = -1;
310 long h = -1;
311 char* str = _strdup(arg->Value);
312
313 if (!str)
314 return fail_at(arg, COMMAND_LINE_ERROR);
315
316 tok[0] = p = str;
317 p = strchr(p + 1, ',');
318
319 if (!p)
320 {
321 free(str);
322 return fail_at(arg, COMMAND_LINE_ERROR);
323 }
324
325 *p++ = '\0';
326 tok[1] = p;
327 p = strchr(p + 1, ',');
328
329 if (!p)
330 {
331 free(str);
332 return fail_at(arg, COMMAND_LINE_ERROR);
333 }
334
335 *p++ = '\0';
336 tok[2] = p;
337 p = strchr(p + 1, ',');
338
339 if (!p)
340 {
341 free(str);
342 return fail_at(arg, COMMAND_LINE_ERROR);
343 }
344
345 *p++ = '\0';
346 tok[3] = p;
347 x = strtol(tok[0], NULL, 0);
348
349 if (errno != 0)
350 goto fail;
351
352 y = strtol(tok[1], NULL, 0);
353
354 if (errno != 0)
355 goto fail;
356
357 w = strtol(tok[2], NULL, 0);
358
359 if (errno != 0)
360 goto fail;
361
362 h = strtol(tok[3], NULL, 0);
363
364 if (errno != 0)
365 goto fail;
366
367 fail:
368 free(str);
369
370 if ((x < 0) || (y < 0) || (w < 1) || (h < 1) || (errno != 0))
371 return fail_at(arg, COMMAND_LINE_ERROR);
372
373 if ((x > UINT16_MAX) || (y > UINT16_MAX) || (x + w > UINT16_MAX) ||
374 (y + h > UINT16_MAX))
375 return fail_at(arg, COMMAND_LINE_ERROR);
376 server->subRect.left = (UINT16)x;
377 server->subRect.top = (UINT16)y;
378 server->subRect.right = (UINT16)(x + w);
379 server->subRect.bottom = (UINT16)(y + h);
380 server->shareSubRect = TRUE;
381 }
382 CommandLineSwitchCase(arg, "auth")
383 {
384 server->authentication = arg->Value ? TRUE : FALSE;
385 }
386 CommandLineSwitchCase(arg, "remote-guard")
387 {
388 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteCredentialGuard,
389 arg->Value ? TRUE : FALSE))
390 return fail_at(arg, COMMAND_LINE_ERROR);
391 }
392 CommandLineSwitchCase(arg, "restricted-admin")
393 {
394 if (!freerdp_settings_set_bool(settings, FreeRDP_RestrictedAdminModeSupported,
395 arg->Value ? TRUE : FALSE))
396 return fail_at(arg, COMMAND_LINE_ERROR);
397 }
398 CommandLineSwitchCase(arg, "vmconnect")
399 {
400 if (!freerdp_settings_set_bool(settings, FreeRDP_VmConnectMode,
401 arg->Value ? TRUE : FALSE))
402 return fail_at(arg, COMMAND_LINE_ERROR);
403 }
404 CommandLineSwitchCase(arg, "sec")
405 {
406 if (strcmp("rdp", arg->Value) == 0) /* Standard RDP */
407 {
408 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, TRUE))
409 return fail_at(arg, COMMAND_LINE_ERROR);
410 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE))
411 return fail_at(arg, COMMAND_LINE_ERROR);
412 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
413 return fail_at(arg, COMMAND_LINE_ERROR);
414 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
415 return fail_at(arg, COMMAND_LINE_ERROR);
416 if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, TRUE))
417 return fail_at(arg, COMMAND_LINE_ERROR);
418 }
419 else if (strcmp("tls", arg->Value) == 0) /* TLS */
420 {
421 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
422 return fail_at(arg, COMMAND_LINE_ERROR);
423 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, TRUE))
424 return fail_at(arg, COMMAND_LINE_ERROR);
425 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
426 return fail_at(arg, COMMAND_LINE_ERROR);
427 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
428 return fail_at(arg, COMMAND_LINE_ERROR);
429 }
430 else if (strcmp("nla", arg->Value) == 0) /* NLA */
431 {
432 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
433 return fail_at(arg, COMMAND_LINE_ERROR);
434 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE))
435 return fail_at(arg, COMMAND_LINE_ERROR);
436 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, TRUE))
437 return fail_at(arg, COMMAND_LINE_ERROR);
438 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
439 return fail_at(arg, COMMAND_LINE_ERROR);
440 }
441 else if (strcmp("ext", arg->Value) == 0) /* NLA Extended */
442 {
443 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
444 return fail_at(arg, COMMAND_LINE_ERROR);
445 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE))
446 return fail_at(arg, COMMAND_LINE_ERROR);
447 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
448 return fail_at(arg, COMMAND_LINE_ERROR);
449 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, TRUE))
450 return fail_at(arg, COMMAND_LINE_ERROR);
451 }
452 else
453 {
454 WLog_ERR(TAG, "unknown protocol security: %s", arg->Value);
455 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
456 }
457 }
458 CommandLineSwitchCase(arg, "sec-rdp")
459 {
460 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity,
461 arg->Value ? TRUE : FALSE))
462 return fail_at(arg, COMMAND_LINE_ERROR);
463 }
464 CommandLineSwitchCase(arg, "sec-tls")
465 {
466 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity,
467 arg->Value ? TRUE : FALSE))
468 return fail_at(arg, COMMAND_LINE_ERROR);
469 }
470 CommandLineSwitchCase(arg, "sec-nla")
471 {
472 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity,
473 arg->Value ? TRUE : FALSE))
474 return fail_at(arg, COMMAND_LINE_ERROR);
475 }
476 CommandLineSwitchCase(arg, "sec-ext")
477 {
478 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity,
479 arg->Value ? TRUE : FALSE))
480 return fail_at(arg, COMMAND_LINE_ERROR);
481 }
482 CommandLineSwitchCase(arg, "sam-file")
483 {
484 if (!freerdp_settings_set_string(settings, FreeRDP_NtlmSamFile, arg->Value))
485 return fail_at(arg, COMMAND_LINE_ERROR);
486 }
487 CommandLineSwitchCase(arg, "log-level")
488 {
489 wLog* root = WLog_GetRoot();
490
491 if (!WLog_SetStringLogLevel(root, arg->Value))
492 return fail_at(arg, COMMAND_LINE_ERROR);
493 }
494 CommandLineSwitchCase(arg, "log-filters")
495 {
496 if (!WLog_AddStringLogFilters(arg->Value))
497 return fail_at(arg, COMMAND_LINE_ERROR);
498 }
499 CommandLineSwitchCase(arg, "nsc")
500 {
501 if (!freerdp_settings_set_bool(settings, FreeRDP_NSCodec, arg->Value ? TRUE : FALSE))
502 return fail_at(arg, COMMAND_LINE_ERROR);
503 }
504 CommandLineSwitchCase(arg, "rfx")
505 {
506 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec,
507 arg->Value ? TRUE : FALSE))
508 return fail_at(arg, COMMAND_LINE_ERROR);
509 }
510 CommandLineSwitchCase(arg, "gfx")
511 {
512 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportGraphicsPipeline,
513 arg->Value ? TRUE : FALSE))
514 return fail_at(arg, COMMAND_LINE_ERROR);
515 }
516 CommandLineSwitchCase(arg, "gfx-progressive")
517 {
518 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxProgressive,
519 arg->Value ? TRUE : FALSE))
520 return fail_at(arg, COMMAND_LINE_ERROR);
521 }
522 CommandLineSwitchCase(arg, "gfx-rfx")
523 {
524 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec,
525 arg->Value ? TRUE : FALSE))
526 return fail_at(arg, COMMAND_LINE_ERROR);
527 }
528 CommandLineSwitchCase(arg, "gfx-planar")
529 {
530 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxPlanar, arg->Value ? TRUE : FALSE))
531 return fail_at(arg, COMMAND_LINE_ERROR);
532 }
533 CommandLineSwitchCase(arg, "gfx-avc420")
534 {
535 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxH264, arg->Value ? TRUE : FALSE))
536 return fail_at(arg, COMMAND_LINE_ERROR);
537 }
538 CommandLineSwitchCase(arg, "gfx-avc444")
539 {
540 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444v2,
541 arg->Value ? TRUE : FALSE))
542 return fail_at(arg, COMMAND_LINE_ERROR);
543 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444, arg->Value ? TRUE : FALSE))
544 return fail_at(arg, COMMAND_LINE_ERROR);
545 }
546 CommandLineSwitchCase(arg, "keytab")
547 {
548 if (!freerdp_settings_set_string(settings, FreeRDP_KerberosKeytab, arg->Value))
549 return fail_at(arg, COMMAND_LINE_ERROR);
550 }
551 CommandLineSwitchCase(arg, "ccache")
552 {
553 if (!freerdp_settings_set_string(settings, FreeRDP_KerberosCache, arg->Value))
554 return fail_at(arg, COMMAND_LINE_ERROR);
555 }
556 CommandLineSwitchCase(arg, "tls-secrets-file")
557 {
558 if (!freerdp_settings_set_string(settings, FreeRDP_TlsSecretsFile, arg->Value))
559 return fail_at(arg, COMMAND_LINE_ERROR);
560 }
561 CommandLineSwitchDefault(arg)
562 {
563 }
564 CommandLineSwitchEnd(arg)
565 } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
566
567 arg = CommandLineFindArgumentA(cargs, "monitors");
568
569 if (arg && (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
570 {
571 UINT32 numMonitors = 0;
572 MONITOR_DEF monitors[16] = { 0 };
573 numMonitors = shadow_enum_monitors(monitors, 16);
574
575 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
576 {
577 /* Select monitors */
578 long val = strtol(arg->Value, NULL, 0);
579
580 if ((val < 0) || (errno != 0) || ((UINT32)val >= numMonitors))
581 status = COMMAND_LINE_STATUS_PRINT;
582
583 server->selectedMonitor = (UINT32)val;
584 }
585 else
586 {
587 /* List monitors */
588
589 for (UINT32 index = 0; index < numMonitors; index++)
590 {
591 const MONITOR_DEF* monitor = &monitors[index];
592 const INT64 width = monitor->right - monitor->left + 1;
593 const INT64 height = monitor->bottom - monitor->top + 1;
594 WLog_INFO(
595 TAG, " %s [%" PRIu32 "] %" PRId64 "x%" PRId64 "\t+%" PRId32 "+%" PRId32 "",
596 (monitor->flags == 1) ? "*" : " ", index, width, height, monitor->left,
597 monitor->top);
598 }
599
600 status = COMMAND_LINE_STATUS_PRINT;
601 }
602 }
603
604 /* If we want to disable authentication we need to ensure that NLA security
605 * is not activated. Only TLS and RDP security allow anonymous login.
606 */
607 if (!server->authentication && !freerdp_settings_get_bool(settings, FreeRDP_VmConnectMode))
608 {
609 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
610 return COMMAND_LINE_ERROR;
611 }
612 return status;
613}
614
615static DWORD WINAPI shadow_server_thread(LPVOID arg)
616{
617 rdpShadowServer* server = (rdpShadowServer*)arg;
618 BOOL running = TRUE;
619 DWORD status = 0;
620 freerdp_listener* listener = server->listener;
621 shadow_subsystem_start(server->subsystem);
622
623 while (running)
624 {
625 HANDLE events[MAXIMUM_WAIT_OBJECTS] = { 0 };
626 DWORD nCount = 0;
627 events[nCount++] = server->StopEvent;
628 nCount += listener->GetEventHandles(listener, &events[nCount], ARRAYSIZE(events) - nCount);
629
630 if (nCount <= 1)
631 {
632 WLog_ERR(TAG, "Failed to get FreeRDP file descriptor");
633 break;
634 }
635
636 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
637
638 switch (status)
639 {
640 case WAIT_FAILED:
641 case WAIT_OBJECT_0:
642 running = FALSE;
643 break;
644
645 default:
646 {
647 if (!listener->CheckFileDescriptor(listener))
648 {
649 WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
650 running = FALSE;
651 }
652 else
653 {
654#ifdef _WIN32
655 Sleep(100); /* FIXME: listener event handles */
656#endif
657 }
658 }
659 break;
660 }
661 }
662
663 listener->Close(listener);
664 shadow_subsystem_stop(server->subsystem);
665
666 /* Signal to the clients that server is being stopped and wait for them
667 * to disconnect. */
668 if (shadow_client_boardcast_quit(server, 0))
669 {
670 while (ArrayList_Count(server->clients) > 0)
671 {
672 Sleep(100);
673 }
674 }
675
676 ExitThread(0);
677 return 0;
678}
679
680static BOOL open_port(rdpShadowServer* server, char* address)
681{
682 BOOL status = 0;
683 char* modaddr = address;
684
685 if (modaddr)
686 {
687 if (modaddr[0] == '[')
688 {
689 char* end = strchr(address, ']');
690 if (!end)
691 {
692 WLog_ERR(TAG, "Could not parse bind-address %s", address);
693 return -1;
694 }
695 *end++ = '\0';
696 if (strlen(end) > 0)
697 {
698 WLog_ERR(TAG, "Excess data after IPv6 address: '%s'", end);
699 return -1;
700 }
701 modaddr++;
702 }
703 }
704 status = server->listener->Open(server->listener, modaddr, (UINT16)server->port);
705
706 if (!status)
707 {
708 WLog_ERR(TAG,
709 "Problem creating TCP listener. (Port already used or insufficient permissions?)");
710 }
711
712 return status;
713}
714
715int shadow_server_start(rdpShadowServer* server)
716{
717 BOOL ipc = 0;
718 BOOL status = 0;
719 WSADATA wsaData;
720
721 if (!server)
722 return -1;
723
724 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
725 return -1;
726
727#ifndef _WIN32
728 (void)signal(SIGPIPE, SIG_IGN);
729#endif
730 server->screen = shadow_screen_new(server);
731
732 if (!server->screen)
733 {
734 WLog_ERR(TAG, "screen_new failed");
735 return -1;
736 }
737
738 server->capture = shadow_capture_new(server);
739
740 if (!server->capture)
741 {
742 WLog_ERR(TAG, "capture_new failed");
743 return -1;
744 }
745
746 /* Bind magic:
747 *
748 * empty ... bind TCP all
749 * <local path> ... bind local (IPC)
750 * bind-socket,<address> ... bind TCP to specified interface
751 */
752 ipc = server->ipcSocket && (strncmp(bind_address, server->ipcSocket,
753 strnlen(bind_address, sizeof(bind_address))) != 0);
754 if (!ipc)
755 {
756 size_t count = 0;
757
758 char** ptr = CommandLineParseCommaSeparatedValuesEx(NULL, server->ipcSocket, &count);
759 if (!ptr || (count <= 1))
760 {
761 if (server->ipcSocket == NULL)
762 {
763 if (!open_port(server, NULL))
764 {
765 CommandLineParserFree(ptr);
766 return -1;
767 }
768 }
769 else
770 {
771 CommandLineParserFree(ptr);
772 return -1;
773 }
774 }
775
776 WINPR_ASSERT(ptr || (count == 0));
777 for (size_t x = 1; x < count; x++)
778 {
779 BOOL success = open_port(server, ptr[x]);
780 if (!success)
781 {
782 CommandLineParserFree(ptr);
783 return -1;
784 }
785 }
786 CommandLineParserFree(ptr);
787 }
788 else
789 {
790 status = server->listener->OpenLocal(server->listener, server->ipcSocket);
791
792 if (!status)
793 {
794 WLog_ERR(TAG, "Problem creating local socket listener. (Port already used or "
795 "insufficient permissions?)");
796 return -1;
797 }
798 }
799
800 if (!(server->thread = CreateThread(NULL, 0, shadow_server_thread, (void*)server, 0, NULL)))
801 {
802 return -1;
803 }
804
805 return 0;
806}
807
808int shadow_server_stop(rdpShadowServer* server)
809{
810 if (!server)
811 return -1;
812
813 if (server->thread)
814 {
815 (void)SetEvent(server->StopEvent);
816 (void)WaitForSingleObject(server->thread, INFINITE);
817 (void)CloseHandle(server->thread);
818 server->thread = NULL;
819 if (server->listener && server->listener->Close)
820 server->listener->Close(server->listener);
821 }
822
823 if (server->screen)
824 {
825 shadow_screen_free(server->screen);
826 server->screen = NULL;
827 }
828
829 if (server->capture)
830 {
831 shadow_capture_free(server->capture);
832 server->capture = NULL;
833 }
834
835 return 0;
836}
837
838static int shadow_server_init_config_path(rdpShadowServer* server)
839{
840 if (!server->ConfigPath)
841 {
842 char* configHome = freerdp_settings_get_config_path();
843
844 if (configHome)
845 {
846 if (!winpr_PathFileExists(configHome) && !winpr_PathMakePath(configHome, 0))
847 {
848 WLog_ERR(TAG, "Failed to create directory '%s'", configHome);
849 free(configHome);
850 return -1;
851 }
852
853 server->ConfigPath = configHome;
854 }
855 }
856
857 if (!server->ConfigPath)
858 return -1; /* no usable config path */
859
860 return 1;
861}
862
863static BOOL shadow_server_create_certificate(rdpShadowServer* server, const char* filepath)
864{
865 BOOL rc = FALSE;
866 char* makecert_argv[6] = { "makecert", "-rdp", "-live", "-silent", "-y", "5" };
867
868 WINPR_STATIC_ASSERT(ARRAYSIZE(makecert_argv) <= INT_MAX);
869 const size_t makecert_argc = ARRAYSIZE(makecert_argv);
870
871 MAKECERT_CONTEXT* makecert = makecert_context_new();
872
873 if (!makecert)
874 goto out_fail;
875
876 if (makecert_context_process(makecert, (int)makecert_argc, makecert_argv) < 0)
877 goto out_fail;
878
879 if (makecert_context_set_output_file_name(makecert, "shadow") != 1)
880 goto out_fail;
881
882 WINPR_ASSERT(server);
883 WINPR_ASSERT(filepath);
884 if (!winpr_PathFileExists(server->CertificateFile))
885 {
886 if (makecert_context_output_certificate_file(makecert, filepath) != 1)
887 goto out_fail;
888 }
889
890 if (!winpr_PathFileExists(server->PrivateKeyFile))
891 {
892 if (makecert_context_output_private_key_file(makecert, filepath) != 1)
893 goto out_fail;
894 }
895 rc = TRUE;
896out_fail:
897 makecert_context_free(makecert);
898 return rc;
899}
900static BOOL shadow_server_init_certificate(rdpShadowServer* server)
901{
902 char* filepath = NULL;
903 BOOL ret = FALSE;
904
905 WINPR_ASSERT(server);
906
907 if (!winpr_PathFileExists(server->ConfigPath) && !winpr_PathMakePath(server->ConfigPath, 0))
908 {
909 WLog_ERR(TAG, "Failed to create directory '%s'", server->ConfigPath);
910 return FALSE;
911 }
912
913 if (!(filepath = GetCombinedPath(server->ConfigPath, "shadow")))
914 return FALSE;
915
916 if (!winpr_PathFileExists(filepath) && !winpr_PathMakePath(filepath, 0))
917 {
918 if (!winpr_PathMakePath(filepath, NULL))
919 {
920 WLog_ERR(TAG, "Failed to create directory '%s'", filepath);
921 goto out_fail;
922 }
923 }
924
925 server->CertificateFile = GetCombinedPath(filepath, "shadow.crt");
926 server->PrivateKeyFile = GetCombinedPath(filepath, "shadow.key");
927
928 if (!server->CertificateFile || !server->PrivateKeyFile)
929 goto out_fail;
930
931 if ((!winpr_PathFileExists(server->CertificateFile)) ||
932 (!winpr_PathFileExists(server->PrivateKeyFile)))
933 {
934 if (!shadow_server_create_certificate(server, filepath))
935 goto out_fail;
936 }
937
938 {
939 rdpSettings* settings = server->settings;
940 WINPR_ASSERT(settings);
941
942 {
943 rdpPrivateKey* key = freerdp_key_new_from_file_enc(server->PrivateKeyFile, NULL);
944 if (!key)
945 goto out_fail;
946 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerRsaKey, key, 1))
947 goto out_fail;
948 }
949 {
950 rdpCertificate* cert = freerdp_certificate_new_from_file(server->CertificateFile);
951 if (!cert)
952 goto out_fail;
953
954 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerCertificate, cert, 1))
955 goto out_fail;
956
957 if (!freerdp_certificate_is_rdp_security_compatible(cert))
958 {
959 if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, FALSE))
960 goto out_fail;
961 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
962 goto out_fail;
963 }
964 }
965 }
966
967 ret = TRUE;
968out_fail:
969 free(filepath);
970 return ret;
971}
972
973static BOOL shadow_server_check_peer_restrictions(freerdp_listener* listener)
974{
975 WINPR_ASSERT(listener);
976
977 rdpShadowServer* server = (rdpShadowServer*)listener->info;
978 WINPR_ASSERT(server);
979
980 if (server->maxClientsConnected > 0)
981 {
982 const size_t count = ArrayList_Count(server->clients);
983 if (count >= server->maxClientsConnected)
984 {
985 WLog_WARN(TAG, "connection limit [%" PRIuz "] reached, discarding client",
986 server->maxClientsConnected);
987 return FALSE;
988 }
989 }
990 return TRUE;
991}
992
993int shadow_server_init(rdpShadowServer* server)
994{
995 int status = 0;
996 winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT);
997 WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi());
998
999 if (!(server->clients = ArrayList_New(TRUE)))
1000 goto fail;
1001
1002 if (!(server->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
1003 goto fail;
1004
1005 if (!InitializeCriticalSectionAndSpinCount(&(server->lock), 4000))
1006 goto fail;
1007
1008 status = shadow_server_init_config_path(server);
1009
1010 if (status < 0)
1011 goto fail;
1012
1013 if (!shadow_server_init_certificate(server))
1014 goto fail;
1015
1016 server->listener = freerdp_listener_new();
1017
1018 if (!server->listener)
1019 goto fail;
1020
1021 server->listener->info = (void*)server;
1022 server->listener->CheckPeerAcceptRestrictions = shadow_server_check_peer_restrictions;
1023 server->listener->PeerAccepted = shadow_client_accepted;
1024 server->subsystem = shadow_subsystem_new();
1025
1026 if (!server->subsystem)
1027 goto fail;
1028
1029 status = shadow_subsystem_init(server->subsystem, server);
1030 if (status < 0)
1031 goto fail;
1032
1033 return status;
1034
1035fail:
1036 shadow_server_uninit(server);
1037 WLog_ERR(TAG, "Failed to initialize shadow server");
1038 return -1;
1039}
1040
1041int shadow_server_uninit(rdpShadowServer* server)
1042{
1043 if (!server)
1044 return -1;
1045
1046 shadow_server_stop(server);
1047 shadow_subsystem_uninit(server->subsystem);
1048 shadow_subsystem_free(server->subsystem);
1049 server->subsystem = NULL;
1050 freerdp_listener_free(server->listener);
1051 server->listener = NULL;
1052 free(server->CertificateFile);
1053 server->CertificateFile = NULL;
1054 free(server->PrivateKeyFile);
1055 server->PrivateKeyFile = NULL;
1056 free(server->ConfigPath);
1057 server->ConfigPath = NULL;
1058 DeleteCriticalSection(&(server->lock));
1059 (void)CloseHandle(server->StopEvent);
1060 server->StopEvent = NULL;
1061 ArrayList_Free(server->clients);
1062 server->clients = NULL;
1063 return 1;
1064}
1065
1066rdpShadowServer* shadow_server_new(void)
1067{
1068 rdpShadowServer* server = NULL;
1069 server = (rdpShadowServer*)calloc(1, sizeof(rdpShadowServer));
1070
1071 if (!server)
1072 return NULL;
1073
1074 server->SupportMultiRectBitmapUpdates = TRUE;
1075 server->port = 3389;
1076 server->mayView = TRUE;
1077 server->mayInteract = TRUE;
1078 server->h264RateControlMode = H264_RATECONTROL_VBR;
1079 server->h264BitRate = 10000000;
1080 server->h264FrameRate = 30;
1081 server->h264QP = 0;
1082 server->authentication = TRUE;
1084 return server;
1085}
1086
1087void shadow_server_free(rdpShadowServer* server)
1088{
1089 if (!server)
1090 return;
1091
1092 free(server->ipcSocket);
1093 server->ipcSocket = NULL;
1094 freerdp_settings_free(server->settings);
1095 server->settings = NULL;
1096 free(server);
1097}
FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *param)
Sets a string settings value. The param is copied.
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
FREERDP_API rdpSettings * freerdp_settings_new(DWORD flags)
creates a new setting struct
FREERDP_API BOOL freerdp_settings_set_pointer_len(rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id, const void *data, size_t len)
Set a pointer to value data.
FREERDP_API void freerdp_settings_free(rdpSettings *settings)
Free a settings struct with all data in it.
#define FREERDP_SETTINGS_SERVER_MODE
FREERDP_API char * freerdp_settings_get_config_path(void)
return the configuration directory for the library
FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL param)
Sets a BOOL settings value.