ndr_compression: change debug levels
[metze/samba/wb-ndr.git] / source / librpc / ndr / ndr_compression.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    libndr compression support
5
6    Copyright (C) Stefan Metzmacher 2005
7    Copyright (C) Matthieu Suiche 2008
8    
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.
13    
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.
18    
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/>.
21 */
22
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>
28
29 static voidpf ndr_zlib_alloc(voidpf opaque, uInt items, uInt size)
30 {
31         return talloc_zero_size(opaque, items * size);
32 }
33
34 static void  ndr_zlib_free(voidpf opaque, voidpf address)
35 {
36         talloc_free(address);
37 }
38
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)
43 {
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;
51
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);
56         }
57
58         NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &comp_chunk_size));
59
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));
62
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;
67
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;
72
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);
77         }
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]);
84         }
85
86         z->next_in      = comp_chunk.data + 2;
87         z->avail_in     = comp_chunk.length -2;
88         z->total_in     = 0;
89
90         z->next_out     = plain_chunk.data;
91         z->avail_out    = plain_chunk.length;
92         z->total_out    = 0;
93
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;
99
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);
105
106                 }
107         }
108
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;
113         }
114
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);
119         }
120
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);
125         }
126
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);
131         }
132
133         if ((plain_chunk_size < 0x00008000) || (ndrpull->offset+4 >= ndrpull->data_size)) {
134                 /* this is the last chunk */
135                 *last = true;
136         }
137
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);
143         }
144
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);
150         }
151
152         return NDR_ERR_SUCCESS;
153 }
154
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)
159 {
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;
170
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));
174
175         plain_chunk.data = ndrpull->data + plain_chunk_offset;
176         plain_chunk.length = plain_chunk_size;
177
178         if (plain_chunk_size < max_plain_size) {
179                 *last = true;
180         }
181
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));
185
186         NDR_CHECK(ndr_push_expand(ndrpush, max_comp_size));
187
188         comp_chunk.data = ndrpush->data + ndrpush->offset;
189         comp_chunk.length = max_comp_size;
190
191         /* CK = Chris Kirmse, official Microsoft purloiner */
192         comp_chunk.data[0] = 'C';
193         comp_chunk.data[1] = 'K';
194
195         z->next_in      = plain_chunk.data;
196         z->avail_in     = plain_chunk.length;
197         z->total_in     = 0;
198
199         z->next_out     = comp_chunk.data + 2;
200         z->avail_out    = comp_chunk.length - 2;
201         z->total_out    = 0;
202
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;
208
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,
214                                      9,
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);
220
221                 }
222         }
223
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;
228         }
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);
233         }
234
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);
239         }
240
241         comp_chunk_size = 2 + z->total_out;
242
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);
248         }
249
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);
255         }
256
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;
261
262         DEBUG(9,("MSZIP comp plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
263                  plain_chunk.length, plain_chunk.length, comp_chunk_size, comp_chunk_size));
264
265         ndrpush->offset += comp_chunk_size;
266         return NDR_ERR_SUCCESS;
267 }
268
269 static enum ndr_err_code ndr_pull_compression_xpress_chunk(struct ndr_pull *ndrpull,
270                                                   struct ndr_push *ndrpush,
271                                                   bool *last)
272 {
273         DATA_BLOB comp_chunk;
274         DATA_BLOB plain_chunk;
275         uint32_t comp_chunk_offset;
276         uint32_t plain_chunk_offset;
277         uint32_t comp_chunk_size;
278         uint32_t plain_chunk_size;
279
280         NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &plain_chunk_size));
281         if (plain_chunk_size > 0x00010000) {
282                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, "Bad XPRESS plain chunk size %08X > 0x00010000 (PULL)", 
283                                       plain_chunk_size);
284         }
285
286         NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &comp_chunk_size));
287
288         comp_chunk_offset = ndrpull->offset;
289         NDR_CHECK(ndr_pull_advance(ndrpull, comp_chunk_size));
290         comp_chunk.length = comp_chunk_size;
291         comp_chunk.data = ndrpull->data + comp_chunk_offset;
292
293         plain_chunk_offset = ndrpush->offset;
294         NDR_CHECK(ndr_push_zero(ndrpush, plain_chunk_size));
295         plain_chunk.length = plain_chunk_size;
296         plain_chunk.data = ndrpush->data + plain_chunk_offset;
297
298         DEBUG(9,("XPRESS plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
299                  plain_chunk_size, plain_chunk_size, comp_chunk_size, comp_chunk_size));
300
301         /* Uncompressing the buffer using LZ Xpress algorithm */
302         lzxpress_decompress(&comp_chunk, &plain_chunk);
303
304         if ((plain_chunk_size < 0x00010000) || (ndrpull->offset+4 >= ndrpull->data_size)) {
305                 /* this is the last chunk */
306                 *last = true;
307         }
308
309         return NDR_ERR_SUCCESS;
310 }
311
312 static enum ndr_err_code ndr_push_compression_xpress_chunk(struct ndr_push *ndrpush,
313                                                            struct ndr_pull *ndrpull,
314                                                            bool *last)
315 {
316         return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION, "XPRESS compression is not supported yet (PUSH)");
317 }
318
319 /*
320   handle compressed subcontext buffers, which in midl land are user-marshalled, but
321   we use magic in pidl to make them easier to cope with
322 */
323 enum ndr_err_code ndr_pull_compression_start(struct ndr_pull *subndr,
324                                     struct ndr_pull **_comndr,
325                                     enum ndr_compression_alg compression_alg,
326                                     ssize_t decompressed_len)
327 {
328         struct ndr_push *ndrpush;
329         struct ndr_pull *comndr;
330         DATA_BLOB uncompressed;
331         bool last = false;
332         z_stream z;
333
334         ndrpush = ndr_push_init_ctx(subndr, subndr->iconv_convenience);
335         NDR_ERR_HAVE_NO_MEMORY(ndrpush);
336
337         switch (compression_alg) {
338         case NDR_COMPRESSION_MSZIP:
339                 ZERO_STRUCT(z);
340                 while (!last) {
341                         NDR_CHECK(ndr_pull_compression_mszip_chunk(subndr, ndrpush, &z, &last));
342                 }
343                 break;
344
345         case NDR_COMPRESSION_XPRESS:
346                 while (!last) {
347                         NDR_CHECK(ndr_pull_compression_xpress_chunk(subndr, ndrpush, &last));
348                 }
349                 break;
350
351         default:
352                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PULL)",
353                                       compression_alg);
354         }
355
356         uncompressed = ndr_push_blob(ndrpush);
357         if (uncompressed.length != decompressed_len) {
358                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION,
359                                       "Bad uncompressed_len [%u] != [%u](0x%08X) (PULL)",
360                                       (int)uncompressed.length,
361                                       (int)decompressed_len,
362                                       (int)decompressed_len);
363         }
364
365         comndr = talloc_zero(subndr, struct ndr_pull);
366         NDR_ERR_HAVE_NO_MEMORY(comndr);
367         comndr->flags           = subndr->flags;
368         comndr->current_mem_ctx = subndr->current_mem_ctx;
369
370         comndr->data            = uncompressed.data;
371         comndr->data_size       = uncompressed.length;
372         comndr->offset          = 0;
373
374         comndr->iconv_convenience = talloc_reference(comndr, subndr->iconv_convenience);
375
376         *_comndr = comndr;
377         return NDR_ERR_SUCCESS;
378 }
379
380 enum ndr_err_code ndr_pull_compression_end(struct ndr_pull *subndr,
381                                   struct ndr_pull *comndr,
382                                   enum ndr_compression_alg compression_alg,
383                                   ssize_t decompressed_len)
384 {
385         return NDR_ERR_SUCCESS;
386 }
387
388 /*
389   push a compressed subcontext
390 */
391 enum ndr_err_code ndr_push_compression_start(struct ndr_push *subndr,
392                                     struct ndr_push **_uncomndr,
393                                     enum ndr_compression_alg compression_alg,
394                                     ssize_t decompressed_len)
395 {
396         struct ndr_push *uncomndr;
397
398         switch (compression_alg) {
399         case NDR_COMPRESSION_MSZIP:
400         case NDR_COMPRESSION_XPRESS:
401                 break;
402         default:
403                 return ndr_push_error(subndr, NDR_ERR_COMPRESSION,
404                                       "Bad compression algorithm %d (PUSH)",
405                                       compression_alg);
406         }
407
408         uncomndr = ndr_push_init_ctx(subndr, subndr->iconv_convenience);
409         NDR_ERR_HAVE_NO_MEMORY(uncomndr);
410         uncomndr->flags = subndr->flags;
411
412         *_uncomndr = uncomndr;
413         return NDR_ERR_SUCCESS;
414 }
415
416 /*
417   push a compressed subcontext
418 */
419 enum ndr_err_code ndr_push_compression_end(struct ndr_push *subndr,
420                                   struct ndr_push *uncomndr,
421                                   enum ndr_compression_alg compression_alg,
422                                   ssize_t decompressed_len)
423 {
424         struct ndr_pull *ndrpull;
425         bool last = false;
426         z_stream z;
427
428         ndrpull = talloc_zero(uncomndr, struct ndr_pull);
429         NDR_ERR_HAVE_NO_MEMORY(ndrpull);
430         ndrpull->flags          = uncomndr->flags;
431         ndrpull->data           = uncomndr->data;
432         ndrpull->data_size      = uncomndr->offset;
433         ndrpull->offset         = 0;
434
435         ndrpull->iconv_convenience = talloc_reference(ndrpull, subndr->iconv_convenience);
436
437         switch (compression_alg) {
438         case NDR_COMPRESSION_MSZIP:
439                 ZERO_STRUCT(z);
440                 while (!last) {
441                         NDR_CHECK(ndr_push_compression_mszip_chunk(subndr, ndrpull, &z, &last));
442                 }
443                 break;
444
445         case NDR_COMPRESSION_XPRESS:
446                 while (!last) {
447                         NDR_CHECK(ndr_push_compression_xpress_chunk(subndr, ndrpull, &last));
448                 }
449                 break;
450
451         default:
452                 return ndr_push_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PUSH)", 
453                                       compression_alg);
454         }
455
456         talloc_free(uncomndr);
457         return NDR_ERR_SUCCESS;
458 }