libndr: implement LIBNDR_RELATIVE_REVERSE handling
authorStefan Metzmacher <metze@samba.org>
Wed, 17 Feb 2010 20:24:40 +0000 (21:24 +0100)
committerStefan Metzmacher <metze@samba.org>
Thu, 18 Feb 2010 12:44:03 +0000 (13:44 +0100)
This is based on Guenther's initial code.

metze

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

index 41e214da2a478eeef47ab196b7f0152c81323717..72e8d21ba40a40836ef4ff1eabcf431a4c275ed6 100644 (file)
@@ -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;
 
index e78b7ee94a3a565cf4ca8dede03f5343581b76dd..93fbb995f9c327cbdea2b6794603d389361031d8 100644 (file)
@@ -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;
 }