Be sure to pass iconv handle down to compression subcontexts (fixes
[samba.git] / source4 / librpc / ndr / ndr_compression.c
index def194634f08d1e459222aacc7fe85301c793449..86a5a2560ef2ba400309dbd151d2710475989f1c 100644 (file)
@@ -7,7 +7,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 #include "lib/compression/mszip.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/ndr/ndr_compression.h"
 
-static NTSTATUS ndr_pull_compression_mszip_chunk(struct ndr_pull *ndrpull,
+static enum ndr_err_code ndr_pull_compression_mszip_chunk(struct ndr_pull *ndrpull,
                                                 struct ndr_push *ndrpush,
-                                                struct decomp_state *decomp_state)
+                                                struct decomp_state *decomp_state,
+                                                bool *last)
 {
        DATA_BLOB comp_chunk;
        uint32_t comp_chunk_offset;
@@ -37,13 +39,13 @@ static NTSTATUS ndr_pull_compression_mszip_chunk(struct ndr_pull *ndrpull,
 
        NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &plain_chunk_size));
        if (plain_chunk_size > 0x00008000) {
-               return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, "Bad ZLIB plain chunk size %08X > 0x00008000 (PULL)", 
+               return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, "Bad MSZIP plain chunk size %08X > 0x00008000 (PULL)", 
                                      plain_chunk_size);
        }
 
        NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &comp_chunk_size));
 
-       DEBUG(10,("plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
+       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));
 
        comp_chunk_offset = ndrpull->offset;
@@ -58,23 +60,22 @@ static NTSTATUS ndr_pull_compression_mszip_chunk(struct ndr_pull *ndrpull,
 
        ret = ZIPdecompress(decomp_state, &comp_chunk, &plain_chunk);
        if (ret != DECR_OK) {
-               return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, "Bad ZIBdecompress() error %d (PULL)",
+               return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, "Bad ZIPdecompress() error %d (PULL)",
                                      ret);
        }
 
        if ((plain_chunk_size < 0x00008000) || (ndrpull->offset+4 >= ndrpull->data_size)) {
                /* this is the last chunk */
-               return NT_STATUS_OK;
+               *last = true;
        }
 
-       return NT_STATUS_MORE_PROCESSING_REQUIRED;
+       return NDR_ERR_SUCCESS;
 }
 
