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