diff --git a/include/freerdp/server/proxy/proxy_context.h b/include/freerdp/server/proxy/proxy_context.h index b1ffb93ab..dcf89efee 100644 --- a/include/freerdp/server/proxy/proxy_context.h +++ b/include/freerdp/server/proxy/proxy_context.h @@ -36,6 +36,7 @@ extern "C" typedef struct proxy_data proxyData; typedef struct proxy_module proxyModule; + typedef struct p_server_channel_context pServerChannelContext; typedef struct s_InterceptContextMapEntry { @@ -49,21 +50,48 @@ extern "C" /** @brief how is handled a channel */ typedef enum { - PF_UTILS_CHANNEL_NOT_HANDLED, - PF_UTILS_CHANNEL_BLOCK, - PF_UTILS_CHANNEL_PASSTHROUGH, - PF_UTILS_CHANNEL_INTERCEPT, + PF_UTILS_CHANNEL_NOT_HANDLED, /*!< channel not handled */ + PF_UTILS_CHANNEL_BLOCK, /*!< block and drop traffic on this channel */ + PF_UTILS_CHANNEL_PASSTHROUGH, /*!< pass traffic from this channel */ + PF_UTILS_CHANNEL_INTERCEPT, /*!< inspect traffic from this channel */ } pf_utils_channel_mode; + /** @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 */ + } PfChannelOpenStatus; + + #define PF_DYNAMIC_CHANNEL_MASK 0xFFFF000000000000 + + /** @brief result of a channel treatment */ + typedef enum + { + PF_CHANNEL_RESULT_PASS, /*!< pass the packet as is */ + PF_CHANNEL_RESULT_DROP, /*!< drop the packet */ + PF_CHANNEL_RESULT_ERROR /*!< error during packet treatment */ + } PfChannelResult; + + typedef PfChannelResult (*proxyChannelDataFn)(proxyData* pdata, const pServerChannelContext* channel, + const BYTE* xdata, size_t xsize, UINT32 flags, + size_t totalSizepServer); + typedef void (*proxyChannelContextDtor)(void *context); + /** @brief per channel configuration */ struct p_server_channel_context { char* channel_name; - UINT32 channel_id; + UINT64 channel_id; + PfChannelOpenStatus openStatus; BOOL isDynamic; pf_utils_channel_mode channelMode; + proxyChannelDataFn onFrontData; + proxyChannelDataFn onBackData; + proxyChannelContextDtor contextDtor; + void *context; }; - typedef struct p_server_channel_context pServerChannelContext; void ChannelContext_free(pServerChannelContext* ctx); @@ -84,7 +112,7 @@ extern "C" }; typedef struct p_server_context pServerContext; - pServerChannelContext* ChannelContext_new(pServerContext* ps, const char* name, UINT32 id); + pServerChannelContext* ChannelContext_new(pServerContext* ps, const char* name, UINT64 id); /** * Wraps rdpContext and holds the state for the proxy's client. diff --git a/libfreerdp/core/autodetect.c b/libfreerdp/core/autodetect.c index d8318af05..bd09fd0e2 100644 --- a/libfreerdp/core/autodetect.c +++ b/libfreerdp/core/autodetect.c @@ -141,7 +141,7 @@ static BOOL autodetect_send_rtt_measure_response(rdpRdp* rdp, UINT16 sequenceNum if (!s) return FALSE; - WLog_VRB(AUTODETECT_TAG, "sending RTT Measure Response PDU"); + WLog_VRB(AUTODETECT_TAG, "sending RTT Measure Response PDU (seqNumber=0x%"PRIx16")", sequenceNumber); Stream_Write_UINT8(s, 0x06); /* headerLength (1 byte) */ Stream_Write_UINT8(s, TYPE_ID_AUTODETECT_RESPONSE); /* headerTypeId (1 byte) */ Stream_Write_UINT16(s, sequenceNumber); /* sequenceNumber (2 bytes) */ @@ -158,7 +158,7 @@ static BOOL autodetect_send_bandwidth_measure_start(rdpContext* context, UINT16 if (!s) return FALSE; - WLog_VRB(AUTODETECT_TAG, "sending Bandwidth Measure Start PDU"); + WLog_VRB(AUTODETECT_TAG, "sending Bandwidth Measure Start PDU(seqNumber=%"PRIu16")", sequenceNumber); Stream_Write_UINT8(s, 0x06); /* headerLength (1 byte) */ Stream_Write_UINT8(s, TYPE_ID_AUTODETECT_REQUEST); /* headerTypeId (1 byte) */ Stream_Write_UINT16(s, sequenceNumber); /* sequenceNumber (2 bytes) */ diff --git a/server/proxy/CMakeLists.txt b/server/proxy/CMakeLists.txt index 92f8f16bc..401a46e0d 100644 --- a/server/proxy/CMakeLists.txt +++ b/server/proxy/CMakeLists.txt @@ -25,6 +25,8 @@ set(MODULE_PREFIX "FREERDP_SERVER_PROXY") set(${MODULE_PREFIX}_SRCS pf_context.c + pf_channel.c + pf_channel.h pf_client.c pf_client.h pf_input.c diff --git a/server/proxy/channels/CMakeLists.txt b/server/proxy/channels/CMakeLists.txt index e5df5f702..1915d83d2 100644 --- a/server/proxy/channels/CMakeLists.txt +++ b/server/proxy/channels/CMakeLists.txt @@ -3,6 +3,8 @@ set(MODULE_NAME pf_channels) set(SOURCES pf_channel_rdpdr.c pf_channel_rdpdr.h + pf_channel_drdynvc.c + pf_channel_drdynvc.h ) if (WITH_PROXY_EMULATE_SMARTCARD) diff --git a/server/proxy/channels/pf_channel_drdynvc.c b/server/proxy/channels/pf_channel_drdynvc.c new file mode 100644 index 000000000..cc1389156 --- /dev/null +++ b/server/proxy/channels/pf_channel_drdynvc.c @@ -0,0 +1,418 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * pf_channel_drdynvc + * + * Copyright 2022 David Fort + * + * 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 +#include + +#include "pf_channel_drdynvc.h" +#include "../pf_channel.h" +#include "../proxy_modules.h" + + +#define TAG PROXY_TAG("drdynvc") + +/** @brief tracker state for a drdynvc stream */ +typedef struct +{ + ChannelStateTracker* tracker; + UINT32 currentDataLength; + UINT32 CurrentDataReceived; + UINT32 CurrentDataFragments; +} DynChannelTrackerState; + +/** @brief context for the dynamic channel */ +typedef struct +{ + DynChannelTrackerState backTracker; + DynChannelTrackerState frontTracker; +} 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 DynvcReadResult dynvc_read_varInt(wStream* s, size_t len, UINT64* varInt, BOOL last) +{ + switch (len) + { + case 0x00: + if (Stream_GetRemainingLength(s) < 1) + return last ? DYNCVC_READ_ERROR : DYNCVC_READ_INCOMPLETE; + Stream_Read_UINT8(s, *varInt); + break; + case 0x01: + if (Stream_GetRemainingLength(s) < 2) + return last ? DYNCVC_READ_ERROR : DYNCVC_READ_INCOMPLETE; + Stream_Read_UINT16(s, *varInt); + break; + case 0x02: + if (Stream_GetRemainingLength(s) < 4) + return last ? DYNCVC_READ_ERROR : DYNCVC_READ_INCOMPLETE; + Stream_Read_UINT32(s, *varInt); + break; + case 0x03: + default: + WLog_ERR(TAG, "Unknown int len %d", len); + return DYNCVC_READ_ERROR; + } + return DYNCVC_READ_OK; +} + + +PfChannelResult DynvcTrackerPeekFn(ChannelStateTracker* tracker, BOOL firstPacket, BOOL lastPacket) +{ + BYTE cmd, byte0; + wStream *s, sbuffer; + BOOL haveChannelId; + BOOL haveLength; + UINT64 dynChannelId = 0; + UINT64 Length = 0; + pServerChannelContext* dynChannel; + DynChannelContext* dynChannelContext = (DynChannelContext*)tracker->trackerData; + BOOL isBackData = (tracker == dynChannelContext->backTracker.tracker); + DynChannelTrackerState* trackerState = isBackData ? &dynChannelContext->backTracker : &dynChannelContext->frontTracker; + UINT32 flags = lastPacket ? CHANNEL_FLAG_LAST : 0; + proxyData* pdata = tracker->pdata; + const char* direction = isBackData ? "B->F" : "F->B"; + + s = Stream_StaticConstInit(&sbuffer, Stream_Buffer(tracker->currentPacket), Stream_GetPosition(tracker->currentPacket)); + if (Stream_Length(s) < 1) + return DYNCVC_READ_INCOMPLETE; + + 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) + { + UINT64 maskedDynChannelId; + BYTE cbId = byte0 & 0x03; + + switch (dynvc_read_varInt(s, cbId, &dynChannelId, lastPacket)) + { + case DYNCVC_READ_OK: + break; + case DYNCVC_READ_INCOMPLETE: + return PF_CHANNEL_RESULT_DROP; + case DYNCVC_READ_ERROR: + default: + WLog_ERR(TAG, "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 + */ + maskedDynChannelId = dynChannelId | PF_DYNAMIC_CHANNEL_MASK; + dynChannel = (pServerChannelContext*)HashTable_GetItemValue(pdata->ps->channelsById, &maskedDynChannelId); + + 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 */ + tracker->mode = CHANNEL_TRACKER_DROP; + return PF_CHANNEL_RESULT_DROP; + } + } + } + + if (haveLength) + { + BYTE lenLen = (byte0 >> 2) & 0x03; + switch (dynvc_read_varInt(s, lenLen, &Length, lastPacket)) + { + case DYNCVC_READ_OK: + break; + case DYNCVC_READ_INCOMPLETE: + return PF_CHANNEL_RESULT_DROP; + case DYNCVC_READ_ERROR: + default: + WLog_ERR(TAG, "DynvcTrackerPeekFn: invalid length field"); + return PF_CHANNEL_RESULT_ERROR; + } + } + + switch (cmd) + { + case CAPABILITY_REQUEST_PDU: + WLog_DBG(TAG, "DynvcTracker: %s CAPABILITY_%s", direction, isBackData ? "REQUEST" : "RESPONSE"); + tracker->mode = CHANNEL_TRACKER_PASS; + return PF_CHANNEL_RESULT_PASS; + + case CREATE_REQUEST_PDU: + { + UINT32 creationStatus; + + /* we only want the full packet */ + if (!lastPacket) + return PF_CHANNEL_RESULT_DROP; + + if (isBackData) + { + proxyChannelDataEventInfo dev; + size_t len; + const char* name = (const char*)Stream_Pointer(s); + size_t nameLen = Stream_GetRemainingLength(s); + + len = strnlen(name, nameLen); + if ((len == 0) || (len == nameLen)) + return PF_CHANNEL_RESULT_ERROR; + + dev.channel_id = dynChannelId; + dev.channel_name = name; + dev.data = Stream_Buffer(s); + dev.data_len = Stream_GetPosition(tracker->currentPacket); + dev.flags = flags; + dev.total_size = Stream_GetPosition(tracker->currentPacket); + + if (!pf_modules_run_filter( + pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE, pdata, &dev)) + return PF_CHANNEL_RESULT_DROP; /* Silently drop */ + + if (!dynChannel) + { + dynChannel = ChannelContext_new(pdata->ps, name, dynChannelId | PF_DYNAMIC_CHANNEL_MASK); + if (!dynChannel) + { + WLog_ERR(TAG, "unable to create dynamic channel context data"); + return PF_CHANNEL_RESULT_ERROR; + } + dynChannel->isDynamic = TRUE; + + if (!HashTable_Insert(pdata->ps->channelsById, &dynChannel->channel_id, dynChannel)) + { + WLog_ERR(TAG, "unable register dynamic channel context data"); + ChannelContext_free(dynChannel); + return PF_CHANNEL_RESULT_ERROR; + } + } + dynChannel->openStatus = CHANNEL_OPENSTATE_WAITING_OPEN_STATUS; + + return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, FALSE); + } + + /* CREATE_REQUEST_PDU response */ + if (Stream_GetRemainingLength(s) < 4) + return PF_CHANNEL_RESULT_ERROR; + + Stream_Read_INT32(s, creationStatus); + WLog_DBG(TAG, "DynvcTracker(%"PRIu16",%s): %s CREATE_RESPONSE openStatus=%"PRIu32, dynChannelId, + dynChannel->channel_name, direction, creationStatus); + + if (creationStatus != 0) + { + /* we remove it from the channels map, as it happens that server reused channel ids when + * the channel can't be opened + */ + HashTable_Remove(pdata->ps->channelsById, &dynChannel->channel_id); + } + else + { + dynChannel->openStatus = CHANNEL_OPENSTATE_OPENED; + } + + return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, TRUE); + } + + case CLOSE_REQUEST_PDU: + if (!lastPacket) + return PF_CHANNEL_RESULT_DROP; + + WLog_DBG(TAG, "DynvcTracker(%s): %s Close request on channel", dynChannel->channel_name, direction); + tracker->mode = CHANNEL_TRACKER_PASS; + 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_DBG(TAG, "SOFT_SYNC_REQUEST_PDU"); + tracker->mode = 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_DBG(TAG, "SOFT_SYNC_RESPONSE_PDU"); + tracker->mode = CHANNEL_TRACKER_PASS; + return PF_CHANNEL_RESULT_PASS; + + case DATA_FIRST_PDU: + case DATA_PDU: + /* treat these below */ + break; + + case DATA_FIRST_COMPRESSED_PDU: + case DATA_COMPRESSED_PDU: + WLog_DBG(TAG, "TODO: compressed data packets, pass them as is for now"); + tracker->mode = CHANNEL_TRACKER_PASS; + return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData); + + default: + return PF_CHANNEL_RESULT_ERROR; + } + + if ((cmd == DATA_FIRST_PDU) || (cmd == DATA_FIRST_COMPRESSED_PDU)) + { + WLog_DBG(TAG, "DynvcTracker(%s): %s DATA_FIRST currentPacketLength=%d", dynChannel->channel_name, direction, Length); + trackerState->currentDataLength = Length; + trackerState->CurrentDataReceived = 0; + trackerState->CurrentDataFragments = 0; + } + + if (cmd == DATA_PDU || cmd == DATA_FIRST_PDU) + { + trackerState->CurrentDataFragments++; + trackerState->CurrentDataReceived += Stream_GetRemainingLength(s); + WLog_DBG(TAG, "DynvcTracker(%s): %s %s frags=%d received=%d(%d)", dynChannel->channel_name, direction, + cmd == DATA_PDU ? "DATA" : "DATA_FIRST", + trackerState->CurrentDataFragments, trackerState->CurrentDataReceived, + trackerState->currentDataLength); + } + + if (cmd == DATA_PDU) + { + if (trackerState->currentDataLength) + { + if (trackerState->CurrentDataReceived > trackerState->currentDataLength) + { + WLog_ERR(TAG, "DynvcTracker: reassembled packet (%d) is bigger than announced length (%d)", trackerState->CurrentDataReceived, trackerState->currentDataLength); + return PF_CHANNEL_RESULT_ERROR; + } + + if (trackerState->CurrentDataReceived == trackerState->currentDataLength) + { + trackerState->currentDataLength = 0; + trackerState->CurrentDataFragments = 0; + trackerState->CurrentDataReceived = 0; + } + } + else + { + trackerState->CurrentDataFragments = 0; + trackerState->CurrentDataReceived = 0; + } + } + + if (dynChannel->openStatus != CHANNEL_OPENSTATE_OPENED) + { + WLog_ERR(TAG, "DynvcTracker(%s): channel is not opened", dynChannel->channel_name); + return PF_CHANNEL_RESULT_ERROR; + } + + switch(dynChannel->channelMode) + { + 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 DynChannelContext* DynChannelContext_new(proxyData* pdata, pServerChannelContext* channel) +{ + DynChannelContext* dyn = calloc(1, sizeof(DynChannelContext)); + if (!dyn) + return FALSE; + + dyn->backTracker.tracker = channelTracker_new(channel, DynvcTrackerPeekFn, dyn); + if (!dyn->backTracker.tracker) + goto out_fromClientTracker; + dyn->backTracker.tracker->pdata = pdata; + + dyn->frontTracker.tracker = channelTracker_new(channel, DynvcTrackerPeekFn, dyn); + if (!dyn->frontTracker.tracker) + goto out_fromServerTracker; + dyn->frontTracker.tracker->pdata = pdata; + + return dyn; + +out_fromServerTracker: + channelTracker_free(dyn->backTracker.tracker); +out_fromClientTracker: + free(dyn); + return NULL; +} + +static void DynChannelContext_free(DynChannelContext* c) +{ + channelTracker_free(c->backTracker.tracker); + channelTracker_free(c->frontTracker.tracker); + free(c); +} + +static PfChannelResult pf_dynvc_back_data(proxyData* pdata, const pServerChannelContext* channel, + const BYTE* xdata, size_t xsize, UINT32 flags, + size_t totalSize) +{ + DynChannelContext* dyn = (DynChannelContext*)channel->context; + return channelTracker_update(dyn->backTracker.tracker, xdata, xsize, flags, totalSize); +} + +static PfChannelResult pf_dynvc_front_data(proxyData* pdata, const pServerChannelContext* channel, + const BYTE* xdata, size_t xsize, UINT32 flags, + size_t totalSize) +{ + DynChannelContext* dyn = (DynChannelContext*)channel->context; + return channelTracker_update(dyn->frontTracker.tracker, xdata, xsize, flags, totalSize); +} + + +BOOL pf_channel_setup_drdynvc(proxyData* pdata, pServerChannelContext* 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 = (proxyChannelContextDtor)DynChannelContext_free; + channel->context = ret; + return TRUE; +} diff --git a/server/proxy/channels/pf_channel_drdynvc.h b/server/proxy/channels/pf_channel_drdynvc.h new file mode 100644 index 000000000..61f16928e --- /dev/null +++ b/server/proxy/channels/pf_channel_drdynvc.h @@ -0,0 +1,27 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * pf_channel_drdynvc + * + * Copyright 2022 David Fort + * + * 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. + */ +#ifndef SERVER_PROXY_CHANNELS_PF_CHANNEL_DRDYNVC_H_ +#define SERVER_PROXY_CHANNELS_PF_CHANNEL_DRDYNVC_H_ + +#include + + +BOOL pf_channel_setup_drdynvc(proxyData* pdata, pServerChannelContext* channel); + +#endif /* SERVER_PROXY_CHANNELS_PF_CHANNEL_DRDYNVC_H_ */ diff --git a/server/proxy/channels/pf_channel_rdpdr.c b/server/proxy/channels/pf_channel_rdpdr.c index c96e89ab0..34305b6c4 100644 --- a/server/proxy/channels/pf_channel_rdpdr.c +++ b/server/proxy/channels/pf_channel_rdpdr.c @@ -1715,3 +1715,42 @@ BOOL pf_channel_rdpdr_client_reset(pClientContext* pc) return TRUE; } + +static PfChannelResult pf_rdpdr_back_data(proxyData* pdata, const pServerChannelContext* channel, + const BYTE* xdata, size_t xsize, UINT32 flags, + size_t totalSize) +{ + if (!pf_channel_rdpdr_client_handle(pdata->pc, channel->channel_id, channel->channel_name, xdata, xsize, flags, totalSize)) + { + WLog_ERR(TAG, "error treating client back data"); + return PF_CHANNEL_RESULT_ERROR; + } + return PF_CHANNEL_RESULT_PASS; +} + +static PfChannelResult pf_rdpdr_front_data(proxyData* pdata, const pServerChannelContext* channel, + const BYTE* xdata, size_t xsize, UINT32 flags, + size_t totalSize) +{ + if (!pf_channel_rdpdr_server_handle(pdata->ps, channel->channel_id, channel->channel_name, xdata, xsize, flags, totalSize)) + { + WLog_ERR(TAG, "error treating front data"); + return PF_CHANNEL_RESULT_ERROR; + + } + return PF_CHANNEL_RESULT_PASS; +} + +BOOL pf_channel_setup_rdpdr(pServerContext* ps, pServerChannelContext* channel) +{ + channel->onBackData = pf_rdpdr_back_data; + channel->onFrontData = pf_rdpdr_front_data; + + if (!pf_channel_rdpdr_server_new(ps)) + return FALSE; + if (!pf_channel_rdpdr_server_announce(ps)) + return FALSE; + + return TRUE; +} + diff --git a/server/proxy/pf_channel.c b/server/proxy/pf_channel.c new file mode 100644 index 000000000..85fbb36e7 --- /dev/null +++ b/server/proxy/pf_channel.c @@ -0,0 +1,234 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Copyright 2022 David Fort + * + * 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 +#include + +#include "proxy_modules.h" +#include "pf_channel.h" + +#define TAG PROXY_TAG("channel") + +ChannelStateTracker* channelTracker_new(pServerChannelContext* channel, ChannelTrackerPeekFn fn, void* data) +{ + ChannelStateTracker* ret = calloc(1, sizeof(*ret)); + if (!ret) + return ret; + + ret->channel = channel; + ret->peekFn = fn; + ret->trackerData = data; + ret->currentPacket = Stream_New(NULL, 10 * 1024); + if (!ret->currentPacket) + { + free(ret); + return NULL; + } + return ret; +} + +PfChannelResult channelTracker_update(ChannelStateTracker* tracker, const BYTE* xdata, size_t xsize, UINT32 flags, + size_t totalSize) +{ + PfChannelResult result; + BOOL firstPacket = !!(flags & CHANNEL_FLAG_FIRST); + BOOL lastPacket = !!(flags & CHANNEL_FLAG_LAST); + + + WLog_VRB(TAG, "channelTracker_update(%s): sz=%d first=%d last=%d", tracker->channel->channel_name, xsize, firstPacket, lastPacket); + if (flags & CHANNEL_FLAG_FIRST) + { + /* don't keep a too big currentPacket */ + if (Stream_Capacity(tracker->currentPacket) > 1 * 1000 * 1000) + { + Stream_Free(tracker->currentPacket, TRUE); + + tracker->currentPacket = Stream_New(NULL, 10 * 1024); + if (!tracker->currentPacket) + { + return PF_CHANNEL_RESULT_ERROR; + } + } + else + { + Stream_SetPosition(tracker->currentPacket, 0); + } + + tracker->currentPacketSize = totalSize; + tracker->currentPacketReceived = 0; + tracker->currentPacketFragments = 0; + } + + if (tracker->currentPacketReceived + xsize > tracker->currentPacketSize) + WLog_INFO(TAG, "cumulated size is bigger (%d) than total size (%d)", tracker->currentPacketReceived + xsize, + tracker->currentPacketSize); + + tracker->currentPacketReceived += xsize; + tracker->currentPacketFragments++; + + + switch (tracker->mode) + { + case CHANNEL_TRACKER_PEEK: + if (!Stream_EnsureRemainingCapacity(tracker->currentPacket, xsize)) + return PF_CHANNEL_RESULT_ERROR; + + Stream_Write(tracker->currentPacket, xdata, xsize); + result = tracker->peekFn(tracker, firstPacket, lastPacket); + break; + case CHANNEL_TRACKER_PASS: + result = PF_CHANNEL_RESULT_PASS; + break; + case CHANNEL_TRACKER_DROP: + result = PF_CHANNEL_RESULT_DROP; + break; + } + + if (lastPacket) + { + tracker->mode = CHANNEL_TRACKER_PEEK; + if (tracker->currentPacketReceived != tracker->currentPacketSize) + WLog_INFO(TAG, "cumulated size(%d) does not match total size (%d)", tracker->currentPacketReceived, tracker->currentPacketSize); + } + + return result; +} + +void channelTracker_free(ChannelStateTracker* t) +{ + if (!t) + return; + + Stream_Free(t->currentPacket, TRUE); + free(t); +} + +/** + * Flushes the current accumulated tracker content, if it's the first packet, then + * when can just return that the packet shall be passed, otherwise to have to refragment + * the accumulated current packet. + */ + +PfChannelResult channelTracker_flushCurrent(ChannelStateTracker* t, BOOL first, BOOL last, BOOL toBack) +{ + proxyData* pdata; + pServerContext* ps; + pServerChannelContext* channel; + UINT32 flags = CHANNEL_FLAG_FIRST; + BOOL r; + const char* direction = toBack ? "F->B" : "B->F"; + + WLog_VRB(TAG, "channelTracker_flushCurrent(%s): %s sz=%d first=%d last=%d", t->channel->channel_name, + direction, Stream_GetPosition(t->currentPacket), first, last); + + if (first) + return PF_CHANNEL_RESULT_PASS; + + pdata = t->pdata; + channel = t->channel; + if (last) + flags |= CHANNEL_FLAG_LAST; + + if (toBack) + { + proxyChannelDataEventInfo ev; + + ev.channel_id = channel->channel_id; + ev.channel_name = channel->channel_name; + ev.data = Stream_Buffer(t->currentPacket); + ev.data_len = Stream_GetPosition(t->currentPacket); + ev.flags = flags; + ev.total_size = t->currentPacketSize; + + if (!pdata->pc->sendChannelData) + return PF_CHANNEL_RESULT_ERROR; + + return pdata->pc->sendChannelData(pdata->pc, &ev) ? PF_CHANNEL_RESULT_DROP : PF_CHANNEL_RESULT_ERROR; + } + + ps = pdata->ps; + r = ps->context.peer->SendChannelPacket(ps->context.peer, channel->channel_id, t->currentPacketSize, + flags, Stream_Buffer(t->currentPacket), Stream_GetPosition(t->currentPacket)); + + return r ? PF_CHANNEL_RESULT_DROP : PF_CHANNEL_RESULT_ERROR; +} + + + +static PfChannelResult pf_channel_generic_back_data(proxyData* pdata, const pServerChannelContext* channel, + const BYTE* xdata, size_t xsize, UINT32 flags, + size_t totalSize) +{ + proxyChannelDataEventInfo ev; + + switch(channel->channelMode) { + case PF_UTILS_CHANNEL_PASSTHROUGH: + ev.channel_id = channel->channel_id; + ev.channel_name = channel->channel_name; + ev.data = xdata; + ev.data_len = xsize; + ev.flags = flags; + ev.total_size = totalSize; + + if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA, pdata, &ev)) + return PF_CHANNEL_RESULT_DROP; /* Silently drop */ + + return PF_CHANNEL_RESULT_PASS; + + case PF_UTILS_CHANNEL_INTERCEPT: + /* TODO */ + case PF_UTILS_CHANNEL_BLOCK: + default: + return PF_CHANNEL_RESULT_DROP; + } +} + +static PfChannelResult pf_channel_generic_front_data(proxyData* pdata, const pServerChannelContext* channel, + const BYTE* xdata, size_t xsize, UINT32 flags, + size_t totalSize) +{ + proxyChannelDataEventInfo ev; + + switch(channel->channelMode) { + case PF_UTILS_CHANNEL_PASSTHROUGH: + ev.channel_id = channel->channel_id; + ev.channel_name = channel->channel_name; + ev.data = xdata; + ev.data_len = xsize; + ev.flags = flags; + ev.total_size = totalSize; + + if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA, pdata, &ev)) + return PF_CHANNEL_RESULT_DROP; /* Silently drop */ + + return PF_CHANNEL_RESULT_PASS; + + case PF_UTILS_CHANNEL_INTERCEPT: + /* TODO */ + case PF_UTILS_CHANNEL_BLOCK: + default: + return PF_CHANNEL_RESULT_DROP; + } +} + + +BOOL pf_channel_setup_generic(pServerChannelContext* channel) +{ + channel->onBackData = pf_channel_generic_back_data; + channel->onFrontData = pf_channel_generic_front_data; + return TRUE; +} diff --git a/server/proxy/pf_channel.h b/server/proxy/pf_channel.h new file mode 100644 index 000000000..9956e062b --- /dev/null +++ b/server/proxy/pf_channel.h @@ -0,0 +1,64 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * + * Copyright 2022 David Fort + * + * 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. + */ +#ifndef SERVER_PROXY_PF_CHANNEL_H_ +#define SERVER_PROXY_PF_CHANNEL_H_ + +#include + + +/** @brief operating mode of a channel tracker */ +typedef enum +{ + CHANNEL_TRACKER_PEEK, /*!< inquiring content, accumulating packet fragments */ + CHANNEL_TRACKER_PASS, /*!< pass all the fragments of the current packet */ + CHANNEL_TRACKER_DROP /*!< drop all the fragments of the current packet */ +} ChannelTrackerMode; + +typedef struct _ChannelStateTracker ChannelStateTracker; +typedef PfChannelResult (*ChannelTrackerPeekFn)(ChannelStateTracker* tracker, BOOL first, BOOL lastPacket); + +/** @brief a tracker for channel packets */ +struct _ChannelStateTracker { + pServerChannelContext* channel; + ChannelTrackerMode mode; + wStream* currentPacket; + size_t currentPacketReceived; + size_t currentPacketSize; + size_t currentPacketFragments; + + ChannelTrackerPeekFn peekFn; + void *trackerData; + proxyData* pdata; +}; + +ChannelStateTracker* channelTracker_new(pServerChannelContext* channel, ChannelTrackerPeekFn fn, void* data); + +void channelTracker_free(ChannelStateTracker* t); + +PfChannelResult channelTracker_update(ChannelStateTracker* tracker, const BYTE* xdata, size_t xsize, UINT32 flags, + size_t totalSize); + +PfChannelResult channelTracker_flushCurrent(ChannelStateTracker* t, BOOL first, BOOL last, BOOL toFront); + + +BOOL pf_channel_setup_rdpdr(pServerContext* ps, pServerChannelContext* channel); +BOOL pf_channel_setup_generic(pServerChannelContext* channel); + + +#endif /* SERVER_PROXY_PF_CHANNEL_H_ */ diff --git a/server/proxy/pf_client.c b/server/proxy/pf_client.c index cef443ce5..82a336980 100644 --- a/server/proxy/pf_client.c +++ b/server/proxy/pf_client.c @@ -36,6 +36,7 @@ #include #include "pf_client.h" +#include "pf_channel.h" #include #include "pf_update.h" #include "pf_input.h" @@ -333,7 +334,6 @@ static BOOL pf_client_pre_connect(freerdp* instance) if (!pf_channel_smartcard_client_new(pc)) return FALSE; #endif - /* Copy the current channel settings from the peer connection to the client. */ if (!freerdp_channels_from_mcs(settings, &ps->context)) return FALSE; @@ -374,126 +374,6 @@ static BOOL pf_client_pre_connect(freerdp* instance) return pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_PRE_CONNECT, pc->pdata, pc); } -static BOOL pf_client_receive_channel_passthrough(proxyData* pdata, - const pServerChannelContext* channel, - const BYTE* xdata, size_t xsize, UINT32 flags, - size_t totalSize) -{ - proxyChannelDataEventInfo ev; - UINT16 server_channel_id; - pServerContext* ps; - - WINPR_ASSERT(pdata); - - ps = pdata->ps; - WINPR_ASSERT(ps); - - ev.channel_id = channel->channel_id; - ev.channel_name = channel->channel_name; - ev.data = xdata; - ev.data_len = xsize; - ev.flags = flags; - ev.total_size = totalSize; - - if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA, pdata, - &ev)) - return TRUE; /* Silently drop */ - - /* Dynamic channels need special treatment - * - * We need to check every message with CHANNEL_FLAG_FIRST set if it - * is a CREATE_REQUEST_PDU (0x01) and extract channelId and name - * from it. - * - * To avoid issues with (misbehaving) clients assume all packets - * that do not have at least a length of 1 byte and all incomplete - * CREATE_REQUEST_PDU (0x01) packets as invalid. - */ - if ((flags & CHANNEL_FLAG_FIRST) && - (strncmp(channel->channel_name, DRDYNVC_SVC_CHANNEL_NAME, CHANNEL_NAME_LEN + 1) == 0)) - { - BYTE cmd, first; - wStream *s, sbuffer; - - s = Stream_StaticConstInit(&sbuffer, xdata, xsize); - if (Stream_Length(s) < 1) - return FALSE; - - Stream_Read_UINT8(s, first); - cmd = first >> 4; - - if (cmd == CREATE_REQUEST_PDU) - { - proxyChannelDataEventInfo dev; - size_t len, nameLen; - const char* name; - UINT32 dynChannelId; - BYTE cbId = first & 0x03; - switch (cbId) - { - case 0x00: - if (Stream_GetRemainingLength(s) < 1) - return FALSE; - Stream_Read_UINT8(s, dynChannelId); - break; - case 0x01: - if (Stream_GetRemainingLength(s) < 2) - return FALSE; - Stream_Read_UINT16(s, dynChannelId); - break; - case 0x02: - if (Stream_GetRemainingLength(s) < 4) - return FALSE; - Stream_Read_UINT32(s, dynChannelId); - break; - default: - return FALSE; - } - - name = (const char*)Stream_Pointer(s); - nameLen = Stream_GetRemainingLength(s); - len = strnlen(name, nameLen); - if ((len == 0) || (len == nameLen)) - return FALSE; - dev.channel_id = dynChannelId; - dev.channel_name = name; - dev.data = xdata; - dev.data_len = xsize; - dev.flags = flags; - dev.total_size = totalSize; - - if (!pf_modules_run_filter( - pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE, pdata, &dev)) - return TRUE; /* Silently drop */ - } - } - server_channel_id = WTSChannelGetId(ps->context.peer, channel->channel_name); - - /* Ignore messages for channels that can not be mapped. - * The client might not have enabled support for this specific channel, - * so just drop the message. */ - if (server_channel_id == 0) - return TRUE; - return ps->context.peer->SendChannelPacket(ps->context.peer, server_channel_id, totalSize, - flags, xdata, xsize); -} - -static BOOL pf_client_receive_channel_intercept(proxyData* pdata, UINT16 channelId, - const char* channel_name, const BYTE* xdata, - size_t xsize, UINT32 flags, size_t totalSize) -{ - WINPR_ASSERT(pdata); - WINPR_ASSERT(channel_name); - - if (strcmp(channel_name, RDPDR_SVC_CHANNEL_NAME) == 0) - return pf_channel_rdpdr_client_handle(pdata->pc, channelId, channel_name, xdata, xsize, - flags, totalSize); - - WLog_ERR(TAG, "[%s]: Channel %s [0x%04" PRIx16 "] intercept mode not implemented, aborting", - __FUNCTION__, channel_name, channelId); - return FALSE; -} - static BOOL pf_client_receive_channel_data_hook(freerdp* instance, UINT16 channelId, const BYTE* xdata, size_t xsize, UINT32 flags, size_t totalSize) @@ -502,7 +382,8 @@ static BOOL pf_client_receive_channel_data_hook(freerdp* instance, UINT16 channe pServerContext* ps; proxyData* pdata; pServerChannelContext* channel; - UINT32 channelId32 = channelId; + UINT16 server_channel_id; + UINT64 channelId64 = channelId; WINPR_ASSERT(instance); WINPR_ASSERT(xdata || (xsize == 0)); @@ -517,26 +398,29 @@ static BOOL pf_client_receive_channel_data_hook(freerdp* instance, UINT16 channe pdata = ps->pdata; WINPR_ASSERT(pdata); - channel = HashTable_GetItemValue(ps->channelsById, &channelId32); + channel = HashTable_GetItemValue(ps->channelsById, &channelId64); if (!channel) return TRUE; - switch (channel->channelMode) + switch (channel->onBackData(pdata, channel, xdata, xsize, flags, totalSize)) { - case PF_UTILS_CHANNEL_BLOCK: - return TRUE; /* Silently drop */ - case PF_UTILS_CHANNEL_PASSTHROUGH: - return pf_client_receive_channel_passthrough(pdata, channel, xdata, xsize, flags, - totalSize); - case PF_UTILS_CHANNEL_INTERCEPT: - return pf_client_receive_channel_intercept(pdata, channelId, channel->channel_name, - xdata, xsize, flags, totalSize); - case PF_UTILS_CHANNEL_NOT_HANDLED: - default: - WINPR_ASSERT(pc->client_receive_channel_data_original); - return pc->client_receive_channel_data_original(instance, channelId, xdata, xsize, - flags, totalSize); + case PF_CHANNEL_RESULT_PASS: + break; + case PF_CHANNEL_RESULT_DROP: + return TRUE; + case PF_CHANNEL_RESULT_ERROR: + return FALSE; } + + server_channel_id = WTSChannelGetId(ps->context.peer, channel->channel_name); + + /* Ignore messages for channels that can not be mapped. + * The client might not have enabled support for this specific channel, + * so just drop the message. */ + if (server_channel_id == 0) + return TRUE; + + return ps->context.peer->SendChannelPacket(ps->context.peer, server_channel_id, totalSize, flags, xdata, xsize); } static BOOL pf_client_on_server_heartbeat(freerdp* instance, BYTE period, BYTE count1, BYTE count2) diff --git a/server/proxy/pf_config.c b/server/proxy/pf_config.c index 798930682..3aa058896 100644 --- a/server/proxy/pf_config.c +++ b/server/proxy/pf_config.c @@ -960,7 +960,6 @@ static BOOL config_plugin_dynamic_channel_create(proxyPlugin* plugin, proxyData* accept = TRUE; break; case PF_UTILS_CHANNEL_BLOCK: - case PF_UTILS_CHANNEL_NOT_HANDLED: default: accept = FALSE; break; @@ -1028,7 +1027,6 @@ static BOOL config_plugin_channel_create(proxyPlugin* plugin, proxyData* pdata, accept = TRUE; break; case PF_UTILS_CHANNEL_BLOCK: - case PF_UTILS_CHANNEL_NOT_HANDLED: default: accept = FALSE; break; diff --git a/server/proxy/pf_context.c b/server/proxy/pf_context.c index f1be7a6b0..0aaf97119 100644 --- a/server/proxy/pf_context.c +++ b/server/proxy/pf_context.c @@ -37,16 +37,16 @@ static UINT32 ChannelId_Hash(const void* key) { - const UINT32* v = (const UINT32*)key; - return *v; + const UINT64* v = (const UINT64*)key; + return (*v & 0xFFFFFFFF) + (*v >> 32); } -static BOOL ChannelId_Compare(const UINT32* v1, const UINT32* v2) +static BOOL ChannelId_Compare(const UINT64* v1, const UINT64* v2) { return (*v1 == *v2); } -pServerChannelContext* ChannelContext_new(pServerContext* ps, const char* name, UINT32 id) +pServerChannelContext* ChannelContext_new(pServerContext* ps, const char* name, UINT64 id) { pServerChannelContext* ret = calloc(1, sizeof(*ret)); if (!ret) @@ -55,6 +55,7 @@ pServerChannelContext* ChannelContext_new(pServerContext* ps, const char* name, return NULL; } + ret->openStatus = CHANNEL_OPENSTATE_OPENED; ret->channel_id = id; ret->channel_name = _strdup(name); if (!ret->channel_name) @@ -73,6 +74,8 @@ void ChannelContext_free(pServerChannelContext* ctx) if (!ctx) return; + IFCALL(ctx->contextDtor,ctx->context); + free(ctx->channel_name); free(ctx); } diff --git a/server/proxy/pf_server.c b/server/proxy/pf_server.c index f6b670bef..bf0cce546 100644 --- a/server/proxy/pf_server.c +++ b/server/proxy/pf_server.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -44,12 +45,14 @@ #include #include "pf_server.h" +#include "pf_channel.h" #include #include "pf_client.h" #include #include "pf_update.h" #include "proxy_modules.h" #include "pf_utils.h" +#include "channels/pf_channel_drdynvc.h" #include "channels/pf_channel_rdpdr.h" #define TAG PROXY_TAG("server") @@ -194,7 +197,7 @@ static BOOL pf_server_setup_channels(freerdp_peer* peer) const char* cname = accepted_channels[i]; UINT16 channelId = WTSChannelGetId(peer, cname); - PROXY_LOG_INFO(TAG, ps, "Accepted channel: %s", cname); + PROXY_LOG_INFO(TAG, ps, "Accepted channel: %s (%d)", cname, channelId); channelContext = ChannelContext_new(ps, cname, channelId); if (!channelContext) { @@ -202,6 +205,34 @@ static BOOL pf_server_setup_channels(freerdp_peer* peer) return FALSE; } + if (strcmp(cname, DRDYNVC_SVC_CHANNEL_NAME) == 0) + { + if (!pf_channel_setup_drdynvc(ps->pdata, channelContext)) + { + PROXY_LOG_ERR(TAG, ps, "error while setting up dynamic channel"); + ChannelContext_free(channelContext); + return FALSE; + } + } + else if (strcmp(cname, RDPDR_SVC_CHANNEL_NAME) == 0 && (channelContext->channelMode == PF_UTILS_CHANNEL_INTERCEPT)) + { + if (!pf_channel_setup_rdpdr(ps, channelContext)) + { + PROXY_LOG_ERR(TAG, ps, "error while setting up redirection channel"); + ChannelContext_free(channelContext); + return FALSE; + } + } + else + { + if (!pf_channel_setup_generic(channelContext)) + { + PROXY_LOG_ERR(TAG, ps, "error while setting up generic channel"); + ChannelContext_free(channelContext); + return FALSE; + } + } + if (!HashTable_Insert(byId, &channelContext->channel_id, channelContext)) { ChannelContext_free(channelContext); @@ -273,18 +304,6 @@ static BOOL pf_server_post_connect(freerdp_peer* peer) if (!pf_modules_run_hook(pdata->module, HOOK_TYPE_SERVER_POST_CONNECT, pdata, peer)) return FALSE; - switch (pf_utils_get_channel_mode(ps->pdata->config, RDPDR_SVC_CHANNEL_NAME)) - { - case PF_UTILS_CHANNEL_INTERCEPT: - if (!pf_channel_rdpdr_server_new(ps)) - return FALSE; - if (!pf_channel_rdpdr_server_announce(ps)) - return FALSE; - break; - default: - break; - } - /* Start a proxy's client in it's own thread */ if (!(pdata->client_thread = CreateThread(NULL, 0, pf_client_start, pc, 0, NULL))) { @@ -349,22 +368,6 @@ static BOOL pf_server_adjust_monitor_layout(freerdp_peer* peer) return TRUE; } -static BOOL pf_server_receive_channel_intercept(proxyData* pdata, UINT16 channelId, - const char* channel_name, const BYTE* data, - size_t size, UINT32 flags, size_t totalSize) -{ - WINPR_ASSERT(pdata); - WINPR_ASSERT(channel_name); - - if (strcmp(channel_name, RDPDR_SVC_CHANNEL_NAME) == 0) - return pf_channel_rdpdr_server_handle(pdata->ps, channelId, channel_name, data, size, flags, - totalSize); - - WLog_ERR(TAG, "[%s]: Channel %s [0x%04" PRIx16 "] intercept mode not implemented, aborting", - __FUNCTION__, channel_name, channelId); - return FALSE; -} - static BOOL pf_server_receive_channel_data_hook(freerdp_peer* peer, UINT16 channelId, const BYTE* data, size_t size, UINT32 flags, size_t totalSize) @@ -374,7 +377,7 @@ static BOOL pf_server_receive_channel_data_hook(freerdp_peer* peer, UINT16 chann proxyData* pdata; const proxyConfig* config; const pServerChannelContext* channel; - UINT32 channelId32 = channelId; + UINT64 channelId64 = channelId; WINPR_ASSERT(peer); @@ -395,40 +398,32 @@ static BOOL pf_server_receive_channel_data_hook(freerdp_peer* peer, UINT16 chann if (!pc) goto original_cb; - channel = HashTable_GetItemValue(ps->channelsById, &channelId32); + channel = HashTable_GetItemValue(ps->channelsById, &channelId64); if (!channel) { - PROXY_LOG_ERR(TAG, ps, "channel id=%d not registered here, dropping", channelId32); + PROXY_LOG_ERR(TAG, ps, "channel id=%d not registered here, dropping", channelId64); return TRUE; } - switch (channel->channelMode) + switch (channel->onFrontData(pdata, channel, data, size, flags, totalSize)) { - case PF_UTILS_CHANNEL_BLOCK: - return TRUE; - case PF_UTILS_CHANNEL_PASSTHROUGH: - { - proxyChannelDataEventInfo ev; + case PF_CHANNEL_RESULT_PASS: { + proxyChannelDataEventInfo ev; - ev.channel_id = channelId; - ev.channel_name = channel->channel_name; - ev.data = data; - ev.data_len = size; - ev.flags = flags; - ev.total_size = totalSize; - - if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA, - pdata, &ev)) - return TRUE; /* Silently ignore */ - - return IFCALLRESULT(TRUE, pc->sendChannelData, pc, &ev); - } - case PF_UTILS_CHANNEL_INTERCEPT: - return pf_server_receive_channel_intercept(pdata, channelId, channel->channel_name, - data, size, flags, totalSize); - default: - break; + ev.channel_id = channelId; + ev.channel_name = channel->channel_name; + ev.data = data; + ev.data_len = size; + ev.flags = flags; + ev.total_size = totalSize; + return IFCALLRESULT(TRUE, pc->sendChannelData, pc, &ev); } + case PF_CHANNEL_RESULT_DROP: + return TRUE; + case PF_CHANNEL_RESULT_ERROR: + return FALSE; + } + original_cb: WINPR_ASSERT(pdata->server_receive_channel_data_original);