37f95bb1b6067a579d4d169b583bee9bdba69460
[metze/samba/wip.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(10,("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         } else {
108                 z_ret = inflateReset2(z, Z_RESET_KEEP_WINDOW);
109                 if (z_ret != Z_OK) {
110                         return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
111                                               "Bad inflateReset2 error %s(%d) (PULL)",
112                                               zError(z_ret), z_ret);
113                 }
114         }
115
116         /* call inflate untill we get Z_STREAM_END or an error */
117         while (true) {
118                 z_ret = inflate(z, Z_BLOCK);
119                 if (z_ret != Z_OK) break;
120         }
121
122         if (z_ret != Z_STREAM_END) {
123                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
124                                       "Bad inflate(Z_BLOCK) error %s(%d) (PULL)",
125                                       zError(z_ret), z_ret);
126         }
127
128         if (z->avail_in) {
129                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
130                                       "MSZIP not all avail_in[%u] bytes consumed (PULL)",
131                                       z->avail_in);
132         }
133
134         if (z->avail_out) {
135                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
136                                       "MSZIP not all avail_out[%u] bytes consumed (PULL)",
137                                       z->avail_out);
138         }
139
140         if ((plain_chunk_size < 0x00008000) || (ndrpull->offset+4 >= ndrpull->data_size)) {
141                 /* this is the last chunk */
142                 *last = true;
143         }
144
145         return NDR_ERR_SUCCESS;
146 }
147
148 static enum ndr_err_code ndr_push_compression_mszip_chunk(struct ndr_push *ndrpush,
149                                                           struct ndr_pull *ndrpull,
150                                                           z_stream *z,
151                                                           bool *last)
152 {
153         DATA_BLOB comp_chunk;
154         uint32_t comp_chunk_size;
155         uint32_t comp_chunk_size_offset;
156         DATA_BLOB plain_chunk;
157         uint32_t plain_chunk_size;
158         uint32_t plain_chunk_offset;
159         uint32_t max_plain_size = 0x00008000;
160         uint32_t max_comp_size = 0x00008000 + 2 + 12 /*TODO: what value do we really need here?*/;
161         uint32_t tmp_offset;
162         int z_ret;
163
164         plain_chunk_size = MIN(max_plain_size, ndrpull->data_size - ndrpull->offset);
165         plain_chunk_offset = ndrpull->offset;
166         NDR_CHECK(ndr_pull_advance(ndrpull, plain_chunk_size));
167
168         plain_chunk.data = ndrpull->data + plain_chunk_offset;
169         plain_chunk.length = plain_chunk_size;
170
171         if (plain_chunk_size < max_plain_size) {
172                 *last = true;
173         }
174
175         NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, plain_chunk_size));
176         comp_chunk_size_offset = ndrpush->offset;
177         NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, 0xFEFEFEFE));
178
179         NDR_CHECK(ndr_push_expand(ndrpush, max_comp_size));
180
181         comp_chunk.data = ndrpush->data + ndrpush->offset;
182         comp_chunk.length = max_comp_size;
183
184         /* CK = Chris Kirmse, official Microsoft purloiner */
185         comp_chunk.data[0] = 'C';
186         comp_chunk.data[1] = 'K';
187
188         z->next_in      = plain_chunk.data;
189         z->avail_in     = plain_chunk.length;
190         z->total_in     = 0;
191
192         z->next_out     = comp_chunk.data + 2;
193         z->avail_out    = comp_chunk.length - 2;
194         z->total_out    = 0;
195
196         if (!z->opaque) {
197                 /* the first time we need to intialize completely */
198                 z->zalloc       = ndr_zlib_alloc;
199                 z->zfree        = ndr_zlib_free;
200                 z->opaque       = ndrpull;
201
202                 /* TODO: find how to trigger the same parameters windows uses */
203                 z_ret = deflateInit2(z,
204                                      Z_DEFAULT_COMPRESSION,
205                                      Z_DEFLATED,
206                                      -15,
207                                      9,
208                                      Z_DEFAULT_STRATEGY);
209                 if (z_ret != Z_OK) {
210                         return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION,
211                                               "Bad deflateInit2 error %s(%d) (PUSH)",
212                                               zError(z_ret), z_ret);
213
214                 }
215         } else {
216                 /* TODO: keep the window */
217                 z_ret = deflateReset(z);
218                 if (z_ret != Z_OK) {
219                         return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION,
220                                               "Bad delateReset2 error %s(%d) (PUSH)",
221                                               zError(z_ret), z_ret);
222                 }
223         }
224
225         /* call deflate untill we get Z_STREAM_END or an error */
226         while (true) {
227                 z_ret = deflate(z, Z_FINISH);
228                 if (z_ret != Z_OK) break;
229         }
230         if (z_ret != Z_STREAM_END) {
231                 return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION,
232                                       "Bad delate(Z_BLOCK) error %s(%d) (PUSH)",
233                                       zError(z_ret), z_ret);
234         }
235
236         if (z->avail_in) {
237                 return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION,
238                                       "MSZIP not all avail_in[%u] bytes consumed (PUSH)",
239                                       z->avail_in);
240         }
241
242         comp_chunk_size = 2 + z->total_out;
243
244         tmp_offset = ndrpush->offset;
245         ndrpush->offset = comp_chunk_size_offset;
246         NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, comp_chunk_size));
247         ndrpush->offset = tmp_offset;
248
249         ndrpush->offset += comp_chunk_size;
250         return NDR_ERR_SUCCESS;
251 }
252
253 static enum ndr_err_code ndr_pull_compression_xpress_chunk(struct ndr_pull *ndrpull,
254                                                   struct ndr_push *ndrpush,
255                                                   bool *last)
256 {
257         DATA_BLOB comp_chunk;
258         DATA_BLOB plain_chunk;
259         uint32_t comp_chunk_offset;
260         uint32_t plain_chunk_offset;
261         uint32_t comp_chunk_size;
262         uint32_t plain_chunk_size;
263
264         NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &plain_chunk_size));
265         if (plain_chunk_size > 0x00010000) {
266                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, "Bad XPRESS plain chunk size %08X > 0x00010000 (PULL)", 
267                                       plain_chunk_size);
268         }
269
270         NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &comp_chunk_size));
271
272         comp_chunk_offset = ndrpull->offset;
273         NDR_CHECK(ndr_pull_advance(ndrpull, comp_chunk_size));
274         comp_chunk.length = comp_chunk_size;
275         comp_chunk.data = ndrpull->data + comp_chunk_offset;
276
277         plain_chunk_offset = ndrpush->offset;
278         NDR_CHECK(ndr_push_zero(ndrpush, plain_chunk_size));
279         plain_chunk.length = plain_chunk_size;
280         plain_chunk.data = ndrpush->data + plain_chunk_offset;
281
282         DEBUG(10,("XPRESS plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
283                   plain_chunk_size, plain_chunk_size, comp_chunk_size, comp_chunk_size));
284
285         /* Uncompressing the buffer using LZ Xpress algorithm */
286         lzxpress_decompress(&comp_chunk, &plain_chunk);
287
288         if ((plain_chunk_size < 0x00010000) || (ndrpull->offset+4 >= ndrpull->data_size)) {
289                 /* this is the last chunk */
290                 *last = true;
291         }
292
293         return NDR_ERR_SUCCESS;
294 }
295
296 static enum ndr_err_code ndr_push_compression_xpress_chunk(struct ndr_push *ndrpush,
297                                                            struct ndr_pull *ndrpull,
298                                                            bool *last)
299 {
300         return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION, "XPRESS compression is not supported yet (PUSH)");
301 }
302
303 /*
304   handle compressed subcontext buffers, which in midl land are user-marshalled, but
305   we use magic in pidl to make them easier to cope with
306 */
307 enum ndr_err_code ndr_pull_compression_start(struct ndr_pull *subndr,
308                                     struct ndr_pull **_comndr,
309                                     enum ndr_compression_alg compression_alg,
310                                     ssize_t decompressed_len)
311 {
312         struct ndr_push *ndrpush;
313         struct ndr_pull *comndr;
314         DATA_BLOB uncompressed;
315         bool last = false;
316         z_stream z;
317
318         ndrpush = ndr_push_init_ctx(subndr, subndr->iconv_convenience);
319         NDR_ERR_HAVE_NO_MEMORY(ndrpush);
320
321         switch (compression_alg) {
322         case NDR_COMPRESSION_MSZIP:
323                 ZERO_STRUCT(z);
324                 while (!last) {
325                         NDR_CHECK(ndr_pull_compression_mszip_chunk(subndr, ndrpush, &z, &last));
326                 }
327                 break;
328
329         case NDR_COMPRESSION_XPRESS:
330                 while (!last) {
331                         NDR_CHECK(ndr_pull_compression_xpress_chunk(subndr, ndrpush, &last));
332                 }
333                 break;
334
335         default:
336                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PULL)",
337                                       compression_alg);
338         }
339
340         uncompressed = ndr_push_blob(ndrpush);
341         if (uncompressed.length != decompressed_len) {
342                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION,
343                                       "Bad uncompressed_len [%u] != [%u](0x%08X) (PULL)",
344                                       (int)uncompressed.length,
345                                       (int)decompressed_len,
346                                       (int)decompressed_len);
347         }
348
349         comndr = talloc_zero(subndr, struct ndr_pull);
350         NDR_ERR_HAVE_NO_MEMORY(comndr);
351         comndr->flags           = subndr->flags;
352         comndr->current_mem_ctx = subndr->current_mem_ctx;
353
354         comndr->data            = uncompressed.data;
355         comndr->data_size       = uncompressed.length;
356         comndr->offset          = 0;
357
358         comndr->iconv_convenience = talloc_reference(comndr, subndr->iconv_convenience);
359
360         *_comndr = comndr;
361         return NDR_ERR_SUCCESS;
362 }
363
364 enum ndr_err_code ndr_pull_compression_end(struct ndr_pull *subndr,
365                                   struct ndr_pull *comndr,
366                                   enum ndr_compression_alg compression_alg,
367                                   ssize_t decompressed_len)
368 {
369         return NDR_ERR_SUCCESS;
370 }
371
372 /*
373   push a compressed subcontext
374 */
375 enum ndr_err_code ndr_push_compression_start(struct ndr_push *subndr,
376                                     struct ndr_push **_uncomndr,
377                                     enum ndr_compression_alg compression_alg,
378                                     ssize_t decompressed_len)
379 {
380         struct ndr_push *uncomndr;
381
382         switch (compression_alg) {
383         case NDR_COMPRESSION_MSZIP:
384         case NDR_COMPRESSION_XPRESS:
385                 break;
386         default:
387                 return ndr_push_error(subndr, NDR_ERR_COMPRESSION,
388                                       "Bad compression algorithm %d (PUSH)",
389                                       compression_alg);
390         }
391
392         uncomndr = ndr_push_init_ctx(subndr, subndr->iconv_convenience);
393         NDR_ERR_HAVE_NO_MEMORY(uncomndr);
394         uncomndr->flags = subndr->flags;
395
396         *_uncomndr = uncomndr;
397         return NDR_ERR_SUCCESS;
398 }
399
400 /*
401   push a compressed subcontext
402 */
403 enum ndr_err_code ndr_push_compression_end(struct ndr_push *subndr,
404                                   struct ndr_push *uncomndr,
405                                   enum ndr_compression_alg compression_alg,
406                                   ssize_t decompressed_len)
407 {
408         struct ndr_pull *ndrpull;
409         bool last = false;
410         z_stream z;
411
412         ndrpull = talloc_zero(uncomndr, struct ndr_pull);
413         NDR_ERR_HAVE_NO_MEMORY(ndrpull);
414         ndrpull->flags          = uncomndr->flags;
415         ndrpull->data           = uncomndr->data;
416         ndrpull->data_size      = uncomndr->offset;
417         ndrpull->offset         = 0;
418
419         ndrpull->iconv_convenience = talloc_reference(ndrpull, subndr->iconv_convenience);
420
421         switch (compression_alg) {
422         case NDR_COMPRESSION_MSZIP:
423                 ZERO_STRUCT(z);
424                 while (!last) {
425                         NDR_CHECK(ndr_push_compression_mszip_chunk(subndr, ndrpull, &z, &last));
426                 }
427                 break;
428
429         case NDR_COMPRESSION_XPRESS:
430                 while (!last) {
431                         NDR_CHECK(ndr_push_compression_xpress_chunk(subndr, ndrpull, &last));
432                 }
433                 break;
434
435         default:
436                 return ndr_push_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PUSH)", 
437                                       compression_alg);
438         }
439
440         talloc_free(uncomndr);
441         return NDR_ERR_SUCCESS;
442 }