librpc: Add autogenerated total cabinet size for Cabinet files
[kai/samba-autobuild/.git] / librpc / ndr / ndr.c
index a151994b6af64c6d25b907ac8194a76880eb4cfc..22c4d763d09f719b9c8fedd440ad85e2b0f594c4 100644 (file)
@@ -1,20 +1,21 @@
-/* 
+/*
    Unix SMB/CIFS implementation.
 
    libndr interface
 
    Copyright (C) Andrew Tridgell 2003
-   
+   Copyright (C) Jelmer Vernooij 2005-2008
+
    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 3 of the License, or
    (at your option) any later version.
-   
+
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    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, see <http://www.gnu.org/licenses/>.
 */
 #include "includes.h"
 #include "librpc/ndr/libndr.h"
 #include "../lib/util/dlinklist.h"
-#if _SAMBA_BUILD_ == 4
-#include "param/param.h"
-#endif
 
 #define NDR_BASE_MARSHALL_SIZE 1024
 
 /* this guid indicates NDR encoding in a protocol tower */
-const struct ndr_syntax_id ndr_transfer_syntax = {
+const struct ndr_syntax_id ndr_transfer_syntax_ndr = {
   { 0x8a885d04, 0x1ceb, 0x11c9, {0x9f, 0xe8}, {0x08,0x00,0x2b,0x10,0x48,0x60} },
   2
 };
 
-const struct ndr_syntax_id ndr64_transfer_syntax = {
+const struct ndr_syntax_id ndr_transfer_syntax_ndr64 = {
   { 0x71710533, 0xbeba, 0x4937, {0x83, 0x19}, {0xb5,0xdb,0xef,0x9c,0xcc,0x36} },
   1
 };
 
+const struct ndr_syntax_id ndr_syntax_id_null = {
+  { 0, 0, 0, { 0, 0 }, { 0, 0, 0, 0, 0, 0 } },
+  0
+};
+
 /*
   work out the number of bytes needed to align on a n byte boundary
 */
@@ -58,7 +61,7 @@ _PUBLIC_ size_t ndr_align_size(uint32_t offset, size_t n)
 /*
   initialise a ndr parse structure from a data blob
 */
-_PUBLIC_ struct ndr_pull *ndr_pull_init_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience)
+_PUBLIC_ struct ndr_pull *ndr_pull_init_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
 {
        struct ndr_pull *ndr;
 
@@ -68,11 +71,115 @@ _PUBLIC_ struct ndr_pull *ndr_pull_init_blob(const DATA_BLOB *blob, TALLOC_CTX *
 
        ndr->data = blob->data;
        ndr->data_size = blob->length;
-       ndr->iconv_convenience = talloc_reference(ndr, iconv_convenience);
 
        return ndr;
 }
 
+_PUBLIC_ enum ndr_err_code ndr_pull_append(struct ndr_pull *ndr, DATA_BLOB *blob)
+{
+       enum ndr_err_code ndr_err;
+       DATA_BLOB b;
+       uint32_t append = 0;
+       bool ok;
+
+       if (blob->length == 0) {
+               return NDR_ERR_SUCCESS;
+       }
+
+       ndr_err = ndr_token_retrieve(&ndr->array_size_list, ndr, &append);
+       if (ndr_err == NDR_ERR_TOKEN) {
+               append = 0;
+               ndr_err = NDR_ERR_SUCCESS;
+       }
+       NDR_CHECK(ndr_err);
+
+       if (ndr->data_size == 0) {
+               ndr->data = NULL;
+               append = UINT32_MAX;
+       }
+
+       if (append == UINT32_MAX) {
+               /*
+                * append == UINT32_MAX means that
+                * ndr->data is either NULL or a valid
+                * talloc child of ndr, which means
+                * we can use data_blob_append() without
+                * data_blob_talloc() of the existing callers data
+                */
+               b = data_blob_const(ndr->data, ndr->data_size);
+       } else {
+               b = data_blob_talloc(ndr, ndr->data, ndr->data_size);
+               if (b.data == NULL) {
+                       return ndr_pull_error(ndr, NDR_ERR_ALLOC, "%s", __location__);
+               }
+       }
+
+       ok = data_blob_append(ndr, &b, blob->data, blob->length);
+       if (!ok) {
+               return ndr_pull_error(ndr, NDR_ERR_ALLOC, "%s", __location__);
+       }
+
+       ndr->data = b.data;
+       ndr->data_size = b.length;
+
+       return ndr_token_store(ndr, &ndr->array_size_list, ndr, UINT32_MAX);
+}
+
+_PUBLIC_ enum ndr_err_code ndr_pull_pop(struct ndr_pull *ndr)
+{
+       uint32_t skip = 0;
+       uint32_t append = 0;
+
+       if (ndr->relative_base_offset != 0) {
+               return ndr_pull_error(ndr, NDR_ERR_RELATIVE,
+                                     "%s", __location__);
+       }
+       if (ndr->relative_highest_offset != 0) {
+               return ndr_pull_error(ndr, NDR_ERR_RELATIVE,
+                                     "%s", __location__);
+       }
+       if (ndr->relative_list != NULL) {
+               return ndr_pull_error(ndr, NDR_ERR_RELATIVE,
+                                     "%s", __location__);
+       }
+       if (ndr->relative_base_list != NULL) {
+               return ndr_pull_error(ndr, NDR_ERR_RELATIVE,
+                                     "%s", __location__);
+       }
+
+       /*
+        * we need to keep up to 7 bytes
+        * in order to get the aligment right.
+        */
+       skip = ndr->offset & 0xFFFFFFF8;
+
+       if (skip == 0) {
+               return NDR_ERR_SUCCESS;
+       }
+
+       ndr->offset -= skip;
+       ndr->data_size -= skip;
+
+       append = ndr_token_peek(&ndr->array_size_list, ndr);
+       if (append != UINT32_MAX) {
+               /*
+                * here we assume, that ndr->data is not a
+                * talloc child of ndr.
+                */
+               ndr->data += skip;
+               return NDR_ERR_SUCCESS;
+       }
+
+       memmove(ndr->data, ndr->data + skip, ndr->data_size);
+
+       ndr->data = talloc_realloc(ndr, ndr->data, uint8_t, ndr->data_size);
+       if (ndr->data_size != 0 && ndr->data == NULL) {
+               return ndr_pull_error(ndr, NDR_ERR_ALLOC, "%s", __location__);
+       }
+
+       return NDR_ERR_SUCCESS;
+}
+
 /*
   advance by 'size' bytes
 */
@@ -102,7 +209,7 @@ static enum ndr_err_code ndr_pull_set_offset(struct ndr_pull *ndr, uint32_t ofs)
 }
 
 /* create a ndr_push structure, ready for some marshalling */
-_PUBLIC_ struct ndr_push *ndr_push_init_ctx(TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience)
+_PUBLIC_ struct ndr_push *ndr_push_init_ctx(TALLOC_CTX *mem_ctx)
 {
        struct ndr_push *ndr;
 
@@ -115,9 +222,9 @@ _PUBLIC_ struct ndr_push *ndr_push_init_ctx(TALLOC_CTX *mem_ctx, struct smb_icon
        ndr->alloc_size = NDR_BASE_MARSHALL_SIZE;
        ndr->data = talloc_array(ndr, uint8_t, ndr->alloc_size);
        if (!ndr->data) {
+               talloc_free(ndr);
                return NULL;
        }
-       ndr->iconv_convenience = talloc_reference(ndr, iconv_convenience);
 
        return ndr;
 }
@@ -147,6 +254,17 @@ _PUBLIC_ enum ndr_err_code ndr_push_expand(struct ndr_push *ndr, uint32_t extra_
                                      size);
        }
 
+       if (ndr->fixed_buf_size) {
+               if (ndr->alloc_size >= size) {
+                       return NDR_ERR_SUCCESS;
+               }
+               return ndr_push_error(ndr,
+                                     NDR_ERR_BUFSIZE,
+                                     "Overflow of fixed buffer in "
+                                     "push_expand to %u",
+                                     size);
+       }
+       
        if (ndr->alloc_size > size) {
                return NDR_ERR_SUCCESS;
        }
@@ -164,11 +282,44 @@ _PUBLIC_ enum ndr_err_code ndr_push_expand(struct ndr_push *ndr, uint32_t extra_
        return NDR_ERR_SUCCESS;
 }
 
+_PUBLIC_ void ndr_print_debugc_helper(struct ndr_print *ndr, const char *format, ...)
+{
+       va_list ap;
+       char *s = NULL;
+       uint32_t i;
+       int ret;
+       int dbgc_class;
+
+       va_start(ap, format);
+       ret = vasprintf(&s, format, ap);
+       va_end(ap);
+
+       if (ret == -1) {
+               return;
+       }
+
+       dbgc_class = *(int *)ndr->private_data;
+
+       if (ndr->no_newline) {
+               DEBUGADDC(dbgc_class, 1,("%s", s));
+               free(s);
+               return;
+       }
+
+       for (i=0;i<ndr->depth;i++) {
+               DEBUGADDC(dbgc_class, 1,("    "));
+       }
+
+       DEBUGADDC(dbgc_class, 1,("%s\n", s));
+       free(s);
+}
+
 _PUBLIC_ void ndr_print_debug_helper(struct ndr_print *ndr, const char *format, ...) 
 {
        va_list ap;
        char *s = NULL;
-       int i, ret;
+       uint32_t i;
+       int ret;
 
        va_start(ap, format);
        ret = vasprintf(&s, format, ap);
@@ -178,6 +329,12 @@ _PUBLIC_ void ndr_print_debug_helper(struct ndr_print *ndr, const char *format,
                return;
        }
 
+       if (ndr->no_newline) {
+               DEBUGADD(1,("%s", s));
+               free(s);
+               return;
+       }
+
        for (i=0;i<ndr->depth;i++) {
                DEBUGADD(1,("    "));
        }
@@ -186,22 +343,64 @@ _PUBLIC_ void ndr_print_debug_helper(struct ndr_print *ndr, const char *format,
        free(s);
 }
 
+_PUBLIC_ void ndr_print_printf_helper(struct ndr_print *ndr, const char *format, ...) 
+{
+       va_list ap;
+       uint32_t i;
+
+       if (!ndr->no_newline) {
+               for (i=0;i<ndr->depth;i++) {
+                       printf("    ");
+               }
+       }
+
+       va_start(ap, format);
+       vprintf(format, ap);
+       va_end(ap);
+       if (!ndr->no_newline) {
+               printf("\n");
+       }
+}
+
 _PUBLIC_ void ndr_print_string_helper(struct ndr_print *ndr, const char *format, ...)
 {
        va_list ap;
-       int i;
+       uint32_t i;
 
-       for (i=0;i<ndr->depth;i++) {
-               ndr->private_data = talloc_asprintf_append_buffer(
-                                       (char *)ndr->private_data, "    ");
+       if (!ndr->no_newline) {
+               for (i=0;i<ndr->depth;i++) {
+                       ndr->private_data = talloc_asprintf_append_buffer(
+                               (char *)ndr->private_data, "    ");
+               }
        }
 
        va_start(ap, format);
        ndr->private_data = talloc_vasprintf_append_buffer((char *)ndr->private_data, 
                                                    format, ap);
        va_end(ap);
-       ndr->private_data = talloc_asprintf_append_buffer((char *)ndr->private_data, 
-                                                  "\n");
+       if (!ndr->no_newline) {
+               ndr->private_data = talloc_asprintf_append_buffer((char *)ndr->private_data,
+                                                                 "\n");
+       }
+}
+
+/*
+  a useful helper function for printing idl structures via DEBUGC()
+*/
+_PUBLIC_ void ndr_print_debugc(int dbgc_class, ndr_print_fn_t fn, const char *name, void *ptr)
+{
+       struct ndr_print *ndr;
+
+       DEBUGC(dbgc_class, 1,(" "));
+
+       ndr = talloc_zero(NULL, struct ndr_print);
+       if (!ndr) return;
+       ndr->private_data = &dbgc_class;
+       ndr->print = ndr_print_debugc_helper;
+       ndr->depth = 1;
+       ndr->flags = 0;
+       fn(ndr, name, ptr);
+       talloc_free(ndr);
 }
 
 /*
@@ -256,13 +455,6 @@ _PUBLIC_ void ndr_print_function_debug(ndr_print_function_t fn, const char *name
        ndr->depth = 1;
        ndr->flags = 0;
 
-       /* this is a s4 hack until we build up the courage to pass
-        * this all the way down 
-        */
-#if _SAMBA_BUILD_ == 4
-       ndr->iconv_convenience = smb_iconv_convenience_init(talloc_autofree_context(), "ASCII", "UTF-8", true);
-#endif
-
        fn(ndr, name, flags, ptr);
        talloc_free(ndr);
 }
@@ -285,13 +477,6 @@ _PUBLIC_ char *ndr_print_struct_string(TALLOC_CTX *mem_ctx, ndr_print_fn_t fn, c
        ndr->depth = 1;
        ndr->flags = 0;
 
-       /* this is a s4 hack until we build up the courage to pass
-        * this all the way down 
-        */
-#if _SAMBA_BUILD_ == 4
-       ndr->iconv_convenience = smb_iconv_convenience_init(talloc_autofree_context(), "ASCII", "UTF-8", true);
-#endif
-
        fn(ndr, name, ptr);
        ret = talloc_steal(mem_ctx, (char *)ndr->private_data);
 failed:
@@ -361,11 +546,14 @@ _PUBLIC_ void ndr_set_flags(uint32_t *pflags, uint32_t new_flags)
                (*pflags) &= ~LIBNDR_FLAG_LITTLE_ENDIAN;
                (*pflags) &= ~LIBNDR_FLAG_NDR64;
        }
-       if (new_flags & LIBNDR_FLAG_REMAINING) {
+       if (new_flags & LIBNDR_ALIGN_FLAGS) {
+               /* Ensure we only have the passed-in
+                  align flag set in the new_flags,
+                  remove any old align flag. */
                (*pflags) &= ~LIBNDR_ALIGN_FLAGS;
        }
-       if (new_flags & LIBNDR_ALIGN_FLAGS) {
-               (*pflags) &= ~LIBNDR_FLAG_REMAINING;
+       if (new_flags & LIBNDR_FLAG_NO_RELATIVE_REVERSE) {
+               (*pflags) &= ~LIBNDR_FLAG_RELATIVE_REVERSE;
        }
        (*pflags) |= new_flags;
 }
@@ -381,6 +569,15 @@ _PUBLIC_ enum ndr_err_code ndr_pull_error(struct ndr_pull *ndr,
        va_list ap;
        int ret;
 
+       if (ndr->flags & LIBNDR_FLAG_INCOMPLETE_BUFFER) {
+               switch (ndr_err) {
+               case NDR_ERR_BUFSIZE:
+                       return NDR_ERR_INCOMPLETE_BUFFER;
+               default:
+                       break;
+               }
+       }
+
        va_start(ap, format);
        ret = vasprintf(&s, format, ap);
        va_end(ap);
@@ -450,8 +647,10 @@ _PUBLIC_ enum ndr_err_code ndr_pull_subcontext_start(struct ndr_pull *ndr,
                uint16_t content_size;
                NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &content_size));
                if (size_is >= 0 && size_is != content_size) {
-                       return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) size_is(%d) mismatch content_size %d", 
-                                               (int)size_is, (int)content_size);
+                       return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) size_is(%d) (0x%04x) mismatch content_size %d (0x%04x)",
+                                               (int)size_is, (int)size_is,
+                                               (int)content_size,
+                                               (int)content_size);
                }
                r_content_size = content_size;
                break;
