Remove confusing TODO file
[Samba.git] / librpc / ndr / ndr_compression.c
blobd291df5ad8c96aaf2f10ff82bf7f31ebce152d44
1 /*
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/>.
23 #include "includes.h"
24 #include "../lib/compression/lzxpress.h"
25 #include "librpc/ndr/libndr.h"
26 #include "../librpc/ndr/ndr_compression.h"
27 #include <zlib.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)
36 talloc_free(address);
39 static enum ndr_err_code ndr_pull_compression_mszip_chunk(struct ndr_pull *ndrpull,
40 struct ndr_push *ndrpush,
41 z_stream *z,
42 bool *last)
44 DATA_BLOB comp_chunk;
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;
50 int z_ret;
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)",
55 plain_chunk_size);
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;
88 z->total_in = 0;
90 z->next_out = plain_chunk.data;
91 z->avail_out = plain_chunk.length;
92 z->total_out = 0;
94 if (!z->opaque) {
95 /* the first time we need to intialize completely */
96 z->zalloc = ndr_zlib_alloc;
97 z->zfree = ndr_zlib_free;
98 z->opaque = ndrpull;
100 z_ret = inflateInit2(z, -15);
101 if (z_ret != Z_OK) {
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 */
110 while (true) {
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);
121 if (z->avail_in) {
122 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
123 "MSZIP not all avail_in[%u] bytes consumed (PULL)",
124 z->avail_in);
127 if (z->avail_out) {
128 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
129 "MSZIP not all avail_out[%u] bytes consumed (PULL)",
130 z->avail_out);
133 if ((plain_chunk_size < 0x00008000) || (ndrpull->offset+4 >= ndrpull->data_size)) {
134 /* this is the last chunk */
135 *last = true;
138 z_ret = inflateReset(z);
139 if (z_ret != Z_OK) {
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);
146 if (z_ret != Z_OK) {
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,
157 z_stream *z,
158 bool *last)
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?*/;
168 uint32_t tmp_offset;
169 int z_ret;
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) {
179 *last = true;
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;
197 z->total_in = 0;
199 z->next_out = comp_chunk.data + 2;
200 z->avail_out = comp_chunk.length - 2;
201 z->total_out = 0;
203 if (!z->opaque) {
204 /* the first time we need to intialize completely */
205 z->zalloc = ndr_zlib_alloc;
206 z->zfree = ndr_zlib_free;
207 z->opaque = ndrpull;
209 /* TODO: find how to trigger the same parameters windows uses */
210 z_ret = deflateInit2(z,
211 Z_DEFAULT_COMPRESSION,
212 Z_DEFLATED,
213 -15,
215 Z_DEFAULT_STRATEGY);
216 if (z_ret != Z_OK) {
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 */
225 while (true) {
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);
235 if (z->avail_in) {
236 return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION,
237 "MSZIP not all avail_in[%u] bytes consumed (PUSH)",
238 z->avail_in);
241 comp_chunk_size = 2 + z->total_out;
243 z_ret = deflateReset(z);
244 if (z_ret != Z_OK) {
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);
251 if (z_ret != Z_OK) {
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,
273 bool *last)
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;
281 ssize_t ret;
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)",
286 plain_chunk_size);
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,
306 comp_chunk.length,
307 plain_chunk.data,
308 plain_chunk.length);
309 if (ret < 0) {
310 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
311 "XPRESS lzxpress_decompress() returned %d\n",
312 (int)ret);
314 plain_chunk.length = ret;
316 if ((plain_chunk_size < 0x00010000) || (ndrpull->offset+4 >= ndrpull->data_size)) {
317 /* this is the last chunk */
318 *last = true;
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,
326 bool *last)
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 */
335 uint32_t tmp_offset;
336 ssize_t ret;
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) {
346 *last = true;
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,
360 plain_chunk.length,
361 comp_chunk.data,
362 comp_chunk.length);
363 if (ret < 0) {
364 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
365 "XPRESS lzxpress_compress() returned %d\n",
366 (int)ret);
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;
391 bool last = false;
392 z_stream z;
394 ndrpush = ndr_push_init_ctx(subndr);
395 NDR_ERR_HAVE_NO_MEMORY(ndrpush);
397 switch (compression_alg) {
398 case NDR_COMPRESSION_MSZIP:
399 ZERO_STRUCT(z);
400 while (!last) {
401 NDR_CHECK(ndr_pull_compression_mszip_chunk(subndr, ndrpush, &z, &last));
403 break;
405 case NDR_COMPRESSION_XPRESS:
406 while (!last) {
407 NDR_CHECK(ndr_pull_compression_xpress_chunk(subndr, ndrpush, &last));
409 break;
411 default:
412 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PULL)",
413 compression_alg);
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;
432 comndr->offset = 0;
434 *_comndr = comndr;
435 return NDR_ERR_SUCCESS;
438 enum ndr_err_code ndr_pull_compression_end(struct ndr_pull *subndr,
439 struct ndr_pull *comndr,
440 enum ndr_compression_alg compression_alg,
441 ssize_t decompressed_len)
443 return NDR_ERR_SUCCESS;
447 push a compressed subcontext
449 enum ndr_err_code ndr_push_compression_start(struct ndr_push *subndr,
450 struct ndr_push **_uncomndr,
451 enum ndr_compression_alg compression_alg,
452 ssize_t decompressed_len)
454 struct ndr_push *uncomndr;
456 switch (compression_alg) {
457 case NDR_COMPRESSION_MSZIP:
458 case NDR_COMPRESSION_XPRESS:
459 break;
460 default:
461 return ndr_push_error(subndr, NDR_ERR_COMPRESSION,
462 "Bad compression algorithm %d (PUSH)",
463 compression_alg);
466 uncomndr = ndr_push_init_ctx(subndr);
467 NDR_ERR_HAVE_NO_MEMORY(uncomndr);
468 uncomndr->flags = subndr->flags;
470 *_uncomndr = uncomndr;
471 return NDR_ERR_SUCCESS;
475 push a compressed subcontext
477 enum ndr_err_code ndr_push_compression_end(struct ndr_push *subndr,
478 struct ndr_push *uncomndr,
479 enum ndr_compression_alg compression_alg,
480 ssize_t decompressed_len)
482 struct ndr_pull *ndrpull;
483 bool last = false;
484 z_stream z;
486 ndrpull = talloc_zero(uncomndr, struct ndr_pull);
487 NDR_ERR_HAVE_NO_MEMORY(ndrpull);
488 ndrpull->flags = uncomndr->flags;
489 ndrpull->data = uncomndr->data;
490 ndrpull->data_size = uncomndr->offset;
491 ndrpull->offset = 0;
493 switch (compression_alg) {
494 case NDR_COMPRESSION_MSZIP:
495 ZERO_STRUCT(z);
496 while (!last) {
497 NDR_CHECK(ndr_push_compression_mszip_chunk(subndr, ndrpull, &z, &last));
499 break;
501 case NDR_COMPRESSION_XPRESS:
502 while (!last) {
503 NDR_CHECK(ndr_push_compression_xpress_chunk(subndr, ndrpull, &last));
505 break;
507 default:
508 return ndr_push_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PUSH)",
509 compression_alg);
512 talloc_free(uncomndr);
513 return NDR_ERR_SUCCESS;