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  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
  26  */
  27 
  28 /*
  29  * Network Data Representation (NDR) is a compatible subset of the DCE RPC
  30  * and MSRPC NDR.  NDR is used to move parameters consisting of
  31  * complicated trees of data constructs between an RPC client and server.
  32  */
  33 
  34 #include <sys/byteorder.h>
  35 #include <strings.h>
  36 #include <assert.h>
  37 #include <string.h>
  38 #include <stdio.h>
  39 #include <stdlib.h>
  40 
  41 #include <libmlrpc.h>
  42 #include <ndr_wchar.h>
  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 
 623         case NDR_F_SIZE_IS:
 624         case NDR_F_DIMENSION_IS:
 625         case NDR_F_IS_POINTER+NDR_F_SIZE_IS:
 626         case NDR_F_IS_REFERENCE+NDR_F_SIZE_IS:
 627                 if (is_varlen) {
 628                         error = NDR_ERR_ARRAY_VARLEN_ILLEGAL;
 629                         break;
 630                 }
 631 
 632                 if (params & NDR_F_SIZE_IS)
 633                         return (ndr_outer_conformant_array(outer_ref));
 634                 else
 635                         return (ndr_outer_fixed_array(outer_ref));
 636 
 637         default:
 638                 error = NDR_ERR_OUTER_PARAMS_BAD;
 639                 break;
 640         }
 641 
 642         /*
 643          * If we get here, something is wrong. Most likely,
 644          * the params flags do not match.
 645          */
 646         NDR_SET_ERROR(outer_ref, error);
 647         return (0);
 648 }
 649 
 650 int
 651 ndr_outer_fixed(ndr_ref_t *outer_ref)
 652 {
 653         ndr_stream_t    *nds = outer_ref->stream;
 654         ndr_typeinfo_t  *ti = outer_ref->ti;
 655         ndr_ref_t       myref;
 656         char            *valp = NULL;
 657         int             is_varlen = ti->pdu_size_variable_part;
 658         int             is_union = NDR_IS_UNION(ti);
 659         int             is_string = NDR_IS_STRING(ti);
 660         int             rc;
 661         unsigned        n_hdr;
 662         unsigned        n_fixed;
 663         unsigned        n_variable;
 664         unsigned        n_alloc;
 665         unsigned        n_pdu_total;
 666         int             params;
 667 
 668         params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
 669 
 670         assert(!is_varlen && !is_string && !is_union);
 671         assert(params == NDR_F_NONE);
 672 
 673         /* no header for this */
 674         n_hdr = 0;
 675 
 676         /* fixed part -- exactly one of these */
 677         n_fixed = ti->pdu_size_fixed_part;
 678         assert(n_fixed > 0);
 679 
 680         /* variable part -- exactly none of these */
 681         n_variable = 0;
 682 
 683         /* sum them up to determine the PDU space required */
 684         n_pdu_total = n_hdr + n_fixed + n_variable;
 685 
 686         /* similar sum to determine how much local memory is required */
 687         n_alloc = n_fixed + n_variable;
 688 
 689         rc = ndr_outer_grow(outer_ref, n_pdu_total);
 690         if (!rc)
 691                 return (rc);            /* error already set */
 692 
 693         switch (nds->m_op) {
 694         case NDR_M_OP_MARSHALL:
 695                 valp = outer_ref->datum;
 696                 if (!valp) {
 697                         NDR_SET_ERROR(outer_ref, NDR_ERR_OUTER_PARAMS_BAD);
 698                         return (0);
 699                 }
 700                 if (outer_ref->backptr)
 701                         assert(valp == *outer_ref->backptr);
 702                 break;
 703 
 704         case NDR_M_OP_UNMARSHALL:
 705                 valp = NDS_MALLOC(nds, n_alloc, outer_ref);
 706                 if (!valp) {
 707                         NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
 708                         return (0);
 709                 }
 710                 if (outer_ref->backptr)
 711                         *outer_ref->backptr = valp;
 712                 outer_ref->datum = valp;
 713                 break;
 714 
 715         default:
 716                 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
 717                 return (0);
 718         }
 719 
 720         bzero(&myref, sizeof (myref));
 721         myref.stream = nds;
 722         myref.enclosing = outer_ref;
 723         myref.ti = outer_ref->ti;
 724         myref.datum = outer_ref->datum;
 725         myref.name = "FIXED-VALUE";
 726         myref.outer_flags = NDR_F_NONE;
 727         myref.inner_flags = NDR_F_NONE;
 728 
 729         myref.pdu_offset = outer_ref->pdu_offset;
 730         outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
 731 
 732         rc = ndr_inner(&myref);
 733         if (!rc)
 734                 return (rc);            /* error already set */
 735 
 736         nds->pdu_scan_offset = outer_ref->pdu_end_offset;
 737         return (1);
 738 }
 739 
 740 int
 741 ndr_outer_fixed_array(ndr_ref_t *outer_ref)
 742 {
 743         ndr_stream_t    *nds = outer_ref->stream;
 744         ndr_typeinfo_t  *ti = outer_ref->ti;
 745         ndr_ref_t       myref;
 746         char            *valp = NULL;
 747         int             is_varlen = ti->pdu_size_variable_part;
 748         int             is_union = NDR_IS_UNION(ti);
 749         int             is_string = NDR_IS_STRING(ti);
 750         int             rc;
 751         unsigned        n_hdr;
 752         unsigned        n_fixed;
 753         unsigned        n_variable;
 754         unsigned        n_alloc;
 755         unsigned        n_pdu_total;
 756         int             params;
 757 
 758         params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
 759 
 760         assert(!is_varlen && !is_string && !is_union);
 761         assert(params == NDR_F_DIMENSION_IS);
 762 
 763         /* no header for this */
 764         n_hdr = 0;
 765 
 766         /* fixed part -- exactly dimension_is of these */
 767         n_fixed = ti->pdu_size_fixed_part * outer_ref->dimension_is;
 768         assert(n_fixed > 0);
 769 
 770         /* variable part -- exactly none of these */
 771         n_variable = 0;
 772 
 773         /* sum them up to determine the PDU space required */
 774         n_pdu_total = n_hdr + n_fixed + n_variable;
 775 
 776         /* similar sum to determine how much local memory is required */
 777         n_alloc = n_fixed + n_variable;
 778 
 779         rc = ndr_outer_grow(outer_ref, n_pdu_total);
 780         if (!rc)
 781                 return (rc);            /* error already set */
 782 
 783         switch (nds->m_op) {
 784         case NDR_M_OP_MARSHALL:
 785                 valp = outer_ref->datum;
 786                 if (!valp) {
 787                         NDR_SET_ERROR(outer_ref, NDR_ERR_OUTER_PARAMS_BAD);
 788                         return (0);
 789                 }
 790                 if (outer_ref->backptr)
 791                         assert(valp == *outer_ref->backptr);
 792                 break;
 793 
 794         case NDR_M_OP_UNMARSHALL:
 795                 valp = NDS_MALLOC(nds, n_alloc, outer_ref);
 796                 if (!valp) {
 797                         NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
 798                         return (0);
 799                 }
 800                 if (outer_ref->backptr)
 801                         *outer_ref->backptr = valp;
 802                 outer_ref->datum = valp;
 803                 break;
 804 
 805         default:
 806                 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
 807                 return (0);
 808         }
 809 
 810         bzero(&myref, sizeof (myref));
 811         myref.stream = nds;
 812         myref.enclosing = outer_ref;
 813         myref.ti = outer_ref->ti;
 814         myref.datum = outer_ref->datum;
 815         myref.name = "FIXED-ARRAY";
 816         myref.outer_flags = NDR_F_NONE;
 817         myref.inner_flags = NDR_F_DIMENSION_IS;
 818         myref.dimension_is = outer_ref->dimension_is;
 819 
 820         myref.pdu_offset = outer_ref->pdu_offset;
 821         outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
 822 
 823         rc = ndr_inner(&myref);
 824         if (!rc)
 825                 return (rc);            /* error already set */
 826 
 827         nds->pdu_scan_offset = outer_ref->pdu_end_offset;
 828         return (1);
 829 }
 830 
 831 int
 832 ndr_outer_conformant_array(ndr_ref_t *outer_ref)
 833 {
 834         ndr_stream_t    *nds = outer_ref->stream;
 835         ndr_typeinfo_t  *ti = outer_ref->ti;
 836         ndr_ref_t       myref;
 837         char            *valp = NULL;
 838         int             is_varlen = ti->pdu_size_variable_part;
 839         int             is_union = NDR_IS_UNION(ti);
 840         int             is_string = NDR_IS_STRING(ti);
 841         unsigned long   size_is;
 842         int             rc;
 843         unsigned        n_hdr;
 844         unsigned        n_fixed;
 845         unsigned        n_variable;
 846         unsigned        n_alloc;
 847         unsigned        n_pdu_total;
 848         unsigned        n_ptr_offset;
 849         int             params;
 850 
 851         params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
 852 
 853         assert(!is_varlen && !is_string && !is_union);
 854         assert(params & NDR_F_SIZE_IS);
 855 
 856         /* conformant header for this */
 857         n_hdr = 4;
 858 
 859         /* fixed part -- exactly none of these */
 860         n_fixed = 0;
 861 
 862         /* variable part -- exactly size_of of these */
 863         /* notice that it is the **fixed** size of the ti */
 864         n_variable = ti->pdu_size_fixed_part * outer_ref->size_is;
 865 
 866         /* sum them up to determine the PDU space required */
 867         n_pdu_total = n_hdr + n_fixed + n_variable;
 868 
 869         /* similar sum to determine how much local memory is required */
 870         n_alloc = n_fixed + n_variable;
 871 
 872         rc = ndr_outer_grow(outer_ref, n_pdu_total);
 873         if (!rc)
 874                 return (rc);            /* error already set */
 875 
 876         switch (nds->m_op) {
 877         case NDR_M_OP_MARSHALL:
 878                 size_is = outer_ref->size_is;
 879                 rc = ndr_outer_poke_sizing(outer_ref, 0, &size_is);
 880                 if (!rc)
 881                         return (0);     /* error already set */
 882 
 883                 valp = outer_ref->datum;
 884                 if (!valp) {
 885                         NDR_SET_ERROR(outer_ref, NDR_ERR_OUTER_PARAMS_BAD);
 886                         return (0);
 887                 }
 888                 if (outer_ref->backptr)
 889                         assert(valp == *outer_ref->backptr);
 890                 n_ptr_offset = 4;
 891                 break;
 892 
 893         case NDR_M_OP_UNMARSHALL:
 894                 if (params & NDR_F_IS_REFERENCE) {
 895                         size_is = outer_ref->size_is;
 896                         n_ptr_offset = 0;
 897                 } else {
 898                         /* NDR_F_IS_POINTER */
 899                         rc = ndr_outer_peek_sizing(outer_ref, 0, &size_is);
 900                         if (!rc)
 901                                 return (0);     /* error already set */
 902 
 903                         if (size_is != outer_ref->size_is) {
 904                                 NDR_SET_ERROR(outer_ref,
 905                                     NDR_ERR_SIZE_IS_MISMATCH_PDU);
 906                                 return (0);
 907                         }
 908 
 909                         n_ptr_offset = 4;
 910                 }
 911 
 912                 if (size_is > 0) {
 913                         valp = NDS_MALLOC(nds, n_alloc, outer_ref);
 914                         if (!valp) {
 915                                 NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
 916                                 return (0);
 917                         }
 918                 }
 919 
 920                 if (outer_ref->backptr)
 921                         *outer_ref->backptr = valp;
 922                 outer_ref->datum = valp;
 923                 break;
 924 
 925         default:
 926                 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
 927                 return (0);
 928         }
 929 
 930         outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
 931         outer_ref->type_flags = NDR_F_NONE;
 932         outer_ref->inner_flags = NDR_F_NONE;
 933 
 934         if (size_is > 0) {
 935                 bzero(&myref, sizeof (myref));
 936                 myref.stream = nds;
 937                 myref.enclosing = outer_ref;
 938                 myref.ti = outer_ref->ti;
 939                 myref.datum = outer_ref->datum;
 940                 myref.name = "CONFORMANT-ARRAY";
 941                 myref.outer_flags = NDR_F_NONE;
 942                 myref.inner_flags = NDR_F_SIZE_IS;
 943                 myref.size_is = outer_ref->size_is;
 944 
 945                 myref.inner_flags = NDR_F_DIMENSION_IS;         /* convenient */
 946                 myref.dimension_is = outer_ref->size_is;     /* convenient */
 947 
 948                 myref.pdu_offset = outer_ref->pdu_offset + n_ptr_offset;
 949 
 950                 rc = ndr_inner(&myref);
 951                 if (!rc)
 952                         return (rc);            /* error already set */
 953         }
 954 
 955         nds->pdu_scan_offset = outer_ref->pdu_end_offset;
 956         return (1);
 957 }
 958 
 959 int
 960 ndr_outer_conformant_construct(ndr_ref_t *outer_ref)
 961 {
 962         ndr_stream_t    *nds = outer_ref->stream;
 963         ndr_typeinfo_t  *ti = outer_ref->ti;
 964         ndr_ref_t       myref;
 965         char            *valp = NULL;
 966         int             is_varlen = ti->pdu_size_variable_part;
 967         int             is_union = NDR_IS_UNION(ti);
 968         int             is_string = NDR_IS_STRING(ti);
 969         unsigned long   size_is;
 970         int             rc;
 971         unsigned        n_hdr;
 972         unsigned        n_fixed;
 973         unsigned        n_variable;
 974         unsigned        n_alloc;
 975         unsigned        n_pdu_total;
 976         int             params;
 977 
 978         params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
 979 
 980         assert(is_varlen && !is_string && !is_union);
 981         assert(params == NDR_F_NONE);
 982 
 983         /* conformant header for this */
 984         n_hdr = 4;
 985 
 986         /* fixed part -- exactly one of these */
 987         n_fixed = ti->pdu_size_fixed_part;
 988 
 989         /* variable part -- exactly size_of of these */
 990         n_variable = 0;         /* 0 for the moment */
 991 
 992         /* sum them up to determine the PDU space required */
 993         n_pdu_total = n_hdr + n_fixed + n_variable;
 994 
 995         /* similar sum to determine how much local memory is required */
 996         n_alloc = n_fixed + n_variable;
 997 
 998         /* For the moment, grow enough for the fixed-size part */
 999         rc = ndr_outer_grow(outer_ref, n_pdu_total);
1000         if (!rc)
1001                 return (rc);            /* error already set */
1002 
1003         switch (nds->m_op) {
1004         case NDR_M_OP_MARSHALL:
1005                 /*
1006                  * We don't know the size yet. We have to wait for
1007                  * it. Proceed with the fixed-size part, and await
1008                  * the call to ndr_size_is().
1009                  */
1010                 size_is = 0;
1011                 rc = ndr_outer_poke_sizing(outer_ref, 0, &size_is);
1012                 if (!rc)
1013                         return (0);     /* error already set */
1014 
1015                 valp = outer_ref->datum;
1016                 if (!valp) {
1017                         NDR_SET_ERROR(outer_ref, NDR_ERR_OUTER_PARAMS_BAD);
1018                         return (0);
1019                 }
1020                 if (outer_ref->backptr)
1021                         assert(valp == *outer_ref->backptr);
1022                 break;
1023 
1024         case NDR_M_OP_UNMARSHALL:
1025                 /*
1026                  * We know the size of the variable part because
1027                  * of the CONFORMANT header. We will verify
1028                  * the header against the [size_is(X)] advice
1029                  * later when ndr_size_is() is called.
1030                  */
1031                 rc = ndr_outer_peek_sizing(outer_ref, 0, &size_is);
1032                 if (!rc)
1033                         return (0);     /* error already set */
1034 
1035                 /* recalculate metrics */
1036                 n_variable = size_is * ti->pdu_size_variable_part;
1037                 n_pdu_total = n_hdr + n_fixed + n_variable;
1038                 n_alloc = n_fixed + n_variable;
1039 
1040                 rc = ndr_outer_grow(outer_ref, n_pdu_total);
1041                 if (!rc)
1042                         return (rc);            /* error already set */
1043 
1044                 outer_ref->size_is = size_is; /* verified later */
1045 
1046                 valp = NDS_MALLOC(nds, n_alloc, outer_ref);
1047                 if (!valp) {
1048                         NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
1049                         return (0);
1050                 }
1051                 if (outer_ref->backptr)
1052                         *outer_ref->backptr = valp;
1053                 outer_ref->datum = valp;
1054                 break;
1055 
1056         default:
1057                 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1058                 return (0);
1059         }
1060 
1061         outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
1062         outer_ref->type_flags = NDR_F_SIZE_IS; /* indicate pending */
1063         outer_ref->inner_flags = NDR_F_NONE;   /* indicate pending */
1064 
1065         bzero(&myref, sizeof (myref));
1066         myref.stream = nds;
1067         myref.enclosing = outer_ref;
1068         myref.ti = outer_ref->ti;
1069         myref.datum = outer_ref->datum;
1070         myref.name = "CONFORMANT-CONSTRUCT";
1071         myref.outer_flags = NDR_F_NONE;
1072         myref.inner_flags = NDR_F_NONE;
1073         myref.size_is = outer_ref->size_is;
1074 
1075         myref.pdu_offset = outer_ref->pdu_offset + 4;
1076 
1077         rc = ndr_inner(&myref);
1078         if (!rc)
1079                 return (rc);            /* error already set */
1080 
1081         nds->pdu_scan_offset = outer_ref->pdu_end_offset;
1082 
1083         if (outer_ref->inner_flags != NDR_F_SIZE_IS) {
1084                 NDR_SET_ERROR(&myref, NDR_ERR_SIZE_IS_MISMATCH_AFTER);
1085                 return (0);
1086         }
1087 
1088         return (1);
1089 }
1090 
1091 int
1092 ndr_size_is(ndr_ref_t *ref)
1093 {
1094         ndr_stream_t            *nds = ref->stream;
1095         ndr_ref_t               *outer_ref = nds->outer_current;
1096         ndr_typeinfo_t          *ti = outer_ref->ti;
1097         unsigned long           size_is;
1098         int                     rc;
1099         unsigned                n_hdr;
1100         unsigned                n_fixed;
1101         unsigned                n_variable;
1102         unsigned                n_pdu_total;
1103 
1104         assert(ref->inner_flags & NDR_F_SIZE_IS);
1105         size_is = ref->size_is;
1106 
1107         if (outer_ref->type_flags != NDR_F_SIZE_IS) {
1108                 NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_UNEXPECTED);
1109                 return (0);
1110         }
1111 
1112         if (outer_ref->inner_flags & NDR_F_SIZE_IS) {
1113                 NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_DUPLICATED);
1114                 return (0);
1115         }
1116 
1117         /* repeat metrics, see ndr_conformant_construct() above */
1118         n_hdr = 4;
1119         n_fixed = ti->pdu_size_fixed_part;
1120         n_variable = size_is * ti->pdu_size_variable_part;
1121         n_pdu_total = n_hdr + n_fixed + n_variable;
1122 
1123         rc = ndr_outer_grow(outer_ref, n_pdu_total);
1124         if (!rc)
1125                 return (rc);            /* error already set */
1126 
1127         switch (nds->m_op) {
1128         case NDR_M_OP_MARSHALL:
1129                 /*
1130                  * We have to set the sizing header and extend
1131                  * the size of the PDU (already done).
1132                  */
1133                 rc = ndr_outer_poke_sizing(outer_ref, 0, &size_is);
1134                 if (!rc)
1135                         return (0);     /* error already set */
1136                 break;
1137 
1138         case NDR_M_OP_UNMARSHALL:
1139                 /*
1140                  * Allocation done during ndr_conformant_construct().
1141                  * All we are doing here is verifying that the
1142                  * intended size (ref->size_is) matches the sizing header.
1143                  */
1144                 if (size_is != outer_ref->size_is) {
1145                         NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_MISMATCH_PDU);
1146                         return (0);
1147                 }
1148                 break;
1149 
1150         default:
1151                 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1152                 return (0);
1153         }
1154 
1155         outer_ref->inner_flags |= NDR_F_SIZE_IS;
1156         outer_ref->size_is = ref->size_is;
1157         return (1);
1158 }
1159 
1160 int
1161 ndr_outer_string(ndr_ref_t *outer_ref)
1162 {
1163         ndr_stream_t    *nds = outer_ref->stream;
1164         ndr_typeinfo_t  *ti = outer_ref->ti;
1165         ndr_ref_t       myref;
1166         char            *valp = NULL;
1167         unsigned        is_varlen = ti->pdu_size_variable_part;
1168         int             is_union = NDR_IS_UNION(ti);
1169         int             is_string = NDR_IS_STRING(ti);
1170         int             rc;
1171         unsigned        n_zeroes;
1172         unsigned        ix;
1173         unsigned long   size_is;
1174         unsigned long   first_is;
1175         unsigned long   length_is;
1176         unsigned        n_hdr;
1177         unsigned        n_fixed;
1178         unsigned        n_variable;
1179         unsigned        n_alloc;
1180         unsigned        n_pdu_total;
1181         int             params;
1182 
1183         params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
1184 
1185         assert(is_varlen && is_string && !is_union);
1186         assert(params == NDR_F_NONE);
1187 
1188         /* string header for this: size_is first_is length_is */
1189         n_hdr = 12;
1190 
1191         /* fixed part -- exactly none of these */
1192         n_fixed = 0;
1193 
1194         if (!ndr_outer_grow(outer_ref, n_hdr))
1195                 return (0);             /* error already set */
1196 
1197         switch (nds->m_op) {
1198         case NDR_M_OP_MARSHALL:
1199                 valp = outer_ref->datum;
1200                 if (!valp) {
1201                         NDR_SET_ERROR(outer_ref, NDR_ERR_OUTER_PARAMS_BAD);
1202                         return (0);
1203                 }
1204 
1205                 if (outer_ref->backptr)
1206                         assert(valp == *outer_ref->backptr);
1207 
1208                 if (ti == &ndt_s_wchar) {
1209                         /*
1210                          * size_is is the number of characters in the
1211                          * (multibyte) string, including the null.
1212                          * In other words, symbols, not bytes.
1213                          */
1214                         size_t wlen;
1215                         wlen = ndr__mbstowcs(NULL, valp, NDR_STRING_MAX);
1216                         if (wlen == (size_t)-1) {
1217                                 /* illegal sequence error? */
1218                                 NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN);
1219                                 return (0);
1220                         }
1221                         if ((nds->flags & NDS_F_NONULL) == 0)
1222                                 wlen++;
1223                         if (wlen > NDR_STRING_MAX) {
1224                                 NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN);
1225                                 return (0);
1226                         }
1227                         size_is = wlen;
1228                 } else {
1229                         valp = outer_ref->datum;
1230                         n_zeroes = 0;
1231                         for (ix = 0; ix < NDR_STRING_MAX; ix++) {
1232                                 if (valp[ix] == 0) {
1233                                         n_zeroes++;
1234                                         if (n_zeroes >= is_varlen &&
1235                                             ix % is_varlen == 0) {
1236                                                 break;
1237                                         }
1238                                 } else {
1239                                         n_zeroes = 0;
1240                                 }
1241                         }
1242                         if (ix >= NDR_STRING_MAX) {
1243                                 NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN);
1244                                 return (0);
1245                         }
1246                         size_is = ix+1;
1247                 }
1248 
1249                 first_is = 0;
1250 
1251                 if (nds->flags & NDS_F_NOTERM)
1252                         length_is = size_is - 1;
1253                 else
1254                         length_is = size_is;
1255 
1256                 if (!ndr_outer_poke_sizing(outer_ref, 0, &size_is) ||
1257                     !ndr_outer_poke_sizing(outer_ref, 4, &first_is) ||
1258                     !ndr_outer_poke_sizing(outer_ref, 8, &length_is))
1259                         return (0);             /* error already set */
1260                 break;
1261 
1262         case NDR_M_OP_UNMARSHALL:
1263                 if (!ndr_outer_peek_sizing(outer_ref, 0, &size_is) ||
1264                     !ndr_outer_peek_sizing(outer_ref, 4, &first_is) ||
1265                     !ndr_outer_peek_sizing(outer_ref, 8, &length_is))
1266                         return (0);             /* error already set */
1267 
1268                 /*
1269                  * In addition to the first_is check, we used to check that
1270                  * size_is or size_is-1 was equal to length_is but Windows95
1271                  * doesn't conform to this "rule" (see variable part below).
1272                  * The srvmgr tool for Windows95 sent the following values
1273                  * for a path string:
1274                  *
1275                  *      size_is   = 261 (0x105)
1276                  *      first_is  = 0
1277                  *      length_is = 53  (0x35)
1278                  *
1279                  * The length_is was correct (for the given path) but the
1280                  * size_is was the maximum path length rather than being
1281                  * related to length_is.
1282                  */
1283                 if (first_is != 0) {
1284                         NDR_SET_ERROR(outer_ref, NDR_ERR_STRING_SIZING);
1285                         return (0);
1286                 }
1287 
1288                 if (ti == &ndt_s_wchar) {
1289                         /*
1290                          * Decoding Unicode to UTF-8; we need to allow
1291                          * for the maximum possible char size. It would
1292                          * be nice to use mbequiv_strlen but the string
1293                          * may not be null terminated.
1294                          */
1295                         n_alloc = (size_is + 1) * NDR_MB_CHAR_MAX;
1296                 } else {
1297                         n_alloc = (size_is + 1) * is_varlen;
1298                 }
1299 
1300                 valp = NDS_MALLOC(nds, n_alloc, outer_ref);
1301                 if (!valp) {
1302                         NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
1303                         return (0);
1304                 }
1305 
1306                 bzero(valp, (size_is+1) * is_varlen);
1307 
1308                 if (outer_ref->backptr)
1309                         *outer_ref->backptr = valp;
1310                 outer_ref->datum = valp;
1311                 break;
1312 
1313         default:
1314                 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1315                 return (0);
1316         }
1317 
1318         /*
1319          * Variable part - exactly length_is of these.
1320          *
1321          * Usually, length_is is same as size_is and includes nul.
1322          * Some protocols use length_is = size_is-1, and length_is does
1323          * not include the nul (which is more consistent with DCE spec).
1324          * If the length_is is 0, there is no data following the
1325          * sizing header, regardless of size_is.
1326          */
1327         n_variable = length_is * is_varlen;
1328 
1329         /* sum them up to determine the PDU space required */
1330         n_pdu_total = n_hdr + n_fixed + n_variable;
1331 
1332         /* similar sum to determine how much local memory is required */
1333         n_alloc = n_fixed + n_variable;
1334 
1335         rc = ndr_outer_grow(outer_ref, n_pdu_total);
1336         if (!rc)
1337                 return (rc);            /* error already set */
1338 
1339         if (length_is > 0) {
1340                 bzero(&myref, sizeof (myref));
1341                 myref.stream = nds;
1342                 myref.enclosing = outer_ref;
1343                 myref.ti = outer_ref->ti;
1344                 myref.datum = outer_ref->datum;
1345                 myref.name = "OUTER-STRING";
1346                 myref.outer_flags = NDR_F_IS_STRING;
1347                 myref.inner_flags = NDR_F_NONE;
1348 
1349                 /*
1350                  * Set up size_is and strlen_is for ndr_s_wchar.
1351                  */
1352                 myref.size_is = size_is;
1353                 myref.strlen_is = length_is;
1354         }
1355 
1356         myref.pdu_offset = outer_ref->pdu_offset + 12;
1357 
1358         /*
1359          * Don't try to decode empty strings.
1360          */
1361         if ((size_is == 0) && (first_is == 0) && (length_is == 0)) {
1362                 nds->pdu_scan_offset = outer_ref->pdu_end_offset;
1363                 return (1);
1364         }
1365 
1366         if ((size_is != 0) && (length_is != 0)) {
1367                 rc = ndr_inner(&myref);
1368                 if (!rc)
1369                         return (rc);            /* error already set */
1370         }
1371 
1372         nds->pdu_scan_offset = outer_ref->pdu_end_offset;
1373         return (1);
1374 }
1375 
1376 int
1377 ndr_outer_peek_sizing(ndr_ref_t *outer_ref, unsigned offset,
1378     unsigned long *sizing_p)
1379 {
1380         ndr_stream_t    *nds = outer_ref->stream;
1381         unsigned long   pdu_offset;
1382         int             rc;
1383 
1384         pdu_offset = outer_ref->pdu_offset + offset;
1385 
1386         if (pdu_offset < nds->outer_current->pdu_offset ||
1387             pdu_offset > nds->outer_current->pdu_end_offset ||
1388             pdu_offset+4 > nds->outer_current->pdu_end_offset) {
1389                 NDR_SET_ERROR(outer_ref, NDR_ERR_BOUNDS_CHECK);
1390                 return (0);
1391         }
1392 
1393         switch (nds->m_op) {
1394         case NDR_M_OP_MARSHALL:
1395                 NDR_SET_ERROR(outer_ref, NDR_ERR_UNIMPLEMENTED);
1396                 return (0);
1397 
1398         case NDR_M_OP_UNMARSHALL:
1399                 rc = NDS_GET_PDU(nds, pdu_offset, 4, (char *)sizing_p,
1400                     nds->swap, outer_ref);
1401                 break;
1402 
1403         default:
1404                 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1405                 return (0);
1406         }
1407 
1408         return (rc);
1409 }
1410 
1411 int
1412 ndr_outer_poke_sizing(ndr_ref_t *outer_ref, unsigned offset,
1413     unsigned long *sizing_p)
1414 {
1415         ndr_stream_t    *nds = outer_ref->stream;
1416         unsigned long   pdu_offset;
1417         int             rc;
1418 
1419         pdu_offset = outer_ref->pdu_offset + offset;
1420 
1421         if (pdu_offset < nds->outer_current->pdu_offset ||
1422             pdu_offset > nds->outer_current->pdu_end_offset ||
1423             pdu_offset+4 > nds->outer_current->pdu_end_offset) {
1424                 NDR_SET_ERROR(outer_ref, NDR_ERR_BOUNDS_CHECK);
1425                 return (0);
1426         }
1427 
1428         switch (nds->m_op) {
1429         case NDR_M_OP_MARSHALL:
1430                 rc = NDS_PUT_PDU(nds, pdu_offset, 4, (char *)sizing_p,
1431                     nds->swap, outer_ref);
1432                 break;
1433 
1434         case NDR_M_OP_UNMARSHALL:
1435                 NDR_SET_ERROR(outer_ref, NDR_ERR_UNIMPLEMENTED);
1436                 return (0);
1437 
1438         default:
1439                 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1440                 return (0);
1441         }
1442 
1443         return (rc);
1444 }
1445 
1446 /*
1447  * All OUTER constructs begin on a mod4 (dword) boundary - except
1448  * for the ones that don't: some MSRPC calls appear to use word or
1449  * packed alignment.  Strings appear to be dword aligned.
1450  */
1451 int
1452 ndr_outer_align(ndr_ref_t *outer_ref)
1453 {
1454         ndr_stream_t    *nds = outer_ref->stream;
1455         int             rc;
1456         unsigned        n_pad;
1457         unsigned        align;
1458 
1459         if (outer_ref->packed_alignment && outer_ref->ti != &ndt_s_wchar) {
1460                 align = outer_ref->ti->alignment;
1461                 n_pad = ((align + 1) - nds->pdu_scan_offset) & align;
1462         } else {
1463                 n_pad = NDR_ALIGN4(nds->pdu_scan_offset);
1464         }
1465 
1466         if (n_pad == 0)
1467                 return (1);     /* already aligned, often the case */
1468 
1469         if (!ndr_outer_grow(outer_ref, n_pad))
1470                 return (0);     /* error already set */
1471 
1472         switch (nds->m_op) {
1473         case NDR_M_OP_MARSHALL:
1474                 rc = NDS_PAD_PDU(nds, nds->pdu_scan_offset, n_pad, outer_ref);
1475                 if (!rc) {
1476                         NDR_SET_ERROR(outer_ref, NDR_ERR_PAD_FAILED);
1477                         return (0);
1478                 }
1479                 break;
1480 
1481         case NDR_M_OP_UNMARSHALL:
1482                 break;
1483 
1484         default:
1485                 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1486                 return (0);
1487         }
1488 
1489         nds->pdu_scan_offset += n_pad;
1490         return (1);
1491 }
1492 
1493 int
1494 ndr_outer_grow(ndr_ref_t *outer_ref, unsigned n_total)
1495 {
1496         ndr_stream_t    *nds = outer_ref->stream;
1497         unsigned long   pdu_want_size;
1498         int             rc, is_ok = 0;
1499 
1500         pdu_want_size = nds->pdu_scan_offset + n_total;
1501 
1502         if (pdu_want_size <= nds->pdu_max_size) {
1503                 is_ok = 1;
1504         }
1505 
1506         switch (nds->m_op) {
1507         case NDR_M_OP_MARSHALL:
1508                 if (is_ok)
1509                         break;
1510                 rc = NDS_GROW_PDU(nds, pdu_want_size, outer_ref);
1511                 if (!rc) {
1512                         NDR_SET_ERROR(outer_ref, NDR_ERR_GROW_FAILED);
1513                         return (0);
1514                 }
1515                 break;
1516 
1517         case NDR_M_OP_UNMARSHALL:
1518                 if (is_ok)
1519                         break;
1520                 NDR_SET_ERROR(outer_ref, NDR_ERR_UNDERFLOW);
1521                 return (0);
1522 
1523         default:
1524                 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1525                 return (0);
1526         }
1527 
1528         if (nds->pdu_size < pdu_want_size)
1529                 nds->pdu_size = pdu_want_size;
1530 
1531         outer_ref->pdu_end_offset = pdu_want_size;
1532         return (1);
1533 }
1534 
1535 /*
1536  * INNER ELEMENTS
1537  *
1538  * The local datum (arg_ref->datum) already exists, there is no need to
1539  * malloc() it.  The datum should point at a member of a structure.
1540  *
1541  * For the most part, ndr_inner() and its helpers are just a sanity
1542  * check.  The underlying ti->ndr_func() could be called immediately
1543  * for non-pointer elements.  For the sake of robustness, we detect
1544  * run-time errors here.  Most of the situations this protects against
1545  * have already been checked by the IDL compiler.  This is also a
1546  * common point for processing of all data, and so is a convenient
1547  * place to work from for debugging.
1548  */
1549 int
1550 ndr_inner(ndr_ref_t *arg_ref)
1551 {
1552         ndr_typeinfo_t  *ti = arg_ref->ti;
1553         int     is_varlen = ti->pdu_size_variable_part;
1554         int     is_union = NDR_IS_UNION(ti);
1555         int     error = NDR_ERR_INNER_PARAMS_BAD;
1556         int     params;
1557 
1558         params = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
1559 
1560         switch (params) {
1561         case NDR_F_NONE:
1562                 if (is_union) {
1563                         error = NDR_ERR_SWITCH_VALUE_MISSING;
1564                         break;
1565                 }
1566                 return (*ti->ndr_func)(arg_ref);
1567 
1568         case NDR_F_SIZE_IS:
1569         case NDR_F_DIMENSION_IS:
1570         case NDR_F_IS_POINTER+NDR_F_SIZE_IS:   /* pointer to something */
1571         case NDR_F_IS_REFERENCE+NDR_F_SIZE_IS: /* pointer to something */
1572                 if (is_varlen) {
1573                         error = NDR_ERR_ARRAY_VARLEN_ILLEGAL;
1574                         break;
1575                 }
1576                 if (is_union) {
1577                         error = NDR_ERR_ARRAY_UNION_ILLEGAL;
1578                         break;
1579                 }
1580                 if (params & NDR_F_IS_POINTER)
1581                         return (ndr_inner_pointer(arg_ref));
1582                 else if (params & NDR_F_IS_REFERENCE)
1583                         return (ndr_inner_reference(arg_ref));
1584                 else
1585                         return (ndr_inner_array(arg_ref));
1586 
1587         case NDR_F_IS_POINTER:  /* type is pointer to one something */
1588                 if (is_union) {
1589                         error = NDR_ERR_ARRAY_UNION_ILLEGAL;
1590                         break;
1591                 }
1592                 return (ndr_inner_pointer(arg_ref));
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 
1601         case NDR_F_SWITCH_IS:
1602                 if (!is_union) {
1603                         error = NDR_ERR_SWITCH_VALUE_ILLEGAL;
1604                         break;
1605                 }
1606                 return (*ti->ndr_func)(arg_ref);
1607 
1608         default:
1609                 error = NDR_ERR_INNER_PARAMS_BAD;
1610                 break;
1611         }
1612 
1613         /*
1614          * If we get here, something is wrong. Most likely,
1615          * the params flags do not match
1616          */
1617         NDR_SET_ERROR(arg_ref, error);
1618         return (0);
1619 }
1620 
1621 int
1622 ndr_inner_pointer(ndr_ref_t *arg_ref)
1623 {
1624         ndr_stream_t    *nds = arg_ref->stream;
1625         /*LINTED E_BAD_PTR_CAST_ALIGN*/
1626         char            **valpp = (char **)arg_ref->datum;
1627         ndr_ref_t       *outer_ref;
1628 
1629         if (!ndr__ulong(arg_ref))
1630                 return (0);     /* error */
1631         if (!*valpp)
1632                 return (1);     /* NULL pointer */
1633 
1634         outer_ref = ndr_enter_outer_queue(arg_ref);
1635         if (!outer_ref)
1636                 return (0);     /* error already set */
1637 
1638         /*
1639          * Move advice in inner_flags to outer_flags.
1640          * Retain pointer flag for conformant arrays.
1641          */
1642         outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
1643         if ((outer_ref->outer_flags & NDR_F_SIZE_IS) == 0)
1644                 outer_ref->outer_flags &= ~NDR_F_IS_POINTER;
1645 #ifdef NDR_INNER_PTR_NOT_YET
1646         outer_ref->outer_flags |= NDR_F_BACKPTR;
1647         if (outer_ref->outer_flags & NDR_F_SIZE_IS) {
1648                 outer_ref->outer_flags |= NDR_F_ARRAY+NDR_F_CONFORMANT;
1649         }
1650 #endif /* NDR_INNER_PTR_NOT_YET */
1651 
1652         outer_ref->backptr = valpp;
1653 
1654         switch (nds->m_op) {
1655         case NDR_M_OP_MARSHALL:
1656                 outer_ref->datum = *valpp;
1657                 break;
1658 
1659         case NDR_M_OP_UNMARSHALL:
1660                 /*
1661                  * This is probably wrong if the application allocated
1662                  * memory in advance.  Indicate no value for now.
1663                  * ONC RPC handles this case.
1664                  */
1665                 *valpp = 0;
1666                 outer_ref->datum = 0;
1667                 break;
1668         }
1669 
1670         return (1);             /* pointer dereference scheduled */
1671 }
1672 
1673 int
1674 ndr_inner_reference(ndr_ref_t *arg_ref)
1675 {
1676         ndr_stream_t    *nds = arg_ref->stream;
1677         /*LINTED E_BAD_PTR_CAST_ALIGN*/
1678         char            **valpp = (char **)arg_ref->datum;
1679         ndr_ref_t       *outer_ref;
1680 
1681         outer_ref = ndr_enter_outer_queue(arg_ref);
1682         if (!outer_ref)
1683                 return (0);     /* error already set */
1684 
1685         /*
1686          * Move advice in inner_flags to outer_flags.
1687          * Retain reference flag for conformant arrays.
1688          */
1689         outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
1690         if ((outer_ref->outer_flags & NDR_F_SIZE_IS) == 0)
1691                 outer_ref->outer_flags &= ~NDR_F_IS_REFERENCE;
1692 #ifdef NDR_INNER_REF_NOT_YET
1693         outer_ref->outer_flags |= NDR_F_BACKPTR;
1694         if (outer_ref->outer_flags & NDR_F_SIZE_IS) {
1695                 outer_ref->outer_flags |= NDR_F_ARRAY+NDR_F_CONFORMANT;
1696         }
1697 #endif /* NDR_INNER_REF_NOT_YET */
1698 
1699         outer_ref->backptr = valpp;
1700 
1701         switch (nds->m_op) {
1702         case NDR_M_OP_MARSHALL:
1703                 outer_ref->datum = *valpp;
1704                 break;
1705 
1706         case NDR_M_OP_UNMARSHALL:
1707                 /*
1708                  * This is probably wrong if the application allocated
1709                  * memory in advance.  Indicate no value for now.
1710                  * ONC RPC handles this case.
1711                  */
1712                 *valpp = 0;
1713                 outer_ref->datum = 0;
1714                 break;
1715         }
1716 
1717         return (1);             /* pointer dereference scheduled */
1718 }
1719 
1720 int
1721 ndr_inner_array(ndr_ref_t *encl_ref)
1722 {
1723         ndr_typeinfo_t          *ti = encl_ref->ti;
1724         ndr_ref_t               myref;
1725         unsigned long           pdu_offset = encl_ref->pdu_offset;
1726         unsigned long           n_elem;
1727         unsigned long           i;
1728         char                    name[30];
1729 
1730         if (encl_ref->inner_flags & NDR_F_SIZE_IS) {
1731                 /* now is the time to check/set size */
1732                 if (!ndr_size_is(encl_ref))
1733                         return (0);     /* error already set */
1734                 n_elem = encl_ref->size_is;
1735         } else {
1736                 assert(encl_ref->inner_flags & NDR_F_DIMENSION_IS);
1737                 n_elem = encl_ref->dimension_is;
1738         }
1739 
1740         bzero(&myref, sizeof (myref));
1741         myref.enclosing = encl_ref;
1742         myref.stream = encl_ref->stream;
1743         myref.packed_alignment = 0;
1744         myref.ti = ti;
1745         myref.inner_flags = NDR_F_NONE;
1746 
1747         for (i = 0; i < n_elem; i++) {
1748                 (void) snprintf(name, sizeof (name), "[%lu]", i);
1749                 myref.name = name;
1750                 myref.pdu_offset = pdu_offset + i * ti->pdu_size_fixed_part;
1751                 myref.datum = encl_ref->datum + i * ti->c_size_fixed_part;
1752 
1753                 if (!ndr_inner(&myref))
1754                         return (0);
1755         }
1756 
1757         return (1);
1758 }
1759 
1760 
1761 /*
1762  * BASIC TYPES
1763  */
1764 #define MAKE_BASIC_TYPE_BASE(TYPE, SIZE) \
1765     extern int ndr_##TYPE(struct ndr_reference *encl_ref); \
1766     ndr_typeinfo_t ndt_##TYPE = { \
1767         1,              /* NDR version */ \
1768         (SIZE)-1,       /* alignment */ \
1769         NDR_F_NONE,     /* flags */ \
1770         ndr_##TYPE,     /* ndr_func */ \
1771         SIZE,           /* pdu_size_fixed_part */ \
1772         0,              /* pdu_size_variable_part */ \
1773         SIZE,           /* c_size_fixed_part */ \
1774         0,              /* c_size_variable_part */ \
1775         }; \
1776     int ndr_##TYPE(struct ndr_reference *ref) { \
1777         return (ndr_basic_integer(ref, SIZE)); \
1778 }
1779 
1780 #define MAKE_BASIC_TYPE_STRING(TYPE, SIZE) \
1781     extern int ndr_s##TYPE(struct ndr_reference *encl_ref); \
1782     ndr_typeinfo_t ndt_s##TYPE = { \
1783         1,              /* NDR version */ \
1784         (SIZE)-1,       /* alignment */ \
1785         NDR_F_STRING,   /* flags */ \
1786         ndr_s##TYPE,    /* ndr_func */ \
1787         0,              /* pdu_size_fixed_part */ \
1788         SIZE,           /* pdu_size_variable_part */ \
1789         0,              /* c_size_fixed_part */ \
1790         SIZE,           /* c_size_variable_part */ \
1791         }; \
1792     int ndr_s##TYPE(struct ndr_reference *ref) { \
1793         return (ndr_string_basic_integer(ref, &ndt_##TYPE)); \
1794 }
1795 
1796 #define MAKE_BASIC_TYPE(TYPE, SIZE) \
1797         MAKE_BASIC_TYPE_BASE(TYPE, SIZE) \
1798         MAKE_BASIC_TYPE_STRING(TYPE, SIZE)
1799 
1800 int ndr_basic_integer(ndr_ref_t *, unsigned);
1801 int ndr_string_basic_integer(ndr_ref_t *, ndr_typeinfo_t *);
1802 
1803 /* Comments to be nice to those searching for these types. */
1804 MAKE_BASIC_TYPE(_char, 1)       /* ndt__char,  ndt_s_char */
1805 MAKE_BASIC_TYPE(_uchar, 1)      /* ndt__uchar, ndt_s_uchar */
1806 MAKE_BASIC_TYPE(_short, 2)      /* ndt__short, ndt_s_short */
1807 MAKE_BASIC_TYPE(_ushort, 2)     /* ndt__ushort, ndt_s_ushort */
1808 MAKE_BASIC_TYPE(_long, 4)       /* ndt__long,  ndt_s_long */
1809 MAKE_BASIC_TYPE(_ulong, 4)      /* ndt__ulong, ndt_s_ulong */
1810 
1811 MAKE_BASIC_TYPE_BASE(_wchar, 2) /* ndt__wchar, ndt_s_wchar */
1812 
1813 int
1814 ndr_basic_integer(ndr_ref_t *ref, unsigned size)
1815 {
1816         ndr_stream_t    *nds = ref->stream;
1817         char            *valp = (char *)ref->datum;
1818         int             rc;
1819 
1820         switch (nds->m_op) {
1821         case NDR_M_OP_MARSHALL:
1822                 rc = NDS_PUT_PDU(nds, ref->pdu_offset, size,
1823                     valp, nds->swap, ref);
1824                 break;
1825 
1826         case NDR_M_OP_UNMARSHALL:
1827                 rc = NDS_GET_PDU(nds, ref->pdu_offset, size,
1828                     valp, nds->swap, ref);
1829                 break;
1830 
1831         default:
1832                 NDR_SET_ERROR(ref, NDR_ERR_M_OP_INVALID);
1833                 return (0);
1834         }
1835 
1836         return (rc);
1837 }
1838 
1839 int
1840 ndr_string_basic_integer(ndr_ref_t *encl_ref, ndr_typeinfo_t *type_under)
1841 {
1842         unsigned long           pdu_offset = encl_ref->pdu_offset;
1843         unsigned                size = type_under->pdu_size_fixed_part;
1844         char                    *valp;
1845         ndr_ref_t               myref;
1846         unsigned long           i;
1847         long                    sense = 0;
1848         char                    name[30];
1849 
1850         assert(size != 0);
1851 
1852         bzero(&myref, sizeof (myref));
1853         myref.enclosing = encl_ref;
1854         myref.stream = encl_ref->stream;
1855         myref.packed_alignment = 0;
1856         myref.ti = type_under;
1857         myref.inner_flags = NDR_F_NONE;
1858         myref.name = name;
1859 
1860         for (i = 0; i < NDR_STRING_MAX; i++) {
1861                 (void) snprintf(name, sizeof (name), "[%lu]", i);
1862                 myref.pdu_offset = pdu_offset + i * size;
1863                 valp = encl_ref->datum + i * size;
1864                 myref.datum = valp;
1865 
1866                 if (!ndr_inner(&myref))
1867                         return (0);
1868 
1869                 switch (size) {
1870                 case 1:         sense = *valp; break;
1871                 /*LINTED E_BAD_PTR_CAST_ALIGN*/
1872                 case 2:         sense = *(short *)valp; break;
1873                 /*LINTED E_BAD_PTR_CAST_ALIGN*/
1874                 case 4:         sense = *(long *)valp; break;
1875                 }
1876 
1877                 if (!sense)
1878                         break;
1879         }
1880 
1881         return (1);
1882 }
1883 
1884 
1885 extern int ndr_s_wchar(ndr_ref_t *encl_ref);
1886 ndr_typeinfo_t ndt_s_wchar = {
1887         1,              /* NDR version */
1888         2-1,            /* alignment */
1889         NDR_F_STRING,   /* flags */
1890         ndr_s_wchar,    /* ndr_func */
1891         0,              /* pdu_size_fixed_part */
1892         2,              /* pdu_size_variable_part */
1893         0,              /* c_size_fixed_part */
1894         1,              /* c_size_variable_part */
1895 };
1896 
1897 
1898 /*
1899  * Hand coded wchar function because all strings are transported
1900  * as wide characters. During NDR_M_OP_MARSHALL, we convert from
1901  * multi-byte to wide characters. During NDR_M_OP_UNMARSHALL, we
1902  * convert from wide characters to multi-byte.
1903  *
1904  * The most critical thing to get right in this function is to
1905  * marshall or unmarshall _exactly_ the number of elements the
1906  * OtW length specifies, as saved by the caller in: strlen_is.
1907  * Doing otherwise would leave us positioned at the wrong place
1908  * in the data stream for whatever follows this.  Note that the
1909  * string data covered by strlen_is may or may not include any
1910  * null termination, but the converted string provided by the
1911  * caller or returned always has a null terminator.
1912  */
1913 int
1914 ndr_s_wchar(ndr_ref_t *encl_ref)
1915 {
1916         ndr_stream_t            *nds = encl_ref->stream;
1917         char                    *valp = encl_ref->datum;
1918         ndr_ref_t               myref;
1919         char                    name[30];
1920         ndr_wchar_t             wcs[NDR_STRING_MAX+1];
1921         size_t                  i, slen, wlen;
1922 
1923         /* This is enforced in ndr_outer_string() */
1924         assert(encl_ref->strlen_is <= NDR_STRING_MAX);
1925 
1926         if (nds->m_op == NDR_M_OP_UNMARSHALL) {
1927                 /*
1928                  * To avoid problems with zero length strings
1929                  * we can just null terminate here and be done.
1930                  */
1931                 if (encl_ref->strlen_is == 0) {
1932                         encl_ref->datum[0] = '\0';
1933                         return (1);
1934                 }
1935         }
1936 
1937         /*
1938          * If we're marshalling, convert the given string
1939          * from UTF-8 into a local UCS-2 string.
1940          */
1941         if (nds->m_op == NDR_M_OP_MARSHALL) {
1942                 wlen = ndr__mbstowcs(wcs, valp, NDR_STRING_MAX);
1943                 if (wlen == (size_t)-1)
1944                         return (0);
1945                 /*
1946                  * Add a nulls to make strlen_is.
1947                  * (always zero or one of them)
1948                  * Then null terminate at wlen,
1949                  * just for debug convenience.
1950                  */
1951                 while (wlen < encl_ref->strlen_is)
1952                         wcs[wlen++] = 0;
1953                 wcs[wlen] = 0;
1954         }
1955 
1956         /*
1957          * Copy wire data to or from the local wc string.
1958          * Always exactly strlen_is elements.
1959          */
1960         bzero(&myref, sizeof (myref));
1961         myref.enclosing = encl_ref;
1962         myref.stream = encl_ref->stream;
1963         myref.packed_alignment = 0;
1964         myref.ti = &ndt__wchar;
1965         myref.inner_flags = NDR_F_NONE;
1966         myref.name = name;
1967         myref.pdu_offset = encl_ref->pdu_offset;
1968         myref.datum = (char *)wcs;
1969         wlen = encl_ref->strlen_is;
1970 
1971         for (i = 0; i < wlen; i++) {
1972                 (void) snprintf(name, sizeof (name), "[%lu]", i);
1973                 if (!ndr_inner(&myref))
1974                         return (0);
1975                 myref.pdu_offset += sizeof (ndr_wchar_t);
1976                 myref.datum      += sizeof (ndr_wchar_t);
1977         }
1978 
1979         /*
1980          * If this is unmarshall, convert the local UCS-2 string
1981          * into a UTF-8 string in the caller's buffer.  The caller
1982          * previously determined the space required and provides a
1983          * buffer of sufficient size.
1984          */
1985         if (nds->m_op == NDR_M_OP_UNMARSHALL) {
1986                 wcs[wlen] = 0;
1987                 slen = ndr__wcstombs(valp, wcs, wlen);
1988                 if (slen == (size_t)-1)
1989                         return (0);
1990                 valp[slen] = '\0';
1991         }
1992 
1993         return (1);
1994 }
1995 
1996 /*
1997  * Converts a multibyte character string to a little-endian, wide-char
1998  * string.  No more than nwchars wide characters are stored.
1999  * A terminating null wide character is appended if there is room.
2000  *
2001  * Returns the number of wide characters converted, not counting
2002  * any terminating null wide character.  Returns -1 if an invalid
2003  * multibyte character is encountered.
2004  */
2005 /* ARGSUSED */
2006 size_t
2007 ndr_mbstowcs(ndr_stream_t *nds, ndr_wchar_t *wcs, const char *mbs,
2008     size_t nwchars)
2009 {
2010         size_t len;
2011 
2012 #ifdef _BIG_ENDIAN
2013         if (nds == NULL || NDR_MODE_MATCH(nds, NDR_MODE_RETURN_SEND)) {
2014                 /* Make WC string in LE order. */
2015                 len = ndr__mbstowcs_le(wcs, mbs, nwchars);
2016         } else
2017 #endif
2018                 len = ndr__mbstowcs(wcs, mbs, nwchars);
2019 
2020         return (len);
2021 }