summaryrefslogtreecommitdiffstats
path: root/server/proxy/channels/pf_channel_drdynvc.c
diff options
context:
space:
mode:
Diffstat (limited to 'server/proxy/channels/pf_channel_drdynvc.c')
-rw-r--r--server/proxy/channels/pf_channel_drdynvc.c711
1 files changed, 711 insertions, 0 deletions
diff --git a/server/proxy/channels/pf_channel_drdynvc.c b/server/proxy/channels/pf_channel_drdynvc.c
new file mode 100644
index 0000000..9d8cab9
--- /dev/null
+++ b/server/proxy/channels/pf_channel_drdynvc.c
@@ -0,0 +1,711 @@
+/**
+ * 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")
+
+/** @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;
+ 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";
+ }
+}
+
+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 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 NULL;
+ }
+
+ 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 NULL;
+ }
+
+ 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;
+}
+
+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);
+}
+
+static UINT32 ChannelId_Hash(const void* key)
+{
+ const UINT32* v = (const UINT32*)key;
+ return *v;
+}
+
+static BOOL ChannelId_Compare(const void* objA, const void* objB)
+{
+ const UINT32* v1 = objA;
+ const UINT32* v2 = objB;
+ return (*v1 == *v2);
+}
+
+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;
+}
+
+static PfChannelResult DynvcTrackerPeekFn(ChannelStateTracker* tracker, BOOL firstPacket,
+ BOOL lastPacket)
+{
+ BYTE cmd = 0;
+ BYTE byte0 = 0;
+ wStream* s = NULL;
+ wStream sbuffer;
+ BOOL haveChannelId = 0;
+ BOOL haveLength = 0;
+ UINT64 dynChannelId = 0;
+ UINT64 Length = 0;
+ pServerDynamicChannelContext* dynChannel = NULL;
+
+ WINPR_ASSERT(tracker);
+
+ DynChannelContext* dynChannelContext =
+ (DynChannelContext*)channelTracker_getCustomData(tracker);
+ WINPR_ASSERT(dynChannelContext);
+
+ BOOL isBackData = (tracker == dynChannelContext->backTracker);
+ DynChannelTrackerState* trackerState = NULL;
+
+ UINT32 flags = lastPacket ? CHANNEL_FLAG_LAST : 0;
+ proxyData* pdata = channelTracker_getPData(tracker);
+ WINPR_ASSERT(pdata);
+
+ const char* direction = isBackData ? "B->F" : "F->B";
+
+ {
+ wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
+ s = Stream_StaticConstInit(&sbuffer, Stream_Buffer(currentPacket),
+ Stream_GetPosition(currentPacket));
+ }
+
+ if (!Stream_CheckAndLogRequiredLengthWLog(dynChannelContext->log, s, 1))
+ return PF_CHANNEL_RESULT_ERROR;
+
+ Stream_Read_UINT8(s, byte0);
+ 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:
+ WLog_Print(dynChannelContext->log, WLOG_ERROR,
+ "DynvcTrackerPeekFn: 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)
+ {
+ /* 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:
+ WLog_Print(dynChannelContext->log, WLOG_ERROR,
+ "DynvcTrackerPeekFn: invalid length field");
+ return PF_CHANNEL_RESULT_ERROR;
+ }
+ }
+
+ switch (cmd)
+ {
+ case CAPABILITY_REQUEST_PDU:
+ WLog_Print(dynChannelContext->log, WLOG_DEBUG, "DynvcTracker: %s CAPABILITY_%s",
+ direction, isBackData ? "REQUEST" : "RESPONSE");
+ channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
+ return PF_CHANNEL_RESULT_PASS;
+
+ case CREATE_REQUEST_PDU:
+ {
+ UINT32 creationStatus = 0;
+
+ /* we only want the full packet */
+ if (!lastPacket)
+ return PF_CHANNEL_RESULT_DROP;
+
+ if (isBackData)
+ {
+ proxyChannelDataEventInfo dev = { 0 };
+ const char* name = Stream_ConstPointer(s);
+ const size_t nameLen = Stream_GetRemainingLength(s);
+
+ const size_t len = strnlen(name, nameLen);
+ if ((len == 0) || (len == nameLen))
+ return PF_CHANNEL_RESULT_ERROR;
+
+ wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
+ dev.channel_id = 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)
+ {
+ WLog_Print(
+ dynChannelContext->log, WLOG_WARN,
+ "Reusing channel id %" PRIu32 ", previously %s [state %s, mode %s], now %s",
+ dynChannel->channelId, dynChannel->channelName,
+ openstatus2str(dynChannel->openStatus),
+ pf_utils_channel_mode_string(dynChannel->channelMode), dev.channel_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,
+ dynChannelId);
+ if (!dynChannel)
+ {
+ WLog_Print(dynChannelContext->log, WLOG_ERROR,
+ "unable to create dynamic channel context data");
+ return PF_CHANNEL_RESULT_ERROR;
+ }
+
+ WLog_Print(dynChannelContext->log, WLOG_DEBUG, "Adding channel '%s'[%d]",
+ dynChannel->channelName, dynChannel->channelId);
+ if (!HashTable_Insert(dynChannelContext->channels, &dynChannel->channelId,
+ dynChannel))
+ {
+ WLog_Print(dynChannelContext->log, WLOG_ERROR,
+ "unable register dynamic channel context data");
+ DynamicChannelContext_free(dynChannel);
+ return PF_CHANNEL_RESULT_ERROR;
+ }
+
+ dynChannel->openStatus = CHANNEL_OPENSTATE_WAITING_OPEN_STATUS;
+
+ // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert owns dynChannel
+ return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, FALSE);
+ }
+
+ /* CREATE_REQUEST_PDU response */
+ if (!Stream_CheckAndLogRequiredLengthWLog(dynChannelContext->log, s, 4))
+ return PF_CHANNEL_RESULT_ERROR;
+
+ Stream_Read_UINT32(s, creationStatus);
+ WLog_Print(dynChannelContext->log, WLOG_DEBUG,
+ "DynvcTracker(%" PRIu64 ",%s): %s CREATE_RESPONSE openStatus=%" PRIu32,
+ dynChannelId, dynChannel->channelName, direction, creationStatus);
+
+ if (creationStatus == 0)
+ dynChannel->openStatus = CHANNEL_OPENSTATE_OPENED;
+
+ return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, TRUE);
+ }
+
+ case CLOSE_REQUEST_PDU:
+ if (!lastPacket)
+ return PF_CHANNEL_RESULT_DROP;
+
+ WLog_Print(dynChannelContext->log, WLOG_DEBUG,
+ "DynvcTracker(%s): %s Close request on channel", dynChannel->channelName,
+ direction);
+ channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
+ if (dynChannel->openStatus != CHANNEL_OPENSTATE_OPENED)
+ {
+ WLog_Print(dynChannelContext->log, WLOG_WARN,
+ "DynvcTracker(%s): is in state %s, expected %s", dynChannel->channelName,
+ openstatus2str(dynChannel->openStatus),
+ openstatus2str(CHANNEL_OPENSTATE_OPENED));
+ }
+ dynChannel->openStatus = CHANNEL_OPENSTATE_CLOSED;
+ return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
+
+ case SOFT_SYNC_REQUEST_PDU:
+ /* just pass then as is for now */
+ WLog_Print(dynChannelContext->log, WLOG_DEBUG, "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 */
+ WLog_Print(dynChannelContext->log, WLOG_DEBUG, "SOFT_SYNC_RESPONSE_PDU");
+ channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
+ return PF_CHANNEL_RESULT_PASS;
+
+ case DATA_FIRST_PDU:
+ case DATA_PDU:
+ /* treat these below */
+ trackerState = isBackData ? &dynChannel->backTracker : &dynChannel->frontTracker;
+ break;
+
+ case DATA_FIRST_COMPRESSED_PDU:
+ case DATA_COMPRESSED_PDU:
+ WLog_Print(dynChannelContext->log, WLOG_DEBUG,
+ "TODO: compressed data packets, pass them as is for now");
+ channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
+ return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
+
+ default:
+ return PF_CHANNEL_RESULT_ERROR;
+ }
+
+ if (dynChannel->openStatus != CHANNEL_OPENSTATE_OPENED)
+ {
+ WLog_Print(dynChannelContext->log, WLOG_ERROR,
+ "DynvcTracker(%s [%s]): channel is not opened", dynChannel->channelName,
+ drdynvc_get_packet_type(cmd));
+ return PF_CHANNEL_RESULT_ERROR;
+ }
+
+ if ((cmd == DATA_FIRST_PDU) || (cmd == DATA_FIRST_COMPRESSED_PDU))
+ {
+ WLog_Print(dynChannelContext->log, WLOG_DEBUG,
+ "DynvcTracker(%s [%s]): %s DATA_FIRST currentPacketLength=%" PRIu64 "",
+ dynChannel->channelName, drdynvc_get_packet_type(cmd), direction, Length);
+ trackerState->currentDataLength = Length;
+ trackerState->CurrentDataReceived = 0;
+ trackerState->CurrentDataFragments = 0;
+
+ if (dynChannel->packetReassembly)
+ {
+ if (trackerState->currentPacket)
+ Stream_SetPosition(trackerState->currentPacket, 0);
+ }
+ }
+
+ if (cmd == DATA_PDU || cmd == DATA_FIRST_PDU)
+ {
+ size_t extraSize = Stream_GetRemainingLength(s);
+
+ trackerState->CurrentDataFragments++;
+ trackerState->CurrentDataReceived += extraSize;
+
+ if (dynChannel->packetReassembly)
+ {
+ if (!trackerState->currentPacket)
+ {
+ trackerState->currentPacket = Stream_New(NULL, 1024);
+ if (!trackerState->currentPacket)
+ {
+ WLog_Print(dynChannelContext->log, WLOG_ERROR,
+ "unable to create current packet");
+ return PF_CHANNEL_RESULT_ERROR;
+ }
+ }
+
+ if (!Stream_EnsureRemainingCapacity(trackerState->currentPacket, extraSize))
+ {
+ WLog_Print(dynChannelContext->log, WLOG_ERROR, "unable to grow current packet");
+ return PF_CHANNEL_RESULT_ERROR;
+ }
+
+ Stream_Write(trackerState->currentPacket, Stream_ConstPointer(s), extraSize);
+ }
+ WLog_Print(dynChannelContext->log, WLOG_DEBUG,
+ "DynvcTracker(%s [%s]): %s frags=%" PRIu32 " received=%" PRIu32 "(%" PRIu32 ")",
+ dynChannel->channelName, drdynvc_get_packet_type(cmd), direction,
+ trackerState->CurrentDataFragments, trackerState->CurrentDataReceived,
+ trackerState->currentDataLength);
+ }
+
+ if (cmd == DATA_PDU)
+ {
+ if (trackerState->currentDataLength)
+ {
+ if (trackerState->CurrentDataReceived > trackerState->currentDataLength)
+ {
+ WLog_Print(dynChannelContext->log, WLOG_ERROR,
+ "DynvcTracker (%s [%s]): reassembled packet (%" PRIu32
+ ") is bigger than announced length (%" PRIu32 ")",
+ dynChannel->channelName, drdynvc_get_packet_type(cmd),
+ trackerState->CurrentDataReceived, trackerState->currentDataLength);
+ return PF_CHANNEL_RESULT_ERROR;
+ }
+ }
+ else
+ {
+ trackerState->CurrentDataFragments = 0;
+ trackerState->CurrentDataReceived = 0;
+ }
+ }
+
+ PfChannelResult result = PF_CHANNEL_RESULT_ERROR;
+ 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
+ {
+ WLog_Print(dynChannelContext->log, WLOG_ERROR,
+ "no intercept callback for channel %s(fromBack=%d), dropping packet",
+ dynChannel->channelName, isBackData);
+ result = PF_CHANNEL_RESULT_DROP;
+ }
+ break;
+ default:
+ WLog_Print(dynChannelContext->log, WLOG_ERROR, "unknown channel mode %d",
+ 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_SetPosition(trackerState->currentPacket, 0);
+ }
+
+ return result;
+}
+
+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);
+}
+
+static const char* dynamic_context(void* arg)
+{
+ proxyData* pdata = arg;
+ if (!pdata)
+ return "pdata=null";
+ return pdata->session_id;
+}
+
+static DynChannelContext* DynChannelContext_new(proxyData* pdata,
+ pServerStaticChannelContext* channel)
+{
+ DynChannelContext* dyn = calloc(1, sizeof(DynChannelContext));
+ if (!dyn)
+ return FALSE;
+
+ 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 NULL;
+}
+
+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);
+}
+
+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;
+}