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