Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
115
third_party/FreeRDP/server/CMakeLists.txt
vendored
Normal file
115
third_party/FreeRDP/server/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP Servers
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
#
|
||||
# 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.
|
||||
|
||||
# Servers
|
||||
option(WITH_SHADOW "Compile with shadow server" ON)
|
||||
option(WITH_PROXY "Compile with proxy server" ON)
|
||||
option(WITH_PLATFORM_SERVER "Compile with platform server" ON)
|
||||
|
||||
add_subdirectory(common)
|
||||
if(WITH_SHADOW)
|
||||
if(APPLE)
|
||||
message(WARNING "Mac shadow server implementation no longer compiles")
|
||||
else()
|
||||
add_subdirectory(shadow)
|
||||
endif()
|
||||
endif()
|
||||
if(WITH_PROXY)
|
||||
add_subdirectory(proxy)
|
||||
endif()
|
||||
|
||||
if(WITH_SAMPLE)
|
||||
add_subdirectory(Sample)
|
||||
endif()
|
||||
|
||||
if(WITH_PLATFORM_SERVER)
|
||||
if(NOT WIN32)
|
||||
if(APPLE AND (NOT IOS))
|
||||
# add_subdirectory(Mac)
|
||||
message(WARNING "Mac platform server implementation no longer compiles")
|
||||
endif()
|
||||
else()
|
||||
add_subdirectory(Windows)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED WITH_FREERDS)
|
||||
set(WITH_FREERDS 1)
|
||||
endif()
|
||||
|
||||
if(WITH_FREERDS AND (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/FreeRDS"))
|
||||
add_subdirectory("FreeRDS")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Pick up other servers
|
||||
|
||||
set(FILENAME "ModuleOptions.cmake")
|
||||
file(GLOB FILEPATHS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*/${FILENAME}")
|
||||
|
||||
foreach(FILEPATH ${FILEPATHS})
|
||||
if(${FILEPATH} MATCHES "^([^/]*)/+${FILENAME}")
|
||||
string(REGEX REPLACE "^([^/]*)/+${FILENAME}" "\\1" FREERDP_SERVER ${FILEPATH})
|
||||
set(FREERDP_SERVER_ENABLED 0)
|
||||
include(${FILEPATH})
|
||||
if(FREERDP_SERVER_ENABLED)
|
||||
if(NOT (${FREERDP_SERVER_VENDOR} MATCHES "FreeRDP"))
|
||||
list(APPEND FREERDP_EXTRA_SERVERS ${FREERDP_SERVER})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
foreach(FREERDP_SERVER ${FREERDP_EXTRA_SERVERS})
|
||||
add_subdirectory(${FREERDP_SERVER})
|
||||
endforeach()
|
||||
|
||||
# Do not set Requires.Private if not a static build
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
set(FREERDP_SERVER_PC_REQUIRES_PRIVATE "freerdp${FREERDP_API_VERSION}")
|
||||
set(FREERDP_SERVER_PC_LIBRARY_PRIVATE "-ldl -lpthread")
|
||||
endif()
|
||||
set(FREERDP_SERVER_PC_REQUIRES freerdp${FREERDP_API_VERSION})
|
||||
|
||||
include(pkg-config-install-prefix)
|
||||
cleaning_configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/freerdp-server.pc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/freerdp-server${FREERDP_VERSION_MAJOR}.pc @ONLY
|
||||
)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/freerdp-server${FREERDP_VERSION_MAJOR}.pc
|
||||
DESTINATION ${PKG_CONFIG_PC_INSTALL_DIR}
|
||||
)
|
||||
|
||||
export(PACKAGE freerdp-server)
|
||||
|
||||
setfreerdpcmakeinstalldir(FREERDP_SERVER_CMAKE_INSTALL_DIR "FreeRDP-Server${FREERDP_VERSION_MAJOR}")
|
||||
|
||||
configure_package_config_file(
|
||||
FreeRDP-ServerConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/FreeRDP-ServerConfig.cmake
|
||||
INSTALL_DESTINATION ${FREERDP_SERVER_CMAKE_INSTALL_DIR} PATH_VARS FREERDP_INCLUDE_DIR
|
||||
)
|
||||
|
||||
write_basic_package_version_file(
|
||||
${CMAKE_CURRENT_BINARY_DIR}/FreeRDP-ServerConfigVersion.cmake VERSION ${FREERDP_VERSION}
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FreeRDP-ServerConfig.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/FreeRDP-ServerConfigVersion.cmake
|
||||
DESTINATION ${FREERDP_SERVER_CMAKE_INSTALL_DIR}
|
||||
)
|
||||
|
||||
install(EXPORT FreeRDP-ServerTargets DESTINATION ${FREERDP_SERVER_CMAKE_INSTALL_DIR})
|
||||
13
third_party/FreeRDP/server/FreeRDP-ServerConfig.cmake.in
vendored
Normal file
13
third_party/FreeRDP/server/FreeRDP-ServerConfig.cmake.in
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
include(CMakeFindDependencyMacro)
|
||||
find_dependency(WinPR @FREERDP_VERSION@)
|
||||
find_dependency(FreeRDP @FREERDP_VERSION@)
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
set(FreeRDP-Server_VERSION_MAJOR "@FREERDP_VERSION_MAJOR@")
|
||||
set(FreeRDP-Server_VERSION_MINOR "@FREERDP_VERSION_MINOR@")
|
||||
set(FreeRDP-Server_VERSION_REVISION "@FREERDP_VERSION_REVISION@")
|
||||
|
||||
set_and_check(FreeRDP-Server_INCLUDE_DIR "@PACKAGE_FREERDP_INCLUDE_DIR@")
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/FreeRDP-ServerTargets.cmake")
|
||||
80
third_party/FreeRDP/server/Mac/CMakeLists.txt
vendored
Normal file
80
third_party/FreeRDP/server/Mac/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP Mac OS X Server cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
#
|
||||
# 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.
|
||||
|
||||
set(MODULE_NAME "mfreerdp-server")
|
||||
set(MODULE_PREFIX "FREERDP_SERVER_MAC")
|
||||
|
||||
include(WarnUnmaintained)
|
||||
warn_unmaintained(${MODULE_NAME} "-DWITH_PLATFORM_SERVER=OFF")
|
||||
|
||||
find_library(AUDIO_TOOL AudioToolbox)
|
||||
find_library(CORE_AUDIO CoreAudio)
|
||||
find_library(CORE_VIDEO CoreVideo)
|
||||
find_library(APP_SERVICES ApplicationServices)
|
||||
find_library(IOKIT IOKit)
|
||||
find_library(IOSURFACE IOSurface)
|
||||
find_library(CARBON Carbon)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
mfreerdp.c
|
||||
mfreerdp.h
|
||||
mf_interface.c
|
||||
mf_interface.h
|
||||
mf_event.c
|
||||
mf_event.h
|
||||
mf_peer.c
|
||||
mf_peer.h
|
||||
mf_info.c
|
||||
mf_info.h
|
||||
mf_input.c
|
||||
mf_input.h
|
||||
mf_mountain_lion.c
|
||||
mf_mountain_lion.h
|
||||
)
|
||||
|
||||
if(CHANNEL_AUDIN_SERVER)
|
||||
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} mf_audin.c mf_audin.h)
|
||||
endif()
|
||||
|
||||
if(CHANNEL_RDPSND_SERVER)
|
||||
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} mf_rdpsnd.c mf_rdpsnd.h)
|
||||
|
||||
endif()
|
||||
|
||||
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
|
||||
if(WITH_BINARY_VERSIONING)
|
||||
set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME "${MODULE_NAME}${FREERDP_API_VERSION}")
|
||||
endif()
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS
|
||||
${${MODULE_PREFIX}_LIBS}
|
||||
freerdp-server
|
||||
${AUDIO_TOOL}
|
||||
${CORE_AUDIO}
|
||||
${CORE_VIDEO}
|
||||
${APP_SERVICES}
|
||||
${IOKIT}
|
||||
${IOSURFACE}
|
||||
${CARBON}
|
||||
)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr freerdp)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/Mac")
|
||||
3
third_party/FreeRDP/server/Mac/ModuleOptions.cmake
vendored
Normal file
3
third_party/FreeRDP/server/Mac/ModuleOptions.cmake
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
set(FREERDP_SERVER_NAME "mfreerdp-server")
|
||||
set(FREERDP_SERVER_PLATFORM "X11")
|
||||
set(FREERDP_SERVER_VENDOR "FreeRDP")
|
||||
65
third_party/FreeRDP/server/Mac/mf_audin.c
vendored
Normal file
65
third_party/FreeRDP/server/Mac/mf_audin.c
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Mac OS X Server (Audio Input)
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2023 Pascal Nowack <Pascal.Nowack@gmx.de>
|
||||
*
|
||||
* 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 "mfreerdp.h"
|
||||
|
||||
#include "mf_audin.h"
|
||||
#include "mf_interface.h"
|
||||
|
||||
#include <freerdp/server/server-common.h>
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("mac")
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static UINT mf_peer_audin_data(audin_server_context* audin, const SNDIN_DATA* data)
|
||||
{
|
||||
/* TODO: Implement */
|
||||
WINPR_ASSERT(audin);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
WLog_WARN(TAG, "not implemented");
|
||||
WLog_DBG(TAG, "receive %" PRIdz " bytes.", Stream_Length(data->Data));
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
BOOL mf_peer_audin_init(mfPeerContext* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
context->audin = audin_server_context_new(context->vcm);
|
||||
context->audin->rdpcontext = &context->_p;
|
||||
context->audin->userdata = context;
|
||||
|
||||
context->audin->Data = mf_peer_audin_data;
|
||||
|
||||
return audin_server_set_formats(context->audin, -1, nullptr);
|
||||
}
|
||||
|
||||
void mf_peer_audin_uninit(mfPeerContext* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
audin_server_context_free(context->audin);
|
||||
context->audin = nullptr;
|
||||
}
|
||||
33
third_party/FreeRDP/server/Mac/mf_audin.h
vendored
Normal file
33
third_party/FreeRDP/server/Mac/mf_audin.h
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Mac OS X Server (Audio Input)
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2013 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_MAC_AUDIN_H
|
||||
#define FREERDP_SERVER_MAC_AUDIN_H
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/listener.h>
|
||||
|
||||
#include "mf_types.h"
|
||||
#include "mfreerdp.h"
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL mf_peer_audin_init(mfPeerContext* context);
|
||||
void mf_peer_audin_uninit(mfPeerContext* context);
|
||||
|
||||
#endif /* FREERDP_SERVER_MAC_AUDIN_H */
|
||||
220
third_party/FreeRDP/server/Mac/mf_event.c
vendored
Normal file
220
third_party/FreeRDP/server/Mac/mf_event.c
vendored
Normal file
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* OS X Server Event Handling
|
||||
*
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "mf_event.h"
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("mac")
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static int mf_is_event_set(mfEventQueue* event_queue)
|
||||
{
|
||||
fd_set rfds;
|
||||
int num_set;
|
||||
struct timeval time = WINPR_C_ARRAY_INIT;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(event_queue->pipe_fd[0], &rfds);
|
||||
num_set = select(event_queue->pipe_fd[0] + 1, &rfds, 0, 0, &time);
|
||||
|
||||
return (num_set == 1);
|
||||
}
|
||||
|
||||
static void mf_signal_event(mfEventQueue* event_queue)
|
||||
{
|
||||
int length;
|
||||
|
||||
length = write(event_queue->pipe_fd[1], "sig", 4);
|
||||
|
||||
if (length != 4)
|
||||
WLog_ERR(TAG, "mf_signal_event: error");
|
||||
}
|
||||
|
||||
static void mf_set_event(mfEventQueue* event_queue)
|
||||
{
|
||||
int length;
|
||||
|
||||
length = write(event_queue->pipe_fd[1], "sig", 4);
|
||||
|
||||
if (length != 4)
|
||||
WLog_ERR(TAG, "mf_set_event: error");
|
||||
}
|
||||
|
||||
static void mf_clear_events(mfEventQueue* event_queue)
|
||||
{
|
||||
int length;
|
||||
|
||||
while (mf_is_event_set(event_queue))
|
||||
{
|
||||
length = read(event_queue->pipe_fd[0], &length, 4);
|
||||
|
||||
if (length != 4)
|
||||
WLog_ERR(TAG, "mf_clear_event: error");
|
||||
}
|
||||
}
|
||||
|
||||
static void mf_clear_event(mfEventQueue* event_queue)
|
||||
{
|
||||
int length;
|
||||
|
||||
length = read(event_queue->pipe_fd[0], &length, 4);
|
||||
|
||||
if (length != 4)
|
||||
WLog_ERR(TAG, "mf_clear_event: error");
|
||||
}
|
||||
|
||||
void mf_event_push(mfEventQueue* event_queue, mfEvent* event)
|
||||
{
|
||||
pthread_mutex_lock(&(event_queue->mutex));
|
||||
|
||||
if (event_queue->count >= event_queue->size)
|
||||
{
|
||||
event_queue->size *= 2;
|
||||
event_queue->events =
|
||||
(mfEvent**)realloc((void*)event_queue->events, sizeof(mfEvent*) * event_queue->size);
|
||||
}
|
||||
|
||||
event_queue->events[(event_queue->count)++] = event;
|
||||
|
||||
pthread_mutex_unlock(&(event_queue->mutex));
|
||||
|
||||
mf_set_event(event_queue);
|
||||
}
|
||||
|
||||
mfEvent* mf_event_peek(mfEventQueue* event_queue)
|
||||
{
|
||||
mfEvent* event;
|
||||
|
||||
pthread_mutex_lock(&(event_queue->mutex));
|
||||
|
||||
if (event_queue->count < 1)
|
||||
event = nullptr;
|
||||
else
|
||||
event = event_queue->events[0];
|
||||
|
||||
pthread_mutex_unlock(&(event_queue->mutex));
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
mfEvent* mf_event_pop(mfEventQueue* event_queue)
|
||||
{
|
||||
mfEvent* event;
|
||||
|
||||
pthread_mutex_lock(&(event_queue->mutex));
|
||||
|
||||
if (event_queue->count < 1)
|
||||
return nullptr;
|
||||
|
||||
/* remove event signal */
|
||||
mf_clear_event(event_queue);
|
||||
|
||||
event = event_queue->events[0];
|
||||
(event_queue->count)--;
|
||||
|
||||
memmove(&event_queue->events[0], &event_queue->events[1], event_queue->count * sizeof(void*));
|
||||
|
||||
pthread_mutex_unlock(&(event_queue->mutex));
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
mfEventRegion* mf_event_region_new(int x, int y, int width, int height)
|
||||
{
|
||||
mfEventRegion* event_region = malloc(sizeof(mfEventRegion));
|
||||
|
||||
if (event_region != nullptr)
|
||||
{
|
||||
event_region->x = x;
|
||||
event_region->y = y;
|
||||
event_region->width = width;
|
||||
event_region->height = height;
|
||||
}
|
||||
|
||||
return event_region;
|
||||
}
|
||||
|
||||
void mf_event_region_free(mfEventRegion* event_region)
|
||||
{
|
||||
free(event_region);
|
||||
}
|
||||
|
||||
mfEvent* mf_event_new(int type)
|
||||
{
|
||||
mfEvent* event = malloc(sizeof(mfEvent));
|
||||
if (!event)
|
||||
return nullptr;
|
||||
event->type = type;
|
||||
return event;
|
||||
}
|
||||
|
||||
void mf_event_free(mfEvent* event)
|
||||
{
|
||||
free(event);
|
||||
}
|
||||
|
||||
mfEventQueue* mf_event_queue_new()
|
||||
{
|
||||
mfEventQueue* event_queue = malloc(sizeof(mfEventQueue));
|
||||
|
||||
if (event_queue != nullptr)
|
||||
{
|
||||
event_queue->pipe_fd[0] = -1;
|
||||
event_queue->pipe_fd[1] = -1;
|
||||
|
||||
event_queue->size = 16;
|
||||
event_queue->count = 0;
|
||||
event_queue->events = (mfEvent**)malloc(sizeof(mfEvent*) * event_queue->size);
|
||||
|
||||
if (pipe(event_queue->pipe_fd) < 0)
|
||||
{
|
||||
free(event_queue);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
pthread_mutex_init(&(event_queue->mutex), nullptr);
|
||||
}
|
||||
|
||||
return event_queue;
|
||||
}
|
||||
|
||||
void mf_event_queue_free(mfEventQueue* event_queue)
|
||||
{
|
||||
if (event_queue->pipe_fd[0] != -1)
|
||||
{
|
||||
close(event_queue->pipe_fd[0]);
|
||||
event_queue->pipe_fd[0] = -1;
|
||||
}
|
||||
|
||||
if (event_queue->pipe_fd[1] != -1)
|
||||
{
|
||||
close(event_queue->pipe_fd[1]);
|
||||
event_queue->pipe_fd[1] = -1;
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&(event_queue->mutex));
|
||||
}
|
||||
81
third_party/FreeRDP/server/Mac/mf_event.h
vendored
Normal file
81
third_party/FreeRDP/server/Mac/mf_event.h
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* OS X Server Event Handling
|
||||
*
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_MAC_EVENT_H
|
||||
#define FREERDP_SERVER_MAC_EVENT_H
|
||||
|
||||
typedef struct mf_event mfEvent;
|
||||
typedef struct mf_event_queue mfEventQueue;
|
||||
typedef struct mf_event_region mfEventRegion;
|
||||
|
||||
#include <pthread.h>
|
||||
#include "mfreerdp.h"
|
||||
|
||||
//#include "mf_peer.h"
|
||||
|
||||
enum mf_event_type
|
||||
{
|
||||
FREERDP_SERVER_MAC_EVENT_TYPE_REGION,
|
||||
FREERDP_SERVER_MAC_EVENT_TYPE_FRAME_TICK
|
||||
};
|
||||
|
||||
struct mf_event
|
||||
{
|
||||
int type;
|
||||
};
|
||||
|
||||
struct mf_event_queue
|
||||
{
|
||||
int size;
|
||||
int count;
|
||||
int pipe_fd[2];
|
||||
mfEvent** events;
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
struct mf_event_region
|
||||
{
|
||||
int type;
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
void mf_event_push(mfEventQueue* event_queue, mfEvent* event);
|
||||
WINPR_ATTR_NODISCARD mfEvent* mf_event_peek(mfEventQueue* event_queue);
|
||||
WINPR_ATTR_NODISCARD mfEvent* mf_event_pop(mfEventQueue* event_queue);
|
||||
|
||||
void mf_event_region_free(mfEventRegion* event_region);
|
||||
|
||||
WINPR_ATTR_MALLOC(mf_event_region_free, 1)
|
||||
WINPR_ATTR_NODISCARD mfEventRegion* mf_event_region_new(int x, int y, int width, int height);
|
||||
|
||||
void mf_event_free(mfEvent* event);
|
||||
|
||||
WINPR_ATTR_MALLOC(mf_event_free, 1)
|
||||
WINPR_ATTR_NODISCARD mfEvent* mf_event_new(int type);
|
||||
|
||||
void mf_event_queue_free(mfEventQueue* event_queue);
|
||||
|
||||
WINPR_ATTR_MALLOC(mf_event_queue_free, 1)
|
||||
WINPR_ATTR_NODISCARD mfEventQueue* mf_event_queue_new(void);
|
||||
|
||||
#endif /* FREERDP_SERVER_MAC_EVENT_H */
|
||||
230
third_party/FreeRDP/server/Mac/mf_info.c
vendored
Normal file
230
third_party/FreeRDP/server/Mac/mf_info.c
vendored
Normal file
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Client
|
||||
* FreeRDP Mac OS X Server
|
||||
*
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "mf_info.h"
|
||||
#include "mf_mountain_lion.h"
|
||||
|
||||
#define MF_INFO_DEFAULT_FPS 30
|
||||
#define MF_INFO_MAXPEERS 32
|
||||
|
||||
static mfInfo* mfInfoInstance = nullptr;
|
||||
|
||||
int mf_info_lock(mfInfo* mfi)
|
||||
{
|
||||
int status = pthread_mutex_lock(&mfi->mutex);
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case 0:
|
||||
return TRUE;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mf_info_try_lock(mfInfo* mfi, UINT32 ms)
|
||||
{
|
||||
int status = pthread_mutex_trylock(&mfi->mutex);
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case 0:
|
||||
return TRUE;
|
||||
break;
|
||||
|
||||
case EBUSY:
|
||||
return FALSE;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mf_info_unlock(mfInfo* mfi)
|
||||
{
|
||||
int status = pthread_mutex_unlock(&mfi->mutex);
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case 0:
|
||||
return TRUE;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static mfInfo* mf_info_init(void)
|
||||
{
|
||||
mfInfo* mfi = (mfInfo*)calloc(1, sizeof(mfInfo));
|
||||
|
||||
if (mfi != nullptr)
|
||||
{
|
||||
pthread_mutex_init(&mfi->mutex, nullptr);
|
||||
|
||||
mfi->peers = (freerdp_peer**)calloc(MF_INFO_MAXPEERS, sizeof(freerdp_peer*));
|
||||
if (!mfi->peers)
|
||||
{
|
||||
free(mfi);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mfi->framesPerSecond = MF_INFO_DEFAULT_FPS;
|
||||
mfi->input_disabled = FALSE;
|
||||
}
|
||||
|
||||
return mfi;
|
||||
}
|
||||
|
||||
mfInfo* mf_info_get_instance(void)
|
||||
{
|
||||
if (mfInfoInstance == nullptr)
|
||||
mfInfoInstance = mf_info_init();
|
||||
|
||||
return mfInfoInstance;
|
||||
}
|
||||
|
||||
void mf_info_peer_register(mfInfo* mfi, mfPeerContext* context)
|
||||
{
|
||||
if (mf_info_lock(mfi) > 0)
|
||||
{
|
||||
int peerId;
|
||||
|
||||
if (mfi->peerCount == MF_INFO_MAXPEERS)
|
||||
{
|
||||
mf_info_unlock(mfi);
|
||||
return;
|
||||
}
|
||||
|
||||
context->info = mfi;
|
||||
|
||||
if (mfi->peerCount == 0)
|
||||
{
|
||||
mf_mlion_display_info(&mfi->servscreen_width, &mfi->servscreen_height, &mfi->scale);
|
||||
mf_mlion_screen_updates_init();
|
||||
mf_mlion_start_getting_screen_updates();
|
||||
}
|
||||
|
||||
peerId = 0;
|
||||
|
||||
for (int i = 0; i < MF_INFO_MAXPEERS; ++i)
|
||||
{
|
||||
// empty index will be our peer id
|
||||
if (mfi->peers[i] == nullptr)
|
||||
{
|
||||
peerId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mfi->peers[peerId] = ((rdpContext*)context)->peer;
|
||||
mfi->peers[peerId]->pId = peerId;
|
||||
mfi->peerCount++;
|
||||
|
||||
mf_info_unlock(mfi);
|
||||
}
|
||||
}
|
||||
|
||||
void mf_info_peer_unregister(mfInfo* mfi, mfPeerContext* context)
|
||||
{
|
||||
if (mf_info_lock(mfi) > 0)
|
||||
{
|
||||
int peerId;
|
||||
|
||||
peerId = ((rdpContext*)context)->peer->pId;
|
||||
mfi->peers[peerId] = nullptr;
|
||||
mfi->peerCount--;
|
||||
|
||||
if (mfi->peerCount == 0)
|
||||
mf_mlion_stop_getting_screen_updates();
|
||||
|
||||
mf_info_unlock(mfi);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL mf_info_have_updates(mfInfo* mfi)
|
||||
{
|
||||
if (mfi->framesWaiting == 0)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void mf_info_update_changes(mfInfo* mfi)
|
||||
{
|
||||
}
|
||||
|
||||
void mf_info_find_invalid_region(mfInfo* mfi)
|
||||
{
|
||||
mf_mlion_get_dirty_region(&mfi->invalid);
|
||||
}
|
||||
|
||||
void mf_info_clear_invalid_region(mfInfo* mfi)
|
||||
{
|
||||
mf_mlion_clear_dirty_region();
|
||||
mfi->invalid.height = 0;
|
||||
mfi->invalid.width = 0;
|
||||
}
|
||||
|
||||
void mf_info_invalidate_full_screen(mfInfo* mfi)
|
||||
{
|
||||
mfi->invalid.x = 0;
|
||||
mfi->invalid.y = 0;
|
||||
mfi->invalid.height = mfi->servscreen_height;
|
||||
mfi->invalid.width = mfi->servscreen_width;
|
||||
}
|
||||
|
||||
BOOL mf_info_have_invalid_region(mfInfo* mfi)
|
||||
{
|
||||
if (mfi->invalid.width * mfi->invalid.height == 0)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void mf_info_getScreenData(mfInfo* mfi, long* width, long* height, BYTE** pBits, int* pitch)
|
||||
{
|
||||
*width = mfi->invalid.width / mfi->scale;
|
||||
*height = mfi->invalid.height / mfi->scale;
|
||||
*pitch = mfi->servscreen_width * mfi->scale * 4;
|
||||
|
||||
mf_mlion_get_pixelData(mfi->invalid.x / mfi->scale, mfi->invalid.y / mfi->scale, *width,
|
||||
*height, pBits);
|
||||
|
||||
*pBits = *pBits + (mfi->invalid.x * 4) + (*pitch * mfi->invalid.y);
|
||||
}
|
||||
49
third_party/FreeRDP/server/Mac/mf_info.h
vendored
Normal file
49
third_party/FreeRDP/server/Mac/mf_info.h
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Mac OS X Server
|
||||
*
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_MAC_INFO_H
|
||||
#define FREERDP_SERVER_MAC_INFO_H
|
||||
|
||||
#define FREERDP_SERVER_MAC_INFO_DEFAULT_FPS 1
|
||||
#define FREERDP_SERVER_MAC_INFO_MAXPEERS 1
|
||||
|
||||
#include <winpr/wtypes.h>
|
||||
#include <freerdp/codec/rfx.h>
|
||||
|
||||
#include "mf_interface.h"
|
||||
|
||||
WINPR_ATTR_NODISCARD int mf_info_lock(mfInfo* mfi);
|
||||
WINPR_ATTR_NODISCARD int mf_info_try_lock(mfInfo* mfi, UINT32 ms);
|
||||
WINPR_ATTR_NODISCARD int mf_info_unlock(mfInfo* mfi);
|
||||
|
||||
WINPR_ATTR_NODISCARD mfInfo* mf_info_get_instance(void);
|
||||
void mf_info_peer_register(mfInfo* mfi, mfPeerContext* context);
|
||||
void mf_info_peer_unregister(mfInfo* mfi, mfPeerContext* context);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL mf_info_have_updates(mfInfo* mfi);
|
||||
void mf_info_update_changes(mfInfo* mfi);
|
||||
void mf_info_find_invalid_region(mfInfo* mfi);
|
||||
void mf_info_clear_invalid_region(mfInfo* mfi);
|
||||
void mf_info_invalidate_full_screen(mfInfo* mfi);
|
||||
WINPR_ATTR_NODISCARD BOOL mf_info_have_invalid_region(mfInfo* mfi);
|
||||
void mf_info_getScreenData(mfInfo* mfi, long* width, long* height, BYTE** pBits, int* pitch);
|
||||
// BOOL CALLBACK mf_info_monEnumCB(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM
|
||||
// dwData);
|
||||
|
||||
#endif /* FREERDP_SERVER_MAC_INFO_H */
|
||||
511
third_party/FreeRDP/server/Mac/mf_input.c
vendored
Normal file
511
third_party/FreeRDP/server/Mac/mf_input.c
vendored
Normal file
@@ -0,0 +1,511 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Mac OS X Server (Input)
|
||||
*
|
||||
* Copyright 2013 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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 <ApplicationServices/ApplicationServices.h>
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
#include <winpr/windows.h>
|
||||
|
||||
#include "mf_input.h"
|
||||
#include "mf_info.h"
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("mac")
|
||||
|
||||
static const CGKeyCode keymap[256] = {
|
||||
0xFF, // 0x0
|
||||
kVK_Escape, // 0x1
|
||||
kVK_ANSI_1, // 0x2
|
||||
kVK_ANSI_2, // 0x3
|
||||
kVK_ANSI_3, // 0x4
|
||||
kVK_ANSI_4, // 0x5
|
||||
kVK_ANSI_5, // 0x6
|
||||
kVK_ANSI_6, // 0x7
|
||||
kVK_ANSI_7, // 0x8
|
||||
kVK_ANSI_8, // 0x9
|
||||
kVK_ANSI_9, // 0xa
|
||||
kVK_ANSI_0, // 0xb
|
||||
kVK_ANSI_Minus, // 0xc
|
||||
kVK_ANSI_Equal, // 0xd
|
||||
kVK_Delete, // 0xe
|
||||
kVK_Tab, // 0xf
|
||||
kVK_ANSI_Q, // 0x10
|
||||
kVK_ANSI_W, // 0x11
|
||||
kVK_ANSI_E, // 0x12
|
||||
kVK_ANSI_R, // 0x13
|
||||
kVK_ANSI_T, // 0x14
|
||||
kVK_ANSI_Y, // 0x15
|
||||
kVK_ANSI_U, // 0x16
|
||||
kVK_ANSI_I, // 0x17
|
||||
kVK_ANSI_O, // 0x18
|
||||
kVK_ANSI_P, // 0x19
|
||||
kVK_ANSI_LeftBracket, // 0x1a
|
||||
kVK_ANSI_RightBracket, // 0x1b
|
||||
kVK_Return, // 0x1c
|
||||
kVK_Control, // 0x1d
|
||||
kVK_ANSI_A, // 0x1e
|
||||
kVK_ANSI_S, // 0x1f
|
||||
kVK_ANSI_D, // 0x20
|
||||
kVK_ANSI_F, // 0x21
|
||||
kVK_ANSI_G, // 0x22
|
||||
kVK_ANSI_H, // 0x23
|
||||
kVK_ANSI_J, // 0x24
|
||||
kVK_ANSI_K, // 0x25
|
||||
kVK_ANSI_L, // 0x26
|
||||
kVK_ANSI_Semicolon, // 0x27
|
||||
kVK_ANSI_Quote, // 0x28
|
||||
kVK_ANSI_Grave, // 0x29
|
||||
kVK_Shift, // 0x2a
|
||||
kVK_ANSI_Backslash, // 0x2b
|
||||
kVK_ANSI_Z, // 0x2c
|
||||
kVK_ANSI_X, // 0x2d
|
||||
kVK_ANSI_C, // 0x2e
|
||||
kVK_ANSI_V, // 0x2f
|
||||
kVK_ANSI_B, // 0x30
|
||||
kVK_ANSI_N, // 0x31
|
||||
kVK_ANSI_M, // 0x32
|
||||
kVK_ANSI_Comma, // 0x33
|
||||
kVK_ANSI_Period, // 0x34
|
||||
kVK_ANSI_Slash, // 0x35
|
||||
kVK_Shift, // 0x36
|
||||
kVK_ANSI_KeypadMultiply, // 0x37
|
||||
kVK_Option, // 0x38
|
||||
kVK_Space, // 0x39
|
||||
kVK_CapsLock, // 0x3a
|
||||
kVK_F1, // 0x3b
|
||||
kVK_F2, // 0x3c
|
||||
kVK_F3, // 0x3d
|
||||
kVK_F4, // 0x3e
|
||||
kVK_F5, // 0x3f
|
||||
kVK_F6, // 0x40
|
||||
kVK_F7, // 0x41
|
||||
kVK_F8, // 0x42
|
||||
kVK_F9, // 0x43
|
||||
kVK_F10, // 0x44
|
||||
0xFF, // 0x45 -- numlock
|
||||
0xFF, // 0x46 -- scroll lock
|
||||
kVK_ANSI_Keypad7, // 0x47
|
||||
kVK_ANSI_Keypad8, // 0x48
|
||||
kVK_ANSI_Keypad9, // 0x49
|
||||
kVK_ANSI_KeypadMinus, // 0x4a
|
||||
kVK_ANSI_Keypad4, // 0x4b
|
||||
kVK_ANSI_Keypad5, // 0x4c
|
||||
kVK_ANSI_Keypad6, // 0x4d
|
||||
kVK_ANSI_KeypadPlus, // 0x4e
|
||||
kVK_ANSI_Keypad1, // 0x4f
|
||||
kVK_ANSI_Keypad2, // 0x50
|
||||
kVK_ANSI_Keypad3, // 0x51
|
||||
kVK_ANSI_Keypad0, // 0x52
|
||||
kVK_ANSI_KeypadDecimal, // 0x53
|
||||
0xFF, // 0x54
|
||||
0xFF, // 0x55
|
||||
0xFF, // 0x56
|
||||
kVK_F11, // 0x57
|
||||
kVK_F12, // 0x58
|
||||
0xFF, // 0x59 -- pause
|
||||
0xFF, // 0x5a
|
||||
kVK_Control, // 0x5b
|
||||
kVK_Control, // 0x5c
|
||||
0xFF, // 0x5d -- application
|
||||
0xFF, // 0x5e -- power
|
||||
0xFF, // 0x5f -- sleep
|
||||
0xFF, // 0x60
|
||||
0xFF, // 0x61
|
||||
0xFF, // 0x62
|
||||
0xFF, // 0x63 -- wake
|
||||
0xFF, // 0x64
|
||||
0xFF, // 0x65
|
||||
0xFF, // 0x66
|
||||
0xFF, // 0x67
|
||||
0xFF, // 0x68
|
||||
0xFF, // 0x69
|
||||
0xFF, // 0x6a
|
||||
0xFF, // 0x6b
|
||||
0xFF, // 0x6c
|
||||
0xFF, // 0x6d
|
||||
0xFF, // 0x6e
|
||||
0xFF, // 0x6f
|
||||
0xFF, // 0x70
|
||||
0xFF, // 0x71
|
||||
0xFF, // 0x72
|
||||
0xFF, // 0x73
|
||||
0xFF, // 0x74
|
||||
0xFF, // 0x75
|
||||
0xFF, // 0x76
|
||||
0xFF, // 0x77
|
||||
0xFF, // 0x78
|
||||
0xFF, // 0x79
|
||||
0xFF, // 0x7a
|
||||
0xFF, // 0x7b
|
||||
0xFF, // 0x7c
|
||||
0xFF, // 0x7d
|
||||
0xFF, // 0x7e
|
||||
0xFF, // 0x7f
|
||||
0xFF, // 0x80
|
||||
0xFF, // 0x81
|
||||
0xFF, // 0x82
|
||||
0xFF, // 0x83
|
||||
0xFF, // 0x84
|
||||
0xFF, // 0x85
|
||||
0xFF, // 0x86
|
||||
0xFF, // 0x87
|
||||
0xFF, // 0x88
|
||||
0xFF, // 0x89
|
||||
0xFF, // 0x8a
|
||||
0xFF, // 0x8b
|
||||
0xFF, // 0x8c
|
||||
0xFF, // 0x8d
|
||||
0xFF, // 0x8e
|
||||
0xFF, // 0x8f
|
||||
0xFF, // 0x90
|
||||
0xFF, // 0x91
|
||||
0xFF, // 0x92
|
||||
0xFF, // 0x93
|
||||
0xFF, // 0x94
|
||||
0xFF, // 0x95
|
||||
0xFF, // 0x96
|
||||
0xFF, // 0x97
|
||||
0xFF, // 0x98
|
||||
0xFF, // 0x99
|
||||
0xFF, // 0x9a
|
||||
0xFF, // 0x9b
|
||||
0xFF, // 0x9c
|
||||
0xFF, // 0x9d
|
||||
0xFF, // 0x9e
|
||||
0xFF, // 0x9f
|
||||
0xFF, // 0xa0
|
||||
0xFF, // 0xa1
|
||||
0xFF, // 0xa2
|
||||
0xFF, // 0xa3
|
||||
0xFF, // 0xa4
|
||||
0xFF, // 0xa5
|
||||
0xFF, // 0xa6
|
||||
0xFF, // 0xa7
|
||||
0xFF, // 0xa8
|
||||
0xFF, // 0xa9
|
||||
0xFF, // 0xaa
|
||||
0xFF, // 0xab
|
||||
0xFF, // 0xac
|
||||
0xFF, // 0xad
|
||||
0xFF, // 0xae
|
||||
0xFF, // 0xaf
|
||||
0xFF, // 0xb0
|
||||
0xFF, // 0xb1
|
||||
0xFF, // 0xb2
|
||||
0xFF, // 0xb3
|
||||
0xFF, // 0xb4
|
||||
0xFF, // 0xb5
|
||||
0xFF, // 0xb6
|
||||
0xFF, // 0xb7
|
||||
0xFF, // 0xb8
|
||||
0xFF, // 0xb9
|
||||
0xFF, // 0xba
|
||||
0xFF, // 0xbb
|
||||
0xFF, // 0xbc
|
||||
0xFF, // 0xbd
|
||||
0xFF, // 0xbe
|
||||
0xFF, // 0xbf
|
||||
0xFF, // 0xc0
|
||||
0xFF, // 0xc1
|
||||
0xFF, // 0xc2
|
||||
0xFF, // 0xc3
|
||||
0xFF, // 0xc4
|
||||
0xFF, // 0xc5
|
||||
0xFF, // 0xc6
|
||||
0xFF, // 0xc7
|
||||
0xFF, // 0xc8
|
||||
0xFF, // 0xc9
|
||||
0xFF, // 0xca
|
||||
0xFF, // 0xcb
|
||||
0xFF, // 0xcc
|
||||
0xFF, // 0xcd
|
||||
0xFF, // 0xce
|
||||
0xFF, // 0xcf
|
||||
0xFF, // 0xd0
|
||||
0xFF, // 0xd1
|
||||
0xFF, // 0xd2
|
||||
0xFF, // 0xd3
|
||||
0xFF, // 0xd4
|
||||
0xFF, // 0xd5
|
||||
0xFF, // 0xd6
|
||||
0xFF, // 0xd7
|
||||
0xFF, // 0xd8
|
||||
0xFF, // 0xd9
|
||||
0xFF, // 0xda
|
||||
0xFF, // 0xdb
|
||||
0xFF, // 0xdc
|
||||
0xFF, // 0xdd
|
||||
0xFF, // 0xde
|
||||
0xFF, // 0xdf
|
||||
0xFF, // 0xe0
|
||||
0xFF, // 0xe1
|
||||
0xFF, // 0xe2
|
||||
0xFF, // 0xe3
|
||||
0xFF, // 0xe4
|
||||
0xFF, // 0xe5
|
||||
0xFF, // 0xe6
|
||||
0xFF, // 0xe7
|
||||
0xFF, // 0xe8
|
||||
0xFF, // 0xe9
|
||||
0xFF, // 0xea
|
||||
0xFF, // 0xeb
|
||||
0xFF, // 0xec
|
||||
0xFF, // 0xed
|
||||
0xFF, // 0xee
|
||||
0xFF, // 0xef
|
||||
0xFF, // 0xf0
|
||||
0xFF, // 0xf1
|
||||
0xFF, // 0xf2
|
||||
0xFF, // 0xf3
|
||||
0xFF, // 0xf4
|
||||
0xFF, // 0xf5
|
||||
0xFF, // 0xf6
|
||||
0xFF, // 0xf7
|
||||
0xFF, // 0xf8
|
||||
0xFF, // 0xf9
|
||||
0xFF, // 0xfa
|
||||
0xFF, // 0xfb
|
||||
0xFF, // 0xfc
|
||||
0xFF, // 0xfd
|
||||
0xFF, // 0xfe
|
||||
};
|
||||
|
||||
BOOL mf_input_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code)
|
||||
{
|
||||
CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
||||
BOOL keyDown = TRUE;
|
||||
CGEventRef kbEvent;
|
||||
CGKeyCode kCode = 0xFF;
|
||||
|
||||
if (flags & KBD_FLAGS_RELEASE)
|
||||
{
|
||||
keyDown = FALSE;
|
||||
}
|
||||
|
||||
if (flags & KBD_FLAGS_EXTENDED)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
// case 0x52: //insert
|
||||
case 0x53:
|
||||
kCode = kVK_ForwardDelete;
|
||||
break;
|
||||
|
||||
case 0x4B:
|
||||
kCode = kVK_LeftArrow;
|
||||
break;
|
||||
|
||||
case 0x47:
|
||||
kCode = kVK_Home;
|
||||
break;
|
||||
|
||||
case 0x4F:
|
||||
kCode = kVK_End;
|
||||
break;
|
||||
|
||||
case 0x48:
|
||||
kCode = kVK_UpArrow;
|
||||
break;
|
||||
|
||||
case 0x50:
|
||||
kCode = kVK_DownArrow;
|
||||
break;
|
||||
|
||||
case 0x49:
|
||||
kCode = kVK_PageUp;
|
||||
break;
|
||||
|
||||
case 0x51:
|
||||
kCode = kVK_PageDown;
|
||||
break;
|
||||
|
||||
case 0x4D:
|
||||
kCode = kVK_RightArrow;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
kCode = keymap[code];
|
||||
}
|
||||
|
||||
kbEvent = CGEventCreateKeyboardEvent(source, kCode, keyDown);
|
||||
CGEventPost(kCGHIDEventTap, kbEvent);
|
||||
CFRelease(kbEvent);
|
||||
CFRelease(source);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL mf_input_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL mf_input_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
|
||||
{
|
||||
float width, height;
|
||||
CGWheelCount wheelCount = 2;
|
||||
INT32 scroll_x = 0;
|
||||
INT32 scroll_y = 0;
|
||||
|
||||
if (flags & (PTR_FLAGS_WHEEL | PTR_FLAGS_HWHEEL))
|
||||
{
|
||||
INT32 scroll = flags & WheelRotationMask;
|
||||
|
||||
if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
|
||||
scroll = -(flags & WheelRotationMask) / 392;
|
||||
else
|
||||
scroll = (flags & WheelRotationMask) / 120;
|
||||
|
||||
if (flags & PTR_FLAGS_WHEEL)
|
||||
scroll_y = scroll;
|
||||
else
|
||||
scroll_x = scroll;
|
||||
|
||||
CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
||||
CGEventRef scrollEvent = CGEventCreateScrollWheelEvent(source, kCGScrollEventUnitLine,
|
||||
wheelCount, scroll_y, scroll_x);
|
||||
CGEventPost(kCGHIDEventTap, scrollEvent);
|
||||
CFRelease(scrollEvent);
|
||||
CFRelease(source);
|
||||
}
|
||||
else
|
||||
{
|
||||
mfInfo* mfi;
|
||||
CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
||||
CGEventType mouseType = kCGEventNull;
|
||||
CGMouseButton mouseButton = kCGMouseButtonLeft;
|
||||
mfi = mf_info_get_instance();
|
||||
// width and height of primary screen (even in multimon setups
|
||||
width = (float)mfi->servscreen_width;
|
||||
height = (float)mfi->servscreen_height;
|
||||
x += mfi->servscreen_xoffset;
|
||||
y += mfi->servscreen_yoffset;
|
||||
|
||||
if (flags & PTR_FLAGS_MOVE)
|
||||
{
|
||||
if (mfi->mouse_down_left == TRUE)
|
||||
{
|
||||
mouseType = kCGEventLeftMouseDragged;
|
||||
}
|
||||
else if (mfi->mouse_down_right == TRUE)
|
||||
{
|
||||
mouseType = kCGEventRightMouseDragged;
|
||||
}
|
||||
else if (mfi->mouse_down_other == TRUE)
|
||||
{
|
||||
mouseType = kCGEventOtherMouseDragged;
|
||||
}
|
||||
else
|
||||
{
|
||||
mouseType = kCGEventMouseMoved;
|
||||
}
|
||||
|
||||
CGEventRef move = CGEventCreateMouseEvent(source, mouseType, CGPointMake(x, y),
|
||||
mouseButton // ignored for just movement
|
||||
);
|
||||
CGEventPost(kCGHIDEventTap, move);
|
||||
CFRelease(move);
|
||||
}
|
||||
|
||||
if (flags & PTR_FLAGS_BUTTON1)
|
||||
{
|
||||
mouseButton = kCGMouseButtonLeft;
|
||||
|
||||
if (flags & PTR_FLAGS_DOWN)
|
||||
{
|
||||
mouseType = kCGEventLeftMouseDown;
|
||||
mfi->mouse_down_left = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
mouseType = kCGEventLeftMouseUp;
|
||||
mfi->mouse_down_right = FALSE;
|
||||
}
|
||||
}
|
||||
else if (flags & PTR_FLAGS_BUTTON2)
|
||||
{
|
||||
mouseButton = kCGMouseButtonRight;
|
||||
|
||||
if (flags & PTR_FLAGS_DOWN)
|
||||
{
|
||||
mouseType = kCGEventRightMouseDown;
|
||||
mfi->mouse_down_right = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
mouseType = kCGEventRightMouseUp;
|
||||
mfi->mouse_down_right = FALSE;
|
||||
}
|
||||
}
|
||||
else if (flags & PTR_FLAGS_BUTTON3)
|
||||
{
|
||||
mouseButton = kCGMouseButtonCenter;
|
||||
|
||||
if (flags & PTR_FLAGS_DOWN)
|
||||
{
|
||||
mouseType = kCGEventOtherMouseDown;
|
||||
mfi->mouse_down_other = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
mouseType = kCGEventOtherMouseUp;
|
||||
mfi->mouse_down_other = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
CGEventRef mouseEvent =
|
||||
CGEventCreateMouseEvent(source, mouseType, CGPointMake(x, y), mouseButton);
|
||||
CGEventPost(kCGHIDEventTap, mouseEvent);
|
||||
CFRelease(mouseEvent);
|
||||
CFRelease(source);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL mf_input_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL mf_input_keyboard_event_dummy(rdpInput* input, UINT16 flags, UINT16 code)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL mf_input_unicode_keyboard_event_dummy(rdpInput* input, UINT16 flags, UINT16 code)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL mf_input_mouse_event_dummy(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL mf_input_extended_mouse_event_dummy(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
41
third_party/FreeRDP/server/Mac/mf_input.h
vendored
Normal file
41
third_party/FreeRDP/server/Mac/mf_input.h
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Mac OS X Server (Input)
|
||||
*
|
||||
* Copyright 2013 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_MAC_INPUT_H
|
||||
#define FREERDP_SERVER_MAC_INPUT_H
|
||||
|
||||
#include "mf_interface.h"
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL mf_input_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code);
|
||||
WINPR_ATTR_NODISCARD BOOL mf_input_unicode_keyboard_event(rdpInput* input, UINT16 flags,
|
||||
UINT16 code);
|
||||
WINPR_ATTR_NODISCARD BOOL mf_input_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y);
|
||||
WINPR_ATTR_NODISCARD BOOL mf_input_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x,
|
||||
UINT16 y);
|
||||
|
||||
// dummy versions
|
||||
WINPR_ATTR_NODISCARD BOOL mf_input_keyboard_event_dummy(rdpInput* input, UINT16 flags, UINT16 code);
|
||||
WINPR_ATTR_NODISCARD BOOL mf_input_unicode_keyboard_event_dummy(rdpInput* input, UINT16 flags,
|
||||
UINT16 code);
|
||||
WINPR_ATTR_NODISCARD BOOL mf_input_mouse_event_dummy(rdpInput* input, UINT16 flags, UINT16 x,
|
||||
UINT16 y);
|
||||
WINPR_ATTR_NODISCARD BOOL mf_input_extended_mouse_event_dummy(rdpInput* input, UINT16 flags,
|
||||
UINT16 x, UINT16 y);
|
||||
|
||||
#endif /* FREERDP_SERVER_MAC_INPUT_H */
|
||||
0
third_party/FreeRDP/server/Mac/mf_interface.c
vendored
Normal file
0
third_party/FreeRDP/server/Mac/mf_interface.c
vendored
Normal file
106
third_party/FreeRDP/server/Mac/mf_interface.h
vendored
Normal file
106
third_party/FreeRDP/server/Mac/mf_interface.h
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Client
|
||||
* FreeRDP Mac OS X Server
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_MAC_INTERFACE_H
|
||||
#define FREERDP_SERVER_MAC_INTERFACE_H
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <freerdp/config.h>
|
||||
#include <freerdp/codec/rfx.h>
|
||||
#include <freerdp/codec/nsc.h>
|
||||
#include <freerdp/listener.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
|
||||
#ifdef WITH_SERVER_CHANNELS
|
||||
#include <freerdp/channels/wtsvc.h>
|
||||
#endif
|
||||
|
||||
#ifdef CHANNEL_RDPSND_SERVER
|
||||
#include <freerdp/server/rdpsnd.h>
|
||||
#include "mf_rdpsnd.h"
|
||||
#endif
|
||||
|
||||
#ifdef CHANNEL_AUDIN_SERVER
|
||||
#include <freerdp/server/audin.h>
|
||||
#include "mf_audin.h"
|
||||
#endif
|
||||
|
||||
#include "mf_types.h"
|
||||
|
||||
struct mf_peer_context
|
||||
{
|
||||
rdpContext _p;
|
||||
|
||||
mfInfo* info;
|
||||
wStream* s;
|
||||
BOOL activated;
|
||||
UINT32 frame_id;
|
||||
BOOL audin_open;
|
||||
RFX_CONTEXT* rfx_context;
|
||||
NSC_CONTEXT* nsc_context;
|
||||
|
||||
#ifdef WITH_SERVER_CHANNELS
|
||||
HANDLE vcm;
|
||||
#endif
|
||||
|
||||
#ifdef CHANNEL_AUDIN_SERVER
|
||||
audin_server_context* audin;
|
||||
#endif
|
||||
|
||||
#ifdef CHANNEL_RDPSND_SERVER
|
||||
RdpsndServerContext* rdpsnd;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct mf_info
|
||||
{
|
||||
// STREAM* s;
|
||||
|
||||
// screen and monitor info
|
||||
UINT32 screenID;
|
||||
UINT32 virtscreen_width;
|
||||
UINT32 virtscreen_height;
|
||||
UINT32 servscreen_width;
|
||||
UINT32 servscreen_height;
|
||||
UINT32 servscreen_xoffset;
|
||||
UINT32 servscreen_yoffset;
|
||||
|
||||
int bitsPerPixel;
|
||||
int peerCount;
|
||||
int activePeerCount;
|
||||
int framesPerSecond;
|
||||
freerdp_peer** peers;
|
||||
unsigned int framesWaiting;
|
||||
UINT32 scale;
|
||||
|
||||
RFX_RECT invalid;
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
BOOL mouse_down_left;
|
||||
BOOL mouse_down_right;
|
||||
BOOL mouse_down_other;
|
||||
BOOL input_disabled;
|
||||
BOOL force_all_disconnect;
|
||||
};
|
||||
|
||||
#endif /* FREERDP_SERVER_MAC_INTERFACE_H */
|
||||
269
third_party/FreeRDP/server/Mac/mf_mountain_lion.c
vendored
Normal file
269
third_party/FreeRDP/server/Mac/mf_mountain_lion.c
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* OS X Server Event Handling
|
||||
*
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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 <dispatch/dispatch.h>
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
#include <CoreVideo/CoreVideo.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOSurface/IOSurface.h>
|
||||
|
||||
#include "mf_mountain_lion.h"
|
||||
|
||||
dispatch_semaphore_t region_sem;
|
||||
dispatch_semaphore_t data_sem;
|
||||
dispatch_queue_t screen_update_q;
|
||||
CGDisplayStreamRef stream;
|
||||
|
||||
CGDisplayStreamUpdateRef lastUpdate = nullptr;
|
||||
|
||||
BYTE* localBuf = nullptr;
|
||||
|
||||
BOOL ready = FALSE;
|
||||
|
||||
void (^streamHandler)(CGDisplayStreamFrameStatus, uint64_t, IOSurfaceRef,
|
||||
CGDisplayStreamUpdateRef) = ^(CGDisplayStreamFrameStatus status,
|
||||
uint64_t displayTime, IOSurfaceRef frameSurface,
|
||||
CGDisplayStreamUpdateRef updateRef) {
|
||||
dispatch_semaphore_wait(region_sem, DISPATCH_TIME_FOREVER);
|
||||
|
||||
// may need to move this down
|
||||
if (ready == TRUE)
|
||||
{
|
||||
|
||||
RFX_RECT rect;
|
||||
unsigned long offset_beg;
|
||||
unsigned long stride;
|
||||
|
||||
rect.x = 0;
|
||||
rect.y = 0;
|
||||
rect.width = 0;
|
||||
rect.height = 0;
|
||||
mf_mlion_peek_dirty_region(&rect);
|
||||
|
||||
// lock surface
|
||||
IOSurfaceLock(frameSurface, kIOSurfaceLockReadOnly, nullptr);
|
||||
// get pointer
|
||||
void* baseAddress = IOSurfaceGetBaseAddress(frameSurface);
|
||||
// copy region
|
||||
|
||||
stride = IOSurfaceGetBytesPerRow(frameSurface);
|
||||
// memcpy(localBuf, baseAddress + offset_beg, surflen);
|
||||
for (int i = 0; i < rect.height; i++)
|
||||
{
|
||||
offset_beg = (stride * (rect.y + i) + (rect.x * 4));
|
||||
memcpy(localBuf + offset_beg, baseAddress + offset_beg, rect.width * 4);
|
||||
}
|
||||
|
||||
// unlock surface
|
||||
IOSurfaceUnlock(frameSurface, kIOSurfaceLockReadOnly, nullptr);
|
||||
|
||||
ready = FALSE;
|
||||
dispatch_semaphore_signal(data_sem);
|
||||
}
|
||||
|
||||
if (status != kCGDisplayStreamFrameStatusFrameComplete)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case kCGDisplayStreamFrameStatusFrameIdle:
|
||||
break;
|
||||
|
||||
case kCGDisplayStreamFrameStatusStopped:
|
||||
break;
|
||||
|
||||
case kCGDisplayStreamFrameStatusFrameBlank:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (lastUpdate == nullptr)
|
||||
{
|
||||
CFRetain(updateRef);
|
||||
lastUpdate = updateRef;
|
||||
}
|
||||
else
|
||||
{
|
||||
CGDisplayStreamUpdateRef tmpRef;
|
||||
tmpRef = lastUpdate;
|
||||
lastUpdate = CGDisplayStreamUpdateCreateMergedUpdate(tmpRef, updateRef);
|
||||
CFRelease(tmpRef);
|
||||
}
|
||||
|
||||
dispatch_semaphore_signal(region_sem);
|
||||
};
|
||||
|
||||
int mf_mlion_display_info(UINT32* disp_width, UINT32* disp_height, UINT32* scale)
|
||||
{
|
||||
CGDirectDisplayID display_id;
|
||||
|
||||
display_id = CGMainDisplayID();
|
||||
|
||||
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(display_id);
|
||||
|
||||
size_t pixelWidth = CGDisplayModeGetPixelWidth(mode);
|
||||
// size_t pixelHeight = CGDisplayModeGetPixelHeight(mode);
|
||||
|
||||
size_t wide = CGDisplayPixelsWide(display_id);
|
||||
size_t high = CGDisplayPixelsHigh(display_id);
|
||||
|
||||
CGDisplayModeRelease(mode);
|
||||
|
||||
*disp_width = wide; // pixelWidth;
|
||||
*disp_height = high; // pixelHeight;
|
||||
*scale = pixelWidth / wide;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mf_mlion_screen_updates_init()
|
||||
{
|
||||
CGDirectDisplayID display_id;
|
||||
|
||||
display_id = CGMainDisplayID();
|
||||
|
||||
screen_update_q = dispatch_queue_create("mfreerdp.server.screenUpdate", nullptr);
|
||||
|
||||
region_sem = dispatch_semaphore_create(1);
|
||||
data_sem = dispatch_semaphore_create(1);
|
||||
|
||||
UINT32 pixelWidth;
|
||||
UINT32 pixelHeight;
|
||||
UINT32 scale;
|
||||
|
||||
mf_mlion_display_info(&pixelWidth, &pixelHeight, &scale);
|
||||
|
||||
localBuf = malloc(pixelWidth * pixelHeight * 4);
|
||||
if (!localBuf)
|
||||
return -1;
|
||||
|
||||
CFDictionaryRef opts;
|
||||
|
||||
void* keys[2];
|
||||
void* values[2];
|
||||
|
||||
keys[0] = (void*)kCGDisplayStreamShowCursor;
|
||||
values[0] = (void*)kCFBooleanFalse;
|
||||
|
||||
opts = CFDictionaryCreate(kCFAllocatorDefault, (const void**)keys, (const void**)values, 1,
|
||||
nullptr, nullptr);
|
||||
|
||||
stream = CGDisplayStreamCreateWithDispatchQueue(display_id, pixelWidth, pixelHeight, 'BGRA',
|
||||
opts, screen_update_q, streamHandler);
|
||||
|
||||
CFRelease(opts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mf_mlion_start_getting_screen_updates()
|
||||
{
|
||||
CGError err;
|
||||
|
||||
err = CGDisplayStreamStart(stream);
|
||||
|
||||
if (err != kCGErrorSuccess)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
int mf_mlion_stop_getting_screen_updates()
|
||||
{
|
||||
CGError err;
|
||||
|
||||
err = CGDisplayStreamStop(stream);
|
||||
|
||||
if (err != kCGErrorSuccess)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mf_mlion_get_dirty_region(RFX_RECT* invalid)
|
||||
{
|
||||
dispatch_semaphore_wait(region_sem, DISPATCH_TIME_FOREVER);
|
||||
|
||||
if (lastUpdate != nullptr)
|
||||
{
|
||||
mf_mlion_peek_dirty_region(invalid);
|
||||
}
|
||||
|
||||
dispatch_semaphore_signal(region_sem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mf_mlion_peek_dirty_region(RFX_RECT* invalid)
|
||||
{
|
||||
size_t num_rects;
|
||||
CGRect dirtyRegion;
|
||||
|
||||
const CGRect* rects =
|
||||
CGDisplayStreamUpdateGetRects(lastUpdate, kCGDisplayStreamUpdateDirtyRects, &num_rects);
|
||||
|
||||
if (num_rects == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
dirtyRegion = *rects;
|
||||
for (size_t i = 0; i < num_rects; i++)
|
||||
{
|
||||
dirtyRegion = CGRectUnion(dirtyRegion, *(rects + i));
|
||||
}
|
||||
|
||||
invalid->x = dirtyRegion.origin.x;
|
||||
invalid->y = dirtyRegion.origin.y;
|
||||
invalid->height = dirtyRegion.size.height;
|
||||
invalid->width = dirtyRegion.size.width;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mf_mlion_clear_dirty_region()
|
||||
{
|
||||
dispatch_semaphore_wait(region_sem, DISPATCH_TIME_FOREVER);
|
||||
|
||||
CFRelease(lastUpdate);
|
||||
lastUpdate = nullptr;
|
||||
|
||||
dispatch_semaphore_signal(region_sem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mf_mlion_get_pixelData(long x, long y, long width, long height, BYTE** pxData)
|
||||
{
|
||||
dispatch_semaphore_wait(region_sem, DISPATCH_TIME_FOREVER);
|
||||
ready = TRUE;
|
||||
dispatch_semaphore_wait(data_sem, DISPATCH_TIME_FOREVER);
|
||||
dispatch_semaphore_signal(region_sem);
|
||||
|
||||
// this second wait allows us to block until data is copied... more on this later
|
||||
dispatch_semaphore_wait(data_sem, DISPATCH_TIME_FOREVER);
|
||||
*pxData = localBuf;
|
||||
dispatch_semaphore_signal(data_sem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
40
third_party/FreeRDP/server/Mac/mf_mountain_lion.h
vendored
Normal file
40
third_party/FreeRDP/server/Mac/mf_mountain_lion.h
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* OS X Server Event Handling
|
||||
*
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_MAC_MLION_H
|
||||
#define FREERDP_SERVER_MAC_MLION_H
|
||||
|
||||
#include <freerdp/codec/rfx.h>
|
||||
|
||||
WINPR_ATTR_NODISCARD int mf_mlion_display_info(UINT32* disp_width, UINT32* dispHeight,
|
||||
UINT32* scale);
|
||||
|
||||
WINPR_ATTR_NODISCARD int mf_mlion_screen_updates_init(void);
|
||||
|
||||
WINPR_ATTR_NODISCARD int mf_mlion_start_getting_screen_updates(void);
|
||||
WINPR_ATTR_NODISCARD int mf_mlion_stop_getting_screen_updates(void);
|
||||
|
||||
WINPR_ATTR_NODISCARD int mf_mlion_get_dirty_region(RFX_RECT* invalid);
|
||||
WINPR_ATTR_NODISCARD int mf_mlion_peek_dirty_region(RFX_RECT* invalid);
|
||||
WINPR_ATTR_NODISCARD int mf_mlion_clear_dirty_region(void);
|
||||
|
||||
WINPR_ATTR_NODISCARD int mf_mlion_get_pixelData(long x, long y, long width, long height,
|
||||
BYTE** pxData);
|
||||
|
||||
#endif /* FREERDP_SERVER_MAC_MLION_H */
|
||||
496
third_party/FreeRDP/server/Mac/mf_peer.c
vendored
Normal file
496
third_party/FreeRDP/server/Mac/mf_peer.c
vendored
Normal file
@@ -0,0 +1,496 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Client
|
||||
* FreeRDP Mac OS X Server
|
||||
*
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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 <freerdp/listener.h>
|
||||
#include <freerdp/codec/rfx.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <freerdp/peer.h>
|
||||
#include <freerdp/codec/color.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include "mf_peer.h"
|
||||
#include "mf_info.h"
|
||||
#include "mf_input.h"
|
||||
#include "mf_event.h"
|
||||
#include "mf_rdpsnd.h"
|
||||
#include "mf_audin.h"
|
||||
|
||||
#include <mach/clock.h>
|
||||
#include <mach/mach.h>
|
||||
#include <dispatch/dispatch.h>
|
||||
|
||||
#include "OpenGL/OpenGL.h"
|
||||
#include "OpenGL/gl.h"
|
||||
|
||||
#include "CoreVideo/CoreVideo.h"
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("mac")
|
||||
|
||||
// refactor these
|
||||
static int info_last_sec = 0;
|
||||
static int info_last_nsec = 0;
|
||||
|
||||
static dispatch_source_t info_timer;
|
||||
static dispatch_queue_t info_queue;
|
||||
|
||||
static mfEventQueue* info_event_queue;
|
||||
|
||||
static CGLContextObj glContext;
|
||||
static CGContextRef bmp;
|
||||
static CGImageRef img;
|
||||
|
||||
static void mf_peer_context_free(freerdp_peer* client, rdpContext* context);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL mf_peer_get_fds(freerdp_peer* client, void** rfds, int* rcount)
|
||||
{
|
||||
if (info_event_queue->pipe_fd[0] == -1)
|
||||
return TRUE;
|
||||
|
||||
rfds[*rcount] = (void*)(long)info_event_queue->pipe_fd[0];
|
||||
(*rcount)++;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void mf_peer_rfx_update(freerdp_peer* client)
|
||||
{
|
||||
// check
|
||||
mfInfo* mfi = mf_info_get_instance();
|
||||
mf_info_find_invalid_region(mfi);
|
||||
|
||||
if (mf_info_have_invalid_region(mfi) == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long width;
|
||||
long height;
|
||||
int pitch;
|
||||
BYTE* dataBits = nullptr;
|
||||
mf_info_getScreenData(mfi, &width, &height, &dataBits, &pitch);
|
||||
mf_info_clear_invalid_region(mfi);
|
||||
// encode
|
||||
wStream* s;
|
||||
RFX_RECT rect;
|
||||
rdpUpdate* update;
|
||||
mfPeerContext* mfp;
|
||||
SURFACE_BITS_COMMAND cmd = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(client);
|
||||
|
||||
mfp = (mfPeerContext*)client->context;
|
||||
WINPR_ASSERT(mfp);
|
||||
|
||||
update = client->context->update;
|
||||
WINPR_ASSERT(update);
|
||||
|
||||
s = mfp->s;
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
Stream_Clear(s);
|
||||
Stream_ResetPosition(s);
|
||||
UINT32 x = mfi->invalid.x / mfi->scale;
|
||||
UINT32 y = mfi->invalid.y / mfi->scale;
|
||||
rect.x = 0;
|
||||
rect.y = 0;
|
||||
rect.width = width;
|
||||
rect.height = height;
|
||||
|
||||
rfx_context_reset(mfp->rfx_context, mfi->servscreen_width, mfi->servscreen_height);
|
||||
|
||||
if (!(rfx_compose_message(mfp->rfx_context, s, &rect, 1, (BYTE*)dataBits, rect.width,
|
||||
rect.height, pitch)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cmd.destLeft = x;
|
||||
cmd.destTop = y;
|
||||
cmd.destRight = x + rect.width;
|
||||
cmd.destBottom = y + rect.height;
|
||||
cmd.bmp.bpp = 32;
|
||||
cmd.bmp.codecID = 3;
|
||||
cmd.bmp.width = rect.width;
|
||||
cmd.bmp.height = rect.height;
|
||||
cmd.bmp.bitmapDataLength = Stream_GetPosition(s);
|
||||
cmd.bmp.bitmapData = Stream_Buffer(s);
|
||||
// send
|
||||
update->SurfaceBits(update->context, &cmd);
|
||||
// clean up... maybe?
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL mf_peer_check_fds(freerdp_peer* client)
|
||||
{
|
||||
mfPeerContext* context = (mfPeerContext*)client->context;
|
||||
mfEvent* event;
|
||||
|
||||
if (context->activated == FALSE)
|
||||
return TRUE;
|
||||
|
||||
event = mf_event_peek(info_event_queue);
|
||||
|
||||
if (event != nullptr)
|
||||
{
|
||||
if (event->type == FREERDP_SERVER_MAC_EVENT_TYPE_REGION)
|
||||
{
|
||||
}
|
||||
else if (event->type == FREERDP_SERVER_MAC_EVENT_TYPE_FRAME_TICK)
|
||||
{
|
||||
event = mf_event_pop(info_event_queue);
|
||||
mf_peer_rfx_update(client);
|
||||
mf_event_free(event);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Called when we have a new peer connecting */
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL mf_peer_context_new(freerdp_peer* client, rdpContext* context)
|
||||
{
|
||||
rdpSettings* settings;
|
||||
mfPeerContext* peer = (mfPeerContext*)context;
|
||||
|
||||
WINPR_ASSERT(client);
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
settings = context->settings;
|
||||
WINPR_ASSERT(settings);
|
||||
|
||||
if (!(peer->info = mf_info_get_instance()))
|
||||
return FALSE;
|
||||
|
||||
if (!(peer->rfx_context = rfx_context_new_ex(
|
||||
TRUE, freerdp_settings_get_uint32(settings, FreeRDP_ThreadingFlags))))
|
||||
goto fail;
|
||||
|
||||
rfx_context_reset(peer->rfx_context,
|
||||
freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
|
||||
freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
|
||||
rfx_context_set_mode(peer->rfx_context, RLGR3);
|
||||
rfx_context_set_pixel_format(peer->rfx_context, PIXEL_FORMAT_BGRA32);
|
||||
|
||||
if (!(peer->s = Stream_New(nullptr, 0xFFFF)))
|
||||
goto fail;
|
||||
|
||||
peer->vcm = WTSOpenServerA((LPSTR)client->context);
|
||||
|
||||
if (!peer->vcm || (peer->vcm == INVALID_HANDLE_VALUE))
|
||||
goto fail;
|
||||
|
||||
mf_info_peer_register(peer->info, peer);
|
||||
return TRUE;
|
||||
fail:
|
||||
mf_peer_context_free(client, context);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Called after a peer disconnects */
|
||||
static void mf_peer_context_free(freerdp_peer* client, rdpContext* context)
|
||||
{
|
||||
mfPeerContext* peer = (mfPeerContext*)context;
|
||||
if (context)
|
||||
{
|
||||
mf_info_peer_unregister(peer->info, peer);
|
||||
dispatch_suspend(info_timer);
|
||||
Stream_Free(peer->s, TRUE);
|
||||
rfx_context_free(peer->rfx_context);
|
||||
// nsc_context_free(peer->nsc_context);
|
||||
#ifdef CHANNEL_AUDIN_SERVER
|
||||
|
||||
mf_peer_audin_uninit(peer);
|
||||
|
||||
#endif
|
||||
#ifdef CHANNEL_RDPSND_SERVER
|
||||
mf_peer_rdpsnd_stop();
|
||||
|
||||
if (peer->rdpsnd)
|
||||
rdpsnd_server_context_free(peer->rdpsnd);
|
||||
|
||||
#endif
|
||||
WTSCloseServer(peer->vcm);
|
||||
}
|
||||
}
|
||||
|
||||
/* Called when a new client connects */
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL mf_peer_init(freerdp_peer* client)
|
||||
{
|
||||
client->ContextSize = sizeof(mfPeerContext);
|
||||
client->ContextNew = mf_peer_context_new;
|
||||
client->ContextFree = mf_peer_context_free;
|
||||
|
||||
if (!freerdp_peer_context_new(client))
|
||||
return FALSE;
|
||||
|
||||
info_event_queue = mf_event_queue_new();
|
||||
info_queue = dispatch_queue_create("FreeRDP.update.timer", DISPATCH_QUEUE_SERIAL);
|
||||
info_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, info_queue);
|
||||
|
||||
if (info_timer)
|
||||
{
|
||||
// DEBUG_WARN( "created timer\n");
|
||||
dispatch_source_set_timer(info_timer, DISPATCH_TIME_NOW, 42ull * NSEC_PER_MSEC,
|
||||
100ull * NSEC_PER_MSEC);
|
||||
dispatch_source_set_event_handler(info_timer, ^{
|
||||
// DEBUG_WARN( "dispatch\n");
|
||||
mfEvent* event = mf_event_new(FREERDP_SERVER_MAC_EVENT_TYPE_FRAME_TICK);
|
||||
mf_event_push(info_event_queue, (mfEvent*)event);
|
||||
});
|
||||
dispatch_resume(info_timer);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL mf_peer_post_connect(freerdp_peer* client)
|
||||
{
|
||||
mfInfo* mfi = mf_info_get_instance();
|
||||
|
||||
WINPR_ASSERT(client);
|
||||
|
||||
mfPeerContext* context = (mfPeerContext*)client->context;
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
rdpSettings* settings = client->context->settings;
|
||||
WINPR_ASSERT(settings);
|
||||
|
||||
mfi->scale = 1;
|
||||
// mfi->servscreen_width = 2880 / mfi->scale;
|
||||
// mfi->servscreen_height = 1800 / mfi->scale;
|
||||
UINT32 bitsPerPixel = 32;
|
||||
|
||||
if ((freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) != mfi->servscreen_width) ||
|
||||
(freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) != mfi->servscreen_height))
|
||||
{
|
||||
}
|
||||
|
||||
if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, mfi->servscreen_width))
|
||||
return FALSE;
|
||||
if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, mfi->servscreen_height))
|
||||
return FALSE;
|
||||
if (!freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth, bitsPerPixel))
|
||||
return FALSE;
|
||||
|
||||
WINPR_ASSERT(client->context->update);
|
||||
WINPR_ASSERT(client->context->update->DesktopResize);
|
||||
client->context->update->DesktopResize(client->context);
|
||||
|
||||
mfi->mouse_down_left = FALSE;
|
||||
mfi->mouse_down_right = FALSE;
|
||||
mfi->mouse_down_other = FALSE;
|
||||
#ifdef CHANNEL_RDPSND_SERVER
|
||||
|
||||
if (WTSVirtualChannelManagerIsChannelJoined(context->vcm, "rdpsnd"))
|
||||
{
|
||||
mf_peer_rdpsnd_init(context); /* Audio Output */
|
||||
}
|
||||
|
||||
#endif
|
||||
/* Dynamic Virtual Channels */
|
||||
#ifdef CHANNEL_AUDIN_SERVER
|
||||
mf_peer_audin_init(context); /* Audio Input */
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL mf_peer_activate(freerdp_peer* client)
|
||||
{
|
||||
WINPR_ASSERT(client);
|
||||
|
||||
mfPeerContext* context = (mfPeerContext*)client->context;
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
rdpSettings* settings = client->context->settings;
|
||||
WINPR_ASSERT(settings);
|
||||
|
||||
rfx_context_reset(context->rfx_context,
|
||||
freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
|
||||
freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
|
||||
context->activated = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL mf_peer_synchronize_event(rdpInput* input, UINT32 flags)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL mf_peer_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code)
|
||||
{
|
||||
bool state_down = FALSE;
|
||||
|
||||
if (flags == KBD_FLAGS_DOWN)
|
||||
{
|
||||
state_down = TRUE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL mf_peer_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL mf_peer_suppress_output(rdpContext* context, BYTE allow, const RECTANGLE_16* area)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static void* mf_peer_main_loop(void* arg)
|
||||
{
|
||||
mfPeerContext* context;
|
||||
rdpSettings* settings;
|
||||
rdpInput* input;
|
||||
rdpUpdate* update;
|
||||
freerdp_peer* client = (freerdp_peer*)arg;
|
||||
|
||||
if (!mf_peer_init(client))
|
||||
goto fail;
|
||||
|
||||
const mf_server_info* info = client->ContextExtra;
|
||||
WINPR_ASSERT(info);
|
||||
|
||||
WINPR_ASSERT(client->context);
|
||||
|
||||
settings = client->context->settings;
|
||||
WINPR_ASSERT(settings);
|
||||
|
||||
/* Initialize the real server settings here */
|
||||
rdpPrivateKey* key = freerdp_key_new_from_file_enc(info->key, nullptr);
|
||||
if (!key)
|
||||
goto fail;
|
||||
if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerRsaKey, key, 1))
|
||||
goto fail;
|
||||
rdpCertificate* cert = freerdp_certificate_new_from_file(info->cert);
|
||||
if (!cert)
|
||||
goto fail;
|
||||
if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerCertificate, cert, 1))
|
||||
goto fail;
|
||||
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
|
||||
goto fail;
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, TRUE))
|
||||
goto fail;
|
||||
if (!freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth, 32))
|
||||
goto fail;
|
||||
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_SuppressOutput, TRUE))
|
||||
goto fail;
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_RefreshRect, FALSE))
|
||||
goto fail;
|
||||
|
||||
client->PostConnect = mf_peer_post_connect;
|
||||
client->Activate = mf_peer_activate;
|
||||
|
||||
input = client->context->input;
|
||||
WINPR_ASSERT(input);
|
||||
|
||||
input->SynchronizeEvent = mf_peer_synchronize_event;
|
||||
input->KeyboardEvent = mf_input_keyboard_event; // mf_peer_keyboard_event;
|
||||
input->UnicodeKeyboardEvent = mf_peer_unicode_keyboard_event;
|
||||
input->MouseEvent = mf_input_mouse_event;
|
||||
input->ExtendedMouseEvent = mf_input_extended_mouse_event;
|
||||
|
||||
update = client->context->update;
|
||||
WINPR_ASSERT(update);
|
||||
|
||||
// update->RefreshRect = mf_peer_refresh_rect;
|
||||
update->SuppressOutput = mf_peer_suppress_output;
|
||||
|
||||
WINPR_ASSERT(client->Initialize);
|
||||
const BOOL rc = client->Initialize(client);
|
||||
if (!rc)
|
||||
goto fail;
|
||||
context = (mfPeerContext*)client->context;
|
||||
|
||||
while (1)
|
||||
{
|
||||
DWORD status;
|
||||
HANDLE handles[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
|
||||
DWORD count = client->GetEventHandles(client, handles, ARRAYSIZE(handles));
|
||||
|
||||
if ((count == 0) || (count == MAXIMUM_WAIT_OBJECTS))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get FreeRDP file descriptor");
|
||||
break;
|
||||
}
|
||||
|
||||
handles[count++] = WTSVirtualChannelManagerGetEventHandle(context->vcm);
|
||||
|
||||
status = WaitForMultipleObjects(count, handles, FALSE, INFINITE);
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed");
|
||||
break;
|
||||
}
|
||||
|
||||
if (client->CheckFileDescriptor(client) != TRUE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if ((mf_peer_check_fds(client)) != TRUE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelManagerCheckFileDescriptor(context->vcm) != TRUE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
client->Disconnect(client);
|
||||
freerdp_peer_context_free(client);
|
||||
fail:
|
||||
freerdp_peer_free(client);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BOOL mf_peer_accepted(freerdp_listener* instance, freerdp_peer* client)
|
||||
{
|
||||
pthread_t th;
|
||||
|
||||
WINPR_ASSERT(instance);
|
||||
WINPR_ASSERT(client);
|
||||
|
||||
client->ContextExtra = instance->info;
|
||||
if (pthread_create(&th, 0, mf_peer_main_loop, client) == 0)
|
||||
{
|
||||
pthread_detach(th);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
33
third_party/FreeRDP/server/Mac/mf_peer.h
vendored
Normal file
33
third_party/FreeRDP/server/Mac/mf_peer.h
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Mac OS X Server
|
||||
*
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_MAC_PEER_H
|
||||
#define FREERDP_SERVER_MAC_PEER_H
|
||||
|
||||
#include "mf_interface.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* cert;
|
||||
const char* key;
|
||||
} mf_server_info;
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL mf_peer_accepted(freerdp_listener* instance, freerdp_peer* client);
|
||||
|
||||
#endif /* FREERDP_SERVER_MAC_PEER_H */
|
||||
200
third_party/FreeRDP/server/Mac/mf_rdpsnd.c
vendored
Normal file
200
third_party/FreeRDP/server/Mac/mf_rdpsnd.c
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Mac OS X Server (Audio Output)
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
*
|
||||
* 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 <freerdp/server/rdpsnd.h>
|
||||
|
||||
#include "mf_info.h"
|
||||
#include "mf_rdpsnd.h"
|
||||
#include "mf_interface.h"
|
||||
|
||||
#include <winpr/sysinfo.h>
|
||||
#include <freerdp/server/server-common.h>
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("mac")
|
||||
|
||||
AQRecorderState recorderState;
|
||||
|
||||
static void mf_peer_rdpsnd_activated(RdpsndServerContext* context)
|
||||
{
|
||||
OSStatus status;
|
||||
BOOL formatAgreed = FALSE;
|
||||
AUDIO_FORMAT* agreedFormat = nullptr;
|
||||
// we should actually loop through the list of client formats here
|
||||
// and see if we can send the client something that it supports...
|
||||
WLog_DBG(TAG, "Client supports the following %d formats: ", context->num_client_formats);
|
||||
|
||||
int i = 0;
|
||||
for (; i < context->num_client_formats; i++)
|
||||
{
|
||||
/* TODO: improve the way we agree on a format */
|
||||
for (int j = 0; j < context->num_server_formats; j++)
|
||||
{
|
||||
if ((context->client_formats[i].wFormatTag == context->server_formats[j].wFormatTag) &&
|
||||
(context->client_formats[i].nChannels == context->server_formats[j].nChannels) &&
|
||||
(context->client_formats[i].nSamplesPerSec ==
|
||||
context->server_formats[j].nSamplesPerSec))
|
||||
{
|
||||
WLog_DBG(TAG, "agreed on format!");
|
||||
formatAgreed = TRUE;
|
||||
agreedFormat = (AUDIO_FORMAT*)&context->server_formats[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (formatAgreed == TRUE)
|
||||
break;
|
||||
}
|
||||
|
||||
if (formatAgreed == FALSE)
|
||||
{
|
||||
WLog_DBG(TAG, "Could not agree on a audio format with the server");
|
||||
return;
|
||||
}
|
||||
|
||||
context->SelectFormat(context, i);
|
||||
context->SetVolume(context, 0x7FFF, 0x7FFF);
|
||||
|
||||
switch (agreedFormat->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_ALAW:
|
||||
recorderState.dataFormat.mFormatID = kAudioFormatDVIIntelIMA;
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_PCM:
|
||||
recorderState.dataFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
break;
|
||||
|
||||
default:
|
||||
recorderState.dataFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
break;
|
||||
}
|
||||
|
||||
recorderState.dataFormat.mSampleRate = agreedFormat->nSamplesPerSec;
|
||||
recorderState.dataFormat.mFormatFlags =
|
||||
kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
|
||||
recorderState.dataFormat.mBytesPerPacket = 4;
|
||||
recorderState.dataFormat.mFramesPerPacket = 1;
|
||||
recorderState.dataFormat.mBytesPerFrame = 4;
|
||||
recorderState.dataFormat.mChannelsPerFrame = agreedFormat->nChannels;
|
||||
recorderState.dataFormat.mBitsPerChannel = agreedFormat->wBitsPerSample;
|
||||
recorderState.snd_context = context;
|
||||
status =
|
||||
AudioQueueNewInput(&recorderState.dataFormat, mf_peer_rdpsnd_input_callback, &recorderState,
|
||||
nullptr, kCFRunLoopCommonModes, 0, &recorderState.queue);
|
||||
|
||||
if (status != noErr)
|
||||
{
|
||||
WLog_DBG(TAG, "Failed to create a new Audio Queue. Status code: %" PRId32 "", status);
|
||||
}
|
||||
|
||||
UInt32 dataFormatSize = sizeof(recorderState.dataFormat);
|
||||
AudioQueueGetProperty(recorderState.queue, kAudioConverterCurrentInputStreamDescription,
|
||||
&recorderState.dataFormat, &dataFormatSize);
|
||||
mf_rdpsnd_derive_buffer_size(recorderState.queue, &recorderState.dataFormat, 0.05,
|
||||
&recorderState.bufferByteSize);
|
||||
|
||||
for (size_t x = 0; x < SND_NUMBUFFERS; ++x)
|
||||
{
|
||||
AudioQueueAllocateBuffer(recorderState.queue, recorderState.bufferByteSize,
|
||||
&recorderState.buffers[x]);
|
||||
AudioQueueEnqueueBuffer(recorderState.queue, recorderState.buffers[x], 0, nullptr);
|
||||
}
|
||||
|
||||
recorderState.currentPacket = 0;
|
||||
recorderState.isRunning = true;
|
||||
AudioQueueStart(recorderState.queue, nullptr);
|
||||
}
|
||||
|
||||
BOOL mf_peer_rdpsnd_init(mfPeerContext* context)
|
||||
{
|
||||
context->rdpsnd = rdpsnd_server_context_new(context->vcm);
|
||||
context->rdpsnd->rdpcontext = &context->_p;
|
||||
context->rdpsnd->data = context;
|
||||
context->rdpsnd->num_server_formats =
|
||||
server_rdpsnd_get_formats(&context->rdpsnd->server_formats);
|
||||
|
||||
if (context->rdpsnd->num_server_formats > 0)
|
||||
context->rdpsnd->src_format = &context->rdpsnd->server_formats[0];
|
||||
|
||||
context->rdpsnd->Activated = mf_peer_rdpsnd_activated;
|
||||
context->rdpsnd->Initialize(context->rdpsnd, TRUE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL mf_peer_rdpsnd_stop(void)
|
||||
{
|
||||
recorderState.isRunning = false;
|
||||
AudioQueueStop(recorderState.queue, true);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void mf_peer_rdpsnd_input_callback(void* inUserData, AudioQueueRef inAQ,
|
||||
AudioQueueBufferRef inBuffer, const AudioTimeStamp* inStartTime,
|
||||
UInt32 inNumberPacketDescriptions,
|
||||
const AudioStreamPacketDescription* inPacketDescs)
|
||||
{
|
||||
OSStatus status;
|
||||
AQRecorderState* rState;
|
||||
rState = inUserData;
|
||||
|
||||
if (inNumberPacketDescriptions == 0 && rState->dataFormat.mBytesPerPacket != 0)
|
||||
{
|
||||
inNumberPacketDescriptions =
|
||||
inBuffer->mAudioDataByteSize / rState->dataFormat.mBytesPerPacket;
|
||||
}
|
||||
|
||||
if (rState->isRunning == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rState->snd_context->SendSamples(rState->snd_context, inBuffer->mAudioData,
|
||||
inBuffer->mAudioDataByteSize / 4,
|
||||
(UINT16)(GetTickCount() & 0xffff));
|
||||
status = AudioQueueEnqueueBuffer(rState->queue, inBuffer, 0, nullptr);
|
||||
|
||||
if (status != noErr)
|
||||
{
|
||||
WLog_DBG(TAG, "AudioQueueEnqueueBuffer() returned status = %" PRId32 "", status);
|
||||
}
|
||||
}
|
||||
|
||||
void mf_rdpsnd_derive_buffer_size(AudioQueueRef audioQueue,
|
||||
AudioStreamBasicDescription* ASBDescription, Float64 seconds,
|
||||
UInt32* outBufferSize)
|
||||
{
|
||||
static const int maxBufferSize = 0x50000;
|
||||
int maxPacketSize = ASBDescription->mBytesPerPacket;
|
||||
|
||||
if (maxPacketSize == 0)
|
||||
{
|
||||
UInt32 maxVBRPacketSize = sizeof(maxPacketSize);
|
||||
AudioQueueGetProperty(audioQueue, kAudioQueueProperty_MaximumOutputPacketSize,
|
||||
// in Mac OS X v10.5, instead use
|
||||
// kAudioConverterPropertyMaximumOutputPacketSize
|
||||
&maxPacketSize, &maxVBRPacketSize);
|
||||
}
|
||||
|
||||
Float64 numBytesForTime = ASBDescription->mSampleRate * maxPacketSize * seconds;
|
||||
*outBufferSize = (UInt32)(numBytesForTime < maxBufferSize ? numBytesForTime : maxBufferSize);
|
||||
}
|
||||
58
third_party/FreeRDP/server/Mac/mf_rdpsnd.h
vendored
Normal file
58
third_party/FreeRDP/server/Mac/mf_rdpsnd.h
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Mac OS X Server (Audio Output)
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_MAC_RDPSND_H
|
||||
#define FREERDP_SERVER_MAC_RDPSND_H
|
||||
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/listener.h>
|
||||
#include <freerdp/server/rdpsnd.h>
|
||||
|
||||
#include "mf_types.h"
|
||||
#include "mfreerdp.h"
|
||||
|
||||
void mf_rdpsnd_derive_buffer_size(AudioQueueRef audioQueue,
|
||||
AudioStreamBasicDescription* ASBDescription, Float64 seconds,
|
||||
UInt32* outBufferSize);
|
||||
|
||||
void mf_peer_rdpsnd_input_callback(void* inUserData, AudioQueueRef inAQ,
|
||||
AudioQueueBufferRef inBuffer, const AudioTimeStamp* inStartTime,
|
||||
UInt32 inNumberPacketDescriptions,
|
||||
const AudioStreamPacketDescription* inPacketDescs);
|
||||
|
||||
#define SND_NUMBUFFERS 3
|
||||
typedef struct
|
||||
{
|
||||
AudioStreamBasicDescription dataFormat;
|
||||
AudioQueueRef queue;
|
||||
AudioQueueBufferRef buffers[SND_NUMBUFFERS];
|
||||
AudioFileID audioFile;
|
||||
UInt32 bufferByteSize;
|
||||
SInt64 currentPacket;
|
||||
bool isRunning;
|
||||
RdpsndServerContext* snd_context;
|
||||
} AQRecorderState;
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL mf_peer_rdpsnd_init(mfPeerContext* context);
|
||||
WINPR_ATTR_NODISCARD BOOL mf_peer_rdpsnd_stop(void);
|
||||
|
||||
#endif /* FREERDP_SERVER_MAC_RDPSND_H */
|
||||
33
third_party/FreeRDP/server/Mac/mf_types.h
vendored
Normal file
33
third_party/FreeRDP/server/Mac/mf_types.h
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Client
|
||||
* FreeRDP Mac OS X Server
|
||||
*
|
||||
* Copyright 2023 Armin Novak <anovak@thincst.com>
|
||||
* Copyright 2023 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_MAC_TYPES_H
|
||||
#define FREERDP_SERVER_MAC_TYPES_H
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
|
||||
typedef struct mf_info mfInfo;
|
||||
typedef struct mf_peer_context mfPeerContext;
|
||||
|
||||
#endif /* FREERDP_SERVER_MAC_TYPES_H */
|
||||
108
third_party/FreeRDP/server/Mac/mfreerdp.c
vendored
Normal file
108
third_party/FreeRDP/server/Mac/mfreerdp.c
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Mac OS X Server
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <CoreGraphics/CGEvent.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/wtsapi.h>
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/constants.h>
|
||||
#include <freerdp/channels/wtsvc.h>
|
||||
#include <freerdp/channels/channels.h>
|
||||
#include <freerdp/server/server-common.h>
|
||||
|
||||
#include "mfreerdp.h"
|
||||
#include "mf_peer.h"
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("mac")
|
||||
|
||||
static void mf_server_main_loop(freerdp_listener* instance)
|
||||
{
|
||||
WINPR_ASSERT(instance);
|
||||
WINPR_ASSERT(instance->GetEventHandles);
|
||||
WINPR_ASSERT(instance->CheckFileDescriptor);
|
||||
|
||||
while (1)
|
||||
{
|
||||
DWORD status;
|
||||
HANDLE handles[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
|
||||
DWORD count = instance->GetEventHandles(instance, handles, ARRAYSIZE(handles));
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get FreeRDP file descriptor");
|
||||
break;
|
||||
}
|
||||
|
||||
status = WaitForMultipleObjects(count, handles, FALSE, INFINITE);
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed");
|
||||
break;
|
||||
}
|
||||
|
||||
if (instance->CheckFileDescriptor(instance) != TRUE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
instance->Close(instance);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
freerdp_server_warn_unmaintained(argc, argv);
|
||||
mf_server_info info = { .key = "server.key", .cert = "server.crt" };
|
||||
|
||||
freerdp_listener* instance;
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi());
|
||||
|
||||
if (!(instance = freerdp_listener_new()))
|
||||
return 1;
|
||||
|
||||
instance->info = &info;
|
||||
instance->PeerAccepted = mf_peer_accepted;
|
||||
|
||||
if (instance->Open(instance, nullptr, 3389))
|
||||
{
|
||||
mf_server_main_loop(instance);
|
||||
}
|
||||
|
||||
freerdp_listener_free(instance);
|
||||
|
||||
return 0;
|
||||
}
|
||||
28
third_party/FreeRDP/server/Mac/mfreerdp.h
vendored
Normal file
28
third_party/FreeRDP/server/Mac/mfreerdp.h
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Mac OS X Server
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_MAC_FREERDP_H
|
||||
#define FREERDP_SERVER_MAC_FREERDP_H
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/listener.h>
|
||||
#include <freerdp/codec/rfx.h>
|
||||
|
||||
#endif /* FREERDP_SERVER_MAC_FREERDP_H */
|
||||
63
third_party/FreeRDP/server/Sample/CMakeLists.txt
vendored
Normal file
63
third_party/FreeRDP/server/Sample/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP Sample Server cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
#
|
||||
# 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.
|
||||
|
||||
set(MODULE_NAME "sfreerdp-server")
|
||||
set(MODULE_PREFIX "FREERDP_SERVER_SAMPLE")
|
||||
|
||||
set(SRCS
|
||||
sfreerdp.c
|
||||
sfreerdp.h
|
||||
sf_audin.c
|
||||
sf_audin.h
|
||||
sf_rdpsnd.c
|
||||
sf_rdpsnd.h
|
||||
sf_encomsp.c
|
||||
sf_encomsp.h
|
||||
)
|
||||
|
||||
if(CHANNEL_AINPUT_SERVER)
|
||||
list(APPEND SRCS sf_ainput.c sf_ainput.h)
|
||||
endif()
|
||||
|
||||
option(SAMPLE_USE_VENDOR_PRODUCT_CONFIG_DIR "Use <vendor>/<product> path for resources" OFF)
|
||||
set(SAMPLE_RESOURCE_ROOT ${CMAKE_INSTALL_FULL_DATAROOTDIR})
|
||||
if(SAMPLE_USE_VENDOR_PRODUCT_CONFIG_DIR)
|
||||
string(APPEND SAMPLE_RESOURCE_ROOT "/${VENDOR}")
|
||||
endif()
|
||||
string(APPEND SAMPLE_RESOURCE_ROOT "/${PRODUCT}")
|
||||
|
||||
if(WITH_RESOURCE_VERSIONING)
|
||||
string(APPEND SAMPLE_RESOURCE_ROOT "${FREERDP_VERSION_MAJOR}")
|
||||
endif()
|
||||
string(APPEND SAMPLE_RESOURCE_ROOT "/images")
|
||||
|
||||
set(SAMPLE_ICONS test_icon.bmp test_icon.png test_icon.jpg test_icon.webp)
|
||||
install(FILES ${SAMPLE_ICONS} DESTINATION ${SAMPLE_RESOURCE_ROOT})
|
||||
|
||||
# We need this in runtime path for TestConnect
|
||||
file(COPY test_icon.bmp DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
addtargetwithresourcefile(${MODULE_NAME} TRUE "${FREERDP_VERSION}" SRCS)
|
||||
|
||||
target_compile_definitions(${MODULE_NAME} PRIVATE SAMPLE_RESOURCE_ROOT="${SAMPLE_RESOURCE_ROOT}")
|
||||
list(APPEND LIBS freerdp-server)
|
||||
list(APPEND LIBS winpr freerdp)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${LIBS})
|
||||
installwithrpath(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT server)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/Sample")
|
||||
3
third_party/FreeRDP/server/Sample/ModuleOptions.cmake
vendored
Normal file
3
third_party/FreeRDP/server/Sample/ModuleOptions.cmake
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
set(FREERDP_SERVER_NAME "sfreerdp-server")
|
||||
set(FREERDP_SERVER_PLATFORM "Sample")
|
||||
set(FREERDP_SERVER_VENDOR "FreeRDP")
|
||||
BIN
third_party/FreeRDP/server/Sample/rfx_test.pcap
vendored
Normal file
BIN
third_party/FreeRDP/server/Sample/rfx_test.pcap
vendored
Normal file
Binary file not shown.
93
third_party/FreeRDP/server/Sample/sf_ainput.c
vendored
Normal file
93
third_party/FreeRDP/server/Sample/sf_ainput.c
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Sample Server (Advanced Input)
|
||||
*
|
||||
* Copyright 2022 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2022 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 <winpr/assert.h>
|
||||
|
||||
#include "sfreerdp.h"
|
||||
|
||||
#include "sf_ainput.h"
|
||||
|
||||
#include <freerdp/server/server-common.h>
|
||||
#include <freerdp/server/ainput.h>
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("sample.ainput")
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
WINPR_ATTR_NODISCARD
|
||||
static UINT sf_peer_ainput_mouse_event(WINPR_ATTR_UNUSED ainput_server_context* context,
|
||||
UINT64 timestamp, UINT64 flags, INT32 x, INT32 y)
|
||||
{
|
||||
/* TODO: Implement */
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
WLog_WARN(TAG, "not implemented: 0x%08" PRIx64 ", 0x%08" PRIx64 ", %" PRId32 "x%" PRId32,
|
||||
timestamp, flags, x, y);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
void sf_peer_ainput_init(testPeerContext* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
context->ainput = ainput_server_context_new(context->vcm);
|
||||
WINPR_ASSERT(context->ainput);
|
||||
|
||||
context->ainput->rdpcontext = &context->_p;
|
||||
context->ainput->data = context;
|
||||
|
||||
context->ainput->MouseEvent = sf_peer_ainput_mouse_event;
|
||||
}
|
||||
|
||||
BOOL sf_peer_ainput_start(testPeerContext* context)
|
||||
{
|
||||
if (!context || !context->ainput || !context->ainput->Open)
|
||||
return FALSE;
|
||||
|
||||
return context->ainput->Open(context->ainput) == CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
BOOL sf_peer_ainput_stop(testPeerContext* context)
|
||||
{
|
||||
if (!context || !context->ainput || !context->ainput->Close)
|
||||
return FALSE;
|
||||
|
||||
return context->ainput->Close(context->ainput) == CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
BOOL sf_peer_ainput_running(testPeerContext* context)
|
||||
{
|
||||
if (!context || !context->ainput || !context->ainput->IsOpen)
|
||||
return FALSE;
|
||||
|
||||
return context->ainput->IsOpen(context->ainput);
|
||||
}
|
||||
|
||||
void sf_peer_ainput_uninit(testPeerContext* context)
|
||||
{
|
||||
ainput_server_context_free(context->ainput);
|
||||
}
|
||||
37
third_party/FreeRDP/server/Sample/sf_ainput.h
vendored
Normal file
37
third_party/FreeRDP/server/Sample/sf_ainput.h
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Sample Server (Advanced Input)
|
||||
*
|
||||
* Copyright 2022 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2022 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_SAMPLE_SF_AINPUT_H
|
||||
#define FREERDP_SERVER_SAMPLE_SF_AINPUT_H
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/listener.h>
|
||||
#include <freerdp/server/ainput.h>
|
||||
|
||||
#include "sfreerdp.h"
|
||||
|
||||
void sf_peer_ainput_init(testPeerContext* context);
|
||||
void sf_peer_ainput_uninit(testPeerContext* context);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL sf_peer_ainput_running(testPeerContext* context);
|
||||
BOOL sf_peer_ainput_start(testPeerContext* context);
|
||||
BOOL sf_peer_ainput_stop(testPeerContext* context);
|
||||
|
||||
#endif /* FREERDP_SERVER_SAMPLE_SF_AINPUT_H */
|
||||
114
third_party/FreeRDP/server/Sample/sf_audin.c
vendored
Normal file
114
third_party/FreeRDP/server/Sample/sf_audin.c
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Sample Server (Audio Input)
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2023 Pascal Nowack <Pascal.Nowack@gmx.de>
|
||||
*
|
||||
* 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 <winpr/assert.h>
|
||||
|
||||
#include "sfreerdp.h"
|
||||
|
||||
#include "sf_audin.h"
|
||||
|
||||
#include <freerdp/server/server-common.h>
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("sample")
|
||||
|
||||
#if defined(CHANNEL_AUDIN_SERVER)
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static UINT sf_peer_audin_data(WINPR_ATTR_UNUSED audin_server_context* audin,
|
||||
const SNDIN_DATA* data)
|
||||
{
|
||||
/* TODO: Implement */
|
||||
WINPR_ASSERT(audin);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
WLog_WARN(TAG, "not implemented");
|
||||
WLog_DBG(TAG, "receive %" PRIuz " bytes.", Stream_Length(data->Data));
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
BOOL sf_peer_audin_init(testPeerContext* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
#if defined(CHANNEL_AUDIN_SERVER)
|
||||
context->audin = audin_server_context_new(context->vcm);
|
||||
WINPR_ASSERT(context->audin);
|
||||
|
||||
context->audin->rdpcontext = &context->_p;
|
||||
context->audin->userdata = context;
|
||||
|
||||
context->audin->Data = sf_peer_audin_data;
|
||||
|
||||
return audin_server_set_formats(context->audin, -1, nullptr);
|
||||
#else
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOL sf_peer_audin_start(testPeerContext* context)
|
||||
{
|
||||
#if defined(CHANNEL_AUDIN_SERVER)
|
||||
if (!context || !context->audin || !context->audin->Open)
|
||||
return FALSE;
|
||||
|
||||
return context->audin->Open(context->audin);
|
||||
#else
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOL sf_peer_audin_stop(testPeerContext* context)
|
||||
{
|
||||
#if defined(CHANNEL_AUDIN_SERVER)
|
||||
if (!context || !context->audin || !context->audin->Close)
|
||||
return FALSE;
|
||||
|
||||
return context->audin->Close(context->audin);
|
||||
#else
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOL sf_peer_audin_running(testPeerContext* context)
|
||||
{
|
||||
#if defined(CHANNEL_AUDIN_SERVER)
|
||||
if (!context || !context->audin || !context->audin->IsOpen)
|
||||
return FALSE;
|
||||
|
||||
return context->audin->IsOpen(context->audin);
|
||||
#else
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
void sf_peer_audin_uninit(testPeerContext* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
#if defined(CHANNEL_AUDIN_SERVER)
|
||||
audin_server_context_free(context->audin);
|
||||
context->audin = nullptr;
|
||||
#endif
|
||||
}
|
||||
35
third_party/FreeRDP/server/Sample/sf_audin.h
vendored
Normal file
35
third_party/FreeRDP/server/Sample/sf_audin.h
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Sample Server (Audio Input)
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_SAMPLE_SF_AUDIN_H
|
||||
#define FREERDP_SERVER_SAMPLE_SF_AUDIN_H
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/listener.h>
|
||||
|
||||
#include "sfreerdp.h"
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL sf_peer_audin_init(testPeerContext* context);
|
||||
void sf_peer_audin_uninit(testPeerContext* context);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL sf_peer_audin_running(testPeerContext* context);
|
||||
BOOL sf_peer_audin_start(testPeerContext* context);
|
||||
BOOL sf_peer_audin_stop(testPeerContext* context);
|
||||
|
||||
#endif /* FREERDP_SERVER_SAMPLE_SF_AUDIN_H */
|
||||
40
third_party/FreeRDP/server/Sample/sf_encomsp.c
vendored
Normal file
40
third_party/FreeRDP/server/Sample/sf_encomsp.c
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Sample Server (Lync Multiparty)
|
||||
*
|
||||
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
*
|
||||
* 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 <winpr/assert.h>
|
||||
|
||||
#include "sf_encomsp.h"
|
||||
|
||||
BOOL sf_peer_encomsp_init(testPeerContext* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
context->encomsp = encomsp_server_context_new(context->vcm);
|
||||
if (!context->encomsp)
|
||||
return FALSE;
|
||||
|
||||
context->encomsp->rdpcontext = &context->_p;
|
||||
|
||||
WINPR_ASSERT(context->encomsp->Start);
|
||||
return (context->encomsp->Start(context->encomsp) == CHANNEL_RC_OK);
|
||||
}
|
||||
31
third_party/FreeRDP/server/Sample/sf_encomsp.h
vendored
Normal file
31
third_party/FreeRDP/server/Sample/sf_encomsp.h
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Sample Server (Lync Multiparty)
|
||||
*
|
||||
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_SAMPLE_SF_ENCOMSP_H
|
||||
#define FREERDP_SERVER_SAMPLE_SF_ENCOMSP_H
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/listener.h>
|
||||
#include <freerdp/server/encomsp.h>
|
||||
|
||||
#include "sfreerdp.h"
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL sf_peer_encomsp_init(testPeerContext* context);
|
||||
|
||||
#endif /* FREERDP_SERVER_SAMPLE_SF_ENCOMSP_H */
|
||||
57
third_party/FreeRDP/server/Sample/sf_rdpsnd.c
vendored
Normal file
57
third_party/FreeRDP/server/Sample/sf_rdpsnd.c
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Sample Server (Audio Output)
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
*
|
||||
* 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 <winpr/assert.h>
|
||||
|
||||
#include "sf_rdpsnd.h"
|
||||
|
||||
#include <freerdp/server/server-common.h>
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("sample")
|
||||
|
||||
static void sf_peer_rdpsnd_activated(RdpsndServerContext* context)
|
||||
{
|
||||
WINPR_UNUSED(context);
|
||||
WINPR_ASSERT(context);
|
||||
WLog_DBG(TAG, "RDPSND Activated");
|
||||
}
|
||||
|
||||
BOOL sf_peer_rdpsnd_init(testPeerContext* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
context->rdpsnd = rdpsnd_server_context_new(context->vcm);
|
||||
WINPR_ASSERT(context->rdpsnd);
|
||||
context->rdpsnd->rdpcontext = &context->_p;
|
||||
context->rdpsnd->data = context;
|
||||
context->rdpsnd->num_server_formats =
|
||||
server_rdpsnd_get_formats(&context->rdpsnd->server_formats);
|
||||
|
||||
if (context->rdpsnd->num_server_formats > 0)
|
||||
context->rdpsnd->src_format = &context->rdpsnd->server_formats[0];
|
||||
|
||||
context->rdpsnd->Activated = sf_peer_rdpsnd_activated;
|
||||
|
||||
WINPR_ASSERT(context->rdpsnd->Initialize);
|
||||
return (context->rdpsnd->Initialize(context->rdpsnd, TRUE) == CHANNEL_RC_OK);
|
||||
}
|
||||
31
third_party/FreeRDP/server/Sample/sf_rdpsnd.h
vendored
Normal file
31
third_party/FreeRDP/server/Sample/sf_rdpsnd.h
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Sample Server (Audio Output)
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_SAMPLE_SF_RDPSND_H
|
||||
#define FREERDP_SERVER_SAMPLE_SF_RDPSND_H
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/listener.h>
|
||||
#include <freerdp/server/rdpsnd.h>
|
||||
|
||||
#include "sfreerdp.h"
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL sf_peer_rdpsnd_init(testPeerContext* context);
|
||||
|
||||
#endif /* FREERDP_SERVER_SAMPLE_SF_RDPSND_H */
|
||||
1530
third_party/FreeRDP/server/Sample/sfreerdp.c
vendored
Normal file
1530
third_party/FreeRDP/server/Sample/sfreerdp.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
76
third_party/FreeRDP/server/Sample/sfreerdp.h
vendored
Normal file
76
third_party/FreeRDP/server/Sample/sfreerdp.h
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Sample Server
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_SAMPLE_SFREERDP_SERVER_H
|
||||
#define FREERDP_SERVER_SAMPLE_SFREERDP_SERVER_H
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/listener.h>
|
||||
#include <freerdp/codec/rfx.h>
|
||||
#include <freerdp/codec/nsc.h>
|
||||
#include <freerdp/channels/wtsvc.h>
|
||||
#if defined(CHANNEL_AINPUT_SERVER)
|
||||
#include <freerdp/server/ainput.h>
|
||||
#endif
|
||||
#if defined(CHANNEL_AUDIN_SERVER)
|
||||
#include <freerdp/server/audin.h>
|
||||
#endif
|
||||
#include <freerdp/server/rdpsnd.h>
|
||||
#include <freerdp/server/encomsp.h>
|
||||
#include <freerdp/transport_io.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/image.h>
|
||||
|
||||
struct test_peer_context
|
||||
{
|
||||
rdpContext _p;
|
||||
|
||||
RFX_CONTEXT* rfx_context;
|
||||
NSC_CONTEXT* nsc_context;
|
||||
wStream* s;
|
||||
BYTE* bg_data;
|
||||
UINT32 icon_x;
|
||||
UINT32 icon_y;
|
||||
BOOL activated;
|
||||
HANDLE event;
|
||||
HANDLE stopEvent;
|
||||
HANDLE vcm;
|
||||
void* debug_channel;
|
||||
HANDLE debug_channel_thread;
|
||||
#if defined(CHANNEL_AUDIN_SERVER)
|
||||
audin_server_context* audin;
|
||||
#endif
|
||||
BOOL audin_open;
|
||||
#if defined(CHANNEL_AINPUT_SERVER)
|
||||
ainput_server_context* ainput;
|
||||
BOOL ainput_open;
|
||||
#endif
|
||||
UINT32 frame_id;
|
||||
RdpsndServerContext* rdpsnd;
|
||||
EncomspServerContext* encomsp;
|
||||
|
||||
rdpTransportIo io;
|
||||
wImage* image;
|
||||
};
|
||||
typedef struct test_peer_context testPeerContext;
|
||||
|
||||
#endif /* FREERDP_SERVER_SAMPLE_SFREERDP_SERVER_H */
|
||||
BIN
third_party/FreeRDP/server/Sample/test_icon.bmp
vendored
Normal file
BIN
third_party/FreeRDP/server/Sample/test_icon.bmp
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
BIN
third_party/FreeRDP/server/Sample/test_icon.jpg
vendored
Normal file
BIN
third_party/FreeRDP/server/Sample/test_icon.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
BIN
third_party/FreeRDP/server/Sample/test_icon.png
vendored
Normal file
BIN
third_party/FreeRDP/server/Sample/test_icon.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
5572
third_party/FreeRDP/server/Sample/test_icon.ppm
vendored
Normal file
5572
third_party/FreeRDP/server/Sample/test_icon.ppm
vendored
Normal file
File diff suppressed because it is too large
Load Diff
BIN
third_party/FreeRDP/server/Sample/test_icon.webp
vendored
Normal file
BIN
third_party/FreeRDP/server/Sample/test_icon.webp
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
82
third_party/FreeRDP/server/Windows/CMakeLists.txt
vendored
Normal file
82
third_party/FreeRDP/server/Windows/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP Windows Server cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
#
|
||||
# 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.
|
||||
|
||||
set(MODULE_NAME "wfreerdp-server")
|
||||
set(MODULE_PREFIX "FREERDP_SERVER_WINDOWS")
|
||||
|
||||
include(WarnUnmaintained)
|
||||
warn_unmaintained(${MODULE_NAME} "-DWITH_PLATFORM_SERVER=OFF")
|
||||
|
||||
include_directories(.)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
wf_update.c
|
||||
wf_update.h
|
||||
wf_dxgi.c
|
||||
wf_dxgi.h
|
||||
wf_input.c
|
||||
wf_input.h
|
||||
wf_interface.c
|
||||
wf_interface.h
|
||||
wf_mirage.c
|
||||
wf_mirage.h
|
||||
wf_peer.c
|
||||
wf_peer.h
|
||||
wf_settings.c
|
||||
wf_settings.h
|
||||
wf_info.c
|
||||
wf_info.h
|
||||
)
|
||||
|
||||
if(CHANNEL_RDPSND AND NOT WITH_RDPSND_DSOUND)
|
||||
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} wf_rdpsnd.c wf_rdpsnd.h wf_wasapi.c wf_wasapi.h)
|
||||
endif()
|
||||
|
||||
if(CHANNEL_RDPSND AND WITH_RDPSND_DSOUND)
|
||||
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} wf_rdpsnd.c wf_rdpsnd.h wf_directsound.c wf_directsound.h)
|
||||
endif()
|
||||
|
||||
if(WITH_SERVER_INTERFACE)
|
||||
addtargetwithresourcefile(${MODULE_NAME} FALSE "${FREERDP_VERSION}" ${MODULE_PREFIX}_SRCS)
|
||||
target_include_directories(${MODULE_NAME} INTERFACE $<INSTALL_INTERFACE:include>)
|
||||
else()
|
||||
list(APPEND ${MODULE_PREFIX}_SRCS cli/wfreerdp.c cli/wfreerdp.h)
|
||||
addtargetwithresourcefile(${MODULE_NAME} TRUE "${FREERDP_VERSION}" ${MODULE_PREFIX}_SRCS)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_WINDOWS_VERSION STREQUAL "WINXP")
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} d3d11 dxgi)
|
||||
endif()
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} dsound)
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-server freerdp)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
if(WITH_SERVER_INTERFACE)
|
||||
install(TARGETS ${MODULE_NAME} COMPONENT libraries ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
else()
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT server)
|
||||
endif()
|
||||
|
||||
if(WITH_SERVER_INTERFACE)
|
||||
add_subdirectory(cli)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/Windows")
|
||||
3
third_party/FreeRDP/server/Windows/ModuleOptions.cmake
vendored
Normal file
3
third_party/FreeRDP/server/Windows/ModuleOptions.cmake
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
set(FREERDP_SERVER_NAME "wfreerdp-server")
|
||||
set(FREERDP_SERVER_PLATFORM "Windows")
|
||||
set(FREERDP_SERVER_VENDOR "FreeRDP")
|
||||
34
third_party/FreeRDP/server/Windows/cli/CMakeLists.txt
vendored
Normal file
34
third_party/FreeRDP/server/Windows/cli/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP Windows Server (CLI) cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
#
|
||||
# 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.
|
||||
|
||||
set(MODULE_NAME "wfreerdp-server-cli")
|
||||
set(OUTPUT_NAME "wfreerdp-server")
|
||||
set(MODULE_PREFIX "FREERDP_SERVER_WINDOWS_CLI")
|
||||
|
||||
include_directories(..)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS wfreerdp.c wfreerdp.h)
|
||||
|
||||
addtargetwithresourcefile(${MODULE_NAME} TRUE "${FREERDP_VERSION}" ${MODULE_PREFIX}_SRCS)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS wfreerdp-server)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT server)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/Windows")
|
||||
171
third_party/FreeRDP/server/Windows/cli/wfreerdp.c
vendored
Normal file
171
third_party/FreeRDP/server/Windows/cli/wfreerdp.c
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Windows Server
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <winpr/tchar.h>
|
||||
#include <winpr/windows.h>
|
||||
|
||||
#include "wf_interface.h"
|
||||
|
||||
#include "wfreerdp.h"
|
||||
|
||||
#include <freerdp/server/server-common.h>
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("windows")
|
||||
|
||||
int IDcount = 0;
|
||||
|
||||
BOOL CALLBACK moncb(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
|
||||
{
|
||||
WLog_DBG(TAG, "%d\t(%ld, %ld), (%ld, %ld)", IDcount, lprcMonitor->left, lprcMonitor->top,
|
||||
lprcMonitor->right, lprcMonitor->bottom);
|
||||
IDcount++;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
freerdp_server_warn_unmaintained(argc, argv);
|
||||
|
||||
BOOL screen_selected = FALSE;
|
||||
int index = 1;
|
||||
wfServer* server;
|
||||
server = wfreerdp_server_new();
|
||||
set_screen_id(0);
|
||||
// handle args
|
||||
errno = 0;
|
||||
|
||||
while (index < argc)
|
||||
{
|
||||
// first the args that will cause the program to terminate
|
||||
if (strcmp("--list-screens", argv[index]) == 0)
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int bpp;
|
||||
WLog_INFO(TAG, "Detecting screens...");
|
||||
WLog_INFO(TAG, "ID\tResolution\t\tName (Interface)");
|
||||
|
||||
for (int i = 0;; i++)
|
||||
{
|
||||
_TCHAR name[128] = WINPR_C_ARRAY_INIT;
|
||||
if (get_screen_info(i, name, ARRAYSIZE(name), &width, &height, &bpp) != 0)
|
||||
{
|
||||
if ((width * height * bpp) == 0)
|
||||
continue;
|
||||
|
||||
WLog_INFO(TAG, "%d\t%dx%dx%d\t", i, width, height, bpp);
|
||||
WLog_INFO(TAG, "%s", name);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int vscreen_w;
|
||||
int vscreen_h;
|
||||
vscreen_w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
||||
vscreen_h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
||||
WLog_INFO(TAG, "");
|
||||
EnumDisplayMonitors(nullptr, nullptr, moncb, 0);
|
||||
IDcount = 0;
|
||||
WLog_INFO(TAG, "Virtual Screen = %dx%d", vscreen_w, vscreen_h);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp("--screen", argv[index]) == 0)
|
||||
{
|
||||
UINT32 val;
|
||||
screen_selected = TRUE;
|
||||
index++;
|
||||
|
||||
if (index == argc)
|
||||
{
|
||||
WLog_INFO(TAG, "missing screen id parameter");
|
||||
return 0;
|
||||
}
|
||||
|
||||
val = strtoul(argv[index], nullptr, 0);
|
||||
|
||||
if ((errno != 0) || (val > UINT32_MAX))
|
||||
return -1;
|
||||
|
||||
set_screen_id(val);
|
||||
index++;
|
||||
}
|
||||
|
||||
if (index == argc - 1)
|
||||
{
|
||||
UINT32 val = strtoul(argv[index], nullptr, 0);
|
||||
|
||||
if ((errno != 0) || (val > UINT32_MAX))
|
||||
return -1;
|
||||
|
||||
server->port = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (screen_selected == FALSE)
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int bpp;
|
||||
WLog_INFO(TAG, "screen id not provided. attempting to detect...");
|
||||
WLog_INFO(TAG, "Detecting screens...");
|
||||
WLog_INFO(TAG, "ID\tResolution\t\tName (Interface)");
|
||||
|
||||
for (int i = 0;; i++)
|
||||
{
|
||||
_TCHAR name[128] = WINPR_C_ARRAY_INIT;
|
||||
if (get_screen_info(i, name, ARRAYSIZE(name), &width, &height, &bpp) != 0)
|
||||
{
|
||||
if ((width * height * bpp) == 0)
|
||||
continue;
|
||||
|
||||
WLog_INFO(TAG, "%d\t%dx%dx%d\t", i, width, height, bpp);
|
||||
WLog_INFO(TAG, "%s", name);
|
||||
set_screen_id(i);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WLog_INFO(TAG, "Starting server");
|
||||
wfreerdp_server_start(server);
|
||||
(void)WaitForSingleObject(server->thread, INFINITE);
|
||||
WLog_INFO(TAG, "Stopping server");
|
||||
wfreerdp_server_stop(server);
|
||||
wfreerdp_server_free(server);
|
||||
return 0;
|
||||
}
|
||||
25
third_party/FreeRDP/server/Windows/cli/wfreerdp.h
vendored
Normal file
25
third_party/FreeRDP/server/Windows/cli/wfreerdp.h
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Windows Server
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_WIN_FREERDP_H
|
||||
#define FREERDP_SERVER_WIN_FREERDP_H
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
#endif /* FREERDP_SERVER_WIN_FREERDP_H */
|
||||
219
third_party/FreeRDP/server/Windows/wf_directsound.c
vendored
Normal file
219
third_party/FreeRDP/server/Windows/wf_directsound.c
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
#include "wf_directsound.h"
|
||||
#include "wf_interface.h"
|
||||
#include "wf_info.h"
|
||||
#include "wf_rdpsnd.h"
|
||||
|
||||
#include <winpr/windows.h>
|
||||
|
||||
#define INITGUID
|
||||
#include <initguid.h>
|
||||
#include <objbase.h>
|
||||
|
||||
#define CINTERFACE 1
|
||||
#include <mmsystem.h>
|
||||
#include <dsound.h>
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("windows")
|
||||
|
||||
IDirectSoundCapture8* cap;
|
||||
IDirectSoundCaptureBuffer8* capBuf;
|
||||
DSCBUFFERDESC dscbd;
|
||||
DWORD lastPos;
|
||||
wfPeerContext* latestPeer;
|
||||
|
||||
int wf_rdpsnd_set_latest_peer(wfPeerContext* peer)
|
||||
{
|
||||
latestPeer = peer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wf_directsound_activate(RdpsndServerContext* context)
|
||||
{
|
||||
HRESULT hr;
|
||||
wfInfo* wfi;
|
||||
HANDLE hThread;
|
||||
|
||||
LPDIRECTSOUNDCAPTUREBUFFER pDSCB;
|
||||
|
||||
wfi = wf_info_get_instance();
|
||||
if (!wfi)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to wfi instance");
|
||||
return 1;
|
||||
}
|
||||
WLog_DBG(TAG, "RDPSND (direct sound) Activated");
|
||||
hr = DirectSoundCaptureCreate8(nullptr, &cap, nullptr);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to create sound capture device");
|
||||
return 1;
|
||||
}
|
||||
|
||||
WLog_INFO(TAG, "Created sound capture device");
|
||||
dscbd.dwSize = sizeof(DSCBUFFERDESC);
|
||||
dscbd.dwFlags = 0;
|
||||
dscbd.dwBufferBytes = wfi->agreed_format->nAvgBytesPerSec;
|
||||
dscbd.dwReserved = 0;
|
||||
dscbd.lpwfxFormat = wfi->agreed_format;
|
||||
dscbd.dwFXCount = 0;
|
||||
dscbd.lpDSCFXDesc = nullptr;
|
||||
|
||||
hr = cap->lpVtbl->CreateCaptureBuffer(cap, &dscbd, &pDSCB, nullptr);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to create capture buffer");
|
||||
}
|
||||
|
||||
WLog_INFO(TAG, "Created capture buffer");
|
||||
hr = pDSCB->lpVtbl->QueryInterface(pDSCB, &IID_IDirectSoundCaptureBuffer8, (LPVOID*)&capBuf);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to QI capture buffer");
|
||||
}
|
||||
WLog_INFO(TAG, "Created IDirectSoundCaptureBuffer8");
|
||||
pDSCB->lpVtbl->Release(pDSCB);
|
||||
lastPos = 0;
|
||||
|
||||
if (!(hThread = CreateThread(nullptr, 0, wf_rdpsnd_directsound_thread, latestPeer, 0, nullptr)))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to create direct sound thread");
|
||||
return 1;
|
||||
}
|
||||
(void)CloseHandle(hThread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DWORD WINAPI wf_rdpsnd_directsound_thread(LPVOID lpParam)
|
||||
{
|
||||
HRESULT hr;
|
||||
DWORD beg = 0;
|
||||
DWORD end = 0;
|
||||
DWORD diff, rate;
|
||||
wfPeerContext* context;
|
||||
wfInfo* wfi;
|
||||
|
||||
VOID* pbCaptureData = nullptr;
|
||||
DWORD dwCaptureLength = 0;
|
||||
VOID* pbCaptureData2 = nullptr;
|
||||
DWORD dwCaptureLength2 = 0;
|
||||
VOID* pbPlayData = nullptr;
|
||||
DWORD dwReadPos = 0;
|
||||
LONG lLockSize = 0;
|
||||
|
||||
wfi = wf_info_get_instance();
|
||||
if (!wfi)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed get instance");
|
||||
return 1;
|
||||
}
|
||||
|
||||
context = (wfPeerContext*)lpParam;
|
||||
rate = 1000 / 24;
|
||||
WLog_INFO(TAG, "Trying to start capture");
|
||||
hr = capBuf->lpVtbl->Start(capBuf, DSCBSTART_LOOPING);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to start capture");
|
||||
}
|
||||
WLog_INFO(TAG, "Capture started");
|
||||
|
||||
while (1)
|
||||
{
|
||||
|
||||
end = GetTickCount();
|
||||
diff = end - beg;
|
||||
|
||||
if (diff < rate)
|
||||
{
|
||||
Sleep(rate - diff);
|
||||
}
|
||||
|
||||
beg = GetTickCount();
|
||||
|
||||
if (wf_rdpsnd_lock() > 0)
|
||||
{
|
||||
// check for main exit condition
|
||||
if (wfi->snd_stop == TRUE)
|
||||
{
|
||||
wf_rdpsnd_unlock();
|
||||
break;
|
||||
}
|
||||
|
||||
hr = capBuf->lpVtbl->GetCurrentPosition(capBuf, nullptr, &dwReadPos);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get read pos");
|
||||
wf_rdpsnd_unlock();
|
||||
break;
|
||||
}
|
||||
|
||||
lLockSize = dwReadPos - lastPos; // dscbd.dwBufferBytes;
|
||||
if (lLockSize < 0)
|
||||
lLockSize += dscbd.dwBufferBytes;
|
||||
|
||||
// WLog_DBG(TAG, "Last, read, lock = [%"PRIu32", %"PRIu32", %"PRId32"]\n", lastPos,
|
||||
// dwReadPos, lLockSize);
|
||||
|
||||
if (lLockSize == 0)
|
||||
{
|
||||
wf_rdpsnd_unlock();
|
||||
continue;
|
||||
}
|
||||
|
||||
hr = capBuf->lpVtbl->Lock(capBuf, lastPos, lLockSize, &pbCaptureData, &dwCaptureLength,
|
||||
&pbCaptureData2, &dwCaptureLength2, 0L);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to lock sound capture buffer");
|
||||
wf_rdpsnd_unlock();
|
||||
break;
|
||||
}
|
||||
|
||||
// fwrite(pbCaptureData, 1, dwCaptureLength, pFile);
|
||||
// fwrite(pbCaptureData2, 1, dwCaptureLength2, pFile);
|
||||
|
||||
// FIXME: frames = bytes/(bytespersample * channels)
|
||||
|
||||
context->rdpsnd->SendSamples(context->rdpsnd, pbCaptureData, dwCaptureLength / 4,
|
||||
(UINT16)(beg & 0xffff));
|
||||
context->rdpsnd->SendSamples(context->rdpsnd, pbCaptureData2, dwCaptureLength2 / 4,
|
||||
(UINT16)(beg & 0xffff));
|
||||
|
||||
hr = capBuf->lpVtbl->Unlock(capBuf, pbCaptureData, dwCaptureLength, pbCaptureData2,
|
||||
dwCaptureLength2);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to unlock sound capture buffer");
|
||||
wf_rdpsnd_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO keep track of location in buffer
|
||||
lastPos += dwCaptureLength;
|
||||
lastPos %= dscbd.dwBufferBytes;
|
||||
lastPos += dwCaptureLength2;
|
||||
lastPos %= dscbd.dwBufferBytes;
|
||||
|
||||
wf_rdpsnd_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
WLog_INFO(TAG, "Trying to stop sound capture");
|
||||
hr = capBuf->lpVtbl->Stop(capBuf);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to stop capture");
|
||||
}
|
||||
|
||||
WLog_INFO(TAG, "Capture stopped");
|
||||
capBuf->lpVtbl->Release(capBuf);
|
||||
cap->lpVtbl->Release(cap);
|
||||
|
||||
lastPos = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
13
third_party/FreeRDP/server/Windows/wf_directsound.h
vendored
Normal file
13
third_party/FreeRDP/server/Windows/wf_directsound.h
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef FREERDP_SERVER_WIN_DSOUND_H
|
||||
#define FREERDP_SERVER_WIN_DSOUND_H
|
||||
|
||||
#include <freerdp/server/rdpsnd.h>
|
||||
#include "wf_interface.h"
|
||||
|
||||
WINPR_ATTR_NODISCARD int wf_rdpsnd_set_latest_peer(wfPeerContext* peer);
|
||||
|
||||
WINPR_ATTR_NODISCARD int wf_directsound_activate(RdpsndServerContext* context);
|
||||
|
||||
WINPR_ATTR_NODISCARD DWORD WINAPI wf_rdpsnd_directsound_thread(LPVOID lpParam);
|
||||
|
||||
#endif /* FREERDP_SERVER_WIN_DSOUND_H */
|
||||
486
third_party/FreeRDP/server/Windows/wf_dxgi.c
vendored
Normal file
486
third_party/FreeRDP/server/Windows/wf_dxgi.c
vendored
Normal file
@@ -0,0 +1,486 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Windows Server
|
||||
*
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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 "wf_interface.h"
|
||||
|
||||
#ifdef WITH_DXGI_1_2
|
||||
|
||||
#define CINTERFACE
|
||||
|
||||
#include <D3D11.h>
|
||||
#include <dxgi1_2.h>
|
||||
|
||||
#include <tchar.h>
|
||||
#include "wf_dxgi.h"
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("windows")
|
||||
|
||||
/* Driver types supported */
|
||||
D3D_DRIVER_TYPE DriverTypes[] = {
|
||||
D3D_DRIVER_TYPE_HARDWARE,
|
||||
D3D_DRIVER_TYPE_WARP,
|
||||
D3D_DRIVER_TYPE_REFERENCE,
|
||||
};
|
||||
UINT NumDriverTypes = ARRAYSIZE(DriverTypes);
|
||||
|
||||
/* Feature levels supported */
|
||||
D3D_FEATURE_LEVEL FeatureLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1,
|
||||
D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_1 };
|
||||
|
||||
UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels);
|
||||
|
||||
D3D_FEATURE_LEVEL FeatureLevel;
|
||||
|
||||
ID3D11Device* gDevice = nullptr;
|
||||
ID3D11DeviceContext* gContext = nullptr;
|
||||
IDXGIOutputDuplication* gOutputDuplication = nullptr;
|
||||
ID3D11Texture2D* gAcquiredDesktopImage = nullptr;
|
||||
|
||||
IDXGISurface* surf;
|
||||
ID3D11Texture2D* sStage;
|
||||
|
||||
DXGI_OUTDUPL_FRAME_INFO FrameInfo;
|
||||
|
||||
int wf_dxgi_init(wfInfo* wfi)
|
||||
{
|
||||
gAcquiredDesktopImage = nullptr;
|
||||
|
||||
if (wf_dxgi_createDevice(wfi) != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (wf_dxgi_getDuplication(wfi) != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wf_dxgi_createDevice(wfInfo* wfi)
|
||||
{
|
||||
HRESULT status;
|
||||
UINT DriverTypeIndex;
|
||||
|
||||
for (DriverTypeIndex = 0; DriverTypeIndex < NumDriverTypes; ++DriverTypeIndex)
|
||||
{
|
||||
status = D3D11CreateDevice(nullptr, DriverTypes[DriverTypeIndex], nullptr, 0, FeatureLevels,
|
||||
NumFeatureLevels, D3D11_SDK_VERSION, &gDevice, &FeatureLevel,
|
||||
&gContext);
|
||||
if (SUCCEEDED(status))
|
||||
break;
|
||||
|
||||
WLog_INFO(TAG, "D3D11CreateDevice returned [%ld] for Driver Type %d", status,
|
||||
DriverTypes[DriverTypeIndex]);
|
||||
}
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to create device in InitializeDx");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wf_dxgi_getDuplication(wfInfo* wfi)
|
||||
{
|
||||
HRESULT status;
|
||||
UINT dTop, i = 0;
|
||||
DXGI_OUTPUT_DESC desc = WINPR_C_ARRAY_INIT;
|
||||
IDXGIOutput* pOutput;
|
||||
IDXGIDevice* DxgiDevice = nullptr;
|
||||
IDXGIAdapter* DxgiAdapter = nullptr;
|
||||
IDXGIOutput* DxgiOutput = nullptr;
|
||||
IDXGIOutput1* DxgiOutput1 = nullptr;
|
||||
|
||||
status = gDevice->lpVtbl->QueryInterface(gDevice, &IID_IDXGIDevice, (void**)&DxgiDevice);
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get QI for DXGI Device");
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = DxgiDevice->lpVtbl->GetParent(DxgiDevice, &IID_IDXGIAdapter, (void**)&DxgiAdapter);
|
||||
DxgiDevice->lpVtbl->Release(DxgiDevice);
|
||||
DxgiDevice = nullptr;
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get parent DXGI Adapter");
|
||||
return 1;
|
||||
}
|
||||
|
||||
pOutput = nullptr;
|
||||
|
||||
while (DxgiAdapter->lpVtbl->EnumOutputs(DxgiAdapter, i, &pOutput) != DXGI_ERROR_NOT_FOUND)
|
||||
{
|
||||
DXGI_OUTPUT_DESC* pDesc = &desc;
|
||||
|
||||
status = pOutput->lpVtbl->GetDesc(pOutput, pDesc);
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get description");
|
||||
return 1;
|
||||
}
|
||||
|
||||
WLog_INFO(TAG, "Output %u: [%s] [%d]", i, pDesc->DeviceName, pDesc->AttachedToDesktop);
|
||||
|
||||
if (pDesc->AttachedToDesktop)
|
||||
dTop = i;
|
||||
|
||||
pOutput->lpVtbl->Release(pOutput);
|
||||
++i;
|
||||
}
|
||||
|
||||
dTop = wfi->screenID;
|
||||
|
||||
status = DxgiAdapter->lpVtbl->EnumOutputs(DxgiAdapter, dTop, &DxgiOutput);
|
||||
DxgiAdapter->lpVtbl->Release(DxgiAdapter);
|
||||
DxgiAdapter = nullptr;
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get output");
|
||||
return 1;
|
||||
}
|
||||
|
||||
status =
|
||||
DxgiOutput->lpVtbl->QueryInterface(DxgiOutput, &IID_IDXGIOutput1, (void**)&DxgiOutput1);
|
||||
DxgiOutput->lpVtbl->Release(DxgiOutput);
|
||||
DxgiOutput = nullptr;
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get IDXGIOutput1");
|
||||
return 1;
|
||||
}
|
||||
|
||||
status =
|
||||
DxgiOutput1->lpVtbl->DuplicateOutput(DxgiOutput1, (IUnknown*)gDevice, &gOutputDuplication);
|
||||
DxgiOutput1->lpVtbl->Release(DxgiOutput1);
|
||||
DxgiOutput1 = nullptr;
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
if (status == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
|
||||
{
|
||||
WLog_ERR(
|
||||
TAG,
|
||||
"There is already the maximum number of applications using the Desktop Duplication "
|
||||
"API running, please close one of those applications and then try again.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
WLog_ERR(TAG, "Failed to get duplicate output. Status = %ld", status);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wf_dxgi_cleanup(wfInfo* wfi)
|
||||
{
|
||||
if (wfi->framesWaiting > 0)
|
||||
{
|
||||
wf_dxgi_releasePixelData(wfi);
|
||||
}
|
||||
|
||||
if (gAcquiredDesktopImage)
|
||||
{
|
||||
gAcquiredDesktopImage->lpVtbl->Release(gAcquiredDesktopImage);
|
||||
gAcquiredDesktopImage = nullptr;
|
||||
}
|
||||
|
||||
if (gOutputDuplication)
|
||||
{
|
||||
gOutputDuplication->lpVtbl->Release(gOutputDuplication);
|
||||
gOutputDuplication = nullptr;
|
||||
}
|
||||
|
||||
if (gContext)
|
||||
{
|
||||
gContext->lpVtbl->Release(gContext);
|
||||
gContext = nullptr;
|
||||
}
|
||||
|
||||
if (gDevice)
|
||||
{
|
||||
gDevice->lpVtbl->Release(gDevice);
|
||||
gDevice = nullptr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wf_dxgi_nextFrame(wfInfo* wfi, UINT timeout)
|
||||
{
|
||||
HRESULT status = 0;
|
||||
UINT i = 0;
|
||||
UINT DataBufferSize = 0;
|
||||
BYTE* DataBuffer = nullptr;
|
||||
IDXGIResource* DesktopResource = nullptr;
|
||||
|
||||
if (wfi->framesWaiting > 0)
|
||||
{
|
||||
wf_dxgi_releasePixelData(wfi);
|
||||
}
|
||||
|
||||
if (gAcquiredDesktopImage)
|
||||
{
|
||||
gAcquiredDesktopImage->lpVtbl->Release(gAcquiredDesktopImage);
|
||||
gAcquiredDesktopImage = nullptr;
|
||||
}
|
||||
|
||||
status = gOutputDuplication->lpVtbl->AcquireNextFrame(gOutputDuplication, timeout, &FrameInfo,
|
||||
&DesktopResource);
|
||||
|
||||
if (status == DXGI_ERROR_WAIT_TIMEOUT)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
if (status == DXGI_ERROR_ACCESS_LOST)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to acquire next frame with status=%ld", status);
|
||||
WLog_ERR(TAG, "Trying to reinitialize due to ACCESS LOST...");
|
||||
|
||||
if (gAcquiredDesktopImage)
|
||||
{
|
||||
gAcquiredDesktopImage->lpVtbl->Release(gAcquiredDesktopImage);
|
||||
gAcquiredDesktopImage = nullptr;
|
||||
}
|
||||
|
||||
if (gOutputDuplication)
|
||||
{
|
||||
gOutputDuplication->lpVtbl->Release(gOutputDuplication);
|
||||
gOutputDuplication = nullptr;
|
||||
}
|
||||
|
||||
wf_dxgi_getDuplication(wfi);
|
||||
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to acquire next frame with status=%ld", status);
|
||||
status = gOutputDuplication->lpVtbl->ReleaseFrame(gOutputDuplication);
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to release frame with status=%ld", status);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
status = DesktopResource->lpVtbl->QueryInterface(DesktopResource, &IID_ID3D11Texture2D,
|
||||
(void**)&gAcquiredDesktopImage);
|
||||
DesktopResource->lpVtbl->Release(DesktopResource);
|
||||
DesktopResource = nullptr;
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
wfi->framesWaiting = FrameInfo.AccumulatedFrames;
|
||||
|
||||
if (FrameInfo.AccumulatedFrames == 0)
|
||||
{
|
||||
status = gOutputDuplication->lpVtbl->ReleaseFrame(gOutputDuplication);
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to release frame with status=%ld", status);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wf_dxgi_getPixelData(wfInfo* wfi, BYTE** data, int* pitch, RECT* invalid)
|
||||
{
|
||||
HRESULT status;
|
||||
D3D11_BOX Box;
|
||||
DXGI_MAPPED_RECT mappedRect;
|
||||
D3D11_TEXTURE2D_DESC tDesc;
|
||||
|
||||
tDesc.Width = (invalid->right - invalid->left);
|
||||
tDesc.Height = (invalid->bottom - invalid->top);
|
||||
tDesc.MipLevels = 1;
|
||||
tDesc.ArraySize = 1;
|
||||
tDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
tDesc.SampleDesc.Count = 1;
|
||||
tDesc.SampleDesc.Quality = 0;
|
||||
tDesc.Usage = D3D11_USAGE_STAGING;
|
||||
tDesc.BindFlags = 0;
|
||||
tDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
tDesc.MiscFlags = 0;
|
||||
|
||||
Box.top = invalid->top;
|
||||
Box.left = invalid->left;
|
||||
Box.right = invalid->right;
|
||||
Box.bottom = invalid->bottom;
|
||||
Box.front = 0;
|
||||
Box.back = 1;
|
||||
|
||||
status = gDevice->lpVtbl->CreateTexture2D(gDevice, &tDesc, nullptr, &sStage);
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to create staging surface");
|
||||
exit(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
gContext->lpVtbl->CopySubresourceRegion(gContext, (ID3D11Resource*)sStage, 0, 0, 0, 0,
|
||||
(ID3D11Resource*)gAcquiredDesktopImage, 0, &Box);
|
||||
|
||||
status = sStage->lpVtbl->QueryInterface(sStage, &IID_IDXGISurface, (void**)&surf);
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to QI staging surface");
|
||||
exit(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
surf->lpVtbl->Map(surf, &mappedRect, DXGI_MAP_READ);
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to map staging surface");
|
||||
exit(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
*data = mappedRect.pBits;
|
||||
*pitch = mappedRect.Pitch;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wf_dxgi_releasePixelData(wfInfo* wfi)
|
||||
{
|
||||
HRESULT status;
|
||||
|
||||
surf->lpVtbl->Unmap(surf);
|
||||
surf->lpVtbl->Release(surf);
|
||||
surf = nullptr;
|
||||
sStage->lpVtbl->Release(sStage);
|
||||
sStage = nullptr;
|
||||
|
||||
status = gOutputDuplication->lpVtbl->ReleaseFrame(gOutputDuplication);
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to release frame");
|
||||
return 1;
|
||||
}
|
||||
|
||||
wfi->framesWaiting = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wf_dxgi_getInvalidRegion(RECT* invalid)
|
||||
{
|
||||
HRESULT status;
|
||||
UINT dirty;
|
||||
UINT BufSize;
|
||||
RECT* pRect;
|
||||
BYTE* DirtyRects;
|
||||
UINT DataBufferSize = 0;
|
||||
BYTE* DataBuffer = nullptr;
|
||||
|
||||
if (FrameInfo.AccumulatedFrames == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (FrameInfo.TotalMetadataBufferSize)
|
||||
{
|
||||
|
||||
if (FrameInfo.TotalMetadataBufferSize > DataBufferSize)
|
||||
{
|
||||
if (DataBuffer)
|
||||
{
|
||||
free(DataBuffer);
|
||||
DataBuffer = nullptr;
|
||||
}
|
||||
|
||||
DataBuffer = (BYTE*)malloc(FrameInfo.TotalMetadataBufferSize);
|
||||
|
||||
if (!DataBuffer)
|
||||
{
|
||||
DataBufferSize = 0;
|
||||
WLog_ERR(TAG, "Failed to allocate memory for metadata");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DataBufferSize = FrameInfo.TotalMetadataBufferSize;
|
||||
}
|
||||
|
||||
BufSize = FrameInfo.TotalMetadataBufferSize;
|
||||
|
||||
status = gOutputDuplication->lpVtbl->GetFrameMoveRects(
|
||||
gOutputDuplication, BufSize, (DXGI_OUTDUPL_MOVE_RECT*)DataBuffer, &BufSize);
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get frame move rects");
|
||||
return 1;
|
||||
}
|
||||
|
||||
DirtyRects = DataBuffer + BufSize;
|
||||
BufSize = FrameInfo.TotalMetadataBufferSize - BufSize;
|
||||
|
||||
status = gOutputDuplication->lpVtbl->GetFrameDirtyRects(gOutputDuplication, BufSize,
|
||||
(RECT*)DirtyRects, &BufSize);
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get frame dirty rects");
|
||||
return 1;
|
||||
}
|
||||
dirty = BufSize / sizeof(RECT);
|
||||
|
||||
pRect = (RECT*)DirtyRects;
|
||||
|
||||
for (UINT i = 0; i < dirty; ++i)
|
||||
{
|
||||
UnionRect(invalid, invalid, pRect);
|
||||
++pRect;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
42
third_party/FreeRDP/server/Windows/wf_dxgi.h
vendored
Normal file
42
third_party/FreeRDP/server/Windows/wf_dxgi.h
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Windows Server
|
||||
*
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_WIN_DXGI_H
|
||||
#define FREERDP_SERVER_WIN_DXGI_H
|
||||
|
||||
#include "wf_interface.h"
|
||||
|
||||
WINPR_ATTR_NODISCARD int wf_dxgi_init(wfInfo* context);
|
||||
|
||||
WINPR_ATTR_NODISCARD int wf_dxgi_createDevice(wfInfo* context);
|
||||
|
||||
WINPR_ATTR_NODISCARD int wf_dxgi_getDuplication(wfInfo* context);
|
||||
|
||||
WINPR_ATTR_NODISCARD int wf_dxgi_cleanup(wfInfo* context);
|
||||
|
||||
WINPR_ATTR_NODISCARD int wf_dxgi_nextFrame(wfInfo* context, UINT timeout);
|
||||
|
||||
WINPR_ATTR_NODISCARD int wf_dxgi_getPixelData(wfInfo* context, BYTE** data, int* pitch,
|
||||
RECT* invalid);
|
||||
|
||||
WINPR_ATTR_NODISCARD int wf_dxgi_releasePixelData(wfInfo* context);
|
||||
|
||||
WINPR_ATTR_NODISCARD int wf_dxgi_getInvalidRegion(RECT* invalid);
|
||||
|
||||
#endif /* FREERDP_SERVER_WIN_DXGI_H */
|
||||
403
third_party/FreeRDP/server/Windows/wf_info.c
vendored
Normal file
403
third_party/FreeRDP/server/Windows/wf_info.c
vendored
Normal file
@@ -0,0 +1,403 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Client
|
||||
* FreeRDP Windows Server
|
||||
*
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
|
||||
#include <freerdp/build-config.h>
|
||||
|
||||
#include <winpr/tchar.h>
|
||||
#include <winpr/windows.h>
|
||||
|
||||
#include "wf_info.h"
|
||||
#include "wf_update.h"
|
||||
#include "wf_mirage.h"
|
||||
#include "wf_dxgi.h"
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("windows")
|
||||
|
||||
#define SERVER_KEY "Software\\" FREERDP_VENDOR_STRING "\\" FREERDP_PRODUCT_STRING "\\Server"
|
||||
|
||||
static wfInfo* wfInfoInstance = nullptr;
|
||||
static int _IDcount = 0;
|
||||
|
||||
BOOL wf_info_lock(wfInfo* wfi)
|
||||
{
|
||||
DWORD dRes;
|
||||
dRes = WaitForSingleObject(wfi->mutex, INFINITE);
|
||||
|
||||
switch (dRes)
|
||||
{
|
||||
case WAIT_ABANDONED:
|
||||
case WAIT_OBJECT_0:
|
||||
return TRUE;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
return FALSE;
|
||||
|
||||
case WAIT_FAILED:
|
||||
WLog_ERR(TAG, "wf_info_lock failed with 0x%08lX", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL wf_info_try_lock(wfInfo* wfi, DWORD dwMilliseconds)
|
||||
{
|
||||
DWORD dRes;
|
||||
dRes = WaitForSingleObject(wfi->mutex, dwMilliseconds);
|
||||
|
||||
switch (dRes)
|
||||
{
|
||||
case WAIT_ABANDONED:
|
||||
case WAIT_OBJECT_0:
|
||||
return TRUE;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
return FALSE;
|
||||
|
||||
case WAIT_FAILED:
|
||||
WLog_ERR(TAG, "wf_info_try_lock failed with 0x%08lX", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL wf_info_unlock(wfInfo* wfi)
|
||||
{
|
||||
if (!ReleaseMutex(wfi->mutex))
|
||||
{
|
||||
WLog_ERR(TAG, "wf_info_unlock failed with 0x%08lX", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
wfInfo* wf_info_init()
|
||||
{
|
||||
wfInfo* wfi;
|
||||
wfi = (wfInfo*)calloc(1, sizeof(wfInfo));
|
||||
|
||||
if (wfi != nullptr)
|
||||
{
|
||||
HKEY hKey;
|
||||
LONG status;
|
||||
DWORD dwType;
|
||||
DWORD dwSize;
|
||||
DWORD dwValue;
|
||||
wfi->mutex = CreateMutex(nullptr, FALSE, nullptr);
|
||||
|
||||
if (wfi->mutex == nullptr)
|
||||
{
|
||||
WLog_ERR(TAG, "CreateMutex error: %lu", GetLastError());
|
||||
free(wfi);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
wfi->updateSemaphore = CreateSemaphore(nullptr, 0, 32, nullptr);
|
||||
|
||||
if (!wfi->updateSemaphore)
|
||||
{
|
||||
WLog_ERR(TAG, "CreateSemaphore error: %lu", GetLastError());
|
||||
(void)CloseHandle(wfi->mutex);
|
||||
free(wfi);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
wfi->updateThread =
|
||||
CreateThread(nullptr, 0, wf_update_thread, wfi, CREATE_SUSPENDED, nullptr);
|
||||
|
||||
if (!wfi->updateThread)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to create update thread");
|
||||
(void)CloseHandle(wfi->mutex);
|
||||
(void)CloseHandle(wfi->updateSemaphore);
|
||||
free(wfi);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
wfi->peers =
|
||||
(freerdp_peer**)calloc(FREERDP_SERVER_WIN_INFO_MAXPEERS, sizeof(freerdp_peer*));
|
||||
|
||||
if (!wfi->peers)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to allocate memory for peer");
|
||||
(void)CloseHandle(wfi->mutex);
|
||||
(void)CloseHandle(wfi->updateSemaphore);
|
||||
(void)CloseHandle(wfi->updateThread);
|
||||
free(wfi);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Set FPS
|
||||
wfi->framesPerSecond = FREERDP_SERVER_WIN_INFO_DEFAULT_FPS;
|
||||
status =
|
||||
RegOpenKeyExA(HKEY_LOCAL_MACHINE, SERVER_KEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
|
||||
|
||||
if (status == ERROR_SUCCESS)
|
||||
{
|
||||
if (RegQueryValueEx(hKey, _T("FramesPerSecond"), nullptr, &dwType, (BYTE*)&dwValue,
|
||||
&dwSize) == ERROR_SUCCESS)
|
||||
wfi->framesPerSecond = dwValue;
|
||||
}
|
||||
|
||||
RegCloseKey(hKey);
|
||||
// Set input toggle
|
||||
wfi->input_disabled = FALSE;
|
||||
status =
|
||||
RegOpenKeyExA(HKEY_LOCAL_MACHINE, SERVER_KEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
|
||||
|
||||
if (status == ERROR_SUCCESS)
|
||||
{
|
||||
if (RegQueryValueEx(hKey, _T("DisableInput"), nullptr, &dwType, (BYTE*)&dwValue,
|
||||
&dwSize) == ERROR_SUCCESS)
|
||||
{
|
||||
if (dwValue != 0)
|
||||
wfi->input_disabled = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
|
||||
return wfi;
|
||||
}
|
||||
|
||||
wfInfo* wf_info_get_instance()
|
||||
{
|
||||
if (wfInfoInstance == nullptr)
|
||||
wfInfoInstance = wf_info_init();
|
||||
|
||||
return wfInfoInstance;
|
||||
}
|
||||
|
||||
BOOL wf_info_peer_register(wfInfo* wfi, wfPeerContext* context)
|
||||
{
|
||||
int peerId = 0;
|
||||
|
||||
if (!wfi || !context)
|
||||
return FALSE;
|
||||
|
||||
if (!wf_info_lock(wfi))
|
||||
return FALSE;
|
||||
|
||||
if (wfi->peerCount == FREERDP_SERVER_WIN_INFO_MAXPEERS)
|
||||
goto fail_peer_count;
|
||||
|
||||
context->info = wfi;
|
||||
|
||||
if (!(context->updateEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
goto fail_update_event;
|
||||
|
||||
// get the offset of the top left corner of selected screen
|
||||
EnumDisplayMonitors(nullptr, nullptr, wf_info_monEnumCB, 0);
|
||||
_IDcount = 0;
|
||||
#ifdef WITH_DXGI_1_2
|
||||
|
||||
if (wfi->peerCount == 0)
|
||||
if (wf_dxgi_init(wfi) != 0)
|
||||
goto fail_driver_init;
|
||||
|
||||
#else
|
||||
|
||||
if (!wf_mirror_driver_activate(wfi))
|
||||
goto fail_driver_init;
|
||||
|
||||
#endif
|
||||
|
||||
// look through the array of peers until an empty slot
|
||||
for (int i = 0; i < FREERDP_SERVER_WIN_INFO_MAXPEERS; ++i)
|
||||
{
|
||||
// empty index will be our peer id
|
||||
if (wfi->peers[i] == nullptr)
|
||||
{
|
||||
peerId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
wfi->peers[peerId] = ((rdpContext*)context)->peer;
|
||||
wfi->peers[peerId]->pId = peerId;
|
||||
wfi->peerCount++;
|
||||
WLog_INFO(TAG, "Registering Peer: id=%d #=%d", peerId, wfi->peerCount);
|
||||
wf_info_unlock(wfi);
|
||||
wfreerdp_server_peer_callback_event(peerId, FREERDP_SERVER_WIN_SRV_CALLBACK_EVENT_CONNECT);
|
||||
return TRUE;
|
||||
fail_driver_init:
|
||||
(void)CloseHandle(context->updateEvent);
|
||||
context->updateEvent = nullptr;
|
||||
fail_update_event:
|
||||
fail_peer_count:
|
||||
context->socketClose = TRUE;
|
||||
wf_info_unlock(wfi);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void wf_info_peer_unregister(wfInfo* wfi, wfPeerContext* context)
|
||||
{
|
||||
if (wf_info_lock(wfi))
|
||||
{
|
||||
int peerId;
|
||||
peerId = ((rdpContext*)context)->peer->pId;
|
||||
wfi->peers[peerId] = nullptr;
|
||||
wfi->peerCount--;
|
||||
(void)CloseHandle(context->updateEvent);
|
||||
WLog_INFO(TAG, "Unregistering Peer: id=%d, #=%d", peerId, wfi->peerCount);
|
||||
#ifdef WITH_DXGI_1_2
|
||||
|
||||
if (wfi->peerCount == 0)
|
||||
wf_dxgi_cleanup(wfi);
|
||||
|
||||
#endif
|
||||
wf_info_unlock(wfi);
|
||||
wfreerdp_server_peer_callback_event(peerId,
|
||||
FREERDP_SERVER_WIN_SRV_CALLBACK_EVENT_DISCONNECT);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL wf_info_have_updates(wfInfo* wfi)
|
||||
{
|
||||
#ifdef WITH_DXGI_1_2
|
||||
|
||||
if (wfi->framesWaiting == 0)
|
||||
return FALSE;
|
||||
|
||||
#else
|
||||
|
||||
if (wfi->nextUpdate == wfi->lastUpdate)
|
||||
return FALSE;
|
||||
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void wf_info_update_changes(wfInfo* wfi)
|
||||
{
|
||||
#ifdef WITH_DXGI_1_2
|
||||
wf_dxgi_nextFrame(wfi, wfi->framesPerSecond * 1000);
|
||||
#else
|
||||
GETCHANGESBUF* buf;
|
||||
buf = (GETCHANGESBUF*)wfi->changeBuffer;
|
||||
wfi->nextUpdate = buf->buffer->counter;
|
||||
#endif
|
||||
}
|
||||
|
||||
void wf_info_find_invalid_region(wfInfo* wfi)
|
||||
{
|
||||
#ifdef WITH_DXGI_1_2
|
||||
wf_dxgi_getInvalidRegion(&wfi->invalid);
|
||||
#else
|
||||
GETCHANGESBUF* buf;
|
||||
buf = (GETCHANGESBUF*)wfi->changeBuffer;
|
||||
|
||||
for (ULONG i = wfi->lastUpdate; i != wfi->nextUpdate; i = (i + 1) % MAXCHANGES_BUF)
|
||||
{
|
||||
LPRECT lpR = &buf->buffer->pointrect[i].rect;
|
||||
|
||||
// need to make sure we only get updates from the selected screen
|
||||
if ((lpR->left >= wfi->servscreen_xoffset) &&
|
||||
(lpR->right <= (wfi->servscreen_xoffset + wfi->servscreen_width)) &&
|
||||
(lpR->top >= wfi->servscreen_yoffset) &&
|
||||
(lpR->bottom <= (wfi->servscreen_yoffset + wfi->servscreen_height)))
|
||||
{
|
||||
UnionRect(&wfi->invalid, &wfi->invalid, lpR);
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (wfi->invalid.left < 0)
|
||||
wfi->invalid.left = 0;
|
||||
|
||||
if (wfi->invalid.top < 0)
|
||||
wfi->invalid.top = 0;
|
||||
|
||||
if (wfi->invalid.right >= wfi->servscreen_width)
|
||||
wfi->invalid.right = wfi->servscreen_width - 1;
|
||||
|
||||
if (wfi->invalid.bottom >= wfi->servscreen_height)
|
||||
wfi->invalid.bottom = wfi->servscreen_height - 1;
|
||||
|
||||
// WLog_DBG(TAG, "invalid region: (%"PRId32", %"PRId32"), (%"PRId32", %"PRId32")",
|
||||
// wfi->invalid.left, wfi->invalid.top, wfi->invalid.right, wfi->invalid.bottom);
|
||||
}
|
||||
|
||||
void wf_info_clear_invalid_region(wfInfo* wfi)
|
||||
{
|
||||
wfi->lastUpdate = wfi->nextUpdate;
|
||||
SetRectEmpty(&wfi->invalid);
|
||||
}
|
||||
|
||||
void wf_info_invalidate_full_screen(wfInfo* wfi)
|
||||
{
|
||||
SetRect(&wfi->invalid, 0, 0, wfi->servscreen_width, wfi->servscreen_height);
|
||||
}
|
||||
|
||||
BOOL wf_info_have_invalid_region(wfInfo* wfi)
|
||||
{
|
||||
return IsRectEmpty(&wfi->invalid);
|
||||
}
|
||||
|
||||
void wf_info_getScreenData(wfInfo* wfi, long* width, long* height, BYTE** pBits, int* pitch)
|
||||
{
|
||||
*width = (wfi->invalid.right - wfi->invalid.left);
|
||||
*height = (wfi->invalid.bottom - wfi->invalid.top);
|
||||
#ifdef WITH_DXGI_1_2
|
||||
wf_dxgi_getPixelData(wfi, pBits, pitch, &wfi->invalid);
|
||||
#else
|
||||
{
|
||||
long offset;
|
||||
GETCHANGESBUF* changes;
|
||||
changes = (GETCHANGESBUF*)wfi->changeBuffer;
|
||||
*width += 1;
|
||||
*height += 1;
|
||||
offset = (4 * wfi->invalid.left) + (wfi->invalid.top * wfi->virtscreen_width * 4);
|
||||
*pBits = ((BYTE*)(changes->Userbuffer)) + offset;
|
||||
*pitch = wfi->virtscreen_width * 4;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOL CALLBACK wf_info_monEnumCB(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor,
|
||||
LPARAM dwData)
|
||||
{
|
||||
wfInfo* wfi;
|
||||
wfi = wf_info_get_instance();
|
||||
|
||||
if (!wfi)
|
||||
return FALSE;
|
||||
|
||||
if (_IDcount == wfi->screenID)
|
||||
{
|
||||
wfi->servscreen_xoffset = lprcMonitor->left;
|
||||
wfi->servscreen_yoffset = lprcMonitor->top;
|
||||
}
|
||||
|
||||
_IDcount++;
|
||||
return TRUE;
|
||||
}
|
||||
46
third_party/FreeRDP/server/Windows/wf_info.h
vendored
Normal file
46
third_party/FreeRDP/server/Windows/wf_info.h
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Windows Server
|
||||
*
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_WIN_INFO_H
|
||||
#define FREERDP_SERVER_WIN_INFO_H
|
||||
|
||||
#include "wf_interface.h"
|
||||
|
||||
#define FREERDP_SERVER_WIN_INFO_DEFAULT_FPS 24
|
||||
#define FREERDP_SERVER_WIN_INFO_MAXPEERS 32
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL wf_info_lock(wfInfo* wfi);
|
||||
WINPR_ATTR_NODISCARD BOOL wf_info_try_lock(wfInfo* wfi, DWORD dwMilliseconds);
|
||||
WINPR_ATTR_NODISCARD BOOL wf_info_unlock(wfInfo* wfi);
|
||||
|
||||
WINPR_ATTR_NODISCARD wfInfo* wf_info_get_instance(void);
|
||||
WINPR_ATTR_NODISCARD BOOL wf_info_peer_register(wfInfo* wfi, wfPeerContext* context);
|
||||
void wf_info_peer_unregister(wfInfo* wfi, wfPeerContext* context);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL wf_info_have_updates(wfInfo* wfi);
|
||||
void wf_info_update_changes(wfInfo* wfi);
|
||||
void wf_info_find_invalid_region(wfInfo* wfi);
|
||||
void wf_info_clear_invalid_region(wfInfo* wfi);
|
||||
void wf_info_invalidate_full_screen(wfInfo* wfi);
|
||||
WINPR_ATTR_NODISCARD BOOL wf_info_have_invalid_region(wfInfo* wfi);
|
||||
void wf_info_getScreenData(wfInfo* wfi, long* width, long* height, BYTE** pBits, int* pitch);
|
||||
WINPR_ATTR_NODISCARD BOOL CALLBACK wf_info_monEnumCB(HMONITOR hMonitor, HDC hdcMonitor,
|
||||
LPRECT lprcMonitor, LPARAM dwData);
|
||||
|
||||
#endif /* FREERDP_SERVER_WIN_INFO_H */
|
||||
223
third_party/FreeRDP/server/Windows/wf_input.c
vendored
Normal file
223
third_party/FreeRDP/server/Windows/wf_input.c
vendored
Normal file
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Windows Server
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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 <winpr/windows.h>
|
||||
|
||||
#include "wf_input.h"
|
||||
#include "wf_info.h"
|
||||
|
||||
BOOL wf_peer_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code)
|
||||
{
|
||||
INPUT keyboard_event;
|
||||
WINPR_UNUSED(input);
|
||||
keyboard_event.type = INPUT_KEYBOARD;
|
||||
keyboard_event.u.ki.wVk = 0;
|
||||
keyboard_event.u.ki.wScan = code;
|
||||
keyboard_event.u.ki.dwFlags = KEYEVENTF_SCANCODE;
|
||||
keyboard_event.u.ki.dwExtraInfo = 0;
|
||||
keyboard_event.u.ki.time = 0;
|
||||
|
||||
if (flags & KBD_FLAGS_RELEASE)
|
||||
keyboard_event.u.ki.dwFlags |= KEYEVENTF_KEYUP;
|
||||
|
||||
if (flags & KBD_FLAGS_EXTENDED)
|
||||
keyboard_event.u.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
|
||||
|
||||
SendInput(1, &keyboard_event, sizeof(INPUT));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL wf_peer_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
|
||||
{
|
||||
INPUT keyboard_event;
|
||||
WINPR_UNUSED(input);
|
||||
keyboard_event.type = INPUT_KEYBOARD;
|
||||
keyboard_event.u.ki.wVk = 0;
|
||||
keyboard_event.u.ki.wScan = code;
|
||||
keyboard_event.u.ki.dwFlags = KEYEVENTF_UNICODE;
|
||||
keyboard_event.u.ki.dwExtraInfo = 0;
|
||||
keyboard_event.u.ki.time = 0;
|
||||
|
||||
if (flags & KBD_FLAGS_RELEASE)
|
||||
keyboard_event.u.ki.dwFlags |= KEYEVENTF_KEYUP;
|
||||
|
||||
SendInput(1, &keyboard_event, sizeof(INPUT));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL wf_peer_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
|
||||
{
|
||||
INPUT mouse_event = WINPR_C_ARRAY_INIT;
|
||||
float width, height;
|
||||
WINPR_UNUSED(input);
|
||||
|
||||
WINPR_ASSERT(input);
|
||||
mouse_event.type = INPUT_MOUSE;
|
||||
|
||||
if (flags & PTR_FLAGS_WHEEL)
|
||||
{
|
||||
mouse_event.u.mi.dwFlags = MOUSEEVENTF_WHEEL;
|
||||
mouse_event.u.mi.mouseData = flags & WheelRotationMask;
|
||||
|
||||
if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
|
||||
mouse_event.u.mi.mouseData *= -1;
|
||||
|
||||
SendInput(1, &mouse_event, sizeof(INPUT));
|
||||
}
|
||||
else
|
||||
{
|
||||
wfInfo* wfi;
|
||||
wfi = wf_info_get_instance();
|
||||
|
||||
if (!wfi)
|
||||
return FALSE;
|
||||
|
||||
// width and height of primary screen (even in multimon setups
|
||||
width = (float)GetSystemMetrics(SM_CXSCREEN);
|
||||
height = (float)GetSystemMetrics(SM_CYSCREEN);
|
||||
x += wfi->servscreen_xoffset;
|
||||
y += wfi->servscreen_yoffset;
|
||||
mouse_event.u.mi.dx = (LONG)((float)x * (65535.0f / width));
|
||||
mouse_event.u.mi.dy = (LONG)((float)y * (65535.0f / height));
|
||||
mouse_event.u.mi.dwFlags = MOUSEEVENTF_ABSOLUTE;
|
||||
|
||||
if (flags & PTR_FLAGS_MOVE)
|
||||
{
|
||||
mouse_event.u.mi.dwFlags |= MOUSEEVENTF_MOVE;
|
||||
SendInput(1, &mouse_event, sizeof(INPUT));
|
||||
}
|
||||
|
||||
mouse_event.u.mi.dwFlags = MOUSEEVENTF_ABSOLUTE;
|
||||
|
||||
if (flags & PTR_FLAGS_BUTTON1)
|
||||
{
|
||||
if (flags & PTR_FLAGS_DOWN)
|
||||
mouse_event.u.mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
|
||||
else
|
||||
mouse_event.u.mi.dwFlags |= MOUSEEVENTF_LEFTUP;
|
||||
|
||||
SendInput(1, &mouse_event, sizeof(INPUT));
|
||||
}
|
||||
else if (flags & PTR_FLAGS_BUTTON2)
|
||||
{
|
||||
if (flags & PTR_FLAGS_DOWN)
|
||||
mouse_event.u.mi.dwFlags |= MOUSEEVENTF_RIGHTDOWN;
|
||||
else
|
||||
mouse_event.u.mi.dwFlags |= MOUSEEVENTF_RIGHTUP;
|
||||
|
||||
SendInput(1, &mouse_event, sizeof(INPUT));
|
||||
}
|
||||
else if (flags & PTR_FLAGS_BUTTON3)
|
||||
{
|
||||
if (flags & PTR_FLAGS_DOWN)
|
||||
mouse_event.u.mi.dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
|
||||
else
|
||||
mouse_event.u.mi.dwFlags |= MOUSEEVENTF_MIDDLEUP;
|
||||
|
||||
SendInput(1, &mouse_event, sizeof(INPUT));
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL wf_peer_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
|
||||
{
|
||||
if ((flags & PTR_XFLAGS_BUTTON1) || (flags & PTR_XFLAGS_BUTTON2))
|
||||
{
|
||||
INPUT mouse_event = WINPR_C_ARRAY_INIT;
|
||||
mouse_event.type = INPUT_MOUSE;
|
||||
|
||||
if (flags & PTR_FLAGS_MOVE)
|
||||
{
|
||||
float width, height;
|
||||
wfInfo* wfi;
|
||||
wfi = wf_info_get_instance();
|
||||
|
||||
if (!wfi)
|
||||
return FALSE;
|
||||
|
||||
// width and height of primary screen (even in multimon setups
|
||||
width = (float)GetSystemMetrics(SM_CXSCREEN);
|
||||
height = (float)GetSystemMetrics(SM_CYSCREEN);
|
||||
x += wfi->servscreen_xoffset;
|
||||
y += wfi->servscreen_yoffset;
|
||||
mouse_event.u.mi.dx = (LONG)((float)x * (65535.0f / width));
|
||||
mouse_event.u.mi.dy = (LONG)((float)y * (65535.0f / height));
|
||||
mouse_event.u.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
|
||||
SendInput(1, &mouse_event, sizeof(INPUT));
|
||||
}
|
||||
|
||||
mouse_event.u.mi.dx = mouse_event.u.mi.dy = mouse_event.u.mi.dwFlags = 0;
|
||||
|
||||
if (flags & PTR_XFLAGS_DOWN)
|
||||
mouse_event.u.mi.dwFlags |= MOUSEEVENTF_XDOWN;
|
||||
else
|
||||
mouse_event.u.mi.dwFlags |= MOUSEEVENTF_XUP;
|
||||
|
||||
if (flags & PTR_XFLAGS_BUTTON1)
|
||||
mouse_event.u.mi.mouseData = XBUTTON1;
|
||||
else if (flags & PTR_XFLAGS_BUTTON2)
|
||||
mouse_event.u.mi.mouseData = XBUTTON2;
|
||||
|
||||
SendInput(1, &mouse_event, sizeof(INPUT));
|
||||
}
|
||||
else
|
||||
{
|
||||
wf_peer_mouse_event(input, flags, x, y);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL wf_peer_keyboard_event_dummy(rdpInput* input, UINT16 flags, UINT8 code)
|
||||
{
|
||||
WINPR_UNUSED(input);
|
||||
WINPR_UNUSED(flags);
|
||||
WINPR_UNUSED(code);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL wf_peer_unicode_keyboard_event_dummy(rdpInput* input, UINT16 flags, UINT16 code)
|
||||
{
|
||||
WINPR_UNUSED(input);
|
||||
WINPR_UNUSED(flags);
|
||||
WINPR_UNUSED(code);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL wf_peer_mouse_event_dummy(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
|
||||
{
|
||||
WINPR_UNUSED(input);
|
||||
WINPR_UNUSED(flags);
|
||||
WINPR_UNUSED(x);
|
||||
WINPR_UNUSED(y);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL wf_peer_extended_mouse_event_dummy(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
|
||||
{
|
||||
WINPR_UNUSED(input);
|
||||
WINPR_UNUSED(flags);
|
||||
WINPR_UNUSED(x);
|
||||
WINPR_UNUSED(y);
|
||||
return TRUE;
|
||||
}
|
||||
41
third_party/FreeRDP/server/Windows/wf_input.h
vendored
Normal file
41
third_party/FreeRDP/server/Windows/wf_input.h
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Windows Server
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_WIN_INPUT_H
|
||||
#define FREERDP_SERVER_WIN_INPUT_H
|
||||
|
||||
#include "wf_interface.h"
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL wf_peer_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code);
|
||||
WINPR_ATTR_NODISCARD BOOL wf_peer_unicode_keyboard_event(rdpInput* input, UINT16 flags,
|
||||
UINT16 code);
|
||||
WINPR_ATTR_NODISCARD BOOL wf_peer_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y);
|
||||
WINPR_ATTR_NODISCARD BOOL wf_peer_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x,
|
||||
UINT16 y);
|
||||
|
||||
// dummy versions
|
||||
WINPR_ATTR_NODISCARD BOOL wf_peer_keyboard_event_dummy(rdpInput* input, UINT16 flags, UINT8 code);
|
||||
WINPR_ATTR_NODISCARD BOOL wf_peer_unicode_keyboard_event_dummy(rdpInput* input, UINT16 flags,
|
||||
UINT16 code);
|
||||
WINPR_ATTR_NODISCARD BOOL wf_peer_mouse_event_dummy(rdpInput* input, UINT16 flags, UINT16 x,
|
||||
UINT16 y);
|
||||
WINPR_ATTR_NODISCARD BOOL wf_peer_extended_mouse_event_dummy(rdpInput* input, UINT16 flags,
|
||||
UINT16 x, UINT16 y);
|
||||
|
||||
#endif /* FREERDP_SERVER_WIN_INPUT_H */
|
||||
342
third_party/FreeRDP/server/Windows/wf_interface.c
vendored
Normal file
342
third_party/FreeRDP/server/Windows/wf_interface.c
vendored
Normal file
@@ -0,0 +1,342 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Windows Server
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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 <winpr/tchar.h>
|
||||
#include <winpr/windows.h>
|
||||
#include <winpr/winsock.h>
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/listener.h>
|
||||
#include <freerdp/constants.h>
|
||||
#include <freerdp/channels/wtsvc.h>
|
||||
#include <freerdp/channels/channels.h>
|
||||
#include <freerdp/build-config.h>
|
||||
|
||||
#include "wf_peer.h"
|
||||
#include "wf_settings.h"
|
||||
#include "wf_info.h"
|
||||
|
||||
#include "wf_interface.h"
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("windows")
|
||||
|
||||
#define SERVER_KEY "Software\\" FREERDP_VENDOR_STRING "\\" FREERDP_PRODUCT_STRING "\\Server"
|
||||
|
||||
static cbCallback cbEvent = nullptr;
|
||||
|
||||
int get_screen_info(int id, _TCHAR* name, size_t length, int* width, int* height, int* bpp)
|
||||
{
|
||||
DISPLAY_DEVICE dd = WINPR_C_ARRAY_INIT;
|
||||
|
||||
dd.cb = sizeof(DISPLAY_DEVICE);
|
||||
|
||||
if (EnumDisplayDevices(nullptr, id, &dd, 0) != 0)
|
||||
{
|
||||
HDC dc;
|
||||
|
||||
if (name != nullptr)
|
||||
_stprintf_s(name, length, _T("%s (%s)"), dd.DeviceName, dd.DeviceString);
|
||||
|
||||
dc = CreateDC(dd.DeviceName, nullptr, nullptr, nullptr);
|
||||
*width = GetDeviceCaps(dc, HORZRES);
|
||||
*height = GetDeviceCaps(dc, VERTRES);
|
||||
*bpp = GetDeviceCaps(dc, BITSPIXEL);
|
||||
// ReleaseDC(nullptr, dc);
|
||||
DeleteDC(dc);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void set_screen_id(int id)
|
||||
{
|
||||
wfInfo* wfi;
|
||||
|
||||
wfi = wf_info_get_instance();
|
||||
if (!wfi)
|
||||
return;
|
||||
wfi->screenID = id;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static DWORD WINAPI wf_server_main_loop(LPVOID lpParam)
|
||||
{
|
||||
freerdp_listener* instance;
|
||||
wfInfo* wfi;
|
||||
|
||||
wfi = wf_info_get_instance();
|
||||
if (!wfi)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get instance");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wfi->force_all_disconnect = FALSE;
|
||||
|
||||
instance = (freerdp_listener*)lpParam;
|
||||
WINPR_ASSERT(instance);
|
||||
WINPR_ASSERT(instance->GetEventHandles);
|
||||
WINPR_ASSERT(instance->CheckFileDescriptor);
|
||||
|
||||
while (wfi->force_all_disconnect == FALSE)
|
||||
{
|
||||
DWORD status;
|
||||
HANDLE handles[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
|
||||
DWORD count = instance->GetEventHandles(instance, handles, ARRAYSIZE(handles));
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get FreeRDP file descriptor");
|
||||
break;
|
||||
}
|
||||
|
||||
status = WaitForMultipleObjects(count, handles, FALSE, INFINITE);
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed");
|
||||
break;
|
||||
}
|
||||
|
||||
if (instance->CheckFileDescriptor(instance) != TRUE)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WLog_INFO(TAG, "wf_server_main_loop terminating");
|
||||
instance->Close(instance);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL wfreerdp_server_start(wfServer* server)
|
||||
{
|
||||
freerdp_listener* instance;
|
||||
|
||||
server->instance = freerdp_listener_new();
|
||||
server->instance->PeerAccepted = wf_peer_accepted;
|
||||
instance = server->instance;
|
||||
|
||||
wf_settings_read_dword(HKEY_LOCAL_MACHINE, SERVER_KEY, _T("DefaultPort"), &server->port);
|
||||
|
||||
if (!instance->Open(instance, nullptr, (UINT16)server->port))
|
||||
return FALSE;
|
||||
|
||||
if (!(server->thread =
|
||||
CreateThread(nullptr, 0, wf_server_main_loop, (void*)instance, 0, nullptr)))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL wfreerdp_server_stop(wfServer* server)
|
||||
{
|
||||
wfInfo* wfi;
|
||||
|
||||
wfi = wf_info_get_instance();
|
||||
if (!wfi)
|
||||
return FALSE;
|
||||
WLog_INFO(TAG, "Stopping server");
|
||||
wfi->force_all_disconnect = TRUE;
|
||||
server->instance->Close(server->instance);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
wfServer* wfreerdp_server_new()
|
||||
{
|
||||
WSADATA wsaData;
|
||||
wfServer* server;
|
||||
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
|
||||
return nullptr;
|
||||
|
||||
server = (wfServer*)calloc(1, sizeof(wfServer));
|
||||
|
||||
if (server)
|
||||
{
|
||||
server->port = 3389;
|
||||
}
|
||||
|
||||
WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi());
|
||||
|
||||
cbEvent = nullptr;
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
void wfreerdp_server_free(wfServer* server)
|
||||
{
|
||||
free(server);
|
||||
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
BOOL wfreerdp_server_is_running(wfServer* server)
|
||||
{
|
||||
DWORD tStatus;
|
||||
BOOL bRet;
|
||||
|
||||
bRet = GetExitCodeThread(server->thread, &tStatus);
|
||||
if (bRet == 0)
|
||||
{
|
||||
WLog_ERR(TAG, "Error in call to GetExitCodeThread");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (tStatus == STILL_ACTIVE)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
UINT32 wfreerdp_server_num_peers()
|
||||
{
|
||||
wfInfo* wfi;
|
||||
|
||||
wfi = wf_info_get_instance();
|
||||
if (!wfi)
|
||||
return -1;
|
||||
return wfi->peerCount;
|
||||
}
|
||||
|
||||
UINT32 wfreerdp_server_get_peer_hostname(int pId, wchar_t* dstStr)
|
||||
{
|
||||
wfInfo* wfi;
|
||||
freerdp_peer* peer;
|
||||
|
||||
wfi = wf_info_get_instance();
|
||||
if (!wfi)
|
||||
return 0;
|
||||
peer = wfi->peers[pId];
|
||||
|
||||
if (peer)
|
||||
{
|
||||
UINT32 sLen;
|
||||
|
||||
sLen = strnlen_s(peer->hostname, 50);
|
||||
swprintf(dstStr, 50, L"%hs", peer->hostname);
|
||||
return sLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
WLog_WARN(TAG, "nonexistent peer id=%d", pId);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL wfreerdp_server_peer_is_local(int pId)
|
||||
{
|
||||
wfInfo* wfi;
|
||||
freerdp_peer* peer;
|
||||
|
||||
wfi = wf_info_get_instance();
|
||||
if (!wfi)
|
||||
return FALSE;
|
||||
peer = wfi->peers[pId];
|
||||
|
||||
if (peer)
|
||||
{
|
||||
return peer->local;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL wfreerdp_server_peer_is_connected(int pId)
|
||||
{
|
||||
wfInfo* wfi;
|
||||
freerdp_peer* peer;
|
||||
|
||||
wfi = wf_info_get_instance();
|
||||
if (!wfi)
|
||||
return FALSE;
|
||||
peer = wfi->peers[pId];
|
||||
|
||||
if (peer)
|
||||
{
|
||||
return peer->connected;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL wfreerdp_server_peer_is_activated(int pId)
|
||||
{
|
||||
wfInfo* wfi;
|
||||
freerdp_peer* peer;
|
||||
|
||||
wfi = wf_info_get_instance();
|
||||
if (!wfi)
|
||||
return FALSE;
|
||||
peer = wfi->peers[pId];
|
||||
|
||||
if (peer)
|
||||
{
|
||||
return peer->activated;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL wfreerdp_server_peer_is_authenticated(int pId)
|
||||
{
|
||||
wfInfo* wfi;
|
||||
freerdp_peer* peer;
|
||||
|
||||
wfi = wf_info_get_instance();
|
||||
if (!wfi)
|
||||
return FALSE;
|
||||
peer = wfi->peers[pId];
|
||||
|
||||
if (peer)
|
||||
{
|
||||
return peer->authenticated;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void wfreerdp_server_register_callback_event(cbCallback cb)
|
||||
{
|
||||
cbEvent = cb;
|
||||
}
|
||||
|
||||
void wfreerdp_server_peer_callback_event(int pId, UINT32 eType)
|
||||
{
|
||||
if (cbEvent)
|
||||
cbEvent(pId, eType);
|
||||
}
|
||||
141
third_party/FreeRDP/server/Windows/wf_interface.h
vendored
Normal file
141
third_party/FreeRDP/server/Windows/wf_interface.h
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Client
|
||||
* FreeRDP Windows Server
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_WIN_INTERFACE_H
|
||||
#define FREERDP_SERVER_WIN_INTERFACE_H
|
||||
|
||||
#include <winpr/windows.h>
|
||||
|
||||
#include <freerdp/api.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/listener.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/codec/rfx.h>
|
||||
|
||||
#include <freerdp/server/rdpsnd.h>
|
||||
|
||||
#if _WIN32_WINNT >= 0x0602
|
||||
#define WITH_DXGI_1_2 1
|
||||
#endif
|
||||
|
||||
#define FREERDP_SERVER_WIN_SRV_CALLBACK_EVENT_CONNECT 1
|
||||
#define FREERDP_SERVER_WIN_SRV_CALLBACK_EVENT_DISCONNECT 2
|
||||
#define FREERDP_SERVER_WIN_SRV_CALLBACK_EVENT_ACTIVATE 4
|
||||
#define FREERDP_SERVER_WIN_SRV_CALLBACK_EVENT_AUTH 8
|
||||
|
||||
typedef struct wf_info wfInfo;
|
||||
typedef struct wf_peer_context wfPeerContext;
|
||||
|
||||
struct wf_info
|
||||
{
|
||||
wStream* s;
|
||||
|
||||
// screen and monitor info
|
||||
int screenID;
|
||||
int virtscreen_width;
|
||||
int virtscreen_height;
|
||||
int servscreen_width;
|
||||
int servscreen_height;
|
||||
int servscreen_xoffset;
|
||||
int servscreen_yoffset;
|
||||
|
||||
int frame_idx;
|
||||
int bitsPerPixel;
|
||||
HDC driverDC;
|
||||
int peerCount;
|
||||
int activePeerCount;
|
||||
void* changeBuffer;
|
||||
int framesPerSecond;
|
||||
LPTSTR deviceKey;
|
||||
TCHAR deviceName[32];
|
||||
freerdp_peer** peers;
|
||||
BOOL mirrorDriverActive;
|
||||
UINT framesWaiting;
|
||||
|
||||
HANDLE snd_mutex;
|
||||
BOOL snd_stop;
|
||||
AUDIO_FORMAT* agreed_format;
|
||||
|
||||
RECT invalid;
|
||||
HANDLE mutex;
|
||||
BOOL updatePending;
|
||||
HANDLE updateEvent;
|
||||
HANDLE updateThread;
|
||||
HANDLE updateSemaphore;
|
||||
RFX_CONTEXT* rfx_context;
|
||||
unsigned long lastUpdate;
|
||||
unsigned long nextUpdate;
|
||||
SURFACE_BITS_COMMAND cmd;
|
||||
|
||||
BOOL input_disabled;
|
||||
BOOL force_all_disconnect;
|
||||
};
|
||||
|
||||
struct wf_peer_context
|
||||
{
|
||||
rdpContext _p;
|
||||
|
||||
wfInfo* info;
|
||||
int frame_idx;
|
||||
HANDLE updateEvent;
|
||||
BOOL socketClose;
|
||||
HANDLE socketEvent;
|
||||
HANDLE socketThread;
|
||||
HANDLE socketSemaphore;
|
||||
|
||||
HANDLE vcm;
|
||||
RdpsndServerContext* rdpsnd;
|
||||
};
|
||||
|
||||
struct wf_server
|
||||
{
|
||||
DWORD port;
|
||||
HANDLE thread;
|
||||
freerdp_listener* instance;
|
||||
};
|
||||
typedef struct wf_server wfServer;
|
||||
|
||||
typedef void(__stdcall* cbCallback)(int, UINT32);
|
||||
|
||||
FREERDP_API WINPR_ATTR_NODISCARD int get_screen_info(int id, _TCHAR* name, size_t length, int* w,
|
||||
int* h, int* b);
|
||||
FREERDP_API void set_screen_id(int id);
|
||||
|
||||
FREERDP_API WINPR_ATTR_NODISCARD BOOL wfreerdp_server_start(wfServer* server);
|
||||
FREERDP_API WINPR_ATTR_NODISCARD BOOL wfreerdp_server_stop(wfServer* server);
|
||||
|
||||
FREERDP_API WINPR_ATTR_NODISCARD wfServer* wfreerdp_server_new(void);
|
||||
FREERDP_API void wfreerdp_server_free(wfServer* server);
|
||||
|
||||
FREERDP_API WINPR_ATTR_NODISCARD BOOL wfreerdp_server_is_running(wfServer* server);
|
||||
|
||||
FREERDP_API WINPR_ATTR_NODISCARD UINT32 wfreerdp_server_num_peers(void);
|
||||
FREERDP_API WINPR_ATTR_NODISCARD UINT32 wfreerdp_server_get_peer_hostname(int pId, wchar_t* dstStr);
|
||||
FREERDP_API WINPR_ATTR_NODISCARD BOOL wfreerdp_server_peer_is_local(int pId);
|
||||
FREERDP_API WINPR_ATTR_NODISCARD BOOL wfreerdp_server_peer_is_connected(int pId);
|
||||
FREERDP_API WINPR_ATTR_NODISCARD BOOL wfreerdp_server_peer_is_activated(int pId);
|
||||
FREERDP_API WINPR_ATTR_NODISCARD BOOL wfreerdp_server_peer_is_authenticated(int pId);
|
||||
|
||||
FREERDP_API void wfreerdp_server_register_callback_event(cbCallback cb);
|
||||
|
||||
void wfreerdp_server_peer_callback_event(int pId, UINT32 eType);
|
||||
|
||||
#endif /* FREERDP_SERVER_WIN_INTERFACE_H */
|
||||
362
third_party/FreeRDP/server/Windows/wf_mirage.c
vendored
Normal file
362
third_party/FreeRDP/server/Windows/wf_mirage.c
vendored
Normal file
@@ -0,0 +1,362 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Windows Server
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2012-2013 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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 <winpr/tchar.h>
|
||||
#include <winpr/windows.h>
|
||||
|
||||
#include "wf_mirage.h"
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("Windows.mirror")
|
||||
|
||||
#define DEVICE_KEY_PREFIX _T("\\Registry\\Machine\\")
|
||||
/*
|
||||
This function will iterate over the loaded display devices until it finds
|
||||
the mirror device we want to load. If found, it will then copy the registry
|
||||
key corresponding to the device to the wfi and returns TRUE. Otherwise
|
||||
the function returns FALSE.
|
||||
*/
|
||||
BOOL wf_mirror_driver_find_display_device(wfInfo* wfi)
|
||||
{
|
||||
BOOL result;
|
||||
BOOL devFound;
|
||||
DWORD deviceNumber;
|
||||
DISPLAY_DEVICE deviceInfo;
|
||||
devFound = FALSE;
|
||||
deviceNumber = 0;
|
||||
deviceInfo.cb = sizeof(deviceInfo);
|
||||
|
||||
while (result = EnumDisplayDevices(nullptr, deviceNumber, &deviceInfo, 0))
|
||||
{
|
||||
if (_tcscmp(deviceInfo.DeviceString, _T("Mirage Driver")) == 0)
|
||||
{
|
||||
int deviceKeyLength;
|
||||
int deviceKeyPrefixLength;
|
||||
deviceKeyPrefixLength = _tcslen(DEVICE_KEY_PREFIX);
|
||||
|
||||
if (_tcsnicmp(deviceInfo.DeviceKey, DEVICE_KEY_PREFIX, deviceKeyPrefixLength) == 0)
|
||||
{
|
||||
deviceKeyLength = _tcslen(deviceInfo.DeviceKey) - deviceKeyPrefixLength;
|
||||
wfi->deviceKey = (LPTSTR)malloc((deviceKeyLength + 1) * sizeof(TCHAR));
|
||||
|
||||
if (!wfi->deviceKey)
|
||||
return FALSE;
|
||||
|
||||
_tcsncpy_s(wfi->deviceKey, deviceKeyLength + 1,
|
||||
&deviceInfo.DeviceKey[deviceKeyPrefixLength], deviceKeyLength);
|
||||
}
|
||||
|
||||
_tcsncpy_s(wfi->deviceName, 32, deviceInfo.DeviceName, _tcslen(deviceInfo.DeviceName));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
deviceNumber++;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will attempt to access the the windows registry using the device
|
||||
* key stored in the current wfi. It will attempt to read the value of the
|
||||
* "Attach.ToDesktop" subkey and will return TRUE if the value is already set to
|
||||
* val. If unable to read the subkey, this function will return FALSE. If the
|
||||
* subkey is not set to val it will then attempt to set it to val and return TRUE. If
|
||||
* unsuccessful or an unexpected value is encountered, the function returns
|
||||
* FALSE.
|
||||
*/
|
||||
|
||||
BOOL wf_mirror_driver_display_device_attach(wfInfo* wfi, DWORD mode)
|
||||
{
|
||||
HKEY hKey;
|
||||
LONG status;
|
||||
DWORD dwType;
|
||||
DWORD dwSize;
|
||||
DWORD dwValue;
|
||||
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, wfi->deviceKey, 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY,
|
||||
&hKey);
|
||||
|
||||
if (status != ERROR_SUCCESS)
|
||||
{
|
||||
WLog_DBG(TAG, "Error opening RegKey: status=0x%08lX", status);
|
||||
|
||||
if (status == ERROR_ACCESS_DENIED)
|
||||
WLog_DBG(TAG, "access denied. Do you have admin privileges?");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
dwSize = sizeof(DWORD);
|
||||
status =
|
||||
RegQueryValueEx(hKey, _T("Attach.ToDesktop"), nullptr, &dwType, (BYTE*)&dwValue, &dwSize);
|
||||
|
||||
if (status != ERROR_SUCCESS)
|
||||
{
|
||||
WLog_DBG(TAG, "Error querying RegKey: status=0x%08lX", status);
|
||||
|
||||
if (status == ERROR_ACCESS_DENIED)
|
||||
WLog_DBG(TAG, "access denied. Do you have admin privileges?");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (dwValue ^ mode) // only if we want to change modes
|
||||
{
|
||||
dwValue = mode;
|
||||
dwSize = sizeof(DWORD);
|
||||
status = RegSetValueEx(hKey, _T("Attach.ToDesktop"), 0, REG_DWORD, (BYTE*)&dwValue, dwSize);
|
||||
|
||||
if (status != ERROR_SUCCESS)
|
||||
{
|
||||
WLog_DBG(TAG, "Error writing registry key: %ld", status);
|
||||
|
||||
if (status == ERROR_ACCESS_DENIED)
|
||||
WLog_DBG(TAG, "access denied. Do you have admin privileges?");
|
||||
|
||||
WLog_DBG(TAG, "");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void wf_mirror_driver_print_display_change_status(LONG status)
|
||||
{
|
||||
TCHAR disp_change[64];
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case DISP_CHANGE_SUCCESSFUL:
|
||||
_tcscpy(disp_change, _T("DISP_CHANGE_SUCCESSFUL"));
|
||||
break;
|
||||
|
||||
case DISP_CHANGE_BADDUALVIEW:
|
||||
_tcscpy(disp_change, _T("DISP_CHANGE_BADDUALVIEW"));
|
||||
break;
|
||||
|
||||
case DISP_CHANGE_BADFLAGS:
|
||||
_tcscpy(disp_change, _T("DISP_CHANGE_BADFLAGS"));
|
||||
break;
|
||||
|
||||
case DISP_CHANGE_BADMODE:
|
||||
_tcscpy(disp_change, _T("DISP_CHANGE_BADMODE"));
|
||||
break;
|
||||
|
||||
case DISP_CHANGE_BADPARAM:
|
||||
_tcscpy(disp_change, _T("DISP_CHANGE_BADPARAM"));
|
||||
break;
|
||||
|
||||
case DISP_CHANGE_FAILED:
|
||||
_tcscpy(disp_change, _T("DISP_CHANGE_FAILED"));
|
||||
break;
|
||||
|
||||
case DISP_CHANGE_NOTUPDATED:
|
||||
_tcscpy(disp_change, _T("DISP_CHANGE_NOTUPDATED"));
|
||||
break;
|
||||
|
||||
case DISP_CHANGE_RESTART:
|
||||
_tcscpy(disp_change, _T("DISP_CHANGE_RESTART"));
|
||||
break;
|
||||
|
||||
default:
|
||||
_tcscpy(disp_change, _T("DISP_CHANGE_UNKNOWN"));
|
||||
break;
|
||||
}
|
||||
|
||||
if (status != DISP_CHANGE_SUCCESSFUL)
|
||||
WLog_ERR(TAG, "ChangeDisplaySettingsEx() failed with %s (%ld)", disp_change, status);
|
||||
else
|
||||
WLog_INFO(TAG, "ChangeDisplaySettingsEx() succeeded with %s (%ld)", disp_change, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will attempt to apply the currently configured display settings
|
||||
* in the registry to the display driver. It will return TRUE if successful
|
||||
* otherwise it returns FALSE.
|
||||
* If mode is MIRROR_UNLOAD then the the driver will be asked to remove itself.
|
||||
*/
|
||||
|
||||
BOOL wf_mirror_driver_update(wfInfo* wfi, int mode)
|
||||
{
|
||||
BOOL status;
|
||||
DWORD* extHdr;
|
||||
WORD drvExtraSaved;
|
||||
DEVMODE* deviceMode;
|
||||
LONG disp_change_status;
|
||||
DWORD dmf_devmodewext_magic_sig = 0xDF20C0DE;
|
||||
|
||||
if ((mode != MIRROR_LOAD) && (mode != MIRROR_UNLOAD))
|
||||
{
|
||||
WLog_DBG(TAG, "Invalid mirror mode!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
deviceMode = (DEVMODE*)malloc(sizeof(DEVMODE) + EXT_DEVMODE_SIZE_MAX);
|
||||
|
||||
if (!deviceMode)
|
||||
return FALSE;
|
||||
|
||||
deviceMode->dmDriverExtra = 2 * sizeof(DWORD);
|
||||
extHdr = (DWORD*)((BYTE*)&deviceMode + sizeof(DEVMODE));
|
||||
extHdr[0] = dmf_devmodewext_magic_sig;
|
||||
extHdr[1] = 0;
|
||||
drvExtraSaved = deviceMode->dmDriverExtra;
|
||||
memset(deviceMode, 0, sizeof(DEVMODE) + EXT_DEVMODE_SIZE_MAX);
|
||||
deviceMode->dmSize = sizeof(DEVMODE);
|
||||
deviceMode->dmDriverExtra = drvExtraSaved;
|
||||
|
||||
if (mode == MIRROR_LOAD)
|
||||
{
|
||||
wfi->virtscreen_width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
||||
wfi->virtscreen_height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
||||
deviceMode->dmPelsWidth = wfi->virtscreen_width;
|
||||
deviceMode->dmPelsHeight = wfi->virtscreen_height;
|
||||
deviceMode->dmBitsPerPel = wfi->bitsPerPixel;
|
||||
deviceMode->u.s2.dmPosition.x = wfi->servscreen_xoffset;
|
||||
deviceMode->u.s2.dmPosition.y = wfi->servscreen_yoffset;
|
||||
}
|
||||
|
||||
deviceMode->dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_POSITION;
|
||||
_tcsncpy_s(deviceMode->dmDeviceName, 32, wfi->deviceName, _tcslen(wfi->deviceName));
|
||||
disp_change_status =
|
||||
ChangeDisplaySettingsEx(wfi->deviceName, deviceMode, nullptr, CDS_UPDATEREGISTRY, nullptr);
|
||||
status = (disp_change_status == DISP_CHANGE_SUCCESSFUL) ? TRUE : FALSE;
|
||||
|
||||
if (!status)
|
||||
wf_mirror_driver_print_display_change_status(disp_change_status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
BOOL wf_mirror_driver_map_memory(wfInfo* wfi)
|
||||
{
|
||||
int status;
|
||||
wfi->driverDC = CreateDC(wfi->deviceName, nullptr, nullptr, nullptr);
|
||||
|
||||
if (wfi->driverDC == nullptr)
|
||||
{
|
||||
WLog_ERR(TAG, "Could not create device driver context!");
|
||||
{
|
||||
LPVOID lpMsgBuf;
|
||||
DWORD dw = GetLastError();
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf,
|
||||
0, nullptr);
|
||||
// Display the error message and exit the process
|
||||
WLog_ERR(TAG, "CreateDC failed on device [%s] with error %lu: %s", wfi->deviceName, dw,
|
||||
lpMsgBuf);
|
||||
LocalFree(lpMsgBuf);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
wfi->changeBuffer = calloc(1, sizeof(GETCHANGESBUF));
|
||||
|
||||
if (!wfi->changeBuffer)
|
||||
return FALSE;
|
||||
|
||||
status = ExtEscape(wfi->driverDC, dmf_esc_usm_pipe_map, 0, 0, sizeof(GETCHANGESBUF),
|
||||
(LPSTR)wfi->changeBuffer);
|
||||
|
||||
if (status <= 0)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to map shared memory from the driver! code %d", status);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Unmap the shared memory and release the DC */
|
||||
|
||||
BOOL wf_mirror_driver_cleanup(wfInfo* wfi)
|
||||
{
|
||||
int status;
|
||||
status = ExtEscape(wfi->driverDC, dmf_esc_usm_pipe_unmap, sizeof(GETCHANGESBUF),
|
||||
(LPSTR)wfi->changeBuffer, 0, 0);
|
||||
|
||||
if (status <= 0)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to unmap shared memory from the driver! code %d", status);
|
||||
}
|
||||
|
||||
if (wfi->driverDC != nullptr)
|
||||
{
|
||||
status = DeleteDC(wfi->driverDC);
|
||||
|
||||
if (status == 0)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to release DC!");
|
||||
}
|
||||
}
|
||||
|
||||
free(wfi->changeBuffer);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL wf_mirror_driver_activate(wfInfo* wfi)
|
||||
{
|
||||
if (!wfi->mirrorDriverActive)
|
||||
{
|
||||
WLog_DBG(TAG, "Activating Mirror Driver");
|
||||
|
||||
if (wf_mirror_driver_find_display_device(wfi) == FALSE)
|
||||
{
|
||||
WLog_DBG(TAG, "Could not find dfmirage mirror driver! Is it installed?");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (wf_mirror_driver_display_device_attach(wfi, 1) == FALSE)
|
||||
{
|
||||
WLog_DBG(TAG, "Could not attach display device!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (wf_mirror_driver_update(wfi, MIRROR_LOAD) == FALSE)
|
||||
{
|
||||
WLog_DBG(TAG, "could not update system with new display settings!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (wf_mirror_driver_map_memory(wfi) == FALSE)
|
||||
{
|
||||
WLog_DBG(TAG, "Unable to map memory for mirror driver!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
wfi->mirrorDriverActive = TRUE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void wf_mirror_driver_deactivate(wfInfo* wfi)
|
||||
{
|
||||
if (wfi->mirrorDriverActive)
|
||||
{
|
||||
WLog_DBG(TAG, "Deactivating Mirror Driver");
|
||||
wf_mirror_driver_cleanup(wfi);
|
||||
wf_mirror_driver_display_device_attach(wfi, 0);
|
||||
wf_mirror_driver_update(wfi, MIRROR_UNLOAD);
|
||||
wfi->mirrorDriverActive = FALSE;
|
||||
}
|
||||
}
|
||||
219
third_party/FreeRDP/server/Windows/wf_mirage.h
vendored
Normal file
219
third_party/FreeRDP/server/Windows/wf_mirage.h
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Windows Server
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2012-2013 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_WIN_MIRAGE_H
|
||||
#define FREERDP_SERVER_WIN_MIRAGE_H
|
||||
|
||||
#include "wf_interface.h"
|
||||
|
||||
enum
|
||||
{
|
||||
MIRROR_LOAD = 0,
|
||||
MIRROR_UNLOAD = 1
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
DMF_ESCAPE_BASE_1_VB = 1030,
|
||||
DMF_ESCAPE_BASE_2_VB = 1026,
|
||||
DMF_ESCAPE_BASE_3_VB = 24
|
||||
};
|
||||
|
||||
#ifdef _WIN64
|
||||
|
||||
#define CLIENT_64BIT 0x8000
|
||||
|
||||
enum
|
||||
{
|
||||
DMF_ESCAPE_BASE_1 = CLIENT_64BIT | DMF_ESCAPE_BASE_1_VB,
|
||||
DMF_ESCAPE_BASE_2 = CLIENT_64BIT | DMF_ESCAPE_BASE_2_VB,
|
||||
DMF_ESCAPE_BASE_3 = CLIENT_64BIT | DMF_ESCAPE_BASE_3_VB,
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
enum
|
||||
{
|
||||
DMF_ESCAPE_BASE_1 = DMF_ESCAPE_BASE_1_VB,
|
||||
DMF_ESCAPE_BASE_2 = DMF_ESCAPE_BASE_2_VB,
|
||||
DMF_ESCAPE_BASE_3 = DMF_ESCAPE_BASE_3_VB,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
typedef enum
|
||||
{
|
||||
dmf_esc_qry_ver_info = DMF_ESCAPE_BASE_2 + 0,
|
||||
dmf_esc_usm_pipe_map = DMF_ESCAPE_BASE_1 + 0,
|
||||
dmf_esc_usm_pipe_unmap = DMF_ESCAPE_BASE_1 + 1,
|
||||
dmf_esc_test = DMF_ESCAPE_BASE_1 + 20,
|
||||
dmf_esc_usm_pipe_mapping_test = DMF_ESCAPE_BASE_1 + 21,
|
||||
dmf_esc_pointer_shape_get = DMF_ESCAPE_BASE_3,
|
||||
|
||||
} dmf_escape;
|
||||
|
||||
#define CLIP_LIMIT 50
|
||||
#define MAXCHANGES_BUF 20000
|
||||
|
||||
typedef enum
|
||||
{
|
||||
dmf_dfo_IGNORE = 0,
|
||||
dmf_dfo_FROM_SCREEN = 1,
|
||||
dmf_dfo_FROM_DIB = 2,
|
||||
dmf_dfo_TO_SCREEN = 3,
|
||||
dmf_dfo_SCREEN_SCREEN = 11,
|
||||
dmf_dfo_BLIT = 12,
|
||||
dmf_dfo_SOLIDFILL = 13,
|
||||
dmf_dfo_BLEND = 14,
|
||||
dmf_dfo_TRANS = 15,
|
||||
dmf_dfo_PLG = 17,
|
||||
dmf_dfo_TEXTOUT = 18,
|
||||
dmf_dfo_Ptr_Shape = 19,
|
||||
dmf_dfo_Ptr_Engage = 48,
|
||||
dmf_dfo_Ptr_Avert = 49,
|
||||
dmf_dfn_assert_on = 64,
|
||||
dmf_dfn_assert_off = 65,
|
||||
} dmf_UpdEvent;
|
||||
|
||||
#define NOCACHE 1
|
||||
#define OLDCACHE 2
|
||||
#define NEWCACHE 3
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ULONG type;
|
||||
RECT rect;
|
||||
#ifndef DFMIRAGE_LEAN
|
||||
RECT origrect;
|
||||
POINT point;
|
||||
ULONG color;
|
||||
ULONG refcolor;
|
||||
#endif
|
||||
} CHANGES_RECORD;
|
||||
|
||||
typedef CHANGES_RECORD* PCHANGES_RECORD;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ULONG counter;
|
||||
CHANGES_RECORD pointrect[MAXCHANGES_BUF];
|
||||
} CHANGES_BUF;
|
||||
|
||||
#define EXT_DEVMODE_SIZE_MAX 3072
|
||||
#define DMF_PIPE_SEC_SIZE_DEFAULT ALIGN64K(sizeof(CHANGES_BUF))
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CHANGES_BUF* buffer;
|
||||
PVOID Userbuffer;
|
||||
} GETCHANGESBUF;
|
||||
|
||||
#define dmf_sprb_ERRORMASK 0x07FF
|
||||
#define dmf_sprb_STRICTSESSION_AFF 0x1FFF
|
||||
|
||||
typedef enum
|
||||
{
|
||||
dmf_sprb_internal_error = 0x0001,
|
||||
dmf_sprb_miniport_gen_error = 0x0004,
|
||||
dmf_sprb_memory_alloc_failed = 0x0008,
|
||||
dmf_sprb_pipe_buff_overflow = 0x0010,
|
||||
dmf_sprb_pipe_buff_insufficient = 0x0020,
|
||||
dmf_sprb_pipe_not_ready = 0x0040,
|
||||
dmf_sprb_gdi_err = 0x0100,
|
||||
dmf_sprb_owner_died = 0x0400,
|
||||
dmf_sprb_tgtwnd_gone = 0x0800,
|
||||
dmf_sprb_pdev_detached = 0x2000,
|
||||
} dmf_session_prob_status;
|
||||
|
||||
#define DMF_ESC_RET_FAILF 0x80000000
|
||||
#define DMF_ESC_RET_SSTMASK 0x0000FFFF
|
||||
#define DMF_ESC_RET_IMMMASK 0x7FFF0000
|
||||
|
||||
typedef enum
|
||||
{
|
||||
dmf_escret_generic_ok = 0x00010000,
|
||||
dmf_escret_bad_state = 0x00100000,
|
||||
dmf_escret_access_denied = 0x00200000,
|
||||
dmf_escret_bad_buffer_size = 0x00400000,
|
||||
dmf_escret_internal_err = 0x00800000,
|
||||
dmf_escret_out_of_memory = 0x02000000,
|
||||
dmf_escret_already_connected = 0x04000000,
|
||||
dmf_escret_oh_boy_too_late = 0x08000000,
|
||||
dmf_escret_bad_window = 0x10000000,
|
||||
dmf_escret_drv_ver_higher = 0x20000000,
|
||||
dmf_escret_drv_ver_lower = 0x40000000,
|
||||
} dmf_esc_retcode;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ULONG cbSize;
|
||||
ULONG app_actual_version;
|
||||
ULONG display_minreq_version;
|
||||
ULONG connect_options;
|
||||
} Esc_dmf_Qvi_IN;
|
||||
|
||||
enum
|
||||
{
|
||||
esc_qvi_prod_name_max = 16,
|
||||
};
|
||||
|
||||
#define ESC_QVI_PROD_MIRAGE "MIRAGE"
|
||||
#define ESC_QVI_PROD_QUASAR "QUASAR"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ULONG cbSize;
|
||||
ULONG display_actual_version;
|
||||
ULONG miniport_actual_version;
|
||||
ULONG app_minreq_version;
|
||||
ULONG display_buildno;
|
||||
ULONG miniport_buildno;
|
||||
char prod_name[esc_qvi_prod_name_max];
|
||||
} Esc_dmf_Qvi_OUT;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ULONG cbSize;
|
||||
char* pDstBmBuf;
|
||||
ULONG nDstBmBufSize;
|
||||
} Esc_dmf_pointer_shape_get_IN;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ULONG cbSize;
|
||||
POINTL BmSize;
|
||||
char* pMaskBm;
|
||||
ULONG nMaskBmSize;
|
||||
char* pColorBm;
|
||||
ULONG nColorBmSize;
|
||||
char* pColorBmPal;
|
||||
ULONG nColorBmPalEntries;
|
||||
} Esc_dmf_pointer_shape_get_OUT;
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL wf_mirror_driver_find_display_device(wfInfo* wfi);
|
||||
WINPR_ATTR_NODISCARD BOOL wf_mirror_driver_display_device_attach(wfInfo* wfi, DWORD mode);
|
||||
WINPR_ATTR_NODISCARD BOOL wf_mirror_driver_update(wfInfo* wfi, int mode);
|
||||
WINPR_ATTR_NODISCARD BOOL wf_mirror_driver_map_memory(wfInfo* wfi);
|
||||
WINPR_ATTR_NODISCARD BOOL wf_mirror_driver_cleanup(wfInfo* wfi);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL wf_mirror_driver_activate(wfInfo* wfi);
|
||||
void wf_mirror_driver_deactivate(wfInfo* wfi);
|
||||
|
||||
#endif /* FREERDP_SERVER_WIN_MIRAGE_H */
|
||||
415
third_party/FreeRDP/server/Windows/wf_peer.c
vendored
Normal file
415
third_party/FreeRDP/server/Windows/wf_peer.c
vendored
Normal file
@@ -0,0 +1,415 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Client
|
||||
* FreeRDP Windows Server
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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 <winpr/assert.h>
|
||||
#include <winpr/tchar.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/windows.h>
|
||||
|
||||
#include <freerdp/listener.h>
|
||||
#include <freerdp/codec/rfx.h>
|
||||
#include <freerdp/build-config.h>
|
||||
#include <freerdp/crypto/certificate.h>
|
||||
|
||||
#include "wf_info.h"
|
||||
#include "wf_input.h"
|
||||
#include "wf_mirage.h"
|
||||
#include "wf_update.h"
|
||||
#include "wf_settings.h"
|
||||
#include "wf_rdpsnd.h"
|
||||
|
||||
#include "wf_peer.h"
|
||||
#include <freerdp/peer.h>
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("windows")
|
||||
|
||||
#define SERVER_KEY "Software\\" FREERDP_VENDOR_STRING "\\" FREERDP_PRODUCT_STRING
|
||||
|
||||
static DWORD WINAPI wf_peer_main_loop(LPVOID lpParam);
|
||||
|
||||
static BOOL wf_peer_context_new(freerdp_peer* client, rdpContext* ctx)
|
||||
{
|
||||
wfPeerContext* context = (wfPeerContext*)ctx;
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (!(context->info = wf_info_get_instance()))
|
||||
return FALSE;
|
||||
|
||||
context->vcm = WTSOpenServerA((LPSTR)client->context);
|
||||
|
||||
if (!context->vcm || context->vcm == INVALID_HANDLE_VALUE)
|
||||
return FALSE;
|
||||
|
||||
if (!wf_info_peer_register(context->info, context))
|
||||
{
|
||||
WTSCloseServer(context->vcm);
|
||||
context->vcm = nullptr;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void wf_peer_context_free(freerdp_peer* client, rdpContext* ctx)
|
||||
{
|
||||
wfPeerContext* context = (wfPeerContext*)ctx;
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
wf_info_peer_unregister(context->info, context);
|
||||
|
||||
if (context->rdpsnd)
|
||||
{
|
||||
wf_rdpsnd_lock();
|
||||
context->info->snd_stop = TRUE;
|
||||
rdpsnd_server_context_free(context->rdpsnd);
|
||||
wf_rdpsnd_unlock();
|
||||
}
|
||||
|
||||
WTSCloseServer(context->vcm);
|
||||
}
|
||||
|
||||
static BOOL wf_peer_init(freerdp_peer* client)
|
||||
{
|
||||
client->ContextSize = sizeof(wfPeerContext);
|
||||
client->ContextNew = wf_peer_context_new;
|
||||
client->ContextFree = wf_peer_context_free;
|
||||
return freerdp_peer_context_new(client);
|
||||
}
|
||||
|
||||
static BOOL wf_peer_post_connect(freerdp_peer* client)
|
||||
{
|
||||
wfInfo* wfi;
|
||||
rdpSettings* settings;
|
||||
wfPeerContext* context;
|
||||
|
||||
WINPR_ASSERT(client);
|
||||
|
||||
context = (wfPeerContext*)client->context;
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
wfi = context->info;
|
||||
WINPR_ASSERT(wfi);
|
||||
|
||||
settings = client->context->settings;
|
||||
WINPR_ASSERT(settings);
|
||||
|
||||
if ((get_screen_info(wfi->screenID, nullptr, 0, &wfi->servscreen_width, &wfi->servscreen_height,
|
||||
&wfi->bitsPerPixel) == 0) ||
|
||||
(wfi->servscreen_width == 0) || (wfi->servscreen_height == 0) || (wfi->bitsPerPixel == 0))
|
||||
{
|
||||
WLog_ERR(TAG, "postconnect: error getting screen info for screen %d", wfi->screenID);
|
||||
WLog_ERR(TAG, "\t%dx%dx%d", wfi->servscreen_height, wfi->servscreen_width,
|
||||
wfi->bitsPerPixel);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) != wfi->servscreen_width) ||
|
||||
(freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) != wfi->servscreen_height))
|
||||
{
|
||||
/*
|
||||
WLog_DBG(TAG, "Client requested resolution %"PRIu32"x%"PRIu32", but will resize to %dx%d",
|
||||
freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
|
||||
freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight), wfi->servscreen_width,
|
||||
wfi->servscreen_height);
|
||||
*/
|
||||
if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, wfi->servscreen_width) ||
|
||||
!freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, wfi->servscreen_height) ||
|
||||
!freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth, wfi->bitsPerPixel))
|
||||
return FALSE;
|
||||
|
||||
WINPR_ASSERT(client->context->update);
|
||||
WINPR_ASSERT(client->context->update->DesktopResize);
|
||||
client->context->update->DesktopResize(client->context);
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelManagerIsChannelJoined(context->vcm, "rdpsnd"))
|
||||
{
|
||||
wf_peer_rdpsnd_init(context); /* Audio Output */
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL wf_peer_activate(freerdp_peer* client)
|
||||
{
|
||||
wfInfo* wfi;
|
||||
wfPeerContext* context = (wfPeerContext*)client->context;
|
||||
wfi = context->info;
|
||||
client->activated = TRUE;
|
||||
wf_update_peer_activate(wfi, context);
|
||||
wfreerdp_server_peer_callback_event(((rdpContext*)context)->peer->pId,
|
||||
FREERDP_SERVER_WIN_SRV_CALLBACK_EVENT_ACTIVATE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL wf_peer_logon(freerdp_peer* client, const SEC_WINNT_AUTH_IDENTITY* identity,
|
||||
BOOL automatic)
|
||||
{
|
||||
wfreerdp_server_peer_callback_event(((rdpContext*)client->context)->peer->pId,
|
||||
FREERDP_SERVER_WIN_SRV_CALLBACK_EVENT_AUTH);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL wf_peer_synchronize_event(rdpInput* input, UINT32 flags)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL wf_peer_accepted(freerdp_listener* instance, freerdp_peer* client)
|
||||
{
|
||||
HANDLE hThread;
|
||||
|
||||
if (!(hThread = CreateThread(nullptr, 0, wf_peer_main_loop, client, 0, nullptr)))
|
||||
return FALSE;
|
||||
|
||||
(void)CloseHandle(hThread);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static DWORD WINAPI wf_peer_socket_listener(LPVOID lpParam)
|
||||
{
|
||||
wfPeerContext* context;
|
||||
freerdp_peer* client = (freerdp_peer*)lpParam;
|
||||
|
||||
WINPR_ASSERT(client);
|
||||
WINPR_ASSERT(client->GetEventHandles);
|
||||
WINPR_ASSERT(client->CheckFileDescriptor);
|
||||
|
||||
context = (wfPeerContext*)client->context;
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
while (1)
|
||||
{
|
||||
DWORD status;
|
||||
HANDLE handles[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
|
||||
DWORD count = client->GetEventHandles(client, handles, ARRAYSIZE(handles));
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get FreeRDP file descriptor");
|
||||
break;
|
||||
}
|
||||
|
||||
status = WaitForMultipleObjects(count, handles, FALSE, INFINITE);
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed");
|
||||
break;
|
||||
}
|
||||
|
||||
(void)SetEvent(context->socketEvent);
|
||||
(void)WaitForSingleObject(context->socketSemaphore, INFINITE);
|
||||
|
||||
if (context->socketClose)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL wf_peer_read_settings(freerdp_peer* client)
|
||||
{
|
||||
rdpSettings* settings;
|
||||
|
||||
WINPR_ASSERT(client);
|
||||
WINPR_ASSERT(client->context);
|
||||
|
||||
settings = client->context->settings;
|
||||
WINPR_ASSERT(settings);
|
||||
|
||||
char* CertificateFile = nullptr;
|
||||
if (!wf_settings_read_string_ascii(HKEY_LOCAL_MACHINE, SERVER_KEY, _T("CertificateFile"),
|
||||
&(CertificateFile)))
|
||||
CertificateFile = _strdup("server.crt");
|
||||
|
||||
rdpCertificate* cert = freerdp_certificate_new_from_file(CertificateFile);
|
||||
free(CertificateFile);
|
||||
if (!cert)
|
||||
return FALSE;
|
||||
|
||||
if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerCertificate, cert, 1))
|
||||
return FALSE;
|
||||
|
||||
char* PrivateKeyFile = nullptr;
|
||||
if (!wf_settings_read_string_ascii(HKEY_LOCAL_MACHINE, SERVER_KEY, _T("PrivateKeyFile"),
|
||||
&(PrivateKeyFile)))
|
||||
PrivateKeyFile = _strdup("server.key");
|
||||
|
||||
rdpPrivateKey* key = freerdp_key_new_from_file_enc(PrivateKeyFile, nullptr);
|
||||
free(PrivateKeyFile);
|
||||
|
||||
if (!key)
|
||||
return FALSE;
|
||||
|
||||
if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerRsaKey, key, 1))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
DWORD WINAPI wf_peer_main_loop(LPVOID lpParam)
|
||||
{
|
||||
wfInfo* wfi;
|
||||
DWORD nCount;
|
||||
DWORD status;
|
||||
HANDLE handles[32];
|
||||
rdpSettings* settings;
|
||||
wfPeerContext* context;
|
||||
freerdp_peer* client = (freerdp_peer*)lpParam;
|
||||
|
||||
if (!wf_peer_init(client))
|
||||
goto fail_peer_init;
|
||||
|
||||
WINPR_ASSERT(client->context);
|
||||
|
||||
settings = client->context->settings;
|
||||
WINPR_ASSERT(settings);
|
||||
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, TRUE))
|
||||
goto fail_peer_init;
|
||||
if (!freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth, 32))
|
||||
goto fail_peer_init;
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_NSCodec, FALSE))
|
||||
goto fail_peer_init;
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_JpegCodec, FALSE))
|
||||
goto fail_peer_init;
|
||||
|
||||
if (!wf_peer_read_settings(client))
|
||||
goto fail_peer_init;
|
||||
|
||||
client->PostConnect = wf_peer_post_connect;
|
||||
client->Activate = wf_peer_activate;
|
||||
client->Logon = wf_peer_logon;
|
||||
|
||||
WINPR_ASSERT(client->context->input);
|
||||
client->context->input->SynchronizeEvent = wf_peer_synchronize_event;
|
||||
client->context->input->KeyboardEvent = wf_peer_keyboard_event;
|
||||
client->context->input->UnicodeKeyboardEvent = wf_peer_unicode_keyboard_event;
|
||||
client->context->input->MouseEvent = wf_peer_mouse_event;
|
||||
client->context->input->ExtendedMouseEvent = wf_peer_extended_mouse_event;
|
||||
|
||||
WINPR_ASSERT(client->Initialize);
|
||||
if (!client->Initialize(client))
|
||||
goto fail_client_initialize;
|
||||
|
||||
context = (wfPeerContext*)client->context;
|
||||
|
||||
if (context->socketClose)
|
||||
goto fail_socked_closed;
|
||||
|
||||
wfi = context->info;
|
||||
|
||||
if (wfi->input_disabled)
|
||||
{
|
||||
WLog_INFO(TAG, "client input is disabled");
|
||||
client->context->input->KeyboardEvent = wf_peer_keyboard_event_dummy;
|
||||
client->context->input->UnicodeKeyboardEvent = wf_peer_unicode_keyboard_event_dummy;
|
||||
client->context->input->MouseEvent = wf_peer_mouse_event_dummy;
|
||||
client->context->input->ExtendedMouseEvent = wf_peer_extended_mouse_event_dummy;
|
||||
}
|
||||
|
||||
if (!(context->socketEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
goto fail_socket_event;
|
||||
|
||||
if (!(context->socketSemaphore = CreateSemaphore(nullptr, 0, 1, nullptr)))
|
||||
goto fail_socket_semaphore;
|
||||
|
||||
if (!(context->socketThread =
|
||||
CreateThread(nullptr, 0, wf_peer_socket_listener, client, 0, nullptr)))
|
||||
goto fail_socket_thread;
|
||||
|
||||
WLog_INFO(TAG, "We've got a client %s", client->local ? "(local)" : client->hostname);
|
||||
nCount = 0;
|
||||
handles[nCount++] = context->updateEvent;
|
||||
handles[nCount++] = context->socketEvent;
|
||||
|
||||
while (1)
|
||||
{
|
||||
status = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
|
||||
|
||||
if ((status == WAIT_FAILED) || (status == WAIT_TIMEOUT))
|
||||
{
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed");
|
||||
break;
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(context->updateEvent, 0) == 0)
|
||||
{
|
||||
if (client->activated)
|
||||
wf_update_peer_send(wfi, context);
|
||||
|
||||
(void)ResetEvent(context->updateEvent);
|
||||
ReleaseSemaphore(wfi->updateSemaphore, 1, nullptr);
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(context->socketEvent, 0) == 0)
|
||||
{
|
||||
if (client->CheckFileDescriptor(client) != TRUE)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to check peer file descriptor");
|
||||
context->socketClose = TRUE;
|
||||
}
|
||||
|
||||
(void)ResetEvent(context->socketEvent);
|
||||
ReleaseSemaphore(context->socketSemaphore, 1, nullptr);
|
||||
|
||||
if (context->socketClose)
|
||||
break;
|
||||
}
|
||||
|
||||
// force disconnect
|
||||
if (wfi->force_all_disconnect == TRUE)
|
||||
{
|
||||
WLog_INFO(TAG, "Forcing Disconnect -> ");
|
||||
break;
|
||||
}
|
||||
|
||||
/* FIXME: we should wait on this, instead of calling it every time */
|
||||
if (WTSVirtualChannelManagerCheckFileDescriptor(context->vcm) != TRUE)
|
||||
break;
|
||||
}
|
||||
|
||||
WLog_INFO(TAG, "Client %s disconnected.", client->local ? "(local)" : client->hostname);
|
||||
|
||||
if (WaitForSingleObject(context->updateEvent, 0) == 0)
|
||||
{
|
||||
(void)ResetEvent(context->updateEvent);
|
||||
ReleaseSemaphore(wfi->updateSemaphore, 1, nullptr);
|
||||
}
|
||||
|
||||
wf_update_peer_deactivate(wfi, context);
|
||||
client->Disconnect(client);
|
||||
fail_socket_thread:
|
||||
(void)CloseHandle(context->socketSemaphore);
|
||||
context->socketSemaphore = nullptr;
|
||||
fail_socket_semaphore:
|
||||
(void)CloseHandle(context->socketEvent);
|
||||
context->socketEvent = nullptr;
|
||||
fail_socket_event:
|
||||
fail_socked_closed:
|
||||
fail_client_initialize:
|
||||
freerdp_peer_context_free(client);
|
||||
fail_peer_init:
|
||||
freerdp_peer_free(client);
|
||||
return 0;
|
||||
}
|
||||
29
third_party/FreeRDP/server/Windows/wf_peer.h
vendored
Normal file
29
third_party/FreeRDP/server/Windows/wf_peer.h
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Windows Server
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_WIN_PEER_H
|
||||
#define FREERDP_SERVER_WIN_PEER_H
|
||||
|
||||
#include "wf_interface.h"
|
||||
|
||||
#include <freerdp/listener.h>
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL wf_peer_accepted(freerdp_listener* instance, freerdp_peer* client);
|
||||
|
||||
#endif /* FREERDP_SERVER_WIN_PEER_H */
|
||||
153
third_party/FreeRDP/server/Windows/wf_rdpsnd.c
vendored
Normal file
153
third_party/FreeRDP/server/Windows/wf_rdpsnd.c
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Windows Server (Audio Output)
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2013 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <winpr/windows.h>
|
||||
#include <freerdp/server/server-common.h>
|
||||
|
||||
#include "wf_rdpsnd.h"
|
||||
#include "wf_info.h"
|
||||
|
||||
#ifdef WITH_RDPSND_DSOUND
|
||||
|
||||
#include "wf_directsound.h"
|
||||
|
||||
#else
|
||||
|
||||
#include "wf_wasapi.h"
|
||||
|
||||
#endif
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("windows")
|
||||
|
||||
static void wf_peer_rdpsnd_activated(RdpsndServerContext* context)
|
||||
{
|
||||
wfInfo* wfi;
|
||||
wfi = wf_info_get_instance();
|
||||
wfi->agreed_format = nullptr;
|
||||
WLog_DBG(TAG, "Client supports the following %d formats:", context->num_client_formats);
|
||||
|
||||
size_t i = 0;
|
||||
for (; i < context->num_client_formats; i++)
|
||||
{
|
||||
// TODO: improve the way we agree on a format
|
||||
for (size_t j = 0; j < context->num_server_formats; j++)
|
||||
{
|
||||
if ((context->client_formats[i].wFormatTag == context->server_formats[j].wFormatTag) &&
|
||||
(context->client_formats[i].nChannels == context->server_formats[j].nChannels) &&
|
||||
(context->client_formats[i].nSamplesPerSec ==
|
||||
context->server_formats[j].nSamplesPerSec))
|
||||
{
|
||||
WLog_DBG(TAG, "agreed on format!");
|
||||
wfi->agreed_format = (AUDIO_FORMAT*)&context->server_formats[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (wfi->agreed_format != nullptr)
|
||||
break;
|
||||
}
|
||||
|
||||
if (wfi->agreed_format == nullptr)
|
||||
{
|
||||
WLog_ERR(TAG, "Could not agree on a audio format with the server");
|
||||
return;
|
||||
}
|
||||
|
||||
context->SelectFormat(context, i);
|
||||
context->SetVolume(context, 0x7FFF, 0x7FFF);
|
||||
#ifdef WITH_RDPSND_DSOUND
|
||||
wf_directsound_activate(context);
|
||||
#else
|
||||
wf_wasapi_activate(context);
|
||||
#endif
|
||||
}
|
||||
|
||||
int wf_rdpsnd_lock()
|
||||
{
|
||||
DWORD dRes;
|
||||
wfInfo* wfi;
|
||||
wfi = wf_info_get_instance();
|
||||
dRes = WaitForSingleObject(wfi->snd_mutex, INFINITE);
|
||||
|
||||
switch (dRes)
|
||||
{
|
||||
case WAIT_ABANDONED:
|
||||
case WAIT_OBJECT_0:
|
||||
return TRUE;
|
||||
break;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
return FALSE;
|
||||
break;
|
||||
|
||||
case WAIT_FAILED:
|
||||
WLog_ERR(TAG, "wf_rdpsnd_lock failed with 0x%08lX", GetLastError());
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int wf_rdpsnd_unlock()
|
||||
{
|
||||
wfInfo* wfi;
|
||||
wfi = wf_info_get_instance();
|
||||
|
||||
if (ReleaseMutex(wfi->snd_mutex) == 0)
|
||||
{
|
||||
WLog_DBG(TAG, "wf_rdpsnd_unlock failed with 0x%08lX", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL wf_peer_rdpsnd_init(wfPeerContext* context)
|
||||
{
|
||||
wfInfo* wfi = wf_info_get_instance();
|
||||
|
||||
if (!wfi)
|
||||
return FALSE;
|
||||
|
||||
if (!(wfi->snd_mutex = CreateMutex(nullptr, FALSE, nullptr)))
|
||||
return FALSE;
|
||||
|
||||
context->rdpsnd = rdpsnd_server_context_new(context->vcm);
|
||||
context->rdpsnd->rdpcontext = &context->_p;
|
||||
context->rdpsnd->data = context;
|
||||
context->rdpsnd->num_server_formats =
|
||||
server_rdpsnd_get_formats(&context->rdpsnd->server_formats);
|
||||
|
||||
if (context->rdpsnd->num_server_formats > 0)
|
||||
context->rdpsnd->src_format = &context->rdpsnd->server_formats[0];
|
||||
|
||||
context->rdpsnd->Activated = wf_peer_rdpsnd_activated;
|
||||
context->rdpsnd->Initialize(context->rdpsnd, TRUE);
|
||||
wf_rdpsnd_set_latest_peer(context);
|
||||
wfi->snd_stop = FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
33
third_party/FreeRDP/server/Windows/wf_rdpsnd.h
vendored
Normal file
33
third_party/FreeRDP/server/Windows/wf_rdpsnd.h
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Windows Server (Audio Output)
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_WIN_RDPSND_H
|
||||
#define FREERDP_SERVER_WIN_RDPSND_H
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/listener.h>
|
||||
#include <freerdp/server/rdpsnd.h>
|
||||
|
||||
#include "wf_interface.h"
|
||||
|
||||
WINPR_ATTR_NODISCARD int wf_rdpsnd_lock(void);
|
||||
WINPR_ATTR_NODISCARD int wf_rdpsnd_unlock(void);
|
||||
WINPR_ATTR_NODISCARD BOOL wf_peer_rdpsnd_init(wfPeerContext* context);
|
||||
|
||||
#endif /* FREERDP_SERVER_WIN_RDPSND_H */
|
||||
103
third_party/FreeRDP/server/Windows/wf_settings.c
vendored
Normal file
103
third_party/FreeRDP/server/Windows/wf_settings.c
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Windows Server
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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 <winpr/tchar.h>
|
||||
#include <winpr/windows.h>
|
||||
|
||||
#include "wf_settings.h"
|
||||
|
||||
BOOL wf_settings_read_dword(HKEY key, LPCSTR subkey, LPTSTR name, DWORD* value)
|
||||
{
|
||||
HKEY hKey;
|
||||
LONG status;
|
||||
DWORD dwType;
|
||||
DWORD dwSize;
|
||||
DWORD dwValue;
|
||||
|
||||
status = RegOpenKeyExA(key, subkey, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
|
||||
|
||||
if (status == ERROR_SUCCESS)
|
||||
{
|
||||
dwSize = sizeof(DWORD);
|
||||
|
||||
status = RegQueryValueEx(hKey, name, nullptr, &dwType, (BYTE*)&dwValue, &dwSize);
|
||||
|
||||
if (status == ERROR_SUCCESS)
|
||||
*value = dwValue;
|
||||
|
||||
RegCloseKey(hKey);
|
||||
|
||||
return (status == ERROR_SUCCESS) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL wf_settings_read_string_ascii(HKEY key, LPCSTR subkey, LPTSTR name, char** value)
|
||||
{
|
||||
HKEY hKey;
|
||||
int length;
|
||||
LONG status;
|
||||
DWORD dwType;
|
||||
DWORD dwSize;
|
||||
char* strA;
|
||||
TCHAR* strX = nullptr;
|
||||
|
||||
status = RegOpenKeyExA(key, subkey, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
|
||||
|
||||
if (status != ERROR_SUCCESS)
|
||||
return FALSE;
|
||||
|
||||
status = RegQueryValueEx(hKey, name, nullptr, &dwType, nullptr, &dwSize);
|
||||
|
||||
if (status == ERROR_SUCCESS)
|
||||
{
|
||||
strX = (LPTSTR)malloc(dwSize + sizeof(TCHAR));
|
||||
if (!strX)
|
||||
return FALSE;
|
||||
status = RegQueryValueEx(hKey, name, nullptr, &dwType, (BYTE*)strX, &dwSize);
|
||||
|
||||
if (status != ERROR_SUCCESS)
|
||||
{
|
||||
free(strX);
|
||||
RegCloseKey(hKey);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (strX)
|
||||
{
|
||||
#ifdef UNICODE
|
||||
length =
|
||||
WideCharToMultiByte(CP_UTF8, 0, strX, lstrlenW(strX), nullptr, 0, nullptr, nullptr);
|
||||
strA = (char*)malloc(length + 1);
|
||||
WideCharToMultiByte(CP_UTF8, 0, strX, lstrlenW(strX), strA, length, nullptr, nullptr);
|
||||
strA[length] = '\0';
|
||||
free(strX);
|
||||
#else
|
||||
strA = (char*)strX;
|
||||
#endif
|
||||
*value = strA;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
30
third_party/FreeRDP/server/Windows/wf_settings.h
vendored
Normal file
30
third_party/FreeRDP/server/Windows/wf_settings.h
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Windows Server
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_WIN_SETTINGS_H
|
||||
#define FREERDP_SERVER_WIN_SETTINGS_H
|
||||
|
||||
#include "wf_interface.h"
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL wf_settings_read_dword(HKEY key, LPCSTR subkey, LPTSTR name,
|
||||
DWORD* value);
|
||||
WINPR_ATTR_NODISCARD BOOL wf_settings_read_string_ascii(HKEY key, LPCSTR subkey, LPTSTR name,
|
||||
char** value);
|
||||
|
||||
#endif /* FREERDP_SERVER_WIN_SETTINGS_H */
|
||||
252
third_party/FreeRDP/server/Windows/wf_update.c
vendored
Normal file
252
third_party/FreeRDP/server/Windows/wf_update.c
vendored
Normal file
@@ -0,0 +1,252 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Client
|
||||
* FreeRDP Windows Server
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
|
||||
#include <winpr/windows.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/listener.h>
|
||||
|
||||
#include "wf_peer.h"
|
||||
#include "wf_info.h"
|
||||
#include "wf_mirage.h"
|
||||
|
||||
#include "wf_update.h"
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("windows")
|
||||
|
||||
DWORD WINAPI wf_update_thread(LPVOID lpParam)
|
||||
{
|
||||
DWORD fps;
|
||||
wfInfo* wfi;
|
||||
DWORD beg, end;
|
||||
DWORD diff, rate;
|
||||
wfi = (wfInfo*)lpParam;
|
||||
fps = wfi->framesPerSecond;
|
||||
rate = 1000 / fps;
|
||||
|
||||
while (1)
|
||||
{
|
||||
beg = GetTickCount();
|
||||
|
||||
if (wf_info_lock(wfi) > 0)
|
||||
{
|
||||
if (wfi->activePeerCount > 0)
|
||||
{
|
||||
wf_info_update_changes(wfi);
|
||||
|
||||
if (wf_info_have_updates(wfi))
|
||||
{
|
||||
wf_update_encode(wfi);
|
||||
// WLog_DBG(TAG, "Start of parallel sending");
|
||||
int index = 0;
|
||||
|
||||
for (int peerindex = 0; peerindex < wfi->peerCount; peerindex++)
|
||||
{
|
||||
for (; index < FREERDP_SERVER_WIN_INFO_MAXPEERS; index++)
|
||||
{
|
||||
if (wfi->peers[index] && wfi->peers[index]->activated)
|
||||
{
|
||||
// WLog_DBG(TAG, "Setting event for %d of %d", index + 1,
|
||||
// wfi->activePeerCount);
|
||||
(void)SetEvent(
|
||||
((wfPeerContext*)wfi->peers[index]->context)->updateEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int index = 0; index < wfi->activePeerCount; index++)
|
||||
{
|
||||
// WLog_DBG(TAG, "Waiting for %d of %d", index + 1, wfi->activePeerCount);
|
||||
// WaitForSingleObject(wfi->updateSemaphore, INFINITE);
|
||||
(void)WaitForSingleObject(wfi->updateSemaphore, 1000);
|
||||
}
|
||||
|
||||
// WLog_DBG(TAG, "End of parallel sending");
|
||||
wf_info_clear_invalid_region(wfi);
|
||||
}
|
||||
}
|
||||
|
||||
wf_info_unlock(wfi);
|
||||
}
|
||||
|
||||
end = GetTickCount();
|
||||
diff = end - beg;
|
||||
|
||||
if (diff < rate)
|
||||
{
|
||||
Sleep(rate - diff);
|
||||
}
|
||||
}
|
||||
|
||||
// WLog_DBG(TAG, "Exiting Update Thread");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wf_update_encode(wfInfo* wfi)
|
||||
{
|
||||
RFX_RECT rect;
|
||||
long height, width;
|
||||
BYTE* pDataBits = nullptr;
|
||||
int stride;
|
||||
SURFACE_BITS_COMMAND* cmd;
|
||||
wf_info_find_invalid_region(wfi);
|
||||
cmd = &wfi->cmd;
|
||||
Stream_ResetPosition(wfi->s);
|
||||
wf_info_getScreenData(wfi, &width, &height, &pDataBits, &stride);
|
||||
rect.x = 0;
|
||||
rect.y = 0;
|
||||
rect.width = (UINT16)width;
|
||||
rect.height = (UINT16)height;
|
||||
// WLog_DBG(TAG, "x:%"PRId32" y:%"PRId32" w:%ld h:%ld", wfi->invalid.left, wfi->invalid.top,
|
||||
// width, height);
|
||||
Stream_Clear(wfi->s);
|
||||
|
||||
if (!(rfx_compose_message(wfi->rfx_context, wfi->s, &rect, 1, pDataBits, width, height,
|
||||
stride)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
wfi->frame_idx = rfx_context_get_frame_idx(wfi->rfx_context);
|
||||
cmd->destLeft = wfi->invalid.left;
|
||||
cmd->destTop = wfi->invalid.top;
|
||||
cmd->destRight = wfi->invalid.left + width;
|
||||
cmd->destBottom = wfi->invalid.top + height;
|
||||
cmd->bmp.bpp = 32;
|
||||
cmd->bmp.codecID = 3;
|
||||
cmd->bmp.width = width;
|
||||
cmd->bmp.height = height;
|
||||
cmd->bmp.bitmapDataLength = Stream_GetPosition(wfi->s);
|
||||
cmd->bmp.bitmapData = Stream_Buffer(wfi->s);
|
||||
}
|
||||
|
||||
void wf_update_peer_send(wfInfo* wfi, wfPeerContext* context)
|
||||
{
|
||||
freerdp_peer* client;
|
||||
|
||||
WINPR_ASSERT(wfi);
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
client = ((rdpContext*)context)->peer;
|
||||
WINPR_ASSERT(client);
|
||||
|
||||
/* This happens when the RemoteFX encoder state is reset */
|
||||
|
||||
if (wfi->frame_idx == 1)
|
||||
context->frame_idx = 0;
|
||||
|
||||
/*
|
||||
* When a new client connects, it is possible that old frames from
|
||||
* from a previous encoding state remain. Those frames should be discarded
|
||||
* as they will cause an error condition in mstsc.
|
||||
*/
|
||||
|
||||
if ((context->frame_idx + 1) != wfi->frame_idx)
|
||||
{
|
||||
/* This frame is meant to be discarded */
|
||||
if (context->frame_idx == 0)
|
||||
return;
|
||||
|
||||
/* This is an unexpected error condition */
|
||||
WLog_DBG(TAG, "Unexpected Frame Index: Actual: %d Expected: %d", wfi->frame_idx,
|
||||
context->frame_idx + 1);
|
||||
}
|
||||
|
||||
WINPR_ASSERT(client->context);
|
||||
WINPR_ASSERT(client->context->settings);
|
||||
WINPR_ASSERT(client->context->update);
|
||||
WINPR_ASSERT(client->context->update->SurfaceBits);
|
||||
|
||||
wfi->cmd.bmp.codecID =
|
||||
freerdp_settings_get_uint32(client->context->settings, FreeRDP_RemoteFxCodecId);
|
||||
client->context->update->SurfaceBits(client->context, &wfi->cmd);
|
||||
context->frame_idx++;
|
||||
}
|
||||
|
||||
void wf_update_encoder_reset(wfInfo* wfi)
|
||||
{
|
||||
if (wf_info_lock(wfi) > 0)
|
||||
{
|
||||
WLog_DBG(TAG, "Resetting encoder");
|
||||
|
||||
if (wfi->rfx_context)
|
||||
{
|
||||
rfx_context_reset(wfi->rfx_context, wfi->servscreen_width, wfi->servscreen_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TODO: pass ThreadingFlags somehow */
|
||||
wfi->rfx_context = rfx_context_new(TRUE);
|
||||
rfx_context_set_mode(wfi->rfx_context, RLGR3);
|
||||
rfx_context_reset(wfi->rfx_context, wfi->servscreen_width, wfi->servscreen_height);
|
||||
rfx_context_set_pixel_format(wfi->rfx_context, PIXEL_FORMAT_BGRA32);
|
||||
wfi->s = Stream_New(nullptr, 0xFFFF);
|
||||
}
|
||||
|
||||
wf_info_invalidate_full_screen(wfi);
|
||||
wf_info_unlock(wfi);
|
||||
}
|
||||
}
|
||||
|
||||
void wf_update_peer_activate(wfInfo* wfi, wfPeerContext* context)
|
||||
{
|
||||
if (wf_info_lock(wfi) > 0)
|
||||
{
|
||||
if (wfi->activePeerCount < 1)
|
||||
{
|
||||
#ifndef WITH_DXGI_1_2
|
||||
wf_mirror_driver_activate(wfi);
|
||||
#endif
|
||||
ResumeThread(wfi->updateThread);
|
||||
}
|
||||
|
||||
wf_update_encoder_reset(wfi);
|
||||
wfi->activePeerCount++;
|
||||
WLog_DBG(TAG, "Activating Peer Updates: %d", wfi->activePeerCount);
|
||||
wf_info_unlock(wfi);
|
||||
}
|
||||
}
|
||||
|
||||
void wf_update_peer_deactivate(wfInfo* wfi, wfPeerContext* context)
|
||||
{
|
||||
if (wf_info_lock(wfi) > 0)
|
||||
{
|
||||
freerdp_peer* client = ((rdpContext*)context)->peer;
|
||||
|
||||
if (client->activated)
|
||||
{
|
||||
if (wfi->activePeerCount <= 1)
|
||||
{
|
||||
wf_mirror_driver_deactivate(wfi);
|
||||
}
|
||||
|
||||
client->activated = FALSE;
|
||||
wfi->activePeerCount--;
|
||||
WLog_DBG(TAG, "Deactivating Peer Updates: %d", wfi->activePeerCount);
|
||||
}
|
||||
|
||||
wf_info_unlock(wfi);
|
||||
}
|
||||
}
|
||||
37
third_party/FreeRDP/server/Windows/wf_update.h
vendored
Normal file
37
third_party/FreeRDP/server/Windows/wf_update.h
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Windows Server
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_WIN_UPDATE_H
|
||||
#define FREERDP_SERVER_WIN_UPDATE_H
|
||||
|
||||
#include "wf_interface.h"
|
||||
|
||||
void wf_update_encode(wfInfo* wfi);
|
||||
void wf_update_send(wfInfo* wfi);
|
||||
|
||||
WINPR_ATTR_NODISCARD DWORD WINAPI wf_update_thread(LPVOID lpParam);
|
||||
|
||||
void wf_update_begin(wfInfo* wfi);
|
||||
void wf_update_peer_send(wfInfo* wfi, wfPeerContext* context);
|
||||
void wf_update_end(wfInfo* wfi);
|
||||
|
||||
void wf_update_peer_activate(wfInfo* wfi, wfPeerContext* context);
|
||||
void wf_update_peer_deactivate(wfInfo* wfi, wfPeerContext* context);
|
||||
|
||||
#endif /* FREERDP_SERVER_WIN_UPDATE_H */
|
||||
333
third_party/FreeRDP/server/Windows/wf_wasapi.c
vendored
Normal file
333
third_party/FreeRDP/server/Windows/wf_wasapi.c
vendored
Normal file
@@ -0,0 +1,333 @@
|
||||
|
||||
#include "wf_wasapi.h"
|
||||
#include "wf_info.h"
|
||||
|
||||
#include <initguid.h>
|
||||
#include <mmdeviceapi.h>
|
||||
#include <functiondiscoverykeys_devpkey.h>
|
||||
#include <audioclient.h>
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#define TAG SERVER_TAG("windows")
|
||||
|
||||
//#define REFTIMES_PER_SEC 10000000
|
||||
//#define REFTIMES_PER_MILLISEC 10000
|
||||
|
||||
#define REFTIMES_PER_SEC 100000
|
||||
#define REFTIMES_PER_MILLISEC 100
|
||||
|
||||
//#define REFTIMES_PER_SEC 50000
|
||||
//#define REFTIMES_PER_MILLISEC 50
|
||||
|
||||
#ifndef __MINGW32__
|
||||
DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92,
|
||||
0x91, 0x69, 0x2E);
|
||||
DEFINE_GUID(IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36,
|
||||
0x17, 0xE6);
|
||||
DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03,
|
||||
0xb2);
|
||||
DEFINE_GUID(IID_IAudioCaptureClient, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c,
|
||||
0xd3, 0x17);
|
||||
#endif
|
||||
|
||||
LPWSTR devStr = nullptr;
|
||||
wfPeerContext* latestPeer = nullptr;
|
||||
|
||||
int wf_rdpsnd_set_latest_peer(wfPeerContext* peer)
|
||||
{
|
||||
latestPeer = peer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wf_wasapi_activate(RdpsndServerContext* context)
|
||||
{
|
||||
wchar_t* pattern = L"Stereo Mix";
|
||||
HANDLE hThread;
|
||||
|
||||
wf_wasapi_get_device_string(pattern, &devStr);
|
||||
|
||||
if (devStr == nullptr)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to match for output device! Disabling rdpsnd.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
WLog_DBG(TAG, "RDPSND (WASAPI) Activated");
|
||||
if (!(hThread = CreateThread(nullptr, 0, wf_rdpsnd_wasapi_thread, latestPeer, 0, nullptr)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed");
|
||||
return 1;
|
||||
}
|
||||
(void)CloseHandle(hThread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wf_wasapi_get_device_string(LPWSTR pattern, LPWSTR* deviceStr)
|
||||
{
|
||||
HRESULT hr;
|
||||
IMMDeviceEnumerator* pEnumerator = nullptr;
|
||||
IMMDeviceCollection* pCollection = nullptr;
|
||||
IMMDevice* pEndpoint = nullptr;
|
||||
IPropertyStore* pProps = nullptr;
|
||||
LPWSTR pwszID = nullptr;
|
||||
unsigned int count;
|
||||
|
||||
CoInitialize(nullptr);
|
||||
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, &IID_IMMDeviceEnumerator,
|
||||
(void**)&pEnumerator);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to cocreate device enumerator");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
hr = pEnumerator->lpVtbl->EnumAudioEndpoints(pEnumerator, eCapture, DEVICE_STATE_ACTIVE,
|
||||
&pCollection);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to create endpoint collection");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pCollection->lpVtbl->GetCount(pCollection, &count);
|
||||
WLog_INFO(TAG, "Num endpoints: %u", count);
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
WLog_ERR(TAG, "No endpoints!");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
PROPVARIANT nameVar;
|
||||
PropVariantInit(&nameVar);
|
||||
|
||||
hr = pCollection->lpVtbl->Item(pCollection, i, &pEndpoint);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get endpoint %u", i);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
hr = pEndpoint->lpVtbl->GetId(pEndpoint, &pwszID);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get endpoint ID");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
hr = pEndpoint->lpVtbl->OpenPropertyStore(pEndpoint, STGM_READ, &pProps);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to open property store");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
hr = pProps->lpVtbl->GetValue(pProps, &PKEY_Device_FriendlyName, &nameVar);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get device friendly name");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// do this a more reliable way
|
||||
if (wcscmp(pattern, nameVar.pwszVal) < 0)
|
||||
{
|
||||
unsigned int devStrLen;
|
||||
WLog_INFO(TAG, "Using sound output endpoint: [%s] (%s)", nameVar.pwszVal, pwszID);
|
||||
// WLog_INFO(TAG, "matched %d characters", wcscmp(pattern, nameVar.pwszVal);
|
||||
devStrLen = wcslen(pwszID);
|
||||
*deviceStr = (LPWSTR)calloc(devStrLen + 1, 2);
|
||||
if (!deviceStr)
|
||||
return -1;
|
||||
wcscpy_s(*deviceStr, devStrLen + 1, pwszID);
|
||||
}
|
||||
CoTaskMemFree(pwszID);
|
||||
pwszID = nullptr;
|
||||
PropVariantClear(&nameVar);
|
||||
|
||||
pProps->lpVtbl->Release(pProps);
|
||||
pProps = nullptr;
|
||||
|
||||
pEndpoint->lpVtbl->Release(pEndpoint);
|
||||
pEndpoint = nullptr;
|
||||
}
|
||||
|
||||
pCollection->lpVtbl->Release(pCollection);
|
||||
pCollection = nullptr;
|
||||
|
||||
pEnumerator->lpVtbl->Release(pEnumerator);
|
||||
pEnumerator = nullptr;
|
||||
CoUninitialize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD WINAPI wf_rdpsnd_wasapi_thread(LPVOID lpParam)
|
||||
{
|
||||
IMMDeviceEnumerator* pEnumerator = nullptr;
|
||||
IMMDevice* pDevice = nullptr;
|
||||
IAudioClient* pAudioClient = nullptr;
|
||||
IAudioCaptureClient* pCaptureClient = nullptr;
|
||||
WAVEFORMATEX* pwfx = nullptr;
|
||||
HRESULT hr;
|
||||
REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
|
||||
REFERENCE_TIME hnsActualDuration;
|
||||
UINT32 bufferFrameCount;
|
||||
UINT32 numFramesAvailable;
|
||||
UINT32 packetLength = 0;
|
||||
UINT32 dCount = 0;
|
||||
BYTE* pData;
|
||||
|
||||
wfPeerContext* context;
|
||||
wfInfo* wfi;
|
||||
|
||||
wfi = wf_info_get_instance();
|
||||
context = (wfPeerContext*)lpParam;
|
||||
|
||||
CoInitialize(nullptr);
|
||||
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, &IID_IMMDeviceEnumerator,
|
||||
(void**)&pEnumerator);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to cocreate device enumerator");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
hr = pEnumerator->lpVtbl->GetDevice(pEnumerator, devStr, &pDevice);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to cocreate get device");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
hr = pDevice->lpVtbl->Activate(pDevice, &IID_IAudioClient, CLSCTX_ALL, nullptr,
|
||||
(void**)&pAudioClient);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to activate audio client");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
hr = pAudioClient->lpVtbl->GetMixFormat(pAudioClient, &pwfx);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get mix format");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pwfx->wFormatTag = wfi->agreed_format->wFormatTag;
|
||||
pwfx->nChannels = wfi->agreed_format->nChannels;
|
||||
pwfx->nSamplesPerSec = wfi->agreed_format->nSamplesPerSec;
|
||||
pwfx->nAvgBytesPerSec = wfi->agreed_format->nAvgBytesPerSec;
|
||||
pwfx->nBlockAlign = wfi->agreed_format->nBlockAlign;
|
||||
pwfx->wBitsPerSample = wfi->agreed_format->wBitsPerSample;
|
||||
pwfx->cbSize = wfi->agreed_format->cbSize;
|
||||
|
||||
hr = pAudioClient->lpVtbl->Initialize(pAudioClient, AUDCLNT_SHAREMODE_SHARED, 0,
|
||||
hnsRequestedDuration, 0, pwfx, nullptr);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to initialize the audio client");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
hr = pAudioClient->lpVtbl->GetBufferSize(pAudioClient, &bufferFrameCount);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get buffer size");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
hr = pAudioClient->lpVtbl->GetService(pAudioClient, &IID_IAudioCaptureClient,
|
||||
(void**)&pCaptureClient);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get the capture client");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
hnsActualDuration = (UINT32)REFTIMES_PER_SEC * bufferFrameCount / pwfx->nSamplesPerSec;
|
||||
|
||||
hr = pAudioClient->lpVtbl->Start(pAudioClient);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to start capture");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
dCount = 0;
|
||||
|
||||
while (wfi->snd_stop == FALSE)
|
||||
{
|
||||
DWORD flags;
|
||||
|
||||
Sleep(hnsActualDuration / REFTIMES_PER_MILLISEC / 2);
|
||||
|
||||
hr = pCaptureClient->lpVtbl->GetNextPacketSize(pCaptureClient, &packetLength);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get packet length");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while (packetLength != 0)
|
||||
{
|
||||
hr = pCaptureClient->lpVtbl->GetBuffer(pCaptureClient, &pData, &numFramesAvailable,
|
||||
&flags, nullptr, nullptr);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get buffer");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Here we are writing the audio data
|
||||
// not sure if this flag is ever set by the system; msdn is not clear about it
|
||||
if (!(flags & AUDCLNT_BUFFERFLAGS_SILENT))
|
||||
context->rdpsnd->SendSamples(context->rdpsnd, pData, packetLength,
|
||||
(UINT16)(GetTickCount() & 0xffff));
|
||||
|
||||
hr = pCaptureClient->lpVtbl->ReleaseBuffer(pCaptureClient, numFramesAvailable);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to release buffer");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
hr = pCaptureClient->lpVtbl->GetNextPacketSize(pCaptureClient, &packetLength);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get packet length");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pAudioClient->lpVtbl->Stop(pAudioClient);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to stop audio client");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
CoTaskMemFree(pwfx);
|
||||
|
||||
if (pEnumerator != nullptr)
|
||||
pEnumerator->lpVtbl->Release(pEnumerator);
|
||||
|
||||
if (pDevice != nullptr)
|
||||
pDevice->lpVtbl->Release(pDevice);
|
||||
|
||||
if (pAudioClient != nullptr)
|
||||
pAudioClient->lpVtbl->Release(pAudioClient);
|
||||
|
||||
if (pCaptureClient != nullptr)
|
||||
pCaptureClient->lpVtbl->Release(pCaptureClient);
|
||||
|
||||
CoUninitialize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
15
third_party/FreeRDP/server/Windows/wf_wasapi.h
vendored
Normal file
15
third_party/FreeRDP/server/Windows/wf_wasapi.h
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef FREERDP_SERVER_WIN_WASAPI_H
|
||||
#define FREERDP_SERVER_WIN_WASAPI_H
|
||||
|
||||
#include <freerdp/server/rdpsnd.h>
|
||||
#include "wf_interface.h"
|
||||
|
||||
WINPR_ATTR_NODISCARD int wf_rdpsnd_set_latest_peer(wfPeerContext* peer);
|
||||
|
||||
WINPR_ATTR_NODISCARD int wf_wasapi_activate(RdpsndServerContext* context);
|
||||
|
||||
WINPR_ATTR_NODISCARD int wf_wasapi_get_device_string(LPWSTR pattern, LPWSTR* deviceStr);
|
||||
|
||||
WINPR_ATTR_NODISCARD DWORD WINAPI wf_rdpsnd_wasapi_thread(LPVOID lpParam);
|
||||
|
||||
#endif /* FREERDP_SERVER_WIN_WASAPI_H */
|
||||
55
third_party/FreeRDP/server/common/CMakeLists.txt
vendored
Normal file
55
third_party/FreeRDP/server/common/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP Server Common
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
#
|
||||
# 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.
|
||||
|
||||
set(MODULE_NAME "freerdp-server")
|
||||
set(MODULE_PREFIX "FREERDP_SERVER")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS server.c)
|
||||
|
||||
foreach(FREERDP_CHANNELS_SERVER_SRC ${FREERDP_CHANNELS_SERVER_SRCS})
|
||||
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} "${FREERDP_CHANNELS_SERVER_SRC}")
|
||||
endforeach()
|
||||
|
||||
if(MSVC)
|
||||
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS})
|
||||
endif()
|
||||
|
||||
addtargetwithresourcefile(${MODULE_NAME} FALSE "${FREERDP_VERSION}" ${MODULE_PREFIX}_SRCS)
|
||||
|
||||
target_include_directories(${MODULE_NAME} INTERFACE $<INSTALL_INTERFACE:include>)
|
||||
target_link_libraries(${MODULE_NAME} PRIVATE ${FREERDP_CHANNELS_SERVER_LIBS})
|
||||
target_link_libraries(${MODULE_NAME} PUBLIC winpr freerdp)
|
||||
|
||||
installwithrpath(
|
||||
TARGETS
|
||||
${MODULE_NAME}
|
||||
COMPONENT
|
||||
libraries
|
||||
EXPORT
|
||||
FreeRDP-ServerTargets
|
||||
ARCHIVE
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/Common")
|
||||
238
third_party/FreeRDP/server/common/server.c
vendored
Normal file
238
third_party/FreeRDP/server/common/server.c
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Server Common
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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 <winpr/wtypes.h>
|
||||
#include <freerdp/codec/audio.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
|
||||
#include <freerdp/server/server-common.h>
|
||||
#include <freerdp/log.h>
|
||||
|
||||
#define TAG FREERDP_TAG("server.common")
|
||||
|
||||
size_t server_audin_get_formats(AUDIO_FORMAT** dst_formats)
|
||||
{
|
||||
/* Default supported audio formats */
|
||||
BYTE adpcm_data_7[] = { 0xf4, 0x07, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
|
||||
0xff, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x40, 0x00, 0xf0, 0x00,
|
||||
0x00, 0x00, 0xcc, 0x01, 0x30, 0xff, 0x88, 0x01, 0x18, 0xff };
|
||||
BYTE adpcm_data_3[] = { 0xf4, 0x03, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
|
||||
0xff, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x40, 0x00, 0xf0, 0x00,
|
||||
0x00, 0x00, 0xcc, 0x01, 0x30, 0xff, 0x88, 0x01, 0x18, 0xff };
|
||||
BYTE adpcm_data_1[] = { 0xf4, 0x01, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
|
||||
0xff, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x40, 0x00, 0xf0, 0x00,
|
||||
0x00, 0x00, 0xcc, 0x01, 0x30, 0xff, 0x88, 0x01, 0x18, 0xff };
|
||||
BYTE adpcm_dvi_data_7[] = { 0xf9, 0x07 };
|
||||
BYTE adpcm_dvi_data_3[] = { 0xf9, 0x03 };
|
||||
BYTE adpcm_dvi_data_1[] = { 0xf9, 0x01 };
|
||||
BYTE gsm610_data[] = { 0x40, 0x01 };
|
||||
const AUDIO_FORMAT default_supported_audio_formats[] = {
|
||||
/* Formats sent by windows 10 server */
|
||||
{ WAVE_FORMAT_AAC_MS, 2, 44100, 24000, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_AAC_MS, 2, 44100, 20000, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_AAC_MS, 2, 44100, 16000, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_AAC_MS, 2, 44100, 12000, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_PCM, 2, 44100, 176400, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_ADPCM, 2, 44100, 44359, 2048, 4, 32, adpcm_data_7 },
|
||||
{ WAVE_FORMAT_DVI_ADPCM, 2, 44100, 44251, 2048, 4, 2, adpcm_dvi_data_7 },
|
||||
{ WAVE_FORMAT_ALAW, 2, 22050, 44100, 2, 8, 0, nullptr },
|
||||
{ WAVE_FORMAT_ADPCM, 2, 22050, 22311, 1024, 4, 32, adpcm_data_3 },
|
||||
{ WAVE_FORMAT_DVI_ADPCM, 2, 22050, 22201, 1024, 4, 2, adpcm_dvi_data_3 },
|
||||
{ WAVE_FORMAT_ADPCM, 1, 44100, 22179, 1024, 4, 32, adpcm_data_7 },
|
||||
{ WAVE_FORMAT_DVI_ADPCM, 1, 44100, 22125, 1024, 4, 2, adpcm_dvi_data_7 },
|
||||
{ WAVE_FORMAT_ADPCM, 2, 11025, 11289, 512, 4, 32, adpcm_data_1 },
|
||||
{ WAVE_FORMAT_DVI_ADPCM, 2, 11025, 11177, 512, 4, 2, adpcm_dvi_data_1 },
|
||||
{ WAVE_FORMAT_ADPCM, 1, 22050, 11155, 512, 4, 32, adpcm_data_3 },
|
||||
{ WAVE_FORMAT_DVI_ADPCM, 1, 22050, 11100, 512, 4, 2, adpcm_dvi_data_3 },
|
||||
{ WAVE_FORMAT_GSM610, 1, 44100, 8957, 65, 0, 2, gsm610_data },
|
||||
{ WAVE_FORMAT_ADPCM, 2, 8000, 8192, 512, 4, 32, adpcm_data_1 },
|
||||
{ WAVE_FORMAT_DVI_ADPCM, 2, 8000, 8110, 512, 4, 2, adpcm_dvi_data_1 },
|
||||
{ WAVE_FORMAT_ADPCM, 1, 11025, 5644, 256, 4, 32, adpcm_data_1 },
|
||||
{ WAVE_FORMAT_DVI_ADPCM, 1, 11025, 5588, 256, 4, 2, adpcm_dvi_data_1 },
|
||||
{ WAVE_FORMAT_GSM610, 1, 22050, 4478, 65, 0, 2, gsm610_data },
|
||||
{ WAVE_FORMAT_ADPCM, 1, 8000, 4096, 256, 4, 32, adpcm_data_1 },
|
||||
{ WAVE_FORMAT_DVI_ADPCM, 1, 8000, 4055, 256, 4, 2, adpcm_dvi_data_1 },
|
||||
{ WAVE_FORMAT_GSM610, 1, 11025, 2239, 65, 0, 2, gsm610_data },
|
||||
{ WAVE_FORMAT_GSM610, 1, 8000, 1625, 65, 0, 2, gsm610_data },
|
||||
/* Formats added for others */
|
||||
|
||||
{ WAVE_FORMAT_MSG723, 2, 44100, 0, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_MSG723, 2, 22050, 0, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_MSG723, 1, 44100, 0, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_MSG723, 1, 22050, 0, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_PCM, 2, 44100, 176400, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_PCM, 2, 22050, 88200, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_PCM, 1, 44100, 88200, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_PCM, 1, 22050, 44100, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_MULAW, 2, 44100, 88200, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_MULAW, 2, 22050, 44100, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_MULAW, 1, 44100, 44100, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_MULAW, 1, 22050, 22050, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_ALAW, 2, 44100, 88200, 2, 8, 0, nullptr },
|
||||
{ WAVE_FORMAT_ALAW, 2, 22050, 44100, 2, 8, 0, nullptr },
|
||||
{ WAVE_FORMAT_ALAW, 1, 44100, 44100, 2, 8, 0, nullptr },
|
||||
{ WAVE_FORMAT_ALAW, 1, 22050, 22050, 2, 8, 0, nullptr }
|
||||
};
|
||||
const size_t nrDefaultFormatsMax = ARRAYSIZE(default_supported_audio_formats);
|
||||
size_t nr_formats = 0;
|
||||
AUDIO_FORMAT* formats = audio_formats_new(nrDefaultFormatsMax);
|
||||
|
||||
if (!dst_formats)
|
||||
goto fail;
|
||||
|
||||
*dst_formats = nullptr;
|
||||
|
||||
if (!formats)
|
||||
goto fail;
|
||||
|
||||
for (size_t x = 0; x < nrDefaultFormatsMax; x++)
|
||||
{
|
||||
const AUDIO_FORMAT* format = &default_supported_audio_formats[x];
|
||||
|
||||
if (freerdp_dsp_supports_format(format, FALSE))
|
||||
{
|
||||
AUDIO_FORMAT* dst = &formats[nr_formats++];
|
||||
|
||||
if (!audio_format_copy(format, dst))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
*dst_formats = formats;
|
||||
return nr_formats;
|
||||
fail:
|
||||
audio_formats_free(formats, nrDefaultFormatsMax);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t server_rdpsnd_get_formats(AUDIO_FORMAT** dst_formats)
|
||||
{
|
||||
/* Default supported audio formats */
|
||||
static const AUDIO_FORMAT default_supported_audio_formats[] = {
|
||||
{ WAVE_FORMAT_AAC_MS, 2, 44100, 176400, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_MPEGLAYER3, 2, 44100, 176400, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_MSG723, 2, 44100, 176400, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_GSM610, 2, 44100, 176400, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_ADPCM, 2, 44100, 176400, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_PCM, 2, 44100, 176400, 4, 16, 0, nullptr },
|
||||
{ WAVE_FORMAT_ALAW, 2, 22050, 44100, 2, 8, 0, nullptr },
|
||||
{ WAVE_FORMAT_MULAW, 2, 22050, 44100, 2, 8, 0, nullptr },
|
||||
};
|
||||
AUDIO_FORMAT* supported_audio_formats =
|
||||
audio_formats_new(ARRAYSIZE(default_supported_audio_formats));
|
||||
|
||||
if (!supported_audio_formats)
|
||||
goto fail;
|
||||
|
||||
{
|
||||
size_t y = 0;
|
||||
for (size_t x = 0; x < ARRAYSIZE(default_supported_audio_formats); x++)
|
||||
{
|
||||
const AUDIO_FORMAT* format = &default_supported_audio_formats[x];
|
||||
|
||||
if (freerdp_dsp_supports_format(format, TRUE))
|
||||
supported_audio_formats[y++] = *format;
|
||||
}
|
||||
|
||||
/* Set default audio formats. */
|
||||
*dst_formats = supported_audio_formats;
|
||||
return y;
|
||||
}
|
||||
fail:
|
||||
audio_formats_free(supported_audio_formats, ARRAYSIZE(default_supported_audio_formats));
|
||||
|
||||
if (dst_formats)
|
||||
*dst_formats = nullptr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void freerdp_server_warn_unmaintained(int argc, char* argv[])
|
||||
{
|
||||
const char* app = (argc > 0) ? argv[0] : "INVALID_ARGV";
|
||||
const DWORD log_level = WLOG_WARN;
|
||||
wLog* log = WLog_Get(TAG);
|
||||
WINPR_ASSERT(log);
|
||||
|
||||
if (!WLog_IsLevelActive(log, log_level))
|
||||
return;
|
||||
|
||||
WLog_Print_unchecked(log, log_level, "[unmaintained] %s server is currently unmaintained!",
|
||||
app);
|
||||
WLog_Print_unchecked(
|
||||
log, log_level,
|
||||
" If problems occur please check https://github.com/FreeRDP/FreeRDP/issues for "
|
||||
"known issues!");
|
||||
WLog_Print_unchecked(
|
||||
log, log_level,
|
||||
"Be prepared to fix issues yourself though as nobody is actively working on this.");
|
||||
WLog_Print_unchecked(
|
||||
log, log_level,
|
||||
" Developers hang out in https://matrix.to/#/#FreeRDP:matrix.org?via=matrix.org "
|
||||
"- don't hesitate to ask some questions. (replies might take some time depending "
|
||||
"on your timezone) - if you intend using this component write us a message");
|
||||
}
|
||||
|
||||
void freerdp_server_warn_experimental(int argc, char* argv[])
|
||||
{
|
||||
const char* app = (argc > 0) ? argv[0] : "INVALID_ARGV";
|
||||
const DWORD log_level = WLOG_WARN;
|
||||
wLog* log = WLog_Get(TAG);
|
||||
WINPR_ASSERT(log);
|
||||
|
||||
if (!WLog_IsLevelActive(log, log_level))
|
||||
return;
|
||||
|
||||
WLog_Print_unchecked(log, log_level, "[experimental] %s server is currently experimental!",
|
||||
app);
|
||||
WLog_Print_unchecked(
|
||||
log, log_level,
|
||||
" If problems occur please check https://github.com/FreeRDP/FreeRDP/issues for "
|
||||
"known issues or create a new one!");
|
||||
WLog_Print_unchecked(
|
||||
log, log_level,
|
||||
" Developers hang out in https://matrix.to/#/#FreeRDP:matrix.org?via=matrix.org "
|
||||
"- don't hesitate to ask some questions. (replies might take some time depending "
|
||||
"on your timezone)");
|
||||
}
|
||||
|
||||
void freerdp_server_warn_deprecated(int argc, char* argv[])
|
||||
{
|
||||
const char* app = (argc > 0) ? argv[0] : "INVALID_ARGV";
|
||||
const DWORD log_level = WLOG_WARN;
|
||||
wLog* log = WLog_Get(TAG);
|
||||
WINPR_ASSERT(log);
|
||||
|
||||
if (!WLog_IsLevelActive(log, log_level))
|
||||
return;
|
||||
|
||||
WLog_Print_unchecked(log, log_level, "[deprecated] %s server has been deprecated", app);
|
||||
WLog_Print_unchecked(log, log_level, "As replacement there is a SDL based client available.");
|
||||
WLog_Print_unchecked(
|
||||
log, log_level,
|
||||
"If you are interested in keeping %s alive get in touch with the developers", app);
|
||||
WLog_Print_unchecked(
|
||||
log, log_level,
|
||||
"The project is hosted at https://github.com/freerdp/freerdp and "
|
||||
" developers hang out in https://matrix.to/#/#FreeRDP:matrix.org?via=matrix.org "
|
||||
"- don't hesitate to ask some questions. (replies might take some time depending "
|
||||
"on your timezone)");
|
||||
}
|
||||
15
third_party/FreeRDP/server/freerdp-server.pc.in
vendored
Normal file
15
third_party/FreeRDP/server/freerdp-server.pc.in
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
prefix=@PKG_CONFIG_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||
includedir=${prefix}/@FREERDP_INCLUDE_DIR@
|
||||
libs=-lfreerdp-server@FREERDP_API_VERSION@
|
||||
|
||||
Name: FreeRDP server
|
||||
Description: FreeRDP: A Remote Desktop Protocol Implementation
|
||||
URL: http://www.freerdp.com/
|
||||
Version: @FREERDP_VERSION@
|
||||
Requires: @FREERDP_SERVER_PC_REQUIRES@
|
||||
Requires.private: @FREERDP_SERVER_PC_REQUIRES_PRIVATE@
|
||||
Libs: -L${libdir} ${libs}
|
||||
Libs.private: @FREERDP_SERVER_PC_LIBRARY_PRIVATE@
|
||||
Cflags: -I${includedir}
|
||||
127
third_party/FreeRDP/server/proxy/CMakeLists.txt
vendored
Normal file
127
third_party/FreeRDP/server/proxy/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP Proxy Server
|
||||
#
|
||||
# Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
|
||||
# Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
# Copyright 2019 Idan Freiberg <speidy@gmail.com>
|
||||
# Copyright 2021 Armin Novak <anovak@thincast.com>
|
||||
# Copyright 2021 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(CMakeDependentOption)
|
||||
set(MODULE_NAME "freerdp-server-proxy")
|
||||
set(MODULE_PREFIX "FREERDP_SERVER_PROXY")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
pf_context.c
|
||||
pf_channel.c
|
||||
pf_channel.h
|
||||
pf_client.c
|
||||
pf_client.h
|
||||
pf_input.c
|
||||
pf_input.h
|
||||
pf_update.c
|
||||
pf_update.h
|
||||
pf_server.c
|
||||
pf_server.h
|
||||
pf_config.c
|
||||
pf_modules.c
|
||||
pf_utils.h
|
||||
pf_utils.c
|
||||
$<TARGET_OBJECTS:pf_channels>
|
||||
)
|
||||
|
||||
set(PROXY_APP_SRCS freerdp_proxy.c)
|
||||
|
||||
option(WITH_PROXY_EMULATE_SMARTCARD "Compile proxy smartcard emulation" OFF)
|
||||
add_subdirectory("channels")
|
||||
|
||||
addtargetwithresourcefile(${MODULE_NAME} FALSE "${FREERDP_VERSION}" ${MODULE_PREFIX}_SRCS)
|
||||
|
||||
set(PRIVATE_LIBS freerdp-client freerdp-server)
|
||||
|
||||
set(PUBLIC_LIBS winpr freerdp)
|
||||
|
||||
target_include_directories(${MODULE_NAME} INTERFACE $<INSTALL_INTERFACE:include>)
|
||||
target_link_libraries(${MODULE_NAME} PRIVATE ${PRIVATE_LIBS} PUBLIC ${PUBLIC_LIBS})
|
||||
|
||||
installwithrpath(
|
||||
TARGETS
|
||||
${MODULE_NAME}
|
||||
COMPONENT
|
||||
server
|
||||
EXPORT
|
||||
FreeRDP-ProxyTargets
|
||||
ARCHIVE
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/Proxy")
|
||||
|
||||
# pkg-config
|
||||
# Do not set Requires.Private if not a static build
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
set(FREERDP_PROXY_PC_REQUIRES_PRIVATE "freerdp-client${FREERDP_API_VERSION} freerdp-server${FREERDP_API_VERSION}")
|
||||
set(FREERDP_PROXY_PC_LIBS_PRIVATE "-ldl -lpthread")
|
||||
endif()
|
||||
set(FREERDP_PROXY_PC_REQUIRES freerdp-server${FREERDP_API_VERSION})
|
||||
|
||||
include(pkg-config-install-prefix)
|
||||
cleaning_configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/freerdp-proxy.pc.in ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME}${FREERDP_VERSION_MAJOR}.pc
|
||||
@ONLY
|
||||
)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME}${FREERDP_VERSION_MAJOR}.pc
|
||||
DESTINATION ${PKG_CONFIG_PC_INSTALL_DIR}
|
||||
)
|
||||
|
||||
export(PACKAGE freerdp-proxy)
|
||||
|
||||
setfreerdpcmakeinstalldir(FREERDP_PROXY_CMAKE_INSTALL_DIR "FreeRDP-Proxy${FREERDP_VERSION_MAJOR}")
|
||||
|
||||
configure_package_config_file(
|
||||
FreeRDP-ProxyConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/FreeRDP-ProxyConfig.cmake
|
||||
INSTALL_DESTINATION ${FREERDP_PROXY_CMAKE_INSTALL_DIR} PATH_VARS FREERDP_INCLUDE_DIR
|
||||
)
|
||||
|
||||
write_basic_package_version_file(
|
||||
${CMAKE_CURRENT_BINARY_DIR}/FreeRDP-ProxyConfigVersion.cmake VERSION ${FREERDP_VERSION}
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FreeRDP-ProxyConfig.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/FreeRDP-ProxyConfigVersion.cmake
|
||||
DESTINATION ${FREERDP_PROXY_CMAKE_INSTALL_DIR}
|
||||
)
|
||||
install(EXPORT FreeRDP-ProxyTargets DESTINATION ${FREERDP_PROXY_CMAKE_INSTALL_DIR})
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/proxy")
|
||||
|
||||
option(WITH_PROXY_APP "Compile proxy application" ON)
|
||||
|
||||
if(WITH_PROXY_APP)
|
||||
add_subdirectory("cli")
|
||||
endif()
|
||||
|
||||
option(WITH_PROXY_MODULES "Compile proxy modules" ON)
|
||||
if(WITH_PROXY_MODULES)
|
||||
add_subdirectory("modules")
|
||||
endif()
|
||||
10
third_party/FreeRDP/server/proxy/FreeRDP-ProxyConfig.cmake.in
vendored
Normal file
10
third_party/FreeRDP/server/proxy/FreeRDP-ProxyConfig.cmake.in
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
set(FreeRDP-Proxy_VERSION_MAJOR "@FREERDP_VERSION_MAJOR@")
|
||||
set(FreeRDP-Proxy_VERSION_MINOR "@FREERDP_VERSION_MINOR@")
|
||||
set(FreeRDP-Proxy_VERSION_REVISION "@FREERDP_VERSION_REVISION@")
|
||||
|
||||
set_and_check(FreeRDP-Proxy_INCLUDE_DIR "@PACKAGE_FREERDP_INCLUDE_DIR@")
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/FreeRDP-ProxyTargets.cmake")
|
||||
8
third_party/FreeRDP/server/proxy/channels/CMakeLists.txt
vendored
Normal file
8
third_party/FreeRDP/server/proxy/channels/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
set(MODULE_NAME pf_channels)
|
||||
set(SOURCES pf_channel_rdpdr.c pf_channel_rdpdr.h pf_channel_drdynvc.c pf_channel_drdynvc.h)
|
||||
|
||||
if(WITH_PROXY_EMULATE_SMARTCARD)
|
||||
list(APPEND SOURCES pf_channel_smartcard.c pf_channel_smartcard.h)
|
||||
endif()
|
||||
|
||||
add_library(${MODULE_NAME} OBJECT ${SOURCES})
|
||||
924
third_party/FreeRDP/server/proxy/channels/pf_channel_drdynvc.c
vendored
Normal file
924
third_party/FreeRDP/server/proxy/channels/pf_channel_drdynvc.c
vendored
Normal file
@@ -0,0 +1,924 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* pf_channel_drdynvc
|
||||
*
|
||||
* Copyright 2022 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* 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 <winpr/assert.h>
|
||||
|
||||
#include <freerdp/channels/drdynvc.h>
|
||||
#include <freerdp/utils/drdynvc.h>
|
||||
#include <freerdp/server/proxy/proxy_log.h>
|
||||
|
||||
#include "pf_channel_drdynvc.h"
|
||||
#include "../pf_channel.h"
|
||||
#include "../proxy_modules.h"
|
||||
#include "../pf_utils.h"
|
||||
|
||||
#define DTAG PROXY_TAG("drdynvc")
|
||||
|
||||
#define Stream_CheckAndLogRequiredLengthWLogWithBackend(log, s, nmemb, backdata) \
|
||||
Stream_CheckAndLogRequiredLengthWLogEx(log, WLOG_WARN, s, nmemb, 1, "%s(%s:%" PRIuz ")[%s]", \
|
||||
__func__, __FILE__, (size_t)__LINE__, \
|
||||
getDirection(backdata))
|
||||
|
||||
/** @brief channel opened status */
|
||||
typedef enum
|
||||
{
|
||||
CHANNEL_OPENSTATE_WAITING_OPEN_STATUS, /*!< dynamic channel waiting for create response */
|
||||
CHANNEL_OPENSTATE_OPENED, /*!< opened */
|
||||
CHANNEL_OPENSTATE_CLOSED /*!< dynamic channel has been opened then closed */
|
||||
} PfDynChannelOpenStatus;
|
||||
|
||||
typedef struct p_server_dynamic_channel_context pServerDynamicChannelContext;
|
||||
typedef struct DynChannelTrackerState DynChannelTrackerState;
|
||||
|
||||
typedef PfChannelResult (*dynamic_channel_on_data_fn)(pServerContext* ps,
|
||||
pServerDynamicChannelContext* channel,
|
||||
BOOL isBackData, ChannelStateTracker* tracker,
|
||||
BOOL firstPacket, BOOL lastPacket);
|
||||
|
||||
/** @brief tracker state for a drdynvc stream */
|
||||
struct DynChannelTrackerState
|
||||
{
|
||||
UINT32 currentDataLength;
|
||||
UINT32 CurrentDataReceived;
|
||||
UINT32 CurrentDataFragments;
|
||||
wStream* currentPacket;
|
||||
WINPR_ATTR_NODISCARD dynamic_channel_on_data_fn dataCallback;
|
||||
};
|
||||
|
||||
typedef void (*channel_data_dtor_fn)(void** user_data);
|
||||
|
||||
struct p_server_dynamic_channel_context
|
||||
{
|
||||
char* channelName;
|
||||
UINT32 channelId;
|
||||
PfDynChannelOpenStatus openStatus;
|
||||
pf_utils_channel_mode channelMode;
|
||||
BOOL packetReassembly;
|
||||
DynChannelTrackerState backTracker;
|
||||
DynChannelTrackerState frontTracker;
|
||||
|
||||
void* channelData;
|
||||
channel_data_dtor_fn channelDataDtor;
|
||||
};
|
||||
|
||||
/** @brief context for the dynamic channel */
|
||||
typedef struct
|
||||
{
|
||||
wHashTable* channels;
|
||||
ChannelStateTracker* backTracker;
|
||||
ChannelStateTracker* frontTracker;
|
||||
wLog* log;
|
||||
} DynChannelContext;
|
||||
|
||||
/** @brief result of dynamic channel packet treatment */
|
||||
typedef enum
|
||||
{
|
||||
DYNCVC_READ_OK, /*!< read was OK */
|
||||
DYNCVC_READ_ERROR, /*!< an error happened during read */
|
||||
DYNCVC_READ_INCOMPLETE /*!< missing bytes to read the complete packet */
|
||||
} DynvcReadResult;
|
||||
|
||||
static const char* openstatus2str(PfDynChannelOpenStatus status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case CHANNEL_OPENSTATE_WAITING_OPEN_STATUS:
|
||||
return "CHANNEL_OPENSTATE_WAITING_OPEN_STATUS";
|
||||
case CHANNEL_OPENSTATE_CLOSED:
|
||||
return "CHANNEL_OPENSTATE_CLOSED";
|
||||
case CHANNEL_OPENSTATE_OPENED:
|
||||
return "CHANNEL_OPENSTATE_OPENED";
|
||||
default:
|
||||
return "CHANNEL_OPENSTATE_UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
#define DynvcTrackerLog(log, level, dynChannel, cmd, isBackData, ...) \
|
||||
dyn_log_((log), (level), (dynChannel), (cmd), (isBackData), __func__, __FILE__, __LINE__, \
|
||||
__VA_ARGS__)
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static const char* getDirection(BOOL isBackData)
|
||||
{
|
||||
return isBackData ? "B->F" : "F->B";
|
||||
}
|
||||
|
||||
static void dyn_log_(wLog* log, DWORD level, const pServerDynamicChannelContext* dynChannel,
|
||||
BYTE cmd, BOOL isBackData, const char* fkt, const char* file, size_t line,
|
||||
const char* fmt, ...)
|
||||
{
|
||||
if (!WLog_IsLevelActive(log, level))
|
||||
return;
|
||||
|
||||
char* prefix = nullptr;
|
||||
char* msg = nullptr;
|
||||
size_t prefixlen = 0;
|
||||
size_t msglen = 0;
|
||||
|
||||
uint32_t channelId = dynChannel ? dynChannel->channelId : UINT32_MAX;
|
||||
const char* channelName = dynChannel ? dynChannel->channelName : "<nullptr>";
|
||||
(void)winpr_asprintf(&prefix, &prefixlen, "DynvcTracker[%s](%s [%s:%" PRIu32 "])",
|
||||
getDirection(isBackData), channelName, drdynvc_get_packet_type(cmd),
|
||||
channelId);
|
||||
|
||||
va_list ap = WINPR_C_ARRAY_INIT;
|
||||
va_start(ap, fmt);
|
||||
(void)winpr_vasprintf(&msg, &msglen, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
WLog_PrintTextMessage(log, level, line, file, fkt, "%s: %s", prefix, msg);
|
||||
free(prefix);
|
||||
free(msg);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult data_cb(pServerContext* ps, pServerDynamicChannelContext* channel,
|
||||
BOOL isBackData, ChannelStateTracker* tracker, BOOL firstPacket,
|
||||
BOOL lastPacket)
|
||||
{
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(channel);
|
||||
WINPR_ASSERT(tracker);
|
||||
WINPR_ASSERT(ps->pdata);
|
||||
|
||||
wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
|
||||
proxyDynChannelInterceptData dyn = { .name = channel->channelName,
|
||||
.channelId = channel->channelId,
|
||||
.data = currentPacket,
|
||||
.isBackData = isBackData,
|
||||
.first = firstPacket,
|
||||
.last = lastPacket,
|
||||
.rewritten = FALSE,
|
||||
.packetSize = channelTracker_getCurrentPacketSize(tracker),
|
||||
.result = PF_CHANNEL_RESULT_ERROR };
|
||||
Stream_SealLength(dyn.data);
|
||||
if (!pf_modules_run_filter(ps->pdata->module, FILTER_TYPE_INTERCEPT_CHANNEL, ps->pdata, &dyn))
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
|
||||
channelTracker_setCurrentPacketSize(tracker, dyn.packetSize);
|
||||
if (dyn.rewritten)
|
||||
return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
|
||||
return dyn.result;
|
||||
}
|
||||
|
||||
static void DynamicChannelContext_free(void* ptr)
|
||||
{
|
||||
pServerDynamicChannelContext* c = (pServerDynamicChannelContext*)ptr;
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
if (c->backTracker.currentPacket)
|
||||
Stream_Free(c->backTracker.currentPacket, TRUE);
|
||||
|
||||
if (c->frontTracker.currentPacket)
|
||||
Stream_Free(c->frontTracker.currentPacket, TRUE);
|
||||
|
||||
if (c->channelDataDtor)
|
||||
c->channelDataDtor(&c->channelData);
|
||||
|
||||
free(c->channelName);
|
||||
free(c);
|
||||
}
|
||||
|
||||
WINPR_ATTR_MALLOC(DynamicChannelContext_free, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
static pServerDynamicChannelContext* DynamicChannelContext_new(wLog* log, pServerContext* ps,
|
||||
const char* name, UINT32 id)
|
||||
{
|
||||
WINPR_ASSERT(log);
|
||||
|
||||
pServerDynamicChannelContext* ret = calloc(1, sizeof(*ret));
|
||||
if (!ret)
|
||||
{
|
||||
WLog_Print(log, WLOG_ERROR, "error allocating dynamic channel context '%s'", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ret->channelId = id;
|
||||
ret->channelName = _strdup(name);
|
||||
if (!ret->channelName)
|
||||
{
|
||||
WLog_Print(log, WLOG_ERROR, "error allocating name in dynamic channel context '%s'", name);
|
||||
free(ret);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ret->frontTracker.dataCallback = data_cb;
|
||||
ret->backTracker.dataCallback = data_cb;
|
||||
|
||||
proxyChannelToInterceptData dyn = { .name = name, .channelId = id, .intercept = FALSE };
|
||||
if (pf_modules_run_filter(ps->pdata->module, FILTER_TYPE_DYN_INTERCEPT_LIST, ps->pdata, &dyn) &&
|
||||
dyn.intercept)
|
||||
ret->channelMode = PF_UTILS_CHANNEL_INTERCEPT;
|
||||
else
|
||||
ret->channelMode = pf_utils_get_channel_mode(ps->pdata->config, name);
|
||||
ret->openStatus = CHANNEL_OPENSTATE_OPENED;
|
||||
ret->packetReassembly = (ret->channelMode == PF_UTILS_CHANNEL_INTERCEPT);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static UINT32 ChannelId_Hash(const void* key)
|
||||
{
|
||||
const UINT32* v = (const UINT32*)key;
|
||||
return *v;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL ChannelId_Compare(const void* objA, const void* objB)
|
||||
{
|
||||
const UINT32* v1 = objA;
|
||||
const UINT32* v2 = objB;
|
||||
return (*v1 == *v2);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static DynvcReadResult dynvc_read_varInt(wLog* log, wStream* s, size_t len, UINT64* varInt,
|
||||
BOOL last)
|
||||
{
|
||||
WINPR_ASSERT(varInt);
|
||||
switch (len)
|
||||
{
|
||||
case 0x00:
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 1))
|
||||
return last ? DYNCVC_READ_ERROR : DYNCVC_READ_INCOMPLETE;
|
||||
Stream_Read_UINT8(s, *varInt);
|
||||
break;
|
||||
case 0x01:
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 2))
|
||||
return last ? DYNCVC_READ_ERROR : DYNCVC_READ_INCOMPLETE;
|
||||
Stream_Read_UINT16(s, *varInt);
|
||||
break;
|
||||
case 0x02:
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 4))
|
||||
return last ? DYNCVC_READ_ERROR : DYNCVC_READ_INCOMPLETE;
|
||||
Stream_Read_UINT32(s, *varInt);
|
||||
break;
|
||||
case 0x03:
|
||||
default:
|
||||
WLog_Print(log, WLOG_ERROR, "Unknown int len %" PRIuz, len);
|
||||
return DYNCVC_READ_ERROR;
|
||||
}
|
||||
return DYNCVC_READ_OK;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult DynvcTrackerPeekHandleByMode(ChannelStateTracker* tracker,
|
||||
DynChannelTrackerState* trackerState,
|
||||
pServerDynamicChannelContext* dynChannel,
|
||||
BYTE cmd, BOOL firstPacket, BOOL lastPacket)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
WINPR_ASSERT(trackerState);
|
||||
WINPR_ASSERT(dynChannel);
|
||||
PfChannelResult result = PF_CHANNEL_RESULT_ERROR;
|
||||
|
||||
DynChannelContext* dynChannelContext =
|
||||
(DynChannelContext*)channelTracker_getCustomData(tracker);
|
||||
WINPR_ASSERT(dynChannelContext);
|
||||
|
||||
proxyData* pdata = channelTracker_getPData(tracker);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
const BOOL isBackData = (tracker == dynChannelContext->backTracker);
|
||||
switch (dynChannel->channelMode)
|
||||
{
|
||||
case PF_UTILS_CHANNEL_PASSTHROUGH:
|
||||
result = channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
|
||||
break;
|
||||
case PF_UTILS_CHANNEL_BLOCK:
|
||||
channelTracker_setMode(tracker, CHANNEL_TRACKER_DROP);
|
||||
result = PF_CHANNEL_RESULT_DROP;
|
||||
break;
|
||||
case PF_UTILS_CHANNEL_INTERCEPT:
|
||||
if (trackerState->dataCallback)
|
||||
{
|
||||
result = trackerState->dataCallback(pdata->ps, dynChannel, isBackData, tracker,
|
||||
firstPacket, lastPacket);
|
||||
}
|
||||
else
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"no intercept callback for channel, dropping packet");
|
||||
result = PF_CHANNEL_RESULT_DROP;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"unknown channel mode %u", dynChannel->channelMode);
|
||||
result = PF_CHANNEL_RESULT_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!trackerState->currentDataLength ||
|
||||
(trackerState->CurrentDataReceived == trackerState->currentDataLength))
|
||||
{
|
||||
trackerState->currentDataLength = 0;
|
||||
trackerState->CurrentDataFragments = 0;
|
||||
trackerState->CurrentDataReceived = 0;
|
||||
|
||||
if (dynChannel->packetReassembly && trackerState->currentPacket)
|
||||
Stream_ResetPosition(trackerState->currentPacket);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult DynvcTrackerHandleClose(ChannelStateTracker* tracker,
|
||||
pServerDynamicChannelContext* dynChannel,
|
||||
DynChannelContext* dynChannelContext,
|
||||
BOOL firstPacket, BOOL lastPacket)
|
||||
{
|
||||
WINPR_ASSERT(dynChannelContext);
|
||||
|
||||
const BOOL isBackData = (tracker == dynChannelContext->backTracker);
|
||||
|
||||
if (!lastPacket || !dynChannel)
|
||||
return PF_CHANNEL_RESULT_DROP;
|
||||
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, CLOSE_REQUEST_PDU, isBackData,
|
||||
"Close request");
|
||||
channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
|
||||
if (dynChannel->openStatus != CHANNEL_OPENSTATE_OPENED)
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, CLOSE_REQUEST_PDU,
|
||||
isBackData, "is in state %s, expected %s",
|
||||
openstatus2str(dynChannel->openStatus),
|
||||
openstatus2str(CHANNEL_OPENSTATE_OPENED));
|
||||
}
|
||||
dynChannel->openStatus = CHANNEL_OPENSTATE_CLOSED;
|
||||
return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult DynvcTrackerHandleCreateBack(ChannelStateTracker* tracker, wStream* s,
|
||||
DWORD flags, proxyData* pdata,
|
||||
pServerDynamicChannelContext* dynChannel,
|
||||
DynChannelContext* dynChannelContext,
|
||||
UINT64 dynChannelId)
|
||||
{
|
||||
proxyChannelDataEventInfo dev = WINPR_C_ARRAY_INIT;
|
||||
const char* name = Stream_ConstPointer(s);
|
||||
const size_t nameLen = Stream_GetRemainingLength(s);
|
||||
const size_t len = strnlen(name, nameLen);
|
||||
const BOOL isBackData = (tracker == dynChannelContext->backTracker);
|
||||
const BYTE cmd = CREATE_REQUEST_PDU;
|
||||
|
||||
if ((len == 0) || (len == nameLen) || (dynChannelId > UINT16_MAX))
|
||||
{
|
||||
char namebuffer[64] = WINPR_C_ARRAY_INIT;
|
||||
(void)_snprintf(namebuffer, sizeof(namebuffer) - 1, "%s", name);
|
||||
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"channel id %" PRIu64 ", name=%s [%" PRIuz "|%" PRIuz "], status=%s",
|
||||
dynChannelId, namebuffer, len, nameLen,
|
||||
dynChannel ? openstatus2str(dynChannel->openStatus) : "nullptr");
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
|
||||
wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
|
||||
dev.channel_id = (UINT16)dynChannelId;
|
||||
dev.channel_name = name;
|
||||
dev.data = Stream_Buffer(s);
|
||||
dev.data_len = Stream_GetPosition(currentPacket);
|
||||
dev.flags = flags;
|
||||
dev.total_size = Stream_GetPosition(currentPacket);
|
||||
|
||||
if (dynChannel)
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_WARN, dynChannel, cmd, isBackData,
|
||||
"Reusing channel id, now %s", name);
|
||||
|
||||
HashTable_Remove(dynChannelContext->channels, &dynChannel->channelId);
|
||||
}
|
||||
|
||||
if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE,
|
||||
pdata, &dev))
|
||||
return PF_CHANNEL_RESULT_DROP; /* Silently drop */
|
||||
|
||||
dynChannel =
|
||||
DynamicChannelContext_new(dynChannelContext->log, pdata->ps, name, (UINT32)dynChannelId);
|
||||
if (!dynChannel)
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"unable to create dynamic channel context data");
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
|
||||
"Adding channel");
|
||||
if (!HashTable_Insert(dynChannelContext->channels, &dynChannel->channelId, dynChannel))
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"unable register dynamic channel context data");
|
||||
DynamicChannelContext_free(dynChannel);
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
|
||||
dynChannel->openStatus = CHANNEL_OPENSTATE_WAITING_OPEN_STATUS;
|
||||
|
||||
const BOOL firstPacket = (flags & CHANNEL_FLAG_FIRST) != 0;
|
||||
const BOOL lastPacket = (flags & CHANNEL_FLAG_LAST) != 0;
|
||||
|
||||
// NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert owns dynChannel
|
||||
return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, FALSE);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult DynvcTrackerHandleCreateFront(ChannelStateTracker* tracker, wStream* s,
|
||||
DWORD flags,
|
||||
WINPR_ATTR_UNUSED proxyData* pdata,
|
||||
pServerDynamicChannelContext* dynChannel,
|
||||
DynChannelContext* dynChannelContext,
|
||||
WINPR_ATTR_UNUSED UINT64 dynChannelId)
|
||||
{
|
||||
const BOOL isBackData = (tracker == dynChannelContext->backTracker);
|
||||
const BYTE cmd = CREATE_REQUEST_PDU;
|
||||
|
||||
/* CREATE_REQUEST_PDU response */
|
||||
if (!Stream_CheckAndLogRequiredLengthWLogWithBackend(dynChannelContext->log, s, 4, FALSE))
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
|
||||
const UINT32 creationStatus = Stream_Get_UINT32(s);
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
|
||||
"CREATE_RESPONSE openStatus=%" PRIu32, creationStatus);
|
||||
|
||||
if (dynChannel && (creationStatus == 0))
|
||||
dynChannel->openStatus = CHANNEL_OPENSTATE_OPENED;
|
||||
|
||||
const BOOL firstPacket = (flags & CHANNEL_FLAG_FIRST) != 0;
|
||||
const BOOL lastPacket = (flags & CHANNEL_FLAG_LAST) != 0;
|
||||
|
||||
return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, TRUE);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult DynvcTrackerHandleCreate(ChannelStateTracker* tracker, wStream* s,
|
||||
DWORD flags,
|
||||
pServerDynamicChannelContext* dynChannel,
|
||||
UINT64 dynChannelId)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
DynChannelContext* dynChannelContext =
|
||||
(DynChannelContext*)channelTracker_getCustomData(tracker);
|
||||
WINPR_ASSERT(dynChannelContext);
|
||||
|
||||
const BOOL lastPacket = (flags & CHANNEL_FLAG_LAST) != 0;
|
||||
const BOOL isBackData = (tracker == dynChannelContext->backTracker);
|
||||
|
||||
proxyData* pdata = channelTracker_getPData(tracker);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
/* we only want the full packet */
|
||||
if (!lastPacket)
|
||||
return PF_CHANNEL_RESULT_DROP;
|
||||
|
||||
if (isBackData)
|
||||
return DynvcTrackerHandleCreateBack(tracker, s, flags, pdata, dynChannel, dynChannelContext,
|
||||
dynChannelId);
|
||||
|
||||
return DynvcTrackerHandleCreateFront(tracker, s, flags, pdata, dynChannel, dynChannelContext,
|
||||
dynChannelId);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult DynvcTrackerHandleCmdDATA(ChannelStateTracker* tracker,
|
||||
pServerDynamicChannelContext* dynChannel,
|
||||
wStream* s, BYTE cmd, UINT64 Length,
|
||||
BOOL firstPacket, BOOL lastPacket)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
DynChannelContext* dynChannelContext =
|
||||
(DynChannelContext*)channelTracker_getCustomData(tracker);
|
||||
WINPR_ASSERT(dynChannelContext);
|
||||
|
||||
const BOOL isBackData = (tracker == dynChannelContext->backTracker);
|
||||
|
||||
if (!dynChannel)
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_WARN, dynChannel, cmd, isBackData,
|
||||
"channel is nullptr, dropping packet");
|
||||
return PF_CHANNEL_RESULT_DROP;
|
||||
}
|
||||
|
||||
DynChannelTrackerState* trackerState =
|
||||
isBackData ? &dynChannel->backTracker : &dynChannel->frontTracker;
|
||||
if (dynChannel->openStatus != CHANNEL_OPENSTATE_OPENED)
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_WARN, dynChannel, cmd, isBackData,
|
||||
"channel is not opened, dropping packet");
|
||||
return PF_CHANNEL_RESULT_DROP;
|
||||
}
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case DATA_FIRST_PDU:
|
||||
case DATA_FIRST_COMPRESSED_PDU:
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
|
||||
"DATA_FIRST currentPacketLength=%" PRIu64 "", Length);
|
||||
if (Length > UINT32_MAX)
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"Length out of bounds: %" PRIu64, Length);
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
trackerState->currentDataLength = (UINT32)Length;
|
||||
trackerState->CurrentDataReceived = 0;
|
||||
trackerState->CurrentDataFragments = 0;
|
||||
|
||||
if (dynChannel->packetReassembly)
|
||||
{
|
||||
if (trackerState->currentPacket)
|
||||
Stream_ResetPosition(trackerState->currentPacket);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case DATA_PDU:
|
||||
case DATA_FIRST_PDU:
|
||||
{
|
||||
size_t extraSize = Stream_GetRemainingLength(s);
|
||||
|
||||
trackerState->CurrentDataFragments++;
|
||||
trackerState->CurrentDataReceived += WINPR_ASSERTING_INT_CAST(uint32_t, extraSize);
|
||||
|
||||
if (dynChannel->packetReassembly)
|
||||
{
|
||||
if (!trackerState->currentPacket)
|
||||
{
|
||||
trackerState->currentPacket = Stream_New(nullptr, 1024);
|
||||
if (!trackerState->currentPacket)
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd,
|
||||
isBackData, "unable to create current packet",
|
||||
getDirection(isBackData), dynChannel->channelName,
|
||||
drdynvc_get_packet_type(cmd));
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(trackerState->currentPacket, extraSize))
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"unable to grow current packet", getDirection(isBackData),
|
||||
dynChannel->channelName, drdynvc_get_packet_type(cmd));
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
|
||||
Stream_Write(trackerState->currentPacket, Stream_ConstPointer(s), extraSize);
|
||||
}
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
|
||||
"frags=%" PRIu32 " received=%" PRIu32 "(%" PRIu32 ")",
|
||||
trackerState->CurrentDataFragments, trackerState->CurrentDataReceived,
|
||||
trackerState->currentDataLength);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case DATA_PDU:
|
||||
{
|
||||
if (trackerState->currentDataLength)
|
||||
{
|
||||
if (trackerState->CurrentDataReceived > trackerState->currentDataLength)
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"reassembled packet (%" PRIu32
|
||||
") is bigger than announced length (%" PRIu32 ")",
|
||||
trackerState->CurrentDataReceived,
|
||||
trackerState->currentDataLength);
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
trackerState->CurrentDataFragments = 0;
|
||||
trackerState->CurrentDataReceived = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return DynvcTrackerPeekHandleByMode(tracker, trackerState, dynChannel, cmd, firstPacket,
|
||||
lastPacket);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult DynvcTrackerHandleCmd(ChannelStateTracker* tracker,
|
||||
pServerDynamicChannelContext* dynChannel, wStream* s,
|
||||
BYTE cmd, UINT32 flags, UINT64 Length,
|
||||
UINT64 dynChannelId, BOOL firstPacket, BOOL lastPacket)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
DynChannelContext* dynChannelContext =
|
||||
(DynChannelContext*)channelTracker_getCustomData(tracker);
|
||||
WINPR_ASSERT(dynChannelContext);
|
||||
|
||||
const BOOL isBackData = (tracker == dynChannelContext->backTracker);
|
||||
switch (cmd)
|
||||
{
|
||||
case CAPABILITY_REQUEST_PDU:
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
|
||||
"CAPABILITY_%s", isBackData ? "REQUEST" : "RESPONSE");
|
||||
channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
|
||||
return PF_CHANNEL_RESULT_PASS;
|
||||
|
||||
case CREATE_REQUEST_PDU:
|
||||
return DynvcTrackerHandleCreate(tracker, s, flags, dynChannel, dynChannelId);
|
||||
|
||||
case CLOSE_REQUEST_PDU:
|
||||
return DynvcTrackerHandleClose(tracker, dynChannel, dynChannelContext, firstPacket,
|
||||
lastPacket);
|
||||
|
||||
case SOFT_SYNC_REQUEST_PDU:
|
||||
/* just pass then as is for now */
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
|
||||
"SOFT_SYNC_REQUEST_PDU");
|
||||
channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
|
||||
/*TODO: return pf_treat_softsync_req(pdata, s);*/
|
||||
return PF_CHANNEL_RESULT_PASS;
|
||||
|
||||
case SOFT_SYNC_RESPONSE_PDU:
|
||||
/* just pass then as is for now */
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
|
||||
"SOFT_SYNC_RESPONSE_PDU");
|
||||
channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
|
||||
return PF_CHANNEL_RESULT_PASS;
|
||||
|
||||
case DATA_FIRST_PDU:
|
||||
case DATA_PDU:
|
||||
return DynvcTrackerHandleCmdDATA(tracker, dynChannel, s, cmd, Length, firstPacket,
|
||||
lastPacket);
|
||||
|
||||
case DATA_FIRST_COMPRESSED_PDU:
|
||||
case DATA_COMPRESSED_PDU:
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
|
||||
"TODO: compressed data packets, pass them as is for now");
|
||||
channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
|
||||
return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
|
||||
|
||||
default:
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"Invalid command ID");
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult DynvcTrackerPeekFn(ChannelStateTracker* tracker, BOOL firstPacket,
|
||||
BOOL lastPacket)
|
||||
{
|
||||
wStream* s = nullptr;
|
||||
wStream sbuffer;
|
||||
BOOL haveChannelId = 0;
|
||||
BOOL haveLength = 0;
|
||||
UINT64 dynChannelId = 0;
|
||||
UINT64 Length = 0;
|
||||
pServerDynamicChannelContext* dynChannel = nullptr;
|
||||
|
||||
WINPR_ASSERT(tracker);
|
||||
|
||||
DynChannelContext* dynChannelContext =
|
||||
(DynChannelContext*)channelTracker_getCustomData(tracker);
|
||||
WINPR_ASSERT(dynChannelContext);
|
||||
|
||||
const BOOL isBackData = (tracker == dynChannelContext->backTracker);
|
||||
|
||||
UINT32 flags = lastPacket ? CHANNEL_FLAG_LAST : 0;
|
||||
if (firstPacket)
|
||||
flags |= CHANNEL_FLAG_FIRST;
|
||||
|
||||
{
|
||||
wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
|
||||
s = Stream_StaticConstInit(&sbuffer, Stream_Buffer(currentPacket),
|
||||
Stream_GetPosition(currentPacket));
|
||||
}
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLogWithBackend(dynChannelContext->log, s, 1, isBackData))
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
|
||||
const BYTE byte0 = Stream_Get_UINT8(s);
|
||||
const BYTE cmd = byte0 >> 4;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case CREATE_REQUEST_PDU:
|
||||
case CLOSE_REQUEST_PDU:
|
||||
case DATA_PDU:
|
||||
case DATA_COMPRESSED_PDU:
|
||||
haveChannelId = TRUE;
|
||||
haveLength = FALSE;
|
||||
break;
|
||||
case DATA_FIRST_PDU:
|
||||
case DATA_FIRST_COMPRESSED_PDU:
|
||||
haveLength = TRUE;
|
||||
haveChannelId = TRUE;
|
||||
break;
|
||||
default:
|
||||
haveChannelId = FALSE;
|
||||
haveLength = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (haveChannelId)
|
||||
{
|
||||
BYTE cbId = byte0 & 0x03;
|
||||
|
||||
switch (dynvc_read_varInt(dynChannelContext->log, s, cbId, &dynChannelId, lastPacket))
|
||||
{
|
||||
case DYNCVC_READ_OK:
|
||||
break;
|
||||
case DYNCVC_READ_INCOMPLETE:
|
||||
return PF_CHANNEL_RESULT_DROP;
|
||||
case DYNCVC_READ_ERROR:
|
||||
default:
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"invalid channelId field");
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
|
||||
/* we always try to retrieve the dynamic channel in case it would have been opened
|
||||
* and closed
|
||||
*/
|
||||
dynChannel = (pServerDynamicChannelContext*)HashTable_GetItemValue(
|
||||
dynChannelContext->channels, &dynChannelId);
|
||||
if ((cmd != CREATE_REQUEST_PDU) || !isBackData)
|
||||
{
|
||||
if (!dynChannel || (dynChannel->openStatus == CHANNEL_OPENSTATE_CLOSED))
|
||||
{
|
||||
/* we've not found the target channel, so we drop this chunk, plus all the rest of
|
||||
* the packet */
|
||||
channelTracker_setMode(tracker, CHANNEL_TRACKER_DROP);
|
||||
return PF_CHANNEL_RESULT_DROP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (haveLength)
|
||||
{
|
||||
BYTE lenLen = (byte0 >> 2) & 0x03;
|
||||
switch (dynvc_read_varInt(dynChannelContext->log, s, lenLen, &Length, lastPacket))
|
||||
{
|
||||
case DYNCVC_READ_OK:
|
||||
break;
|
||||
case DYNCVC_READ_INCOMPLETE:
|
||||
return PF_CHANNEL_RESULT_DROP;
|
||||
case DYNCVC_READ_ERROR:
|
||||
default:
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"invalid length field");
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return DynvcTrackerHandleCmd(tracker, dynChannel, s, cmd, flags, Length, dynChannelId,
|
||||
firstPacket, lastPacket);
|
||||
}
|
||||
|
||||
static void DynChannelContext_free(void* context)
|
||||
{
|
||||
DynChannelContext* c = context;
|
||||
if (!c)
|
||||
return;
|
||||
channelTracker_free(c->backTracker);
|
||||
channelTracker_free(c->frontTracker);
|
||||
HashTable_Free(c->channels);
|
||||
free(c);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static const char* dynamic_context(void* arg)
|
||||
{
|
||||
proxyData* pdata = arg;
|
||||
if (!pdata)
|
||||
return "pdata=null";
|
||||
return pdata->session_id;
|
||||
}
|
||||
|
||||
WINPR_ATTR_MALLOC(DynChannelContext_free, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
static DynChannelContext* DynChannelContext_new(proxyData* pdata,
|
||||
pServerStaticChannelContext* channel)
|
||||
{
|
||||
DynChannelContext* dyn = calloc(1, sizeof(DynChannelContext));
|
||||
if (!dyn)
|
||||
return nullptr;
|
||||
|
||||
dyn->log = WLog_Get(DTAG);
|
||||
WINPR_ASSERT(dyn->log);
|
||||
WLog_SetContext(dyn->log, dynamic_context, pdata);
|
||||
|
||||
dyn->backTracker = channelTracker_new(channel, DynvcTrackerPeekFn, dyn);
|
||||
if (!dyn->backTracker)
|
||||
goto fail;
|
||||
if (!channelTracker_setPData(dyn->backTracker, pdata))
|
||||
goto fail;
|
||||
|
||||
dyn->frontTracker = channelTracker_new(channel, DynvcTrackerPeekFn, dyn);
|
||||
if (!dyn->frontTracker)
|
||||
goto fail;
|
||||
if (!channelTracker_setPData(dyn->frontTracker, pdata))
|
||||
goto fail;
|
||||
|
||||
dyn->channels = HashTable_New(FALSE);
|
||||
if (!dyn->channels)
|
||||
goto fail;
|
||||
|
||||
if (!HashTable_SetHashFunction(dyn->channels, ChannelId_Hash))
|
||||
goto fail;
|
||||
|
||||
{
|
||||
wObject* kobj = HashTable_KeyObject(dyn->channels);
|
||||
WINPR_ASSERT(kobj);
|
||||
kobj->fnObjectEquals = ChannelId_Compare;
|
||||
}
|
||||
|
||||
{
|
||||
wObject* vobj = HashTable_ValueObject(dyn->channels);
|
||||
WINPR_ASSERT(vobj);
|
||||
vobj->fnObjectFree = DynamicChannelContext_free;
|
||||
}
|
||||
|
||||
return dyn;
|
||||
|
||||
fail:
|
||||
DynChannelContext_free(dyn);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult pf_dynvc_back_data(proxyData* pdata,
|
||||
const pServerStaticChannelContext* channel,
|
||||
const BYTE* xdata, size_t xsize, UINT32 flags,
|
||||
size_t totalSize)
|
||||
{
|
||||
WINPR_ASSERT(channel);
|
||||
|
||||
DynChannelContext* dyn = (DynChannelContext*)channel->context;
|
||||
WINPR_UNUSED(pdata);
|
||||
WINPR_ASSERT(dyn);
|
||||
|
||||
return channelTracker_update(dyn->backTracker, xdata, xsize, flags, totalSize);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult pf_dynvc_front_data(proxyData* pdata,
|
||||
const pServerStaticChannelContext* channel,
|
||||
const BYTE* xdata, size_t xsize, UINT32 flags,
|
||||
size_t totalSize)
|
||||
{
|
||||
WINPR_ASSERT(channel);
|
||||
|
||||
DynChannelContext* dyn = (DynChannelContext*)channel->context;
|
||||
WINPR_UNUSED(pdata);
|
||||
WINPR_ASSERT(dyn);
|
||||
|
||||
return channelTracker_update(dyn->frontTracker, xdata, xsize, flags, totalSize);
|
||||
}
|
||||
|
||||
BOOL pf_channel_setup_drdynvc(proxyData* pdata, pServerStaticChannelContext* channel)
|
||||
{
|
||||
DynChannelContext* ret = DynChannelContext_new(pdata, channel);
|
||||
if (!ret)
|
||||
return FALSE;
|
||||
|
||||
channel->onBackData = pf_dynvc_back_data;
|
||||
channel->onFrontData = pf_dynvc_front_data;
|
||||
channel->contextDtor = DynChannelContext_free;
|
||||
channel->context = ret;
|
||||
return TRUE;
|
||||
}
|
||||
27
third_party/FreeRDP/server/proxy/channels/pf_channel_drdynvc.h
vendored
Normal file
27
third_party/FreeRDP/server/proxy/channels/pf_channel_drdynvc.h
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* pf_channel_drdynvc
|
||||
*
|
||||
* Copyright 2022 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef SERVER_PROXY_CHANNELS_PF_CHANNEL_DRDYNVC_H_
|
||||
#define SERVER_PROXY_CHANNELS_PF_CHANNEL_DRDYNVC_H_
|
||||
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_setup_drdynvc(proxyData* pdata,
|
||||
pServerStaticChannelContext* channel);
|
||||
|
||||
#endif /* SERVER_PROXY_CHANNELS_PF_CHANNEL_DRDYNVC_H_ */
|
||||
2075
third_party/FreeRDP/server/proxy/channels/pf_channel_rdpdr.c
vendored
Normal file
2075
third_party/FreeRDP/server/proxy/channels/pf_channel_rdpdr.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
50
third_party/FreeRDP/server/proxy/channels/pf_channel_rdpdr.h
vendored
Normal file
50
third_party/FreeRDP/server/proxy/channels/pf_channel_rdpdr.h
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server
|
||||
*
|
||||
* Copyright 2021 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_PROXY_RDPDR_H
|
||||
#define FREERDP_SERVER_PROXY_RDPDR_H
|
||||
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_setup_rdpdr(pServerContext* ps,
|
||||
pServerStaticChannelContext* channel);
|
||||
|
||||
void pf_channel_rdpdr_client_free(pClientContext* pc);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_rdpdr_client_new(pClientContext* pc);
|
||||
|
||||
BOOL pf_channel_rdpdr_client_reset(pClientContext* pc);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_rdpdr_client_handle(pClientContext* pc, UINT16 channelId,
|
||||
const char* channel_name,
|
||||
const BYTE* xdata, size_t xsize,
|
||||
UINT32 flags, size_t totalSize);
|
||||
|
||||
void pf_channel_rdpdr_server_free(pServerContext* ps);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_rdpdr_server_new(pServerContext* ps);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_rdpdr_server_announce(pServerContext* ps);
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_rdpdr_server_handle(pServerContext* ps, UINT16 channelId,
|
||||
const char* channel_name,
|
||||
const BYTE* xdata, size_t xsize,
|
||||
UINT32 flags, size_t totalSize);
|
||||
|
||||
#endif /* FREERDP_SERVER_PROXY_RDPDR_H */
|
||||
394
third_party/FreeRDP/server/proxy/channels/pf_channel_smartcard.c
vendored
Normal file
394
third_party/FreeRDP/server/proxy/channels/pf_channel_smartcard.c
vendored
Normal file
@@ -0,0 +1,394 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server
|
||||
*
|
||||
* Copyright 2021 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2021 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 <winpr/assert.h>
|
||||
#include <winpr/string.h>
|
||||
|
||||
#include <winpr/smartcard.h>
|
||||
#include <winpr/pool.h>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_log.h>
|
||||
#include <freerdp/emulate/scard/smartcard_emulate.h>
|
||||
#include <freerdp/channels/scard.h>
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
#include <freerdp/utils/rdpdr_utils.h>
|
||||
|
||||
#include <freerdp/utils/smartcard_operations.h>
|
||||
#include <freerdp/utils/smartcard_call.h>
|
||||
|
||||
#include "pf_channel_smartcard.h"
|
||||
#include "pf_channel_rdpdr.h"
|
||||
|
||||
#define TAG PROXY_TAG("channel.scard")
|
||||
|
||||
#define SCARD_SVC_CHANNEL_NAME "SCARD"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
InterceptContextMapEntry base;
|
||||
scard_call_context* callctx;
|
||||
wArrayList* workObjects;
|
||||
} pf_channel_client_context;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SMARTCARD_OPERATION op;
|
||||
wStream* out;
|
||||
pClientContext* pc;
|
||||
wLog* log;
|
||||
pf_scard_send_fkt_t send_fkt;
|
||||
} pf_channel_client_queue_element;
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static pf_channel_client_context* scard_get_client_context(pClientContext* pc)
|
||||
{
|
||||
pf_channel_client_context* scard = nullptr;
|
||||
|
||||
WINPR_ASSERT(pc);
|
||||
WINPR_ASSERT(pc->interceptContextMap);
|
||||
|
||||
scard = HashTable_GetItemValue(pc->interceptContextMap, SCARD_SVC_CHANNEL_NAME);
|
||||
if (!scard)
|
||||
WLog_WARN(TAG, "[%s] missing in pc->interceptContextMap", SCARD_SVC_CHANNEL_NAME);
|
||||
return scard;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_channel_client_write_iostatus(wStream* out, const SMARTCARD_OPERATION* op,
|
||||
NTSTATUS ioStatus)
|
||||
{
|
||||
UINT16 component = 0;
|
||||
UINT16 packetid = 0;
|
||||
UINT32 dID = 0;
|
||||
UINT32 cID = 0;
|
||||
size_t pos = 0;
|
||||
|
||||
WINPR_ASSERT(op);
|
||||
WINPR_ASSERT(out);
|
||||
|
||||
pos = Stream_GetPosition(out);
|
||||
Stream_ResetPosition(out);
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, out, 16))
|
||||
return FALSE;
|
||||
|
||||
Stream_Read_UINT16(out, component);
|
||||
Stream_Read_UINT16(out, packetid);
|
||||
|
||||
Stream_Read_UINT32(out, dID);
|
||||
Stream_Read_UINT32(out, cID);
|
||||
|
||||
WINPR_ASSERT(component == RDPDR_CTYP_CORE);
|
||||
WINPR_ASSERT(packetid == PAKID_CORE_DEVICE_IOCOMPLETION);
|
||||
WINPR_ASSERT(dID == op->deviceID);
|
||||
WINPR_ASSERT(cID == op->completionID);
|
||||
|
||||
Stream_Write_INT32(out, ioStatus);
|
||||
Stream_SetPosition(out, pos);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
struct thread_arg
|
||||
{
|
||||
pf_channel_client_context* scard;
|
||||
pf_channel_client_queue_element* e;
|
||||
};
|
||||
|
||||
static void queue_free(void* obj);
|
||||
|
||||
WINPR_ATTR_MALLOC(queue_free, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
static void* queue_copy(const void* obj);
|
||||
|
||||
static VOID irp_thread(WINPR_ATTR_UNUSED PTP_CALLBACK_INSTANCE Instance, PVOID Context,
|
||||
PTP_WORK Work)
|
||||
{
|
||||
struct thread_arg* arg = Context;
|
||||
pf_channel_client_context* scard = arg->scard;
|
||||
{
|
||||
NTSTATUS ioStatus = 0;
|
||||
LONG rc = smartcard_irp_device_control_call(arg->scard->callctx, arg->e->out, &ioStatus,
|
||||
&arg->e->op);
|
||||
if (rc == CHANNEL_RC_OK)
|
||||
{
|
||||
if (pf_channel_client_write_iostatus(arg->e->out, &arg->e->op, ioStatus))
|
||||
arg->e->send_fkt(arg->e->log, arg->e->pc, arg->e->out);
|
||||
}
|
||||
}
|
||||
queue_free(arg->e);
|
||||
free(arg);
|
||||
ArrayList_Remove(scard->workObjects, Work);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL start_irp_thread(pf_channel_client_context* scard,
|
||||
const pf_channel_client_queue_element* e)
|
||||
{
|
||||
PTP_WORK work = nullptr;
|
||||
struct thread_arg* arg = calloc(1, sizeof(struct thread_arg));
|
||||
if (!arg)
|
||||
return FALSE;
|
||||
arg->scard = scard;
|
||||
arg->e = queue_copy(e);
|
||||
if (!arg->e)
|
||||
goto fail;
|
||||
|
||||
work = CreateThreadpoolWork(irp_thread, arg, nullptr);
|
||||
if (!work)
|
||||
goto fail;
|
||||
ArrayList_Append(scard->workObjects, work);
|
||||
SubmitThreadpoolWork(work);
|
||||
|
||||
return TRUE;
|
||||
|
||||
fail:
|
||||
if (arg)
|
||||
queue_free(arg->e);
|
||||
free(arg);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL pf_channel_smartcard_client_handle(wLog* log, pClientContext* pc, wStream* s, wStream* out,
|
||||
pf_scard_send_fkt_t send_fkt)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
LONG status = 0;
|
||||
UINT32 FileId = 0;
|
||||
UINT32 CompletionId = 0;
|
||||
NTSTATUS ioStatus = 0;
|
||||
pf_channel_client_queue_element e = WINPR_C_ARRAY_INIT;
|
||||
pf_channel_client_context* scard = scard_get_client_context(pc);
|
||||
|
||||
WINPR_ASSERT(log);
|
||||
WINPR_ASSERT(send_fkt);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
if (!scard)
|
||||
return FALSE;
|
||||
|
||||
e.log = log;
|
||||
e.pc = pc;
|
||||
e.out = out;
|
||||
e.send_fkt = send_fkt;
|
||||
|
||||
/* Skip IRP header */
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
|
||||
return FALSE;
|
||||
else
|
||||
{
|
||||
const uint32_t DeviceId = Stream_Get_UINT32(s); /* DeviceId (4 bytes) */
|
||||
FileId = Stream_Get_UINT32(s); /* FileId (4 bytes) */
|
||||
CompletionId = Stream_Get_UINT32(s); /* CompletionId (4 bytes) */
|
||||
const uint32_t MajorFunction = Stream_Get_UINT32(s); /* MajorFunction (4 bytes) */
|
||||
const uint32_t MinorFunction = Stream_Get_UINT32(s); /* MinorFunction (4 bytes) */
|
||||
|
||||
if (MajorFunction != IRP_MJ_DEVICE_CONTROL)
|
||||
{
|
||||
WLog_WARN(TAG, "[%s] Invalid IRP received, expected %s, got %s [0x%08" PRIx32 "]",
|
||||
SCARD_SVC_CHANNEL_NAME, rdpdr_irp_string(IRP_MJ_DEVICE_CONTROL),
|
||||
rdpdr_irp_string(MajorFunction), MinorFunction);
|
||||
return FALSE;
|
||||
}
|
||||
e.op.completionID = CompletionId;
|
||||
e.op.deviceID = DeviceId;
|
||||
|
||||
if (!rdpdr_write_iocompletion_header(out, DeviceId, CompletionId, 0))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
status = smartcard_irp_device_control_decode(s, CompletionId, FileId, &e.op);
|
||||
if (status != 0)
|
||||
goto fail;
|
||||
|
||||
switch (e.op.ioControlCode)
|
||||
{
|
||||
case SCARD_IOCTL_LISTREADERGROUPSA:
|
||||
case SCARD_IOCTL_LISTREADERGROUPSW:
|
||||
case SCARD_IOCTL_LISTREADERSA:
|
||||
case SCARD_IOCTL_LISTREADERSW:
|
||||
case SCARD_IOCTL_LOCATECARDSA:
|
||||
case SCARD_IOCTL_LOCATECARDSW:
|
||||
case SCARD_IOCTL_LOCATECARDSBYATRA:
|
||||
case SCARD_IOCTL_LOCATECARDSBYATRW:
|
||||
case SCARD_IOCTL_GETSTATUSCHANGEA:
|
||||
case SCARD_IOCTL_GETSTATUSCHANGEW:
|
||||
case SCARD_IOCTL_CONNECTA:
|
||||
case SCARD_IOCTL_CONNECTW:
|
||||
case SCARD_IOCTL_RECONNECT:
|
||||
case SCARD_IOCTL_DISCONNECT:
|
||||
case SCARD_IOCTL_BEGINTRANSACTION:
|
||||
case SCARD_IOCTL_ENDTRANSACTION:
|
||||
case SCARD_IOCTL_STATE:
|
||||
case SCARD_IOCTL_STATUSA:
|
||||
case SCARD_IOCTL_STATUSW:
|
||||
case SCARD_IOCTL_TRANSMIT:
|
||||
case SCARD_IOCTL_CONTROL:
|
||||
case SCARD_IOCTL_GETATTRIB:
|
||||
case SCARD_IOCTL_SETATTRIB:
|
||||
if (!start_irp_thread(scard, &e))
|
||||
goto fail;
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
status = smartcard_irp_device_control_call(scard->callctx, out, &ioStatus, &e.op);
|
||||
if (status != 0)
|
||||
goto fail;
|
||||
if (!pf_channel_client_write_iostatus(out, &e.op, ioStatus))
|
||||
goto fail;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = send_fkt(log, pc, out) == CHANNEL_RC_OK;
|
||||
|
||||
fail:
|
||||
smartcard_operation_free(&e.op, FALSE);
|
||||
return rc;
|
||||
}
|
||||
|
||||
BOOL pf_channel_smartcard_server_handle(WINPR_ATTR_UNUSED pServerContext* ps,
|
||||
WINPR_ATTR_UNUSED wStream* s)
|
||||
{
|
||||
WLog_ERR(TAG, "TODO: unimplemented");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void channel_stop_and_wait(pf_channel_client_context* scard, BOOL reset)
|
||||
{
|
||||
WINPR_ASSERT(scard);
|
||||
smartcard_call_context_signal_stop(scard->callctx, FALSE);
|
||||
|
||||
while (ArrayList_Count(scard->workObjects) > 0)
|
||||
{
|
||||
PTP_WORK work = ArrayList_GetItem(scard->workObjects, 0);
|
||||
if (!work)
|
||||
continue;
|
||||
WaitForThreadpoolWorkCallbacks(work, TRUE);
|
||||
}
|
||||
|
||||
smartcard_call_context_signal_stop(scard->callctx, reset);
|
||||
}
|
||||
|
||||
static void pf_channel_scard_client_context_free(InterceptContextMapEntry* base)
|
||||
{
|
||||
pf_channel_client_context* entry = (pf_channel_client_context*)base;
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
/* Set the stop event.
|
||||
* All threads waiting in blocking operations will abort at the next
|
||||
* available polling slot */
|
||||
channel_stop_and_wait(entry, FALSE);
|
||||
ArrayList_Free(entry->workObjects);
|
||||
|
||||
smartcard_call_context_free(entry->callctx);
|
||||
free(entry);
|
||||
}
|
||||
|
||||
static void queue_free(void* obj)
|
||||
{
|
||||
pf_channel_client_queue_element* element = obj;
|
||||
if (!element)
|
||||
return;
|
||||
smartcard_operation_free(&element->op, FALSE);
|
||||
Stream_Free(element->out, TRUE);
|
||||
free(element);
|
||||
}
|
||||
|
||||
WINPR_ATTR_MALLOC(queue_free, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
static void* queue_copy(const void* obj)
|
||||
{
|
||||
const pf_channel_client_queue_element* other = obj;
|
||||
pf_channel_client_queue_element* copy = nullptr;
|
||||
if (!other)
|
||||
return nullptr;
|
||||
copy = calloc(1, sizeof(pf_channel_client_queue_element));
|
||||
if (!copy)
|
||||
return nullptr;
|
||||
|
||||
*copy = *other;
|
||||
copy->out = Stream_New(nullptr, Stream_Capacity(other->out));
|
||||
if (!copy->out)
|
||||
goto fail;
|
||||
Stream_Write(copy->out, Stream_Buffer(other->out), Stream_GetPosition(other->out));
|
||||
return copy;
|
||||
fail:
|
||||
queue_free(copy);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void work_object_free(void* arg)
|
||||
{
|
||||
PTP_WORK work = arg;
|
||||
CloseThreadpoolWork(work);
|
||||
}
|
||||
|
||||
BOOL pf_channel_smartcard_client_new(pClientContext* pc)
|
||||
{
|
||||
pf_channel_client_context* scard = nullptr;
|
||||
wObject* obj = nullptr;
|
||||
|
||||
WINPR_ASSERT(pc);
|
||||
WINPR_ASSERT(pc->interceptContextMap);
|
||||
|
||||
scard = calloc(1, sizeof(pf_channel_client_context));
|
||||
if (!scard)
|
||||
return FALSE;
|
||||
scard->base.free = pf_channel_scard_client_context_free;
|
||||
scard->callctx = smartcard_call_context_new(pc->context.settings);
|
||||
if (!scard->callctx)
|
||||
goto fail;
|
||||
|
||||
scard->workObjects = ArrayList_New(TRUE);
|
||||
if (!scard->workObjects)
|
||||
goto fail;
|
||||
obj = ArrayList_Object(scard->workObjects);
|
||||
WINPR_ASSERT(obj);
|
||||
obj->fnObjectFree = work_object_free;
|
||||
|
||||
return HashTable_Insert(pc->interceptContextMap, SCARD_SVC_CHANNEL_NAME, scard);
|
||||
fail:
|
||||
pf_channel_scard_client_context_free(&scard->base);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void pf_channel_smartcard_client_free(pClientContext* pc)
|
||||
{
|
||||
WINPR_ASSERT(pc);
|
||||
WINPR_ASSERT(pc->interceptContextMap);
|
||||
HashTable_Remove(pc->interceptContextMap, SCARD_SVC_CHANNEL_NAME);
|
||||
}
|
||||
|
||||
BOOL pf_channel_smartcard_client_emulate(pClientContext* pc)
|
||||
{
|
||||
pf_channel_client_context* scard = scard_get_client_context(pc);
|
||||
if (!scard)
|
||||
return FALSE;
|
||||
return smartcard_call_is_configured(scard->callctx);
|
||||
}
|
||||
|
||||
BOOL pf_channel_smartcard_client_reset(pClientContext* pc)
|
||||
{
|
||||
pf_channel_client_context* scard = scard_get_client_context(pc);
|
||||
if (!scard)
|
||||
return TRUE;
|
||||
|
||||
channel_stop_and_wait(scard, TRUE);
|
||||
return TRUE;
|
||||
}
|
||||
40
third_party/FreeRDP/server/proxy/channels/pf_channel_smartcard.h
vendored
Normal file
40
third_party/FreeRDP/server/proxy/channels/pf_channel_smartcard.h
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server
|
||||
*
|
||||
* Copyright 2021 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_PROXY_SCARD_H
|
||||
#define FREERDP_SERVER_PROXY_SCARD_H
|
||||
|
||||
#include <winpr/wlog.h>
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
typedef UINT (*pf_scard_send_fkt_t)(wLog* log, pClientContext*, wStream*);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_smartcard_client_new(pClientContext* pc);
|
||||
void pf_channel_smartcard_client_free(pClientContext* pc);
|
||||
|
||||
BOOL pf_channel_smartcard_client_reset(pClientContext* pc);
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_smartcard_client_emulate(pClientContext* pc);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_smartcard_client_handle(wLog* log, pClientContext* pc,
|
||||
wStream* s, wStream* out,
|
||||
pf_scard_send_fkt_t fkt);
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_smartcard_server_handle(pServerContext* ps, wStream* s);
|
||||
|
||||
#endif /* FREERDP_SERVER_PROXY_SCARD_H */
|
||||
29
third_party/FreeRDP/server/proxy/cli/CMakeLists.txt
vendored
Normal file
29
third_party/FreeRDP/server/proxy/cli/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP Proxy Server
|
||||
#
|
||||
# Copyright 2021 Armin Novak <armin.novak@thincast.com>
|
||||
# Copyright 2021 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.
|
||||
|
||||
set(PROXY_APP_SRCS freerdp_proxy.c)
|
||||
|
||||
set(APP_NAME "freerdp-proxy")
|
||||
addtargetwithresourcefile(${APP_NAME} TRUE "${FREERDP_VERSION}" PROXY_APP_SRCS)
|
||||
|
||||
target_link_libraries(${APP_NAME} ${MODULE_NAME})
|
||||
installwithrpath(TARGETS ${APP_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT server)
|
||||
|
||||
set_property(TARGET ${APP_NAME} PROPERTY FOLDER "Server/proxy")
|
||||
|
||||
generate_and_install_freerdp_man_from_template(${APP_NAME} "1" "${FREERDP_API_VERSION}")
|
||||
85
third_party/FreeRDP/server/proxy/cli/freerdp-proxy.1.in
vendored
Normal file
85
third_party/FreeRDP/server/proxy/cli/freerdp-proxy.1.in
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
.de URL
|
||||
\\$2 \(laURL: \\$1 \(ra\\$3
|
||||
..
|
||||
.if \n[.g] .mso www.tmac
|
||||
.TH @MANPAGE_NAME@ 1 2023-12-14 "@FREERDP_VERSION_FULL@" "FreeRDP"
|
||||
.SH NAME
|
||||
@MANPAGE_NAME@ \- A server binary allowing MITM proxying of RDP connections
|
||||
.SH SYNOPSIS
|
||||
.B @MANPAGE_NAME@
|
||||
[\fB-h\fP]
|
||||
[\fB--help\fP]
|
||||
[\fB--buildconfig\fP]
|
||||
[\fB--dump-config\fP \fB<config file>\fP]
|
||||
[\fB-v\fP]
|
||||
[\fB--version\fP]
|
||||
[\fB<config file>\fP]
|
||||
.SH DESCRIPTION
|
||||
.B @MANPAGE_NAME@
|
||||
can be used to proxy a RDP connection between a target server and connecting clients.
|
||||
Possible usage scenarios are:
|
||||
.IP Proxying
|
||||
Connect outdated/insecure RDP servers from behind a (more secure) proxy
|
||||
.IP Analysis
|
||||
Allow detailed protocol analysis of (many) unknown protocol features (channels)
|
||||
.IP Inspection
|
||||
MITM proxy for session inspection and recording
|
||||
|
||||
.SH OPTIONS
|
||||
.IP -h,--help
|
||||
Display a help text explaining usage.
|
||||
.IP --buildconfig
|
||||
Print the build configuration of the proxy and exit.
|
||||
.IP -v,--version
|
||||
Print the version of the proxy and exit.
|
||||
.IP --dump-config \fB<config-ini-file>\fP
|
||||
Dump a template configuration to \fB<config-ini-file>\fP
|
||||
.IP \fB<config-ini-file>\fP
|
||||
Start the proxy with settings read from \fB<config-ini-file>\fP
|
||||
|
||||
.SH WARNING
|
||||
The proxy does not support authentication out of the box but acts simply as intermediary.
|
||||
Only \fBRDP\fP and \fBTLS\fP security modes are supported, \fBNLA\fP will fail for connections to the proxy.
|
||||
To implement authentication a \fBproxy-module\fP can be implemented that can authenticate against some backend
|
||||
and map connecting users and credentials to target server users and credentials.
|
||||
|
||||
.SH EXAMPLES
|
||||
@MANPAGE_NAME@ /some/config/file
|
||||
|
||||
@MANPAGE_NAME@ --dump-config /some/config/file
|
||||
|
||||
.SH PREPARATIONS
|
||||
|
||||
1. generate certificates for proxy
|
||||
|
||||
\fBwinpr-makecert -rdp -path . proxy\fP
|
||||
|
||||
2. generate proxy configuration
|
||||
|
||||
\fB@MANPAGE_NAME@ --dump-config proxy.ini\fP
|
||||
|
||||
3. edit configurartion and:
|
||||
|
||||
* provide (preferably absolute) paths for \fBCertificateFile\fP and \fBPrivateKeyFile\fP generated previously
|
||||
* remove the \fBCertificateContents\fP and \fBPrivateKeyContents\fP
|
||||
* Adjust the \fB[Server]\fP settings \fBHost\fP and \fBPort\fP to bind a specific port on a network interface
|
||||
* Adjust the \fB[Target]\fP \fBHost\fP and \fBPort\fP settings to the \fBRDP\fP target server
|
||||
* Adjust (or remove if unuse) the \fBPlugins\fP settings
|
||||
|
||||
3. start proxy server
|
||||
|
||||
\fB@MANPAGE_NAME@ proxy.ini\fP
|
||||
|
||||
.SH EXIT STATUS
|
||||
.TP
|
||||
.B 0
|
||||
Successful program execution.
|
||||
.TP
|
||||
.B 1
|
||||
Otherwise.
|
||||
|
||||
.SH SEE ALSO
|
||||
wlog(7)
|
||||
|
||||
.SH AUTHOR
|
||||
FreeRDP <team@freerdp.com>
|
||||
193
third_party/FreeRDP/server/proxy/cli/freerdp_proxy.c
vendored
Normal file
193
third_party/FreeRDP/server/proxy/cli/freerdp_proxy.c
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server
|
||||
*
|
||||
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
|
||||
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
|
||||
*
|
||||
* 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 <winpr/collections.h>
|
||||
|
||||
#include <freerdp/version.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_server.h>
|
||||
#include <freerdp/server/proxy/proxy_log.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define TAG PROXY_TAG("server")
|
||||
|
||||
static proxyServer* server = nullptr;
|
||||
|
||||
#if defined(_WIN32)
|
||||
WINPR_ATTR_NODISCARD
|
||||
static const char* strsignal(int signum)
|
||||
{
|
||||
switch (signum)
|
||||
{
|
||||
case SIGINT:
|
||||
return "SIGINT";
|
||||
case SIGTERM:
|
||||
return "SIGTERM";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// NOLINTBEGIN(bugprone-signal-handler,cert-msc54-cpp,cert-sig30-c)
|
||||
static void cleanup_handler(int signum)
|
||||
{
|
||||
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
||||
WLog_INFO(TAG, "caught signal %s [%d], starting cleanup...", strsignal(signum), signum);
|
||||
|
||||
WLog_INFO(TAG, "stopping all connections.");
|
||||
pf_server_stop(server);
|
||||
}
|
||||
// NOLINTEND(bugprone-signal-handler,cert-msc54-cpp,cert-sig30-c)
|
||||
|
||||
static void pf_server_register_signal_handlers(void)
|
||||
{
|
||||
(void)signal(SIGINT, cleanup_handler);
|
||||
(void)signal(SIGTERM, cleanup_handler);
|
||||
#ifndef _WIN32
|
||||
(void)signal(SIGQUIT, cleanup_handler);
|
||||
(void)signal(SIGKILL, cleanup_handler);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int usage(const char* app)
|
||||
{
|
||||
printf("Usage:\n");
|
||||
printf("%s -h Display this help text.\n", app);
|
||||
printf("%s --help Display this help text.\n", app);
|
||||
printf("%s --buildconfig Print the build configuration.\n", app);
|
||||
printf("%s <config ini file> Start the proxy with <config.ini>\n", app);
|
||||
printf("%s --dump-config <config ini file> Create a template <config.ini>\n", app);
|
||||
printf("%s -v Print out binary version.\n", app);
|
||||
printf("%s --version Print out binary version.\n", app);
|
||||
return 0;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static int version(const char* app)
|
||||
{
|
||||
printf("%s version %s", app, freerdp_get_version_string());
|
||||
return 0;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static int buildconfig(WINPR_ATTR_UNUSED const char* app)
|
||||
{
|
||||
printf("This is FreeRDP version %s (%s)\n", FREERDP_VERSION_FULL, FREERDP_GIT_REVISION);
|
||||
printf("%s", freerdp_get_build_config());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int status = -1;
|
||||
|
||||
pf_server_register_signal_handlers();
|
||||
|
||||
WLog_INFO(TAG, "freerdp-proxy version info:");
|
||||
WLog_INFO(TAG, "\tFreeRDP version: %s", FREERDP_VERSION_FULL);
|
||||
WLog_INFO(TAG, "\tGit commit: %s", FREERDP_GIT_REVISION);
|
||||
WLog_DBG(TAG, "\tBuild config: %s", freerdp_get_build_config());
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
status = usage(argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
{
|
||||
const char* arg = argv[1];
|
||||
if (_stricmp(arg, "-h") == 0)
|
||||
{
|
||||
status = usage(argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
else if (_stricmp(arg, "--help") == 0)
|
||||
{
|
||||
status = usage(argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
else if (_stricmp(arg, "--buildconfig") == 0)
|
||||
{
|
||||
status = buildconfig(argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
else if (_stricmp(arg, "--dump-config") == 0)
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
status = usage(argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
status = pf_server_config_dump(argv[2]) ? 0 : -1;
|
||||
goto fail;
|
||||
}
|
||||
else if (_stricmp(arg, "-v") == 0)
|
||||
{
|
||||
status = version(argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
else if (_stricmp(arg, "--version") == 0)
|
||||
{
|
||||
status = version(argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const char* config_path = argv[1];
|
||||
if (argc != 2)
|
||||
{
|
||||
status = usage(argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
{
|
||||
proxyConfig* config = pf_server_config_load_file(config_path);
|
||||
if (!config)
|
||||
goto fail;
|
||||
|
||||
pf_server_config_print(config);
|
||||
|
||||
server = pf_server_new(config);
|
||||
pf_server_config_free(config);
|
||||
}
|
||||
}
|
||||
|
||||
if (!server)
|
||||
goto fail;
|
||||
|
||||
if (!pf_server_start(server))
|
||||
goto fail;
|
||||
|
||||
if (!pf_server_run(server))
|
||||
goto fail;
|
||||
|
||||
status = 0;
|
||||
|
||||
fail:
|
||||
pf_server_free(server);
|
||||
|
||||
return status;
|
||||
}
|
||||
15
third_party/FreeRDP/server/proxy/freerdp-proxy.pc.in
vendored
Normal file
15
third_party/FreeRDP/server/proxy/freerdp-proxy.pc.in
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
prefix=@PKG_CONFIG_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||
includedir=${prefix}/@FREERDP_INCLUDE_DIR@
|
||||
libs=-lfreerdp-server-proxy@FREERDP_API_VERSION@
|
||||
|
||||
Name: FreeRDP proxy
|
||||
Description: FreeRDP: A Remote Desktop Protocol Implementation
|
||||
URL: http://www.freerdp.com/
|
||||
Version: @FREERDP_VERSION@
|
||||
Requires: @FREERDP_PROXY_PC_REQUIRES@
|
||||
Requires.private: @FREERDP_PROXY_PC_REQUIRES_PRIVATE@
|
||||
Libs: -L${libdir} ${libs}
|
||||
Libs.private: @FREERDP_PROXY_PC_LIBS_PRIVATE@
|
||||
Cflags: -I${includedir}
|
||||
33
third_party/FreeRDP/server/proxy/modules/CMakeLists.txt
vendored
Normal file
33
third_party/FreeRDP/server/proxy/modules/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
# Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
#
|
||||
# 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.
|
||||
|
||||
# The third-party directory is meant for third-party components to be built
|
||||
# as part of the main FreeRDP build system, making separate maintenance easier.
|
||||
# Subdirectories of the third-party directory are ignored by git, but are
|
||||
# automatically included by CMake when the -DWITH_THIRD_PARTY=on option is used.
|
||||
|
||||
# include proxy header files for proxy modules
|
||||
include_directories("${PROJECT_SOURCE_DIR}/server/proxy")
|
||||
include_directories("${PROJECT_SOURCE_DIR}/server/proxy/modules")
|
||||
|
||||
# taken from FreeRDP/third-party/CMakeLists.txt
|
||||
file(GLOB all_valid_subdirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*/CMakeLists.txt")
|
||||
|
||||
foreach(dir ${all_valid_subdirs})
|
||||
if(${dir} MATCHES "^([^/]*)/+CMakeLists.txt")
|
||||
string(REGEX REPLACE "^([^/]*)/+CMakeLists.txt" "\\1" dir_trimmed ${dir})
|
||||
message(STATUS "Adding proxy module ${dir_trimmed}")
|
||||
add_subdirectory(${dir_trimmed})
|
||||
endif()
|
||||
endforeach(dir)
|
||||
66
third_party/FreeRDP/server/proxy/modules/README.md
vendored
Normal file
66
third_party/FreeRDP/server/proxy/modules/README.md
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
# Proxy module API
|
||||
|
||||
`freerdp-proxy` has an API for hooking/filtering certain events/messages.
|
||||
A module can register callbacks to events, allowing to record the data and control whether to pass/ignore, or right out drop the connection.
|
||||
|
||||
During startup, the proxy reads its modules from the configuration:
|
||||
|
||||
```ini
|
||||
[Plugins]
|
||||
Modules = demo,cap
|
||||
```
|
||||
|
||||
These modules are loaded in a best effort manner. Additionally there is a configuration field for modules that must be loaded,
|
||||
so the proxy refuses to start if they are not found:
|
||||
|
||||
```ini
|
||||
[Plugins]
|
||||
Required = demo,cap
|
||||
```
|
||||
|
||||
Modules must be installed as shared libraries in the `<base install>/lib/freerdp3/proxy` folder and match the pattern
|
||||
`proxy-<name>-plugin.<ext>` (e.g. `proxy-demo-plugin.so`) to be found.
|
||||
For security reasons loading by full path is not supported and only the installation path is used for lookup.
|
||||
|
||||
## Currently supported hook events
|
||||
|
||||
### Client
|
||||
|
||||
* ClientInitConnect: Called before the client tries to open a connection
|
||||
* ClientUninitConnect: Called after the client has disconnected
|
||||
* ClientPreConnect: Called in client PreConnect callback
|
||||
* ClientPostConnect: Called in client PostConnect callback
|
||||
* ClientPostDisconnect: Called in client PostDisconnect callback
|
||||
* ClientX509Certificate: Called in client X509 certificate verification callback
|
||||
* ClientLoginFailure: Called in client login failure callback
|
||||
* ClientEndPaint: Called in client EndPaint callback
|
||||
|
||||
### Server
|
||||
|
||||
* ServerPostConnect: Called after a client has connected
|
||||
* ServerPeerActivate: Called after a client has activated
|
||||
* ServerChannelsInit: Called after channels are initialized
|
||||
* ServerChannelsFree: Called after channels are cleaned up
|
||||
* ServerSessionEnd: Called after the client connection disconnected
|
||||
|
||||
## Currently supported filter events
|
||||
|
||||
* KeyboardEvent: Keyboard event, e.g. all key press and release events
|
||||
* MouseEvent: Mouse event, e.g. mouse movement and button press/release events
|
||||
* ClientChannelData: Client static channel data
|
||||
* ServerChannelData: Server static channel data
|
||||
* DynamicChannelCreate: Dynamic channel create
|
||||
* ServerFetchTargetAddr: Fetch target address (e.g. RDP TargetInfo)
|
||||
* ServerPeerLogon: A peer is logging on
|
||||
|
||||
## Developing a new module
|
||||
* Create a new file that includes `freerdp/server/proxy/proxy_modules_api.h`.
|
||||
* Implement the `proxy_module_entry_point` function and register the callbacks you are interested in.
|
||||
* Each callback receives two parameters:
|
||||
* `connectionInfo* info` holds connection info of the raised event.
|
||||
* `void* param` holds the actual event data. It should be casted by the filter to the suitable struct from `filters_api.h`.
|
||||
* Each callback must return a `BOOL`:
|
||||
* `FALSE`: The event will not be proxied.
|
||||
* `TRUE`: The event will be proxied.
|
||||
|
||||
A demo can be found in `filter_demo.c`.
|
||||
50
third_party/FreeRDP/server/proxy/modules/bitmap-filter/CMakeLists.txt
vendored
Normal file
50
third_party/FreeRDP/server/proxy/modules/bitmap-filter/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
#
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP Proxy Server Demo C++ Module
|
||||
#
|
||||
# Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
# Copyright 2021 Armin Novak <anovak@thincast.com>
|
||||
# Copyright 2021 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.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
if(POLICY CMP0091)
|
||||
cmake_policy(SET CMP0091 NEW)
|
||||
endif()
|
||||
if(NOT FREERDP_DEFAULT_PROJECT_VERSION)
|
||||
set(FREERDP_DEFAULT_PROJECT_VERSION "1.0.0.0")
|
||||
endif()
|
||||
|
||||
project(proxy-bitmap-filter-plugin VERSION ${FREERDP_DEFAULT_PROJECT_VERSION} LANGUAGES CXX)
|
||||
|
||||
message("project ${PROJECT_NAME} is using version ${PROJECT_VERSION}")
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../../../cmake/)
|
||||
include(ProjectCXXStandard)
|
||||
include(CommonConfigOptions)
|
||||
include(CXXCompilerFlags)
|
||||
|
||||
set(SRCS bitmap-filter.cpp)
|
||||
addtargetwithresourcefile(${PROJECT_NAME} FALSE "${PROJECT_VERSION}" SRCS FALSE)
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE $<$<BOOL:${BUILD_SHARED_LIBS}>:BUILD_SHARED_LIBS>)
|
||||
target_link_libraries(${PROJECT_NAME} winpr freerdp)
|
||||
|
||||
installwithrpath(TARGETS ${PROJECT_NAME} DESTINATION ${FREERDP_PROXY_PLUGINDIR})
|
||||
|
||||
set(PROJECT_PC_REQUIRES_PRIVATE "winpr${FREERDP_API_VERSION} freerdp${FREERDP_API_VERSION}")
|
||||
include(ProxyModuleConfig)
|
||||
generate_proxy_module_config()
|
||||
504
third_party/FreeRDP/server/proxy/modules/bitmap-filter/bitmap-filter.cpp
vendored
Normal file
504
third_party/FreeRDP/server/proxy/modules/bitmap-filter/bitmap-filter.cpp
vendored
Normal file
@@ -0,0 +1,504 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server persist-bitmap-filter Module
|
||||
*
|
||||
* this module is designed to deactivate all persistent bitmap cache settings a
|
||||
* client might send.
|
||||
*
|
||||
* Copyright 2023 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2023 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 <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_modules_api.h>
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
#include <freerdp/channels/drdynvc.h>
|
||||
#include <freerdp/channels/rdpgfx.h>
|
||||
#include <freerdp/utils/gfx.h>
|
||||
|
||||
#define TAG MODULE_TAG("persist-bitmap-filter")
|
||||
|
||||
// #define REPLY_WITH_EMPTY_OFFER
|
||||
|
||||
static constexpr char plugin_name[] = "bitmap-filter";
|
||||
static constexpr char plugin_desc[] =
|
||||
"this plugin deactivates and filters persistent bitmap cache.";
|
||||
|
||||
[[nodiscard]] static const std::vector<std::string>& plugin_static_intercept()
|
||||
{
|
||||
static std::vector<std::string> vec;
|
||||
if (vec.empty())
|
||||
vec.emplace_back(DRDYNVC_SVC_CHANNEL_NAME);
|
||||
return vec;
|
||||
}
|
||||
|
||||
[[nodiscard]] static const std::vector<std::string>& plugin_dyn_intercept()
|
||||
{
|
||||
static std::vector<std::string> vec;
|
||||
if (vec.empty())
|
||||
vec.emplace_back(RDPGFX_DVC_CHANNEL_NAME);
|
||||
return vec;
|
||||
}
|
||||
|
||||
class DynChannelState
|
||||
{
|
||||
|
||||
public:
|
||||
[[nodiscard]] bool skip() const
|
||||
{
|
||||
return _toSkip != 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool skip(size_t s)
|
||||
{
|
||||
if (s > _toSkip)
|
||||
_toSkip = 0;
|
||||
else
|
||||
_toSkip -= s;
|
||||
return skip();
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t remaining() const
|
||||
{
|
||||
return _toSkip;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t total() const
|
||||
{
|
||||
return _totalSkipSize;
|
||||
}
|
||||
|
||||
void setSkipSize(size_t len)
|
||||
{
|
||||
_toSkip = _totalSkipSize = len;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool drop() const
|
||||
{
|
||||
return _drop;
|
||||
}
|
||||
|
||||
void setDrop(bool d)
|
||||
{
|
||||
_drop = d;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint32_t channelId() const
|
||||
{
|
||||
return _channelId;
|
||||
}
|
||||
|
||||
void setChannelId(uint32_t id)
|
||||
{
|
||||
_channelId = id;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t _toSkip = 0;
|
||||
size_t _totalSkipSize = 0;
|
||||
bool _drop = false;
|
||||
uint32_t _channelId = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] static BOOL filter_client_pre_connect([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(pdata->pc);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
auto settings = pdata->pc->context.settings;
|
||||
|
||||
/* We do not want persistent bitmap cache to be used with proxy */
|
||||
return freerdp_settings_set_bool(settings, FreeRDP_BitmapCachePersistEnabled, FALSE);
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL filter_dyn_channel_intercept_list([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* arg)
|
||||
{
|
||||
auto data = static_cast<proxyChannelToInterceptData*>(arg);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
auto intercept = std::find(plugin_dyn_intercept().begin(), plugin_dyn_intercept().end(),
|
||||
data->name) != plugin_dyn_intercept().end();
|
||||
if (intercept)
|
||||
data->intercept = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL filter_static_channel_intercept_list([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* arg)
|
||||
{
|
||||
auto data = static_cast<proxyChannelToInterceptData*>(arg);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
auto intercept = std::find(plugin_static_intercept().begin(), plugin_static_intercept().end(),
|
||||
data->name) != plugin_static_intercept().end();
|
||||
if (intercept)
|
||||
data->intercept = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static size_t drdynvc_cblen_to_bytes(UINT8 cbLen)
|
||||
{
|
||||
switch (cbLen)
|
||||
{
|
||||
case 0:
|
||||
return 1;
|
||||
|
||||
case 1:
|
||||
return 2;
|
||||
|
||||
default:
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] static UINT32 drdynvc_read_variable_uint(wStream* s, UINT8 cbLen)
|
||||
{
|
||||
UINT32 val = 0;
|
||||
|
||||
switch (cbLen)
|
||||
{
|
||||
case 0:
|
||||
Stream_Read_UINT8(s, val);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
Stream_Read_UINT16(s, val);
|
||||
break;
|
||||
|
||||
default:
|
||||
Stream_Read_UINT32(s, val);
|
||||
break;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL drdynvc_try_read_header(wStream* s, uint32_t& channelId, size_t& length)
|
||||
{
|
||||
UINT8 value = 0;
|
||||
Stream_ResetPosition(s);
|
||||
if (Stream_GetRemainingLength(s) < 1)
|
||||
return FALSE;
|
||||
Stream_Read_UINT8(s, value);
|
||||
|
||||
const UINT8 cmd = (value & 0xf0) >> 4;
|
||||
const UINT8 Sp = (value & 0x0c) >> 2;
|
||||
const UINT8 cbChId = (value & 0x03);
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case DATA_PDU:
|
||||
case DATA_FIRST_PDU:
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const size_t channelIdLen = drdynvc_cblen_to_bytes(cbChId);
|
||||
if (Stream_GetRemainingLength(s) < channelIdLen)
|
||||
return FALSE;
|
||||
|
||||
channelId = drdynvc_read_variable_uint(s, cbChId);
|
||||
length = Stream_Length(s);
|
||||
if (cmd == DATA_FIRST_PDU)
|
||||
{
|
||||
const size_t dataLen = drdynvc_cblen_to_bytes(Sp);
|
||||
if (Stream_GetRemainingLength(s) < dataLen)
|
||||
return FALSE;
|
||||
|
||||
length = drdynvc_read_variable_uint(s, Sp);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static DynChannelState* filter_get_plugin_data(proxyPlugin* plugin, proxyData* pdata)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
auto mgr = static_cast<proxyPluginsManager*>(plugin->custom);
|
||||
WINPR_ASSERT(mgr);
|
||||
|
||||
WINPR_ASSERT(mgr->GetPluginData);
|
||||
return static_cast<DynChannelState*>(mgr->GetPluginData(mgr, plugin_name, pdata));
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL filter_set_plugin_data(proxyPlugin* plugin, proxyData* pdata,
|
||||
DynChannelState* data)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
auto mgr = static_cast<proxyPluginsManager*>(plugin->custom);
|
||||
WINPR_ASSERT(mgr);
|
||||
|
||||
WINPR_ASSERT(mgr->SetPluginData);
|
||||
return mgr->SetPluginData(mgr, plugin_name, pdata, data);
|
||||
}
|
||||
|
||||
#if defined(REPLY_WITH_EMPTY_OFFER)
|
||||
[[nodiscard]] static UINT8 drdynvc_value_to_cblen(UINT32 value)
|
||||
{
|
||||
if (value <= 0xFF)
|
||||
return 0;
|
||||
if (value <= 0xFFFF)
|
||||
return 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL drdynvc_write_variable_uint(wStream* s, UINT32 value, UINT8 cbLen)
|
||||
{
|
||||
switch (cbLen)
|
||||
{
|
||||
case 0:
|
||||
Stream_Write_UINT8(s, static_cast<UINT8>(value));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
Stream_Write_UINT16(s, static_cast<UINT16>(value));
|
||||
break;
|
||||
|
||||
default:
|
||||
Stream_Write_UINT32(s, value);
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL drdynvc_write_header(wStream* s, UINT32 channelId)
|
||||
{
|
||||
const UINT8 cbChId = drdynvc_value_to_cblen(channelId);
|
||||
const UINT8 value = (DATA_PDU << 4) | cbChId;
|
||||
const size_t len = drdynvc_cblen_to_bytes(cbChId) + 1;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, len))
|
||||
return FALSE;
|
||||
|
||||
Stream_Write_UINT8(s, value);
|
||||
return drdynvc_write_variable_uint(s, value, cbChId);
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL filter_forward_empty_offer(const char* sessionID,
|
||||
proxyDynChannelInterceptData* data,
|
||||
size_t startPosition, UINT32 channelId)
|
||||
{
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
Stream_SetPosition(data->data, startPosition);
|
||||
if (!drdynvc_write_header(data->data, channelId))
|
||||
return FALSE;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(data->data, sizeof(UINT16)))
|
||||
return FALSE;
|
||||
Stream_Write_UINT16(data->data, 0);
|
||||
Stream_SealLength(data->data);
|
||||
|
||||
WLog_INFO(TAG, "[SessionID=%s][%s] forwarding empty %s", sessionID, plugin_name,
|
||||
rdpgfx_get_cmd_id_string(RDPGFX_CMDID_CACHEIMPORTOFFER));
|
||||
data->rewritten = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
[[nodiscard]] static BOOL filter_dyn_channel_intercept(proxyPlugin* plugin, proxyData* pdata,
|
||||
void* arg)
|
||||
{
|
||||
auto data = static_cast<proxyDynChannelInterceptData*>(arg);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
data->result = PF_CHANNEL_RESULT_PASS;
|
||||
if (!data->isBackData &&
|
||||
(strncmp(data->name, RDPGFX_DVC_CHANNEL_NAME, ARRAYSIZE(RDPGFX_DVC_CHANNEL_NAME)) == 0))
|
||||
{
|
||||
auto state = filter_get_plugin_data(plugin, pdata);
|
||||
if (!state)
|
||||
{
|
||||
WLog_ERR(TAG, "[SessionID=%s][%s] missing custom data, aborting!", pdata->session_id,
|
||||
plugin_name);
|
||||
return FALSE;
|
||||
}
|
||||
const size_t inputDataLength = Stream_Length(data->data);
|
||||
UINT16 cmdId = RDPGFX_CMDID_UNUSED_0000;
|
||||
|
||||
const auto pos = Stream_GetPosition(data->data);
|
||||
if (!state->skip())
|
||||
{
|
||||
if (data->first)
|
||||
{
|
||||
uint32_t channelId = 0;
|
||||
size_t length = 0;
|
||||
if (drdynvc_try_read_header(data->data, channelId, length))
|
||||
{
|
||||
if (Stream_GetRemainingLength(data->data) >= 2)
|
||||
{
|
||||
Stream_Read_UINT16(data->data, cmdId);
|
||||
state->setSkipSize(length);
|
||||
state->setDrop(false);
|
||||
}
|
||||
}
|
||||
|
||||
switch (cmdId)
|
||||
{
|
||||
case RDPGFX_CMDID_CACHEIMPORTOFFER:
|
||||
state->setDrop(true);
|
||||
state->setChannelId(channelId);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
Stream_SetPosition(data->data, pos);
|
||||
}
|
||||
}
|
||||
|
||||
if (state->skip())
|
||||
{
|
||||
if (state->skip(inputDataLength))
|
||||
{
|
||||
WLog_DBG(TAG,
|
||||
"skipping data, but %" PRIuz " bytes left [stream has %" PRIuz " bytes]",
|
||||
state->remaining(), inputDataLength);
|
||||
}
|
||||
|
||||
if (state->drop())
|
||||
{
|
||||
WLog_WARN(TAG,
|
||||
"[SessionID=%s][%s] dropping %s packet [total:%" PRIuz ", current:%" PRIuz
|
||||
", remaining: %" PRIuz "]",
|
||||
pdata->session_id, plugin_name,
|
||||
rdpgfx_get_cmd_id_string(RDPGFX_CMDID_CACHEIMPORTOFFER), state->total(),
|
||||
inputDataLength, state->remaining());
|
||||
data->result = PF_CHANNEL_RESULT_DROP;
|
||||
|
||||
#if defined(REPLY_WITH_EMPTY_OFFER) // TODO: Sending this does screw up some windows RDP server
|
||||
// versions :/
|
||||
if (state->remaining() == 0)
|
||||
{
|
||||
if (!filter_forward_empty_offer(pdata->session_id, data, pos,
|
||||
state->channelId()))
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL filter_server_session_started(proxyPlugin* plugin, proxyData* pdata,
|
||||
void* /*unused*/)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
auto state = filter_get_plugin_data(plugin, pdata);
|
||||
delete state;
|
||||
|
||||
auto newstate = new DynChannelState();
|
||||
if (!filter_set_plugin_data(plugin, pdata, newstate))
|
||||
{
|
||||
delete newstate;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL filter_server_session_end(proxyPlugin* plugin, proxyData* pdata,
|
||||
void* /*unused*/)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
auto state = filter_get_plugin_data(plugin, pdata);
|
||||
delete state;
|
||||
return filter_set_plugin_data(plugin, pdata, nullptr);
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL int_proxy_module_entry_point(proxyPluginsManager* plugins_manager,
|
||||
void* userdata)
|
||||
{
|
||||
proxyPlugin plugin = {};
|
||||
|
||||
plugin.name = plugin_name;
|
||||
plugin.description = plugin_desc;
|
||||
|
||||
plugin.ServerSessionStarted = filter_server_session_started;
|
||||
plugin.ServerSessionEnd = filter_server_session_end;
|
||||
|
||||
plugin.ClientPreConnect = filter_client_pre_connect;
|
||||
|
||||
plugin.StaticChannelToIntercept = filter_static_channel_intercept_list;
|
||||
plugin.DynChannelToIntercept = filter_dyn_channel_intercept_list;
|
||||
plugin.DynChannelIntercept = filter_dyn_channel_intercept;
|
||||
|
||||
plugin.custom = plugins_manager;
|
||||
if (!plugin.custom)
|
||||
return FALSE;
|
||||
plugin.userdata = userdata;
|
||||
|
||||
return plugins_manager->RegisterPlugin(plugins_manager, &plugin);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#if defined(BUILD_SHARED_LIBS)
|
||||
[[nodiscard]]
|
||||
FREERDP_API BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata);
|
||||
|
||||
BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata)
|
||||
{
|
||||
return int_proxy_module_entry_point(plugins_manager, userdata);
|
||||
}
|
||||
#else
|
||||
[[nodiscard]]
|
||||
FREERDP_API BOOL bitmap_filter_proxy_module_entry_point(proxyPluginsManager* plugins_manager,
|
||||
void* userdata);
|
||||
BOOL bitmap_filter_proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata)
|
||||
{
|
||||
return int_proxy_module_entry_point(plugins_manager, userdata);
|
||||
}
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
50
third_party/FreeRDP/server/proxy/modules/demo/CMakeLists.txt
vendored
Normal file
50
third_party/FreeRDP/server/proxy/modules/demo/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
#
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP Proxy Server Demo C++ Module
|
||||
#
|
||||
# Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
# Copyright 2021 Armin Novak <anovak@thincast.com>
|
||||
# Copyright 2021 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.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
if(POLICY CMP0091)
|
||||
cmake_policy(SET CMP0091 NEW)
|
||||
endif()
|
||||
if(NOT FREERDP_DEFAULT_PROJECT_VERSION)
|
||||
set(FREERDP_DEFAULT_PROJECT_VERSION "1.0.0.0")
|
||||
endif()
|
||||
|
||||
project(proxy-demo-plugin VERSION ${FREERDP_DEFAULT_PROJECT_VERSION} LANGUAGES CXX)
|
||||
|
||||
message("project ${PROJECT_NAME} is using version ${PROJECT_VERSION}")
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../../../cmake/)
|
||||
include(ProjectCXXStandard)
|
||||
include(CommonConfigOptions)
|
||||
include(CXXCompilerFlags)
|
||||
|
||||
set(SRCS demo.cpp)
|
||||
addtargetwithresourcefile(${PROJECT_NAME} FALSE "${PROJECT_VERSION}" SRCS FALSE)
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE $<$<BOOL:${BUILD_SHARED_LIBS}>:BUILD_SHARED_LIBS>)
|
||||
target_link_libraries(${PROJECT_NAME} winpr)
|
||||
|
||||
installwithrpath(TARGETS ${PROJECT_NAME} DESTINATION ${FREERDP_PROXY_PLUGINDIR})
|
||||
|
||||
set(PROJECT_PC_REQUIRES_PRIVATE "winpr${FREERDP_API_VERSION}")
|
||||
include(ProxyModuleConfig)
|
||||
generate_proxy_module_config()
|
||||
513
third_party/FreeRDP/server/proxy/modules/demo/demo.cpp
vendored
Normal file
513
third_party/FreeRDP/server/proxy/modules/demo/demo.cpp
vendored
Normal file
@@ -0,0 +1,513 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server Demo C++ Module
|
||||
*
|
||||
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
* Copyright 2021 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2021 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 <iostream>
|
||||
|
||||
#include <freerdp/api.h>
|
||||
#include <freerdp/scancode.h>
|
||||
#include <freerdp/server/proxy/proxy_modules_api.h>
|
||||
|
||||
#define TAG MODULE_TAG("demo")
|
||||
|
||||
struct demo_custom_data
|
||||
{
|
||||
proxyPluginsManager* mgr;
|
||||
int somesetting;
|
||||
};
|
||||
|
||||
static constexpr char plugin_name[] = "demo";
|
||||
static constexpr char plugin_desc[] = "this is a test plugin";
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_plugin_unload([[maybe_unused]] proxyPlugin* plugin)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
|
||||
std::cout << "C++ demo plugin: unloading..." << std::endl;
|
||||
|
||||
/* Here we have to free up our custom data storage. */
|
||||
if (plugin)
|
||||
delete static_cast<struct demo_custom_data*>(plugin->custom);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_init_connect([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_uninit_connect([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_pre_connect([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_post_connect([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_post_disconnect([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_x509_certificate([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_login_failure([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_end_paint([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata, [[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_redirect([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata, [[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_server_post_connect([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_server_peer_activate([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_server_channels_init([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_server_channels_free([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_server_session_end([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_filter_keyboard_event([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* param)
|
||||
{
|
||||
proxyPluginsManager* mgr = nullptr;
|
||||
auto event_data = static_cast<const proxyKeyboardEventInfo*>(param);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(event_data);
|
||||
|
||||
mgr = plugin->mgr;
|
||||
WINPR_ASSERT(mgr);
|
||||
|
||||
if (event_data == nullptr)
|
||||
return FALSE;
|
||||
|
||||
if (event_data->rdp_scan_code == RDP_SCANCODE_KEY_B)
|
||||
{
|
||||
/* user typed 'B', that means bye :) */
|
||||
std::cout << "C++ demo plugin: aborting connection" << std::endl;
|
||||
mgr->AbortConnect(mgr, pdata);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_filter_unicode_event([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* param)
|
||||
{
|
||||
proxyPluginsManager* mgr = nullptr;
|
||||
auto event_data = static_cast<const proxyUnicodeEventInfo*>(param);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(event_data);
|
||||
|
||||
mgr = plugin->mgr;
|
||||
WINPR_ASSERT(mgr);
|
||||
|
||||
if (event_data == nullptr)
|
||||
return FALSE;
|
||||
|
||||
if (event_data->code == 'b')
|
||||
{
|
||||
/* user typed 'B', that means bye :) */
|
||||
std::cout << "C++ demo plugin: aborting connection" << std::endl;
|
||||
mgr->AbortConnect(mgr, pdata);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_mouse_event([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata, [[maybe_unused]] void* param)
|
||||
{
|
||||
auto event_data = static_cast<const proxyMouseEventInfo*>(param);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(event_data);
|
||||
|
||||
WLog_INFO(TAG, "called %p", WINPR_CXX_COMPAT_CAST(const void*, event_data));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_mouse_ex_event([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata, [[maybe_unused]] void* param)
|
||||
{
|
||||
auto event_data = static_cast<const proxyMouseExEventInfo*>(param);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(event_data);
|
||||
|
||||
WLog_INFO(TAG, "called %p", WINPR_CXX_COMPAT_CAST(const void*, event_data));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_channel_data([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* param)
|
||||
{
|
||||
const auto* channel = static_cast<const proxyChannelDataEventInfo*>(param);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(channel);
|
||||
|
||||
WLog_INFO(TAG, "%s [0x%04" PRIx16 "] got %" PRIuz, channel->channel_name, channel->channel_id,
|
||||
channel->data_len);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_server_channel_data([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* param)
|
||||
{
|
||||
const auto* channel = static_cast<const proxyChannelDataEventInfo*>(param);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(channel);
|
||||
|
||||
WLog_WARN(TAG, "%s [0x%04" PRIx16 "] got %" PRIuz, channel->channel_name, channel->channel_id,
|
||||
channel->data_len);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_dynamic_channel_create([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* param)
|
||||
{
|
||||
const auto* channel = static_cast<const proxyChannelDataEventInfo*>(param);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(channel);
|
||||
|
||||
WLog_WARN(TAG, "%s [0x%04" PRIx16 "]", channel->channel_name, channel->channel_id);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_server_fetch_target_addr([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* param)
|
||||
{
|
||||
auto event_data = static_cast<const proxyFetchTargetEventInfo*>(param);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(event_data);
|
||||
|
||||
WLog_INFO(TAG, "called %p", WINPR_CXX_COMPAT_CAST(const void*, event_data));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_server_peer_logon([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata, [[maybe_unused]] void* param)
|
||||
{
|
||||
auto info = static_cast<const proxyServerPeerLogon*>(param);
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(info);
|
||||
WINPR_ASSERT(info->identity);
|
||||
|
||||
WLog_INFO(TAG, "%d", info->automatic);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_dyn_channel_intercept_list([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* arg)
|
||||
{
|
||||
auto data = static_cast<proxyChannelToInterceptData*>(arg);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
WLog_INFO(TAG, "%s: %p", __func__, WINPR_CXX_COMPAT_CAST(const void*, data));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_static_channel_intercept_list([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* arg)
|
||||
{
|
||||
auto data = static_cast<proxyChannelToInterceptData*>(arg);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
WLog_INFO(TAG, "%s: %p", __func__, WINPR_CXX_COMPAT_CAST(const void*, data));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_dyn_channel_intercept([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* arg)
|
||||
{
|
||||
auto data = static_cast<proxyDynChannelInterceptData*>(arg);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
WLog_INFO(TAG, "%s: %p", __func__, WINPR_CXX_COMPAT_CAST(const void*, data));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL int_proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata)
|
||||
{
|
||||
struct demo_custom_data* custom = nullptr;
|
||||
proxyPlugin plugin = {};
|
||||
|
||||
plugin.name = plugin_name;
|
||||
plugin.description = plugin_desc;
|
||||
plugin.PluginUnload = demo_plugin_unload;
|
||||
plugin.ClientInitConnect = demo_client_init_connect;
|
||||
plugin.ClientUninitConnect = demo_client_uninit_connect;
|
||||
plugin.ClientPreConnect = demo_client_pre_connect;
|
||||
plugin.ClientPostConnect = demo_client_post_connect;
|
||||
plugin.ClientPostDisconnect = demo_client_post_disconnect;
|
||||
plugin.ClientX509Certificate = demo_client_x509_certificate;
|
||||
plugin.ClientLoginFailure = demo_client_login_failure;
|
||||
plugin.ClientEndPaint = demo_client_end_paint;
|
||||
plugin.ClientRedirect = demo_client_redirect;
|
||||
plugin.ServerPostConnect = demo_server_post_connect;
|
||||
plugin.ServerPeerActivate = demo_server_peer_activate;
|
||||
plugin.ServerChannelsInit = demo_server_channels_init;
|
||||
plugin.ServerChannelsFree = demo_server_channels_free;
|
||||
plugin.ServerSessionEnd = demo_server_session_end;
|
||||
plugin.KeyboardEvent = demo_filter_keyboard_event;
|
||||
plugin.UnicodeEvent = demo_filter_unicode_event;
|
||||
plugin.MouseEvent = demo_mouse_event;
|
||||
plugin.MouseExEvent = demo_mouse_ex_event;
|
||||
plugin.ClientChannelData = demo_client_channel_data;
|
||||
plugin.ServerChannelData = demo_server_channel_data;
|
||||
plugin.DynamicChannelCreate = demo_dynamic_channel_create;
|
||||
plugin.ServerFetchTargetAddr = demo_server_fetch_target_addr;
|
||||
plugin.ServerPeerLogon = demo_server_peer_logon;
|
||||
|
||||
plugin.StaticChannelToIntercept = demo_static_channel_intercept_list;
|
||||
plugin.DynChannelToIntercept = demo_dyn_channel_intercept_list;
|
||||
plugin.DynChannelIntercept = demo_dyn_channel_intercept;
|
||||
|
||||
plugin.userdata = userdata;
|
||||
|
||||
custom = new (struct demo_custom_data);
|
||||
if (!custom)
|
||||
return FALSE;
|
||||
|
||||
custom->mgr = plugins_manager;
|
||||
custom->somesetting = 42;
|
||||
|
||||
plugin.custom = custom;
|
||||
plugin.userdata = userdata;
|
||||
|
||||
return plugins_manager->RegisterPlugin(plugins_manager, &plugin);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#if defined(BUILD_SHARED_LIBS)
|
||||
[[nodiscard]]
|
||||
FREERDP_API BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata);
|
||||
|
||||
BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata)
|
||||
{
|
||||
return int_proxy_module_entry_point(plugins_manager, userdata);
|
||||
}
|
||||
#else
|
||||
[[nodiscard]]
|
||||
FREERDP_API BOOL demo_proxy_module_entry_point(proxyPluginsManager* plugins_manager,
|
||||
void* userdata);
|
||||
BOOL demo_proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata)
|
||||
{
|
||||
return int_proxy_module_entry_point(plugins_manager, userdata);
|
||||
}
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
51
third_party/FreeRDP/server/proxy/modules/dyn-channel-dump/CMakeLists.txt
vendored
Normal file
51
third_party/FreeRDP/server/proxy/modules/dyn-channel-dump/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
#
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP Proxy Server Demo C++ Module
|
||||
#
|
||||
# Copyright 2023 Armin Novak <anovak@thincast.com>
|
||||
# Copyright 2023 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.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
if(POLICY CMP0091)
|
||||
cmake_policy(SET CMP0091 NEW)
|
||||
endif()
|
||||
if(NOT FREERDP_DEFAULT_PROJECT_VERSION)
|
||||
set(FREERDP_DEFAULT_PROJECT_VERSION "1.0.0.0")
|
||||
endif()
|
||||
|
||||
project(proxy-dyn-channel-dump-plugin VERSION ${FREERDP_DEFAULT_PROJECT_VERSION} LANGUAGES CXX)
|
||||
|
||||
message("project ${PROJECT_NAME} is using version ${PROJECT_VERSION}")
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../../../cmake/)
|
||||
include(CommonConfigOptions)
|
||||
include(CXXCompilerFlags)
|
||||
include(ProjectCXXStandard)
|
||||
|
||||
set(SRCS dyn-channel-dump.cpp)
|
||||
addtargetwithresourcefile(${PROJECT_NAME} FALSE "${PROJECT_VERSION}" SRCS FALSE)
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE $<$<BOOL:${BUILD_SHARED_LIBS}>:BUILD_SHARED_LIBS>)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE winpr freerdp freerdp-client freerdp-server freerdp-server-proxy)
|
||||
|
||||
installwithrpath(TARGETS ${PROJECT_NAME} DESTINATION ${FREERDP_PROXY_PLUGINDIR})
|
||||
|
||||
set(PROJECT_PC_REQUIRES_PRIVATE
|
||||
"winpr${FREERDP_API_VERSION} freerdp${FREERDP_API_VERSION} freerdp-server${FREERDP_API_VERSION} freerdp-client${FREERDP_API_VERSION} freerdp-server-proxy${FREERDP_API_VERSION}"
|
||||
)
|
||||
include(ProxyModuleConfig)
|
||||
generate_proxy_module_config()
|
||||
480
third_party/FreeRDP/server/proxy/modules/dyn-channel-dump/dyn-channel-dump.cpp
vendored
Normal file
480
third_party/FreeRDP/server/proxy/modules/dyn-channel-dump/dyn-channel-dump.cpp
vendored
Normal file
@@ -0,0 +1,480 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server persist-bitmap-filter Module
|
||||
*
|
||||
* this module is designed to deactivate all persistent bitmap cache settings a
|
||||
* client might send.
|
||||
*
|
||||
* Copyright 2023 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2023 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 <fstream>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#if __has_include(<filesystem>)
|
||||
#include <filesystem>
|
||||
namespace fs = std::filesystem;
|
||||
#elif __has_include(<experimental/filesystem>)
|
||||
#include <experimental/filesystem>
|
||||
namespace fs = std::experimental::filesystem;
|
||||
#else
|
||||
#error Could not find system header "<filesystem>" or "<experimental/filesystem>"
|
||||
#endif
|
||||
|
||||
#include <freerdp/server/proxy/proxy_modules_api.h>
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
#include <freerdp/channels/drdynvc.h>
|
||||
#include <freerdp/channels/rdpgfx.h>
|
||||
#include <freerdp/utils/gfx.h>
|
||||
|
||||
#define TAG MODULE_TAG("dyn-channel-dump")
|
||||
|
||||
static constexpr char plugin_name[] = "dyn-channel-dump";
|
||||
static constexpr char plugin_desc[] =
|
||||
"This plugin dumps configurable dynamic channel data to a file.";
|
||||
|
||||
[[nodiscard]] static const std::vector<std::string>& plugin_static_intercept()
|
||||
{
|
||||
static std::vector<std::string> vec;
|
||||
if (vec.empty())
|
||||
vec.emplace_back(DRDYNVC_SVC_CHANNEL_NAME);
|
||||
return vec;
|
||||
}
|
||||
|
||||
static constexpr char key_path[] = "path";
|
||||
static constexpr char key_channels[] = "channels";
|
||||
|
||||
class PluginData
|
||||
{
|
||||
public:
|
||||
explicit PluginData(proxyPluginsManager* mgr) : _mgr(mgr)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] proxyPluginsManager* mgr() const
|
||||
{
|
||||
return _mgr;
|
||||
}
|
||||
|
||||
uint64_t session()
|
||||
{
|
||||
return _sessionid++;
|
||||
}
|
||||
|
||||
private:
|
||||
proxyPluginsManager* _mgr;
|
||||
uint64_t _sessionid{ 0 };
|
||||
};
|
||||
|
||||
class ChannelData
|
||||
{
|
||||
public:
|
||||
ChannelData(const std::string& base, std::vector<std::string> list, uint64_t sessionid)
|
||||
: _base(base), _channels_to_dump(std::move(list)), _session_id(sessionid)
|
||||
{
|
||||
char str[64] = {};
|
||||
(void)_snprintf(str, sizeof(str), "session-%016" PRIx64, _session_id);
|
||||
_base /= str;
|
||||
}
|
||||
|
||||
bool add(const std::string& name, WINPR_ATTR_UNUSED bool back)
|
||||
{
|
||||
std::scoped_lock guard(_mux);
|
||||
if (_map.find(name) == _map.end())
|
||||
{
|
||||
WLog_INFO(TAG, "adding '%s' to dump list", name.c_str());
|
||||
_map.insert({ name, 0 });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::ofstream stream(const std::string& name, bool back)
|
||||
{
|
||||
std::scoped_lock guard(_mux);
|
||||
auto& atom = _map[name];
|
||||
auto count = atom++;
|
||||
auto path = filepath(name, back, count);
|
||||
WLog_DBG(TAG, "[%s] writing file '%s'", name.c_str(), path.c_str());
|
||||
return std::ofstream(path);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool dump_enabled(const std::string& name) const
|
||||
{
|
||||
if (name.empty())
|
||||
{
|
||||
WLog_WARN(TAG, "empty dynamic channel name, skipping");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto enabled = std::find(_channels_to_dump.begin(), _channels_to_dump.end(), name) !=
|
||||
_channels_to_dump.end();
|
||||
WLog_DBG(TAG, "channel '%s' dumping %s", name.c_str(), enabled ? "enabled" : "disabled");
|
||||
return enabled;
|
||||
}
|
||||
|
||||
bool ensure_path_exists()
|
||||
{
|
||||
if (!fs::exists(_base))
|
||||
{
|
||||
if (!fs::create_directories(_base))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to create dump directory %s", _base.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!fs::is_directory(_base))
|
||||
{
|
||||
WLog_ERR(TAG, "dump path %s is not a directory", _base.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool create()
|
||||
{
|
||||
if (!ensure_path_exists())
|
||||
return false;
|
||||
|
||||
if (_channels_to_dump.empty())
|
||||
{
|
||||
WLog_ERR(TAG, "Empty configuration entry [%s/%s], can not continue", plugin_name,
|
||||
key_channels);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t session() const
|
||||
{
|
||||
return _session_id;
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] fs::path filepath(const std::string& channel, bool back, uint64_t count) const
|
||||
{
|
||||
auto name = idstr(channel, back);
|
||||
char cstr[32] = {};
|
||||
(void)_snprintf(cstr, sizeof(cstr), "%016" PRIx64 "-", count);
|
||||
auto path = _base / cstr;
|
||||
path += name;
|
||||
path += ".dump";
|
||||
return path;
|
||||
}
|
||||
|
||||
[[nodiscard]] static std::string idstr(const std::string& name, bool back)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << name << ".";
|
||||
if (back)
|
||||
ss << "back";
|
||||
else
|
||||
ss << "front";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
fs::path _base;
|
||||
std::vector<std::string> _channels_to_dump;
|
||||
|
||||
std::mutex _mux;
|
||||
std::map<std::string, uint64_t> _map;
|
||||
uint64_t _session_id;
|
||||
};
|
||||
|
||||
[[nodiscard]] static PluginData* dump_get_plugin_data(proxyPlugin* plugin)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
|
||||
auto plugindata = static_cast<PluginData*>(plugin->custom);
|
||||
WINPR_ASSERT(plugindata);
|
||||
return plugindata;
|
||||
}
|
||||
|
||||
[[nodiscard]] static ChannelData* dump_get_plugin_data(proxyPlugin* plugin, proxyData* pdata)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
auto plugindata = dump_get_plugin_data(plugin);
|
||||
WINPR_ASSERT(plugindata);
|
||||
|
||||
auto mgr = plugindata->mgr();
|
||||
WINPR_ASSERT(mgr);
|
||||
|
||||
WINPR_ASSERT(mgr->GetPluginData);
|
||||
return static_cast<ChannelData*>(mgr->GetPluginData(mgr, plugin_name, pdata));
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL dump_set_plugin_data(proxyPlugin* plugin, proxyData* pdata,
|
||||
ChannelData* data)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
auto plugindata = dump_get_plugin_data(plugin);
|
||||
WINPR_ASSERT(plugindata);
|
||||
|
||||
auto mgr = plugindata->mgr();
|
||||
WINPR_ASSERT(mgr);
|
||||
|
||||
auto cdata = dump_get_plugin_data(plugin, pdata);
|
||||
delete cdata;
|
||||
|
||||
WINPR_ASSERT(mgr->SetPluginData);
|
||||
return mgr->SetPluginData(mgr, plugin_name, pdata, data);
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool dump_channel_enabled(proxyPlugin* plugin, proxyData* pdata,
|
||||
const std::string& name)
|
||||
{
|
||||
auto config = dump_get_plugin_data(plugin, pdata);
|
||||
if (!config)
|
||||
{
|
||||
WLog_ERR(TAG, "Missing channel data");
|
||||
return false;
|
||||
}
|
||||
return config->dump_enabled(name);
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL dump_dyn_channel_intercept_list(proxyPlugin* plugin, proxyData* pdata,
|
||||
void* arg)
|
||||
{
|
||||
auto data = static_cast<proxyChannelToInterceptData*>(arg);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
data->intercept = dump_channel_enabled(plugin, pdata, data->name);
|
||||
if (data->intercept)
|
||||
{
|
||||
auto cdata = dump_get_plugin_data(plugin, pdata);
|
||||
if (!cdata)
|
||||
return FALSE;
|
||||
|
||||
if (!cdata->add(data->name, false))
|
||||
{
|
||||
WLog_ERR(TAG, "failed to create files for '%s'", data->name);
|
||||
}
|
||||
if (!cdata->add(data->name, true))
|
||||
{
|
||||
WLog_ERR(TAG, "failed to create files for '%s'", data->name);
|
||||
}
|
||||
WLog_INFO(TAG, "Dumping channel '%s'", data->name);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL dump_static_channel_intercept_list([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
void* arg)
|
||||
{
|
||||
auto data = static_cast<proxyChannelToInterceptData*>(arg);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
auto intercept = std::find(plugin_static_intercept().begin(), plugin_static_intercept().end(),
|
||||
data->name) != plugin_static_intercept().end();
|
||||
if (intercept)
|
||||
{
|
||||
WLog_INFO(TAG, "intercepting channel '%s'", data->name);
|
||||
data->intercept = TRUE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL dump_dyn_channel_intercept(proxyPlugin* plugin, proxyData* pdata,
|
||||
void* arg)
|
||||
{
|
||||
auto data = static_cast<proxyDynChannelInterceptData*>(arg);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
data->result = PF_CHANNEL_RESULT_PASS;
|
||||
if (dump_channel_enabled(plugin, pdata, data->name))
|
||||
{
|
||||
WLog_DBG(TAG, "intercepting channel '%s'", data->name);
|
||||
auto cdata = dump_get_plugin_data(plugin, pdata);
|
||||
if (!cdata)
|
||||
{
|
||||
WLog_ERR(TAG, "Missing channel data");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!cdata->ensure_path_exists())
|
||||
return FALSE;
|
||||
|
||||
auto stream = cdata->stream(data->name, data->isBackData);
|
||||
auto buffer = reinterpret_cast<const char*>(Stream_ConstBuffer(data->data));
|
||||
if (!stream.is_open() || !stream.good())
|
||||
{
|
||||
WLog_ERR(TAG, "Could not write to stream");
|
||||
return FALSE;
|
||||
}
|
||||
const auto s = Stream_Length(data->data);
|
||||
if (s > std::numeric_limits<std::streamsize>::max())
|
||||
{
|
||||
WLog_ERR(TAG, "Stream length %" PRIuz " exceeds std::streamsize::max", s);
|
||||
return FALSE;
|
||||
}
|
||||
stream.write(buffer, static_cast<std::streamsize>(s));
|
||||
if (stream.fail())
|
||||
{
|
||||
WLog_ERR(TAG, "Could not write to stream");
|
||||
return FALSE;
|
||||
}
|
||||
stream.flush();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static std::vector<std::string> split(const std::string& input,
|
||||
const std::string& regex)
|
||||
{
|
||||
// passing -1 as the submatch index parameter performs splitting
|
||||
std::regex re(regex);
|
||||
std::sregex_token_iterator first{ input.begin(), input.end(), re, -1 };
|
||||
std::sregex_token_iterator last;
|
||||
return { first, last };
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL dump_session_started(proxyPlugin* plugin, proxyData* pdata,
|
||||
void* /*unused*/)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
auto custom = dump_get_plugin_data(plugin);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
auto config = pdata->config;
|
||||
WINPR_ASSERT(config);
|
||||
|
||||
auto cpath = pf_config_get(config, plugin_name, key_path);
|
||||
if (!cpath)
|
||||
{
|
||||
WLog_ERR(TAG, "Missing configuration entry [%s/%s], can not continue", plugin_name,
|
||||
key_path);
|
||||
return FALSE;
|
||||
}
|
||||
auto cchannels = pf_config_get(config, plugin_name, key_channels);
|
||||
if (!cchannels)
|
||||
{
|
||||
WLog_ERR(TAG, "Missing configuration entry [%s/%s], can not continue", plugin_name,
|
||||
key_channels);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
std::string path(cpath);
|
||||
std::string channels(cchannels);
|
||||
std::vector<std::string> list = split(channels, "[;,]");
|
||||
auto cfg = new ChannelData(path, std::move(list), custom->session());
|
||||
if (!cfg || !cfg->create())
|
||||
{
|
||||
delete cfg;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!dump_set_plugin_data(plugin, pdata, cfg))
|
||||
return FALSE;
|
||||
|
||||
WLog_DBG(TAG, "starting session dump %" PRIu64, cfg->session());
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL dump_session_end(proxyPlugin* plugin, proxyData* pdata, void* /*unused*/)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
auto cfg = dump_get_plugin_data(plugin, pdata);
|
||||
if (cfg)
|
||||
WLog_DBG(TAG, "ending session dump %" PRIu64, cfg->session());
|
||||
return dump_set_plugin_data(plugin, pdata, nullptr);
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL dump_unload(proxyPlugin* plugin)
|
||||
{
|
||||
if (!plugin)
|
||||
return TRUE;
|
||||
delete static_cast<PluginData*>(plugin->custom);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL int_proxy_module_entry_point(proxyPluginsManager* plugins_manager,
|
||||
void* userdata)
|
||||
{
|
||||
proxyPlugin plugin = {};
|
||||
|
||||
plugin.name = plugin_name;
|
||||
plugin.description = plugin_desc;
|
||||
|
||||
plugin.PluginUnload = dump_unload;
|
||||
plugin.ServerSessionStarted = dump_session_started;
|
||||
plugin.ServerSessionEnd = dump_session_end;
|
||||
|
||||
plugin.StaticChannelToIntercept = dump_static_channel_intercept_list;
|
||||
plugin.DynChannelToIntercept = dump_dyn_channel_intercept_list;
|
||||
plugin.DynChannelIntercept = dump_dyn_channel_intercept;
|
||||
|
||||
plugin.custom = new PluginData(plugins_manager);
|
||||
if (!plugin.custom)
|
||||
return FALSE;
|
||||
plugin.userdata = userdata;
|
||||
|
||||
return plugins_manager->RegisterPlugin(plugins_manager, &plugin);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#if defined(BUILD_SHARED_LIBS)
|
||||
[[nodiscard]]
|
||||
FREERDP_API BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata);
|
||||
|
||||
BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata)
|
||||
{
|
||||
return int_proxy_module_entry_point(plugins_manager, userdata);
|
||||
}
|
||||
#else
|
||||
[[nodiscard]]
|
||||
FREERDP_API BOOL dyn_channel_dump_proxy_module_entry_point(proxyPluginsManager* plugins_manager,
|
||||
void* userdata);
|
||||
BOOL dyn_channel_dump_proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata)
|
||||
{
|
||||
return int_proxy_module_entry_point(plugins_manager, userdata);
|
||||
}
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
16
third_party/FreeRDP/server/proxy/modules/freerdp-proxy-module.pc.in
vendored
Normal file
16
third_party/FreeRDP/server/proxy/modules/freerdp-proxy-module.pc.in
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
prefix=@PKG_CONFIG_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||
includedir=${prefix}/@WINPR_INCLUDE_DIR@
|
||||
plugindir=${libdir}/@FREERDP_MAJOR_DIR@
|
||||
proxy_plugindir=${plugindir}/proxy
|
||||
|
||||
Name: @PROJECT_NAME@
|
||||
Description: FreeRDP proxy module
|
||||
URL: http://www.freerdp.com/
|
||||
Version: @PROJECT_VERSION@
|
||||
Requires: @PROJECT_PC_REQUIRES@
|
||||
Requires.private: @PROJECT_PC_REQUIRES_PRIVATE@
|
||||
Libs: -L${libdir}
|
||||
Libs.private: -Wl,--whole-archive \${proxy_plugindir}/@PROJECT_LIBRARY_NAME@ -u @PROJECT_SHORT_NAME_UNDERSCORE@_proxy_module_entry_point -Wl,--no-whole-archive
|
||||
Cflags: -I${includedir}
|
||||
362
third_party/FreeRDP/server/proxy/pf_channel.c
vendored
Normal file
362
third_party/FreeRDP/server/proxy/pf_channel.c
vendored
Normal file
@@ -0,0 +1,362 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
*
|
||||
* Copyright 2022 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* 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 <winpr/assert.h>
|
||||
#include <winpr/cast.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/server/proxy/proxy_log.h>
|
||||
|
||||
#include "proxy_modules.h"
|
||||
#include "pf_channel.h"
|
||||
|
||||
#define TAG PROXY_TAG("channel")
|
||||
|
||||
/** @brief a tracker for channel packets */
|
||||
struct sChannelStateTracker
|
||||
{
|
||||
pServerStaticChannelContext* channel;
|
||||
ChannelTrackerMode mode;
|
||||
wStream* currentPacket;
|
||||
size_t currentPacketReceived;
|
||||
size_t currentPacketSize;
|
||||
size_t currentPacketFragments;
|
||||
|
||||
ChannelTrackerPeekFn peekFn;
|
||||
void* trackerData;
|
||||
proxyData* pdata;
|
||||
};
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL channelTracker_resetCurrentPacket(ChannelStateTracker* tracker)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
|
||||
BOOL create = TRUE;
|
||||
if (tracker->currentPacket)
|
||||
{
|
||||
const size_t cap = Stream_Capacity(tracker->currentPacket);
|
||||
if (cap < 1ULL * 1000ULL * 1000ULL)
|
||||
create = FALSE;
|
||||
else
|
||||
Stream_Free(tracker->currentPacket, TRUE);
|
||||
}
|
||||
|
||||
if (create)
|
||||
tracker->currentPacket = Stream_New(nullptr, 10ULL * 1024ULL);
|
||||
if (!tracker->currentPacket)
|
||||
return FALSE;
|
||||
Stream_ResetPosition(tracker->currentPacket);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ChannelStateTracker* channelTracker_new(pServerStaticChannelContext* channel,
|
||||
ChannelTrackerPeekFn fn, void* data)
|
||||
{
|
||||
ChannelStateTracker* ret = calloc(1, sizeof(ChannelStateTracker));
|
||||
if (!ret)
|
||||
return ret;
|
||||
|
||||
WINPR_ASSERT(fn);
|
||||
|
||||
ret->channel = channel;
|
||||
ret->peekFn = fn;
|
||||
|
||||
if (!channelTracker_setCustomData(ret, data))
|
||||
goto fail;
|
||||
|
||||
if (!channelTracker_resetCurrentPacket(ret))
|
||||
goto fail;
|
||||
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
WINPR_PRAGMA_DIAG_PUSH
|
||||
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
|
||||
channelTracker_free(ret);
|
||||
WINPR_PRAGMA_DIAG_POP
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PfChannelResult channelTracker_update(ChannelStateTracker* tracker, const BYTE* xdata, size_t xsize,
|
||||
UINT32 flags, size_t totalSize)
|
||||
{
|
||||
PfChannelResult result = PF_CHANNEL_RESULT_ERROR;
|
||||
BOOL firstPacket = (flags & CHANNEL_FLAG_FIRST) != 0;
|
||||
BOOL lastPacket = (flags & CHANNEL_FLAG_LAST) != 0;
|
||||
|
||||
WINPR_ASSERT(tracker);
|
||||
|
||||
WLog_VRB(TAG, "channelTracker_update(%s): sz=%" PRIuz " first=%d last=%d",
|
||||
tracker->channel->channel_name, xsize, firstPacket, lastPacket);
|
||||
if (flags & CHANNEL_FLAG_FIRST)
|
||||
{
|
||||
if (!channelTracker_resetCurrentPacket(tracker))
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
channelTracker_setCurrentPacketSize(tracker, totalSize);
|
||||
tracker->currentPacketReceived = 0;
|
||||
tracker->currentPacketFragments = 0;
|
||||
}
|
||||
|
||||
{
|
||||
const size_t currentPacketSize = channelTracker_getCurrentPacketSize(tracker);
|
||||
if (tracker->currentPacketReceived + xsize > currentPacketSize)
|
||||
WLog_INFO(TAG, "cumulated size is bigger (%" PRIuz ") than total size (%" PRIuz ")",
|
||||
tracker->currentPacketReceived + xsize, currentPacketSize);
|
||||
}
|
||||
|
||||
tracker->currentPacketReceived += xsize;
|
||||
tracker->currentPacketFragments++;
|
||||
|
||||
switch (channelTracker_getMode(tracker))
|
||||
{
|
||||
case CHANNEL_TRACKER_PEEK:
|
||||
{
|
||||
wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
|
||||
if (!Stream_EnsureRemainingCapacity(currentPacket, xsize))
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
|
||||
Stream_Write(currentPacket, xdata, xsize);
|
||||
|
||||
WINPR_ASSERT(tracker->peekFn);
|
||||
result = tracker->peekFn(tracker, firstPacket, lastPacket);
|
||||
}
|
||||
break;
|
||||
case CHANNEL_TRACKER_PASS:
|
||||
result = PF_CHANNEL_RESULT_PASS;
|
||||
break;
|
||||
case CHANNEL_TRACKER_DROP:
|
||||
result = PF_CHANNEL_RESULT_DROP;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (lastPacket)
|
||||
{
|
||||
const size_t currentPacketSize = channelTracker_getCurrentPacketSize(tracker);
|
||||
channelTracker_setMode(tracker, CHANNEL_TRACKER_PEEK);
|
||||
|
||||
if (tracker->currentPacketReceived != currentPacketSize)
|
||||
WLog_INFO(TAG, "cumulated size(%" PRIuz ") does not match total size (%" PRIuz ")",
|
||||
tracker->currentPacketReceived, currentPacketSize);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void channelTracker_free(ChannelStateTracker* t)
|
||||
{
|
||||
if (!t)
|
||||
return;
|
||||
|
||||
Stream_Free(t->currentPacket, TRUE);
|
||||
free(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the current accumulated tracker content, if it's the first packet, then
|
||||
* when can just return that the packet shall be passed, otherwise to have to refragment
|
||||
* the accumulated current packet.
|
||||
*/
|
||||
|
||||
PfChannelResult channelTracker_flushCurrent(ChannelStateTracker* t, BOOL first, BOOL last,
|
||||
BOOL toBack)
|
||||
{
|
||||
proxyData* pdata = nullptr;
|
||||
pServerContext* ps = nullptr;
|
||||
pServerStaticChannelContext* channel = nullptr;
|
||||
UINT32 flags = CHANNEL_FLAG_FIRST;
|
||||
BOOL r = 0;
|
||||
const char* direction = toBack ? "F->B" : "B->F";
|
||||
const size_t currentPacketSize = channelTracker_getCurrentPacketSize(t);
|
||||
wStream* currentPacket = channelTracker_getCurrentPacket(t);
|
||||
|
||||
WINPR_ASSERT(t);
|
||||
|
||||
WLog_VRB(TAG, "channelTracker_flushCurrent(%s): %s sz=%" PRIuz " first=%d last=%d",
|
||||
t->channel->channel_name, direction, Stream_GetPosition(currentPacket), first, last);
|
||||
|
||||
if (first)
|
||||
return PF_CHANNEL_RESULT_PASS;
|
||||
|
||||
pdata = t->pdata;
|
||||
channel = t->channel;
|
||||
if (last)
|
||||
flags |= CHANNEL_FLAG_LAST;
|
||||
|
||||
if (toBack)
|
||||
{
|
||||
proxyChannelDataEventInfo ev = WINPR_C_ARRAY_INIT;
|
||||
|
||||
ev.channel_id = WINPR_ASSERTING_INT_CAST(UINT16, channel->front_channel_id);
|
||||
ev.channel_name = channel->channel_name;
|
||||
ev.data = Stream_Buffer(currentPacket);
|
||||
ev.data_len = Stream_GetPosition(currentPacket);
|
||||
ev.flags = flags;
|
||||
ev.total_size = currentPacketSize;
|
||||
|
||||
if (!pdata->pc->sendChannelData)
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
|
||||
return pdata->pc->sendChannelData(pdata->pc, &ev) ? PF_CHANNEL_RESULT_DROP
|
||||
: PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
|
||||
ps = pdata->ps;
|
||||
r = ps->context.peer->SendChannelPacket(
|
||||
ps->context.peer, WINPR_ASSERTING_INT_CAST(UINT16, channel->front_channel_id),
|
||||
currentPacketSize, flags, Stream_Buffer(currentPacket), Stream_GetPosition(currentPacket));
|
||||
|
||||
return r ? PF_CHANNEL_RESULT_DROP : PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult pf_channel_generic_back_data(proxyData* pdata,
|
||||
const pServerStaticChannelContext* channel,
|
||||
const BYTE* xdata, size_t xsize, UINT32 flags,
|
||||
size_t totalSize)
|
||||
{
|
||||
proxyChannelDataEventInfo ev = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(channel);
|
||||
|
||||
switch (channel->channelMode)
|
||||
{
|
||||
case PF_UTILS_CHANNEL_PASSTHROUGH:
|
||||
ev.channel_id = WINPR_ASSERTING_INT_CAST(UINT16, channel->back_channel_id);
|
||||
ev.channel_name = channel->channel_name;
|
||||
ev.data = xdata;
|
||||
ev.data_len = xsize;
|
||||
ev.flags = flags;
|
||||
ev.total_size = totalSize;
|
||||
|
||||
if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA,
|
||||
pdata, &ev))
|
||||
return PF_CHANNEL_RESULT_DROP; /* Silently drop */
|
||||
|
||||
return PF_CHANNEL_RESULT_PASS;
|
||||
|
||||
case PF_UTILS_CHANNEL_INTERCEPT:
|
||||
/* TODO */
|
||||
case PF_UTILS_CHANNEL_BLOCK:
|
||||
default:
|
||||
return PF_CHANNEL_RESULT_DROP;
|
||||
}
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult pf_channel_generic_front_data(proxyData* pdata,
|
||||
const pServerStaticChannelContext* channel,
|
||||
const BYTE* xdata, size_t xsize, UINT32 flags,
|
||||
size_t totalSize)
|
||||
{
|
||||
proxyChannelDataEventInfo ev = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(channel);
|
||||
|
||||
switch (channel->channelMode)
|
||||
{
|
||||
case PF_UTILS_CHANNEL_PASSTHROUGH:
|
||||
ev.channel_id = WINPR_ASSERTING_INT_CAST(UINT16, channel->front_channel_id);
|
||||
ev.channel_name = channel->channel_name;
|
||||
ev.data = xdata;
|
||||
ev.data_len = xsize;
|
||||
ev.flags = flags;
|
||||
ev.total_size = totalSize;
|
||||
|
||||
if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA,
|
||||
pdata, &ev))
|
||||
return PF_CHANNEL_RESULT_DROP; /* Silently drop */
|
||||
|
||||
return PF_CHANNEL_RESULT_PASS;
|
||||
|
||||
case PF_UTILS_CHANNEL_INTERCEPT:
|
||||
/* TODO */
|
||||
case PF_UTILS_CHANNEL_BLOCK:
|
||||
default:
|
||||
return PF_CHANNEL_RESULT_DROP;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL pf_channel_setup_generic(pServerStaticChannelContext* channel)
|
||||
{
|
||||
WINPR_ASSERT(channel);
|
||||
channel->onBackData = pf_channel_generic_back_data;
|
||||
channel->onFrontData = pf_channel_generic_front_data;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL channelTracker_setMode(ChannelStateTracker* tracker, ChannelTrackerMode mode)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
tracker->mode = mode;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ChannelTrackerMode channelTracker_getMode(ChannelStateTracker* tracker)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
return tracker->mode;
|
||||
}
|
||||
|
||||
BOOL channelTracker_setPData(ChannelStateTracker* tracker, proxyData* pdata)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
tracker->pdata = pdata;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
proxyData* channelTracker_getPData(ChannelStateTracker* tracker)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
return tracker->pdata;
|
||||
}
|
||||
|
||||
wStream* channelTracker_getCurrentPacket(ChannelStateTracker* tracker)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
return tracker->currentPacket;
|
||||
}
|
||||
|
||||
BOOL channelTracker_setCustomData(ChannelStateTracker* tracker, void* data)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
tracker->trackerData = data;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void* channelTracker_getCustomData(ChannelStateTracker* tracker)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
return tracker->trackerData;
|
||||
}
|
||||
|
||||
size_t channelTracker_getCurrentPacketSize(ChannelStateTracker* tracker)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
return tracker->currentPacketSize;
|
||||
}
|
||||
|
||||
BOOL channelTracker_setCurrentPacketSize(ChannelStateTracker* tracker, size_t size)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
tracker->currentPacketSize = size;
|
||||
return TRUE;
|
||||
}
|
||||
66
third_party/FreeRDP/server/proxy/pf_channel.h
vendored
Normal file
66
third_party/FreeRDP/server/proxy/pf_channel.h
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
*
|
||||
*
|
||||
* Copyright 2022 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef SERVER_PROXY_PF_CHANNEL_H_
|
||||
#define SERVER_PROXY_PF_CHANNEL_H_
|
||||
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
/** @brief operating mode of a channel tracker */
|
||||
typedef enum
|
||||
{
|
||||
CHANNEL_TRACKER_PEEK, /*!< inquiring content, accumulating packet fragments */
|
||||
CHANNEL_TRACKER_PASS, /*!< pass all the fragments of the current packet */
|
||||
CHANNEL_TRACKER_DROP /*!< drop all the fragments of the current packet */
|
||||
} ChannelTrackerMode;
|
||||
|
||||
typedef struct sChannelStateTracker ChannelStateTracker;
|
||||
typedef PfChannelResult (*ChannelTrackerPeekFn)(ChannelStateTracker* tracker, BOOL first,
|
||||
BOOL lastPacket);
|
||||
|
||||
void channelTracker_free(ChannelStateTracker* t);
|
||||
|
||||
WINPR_ATTR_MALLOC(channelTracker_free, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
ChannelStateTracker* channelTracker_new(pServerStaticChannelContext* channel,
|
||||
ChannelTrackerPeekFn fn, void* data);
|
||||
|
||||
BOOL channelTracker_setMode(ChannelStateTracker* tracker, ChannelTrackerMode mode);
|
||||
WINPR_ATTR_NODISCARD ChannelTrackerMode channelTracker_getMode(ChannelStateTracker* tracker);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL channelTracker_setPData(ChannelStateTracker* tracker, proxyData* pdata);
|
||||
WINPR_ATTR_NODISCARD proxyData* channelTracker_getPData(ChannelStateTracker* tracker);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL channelTracker_setCustomData(ChannelStateTracker* tracker, void* data);
|
||||
WINPR_ATTR_NODISCARD void* channelTracker_getCustomData(ChannelStateTracker* tracker);
|
||||
|
||||
WINPR_ATTR_NODISCARD wStream* channelTracker_getCurrentPacket(ChannelStateTracker* tracker);
|
||||
|
||||
WINPR_ATTR_NODISCARD size_t channelTracker_getCurrentPacketSize(ChannelStateTracker* tracker);
|
||||
BOOL channelTracker_setCurrentPacketSize(ChannelStateTracker* tracker, size_t size);
|
||||
|
||||
WINPR_ATTR_NODISCARD PfChannelResult channelTracker_update(ChannelStateTracker* tracker,
|
||||
const BYTE* xdata, size_t xsize,
|
||||
UINT32 flags, size_t totalSize);
|
||||
|
||||
WINPR_ATTR_NODISCARD PfChannelResult channelTracker_flushCurrent(ChannelStateTracker* t, BOOL first,
|
||||
BOOL last, BOOL toBack);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_setup_generic(pServerStaticChannelContext* channel);
|
||||
|
||||
#endif /* SERVER_PROXY_PF_CHANNEL_H_ */
|
||||
1091
third_party/FreeRDP/server/proxy/pf_client.c
vendored
Normal file
1091
third_party/FreeRDP/server/proxy/pf_client.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
31
third_party/FreeRDP/server/proxy/pf_client.h
vendored
Normal file
31
third_party/FreeRDP/server/proxy/pf_client.h
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server
|
||||
*
|
||||
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
|
||||
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_PROXY_PFCLIENT_H
|
||||
#define FREERDP_SERVER_PROXY_PFCLIENT_H
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <winpr/wtypes.h>
|
||||
|
||||
int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints);
|
||||
WINPR_ATTR_NODISCARD DWORD WINAPI pf_client_start(LPVOID arg);
|
||||
|
||||
#endif /* FREERDP_SERVER_PROXY_PFCLIENT_H */
|
||||
1363
third_party/FreeRDP/server/proxy/pf_config.c
vendored
Normal file
1363
third_party/FreeRDP/server/proxy/pf_config.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
427
third_party/FreeRDP/server/proxy/pf_context.c
vendored
Normal file
427
third_party/FreeRDP/server/proxy/pf_context.c
vendored
Normal file
@@ -0,0 +1,427 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server
|
||||
*
|
||||
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
|
||||
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
|
||||
* Copyright 2021 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2021 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 <winpr/crypto.h>
|
||||
#include <winpr/print.h>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_log.h>
|
||||
#include <freerdp/server/proxy/proxy_server.h>
|
||||
#include <freerdp/channels/drdynvc.h>
|
||||
|
||||
#include "pf_client.h"
|
||||
#include "pf_utils.h"
|
||||
#include "proxy_modules.h"
|
||||
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
#include "channels/pf_channel_rdpdr.h"
|
||||
|
||||
#define TAG PROXY_TAG("server")
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static UINT32 ChannelId_Hash(const void* key)
|
||||
{
|
||||
const UINT32* v = (const UINT32*)key;
|
||||
return *v;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL ChannelId_Compare(const void* pv1, const void* pv2)
|
||||
{
|
||||
const UINT32* v1 = pv1;
|
||||
const UINT32* v2 = pv2;
|
||||
WINPR_ASSERT(v1);
|
||||
WINPR_ASSERT(v2);
|
||||
return (*v1 == *v2);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL dyn_intercept(pServerContext* ps, const char* name)
|
||||
{
|
||||
if (strncmp(DRDYNVC_SVC_CHANNEL_NAME, name, sizeof(DRDYNVC_SVC_CHANNEL_NAME)) != 0)
|
||||
return FALSE;
|
||||
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->pdata);
|
||||
|
||||
const proxyConfig* cfg = ps->pdata->config;
|
||||
WINPR_ASSERT(cfg);
|
||||
if (!cfg->GFX)
|
||||
return TRUE;
|
||||
if (!cfg->AudioOutput)
|
||||
return TRUE;
|
||||
if (!cfg->AudioInput)
|
||||
return TRUE;
|
||||
if (!cfg->Multitouch)
|
||||
return TRUE;
|
||||
if (!cfg->VideoRedirection)
|
||||
return TRUE;
|
||||
if (!cfg->CameraRedirection)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pServerStaticChannelContext* StaticChannelContext_new(pServerContext* ps, const char* name,
|
||||
UINT32 id)
|
||||
{
|
||||
pServerStaticChannelContext* ret = calloc(1, sizeof(*ret));
|
||||
if (!ret)
|
||||
{
|
||||
PROXY_LOG_ERR(TAG, ps, "error allocating channel context for '%s'", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ret->front_channel_id = id;
|
||||
ret->channel_name = _strdup(name);
|
||||
if (!ret->channel_name)
|
||||
{
|
||||
PROXY_LOG_ERR(TAG, ps, "error allocating name in channel context for '%s'", name);
|
||||
free(ret);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
proxyChannelToInterceptData channel = { .name = name, .channelId = id, .intercept = FALSE };
|
||||
|
||||
if (pf_modules_run_filter(ps->pdata->module, FILTER_TYPE_STATIC_INTERCEPT_LIST, ps->pdata,
|
||||
&channel) &&
|
||||
channel.intercept)
|
||||
ret->channelMode = PF_UTILS_CHANNEL_INTERCEPT;
|
||||
else if (dyn_intercept(ps, name))
|
||||
ret->channelMode = PF_UTILS_CHANNEL_INTERCEPT;
|
||||
else
|
||||
ret->channelMode = pf_utils_get_channel_mode(ps->pdata->config, name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void StaticChannelContext_free(pServerStaticChannelContext* ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
IFCALL(ctx->contextDtor, ctx->context);
|
||||
|
||||
free(ctx->channel_name);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
static void HashStaticChannelContext_free(void* ptr)
|
||||
{
|
||||
pServerStaticChannelContext* ctx = (pServerStaticChannelContext*)ptr;
|
||||
StaticChannelContext_free(ctx);
|
||||
}
|
||||
|
||||
/* Proxy context initialization callback */
|
||||
static void client_to_proxy_context_free(freerdp_peer* client, rdpContext* ctx);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL client_to_proxy_context_new(freerdp_peer* client, rdpContext* ctx)
|
||||
{
|
||||
wObject* obj = nullptr;
|
||||
pServerContext* context = (pServerContext*)ctx;
|
||||
|
||||
WINPR_ASSERT(client);
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
context->dynvcReady = nullptr;
|
||||
|
||||
context->vcm = WTSOpenServerA((LPSTR)client->context);
|
||||
|
||||
if (!context->vcm || context->vcm == INVALID_HANDLE_VALUE)
|
||||
goto error;
|
||||
|
||||
if (!(context->dynvcReady = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
goto error;
|
||||
|
||||
context->interceptContextMap = HashTable_New(FALSE);
|
||||
if (!context->interceptContextMap)
|
||||
goto error;
|
||||
if (!HashTable_SetupForStringData(context->interceptContextMap, FALSE))
|
||||
goto error;
|
||||
obj = HashTable_ValueObject(context->interceptContextMap);
|
||||
WINPR_ASSERT(obj);
|
||||
obj->fnObjectFree = intercept_context_entry_free;
|
||||
|
||||
/* channels by ids */
|
||||
context->channelsByFrontId = HashTable_New(FALSE);
|
||||
if (!context->channelsByFrontId)
|
||||
goto error;
|
||||
if (!HashTable_SetHashFunction(context->channelsByFrontId, ChannelId_Hash))
|
||||
goto error;
|
||||
|
||||
obj = HashTable_KeyObject(context->channelsByFrontId);
|
||||
obj->fnObjectEquals = ChannelId_Compare;
|
||||
|
||||
obj = HashTable_ValueObject(context->channelsByFrontId);
|
||||
obj->fnObjectFree = HashStaticChannelContext_free;
|
||||
|
||||
context->channelsByBackId = HashTable_New(FALSE);
|
||||
if (!context->channelsByBackId)
|
||||
goto error;
|
||||
if (!HashTable_SetHashFunction(context->channelsByBackId, ChannelId_Hash))
|
||||
goto error;
|
||||
|
||||
obj = HashTable_KeyObject(context->channelsByBackId);
|
||||
obj->fnObjectEquals = ChannelId_Compare;
|
||||
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
client_to_proxy_context_free(client, ctx);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Proxy context free callback */
|
||||
void client_to_proxy_context_free(freerdp_peer* client, rdpContext* ctx)
|
||||
{
|
||||
pServerContext* context = (pServerContext*)ctx;
|
||||
|
||||
WINPR_UNUSED(client);
|
||||
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
if (context->dynvcReady)
|
||||
{
|
||||
(void)CloseHandle(context->dynvcReady);
|
||||
context->dynvcReady = nullptr;
|
||||
}
|
||||
|
||||
HashTable_Free(context->interceptContextMap);
|
||||
HashTable_Free(context->channelsByFrontId);
|
||||
HashTable_Free(context->channelsByBackId);
|
||||
|
||||
if (context->vcm && (context->vcm != INVALID_HANDLE_VALUE))
|
||||
WTSCloseServer(context->vcm);
|
||||
context->vcm = nullptr;
|
||||
}
|
||||
|
||||
BOOL pf_context_init_server_context(freerdp_peer* client)
|
||||
{
|
||||
WINPR_ASSERT(client);
|
||||
|
||||
client->ContextSize = sizeof(pServerContext);
|
||||
client->ContextNew = client_to_proxy_context_new;
|
||||
client->ContextFree = client_to_proxy_context_free;
|
||||
|
||||
return freerdp_peer_context_new(client);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_context_revert_str_settings(rdpSettings* dst, const rdpSettings* before, size_t nr,
|
||||
const FreeRDP_Settings_Keys_String* ids)
|
||||
{
|
||||
WINPR_ASSERT(dst);
|
||||
WINPR_ASSERT(before);
|
||||
WINPR_ASSERT(ids || (nr == 0));
|
||||
|
||||
for (size_t x = 0; x < nr; x++)
|
||||
{
|
||||
FreeRDP_Settings_Keys_String id = ids[x];
|
||||
const char* what = freerdp_settings_get_string(before, id);
|
||||
if (!freerdp_settings_set_string(dst, id, what))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void intercept_context_entry_free(void* obj)
|
||||
{
|
||||
InterceptContextMapEntry* entry = obj;
|
||||
if (!entry)
|
||||
return;
|
||||
if (!entry->free)
|
||||
return;
|
||||
entry->free(entry);
|
||||
}
|
||||
|
||||
BOOL pf_context_copy_settings(rdpSettings* dst, const rdpSettings* src)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
rdpSettings* before_copy = nullptr;
|
||||
const FreeRDP_Settings_Keys_String to_revert[] = { FreeRDP_ConfigPath,
|
||||
FreeRDP_CertificateName };
|
||||
|
||||
if (!dst || !src)
|
||||
return FALSE;
|
||||
|
||||
before_copy = freerdp_settings_clone(dst);
|
||||
if (!before_copy)
|
||||
return FALSE;
|
||||
|
||||
if (!freerdp_settings_copy(dst, src))
|
||||
goto out_fail;
|
||||
|
||||
/* keep original ServerMode value */
|
||||
if (!freerdp_settings_copy_item(dst, before_copy, FreeRDP_ServerMode))
|
||||
goto out_fail;
|
||||
|
||||
/* revert some values that must not be changed */
|
||||
if (!pf_context_revert_str_settings(dst, before_copy, ARRAYSIZE(to_revert), to_revert))
|
||||
goto out_fail;
|
||||
|
||||
if (!freerdp_settings_get_bool(dst, FreeRDP_ServerMode))
|
||||
{
|
||||
/* adjust instance pointer */
|
||||
if (!freerdp_settings_copy_item(dst, before_copy, FreeRDP_instance))
|
||||
goto out_fail;
|
||||
|
||||
/*
|
||||
* RdpServerRsaKey must be set to nullptr if `dst` is client's context
|
||||
* it must be freed before setting it to nullptr to avoid a memory leak!
|
||||
*/
|
||||
|
||||
if (!freerdp_settings_set_pointer_len(dst, FreeRDP_RdpServerRsaKey, nullptr, 1))
|
||||
goto out_fail;
|
||||
}
|
||||
|
||||
/* We handle certificate management for this client ourselves. */
|
||||
rc = freerdp_settings_set_bool(dst, FreeRDP_ExternalCertificateManagement, TRUE);
|
||||
|
||||
out_fail:
|
||||
freerdp_settings_free(before_copy);
|
||||
return rc;
|
||||
}
|
||||
|
||||
pClientContext* pf_context_create_client_context(const rdpSettings* clientSettings)
|
||||
{
|
||||
RDP_CLIENT_ENTRY_POINTS clientEntryPoints = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(clientSettings);
|
||||
|
||||
RdpClientEntry(&clientEntryPoints);
|
||||
rdpContext* context = freerdp_client_context_new(&clientEntryPoints);
|
||||
|
||||
if (!context)
|
||||
return nullptr;
|
||||
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
|
||||
if (!pf_context_copy_settings(context->settings, clientSettings))
|
||||
goto error;
|
||||
|
||||
return pc;
|
||||
error:
|
||||
freerdp_client_context_free(context);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
proxyData* proxy_data_new(void)
|
||||
{
|
||||
BYTE temp[16];
|
||||
char* hex = nullptr;
|
||||
proxyData* pdata = nullptr;
|
||||
|
||||
pdata = calloc(1, sizeof(proxyData));
|
||||
if (!pdata)
|
||||
return nullptr;
|
||||
|
||||
if (!(pdata->abort_event = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
goto error;
|
||||
|
||||
if (!(pdata->gfx_server_ready = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
goto error;
|
||||
|
||||
if (winpr_RAND(&temp, 16) < 0)
|
||||
goto error;
|
||||
hex = winpr_BinToHexString(temp, 16, FALSE);
|
||||
if (!hex)
|
||||
goto error;
|
||||
|
||||
CopyMemory(pdata->session_id, hex, PROXY_SESSION_ID_LENGTH);
|
||||
pdata->session_id[PROXY_SESSION_ID_LENGTH] = '\0';
|
||||
free(hex);
|
||||
|
||||
if (!(pdata->modules_info = HashTable_New(FALSE)))
|
||||
goto error;
|
||||
|
||||
/* modules_info maps between plugin name to custom data */
|
||||
if (!HashTable_SetupForStringData(pdata->modules_info, FALSE))
|
||||
goto error;
|
||||
|
||||
return pdata;
|
||||
error:
|
||||
WINPR_PRAGMA_DIAG_PUSH
|
||||
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
|
||||
proxy_data_free(pdata);
|
||||
WINPR_PRAGMA_DIAG_POP
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* updates circular pointers between proxyData and pClientContext instances */
|
||||
void proxy_data_set_client_context(proxyData* pdata, pClientContext* context)
|
||||
{
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(context);
|
||||
pdata->pc = context;
|
||||
context->pdata = pdata;
|
||||
}
|
||||
|
||||
/* updates circular pointers between proxyData and pServerContext instances */
|
||||
void proxy_data_set_server_context(proxyData* pdata, pServerContext* context)
|
||||
{
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(context);
|
||||
pdata->ps = context;
|
||||
context->pdata = pdata;
|
||||
}
|
||||
|
||||
void proxy_data_free(proxyData* pdata)
|
||||
{
|
||||
if (!pdata)
|
||||
return;
|
||||
|
||||
if (pdata->abort_event)
|
||||
(void)CloseHandle(pdata->abort_event);
|
||||
|
||||
if (pdata->client_thread)
|
||||
(void)CloseHandle(pdata->client_thread);
|
||||
|
||||
if (pdata->gfx_server_ready)
|
||||
(void)CloseHandle(pdata->gfx_server_ready);
|
||||
|
||||
if (pdata->modules_info)
|
||||
HashTable_Free(pdata->modules_info);
|
||||
|
||||
if (pdata->pc)
|
||||
freerdp_client_context_free(&pdata->pc->context);
|
||||
|
||||
free(pdata);
|
||||
}
|
||||
|
||||
void proxy_data_abort_connect(proxyData* pdata)
|
||||
{
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(pdata->abort_event);
|
||||
(void)SetEvent(pdata->abort_event);
|
||||
if (pdata->pc)
|
||||
freerdp_abort_connect_context(&pdata->pc->context);
|
||||
}
|
||||
|
||||
BOOL proxy_data_shall_disconnect(proxyData* pdata)
|
||||
{
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(pdata->abort_event);
|
||||
return WaitForSingleObject(pdata->abort_event, 0) == WAIT_OBJECT_0;
|
||||
}
|
||||
211
third_party/FreeRDP/server/proxy/pf_input.c
vendored
Normal file
211
third_party/FreeRDP/server/proxy/pf_input.c
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server
|
||||
*
|
||||
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
|
||||
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
|
||||
* Copyright 2021 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2021 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 <winpr/assert.h>
|
||||
|
||||
#include "pf_input.h"
|
||||
#include <freerdp/server/proxy/proxy_config.h>
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
#include "proxy_modules.h"
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_server_check_and_sync_input_state(pClientContext* pc)
|
||||
{
|
||||
WINPR_ASSERT(pc);
|
||||
|
||||
if (!freerdp_is_active_state(&pc->context))
|
||||
return FALSE;
|
||||
if (pc->input_state_sync_pending)
|
||||
{
|
||||
BOOL rc = freerdp_input_send_synchronize_event(pc->context.input, pc->input_state);
|
||||
if (rc)
|
||||
pc->input_state_sync_pending = FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_server_synchronize_event(rdpInput* input, UINT32 flags)
|
||||
{
|
||||
pServerContext* ps = nullptr;
|
||||
pClientContext* pc = nullptr;
|
||||
|
||||
WINPR_ASSERT(input);
|
||||
ps = (pServerContext*)input->context;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->pdata);
|
||||
|
||||
pc = ps->pdata->pc;
|
||||
WINPR_ASSERT(pc);
|
||||
|
||||
pc->input_state = flags;
|
||||
pc->input_state_sync_pending = TRUE;
|
||||
|
||||
return pf_server_check_and_sync_input_state(pc);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_server_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code)
|
||||
{
|
||||
const proxyConfig* config = nullptr;
|
||||
proxyKeyboardEventInfo event = WINPR_C_ARRAY_INIT;
|
||||
pServerContext* ps = nullptr;
|
||||
pClientContext* pc = nullptr;
|
||||
|
||||
WINPR_ASSERT(input);
|
||||
ps = (pServerContext*)input->context;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->pdata);
|
||||
|
||||
pc = ps->pdata->pc;
|
||||
WINPR_ASSERT(pc);
|
||||
|
||||
config = ps->pdata->config;
|
||||
WINPR_ASSERT(config);
|
||||
|
||||
if (!pf_server_check_and_sync_input_state(pc))
|
||||
return TRUE;
|
||||
|
||||
if (!config->Keyboard)
|
||||
return TRUE;
|
||||
|
||||
event.flags = flags;
|
||||
event.rdp_scan_code = code;
|
||||
|
||||
if (pf_modules_run_filter(pc->pdata->module, FILTER_TYPE_KEYBOARD, pc->pdata, &event))
|
||||
return freerdp_input_send_keyboard_event(pc->context.input, flags, code);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_server_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
|
||||
{
|
||||
const proxyConfig* config = nullptr;
|
||||
proxyUnicodeEventInfo event = WINPR_C_ARRAY_INIT;
|
||||
pServerContext* ps = nullptr;
|
||||
pClientContext* pc = nullptr;
|
||||
|
||||
WINPR_ASSERT(input);
|
||||
ps = (pServerContext*)input->context;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->pdata);
|
||||
|
||||
pc = ps->pdata->pc;
|
||||
WINPR_ASSERT(pc);
|
||||
|
||||
config = ps->pdata->config;
|
||||
WINPR_ASSERT(config);
|
||||
|
||||
if (!pf_server_check_and_sync_input_state(pc))
|
||||
return TRUE;
|
||||
|
||||
if (!config->Keyboard)
|
||||
return TRUE;
|
||||
|
||||
event.flags = flags;
|
||||
event.code = code;
|
||||
if (pf_modules_run_filter(pc->pdata->module, FILTER_TYPE_UNICODE, pc->pdata, &event))
|
||||
return freerdp_input_send_unicode_keyboard_event(pc->context.input, flags, code);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_server_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
|
||||
{
|
||||
proxyMouseEventInfo event = WINPR_C_ARRAY_INIT;
|
||||
const proxyConfig* config = nullptr;
|
||||
pServerContext* ps = nullptr;
|
||||
pClientContext* pc = nullptr;
|
||||
|
||||
WINPR_ASSERT(input);
|
||||
ps = (pServerContext*)input->context;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->pdata);
|
||||
|
||||
pc = ps->pdata->pc;
|
||||
WINPR_ASSERT(pc);
|
||||
|
||||
config = ps->pdata->config;
|
||||
WINPR_ASSERT(config);
|
||||
|
||||
if (!pf_server_check_and_sync_input_state(pc))
|
||||
return TRUE;
|
||||
|
||||
if (!config->Mouse)
|
||||
return TRUE;
|
||||
|
||||
event.flags = flags;
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
|
||||
if (pf_modules_run_filter(pc->pdata->module, FILTER_TYPE_MOUSE, pc->pdata, &event))
|
||||
return freerdp_input_send_mouse_event(pc->context.input, flags, x, y);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_server_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
|
||||
{
|
||||
const proxyConfig* config = nullptr;
|
||||
proxyMouseExEventInfo event = WINPR_C_ARRAY_INIT;
|
||||
pServerContext* ps = nullptr;
|
||||
pClientContext* pc = nullptr;
|
||||
|
||||
WINPR_ASSERT(input);
|
||||
ps = (pServerContext*)input->context;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->pdata);
|
||||
|
||||
pc = ps->pdata->pc;
|
||||
WINPR_ASSERT(pc);
|
||||
|
||||
config = ps->pdata->config;
|
||||
WINPR_ASSERT(config);
|
||||
|
||||
if (!pf_server_check_and_sync_input_state(pc))
|
||||
return TRUE;
|
||||
|
||||
if (!config->Mouse)
|
||||
return TRUE;
|
||||
|
||||
event.flags = flags;
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
if (pf_modules_run_filter(pc->pdata->module, FILTER_TYPE_MOUSE, pc->pdata, &event))
|
||||
return freerdp_input_send_extended_mouse_event(pc->context.input, flags, x, y);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void pf_server_register_input_callbacks(rdpInput* input)
|
||||
{
|
||||
WINPR_ASSERT(input);
|
||||
|
||||
input->SynchronizeEvent = pf_server_synchronize_event;
|
||||
input->KeyboardEvent = pf_server_keyboard_event;
|
||||
input->UnicodeKeyboardEvent = pf_server_unicode_keyboard_event;
|
||||
input->MouseEvent = pf_server_mouse_event;
|
||||
input->ExtendedMouseEvent = pf_server_extended_mouse_event;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user