diff options
Diffstat (limited to 'server/shadow/shadow_server.c')
-rw-r--r-- | server/shadow/shadow_server.c | 1028 |
1 files changed, 1028 insertions, 0 deletions
diff --git a/server/shadow/shadow_server.c b/server/shadow/shadow_server.c new file mode 100644 index 0000000..067dcc6 --- /dev/null +++ b/server/shadow/shadow_server.c @@ -0,0 +1,1028 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * Copyright 2017 Armin Novak <armin.novak@thincast.com> + * Copyright 2017 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <freerdp/config.h> + +#include <errno.h> + +#include <winpr/crt.h> +#include <winpr/ssl.h> +#include <winpr/path.h> +#include <winpr/cmdline.h> +#include <winpr/winsock.h> + +#include <freerdp/log.h> +#include <freerdp/version.h> + +#include <winpr/tools/makecert.h> + +#ifndef _WIN32 +#include <sys/select.h> +#include <signal.h> +#endif + +#include "shadow.h" + +#define TAG SERVER_TAG("shadow") + +static const char bind_address[] = "bind-address,"; + +static int shadow_server_print_command_line_help(int argc, char** argv, + COMMAND_LINE_ARGUMENT_A* largs) +{ + char* str = NULL; + size_t length = 0; + const COMMAND_LINE_ARGUMENT_A* arg = NULL; + if ((argc < 1) || !largs || !argv) + return -1; + + printf("Usage: %s [options]\n", argv[0]); + printf("\n"); + printf("Syntax:\n"); + printf(" /flag (enables flag)\n"); + printf(" /option:<value> (specifies option with value)\n"); + printf(" +toggle -toggle (enables or disables toggle, where '/' is a synonym of '+')\n"); + printf("\n"); + arg = largs; + + do + { + if (arg->Flags & COMMAND_LINE_VALUE_FLAG) + { + printf(" %s", "/"); + printf("%-20s\n", arg->Name); + printf("\t%s\n", arg->Text); + } + else if ((arg->Flags & COMMAND_LINE_VALUE_REQUIRED) || + (arg->Flags & COMMAND_LINE_VALUE_OPTIONAL)) + { + printf(" %s", "/"); + + if (arg->Format) + { + length = (strlen(arg->Name) + strlen(arg->Format) + 2); + str = (char*)malloc(length + 1); + + if (!str) + return -1; + + sprintf_s(str, length + 1, "%s:%s", arg->Name, arg->Format); + printf("%-20s\n", str); + free(str); + } + else + { + printf("%-20s\n", arg->Name); + } + + printf("\t%s\n", arg->Text); + } + else if (arg->Flags & COMMAND_LINE_VALUE_BOOL) + { + length = strlen(arg->Name) + 32; + str = (char*)malloc(length + 1); + + if (!str) + return -1; + + sprintf_s(str, length + 1, "%s (default:%s)", arg->Name, arg->Default ? "on" : "off"); + printf(" %s", arg->Default ? "-" : "+"); + printf("%-20s\n", str); + free(str); + printf("\t%s\n", arg->Text); + } + } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL); + + return 1; +} + +int shadow_server_command_line_status_print(rdpShadowServer* server, int argc, char** argv, + int status, COMMAND_LINE_ARGUMENT_A* cargs) +{ + WINPR_UNUSED(server); + + if (status == COMMAND_LINE_STATUS_PRINT_VERSION) + { + printf("FreeRDP version %s (git %s)\n", FREERDP_VERSION_FULL, FREERDP_GIT_REVISION); + return COMMAND_LINE_STATUS_PRINT_VERSION; + } + else if (status == COMMAND_LINE_STATUS_PRINT_BUILDCONFIG) + { + printf("%s\n", freerdp_get_build_config()); + return COMMAND_LINE_STATUS_PRINT_BUILDCONFIG; + } + else if (status == COMMAND_LINE_STATUS_PRINT) + { + return COMMAND_LINE_STATUS_PRINT; + } + else if (status < 0) + { + if (shadow_server_print_command_line_help(argc, argv, cargs) < 0) + return -1; + + return COMMAND_LINE_STATUS_PRINT_HELP; + } + + return 1; +} + +int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** argv, + COMMAND_LINE_ARGUMENT_A* cargs) +{ + int status = 0; + DWORD flags = 0; + const COMMAND_LINE_ARGUMENT_A* arg = NULL; + rdpSettings* settings = server->settings; + + if ((argc < 2) || !argv || !cargs) + return 1; + + CommandLineClearArgumentsA(cargs); + flags = COMMAND_LINE_SEPARATOR_COLON; + flags |= COMMAND_LINE_SIGIL_SLASH | COMMAND_LINE_SIGIL_PLUS_MINUS; + status = CommandLineParseArgumentsA(argc, argv, cargs, flags, server, NULL, NULL); + + if (status < 0) + return status; + + arg = cargs; + errno = 0; + + do + { + if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT)) + continue; + + CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "port") + { + long val = strtol(arg->Value, NULL, 0); + + if ((errno != 0) || (val <= 0) || (val > UINT16_MAX)) + return -1; + + server->port = (DWORD)val; + } + CommandLineSwitchCase(arg, "ipc-socket") + { + /* /bind-address is incompatible */ + if (server->ipcSocket) + return -1; + server->ipcSocket = _strdup(arg->Value); + + if (!server->ipcSocket) + return -1; + } + CommandLineSwitchCase(arg, "bind-address") + { + int rc = 0; + size_t len = strlen(arg->Value) + sizeof(bind_address); + /* /ipc-socket is incompatible */ + if (server->ipcSocket) + return -1; + server->ipcSocket = calloc(len, sizeof(CHAR)); + + if (!server->ipcSocket) + return -1; + + rc = _snprintf(server->ipcSocket, len, "%s%s", bind_address, arg->Value); + if ((rc < 0) || ((size_t)rc != len - 1)) + return -1; + } + CommandLineSwitchCase(arg, "may-view") + { + server->mayView = arg->Value ? TRUE : FALSE; + } + CommandLineSwitchCase(arg, "may-interact") + { + server->mayInteract = arg->Value ? TRUE : FALSE; + } + CommandLineSwitchCase(arg, "max-connections") + { + errno = 0; + unsigned long val = strtoul(arg->Value, NULL, 0); + + if ((errno != 0) || (val > UINT32_MAX)) + return -1; + server->maxClientsConnected = val; + } + CommandLineSwitchCase(arg, "rect") + { + char* p = NULL; + char* tok[4]; + long x = -1; + long y = -1; + long w = -1; + long h = -1; + char* str = _strdup(arg->Value); + + if (!str) + return -1; + + tok[0] = p = str; + p = strchr(p + 1, ','); + + if (!p) + { + free(str); + return -1; + } + + *p++ = '\0'; + tok[1] = p; + p = strchr(p + 1, ','); + + if (!p) + { + free(str); + return -1; + } + + *p++ = '\0'; + tok[2] = p; + p = strchr(p + 1, ','); + + if (!p) + { + free(str); + return -1; + } + + *p++ = '\0'; + tok[3] = p; + x = strtol(tok[0], NULL, 0); + + if (errno != 0) + goto fail; + + y = strtol(tok[1], NULL, 0); + + if (errno != 0) + goto fail; + + w = strtol(tok[2], NULL, 0); + + if (errno != 0) + goto fail; + + h = strtol(tok[3], NULL, 0); + + if (errno != 0) + goto fail; + + fail: + free(str); + + if ((x < 0) || (y < 0) || (w < 1) || (h < 1) || (errno != 0)) + return -1; + + if ((x > UINT16_MAX) || (y > UINT16_MAX) || (x + w > UINT16_MAX) || + (y + h > UINT16_MAX)) + return -1; + server->subRect.left = (UINT16)x; + server->subRect.top = (UINT16)y; + server->subRect.right = (UINT16)(x + w); + server->subRect.bottom = (UINT16)(y + h); + server->shareSubRect = TRUE; + } + CommandLineSwitchCase(arg, "auth") + { + server->authentication = arg->Value ? TRUE : FALSE; + } + CommandLineSwitchCase(arg, "remote-guard") + { + if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteCredentialGuard, + arg->Value ? TRUE : FALSE)) + return COMMAND_LINE_ERROR; + } + CommandLineSwitchCase(arg, "sec") + { + if (strcmp("rdp", arg->Value) == 0) /* Standard RDP */ + { + if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, TRUE)) + return COMMAND_LINE_ERROR; + if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE)) + return COMMAND_LINE_ERROR; + if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE)) + return COMMAND_LINE_ERROR; + if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE)) + return COMMAND_LINE_ERROR; + if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, TRUE)) + return COMMAND_LINE_ERROR; + } + else if (strcmp("tls", arg->Value) == 0) /* TLS */ + { + if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE)) + return COMMAND_LINE_ERROR; + if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, TRUE)) + return COMMAND_LINE_ERROR; + if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE)) + return COMMAND_LINE_ERROR; + if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE)) + return COMMAND_LINE_ERROR; + } + else if (strcmp("nla", arg->Value) == 0) /* NLA */ + { + if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE)) + return COMMAND_LINE_ERROR; + if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE)) + return COMMAND_LINE_ERROR; + if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, TRUE)) + return COMMAND_LINE_ERROR; + if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE)) + return COMMAND_LINE_ERROR; + } + else if (strcmp("ext", arg->Value) == 0) /* NLA Extended */ + { + if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE)) + return COMMAND_LINE_ERROR; + if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE)) + return COMMAND_LINE_ERROR; + if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE)) + return COMMAND_LINE_ERROR; + if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, TRUE)) + return COMMAND_LINE_ERROR; + } + else + { + WLog_ERR(TAG, "unknown protocol security: %s", arg->Value); + } + } + CommandLineSwitchCase(arg, "sec-rdp") + { + if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, + arg->Value ? TRUE : FALSE)) + return COMMAND_LINE_ERROR; + } + CommandLineSwitchCase(arg, "sec-tls") + { + if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, + arg->Value ? TRUE : FALSE)) + return COMMAND_LINE_ERROR; + } + CommandLineSwitchCase(arg, "sec-nla") + { + if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, + arg->Value ? TRUE : FALSE)) + return COMMAND_LINE_ERROR; + } + CommandLineSwitchCase(arg, "sec-ext") + { + if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, + arg->Value ? TRUE : FALSE)) + return COMMAND_LINE_ERROR; + } + CommandLineSwitchCase(arg, "sam-file") + { + freerdp_settings_set_string(settings, FreeRDP_NtlmSamFile, arg->Value); + } + CommandLineSwitchCase(arg, "log-level") + { + wLog* root = WLog_GetRoot(); + + if (!WLog_SetStringLogLevel(root, arg->Value)) + return COMMAND_LINE_ERROR; + } + CommandLineSwitchCase(arg, "log-filters") + { + if (!WLog_AddStringLogFilters(arg->Value)) + return COMMAND_LINE_ERROR; + } + CommandLineSwitchCase(arg, "gfx-progressive") + { + if (!freerdp_settings_set_bool(settings, FreeRDP_GfxProgressive, + arg->Value ? TRUE : FALSE)) + return COMMAND_LINE_ERROR; + } + CommandLineSwitchCase(arg, "gfx-rfx") + { + if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, + arg->Value ? TRUE : FALSE)) + return COMMAND_LINE_ERROR; + } + CommandLineSwitchCase(arg, "gfx-planar") + { + if (!freerdp_settings_set_bool(settings, FreeRDP_GfxPlanar, arg->Value ? TRUE : FALSE)) + return COMMAND_LINE_ERROR; + } + CommandLineSwitchCase(arg, "gfx-avc420") + { + if (!freerdp_settings_set_bool(settings, FreeRDP_GfxH264, arg->Value ? TRUE : FALSE)) + return COMMAND_LINE_ERROR; + } + CommandLineSwitchCase(arg, "gfx-avc444") + { + if (!freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444v2, + arg->Value ? TRUE : FALSE)) + return COMMAND_LINE_ERROR; + if (!freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444, arg->Value ? TRUE : FALSE)) + return COMMAND_LINE_ERROR; + } + CommandLineSwitchCase(arg, "keytab") + { + if (!freerdp_settings_set_string(settings, FreeRDP_KerberosKeytab, arg->Value)) + return COMMAND_LINE_ERROR; + } + CommandLineSwitchCase(arg, "ccache") + { + if (!freerdp_settings_set_string(settings, FreeRDP_KerberosCache, arg->Value)) + return COMMAND_LINE_ERROR; + } + CommandLineSwitchCase(arg, "tls-secrets-file") + { + if (!freerdp_settings_set_string(settings, FreeRDP_TlsSecretsFile, arg->Value)) + return COMMAND_LINE_ERROR; + } + CommandLineSwitchDefault(arg) + { + } + CommandLineSwitchEnd(arg) + } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL); + + arg = CommandLineFindArgumentA(cargs, "monitors"); + + if (arg && (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT)) + { + UINT32 numMonitors = 0; + MONITOR_DEF monitors[16] = { 0 }; + numMonitors = shadow_enum_monitors(monitors, 16); + + if (arg->Flags & COMMAND_LINE_VALUE_PRESENT) + { + /* Select monitors */ + long val = strtol(arg->Value, NULL, 0); + + if ((val < 0) || (errno != 0) || ((UINT32)val >= numMonitors)) + status = COMMAND_LINE_STATUS_PRINT; + + server->selectedMonitor = (UINT32)val; + } + else + { + /* List monitors */ + + for (UINT32 index = 0; index < numMonitors; index++) + { + const MONITOR_DEF* monitor = &monitors[index]; + const INT64 width = monitor->right - monitor->left + 1; + const INT64 height = monitor->bottom - monitor->top + 1; + WLog_INFO(TAG, " %s [%d] %" PRId64 "x%" PRId64 "\t+%" PRId32 "+%" PRId32 "", + (monitor->flags == 1) ? "*" : " ", index, width, height, monitor->left, + monitor->top); + } + + status = COMMAND_LINE_STATUS_PRINT; + } + } + + /* If we want to disable authentication we need to ensure that NLA security + * is not activated. Only TLS and RDP security allow anonymous login. + */ + if (!server->authentication) + { + if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE)) + return COMMAND_LINE_ERROR; + } + return status; +} + +static DWORD WINAPI shadow_server_thread(LPVOID arg) +{ + rdpShadowServer* server = (rdpShadowServer*)arg; + BOOL running = TRUE; + DWORD status = 0; + freerdp_listener* listener = server->listener; + shadow_subsystem_start(server->subsystem); + + while (running) + { + HANDLE events[MAXIMUM_WAIT_OBJECTS] = { 0 }; + DWORD nCount = 0; + events[nCount++] = server->StopEvent; + nCount += listener->GetEventHandles(listener, &events[nCount], ARRAYSIZE(events) - nCount); + + if (nCount <= 1) + { + WLog_ERR(TAG, "Failed to get FreeRDP file descriptor"); + break; + } + + status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); + + switch (status) + { + case WAIT_FAILED: + case WAIT_OBJECT_0: + running = FALSE; + break; + + default: + { + if (!listener->CheckFileDescriptor(listener)) + { + WLog_ERR(TAG, "Failed to check FreeRDP file descriptor"); + running = FALSE; + } + else + { +#ifdef _WIN32 + Sleep(100); /* FIXME: listener event handles */ +#endif + } + } + break; + } + } + + listener->Close(listener); + shadow_subsystem_stop(server->subsystem); + + /* Signal to the clients that server is being stopped and wait for them + * to disconnect. */ + if (shadow_client_boardcast_quit(server, 0)) + { + while (ArrayList_Count(server->clients) > 0) + { + Sleep(100); + } + } + + ExitThread(0); + return 0; +} + +static BOOL open_port(rdpShadowServer* server, char* address) +{ + BOOL status = 0; + char* modaddr = address; + + if (modaddr) + { + if (modaddr[0] == '[') + { + char* end = strchr(address, ']'); + if (!end) + { + WLog_ERR(TAG, "Could not parse bind-address %s", address); + return -1; + } + *end++ = '\0'; + if (strlen(end) > 0) + { + WLog_ERR(TAG, "Excess data after IPv6 address: '%s'", end); + return -1; + } + modaddr++; + } + } + status = server->listener->Open(server->listener, modaddr, (UINT16)server->port); + + if (!status) + { + WLog_ERR(TAG, + "Problem creating TCP listener. (Port already used or insufficient permissions?)"); + } + + return status; +} + +int shadow_server_start(rdpShadowServer* server) +{ + BOOL ipc = 0; + BOOL status = 0; + WSADATA wsaData; + + if (!server) + return -1; + + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) + return -1; + +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif + server->screen = shadow_screen_new(server); + + if (!server->screen) + { + WLog_ERR(TAG, "screen_new failed"); + return -1; + } + + server->capture = shadow_capture_new(server); + + if (!server->capture) + { + WLog_ERR(TAG, "capture_new failed"); + return -1; + } + + /* Bind magic: + * + * emtpy ... bind TCP all + * <local path> ... bind local (IPC) + * bind-socket,<address> ... bind TCP to specified interface + */ + ipc = server->ipcSocket && (strncmp(bind_address, server->ipcSocket, + strnlen(bind_address, sizeof(bind_address))) != 0); + if (!ipc) + { + size_t count = 0; + char** list = CommandLineParseCommaSeparatedValuesEx(NULL, server->ipcSocket, &count); + if (!list || (count <= 1)) + { + if (server->ipcSocket == NULL) + { + if (!open_port(server, NULL)) + { + free(list); + return -1; + } + } + else + { + free(list); + return -1; + } + } + + WINPR_ASSERT(list || (count == 0)); + for (size_t x = 1; x < count; x++) + { + BOOL success = open_port(server, list[x]); + if (!success) + { + free(list); + return -1; + } + } + free(list); + } + else + { + status = server->listener->OpenLocal(server->listener, server->ipcSocket); + + if (!status) + { + WLog_ERR(TAG, "Problem creating local socket listener. (Port already used or " + "insufficient permissions?)"); + return -1; + } + } + + if (!(server->thread = CreateThread(NULL, 0, shadow_server_thread, (void*)server, 0, NULL))) + { + return -1; + } + + return 0; +} + +int shadow_server_stop(rdpShadowServer* server) +{ + if (!server) + return -1; + + if (server->thread) + { + SetEvent(server->StopEvent); + WaitForSingleObject(server->thread, INFINITE); + CloseHandle(server->thread); + server->thread = NULL; + if (server->listener && server->listener->Close) + server->listener->Close(server->listener); + } + + if (server->screen) + { + shadow_screen_free(server->screen); + server->screen = NULL; + } + + if (server->capture) + { + shadow_capture_free(server->capture); + server->capture = NULL; + } + + return 0; +} + +static int shadow_server_init_config_path(rdpShadowServer* server) +{ +#ifdef _WIN32 + + if (!server->ConfigPath) + { + server->ConfigPath = GetEnvironmentSubPath("LOCALAPPDATA", "freerdp"); + } + +#endif +#ifdef __APPLE__ + + if (!server->ConfigPath) + { + char* userLibraryPath; + char* userApplicationSupportPath; + userLibraryPath = GetKnownSubPath(KNOWN_PATH_HOME, "Library"); + + if (userLibraryPath) + { + if (!winpr_PathFileExists(userLibraryPath) && !winpr_PathMakePath(userLibraryPath, 0)) + { + WLog_ERR(TAG, "Failed to create directory '%s'", userLibraryPath); + free(userLibraryPath); + return -1; + } + + userApplicationSupportPath = GetCombinedPath(userLibraryPath, "Application Support"); + + if (userApplicationSupportPath) + { + if (!winpr_PathFileExists(userApplicationSupportPath) && + !winpr_PathMakePath(userApplicationSupportPath, 0)) + { + WLog_ERR(TAG, "Failed to create directory '%s'", userApplicationSupportPath); + free(userLibraryPath); + free(userApplicationSupportPath); + return -1; + } + + server->ConfigPath = GetCombinedPath(userApplicationSupportPath, "freerdp"); + } + + free(userLibraryPath); + free(userApplicationSupportPath); + } + } + +#endif + + if (!server->ConfigPath) + { + char* configHome = NULL; + configHome = GetKnownPath(KNOWN_PATH_XDG_CONFIG_HOME); + + if (configHome) + { + if (!winpr_PathFileExists(configHome) && !winpr_PathMakePath(configHome, 0)) + { + WLog_ERR(TAG, "Failed to create directory '%s'", configHome); + free(configHome); + return -1; + } + + server->ConfigPath = GetKnownSubPath(KNOWN_PATH_XDG_CONFIG_HOME, "freerdp"); + free(configHome); + } + } + + if (!server->ConfigPath) + return -1; /* no usable config path */ + + return 1; +} + +static BOOL shadow_server_create_certificate(rdpShadowServer* server, const char* filepath) +{ + BOOL rc = FALSE; + char* makecert_argv[6] = { "makecert", "-rdp", "-live", "-silent", "-y", "5" }; + const size_t makecert_argc = ARRAYSIZE(makecert_argv); + + MAKECERT_CONTEXT* makecert = makecert_context_new(); + + if (!makecert) + goto out_fail; + + if (makecert_context_process(makecert, makecert_argc, makecert_argv) < 0) + goto out_fail; + + if (makecert_context_set_output_file_name(makecert, "shadow") != 1) + goto out_fail; + + WINPR_ASSERT(server); + WINPR_ASSERT(filepath); + if (!winpr_PathFileExists(server->CertificateFile)) + { + if (makecert_context_output_certificate_file(makecert, filepath) != 1) + goto out_fail; + } + + if (!winpr_PathFileExists(server->PrivateKeyFile)) + { + if (makecert_context_output_private_key_file(makecert, filepath) != 1) + goto out_fail; + } + rc = TRUE; +out_fail: + makecert_context_free(makecert); + return rc; +} +static BOOL shadow_server_init_certificate(rdpShadowServer* server) +{ + char* filepath = NULL; + BOOL ret = FALSE; + + WINPR_ASSERT(server); + + if (!winpr_PathFileExists(server->ConfigPath) && !winpr_PathMakePath(server->ConfigPath, 0)) + { + WLog_ERR(TAG, "Failed to create directory '%s'", server->ConfigPath); + return FALSE; + } + + if (!(filepath = GetCombinedPath(server->ConfigPath, "shadow"))) + return FALSE; + + if (!winpr_PathFileExists(filepath) && !winpr_PathMakePath(filepath, 0)) + { + if (!CreateDirectoryA(filepath, 0)) + { + WLog_ERR(TAG, "Failed to create directory '%s'", filepath); + goto out_fail; + } + } + + server->CertificateFile = GetCombinedPath(filepath, "shadow.crt"); + server->PrivateKeyFile = GetCombinedPath(filepath, "shadow.key"); + + if (!server->CertificateFile || !server->PrivateKeyFile) + goto out_fail; + + if ((!winpr_PathFileExists(server->CertificateFile)) || + (!winpr_PathFileExists(server->PrivateKeyFile))) + { + if (!shadow_server_create_certificate(server, filepath)) + goto out_fail; + } + + rdpSettings* settings = server->settings; + WINPR_ASSERT(settings); + + rdpPrivateKey* key = freerdp_key_new_from_file(server->PrivateKeyFile); + if (!key) + goto out_fail; + if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerRsaKey, key, 1)) + goto out_fail; + + rdpCertificate* cert = freerdp_certificate_new_from_file(server->CertificateFile); + if (!cert) + goto out_fail; + + if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerCertificate, cert, 1)) + goto out_fail; + + if (!freerdp_certificate_is_rdp_security_compatible(cert)) + { + if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, FALSE)) + goto out_fail; + if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE)) + goto out_fail; + } + ret = TRUE; +out_fail: + free(filepath); + return ret; +} + +static BOOL shadow_server_check_peer_restrictions(freerdp_listener* listener) +{ + WINPR_ASSERT(listener); + + rdpShadowServer* server = (rdpShadowServer*)listener->info; + WINPR_ASSERT(server); + + if (server->maxClientsConnected > 0) + { + const size_t count = ArrayList_Count(server->clients); + if (count >= server->maxClientsConnected) + { + WLog_WARN(TAG, "connection limit [%" PRIuz "] reached, discarding client", + server->maxClientsConnected); + return FALSE; + } + } + return TRUE; +} + +int shadow_server_init(rdpShadowServer* server) +{ + int status = 0; + winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT); + WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi()); + + if (!(server->clients = ArrayList_New(TRUE))) + goto fail; + + if (!(server->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + goto fail; + + if (!InitializeCriticalSectionAndSpinCount(&(server->lock), 4000)) + goto fail; + + status = shadow_server_init_config_path(server); + + if (status < 0) + goto fail; + + if (!shadow_server_init_certificate(server)) + goto fail; + + server->listener = freerdp_listener_new(); + + if (!server->listener) + goto fail; + + server->listener->info = (void*)server; + server->listener->CheckPeerAcceptRestrictions = shadow_server_check_peer_restrictions; + server->listener->PeerAccepted = shadow_client_accepted; + server->subsystem = shadow_subsystem_new(); + + if (!server->subsystem) + goto fail; + + status = shadow_subsystem_init(server->subsystem, server); + if (status < 0) + goto fail; + + return status; + +fail: + shadow_server_uninit(server); + WLog_ERR(TAG, "Failed to initialize shadow server"); + return -1; +} + +int shadow_server_uninit(rdpShadowServer* server) +{ + if (!server) + return -1; + + shadow_server_stop(server); + shadow_subsystem_uninit(server->subsystem); + shadow_subsystem_free(server->subsystem); + server->subsystem = NULL; + freerdp_listener_free(server->listener); + server->listener = NULL; + free(server->CertificateFile); + server->CertificateFile = NULL; + free(server->PrivateKeyFile); + server->PrivateKeyFile = NULL; + free(server->ConfigPath); + server->ConfigPath = NULL; + DeleteCriticalSection(&(server->lock)); + CloseHandle(server->StopEvent); + server->StopEvent = NULL; + ArrayList_Free(server->clients); + server->clients = NULL; + return 1; +} + +rdpShadowServer* shadow_server_new(void) +{ + rdpShadowServer* server = NULL; + server = (rdpShadowServer*)calloc(1, sizeof(rdpShadowServer)); + + if (!server) + return NULL; + + server->port = 3389; + server->mayView = TRUE; + server->mayInteract = TRUE; + server->rfxMode = RLGR3; + server->h264RateControlMode = H264_RATECONTROL_VBR; + server->h264BitRate = 10000000; + server->h264FrameRate = 30; + server->h264QP = 0; + server->authentication = TRUE; + server->settings = freerdp_settings_new(FREERDP_SETTINGS_SERVER_MODE); + return server; +} + +void shadow_server_free(rdpShadowServer* server) +{ + if (!server) + return; + + free(server->ipcSocket); + server->ipcSocket = NULL; + freerdp_settings_free(server->settings); + server->settings = NULL; + free(server); +} |