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