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 struct ndr_compression_state
{
30 enum ndr_compression_alg type
;
40 static voidpf
ndr_zlib_alloc(voidpf opaque
, uInt items
, uInt size
)
42 return talloc_zero_size(opaque
, items
* size
);
45 static void ndr_zlib_free(voidpf opaque
, voidpf address
)
50 static enum ndr_err_code
ndr_pull_compression_mszip_chunk(struct ndr_pull
*ndrpull
,
51 struct ndr_push
*ndrpush
,
56 uint32_t comp_chunk_offset
;
57 uint32_t comp_chunk_size
;
58 DATA_BLOB plain_chunk
;
59 uint32_t plain_chunk_offset
;
60 uint32_t plain_chunk_size
;
63 NDR_CHECK(ndr_pull_uint32(ndrpull
, NDR_SCALARS
, &plain_chunk_size
));
64 if (plain_chunk_size
> 0x00008000) {
65 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
, "Bad MSZIP plain chunk size %08X > 0x00008000 (PULL)",
69 NDR_CHECK(ndr_pull_uint32(ndrpull
, NDR_SCALARS
, &comp_chunk_size
));
71 DEBUG(9,("MSZIP plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
72 plain_chunk_size
, plain_chunk_size
, comp_chunk_size
, comp_chunk_size
));
74 comp_chunk_offset
= ndrpull
->offset
;
75 NDR_CHECK(ndr_pull_advance(ndrpull
, comp_chunk_size
));
76 comp_chunk
.length
= comp_chunk_size
;
77 comp_chunk
.data
= ndrpull
->data
+ comp_chunk_offset
;
79 plain_chunk_offset
= ndrpush
->offset
;
80 NDR_CHECK(ndr_push_zero(ndrpush
, plain_chunk_size
));
81 plain_chunk
.length
= plain_chunk_size
;
82 plain_chunk
.data
= ndrpush
->data
+ plain_chunk_offset
;
84 if (comp_chunk
.length
< 2) {
85 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
86 "Bad MSZIP comp chunk size %u < 2 (PULL)",
87 (unsigned int)comp_chunk
.length
);
89 /* CK = Chris Kirmse, official Microsoft purloiner */
90 if (comp_chunk
.data
[0] != 'C' ||
91 comp_chunk
.data
[1] != 'K') {
92 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
93 "Bad MSZIP invalid prefix [%c%c] != [CK]",
94 comp_chunk
.data
[0], comp_chunk
.data
[1]);
97 z
->next_in
= comp_chunk
.data
+ 2;
98 z
->avail_in
= comp_chunk
.length
-2;
101 z
->next_out
= plain_chunk
.data
;
102 z
->avail_out
= plain_chunk
.length
;
106 /* the first time we need to intialize completely */
107 z
->zalloc
= ndr_zlib_alloc
;
108 z
->zfree
= ndr_zlib_free
;
111 z_ret
= inflateInit2(z
, -MAX_WBITS
);
113 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
114 "Bad inflateInit2 error %s(%d) (PULL)",
115 zError(z_ret
), z_ret
);
120 /* call inflate untill we get Z_STREAM_END or an error */
122 z_ret
= inflate(z
, Z_BLOCK
);
123 if (z_ret
!= Z_OK
) break;
126 if (z_ret
!= Z_STREAM_END
) {
127 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
128 "Bad inflate(Z_BLOCK) error %s(%d) (PULL)",
129 zError(z_ret
), z_ret
);
133 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
134 "MSZIP not all avail_in[%u] bytes consumed (PULL)",
139 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
140 "MSZIP not all avail_out[%u] bytes consumed (PULL)",
144 if ((plain_chunk_size
< 0x00008000) || (ndrpull
->offset
+4 >= ndrpull
->data_size
)) {
145 /* this is the last chunk */
149 z_ret
= inflateReset(z
);
151 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
152 "Bad inflateReset error %s(%d) (PULL)",
153 zError(z_ret
), z_ret
);
156 z_ret
= inflateSetDictionary(z
, plain_chunk
.data
, plain_chunk
.length
);
158 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
159 "Bad inflateSetDictionary error %s(%d) (PULL)",
160 zError(z_ret
), z_ret
);
163 return NDR_ERR_SUCCESS
;
166 static enum ndr_err_code
ndr_push_compression_mszip_chunk(struct ndr_push
*ndrpush
,
167 struct ndr_pull
*ndrpull
,
171 DATA_BLOB comp_chunk
;
172 uint32_t comp_chunk_size
;
173 uint32_t comp_chunk_size_offset
;
174 DATA_BLOB plain_chunk
;
175 uint32_t plain_chunk_size
;
176 uint32_t plain_chunk_offset
;
177 uint32_t max_plain_size
= 0x00008000;
178 uint32_t max_comp_size
= 0x00008000 + 2 + 12 /*TODO: what value do we really need here?*/;
182 plain_chunk_size
= MIN(max_plain_size
, ndrpull
->data_size
- ndrpull
->offset
);
183 plain_chunk_offset
= ndrpull
->offset
;
184 NDR_CHECK(ndr_pull_advance(ndrpull
, plain_chunk_size
));
186 plain_chunk
.data
= ndrpull
->data
+ plain_chunk_offset
;
187 plain_chunk
.length
= plain_chunk_size
;
189 if (plain_chunk_size
< max_plain_size
) {
193 NDR_CHECK(ndr_push_uint32(ndrpush
, NDR_SCALARS
, plain_chunk_size
));
194 comp_chunk_size_offset
= ndrpush
->offset
;
195 NDR_CHECK(ndr_push_uint32(ndrpush
, NDR_SCALARS
, 0xFEFEFEFE));
197 NDR_CHECK(ndr_push_expand(ndrpush
, max_comp_size
));
199 comp_chunk
.data
= ndrpush
->data
+ ndrpush
->offset
;
200 comp_chunk
.length
= max_comp_size
;
202 /* CK = Chris Kirmse, official Microsoft purloiner */
203 comp_chunk
.data
[0] = 'C';
204 comp_chunk
.data
[1] = 'K';
206 z
->next_in
= plain_chunk
.data
;
207 z
->avail_in
= plain_chunk
.length
;
210 z
->next_out
= comp_chunk
.data
+ 2;
211 z
->avail_out
= comp_chunk
.length
- 2;
215 /* the first time we need to intialize completely */
216 z
->zalloc
= ndr_zlib_alloc
;
217 z
->zfree
= ndr_zlib_free
;
220 /* TODO: find how to trigger the same parameters windows uses */
221 z_ret
= deflateInit2(z
,
222 Z_DEFAULT_COMPRESSION
,
228 return ndr_push_error(ndrpush
, NDR_ERR_COMPRESSION
,
229 "Bad deflateInit2 error %s(%d) (PUSH)",
230 zError(z_ret
), z_ret
);
235 /* call deflate untill we get Z_STREAM_END or an error */
237 z_ret
= deflate(z
, Z_FINISH
);
238 if (z_ret
!= Z_OK
) break;
240 if (z_ret
!= Z_STREAM_END
) {
241 return ndr_push_error(ndrpush
, NDR_ERR_COMPRESSION
,
242 "Bad delate(Z_BLOCK) error %s(%d) (PUSH)",
243 zError(z_ret
), z_ret
);
247 return ndr_push_error(ndrpush
, NDR_ERR_COMPRESSION
,
248 "MSZIP not all avail_in[%u] bytes consumed (PUSH)",
252 comp_chunk_size
= 2 + z
->total_out
;
254 z_ret
= deflateReset(z
);
256 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
257 "Bad deflateReset error %s(%d) (PULL)",
258 zError(z_ret
), z_ret
);
261 z_ret
= deflateSetDictionary(z
, plain_chunk
.data
, plain_chunk
.length
);
263 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
264 "Bad deflateSetDictionary error %s(%d) (PULL)",
265 zError(z_ret
), z_ret
);
268 tmp_offset
= ndrpush
->offset
;
269 ndrpush
->offset
= comp_chunk_size_offset
;
270 NDR_CHECK(ndr_push_uint32(ndrpush
, NDR_SCALARS
, comp_chunk_size
));
271 ndrpush
->offset
= tmp_offset
;
273 DEBUG(9,("MSZIP comp plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
274 (unsigned int)plain_chunk
.length
,
275 (unsigned int)plain_chunk
.length
,
276 comp_chunk_size
, comp_chunk_size
));
278 ndrpush
->offset
+= comp_chunk_size
;
279 return NDR_ERR_SUCCESS
;
282 static enum ndr_err_code
ndr_pull_compression_xpress_chunk(struct ndr_pull
*ndrpull
,
283 struct ndr_push
*ndrpush
,
286 DATA_BLOB comp_chunk
;
287 DATA_BLOB plain_chunk
;
288 uint32_t comp_chunk_offset
;
289 uint32_t plain_chunk_offset
;
290 uint32_t comp_chunk_size
;
291 uint32_t plain_chunk_size
;
294 NDR_CHECK(ndr_pull_uint32(ndrpull
, NDR_SCALARS
, &plain_chunk_size
));
295 if (plain_chunk_size
> 0x00010000) {
296 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
, "Bad XPRESS plain chunk size %08X > 0x00010000 (PULL)",
300 NDR_CHECK(ndr_pull_uint32(ndrpull
, NDR_SCALARS
, &comp_chunk_size
));
302 comp_chunk_offset
= ndrpull
->offset
;
303 NDR_CHECK(ndr_pull_advance(ndrpull
, comp_chunk_size
));
304 comp_chunk
.length
= comp_chunk_size
;
305 comp_chunk
.data
= ndrpull
->data
+ comp_chunk_offset
;
307 plain_chunk_offset
= ndrpush
->offset
;
308 NDR_CHECK(ndr_push_zero(ndrpush
, plain_chunk_size
));
309 plain_chunk
.length
= plain_chunk_size
;
310 plain_chunk
.data
= ndrpush
->data
+ plain_chunk_offset
;
312 DEBUG(9,("XPRESS plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
313 plain_chunk_size
, plain_chunk_size
, comp_chunk_size
, comp_chunk_size
));
315 /* Uncompressing the buffer using LZ Xpress algorithm */
316 ret
= lzxpress_decompress(comp_chunk
.data
,
321 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
322 "XPRESS lzxpress_decompress() returned %d\n",
325 plain_chunk
.length
= ret
;
327 if ((plain_chunk_size
< 0x00010000) || (ndrpull
->offset
+4 >= ndrpull
->data_size
)) {
328 /* this is the last chunk */
332 return NDR_ERR_SUCCESS
;
335 static enum ndr_err_code
ndr_push_compression_xpress_chunk(struct ndr_push
*ndrpush
,
336 struct ndr_pull
*ndrpull
,
339 DATA_BLOB comp_chunk
;
340 uint32_t comp_chunk_size_offset
;
341 DATA_BLOB plain_chunk
;
342 uint32_t plain_chunk_size
;
343 uint32_t plain_chunk_offset
;
344 uint32_t max_plain_size
= 0x00010000;
345 uint32_t max_comp_size
= 0x00020000 + 2; /* TODO: use the correct value here */
349 plain_chunk_size
= MIN(max_plain_size
, ndrpull
->data_size
- ndrpull
->offset
);
350 plain_chunk_offset
= ndrpull
->offset
;
351 NDR_CHECK(ndr_pull_advance(ndrpull
, plain_chunk_size
));
353 plain_chunk
.data
= ndrpull
->data
+ plain_chunk_offset
;
354 plain_chunk
.length
= plain_chunk_size
;
356 if (plain_chunk_size
< max_plain_size
) {
360 NDR_CHECK(ndr_push_uint32(ndrpush
, NDR_SCALARS
, plain_chunk_size
));
361 comp_chunk_size_offset
= ndrpush
->offset
;
362 NDR_CHECK(ndr_push_uint32(ndrpush
, NDR_SCALARS
, 0xFEFEFEFE));
364 NDR_CHECK(ndr_push_expand(ndrpush
, max_comp_size
));
366 comp_chunk
.data
= ndrpush
->data
+ ndrpush
->offset
;
367 comp_chunk
.length
= max_comp_size
;
369 /* Compressing the buffer using LZ Xpress algorithm */
370 ret
= lzxpress_compress(plain_chunk
.data
,
375 return ndr_pull_error(ndrpull
, NDR_ERR_COMPRESSION
,
376 "XPRESS lzxpress_compress() returned %d\n",
379 comp_chunk
.length
= ret
;
381 tmp_offset
= ndrpush
->offset
;
382 ndrpush
->offset
= comp_chunk_size_offset
;
383 NDR_CHECK(ndr_push_uint32(ndrpush
, NDR_SCALARS
, comp_chunk
.length
));
384 ndrpush
->offset
= tmp_offset
;
386 ndrpush
->offset
+= comp_chunk
.length
;
387 return NDR_ERR_SUCCESS
;
391 handle compressed subcontext buffers, which in midl land are user-marshalled, but
392 we use magic in pidl to make them easier to cope with
394 enum ndr_err_code
ndr_pull_compression_start(struct ndr_pull
*subndr
,
395 struct ndr_pull
**_comndr
,
396 enum ndr_compression_alg compression_alg
,
397 ssize_t decompressed_len
,
398 ssize_t compressed_len
)
400 struct ndr_push
*ndrpush
;
401 struct ndr_pull
*comndr
;
402 DATA_BLOB uncompressed
;
406 ndrpush
= ndr_push_init_ctx(subndr
);
407 NDR_ERR_HAVE_NO_MEMORY(ndrpush
);
409 switch (compression_alg
) {
410 case NDR_COMPRESSION_MSZIP
:
413 NDR_CHECK(ndr_pull_compression_mszip_chunk(subndr
, ndrpush
, &z
, &last
));
417 case NDR_COMPRESSION_XPRESS
:
419 NDR_CHECK(ndr_pull_compression_xpress_chunk(subndr
, ndrpush
, &last
));
424 return ndr_pull_error(subndr
, NDR_ERR_COMPRESSION
, "Bad compression algorithm %d (PULL)",
428 uncompressed
= ndr_push_blob(ndrpush
);
429 if (uncompressed
.length
!= decompressed_len
) {
430 return ndr_pull_error(subndr
, NDR_ERR_COMPRESSION
,
431 "Bad uncompressed_len [%u] != [%u](0x%08X) (PULL)",
432 (int)uncompressed
.length
,
433 (int)decompressed_len
,
434 (int)decompressed_len
);
437 comndr
= talloc_zero(subndr
, struct ndr_pull
);
438 NDR_ERR_HAVE_NO_MEMORY(comndr
);
439 comndr
->flags
= subndr
->flags
;
440 comndr
->current_mem_ctx
= subndr
->current_mem_ctx
;
442 comndr
->data
= uncompressed
.data
;
443 comndr
->data_size
= uncompressed
.length
;
447 return NDR_ERR_SUCCESS
;
450 enum ndr_err_code
ndr_pull_compression_end(struct ndr_pull
*subndr
,
451 struct ndr_pull
*comndr
,
452 enum ndr_compression_alg compression_alg
,
453 ssize_t decompressed_len
)
455 return NDR_ERR_SUCCESS
;
459 push a compressed subcontext
461 enum ndr_err_code
ndr_push_compression_start(struct ndr_push
*subndr
,
462 struct ndr_push
**_uncomndr
,
463 enum ndr_compression_alg compression_alg
,
464 ssize_t decompressed_len
)
466 struct ndr_push
*uncomndr
;
468 switch (compression_alg
) {
469 case NDR_COMPRESSION_MSZIP
:
470 case NDR_COMPRESSION_XPRESS
:
473 return ndr_push_error(subndr
, NDR_ERR_COMPRESSION
,
474 "Bad compression algorithm %d (PUSH)",
478 uncomndr
= ndr_push_init_ctx(subndr
);
479 NDR_ERR_HAVE_NO_MEMORY(uncomndr
);
480 uncomndr
->flags
= subndr
->flags
;
482 *_uncomndr
= uncomndr
;
483 return NDR_ERR_SUCCESS
;
487 push a compressed subcontext
489 enum ndr_err_code
ndr_push_compression_end(struct ndr_push
*subndr
,
490 struct ndr_push
*uncomndr
,
491 enum ndr_compression_alg compression_alg
,
492 ssize_t decompressed_len
)
494 struct ndr_pull
*ndrpull
;
498 ndrpull
= talloc_zero(uncomndr
, struct ndr_pull
);
499 NDR_ERR_HAVE_NO_MEMORY(ndrpull
);
500 ndrpull
->flags
= uncomndr
->flags
;
501 ndrpull
->data
= uncomndr
->data
;
502 ndrpull
->data_size
= uncomndr
->offset
;
505 switch (compression_alg
) {
506 case NDR_COMPRESSION_MSZIP
:
509 NDR_CHECK(ndr_push_compression_mszip_chunk(subndr
, ndrpull
, &z
, &last
));
513 case NDR_COMPRESSION_XPRESS
:
515 NDR_CHECK(ndr_push_compression_xpress_chunk(subndr
, ndrpull
, &last
));
520 return ndr_push_error(subndr
, NDR_ERR_COMPRESSION
, "Bad compression algorithm %d (PUSH)",
524 talloc_free(uncomndr
);
525 return NDR_ERR_SUCCESS
;
528 static enum ndr_err_code
generic_mszip_init(TALLOC_CTX
*mem_ctx
,
529 struct ndr_compression_state
*state
)
531 z_stream
*z
= talloc_zero(mem_ctx
, z_stream
);
532 NDR_ERR_HAVE_NO_MEMORY(z
);
534 z
->zalloc
= ndr_zlib_alloc
;
535 z
->zfree
= ndr_zlib_free
;
539 state
->mszip
.dict_size
= 0;
540 /* pre-alloc dictionnary */
541 state
->mszip
.dict
= talloc_array(mem_ctx
, uint8_t, 0x8000);
542 NDR_ERR_HAVE_NO_MEMORY(state
->mszip
.dict
);
544 return NDR_ERR_SUCCESS
;
547 static void generic_mszip_free(struct ndr_compression_state
*state
)
553 TALLOC_FREE(state
->mszip
.z
);
554 TALLOC_FREE(state
->mszip
.dict
);
558 enum ndr_err_code
ndr_pull_compression_state_init(struct ndr_pull
*ndr
,
559 enum ndr_compression_alg compression_alg
,
560 struct ndr_compression_state
**state
)
562 struct ndr_compression_state
*s
;
565 s
= talloc_zero(ndr
, struct ndr_compression_state
);
566 NDR_ERR_HAVE_NO_MEMORY(s
);
567 s
->type
= compression_alg
;
569 switch (compression_alg
) {
570 case NDR_COMPRESSION_MSZIP
:
571 case NDR_COMPRESSION_XPRESS
:
573 case NDR_COMPRESSION_MSZIP_CAB
:
574 NDR_CHECK(generic_mszip_init(ndr
, s
));
575 z_ret
= inflateInit2(s
->mszip
.z
, -MAX_WBITS
);
577 return ndr_pull_error(ndr
, NDR_ERR_COMPRESSION
,
578 "zlib inflateinit2 error %s (%d) %s (PULL)",
579 zError(z_ret
), z_ret
, s
->mszip
.z
->msg
);
583 return ndr_pull_error(ndr
, NDR_ERR_COMPRESSION
,
584 "Bad compression algorithm %d (PULL)",
591 return NDR_ERR_SUCCESS
;
594 void ndr_pull_compression_state_free(struct ndr_compression_state
*state
)
600 switch (state
->type
) {
601 case NDR_COMPRESSION_MSZIP
:
602 case NDR_COMPRESSION_XPRESS
:
604 case NDR_COMPRESSION_MSZIP_CAB
:
605 generic_mszip_free(state
);
613 enum ndr_err_code
ndr_push_compression_state_init(struct ndr_push
*ndr
,
614 enum ndr_compression_alg compression_alg
,
615 struct ndr_compression_state
**state
)
617 struct ndr_compression_state
*s
;
620 s
= talloc_zero(ndr
, struct ndr_compression_state
);
621 NDR_ERR_HAVE_NO_MEMORY(s
);
622 s
->type
= compression_alg
;
624 switch (compression_alg
) {
625 case NDR_COMPRESSION_XPRESS
:
626 case NDR_COMPRESSION_MSZIP
:
628 case NDR_COMPRESSION_MSZIP_CAB
:
629 NDR_CHECK(generic_mszip_init(ndr
, s
));
630 z_ret
= deflateInit2(s
->mszip
.z
,
631 Z_DEFAULT_COMPRESSION
,
637 return ndr_push_error(ndr
, NDR_ERR_COMPRESSION
,
638 "zlib inflateinit2 error %s (%d) %s (PUSH)",
639 zError(z_ret
), z_ret
, s
->mszip
.z
->msg
);
643 return ndr_push_error(ndr
, NDR_ERR_COMPRESSION
,
644 "Bad compression algorithm %d (PUSH)",
651 return NDR_ERR_SUCCESS
;
654 void ndr_push_compression_state_free(struct ndr_compression_state
*state
)
660 switch (state
->type
) {
661 case NDR_COMPRESSION_MSZIP
:
662 case NDR_COMPRESSION_XPRESS
:
664 case NDR_COMPRESSION_MSZIP_CAB
:
665 generic_mszip_free(state
);