Coverage Report

Created: 2024-05-20 06:11

/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* bulk)
77
1.92k
{
78
1.92k
  rdpSettings* settings = NULL;
79
1.92k
  WINPR_ASSERT(bulk);
80
1.92k
  WINPR_ASSERT(bulk->context);
81
1.92k
  settings = bulk->context->settings;
82
1.92k
  WINPR_ASSERT(settings);
83
1.92k
  bulk->CompressionLevel = (settings->CompressionLevel >= PACKET_COMPR_TYPE_RDP61)
84
1.92k
                               ? PACKET_COMPR_TYPE_RDP61
85
1.92k
                               : settings->CompressionLevel;
86
1.92k
  return bulk->CompressionLevel;
87
1.92k
}
88
89
UINT32 bulk_compression_max_size(rdpBulk* bulk)
90
1.92k
{
91
1.92k
  WINPR_ASSERT(bulk);
92
1.92k
  bulk_compression_level(bulk);
93
1.92k
  bulk->CompressionMaxSize = (bulk->CompressionLevel < PACKET_COMPR_TYPE_64K) ? 8192 : 65536;
94
1.92k
  return bulk->CompressionMaxSize;
95
1.92k
}
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* bulk, const BYTE* pSrcData, UINT32 SrcSize, const BYTE** ppDstData,
150
                    UINT32* pDstSize, UINT32 flags)
151
1.37k
{
152
1.37k
  UINT32 type = 0;
153
1.37k
  int status = -1;
154
1.37k
  rdpMetrics* metrics = NULL;
155
1.37k
  UINT32 CompressedBytes = 0;
156
1.37k
  UINT32 UncompressedBytes = 0;
157
1.37k
  double CompressionRatio = NAN;
158
159
1.37k
  WINPR_ASSERT(bulk);
160
1.37k
  WINPR_ASSERT(bulk->context);
161
1.37k
  WINPR_ASSERT(pSrcData);
162
1.37k
  WINPR_ASSERT(ppDstData);
163
1.37k
  WINPR_ASSERT(pDstSize);
164
165
1.37k
  metrics = bulk->context->metrics;
166
1.37k
  WINPR_ASSERT(metrics);
167
168
1.37k
  bulk_compression_max_size(bulk);
169
1.37k
  type = flags & BULK_COMPRESSION_TYPE_MASK;
170
171
1.37k
  if (flags & BULK_COMPRESSION_FLAGS_MASK)
172
1.37k
  {
173
1.37k
    switch (type)
174
1.37k
    {
175
214
      case PACKET_COMPR_TYPE_8K:
176
214
        mppc_set_compression_level(bulk->mppcRecv, 0);
177
214
        status =
178
214
            mppc_decompress(bulk->mppcRecv, pSrcData, SrcSize, ppDstData, pDstSize, flags);
179
214
        break;
180
181
513
      case PACKET_COMPR_TYPE_64K:
182
513
        mppc_set_compression_level(bulk->mppcRecv, 1);
183
513
        status =
184
513
            mppc_decompress(bulk->mppcRecv, pSrcData, SrcSize, ppDstData, pDstSize, flags);
185
513
        break;
186
187
287
      case PACKET_COMPR_TYPE_RDP6:
188
287
        status = ncrush_decompress(bulk->ncrushRecv, pSrcData, SrcSize, ppDstData, pDstSize,
189
287
                                   flags);
190
287
        break;
191
192
289
      case PACKET_COMPR_TYPE_RDP61:
193
289
        status = xcrush_decompress(bulk->xcrushRecv, pSrcData, SrcSize, ppDstData, pDstSize,
194
289
                                   flags);
195
289
        break;
196
197
5
      case PACKET_COMPR_TYPE_RDP8:
198
5
        WLog_ERR(TAG, "Unsupported bulk compression type %08" PRIx32,
199
5
                 bulk->CompressionLevel);
200
5
        status = -1;
201
5
        break;
202
64
      default:
203
64
        WLog_ERR(TAG, "Unknown bulk compression type %08" PRIx32, bulk->CompressionLevel);
204
64
        status = -1;
205
64
        break;
206
1.37k
    }
207
1.37k
  }
208
0
  else
209
0
  {
210
0
    *ppDstData = pSrcData;
211
0
    *pDstSize = SrcSize;
212
0
    status = 0;
213
0
  }
214
215
1.37k
  if (status >= 0)
216
704
  {
217
704
    CompressedBytes = SrcSize;
218
704
    UncompressedBytes = *pDstSize;
219
704
    CompressionRatio = metrics_write_bytes(metrics, UncompressedBytes, CompressedBytes);
220
#ifdef WITH_BULK_DEBUG
221
    {
222
      WLog_DBG(TAG,
223
               "Decompress Type: %" PRIu32 " Flags: %s (0x%08" PRIX32
224
               ") Compression Ratio: %f (%" PRIu32 " / %" PRIu32 "), Total: %f (%" PRIu64
225
               " / %" PRIu64 ")",
226
               type, bulk_get_compression_flags_string(flags), flags, CompressionRatio,
227
               CompressedBytes, UncompressedBytes, metrics->TotalCompressionRatio,
228
               metrics->TotalCompressedBytes, metrics->TotalUncompressedBytes);
229
    }
230
#else
231
704
    WINPR_UNUSED(CompressionRatio);
232
704
#endif
233
704
  }
234
668
  else
235
668
  {
236
668
    WLog_ERR(TAG, "Decompression failure!");
237
668
  }
238
239
1.37k
  return status;
240
1.37k
}
241
242
int bulk_compress(rdpBulk* bulk, const BYTE* pSrcData, UINT32 SrcSize, const BYTE** ppDstData,
243
                  UINT32* pDstSize, UINT32* pFlags)
