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