xref: /titanic_41/usr/src/lib/smbsrv/libmlrpc/common/ndr_process.c (revision 6ab3986229f6d7d9a83c94e3a6e68fd05da5cff0)
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  * Copyright 2012 Milan Jurik. All rights reserved.
25  */
26 
27 /*
28  * Network Data Representation (NDR) is a compatible subset of the DCE RPC
29  * and MSRPC NDR.  NDR is used to move parameters consisting of
30  * complicated trees of data constructs between an RPC client and server.
31  */
32 
33 #include <sys/byteorder.h>
34 #include <strings.h>
35 #include <assert.h>
36 #include <string.h>
37 #include <stdlib.h>
38 
39 #include <smbsrv/libsmb.h>
40 #include <smbsrv/string.h>
41 #include <smbsrv/libmlrpc.h>
42 
43 #define	NDR_STRING_MAX		4096
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 			 */
1214 			size_is = smb_wcequiv_strlen(valp) /
1215 			    sizeof (smb_wchar_t);
1216 
1217 			if (!(nds->flags & NDS_F_NONULL))
1218 				++size_is;
1219 
1220 			if (size_is > NDR_STRING_MAX) {
1221 				NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN);
1222 				return (0);
1223 			}
1224 		} else {
1225 			valp = outer_ref->datum;
1226 			n_zeroes = 0;
1227 			for (ix = 0; ix < NDR_STRING_MAX; ix++) {
1228 				if (valp[ix] == 0) {
1229 					n_zeroes++;
1230 					if (n_zeroes >= is_varlen &&
1231 					    ix % is_varlen == 0) {
1232 						break;
1233 					}
1234 				} else {
1235 					n_zeroes = 0;
1236 				}
1237 			}
1238 			if (ix >= NDR_STRING_MAX) {
1239 				NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN);
1240 				return (0);
1241 			}
1242 			size_is = ix+1;
1243 		}
1244 
1245 		first_is = 0;
1246 
1247 		if (nds->flags & NDS_F_NOTERM)
1248 			length_is = size_is - 1;
1249 		else
1250 			length_is = size_is;
1251 
1252 		if (!ndr_outer_poke_sizing(outer_ref, 0, &size_is) ||
1253 		    !ndr_outer_poke_sizing(outer_ref, 4, &first_is) ||
1254 		    !ndr_outer_poke_sizing(outer_ref, 8, &length_is))
1255 			return (0);		/* error already set */
1256 		break;
1257 
1258 	case NDR_M_OP_UNMARSHALL:
1259 		if (!ndr_outer_peek_sizing(outer_ref, 0, &size_is) ||
1260 		    !ndr_outer_peek_sizing(outer_ref, 4, &first_is) ||
1261 		    !ndr_outer_peek_sizing(outer_ref, 8, &length_is))
1262 			return (0);		/* error already set */
1263 
1264 		/*
1265 		 * In addition to the first_is check, we used to check that
1266 		 * size_is or size_is-1 was equal to length_is but Windows95
1267 		 * doesn't conform to this "rule" (see variable part below).
1268 		 * The srvmgr tool for Windows95 sent the following values
1269 		 * for a path string:
1270 		 *
1271 		 *	size_is   = 261 (0x105)
1272 		 *	first_is  = 0
1273 		 *	length_is = 53  (0x35)
1274 		 *
1275 		 * The length_is was correct (for the given path) but the
1276 		 * size_is was the maximum path length rather than being
1277 		 * related to length_is.
1278 		 */
1279 		if (first_is != 0) {
1280 			NDR_SET_ERROR(outer_ref, NDR_ERR_STRING_SIZING);
1281 			return (0);
1282 		}
1283 
1284 		if (ti == &ndt_s_wchar) {
1285 			/*
1286 			 * Decoding Unicode to UTF-8; we need to allow
1287 			 * for the maximum possible char size. It would
1288 			 * be nice to use mbequiv_strlen but the string
1289 			 * may not be null terminated.
1290 			 */
1291 			n_alloc = (size_is + 1) * MTS_MB_CHAR_MAX;
1292 		} else {
1293 			n_alloc = (size_is + 1) * is_varlen;
1294 		}
1295 
1296 		valp = NDS_MALLOC(nds, n_alloc, outer_ref);
1297 		if (!valp) {
1298 			NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
1299 			return (0);
1300 		}
1301 
1302 		bzero(valp, (size_is+1) * is_varlen);
1303 
1304 		if (outer_ref->backptr)
1305 			*outer_ref->backptr = valp;
1306 		outer_ref->datum = valp;
1307 		break;
1308 
1309 	default:
1310 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1311 		return (0);
1312 	}
1313 
1314 	/*
1315 	 * Variable part - exactly length_is of these.
1316 	 *
1317 	 * Usually, length_is is same as size_is and includes nul.
1318 	 * Some protocols use length_is = size_is-1, and length_is does
1319 	 * not include the nul (which is more consistent with DCE spec).
1320 	 * If the length_is is 0, there is no data following the
1321 	 * sizing header, regardless of size_is.
1322 	 */
1323 	n_variable = length_is * is_varlen;
1324 
1325 	/* sum them up to determine the PDU space required */
1326 	n_pdu_total = n_hdr + n_fixed + n_variable;
1327 
1328 	/* similar sum to determine how much local memory is required */
1329 	n_alloc = n_fixed + n_variable;
1330 
1331 	rc = ndr_outer_grow(outer_ref, n_pdu_total);
1332 	if (!rc)
1333 		return (rc);		/* error already set */
1334 
1335 	if (length_is > 0) {
1336 		bzero(&myref, sizeof (myref));
1337 		myref.stream = nds;
1338 		myref.enclosing = outer_ref;
1339 		myref.ti = outer_ref->ti;
1340 		myref.datum = outer_ref->datum;
1341 		myref.name = "OUTER-STRING";
1342 		myref.outer_flags = NDR_F_IS_STRING;
1343 		myref.inner_flags = NDR_F_NONE;
1344 
1345 		/*
1346 		 * Set up size_is and strlen_is for ndr_s_wchar.
1347 		 */
1348 		myref.size_is = size_is;
1349 		myref.strlen_is = length_is;
1350 	}
1351 
1352 	myref.pdu_offset = outer_ref->pdu_offset + 12;
1353 
1354 	/*
1355 	 * Don't try to decode empty strings.
1356 	 */
1357 	if ((size_is == 0) && (first_is == 0) && (length_is == 0)) {
1358 		nds->pdu_scan_offset = outer_ref->pdu_end_offset;
1359 		return (1);
1360 	}
1361 
1362 	if ((size_is != 0) && (length_is != 0)) {
1363 		rc = ndr_inner(&myref);
1364 		if (!rc)
1365 			return (rc);		/* error already set */
1366 	}
1367 
1368 	nds->pdu_scan_offset = outer_ref->pdu_end_offset;
1369 	return (1);
1370 }
1371 
1372 int
1373 ndr_outer_peek_sizing(ndr_ref_t *outer_ref, unsigned offset,
1374     unsigned long *sizing_p)
1375 {
1376 	ndr_stream_t 	*nds = outer_ref->stream;
1377 	unsigned long	pdu_offset;
1378 	int		rc;
1379 
1380 	pdu_offset = outer_ref->pdu_offset + offset;
1381 
1382 	if (pdu_offset < nds->outer_current->pdu_offset ||
1383 	    pdu_offset > nds->outer_current->pdu_end_offset ||
1384 	    pdu_offset+4 > nds->outer_current->pdu_end_offset) {
1385 		NDR_SET_ERROR(outer_ref, NDR_ERR_BOUNDS_CHECK);
1386 		return (0);
1387 	}
1388 
1389 	switch (nds->m_op) {
1390 	case NDR_M_OP_MARSHALL:
1391 		NDR_SET_ERROR(outer_ref, NDR_ERR_UNIMPLEMENTED);
1392 		return (0);
1393 
1394 	case NDR_M_OP_UNMARSHALL:
1395 		rc = NDS_GET_PDU(nds, pdu_offset, 4, (char *)sizing_p,
1396 		    nds->swap, outer_ref);
1397 		break;
1398 
1399 	default:
1400 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1401 		return (0);
1402 	}
1403 
1404 	return (rc);
1405 }
1406 
1407 int
1408 ndr_outer_poke_sizing(ndr_ref_t *outer_ref, unsigned offset,
1409     unsigned long *sizing_p)
1410 {
1411 	ndr_stream_t 	*nds = outer_ref->stream;
1412 	unsigned long	pdu_offset;
1413 	int		rc;
1414 
1415 	pdu_offset = outer_ref->pdu_offset + offset;
1416 
1417 	if (pdu_offset < nds->outer_current->pdu_offset ||
1418 	    pdu_offset > nds->outer_current->pdu_end_offset ||
1419 	    pdu_offset+4 > nds->outer_current->pdu_end_offset) {
1420 		NDR_SET_ERROR(outer_ref, NDR_ERR_BOUNDS_CHECK);
1421 		return (0);
1422 	}
1423 
1424 	switch (nds->m_op) {
1425 	case NDR_M_OP_MARSHALL:
1426 		rc = NDS_PUT_PDU(nds, pdu_offset, 4, (char *)sizing_p,
1427 		    nds->swap, outer_ref);
1428 		break;
1429 
1430 	case NDR_M_OP_UNMARSHALL:
1431 		NDR_SET_ERROR(outer_ref, NDR_ERR_UNIMPLEMENTED);
1432 		return (0);
1433 
1434 	default:
1435 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1436 		return (0);
1437 	}
1438 
1439 	return (rc);
1440 }
1441 
1442 /*
1443  * All OUTER constructs begin on a mod4 (dword) boundary - except
1444  * for the ones that don't: some MSRPC calls appear to use word or
1445  * packed alignment.  Strings appear to be dword aligned.
1446  */
1447 int
1448 ndr_outer_align(ndr_ref_t *outer_ref)
1449 {
1450 	ndr_stream_t 	*nds = outer_ref->stream;
1451 	int		rc;
1452 	unsigned	n_pad;
1453 	unsigned	align;
1454 
1455 	if (outer_ref->packed_alignment && outer_ref->ti != &ndt_s_wchar) {
1456 		align = outer_ref->ti->alignment;
1457 		n_pad = ((align + 1) - nds->pdu_scan_offset) & align;
1458 	} else {
1459 		n_pad = NDR_ALIGN4(nds->pdu_scan_offset);
1460 	}
1461 
1462 	if (n_pad == 0)
1463 		return (1);	/* already aligned, often the case */
1464 
1465 	if (!ndr_outer_grow(outer_ref, n_pad))
1466 		return (0);	/* error already set */
1467 
1468 	switch (nds->m_op) {
1469 	case NDR_M_OP_MARSHALL:
1470 		rc = NDS_PAD_PDU(nds, nds->pdu_scan_offset, n_pad, outer_ref);
1471 		if (!rc) {
1472 			NDR_SET_ERROR(outer_ref, NDR_ERR_PAD_FAILED);
1473 			return (0);
1474 		}
1475 		break;
1476 
1477 	case NDR_M_OP_UNMARSHALL:
1478 		break;
1479 
1480 	default:
1481 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1482 		return (0);
1483 	}
1484 
1485 	nds->pdu_scan_offset += n_pad;
1486 	return (1);
1487 }
1488 
1489 int
1490 ndr_outer_grow(ndr_ref_t *outer_ref, unsigned n_total)
1491 {
1492 	ndr_stream_t 	*nds = outer_ref->stream;
1493 	unsigned long	pdu_want_size;
1494 	int		rc, is_ok = 0;
1495 
1496 	pdu_want_size = nds->pdu_scan_offset + n_total;
1497 
1498 	if (pdu_want_size <= nds->pdu_max_size) {
1499 		is_ok = 1;
1500 	}
1501 
1502 	switch (nds->m_op) {
1503 	case NDR_M_OP_MARSHALL:
1504 		if (is_ok)
1505 			break;
1506 		rc = NDS_GROW_PDU(nds, pdu_want_size, outer_ref);
1507 		if (!rc) {
1508 			NDR_SET_ERROR(outer_ref, NDR_ERR_GROW_FAILED);
1509 			return (0);
1510 		}
1511 		break;
1512 
1513 	case NDR_M_OP_UNMARSHALL:
1514 		if (is_ok)
1515 			break;
1516 		NDR_SET_ERROR(outer_ref, NDR_ERR_UNDERFLOW);
1517 		return (0);
1518 
1519 	default:
1520 		NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1521 		return (0);
1522 	}
1523 
1524 	if (nds->pdu_size < pdu_want_size)
1525 		nds->pdu_size = pdu_want_size;
1526 
1527 	outer_ref->pdu_end_offset = pdu_want_size;
1528 	return (1);
1529 }
1530 
1531 /*
1532  * INNER ELEMENTS
1533  *
1534  * The local datum (arg_ref->datum) already exists, there is no need to
1535  * malloc() it.  The datum should point at a member of a structure.
1536  *
1537  * For the most part, ndr_inner() and its helpers are just a sanity
1538  * check.  The underlying ti->ndr_func() could be called immediately
1539  * for non-pointer elements.  For the sake of robustness, we detect
1540  * run-time errors here.  Most of the situations this protects against
1541  * have already been checked by the IDL compiler.  This is also a
1542  * common point for processing of all data, and so is a convenient
1543  * place to work from for debugging.
1544  */
1545 int
1546 ndr_inner(ndr_ref_t *arg_ref)
1547 {
1548 	ndr_typeinfo_t 	*ti = arg_ref->ti;
1549 	int	is_varlen = ti->pdu_size_variable_part;
1550 	int	is_union = NDR_IS_UNION(ti);
1551 	int	error = NDR_ERR_INNER_PARAMS_BAD;
1552 	int	params;
1553 
1554 	params = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
1555 
1556 	switch (params) {
1557 	case NDR_F_NONE:
1558 		if (is_union) {
1559 			error = NDR_ERR_SWITCH_VALUE_MISSING;
1560 			break;
1561 		}
1562 		return (*ti->ndr_func)(arg_ref);
1563 
1564 	case NDR_F_SIZE_IS:
1565 	case NDR_F_DIMENSION_IS:
1566 	case NDR_F_IS_POINTER+NDR_F_SIZE_IS:   /* pointer to something */
1567 	case NDR_F_IS_REFERENCE+NDR_F_SIZE_IS: /* pointer to something */
1568 		if (is_varlen) {
1569 			error = NDR_ERR_ARRAY_VARLEN_ILLEGAL;
1570 			break;
1571 		}
1572 		if (is_union) {
1573 			error = NDR_ERR_ARRAY_UNION_ILLEGAL;
1574 			break;
1575 		}
1576 		if (params & NDR_F_IS_POINTER)
1577 			return (ndr_inner_pointer(arg_ref));
1578 		else if (params & NDR_F_IS_REFERENCE)
1579 			return (ndr_inner_reference(arg_ref));
1580 		else
1581 			return (ndr_inner_array(arg_ref));
1582 
1583 	case NDR_F_IS_POINTER:	/* type is pointer to one something */
1584 		if (is_union) {
1585 			error = NDR_ERR_ARRAY_UNION_ILLEGAL;
1586 			break;
1587 		}
1588 		return (ndr_inner_pointer(arg_ref));
1589 
1590 	case NDR_F_IS_REFERENCE:	/* type is pointer to one something */
1591 		if (is_union) {
1592 			error = NDR_ERR_ARRAY_UNION_ILLEGAL;
1593 			break;
1594 		}
1595 		return (ndr_inner_reference(arg_ref));
1596 
1597 	case NDR_F_SWITCH_IS:
1598 		if (!is_union) {
1599 			error = NDR_ERR_SWITCH_VALUE_ILLEGAL;
1600 			break;
1601 		}
1602 		return (*ti->ndr_func)(arg_ref);
1603 
1604 	default:
1605 		error = NDR_ERR_INNER_PARAMS_BAD;
1606 		break;
1607 	}
1608 
1609 	/*
1610 	 * If we get here, something is wrong. Most likely,
1611 	 * the params flags do not match
1612 	 */
1613 	NDR_SET_ERROR(arg_ref, error);
1614 	return (0);
1615 }
1616 
1617 int
1618 ndr_inner_pointer(ndr_ref_t *arg_ref)
1619 {
1620 	ndr_stream_t	*nds = arg_ref->stream;
1621 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
1622 	char 		**valpp = (char **)arg_ref->datum;
1623 	ndr_ref_t 	*outer_ref;
1624 
1625 	if (!ndr__ulong(arg_ref))
1626 		return (0);	/* error */
1627 	if (!*valpp)
1628 		return (1);	/* NULL pointer */
1629 
1630 	outer_ref = ndr_enter_outer_queue(arg_ref);
1631 	if (!outer_ref)
1632 		return (0);	/* error already set */
1633 
1634 	/*
1635 	 * Move advice in inner_flags to outer_flags.
1636 	 * Retain pointer flag for conformant arrays.
1637 	 */
1638 	outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
1639 	if ((outer_ref->outer_flags & NDR_F_SIZE_IS) == 0)
1640 		outer_ref->outer_flags &= ~NDR_F_IS_POINTER;
1641 #ifdef NDR_INNER_PTR_NOT_YET
1642 	outer_ref->outer_flags |= NDR_F_BACKPTR;
1643 	if (outer_ref->outer_flags & NDR_F_SIZE_IS) {
1644 		outer_ref->outer_flags |= NDR_F_ARRAY+NDR_F_CONFORMANT;
1645 	}
1646 #endif /* NDR_INNER_PTR_NOT_YET */
1647 
1648 	outer_ref->backptr = valpp;
1649 
1650 	switch (nds->m_op) {
1651 	case NDR_M_OP_MARSHALL:
1652 		outer_ref->datum = *valpp;
1653 		break;
1654 
1655 	case NDR_M_OP_UNMARSHALL:
1656 		/*
1657 		 * This is probably wrong if the application allocated
1658 		 * memory in advance.  Indicate no value for now.
1659 		 * ONC RPC handles this case.
1660 		 */
1661 		*valpp = 0;
1662 		outer_ref->datum = 0;
1663 		break;
1664 	}
1665 
1666 	return (1);		/* pointer dereference scheduled */
1667 }
1668 
1669 int
1670 ndr_inner_reference(ndr_ref_t *arg_ref)
1671 {
1672 	ndr_stream_t	*nds = arg_ref->stream;
1673 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
1674 	char		**valpp = (char **)arg_ref->datum;
1675 	ndr_ref_t	*outer_ref;
1676 
1677 	outer_ref = ndr_enter_outer_queue(arg_ref);
1678 	if (!outer_ref)
1679 		return (0);	/* error already set */
1680 
1681 	/*
1682 	 * Move advice in inner_flags to outer_flags.
1683 	 * Retain reference flag for conformant arrays.
1684 	 */
1685 	outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
1686 	if ((outer_ref->outer_flags & NDR_F_SIZE_IS) == 0)
1687 		outer_ref->outer_flags &= ~NDR_F_IS_REFERENCE;
1688 #ifdef NDR_INNER_REF_NOT_YET
1689 	outer_ref->outer_flags |= NDR_F_BACKPTR;
1690 	if (outer_ref->outer_flags & NDR_F_SIZE_IS) {
1691 		outer_ref->outer_flags |= NDR_F_ARRAY+NDR_F_CONFORMANT;
1692 	}
1693 #endif /* NDR_INNER_REF_NOT_YET */
1694 
1695 	outer_ref->backptr = valpp;
1696 
1697 	switch (nds->m_op) {
1698 	case NDR_M_OP_MARSHALL:
1699 		outer_ref->datum = *valpp;
1700 		break;
1701 
1702 	case NDR_M_OP_UNMARSHALL:
1703 		/*
1704 		 * This is probably wrong if the application allocated
1705 		 * memory in advance.  Indicate no value for now.
1706 		 * ONC RPC handles this case.
1707 		 */
1708 		*valpp = 0;
1709 		outer_ref->datum = 0;
1710 		break;
1711 	}
1712 
1713 	return (1);		/* pointer dereference scheduled */
1714 }
1715 
1716 int
1717 ndr_inner_array(ndr_ref_t *encl_ref)
1718 {
1719 	ndr_typeinfo_t		*ti = encl_ref->ti;
1720 	ndr_ref_t		myref;
1721 	unsigned long		pdu_offset = encl_ref->pdu_offset;
1722 	unsigned long		n_elem;
1723 	unsigned long		i;
1724 	char			name[30];
1725 
1726 	if (encl_ref->inner_flags & NDR_F_SIZE_IS) {
1727 		/* now is the time to check/set size */
1728 		if (!ndr_size_is(encl_ref))
1729 			return (0);	/* error already set */
1730 		n_elem = encl_ref->size_is;
1731 	} else {
1732 		assert(encl_ref->inner_flags & NDR_F_DIMENSION_IS);
1733 		n_elem = encl_ref->dimension_is;
1734 	}
1735 
1736 	bzero(&myref, sizeof (myref));
1737 	myref.enclosing = encl_ref;
1738 	myref.stream = encl_ref->stream;
1739 	myref.packed_alignment = 0;
1740 	myref.ti = ti;
1741 	myref.inner_flags = NDR_F_NONE;
1742 
1743 	for (i = 0; i < n_elem; i++) {
1744 		(void) sprintf(name, "[%lu]", i);
1745 		myref.name = name;
1746 		myref.pdu_offset = pdu_offset + i * ti->pdu_size_fixed_part;
1747 		myref.datum = encl_ref->datum + i * ti->c_size_fixed_part;
1748 
1749 		if (!ndr_inner(&myref))
1750 			return (0);
1751 	}
1752 
1753 	return (1);
1754 }
1755 
1756 
1757 /*
1758  * BASIC TYPES
1759  */
1760 #define	MAKE_BASIC_TYPE_BASE(TYPE, SIZE) \
1761     extern int ndr_##TYPE(struct ndr_reference *encl_ref); \
1762     ndr_typeinfo_t ndt_##TYPE = { \
1763 	1,		/* NDR version */ \
1764 	(SIZE)-1,	/* alignment */ \
1765 	NDR_F_NONE,	/* flags */ \
1766 	ndr_##TYPE,	/* ndr_func */ \
1767 	SIZE,		/* pdu_size_fixed_part */ \
1768 	0,		/* pdu_size_variable_part */ \
1769 	SIZE,		/* c_size_fixed_part */ \
1770 	0,		/* c_size_variable_part */ \
1771 	}; \
1772     int ndr_##TYPE(struct ndr_reference *ref) { \
1773 	return (ndr_basic_integer(ref, SIZE)); \
1774 }
1775 
1776 #define	MAKE_BASIC_TYPE_STRING(TYPE, SIZE) \
1777     extern int ndr_s##TYPE(struct ndr_reference *encl_ref); \
1778     ndr_typeinfo_t ndt_s##TYPE = { \
1779 	1,		/* NDR version */ \
1780 	(SIZE)-1,	/* alignment */ \
1781 	NDR_F_STRING,	/* flags */ \
1782 	ndr_s##TYPE,	/* ndr_func */ \
1783 	0,		/* pdu_size_fixed_part */ \
1784 	SIZE,		/* pdu_size_variable_part */ \
1785 	0,		/* c_size_fixed_part */ \
1786 	SIZE,		/* c_size_variable_part */ \
1787 	}; \
1788     int ndr_s##TYPE(struct ndr_reference *ref) { \
1789 	return (ndr_string_basic_integer(ref, &ndt_##TYPE)); \
1790 }
1791 
1792 #define	MAKE_BASIC_TYPE(TYPE, SIZE) \
1793 	MAKE_BASIC_TYPE_BASE(TYPE, SIZE) \
1794 	MAKE_BASIC_TYPE_STRING(TYPE, SIZE)
1795 
1796 int ndr_basic_integer(ndr_ref_t *, unsigned);
1797 int ndr_string_basic_integer(ndr_ref_t *, ndr_typeinfo_t *);
1798 
1799 
1800 MAKE_BASIC_TYPE(_char, 1)
1801 MAKE_BASIC_TYPE(_uchar, 1)
1802 MAKE_BASIC_TYPE(_short, 2)
1803 MAKE_BASIC_TYPE(_ushort, 2)
1804 MAKE_BASIC_TYPE(_long, 4)
1805 MAKE_BASIC_TYPE(_ulong, 4)
1806 
1807 MAKE_BASIC_TYPE_BASE(_wchar, 2)
1808 
1809 int
1810 ndr_basic_integer(ndr_ref_t *ref, unsigned size)
1811 {
1812 	ndr_stream_t 	*nds = ref->stream;
1813 	char 		*valp = (char *)ref->datum;
1814 	int		rc;
1815 
1816 	switch (nds->m_op) {
1817 	case NDR_M_OP_MARSHALL:
1818 		rc = NDS_PUT_PDU(nds, ref->pdu_offset, size,
1819 		    valp, nds->swap, ref);
1820 		break;
1821 
1822 	case NDR_M_OP_UNMARSHALL:
1823 		rc = NDS_GET_PDU(nds, ref->pdu_offset, size,
1824 		    valp, nds->swap, ref);
1825 		break;
1826 
1827 	default:
1828 		NDR_SET_ERROR(ref, NDR_ERR_M_OP_INVALID);
1829 		return (0);
1830 	}
1831 
1832 	return (rc);
1833 }
1834 
1835 int
1836 ndr_string_basic_integer(ndr_ref_t *encl_ref, ndr_typeinfo_t *type_under)
1837 {
1838 	unsigned long		pdu_offset = encl_ref->pdu_offset;
1839 	unsigned		size = type_under->pdu_size_fixed_part;
1840 	char			*valp;
1841 	ndr_ref_t		myref;
1842 	unsigned long		i;
1843 	long			sense = 0;
1844 	char			name[30];
1845 
1846 	assert(size != 0);
1847 
1848 	bzero(&myref, sizeof (myref));
1849 	myref.enclosing = encl_ref;
1850 	myref.stream = encl_ref->stream;
1851 	myref.packed_alignment = 0;
1852 	myref.ti = type_under;
1853 	myref.inner_flags = NDR_F_NONE;
1854 	myref.name = name;
1855 
1856 	for (i = 0; i < NDR_STRING_MAX; i++) {
1857 		(void) sprintf(name, "[%lu]", i);
1858 		myref.pdu_offset = pdu_offset + i * size;
1859 		valp = encl_ref->datum + i * size;
1860 		myref.datum = valp;
1861 
1862 		if (!ndr_inner(&myref))
1863 			return (0);
1864 
1865 		switch (size) {
1866 		case 1:		sense = *valp; break;
1867 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
1868 		case 2:		sense = *(short *)valp; break;
1869 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
1870 		case 4:		sense = *(long *)valp; break;
1871 		}
1872 
1873 		if (!sense)
1874 			break;
1875 	}
1876 
1877 	return (1);
1878 }
1879 
1880 
1881 extern int ndr_s_wchar(ndr_ref_t *encl_ref);
1882 ndr_typeinfo_t ndt_s_wchar = {
1883 	1,		/* NDR version */
1884 	2-1,		/* alignment */
1885 	NDR_F_STRING,	/* flags */
1886 	ndr_s_wchar,	/* ndr_func */
1887 	0,		/* pdu_size_fixed_part */
1888 	2,		/* pdu_size_variable_part */
1889 	0,		/* c_size_fixed_part */
1890 	1,		/* c_size_variable_part */
1891 };
1892 
1893 
1894 /*
1895  * Hand coded wchar function because all strings are transported
1896  * as wide characters. During NDR_M_OP_MARSHALL, we convert from
1897  * multi-byte to wide characters. During NDR_M_OP_UNMARSHALL, we
1898  * convert from wide characters to multi-byte.
1899  *
1900  * It appeared that NT would sometimes leave a spurious character
1901  * in the data stream before the null wide_char, which would get
1902  * included in the string decode because we processed until the
1903  * null character. It now looks like NT does not always terminate
1904  * RPC Unicode strings and the terminating null is a side effect
1905  * of field alignment. So now we rely on the strlen_is (set up in
1906  * ndr_outer_string) of the enclosing reference. This may or may
1907  * not include the null but it doesn't matter, the algorithm will
1908  * get it right.
1909  */
1910 int
1911 ndr_s_wchar(ndr_ref_t *encl_ref)
1912 {
1913 	ndr_stream_t		*nds = encl_ref->stream;
1914 	unsigned short		wide_char;
1915 	char 			*valp;
1916 	ndr_ref_t		myref;
1917 	unsigned long		i;
1918 	char			name[30];
1919 	int			count;
1920 	int			char_count = 0;
1921 
1922 	if (nds->m_op == NDR_M_OP_UNMARSHALL) {
1923 		/*
1924 		 * To avoid problems with zero length strings
1925 		 * we can just null terminate here and be done.
1926 		 */
1927 		if (encl_ref->strlen_is == 0) {
1928 			encl_ref->datum[0] = '\0';
1929 			return (1);
1930 		}
1931 	}
1932 
1933 	bzero(&myref, sizeof (myref));
1934 	myref.enclosing = encl_ref;
1935 	myref.stream = encl_ref->stream;
1936 	myref.packed_alignment = 0;
1937 	myref.ti = &ndt__wchar;
1938 	myref.inner_flags = NDR_F_NONE;
1939 	myref.datum = (char *)&wide_char;
1940 	myref.name = name;
1941 	myref.pdu_offset = encl_ref->pdu_offset;
1942 
1943 	valp = encl_ref->datum;
1944 	count = 0;
1945 
1946 	for (i = 0; i < NDR_STRING_MAX; i++) {
1947 		(void) sprintf(name, "[%lu]", i);
1948 
1949 		if (nds->m_op == NDR_M_OP_MARSHALL) {
1950 			count = smb_mbtowc((smb_wchar_t *)&wide_char, valp,
1951 			    MTS_MB_CHAR_MAX);
1952 			if (count < 0) {
1953 				return (0);
1954 			} else if (count == 0) {
1955 				if (encl_ref->strlen_is != encl_ref->size_is)
1956 					break;
1957 
1958 				/*
1959 				 * If the input char is 0, mbtowc
1960 				 * returns 0 without setting wide_char.
1961 				 * Set wide_char to 0 and a count of 1.
1962 				 */
1963 				wide_char = *valp;
1964 				count = 1;
1965 			}
1966 		}
1967 
1968 		if (!ndr_inner(&myref))
1969 			return (0);
1970 
1971 		if (nds->m_op == NDR_M_OP_UNMARSHALL) {
1972 			count = smb_wctomb(valp, wide_char);
1973 
1974 			if ((++char_count) == encl_ref->strlen_is) {
1975 				valp += count;
1976 				*valp = '\0';
1977 				break;
1978 			}
1979 		}
1980 
1981 		if (!wide_char)
1982 			break;
1983 
1984 		myref.pdu_offset += sizeof (wide_char);
1985 		valp += count;
1986 	}
1987 
1988 	return (1);
1989 }
1990 
1991 /*
1992  * Converts a multibyte character string to a little-endian, wide-char
1993  * string.  No more than nwchars wide characters are stored.
1994  * A terminating null wide character is appended if there is room.
1995  *
1996  * Returns the number of wide characters converted, not counting
1997  * any terminating null wide character.  Returns -1 if an invalid
1998  * multibyte character is encountered.
1999  */
2000 size_t
2001 ndr_mbstowcs(ndr_stream_t *nds, smb_wchar_t *wcs, const char *mbs,
2002     size_t nwchars)
2003 {
2004 	smb_wchar_t *start = wcs;
2005 	int nbytes;
2006 
2007 	while (nwchars--) {
2008 		nbytes = ndr_mbtowc(nds, wcs, mbs, MTS_MB_CHAR_MAX);
2009 		if (nbytes < 0) {
2010 			*wcs = 0;
2011 			return ((size_t)-1);
2012 		}
2013 
2014 		if (*mbs == 0)
2015 			break;
2016 
2017 		++wcs;
2018 		mbs += nbytes;
2019 	}
2020 
2021 	return (wcs - start);
2022 }
2023 
2024 /*
2025  * Converts a multibyte character to a little-endian, wide-char, which
2026  * is stored in wcharp.  Up to nbytes bytes are examined.
2027  *
2028  * If mbchar is valid, returns the number of bytes processed in mbchar.
2029  * If mbchar is invalid, returns -1.  See also smb_mbtowc().
2030  */
2031 /*ARGSUSED*/
2032 int
2033 ndr_mbtowc(ndr_stream_t *nds, smb_wchar_t *wcharp, const char *mbchar,
2034     size_t nbytes)
2035 {
2036 	int rc;
2037 
2038 	if ((rc = smb_mbtowc(wcharp, mbchar, nbytes)) < 0)
2039 		return (rc);
2040 
2041 #ifdef _BIG_ENDIAN
2042 	if (nds == NULL || NDR_MODE_MATCH(nds, NDR_MODE_RETURN_SEND))
2043 		*wcharp = BSWAP_16(*wcharp);
2044 #endif
2045 
2046 	return (rc);
2047 }
2048