Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
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}
|
||||
Reference in New Issue
Block a user