Coverage Report

Created: 2023-09-25 06:56

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