libndr/compression: pass down compressed length in ndr_pull_compression_start
[samba.git] / 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, -MAX_WBITS);
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                  (unsigned int)plain_chunk.length,
264                  (unsigned int)plain_chunk.length,
265                  comp_chunk_size, comp_chunk_size));
266
267         ndrpush->offset += comp_chunk_size;
268         return NDR_ERR_SUCCESS;
269 }
270
271 static enum ndr_err_code ndr_pull_compression_xpress_chunk(struct ndr_pull *ndrpull,
272                                                   struct ndr_push *ndrpush,
273                                                   bool *last)
274 {
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;
282
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);
287         }
288
289         NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &comp_chunk_size));
290
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;
295
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;
300
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));
303
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);
313         }
314         plain_chunk.length = ret;
315
316         if ((plain_chunk_size < 0x00010000) || (ndrpull->offset+4 >= ndrpull->data_size)) {
317                 /* this is the last chunk */
318                 *last = true;
319         }
320
321         return NDR_ERR_SUCCESS;
322 }
323
324 static enum ndr_err_code ndr_push_compression_xpress_chunk(struct ndr_push *ndrpush,
325                                                            struct ndr_pull *ndrpull,
326                                                            bool *last)
327 {
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;
337
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));
341
342         plain_chunk.data = ndrpull->data + plain_chunk_offset;
343         plain_chunk.length = plain_chunk_size;
344
345         if (plain_chunk_size < max_plain_size) {
346                 *last = true;
347         }
348
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));
352
353         NDR_CHECK(ndr_push_expand(ndrpush, max_comp_size));
354
355         comp_chunk.data = ndrpush->data + ndrpush->offset;
356         comp_chunk.length = max_comp_size;
357
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);
367         }
368         comp_chunk.length = ret;
369
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;
374
375         ndrpush->offset += comp_chunk.length;
376         return NDR_ERR_SUCCESS;
377 }
378
379 /*
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
382 */
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,
387                                     ssize_t compressed_len)
388 {
389         struct ndr_push *ndrpush;
390         struct ndr_pull *comndr;
391         DATA_BLOB uncompressed;
392         bool last = false;
393         z_stream z;
394
395         ndrpush = ndr_push_init_ctx(subndr);
396         NDR_ERR_HAVE_NO_MEMORY(ndrpush);
397
398         switch (compression_alg) {
399         case NDR_COMPRESSION_MSZIP:
400                 ZERO_STRUCT(z);
401                 while (!last) {
402                         NDR_CHECK(ndr_pull_compression_mszip_chunk(subndr, ndrpush, &z, &last));
403                 }
404                 break;
405
406         case NDR_COMPRESSION_XPRESS:
407                 while (!last) {
408                         NDR_CHECK(ndr_pull_compression_xpress_chunk(subndr, ndrpush, &last));
409                 }
410                 break;
411
412         default:
413                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PULL)",
414                                       compression_alg);
415         }
416
417         uncompressed = ndr_push_blob(ndrpush);
418         if (uncompressed.length != decompressed_len) {
419                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION,
420                                       "Bad uncompressed_len [%u] != [%u](0x%08X) (PULL)",
421                                       (int)uncompressed.length,
422                                       (int)decompressed_len,
423                                       (int)decompressed_len);
424         }
425
426         comndr = talloc_zero(subndr, struct ndr_pull);
427         NDR_ERR_HAVE_NO_MEMORY(comndr);
428         comndr->flags           = subndr->flags;
429         comndr->current_mem_ctx = subndr->current_mem_ctx;
430
431         comndr->data            = uncompressed.data;
432         comndr->data_size       = uncompressed.length;
433         comndr->offset          = 0;
434
435         *_comndr = comndr;
436         return NDR_ERR_SUCCESS;
437 }
438
439 enum ndr_err_code ndr_pull_compression_end(struct ndr_pull *subndr,
440                                   struct ndr_pull *comndr,
441                                   enum ndr_compression_alg compression_alg,
442                                   ssize_t decompressed_len)
443 {
444         return NDR_ERR_SUCCESS;
445 }
446
447 /*
448   push a compressed subcontext
449 */
450 enum ndr_err_code ndr_push_compression_start(struct ndr_push *subndr,
451                                     struct ndr_push **_uncomndr,
452                                     enum ndr_compression_alg compression_alg,
453                                     ssize_t decompressed_len)
454 {
455         struct ndr_push *uncomndr;
456
457         switch (compression_alg) {
458         case NDR_COMPRESSION_MSZIP:
459         case NDR_COMPRESSION_XPRESS:
460                 break;
461         default:
462                 return ndr_push_error(subndr, NDR_ERR_COMPRESSION,
463                                       "Bad compression algorithm %d (PUSH)",
464                                       compression_alg);
465         }
466
467         uncomndr = ndr_push_init_ctx(subndr);
468         NDR_ERR_HAVE_NO_MEMORY(uncomndr);
469         uncomndr->flags = subndr->flags;
470
471         *_uncomndr = uncomndr;
472         return NDR_ERR_SUCCESS;
473 }
474
475 /*
476   push a compressed subcontext
477 */
478 enum ndr_err_code ndr_push_compression_end(struct ndr_push *subndr,
479                                   struct ndr_push *uncomndr,
480                                   enum ndr_compression_alg compression_alg,
481                                   ssize_t decompressed_len)
482 {
483         struct ndr_pull *ndrpull;
484         bool last = false;
485         z_stream z;
486
487         ndrpull = talloc_zero(uncomndr, struct ndr_pull);
488         NDR_ERR_HAVE_NO_MEMORY(ndrpull);
489         ndrpull->flags          = uncomndr->flags;
490         ndrpull->data           = uncomndr->data;
491         ndrpull->data_size      = uncomndr->offset;
492         ndrpull->offset         = 0;
493
494         switch (compression_alg) {
495         case NDR_COMPRESSION_MSZIP:
496                 ZERO_STRUCT(z);
497                 while (!last) {
498                         NDR_CHECK(ndr_push_compression_mszip_chunk(subndr, ndrpull, &z, &last));
499                 }
500                 break;
501
502         case NDR_COMPRESSION_XPRESS:
503                 while (!last) {
504                         NDR_CHECK(ndr_push_compression_xpress_chunk(subndr, ndrpull, &last));
505                 }
506                 break;
507
508         default:
509                 return ndr_push_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PUSH)",
510                                       compression_alg);
511         }
512
513         talloc_free(uncomndr);
514         return NDR_ERR_SUCCESS;
515 }