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