Move common libraries from root to lib/.
[kai/samba-autobuild/.git] / source4 / librpc / ndr / ndr_compression.c
index f6de0a1319b6da08afcd145ec244c88a28b1db6c..05a80e20b2cc112699f37ab68212f0bdf3a09cab 100644 (file)
@@ -21,7 +21,7 @@
 */
 
 #include "includes.h"
-#include "lib/compression/lzxpress.h"
+#include "../lib/compression/lzxpress.h"
 #include "librpc/ndr/libndr.h"
 #include "librpc/ndr/ndr_compression.h"
 #include <zlib.h>
@@ -57,8 +57,8 @@ static enum ndr_err_code ndr_pull_compression_mszip_chunk(struct ndr_pull *ndrpu
 
        NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &comp_chunk_size));
 
-       DEBUG(10,("MSZIP plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
-                 plain_chunk_size, plain_chunk_size, comp_chunk_size, comp_chunk_size));
+       DEBUG(9,("MSZIP plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
+                plain_chunk_size, plain_chunk_size, comp_chunk_size, comp_chunk_size));
 
        comp_chunk_offset = ndrpull->offset;
        NDR_CHECK(ndr_pull_advance(ndrpull, comp_chunk_size));
@@ -104,13 +104,6 @@ static enum ndr_err_code ndr_pull_compression_mszip_chunk(struct ndr_pull *ndrpu
                                              zError(z_ret), z_ret);
 
                }
-       } else {
-               z_ret = inflateReset2(z, Z_RESET_KEEP_WINDOW);
-               if (z_ret != Z_OK) {
-                       return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
-                                             "Bad inflateReset2 error %s(%d) (PULL)",
-                                             zError(z_ret), z_ret);
-               }
        }
 
        /* call inflate untill we get Z_STREAM_END or an error */
@@ -142,13 +135,135 @@ static enum ndr_err_code ndr_pull_compression_mszip_chunk(struct ndr_pull *ndrpu
                *last = true;
        }
 
+       z_ret = inflateReset(z);
+       if (z_ret != Z_OK) {
+               return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
+                                     "Bad inflateReset error %s(%d) (PULL)",
+                                     zError(z_ret), z_ret);
+       }
+
+       z_ret = inflateSetDictionary(z, plain_chunk.data, plain_chunk.length);
+       if (z_ret != Z_OK) {
+               return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
+                                     "Bad inflateSetDictionary error %s(%d) (PULL)",
+                                     zError(z_ret), z_ret);
+       }
+
        return NDR_ERR_SUCCESS;
 }
 
