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.";
47static const std::vector<std::string>& plugin_static_intercept()
49 static std::vector<std::string> vec;
51 vec.emplace_back(DRDYNVC_SVC_CHANNEL_NAME);
55static 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;
123static 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;
138static 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;
155static 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;
172static size_t drdynvc_cblen_to_bytes(UINT8 cbLen)
187static 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);
209static 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);
248static 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));
260static BOOL filter_set_plugin_data(proxyPlugin* plugin, proxyData* pdata, DynChannelState* data)
262 WINPR_ASSERT(plugin);
265 auto mgr =
static_cast<proxyPluginsManager*
>(plugin->custom);
268 WINPR_ASSERT(mgr->SetPluginData);
269 return mgr->SetPluginData(mgr, plugin_name, pdata, data);
272#if defined(REPLY_WITH_EMPTY_OFFER)
273static UINT8 drdynvc_value_to_cblen(UINT32 value)
282static BOOL drdynvc_write_variable_uint(
wStream* s, UINT32 value, UINT8 cbLen)
287 Stream_Write_UINT8(s,
static_cast<UINT8
>(value));
291 Stream_Write_UINT16(s,
static_cast<UINT16
>(value));
295 Stream_Write_UINT32(s, value);
302static BOOL drdynvc_write_header(
wStream* s, UINT32 channelId)
304 const UINT8 cbChId = drdynvc_value_to_cblen(channelId);
305 const UINT8 value = (DATA_PDU << 4) | cbChId;
306 const size_t len = drdynvc_cblen_to_bytes(cbChId) + 1;
308 if (!Stream_EnsureRemainingCapacity(s, len))
311 Stream_Write_UINT8(s, value);
312 return drdynvc_write_variable_uint(s, value, cbChId);
316 size_t startPosition, UINT32 channelId)
320 Stream_SetPosition(data->data, startPosition);
321 if (!drdynvc_write_header(data->data, channelId))
324 if (!Stream_EnsureRemainingCapacity(data->data,
sizeof(UINT16)))
326 Stream_Write_UINT16(data->data, 0);
327 Stream_SealLength(data->data);
329 WLog_INFO(TAG,
"[SessionID=%s][%s] forwarding empty %s", sessionID, plugin_name,
330 rdpgfx_get_cmd_id_string(RDPGFX_CMDID_CACHEIMPORTOFFER));
331 data->rewritten = TRUE;
336static BOOL filter_dyn_channel_intercept(proxyPlugin* plugin, proxyData* pdata,
void* arg)
340 WINPR_ASSERT(plugin);
344 data->result = PF_CHANNEL_RESULT_PASS;
345 if (!data->isBackData &&
346 (strncmp(data->name, RDPGFX_DVC_CHANNEL_NAME, ARRAYSIZE(RDPGFX_DVC_CHANNEL_NAME)) == 0))
348 auto state = filter_get_plugin_data(plugin, pdata);
351 WLog_ERR(TAG,
"[SessionID=%s][%s] missing custom data, aborting!", pdata->session_id,
355 const size_t inputDataLength = Stream_Length(data->data);
356 UINT16 cmdId = RDPGFX_CMDID_UNUSED_0000;
358 const auto pos = Stream_GetPosition(data->data);
363 uint32_t channelId = 0;
365 if (drdynvc_try_read_header(data->data, channelId, length))
367 if (Stream_GetRemainingLength(data->data) >= 2)
369 Stream_Read_UINT16(data->data, cmdId);
370 state->setSkipSize(length);
371 state->setDrop(
false);
377 case RDPGFX_CMDID_CACHEIMPORTOFFER:
378 state->setDrop(
true);
379 state->setChannelId(channelId);
384 Stream_SetPosition(data->data, pos);
390 if (!state->skip(inputDataLength))
396 "[SessionID=%s][%s] dropping %s packet [total:%" PRIuz
", current:%" PRIuz
397 ", remaining: %" PRIuz
"]",
398 pdata->session_id, plugin_name,
399 rdpgfx_get_cmd_id_string(RDPGFX_CMDID_CACHEIMPORTOFFER), state->total(),
400 inputDataLength, state->remaining());
401 data->result = PF_CHANNEL_RESULT_DROP;
403#if defined(REPLY_WITH_EMPTY_OFFER)
405 if (state->remaining() == 0)
407 if (!filter_forward_empty_offer(pdata->session_id, data, pos,
419static BOOL filter_server_session_started(proxyPlugin* plugin, proxyData* pdata,
void* )
421 WINPR_ASSERT(plugin);
424 auto state = filter_get_plugin_data(plugin, pdata);
427 auto newstate =
new DynChannelState();
428 if (!filter_set_plugin_data(plugin, pdata, newstate))
437static BOOL filter_server_session_end(proxyPlugin* plugin, proxyData* pdata,
void* )
439 WINPR_ASSERT(plugin);
442 auto state = filter_get_plugin_data(plugin, pdata);
444 filter_set_plugin_data(plugin, pdata,
nullptr);
452 FREERDP_API BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager,
void* userdata);
457BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager,
void* userdata)
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);
FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL param)
Sets a BOOL settings value.