FreeRDP
Loading...
Searching...
No Matches
pf_channel.c
1
18#include <winpr/assert.h>
19#include <winpr/cast.h>
20
21#include <freerdp/freerdp.h>
22#include <freerdp/server/proxy/proxy_log.h>
23
24#include "proxy_modules.h"
25#include "pf_channel.h"
26#include "pf_client.h"
27#include "pf_server.h"
28
29#define TAG PROXY_TAG("channel")
30
32struct sChannelStateTracker
33{
34 pServerStaticChannelContext* channel;
35 ChannelTrackerMode mode;
36 wStream* currentPacket;
37 size_t currentPacketReceived;
38 size_t currentPacketSize;
39 size_t currentPacketFragments;
40
41 ChannelTrackerPeekFn peekFn;
42 void* trackerData;
43 proxyData* pdata;
44};
45
46WINPR_ATTR_NODISCARD
47static BOOL channelTracker_resetCurrentPacket(ChannelStateTracker* tracker)
48{
49 WINPR_ASSERT(tracker);
50
51 BOOL create = TRUE;
52 if (tracker->currentPacket)
53 {
54 const size_t cap = Stream_Capacity(tracker->currentPacket);
55 if (cap < 1ULL * 1000ULL * 1000ULL)
56 create = FALSE;
57 else
58 Stream_Free(tracker->currentPacket, TRUE);
59 }
60
61 if (create)
62 tracker->currentPacket = Stream_New(nullptr, 10ULL * 1024ULL);
63 if (!tracker->currentPacket)
64 return FALSE;
65 Stream_ResetPosition(tracker->currentPacket);
66 return TRUE;
67}
68
69ChannelStateTracker* channelTracker_new(pServerStaticChannelContext* channel,
70 ChannelTrackerPeekFn fn, void* data)
71{
72 ChannelStateTracker* ret = calloc(1, sizeof(ChannelStateTracker));
73 if (!ret)
74 return ret;
75
76 WINPR_ASSERT(fn);
77
78 ret->channel = channel;
79 ret->peekFn = fn;
80
81 if (!channelTracker_setCustomData(ret, data))
82 goto fail;
83
84 if (!channelTracker_resetCurrentPacket(ret))
85 goto fail;
86
87 return ret;
88
89fail:
90 WINPR_PRAGMA_DIAG_PUSH
91 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
92 channelTracker_free(ret);
93 WINPR_PRAGMA_DIAG_POP
94 return nullptr;
95}
96
97PfChannelResult channelTracker_update(ChannelStateTracker* tracker, const BYTE* xdata, size_t xsize,
98 UINT32 flags, size_t totalSize)
99{
100 PfChannelResult result = PF_CHANNEL_RESULT_ERROR;
101 BOOL firstPacket = (flags & CHANNEL_FLAG_FIRST) != 0;
102 BOOL lastPacket = (flags & CHANNEL_FLAG_LAST) != 0;
103
104 WINPR_ASSERT(tracker);
105
106 WLog_VRB(TAG, "channelTracker_update(%s): sz=%" PRIuz " first=%d last=%d",
107 tracker->channel->channel_name, xsize, firstPacket, lastPacket);
108 if (flags & CHANNEL_FLAG_FIRST)
109 {
110 /* A new packet may only start once the previous one was completed with
111 * CHANNEL_FLAG_LAST, same as drdynvc_virtual_channel_event_data_received */
112 if (tracker->currentPacketFragments != 0)
113 {
114 WLog_WARN(TAG,
115 "new packet started while the previous one is incomplete (%" PRIuz
116 " of %" PRIuz " bytes received)",
117 tracker->currentPacketReceived, channelTracker_getCurrentPacketSize(tracker));
118 return PF_CHANNEL_RESULT_ERROR;
119 }
120
121 /* The mode is a decision about the current packet only */
122 channelTracker_setMode(tracker, CHANNEL_TRACKER_PEEK);
123 if (!channelTracker_resetCurrentPacket(tracker))
124 return PF_CHANNEL_RESULT_ERROR;
125 channelTracker_setCurrentPacketSize(tracker, totalSize);
126 tracker->currentPacketReceived = 0;
127 tracker->currentPacketFragments = 0;
128 }
129
130 {
131 const size_t currentPacketSize = channelTracker_getCurrentPacketSize(tracker);
132 if ((xsize > currentPacketSize) || (currentPacketSize != totalSize))
133 {
134 WLog_WARN(TAG,
135 "current fragment size is bigger (%" PRIuz ") than total size (%" PRIuz ")",
136 xsize, currentPacketSize);
137 return PF_CHANNEL_RESULT_ERROR;
138 }
139
140 if (xsize > SIZE_MAX - tracker->currentPacketReceived)
141 {
142 WLog_WARN(TAG,
143 "current fragment size is overflowing size_t (%" PRIuz
144 ") when added to (%" PRIuz ")",
145 xsize, tracker->currentPacketReceived);
146 return PF_CHANNEL_RESULT_ERROR;
147 }
148
149 tracker->currentPacketReceived += xsize;
150 tracker->currentPacketFragments++;
151 if (tracker->currentPacketReceived > currentPacketSize)
152 {
153 WLog_WARN(TAG, "cumulated size is bigger (%" PRIuz ") than total size (%" PRIuz ")",
154 tracker->currentPacketReceived + xsize, currentPacketSize);
155 return PF_CHANNEL_RESULT_ERROR;
156 }
157 else if ((tracker->currentPacketReceived == currentPacketSize) && !lastPacket)
158 return PF_CHANNEL_RESULT_ERROR;
159 }
160
161 switch (channelTracker_getMode(tracker))
162 {
163 case CHANNEL_TRACKER_PEEK:
164 {
165 wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
166 if (!Stream_EnsureRemainingCapacity(currentPacket, xsize))
167 return PF_CHANNEL_RESULT_ERROR;
168
169 Stream_Write(currentPacket, xdata, xsize);
170
171 WINPR_ASSERT(tracker->peekFn);
172 result = tracker->peekFn(tracker, firstPacket, lastPacket);
173 }
174 break;
175 case CHANNEL_TRACKER_PASS:
176 result = PF_CHANNEL_RESULT_PASS;
177 break;
178 case CHANNEL_TRACKER_DROP:
179 result = PF_CHANNEL_RESULT_DROP;
180 break;
181 default:
182 break;
183 }
184
185 if (lastPacket)
186 {
187 const size_t currentPacketSize = channelTracker_getCurrentPacketSize(tracker);
188 channelTracker_setMode(tracker, CHANNEL_TRACKER_PEEK);
189
190 if (tracker->currentPacketReceived != currentPacketSize)
191 WLog_INFO(TAG, "cumulated size(%" PRIuz ") does not match total size (%" PRIuz ")",
192 tracker->currentPacketReceived, currentPacketSize);
193
194 /* the packet is complete, the next fragment must start a new one */
195 tracker->currentPacketFragments = 0;
196 }
197
198 return result;
199}
200
201void channelTracker_free(ChannelStateTracker* t)
202{
203 if (!t)
204 return;
205
206 Stream_Free(t->currentPacket, TRUE);
207 free(t);
208}
209
216PfChannelResult channelTracker_flushCurrent(ChannelStateTracker* t, BOOL first, BOOL last,
217 BOOL toBack)
218{
219 UINT32 flags = CHANNEL_FLAG_FIRST;
220 BOOL r = 0;
221 const char* direction = toBack ? "F->B" : "B->F";
222 const size_t currentPacketSize = channelTracker_getCurrentPacketSize(t);
223 wStream* currentPacket = channelTracker_getCurrentPacket(t);
224
225 WINPR_ASSERT(t);
226
227 WLog_VRB(TAG, "channelTracker_flushCurrent(%s): %s sz=%" PRIuz " first=%d last=%d",
228 t->channel->channel_name, direction, Stream_GetPosition(currentPacket), first, last);
229
230 if (first)
231 return PF_CHANNEL_RESULT_PASS;
232
233 proxyData* pdata = t->pdata;
234 pServerStaticChannelContext* channel = t->channel;
235 if (last)
236 flags |= CHANNEL_FLAG_LAST;
237
238 if (toBack)
239 {
240 proxyChannelDataEventInfo ev = WINPR_C_ARRAY_INIT;
241
242 ev.channel_id = WINPR_ASSERTING_INT_CAST(UINT16, channel->front_channel_id);
243 ev.channel_name = channel->channel_name;
244 ev.data = Stream_Buffer(currentPacket);
245 ev.data_len = Stream_GetPosition(currentPacket);
246 ev.flags = flags;
247 ev.total_size = currentPacketSize;
248
249 pClientContext* pc = proxy_data_get_client_context(pdata);
250 if (!pc->sendChannelData)
251 return PF_CHANNEL_RESULT_ERROR;
252
253 return pc->sendChannelData(pc, &ev) ? PF_CHANNEL_RESULT_DROP : PF_CHANNEL_RESULT_ERROR;
254 }
255
256 pServerContext* ps = proxy_data_get_server_context(pdata);
257 r = ps->context.peer->SendChannelPacket(
258 ps->context.peer, WINPR_ASSERTING_INT_CAST(UINT16, channel->front_channel_id),
259 currentPacketSize, flags, Stream_Buffer(currentPacket), Stream_GetPosition(currentPacket));
260
261 return r ? PF_CHANNEL_RESULT_DROP : PF_CHANNEL_RESULT_ERROR;
262}
263
264WINPR_ATTR_NODISCARD
265static PfChannelResult pf_channel_generic_back_data(proxyData* pdata,
266 const pServerStaticChannelContext* channel,
267 const BYTE* xdata, size_t xsize, UINT32 flags,
268 size_t totalSize)
269{
270 proxyChannelDataEventInfo ev = WINPR_C_ARRAY_INIT;
271
272 WINPR_ASSERT(pdata);
273 WINPR_ASSERT(channel);
274
275 switch (channel->channelMode)
276 {
277 case PF_UTILS_CHANNEL_PASSTHROUGH:
278 ev.channel_id = WINPR_ASSERTING_INT_CAST(UINT16, channel->back_channel_id);
279 ev.channel_name = channel->channel_name;
280 ev.data = xdata;
281 ev.data_len = xsize;
282 ev.flags = flags;
283 ev.total_size = totalSize;
284
285 if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA,
286 pdata, &ev))
287 return PF_CHANNEL_RESULT_DROP; /* Silently drop */
288
289 return PF_CHANNEL_RESULT_PASS;
290
291 case PF_UTILS_CHANNEL_INTERCEPT:
292 /* TODO */
293 case PF_UTILS_CHANNEL_BLOCK:
294 default:
295 return PF_CHANNEL_RESULT_DROP;
296 }
297}
298
299WINPR_ATTR_NODISCARD
300static PfChannelResult pf_channel_generic_front_data(proxyData* pdata,
301 const pServerStaticChannelContext* channel,
302 const BYTE* xdata, size_t xsize, UINT32 flags,
303 size_t totalSize)
304{
305 proxyChannelDataEventInfo ev = WINPR_C_ARRAY_INIT;
306
307 WINPR_ASSERT(pdata);
308 WINPR_ASSERT(channel);
309
310 switch (channel->channelMode)
311 {
312 case PF_UTILS_CHANNEL_PASSTHROUGH:
313 ev.channel_id = WINPR_ASSERTING_INT_CAST(UINT16, channel->front_channel_id);
314 ev.channel_name = channel->channel_name;
315 ev.data = xdata;
316 ev.data_len = xsize;
317 ev.flags = flags;
318 ev.total_size = totalSize;
319
320 if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA,
321 pdata, &ev))
322 return PF_CHANNEL_RESULT_DROP; /* Silently drop */
323
324 return PF_CHANNEL_RESULT_PASS;
325
326 case PF_UTILS_CHANNEL_INTERCEPT:
327 /* TODO */
328 case PF_UTILS_CHANNEL_BLOCK:
329 default:
330 return PF_CHANNEL_RESULT_DROP;
331 }
332}
333
334BOOL pf_channel_setup_generic(pServerStaticChannelContext* channel)
335{
336 WINPR_ASSERT(channel);
337 channel->onBackData = pf_channel_generic_back_data;
338 channel->onFrontData = pf_channel_generic_front_data;
339 return TRUE;
340}
341
342BOOL channelTracker_setMode(ChannelStateTracker* tracker, ChannelTrackerMode mode)
343{
344 WINPR_ASSERT(tracker);
345 tracker->mode = mode;
346 return TRUE;
347}
348
349ChannelTrackerMode channelTracker_getMode(ChannelStateTracker* tracker)
350{
351 WINPR_ASSERT(tracker);
352 return tracker->mode;
353}
354
355BOOL channelTracker_setPData(ChannelStateTracker* tracker, proxyData* pdata)
356{
357 WINPR_ASSERT(tracker);
358 tracker->pdata = pdata;
359 return TRUE;
360}
361
362proxyData* channelTracker_getPData(ChannelStateTracker* tracker)
363{
364 WINPR_ASSERT(tracker);
365 return tracker->pdata;
366}
367
368wStream* channelTracker_getCurrentPacket(ChannelStateTracker* tracker)
369{
370 WINPR_ASSERT(tracker);
371 return tracker->currentPacket;
372}
373
374BOOL channelTracker_setCustomData(ChannelStateTracker* tracker, void* data)
375{
376 WINPR_ASSERT(tracker);
377 tracker->trackerData = data;
378 return TRUE;
379}
380
381void* channelTracker_getCustomData(ChannelStateTracker* tracker)
382{
383 WINPR_ASSERT(tracker);
384 return tracker->trackerData;
385}
386
387size_t channelTracker_getCurrentPacketSize(ChannelStateTracker* tracker)
388{
389 WINPR_ASSERT(tracker);
390 return tracker->currentPacketSize;
391}
392
393BOOL channelTracker_setCurrentPacketSize(ChannelStateTracker* tracker, size_t size)
394{
395 WINPR_ASSERT(tracker);
396 tracker->currentPacketSize = size;
397 return TRUE;
398}