summaryrefslogtreecommitdiff
path: root/usr/src/lib/smbsrv/libmlrpc/common/ndr_server.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/smbsrv/libmlrpc/common/ndr_server.c')
-rw-r--r--usr/src/lib/smbsrv/libmlrpc/common/ndr_server.c849
1 files changed, 849 insertions, 0 deletions
diff --git a/usr/src/lib/smbsrv/libmlrpc/common/ndr_server.c b/usr/src/lib/smbsrv/libmlrpc/common/ndr_server.c
new file mode 100644
index 0000000000..a1f203c09c
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlrpc/common/ndr_server.c
@@ -0,0 +1,849 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Server side RPC handler.
+ */
+
+#include <sys/byteorder.h>
+#include <thread.h>
+#include <synch.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <time.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/libmlrpc.h>
+#include <smbsrv/mlsvc.h>
+#include <smbsrv/ndr.h>
+#include <smbsrv/mlrpc.h>
+#include <smbsrv/mlsvc_util.h>
+#include <smbsrv/ntsid.h>
+#include <smbsrv/smb_winpipe.h>
+
+/*
+ * Fragment size (5680: NT style).
+ */
+#define MLRPC_FRAG_SZ 5680
+static unsigned long mlrpc_frag_size = MLRPC_FRAG_SZ;
+
+/*
+ * Context table.
+ */
+#define CTXT_PIPE_SZ 65536
+#define CTXT_TABLE_ENTRIES 128
+static struct mlsvc_rpc_context context_table[CTXT_TABLE_ENTRIES];
+static mutex_t mlrpc_context_lock;
+
+static int mlrpc_s_process(struct mlrpc_xaction *);
+static int mlrpc_s_bind(struct mlrpc_xaction *);
+static int mlrpc_s_request(struct mlrpc_xaction *);
+static void mlrpc_reply_prepare_hdr(struct mlrpc_xaction *);
+static int mlrpc_s_alter_context(struct mlrpc_xaction *);
+static void mlrpc_reply_bind_ack(struct mlrpc_xaction *);
+static void mlrpc_reply_fault(struct mlrpc_xaction *, unsigned long);
+static int mlrpc_build_reply(struct mlrpc_xaction *);
+
+/*
+ * This is the RPC service server-side entry point. All MSRPC encoded
+ * messages should be passed through here. We use the same context
+ * structure as the client side but we don't need to set up the client
+ * side info.
+ */
+struct mlsvc_rpc_context *
+mlrpc_process(int fid, smb_dr_user_ctx_t *user_ctx)
+{
+ struct mlsvc_rpc_context *context;
+ struct mlrpc_xaction *mxa;
+ struct mlndr_stream *recv_mlnds;
+ struct mlndr_stream *send_mlnds;
+ unsigned char *pdu_base_addr;
+ char *data;
+ int datalen;
+
+ if ((context = mlrpc_lookup(fid)) == NULL)
+ return (NULL);
+
+ context->user_ctx = user_ctx;
+ data = context->inpipe->sp_data;
+ datalen = context->inpipe->sp_datalen;
+
+ mxa = (struct mlrpc_xaction *)malloc(sizeof (struct mlrpc_xaction));
+ if (mxa == NULL)
+ return (NULL);
+
+ bzero(mxa, sizeof (struct mlrpc_xaction));
+ mxa->fid = fid;
+ mxa->context = context;
+ mxa->binding_list = context->binding;
+
+ if ((mxa->heap = mlrpc_heap_create()) == NULL) {
+ free(mxa);
+ return (NULL);
+ }
+
+ recv_mlnds = &mxa->recv_mlnds;
+ (void) mlnds_initialize(recv_mlnds, datalen, NDR_MODE_CALL_RECV,
+ mxa->heap);
+
+ bcopy(data, recv_mlnds->pdu_base_addr, datalen);
+
+ send_mlnds = &mxa->send_mlnds;
+ (void) mlnds_initialize(send_mlnds, 0, NDR_MODE_RETURN_SEND, mxa->heap);
+
+ (void) mlrpc_s_process(mxa);
+
+ /*
+ * Different pointers for single frag vs multi frag responses.
+ */
+ if (send_mlnds->pdu_base_addr_with_rpc_hdrs)
+ pdu_base_addr = send_mlnds->pdu_base_addr_with_rpc_hdrs;
+ else
+ pdu_base_addr = send_mlnds->pdu_base_addr;
+
+ datalen = send_mlnds->pdu_size_with_rpc_hdrs;
+ context->outpipe->sp_datalen = datalen;
+ bcopy(pdu_base_addr, context->outpipe->sp_data, datalen);
+
+ mlnds_destruct(&mxa->recv_mlnds);
+ mlnds_destruct(&mxa->send_mlnds);
+ mlrpc_heap_destroy(mxa->heap);
+ free(mxa);
+ return (context);
+}
+
+/*
+ * Lookup the context for pipeid. If one exists, return a pointer to it.
+ * Otherwise attempt to allocate a new context and return it. If the
+ * context table is full, return a null pointer.
+ */
+struct mlsvc_rpc_context *
+mlrpc_lookup(int fid)
+{
+ struct mlsvc_rpc_context *context;
+ struct mlsvc_rpc_context *available = NULL;
+ int i;
+
+ (void) mutex_lock(&mlrpc_context_lock);
+
+ for (i = 0; i < CTXT_TABLE_ENTRIES; ++i) {
+ context = &context_table[i];
+
+ if (available == NULL && context->fid == 0) {
+ available = context;
+ continue;
+ }
+
+ if (context->fid == fid) {
+ (void) mutex_unlock(&mlrpc_context_lock);
+ return (context);
+ }
+ }
+
+ if (available) {
+ bzero(available, sizeof (struct mlsvc_rpc_context));
+ available->inpipe = malloc(CTXT_PIPE_SZ);
+ available->outpipe = malloc(CTXT_PIPE_SZ);
+
+ if (available->inpipe == NULL || available->outpipe == NULL) {
+ free(available->inpipe);
+ free(available->outpipe);
+ bzero(available, sizeof (struct mlsvc_rpc_context));
+ (void) mutex_unlock(&mlrpc_context_lock);
+ return (NULL);
+ }
+
+ bzero(available->inpipe, sizeof (smb_pipe_t));
+ bzero(available->outpipe, sizeof (smb_pipe_t));
+ available->fid = fid;
+ available->inpipe->sp_pipeid = fid;
+ available->outpipe->sp_pipeid = fid;
+
+ mlrpc_binding_pool_initialize(&available->binding,
+ available->binding_pool, CTXT_N_BINDING_POOL);
+ }
+
+ (void) mutex_unlock(&mlrpc_context_lock);
+ return (available);
+}
+
+/*
+ * This function should be called to release the context associated
+ * with a fid when the client performs a close file.
+ */
+void
+mlrpc_release(int fid)
+{
+ struct mlsvc_rpc_context *context;
+ int i;
+
+ (void) mutex_lock(&mlrpc_context_lock);
+
+ for (i = 0; i < CTXT_TABLE_ENTRIES; ++i) {
+ context = &context_table[i];
+
+ if (context->fid == fid) {
+ ndr_hdclose(fid);
+ free(context->inpipe);
+ free(context->outpipe);
+ bzero(context, sizeof (struct mlsvc_rpc_context));
+ break;
+ }
+ }
+
+ (void) mutex_unlock(&mlrpc_context_lock);
+}
+
+/*
+ * This is the entry point for all server-side RPC processing.
+ * It is assumed that the PDU has already been received.
+ */
+static int
+mlrpc_s_process(struct mlrpc_xaction *mxa)
+{
+ int rc;
+
+ rc = mlrpc_decode_pdu_hdr(mxa);
+ if (!MLRPC_DRC_IS_OK(rc))
+ return (-1);
+
+ (void) mlrpc_reply_prepare_hdr(mxa);
+
+ switch (mxa->ptype) {
+ case MLRPC_PTYPE_BIND:
+ rc = mlrpc_s_bind(mxa);
+ break;
+
+ case MLRPC_PTYPE_REQUEST:
+ rc = mlrpc_s_request(mxa);
+ break;
+
+ case MLRPC_PTYPE_ALTER_CONTEXT:
+ rc = mlrpc_s_alter_context(mxa);
+ break;
+
+ default:
+ rc = MLRPC_DRC_FAULT_RPCHDR_PTYPE_INVALID;
+ break;
+ }
+
+ if (MLRPC_DRC_IS_FAULT(rc))
+ mlrpc_reply_fault(mxa, rc);
+
+ (void) mlrpc_build_reply(mxa);
+ return (rc);
+}
+
+/*
+ * Multiple p_cont_elem[]s, multiple transfer_syntaxes[] and multiple
+ * p_results[] not supported.
+ */
+static int
+mlrpc_s_bind(struct mlrpc_xaction *mxa)
+{
+ mlrpc_p_cont_list_t *cont_list;
+ mlrpc_p_result_list_t *result_list;
+ mlrpc_p_result_t *result;
+ unsigned p_cont_id;
+ struct mlrpc_binding *mbind;
+ ndr_uuid_t *as_uuid;
+ ndr_uuid_t *ts_uuid;
+ char as_buf[64];
+ char ts_buf[64];
+ int as_vers;
+ int ts_vers;
+ struct mlndr_stream *send_mlnds;
+ struct mlrpc_service *msvc;
+ int rc;
+ mlrpc_port_any_t *sec_addr;
+
+ /* acquire targets */
+ cont_list = &mxa->recv_hdr.bind_hdr.p_context_elem;
+ result_list = &mxa->send_hdr.bind_ack_hdr.p_result_list;
+ result = &result_list->p_results[0];
+
+ /*
+ * Set up temporary secondary address port.
+ * We will correct this later (below).
+ */
+ send_mlnds = &mxa->send_mlnds;
+ sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
+ sec_addr->length = 13;
+ (void) strcpy((char *)sec_addr->port_spec, "\\PIPE\\ntsvcs");
+
+ result_list->n_results = 1;
+ result_list->reserved = 0;
+ result_list->reserved2 = 0;
+ result->result = MLRPC_PCDR_ACCEPTANCE;
+ result->reason = 0;
+ bzero(&result->transfer_syntax, sizeof (result->transfer_syntax));
+
+ /* sanity check */
+ if (cont_list->n_context_elem != 1 ||
+ cont_list->p_cont_elem[0].n_transfer_syn != 1) {
+ mlndo_trace("mlrpc_s_bind: warning: multiple p_cont_elem");
+ }
+
+ p_cont_id = cont_list->p_cont_elem[0].p_cont_id;
+
+ if ((mbind = mlrpc_find_binding(mxa, p_cont_id)) != NULL) {
+ /*
+ * Duplicate p_cont_id.
+ * Send a bind_ack with a better error.
+ */
+ mlndo_trace("mlrpc_s_bind: duplicate binding");
+ return (MLRPC_DRC_FAULT_BIND_PCONT_BUSY);
+ }
+
+ if ((mbind = mlrpc_new_binding(mxa)) == NULL) {
+ /*
+ * No free binding slot
+ */
+ result->result = MLRPC_PCDR_PROVIDER_REJECTION;
+ result->reason = MLRPC_PPR_LOCAL_LIMIT_EXCEEDED;
+ mlndo_trace("mlrpc_s_bind: no resources");
+ return (MLRPC_DRC_OK);
+ }
+
+ as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid;
+ as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version;
+
+ ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid;
+ ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version;
+
+ msvc = mlrpc_find_service_by_uuids(as_uuid, as_vers, ts_uuid, ts_vers);
+ if (!msvc) {
+ mlrpc_uuid_to_str(as_uuid, as_buf);
+ mlrpc_uuid_to_str(ts_uuid, ts_buf);
+
+ mlndo_printf(send_mlnds, 0, "mlrpc_s_bind: unknown service");
+ mlndo_printf(send_mlnds, 0, "abs=%s v%d, xfer=%s v%d",
+ as_buf, as_vers, ts_buf, ts_vers);
+
+ result->result = MLRPC_PCDR_PROVIDER_REJECTION;
+ result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
+ return (MLRPC_DRC_OK);
+ }
+
+ /*
+ * We can now use the correct secondary address port.
+ */
+ sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
+ sec_addr->length = strlen(msvc->sec_addr_port) + 1;
+ (void) strlcpy((char *)sec_addr->port_spec, msvc->sec_addr_port,
+ MLRPC_PORT_ANY_MAX_PORT_SPEC);
+
+ mbind->p_cont_id = p_cont_id;
+ mbind->which_side = MLRPC_BIND_SIDE_SERVER;
+ /* mbind->context set by app */
+ mbind->service = msvc;
+ mbind->instance_specific = 0;
+
+ mxa->binding = mbind;
+
+ if (msvc->bind_req) {
+ /*
+ * Call the service-specific bind() handler. If
+ * this fails, we shouild send a specific error
+ * on the bind ack.
+ */
+ rc = (msvc->bind_req)(mxa);
+ if (MLRPC_DRC_IS_FAULT(rc)) {
+ mbind->service = 0; /* free binding slot */
+ mbind->which_side = 0;
+ mbind->p_cont_id = 0;
+ mbind->instance_specific = 0;
+ return (rc);
+ }
+ }
+
+ result->transfer_syntax =
+ cont_list->p_cont_elem[0].transfer_syntaxes[0];
+
+ /*
+ * Special rejection of Windows 2000 DSSETUP interface.
+ * This interface was introduced in Windows 2000 but has
+ * been subsequently deprecated due to problems.
+ */
+ if (strcmp(msvc->name, "DSSETUP") == 0) {
+ result->result = MLRPC_PCDR_PROVIDER_REJECTION;
+ result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
+ }
+
+ return (MLRPC_DRC_BINDING_MADE);
+}
+
+/*
+ * mlrpc_s_alter_context
+ *
+ * The alter context request is used to request additional presentation
+ * context for another interface and/or version. It's very similar to a
+ * bind request.
+ *
+ * We don't fully support multiple contexts so, for now, we reject this
+ * request. Windows 2000 clients attempt to use an alternate LSA context
+ * when ACLs are modified.
+ */
+static int
+mlrpc_s_alter_context(struct mlrpc_xaction *mxa)
+{
+ mlrpc_p_result_list_t *result_list;
+ mlrpc_p_result_t *result;
+ mlrpc_p_cont_list_t *cont_list;
+ struct mlrpc_binding *mbind;
+ struct mlrpc_service *msvc;
+ unsigned p_cont_id;
+ ndr_uuid_t *as_uuid;
+ ndr_uuid_t *ts_uuid;
+ int as_vers;
+ int ts_vers;
+ mlrpc_port_any_t *sec_addr;
+
+ result_list = &mxa->send_hdr.bind_ack_hdr.p_result_list;
+ result_list->n_results = 1;
+ result_list->reserved = 0;
+ result_list->reserved2 = 0;
+
+ result = &result_list->p_results[0];
+ result->result = MLRPC_PCDR_ACCEPTANCE;
+ result->reason = 0;
+ bzero(&result->transfer_syntax, sizeof (result->transfer_syntax));
+
+ if (mxa != NULL) {
+ result->result = MLRPC_PCDR_PROVIDER_REJECTION;
+ result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
+ return (MLRPC_DRC_OK);
+ }
+
+ cont_list = &mxa->recv_hdr.bind_hdr.p_context_elem;
+ p_cont_id = cont_list->p_cont_elem[0].p_cont_id;
+
+ if ((mbind = mlrpc_find_binding(mxa, p_cont_id)) != NULL)
+ return (MLRPC_DRC_FAULT_BIND_PCONT_BUSY);
+
+ if ((mbind = mlrpc_new_binding(mxa)) == NULL) {
+ result->result = MLRPC_PCDR_PROVIDER_REJECTION;
+ result->reason = MLRPC_PPR_LOCAL_LIMIT_EXCEEDED;
+ return (MLRPC_DRC_OK);
+ }
+
+ as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid;
+ as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version;
+
+ ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid;
+ ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version;
+
+ msvc = mlrpc_find_service_by_uuids(as_uuid, as_vers, ts_uuid, ts_vers);
+ if (msvc == 0) {
+ result->result = MLRPC_PCDR_PROVIDER_REJECTION;
+ result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
+ return (MLRPC_DRC_OK);
+ }
+
+ mbind->p_cont_id = p_cont_id;
+ mbind->which_side = MLRPC_BIND_SIDE_SERVER;
+ /* mbind->context set by app */
+ mbind->service = msvc;
+ mbind->instance_specific = 0;
+ mxa->binding = mbind;
+
+ sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
+ sec_addr->length = 0;
+ bzero(sec_addr->port_spec, MLRPC_PORT_ANY_MAX_PORT_SPEC);
+
+ result->transfer_syntax =
+ cont_list->p_cont_elem[0].transfer_syntaxes[0];
+
+ return (MLRPC_DRC_BINDING_MADE);
+}
+
+static int
+mlrpc_s_request(struct mlrpc_xaction *mxa)
+{
+ struct mlrpc_binding *mbind;
+ struct mlrpc_service *msvc;
+ unsigned p_cont_id;
+ int rc;
+
+ mxa->opnum = mxa->recv_hdr.request_hdr.opnum;
+ p_cont_id = mxa->recv_hdr.request_hdr.p_cont_id;
+
+ if ((mbind = mlrpc_find_binding(mxa, p_cont_id)) == NULL)
+ return (MLRPC_DRC_FAULT_REQUEST_PCONT_INVALID);
+
+ mxa->binding = mbind;
+ msvc = mbind->service;
+
+ /*
+ * Make room for the response hdr.
+ */
+ mxa->send_mlnds.pdu_scan_offset = MLRPC_RSP_HDR_SIZE;
+
+ if (msvc->call_stub)
+ rc = (*msvc->call_stub)(mxa);
+ else
+ rc = mlrpc_generic_call_stub(mxa);
+
+ if (MLRPC_DRC_IS_FAULT(rc)) {
+ mlndo_printf(0, 0, "%s[0x%02x]: 0x%04x",
+ msvc->name, mxa->opnum, rc);
+ }
+
+ return (rc);
+}
+
+/*
+ * The transaction and the two mlnds streams use the same heap, which
+ * should already exist at this point. The heap will also be available
+ * to the stub.
+ */
+int
+mlrpc_generic_call_stub(struct mlrpc_xaction *mxa)
+{
+ struct mlrpc_binding *mbind = mxa->binding;
+ struct mlrpc_service *msvc = mbind->service;
+ struct ndr_typeinfo *intf_ti = msvc->interface_ti;
+ struct mlrpc_stub_table *ste;
+ int opnum = mxa->opnum;
+ unsigned p_len = intf_ti->c_size_fixed_part;
+ char *param;
+ int rc;
+
+ if (mxa->heap == NULL) {
+ mlndo_printf(0, 0, "%s[0x%02x]: no heap", msvc->name, opnum);
+ return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
+ }
+
+ if ((ste = mlrpc_find_stub_in_svc(msvc, opnum)) == NULL) {
+ mlndo_printf(0, 0, "%s[0x%02x]: invalid opnum",
+ msvc->name, opnum);
+ return (MLRPC_DRC_FAULT_REQUEST_OPNUM_INVALID);
+ }
+
+ if ((param = mlrpc_heap_malloc(mxa->heap, p_len)) == NULL)
+ return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
+
+ bzero(param, p_len);
+
+ rc = mlrpc_decode_call(mxa, param);
+ if (!MLRPC_DRC_IS_OK(rc))
+ return (rc);
+
+ rc = (*ste->func)(param, mxa);
+ if (rc == MLRPC_DRC_OK)
+ rc = mlrpc_encode_return(mxa, param);
+
+ return (rc);
+}
+
+/*
+ * We can perform some initial setup of the response header here.
+ * We also need to cache some of the information from the bind
+ * negotiation for use during subsequent RPC calls.
+ */
+static void
+mlrpc_reply_prepare_hdr(struct mlrpc_xaction *mxa)
+{
+ mlrpcconn_common_header_t *rhdr = &mxa->recv_hdr.common_hdr;
+ mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr;
+
+ hdr->rpc_vers = 5;
+ hdr->rpc_vers_minor = 0;
+ hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG + MLRPC_PFC_LAST_FRAG;
+ hdr->packed_drep = rhdr->packed_drep;
+ hdr->frag_length = 0;
+ hdr->auth_length = 0;
+ hdr->call_id = rhdr->call_id;
+#ifdef _BIG_ENDIAN
+ hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII
+ | MLRPC_REPLAB_INTG_BIG_ENDIAN;
+#else
+ hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII
+ | MLRPC_REPLAB_INTG_LITTLE_ENDIAN;
+#endif
+
+ switch (mxa->ptype) {
+ case MLRPC_PTYPE_BIND:
+ hdr->ptype = MLRPC_PTYPE_BIND_ACK;
+ mxa->send_hdr.bind_ack_hdr.max_xmit_frag =
+ mxa->recv_hdr.bind_hdr.max_xmit_frag;
+ mxa->send_hdr.bind_ack_hdr.max_recv_frag =
+ mxa->recv_hdr.bind_hdr.max_recv_frag;
+ mxa->send_hdr.bind_ack_hdr.assoc_group_id =
+ mxa->recv_hdr.bind_hdr.assoc_group_id;
+
+ if (mxa->send_hdr.bind_ack_hdr.assoc_group_id == 0)
+ mxa->send_hdr.bind_ack_hdr.assoc_group_id = time(0);
+
+ /*
+ * Save the maximum fragment sizes
+ * for use with subsequent requests.
+ */
+ mxa->context->max_xmit_frag =
+ mxa->recv_hdr.bind_hdr.max_xmit_frag;
+
+ mxa->context->max_recv_frag =
+ mxa->recv_hdr.bind_hdr.max_recv_frag;
+
+ break;
+
+ case MLRPC_PTYPE_REQUEST:
+ hdr->ptype = MLRPC_PTYPE_RESPONSE;
+ /* mxa->send_hdr.response_hdr.alloc_hint */
+ mxa->send_hdr.response_hdr.p_cont_id =
+ mxa->recv_hdr.request_hdr.p_cont_id;
+ mxa->send_hdr.response_hdr.cancel_count = 0;
+ mxa->send_hdr.response_hdr.reserved = 0;
+ break;
+
+ case MLRPC_PTYPE_ALTER_CONTEXT:
+ hdr->ptype = MLRPC_PTYPE_ALTER_CONTEXT_RESP;
+ /*
+ * The max_xmit_frag, max_recv_frag
+ * and assoc_group_id are ignored.
+ */
+ break;
+
+ default:
+ hdr->ptype = 0xFF;
+ }
+}
+
+/*
+ * Finish and encode the bind acknowledge (MLRPC_PTYPE_BIND_ACK) header.
+ * The frag_length is different from a regular RPC response.
+ */
+static void
+mlrpc_reply_bind_ack(struct mlrpc_xaction *mxa)
+{
+ mlrpcconn_common_header_t *hdr;
+ mlrpcconn_bind_ack_hdr_t *bahdr;
+
+ hdr = &mxa->send_hdr.common_hdr;
+ bahdr = &mxa->send_hdr.bind_ack_hdr;
+ hdr->frag_length = mlrpc_bind_ack_hdr_size(bahdr);
+}
+
+/*
+ * Signal an RPC fault. The stream is reset and we overwrite whatever
+ * was in the response header with the fault information.
+ */
+static void
+mlrpc_reply_fault(struct mlrpc_xaction *mxa, unsigned long drc)
+{
+ mlrpcconn_common_header_t *rhdr = &mxa->recv_hdr.common_hdr;
+ mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr;
+ struct mlndr_stream *mlnds = &mxa->send_mlnds;
+ unsigned long fault_status;
+
+ MLNDS_RESET(mlnds);
+
+ hdr->rpc_vers = 5;
+ hdr->rpc_vers_minor = 0;
+ hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG + MLRPC_PFC_LAST_FRAG;
+ hdr->packed_drep = rhdr->packed_drep;
+ hdr->frag_length = sizeof (mxa->send_hdr.fault_hdr);
+ hdr->auth_length = 0;
+ hdr->call_id = rhdr->call_id;
+#ifdef _BIG_ENDIAN
+ hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII
+ | MLRPC_REPLAB_INTG_BIG_ENDIAN;
+#else
+ hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII
+ | MLRPC_REPLAB_INTG_LITTLE_ENDIAN;
+#endif
+
+ switch (drc & MLRPC_DRC_MASK_SPECIFIER) {
+ case MLRPC_DRC_FAULT_OUT_OF_MEMORY:
+ case MLRPC_DRC_FAULT_ENCODE_TOO_BIG:
+ fault_status = MLRPC_FAULT_NCA_OUT_ARGS_TOO_BIG;
+ break;
+
+ case MLRPC_DRC_FAULT_REQUEST_PCONT_INVALID:
+ fault_status = MLRPC_FAULT_NCA_INVALID_PRES_CONTEXT_ID;
+ break;
+
+ case MLRPC_DRC_FAULT_REQUEST_OPNUM_INVALID:
+ fault_status = MLRPC_FAULT_NCA_OP_RNG_ERROR;
+ break;
+
+ case MLRPC_DRC_FAULT_DECODE_FAILED:
+ case MLRPC_DRC_FAULT_ENCODE_FAILED:
+ fault_status = MLRPC_FAULT_NCA_PROTO_ERROR;
+ break;
+
+ default:
+ fault_status = MLRPC_FAULT_NCA_UNSPEC_REJECT;
+ break;
+ }
+
+ mxa->send_hdr.fault_hdr.common_hdr.ptype = MLRPC_PTYPE_FAULT;
+ mxa->send_hdr.fault_hdr.status = fault_status;
+ mxa->send_hdr.response_hdr.alloc_hint = hdr->frag_length;
+}
+
+static int
+mlrpc_build_reply(struct mlrpc_xaction *mxa)
+{
+ mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr;
+ struct mlndr_stream *mlnds = &mxa->send_mlnds;
+ unsigned long pdu_size;
+ unsigned long frag_size;
+ unsigned long pdu_data_size;
+ unsigned long frag_data_size;
+ uint32_t rem_dlen;
+ uint32_t save_rem_dlen;
+ uint32_t bytesoff;
+ uint32_t cnt;
+ uint32_t obytes;
+ uint32_t num_ext_frags;
+ uint16_t last_frag = 0;
+ uchar_t *frag_startp;
+ mlrpcconn_common_header_t *rpc_hdr;
+
+ hdr = &mxa->send_hdr.common_hdr;
+
+ frag_size = mlrpc_frag_size;
+ pdu_size = mlnds->pdu_size;
+
+ if (pdu_size <= frag_size) {
+ /*
+ * Single fragment response. The PDU size may be zero
+ * here (i.e. bind or fault response). So don't make
+ * any assumptions about it until after the header is
+ * encoded.
+ */
+ switch (hdr->ptype) {
+ case MLRPC_PTYPE_BIND_ACK:
+ mlrpc_reply_bind_ack(mxa);
+ break;
+
+ case MLRPC_PTYPE_FAULT:
+ /* already setup */
+ break;
+
+ case MLRPC_PTYPE_RESPONSE:
+ hdr->frag_length = pdu_size;
+ mxa->send_hdr.response_hdr.alloc_hint =
+ hdr->frag_length;
+ break;
+
+ default:
+ hdr->frag_length = pdu_size;
+ break;
+ }
+
+ mlnds->pdu_scan_offset = 0;
+ (void) mlrpc_encode_pdu_hdr(mxa);
+
+ mlnds->pdu_size_with_rpc_hdrs = mlnds->pdu_size;
+ mlnds->pdu_base_addr_with_rpc_hdrs = 0;
+ return (0);
+ }
+
+ /*
+ * Multiple fragment response.
+ */
+ hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG;
+ hdr->frag_length = frag_size;
+ mxa->send_hdr.response_hdr.alloc_hint = pdu_size - MLRPC_RSP_HDR_SIZE;
+ mlnds->pdu_scan_offset = 0;
+
+ (void) mlrpc_encode_pdu_hdr(mxa);
+
+ /*
+ * We need to update the 24-byte header in subsequent fragments.
+ *
+ * pdu_data_size: total data remaining to be handled
+ * frag_size: total fragment size including header
+ * frag_data_size: data in fragment
+ * (i.e. frag_size - MLRPC_RSP_HDR_SIZE)
+ */
+ pdu_data_size = pdu_size - MLRPC_RSP_HDR_SIZE;
+ frag_data_size = frag_size - MLRPC_RSP_HDR_SIZE;
+
+ num_ext_frags = pdu_data_size / frag_data_size;
+
+ /*
+ * We may need to stretch the pipe and insert an RPC header
+ * at each frag boundary. The response will get chunked into
+ * xdrlen sizes for each trans request.
+ */
+ mlnds->pdu_base_addr_with_rpc_hdrs
+ = malloc(pdu_size + (num_ext_frags * MLRPC_RSP_HDR_SIZE));
+ mlnds->pdu_size_with_rpc_hdrs =
+ mlnds->pdu_size + (num_ext_frags * MLRPC_RSP_HDR_SIZE);
+
+ /*
+ * Start stretching loop.
+ */
+ bcopy(mlnds->pdu_base_addr,
+ mlnds->pdu_base_addr_with_rpc_hdrs, frag_size);
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ rpc_hdr = (mlrpcconn_common_header_t *)
+ mlnds->pdu_base_addr_with_rpc_hdrs;
+ rpc_hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG;
+ rem_dlen = pdu_data_size - frag_size;
+ bytesoff = frag_size;
+ cnt = 1;
+ while (num_ext_frags--) {
+ /* first copy the RPC header to the front of the frag */
+ bcopy(mlnds->pdu_base_addr, mlnds->pdu_base_addr_with_rpc_hdrs +
+ (cnt * frag_size), MLRPC_RSP_HDR_SIZE);
+
+ /* then copy the data portion of the frag */
+ save_rem_dlen = rem_dlen;
+ if (rem_dlen >= (frag_size - MLRPC_RSP_HDR_SIZE)) {
+ rem_dlen = rem_dlen - frag_size + MLRPC_RSP_HDR_SIZE;
+ obytes = frag_size - MLRPC_RSP_HDR_SIZE;
+ } else {
+ last_frag = 1; /* this is the last one */
+ obytes = rem_dlen;
+ }
+
+ frag_startp = mlnds->pdu_base_addr_with_rpc_hdrs +
+ (cnt * frag_size);
+ bcopy(mlnds->pdu_base_addr + bytesoff,
+ frag_startp + MLRPC_RSP_HDR_SIZE, obytes);
+
+ /* set the FRAG FLAGS in the frag header spot */
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ rpc_hdr = (mlrpcconn_common_header_t *)frag_startp;
+ if (last_frag) {
+ rpc_hdr->frag_length = save_rem_dlen;
+ rpc_hdr->pfc_flags = MLRPC_PFC_LAST_FRAG;
+ } else {
+ rpc_hdr->pfc_flags = 0;
+ }
+
+ bytesoff += (frag_size - MLRPC_RSP_HDR_SIZE);
+ cnt++;
+ }
+
+ return (0);
+}