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
37static void ndr_clnt_init_hdr(ndr_client_t *, ndr_xa_t *);
38static int ndr_clnt_get_frags(ndr_client_t *, ndr_xa_t *);
39static int ndr_clnt_get_frag(ndr_client_t *, ndr_xa_t *, ndr_common_header_t *);
40
41int
42ndr_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
130fault_exit:
131	(*clnt->xa_destruct)(clnt, &mxa);
132	return (rc);
133}
134
135int
136ndr_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
227fault_exit:
228	(*clnt->xa_destruct)(clnt, &mxa);
229	return (rc);
230}
231
232void
233ndr_clnt_free_heap(ndr_client_t *clnt)
234{
235	(*clnt->xa_release)(clnt);
236}
237
238static void
239ndr_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 */
266static int
267ndr_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 */
300static int
301ndr_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