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
50extern 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
226static ndr_ref_t *ndr_enter_outer_queue(ndr_ref_t *);
227extern 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 */
242int
243ndo_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
256int
257ndo_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
277int
278ndr_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
288int
289ndr_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
403static ndr_ref_t *
404ndr_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
433int
434ndr_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 */
587int
588ndr_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
651int
652ndr_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
741int
742ndr_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
832int
833ndr_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
960int
961ndr_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
1092int
1093ndr_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
1161int
1162ndr_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
1387int
1388ndr_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
1422int
1423ndr_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 */
1462int
1463ndr_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
1504int
1505ndr_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 */
1560int
1561ndr_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
1632int
1633ndr_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
1684int
1685ndr_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
1731int
1732ndr_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
1811int ndr_basic_integer(ndr_ref_t *, unsigned);
1812int 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. */
1816MAKE_BASIC_TYPE(_char, 1)	/* ndt__char,  ndt_s_char */
1817MAKE_BASIC_TYPE(_uchar, 1)	/* ndt__uchar, ndt_s_uchar */
1818MAKE_BASIC_TYPE(_short, 2)	/* ndt__short, ndt_s_short */
1819MAKE_BASIC_TYPE(_ushort, 2)	/* ndt__ushort, ndt_s_ushort */
1820MAKE_BASIC_TYPE(_long, 4)	/* ndt__long,  ndt_s_long */
1821MAKE_BASIC_TYPE(_ulong, 4)	/* ndt__ulong, ndt_s_ulong */
1822
1823MAKE_BASIC_TYPE_BASE(_wchar, 2)	/* ndt__wchar, ndt_s_wchar */
1824/* END CSTYLED */
1825
1826int
1827ndr_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
1852int
1853ndr_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
1898extern int ndr_s_wchar(ndr_ref_t *encl_ref);
1899ndr_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 */
1926int
1927ndr_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 */
2020size_t
2021ndr_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