librpc/ndr: add support for relative_short pointers
[ira/wip.git] / librpc / ndr / ndr.c
index 2f1daeaeb5ac2982aec61787d99d7ecad0b9c0a2..a151994b6af64c6d25b907ac8194a76880eb4cfc 100644 (file)
@@ -179,10 +179,10 @@ _PUBLIC_ void ndr_print_debug_helper(struct ndr_print *ndr, const char *format,
        }
 
        for (i=0;i<ndr->depth;i++) {
-               DEBUGADD(0,("    "));
+               DEBUGADD(1,("    "));
        }
 
-       DEBUGADD(0,("%s\n", s));
+       DEBUGADD(1,("%s\n", s));
        free(s);
 }
 
@@ -211,6 +211,8 @@ _PUBLIC_ void ndr_print_debug(ndr_print_fn_t fn, const char *name, void *ptr)
 {
        struct ndr_print *ndr;
 
+       DEBUG(1,(" "));
+
        ndr = talloc_zero(NULL, struct ndr_print);
        if (!ndr) return;
        ndr->print = ndr_print_debug_helper;
@@ -227,6 +229,8 @@ _PUBLIC_ void ndr_print_union_debug(ndr_print_fn_t fn, const char *name, uint32_
 {
        struct ndr_print *ndr;
 
+       DEBUG(1,(" "));
+
        ndr = talloc_zero(NULL, struct ndr_print);
        if (!ndr) return;
        ndr->print = ndr_print_debug_helper;
@@ -244,11 +248,21 @@ _PUBLIC_ void ndr_print_function_debug(ndr_print_function_t fn, const char *name
 {
        struct ndr_print *ndr;
 
+       DEBUG(1,(" "));
+
        ndr = talloc_zero(NULL, struct ndr_print);
        if (!ndr) return;
        ndr->print = ndr_print_debug_helper;
        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);
 }
@@ -270,6 +284,14 @@ _PUBLIC_ char *ndr_print_struct_string(TALLOC_CTX *mem_ctx, ndr_print_fn_t fn, c
        ndr->print = ndr_print_string_helper;
        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:
@@ -333,9 +355,11 @@ _PUBLIC_ void ndr_set_flags(uint32_t *pflags, uint32_t new_flags)
        /* the big/little endian flags are inter-dependent */
        if (new_flags & LIBNDR_FLAG_LITTLE_ENDIAN) {
                (*pflags) &= ~LIBNDR_FLAG_BIGENDIAN;
+               (*pflags) &= ~LIBNDR_FLAG_NDR64;
        }
        if (new_flags & LIBNDR_FLAG_BIGENDIAN) {
                (*pflags) &= ~LIBNDR_FLAG_LITTLE_ENDIAN;
+               (*pflags) &= ~LIBNDR_FLAG_NDR64;
        }
        if (new_flags & LIBNDR_FLAG_REMAINING) {
                (*pflags) &= ~LIBNDR_ALIGN_FLAGS;
@@ -435,7 +459,7 @@ _PUBLIC_ enum ndr_err_code ndr_pull_subcontext_start(struct ndr_pull *ndr,
 
        case 4: {
                uint32_t content_size;
-               NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &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);
@@ -520,7 +544,7 @@ _PUBLIC_ enum ndr_err_code ndr_pull_subcontext_start(struct ndr_pull *ndr,
 
        subndr = talloc_zero(ndr, struct ndr_pull);
        NDR_ERR_HAVE_NO_MEMORY(subndr);
-       subndr->flags           = ndr->flags;
+       subndr->flags           = ndr->flags & ~LIBNDR_FLAG_NDR64;
        subndr->current_mem_ctx = ndr->current_mem_ctx;
 
        subndr->data = ndr->data + ndr->offset;
@@ -564,7 +588,7 @@ _PUBLIC_ enum ndr_err_code ndr_push_subcontext_start(struct ndr_push *ndr,
 
        subndr = ndr_push_init_ctx(ndr, ndr->iconv_convenience);
        NDR_ERR_HAVE_NO_MEMORY(subndr);
-       subndr->flags   = ndr->flags;
+       subndr->flags   = ndr->flags & ~LIBNDR_FLAG_NDR64;
 
        *_subndr = subndr;
        return NDR_ERR_SUCCESS;
@@ -599,7 +623,7 @@ _PUBLIC_ enum ndr_err_code ndr_push_subcontext_end(struct ndr_push *ndr,
                break;
 
        case 4: 
-               NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, subndr->offset));
+               NDR_CHECK(ndr_push_uint3264(ndr, NDR_SCALARS, subndr->offset));
                break;
 
        case 0xFFFFFC01:
@@ -714,7 +738,7 @@ _PUBLIC_ uint32_t ndr_token_peek(struct ndr_token_list **list, const void *key)
 _PUBLIC_ enum ndr_err_code ndr_pull_array_size(struct ndr_pull *ndr, const void *p)
 {
        uint32_t size;
-       NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &size));
+       NDR_CHECK(ndr_pull_uint3264(ndr, NDR_SCALARS, &size));
        return ndr_token_store(ndr, &ndr->array_size_list, p, size);
 }
 
@@ -747,12 +771,12 @@ _PUBLIC_ enum ndr_err_code ndr_check_array_size(struct ndr_pull *ndr, void *p, u
 _PUBLIC_ enum ndr_err_code ndr_pull_array_length(struct ndr_pull *ndr, const void *p)
 {
        uint32_t length, offset;
-       NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &offset));
+       NDR_CHECK(ndr_pull_uint3264(ndr, NDR_SCALARS, &offset));
        if (offset != 0) {
                return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, 
                                      "non-zero array offset %u\n", offset);
        }
-       NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &length));
+       NDR_CHECK(ndr_pull_uint3264(ndr, NDR_SCALARS, &length));
        return ndr_token_store(ndr, &ndr->array_length_list, p, length);
 }
 
@@ -824,7 +848,8 @@ _PUBLIC_ enum ndr_err_code ndr_pull_struct_blob(const DATA_BLOB *blob, TALLOC_CT
        struct ndr_pull *ndr;
        ndr = ndr_pull_init_blob(blob, mem_ctx, iconv_convenience);
        NDR_ERR_HAVE_NO_MEMORY(ndr);
-       NDR_CHECK(fn(ndr, NDR_SCALARS|NDR_BUFFERS, p));
+       NDR_CHECK_FREE(fn(ndr, NDR_SCALARS|NDR_BUFFERS, p));
+       talloc_free(ndr);
        return NDR_ERR_SUCCESS;
 }
 
@@ -836,14 +861,24 @@ _PUBLIC_ enum ndr_err_code ndr_pull_struct_blob_all(const DATA_BLOB *blob, TALLO
                                                    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_ERR_HAVE_NO_MEMORY(ndr);
-       NDR_CHECK(fn(ndr, NDR_SCALARS|NDR_BUFFERS, p));
-       if (ndr->offset < ndr->data_size) {
-               return ndr_pull_error(ndr, NDR_ERR_UNREAD_BYTES,
-                                     "not all bytes consumed ofs[%u] size[%u]",
-                                     ndr->offset, ndr->data_size);
+       NDR_CHECK_FREE(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);
+               talloc_free(ndr);
+               return ret;
        }
+       talloc_free(ndr);
        return NDR_ERR_SUCCESS;
 }
 
@@ -857,8 +892,9 @@ _PUBLIC_ enum ndr_err_code ndr_pull_union_blob(const DATA_BLOB *blob, TALLOC_CTX
        struct ndr_pull *ndr;
        ndr = ndr_pull_init_blob(blob, mem_ctx, iconv_convenience);
        NDR_ERR_HAVE_NO_MEMORY(ndr);
-       NDR_CHECK(ndr_pull_set_switch_value(ndr, p, level));
-       NDR_CHECK(fn(ndr, NDR_SCALARS|NDR_BUFFERS, p));
+       NDR_CHECK_FREE(ndr_pull_set_switch_value(ndr, p, level));
+       NDR_CHECK_FREE(fn(ndr, NDR_SCALARS|NDR_BUFFERS, p));
+       talloc_free(ndr);
        return NDR_ERR_SUCCESS;
 }
 
@@ -871,15 +907,25 @@ _PUBLIC_ enum ndr_err_code ndr_pull_union_blob_all(const DATA_BLOB *blob, TALLOC
                             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_ERR_HAVE_NO_MEMORY(ndr);
-       NDR_CHECK(ndr_pull_set_switch_value(ndr, p, level));
-       NDR_CHECK(fn(ndr, NDR_SCALARS|NDR_BUFFERS, p));
-       if (ndr->offset < ndr->data_size) {
-               return ndr_pull_error(ndr, NDR_ERR_UNREAD_BYTES,
-                                     "not all bytes consumed ofs[%u] size[%u]",
-                                     ndr->offset, ndr->data_size);
+       NDR_CHECK_FREE(ndr_pull_set_switch_value(ndr, p, level));
+       NDR_CHECK_FREE(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);
+               talloc_free(ndr);
+               return ret;
+       }
+       talloc_free(ndr);
        return NDR_ERR_SUCCESS;
 }
 
@@ -924,7 +970,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)
+_PUBLIC_ size_t ndr_size_struct(const void *p, int flags, ndr_push_flags_fn_t push, struct smb_iconv_convenience *iconv_convenience)
 {
        struct ndr_push *ndr;
        enum ndr_err_code status;
@@ -933,7 +979,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, lp_iconv_convenience(global_loadparm));
+       ndr = ndr_push_init_ctx(NULL, iconv_convenience);
        if (!ndr) return 0;
        ndr->flags |= flags | LIBNDR_FLAG_NO_NDR_SIZE;
        status = push(ndr, NDR_SCALARS|NDR_BUFFERS, discard_const(p));
