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