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 currentDataLength;
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 PfChannelResult DynvcTrackerPeekHandleByMode(ChannelStateTracker* tracker,
284 DynChannelTrackerState* trackerState,
285 pServerDynamicChannelContext* dynChannel,
286 BYTE cmd, BOOL firstPacket, BOOL lastPacket)
287{
288 WINPR_ASSERT(tracker);
289 WINPR_ASSERT(trackerState);
290 WINPR_ASSERT(dynChannel);
291 PfChannelResult result = PF_CHANNEL_RESULT_ERROR;
292
293 DynChannelContext* dynChannelContext =
294 (DynChannelContext*)channelTracker_getCustomData(tracker);
295 WINPR_ASSERT(dynChannelContext);
296
297 proxyData* pdata = channelTracker_getPData(tracker);
298 WINPR_ASSERT(pdata);
299
300 const BOOL isBackData = (tracker == dynChannelContext->backTracker);
301 switch (dynChannel->channelMode)
302 {
303 case PF_UTILS_CHANNEL_PASSTHROUGH:
304 result = channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
305 break;
306 case PF_UTILS_CHANNEL_BLOCK:
307 channelTracker_setMode(tracker, CHANNEL_TRACKER_DROP);
308 result = PF_CHANNEL_RESULT_DROP;
309 break;
310 case PF_UTILS_CHANNEL_INTERCEPT:
311 if (trackerState->dataCallback)
312 {
313 pServerContext* ps = proxy_data_get_server_context(pdata);
314 result = trackerState->dataCallback(ps, dynChannel, isBackData, tracker,
315 firstPacket, lastPacket);
316 }
317 else
318 {
319 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
320 "no intercept callback for channel, dropping packet");
321 result = PF_CHANNEL_RESULT_DROP;
322 }
323 break;
324 default:
325 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
326 "unknown channel mode %u", dynChannel->channelMode);
327 result = PF_CHANNEL_RESULT_ERROR;
328 break;
329 }
330
331 if (!trackerState->currentDataLength ||
332 (trackerState->CurrentDataReceived == trackerState->currentDataLength))
333 {
334 trackerState->currentDataLength = 0;
335 trackerState->CurrentDataFragments = 0;
336 trackerState->CurrentDataReceived = 0;
337
338 if (dynChannel->packetReassembly && trackerState->currentPacket)
339 Stream_ResetPosition(trackerState->currentPacket);
340 }
341
342 return result;
343}
344
345WINPR_ATTR_NODISCARD
346static PfChannelResult DynvcTrackerHandleClose(ChannelStateTracker* tracker,
347 pServerDynamicChannelContext* dynChannel,
348 DynChannelContext* dynChannelContext,
349 BOOL firstPacket, BOOL lastPacket)
350{
351 WINPR_ASSERT(dynChannelContext);
352
353 const BOOL isBackData = (tracker == dynChannelContext->backTracker);
354
355 if (!lastPacket || !dynChannel)
356 return PF_CHANNEL_RESULT_DROP;
357
358 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, CLOSE_REQUEST_PDU, isBackData,
359 "Close request");
360 channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
361 if (dynChannel->openStatus != CHANNEL_OPENSTATE_OPENED)
362 {
363 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, CLOSE_REQUEST_PDU,
364 isBackData, "is in state %s, expected %s",
365 openstatus2str(dynChannel->openStatus),
366 openstatus2str(CHANNEL_OPENSTATE_OPENED));
367 }
368 dynChannel->openStatus = CHANNEL_OPENSTATE_CLOSED;
369 return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
370}
371
372WINPR_ATTR_NODISCARD
373static PfChannelResult DynvcTrackerHandleCreateBack(ChannelStateTracker* tracker, wStream* s,
374 DWORD flags, proxyData* pdata,
375 pServerDynamicChannelContext* dynChannel,
376 DynChannelContext* dynChannelContext,
377 UINT64 dynChannelId)
378{
379 proxyChannelDataEventInfo dev = WINPR_C_ARRAY_INIT;
380 const char* name = Stream_ConstPointer(s);
381 const size_t nameLen = Stream_GetRemainingLength(s);
382 const size_t len = strnlen(name, nameLen);
383 const BOOL isBackData = (tracker == dynChannelContext->backTracker);
384 const BYTE cmd = CREATE_REQUEST_PDU;
385
386 if ((len == 0) || (len == nameLen) || (dynChannelId > UINT16_MAX))
387 {
388 char namebuffer[64] = WINPR_C_ARRAY_INIT;
389 if (len <= INT32_MAX)
390 (void)_snprintf(namebuffer, sizeof(namebuffer) - 1, "%.*s",
391 WINPR_ASSERTING_INT_CAST(int, len), name);
392 else
393 (void)_snprintf(namebuffer, sizeof(namebuffer) - 1, "<LENGTH OUT OF BOUND>[%" PRIuz "]",
394 len);
395
396 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
397 "channel id %" PRIu64 ", name=%s [%" PRIuz "|%" PRIuz "], status=%s",
398 dynChannelId, namebuffer, len, nameLen,
399 dynChannel ? openstatus2str(dynChannel->openStatus) : "nullptr");
400 return PF_CHANNEL_RESULT_ERROR;
401 }
402
403 wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
404 dev.channel_id = (UINT16)dynChannelId;
405 dev.channel_name = name;
406 dev.data = Stream_Buffer(s);
407 dev.data_len = Stream_GetPosition(currentPacket);
408 dev.flags = flags;
409 dev.total_size = Stream_GetPosition(currentPacket);
410
411 if (dynChannel)
412 {
413 DynvcTrackerLog(dynChannelContext->log, WLOG_WARN, dynChannel, cmd, isBackData,
414 "Reusing channel id, now %s", name);
415
416 HashTable_Remove(dynChannelContext->channels, &dynChannel->channelId);
417 }
418
419 if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE,
420 pdata, &dev))
421 return PF_CHANNEL_RESULT_DROP; /* Silently drop */
422
423 pServerContext* ps = proxy_data_get_server_context(pdata);
424 dynChannel = DynamicChannelContext_new(dynChannelContext->log, ps, name, (UINT32)dynChannelId);
425 if (!dynChannel)
426 {
427 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
428 "unable to create dynamic channel context data");
429 return PF_CHANNEL_RESULT_ERROR;
430 }
431
432 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
433 "Adding channel");
434 if (!HashTable_Insert(dynChannelContext->channels, &dynChannel->channelId, dynChannel))
435 {
436 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
437 "unable register dynamic channel context data");
438 DynamicChannelContext_free(dynChannel);
439 return PF_CHANNEL_RESULT_ERROR;
440 }
441
442 dynChannel->openStatus = CHANNEL_OPENSTATE_WAITING_OPEN_STATUS;
443
444 const BOOL firstPacket = (flags & CHANNEL_FLAG_FIRST) != 0;
445 const BOOL lastPacket = (flags & CHANNEL_FLAG_LAST) != 0;
446
447 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert owns dynChannel
448 return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, FALSE);
449}
450
451WINPR_ATTR_NODISCARD
452static PfChannelResult DynvcTrackerHandleCreateFront(ChannelStateTracker* tracker, wStream* s,
453 DWORD flags,
454 WINPR_ATTR_UNUSED proxyData* pdata,
455 pServerDynamicChannelContext* dynChannel,
456 DynChannelContext* dynChannelContext,
457 WINPR_ATTR_UNUSED UINT64 dynChannelId)
458{
459 const BOOL isBackData = (tracker == dynChannelContext->backTracker);
460 const BYTE cmd = CREATE_REQUEST_PDU;
461
462 /* CREATE_REQUEST_PDU response */
463 if (!Stream_CheckAndLogRequiredLengthWLogWithBackend(dynChannelContext->log, s, 4, FALSE))
464 return PF_CHANNEL_RESULT_ERROR;
465
466 const UINT32 creationStatus = Stream_Get_UINT32(s);
467 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
468 "CREATE_RESPONSE openStatus=%" PRIu32, creationStatus);
469
470 if (dynChannel && (creationStatus == 0))
471 dynChannel->openStatus = CHANNEL_OPENSTATE_OPENED;
472
473 const BOOL firstPacket = (flags & CHANNEL_FLAG_FIRST) != 0;
474 const BOOL lastPacket = (flags & CHANNEL_FLAG_LAST) != 0;
475
476 return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, TRUE);
477}
478
479WINPR_ATTR_NODISCARD
480static PfChannelResult DynvcTrackerHandleCreate(ChannelStateTracker* tracker, wStream* s,
481 DWORD flags,
482 pServerDynamicChannelContext* dynChannel,
483 UINT64 dynChannelId)
484{
485 WINPR_ASSERT(tracker);
486 WINPR_ASSERT(s);
487
488 DynChannelContext* dynChannelContext =
489 (DynChannelContext*)channelTracker_getCustomData(tracker);
490 WINPR_ASSERT(dynChannelContext);
491
492 const BOOL lastPacket = (flags & CHANNEL_FLAG_LAST) != 0;
493 const BOOL isBackData = (tracker == dynChannelContext->backTracker);
494
495 proxyData* pdata = channelTracker_getPData(tracker);
496 WINPR_ASSERT(pdata);
497
498 /* we only want the full packet */
499 if (!lastPacket)
500 return PF_CHANNEL_RESULT_DROP;
501
502 if (isBackData)
503 return DynvcTrackerHandleCreateBack(tracker, s, flags, pdata, dynChannel, dynChannelContext,
504 dynChannelId);
505
506 return DynvcTrackerHandleCreateFront(tracker, s, flags, pdata, dynChannel, dynChannelContext,
507 dynChannelId);
508}
509
510WINPR_ATTR_NODISCARD
511static PfChannelResult DynvcTrackerHandleCmdDATA(ChannelStateTracker* tracker,
512 pServerDynamicChannelContext* dynChannel,
513 wStream* s, BYTE cmd, UINT64 Length,
514 BOOL firstPacket, BOOL lastPacket)
515{
516 WINPR_ASSERT(tracker);
517 WINPR_ASSERT(s);
518
519 DynChannelContext* dynChannelContext =
520 (DynChannelContext*)channelTracker_getCustomData(tracker);
521 WINPR_ASSERT(dynChannelContext);
522
523 const BOOL isBackData = (tracker == dynChannelContext->backTracker);
524
525 if (!dynChannel)
526 {
527 DynvcTrackerLog(dynChannelContext->log, WLOG_WARN, dynChannel, cmd, isBackData,
528 "channel is nullptr, dropping packet");
529 return PF_CHANNEL_RESULT_DROP;
530 }
531
532 DynChannelTrackerState* trackerState =
533 isBackData ? &dynChannel->backTracker : &dynChannel->frontTracker;
534 if (dynChannel->openStatus != CHANNEL_OPENSTATE_OPENED)
535 {
536 DynvcTrackerLog(dynChannelContext->log, WLOG_WARN, dynChannel, cmd, isBackData,
537 "channel is not opened, dropping packet");
538 return PF_CHANNEL_RESULT_DROP;
539 }
540
541 switch (cmd)
542 {
543 case DATA_FIRST_PDU:
544 case DATA_FIRST_COMPRESSED_PDU:
545 {
546 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
547 "DATA_FIRST currentPacketLength=%" PRIu64 "", Length);
548 if (Length > UINT32_MAX)
549 {
550 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
551 "Length out of bounds: %" PRIu64, Length);
552 return PF_CHANNEL_RESULT_ERROR;
553 }
554 trackerState->currentDataLength = (UINT32)Length;
555 trackerState->CurrentDataReceived = 0;
556 trackerState->CurrentDataFragments = 0;
557
558 if (dynChannel->packetReassembly)
559 {
560 if (trackerState->currentPacket)
561 Stream_ResetPosition(trackerState->currentPacket);
562 }
563 }
564 break;
565 default:
566 break;
567 }
568
569 switch (cmd)
570 {
571 case DATA_PDU:
572 case DATA_FIRST_PDU:
573 {
574 size_t extraSize = Stream_GetRemainingLength(s);
575
576 trackerState->CurrentDataFragments++;
577 trackerState->CurrentDataReceived += WINPR_ASSERTING_INT_CAST(uint32_t, extraSize);
578
579 if (dynChannel->packetReassembly)
580 {
581 if (!trackerState->currentPacket)
582 {
583 trackerState->currentPacket = Stream_New(nullptr, 1024);
584 if (!trackerState->currentPacket)
585 {
586 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd,
587 isBackData, "unable to create current packet",
588 getDirection(isBackData), dynChannel->channelName,
589 drdynvc_get_packet_type(cmd));
590 return PF_CHANNEL_RESULT_ERROR;
591 }
592 }
593
594 if (!Stream_EnsureRemainingCapacity(trackerState->currentPacket, extraSize))
595 {
596 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
597 "unable to grow current packet", getDirection(isBackData),
598 dynChannel->channelName, drdynvc_get_packet_type(cmd));
599 return PF_CHANNEL_RESULT_ERROR;
600 }
601
602 Stream_Write(trackerState->currentPacket, Stream_ConstPointer(s), extraSize);
603 }
604 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
605 "frags=%" PRIu32 " received=%" PRIu32 "(%" PRIu32 ")",
606 trackerState->CurrentDataFragments, trackerState->CurrentDataReceived,
607 trackerState->currentDataLength);
608 }
609 break;
610 default:
611 break;
612 }
613
614 switch (cmd)
615 {
616 case DATA_PDU:
617 {
618 if (trackerState->currentDataLength)
619 {
620 if (trackerState->CurrentDataReceived > trackerState->currentDataLength)
621 {
622 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
623 "reassembled packet (%" PRIu32
624 ") is bigger than announced length (%" PRIu32 ")",
625 trackerState->CurrentDataReceived,
626 trackerState->currentDataLength);
627 return PF_CHANNEL_RESULT_ERROR;
628 }
629 }
630 else
631 {
632 trackerState->CurrentDataFragments = 0;
633 trackerState->CurrentDataReceived = 0;
634 }
635 }
636 break;
637 default:
638 break;
639 }
640
641 return DynvcTrackerPeekHandleByMode(tracker, trackerState, dynChannel, cmd, firstPacket,
642 lastPacket);
643}
644
645WINPR_ATTR_NODISCARD
646static PfChannelResult DynvcTrackerHandleCmd(ChannelStateTracker* tracker,
647 pServerDynamicChannelContext* dynChannel, wStream* s,
648 BYTE cmd, UINT32 flags, UINT64 Length,
649 UINT64 dynChannelId, BOOL firstPacket, BOOL lastPacket)
650{
651 WINPR_ASSERT(tracker);
652 WINPR_ASSERT(s);
653
654 DynChannelContext* dynChannelContext =
655 (DynChannelContext*)channelTracker_getCustomData(tracker);
656 WINPR_ASSERT(dynChannelContext);
657
658 const BOOL isBackData = (tracker == dynChannelContext->backTracker);
659 switch (cmd)
660 {
661 case CAPABILITY_REQUEST_PDU:
662 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
663 "CAPABILITY_%s", isBackData ? "REQUEST" : "RESPONSE");
664 channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
665 return PF_CHANNEL_RESULT_PASS;
666
667 case CREATE_REQUEST_PDU:
668 return DynvcTrackerHandleCreate(tracker, s, flags, dynChannel, dynChannelId);
669
670 case CLOSE_REQUEST_PDU:
671 return DynvcTrackerHandleClose(tracker, dynChannel, dynChannelContext, firstPacket,
672 lastPacket);
673
674 case SOFT_SYNC_REQUEST_PDU:
675 /* just pass then as is for now */
676 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
677 "SOFT_SYNC_REQUEST_PDU");
678 channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
679 /*TODO: return pf_treat_softsync_req(pdata, s);*/
680 return PF_CHANNEL_RESULT_PASS;
681
682 case SOFT_SYNC_RESPONSE_PDU:
683 /* just pass then as is for now */
684 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
685 "SOFT_SYNC_RESPONSE_PDU");
686 channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
687 return PF_CHANNEL_RESULT_PASS;
688
689 case DATA_FIRST_PDU:
690 case DATA_PDU:
691 return DynvcTrackerHandleCmdDATA(tracker, dynChannel, s, cmd, Length, firstPacket,
692 lastPacket);
693
694 case DATA_FIRST_COMPRESSED_PDU:
695 case DATA_COMPRESSED_PDU:
696 DynvcTrackerLog(dynChannelContext->log, WLOG_DEBUG, dynChannel, cmd, isBackData,
697 "TODO: compressed data packets, pass them as is for now");
698 channelTracker_setMode(tracker, CHANNEL_TRACKER_PASS);
699 return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
700
701 default:
702 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
703 "Invalid command ID");
704 return PF_CHANNEL_RESULT_ERROR;
705 }
706}
707
708WINPR_ATTR_NODISCARD
709static PfChannelResult DynvcTrackerPeekFn(ChannelStateTracker* tracker, BOOL firstPacket,
710 BOOL lastPacket)
711{
712 wStream* s = nullptr;
713 wStream sbuffer;
714 BOOL haveChannelId = 0;
715 BOOL haveLength = 0;
716 UINT64 dynChannelId = 0;
717 UINT64 Length = 0;
718 pServerDynamicChannelContext* dynChannel = nullptr;
719
720 WINPR_ASSERT(tracker);
721
722 DynChannelContext* dynChannelContext =
723 (DynChannelContext*)channelTracker_getCustomData(tracker);
724 WINPR_ASSERT(dynChannelContext);
725
726 const BOOL isBackData = (tracker == dynChannelContext->backTracker);
727
728 UINT32 flags = lastPacket ? CHANNEL_FLAG_LAST : 0;
729 if (firstPacket)
730 flags |= CHANNEL_FLAG_FIRST;
731
732 {
733 wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
734 s = Stream_StaticConstInit(&sbuffer, Stream_Buffer(currentPacket),
735 Stream_GetPosition(currentPacket));
736 }
737
738 if (!Stream_CheckAndLogRequiredLengthWLogWithBackend(dynChannelContext->log, s, 1, isBackData))
739 return PF_CHANNEL_RESULT_ERROR;
740
741 const BYTE byte0 = Stream_Get_UINT8(s);
742 const BYTE cmd = byte0 >> 4;
743
744 switch (cmd)
745 {
746 case CREATE_REQUEST_PDU:
747 case CLOSE_REQUEST_PDU:
748 case DATA_PDU:
749 case DATA_COMPRESSED_PDU:
750 haveChannelId = TRUE;
751 haveLength = FALSE;
752 break;
753 case DATA_FIRST_PDU:
754 case DATA_FIRST_COMPRESSED_PDU:
755 haveLength = TRUE;
756 haveChannelId = TRUE;
757 break;
758 default:
759 haveChannelId = FALSE;
760 haveLength = FALSE;
761 break;
762 }
763
764 if (haveChannelId)
765 {
766 BYTE cbId = byte0 & 0x03;
767
768 switch (dynvc_read_varInt(dynChannelContext->log, s, cbId, &dynChannelId, lastPacket))
769 {
770 case DYNCVC_READ_OK:
771 break;
772 case DYNCVC_READ_INCOMPLETE:
773 return PF_CHANNEL_RESULT_DROP;
774 case DYNCVC_READ_ERROR:
775 default:
776 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
777 "invalid channelId field");
778 return PF_CHANNEL_RESULT_ERROR;
779 }
780
781 /* we always try to retrieve the dynamic channel in case it would have been opened
782 * and closed
783 */
784 dynChannel = (pServerDynamicChannelContext*)HashTable_GetItemValue(
785 dynChannelContext->channels, &dynChannelId);
786 if ((cmd != CREATE_REQUEST_PDU) || !isBackData)
787 {
788 if (!dynChannel || (dynChannel->openStatus == CHANNEL_OPENSTATE_CLOSED))
789 {
790 /* we've not found the target channel, so we drop this chunk, plus all the rest of
791 * the packet */
792 channelTracker_setMode(tracker, CHANNEL_TRACKER_DROP);
793 return PF_CHANNEL_RESULT_DROP;
794 }
795 }
796 }
797
798 if (haveLength)
799 {
800 BYTE lenLen = (byte0 >> 2) & 0x03;
801 switch (dynvc_read_varInt(dynChannelContext->log, s, lenLen, &Length, lastPacket))
802 {
803 case DYNCVC_READ_OK:
804 break;
805 case DYNCVC_READ_INCOMPLETE:
806 return PF_CHANNEL_RESULT_DROP;
807 case DYNCVC_READ_ERROR:
808 default:
809 DynvcTrackerLog(dynChannelContext->log, WLOG_ERROR, dynChannel, cmd, isBackData,
810 "invalid length field");
811 return PF_CHANNEL_RESULT_ERROR;
812 }
813 }
814
815 return DynvcTrackerHandleCmd(tracker, dynChannel, s, cmd, flags, Length, dynChannelId,
816 firstPacket, lastPacket);
817}
818
819static void DynChannelContext_free(void* context)
820{
821 DynChannelContext* c = context;
822 if (!c)
823 return;
824 channelTracker_free(c->backTracker);
825 channelTracker_free(c->frontTracker);
826 HashTable_Free(c->channels);
827 WLog_Discard(c->log);
828 free(c);
829}
830
831WINPR_ATTR_NODISCARD
832static const char* dynamic_context(void* arg)
833{
834 proxyData* pdata = arg;
835 if (!pdata)
836 return "pdata=null";
837 return pdata->session_id;
838}
839
840WINPR_ATTR_MALLOC(DynChannelContext_free, 1)
841static DynChannelContext* DynChannelContext_new(proxyData* pdata,
842 pServerStaticChannelContext* channel)
843{
844 DynChannelContext* dyn = calloc(1, sizeof(DynChannelContext));
845 if (!dyn)
846 return nullptr;
847
848 dyn->log = WLog_Create(DTAG, WLog_GetRoot());
849 if (!dyn->log)
850 goto fail;
851
852 if (!WLog_SetContext(dyn->log, dynamic_context, pdata))
853 goto fail;
854
855 dyn->backTracker = channelTracker_new(channel, DynvcTrackerPeekFn, dyn);
856 if (!dyn->backTracker)
857 goto fail;
858 if (!channelTracker_setPData(dyn->backTracker, pdata))
859 goto fail;
860
861 dyn->frontTracker = channelTracker_new(channel, DynvcTrackerPeekFn, dyn);
862 if (!dyn->frontTracker)
863 goto fail;
864 if (!channelTracker_setPData(dyn->frontTracker, pdata))
865 goto fail;
866
867 dyn->channels = HashTable_New(FALSE);
868 if (!dyn->channels)
869 goto fail;
870
871 if (!HashTable_SetHashFunction(dyn->channels, ChannelId_Hash))
872 goto fail;
873
874 {
875 wObject* kobj = HashTable_KeyObject(dyn->channels);
876 WINPR_ASSERT(kobj);
877 kobj->fnObjectEquals = ChannelId_Compare;
878 }
879
880 {
881 wObject* vobj = HashTable_ValueObject(dyn->channels);
882 WINPR_ASSERT(vobj);
883 vobj->fnObjectFree = DynamicChannelContext_free;
884 }
885
886 return dyn;
887
888fail:
889 DynChannelContext_free(dyn);
890 return nullptr;
891}
892
893WINPR_ATTR_NODISCARD
894static PfChannelResult pf_dynvc_back_data(proxyData* pdata,
895 const pServerStaticChannelContext* channel,
896 const BYTE* xdata, size_t xsize, UINT32 flags,
897 size_t totalSize)
898{
899 WINPR_ASSERT(channel);
900
901 DynChannelContext* dyn = (DynChannelContext*)channel->context;
902 WINPR_UNUSED(pdata);
903 WINPR_ASSERT(dyn);
904
905 return channelTracker_update(dyn->backTracker, xdata, xsize, flags, totalSize);
906}
907
908WINPR_ATTR_NODISCARD
909static PfChannelResult pf_dynvc_front_data(proxyData* pdata,
910 const pServerStaticChannelContext* channel,
911 const BYTE* xdata, size_t xsize, UINT32 flags,
912 size_t totalSize)
913{
914 WINPR_ASSERT(channel);
915
916 DynChannelContext* dyn = (DynChannelContext*)channel->context;
917 WINPR_UNUSED(pdata);
918 WINPR_ASSERT(dyn);
919
920 return channelTracker_update(dyn->frontTracker, xdata, xsize, flags, totalSize);
921}
922
923BOOL pf_channel_setup_drdynvc(proxyData* pdata, pServerStaticChannelContext* channel)
924{
925 DynChannelContext* ret = DynChannelContext_new(pdata, channel);
926 if (!ret)
927 return FALSE;
928
929 channel->onBackData = pf_dynvc_back_data;
930 channel->onFrontData = pf_dynvc_front_data;
931 channel->contextDtor = DynChannelContext_free;
932 channel->context = ret;
933 return TRUE;
934}
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