-static enum ndr_err_code ndr_push_compression_mszip(struct ndr_push *subndr,
-                                          struct ndr_push *comndr)
+static enum ndr_err_code ndr_push_compression_mszip_chunk(struct ndr_push *ndrpush,
+                                                         struct ndr_pull *ndrpull,
+                                                         z_stream *z,
+                                                         bool *last)
 {
-       return ndr_push_error(subndr, NDR_ERR_COMPRESSION, "Sorry MSZIP compression is not supported yet (PUSH)");
+       DATA_BLOB comp_chunk;
+       uint32_t comp_chunk_size;
+       uint32_t comp_chunk_size_offset;
+       DATA_BLOB plain_chunk;
+       uint32_t plain_chunk_size;
+       uint32_t plain_chunk_offset;
+       uint32_t max_plain_size = 0x00008000;
+       uint32_t max_comp_size = 0x00008000 + 2 + 12 /*TODO: what value do we really need here?*/;
+       uint32_t tmp_offset;
+       int z_ret;
+
+       plain_chunk_size = MIN(max_plain_size, ndrpull->data_size - ndrpull->offset);
+       plain_chunk_offset = ndrpull->offset;
+       NDR_CHECK(ndr_pull_advance(ndrpull, plain_chunk_size));
+
+       plain_chunk.data = ndrpull->data + plain_chunk_offset;
+       plain_chunk.length = plain_chunk_size;
+
+       if (plain_chunk_size < max_plain_size) {
+               *last = true;
+       }
+
+       NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, plain_chunk_size));
+       comp_chunk_size_offset = ndrpush->offset;
+       NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, 0xFEFEFEFE));
+
+       NDR_CHECK(ndr_push_expand(ndrpush, max_comp_size));
+
+       comp_chunk.data = ndrpush->data + ndrpush->offset;
+       comp_chunk.length = max_comp_size;
+
+       /* CK = Chris Kirmse, official Microsoft purloiner */
+       comp_chunk.data[0] = 'C';
+       comp_chunk.data[1] = 'K';
+
+       z->next_in      = plain_chunk.data;
+       z->avail_in     = plain_chunk.length;
+       z->total_in     = 0;
+
+       z->next_out     = comp_chunk.data + 2;
+       z->avail_out    = comp_chunk.length - 2;
+       z->total_out    = 0;
+
+       if (!z->opaque) {
+               /* the first time we need to intialize completely */
+               z->zalloc       = ndr_zlib_alloc;
+               z->zfree        = ndr_zlib_free;
+               z->opaque       = ndrpull;
+
+               /* TODO: find how to trigger the same parameters windows uses */
+               z_ret = deflateInit2(z,
+                                    Z_DEFAULT_COMPRESSION,
+                                    Z_DEFLATED,
+                                    -15,
+                                    9,
+                                    Z_DEFAULT_STRATEGY);
+               if (z_ret != Z_OK) {
+                       return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION,
+                                             "Bad deflateInit2 error %s(%d) (PUSH)",
+                                             zError(z_ret), z_ret);
+
+               }
+       }
+
+       /* call deflate untill we get Z_STREAM_END or an error */
+       while (true) {
+               z_ret = deflate(z, Z_FINISH);
+               if (z_ret != Z_OK) break;
+       }
+       if (z_ret != Z_STREAM_END) {
+               return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION,
+                                     "Bad delate(Z_BLOCK) error %s(%d) (PUSH)",
+                                     zError(z_ret), z_ret);
+       }
+
+       if (z->avail_in) {
+               return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION,
+                                     "MSZIP not all avail_in[%u] bytes consumed (PUSH)",
+                                     z->avail_in);
+       }
+
+       comp_chunk_size = 2 + z->total_out;
+
+       z_ret = deflateReset(z);
+       if (z_ret != Z_OK) {
+               return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
+                                     "Bad deflateReset error %s(%d) (PULL)",
+                                     zError(z_ret), z_ret);
+       }
+
+       z_ret = deflateSetDictionary(z, plain_chunk.data, plain_chunk.length);
+       if (z_ret != Z_OK) {
+               return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
+                                     "Bad deflateSetDictionary error %s(%d) (PULL)",
+                                     zError(z_ret), z_ret);
+       }
+
+       tmp_offset = ndrpush->offset;
+       ndrpush->offset = comp_chunk_size_offset;
+       NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, comp_chunk_size));
+       ndrpush->offset = tmp_offset;
+
+       DEBUG(9,("MSZIP comp plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
+                plain_chunk.length, plain_chunk.length, comp_chunk_size, comp_chunk_size));
+
+       ndrpush->offset += comp_chunk_size;
+       return NDR_ERR_SUCCESS;
 }
 
 static enum ndr_err_code ndr_pull_compression_xpress_chunk(struct ndr_pull *ndrpull,
@@ -161,6 +276,7 @@ static enum ndr_err_code ndr_pull_compression_xpress_chunk(struct ndr_pull *ndrp
        uint32_t plain_chunk_offset;
        uint32_t comp_chunk_size;
        uint32_t plain_chunk_size;
+       ssize_t ret;
 
        NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &plain_chunk_size));
        if (plain_chunk_size > 0x00010000) {
@@ -180,11 +296,20 @@ static enum ndr_err_code ndr_pull_compression_xpress_chunk(struct ndr_pull *ndrp
        plain_chunk.length = plain_chunk_size;
        plain_chunk.data = ndrpush->data + plain_chunk_offset;
 
-       DEBUG(10,("XPRESS plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
-                 plain_chunk_size, plain_chunk_size, comp_chunk_size, comp_chunk_size));
+       DEBUG(9,("XPRESS plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
+                plain_chunk_size, plain_chunk_size, comp_chunk_size, comp_chunk_size));
 
        /* Uncompressing the buffer using LZ Xpress algorithm */
-       lzxpress_decompress(&comp_chunk, &plain_chunk);
+       ret = lzxpress_decompress(comp_chunk.data,
+                                 comp_chunk.length,
+                                 plain_chunk.data,
+                                 plain_chunk.length);
+       if (ret < 0) {
+               return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
+                                     "XPRESS lzxpress_decompress() returned %d\n",
+                                     ret);
+       }
+       plain_chunk.length = ret;
 
        if ((plain_chunk_size < 0x00010000) || (ndrpull->offset+4 >= ndrpull->data_size)) {
                /* this is the last chunk */
@@ -194,10 +319,59 @@ static enum ndr_err_code ndr_pull_compression_xpress_chunk(struct ndr_pull *ndrp
        return NDR_ERR_SUCCESS;
 }
 
-static enum ndr_err_code ndr_push_compression_xpress(struct ndr_push *subndr,
-                                           struct ndr_push *comndr)
+static enum ndr_err_code ndr_push_compression_xpress_chunk(struct ndr_push *ndrpush,
+                                                          struct ndr_pull *ndrpull,
+                                                          bool *last)
 {
-       return ndr_push_error(subndr, NDR_ERR_COMPRESSION, "XPRESS compression is not supported yet (PUSH)");
+       DATA_BLOB comp_chunk;
+       uint32_t comp_chunk_size_offset;
+       DATA_BLOB plain_chunk;
+       uint32_t plain_chunk_size;
+       uint32_t plain_chunk_offset;
+       uint32_t max_plain_size = 0x00010000;
+       uint32_t max_comp_size = 0x00020000 + 2; /* TODO: use the correct value here */
+       uint32_t tmp_offset;
+       ssize_t ret;
+
+       plain_chunk_size = MIN(max_plain_size, ndrpull->data_size - ndrpull->offset);
+       plain_chunk_offset = ndrpull->offset;
+       NDR_CHECK(ndr_pull_advance(ndrpull, plain_chunk_size));
+
+       plain_chunk.data = ndrpull->data + plain_chunk_offset;
+       plain_chunk.length = plain_chunk_size;
+
+       if (plain_chunk_size < max_plain_size) {
+               *last = true;
+       }
+
+       NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, plain_chunk_size));
+       comp_chunk_size_offset = ndrpush->offset;
+       NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, 0xFEFEFEFE));
+
+       NDR_CHECK(ndr_push_expand(ndrpush, max_comp_size));
+
+       comp_chunk.data = ndrpush->data + ndrpush->offset;
+       comp_chunk.length = max_comp_size;
+
+       /* Compressing the buffer using LZ Xpress algorithm */
+       ret = lzxpress_compress(plain_chunk.data,
+                               plain_chunk.length,
+                               comp_chunk.data,
+                               comp_chunk.length);
+       if (ret < 0) {
+               return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
+                                     "XPRESS lzxpress_compress() returned %d\n",
+                                     ret);
+       }
+       comp_chunk.length = ret;
+
+       tmp_offset = ndrpush->offset;
+       ndrpush->offset = comp_chunk_size_offset;
+       NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, comp_chunk.length));
+       ndrpush->offset = tmp_offset;
+
+       ndrpush->offset += comp_chunk.length;
+       return NDR_ERR_SUCCESS;
 }
 
 /*
@@ -273,17 +447,27 @@ enum ndr_err_code ndr_pull_compression_end(struct ndr_pull *subndr,
   push a compressed subcontext
 */
 enum ndr_err_code ndr_push_compression_start(struct ndr_push *subndr,
-                                   struct ndr_push **_comndr,
+                                   struct ndr_push **_uncomndr,
                                    enum ndr_compression_alg compression_alg,
                                    ssize_t decompressed_len)
 {
-       struct ndr_push *comndr;
+       struct ndr_push *uncomndr;
 
-       comndr = ndr_push_init_ctx(subndr, subndr->iconv_convenience);
-       NDR_ERR_HAVE_NO_MEMORY(comndr);
-       comndr->flags   = subndr->flags;
+       switch (compression_alg) {
+       case NDR_COMPRESSION_MSZIP:
+       case NDR_COMPRESSION_XPRESS:
+               break;
+       default:
+               return ndr_push_error(subndr, NDR_ERR_COMPRESSION,
+                                     "Bad compression algorithm %d (PUSH)",
+                                     compression_alg);
+       }
 
-       *_comndr = comndr;
+       uncomndr = ndr_push_init_ctx(subndr, subndr->iconv_convenience);
+       NDR_ERR_HAVE_NO_MEMORY(uncomndr);
+       uncomndr->flags = subndr->flags;
+
+       *_uncomndr = uncomndr;
        return NDR_ERR_SUCCESS;
 }
 
