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 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "bulk.h"
25
26 #define TAG "com.freerdp.core"
27
28 //#define WITH_BULK_DEBUG 1
29 struct rdp_bulk
30 {
31 rdpContext* context;
32 UINT32 CompressionLevel;
33 UINT32 CompressionMaxSize;
34 MPPC_CONTEXT* mppcSend;
35 MPPC_CONTEXT* mppcRecv;
36 NCRUSH_CONTEXT* ncrushRecv;
37 NCRUSH_CONTEXT* ncrushSend;
38 XCRUSH_CONTEXT* xcrushRecv;
39 XCRUSH_CONTEXT* xcrushSend;
40 BYTE OutputBuffer[65536];
41 };
42
43 #if WITH_BULK_DEBUG
bulk_get_compression_flags_string(UINT32 flags)44 static INLINE const char* bulk_get_compression_flags_string(UINT32 flags)
45 {
46 flags &= BULK_COMPRESSION_FLAGS_MASK;
47
48 if (flags == 0)
49 return "PACKET_UNCOMPRESSED";
50 else if (flags == PACKET_COMPRESSED)
51 return "PACKET_COMPRESSED";
52 else if (flags == PACKET_AT_FRONT)
53 return "PACKET_AT_FRONT";
54 else if (flags == PACKET_FLUSHED)
55 return "PACKET_FLUSHED";
56 else if (flags == (PACKET_COMPRESSED | PACKET_AT_FRONT))
57 return "PACKET_COMPRESSED | PACKET_AT_FRONT";
58 else if (flags == (PACKET_COMPRESSED | PACKET_FLUSHED))
59 return "PACKET_COMPRESSED | PACKET_FLUSHED";
60 else if (flags == (PACKET_AT_FRONT | PACKET_FLUSHED))
61 return "PACKET_AT_FRONT | PACKET_FLUSHED";
62 else if (flags == (PACKET_COMPRESSED | PACKET_AT_FRONT | PACKET_FLUSHED))
63 return "PACKET_COMPRESSED | PACKET_AT_FRONT | PACKET_FLUSHED";
64
65 return "PACKET_UNKNOWN";
66 }
67 #endif
68
bulk_compression_level(rdpBulk * bulk)69 static UINT32 bulk_compression_level(rdpBulk* bulk)
70 {
71 rdpSettings* settings = bulk->context->settings;
72 bulk->CompressionLevel = (settings->CompressionLevel >= PACKET_COMPR_TYPE_RDP61)
73 ? PACKET_COMPR_TYPE_RDP61
74 : settings->CompressionLevel;
75 return bulk->CompressionLevel;
76 }
77
bulk_compression_max_size(rdpBulk * bulk)78 UINT32 bulk_compression_max_size(rdpBulk* bulk)
79 {
80 bulk_compression_level(bulk);
81 bulk->CompressionMaxSize = (bulk->CompressionLevel < PACKET_COMPR_TYPE_64K) ? 8192 : 65536;
82 return bulk->CompressionMaxSize;
83 }
84
85 #if WITH_BULK_DEBUG
bulk_compress_validate(rdpBulk * bulk,BYTE * pSrcData,UINT32 SrcSize,BYTE ** ppDstData,UINT32 * pDstSize,UINT32 * pFlags)86 static INLINE int bulk_compress_validate(rdpBulk* bulk, BYTE* pSrcData, UINT32 SrcSize,
87 BYTE** ppDstData, UINT32* pDstSize, UINT32* pFlags)
88 {
89 int status;
90 BYTE* _pSrcData = NULL;
91 BYTE* _pDstData = NULL;
92 UINT32 _SrcSize = 0;
93 UINT32 _DstSize = 0;
94 UINT32 _Flags = 0;
95 _pSrcData = *ppDstData;
96 _SrcSize = *pDstSize;
97 _Flags = *pFlags | bulk->CompressionLevel;
98 status = bulk_decompress(bulk, _pSrcData, _SrcSize, &_pDstData, &_DstSize, _Flags);
99
100 if (status < 0)
101 {
102 WLog_DBG(TAG, "compression/decompression failure");
103 return status;
104 }
105
106 if (_DstSize != SrcSize)
107 {
108 WLog_DBG(TAG,
109 "compression/decompression size mismatch: Actual: %" PRIu32 ", Expected: %" PRIu32
110 "",
111 _DstSize, SrcSize);
112 return -1;
113 }
114
115 if (memcmp(_pDstData, pSrcData, SrcSize) != 0)
116 {
117 WLog_DBG(TAG, "compression/decompression input/output mismatch! flags: 0x%08" PRIX32 "",
118 _Flags);
119 #if 1
120 WLog_DBG(TAG, "Actual:");
121 winpr_HexDump(TAG, WLOG_DEBUG, _pDstData, SrcSize);
122 WLog_DBG(TAG, "Expected:");
123 winpr_HexDump(TAG, WLOG_DEBUG, pSrcData, SrcSize);
124 #endif
125 return -1;
126 }
127
128 return status;
129 }
130 #endif
131
bulk_decompress(rdpBulk * bulk,BYTE * pSrcData,UINT32 SrcSize,BYTE ** ppDstData,UINT32 * pDstSize,UINT32 flags)132 int bulk_decompress(rdpBulk* bulk, BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData,
133 UINT32* pDstSize, UINT32 flags)
134 {
135 UINT32 type;
136 int status = -1;
137 rdpMetrics* metrics;
138 UINT32 CompressedBytes;
139 UINT32 UncompressedBytes;
140 double CompressionRatio;
141 metrics = bulk->context->metrics;
142 bulk_compression_max_size(bulk);
143 type = flags & BULK_COMPRESSION_TYPE_MASK;
144
145 if (flags & BULK_COMPRESSION_FLAGS_MASK)
146 {
147 switch (type)
148 {
149 case PACKET_COMPR_TYPE_8K:
150 mppc_set_compression_level(bulk->mppcRecv, 0);
151 status =
152 mppc_decompress(bulk->mppcRecv, pSrcData, SrcSize, ppDstData, pDstSize, flags);
153 break;
154
155 case PACKET_COMPR_TYPE_64K:
156 mppc_set_compression_level(bulk->mppcRecv, 1);
157 status =
158 mppc_decompress(bulk->mppcRecv, pSrcData, SrcSize, ppDstData, pDstSize, flags);
159 break;
160
161 case PACKET_COMPR_TYPE_RDP6:
162 status = ncrush_decompress(bulk->ncrushRecv, pSrcData, SrcSize, ppDstData, pDstSize,
163 flags);
164 break;
165
166 case PACKET_COMPR_TYPE_RDP61:
167 status = xcrush_decompress(bulk->xcrushRecv, pSrcData, SrcSize, ppDstData, pDstSize,
168 flags);
169 break;
170
171 case PACKET_COMPR_TYPE_RDP8:
172 WLog_ERR(TAG, "Unsupported bulk compression type %08" PRIx32,
173 bulk->CompressionLevel);
174 status = -1;
175 break;
176 default:
177 WLog_ERR(TAG, "Unknown bulk compression type %08" PRIx32, bulk->CompressionLevel);
178 status = -1;
179 break;
180 }
181 }
182 else
183 {
184 *ppDstData = pSrcData;
185 *pDstSize = SrcSize;
186 status = 0;
187 }
188
189 if (status >= 0)
190 {
191 CompressedBytes = SrcSize;
192 UncompressedBytes = *pDstSize;
193 CompressionRatio = metrics_write_bytes(metrics, UncompressedBytes, CompressedBytes);
194 #ifdef WITH_BULK_DEBUG
195 {
196 WLog_DBG(TAG,
197 "Decompress Type: %" PRIu32 " Flags: %s (0x%08" PRIX32
198 ") Compression Ratio: %f (%" PRIu32 " / %" PRIu32 "), Total: %f (%" PRIu64
199 " / %" PRIu64 ")",
200 type, bulk_get_compression_flags_string(flags), flags, CompressionRatio,
201 CompressedBytes, UncompressedBytes, metrics->TotalCompressionRatio,
202 metrics->TotalCompressedBytes, metrics->TotalUncompressedBytes);
203 }
204 #else
205 WINPR_UNUSED(CompressionRatio);
206 #endif
207 }
208 else
209 {
210 WLog_ERR(TAG, "Decompression failure!");
211 }
212
213 return status;
214 }
215
bulk_compress(rdpBulk * bulk,BYTE * pSrcData,UINT32 SrcSize,BYTE ** ppDstData,UINT32 * pDstSize,UINT32 * pFlags)216 int bulk_compress(rdpBulk* bulk, BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData, UINT32* pDstSize,
217 UINT32* pFlags)
218 {
219 int status = -1;
220 rdpMetrics* metrics;
221 UINT32 CompressedBytes;
222 UINT32 UncompressedBytes;
223 double CompressionRatio;
224 metrics = bulk->context->metrics;
225
226 if ((SrcSize <= 50) || (SrcSize >= 16384))
227 {
228 *ppDstData = pSrcData;
229 *pDstSize = SrcSize;
230 return 0;
231 }
232
233 *ppDstData = bulk->OutputBuffer;
234 *pDstSize = sizeof(bulk->OutputBuffer);
235 bulk_compression_level(bulk);
236 bulk_compression_max_size(bulk);
237
238 switch (bulk->CompressionLevel)
239 {
240 case PACKET_COMPR_TYPE_8K:
241 case PACKET_COMPR_TYPE_64K:
242 mppc_set_compression_level(bulk->mppcSend, bulk->CompressionLevel);
243 status = mppc_compress(bulk->mppcSend, pSrcData, SrcSize, ppDstData, pDstSize, pFlags);
244 break;
245 case PACKET_COMPR_TYPE_RDP6:
246 status =
247 ncrush_compress(bulk->ncrushSend, pSrcData, SrcSize, ppDstData, pDstSize, pFlags);
248 break;
249 case PACKET_COMPR_TYPE_RDP61:
250 status =
251 xcrush_compress(bulk->xcrushSend, pSrcData, SrcSize, ppDstData, pDstSize, pFlags);
252 break;
253 case PACKET_COMPR_TYPE_RDP8:
254 WLog_ERR(TAG, "Unsupported bulk compression type %08" PRIx32, bulk->CompressionLevel);
255 status = -1;
256 break;
257 default:
258 WLog_ERR(TAG, "Unknown bulk compression type %08" PRIx32, bulk->CompressionLevel);
259 status = -1;
260 break;
261 }
262
263 if (status >= 0)
264 {
265 CompressedBytes = *pDstSize;
266 UncompressedBytes = SrcSize;
267 CompressionRatio = metrics_write_bytes(metrics, UncompressedBytes, CompressedBytes);
268 #ifdef WITH_BULK_DEBUG
269 {
270 WLog_DBG(TAG,
271 "Compress Type: %" PRIu32 " Flags: %s (0x%08" PRIX32
272 ") Compression Ratio: %f (%" PRIu32 " / %" PRIu32 "), Total: %f (%" PRIu64
273 " / %" PRIu64 ")",
274 bulk->CompressionLevel, bulk_get_compression_flags_string(*pFlags), *pFlags,
275 CompressionRatio, CompressedBytes, UncompressedBytes,
276 metrics->TotalCompressionRatio, metrics->TotalCompressedBytes,
277 metrics->TotalUncompressedBytes);
278 }
279 #else
280 WINPR_UNUSED(CompressionRatio);
281 #endif
282 }
283
284 #if WITH_BULK_DEBUG
285
286 if (bulk_compress_validate(bulk, pSrcData, SrcSize, ppDstData, pDstSize, pFlags) < 0)
287 status = -1;
288
289 #endif
290 return status;
291 }
292
bulk_reset(rdpBulk * bulk)293 void bulk_reset(rdpBulk* bulk)
294 {
295 mppc_context_reset(bulk->mppcSend, FALSE);
296 mppc_context_reset(bulk->mppcRecv, FALSE);
297 ncrush_context_reset(bulk->ncrushRecv, FALSE);
298 ncrush_context_reset(bulk->ncrushSend, FALSE);
299 xcrush_context_reset(bulk->xcrushRecv, FALSE);
300 xcrush_context_reset(bulk->xcrushSend, FALSE);
301 }
302
bulk_new(rdpContext * context)303 rdpBulk* bulk_new(rdpContext* context)
304 {
305 rdpBulk* bulk;
306 bulk = (rdpBulk*)calloc(1, sizeof(rdpBulk));
307
308 if (bulk)
309 {
310 bulk->context = context;
311 bulk->mppcSend = mppc_context_new(1, TRUE);
312 bulk->mppcRecv = mppc_context_new(1, FALSE);
313 bulk->ncrushRecv = ncrush_context_new(FALSE);
314 bulk->ncrushSend = ncrush_context_new(TRUE);
315 bulk->xcrushRecv = xcrush_context_new(FALSE);
316 bulk->xcrushSend = xcrush_context_new(TRUE);
317 bulk->CompressionLevel = context->settings->CompressionLevel;
318 }
319
320 return bulk;
321 }
322
bulk_free(rdpBulk * bulk)323 void bulk_free(rdpBulk* bulk)
324 {
325 if (!bulk)
326 return;
327
328 mppc_context_free(bulk->mppcSend);
329 mppc_context_free(bulk->mppcRecv);
330 ncrush_context_free(bulk->ncrushRecv);
331 ncrush_context_free(bulk->ncrushSend);
332 xcrush_context_free(bulk->xcrushRecv);
333 xcrush_context_free(bulk->xcrushSend);
334 free(bulk);
335 }
336