@@ -949,7 +995,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)
+_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)
 {
        struct ndr_push *ndr;
        enum ndr_err_code status;
@@ -958,7 +1004,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, lp_iconv_convenience(global_loadparm));
+       ndr = ndr_push_init_ctx(NULL, iconv_convenience);
        if (!ndr) return 0;
        ndr->flags |= flags | LIBNDR_FLAG_NO_NDR_SIZE;
 
@@ -1027,6 +1073,20 @@ _PUBLIC_ enum ndr_err_code ndr_push_relative_ptr1(struct ndr_push *ndr, const vo
        return ndr_push_uint32(ndr, NDR_SCALARS, 0xFFFFFFFF);
 }
 
+/*
+  push a short relative object - stage1
+  this is called during SCALARS processing
+*/
+_PUBLIC_ enum ndr_err_code ndr_push_short_relative_ptr1(struct ndr_push *ndr, const void *p)
+{
+       if (p == NULL) {
+               NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, 0));
+               return NDR_ERR_SUCCESS;
+       }
+       NDR_CHECK(ndr_push_align(ndr, 2));
+       NDR_CHECK(ndr_token_store(ndr, &ndr->relative_list, p, ndr->offset));
+       return ndr_push_uint16(ndr, NDR_SCALARS, 0xFFFF);
+}
 /*
   push a relative object - stage2
   this is called during buffers processing
@@ -1055,6 +1115,34 @@ _PUBLIC_ enum ndr_err_code ndr_push_relative_ptr2(struct ndr_push *ndr, const vo
        ndr->offset = save_offset;
        return NDR_ERR_SUCCESS;
 }
+/*
+  push a short relative object - stage2
+  this is called during buffers processing
+*/
+_PUBLIC_ enum ndr_err_code ndr_push_short_relative_ptr2(struct ndr_push *ndr, const void *p)
+{
+       uint32_t save_offset;
+       uint32_t ptr_offset = 0xFFFF;
+       if (p == NULL) {
+               return NDR_ERR_SUCCESS;
+       }
+       save_offset = ndr->offset;
+       NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &ptr_offset));
+       if (ptr_offset > ndr->offset) {
+               return ndr_push_error(ndr, NDR_ERR_BUFSIZE,
+                                     "ndr_push_short_relative_ptr2 ptr_offset(%u) > ndr->offset(%u)",
+                                     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->offset = save_offset;
+       return NDR_ERR_SUCCESS;
+}
 
 /*
   get the current base for relative pointers for the pull
@@ -1116,3 +1204,39 @@ _PUBLIC_ enum ndr_err_code ndr_pull_relative_ptr2(struct ndr_pull *ndr, const vo
        NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &rel_offset));
        return ndr_pull_set_offset(ndr, rel_offset);
 }
+
+const static struct {
+       enum ndr_err_code err;
+       const char *string;
+} ndr_err_code_strings[] = {
+       { NDR_ERR_SUCCESS, "Success" },
+       { NDR_ERR_ARRAY_SIZE, "Bad Array Size" },
+       { NDR_ERR_BAD_SWITCH, "Bad Switch" },
+       { NDR_ERR_OFFSET, "Offset Error" },
+       { NDR_ERR_RELATIVE, "Relative Pointer Error" },
+       { NDR_ERR_CHARCNV, "Character Conversion Error" },
+       { NDR_ERR_LENGTH, "Length Error" },
+       { NDR_ERR_SUBCONTEXT, "Subcontext Error" },
+       { NDR_ERR_COMPRESSION, "Compression Error" },
+       { NDR_ERR_STRING, "String Error" },
+       { NDR_ERR_VALIDATE, "Validate Error" },
+       { NDR_ERR_BUFSIZE, "Buffer Size Error" },
+       { NDR_ERR_ALLOC, "Allocation Error" },
+       { NDR_ERR_RANGE, "Range Error" },
+       { NDR_ERR_TOKEN, "Token Error" },
+       { NDR_ERR_IPV4ADDRESS, "IPv4 Address Error" },
+       { NDR_ERR_INVALID_POINTER, "Invalid Pointer" },
+       { NDR_ERR_UNREAD_BYTES, "Unread Bytes" },
+       { NDR_ERR_NDR64, "NDR64 assertion error" },
+       { 0, NULL }
+};
+
+_PUBLIC_ const char *ndr_map_error2string(enum ndr_err_code ndr_err)
+{
+       int i;
+       for (i = 0; ndr_err_code_strings[i].string != NULL; i++) {
+               if (ndr_err_code_strings[i].err == ndr_err)
+                       return ndr_err_code_strings[i].string;
+       }
+       return "Unknown error";
+}