#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>
#ifdef APPLE
#include <compat/synch.h>
#include <core/rqzone.h>
#include <core/logging.h>
#endif
#define MLRPC_FRAG_SZ 5680
static unsigned long mlrpc_frag_size = MLRPC_FRAG_SZ;
#define CTXT_TABLE_ENTRIES 128
static struct mlsvc_rpc_context context_table[CTXT_TABLE_ENTRIES];
static mutex_t mlrpc_context_lock = DEFAULTMUTEX;
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 *);
int
mlsvc_rpc_process(void *zone,
smb_pipe_t *inpipe,
smb_pipe_t *outpipe,
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;
int datalen;
if (inpipe == NULL || user_ctx == NULL)
return (-1);
context = mlsvc_lookup_context(inpipe->sp_pipeid);
if (context == NULL)
return (-1);
context->user_ctx = user_ctx;
mxa = (struct mlrpc_xaction *)malloc(sizeof (struct mlrpc_xaction));
if (mxa == NULL)
return (-1);
bzero(mxa, sizeof (struct mlrpc_xaction));
mxa->context = context;
mxa->binding_list = context->binding;
if ((mxa->heap = mlrpc_heap_create()) == NULL) {
free(mxa);
return (-1);
}
recv_mlnds = &mxa->recv_mlnds;
mlnds_initialize(recv_mlnds, inpipe->sp_data.iov_len,
NDR_MODE_CALL_RECV, mxa->heap);
memcpy(recv_mlnds->pdu_base_addr, inpipe->sp_data.iov_base,
inpipe->sp_data.iov_len);
send_mlnds = &mxa->send_mlnds;
mlnds_initialize(send_mlnds, 0,
NDR_MODE_RETURN_SEND, mxa->heap);
mlrpc_s_process(mxa);
datalen = send_mlnds->pdu_size_with_rpc_hdrs;
ASSERT(outpipe->sp_data.iov_base == NULL);
ASSERT(outpipe->sp_data.iov_len == 0);
outpipe->sp_data.iov_base = ZONE_ZALLOC_BYTES(zone, datalen);
outpipe->sp_data.iov_len = datalen;
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;
bcopy((char *)pdu_base_addr, outpipe->sp_data.iov_base, datalen);
mlnds_destruct(&mxa->recv_mlnds);
mlnds_destruct(&mxa->send_mlnds);
mlrpc_heap_destroy(mxa->heap);
free(mxa);
return (datalen);
}
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(SMB_CTXT_PIPE_SZ);
available->outpipe = malloc(SMB_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);
}
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);
}
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);
}
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;
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];
send_mlnds = &mxa->send_mlnds;
sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
sec_addr->length = 13;
(void) strlcpy((char *)sec_addr->port_spec, "\\PIPE\\ntsvcs", sizeof(sec_addr->port_spec));
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));
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) {
mlndo_trace("mlrpc_s_bind: duplicate binding");
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;
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, sizeof(as_buf));
mlrpc_uuid_to_str(ts_uuid, ts_buf, sizeof(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);
}
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->service = msvc;
mbind->instance_specific = 0;
mxa->binding = mbind;
if (msvc->bind_req) {
rc = (msvc->bind_req)(mxa);
if (MLRPC_DRC_IS_FAULT(rc)) {
mbind->service = 0;
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];
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);
}
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->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;
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);
}
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);
}
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);
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.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;
break;
default:
hdr->ptype = 0xFF;
}
}
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);
}
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) {
switch (hdr->ptype) {
case MLRPC_PTYPE_BIND_ACK:
mlrpc_reply_bind_ack(mxa);
break;
case MLRPC_PTYPE_FAULT:
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);
}
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);
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;
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);
bcopy(mlnds->pdu_base_addr,
mlnds->pdu_base_addr_with_rpc_hdrs, frag_size);
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--) {
bcopy(mlnds->pdu_base_addr, mlnds->pdu_base_addr_with_rpc_hdrs +
(cnt * frag_size), MLRPC_RSP_HDR_SIZE);
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;
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);
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);
}