Coverage Report

Created: 2024-09-08 06:20

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