2 Unix SMB/CIFS implementation.
4 libndr compression support
6 Copyright (C) Stefan Metzmacher 2005
7 Copyright (C) Matthieu Suiche 2008
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "../lib/compression/lzxpress.h"
25 #include "librpc/ndr/libndr.h"
26 #include "../librpc/ndr/ndr_compression.h"
29 static voidpf
ndr_zlib_alloc(voidpf opaque
, uInt items
, uInt size
)
31 return talloc_zero_size(opaque
, items
* size
);
34 static void ndr_zlib_free(voidpf opaque
, voidpf address
)
39 static enum ndr_err_code
ndr_pull_compression_mszip_chunk(struct ndr_pull
*ndrpull
,
40 struct ndr_push
*ndrpush
,
45 uint32_t comp_chunk_offset
;
46 uint32_t comp_chunk_size
;
47 DATA_BLOB plain_chunk
;
48 uint32_t plain_chunk_offset
;
49 uint32_t plain_chunk_size
;
52 NDR_CHECK(ndr_pull_uint32(ndrpull
, NDR_SCALARS
, &plain_chunk_size
));
53 if (plain_chunk_size
> 0x00008000) {
54 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
, "Bad MSZIP plain chunk size %08X > 0x00008000 (PULL)",
58 NDR_CHECK(ndr_pull_uint32(ndrpull
, NDR_SCALARS
, &comp_chunk_size
));
60 DEBUG(9,("MSZIP plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
61 plain_chunk_size
, plain_chunk_size
, comp_chunk_size
, comp_chunk_size
));
63 comp_chunk_offset
= ndrpull
->offset
;
64 NDR_CHECK(ndr_pull_advance(ndrpull
, comp_chunk_size
));
65 comp_chunk
.length
= comp_chunk_size
;
66 comp_chunk
.data
= ndrpull
->data
+ comp_chunk_offset
;
68 plain_chunk_offset
= ndrpush
->offset
;
69 NDR_CHECK(ndr_push_zero(ndrpush
, plain_chunk_size
));
70 plain_chunk
.length
= plain_chunk_size
;
71 plain_chunk
.data
= ndrpush
->data
+ plain_chunk_offset
;
73 if (comp_chunk
.length
< 2) {
74 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
75 "Bad MSZIP comp chunk size %u < 2 (PULL)",
76 (unsigned int)comp_chunk
.length
);
78 /* CK = Chris Kirmse, official Microsoft purloiner */
79 if (comp_chunk
.data
[0] != 'C' ||
80 comp_chunk
.data
[1] != 'K') {
81 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
82 "Bad MSZIP invalid prefix [%c%c] != [CK]",
83 comp_chunk
.data
[0], comp_chunk
.data
[1]);
86 z
->next_in
= comp_chunk
.data
+ 2;
87 z
->avail_in
= comp_chunk
.length
-2;
90 z
->next_out
= plain_chunk
.data
;
91 z
->avail_out
= plain_chunk
.length
;
95 /* the first time we need to intialize completely */
96 z
->zalloc
= ndr_zlib_alloc
;
97 z
->zfree
= ndr_zlib_free
;
100 z_ret
= inflateInit2(z
, -15);
102 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
103 "Bad inflateInit2 error %s(%d) (PULL)",
104 zError(z_ret
), z_ret
);
109 /* call inflate untill we get Z_STREAM_END or an error */
111 z_ret
= inflate(z
, Z_BLOCK
);
112 if (z_ret
!= Z_OK
) break;
115 if (z_ret
!= Z_STREAM_END
) {
116 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
117 "Bad inflate(Z_BLOCK) error %s(%d) (PULL)",
118 zError(z_ret
), z_ret
);
122 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
123 "MSZIP not all avail_in[%u] bytes consumed (PULL)",
128 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
129 "MSZIP not all avail_out[%u] bytes consumed (PULL)",
133 if ((plain_chunk_size
< 0x00008000) || (ndrpull
->offset
+4 >= ndrpull
->data_size
)) {
134 /* this is the last chunk */
138 z_ret
= inflateReset(z
);
140 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
141 "Bad inflateReset error %s(%d) (PULL)",
142 zError(z_ret
), z_ret
);
145 z_ret
= inflateSetDictionary(z
, plain_chunk
.data
, plain_chunk
.length
);
147 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
148 "Bad inflateSetDictionary error %s(%d) (PULL)",
149 zError(z_ret
), z_ret
);
152 return NDR_ERR_SUCCESS
;
155 static enum ndr_err_code
ndr_push_compression_mszip_chunk(struct ndr_push
*ndrpush
,
156 struct ndr_pull
*ndrpull
,
160 DATA_BLOB comp_chunk
;
161 uint32_t comp_chunk_size
;
162 uint32_t comp_chunk_size_offset
;
163 DATA_BLOB plain_chunk
;
164 uint32_t plain_chunk_size
;
165 uint32_t plain_chunk_offset
;
166 uint32_t max_plain_size
= 0x00008000;
167 uint32_t max_comp_size
= 0x00008000 + 2 + 12 /*TODO: what value do we really need here?*/;
171 plain_chunk_size
= MIN(max_plain_size
, ndrpull
->data_size
- ndrpull
->offset
);
172 plain_chunk_offset
= ndrpull
->offset
;
173 NDR_CHECK(ndr_pull_advance(ndrpull
, plain_chunk_size
));
175 plain_chunk
.data
= ndrpull
->data
+ plain_chunk_offset
;
176 plain_chunk
.length
= plain_chunk_size
;
178 if (plain_chunk_size
< max_plain_size
) {
182 NDR_CHECK(ndr_push_uint32(ndrpush
, NDR_SCALARS
, plain_chunk_size
));
183 comp_chunk_size_offset
= ndrpush
->offset
;
184 NDR_CHECK(ndr_push_uint32(ndrpush
, NDR_SCALARS
, 0xFEFEFEFE));
186 NDR_CHECK(ndr_push_expand(ndrpush
, max_comp_size
));
188 comp_chunk
.data
= ndrpush
->data
+ ndrpush
->offset
;
189 comp_chunk
.length
= max_comp_size
;
191 /* CK = Chris Kirmse, official Microsoft purloiner */
192 comp_chunk
.data
[0] = 'C';
193 comp_chunk
.data
[1] = 'K';
195 z
->next_in
= plain_chunk
.data
;
196 z
->avail_in
= plain_chunk
.length
;
199 z
->next_out
= comp_chunk
.data
+ 2;
200 z
->avail_out
= comp_chunk
.length
- 2;
204 /* the first time we need to intialize completely */
205 z
->zalloc
= ndr_zlib_alloc
;
206 z
->zfree
= ndr_zlib_free
;
209 /* TODO: find how to trigger the same parameters windows uses */
210 z_ret
= deflateInit2(z
,
211 Z_DEFAULT_COMPRESSION
,
217 return ndr_push_error(ndrpush
, NDR_ERR_COMPRESSION
,
218 "Bad deflateInit2 error %s(%d) (PUSH)",
219 zError(z_ret
), z_ret
);
224 /* call deflate untill we get Z_STREAM_END or an error */
226 z_ret
= deflate(z
, Z_FINISH
);
227 if (z_ret
!= Z_OK
) break;
229 if (z_ret
!= Z_STREAM_END
) {
230 return ndr_push_error(ndrpush
, NDR_ERR_COMPRESSION
,
231 "Bad delate(Z_BLOCK) error %s(%d) (PUSH)",
232 zError(z_ret
), z_ret
);
236 return ndr_push_error(ndrpush
, NDR_ERR_COMPRESSION
,
237 "MSZIP not all avail_in[%u] bytes consumed (PUSH)",
241 comp_chunk_size
= 2 + z
->total_out
;
243 z_ret
= deflateReset(z
);
245 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
246 "Bad deflateReset error %s(%d) (PULL)",
247 zError(z_ret
), z_ret
);
250 z_ret
= deflateSetDictionary(z
, plain_chunk
.data
, plain_chunk
.length
);
252 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
253 "Bad deflateSetDictionary error %s(%d) (PULL)",
254 zError(z_ret
), z_ret
);
257 tmp_offset
= ndrpush
->offset
;
258 ndrpush
->offset
= comp_chunk_size_offset
;
259 NDR_CHECK(ndr_push_uint32(ndrpush
, NDR_SCALARS
, comp_chunk_size
));
260 ndrpush
->offset
= tmp_offset
;
262 DEBUG(9,("MSZIP comp plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
263 (unsigned int)plain_chunk
.length
,
264 (unsigned int)plain_chunk
.length
,
265 comp_chunk_size
, comp_chunk_size
));
267 ndrpush
->offset
+= comp_chunk_size
;
268 return NDR_ERR_SUCCESS
;
271 static enum ndr_err_code
ndr_pull_compression_xpress_chunk(struct ndr_pull
*ndrpull
,
272 struct ndr_push
*ndrpush
,
275 DATA_BLOB comp_chunk
;
276 DATA_BLOB plain_chunk
;
277 uint32_t comp_chunk_offset
;
278 uint32_t plain_chunk_offset
;
279 uint32_t comp_chunk_size
;
280 uint32_t plain_chunk_size
;
283 NDR_CHECK(ndr_pull_uint32(ndrpull
, NDR_SCALARS
, &plain_chunk_size
));
284 if (plain_chunk_size
> 0x00010000) {
285 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
, "Bad XPRESS plain chunk size %08X > 0x00010000 (PULL)",
289 NDR_CHECK(ndr_pull_uint32(ndrpull
, NDR_SCALARS
, &comp_chunk_size
));
291 comp_chunk_offset
= ndrpull
->offset
;
292 NDR_CHECK(ndr_pull_advance(ndrpull
, comp_chunk_size
));
293 comp_chunk
.length
= comp_chunk_size
;
294 comp_chunk
.data
= ndrpull
->data
+ comp_chunk_offset
;
296 plain_chunk_offset
= ndrpush
->offset
;
297 NDR_CHECK(ndr_push_zero(ndrpush
, plain_chunk_size
));
298 plain_chunk
.length
= plain_chunk_size
;
299 plain_chunk
.data
= ndrpush
->data
+ plain_chunk_offset
;
301 DEBUG(9,("XPRESS plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
302 plain_chunk_size
, plain_chunk_size
, comp_chunk_size
, comp_chunk_size
));
304 /* Uncompressing the buffer using LZ Xpress algorithm */
305 ret
= lzxpress_decompress(comp_chunk
.data
,
310 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
311 "XPRESS lzxpress_decompress() returned %d\n",
314 plain_chunk
.length
= ret
;
316 if ((plain_chunk_size
< 0x00010000) || (ndrpull
->offset
+4 >= ndrpull
->data_size
)) {
317 /* this is the last chunk */
321 return NDR_ERR_SUCCESS
;
324 static enum ndr_err_code
ndr_push_compression_xpress_chunk(struct ndr_push
*ndrpush
,
325 struct ndr_pull
*ndrpull
,
328 DATA_BLOB comp_chunk
;
329 uint32_t comp_chunk_size_offset
;
330 DATA_BLOB plain_chunk
;
331 uint32_t plain_chunk_size
;
332 uint32_t plain_chunk_offset
;
333 uint32_t max_plain_size
= 0x00010000;
334 uint32_t max_comp_size
= 0x00020000 + 2; /* TODO: use the correct value here */
338 plain_chunk_size
= MIN(max_plain_size
, ndrpull
->data_size
- ndrpull
->offset
);
339 plain_chunk_offset
= ndrpull
->offset
;
340 NDR_CHECK(ndr_pull_advance(ndrpull
, plain_chunk_size
));
342 plain_chunk
.data
= ndrpull
->data
+ plain_chunk_offset
;
343 plain_chunk
.length
= plain_chunk_size
;
345 if (plain_chunk_size
< max_plain_size
) {
349 NDR_CHECK(ndr_push_uint32(ndrpush
, NDR_SCALARS
, plain_chunk_size
));
350 comp_chunk_size_offset
= ndrpush
->offset
;
351 NDR_CHECK(ndr_push_uint32(ndrpush
, NDR_SCALARS
, 0xFEFEFEFE));
353 NDR_CHECK(ndr_push_expand(ndrpush
, max_comp_size
));
355 comp_chunk
.data
= ndrpush
->data
+ ndrpush
->offset
;
356 comp_chunk
.length
= max_comp_size
;
358 /* Compressing the buffer using LZ Xpress algorithm */
359 ret
= lzxpress_compress(plain_chunk
.data
,
364 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
365 "XPRESS lzxpress_compress() returned %d\n",
368 comp_chunk
.length
= ret
;
370 tmp_offset
= ndrpush
->offset
;
371 ndrpush
->offset
= comp_chunk_size_offset
;
372 NDR_CHECK(ndr_push_uint32(ndrpush
, NDR_SCALARS
, comp_chunk
.length
));
373 ndrpush
->offset
= tmp_offset
;
375 ndrpush
->offset
+= comp_chunk
.length
;
376 return NDR_ERR_SUCCESS
;
380 handle compressed subcontext buffers, which in midl land are user-marshalled, but
381 we use magic in pidl to make them easier to cope with
383 enum ndr_err_code
ndr_pull_compression_start(struct ndr_pull
*subndr
,
384 struct ndr_pull
**_comndr
,
385 enum ndr_compression_alg compression_alg
,
386 ssize_t decompressed_len
)
388 struct ndr_push
*ndrpush
;
389 struct ndr_pull
*comndr
;
390 DATA_BLOB uncompressed
;
394 ndrpush
= ndr_push_init_ctx(subndr
, subndr
->iconv_convenience
);
395 NDR_ERR_HAVE_NO_MEMORY(ndrpush
);
397 switch (compression_alg
) {
398 case NDR_COMPRESSION_MSZIP
:
401 NDR_CHECK(ndr_pull_compression_mszip_chunk(subndr
, ndrpush
, &z
, &last
));
405 case NDR_COMPRESSION_XPRESS
:
407 NDR_CHECK(ndr_pull_compression_xpress_chunk(subndr
, ndrpush
, &last
));
412 return ndr_pull_error(subndr
, NDR_ERR_COMPRESSION
, "Bad compression algorithm %d (PULL)",
416 uncompressed
= ndr_push_blob(ndrpush
);
417 if (uncompressed
.length
!= decompressed_len
) {
418 return ndr_pull_error(subndr
, NDR_ERR_COMPRESSION
,
419 "Bad uncompressed_len [%u] != [%u](0x%08X) (PULL)",
420 (int)uncompressed
.length
,
421 (int)decompressed_len
,
422 (int)decompressed_len
);
425 comndr
= talloc_zero(subndr
, struct ndr_pull
);
426 NDR_ERR_HAVE_NO_MEMORY(comndr
);
427 comndr
->flags
= subndr
->flags
;
428 comndr
->current_mem_ctx
= subndr
->current_mem_ctx
;
430 comndr
->data
= uncompressed
.data
;
431 comndr
->data_size
= uncompressed
.length
;
434 comndr
->iconv_convenience
= talloc_reference(comndr
, subndr
->iconv_convenience
);
437 return NDR_ERR_SUCCESS
;
440 enum ndr_err_code
ndr_pull_compression_end(struct ndr_pull
*subndr
,
441 struct ndr_pull
*comndr
,
442 enum ndr_compression_alg compression_alg
,
443 ssize_t decompressed_len
)
445 return NDR_ERR_SUCCESS
;
449 push a compressed subcontext
451 enum ndr_err_code
ndr_push_compression_start(struct ndr_push
*subndr
,
452 struct ndr_push
**_uncomndr
,
453 enum ndr_compression_alg compression_alg
,
454 ssize_t decompressed_len
)
456 struct ndr_push
*uncomndr
;
458 switch (compression_alg
) {
459 case NDR_COMPRESSION_MSZIP
:
460 case NDR_COMPRESSION_XPRESS
:
463 return ndr_push_error(subndr
, NDR_ERR_COMPRESSION
,
464 "Bad compression algorithm %d (PUSH)",
468 uncomndr
= ndr_push_init_ctx(subndr
, subndr
->iconv_convenience
);
469 NDR_ERR_HAVE_NO_MEMORY(uncomndr
);
470 uncomndr
->flags
= subndr
->flags
;
472 *_uncomndr
= uncomndr
;
473 return NDR_ERR_SUCCESS
;
477 push a compressed subcontext
479 enum ndr_err_code
ndr_push_compression_end(struct ndr_push
*subndr
,
480 struct ndr_push
*uncomndr
,
481 enum ndr_compression_alg compression_alg
,
482 ssize_t decompressed_len
)
484 struct ndr_pull
*ndrpull
;
488 ndrpull
= talloc_zero(uncomndr
, struct ndr_pull
);
489 NDR_ERR_HAVE_NO_MEMORY(ndrpull
);
490 ndrpull
->flags
= uncomndr
->flags
;
491 ndrpull
->data
= uncomndr
->data
;
492 ndrpull
->data_size
= uncomndr
->offset
;
495 ndrpull
->iconv_convenience
= talloc_reference(ndrpull
, subndr
->iconv_convenience
);
497 switch (compression_alg
) {
498 case NDR_COMPRESSION_MSZIP
:
501 NDR_CHECK(ndr_push_compression_mszip_chunk(subndr
, ndrpull
, &z
, &last
));
505 case NDR_COMPRESSION_XPRESS
:
507 NDR_CHECK(ndr_push_compression_xpress_chunk(subndr
, ndrpull
, &last
));
512 return ndr_push_error(subndr
, NDR_ERR_COMPRESSION
, "Bad compression algorithm %d (PUSH)",
516 talloc_free(uncomndr
);
517 return NDR_ERR_SUCCESS
;