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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Context functions to support the RPC interface library.
30  */
31 
32 #include <sys/errno.h>
33 #include <strings.h>
34 
35 #include <smbsrv/libsmb.h>
36 #include <smbsrv/libsmbrdr.h>
37 #include <smbsrv/ndr.h>
38 #include <smbsrv/mlrpc.h>
39 #include <smbsrv/mlsvc_util.h>
40 
41 static int mlsvc_xa_init(struct mlrpc_client *, struct mlrpc_xaction *,
42     mlrpc_heap_t *);
43 static int mlsvc_xa_exchange(struct mlrpc_client *, struct mlrpc_xaction *);
44 static int mlsvc_xa_read(struct mlrpc_client *, struct mlrpc_xaction *);
45 static int mlsvc_xa_preserve(struct mlrpc_client *, struct mlrpc_xaction *,
46     mlrpc_heapref_t *);
47 static int mlsvc_xa_destruct(struct mlrpc_client *, struct mlrpc_xaction *);
48 static void mlsvc_xa_release(struct mlrpc_client *, mlrpc_heapref_t *heapref);
49 
50 /*
51  * mlsvc_rpc_bind
52  *
53  * This the entry point for all client RPC services. This call must be
54  * made to initialize an RPC context structure and bind to the remote
55  * service before any RPCs can be exchanged with that service. The
56  * descriptor is a wrapper that is used to associate an RPC handle with
57  * the context data for that specific instance of the interface. The
58  * handle is zeroed to ensure that it doesn't look like a valid handle.
59  * The context handle is assigned to point at the RPC handle so that we
60  * know when to free the context. As each handle is initialized it will
61  * include a pointer to this context but only when we close this initial
62  * RPC handle can the context be freed.
63  *
64  * On success, return a pointer to the descriptor. Otherwise return a
65  * null pointer.
66  */
67 int
68 mlsvc_rpc_bind(mlsvc_handle_t *desc, int fid, char *service)
69 {
70 	struct mlsvc_rpc_context *context;
71 	int rc;
72 
73 	bzero(&desc->handle, sizeof (ms_handle_t));
74 
75 	context = malloc(sizeof (struct mlsvc_rpc_context));
76 	if ((desc->context = context) == NULL)
77 		return (-1);
78 
79 	bzero(context, sizeof (struct mlsvc_rpc_context));
80 	context->cli.context = context;
81 
82 	mlrpc_binding_pool_initialize(&context->cli.binding_list,
83 	    context->binding_pool, CTXT_N_BINDING_POOL);
84 
85 	context->fid = fid;
86 	context->handle = &desc->handle;
87 	context->cli.xa_init = mlsvc_xa_init;
88 	context->cli.xa_exchange = mlsvc_xa_exchange;
89 	context->cli.xa_read = mlsvc_xa_read;
90 	context->cli.xa_preserve = mlsvc_xa_preserve;
91 	context->cli.xa_destruct = mlsvc_xa_destruct;
92 	context->cli.xa_release = mlsvc_xa_release;
93 
94 	rc = mlrpc_c_bind(&context->cli, service, &context->binding);
95 	if (MLRPC_DRC_IS_FAULT(rc)) {
96 		free(context);
97 		desc->context = NULL;
98 		return (-1);
99 	}
100 
101 	return (rc);
102 }
103 
104 /*
105  * mlsvc_rpc_init
106  *
107  * This function must be called by client side applications before
108  * calling mlsvc_rpc_call to allocate a heap. The heap must be
109  * destroyed by either calling mlrpc_heap_destroy or mlsvc_rpc_free.
110  * Use mlrpc_heap_destroy if mlsvc_rpc_call has not yet been called.
111  * Otherwise use mlsvc_rpc_free.
112  *
113  * Returns 0 on success. Otherwise returns -1 to indicate an error.
114  */
115 int
116 mlsvc_rpc_init(mlrpc_heapref_t *heapref)
117 {
118 	bzero(heapref, sizeof (mlrpc_heapref_t));
119 
120 	if ((heapref->heap = mlrpc_heap_create()) == NULL)
121 		return (-1);
122 
123 	return (0);
124 }
125 
126 /*
127  * mlsvc_rpc_call
128  *
129  * This function should be called by the client RPC interface functions
130  * to make an RPC call. The remote service is identified by the context
131  * handle, which should have been initialized with by mlsvc_rpc_bind.
132  */
133 int
134 mlsvc_rpc_call(struct mlsvc_rpc_context *context, int opnum, void *params,
135     mlrpc_heapref_t *heapref)
136 {
137 	return (mlrpc_c_call(context->binding, opnum, params, heapref));
138 }
139 
140 /*
141  * mlsvc_rpc_free
142  *
143  * This function should be called by the client RPC interface functions
144  * to free the heap after an RPC call returns.
145  */
146 void
147 mlsvc_rpc_free(struct mlsvc_rpc_context *context, mlrpc_heapref_t *heapref)
148 {
149 	mlrpc_c_free_heap(context->binding, heapref);
150 }
151 
152 /*
153  * The following functions provide the callback interface in the
154  * context handle.
155  */
156 /*ARGSUSED*/
157 static int
158 mlsvc_xa_init(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa,
159     mlrpc_heap_t *heap)
160 {
161 	struct mlndr_stream *recv_mlnds = &mxa->recv_mlnds;
162 	struct mlndr_stream *send_mlnds = &mxa->send_mlnds;
163 
164 	/*
165 	 * If the caller hasn't provided a heap, create one here.
166 	 */
167 	if (heap == 0) {
168 		if ((heap = mlrpc_heap_create()) == 0)
169 			return (-1);
170 	}
171 
172 	mxa->heap = heap;
173 
174 	mlnds_initialize(send_mlnds, 0, NDR_MODE_CALL_SEND, heap);
175 	mlnds_initialize(recv_mlnds, 16 * 1024, NDR_MODE_RETURN_RECV, heap);
176 	return (0);
177 }
178 
179 /*
180  * mlsvc_xa_exchange
181  *
182  * This is the entry pointy for an RPC client call exchange with
183  * a server, which will result in an smbrdr SmbTransact request.
184  *
185  * SmbTransact should return the number of bytes received, which
186  * we record as the PDU size, or a negative error code.
187  */
188 static int
189 mlsvc_xa_exchange(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa)
190 {
191 	struct mlsvc_rpc_context *context = mcli->context;
192 	struct mlndr_stream *recv_mlnds = &mxa->recv_mlnds;
193 	struct mlndr_stream *send_mlnds = &mxa->send_mlnds;
194 	int rc;
195 
196 	rc = smbrdr_transact(context->fid,
197 	    (char *)send_mlnds->pdu_base_offset, send_mlnds->pdu_size,
198 	    (char *)recv_mlnds->pdu_base_offset, recv_mlnds->pdu_max_size);
199 
200 	if (rc < 0)
201 		recv_mlnds->pdu_size = 0;
202 	else
203 		recv_mlnds->pdu_size = rc;
204 
205 	return (rc);
206 }
207 
208 /*
209  * mlsvc_xa_read
210  *
211  * This entry point will be invoked if the xa-exchange response contained
212  * only the first fragment of a multi-fragment response.  The RPC client
213  * code will then make repeated xa-read requests to obtain the remaining
214  * fragments, which will result in smbrdr SmbReadX requests.
215  *
216  * SmbReadX should return the number of bytes received, in which case we
217  * expand the PDU size to include the received data, or a negative error
218  * code.
219  */
220 static int
221 mlsvc_xa_read(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa)
222 {
223 	struct mlsvc_rpc_context *context = mcli->context;
224 	struct mlndr_stream *mlnds = &mxa->recv_mlnds;
225 	int len;
226 	int rc;
227 
228 	if ((len = (mlnds->pdu_max_size - mlnds->pdu_size)) < 0)
229 		return (-1);
230 
231 	rc = smbrdr_readx(context->fid,
232 	    (char *)mlnds->pdu_base_offset + mlnds->pdu_size, len);
233 
234 	if (rc < 0)
235 		return (-1);
236 
237 	mlnds->pdu_size += rc;
238 
239 	if (mlnds->pdu_size > mlnds->pdu_max_size) {
240 		mlnds->pdu_size = mlnds->pdu_max_size;
241 		return (-1);
242 	}
243 
244 	return (rc);
245 }
246 
247 /*
248  * mlsvc_xa_preserve
249  *
250  * This function is called to preserve the heap. We save a reference
251  * to the heap and set the mxa heap pointer to null so that the heap
252  * will not be discarded when mlsvc_xa_destruct is called.
253  */
254 /*ARGSUSED*/
255 static int
256 mlsvc_xa_preserve(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa,
257     mlrpc_heapref_t *heapref)
258 {
259 	heapref->state = MLRPC_HRST_PRESERVED;
260 	heapref->heap = mxa->heap;
261 	heapref->recv_pdu_buf = (char *)mxa->recv_mlnds.pdu_base_addr;
262 	heapref->send_pdu_buf = (char *)mxa->send_mlnds.pdu_base_addr;
263 
264 	mxa->heap = NULL;
265 	return (0);
266 }
267 
268 /*
269  * mlsvc_xa_destruct
270  *
271  * This function is called to dispose of the heap. If the heap has
272  * been preserved via mlsvc_xa_preserve, the mxa heap pointer will
273  * be null and we assume that the heap will be released later via
274  * a call to mlsvc_xa_release. Otherwise we free the memory here.
275  */
276 /*ARGSUSED*/
277 static int
278 mlsvc_xa_destruct(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa)
279 {
280 	if (mxa->heap) {
281 		mlnds_destruct(&mxa->recv_mlnds);
282 		mlnds_destruct(&mxa->send_mlnds);
283 		mlrpc_heap_destroy(mxa->heap);
284 	}
285 
286 	return (0);
287 }
288 
289 /*
290  * mlsvc_xa_release
291  *
292  * This function is called, via some indirection, as a result of a
293  * call to mlsvc_rpc_free. This is where we free the heap memory
294  * that was preserved during an RPC call.
295  */
296 /*ARGSUSED*/
297 static void
298 mlsvc_xa_release(struct mlrpc_client *mcli, mlrpc_heapref_t *heapref)
299 {
300 	if (heapref == NULL)
301 		return;
302 
303 	if (heapref->state == MLRPC_HRST_PRESERVED) {
304 		free(heapref->recv_pdu_buf);
305 		free(heapref->send_pdu_buf);
306 		mlrpc_heap_destroy(heapref->heap);
307 	}
308 }
309