1da6c28aaSamw /*
2da6c28aaSamw  * CDDL HEADER START
3da6c28aaSamw  *
4da6c28aaSamw  * The contents of this file are subject to the terms of the
5da6c28aaSamw  * Common Development and Distribution License (the "License").
6da6c28aaSamw  * You may not use this file except in compliance with the License.
7da6c28aaSamw  *
8da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw  * See the License for the specific language governing permissions
11da6c28aaSamw  * and limitations under the License.
12da6c28aaSamw  *
13da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw  *
19da6c28aaSamw  * CDDL HEADER END
20da6c28aaSamw  */
21da6c28aaSamw /*
22cb174861Sjoyce mcintosh  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
2368b2bbf2SGordon Ross  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
24*4aab0a3bSJohn Levon  * Copyright (c) 2018, Joyent, Inc.
25da6c28aaSamw  */
26da6c28aaSamw 
27da6c28aaSamw /*
28da6c28aaSamw  * Server side RPC handler.
29da6c28aaSamw  */
30da6c28aaSamw 
3155bf511dSas #include <sys/byteorder.h>
323db3f65cSamw #include <sys/uio.h>
3368b2bbf2SGordon Ross #include <errno.h>
34da6c28aaSamw #include <synch.h>
35da6c28aaSamw #include <stdlib.h>
36da6c28aaSamw #include <strings.h>
37da6c28aaSamw #include <string.h>
3868b2bbf2SGordon Ross #include <thread.h>
39da6c28aaSamw 
403299f39fSGordon Ross #include <libmlrpc.h>
41da6c28aaSamw 
4268b2bbf2SGordon Ross #define	NDR_PIPE_SEND(np, buf, len) \
4368b2bbf2SGordon Ross 	((np)->np_send)((np), (buf), (len))
4468b2bbf2SGordon Ross #define	NDR_PIPE_RECV(np, buf, len) \
4568b2bbf2SGordon Ross 	((np)->np_recv)((np), (buf), (len))
468d7e4166Sjose borrego 
478d7e4166Sjose borrego static int ndr_svc_process(ndr_xa_t *);
488d7e4166Sjose borrego static int ndr_svc_bind(ndr_xa_t *);
498d7e4166Sjose borrego static int ndr_svc_request(ndr_xa_t *);
508d7e4166Sjose borrego static void ndr_reply_prepare_hdr(ndr_xa_t *);
518d7e4166Sjose borrego static int ndr_svc_alter_context(ndr_xa_t *);
528d7e4166Sjose borrego static void ndr_reply_fault(ndr_xa_t *, unsigned long);
5368b2bbf2SGordon Ross 
5468b2bbf2SGordon Ross static int ndr_recv_request(ndr_xa_t *mxa);
5568b2bbf2SGordon Ross static int ndr_recv_frag(ndr_xa_t *mxa);
5668b2bbf2SGordon Ross static int ndr_send_reply(ndr_xa_t *);
5768b2bbf2SGordon Ross 
5868b2bbf2SGordon Ross static int ndr_pipe_process(ndr_pipe_t *, ndr_xa_t *);
59da6c28aaSamw 
60da6c28aaSamw /*
6168b2bbf2SGordon Ross  * External entry point called by smbd.
62da6c28aaSamw  */
6368b2bbf2SGordon Ross void
ndr_pipe_worker(ndr_pipe_t * np)6468b2bbf2SGordon Ross ndr_pipe_worker(ndr_pipe_t *np)
653db3f65cSamw {
6668b2bbf2SGordon Ross 	ndr_xa_t	*mxa;
6768b2bbf2SGordon Ross 	int rc;
683db3f65cSamw 
698d7e4166Sjose borrego 	ndr_svc_binding_pool_init(&np->np_binding, np->np_binding_pool,
708d7e4166Sjose borrego 	    NDR_N_BINDING_POOL);
713db3f65cSamw 
7268b2bbf2SGordon Ross 	if ((mxa = malloc(sizeof (*mxa))) == NULL)
7368b2bbf2SGordon Ross 		return;
743db3f65cSamw 
7568b2bbf2SGordon Ross 	do {
7668b2bbf2SGordon Ross 		bzero(mxa, sizeof (*mxa));
7768b2bbf2SGordon Ross 		rc = ndr_pipe_process(np, mxa);
7868b2bbf2SGordon Ross 	} while (rc == 0);
793db3f65cSamw 
8068b2bbf2SGordon Ross 	free(mxa);
813db3f65cSamw 
823db3f65cSamw 	/*
8368b2bbf2SGordon Ross 	 * Ensure that there are no RPC service policy handles
8468b2bbf2SGordon Ross 	 * (associated with this fid) left around.
853db3f65cSamw 	 */
8668b2bbf2SGordon Ross 	ndr_hdclose(np);
879fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States }
889fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 
893db3f65cSamw /*
9068b2bbf2SGordon Ross  * Process one server-side RPC request.
913db3f65cSamw  */
923db3f65cSamw static int
ndr_pipe_process(ndr_pipe_t * np,ndr_xa_t * mxa)9368b2bbf2SGordon Ross ndr_pipe_process(ndr_pipe_t *np, ndr_xa_t *mxa)
943db3f65cSamw {
958d7e4166Sjose borrego 	ndr_stream_t	*recv_nds;
968d7e4166Sjose borrego 	ndr_stream_t	*send_nds;
9768b2bbf2SGordon Ross 	int		rc = ENOMEM;
98da6c28aaSamw 
998d7e4166Sjose borrego 	mxa->pipe = np;
1008d7e4166Sjose borrego 	mxa->binding_list = np->np_binding;
101da6c28aaSamw 
10268b2bbf2SGordon Ross 	if ((mxa->heap = ndr_heap_create()) == NULL)
10368b2bbf2SGordon Ross 		goto out1;
104da6c28aaSamw 
1058d7e4166Sjose borrego 	recv_nds = &mxa->recv_nds;
10668b2bbf2SGordon Ross 	rc = nds_initialize(recv_nds, 0, NDR_MODE_CALL_RECV, mxa->heap);
10768b2bbf2SGordon Ross 	if (rc != 0)
10868b2bbf2SGordon Ross 		goto out2;
109da6c28aaSamw 
1108d7e4166Sjose borrego 	send_nds = &mxa->send_nds;
111fe1c642dSBill Krier 	rc = nds_initialize(send_nds, 0, NDR_MODE_RETURN_SEND, mxa->heap);
11268b2bbf2SGordon Ross 	if (rc != 0)
11368b2bbf2SGordon Ross 		goto out3;
11468b2bbf2SGordon Ross 
11568b2bbf2SGordon Ross 	rc = ndr_recv_request(mxa);
11668b2bbf2SGordon Ross 	if (rc != 0)
11768b2bbf2SGordon Ross 		goto out4;
118da6c28aaSamw 
1198d7e4166Sjose borrego 	(void) ndr_svc_process(mxa);
12068b2bbf2SGordon Ross 	(void) ndr_send_reply(mxa);
12168b2bbf2SGordon Ross 	rc = 0;
122da6c28aaSamw 
12368b2bbf2SGordon Ross out4:
1248d7e4166Sjose borrego 	nds_destruct(&mxa->send_nds);
12568b2bbf2SGordon Ross out3:
12668b2bbf2SGordon Ross 	nds_destruct(&mxa->recv_nds);
12768b2bbf2SGordon Ross out2:
1288d7e4166Sjose borrego 	ndr_heap_destroy(mxa->heap);
12968b2bbf2SGordon Ross out1:
13068b2bbf2SGordon Ross 	return (rc);
131da6c28aaSamw }
132da6c28aaSamw 
1339fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States /*
13468b2bbf2SGordon Ross  * Receive an entire RPC request (all fragments)
13568b2bbf2SGordon Ross  * Returns zero or an NDR fault code.
1369fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States  */
1379fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States static int
ndr_recv_request(ndr_xa_t * mxa)13868b2bbf2SGordon Ross ndr_recv_request(ndr_xa_t *mxa)
1399fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States {
14068b2bbf2SGordon Ross 	ndr_common_header_t	*hdr = &mxa->recv_hdr.common_hdr;
14168b2bbf2SGordon Ross 	ndr_stream_t		*nds = &mxa->recv_nds;
14268b2bbf2SGordon Ross 	unsigned long		saved_size;
14368b2bbf2SGordon Ross 	int			rc;
1449fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 
14568b2bbf2SGordon Ross 	rc = ndr_recv_frag(mxa);
14668b2bbf2SGordon Ross 	if (rc != 0)
14768b2bbf2SGordon Ross 		return (rc);
14868b2bbf2SGordon Ross 	if (!NDR_IS_FIRST_FRAG(hdr->pfc_flags))
14968b2bbf2SGordon Ross 		return (NDR_DRC_FAULT_DECODE_FAILED);
1509fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 
15168b2bbf2SGordon Ross 	while (!NDR_IS_LAST_FRAG(hdr->pfc_flags)) {
15268b2bbf2SGordon Ross 		rc = ndr_recv_frag(mxa);
15368b2bbf2SGordon Ross 		if (rc != 0)
15468b2bbf2SGordon Ross 			return (rc);
1553db3f65cSamw 	}
15668b2bbf2SGordon Ross 	nds->pdu_scan_offset = 0;
157da6c28aaSamw 
15868b2bbf2SGordon Ross 	/*
15968b2bbf2SGordon Ross 	 * This whacks nds->pdu_size, so save/restore.
16068b2bbf2SGordon Ross 	 * It leaves scan_offset after the header.
16168b2bbf2SGordon Ross 	 */
16268b2bbf2SGordon Ross 	saved_size = nds->pdu_size;
16368b2bbf2SGordon Ross 	rc = ndr_decode_pdu_hdr(mxa);
16468b2bbf2SGordon Ross 	nds->pdu_size = saved_size;
165da6c28aaSamw 
16668b2bbf2SGordon Ross 	return (rc);
1673db3f65cSamw }
1683db3f65cSamw 
1693db3f65cSamw /*
17068b2bbf2SGordon Ross  * Read one fragment, leaving the decoded frag header in
17168b2bbf2SGordon Ross  * recv_hdr.common_hdr, and the data in the recv_nds.
17268b2bbf2SGordon Ross  *
17368b2bbf2SGordon Ross  * Returns zero or an NDR fault code.
17468b2bbf2SGordon Ross  *
17568b2bbf2SGordon Ross  * If a first frag, the header is included in the data
17668b2bbf2SGordon Ross  * placed in recv_nds (because it's not fully decoded
17768b2bbf2SGordon Ross  * until later - we only decode the common part here).
17868b2bbf2SGordon Ross  * Additional frags are placed in the recv_nds without
17968b2bbf2SGordon Ross  * the header, so that after the first frag header,
18068b2bbf2SGordon Ross  * the remaining data will be contiguous.  We do this
18168b2bbf2SGordon Ross  * by simply not advancing the offset in recv_nds after
18268b2bbf2SGordon Ross  * reading and decoding these additional fragments, so
18368b2bbf2SGordon Ross  * the payload of such frags will overwrite what was
18468b2bbf2SGordon Ross  * (temporarily) the frag header.
1853db3f65cSamw  */
18668b2bbf2SGordon Ross static int
ndr_recv_frag(ndr_xa_t * mxa)18768b2bbf2SGordon Ross ndr_recv_frag(ndr_xa_t *mxa)
1883db3f65cSamw {
18968b2bbf2SGordon Ross 	ndr_common_header_t	*hdr = &mxa->recv_hdr.common_hdr;
19068b2bbf2SGordon Ross 	ndr_stream_t		*nds = &mxa->recv_nds;
19168b2bbf2SGordon Ross 	unsigned char		*data;
19268b2bbf2SGordon Ross 	unsigned long		next_offset;
19368b2bbf2SGordon Ross 	unsigned long		pay_size;
19468b2bbf2SGordon Ross 	int			rc;
1953db3f65cSamw 
19668b2bbf2SGordon Ross 	/* Make room for the frag header. */
19768b2bbf2SGordon Ross 	next_offset = nds->pdu_scan_offset + NDR_RSP_HDR_SIZE;
19868b2bbf2SGordon Ross 	if (!NDS_GROW_PDU(nds, next_offset, 0))
19968b2bbf2SGordon Ross 		return (NDR_DRC_FAULT_OUT_OF_MEMORY);
2003db3f65cSamw 
20168b2bbf2SGordon Ross 	/* Read the frag header. */
20268b2bbf2SGordon Ross 	data = nds->pdu_base_addr + nds->pdu_scan_offset;
20368b2bbf2SGordon Ross 	rc = NDR_PIPE_RECV(mxa->pipe, data, NDR_RSP_HDR_SIZE);
20468b2bbf2SGordon Ross 	if (rc != 0)
20568b2bbf2SGordon Ross 		return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT);
2063db3f65cSamw 
20768b2bbf2SGordon Ross 	/*
20868b2bbf2SGordon Ross 	 * Decode the frag header, get the length.
20968b2bbf2SGordon Ross 	 * NB: It uses nds->pdu_scan_offset
21068b2bbf2SGordon Ross 	 */
21168b2bbf2SGordon Ross 	ndr_decode_frag_hdr(nds, hdr);
21268b2bbf2SGordon Ross 	ndr_show_hdr(hdr);
21368b2bbf2SGordon Ross 	if (hdr->frag_length < NDR_RSP_HDR_SIZE ||
21468b2bbf2SGordon Ross 	    hdr->frag_length > mxa->pipe->np_max_xmit_frag)
21568b2bbf2SGordon Ross 		return (NDR_DRC_FAULT_DECODE_FAILED);
21668b2bbf2SGordon Ross 
21768b2bbf2SGordon Ross 	if (nds->pdu_scan_offset == 0) {
21868b2bbf2SGordon Ross 		/* First frag: header stays in the data. */
21968b2bbf2SGordon Ross 		nds->pdu_scan_offset = next_offset;
22068b2bbf2SGordon Ross 	} /* else overwrite with the payload */
22168b2bbf2SGordon Ross 
22268b2bbf2SGordon Ross 	/* Make room for the payload. */
22368b2bbf2SGordon Ross 	pay_size = hdr->frag_length - NDR_RSP_HDR_SIZE;
22468b2bbf2SGordon Ross 	next_offset = nds->pdu_scan_offset + pay_size;
22568b2bbf2SGordon Ross 	if (!NDS_GROW_PDU(nds, next_offset, 0))
22668b2bbf2SGordon Ross 		return (NDR_DRC_FAULT_OUT_OF_MEMORY);
2273db3f65cSamw 
22868b2bbf2SGordon Ross 	/* Read the payload. */
22968b2bbf2SGordon Ross 	data = nds->pdu_base_addr + nds->pdu_scan_offset;
23068b2bbf2SGordon Ross 	rc = NDR_PIPE_RECV(mxa->pipe, data, pay_size);
23168b2bbf2SGordon Ross 	if (rc != 0)
23268b2bbf2SGordon Ross 		return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT);
23368b2bbf2SGordon Ross 	nds->pdu_scan_offset = next_offset;
2343db3f65cSamw 
23568b2bbf2SGordon Ross 	return (NDR_DRC_OK);
236da6c28aaSamw }
237da6c28aaSamw 
238da6c28aaSamw /*
239da6c28aaSamw  * This is the entry point for all server-side RPC processing.
240da6c28aaSamw  * It is assumed that the PDU has already been received.
241da6c28aaSamw  */
242da6c28aaSamw static int
ndr_svc_process(ndr_xa_t * mxa)2438d7e4166Sjose borrego ndr_svc_process(ndr_xa_t *mxa)
244da6c28aaSamw {
2459fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 	int			rc;
246da6c28aaSamw 
2478d7e4166Sjose borrego 	(void) ndr_reply_prepare_hdr(mxa);
248da6c28aaSamw 
249da6c28aaSamw 	switch (mxa->ptype) {
2508d7e4166Sjose borrego 	case NDR_PTYPE_BIND:
2518d7e4166Sjose borrego 		rc = ndr_svc_bind(mxa);
252da6c28aaSamw 		break;
253da6c28aaSamw 
2548d7e4166Sjose borrego 	case NDR_PTYPE_REQUEST:
2558d7e4166Sjose borrego 		rc = ndr_svc_request(mxa);
256da6c28aaSamw 		break;
257da6c28aaSamw 
2588d7e4166Sjose borrego 	case NDR_PTYPE_ALTER_CONTEXT:
2598d7e4166Sjose borrego 		rc = ndr_svc_alter_context(mxa);
260da6c28aaSamw 		break;
261da6c28aaSamw 
262da6c28aaSamw 	default:
2638d7e4166Sjose borrego 		rc = NDR_DRC_FAULT_RPCHDR_PTYPE_INVALID;
264da6c28aaSamw 		break;
265da6c28aaSamw 	}
266da6c28aaSamw 
2678d7e4166Sjose borrego 	if (NDR_DRC_IS_FAULT(rc))
2688d7e4166Sjose borrego 		ndr_reply_fault(mxa, rc);
269da6c28aaSamw 
270da6c28aaSamw 	return (rc);
271da6c28aaSamw }
272da6c28aaSamw 
273da6c28aaSamw /*
274da6c28aaSamw  * Multiple p_cont_elem[]s, multiple transfer_syntaxes[] and multiple
275da6c28aaSamw  * p_results[] not supported.
276da6c28aaSamw  */
277da6c28aaSamw static int
ndr_svc_bind(ndr_xa_t * mxa)2788d7e4166Sjose borrego ndr_svc_bind(ndr_xa_t *mxa)
279da6c28aaSamw {
2808d7e4166Sjose borrego 	ndr_p_cont_list_t	*cont_list;
2818d7e4166Sjose borrego 	ndr_p_result_list_t	*result_list;
2828d7e4166Sjose borrego 	ndr_p_result_t		*result;
283da6c28aaSamw 	unsigned		p_cont_id;
2848d7e4166Sjose borrego 	ndr_binding_t		*mbind;
285dc20a302Sas 	ndr_uuid_t		*as_uuid;
286dc20a302Sas 	ndr_uuid_t		*ts_uuid;
287da6c28aaSamw 	int			as_vers;
288da6c28aaSamw 	int			ts_vers;
2898d7e4166Sjose borrego 	ndr_service_t		*msvc;
290da6c28aaSamw 	int			rc;
2918d7e4166Sjose borrego 	ndr_port_any_t		*sec_addr;
292da6c28aaSamw 
293da6c28aaSamw 	/* acquire targets */
294da6c28aaSamw 	cont_list = &mxa->recv_hdr.bind_hdr.p_context_elem;
295da6c28aaSamw 	result_list = &mxa->send_hdr.bind_ack_hdr.p_result_list;
296da6c28aaSamw 	result = &result_list->p_results[0];
297da6c28aaSamw 
298da6c28aaSamw 	/*
299da6c28aaSamw 	 * Set up temporary secondary address port.
300da6c28aaSamw 	 * We will correct this later (below).
301da6c28aaSamw 	 */
302da6c28aaSamw 	sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
303da6c28aaSamw 	sec_addr->length = 13;
304da6c28aaSamw 	(void) strcpy((char *)sec_addr->port_spec, "\\PIPE\\ntsvcs");
305da6c28aaSamw 
306da6c28aaSamw 	result_list->n_results = 1;
307da6c28aaSamw 	result_list->reserved = 0;
308da6c28aaSamw 	result_list->reserved2 = 0;
3098d7e4166Sjose borrego 	result->result = NDR_PCDR_ACCEPTANCE;
310da6c28aaSamw 	result->reason = 0;
311da6c28aaSamw 	bzero(&result->transfer_syntax, sizeof (result->transfer_syntax));
312da6c28aaSamw 
313da6c28aaSamw 	/* sanity check */
314da6c28aaSamw 	if (cont_list->n_context_elem != 1 ||
315da6c28aaSamw 	    cont_list->p_cont_elem[0].n_transfer_syn != 1) {
3168d7e4166Sjose borrego 		ndo_trace("ndr_svc_bind: warning: multiple p_cont_elem");
317da6c28aaSamw 	}
318da6c28aaSamw 
319da6c28aaSamw 	p_cont_id = cont_list->p_cont_elem[0].p_cont_id;
320da6c28aaSamw 
3218d7e4166Sjose borrego 	if ((mbind = ndr_svc_find_binding(mxa, p_cont_id)) != NULL) {
322da6c28aaSamw 		/*
3238d7e4166Sjose borrego 		 * Duplicate presentation context id.
324da6c28aaSamw 		 */
3258d7e4166Sjose borrego 		ndo_trace("ndr_svc_bind: duplicate binding");
3268d7e4166Sjose borrego 		return (NDR_DRC_FAULT_BIND_PCONT_BUSY);
327da6c28aaSamw 	}
328da6c28aaSamw 
3298d7e4166Sjose borrego 	if ((mbind = ndr_svc_new_binding(mxa)) == NULL) {
330da6c28aaSamw 		/*
331da6c28aaSamw 		 * No free binding slot
332da6c28aaSamw 		 */
3338d7e4166Sjose borrego 		result->result = NDR_PCDR_PROVIDER_REJECTION;
3348d7e4166Sjose borrego 		result->reason = NDR_PPR_LOCAL_LIMIT_EXCEEDED;
3358d7e4166Sjose borrego 		ndo_trace("ndr_svc_bind: no resources");
3368d7e4166Sjose borrego 		return (NDR_DRC_OK);
337da6c28aaSamw 	}
338da6c28aaSamw 
339da6c28aaSamw 	as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid;
340da6c28aaSamw 	as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version;
341da6c28aaSamw 
342da6c28aaSamw 	ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid;
343da6c28aaSamw 	ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version;
344da6c28aaSamw 
3458d7e4166Sjose borrego 	msvc = ndr_svc_lookup_uuid(as_uuid, as_vers, ts_uuid, ts_vers);
3468d7e4166Sjose borrego 	if (msvc == NULL) {
3478d7e4166Sjose borrego 		result->result = NDR_PCDR_PROVIDER_REJECTION;
3488d7e4166Sjose borrego 		result->reason = NDR_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
3498d7e4166Sjose borrego 		return (NDR_DRC_OK);
350da6c28aaSamw 	}
351da6c28aaSamw 
352da6c28aaSamw 	/*
353da6c28aaSamw 	 * We can now use the correct secondary address port.
354da6c28aaSamw 	 */
355da6c28aaSamw 	sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
356da6c28aaSamw 	sec_addr->length = strlen(msvc->sec_addr_port) + 1;
357da6c28aaSamw 	(void) strlcpy((char *)sec_addr->port_spec, msvc->sec_addr_port,
3588d7e4166Sjose borrego 	    NDR_PORT_ANY_MAX_PORT_SPEC);
359da6c28aaSamw 
360da6c28aaSamw 	mbind->p_cont_id = p_cont_id;
3618d7e4166Sjose borrego 	mbind->which_side = NDR_BIND_SIDE_SERVER;
362da6c28aaSamw 	/* mbind->context set by app */
363da6c28aaSamw 	mbind->service = msvc;
364da6c28aaSamw 	mbind->instance_specific = 0;
365da6c28aaSamw 
366da6c28aaSamw 	mxa->binding = mbind;
367da6c28aaSamw 
368da6c28aaSamw 	if (msvc->bind_req) {
369da6c28aaSamw 		/*
370da6c28aaSamw 		 * Call the service-specific bind() handler.  If
371da6c28aaSamw 		 * this fails, we shouild send a specific error
372da6c28aaSamw 		 * on the bind ack.
373da6c28aaSamw 		 */
374da6c28aaSamw 		rc = (msvc->bind_req)(mxa);
3758d7e4166Sjose borrego 		if (NDR_DRC_IS_FAULT(rc)) {
376da6c28aaSamw 			mbind->service = 0;	/* free binding slot */
377da6c28aaSamw 			mbind->which_side = 0;
378da6c28aaSamw 			mbind->p_cont_id = 0;
379da6c28aaSamw 			mbind->instance_specific = 0;
380da6c28aaSamw 			return (rc);
381da6c28aaSamw 		}
382da6c28aaSamw 	}
383da6c28aaSamw 
384da6c28aaSamw 	result->transfer_syntax =
385da6c28aaSamw 	    cont_list->p_cont_elem[0].transfer_syntaxes[0];
386da6c28aaSamw 
3878d7e4166Sjose borrego 	return (NDR_DRC_BINDING_MADE);
388da6c28aaSamw }
389da6c28aaSamw 
390da6c28aaSamw /*
3918d7e4166Sjose borrego  * ndr_svc_alter_context
392da6c28aaSamw  *
393da6c28aaSamw  * The alter context request is used to request additional presentation
3942c1b14e5Sjose borrego  * context for another interface and/or version.  It is very similar to
3952c1b14e5Sjose borrego  * a bind request.
396da6c28aaSamw  */
397da6c28aaSamw static int
ndr_svc_alter_context(ndr_xa_t * mxa)3988d7e4166Sjose borrego ndr_svc_alter_context(ndr_xa_t *mxa)
399da6c28aaSamw {
4008d7e4166Sjose borrego 	ndr_p_result_list_t *result_list;
4018d7e4166Sjose borrego 	ndr_p_result_t *result;
4028d7e4166Sjose borrego 	ndr_p_cont_list_t *cont_list;
4038d7e4166Sjose borrego 	ndr_binding_t *mbind;
4048d7e4166Sjose borrego 	ndr_service_t *msvc;
405da6c28aaSamw 	unsigned p_cont_id;
406dc20a302Sas 	ndr_uuid_t *as_uuid;
407dc20a302Sas 	ndr_uuid_t *ts_uuid;
408da6c28aaSamw 	int as_vers;
409da6c28aaSamw 	int ts_vers;
4108d7e4166Sjose borrego 	ndr_port_any_t *sec_addr;
411da6c28aaSamw 
4122c1b14e5Sjose borrego 	result_list = &mxa->send_hdr.alter_context_rsp_hdr.p_result_list;
413da6c28aaSamw 	result_list->n_results = 1;
414da6c28aaSamw 	result_list->reserved = 0;
415da6c28aaSamw 	result_list->reserved2 = 0;
416da6c28aaSamw 
417da6c28aaSamw 	result = &result_list->p_results[0];
4188d7e4166Sjose borrego 	result->result = NDR_PCDR_ACCEPTANCE;
419da6c28aaSamw 	result->reason = 0;
420da6c28aaSamw 	bzero(&result->transfer_syntax, sizeof (result->transfer_syntax));
421da6c28aaSamw 
4222c1b14e5Sjose borrego 	cont_list = &mxa->recv_hdr.alter_context_hdr.p_context_elem;
423da6c28aaSamw 	p_cont_id = cont_list->p_cont_elem[0].p_cont_id;
424da6c28aaSamw 
4258d7e4166Sjose borrego 	if (ndr_svc_find_binding(mxa, p_cont_id) != NULL)
4268d7e4166Sjose borrego 		return (NDR_DRC_FAULT_BIND_PCONT_BUSY);
427da6c28aaSamw 
4288d7e4166Sjose borrego 	if ((mbind = ndr_svc_new_binding(mxa)) == NULL) {
4298d7e4166Sjose borrego 		result->result = NDR_PCDR_PROVIDER_REJECTION;
4308d7e4166Sjose borrego 		result->reason = NDR_PPR_LOCAL_LIMIT_EXCEEDED;
4318d7e4166Sjose borrego 		return (NDR_DRC_OK);
432da6c28aaSamw 	}
433da6c28aaSamw 
434da6c28aaSamw 	as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid;
435da6c28aaSamw 	as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version;
436da6c28aaSamw 
437da6c28aaSamw 	ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid;
438da6c28aaSamw 	ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version;
439da6c28aaSamw 
4408d7e4166Sjose borrego 	msvc = ndr_svc_lookup_uuid(as_uuid, as_vers, ts_uuid, ts_vers);
4418d7e4166Sjose borrego 	if (msvc == NULL) {
4428d7e4166Sjose borrego 		result->result = NDR_PCDR_PROVIDER_REJECTION;
4438d7e4166Sjose borrego 		result->reason = NDR_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
4448d7e4166Sjose borrego 		return (NDR_DRC_OK);
445da6c28aaSamw 	}
446da6c28aaSamw 
447da6c28aaSamw 	mbind->p_cont_id = p_cont_id;
4488d7e4166Sjose borrego 	mbind->which_side = NDR_BIND_SIDE_SERVER;
449da6c28aaSamw 	/* mbind->context set by app */
450da6c28aaSamw 	mbind->service = msvc;
451da6c28aaSamw 	mbind->instance_specific = 0;
452da6c28aaSamw 	mxa->binding = mbind;
453da6c28aaSamw 
4542c1b14e5Sjose borrego 	sec_addr = &mxa->send_hdr.alter_context_rsp_hdr.sec_addr;
455da6c28aaSamw 	sec_addr->length = 0;
4568d7e4166Sjose borrego 	bzero(sec_addr->port_spec, NDR_PORT_ANY_MAX_PORT_SPEC);
457da6c28aaSamw 
458da6c28aaSamw 	result->transfer_syntax =
459da6c28aaSamw 	    cont_list->p_cont_elem[0].transfer_syntaxes[0];
460da6c28aaSamw 
4618d7e4166Sjose borrego 	return (NDR_DRC_BINDING_MADE);
462da6c28aaSamw }
463da6c28aaSamw 
464da6c28aaSamw static int
ndr_svc_request(ndr_xa_t * mxa)4658d7e4166Sjose borrego ndr_svc_request(ndr_xa_t *mxa)
466da6c28aaSamw {
4678d7e4166Sjose borrego 	ndr_binding_t	*mbind;
4688d7e4166Sjose borrego 	ndr_service_t	*msvc;
4698d7e4166Sjose borrego 	unsigned	p_cont_id;
4708d7e4166Sjose borrego 	int		rc;
471da6c28aaSamw 
472da6c28aaSamw 	mxa->opnum = mxa->recv_hdr.request_hdr.opnum;
473da6c28aaSamw 	p_cont_id = mxa->recv_hdr.request_hdr.p_cont_id;
474da6c28aaSamw 
4758d7e4166Sjose borrego 	if ((mbind = ndr_svc_find_binding(mxa, p_cont_id)) == NULL)
4768d7e4166Sjose borrego 		return (NDR_DRC_FAULT_REQUEST_PCONT_INVALID);
477da6c28aaSamw 
478da6c28aaSamw 	mxa->binding = mbind;
479da6c28aaSamw 	msvc = mbind->service;
480da6c28aaSamw 
481da6c28aaSamw 	/*
482da6c28aaSamw 	 * Make room for the response hdr.
483da6c28aaSamw 	 */
4848d7e4166Sjose borrego 	mxa->send_nds.pdu_scan_offset = NDR_RSP_HDR_SIZE;
485da6c28aaSamw 
486da6c28aaSamw 	if (msvc->call_stub)
487da6c28aaSamw 		rc = (*msvc->call_stub)(mxa);
488da6c28aaSamw 	else
4898d7e4166Sjose borrego 		rc = ndr_generic_call_stub(mxa);
490da6c28aaSamw 
4918d7e4166Sjose borrego 	if (NDR_DRC_IS_FAULT(rc)) {
4928d7e4166Sjose borrego 		ndo_printf(0, 0, "%s[0x%02x]: 0x%04x",
493da6c28aaSamw 		    msvc->name, mxa->opnum, rc);
494da6c28aaSamw 	}
495da6c28aaSamw 
496da6c28aaSamw 	return (rc);
497da6c28aaSamw }
498da6c28aaSamw 
499da6c28aaSamw /*
5008d7e4166Sjose borrego  * The transaction and the two nds streams use the same heap, which
501da6c28aaSamw  * should already exist at this point.  The heap will also be available
502da6c28aaSamw  * to the stub.
503da6c28aaSamw  */
50455bf511dSas int
ndr_generic_call_stub(ndr_xa_t * mxa)5058d7e4166Sjose borrego ndr_generic_call_stub(ndr_xa_t *mxa)
506da6c28aaSamw {
5078d7e4166Sjose borrego 	ndr_binding_t 		*mbind = mxa->binding;
5088d7e4166Sjose borrego 	ndr_service_t		*msvc = mbind->service;
5098d7e4166Sjose borrego 	ndr_typeinfo_t		*intf_ti = msvc->interface_ti;
5108d7e4166Sjose borrego 	ndr_stub_table_t	*ste;
511da6c28aaSamw 	int			opnum = mxa->opnum;
512da6c28aaSamw 	unsigned		p_len = intf_ti->c_size_fixed_part;
513da6c28aaSamw 	char 			*param;
514da6c28aaSamw 	int			rc;
515da6c28aaSamw 
516da6c28aaSamw 	if (mxa->heap == NULL) {
5178d7e4166Sjose borrego 		ndo_printf(0, 0, "%s[0x%02x]: no heap", msvc->name, opnum);
5188d7e4166Sjose borrego 		return (NDR_DRC_FAULT_OUT_OF_MEMORY);
519da6c28aaSamw 	}
520da6c28aaSamw 
5218d7e4166Sjose borrego 	if ((ste = ndr_svc_find_stub(msvc, opnum)) == NULL) {
5228d7e4166Sjose borrego 		ndo_printf(0, 0, "%s[0x%02x]: invalid opnum",
523da6c28aaSamw 		    msvc->name, opnum);
5248d7e4166Sjose borrego 		return (NDR_DRC_FAULT_REQUEST_OPNUM_INVALID);
525da6c28aaSamw 	}
526da6c28aaSamw 
5278d7e4166Sjose borrego 	if ((param = ndr_heap_malloc(mxa->heap, p_len)) == NULL)
5288d7e4166Sjose borrego 		return (NDR_DRC_FAULT_OUT_OF_MEMORY);
529da6c28aaSamw 
530da6c28aaSamw 	bzero(param, p_len);
531da6c28aaSamw 
5328d7e4166Sjose borrego 	rc = ndr_decode_call(mxa, param);
5338d7e4166Sjose borrego 	if (!NDR_DRC_IS_OK(rc))
534da6c28aaSamw 		return (rc);
535da6c28aaSamw 
536da6c28aaSamw 	rc = (*ste->func)(param, mxa);
5378d7e4166Sjose borrego 	if (rc == NDR_DRC_OK)
5388d7e4166Sjose borrego 		rc = ndr_encode_return(mxa, param);
539da6c28aaSamw 
540da6c28aaSamw 	return (rc);
541da6c28aaSamw }
542da6c28aaSamw 
543da6c28aaSamw /*
544da6c28aaSamw  * We can perform some initial setup of the response header here.
545da6c28aaSamw  * We also need to cache some of the information from the bind
546da6c28aaSamw  * negotiation for use during subsequent RPC calls.
547da6c28aaSamw  */
548da6c28aaSamw static void
ndr_reply_prepare_hdr(ndr_xa_t * mxa)5498d7e4166Sjose borrego ndr_reply_prepare_hdr(ndr_xa_t *mxa)
550da6c28aaSamw {
5512c1b14e5Sjose borrego 	ndr_common_header_t *rhdr = &mxa->recv_hdr.common_hdr;
5522c1b14e5Sjose borrego 	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
553da6c28aaSamw 
554da6c28aaSamw 	hdr->rpc_vers = 5;
555da6c28aaSamw 	hdr->rpc_vers_minor = 0;
5568d7e4166Sjose borrego 	hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG;
557da6c28aaSamw 	hdr->packed_drep = rhdr->packed_drep;
558da6c28aaSamw 	hdr->frag_length = 0;
559da6c28aaSamw 	hdr->auth_length = 0;
560da6c28aaSamw 	hdr->call_id = rhdr->call_id;
561da6c28aaSamw #ifdef _BIG_ENDIAN
5628d7e4166Sjose borrego 	hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
5638d7e4166Sjose borrego 	    | NDR_REPLAB_INTG_BIG_ENDIAN;
564da6c28aaSamw #else
5658d7e4166Sjose borrego 	hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
5668d7e4166Sjose borrego 	    | NDR_REPLAB_INTG_LITTLE_ENDIAN;
567da6c28aaSamw #endif
568da6c28aaSamw 
569da6c28aaSamw 	switch (mxa->ptype) {
5708d7e4166Sjose borrego 	case NDR_PTYPE_BIND:
57168b2bbf2SGordon Ross 		/*
57268b2bbf2SGordon Ross 		 * Compute the maximum fragment sizes for xmit/recv
57368b2bbf2SGordon Ross 		 * and store in the pipe endpoint.  Note "xmit" is
57468b2bbf2SGordon Ross 		 * client-to-server; "recv" is server-to-client.
57568b2bbf2SGordon Ross 		 */
57668b2bbf2SGordon Ross 		if (mxa->pipe->np_max_xmit_frag >
57768b2bbf2SGordon Ross 		    mxa->recv_hdr.bind_hdr.max_xmit_frag)
57868b2bbf2SGordon Ross 			mxa->pipe->np_max_xmit_frag =
57968b2bbf2SGordon Ross 			    mxa->recv_hdr.bind_hdr.max_xmit_frag;
58068b2bbf2SGordon Ross 		if (mxa->pipe->np_max_recv_frag >
58168b2bbf2SGordon Ross 		    mxa->recv_hdr.bind_hdr.max_recv_frag)
58268b2bbf2SGordon Ross 			mxa->pipe->np_max_recv_frag =
58368b2bbf2SGordon Ross 			    mxa->recv_hdr.bind_hdr.max_recv_frag;
58468b2bbf2SGordon Ross 
5858d7e4166Sjose borrego 		hdr->ptype = NDR_PTYPE_BIND_ACK;
586da6c28aaSamw 		mxa->send_hdr.bind_ack_hdr.max_xmit_frag =
58768b2bbf2SGordon Ross 		    mxa->pipe->np_max_xmit_frag;
588da6c28aaSamw 		mxa->send_hdr.bind_ack_hdr.max_recv_frag =
58968b2bbf2SGordon Ross 		    mxa->pipe->np_max_recv_frag;
59068b2bbf2SGordon Ross 
59168b2bbf2SGordon Ross 		/*
59268b2bbf2SGordon Ross 		 * We're supposed to assign a unique "assoc group"
59368b2bbf2SGordon Ross 		 * (identifies this connection for the client).
59468b2bbf2SGordon Ross 		 * Using the pipe address is adequate.
59568b2bbf2SGordon Ross 		 */
596da6c28aaSamw 		mxa->send_hdr.bind_ack_hdr.assoc_group_id =
597da6c28aaSamw 		    mxa->recv_hdr.bind_hdr.assoc_group_id;
598da6c28aaSamw 		if (mxa->send_hdr.bind_ack_hdr.assoc_group_id == 0)
59968b2bbf2SGordon Ross 			mxa->send_hdr.bind_ack_hdr.assoc_group_id =
60068b2bbf2SGordon Ross 			    (DWORD)(uintptr_t)mxa->pipe;
601da6c28aaSamw 
602da6c28aaSamw 		break;
603da6c28aaSamw 
6048d7e4166Sjose borrego 	case NDR_PTYPE_REQUEST:
6058d7e4166Sjose borrego 		hdr->ptype = NDR_PTYPE_RESPONSE;
606da6c28aaSamw 		/* mxa->send_hdr.response_hdr.alloc_hint */
607da6c28aaSamw 		mxa->send_hdr.response_hdr.p_cont_id =
608da6c28aaSamw 		    mxa->recv_hdr.request_hdr.p_cont_id;
609da6c28aaSamw 		mxa->send_hdr.response_hdr.cancel_count = 0;
610da6c28aaSamw 		mxa->send_hdr.response_hdr.reserved = 0;
611da6c28aaSamw 		break;
612da6c28aaSamw 
6138d7e4166Sjose borrego 	case NDR_PTYPE_ALTER_CONTEXT:
6148d7e4166Sjose borrego 		hdr->ptype = NDR_PTYPE_ALTER_CONTEXT_RESP;
615da6c28aaSamw 		/*
6162c1b14e5Sjose borrego 		 * The max_xmit_frag, max_recv_frag and assoc_group_id are
6172c1b14e5Sjose borrego 		 * ignored by the client but it's useful to fill them in.
618da6c28aaSamw 		 */
6192c1b14e5Sjose borrego 		mxa->send_hdr.alter_context_rsp_hdr.max_xmit_frag =
6202c1b14e5Sjose borrego 		    mxa->recv_hdr.alter_context_hdr.max_xmit_frag;
6212c1b14e5Sjose borrego 		mxa->send_hdr.alter_context_rsp_hdr.max_recv_frag =
6222c1b14e5Sjose borrego 		    mxa->recv_hdr.alter_context_hdr.max_recv_frag;
6232c1b14e5Sjose borrego 		mxa->send_hdr.alter_context_rsp_hdr.assoc_group_id =
6242c1b14e5Sjose borrego 		    mxa->recv_hdr.alter_context_hdr.assoc_group_id;
625da6c28aaSamw 		break;
626da6c28aaSamw 
627da6c28aaSamw 	default:
628da6c28aaSamw 		hdr->ptype = 0xFF;
629da6c28aaSamw 	}
630da6c28aaSamw }
631da6c28aaSamw 
632da6c28aaSamw /*
633da6c28aaSamw  * Signal an RPC fault. The stream is reset and we overwrite whatever
634da6c28aaSamw  * was in the response header with the fault information.
635da6c28aaSamw  */
636da6c28aaSamw static void
ndr_reply_fault(ndr_xa_t * mxa,unsigned long drc)6378d7e4166Sjose borrego ndr_reply_fault(ndr_xa_t *mxa, unsigned long drc)
638da6c28aaSamw {
6392c1b14e5Sjose borrego 	ndr_common_header_t *rhdr = &mxa->recv_hdr.common_hdr;
6402c1b14e5Sjose borrego 	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
6418d7e4166Sjose borrego 	ndr_stream_t *nds = &mxa->send_nds;
642da6c28aaSamw 	unsigned long fault_status;
643da6c28aaSamw 
644*4aab0a3bSJohn Levon 	(void) NDS_RESET(nds);
645da6c28aaSamw 
646da6c28aaSamw 	hdr->rpc_vers = 5;
647da6c28aaSamw 	hdr->rpc_vers_minor = 0;
6488d7e4166Sjose borrego 	hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG;
649da6c28aaSamw 	hdr->packed_drep = rhdr->packed_drep;
650da6c28aaSamw 	hdr->frag_length = sizeof (mxa->send_hdr.fault_hdr);
651da6c28aaSamw 	hdr->auth_length = 0;
652da6c28aaSamw 	hdr->call_id = rhdr->call_id;
653da6c28aaSamw #ifdef _BIG_ENDIAN
6548d7e4166Sjose borrego 	hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
6558d7e4166Sjose borrego 	    | NDR_REPLAB_INTG_BIG_ENDIAN;
656da6c28aaSamw #else
6578d7e4166Sjose borrego 	hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
6588d7e4166Sjose borrego 	    | NDR_REPLAB_INTG_LITTLE_ENDIAN;
659da6c28aaSamw #endif
660da6c28aaSamw 
6618d7e4166Sjose borrego 	switch (drc & NDR_DRC_MASK_SPECIFIER) {
6628d7e4166Sjose borrego 	case NDR_DRC_FAULT_OUT_OF_MEMORY:
6638d7e4166Sjose borrego 	case NDR_DRC_FAULT_ENCODE_TOO_BIG:
6648d7e4166Sjose borrego 		fault_status = NDR_FAULT_NCA_OUT_ARGS_TOO_BIG;
665da6c28aaSamw 		break;
666da6c28aaSamw 
6678d7e4166Sjose borrego 	case NDR_DRC_FAULT_REQUEST_PCONT_INVALID:
6688d7e4166Sjose borrego 		fault_status = NDR_FAULT_NCA_INVALID_PRES_CONTEXT_ID;
669da6c28aaSamw 		break;
670da6c28aaSamw 
6718d7e4166Sjose borrego 	case NDR_DRC_FAULT_REQUEST_OPNUM_INVALID:
6728d7e4166Sjose borrego 		fault_status = NDR_FAULT_NCA_OP_RNG_ERROR;
673da6c28aaSamw 		break;
674da6c28aaSamw 
6758d7e4166Sjose borrego 	case NDR_DRC_FAULT_DECODE_FAILED:
6768d7e4166Sjose borrego 	case NDR_DRC_FAULT_ENCODE_FAILED:
6778d7e4166Sjose borrego 		fault_status = NDR_FAULT_NCA_PROTO_ERROR;
678da6c28aaSamw 		break;
679da6c28aaSamw 
680da6c28aaSamw 	default:
6818d7e4166Sjose borrego 		fault_status = NDR_FAULT_NCA_UNSPEC_REJECT;
682da6c28aaSamw 		break;
683da6c28aaSamw 	}
684da6c28aaSamw 
6858d7e4166Sjose borrego 	mxa->send_hdr.fault_hdr.common_hdr.ptype = NDR_PTYPE_FAULT;
686da6c28aaSamw 	mxa->send_hdr.fault_hdr.status = fault_status;
687da6c28aaSamw 	mxa->send_hdr.response_hdr.alloc_hint = hdr->frag_length;
688da6c28aaSamw }
689da6c28aaSamw 
6902c1b14e5Sjose borrego /*
6912c1b14e5Sjose borrego  * Note that the frag_length for bind ack and alter context is
6922c1b14e5Sjose borrego  * non-standard.
6932c1b14e5Sjose borrego  */
694da6c28aaSamw static int
ndr_send_reply(ndr_xa_t * mxa)69568b2bbf2SGordon Ross ndr_send_reply(ndr_xa_t *mxa)
696da6c28aaSamw {
6972c1b14e5Sjose borrego 	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
6988d7e4166Sjose borrego 	ndr_stream_t *nds = &mxa->send_nds;
69919d41fccSamw 	uint8_t *pdu_buf;
700da6c28aaSamw 	unsigned long pdu_size;
701da6c28aaSamw 	unsigned long frag_size;
702da6c28aaSamw 	unsigned long pdu_data_size;
703da6c28aaSamw 	unsigned long frag_data_size;
704da6c28aaSamw 
70568b2bbf2SGordon Ross 	frag_size = mxa->pipe->np_max_recv_frag;
7068d7e4166Sjose borrego 	pdu_size = nds->pdu_size;
7078d7e4166Sjose borrego 	pdu_buf = nds->pdu_base_addr;
708da6c28aaSamw 
709da6c28aaSamw 	if (pdu_size <= frag_size) {
710da6c28aaSamw 		/*
711da6c28aaSamw 		 * Single fragment response. The PDU size may be zero
712da6c28aaSamw 		 * here (i.e. bind or fault response). So don't make
713da6c28aaSamw 		 * any assumptions about it until after the header is
714da6c28aaSamw 		 * encoded.
715da6c28aaSamw 		 */
716da6c28aaSamw 		switch (hdr->ptype) {
7178d7e4166Sjose borrego 		case NDR_PTYPE_BIND_ACK:
7188d7e4166Sjose borrego 			hdr->frag_length = ndr_bind_ack_hdr_size(mxa);
719da6c28aaSamw 			break;
720da6c28aaSamw 
7218d7e4166Sjose borrego 		case NDR_PTYPE_FAULT:
722da6c28aaSamw 			/* already setup */
723da6c28aaSamw 			break;
724da6c28aaSamw 
7258d7e4166Sjose borrego 		case NDR_PTYPE_RESPONSE:
726da6c28aaSamw 			hdr->frag_length = pdu_size;
727da6c28aaSamw 			mxa->send_hdr.response_hdr.alloc_hint =
728da6c28aaSamw 			    hdr->frag_length;
729da6c28aaSamw 			break;
730da6c28aaSamw 
7318d7e4166Sjose borrego 		case NDR_PTYPE_ALTER_CONTEXT_RESP:
7328d7e4166Sjose borrego 			hdr->frag_length = ndr_alter_context_rsp_hdr_size();
7332c1b14e5Sjose borrego 			break;
7342c1b14e5Sjose borrego 
735da6c28aaSamw 		default:
736da6c28aaSamw 			hdr->frag_length = pdu_size;
737da6c28aaSamw 			break;
738da6c28aaSamw 		}
739da6c28aaSamw 
7408d7e4166Sjose borrego 		nds->pdu_scan_offset = 0;
7418d7e4166Sjose borrego 		(void) ndr_encode_pdu_hdr(mxa);
7428d7e4166Sjose borrego 		pdu_size = nds->pdu_size;
74368b2bbf2SGordon Ross 		(void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, pdu_size);
744da6c28aaSamw 		return (0);
745da6c28aaSamw 	}
746da6c28aaSamw 
747da6c28aaSamw 	/*
748da6c28aaSamw 	 * Multiple fragment response.
74968b2bbf2SGordon Ross 	 *
75068b2bbf2SGordon Ross 	 * We need to update the RPC header for every fragment.
751da6c28aaSamw 	 *
75219d41fccSamw 	 * pdu_data_size:	total data remaining to be handled
75319d41fccSamw 	 * frag_size:		total fragment size including header
75419d41fccSamw 	 * frag_data_size:	data in fragment
7558d7e4166Sjose borrego 	 *			(i.e. frag_size - NDR_RSP_HDR_SIZE)
756da6c28aaSamw 	 */
7578d7e4166Sjose borrego 	pdu_data_size = pdu_size - NDR_RSP_HDR_SIZE;
7588d7e4166Sjose borrego 	frag_data_size = frag_size - NDR_RSP_HDR_SIZE;
759da6c28aaSamw 
76068b2bbf2SGordon Ross 	/*
76168b2bbf2SGordon Ross 	 * Send the first frag.
76268b2bbf2SGordon Ross 	 */
76368b2bbf2SGordon Ross 	hdr->pfc_flags = NDR_PFC_FIRST_FRAG;
76468b2bbf2SGordon Ross 	hdr->frag_length = frag_size;
76568b2bbf2SGordon Ross 	mxa->send_hdr.response_hdr.alloc_hint = pdu_data_size;
76668b2bbf2SGordon Ross 	nds->pdu_scan_offset = 0;
76768b2bbf2SGordon Ross 	(void) ndr_encode_pdu_hdr(mxa);
76868b2bbf2SGordon Ross 	(void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, frag_size);
76968b2bbf2SGordon Ross 	pdu_data_size -= frag_data_size;
77068b2bbf2SGordon Ross 	pdu_buf += frag_data_size;
771da6c28aaSamw 
77268b2bbf2SGordon Ross 	/*
77368b2bbf2SGordon Ross 	 * Send "middle" (full-sized) fragments...
77468b2bbf2SGordon Ross 	 */
77568b2bbf2SGordon Ross 	hdr->pfc_flags = 0;
77668b2bbf2SGordon Ross 	while (pdu_data_size > frag_data_size) {
777da6c28aaSamw 
77819d41fccSamw 		hdr->frag_length = frag_size;
77968b2bbf2SGordon Ross 		mxa->send_hdr.response_hdr.alloc_hint = pdu_data_size;
7808d7e4166Sjose borrego 		nds->pdu_scan_offset = 0;
7818d7e4166Sjose borrego 		(void) ndr_encode_pdu_hdr(mxa);
7828d7e4166Sjose borrego 		bcopy(nds->pdu_base_addr, pdu_buf, NDR_RSP_HDR_SIZE);
78368b2bbf2SGordon Ross 		(void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, frag_size);
78468b2bbf2SGordon Ross 		pdu_data_size -= frag_data_size;
78568b2bbf2SGordon Ross 		pdu_buf += frag_data_size;
786da6c28aaSamw 	}
787da6c28aaSamw 
78868b2bbf2SGordon Ross 	/*
78968b2bbf2SGordon Ross 	 * Last frag (pdu_data_size <= frag_data_size)
79068b2bbf2SGordon Ross 	 */
79168b2bbf2SGordon Ross 	hdr->pfc_flags = NDR_PFC_LAST_FRAG;
79268b2bbf2SGordon Ross 	frag_size = pdu_data_size + NDR_RSP_HDR_SIZE;
79368b2bbf2SGordon Ross 	hdr->frag_length = frag_size;
79468b2bbf2SGordon Ross 	mxa->send_hdr.response_hdr.alloc_hint = pdu_data_size;
79568b2bbf2SGordon Ross 	nds->pdu_scan_offset = 0;
79668b2bbf2SGordon Ross 	(void) ndr_encode_pdu_hdr(mxa);
79768b2bbf2SGordon Ross 	bcopy(nds->pdu_base_addr, pdu_buf, NDR_RSP_HDR_SIZE);
79868b2bbf2SGordon Ross 	(void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, frag_size);
79919d41fccSamw 
80068b2bbf2SGordon Ross 	return (0);
80119d41fccSamw }
802