xref: /titanic_44/usr/src/lib/smbsrv/libmlrpc/common/ndr_server.c (revision 67e3a03ed4a2813074d36330f062ed6e593a4937)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Server side RPC handler.
30  */
31 
32 #include <sys/byteorder.h>
33 #include <thread.h>
34 #include <synch.h>
35 #include <stdlib.h>
36 #include <strings.h>
37 #include <string.h>
38 #include <time.h>
39 
40 #include <smbsrv/libsmb.h>
41 #include <smbsrv/libmlrpc.h>
42 #include <smbsrv/mlsvc.h>
43 #include <smbsrv/ndr.h>
44 #include <smbsrv/mlrpc.h>
45 #include <smbsrv/mlsvc_util.h>
46 #include <smbsrv/smb_winpipe.h>
47 
48 /*
49  * Fragment size (5680: NT style).
50  */
51 #define	MLRPC_FRAG_SZ		5680
52 static unsigned long mlrpc_frag_size = MLRPC_FRAG_SZ;
53 
54 /*
55  * Context table.
56  */
57 #define	CTXT_TABLE_ENTRIES	128
58 static struct mlsvc_rpc_context context_table[CTXT_TABLE_ENTRIES];
59 static mutex_t mlrpc_context_lock;
60 
61 static int mlrpc_s_process(struct mlrpc_xaction *);
62 static int mlrpc_s_bind(struct mlrpc_xaction *);
63 static int mlrpc_s_request(struct mlrpc_xaction *);
64 static void mlrpc_reply_prepare_hdr(struct mlrpc_xaction *);
65 static int mlrpc_s_alter_context(struct mlrpc_xaction *);
66 static void mlrpc_reply_bind_ack(struct mlrpc_xaction *);
67 static void mlrpc_reply_fault(struct mlrpc_xaction *, unsigned long);
68 static int mlrpc_build_reply(struct mlrpc_xaction *);
69 static void mlrpc_build_frag(struct mlndr_stream *, uint8_t *, uint32_t);
70 
71 /*
72  * This is the RPC service server-side entry point.  All MSRPC encoded
73  * messages should be passed through here.  We use the same context
74  * structure as the client side but we don't need to set up the client
75  * side info.
76  */
77 struct mlsvc_rpc_context *
78 mlrpc_process(int fid, smb_dr_user_ctx_t *user_ctx)
79 {
80 	struct mlsvc_rpc_context	*context;
81 	struct mlrpc_xaction		*mxa;
82 	struct mlndr_stream		*recv_mlnds;
83 	struct mlndr_stream		*send_mlnds;
84 	char				*data;
85 	int				datalen;
86 
87 	if ((context = mlrpc_lookup(fid)) == NULL)
88 		return (NULL);
89 
90 	context->user_ctx = user_ctx;
91 	data = context->inpipe->sp_data;
92 	datalen = context->inpipe->sp_datalen;
93 
94 	mxa = (struct mlrpc_xaction *)malloc(sizeof (struct mlrpc_xaction));
95 	if (mxa == NULL)
96 		return (NULL);
97 
98 	bzero(mxa, sizeof (struct mlrpc_xaction));
99 	mxa->fid = fid;
100 	mxa->context = context;
101 	mxa->binding_list = context->binding;
102 
103 	if ((mxa->heap = mlrpc_heap_create()) == NULL) {
104 		free(mxa);
105 		return (NULL);
106 	}
107 
108 	recv_mlnds = &mxa->recv_mlnds;
109 	(void) mlnds_initialize(recv_mlnds, datalen, NDR_MODE_CALL_RECV,
110 	    mxa->heap);
111 
112 	bcopy(data, recv_mlnds->pdu_base_addr, datalen);
113 
114 	send_mlnds = &mxa->send_mlnds;
115 	(void) mlnds_initialize(send_mlnds, 0, NDR_MODE_RETURN_SEND, mxa->heap);
116 
117 	(void) mlrpc_s_process(mxa);
118 
119 	context->outpipe->sp_datalen = mlnds_finalize(send_mlnds,
120 	    (uint8_t *)context->outpipe->sp_data,
121 	    SMB_CTXT_PIPE_SZ - sizeof (smb_pipe_t));
122 
123 	mlnds_destruct(&mxa->recv_mlnds);
124 	mlnds_destruct(&mxa->send_mlnds);
125 	mlrpc_heap_destroy(mxa->heap);
126 	free(mxa);
127 	return (context);
128 }
129 
130 /*
131  * Lookup the context for pipeid. If one exists, return a pointer to it.
132  * Otherwise attempt to allocate a new context and return it. If the
133  * context table is full, return a null pointer.
134  */
135 struct mlsvc_rpc_context *
136 mlrpc_lookup(int fid)
137 {
138 	struct mlsvc_rpc_context *context;
139 	struct mlsvc_rpc_context *available = NULL;
140 	int i;
141 
142 	(void) mutex_lock(&mlrpc_context_lock);
143 
144 	for (i = 0; i < CTXT_TABLE_ENTRIES; ++i) {
145 		context = &context_table[i];
146 
147 		if (available == NULL && context->fid == 0) {
148 			available = context;
149 			continue;
150 		}
151 
152 		if (context->fid == fid) {
153 			(void) mutex_unlock(&mlrpc_context_lock);
154 			return (context);
155 		}
156 	}
157 
158 	if (available) {
159 		bzero(available, sizeof (struct mlsvc_rpc_context));
160 		available->inpipe = malloc(SMB_CTXT_PIPE_SZ);
161 		available->outpipe = malloc(SMB_CTXT_PIPE_SZ);
162 
163 		if (available->inpipe == NULL || available->outpipe == NULL) {
164 			free(available->inpipe);
165 			free(available->outpipe);
166 			bzero(available, sizeof (struct mlsvc_rpc_context));
167 			(void) mutex_unlock(&mlrpc_context_lock);
168 			return (NULL);
169 		}
170 
171 		bzero(available->inpipe, sizeof (smb_pipe_t));
172 		bzero(available->outpipe, sizeof (smb_pipe_t));
173 		available->fid = fid;
174 		available->inpipe->sp_pipeid = fid;
175 		available->outpipe->sp_pipeid = fid;
176 
177 		mlrpc_binding_pool_initialize(&available->binding,
178 		    available->binding_pool, CTXT_N_BINDING_POOL);
179 	}
180 
181 	(void) mutex_unlock(&mlrpc_context_lock);
182 	return (available);
183 }
184 
185 /*
186  * This function should be called to release the context associated
187  * with a fid when the client performs a close file.
188  */
189 void
190 mlrpc_release(int fid)
191 {
192 	struct mlsvc_rpc_context *context;
193 	int i;
194 
195 	(void) mutex_lock(&mlrpc_context_lock);
196 
197 	for (i = 0; i < CTXT_TABLE_ENTRIES; ++i) {
198 		context = &context_table[i];
199 
200 		if (context->fid == fid) {
201 			ndr_hdclose(fid);
202 			free(context->inpipe);
203 			free(context->outpipe);
204 			bzero(context, sizeof (struct mlsvc_rpc_context));
205 			break;
206 		}
207 	}
208 
209 	(void) mutex_unlock(&mlrpc_context_lock);
210 }
211 
212 /*
213  * This is the entry point for all server-side RPC processing.
214  * It is assumed that the PDU has already been received.
215  */
216 static int
217 mlrpc_s_process(struct mlrpc_xaction *mxa)
218 {
219 	int rc;
220 
221 	rc = mlrpc_decode_pdu_hdr(mxa);
222 	if (!MLRPC_DRC_IS_OK(rc))
223 		return (-1);
224 
225 	(void) mlrpc_reply_prepare_hdr(mxa);
226 
227 	switch (mxa->ptype) {
228 	case MLRPC_PTYPE_BIND:
229 		rc = mlrpc_s_bind(mxa);
230 		break;
231 
232 	case MLRPC_PTYPE_REQUEST:
233 		rc = mlrpc_s_request(mxa);
234 		break;
235 
236 	case MLRPC_PTYPE_ALTER_CONTEXT:
237 		rc = mlrpc_s_alter_context(mxa);
238 		break;
239 
240 	default:
241 		rc = MLRPC_DRC_FAULT_RPCHDR_PTYPE_INVALID;
242 		break;
243 	}
244 
245 	if (MLRPC_DRC_IS_FAULT(rc))
246 		mlrpc_reply_fault(mxa, rc);
247 
248 	(void) mlrpc_build_reply(mxa);
249 	return (rc);
250 }
251 
252 /*
253  * Multiple p_cont_elem[]s, multiple transfer_syntaxes[] and multiple
254  * p_results[] not supported.
255  */
256 static int
257 mlrpc_s_bind(struct mlrpc_xaction *mxa)
258 {
259 	mlrpc_p_cont_list_t	*cont_list;
260 	mlrpc_p_result_list_t	*result_list;
261 	mlrpc_p_result_t	*result;
262 	unsigned		p_cont_id;
263 	struct mlrpc_binding	*mbind;
264 	ndr_uuid_t		*as_uuid;
265 	ndr_uuid_t		*ts_uuid;
266 	char			as_buf[64];
267 	char			ts_buf[64];
268 	int			as_vers;
269 	int			ts_vers;
270 	struct mlndr_stream	*send_mlnds;
271 	struct mlrpc_service	*msvc;
272 	int			rc;
273 	mlrpc_port_any_t	*sec_addr;
274 
275 	/* acquire targets */
276 	cont_list = &mxa->recv_hdr.bind_hdr.p_context_elem;
277 	result_list = &mxa->send_hdr.bind_ack_hdr.p_result_list;
278 	result = &result_list->p_results[0];
279 
280 	/*
281 	 * Set up temporary secondary address port.
282 	 * We will correct this later (below).
283 	 */
284 	send_mlnds = &mxa->send_mlnds;
285 	sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
286 	sec_addr->length = 13;
287 	(void) strcpy((char *)sec_addr->port_spec, "\\PIPE\\ntsvcs");
288 
289 	result_list->n_results = 1;
290 	result_list->reserved = 0;
291 	result_list->reserved2 = 0;
292 	result->result = MLRPC_PCDR_ACCEPTANCE;
293 	result->reason = 0;
294 	bzero(&result->transfer_syntax, sizeof (result->transfer_syntax));
295 
296 	/* sanity check */
297 	if (cont_list->n_context_elem != 1 ||
298 	    cont_list->p_cont_elem[0].n_transfer_syn != 1) {
299 		mlndo_trace("mlrpc_s_bind: warning: multiple p_cont_elem");
300 	}
301 
302 	p_cont_id = cont_list->p_cont_elem[0].p_cont_id;
303 
304 	if ((mbind = mlrpc_find_binding(mxa, p_cont_id)) != NULL) {
305 		/*
306 		 * Duplicate p_cont_id.
307 		 * Send a bind_ack with a better error.
308 		 */
309 		mlndo_trace("mlrpc_s_bind: duplicate binding");
310 		return (MLRPC_DRC_FAULT_BIND_PCONT_BUSY);
311 	}
312 
313 	if ((mbind = mlrpc_new_binding(mxa)) == NULL) {
314 		/*
315 		 * No free binding slot
316 		 */
317 		result->result = MLRPC_PCDR_PROVIDER_REJECTION;
318 		result->reason = MLRPC_PPR_LOCAL_LIMIT_EXCEEDED;
319 		mlndo_trace("mlrpc_s_bind: no resources");
320 		return (MLRPC_DRC_OK);
321 	}
322 
323 	as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid;
324 	as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version;
325 
326 	ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid;
327 	ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version;
328 
329 	msvc = mlrpc_find_service_by_uuids(as_uuid, as_vers, ts_uuid, ts_vers);
330 	if (!msvc) {
331 		mlrpc_uuid_to_str(as_uuid, as_buf);
332 		mlrpc_uuid_to_str(ts_uuid, ts_buf);
333 
334 		mlndo_printf(send_mlnds, 0, "mlrpc_s_bind: unknown service");
335 		mlndo_printf(send_mlnds, 0, "abs=%s v%d, xfer=%s v%d",
336 		    as_buf, as_vers, ts_buf, ts_vers);
337 
338 		result->result = MLRPC_PCDR_PROVIDER_REJECTION;
339 		result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
340 		return (MLRPC_DRC_OK);
341 	}
342 
343 	/*
344 	 * We can now use the correct secondary address port.
345 	 */
346 	sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
347 	sec_addr->length = strlen(msvc->sec_addr_port) + 1;
348 	(void) strlcpy((char *)sec_addr->port_spec, msvc->sec_addr_port,
349 	    MLRPC_PORT_ANY_MAX_PORT_SPEC);
350 
351 	mbind->p_cont_id = p_cont_id;
352 	mbind->which_side = MLRPC_BIND_SIDE_SERVER;
353 	/* mbind->context set by app */
354 	mbind->service = msvc;
355 	mbind->instance_specific = 0;
356 
357 	mxa->binding = mbind;
358 
359 	if (msvc->bind_req) {
360 		/*
361 		 * Call the service-specific bind() handler.  If
362 		 * this fails, we shouild send a specific error
363 		 * on the bind ack.
364 		 */
365 		rc = (msvc->bind_req)(mxa);
366 		if (MLRPC_DRC_IS_FAULT(rc)) {
367 			mbind->service = 0;	/* free binding slot */
368 			mbind->which_side = 0;
369 			mbind->p_cont_id = 0;
370 			mbind->instance_specific = 0;
371 			return (rc);
372 		}
373 	}
374 
375 	result->transfer_syntax =
376 	    cont_list->p_cont_elem[0].transfer_syntaxes[0];
377 
378 	/*
379 	 * Special rejection of Windows 2000 DSSETUP interface.
380 	 * This interface was introduced in Windows 2000 but has
381 	 * been subsequently deprecated due to problems.
382 	 */
383 	if (strcmp(msvc->name, "DSSETUP") == 0) {
384 		result->result = MLRPC_PCDR_PROVIDER_REJECTION;
385 		result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
386 	}
387 
388 	return (MLRPC_DRC_BINDING_MADE);
389 }
390 
391 /*
392  * mlrpc_s_alter_context
393  *
394  * The alter context request is used to request additional presentation
395  * context for another interface and/or version. It's very similar to a
396  * bind request.
397  *
398  * We don't fully support multiple contexts so, for now, we reject this
399  * request.  Windows 2000 clients attempt to use an alternate LSA context
400  * when ACLs are modified.
401  */
402 static int
403 mlrpc_s_alter_context(struct mlrpc_xaction *mxa)
404 {
405 	mlrpc_p_result_list_t *result_list;
406 	mlrpc_p_result_t *result;
407 	mlrpc_p_cont_list_t *cont_list;
408 	struct mlrpc_binding *mbind;
409 	struct mlrpc_service *msvc;
410 	unsigned p_cont_id;
411 	ndr_uuid_t *as_uuid;
412 	ndr_uuid_t *ts_uuid;
413 	int as_vers;
414 	int ts_vers;
415 	mlrpc_port_any_t *sec_addr;
416 
417 	result_list = &mxa->send_hdr.bind_ack_hdr.p_result_list;
418 	result_list->n_results = 1;
419 	result_list->reserved = 0;
420 	result_list->reserved2 = 0;
421 
422 	result = &result_list->p_results[0];
423 	result->result = MLRPC_PCDR_ACCEPTANCE;
424 	result->reason = 0;
425 	bzero(&result->transfer_syntax, sizeof (result->transfer_syntax));
426 
427 	if (mxa != NULL) {
428 		result->result = MLRPC_PCDR_PROVIDER_REJECTION;
429 		result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
430 		return (MLRPC_DRC_OK);
431 	}
432 
433 	cont_list = &mxa->recv_hdr.bind_hdr.p_context_elem;
434 	p_cont_id = cont_list->p_cont_elem[0].p_cont_id;
435 
436 	if ((mbind = mlrpc_find_binding(mxa, p_cont_id)) != NULL)
437 		return (MLRPC_DRC_FAULT_BIND_PCONT_BUSY);
438 
439 	if ((mbind = mlrpc_new_binding(mxa)) == NULL) {
440 		result->result = MLRPC_PCDR_PROVIDER_REJECTION;
441 		result->reason = MLRPC_PPR_LOCAL_LIMIT_EXCEEDED;
442 		return (MLRPC_DRC_OK);
443 	}
444 
445 	as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid;
446 	as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version;
447 
448 	ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid;
449 	ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version;
450 
451 	msvc = mlrpc_find_service_by_uuids(as_uuid, as_vers, ts_uuid, ts_vers);
452 	if (msvc == 0) {
453 		result->result = MLRPC_PCDR_PROVIDER_REJECTION;
454 		result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
455 		return (MLRPC_DRC_OK);
456 	}
457 
458 	mbind->p_cont_id = p_cont_id;
459 	mbind->which_side = MLRPC_BIND_SIDE_SERVER;
460 	/* mbind->context set by app */
461 	mbind->service = msvc;
462 	mbind->instance_specific = 0;
463 	mxa->binding = mbind;
464 
465 	sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
466 	sec_addr->length = 0;
467 	bzero(sec_addr->port_spec, MLRPC_PORT_ANY_MAX_PORT_SPEC);
468 
469 	result->transfer_syntax =
470 	    cont_list->p_cont_elem[0].transfer_syntaxes[0];
471 
472 	return (MLRPC_DRC_BINDING_MADE);
473 }
474 
475 static int
476 mlrpc_s_request(struct mlrpc_xaction *mxa)
477 {
478 	struct mlrpc_binding	*mbind;
479 	struct mlrpc_service	*msvc;
480 	unsigned		p_cont_id;
481 	int			rc;
482 
483 	mxa->opnum = mxa->recv_hdr.request_hdr.opnum;
484 	p_cont_id = mxa->recv_hdr.request_hdr.p_cont_id;
485 
486 	if ((mbind = mlrpc_find_binding(mxa, p_cont_id)) == NULL)
487 		return (MLRPC_DRC_FAULT_REQUEST_PCONT_INVALID);
488 
489 	mxa->binding = mbind;
490 	msvc = mbind->service;
491 
492 	/*
493 	 * Make room for the response hdr.
494 	 */
495 	mxa->send_mlnds.pdu_scan_offset = MLRPC_RSP_HDR_SIZE;
496 
497 	if (msvc->call_stub)
498 		rc = (*msvc->call_stub)(mxa);
499 	else
500 		rc = mlrpc_generic_call_stub(mxa);
501 
502 	if (MLRPC_DRC_IS_FAULT(rc)) {
503 		mlndo_printf(0, 0, "%s[0x%02x]: 0x%04x",
504 		    msvc->name, mxa->opnum, rc);
505 	}
506 
507 	return (rc);
508 }
509 
510 /*
511  * The transaction and the two mlnds streams use the same heap, which
512  * should already exist at this point.  The heap will also be available
513  * to the stub.
514  */
515 int
516 mlrpc_generic_call_stub(struct mlrpc_xaction *mxa)
517 {
518 	struct mlrpc_binding 	*mbind = mxa->binding;
519 	struct mlrpc_service 	*msvc = mbind->service;
520 	struct ndr_typeinfo 	*intf_ti = msvc->interface_ti;
521 	struct mlrpc_stub_table *ste;
522 	int			opnum = mxa->opnum;
523 	unsigned		p_len = intf_ti->c_size_fixed_part;
524 	char 			*param;
525 	int			rc;
526 
527 	if (mxa->heap == NULL) {
528 		mlndo_printf(0, 0, "%s[0x%02x]: no heap", msvc->name, opnum);
529 		return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
530 	}
531 
532 	if ((ste = mlrpc_find_stub_in_svc(msvc, opnum)) == NULL) {
533 		mlndo_printf(0, 0, "%s[0x%02x]: invalid opnum",
534 		    msvc->name, opnum);
535 		return (MLRPC_DRC_FAULT_REQUEST_OPNUM_INVALID);
536 	}
537 
538 	if ((param = mlrpc_heap_malloc(mxa->heap, p_len)) == NULL)
539 		return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
540 
541 	bzero(param, p_len);
542 
543 	rc = mlrpc_decode_call(mxa, param);
544 	if (!MLRPC_DRC_IS_OK(rc))
545 		return (rc);
546 
547 	rc = (*ste->func)(param, mxa);
548 	if (rc == MLRPC_DRC_OK)
549 		rc = mlrpc_encode_return(mxa, param);
550 
551 	return (rc);
552 }
553 
554 /*
555  * We can perform some initial setup of the response header here.
556  * We also need to cache some of the information from the bind
557  * negotiation for use during subsequent RPC calls.
558  */
559 static void
560 mlrpc_reply_prepare_hdr(struct mlrpc_xaction *mxa)
561 {
562 	mlrpcconn_common_header_t *rhdr = &mxa->recv_hdr.common_hdr;
563 	mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr;
564 
565 	hdr->rpc_vers = 5;
566 	hdr->rpc_vers_minor = 0;
567 	hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG + MLRPC_PFC_LAST_FRAG;
568 	hdr->packed_drep = rhdr->packed_drep;
569 	hdr->frag_length = 0;
570 	hdr->auth_length = 0;
571 	hdr->call_id = rhdr->call_id;
572 #ifdef _BIG_ENDIAN
573 	hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII
574 	    | MLRPC_REPLAB_INTG_BIG_ENDIAN;
575 #else
576 	hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII
577 	    | MLRPC_REPLAB_INTG_LITTLE_ENDIAN;
578 #endif
579 
580 	switch (mxa->ptype) {
581 	case MLRPC_PTYPE_BIND:
582 		hdr->ptype = MLRPC_PTYPE_BIND_ACK;
583 		mxa->send_hdr.bind_ack_hdr.max_xmit_frag =
584 		    mxa->recv_hdr.bind_hdr.max_xmit_frag;
585 		mxa->send_hdr.bind_ack_hdr.max_recv_frag =
586 		    mxa->recv_hdr.bind_hdr.max_recv_frag;
587 		mxa->send_hdr.bind_ack_hdr.assoc_group_id =
588 		    mxa->recv_hdr.bind_hdr.assoc_group_id;
589 
590 		if (mxa->send_hdr.bind_ack_hdr.assoc_group_id == 0)
591 			mxa->send_hdr.bind_ack_hdr.assoc_group_id = time(0);
592 
593 		/*
594 		 * Save the maximum fragment sizes
595 		 * for use with subsequent requests.
596 		 */
597 		mxa->context->max_xmit_frag =
598 		    mxa->recv_hdr.bind_hdr.max_xmit_frag;
599 
600 		mxa->context->max_recv_frag =
601 		    mxa->recv_hdr.bind_hdr.max_recv_frag;
602 
603 		break;
604 
605 	case MLRPC_PTYPE_REQUEST:
606 		hdr->ptype = MLRPC_PTYPE_RESPONSE;
607 		/* mxa->send_hdr.response_hdr.alloc_hint */
608 		mxa->send_hdr.response_hdr.p_cont_id =
609 		    mxa->recv_hdr.request_hdr.p_cont_id;
610 		mxa->send_hdr.response_hdr.cancel_count = 0;
611 		mxa->send_hdr.response_hdr.reserved = 0;
612 		break;
613 
614 	case MLRPC_PTYPE_ALTER_CONTEXT:
615 		hdr->ptype = MLRPC_PTYPE_ALTER_CONTEXT_RESP;
616 		/*
617 		 * The max_xmit_frag, max_recv_frag
618 		 * and assoc_group_id are ignored.
619 		 */
620 		break;
621 
622 	default:
623 		hdr->ptype = 0xFF;
624 	}
625 }
626 
627 /*
628  * Finish and encode the bind acknowledge (MLRPC_PTYPE_BIND_ACK) header.
629  * The frag_length is different from a regular RPC response.
630  */
631 static void
632 mlrpc_reply_bind_ack(struct mlrpc_xaction *mxa)
633 {
634 	mlrpcconn_common_header_t	*hdr;
635 	mlrpcconn_bind_ack_hdr_t	*bahdr;
636 
637 	hdr = &mxa->send_hdr.common_hdr;
638 	bahdr = &mxa->send_hdr.bind_ack_hdr;
639 	hdr->frag_length = mlrpc_bind_ack_hdr_size(bahdr);
640 }
641 
642 /*
643  * Signal an RPC fault. The stream is reset and we overwrite whatever
644  * was in the response header with the fault information.
645  */
646 static void
647 mlrpc_reply_fault(struct mlrpc_xaction *mxa, unsigned long drc)
648 {
649 	mlrpcconn_common_header_t *rhdr = &mxa->recv_hdr.common_hdr;
650 	mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr;
651 	struct mlndr_stream *mlnds = &mxa->send_mlnds;
652 	unsigned long fault_status;
653 
654 	MLNDS_RESET(mlnds);
655 
656 	hdr->rpc_vers = 5;
657 	hdr->rpc_vers_minor = 0;
658 	hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG + MLRPC_PFC_LAST_FRAG;
659 	hdr->packed_drep = rhdr->packed_drep;
660 	hdr->frag_length = sizeof (mxa->send_hdr.fault_hdr);
661 	hdr->auth_length = 0;
662 	hdr->call_id = rhdr->call_id;
663 #ifdef _BIG_ENDIAN
664 	hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII
665 	    | MLRPC_REPLAB_INTG_BIG_ENDIAN;
666 #else
667 	hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII
668 	    | MLRPC_REPLAB_INTG_LITTLE_ENDIAN;
669 #endif
670 
671 	switch (drc & MLRPC_DRC_MASK_SPECIFIER) {
672 	case MLRPC_DRC_FAULT_OUT_OF_MEMORY:
673 	case MLRPC_DRC_FAULT_ENCODE_TOO_BIG:
674 		fault_status = MLRPC_FAULT_NCA_OUT_ARGS_TOO_BIG;
675 		break;
676 
677 	case MLRPC_DRC_FAULT_REQUEST_PCONT_INVALID:
678 		fault_status = MLRPC_FAULT_NCA_INVALID_PRES_CONTEXT_ID;
679 		break;
680 
681 	case MLRPC_DRC_FAULT_REQUEST_OPNUM_INVALID:
682 		fault_status = MLRPC_FAULT_NCA_OP_RNG_ERROR;
683 		break;
684 
685 	case MLRPC_DRC_FAULT_DECODE_FAILED:
686 	case MLRPC_DRC_FAULT_ENCODE_FAILED:
687 		fault_status = MLRPC_FAULT_NCA_PROTO_ERROR;
688 		break;
689 
690 	default:
691 		fault_status = MLRPC_FAULT_NCA_UNSPEC_REJECT;
692 		break;
693 	}
694 
695 	mxa->send_hdr.fault_hdr.common_hdr.ptype = MLRPC_PTYPE_FAULT;
696 	mxa->send_hdr.fault_hdr.status = fault_status;
697 	mxa->send_hdr.response_hdr.alloc_hint = hdr->frag_length;
698 }
699 
700 static int
701 mlrpc_build_reply(struct mlrpc_xaction *mxa)
702 {
703 	mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr;
704 	struct mlndr_stream *mlnds = &mxa->send_mlnds;
705 	uint8_t *pdu_buf;
706 	unsigned long pdu_size;
707 	unsigned long frag_size;
708 	unsigned long pdu_data_size;
709 	unsigned long frag_data_size;
710 
711 	frag_size = mlrpc_frag_size;
712 	pdu_size = mlnds->pdu_size;
713 	pdu_buf = mlnds->pdu_base_addr;
714 
715 	if (pdu_size <= frag_size) {
716 		/*
717 		 * Single fragment response. The PDU size may be zero
718 		 * here (i.e. bind or fault response). So don't make
719 		 * any assumptions about it until after the header is
720 		 * encoded.
721 		 */
722 		switch (hdr->ptype) {
723 		case MLRPC_PTYPE_BIND_ACK:
724 			mlrpc_reply_bind_ack(mxa);
725 			break;
726 
727 		case MLRPC_PTYPE_FAULT:
728 			/* already setup */
729 			break;
730 
731 		case MLRPC_PTYPE_RESPONSE:
732 			hdr->frag_length = pdu_size;
733 			mxa->send_hdr.response_hdr.alloc_hint =
734 			    hdr->frag_length;
735 			break;
736 
737 		default:
738 			hdr->frag_length = pdu_size;
739 			break;
740 		}
741 
742 		mlnds->pdu_scan_offset = 0;
743 		(void) mlrpc_encode_pdu_hdr(mxa);
744 		pdu_size = mlnds->pdu_size;
745 		mlrpc_build_frag(mlnds, pdu_buf,  pdu_size);
746 		return (0);
747 	}
748 
749 	/*
750 	 * Multiple fragment response.
751 	 */
752 	hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG;
753 	hdr->frag_length = frag_size;
754 	mxa->send_hdr.response_hdr.alloc_hint = pdu_size - MLRPC_RSP_HDR_SIZE;
755 	mlnds->pdu_scan_offset = 0;
756 	(void) mlrpc_encode_pdu_hdr(mxa);
757 	mlrpc_build_frag(mlnds, pdu_buf,  frag_size);
758 
759 	/*
760 	 * We need to update the 24-byte header in subsequent fragments.
761 	 *
762 	 * pdu_data_size:	total data remaining to be handled
763 	 * frag_size:		total fragment size including header
764 	 * frag_data_size:	data in fragment
765 	 *			(i.e. frag_size - MLRPC_RSP_HDR_SIZE)
766 	 */
767 	pdu_data_size = pdu_size - MLRPC_RSP_HDR_SIZE;
768 	frag_data_size = frag_size - MLRPC_RSP_HDR_SIZE;
769 
770 	while (pdu_data_size) {
771 		mxa->send_hdr.response_hdr.alloc_hint -= frag_data_size;
772 		pdu_data_size -= frag_data_size;
773 		pdu_buf += frag_data_size;
774 
775 		if (pdu_data_size <= frag_data_size) {
776 			frag_data_size = pdu_data_size;
777 			frag_size = frag_data_size + MLRPC_RSP_HDR_SIZE;
778 			hdr->pfc_flags = MLRPC_PFC_LAST_FRAG;
779 		} else {
780 			hdr->pfc_flags = 0;
781 		}
782 
783 		hdr->frag_length = frag_size;
784 		mlnds->pdu_scan_offset = 0;
785 		(void) mlrpc_encode_pdu_hdr(mxa);
786 		bcopy(mlnds->pdu_base_addr, pdu_buf, MLRPC_RSP_HDR_SIZE);
787 
788 		mlrpc_build_frag(mlnds, pdu_buf, frag_size);
789 
790 		if (hdr->pfc_flags & MLRPC_PFC_LAST_FRAG)
791 			break;
792 	}
793 
794 	return (0);
795 }
796 
797 /*
798  * mlrpc_build_frag
799  *
800  * Build an RPC PDU fragment from the specified buffer.
801  * If malloc fails, the client will see a header/pdu inconsistency
802  * and report an error.
803  */
804 static void
805 mlrpc_build_frag(struct mlndr_stream *mlnds, uint8_t *buf, uint32_t len)
806 {
807 	ndr_frag_t *frag;
808 	int size = sizeof (ndr_frag_t) + len;
809 
810 	if ((frag = (ndr_frag_t *)malloc(size)) == NULL)
811 		return;
812 
813 	frag->next = NULL;
814 	frag->buf = (uint8_t *)frag + sizeof (ndr_frag_t);
815 	frag->len = len;
816 	bcopy(buf, frag->buf, len);
817 
818 	if (mlnds->head == NULL) {
819 		mlnds->head = frag;
820 		mlnds->tail = frag;
821 		mlnds->nfrag = 1;
822 	} else {
823 		mlnds->tail->next = frag;
824 		mlnds->tail = frag;
825 		++mlnds->nfrag;
826 	}
827 }
828