summaryrefslogtreecommitdiff
path: root/librpc/ndr
diff options
context:
space:
mode:
Diffstat (limited to 'librpc/ndr')
-rw-r--r--librpc/ndr/libndr.h1
-rw-r--r--librpc/ndr/ndr.c89
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;
}