@@ -461,8 +660,10 @@ _PUBLIC_ enum ndr_err_code ndr_pull_subcontext_start(struct ndr_pull *ndr,
                uint32_t content_size;
                NDR_CHECK(ndr_pull_uint3264(ndr, NDR_SCALARS, &content_size));
                if (size_is >= 0 && size_is != content_size) {
-                       return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) size_is(%d) mismatch content_size %d", 
-                                               (int)size_is, (int)content_size);
+                       return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) size_is(%d) (0x%08x) mismatch content_size %d (0x%08x)",
+                                               (int)size_is, (int)size_is,
+                                               (int)content_size,
+                                               (int)content_size);
                }
                r_content_size = content_size;
                break;
@@ -535,6 +736,23 @@ _PUBLIC_ enum ndr_err_code ndr_pull_subcontext_start(struct ndr_pull *ndr,
                NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &reserved));
                break;
        }
+       case 0xFFFFFFFF:
+               /*
+                * a shallow copy like subcontext
+                * useful for DCERPC pipe chunks.
+                */
+               subndr = talloc_zero(ndr, struct ndr_pull);
+               NDR_ERR_HAVE_NO_MEMORY(subndr);
+
+               subndr->flags           = ndr->flags;
+               subndr->current_mem_ctx = ndr->current_mem_ctx;
+               subndr->data            = ndr->data;
+               subndr->offset          = ndr->offset;
+               subndr->data_size       = ndr->data_size;
+
+               *_subndr = subndr;
+               return NDR_ERR_SUCCESS;
+
        default:
                return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) header_size %d", 
                                      (int)header_size);
