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.
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.
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]
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
28 #include <sys/errno.h>
34 #define NDR_DEFAULT_FRAGSZ 8192
35 #define NDR_MULTI_FRAGSZ (60 * 1024)
37 static void ndr_clnt_init_hdr(ndr_client_t
*, ndr_xa_t
*);
38 static int ndr_clnt_get_frags(ndr_client_t
*, ndr_xa_t
*);
39 static int ndr_clnt_get_frag(ndr_client_t
*, ndr_xa_t
*, ndr_common_header_t
*);
42 ndr_clnt_bind(ndr_client_t
*clnt
, ndr_service_t
*msvc
,
43 ndr_binding_t
**ret_binding_p
)
48 ndr_p_cont_elem_t
*pce
;
49 ndr_bind_ack_hdr_t
*bahdr
;
53 bzero(&mxa
, sizeof (mxa
));
55 mxa
.binding_list
= clnt
->binding_list
;
56 if ((mbind
= ndr_svc_new_binding(&mxa
)) == NULL
)
57 return (NDR_DRC_FAULT_API_BIND_NO_SLOTS
);
59 ndr_clnt_init_hdr(clnt
, &mxa
);
61 bhdr
= &mxa
.send_hdr
.bind_hdr
;
62 bhdr
->common_hdr
.ptype
= NDR_PTYPE_BIND
;
63 bhdr
->common_hdr
.frag_length
= sizeof (*bhdr
);
64 bhdr
->max_xmit_frag
= NDR_DEFAULT_FRAGSZ
;
65 bhdr
->max_recv_frag
= NDR_DEFAULT_FRAGSZ
;
66 bhdr
->assoc_group_id
= 0;
67 bhdr
->p_context_elem
.n_context_elem
= 1;
69 /* Assign presentation context id */
70 pce
= &bhdr
->p_context_elem
.p_cont_elem
[0];
71 pce
->p_cont_id
= clnt
->next_p_cont_id
++;
72 pce
->n_transfer_syn
= 1;
74 /* Set up UUIDs and versions from the service */
75 pce
->abstract_syntax
.if_version
= msvc
->abstract_syntax_version
;
76 rc
= ndr_uuid_parse(msvc
->abstract_syntax_uuid
,
77 &pce
->abstract_syntax
.if_uuid
);
79 return (NDR_DRC_FAULT_API_SERVICE_INVALID
);
81 pce
->transfer_syntaxes
[0].if_version
= msvc
->transfer_syntax_version
;
82 rc
= ndr_uuid_parse(msvc
->transfer_syntax_uuid
,
83 &pce
->transfer_syntaxes
[0].if_uuid
);
85 return (NDR_DRC_FAULT_API_SERVICE_INVALID
);
87 /* Format and exchange the PDU */
89 if ((*clnt
->xa_init
)(clnt
, &mxa
) < 0)
90 return (NDR_DRC_FAULT_OUT_OF_MEMORY
);
92 rc
= ndr_encode_pdu_hdr(&mxa
);
93 if (NDR_DRC_IS_FAULT(rc
))
96 if ((*clnt
->xa_exchange
)(clnt
, &mxa
) < 0) {
97 rc
= NDR_DRC_FAULT_SEND_FAILED
;
101 rc
= ndr_decode_pdu_hdr(&mxa
);
102 if (NDR_DRC_IS_FAULT(rc
))
105 /* done with buffers */
106 (*clnt
->xa_destruct
)(clnt
, &mxa
);
108 bahdr
= &mxa
.recv_hdr
.bind_ack_hdr
;
110 if (mxa
.ptype
!= NDR_PTYPE_BIND_ACK
)
111 return (NDR_DRC_FAULT_RECEIVED_MALFORMED
);
113 if (bahdr
->p_result_list
.n_results
!= 1)
114 return (NDR_DRC_FAULT_RECEIVED_MALFORMED
);
116 pre
= &bahdr
->p_result_list
.p_results
[0];
118 if (pre
->result
!= NDR_PCDR_ACCEPTANCE
)
119 return (NDR_DRC_FAULT_RECEIVED_MALFORMED
);
121 mbind
->p_cont_id
= pce
->p_cont_id
;
122 mbind
->which_side
= NDR_BIND_SIDE_CLIENT
;
124 mbind
->service
= msvc
;
125 mbind
->instance_specific
= 0;
127 *ret_binding_p
= mbind
;
131 (*clnt
->xa_destruct
)(clnt
, &mxa
);
136 ndr_clnt_call(ndr_binding_t
*mbind
, int opnum
, void *params
)
138 ndr_client_t
*clnt
= mbind
->clnt
;
140 ndr_request_hdr_t
*reqhdr
;
141 ndr_common_header_t
*rsphdr
;
142 unsigned long recv_pdu_scan_offset
;
145 bzero(&mxa
, sizeof (mxa
));
146 mxa
.ptype
= NDR_PTYPE_REQUEST
;
150 ndr_clnt_init_hdr(clnt
, &mxa
);
152 reqhdr
= &mxa
.send_hdr
.request_hdr
;
153 reqhdr
->common_hdr
.ptype
= NDR_PTYPE_REQUEST
;
154 reqhdr
->p_cont_id
= mbind
->p_cont_id
;
155 reqhdr
->opnum
= opnum
;
157 rc
= (*clnt
->xa_init
)(clnt
, &mxa
);
158 if (NDR_DRC_IS_FAULT(rc
))
161 /* Reserve room for hdr */
162 mxa
.send_nds
.pdu_scan_offset
= sizeof (*reqhdr
);
164 rc
= ndr_encode_call(&mxa
, params
);
165 if (!NDR_DRC_IS_OK(rc
))
168 mxa
.send_nds
.pdu_scan_offset
= 0;
171 * Now we have the PDU size, we need to set up the
172 * frag_length and calculate the alloc_hint.
174 mxa
.send_hdr
.common_hdr
.frag_length
= mxa
.send_nds
.pdu_size
;
175 reqhdr
->alloc_hint
= mxa
.send_nds
.pdu_size
-
176 sizeof (ndr_request_hdr_t
);
178 rc
= ndr_encode_pdu_hdr(&mxa
);
179 if (NDR_DRC_IS_FAULT(rc
))
182 rc
= (*clnt
->xa_exchange
)(clnt
, &mxa
);
183 if (NDR_DRC_IS_FAULT(rc
))
186 rc
= ndr_decode_pdu_hdr(&mxa
);
187 if (NDR_DRC_IS_FAULT(rc
))
190 if (mxa
.ptype
!= NDR_PTYPE_RESPONSE
) {
191 rc
= NDR_DRC_FAULT_RECEIVED_MALFORMED
;
195 rsphdr
= &mxa
.recv_hdr
.common_hdr
;
197 if (!NDR_IS_LAST_FRAG(rsphdr
->pfc_flags
)) {
199 * This is a multi-fragment response.
200 * Preserve the current scan offset while getting
201 * fragments so that we can continue afterward
202 * as if we had received the entire response as
205 (void) NDS_GROW_PDU(&mxa
.recv_nds
, NDR_MULTI_FRAGSZ
, NULL
);
207 recv_pdu_scan_offset
= mxa
.recv_nds
.pdu_scan_offset
;
208 mxa
.recv_nds
.pdu_scan_offset
= rsphdr
->frag_length
;
209 mxa
.recv_nds
.pdu_size
= rsphdr
->frag_length
;
211 if (ndr_clnt_get_frags(clnt
, &mxa
) < 0) {
212 rc
= NDR_DRC_FAULT_RECEIVED_MALFORMED
;
216 mxa
.recv_nds
.pdu_scan_offset
= recv_pdu_scan_offset
;
219 rc
= ndr_decode_return(&mxa
, params
);
220 if (NDR_DRC_IS_FAULT(rc
))
223 (*clnt
->xa_preserve
)(clnt
, &mxa
);
224 (*clnt
->xa_destruct
)(clnt
, &mxa
);
228 (*clnt
->xa_destruct
)(clnt
, &mxa
);
233 ndr_clnt_free_heap(ndr_client_t
*clnt
)
235 (*clnt
->xa_release
)(clnt
);
239 ndr_clnt_init_hdr(ndr_client_t
*clnt
, ndr_xa_t
*mxa
)
241 ndr_common_header_t
*hdr
= &mxa
->send_hdr
.common_hdr
;
244 hdr
->rpc_vers_minor
= 0;
245 hdr
->pfc_flags
= NDR_PFC_FIRST_FRAG
+ NDR_PFC_LAST_FRAG
;
246 hdr
->packed_drep
.intg_char_rep
= NDR_REPLAB_CHAR_ASCII
;
248 hdr
->packed_drep
.intg_char_rep
|= NDR_REPLAB_INTG_LITTLE_ENDIAN
;
250 /* hdr->frag_length */
251 hdr
->auth_length
= 0;
252 hdr
->call_id
= clnt
->next_call_id
++;
258 * A DCE RPC message that is larger than a single fragment is transmitted
259 * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for
260 * both Windows 2000 and 2003.
262 * Collect RPC fragments and append them to the receive stream buffer.
263 * Each received fragment has a header, which we need to remove as we
264 * build the full RPC PDU. The scan offset is used to track frag headers.
267 ndr_clnt_get_frags(ndr_client_t
*clnt
, ndr_xa_t
*mxa
)
269 ndr_stream_t
*nds
= &mxa
->recv_nds
;
270 ndr_common_header_t hdr
;
275 if (ndr_clnt_get_frag(clnt
, mxa
, &hdr
) < 0) {
280 last_frag
= NDR_IS_LAST_FRAG(hdr
.pfc_flags
);
281 frag_size
= hdr
.frag_length
;
283 if (frag_size
> (nds
->pdu_size
- nds
->pdu_scan_offset
)) {
288 ndr_remove_frag_hdr(nds
);
289 nds
->pdu_scan_offset
+= frag_size
- NDR_RSP_HDR_SIZE
;
290 } while (!last_frag
);
296 * Read the next RPC fragment. The xa_read() calls correspond to SmbReadX
297 * requests. Note that there is no correspondence between SmbReadX buffering
298 * and DCE RPC fragment alignment.
301 ndr_clnt_get_frag(ndr_client_t
*clnt
, ndr_xa_t
*mxa
, ndr_common_header_t
*hdr
)
303 ndr_stream_t
*nds
= &mxa
->recv_nds
;
304 unsigned long available
;
307 available
= nds
->pdu_size
- nds
->pdu_scan_offset
;
309 while (available
< NDR_RSP_HDR_SIZE
) {
310 if ((nbytes
+= (*clnt
->xa_read
)(clnt
, mxa
)) <= 0)
315 ndr_decode_frag_hdr(nds
, hdr
);
318 while (available
< hdr
->frag_length
) {
319 if ((nbytes
= (*clnt
->xa_read
)(clnt
, mxa
)) <= 0)