FreeRDP
Loading...
Searching...
No Matches
bulk.c
1
20#include <math.h>
21#include <winpr/assert.h>
22
23#include <freerdp/config.h>
24
25#include "../core/settings.h"
26#include "bulk.h"
27#include "../codec/mppc.h"
28#include "../codec/ncrush.h"
29#include "../codec/xcrush.h"
30
31#include <freerdp/log.h>
32#define TAG FREERDP_TAG("core")
33
34//#define WITH_BULK_DEBUG 1
35
36struct rdp_bulk
37{
38 ALIGN64 rdpContext* context;
39 ALIGN64 UINT32 CompressionLevel;
40 ALIGN64 UINT16 CompressionMaxSize;
41 ALIGN64 MPPC_CONTEXT* mppcSend;
42 ALIGN64 MPPC_CONTEXT* mppcRecv;
43 ALIGN64 NCRUSH_CONTEXT* ncrushRecv;
44 ALIGN64 NCRUSH_CONTEXT* ncrushSend;
45 ALIGN64 XCRUSH_CONTEXT* xcrushRecv;
46 ALIGN64 XCRUSH_CONTEXT* xcrushSend;
47 ALIGN64 BYTE OutputBuffer[65536];
48};
49
50#if defined(WITH_BULK_DEBUG)
51static INLINE const char* bulk_get_compression_flags_string(UINT32 flags)
52{
53 flags &= BULK_COMPRESSION_FLAGS_MASK;
54
55 if (flags == 0)
56 return "PACKET_UNCOMPRESSED";
57 else if (flags == PACKET_COMPRESSED)
58 return "PACKET_COMPRESSED";
59 else if (flags == PACKET_AT_FRONT)
60 return "PACKET_AT_FRONT";
61 else if (flags == PACKET_FLUSHED)
62 return "PACKET_FLUSHED";
63 else if (flags == (PACKET_COMPRESSED | PACKET_AT_FRONT))
64 return "PACKET_COMPRESSED | PACKET_AT_FRONT";
65 else if (flags == (PACKET_COMPRESSED | PACKET_FLUSHED))
66 return "PACKET_COMPRESSED | PACKET_FLUSHED";
67 else if (flags == (PACKET_AT_FRONT | PACKET_FLUSHED))
68 return "PACKET_AT_FRONT | PACKET_FLUSHED";
69 else if (flags == (PACKET_COMPRESSED | PACKET_AT_FRONT | PACKET_FLUSHED))
70 return "PACKET_COMPRESSED | PACKET_AT_FRONT | PACKET_FLUSHED";
71
72 return "PACKET_UNKNOWN";
73}
74#endif
75
76static UINT32 bulk_compression_level(rdpBulk* WINPR_RESTRICT bulk)
77{
78 rdpSettings* settings = NULL;
79 WINPR_ASSERT(bulk);
80 WINPR_ASSERT(bulk->context);
81 settings = bulk->context->settings;
82 WINPR_ASSERT(settings);
83 bulk->CompressionLevel = (settings->CompressionLevel >= PACKET_COMPR_TYPE_RDP61)
84 ? PACKET_COMPR_TYPE_RDP61
85 : settings->CompressionLevel;
86 WINPR_ASSERT(bulk->CompressionLevel <= UINT16_MAX);
87 return bulk->CompressionLevel;
88}
89
90UINT16 bulk_compression_max_size(rdpBulk* WINPR_RESTRICT bulk)
91{
92 WINPR_ASSERT(bulk);
93 (void)bulk_compression_level(bulk);
94 bulk->CompressionMaxSize = (bulk->CompressionLevel < PACKET_COMPR_TYPE_64K) ? 8192 : UINT16_MAX;
95 return bulk->CompressionMaxSize;
96}
97
98#if defined(WITH_BULK_DEBUG)
99static INLINE int bulk_compress_validate(rdpBulk* bulk, const BYTE* pSrcData, UINT32 SrcSize,
100 const BYTE* pDstData, UINT32 DstSize, UINT32 Flags)
101{
102 int status;
103 const BYTE* v_pSrcData = NULL;
104 const BYTE* v_pDstData = NULL;
105 UINT32 v_SrcSize = 0;
106 UINT32 v_DstSize = 0;
107 UINT32 v_Flags = 0;
108
109 WINPR_ASSERT(bulk);
110 WINPR_ASSERT(pSrcData);
111 WINPR_ASSERT(pDstData);
112
113 v_pSrcData = pDstData;
114 v_SrcSize = DstSize;
115 v_Flags = Flags | bulk->CompressionLevel;
116 status = bulk_decompress(bulk, v_pSrcData, v_SrcSize, &v_pDstData, &v_DstSize, v_Flags);
117
118 if (status < 0)
119 {
120 WLog_DBG(TAG, "compression/decompression failure");
121 return status;
122 }
123
124 if (v_DstSize != SrcSize)
125 {
126 WLog_DBG(TAG,
127 "compression/decompression size mismatch: Actual: %" PRIu32 ", Expected: %" PRIu32
128 "",
129 v_DstSize, SrcSize);
130 return -1;
131 }
132
133 if (memcmp(v_pDstData, pSrcData, SrcSize) != 0)
134 {
135 WLog_DBG(TAG, "compression/decompression input/output mismatch! flags: 0x%08" PRIX32 "",
136 v_Flags);
137#if 1
138 WLog_DBG(TAG, "Actual:");
139 winpr_HexDump(TAG, WLOG_DEBUG, v_pDstData, SrcSize);
140 WLog_DBG(TAG, "Expected:");
141 winpr_HexDump(TAG, WLOG_DEBUG, pSrcData, SrcSize);
142#endif
143 return -1;
144 }
145
146 return status;
147}
148#endif
149
150int bulk_decompress(rdpBulk* WINPR_RESTRICT bulk, const BYTE* WINPR_RESTRICT pSrcData,
151 UINT32 SrcSize, const BYTE** WINPR_RESTRICT ppDstData,
152 UINT32* WINPR_RESTRICT pDstSize, UINT32 flags)
153{
154 int status = -1;
155
156 WINPR_ASSERT(bulk);
157 WINPR_ASSERT(bulk->context);
158 WINPR_ASSERT(pSrcData);
159 WINPR_ASSERT(ppDstData);
160 WINPR_ASSERT(pDstSize);
161
162 rdpMetrics* metrics = bulk->context->metrics;
163 WINPR_ASSERT(metrics);
164
165 (void)bulk_compression_max_size(bulk);
166 const UINT32 type = flags & BULK_COMPRESSION_TYPE_MASK;
167
168 if (flags & BULK_COMPRESSION_FLAGS_MASK)
169 {
170 switch (type)
171 {
172 case PACKET_COMPR_TYPE_8K:
173 mppc_set_compression_level(bulk->mppcRecv, 0);
174 status =
175 mppc_decompress(bulk->mppcRecv, pSrcData, SrcSize, ppDstData, pDstSize, flags);
176 break;
177
178 case PACKET_COMPR_TYPE_64K:
179 mppc_set_compression_level(bulk->mppcRecv, 1);
180 status =
181 mppc_decompress(bulk->mppcRecv, pSrcData, SrcSize, ppDstData, pDstSize, flags);
182 break;
183
184 case PACKET_COMPR_TYPE_RDP6:
185 status = ncrush_decompress(bulk->ncrushRecv, pSrcData, SrcSize, ppDstData, pDstSize,
186 flags);
187 break;
188
189 case PACKET_COMPR_TYPE_RDP61:
190 status = xcrush_decompress(bulk->xcrushRecv, pSrcData, SrcSize, ppDstData, pDstSize,
191 flags);
192 break;
193
194 case PACKET_COMPR_TYPE_RDP8:
195 WLog_ERR(TAG, "Unsupported bulk compression type %08" PRIx32,
196 bulk->CompressionLevel);
197 status = -1;
198 break;
199 default:
200 WLog_ERR(TAG, "Unknown bulk compression type %08" PRIx32, bulk->CompressionLevel);
201 status = -1;
202 break;
203 }
204 }
205 else
206 {
207 *ppDstData = pSrcData;
208 *pDstSize = SrcSize;
209 status = 0;
210 }
211
212 if (status >= 0)
213 {
214 const UINT32 CompressedBytes = SrcSize;
215 const UINT32 UncompressedBytes = *pDstSize;
216 const double CompressionRatio =
217 metrics_write_bytes(metrics, UncompressedBytes, CompressedBytes);
218#ifdef WITH_BULK_DEBUG
219 {
220 WLog_DBG(TAG,
221 "Decompress Type: %" PRIu32 " Flags: %s (0x%08" PRIX32
222 ") Compression Ratio: %f (%" PRIu32 " / %" PRIu32 "), Total: %f (%" PRIu64
223 " / %" PRIu64 ")",
224 type, bulk_get_compression_flags_string(flags), flags, CompressionRatio,
225 CompressedBytes, UncompressedBytes, metrics->TotalCompressionRatio,
226 metrics->TotalCompressedBytes, metrics->TotalUncompressedBytes);
227 }
228#else
229 WINPR_UNUSED(CompressionRatio);
230#endif
231 }
232 else
233 {
234 WLog_ERR(TAG, "Decompression failure!");
235 }
236
237 return status;
238}
239
240int bulk_compress(rdpBulk* WINPR_RESTRICT bulk, const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize,
241 const BYTE** WINPR_RESTRICT ppDstData, UINT32* WINPR_RESTRICT pDstSize,
242 UINT32* WINPR_RESTRICT pFlags)
243{
244 int status = -1;
245
246 WINPR_ASSERT(bulk);
247 WINPR_ASSERT(bulk->context);
248 WINPR_ASSERT(pSrcData);
249 WINPR_ASSERT(ppDstData);
250 WINPR_ASSERT(pDstSize);
251
252 rdpMetrics* metrics = bulk->context->metrics;
253 WINPR_ASSERT(metrics);
254
255 if ((SrcSize <= 50) || (SrcSize >= 16384))
256 {
257 *ppDstData = pSrcData;
258 *pDstSize = SrcSize;
259 return 0;
260 }
261
262 *pDstSize = sizeof(bulk->OutputBuffer);
263 (void)bulk_compression_level(bulk);
264 (void)bulk_compression_max_size(bulk);
265
266 switch (bulk->CompressionLevel)
267 {
268 case PACKET_COMPR_TYPE_8K:
269 case PACKET_COMPR_TYPE_64K:
270 mppc_set_compression_level(bulk->mppcSend, bulk->CompressionLevel);
271 status = mppc_compress(bulk->mppcSend, pSrcData, SrcSize, bulk->OutputBuffer, ppDstData,
272 pDstSize, pFlags);
273 break;
274 case PACKET_COMPR_TYPE_RDP6:
275 status = ncrush_compress(bulk->ncrushSend, pSrcData, SrcSize, bulk->OutputBuffer,
276 ppDstData, pDstSize, pFlags);
277 break;
278 case PACKET_COMPR_TYPE_RDP61:
279 status = xcrush_compress(bulk->xcrushSend, pSrcData, SrcSize, bulk->OutputBuffer,
280 ppDstData, pDstSize, pFlags);
281 break;
282 case PACKET_COMPR_TYPE_RDP8:
283 WLog_ERR(TAG, "Unsupported bulk compression type %08" PRIx32, bulk->CompressionLevel);
284 status = -1;
285 break;
286 default:
287 WLog_ERR(TAG, "Unknown bulk compression type %08" PRIx32, bulk->CompressionLevel);
288 status = -1;
289 break;
290 }
291
292 if (status >= 0)
293 {
294 const UINT32 CompressedBytes = *pDstSize;
295 const UINT32 UncompressedBytes = SrcSize;
296 const double CompressionRatio =
297 metrics_write_bytes(metrics, UncompressedBytes, CompressedBytes);
298#ifdef WITH_BULK_DEBUG
299 {
300 WLog_DBG(TAG,
301 "Compress Type: %" PRIu32 " Flags: %s (0x%08" PRIX32
302 ") Compression Ratio: %f (%" PRIu32 " / %" PRIu32 "), Total: %f (%" PRIu64
303 " / %" PRIu64 ")",
304 bulk->CompressionLevel, bulk_get_compression_flags_string(*pFlags), *pFlags,
305 CompressionRatio, CompressedBytes, UncompressedBytes,
306 metrics->TotalCompressionRatio, metrics->TotalCompressedBytes,
307 metrics->TotalUncompressedBytes);
308 }
309#else
310 WINPR_UNUSED(CompressionRatio);
311#endif
312 }
313
314#if defined(WITH_BULK_DEBUG)
315
316 if (bulk_compress_validate(bulk, pSrcData, SrcSize, *ppDstData, *pDstSize, *pFlags) < 0)
317 status = -1;
318
319#endif
320 return status;
321}
322
323void bulk_reset(rdpBulk* WINPR_RESTRICT bulk)
324{
325 WINPR_ASSERT(bulk);
326
327 mppc_context_reset(bulk->mppcSend, FALSE);
328 mppc_context_reset(bulk->mppcRecv, FALSE);
329 ncrush_context_reset(bulk->ncrushRecv, FALSE);
330 ncrush_context_reset(bulk->ncrushSend, FALSE);
331 xcrush_context_reset(bulk->xcrushRecv, FALSE);
332 xcrush_context_reset(bulk->xcrushSend, FALSE);
333}
334
335rdpBulk* bulk_new(rdpContext* context)
336{
337 rdpBulk* bulk = NULL;
338 WINPR_ASSERT(context);
339
340 bulk = (rdpBulk*)calloc(1, sizeof(rdpBulk));
341
342 if (!bulk)
343 goto fail;
344
345 bulk->context = context;
346 bulk->mppcSend = mppc_context_new(1, TRUE);
347 if (!bulk->mppcSend)
348 goto fail;
349 bulk->mppcRecv = mppc_context_new(1, FALSE);
350 if (!bulk->mppcRecv)
351 goto fail;
352 bulk->ncrushRecv = ncrush_context_new(FALSE);
353 if (!bulk->ncrushRecv)
354 goto fail;
355 bulk->ncrushSend = ncrush_context_new(TRUE);
356 if (!bulk->ncrushSend)
357 goto fail;
358 bulk->xcrushRecv = xcrush_context_new(FALSE);
359 if (!bulk->xcrushRecv)
360 goto fail;
361 bulk->xcrushSend = xcrush_context_new(TRUE);
362 if (!bulk->xcrushSend)
363 goto fail;
364 bulk->CompressionLevel = context->settings->CompressionLevel;
365
366 return bulk;
367fail:
368 WINPR_PRAGMA_DIAG_PUSH
369 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
370 bulk_free(bulk);
371 WINPR_PRAGMA_DIAG_POP
372 return NULL;
373}
374
375void bulk_free(rdpBulk* bulk)
376{
377 if (!bulk)
378 return;
379
380 mppc_context_free(bulk->mppcSend);
381 mppc_context_free(bulk->mppcRecv);
382 ncrush_context_free(bulk->ncrushRecv);
383 ncrush_context_free(bulk->ncrushSend);
384 xcrush_context_free(bulk->xcrushRecv);
385 xcrush_context_free(bulk->xcrushSend);
386 free(bulk);
387}