@@ -291,18 +475,42 @@ enum ndr_err_code ndr_push_compression_start(struct ndr_push *subndr,
   push a compressed subcontext
 */
 enum ndr_err_code ndr_push_compression_end(struct ndr_push *subndr,
-                                 struct ndr_push *comndr,
+                                 struct ndr_push *uncomndr,
                                  enum ndr_compression_alg compression_alg,
                                  ssize_t decompressed_len)
 {
+       struct ndr_pull *ndrpull;
+       bool last = false;
+       z_stream z;
+
+       ndrpull = talloc_zero(uncomndr, struct ndr_pull);
+       NDR_ERR_HAVE_NO_MEMORY(ndrpull);
+       ndrpull->flags          = uncomndr->flags;
+       ndrpull->data           = uncomndr->data;
+       ndrpull->data_size      = uncomndr->offset;
+       ndrpull->offset         = 0;
+
+       ndrpull->iconv_convenience = talloc_reference(ndrpull, subndr->iconv_convenience);
+
        switch (compression_alg) {
        case NDR_COMPRESSION_MSZIP:
-               return ndr_push_compression_mszip(subndr, comndr);
+               ZERO_STRUCT(z);
+               while (!last) {
+                       NDR_CHECK(ndr_push_compression_mszip_chunk(subndr, ndrpull, &z, &last));
+               }
+               break;
+
        case NDR_COMPRESSION_XPRESS:
-               return ndr_push_compression_xpress(subndr, comndr);
+               while (!last) {
+                       NDR_CHECK(ndr_push_compression_xpress_chunk(subndr, ndrpull, &last));
+               }
+               break;
+
        default:
                return ndr_push_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PUSH)", 
                                      compression_alg);
        }
+
+       talloc_free(uncomndr);
        return NDR_ERR_SUCCESS;
 }