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;