libndr: implement LIBNDR_RELATIVE_REVERSE handling
authorStefan Metzmacher <metze@samba.org>
Wed, 17 Feb 2010 20:24:40 +0000 (21:24 +0100)
committerKarolin Seeger <kseeger@samba.org>
Fri, 19 Feb 2010 13:32:31 +0000 (14:32 +0100)
This is based on Guenther's initial code.

metze
(cherry picked from commit 31c7780c16651b284009874cf3da04587dc36d19)
(cherry picked from commit 148460b4ed9dd1ecf83acace0641bab6d22f7296)

librpc/ndr/libndr.h
librpc/ndr/ndr.c

index ca920ec068be489cfed78abeecb187d59dd669c4..ff7c9125d63094f471b21f185ce1d7494ee8461b 100644 (file)
@@ -90,6 +90,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;
 
index 19620acc56f15b119dcf36aef50912ecdbf125f2..c5c6804afc286e3c7be69711313a9f0fcceca9e9 100644 (file)
@@ -1107,9 +1107,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;
 }
 
 /*
@@ -1118,9 +1122,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;
 }