19 #include <winpr/assert.h>
21 #include <freerdp/channels/drdynvc.h>
22 #include <freerdp/utils/drdynvc.h>
23 #include <freerdp/server/proxy/proxy_log.h>
25 #include "pf_channel_drdynvc.h"
26 #include "../pf_channel.h"
27 #include "../proxy_modules.h"
28 #include "../pf_utils.h"
30 #define DTAG PROXY_TAG("drdynvc")
35 CHANNEL_OPENSTATE_WAITING_OPEN_STATUS,
36 CHANNEL_OPENSTATE_OPENED,
37 CHANNEL_OPENSTATE_CLOSED
38 } PfDynChannelOpenStatus;
40 typedef struct p_server_dynamic_channel_context pServerDynamicChannelContext;
41 typedef struct DynChannelTrackerState DynChannelTrackerState;
43 typedef PfChannelResult (*dynamic_channel_on_data_fn)(pServerContext* ps,
44 pServerDynamicChannelContext* channel,
45 BOOL isBackData, ChannelStateTracker* tracker,
46 BOOL firstPacket, BOOL lastPacket);
49 struct DynChannelTrackerState
51 UINT32 currentDataLength;
52 UINT32 CurrentDataReceived;
53 UINT32 CurrentDataFragments;
55 dynamic_channel_on_data_fn dataCallback;
58 typedef void (*channel_data_dtor_fn)(
void** user_data);
60 struct p_server_dynamic_channel_context
64 PfDynChannelOpenStatus openStatus;
65 pf_utils_channel_mode channelMode;
66 BOOL packetReassembly;
67 DynChannelTrackerState backTracker;
68 DynChannelTrackerState frontTracker;
71 channel_data_dtor_fn channelDataDtor;
78 ChannelStateTracker* backTracker;
79 ChannelStateTracker* frontTracker;
88 DYNCVC_READ_INCOMPLETE
91 static const char* openstatus2str(PfDynChannelOpenStatus status)
95 case CHANNEL_OPENSTATE_WAITING_OPEN_STATUS:
96 return "CHANNEL_OPENSTATE_WAITING_OPEN_STATUS";
97 case CHANNEL_OPENSTATE_CLOSED:
98 return "CHANNEL_OPENSTATE_CLOSED";
99 case CHANNEL_OPENSTATE_OPENED:
100 return "CHANNEL_OPENSTATE_OPENED";
102 return "CHANNEL_OPENSTATE_UNKNOWN";
106 static PfChannelResult data_cb(pServerContext* ps, pServerDynamicChannelContext* channel,
107 BOOL isBackData, ChannelStateTracker* tracker, BOOL firstPacket,
111 WINPR_ASSERT(channel);
112 WINPR_ASSERT(tracker);
113 WINPR_ASSERT(ps->pdata);
115 wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
117 .channelId = channel->channelId,
118 .data = currentPacket,
119 .isBackData = isBackData,
120 .first = firstPacket,
123 .packetSize = channelTracker_getCurrentPacketSize(tracker),
124 .result = PF_CHANNEL_RESULT_ERROR };
125 Stream_SealLength(dyn.data);
126 if (!pf_modules_run_filter(ps->pdata->module, FILTER_TYPE_INTERCEPT_CHANNEL, ps->pdata, &dyn))
127 return PF_CHANNEL_RESULT_ERROR;
129 channelTracker_setCurrentPacketSize(tracker, dyn.packetSize);
131 return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
135 static pServerDynamicChannelContext* DynamicChannelContext_new(wLog* log, pServerContext* ps,
136 const char* name, UINT32
id)
140 pServerDynamicChannelContext* ret = calloc(1,
sizeof(*ret));
143 WLog_Print(log, WLOG_ERROR,
"error allocating dynamic channel context '%s'", name);
148 ret->channelName = _strdup(name);
149 if (!ret->channelName)
151 WLog_Print(log, WLOG_ERROR,
"error allocating name in dynamic channel context '%s'", name);
156 ret->frontTracker.dataCallback = data_cb;
157 ret->backTracker.dataCallback = data_cb;
160 if (pf_modules_run_filter(ps->pdata->module, FILTER_TYPE_DYN_INTERCEPT_LIST, ps->pdata, &dyn) &&
162 ret->channelMode = PF_UTILS_CHANNEL_INTERCEPT;
164 ret->channelMode = pf_utils_get_channel_mode(ps->pdata->config, name);
165 ret->openStatus = CHANNEL_OPENSTATE_OPENED;
166 ret->packetReassembly = (ret->channelMode == PF_UTILS_CHANNEL_INTERCEPT);
171 static void DynamicChannelContext_free(
void* ptr)
173 pServerDynamicChannelContext* c = (pServerDynamicChannelContext*)ptr;
177 if (c->backTracker.currentPacket)
178 Stream_Free(c->backTracker.currentPacket, TRUE);
180 if (c->frontTracker.currentPacket)
181 Stream_Free(c->frontTracker.currentPacket, TRUE);
183 if (c->channelDataDtor)
184 c->channelDataDtor(&c->channelData);
186 free(c->channelName);
190 static UINT32 ChannelId_Hash(
const void* key)
192 const UINT32* v = (
const UINT32*)key;
196 static BOOL ChannelId_Compare(
const void* objA,
const void* objB)
198 const UINT32* v1 = objA;
199 const UINT32* v2 = objB;
203 static DynvcReadResult dynvc_read_varInt(wLog* log,
wStream* s,
size_t len, UINT64* varInt,
206 WINPR_ASSERT(varInt);
210 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 1))
211 return last ? DYNCVC_READ_ERROR : DYNCVC_READ_INCOMPLETE;
212 Stream_Read_UINT8(s, *varInt);
215 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 2))
216 return last ? DYNCVC_READ_ERROR : DYNCVC_READ_INCOMPLETE;
217 Stream_Read_UINT16(s, *varInt);
220 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 4))
221 return last ? DYNCVC_READ_ERROR : DYNCVC_READ_INCOMPLETE;
222 Stream_Read_UINT32(s, *varInt);
226 WLog_Print(log, WLOG_ERROR,
"Unknown int len %" PRIuz, len);
227 return DYNCVC_READ_ERROR;
229 return DYNCVC_READ_OK;
232 static PfChannelResult DynvcTrackerPeekFn(ChannelStateTracker* tracker, BOOL firstPacket,
239 BOOL haveChannelId = 0;
241 UINT64 dynChannelId = 0;
243 pServerDynamicChannelContext* dynChannel = NULL;
245 WINPR_ASSERT(tracker);
247 DynChannelContext* dynChannelContext =
248 (DynChannelContext*)channelTracker_getCustomData(tracker);
249 WINPR_ASSERT(dynChannelContext);
251 BOOL isBackData = (tracker == dynChannelContext->backTracker);
252 DynChannelTrackerState* trackerState = NULL;
254 UINT32 flags = lastPacket ? CHANNEL_FLAG_LAST : 0;
255 proxyData* pdata = channelTracker_getPData(tracker);
258 const char* direction = isBackData ?
"B->F" :
"F->B";
261 wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
262 s = Stream_StaticConstInit(&sbuffer, Stream_Buffer(currentPacket),
263 Stream_GetPosition(currentPacket));
266 if (!Stream_CheckAndLogRequiredLengthWLog(dynChannelContext->log, s, 1))
267 return PF_CHANNEL_RESULT_ERROR;
269 Stream_Read_UINT8(s, byte0);
274 case CREATE_REQUEST_PDU:
275 case CLOSE_REQUEST_PDU:
277 case DATA_COMPRESSED_PDU:
278 haveChannelId = TRUE;
282 case DATA_FIRST_COMPRESSED_PDU:
284 haveChannelId = TRUE;
287 haveChannelId = FALSE;
294 BYTE cbId = byte0 & 0x03;
296 switch (dynvc_read_varInt(dynChannelContext->log, s, cbId, &dynChannelId, lastPacket))
300 case DYNCVC_READ_INCOMPLETE:
301 return PF_CHANNEL_RESULT_DROP;
302 case DYNCVC_READ_ERROR:
304 WLog_Print(dynChannelContext->log, WLOG_ERROR,
305 "DynvcTrackerPeekFn: invalid channelId field");
306 return PF_CHANNEL_RESULT_ERROR;
312 dynChannel = (pServerDynamicChannelContext*)HashTable_GetItemValue(
313 dynChannelContext->channels, &dynChannelId);
314 if ((cmd != CREATE_REQUEST_PDU) || !isBackData)
316 if (!dynChannel || (dynChannel->openStatus == CHANNEL_OPENSTATE_CLOSED))
320 channelTracker_setMode(tracker, CHANNEL_TRACKER_DROP);
321 return PF_CHANNEL_RESULT_DROP;
328 BYTE lenLen = (byte0 >> 2) & 0x03;
329 switch (dynvc_read_varInt(dynChannelContext->log, s, lenLen, &Length, lastPacket))
333 case DYNCVC_READ_INCOMPLETE:
334 return PF_CHANNEL_RESULT_DROP;
335 case DYNCVC_READ_ERROR:
337 WLog_Print(dynChannelContext->log, WLOG_ERROR,
338 "DynvcTrackerPeekFn: invalid length field");
339 return PF_CHANNEL_RESULT_ERROR;
345 case CAPABILITY_REQUEST_PDU:
346 WLog_Print(dynChannelContext->log, WLOG_DEBUG,
"DynvcTracker: %s CAPABILITY_%s",
347 direction, isBackData ?
"REQUEST" :
"RESPONSE");
348 channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
349 return PF_CHANNEL_RESULT_PASS;
351 case CREATE_REQUEST_PDU:
353 UINT32 creationStatus = 0;
357 return PF_CHANNEL_RESULT_DROP;
362 const char* name = Stream_ConstPointer(s);
363 const size_t nameLen = Stream_GetRemainingLength(s);
365 const size_t len = strnlen(name, nameLen);
366 if ((len == 0) || (len == nameLen) || (dynChannelId > UINT16_MAX))
367 return PF_CHANNEL_RESULT_ERROR;
369 wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
370 dev.channel_id = (UINT16)dynChannelId;
371 dev.channel_name = name;
372 dev.data = Stream_Buffer(s);
373 dev.data_len = Stream_GetPosition(currentPacket);
375 dev.total_size = Stream_GetPosition(currentPacket);
380 dynChannelContext->log, WLOG_WARN,
381 "Reusing channel id %" PRIu32
", previously %s [state %s, mode %s], now %s",
382 dynChannel->channelId, dynChannel->channelName,
383 openstatus2str(dynChannel->openStatus),
384 pf_utils_channel_mode_string(dynChannel->channelMode), dev.channel_name);
386 HashTable_Remove(dynChannelContext->channels, &dynChannel->channelId);
389 if (!pf_modules_run_filter(pdata->module,
390 FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE, pdata,
392 return PF_CHANNEL_RESULT_DROP;
394 dynChannel = DynamicChannelContext_new(dynChannelContext->log, pdata->ps, name,
395 (UINT32)dynChannelId);
398 WLog_Print(dynChannelContext->log, WLOG_ERROR,
399 "unable to create dynamic channel context data");
400 return PF_CHANNEL_RESULT_ERROR;
403 WLog_Print(dynChannelContext->log, WLOG_DEBUG,
"Adding channel '%s'[%d]",
404 dynChannel->channelName, dynChannel->channelId);
405 if (!HashTable_Insert(dynChannelContext->channels, &dynChannel->channelId,
408 WLog_Print(dynChannelContext->log, WLOG_ERROR,
409 "unable register dynamic channel context data");
410 DynamicChannelContext_free(dynChannel);
411 return PF_CHANNEL_RESULT_ERROR;
414 dynChannel->openStatus = CHANNEL_OPENSTATE_WAITING_OPEN_STATUS;
417 return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, FALSE);
421 if (!Stream_CheckAndLogRequiredLengthWLog(dynChannelContext->log, s, 4))
422 return PF_CHANNEL_RESULT_ERROR;
424 Stream_Read_UINT32(s, creationStatus);
425 WLog_Print(dynChannelContext->log, WLOG_DEBUG,
426 "DynvcTracker(%" PRIu64
",%s): %s CREATE_RESPONSE openStatus=%" PRIu32,
427 dynChannelId, dynChannel->channelName, direction, creationStatus);
429 if (creationStatus == 0)
430 dynChannel->openStatus = CHANNEL_OPENSTATE_OPENED;
432 return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, TRUE);
435 case CLOSE_REQUEST_PDU:
437 return PF_CHANNEL_RESULT_DROP;
439 WLog_Print(dynChannelContext->log, WLOG_DEBUG,
440 "DynvcTracker(%s): %s Close request on channel", dynChannel->channelName,
442 channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
443 if (dynChannel->openStatus != CHANNEL_OPENSTATE_OPENED)
445 WLog_Print(dynChannelContext->log, WLOG_WARN,
446 "DynvcTracker(%s): is in state %s, expected %s", dynChannel->channelName,
447 openstatus2str(dynChannel->openStatus),
448 openstatus2str(CHANNEL_OPENSTATE_OPENED));
450 dynChannel->openStatus = CHANNEL_OPENSTATE_CLOSED;
451 return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
453 case SOFT_SYNC_REQUEST_PDU:
455 WLog_Print(dynChannelContext->log, WLOG_DEBUG,
"SOFT_SYNC_REQUEST_PDU");
456 channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
458 return PF_CHANNEL_RESULT_PASS;
460 case SOFT_SYNC_RESPONSE_PDU:
462 WLog_Print(dynChannelContext->log, WLOG_DEBUG,
"SOFT_SYNC_RESPONSE_PDU");
463 channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
464 return PF_CHANNEL_RESULT_PASS;
469 trackerState = isBackData ? &dynChannel->backTracker : &dynChannel->frontTracker;
472 case DATA_FIRST_COMPRESSED_PDU:
473 case DATA_COMPRESSED_PDU:
474 WLog_Print(dynChannelContext->log, WLOG_DEBUG,
475 "TODO: compressed data packets, pass them as is for now");
476 channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
477 return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
480 return PF_CHANNEL_RESULT_ERROR;
483 if (dynChannel->openStatus != CHANNEL_OPENSTATE_OPENED)
485 WLog_Print(dynChannelContext->log, WLOG_ERROR,
486 "DynvcTracker(%s [%s]): channel is not opened", dynChannel->channelName,
487 drdynvc_get_packet_type(cmd));
488 return PF_CHANNEL_RESULT_ERROR;
491 if ((cmd == DATA_FIRST_PDU) || (cmd == DATA_FIRST_COMPRESSED_PDU))
493 WLog_Print(dynChannelContext->log, WLOG_DEBUG,
494 "DynvcTracker(%s [%s]): %s DATA_FIRST currentPacketLength=%" PRIu64
"",
495 dynChannel->channelName, drdynvc_get_packet_type(cmd), direction, Length);
496 if (Length > UINT32_MAX)
497 return PF_CHANNEL_RESULT_ERROR;
498 trackerState->currentDataLength = (UINT32)Length;
499 trackerState->CurrentDataReceived = 0;
500 trackerState->CurrentDataFragments = 0;
502 if (dynChannel->packetReassembly)
504 if (trackerState->currentPacket)
505 Stream_SetPosition(trackerState->currentPacket, 0);
509 if (cmd == DATA_PDU || cmd == DATA_FIRST_PDU)
511 size_t extraSize = Stream_GetRemainingLength(s);
513 trackerState->CurrentDataFragments++;
514 trackerState->CurrentDataReceived += extraSize;
516 if (dynChannel->packetReassembly)
518 if (!trackerState->currentPacket)
520 trackerState->currentPacket = Stream_New(NULL, 1024);
521 if (!trackerState->currentPacket)
523 WLog_Print(dynChannelContext->log, WLOG_ERROR,
524 "unable to create current packet");
525 return PF_CHANNEL_RESULT_ERROR;
529 if (!Stream_EnsureRemainingCapacity(trackerState->currentPacket, extraSize))
531 WLog_Print(dynChannelContext->log, WLOG_ERROR,
"unable to grow current packet");
532 return PF_CHANNEL_RESULT_ERROR;
535 Stream_Write(trackerState->currentPacket, Stream_ConstPointer(s), extraSize);
537 WLog_Print(dynChannelContext->log, WLOG_DEBUG,
538 "DynvcTracker(%s [%s]): %s frags=%" PRIu32
" received=%" PRIu32
"(%" PRIu32
")",
539 dynChannel->channelName, drdynvc_get_packet_type(cmd), direction,
540 trackerState->CurrentDataFragments, trackerState->CurrentDataReceived,
541 trackerState->currentDataLength);
546 if (trackerState->currentDataLength)
548 if (trackerState->CurrentDataReceived > trackerState->currentDataLength)
550 WLog_Print(dynChannelContext->log, WLOG_ERROR,
551 "DynvcTracker (%s [%s]): reassembled packet (%" PRIu32
552 ") is bigger than announced length (%" PRIu32
")",
553 dynChannel->channelName, drdynvc_get_packet_type(cmd),
554 trackerState->CurrentDataReceived, trackerState->currentDataLength);
555 return PF_CHANNEL_RESULT_ERROR;
560 trackerState->CurrentDataFragments = 0;
561 trackerState->CurrentDataReceived = 0;
565 PfChannelResult result = PF_CHANNEL_RESULT_ERROR;
566 switch (dynChannel->channelMode)
568 case PF_UTILS_CHANNEL_PASSTHROUGH:
569 result = channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
571 case PF_UTILS_CHANNEL_BLOCK:
572 channelTracker_setMode(tracker, CHANNEL_TRACKER_DROP);
573 result = PF_CHANNEL_RESULT_DROP;
575 case PF_UTILS_CHANNEL_INTERCEPT:
576 if (trackerState->dataCallback)
578 result = trackerState->dataCallback(pdata->ps, dynChannel, isBackData, tracker,
579 firstPacket, lastPacket);
583 WLog_Print(dynChannelContext->log, WLOG_ERROR,
584 "no intercept callback for channel %s(fromBack=%d), dropping packet",
585 dynChannel->channelName, isBackData);
586 result = PF_CHANNEL_RESULT_DROP;
590 WLog_Print(dynChannelContext->log, WLOG_ERROR,
"unknown channel mode %d",
591 dynChannel->channelMode);
592 result = PF_CHANNEL_RESULT_ERROR;
596 if (!trackerState->currentDataLength ||
597 (trackerState->CurrentDataReceived == trackerState->currentDataLength))
599 trackerState->currentDataLength = 0;
600 trackerState->CurrentDataFragments = 0;
601 trackerState->CurrentDataReceived = 0;
603 if (dynChannel->packetReassembly && trackerState->currentPacket)
604 Stream_SetPosition(trackerState->currentPacket, 0);
610 static void DynChannelContext_free(
void* context)
612 DynChannelContext* c = context;
615 channelTracker_free(c->backTracker);
616 channelTracker_free(c->frontTracker);
617 HashTable_Free(c->channels);
621 static const char* dynamic_context(
void* arg)
623 proxyData* pdata = arg;
626 return pdata->session_id;
629 static DynChannelContext* DynChannelContext_new(proxyData* pdata,
630 pServerStaticChannelContext* channel)
632 DynChannelContext* dyn = calloc(1,
sizeof(DynChannelContext));
636 dyn->log = WLog_Get(DTAG);
637 WINPR_ASSERT(dyn->log);
638 WLog_SetContext(dyn->log, dynamic_context, pdata);
640 dyn->backTracker = channelTracker_new(channel, DynvcTrackerPeekFn, dyn);
641 if (!dyn->backTracker)
643 if (!channelTracker_setPData(dyn->backTracker, pdata))
646 dyn->frontTracker = channelTracker_new(channel, DynvcTrackerPeekFn, dyn);
647 if (!dyn->frontTracker)
649 if (!channelTracker_setPData(dyn->frontTracker, pdata))
652 dyn->channels = HashTable_New(FALSE);
656 if (!HashTable_SetHashFunction(dyn->channels, ChannelId_Hash))
659 wObject* kobj = HashTable_KeyObject(dyn->channels);
661 kobj->fnObjectEquals = ChannelId_Compare;
663 wObject* vobj = HashTable_ValueObject(dyn->channels);
665 vobj->fnObjectFree = DynamicChannelContext_free;
670 DynChannelContext_free(dyn);
674 static PfChannelResult pf_dynvc_back_data(proxyData* pdata,
675 const pServerStaticChannelContext* channel,
676 const BYTE* xdata,
size_t xsize, UINT32 flags,
679 WINPR_ASSERT(channel);
681 DynChannelContext* dyn = (DynChannelContext*)channel->context;
685 return channelTracker_update(dyn->backTracker, xdata, xsize, flags, totalSize);
688 static PfChannelResult pf_dynvc_front_data(proxyData* pdata,
689 const pServerStaticChannelContext* channel,
690 const BYTE* xdata,
size_t xsize, UINT32 flags,
693 WINPR_ASSERT(channel);
695 DynChannelContext* dyn = (DynChannelContext*)channel->context;
699 return channelTracker_update(dyn->frontTracker, xdata, xsize, flags, totalSize);
702 BOOL pf_channel_setup_drdynvc(proxyData* pdata, pServerStaticChannelContext* channel)
704 DynChannelContext* ret = DynChannelContext_new(pdata, channel);
708 channel->onBackData = pf_dynvc_back_data;
709 channel->onFrontData = pf_dynvc_front_data;
710 channel->contextDtor = DynChannelContext_free;
711 channel->context = ret;
This struct contains function pointer to initialize/free objects.