FreeRDP
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 
36 struct 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)
51 static 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 
76 static 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 
90 UINT16 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)
99 static 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 
150 int 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 
240 int 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 
323 void 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 
335 rdpBulk* 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;
367 fail:
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 
375 void 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 }