Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into 4-0-abartlet
[tprouty/samba.git] / source4 / 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_pull_compression_mszip(struct ndr_pull *subndr,
149                                            struct ndr_pull **_comndr,
150                                            ssize_t decompressed_len)
151 {
152         struct ndr_push *ndrpush;
153         struct ndr_pull *comndr;
154         DATA_BLOB uncompressed;
155         uint32_t payload_header[4];
156         uint32_t payload_size;
157         uint32_t payload_offset;
158         uint8_t *payload;
159         z_stream z;
160         bool last = false;
161
162         ndrpush = ndr_push_init_ctx(subndr, subndr->iconv_convenience);
163         NDR_ERR_HAVE_NO_MEMORY(ndrpush);
164
165         ZERO_STRUCT(z);
166
167         while (!last) {
168                 NDR_CHECK(ndr_pull_compression_mszip_chunk(subndr, ndrpush, &z, &last));
169         }
170
171         uncompressed = ndr_push_blob(ndrpush);
172
173         if (uncompressed.length != decompressed_len) {
174                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad MSZIP uncompressed_len [%u] != [%d] (PULL)",
175                                       (int)uncompressed.length, (int)decompressed_len);
176         }
177
178         comndr = talloc_zero(subndr, struct ndr_pull);
179         NDR_ERR_HAVE_NO_MEMORY(comndr);
180         comndr->flags           = subndr->flags;
181         comndr->current_mem_ctx = subndr->current_mem_ctx;
182
183         comndr->data            = uncompressed.data;
184         comndr->data_size       = uncompressed.length;
185         comndr->offset          = 0;
186
187         comndr->iconv_convenience = talloc_reference(comndr, subndr->iconv_convenience);
188
189         NDR_CHECK(ndr_pull_uint32(comndr, NDR_SCALARS, &payload_header[0]));
190         NDR_CHECK(ndr_pull_uint32(comndr, NDR_SCALARS, &payload_header[1]));
191         NDR_CHECK(ndr_pull_uint32(comndr, NDR_SCALARS, &payload_header[2]));
192         NDR_CHECK(ndr_pull_uint32(comndr, NDR_SCALARS, &payload_header[3]));
193
194         if (payload_header[0] != 0x00081001) {
195                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad MSZIP payload_header[0] [0x%08X] != [0x00081001] (PULL)",
196                                       payload_header[0]);
197         }
198         if (payload_header[1] != 0xCCCCCCCC) {
199                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad MSZIP payload_header[1] [0x%08X] != [0xCCCCCCCC] (PULL)",
200                                       payload_header[1]);
201         }
202
203         payload_size = payload_header[2];
204
205         if (payload_header[3] != 0x00000000) {
206                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad MSZIP payload_header[3] [0x%08X] != [0x00000000] (PULL)",
207                                       payload_header[3]);
208         }
209
210         payload_offset = comndr->offset;
211         NDR_CHECK(ndr_pull_advance(comndr, payload_size));
212         payload = comndr->data + payload_offset;
213
214         comndr->data            = payload;
215         comndr->data_size       = payload_size;
216         comndr->offset          = 0;
217
218         *_comndr = comndr;
219         return NDR_ERR_SUCCESS;
220 }
221
222 static enum ndr_err_code ndr_push_compression_mszip(struct ndr_push *subndr,
223                                            struct ndr_push *comndr)
224 {
225         return ndr_push_error(subndr, NDR_ERR_COMPRESSION, "Sorry MSZIP compression is not supported yet (PUSH)");
226 }
227
228 static enum ndr_err_code ndr_pull_compression_xpress_chunk(struct ndr_pull *ndrpull,
229                                                   struct ndr_push *ndrpush,
230                                                   bool *last)
231 {
232         DATA_BLOB comp_chunk;
233         DATA_BLOB plain_chunk;
234         uint32_t comp_chunk_offset;
235         uint32_t plain_chunk_offset;
236         uint32_t comp_chunk_size;
237         uint32_t plain_chunk_size;
238
239         NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &plain_chunk_size));
240         if (plain_chunk_size > 0x00010000) {
241                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, "Bad XPRESS plain chunk size %08X > 0x00010000 (PULL)", 
242                                       plain_chunk_size);
243         }
244
245         NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &comp_chunk_size));
246
247         comp_chunk_offset = ndrpull->offset;
248         NDR_CHECK(ndr_pull_advance(ndrpull, comp_chunk_size));
249         comp_chunk.length = comp_chunk_size;
250         comp_chunk.data = ndrpull->data + comp_chunk_offset;
251
252         plain_chunk_offset = ndrpush->offset;
253         NDR_CHECK(ndr_push_zero(ndrpush, plain_chunk_size));
254         plain_chunk.length = plain_chunk_size;
255         plain_chunk.data = ndrpush->data + plain_chunk_offset;
256
257         DEBUG(10,("XPRESS plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
258                   plain_chunk_size, plain_chunk_size, comp_chunk_size, comp_chunk_size));
259
260         /* Uncompressing the buffer using LZ Xpress algorithm */
261         lzxpress_decompress(&comp_chunk, &plain_chunk);
262
263         if ((plain_chunk_size < 0x00010000) || (ndrpull->offset+4 >= ndrpull->data_size)) {
264                 /* this is the last chunk */
265                 *last = true;
266         }
267
268         return NDR_ERR_SUCCESS;
269 }
270
271 static enum ndr_err_code ndr_pull_compression_xpress(struct ndr_pull *subndr,
272                                             struct ndr_pull **_comndr,
273                                             ssize_t decompressed_len)
274 {
275         struct ndr_push *ndrpush;
276         struct ndr_pull *comndr;
277         DATA_BLOB uncompressed;
278         uint32_t payload_header[4];
279         uint32_t payload_size;
280         uint32_t payload_offset;
281         uint8_t *payload;
282         bool last = false;
283
284         ndrpush = ndr_push_init_ctx(subndr, subndr->iconv_convenience);
285         NDR_ERR_HAVE_NO_MEMORY(ndrpush);
286
287         while (!last) {
288                 NDR_CHECK(ndr_pull_compression_xpress_chunk(subndr, ndrpush, &last));
289         }
290
291         uncompressed = ndr_push_blob(ndrpush);
292         if (uncompressed.length != decompressed_len) {
293                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION,
294                                       "Bad XPRESS uncompressed_len [%u] != [%u](0x%08X) (PULL)",
295                                       (int)uncompressed.length,
296                                       (int)decompressed_len,
297                                       (int)decompressed_len);
298         }
299
300         comndr = talloc_zero(subndr, struct ndr_pull);
301         NDR_ERR_HAVE_NO_MEMORY(comndr);
302         comndr->flags           = subndr->flags;
303         comndr->current_mem_ctx = subndr->current_mem_ctx;
304
305         comndr->data            = uncompressed.data;
306         comndr->data_size       = uncompressed.length;
307         comndr->offset          = 0;
308
309         comndr->iconv_convenience = talloc_reference(comndr, subndr->iconv_convenience);
310
311         NDR_CHECK(ndr_pull_uint32(comndr, NDR_SCALARS, &payload_header[0]));
312         NDR_CHECK(ndr_pull_uint32(comndr, NDR_SCALARS, &payload_header[1]));
313         NDR_CHECK(ndr_pull_uint32(comndr, NDR_SCALARS, &payload_header[2]));
314         NDR_CHECK(ndr_pull_uint32(comndr, NDR_SCALARS, &payload_header[3]));
315
316         if (payload_header[0] != 0x00081001) {
317                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION,
318                                       "Bad XPRESS payload_header[0] [0x%08X] != [0x00081001] (PULL)",
319                                       payload_header[0]);
320         }
321         if (payload_header[1] != 0xCCCCCCCC) {
322                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION,
323                                       "Bad XPRESS payload_header[1] [0x%08X] != [0xCCCCCCCC] (PULL)",
324                                       payload_header[1]);
325         }
326
327         payload_size = payload_header[2];
328
329         if (payload_header[3] != 0x00000000) {
330                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION,
331                                       "Bad XPRESS payload_header[3] [0x%08X] != [0x00000000] (PULL)",
332                                       payload_header[3]);
333         }
334
335         payload_offset = comndr->offset;
336         NDR_CHECK(ndr_pull_advance(comndr, payload_size));
337         payload = comndr->data + payload_offset;
338
339         comndr->data            = payload;
340         comndr->data_size       = payload_size;
341         comndr->offset          = 0;
342
343         *_comndr = comndr;
344         return NDR_ERR_SUCCESS;
345 }
346
347 static enum ndr_err_code ndr_push_compression_xpress(struct ndr_push *subndr,
348                                             struct ndr_push *comndr)
349 {
350         return ndr_push_error(subndr, NDR_ERR_COMPRESSION, "XPRESS compression is not supported yet (PUSH)");
351 }
352
353 /*
354   handle compressed subcontext buffers, which in midl land are user-marshalled, but
355   we use magic in pidl to make them easier to cope with
356 */
357 enum ndr_err_code ndr_pull_compression_start(struct ndr_pull *subndr,
358                                     struct ndr_pull **_comndr,
359                                     enum ndr_compression_alg compression_alg,
360                                     ssize_t decompressed_len)
361 {
362         switch (compression_alg) {
363         case NDR_COMPRESSION_MSZIP:
364                 return ndr_pull_compression_mszip(subndr, _comndr, decompressed_len);
365         case NDR_COMPRESSION_XPRESS:
366                 return ndr_pull_compression_xpress(subndr, _comndr, decompressed_len);
367         default:
368                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PULL)", 
369                                       compression_alg);
370         }
371         return NDR_ERR_SUCCESS;
372 }
373
374 enum ndr_err_code ndr_pull_compression_end(struct ndr_pull *subndr,
375                                   struct ndr_pull *comndr,
376                                   enum ndr_compression_alg compression_alg,
377                                   ssize_t decompressed_len)
378 {
379         return NDR_ERR_SUCCESS;
380 }
381
382 /*
383   push a compressed subcontext
384 */
385 enum ndr_err_code ndr_push_compression_start(struct ndr_push *subndr,
386                                     struct ndr_push **_comndr,
387                                     enum ndr_compression_alg compression_alg,
388                                     ssize_t decompressed_len)
389 {
390         struct ndr_push *comndr;
391
392         comndr = ndr_push_init_ctx(subndr, subndr->iconv_convenience);
393         NDR_ERR_HAVE_NO_MEMORY(comndr);
394         comndr->flags   = subndr->flags;
395
396         *_comndr = comndr;
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 *comndr,
405                                   enum ndr_compression_alg compression_alg,
406                                   ssize_t decompressed_len)
407 {
408         switch (compression_alg) {
409         case NDR_COMPRESSION_MSZIP:
410                 return ndr_push_compression_mszip(subndr, comndr);
411         case NDR_COMPRESSION_XPRESS:
412                 return ndr_push_compression_xpress(subndr, comndr);
413         default:
414                 return ndr_push_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PUSH)", 
415                                       compression_alg);
416         }
417         return NDR_ERR_SUCCESS;
418 }