-static NTSTATUS ndr_pull_compression_mszip(struct ndr_pull *subndr,
+static enum ndr_err_code ndr_pull_compression_mszip(struct ndr_pull *subndr,
                                           struct ndr_pull **_comndr,
                                           ssize_t decompressed_len)
 {
-       NTSTATUS status = NT_STATUS_MORE_PROCESSING_REQUIRED;
        struct ndr_push *ndrpush;
        struct ndr_pull *comndr;
        DATA_BLOB uncompressed;
@@ -83,27 +84,27 @@ static NTSTATUS ndr_pull_compression_mszip(struct ndr_pull *subndr,
        uint32_t payload_offset;
        uint8_t *payload;
        struct decomp_state *decomp_state;
+       bool last = false;
 
-       ndrpush = ndr_push_init_ctx(subndr);
-       NT_STATUS_HAVE_NO_MEMORY(ndrpush);
+       ndrpush = ndr_push_init_ctx(subndr, subndr->iconv_convenience);
+       NDR_ERR_HAVE_NO_MEMORY(ndrpush);
 
        decomp_state = ZIPdecomp_state(subndr);
-       NT_STATUS_HAVE_NO_MEMORY(decomp_state);
+       NDR_ERR_HAVE_NO_MEMORY(decomp_state);
 
-       while (NT_STATUS_EQUAL(NT_STATUS_MORE_PROCESSING_REQUIRED, status)) {
-               status = ndr_pull_compression_mszip_chunk(subndr, ndrpush, decomp_state);
+       while (!last) {
+               NDR_CHECK(ndr_pull_compression_mszip_chunk(subndr, ndrpush, decomp_state, &last));
        }
-       NT_STATUS_NOT_OK_RETURN(status);
 
        uncompressed = ndr_push_blob(ndrpush);
 
        if (uncompressed.length != decompressed_len) {
-               return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad uncompressed_len [%u] != [%d] (PULL)",
+               return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad MSZIP uncompressed_len [%u] != [%d] (PULL)",
                                      (int)uncompressed.length, (int)decompressed_len);
        }
 
        comndr = talloc_zero(subndr, struct ndr_pull);
-       NT_STATUS_HAVE_NO_MEMORY(comndr);
+       NDR_ERR_HAVE_NO_MEMORY(comndr);
        comndr->flags           = subndr->flags;
        comndr->current_mem_ctx = subndr->current_mem_ctx;
 
@@ -111,19 +112,29 @@ static NTSTATUS ndr_pull_compression_mszip(struct ndr_pull *subndr,
        comndr->data_size       = uncompressed.length;
        comndr->offset          = 0;
 
+       comndr->iconv_convenience = talloc_reference(comndr, subndr->iconv_convenience);
+
        NDR_CHECK(ndr_pull_uint32(comndr, NDR_SCALARS, &payload_header[0]));
        NDR_CHECK(ndr_pull_uint32(comndr, NDR_SCALARS, &payload_header[1]));
        NDR_CHECK(ndr_pull_uint32(comndr, NDR_SCALARS, &payload_header[2]));
        NDR_CHECK(ndr_pull_uint32(comndr, NDR_SCALARS, &payload_header[3]));
 
-       payload_size = payload_header[2];
-
-       /* TODO: check the first 4 bytes of the header */
+       if (payload_header[0] != 0x00081001) {
+               return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad MSZIP payload_header[0] [0x%08X] != [0x00081001] (PULL)",
+                                     payload_header[0]);
+       }
        if (payload_header[1] != 0xCCCCCCCC) {
-               return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad payload_header[1] [0x%08X] != [0xCCCCCCCC] (PULL)",
+               return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad MSZIP payload_header[1] [0x%08X] != [0xCCCCCCCC] (PULL)",
                                      payload_header[1]);
        }
 
+       payload_size = payload_header[2];
+
+       if (payload_header[3] != 0x00000000) {
+               return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad MSZIP payload_header[3] [0x%08X] != [0x00000000] (PULL)",
+                                     payload_header[3]);
+       }
+
        payload_offset = comndr->offset;
        NDR_CHECK(ndr_pull_advance(comndr, payload_size));
        payload = comndr->data + payload_offset;
@@ -133,20 +144,96 @@ static NTSTATUS ndr_pull_compression_mszip(struct ndr_pull *subndr,
        comndr->offset          = 0;
 
        *_comndr = comndr;
-       return NT_STATUS_OK;
+       return NDR_ERR_SUCCESS;
+}
+
+static enum ndr_err_code ndr_push_compression_mszip(struct ndr_push *subndr,
+                                          struct ndr_push *comndr)
+{
+       return ndr_push_error(subndr, NDR_ERR_COMPRESSION, "Sorry MSZIP compression is not supported yet (PUSH)");
+}
+
+static enum ndr_err_code ndr_pull_compression_xpress_chunk(struct ndr_pull *ndrpull,
+                                                 struct ndr_push *ndrpush,
+                                                 bool *last)
+{
+       DATA_BLOB comp_chunk;
+       uint32_t comp_chunk_offset;
+       uint32_t comp_chunk_size;
+       uint32_t plain_chunk_size;
+
+       comp_chunk_offset = ndrpull->offset;
+
+       NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &plain_chunk_size));
+       if (plain_chunk_size > 0x00010000) {
+               return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, "Bad XPRESS plain chunk size %08X > 0x00010000 (PULL)", 
+                                     plain_chunk_size);
+       }
+
+       NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &comp_chunk_size));
+
+       NDR_CHECK(ndr_pull_advance(ndrpull, comp_chunk_size));
+       comp_chunk.length = comp_chunk_size + 8;
+       comp_chunk.data = ndrpull->data + comp_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));
+
+       /* For now, we just copy over the compressed blob */
+       NDR_CHECK(ndr_push_bytes(ndrpush, comp_chunk.data, comp_chunk.length));
+
+       if ((plain_chunk_size < 0x00010000) || (ndrpull->offset+4 >= ndrpull->data_size)) {
+               /* this is the last chunk */
+               *last = true;
+       }
+
+       return NDR_ERR_SUCCESS;
+}
+
+static enum ndr_err_code ndr_pull_compression_xpress(struct ndr_pull *subndr,
+                                           struct ndr_pull **_comndr,
+                                           ssize_t decompressed_len)
+{
+       struct ndr_push *ndrpush;
+       struct ndr_pull *comndr;
+       DATA_BLOB uncompressed;
+       bool last = false;
+
+       ndrpush = ndr_push_init_ctx(subndr, subndr->iconv_convenience);
+       NDR_ERR_HAVE_NO_MEMORY(ndrpush);
+
+       while (!last) {
+               NDR_CHECK(ndr_pull_compression_xpress_chunk(subndr, ndrpush, &last));
+       }
+
+       uncompressed = ndr_push_blob(ndrpush);
+
+       comndr = talloc_zero(subndr, struct ndr_pull);
+       NDR_ERR_HAVE_NO_MEMORY(comndr);
+       comndr->flags           = subndr->flags;
+       comndr->current_mem_ctx = subndr->current_mem_ctx;
+
+       comndr->data            = uncompressed.data;
+       comndr->data_size       = uncompressed.length;
+       comndr->offset          = 0;
+
+       comndr->iconv_convenience = talloc_reference(comndr, subndr->iconv_convenience);
+
+       *_comndr = comndr;
+       return NDR_ERR_SUCCESS;
 }
 
