FreeRDP
Loading...
Searching...
No Matches
pf_channel_drdynvc.c
1
19#include <winpr/assert.h>
20
21#include <freerdp/channels/drdynvc.h>
22#include <freerdp/utils/drdynvc.h>
23#include <freerdp/server/proxy/proxy_log.h>
24
25#include "pf_channel_drdynvc.h"
26#include "../pf_channel.h"
27#include "../proxy_modules.h"
28#include "../pf_utils.h"
29#include "../pf_server.h"
30
31#define DTAG PROXY_TAG("drdynvc")
32
33#define Stream_CheckAndLogRequiredLengthWLogWithBackend(log, s, nmemb, backdata) \
34 Stream_CheckAndLogRequiredLengthWLogEx(log, WLOG_WARN, s, nmemb, 1, "%s(%s:%" PRIuz ")[%s]", \
35 __func__, __FILE__, (size_t)__LINE__, \
36 getDirection(backdata))
37
39typedef enum
40{
41 CHANNEL_OPENSTATE_WAITING_OPEN_STATUS,
42 CHANNEL_OPENSTATE_OPENED,
43 CHANNEL_OPENSTATE_CLOSED
44} PfDynChannelOpenStatus;
45
46typedef struct p_server_dynamic_channel_context pServerDynamicChannelContext;
47typedef struct DynChannelTrackerState DynChannelTrackerState;
48
49typedef PfChannelResult (*dynamic_channel_on_data_fn)(pServerContext* ps,
50 pServerDynamicChannelContext* channel,
51 BOOL isBackData, ChannelStateTracker* tracker,
52 BOOL firstPacket, BOOL lastPacket);
53
55struct DynChannelTrackerState
56{
57 UINT32 ExpectedTotalDataLength;
58 UINT32 CurrentDataReceived;
59 UINT32 CurrentDataFragments;
60 wStream* currentPacket;
61 WINPR_ATTR_NODISCARD dynamic_channel_on_data_fn dataCallback;
62};
63
64typedef void (*channel_data_dtor_fn)(void** user_data);
65
66struct p_server_dynamic_channel_context
67{
68 char* channelName;
69 UINT32 channelId;
70 PfDynChannelOpenStatus openStatus;
71 pf_utils_channel_mode channelMode;
72 BOOL packetReassembly;
73 DynChannelTrackerState backTracker;
74 DynChannelTrackerState frontTracker;
75
76 void* channelData;
77 channel_data_dtor_fn channelDataDtor;
78};
79
81typedef struct
82{
83 wHashTable* channels;
84 ChannelStateTracker* backTracker;
85 ChannelStateTracker* frontTracker;
86 wLog* log;
87} DynChannelContext;
88
90typedef enum
91{
92 DYNCVC_READ_OK,
93 DYNCVC_READ_ERROR,
94 DYNCVC_READ_INCOMPLETE
95} DynvcReadResult;
96
97static const char* openstatus2str(PfDynChannelOpenStatus status)
98{
99 switch (status)
100 {
101 case CHANNEL_OPENSTATE_WAITING_OPEN_STATUS:
102 return "CHANNEL_OPENSTATE_WAITING_OPEN_STATUS";
103 case CHANNEL_OPENSTATE_CLOSED:
104 return "CHANNEL_OPENSTATE_CLOSED";
105 case CHANNEL_OPENSTATE_OPENED:
106 return "CHANNEL_OPENSTATE_OPENED";
107 default:
108 return "CHANNEL_OPENSTATE_UNKNOWN";
109 }
110}
111
112#define DynvcTrackerLog(log, level, dynChannel, cmd, isBackData, ...) \
113 dyn_log_((log), (level), (dynChannel), (cmd), (isBackData), __func__, __FILE__, __LINE__, \
114 __VA_ARGS__)
115
116WINPR_ATTR_NODISCARD
117static const char* getDirection(BOOL isBackData)
118{
119 return isBackData ? "B->F" : "F->B";
120}
121
122static void dyn_log_(wLog* log, DWORD level, const pServerDynamicChannelContext* dynChannel,
123 BYTE cmd, BOOL isBackData, const char* fkt, const char* file, size_t line,
124 const char* fmt, ...)
125{
126 if (!WLog_IsLevelActive(log, level))
127 return;
128
129 char* prefix = nullptr;
130 char* msg = nullptr;
131 size_t prefixlen = 0;
132 size_t msglen = 0;
133
134 uint32_t channelId = dynChannel ? dynChannel->channelId : UINT32_MAX;
135 const char* channelName = dynChannel ? dynChannel->channelName : "<nullptr>";
136 (void)winpr_asprintf(&prefix, &prefixlen, "DynvcTracker[%s](%s [%s:%" PRIu32 "])",
137 getDirection(isBackData), channelName, drdynvc_get_packet_type(cmd),
138 channelId);
139
140 va_list ap = WINPR_C_ARRAY_INIT;
141 va_start(ap, fmt);
142 (void)winpr_vasprintf(&msg, &msglen, fmt, ap);
143 va_end(ap);
144
145 WLog_PrintTextMessage(log, level, line, file, fkt, "%s: %s", prefix, msg);
146 free(prefix);
147 free(msg);
148}
149
150WINPR_ATTR_NODISCARD
151static PfChannelResult data_cb(pServerContext* ps, pServerDynamicChannelContext* channel,
152 BOOL isBackData, ChannelStateTracker* tracker, BOOL firstPacket,
153 BOOL lastPacket)
154{
155 WINPR_ASSERT(ps);
156 WINPR_ASSERT(channel);
157 WINPR_ASSERT(tracker);
158 WINPR_ASSERT(ps->pdata);
159
160 wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
161 proxyDynChannelInterceptData dyn = { .name = channel->channelName,
162 .channelId = channel->channelId,
163 .data = currentPacket,
164 .isBackData = isBackData,
165 .first = firstPacket,
166 .last = lastPacket,
167 .rewritten = FALSE,
168 .packetSize = channelTracker_getCurrentPacketSize(tracker),
169 .result = PF_CHANNEL_RESULT_ERROR };
170 Stream_SealLength(dyn.data);
171 if (!pf_modules_run_filter(ps->pdata->module, FILTER_TYPE_INTERCEPT_CHANNEL, ps->pdata, &dyn))
172 return PF_CHANNEL_RESULT_ERROR;
173
174 channelTracker_setCurrentPacketSize(tracker, dyn.packetSize);
175 if (dyn.rewritten)
176 return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
177 return dyn.result;
178}
179
180static void DynamicChannelContext_free(void* ptr)
181{
182 pServerDynamicChannelContext* c = (pServerDynamicChannelContext*)ptr;
183 if (!c)
184 return;
185
186 if (c->backTracker.currentPacket)
187 Stream_Free(c->backTracker.currentPacket, TRUE);
188
189 if (c->frontTracker.currentPacket)
190 Stream_Free(c->frontTracker.currentPacket, TRUE);
191
192 if (c->channelDataDtor)
193 c->channelDataDtor(&c->channelData);
194
195 free(c->channelName);
196 free(c);
197}
198
199WINPR_ATTR_MALLOC(DynamicChannelContext_free, 1)
200WINPR_ATTR_NODISCARD
201static pServerDynamicChannelContext* DynamicChannelContext_new(wLog* log, pServerContext* ps,
202 const char* name, UINT32 id)
203{
204 WINPR_ASSERT(log);
205
206 pServerDynamicChannelContext* ret = calloc(1, sizeof(*ret));
207 if (!ret)
208 {
209 WLog_Print(log, WLOG_ERROR, "error allocating dynamic channel context '%s'", name);
210 return nullptr;
211 }
212
213 ret->channelId = id;
214 ret->channelName = _strdup(name);
215 if (!ret->channelName)
216 {
217 WLog_Print(log, WLOG_ERROR, "error allocating name in dynamic channel context '%s'", name);
218 free(ret);
219 return nullptr;
220 }
221
222 ret->frontTracker.dataCallback = data_cb;
223 ret->backTracker.dataCallback = data_cb;
224
225 proxyChannelToInterceptData dyn = { .name = name, .channelId = id, .intercept = FALSE };
226 if (pf_modules_run_filter(ps->pdata->module, FILTER_TYPE_DYN_INTERCEPT_LIST, ps->pdata, &dyn) &&
227 dyn.intercept)
228 ret->channelMode = PF_UTILS_CHANNEL_INTERCEPT;
229 else
230 ret->channelMode = pf_utils_get_channel_mode(ps->pdata->config, name);
231 ret->openStatus = CHANNEL_OPENSTATE_OPENED;
232 ret->packetReassembly = (ret->channelMode == PF_UTILS_CHANNEL_INTERCEPT);
233
234 return ret;
235}
236
237WINPR_ATTR_NODISCARD
238static UINT32 ChannelId_Hash(const void* key)
239{
240 const UINT32* v = (const UINT32*)key;
241 return *v;
242}
243
244WINPR_ATTR_NODISCARD
245static BOOL ChannelId_Compare(const void* objA, const void* objB)
246{
247 const UINT32* v1 = objA;
248 const UINT32* v2 = objB;
249 return (*v1 == *v2);
250}
251
252WINPR_ATTR_NODISCARD
253static DynvcReadResult dynvc_read_varInt(wLog* log, wStream* s, size_t len, UINT64* varInt,
254 BOOL last)
255{
256 WINPR_ASSERT(varInt);
257 switch (len)
258 {
259 case 0x00:
260 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 1))
261 return last ? DYNCVC_READ_ERROR : DYNCVC_READ_INCOMPLETE;
262 Stream_Read_UINT8(s, *varInt);
263 break;
264 case 0x01:
265 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 2))
266 return last ? DYNCVC_READ_ERROR : DYNCVC_READ_INCOMPLETE;
267 Stream_Read_UINT16(s, *varInt);
268 break;
269 case 0x02:
270 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 4))
271 return last ? DYNCVC_READ_ERROR : DYNCVC_READ_INCOMPLETE;
272 Stream_Read_UINT32(s, *varInt);
273 break;
274 case 0x03:
275 default:
276 WLog_Print(log, WLOG_ERROR, "Unknown int len %" PRIuz, len);
277 return DYNCVC_READ_ERROR;
278 }
279 return DYNCVC_READ_OK;
280}
281
282WINPR_ATTR_NODISCARD
283static BOOL Stream_ResetAndResize(DynChannelTrackerState* tracker)
284{
285 WINPR_ASSERT(tracker);
286 Stream_Free(tracker->currentPacket, TRUE);
287 tracker->currentPacket = Stream_New(nullptr, 2400);
288 return tracker->currentPacket != nullptr;
289}
290
291WINPR_ATTR_NODISCARD
292static PfChannelResult DynvcTrackerPeekHandleByMode(ChannelStateTracker* tracker,
293 DynChannelTrackerState* trackerState,
294 pServerDynamicChannelContext* dynChannel,
295 BYTE cmd, BOOL firstPacket, BOOL lastPacket)
296{
297 WINPR_ASSERT(tracker);
298 WINPR_ASSERT(trackerState);
299 WINPR_ASSERT(dynChannel);
300 PfChannelResult result = PF_CHANNEL_RESULT_ERROR;
301
302 DynChannelContext* dynChannelContext =
303 (DynChannelContext*)channelTracker_getCustomData(tracker);
304 WINPR_ASSERT(dynChannelContext);
305
306 proxyData* pdata = channelTracker_getPData(tracker);
307 WINPR_ASSERT(pdata);
308
309 const BOOL isBackData = (tracker == dynChannelContext->backTracker);
310 switch (dynChannel->channelMode)
311 {
312 case PF_UTILS_CHANNEL_PASSTHROUGH:
313 result = channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
314 break;
315 case PF_UTILS_CHANNEL_BLOCK:
316 channelTracker_setMode(tracker, CHANNEL_TRACKER_DROP);
317 result = PF_CHANNEL_RESULT_DROP;
318 break;
319 case PF_UTILS_CHANNEL_INTERCEPT:
320 if (trackerState->dataCallback)
321 {
322 pServerContext* ps = proxy_data_get_server_context(pdata);
323 result = trackerState->dataCallback(ps, dynChannel, isBackData, tracker,
324 firstPacket, lastPacket);
325 }
326 else
327 {
328 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
329 "no intercept callback for channel, dropping packet");
330 result = PF_CHANNEL_RESULT_DROP;
331 }
332 break;
333 default:
334 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
335 "unknown channel mode %u", dynChannel->channelMode);
336 result = PF_CHANNEL_RESULT_ERROR;
337 break;
338 }
339
340 if ((trackerState->ExpectedTotalDataLength == 0) ||
341 (trackerState->CurrentDataReceived >= trackerState->ExpectedTotalDataLength))
342 {
343 trackerState->ExpectedTotalDataLength = 0;
344 trackerState->CurrentDataFragments = 0;
345 trackerState->CurrentDataReceived = 0;
346
347 if (dynChannel->packetReassembly && trackerState->currentPacket)
348 {
349 if (!Stream_ResetAndResize(trackerState))
350 return PF_CHANNEL_RESULT_ERROR;
351 }
352 }
353
354 return result;
355}
356
357WINPR_ATTR_NODISCARD
358static PfChannelResult DynvcTrackerHandleClose(ChannelStateTracker* tracker,
359 pServerDynamicChannelContext* dynChannel,
360 DynChannelContext* dynChannelContext,
361 BOOL firstPacket, BOOL lastPacket)
362{
363 WINPR_ASSERT(dynChannelContext);
364
365 const BOOL isBackData = (tracker == dynChannelContext->backTracker);
366
367 if (!lastPacket || !dynChannel)
368 return PF_CHANNEL_RESULT_DROP;
369
370 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, CLOSE_REQUEST_PDU, isBackData,
371 "Close request");
372 channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
373 if (dynChannel->openStatus != CHANNEL_OPENSTATE_OPENED)
374 {
375 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, CLOSE_REQUEST_PDU,
376 isBackData, "is in state %s, expected %s",
377 openstatus2str(dynChannel->openStatus),
378 openstatus2str(CHANNEL_OPENSTATE_OPENED));
379 }
380 dynChannel->openStatus = CHANNEL_OPENSTATE_CLOSED;
381 return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
382}
383
384WINPR_ATTR_NODISCARD
385static PfChannelResult DynvcTrackerHandleCreateBack(ChannelStateTracker* tracker, wStream* s,
386 DWORD flags, proxyData* pdata,
387 pServerDynamicChannelContext* dynChannel,
388 DynChannelContext* dynChannelContext,
389 UINT64 dynChannelId)
390{
391 proxyChannelDataEventInfo dev = WINPR_C_ARRAY_INIT;
392 const char* name = Stream_ConstPointer(s);
393 const size_t nameLen = Stream_GetRemainingLength(s);
394 const size_t len = strnlen(name, nameLen);
395 const BOOL isBackData = (tracker == dynChannelContext->backTracker);
396 const BYTE cmd = CREATE_REQUEST_PDU;
397
398 if ((len == 0) || (len == nameLen) || (dynChannelId > UINT16_MAX))
399 {
400 char namebuffer[64] = WINPR_C_ARRAY_INIT;
401 if (len <= INT32_MAX)
402 (void)_snprintf(namebuffer, sizeof(namebuffer) - 1, "%.*s",
403 WINPR_ASSERTING_INT_CAST(int, len), name);
404 else
405 (void)_snprintf(namebuffer, sizeof(namebuffer) - 1, "<LENGTH OUT OF BOUND>[%" PRIuz "]",
406 len);
407
408 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
409 "channel id %" PRIu64 ", name=%s [%" PRIuz "|%" PRIuz "], status=%s",
410 dynChannelId, namebuffer, len, nameLen,
411 dynChannel ? openstatus2str(dynChannel->openStatus) : "nullptr");
412 return PF_CHANNEL_RESULT_ERROR;
413 }
414
415 wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
416 dev.channel_id = (UINT16)dynChannelId;
417 dev.channel_name = name;
418 dev.data = Stream_Buffer(s);
419 dev.data_len = Stream_GetPosition(currentPacket);
420 dev.flags = flags;
421 dev.total_size = Stream_GetPosition(currentPacket);
422
423 if (dynChannel)
424 {
425 DynvcTrackerLog(dynChannelContext->log, WLOG_WARN, dynChannel, cmd, isBackData,
426 "Reusing channel id, now %s", name);
427
428 HashTable_Remove(dynChannelContext->channels, &dynChannel->channelId);
429 }
430
431 if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE,
432 pdata, &dev))
433 return PF_CHANNEL_RESULT_DROP; /* Silently drop */
434
435 pServerContext* ps = proxy_data_get_server_context(pdata);
436 dynChannel = DynamicChannelContext_new(dynChannelContext->log, ps, name, (UINT32)dynChannelId);
437 if (!dynChannel)
438 {
439 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
440 "unable to create dynamic channel context data");
441 return PF_CHANNEL_RESULT_ERROR;
442 }
443
444 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
445 "Adding channel");
446 if (!HashTable_Insert(dynChannelContext->channels, &dynChannel->channelId, dynChannel))
447 {
448 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
449 "unable register dynamic channel context data");
450 DynamicChannelContext_free(dynChannel);
451 return PF_CHANNEL_RESULT_ERROR;
452 }
453
454 dynChannel->openStatus = CHANNEL_OPENSTATE_WAITING_OPEN_STATUS;
455
456 const BOOL firstPacket = (flags & CHANNEL_FLAG_FIRST) != 0;
457 const BOOL lastPacket = (flags & CHANNEL_FLAG_LAST) != 0;
458
459 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert owns dynChannel
460 return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, FALSE);
461}
462
463WINPR_ATTR_NODISCARD
464static PfChannelResult DynvcTrackerHandleCreateFront(ChannelStateTracker* tracker, wStream* s,
465 DWORD flags,
466 WINPR_ATTR_UNUSED proxyData* pdata,
467 pServerDynamicChannelContext* dynChannel,
468 DynChannelContext* dynChannelContext,
469 WINPR_ATTR_UNUSED UINT64 dynChannelId)
470{
471 const BOOL isBackData = (tracker == dynChannelContext->backTracker);
472 const BYTE cmd = CREATE_REQUEST_PDU;
473
474 /* CREATE_REQUEST_PDU response */
475 if (!Stream_CheckAndLogRequiredLengthWLogWithBackend(dynChannelContext->log, s, 4, FALSE))
476 return PF_CHANNEL_RESULT_ERROR;
477
478 const UINT32 creationStatus = Stream_Get_UINT32(s);
479 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
480 "CREATE_RESPONSE openStatus=%" PRIu32, creationStatus);
481
482 if (dynChannel && (creationStatus == 0))
483 dynChannel->openStatus = CHANNEL_OPENSTATE_OPENED;
484
485 const BOOL firstPacket = (flags & CHANNEL_FLAG_FIRST) != 0;
486 const BOOL lastPacket = (flags & CHANNEL_FLAG_LAST) != 0;
487
488 return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, TRUE);
489}
490
491WINPR_ATTR_NODISCARD
492static PfChannelResult DynvcTrackerHandleCreate(ChannelStateTracker* tracker, wStream* s,
493 DWORD flags,
494 pServerDynamicChannelContext* dynChannel,
495 UINT64 dynChannelId)
496{
497 WINPR_ASSERT(tracker);
498 WINPR_ASSERT(s);
499
500 DynChannelContext* dynChannelContext =
501 (DynChannelContext*)channelTracker_getCustomData(tracker);
502 WINPR_ASSERT(dynChannelContext);
503
504 const BOOL lastPacket = (flags & CHANNEL_FLAG_LAST) != 0;
505 const BOOL isBackData = (tracker == dynChannelContext->backTracker);
506
507 proxyData* pdata = channelTracker_getPData(tracker);
508 WINPR_ASSERT(pdata);
509
510 /* we only want the full packet */
511 if (!lastPacket)
512 return PF_CHANNEL_RESULT_DROP;
513
514 if (isBackData)
515 return DynvcTrackerHandleCreateBack(tracker, s, flags, pdata, dynChannel, dynChannelContext,
516 dynChannelId);
517
518 return DynvcTrackerHandleCreateFront(tracker, s, flags, pdata, dynChannel, dynChannelContext,
519 dynChannelId);
520}
521
522WINPR_ATTR_NODISCARD
523static PfChannelResult DynvcTrackerHandleCmdDATA(ChannelStateTracker* tracker,
524 pServerDynamicChannelContext* dynChannel,
525 wStream* s, BYTE cmd, UINT64 Length,
526 BOOL firstPacket, BOOL lastPacket)
527{
528 WINPR_ASSERT(tracker);
529 WINPR_ASSERT(s);
530
531 DynChannelContext* dynChannelContext =
532 (DynChannelContext*)channelTracker_getCustomData(tracker);
533 WINPR_ASSERT(dynChannelContext);
534
535 const BOOL isBackData = (tracker == dynChannelContext->backTracker);
536
537 if (!dynChannel)
538 {
539 DynvcTrackerLog(dynChannelContext->log, WLOG_WARN, dynChannel, cmd, isBackData,
540 "channel is nullptr, dropping packet");
541 return PF_CHANNEL_RESULT_DROP;
542 }
543
544 DynChannelTrackerState* trackerState =
545 isBackData ? &dynChannel->backTracker : &dynChannel->frontTracker;
546 if (dynChannel->openStatus != CHANNEL_OPENSTATE_OPENED)
547 {
548 DynvcTrackerLog(dynChannelContext->log, WLOG_WARN, dynChannel, cmd, isBackData,
549 "channel is not opened, dropping packet");
550 return PF_CHANNEL_RESULT_DROP;
551 }
552
553 switch (cmd)
554 {
555 case DATA_FIRST_PDU:
556 case DATA_FIRST_COMPRESSED_PDU:
557 {
558 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
559 "DATA_FIRST currentPacketLength=%" PRIu64 "", Length);
560 if (Length > UINT32_MAX)
561 {
562 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
563 "Length out of bounds: %" PRIu64, Length);
564 return PF_CHANNEL_RESULT_ERROR;
565 }
566 trackerState->ExpectedTotalDataLength = (UINT32)Length;
567 trackerState->CurrentDataReceived = 0;
568 trackerState->CurrentDataFragments = 0;
569
570 if (dynChannel->packetReassembly)
571 {
572 if (trackerState->currentPacket)
573 {
574 if (!Stream_ResetAndResize(trackerState))
575 return PF_CHANNEL_RESULT_ERROR;
576 }
577 }
578 }
579 break;
580 default:
581 break;
582 }
583
584 switch (cmd)
585 {
586 case DATA_PDU:
587 case DATA_FIRST_PDU:
588 {
589 size_t extraSize = Stream_GetRemainingLength(s);
590
591 trackerState->CurrentDataFragments++;
592 trackerState->CurrentDataReceived += WINPR_ASSERTING_INT_CAST(uint32_t, extraSize);
593
594 if (dynChannel->packetReassembly)
595 {
596 if (!trackerState->currentPacket)
597 {
598 if (!Stream_ResetAndResize(trackerState))
599 {
600 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd,
601 isBackData, "unable to create current packet",
602 getDirection(isBackData), dynChannel->channelName,
603 drdynvc_get_packet_type(cmd));
604 return PF_CHANNEL_RESULT_ERROR;
605 }
606 }
607
608 if (!Stream_EnsureRemainingCapacity(trackerState->currentPacket, extraSize))
609 {
610 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
611 "unable to grow current packet", getDirection(isBackData),
612 dynChannel->channelName, drdynvc_get_packet_type(cmd));
613 return PF_CHANNEL_RESULT_ERROR;
614 }
615
616 Stream_Write(trackerState->currentPacket, Stream_ConstPointer(s), extraSize);
617 }
618 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
619 "frags=%" PRIu32 " received=%" PRIu32 "(%" PRIu32 ")",
620 trackerState->CurrentDataFragments, trackerState->CurrentDataReceived,
621 trackerState->ExpectedTotalDataLength);
622 }
623 break;
624 default:
625 break;
626 }
627
628 switch (cmd)
629 {
630 case DATA_PDU:
631 {
632 if (trackerState->ExpectedTotalDataLength)
633 {
634 if (trackerState->CurrentDataReceived > trackerState->ExpectedTotalDataLength)
635 {
636 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
637 "reassembled packet (%" PRIu32
638 ") is bigger than announced length (%" PRIu32 ")",
639 trackerState->CurrentDataReceived,
640 trackerState->ExpectedTotalDataLength);
641 return PF_CHANNEL_RESULT_ERROR;
642 }
643 }
644 else
645 {
646 trackerState->CurrentDataFragments = 0;
647 trackerState->CurrentDataReceived = 0;
648 }
649 }
650 break;
651 default:
652 break;
653 }
654
655 return DynvcTrackerPeekHandleByMode(tracker, trackerState, dynChannel, cmd, firstPacket,
656 lastPacket);
657}
658
659WINPR_ATTR_NODISCARD
660static PfChannelResult DynvcTrackerHandleCmd(ChannelStateTracker* tracker,
661 pServerDynamicChannelContext* dynChannel, wStream* s,
662 BYTE cmd, UINT32 flags, UINT64 Length,
663 UINT64 dynChannelId, BOOL firstPacket, BOOL lastPacket)
664{
665 WINPR_ASSERT(tracker);
666 WINPR_ASSERT(s);
667
668 DynChannelContext* dynChannelContext =
669 (DynChannelContext*)channelTracker_getCustomData(tracker);
670 WINPR_ASSERT(dynChannelContext);
671
672 const BOOL isBackData = (tracker == dynChannelContext->backTracker);
673 switch (cmd)
674 {
675 case CAPABILITY_REQUEST_PDU:
676 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
677 "CAPABILITY_%s", isBackData ? "REQUEST" : "RESPONSE");
678 channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
679 return PF_CHANNEL_RESULT_PASS;
680
681 case CREATE_REQUEST_PDU:
682 return DynvcTrackerHandleCreate(tracker, s, flags, dynChannel, dynChannelId);
683
684 case CLOSE_REQUEST_PDU:
685 return DynvcTrackerHandleClose(tracker, dynChannel, dynChannelContext, firstPacket,
686 lastPacket);
687
688 case SOFT_SYNC_REQUEST_PDU:
689 /* just pass then as is for now */
690 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
691 "SOFT_SYNC_REQUEST_PDU");
692 channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
693 /*TODO: return pf_treat_softsync_req(pdata, s);*/
694 return PF_CHANNEL_RESULT_PASS;
695
696 case SOFT_SYNC_RESPONSE_PDU:
697 /* just pass then as is for now */
698 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
699 "SOFT_SYNC_RESPONSE_PDU");
700 channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
701 return PF_CHANNEL_RESULT_PASS;
702
703 case DATA_FIRST_PDU:
704 case DATA_PDU:
705 return DynvcTrackerHandleCmdDATA(tracker, dynChannel, s, cmd, Length, firstPacket,
706 lastPacket);
707
708 case DATA_FIRST_COMPRESSED_PDU:
709 case DATA_COMPRESSED_PDU:
710 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
711 "TODO: compressed data packets, pass them as is for now");
712 channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
713 return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
714
715 default:
716 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
717 "Invalid command ID");
718 return PF_CHANNEL_RESULT_ERROR;
719 }
720}
721
722WINPR_ATTR_NODISCARD
723static PfChannelResult DynvcTrackerPeekFn(ChannelStateTracker* tracker, BOOL firstPacket,
724 BOOL lastPacket)
725{
726 wStream* s = nullptr;
727 wStream sbuffer;
728 BOOL haveChannelId = 0;
729 BOOL haveLength = 0;
730 UINT64 dynChannelId = 0;
731 UINT64 Length = 0;
732 pServerDynamicChannelContext* dynChannel = nullptr;
733
734 WINPR_ASSERT(tracker);
735
736 DynChannelContext* dynChannelContext =
737 (DynChannelContext*)channelTracker_getCustomData(tracker);
738 WINPR_ASSERT(dynChannelContext);
739
740 const BOOL isBackData = (tracker == dynChannelContext->backTracker);
741
742 UINT32 flags = lastPacket ? CHANNEL_FLAG_LAST : 0;
743 if (firstPacket)
744 flags |= CHANNEL_FLAG_FIRST;
745
746 {
747 wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
748 s = Stream_StaticConstInit(&sbuffer, Stream_Buffer(currentPacket),
749 Stream_GetPosition(currentPacket));
750 }
751
752 if (!Stream_CheckAndLogRequiredLengthWLogWithBackend(dynChannelContext->log, s, 1, isBackData))
753 return PF_CHANNEL_RESULT_ERROR;
754
755 const BYTE byte0 = Stream_Get_UINT8(s);
756 const BYTE cmd = byte0 >> 4;
757
758 switch (cmd)
759 {
760 case CREATE_REQUEST_PDU:
761 case CLOSE_REQUEST_PDU:
762 case DATA_PDU:
763 case DATA_COMPRESSED_PDU:
764 haveChannelId = TRUE;
765 haveLength = FALSE;
766 break;
767 case DATA_FIRST_PDU:
768 case DATA_FIRST_COMPRESSED_PDU:
769 haveLength = TRUE;
770 haveChannelId = TRUE;
771 break;
772 default:
773 haveChannelId = FALSE;
774 haveLength = FALSE;
775 break;
776 }
777
778 HashTable_Lock(dynChannelContext->channels);
779 if (haveChannelId)
780 {
781 BYTE cbId = byte0 & 0x03;
782
783 switch (dynvc_read_varInt(dynChannelContext->log, s, cbId, &dynChannelId, lastPacket))
784 {
785 case DYNCVC_READ_OK:
786 break;
787 case DYNCVC_READ_INCOMPLETE:
788 return PF_CHANNEL_RESULT_DROP;
789 case DYNCVC_READ_ERROR:
790 default:
791 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
792 "invalid channelId field");
793 HashTable_Unlock(dynChannelContext->channels);
794 return PF_CHANNEL_RESULT_ERROR;
795 }
796
797 /* we always try to retrieve the dynamic channel in case it would have been opened
798 * and closed
799 */
800 dynChannel = (pServerDynamicChannelContext*)HashTable_GetItemValue(
801 dynChannelContext->channels, &dynChannelId);
802 if ((cmd != CREATE_REQUEST_PDU) || !isBackData)
803 {
804 if (!dynChannel || (dynChannel->openStatus == CHANNEL_OPENSTATE_CLOSED))
805 {
806 /* we've not found the target channel, so we drop this chunk, plus all the rest of
807 * the packet */
808 channelTracker_setMode(tracker, CHANNEL_TRACKER_DROP);
809 HashTable_Unlock(dynChannelContext->channels);
810 return PF_CHANNEL_RESULT_DROP;
811 }
812 }
813 }
814
815 if (haveLength)
816 {
817 BYTE lenLen = (byte0 >> 2) & 0x03;
818 switch (dynvc_read_varInt(dynChannelContext->log, s, lenLen, &Length, lastPacket))
819 {
820 case DYNCVC_READ_OK:
821 break;
822 case DYNCVC_READ_INCOMPLETE:
823 return PF_CHANNEL_RESULT_DROP;
824 case DYNCVC_READ_ERROR:
825 default:
826 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
827 "invalid length field");
828 HashTable_Unlock(dynChannelContext->channels);
829 return PF_CHANNEL_RESULT_ERROR;
830 }
831 }
832
833 PfChannelResult rc = DynvcTrackerHandleCmd(tracker, dynChannel, s, cmd, flags, Length,
834 dynChannelId, firstPacket, lastPacket);
835 HashTable_Unlock(dynChannelContext->channels);
836 return rc;
837}
838
839static void DynChannelContext_free(void* context)
840{
841 DynChannelContext* c = context;
842 if (!c)
843 return;
844 channelTracker_free(c->backTracker);
845 channelTracker_free(c->frontTracker);
846 HashTable_Free(c->channels);
847 WLog_Discard(c->log);
848 free(c);
849}
850
851WINPR_ATTR_NODISCARD
852static const char* dynamic_context(void* arg)
853{
854 proxyData* pdata = arg;
855 if (!pdata)
856 return "pdata=null";
857 return pdata->session_id;
858}
859
860WINPR_ATTR_MALLOC(DynChannelContext_free, 1)
861static DynChannelContext* DynChannelContext_new(proxyData* pdata,
862 pServerStaticChannelContext* channel)
863{
864 DynChannelContext* dyn = calloc(1, sizeof(DynChannelContext));
865 if (!dyn)
866 return nullptr;
867
868 dyn->log = WLog_Create(DTAG, WLog_GetRoot());
869 if (!dyn->log)
870 goto fail;
871
872 if (!WLog_SetContext(dyn->log, dynamic_context, pdata))
873 goto fail;
874
875 dyn->backTracker = channelTracker_new(channel, DynvcTrackerPeekFn, dyn);
876 if (!dyn->backTracker)
877 goto fail;
878 if (!channelTracker_setPData(dyn->backTracker, pdata))
879 goto fail;
880
881 dyn->frontTracker = channelTracker_new(channel, DynvcTrackerPeekFn, dyn);
882 if (!dyn->frontTracker)
883 goto fail;
884 if (!channelTracker_setPData(dyn->frontTracker, pdata))
885 goto fail;
886
887 dyn->channels = HashTable_New(FALSE);
888 if (!dyn->channels)
889 goto fail;
890
891 if (!HashTable_SetHashFunction(dyn->channels, ChannelId_Hash))
892 goto fail;
893
894 {
895 wObject* kobj = HashTable_KeyObject(dyn->channels);
896 WINPR_ASSERT(kobj);
897 kobj->fnObjectEquals = ChannelId_Compare;
898 }
899
900 {
901 wObject* vobj = HashTable_ValueObject(dyn->channels);
902 WINPR_ASSERT(vobj);
903 vobj->fnObjectFree = DynamicChannelContext_free;
904 }
905
906 return dyn;
907
908fail:
909 DynChannelContext_free(dyn);
910 return nullptr;
911}
912
913WINPR_ATTR_NODISCARD
914static PfChannelResult pf_dynvc_back_data(proxyData* pdata,
915 const pServerStaticChannelContext* channel,
916 const BYTE* xdata, size_t xsize, UINT32 flags,
917 size_t totalSize)
918{
919 WINPR_ASSERT(channel);
920
921 DynChannelContext* dyn = (DynChannelContext*)channel->context;
922 WINPR_UNUSED(pdata);
923 WINPR_ASSERT(dyn);
924
925 return channelTracker_update(dyn->backTracker, xdata, xsize, flags, totalSize);
926}
927
928WINPR_ATTR_NODISCARD
929static PfChannelResult pf_dynvc_front_data(proxyData* pdata,
930 const pServerStaticChannelContext* channel,
931 const BYTE* xdata, size_t xsize, UINT32 flags,
932 size_t totalSize)
933{
934 WINPR_ASSERT(channel);
935
936 DynChannelContext* dyn = (DynChannelContext*)channel->context;
937 WINPR_UNUSED(pdata);
938 WINPR_ASSERT(dyn);
939
940 return channelTracker_update(dyn->frontTracker, xdata, xsize, flags, totalSize);
941}
942
943BOOL pf_channel_setup_drdynvc(proxyData* pdata, pServerStaticChannelContext* channel)
944{
945 DynChannelContext* ret = DynChannelContext_new(pdata, channel);
946 if (!ret)
947 return FALSE;
948
949 channel->onBackData = pf_dynvc_back_data;
950 channel->onFrontData = pf_dynvc_front_data;
951 channel->contextDtor = DynChannelContext_free;
952 channel->context = ret;
953 return TRUE;
954}
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59
WINPR_ATTR_NODISCARD OBJECT_EQUALS_FN fnObjectEquals
Definition collections.h:61