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
47static int ndr_svc_process(ndr_xa_t *);
48static int ndr_svc_bind(ndr_xa_t *);
49static int ndr_svc_request(ndr_xa_t *);
50static void ndr_reply_prepare_hdr(ndr_xa_t *);
51static int ndr_svc_alter_context(ndr_xa_t *);
52static void ndr_reply_fault(ndr_xa_t *, unsigned long);
53
54static int ndr_recv_request(ndr_xa_t *mxa);
55static int ndr_recv_frag(ndr_xa_t *mxa);
56static int ndr_send_reply(ndr_xa_t *);
57
58static int ndr_pipe_process(ndr_pipe_t *, ndr_xa_t *);
59
60/*
61 * External entry point called by smbd.
62 */
63void
64ndr_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 */
92static int
93ndr_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
123out4:
124	nds_destruct(&mxa->send_nds);
125out3:
126	nds_destruct(&mxa->recv_nds);
127out2:
128	ndr_heap_destroy(mxa->heap);
129out1:
130	return (rc);
131}
132
133/*
134 * Receive an entire RPC request (all fragments)
135 * Returns zero or an NDR fault code.
136 */
137static int
138ndr_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 */
186static int
187ndr_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 */
242static int
243ndr_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 */
277static int
278ndr_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 */
397static int
398ndr_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
464static int
465ndr_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 */
504int
505ndr_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 */
548static void
549ndr_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 */
636static void
637ndr_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 */
694static int
695ndr_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}
802