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