diff options
Diffstat (limited to 'librpc/ndr')
-rw-r--r-- | librpc/ndr/libndr.h | 1 | ||||
-rw-r--r-- | librpc/ndr/ndr.c | 89 |
2 files changed, 87 insertions, 3 deletions
diff --git a/librpc/ndr/libndr.h b/librpc/ndr/libndr.h index 41e214da2a..72e8d21ba4 100644 --- a/librpc/ndr/libndr.h +++ b/librpc/ndr/libndr.h @@ -91,6 +91,7 @@ struct ndr_push { struct ndr_token_list *switch_list; struct ndr_token_list *relative_list; + struct ndr_token_list *relative_begin_list; struct ndr_token_list *nbt_string_list; struct ndr_token_list *full_ptr_list; diff --git a/librpc/ndr/ndr.c b/librpc/ndr/ndr.c index e78b7ee94a..93fbb995f9 100644 --- a/librpc/ndr/ndr.c +++ b/librpc/ndr/ndr.c @@ -1164,9 +1164,13 @@ _PUBLIC_ enum ndr_err_code ndr_push_relative_ptr2_start(struct ndr_push *ndr, co if (!(ndr->flags & LIBNDR_FLAG_RELATIVE_REVERSE)) { return ndr_push_relative_ptr2(ndr, p); } - - return ndr_push_error(ndr, NDR_ERR_RELATIVE, - "ndr_push_relative_ptr2_start RELATIVE_REVERSE flag set, but not supported"); + 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; } /* @@ -1175,9 +1179,88 @@ _PUBLIC_ enum ndr_err_code ndr_push_relative_ptr2_start(struct ndr_push *ndr, co */ _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->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_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) { + /* now move the marshalled buffer to the end of the main buffer */ + memmove(ndr->data + correct_offset, ndr->data + begin_offset, len); + + /* and wipe out old buffer within the main buffer */ + memset(ndr->data + begin_offset, '\0', len); + } + + /* 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; } |