xref: /titanic_44/usr/src/lib/smbsrv/libmlrpc/common/ndr_process.c (revision 22eb7cb54d8a6bcf6fe2674cb4b1f0cf2d85cfb6)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Network Data Representation (NDR) is a compatible subset of the DCE RPC
30  * and MSRPC NDR.  NDR is used to move parameters consisting of
31  * complicated trees of data constructs between an RPC client and server.
32  */
33 
34 #include <sys/byteorder.h>
35 #include <strings.h>
36 #include <assert.h>
37 #include <string.h>
38 #include <stdlib.h>
39 
40 #include <smbsrv/libsmb.h>
41 #include <smbsrv/string.h>
42 #include <smbsrv/ndr.h>
43 
44 #define	NDR_STRING_MAX		256
45 
46 #define	NDR_IS_UNION(T)	\
47 	(((T)->type_flags & NDR_F_TYPEOP_MASK) == NDR_F_UNION)
48 #define	NDR_IS_STRING(T)	\
49 	(((T)->type_flags & NDR_F_TYPEOP_MASK) == NDR_F_STRING)
50 
51 extern struct ndr_typeinfo ndt_s_wchar;
52 
53 /*
54  * The following synopsis describes the terms TOP-MOST, OUTER and INNER.
55  *
56  * Each parameter (call arguments and return values) is a TOP-MOST item.
57  * A TOP-MOST item consists of one or more OUTER items.  An OUTER item
58  * consists of one or more INNER items.  There are important differences
59  * between each kind, which, primarily, have to do with the allocation
60  * of memory to contain data structures and the order of processing.
61  *
62  * This is most easily demonstrated with a short example.
63  * Consider these structures:
64  *
65  *	struct top_param {
66  *		long		level;
67  *		struct list *	head;
68  *		long		count;
69  *	};
70  *
71  *	struct list {
72  *		struct list *	next;
73  *		char *		str; // a string
74  *	};
75  *
76  * Now, consider an instance tree like this:
77  *
78  *	+---------+       +-------+       +-------+
79  *	|top_param|  +--->|list #1|  +--->|list #2|
80  *	+---------+  |    +-------+  |    +-------+
81  *	| level   |  |    | next ----+    | next --->(NULL)
82  *	| head   ----+    | str  -->"foo" | str  -->"bar"
83  *	| count   |       | flag  |       | flag  |
84  *	+---------+       +-------+       +-------+
85  *
86  * The DCE(MS)/RPC Stub Data encoding for the tree is the following.
87  * The vertical bars (|) indicate OUTER construct boundaries.
88  *
89  *   +-----+----------------------+----------------------+-----+-----+-----+
90  *   |level|#1.next #1.str #1.flag|#2.next #2.str #2.flag|"bar"|"foo"|count|
91  *   +-----+----------------------+----------------------+-----+-----+-----+
92  *   level |<----------------------- head -------------------------->|count
93  *   TOP    TOP                                                       TOP
94  *
95  * Here's what to notice:
96  *
97  * - The members of the TOP-MOST construct are scattered through the Stub
98  *   Data in the order they occur.  This example shows a TOP-MOST construct
99  *   consisting of atomic types (pointers and integers).  A construct
100  *   (struct) within the TOP-MOST construct would be contiguous and not
101  *   scattered.
102  *
103  * - The members of OUTER constructs are contiguous, which allows for
104  *   non-copied relocated (fixed-up) data structures at the packet's
105  *   destination.  We don't do fix-ups here.  The pointers within the
106  *   OUTER constructs are processed depth-first in the order that they
107  *   occur.  If they were processed breadth first, the sequence would
108  *   be #1,"foo",#2,"bar".  This is tricky because OUTER constructs may
109  *   be variable length, and pointers are often encountered before the
110  *   size(s) is known.
111  *
112  * - The INNER constructs are simply the members of an OUTER construct.
113  *
114  * For comparison, consider how ONC RPC would handle the same tree of
115  * data.  ONC requires very little buffering, while DCE requires enough
116  * buffer space for the entire message.  ONC does atom-by-atom depth-first
117  * (de)serialization and copy, while DCE allows for constructs to be
118  * "fixed-up" (relocated) in place at the destination.  The packet data
119  * for the same tree processed by ONC RPC would look like this:
120  *
121  *   +---------------------------------------------------------------------+
122  *   |level #1.next #2.next #2.str "bar" #2.flag #1.str "foo" #1.flag count|
123  *   +---------------------------------------------------------------------+
124  *   TOP    #1      #2      #2     bar   #2      #1     foo   #1      TOP
125  *
126  * More details about each TOP-MOST, OUTER, and INNER constructs appear
127  * throughout this source file near where such constructs are processed.
128  *
129  * NDR_REFERENCE
130  *
131  * The primary object for NDR is the struct ndr_reference.
132  *
133  * An ndr_reference indicates the local datum (i.e. native "C" data
134  * format), and the element within the Stub Data (contained within the
135  * RPC PDU (protocol data unit).  An ndr_reference also indicates,
136  * largely as a debugging aid, something about the type of the
137  * element/datum, and the enclosing construct for the element. The
138  * ndr_reference's are typically allocated on the stack as locals,
139  * and the chain of ndr_reference.enclosing references is in reverse
140  * order of the call graph.
141  *
142  * The ndr_reference.datum is a pointer to the local memory that
143  * contains/receives the value. The ndr_reference.pdu_offset indicates
144  * where in the Stub Data the value is to be stored/retrieved.
145  *
146  * The ndr_reference also contains various parameters to the NDR
147  * process, such as ndr_reference.size_is, which indicates the size
148  * of variable length data, or ndr_reference.switch_is, which
149  * indicates the arm of a union to use.
150  *
151  * QUEUE OF OUTER REFERENCES
152  *
153  * Some OUTER constructs are variable size.  Sometimes (often) we don't
154  * know the size of the OUTER construct until after pointers have been
155  * encountered. Hence, we can not begin processing the referent of the
156  * pointer until after the referring OUTER construct is completely
157  * processed, i.e. we don't know where to find/put the referent in the
158  * Stub Data until we know the size of all its predecessors.
159  *
160  * This is managed using the queue of OUTER references.  The queue is
161  * anchored in mlndr_stream.outer_queue_head.  At any time,
162  * mlndr_stream.outer_queue_tailp indicates where to put the
163  * ndr_reference for the next encountered pointer.
164  *
165  * Refer to the example above as we illustrate the queue here.  In these
166  * illustrations, the queue entries are not the data structures themselves.
167  * Rather, they are ndr_reference entries which **refer** to the data
168  * structures in both the PDU and local memory.
169  *
170  * During some point in the processing, the queue looks like this:
171  *
172  *   outer_current -------v
173  *   outer_queue_head --> list#1 --0
174  *   outer_queue_tailp ---------&
175  *
176  * When the pointer #1.next is encountered, and entry is added to the
177  * queue,
178  *
179  *   outer_current -------v
180  *   outer_queue_head --> list#1 --> list#2 --0
181  *   outer_queue_tailp --------------------&
182  *
183  * and the members of #1 continue to be processed, which encounters
184  * #1.str:
185  *
186  *   outer_current -------v
187  *   outer_queue_head --> list#1 --> list#2 --> "foo" --0
188  *   outer_queue_tailp ------------------------------&
189  *
190  * Upon the completion of list#1, the processing continues by moving
191  * to mlndr_stream.outer_current->next, and the tail is set to this
192  * outer member:
193  *
194  *   outer_current ------------------v
195  *   outer_queue_head --> list#1 --> list#2 --> "foo" --0
196  *   outer_queue_tailp --------------------&
197  *
198  * Space for list#2 is allocated, either in the Stub Data or of local
199  * memory.  When #2.next is encountered, it is found to be the null
200  * pointer and no reference is added to the queue.  When #2.str is
201  * encountered, it is found to be valid, and a reference is added:
202  *
203  *   outer_current ------------------v
204  *   outer_queue_head --> list#1 --> list#2 --> "bar" --> "foo" --0
205  *   outer_queue_tailp ------------------------------&
206  *
207  * Processing continues in a similar fashion with the string "bar",
208  * which is variable-length.  At this point, memory for "bar" may be
209  * malloc()ed during NDR_M_OP_UNMARSHALL:
210  *
211  *   outer_current -----------------------------v
212  *   outer_queue_head --> list#1 --> list#2 --> "bar" --> "foo" --0
213  *   outer_queue_tailp ------------------------------&
214  *
215  * And finishes on string "foo".  Notice that because "bar" is a
216  * variable length string, and we don't know the PDU offset for "foo"
217  * until we reach this point.
218  *
219  * When the queue is drained (current->next==0), processing continues
220  * with the next TOP-MOST member.
221  *
222  * The queue of OUTER constructs manages the variable-length semantics
223  * of OUTER constructs and satisfies the depth-first requirement.
224  * We allow the queue to linger until the entire TOP-MOST structure is
225  * processed as an aid to debugging.
226  */
227 
228 static struct ndr_reference *mlndr_enter_outer_queue(struct ndr_reference *);
229 extern int mlndr__ulong(struct ndr_reference *);
230 
231 /*
232  * TOP-MOST ELEMENTS
233  *
234  * This is fundamentally the first OUTER construct of the parameter,
235  * possibly followed by more OUTER constructs due to pointers.  The
236  * datum (local memory) for TOP-MOST constructs (structs) is allocated
237  * by the caller of NDR.
238  *
239  * After the element is transferred, the outer_queue is drained.
240  *
241  * All we have to do is add an entry to the outer_queue for this
242  * top-most member, and commence the outer_queue processing.
243  */
244 int
245 mlndo_process(struct mlndr_stream *mlnds, struct ndr_typeinfo *ti,
246     char *datum)
247 {
248 	struct ndr_reference	myref;
249 
250 	bzero(&myref, sizeof (myref));
251 	myref.stream = mlnds;
252 	myref.datum = datum;
253 	myref.name = "PROCESS";
254 	myref.ti = ti;
255 
256 	return (mlndr_topmost(&myref));
257 }
258 
259 int
260 mlndo_operation(struct mlndr_stream *mlnds, struct ndr_typeinfo *ti,
261     int opnum, char *datum)
262 {
263 	struct ndr_reference	myref;
264 
265 	bzero(&myref, sizeof (myref));
266 	myref.stream = mlnds;
267 	myref.datum = datum;
268 	myref.name = "OPERATION";
269 	myref.ti = ti;
270 	myref.inner_flags = NDR_F_SWITCH_IS;
271 	myref.switch_is = opnum;
272 
273 	if (ti->type_flags != NDR_F_INTERFACE) {
274 		NDR_SET_ERROR(&myref, NDR_ERR_NOT_AN_INTERFACE);
275 		return (0);
276 	}
277 
278 	return ((*ti->ndr_func)(&myref));
279 }
280 
281 int
282 mlndr_params(struct ndr_reference *params_ref)
283 {
284 	struct ndr_typeinfo *ti = params_ref->ti;
285 
286 	if (ti->type_flags == NDR_F_OPERATION)
287 		return (*ti->ndr_func) (params_ref);
288 	else
289 		return (mlndr_topmost(params_ref));
290 }
291 
292 int
293 mlndr_topmost(struct ndr_reference *top_ref)
294 {
295 	struct mlndr_stream *mlnds;
296 	struct ndr_typeinfo *ti;
297 	struct ndr_reference *outer_ref = 0;
298 	int	is_varlen;
299 	int	is_string;
300 	int	error;
301 	int	rc;
302 	unsigned n_fixed;
303 	int	params;
304 
305 	assert(top_ref);
306 	assert(top_ref->stream);
307 	assert(top_ref->ti);
308 
309 	mlnds = top_ref->stream;
310 	ti = top_ref->ti;
311 
312 	is_varlen = ti->pdu_size_variable_part;
313 	is_string = NDR_IS_STRING(ti);
314 
315 	assert(mlnds->outer_queue_tailp && !*mlnds->outer_queue_tailp);
316 	assert(!mlnds->outer_current);
317 
318 	params = top_ref->inner_flags & NDR_F_PARAMS_MASK;
319 
320 	switch (params) {
321 	case NDR_F_NONE:
322 	case NDR_F_SWITCH_IS:
323 		if (is_string || is_varlen) {
324 			error = NDR_ERR_TOPMOST_VARLEN_ILLEGAL;
325 			NDR_SET_ERROR(outer_ref, error);
326 			return (0);
327 		}
328 		n_fixed = ti->pdu_size_fixed_part;
329 		break;
330 
331 	case NDR_F_SIZE_IS:
332 		error = NDR_ERR_TOPMOST_VARLEN_ILLEGAL;
333 		NDR_SET_ERROR(outer_ref, error);
334 		return (0);
335 
336 	case NDR_F_DIMENSION_IS:
337 		if (is_varlen) {
338 			error = NDR_ERR_ARRAY_VARLEN_ILLEGAL;
339 			NDR_SET_ERROR(outer_ref, error);
340 			return (0);
341 		}
342 		n_fixed = ti->pdu_size_fixed_part * top_ref->dimension_is;
343 		break;
344 
345 	case NDR_F_IS_POINTER:
346 	case NDR_F_IS_POINTER+NDR_F_SIZE_IS:
347 		n_fixed = 4;
348 		break;
349 
350 	case NDR_F_IS_REFERENCE:
351 	case NDR_F_IS_REFERENCE+NDR_F_SIZE_IS:
352 		n_fixed = 0;
353 		break;
354 
355 	default:
356 		error = NDR_ERR_OUTER_PARAMS_BAD;
357 		NDR_SET_ERROR(outer_ref, error);
358 		return (0);
359 	}
360 
361 	outer_ref = mlndr_enter_outer_queue(top_ref);
362 	if (!outer_ref)
363 		return (0);	/* error already set */
364 
365 	/*
366 	 * Hand-craft the first OUTER construct and directly call
367 	 * mlndr_inner(). Then, run the outer_queue. We do this
368 	 * because mlndr_outer() wants to malloc() memory for
369 	 * the construct, and we already have the memory.
370 	 */
371 
372 	/* move the flags, etc, around again, undoes enter_outer_queue() */
373 	outer_ref->inner_flags = top_ref->inner_flags;
374 	outer_ref->outer_flags = 0;
375 	outer_ref->datum = top_ref->datum;
376 
377 	/* All outer constructs start on a mod4 (longword) boundary */
378 	if (!mlndr_outer_align(outer_ref))
379 		return (0);		/* error already set */
380 
381 	/* Regardless of what it is, this is where it starts */
382 	outer_ref->pdu_offset = mlnds->pdu_scan_offset;
383 
384 	rc = mlndr_outer_grow(outer_ref, n_fixed);
385 	if (!rc)
386 		return (0);		/* error already set */
387 
388 	outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_fixed;
389 
390 	/* set-up outer_current, as though run_outer_queue() was doing it */
391 	mlnds->outer_current = outer_ref;
392 	mlnds->outer_queue_tailp = &mlnds->outer_current->next;
393 	mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
394 
395 	/* do the topmost member */
396 	rc = mlndr_inner(outer_ref);
397 	if (!rc)
398 		return (0);		/* error already set */
399 
400 	mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
401 
402 	/* advance, as though run_outer_queue() was doing it */
403 	mlnds->outer_current = mlnds->outer_current->next;
404 	return (mlndr_run_outer_queue(mlnds));
405 }
406 
407 static struct ndr_reference *
408 mlndr_enter_outer_queue(struct ndr_reference *arg_ref)
409 {
410 	struct mlndr_stream	*mlnds = arg_ref->stream;
411 	struct ndr_reference	*outer_ref;
412 
413 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
414 	outer_ref = (struct ndr_reference *)
415 	    MLNDS_MALLOC(mlnds, sizeof (*outer_ref), arg_ref);
416 	if (!outer_ref) {
417 		NDR_SET_ERROR(arg_ref, NDR_ERR_MALLOC_FAILED);
418 		return (0);
419 	}
420 
421 	*outer_ref = *arg_ref;
422 
423 	/* move advice in inner_flags to outer_flags */
424 	outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
425 	outer_ref->inner_flags = 0;
426 	outer_ref->enclosing = mlnds->outer_current;
427 	outer_ref->backptr = 0;
428 	outer_ref->datum = 0;
429 
430 	assert(mlnds->outer_queue_tailp);
431 
432 	outer_ref->next = *mlnds->outer_queue_tailp;
433 	*mlnds->outer_queue_tailp = outer_ref;
434 	mlnds->outer_queue_tailp = &outer_ref->next;
435 	return (outer_ref);
436 }
437 
438 int
439 mlndr_run_outer_queue(struct mlndr_stream *mlnds)
440 {
441 	while (mlnds->outer_current) {
442 		mlnds->outer_queue_tailp = &mlnds->outer_current->next;
443 
444 		if (!mlndr_outer(mlnds->outer_current))
445 			return (0);
446 
447 		mlnds->outer_current = mlnds->outer_current->next;
448 	}
449 
450 	return (1);
451 }
452 
453 /*
454  * OUTER CONSTRUCTS
455  *
456  * OUTER constructs are where the real work is, which stems from the
457  * variable-length potential.
458  *
459  * DCE(MS)/RPC VARIABLE LENGTH -- CONFORMANT, VARYING, VARYING/CONFORMANT
460  *
461  * DCE(MS)/RPC provides for three forms of variable length: CONFORMANT,
462  * VARYING, and VARYING/CONFORMANT.
463  *
464  * What makes this so tough is that the variable-length array may be well
465  * encapsulated within the outer construct.  Further, because DCE(MS)/RPC
466  * tries to keep the constructs contiguous in the data stream, the sizing
467  * information precedes the entire OUTER construct.  The sizing information
468  * must be used at the appropriate time, which can be after many, many,
469  * many fixed-length elements.  During IDL type analysis, we know in
470  * advance constructs that encapsulate variable-length constructs.  So,
471  * we know when we have a sizing header and when we don't.  The actual
472  * semantics of the header are largely deferred.
473  *
474  * Currently, VARYING constructs are not implemented but they are described
475  * here in case they have to be implemented in the future.  Similarly,
476  * DCE(MS)/RPC provides for multi-dimensional arrays, which are currently
477  * not implemented.  Only one-dimensional, variable-length arrays are
478  * supported.
479  *
480  * CONFORMANT CONSTRUCTS -- VARIABLE LENGTH ARRAYS START THE SHOW
481  *
482  * All variable-length values are arrays.  These arrays may be embedded
483  * well within another construct.  However, a variable-length construct
484  * may ONLY appear as the last member of an enclosing construct.  Example:
485  *
486  *	struct credentials {
487  *		ulong	uid, gid;
488  *		ulong	n_gids;
489  *	    [size_is(n_gids)]
490  *		ulong	gids[*];    // variable-length.
491  *	};
492  *
493  * CONFORMANT constructs have a dynamic size in local memory and in the
494  * PDU.  The CONFORMANT quality is indicated by the [size_is()] advice.
495  * CONFORMANT constructs have the following header:
496  *
497  *	struct conformant_header {
498  *		ulong		size_is;
499  *	};
500  *
501  * (Multi-dimensional CONFORMANT arrays have a similar header for each
502  * dimension - not implemented).
503  *
504  * Example CONFORMANT construct:
505  *
506  *	struct user {
507  *		char *			name;
508  *		struct credentials	cred;	// see above
509  *	};
510  *
511  * Consider the data tree:
512  *
513  *    +--------+
514  *    |  user  |
515  *    +--------+
516  *    | name  ----> "fred" (the string is a different OUTER)
517  *    | uid    |
518  *    | gid    |
519  *    | n_gids |    for example, 3
520  *    | gids[0]|
521  *    | gids[1]|
522  *    | gids[2]|
523  *    +--------+
524  *
525  * The OUTER construct in the Stub Data would be:
526  *
527  *    +---+---------+---------------------------------------------+
528  *    |pad|size_is=3 name uid gid n_gids=3 gids[0] gids[1] gids[2]|
529  *    +---+---------+---------------------------------------------+
530  *         szing hdr|user |<-------------- user.cred ------------>|
531  *                  |<--- fixed-size ---->|<----- conformant ---->|
532  *
533  * The ndr_typeinfo for struct user will have:
534  *	pdu_fixed_size_part = 16	four long words (name uid gid n_gids)
535  *	pdu_variable_size_part = 4	per element, sizeof gids[0]
536  *
537  * VARYING CONSTRUCTS -- NOT IMPLEMENTED
538  *
539  * VARYING constructs have the following header:
540  *
541  *	struct varying_header {
542  *		ulong		first_is;
543  *		ulong		length_is;
544  *	};
545  *
546  * This indicates which interval of an array is significant.
547  * Non-intersecting elements of the array are undefined and usually
548  * zero-filled.  The first_is parameter for C arrays is always 0 for
549  * the first element.
550  *
551  * N.B. Constructs may contain one CONFORMANT element, which is always
552  * last, but may contain many VARYING elements, which can be anywhere.
553  *
554  * VARYING CONFORMANT constructs have the sizing headers arranged like
555  * this:
556  *
557  *	struct conformant_header	all_conformant[N_CONFORMANT_DIM];
558  *	struct varying_header		all_varying[N_VARYING_ELEMS_AND_DIMS];
559  *
560  * The sizing header is immediately followed by the values for the
561  * construct.  Again, we don't support more than one dimension and
562  * we don't support VARYING constructs at this time.
563  *
564  * A good example of a VARYING/CONFORMANT data structure is the UNIX
565  * directory entry:
566  *
567  *	struct dirent {
568  *		ushort		reclen;
569  *		ushort		namlen;
570  *		ulong		inum;
571  *	    [size_is(reclen-8) length_is(namlen+1)] // -(2+2+4), +1 for NUL
572  *		uchar		name[*];
573  *	};
574  *
575  *
576  * STRINGS ARE A SPECIAL CASE
577  *
578  * Strings are handled specially.  MS/RPC uses VARYING/CONFORMANT structures
579  * for strings.  This is a simple one-dimensional variable-length array,
580  * typically with its last element all zeroes.  We handle strings with the
581  * header:
582  *
583  *	struct string_header {
584  *		ulong		size_is;
585  *		ulong		first_is;	// always 0
586  *		ulong		length_is;	// always same as size_is
587  *	};
588  *
589  * If general support for VARYING and VARYING/CONFORMANT mechanisms is
590  * implemented, we probably won't need the strings special case.
591  */
592 int
593 mlndr_outer(struct ndr_reference *outer_ref)
594 {
595 	struct mlndr_stream 	*mlnds = outer_ref->stream;
596 	struct ndr_typeinfo 	*ti = outer_ref->ti;
597 	int	is_varlen = ti->pdu_size_variable_part;
598 	int	is_union = NDR_IS_UNION(ti);
599 	int	is_string = NDR_IS_STRING(ti);
600 	int	error = NDR_ERR_OUTER_PARAMS_BAD;
601 	int	params;
602 
603 	params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
604 
605 	NDR_TATTLE(outer_ref, "--OUTER--");
606 
607 	/* All outer constructs start on a mod4 (longword) boundary */
608 	if (!mlndr_outer_align(outer_ref))
609 		return (0);		/* error already set */
610 
611 	/* Regardless of what it is, this is where it starts */
612 	outer_ref->pdu_offset = mlnds->pdu_scan_offset;
613 
614 	if (is_union) {
615 		error = NDR_ERR_OUTER_UNION_ILLEGAL;
616 		NDR_SET_ERROR(outer_ref, error);
617 		return (0);
618 	}
619 
620 	switch (params) {
621 	case NDR_F_NONE:
622 		if (is_string)
623 			return (mlndr_outer_string(outer_ref));
624 		if (is_varlen)
625 			return (mlndr_outer_conformant_construct(outer_ref));
626 
627 		return (mlndr_outer_fixed(outer_ref));
628 		break;
629 
630 	case NDR_F_SIZE_IS:
631 	case NDR_F_DIMENSION_IS:
632 		if (is_varlen) {
633 			error = NDR_ERR_ARRAY_VARLEN_ILLEGAL;
634 			break;
635 		}
636 
637 		if (params == NDR_F_SIZE_IS)
638 			return (mlndr_outer_conformant_array(outer_ref));
639 		else
640 			return (mlndr_outer_fixed_array(outer_ref));
641 		break;
642 
643 	default:
644 		error = NDR_ERR_OUTER_PARAMS_BAD;
645 		break;
646 	}
647 
648 	/*
649 	 * If we get here, something is wrong. Most likely,
650 	 * the params flags do not match.
651 	 */
652 	NDR_SET_ERROR(outer_ref, error);
653 	return (0);
654 }
655 
656 int
657 mlndr_outer_fixed(struct ndr_reference *outer_ref)
658 {
659 	struct mlndr_stream 	*mlnds = outer_ref->stream;
660 	struct ndr_typeinfo 	*ti = outer_ref->ti;
661 	struct ndr_reference	myref;
662 	char 		*valp = NULL;
663 	int		is_varlen = ti->pdu_size_variable_part;
664 	int		is_union = NDR_IS_UNION(ti);
665 	int		is_string = NDR_IS_STRING(ti);
666 	int		rc;
667 	unsigned	n_hdr;
668 	unsigned	n_fixed;
669 	unsigned	n_variable;
670 	unsigned	n_alloc;
671 	unsigned	n_pdu_total;
672 	int		params;
673 
674 	params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
675 
676 	assert(!is_varlen && !is_string && !is_union);
677 	assert(params == NDR_F_NONE);
678 
679 	/* no header for this */
680 	n_hdr = 0;
681 
682 	/* fixed part -- exactly one of these */
683 	n_fixed = ti->pdu_size_fixed_part;
684 	assert(n_fixed > 0);
685 
686 	/* variable part -- exactly none of these */
687 	n_variable = 0;
688 
689 	/* sum them up to determine the PDU space required */
690 	n_pdu_total = n_hdr + n_fixed + n_variable;
691 
692 	/* similar sum to determine how much local memory is required */
693 	n_alloc = n_fixed + n_variable;
694 
695 	rc = mlndr_outer_grow(outer_ref, n_pdu_total);
696 	if (!rc)
697 		return (rc);		/* error already set */
698 
699 	switch (mlnds->m_op) {
700 	case NDR_M_OP_MARSHALL:
701 		valp = outer_ref->datum;
702 		assert(valp);
703 		if (outer_ref->backptr) {
704 			assert(valp == *outer_ref->backptr);
705 		}
706 		break;
707 
708 	case NDR_M_OP_UNMARSHALL:
709 		valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref);
710 		if (!valp) {
711 			NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
712 			return (0);
713 		}
714 		if (outer_ref->backptr)
715 			*outer_ref->backptr = valp;
716 		outer_ref->datum = valp;
717 		break;
718 
719 	default:
720 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
721 		return (0);
722 	}
723 
724 	bzero(&myref, sizeof (myref));
725 	myref.stream = mlnds;
726 	myref.enclosing = outer_ref;
727 	myref.ti = outer_ref->ti;
728 	myref.datum = outer_ref->datum;
729 	myref.name = "FIXED-VALUE";
730 	myref.outer_flags = NDR_F_NONE;
731 	myref.inner_flags = NDR_F_NONE;
732 
733 	myref.pdu_offset = outer_ref->pdu_offset;
734 	outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
735 
736 	rc = mlndr_inner(&myref);
737 	if (!rc)
738 		return (rc);		/* error already set */
739 
740 	mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
741 	return (1);
742 }
743 
744 int
745 mlndr_outer_fixed_array(struct ndr_reference *outer_ref)
746 {
747 	struct mlndr_stream 	*mlnds = outer_ref->stream;
748 	struct ndr_typeinfo 	*ti = outer_ref->ti;
749 	struct ndr_reference	myref;
750 	char 		*valp = NULL;
751 	int		is_varlen = ti->pdu_size_variable_part;
752 	int		is_union = NDR_IS_UNION(ti);
753 	int		is_string = NDR_IS_STRING(ti);
754 	int		rc;
755 	unsigned	n_hdr;
756 	unsigned	n_fixed;
757 	unsigned	n_variable;
758 	unsigned	n_alloc;
759 	unsigned	n_pdu_total;
760 	int		params;
761 
762 	params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
763 
764 	assert(!is_varlen && !is_string && !is_union);
765 	assert(params == NDR_F_DIMENSION_IS);
766 
767 	/* no header for this */
768 	n_hdr = 0;
769 
770 	/* fixed part -- exactly dimension_is of these */
771 	n_fixed = ti->pdu_size_fixed_part * outer_ref->dimension_is;
772 	assert(n_fixed > 0);
773 
774 	/* variable part -- exactly none of these */
775 	n_variable = 0;
776 
777 	/* sum them up to determine the PDU space required */
778 	n_pdu_total = n_hdr + n_fixed + n_variable;
779 
780 	/* similar sum to determine how much local memory is required */
781 	n_alloc = n_fixed + n_variable;
782 
783 	rc = mlndr_outer_grow(outer_ref, n_pdu_total);
784 	if (!rc)
785 		return (rc);		/* error already set */
786 
787 	switch (mlnds->m_op) {
788 	case NDR_M_OP_MARSHALL:
789 		valp = outer_ref->datum;
790 		assert(valp);
791 		if (outer_ref->backptr) {
792 			assert(valp == *outer_ref->backptr);
793 		}
794 		break;
795 
796 	case NDR_M_OP_UNMARSHALL:
797 		valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref);
798 		if (!valp) {
799 			NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
800 			return (0);
801 		}
802 		if (outer_ref->backptr)
803 			*outer_ref->backptr = valp;
804 		outer_ref->datum = valp;
805 		break;
806 
807 	default:
808 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
809 		return (0);
810 	}
811 
812 	bzero(&myref, sizeof (myref));
813 	myref.stream = mlnds;
814 	myref.enclosing = outer_ref;
815 	myref.ti = outer_ref->ti;
816 	myref.datum = outer_ref->datum;
817 	myref.name = "FIXED-ARRAY";
818 	myref.outer_flags = NDR_F_NONE;
819 	myref.inner_flags = NDR_F_DIMENSION_IS;
820 	myref.dimension_is = outer_ref->dimension_is;
821 
822 	myref.pdu_offset = outer_ref->pdu_offset;
823 	outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
824 
825 	rc = mlndr_inner(&myref);
826 	if (!rc)
827 		return (rc);		/* error already set */
828 
829 	mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
830 	return (1);
831 }
832 
833 int
834 mlndr_outer_conformant_array(struct ndr_reference *outer_ref)
835 {
836 	struct mlndr_stream 	*mlnds = outer_ref->stream;
837 	struct ndr_typeinfo 	*ti = outer_ref->ti;
838 	struct ndr_reference	myref;
839 	char 		*valp = NULL;
840 	int		is_varlen = ti->pdu_size_variable_part;
841 	int		is_union = NDR_IS_UNION(ti);
842 	int		is_string = NDR_IS_STRING(ti);
843 	unsigned long	size_is;
844 	int		rc;
845 	unsigned	n_hdr;
846 	unsigned	n_fixed;
847 	unsigned	n_variable;
848 	unsigned	n_alloc;
849 	unsigned	n_pdu_total;
850 	int		params;
851 
852 	params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
853 
854 	assert(!is_varlen && !is_string && !is_union);
855 	assert(params == NDR_F_SIZE_IS);
856 
857 	/* conformant header for this */
858 	n_hdr = 4;
859 
860 	/* fixed part -- exactly none of these */
861 	n_fixed = 0;
862 
863 	/* variable part -- exactly size_of of these */
864 	/* notice that it is the **fixed** size of the ti */
865 	n_variable = ti->pdu_size_fixed_part * outer_ref->size_is;
866 
867 	/* sum them up to determine the PDU space required */
868 	n_pdu_total = n_hdr + n_fixed + n_variable;
869 
870 	/* similar sum to determine how much local memory is required */
871 	n_alloc = n_fixed + n_variable;
872 
873 	rc = mlndr_outer_grow(outer_ref, n_pdu_total);
874 	if (!rc)
875 		return (rc);		/* error already set */
876 
877 	switch (mlnds->m_op) {
878 	case NDR_M_OP_MARSHALL:
879 		size_is = outer_ref->size_is;
880 		rc = mlndr_outer_poke_sizing(outer_ref, 0, &size_is);
881 		if (!rc)
882 			return (0);	/* error already set */
883 
884 		valp = outer_ref->datum;
885 		assert(valp);
886 		if (outer_ref->backptr) {
887 			assert(valp == *outer_ref->backptr);
888 		}
889 		break;
890 
891 	case NDR_M_OP_UNMARSHALL:
892 		rc = mlndr_outer_peek_sizing(outer_ref, 0, &size_is);
893 		if (!rc)
894 			return (0);	/* error already set */
895 
896 		if (size_is != outer_ref->size_is) {
897 			NDR_SET_ERROR(outer_ref,
898 			    NDR_ERR_SIZE_IS_MISMATCH_PDU);
899 			return (0);
900 		}
901 
902 		if (size_is > 0) {
903 			valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref);
904 			if (!valp) {
905 				NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
906 				return (0);
907 			}
908 		}
909 
910 		if (outer_ref->backptr)
911 			*outer_ref->backptr = valp;
912 		outer_ref->datum = valp;
913 		break;
914 
915 	default:
916 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
917 		return (0);
918 	}
919 
920 	outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
921 	outer_ref->type_flags = NDR_F_NONE;
922 	outer_ref->inner_flags = NDR_F_NONE;
923 
924 	if (size_is > 0) {
925 		bzero(&myref, sizeof (myref));
926 		myref.stream = mlnds;
927 		myref.enclosing = outer_ref;
928 		myref.ti = outer_ref->ti;
929 		myref.datum = outer_ref->datum;
930 		myref.name = "CONFORMANT-ARRAY";
931 		myref.outer_flags = NDR_F_NONE;
932 		myref.inner_flags = NDR_F_SIZE_IS;
933 		myref.size_is = outer_ref->size_is;
934 
935 		myref.inner_flags = NDR_F_DIMENSION_IS;		/* convenient */
936 		myref.dimension_is = outer_ref->size_is;	/* convenient */
937 
938 		myref.pdu_offset = outer_ref->pdu_offset + 4;
939 
940 		rc = mlndr_inner(&myref);
941 		if (!rc)
942 			return (rc);		/* error already set */
943 	}
944 
945 	mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
946 	return (1);
947 }
948 
949 int
950 mlndr_outer_conformant_construct(struct ndr_reference *outer_ref)
951 {
952 	struct mlndr_stream 	*mlnds = outer_ref->stream;
953 	struct ndr_typeinfo 	*ti = outer_ref->ti;
954 	struct ndr_reference	myref;
955 	char 		*valp = NULL;
956 	int		is_varlen = ti->pdu_size_variable_part;
957 	int		is_union = NDR_IS_UNION(ti);
958 	int		is_string = NDR_IS_STRING(ti);
959 	unsigned long	size_is;
960 	int		rc;
961 	unsigned	n_hdr;
962 	unsigned	n_fixed;
963 	unsigned	n_variable;
964 	unsigned	n_alloc;
965 	unsigned	n_pdu_total;
966 	int		params;
967 
968 	params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
969 
970 	assert(is_varlen && !is_string && !is_union);
971 	assert(params == NDR_F_NONE);
972 
973 	/* conformant header for this */
974 	n_hdr = 4;
975 
976 	/* fixed part -- exactly one of these */
977 	n_fixed = ti->pdu_size_fixed_part;
978 
979 	/* variable part -- exactly size_of of these */
980 	n_variable = 0;		/* 0 for the moment */
981 
982 	/* sum them up to determine the PDU space required */
983 	n_pdu_total = n_hdr + n_fixed + n_variable;
984 
985 	/* similar sum to determine how much local memory is required */
986 	n_alloc = n_fixed + n_variable;
987 
988 	/* For the moment, grow enough for the fixed-size part */
989 	rc = mlndr_outer_grow(outer_ref, n_pdu_total);
990 	if (!rc)
991 		return (rc);		/* error already set */
992 
993 	switch (mlnds->m_op) {
994 	case NDR_M_OP_MARSHALL:
995 		/*
996 		 * We don't know the size yet. We have to wait for
997 		 * it. Proceed with the fixed-size part, and await
998 		 * the call to mlndr_size_is().
999 		 */
1000 		size_is = 0;
1001 		rc = mlndr_outer_poke_sizing(outer_ref, 0, &size_is);
1002 		if (!rc)
1003 			return (0);	/* error already set */
1004 
1005 		valp = outer_ref->datum;
1006 		assert(valp);
1007 		if (outer_ref->backptr) {
1008 			assert(valp == *outer_ref->backptr);
1009 		}
1010 		break;
1011 
1012 	case NDR_M_OP_UNMARSHALL:
1013 		/*
1014 		 * We know the size of the variable part because
1015 		 * of the CONFORMANT header. We will verify
1016 		 * the header against the [size_is(X)] advice
1017 		 * later when mlndr_size_is() is called.
1018 		 */
1019 		rc = mlndr_outer_peek_sizing(outer_ref, 0, &size_is);
1020 		if (!rc)
1021 			return (0);	/* error already set */
1022 
1023 		/* recalculate metrics */
1024 		n_variable = size_is * ti->pdu_size_variable_part;
1025 		n_pdu_total = n_hdr + n_fixed + n_variable;
1026 		n_alloc = n_fixed + n_variable;
1027 
1028 		rc = mlndr_outer_grow(outer_ref, n_pdu_total);
1029 		if (!rc)
1030 			return (rc);		/* error already set */
1031 
1032 		outer_ref->size_is = size_is; /* verified later */
1033 
1034 		valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref);
1035 		if (!valp) {
1036 			NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
1037 			return (0);
1038 		}
1039 		if (outer_ref->backptr)
1040 			*outer_ref->backptr = valp;
1041 		outer_ref->datum = valp;
1042 		break;
1043 
1044 	default:
1045 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1046 		return (0);
1047 	}
1048 
1049 	outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
1050 	outer_ref->type_flags = NDR_F_SIZE_IS; /* indicate pending */
1051 	outer_ref->inner_flags = NDR_F_NONE;   /* indicate pending */
1052 
1053 	bzero(&myref, sizeof (myref));
1054 	myref.stream = mlnds;
1055 	myref.enclosing = outer_ref;
1056 	myref.ti = outer_ref->ti;
1057 	myref.datum = outer_ref->datum;
1058 	myref.name = "CONFORMANT-CONSTRUCT";
1059 	myref.outer_flags = NDR_F_NONE;
1060 	myref.inner_flags = NDR_F_NONE;
1061 	myref.size_is = outer_ref->size_is;
1062 
1063 	myref.pdu_offset = outer_ref->pdu_offset + 4;
1064 
1065 	rc = mlndr_inner(&myref);
1066 	if (!rc)
1067 		return (rc);		/* error already set */
1068 
1069 	mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
1070 
1071 	if (outer_ref->inner_flags != NDR_F_SIZE_IS) {
1072 		NDR_SET_ERROR(&myref, NDR_ERR_SIZE_IS_MISMATCH_AFTER);
1073 		return (0);
1074 	}
1075 
1076 	return (1);
1077 }
1078 
1079 int
1080 mlndr_size_is(struct ndr_reference *ref)
1081 {
1082 	struct mlndr_stream 	*mlnds = ref->stream;
1083 	struct ndr_reference 	*outer_ref = mlnds->outer_current;
1084 	struct ndr_typeinfo 	*ti = outer_ref->ti;
1085 	unsigned long		size_is;
1086 	int			rc;
1087 	unsigned		n_hdr;
1088 	unsigned		n_fixed;
1089 	unsigned		n_variable;
1090 	unsigned		n_pdu_total;
1091 
1092 	assert(ref->inner_flags & NDR_F_SIZE_IS);
1093 	size_is = ref->size_is;
1094 
1095 	if (outer_ref->type_flags != NDR_F_SIZE_IS) {
1096 		NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_UNEXPECTED);
1097 		return (0);
1098 	}
1099 
1100 	if (outer_ref->inner_flags & NDR_F_SIZE_IS) {
1101 		NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_DUPLICATED);
1102 		return (0);
1103 	}
1104 
1105 	/* repeat metrics, see mlndr_conformant_construct() above */
1106 	n_hdr = 4;
1107 	n_fixed = ti->pdu_size_fixed_part;
1108 	n_variable = size_is * ti->pdu_size_variable_part;
1109 	n_pdu_total = n_hdr + n_fixed + n_variable;
1110 
1111 	rc = mlndr_outer_grow(outer_ref, n_pdu_total);
1112 	if (!rc)
1113 		return (rc);		/* error already set */
1114 
1115 	switch (mlnds->m_op) {
1116 	case NDR_M_OP_MARSHALL:
1117 		/*
1118 		 * We have to set the sizing header and extend
1119 		 * the size of the PDU (already done).
1120 		 */
1121 		rc = mlndr_outer_poke_sizing(outer_ref, 0, &size_is);
1122 		if (!rc)
1123 			return (0);	/* error already set */
1124 		break;
1125 
1126 	case NDR_M_OP_UNMARSHALL:
1127 		/*
1128 		 * Allocation done during mlndr_conformant_construct().
1129 		 * All we are doing here is verifying that the
1130 		 * intended size (ref->size_is) matches the sizing header.
1131 		 */
1132 		if (size_is != outer_ref->size_is) {
1133 			NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_MISMATCH_PDU);
1134 			return (0);
1135 		}
1136 		break;
1137 
1138 	default:
1139 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1140 		return (0);
1141 	}
1142 
1143 	outer_ref->inner_flags |= NDR_F_SIZE_IS;
1144 	outer_ref->size_is = ref->size_is;
1145 	return (1);
1146 }
1147 
1148 int
1149 mlndr_outer_string(struct ndr_reference *outer_ref)
1150 {
1151 	struct mlndr_stream 	*mlnds = outer_ref->stream;
1152 	struct ndr_typeinfo 	*ti = outer_ref->ti;
1153 	struct ndr_reference	myref;
1154 	char 		*valp = NULL;
1155 	unsigned	is_varlen = ti->pdu_size_variable_part;
1156 	int		is_union = NDR_IS_UNION(ti);
1157 	int		is_string = NDR_IS_STRING(ti);
1158 	int		rc;
1159 	unsigned	n_zeroes;
1160 	unsigned	ix;
1161 	unsigned long	size_is;
1162 	unsigned long	first_is;
1163 	unsigned long	length_is;
1164 	unsigned	n_hdr;
1165 	unsigned	n_fixed;
1166 	unsigned	n_variable;
1167 	unsigned	n_alloc;
1168 	unsigned	n_pdu_total;
1169 	int		params;
1170 
1171 	params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
1172 
1173 	assert(is_varlen && is_string && !is_union);
1174 	assert(params == NDR_F_NONE);
1175 
1176 	/* string header for this: size_is first_is length_is */
1177 	n_hdr = 12;
1178 
1179 	/* fixed part -- exactly none of these */
1180 	n_fixed = 0;
1181 
1182 	if (!mlndr_outer_grow(outer_ref, n_hdr))
1183 		return (0);		/* error already set */
1184 
1185 	switch (mlnds->m_op) {
1186 	case NDR_M_OP_MARSHALL:
1187 		valp = outer_ref->datum;
1188 		assert(valp);
1189 
1190 		if (outer_ref->backptr)
1191 			assert(valp == *outer_ref->backptr);
1192 
1193 		if (ti == &ndt_s_wchar) {
1194 			/*
1195 			 * size_is is the number of characters in the string,
1196 			 * including the null. We assume valp is UTF-8 encoded.
1197 			 * We can use mts_wcequiv_strlen for ASCII, extended
1198 			 * ASCII or Unicode (UCS-2).
1199 			 */
1200 			size_is = (mts_wcequiv_strlen(valp) /
1201 			    sizeof (mts_wchar_t)) + 1;
1202 
1203 			if (size_is > NDR_STRING_MAX) {
1204 				NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN);
1205 				return (0);
1206 			}
1207 		} else {
1208 			valp = outer_ref->datum;
1209 			n_zeroes = 0;
1210 			for (ix = 0; ix < 1024; ix++) {
1211 				if (valp[ix] == 0) {
1212 					n_zeroes++;
1213 					if (n_zeroes >= is_varlen &&
1214 					    ix % is_varlen == 0) {
1215 						break;
1216 					}
1217 				} else {
1218 					n_zeroes = 0;
1219 				}
1220 			}
1221 			if (ix >= 1024) {
1222 				NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN);
1223 				return (0);
1224 			}
1225 			size_is = ix+1;
1226 		}
1227 
1228 		first_is = 0;
1229 
1230 		if (mlnds->flags & MLNDS_F_NOTERM)
1231 			length_is = size_is - 1;
1232 		else
1233 			length_is = size_is;
1234 
1235 		if (!mlndr_outer_poke_sizing(outer_ref, 0, &size_is) ||
1236 		    !mlndr_outer_poke_sizing(outer_ref, 4, &first_is) ||
1237 		    !mlndr_outer_poke_sizing(outer_ref, 8, &length_is))
1238 			return (0);		/* error already set */
1239 		break;
1240 
1241 	case NDR_M_OP_UNMARSHALL:
1242 		if (!mlndr_outer_peek_sizing(outer_ref, 0, &size_is) ||
1243 		    !mlndr_outer_peek_sizing(outer_ref, 4, &first_is) ||
1244 		    !mlndr_outer_peek_sizing(outer_ref, 8, &length_is))
1245 			return (0);		/* error already set */
1246 
1247 		/*
1248 		 * In addition to the first_is check, we used to check that
1249 		 * size_is or size_is-1 was equal to length_is but Windows95
1250 		 * doesn't conform to this "rule" (see variable part below).
1251 		 * The srvmgr tool for Windows95 sent the following values
1252 		 * for a path string:
1253 		 *
1254 		 *	size_is   = 261 (0x105)
1255 		 *	first_is  = 0
1256 		 *	length_is = 53  (0x35)
1257 		 *
1258 		 * The length_is was correct (for the given path) but the
1259 		 * size_is was the maximum path length rather than being
1260 		 * related to length_is.
1261 		 */
1262 		if (first_is != 0) {
1263 			NDR_SET_ERROR(outer_ref, NDR_ERR_STRING_SIZING);
1264 			return (0);
1265 		}
1266 
1267 		if (ti == &ndt_s_wchar) {
1268 			/*
1269 			 * Decoding Unicode to UTF-8; we need to allow
1270 			 * for the maximum possible char size. It would
1271 			 * be nice to use mbequiv_strlen but the string
1272 			 * may not be null terminated.
1273 			 */
1274 			n_alloc = (size_is + 1) * MTS_MB_CHAR_MAX;
1275 		} else {
1276 			n_alloc = (size_is + 1) * is_varlen;
1277 		}
1278 
1279 		valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref);
1280 		if (!valp) {
1281 			NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
1282 			return (0);
1283 		}
1284 
1285 		bzero(valp, (size_is+1) * is_varlen);
1286 
1287 		if (outer_ref->backptr)
1288 			*outer_ref->backptr = valp;
1289 		outer_ref->datum = valp;
1290 		break;
1291 
1292 	default:
1293 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1294 		return (0);
1295 	}
1296 
1297 	/*
1298 	 * Variable part - exactly length_is of these.
1299 	 *
1300 	 * Usually, length_is is same as size_is and includes nul.
1301 	 * Some protocols use length_is = size_is-1, and length_is does
1302 	 * not include the nul (which is more consistent with DCE spec).
1303 	 * If the length_is is 0, there is no data following the
1304 	 * sizing header, regardless of size_is.
1305 	 */
1306 	n_variable = length_is * is_varlen;
1307 
1308 	/* sum them up to determine the PDU space required */
1309 	n_pdu_total = n_hdr + n_fixed + n_variable;
1310 
1311 	/* similar sum to determine how much local memory is required */
1312 	n_alloc = n_fixed + n_variable;
1313 
1314 	rc = mlndr_outer_grow(outer_ref, n_pdu_total);
1315 	if (!rc)
1316 		return (rc);		/* error already set */
1317 
1318 	if (length_is > 0) {
1319 		bzero(&myref, sizeof (myref));
1320 		myref.stream = mlnds;
1321 		myref.enclosing = outer_ref;
1322 		myref.ti = outer_ref->ti;
1323 		myref.datum = outer_ref->datum;
1324 		myref.name = "OUTER-STRING";
1325 		myref.outer_flags = NDR_F_IS_STRING;
1326 		myref.inner_flags = NDR_F_NONE;
1327 
1328 		/*
1329 		 * Set up size_is and strlen_is for mlndr_s_wchar.
1330 		 */
1331 		myref.size_is = size_is;
1332 		myref.strlen_is = length_is;
1333 	}
1334 
1335 	myref.pdu_offset = outer_ref->pdu_offset + 12;
1336 
1337 	/*
1338 	 * Don't try to decode empty strings.
1339 	 */
1340 	if ((size_is == 0) && (first_is == 0) && (length_is == 0)) {
1341 		mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
1342 		return (1);
1343 	}
1344 
1345 	if ((size_is != 0) && (length_is != 0)) {
1346 		rc = mlndr_inner(&myref);
1347 		if (!rc)
1348 			return (rc);		/* error already set */
1349 	}
1350 
1351 	mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
1352 	return (1);
1353 }
1354 
1355 int
1356 mlndr_outer_peek_sizing(struct ndr_reference *outer_ref, unsigned offset,
1357     unsigned long *sizing_p)
1358 {
1359 	struct mlndr_stream 	*mlnds = outer_ref->stream;
1360 	unsigned long		pdu_offset;
1361 	int			rc;
1362 
1363 	pdu_offset = outer_ref->pdu_offset + offset;
1364 
1365 	if (pdu_offset < mlnds->outer_current->pdu_offset ||
1366 	    pdu_offset > mlnds->outer_current->pdu_end_offset ||
1367 	    pdu_offset+4 > mlnds->outer_current->pdu_end_offset) {
1368 		NDR_SET_ERROR(outer_ref, NDR_ERR_BOUNDS_CHECK);
1369 		return (0);
1370 	}
1371 
1372 	switch (mlnds->m_op) {
1373 	case NDR_M_OP_MARSHALL:
1374 		NDR_SET_ERROR(outer_ref, NDR_ERR_UNIMPLEMENTED);
1375 		return (0);
1376 
1377 	case NDR_M_OP_UNMARSHALL:
1378 		rc = MLNDS_GET_PDU(mlnds, pdu_offset, 4, (char *)sizing_p,
1379 		    mlnds->swap, outer_ref);
1380 		break;
1381 
1382 	default:
1383 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1384 		return (0);
1385 	}
1386 
1387 	return (rc);
1388 }
1389 
1390 int
1391 mlndr_outer_poke_sizing(struct ndr_reference *outer_ref, unsigned offset,
1392     unsigned long *sizing_p)
1393 {
1394 	struct mlndr_stream 	*mlnds = outer_ref->stream;
1395 	unsigned long		pdu_offset;
1396 	int			rc;
1397 
1398 	pdu_offset = outer_ref->pdu_offset + offset;
1399 
1400 	if (pdu_offset < mlnds->outer_current->pdu_offset ||
1401 	    pdu_offset > mlnds->outer_current->pdu_end_offset ||
1402 	    pdu_offset+4 > mlnds->outer_current->pdu_end_offset) {
1403 		NDR_SET_ERROR(outer_ref, NDR_ERR_BOUNDS_CHECK);
1404 		return (0);
1405 	}
1406 
1407 	switch (mlnds->m_op) {
1408 	case NDR_M_OP_MARSHALL:
1409 		rc = MLNDS_PUT_PDU(mlnds, pdu_offset, 4, (char *)sizing_p,
1410 		    mlnds->swap, outer_ref);
1411 		break;
1412 
1413 	case NDR_M_OP_UNMARSHALL:
1414 		NDR_SET_ERROR(outer_ref, NDR_ERR_UNIMPLEMENTED);
1415 		return (0);
1416 
1417 	default:
1418 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1419 		return (0);
1420 	}
1421 
1422 	return (rc);
1423 }
1424 
1425 /*
1426  * All OUTER constructs begin on a mod4 (dword) boundary - except
1427  * for the ones that don't: some MSRPC calls appear to use word or
1428  * packed alignment.  Strings appear to be dword aligned.
1429  */
1430 int
1431 mlndr_outer_align(struct ndr_reference *outer_ref)
1432 {
1433 	struct mlndr_stream 	*mlnds = outer_ref->stream;
1434 	int			rc;
1435 	unsigned		n_pad;
1436 	unsigned		align;
1437 
1438 	if (outer_ref->packed_alignment && outer_ref->ti != &ndt_s_wchar) {
1439 		align = outer_ref->ti->alignment;
1440 		n_pad = ((align + 1) - mlnds->pdu_scan_offset) & align;
1441 	} else {
1442 		n_pad = (4 - mlnds->pdu_scan_offset) & 3;
1443 	}
1444 
1445 	if (n_pad == 0)
1446 		return (1);	/* already aligned, often the case */
1447 
1448 	if (!mlndr_outer_grow(outer_ref, n_pad))
1449 		return (0);	/* error already set */
1450 
1451 	switch (mlnds->m_op) {
1452 	case NDR_M_OP_MARSHALL:
1453 		rc = MLNDS_PAD_PDU(mlnds,
1454 		    mlnds->pdu_scan_offset, n_pad, outer_ref);
1455 		if (!rc) {
1456 			NDR_SET_ERROR(outer_ref, NDR_ERR_PAD_FAILED);
1457 			return (0);
1458 		}
1459 		break;
1460 
1461 	case NDR_M_OP_UNMARSHALL:
1462 		break;
1463 
1464 	default:
1465 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1466 		return (0);
1467 	}
1468 
1469 	mlnds->pdu_scan_offset += n_pad;
1470 	return (1);
1471 }
1472 
1473 int
1474 mlndr_outer_grow(struct ndr_reference *outer_ref, unsigned n_total)
1475 {
1476 	struct mlndr_stream 	*mlnds = outer_ref->stream;
1477 	unsigned long		pdu_want_size;
1478 	int			rc, is_ok = 0;
1479 
1480 	pdu_want_size = mlnds->pdu_scan_offset + n_total;
1481 
1482 	if (pdu_want_size <= mlnds->pdu_max_size) {
1483 		is_ok = 1;
1484 	}
1485 
1486 	switch (mlnds->m_op) {
1487 	case NDR_M_OP_MARSHALL:
1488 		if (is_ok)
1489 			break;
1490 		rc = MLNDS_GROW_PDU(mlnds, pdu_want_size, outer_ref);
1491 		if (!rc) {
1492 			NDR_SET_ERROR(outer_ref, NDR_ERR_GROW_FAILED);
1493 			return (0);
1494 		}
1495 		break;
1496 
1497 	case NDR_M_OP_UNMARSHALL:
1498 		if (is_ok)
1499 			break;
1500 		NDR_SET_ERROR(outer_ref, NDR_ERR_UNDERFLOW);
1501 		return (0);
1502 
1503 	default:
1504 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1505 		return (0);
1506 	}
1507 
1508 	if (mlnds->pdu_size < pdu_want_size)
1509 		mlnds->pdu_size = pdu_want_size;
1510 
1511 	outer_ref->pdu_end_offset = pdu_want_size;
1512 	return (1);
1513 }
1514 
1515 /*
1516  * INNER ELEMENTS
1517  *
1518  * The local datum (arg_ref->datum) already exists, there is no need to
1519  * malloc() it.  The datum should point at a member of a structure.
1520  *
1521  * For the most part, mlndr_inner() and its helpers are just a sanity
1522  * check.  The underlying ti->ndr_func() could be called immediately
1523  * for non-pointer elements.  For the sake of robustness, we detect
1524  * run-time errors here.  Most of the situations this protects against
1525  * have already been checked by the IDL compiler.  This is also a
1526  * common point for processing of all data, and so is a convenient
1527  * place to work from for debugging.
1528  */
1529 int
1530 mlndr_inner(struct ndr_reference *arg_ref)
1531 {
1532 	struct ndr_typeinfo 	*ti = arg_ref->ti;
1533 	int	is_varlen = ti->pdu_size_variable_part;
1534 	int	is_union = NDR_IS_UNION(ti);
1535 	int	error = NDR_ERR_INNER_PARAMS_BAD;
1536 	int	params;
1537 
1538 	params = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
1539 
1540 	switch (params) {
1541 	case NDR_F_NONE:
1542 		if (is_union) {
1543 			error = NDR_ERR_SWITCH_VALUE_MISSING;
1544 			break;
1545 		}
1546 		return (*ti->ndr_func)(arg_ref);
1547 		break;
1548 
1549 	case NDR_F_SIZE_IS:
1550 	case NDR_F_DIMENSION_IS:
1551 	case NDR_F_IS_POINTER+NDR_F_SIZE_IS:   /* pointer to something */
1552 	case NDR_F_IS_REFERENCE+NDR_F_SIZE_IS: /* pointer to something */
1553 		if (is_varlen) {
1554 			error = NDR_ERR_ARRAY_VARLEN_ILLEGAL;
1555 			break;
1556 		}
1557 		if (is_union) {
1558 			error = NDR_ERR_ARRAY_UNION_ILLEGAL;
1559 			break;
1560 		}
1561 		if (params & NDR_F_IS_POINTER)
1562 			return (mlndr_inner_pointer(arg_ref));
1563 		else if (params & NDR_F_IS_REFERENCE)
1564 			return (mlndr_inner_reference(arg_ref));
1565 		else
1566 			return (mlndr_inner_array(arg_ref));
1567 		break;
1568 
1569 	case NDR_F_IS_POINTER:	/* type is pointer to one something */
1570 		if (is_union) {
1571 			error = NDR_ERR_ARRAY_UNION_ILLEGAL;
1572 			break;
1573 		}
1574 		return (mlndr_inner_pointer(arg_ref));
1575 		break;
1576 
1577 	case NDR_F_IS_REFERENCE:	/* type is pointer to one something */
1578 		if (is_union) {
1579 			error = NDR_ERR_ARRAY_UNION_ILLEGAL;
1580 			break;
1581 		}
1582 		return (mlndr_inner_reference(arg_ref));
1583 		break;
1584 
1585 	case NDR_F_SWITCH_IS:
1586 		if (!is_union) {
1587 			error = NDR_ERR_SWITCH_VALUE_ILLEGAL;
1588 			break;
1589 		}
1590 		return (*ti->ndr_func)(arg_ref);
1591 		break;
1592 
1593 	default:
1594 		error = NDR_ERR_INNER_PARAMS_BAD;
1595 		break;
1596 	}
1597 
1598 	/*
1599 	 * If we get here, something is wrong. Most likely,
1600 	 * the params flags do not match
1601 	 */
1602 	NDR_SET_ERROR(arg_ref, error);
1603 	return (0);
1604 }
1605 
1606 int
1607 mlndr_inner_pointer(struct ndr_reference *arg_ref)
1608 {
1609 	struct mlndr_stream 	*mlnds = arg_ref->stream;
1610 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
1611 	char 			**valpp = (char **)arg_ref->datum;
1612 	struct ndr_reference 	*outer_ref;
1613 
1614 	if (!mlndr__ulong(arg_ref))
1615 		return (0);	/* error */
1616 	if (!*valpp)
1617 		return (1);	/* NULL pointer */
1618 
1619 	outer_ref = mlndr_enter_outer_queue(arg_ref);
1620 	if (!outer_ref)
1621 		return (0);	/* error already set */
1622 
1623 	/* move advice in inner_flags to outer_flags sans pointer */
1624 	outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
1625 	outer_ref->outer_flags &= ~NDR_F_IS_POINTER;
1626 #ifdef NDR_INNER_NOT_YET
1627 	outer_ref->outer_flags |= NDR_F_BACKPTR;
1628 	if (outer_ref->outer_flags & NDR_F_SIZE_IS) {
1629 		outer_ref->outer_flags |= NDR_F_ARRAY+NDR_F_CONFORMANT;
1630 	}
1631 #endif /* NDR_INNER_NOT_YET */
1632 
1633 	outer_ref->backptr = valpp;
1634 
1635 	switch (mlnds->m_op) {
1636 	case NDR_M_OP_MARSHALL:
1637 		outer_ref->datum = *valpp;
1638 		break;
1639 
1640 	case NDR_M_OP_UNMARSHALL:
1641 		/*
1642 		 * This is probably wrong if the application allocated
1643 		 * memory in advance.  Indicate no value for now.
1644 		 * ONC RPC handles this case.
1645 		 */
1646 		*valpp = 0;
1647 		outer_ref->datum = 0;
1648 		break;
1649 	}
1650 
1651 	return (1);		/* pointer dereference scheduled */
1652 }
1653 
1654 int
1655 mlndr_inner_reference(struct ndr_reference *arg_ref)
1656 {
1657 	struct mlndr_stream	*mlnds = arg_ref->stream;
1658 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
1659 	char			**valpp = (char **)arg_ref->datum;
1660 	struct ndr_reference	*outer_ref;
1661 
1662 	outer_ref = mlndr_enter_outer_queue(arg_ref);
1663 	if (!outer_ref)
1664 		return (0);	/* error already set */
1665 
1666 	/* move advice in inner_flags to outer_flags sans pointer */
1667 	outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
1668 	outer_ref->outer_flags &= ~NDR_F_IS_REFERENCE;
1669 #ifdef NDR_INNER_REF_NOT_YET
1670 	outer_ref->outer_flags |= NDR_F_BACKPTR;
1671 	if (outer_ref->outer_flags & NDR_F_SIZE_IS) {
1672 		outer_ref->outer_flags |= NDR_F_ARRAY+NDR_F_CONFORMANT;
1673 	}
1674 #endif /* NDR_INNER_REF_NOT_YET */
1675 
1676 	outer_ref->backptr = valpp;
1677 
1678 	switch (mlnds->m_op) {
1679 	case NDR_M_OP_MARSHALL:
1680 		outer_ref->datum = *valpp;
1681 		break;
1682 
1683 	case NDR_M_OP_UNMARSHALL:
1684 		/*
1685 		 * This is probably wrong if the application allocated
1686 		 * memory in advance.  Indicate no value for now.
1687 		 * ONC RPC handles this case.
1688 		 */
1689 		*valpp = 0;
1690 		outer_ref->datum = 0;
1691 		break;
1692 	}
1693 
1694 	return (1);		/* pointer dereference scheduled */
1695 }
1696 
1697 int
1698 mlndr_inner_array(struct ndr_reference *encl_ref)
1699 {
1700 	struct ndr_typeinfo	*ti = encl_ref->ti;
1701 	struct ndr_reference	myref;
1702 	unsigned long		pdu_offset = encl_ref->pdu_offset;
1703 	unsigned long		n_elem;
1704 	unsigned long		i;
1705 	char			name[30];
1706 
1707 	if (encl_ref->inner_flags & NDR_F_SIZE_IS) {
1708 		/* now is the time to check/set size */
1709 		if (!mlndr_size_is(encl_ref))
1710 			return (0);	/* error already set */
1711 		n_elem = encl_ref->size_is;
1712 	} else {
1713 		assert(encl_ref->inner_flags & NDR_F_DIMENSION_IS);
1714 		n_elem = encl_ref->dimension_is;
1715 	}
1716 
1717 	bzero(&myref, sizeof (myref));
1718 	myref.enclosing = encl_ref;
1719 	myref.stream = encl_ref->stream;
1720 	myref.packed_alignment = 0;
1721 	myref.ti = ti;
1722 	myref.inner_flags = NDR_F_NONE;
1723 
1724 	for (i = 0; i < n_elem; i++) {
1725 		(void) sprintf(name, "[%lu]", i);
1726 		myref.name = name;
1727 		myref.pdu_offset = pdu_offset + i * ti->pdu_size_fixed_part;
1728 		myref.datum = encl_ref->datum + i * ti->c_size_fixed_part;
1729 
1730 		if (!mlndr_inner(&myref))
1731 			return (0);
1732 	}
1733 
1734 	return (1);
1735 }
1736 
1737 
1738 /*
1739  * BASIC TYPES
1740  */
1741 #define	MAKE_BASIC_TYPE_BASE(TYPE, SIZE) \
1742     extern int mlndr_##TYPE(struct ndr_reference *encl_ref); \
1743     struct ndr_typeinfo ndt_##TYPE = { \
1744 	1,		/* NDR version */ \
1745 	(SIZE)-1,	/* alignment */ \
1746 	NDR_F_NONE,	/* flags */ \
1747 	mlndr_##TYPE,	/* ndr_func */ \
1748 	SIZE,		/* pdu_size_fixed_part */ \
1749 	0,		/* pdu_size_variable_part */ \
1750 	SIZE,		/* c_size_fixed_part */ \
1751 	0,		/* c_size_variable_part */ \
1752 	}; \
1753     int mlndr_##TYPE(struct ndr_reference *ref) { \
1754 	return (mlndr_basic_integer(ref, SIZE)); \
1755 }
1756 
1757 #define	MAKE_BASIC_TYPE_STRING(TYPE, SIZE) \
1758     extern int mlndr_s##TYPE(struct ndr_reference *encl_ref); \
1759     struct ndr_typeinfo ndt_s##TYPE = { \
1760 	1,		/* NDR version */ \
1761 	(SIZE)-1,	/* alignment */ \
1762 	NDR_F_STRING,	/* flags */ \
1763 	mlndr_s##TYPE,	/* ndr_func */ \
1764 	0,		/* pdu_size_fixed_part */ \
1765 	SIZE,		/* pdu_size_variable_part */ \
1766 	0,		/* c_size_fixed_part */ \
1767 	SIZE,		/* c_size_variable_part */ \
1768 	}; \
1769     int mlndr_s##TYPE(struct ndr_reference *ref) { \
1770 	return (mlndr_string_basic_integer(ref, &ndt_##TYPE)); \
1771 }
1772 
1773 #define	MAKE_BASIC_TYPE(TYPE, SIZE) \
1774 	MAKE_BASIC_TYPE_BASE(TYPE, SIZE) \
1775 	MAKE_BASIC_TYPE_STRING(TYPE, SIZE)
1776 
1777 extern int
1778 mlndr_basic_integer(struct ndr_reference *ref, unsigned size);
1779 
1780 extern int
1781 mlndr_string_basic_integer(struct ndr_reference *encl_ref,
1782     struct ndr_typeinfo *type_under);
1783 
1784 
1785 MAKE_BASIC_TYPE(_char, 1)
1786 MAKE_BASIC_TYPE(_uchar, 1)
1787 MAKE_BASIC_TYPE(_short, 2)
1788 MAKE_BASIC_TYPE(_ushort, 2)
1789 MAKE_BASIC_TYPE(_long, 4)
1790 MAKE_BASIC_TYPE(_ulong, 4)
1791 
1792 MAKE_BASIC_TYPE_BASE(_wchar, 2)
1793 
1794 int
1795 mlndr_basic_integer(struct ndr_reference *ref, unsigned size)
1796 {
1797 	struct mlndr_stream 	*mlnds = ref->stream;
1798 	char 			*valp = (char *)ref->datum;
1799 	int			rc;
1800 
1801 	switch (mlnds->m_op) {
1802 	case NDR_M_OP_MARSHALL:
1803 		rc = MLNDS_PUT_PDU(mlnds, ref->pdu_offset, size,
1804 		    valp, mlnds->swap, ref);
1805 		break;
1806 
1807 	case NDR_M_OP_UNMARSHALL:
1808 		rc = MLNDS_GET_PDU(mlnds, ref->pdu_offset, size,
1809 		    valp, mlnds->swap, ref);
1810 		break;
1811 
1812 	default:
1813 		NDR_SET_ERROR(ref, NDR_ERR_M_OP_INVALID);
1814 		return (0);
1815 	}
1816 
1817 	return (rc);
1818 }
1819 
1820 int
1821 mlndr_string_basic_integer(struct ndr_reference *encl_ref,
1822     struct ndr_typeinfo *type_under)
1823 {
1824 	unsigned long		pdu_offset = encl_ref->pdu_offset;
1825 	unsigned		size = type_under->pdu_size_fixed_part;
1826 	char			*valp;
1827 	struct ndr_reference	myref;
1828 	unsigned long		i;
1829 	long			sense = 0;
1830 	char			name[30];
1831 
1832 	assert(size != 0);
1833 
1834 	bzero(&myref, sizeof (myref));
1835 	myref.enclosing = encl_ref;
1836 	myref.stream = encl_ref->stream;
1837 	myref.packed_alignment = 0;
1838 	myref.ti = type_under;
1839 	myref.inner_flags = NDR_F_NONE;
1840 	myref.name = name;
1841 
1842 	for (i = 0; i < NDR_STRING_MAX; i++) {
1843 		(void) sprintf(name, "[%lu]", i);
1844 		myref.pdu_offset = pdu_offset + i * size;
1845 		valp = encl_ref->datum + i * size;
1846 		myref.datum = valp;
1847 
1848 		if (!mlndr_inner(&myref))
1849 			return (0);
1850 
1851 		switch (size) {
1852 		case 1:		sense = *valp; break;
1853 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
1854 		case 2:		sense = *(short *)valp; break;
1855 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
1856 		case 4:		sense = *(long *)valp; break;
1857 		}
1858 
1859 		if (!sense)
1860 			break;
1861 	}
1862 
1863 	return (1);
1864 }
1865 
1866 
1867 extern int mlndr_s_wchar(struct ndr_reference *encl_ref);
1868 struct ndr_typeinfo ndt_s_wchar = {
1869 	1,		/* NDR version */
1870 	2-1,		/* alignment */
1871 	NDR_F_STRING,	/* flags */
1872 	mlndr_s_wchar,	/* ndr_func */
1873 	0,		/* pdu_size_fixed_part */
1874 	2,		/* pdu_size_variable_part */
1875 	0,		/* c_size_fixed_part */
1876 	1,		/* c_size_variable_part */
1877 };
1878 
1879 
1880 /*
1881  * Hand coded wchar function because all strings are transported
1882  * as wide characters. During NDR_M_OP_MARSHALL, we convert from
1883  * multi-byte to wide characters. During NDR_M_OP_UNMARSHALL, we
1884  * convert from wide characters to multi-byte.
1885  *
1886  * It appeared that NT would sometimes leave a spurious character
1887  * in the data stream before the null wide_char, which would get
1888  * included in the string decode because we processed until the
1889  * null character. It now looks like NT does not always terminate
1890  * RPC Unicode strings and the terminating null is a side effect
1891  * of field alignment. So now we rely on the strlen_is (set up in
1892  * mlndr_outer_string) of the enclosing reference. This may or may
1893  * not include the null but it doesn't matter, the algorithm will
1894  * get it right.
1895  */
1896 int
1897 mlndr_s_wchar(struct ndr_reference *encl_ref)
1898 {
1899 	struct mlndr_stream 	*mlnds = encl_ref->stream;
1900 	unsigned short		wide_char;
1901 	char 			*valp;
1902 	struct ndr_reference	myref;
1903 	unsigned long		i;
1904 	char			name[30];
1905 	int			count;
1906 	int			char_count = 0;
1907 
1908 	if (mlnds->m_op == NDR_M_OP_UNMARSHALL) {
1909 		/*
1910 		 * To avoid problems with zero length strings
1911 		 * we can just null terminate here and be done.
1912 		 */
1913 		if (encl_ref->strlen_is == 0) {
1914 			encl_ref->datum[0] = '\0';
1915 			return (1);
1916 		}
1917 	}
1918 
1919 	bzero(&myref, sizeof (myref));
1920 	myref.enclosing = encl_ref;
1921 	myref.stream = encl_ref->stream;
1922 	myref.packed_alignment = 0;
1923 	myref.ti = &ndt__wchar;
1924 	myref.inner_flags = NDR_F_NONE;
1925 	myref.datum = (char *)&wide_char;
1926 	myref.name = name;
1927 	myref.pdu_offset = encl_ref->pdu_offset;
1928 
1929 	valp = encl_ref->datum;
1930 	count = 0;
1931 
1932 	for (i = 0; i < NDR_STRING_MAX; i++) {
1933 		(void) sprintf(name, "[%lu]", i);
1934 
1935 		if (mlnds->m_op == NDR_M_OP_MARSHALL) {
1936 			count = mts_mbtowc((mts_wchar_t *)&wide_char, valp,
1937 			    MTS_MB_CHAR_MAX);
1938 			if (count < 0) {
1939 				return (0);
1940 			} else if (count == 0) {
1941 				if (encl_ref->strlen_is != encl_ref->size_is)
1942 					break;
1943 
1944 				/*
1945 				 * If the input char is 0, mbtowc
1946 				 * returns 0 without setting wide_char.
1947 				 * Set wide_char to 0 and a count of 1.
1948 				 */
1949 				wide_char = *valp;
1950 				count = 1;
1951 			}
1952 		}
1953 
1954 		if (!mlndr_inner(&myref))
1955 			return (0);
1956 
1957 		if (mlnds->m_op == NDR_M_OP_UNMARSHALL) {
1958 			count = mts_wctomb(valp, wide_char);
1959 
1960 			if ((++char_count) == encl_ref->strlen_is) {
1961 				valp += count;
1962 				*valp = '\0';
1963 				break;
1964 			}
1965 		}
1966 
1967 		if (!wide_char)
1968 			break;
1969 
1970 		myref.pdu_offset += sizeof (wide_char);
1971 		valp += count;
1972 	}
1973 
1974 	return (1);
1975 }
1976 
1977 /*
1978  * Converts a multibyte character string to a little-endian, wide-char
1979  * string.  No more than nwchars wide characters are stored.
1980  * A terminating null wide character is appended if there is room.
1981  *
1982  * Returns the number of wide characters converted, not counting
1983  * any terminating null wide character.  Returns -1 if an invalid
1984  * multibyte character is encountered.
1985  */
1986 size_t
1987 ndr_mbstowcs(struct mlndr_stream *mlnds, mts_wchar_t *wcs, const char *mbs,
1988     size_t nwchars)
1989 {
1990 	mts_wchar_t *start = wcs;
1991 	int nbytes;
1992 
1993 	while (nwchars--) {
1994 		nbytes = ndr_mbtowc(mlnds, wcs, mbs, MTS_MB_CHAR_MAX);
1995 		if (nbytes < 0) {
1996 			*wcs = 0;
1997 			return ((size_t)-1);
1998 		}
1999 
2000 		if (*mbs == 0)
2001 			break;
2002 
2003 		++wcs;
2004 		mbs += nbytes;
2005 	}
2006 
2007 	return (wcs - start);
2008 }
2009 
2010 /*
2011  * Converts a multibyte character to a little-endian, wide-char, which
2012  * is stored in wcharp.  Up to nbytes bytes are examined.
2013  *
2014  * If mbchar is valid, returns the number of bytes processed in mbchar.
2015  * If mbchar is invalid, returns -1.  See also mts_mbtowc().
2016  */
2017 /*ARGSUSED*/
2018 int
2019 ndr_mbtowc(struct mlndr_stream *mlnds, mts_wchar_t *wcharp,
2020     const char *mbchar, size_t nbytes)
2021 {
2022 	int rc;
2023 
2024 	if ((rc = mts_mbtowc(wcharp, mbchar, nbytes)) < 0)
2025 		return (rc);
2026 
2027 #ifdef _BIG_ENDIAN
2028 	if (mlnds == NULL || NDR_MODE_MATCH(mlnds, NDR_MODE_RETURN_SEND))
2029 		*wcharp = BSWAP_16(*wcharp);
2030 #endif
2031 
2032 	return (rc);
2033 }
2034