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