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