@@ -550,7 +768,6 @@ _PUBLIC_ enum ndr_err_code ndr_pull_subcontext_start(struct ndr_pull *ndr,
        subndr->data = ndr->data + ndr->offset;
        subndr->offset = 0;
        subndr->data_size = r_content_size;
-       subndr->iconv_convenience = talloc_reference(subndr, ndr->iconv_convenience);
 
        if (force_le) {
                ndr_set_flags(&ndr->flags, LIBNDR_FLAG_LITTLE_ENDIAN);
@@ -568,13 +785,35 @@ _PUBLIC_ enum ndr_err_code ndr_pull_subcontext_end(struct ndr_pull *ndr,
                                 ssize_t size_is)
 {
        uint32_t advance;
-       if (size_is >= 0) {
+       uint32_t highest_ofs;
+
+       if (header_size == 0xFFFFFFFF) {
+               advance = subndr->offset - ndr->offset;
+       } else if (size_is >= 0) {
                advance = size_is;
        } else if (header_size > 0) {
                advance = subndr->data_size;
        } else {
                advance = subndr->offset;
        }
+
+       if (subndr->offset > ndr->relative_highest_offset) {
+               highest_ofs = subndr->offset;
+       } else {
+               highest_ofs = subndr->relative_highest_offset;
+       }
+       if (!(subndr->flags & LIBNDR_FLAG_SUBCONTEXT_NO_UNREAD_BYTES)) {
+               /*
+                * avoid an error unless SUBCONTEXT_NO_UNREAD_BYTES is specified
+                */
+               highest_ofs = advance;
+       }
+       if (highest_ofs < advance) {
+               return ndr_pull_error(subndr, NDR_ERR_UNREAD_BYTES,
+                                     "not all bytes consumed ofs[%u] advance[%u]",
+                                     highest_ofs, advance);
+       }
+
        NDR_CHECK(ndr_pull_advance(ndr, advance));
        return NDR_ERR_SUCCESS;
 }
@@ -586,10 +825,16 @@ _PUBLIC_ enum ndr_err_code ndr_push_subcontext_start(struct ndr_push *ndr,
 {
        struct ndr_push *subndr;
 
-       subndr = ndr_push_init_ctx(ndr, ndr->iconv_convenience);
+       subndr = ndr_push_init_ctx(ndr);
        NDR_ERR_HAVE_NO_MEMORY(subndr);
        subndr->flags   = ndr->flags & ~LIBNDR_FLAG_NDR64;
 
+       if (size_is > 0) {
+               NDR_CHECK(ndr_push_zero(subndr, size_is));
+               subndr->offset = 0;
+               subndr->relative_end_offset = size_is;
+       }
+
        *_subndr = subndr;
        return NDR_ERR_SUCCESS;
 }
@@ -606,12 +851,11 @@ _PUBLIC_ enum ndr_err_code ndr_push_subcontext_end(struct ndr_push *ndr,
 
        if (size_is >= 0) {
                padding_len = size_is - subndr->offset;
-               if (padding_len > 0) {
-                       NDR_CHECK(ndr_push_zero(subndr, padding_len));
-               } else if (padding_len < 0) {
+               if (padding_len < 0) {
                        return ndr_push_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PUSH) content_size %d is larger than size_is(%d)",
                                              (int)subndr->offset, (int)size_is);
                }
+               subndr->offset = size_is;
        }
 
        switch (header_size) {
@@ -721,15 +965,13 @@ _PUBLIC_ enum ndr_err_code ndr_token_retrieve(struct ndr_token_list **list, cons
 */
 _PUBLIC_ uint32_t ndr_token_peek(struct ndr_token_list **list, const void *key)
 {
-       enum ndr_err_code status;
-       uint32_t v;
-
-       status = ndr_token_retrieve_cmp_fn(list, key, &v, NULL, false);
-       if (!NDR_ERR_CODE_IS_SUCCESS(status)) {
-               return 0;
+       struct ndr_token_list *tok;
+       for (tok = *list; tok; tok = tok->next) {
+               if (tok->key == key) {
+                       return tok->value;
+               }
        }
-
-       return v;
+       return 0;
 }
 
 /*
@@ -803,6 +1045,40 @@ _PUBLIC_ enum ndr_err_code ndr_check_array_length(struct ndr_pull *ndr, void *p,
        return NDR_ERR_SUCCESS;
 }
 
+_PUBLIC_ enum ndr_err_code ndr_push_pipe_chunk_trailer(struct ndr_push *ndr, int ndr_flags, uint32_t count)
+{
+       if (ndr->flags & LIBNDR_FLAG_NDR64) {
+               int64_t tmp = 0 - (int64_t)count;
+               uint64_t ncount = tmp;
+
+               NDR_CHECK(ndr_push_hyper(ndr, ndr_flags, ncount));
+       }
+
+       return NDR_ERR_SUCCESS;
+}
+
+_PUBLIC_ enum ndr_err_code ndr_check_pipe_chunk_trailer(struct ndr_pull *ndr, int ndr_flags, uint32_t count)
+{
+       if (ndr->flags & LIBNDR_FLAG_NDR64) {
+               int64_t tmp = 0 - (int64_t)count;
+               uint64_t ncount1 = tmp;
+               uint64_t ncount2;
+
+               NDR_CHECK(ndr_pull_hyper(ndr, ndr_flags, &ncount2));
+               if (ncount1 == ncount2) {
+                       return NDR_ERR_SUCCESS;
+               }
+
+               return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE,
+                       "Bad pipe trailer[%lld should be %lld] size was %lu\"",
+                       (unsigned long long)ncount2,
+                       (unsigned long long)ncount1,
+                       (unsigned long)count);
+       }
+
+       return NDR_ERR_SUCCESS;
+}
+
 /*
   store a switch value
  */
@@ -839,14 +1115,28 @@ _PUBLIC_ uint32_t ndr_print_get_switch_value(struct ndr_print *ndr, const void *
        return ndr_token_peek(&ndr->switch_list, p);
 }
 
+/* retrieve a switch value and remove it from the list */
+_PUBLIC_ uint32_t ndr_pull_steal_switch_value(struct ndr_pull *ndr, const void *p)
+{
+       enum ndr_err_code status;
+       uint32_t v;
+
+       status = ndr_token_retrieve(&ndr->switch_list, p, &v);
+       if (!NDR_ERR_CODE_IS_SUCCESS(status)) {
+               return 0;
+       }
+
+       return v;
+}
+
 /*
   pull a struct from a blob using NDR
 */
-_PUBLIC_ enum ndr_err_code ndr_pull_struct_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience, void *p,
+_PUBLIC_ enum ndr_err_code ndr_pull_struct_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
                              ndr_pull_flags_fn_t fn)
 {
        struct ndr_pull *ndr;
-       ndr = ndr_pull_init_blob(blob, mem_ctx, iconv_convenience);
+       ndr = ndr_pull_init_blob(blob, mem_ctx);
        NDR_ERR_HAVE_NO_MEMORY(ndr);
        NDR_CHECK_FREE(fn(ndr, NDR_SCALARS|NDR_BUFFERS, p));
        talloc_free(ndr);
@@ -857,12 +1147,11 @@ _PUBLIC_ enum ndr_err_code ndr_pull_struct_blob(const DATA_BLOB *blob, TALLOC_CT
   pull a struct from a blob using NDR - failing if all bytes are not consumed
 */
 _PUBLIC_ enum ndr_err_code ndr_pull_struct_blob_all(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
-                                                   struct smb_iconv_convenience *iconv_convenience,
                                                    void *p, ndr_pull_flags_fn_t fn)
 {
        struct ndr_pull *ndr;
        uint32_t highest_ofs;
-       ndr = ndr_pull_init_blob(blob, mem_ctx, iconv_convenience);
+       ndr = ndr_pull_init_blob(blob, mem_ctx);
        NDR_ERR_HAVE_NO_MEMORY(ndr);
        NDR_CHECK_FREE(fn(ndr, NDR_SCALARS|NDR_BUFFERS, p));
        if (ndr->offset > ndr->relative_highest_offset) {
@@ -882,15 +1171,54 @@ _PUBLIC_ enum ndr_err_code ndr_pull_struct_blob_all(const DATA_BLOB *blob, TALLO
        return NDR_ERR_SUCCESS;
 }
 
+/*
+  pull a struct from a blob using NDR - failing if all bytes are not consumed
+
+  This only works for structures with NO allocated memory, like
+  objectSID and GUID.  This helps because we parse these a lot.
+*/
+_PUBLIC_ enum ndr_err_code ndr_pull_struct_blob_all_noalloc(const DATA_BLOB *blob,
+                                                           void *p, ndr_pull_flags_fn_t fn)
+{
+       /*
+        * We init this structure on the stack here, to avoid a
+        * talloc() as otherwise this call to the fn() is assured not
+        * to be doing any allocation, eg SIDs and GUIDs.
+        *
+        * This allows us to keep the safety of the PIDL-generated
+        * code without the talloc() overhead.
+        */
+       struct ndr_pull ndr = {
+               .data = blob->data,
+               .data_size = blob->length,
+               .current_mem_ctx = (void *)-1
+       };
+       uint32_t highest_ofs;
+       NDR_CHECK(fn(&ndr, NDR_SCALARS|NDR_BUFFERS, p));
+       if (ndr.offset > ndr.relative_highest_offset) {
+               highest_ofs = ndr.offset;
+       } else {
+               highest_ofs = ndr.relative_highest_offset;
+       }
+       if (highest_ofs < ndr.data_size) {
+               enum ndr_err_code ret;
+               ret = ndr_pull_error(&ndr, NDR_ERR_UNREAD_BYTES,
+                                    "not all bytes consumed ofs[%u] size[%u]",
+                                    highest_ofs, ndr.data_size);
+               return ret;
+       }
+       return NDR_ERR_SUCCESS;
+}
+
 /*
   pull a union from a blob using NDR, given the union discriminator
 */
 _PUBLIC_ enum ndr_err_code ndr_pull_union_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
-                                              struct smb_iconv_convenience *iconv_convenience, void *p,
+                                              void *p,
                             uint32_t level, ndr_pull_flags_fn_t fn)
 {
        struct ndr_pull *ndr;
-       ndr = ndr_pull_init_blob(blob, mem_ctx, iconv_convenience);
+       ndr = ndr_pull_init_blob(blob, mem_ctx);
        NDR_ERR_HAVE_NO_MEMORY(ndr);
        NDR_CHECK_FREE(ndr_pull_set_switch_value(ndr, p, level));
        NDR_CHECK_FREE(fn(ndr, NDR_SCALARS|NDR_BUFFERS, p));
@@ -903,12 +1231,12 @@ _PUBLIC_ enum ndr_err_code ndr_pull_union_blob(const DATA_BLOB *blob, TALLOC_CTX
   failing if all bytes are not consumed
 */
 _PUBLIC_ enum ndr_err_code ndr_pull_union_blob_all(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
-                                                  struct smb_iconv_convenience *iconv_convenience, void *p,
+                                                  void *p,
                             uint32_t level, ndr_pull_flags_fn_t fn)
 {
        struct ndr_pull *ndr;
        uint32_t highest_ofs;
-       ndr = ndr_pull_init_blob(blob, mem_ctx, iconv_convenience);
+       ndr = ndr_pull_init_blob(blob, mem_ctx);
        NDR_ERR_HAVE_NO_MEMORY(ndr);
        NDR_CHECK_FREE(ndr_pull_set_switch_value(ndr, p, level));
        NDR_CHECK_FREE(fn(ndr, NDR_SCALARS|NDR_BUFFERS, p));
@@ -932,10 +1260,10 @@ _PUBLIC_ enum ndr_err_code ndr_pull_union_blob_all(const DATA_BLOB *blob, TALLOC
 /*
   push a struct to a blob using NDR
 */
-_PUBLIC_ enum ndr_err_code ndr_push_struct_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience, const void *p, ndr_push_flags_fn_t fn)
+_PUBLIC_ enum ndr_err_code ndr_push_struct_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, const void *p, ndr_push_flags_fn_t fn)
 {
        struct ndr_push *ndr;
-       ndr = ndr_push_init_ctx(mem_ctx, iconv_convenience);
+       ndr = ndr_push_init_ctx(mem_ctx);
        NDR_ERR_HAVE_NO_MEMORY(ndr);
 
        NDR_CHECK(fn(ndr, NDR_SCALARS|NDR_BUFFERS, p));
@@ -947,14 +1275,43 @@ _PUBLIC_ enum ndr_err_code ndr_push_struct_blob(DATA_BLOB *blob, TALLOC_CTX *mem
        return NDR_ERR_SUCCESS;
 }
 
+/* 
+  push a struct into a provided blob using NDR. 
+  We error because we want to have the performance issue (extra
+  talloc() calls) show up as an error, not just slower code.  This is
+  used for things like GUIDs, which we expect to be a fixed size, and
+  SIDs that we can pre-calculate the size for.
+*/
+_PUBLIC_ enum ndr_err_code ndr_push_struct_into_fixed_blob(
+       DATA_BLOB *blob, const void *p, ndr_push_flags_fn_t fn)
+{
+       struct ndr_push ndr = {
+               .data = blob->data,
+               .alloc_size = blob->length,
+               .fixed_buf_size = true
+       };
+
+       NDR_CHECK(fn(&ndr, NDR_SCALARS|NDR_BUFFERS, p));
+
+       if (ndr.offset != blob->length) {
+               return ndr_push_error(&ndr, NDR_ERR_BUFSIZE,
+                                     "buffer was either to large or small "
+                                     "ofs[%u] size[%zu]",
+                                     ndr.offset, blob->length);
+       }
+
+       return NDR_ERR_SUCCESS;
+}
+
 /*
   push a union to a blob using NDR
 */
-_PUBLIC_ enum ndr_err_code ndr_push_union_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience, void *p,
+_PUBLIC_ enum ndr_err_code ndr_push_union_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p,
                             uint32_t level, ndr_push_flags_fn_t fn)
 {
        struct ndr_push *ndr;
-       ndr = ndr_push_init_ctx(mem_ctx, iconv_convenience);
+       ndr = ndr_push_init_ctx(mem_ctx);
        NDR_ERR_HAVE_NO_MEMORY(ndr);
 
        NDR_CHECK(ndr_push_set_switch_value(ndr, p, level));
@@ -970,7 +1327,7 @@ _PUBLIC_ enum ndr_err_code ndr_push_union_blob(DATA_BLOB *blob, TALLOC_CTX *mem_
 /*
   generic ndr_size_*() handler for structures
 */
-_PUBLIC_ size_t ndr_size_struct(const void *p, int flags, ndr_push_flags_fn_t push, struct smb_iconv_convenience *iconv_convenience)
+_PUBLIC_ size_t ndr_size_struct(const void *p, int flags, ndr_push_flags_fn_t push)
 {
        struct ndr_push *ndr;
        enum ndr_err_code status;
@@ -979,7 +1336,7 @@ _PUBLIC_ size_t ndr_size_struct(const void *p, int flags, ndr_push_flags_fn_t pu
        /* avoid recursion */
        if (flags & LIBNDR_FLAG_NO_NDR_SIZE) return 0;
 
-       ndr = ndr_push_init_ctx(NULL, iconv_convenience);
+       ndr = ndr_push_init_ctx(NULL);
        if (!ndr) return 0;
        ndr->flags |= flags | LIBNDR_FLAG_NO_NDR_SIZE;
        status = push(ndr, NDR_SCALARS|NDR_BUFFERS, discard_const(p));
@@ -995,7 +1352,7 @@ _PUBLIC_ size_t ndr_size_struct(const void *p, int flags, ndr_push_flags_fn_t pu
 /*
   generic ndr_size_*() handler for unions
 */
-_PUBLIC_ size_t ndr_size_union(const void *p, int flags, uint32_t level, ndr_push_flags_fn_t push, struct smb_iconv_convenience *iconv_convenience)
+_PUBLIC_ size_t ndr_size_union(const void *p, int flags, uint32_t level, ndr_push_flags_fn_t push)
 {
        struct ndr_push *ndr;
        enum ndr_err_code status;
@@ -1004,7 +1361,7 @@ _PUBLIC_ size_t ndr_size_union(const void *p, int flags, uint32_t level, ndr_pus
        /* avoid recursion */
        if (flags & LIBNDR_FLAG_NO_NDR_SIZE) return 0;
 
-       ndr = ndr_push_init_ctx(NULL, iconv_convenience);
+       ndr = ndr_push_init_ctx(NULL);
        if (!ndr) return 0;
        ndr->flags |= flags | LIBNDR_FLAG_NO_NDR_SIZE;
 
@@ -1091,7 +1448,7 @@ _PUBLIC_ enum ndr_err_code ndr_push_short_relative_ptr1(struct ndr_push *ndr, co
   push a relative object - stage2
   this is called during buffers processing
 */
-_PUBLIC_ enum ndr_err_code ndr_push_relative_ptr2(struct ndr_push *ndr, const void *p)
+static enum ndr_err_code ndr_push_relative_ptr2(struct ndr_push *ndr, const void *p)
 {
        uint32_t save_offset;
        uint32_t ptr_offset = 0xFFFFFFFF;
@@ -1123,9 +1480,44 @@ _PUBLIC_ enum ndr_err_code ndr_push_short_relative_ptr2(struct ndr_push *ndr, co
 {
        uint32_t save_offset;
        uint32_t ptr_offset = 0xFFFF;
+       uint32_t relative_offset;
+       size_t pad;
+       size_t align = 1;
+
        if (p == NULL) {
                return NDR_ERR_SUCCESS;
        }
+
+       if (ndr->offset < ndr->relative_base_offset) {
+               return ndr_push_error(ndr, NDR_ERR_BUFSIZE,
+                                     "ndr_push_relative_ptr2 ndr->offset(%u) < ndr->relative_base_offset(%u)",
+                                     ndr->offset, ndr->relative_base_offset);
+       }
+
+       relative_offset = ndr->offset - ndr->relative_base_offset;
+
+       if (ndr->flags & LIBNDR_FLAG_NOALIGN) {
+               align = 1;
+       } else if (ndr->flags & LIBNDR_FLAG_ALIGN2) {
+               align = 2;
+       } else if (ndr->flags & LIBNDR_FLAG_ALIGN4) {
+               align = 4;
+       } else if (ndr->flags & LIBNDR_FLAG_ALIGN8) {
+               align = 8;
+       }
+
+       pad = ndr_align_size(relative_offset, align);
+       if (pad != 0) {
+               NDR_CHECK(ndr_push_zero(ndr, pad));
+       }
+
+       relative_offset = ndr->offset - ndr->relative_base_offset;
+       if (relative_offset > UINT16_MAX) {
+               return ndr_push_error(ndr, NDR_ERR_BUFSIZE,
+                                     "ndr_push_relative_ptr2 relative_offset(%u) > UINT16_MAX",
+                                     relative_offset);
+       }
+
        save_offset = ndr->offset;
        NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &ptr_offset));
        if (ptr_offset > ndr->offset) {
@@ -1134,16 +1526,164 @@ _PUBLIC_ enum ndr_err_code ndr_push_short_relative_ptr2(struct ndr_push *ndr, co
                                      ptr_offset, ndr->offset);
        }
        ndr->offset = ptr_offset;
-       if (save_offset < ndr->relative_base_offset) {
-               return ndr_push_error(ndr, NDR_ERR_BUFSIZE,
-                                     "ndr_push_relative_ptr2 save_offset(%u) < ndr->relative_base_offset(%u)",
-                                     save_offset, ndr->relative_base_offset);
-       }
-       NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, save_offset - ndr->relative_base_offset));
+       NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, relative_offset));
        ndr->offset = save_offset;
        return NDR_ERR_SUCCESS;
 }
 
+/*
+  push a relative object - stage2 start
+  this is called during buffers processing
+*/
+_PUBLIC_ enum ndr_err_code ndr_push_relative_ptr2_start(struct ndr_push *ndr, const void *p)
+{
+       if (p == NULL) {
+               return NDR_ERR_SUCCESS;
+       }
+       if (!(ndr->flags & LIBNDR_FLAG_RELATIVE_REVERSE)) {
+               uint32_t relative_offset;
+               size_t pad;
+               size_t align = 1;
+
+               if (ndr->offset < ndr->relative_base_offset) {
+                       return ndr_push_error(ndr, NDR_ERR_BUFSIZE,
+                                     "ndr_push_relative_ptr2_start ndr->offset(%u) < ndr->relative_base_offset(%u)",
+                                     ndr->offset, ndr->relative_base_offset);
+               }
+
+               relative_offset = ndr->offset - ndr->relative_base_offset;
+
+               if (ndr->flags & LIBNDR_FLAG_NOALIGN) {
+                       align = 1;
+               } else if (ndr->flags & LIBNDR_FLAG_ALIGN2) {
+                       align = 2;
+               } else if (ndr->flags & LIBNDR_FLAG_ALIGN4) {
+                       align = 4;
+               } else if (ndr->flags & LIBNDR_FLAG_ALIGN8) {
+                       align = 8;
+               }
+
+               pad = ndr_align_size(relative_offset, align);
+               if (pad) {
+                       NDR_CHECK(ndr_push_zero(ndr, pad));
+               }
+
+               return ndr_push_relative_ptr2(ndr, p);
+       }
+       if (ndr->relative_end_offset == -1) {
+               return ndr_push_error(ndr, NDR_ERR_RELATIVE,
+                             "ndr_push_relative_ptr2_start RELATIVE_REVERSE flag set and relative_end_offset %d",
+                             ndr->relative_end_offset);
+       }
+       NDR_CHECK(ndr_token_store(ndr, &ndr->relative_begin_list, p, ndr->offset));
+       return NDR_ERR_SUCCESS;
+}
+
+/*
+  push a relative object - stage2 end
+  this is called during buffers processing
+*/
+_PUBLIC_ enum ndr_err_code ndr_push_relative_ptr2_end(struct ndr_push *ndr, const void *p)
+{
+       uint32_t begin_offset = 0xFFFFFFFF;
+       ssize_t len;
+       uint32_t correct_offset = 0;
+       uint32_t align = 1;
+       uint32_t pad = 0;
+
+       if (p == NULL) {
+               return NDR_ERR_SUCCESS;
+       }
+
+       if (!(ndr->flags & LIBNDR_FLAG_RELATIVE_REVERSE)) {
+               return NDR_ERR_SUCCESS;
+       }
+
+       if (ndr->flags & LIBNDR_FLAG_NO_NDR_SIZE) {
+               /* better say more than calculation a too small buffer */
+               NDR_PUSH_ALIGN(ndr, 8);
+               return NDR_ERR_SUCCESS;
+       }
+
+       if (ndr->relative_end_offset < ndr->offset) {
+               return ndr_push_error(ndr, NDR_ERR_RELATIVE,
+                                     "ndr_push_relative_ptr2_end:"
+                                     "relative_end_offset %u < offset %u",
+                                     ndr->relative_end_offset, ndr->offset);
+       }
+
+       NDR_CHECK(ndr_token_retrieve(&ndr->relative_begin_list, p, &begin_offset));
+
+       /* we have marshalled a buffer, see how long it was */
+       len = ndr->offset - begin_offset;
+
+       if (len < 0) {
+               return ndr_push_error(ndr, NDR_ERR_RELATIVE,
+                                     "ndr_push_relative_ptr2_end:"
+                                     "offset %u - begin_offset %u < 0",
+                                     ndr->offset, begin_offset);
+       }
+
+       if (ndr->relative_end_offset < len) {
+               return ndr_push_error(ndr, NDR_ERR_RELATIVE,
+                                     "ndr_push_relative_ptr2_end:"
+                                     "relative_end_offset %u < len %lld",
+                                     ndr->offset, (long long)len);
+       }
+
+       /* the reversed offset is at the end of the main buffer */
+       correct_offset = ndr->relative_end_offset - len;
+
+       if (ndr->flags & LIBNDR_FLAG_NOALIGN) {
+               align = 1;
+       } else if (ndr->flags & LIBNDR_FLAG_ALIGN2) {
+               align = 2;
+       } else if (ndr->flags & LIBNDR_FLAG_ALIGN4) {
+               align = 4;
+       } else if (ndr->flags & LIBNDR_FLAG_ALIGN8) {
+               align = 8;
+       }
+
+       pad = ndr_align_size(correct_offset, align);
+       if (pad) {
+               correct_offset += pad;
+               correct_offset -= align;
+       }
+
+       if (correct_offset < begin_offset) {
+               return ndr_push_error(ndr, NDR_ERR_RELATIVE,
+                                     "ndr_push_relative_ptr2_end: "
+                                     "correct_offset %u < begin_offset %u",
+                                     correct_offset, begin_offset);
+       }
+
+       if (len > 0) {
+               uint32_t clear_size = correct_offset - begin_offset;
+
+               clear_size = MIN(clear_size, len);
+
+               /* now move the marshalled buffer to the end of the main buffer */
+               memmove(ndr->data + correct_offset, ndr->data + begin_offset, len);
+
+               if (clear_size) {
+                       /* and wipe out old buffer within the main buffer */
+                       memset(ndr->data + begin_offset, '\0', clear_size);
+               }
+       }
+
+       /* and set the end offset for the next buffer */
+       ndr->relative_end_offset = correct_offset;
+
+       /* finally write the offset to the main buffer */
+       ndr->offset = correct_offset;
+       NDR_CHECK(ndr_push_relative_ptr2(ndr, p));
+
+       /* restore to where we were in the main buffer */
+       ndr->offset = begin_offset;
+
+       return NDR_ERR_SUCCESS;
+}
+
 /*
   get the current base for relative pointers for the pull
 */
@@ -1228,6 +1768,7 @@ const static struct {
        { NDR_ERR_INVALID_POINTER, "Invalid Pointer" },
        { NDR_ERR_UNREAD_BYTES, "Unread Bytes" },
        { NDR_ERR_NDR64, "NDR64 assertion error" },
+       { NDR_ERR_INCOMPLETE_BUFFER, "Incomplete Buffer" },
        { 0, NULL }
 };