Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / lib / libmlrpc / common / ndr_client.c
blob56cc1847efaad7aad7581fdf18bb700805d2f9f0
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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>
29 #include <string.h>
30 #include <strings.h>
32 #include <libmlrpc.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 *);
41 int
42 ndr_clnt_bind(ndr_client_t *clnt, ndr_service_t *msvc,
43 ndr_binding_t **ret_binding_p)
45 ndr_binding_t *mbind;
46 ndr_xa_t mxa;
47 ndr_bind_hdr_t *bhdr;
48 ndr_p_cont_elem_t *pce;
49 ndr_bind_ack_hdr_t *bahdr;
50 ndr_p_result_t *pre;
51 int rc;
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);
78 if (rc != 0)
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);
84 if (rc != 0)
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))
94 goto fault_exit;
96 if ((*clnt->xa_exchange)(clnt, &mxa) < 0) {
97 rc = NDR_DRC_FAULT_SEND_FAILED;
98 goto fault_exit;
101 rc = ndr_decode_pdu_hdr(&mxa);
102 if (NDR_DRC_IS_FAULT(rc))
103 goto fault_exit;
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;
123 mbind->clnt = clnt;
124 mbind->service = msvc;
125 mbind->instance_specific = 0;
127 *ret_binding_p = mbind;
128 return (NDR_DRC_OK);
130 fault_exit:
131 (*clnt->xa_destruct)(clnt, &mxa);
132 return (rc);
136 ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params)
138 ndr_client_t *clnt = mbind->clnt;
139 ndr_xa_t mxa;
140 ndr_request_hdr_t *reqhdr;
141 ndr_common_header_t *rsphdr;
142 unsigned long recv_pdu_scan_offset;
143 int rc;
145 bzero(&mxa, sizeof (mxa));
146 mxa.ptype = NDR_PTYPE_REQUEST;
147 mxa.opnum = opnum;
148 mxa.binding = mbind;
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))
159 return (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))
166 goto fault_exit;
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))
180 goto fault_exit;
182 rc = (*clnt->xa_exchange)(clnt, &mxa);
183 if (NDR_DRC_IS_FAULT(rc))
184 goto fault_exit;
186 rc = ndr_decode_pdu_hdr(&mxa);
187 if (NDR_DRC_IS_FAULT(rc))
188 goto fault_exit;
190 if (mxa.ptype != NDR_PTYPE_RESPONSE) {
191 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
192 goto fault_exit;
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
203 * a single PDU.
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;
213 goto fault_exit;
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))
221 goto fault_exit;
223 (*clnt->xa_preserve)(clnt, &mxa);
224 (*clnt->xa_destruct)(clnt, &mxa);
225 return (NDR_DRC_OK);
227 fault_exit:
228 (*clnt->xa_destruct)(clnt, &mxa);
229 return (rc);
232 void
233 ndr_clnt_free_heap(ndr_client_t *clnt)
235 (*clnt->xa_release)(clnt);
238 static void
239 ndr_clnt_init_hdr(ndr_client_t *clnt, ndr_xa_t *mxa)
241 ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
243 hdr->rpc_vers = 5;
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;
247 #ifndef _BIG_ENDIAN
248 hdr->packed_drep.intg_char_rep |= NDR_REPLAB_INTG_LITTLE_ENDIAN;
249 #endif
250 /* hdr->frag_length */
251 hdr->auth_length = 0;
252 hdr->call_id = clnt->next_call_id++;
256 * ndr_clnt_get_frags
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.
266 static int
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;
271 int frag_size;
272 int last_frag;
274 do {
275 if (ndr_clnt_get_frag(clnt, mxa, &hdr) < 0) {
276 nds_show_state(nds);
277 return (-1);
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)) {
284 nds_show_state(nds);
285 return (-1);
288 ndr_remove_frag_hdr(nds);
289 nds->pdu_scan_offset += frag_size - NDR_RSP_HDR_SIZE;
290 } while (!last_frag);
292 return (0);
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.
300 static int
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;
305 int nbytes = 0;
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)
311 return (-1);
312 available += nbytes;
315 ndr_decode_frag_hdr(nds, hdr);
316 ndr_show_hdr(hdr);
318 while (available < hdr->frag_length) {
319 if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0)
320 return (-1);
321 available += nbytes;
324 return (nbytes);