1/*	$NetBSD: clnt_raw.c,v 1.29 2008/04/25 17:44:44 christos Exp $	*/
2
3/*
4 * Copyright (c) 2010, Oracle America, Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *     * Redistributions of source code must retain the above copyright
11 *       notice, this list of conditions and the following disclaimer.
12 *     * Redistributions in binary form must reproduce the above
13 *       copyright notice, this list of conditions and the following
14 *       disclaimer in the documentation and/or other materials
15 *       provided with the distribution.
16 *     * Neither the name of the "Oracle America, Inc." nor the names of its
17 *       contributors may be used to endorse or promote products derived
18 *       from this software without specific prior written permission.
19 *
20 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <sys/cdefs.h>
35#if defined(LIBC_SCCS) && !defined(lint)
36#if 0
37static char *sccsid = "@(#)clnt_raw.c 1.22 87/08/11 Copyr 1984 Sun Micro";
38static char *sccsid = "@(#)clnt_raw.c	2.2 88/08/01 4.0 RPCSRC";
39#else
40__RCSID("$NetBSD: clnt_raw.c,v 1.29 2008/04/25 17:44:44 christos Exp $");
41#endif
42#endif
43
44/*
45 * clnt_raw.c
46 *
47 * Copyright (C) 1984, Sun Microsystems, Inc.
48 *
49 * Memory based rpc for simple testing and timing.
50 * Interface to create an rpc client and server in the same process.
51 * This lets us similate rpc and get round trip overhead, without
52 * any interference from the kernel.
53 */
54
55#include "namespace.h"
56#include "reentrant.h"
57#include <assert.h>
58#include <err.h>
59#include <stdio.h>
60#include <stdlib.h>
61
62#include <rpc/rpc.h>
63#include <rpc/raw.h>
64
65#ifdef __weak_alias
66__weak_alias(clntraw_create,_clntraw_create)
67__weak_alias(clnt_raw_create,_clnt_raw_create)
68#endif
69
70#ifdef _REENTRANT
71extern mutex_t clntraw_lock;
72#endif
73
74#define MCALL_MSG_SIZE 24
75
76/*
77 * This is the "network" we will be moving stuff over.
78 */
79static struct clntraw_private {
80	CLIENT	client_object;
81	XDR	xdr_stream;
82	char	*_raw_buf;
83	union {
84	    struct rpc_msg	mashl_rpcmsg;
85	    char 		mashl_callmsg[MCALL_MSG_SIZE];
86	} u;
87	u_int	mcnt;
88} *clntraw_private;
89
90static enum clnt_stat clnt_raw_call __P((CLIENT *, rpcproc_t, xdrproc_t,
91    const char *, xdrproc_t, caddr_t, struct timeval));
92static void clnt_raw_geterr __P((CLIENT *, struct rpc_err *));
93static bool_t clnt_raw_freeres __P((CLIENT *, xdrproc_t, caddr_t));
94static void clnt_raw_abort __P((CLIENT *));
95static bool_t clnt_raw_control __P((CLIENT *, u_int, char *));
96static void clnt_raw_destroy __P((CLIENT *));
97static struct clnt_ops *clnt_raw_ops __P((void));
98
99/*
100 * Create a client handle for memory based rpc.
101 */
102CLIENT *
103clnt_raw_create(prog, vers)
104	rpcprog_t prog;
105	rpcvers_t vers;
106{
107	struct clntraw_private *clp = clntraw_private;
108	struct rpc_msg call_msg;
109	XDR *xdrs = &clp->xdr_stream;
110	CLIENT	*client = &clp->client_object;
111
112	mutex_lock(&clntraw_lock);
113	if (clp == NULL) {
114		clp = calloc((size_t)1, sizeof (*clp));
115		if (clp == NULL)
116			goto out;
117		if (__rpc_rawcombuf == NULL)
118			__rpc_rawcombuf =
119			    malloc(UDPMSGSIZE);
120		if (__rpc_rawcombuf == NULL)
121			goto out;
122		clp->_raw_buf = __rpc_rawcombuf;
123		clntraw_private = clp;
124	}
125	/*
126	 * pre-serialize the static part of the call msg and stash it away
127	 */
128	call_msg.rm_direction = CALL;
129	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
130	/* XXX: prog and vers have been long historically :-( */
131	call_msg.rm_call.cb_prog = (u_int32_t)prog;
132	call_msg.rm_call.cb_vers = (u_int32_t)vers;
133	xdrmem_create(xdrs, clp->u.mashl_callmsg, MCALL_MSG_SIZE, XDR_ENCODE);
134	if (! xdr_callhdr(xdrs, &call_msg))
135		warnx("clntraw_create - Fatal header serialization error.");
136	clp->mcnt = XDR_GETPOS(xdrs);
137	XDR_DESTROY(xdrs);
138
139	/*
140	 * Set xdrmem for client/server shared buffer
141	 */
142	xdrmem_create(xdrs, clp->_raw_buf, UDPMSGSIZE, XDR_FREE);
143
144	/*
145	 * create client handle
146	 */
147	client->cl_ops = clnt_raw_ops();
148	client->cl_auth = authnone_create();
149	mutex_unlock(&clntraw_lock);
150	return (client);
151out:
152	if (clp)
153		free(clp);
154	mutex_unlock(&clntraw_lock);
155	return NULL;
156
157}
158
159/* ARGSUSED */
160static enum clnt_stat
161clnt_raw_call(h, proc, xargs, argsp, xresults, resultsp, timeout)
162	CLIENT *h;
163	rpcproc_t proc;
164	xdrproc_t xargs;
165	const char *argsp;
166	xdrproc_t xresults;
167	caddr_t resultsp;
168	struct timeval timeout;
169{
170	struct clntraw_private *clp = clntraw_private;
171	XDR *xdrs = &clp->xdr_stream;
172	struct rpc_msg msg;
173	enum clnt_stat status;
174	struct rpc_err error;
175
176	_DIAGASSERT(h != NULL);
177
178	mutex_lock(&clntraw_lock);
179	if (clp == NULL) {
180		mutex_unlock(&clntraw_lock);
181		return (RPC_FAILED);
182	}
183	mutex_unlock(&clntraw_lock);
184
185call_again:
186	/*
187	 * send request
188	 */
189	xdrs->x_op = XDR_ENCODE;
190	XDR_SETPOS(xdrs, 0);
191	clp->u.mashl_rpcmsg.rm_xid ++ ;
192	if ((! XDR_PUTBYTES(xdrs, clp->u.mashl_callmsg, clp->mcnt)) ||
193	    (! XDR_PUTINT32(xdrs, (int32_t *)&proc)) ||
194	    (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
195	    (! (*xargs)(xdrs, __UNCONST(argsp)))) {
196		return (RPC_CANTENCODEARGS);
197	}
198	(void)XDR_GETPOS(xdrs);  /* called just to cause overhead */
199
200	/*
201	 * We have to call server input routine here because this is
202	 * all going on in one process. Yuk.
203	 */
204	svc_getreq_common(FD_SETSIZE);
205
206	/*
207	 * get results
208	 */
209	xdrs->x_op = XDR_DECODE;
210	XDR_SETPOS(xdrs, 0);
211	msg.acpted_rply.ar_verf = _null_auth;
212	msg.acpted_rply.ar_results.where = resultsp;
213	msg.acpted_rply.ar_results.proc = xresults;
214	if (! xdr_replymsg(xdrs, &msg)) {
215		/*
216		 * It's possible for xdr_replymsg() to fail partway
217		 * through its attempt to decode the result from the
218		 * server. If this happens, it will leave the reply
219		 * structure partially populated with dynamically
220		 * allocated memory. (This can happen if someone uses
221		 * clntudp_bufcreate() to create a CLIENT handle and
222		 * specifies a receive buffer size that is too small.)
223		 * This memory must be free()ed to avoid a leak.
224		 */
225		int op = xdrs->x_op;
226		xdrs->x_op = XDR_FREE;
227		xdr_replymsg(xdrs, &msg);
228		xdrs->x_op = op;
229		return (RPC_CANTDECODERES);
230	}
231	_seterr_reply(&msg, &error);
232	status = error.re_status;
233
234	if (status == RPC_SUCCESS) {
235		if (! AUTH_VALIDATE(h->cl_auth, &msg.acpted_rply.ar_verf)) {
236			status = RPC_AUTHERROR;
237		}
238	}  /* end successful completion */
239	else {
240		if (AUTH_REFRESH(h->cl_auth))
241			goto call_again;
242	}  /* end of unsuccessful completion */
243
244	if (status == RPC_SUCCESS) {
245		if (! AUTH_VALIDATE(h->cl_auth, &msg.acpted_rply.ar_verf)) {
246			status = RPC_AUTHERROR;
247		}
248		if (msg.acpted_rply.ar_verf.oa_base != NULL) {
249			xdrs->x_op = XDR_FREE;
250			(void)xdr_opaque_auth(xdrs, &(msg.acpted_rply.ar_verf));
251		}
252	}
253
254	return (status);
255}
256
257/*ARGSUSED*/
258static void
259clnt_raw_geterr(cl, error)
260	CLIENT *cl;
261	struct rpc_err *error;
262{
263}
264
265
266/* ARGSUSED */
267static bool_t
268clnt_raw_freeres(cl, xdr_res, res_ptr)
269	CLIENT *cl;
270	xdrproc_t xdr_res;
271	caddr_t res_ptr;
272{
273	struct clntraw_private *clp = clntraw_private;
274	XDR *xdrs = &clp->xdr_stream;
275	bool_t rval;
276
277	mutex_lock(&clntraw_lock);
278	if (clp == NULL) {
279		rval = (bool_t) RPC_FAILED;
280		mutex_unlock(&clntraw_lock);
281		return (rval);
282	}
283	mutex_unlock(&clntraw_lock);
284	xdrs->x_op = XDR_FREE;
285	return ((*xdr_res)(xdrs, res_ptr));
286}
287
288/*ARGSUSED*/
289static void
290clnt_raw_abort(cl)
291	CLIENT *cl;
292{
293}
294
295/*ARGSUSED*/
296static bool_t
297clnt_raw_control(cl, ui, str)
298	CLIENT *cl;
299	u_int ui;
300	char *str;
301{
302	return (FALSE);
303}
304
305/*ARGSUSED*/
306static void
307clnt_raw_destroy(cl)
308	CLIENT *cl;
309{
310}
311
312static struct clnt_ops *
313clnt_raw_ops()
314{
315	static struct clnt_ops ops;
316#ifdef _REENTRANT
317	extern mutex_t  ops_lock;
318#endif
319
320	/* VARIABLES PROTECTED BY ops_lock: ops */
321
322	mutex_lock(&ops_lock);
323	if (ops.cl_call == NULL) {
324		ops.cl_call = clnt_raw_call;
325		ops.cl_abort = clnt_raw_abort;
326		ops.cl_geterr = clnt_raw_geterr;
327		ops.cl_freeres = clnt_raw_freeres;
328		ops.cl_destroy = clnt_raw_destroy;
329		ops.cl_control = clnt_raw_control;
330	}
331	mutex_unlock(&ops_lock);
332	return (&ops);
333}
334