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