mirror of
https://github.com/FreeRDP/FreeRDP.git
synced 2025-06-03 00:00:20 +00:00
proxy: prepare intercept mode for dynamic channels
This patch introduce the basic architecture to intercept content of dynamic channel. When a dynamic channel is in intercept mode, we accumulate and reassemble the current packet so that it can be passed for inspection.
This commit is contained in:
parent
ff266e9575
commit
7041517ed6
@ -36,24 +36,38 @@ typedef enum
|
|||||||
CHANNEL_OPENSTATE_CLOSED /*!< dynamic channel has been opened then closed */
|
CHANNEL_OPENSTATE_CLOSED /*!< dynamic channel has been opened then closed */
|
||||||
} PfDynChannelOpenStatus;
|
} 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 */
|
/** @brief tracker state for a drdynvc stream */
|
||||||
typedef struct
|
struct DynChannelTrackerState
|
||||||
{
|
{
|
||||||
UINT32 currentDataLength;
|
UINT32 currentDataLength;
|
||||||
UINT32 CurrentDataReceived;
|
UINT32 CurrentDataReceived;
|
||||||
UINT32 CurrentDataFragments;
|
UINT32 CurrentDataFragments;
|
||||||
} DynChannelTrackerState;
|
wStream* currentPacket;
|
||||||
|
dynamic_channel_on_data_fn dataCallback;
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct p_server_dynamic_channel_context pServerDynamicChannelContext;
|
typedef void (*channel_data_dtor_fn)(void** user_data);
|
||||||
|
|
||||||
struct p_server_dynamic_channel_context
|
struct p_server_dynamic_channel_context
|
||||||
{
|
{
|
||||||
char* channel_name;
|
char* channelName;
|
||||||
UINT32 channel_id;
|
UINT32 channelId;
|
||||||
PfDynChannelOpenStatus openStatus;
|
PfDynChannelOpenStatus openStatus;
|
||||||
pf_utils_channel_mode channelMode;
|
pf_utils_channel_mode channelMode;
|
||||||
|
BOOL packetReassembly;
|
||||||
DynChannelTrackerState backTracker;
|
DynChannelTrackerState backTracker;
|
||||||
DynChannelTrackerState frontTracker;
|
DynChannelTrackerState frontTracker;
|
||||||
|
|
||||||
|
void* channelData;
|
||||||
|
channel_data_dtor_fn channelDataDtor;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @brief context for the dynamic channel */
|
/** @brief context for the dynamic channel */
|
||||||
@ -82,9 +96,9 @@ static pServerDynamicChannelContext* DynamicChannelContext_new(pServerContext* p
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret->channel_id = id;
|
ret->channelId = id;
|
||||||
ret->channel_name = _strdup(name);
|
ret->channelName = _strdup(name);
|
||||||
if (!ret->channel_name)
|
if (!ret->channelName)
|
||||||
{
|
{
|
||||||
PROXY_LOG_ERR(TAG, ps, "error allocating name in dynamic channel context '%s'", name);
|
PROXY_LOG_ERR(TAG, ps, "error allocating name in dynamic channel context '%s'", name);
|
||||||
free(ret);
|
free(ret);
|
||||||
@ -93,16 +107,27 @@ static pServerDynamicChannelContext* DynamicChannelContext_new(pServerContext* p
|
|||||||
|
|
||||||
ret->channelMode = pf_utils_get_channel_mode(ps->pdata->config, name);
|
ret->channelMode = pf_utils_get_channel_mode(ps->pdata->config, name);
|
||||||
ret->openStatus = CHANNEL_OPENSTATE_OPENED;
|
ret->openStatus = CHANNEL_OPENSTATE_OPENED;
|
||||||
|
ret->packetReassembly = (ret->channelMode == PF_UTILS_CHANNEL_INTERCEPT);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DynamicChannelContext_free(pServerDynamicChannelContext* c)
|
static void DynamicChannelContext_free(pServerDynamicChannelContext* c)
|
||||||
{
|
{
|
||||||
if (c)
|
if (!c)
|
||||||
{
|
return;
|
||||||
free(c->channel_name);
|
|
||||||
free(c);
|
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)
|
static UINT32 ChannelId_Hash(const void* key)
|
||||||
@ -321,7 +346,7 @@ static PfChannelResult DynvcTrackerPeekFn(ChannelStateTracker* tracker, BOOL fir
|
|||||||
return PF_CHANNEL_RESULT_ERROR;
|
return PF_CHANNEL_RESULT_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!HashTable_Insert(dynChannelContext->channels, &dynChannel->channel_id,
|
if (!HashTable_Insert(dynChannelContext->channels, &dynChannel->channelId,
|
||||||
dynChannel))
|
dynChannel))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "unable register dynamic channel context data");
|
WLog_ERR(TAG, "unable register dynamic channel context data");
|
||||||
@ -340,14 +365,14 @@ static PfChannelResult DynvcTrackerPeekFn(ChannelStateTracker* tracker, BOOL fir
|
|||||||
|
|
||||||
Stream_Read_UINT32(s, creationStatus);
|
Stream_Read_UINT32(s, creationStatus);
|
||||||
WLog_DBG(TAG, "DynvcTracker(%" PRIu64 ",%s): %s CREATE_RESPONSE openStatus=%" PRIu32,
|
WLog_DBG(TAG, "DynvcTracker(%" PRIu64 ",%s): %s CREATE_RESPONSE openStatus=%" PRIu32,
|
||||||
dynChannelId, dynChannel->channel_name, direction, creationStatus);
|
dynChannelId, dynChannel->channelName, direction, creationStatus);
|
||||||
|
|
||||||
if (creationStatus != 0)
|
if (creationStatus != 0)
|
||||||
{
|
{
|
||||||
/* we remove it from the channels map, as it happens that server reused channel ids
|
/* we remove it from the channels map, as it happens that server reused channel ids
|
||||||
* when the channel can't be opened
|
* when the channel can't be opened
|
||||||
*/
|
*/
|
||||||
HashTable_Remove(dynChannelContext->channels, &dynChannel->channel_id);
|
HashTable_Remove(dynChannelContext->channels, &dynChannel->channelId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -361,7 +386,7 @@ static PfChannelResult DynvcTrackerPeekFn(ChannelStateTracker* tracker, BOOL fir
|
|||||||
if (!lastPacket)
|
if (!lastPacket)
|
||||||
return PF_CHANNEL_RESULT_DROP;
|
return PF_CHANNEL_RESULT_DROP;
|
||||||
|
|
||||||
WLog_DBG(TAG, "DynvcTracker(%s): %s Close request on channel", dynChannel->channel_name,
|
WLog_DBG(TAG, "DynvcTracker(%s): %s Close request on channel", dynChannel->channelName,
|
||||||
direction);
|
direction);
|
||||||
tracker->mode = CHANNEL_TRACKER_PASS;
|
tracker->mode = CHANNEL_TRACKER_PASS;
|
||||||
dynChannel->openStatus = CHANNEL_OPENSTATE_CLOSED;
|
dynChannel->openStatus = CHANNEL_OPENSTATE_CLOSED;
|
||||||
@ -398,7 +423,7 @@ static PfChannelResult DynvcTrackerPeekFn(ChannelStateTracker* tracker, BOOL fir
|
|||||||
|
|
||||||
if (dynChannel->openStatus != CHANNEL_OPENSTATE_OPENED)
|
if (dynChannel->openStatus != CHANNEL_OPENSTATE_OPENED)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "DynvcTracker(%s [%s]): channel is not opened", dynChannel->channel_name,
|
WLog_ERR(TAG, "DynvcTracker(%s [%s]): channel is not opened", dynChannel->channelName,
|
||||||
get_packet_type(cmd));
|
get_packet_type(cmd));
|
||||||
return PF_CHANNEL_RESULT_ERROR;
|
return PF_CHANNEL_RESULT_ERROR;
|
||||||
}
|
}
|
||||||
@ -406,19 +431,48 @@ static PfChannelResult DynvcTrackerPeekFn(ChannelStateTracker* tracker, BOOL fir
|
|||||||
if ((cmd == DATA_FIRST_PDU) || (cmd == DATA_FIRST_COMPRESSED_PDU))
|
if ((cmd == DATA_FIRST_PDU) || (cmd == DATA_FIRST_COMPRESSED_PDU))
|
||||||
{
|
{
|
||||||
WLog_DBG(TAG, "DynvcTracker(%s [%s]): %s DATA_FIRST currentPacketLength=%" PRIu64 "",
|
WLog_DBG(TAG, "DynvcTracker(%s [%s]): %s DATA_FIRST currentPacketLength=%" PRIu64 "",
|
||||||
dynChannel->channel_name, get_packet_type(cmd), direction, Length);
|
dynChannel->channelName, get_packet_type(cmd), direction, Length);
|
||||||
trackerState->currentDataLength = Length;
|
trackerState->currentDataLength = Length;
|
||||||
trackerState->CurrentDataReceived = 0;
|
trackerState->CurrentDataReceived = 0;
|
||||||
trackerState->CurrentDataFragments = 0;
|
trackerState->CurrentDataFragments = 0;
|
||||||
|
|
||||||
|
if (dynChannel->packetReassembly)
|
||||||
|
{
|
||||||
|
if (trackerState->currentPacket)
|
||||||
|
Stream_SetPosition(trackerState->currentPacket, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd == DATA_PDU || cmd == DATA_FIRST_PDU)
|
if (cmd == DATA_PDU || cmd == DATA_FIRST_PDU)
|
||||||
{
|
{
|
||||||
|
size_t extraSize = Stream_GetRemainingLength(s);
|
||||||
|
|
||||||
trackerState->CurrentDataFragments++;
|
trackerState->CurrentDataFragments++;
|
||||||
trackerState->CurrentDataReceived += Stream_GetRemainingLength(s);
|
trackerState->CurrentDataReceived += extraSize;
|
||||||
|
|
||||||
|
if (dynChannel->packetReassembly)
|
||||||
|
{
|
||||||
|
if (!trackerState->currentPacket)
|
||||||
|
{
|
||||||
|
trackerState->currentPacket = Stream_New(NULL, 1024);
|
||||||
|
if (!trackerState->currentPacket)
|
||||||
|
{
|
||||||
|
WLog_ERR(TAG, "unable to create current packet");
|
||||||
|
return PF_CHANNEL_RESULT_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Stream_EnsureRemainingCapacity(trackerState->currentPacket, extraSize))
|
||||||
|
{
|
||||||
|
WLog_ERR(TAG, "unable to grow current packet");
|
||||||
|
return PF_CHANNEL_RESULT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream_Write(trackerState->currentPacket, Stream_Pointer(s), extraSize);
|
||||||
|
}
|
||||||
WLog_DBG(TAG,
|
WLog_DBG(TAG,
|
||||||
"DynvcTracker(%s [%s]): %s frags=%" PRIu32 " received=%" PRIu32 "(%" PRIu32 ")",
|
"DynvcTracker(%s [%s]): %s frags=%" PRIu32 " received=%" PRIu32 "(%" PRIu32 ")",
|
||||||
dynChannel->channel_name, get_packet_type(cmd), direction,
|
dynChannel->channelName, get_packet_type(cmd), direction,
|
||||||
trackerState->CurrentDataFragments, trackerState->CurrentDataReceived,
|
trackerState->CurrentDataFragments, trackerState->CurrentDataReceived,
|
||||||
trackerState->currentDataLength);
|
trackerState->currentDataLength);
|
||||||
}
|
}
|
||||||
@ -432,7 +486,7 @@ static PfChannelResult DynvcTrackerPeekFn(ChannelStateTracker* tracker, BOOL fir
|
|||||||
WLog_ERR(TAG,
|
WLog_ERR(TAG,
|
||||||
"DynvcTracker (%s [%s]): reassembled packet (%" PRIu32
|
"DynvcTracker (%s [%s]): reassembled packet (%" PRIu32
|
||||||
") is bigger than announced length (%" PRIu32 ")",
|
") is bigger than announced length (%" PRIu32 ")",
|
||||||
dynChannel->channel_name, get_packet_type(cmd),
|
dynChannel->channelName, get_packet_type(cmd),
|
||||||
trackerState->CurrentDataReceived, trackerState->currentDataLength);
|
trackerState->CurrentDataReceived, trackerState->currentDataLength);
|
||||||
return PF_CHANNEL_RESULT_ERROR;
|
return PF_CHANNEL_RESULT_ERROR;
|
||||||
}
|
}
|
||||||
@ -444,27 +498,47 @@ static PfChannelResult DynvcTrackerPeekFn(ChannelStateTracker* tracker, BOOL fir
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackerState->CurrentDataReceived == trackerState->currentDataLength)
|
PfChannelResult result;
|
||||||
|
switch (dynChannel->channelMode)
|
||||||
|
{
|
||||||
|
case PF_UTILS_CHANNEL_PASSTHROUGH:
|
||||||
|
result = channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
|
||||||
|
break;
|
||||||
|
case PF_UTILS_CHANNEL_BLOCK:
|
||||||
|
tracker->mode = 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_ERR(TAG, "no intercept callback for channel %s(fromBack=%d), dropping packet",
|
||||||
|
dynChannel->channelName, isBackData);
|
||||||
|
result = PF_CHANNEL_RESULT_DROP;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
WLog_ERR(TAG, "unknown channel mode %d", dynChannel->channelMode);
|
||||||
|
result = PF_CHANNEL_RESULT_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!trackerState->currentDataLength ||
|
||||||
|
(trackerState->CurrentDataReceived == trackerState->currentDataLength))
|
||||||
{
|
{
|
||||||
trackerState->currentDataLength = 0;
|
trackerState->currentDataLength = 0;
|
||||||
trackerState->CurrentDataFragments = 0;
|
trackerState->CurrentDataFragments = 0;
|
||||||
trackerState->CurrentDataReceived = 0;
|
trackerState->CurrentDataReceived = 0;
|
||||||
|
|
||||||
|
if (dynChannel->packetReassembly && trackerState->currentPacket)
|
||||||
|
Stream_SetPosition(trackerState->currentPacket, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (dynChannel->channelMode)
|
return result;
|
||||||
{
|
|
||||||
case PF_UTILS_CHANNEL_PASSTHROUGH:
|
|
||||||
return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
|
|
||||||
case PF_UTILS_CHANNEL_BLOCK:
|
|
||||||
tracker->mode = CHANNEL_TRACKER_DROP;
|
|
||||||
return PF_CHANNEL_RESULT_DROP;
|
|
||||||
case PF_UTILS_CHANNEL_INTERCEPT:
|
|
||||||
WLog_DBG(TAG, "TODO: implement intercepted dynamic channel");
|
|
||||||
return PF_CHANNEL_RESULT_DROP;
|
|
||||||
default:
|
|
||||||
WLog_ERR(TAG, "unknown channel mode");
|
|
||||||
return PF_CHANNEL_RESULT_ERROR;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DynChannelContext_free(void* context)
|
static void DynChannelContext_free(void* context)
|
||||||
|
Loading…
Reference in New Issue
Block a user