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