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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2015 Nexenta Systems, Inc. All rights reserved. 24 */ 25 26 /* 27 * Server side RPC handler. 28 */ 29 30 #include <sys/byteorder.h> 31 #include <sys/uio.h> 32 #include <errno.h> 33 #include <synch.h> 34 #include <stdlib.h> 35 #include <strings.h> 36 #include <string.h> 37 #include <thread.h> 38 39 #include <libmlrpc.h> 40 41 #define NDR_PIPE_SEND(np, buf, len) \ 42 ((np)->np_send)((np), (buf), (len)) 43 #define NDR_PIPE_RECV(np, buf, len) \ 44 ((np)->np_recv)((np), (buf), (len)) 45 46 static int ndr_svc_process(ndr_xa_t *); 47 static int ndr_svc_bind(ndr_xa_t *); 48 static int ndr_svc_request(ndr_xa_t *); 49 static void ndr_reply_prepare_hdr(ndr_xa_t *); 50 static int ndr_svc_alter_context(ndr_xa_t *); 51 static void ndr_reply_fault(ndr_xa_t *, unsigned long); 52 53 static int ndr_recv_request(ndr_xa_t *mxa); 54 static int ndr_recv_frag(ndr_xa_t *mxa); 55 static int ndr_send_reply(ndr_xa_t *); 56 57 static int ndr_pipe_process(ndr_pipe_t *, ndr_xa_t *); 58 59 /* 60 * External entry point called by smbd. 61 */ 62 void 63 ndr_pipe_worker(ndr_pipe_t *np) 64 { 65 ndr_xa_t *mxa; 66 int rc; 67 68 ndr_svc_binding_pool_init(&np->np_binding, np->np_binding_pool, 69 NDR_N_BINDING_POOL); 70 71 if ((mxa = malloc(sizeof (*mxa))) == NULL) 72 return; 73 74 do { 75 bzero(mxa, sizeof (*mxa)); 76 rc = ndr_pipe_process(np, mxa); 77 } while (rc == 0); 78 79 free(mxa); 80 81 /* 82 * Ensure that there are no RPC service policy handles 83 * (associated with this fid) left around. 84 */ 85 ndr_hdclose(np); 86 } 87 88 /* 89 * Process one server-side RPC request. 90 */ 91 static int 92 ndr_pipe_process(ndr_pipe_t *np, ndr_xa_t *mxa) 93 { 94 ndr_stream_t *recv_nds; 95 ndr_stream_t *send_nds; 96 int rc = ENOMEM; 97 98 mxa->pipe = np; 99 mxa->binding_list = np->np_binding; 100 101 if ((mxa->heap = ndr_heap_create()) == NULL) 102 goto out1; 103 104 recv_nds = &mxa->recv_nds; 105 rc = nds_initialize(recv_nds, 0, NDR_MODE_CALL_RECV, mxa->heap); 106 if (rc != 0) 107 goto out2; 108 109 send_nds = &mxa->send_nds; 110 rc = nds_initialize(send_nds, 0, NDR_MODE_RETURN_SEND, mxa->heap); 111 if (rc != 0) 112 goto out3; 113 114 rc = ndr_recv_request(mxa); 115 if (rc != 0) 116 goto out4; 117 118 (void) ndr_svc_process(mxa); 119 (void) ndr_send_reply(mxa); 120 rc = 0; 121 122 out4: 123 nds_destruct(&mxa->send_nds); 124 out3: 125 nds_destruct(&mxa->recv_nds); 126 out2: 127 ndr_heap_destroy(mxa->heap); 128 out1: 129 return (rc); 130 } 131 132 /* 133 * Receive an entire RPC request (all fragments) 134 * Returns zero or an NDR fault code. 135 */ 136 static int 137 ndr_recv_request(ndr_xa_t *mxa) 138 { 139 ndr_common_header_t *hdr = &mxa->recv_hdr.common_hdr; 140 ndr_stream_t *nds = &mxa->recv_nds; 141 unsigned long saved_size; 142 int rc; 143 144 rc = ndr_recv_frag(mxa); 145 if (rc != 0) 146 return (rc); 147 if (!NDR_IS_FIRST_FRAG(hdr->pfc_flags)) 148 return (NDR_DRC_FAULT_DECODE_FAILED); 149 150 while (!NDR_IS_LAST_FRAG(hdr->pfc_flags)) { 151 rc = ndr_recv_frag(mxa); 152 if (rc != 0) 153 return (rc); 154 } 155 nds->pdu_scan_offset = 0; 156 157 /* 158 * This whacks nds->pdu_size, so save/restore. 159 * It leaves scan_offset after the header. 160 */ 161 saved_size = nds->pdu_size; 162 rc = ndr_decode_pdu_hdr(mxa); 163 nds->pdu_size = saved_size; 164 165 return (rc); 166 } 167 168 /* 169 * Read one fragment, leaving the decoded frag header in 170 * recv_hdr.common_hdr, and the data in the recv_nds. 171 * 172 * Returns zero or an NDR fault code. 173 * 174 * If a first frag, the header is included in the data 175 * placed in recv_nds (because it's not fully decoded 176 * until later - we only decode the common part here). 177 * Additional frags are placed in the recv_nds without 178 * the header, so that after the first frag header, 179 * the remaining data will be contiguous. We do this 180 * by simply not advancing the offset in recv_nds after 181 * reading and decoding these additional fragments, so 182 * the payload of such frags will overwrite what was 183 * (temporarily) the frag header. 184 */ 185 static int 186 ndr_recv_frag(ndr_xa_t *mxa) 187 { 188 ndr_common_header_t *hdr = &mxa->recv_hdr.common_hdr; 189 ndr_stream_t *nds = &mxa->recv_nds; 190 unsigned char *data; 191 unsigned long next_offset; 192 unsigned long pay_size; 193 int rc; 194 195 /* Make room for the frag header. */ 196 next_offset = nds->pdu_scan_offset + NDR_RSP_HDR_SIZE; 197 if (!NDS_GROW_PDU(nds, next_offset, 0)) 198 return (NDR_DRC_FAULT_OUT_OF_MEMORY); 199 200 /* Read the frag header. */ 201 data = nds->pdu_base_addr + nds->pdu_scan_offset; 202 rc = NDR_PIPE_RECV(mxa->pipe, data, NDR_RSP_HDR_SIZE); 203 if (rc != 0) 204 return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT); 205 206 /* 207 * Decode the frag header, get the length. 208 * NB: It uses nds->pdu_scan_offset 209 */ 210 ndr_decode_frag_hdr(nds, hdr); 211 ndr_show_hdr(hdr); 212 if (hdr->frag_length < NDR_RSP_HDR_SIZE || 213 hdr->frag_length > mxa->pipe->np_max_xmit_frag) 214 return (NDR_DRC_FAULT_DECODE_FAILED); 215 216 if (nds->pdu_scan_offset == 0) { 217 /* First frag: header stays in the data. */ 218 nds->pdu_scan_offset = next_offset; 219 } /* else overwrite with the payload */ 220 221 /* Make room for the payload. */ 222 pay_size = hdr->frag_length - NDR_RSP_HDR_SIZE; 223 next_offset = nds->pdu_scan_offset + pay_size; 224 if (!NDS_GROW_PDU(nds, next_offset, 0)) 225 return (NDR_DRC_FAULT_OUT_OF_MEMORY); 226 227 /* Read the payload. */ 228 data = nds->pdu_base_addr + nds->pdu_scan_offset; 229 rc = NDR_PIPE_RECV(mxa->pipe, data, pay_size); 230 if (rc != 0) 231 return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT); 232 nds->pdu_scan_offset = next_offset; 233 234 return (NDR_DRC_OK); 235 } 236 237 /* 238 * This is the entry point for all server-side RPC processing. 239 * It is assumed that the PDU has already been received. 240 */ 241 static int 242 ndr_svc_process(ndr_xa_t *mxa) 243 { 244 int rc; 245 246 (void) ndr_reply_prepare_hdr(mxa); 247 248 switch (mxa->ptype) { 249 case NDR_PTYPE_BIND: 250 rc = ndr_svc_bind(mxa); 251 break; 252 253 case NDR_PTYPE_REQUEST: 254 rc = ndr_svc_request(mxa); 255 break; 256 257 case NDR_PTYPE_ALTER_CONTEXT: 258 rc = ndr_svc_alter_context(mxa); 259 break; 260 261 default: 262 rc = NDR_DRC_FAULT_RPCHDR_PTYPE_INVALID; 263 break; 264 } 265 266 if (NDR_DRC_IS_FAULT(rc)) 267 ndr_reply_fault(mxa, rc); 268 269 return (rc); 270 } 271 272 /* 273 * Multiple p_cont_elem[]s, multiple transfer_syntaxes[] and multiple 274 * p_results[] not supported. 275 */ 276 static int 277 ndr_svc_bind(ndr_xa_t *mxa) 278 { 279 ndr_p_cont_list_t *cont_list; 280 ndr_p_result_list_t *result_list; 281 ndr_p_result_t *result; 282 unsigned p_cont_id; 283 ndr_binding_t *mbind; 284 ndr_uuid_t *as_uuid; 285 ndr_uuid_t *ts_uuid; 286 int as_vers; 287 int ts_vers; 288 ndr_service_t *msvc; 289 int rc; 290 ndr_port_any_t *sec_addr; 291 292 /* acquire targets */ 293 cont_list = &mxa->recv_hdr.bind_hdr.p_context_elem; 294 result_list = &mxa->send_hdr.bind_ack_hdr.p_result_list; 295 result = &result_list->p_results[0]; 296 297 /* 298 * Set up temporary secondary address port. 299 * We will correct this later (below). 300 */ 301 sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr; 302 sec_addr->length = 13; 303 (void) strcpy((char *)sec_addr->port_spec, "\\PIPE\\ntsvcs"); 304 305 result_list->n_results = 1; 306 result_list->reserved = 0; 307 result_list->reserved2 = 0; 308 result->result = NDR_PCDR_ACCEPTANCE; 309 result->reason = 0; 310 bzero(&result->transfer_syntax, sizeof (result->transfer_syntax)); 311 312 /* sanity check */ 313 if (cont_list->n_context_elem != 1 || 314 cont_list->p_cont_elem[0].n_transfer_syn != 1) { 315 ndo_trace("ndr_svc_bind: warning: multiple p_cont_elem"); 316 } 317 318 p_cont_id = cont_list->p_cont_elem[0].p_cont_id; 319 320 if ((mbind = ndr_svc_find_binding(mxa, p_cont_id)) != NULL) { 321 /* 322 * Duplicate presentation context id. 323 */ 324 ndo_trace("ndr_svc_bind: duplicate binding"); 325 return (NDR_DRC_FAULT_BIND_PCONT_BUSY); 326 } 327 328 if ((mbind = ndr_svc_new_binding(mxa)) == NULL) { 329 /* 330 * No free binding slot 331 */ 332 result->result = NDR_PCDR_PROVIDER_REJECTION; 333 result->reason = NDR_PPR_LOCAL_LIMIT_EXCEEDED; 334 ndo_trace("ndr_svc_bind: no resources"); 335 return (NDR_DRC_OK); 336 } 337 338 as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid; 339 as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version; 340 341 ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid; 342 ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version; 343 344 msvc = ndr_svc_lookup_uuid(as_uuid, as_vers, ts_uuid, ts_vers); 345 if (msvc == NULL) { 346 result->result = NDR_PCDR_PROVIDER_REJECTION; 347 result->reason = NDR_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED; 348 return (NDR_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 NDR_PORT_ANY_MAX_PORT_SPEC); 358 359 mbind->p_cont_id = p_cont_id; 360 mbind->which_side = NDR_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 (NDR_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 return (NDR_DRC_BINDING_MADE); 387 } 388 389 /* 390 * ndr_svc_alter_context 391 * 392 * The alter context request is used to request additional presentation 393 * context for another interface and/or version. It is very similar to 394 * a bind request. 395 */ 396 static int 397 ndr_svc_alter_context(ndr_xa_t *mxa) 398 { 399 ndr_p_result_list_t *result_list; 400 ndr_p_result_t *result; 401 ndr_p_cont_list_t *cont_list; 402 ndr_binding_t *mbind; 403 ndr_service_t *msvc; 404 unsigned p_cont_id; 405 ndr_uuid_t *as_uuid; 406 ndr_uuid_t *ts_uuid; 407 int as_vers; 408 int ts_vers; 409 ndr_port_any_t *sec_addr; 410 411 result_list = &mxa->send_hdr.alter_context_rsp_hdr.p_result_list; 412 result_list->n_results = 1; 413 result_list->reserved = 0; 414 result_list->reserved2 = 0; 415 416 result = &result_list->p_results[0]; 417 result->result = NDR_PCDR_ACCEPTANCE; 418 result->reason = 0; 419 bzero(&result->transfer_syntax, sizeof (result->transfer_syntax)); 420 421 cont_list = &mxa->recv_hdr.alter_context_hdr.p_context_elem; 422 p_cont_id = cont_list->p_cont_elem[0].p_cont_id; 423 424 if (ndr_svc_find_binding(mxa, p_cont_id) != NULL) 425 return (NDR_DRC_FAULT_BIND_PCONT_BUSY); 426 427 if ((mbind = ndr_svc_new_binding(mxa)) == NULL) { 428 result->result = NDR_PCDR_PROVIDER_REJECTION; 429 result->reason = NDR_PPR_LOCAL_LIMIT_EXCEEDED; 430 return (NDR_DRC_OK); 431 } 432 433 as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid; 434 as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version; 435 436 ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid; 437 ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version; 438 439 msvc = ndr_svc_lookup_uuid(as_uuid, as_vers, ts_uuid, ts_vers); 440 if (msvc == NULL) { 441 result->result = NDR_PCDR_PROVIDER_REJECTION; 442 result->reason = NDR_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED; 443 return (NDR_DRC_OK); 444 } 445 446 mbind->p_cont_id = p_cont_id; 447 mbind->which_side = NDR_BIND_SIDE_SERVER; 448 /* mbind->context set by app */ 449 mbind->service = msvc; 450 mbind->instance_specific = 0; 451 mxa->binding = mbind; 452 453 sec_addr = &mxa->send_hdr.alter_context_rsp_hdr.sec_addr; 454 sec_addr->length = 0; 455 bzero(sec_addr->port_spec, NDR_PORT_ANY_MAX_PORT_SPEC); 456 457 result->transfer_syntax = 458 cont_list->p_cont_elem[0].transfer_syntaxes[0]; 459 460 return (NDR_DRC_BINDING_MADE); 461 } 462 463 static int 464 ndr_svc_request(ndr_xa_t *mxa) 465 { 466 ndr_binding_t *mbind; 467 ndr_service_t *msvc; 468 unsigned p_cont_id; 469 int rc; 470 471 mxa->opnum = mxa->recv_hdr.request_hdr.opnum; 472 p_cont_id = mxa->recv_hdr.request_hdr.p_cont_id; 473 474 if ((mbind = ndr_svc_find_binding(mxa, p_cont_id)) == NULL) 475 return (NDR_DRC_FAULT_REQUEST_PCONT_INVALID); 476 477 mxa->binding = mbind; 478 msvc = mbind->service; 479 480 /* 481 * Make room for the response hdr. 482 */ 483 mxa->send_nds.pdu_scan_offset = NDR_RSP_HDR_SIZE; 484 485 if (msvc->call_stub) 486 rc = (*msvc->call_stub)(mxa); 487 else 488 rc = ndr_generic_call_stub(mxa); 489 490 if (NDR_DRC_IS_FAULT(rc)) { 491 ndo_printf(0, 0, "%s[0x%02x]: 0x%04x", 492 msvc->name, mxa->opnum, rc); 493 } 494 495 return (rc); 496 } 497 498 /* 499 * The transaction and the two nds streams use the same heap, which 500 * should already exist at this point. The heap will also be available 501 * to the stub. 502 */ 503 int 504 ndr_generic_call_stub(ndr_xa_t *mxa) 505 { 506 ndr_binding_t *mbind = mxa->binding; 507 ndr_service_t *msvc = mbind->service; 508 ndr_typeinfo_t *intf_ti = msvc->interface_ti; 509 ndr_stub_table_t *ste; 510 int opnum = mxa->opnum; 511 unsigned p_len = intf_ti->c_size_fixed_part; 512 char *param; 513 int rc; 514 515 if (mxa->heap == NULL) { 516 ndo_printf(0, 0, "%s[0x%02x]: no heap", msvc->name, opnum); 517 return (NDR_DRC_FAULT_OUT_OF_MEMORY); 518 } 519 520 if ((ste = ndr_svc_find_stub(msvc, opnum)) == NULL) { 521 ndo_printf(0, 0, "%s[0x%02x]: invalid opnum", 522 msvc->name, opnum); 523 return (NDR_DRC_FAULT_REQUEST_OPNUM_INVALID); 524 } 525 526 if ((param = ndr_heap_malloc(mxa->heap, p_len)) == NULL) 527 return (NDR_DRC_FAULT_OUT_OF_MEMORY); 528 529 bzero(param, p_len); 530 531 rc = ndr_decode_call(mxa, param); 532 if (!NDR_DRC_IS_OK(rc)) 533 return (rc); 534 535 rc = (*ste->func)(param, mxa); 536 if (rc == NDR_DRC_OK) 537 rc = ndr_encode_return(mxa, param); 538 539 return (rc); 540 } 541 542 /* 543 * We can perform some initial setup of the response header here. 544 * We also need to cache some of the information from the bind 545 * negotiation for use during subsequent RPC calls. 546 */ 547 static void 548 ndr_reply_prepare_hdr(ndr_xa_t *mxa) 549 { 550 ndr_common_header_t *rhdr = &mxa->recv_hdr.common_hdr; 551 ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; 552 553 hdr->rpc_vers = 5; 554 hdr->rpc_vers_minor = 0; 555 hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG; 556 hdr->packed_drep = rhdr->packed_drep; 557 hdr->frag_length = 0; 558 hdr->auth_length = 0; 559 hdr->call_id = rhdr->call_id; 560 #ifdef _BIG_ENDIAN 561 hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII 562 | NDR_REPLAB_INTG_BIG_ENDIAN; 563 #else 564 hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII 565 | NDR_REPLAB_INTG_LITTLE_ENDIAN; 566 #endif 567 568 switch (mxa->ptype) { 569 case NDR_PTYPE_BIND: 570 /* 571 * Compute the maximum fragment sizes for xmit/recv 572 * and store in the pipe endpoint. Note "xmit" is 573 * client-to-server; "recv" is server-to-client. 574 */ 575 if (mxa->pipe->np_max_xmit_frag > 576 mxa->recv_hdr.bind_hdr.max_xmit_frag) 577 mxa->pipe->np_max_xmit_frag = 578 mxa->recv_hdr.bind_hdr.max_xmit_frag; 579 if (mxa->pipe->np_max_recv_frag > 580 mxa->recv_hdr.bind_hdr.max_recv_frag) 581 mxa->pipe->np_max_recv_frag = 582 mxa->recv_hdr.bind_hdr.max_recv_frag; 583 584 hdr->ptype = NDR_PTYPE_BIND_ACK; 585 mxa->send_hdr.bind_ack_hdr.max_xmit_frag = 586 mxa->pipe->np_max_xmit_frag; 587 mxa->send_hdr.bind_ack_hdr.max_recv_frag = 588 mxa->pipe->np_max_recv_frag; 589 590 /* 591 * We're supposed to assign a unique "assoc group" 592 * (identifies this connection for the client). 593 * Using the pipe address is adequate. 594 */ 595 mxa->send_hdr.bind_ack_hdr.assoc_group_id = 596 mxa->recv_hdr.bind_hdr.assoc_group_id; 597 if (mxa->send_hdr.bind_ack_hdr.assoc_group_id == 0) 598 mxa->send_hdr.bind_ack_hdr.assoc_group_id = 599 (DWORD)(uintptr_t)mxa->pipe; 600 601 break; 602 603 case NDR_PTYPE_REQUEST: 604 hdr->ptype = NDR_PTYPE_RESPONSE; 605 /* mxa->send_hdr.response_hdr.alloc_hint */ 606 mxa->send_hdr.response_hdr.p_cont_id = 607 mxa->recv_hdr.request_hdr.p_cont_id; 608 mxa->send_hdr.response_hdr.cancel_count = 0; 609 mxa->send_hdr.response_hdr.reserved = 0; 610 break; 611 612 case NDR_PTYPE_ALTER_CONTEXT: 613 hdr->ptype = NDR_PTYPE_ALTER_CONTEXT_RESP; 614 /* 615 * The max_xmit_frag, max_recv_frag and assoc_group_id are 616 * ignored by the client but it's useful to fill them in. 617 */ 618 mxa->send_hdr.alter_context_rsp_hdr.max_xmit_frag = 619 mxa->recv_hdr.alter_context_hdr.max_xmit_frag; 620 mxa->send_hdr.alter_context_rsp_hdr.max_recv_frag = 621 mxa->recv_hdr.alter_context_hdr.max_recv_frag; 622 mxa->send_hdr.alter_context_rsp_hdr.assoc_group_id = 623 mxa->recv_hdr.alter_context_hdr.assoc_group_id; 624 break; 625 626 default: 627 hdr->ptype = 0xFF; 628 } 629 } 630 631 /* 632 * Signal an RPC fault. The stream is reset and we overwrite whatever 633 * was in the response header with the fault information. 634 */ 635 static void 636 ndr_reply_fault(ndr_xa_t *mxa, unsigned long drc) 637 { 638 ndr_common_header_t *rhdr = &mxa->recv_hdr.common_hdr; 639 ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; 640 ndr_stream_t *nds = &mxa->send_nds; 641 unsigned long fault_status; 642 643 NDS_RESET(nds); 644 645 hdr->rpc_vers = 5; 646 hdr->rpc_vers_minor = 0; 647 hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG; 648 hdr->packed_drep = rhdr->packed_drep; 649 hdr->frag_length = sizeof (mxa->send_hdr.fault_hdr); 650 hdr->auth_length = 0; 651 hdr->call_id = rhdr->call_id; 652 #ifdef _BIG_ENDIAN 653 hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII 654 | NDR_REPLAB_INTG_BIG_ENDIAN; 655 #else 656 hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII 657 | NDR_REPLAB_INTG_LITTLE_ENDIAN; 658 #endif 659 660 switch (drc & NDR_DRC_MASK_SPECIFIER) { 661 case NDR_DRC_FAULT_OUT_OF_MEMORY: 662 case NDR_DRC_FAULT_ENCODE_TOO_BIG: 663 fault_status = NDR_FAULT_NCA_OUT_ARGS_TOO_BIG; 664 break; 665 666 case NDR_DRC_FAULT_REQUEST_PCONT_INVALID: 667 fault_status = NDR_FAULT_NCA_INVALID_PRES_CONTEXT_ID; 668 break; 669 670 case NDR_DRC_FAULT_REQUEST_OPNUM_INVALID: 671 fault_status = NDR_FAULT_NCA_OP_RNG_ERROR; 672 break; 673 674 case NDR_DRC_FAULT_DECODE_FAILED: 675 case NDR_DRC_FAULT_ENCODE_FAILED: 676 fault_status = NDR_FAULT_NCA_PROTO_ERROR; 677 break; 678 679 default: 680 fault_status = NDR_FAULT_NCA_UNSPEC_REJECT; 681 break; 682 } 683 684 mxa->send_hdr.fault_hdr.common_hdr.ptype = NDR_PTYPE_FAULT; 685 mxa->send_hdr.fault_hdr.status = fault_status; 686 mxa->send_hdr.response_hdr.alloc_hint = hdr->frag_length; 687 } 688 689 /* 690 * Note that the frag_length for bind ack and alter context is 691 * non-standard. 692 */ 693 static int 694 ndr_send_reply(ndr_xa_t *mxa) 695 { 696 ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; 697 ndr_stream_t *nds = &mxa->send_nds; 698 uint8_t *pdu_buf; 699 unsigned long pdu_size; 700 unsigned long frag_size; 701 unsigned long pdu_data_size; 702 unsigned long frag_data_size; 703 704 frag_size = mxa->pipe->np_max_recv_frag; 705 pdu_size = nds->pdu_size; 706 pdu_buf = nds->pdu_base_addr; 707 708 if (pdu_size <= frag_size) { 709 /* 710 * Single fragment response. The PDU size may be zero 711 * here (i.e. bind or fault response). So don't make 712 * any assumptions about it until after the header is 713 * encoded. 714 */ 715 switch (hdr->ptype) { 716 case NDR_PTYPE_BIND_ACK: 717 hdr->frag_length = ndr_bind_ack_hdr_size(mxa); 718 break; 719 720 case NDR_PTYPE_FAULT: 721 /* already setup */ 722 break; 723 724 case NDR_PTYPE_RESPONSE: 725 hdr->frag_length = pdu_size; 726 mxa->send_hdr.response_hdr.alloc_hint = 727 hdr->frag_length; 728 break; 729 730 case NDR_PTYPE_ALTER_CONTEXT_RESP: 731 hdr->frag_length = ndr_alter_context_rsp_hdr_size(); 732 break; 733 734 default: 735 hdr->frag_length = pdu_size; 736 break; 737 } 738 739 nds->pdu_scan_offset = 0; 740 (void) ndr_encode_pdu_hdr(mxa); 741 pdu_size = nds->pdu_size; 742 (void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, pdu_size); 743 return (0); 744 } 745 746 /* 747 * Multiple fragment response. 748 * 749 * We need to update the RPC header for every fragment. 750 * 751 * pdu_data_size: total data remaining to be handled 752 * frag_size: total fragment size including header 753 * frag_data_size: data in fragment 754 * (i.e. frag_size - NDR_RSP_HDR_SIZE) 755 */ 756 pdu_data_size = pdu_size - NDR_RSP_HDR_SIZE; 757 frag_data_size = frag_size - NDR_RSP_HDR_SIZE; 758 759 /* 760 * Send the first frag. 761 */ 762 hdr->pfc_flags = NDR_PFC_FIRST_FRAG; 763 hdr->frag_length = frag_size; 764 mxa->send_hdr.response_hdr.alloc_hint = pdu_data_size; 765 nds->pdu_scan_offset = 0; 766 (void) ndr_encode_pdu_hdr(mxa); 767 (void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, frag_size); 768 pdu_data_size -= frag_data_size; 769 pdu_buf += frag_data_size; 770 771 /* 772 * Send "middle" (full-sized) fragments... 773 */ 774 hdr->pfc_flags = 0; 775 while (pdu_data_size > frag_data_size) { 776 777 hdr->frag_length = frag_size; 778 mxa->send_hdr.response_hdr.alloc_hint = pdu_data_size; 779 nds->pdu_scan_offset = 0; 780 (void) ndr_encode_pdu_hdr(mxa); 781 bcopy(nds->pdu_base_addr, pdu_buf, NDR_RSP_HDR_SIZE); 782 (void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, frag_size); 783 pdu_data_size -= frag_data_size; 784 pdu_buf += frag_data_size; 785 } 786 787 /* 788 * Last frag (pdu_data_size <= frag_data_size) 789 */ 790 hdr->pfc_flags = NDR_PFC_LAST_FRAG; 791 frag_size = pdu_data_size + NDR_RSP_HDR_SIZE; 792 hdr->frag_length = frag_size; 793 mxa->send_hdr.response_hdr.alloc_hint = pdu_data_size; 794 nds->pdu_scan_offset = 0; 795 (void) ndr_encode_pdu_hdr(mxa); 796 bcopy(nds->pdu_base_addr, pdu_buf, NDR_RSP_HDR_SIZE); 797 (void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, frag_size); 798 799 return (0); 800 }