ndr_compression: add common parts of ndr compression
[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_push_compression_mszip_chunk(struct ndr_push *ndrpush,
149                                                           struct ndr_pull *ndrpull,
150                                                           z_stream *z,
151                                                           bool *last)
152 {
153         return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION, "MSZIP compression is not supported yet (PUSH)");
154 }
155
156 static enum ndr_err_code ndr_pull_compression_xpress_chunk(struct ndr_pull *ndrpull,
157                                                   struct ndr_push *ndrpush,
158                                                   bool *last)
159 {
160         DATA_BLOB comp_chunk;
161         DATA_BLOB plain_chunk;
162         uint32_t comp_chunk_offset;
163         uint32_t plain_chunk_offset;
164         uint32_t comp_chunk_size;
165         uint32_t plain_chunk_size;
166
167         NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &plain_chunk_size));
168         if (plain_chunk_size > 0x00010000) {
169                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, "Bad XPRESS plain chunk size %08X > 0x00010000 (PULL)", 
170                                       plain_chunk_size);
171         }
172
173         NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &comp_chunk_size));
174
175         comp_chunk_offset = ndrpull->offset;
176         NDR_CHECK(ndr_pull_advance(ndrpull, comp_chunk_size));
177         comp_chunk.length = comp_chunk_size;
178         comp_chunk.data = ndrpull->data + comp_chunk_offset;
179
180         plain_chunk_offset = ndrpush->offset;
181         NDR_CHECK(ndr_push_zero(ndrpush, plain_chunk_size));
182         plain_chunk.length = plain_chunk_size;
183         plain_chunk.data = ndrpush->data + plain_chunk_offset;
184
185         DEBUG(10,("XPRESS plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
186                   plain_chunk_size, plain_chunk_size, comp_chunk_size, comp_chunk_size));
187
188         /* Uncompressing the buffer using LZ Xpress algorithm */
189         lzxpress_decompress(&comp_chunk, &plain_chunk);
190
191         if ((plain_chunk_size < 0x00010000) || (ndrpull->offset+4 >= ndrpull->data_size)) {
192                 /* this is the last chunk */
193                 *last = true;
194         }
195
196         return NDR_ERR_SUCCESS;
197 }
198
199 static enum ndr_err_code ndr_push_compression_xpress_chunk(struct ndr_push *ndrpush,
200                                                            struct ndr_pull *ndrpull,
201                                                            bool *last)
202 {
203         return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION, "XPRESS compression is not supported yet (PUSH)");
204 }
205
206 /*
207   handle compressed subcontext buffers, which in midl land are user-marshalled, but
208   we use magic in pidl to make them easier to cope with
209 */
210 enum ndr_err_code ndr_pull_compression_start(struct ndr_pull *subndr,
211                                     struct ndr_pull **_comndr,
212                                     enum ndr_compression_alg compression_alg,
213                                     ssize_t decompressed_len)
214 {
215         struct ndr_push *ndrpush;
216         struct ndr_pull *comndr;
217         DATA_BLOB uncompressed;
218         bool last = false;
219         z_stream z;
220
221         ndrpush = ndr_push_init_ctx(subndr, subndr->iconv_convenience);
222         NDR_ERR_HAVE_NO_MEMORY(ndrpush);
223
224         switch (compression_alg) {
225         case NDR_COMPRESSION_MSZIP:
226                 ZERO_STRUCT(z);
227                 while (!last) {
228                         NDR_CHECK(ndr_pull_compression_mszip_chunk(subndr, ndrpush, &z, &last));
229                 }
230                 break;
231
232         case NDR_COMPRESSION_XPRESS:
233                 while (!last) {
234                         NDR_CHECK(ndr_pull_compression_xpress_chunk(subndr, ndrpush, &last));
235                 }
236                 break;
237
238         default:
239                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PULL)",
240                                       compression_alg);
241         }
242
243         uncompressed = ndr_push_blob(ndrpush);
244         if (uncompressed.length != decompressed_len) {
245                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION,
246                                       "Bad uncompressed_len [%u] != [%u](0x%08X) (PULL)",
247                                       (int)uncompressed.length,
248                                       (int)decompressed_len,
249                                       (int)decompressed_len);
250         }
251
252         comndr = talloc_zero(subndr, struct ndr_pull);
253         NDR_ERR_HAVE_NO_MEMORY(comndr);
254         comndr->flags           = subndr->flags;
255         comndr->current_mem_ctx = subndr->current_mem_ctx;
256
257         comndr->data            = uncompressed.data;
258         comndr->data_size       = uncompressed.length;
259         comndr->offset          = 0;
260
261         comndr->iconv_convenience = talloc_reference(comndr, subndr->iconv_convenience);
262
263         *_comndr = comndr;
264         return NDR_ERR_SUCCESS;
265 }
266
267 enum ndr_err_code ndr_pull_compression_end(struct ndr_pull *subndr,
268                                   struct ndr_pull *comndr,
269                                   enum ndr_compression_alg compression_alg,
270                                   ssize_t decompressed_len)
271 {
272         return NDR_ERR_SUCCESS;
273 }
274
275 /*
276   push a compressed subcontext
277 */
278 enum ndr_err_code ndr_push_compression_start(struct ndr_push *subndr,
279                                     struct ndr_push **_uncomndr,
280                                     enum ndr_compression_alg compression_alg,
281                                     ssize_t decompressed_len)
282 {
283         struct ndr_push *uncomndr;
284
285         switch (compression_alg) {
286         case NDR_COMPRESSION_MSZIP:
287         case NDR_COMPRESSION_XPRESS:
288                 break;
289         default:
290                 return ndr_push_error(subndr, NDR_ERR_COMPRESSION,
291                                       "Bad compression algorithm %d (PUSH)",
292                                       compression_alg);
293         }
294
295         uncomndr = ndr_push_init_ctx(subndr, subndr->iconv_convenience);
296         NDR_ERR_HAVE_NO_MEMORY(uncomndr);
297         uncomndr->flags = subndr->flags;
298
299         *_uncomndr = uncomndr;
300         return NDR_ERR_SUCCESS;
301 }
302
303 /*
304   push a compressed subcontext
305 */
306 enum ndr_err_code ndr_push_compression_end(struct ndr_push *subndr,
307                                   struct ndr_push *uncomndr,
308                                   enum ndr_compression_alg compression_alg,
309                                   ssize_t decompressed_len)
310 {
311         struct ndr_pull *ndrpull;
312         bool last = false;
313         z_stream z;
314
315         ndrpull = talloc_zero(uncomndr, struct ndr_pull);
316         NDR_ERR_HAVE_NO_MEMORY(ndrpull);
317         ndrpull->flags          = uncomndr->flags;
318         ndrpull->data           = uncomndr->data;
319         ndrpull->data_size      = uncomndr->offset;
320         ndrpull->offset         = 0;
321
322         ndrpull->iconv_convenience = talloc_reference(ndrpull, subndr->iconv_convenience);
323
324         switch (compression_alg) {
325         case NDR_COMPRESSION_MSZIP:
326                 ZERO_STRUCT(z);
327                 while (!last) {
328                         NDR_CHECK(ndr_push_compression_mszip_chunk(subndr, ndrpull, &z, &last));
329                 }
330                 break;
331
332         case NDR_COMPRESSION_XPRESS:
333                 while (!last) {
334                         NDR_CHECK(ndr_push_compression_xpress_chunk(subndr, ndrpull, &last));
335                 }
336                 break;
337
338         default:
339                 return ndr_push_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PUSH)", 
340                                       compression_alg);
341         }
342
343         talloc_free(uncomndr);
344         return NDR_ERR_SUCCESS;
345 }