diff options
author | Andrew Tridgell <tridge@samba.org> | 2004-12-09 07:05:00 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 13:06:27 -0500 |
commit | 74eb0017be0e89df7c0ce0a18a7042dc7d06bfdb (patch) | |
tree | f92ef13e0f4712f77852d2ba768e3b8b7d94a0a1 /source4/librpc/ndr/ndr.c | |
parent | 22f141166ad8f54282210699921a5de0b8436742 (diff) | |
download | samba-74eb0017be0e89df7c0ce0a18a7042dc7d06bfdb.tar.gz samba-74eb0017be0e89df7c0ce0a18a7042dc7d06bfdb.tar.bz2 samba-74eb0017be0e89df7c0ce0a18a7042dc7d06bfdb.zip |
r4110: fixed pidl to allow arrays to have size_is() and length_is() elements
that depend on variables that come after the array in the structure or function.
This has been something that has been problematic for a while, but the
winreg QueryValue problem finally prompted me to fix it properly. We
should now go back and fix up all the ugly workarounds we have used to
avoid this problem in other calls.
Unfortunately the solution is fairly complex, and involves the use of
the internal ndr token lists (similar to the solution for relative
pointers). I wonder if anyone else will be able to follow the logic if
I get run over by a bus :-)
(This used to be commit e839b19ec5581f669f2a7705b1fb80845313251c)
Diffstat (limited to 'source4/librpc/ndr/ndr.c')
-rw-r--r-- | source4/librpc/ndr/ndr.c | 118 |
1 files changed, 100 insertions, 18 deletions
diff --git a/source4/librpc/ndr/ndr.c b/source4/librpc/ndr/ndr.c index 857e171224..7b55f4efb7 100644 --- a/source4/librpc/ndr/ndr.c +++ b/source4/librpc/ndr/ndr.c @@ -48,15 +48,11 @@ struct ndr_pull *ndr_pull_init_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx) { struct ndr_pull *ndr; - ndr = talloc_p(mem_ctx, struct ndr_pull); + ndr = talloc_zero_p(mem_ctx, struct ndr_pull); if (!ndr) return NULL; - ndr->flags = 0; ndr->data = blob->data; ndr->data_size = blob->length; - ndr->offset = 0; - ndr->ptr_count = 0; - ndr->relative_list = NULL; return ndr; } @@ -125,7 +121,7 @@ struct ndr_push *ndr_push_init_ctx(TALLOC_CTX *mem_ctx) { struct ndr_push *ndr; - ndr = talloc_p(mem_ctx, struct ndr_push); + ndr = talloc_zero_p(mem_ctx, struct ndr_push); if (!ndr) { return NULL; } @@ -136,9 +132,6 @@ struct ndr_push *ndr_push_init_ctx(TALLOC_CTX *mem_ctx) if (!ndr->data) { return NULL; } - ndr->offset = 0; - ndr->ptr_count = 0; - ndr->relative_list = NULL; return ndr; } @@ -381,8 +374,12 @@ static NTSTATUS ndr_map_error(enum ndr_err_code err) switch (err) { case NDR_ERR_BUFSIZE: return NT_STATUS_BUFFER_TOO_SMALL; + case NDR_ERR_TOKEN: + return NT_STATUS_INTERNAL_ERROR; case NDR_ERR_ALLOC: return NT_STATUS_NO_MEMORY; + case NDR_ERR_ARRAY_SIZE: + return NT_STATUS_ARRAY_BOUNDS_EXCEEDED; default: break; } @@ -655,18 +652,109 @@ static NTSTATUS ndr_token_store(TALLOC_CTX *mem_ctx, /* retrieve a token from a ndr context */ -static uint32_t ndr_token_retrieve(struct ndr_token_list **list, const void *key) +static NTSTATUS ndr_token_retrieve(struct ndr_token_list **list, const void *key, uint32_t *v) { struct ndr_token_list *tok; for (tok=*list;tok;tok=tok->next) { if (tok->key == key) { DLIST_REMOVE((*list), tok); + *v = tok->value; + return NT_STATUS_OK; + } + } + return ndr_map_error(NDR_ERR_TOKEN); +} + +/* + peek at but don't removed a token from a ndr context +*/ +static uint32_t ndr_token_peek(struct ndr_token_list **list, const void *key) +{ + struct ndr_token_list *tok; + for (tok=*list;tok;tok=tok->next) { + if (tok->key == key) { return tok->value; } } return 0; } +/* + pull an array size field and add it to the array_size_list token list +*/ +NTSTATUS ndr_pull_array_size(struct ndr_pull *ndr, const void *p) +{ + uint32_t size; + NDR_CHECK(ndr_pull_uint32(ndr, &size)); + return ndr_token_store(ndr, &ndr->array_size_list, p, size); +} + +/* + get the stored array size field +*/ +uint32_t ndr_get_array_size(struct ndr_pull *ndr, const void *p) +{ + return ndr_token_peek(&ndr->array_size_list, p); +} + +/* + check the stored array size field +*/ +NTSTATUS ndr_check_array_size(struct ndr_pull *ndr, const void **p, uint32_t size) +{ + uint32 stored; + if (*p == NULL) { + return NT_STATUS_OK; + } + NDR_CHECK(ndr_token_retrieve(&ndr->array_size_list, p, &stored)); + if (stored != size) { + return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, + "Bad array size - got %u expected %u\n", + stored, size); + } + return NT_STATUS_OK; +} + +/* + pull an array length field and add it to the array_length_list token list +*/ +NTSTATUS ndr_pull_array_length(struct ndr_pull *ndr, const void *p) +{ + uint32_t length, offset; + NDR_CHECK(ndr_pull_uint32(ndr, &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, &length)); + return ndr_token_store(ndr, &ndr->array_length_list, p, length); +} + +/* + get the stored array length field +*/ +uint32_t ndr_get_array_length(struct ndr_pull *ndr, const void *p) +{ + return ndr_token_peek(&ndr->array_length_list, p); +} + +/* + check the stored array length field +*/ +NTSTATUS ndr_check_array_length(struct ndr_pull *ndr, void *p, uint32_t length) +{ + uint32_t stored; + if (*(void **)p == NULL) { + return NT_STATUS_OK; + } + NDR_CHECK(ndr_token_retrieve(&ndr->array_length_list, p, &stored)); + if (stored != length) { + return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, + "Bad array length - got %u expected %u\n", + stored, length); + } + return NT_STATUS_OK; +} /* pull a relative object - stage1 @@ -689,10 +777,7 @@ NTSTATUS ndr_pull_relative1(struct ndr_pull *ndr, const void *p, uint32_t rel_of NTSTATUS ndr_pull_relative2(struct ndr_pull *ndr, const void *p) { uint32_t rel_offset; - rel_offset = ndr_token_retrieve(&ndr->relative_list, p); - if (rel_offset == 0) { - return NT_STATUS_INTERNAL_ERROR; - } + NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &rel_offset)); return ndr_pull_set_offset(ndr, rel_offset); } @@ -723,10 +808,7 @@ NTSTATUS ndr_push_relative2(struct ndr_push *ndr, const void *p) } NDR_CHECK(ndr_push_align(ndr, 4)); ndr_push_save(ndr, &save); - ndr->offset = ndr_token_retrieve(&ndr->relative_list, p); - if (ndr->offset == 0) { - return NT_STATUS_INTERNAL_ERROR; - } + NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &ndr->offset)); if (ndr->flags & LIBNDR_FLAG_RELATIVE_CURRENT) { NDR_CHECK(ndr_push_uint32(ndr, save.offset - ndr->offset)); } else { |