Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
127
third_party/FreeRDP/server/proxy/CMakeLists.txt
vendored
Normal file
127
third_party/FreeRDP/server/proxy/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP Proxy Server
|
||||
#
|
||||
# Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
|
||||
# Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
# Copyright 2019 Idan Freiberg <speidy@gmail.com>
|
||||
# Copyright 2021 Armin Novak <anovak@thincast.com>
|
||||
# Copyright 2021 Thincast Technologies GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
include(CMakeDependentOption)
|
||||
set(MODULE_NAME "freerdp-server-proxy")
|
||||
set(MODULE_PREFIX "FREERDP_SERVER_PROXY")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
pf_context.c
|
||||
pf_channel.c
|
||||
pf_channel.h
|
||||
pf_client.c
|
||||
pf_client.h
|
||||
pf_input.c
|
||||
pf_input.h
|
||||
pf_update.c
|
||||
pf_update.h
|
||||
pf_server.c
|
||||
pf_server.h
|
||||
pf_config.c
|
||||
pf_modules.c
|
||||
pf_utils.h
|
||||
pf_utils.c
|
||||
$<TARGET_OBJECTS:pf_channels>
|
||||
)
|
||||
|
||||
set(PROXY_APP_SRCS freerdp_proxy.c)
|
||||
|
||||
option(WITH_PROXY_EMULATE_SMARTCARD "Compile proxy smartcard emulation" OFF)
|
||||
add_subdirectory("channels")
|
||||
|
||||
addtargetwithresourcefile(${MODULE_NAME} FALSE "${FREERDP_VERSION}" ${MODULE_PREFIX}_SRCS)
|
||||
|
||||
set(PRIVATE_LIBS freerdp-client freerdp-server)
|
||||
|
||||
set(PUBLIC_LIBS winpr freerdp)
|
||||
|
||||
target_include_directories(${MODULE_NAME} INTERFACE $<INSTALL_INTERFACE:include>)
|
||||
target_link_libraries(${MODULE_NAME} PRIVATE ${PRIVATE_LIBS} PUBLIC ${PUBLIC_LIBS})
|
||||
|
||||
installwithrpath(
|
||||
TARGETS
|
||||
${MODULE_NAME}
|
||||
COMPONENT
|
||||
server
|
||||
EXPORT
|
||||
FreeRDP-ProxyTargets
|
||||
ARCHIVE
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/Proxy")
|
||||
|
||||
# pkg-config
|
||||
# Do not set Requires.Private if not a static build
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
set(FREERDP_PROXY_PC_REQUIRES_PRIVATE "freerdp-client${FREERDP_API_VERSION} freerdp-server${FREERDP_API_VERSION}")
|
||||
set(FREERDP_PROXY_PC_LIBS_PRIVATE "-ldl -lpthread")
|
||||
endif()
|
||||
set(FREERDP_PROXY_PC_REQUIRES freerdp-server${FREERDP_API_VERSION})
|
||||
|
||||
include(pkg-config-install-prefix)
|
||||
cleaning_configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/freerdp-proxy.pc.in ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME}${FREERDP_VERSION_MAJOR}.pc
|
||||
@ONLY
|
||||
)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME}${FREERDP_VERSION_MAJOR}.pc
|
||||
DESTINATION ${PKG_CONFIG_PC_INSTALL_DIR}
|
||||
)
|
||||
|
||||
export(PACKAGE freerdp-proxy)
|
||||
|
||||
setfreerdpcmakeinstalldir(FREERDP_PROXY_CMAKE_INSTALL_DIR "FreeRDP-Proxy${FREERDP_VERSION_MAJOR}")
|
||||
|
||||
configure_package_config_file(
|
||||
FreeRDP-ProxyConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/FreeRDP-ProxyConfig.cmake
|
||||
INSTALL_DESTINATION ${FREERDP_PROXY_CMAKE_INSTALL_DIR} PATH_VARS FREERDP_INCLUDE_DIR
|
||||
)
|
||||
|
||||
write_basic_package_version_file(
|
||||
${CMAKE_CURRENT_BINARY_DIR}/FreeRDP-ProxyConfigVersion.cmake VERSION ${FREERDP_VERSION}
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FreeRDP-ProxyConfig.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/FreeRDP-ProxyConfigVersion.cmake
|
||||
DESTINATION ${FREERDP_PROXY_CMAKE_INSTALL_DIR}
|
||||
)
|
||||
install(EXPORT FreeRDP-ProxyTargets DESTINATION ${FREERDP_PROXY_CMAKE_INSTALL_DIR})
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/proxy")
|
||||
|
||||
option(WITH_PROXY_APP "Compile proxy application" ON)
|
||||
|
||||
if(WITH_PROXY_APP)
|
||||
add_subdirectory("cli")
|
||||
endif()
|
||||
|
||||
option(WITH_PROXY_MODULES "Compile proxy modules" ON)
|
||||
if(WITH_PROXY_MODULES)
|
||||
add_subdirectory("modules")
|
||||
endif()
|
||||
10
third_party/FreeRDP/server/proxy/FreeRDP-ProxyConfig.cmake.in
vendored
Normal file
10
third_party/FreeRDP/server/proxy/FreeRDP-ProxyConfig.cmake.in
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
set(FreeRDP-Proxy_VERSION_MAJOR "@FREERDP_VERSION_MAJOR@")
|
||||
set(FreeRDP-Proxy_VERSION_MINOR "@FREERDP_VERSION_MINOR@")
|
||||
set(FreeRDP-Proxy_VERSION_REVISION "@FREERDP_VERSION_REVISION@")
|
||||
|
||||
set_and_check(FreeRDP-Proxy_INCLUDE_DIR "@PACKAGE_FREERDP_INCLUDE_DIR@")
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/FreeRDP-ProxyTargets.cmake")
|
||||
8
third_party/FreeRDP/server/proxy/channels/CMakeLists.txt
vendored
Normal file
8
third_party/FreeRDP/server/proxy/channels/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
set(MODULE_NAME pf_channels)
|
||||
set(SOURCES pf_channel_rdpdr.c pf_channel_rdpdr.h pf_channel_drdynvc.c pf_channel_drdynvc.h)
|
||||
|
||||
if(WITH_PROXY_EMULATE_SMARTCARD)
|
||||
list(APPEND SOURCES pf_channel_smartcard.c pf_channel_smartcard.h)
|
||||
endif()
|
||||
|
||||
add_library(${MODULE_NAME} OBJECT ${SOURCES})
|
||||
924
third_party/FreeRDP/server/proxy/channels/pf_channel_drdynvc.c
vendored
Normal file
924
third_party/FreeRDP/server/proxy/channels/pf_channel_drdynvc.c
vendored
Normal file
@@ -0,0 +1,924 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* pf_channel_drdynvc
|
||||
*
|
||||
* Copyright 2022 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include <freerdp/channels/drdynvc.h>
|
||||
#include <freerdp/utils/drdynvc.h>
|
||||
#include <freerdp/server/proxy/proxy_log.h>
|
||||
|
||||
#include "pf_channel_drdynvc.h"
|
||||
#include "../pf_channel.h"
|
||||
#include "../proxy_modules.h"
|
||||
#include "../pf_utils.h"
|
||||
|
||||
#define DTAG PROXY_TAG("drdynvc")
|
||||
|
||||
#define Stream_CheckAndLogRequiredLengthWLogWithBackend(log, s, nmemb, backdata) \
|
||||
Stream_CheckAndLogRequiredLengthWLogEx(log, WLOG_WARN, s, nmemb, 1, "%s(%s:%" PRIuz ")[%s]", \
|
||||
__func__, __FILE__, (size_t)__LINE__, \
|
||||
getDirection(backdata))
|
||||
|
||||
/** @brief channel opened status */
|
||||
typedef enum
|
||||
{
|
||||
CHANNEL_OPENSTATE_WAITING_OPEN_STATUS, /*!< dynamic channel waiting for create response */
|
||||
CHANNEL_OPENSTATE_OPENED, /*!< opened */
|
||||
CHANNEL_OPENSTATE_CLOSED /*!< dynamic channel has been opened then closed */
|
||||
} PfDynChannelOpenStatus;
|
||||
|
||||
typedef struct p_server_dynamic_channel_context pServerDynamicChannelContext;
|
||||
typedef struct DynChannelTrackerState DynChannelTrackerState;
|
||||
|
||||
typedef PfChannelResult (*dynamic_channel_on_data_fn)(pServerContext* ps,
|
||||
pServerDynamicChannelContext* channel,
|
||||
BOOL isBackData, ChannelStateTracker* tracker,
|
||||
BOOL firstPacket, BOOL lastPacket);
|
||||
|
||||
/** @brief tracker state for a drdynvc stream */
|
||||
struct DynChannelTrackerState
|
||||
{
|
||||
UINT32 currentDataLength;
|
||||
UINT32 CurrentDataReceived;
|
||||
UINT32 CurrentDataFragments;
|
||||
wStream* currentPacket;
|
||||
WINPR_ATTR_NODISCARD dynamic_channel_on_data_fn dataCallback;
|
||||
};
|
||||
|
||||
typedef void (*channel_data_dtor_fn)(void** user_data);
|
||||
|
||||
struct p_server_dynamic_channel_context
|
||||
{
|
||||
char* channelName;
|
||||
UINT32 channelId;
|
||||
PfDynChannelOpenStatus openStatus;
|
||||
pf_utils_channel_mode channelMode;
|
||||
BOOL packetReassembly;
|
||||
DynChannelTrackerState backTracker;
|
||||
DynChannelTrackerState frontTracker;
|
||||
|
||||
void* channelData;
|
||||
channel_data_dtor_fn channelDataDtor;
|
||||
};
|
||||
|
||||
/** @brief context for the dynamic channel */
|
||||
typedef struct
|
||||
{
|
||||
wHashTable* channels;
|
||||
ChannelStateTracker* backTracker;
|
||||
ChannelStateTracker* frontTracker;
|
||||
wLog* log;
|
||||
} DynChannelContext;
|
||||
|
||||
/** @brief result of dynamic channel packet treatment */
|
||||
typedef enum
|
||||
{
|
||||
DYNCVC_READ_OK, /*!< read was OK */
|
||||
DYNCVC_READ_ERROR, /*!< an error happened during read */
|
||||
DYNCVC_READ_INCOMPLETE /*!< missing bytes to read the complete packet */
|
||||
} DynvcReadResult;
|
||||
|
||||
static const char* openstatus2str(PfDynChannelOpenStatus status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case CHANNEL_OPENSTATE_WAITING_OPEN_STATUS:
|
||||
return "CHANNEL_OPENSTATE_WAITING_OPEN_STATUS";
|
||||
case CHANNEL_OPENSTATE_CLOSED:
|
||||
return "CHANNEL_OPENSTATE_CLOSED";
|
||||
case CHANNEL_OPENSTATE_OPENED:
|
||||
return "CHANNEL_OPENSTATE_OPENED";
|
||||
default:
|
||||
return "CHANNEL_OPENSTATE_UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
#define DynvcTrackerLog(log, level, dynChannel, cmd, isBackData, ...) \
|
||||
dyn_log_((log), (level), (dynChannel), (cmd), (isBackData), __func__, __FILE__, __LINE__, \
|
||||
__VA_ARGS__)
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static const char* getDirection(BOOL isBackData)
|
||||
{
|
||||
return isBackData ? "B->F" : "F->B";
|
||||
}
|
||||
|
||||
static void dyn_log_(wLog* log, DWORD level, const pServerDynamicChannelContext* dynChannel,
|
||||
BYTE cmd, BOOL isBackData, const char* fkt, const char* file, size_t line,
|
||||
const char* fmt, ...)
|
||||
{
|
||||
if (!WLog_IsLevelActive(log, level))
|
||||
return;
|
||||
|
||||
char* prefix = nullptr;
|
||||
char* msg = nullptr;
|
||||
size_t prefixlen = 0;
|
||||
size_t msglen = 0;
|
||||
|
||||
uint32_t channelId = dynChannel ? dynChannel->channelId : UINT32_MAX;
|
||||
const char* channelName = dynChannel ? dynChannel->channelName : "<nullptr>";
|
||||
(void)winpr_asprintf(&prefix, &prefixlen, "DynvcTracker[%s](%s [%s:%" PRIu32 "])",
|
||||
getDirection(isBackData), channelName, drdynvc_get_packet_type(cmd),
|
||||
channelId);
|
||||
|
||||
va_list ap = WINPR_C_ARRAY_INIT;
|
||||
va_start(ap, fmt);
|
||||
(void)winpr_vasprintf(&msg, &msglen, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
WLog_PrintTextMessage(log, level, line, file, fkt, "%s: %s", prefix, msg);
|
||||
free(prefix);
|
||||
free(msg);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult data_cb(pServerContext* ps, pServerDynamicChannelContext* channel,
|
||||
BOOL isBackData, ChannelStateTracker* tracker, BOOL firstPacket,
|
||||
BOOL lastPacket)
|
||||
{
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(channel);
|
||||
WINPR_ASSERT(tracker);
|
||||
WINPR_ASSERT(ps->pdata);
|
||||
|
||||
wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
|
||||
proxyDynChannelInterceptData dyn = { .name = channel->channelName,
|
||||
.channelId = channel->channelId,
|
||||
.data = currentPacket,
|
||||
.isBackData = isBackData,
|
||||
.first = firstPacket,
|
||||
.last = lastPacket,
|
||||
.rewritten = FALSE,
|
||||
.packetSize = channelTracker_getCurrentPacketSize(tracker),
|
||||
.result = PF_CHANNEL_RESULT_ERROR };
|
||||
Stream_SealLength(dyn.data);
|
||||
if (!pf_modules_run_filter(ps->pdata->module, FILTER_TYPE_INTERCEPT_CHANNEL, ps->pdata, &dyn))
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
|
||||
channelTracker_setCurrentPacketSize(tracker, dyn.packetSize);
|
||||
if (dyn.rewritten)
|
||||
return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
|
||||
return dyn.result;
|
||||
}
|
||||
|
||||
static void DynamicChannelContext_free(void* ptr)
|
||||
{
|
||||
pServerDynamicChannelContext* c = (pServerDynamicChannelContext*)ptr;
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
if (c->backTracker.currentPacket)
|
||||
Stream_Free(c->backTracker.currentPacket, TRUE);
|
||||
|
||||
if (c->frontTracker.currentPacket)
|
||||
Stream_Free(c->frontTracker.currentPacket, TRUE);
|
||||
|
||||
if (c->channelDataDtor)
|
||||
c->channelDataDtor(&c->channelData);
|
||||
|
||||
free(c->channelName);
|
||||
free(c);
|
||||
}
|
||||
|
||||
WINPR_ATTR_MALLOC(DynamicChannelContext_free, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
static pServerDynamicChannelContext* DynamicChannelContext_new(wLog* log, pServerContext* ps,
|
||||
const char* name, UINT32 id)
|
||||
{
|
||||
WINPR_ASSERT(log);
|
||||
|
||||
pServerDynamicChannelContext* ret = calloc(1, sizeof(*ret));
|
||||
if (!ret)
|
||||
{
|
||||
WLog_Print(log, WLOG_ERROR, "error allocating dynamic channel context '%s'", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ret->channelId = id;
|
||||
ret->channelName = _strdup(name);
|
||||
if (!ret->channelName)
|
||||
{
|
||||
WLog_Print(log, WLOG_ERROR, "error allocating name in dynamic channel context '%s'", name);
|
||||
free(ret);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ret->frontTracker.dataCallback = data_cb;
|
||||
ret->backTracker.dataCallback = data_cb;
|
||||
|
||||
proxyChannelToInterceptData dyn = { .name = name, .channelId = id, .intercept = FALSE };
|
||||
if (pf_modules_run_filter(ps->pdata->module, FILTER_TYPE_DYN_INTERCEPT_LIST, ps->pdata, &dyn) &&
|
||||
dyn.intercept)
|
||||
ret->channelMode = PF_UTILS_CHANNEL_INTERCEPT;
|
||||
else
|
||||
ret->channelMode = pf_utils_get_channel_mode(ps->pdata->config, name);
|
||||
ret->openStatus = CHANNEL_OPENSTATE_OPENED;
|
||||
ret->packetReassembly = (ret->channelMode == PF_UTILS_CHANNEL_INTERCEPT);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static UINT32 ChannelId_Hash(const void* key)
|
||||
{
|
||||
const UINT32* v = (const UINT32*)key;
|
||||
return *v;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL ChannelId_Compare(const void* objA, const void* objB)
|
||||
{
|
||||
const UINT32* v1 = objA;
|
||||
const UINT32* v2 = objB;
|
||||
return (*v1 == *v2);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static DynvcReadResult dynvc_read_varInt(wLog* log, wStream* s, size_t len, UINT64* varInt,
|
||||
BOOL last)
|
||||
{
|
||||
WINPR_ASSERT(varInt);
|
||||
switch (len)
|
||||
{
|
||||
case 0x00:
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 1))
|
||||
return last ? DYNCVC_READ_ERROR : DYNCVC_READ_INCOMPLETE;
|
||||
Stream_Read_UINT8(s, *varInt);
|
||||
break;
|
||||
case 0x01:
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 2))
|
||||
return last ? DYNCVC_READ_ERROR : DYNCVC_READ_INCOMPLETE;
|
||||
Stream_Read_UINT16(s, *varInt);
|
||||
break;
|
||||
case 0x02:
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 4))
|
||||
return last ? DYNCVC_READ_ERROR : DYNCVC_READ_INCOMPLETE;
|
||||
Stream_Read_UINT32(s, *varInt);
|
||||
break;
|
||||
case 0x03:
|
||||
default:
|
||||
WLog_Print(log, WLOG_ERROR, "Unknown int len %" PRIuz, len);
|
||||
return DYNCVC_READ_ERROR;
|
||||
}
|
||||
return DYNCVC_READ_OK;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult DynvcTrackerPeekHandleByMode(ChannelStateTracker* tracker,
|
||||
DynChannelTrackerState* trackerState,
|
||||
pServerDynamicChannelContext* dynChannel,
|
||||
BYTE cmd, BOOL firstPacket, BOOL lastPacket)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
WINPR_ASSERT(trackerState);
|
||||
WINPR_ASSERT(dynChannel);
|
||||
PfChannelResult result = PF_CHANNEL_RESULT_ERROR;
|
||||
|
||||
DynChannelContext* dynChannelContext =
|
||||
(DynChannelContext*)channelTracker_getCustomData(tracker);
|
||||
WINPR_ASSERT(dynChannelContext);
|
||||
|
||||
proxyData* pdata = channelTracker_getPData(tracker);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
const BOOL isBackData = (tracker == dynChannelContext->backTracker);
|
||||
switch (dynChannel->channelMode)
|
||||
{
|
||||
case PF_UTILS_CHANNEL_PASSTHROUGH:
|
||||
result = channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
|
||||
break;
|
||||
case PF_UTILS_CHANNEL_BLOCK:
|
||||
channelTracker_setMode(tracker, CHANNEL_TRACKER_DROP);
|
||||
result = PF_CHANNEL_RESULT_DROP;
|
||||
break;
|
||||
case PF_UTILS_CHANNEL_INTERCEPT:
|
||||
if (trackerState->dataCallback)
|
||||
{
|
||||
result = trackerState->dataCallback(pdata->ps, dynChannel, isBackData, tracker,
|
||||
firstPacket, lastPacket);
|
||||
}
|
||||
else
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"no intercept callback for channel, dropping packet");
|
||||
result = PF_CHANNEL_RESULT_DROP;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"unknown channel mode %u", dynChannel->channelMode);
|
||||
result = PF_CHANNEL_RESULT_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!trackerState->currentDataLength ||
|
||||
(trackerState->CurrentDataReceived == trackerState->currentDataLength))
|
||||
{
|
||||
trackerState->currentDataLength = 0;
|
||||
trackerState->CurrentDataFragments = 0;
|
||||
trackerState->CurrentDataReceived = 0;
|
||||
|
||||
if (dynChannel->packetReassembly && trackerState->currentPacket)
|
||||
Stream_ResetPosition(trackerState->currentPacket);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult DynvcTrackerHandleClose(ChannelStateTracker* tracker,
|
||||
pServerDynamicChannelContext* dynChannel,
|
||||
DynChannelContext* dynChannelContext,
|
||||
BOOL firstPacket, BOOL lastPacket)
|
||||
{
|
||||
WINPR_ASSERT(dynChannelContext);
|
||||
|
||||
const BOOL isBackData = (tracker == dynChannelContext->backTracker);
|
||||
|
||||
if (!lastPacket || !dynChannel)
|
||||
return PF_CHANNEL_RESULT_DROP;
|
||||
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, CLOSE_REQUEST_PDU, isBackData,
|
||||
"Close request");
|
||||
channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
|
||||
if (dynChannel->openStatus != CHANNEL_OPENSTATE_OPENED)
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, CLOSE_REQUEST_PDU,
|
||||
isBackData, "is in state %s, expected %s",
|
||||
openstatus2str(dynChannel->openStatus),
|
||||
openstatus2str(CHANNEL_OPENSTATE_OPENED));
|
||||
}
|
||||
dynChannel->openStatus = CHANNEL_OPENSTATE_CLOSED;
|
||||
return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult DynvcTrackerHandleCreateBack(ChannelStateTracker* tracker, wStream* s,
|
||||
DWORD flags, proxyData* pdata,
|
||||
pServerDynamicChannelContext* dynChannel,
|
||||
DynChannelContext* dynChannelContext,
|
||||
UINT64 dynChannelId)
|
||||
{
|
||||
proxyChannelDataEventInfo dev = WINPR_C_ARRAY_INIT;
|
||||
const char* name = Stream_ConstPointer(s);
|
||||
const size_t nameLen = Stream_GetRemainingLength(s);
|
||||
const size_t len = strnlen(name, nameLen);
|
||||
const BOOL isBackData = (tracker == dynChannelContext->backTracker);
|
||||
const BYTE cmd = CREATE_REQUEST_PDU;
|
||||
|
||||
if ((len == 0) || (len == nameLen) || (dynChannelId > UINT16_MAX))
|
||||
{
|
||||
char namebuffer[64] = WINPR_C_ARRAY_INIT;
|
||||
(void)_snprintf(namebuffer, sizeof(namebuffer) - 1, "%s", name);
|
||||
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"channel id %" PRIu64 ", name=%s [%" PRIuz "|%" PRIuz "], status=%s",
|
||||
dynChannelId, namebuffer, len, nameLen,
|
||||
dynChannel ? openstatus2str(dynChannel->openStatus) : "nullptr");
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
|
||||
wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
|
||||
dev.channel_id = (UINT16)dynChannelId;
|
||||
dev.channel_name = name;
|
||||
dev.data = Stream_Buffer(s);
|
||||
dev.data_len = Stream_GetPosition(currentPacket);
|
||||
dev.flags = flags;
|
||||
dev.total_size = Stream_GetPosition(currentPacket);
|
||||
|
||||
if (dynChannel)
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_WARN, dynChannel, cmd, isBackData,
|
||||
"Reusing channel id, now %s", name);
|
||||
|
||||
HashTable_Remove(dynChannelContext->channels, &dynChannel->channelId);
|
||||
}
|
||||
|
||||
if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE,
|
||||
pdata, &dev))
|
||||
return PF_CHANNEL_RESULT_DROP; /* Silently drop */
|
||||
|
||||
dynChannel =
|
||||
DynamicChannelContext_new(dynChannelContext->log, pdata->ps, name, (UINT32)dynChannelId);
|
||||
if (!dynChannel)
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"unable to create dynamic channel context data");
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
|
||||
"Adding channel");
|
||||
if (!HashTable_Insert(dynChannelContext->channels, &dynChannel->channelId, dynChannel))
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"unable register dynamic channel context data");
|
||||
DynamicChannelContext_free(dynChannel);
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
|
||||
dynChannel->openStatus = CHANNEL_OPENSTATE_WAITING_OPEN_STATUS;
|
||||
|
||||
const BOOL firstPacket = (flags & CHANNEL_FLAG_FIRST) != 0;
|
||||
const BOOL lastPacket = (flags & CHANNEL_FLAG_LAST) != 0;
|
||||
|
||||
// NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert owns dynChannel
|
||||
return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, FALSE);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult DynvcTrackerHandleCreateFront(ChannelStateTracker* tracker, wStream* s,
|
||||
DWORD flags,
|
||||
WINPR_ATTR_UNUSED proxyData* pdata,
|
||||
pServerDynamicChannelContext* dynChannel,
|
||||
DynChannelContext* dynChannelContext,
|
||||
WINPR_ATTR_UNUSED UINT64 dynChannelId)
|
||||
{
|
||||
const BOOL isBackData = (tracker == dynChannelContext->backTracker);
|
||||
const BYTE cmd = CREATE_REQUEST_PDU;
|
||||
|
||||
/* CREATE_REQUEST_PDU response */
|
||||
if (!Stream_CheckAndLogRequiredLengthWLogWithBackend(dynChannelContext->log, s, 4, FALSE))
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
|
||||
const UINT32 creationStatus = Stream_Get_UINT32(s);
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
|
||||
"CREATE_RESPONSE openStatus=%" PRIu32, creationStatus);
|
||||
|
||||
if (dynChannel && (creationStatus == 0))
|
||||
dynChannel->openStatus = CHANNEL_OPENSTATE_OPENED;
|
||||
|
||||
const BOOL firstPacket = (flags & CHANNEL_FLAG_FIRST) != 0;
|
||||
const BOOL lastPacket = (flags & CHANNEL_FLAG_LAST) != 0;
|
||||
|
||||
return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, TRUE);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult DynvcTrackerHandleCreate(ChannelStateTracker* tracker, wStream* s,
|
||||
DWORD flags,
|
||||
pServerDynamicChannelContext* dynChannel,
|
||||
UINT64 dynChannelId)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
DynChannelContext* dynChannelContext =
|
||||
(DynChannelContext*)channelTracker_getCustomData(tracker);
|
||||
WINPR_ASSERT(dynChannelContext);
|
||||
|
||||
const BOOL lastPacket = (flags & CHANNEL_FLAG_LAST) != 0;
|
||||
const BOOL isBackData = (tracker == dynChannelContext->backTracker);
|
||||
|
||||
proxyData* pdata = channelTracker_getPData(tracker);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
/* we only want the full packet */
|
||||
if (!lastPacket)
|
||||
return PF_CHANNEL_RESULT_DROP;
|
||||
|
||||
if (isBackData)
|
||||
return DynvcTrackerHandleCreateBack(tracker, s, flags, pdata, dynChannel, dynChannelContext,
|
||||
dynChannelId);
|
||||
|
||||
return DynvcTrackerHandleCreateFront(tracker, s, flags, pdata, dynChannel, dynChannelContext,
|
||||
dynChannelId);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult DynvcTrackerHandleCmdDATA(ChannelStateTracker* tracker,
|
||||
pServerDynamicChannelContext* dynChannel,
|
||||
wStream* s, BYTE cmd, UINT64 Length,
|
||||
BOOL firstPacket, BOOL lastPacket)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
DynChannelContext* dynChannelContext =
|
||||
(DynChannelContext*)channelTracker_getCustomData(tracker);
|
||||
WINPR_ASSERT(dynChannelContext);
|
||||
|
||||
const BOOL isBackData = (tracker == dynChannelContext->backTracker);
|
||||
|
||||
if (!dynChannel)
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_WARN, dynChannel, cmd, isBackData,
|
||||
"channel is nullptr, dropping packet");
|
||||
return PF_CHANNEL_RESULT_DROP;
|
||||
}
|
||||
|
||||
DynChannelTrackerState* trackerState =
|
||||
isBackData ? &dynChannel->backTracker : &dynChannel->frontTracker;
|
||||
if (dynChannel->openStatus != CHANNEL_OPENSTATE_OPENED)
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_WARN, dynChannel, cmd, isBackData,
|
||||
"channel is not opened, dropping packet");
|
||||
return PF_CHANNEL_RESULT_DROP;
|
||||
}
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case DATA_FIRST_PDU:
|
||||
case DATA_FIRST_COMPRESSED_PDU:
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
|
||||
"DATA_FIRST currentPacketLength=%" PRIu64 "", Length);
|
||||
if (Length > UINT32_MAX)
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"Length out of bounds: %" PRIu64, Length);
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
trackerState->currentDataLength = (UINT32)Length;
|
||||
trackerState->CurrentDataReceived = 0;
|
||||
trackerState->CurrentDataFragments = 0;
|
||||
|
||||
if (dynChannel->packetReassembly)
|
||||
{
|
||||
if (trackerState->currentPacket)
|
||||
Stream_ResetPosition(trackerState->currentPacket);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case DATA_PDU:
|
||||
case DATA_FIRST_PDU:
|
||||
{
|
||||
size_t extraSize = Stream_GetRemainingLength(s);
|
||||
|
||||
trackerState->CurrentDataFragments++;
|
||||
trackerState->CurrentDataReceived += WINPR_ASSERTING_INT_CAST(uint32_t, extraSize);
|
||||
|
||||
if (dynChannel->packetReassembly)
|
||||
{
|
||||
if (!trackerState->currentPacket)
|
||||
{
|
||||
trackerState->currentPacket = Stream_New(nullptr, 1024);
|
||||
if (!trackerState->currentPacket)
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd,
|
||||
isBackData, "unable to create current packet",
|
||||
getDirection(isBackData), dynChannel->channelName,
|
||||
drdynvc_get_packet_type(cmd));
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(trackerState->currentPacket, extraSize))
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"unable to grow current packet", getDirection(isBackData),
|
||||
dynChannel->channelName, drdynvc_get_packet_type(cmd));
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
|
||||
Stream_Write(trackerState->currentPacket, Stream_ConstPointer(s), extraSize);
|
||||
}
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
|
||||
"frags=%" PRIu32 " received=%" PRIu32 "(%" PRIu32 ")",
|
||||
trackerState->CurrentDataFragments, trackerState->CurrentDataReceived,
|
||||
trackerState->currentDataLength);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case DATA_PDU:
|
||||
{
|
||||
if (trackerState->currentDataLength)
|
||||
{
|
||||
if (trackerState->CurrentDataReceived > trackerState->currentDataLength)
|
||||
{
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"reassembled packet (%" PRIu32
|
||||
") is bigger than announced length (%" PRIu32 ")",
|
||||
trackerState->CurrentDataReceived,
|
||||
trackerState->currentDataLength);
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
trackerState->CurrentDataFragments = 0;
|
||||
trackerState->CurrentDataReceived = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return DynvcTrackerPeekHandleByMode(tracker, trackerState, dynChannel, cmd, firstPacket,
|
||||
lastPacket);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult DynvcTrackerHandleCmd(ChannelStateTracker* tracker,
|
||||
pServerDynamicChannelContext* dynChannel, wStream* s,
|
||||
BYTE cmd, UINT32 flags, UINT64 Length,
|
||||
UINT64 dynChannelId, BOOL firstPacket, BOOL lastPacket)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
DynChannelContext* dynChannelContext =
|
||||
(DynChannelContext*)channelTracker_getCustomData(tracker);
|
||||
WINPR_ASSERT(dynChannelContext);
|
||||
|
||||
const BOOL isBackData = (tracker == dynChannelContext->backTracker);
|
||||
switch (cmd)
|
||||
{
|
||||
case CAPABILITY_REQUEST_PDU:
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
|
||||
"CAPABILITY_%s", isBackData ? "REQUEST" : "RESPONSE");
|
||||
channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
|
||||
return PF_CHANNEL_RESULT_PASS;
|
||||
|
||||
case CREATE_REQUEST_PDU:
|
||||
return DynvcTrackerHandleCreate(tracker, s, flags, dynChannel, dynChannelId);
|
||||
|
||||
case CLOSE_REQUEST_PDU:
|
||||
return DynvcTrackerHandleClose(tracker, dynChannel, dynChannelContext, firstPacket,
|
||||
lastPacket);
|
||||
|
||||
case SOFT_SYNC_REQUEST_PDU:
|
||||
/* just pass then as is for now */
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
|
||||
"SOFT_SYNC_REQUEST_PDU");
|
||||
channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
|
||||
/*TODO: return pf_treat_softsync_req(pdata, s);*/
|
||||
return PF_CHANNEL_RESULT_PASS;
|
||||
|
||||
case SOFT_SYNC_RESPONSE_PDU:
|
||||
/* just pass then as is for now */
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
|
||||
"SOFT_SYNC_RESPONSE_PDU");
|
||||
channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
|
||||
return PF_CHANNEL_RESULT_PASS;
|
||||
|
||||
case DATA_FIRST_PDU:
|
||||
case DATA_PDU:
|
||||
return DynvcTrackerHandleCmdDATA(tracker, dynChannel, s, cmd, Length, firstPacket,
|
||||
lastPacket);
|
||||
|
||||
case DATA_FIRST_COMPRESSED_PDU:
|
||||
case DATA_COMPRESSED_PDU:
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
|
||||
"TODO: compressed data packets, pass them as is for now");
|
||||
channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
|
||||
return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
|
||||
|
||||
default:
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"Invalid command ID");
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult DynvcTrackerPeekFn(ChannelStateTracker* tracker, BOOL firstPacket,
|
||||
BOOL lastPacket)
|
||||
{
|
||||
wStream* s = nullptr;
|
||||
wStream sbuffer;
|
||||
BOOL haveChannelId = 0;
|
||||
BOOL haveLength = 0;
|
||||
UINT64 dynChannelId = 0;
|
||||
UINT64 Length = 0;
|
||||
pServerDynamicChannelContext* dynChannel = nullptr;
|
||||
|
||||
WINPR_ASSERT(tracker);
|
||||
|
||||
DynChannelContext* dynChannelContext =
|
||||
(DynChannelContext*)channelTracker_getCustomData(tracker);
|
||||
WINPR_ASSERT(dynChannelContext);
|
||||
|
||||
const BOOL isBackData = (tracker == dynChannelContext->backTracker);
|
||||
|
||||
UINT32 flags = lastPacket ? CHANNEL_FLAG_LAST : 0;
|
||||
if (firstPacket)
|
||||
flags |= CHANNEL_FLAG_FIRST;
|
||||
|
||||
{
|
||||
wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
|
||||
s = Stream_StaticConstInit(&sbuffer, Stream_Buffer(currentPacket),
|
||||
Stream_GetPosition(currentPacket));
|
||||
}
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLogWithBackend(dynChannelContext->log, s, 1, isBackData))
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
|
||||
const BYTE byte0 = Stream_Get_UINT8(s);
|
||||
const BYTE cmd = byte0 >> 4;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case CREATE_REQUEST_PDU:
|
||||
case CLOSE_REQUEST_PDU:
|
||||
case DATA_PDU:
|
||||
case DATA_COMPRESSED_PDU:
|
||||
haveChannelId = TRUE;
|
||||
haveLength = FALSE;
|
||||
break;
|
||||
case DATA_FIRST_PDU:
|
||||
case DATA_FIRST_COMPRESSED_PDU:
|
||||
haveLength = TRUE;
|
||||
haveChannelId = TRUE;
|
||||
break;
|
||||
default:
|
||||
haveChannelId = FALSE;
|
||||
haveLength = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (haveChannelId)
|
||||
{
|
||||
BYTE cbId = byte0 & 0x03;
|
||||
|
||||
switch (dynvc_read_varInt(dynChannelContext->log, s, cbId, &dynChannelId, lastPacket))
|
||||
{
|
||||
case DYNCVC_READ_OK:
|
||||
break;
|
||||
case DYNCVC_READ_INCOMPLETE:
|
||||
return PF_CHANNEL_RESULT_DROP;
|
||||
case DYNCVC_READ_ERROR:
|
||||
default:
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"invalid channelId field");
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
|
||||
/* we always try to retrieve the dynamic channel in case it would have been opened
|
||||
* and closed
|
||||
*/
|
||||
dynChannel = (pServerDynamicChannelContext*)HashTable_GetItemValue(
|
||||
dynChannelContext->channels, &dynChannelId);
|
||||
if ((cmd != CREATE_REQUEST_PDU) || !isBackData)
|
||||
{
|
||||
if (!dynChannel || (dynChannel->openStatus == CHANNEL_OPENSTATE_CLOSED))
|
||||
{
|
||||
/* we've not found the target channel, so we drop this chunk, plus all the rest of
|
||||
* the packet */
|
||||
channelTracker_setMode(tracker, CHANNEL_TRACKER_DROP);
|
||||
return PF_CHANNEL_RESULT_DROP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (haveLength)
|
||||
{
|
||||
BYTE lenLen = (byte0 >> 2) & 0x03;
|
||||
switch (dynvc_read_varInt(dynChannelContext->log, s, lenLen, &Length, lastPacket))
|
||||
{
|
||||
case DYNCVC_READ_OK:
|
||||
break;
|
||||
case DYNCVC_READ_INCOMPLETE:
|
||||
return PF_CHANNEL_RESULT_DROP;
|
||||
case DYNCVC_READ_ERROR:
|
||||
default:
|
||||
DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
|
||||
"invalid length field");
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return DynvcTrackerHandleCmd(tracker, dynChannel, s, cmd, flags, Length, dynChannelId,
|
||||
firstPacket, lastPacket);
|
||||
}
|
||||
|
||||
static void DynChannelContext_free(void* context)
|
||||
{
|
||||
DynChannelContext* c = context;
|
||||
if (!c)
|
||||
return;
|
||||
channelTracker_free(c->backTracker);
|
||||
channelTracker_free(c->frontTracker);
|
||||
HashTable_Free(c->channels);
|
||||
free(c);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static const char* dynamic_context(void* arg)
|
||||
{
|
||||
proxyData* pdata = arg;
|
||||
if (!pdata)
|
||||
return "pdata=null";
|
||||
return pdata->session_id;
|
||||
}
|
||||
|
||||
WINPR_ATTR_MALLOC(DynChannelContext_free, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
static DynChannelContext* DynChannelContext_new(proxyData* pdata,
|
||||
pServerStaticChannelContext* channel)
|
||||
{
|
||||
DynChannelContext* dyn = calloc(1, sizeof(DynChannelContext));
|
||||
if (!dyn)
|
||||
return nullptr;
|
||||
|
||||
dyn->log = WLog_Get(DTAG);
|
||||
WINPR_ASSERT(dyn->log);
|
||||
WLog_SetContext(dyn->log, dynamic_context, pdata);
|
||||
|
||||
dyn->backTracker = channelTracker_new(channel, DynvcTrackerPeekFn, dyn);
|
||||
if (!dyn->backTracker)
|
||||
goto fail;
|
||||
if (!channelTracker_setPData(dyn->backTracker, pdata))
|
||||
goto fail;
|
||||
|
||||
dyn->frontTracker = channelTracker_new(channel, DynvcTrackerPeekFn, dyn);
|
||||
if (!dyn->frontTracker)
|
||||
goto fail;
|
||||
if (!channelTracker_setPData(dyn->frontTracker, pdata))
|
||||
goto fail;
|
||||
|
||||
dyn->channels = HashTable_New(FALSE);
|
||||
if (!dyn->channels)
|
||||
goto fail;
|
||||
|
||||
if (!HashTable_SetHashFunction(dyn->channels, ChannelId_Hash))
|
||||
goto fail;
|
||||
|
||||
{
|
||||
wObject* kobj = HashTable_KeyObject(dyn->channels);
|
||||
WINPR_ASSERT(kobj);
|
||||
kobj->fnObjectEquals = ChannelId_Compare;
|
||||
}
|
||||
|
||||
{
|
||||
wObject* vobj = HashTable_ValueObject(dyn->channels);
|
||||
WINPR_ASSERT(vobj);
|
||||
vobj->fnObjectFree = DynamicChannelContext_free;
|
||||
}
|
||||
|
||||
return dyn;
|
||||
|
||||
fail:
|
||||
DynChannelContext_free(dyn);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult pf_dynvc_back_data(proxyData* pdata,
|
||||
const pServerStaticChannelContext* channel,
|
||||
const BYTE* xdata, size_t xsize, UINT32 flags,
|
||||
size_t totalSize)
|
||||
{
|
||||
WINPR_ASSERT(channel);
|
||||
|
||||
DynChannelContext* dyn = (DynChannelContext*)channel->context;
|
||||
WINPR_UNUSED(pdata);
|
||||
WINPR_ASSERT(dyn);
|
||||
|
||||
return channelTracker_update(dyn->backTracker, xdata, xsize, flags, totalSize);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult pf_dynvc_front_data(proxyData* pdata,
|
||||
const pServerStaticChannelContext* channel,
|
||||
const BYTE* xdata, size_t xsize, UINT32 flags,
|
||||
size_t totalSize)
|
||||
{
|
||||
WINPR_ASSERT(channel);
|
||||
|
||||
DynChannelContext* dyn = (DynChannelContext*)channel->context;
|
||||
WINPR_UNUSED(pdata);
|
||||
WINPR_ASSERT(dyn);
|
||||
|
||||
return channelTracker_update(dyn->frontTracker, xdata, xsize, flags, totalSize);
|
||||
}
|
||||
|
||||
BOOL pf_channel_setup_drdynvc(proxyData* pdata, pServerStaticChannelContext* channel)
|
||||
{
|
||||
DynChannelContext* ret = DynChannelContext_new(pdata, channel);
|
||||
if (!ret)
|
||||
return FALSE;
|
||||
|
||||
channel->onBackData = pf_dynvc_back_data;
|
||||
channel->onFrontData = pf_dynvc_front_data;
|
||||
channel->contextDtor = DynChannelContext_free;
|
||||
channel->context = ret;
|
||||
return TRUE;
|
||||
}
|
||||
27
third_party/FreeRDP/server/proxy/channels/pf_channel_drdynvc.h
vendored
Normal file
27
third_party/FreeRDP/server/proxy/channels/pf_channel_drdynvc.h
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* pf_channel_drdynvc
|
||||
*
|
||||
* Copyright 2022 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef SERVER_PROXY_CHANNELS_PF_CHANNEL_DRDYNVC_H_
|
||||
#define SERVER_PROXY_CHANNELS_PF_CHANNEL_DRDYNVC_H_
|
||||
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_setup_drdynvc(proxyData* pdata,
|
||||
pServerStaticChannelContext* channel);
|
||||
|
||||
#endif /* SERVER_PROXY_CHANNELS_PF_CHANNEL_DRDYNVC_H_ */
|
||||
2075
third_party/FreeRDP/server/proxy/channels/pf_channel_rdpdr.c
vendored
Normal file
2075
third_party/FreeRDP/server/proxy/channels/pf_channel_rdpdr.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
50
third_party/FreeRDP/server/proxy/channels/pf_channel_rdpdr.h
vendored
Normal file
50
third_party/FreeRDP/server/proxy/channels/pf_channel_rdpdr.h
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server
|
||||
*
|
||||
* Copyright 2021 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2021 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_PROXY_RDPDR_H
|
||||
#define FREERDP_SERVER_PROXY_RDPDR_H
|
||||
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_setup_rdpdr(pServerContext* ps,
|
||||
pServerStaticChannelContext* channel);
|
||||
|
||||
void pf_channel_rdpdr_client_free(pClientContext* pc);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_rdpdr_client_new(pClientContext* pc);
|
||||
|
||||
BOOL pf_channel_rdpdr_client_reset(pClientContext* pc);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_rdpdr_client_handle(pClientContext* pc, UINT16 channelId,
|
||||
const char* channel_name,
|
||||
const BYTE* xdata, size_t xsize,
|
||||
UINT32 flags, size_t totalSize);
|
||||
|
||||
void pf_channel_rdpdr_server_free(pServerContext* ps);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_rdpdr_server_new(pServerContext* ps);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_rdpdr_server_announce(pServerContext* ps);
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_rdpdr_server_handle(pServerContext* ps, UINT16 channelId,
|
||||
const char* channel_name,
|
||||
const BYTE* xdata, size_t xsize,
|
||||
UINT32 flags, size_t totalSize);
|
||||
|
||||
#endif /* FREERDP_SERVER_PROXY_RDPDR_H */
|
||||
394
third_party/FreeRDP/server/proxy/channels/pf_channel_smartcard.c
vendored
Normal file
394
third_party/FreeRDP/server/proxy/channels/pf_channel_smartcard.c
vendored
Normal file
@@ -0,0 +1,394 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server
|
||||
*
|
||||
* Copyright 2021 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2021 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/string.h>
|
||||
|
||||
#include <winpr/smartcard.h>
|
||||
#include <winpr/pool.h>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_log.h>
|
||||
#include <freerdp/emulate/scard/smartcard_emulate.h>
|
||||
#include <freerdp/channels/scard.h>
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
#include <freerdp/utils/rdpdr_utils.h>
|
||||
|
||||
#include <freerdp/utils/smartcard_operations.h>
|
||||
#include <freerdp/utils/smartcard_call.h>
|
||||
|
||||
#include "pf_channel_smartcard.h"
|
||||
#include "pf_channel_rdpdr.h"
|
||||
|
||||
#define TAG PROXY_TAG("channel.scard")
|
||||
|
||||
#define SCARD_SVC_CHANNEL_NAME "SCARD"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
InterceptContextMapEntry base;
|
||||
scard_call_context* callctx;
|
||||
wArrayList* workObjects;
|
||||
} pf_channel_client_context;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SMARTCARD_OPERATION op;
|
||||
wStream* out;
|
||||
pClientContext* pc;
|
||||
wLog* log;
|
||||
pf_scard_send_fkt_t send_fkt;
|
||||
} pf_channel_client_queue_element;
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static pf_channel_client_context* scard_get_client_context(pClientContext* pc)
|
||||
{
|
||||
pf_channel_client_context* scard = nullptr;
|
||||
|
||||
WINPR_ASSERT(pc);
|
||||
WINPR_ASSERT(pc->interceptContextMap);
|
||||
|
||||
scard = HashTable_GetItemValue(pc->interceptContextMap, SCARD_SVC_CHANNEL_NAME);
|
||||
if (!scard)
|
||||
WLog_WARN(TAG, "[%s] missing in pc->interceptContextMap", SCARD_SVC_CHANNEL_NAME);
|
||||
return scard;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_channel_client_write_iostatus(wStream* out, const SMARTCARD_OPERATION* op,
|
||||
NTSTATUS ioStatus)
|
||||
{
|
||||
UINT16 component = 0;
|
||||
UINT16 packetid = 0;
|
||||
UINT32 dID = 0;
|
||||
UINT32 cID = 0;
|
||||
size_t pos = 0;
|
||||
|
||||
WINPR_ASSERT(op);
|
||||
WINPR_ASSERT(out);
|
||||
|
||||
pos = Stream_GetPosition(out);
|
||||
Stream_ResetPosition(out);
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, out, 16))
|
||||
return FALSE;
|
||||
|
||||
Stream_Read_UINT16(out, component);
|
||||
Stream_Read_UINT16(out, packetid);
|
||||
|
||||
Stream_Read_UINT32(out, dID);
|
||||
Stream_Read_UINT32(out, cID);
|
||||
|
||||
WINPR_ASSERT(component == RDPDR_CTYP_CORE);
|
||||
WINPR_ASSERT(packetid == PAKID_CORE_DEVICE_IOCOMPLETION);
|
||||
WINPR_ASSERT(dID == op->deviceID);
|
||||
WINPR_ASSERT(cID == op->completionID);
|
||||
|
||||
Stream_Write_INT32(out, ioStatus);
|
||||
Stream_SetPosition(out, pos);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
struct thread_arg
|
||||
{
|
||||
pf_channel_client_context* scard;
|
||||
pf_channel_client_queue_element* e;
|
||||
};
|
||||
|
||||
static void queue_free(void* obj);
|
||||
|
||||
WINPR_ATTR_MALLOC(queue_free, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
static void* queue_copy(const void* obj);
|
||||
|
||||
static VOID irp_thread(WINPR_ATTR_UNUSED PTP_CALLBACK_INSTANCE Instance, PVOID Context,
|
||||
PTP_WORK Work)
|
||||
{
|
||||
struct thread_arg* arg = Context;
|
||||
pf_channel_client_context* scard = arg->scard;
|
||||
{
|
||||
NTSTATUS ioStatus = 0;
|
||||
LONG rc = smartcard_irp_device_control_call(arg->scard->callctx, arg->e->out, &ioStatus,
|
||||
&arg->e->op);
|
||||
if (rc == CHANNEL_RC_OK)
|
||||
{
|
||||
if (pf_channel_client_write_iostatus(arg->e->out, &arg->e->op, ioStatus))
|
||||
arg->e->send_fkt(arg->e->log, arg->e->pc, arg->e->out);
|
||||
}
|
||||
}
|
||||
queue_free(arg->e);
|
||||
free(arg);
|
||||
ArrayList_Remove(scard->workObjects, Work);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL start_irp_thread(pf_channel_client_context* scard,
|
||||
const pf_channel_client_queue_element* e)
|
||||
{
|
||||
PTP_WORK work = nullptr;
|
||||
struct thread_arg* arg = calloc(1, sizeof(struct thread_arg));
|
||||
if (!arg)
|
||||
return FALSE;
|
||||
arg->scard = scard;
|
||||
arg->e = queue_copy(e);
|
||||
if (!arg->e)
|
||||
goto fail;
|
||||
|
||||
work = CreateThreadpoolWork(irp_thread, arg, nullptr);
|
||||
if (!work)
|
||||
goto fail;
|
||||
ArrayList_Append(scard->workObjects, work);
|
||||
SubmitThreadpoolWork(work);
|
||||
|
||||
return TRUE;
|
||||
|
||||
fail:
|
||||
if (arg)
|
||||
queue_free(arg->e);
|
||||
free(arg);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL pf_channel_smartcard_client_handle(wLog* log, pClientContext* pc, wStream* s, wStream* out,
|
||||
pf_scard_send_fkt_t send_fkt)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
LONG status = 0;
|
||||
UINT32 FileId = 0;
|
||||
UINT32 CompletionId = 0;
|
||||
NTSTATUS ioStatus = 0;
|
||||
pf_channel_client_queue_element e = WINPR_C_ARRAY_INIT;
|
||||
pf_channel_client_context* scard = scard_get_client_context(pc);
|
||||
|
||||
WINPR_ASSERT(log);
|
||||
WINPR_ASSERT(send_fkt);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
if (!scard)
|
||||
return FALSE;
|
||||
|
||||
e.log = log;
|
||||
e.pc = pc;
|
||||
e.out = out;
|
||||
e.send_fkt = send_fkt;
|
||||
|
||||
/* Skip IRP header */
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
|
||||
return FALSE;
|
||||
else
|
||||
{
|
||||
const uint32_t DeviceId = Stream_Get_UINT32(s); /* DeviceId (4 bytes) */
|
||||
FileId = Stream_Get_UINT32(s); /* FileId (4 bytes) */
|
||||
CompletionId = Stream_Get_UINT32(s); /* CompletionId (4 bytes) */
|
||||
const uint32_t MajorFunction = Stream_Get_UINT32(s); /* MajorFunction (4 bytes) */
|
||||
const uint32_t MinorFunction = Stream_Get_UINT32(s); /* MinorFunction (4 bytes) */
|
||||
|
||||
if (MajorFunction != IRP_MJ_DEVICE_CONTROL)
|
||||
{
|
||||
WLog_WARN(TAG, "[%s] Invalid IRP received, expected %s, got %s [0x%08" PRIx32 "]",
|
||||
SCARD_SVC_CHANNEL_NAME, rdpdr_irp_string(IRP_MJ_DEVICE_CONTROL),
|
||||
rdpdr_irp_string(MajorFunction), MinorFunction);
|
||||
return FALSE;
|
||||
}
|
||||
e.op.completionID = CompletionId;
|
||||
e.op.deviceID = DeviceId;
|
||||
|
||||
if (!rdpdr_write_iocompletion_header(out, DeviceId, CompletionId, 0))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
status = smartcard_irp_device_control_decode(s, CompletionId, FileId, &e.op);
|
||||
if (status != 0)
|
||||
goto fail;
|
||||
|
||||
switch (e.op.ioControlCode)
|
||||
{
|
||||
case SCARD_IOCTL_LISTREADERGROUPSA:
|
||||
case SCARD_IOCTL_LISTREADERGROUPSW:
|
||||
case SCARD_IOCTL_LISTREADERSA:
|
||||
case SCARD_IOCTL_LISTREADERSW:
|
||||
case SCARD_IOCTL_LOCATECARDSA:
|
||||
case SCARD_IOCTL_LOCATECARDSW:
|
||||
case SCARD_IOCTL_LOCATECARDSBYATRA:
|
||||
case SCARD_IOCTL_LOCATECARDSBYATRW:
|
||||
case SCARD_IOCTL_GETSTATUSCHANGEA:
|
||||
case SCARD_IOCTL_GETSTATUSCHANGEW:
|
||||
case SCARD_IOCTL_CONNECTA:
|
||||
case SCARD_IOCTL_CONNECTW:
|
||||
case SCARD_IOCTL_RECONNECT:
|
||||
case SCARD_IOCTL_DISCONNECT:
|
||||
case SCARD_IOCTL_BEGINTRANSACTION:
|
||||
case SCARD_IOCTL_ENDTRANSACTION:
|
||||
case SCARD_IOCTL_STATE:
|
||||
case SCARD_IOCTL_STATUSA:
|
||||
case SCARD_IOCTL_STATUSW:
|
||||
case SCARD_IOCTL_TRANSMIT:
|
||||
case SCARD_IOCTL_CONTROL:
|
||||
case SCARD_IOCTL_GETATTRIB:
|
||||
case SCARD_IOCTL_SETATTRIB:
|
||||
if (!start_irp_thread(scard, &e))
|
||||
goto fail;
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
status = smartcard_irp_device_control_call(scard->callctx, out, &ioStatus, &e.op);
|
||||
if (status != 0)
|
||||
goto fail;
|
||||
if (!pf_channel_client_write_iostatus(out, &e.op, ioStatus))
|
||||
goto fail;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = send_fkt(log, pc, out) == CHANNEL_RC_OK;
|
||||
|
||||
fail:
|
||||
smartcard_operation_free(&e.op, FALSE);
|
||||
return rc;
|
||||
}
|
||||
|
||||
BOOL pf_channel_smartcard_server_handle(WINPR_ATTR_UNUSED pServerContext* ps,
|
||||
WINPR_ATTR_UNUSED wStream* s)
|
||||
{
|
||||
WLog_ERR(TAG, "TODO: unimplemented");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void channel_stop_and_wait(pf_channel_client_context* scard, BOOL reset)
|
||||
{
|
||||
WINPR_ASSERT(scard);
|
||||
smartcard_call_context_signal_stop(scard->callctx, FALSE);
|
||||
|
||||
while (ArrayList_Count(scard->workObjects) > 0)
|
||||
{
|
||||
PTP_WORK work = ArrayList_GetItem(scard->workObjects, 0);
|
||||
if (!work)
|
||||
continue;
|
||||
WaitForThreadpoolWorkCallbacks(work, TRUE);
|
||||
}
|
||||
|
||||
smartcard_call_context_signal_stop(scard->callctx, reset);
|
||||
}
|
||||
|
||||
static void pf_channel_scard_client_context_free(InterceptContextMapEntry* base)
|
||||
{
|
||||
pf_channel_client_context* entry = (pf_channel_client_context*)base;
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
/* Set the stop event.
|
||||
* All threads waiting in blocking operations will abort at the next
|
||||
* available polling slot */
|
||||
channel_stop_and_wait(entry, FALSE);
|
||||
ArrayList_Free(entry->workObjects);
|
||||
|
||||
smartcard_call_context_free(entry->callctx);
|
||||
free(entry);
|
||||
}
|
||||
|
||||
static void queue_free(void* obj)
|
||||
{
|
||||
pf_channel_client_queue_element* element = obj;
|
||||
if (!element)
|
||||
return;
|
||||
smartcard_operation_free(&element->op, FALSE);
|
||||
Stream_Free(element->out, TRUE);
|
||||
free(element);
|
||||
}
|
||||
|
||||
WINPR_ATTR_MALLOC(queue_free, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
static void* queue_copy(const void* obj)
|
||||
{
|
||||
const pf_channel_client_queue_element* other = obj;
|
||||
pf_channel_client_queue_element* copy = nullptr;
|
||||
if (!other)
|
||||
return nullptr;
|
||||
copy = calloc(1, sizeof(pf_channel_client_queue_element));
|
||||
if (!copy)
|
||||
return nullptr;
|
||||
|
||||
*copy = *other;
|
||||
copy->out = Stream_New(nullptr, Stream_Capacity(other->out));
|
||||
if (!copy->out)
|
||||
goto fail;
|
||||
Stream_Write(copy->out, Stream_Buffer(other->out), Stream_GetPosition(other->out));
|
||||
return copy;
|
||||
fail:
|
||||
queue_free(copy);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void work_object_free(void* arg)
|
||||
{
|
||||
PTP_WORK work = arg;
|
||||
CloseThreadpoolWork(work);
|
||||
}
|
||||
|
||||
BOOL pf_channel_smartcard_client_new(pClientContext* pc)
|
||||
{
|
||||
pf_channel_client_context* scard = nullptr;
|
||||
wObject* obj = nullptr;
|
||||
|
||||
WINPR_ASSERT(pc);
|
||||
WINPR_ASSERT(pc->interceptContextMap);
|
||||
|
||||
scard = calloc(1, sizeof(pf_channel_client_context));
|
||||
if (!scard)
|
||||
return FALSE;
|
||||
scard->base.free = pf_channel_scard_client_context_free;
|
||||
scard->callctx = smartcard_call_context_new(pc->context.settings);
|
||||
if (!scard->callctx)
|
||||
goto fail;
|
||||
|
||||
scard->workObjects = ArrayList_New(TRUE);
|
||||
if (!scard->workObjects)
|
||||
goto fail;
|
||||
obj = ArrayList_Object(scard->workObjects);
|
||||
WINPR_ASSERT(obj);
|
||||
obj->fnObjectFree = work_object_free;
|
||||
|
||||
return HashTable_Insert(pc->interceptContextMap, SCARD_SVC_CHANNEL_NAME, scard);
|
||||
fail:
|
||||
pf_channel_scard_client_context_free(&scard->base);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void pf_channel_smartcard_client_free(pClientContext* pc)
|
||||
{
|
||||
WINPR_ASSERT(pc);
|
||||
WINPR_ASSERT(pc->interceptContextMap);
|
||||
HashTable_Remove(pc->interceptContextMap, SCARD_SVC_CHANNEL_NAME);
|
||||
}
|
||||
|
||||
BOOL pf_channel_smartcard_client_emulate(pClientContext* pc)
|
||||
{
|
||||
pf_channel_client_context* scard = scard_get_client_context(pc);
|
||||
if (!scard)
|
||||
return FALSE;
|
||||
return smartcard_call_is_configured(scard->callctx);
|
||||
}
|
||||
|
||||
BOOL pf_channel_smartcard_client_reset(pClientContext* pc)
|
||||
{
|
||||
pf_channel_client_context* scard = scard_get_client_context(pc);
|
||||
if (!scard)
|
||||
return TRUE;
|
||||
|
||||
channel_stop_and_wait(scard, TRUE);
|
||||
return TRUE;
|
||||
}
|
||||
40
third_party/FreeRDP/server/proxy/channels/pf_channel_smartcard.h
vendored
Normal file
40
third_party/FreeRDP/server/proxy/channels/pf_channel_smartcard.h
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server
|
||||
*
|
||||
* Copyright 2021 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2021 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_PROXY_SCARD_H
|
||||
#define FREERDP_SERVER_PROXY_SCARD_H
|
||||
|
||||
#include <winpr/wlog.h>
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
typedef UINT (*pf_scard_send_fkt_t)(wLog* log, pClientContext*, wStream*);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_smartcard_client_new(pClientContext* pc);
|
||||
void pf_channel_smartcard_client_free(pClientContext* pc);
|
||||
|
||||
BOOL pf_channel_smartcard_client_reset(pClientContext* pc);
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_smartcard_client_emulate(pClientContext* pc);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_smartcard_client_handle(wLog* log, pClientContext* pc,
|
||||
wStream* s, wStream* out,
|
||||
pf_scard_send_fkt_t fkt);
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_smartcard_server_handle(pServerContext* ps, wStream* s);
|
||||
|
||||
#endif /* FREERDP_SERVER_PROXY_SCARD_H */
|
||||
29
third_party/FreeRDP/server/proxy/cli/CMakeLists.txt
vendored
Normal file
29
third_party/FreeRDP/server/proxy/cli/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP Proxy Server
|
||||
#
|
||||
# Copyright 2021 Armin Novak <armin.novak@thincast.com>
|
||||
# Copyright 2021 Thincast Technologies GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set(PROXY_APP_SRCS freerdp_proxy.c)
|
||||
|
||||
set(APP_NAME "freerdp-proxy")
|
||||
addtargetwithresourcefile(${APP_NAME} TRUE "${FREERDP_VERSION}" PROXY_APP_SRCS)
|
||||
|
||||
target_link_libraries(${APP_NAME} ${MODULE_NAME})
|
||||
installwithrpath(TARGETS ${APP_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT server)
|
||||
|
||||
set_property(TARGET ${APP_NAME} PROPERTY FOLDER "Server/proxy")
|
||||
|
||||
generate_and_install_freerdp_man_from_template(${APP_NAME} "1" "${FREERDP_API_VERSION}")
|
||||
85
third_party/FreeRDP/server/proxy/cli/freerdp-proxy.1.in
vendored
Normal file
85
third_party/FreeRDP/server/proxy/cli/freerdp-proxy.1.in
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
.de URL
|
||||
\\$2 \(laURL: \\$1 \(ra\\$3
|
||||
..
|
||||
.if \n[.g] .mso www.tmac
|
||||
.TH @MANPAGE_NAME@ 1 2023-12-14 "@FREERDP_VERSION_FULL@" "FreeRDP"
|
||||
.SH NAME
|
||||
@MANPAGE_NAME@ \- A server binary allowing MITM proxying of RDP connections
|
||||
.SH SYNOPSIS
|
||||
.B @MANPAGE_NAME@
|
||||
[\fB-h\fP]
|
||||
[\fB--help\fP]
|
||||
[\fB--buildconfig\fP]
|
||||
[\fB--dump-config\fP \fB<config file>\fP]
|
||||
[\fB-v\fP]
|
||||
[\fB--version\fP]
|
||||
[\fB<config file>\fP]
|
||||
.SH DESCRIPTION
|
||||
.B @MANPAGE_NAME@
|
||||
can be used to proxy a RDP connection between a target server and connecting clients.
|
||||
Possible usage scenarios are:
|
||||
.IP Proxying
|
||||
Connect outdated/insecure RDP servers from behind a (more secure) proxy
|
||||
.IP Analysis
|
||||
Allow detailed protocol analysis of (many) unknown protocol features (channels)
|
||||
.IP Inspection
|
||||
MITM proxy for session inspection and recording
|
||||
|
||||
.SH OPTIONS
|
||||
.IP -h,--help
|
||||
Display a help text explaining usage.
|
||||
.IP --buildconfig
|
||||
Print the build configuration of the proxy and exit.
|
||||
.IP -v,--version
|
||||
Print the version of the proxy and exit.
|
||||
.IP --dump-config \fB<config-ini-file>\fP
|
||||
Dump a template configuration to \fB<config-ini-file>\fP
|
||||
.IP \fB<config-ini-file>\fP
|
||||
Start the proxy with settings read from \fB<config-ini-file>\fP
|
||||
|
||||
.SH WARNING
|
||||
The proxy does not support authentication out of the box but acts simply as intermediary.
|
||||
Only \fBRDP\fP and \fBTLS\fP security modes are supported, \fBNLA\fP will fail for connections to the proxy.
|
||||
To implement authentication a \fBproxy-module\fP can be implemented that can authenticate against some backend
|
||||
and map connecting users and credentials to target server users and credentials.
|
||||
|
||||
.SH EXAMPLES
|
||||
@MANPAGE_NAME@ /some/config/file
|
||||
|
||||
@MANPAGE_NAME@ --dump-config /some/config/file
|
||||
|
||||
.SH PREPARATIONS
|
||||
|
||||
1. generate certificates for proxy
|
||||
|
||||
\fBwinpr-makecert -rdp -path . proxy\fP
|
||||
|
||||
2. generate proxy configuration
|
||||
|
||||
\fB@MANPAGE_NAME@ --dump-config proxy.ini\fP
|
||||
|
||||
3. edit configurartion and:
|
||||
|
||||
* provide (preferably absolute) paths for \fBCertificateFile\fP and \fBPrivateKeyFile\fP generated previously
|
||||
* remove the \fBCertificateContents\fP and \fBPrivateKeyContents\fP
|
||||
* Adjust the \fB[Server]\fP settings \fBHost\fP and \fBPort\fP to bind a specific port on a network interface
|
||||
* Adjust the \fB[Target]\fP \fBHost\fP and \fBPort\fP settings to the \fBRDP\fP target server
|
||||
* Adjust (or remove if unuse) the \fBPlugins\fP settings
|
||||
|
||||
3. start proxy server
|
||||
|
||||
\fB@MANPAGE_NAME@ proxy.ini\fP
|
||||
|
||||
.SH EXIT STATUS
|
||||
.TP
|
||||
.B 0
|
||||
Successful program execution.
|
||||
.TP
|
||||
.B 1
|
||||
Otherwise.
|
||||
|
||||
.SH SEE ALSO
|
||||
wlog(7)
|
||||
|
||||
.SH AUTHOR
|
||||
FreeRDP <team@freerdp.com>
|
||||
193
third_party/FreeRDP/server/proxy/cli/freerdp_proxy.c
vendored
Normal file
193
third_party/FreeRDP/server/proxy/cli/freerdp_proxy.c
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server
|
||||
*
|
||||
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
|
||||
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/collections.h>
|
||||
|
||||
#include <freerdp/version.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_server.h>
|
||||
#include <freerdp/server/proxy/proxy_log.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define TAG PROXY_TAG("server")
|
||||
|
||||
static proxyServer* server = nullptr;
|
||||
|
||||
#if defined(_WIN32)
|
||||
WINPR_ATTR_NODISCARD
|
||||
static const char* strsignal(int signum)
|
||||
{
|
||||
switch (signum)
|
||||
{
|
||||
case SIGINT:
|
||||
return "SIGINT";
|
||||
case SIGTERM:
|
||||
return "SIGTERM";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// NOLINTBEGIN(bugprone-signal-handler,cert-msc54-cpp,cert-sig30-c)
|
||||
static void cleanup_handler(int signum)
|
||||
{
|
||||
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
||||
WLog_INFO(TAG, "caught signal %s [%d], starting cleanup...", strsignal(signum), signum);
|
||||
|
||||
WLog_INFO(TAG, "stopping all connections.");
|
||||
pf_server_stop(server);
|
||||
}
|
||||
// NOLINTEND(bugprone-signal-handler,cert-msc54-cpp,cert-sig30-c)
|
||||
|
||||
static void pf_server_register_signal_handlers(void)
|
||||
{
|
||||
(void)signal(SIGINT, cleanup_handler);
|
||||
(void)signal(SIGTERM, cleanup_handler);
|
||||
#ifndef _WIN32
|
||||
(void)signal(SIGQUIT, cleanup_handler);
|
||||
(void)signal(SIGKILL, cleanup_handler);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int usage(const char* app)
|
||||
{
|
||||
printf("Usage:\n");
|
||||
printf("%s -h Display this help text.\n", app);
|
||||
printf("%s --help Display this help text.\n", app);
|
||||
printf("%s --buildconfig Print the build configuration.\n", app);
|
||||
printf("%s <config ini file> Start the proxy with <config.ini>\n", app);
|
||||
printf("%s --dump-config <config ini file> Create a template <config.ini>\n", app);
|
||||
printf("%s -v Print out binary version.\n", app);
|
||||
printf("%s --version Print out binary version.\n", app);
|
||||
return 0;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static int version(const char* app)
|
||||
{
|
||||
printf("%s version %s", app, freerdp_get_version_string());
|
||||
return 0;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static int buildconfig(WINPR_ATTR_UNUSED const char* app)
|
||||
{
|
||||
printf("This is FreeRDP version %s (%s)\n", FREERDP_VERSION_FULL, FREERDP_GIT_REVISION);
|
||||
printf("%s", freerdp_get_build_config());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int status = -1;
|
||||
|
||||
pf_server_register_signal_handlers();
|
||||
|
||||
WLog_INFO(TAG, "freerdp-proxy version info:");
|
||||
WLog_INFO(TAG, "\tFreeRDP version: %s", FREERDP_VERSION_FULL);
|
||||
WLog_INFO(TAG, "\tGit commit: %s", FREERDP_GIT_REVISION);
|
||||
WLog_DBG(TAG, "\tBuild config: %s", freerdp_get_build_config());
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
status = usage(argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
{
|
||||
const char* arg = argv[1];
|
||||
if (_stricmp(arg, "-h") == 0)
|
||||
{
|
||||
status = usage(argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
else if (_stricmp(arg, "--help") == 0)
|
||||
{
|
||||
status = usage(argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
else if (_stricmp(arg, "--buildconfig") == 0)
|
||||
{
|
||||
status = buildconfig(argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
else if (_stricmp(arg, "--dump-config") == 0)
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
status = usage(argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
status = pf_server_config_dump(argv[2]) ? 0 : -1;
|
||||
goto fail;
|
||||
}
|
||||
else if (_stricmp(arg, "-v") == 0)
|
||||
{
|
||||
status = version(argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
else if (_stricmp(arg, "--version") == 0)
|
||||
{
|
||||
status = version(argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const char* config_path = argv[1];
|
||||
if (argc != 2)
|
||||
{
|
||||
status = usage(argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
{
|
||||
proxyConfig* config = pf_server_config_load_file(config_path);
|
||||
if (!config)
|
||||
goto fail;
|
||||
|
||||
pf_server_config_print(config);
|
||||
|
||||
server = pf_server_new(config);
|
||||
pf_server_config_free(config);
|
||||
}
|
||||
}
|
||||
|
||||
if (!server)
|
||||
goto fail;
|
||||
|
||||
if (!pf_server_start(server))
|
||||
goto fail;
|
||||
|
||||
if (!pf_server_run(server))
|
||||
goto fail;
|
||||
|
||||
status = 0;
|
||||
|
||||
fail:
|
||||
pf_server_free(server);
|
||||
|
||||
return status;
|
||||
}
|
||||
15
third_party/FreeRDP/server/proxy/freerdp-proxy.pc.in
vendored
Normal file
15
third_party/FreeRDP/server/proxy/freerdp-proxy.pc.in
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
prefix=@PKG_CONFIG_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||
includedir=${prefix}/@FREERDP_INCLUDE_DIR@
|
||||
libs=-lfreerdp-server-proxy@FREERDP_API_VERSION@
|
||||
|
||||
Name: FreeRDP proxy
|
||||
Description: FreeRDP: A Remote Desktop Protocol Implementation
|
||||
URL: http://www.freerdp.com/
|
||||
Version: @FREERDP_VERSION@
|
||||
Requires: @FREERDP_PROXY_PC_REQUIRES@
|
||||
Requires.private: @FREERDP_PROXY_PC_REQUIRES_PRIVATE@
|
||||
Libs: -L${libdir} ${libs}
|
||||
Libs.private: @FREERDP_PROXY_PC_LIBS_PRIVATE@
|
||||
Cflags: -I${includedir}
|
||||
33
third_party/FreeRDP/server/proxy/modules/CMakeLists.txt
vendored
Normal file
33
third_party/FreeRDP/server/proxy/modules/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
# Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# The third-party directory is meant for third-party components to be built
|
||||
# as part of the main FreeRDP build system, making separate maintenance easier.
|
||||
# Subdirectories of the third-party directory are ignored by git, but are
|
||||
# automatically included by CMake when the -DWITH_THIRD_PARTY=on option is used.
|
||||
|
||||
# include proxy header files for proxy modules
|
||||
include_directories("${PROJECT_SOURCE_DIR}/server/proxy")
|
||||
include_directories("${PROJECT_SOURCE_DIR}/server/proxy/modules")
|
||||
|
||||
# taken from FreeRDP/third-party/CMakeLists.txt
|
||||
file(GLOB all_valid_subdirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*/CMakeLists.txt")
|
||||
|
||||
foreach(dir ${all_valid_subdirs})
|
||||
if(${dir} MATCHES "^([^/]*)/+CMakeLists.txt")
|
||||
string(REGEX REPLACE "^([^/]*)/+CMakeLists.txt" "\\1" dir_trimmed ${dir})
|
||||
message(STATUS "Adding proxy module ${dir_trimmed}")
|
||||
add_subdirectory(${dir_trimmed})
|
||||
endif()
|
||||
endforeach(dir)
|
||||
66
third_party/FreeRDP/server/proxy/modules/README.md
vendored
Normal file
66
third_party/FreeRDP/server/proxy/modules/README.md
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
# Proxy module API
|
||||
|
||||
`freerdp-proxy` has an API for hooking/filtering certain events/messages.
|
||||
A module can register callbacks to events, allowing to record the data and control whether to pass/ignore, or right out drop the connection.
|
||||
|
||||
During startup, the proxy reads its modules from the configuration:
|
||||
|
||||
```ini
|
||||
[Plugins]
|
||||
Modules = demo,cap
|
||||
```
|
||||
|
||||
These modules are loaded in a best effort manner. Additionally there is a configuration field for modules that must be loaded,
|
||||
so the proxy refuses to start if they are not found:
|
||||
|
||||
```ini
|
||||
[Plugins]
|
||||
Required = demo,cap
|
||||
```
|
||||
|
||||
Modules must be installed as shared libraries in the `<base install>/lib/freerdp3/proxy` folder and match the pattern
|
||||
`proxy-<name>-plugin.<ext>` (e.g. `proxy-demo-plugin.so`) to be found.
|
||||
For security reasons loading by full path is not supported and only the installation path is used for lookup.
|
||||
|
||||
## Currently supported hook events
|
||||
|
||||
### Client
|
||||
|
||||
* ClientInitConnect: Called before the client tries to open a connection
|
||||
* ClientUninitConnect: Called after the client has disconnected
|
||||
* ClientPreConnect: Called in client PreConnect callback
|
||||
* ClientPostConnect: Called in client PostConnect callback
|
||||
* ClientPostDisconnect: Called in client PostDisconnect callback
|
||||
* ClientX509Certificate: Called in client X509 certificate verification callback
|
||||
* ClientLoginFailure: Called in client login failure callback
|
||||
* ClientEndPaint: Called in client EndPaint callback
|
||||
|
||||
### Server
|
||||
|
||||
* ServerPostConnect: Called after a client has connected
|
||||
* ServerPeerActivate: Called after a client has activated
|
||||
* ServerChannelsInit: Called after channels are initialized
|
||||
* ServerChannelsFree: Called after channels are cleaned up
|
||||
* ServerSessionEnd: Called after the client connection disconnected
|
||||
|
||||
## Currently supported filter events
|
||||
|
||||
* KeyboardEvent: Keyboard event, e.g. all key press and release events
|
||||
* MouseEvent: Mouse event, e.g. mouse movement and button press/release events
|
||||
* ClientChannelData: Client static channel data
|
||||
* ServerChannelData: Server static channel data
|
||||
* DynamicChannelCreate: Dynamic channel create
|
||||
* ServerFetchTargetAddr: Fetch target address (e.g. RDP TargetInfo)
|
||||
* ServerPeerLogon: A peer is logging on
|
||||
|
||||
## Developing a new module
|
||||
* Create a new file that includes `freerdp/server/proxy/proxy_modules_api.h`.
|
||||
* Implement the `proxy_module_entry_point` function and register the callbacks you are interested in.
|
||||
* Each callback receives two parameters:
|
||||
* `connectionInfo* info` holds connection info of the raised event.
|
||||
* `void* param` holds the actual event data. It should be casted by the filter to the suitable struct from `filters_api.h`.
|
||||
* Each callback must return a `BOOL`:
|
||||
* `FALSE`: The event will not be proxied.
|
||||
* `TRUE`: The event will be proxied.
|
||||
|
||||
A demo can be found in `filter_demo.c`.
|
||||
50
third_party/FreeRDP/server/proxy/modules/bitmap-filter/CMakeLists.txt
vendored
Normal file
50
third_party/FreeRDP/server/proxy/modules/bitmap-filter/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
#
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP Proxy Server Demo C++ Module
|
||||
#
|
||||
# Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
# Copyright 2021 Armin Novak <anovak@thincast.com>
|
||||
# Copyright 2021 Thincast Technologies GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
if(POLICY CMP0091)
|
||||
cmake_policy(SET CMP0091 NEW)
|
||||
endif()
|
||||
if(NOT FREERDP_DEFAULT_PROJECT_VERSION)
|
||||
set(FREERDP_DEFAULT_PROJECT_VERSION "1.0.0.0")
|
||||
endif()
|
||||
|
||||
project(proxy-bitmap-filter-plugin VERSION ${FREERDP_DEFAULT_PROJECT_VERSION} LANGUAGES CXX)
|
||||
|
||||
message("project ${PROJECT_NAME} is using version ${PROJECT_VERSION}")
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../../../cmake/)
|
||||
include(ProjectCXXStandard)
|
||||
include(CommonConfigOptions)
|
||||
include(CXXCompilerFlags)
|
||||
|
||||
set(SRCS bitmap-filter.cpp)
|
||||
addtargetwithresourcefile(${PROJECT_NAME} FALSE "${PROJECT_VERSION}" SRCS FALSE)
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE $<$<BOOL:${BUILD_SHARED_LIBS}>:BUILD_SHARED_LIBS>)
|
||||
target_link_libraries(${PROJECT_NAME} winpr freerdp)
|
||||
|
||||
installwithrpath(TARGETS ${PROJECT_NAME} DESTINATION ${FREERDP_PROXY_PLUGINDIR})
|
||||
|
||||
set(PROJECT_PC_REQUIRES_PRIVATE "winpr${FREERDP_API_VERSION} freerdp${FREERDP_API_VERSION}")
|
||||
include(ProxyModuleConfig)
|
||||
generate_proxy_module_config()
|
||||
504
third_party/FreeRDP/server/proxy/modules/bitmap-filter/bitmap-filter.cpp
vendored
Normal file
504
third_party/FreeRDP/server/proxy/modules/bitmap-filter/bitmap-filter.cpp
vendored
Normal file
@@ -0,0 +1,504 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server persist-bitmap-filter Module
|
||||
*
|
||||
* this module is designed to deactivate all persistent bitmap cache settings a
|
||||
* client might send.
|
||||
*
|
||||
* Copyright 2023 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2023 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_modules_api.h>
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
#include <freerdp/channels/drdynvc.h>
|
||||
#include <freerdp/channels/rdpgfx.h>
|
||||
#include <freerdp/utils/gfx.h>
|
||||
|
||||
#define TAG MODULE_TAG("persist-bitmap-filter")
|
||||
|
||||
// #define REPLY_WITH_EMPTY_OFFER
|
||||
|
||||
static constexpr char plugin_name[] = "bitmap-filter";
|
||||
static constexpr char plugin_desc[] =
|
||||
"this plugin deactivates and filters persistent bitmap cache.";
|
||||
|
||||
[[nodiscard]] static const std::vector<std::string>& plugin_static_intercept()
|
||||
{
|
||||
static std::vector<std::string> vec;
|
||||
if (vec.empty())
|
||||
vec.emplace_back(DRDYNVC_SVC_CHANNEL_NAME);
|
||||
return vec;
|
||||
}
|
||||
|
||||
[[nodiscard]] static const std::vector<std::string>& plugin_dyn_intercept()
|
||||
{
|
||||
static std::vector<std::string> vec;
|
||||
if (vec.empty())
|
||||
vec.emplace_back(RDPGFX_DVC_CHANNEL_NAME);
|
||||
return vec;
|
||||
}
|
||||
|
||||
class DynChannelState
|
||||
{
|
||||
|
||||
public:
|
||||
[[nodiscard]] bool skip() const
|
||||
{
|
||||
return _toSkip != 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool skip(size_t s)
|
||||
{
|
||||
if (s > _toSkip)
|
||||
_toSkip = 0;
|
||||
else
|
||||
_toSkip -= s;
|
||||
return skip();
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t remaining() const
|
||||
{
|
||||
return _toSkip;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t total() const
|
||||
{
|
||||
return _totalSkipSize;
|
||||
}
|
||||
|
||||
void setSkipSize(size_t len)
|
||||
{
|
||||
_toSkip = _totalSkipSize = len;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool drop() const
|
||||
{
|
||||
return _drop;
|
||||
}
|
||||
|
||||
void setDrop(bool d)
|
||||
{
|
||||
_drop = d;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint32_t channelId() const
|
||||
{
|
||||
return _channelId;
|
||||
}
|
||||
|
||||
void setChannelId(uint32_t id)
|
||||
{
|
||||
_channelId = id;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t _toSkip = 0;
|
||||
size_t _totalSkipSize = 0;
|
||||
bool _drop = false;
|
||||
uint32_t _channelId = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] static BOOL filter_client_pre_connect([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(pdata->pc);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
auto settings = pdata->pc->context.settings;
|
||||
|
||||
/* We do not want persistent bitmap cache to be used with proxy */
|
||||
return freerdp_settings_set_bool(settings, FreeRDP_BitmapCachePersistEnabled, FALSE);
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL filter_dyn_channel_intercept_list([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* arg)
|
||||
{
|
||||
auto data = static_cast<proxyChannelToInterceptData*>(arg);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
auto intercept = std::find(plugin_dyn_intercept().begin(), plugin_dyn_intercept().end(),
|
||||
data->name) != plugin_dyn_intercept().end();
|
||||
if (intercept)
|
||||
data->intercept = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL filter_static_channel_intercept_list([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* arg)
|
||||
{
|
||||
auto data = static_cast<proxyChannelToInterceptData*>(arg);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
auto intercept = std::find(plugin_static_intercept().begin(), plugin_static_intercept().end(),
|
||||
data->name) != plugin_static_intercept().end();
|
||||
if (intercept)
|
||||
data->intercept = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static size_t drdynvc_cblen_to_bytes(UINT8 cbLen)
|
||||
{
|
||||
switch (cbLen)
|
||||
{
|
||||
case 0:
|
||||
return 1;
|
||||
|
||||
case 1:
|
||||
return 2;
|
||||
|
||||
default:
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] static UINT32 drdynvc_read_variable_uint(wStream* s, UINT8 cbLen)
|
||||
{
|
||||
UINT32 val = 0;
|
||||
|
||||
switch (cbLen)
|
||||
{
|
||||
case 0:
|
||||
Stream_Read_UINT8(s, val);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
Stream_Read_UINT16(s, val);
|
||||
break;
|
||||
|
||||
default:
|
||||
Stream_Read_UINT32(s, val);
|
||||
break;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL drdynvc_try_read_header(wStream* s, uint32_t& channelId, size_t& length)
|
||||
{
|
||||
UINT8 value = 0;
|
||||
Stream_ResetPosition(s);
|
||||
if (Stream_GetRemainingLength(s) < 1)
|
||||
return FALSE;
|
||||
Stream_Read_UINT8(s, value);
|
||||
|
||||
const UINT8 cmd = (value & 0xf0) >> 4;
|
||||
const UINT8 Sp = (value & 0x0c) >> 2;
|
||||
const UINT8 cbChId = (value & 0x03);
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case DATA_PDU:
|
||||
case DATA_FIRST_PDU:
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const size_t channelIdLen = drdynvc_cblen_to_bytes(cbChId);
|
||||
if (Stream_GetRemainingLength(s) < channelIdLen)
|
||||
return FALSE;
|
||||
|
||||
channelId = drdynvc_read_variable_uint(s, cbChId);
|
||||
length = Stream_Length(s);
|
||||
if (cmd == DATA_FIRST_PDU)
|
||||
{
|
||||
const size_t dataLen = drdynvc_cblen_to_bytes(Sp);
|
||||
if (Stream_GetRemainingLength(s) < dataLen)
|
||||
return FALSE;
|
||||
|
||||
length = drdynvc_read_variable_uint(s, Sp);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static DynChannelState* filter_get_plugin_data(proxyPlugin* plugin, proxyData* pdata)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
auto mgr = static_cast<proxyPluginsManager*>(plugin->custom);
|
||||
WINPR_ASSERT(mgr);
|
||||
|
||||
WINPR_ASSERT(mgr->GetPluginData);
|
||||
return static_cast<DynChannelState*>(mgr->GetPluginData(mgr, plugin_name, pdata));
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL filter_set_plugin_data(proxyPlugin* plugin, proxyData* pdata,
|
||||
DynChannelState* data)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
auto mgr = static_cast<proxyPluginsManager*>(plugin->custom);
|
||||
WINPR_ASSERT(mgr);
|
||||
|
||||
WINPR_ASSERT(mgr->SetPluginData);
|
||||
return mgr->SetPluginData(mgr, plugin_name, pdata, data);
|
||||
}
|
||||
|
||||
#if defined(REPLY_WITH_EMPTY_OFFER)
|
||||
[[nodiscard]] static UINT8 drdynvc_value_to_cblen(UINT32 value)
|
||||
{
|
||||
if (value <= 0xFF)
|
||||
return 0;
|
||||
if (value <= 0xFFFF)
|
||||
return 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL drdynvc_write_variable_uint(wStream* s, UINT32 value, UINT8 cbLen)
|
||||
{
|
||||
switch (cbLen)
|
||||
{
|
||||
case 0:
|
||||
Stream_Write_UINT8(s, static_cast<UINT8>(value));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
Stream_Write_UINT16(s, static_cast<UINT16>(value));
|
||||
break;
|
||||
|
||||
default:
|
||||
Stream_Write_UINT32(s, value);
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL drdynvc_write_header(wStream* s, UINT32 channelId)
|
||||
{
|
||||
const UINT8 cbChId = drdynvc_value_to_cblen(channelId);
|
||||
const UINT8 value = (DATA_PDU << 4) | cbChId;
|
||||
const size_t len = drdynvc_cblen_to_bytes(cbChId) + 1;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, len))
|
||||
return FALSE;
|
||||
|
||||
Stream_Write_UINT8(s, value);
|
||||
return drdynvc_write_variable_uint(s, value, cbChId);
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL filter_forward_empty_offer(const char* sessionID,
|
||||
proxyDynChannelInterceptData* data,
|
||||
size_t startPosition, UINT32 channelId)
|
||||
{
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
Stream_SetPosition(data->data, startPosition);
|
||||
if (!drdynvc_write_header(data->data, channelId))
|
||||
return FALSE;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(data->data, sizeof(UINT16)))
|
||||
return FALSE;
|
||||
Stream_Write_UINT16(data->data, 0);
|
||||
Stream_SealLength(data->data);
|
||||
|
||||
WLog_INFO(TAG, "[SessionID=%s][%s] forwarding empty %s", sessionID, plugin_name,
|
||||
rdpgfx_get_cmd_id_string(RDPGFX_CMDID_CACHEIMPORTOFFER));
|
||||
data->rewritten = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
[[nodiscard]] static BOOL filter_dyn_channel_intercept(proxyPlugin* plugin, proxyData* pdata,
|
||||
void* arg)
|
||||
{
|
||||
auto data = static_cast<proxyDynChannelInterceptData*>(arg);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
data->result = PF_CHANNEL_RESULT_PASS;
|
||||
if (!data->isBackData &&
|
||||
(strncmp(data->name, RDPGFX_DVC_CHANNEL_NAME, ARRAYSIZE(RDPGFX_DVC_CHANNEL_NAME)) == 0))
|
||||
{
|
||||
auto state = filter_get_plugin_data(plugin, pdata);
|
||||
if (!state)
|
||||
{
|
||||
WLog_ERR(TAG, "[SessionID=%s][%s] missing custom data, aborting!", pdata->session_id,
|
||||
plugin_name);
|
||||
return FALSE;
|
||||
}
|
||||
const size_t inputDataLength = Stream_Length(data->data);
|
||||
UINT16 cmdId = RDPGFX_CMDID_UNUSED_0000;
|
||||
|
||||
const auto pos = Stream_GetPosition(data->data);
|
||||
if (!state->skip())
|
||||
{
|
||||
if (data->first)
|
||||
{
|
||||
uint32_t channelId = 0;
|
||||
size_t length = 0;
|
||||
if (drdynvc_try_read_header(data->data, channelId, length))
|
||||
{
|
||||
if (Stream_GetRemainingLength(data->data) >= 2)
|
||||
{
|
||||
Stream_Read_UINT16(data->data, cmdId);
|
||||
state->setSkipSize(length);
|
||||
state->setDrop(false);
|
||||
}
|
||||
}
|
||||
|
||||
switch (cmdId)
|
||||
{
|
||||
case RDPGFX_CMDID_CACHEIMPORTOFFER:
|
||||
state->setDrop(true);
|
||||
state->setChannelId(channelId);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
Stream_SetPosition(data->data, pos);
|
||||
}
|
||||
}
|
||||
|
||||
if (state->skip())
|
||||
{
|
||||
if (state->skip(inputDataLength))
|
||||
{
|
||||
WLog_DBG(TAG,
|
||||
"skipping data, but %" PRIuz " bytes left [stream has %" PRIuz " bytes]",
|
||||
state->remaining(), inputDataLength);
|
||||
}
|
||||
|
||||
if (state->drop())
|
||||
{
|
||||
WLog_WARN(TAG,
|
||||
"[SessionID=%s][%s] dropping %s packet [total:%" PRIuz ", current:%" PRIuz
|
||||
", remaining: %" PRIuz "]",
|
||||
pdata->session_id, plugin_name,
|
||||
rdpgfx_get_cmd_id_string(RDPGFX_CMDID_CACHEIMPORTOFFER), state->total(),
|
||||
inputDataLength, state->remaining());
|
||||
data->result = PF_CHANNEL_RESULT_DROP;
|
||||
|
||||
#if defined(REPLY_WITH_EMPTY_OFFER) // TODO: Sending this does screw up some windows RDP server
|
||||
// versions :/
|
||||
if (state->remaining() == 0)
|
||||
{
|
||||
if (!filter_forward_empty_offer(pdata->session_id, data, pos,
|
||||
state->channelId()))
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL filter_server_session_started(proxyPlugin* plugin, proxyData* pdata,
|
||||
void* /*unused*/)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
auto state = filter_get_plugin_data(plugin, pdata);
|
||||
delete state;
|
||||
|
||||
auto newstate = new DynChannelState();
|
||||
if (!filter_set_plugin_data(plugin, pdata, newstate))
|
||||
{
|
||||
delete newstate;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL filter_server_session_end(proxyPlugin* plugin, proxyData* pdata,
|
||||
void* /*unused*/)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
auto state = filter_get_plugin_data(plugin, pdata);
|
||||
delete state;
|
||||
return filter_set_plugin_data(plugin, pdata, nullptr);
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL int_proxy_module_entry_point(proxyPluginsManager* plugins_manager,
|
||||
void* userdata)
|
||||
{
|
||||
proxyPlugin plugin = {};
|
||||
|
||||
plugin.name = plugin_name;
|
||||
plugin.description = plugin_desc;
|
||||
|
||||
plugin.ServerSessionStarted = filter_server_session_started;
|
||||
plugin.ServerSessionEnd = filter_server_session_end;
|
||||
|
||||
plugin.ClientPreConnect = filter_client_pre_connect;
|
||||
|
||||
plugin.StaticChannelToIntercept = filter_static_channel_intercept_list;
|
||||
plugin.DynChannelToIntercept = filter_dyn_channel_intercept_list;
|
||||
plugin.DynChannelIntercept = filter_dyn_channel_intercept;
|
||||
|
||||
plugin.custom = plugins_manager;
|
||||
if (!plugin.custom)
|
||||
return FALSE;
|
||||
plugin.userdata = userdata;
|
||||
|
||||
return plugins_manager->RegisterPlugin(plugins_manager, &plugin);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#if defined(BUILD_SHARED_LIBS)
|
||||
[[nodiscard]]
|
||||
FREERDP_API BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata);
|
||||
|
||||
BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata)
|
||||
{
|
||||
return int_proxy_module_entry_point(plugins_manager, userdata);
|
||||
}
|
||||
#else
|
||||
[[nodiscard]]
|
||||
FREERDP_API BOOL bitmap_filter_proxy_module_entry_point(proxyPluginsManager* plugins_manager,
|
||||
void* userdata);
|
||||
BOOL bitmap_filter_proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata)
|
||||
{
|
||||
return int_proxy_module_entry_point(plugins_manager, userdata);
|
||||
}
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
50
third_party/FreeRDP/server/proxy/modules/demo/CMakeLists.txt
vendored
Normal file
50
third_party/FreeRDP/server/proxy/modules/demo/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
#
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP Proxy Server Demo C++ Module
|
||||
#
|
||||
# Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
# Copyright 2021 Armin Novak <anovak@thincast.com>
|
||||
# Copyright 2021 Thincast Technologies GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
if(POLICY CMP0091)
|
||||
cmake_policy(SET CMP0091 NEW)
|
||||
endif()
|
||||
if(NOT FREERDP_DEFAULT_PROJECT_VERSION)
|
||||
set(FREERDP_DEFAULT_PROJECT_VERSION "1.0.0.0")
|
||||
endif()
|
||||
|
||||
project(proxy-demo-plugin VERSION ${FREERDP_DEFAULT_PROJECT_VERSION} LANGUAGES CXX)
|
||||
|
||||
message("project ${PROJECT_NAME} is using version ${PROJECT_VERSION}")
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../../../cmake/)
|
||||
include(ProjectCXXStandard)
|
||||
include(CommonConfigOptions)
|
||||
include(CXXCompilerFlags)
|
||||
|
||||
set(SRCS demo.cpp)
|
||||
addtargetwithresourcefile(${PROJECT_NAME} FALSE "${PROJECT_VERSION}" SRCS FALSE)
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE $<$<BOOL:${BUILD_SHARED_LIBS}>:BUILD_SHARED_LIBS>)
|
||||
target_link_libraries(${PROJECT_NAME} winpr)
|
||||
|
||||
installwithrpath(TARGETS ${PROJECT_NAME} DESTINATION ${FREERDP_PROXY_PLUGINDIR})
|
||||
|
||||
set(PROJECT_PC_REQUIRES_PRIVATE "winpr${FREERDP_API_VERSION}")
|
||||
include(ProxyModuleConfig)
|
||||
generate_proxy_module_config()
|
||||
513
third_party/FreeRDP/server/proxy/modules/demo/demo.cpp
vendored
Normal file
513
third_party/FreeRDP/server/proxy/modules/demo/demo.cpp
vendored
Normal file
@@ -0,0 +1,513 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server Demo C++ Module
|
||||
*
|
||||
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
* Copyright 2021 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2021 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <freerdp/api.h>
|
||||
#include <freerdp/scancode.h>
|
||||
#include <freerdp/server/proxy/proxy_modules_api.h>
|
||||
|
||||
#define TAG MODULE_TAG("demo")
|
||||
|
||||
struct demo_custom_data
|
||||
{
|
||||
proxyPluginsManager* mgr;
|
||||
int somesetting;
|
||||
};
|
||||
|
||||
static constexpr char plugin_name[] = "demo";
|
||||
static constexpr char plugin_desc[] = "this is a test plugin";
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_plugin_unload([[maybe_unused]] proxyPlugin* plugin)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
|
||||
std::cout << "C++ demo plugin: unloading..." << std::endl;
|
||||
|
||||
/* Here we have to free up our custom data storage. */
|
||||
if (plugin)
|
||||
delete static_cast<struct demo_custom_data*>(plugin->custom);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_init_connect([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_uninit_connect([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_pre_connect([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_post_connect([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_post_disconnect([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_x509_certificate([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_login_failure([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_end_paint([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata, [[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_redirect([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata, [[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_server_post_connect([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_server_peer_activate([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_server_channels_init([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_server_channels_free([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_server_session_end([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* custom)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
WLog_INFO(TAG, "called");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_filter_keyboard_event([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* param)
|
||||
{
|
||||
proxyPluginsManager* mgr = nullptr;
|
||||
auto event_data = static_cast<const proxyKeyboardEventInfo*>(param);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(event_data);
|
||||
|
||||
mgr = plugin->mgr;
|
||||
WINPR_ASSERT(mgr);
|
||||
|
||||
if (event_data == nullptr)
|
||||
return FALSE;
|
||||
|
||||
if (event_data->rdp_scan_code == RDP_SCANCODE_KEY_B)
|
||||
{
|
||||
/* user typed 'B', that means bye :) */
|
||||
std::cout << "C++ demo plugin: aborting connection" << std::endl;
|
||||
mgr->AbortConnect(mgr, pdata);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_filter_unicode_event([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* param)
|
||||
{
|
||||
proxyPluginsManager* mgr = nullptr;
|
||||
auto event_data = static_cast<const proxyUnicodeEventInfo*>(param);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(event_data);
|
||||
|
||||
mgr = plugin->mgr;
|
||||
WINPR_ASSERT(mgr);
|
||||
|
||||
if (event_data == nullptr)
|
||||
return FALSE;
|
||||
|
||||
if (event_data->code == 'b')
|
||||
{
|
||||
/* user typed 'B', that means bye :) */
|
||||
std::cout << "C++ demo plugin: aborting connection" << std::endl;
|
||||
mgr->AbortConnect(mgr, pdata);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_mouse_event([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata, [[maybe_unused]] void* param)
|
||||
{
|
||||
auto event_data = static_cast<const proxyMouseEventInfo*>(param);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(event_data);
|
||||
|
||||
WLog_INFO(TAG, "called %p", WINPR_CXX_COMPAT_CAST(const void*, event_data));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_mouse_ex_event([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata, [[maybe_unused]] void* param)
|
||||
{
|
||||
auto event_data = static_cast<const proxyMouseExEventInfo*>(param);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(event_data);
|
||||
|
||||
WLog_INFO(TAG, "called %p", WINPR_CXX_COMPAT_CAST(const void*, event_data));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_client_channel_data([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* param)
|
||||
{
|
||||
const auto* channel = static_cast<const proxyChannelDataEventInfo*>(param);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(channel);
|
||||
|
||||
WLog_INFO(TAG, "%s [0x%04" PRIx16 "] got %" PRIuz, channel->channel_name, channel->channel_id,
|
||||
channel->data_len);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_server_channel_data([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* param)
|
||||
{
|
||||
const auto* channel = static_cast<const proxyChannelDataEventInfo*>(param);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(channel);
|
||||
|
||||
WLog_WARN(TAG, "%s [0x%04" PRIx16 "] got %" PRIuz, channel->channel_name, channel->channel_id,
|
||||
channel->data_len);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_dynamic_channel_create([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* param)
|
||||
{
|
||||
const auto* channel = static_cast<const proxyChannelDataEventInfo*>(param);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(channel);
|
||||
|
||||
WLog_WARN(TAG, "%s [0x%04" PRIx16 "]", channel->channel_name, channel->channel_id);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_server_fetch_target_addr([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* param)
|
||||
{
|
||||
auto event_data = static_cast<const proxyFetchTargetEventInfo*>(param);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(event_data);
|
||||
|
||||
WLog_INFO(TAG, "called %p", WINPR_CXX_COMPAT_CAST(const void*, event_data));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_server_peer_logon([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata, [[maybe_unused]] void* param)
|
||||
{
|
||||
auto info = static_cast<const proxyServerPeerLogon*>(param);
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(info);
|
||||
WINPR_ASSERT(info->identity);
|
||||
|
||||
WLog_INFO(TAG, "%d", info->automatic);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_dyn_channel_intercept_list([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* arg)
|
||||
{
|
||||
auto data = static_cast<proxyChannelToInterceptData*>(arg);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
WLog_INFO(TAG, "%s: %p", __func__, WINPR_CXX_COMPAT_CAST(const void*, data));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_static_channel_intercept_list([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* arg)
|
||||
{
|
||||
auto data = static_cast<proxyChannelToInterceptData*>(arg);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
WLog_INFO(TAG, "%s: %p", __func__, WINPR_CXX_COMPAT_CAST(const void*, data));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL demo_dyn_channel_intercept([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
[[maybe_unused]] void* arg)
|
||||
{
|
||||
auto data = static_cast<proxyDynChannelInterceptData*>(arg);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
WLog_INFO(TAG, "%s: %p", __func__, WINPR_CXX_COMPAT_CAST(const void*, data));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static BOOL int_proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata)
|
||||
{
|
||||
struct demo_custom_data* custom = nullptr;
|
||||
proxyPlugin plugin = {};
|
||||
|
||||
plugin.name = plugin_name;
|
||||
plugin.description = plugin_desc;
|
||||
plugin.PluginUnload = demo_plugin_unload;
|
||||
plugin.ClientInitConnect = demo_client_init_connect;
|
||||
plugin.ClientUninitConnect = demo_client_uninit_connect;
|
||||
plugin.ClientPreConnect = demo_client_pre_connect;
|
||||
plugin.ClientPostConnect = demo_client_post_connect;
|
||||
plugin.ClientPostDisconnect = demo_client_post_disconnect;
|
||||
plugin.ClientX509Certificate = demo_client_x509_certificate;
|
||||
plugin.ClientLoginFailure = demo_client_login_failure;
|
||||
plugin.ClientEndPaint = demo_client_end_paint;
|
||||
plugin.ClientRedirect = demo_client_redirect;
|
||||
plugin.ServerPostConnect = demo_server_post_connect;
|
||||
plugin.ServerPeerActivate = demo_server_peer_activate;
|
||||
plugin.ServerChannelsInit = demo_server_channels_init;
|
||||
plugin.ServerChannelsFree = demo_server_channels_free;
|
||||
plugin.ServerSessionEnd = demo_server_session_end;
|
||||
plugin.KeyboardEvent = demo_filter_keyboard_event;
|
||||
plugin.UnicodeEvent = demo_filter_unicode_event;
|
||||
plugin.MouseEvent = demo_mouse_event;
|
||||
plugin.MouseExEvent = demo_mouse_ex_event;
|
||||
plugin.ClientChannelData = demo_client_channel_data;
|
||||
plugin.ServerChannelData = demo_server_channel_data;
|
||||
plugin.DynamicChannelCreate = demo_dynamic_channel_create;
|
||||
plugin.ServerFetchTargetAddr = demo_server_fetch_target_addr;
|
||||
plugin.ServerPeerLogon = demo_server_peer_logon;
|
||||
|
||||
plugin.StaticChannelToIntercept = demo_static_channel_intercept_list;
|
||||
plugin.DynChannelToIntercept = demo_dyn_channel_intercept_list;
|
||||
plugin.DynChannelIntercept = demo_dyn_channel_intercept;
|
||||
|
||||
plugin.userdata = userdata;
|
||||
|
||||
custom = new (struct demo_custom_data);
|
||||
if (!custom)
|
||||
return FALSE;
|
||||
|
||||
custom->mgr = plugins_manager;
|
||||
custom->somesetting = 42;
|
||||
|
||||
plugin.custom = custom;
|
||||
plugin.userdata = userdata;
|
||||
|
||||
return plugins_manager->RegisterPlugin(plugins_manager, &plugin);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#if defined(BUILD_SHARED_LIBS)
|
||||
[[nodiscard]]
|
||||
FREERDP_API BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata);
|
||||
|
||||
BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata)
|
||||
{
|
||||
return int_proxy_module_entry_point(plugins_manager, userdata);
|
||||
}
|
||||
#else
|
||||
[[nodiscard]]
|
||||
FREERDP_API BOOL demo_proxy_module_entry_point(proxyPluginsManager* plugins_manager,
|
||||
void* userdata);
|
||||
BOOL demo_proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata)
|
||||
{
|
||||
return int_proxy_module_entry_point(plugins_manager, userdata);
|
||||
}
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
51
third_party/FreeRDP/server/proxy/modules/dyn-channel-dump/CMakeLists.txt
vendored
Normal file
51
third_party/FreeRDP/server/proxy/modules/dyn-channel-dump/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
#
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP Proxy Server Demo C++ Module
|
||||
#
|
||||
# Copyright 2023 Armin Novak <anovak@thincast.com>
|
||||
# Copyright 2023 Thincast Technologies GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
if(POLICY CMP0091)
|
||||
cmake_policy(SET CMP0091 NEW)
|
||||
endif()
|
||||
if(NOT FREERDP_DEFAULT_PROJECT_VERSION)
|
||||
set(FREERDP_DEFAULT_PROJECT_VERSION "1.0.0.0")
|
||||
endif()
|
||||
|
||||
project(proxy-dyn-channel-dump-plugin VERSION ${FREERDP_DEFAULT_PROJECT_VERSION} LANGUAGES CXX)
|
||||
|
||||
message("project ${PROJECT_NAME} is using version ${PROJECT_VERSION}")
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../../../cmake/)
|
||||
include(CommonConfigOptions)
|
||||
include(CXXCompilerFlags)
|
||||
include(ProjectCXXStandard)
|
||||
|
||||
set(SRCS dyn-channel-dump.cpp)
|
||||
addtargetwithresourcefile(${PROJECT_NAME} FALSE "${PROJECT_VERSION}" SRCS FALSE)
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE $<$<BOOL:${BUILD_SHARED_LIBS}>:BUILD_SHARED_LIBS>)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE winpr freerdp freerdp-client freerdp-server freerdp-server-proxy)
|
||||
|
||||
installwithrpath(TARGETS ${PROJECT_NAME} DESTINATION ${FREERDP_PROXY_PLUGINDIR})
|
||||
|
||||
set(PROJECT_PC_REQUIRES_PRIVATE
|
||||
"winpr${FREERDP_API_VERSION} freerdp${FREERDP_API_VERSION} freerdp-server${FREERDP_API_VERSION} freerdp-client${FREERDP_API_VERSION} freerdp-server-proxy${FREERDP_API_VERSION}"
|
||||
)
|
||||
include(ProxyModuleConfig)
|
||||
generate_proxy_module_config()
|
||||
480
third_party/FreeRDP/server/proxy/modules/dyn-channel-dump/dyn-channel-dump.cpp
vendored
Normal file
480
third_party/FreeRDP/server/proxy/modules/dyn-channel-dump/dyn-channel-dump.cpp
vendored
Normal file
@@ -0,0 +1,480 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server persist-bitmap-filter Module
|
||||
*
|
||||
* this module is designed to deactivate all persistent bitmap cache settings a
|
||||
* client might send.
|
||||
*
|
||||
* Copyright 2023 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2023 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#if __has_include(<filesystem>)
|
||||
#include <filesystem>
|
||||
namespace fs = std::filesystem;
|
||||
#elif __has_include(<experimental/filesystem>)
|
||||
#include <experimental/filesystem>
|
||||
namespace fs = std::experimental::filesystem;
|
||||
#else
|
||||
#error Could not find system header "<filesystem>" or "<experimental/filesystem>"
|
||||
#endif
|
||||
|
||||
#include <freerdp/server/proxy/proxy_modules_api.h>
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
#include <freerdp/channels/drdynvc.h>
|
||||
#include <freerdp/channels/rdpgfx.h>
|
||||
#include <freerdp/utils/gfx.h>
|
||||
|
||||
#define TAG MODULE_TAG("dyn-channel-dump")
|
||||
|
||||
static constexpr char plugin_name[] = "dyn-channel-dump";
|
||||
static constexpr char plugin_desc[] =
|
||||
"This plugin dumps configurable dynamic channel data to a file.";
|
||||
|
||||
[[nodiscard]] static const std::vector<std::string>& plugin_static_intercept()
|
||||
{
|
||||
static std::vector<std::string> vec;
|
||||
if (vec.empty())
|
||||
vec.emplace_back(DRDYNVC_SVC_CHANNEL_NAME);
|
||||
return vec;
|
||||
}
|
||||
|
||||
static constexpr char key_path[] = "path";
|
||||
static constexpr char key_channels[] = "channels";
|
||||
|
||||
class PluginData
|
||||
{
|
||||
public:
|
||||
explicit PluginData(proxyPluginsManager* mgr) : _mgr(mgr)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] proxyPluginsManager* mgr() const
|
||||
{
|
||||
return _mgr;
|
||||
}
|
||||
|
||||
uint64_t session()
|
||||
{
|
||||
return _sessionid++;
|
||||
}
|
||||
|
||||
private:
|
||||
proxyPluginsManager* _mgr;
|
||||
uint64_t _sessionid{ 0 };
|
||||
};
|
||||
|
||||
class ChannelData
|
||||
{
|
||||
public:
|
||||
ChannelData(const std::string& base, std::vector<std::string> list, uint64_t sessionid)
|
||||
: _base(base), _channels_to_dump(std::move(list)), _session_id(sessionid)
|
||||
{
|
||||
char str[64] = {};
|
||||
(void)_snprintf(str, sizeof(str), "session-%016" PRIx64, _session_id);
|
||||
_base /= str;
|
||||
}
|
||||
|
||||
bool add(const std::string& name, WINPR_ATTR_UNUSED bool back)
|
||||
{
|
||||
std::scoped_lock guard(_mux);
|
||||
if (_map.find(name) == _map.end())
|
||||
{
|
||||
WLog_INFO(TAG, "adding '%s' to dump list", name.c_str());
|
||||
_map.insert({ name, 0 });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::ofstream stream(const std::string& name, bool back)
|
||||
{
|
||||
std::scoped_lock guard(_mux);
|
||||
auto& atom = _map[name];
|
||||
auto count = atom++;
|
||||
auto path = filepath(name, back, count);
|
||||
WLog_DBG(TAG, "[%s] writing file '%s'", name.c_str(), path.c_str());
|
||||
return std::ofstream(path);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool dump_enabled(const std::string& name) const
|
||||
{
|
||||
if (name.empty())
|
||||
{
|
||||
WLog_WARN(TAG, "empty dynamic channel name, skipping");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto enabled = std::find(_channels_to_dump.begin(), _channels_to_dump.end(), name) !=
|
||||
_channels_to_dump.end();
|
||||
WLog_DBG(TAG, "channel '%s' dumping %s", name.c_str(), enabled ? "enabled" : "disabled");
|
||||
return enabled;
|
||||
}
|
||||
|
||||
bool ensure_path_exists()
|
||||
{
|
||||
if (!fs::exists(_base))
|
||||
{
|
||||
if (!fs::create_directories(_base))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to create dump directory %s", _base.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!fs::is_directory(_base))
|
||||
{
|
||||
WLog_ERR(TAG, "dump path %s is not a directory", _base.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool create()
|
||||
{
|
||||
if (!ensure_path_exists())
|
||||
return false;
|
||||
|
||||
if (_channels_to_dump.empty())
|
||||
{
|
||||
WLog_ERR(TAG, "Empty configuration entry [%s/%s], can not continue", plugin_name,
|
||||
key_channels);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t session() const
|
||||
{
|
||||
return _session_id;
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] fs::path filepath(const std::string& channel, bool back, uint64_t count) const
|
||||
{
|
||||
auto name = idstr(channel, back);
|
||||
char cstr[32] = {};
|
||||
(void)_snprintf(cstr, sizeof(cstr), "%016" PRIx64 "-", count);
|
||||
auto path = _base / cstr;
|
||||
path += name;
|
||||
path += ".dump";
|
||||
return path;
|
||||
}
|
||||
|
||||
[[nodiscard]] static std::string idstr(const std::string& name, bool back)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << name << ".";
|
||||
if (back)
|
||||
ss << "back";
|
||||
else
|
||||
ss << "front";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
fs::path _base;
|
||||
std::vector<std::string> _channels_to_dump;
|
||||
|
||||
std::mutex _mux;
|
||||
std::map<std::string, uint64_t> _map;
|
||||
uint64_t _session_id;
|
||||
};
|
||||
|
||||
[[nodiscard]] static PluginData* dump_get_plugin_data(proxyPlugin* plugin)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
|
||||
auto plugindata = static_cast<PluginData*>(plugin->custom);
|
||||
WINPR_ASSERT(plugindata);
|
||||
return plugindata;
|
||||
}
|
||||
|
||||
[[nodiscard]] static ChannelData* dump_get_plugin_data(proxyPlugin* plugin, proxyData* pdata)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
auto plugindata = dump_get_plugin_data(plugin);
|
||||
WINPR_ASSERT(plugindata);
|
||||
|
||||
auto mgr = plugindata->mgr();
|
||||
WINPR_ASSERT(mgr);
|
||||
|
||||
WINPR_ASSERT(mgr->GetPluginData);
|
||||
return static_cast<ChannelData*>(mgr->GetPluginData(mgr, plugin_name, pdata));
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL dump_set_plugin_data(proxyPlugin* plugin, proxyData* pdata,
|
||||
ChannelData* data)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
auto plugindata = dump_get_plugin_data(plugin);
|
||||
WINPR_ASSERT(plugindata);
|
||||
|
||||
auto mgr = plugindata->mgr();
|
||||
WINPR_ASSERT(mgr);
|
||||
|
||||
auto cdata = dump_get_plugin_data(plugin, pdata);
|
||||
delete cdata;
|
||||
|
||||
WINPR_ASSERT(mgr->SetPluginData);
|
||||
return mgr->SetPluginData(mgr, plugin_name, pdata, data);
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool dump_channel_enabled(proxyPlugin* plugin, proxyData* pdata,
|
||||
const std::string& name)
|
||||
{
|
||||
auto config = dump_get_plugin_data(plugin, pdata);
|
||||
if (!config)
|
||||
{
|
||||
WLog_ERR(TAG, "Missing channel data");
|
||||
return false;
|
||||
}
|
||||
return config->dump_enabled(name);
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL dump_dyn_channel_intercept_list(proxyPlugin* plugin, proxyData* pdata,
|
||||
void* arg)
|
||||
{
|
||||
auto data = static_cast<proxyChannelToInterceptData*>(arg);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
data->intercept = dump_channel_enabled(plugin, pdata, data->name);
|
||||
if (data->intercept)
|
||||
{
|
||||
auto cdata = dump_get_plugin_data(plugin, pdata);
|
||||
if (!cdata)
|
||||
return FALSE;
|
||||
|
||||
if (!cdata->add(data->name, false))
|
||||
{
|
||||
WLog_ERR(TAG, "failed to create files for '%s'", data->name);
|
||||
}
|
||||
if (!cdata->add(data->name, true))
|
||||
{
|
||||
WLog_ERR(TAG, "failed to create files for '%s'", data->name);
|
||||
}
|
||||
WLog_INFO(TAG, "Dumping channel '%s'", data->name);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL dump_static_channel_intercept_list([[maybe_unused]] proxyPlugin* plugin,
|
||||
[[maybe_unused]] proxyData* pdata,
|
||||
void* arg)
|
||||
{
|
||||
auto data = static_cast<proxyChannelToInterceptData*>(arg);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
auto intercept = std::find(plugin_static_intercept().begin(), plugin_static_intercept().end(),
|
||||
data->name) != plugin_static_intercept().end();
|
||||
if (intercept)
|
||||
{
|
||||
WLog_INFO(TAG, "intercepting channel '%s'", data->name);
|
||||
data->intercept = TRUE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL dump_dyn_channel_intercept(proxyPlugin* plugin, proxyData* pdata,
|
||||
void* arg)
|
||||
{
|
||||
auto data = static_cast<proxyDynChannelInterceptData*>(arg);
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
data->result = PF_CHANNEL_RESULT_PASS;
|
||||
if (dump_channel_enabled(plugin, pdata, data->name))
|
||||
{
|
||||
WLog_DBG(TAG, "intercepting channel '%s'", data->name);
|
||||
auto cdata = dump_get_plugin_data(plugin, pdata);
|
||||
if (!cdata)
|
||||
{
|
||||
WLog_ERR(TAG, "Missing channel data");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!cdata->ensure_path_exists())
|
||||
return FALSE;
|
||||
|
||||
auto stream = cdata->stream(data->name, data->isBackData);
|
||||
auto buffer = reinterpret_cast<const char*>(Stream_ConstBuffer(data->data));
|
||||
if (!stream.is_open() || !stream.good())
|
||||
{
|
||||
WLog_ERR(TAG, "Could not write to stream");
|
||||
return FALSE;
|
||||
}
|
||||
const auto s = Stream_Length(data->data);
|
||||
if (s > std::numeric_limits<std::streamsize>::max())
|
||||
{
|
||||
WLog_ERR(TAG, "Stream length %" PRIuz " exceeds std::streamsize::max", s);
|
||||
return FALSE;
|
||||
}
|
||||
stream.write(buffer, static_cast<std::streamsize>(s));
|
||||
if (stream.fail())
|
||||
{
|
||||
WLog_ERR(TAG, "Could not write to stream");
|
||||
return FALSE;
|
||||
}
|
||||
stream.flush();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static std::vector<std::string> split(const std::string& input,
|
||||
const std::string& regex)
|
||||
{
|
||||
// passing -1 as the submatch index parameter performs splitting
|
||||
std::regex re(regex);
|
||||
std::sregex_token_iterator first{ input.begin(), input.end(), re, -1 };
|
||||
std::sregex_token_iterator last;
|
||||
return { first, last };
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL dump_session_started(proxyPlugin* plugin, proxyData* pdata,
|
||||
void* /*unused*/)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
auto custom = dump_get_plugin_data(plugin);
|
||||
WINPR_ASSERT(custom);
|
||||
|
||||
auto config = pdata->config;
|
||||
WINPR_ASSERT(config);
|
||||
|
||||
auto cpath = pf_config_get(config, plugin_name, key_path);
|
||||
if (!cpath)
|
||||
{
|
||||
WLog_ERR(TAG, "Missing configuration entry [%s/%s], can not continue", plugin_name,
|
||||
key_path);
|
||||
return FALSE;
|
||||
}
|
||||
auto cchannels = pf_config_get(config, plugin_name, key_channels);
|
||||
if (!cchannels)
|
||||
{
|
||||
WLog_ERR(TAG, "Missing configuration entry [%s/%s], can not continue", plugin_name,
|
||||
key_channels);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
std::string path(cpath);
|
||||
std::string channels(cchannels);
|
||||
std::vector<std::string> list = split(channels, "[;,]");
|
||||
auto cfg = new ChannelData(path, std::move(list), custom->session());
|
||||
if (!cfg || !cfg->create())
|
||||
{
|
||||
delete cfg;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!dump_set_plugin_data(plugin, pdata, cfg))
|
||||
return FALSE;
|
||||
|
||||
WLog_DBG(TAG, "starting session dump %" PRIu64, cfg->session());
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL dump_session_end(proxyPlugin* plugin, proxyData* pdata, void* /*unused*/)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
auto cfg = dump_get_plugin_data(plugin, pdata);
|
||||
if (cfg)
|
||||
WLog_DBG(TAG, "ending session dump %" PRIu64, cfg->session());
|
||||
return dump_set_plugin_data(plugin, pdata, nullptr);
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL dump_unload(proxyPlugin* plugin)
|
||||
{
|
||||
if (!plugin)
|
||||
return TRUE;
|
||||
delete static_cast<PluginData*>(plugin->custom);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL int_proxy_module_entry_point(proxyPluginsManager* plugins_manager,
|
||||
void* userdata)
|
||||
{
|
||||
proxyPlugin plugin = {};
|
||||
|
||||
plugin.name = plugin_name;
|
||||
plugin.description = plugin_desc;
|
||||
|
||||
plugin.PluginUnload = dump_unload;
|
||||
plugin.ServerSessionStarted = dump_session_started;
|
||||
plugin.ServerSessionEnd = dump_session_end;
|
||||
|
||||
plugin.StaticChannelToIntercept = dump_static_channel_intercept_list;
|
||||
plugin.DynChannelToIntercept = dump_dyn_channel_intercept_list;
|
||||
plugin.DynChannelIntercept = dump_dyn_channel_intercept;
|
||||
|
||||
plugin.custom = new PluginData(plugins_manager);
|
||||
if (!plugin.custom)
|
||||
return FALSE;
|
||||
plugin.userdata = userdata;
|
||||
|
||||
return plugins_manager->RegisterPlugin(plugins_manager, &plugin);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#if defined(BUILD_SHARED_LIBS)
|
||||
[[nodiscard]]
|
||||
FREERDP_API BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata);
|
||||
|
||||
BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata)
|
||||
{
|
||||
return int_proxy_module_entry_point(plugins_manager, userdata);
|
||||
}
|
||||
#else
|
||||
[[nodiscard]]
|
||||
FREERDP_API BOOL dyn_channel_dump_proxy_module_entry_point(proxyPluginsManager* plugins_manager,
|
||||
void* userdata);
|
||||
BOOL dyn_channel_dump_proxy_module_entry_point(proxyPluginsManager* plugins_manager, void* userdata)
|
||||
{
|
||||
return int_proxy_module_entry_point(plugins_manager, userdata);
|
||||
}
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
16
third_party/FreeRDP/server/proxy/modules/freerdp-proxy-module.pc.in
vendored
Normal file
16
third_party/FreeRDP/server/proxy/modules/freerdp-proxy-module.pc.in
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
prefix=@PKG_CONFIG_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||
includedir=${prefix}/@WINPR_INCLUDE_DIR@
|
||||
plugindir=${libdir}/@FREERDP_MAJOR_DIR@
|
||||
proxy_plugindir=${plugindir}/proxy
|
||||
|
||||
Name: @PROJECT_NAME@
|
||||
Description: FreeRDP proxy module
|
||||
URL: http://www.freerdp.com/
|
||||
Version: @PROJECT_VERSION@
|
||||
Requires: @PROJECT_PC_REQUIRES@
|
||||
Requires.private: @PROJECT_PC_REQUIRES_PRIVATE@
|
||||
Libs: -L${libdir}
|
||||
Libs.private: -Wl,--whole-archive \${proxy_plugindir}/@PROJECT_LIBRARY_NAME@ -u @PROJECT_SHORT_NAME_UNDERSCORE@_proxy_module_entry_point -Wl,--no-whole-archive
|
||||
Cflags: -I${includedir}
|
||||
362
third_party/FreeRDP/server/proxy/pf_channel.c
vendored
Normal file
362
third_party/FreeRDP/server/proxy/pf_channel.c
vendored
Normal file
@@ -0,0 +1,362 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
*
|
||||
* Copyright 2022 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/cast.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/server/proxy/proxy_log.h>
|
||||
|
||||
#include "proxy_modules.h"
|
||||
#include "pf_channel.h"
|
||||
|
||||
#define TAG PROXY_TAG("channel")
|
||||
|
||||
/** @brief a tracker for channel packets */
|
||||
struct sChannelStateTracker
|
||||
{
|
||||
pServerStaticChannelContext* channel;
|
||||
ChannelTrackerMode mode;
|
||||
wStream* currentPacket;
|
||||
size_t currentPacketReceived;
|
||||
size_t currentPacketSize;
|
||||
size_t currentPacketFragments;
|
||||
|
||||
ChannelTrackerPeekFn peekFn;
|
||||
void* trackerData;
|
||||
proxyData* pdata;
|
||||
};
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL channelTracker_resetCurrentPacket(ChannelStateTracker* tracker)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
|
||||
BOOL create = TRUE;
|
||||
if (tracker->currentPacket)
|
||||
{
|
||||
const size_t cap = Stream_Capacity(tracker->currentPacket);
|
||||
if (cap < 1ULL * 1000ULL * 1000ULL)
|
||||
create = FALSE;
|
||||
else
|
||||
Stream_Free(tracker->currentPacket, TRUE);
|
||||
}
|
||||
|
||||
if (create)
|
||||
tracker->currentPacket = Stream_New(nullptr, 10ULL * 1024ULL);
|
||||
if (!tracker->currentPacket)
|
||||
return FALSE;
|
||||
Stream_ResetPosition(tracker->currentPacket);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ChannelStateTracker* channelTracker_new(pServerStaticChannelContext* channel,
|
||||
ChannelTrackerPeekFn fn, void* data)
|
||||
{
|
||||
ChannelStateTracker* ret = calloc(1, sizeof(ChannelStateTracker));
|
||||
if (!ret)
|
||||
return ret;
|
||||
|
||||
WINPR_ASSERT(fn);
|
||||
|
||||
ret->channel = channel;
|
||||
ret->peekFn = fn;
|
||||
|
||||
if (!channelTracker_setCustomData(ret, data))
|
||||
goto fail;
|
||||
|
||||
if (!channelTracker_resetCurrentPacket(ret))
|
||||
goto fail;
|
||||
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
WINPR_PRAGMA_DIAG_PUSH
|
||||
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
|
||||
channelTracker_free(ret);
|
||||
WINPR_PRAGMA_DIAG_POP
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PfChannelResult channelTracker_update(ChannelStateTracker* tracker, const BYTE* xdata, size_t xsize,
|
||||
UINT32 flags, size_t totalSize)
|
||||
{
|
||||
PfChannelResult result = PF_CHANNEL_RESULT_ERROR;
|
||||
BOOL firstPacket = (flags & CHANNEL_FLAG_FIRST) != 0;
|
||||
BOOL lastPacket = (flags & CHANNEL_FLAG_LAST) != 0;
|
||||
|
||||
WINPR_ASSERT(tracker);
|
||||
|
||||
WLog_VRB(TAG, "channelTracker_update(%s): sz=%" PRIuz " first=%d last=%d",
|
||||
tracker->channel->channel_name, xsize, firstPacket, lastPacket);
|
||||
if (flags & CHANNEL_FLAG_FIRST)
|
||||
{
|
||||
if (!channelTracker_resetCurrentPacket(tracker))
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
channelTracker_setCurrentPacketSize(tracker, totalSize);
|
||||
tracker->currentPacketReceived = 0;
|
||||
tracker->currentPacketFragments = 0;
|
||||
}
|
||||
|
||||
{
|
||||
const size_t currentPacketSize = channelTracker_getCurrentPacketSize(tracker);
|
||||
if (tracker->currentPacketReceived + xsize > currentPacketSize)
|
||||
WLog_INFO(TAG, "cumulated size is bigger (%" PRIuz ") than total size (%" PRIuz ")",
|
||||
tracker->currentPacketReceived + xsize, currentPacketSize);
|
||||
}
|
||||
|
||||
tracker->currentPacketReceived += xsize;
|
||||
tracker->currentPacketFragments++;
|
||||
|
||||
switch (channelTracker_getMode(tracker))
|
||||
{
|
||||
case CHANNEL_TRACKER_PEEK:
|
||||
{
|
||||
wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
|
||||
if (!Stream_EnsureRemainingCapacity(currentPacket, xsize))
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
|
||||
Stream_Write(currentPacket, xdata, xsize);
|
||||
|
||||
WINPR_ASSERT(tracker->peekFn);
|
||||
result = tracker->peekFn(tracker, firstPacket, lastPacket);
|
||||
}
|
||||
break;
|
||||
case CHANNEL_TRACKER_PASS:
|
||||
result = PF_CHANNEL_RESULT_PASS;
|
||||
break;
|
||||
case CHANNEL_TRACKER_DROP:
|
||||
result = PF_CHANNEL_RESULT_DROP;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (lastPacket)
|
||||
{
|
||||
const size_t currentPacketSize = channelTracker_getCurrentPacketSize(tracker);
|
||||
channelTracker_setMode(tracker, CHANNEL_TRACKER_PEEK);
|
||||
|
||||
if (tracker->currentPacketReceived != currentPacketSize)
|
||||
WLog_INFO(TAG, "cumulated size(%" PRIuz ") does not match total size (%" PRIuz ")",
|
||||
tracker->currentPacketReceived, currentPacketSize);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void channelTracker_free(ChannelStateTracker* t)
|
||||
{
|
||||
if (!t)
|
||||
return;
|
||||
|
||||
Stream_Free(t->currentPacket, TRUE);
|
||||
free(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the current accumulated tracker content, if it's the first packet, then
|
||||
* when can just return that the packet shall be passed, otherwise to have to refragment
|
||||
* the accumulated current packet.
|
||||
*/
|
||||
|
||||
PfChannelResult channelTracker_flushCurrent(ChannelStateTracker* t, BOOL first, BOOL last,
|
||||
BOOL toBack)
|
||||
{
|
||||
proxyData* pdata = nullptr;
|
||||
pServerContext* ps = nullptr;
|
||||
pServerStaticChannelContext* channel = nullptr;
|
||||
UINT32 flags = CHANNEL_FLAG_FIRST;
|
||||
BOOL r = 0;
|
||||
const char* direction = toBack ? "F->B" : "B->F";
|
||||
const size_t currentPacketSize = channelTracker_getCurrentPacketSize(t);
|
||||
wStream* currentPacket = channelTracker_getCurrentPacket(t);
|
||||
|
||||
WINPR_ASSERT(t);
|
||||
|
||||
WLog_VRB(TAG, "channelTracker_flushCurrent(%s): %s sz=%" PRIuz " first=%d last=%d",
|
||||
t->channel->channel_name, direction, Stream_GetPosition(currentPacket), first, last);
|
||||
|
||||
if (first)
|
||||
return PF_CHANNEL_RESULT_PASS;
|
||||
|
||||
pdata = t->pdata;
|
||||
channel = t->channel;
|
||||
if (last)
|
||||
flags |= CHANNEL_FLAG_LAST;
|
||||
|
||||
if (toBack)
|
||||
{
|
||||
proxyChannelDataEventInfo ev = WINPR_C_ARRAY_INIT;
|
||||
|
||||
ev.channel_id = WINPR_ASSERTING_INT_CAST(UINT16, channel->front_channel_id);
|
||||
ev.channel_name = channel->channel_name;
|
||||
ev.data = Stream_Buffer(currentPacket);
|
||||
ev.data_len = Stream_GetPosition(currentPacket);
|
||||
ev.flags = flags;
|
||||
ev.total_size = currentPacketSize;
|
||||
|
||||
if (!pdata->pc->sendChannelData)
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
|
||||
return pdata->pc->sendChannelData(pdata->pc, &ev) ? PF_CHANNEL_RESULT_DROP
|
||||
: PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
|
||||
ps = pdata->ps;
|
||||
r = ps->context.peer->SendChannelPacket(
|
||||
ps->context.peer, WINPR_ASSERTING_INT_CAST(UINT16, channel->front_channel_id),
|
||||
currentPacketSize, flags, Stream_Buffer(currentPacket), Stream_GetPosition(currentPacket));
|
||||
|
||||
return r ? PF_CHANNEL_RESULT_DROP : PF_CHANNEL_RESULT_ERROR;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult pf_channel_generic_back_data(proxyData* pdata,
|
||||
const pServerStaticChannelContext* channel,
|
||||
const BYTE* xdata, size_t xsize, UINT32 flags,
|
||||
size_t totalSize)
|
||||
{
|
||||
proxyChannelDataEventInfo ev = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(channel);
|
||||
|
||||
switch (channel->channelMode)
|
||||
{
|
||||
case PF_UTILS_CHANNEL_PASSTHROUGH:
|
||||
ev.channel_id = WINPR_ASSERTING_INT_CAST(UINT16, channel->back_channel_id);
|
||||
ev.channel_name = channel->channel_name;
|
||||
ev.data = xdata;
|
||||
ev.data_len = xsize;
|
||||
ev.flags = flags;
|
||||
ev.total_size = totalSize;
|
||||
|
||||
if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA,
|
||||
pdata, &ev))
|
||||
return PF_CHANNEL_RESULT_DROP; /* Silently drop */
|
||||
|
||||
return PF_CHANNEL_RESULT_PASS;
|
||||
|
||||
case PF_UTILS_CHANNEL_INTERCEPT:
|
||||
/* TODO */
|
||||
case PF_UTILS_CHANNEL_BLOCK:
|
||||
default:
|
||||
return PF_CHANNEL_RESULT_DROP;
|
||||
}
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static PfChannelResult pf_channel_generic_front_data(proxyData* pdata,
|
||||
const pServerStaticChannelContext* channel,
|
||||
const BYTE* xdata, size_t xsize, UINT32 flags,
|
||||
size_t totalSize)
|
||||
{
|
||||
proxyChannelDataEventInfo ev = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(channel);
|
||||
|
||||
switch (channel->channelMode)
|
||||
{
|
||||
case PF_UTILS_CHANNEL_PASSTHROUGH:
|
||||
ev.channel_id = WINPR_ASSERTING_INT_CAST(UINT16, channel->front_channel_id);
|
||||
ev.channel_name = channel->channel_name;
|
||||
ev.data = xdata;
|
||||
ev.data_len = xsize;
|
||||
ev.flags = flags;
|
||||
ev.total_size = totalSize;
|
||||
|
||||
if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA,
|
||||
pdata, &ev))
|
||||
return PF_CHANNEL_RESULT_DROP; /* Silently drop */
|
||||
|
||||
return PF_CHANNEL_RESULT_PASS;
|
||||
|
||||
case PF_UTILS_CHANNEL_INTERCEPT:
|
||||
/* TODO */
|
||||
case PF_UTILS_CHANNEL_BLOCK:
|
||||
default:
|
||||
return PF_CHANNEL_RESULT_DROP;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL pf_channel_setup_generic(pServerStaticChannelContext* channel)
|
||||
{
|
||||
WINPR_ASSERT(channel);
|
||||
channel->onBackData = pf_channel_generic_back_data;
|
||||
channel->onFrontData = pf_channel_generic_front_data;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL channelTracker_setMode(ChannelStateTracker* tracker, ChannelTrackerMode mode)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
tracker->mode = mode;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ChannelTrackerMode channelTracker_getMode(ChannelStateTracker* tracker)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
return tracker->mode;
|
||||
}
|
||||
|
||||
BOOL channelTracker_setPData(ChannelStateTracker* tracker, proxyData* pdata)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
tracker->pdata = pdata;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
proxyData* channelTracker_getPData(ChannelStateTracker* tracker)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
return tracker->pdata;
|
||||
}
|
||||
|
||||
wStream* channelTracker_getCurrentPacket(ChannelStateTracker* tracker)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
return tracker->currentPacket;
|
||||
}
|
||||
|
||||
BOOL channelTracker_setCustomData(ChannelStateTracker* tracker, void* data)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
tracker->trackerData = data;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void* channelTracker_getCustomData(ChannelStateTracker* tracker)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
return tracker->trackerData;
|
||||
}
|
||||
|
||||
size_t channelTracker_getCurrentPacketSize(ChannelStateTracker* tracker)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
return tracker->currentPacketSize;
|
||||
}
|
||||
|
||||
BOOL channelTracker_setCurrentPacketSize(ChannelStateTracker* tracker, size_t size)
|
||||
{
|
||||
WINPR_ASSERT(tracker);
|
||||
tracker->currentPacketSize = size;
|
||||
return TRUE;
|
||||
}
|
||||
66
third_party/FreeRDP/server/proxy/pf_channel.h
vendored
Normal file
66
third_party/FreeRDP/server/proxy/pf_channel.h
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
*
|
||||
*
|
||||
* Copyright 2022 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef SERVER_PROXY_PF_CHANNEL_H_
|
||||
#define SERVER_PROXY_PF_CHANNEL_H_
|
||||
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
/** @brief operating mode of a channel tracker */
|
||||
typedef enum
|
||||
{
|
||||
CHANNEL_TRACKER_PEEK, /*!< inquiring content, accumulating packet fragments */
|
||||
CHANNEL_TRACKER_PASS, /*!< pass all the fragments of the current packet */
|
||||
CHANNEL_TRACKER_DROP /*!< drop all the fragments of the current packet */
|
||||
} ChannelTrackerMode;
|
||||
|
||||
typedef struct sChannelStateTracker ChannelStateTracker;
|
||||
typedef PfChannelResult (*ChannelTrackerPeekFn)(ChannelStateTracker* tracker, BOOL first,
|
||||
BOOL lastPacket);
|
||||
|
||||
void channelTracker_free(ChannelStateTracker* t);
|
||||
|
||||
WINPR_ATTR_MALLOC(channelTracker_free, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
ChannelStateTracker* channelTracker_new(pServerStaticChannelContext* channel,
|
||||
ChannelTrackerPeekFn fn, void* data);
|
||||
|
||||
BOOL channelTracker_setMode(ChannelStateTracker* tracker, ChannelTrackerMode mode);
|
||||
WINPR_ATTR_NODISCARD ChannelTrackerMode channelTracker_getMode(ChannelStateTracker* tracker);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL channelTracker_setPData(ChannelStateTracker* tracker, proxyData* pdata);
|
||||
WINPR_ATTR_NODISCARD proxyData* channelTracker_getPData(ChannelStateTracker* tracker);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL channelTracker_setCustomData(ChannelStateTracker* tracker, void* data);
|
||||
WINPR_ATTR_NODISCARD void* channelTracker_getCustomData(ChannelStateTracker* tracker);
|
||||
|
||||
WINPR_ATTR_NODISCARD wStream* channelTracker_getCurrentPacket(ChannelStateTracker* tracker);
|
||||
|
||||
WINPR_ATTR_NODISCARD size_t channelTracker_getCurrentPacketSize(ChannelStateTracker* tracker);
|
||||
BOOL channelTracker_setCurrentPacketSize(ChannelStateTracker* tracker, size_t size);
|
||||
|
||||
WINPR_ATTR_NODISCARD PfChannelResult channelTracker_update(ChannelStateTracker* tracker,
|
||||
const BYTE* xdata, size_t xsize,
|
||||
UINT32 flags, size_t totalSize);
|
||||
|
||||
WINPR_ATTR_NODISCARD PfChannelResult channelTracker_flushCurrent(ChannelStateTracker* t, BOOL first,
|
||||
BOOL last, BOOL toBack);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_channel_setup_generic(pServerStaticChannelContext* channel);
|
||||
|
||||
#endif /* SERVER_PROXY_PF_CHANNEL_H_ */
|
||||
1091
third_party/FreeRDP/server/proxy/pf_client.c
vendored
Normal file
1091
third_party/FreeRDP/server/proxy/pf_client.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
31
third_party/FreeRDP/server/proxy/pf_client.h
vendored
Normal file
31
third_party/FreeRDP/server/proxy/pf_client.h
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server
|
||||
*
|
||||
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
|
||||
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_SERVER_PROXY_PFCLIENT_H
|
||||
#define FREERDP_SERVER_PROXY_PFCLIENT_H
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <winpr/wtypes.h>
|
||||
|
||||
int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints);
|
||||
WINPR_ATTR_NODISCARD DWORD WINAPI pf_client_start(LPVOID arg);
|
||||
|
||||
#endif /* FREERDP_SERVER_PROXY_PFCLIENT_H */
|
||||
1363
third_party/FreeRDP/server/proxy/pf_config.c
vendored
Normal file
1363
third_party/FreeRDP/server/proxy/pf_config.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
427
third_party/FreeRDP/server/proxy/pf_context.c
vendored
Normal file
427
third_party/FreeRDP/server/proxy/pf_context.c
vendored
Normal file
@@ -0,0 +1,427 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server
|
||||
*
|
||||
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
|
||||
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
|
||||
* Copyright 2021 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2021 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/crypto.h>
|
||||
#include <winpr/print.h>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_log.h>
|
||||
#include <freerdp/server/proxy/proxy_server.h>
|
||||
#include <freerdp/channels/drdynvc.h>
|
||||
|
||||
#include "pf_client.h"
|
||||
#include "pf_utils.h"
|
||||
#include "proxy_modules.h"
|
||||
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
#include "channels/pf_channel_rdpdr.h"
|
||||
|
||||
#define TAG PROXY_TAG("server")
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static UINT32 ChannelId_Hash(const void* key)
|
||||
{
|
||||
const UINT32* v = (const UINT32*)key;
|
||||
return *v;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL ChannelId_Compare(const void* pv1, const void* pv2)
|
||||
{
|
||||
const UINT32* v1 = pv1;
|
||||
const UINT32* v2 = pv2;
|
||||
WINPR_ASSERT(v1);
|
||||
WINPR_ASSERT(v2);
|
||||
return (*v1 == *v2);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL dyn_intercept(pServerContext* ps, const char* name)
|
||||
{
|
||||
if (strncmp(DRDYNVC_SVC_CHANNEL_NAME, name, sizeof(DRDYNVC_SVC_CHANNEL_NAME)) != 0)
|
||||
return FALSE;
|
||||
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->pdata);
|
||||
|
||||
const proxyConfig* cfg = ps->pdata->config;
|
||||
WINPR_ASSERT(cfg);
|
||||
if (!cfg->GFX)
|
||||
return TRUE;
|
||||
if (!cfg->AudioOutput)
|
||||
return TRUE;
|
||||
if (!cfg->AudioInput)
|
||||
return TRUE;
|
||||
if (!cfg->Multitouch)
|
||||
return TRUE;
|
||||
if (!cfg->VideoRedirection)
|
||||
return TRUE;
|
||||
if (!cfg->CameraRedirection)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pServerStaticChannelContext* StaticChannelContext_new(pServerContext* ps, const char* name,
|
||||
UINT32 id)
|
||||
{
|
||||
pServerStaticChannelContext* ret = calloc(1, sizeof(*ret));
|
||||
if (!ret)
|
||||
{
|
||||
PROXY_LOG_ERR(TAG, ps, "error allocating channel context for '%s'", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ret->front_channel_id = id;
|
||||
ret->channel_name = _strdup(name);
|
||||
if (!ret->channel_name)
|
||||
{
|
||||
PROXY_LOG_ERR(TAG, ps, "error allocating name in channel context for '%s'", name);
|
||||
free(ret);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
proxyChannelToInterceptData channel = { .name = name, .channelId = id, .intercept = FALSE };
|
||||
|
||||
if (pf_modules_run_filter(ps->pdata->module, FILTER_TYPE_STATIC_INTERCEPT_LIST, ps->pdata,
|
||||
&channel) &&
|
||||
channel.intercept)
|
||||
ret->channelMode = PF_UTILS_CHANNEL_INTERCEPT;
|
||||
else if (dyn_intercept(ps, name))
|
||||
ret->channelMode = PF_UTILS_CHANNEL_INTERCEPT;
|
||||
else
|
||||
ret->channelMode = pf_utils_get_channel_mode(ps->pdata->config, name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void StaticChannelContext_free(pServerStaticChannelContext* ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
IFCALL(ctx->contextDtor, ctx->context);
|
||||
|
||||
free(ctx->channel_name);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
static void HashStaticChannelContext_free(void* ptr)
|
||||
{
|
||||
pServerStaticChannelContext* ctx = (pServerStaticChannelContext*)ptr;
|
||||
StaticChannelContext_free(ctx);
|
||||
}
|
||||
|
||||
/* Proxy context initialization callback */
|
||||
static void client_to_proxy_context_free(freerdp_peer* client, rdpContext* ctx);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL client_to_proxy_context_new(freerdp_peer* client, rdpContext* ctx)
|
||||
{
|
||||
wObject* obj = nullptr;
|
||||
pServerContext* context = (pServerContext*)ctx;
|
||||
|
||||
WINPR_ASSERT(client);
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
context->dynvcReady = nullptr;
|
||||
|
||||
context->vcm = WTSOpenServerA((LPSTR)client->context);
|
||||
|
||||
if (!context->vcm || context->vcm == INVALID_HANDLE_VALUE)
|
||||
goto error;
|
||||
|
||||
if (!(context->dynvcReady = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
goto error;
|
||||
|
||||
context->interceptContextMap = HashTable_New(FALSE);
|
||||
if (!context->interceptContextMap)
|
||||
goto error;
|
||||
if (!HashTable_SetupForStringData(context->interceptContextMap, FALSE))
|
||||
goto error;
|
||||
obj = HashTable_ValueObject(context->interceptContextMap);
|
||||
WINPR_ASSERT(obj);
|
||||
obj->fnObjectFree = intercept_context_entry_free;
|
||||
|
||||
/* channels by ids */
|
||||
context->channelsByFrontId = HashTable_New(FALSE);
|
||||
if (!context->channelsByFrontId)
|
||||
goto error;
|
||||
if (!HashTable_SetHashFunction(context->channelsByFrontId, ChannelId_Hash))
|
||||
goto error;
|
||||
|
||||
obj = HashTable_KeyObject(context->channelsByFrontId);
|
||||
obj->fnObjectEquals = ChannelId_Compare;
|
||||
|
||||
obj = HashTable_ValueObject(context->channelsByFrontId);
|
||||
obj->fnObjectFree = HashStaticChannelContext_free;
|
||||
|
||||
context->channelsByBackId = HashTable_New(FALSE);
|
||||
if (!context->channelsByBackId)
|
||||
goto error;
|
||||
if (!HashTable_SetHashFunction(context->channelsByBackId, ChannelId_Hash))
|
||||
goto error;
|
||||
|
||||
obj = HashTable_KeyObject(context->channelsByBackId);
|
||||
obj->fnObjectEquals = ChannelId_Compare;
|
||||
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
client_to_proxy_context_free(client, ctx);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Proxy context free callback */
|
||||
void client_to_proxy_context_free(freerdp_peer* client, rdpContext* ctx)
|
||||
{
|
||||
pServerContext* context = (pServerContext*)ctx;
|
||||
|
||||
WINPR_UNUSED(client);
|
||||
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
if (context->dynvcReady)
|
||||
{
|
||||
(void)CloseHandle(context->dynvcReady);
|
||||
context->dynvcReady = nullptr;
|
||||
}
|
||||
|
||||
HashTable_Free(context->interceptContextMap);
|
||||
HashTable_Free(context->channelsByFrontId);
|
||||
HashTable_Free(context->channelsByBackId);
|
||||
|
||||
if (context->vcm && (context->vcm != INVALID_HANDLE_VALUE))
|
||||
WTSCloseServer(context->vcm);
|
||||
context->vcm = nullptr;
|
||||
}
|
||||
|
||||
BOOL pf_context_init_server_context(freerdp_peer* client)
|
||||
{
|
||||
WINPR_ASSERT(client);
|
||||
|
||||
client->ContextSize = sizeof(pServerContext);
|
||||
client->ContextNew = client_to_proxy_context_new;
|
||||
client->ContextFree = client_to_proxy_context_free;
|
||||
|
||||
return freerdp_peer_context_new(client);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_context_revert_str_settings(rdpSettings* dst, const rdpSettings* before, size_t nr,
|
||||
const FreeRDP_Settings_Keys_String* ids)
|
||||
{
|
||||
WINPR_ASSERT(dst);
|
||||
WINPR_ASSERT(before);
|
||||
WINPR_ASSERT(ids || (nr == 0));
|
||||
|
||||
for (size_t x = 0; x < nr; x++)
|
||||
{
|
||||
FreeRDP_Settings_Keys_String id = ids[x];
|
||||
const char* what = freerdp_settings_get_string(before, id);
|
||||
if (!freerdp_settings_set_string(dst, id, what))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void intercept_context_entry_free(void* obj)
|
||||
{
|
||||
InterceptContextMapEntry* entry = obj;
|
||||
if (!entry)
|
||||
return;
|
||||
if (!entry->free)
|
||||
return;
|
||||
entry->free(entry);
|
||||
}
|
||||
|
||||
BOOL pf_context_copy_settings(rdpSettings* dst, const rdpSettings* src)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
rdpSettings* before_copy = nullptr;
|
||||
const FreeRDP_Settings_Keys_String to_revert[] = { FreeRDP_ConfigPath,
|
||||
FreeRDP_CertificateName };
|
||||
|
||||
if (!dst || !src)
|
||||
return FALSE;
|
||||
|
||||
before_copy = freerdp_settings_clone(dst);
|
||||
if (!before_copy)
|
||||
return FALSE;
|
||||
|
||||
if (!freerdp_settings_copy(dst, src))
|
||||
goto out_fail;
|
||||
|
||||
/* keep original ServerMode value */
|
||||
if (!freerdp_settings_copy_item(dst, before_copy, FreeRDP_ServerMode))
|
||||
goto out_fail;
|
||||
|
||||
/* revert some values that must not be changed */
|
||||
if (!pf_context_revert_str_settings(dst, before_copy, ARRAYSIZE(to_revert), to_revert))
|
||||
goto out_fail;
|
||||
|
||||
if (!freerdp_settings_get_bool(dst, FreeRDP_ServerMode))
|
||||
{
|
||||
/* adjust instance pointer */
|
||||
if (!freerdp_settings_copy_item(dst, before_copy, FreeRDP_instance))
|
||||
goto out_fail;
|
||||
|
||||
/*
|
||||
* RdpServerRsaKey must be set to nullptr if `dst` is client's context
|
||||
* it must be freed before setting it to nullptr to avoid a memory leak!
|
||||
*/
|
||||
|
||||
if (!freerdp_settings_set_pointer_len(dst, FreeRDP_RdpServerRsaKey, nullptr, 1))
|
||||
goto out_fail;
|
||||
}
|
||||
|
||||
/* We handle certificate management for this client ourselves. */
|
||||
rc = freerdp_settings_set_bool(dst, FreeRDP_ExternalCertificateManagement, TRUE);
|
||||
|
||||
out_fail:
|
||||
freerdp_settings_free(before_copy);
|
||||
return rc;
|
||||
}
|
||||
|
||||
pClientContext* pf_context_create_client_context(const rdpSettings* clientSettings)
|
||||
{
|
||||
RDP_CLIENT_ENTRY_POINTS clientEntryPoints = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(clientSettings);
|
||||
|
||||
RdpClientEntry(&clientEntryPoints);
|
||||
rdpContext* context = freerdp_client_context_new(&clientEntryPoints);
|
||||
|
||||
if (!context)
|
||||
return nullptr;
|
||||
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
|
||||
if (!pf_context_copy_settings(context->settings, clientSettings))
|
||||
goto error;
|
||||
|
||||
return pc;
|
||||
error:
|
||||
freerdp_client_context_free(context);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
proxyData* proxy_data_new(void)
|
||||
{
|
||||
BYTE temp[16];
|
||||
char* hex = nullptr;
|
||||
proxyData* pdata = nullptr;
|
||||
|
||||
pdata = calloc(1, sizeof(proxyData));
|
||||
if (!pdata)
|
||||
return nullptr;
|
||||
|
||||
if (!(pdata->abort_event = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
goto error;
|
||||
|
||||
if (!(pdata->gfx_server_ready = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
goto error;
|
||||
|
||||
if (winpr_RAND(&temp, 16) < 0)
|
||||
goto error;
|
||||
hex = winpr_BinToHexString(temp, 16, FALSE);
|
||||
if (!hex)
|
||||
goto error;
|
||||
|
||||
CopyMemory(pdata->session_id, hex, PROXY_SESSION_ID_LENGTH);
|
||||
pdata->session_id[PROXY_SESSION_ID_LENGTH] = '\0';
|
||||
free(hex);
|
||||
|
||||
if (!(pdata->modules_info = HashTable_New(FALSE)))
|
||||
goto error;
|
||||
|
||||
/* modules_info maps between plugin name to custom data */
|
||||
if (!HashTable_SetupForStringData(pdata->modules_info, FALSE))
|
||||
goto error;
|
||||
|
||||
return pdata;
|
||||
error:
|
||||
WINPR_PRAGMA_DIAG_PUSH
|
||||
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
|
||||
proxy_data_free(pdata);
|
||||
WINPR_PRAGMA_DIAG_POP
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* updates circular pointers between proxyData and pClientContext instances */
|
||||
void proxy_data_set_client_context(proxyData* pdata, pClientContext* context)
|
||||
{
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(context);
|
||||
pdata->pc = context;
|
||||
context->pdata = pdata;
|
||||
}
|
||||
|
||||
/* updates circular pointers between proxyData and pServerContext instances */
|
||||
void proxy_data_set_server_context(proxyData* pdata, pServerContext* context)
|
||||
{
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(context);
|
||||
pdata->ps = context;
|
||||
context->pdata = pdata;
|
||||
}
|
||||
|
||||
void proxy_data_free(proxyData* pdata)
|
||||
{
|
||||
if (!pdata)
|
||||
return;
|
||||
|
||||
if (pdata->abort_event)
|
||||
(void)CloseHandle(pdata->abort_event);
|
||||
|
||||
if (pdata->client_thread)
|
||||
(void)CloseHandle(pdata->client_thread);
|
||||
|
||||
if (pdata->gfx_server_ready)
|
||||
(void)CloseHandle(pdata->gfx_server_ready);
|
||||
|
||||
if (pdata->modules_info)
|
||||
HashTable_Free(pdata->modules_info);
|
||||
|
||||
if (pdata->pc)
|
||||
freerdp_client_context_free(&pdata->pc->context);
|
||||
|
||||
free(pdata);
|
||||
}
|
||||
|
||||
void proxy_data_abort_connect(proxyData* pdata)
|
||||
{
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(pdata->abort_event);
|
||||
(void)SetEvent(pdata->abort_event);
|
||||
if (pdata->pc)
|
||||
freerdp_abort_connect_context(&pdata->pc->context);
|
||||
}
|
||||
|
||||
BOOL proxy_data_shall_disconnect(proxyData* pdata)
|
||||
{
|
||||
WINPR_ASSERT(pdata);
|
||||
WINPR_ASSERT(pdata->abort_event);
|
||||
return WaitForSingleObject(pdata->abort_event, 0) == WAIT_OBJECT_0;
|
||||
}
|
||||
211
third_party/FreeRDP/server/proxy/pf_input.c
vendored
Normal file
211
third_party/FreeRDP/server/proxy/pf_input.c
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server
|
||||
*
|
||||
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
|
||||
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
|
||||
* Copyright 2021 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2021 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include "pf_input.h"
|
||||
#include <freerdp/server/proxy/proxy_config.h>
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
#include "proxy_modules.h"
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_server_check_and_sync_input_state(pClientContext* pc)
|
||||
{
|
||||
WINPR_ASSERT(pc);
|
||||
|
||||
if (!freerdp_is_active_state(&pc->context))
|
||||
return FALSE;
|
||||
if (pc->input_state_sync_pending)
|
||||
{
|
||||
BOOL rc = freerdp_input_send_synchronize_event(pc->context.input, pc->input_state);
|
||||
if (rc)
|
||||
pc->input_state_sync_pending = FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_server_synchronize_event(rdpInput* input, UINT32 flags)
|
||||
{
|
||||
pServerContext* ps = nullptr;
|
||||
pClientContext* pc = nullptr;
|
||||
|
||||
WINPR_ASSERT(input);
|
||||
ps = (pServerContext*)input->context;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->pdata);
|
||||
|
||||
pc = ps->pdata->pc;
|
||||
WINPR_ASSERT(pc);
|
||||
|
||||
pc->input_state = flags;
|
||||
pc->input_state_sync_pending = TRUE;
|
||||
|
||||
return pf_server_check_and_sync_input_state(pc);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_server_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code)
|
||||
{
|
||||
const proxyConfig* config = nullptr;
|
||||
proxyKeyboardEventInfo event = WINPR_C_ARRAY_INIT;
|
||||
pServerContext* ps = nullptr;
|
||||
pClientContext* pc = nullptr;
|
||||
|
||||
WINPR_ASSERT(input);
|
||||
ps = (pServerContext*)input->context;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->pdata);
|
||||
|
||||
pc = ps->pdata->pc;
|
||||
WINPR_ASSERT(pc);
|
||||
|
||||
config = ps->pdata->config;
|
||||
WINPR_ASSERT(config);
|
||||
|
||||
if (!pf_server_check_and_sync_input_state(pc))
|
||||
return TRUE;
|
||||
|
||||
if (!config->Keyboard)
|
||||
return TRUE;
|
||||
|
||||
event.flags = flags;
|
||||
event.rdp_scan_code = code;
|
||||
|
||||
if (pf_modules_run_filter(pc->pdata->module, FILTER_TYPE_KEYBOARD, pc->pdata, &event))
|
||||
return freerdp_input_send_keyboard_event(pc->context.input, flags, code);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_server_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
|
||||
{
|
||||
const proxyConfig* config = nullptr;
|
||||
proxyUnicodeEventInfo event = WINPR_C_ARRAY_INIT;
|
||||
pServerContext* ps = nullptr;
|
||||
pClientContext* pc = nullptr;
|
||||
|
||||
WINPR_ASSERT(input);
|
||||
ps = (pServerContext*)input->context;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->pdata);
|
||||
|
||||
pc = ps->pdata->pc;
|
||||
WINPR_ASSERT(pc);
|
||||
|
||||
config = ps->pdata->config;
|
||||
WINPR_ASSERT(config);
|
||||
|
||||
if (!pf_server_check_and_sync_input_state(pc))
|
||||
return TRUE;
|
||||
|
||||
if (!config->Keyboard)
|
||||
return TRUE;
|
||||
|
||||
event.flags = flags;
|
||||
event.code = code;
|
||||
if (pf_modules_run_filter(pc->pdata->module, FILTER_TYPE_UNICODE, pc->pdata, &event))
|
||||
return freerdp_input_send_unicode_keyboard_event(pc->context.input, flags, code);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_server_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
|
||||
{
|
||||
proxyMouseEventInfo event = WINPR_C_ARRAY_INIT;
|
||||
const proxyConfig* config = nullptr;
|
||||
pServerContext* ps = nullptr;
|
||||
pClientContext* pc = nullptr;
|
||||
|
||||
WINPR_ASSERT(input);
|
||||
ps = (pServerContext*)input->context;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->pdata);
|
||||
|
||||
pc = ps->pdata->pc;
|
||||
WINPR_ASSERT(pc);
|
||||
|
||||
config = ps->pdata->config;
|
||||
WINPR_ASSERT(config);
|
||||
|
||||
if (!pf_server_check_and_sync_input_state(pc))
|
||||
return TRUE;
|
||||
|
||||
if (!config->Mouse)
|
||||
return TRUE;
|
||||
|
||||
event.flags = flags;
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
|
||||
if (pf_modules_run_filter(pc->pdata->module, FILTER_TYPE_MOUSE, pc->pdata, &event))
|
||||
return freerdp_input_send_mouse_event(pc->context.input, flags, x, y);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_server_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
|
||||
{
|
||||
const proxyConfig* config = nullptr;
|
||||
proxyMouseExEventInfo event = WINPR_C_ARRAY_INIT;
|
||||
pServerContext* ps = nullptr;
|
||||
pClientContext* pc = nullptr;
|
||||
|
||||
WINPR_ASSERT(input);
|
||||
ps = (pServerContext*)input->context;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->pdata);
|
||||
|
||||
pc = ps->pdata->pc;
|
||||
WINPR_ASSERT(pc);
|
||||
|
||||
config = ps->pdata->config;
|
||||
WINPR_ASSERT(config);
|
||||
|
||||
if (!pf_server_check_and_sync_input_state(pc))
|
||||
return TRUE;
|
||||
|
||||
if (!config->Mouse)
|
||||
return TRUE;
|
||||
|
||||
event.flags = flags;
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
if (pf_modules_run_filter(pc->pdata->module, FILTER_TYPE_MOUSE, pc->pdata, &event))
|
||||
return freerdp_input_send_extended_mouse_event(pc->context.input, flags, x, y);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void pf_server_register_input_callbacks(rdpInput* input)
|
||||
{
|
||||
WINPR_ASSERT(input);
|
||||
|
||||
input->SynchronizeEvent = pf_server_synchronize_event;
|
||||
input->KeyboardEvent = pf_server_keyboard_event;
|
||||
input->UnicodeKeyboardEvent = pf_server_unicode_keyboard_event;
|
||||
input->MouseEvent = pf_server_mouse_event;
|
||||
input->ExtendedMouseEvent = pf_server_extended_mouse_event;
|
||||
}
|
||||
29
third_party/FreeRDP/server/proxy/pf_input.h
vendored
Normal file
29
third_party/FreeRDP/server/proxy/pf_input.h
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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_PFINPUT_H
|
||||
#define FREERDP_SERVER_PROXY_PFINPUT_H
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
void pf_server_register_input_callbacks(rdpInput* input);
|
||||
|
||||
#endif /* FREERDP_SERVER_PROXY_PFINPUT_H */
|
||||
745
third_party/FreeRDP/server/proxy/pf_modules.c
vendored
Normal file
745
third_party/FreeRDP/server/proxy/pf_modules.c
vendored
Normal file
@@ -0,0 +1,745 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server modules API
|
||||
*
|
||||
* 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 <winpr/file.h>
|
||||
#include <winpr/wlog.h>
|
||||
#include <winpr/path.h>
|
||||
#include <winpr/library.h>
|
||||
|
||||
#include <freerdp/version.h>
|
||||
#include <freerdp/api.h>
|
||||
#include <freerdp/build-config.h>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_log.h>
|
||||
#include <freerdp/server/proxy/proxy_modules_api.h>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
#include "proxy_modules.h"
|
||||
|
||||
#define TAG PROXY_TAG("modules")
|
||||
|
||||
#define MODULE_ENTRY_POINT "proxy_module_entry_point"
|
||||
|
||||
struct proxy_module
|
||||
{
|
||||
proxyPluginsManager mgr;
|
||||
wArrayList* plugins;
|
||||
wArrayList* handles;
|
||||
};
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static const char* pf_modules_get_filter_type_string(PF_FILTER_TYPE result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case FILTER_TYPE_KEYBOARD:
|
||||
return "FILTER_TYPE_KEYBOARD";
|
||||
case FILTER_TYPE_UNICODE:
|
||||
return "FILTER_TYPE_UNICODE";
|
||||
case FILTER_TYPE_MOUSE:
|
||||
return "FILTER_TYPE_MOUSE";
|
||||
case FILTER_TYPE_MOUSE_EX:
|
||||
return "FILTER_TYPE_MOUSE_EX";
|
||||
case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA:
|
||||
return "FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA";
|
||||
case FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA:
|
||||
return "FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA";
|
||||
case FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE:
|
||||
return "FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE";
|
||||
case FILTER_TYPE_SERVER_FETCH_TARGET_ADDR:
|
||||
return "FILTER_TYPE_SERVER_FETCH_TARGET_ADDR";
|
||||
case FILTER_TYPE_SERVER_PEER_LOGON:
|
||||
return "FILTER_TYPE_SERVER_PEER_LOGON";
|
||||
case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE:
|
||||
return "FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE";
|
||||
case FILTER_TYPE_STATIC_INTERCEPT_LIST:
|
||||
return "FILTER_TYPE_STATIC_INTERCEPT_LIST";
|
||||
case FILTER_TYPE_DYN_INTERCEPT_LIST:
|
||||
return "FILTER_TYPE_DYN_INTERCEPT_LIST";
|
||||
case FILTER_TYPE_INTERCEPT_CHANNEL:
|
||||
return "FILTER_TYPE_INTERCEPT_CHANNEL";
|
||||
case FILTER_LAST:
|
||||
return "FILTER_LAST";
|
||||
default:
|
||||
return "FILTER_UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static const char* pf_modules_get_hook_type_string(PF_HOOK_TYPE result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case HOOK_TYPE_CLIENT_INIT_CONNECT:
|
||||
return "HOOK_TYPE_CLIENT_INIT_CONNECT";
|
||||
case HOOK_TYPE_CLIENT_UNINIT_CONNECT:
|
||||
return "HOOK_TYPE_CLIENT_UNINIT_CONNECT";
|
||||
case HOOK_TYPE_CLIENT_PRE_CONNECT:
|
||||
return "HOOK_TYPE_CLIENT_PRE_CONNECT";
|
||||
case HOOK_TYPE_CLIENT_POST_CONNECT:
|
||||
return "HOOK_TYPE_CLIENT_POST_CONNECT";
|
||||
case HOOK_TYPE_CLIENT_POST_DISCONNECT:
|
||||
return "HOOK_TYPE_CLIENT_POST_DISCONNECT";
|
||||
case HOOK_TYPE_CLIENT_REDIRECT:
|
||||
return "HOOK_TYPE_CLIENT_REDIRECT";
|
||||
case HOOK_TYPE_CLIENT_VERIFY_X509:
|
||||
return "HOOK_TYPE_CLIENT_VERIFY_X509";
|
||||
case HOOK_TYPE_CLIENT_LOGIN_FAILURE:
|
||||
return "HOOK_TYPE_CLIENT_LOGIN_FAILURE";
|
||||
case HOOK_TYPE_CLIENT_END_PAINT:
|
||||
return "HOOK_TYPE_CLIENT_END_PAINT";
|
||||
case HOOK_TYPE_SERVER_POST_CONNECT:
|
||||
return "HOOK_TYPE_SERVER_POST_CONNECT";
|
||||
case HOOK_TYPE_SERVER_ACTIVATE:
|
||||
return "HOOK_TYPE_SERVER_ACTIVATE";
|
||||
case HOOK_TYPE_SERVER_CHANNELS_INIT:
|
||||
return "HOOK_TYPE_SERVER_CHANNELS_INIT";
|
||||
case HOOK_TYPE_SERVER_CHANNELS_FREE:
|
||||
return "HOOK_TYPE_SERVER_CHANNELS_FREE";
|
||||
case HOOK_TYPE_SERVER_SESSION_END:
|
||||
return "HOOK_TYPE_SERVER_SESSION_END";
|
||||
case HOOK_TYPE_CLIENT_LOAD_CHANNELS:
|
||||
return "HOOK_TYPE_CLIENT_LOAD_CHANNELS";
|
||||
case HOOK_TYPE_SERVER_SESSION_INITIALIZE:
|
||||
return "HOOK_TYPE_SERVER_SESSION_INITIALIZE";
|
||||
case HOOK_TYPE_SERVER_SESSION_STARTED:
|
||||
return "HOOK_TYPE_SERVER_SESSION_STARTED";
|
||||
case HOOK_LAST:
|
||||
return "HOOK_LAST";
|
||||
default:
|
||||
return "HOOK_TYPE_UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_proxy_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
|
||||
{
|
||||
proxyPlugin* plugin = (proxyPlugin*)data;
|
||||
BOOL ok = FALSE;
|
||||
|
||||
WINPR_UNUSED(index);
|
||||
|
||||
PF_HOOK_TYPE type = va_arg(ap, PF_HOOK_TYPE);
|
||||
proxyData* pdata = va_arg(ap, proxyData*);
|
||||
void* custom = va_arg(ap, void*);
|
||||
|
||||
WLog_VRB(TAG, "running hook %s.%s", plugin->name, pf_modules_get_hook_type_string(type));
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case HOOK_TYPE_CLIENT_INIT_CONNECT:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientInitConnect, plugin, pdata, custom);
|
||||
break;
|
||||
case HOOK_TYPE_CLIENT_UNINIT_CONNECT:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientUninitConnect, plugin, pdata, custom);
|
||||
break;
|
||||
case HOOK_TYPE_CLIENT_PRE_CONNECT:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientPreConnect, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_CLIENT_POST_CONNECT:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientPostConnect, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_CLIENT_REDIRECT:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientRedirect, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_CLIENT_POST_DISCONNECT:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientPostDisconnect, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_CLIENT_VERIFY_X509:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientX509Certificate, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_CLIENT_LOGIN_FAILURE:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientLoginFailure, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_CLIENT_END_PAINT:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientEndPaint, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_CLIENT_LOAD_CHANNELS:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientLoadChannels, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_SERVER_POST_CONNECT:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ServerPostConnect, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_SERVER_ACTIVATE:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ServerPeerActivate, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_SERVER_CHANNELS_INIT:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ServerChannelsInit, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_SERVER_CHANNELS_FREE:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ServerChannelsFree, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_SERVER_SESSION_END:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ServerSessionEnd, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_SERVER_SESSION_INITIALIZE:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ServerSessionInitialize, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_SERVER_SESSION_STARTED:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ServerSessionStarted, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_LAST:
|
||||
default:
|
||||
WLog_ERR(TAG, "invalid hook called");
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
WLog_INFO(TAG, "plugin %s, hook %s failed!", plugin->name,
|
||||
pf_modules_get_hook_type_string(type));
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* runs all hooks of type `type`.
|
||||
*
|
||||
* @type: hook type to run.
|
||||
* @server: pointer of server's rdpContext struct of the current session.
|
||||
*/
|
||||
BOOL pf_modules_run_hook(proxyModule* module, PF_HOOK_TYPE type, proxyData* pdata, void* custom)
|
||||
{
|
||||
WINPR_ASSERT(module);
|
||||
WINPR_ASSERT(module->plugins);
|
||||
return ArrayList_ForEach(module->plugins, pf_modules_proxy_ArrayList_ForEachFkt, type, pdata,
|
||||
custom);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
|
||||
{
|
||||
proxyPlugin* plugin = (proxyPlugin*)data;
|
||||
BOOL result = FALSE;
|
||||
|
||||
WINPR_UNUSED(index);
|
||||
|
||||
PF_FILTER_TYPE type = va_arg(ap, PF_FILTER_TYPE);
|
||||
proxyData* pdata = va_arg(ap, proxyData*);
|
||||
void* param = va_arg(ap, void*);
|
||||
|
||||
WLog_VRB(TAG, "running filter: %s", plugin->name);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case FILTER_TYPE_KEYBOARD:
|
||||
result = IFCALLRESULT(TRUE, plugin->KeyboardEvent, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_UNICODE:
|
||||
result = IFCALLRESULT(TRUE, plugin->UnicodeEvent, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_MOUSE:
|
||||
result = IFCALLRESULT(TRUE, plugin->MouseEvent, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_MOUSE_EX:
|
||||
result = IFCALLRESULT(TRUE, plugin->MouseExEvent, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA:
|
||||
result = IFCALLRESULT(TRUE, plugin->ClientChannelData, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA:
|
||||
result = IFCALLRESULT(TRUE, plugin->ServerChannelData, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE:
|
||||
result = IFCALLRESULT(TRUE, plugin->ChannelCreate, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE:
|
||||
result = IFCALLRESULT(TRUE, plugin->DynamicChannelCreate, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_SERVER_FETCH_TARGET_ADDR:
|
||||
result = IFCALLRESULT(TRUE, plugin->ServerFetchTargetAddr, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_SERVER_PEER_LOGON:
|
||||
result = IFCALLRESULT(TRUE, plugin->ServerPeerLogon, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_INTERCEPT_CHANNEL:
|
||||
result = IFCALLRESULT(TRUE, plugin->DynChannelIntercept, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_DYN_INTERCEPT_LIST:
|
||||
result = IFCALLRESULT(TRUE, plugin->DynChannelToIntercept, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_STATIC_INTERCEPT_LIST:
|
||||
result = IFCALLRESULT(TRUE, plugin->StaticChannelToIntercept, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_LAST:
|
||||
default:
|
||||
WLog_ERR(TAG, "invalid filter called");
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
/* current filter return FALSE, no need to run other filters. */
|
||||
WLog_DBG(TAG, "plugin %s, filter type [%s] returned FALSE", plugin->name,
|
||||
pf_modules_get_filter_type_string(type));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* runs all filters of type `type`.
|
||||
*
|
||||
* @type: filter type to run.
|
||||
* @server: pointer of server's rdpContext struct of the current session.
|
||||
*/
|
||||
BOOL pf_modules_run_filter(proxyModule* module, PF_FILTER_TYPE type, proxyData* pdata, void* param)
|
||||
{
|
||||
WINPR_ASSERT(module);
|
||||
WINPR_ASSERT(module->plugins);
|
||||
|
||||
return ArrayList_ForEach(module->plugins, pf_modules_ArrayList_ForEachFkt, type, pdata, param);
|
||||
}
|
||||
|
||||
/*
|
||||
* stores per-session data needed by a plugin.
|
||||
*
|
||||
* @context: current session server's rdpContext instance.
|
||||
* @info: pointer to per-session data.
|
||||
*/
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_set_plugin_data(WINPR_ATTR_UNUSED proxyPluginsManager* mgr,
|
||||
const char* plugin_name, proxyData* pdata, void* data)
|
||||
{
|
||||
union
|
||||
{
|
||||
const char* ccp;
|
||||
char* cp;
|
||||
} ccharconv;
|
||||
|
||||
WINPR_ASSERT(plugin_name);
|
||||
|
||||
ccharconv.ccp = plugin_name;
|
||||
if (data == nullptr) /* no need to store anything */
|
||||
return FALSE;
|
||||
|
||||
if (!HashTable_Insert(pdata->modules_info, ccharconv.cp, data))
|
||||
{
|
||||
WLog_ERR(TAG, "HashTable_Insert failed!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* returns per-session data needed a plugin.
|
||||
*
|
||||
* @context: current session server's rdpContext instance.
|
||||
* if there's no data related to `plugin_name` in `context` (current session), a nullptr will be
|
||||
* returned.
|
||||
*/
|
||||
WINPR_ATTR_NODISCARD
|
||||
static void* pf_modules_get_plugin_data(WINPR_ATTR_UNUSED proxyPluginsManager* mgr,
|
||||
const char* plugin_name, proxyData* pdata)
|
||||
{
|
||||
union
|
||||
{
|
||||
const char* ccp;
|
||||
char* cp;
|
||||
} ccharconv;
|
||||
WINPR_ASSERT(plugin_name);
|
||||
WINPR_ASSERT(pdata);
|
||||
ccharconv.ccp = plugin_name;
|
||||
|
||||
return HashTable_GetItemValue(pdata->modules_info, ccharconv.cp);
|
||||
}
|
||||
|
||||
static void pf_modules_abort_connect(WINPR_ATTR_UNUSED proxyPluginsManager* mgr, proxyData* pdata)
|
||||
{
|
||||
WINPR_ASSERT(pdata);
|
||||
WLog_DBG(TAG, "is called!");
|
||||
proxy_data_abort_connect(pdata);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_register_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
|
||||
{
|
||||
proxyPlugin* plugin = (proxyPlugin*)data;
|
||||
proxyPlugin* plugin_to_register = va_arg(ap, proxyPlugin*);
|
||||
|
||||
WINPR_UNUSED(index);
|
||||
|
||||
if (strcmp(plugin->name, plugin_to_register->name) == 0)
|
||||
{
|
||||
WLog_ERR(TAG, "can not register plugin '%s', it is already registered!", plugin->name);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_register_plugin(proxyPluginsManager* mgr,
|
||||
const proxyPlugin* plugin_to_register)
|
||||
{
|
||||
proxyPlugin internal = WINPR_C_ARRAY_INIT;
|
||||
proxyModule* module = (proxyModule*)mgr;
|
||||
WINPR_ASSERT(module);
|
||||
|
||||
if (!plugin_to_register)
|
||||
return FALSE;
|
||||
|
||||
internal = *plugin_to_register;
|
||||
internal.mgr = mgr;
|
||||
|
||||
/* make sure there's no other loaded plugin with the same name of `plugin_to_register`. */
|
||||
if (!ArrayList_ForEach(module->plugins, pf_modules_register_ArrayList_ForEachFkt, &internal))
|
||||
return FALSE;
|
||||
|
||||
if (!ArrayList_Append(module->plugins, &internal))
|
||||
{
|
||||
WLog_ERR(TAG, "failed adding plugin to list: %s", plugin_to_register->name);
|
||||
return FALSE;
|
||||
}
|
||||
WLog_INFO(TAG, "Successfully registered proxy plugin '%s'", plugin_to_register->name);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_load_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
|
||||
{
|
||||
proxyPlugin* plugin = (proxyPlugin*)data;
|
||||
const char* plugin_name = va_arg(ap, const char*);
|
||||
BOOL* res = va_arg(ap, BOOL*);
|
||||
|
||||
WINPR_UNUSED(index);
|
||||
WINPR_UNUSED(ap);
|
||||
WINPR_ASSERT(res);
|
||||
|
||||
if (strcmp(plugin->name, plugin_name) == 0)
|
||||
*res = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL pf_modules_is_plugin_loaded(proxyModule* module, const char* plugin_name)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
WINPR_ASSERT(module);
|
||||
if (ArrayList_Count(module->plugins) < 1)
|
||||
return FALSE;
|
||||
if (!ArrayList_ForEach(module->plugins, pf_modules_load_ArrayList_ForEachFkt, plugin_name, &rc))
|
||||
return FALSE;
|
||||
return rc;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_print_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
|
||||
{
|
||||
proxyPlugin* plugin = (proxyPlugin*)data;
|
||||
|
||||
WINPR_UNUSED(index);
|
||||
WINPR_UNUSED(ap);
|
||||
|
||||
WLog_INFO(TAG, "\tName: %s", plugin->name);
|
||||
WLog_INFO(TAG, "\tDescription: %s", plugin->description);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void pf_modules_list_loaded_plugins(proxyModule* module)
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
WINPR_ASSERT(module);
|
||||
WINPR_ASSERT(module->plugins);
|
||||
|
||||
count = ArrayList_Count(module->plugins);
|
||||
|
||||
if (count > 0)
|
||||
WLog_INFO(TAG, "Loaded plugins:");
|
||||
|
||||
ArrayList_ForEach(module->plugins, pf_modules_print_ArrayList_ForEachFkt);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_load_static_module(const char* module_name, proxyModule* module,
|
||||
void* userdata)
|
||||
{
|
||||
WINPR_ASSERT(module);
|
||||
|
||||
HANDLE handle = GetModuleHandleA(nullptr);
|
||||
|
||||
if (handle == nullptr)
|
||||
{
|
||||
WLog_DBG(TAG, "failed loading static library: %s", module_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
char name[256] = WINPR_C_ARRAY_INIT;
|
||||
(void)_snprintf(name, sizeof(name), "%s_%s", module_name, MODULE_ENTRY_POINT);
|
||||
for (size_t x = 0; x < strnlen(name, sizeof(name)); x++)
|
||||
{
|
||||
if (name[x] == '-')
|
||||
name[x] = '_';
|
||||
}
|
||||
|
||||
proxyModuleEntryPoint pEntryPoint = GetProcAddressAs(handle, name, proxyModuleEntryPoint);
|
||||
if (!pEntryPoint)
|
||||
{
|
||||
WLog_DBG(TAG, "GetProcAddress failed for static %s (module %s)", name, module_name);
|
||||
goto error;
|
||||
}
|
||||
if (!ArrayList_Append(module->handles, handle))
|
||||
{
|
||||
WLog_ERR(TAG, "ArrayList_Append failed!");
|
||||
goto error;
|
||||
}
|
||||
return pf_modules_add(module, pEntryPoint, userdata);
|
||||
|
||||
error:
|
||||
FreeLibrary(handle);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_load_dynamic_module(const char* module_path, proxyModule* module,
|
||||
void* userdata)
|
||||
{
|
||||
if (!winpr_PathFileExists(module_path))
|
||||
{
|
||||
WLog_DBG(TAG, "failed loading external library: file '%s' does not exist", module_path);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
HANDLE handle = LoadLibraryX(module_path);
|
||||
|
||||
if (handle == nullptr)
|
||||
{
|
||||
WLog_DBG(TAG, "failed loading external library: %s", module_path);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
proxyModuleEntryPoint pEntryPoint =
|
||||
GetProcAddressAs(handle, MODULE_ENTRY_POINT, proxyModuleEntryPoint);
|
||||
if (!pEntryPoint)
|
||||
{
|
||||
WLog_DBG(TAG, "GetProcAddress failed while loading %s", module_path);
|
||||
goto error;
|
||||
}
|
||||
if (!ArrayList_Append(module->handles, handle))
|
||||
{
|
||||
WLog_ERR(TAG, "ArrayList_Append failed!");
|
||||
goto error;
|
||||
}
|
||||
return pf_modules_add(module, pEntryPoint, userdata);
|
||||
|
||||
error:
|
||||
FreeLibrary(handle);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_try_load_dynamic_module(const char* module_path, proxyModule* module,
|
||||
void* userdata, size_t count, const char* names[])
|
||||
{
|
||||
for (size_t x = 0; x < count; x++)
|
||||
{
|
||||
const char* name = names[x];
|
||||
|
||||
char* fullpath = GetCombinedPath(module_path, name);
|
||||
if (!fullpath)
|
||||
continue;
|
||||
|
||||
const BOOL rc = pf_modules_load_dynamic_module(fullpath, module, userdata);
|
||||
free(fullpath);
|
||||
if (rc)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_load_module(const char* module_path, const char* module_name,
|
||||
proxyModule* module, void* userdata)
|
||||
{
|
||||
WINPR_ASSERT(module);
|
||||
|
||||
if (pf_modules_load_static_module(module_name, module, userdata))
|
||||
return TRUE;
|
||||
|
||||
char names[5][MAX_PATH] = WINPR_C_ARRAY_INIT;
|
||||
(void)_snprintf(names[0], sizeof(names[0]), "proxy-%s-plugin%s", module_name,
|
||||
FREERDP_SHARED_LIBRARY_SUFFIX);
|
||||
(void)_snprintf(names[1], sizeof(names[1]), "%sproxy-%s-plugin%s",
|
||||
FREERDP_SHARED_LIBRARY_PREFIX, module_name, FREERDP_SHARED_LIBRARY_SUFFIX);
|
||||
(void)_snprintf(names[2], sizeof(names[2]), "%sproxy-%s-plugin%s.%d",
|
||||
FREERDP_SHARED_LIBRARY_PREFIX, module_name, FREERDP_SHARED_LIBRARY_SUFFIX,
|
||||
FREERDP_VERSION_MAJOR);
|
||||
(void)_snprintf(names[3], sizeof(names[3]), "%sproxy-%s-plugin%d%s",
|
||||
FREERDP_SHARED_LIBRARY_PREFIX, module_name, FREERDP_VERSION_MAJOR,
|
||||
FREERDP_SHARED_LIBRARY_SUFFIX);
|
||||
(void)_snprintf(names[4], sizeof(names[4]), "%sproxy-%s-plugin%d%s.%d",
|
||||
FREERDP_SHARED_LIBRARY_PREFIX, module_name, FREERDP_VERSION_MAJOR,
|
||||
FREERDP_SHARED_LIBRARY_SUFFIX, FREERDP_VERSION_MAJOR);
|
||||
|
||||
const char* cnames[5] = { names[0], names[1], names[2], names[3], names[4] };
|
||||
return pf_modules_try_load_dynamic_module(module_path, module, userdata, ARRAYSIZE(cnames),
|
||||
cnames);
|
||||
}
|
||||
|
||||
static void free_handle(void* obj)
|
||||
{
|
||||
HANDLE handle = (HANDLE)obj;
|
||||
if (handle)
|
||||
FreeLibrary(handle);
|
||||
}
|
||||
|
||||
static void free_plugin(void* obj)
|
||||
{
|
||||
proxyPlugin* plugin = (proxyPlugin*)obj;
|
||||
WINPR_ASSERT(plugin);
|
||||
|
||||
if (!IFCALLRESULT(TRUE, plugin->PluginUnload, plugin))
|
||||
WLog_WARN(TAG, "PluginUnload failed for plugin '%s'", plugin->name);
|
||||
|
||||
free(plugin);
|
||||
}
|
||||
|
||||
WINPR_ATTR_MALLOC(free_plugin, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
static void* new_plugin(const void* obj)
|
||||
{
|
||||
const proxyPlugin* src = obj;
|
||||
proxyPlugin* proxy = calloc(1, sizeof(proxyPlugin));
|
||||
if (!proxy)
|
||||
return nullptr;
|
||||
*proxy = *src;
|
||||
return proxy;
|
||||
}
|
||||
|
||||
proxyModule* pf_modules_new(const char* root_dir, const char** modules, size_t count)
|
||||
{
|
||||
wObject* obj = nullptr;
|
||||
char* path = nullptr;
|
||||
proxyModule* module = calloc(1, sizeof(proxyModule));
|
||||
if (!module)
|
||||
return nullptr;
|
||||
|
||||
module->mgr.RegisterPlugin = pf_modules_register_plugin;
|
||||
module->mgr.SetPluginData = pf_modules_set_plugin_data;
|
||||
module->mgr.GetPluginData = pf_modules_get_plugin_data;
|
||||
module->mgr.AbortConnect = pf_modules_abort_connect;
|
||||
module->plugins = ArrayList_New(FALSE);
|
||||
|
||||
if (module->plugins == nullptr)
|
||||
{
|
||||
WLog_ERR(TAG, "ArrayList_New failed!");
|
||||
goto error;
|
||||
}
|
||||
obj = ArrayList_Object(module->plugins);
|
||||
WINPR_ASSERT(obj);
|
||||
|
||||
obj->fnObjectFree = free_plugin;
|
||||
obj->fnObjectNew = new_plugin;
|
||||
|
||||
module->handles = ArrayList_New(FALSE);
|
||||
if (module->handles == nullptr)
|
||||
{
|
||||
|
||||
WLog_ERR(TAG, "ArrayList_New failed!");
|
||||
goto error;
|
||||
}
|
||||
ArrayList_Object(module->handles)->fnObjectFree = free_handle;
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
WINPR_ASSERT(root_dir);
|
||||
if (!winpr_PathFileExists(root_dir))
|
||||
path = GetCombinedPath(FREERDP_INSTALL_PREFIX, root_dir);
|
||||
else
|
||||
path = _strdup(root_dir);
|
||||
|
||||
if (!winpr_PathFileExists(path))
|
||||
{
|
||||
if (!winpr_PathMakePath(path, nullptr))
|
||||
{
|
||||
WLog_ERR(TAG, "error occurred while creating modules directory: %s", root_dir);
|
||||
}
|
||||
}
|
||||
|
||||
if (winpr_PathFileExists(path))
|
||||
WLog_DBG(TAG, "modules root directory: %s", path);
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
const char* module_name = modules[i];
|
||||
if (!pf_modules_load_module(path, module_name, module, nullptr))
|
||||
WLog_WARN(TAG, "Failed to load proxy module '%s'", module_name);
|
||||
else
|
||||
WLog_INFO(TAG, "Successfully loaded proxy module '%s'", module_name);
|
||||
}
|
||||
}
|
||||
|
||||
free(path);
|
||||
return module;
|
||||
|
||||
error:
|
||||
free(path);
|
||||
pf_modules_free(module);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void pf_modules_free(proxyModule* module)
|
||||
{
|
||||
if (!module)
|
||||
return;
|
||||
|
||||
ArrayList_Free(module->plugins);
|
||||
ArrayList_Free(module->handles);
|
||||
free(module);
|
||||
}
|
||||
|
||||
BOOL pf_modules_add(proxyModule* module, proxyModuleEntryPoint ep, void* userdata)
|
||||
{
|
||||
WINPR_ASSERT(module);
|
||||
WINPR_ASSERT(ep);
|
||||
|
||||
return ep(&module->mgr, userdata);
|
||||
}
|
||||
1111
third_party/FreeRDP/server/proxy/pf_server.c
vendored
Normal file
1111
third_party/FreeRDP/server/proxy/pf_server.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
43
third_party/FreeRDP/server/proxy/pf_server.h
vendored
Normal file
43
third_party/FreeRDP/server/proxy/pf_server.h
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef INT_FREERDP_SERVER_PROXY_SERVER_H
|
||||
#define INT_FREERDP_SERVER_PROXY_SERVER_H
|
||||
|
||||
#include <winpr/collections.h>
|
||||
#include <freerdp/listener.h>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_config.h>
|
||||
#include "proxy_modules.h"
|
||||
|
||||
struct proxy_server
|
||||
{
|
||||
proxyModule* module;
|
||||
proxyConfig* config;
|
||||
|
||||
freerdp_listener* listener;
|
||||
HANDLE stopEvent; /* an event used to signal the main thread to stop */
|
||||
wArrayList* peer_list;
|
||||
};
|
||||
|
||||
#endif /* INT_FREERDP_SERVER_PROXY_SERVER_H */
|
||||
656
third_party/FreeRDP/server/proxy/pf_update.c
vendored
Normal file
656
third_party/FreeRDP/server/proxy/pf_update.c
vendored
Normal file
@@ -0,0 +1,656 @@
|
||||
/**
|
||||
* 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 <freerdp/display.h>
|
||||
#include <freerdp/session.h>
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/image.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_log.h>
|
||||
|
||||
#include "pf_update.h"
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
#include "proxy_modules.h"
|
||||
|
||||
#define TAG PROXY_TAG("update")
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_server_refresh_rect(rdpContext* context, BYTE count, const RECTANGLE_16* areas)
|
||||
{
|
||||
pServerContext* ps = (pServerContext*)context;
|
||||
rdpContext* pc = nullptr;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->pdata);
|
||||
pc = (rdpContext*)ps->pdata->pc;
|
||||
WINPR_ASSERT(pc);
|
||||
WINPR_ASSERT(pc->update);
|
||||
WINPR_ASSERT(pc->update->RefreshRect);
|
||||
return pc->update->RefreshRect(pc, count, areas);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_server_suppress_output(rdpContext* context, BYTE allow, const RECTANGLE_16* area)
|
||||
{
|
||||
pServerContext* ps = (pServerContext*)context;
|
||||
rdpContext* pc = nullptr;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->pdata);
|
||||
pc = (rdpContext*)ps->pdata->pc;
|
||||
WINPR_ASSERT(pc);
|
||||
WINPR_ASSERT(pc->update);
|
||||
WINPR_ASSERT(pc->update->SuppressOutput);
|
||||
return pc->update->SuppressOutput(pc, allow, area);
|
||||
}
|
||||
|
||||
/* Proxy from PC to PS */
|
||||
|
||||
/**
|
||||
* This function is called whenever a new frame starts.
|
||||
* It can be used to reset invalidated areas.
|
||||
*/
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_begin_paint(rdpContext* context)
|
||||
{
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->BeginPaint);
|
||||
WLog_DBG(TAG, "called");
|
||||
return ps->update->BeginPaint(ps);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called when the library completed composing a new
|
||||
* frame. Read out the changed areas and blit them to your output device.
|
||||
* The image buffer will have the format specified by gdi_init
|
||||
*/
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_end_paint(rdpContext* context)
|
||||
{
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->EndPaint);
|
||||
|
||||
WLog_DBG(TAG, "called");
|
||||
|
||||
/* proxy end paint */
|
||||
if (!ps->update->EndPaint(ps))
|
||||
return FALSE;
|
||||
|
||||
if (!pf_modules_run_hook(pdata->module, HOOK_TYPE_CLIENT_END_PAINT, pdata, context))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_bitmap_update(rdpContext* context, const BITMAP_UPDATE* bitmap)
|
||||
{
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->BitmapUpdate);
|
||||
WLog_DBG(TAG, "called");
|
||||
return ps->update->BitmapUpdate(ps, bitmap);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_desktop_resize(rdpContext* context)
|
||||
{
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->DesktopResize);
|
||||
WINPR_ASSERT(context->settings);
|
||||
WINPR_ASSERT(ps->settings);
|
||||
WLog_DBG(TAG, "called");
|
||||
if (!freerdp_settings_copy_item(ps->settings, context->settings, FreeRDP_DesktopWidth))
|
||||
return FALSE;
|
||||
if (!freerdp_settings_copy_item(ps->settings, context->settings, FreeRDP_DesktopHeight))
|
||||
return FALSE;
|
||||
return ps->update->DesktopResize(ps);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_remote_monitors(rdpContext* context, UINT32 count,
|
||||
const MONITOR_DEF* monitors)
|
||||
{
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WLog_DBG(TAG, "called");
|
||||
return freerdp_display_send_monitor_layout(ps, count, monitors);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_send_pointer_system(rdpContext* context,
|
||||
const POINTER_SYSTEM_UPDATE* pointer_system)
|
||||
{
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->pointer);
|
||||
WINPR_ASSERT(ps->update->pointer->PointerSystem);
|
||||
WLog_DBG(TAG, "called");
|
||||
return ps->update->pointer->PointerSystem(ps, pointer_system);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_send_pointer_position(rdpContext* context,
|
||||
const POINTER_POSITION_UPDATE* pointerPosition)
|
||||
{
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->pointer);
|
||||
WINPR_ASSERT(ps->update->pointer->PointerPosition);
|
||||
WLog_DBG(TAG, "called");
|
||||
return ps->update->pointer->PointerPosition(ps, pointerPosition);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_send_pointer_color(rdpContext* context,
|
||||
const POINTER_COLOR_UPDATE* pointer_color)
|
||||
{
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->pointer);
|
||||
WINPR_ASSERT(ps->update->pointer->PointerColor);
|
||||
WLog_DBG(TAG, "called");
|
||||
return ps->update->pointer->PointerColor(ps, pointer_color);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_send_pointer_large(rdpContext* context,
|
||||
const POINTER_LARGE_UPDATE* pointer_large)
|
||||
{
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->pointer);
|
||||
WINPR_ASSERT(ps->update->pointer->PointerLarge);
|
||||
WLog_DBG(TAG, "called");
|
||||
return ps->update->pointer->PointerLarge(ps, pointer_large);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_send_pointer_new(rdpContext* context, const POINTER_NEW_UPDATE* pointer_new)
|
||||
{
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->pointer);
|
||||
WINPR_ASSERT(ps->update->pointer->PointerNew);
|
||||
WLog_DBG(TAG, "called");
|
||||
return ps->update->pointer->PointerNew(ps, pointer_new);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_send_pointer_cached(rdpContext* context,
|
||||
const POINTER_CACHED_UPDATE* pointer_cached)
|
||||
{
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->pointer);
|
||||
WINPR_ASSERT(ps->update->pointer->PointerCached);
|
||||
WLog_DBG(TAG, "called");
|
||||
return ps->update->pointer->PointerCached(ps, pointer_cached);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_save_session_info(rdpContext* context, UINT32 type, void* data)
|
||||
{
|
||||
logon_info* logonInfo = nullptr;
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->SaveSessionInfo);
|
||||
|
||||
WLog_DBG(TAG, "called");
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case INFO_TYPE_LOGON:
|
||||
case INFO_TYPE_LOGON_LONG:
|
||||
{
|
||||
logonInfo = (logon_info*)data;
|
||||
PROXY_LOG_INFO(TAG, pc, "client logon info: Username: %s, Domain: %s",
|
||||
logonInfo->username, logonInfo->domain);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ps->update->SaveSessionInfo(ps, type, data);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_server_status_info(rdpContext* context, UINT32 status)
|
||||
{
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->ServerStatusInfo);
|
||||
|
||||
WLog_DBG(TAG, "called");
|
||||
return ps->update->ServerStatusInfo(ps, status);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_set_keyboard_indicators(rdpContext* context, UINT16 led_flags)
|
||||
{
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->SetKeyboardIndicators);
|
||||
|
||||
WLog_DBG(TAG, "called");
|
||||
return ps->update->SetKeyboardIndicators(ps, led_flags);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_set_keyboard_ime_status(rdpContext* context, UINT16 imeId, UINT32 imeState,
|
||||
UINT32 imeConvMode)
|
||||
{
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->SetKeyboardImeStatus);
|
||||
|
||||
WLog_DBG(TAG, "called");
|
||||
return ps->update->SetKeyboardImeStatus(ps, imeId, imeState, imeConvMode);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_window_create(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
|
||||
const WINDOW_STATE_ORDER* windowState)
|
||||
{
|
||||
BOOL rc = 0;
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->window);
|
||||
WINPR_ASSERT(ps->update->window->WindowCreate);
|
||||
|
||||
WLog_DBG(TAG, "called");
|
||||
rdp_update_lock(ps->update);
|
||||
rc = ps->update->window->WindowCreate(ps, orderInfo, windowState);
|
||||
rdp_update_unlock(ps->update);
|
||||
return rc;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_window_update(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
|
||||
const WINDOW_STATE_ORDER* windowState)
|
||||
{
|
||||
BOOL rc = 0;
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->window);
|
||||
WINPR_ASSERT(ps->update->window->WindowUpdate);
|
||||
|
||||
WLog_DBG(TAG, "called");
|
||||
rdp_update_lock(ps->update);
|
||||
rc = ps->update->window->WindowUpdate(ps, orderInfo, windowState);
|
||||
rdp_update_unlock(ps->update);
|
||||
return rc;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_window_icon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
|
||||
const WINDOW_ICON_ORDER* windowIcon)
|
||||
{
|
||||
BOOL rc = 0;
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->window);
|
||||
WINPR_ASSERT(ps->update->window->WindowIcon);
|
||||
|
||||
WLog_DBG(TAG, "called");
|
||||
rdp_update_lock(ps->update);
|
||||
rc = ps->update->window->WindowIcon(ps, orderInfo, windowIcon);
|
||||
rdp_update_unlock(ps->update);
|
||||
return rc;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_window_cached_icon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
|
||||
const WINDOW_CACHED_ICON_ORDER* windowCachedIcon)
|
||||
{
|
||||
BOOL rc = 0;
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->window);
|
||||
WINPR_ASSERT(ps->update->window->WindowCachedIcon);
|
||||
|
||||
WLog_DBG(TAG, "called");
|
||||
rdp_update_lock(ps->update);
|
||||
rc = ps->update->window->WindowCachedIcon(ps, orderInfo, windowCachedIcon);
|
||||
rdp_update_unlock(ps->update);
|
||||
return rc;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_window_delete(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo)
|
||||
{
|
||||
BOOL rc = 0;
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->window);
|
||||
WINPR_ASSERT(ps->update->window->WindowDelete);
|
||||
|
||||
WLog_DBG(TAG, "called");
|
||||
rdp_update_lock(ps->update);
|
||||
rc = ps->update->window->WindowDelete(ps, orderInfo);
|
||||
rdp_update_unlock(ps->update);
|
||||
return rc;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_notify_icon_create(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
|
||||
const NOTIFY_ICON_STATE_ORDER* notifyIconState)
|
||||
{
|
||||
BOOL rc = 0;
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->window);
|
||||
WINPR_ASSERT(ps->update->window->NotifyIconCreate);
|
||||
|
||||
WLog_DBG(TAG, "called");
|
||||
rdp_update_lock(ps->update);
|
||||
rc = ps->update->window->NotifyIconCreate(ps, orderInfo, notifyIconState);
|
||||
rdp_update_unlock(ps->update);
|
||||
return rc;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_notify_icon_update(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
|
||||
const NOTIFY_ICON_STATE_ORDER* notifyIconState)
|
||||
{
|
||||
BOOL rc = 0;
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->window);
|
||||
WINPR_ASSERT(ps->update->window->NotifyIconUpdate);
|
||||
|
||||
WLog_DBG(TAG, "called");
|
||||
rdp_update_lock(ps->update);
|
||||
rc = ps->update->window->NotifyIconUpdate(ps, orderInfo, notifyIconState);
|
||||
rdp_update_unlock(ps->update);
|
||||
return rc;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_notify_icon_delete(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo)
|
||||
{
|
||||
BOOL rc = 0;
|
||||
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->window);
|
||||
WINPR_ASSERT(ps->update->window->NotifyIconDelete);
|
||||
|
||||
WLog_DBG(TAG, "called");
|
||||
rdp_update_lock(ps->update);
|
||||
rc = ps->update->window->NotifyIconDelete(ps, orderInfo);
|
||||
rdp_update_unlock(ps->update);
|
||||
return rc;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_monitored_desktop(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
|
||||
const MONITORED_DESKTOP_ORDER* monitoredDesktop)
|
||||
{
|
||||
BOOL rc = 0;
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->window);
|
||||
WINPR_ASSERT(ps->update->window->MonitoredDesktop);
|
||||
|
||||
WLog_DBG(TAG, "called");
|
||||
rdp_update_lock(ps->update);
|
||||
rc = ps->update->window->MonitoredDesktop(ps, orderInfo, monitoredDesktop);
|
||||
rdp_update_unlock(ps->update);
|
||||
return rc;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_client_non_monitored_desktop(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo)
|
||||
{
|
||||
BOOL rc = 0;
|
||||
pClientContext* pc = (pClientContext*)context;
|
||||
proxyData* pdata = nullptr;
|
||||
rdpContext* ps = nullptr;
|
||||
WINPR_ASSERT(pc);
|
||||
pdata = pc->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
ps = (rdpContext*)pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(ps->update);
|
||||
WINPR_ASSERT(ps->update->window);
|
||||
WINPR_ASSERT(ps->update->window->NonMonitoredDesktop);
|
||||
|
||||
WLog_DBG(TAG, "called");
|
||||
rdp_update_lock(ps->update);
|
||||
rc = ps->update->window->NonMonitoredDesktop(ps, orderInfo);
|
||||
rdp_update_unlock(ps->update);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void pf_server_register_update_callbacks(rdpUpdate* update)
|
||||
{
|
||||
WINPR_ASSERT(update);
|
||||
update->RefreshRect = pf_server_refresh_rect;
|
||||
update->SuppressOutput = pf_server_suppress_output;
|
||||
}
|
||||
|
||||
void pf_client_register_update_callbacks(rdpUpdate* update)
|
||||
{
|
||||
WINPR_ASSERT(update);
|
||||
update->BeginPaint = pf_client_begin_paint;
|
||||
update->EndPaint = pf_client_end_paint;
|
||||
update->BitmapUpdate = pf_client_bitmap_update;
|
||||
update->DesktopResize = pf_client_desktop_resize;
|
||||
update->RemoteMonitors = pf_client_remote_monitors;
|
||||
update->SaveSessionInfo = pf_client_save_session_info;
|
||||
update->ServerStatusInfo = pf_client_server_status_info;
|
||||
update->SetKeyboardIndicators = pf_client_set_keyboard_indicators;
|
||||
update->SetKeyboardImeStatus = pf_client_set_keyboard_ime_status;
|
||||
|
||||
/* Rail window updates */
|
||||
update->window->WindowCreate = pf_client_window_create;
|
||||
update->window->WindowUpdate = pf_client_window_update;
|
||||
update->window->WindowIcon = pf_client_window_icon;
|
||||
update->window->WindowCachedIcon = pf_client_window_cached_icon;
|
||||
update->window->WindowDelete = pf_client_window_delete;
|
||||
update->window->NotifyIconCreate = pf_client_notify_icon_create;
|
||||
update->window->NotifyIconUpdate = pf_client_notify_icon_update;
|
||||
update->window->NotifyIconDelete = pf_client_notify_icon_delete;
|
||||
update->window->MonitoredDesktop = pf_client_monitored_desktop;
|
||||
update->window->NonMonitoredDesktop = pf_client_non_monitored_desktop;
|
||||
|
||||
/* Pointer updates */
|
||||
update->pointer->PointerSystem = pf_client_send_pointer_system;
|
||||
update->pointer->PointerPosition = pf_client_send_pointer_position;
|
||||
update->pointer->PointerColor = pf_client_send_pointer_color;
|
||||
update->pointer->PointerLarge = pf_client_send_pointer_large;
|
||||
update->pointer->PointerNew = pf_client_send_pointer_new;
|
||||
update->pointer->PointerCached = pf_client_send_pointer_cached;
|
||||
}
|
||||
34
third_party/FreeRDP/server/proxy/pf_update.h
vendored
Normal file
34
third_party/FreeRDP/server/proxy/pf_update.h
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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_PFUPDATE_H
|
||||
#define FREERDP_SERVER_PROXY_PFUPDATE_H
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/gdi/gdi.h>
|
||||
#include <freerdp/gdi/bitmap.h>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
void pf_server_register_update_callbacks(rdpUpdate* update);
|
||||
void pf_client_register_update_callbacks(rdpUpdate* update);
|
||||
|
||||
#endif /* FREERDP_SERVER_PROXY_PFUPDATE_H */
|
||||
95
third_party/FreeRDP/server/proxy/pf_utils.c
vendored
Normal file
95
third_party/FreeRDP/server/proxy/pf_utils.c
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* 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/wtsapi.h>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_log.h>
|
||||
#include "pf_utils.h"
|
||||
|
||||
#define TAG PROXY_TAG("utils")
|
||||
|
||||
pf_utils_channel_mode pf_utils_get_channel_mode(const proxyConfig* config, const char* name)
|
||||
{
|
||||
pf_utils_channel_mode rc = PF_UTILS_CHANNEL_NOT_HANDLED;
|
||||
BOOL found = FALSE;
|
||||
|
||||
WINPR_ASSERT(config);
|
||||
WINPR_ASSERT(name);
|
||||
|
||||
for (size_t i = 0; i < config->InterceptCount; i++)
|
||||
{
|
||||
const char* channel_name = config->Intercept[i];
|
||||
if (strcmp(name, channel_name) == 0)
|
||||
{
|
||||
rc = PF_UTILS_CHANNEL_INTERCEPT;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < config->PassthroughCount; i++)
|
||||
{
|
||||
const char* channel_name = config->Passthrough[i];
|
||||
if (strcmp(name, channel_name) == 0)
|
||||
{
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
if (config->PassthroughIsBlacklist)
|
||||
rc = PF_UTILS_CHANNEL_BLOCK;
|
||||
else
|
||||
rc = PF_UTILS_CHANNEL_PASSTHROUGH;
|
||||
}
|
||||
else if (config->PassthroughIsBlacklist)
|
||||
rc = PF_UTILS_CHANNEL_PASSTHROUGH;
|
||||
|
||||
end:
|
||||
WLog_DBG(TAG, "%s -> %s", name, pf_utils_channel_mode_string(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
BOOL pf_utils_is_passthrough(WINPR_ATTR_UNUSED const proxyConfig* config)
|
||||
{
|
||||
WINPR_ASSERT(config);
|
||||
|
||||
/* TODO: For the time being only passthrough mode is supported. */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
const char* pf_utils_channel_mode_string(pf_utils_channel_mode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case PF_UTILS_CHANNEL_BLOCK:
|
||||
return "blocked";
|
||||
case PF_UTILS_CHANNEL_PASSTHROUGH:
|
||||
return "passthrough";
|
||||
case PF_UTILS_CHANNEL_INTERCEPT:
|
||||
return "intercepted";
|
||||
case PF_UTILS_CHANNEL_NOT_HANDLED:
|
||||
default:
|
||||
return "ignored";
|
||||
}
|
||||
}
|
||||
44
third_party/FreeRDP/server/proxy/pf_utils.h
vendored
Normal file
44
third_party/FreeRDP/server/proxy/pf_utils.h
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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_PFUTILS_H
|
||||
#define FREERDP_SERVER_PROXY_PFUTILS_H
|
||||
|
||||
#include <freerdp/server/proxy/proxy_config.h>
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
/**
|
||||
* @brief pf_utils_channel_is_passthrough Checks of a channel identified by 'name'
|
||||
* should be handled as passthrough.
|
||||
*
|
||||
* @param config The proxy configuration to check against. Must NOT be nullptr.
|
||||
* @param name The name of the channel. Must NOT be nullptr.
|
||||
* @return -1 if the channel is not handled, 0 if the channel should be ignored,
|
||||
* 1 if the channel should be passed, 2 the channel will be intercepted
|
||||
* e.g. proxy client and server are termination points and data passed
|
||||
* between.
|
||||
*/
|
||||
WINPR_ATTR_NODISCARD pf_utils_channel_mode pf_utils_get_channel_mode(const proxyConfig* config,
|
||||
const char* name);
|
||||
WINPR_ATTR_NODISCARD const char* pf_utils_channel_mode_string(pf_utils_channel_mode mode);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_utils_is_passthrough(const proxyConfig* config);
|
||||
|
||||
#endif /* FREERDP_SERVER_PROXY_PFUTILS_H */
|
||||
106
third_party/FreeRDP/server/proxy/proxy_modules.h
vendored
Normal file
106
third_party/FreeRDP/server/proxy/proxy_modules.h
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server
|
||||
*
|
||||
* 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_MODULES_H
|
||||
#define FREERDP_SERVER_PROXY_MODULES_H
|
||||
|
||||
#include <winpr/wtypes.h>
|
||||
#include <winpr/collections.h>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_modules_api.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
FILTER_TYPE_KEYBOARD, /* proxyKeyboardEventInfo */
|
||||
FILTER_TYPE_UNICODE, /* proxyUnicodeEventInfo */
|
||||
FILTER_TYPE_MOUSE, /* proxyMouseEventInfo */
|
||||
FILTER_TYPE_MOUSE_EX, /* proxyMouseExEventInfo */
|
||||
FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA, /* proxyChannelDataEventInfo */
|
||||
FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA, /* proxyChannelDataEventInfo */
|
||||
FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE, /* proxyChannelDataEventInfo */
|
||||
FILTER_TYPE_SERVER_FETCH_TARGET_ADDR, /* proxyFetchTargetEventInfo */
|
||||
FILTER_TYPE_SERVER_PEER_LOGON, /* proxyServerPeerLogon */
|
||||
FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE, /* proxyChannelDataEventInfo */
|
||||
|
||||
FILTER_TYPE_STATIC_INTERCEPT_LIST, /* proxyChannelToInterceptData */
|
||||
FILTER_TYPE_DYN_INTERCEPT_LIST, /* proxyChannelToInterceptData */
|
||||
FILTER_TYPE_INTERCEPT_CHANNEL, /* proxyDynChannelInterceptData */
|
||||
FILTER_LAST
|
||||
} PF_FILTER_TYPE;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
HOOK_TYPE_CLIENT_INIT_CONNECT,
|
||||
HOOK_TYPE_CLIENT_UNINIT_CONNECT,
|
||||
HOOK_TYPE_CLIENT_PRE_CONNECT,
|
||||
HOOK_TYPE_CLIENT_POST_CONNECT,
|
||||
HOOK_TYPE_CLIENT_POST_DISCONNECT,
|
||||
HOOK_TYPE_CLIENT_REDIRECT,
|
||||
HOOK_TYPE_CLIENT_VERIFY_X509,
|
||||
HOOK_TYPE_CLIENT_LOGIN_FAILURE,
|
||||
HOOK_TYPE_CLIENT_END_PAINT,
|
||||
HOOK_TYPE_CLIENT_LOAD_CHANNELS,
|
||||
|
||||
HOOK_TYPE_SERVER_POST_CONNECT,
|
||||
HOOK_TYPE_SERVER_ACTIVATE,
|
||||
HOOK_TYPE_SERVER_CHANNELS_INIT,
|
||||
HOOK_TYPE_SERVER_CHANNELS_FREE,
|
||||
HOOK_TYPE_SERVER_SESSION_END,
|
||||
HOOK_TYPE_SERVER_SESSION_INITIALIZE,
|
||||
HOOK_TYPE_SERVER_SESSION_STARTED,
|
||||
|
||||
HOOK_LAST
|
||||
} PF_HOOK_TYPE;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
void pf_modules_free(proxyModule* module);
|
||||
|
||||
WINPR_ATTR_MALLOC(pf_modules_free, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
proxyModule* pf_modules_new(const char* root_dir, const char** modules, size_t count);
|
||||
|
||||
/**
|
||||
* @brief pf_modules_add Registers a new plugin
|
||||
* @param ep A module entry point function, must NOT be nullptr
|
||||
* @return TRUE for success, FALSE otherwise
|
||||
*/
|
||||
WINPR_ATTR_NODISCARD BOOL pf_modules_add(proxyModule* module, proxyModuleEntryPoint ep,
|
||||
void* userdata);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_modules_is_plugin_loaded(proxyModule* module,
|
||||
const char* plugin_name);
|
||||
void pf_modules_list_loaded_plugins(proxyModule* module);
|
||||
|
||||
WINPR_ATTR_NODISCARD BOOL pf_modules_run_filter(proxyModule* module, PF_FILTER_TYPE type,
|
||||
proxyData* pdata, void* param);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
BOOL pf_modules_run_hook(proxyModule* module, PF_HOOK_TYPE type, proxyData* pdata,
|
||||
void* custom);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FREERDP_SERVER_PROXY_MODULES_H */
|
||||
Reference in New Issue
Block a user