xref: /illumos-gate/usr/src/lib/libmlrpc/common/ndr_client.c (revision 9b40c3052b9b0d91120c568df0c5211c131c8da1)
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 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
26  */
27 
28 #include <sys/errno.h>
29 #include <string.h>
30 #include <strings.h>
31 
32 #include <libmlrpc.h>
33 
34 #define	NDR_DEFAULT_FRAGSZ	8192
35 #define	NDR_MULTI_FRAGSZ	(60 * 1024)
36 
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 *);
40 
41 int
42 ndr_clnt_bind(ndr_client_t *clnt, ndr_service_t *msvc,
43     ndr_binding_t **ret_binding_p)
44 {
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;
52 
53 	bzero(&mxa, sizeof (mxa));
54 
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);
58 
59 	ndr_clnt_init_hdr(clnt, &mxa);
60 
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;
68 
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;
73 
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);
80 
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);
86 
87 	/* Format and exchange the PDU */
88 
89 	if ((*clnt->xa_init)(clnt, &mxa) < 0)
90 		return (NDR_DRC_FAULT_OUT_OF_MEMORY);
91 
92 	rc = ndr_encode_pdu_hdr(&mxa);
93 	if (NDR_DRC_IS_FAULT(rc))
94 		goto fault_exit;
95 
96 	if ((*clnt->xa_exchange)(clnt, &mxa) < 0) {
97 		rc = NDR_DRC_FAULT_SEND_FAILED;
98 		goto fault_exit;
99 	}
100 
101 	rc = ndr_decode_pdu_hdr(&mxa);
102 	if (NDR_DRC_IS_FAULT(rc))
103 		goto fault_exit;
104 
105 	/* done with buffers */
106 	(*clnt->xa_destruct)(clnt, &mxa);
107 
108 	bahdr = &mxa.recv_hdr.bind_ack_hdr;
109 
110 	if (mxa.ptype != NDR_PTYPE_BIND_ACK)
111 		return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
112 
113 	if (bahdr->p_result_list.n_results != 1)
114 		return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
115 
116 	pre = &bahdr->p_result_list.p_results[0];
117 
118 	if (pre->result != NDR_PCDR_ACCEPTANCE)
119 		return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
120 
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;
126 
127 	*ret_binding_p = mbind;
128 	return (NDR_DRC_OK);
129 
130 fault_exit:
131 	(*clnt->xa_destruct)(clnt, &mxa);
132 	return (rc);
133 }
134 
135 int
136 ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params)
137 {
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;
144 
145 	bzero(&mxa, sizeof (mxa));
146 	mxa.ptype = NDR_PTYPE_REQUEST;
147 	mxa.opnum = opnum;
148 	mxa.binding = mbind;
149 
150 	ndr_clnt_init_hdr(clnt, &mxa);
151 
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;
156 
157 	rc = (*clnt->xa_init)(clnt, &mxa);
158 	if (NDR_DRC_IS_FAULT(rc))
159 		return (rc);
160 
161 	/* Reserve room for hdr */
162 	mxa.send_nds.pdu_scan_offset = sizeof (*reqhdr);
163 
164 	rc = ndr_encode_call(&mxa, params);
165 	if (!NDR_DRC_IS_OK(rc))
166 		goto fault_exit;
167 
168 	mxa.send_nds.pdu_scan_offset = 0;
169 
170 	/*
171 	 * Now we have the PDU size, we need to set up the
172 	 * frag_length and calculate the alloc_hint.
173 	 */
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);
177 
178 	rc = ndr_encode_pdu_hdr(&mxa);
179 	if (NDR_DRC_IS_FAULT(rc))
180 		goto fault_exit;
181 
182 	rc = (*clnt->xa_exchange)(clnt, &mxa);
183 	if (NDR_DRC_IS_FAULT(rc))
184 		goto fault_exit;
185 
186 	rc = ndr_decode_pdu_hdr(&mxa);
187 	if (NDR_DRC_IS_FAULT(rc))
188 		goto fault_exit;
189 
190 	if (mxa.ptype != NDR_PTYPE_RESPONSE) {
191 		rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
192 		goto fault_exit;
193 	}
194 
195 	rsphdr = &mxa.recv_hdr.common_hdr;
196 
197 	if (!NDR_IS_LAST_FRAG(rsphdr->pfc_flags)) {
198 		/*
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.
204 		 */
205 		(void) NDS_GROW_PDU(&mxa.recv_nds, NDR_MULTI_FRAGSZ, NULL);
206 
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;
210 
211 		if (ndr_clnt_get_frags(clnt, &mxa) < 0) {
212 			rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
213 			goto fault_exit;
214 		}
215 
216 		mxa.recv_nds.pdu_scan_offset = recv_pdu_scan_offset;
217 	}
218 
219 	rc = ndr_decode_return(&mxa, params);
220 	if (NDR_DRC_IS_FAULT(rc))
221 		goto fault_exit;
222 
223 	(*clnt->xa_preserve)(clnt, &mxa);
224 	(*clnt->xa_destruct)(clnt, &mxa);
225 	return (NDR_DRC_OK);
226 
227 fault_exit:
228 	(*clnt->xa_destruct)(clnt, &mxa);
229 	return (rc);
230 }
231 
232 void
233 ndr_clnt_free_heap(ndr_client_t *clnt)
234 {
235 	(*clnt->xa_release)(clnt);
236 }
237 
238 static void
239 ndr_clnt_init_hdr(ndr_client_t *clnt, ndr_xa_t *mxa)
240 {
241 	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
242 
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++;
253 }
254 
255 /*
256  * ndr_clnt_get_frags
257  *
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.
261  *
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.
265  */
266 static int
267 ndr_clnt_get_frags(ndr_client_t *clnt, ndr_xa_t *mxa)
268 {
269 	ndr_stream_t *nds = &mxa->recv_nds;
270 	ndr_common_header_t hdr;
271 	int frag_size;
272 	int last_frag;
273 
274 	do {
275 		if (ndr_clnt_get_frag(clnt, mxa, &hdr) < 0) {
276 			nds_show_state(nds);
277 			return (-1);
278 		}
279 
280 		last_frag = NDR_IS_LAST_FRAG(hdr.pfc_flags);
281 		frag_size = hdr.frag_length;
282 
283 		if (frag_size > (nds->pdu_size - nds->pdu_scan_offset)) {
284 			nds_show_state(nds);
285 			return (-1);
286 		}
287 
288 		ndr_remove_frag_hdr(nds);
289 		nds->pdu_scan_offset += frag_size - NDR_RSP_HDR_SIZE;
290 	} while (!last_frag);
291 
292 	return (0);
293 }
294 
295 /*
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.
299  */
300 static int
301 ndr_clnt_get_frag(ndr_client_t *clnt, ndr_xa_t *mxa, ndr_common_header_t *hdr)
302 {
303 	ndr_stream_t		*nds = &mxa->recv_nds;
304 	unsigned long		available;
305 	int			nbytes = 0;
306 
307 	available = nds->pdu_size - nds->pdu_scan_offset;
308 
309 	while (available < NDR_RSP_HDR_SIZE) {
310 		if ((nbytes += (*clnt->xa_read)(clnt, mxa)) <= 0)
311 			return (-1);
312 		available += nbytes;
313 	}
314 
315 	ndr_decode_frag_hdr(nds, hdr);
316 	ndr_show_hdr(hdr);
317 
318 	while (available < hdr->frag_length) {
319 		if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0)
320 			return (-1);
321 		available += nbytes;
322 	}
323 
324 	return (nbytes);
325 }
326