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