-static NTSTATUS ndr_push_compression_mszip(struct ndr_push *subndr,
-                                         struct ndr_push *comndr)
+static enum ndr_err_code ndr_push_compression_xpress(struct ndr_push *subndr,
+                                           struct ndr_push *comndr)
 {
-       return ndr_push_error(subndr, NDR_ERR_COMPRESSION, "Bad MSZIP compression is not supported yet (PUSH)");
+       return ndr_push_error(subndr, NDR_ERR_COMPRESSION, "XPRESS compression is not supported yet (PUSH)");
 }
 
 /*
   handle compressed subcontext buffers, which in midl land are user-marshalled, but
   we use magic in pidl to make them easier to cope with
 */
-NTSTATUS ndr_pull_compression_start(struct ndr_pull *subndr,
+enum ndr_err_code ndr_pull_compression_start(struct ndr_pull *subndr,
                                    struct ndr_pull **_comndr,
                                    enum ndr_compression_alg compression_alg,
                                    ssize_t decompressed_len)
@@ -154,43 +241,45 @@ NTSTATUS ndr_pull_compression_start(struct ndr_pull *subndr,
        switch (compression_alg) {
        case NDR_COMPRESSION_MSZIP:
                return ndr_pull_compression_mszip(subndr, _comndr, decompressed_len);
+       case NDR_COMPRESSION_XPRESS:
+               return ndr_pull_compression_xpress(subndr, _comndr, decompressed_len);
        default:
                return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PULL)", 
                                      compression_alg);
        }
-       return NT_STATUS_OK;
+       return NDR_ERR_SUCCESS;
 }
 
-NTSTATUS ndr_pull_compression_end(struct ndr_pull *subndr,
+enum ndr_err_code ndr_pull_compression_end(struct ndr_pull *subndr,
                                  struct ndr_pull *comndr,
                                  enum ndr_compression_alg compression_alg,
                                  ssize_t decompressed_len)
 {
-       return NT_STATUS_OK;
+       return NDR_ERR_SUCCESS;
 }
 
 /*
   push a compressed subcontext
 */
-NTSTATUS ndr_push_compression_start(struct ndr_push *subndr,
+enum ndr_err_code ndr_push_compression_start(struct ndr_push *subndr,
                                    struct ndr_push **_comndr,
                                    enum ndr_compression_alg compression_alg,
                                    ssize_t decompressed_len)
 {
        struct ndr_push *comndr;
 
-       comndr = ndr_push_init_ctx(subndr);
-       NT_STATUS_HAVE_NO_MEMORY(comndr);
+       comndr = ndr_push_init_ctx(subndr, subndr->iconv_convenience);
+       NDR_ERR_HAVE_NO_MEMORY(comndr);
        comndr->flags   = subndr->flags;
 
        *_comndr = comndr;
-       return NT_STATUS_OK;
+       return NDR_ERR_SUCCESS;
 }
 
 /*
   push a compressed subcontext
 */
-NTSTATUS ndr_push_compression_end(struct ndr_push *subndr,
+enum ndr_err_code ndr_push_compression_end(struct ndr_push *subndr,
                                  struct ndr_push *comndr,
                                  enum ndr_compression_alg compression_alg,
                                  ssize_t decompressed_len)
@@ -198,9 +287,11 @@ NTSTATUS ndr_push_compression_end(struct ndr_push *subndr,
        switch (compression_alg) {
        case NDR_COMPRESSION_MSZIP:
                return ndr_push_compression_mszip(subndr, comndr);
+       case NDR_COMPRESSION_XPRESS:
+               return ndr_push_compression_xpress(subndr, comndr);
        default:
                return ndr_push_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PUSH)", 
                                      compression_alg);
        }
-       return NT_STATUS_OK;
+       return NDR_ERR_SUCCESS;
 }