xref: /titanic_44/usr/src/lib/smbsrv/libmlrpc/common/ndr_process.c (revision 98157a7002f4f2cf7978f3084ca5577f0a1d72b2)
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 		valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref);
903 		if (!valp) {
904 			NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
905 			return (0);
906 		}
907 		if (outer_ref->backptr)
908 			*outer_ref->backptr = valp;
909 		outer_ref->datum = valp;
910 		break;
911 
912 	default:
913 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
914 		return (0);
915 	}
916 
917 	bzero(&myref, sizeof (myref));
918 	myref.stream = mlnds;
919 	myref.enclosing = outer_ref;
920 	myref.ti = outer_ref->ti;
921 	myref.datum = outer_ref->datum;
922 	myref.name = "CONFORMANT-ARRAY";
923 	myref.outer_flags = NDR_F_NONE;
924 	myref.inner_flags = NDR_F_SIZE_IS;
925 	myref.size_is = outer_ref->size_is;
926 
927 	myref.inner_flags = NDR_F_DIMENSION_IS;		/* convenient */
928 	myref.dimension_is = outer_ref->size_is;	/* convenient */
929 
930 	myref.pdu_offset = outer_ref->pdu_offset + 4;
931 	outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
932 
933 	outer_ref->type_flags = NDR_F_NONE;
934 	outer_ref->inner_flags = NDR_F_NONE;
935 
936 	rc = mlndr_inner(&myref);
937 	if (!rc)
938 		return (rc);		/* error already set */
939 
940 	mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
941 	return (1);
942 }
943 
944 int
945 mlndr_outer_conformant_construct(struct ndr_reference *outer_ref)
946 {
947 	struct mlndr_stream 	*mlnds = outer_ref->stream;
948 	struct ndr_typeinfo 	*ti = outer_ref->ti;
949 	struct ndr_reference	myref;
950 	char 		*valp = NULL;
951 	int		is_varlen = ti->pdu_size_variable_part;
952 	int		is_union = NDR_IS_UNION(ti);
953 	int		is_string = NDR_IS_STRING(ti);
954 	unsigned long	size_is;
955 	int		rc;
956 	unsigned	n_hdr;
957 	unsigned	n_fixed;
958 	unsigned	n_variable;
959 	unsigned	n_alloc;
960 	unsigned	n_pdu_total;
961 	int		params;
962 
963 	params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
964 
965 	assert(is_varlen && !is_string && !is_union);
966 	assert(params == NDR_F_NONE);
967 
968 	/* conformant header for this */
969 	n_hdr = 4;
970 
971 	/* fixed part -- exactly one of these */
972 	n_fixed = ti->pdu_size_fixed_part;
973 
974 	/* variable part -- exactly size_of of these */
975 	n_variable = 0;		/* 0 for the moment */
976 
977 	/* sum them up to determine the PDU space required */
978 	n_pdu_total = n_hdr + n_fixed + n_variable;
979 
980 	/* similar sum to determine how much local memory is required */
981 	n_alloc = n_fixed + n_variable;
982 
983 	/* For the moment, grow enough for the fixed-size part */
984 	rc = mlndr_outer_grow(outer_ref, n_pdu_total);
985 	if (!rc)
986 		return (rc);		/* error already set */
987 
988 	switch (mlnds->m_op) {
989 	case NDR_M_OP_MARSHALL:
990 		/*
991 		 * We don't know the size yet. We have to wait for
992 		 * it. Proceed with the fixed-size part, and await
993 		 * the call to mlndr_size_is().
994 		 */
995 		size_is = 0;
996 		rc = mlndr_outer_poke_sizing(outer_ref, 0, &size_is);
997 		if (!rc)
998 			return (0);	/* error already set */
999 
1000 		valp = outer_ref->datum;
1001 		assert(valp);
1002 		if (outer_ref->backptr) {
1003 			assert(valp == *outer_ref->backptr);
1004 		}
1005 		break;
1006 
1007 	case NDR_M_OP_UNMARSHALL:
1008 		/*
1009 		 * We know the size of the variable part because
1010 		 * of the CONFORMANT header. We will verify
1011 		 * the header against the [size_is(X)] advice
1012 		 * later when mlndr_size_is() is called.
1013 		 */
1014 		rc = mlndr_outer_peek_sizing(outer_ref, 0, &size_is);
1015 		if (!rc)
1016 			return (0);	/* error already set */
1017 
1018 		/* recalculate metrics */
1019 		n_variable = size_is * ti->pdu_size_variable_part;
1020 		n_pdu_total = n_hdr + n_fixed + n_variable;
1021 		n_alloc = n_fixed + n_variable;
1022 
1023 		rc = mlndr_outer_grow(outer_ref, n_pdu_total);
1024 		if (!rc)
1025 			return (rc);		/* error already set */
1026 
1027 		outer_ref->size_is = size_is; /* verified later */
1028 
1029 		valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref);
1030 		if (!valp) {
1031 			NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
1032 			return (0);
1033 		}
1034 		if (outer_ref->backptr)
1035 			*outer_ref->backptr = valp;
1036 		outer_ref->datum = valp;
1037 		break;
1038 
1039 	default:
1040 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1041 		return (0);
1042 	}
1043 
1044 	bzero(&myref, sizeof (myref));
1045 	myref.stream = mlnds;
1046 	myref.enclosing = outer_ref;
1047 	myref.ti = outer_ref->ti;
1048 	myref.datum = outer_ref->datum;
1049 	myref.name = "CONFORMANT-CONSTRUCT";
1050 	myref.outer_flags = NDR_F_NONE;
1051 	myref.inner_flags = NDR_F_NONE;
1052 	myref.size_is = outer_ref->size_is;
1053 
1054 	myref.pdu_offset = outer_ref->pdu_offset + 4;
1055 	outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
1056 
1057 	outer_ref->type_flags = NDR_F_SIZE_IS; /* indicate pending */
1058 	outer_ref->inner_flags = NDR_F_NONE;   /* indicate pending */
1059 
1060 	rc = mlndr_inner(&myref);
1061 	if (!rc)
1062 		return (rc);		/* error already set */
1063 
1064 	mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
1065 
1066 	if (outer_ref->inner_flags != NDR_F_SIZE_IS) {
1067 		NDR_SET_ERROR(&myref, NDR_ERR_SIZE_IS_MISMATCH_AFTER);
1068 		return (0);
1069 	}
1070 
1071 	return (1);
1072 }
1073 
1074 int
1075 mlndr_size_is(struct ndr_reference *ref)
1076 {
1077 	struct mlndr_stream 	*mlnds = ref->stream;
1078 	struct ndr_reference 	*outer_ref = mlnds->outer_current;
1079 	struct ndr_typeinfo 	*ti = outer_ref->ti;
1080 	unsigned long		size_is;
1081 	int			rc;
1082 	unsigned		n_hdr;
1083 	unsigned		n_fixed;
1084 	unsigned		n_variable;
1085 	unsigned		n_pdu_total;
1086 
1087 	assert(ref->inner_flags & NDR_F_SIZE_IS);
1088 	size_is = ref->size_is;
1089 
1090 	if (outer_ref->type_flags != NDR_F_SIZE_IS) {
1091 		NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_UNEXPECTED);
1092 		return (0);
1093 	}
1094 
1095 	if (outer_ref->inner_flags & NDR_F_SIZE_IS) {
1096 		NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_DUPLICATED);
1097 		return (0);
1098 	}
1099 
1100 	/* repeat metrics, see mlndr_conformant_construct() above */
1101 	n_hdr = 4;
1102 	n_fixed = ti->pdu_size_fixed_part;
1103 	n_variable = size_is * ti->pdu_size_variable_part;
1104 	n_pdu_total = n_hdr + n_fixed + n_variable;
1105 
1106 	rc = mlndr_outer_grow(outer_ref, n_pdu_total);
1107 	if (!rc)
1108 		return (rc);		/* error already set */
1109 
1110 	switch (mlnds->m_op) {
1111 	case NDR_M_OP_MARSHALL:
1112 		/*
1113 		 * We have to set the sizing header and extend
1114 		 * the size of the PDU (already done).
1115 		 */
1116 		rc = mlndr_outer_poke_sizing(outer_ref, 0, &size_is);
1117 		if (!rc)
1118 			return (0);	/* error already set */
1119 		break;
1120 
1121 	case NDR_M_OP_UNMARSHALL:
1122 		/*
1123 		 * Allocation done during mlndr_conformant_construct().
1124 		 * All we are doing here is verifying that the
1125 		 * intended size (ref->size_is) matches the sizing header.
1126 		 */
1127 		if (size_is != outer_ref->size_is) {
1128 			NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_MISMATCH_PDU);
1129 			return (0);
1130 		}
1131 		break;
1132 
1133 	default:
1134 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1135 		return (0);
1136 	}
1137 
1138 	outer_ref->inner_flags |= NDR_F_SIZE_IS;
1139 	outer_ref->size_is = ref->size_is;
1140 	return (1);
1141 }
1142 
1143 int
1144 mlndr_outer_string(struct ndr_reference *outer_ref)
1145 {
1146 	struct mlndr_stream 	*mlnds = outer_ref->stream;
1147 	struct ndr_typeinfo 	*ti = outer_ref->ti;
1148 	struct ndr_reference	myref;
1149 	char 		*valp = NULL;
1150 	unsigned	is_varlen = ti->pdu_size_variable_part;
1151 	int		is_union = NDR_IS_UNION(ti);
1152 	int		is_string = NDR_IS_STRING(ti);
1153 	int		rc;
1154 	unsigned	n_zeroes;
1155 	unsigned	ix;
1156 	unsigned long	size_is;
1157 	unsigned long	first_is;
1158 	unsigned long	length_is;
1159 	unsigned	n_hdr;
1160 	unsigned	n_fixed;
1161 	unsigned	n_variable;
1162 	unsigned	n_alloc;
1163 	unsigned	n_pdu_total;
1164 	int		params;
1165 
1166 	params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
1167 
1168 	assert(is_varlen && is_string && !is_union);
1169 	assert(params == NDR_F_NONE);
1170 
1171 	/* string header for this: size_is first_is length_is */
1172 	n_hdr = 12;
1173 
1174 	/* fixed part -- exactly none of these */
1175 	n_fixed = 0;
1176 
1177 	if (!mlndr_outer_grow(outer_ref, n_hdr))
1178 		return (0);		/* error already set */
1179 
1180 	switch (mlnds->m_op) {
1181 	case NDR_M_OP_MARSHALL:
1182 		valp = outer_ref->datum;
1183 		assert(valp);
1184 
1185 		if (outer_ref->backptr)
1186 			assert(valp == *outer_ref->backptr);
1187 
1188 		if (ti == &ndt_s_wchar) {
1189 			/*
1190 			 * size_is is the number of characters in the string,
1191 			 * including the null. We assume valp is UTF-8 encoded.
1192 			 * We can use mts_wcequiv_strlen for ASCII, extended
1193 			 * ASCII or Unicode (UCS-2).
1194 			 */
1195 			size_is = (mts_wcequiv_strlen(valp) /
1196 			    sizeof (mts_wchar_t)) + 1;
1197 
1198 			if (size_is > NDR_STRING_MAX) {
1199 				NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN);
1200 				return (0);
1201 			}
1202 		} else {
1203 			valp = outer_ref->datum;
1204 			n_zeroes = 0;
1205 			for (ix = 0; ix < 1024; ix++) {
1206 				if (valp[ix] == 0) {
1207 					n_zeroes++;
1208 					if (n_zeroes >= is_varlen &&
1209 					    ix % is_varlen == 0) {
1210 						break;
1211 					}
1212 				} else {
1213 					n_zeroes = 0;
1214 				}
1215 			}
1216 			if (ix >= 1024) {
1217 				NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN);
1218 				return (0);
1219 			}
1220 			size_is = ix+1;
1221 		}
1222 
1223 		first_is = 0;
1224 
1225 		if (mlnds->flags & MLNDS_F_NOTERM)
1226 			length_is = size_is - 1;
1227 		else
1228 			length_is = size_is;
1229 
1230 		if (!mlndr_outer_poke_sizing(outer_ref, 0, &size_is) ||
1231 		    !mlndr_outer_poke_sizing(outer_ref, 4, &first_is) ||
1232 		    !mlndr_outer_poke_sizing(outer_ref, 8, &length_is))
1233 			return (0);		/* error already set */
1234 		break;
1235 
1236 	case NDR_M_OP_UNMARSHALL:
1237 		if (!mlndr_outer_peek_sizing(outer_ref, 0, &size_is) ||
1238 		    !mlndr_outer_peek_sizing(outer_ref, 4, &first_is) ||
1239 		    !mlndr_outer_peek_sizing(outer_ref, 8, &length_is))
1240 			return (0);		/* error already set */
1241 
1242 		/*
1243 		 * In addition to the first_is check, we used to check that
1244 		 * size_is or size_is-1 was equal to length_is but Windows95
1245 		 * doesn't conform to this "rule" (see variable part below).
1246 		 * The srvmgr tool for Windows95 sent the following values
1247 		 * for a path string:
1248 		 *
1249 		 *	size_is   = 261 (0x105)
1250 		 *	first_is  = 0
1251 		 *	length_is = 53  (0x35)
1252 		 *
1253 		 * The length_is was correct (for the given path) but the
1254 		 * size_is was the maximum path length rather than being
1255 		 * related to length_is.
1256 		 */
1257 		if (first_is != 0) {
1258 			NDR_SET_ERROR(outer_ref, NDR_ERR_STRING_SIZING);
1259 			return (0);
1260 		}
1261 
1262 		if (ti == &ndt_s_wchar) {
1263 			/*
1264 			 * Decoding Unicode to UTF-8; we need to allow
1265 			 * for the maximum possible char size. It would
1266 			 * be nice to use mbequiv_strlen but the string
1267 			 * may not be null terminated.
1268 			 */
1269 			n_alloc = (size_is + 1) * MTS_MB_CHAR_MAX;
1270 		} else {
1271 			n_alloc = (size_is + 1) * is_varlen;
1272 		}
1273 
1274 		valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref);
1275 		if (!valp) {
1276 			NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
1277 			return (0);
1278 		}
1279 
1280 		bzero(valp, (size_is+1) * is_varlen);
1281 
1282 		if (outer_ref->backptr)
1283 			*outer_ref->backptr = valp;
1284 		outer_ref->datum = valp;
1285 		break;
1286 
1287 	default:
1288 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1289 		return (0);
1290 	}
1291 
1292 	/*
1293 	 * Variable part - exactly length_is of these.
1294 	 *
1295 	 * Usually, length_is is same as size_is and includes nul.
1296 	 * Some protocols use length_is = size_is-1, and length_is does
1297 	 * not include the nul (which is more consistent with DCE spec).
1298 	 * If the length_is is 0, there is no data following the
1299 	 * sizing header, regardless of size_is.
1300 	 */
1301 	n_variable = length_is * is_varlen;
1302 
1303 	/* sum them up to determine the PDU space required */
1304 	n_pdu_total = n_hdr + n_fixed + n_variable;
1305 
1306 	/* similar sum to determine how much local memory is required */
1307 	n_alloc = n_fixed + n_variable;
1308 
1309 	rc = mlndr_outer_grow(outer_ref, n_pdu_total);
1310 	if (!rc)
1311 		return (rc);		/* error already set */
1312 
1313 	if (length_is > 0) {
1314 		bzero(&myref, sizeof (myref));
1315 		myref.stream = mlnds;
1316 		myref.enclosing = outer_ref;
1317 		myref.ti = outer_ref->ti;
1318 		myref.datum = outer_ref->datum;
1319 		myref.name = "OUTER-STRING";
1320 		myref.outer_flags = NDR_F_IS_STRING;
1321 		myref.inner_flags = NDR_F_NONE;
1322 
1323 		/*
1324 		 * Set up size_is and strlen_is for mlndr_s_wchar.
1325 		 */
1326 		myref.size_is = size_is;
1327 		myref.strlen_is = length_is;
1328 	}
1329 
1330 	myref.pdu_offset = outer_ref->pdu_offset + 12;
1331 
1332 	/*
1333 	 * Don't try to decode empty strings.
1334 	 */
1335 	if ((size_is == 0) && (first_is == 0) && (length_is == 0)) {
1336 		mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
1337 		return (1);
1338 	}
1339 
1340 	if ((size_is != 0) && (length_is != 0)) {
1341 		rc = mlndr_inner(&myref);
1342 		if (!rc)
1343 			return (rc);		/* error already set */
1344 	}
1345 
1346 	mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
1347 	return (1);
1348 }
1349 
1350 int
1351 mlndr_outer_peek_sizing(struct ndr_reference *outer_ref, unsigned offset,
1352     unsigned long *sizing_p)
1353 {
1354 	struct mlndr_stream 	*mlnds = outer_ref->stream;
1355 	unsigned long		pdu_offset;
1356 	int			rc;
1357 
1358 	pdu_offset = outer_ref->pdu_offset + offset;
1359 
1360 	if (pdu_offset < mlnds->outer_current->pdu_offset ||
1361 	    pdu_offset > mlnds->outer_current->pdu_end_offset ||
1362 	    pdu_offset+4 > mlnds->outer_current->pdu_end_offset) {
1363 		NDR_SET_ERROR(outer_ref, NDR_ERR_BOUNDS_CHECK);
1364 		return (0);
1365 	}
1366 
1367 	switch (mlnds->m_op) {
1368 	case NDR_M_OP_MARSHALL:
1369 		NDR_SET_ERROR(outer_ref, NDR_ERR_UNIMPLEMENTED);
1370 		return (0);
1371 
1372 	case NDR_M_OP_UNMARSHALL:
1373 		rc = MLNDS_GET_PDU(mlnds, pdu_offset, 4, (char *)sizing_p,
1374 		    mlnds->swap, outer_ref);
1375 		break;
1376 
1377 	default:
1378 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1379 		return (0);
1380 	}
1381 
1382 	return (rc);
1383 }
1384 
1385 int
1386 mlndr_outer_poke_sizing(struct ndr_reference *outer_ref, unsigned offset,
1387     unsigned long *sizing_p)
1388 {
1389 	struct mlndr_stream 	*mlnds = outer_ref->stream;
1390 	unsigned long		pdu_offset;
1391 	int			rc;
1392 
1393 	pdu_offset = outer_ref->pdu_offset + offset;
1394 
1395 	if (pdu_offset < mlnds->outer_current->pdu_offset ||
1396 	    pdu_offset > mlnds->outer_current->pdu_end_offset ||
1397 	    pdu_offset+4 > mlnds->outer_current->pdu_end_offset) {
1398 		NDR_SET_ERROR(outer_ref, NDR_ERR_BOUNDS_CHECK);
1399 		return (0);
1400 	}
1401 
1402 	switch (mlnds->m_op) {
1403 	case NDR_M_OP_MARSHALL:
1404 		rc = MLNDS_PUT_PDU(mlnds, pdu_offset, 4, (char *)sizing_p,
1405 		    mlnds->swap, outer_ref);
1406 		break;
1407 
1408 	case NDR_M_OP_UNMARSHALL:
1409 		NDR_SET_ERROR(outer_ref, NDR_ERR_UNIMPLEMENTED);
1410 		return (0);
1411 
1412 	default:
1413 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1414 		return (0);
1415 	}
1416 
1417 	return (rc);
1418 }
1419 
1420 /*
1421  * All OUTER constructs begin on a mod4 (dword) boundary - except
1422  * for the ones that don't: some MSRPC calls appear to use word or
1423  * packed alignment.  Strings appear to be dword aligned.
1424  */
1425 int
1426 mlndr_outer_align(struct ndr_reference *outer_ref)
1427 {
1428 	struct mlndr_stream 	*mlnds = outer_ref->stream;
1429 	int			rc;
1430 	unsigned		n_pad;
1431 	unsigned		align;
1432 
1433 	if (outer_ref->packed_alignment && outer_ref->ti != &ndt_s_wchar) {
1434 		align = outer_ref->ti->alignment;
1435 		n_pad = ((align + 1) - mlnds->pdu_scan_offset) & align;
1436 	} else {
1437 		n_pad = (4 - mlnds->pdu_scan_offset) & 3;
1438 	}
1439 
1440 	if (n_pad == 0)
1441 		return (1);	/* already aligned, often the case */
1442 
1443 	if (!mlndr_outer_grow(outer_ref, n_pad))
1444 		return (0);	/* error already set */
1445 
1446 	switch (mlnds->m_op) {
1447 	case NDR_M_OP_MARSHALL:
1448 		rc = MLNDS_PAD_PDU(mlnds,
1449 		    mlnds->pdu_scan_offset, n_pad, outer_ref);
1450 		if (!rc) {
1451 			NDR_SET_ERROR(outer_ref, NDR_ERR_PAD_FAILED);
1452 			return (0);
1453 		}
1454 		break;
1455 
1456 	case NDR_M_OP_UNMARSHALL:
1457 		break;
1458 
1459 	default:
1460 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1461 		return (0);
1462 	}
1463 
1464 	mlnds->pdu_scan_offset += n_pad;
1465 	return (1);
1466 }
1467 
1468 int
1469 mlndr_outer_grow(struct ndr_reference *outer_ref, unsigned n_total)
1470 {
1471 	struct mlndr_stream 	*mlnds = outer_ref->stream;
1472 	unsigned long		pdu_want_size;
1473 	int			rc, is_ok = 0;
1474 
1475 	pdu_want_size = mlnds->pdu_scan_offset + n_total;
1476 
1477 	if (pdu_want_size <= mlnds->pdu_max_size) {
1478 		is_ok = 1;
1479 	}
1480 
1481 	switch (mlnds->m_op) {
1482 	case NDR_M_OP_MARSHALL:
1483 		if (is_ok)
1484 			break;
1485 		rc = MLNDS_GROW_PDU(mlnds, pdu_want_size, outer_ref);
1486 		if (!rc) {
1487 			NDR_SET_ERROR(outer_ref, NDR_ERR_GROW_FAILED);
1488 			return (0);
1489 		}
1490 		break;
1491 
1492 	case NDR_M_OP_UNMARSHALL:
1493 		if (is_ok)
1494 			break;
1495 		NDR_SET_ERROR(outer_ref, NDR_ERR_UNDERFLOW);
1496 		return (0);
1497 
1498 	default:
1499 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1500 		return (0);
1501 	}
1502 
1503 	if (mlnds->pdu_size < pdu_want_size)
1504 		mlnds->pdu_size = pdu_want_size;
1505 
1506 	outer_ref->pdu_end_offset = pdu_want_size;
1507 	return (1);
1508 }
1509 
1510 /*
1511  * INNER ELEMENTS
1512  *
1513  * The local datum (arg_ref->datum) already exists, there is no need to
1514  * malloc() it.  The datum should point at a member of a structure.
1515  *
1516  * For the most part, mlndr_inner() and its helpers are just a sanity
1517  * check.  The underlying ti->ndr_func() could be called immediately
1518  * for non-pointer elements.  For the sake of robustness, we detect
1519  * run-time errors here.  Most of the situations this protects against
1520  * have already been checked by the IDL compiler.  This is also a
1521  * common point for processing of all data, and so is a convenient
1522  * place to work from for debugging.
1523  */
1524 int
1525 mlndr_inner(struct ndr_reference *arg_ref)
1526 {
1527 	struct ndr_typeinfo 	*ti = arg_ref->ti;
1528 	int	is_varlen = ti->pdu_size_variable_part;
1529 	int	is_union = NDR_IS_UNION(ti);
1530 	int	error = NDR_ERR_INNER_PARAMS_BAD;
1531 	int	params;
1532 
1533 	params = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
1534 
1535 	switch (params) {
1536 	case NDR_F_NONE:
1537 		if (is_union) {
1538 			error = NDR_ERR_SWITCH_VALUE_MISSING;
1539 			break;
1540 		}
1541 		return (*ti->ndr_func)(arg_ref);
1542 		break;
1543 
1544 	case NDR_F_SIZE_IS:
1545 	case NDR_F_DIMENSION_IS:
1546 	case NDR_F_IS_POINTER+NDR_F_SIZE_IS:   /* pointer to something */
1547 	case NDR_F_IS_REFERENCE+NDR_F_SIZE_IS: /* pointer to something */
1548 		if (is_varlen) {
1549 			error = NDR_ERR_ARRAY_VARLEN_ILLEGAL;
1550 			break;
1551 		}
1552 		if (is_union) {
1553 			error = NDR_ERR_ARRAY_UNION_ILLEGAL;
1554 			break;
1555 		}
1556 		if (params & NDR_F_IS_POINTER)
1557 			return (mlndr_inner_pointer(arg_ref));
1558 		else if (params & NDR_F_IS_REFERENCE)
1559 			return (mlndr_inner_reference(arg_ref));
1560 		else
1561 			return (mlndr_inner_array(arg_ref));
1562 		break;
1563 
1564 	case NDR_F_IS_POINTER:	/* type is pointer to one something */
1565 		if (is_union) {
1566 			error = NDR_ERR_ARRAY_UNION_ILLEGAL;
1567 			break;
1568 		}
1569 		return (mlndr_inner_pointer(arg_ref));
1570 		break;
1571 
1572 	case NDR_F_IS_REFERENCE:	/* type is pointer to one something */
1573 		if (is_union) {
1574 			error = NDR_ERR_ARRAY_UNION_ILLEGAL;
1575 			break;
1576 		}
1577 		return (mlndr_inner_reference(arg_ref));
1578 		break;
1579 
1580 	case NDR_F_SWITCH_IS:
1581 		if (!is_union) {
1582 			error = NDR_ERR_SWITCH_VALUE_ILLEGAL;
1583 			break;
1584 		}
1585 		return (*ti->ndr_func)(arg_ref);
1586 		break;
1587 
1588 	default:
1589 		error = NDR_ERR_INNER_PARAMS_BAD;
1590 		break;
1591 	}
1592 
1593 	/*
1594 	 * If we get here, something is wrong. Most likely,
1595 	 * the params flags do not match
1596 	 */
1597 	NDR_SET_ERROR(arg_ref, error);
1598 	return (0);
1599 }
1600 
1601 int
1602 mlndr_inner_pointer(struct ndr_reference *arg_ref)
1603 {
1604 	struct mlndr_stream 	*mlnds = arg_ref->stream;
1605 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
1606 	char 			**valpp = (char **)arg_ref->datum;
1607 	struct ndr_reference 	*outer_ref;
1608 
1609 	if (!mlndr__ulong(arg_ref))
1610 		return (0);	/* error */
1611 	if (!*valpp)
1612 		return (1);	/* NULL pointer */
1613 
1614 	outer_ref = mlndr_enter_outer_queue(arg_ref);
1615 	if (!outer_ref)
1616 		return (0);	/* error already set */
1617 
1618 	/* move advice in inner_flags to outer_flags sans pointer */
1619 	outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
1620 	outer_ref->outer_flags &= ~NDR_F_IS_POINTER;
1621 #ifdef NDR_INNER_NOT_YET
1622 	outer_ref->outer_flags |= NDR_F_BACKPTR;
1623 	if (outer_ref->outer_flags & NDR_F_SIZE_IS) {
1624 		outer_ref->outer_flags |= NDR_F_ARRAY+NDR_F_CONFORMANT;
1625 	}
1626 #endif /* NDR_INNER_NOT_YET */
1627 
1628 	outer_ref->backptr = valpp;
1629 
1630 	switch (mlnds->m_op) {
1631 	case NDR_M_OP_MARSHALL:
1632 		outer_ref->datum = *valpp;
1633 		break;
1634 
1635 	case NDR_M_OP_UNMARSHALL:
1636 		/*
1637 		 * This is probably wrong if the application allocated
1638 		 * memory in advance.  Indicate no value for now.
1639 		 * ONC RPC handles this case.
1640 		 */
1641 		*valpp = 0;
1642 		outer_ref->datum = 0;
1643 		break;
1644 	}
1645 
1646 	return (1);		/* pointer dereference scheduled */
1647 }
1648 
1649 int
1650 mlndr_inner_reference(struct ndr_reference *arg_ref)
1651 {
1652 	struct mlndr_stream	*mlnds = arg_ref->stream;
1653 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
1654 	char			**valpp = (char **)arg_ref->datum;
1655 	struct ndr_reference	*outer_ref;
1656 
1657 	outer_ref = mlndr_enter_outer_queue(arg_ref);
1658 	if (!outer_ref)
1659 		return (0);	/* error already set */
1660 
1661 	/* move advice in inner_flags to outer_flags sans pointer */
1662 	outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
1663 	outer_ref->outer_flags &= ~NDR_F_IS_REFERENCE;
1664 #ifdef NDR_INNER_REF_NOT_YET
1665 	outer_ref->outer_flags |= NDR_F_BACKPTR;
1666 	if (outer_ref->outer_flags & NDR_F_SIZE_IS) {
1667 		outer_ref->outer_flags |= NDR_F_ARRAY+NDR_F_CONFORMANT;
1668 	}
1669 #endif /* NDR_INNER_REF_NOT_YET */
1670 
1671 	outer_ref->backptr = valpp;
1672 
1673 	switch (mlnds->m_op) {
1674 	case NDR_M_OP_MARSHALL:
1675 		outer_ref->datum = *valpp;
1676 		break;
1677 
1678 	case NDR_M_OP_UNMARSHALL:
1679 		/*
1680 		 * This is probably wrong if the application allocated
1681 		 * memory in advance.  Indicate no value for now.
1682 		 * ONC RPC handles this case.
1683 		 */
1684 		*valpp = 0;
1685 		outer_ref->datum = 0;
1686 		break;
1687 	}
1688 
1689 	return (1);		/* pointer dereference scheduled */
1690 }
1691 
1692 int
1693 mlndr_inner_array(struct ndr_reference *encl_ref)
1694 {
1695 	struct ndr_typeinfo	*ti = encl_ref->ti;
1696 	struct ndr_reference	myref;
1697 	unsigned long		pdu_offset = encl_ref->pdu_offset;
1698 	unsigned long		n_elem;
1699 	unsigned long		i;
1700 	char			name[30];
1701 
1702 	if (encl_ref->inner_flags & NDR_F_SIZE_IS) {
1703 		/* now is the time to check/set size */
1704 		if (!mlndr_size_is(encl_ref))
1705 			return (0);	/* error already set */
1706 		n_elem = encl_ref->size_is;
1707 	} else {
1708 		assert(encl_ref->inner_flags & NDR_F_DIMENSION_IS);
1709 		n_elem = encl_ref->dimension_is;
1710 	}
1711 
1712 	bzero(&myref, sizeof (myref));
1713 	myref.enclosing = encl_ref;
1714 	myref.stream = encl_ref->stream;
1715 	myref.packed_alignment = 0;
1716 	myref.ti = ti;
1717 	myref.inner_flags = NDR_F_NONE;
1718 
1719 	for (i = 0; i < n_elem; i++) {
1720 		(void) sprintf(name, "[%lu]", i);
1721 		myref.name = name;
1722 		myref.pdu_offset = pdu_offset + i * ti->pdu_size_fixed_part;
1723 		myref.datum = encl_ref->datum + i * ti->c_size_fixed_part;
1724 
1725 		if (!mlndr_inner(&myref))
1726 			return (0);
1727 	}
1728 
1729 	return (1);
1730 }
1731 
1732 
1733 /*
1734  * BASIC TYPES
1735  */
1736 #define	MAKE_BASIC_TYPE_BASE(TYPE, SIZE) \
1737     extern int mlndr_##TYPE(struct ndr_reference *encl_ref); \
1738     struct ndr_typeinfo ndt_##TYPE = { \
1739 	1,		/* NDR version */ \
1740 	(SIZE)-1,	/* alignment */ \
1741 	NDR_F_NONE,	/* flags */ \
1742 	mlndr_##TYPE,	/* ndr_func */ \
1743 	SIZE,		/* pdu_size_fixed_part */ \
1744 	0,		/* pdu_size_variable_part */ \
1745 	SIZE,		/* c_size_fixed_part */ \
1746 	0,		/* c_size_variable_part */ \
1747 	}; \
1748     int mlndr_##TYPE(struct ndr_reference *ref) { \
1749 	return (mlndr_basic_integer(ref, SIZE)); \
1750 }
1751 
1752 #define	MAKE_BASIC_TYPE_STRING(TYPE, SIZE) \
1753     extern int mlndr_s##TYPE(struct ndr_reference *encl_ref); \
1754     struct ndr_typeinfo ndt_s##TYPE = { \
1755 	1,		/* NDR version */ \
1756 	(SIZE)-1,	/* alignment */ \
1757 	NDR_F_STRING,	/* flags */ \
1758 	mlndr_s##TYPE,	/* ndr_func */ \
1759 	0,		/* pdu_size_fixed_part */ \
1760 	SIZE,		/* pdu_size_variable_part */ \
1761 	0,		/* c_size_fixed_part */ \
1762 	SIZE,		/* c_size_variable_part */ \
1763 	}; \
1764     int mlndr_s##TYPE(struct ndr_reference *ref) { \
1765 	return (mlndr_string_basic_integer(ref, &ndt_##TYPE)); \
1766 }
1767 
1768 #define	MAKE_BASIC_TYPE(TYPE, SIZE) \
1769 	MAKE_BASIC_TYPE_BASE(TYPE, SIZE) \
1770 	MAKE_BASIC_TYPE_STRING(TYPE, SIZE)
1771 
1772 extern int
1773 mlndr_basic_integer(struct ndr_reference *ref, unsigned size);
1774 
1775 extern int
1776 mlndr_string_basic_integer(struct ndr_reference *encl_ref,
1777     struct ndr_typeinfo *type_under);
1778 
1779 
1780 MAKE_BASIC_TYPE(_char, 1)
1781 MAKE_BASIC_TYPE(_uchar, 1)
1782 MAKE_BASIC_TYPE(_short, 2)
1783 MAKE_BASIC_TYPE(_ushort, 2)
1784 MAKE_BASIC_TYPE(_long, 4)
1785 MAKE_BASIC_TYPE(_ulong, 4)
1786 
1787 MAKE_BASIC_TYPE_BASE(_wchar, 2)
1788 
1789 int
1790 mlndr_basic_integer(struct ndr_reference *ref, unsigned size)
1791 {
1792 	struct mlndr_stream 	*mlnds = ref->stream;
1793 	char 			*valp = (char *)ref->datum;
1794 	int			rc;
1795 
1796 	switch (mlnds->m_op) {
1797 	case NDR_M_OP_MARSHALL:
1798 		rc = MLNDS_PUT_PDU(mlnds, ref->pdu_offset, size,
1799 		    valp, mlnds->swap, ref);
1800 		break;
1801 
1802 	case NDR_M_OP_UNMARSHALL:
1803 		rc = MLNDS_GET_PDU(mlnds, ref->pdu_offset, size,
1804 		    valp, mlnds->swap, ref);
1805 		break;
1806 
1807 	default:
1808 		NDR_SET_ERROR(ref, NDR_ERR_M_OP_INVALID);
1809 		return (0);
1810 	}
1811 
1812 	return (rc);
1813 }
1814 
1815 int
1816 mlndr_string_basic_integer(struct ndr_reference *encl_ref,
1817     struct ndr_typeinfo *type_under)
1818 {
1819 	unsigned long		pdu_offset = encl_ref->pdu_offset;
1820 	unsigned		size = type_under->pdu_size_fixed_part;
1821 	char			*valp;
1822 	struct ndr_reference	myref;
1823 	unsigned long		i;
1824 	long			sense = 0;
1825 	char			name[30];
1826 
1827 	assert(size != 0);
1828 
1829 	bzero(&myref, sizeof (myref));
1830 	myref.enclosing = encl_ref;
1831 	myref.stream = encl_ref->stream;
1832 	myref.packed_alignment = 0;
1833 	myref.ti = type_under;
1834 	myref.inner_flags = NDR_F_NONE;
1835 	myref.name = name;
1836 
1837 	for (i = 0; i < NDR_STRING_MAX; i++) {
1838 		(void) sprintf(name, "[%lu]", i);
1839 		myref.pdu_offset = pdu_offset + i * size;
1840 		valp = encl_ref->datum + i * size;
1841 		myref.datum = valp;
1842 
1843 		if (!mlndr_inner(&myref))
1844 			return (0);
1845 
1846 		switch (size) {
1847 		case 1:		sense = *valp; break;
1848 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
1849 		case 2:		sense = *(short *)valp; break;
1850 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
1851 		case 4:		sense = *(long *)valp; break;
1852 		}
1853 
1854 		if (!sense)
1855 			break;
1856 	}
1857 
1858 	return (1);
1859 }
1860 
1861 
1862 extern int mlndr_s_wchar(struct ndr_reference *encl_ref);
1863 struct ndr_typeinfo ndt_s_wchar = {
1864 	1,		/* NDR version */
1865 	2-1,		/* alignment */
1866 	NDR_F_STRING,	/* flags */
1867 	mlndr_s_wchar,	/* ndr_func */
1868 	0,		/* pdu_size_fixed_part */
1869 	2,		/* pdu_size_variable_part */
1870 	0,		/* c_size_fixed_part */
1871 	1,		/* c_size_variable_part */
1872 };
1873 
1874 
1875 /*
1876  * Hand coded wchar function because all strings are transported
1877  * as wide characters. During NDR_M_OP_MARSHALL, we convert from
1878  * multi-byte to wide characters. During NDR_M_OP_UNMARSHALL, we
1879  * convert from wide characters to multi-byte.
1880  *
1881  * It appeared that NT would sometimes leave a spurious character
1882  * in the data stream before the null wide_char, which would get
1883  * included in the string decode because we processed until the
1884  * null character. It now looks like NT does not always terminate
1885  * RPC Unicode strings and the terminating null is a side effect
1886  * of field alignment. So now we rely on the strlen_is (set up in
1887  * mlndr_outer_string) of the enclosing reference. This may or may
1888  * not include the null but it doesn't matter, the algorithm will
1889  * get it right.
1890  */
1891 int
1892 mlndr_s_wchar(struct ndr_reference *encl_ref)
1893 {
1894 	struct mlndr_stream 	*mlnds = encl_ref->stream;
1895 	unsigned short		wide_char;
1896 	char 			*valp;
1897 	struct ndr_reference	myref;
1898 	unsigned long		i;
1899 	char			name[30];
1900 	int			count;
1901 	int			char_count = 0;
1902 
1903 	if (mlnds->m_op == NDR_M_OP_UNMARSHALL) {
1904 		/*
1905 		 * To avoid problems with zero length strings
1906 		 * we can just null terminate here and be done.
1907 		 */
1908 		if (encl_ref->strlen_is == 0) {
1909 			encl_ref->datum[0] = '\0';
1910 			return (1);
1911 		}
1912 	}
1913 
1914 	bzero(&myref, sizeof (myref));
1915 	myref.enclosing = encl_ref;
1916 	myref.stream = encl_ref->stream;
1917 	myref.packed_alignment = 0;
1918 	myref.ti = &ndt__wchar;
1919 	myref.inner_flags = NDR_F_NONE;
1920 	myref.datum = (char *)&wide_char;
1921 	myref.name = name;
1922 	myref.pdu_offset = encl_ref->pdu_offset;
1923 
1924 	valp = encl_ref->datum;
1925 	count = 0;
1926 
1927 	for (i = 0; i < NDR_STRING_MAX; i++) {
1928 		(void) sprintf(name, "[%lu]", i);
1929 
1930 		if (mlnds->m_op == NDR_M_OP_MARSHALL) {
1931 			count = mts_mbtowc((mts_wchar_t *)&wide_char, valp,
1932 			    MTS_MB_CHAR_MAX);
1933 			if (count < 0) {
1934 				return (0);
1935 			} else if (count == 0) {
1936 				if (encl_ref->strlen_is != encl_ref->size_is)
1937 					break;
1938 
1939 				/*
1940 				 * If the input char is 0, mbtowc
1941 				 * returns 0 without setting wide_char.
1942 				 * Set wide_char to 0 and a count of 1.
1943 				 */
1944 				wide_char = *valp;
1945 				count = 1;
1946 			}
1947 		}
1948 
1949 		if (!mlndr_inner(&myref))
1950 			return (0);
1951 
1952 		if (mlnds->m_op == NDR_M_OP_UNMARSHALL) {
1953 			count = mts_wctomb(valp, wide_char);
1954 
1955 			if ((++char_count) == encl_ref->strlen_is) {
1956 				valp += count;
1957 				*valp = '\0';
1958 				break;
1959 			}
1960 		}
1961 
1962 		if (!wide_char)
1963 			break;
1964 
1965 		myref.pdu_offset += sizeof (wide_char);
1966 		valp += count;
1967 	}
1968 
1969 	return (1);
1970 }
1971 
1972 /*
1973  * Converts a multibyte character string to a little-endian, wide-char
1974  * string.  No more than nwchars wide characters are stored.
1975  * A terminating null wide character is appended if there is room.
1976  *
1977  * Returns the number of wide characters converted, not counting
1978  * any terminating null wide character.  Returns -1 if an invalid
1979  * multibyte character is encountered.
1980  */
1981 size_t
1982 ndr_mbstowcs(struct mlndr_stream *mlnds, mts_wchar_t *wcs, const char *mbs,
1983     size_t nwchars)
1984 {
1985 	mts_wchar_t *start = wcs;
1986 	int nbytes;
1987 
1988 	while (nwchars--) {
1989 		nbytes = ndr_mbtowc(mlnds, wcs, mbs, MTS_MB_CHAR_MAX);
1990 		if (nbytes < 0) {
1991 			*wcs = 0;
1992 			return ((size_t)-1);
1993 		}
1994 
1995 		if (*mbs == 0)
1996 			break;
1997 
1998 		++wcs;
1999 		mbs += nbytes;
2000 	}
2001 
2002 	return (wcs - start);
2003 }
2004 
2005 /*
2006  * Converts a multibyte character to a little-endian, wide-char, which
2007  * is stored in wcharp.  Up to nbytes bytes are examined.
2008  *
2009  * If mbchar is valid, returns the number of bytes processed in mbchar.
2010  * If mbchar is invalid, returns -1.  See also mts_mbtowc().
2011  */
2012 /*ARGSUSED*/
2013 int
2014 ndr_mbtowc(struct mlndr_stream *mlnds, mts_wchar_t *wcharp,
2015     const char *mbchar, size_t nbytes)
2016 {
2017 	int rc;
2018 
2019 	if ((rc = mts_mbtowc(wcharp, mbchar, nbytes)) < 0)
2020 		return (rc);
2021 
2022 #ifdef _BIG_ENDIAN
2023 	if (mlnds == NULL || NDR_MODE_MATCH(mlnds, NDR_MODE_RETURN_SEND))
2024 		*wcharp = BSWAP_16(*wcharp);
2025 #endif
2026 
2027 	return (rc);
2028 }
2029