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