Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View 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})

View 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")

View 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")

View File

@@ -0,0 +1,3 @@
set(FREERDP_SERVER_NAME "mfreerdp-server")
set(FREERDP_SERVER_PLATFORM "X11")
set(FREERDP_SERVER_VENDOR "FreeRDP")

View 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;
}

View 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 */

View 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));
}

View 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
View 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);
}

View 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 */

View 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;
}

View 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 */

View File

View 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 */

View 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;
}

View 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
View 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;
}

View 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 */

View 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);
}

View 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 */

View 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 */

View 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;
}

View 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 */

View 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")

View File

@@ -0,0 +1,3 @@
set(FREERDP_SERVER_NAME "sfreerdp-server")
set(FREERDP_SERVER_PLATFORM "Sample")
set(FREERDP_SERVER_VENDOR "FreeRDP")

Binary file not shown.

View 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);
}

View 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 */

View 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
}

View 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 */

View 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);
}

View 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 */

View 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);
}

View 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 */

File diff suppressed because it is too large Load Diff

View 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 */

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View 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")

View File

@@ -0,0 +1,3 @@
set(FREERDP_SERVER_NAME "wfreerdp-server")
set(FREERDP_SERVER_PLATFORM "Windows")
set(FREERDP_SERVER_VENDOR "FreeRDP")

View 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")

View 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;
}

View 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 */

View 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;
}

View 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 */

View 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

View 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 */

View 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;
}

View 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 */

View 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;
}

View 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 */

View 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);
}

View 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 */

View 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;
}
}

View 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 */

View 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;
}

View 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 */

View 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;
}

View 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 */

View 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;
}

View 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 */

View 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);
}
}

View 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 */

View 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;
}

View 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 */

View 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")

View 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)");
}

View 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}

View 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()

View 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")

View 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})

View 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;
}

View 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_ */

File diff suppressed because it is too large Load Diff

View 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 */

View 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;
}

View 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 */

View 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}")

View 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>

View 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;
}

View 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}

View 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)

View 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`.

View 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()

View 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

View 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()

View 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

View 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()

View 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

View 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}

View 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;
}

View 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_ */

File diff suppressed because it is too large Load Diff

View 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 */

File diff suppressed because it is too large Load Diff

View 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;
}

View 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