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