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 * Server side RPC handler. 30 */ 31 32 #include <sys/byteorder.h> 33 #include <thread.h> 34 #include <synch.h> 35 #include <stdlib.h> 36 #include <strings.h> 37 #include <string.h> 38 #include <time.h> 39 40 #include <smbsrv/libsmb.h> 41 #include <smbsrv/libmlrpc.h> 42 #include <smbsrv/mlsvc.h> 43 #include <smbsrv/ndr.h> 44 #include <smbsrv/mlrpc.h> 45 #include <smbsrv/mlsvc_util.h> 46 #include <smbsrv/smb_winpipe.h> 47 48 /* 49 * Fragment size (5680: NT style). 50 */ 51 #define MLRPC_FRAG_SZ 5680 52 static unsigned long mlrpc_frag_size = MLRPC_FRAG_SZ; 53 54 /* 55 * Context table. 56 */ 57 #define CTXT_TABLE_ENTRIES 128 58 static struct mlsvc_rpc_context context_table[CTXT_TABLE_ENTRIES]; 59 static mutex_t mlrpc_context_lock; 60 61 static int mlrpc_s_process(struct mlrpc_xaction *); 62 static int mlrpc_s_bind(struct mlrpc_xaction *); 63 static int mlrpc_s_request(struct mlrpc_xaction *); 64 static void mlrpc_reply_prepare_hdr(struct mlrpc_xaction *); 65 static int mlrpc_s_alter_context(struct mlrpc_xaction *); 66 static void mlrpc_reply_bind_ack(struct mlrpc_xaction *); 67 static void mlrpc_reply_fault(struct mlrpc_xaction *, unsigned long); 68 static int mlrpc_build_reply(struct mlrpc_xaction *); 69 70 /* 71 * This is the RPC service server-side entry point. All MSRPC encoded 72 * messages should be passed through here. We use the same context 73 * structure as the client side but we don't need to set up the client 74 * side info. 75 */ 76 struct mlsvc_rpc_context * 77 mlrpc_process(int fid, smb_dr_user_ctx_t *user_ctx) 78 { 79 struct mlsvc_rpc_context *context; 80 struct mlrpc_xaction *mxa; 81 struct mlndr_stream *recv_mlnds; 82 struct mlndr_stream *send_mlnds; 83 unsigned char *pdu_base_addr; 84 char *data; 85 int datalen; 86 87 if ((context = mlrpc_lookup(fid)) == NULL) 88 return (NULL); 89 90 context->user_ctx = user_ctx; 91 data = context->inpipe->sp_data; 92 datalen = context->inpipe->sp_datalen; 93 94 mxa = (struct mlrpc_xaction *)malloc(sizeof (struct mlrpc_xaction)); 95 if (mxa == NULL) 96 return (NULL); 97 98 bzero(mxa, sizeof (struct mlrpc_xaction)); 99 mxa->fid = fid; 100 mxa->context = context; 101 mxa->binding_list = context->binding; 102 103 if ((mxa->heap = mlrpc_heap_create()) == NULL) { 104 free(mxa); 105 return (NULL); 106 } 107 108 recv_mlnds = &mxa->recv_mlnds; 109 (void) mlnds_initialize(recv_mlnds, datalen, NDR_MODE_CALL_RECV, 110 mxa->heap); 111 112 bcopy(data, recv_mlnds->pdu_base_addr, datalen); 113 114 send_mlnds = &mxa->send_mlnds; 115 (void) mlnds_initialize(send_mlnds, 0, NDR_MODE_RETURN_SEND, mxa->heap); 116 117 (void) mlrpc_s_process(mxa); 118 119 /* 120 * Different pointers for single frag vs multi frag responses. 121 */ 122 if (send_mlnds->pdu_base_addr_with_rpc_hdrs) 123 pdu_base_addr = send_mlnds->pdu_base_addr_with_rpc_hdrs; 124 else 125 pdu_base_addr = send_mlnds->pdu_base_addr; 126 127 datalen = send_mlnds->pdu_size_with_rpc_hdrs; 128 context->outpipe->sp_datalen = datalen; 129 bcopy(pdu_base_addr, context->outpipe->sp_data, datalen); 130 131 mlnds_destruct(&mxa->recv_mlnds); 132 mlnds_destruct(&mxa->send_mlnds); 133 mlrpc_heap_destroy(mxa->heap); 134 free(mxa); 135 return (context); 136 } 137 138 /* 139 * Lookup the context for pipeid. If one exists, return a pointer to it. 140 * Otherwise attempt to allocate a new context and return it. If the 141 * context table is full, return a null pointer. 142 */ 143 struct mlsvc_rpc_context * 144 mlrpc_lookup(int fid) 145 { 146 struct mlsvc_rpc_context *context; 147 struct mlsvc_rpc_context *available = NULL; 148 int i; 149 150 (void) mutex_lock(&mlrpc_context_lock); 151 152 for (i = 0; i < CTXT_TABLE_ENTRIES; ++i) { 153 context = &context_table[i]; 154 155 if (available == NULL && context->fid == 0) { 156 available = context; 157 continue; 158 } 159 160 if (context->fid == fid) { 161 (void) mutex_unlock(&mlrpc_context_lock); 162 return (context); 163 } 164 } 165 166 if (available) { 167 bzero(available, sizeof (struct mlsvc_rpc_context)); 168 available->inpipe = malloc(SMB_CTXT_PIPE_SZ); 169 available->outpipe = malloc(SMB_CTXT_PIPE_SZ); 170 171 if (available->inpipe == NULL || available->outpipe == NULL) { 172 free(available->inpipe); 173 free(available->outpipe); 174 bzero(available, sizeof (struct mlsvc_rpc_context)); 175 (void) mutex_unlock(&mlrpc_context_lock); 176 return (NULL); 177 } 178 179 bzero(available->inpipe, sizeof (smb_pipe_t)); 180 bzero(available->outpipe, sizeof (smb_pipe_t)); 181 available->fid = fid; 182 available->inpipe->sp_pipeid = fid; 183 available->outpipe->sp_pipeid = fid; 184 185 mlrpc_binding_pool_initialize(&available->binding, 186 available->binding_pool, CTXT_N_BINDING_POOL); 187 } 188 189 (void) mutex_unlock(&mlrpc_context_lock); 190 return (available); 191 } 192 193 /* 194 * This function should be called to release the context associated 195 * with a fid when the client performs a close file. 196 */ 197 void 198 mlrpc_release(int fid) 199 { 200 struct mlsvc_rpc_context *context; 201 int i; 202 203 (void) mutex_lock(&mlrpc_context_lock); 204 205 for (i = 0; i < CTXT_TABLE_ENTRIES; ++i) { 206 context = &context_table[i]; 207 208 if (context->fid == fid) { 209 ndr_hdclose(fid); 210 free(context->inpipe); 211 free(context->outpipe); 212 bzero(context, sizeof (struct mlsvc_rpc_context)); 213 break; 214 } 215 } 216 217 (void) mutex_unlock(&mlrpc_context_lock); 218 } 219 220 /* 221 * This is the entry point for all server-side RPC processing. 222 * It is assumed that the PDU has already been received. 223 */ 224 static int 225 mlrpc_s_process(struct mlrpc_xaction *mxa) 226 { 227 int rc; 228 229 rc = mlrpc_decode_pdu_hdr(mxa); 230 if (!MLRPC_DRC_IS_OK(rc)) 231 return (-1); 232 233 (void) mlrpc_reply_prepare_hdr(mxa); 234 235 switch (mxa->ptype) { 236 case MLRPC_PTYPE_BIND: 237 rc = mlrpc_s_bind(mxa); 238 break; 239 240 case MLRPC_PTYPE_REQUEST: 241 rc = mlrpc_s_request(mxa); 242 break; 243 244 case MLRPC_PTYPE_ALTER_CONTEXT: 245 rc = mlrpc_s_alter_context(mxa); 246 break; 247 248 default: 249 rc = MLRPC_DRC_FAULT_RPCHDR_PTYPE_INVALID; 250 break; 251 } 252 253 if (MLRPC_DRC_IS_FAULT(rc)) 254 mlrpc_reply_fault(mxa, rc); 255 256 (void) mlrpc_build_reply(mxa); 257 return (rc); 258 } 259 260 /* 261 * Multiple p_cont_elem[]s, multiple transfer_syntaxes[] and multiple 262 * p_results[] not supported. 263 */ 264 static int 265 mlrpc_s_bind(struct mlrpc_xaction *mxa) 266 { 267 mlrpc_p_cont_list_t *cont_list; 268 mlrpc_p_result_list_t *result_list; 269 mlrpc_p_result_t *result; 270 unsigned p_cont_id; 271 struct mlrpc_binding *mbind; 272 ndr_uuid_t *as_uuid; 273 ndr_uuid_t *ts_uuid; 274 char as_buf[64]; 275 char ts_buf[64]; 276 int as_vers; 277 int ts_vers; 278 struct mlndr_stream *send_mlnds; 279 struct mlrpc_service *msvc; 280 int rc; 281 mlrpc_port_any_t *sec_addr; 282 283 /* acquire targets */ 284 cont_list = &mxa->recv_hdr.bind_hdr.p_context_elem; 285 result_list = &mxa->send_hdr.bind_ack_hdr.p_result_list; 286 result = &result_list->p_results[0]; 287 288 /* 289 * Set up temporary secondary address port. 290 * We will correct this later (below). 291 */ 292 send_mlnds = &mxa->send_mlnds; 293 sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr; 294 sec_addr->length = 13; 295 (void) strcpy((char *)sec_addr->port_spec, "\\PIPE\\ntsvcs"); 296 297 result_list->n_results = 1; 298 result_list->reserved = 0; 299 result_list->reserved2 = 0; 300 result->result = MLRPC_PCDR_ACCEPTANCE; 301 result->reason = 0; 302 bzero(&result->transfer_syntax, sizeof (result->transfer_syntax)); 303 304 /* sanity check */ 305 if (cont_list->n_context_elem != 1 || 306 cont_list->p_cont_elem[0].n_transfer_syn != 1) { 307 mlndo_trace("mlrpc_s_bind: warning: multiple p_cont_elem"); 308 } 309 310 p_cont_id = cont_list->p_cont_elem[0].p_cont_id; 311 312 if ((mbind = mlrpc_find_binding(mxa, p_cont_id)) != NULL) { 313 /* 314 * Duplicate p_cont_id. 315 * Send a bind_ack with a better error. 316 */ 317 mlndo_trace("mlrpc_s_bind: duplicate binding"); 318 return (MLRPC_DRC_FAULT_BIND_PCONT_BUSY); 319 } 320 321 if ((mbind = mlrpc_new_binding(mxa)) == NULL) { 322 /* 323 * No free binding slot 324 */ 325 result->result = MLRPC_PCDR_PROVIDER_REJECTION; 326 result->reason = MLRPC_PPR_LOCAL_LIMIT_EXCEEDED; 327 mlndo_trace("mlrpc_s_bind: no resources"); 328 return (MLRPC_DRC_OK); 329 } 330 331 as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid; 332 as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version; 333 334 ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid; 335 ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version; 336 337 msvc = mlrpc_find_service_by_uuids(as_uuid, as_vers, ts_uuid, ts_vers); 338 if (!msvc) { 339 mlrpc_uuid_to_str(as_uuid, as_buf); 340 mlrpc_uuid_to_str(ts_uuid, ts_buf); 341 342 mlndo_printf(send_mlnds, 0, "mlrpc_s_bind: unknown service"); 343 mlndo_printf(send_mlnds, 0, "abs=%s v%d, xfer=%s v%d", 344 as_buf, as_vers, ts_buf, ts_vers); 345 346 result->result = MLRPC_PCDR_PROVIDER_REJECTION; 347 result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED; 348 return (MLRPC_DRC_OK); 349 } 350 351 /* 352 * We can now use the correct secondary address port. 353 */ 354 sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr; 355 sec_addr->length = strlen(msvc->sec_addr_port) + 1; 356 (void) strlcpy((char *)sec_addr->port_spec, msvc->sec_addr_port, 357 MLRPC_PORT_ANY_MAX_PORT_SPEC); 358 359 mbind->p_cont_id = p_cont_id; 360 mbind->which_side = MLRPC_BIND_SIDE_SERVER; 361 /* mbind->context set by app */ 362 mbind->service = msvc; 363 mbind->instance_specific = 0; 364 365 mxa->binding = mbind; 366 367 if (msvc->bind_req) { 368 /* 369 * Call the service-specific bind() handler. If 370 * this fails, we shouild send a specific error 371 * on the bind ack. 372 */ 373 rc = (msvc->bind_req)(mxa); 374 if (MLRPC_DRC_IS_FAULT(rc)) { 375 mbind->service = 0; /* free binding slot */ 376 mbind->which_side = 0; 377 mbind->p_cont_id = 0; 378 mbind->instance_specific = 0; 379 return (rc); 380 } 381 } 382 383 result->transfer_syntax = 384 cont_list->p_cont_elem[0].transfer_syntaxes[0]; 385 386 /* 387 * Special rejection of Windows 2000 DSSETUP interface. 388 * This interface was introduced in Windows 2000 but has 389 * been subsequently deprecated due to problems. 390 */ 391 if (strcmp(msvc->name, "DSSETUP") == 0) { 392 result->result = MLRPC_PCDR_PROVIDER_REJECTION; 393 result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED; 394 } 395 396 return (MLRPC_DRC_BINDING_MADE); 397 } 398 399 /* 400 * mlrpc_s_alter_context 401 * 402 * The alter context request is used to request additional presentation 403 * context for another interface and/or version. It's very similar to a 404 * bind request. 405 * 406 * We don't fully support multiple contexts so, for now, we reject this 407 * request. Windows 2000 clients attempt to use an alternate LSA context 408 * when ACLs are modified. 409 */ 410 static int 411 mlrpc_s_alter_context(struct mlrpc_xaction *mxa) 412 { 413 mlrpc_p_result_list_t *result_list; 414 mlrpc_p_result_t *result; 415 mlrpc_p_cont_list_t *cont_list; 416 struct mlrpc_binding *mbind; 417 struct mlrpc_service *msvc; 418 unsigned p_cont_id; 419 ndr_uuid_t *as_uuid; 420 ndr_uuid_t *ts_uuid; 421 int as_vers; 422 int ts_vers; 423 mlrpc_port_any_t *sec_addr; 424 425 result_list = &mxa->send_hdr.bind_ack_hdr.p_result_list; 426 result_list->n_results = 1; 427 result_list->reserved = 0; 428 result_list->reserved2 = 0; 429 430 result = &result_list->p_results[0]; 431 result->result = MLRPC_PCDR_ACCEPTANCE; 432 result->reason = 0; 433 bzero(&result->transfer_syntax, sizeof (result->transfer_syntax)); 434 435 if (mxa != NULL) { 436 result->result = MLRPC_PCDR_PROVIDER_REJECTION; 437 result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED; 438 return (MLRPC_DRC_OK); 439 } 440 441 cont_list = &mxa->recv_hdr.bind_hdr.p_context_elem; 442 p_cont_id = cont_list->p_cont_elem[0].p_cont_id; 443 444 if ((mbind = mlrpc_find_binding(mxa, p_cont_id)) != NULL) 445 return (MLRPC_DRC_FAULT_BIND_PCONT_BUSY); 446 447 if ((mbind = mlrpc_new_binding(mxa)) == NULL) { 448 result->result = MLRPC_PCDR_PROVIDER_REJECTION; 449 result->reason = MLRPC_PPR_LOCAL_LIMIT_EXCEEDED; 450 return (MLRPC_DRC_OK); 451 } 452 453 as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid; 454 as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version; 455 456 ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid; 457 ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version; 458 459 msvc = mlrpc_find_service_by_uuids(as_uuid, as_vers, ts_uuid, ts_vers); 460 if (msvc == 0) { 461 result->result = MLRPC_PCDR_PROVIDER_REJECTION; 462 result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED; 463 return (MLRPC_DRC_OK); 464 } 465 466 mbind->p_cont_id = p_cont_id; 467 mbind->which_side = MLRPC_BIND_SIDE_SERVER; 468 /* mbind->context set by app */ 469 mbind->service = msvc; 470 mbind->instance_specific = 0; 471 mxa->binding = mbind; 472 473 sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr; 474 sec_addr->length = 0; 475 bzero(sec_addr->port_spec, MLRPC_PORT_ANY_MAX_PORT_SPEC); 476 477 result->transfer_syntax = 478 cont_list->p_cont_elem[0].transfer_syntaxes[0]; 479 480 return (MLRPC_DRC_BINDING_MADE); 481 } 482 483 static int 484 mlrpc_s_request(struct mlrpc_xaction *mxa) 485 { 486 struct mlrpc_binding *mbind; 487 struct mlrpc_service *msvc; 488 unsigned p_cont_id; 489 int rc; 490 491 mxa->opnum = mxa->recv_hdr.request_hdr.opnum; 492 p_cont_id = mxa->recv_hdr.request_hdr.p_cont_id; 493 494 if ((mbind = mlrpc_find_binding(mxa, p_cont_id)) == NULL) 495 return (MLRPC_DRC_FAULT_REQUEST_PCONT_INVALID); 496 497 mxa->binding = mbind; 498 msvc = mbind->service; 499 500 /* 501 * Make room for the response hdr. 502 */ 503 mxa->send_mlnds.pdu_scan_offset = MLRPC_RSP_HDR_SIZE; 504 505 if (msvc->call_stub) 506 rc = (*msvc->call_stub)(mxa); 507 else 508 rc = mlrpc_generic_call_stub(mxa); 509 510 if (MLRPC_DRC_IS_FAULT(rc)) { 511 mlndo_printf(0, 0, "%s[0x%02x]: 0x%04x", 512 msvc->name, mxa->opnum, rc); 513 } 514 515 return (rc); 516 } 517 518 /* 519 * The transaction and the two mlnds streams use the same heap, which 520 * should already exist at this point. The heap will also be available 521 * to the stub. 522 */ 523 int 524 mlrpc_generic_call_stub(struct mlrpc_xaction *mxa) 525 { 526 struct mlrpc_binding *mbind = mxa->binding; 527 struct mlrpc_service *msvc = mbind->service; 528 struct ndr_typeinfo *intf_ti = msvc->interface_ti; 529 struct mlrpc_stub_table *ste; 530 int opnum = mxa->opnum; 531 unsigned p_len = intf_ti->c_size_fixed_part; 532 char *param; 533 int rc; 534 535 if (mxa->heap == NULL) { 536 mlndo_printf(0, 0, "%s[0x%02x]: no heap", msvc->name, opnum); 537 return (MLRPC_DRC_FAULT_OUT_OF_MEMORY); 538 } 539 540 if ((ste = mlrpc_find_stub_in_svc(msvc, opnum)) == NULL) { 541 mlndo_printf(0, 0, "%s[0x%02x]: invalid opnum", 542 msvc->name, opnum); 543 return (MLRPC_DRC_FAULT_REQUEST_OPNUM_INVALID); 544 } 545 546 if ((param = mlrpc_heap_malloc(mxa->heap, p_len)) == NULL) 547 return (MLRPC_DRC_FAULT_OUT_OF_MEMORY); 548 549 bzero(param, p_len); 550 551 rc = mlrpc_decode_call(mxa, param); 552 if (!MLRPC_DRC_IS_OK(rc)) 553 return (rc); 554 555 rc = (*ste->func)(param, mxa); 556 if (rc == MLRPC_DRC_OK) 557 rc = mlrpc_encode_return(mxa, param); 558 559 return (rc); 560 } 561 562 /* 563 * We can perform some initial setup of the response header here. 564 * We also need to cache some of the information from the bind 565 * negotiation for use during subsequent RPC calls. 566 */ 567 static void 568 mlrpc_reply_prepare_hdr(struct mlrpc_xaction *mxa) 569 { 570 mlrpcconn_common_header_t *rhdr = &mxa->recv_hdr.common_hdr; 571 mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr; 572 573 hdr->rpc_vers = 5; 574 hdr->rpc_vers_minor = 0; 575 hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG + MLRPC_PFC_LAST_FRAG; 576 hdr->packed_drep = rhdr->packed_drep; 577 hdr->frag_length = 0; 578 hdr->auth_length = 0; 579 hdr->call_id = rhdr->call_id; 580 #ifdef _BIG_ENDIAN 581 hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII 582 | MLRPC_REPLAB_INTG_BIG_ENDIAN; 583 #else 584 hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII 585 | MLRPC_REPLAB_INTG_LITTLE_ENDIAN; 586 #endif 587 588 switch (mxa->ptype) { 589 case MLRPC_PTYPE_BIND: 590 hdr->ptype = MLRPC_PTYPE_BIND_ACK; 591 mxa->send_hdr.bind_ack_hdr.max_xmit_frag = 592 mxa->recv_hdr.bind_hdr.max_xmit_frag; 593 mxa->send_hdr.bind_ack_hdr.max_recv_frag = 594 mxa->recv_hdr.bind_hdr.max_recv_frag; 595 mxa->send_hdr.bind_ack_hdr.assoc_group_id = 596 mxa->recv_hdr.bind_hdr.assoc_group_id; 597 598 if (mxa->send_hdr.bind_ack_hdr.assoc_group_id == 0) 599 mxa->send_hdr.bind_ack_hdr.assoc_group_id = time(0); 600 601 /* 602 * Save the maximum fragment sizes 603 * for use with subsequent requests. 604 */ 605 mxa->context->max_xmit_frag = 606 mxa->recv_hdr.bind_hdr.max_xmit_frag; 607 608 mxa->context->max_recv_frag = 609 mxa->recv_hdr.bind_hdr.max_recv_frag; 610 611 break; 612 613 case MLRPC_PTYPE_REQUEST: 614 hdr->ptype = MLRPC_PTYPE_RESPONSE; 615 /* mxa->send_hdr.response_hdr.alloc_hint */ 616 mxa->send_hdr.response_hdr.p_cont_id = 617 mxa->recv_hdr.request_hdr.p_cont_id; 618 mxa->send_hdr.response_hdr.cancel_count = 0; 619 mxa->send_hdr.response_hdr.reserved = 0; 620 break; 621 622 case MLRPC_PTYPE_ALTER_CONTEXT: 623 hdr->ptype = MLRPC_PTYPE_ALTER_CONTEXT_RESP; 624 /* 625 * The max_xmit_frag, max_recv_frag 626 * and assoc_group_id are ignored. 627 */ 628 break; 629 630 default: 631 hdr->ptype = 0xFF; 632 } 633 } 634 635 /* 636 * Finish and encode the bind acknowledge (MLRPC_PTYPE_BIND_ACK) header. 637 * The frag_length is different from a regular RPC response. 638 */ 639 static void 640 mlrpc_reply_bind_ack(struct mlrpc_xaction *mxa) 641 { 642 mlrpcconn_common_header_t *hdr; 643 mlrpcconn_bind_ack_hdr_t *bahdr; 644 645 hdr = &mxa->send_hdr.common_hdr; 646 bahdr = &mxa->send_hdr.bind_ack_hdr; 647 hdr->frag_length = mlrpc_bind_ack_hdr_size(bahdr); 648 } 649 650 /* 651 * Signal an RPC fault. The stream is reset and we overwrite whatever 652 * was in the response header with the fault information. 653 */ 654 static void 655 mlrpc_reply_fault(struct mlrpc_xaction *mxa, unsigned long drc) 656 { 657 mlrpcconn_common_header_t *rhdr = &mxa->recv_hdr.common_hdr; 658 mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr; 659 struct mlndr_stream *mlnds = &mxa->send_mlnds; 660 unsigned long fault_status; 661 662 MLNDS_RESET(mlnds); 663 664 hdr->rpc_vers = 5; 665 hdr->rpc_vers_minor = 0; 666 hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG + MLRPC_PFC_LAST_FRAG; 667 hdr->packed_drep = rhdr->packed_drep; 668 hdr->frag_length = sizeof (mxa->send_hdr.fault_hdr); 669 hdr->auth_length = 0; 670 hdr->call_id = rhdr->call_id; 671 #ifdef _BIG_ENDIAN 672 hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII 673 | MLRPC_REPLAB_INTG_BIG_ENDIAN; 674 #else 675 hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII 676 | MLRPC_REPLAB_INTG_LITTLE_ENDIAN; 677 #endif 678 679 switch (drc & MLRPC_DRC_MASK_SPECIFIER) { 680 case MLRPC_DRC_FAULT_OUT_OF_MEMORY: 681 case MLRPC_DRC_FAULT_ENCODE_TOO_BIG: 682 fault_status = MLRPC_FAULT_NCA_OUT_ARGS_TOO_BIG; 683 break; 684 685 case MLRPC_DRC_FAULT_REQUEST_PCONT_INVALID: 686 fault_status = MLRPC_FAULT_NCA_INVALID_PRES_CONTEXT_ID; 687 break; 688 689 case MLRPC_DRC_FAULT_REQUEST_OPNUM_INVALID: 690 fault_status = MLRPC_FAULT_NCA_OP_RNG_ERROR; 691 break; 692 693 case MLRPC_DRC_FAULT_DECODE_FAILED: 694 case MLRPC_DRC_FAULT_ENCODE_FAILED: 695 fault_status = MLRPC_FAULT_NCA_PROTO_ERROR; 696 break; 697 698 default: 699 fault_status = MLRPC_FAULT_NCA_UNSPEC_REJECT; 700 break; 701 } 702 703 mxa->send_hdr.fault_hdr.common_hdr.ptype = MLRPC_PTYPE_FAULT; 704 mxa->send_hdr.fault_hdr.status = fault_status; 705 mxa->send_hdr.response_hdr.alloc_hint = hdr->frag_length; 706 } 707 708 static int 709 mlrpc_build_reply(struct mlrpc_xaction *mxa) 710 { 711 mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr; 712 struct mlndr_stream *mlnds = &mxa->send_mlnds; 713 unsigned long pdu_size; 714 unsigned long frag_size; 715 unsigned long pdu_data_size; 716 unsigned long frag_data_size; 717 uint32_t rem_dlen; 718 uint32_t save_rem_dlen; 719 uint32_t bytesoff; 720 uint32_t cnt; 721 uint32_t obytes; 722 uint32_t num_ext_frags; 723 uint16_t last_frag = 0; 724 uchar_t *frag_startp; 725 mlrpcconn_common_header_t *rpc_hdr; 726 727 hdr = &mxa->send_hdr.common_hdr; 728 729 frag_size = mlrpc_frag_size; 730 pdu_size = mlnds->pdu_size; 731 732 if (pdu_size <= frag_size) { 733 /* 734 * Single fragment response. The PDU size may be zero 735 * here (i.e. bind or fault response). So don't make 736 * any assumptions about it until after the header is 737 * encoded. 738 */ 739 switch (hdr->ptype) { 740 case MLRPC_PTYPE_BIND_ACK: 741 mlrpc_reply_bind_ack(mxa); 742 break; 743 744 case MLRPC_PTYPE_FAULT: 745 /* already setup */ 746 break; 747 748 case MLRPC_PTYPE_RESPONSE: 749 hdr->frag_length = pdu_size; 750 mxa->send_hdr.response_hdr.alloc_hint = 751 hdr->frag_length; 752 break; 753 754 default: 755 hdr->frag_length = pdu_size; 756 break; 757 } 758 759 mlnds->pdu_scan_offset = 0; 760 (void) mlrpc_encode_pdu_hdr(mxa); 761 762 mlnds->pdu_size_with_rpc_hdrs = mlnds->pdu_size; 763 mlnds->pdu_base_addr_with_rpc_hdrs = 0; 764 return (0); 765 } 766 767 /* 768 * Multiple fragment response. 769 */ 770 hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG; 771 hdr->frag_length = frag_size; 772 mxa->send_hdr.response_hdr.alloc_hint = pdu_size - MLRPC_RSP_HDR_SIZE; 773 mlnds->pdu_scan_offset = 0; 774 775 (void) mlrpc_encode_pdu_hdr(mxa); 776 777 /* 778 * We need to update the 24-byte header in subsequent fragments. 779 * 780 * pdu_data_size: total data remaining to be handled 781 * frag_size: total fragment size including header 782 * frag_data_size: data in fragment 783 * (i.e. frag_size - MLRPC_RSP_HDR_SIZE) 784 */ 785 pdu_data_size = pdu_size - MLRPC_RSP_HDR_SIZE; 786 frag_data_size = frag_size - MLRPC_RSP_HDR_SIZE; 787 788 num_ext_frags = pdu_data_size / frag_data_size; 789 790 /* 791 * We may need to stretch the pipe and insert an RPC header 792 * at each frag boundary. The response will get chunked into 793 * xdrlen sizes for each trans request. 794 */ 795 mlnds->pdu_base_addr_with_rpc_hdrs 796 = malloc(pdu_size + (num_ext_frags * MLRPC_RSP_HDR_SIZE)); 797 mlnds->pdu_size_with_rpc_hdrs = 798 mlnds->pdu_size + (num_ext_frags * MLRPC_RSP_HDR_SIZE); 799 800 /* 801 * Start stretching loop. 802 */ 803 bcopy(mlnds->pdu_base_addr, 804 mlnds->pdu_base_addr_with_rpc_hdrs, frag_size); 805 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 806 rpc_hdr = (mlrpcconn_common_header_t *) 807 mlnds->pdu_base_addr_with_rpc_hdrs; 808 rpc_hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG; 809 rem_dlen = pdu_data_size - frag_size; 810 bytesoff = frag_size; 811 cnt = 1; 812 while (num_ext_frags--) { 813 /* first copy the RPC header to the front of the frag */ 814 bcopy(mlnds->pdu_base_addr, mlnds->pdu_base_addr_with_rpc_hdrs + 815 (cnt * frag_size), MLRPC_RSP_HDR_SIZE); 816 817 /* then copy the data portion of the frag */ 818 save_rem_dlen = rem_dlen; 819 if (rem_dlen >= (frag_size - MLRPC_RSP_HDR_SIZE)) { 820 rem_dlen = rem_dlen - frag_size + MLRPC_RSP_HDR_SIZE; 821 obytes = frag_size - MLRPC_RSP_HDR_SIZE; 822 } else { 823 last_frag = 1; /* this is the last one */ 824 obytes = rem_dlen; 825 } 826 827 frag_startp = mlnds->pdu_base_addr_with_rpc_hdrs + 828 (cnt * frag_size); 829 bcopy(mlnds->pdu_base_addr + bytesoff, 830 frag_startp + MLRPC_RSP_HDR_SIZE, obytes); 831 832 /* set the FRAG FLAGS in the frag header spot */ 833 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 834 rpc_hdr = (mlrpcconn_common_header_t *)frag_startp; 835 if (last_frag) { 836 rpc_hdr->frag_length = save_rem_dlen; 837 rpc_hdr->pfc_flags = MLRPC_PFC_LAST_FRAG; 838 } else { 839 rpc_hdr->pfc_flags = 0; 840 } 841 842 bytesoff += (frag_size - MLRPC_RSP_HDR_SIZE); 843 cnt++; 844 } 845 846 return (0); 847 } 848