244
158
{
245
158
  int status = -1;
246
158
  rdpMetrics* metrics = NULL;
247
158
  UINT32 CompressedBytes = 0;
248
158
  UINT32 UncompressedBytes = 0;
249
158
  double CompressionRatio = NAN;
250
251
158
  WINPR_ASSERT(bulk);
252
158
  WINPR_ASSERT(bulk->context);
253
158
  WINPR_ASSERT(pSrcData);
254
158
  WINPR_ASSERT(ppDstData);
255
158
  WINPR_ASSERT(pDstSize);
256
257
158
  metrics = bulk->context->metrics;
258
158
  WINPR_ASSERT(metrics);
259
260
158
  if ((SrcSize <= 50) || (SrcSize >= 16384))
261
158
  {
262
158
    *ppDstData = pSrcData;
263
158
    *pDstSize = SrcSize;
264
158
    return 0;
265
158
  }
266
267
0
  *pDstSize = sizeof(bulk->OutputBuffer);
268
0
  bulk_compression_level(bulk);
269
0
  bulk_compression_max_size(bulk);
270
271
0
  switch (bulk->CompressionLevel)
272
0
  {
273
0
    case PACKET_COMPR_TYPE_8K:
274
0
    case PACKET_COMPR_TYPE_64K:
275
0
      mppc_set_compression_level(bulk->mppcSend, bulk->CompressionLevel);
276
0
      status = mppc_compress(bulk->mppcSend, pSrcData, SrcSize, bulk->OutputBuffer, ppDstData,
277
0
                             pDstSize, pFlags);
278
0
      break;
279
0
    case PACKET_COMPR_TYPE_RDP6:
280
0
      status = ncrush_compress(bulk->ncrushSend, pSrcData, SrcSize, bulk->OutputBuffer,
281
0
                               ppDstData, pDstSize, pFlags);
282
0
      break;
283
0
    case PACKET_COMPR_TYPE_RDP61:
284
0
      status = xcrush_compress(bulk->xcrushSend, pSrcData, SrcSize, bulk->OutputBuffer,
285
0
                               ppDstData, pDstSize, pFlags);
286
0
      break;
287
0
    case PACKET_COMPR_TYPE_RDP8:
288
0
      WLog_ERR(TAG, "Unsupported bulk compression type %08" PRIx32, bulk->CompressionLevel);
289
0
      status = -1;
290
0
      break;
291
0
    default:
292
0
      WLog_ERR(TAG, "Unknown bulk compression type %08" PRIx32, bulk->CompressionLevel);
293
0
      status = -1;
294
0
      break;
295
0
  }
296
297
0
  if (status >= 0)
298
0
  {
299
0
    CompressedBytes = *pDstSize;
300
0
    UncompressedBytes = SrcSize;
301
0
    CompressionRatio = 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
0
    WINPR_UNUSED(CompressionRatio);
315
0
#endif
316
0
  }
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
0
  return status;
325
0
}
326
327
void bulk_reset(rdpBulk* bulk)
328
0
{
329
0
  WINPR_ASSERT(bulk);
330
331
0
  mppc_context_reset(bulk->mppcSend, FALSE);
332
0
  mppc_context_reset(bulk->mppcRecv, FALSE);
333
0
  ncrush_context_reset(bulk->ncrushRecv, FALSE);
334
0
  ncrush_context_reset(bulk->ncrushSend, FALSE);
335
0
  xcrush_context_reset(bulk->xcrushRecv, FALSE);
336
0
  xcrush_context_reset(bulk->xcrushSend, FALSE);
337
0
}
338
339
rdpBulk* bulk_new(rdpContext* context)
340
14.9k
{
341
14.9k
  rdpBulk* bulk = NULL;
342
14.9k
  WINPR_ASSERT(context);
343
344
14.9k
  bulk = (rdpBulk*)calloc(1, sizeof(rdpBulk));
345
346
14.9k
  if (!bulk)
347
0
    goto fail;
348
349
14.9k
  bulk->context = context;
350
14.9k
  bulk->mppcSend = mppc_context_new(1, TRUE);
351
14.9k
  if (!bulk->mppcSend)
352
0
    goto fail;
353
14.9k
  bulk->mppcRecv = mppc_context_new(1, FALSE);
354
14.9k
  if (!bulk->mppcRecv)
355
0
    goto fail;
356
14.9k
  bulk->ncrushRecv = ncrush_context_new(FALSE);
357
14.9k
  if (!bulk->ncrushRecv)
358
0
    goto fail;
359
14.9k
  bulk->ncrushSend = ncrush_context_new(TRUE);
360
14.9k
  if (!bulk->ncrushSend)
361
0
    goto fail;
362
14.9k
  bulk->xcrushRecv = xcrush_context_new(FALSE);
363
14.9k
  if (!bulk->xcrushRecv)
364
0
    goto fail;
365
14.9k
  bulk->xcrushSend = xcrush_context_new(TRUE);
366
14.9k
  if (!bulk->xcrushSend)
367
0
    goto fail;
368
14.9k
  bulk->CompressionLevel = context->settings->CompressionLevel;
369
370
14.9k
  return bulk;
371
0
fail:
372
0
  WINPR_PRAGMA_DIAG_PUSH
373
0
  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
374
0
  bulk_free(bulk);
375
0
  WINPR_PRAGMA_DIAG_POP
376
0
  return NULL;
377
14.9k
}
378
379
void bulk_free(rdpBulk* bulk)
380
14.9k
{
381
14.9k
  if (!bulk)
382
0
    return;
383
384
14.9k
  mppc_context_free(bulk->mppcSend);
385
14.9k
  mppc_context_free(bulk->mppcRecv);
386
14.9k
  ncrush_context_free(bulk->ncrushRecv);
387
14.9k
  ncrush_context_free(bulk->ncrushSend);
388
14.9k
  xcrush_context_free(bulk->xcrushRecv);
389
14.9k
  xcrush_context_free(bulk->xcrushSend);
390
14.9k
  free(bulk);
391
14.9k
}