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