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 }