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