summaryrefslogtreecommitdiff
path: root/usr/src/lib/smbsrv/libmlrpc/common/ndr_process.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/smbsrv/libmlrpc/common/ndr_process.c')
-rw-r--r--usr/src/lib/smbsrv/libmlrpc/common/ndr_process.c2028
1 files changed, 2028 insertions, 0 deletions
diff --git a/usr/src/lib/smbsrv/libmlrpc/common/ndr_process.c b/usr/src/lib/smbsrv/libmlrpc/common/ndr_process.c
new file mode 100644
index 0000000000..b64ada2955
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlrpc/common/ndr_process.c
@@ -0,0 +1,2028 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Network Data Representation (NDR) is a compatible subset of the DCE RPC
+ * and MSRPC NDR. NDR is used to move parameters consisting of
+ * complicated trees of data constructs between an RPC client and server.
+ */
+
+#include <sys/byteorder.h>
+#include <strings.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/string.h>
+#include <smbsrv/ndr.h>
+
+#define NDR_STRING_MAX 256
+
+#define NDR_IS_UNION(T) \
+ (((T)->type_flags & NDR_F_TYPEOP_MASK) == NDR_F_UNION)
+#define NDR_IS_STRING(T) \
+ (((T)->type_flags & NDR_F_TYPEOP_MASK) == NDR_F_STRING)
+
+extern struct ndr_typeinfo ndt_s_wchar;
+
+/*
+ * The following synopsis describes the terms TOP-MOST, OUTER and INNER.
+ *
+ * Each parameter (call arguments and return values) is a TOP-MOST item.
+ * A TOP-MOST item consists of one or more OUTER items. An OUTER item
+ * consists of one or more INNER items. There are important differences
+ * between each kind, which, primarily, have to do with the allocation
+ * of memory to contain data structures and the order of processing.
+ *
+ * This is most easily demonstrated with a short example.
+ * Consider these structures:
+ *
+ * struct top_param {
+ * long level;
+ * struct list * head;
+ * long count;
+ * };
+ *
+ * struct list {
+ * struct list * next;
+ * char * str; // a string
+ * };
+ *
+ * Now, consider an instance tree like this:
+ *
+ * +---------+ +-------+ +-------+
+ * |top_param| +--->|list #1| +--->|list #2|
+ * +---------+ | +-------+ | +-------+
+ * | level | | | next ----+ | next --->(NULL)
+ * | head ----+ | str -->"foo" | str -->"bar"
+ * | count | | flag | | flag |
+ * +---------+ +-------+ +-------+
+ *
+ * The DCE(MS)/RPC Stub Data encoding for the tree is the following.
+ * The vertical bars (|) indicate OUTER construct boundaries.
+ *
+ * +-----+----------------------+----------------------+-----+-----+-----+
+ * |level|#1.next #1.str #1.flag|#2.next #2.str #2.flag|"bar"|"foo"|count|
+ * +-----+----------------------+----------------------+-----+-----+-----+
+ * level |<----------------------- head -------------------------->|count
+ * TOP TOP TOP
+ *
+ * Here's what to notice:
+ *
+ * - The members of the TOP-MOST construct are scattered through the Stub
+ * Data in the order they occur. This example shows a TOP-MOST construct
+ * consisting of atomic types (pointers and integers). A construct
+ * (struct) within the TOP-MOST construct would be contiguous and not
+ * scattered.
+ *
+ * - The members of OUTER constructs are contiguous, which allows for
+ * non-copied relocated (fixed-up) data structures at the packet's
+ * destination. We don't do fix-ups here. The pointers within the
+ * OUTER constructs are processed depth-first in the order that they
+ * occur. If they were processed breadth first, the sequence would
+ * be #1,"foo",#2,"bar". This is tricky because OUTER constructs may
+ * be variable length, and pointers are often encountered before the
+ * size(s) is known.
+ *
+ * - The INNER constructs are simply the members of an OUTER construct.
+ *
+ * For comparison, consider how ONC RPC would handle the same tree of
+ * data. ONC requires very little buffering, while DCE requires enough
+ * buffer space for the entire message. ONC does atom-by-atom depth-first
+ * (de)serialization and copy, while DCE allows for constructs to be
+ * "fixed-up" (relocated) in place at the destination. The packet data
+ * for the same tree processed by ONC RPC would look like this:
+ *
+ * +---------------------------------------------------------------------+
+ * |level #1.next #2.next #2.str "bar" #2.flag #1.str "foo" #1.flag count|
+ * +---------------------------------------------------------------------+
+ * TOP #1 #2 #2 bar #2 #1 foo #1 TOP
+ *
+ * More details about each TOP-MOST, OUTER, and INNER constructs appear
+ * throughout this source file near where such constructs are processed.
+ *
+ * NDR_REFERENCE
+ *
+ * The primary object for NDR is the struct ndr_reference.
+ *
+ * An ndr_reference indicates the local datum (i.e. native "C" data
+ * format), and the element within the Stub Data (contained within the
+ * RPC PDU (protocol data unit). An ndr_reference also indicates,
+ * largely as a debugging aid, something about the type of the
+ * element/datum, and the enclosing construct for the element. The
+ * ndr_reference's are typically allocated on the stack as locals,
+ * and the chain of ndr_reference.enclosing references is in reverse
+ * order of the call graph.
+ *
+ * The ndr_reference.datum is a pointer to the local memory that
+ * contains/receives the value. The ndr_reference.pdu_offset indicates
+ * where in the Stub Data the value is to be stored/retrieved.
+ *
+ * The ndr_reference also contains various parameters to the NDR
+ * process, such as ndr_reference.size_is, which indicates the size
+ * of variable length data, or ndr_reference.switch_is, which
+ * indicates the arm of a union to use.
+ *
+ * QUEUE OF OUTER REFERENCES
+ *
+ * Some OUTER constructs are variable size. Sometimes (often) we don't
+ * know the size of the OUTER construct until after pointers have been
+ * encountered. Hence, we can not begin processing the referent of the
+ * pointer until after the referring OUTER construct is completely
+ * processed, i.e. we don't know where to find/put the referent in the
+ * Stub Data until we know the size of all its predecessors.
+ *
+ * This is managed using the queue of OUTER references. The queue is
+ * anchored in mlndr_stream.outer_queue_head. At any time,
+ * mlndr_stream.outer_queue_tailp indicates where to put the
+ * ndr_reference for the next encountered pointer.
+ *
+ * Refer to the example above as we illustrate the queue here. In these
+ * illustrations, the queue entries are not the data structures themselves.
+ * Rather, they are ndr_reference entries which **refer** to the data
+ * structures in both the PDU and local memory.
+ *
+ * During some point in the processing, the queue looks like this:
+ *
+ * outer_current -------v
+ * outer_queue_head --> list#1 --0
+ * outer_queue_tailp ---------&
+ *
+ * When the pointer #1.next is encountered, and entry is added to the
+ * queue,
+ *
+ * outer_current -------v
+ * outer_queue_head --> list#1 --> list#2 --0
+ * outer_queue_tailp --------------------&
+ *
+ * and the members of #1 continue to be processed, which encounters
+ * #1.str:
+ *
+ * outer_current -------v
+ * outer_queue_head --> list#1 --> list#2 --> "foo" --0
+ * outer_queue_tailp ------------------------------&
+ *
+ * Upon the completion of list#1, the processing continues by moving
+ * to mlndr_stream.outer_current->next, and the tail is set to this
+ * outer member:
+ *
+ * outer_current ------------------v
+ * outer_queue_head --> list#1 --> list#2 --> "foo" --0
+ * outer_queue_tailp --------------------&
+ *
+ * Space for list#2 is allocated, either in the Stub Data or of local
+ * memory. When #2.next is encountered, it is found to be the null
+ * pointer and no reference is added to the queue. When #2.str is
+ * encountered, it is found to be valid, and a reference is added:
+ *
+ * outer_current ------------------v
+ * outer_queue_head --> list#1 --> list#2 --> "bar" --> "foo" --0
+ * outer_queue_tailp ------------------------------&
+ *
+ * Processing continues in a similar fashion with the string "bar",
+ * which is variable-length. At this point, memory for "bar" may be
+ * malloc()ed during NDR_M_OP_UNMARSHALL:
+ *
+ * outer_current -----------------------------v
+ * outer_queue_head --> list#1 --> list#2 --> "bar" --> "foo" --0
+ * outer_queue_tailp ------------------------------&
+ *
+ * And finishes on string "foo". Notice that because "bar" is a
+ * variable length string, and we don't know the PDU offset for "foo"
+ * until we reach this point.
+ *
+ * When the queue is drained (current->next==0), processing continues
+ * with the next TOP-MOST member.
+ *
+ * The queue of OUTER constructs manages the variable-length semantics
+ * of OUTER constructs and satisfies the depth-first requirement.
+ * We allow the queue to linger until the entire TOP-MOST structure is
+ * processed as an aid to debugging.
+ */
+
+static struct ndr_reference *mlndr_enter_outer_queue(struct ndr_reference *);
+extern int mlndr__ulong(struct ndr_reference *);
+
+/*
+ * TOP-MOST ELEMENTS
+ *
+ * This is fundamentally the first OUTER construct of the parameter,
+ * possibly followed by more OUTER constructs due to pointers. The
+ * datum (local memory) for TOP-MOST constructs (structs) is allocated
+ * by the caller of NDR.
+ *
+ * After the element is transferred, the outer_queue is drained.
+ *
+ * All we have to do is add an entry to the outer_queue for this
+ * top-most member, and commence the outer_queue processing.
+ */
+int
+mlndo_process(struct mlndr_stream *mlnds, struct ndr_typeinfo *ti,
+ char *datum)
+{
+ struct ndr_reference myref;
+
+ bzero(&myref, sizeof (myref));
+ myref.stream = mlnds;
+ myref.datum = datum;
+ myref.name = "PROCESS";
+ myref.ti = ti;
+
+ return (mlndr_topmost(&myref));
+}
+
+int
+mlndo_operation(struct mlndr_stream *mlnds, struct ndr_typeinfo *ti,
+ int opnum, char *datum)
+{
+ struct ndr_reference myref;
+
+ bzero(&myref, sizeof (myref));
+ myref.stream = mlnds;
+ myref.datum = datum;
+ myref.name = "OPERATION";
+ myref.ti = ti;
+ myref.inner_flags = NDR_F_SWITCH_IS;
+ myref.switch_is = opnum;
+
+ if (ti->type_flags != NDR_F_INTERFACE) {
+ NDR_SET_ERROR(&myref, NDR_ERR_NOT_AN_INTERFACE);
+ return (0);
+ }
+
+ return ((*ti->ndr_func)(&myref));
+}
+
+int
+mlndr_params(struct ndr_reference *params_ref)
+{
+ struct ndr_typeinfo *ti = params_ref->ti;
+
+ if (ti->type_flags == NDR_F_OPERATION)
+ return (*ti->ndr_func) (params_ref);
+ else
+ return (mlndr_topmost(params_ref));
+}
+
+int
+mlndr_topmost(struct ndr_reference *top_ref)
+{
+ struct mlndr_stream *mlnds;
+ struct ndr_typeinfo *ti;
+ struct ndr_reference *outer_ref = 0;
+ int is_varlen;
+ int is_string;
+ int error;
+ int rc;
+ unsigned n_fixed;
+ int params;
+
+ assert(top_ref);
+ assert(top_ref->stream);
+ assert(top_ref->ti);
+
+ mlnds = top_ref->stream;
+ ti = top_ref->ti;
+
+ is_varlen = ti->pdu_size_variable_part;
+ is_string = NDR_IS_STRING(ti);
+
+ assert(mlnds->outer_queue_tailp && !*mlnds->outer_queue_tailp);
+ assert(!mlnds->outer_current);
+
+ params = top_ref->inner_flags & NDR_F_PARAMS_MASK;
+
+ switch (params) {
+ case NDR_F_NONE:
+ case NDR_F_SWITCH_IS:
+ if (is_string || is_varlen) {
+ error = NDR_ERR_TOPMOST_VARLEN_ILLEGAL;
+ NDR_SET_ERROR(outer_ref, error);
+ return (0);
+ }
+ n_fixed = ti->pdu_size_fixed_part;
+ break;
+
+ case NDR_F_SIZE_IS:
+ error = NDR_ERR_TOPMOST_VARLEN_ILLEGAL;
+ NDR_SET_ERROR(outer_ref, error);
+ return (0);
+
+ case NDR_F_DIMENSION_IS:
+ if (is_varlen) {
+ error = NDR_ERR_ARRAY_VARLEN_ILLEGAL;
+ NDR_SET_ERROR(outer_ref, error);
+ return (0);
+ }
+ n_fixed = ti->pdu_size_fixed_part * top_ref->dimension_is;
+ break;
+
+ case NDR_F_IS_POINTER:
+ case NDR_F_IS_POINTER+NDR_F_SIZE_IS:
+ n_fixed = 4;
+ break;
+
+ case NDR_F_IS_REFERENCE:
+ case NDR_F_IS_REFERENCE+NDR_F_SIZE_IS:
+ n_fixed = 0;
+ break;
+
+ default:
+ error = NDR_ERR_OUTER_PARAMS_BAD;
+ NDR_SET_ERROR(outer_ref, error);
+ return (0);
+ }
+
+ outer_ref = mlndr_enter_outer_queue(top_ref);
+ if (!outer_ref)
+ return (0); /* error already set */
+
+ /*
+ * Hand-craft the first OUTER construct and directly call
+ * mlndr_inner(). Then, run the outer_queue. We do this
+ * because mlndr_outer() wants to malloc() memory for
+ * the construct, and we already have the memory.
+ */
+
+ /* move the flags, etc, around again, undoes enter_outer_queue() */
+ outer_ref->inner_flags = top_ref->inner_flags;
+ outer_ref->outer_flags = 0;
+ outer_ref->datum = top_ref->datum;
+
+ /* All outer constructs start on a mod4 (longword) boundary */
+ if (!mlndr_outer_align(outer_ref))
+ return (0); /* error already set */
+
+ /* Regardless of what it is, this is where it starts */
+ outer_ref->pdu_offset = mlnds->pdu_scan_offset;
+
+ rc = mlndr_outer_grow(outer_ref, n_fixed);
+ if (!rc)
+ return (0); /* error already set */
+
+ outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_fixed;
+
+ /* set-up outer_current, as though run_outer_queue() was doing it */
+ mlnds->outer_current = outer_ref;
+ mlnds->outer_queue_tailp = &mlnds->outer_current->next;
+ mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
+
+ /* do the topmost member */
+ rc = mlndr_inner(outer_ref);
+ if (!rc)
+ return (0); /* error already set */
+
+ mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
+
+ /* advance, as though run_outer_queue() was doing it */
+ mlnds->outer_current = mlnds->outer_current->next;
+ return (mlndr_run_outer_queue(mlnds));
+}
+
+static struct ndr_reference *
+mlndr_enter_outer_queue(struct ndr_reference *arg_ref)
+{
+ struct mlndr_stream *mlnds = arg_ref->stream;
+ struct ndr_reference *outer_ref;
+
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ outer_ref = (struct ndr_reference *)
+ MLNDS_MALLOC(mlnds, sizeof (*outer_ref), arg_ref);
+ if (!outer_ref) {
+ NDR_SET_ERROR(arg_ref, NDR_ERR_MALLOC_FAILED);
+ return (0);
+ }
+
+ *outer_ref = *arg_ref;
+
+ /* move advice in inner_flags to outer_flags */
+ outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
+ outer_ref->inner_flags = 0;
+ outer_ref->enclosing = mlnds->outer_current;
+ outer_ref->backptr = 0;
+ outer_ref->datum = 0;
+
+ assert(mlnds->outer_queue_tailp);
+
+ outer_ref->next = *mlnds->outer_queue_tailp;
+ *mlnds->outer_queue_tailp = outer_ref;
+ mlnds->outer_queue_tailp = &outer_ref->next;
+ return (outer_ref);
+}
+
+int
+mlndr_run_outer_queue(struct mlndr_stream *mlnds)
+{
+ while (mlnds->outer_current) {
+ mlnds->outer_queue_tailp = &mlnds->outer_current->next;
+
+ if (!mlndr_outer(mlnds->outer_current))
+ return (0);
+
+ mlnds->outer_current = mlnds->outer_current->next;
+ }
+
+ return (1);
+}
+
+/*
+ * OUTER CONSTRUCTS
+ *
+ * OUTER constructs are where the real work is, which stems from the
+ * variable-length potential.
+ *
+ * DCE(MS)/RPC VARIABLE LENGTH -- CONFORMANT, VARYING, VARYING/CONFORMANT
+ *
+ * DCE(MS)/RPC provides for three forms of variable length: CONFORMANT,
+ * VARYING, and VARYING/CONFORMANT.
+ *
+ * What makes this so tough is that the variable-length array may be well
+ * encapsulated within the outer construct. Further, because DCE(MS)/RPC
+ * tries to keep the constructs contiguous in the data stream, the sizing
+ * information precedes the entire OUTER construct. The sizing information
+ * must be used at the appropriate time, which can be after many, many,
+ * many fixed-length elements. During IDL type analysis, we know in
+ * advance constructs that encapsulate variable-length constructs. So,
+ * we know when we have a sizing header and when we don't. The actual
+ * semantics of the header are largely deferred.
+ *
+ * Currently, VARYING constructs are not implemented but they are described
+ * here in case they have to be implemented in the future. Similarly,
+ * DCE(MS)/RPC provides for multi-dimensional arrays, which are currently
+ * not implemented. Only one-dimensional, variable-length arrays are
+ * supported.
+ *
+ * CONFORMANT CONSTRUCTS -- VARIABLE LENGTH ARRAYS START THE SHOW
+ *
+ * All variable-length values are arrays. These arrays may be embedded
+ * well within another construct. However, a variable-length construct
+ * may ONLY appear as the last member of an enclosing construct. Example:
+ *
+ * struct credentials {
+ * ulong uid, gid;
+ * ulong n_gids;
+ * [size_is(n_gids)]
+ * ulong gids[*]; // variable-length.
+ * };
+ *
+ * CONFORMANT constructs have a dynamic size in local memory and in the
+ * PDU. The CONFORMANT quality is indicated by the [size_is()] advice.
+ * CONFORMANT constructs have the following header:
+ *
+ * struct conformant_header {
+ * ulong size_is;
+ * };
+ *
+ * (Multi-dimensional CONFORMANT arrays have a similar header for each
+ * dimension - not implemented).
+ *
+ * Example CONFORMANT construct:
+ *
+ * struct user {
+ * char * name;
+ * struct credentials cred; // see above
+ * };
+ *
+ * Consider the data tree:
+ *
+ * +--------+
+ * | user |
+ * +--------+
+ * | name ----> "fred" (the string is a different OUTER)
+ * | uid |
+ * | gid |
+ * | n_gids | for example, 3
+ * | gids[0]|
+ * | gids[1]|
+ * | gids[2]|
+ * +--------+
+ *
+ * The OUTER construct in the Stub Data would be:
+ *
+ * +---+---------+---------------------------------------------+
+ * |pad|size_is=3 name uid gid n_gids=3 gids[0] gids[1] gids[2]|
+ * +---+---------+---------------------------------------------+
+ * szing hdr|user |<-------------- user.cred ------------>|
+ * |<--- fixed-size ---->|<----- conformant ---->|
+ *
+ * The ndr_typeinfo for struct user will have:
+ * pdu_fixed_size_part = 16 four long words (name uid gid n_gids)
+ * pdu_variable_size_part = 4 per element, sizeof gids[0]
+ *
+ * VARYING CONSTRUCTS -- NOT IMPLEMENTED
+ *
+ * VARYING constructs have the following header:
+ *
+ * struct varying_header {
+ * ulong first_is;
+ * ulong length_is;
+ * };
+ *
+ * This indicates which interval of an array is significant.
+ * Non-intersecting elements of the array are undefined and usually
+ * zero-filled. The first_is parameter for C arrays is always 0 for
+ * the first element.
+ *
+ * N.B. Constructs may contain one CONFORMANT element, which is always
+ * last, but may contain many VARYING elements, which can be anywhere.
+ *
+ * VARYING CONFORMANT constructs have the sizing headers arranged like
+ * this:
+ *
+ * struct conformant_header all_conformant[N_CONFORMANT_DIM];
+ * struct varying_header all_varying[N_VARYING_ELEMS_AND_DIMS];
+ *
+ * The sizing header is immediately followed by the values for the
+ * construct. Again, we don't support more than one dimension and
+ * we don't support VARYING constructs at this time.
+ *
+ * A good example of a VARYING/CONFORMANT data structure is the UNIX
+ * directory entry:
+ *
+ * struct dirent {
+ * ushort reclen;
+ * ushort namlen;
+ * ulong inum;
+ * [size_is(reclen-8) length_is(namlen+1)] // -(2+2+4), +1 for NUL
+ * uchar name[*];
+ * };
+ *
+ *
+ * STRINGS ARE A SPECIAL CASE
+ *
+ * Strings are handled specially. MS/RPC uses VARYING/CONFORMANT structures
+ * for strings. This is a simple one-dimensional variable-length array,
+ * typically with its last element all zeroes. We handle strings with the
+ * header:
+ *
+ * struct string_header {
+ * ulong size_is;
+ * ulong first_is; // always 0
+ * ulong length_is; // always same as size_is
+ * };
+ *
+ * If general support for VARYING and VARYING/CONFORMANT mechanisms is
+ * implemented, we probably won't need the strings special case.
+ */
+int
+mlndr_outer(struct ndr_reference *outer_ref)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ struct ndr_typeinfo *ti = outer_ref->ti;
+ int is_varlen = ti->pdu_size_variable_part;
+ int is_union = NDR_IS_UNION(ti);
+ int is_string = NDR_IS_STRING(ti);
+ int error = NDR_ERR_OUTER_PARAMS_BAD;
+ int params;
+
+ params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
+
+ NDR_TATTLE(outer_ref, "--OUTER--");
+
+ /* All outer constructs start on a mod4 (longword) boundary */
+ if (!mlndr_outer_align(outer_ref))
+ return (0); /* error already set */
+
+ /* Regardless of what it is, this is where it starts */
+ outer_ref->pdu_offset = mlnds->pdu_scan_offset;
+
+ if (is_union) {
+ error = NDR_ERR_OUTER_UNION_ILLEGAL;
+ NDR_SET_ERROR(outer_ref, error);
+ return (0);
+ }
+
+ switch (params) {
+ case NDR_F_NONE:
+ if (is_string)
+ return (mlndr_outer_string(outer_ref));
+ if (is_varlen)
+ return (mlndr_outer_conformant_construct(outer_ref));
+
+ return (mlndr_outer_fixed(outer_ref));
+ break;
+
+ case NDR_F_SIZE_IS:
+ case NDR_F_DIMENSION_IS:
+ if (is_varlen) {
+ error = NDR_ERR_ARRAY_VARLEN_ILLEGAL;
+ break;
+ }
+
+ if (params == NDR_F_SIZE_IS)
+ return (mlndr_outer_conformant_array(outer_ref));
+ else
+ return (mlndr_outer_fixed_array(outer_ref));
+ break;
+
+ default:
+ error = NDR_ERR_OUTER_PARAMS_BAD;
+ break;
+ }
+
+ /*
+ * If we get here, something is wrong. Most likely,
+ * the params flags do not match.
+ */
+ NDR_SET_ERROR(outer_ref, error);
+ return (0);
+}
+
+int
+mlndr_outer_fixed(struct ndr_reference *outer_ref)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ struct ndr_typeinfo *ti = outer_ref->ti;
+ struct ndr_reference myref;
+ char *valp = NULL;
+ int is_varlen = ti->pdu_size_variable_part;
+ int is_union = NDR_IS_UNION(ti);
+ int is_string = NDR_IS_STRING(ti);
+ int rc;
+ unsigned n_hdr;
+ unsigned n_fixed;
+ unsigned n_variable;
+ unsigned n_alloc;
+ unsigned n_pdu_total;
+ int params;
+
+ params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
+
+ assert(!is_varlen && !is_string && !is_union);
+ assert(params == NDR_F_NONE);
+
+ /* no header for this */
+ n_hdr = 0;
+
+ /* fixed part -- exactly one of these */
+ n_fixed = ti->pdu_size_fixed_part;
+ assert(n_fixed > 0);
+
+ /* variable part -- exactly none of these */
+ n_variable = 0;
+
+ /* sum them up to determine the PDU space required */
+ n_pdu_total = n_hdr + n_fixed + n_variable;
+
+ /* similar sum to determine how much local memory is required */
+ n_alloc = n_fixed + n_variable;
+
+ rc = mlndr_outer_grow(outer_ref, n_pdu_total);
+ if (!rc)
+ return (rc); /* error already set */
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ valp = outer_ref->datum;
+ assert(valp);
+ if (outer_ref->backptr) {
+ assert(valp == *outer_ref->backptr);
+ }
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref);
+ if (!valp) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
+ return (0);
+ }
+ if (outer_ref->backptr)
+ *outer_ref->backptr = valp;
+ outer_ref->datum = valp;
+ break;
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ bzero(&myref, sizeof (myref));
+ myref.stream = mlnds;
+ myref.enclosing = outer_ref;
+ myref.ti = outer_ref->ti;
+ myref.datum = outer_ref->datum;
+ myref.name = "FIXED-VALUE";
+ myref.outer_flags = NDR_F_NONE;
+ myref.inner_flags = NDR_F_NONE;
+
+ myref.pdu_offset = outer_ref->pdu_offset;
+ outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
+
+ rc = mlndr_inner(&myref);
+ if (!rc)
+ return (rc); /* error already set */
+
+ mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
+ return (1);
+}
+
+int
+mlndr_outer_fixed_array(struct ndr_reference *outer_ref)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ struct ndr_typeinfo *ti = outer_ref->ti;
+ struct ndr_reference myref;
+ char *valp = NULL;
+ int is_varlen = ti->pdu_size_variable_part;
+ int is_union = NDR_IS_UNION(ti);
+ int is_string = NDR_IS_STRING(ti);
+ int rc;
+ unsigned n_hdr;
+ unsigned n_fixed;
+ unsigned n_variable;
+ unsigned n_alloc;
+ unsigned n_pdu_total;
+ int params;
+
+ params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
+
+ assert(!is_varlen && !is_string && !is_union);
+ assert(params == NDR_F_DIMENSION_IS);
+
+ /* no header for this */
+ n_hdr = 0;
+
+ /* fixed part -- exactly dimension_is of these */
+ n_fixed = ti->pdu_size_fixed_part * outer_ref->dimension_is;
+ assert(n_fixed > 0);
+
+ /* variable part -- exactly none of these */
+ n_variable = 0;
+
+ /* sum them up to determine the PDU space required */
+ n_pdu_total = n_hdr + n_fixed + n_variable;
+
+ /* similar sum to determine how much local memory is required */
+ n_alloc = n_fixed + n_variable;
+
+ rc = mlndr_outer_grow(outer_ref, n_pdu_total);
+ if (!rc)
+ return (rc); /* error already set */
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ valp = outer_ref->datum;
+ assert(valp);
+ if (outer_ref->backptr) {
+ assert(valp == *outer_ref->backptr);
+ }
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref);
+ if (!valp) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
+ return (0);
+ }
+ if (outer_ref->backptr)
+ *outer_ref->backptr = valp;
+ outer_ref->datum = valp;
+ break;
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ bzero(&myref, sizeof (myref));
+ myref.stream = mlnds;
+ myref.enclosing = outer_ref;
+ myref.ti = outer_ref->ti;
+ myref.datum = outer_ref->datum;
+ myref.name = "FIXED-ARRAY";
+ myref.outer_flags = NDR_F_NONE;
+ myref.inner_flags = NDR_F_DIMENSION_IS;
+ myref.dimension_is = outer_ref->dimension_is;
+
+ myref.pdu_offset = outer_ref->pdu_offset;
+ outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
+
+ rc = mlndr_inner(&myref);
+ if (!rc)
+ return (rc); /* error already set */
+
+ mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
+ return (1);
+}
+
+int
+mlndr_outer_conformant_array(struct ndr_reference *outer_ref)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ struct ndr_typeinfo *ti = outer_ref->ti;
+ struct ndr_reference myref;
+ char *valp = NULL;
+ int is_varlen = ti->pdu_size_variable_part;
+ int is_union = NDR_IS_UNION(ti);
+ int is_string = NDR_IS_STRING(ti);
+ unsigned long size_is;
+ int rc;
+ unsigned n_hdr;
+ unsigned n_fixed;
+ unsigned n_variable;
+ unsigned n_alloc;
+ unsigned n_pdu_total;
+ int params;
+
+ params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
+
+ assert(!is_varlen && !is_string && !is_union);
+ assert(params == NDR_F_SIZE_IS);
+
+ /* conformant header for this */
+ n_hdr = 4;
+
+ /* fixed part -- exactly none of these */
+ n_fixed = 0;
+
+ /* variable part -- exactly size_of of these */
+ /* notice that it is the **fixed** size of the ti */
+ n_variable = ti->pdu_size_fixed_part * outer_ref->size_is;
+
+ /* sum them up to determine the PDU space required */
+ n_pdu_total = n_hdr + n_fixed + n_variable;
+
+ /* similar sum to determine how much local memory is required */
+ n_alloc = n_fixed + n_variable;
+
+ rc = mlndr_outer_grow(outer_ref, n_pdu_total);
+ if (!rc)
+ return (rc); /* error already set */
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ size_is = outer_ref->size_is;
+ rc = mlndr_outer_poke_sizing(outer_ref, 0, &size_is);
+ if (!rc)
+ return (0); /* error already set */
+
+ valp = outer_ref->datum;
+ assert(valp);
+ if (outer_ref->backptr) {
+ assert(valp == *outer_ref->backptr);
+ }
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ rc = mlndr_outer_peek_sizing(outer_ref, 0, &size_is);
+ if (!rc)
+ return (0); /* error already set */
+
+ if (size_is != outer_ref->size_is) {
+ NDR_SET_ERROR(outer_ref,
+ NDR_ERR_SIZE_IS_MISMATCH_PDU);
+ return (0);
+ }
+
+ valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref);
+ if (!valp) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
+ return (0);
+ }
+ if (outer_ref->backptr)
+ *outer_ref->backptr = valp;
+ outer_ref->datum = valp;
+ break;
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ bzero(&myref, sizeof (myref));
+ myref.stream = mlnds;
+ myref.enclosing = outer_ref;
+ myref.ti = outer_ref->ti;
+ myref.datum = outer_ref->datum;
+ myref.name = "CONFORMANT-ARRAY";
+ myref.outer_flags = NDR_F_NONE;
+ myref.inner_flags = NDR_F_SIZE_IS;
+ myref.size_is = outer_ref->size_is;
+
+ myref.inner_flags = NDR_F_DIMENSION_IS; /* convenient */
+ myref.dimension_is = outer_ref->size_is; /* convenient */
+
+ myref.pdu_offset = outer_ref->pdu_offset + 4;
+ outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
+
+ outer_ref->type_flags = NDR_F_NONE;
+ outer_ref->inner_flags = NDR_F_NONE;
+
+ rc = mlndr_inner(&myref);
+ if (!rc)
+ return (rc); /* error already set */
+
+ mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
+ return (1);
+}
+
+int
+mlndr_outer_conformant_construct(struct ndr_reference *outer_ref)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ struct ndr_typeinfo *ti = outer_ref->ti;
+ struct ndr_reference myref;
+ char *valp = NULL;
+ int is_varlen = ti->pdu_size_variable_part;
+ int is_union = NDR_IS_UNION(ti);
+ int is_string = NDR_IS_STRING(ti);
+ unsigned long size_is;
+ int rc;
+ unsigned n_hdr;
+ unsigned n_fixed;
+ unsigned n_variable;
+ unsigned n_alloc;
+ unsigned n_pdu_total;
+ int params;
+
+ params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
+
+ assert(is_varlen && !is_string && !is_union);
+ assert(params == NDR_F_NONE);
+
+ /* conformant header for this */
+ n_hdr = 4;
+
+ /* fixed part -- exactly one of these */
+ n_fixed = ti->pdu_size_fixed_part;
+
+ /* variable part -- exactly size_of of these */
+ n_variable = 0; /* 0 for the moment */
+
+ /* sum them up to determine the PDU space required */
+ n_pdu_total = n_hdr + n_fixed + n_variable;
+
+ /* similar sum to determine how much local memory is required */
+ n_alloc = n_fixed + n_variable;
+
+ /* For the moment, grow enough for the fixed-size part */
+ rc = mlndr_outer_grow(outer_ref, n_pdu_total);
+ if (!rc)
+ return (rc); /* error already set */
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ /*
+ * We don't know the size yet. We have to wait for
+ * it. Proceed with the fixed-size part, and await
+ * the call to mlndr_size_is().
+ */
+ size_is = 0;
+ rc = mlndr_outer_poke_sizing(outer_ref, 0, &size_is);
+ if (!rc)
+ return (0); /* error already set */
+
+ valp = outer_ref->datum;
+ assert(valp);
+ if (outer_ref->backptr) {
+ assert(valp == *outer_ref->backptr);
+ }
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ /*
+ * We know the size of the variable part because
+ * of the CONFORMANT header. We will verify
+ * the header against the [size_is(X)] advice
+ * later when mlndr_size_is() is called.
+ */
+ rc = mlndr_outer_peek_sizing(outer_ref, 0, &size_is);
+ if (!rc)
+ return (0); /* error already set */
+
+ /* recalculate metrics */
+ n_variable = size_is * ti->pdu_size_variable_part;
+ n_pdu_total = n_hdr + n_fixed + n_variable;
+ n_alloc = n_fixed + n_variable;
+
+ rc = mlndr_outer_grow(outer_ref, n_pdu_total);
+ if (!rc)
+ return (rc); /* error already set */
+
+ outer_ref->size_is = size_is; /* verified later */
+
+ valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref);
+ if (!valp) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
+ return (0);
+ }
+ if (outer_ref->backptr)
+ *outer_ref->backptr = valp;
+ outer_ref->datum = valp;
+ break;
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ bzero(&myref, sizeof (myref));
+ myref.stream = mlnds;
+ myref.enclosing = outer_ref;
+ myref.ti = outer_ref->ti;
+ myref.datum = outer_ref->datum;
+ myref.name = "CONFORMANT-CONSTRUCT";
+ myref.outer_flags = NDR_F_NONE;
+ myref.inner_flags = NDR_F_NONE;
+ myref.size_is = outer_ref->size_is;
+
+ myref.pdu_offset = outer_ref->pdu_offset + 4;
+ outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
+
+ outer_ref->type_flags = NDR_F_SIZE_IS; /* indicate pending */
+ outer_ref->inner_flags = NDR_F_NONE; /* indicate pending */
+
+ rc = mlndr_inner(&myref);
+ if (!rc)
+ return (rc); /* error already set */
+
+ mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
+
+ if (outer_ref->inner_flags != NDR_F_SIZE_IS) {
+ NDR_SET_ERROR(&myref, NDR_ERR_SIZE_IS_MISMATCH_AFTER);
+ return (0);
+ }
+
+ return (1);
+}
+
+int
+mlndr_size_is(struct ndr_reference *ref)
+{
+ struct mlndr_stream *mlnds = ref->stream;
+ struct ndr_reference *outer_ref = mlnds->outer_current;
+ struct ndr_typeinfo *ti = outer_ref->ti;
+ unsigned long size_is;
+ int rc;
+ unsigned n_hdr;
+ unsigned n_fixed;
+ unsigned n_variable;
+ unsigned n_pdu_total;
+
+ assert(ref->inner_flags & NDR_F_SIZE_IS);
+ size_is = ref->size_is;
+
+ if (outer_ref->type_flags != NDR_F_SIZE_IS) {
+ NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_UNEXPECTED);
+ return (0);
+ }
+
+ if (outer_ref->inner_flags & NDR_F_SIZE_IS) {
+ NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_DUPLICATED);
+ return (0);
+ }
+
+ /* repeat metrics, see mlndr_conformant_construct() above */
+ n_hdr = 4;
+ n_fixed = ti->pdu_size_fixed_part;
+ n_variable = size_is * ti->pdu_size_variable_part;
+ n_pdu_total = n_hdr + n_fixed + n_variable;
+
+ rc = mlndr_outer_grow(outer_ref, n_pdu_total);
+ if (!rc)
+ return (rc); /* error already set */
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ /*
+ * We have to set the sizing header and extend
+ * the size of the PDU (already done).
+ */
+ rc = mlndr_outer_poke_sizing(outer_ref, 0, &size_is);
+ if (!rc)
+ return (0); /* error already set */
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ /*
+ * Allocation done during mlndr_conformant_construct().
+ * All we are doing here is verifying that the
+ * intended size (ref->size_is) matches the sizing header.
+ */
+ if (size_is != outer_ref->size_is) {
+ NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_MISMATCH_PDU);
+ return (0);
+ }
+ break;
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ outer_ref->inner_flags |= NDR_F_SIZE_IS;
+ outer_ref->size_is = ref->size_is;
+ return (1);
+}
+
+int
+mlndr_outer_string(struct ndr_reference *outer_ref)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ struct ndr_typeinfo *ti = outer_ref->ti;
+ struct ndr_reference myref;
+ char *valp = NULL;
+ unsigned is_varlen = ti->pdu_size_variable_part;
+ int is_union = NDR_IS_UNION(ti);
+ int is_string = NDR_IS_STRING(ti);
+ int rc;
+ unsigned n_zeroes;
+ unsigned ix;
+ unsigned long size_is;
+ unsigned long first_is;
+ unsigned long length_is;
+ unsigned n_hdr;
+ unsigned n_fixed;
+ unsigned n_variable;
+ unsigned n_alloc;
+ unsigned n_pdu_total;
+ int params;
+
+ params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
+
+ assert(is_varlen && is_string && !is_union);
+ assert(params == NDR_F_NONE);
+
+ /* string header for this: size_is first_is length_is */
+ n_hdr = 12;
+
+ /* fixed part -- exactly none of these */
+ n_fixed = 0;
+
+ if (!mlndr_outer_grow(outer_ref, n_hdr))
+ return (0); /* error already set */
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ valp = outer_ref->datum;
+ assert(valp);
+
+ if (outer_ref->backptr)
+ assert(valp == *outer_ref->backptr);
+
+ if (ti == &ndt_s_wchar) {
+ /*
+ * size_is is the number of characters in the string,
+ * including the null. We assume valp is UTF-8 encoded.
+ * We can use mts_wcequiv_strlen for ASCII, extended
+ * ASCII or Unicode (UCS-2).
+ */
+ size_is = (mts_wcequiv_strlen(valp) /
+ sizeof (mts_wchar_t)) + 1;
+
+ if (size_is > NDR_STRING_MAX) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN);
+ return (0);
+ }
+ } else {
+ valp = outer_ref->datum;
+ n_zeroes = 0;
+ for (ix = 0; ix < 1024; ix++) {
+ if (valp[ix] == 0) {
+ n_zeroes++;
+ if (n_zeroes >= is_varlen &&
+ ix % is_varlen == 0) {
+ break;
+ }
+ } else {
+ n_zeroes = 0;
+ }
+ }
+ if (ix >= 1024) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN);
+ return (0);
+ }
+ size_is = ix+1;
+ }
+
+ first_is = 0;
+
+ if (mlnds->flags & MLNDS_F_NOTERM)
+ length_is = size_is - 1;
+ else
+ length_is = size_is;
+
+ if (!mlndr_outer_poke_sizing(outer_ref, 0, &size_is) ||
+ !mlndr_outer_poke_sizing(outer_ref, 4, &first_is) ||
+ !mlndr_outer_poke_sizing(outer_ref, 8, &length_is))
+ return (0); /* error already set */
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ if (!mlndr_outer_peek_sizing(outer_ref, 0, &size_is) ||
+ !mlndr_outer_peek_sizing(outer_ref, 4, &first_is) ||
+ !mlndr_outer_peek_sizing(outer_ref, 8, &length_is))
+ return (0); /* error already set */
+
+ /*
+ * In addition to the first_is check, we used to check that
+ * size_is or size_is-1 was equal to length_is but Windows95
+ * doesn't conform to this "rule" (see variable part below).
+ * The srvmgr tool for Windows95 sent the following values
+ * for a path string:
+ *
+ * size_is = 261 (0x105)
+ * first_is = 0
+ * length_is = 53 (0x35)
+ *
+ * The length_is was correct (for the given path) but the
+ * size_is was the maximum path length rather than being
+ * related to length_is.
+ */
+ if (first_is != 0) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_STRING_SIZING);
+ return (0);
+ }
+
+ if (ti == &ndt_s_wchar) {
+ /*
+ * Decoding Unicode to UTF-8; we need to allow
+ * for the maximum possible char size. It would
+ * be nice to use mbequiv_strlen but the string
+ * may not be null terminated.
+ */
+ n_alloc = (size_is + 1) * MTS_MB_CHAR_MAX;
+ } else {
+ n_alloc = (size_is + 1) * is_varlen;
+ }
+
+ valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref);
+ if (!valp) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
+ return (0);
+ }
+
+ bzero(valp, (size_is+1) * is_varlen);
+
+ if (outer_ref->backptr)
+ *outer_ref->backptr = valp;
+ outer_ref->datum = valp;
+ break;
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ /*
+ * Variable part - exactly length_is of these.
+ *
+ * Usually, length_is is same as size_is and includes nul.
+ * Some protocols use length_is = size_is-1, and length_is does
+ * not include the nul (which is more consistent with DCE spec).
+ * If the length_is is 0, there is no data following the
+ * sizing header, regardless of size_is.
+ */
+ n_variable = length_is * is_varlen;
+
+ /* sum them up to determine the PDU space required */
+ n_pdu_total = n_hdr + n_fixed + n_variable;
+
+ /* similar sum to determine how much local memory is required */
+ n_alloc = n_fixed + n_variable;
+
+ rc = mlndr_outer_grow(outer_ref, n_pdu_total);
+ if (!rc)
+ return (rc); /* error already set */
+
+ if (length_is > 0) {
+ bzero(&myref, sizeof (myref));
+ myref.stream = mlnds;
+ myref.enclosing = outer_ref;
+ myref.ti = outer_ref->ti;
+ myref.datum = outer_ref->datum;
+ myref.name = "OUTER-STRING";
+ myref.outer_flags = NDR_F_IS_STRING;
+ myref.inner_flags = NDR_F_NONE;
+
+ /*
+ * Set up size_is and strlen_is for mlndr_s_wchar.
+ */
+ myref.size_is = size_is;
+ myref.strlen_is = length_is;
+ }
+
+ myref.pdu_offset = outer_ref->pdu_offset + 12;
+
+ /*
+ * Don't try to decode empty strings.
+ */
+ if ((size_is == 0) && (first_is == 0) && (length_is == 0)) {
+ mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
+ return (1);
+ }
+
+ if ((size_is != 0) && (length_is != 0)) {
+ rc = mlndr_inner(&myref);
+ if (!rc)
+ return (rc); /* error already set */
+ }
+
+ mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
+ return (1);
+}
+
+int
+mlndr_outer_peek_sizing(struct ndr_reference *outer_ref, unsigned offset,
+ unsigned long *sizing_p)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ unsigned long pdu_offset;
+ int rc;
+
+ pdu_offset = outer_ref->pdu_offset + offset;
+
+ if (pdu_offset < mlnds->outer_current->pdu_offset ||
+ pdu_offset > mlnds->outer_current->pdu_end_offset ||
+ pdu_offset+4 > mlnds->outer_current->pdu_end_offset) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_BOUNDS_CHECK);
+ return (0);
+ }
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_UNIMPLEMENTED);
+ return (0);
+
+ case NDR_M_OP_UNMARSHALL:
+ rc = MLNDS_GET_PDU(mlnds, pdu_offset, 4, (char *)sizing_p,
+ mlnds->swap, outer_ref);
+ break;
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ return (rc);
+}
+
+int
+mlndr_outer_poke_sizing(struct ndr_reference *outer_ref, unsigned offset,
+ unsigned long *sizing_p)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ unsigned long pdu_offset;
+ int rc;
+
+ pdu_offset = outer_ref->pdu_offset + offset;
+
+ if (pdu_offset < mlnds->outer_current->pdu_offset ||
+ pdu_offset > mlnds->outer_current->pdu_end_offset ||
+ pdu_offset+4 > mlnds->outer_current->pdu_end_offset) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_BOUNDS_CHECK);
+ return (0);
+ }
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ rc = MLNDS_PUT_PDU(mlnds, pdu_offset, 4, (char *)sizing_p,
+ mlnds->swap, outer_ref);
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_UNIMPLEMENTED);
+ return (0);
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ return (rc);
+}
+
+/*
+ * All OUTER constructs begin on a mod4 (dword) boundary - except
+ * for the ones that don't: some MSRPC calls appear to use word or
+ * packed alignment. Strings appear to be dword aligned.
+ */
+int
+mlndr_outer_align(struct ndr_reference *outer_ref)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ int rc;
+ unsigned n_pad;
+ unsigned align;
+
+ if (outer_ref->packed_alignment && outer_ref->ti != &ndt_s_wchar) {
+ align = outer_ref->ti->alignment;
+ n_pad = ((align + 1) - mlnds->pdu_scan_offset) & align;
+ } else {
+ n_pad = (4 - mlnds->pdu_scan_offset) & 3;
+ }
+
+ if (n_pad == 0)
+ return (1); /* already aligned, often the case */
+
+ if (!mlndr_outer_grow(outer_ref, n_pad))
+ return (0); /* error already set */
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ rc = MLNDS_PAD_PDU(mlnds,
+ mlnds->pdu_scan_offset, n_pad, outer_ref);
+ if (!rc) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_PAD_FAILED);
+ return (0);
+ }
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ break;
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ mlnds->pdu_scan_offset += n_pad;
+ return (1);
+}
+
+int
+mlndr_outer_grow(struct ndr_reference *outer_ref, unsigned n_total)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ unsigned long pdu_want_size;
+ int rc, is_ok = 0;
+
+ pdu_want_size = mlnds->pdu_scan_offset + n_total;
+
+ if (pdu_want_size <= mlnds->pdu_max_size) {
+ is_ok = 1;
+ }
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ if (is_ok)
+ break;
+ rc = MLNDS_GROW_PDU(mlnds, pdu_want_size, outer_ref);
+ if (!rc) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_GROW_FAILED);
+ return (0);
+ }
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ if (is_ok)
+ break;
+ NDR_SET_ERROR(outer_ref, NDR_ERR_UNDERFLOW);
+ return (0);
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ if (mlnds->pdu_size < pdu_want_size)
+ mlnds->pdu_size = pdu_want_size;
+
+ outer_ref->pdu_end_offset = pdu_want_size;
+ return (1);
+}
+
+/*
+ * INNER ELEMENTS
+ *
+ * The local datum (arg_ref->datum) already exists, there is no need to
+ * malloc() it. The datum should point at a member of a structure.
+ *
+ * For the most part, mlndr_inner() and its helpers are just a sanity
+ * check. The underlying ti->ndr_func() could be called immediately
+ * for non-pointer elements. For the sake of robustness, we detect
+ * run-time errors here. Most of the situations this protects against
+ * have already been checked by the IDL compiler. This is also a
+ * common point for processing of all data, and so is a convenient
+ * place to work from for debugging.
+ */
+int
+mlndr_inner(struct ndr_reference *arg_ref)
+{
+ struct ndr_typeinfo *ti = arg_ref->ti;
+ int is_varlen = ti->pdu_size_variable_part;
+ int is_union = NDR_IS_UNION(ti);
+ int error = NDR_ERR_INNER_PARAMS_BAD;
+ int params;
+
+ params = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
+
+ switch (params) {
+ case NDR_F_NONE:
+ if (is_union) {
+ error = NDR_ERR_SWITCH_VALUE_MISSING;
+ break;
+ }
+ return (*ti->ndr_func)(arg_ref);
+ break;
+
+ case NDR_F_SIZE_IS:
+ case NDR_F_DIMENSION_IS:
+ case NDR_F_IS_POINTER+NDR_F_SIZE_IS: /* pointer to something */
+ case NDR_F_IS_REFERENCE+NDR_F_SIZE_IS: /* pointer to something */
+ if (is_varlen) {
+ error = NDR_ERR_ARRAY_VARLEN_ILLEGAL;
+ break;
+ }
+ if (is_union) {
+ error = NDR_ERR_ARRAY_UNION_ILLEGAL;
+ break;
+ }
+ if (params & NDR_F_IS_POINTER)
+ return (mlndr_inner_pointer(arg_ref));
+ else if (params & NDR_F_IS_REFERENCE)
+ return (mlndr_inner_reference(arg_ref));
+ else
+ return (mlndr_inner_array(arg_ref));
+ break;
+
+ case NDR_F_IS_POINTER: /* type is pointer to one something */
+ if (is_union) {
+ error = NDR_ERR_ARRAY_UNION_ILLEGAL;
+ break;
+ }
+ return (mlndr_inner_pointer(arg_ref));
+ break;
+
+ case NDR_F_IS_REFERENCE: /* type is pointer to one something */
+ if (is_union) {
+ error = NDR_ERR_ARRAY_UNION_ILLEGAL;
+ break;
+ }
+ return (mlndr_inner_reference(arg_ref));
+ break;
+
+ case NDR_F_SWITCH_IS:
+ if (!is_union) {
+ error = NDR_ERR_SWITCH_VALUE_ILLEGAL;
+ break;
+ }
+ return (*ti->ndr_func)(arg_ref);
+ break;
+
+ default:
+ error = NDR_ERR_INNER_PARAMS_BAD;
+ break;
+ }
+
+ /*
+ * If we get here, something is wrong. Most likely,
+ * the params flags do not match
+ */
+ NDR_SET_ERROR(arg_ref, error);
+ return (0);
+}
+
+int
+mlndr_inner_pointer(struct ndr_reference *arg_ref)
+{
+ struct mlndr_stream *mlnds = arg_ref->stream;
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ char **valpp = (char **)arg_ref->datum;
+ struct ndr_reference *outer_ref;
+
+ if (!mlndr__ulong(arg_ref))
+ return (0); /* error */
+ if (!*valpp)
+ return (1); /* NULL pointer */
+
+ outer_ref = mlndr_enter_outer_queue(arg_ref);
+ if (!outer_ref)
+ return (0); /* error already set */
+
+ /* move advice in inner_flags to outer_flags sans pointer */
+ outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
+ outer_ref->outer_flags &= ~NDR_F_IS_POINTER;
+#ifdef NDR_INNER_NOT_YET
+ outer_ref->outer_flags |= NDR_F_BACKPTR;
+ if (outer_ref->outer_flags & NDR_F_SIZE_IS) {
+ outer_ref->outer_flags |= NDR_F_ARRAY+NDR_F_CONFORMANT;
+ }
+#endif /* NDR_INNER_NOT_YET */
+
+ outer_ref->backptr = valpp;
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ outer_ref->datum = *valpp;
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ /*
+ * This is probably wrong if the application allocated
+ * memory in advance. Indicate no value for now.
+ * ONC RPC handles this case.
+ */
+ *valpp = 0;
+ outer_ref->datum = 0;
+ break;
+ }
+
+ return (1); /* pointer dereference scheduled */
+}
+
+int
+mlndr_inner_reference(struct ndr_reference *arg_ref)
+{
+ struct mlndr_stream *mlnds = arg_ref->stream;
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ char **valpp = (char **)arg_ref->datum;
+ struct ndr_reference *outer_ref;
+
+ outer_ref = mlndr_enter_outer_queue(arg_ref);
+ if (!outer_ref)
+ return (0); /* error already set */
+
+ /* move advice in inner_flags to outer_flags sans pointer */
+ outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
+ outer_ref->outer_flags &= ~NDR_F_IS_REFERENCE;
+#ifdef NDR_INNER_REF_NOT_YET
+ outer_ref->outer_flags |= NDR_F_BACKPTR;
+ if (outer_ref->outer_flags & NDR_F_SIZE_IS) {
+ outer_ref->outer_flags |= NDR_F_ARRAY+NDR_F_CONFORMANT;
+ }
+#endif /* NDR_INNER_REF_NOT_YET */
+
+ outer_ref->backptr = valpp;
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ outer_ref->datum = *valpp;
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ /*
+ * This is probably wrong if the application allocated
+ * memory in advance. Indicate no value for now.
+ * ONC RPC handles this case.
+ */
+ *valpp = 0;
+ outer_ref->datum = 0;
+ break;
+ }
+
+ return (1); /* pointer dereference scheduled */
+}
+
+int
+mlndr_inner_array(struct ndr_reference *encl_ref)
+{
+ struct ndr_typeinfo *ti = encl_ref->ti;
+ struct ndr_reference myref;
+ unsigned long pdu_offset = encl_ref->pdu_offset;
+ unsigned long n_elem;
+ unsigned long i;
+ char name[30];
+
+ if (encl_ref->inner_flags & NDR_F_SIZE_IS) {
+ /* now is the time to check/set size */
+ if (!mlndr_size_is(encl_ref))
+ return (0); /* error already set */
+ n_elem = encl_ref->size_is;
+ } else {
+ assert(encl_ref->inner_flags & NDR_F_DIMENSION_IS);
+ n_elem = encl_ref->dimension_is;
+ }
+
+ bzero(&myref, sizeof (myref));
+ myref.enclosing = encl_ref;
+ myref.stream = encl_ref->stream;
+ myref.packed_alignment = 0;
+ myref.ti = ti;
+ myref.inner_flags = NDR_F_NONE;
+
+ for (i = 0; i < n_elem; i++) {
+ (void) sprintf(name, "[%lu]", i);
+ myref.name = name;
+ myref.pdu_offset = pdu_offset + i * ti->pdu_size_fixed_part;
+ myref.datum = encl_ref->datum + i * ti->c_size_fixed_part;
+
+ if (!mlndr_inner(&myref))
+ return (0);
+ }
+
+ return (1);
+}
+
+
+/*
+ * BASIC TYPES
+ */
+#define MAKE_BASIC_TYPE_BASE(TYPE, SIZE) \
+ extern int mlndr_##TYPE(struct ndr_reference *encl_ref); \
+ struct ndr_typeinfo ndt_##TYPE = { \
+ 1, /* NDR version */ \
+ (SIZE)-1, /* alignment */ \
+ NDR_F_NONE, /* flags */ \
+ mlndr_##TYPE, /* ndr_func */ \
+ SIZE, /* pdu_size_fixed_part */ \
+ 0, /* pdu_size_variable_part */ \
+ SIZE, /* c_size_fixed_part */ \
+ 0, /* c_size_variable_part */ \
+ }; \
+ int mlndr_##TYPE(struct ndr_reference *ref) { \
+ return (mlndr_basic_integer(ref, SIZE)); \
+}
+
+#define MAKE_BASIC_TYPE_STRING(TYPE, SIZE) \
+ extern int mlndr_s##TYPE(struct ndr_reference *encl_ref); \
+ struct ndr_typeinfo ndt_s##TYPE = { \
+ 1, /* NDR version */ \
+ (SIZE)-1, /* alignment */ \
+ NDR_F_STRING, /* flags */ \
+ mlndr_s##TYPE, /* ndr_func */ \
+ 0, /* pdu_size_fixed_part */ \
+ SIZE, /* pdu_size_variable_part */ \
+ 0, /* c_size_fixed_part */ \
+ SIZE, /* c_size_variable_part */ \
+ }; \
+ int mlndr_s##TYPE(struct ndr_reference *ref) { \
+ return (mlndr_string_basic_integer(ref, &ndt_##TYPE)); \
+}
+
+#define MAKE_BASIC_TYPE(TYPE, SIZE) \
+ MAKE_BASIC_TYPE_BASE(TYPE, SIZE) \
+ MAKE_BASIC_TYPE_STRING(TYPE, SIZE)
+
+extern int
+mlndr_basic_integer(struct ndr_reference *ref, unsigned size);
+
+extern int
+mlndr_string_basic_integer(struct ndr_reference *encl_ref,
+ struct ndr_typeinfo *type_under);
+
+
+MAKE_BASIC_TYPE(_char, 1)
+MAKE_BASIC_TYPE(_uchar, 1)
+MAKE_BASIC_TYPE(_short, 2)
+MAKE_BASIC_TYPE(_ushort, 2)
+MAKE_BASIC_TYPE(_long, 4)
+MAKE_BASIC_TYPE(_ulong, 4)
+
+MAKE_BASIC_TYPE_BASE(_wchar, 2)
+
+int
+mlndr_basic_integer(struct ndr_reference *ref, unsigned size)
+{
+ struct mlndr_stream *mlnds = ref->stream;
+ char *valp = (char *)ref->datum;
+ int rc;
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ rc = MLNDS_PUT_PDU(mlnds, ref->pdu_offset, size,
+ valp, mlnds->swap, ref);
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ rc = MLNDS_GET_PDU(mlnds, ref->pdu_offset, size,
+ valp, mlnds->swap, ref);
+ break;
+
+ default:
+ NDR_SET_ERROR(ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ return (rc);
+}
+
+int
+mlndr_string_basic_integer(struct ndr_reference *encl_ref,
+ struct ndr_typeinfo *type_under)
+{
+ unsigned long pdu_offset = encl_ref->pdu_offset;
+ unsigned size = type_under->pdu_size_fixed_part;
+ char *valp;
+ struct ndr_reference myref;
+ unsigned long i;
+ long sense = 0;
+ char name[30];
+
+ assert(size != 0);
+
+ bzero(&myref, sizeof (myref));
+ myref.enclosing = encl_ref;
+ myref.stream = encl_ref->stream;
+ myref.packed_alignment = 0;
+ myref.ti = type_under;
+ myref.inner_flags = NDR_F_NONE;
+ myref.name = name;
+
+ for (i = 0; i < NDR_STRING_MAX; i++) {
+ (void) sprintf(name, "[%lu]", i);
+ myref.pdu_offset = pdu_offset + i * size;
+ valp = encl_ref->datum + i * size;
+ myref.datum = valp;
+
+ if (!mlndr_inner(&myref))
+ return (0);
+
+ switch (size) {
+ case 1: sense = *valp; break;
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ case 2: sense = *(short *)valp; break;
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ case 4: sense = *(long *)valp; break;
+ }
+
+ if (!sense)
+ break;
+ }
+
+ return (1);
+}
+
+
+extern int mlndr_s_wchar(struct ndr_reference *encl_ref);
+struct ndr_typeinfo ndt_s_wchar = {
+ 1, /* NDR version */
+ 2-1, /* alignment */
+ NDR_F_STRING, /* flags */
+ mlndr_s_wchar, /* ndr_func */
+ 0, /* pdu_size_fixed_part */
+ 2, /* pdu_size_variable_part */
+ 0, /* c_size_fixed_part */
+ 1, /* c_size_variable_part */
+};
+
+
+/*
+ * Hand coded wchar function because all strings are transported
+ * as wide characters. During NDR_M_OP_MARSHALL, we convert from
+ * multi-byte to wide characters. During NDR_M_OP_UNMARSHALL, we
+ * convert from wide characters to multi-byte.
+ *
+ * It appeared that NT would sometimes leave a spurious character
+ * in the data stream before the null wide_char, which would get
+ * included in the string decode because we processed until the
+ * null character. It now looks like NT does not always terminate
+ * RPC Unicode strings and the terminating null is a side effect
+ * of field alignment. So now we rely on the strlen_is (set up in
+ * mlndr_outer_string) of the enclosing reference. This may or may
+ * not include the null but it doesn't matter, the algorithm will
+ * get it right.
+ */
+int
+mlndr_s_wchar(struct ndr_reference *encl_ref)
+{
+ struct mlndr_stream *mlnds = encl_ref->stream;
+ unsigned short wide_char;
+ char *valp;
+ struct ndr_reference myref;
+ unsigned long i;
+ char name[30];
+ int count;
+ int char_count = 0;
+
+ if (mlnds->m_op == NDR_M_OP_UNMARSHALL) {
+ /*
+ * To avoid problems with zero length strings
+ * we can just null terminate here and be done.
+ */
+ if (encl_ref->strlen_is == 0) {
+ encl_ref->datum[0] = '\0';
+ return (1);
+ }
+ }
+
+ bzero(&myref, sizeof (myref));
+ myref.enclosing = encl_ref;
+ myref.stream = encl_ref->stream;
+ myref.packed_alignment = 0;
+ myref.ti = &ndt__wchar;
+ myref.inner_flags = NDR_F_NONE;
+ myref.datum = (char *)&wide_char;
+ myref.name = name;
+ myref.pdu_offset = encl_ref->pdu_offset;
+
+ valp = encl_ref->datum;
+ count = 0;
+
+ for (i = 0; i < NDR_STRING_MAX; i++) {
+ (void) sprintf(name, "[%lu]", i);
+
+ if (mlnds->m_op == NDR_M_OP_MARSHALL) {
+ count = mts_mbtowc((mts_wchar_t *)&wide_char, valp,
+ MTS_MB_CHAR_MAX);
+ if (count < 0) {
+ return (0);
+ } else if (count == 0) {
+ if (encl_ref->strlen_is != encl_ref->size_is)
+ break;
+
+ /*
+ * If the input char is 0, mbtowc
+ * returns 0 without setting wide_char.
+ * Set wide_char to 0 and a count of 1.
+ */
+ wide_char = *valp;
+ count = 1;
+ }
+ }
+
+ if (!mlndr_inner(&myref))
+ return (0);
+
+ if (mlnds->m_op == NDR_M_OP_UNMARSHALL) {
+ count = mts_wctomb(valp, wide_char);
+
+ if ((++char_count) == encl_ref->strlen_is) {
+ valp += count;
+ *valp = '\0';
+ break;
+ }
+ }
+
+ if (!wide_char)
+ break;
+
+ myref.pdu_offset += sizeof (wide_char);
+ valp += count;
+ }
+
+ return (1);
+}
+
+/*
+ * Converts a multibyte character string to a little-endian, wide-char
+ * string. No more than nwchars wide characters are stored.
+ * A terminating null wide character is appended if there is room.
+ *
+ * Returns the number of wide characters converted, not counting
+ * any terminating null wide character. Returns -1 if an invalid
+ * multibyte character is encountered.
+ */
+size_t
+ndr_mbstowcs(struct mlndr_stream *mlnds, mts_wchar_t *wcs, const char *mbs,
+ size_t nwchars)
+{
+ mts_wchar_t *start = wcs;
+ int nbytes;
+
+ while (nwchars--) {
+ nbytes = ndr_mbtowc(mlnds, wcs, mbs, MTS_MB_CHAR_MAX);
+ if (nbytes < 0) {
+ *wcs = 0;
+ return ((size_t)-1);
+ }
+
+ if (*mbs == 0)
+ break;
+
+ ++wcs;
+ mbs += nbytes;
+ }
+
+ return (wcs - start);
+}
+
+/*
+ * Converts a multibyte character to a little-endian, wide-char, which
+ * is stored in wcharp. Up to nbytes bytes are examined.
+ *
+ * If mbchar is valid, returns the number of bytes processed in mbchar.
+ * If mbchar is invalid, returns -1. See also mts_mbtowc().
+ */
+/*ARGSUSED*/
+int
+ndr_mbtowc(struct mlndr_stream *mlnds, mts_wchar_t *wcharp,
+ const char *mbchar, size_t nbytes)
+{
+ int rc;
+
+ if ((rc = mts_mbtowc(wcharp, mbchar, nbytes)) < 0)
+ return (rc);
+
+#ifdef _BIG_ENDIAN
+ if (mlnds == NULL || NDR_MODE_MATCH(mlnds, NDR_MODE_RETURN_SEND))
+ *wcharp = BSWAP_16(*wcharp);
+#endif
+
+ return (rc);
+}