32#include <freerdp/server/proxy/proxy_modules_api.h>
33#include <freerdp/server/proxy/proxy_context.h>
35#include <freerdp/channels/drdynvc.h>
36#include <freerdp/channels/rdpgfx.h>
37#include <freerdp/utils/gfx.h>
39#define TAG MODULE_TAG("persist-bitmap-filter")
43static constexpr char plugin_name[] =
"bitmap-filter";
44static constexpr char plugin_desc[] =
45 "this plugin deactivates and filters persistent bitmap cache.";
47[[nodiscard]]
static const std::vector<std::string>& plugin_static_intercept()
49 static std::vector<std::string> vec;
51 vec.emplace_back(DRDYNVC_SVC_CHANNEL_NAME);
55[[nodiscard]]
static const std::vector<std::string>& plugin_dyn_intercept()
57 static std::vector<std::string> vec;
59 vec.emplace_back(RDPGFX_DVC_CHANNEL_NAME);
67 [[nodiscard]]
bool skip()
const
72 [[nodiscard]]
bool skip(
size_t s)
81 [[nodiscard]]
size_t remaining()
const
86 [[nodiscard]]
size_t total()
const
88 return _totalSkipSize;
91 void setSkipSize(
size_t len)
93 _toSkip = _totalSkipSize = len;
96 [[nodiscard]]
bool drop()
const
106 [[nodiscard]] uint32_t channelId()
const
111 void setChannelId(uint32_t
id)
118 size_t _totalSkipSize = 0;
120 uint32_t _channelId = 0;
123[[nodiscard]]
static BOOL filter_client_pre_connect([[maybe_unused]] proxyPlugin* plugin,
124 [[maybe_unused]] proxyData* pdata,
125 [[maybe_unused]]
void* custom)
127 WINPR_ASSERT(plugin);
129 WINPR_ASSERT(pdata->pc);
130 WINPR_ASSERT(custom);
132 auto settings = pdata->pc->context.settings;
138[[nodiscard]]
static BOOL filter_dyn_channel_intercept_list([[maybe_unused]] proxyPlugin* plugin,
139 [[maybe_unused]] proxyData* pdata,
140 [[maybe_unused]]
void* arg)
144 WINPR_ASSERT(plugin);
148 auto intercept = std::find(plugin_dyn_intercept().begin(), plugin_dyn_intercept().end(),
149 data->name) != plugin_dyn_intercept().end();
151 data->intercept = TRUE;
155[[nodiscard]]
static BOOL filter_static_channel_intercept_list([[maybe_unused]] proxyPlugin* plugin,
156 [[maybe_unused]] proxyData* pdata,
157 [[maybe_unused]]
void* arg)
161 WINPR_ASSERT(plugin);
165 auto intercept = std::find(plugin_static_intercept().begin(), plugin_static_intercept().end(),
166 data->name) != plugin_static_intercept().end();
168 data->intercept = TRUE;
172[[nodiscard]]
static size_t drdynvc_cblen_to_bytes(UINT8 cbLen)
187[[nodiscard]]
static UINT32 drdynvc_read_variable_uint(
wStream* s, UINT8 cbLen)
194 Stream_Read_UINT8(s, val);
198 Stream_Read_UINT16(s, val);
202 Stream_Read_UINT32(s, val);
209[[nodiscard]]
static BOOL drdynvc_try_read_header(
wStream* s, uint32_t& channelId,
size_t& length)
212 Stream_SetPosition(s, 0);
213 if (Stream_GetRemainingLength(s) < 1)
215 Stream_Read_UINT8(s, value);
217 const UINT8 cmd = (value & 0xf0) >> 4;
218 const UINT8 Sp = (value & 0x0c) >> 2;
219 const UINT8 cbChId = (value & 0x03);
230 const size_t channelIdLen = drdynvc_cblen_to_bytes(cbChId);
231 if (Stream_GetRemainingLength(s) < channelIdLen)
234 channelId = drdynvc_read_variable_uint(s, cbChId);
235 length = Stream_Length(s);
236 if (cmd == DATA_FIRST_PDU)
238 const size_t dataLen = drdynvc_cblen_to_bytes(Sp);
239 if (Stream_GetRemainingLength(s) < dataLen)
242 length = drdynvc_read_variable_uint(s, Sp);
248[[nodiscard]]
static DynChannelState* filter_get_plugin_data(proxyPlugin* plugin, proxyData* pdata)
250 WINPR_ASSERT(plugin);
253 auto mgr =
static_cast<proxyPluginsManager*
>(plugin->custom);
256 WINPR_ASSERT(mgr->GetPluginData);
257 return static_cast<DynChannelState*
>(mgr->GetPluginData(mgr, plugin_name, pdata));
260[[nodiscard]]
static BOOL filter_set_plugin_data(proxyPlugin* plugin, proxyData* pdata,
261 DynChannelState* data)
263 WINPR_ASSERT(plugin);
266 auto mgr =
static_cast<proxyPluginsManager*
>(plugin->custom);
269 WINPR_ASSERT(mgr->SetPluginData);
270 return mgr->SetPluginData(mgr, plugin_name, pdata, data);
273#if defined(REPLY_WITH_EMPTY_OFFER)
274[[nodiscard]]
static UINT8 drdynvc_value_to_cblen(UINT32 value)
283[[nodiscard]]
static BOOL drdynvc_write_variable_uint(
wStream* s, UINT32 value, UINT8 cbLen)
288 Stream_Write_UINT8(s,
static_cast<UINT8
>(value));
292 Stream_Write_UINT16(s,
static_cast<UINT16
>(value));
296 Stream_Write_UINT32(s, value);
303[[nodiscard]]
static BOOL drdynvc_write_header(
wStream* s, UINT32 channelId)
305 const UINT8 cbChId = drdynvc_value_to_cblen(channelId);
306 const UINT8 value = (DATA_PDU << 4) | cbChId;
307 const size_t len = drdynvc_cblen_to_bytes(cbChId) + 1;
309 if (!Stream_EnsureRemainingCapacity(s, len))
312 Stream_Write_UINT8(s, value);
313 return drdynvc_write_variable_uint(s, value, cbChId);
316[[nodiscard]]
static BOOL filter_forward_empty_offer(
const char* sessionID,
318 size_t startPosition, UINT32 channelId)
322 Stream_SetPosition(data->data, startPosition);
323 if (!drdynvc_write_header(data->data, channelId))
326 if (!Stream_EnsureRemainingCapacity(data->data,
sizeof(UINT16)))
328 Stream_Write_UINT16(data->data, 0);
329 Stream_SealLength(data->data);
331 WLog_INFO(TAG,
"[SessionID=%s][%s] forwarding empty %s", sessionID, plugin_name,
332 rdpgfx_get_cmd_id_string(RDPGFX_CMDID_CACHEIMPORTOFFER));
333 data->rewritten = TRUE;
338[[nodiscard]]
static BOOL filter_dyn_channel_intercept(proxyPlugin* plugin, proxyData* pdata,
343 WINPR_ASSERT(plugin);
347 data->result = PF_CHANNEL_RESULT_PASS;
348 if (!data->isBackData &&
349 (strncmp(data->name, RDPGFX_DVC_CHANNEL_NAME, ARRAYSIZE(RDPGFX_DVC_CHANNEL_NAME)) == 0))
351 auto state = filter_get_plugin_data(plugin, pdata);
354 WLog_ERR(TAG,
"[SessionID=%s][%s] missing custom data, aborting!", pdata->session_id,
358 const size_t inputDataLength = Stream_Length(data->data);
359 UINT16 cmdId = RDPGFX_CMDID_UNUSED_0000;
361 const auto pos = Stream_GetPosition(data->data);
366 uint32_t channelId = 0;
368 if (drdynvc_try_read_header(data->data, channelId, length))
370 if (Stream_GetRemainingLength(data->data) >= 2)
372 Stream_Read_UINT16(data->data, cmdId);
373 state->setSkipSize(length);
374 state->setDrop(
false);
380 case RDPGFX_CMDID_CACHEIMPORTOFFER:
381 state->setDrop(
true);
382 state->setChannelId(channelId);
387 Stream_SetPosition(data->data, pos);
393 if (state->skip(inputDataLength))
396 "skipping data, but %" PRIuz
" bytes left [stream has %" PRIuz
" bytes]",
397 state->remaining(), inputDataLength);
403 "[SessionID=%s][%s] dropping %s packet [total:%" PRIuz
", current:%" PRIuz
404 ", remaining: %" PRIuz
"]",
405 pdata->session_id, plugin_name,
406 rdpgfx_get_cmd_id_string(RDPGFX_CMDID_CACHEIMPORTOFFER), state->total(),
407 inputDataLength, state->remaining());
408 data->result = PF_CHANNEL_RESULT_DROP;
410#if defined(REPLY_WITH_EMPTY_OFFER)
412 if (state->remaining() == 0)
414 if (!filter_forward_empty_offer(pdata->session_id, data, pos,
426[[nodiscard]]
static BOOL filter_server_session_started(proxyPlugin* plugin, proxyData* pdata,
429 WINPR_ASSERT(plugin);
432 auto state = filter_get_plugin_data(plugin, pdata);
435 auto newstate =
new DynChannelState();
436 if (!filter_set_plugin_data(plugin, pdata, newstate))
445[[nodiscard]]
static BOOL filter_server_session_end(proxyPlugin* plugin, proxyData* pdata,
448 WINPR_ASSERT(plugin);
451 auto state = filter_get_plugin_data(plugin, pdata);
453 return filter_set_plugin_data(plugin, pdata,
nullptr);
456[[nodiscard]]
static BOOL int_proxy_module_entry_point(proxyPluginsManager* plugins_manager,
459 proxyPlugin plugin = {};
461 plugin.name = plugin_name;
462 plugin.description = plugin_desc;
464 plugin.ServerSessionStarted = filter_server_session_started;
465 plugin.ServerSessionEnd = filter_server_session_end;
467 plugin.ClientPreConnect = filter_client_pre_connect;
469 plugin.StaticChannelToIntercept = filter_static_channel_intercept_list;
470 plugin.DynChannelToIntercept = filter_dyn_channel_intercept_list;
471 plugin.DynChannelIntercept = filter_dyn_channel_intercept;
473 plugin.custom = plugins_manager;
476 plugin.userdata = userdata;
478 return plugins_manager->RegisterPlugin(plugins_manager, &plugin);
485#if defined(BUILD_SHARED_LIBS)
487 FREERDP_API BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager,
void* userdata);
489 BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager,
void* userdata)
491 return int_proxy_module_entry_point(plugins_manager, userdata);
495FREERDP_API BOOL bitmap_filter_proxy_module_entry_point(proxyPluginsManager* plugins_manager,
497BOOL bitmap_filter_proxy_module_entry_point(proxyPluginsManager* plugins_manager,
void* userdata)
499 return int_proxy_module_entry_point(plugins_manager, userdata);
FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL param)
Sets a BOOL settings value.